126 lines
4.3 KiB
Python
126 lines
4.3 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import argparse
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from agent import CoreAgent
|
|||
|
|
from core_agent.config import apply_compat_env_aliases, build_core_agent_config, load_core_agent_env, require_model_config
|
|||
|
|
from providers.openai_compatible import OpenAICompatibleProvider
|
|||
|
|
from providers.rule_based import RuleBasedMeetingProvider
|
|||
|
|
from tools.default_tools import build_default_registry
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TerminalRenderer:
|
|||
|
|
def __init__(self, assistant_name: str) -> None:
|
|||
|
|
self.assistant_name = assistant_name
|
|||
|
|
|
|||
|
|
def render_event(self, event) -> None:
|
|||
|
|
if event.type == "round_start":
|
|||
|
|
print(f"\n[step {event.iteration}/{event.max_iterations}]", end="")
|
|||
|
|
elif event.type == "tool_call":
|
|||
|
|
print(f"\n[tool] {event.tool_name} {event.tool_args}")
|
|||
|
|
elif event.type == "tool_result":
|
|||
|
|
print(f"[tool-result] {_compact(event.tool_result)}")
|
|||
|
|
elif event.type == "final":
|
|||
|
|
print(f"\n{self.assistant_name}> {event.final_response}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_parser() -> argparse.ArgumentParser:
|
|||
|
|
parser = argparse.ArgumentParser(description="Meeting knowledge agent CLI.")
|
|||
|
|
parser.add_argument("--data-dir", default="data", help="台账和结构化数据保存目录。")
|
|||
|
|
parser.add_argument("--once", default="", help="只执行一条消息,适合测试。")
|
|||
|
|
parser.add_argument("--offline", action="store_true", help="只用于本地演示:强制使用离线规则 provider,不调用大模型。")
|
|||
|
|
parser.add_argument("--list-models", action="store_true", help="列出当前 OpenAI-compatible 服务暴露的模型名。")
|
|||
|
|
return parser
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> None:
|
|||
|
|
args = build_parser().parse_args()
|
|||
|
|
workspace = Path.cwd()
|
|||
|
|
load_core_agent_env(workspace)
|
|||
|
|
apply_compat_env_aliases()
|
|||
|
|
config = build_core_agent_config()
|
|||
|
|
|
|||
|
|
if args.list_models:
|
|||
|
|
_list_models(config)
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
provider = _build_provider(config, force_offline=args.offline)
|
|||
|
|
except RuntimeError as exc:
|
|||
|
|
print(f"配置错误:{exc}")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
registry = build_default_registry(workspace / args.data_dir)
|
|||
|
|
agent = CoreAgent(
|
|||
|
|
provider=provider,
|
|||
|
|
workspace=workspace,
|
|||
|
|
skill_dirs=[workspace / "skills"],
|
|||
|
|
tool_registry=registry,
|
|||
|
|
max_iterations=config.max_iterations,
|
|||
|
|
)
|
|||
|
|
session = agent.new_session(session_id="cli_default")
|
|||
|
|
renderer = TerminalRenderer(config.agent_name)
|
|||
|
|
if args.once:
|
|||
|
|
for event in session.stream_ask(args.once):
|
|||
|
|
renderer.render_event(event)
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
print("Meeting Knowledge Interactive Agent")
|
|||
|
|
print(r"示例:导入 D:\github_project\my_code\meeting_agent\examples\huiyi.txt 到知识库 合川分公司")
|
|||
|
|
print("示例:专线护航还有哪些待办?知识库 合川分公司")
|
|||
|
|
print("输入 exit / quit 退出。")
|
|||
|
|
while True:
|
|||
|
|
try:
|
|||
|
|
user_message = input("\n你> ").strip()
|
|||
|
|
except (EOFError, KeyboardInterrupt):
|
|||
|
|
print()
|
|||
|
|
break
|
|||
|
|
if user_message.lower() in {"exit", "quit", "q"}:
|
|||
|
|
break
|
|||
|
|
if not user_message:
|
|||
|
|
continue
|
|||
|
|
for event in session.stream_ask(user_message):
|
|||
|
|
renderer.render_event(event)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _build_provider(config, *, force_offline: bool):
|
|||
|
|
if force_offline:
|
|||
|
|
return RuleBasedMeetingProvider()
|
|||
|
|
require_model_config(config)
|
|||
|
|
return OpenAICompatibleProvider(
|
|||
|
|
model=config.model,
|
|||
|
|
api_key=config.api_key,
|
|||
|
|
base_url=config.base_url,
|
|||
|
|
timeout=config.timeout,
|
|||
|
|
temperature=config.temperature,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _list_models(config) -> None:
|
|||
|
|
from openai import OpenAI
|
|||
|
|
|
|||
|
|
if not config.api_key or not config.base_url:
|
|||
|
|
print("配置错误:列出模型需要 OPENAI_API_KEY 和 OPENAI_BASE_URL。")
|
|||
|
|
return
|
|||
|
|
try:
|
|||
|
|
client = OpenAI(api_key=config.api_key, base_url=config.base_url, timeout=config.timeout)
|
|||
|
|
models = client.models.list()
|
|||
|
|
except Exception as exc:
|
|||
|
|
print(f"获取模型列表失败:{exc}")
|
|||
|
|
return
|
|||
|
|
print(f"OPENAI_BASE_URL={config.base_url}")
|
|||
|
|
print("可用模型:")
|
|||
|
|
for model in models.data:
|
|||
|
|
print(f"- {model.id}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _compact(text: str, limit: int = 240) -> str:
|
|||
|
|
text = " ".join(text.split())
|
|||
|
|
return text if len(text) <= limit else text[:limit] + "..."
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|
|||
|
|
|