From 1396d652f70e38f4b7db46dc1411b8f59f6c47fa Mon Sep 17 00:00:00 2001 From: minorQAQ <202200120184@mail.sdu.edu.cn> Date: Wed, 17 Jun 2026 12:58:18 +0800 Subject: [PATCH] 20260617 --- .env | 38 - docs/feishu_development_guide.md | 373 ++++ pyproject.toml | 1 - requirements.txt | 1 - src/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 162 bytes src/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 162 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 168 bytes .../__pycache__/chatbot_agent.cpython-312.pyc | Bin 9712 -> 1839 bytes .../__pycache__/chatbot_agent.cpython-313.pyc | Bin 0 -> 1681 bytes src/agent/__pycache__/llm.cpython-312.pyc | Bin 25815 -> 264 bytes src/agent/__pycache__/llm.cpython-313.pyc | Bin 0 -> 264 bytes src/agent/agents/__init__.py | 31 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1086 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 272 bytes .../__pycache__/browser_agent.cpython-312.pyc | Bin 0 -> 259 bytes .../__pycache__/browser_agent.cpython-313.pyc | Bin 0 -> 259 bytes .../__pycache__/code_agent.cpython-312.pyc | Bin 0 -> 253 bytes .../__pycache__/code_agent.cpython-313.pyc | Bin 0 -> 253 bytes .../__pycache__/main_agent.cpython-312.pyc | Bin 0 -> 3126 bytes .../__pycache__/main_agent.cpython-313.pyc | Bin 0 -> 912 bytes .../multi_agent_runtime.cpython-312.pyc | Bin 0 -> 9756 bytes .../research_agent.cpython-312.pyc | Bin 0 -> 257 bytes .../research_agent.cpython-313.pyc | Bin 0 -> 257 bytes src/agent/agents/code_agent.py | 10 + src/agent/agents/doc_agent.py | 0 src/agent/agents/gui_agent.py | 10 + src/agent/agents/main_agent.py | 74 + src/agent/agents/multi_agent_runtime.py | 252 +++ src/agent/agents/research_agent.py | 12 + src/agent/chatbot_agent.py | 299 +--- src/agent/core/__init__.py | 16 + .../core/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 762 bytes .../core/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 762 bytes .../__pycache__/approvals.cpython-312.pyc | Bin 0 -> 6702 bytes .../__pycache__/approvals.cpython-313.pyc | Bin 0 -> 6675 bytes .../config_manager.cpython-312.pyc | Bin 0 -> 14029 bytes .../__pycache__/graph_builder.cpython-312.pyc | Bin 0 -> 2544 bytes .../__pycache__/graph_builder.cpython-313.pyc | Bin 0 -> 1448 bytes .../human_interaction.cpython-312.pyc | Bin 0 -> 3808 bytes .../human_interaction.cpython-313.pyc | Bin 0 -> 2402 bytes .../core/__pycache__/llm.cpython-312.pyc | Bin 0 -> 32457 bytes .../core/__pycache__/llm.cpython-313.pyc | Bin 0 -> 31945 bytes .../multi_agent_graph.cpython-312.pyc | Bin 0 -> 18581 bytes .../multi_agent_state.cpython-312.pyc | Bin 0 -> 7132 bytes .../multi_agent_state.cpython-313.pyc | Bin 0 -> 7102 bytes .../core/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 6187 bytes .../core/__pycache__/nodes.cpython-313.pyc | Bin 0 -> 4461 bytes .../core/__pycache__/plan.cpython-312.pyc | Bin 0 -> 2447 bytes .../core/__pycache__/plan.cpython-313.pyc | Bin 0 -> 2497 bytes .../core/__pycache__/routing.cpython-312.pyc | Bin 0 -> 3708 bytes .../core/__pycache__/routing.cpython-313.pyc | Bin 0 -> 2203 bytes .../core/__pycache__/runtime.cpython-312.pyc | Bin 0 -> 20365 bytes .../core/__pycache__/runtime.cpython-313.pyc | Bin 0 -> 25743 bytes .../core/__pycache__/state.cpython-312.pyc | Bin 0 -> 1482 bytes .../core/__pycache__/state.cpython-313.pyc | Bin 0 -> 750 bytes .../__pycache__/todo_manager.cpython-312.pyc | Bin 0 -> 2487 bytes .../__pycache__/tool_policy.cpython-312.pyc | Bin 0 -> 5547 bytes .../__pycache__/tool_policy.cpython-313.pyc | Bin 0 -> 2820 bytes src/agent/core/agent_config.json | 26 + src/agent/core/approvals.py | 166 ++ src/agent/core/config_manager.py | 339 ++++ src/agent/core/env_config.json | 25 + src/agent/core/graph_builder.py | 53 + src/agent/core/human_interaction.py | 75 + src/agent/core/llm.py | 670 ++++++++ src/agent/core/multi_agent_graph.py | 533 ++++++ src/agent/core/multi_agent_state.py | 172 ++ src/agent/core/nodes.py | 143 ++ src/agent/core/routing.py | 77 + src/agent/core/runtime.py | 476 ++++++ src/agent/core/state.py | 32 + src/agent/core/tool_config.json | 58 + src/agent/core/tool_policy.py | 144 ++ src/agent/history/data_base/chat_history.db | Bin 57344 -> 0 bytes .../20260527_130428/ui_messages.json | 22 - .../20260527_130748/ui_messages.json | 56 - .../20260527_214854/ui_messages.json | 39 - .../20260527_215121/ui_messages.json | 49 - .../后端建议_20260527_215121_1.png | Bin 422238 -> 0 bytes ...®²——我的故事_20260527_215121_1.pdf | Bin 84903 -> 0 bytes ...´优秀学生干部_20260527_215307_1.xlsx | Bin 9042 -> 0 bytes .../20260527_215307/ui_messages.json | 27 - .../20260603_121405/ui_messages.json | 22 - .../20260603_121521/ui_messages.json | 22 - .../20260603_121652/ui_messages.json | 39 - .../20260603_121729/ui_messages.json | 22 - .../20260603_122026/ui_messages.json | 22 - .../20260603_122112/ui_messages.json | 22 - .../20260603_122357/ui_messages.json | 22 - .../20260603_172211/ui_messages.json | 39 - .../20260603_175632/ui_messages.json | 39 - src/agent/history/tool_call_recorder.py | 263 +++ src/agent/llm.py | 490 +----- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 175 bytes .../__pycache__/system_prompt.cpython-312.pyc | Bin 5689 -> 6090 bytes .../__pycache__/system_prompt.cpython-313.pyc | Bin 0 -> 7491 bytes src/agent/memory/system_prompt.py | 13 +- src/agent/tools/__init__.py | 48 +- .../__pycache__/__init__.cpython-312.pyc | Bin 502 -> 1897 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 668 bytes .../tools/__pycache__/tool.cpython-312.pyc | Bin 15922 -> 23021 bytes .../tools/__pycache__/tool.cpython-313.pyc | Bin 0 -> 23065 bytes .../tool_agent_call.cpython-312.pyc | Bin 0 -> 2549 bytes .../__pycache__/tool_database.cpython-312.pyc | Bin 0 -> 7890 bytes .../__pycache__/tool_database.cpython-313.pyc | Bin 0 -> 8123 bytes .../__pycache__/tool_rag.cpython-312.pyc | Bin 25769 -> 26651 bytes .../__pycache__/tool_rag.cpython-313.pyc | Bin 0 -> 26660 bytes .../tool_software_control.cpython-312.pyc | Bin 0 -> 26948 bytes .../tool_software_control.cpython-313.pyc | Bin 0 -> 27608 bytes src/agent/tools/doc_tool.py | 0 src/agent/tools/gui_tool.py | 1 + src/agent/tools/tool.py | 120 +- src/agent/tools/tool_agent_call.py | 52 + src/agent/tools/tool_rag.py | 17 +- src/agent/tools/tool_software_control.py | 519 ++++++ .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 174 bytes .../__pycache__/agent_utils.cpython-312.pyc | Bin 26361 -> 33608 bytes .../__pycache__/agent_utils.cpython-313.pyc | Bin 0 -> 33570 bytes .../database_utils.cpython-312.pyc | Bin 0 -> 10994 bytes .../database_utils.cpython-313.pyc | Bin 0 -> 10881 bytes .../__pycache__/env_utils.cpython-312.pyc | Bin 2452 -> 8166 bytes .../__pycache__/env_utils.cpython-313.pyc | Bin 0 -> 2430 bytes .../__pycache__/image_utils.cpython-312.pyc | Bin 6808 -> 9485 bytes .../__pycache__/image_utils.cpython-313.pyc | Bin 0 -> 6920 bytes .../__pycache__/log_utils.cpython-312.pyc | Bin 0 -> 1681 bytes .../__pycache__/log_utils.cpython-313.pyc | Bin 0 -> 1737 bytes .../tool_call_utils.cpython-312.pyc | Bin 0 -> 5391 bytes .../tool_call_utils.cpython-313.pyc | Bin 0 -> 2848 bytes src/agent/utils/agent_utils.py | 273 ++- src/agent/utils/database_utils.py | 158 +- src/agent/utils/env_utils.py | 133 +- src/agent/utils/image_utils.py | 127 +- src/agent/utils/log_utils.py | 32 + src/agent/utils/tool_call_utils.py | 121 ++ src/agent/workspace/.todo | 53 + src/web/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 256 bytes src/web/__pycache__/server.cpython-312.pyc | Bin 21766 -> 43142 bytes src/web/__pycache__/server.cpython-313.pyc | Bin 0 -> 29479 bytes .../__pycache__/ui_session.cpython-312.pyc | Bin 24449 -> 32617 bytes .../__pycache__/ui_session.cpython-313.pyc | Bin 0 -> 26209 bytes src/web/app.css | 1423 +++++++++++++++- src/web/app.js | 1513 ++++++++++++++++- src/web/index.html | 143 +- src/web/server.py | 583 ++++++- src/web/ui_session.py | 365 +++- 145 files changed, 9542 insertions(+), 1453 deletions(-) delete mode 100644 .env create mode 100644 docs/feishu_development_guide.md create mode 100644 src/__pycache__/__init__.cpython-312.pyc create mode 100644 src/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/__pycache__/chatbot_agent.cpython-313.pyc create mode 100644 src/agent/__pycache__/llm.cpython-313.pyc create mode 100644 src/agent/agents/__init__.py create mode 100644 src/agent/agents/__pycache__/__init__.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/agents/__pycache__/browser_agent.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/browser_agent.cpython-313.pyc create mode 100644 src/agent/agents/__pycache__/code_agent.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/code_agent.cpython-313.pyc create mode 100644 src/agent/agents/__pycache__/main_agent.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/main_agent.cpython-313.pyc create mode 100644 src/agent/agents/__pycache__/multi_agent_runtime.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/research_agent.cpython-312.pyc create mode 100644 src/agent/agents/__pycache__/research_agent.cpython-313.pyc create mode 100644 src/agent/agents/code_agent.py create mode 100644 src/agent/agents/doc_agent.py create mode 100644 src/agent/agents/gui_agent.py create mode 100644 src/agent/agents/main_agent.py create mode 100644 src/agent/agents/multi_agent_runtime.py create mode 100644 src/agent/agents/research_agent.py create mode 100644 src/agent/core/__init__.py create mode 100644 src/agent/core/__pycache__/__init__.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/approvals.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/approvals.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/config_manager.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/graph_builder.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/graph_builder.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/human_interaction.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/human_interaction.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/llm.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/llm.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/multi_agent_graph.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/multi_agent_state.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/multi_agent_state.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/nodes.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/nodes.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/plan.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/plan.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/routing.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/routing.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/runtime.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/runtime.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/state.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/state.cpython-313.pyc create mode 100644 src/agent/core/__pycache__/todo_manager.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/tool_policy.cpython-312.pyc create mode 100644 src/agent/core/__pycache__/tool_policy.cpython-313.pyc create mode 100644 src/agent/core/agent_config.json create mode 100644 src/agent/core/approvals.py create mode 100644 src/agent/core/config_manager.py create mode 100644 src/agent/core/env_config.json create mode 100644 src/agent/core/graph_builder.py create mode 100644 src/agent/core/human_interaction.py create mode 100644 src/agent/core/llm.py create mode 100644 src/agent/core/multi_agent_graph.py create mode 100644 src/agent/core/multi_agent_state.py create mode 100644 src/agent/core/nodes.py create mode 100644 src/agent/core/routing.py create mode 100644 src/agent/core/runtime.py create mode 100644 src/agent/core/state.py create mode 100644 src/agent/core/tool_config.json create mode 100644 src/agent/core/tool_policy.py delete mode 100644 src/agent/history/data_base/chat_history.db delete mode 100644 src/agent/history/multi_inputs/20260527_130428/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260527_130748/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260527_214854/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260527_215121/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260527_215121/后端建议_20260527_215121_1.png delete mode 100644 src/agent/history/multi_inputs/20260527_215121/英语演讲——我的故事_20260527_215121_1.pdf delete mode 100644 src/agent/history/multi_inputs/20260527_215307/22级-25年优秀学生干部_20260527_215307_1.xlsx delete mode 100644 src/agent/history/multi_inputs/20260527_215307/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_121405/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_121521/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_121652/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_121729/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_122026/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_122112/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_122357/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_172211/ui_messages.json delete mode 100644 src/agent/history/multi_inputs/20260603_175632/ui_messages.json create mode 100644 src/agent/history/tool_call_recorder.py create mode 100644 src/agent/memory/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/memory/__pycache__/system_prompt.cpython-313.pyc create mode 100644 src/agent/tools/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/tools/__pycache__/tool.cpython-313.pyc create mode 100644 src/agent/tools/__pycache__/tool_agent_call.cpython-312.pyc create mode 100644 src/agent/tools/__pycache__/tool_database.cpython-312.pyc create mode 100644 src/agent/tools/__pycache__/tool_database.cpython-313.pyc create mode 100644 src/agent/tools/__pycache__/tool_rag.cpython-313.pyc create mode 100644 src/agent/tools/__pycache__/tool_software_control.cpython-312.pyc create mode 100644 src/agent/tools/__pycache__/tool_software_control.cpython-313.pyc create mode 100644 src/agent/tools/doc_tool.py create mode 100644 src/agent/tools/gui_tool.py create mode 100644 src/agent/tools/tool_agent_call.py create mode 100644 src/agent/tools/tool_software_control.py create mode 100644 src/agent/utils/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/agent_utils.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/database_utils.cpython-312.pyc create mode 100644 src/agent/utils/__pycache__/database_utils.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/env_utils.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/image_utils.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/log_utils.cpython-312.pyc create mode 100644 src/agent/utils/__pycache__/log_utils.cpython-313.pyc create mode 100644 src/agent/utils/__pycache__/tool_call_utils.cpython-312.pyc create mode 100644 src/agent/utils/__pycache__/tool_call_utils.cpython-313.pyc create mode 100644 src/agent/utils/tool_call_utils.py create mode 100644 src/agent/workspace/.todo create mode 100644 src/web/__pycache__/__init__.cpython-313.pyc create mode 100644 src/web/__pycache__/server.cpython-313.pyc create mode 100644 src/web/__pycache__/ui_session.cpython-313.pyc diff --git a/.env b/.env deleted file mode 100644 index 3fbfb93..0000000 --- a/.env +++ /dev/null @@ -1,38 +0,0 @@ -LANGSMITH_TRACING=True -LANGSMITH_ENDPOINT=https://api.smith.langchain.com -LANGSMITH_API_KEY=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 -LANGSMITH_PROJECT="agent_learning" -DATABASE_URI=sqlite:///./langgraph.db -REDIS_URI=redis://localhost:6379/0 -#spark: -LLM_API_KEY='semi-ai.tech' -# LLM_BASE_URL="http://192.168.137.96:8000/v1" -LLM_BASE_URL="http://semi-ai.tech:50400/v1" -LLM_MODEL = "Qwen/Qwen3.6-35B-A3B-FP8" -# ASR_TTS_BASE_URL = "http://192.168.137.96:8100" -# ASR_BASE_URL = "http://192.168.137.96:8901/v1" -# TTS_BASE_URL = "http://192.168.137.96:8902" -# RAG_BASE_URL = "http://192.168.137.96:8903" - -#6000pro: -# LLM_API_KEY='semi-ai.tech' -# LLM_BASE_URL="http://semi-ai.tech:40056/v1" -# LLM_MODEL = "Intel/Qwen3.6-35B-A3B-int4-mixed-AutoRound" - -#A100: -# LLM_API_KEY = 'sk-kGhWAQTqLk9ECsmIDb758a456bD34e92B9771dF2E36432Fb' -# LLM_BASE_URL = "https://app.semi-ai.tech:60004/v1" -# LLM_MODEL = "Qwen3.5-122B-A10B" -# LLM_TIMEOUT = 300 -# LLM_REQUESTS_PER_SECOND = 10.0 -# LLM_MAX_BUCKET_SIZE = 10 -# LLM_CHECK_EVERY_N_SECONDS = 0.1 - -#context: -LLM_CONTEXT_WINDOW=32768 -MINI_COMPRESS_RATE=0.4 -HARD_COMPRESS_RATE=0.6 -CURRENT_TURNS=4 -IMG_SIZE=768 -RAG_CHUNK_SIZE=500 -RAG_CHUNK_OVERLAP=50 \ No newline at end of file diff --git a/docs/feishu_development_guide.md b/docs/feishu_development_guide.md new file mode 100644 index 0000000..d7185a4 --- /dev/null +++ b/docs/feishu_development_guide.md @@ -0,0 +1,373 @@ +# Minor Agent 系统架构、部署与开发指南(飞书版) + +## 1. 文档目标 + +本文面向 Minor Agent 的研发、部署和维护人员,说明当前系统的整体架构、核心运行链路、Plan TodoList 机制、工具扩展方式、Web 前端交互、部署步骤与常见排障方法。 + +## 2. 系统概览 + +Minor Agent 是一个基于 LangGraph 的多模态本地 Agent 系统,提供 Web 对话界面,支持文本、图片、音频和文件输入,并通过 LangChain Tool 接入外部能力。 + +### 2.1 核心能力 + +- 多模态对话:文本、图片、音频、普通文件。 +- Agent 模式:普通 agent、plan、team 预留。 +- ReAct 工具调用:浏览器控制、屏幕截图、目录查看、人工交互、本机软件控制、Todo 状态更新等。 +- 历史持久化:SQLite 保存对话历史与压缩元信息。 +- 上下文压缩:按 token 阈值触发轻量/全量摘要。 +- Plan TodoList:Plan 模式下生成 `.todo`,前端浮窗展示,执行过程中动态更新。 + +## 3. 目录结构 + +```text +Agent/ +├─ requirements.txt +├─ pyproject.toml +├─ README.md +├─ docs/ +│ └─ feishu_development_guide.md +└─ src/ + ├─ web/ + │ ├─ server.py # FastAPI 后端 API 与静态资源服务 + │ ├─ app.js # Web 前端主逻辑 + │ ├─ app.css # Web 前端样式 + │ └─ index.html # Web 页面入口 + └─ agent/ + ├─ agents/ + │ └─ main_agent.py # 主 Agent 装配入口 + ├─ core/ + │ ├─ runtime.py # Agent 执行、Plan 确认、人工交互续跑 + │ ├─ graph_builder.py # LangGraph ReAct 图构建 + │ ├─ nodes.py # LLM 节点与工具产物处理 + │ ├─ routing.py # 工具调用路由/审批识别 + │ ├─ plan.py # Plan 模式路径选择 pending action + │ ├─ todo_manager.py # `.todo` 读写、解析、状态更新、上下文注入 + │ └─ state.py # LangGraph 状态定义 + ├─ memory/ + │ └─ system_prompt.py # 系统提示词、Plan 提示词 + ├─ tools/ + │ ├─ tool.py # 通用工具集合 + │ ├─ tool_software_control.py # Windows 本机软件控制工具 + │ └─ __init__.py # 工具注册 + ├─ utils/ + │ └─ agent_utils.py # 历史、压缩、多模态文件处理 + ├─ history/ # 聊天历史、会话附件、数据库 + └─ workspace/ + └─ .todo # Plan TodoList 文件 +``` + +## 4. 后端架构 + +### 4.1 Web API 层 + +入口文件:`src/web/server.py` + +主要职责: + +- 提供静态页面:`/`、`/app.js`、`/app.css`、`/image/{asset}`。 +- 会话管理:新建、切换、清空、删除。 +- 多模态上传:将上传文件临时落盘,再同步到会话目录。 +- Agent 执行:调用 `chatbot_agent` 间接进入 `AgentRuntime`。 +- 人工交互回调:处理工具审批、Plan 路径确认等 pending action。 +- Todo API:`GET /api/todo` 返回当前 `.todo` 内容和结构化任务列表。 + +### 4.2 Runtime 层 + +入口文件:`src/agent/core/runtime.py` + +核心接口: + +- `execute_agent(...)`:处理用户新消息,执行普通 agent / plan / team 流程。 +- `continue_after_human_action(...)`:处理人工确认、Plan 路径选择、工具审批后的续跑。 + +运行流程: + +1. Web UI 提交用户消息。 +2. Runtime 调用 `summarizer(...)` 组装模型上下文。 +3. 普通模式直接执行 LangGraph。 +4. Plan 模式先进入研究阶段,只输出可选路径,不执行任务。 +5. 用户确认路径后,Runtime 调用模型生成 TodoList,并覆盖写入 `agent/workspace/.todo`。 +6. 后续 Plan 上下文会在系统提示词后注入 `.todo` 内容,确保压缩不影响 TodoList。 + +### 4.3 LangGraph 层 + +入口文件:`src/agent/core/graph_builder.py` + +图结构: + +```text +START -> agent -> (tools | END) +tools -> process_tool_artifact -> agent +``` + +说明: + +- `agent` 节点调用已绑定工具的 LLM。 +- `tools` 节点执行 LangChain Tool。 +- `process_tool_artifact` 处理截图等 artifact,将图片转为模型可见的多模态消息。 + +## 5. Plan TodoList 机制 + +### 5.1 设计目标 + +Plan 模式应实现: + +- 根据用户选择的路径生成动态 TodoList。 +- TodoList 写入 `src/agent/workspace/.todo`。 +- 每次 Plan 模式制定 TodoList 时覆盖旧 `.todo`。 +- 前端以浮窗展示,可收起为悬浮球。 +- Plan 模式上下文开头携带 `.todo`,紧跟系统提示词。 +- 上下文压缩不应压缩或丢失 `.todo`。 +- Agent 执行过程中可更新任务状态。 + +### 5.2 当前实现 + +核心文件:`src/agent/core/todo_manager.py` + +能力: + +- `read_todo_text()`:读取 `.todo`。 +- `write_todo_text()`:覆盖写入 `.todo`。 +- `overwrite_todo_from_plan()`:Plan 路径确认后覆盖旧 TodoList。 +- `todo_context_block()`:生成上下文注入块。 +- `system_prompt_with_todo()`:将 `.todo` 紧贴系统提示词之后注入。 +- `parse_todo_items()`:解析 Markdown checkbox 和状态前缀。 +- `update_todo_status()`:按任务描述更新状态,不匹配时追加新任务。 +- `todo_payload()`:提供给前端的结构化数据。 + +### 5.3 前端展示 + +核心文件:`src/web/app.js`、`src/web/app.css` + +- 前端启动时调用 `/api/todo`。 +- 每 2.5 秒自动刷新一次。 +- TodoList 显示为右侧浮窗。 +- 点击收起后变成悬浮球。 +- 展示任务状态、完成数量、任务文本。 + +### 5.4 Agent 更新 Todo 状态 + +工具:`todo_status_update` + +注册位置:`src/agent/tools/__init__.py` + +适用场景: + +- 任务开始:更新为 `in_progress`。 +- 任务完成:更新为 `completed`。 +- 任务取消:更新为 `cancelled`。 +- 计划变化:追加或修改任务状态。 + +## 6. 工具系统 + +### 6.1 工具注册 + +入口文件:`src/agent/tools/__init__.py` + +当前主 Agent 工具包含: + +- `web_search_class`:网络搜索。 +- `date_query_class`:当前日期时间。 +- `screen_capture`:屏幕截图。 +- `browser_control`:浏览器打开/关闭/刷新。 +- `human_interaction`:人工补充、选择、审批。 +- `software_control`:Windows 本机软件打开/关闭/强制关闭。 +- `todo_status_update`:更新 `.todo` 中任务状态。 +- `directory_listing`:查看目录。 + +### 6.2 本机软件控制工具 + +文件:`src/agent/tools/tool_software_control.py` + +能力: + +- 扫描 Windows 开始菜单、桌面快捷方式和 App Paths。 +- 建立软件索引:`src/agent/history/data_base/software_index.json`。 +- 根据软件名做模糊匹配。 +- 支持打开、正常关闭、强制关闭。 + +安全策略: + +- 仅当用户明确要求操作本机软件时调用。 +- `close` 只发送正常关闭请求。 +- `force_close` 或 `force=True` 才允许强制结束进程。 +- 非 Windows 平台返回明确不支持提示。 + +## 7. 历史与上下文压缩 + +核心文件:`src/agent/utils/agent_utils.py` + +### 7.1 历史存储 + +- SQLite 数据库:`src/agent/history/data_base/chat_history.db` +- 表: + - `message_store`:聊天消息。 + - `session_meta`:压缩游标、摘要、上次 token 用量。 + +### 7.2 压缩策略 + +- 当 token 超过轻量阈值,触发轻量压缩,保留最近若干轮对话。 +- 当 token 超过硬阈值,触发全量压缩。 +- `.todo` 不进入历史压缩摘要,而是每次 Plan 模式构造系统提示词时重新从文件读取并注入,因此不会被压缩丢失。 + +## 8. 前端架构 + +文件:`src/web/index.html`、`src/web/app.js`、`src/web/app.css` + +主要模块: + +- 会话侧栏:新建、切换、删除会话。 +- 消息区:Markdown 渲染、代码块复制、图片/音频展示。 +- 输入区:文本、文件、麦克风、自动语音输入。 +- Pending Action 卡片:工具审批、人工交互、Plan 路径选择。 +- Todo 浮窗:Plan TodoList 展示和自动刷新。 + +## 9. 部署指南 + +### 9.1 环境准备 + +推荐 Python 3.10+。 + +```bash +cd Agent +python -m venv myenv +``` + +Windows PowerShell: + +```powershell +.\myenv\Scripts\Activate.ps1 +``` + +macOS / Linux: + +```bash +source myenv/bin/activate +``` + +安装依赖: + +```bash +pip install --upgrade pip +pip install -r requirements.txt +pip install -e . +``` + +### 9.2 环境变量 + +在 `Agent/.env` 中配置: + +```text +LLM_BASE_URL=... +LLM_API_KEY=... +LLM_MODEL=... +TTS_BASE_URL=... +ASR_BASE_URL=... +RAG_BASE_URL=... +``` + +其中 TTS、ASR、RAG 可按实际功能启用情况配置。 + +### 9.3 启动 Web 服务 + +Windows PowerShell: + +```powershell +cd Agent +$env:PYTHONPATH="src;$env:PYTHONPATH" +uvicorn web.server:app --host 127.0.0.1 --port 8765 +``` + +macOS / Linux: + +```bash +cd Agent +export PYTHONPATH=src:$PYTHONPATH +uvicorn web.server:app --host 127.0.0.1 --port 8765 +``` + +访问: + +```text +http://127.0.0.1:8765 +``` + +### 9.4 公网访问(可选) + +```bash +cloudflared tunnel --url http://localhost:8765 +``` + +## 10. 开发指南 + +### 10.1 新增工具 + +1. 在 `src/agent/tools/tool.py` 或独立工具文件中实现 `BaseTool`。 +2. 定义 `args_schema`、`name`、`description`、`_run()`、可选 `_arun()`。 +3. 在 `src/agent/tools/__init__.py` 的 `_all_tools` 中注册。 +4. 重启服务,让 `llm.bind_tools(...)` 重新绑定工具。 + +### 10.2 修改 Plan 流程 + +相关文件: + +- `src/agent/memory/system_prompt.py` +- `src/agent/core/plan.py` +- `src/agent/core/runtime.py` +- `src/agent/core/todo_manager.py` + +注意事项: + +- 研究阶段不能执行实际任务或调用工具。 +- 用户确认路径后才能生成 TodoList。 +- 生成 TodoList 后必须覆盖 `.todo`。 +- 执行阶段应根据任务进度调用 `todo_status_update`。 + +### 10.3 修改前端 Todo 浮窗 + +相关文件: + +- `src/web/app.js`:渲染、刷新、收起逻辑。 +- `src/web/app.css`:浮窗视觉样式。 +- `src/web/server.py`:`/api/todo` 数据接口。 + +## 11. 常见问题排查 + +### 11.1 前端看不到 Todo 浮窗 + +检查: + +1. 后端 `/api/todo` 是否返回内容。 +2. `src/agent/workspace/.todo` 是否存在且非空。 +3. 浏览器控制台是否有 JS 错误。 +4. 是否被收起为右侧悬浮球。 + +### 11.2 Plan 模式没有写入 `.todo` + +检查: + +1. 是否完成了路径选择确认。 +2. `continue_after_human_action` 中 pending type 是否为 `plan_choice`。 +3. 模型是否返回了 TodoList 文本。 +4. 进程是否有写入 `src/agent/workspace/.todo` 的权限。 + +### 11.3 软件控制工具找不到软件 + +处理: + +1. 使用更具体的软件名称。 +2. 调用时设置 `rescan=True` 重建索引。 +3. 确认软件有开始菜单或桌面快捷方式,或注册了 App Paths。 +4. 检查索引文件:`src/agent/history/data_base/software_index.json`。 + +### 11.4 上下文过长 + +系统会基于 token 阈值自动压缩历史。如果超过硬限制,会提示新建会话。Plan TodoList 不依赖历史摘要保存,而是从 `.todo` 文件注入,因此不会因压缩丢失。 + +## 12. 维护建议 + +- `.todo` 属于运行态工作区文件,不建议作为长期知识库。 +- 对高风险工具应保持人工审批或明确调用条件。 +- 工具描述应写清楚“何时调用”和“何时严禁调用”,降低误调用概率。 +- 修改工具注册后必须重启服务。 +- 修改前端静态文件后刷新浏览器缓存。 diff --git a/pyproject.toml b/pyproject.toml index df5644b..0d68180 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ license = { text = "MIT" } requires-python = ">=3.10" dependencies = [ "langgraph>=1.0.0", - "python-dotenv>=1.0.1", "requests>=2.31.0", "langchain>=0.1.0", "langchain-core>=0.1.0", diff --git a/requirements.txt b/requirements.txt index d6f1aad..0026379 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,7 +35,6 @@ python-docx>=1.0.0 pdfplumber>=0.10.0 # Utilities -python-dotenv>=1.0.1 loguru>=0.7.0 pydantic>=2.0.0 diff --git a/src/__pycache__/__init__.cpython-312.pyc b/src/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72175c55d116f121b8ae40b2bec767c86ab8ac61 GIT binary patch literal 162 zcmX@j%ge<81p0Txvq1D?5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!a&)$e2`x@7Dvq%* zGc+@caY-%CF3B&5aZFFmD~b0>O)SdG%uA2Y&CJU$f(XVG7bVBU$7kkcmc+;F6;$5h fu*uC&Da}c>D`Ewj$Oy#6AjU^#Mn=XWW*`dyYlA1> literal 0 HcmV?d00001 diff --git a/src/__pycache__/__init__.cpython-313.pyc b/src/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8743c991df8259d945b10dff128740a5f34586e5 GIT binary patch literal 162 zcmey&%ge<81p0Txvq1D?5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iOvsFxJacWU< zjD?w@nOTfWYH@Z+enE_5dTL%tyiaOkQC?PO4oIE6_xcjm03wM`lJw#v*1Q3jl9?C)5A{ literal 0 HcmV?d00001 diff --git a/src/agent/__pycache__/__init__.cpython-313.pyc b/src/agent/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d30ff6a0fdee4135cc8a92913b3da34115894ff GIT binary patch literal 168 zcmey&%ge<81aI7~XM*U*AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=AXRDad;?$zz z7z;B)GqV_%)Z*-t{DK(A^whkPc%Rh7qP)z!^!VJ&y!;}FU`%mQa!evfIwn3oGcU6w lK3=b&@)n0pZhlH>PO4oIE6`k!y~QBLM`lJw#v*1Q3jihqD=Pp1 literal 0 HcmV?d00001 diff --git a/src/agent/__pycache__/chatbot_agent.cpython-312.pyc b/src/agent/__pycache__/chatbot_agent.cpython-312.pyc index 5669ab914356c63a3620a108034fb432a131a09a..2a92f8ea9f1d2a04c0cbbac39c6eb1ece2f82569 100644 GIT binary patch literal 1839 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 Yo04rwaq^Mm*bCh`UG-ff1|i%1zhI_>rT_o{ diff --git a/src/agent/__pycache__/chatbot_agent.cpython-313.pyc b/src/agent/__pycache__/chatbot_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7bc61176c4519f4d542a61f32b011fc6e9e88d3 GIT binary patch literal 1681 zcmaJ>&2HO95MGKRMM>0;Ww}liJEDIeM~LC3C}6ZjQNT@eu^j~nt3x3f1Ve9Zx)LcY zS49-`;J!oi5Ph5KWMNSQ0g9sNje*-sPn}tck&6OdTQlFz?(EKdGs~M&sX$;<9=9%j z)Cl<%C+ScVpxh9JkT=98wsJ%c6{c8-Ge?<2m8pkWmX&k$NITS-E^+oKcWAJ@#2VlN zD_W$VwRO0HF!MgaKf93V+tM!Dxdyq&UMNhi+Vj1)_oUb^+4=ciqpv@2q+u^KD}(+1 zVxA&a0lSsX7Md-AeS0xa5v!dU?eZTuZ7ZVa20_SO?uS9t&It7&n26l5&na^Uuv{Cu ze&D!gG~f;!2izZ0u-@}RfWeqL?n~I|oR5bt?DFswST505)EjeHMZdNxGzmm@G;jlv z<?QL)FoY|o9U{y@~O5>+L=yU6S;Xn!t~Bv?wtD( z57|T%A{s>yh~uA%VmRibF?aZ6L`5M&xr=80(9W zC)e$bCJeKe!o*7~5SQdnlb9R7R5xcU8?)v0S-lDJT~RmnnCO~*1A9ov!%)+KeJ?3| z0O0>A>}I!u?ojXAOj8p3QNTw+ILB!cumRNEE6mGq5gZCW% zryUYP^~DA31H5k)#T^vLZ-l8JwU)(}(U?ocJzQ;p08XmJ{P16#EZs$B>dki*1DP>2 zoz-D0G#vC;#8JNDERUvw4B+qj0GgzC>U+Eg+3Z@-?I$O)&O!x4QtWkbkQEnsS<$D^ zFhXaPk6FOh@U%>aVPNfYk`~&MW|6KkL5VQS*W2^g)Z0mzr8~RU4x<;;A-#DFvr%!}B7z}?RvKq3W!uTAX-FQI1U=QJ7eBhA)5vz)#T$9b;N%J?de@)zL z^5m_#{B!-`)ZD$y#|5&wK7;?YtvGKg`mEN9RnTv%%drMJ)=6z8&LJ|$QbXNSys6c9KaS@S;2cutH#^Wl8TBNdZRc=kot;^!f jXj~cA)UYnKS#|TO`e<5xbXl6|cdqocslN7uDs%q_@c)iA literal 0 HcmV?d00001 diff --git a/src/agent/__pycache__/llm.cpython-312.pyc b/src/agent/__pycache__/llm.cpython-312.pyc index 16446dffe9e184040e95bd8daf877f37818ac80d..4d2f8a0b37f91be5c25148d504360cc5721d98ac 100644 GIT binary patch delta 208 zcmcb9lCguS{xmNy7XuK8IICrO0O`jd4h%3s8J}f*a&dJFwVg`y9u>c7_O_p1HiRr0%C3?yE zMX7pFNim4*E%x~M#GIV?_>~NwK?Y6U#_6Jei^C>2KczG$)vgHSL`EPk76K9C%v W?=xsT;1-=wdVx!}k-dlmC=38x-YyLQ literal 25815 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 literal 0 HcmV?d00001 diff --git a/src/agent/agents/__init__.py b/src/agent/agents/__init__.py new file mode 100644 index 0000000..0f3c597 --- /dev/null +++ b/src/agent/agents/__init__.py @@ -0,0 +1,31 @@ +"""Agent 实例注册包。 + +系统定位: + 对外导出各 Agent 的 ``AgentRuntime`` 单例及多 Agent 运行时。 + +Agent 列表: + - main_agent_runtime: 主 Agent 运行时(单 Agent 模式)。 + - build_multi_agent_runtime: 构建一主多子 Agent 运行时。 + - get_agent_graph_snapshot: 获取多 Agent 图状态快照。 + +可扩展性: + 子 Agent 实现后在此追加导出,如:: + from agent.agents.research_agent import research_agent_runtime + __all__ = ["main_agent_runtime", "research_agent_runtime"] +""" +from agent.agents.main_agent import main_agent_runtime, reload_main_agent_runtime +from agent.agents.multi_agent_runtime import ( + MultiAgentRuntime, + SubAgentConfig, + build_multi_agent_runtime, + get_agent_graph_snapshot, +) + +__all__ = [ + "main_agent_runtime", + "reload_main_agent_runtime", + "MultiAgentRuntime", + "SubAgentConfig", + "build_multi_agent_runtime", + "get_agent_graph_snapshot", +] diff --git a/src/agent/agents/__pycache__/__init__.cpython-312.pyc b/src/agent/agents/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62a1d839616a9d3d64889cc96a400425b0f0ea16 GIT binary patch literal 1086 zcmZ{jO-~a+7{_L+TE;hVPvj5CX=J`F({GZt$EiDm-jE=^g z7e9RrvriYtgYzbbU1T!Hhz!q&tjH;>%JH1)<9#e$`xL(#-~(!q52_(Pq=xx0%cL36 z|B>Y{4F%%CY>a>&q$SM+cx|D&`^MfV;a7Q_AKTB3L?X3~a;^LxugzC?rVXQ!X#dr_s-S#iuJ|3J-6x5y}aXd z^~I7)Zv$#d)?kVj1d&Thfa+HHbbO;SPVnBYU0TGILZdQ4skF#8(3h1J5vp0mluy&_ zg;8AIu5RTBmQ;pUi>KjDOjLyL_rSB*S({_GLF{}$?0MvX zzP31LT{|0RwIWH;Q^f2uos0XetB+|5oJM;O!-_u1_Bk8fx;7xEt=0>BW!<>Q|9B9Q zAA&)CDgi1%Dj_OiB0cel6@pOI`ysThpRlf3W)oqZboZQRb6ygf!EtnjIV3rmzdhXv z;i(}ZL+I-4=V-N=hn4pZ$VnD`JhG2$ZMsEf$#v(=qhF~Ur#v-P4W=)ot zAVr#tw}f&NGxOpT(^Kbp%AR#7JGbrVopwc z{7Qz;AhT}yIa|eq7N-^!$5@ydnwiD8q!wqFWHa^HWN5QtgU3fyOfeaj^i9_`uA_$at5*>;bp@ N1ulg~_9AwmAOOCXOZfl* literal 0 HcmV?d00001 diff --git a/src/agent/agents/__pycache__/browser_agent.cpython-312.pyc b/src/agent/agents/__pycache__/browser_agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3591b5e1253e7addd7b163d9a64f8e165ae1cece GIT binary patch literal 259 zcmX@j%ge<81pl4XvRr`lV-N=h7@>^M96-i&h7^VyiaOk zQC?^M96-iYhG2#whIB?vrczrV_vNCN=Tm3C-!tpk z*8UgEJD$#5@pSD3g~asKyb^`yvsx62O7luGb5j+bZfJWtXT^*CGhep%JY6^I$yiaOk zQC?^M96-i&h7^V(&Ff%kWi*ZRU&MwI>h;alN8Sj&tSd^EU zmmZ&+nU`M#5sWD=N{#{B2qBAOlJiqi^M96-iYhG2#whIB?vrcx^)_vNCN=Tm3C-!tpU z?#0g+G(KHBK_M|cHLpbB`K%U&qSCyQ%-mFkryJUy&ROwd|IC-|Jx|xoda|?gaYK`z zCgUyk`1q9k(&Ff%kWi*ZRU&MwI>h;alN8Sj&tSd^EU zmmZ&+nU`M#5sWD=N{#{B2qBAOlJiqi$XAg7 literal 0 HcmV?d00001 diff --git a/src/agent/agents/__pycache__/main_agent.cpython-312.pyc b/src/agent/agents/__pycache__/main_agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb84c50911070efaea43fc485b5f299e8035b1a9 GIT binary patch literal 3126 zcmZuzYfKy26~1GSpV*j~hXbs-4v!^n3v3(YO~|ThUaPINfheR}g|Z!+@dTWBY&0Gh zkc{F@LVyGoqRmTTm6FY_l86)%t&~j`NYw40O8wC|tYl{jzm~DhuaL(|Rn;Fo_l^fU z4OjBL=bm%!Ip>~x&gc1u)oMmC{`utHqkjbIN4!yoMj@z|0U$0RA8JQF%BKlXyrx}4 zVOkr|@^m{*XgZ+d_3e5>>jDPe*lr}WK49X_?Pfw70tLLK-2$}n6l%9>5NDmvdh-_g z3Vfzhbi1wJ?6O4mf%BF7bIx5|Tu^jot`Dvbj>&^x%j4Id&0mqf{aPOU_Q%+#shij2 zt7n0bCm*id|19%pB6Dptb?Kfwc46fq@K2b{>2K!JbAOg+uBCmOguj*W<%V#nigHiBU`1lEEPU>*@Uf$5I$UU0;U{!q~4A%SM^ zczRHtd7uPZ@9g44Mht}l;jOM_XD%J`_$;_qvamXNn#54=3AXD)YOnXblQ24+r^4bNM=@Z|E}M)Fl~q14(;2|95~pz z|G=SUr;^ar_(b}v?=rJvW-~;S8o4Fky^xCCPy@%j(UJ6Lv+|cG9b1yl;a1Lx5If|#r5T)qyld+7!tiW;bGWrdLs$9C`k(`1et{t zUDgKd?Qf<5xTf{TVY4MnmI9cy2vILDQ>1Bd!yuj-Kok;bRdE1XXFwww)V^{)x&de_4T{q1GF?=+7k#o9p#gnVf7GI$&8 z6)YR`2Poj%b9yQ@0PP>7XcR?99V#!kMzucuZNqdHJ_Zb8xhnJN#7dQpYNP8qQ3N~f zUNmlGHGafuKC!_qjZx!9w~QJ;*NkqWK?^jfsOGkDI-AEfRE@+E)qV{Uv=lmwM%#K( zpY|~7rC5j>pXx;}Q=|#>Qd197V>9x?=kmxH;7fX8W_4&Rb@5K>>M42dyIjAm;|dg% zfUhO!U|Rh@rC- z1OB8!=Jg0|U_U=ZNv~iLaVHtn4$~WY`lWJ4 z>HHj5MS7tITvxy^`~;>LlIfo>mDa^tl4a$w)+KA%*+0ZSSkstw)@9>wpW15{?e7d5 z{#9HtyeDb1kN1uAU3M?pwoI`%j$J>NEG|nF*E}h%N!D&n)Vkxf?unLURc)fGDPGky zVS8Fsx>kTnD%b32^IMmWpF2MFr~B@SYJ7?Tdvwl9r-m zVad~NI~L2D@49a`O^BEJ&-G8$oI8HU9WQH6menL3@4T`ZE&4TN)an2I%7iSXFA$}( zJ~dj_w6Om7g&jF|hO6LOSGMflj^-^e{nbnNhi5E9psE>CZRD5oJ)c;snqRyg^yPy23J(78JUY@+F09X3Vy;SVem4}n?PoJ5+ph8rOORF?E0iQ6#hTuPu^ta0@ z6=#v5+bed4MCDUJvaWA^_YZVAG-;x=K3-ZM+kL8K$+jh7tBc#}VDsUU!NHQ>tFT^s8WM-Hn5fE_2>AM)p^F8>J^k F{|Cnvwu}G( literal 0 HcmV?d00001 diff --git a/src/agent/agents/__pycache__/main_agent.cpython-313.pyc b/src/agent/agents/__pycache__/main_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c784715bbcc41573d5e9322cada4e695860d8abd GIT binary patch literal 912 zcmY*W&1(}u6o0#$&Btz%wy78(uWM%?J(K~y zaOdyQLm-`eV&D}RU`Q1xOH`WUSgy!rg(?Col}K5os=$#-v>c-`fz?X9oS+GTqZO^J zQ+*DQo`t2bHP9qdYtsW~$YcQ}jhGQH$~28)Suhev;T%dYu_^gMD)W-#a1T6rk$vNE zdj!CR+H4pfzBd8O)iD4a=2)f!C}C*DC>?F;o*XjUkV}5!B{?b3O~-M4)3;p5WA4~i z$7(gO*(8pSo78OYFj8r?Y-W-r&el4PZ`p+TnQH0IDmLyL8#k+Xy|z|e-(YN_Nqp?P zZp#ZIX2*9KrE?e8cU-GZf{1NePA(cm9MdL&Dm19dh6&WLDkTiw>SB?R^17Z+Y}}@< z-S$^=V}XY8cE|5ff-zNi;glFRUw0|Vi+)&aK`4#zaC#`rHVMTtLnIAXqEVhb9R_S- zyR@7CPn^bhf*vM_DQTQD8eeI!v$kb9jRBqzyzMqf3qQ5|osgzUUXu|q(Fzar*-J%4 zSBWJo2J6?C(WXbJhb|T_6bk4D@gDnb8{dp(K5U}^iL$; z?gsJ8Y?e-otkBa;fvb29yS^)uB=sTt6$*Wr?ZZMJihY>>oSJ<8;F;E&nt7XkliuI` z@Z@mn)&Wc$Pt5lcBgfjzUiskMl_PDX7uS2*spIp-qs;RA-F^Rc@kMd3@$%}s-NVds dFLUNcDmEz}Lgx2R4aP5d{FOhZCrb;Xe*j&62B!c3 literal 0 HcmV?d00001 diff --git a/src/agent/agents/__pycache__/multi_agent_runtime.cpython-312.pyc b/src/agent/agents/__pycache__/multi_agent_runtime.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ef8c03dfa8f3666d7f34418302a85699a730537 GIT binary patch literal 9756 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 RlMl>{jEuJ!9Ew dict: + """重新加载配置并重建主 Agent 运行时,返回当前状态供调试。""" + global main_agent_runtime + import agent.chatbot_agent as ca + main_agent_runtime = _build_main_agent_runtime() + # 更新 chatbot_agent 中缓存的引用 + ca.main_agent_runtime = main_agent_runtime + ca.agent_graph = main_agent_runtime.graph + ca.tools = main_agent_runtime.tools + ca.TOOLS_BY_NAME = main_agent_runtime.tools_by_name + ca.llm = main_agent_runtime.llm + return { + "name": main_agent_runtime.name, + "tools_count": len(main_agent_runtime.tools), + "max_iterations": main_agent_runtime.max_iterations, + } diff --git a/src/agent/agents/multi_agent_runtime.py b/src/agent/agents/multi_agent_runtime.py new file mode 100644 index 0000000..ef9b7e5 --- /dev/null +++ b/src/agent/agents/multi_agent_runtime.py @@ -0,0 +1,252 @@ +"""多 Agent 运行时:一主多子 Agent 体系的初始化与管理。 + +系统定位: + 提供 ``MultiAgentRuntime`` 将主 Agent 与多个子 Agent 组装为 + 完整的 SupervisorGraph 运行时,供 Web 层调用。 + +与单 Agent 的区别: + - 使用 MultiAgentState 替代 AgentState + - 使用 build_multi_agent_graph 替代 build_agent_graph + - 额外维护 graph_events / agent_status / tool_calls_history 供前端 graph 面板 + +可扩展性: + - 支持运行时动态添加/移除子 Agent + - 支持并行子 Agent 执行策略 +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any + +from agent.core.multi_agent_graph import build_multi_agent_graph +from agent.core.multi_agent_state import ( + AgentStatusInfo, + GraphEvent, + ToolCallEvent, + init_multi_agent_state, +) +from agent.tools.tool_agent_call import AgentCallTool + + +@dataclass +class SubAgentConfig: + """子 Agent 配置容器。 + + 字段: + name: 子 Agent 名称。 + graph: 编译后的 LangGraph 图。 + llm: 原始 LLM 实例。 + tools: 工具列表。 + tools_by_name: 工具名 -> 工具实例映射。 + system_prompt: 系统提示词。 + icon: 前端展示图标。 + """ + name: str + graph: Any + llm: Any + tools: list[Any] = field(default_factory=list) + tools_by_name: dict[str, Any] = field(default_factory=dict) + system_prompt: str = "" + icon: str = "" + + +@dataclass +class MultiAgentRuntime: + """多 Agent 运行时配置容器。 + + 字段: + name: 运行时标识(如 "multi_agent_v1")。 + graph: 编译后的 SupervisorGraph。 + llm: 主 Agent 的 LLM 实例。 + main_agent_name: 主 Agent 名称。 + agents: {name: SubAgentConfig} 所有 agent 配置(含主 agent 信息)。 + graph_events: 最近一次执行的图事件列表(浅拷贝)。 + agent_status: 最近一次执行的 agent 状态。 + tool_calls_history: 最近一次执行的工具调用历史。 + """ + name: str + graph: Any + llm: Any + main_agent_name: str = "main" + agents: dict[str, SubAgentConfig] = field(default_factory=dict) + graph_events: list[GraphEvent] = field(default_factory=list) + agent_status: dict[str, AgentStatusInfo] = field(default_factory=dict) + tool_calls_history: dict[str, list[ToolCallEvent]] = field(default_factory=dict) + + +def build_multi_agent_runtime( + main_runtime: Any, + sub_runtimes: list[dict[str, Any]] | None = None, +) -> MultiAgentRuntime: + """根据主 Agent 和子 Agent 运行时列表构建多 Agent 运行时。 + + 输入: + main_runtime: 主 Agent 的 AgentRuntime 实例(name/graph/llm/tools/tools_by_name/system_prompt)。 + sub_runtimes: 子 Agent 配置列表,每项为 dict,含: + - runtime: AgentRuntime 实例(必填) + - icon: 前端图标(可选,默认 🔧) + sub_runtimes 为 None 时构建纯主 Agent 的单 Agent 运行时。 + + 输出: + MultiAgentRuntime 实例,其 graph 为编译后的 SupervisorGraph。 + + 示例:: + + from agent.agents.main_agent import main_agent_runtime + runtime = build_multi_agent_runtime( + main_runtime=main_agent_runtime, + sub_runtimes=[ + {"runtime": code_agent_runtime, "icon": "💻"}, + {"runtime": research_agent_runtime, "icon": "🔍"}, + ], + ) + """ + sub_runtimes = sub_runtimes or [] + + # 组装主 agent 工具(含 agent_call) + main_tools = list(main_runtime.tools) + [AgentCallTool()] + main_tools_by_name = dict(main_runtime.tools_by_name) + main_tools_by_name["agent_call"] = AgentCallTool() + main_model_with_tools = main_runtime.llm.bind_tools(main_tools, tool_choice="auto") + + # 子 agent 配置列表 + sub_agent_configs: list[dict[str, Any]] = [] + agent_configs: dict[str, SubAgentConfig] = {} + + main_agent_name = main_runtime.name + + agent_configs[main_agent_name] = SubAgentConfig( + name=main_agent_name, + graph=main_runtime.graph, + llm=main_runtime.llm, + tools=main_tools, + tools_by_name=main_tools_by_name, + system_prompt=main_runtime.system_prompt, + icon="🧠", + ) + + for sr in sub_runtimes: + sr_runtime = sr["runtime"] + sr_icon = sr.get("icon", "🔧") + sa_config = SubAgentConfig( + name=sr_runtime.name, + graph=sr_runtime.graph, + llm=sr_runtime.llm, + tools=sr_runtime.tools, + tools_by_name=sr_runtime.tools_by_name, + system_prompt=sr_runtime.system_prompt, + icon=sr_icon, + ) + agent_configs[sr_runtime.name] = sa_config + sub_agent_configs.append({ + "name": sr_runtime.name, + "graph": sr_runtime.graph, + "tools_by_name": sr_runtime.tools_by_name, + }) + + # 构建 SupervisorGraph + graph = build_multi_agent_graph( + main_model_with_tools=main_model_with_tools, + main_tools=main_tools, + main_tools_by_name=main_tools_by_name, + sub_agents=sub_agent_configs, + main_agent_name=main_agent_name, + ) + + return MultiAgentRuntime( + name=f"multi_{main_agent_name}", + graph=graph, + llm=main_runtime.llm, + main_agent_name=main_agent_name, + agents=agent_configs, + ) + + +def get_agent_graph_snapshot(runtime: MultiAgentRuntime) -> dict: + """获取当前多 Agent 运行时状态快照,供前端 graph 面板渲染。 + + 输入: + runtime: MultiAgentRuntime 实例。 + + 输出: + { + "agents": [{"name": str, "role": "main"|"sub", "icon": str, "status": str, "error_message": str}, ...], + "edges": [{"source": str, "target": str, "highlighted": bool}, ...], + "events": [...], + "tool_calls": {agent_name: [ToolCallEvent, ...]}, + } + """ + agents_list = [] + for name, cfg in runtime.agents.items(): + status_info = runtime.agent_status.get(name) + status = status_info.status if status_info and isinstance(status_info, AgentStatusInfo) else "idle" + error_msg = status_info.error_message if status_info and isinstance(status_info, AgentStatusInfo) else "" + agents_list.append({ + "name": name, + "role": "main" if name == runtime.main_agent_name else "sub", + "icon": cfg.icon, + "status": status, + "error_message": error_msg, + }) + + # 构建边:主 agent 与每个子 agent 之间两条单向线 + edges = [] + main_name = runtime.main_agent_name + for name in runtime.agents: + if name == main_name: + continue + # 检查是否有消息传递事件 + highlighted_to_sub = any( + e.event_type == "message_pass" and e.source_agent == main_name and e.target_agent == name + for e in runtime.graph_events + ) + highlighted_to_main = any( + e.event_type == "message_pass" and e.source_agent == name and e.target_agent == main_name + for e in runtime.graph_events + ) + edges.append({ + "source": main_name, + "target": name, + "highlighted": highlighted_to_sub, + }) + edges.append({ + "source": name, + "target": main_name, + "highlighted": highlighted_to_main, + }) + + # 序列化事件 + events_list = [] + for e in runtime.graph_events: + events_list.append({ + "event_type": e.event_type, + "source_agent": e.source_agent, + "target_agent": e.target_agent, + "agent_name": e.agent_name, + "tool_name": e.tool_name, + "tool_args": e.tool_args, + "tool_result_preview": e.tool_result_preview, + "timestamp": e.timestamp, + "message_preview": e.message_preview, + }) + + # 序列化工具调用历史 + tool_calls_serialized = {} + for agent_name, calls in runtime.tool_calls_history.items(): + tool_calls_serialized[agent_name] = [ + { + "tool_name": c.tool_name, + "tool_args": c.tool_args, + "result_preview": c.result_preview, + "status": c.status, + "timestamp": c.timestamp, + } + for c in calls + ] + + return { + "agents": agents_list, + "edges": edges, + "events": events_list, + "tool_calls": tool_calls_serialized, + } diff --git a/src/agent/agents/research_agent.py b/src/agent/agents/research_agent.py new file mode 100644 index 0000000..76a7d8a --- /dev/null +++ b/src/agent/agents/research_agent.py @@ -0,0 +1,12 @@ +"""研究子 Agent(预留)。 + +系统定位: + 计划用于深度检索、文献综述等专用任务,与 main Agent 通过 team 模式 + 或子图委派协作。 + +可扩展性: + 实现步骤建议: + 1. 定义 RESEARCH_SYSTEM_PROMPT 与精简工具集(web_search、RAG 等) + 2. build_agent_graph(tools, model) 或自定义图(含检索节点) + 3. 导出 research_agent_runtime = AgentRuntime(...) +""" diff --git a/src/agent/chatbot_agent.py b/src/agent/chatbot_agent.py index 53bd378..5579c56 100644 --- a/src/agent/chatbot_agent.py +++ b/src/agent/chatbot_agent.py @@ -1,280 +1,51 @@ -from typing import Any, Literal, Sequence, Annotated, TypedDict -import json +from __future__ import annotations -from langgraph.graph import StateGraph, START, END -from langgraph.prebuilt import ToolNode -from langgraph.graph.message import add_messages -from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, ToolMessage +from typing import Any, Literal -from agent.utils.image_utils import image_bytes_to_openai_image_url_part -from agent.llm import llm -from agent.tools import get_tools -from agent.utils.agent_utils import ( - assistant_text, - get_history, - summarizer, - get_session_meta, - update_session_meta, -) -from web.ui_session import ( - rewrite_user_message_files_to_session_dir, - session_dir, - sync_manifest_from_chat_history, -) -from agent.memory.system_prompt import MAIN_SYSTEM_PROMPT +from agent.agents.main_agent import main_agent_runtime +from agent.core.runtime import continue_after_human_action as continue_runtime_after_human_action +from agent.core.runtime import execute_agent as execute_runtime_agent -# ---------- 1. 工具与模型 ---------- -tools = get_tools() -model_with_tools = llm.bind_tools(tools) +AgentMode = Literal["agent", "team"] -# ---------- 2. 定义状态 ---------- -class AgentState(TypedDict): - messages: Annotated[Sequence[BaseMessage], add_messages] -# ---------- 3. 节点定义 ---------- -def call_model(state: AgentState) -> dict: - """调用 LLM 生成回复或工具调用""" - response = model_with_tools.invoke(state["messages"]) - - # ----- 打印 token 使用情况 ----- - usage = response.usage_metadata if hasattr(response, 'usage_metadata') else {} - if usage: - input_tokens = usage.get('input_tokens', '?') - output_tokens = usage.get('output_tokens', '?') - total_tokens = usage.get('total_tokens', '?') - print(f"[LLM] Token使用: input={input_tokens}, output={output_tokens}, total={total_tokens}") - - # ----- 打印工具调用信息 ----- - if hasattr(response, 'tool_calls') and response.tool_calls: - for tc in response.tool_calls: - # tc 是 ToolCall 对象,可直接访问 .name 和 .args - tool_name = tc.get('name') if isinstance(tc, dict) else tc.name - args = tc.get('args') if isinstance(tc, dict) else tc.args - if isinstance(args, str): - try: - args = json.loads(args) - except Exception: - pass - print(f"[LLM] 工具调用: {tool_name}, 参数: {args}") - - return {"messages": [response]} - -# 工具执行节点(LangGraph 内置,自动解包 content_and_artifact 到 ToolMessage.artifact) -tool_node = ToolNode(tools) - -def process_tool_artifact(state: AgentState) -> dict: - last_msg = state["messages"][-1] - - # 日志打印 - if isinstance(last_msg, ToolMessage): - content_preview = str(last_msg.content) - if len(content_preview) > 200: - content_preview = content_preview[:200] + '...' - print(f"[Tool] 工具返回 (id={last_msg.tool_call_id}): {content_preview}") - - # 只处理 ToolMessage 且有 artifact 的情况 - if not isinstance(last_msg, ToolMessage) or not last_msg.artifact: - return {} - - artifact = last_msg.artifact - - # ---------- 统一提取所有 image_bytes ---------- - img_bytes_list: list[bytes] = [] - - if isinstance(artifact, dict) and "image_bytes" in artifact: - # 单张图片:artifact = {"image_bytes": b"..."} - img_bytes_list.append(artifact["image_bytes"]) - - elif isinstance(artifact, list): - for item in artifact: - if isinstance(item, dict) and "image_bytes" in item: - img_bytes_list.append(item["image_bytes"]) - elif isinstance(item, bytes): - img_bytes_list.append(item) - - # 兼容 artifact 直接就是 bytes 的情况 - elif isinstance(artifact, bytes): - img_bytes_list.append(artifact) - - if not img_bytes_list: - return {} - - # ---------- 将图片转换为多模态 content ---------- - content_parts = [] - failed_count = 0 - - for img_bytes in img_bytes_list: - data_url = image_bytes_to_openai_image_url_part(img_bytes) - if data_url is None: - failed_count += 1 - continue - content_parts.append(data_url) - - # 所有图片都处理失败时的降级方案 - if not content_parts: - human_msg = HumanMessage( - content="工具返回了截图,但图片处理失败,请向用户说明。", - additional_kwargs={"synthetic": True} - ) - return {"messages": [human_msg]} - - # 追加引导文本(放在最后,也可以放在最前,取决于你的需求) - content_parts.append({ - "type": "text", - "text": "上面是工具返回的截图。" - }) - - # 如果有部分图片失败,可以在 text 中提示(可选) - if failed_count > 0: - content_parts[-1]["text"] += f"(其中 {failed_count} 张图片处理失败,请忽略)" - - human_msg = HumanMessage( - content=content_parts, - additional_kwargs={"synthetic": True} - ) - return {"messages": [human_msg]} - -# ---------- 4. 条件路由 ---------- -def should_continue(state: AgentState) -> Literal["tools", END]: - last_msg = state["messages"][-1] - if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: - return "tools" - return END - -# ---------- 5. 构建 Graph ---------- -workflow = StateGraph(AgentState) - -workflow.add_node("agent", call_model) -workflow.add_node("tools", tool_node) -workflow.add_node("process_tool_artifact", process_tool_artifact) - -workflow.add_edge(START, "agent") -workflow.add_conditional_edges("agent", should_continue, { - "tools": "tools", - END: END, -}) -workflow.add_edge("tools", "process_tool_artifact") -workflow.add_edge("process_tool_artifact", "agent") - -agent_graph = workflow.compile() - -# ---------- 6. 辅助函数:保存消息(过滤合成消息) ---------- -def save_messages_to_history(session_id: str, user_msg: HumanMessage, assistant_msg: AIMessage) -> None: - if not session_id: - return - history = get_history(session_id) - # 只保存非合成消息 - if not user_msg.additional_kwargs.get("synthetic"): - history.add_message(user_msg) - history.add_message(assistant_msg) - -# ---------- 7. 核心执行函数 ---------- def execute_agent( chat_history: list[dict[str, Any]], session_id: str = "test", output_type: str = "text", + agent_mode: AgentMode = "agent", ) -> list[dict[str, Any]]: - chat_history = chat_history or [] - - if not chat_history or chat_history[-1].get("role") != "user": - return chat_history - - # 上传文件处理 - if session_id: - rewrite_user_message_files_to_session_dir(chat_history[-1], session_id) - - user_content = chat_history[-1]["content"] - - # 上下文感知构建 - messages, context_exceeded = summarizer( + """Web/API 层入口:使用 main_agent 运行时处理用户新一轮输入。""" + return execute_runtime_agent( + runtime=main_agent_runtime, + chat_history=chat_history, session_id=session_id, - agent_name="main", - system_prompt=MAIN_SYSTEM_PROMPT, - user_content=user_content, - llm=llm, + output_type=output_type, + agent_mode=agent_mode, ) - if context_exceeded: - chat_history.append({ - "role": "assistant", - "content": "上下文已满,请新建会话继续对话。", - }) - if session_id: - sync_manifest_from_chat_history(session_id, chat_history, "") - return chat_history - graph_config = { - "configurable": {"session_id": session_id}, - "metadata": { - "output_type": output_type, - "audio_output_dir": session_dir(session_id) if session_id else None, - }, - } +def continue_after_human_action( + chat_history: list[dict[str, Any]], + session_id: str, + approval_id: str, + decision: Literal["approve", "reject", "edit"], + instruction: str | None = None, + output_type: str = "text", +) -> list[dict[str, Any]]: + """Web/API 层入口:用户对待确认项决策后继续执行 main_agent。""" + return continue_runtime_after_human_action( + runtime=main_agent_runtime, + chat_history=chat_history, + session_id=session_id, + approval_id=approval_id, + decision=decision, + instruction=instruction, + output_type=output_type, + ) - try: - # 调用自定义 Graph - result = agent_graph.invoke({"messages": messages}, config=graph_config) - # 提取最后一条 AI 消息 - all_messages = result["messages"] - assistant_msg = all_messages[-1] if all_messages else AIMessage(content="") - text_content = assistant_text(assistant_msg.content) - - # token 统计更新 - usage = assistant_msg.usage_metadata if hasattr(assistant_msg, 'usage_metadata') else {} - total_tokens = usage.get('total_tokens', 0) if isinstance(usage, dict) else 0 - if total_tokens > 0: - meta = get_session_meta(session_id, "main") - update_session_meta( - session_id=session_id, - agent_name="main", - cursor_index=meta["cursor_index"], - compressed_content=meta["compressed_content"], - last_total_tokens=total_tokens, - ) - - # TTS 处理 - output_type_from_config = graph_config.get("metadata", {}).get("output_type", "text") - audio_data = llm.last_audio_kwargs or {} - if audio_data.get("output_type") != output_type_from_config: - audio_data = {} - - assistant_meta = { - "output_type": output_type_from_config, - "autoplay": False, - "audio": audio_data.get("audio"), - } - - if output_type_from_config == "audio" and assistant_meta.get("audio"): - assistant_content = [] - elif output_type_from_config == "both" and assistant_meta.get("audio"): - assistant_content = text_content - else: - assistant_content = text_content if text_content else "(空回复)" - - chat_history.append({ - "role": "assistant", - "content": assistant_content, - "meta": assistant_meta, - }) - - # 持久化历史消息(注意过滤掉合成消息) - save_messages_to_history( - session_id, - HumanMessage(content=user_content), - AIMessage(content=text_content), - ) - - llm.last_audio_kwargs = None - - except Exception as e: - import traceback - print("[ERROR] 调用模型时出错:") - traceback.print_exc() - err_text = "抱歉,我遇到了一些问题,请稍后再试。" - chat_history.append({"role": "assistant", "content": err_text}) - - if session_id: - sync_manifest_from_chat_history(session_id, chat_history, "") - - return chat_history \ No newline at end of file +agent_graph = main_agent_runtime.graph +tools = main_agent_runtime.tools +TOOLS_BY_NAME = main_agent_runtime.tools_by_name +llm = main_agent_runtime.llm diff --git a/src/agent/core/__init__.py b/src/agent/core/__init__.py new file mode 100644 index 0000000..c2a4525 --- /dev/null +++ b/src/agent/core/__init__.py @@ -0,0 +1,16 @@ +"""Agent 核心运行时包。 + +包含: + - state / graph_builder: LangGraph 状态与图拓扑 + - nodes / routing: 图节点与条件路由 + - runtime: 对外执行入口 execute_agent / continue_after_human_action + - llm: ChatQwen 多模态网关 + - approvals / human_interaction / tool_policy: HITL 与安全策略 + - plan: Plan 模式 UI 构建 + +系统定位: + Agent 业务逻辑中枢,``agents`` 包在此之上组装具体 Agent 实例。 + +可扩展性: + 子 Agent 可复用本包组件,仅替换 tools、system_prompt 与 graph 变体。 +""" diff --git a/src/agent/core/__pycache__/__init__.cpython-312.pyc b/src/agent/core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef548deafaecf5522bddbe4f0202629d4ee0276b GIT binary patch literal 762 zcmXw1O;6N77;cd$*@XBP=E^~d7ov?ZF_7nT=CK& z9yM@I0Rqq}jYs2$0?a>G!eGC3(1_-DJ9x*#)Uc4iH4a&Ikr%gwFXnHu@&-Wfk?v6h zwYKqopc8?`o=Ci+$bqAtrD3S))F8OSW}21)21hme>>a{@RhN0KB78>2v&`S?U^Lry z$Xm^fT_zL;7IX=Z$WcN}XcN=W^9pz}^gIpXGIxs1uSNC6XkoL%W1AXQz_9oNp~8;J zU}OmJwFwsdNTs5^APT;)o#pU&I%#Us{jj;rN|j%Qpmj1EHtT$C{rAySCeyCa&1674 zhSeIcSHr`}uvvGr zHNN^?kP0Hvd7%{sK3_TI)9dXz-Sfhjn|CR)plFI^)40}T>VTC$3DsEWX`g&{1HvrP zA>5WO;}!dx9#qs37dftaci_&zfcglzZz!?Vwn>;qnuEpop=Ds=BuUkEbhTZmsuKsP r5E|H^5Z<=)XFc~VlJ!jVz_~8S<0W?E9shGpmgS4Cn{v0)Ew29o*nmvn literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/__init__.cpython-313.pyc b/src/agent/core/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5353092cd15f5790851c39e1df42ec78290f5df GIT binary patch literal 762 zcmXw1O;6N77;cd$*@XBP=E^~d7ov?ZF`6jJ#$c3NliAuGmufrHOs6Wh1?gcIa3OqX z0MUgdEYW2{NMNyi{12{lrt3)~R}W6N>w9Qt-aPX>&--!N-=9%r{EY`+jr1tWMRNHc zYWHy2Cl5=?GlePzb)d(;BZ2n|2s0qQw)olm_Ssy!IwL+F@R{JEIGM@FKcD-iWq^zu zz+(nOU=S3j;f}!40FP8(!7}n~d zQHzeIqgFW%r`nqV4-TV~1&}iE-Sy~nI-OaiDM~whZ%GulQxV>n`wy!$d2Kdctc$hp zl2j7O&P%N*48-c0nBC~q>0J~jym5~q2g;^6E=y`nrw(}KlT=NFo(!nx*C5Oj6T%(k zGFh?ntf}dv9-^Lpd+65Ckp2*PuNiUmj!BqB2E}sx(6KO~X;Sy7sdoz1O+t|lp@l65 m;Z1k^yzj0<^1hAk(W{a?Sz<5Q@!wZeRsGX*UG1g4^7;=d{Y?7+ literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/approvals.cpython-312.pyc b/src/agent/core/__pycache__/approvals.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cc2daba55025555232b1d2757e7ab32b77379c4 GIT binary patch literal 6702 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/approvals.cpython-313.pyc b/src/agent/core/__pycache__/approvals.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a15939337f629173b38a850fc68d0530582db6fd GIT binary patch literal 6675 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/config_manager.cpython-312.pyc b/src/agent/core/__pycache__/config_manager.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe336dccb752be7de0753bdbfece7c8f9548aae6 GIT binary patch literal 14029 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/llm.cpython-313.pyc b/src/agent/core/__pycache__/llm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f882e023e0cc05b536ae0f6cf99f3d1a82dd528c GIT binary patch literal 31945 zcmc(|d3Y4p*(X}PZ`$`=tzC`Kf+RMvSrVHC5}>A8EII8)Y6*eVlB*>!IB|&MMR-H< zhOn^_4vB?h8~KaBSlEssPQDCtzf7jvP12o;;_%!L332C{Cn7L$#=du+`MsyQx?56X zJM+&i#HrJD);i0(zo%|TM(R0SBU=g&Jn!JR|DYezrSnH_zpvo9mpC)Wb7q-YzD35% z*j>)c*ph*09q z7ME{{=A&8p7(NDf^_JL%SU%357SG4ynPy8uLn5Epki;jk=h`jF4Jmv|Ln@!zkjAGq zr1R+w8GHsy({0IY$l|javiWTGTffEFki+M&xM53fLmr>U;t^Z&8w&V>hC;rup@=VP zDCUbBOuVUK5xxw9@N-m`vxz^>)4=t<#ITJ(jP|2iu zzmwAc=j zPcmEAHXmD9f0&$Et+k z_saCx+390HFmAOuI#H<0VVwEFQ&Z>8y}Kl|v$M`xzqedYQuo~_Xv zaos$7@y4s~-u!4}=G}9~ty>$7Q_sG1{gbDS4?NJ_-SfZ$fBpEGnKyoFv~}7Y{Z8YJ zkA_ip>VqFoJ^lewrY`<``uL^kryobxSY@Tg8!wJc4}LKH^TFvKT)6oLI`!C%p(phT{=zcfDm?7RLfH(z^wdi2SuC!a9(_x5&M+icz4#yyQ&%Nxwwnv64V z{%HF3A%D(YUCypvhjIGEiJ42|)6e~Q>co%q`s<&(HuJA9i!X0?9I_7dcXd0B*C*b= z7}5Y(7a7Zq=!Uhf*1W-r&fzCI9r&r&Po20hef<2?doN8NdsD2Z)QD$O?+o2|@1v<_ zUY_~M6E|Lc*=X(K?R|W2o89TO_8`C*I`Qh(Hr{UQw_DpVtybRd9O&-%cX#?T;J8!FKD;*^if*k$i-M?{OUxA%AT*iCXFZs&gVseLNengs&IQ#8A(ogM9 zR9TM+LL+)w$Ia}0g(!+|5?=>BLw8uJxUTUlzpDqMW!-nA-|n>b_gZ_g;B8%2@#z5H zZSAx1{U)_wp!vf3w{F?6SBRh=(xeGG`qj8?{f4b#uATQbZrEnoDMW-$tq>79vlzQ5 zo70JbvpM>${q~3Yh1m7IZ38{D_A#4mDh0i>ue-~itnab&=;vYEA*V?pAE&8i|4Vo%lVsjgLz zr<%Z1EqiKcjcAQjQBJK{M`>n#Yn0hw)>bPmT(!&`k;Wa+9#Hbptuf|8b7X6*IjS{| zrK`=+NK+n=1+Zg0^2D?zp!KL~In`-4g#M~lnq#R%Ab%qA$F*vZOM|rdIcZ5DDG8Lq zCz}&nQ(9B~_4;Edm&8)j%nDXhES@B@C+Vo2`fF&-Fl$RS9EX&YIc;Q`i-7O4%qo;j z!+(|w`Ln9E=5)$=K*nchaao+XIHc_ij95ymhWf$!l8N6@EXGC-rS)v&vQPs?4s{sO z4%I4W&$9#1bIc~Q5$)!%o@=Vr=3IZPxk$-F--9Xn{**j(0iSQ-*2wsRe9p8;(9)(N zL~)bQ;lMdi7WB&qm_IdmQbYm_#s$#B)c6l(egxqCj0jBv7zCh~AfR#PhrgJ5>Q#m~ zrrvu!0PT7Q`uhg31CI3BYm6f15J5Jz@$m^G?HuEGjkbaIu3p^t_4emvsiEhlM=nea{bc&|+f%23zm-*EJ$Ts0cRFi~)2ENk zTpGSUacuhSv#bKV(e%qt0=LY3aAE3`$I$iZlRv-t(xn^k91kFlyP!T1sg?HtQ(9S^ z(5Mk;57^*29B>N0D}R2JEK*)o}t>(rX`1IsZS z*FSph#&Z{@-uU6viIJO6o|qnd*(i>gacc0jsW+bn&YnwCO}+aDsF)i+y};(27Wd;n z7BRiF4zUjWXuhYn-QEpcZR_i@9<(1pY#$J!6<7*ULa>-k#CmKG0o(WUT|@+6A&EOi ztaSS1D0{Y$I-?!++Q}OqzA4gU#x+K1lNXyFc)(AqO~3xw&0}H>3zsJm4_Ka0jT-&S z%eo(^-p&�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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/multi_agent_graph.cpython-312.pyc b/src/agent/core/__pycache__/multi_agent_graph.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d31a527319d9ee972d1f396dcf82773c2c49f1df GIT binary patch literal 18581 zcmd6P3vd%xmUg$)ddrghw)~Q-B^GbB5^wGDK)`rdo)z2}~L?m6E*mw!o0GE#8;=l@->chQ3s^?UpwU25@#zCD+w zs8bY2)lnSHsha3!Rh^2)cXgAxPEDRn9Rp9MNz<&Y(>CksbR=EVq;EFV8JdlC#%5EU ziG0&GnVXa9lE}NR$${Kb)bLi#K&8PaD>rLWAxv0Mt|U=OJsv)c{8w#duRvnv{1Enar?gA-%_ zaGBlM-sa->yFIP^TE4z*&u3SjA06x*9lQ(~BX0!Q`7FGTUVb|A?kl4o^hW>jv9T-9 zMW24=_nkjA8lm{u<+G7DpCA3;sVXB2*XZE;Y=N?T0ULRy8$Th90yg^MJJA;(j~)w* zUOfXvA?MihkF)FRTN+8_Y~;*UNRM28|FbLIgkbdKrD*qKP(0S?NZ>fOO6)=8{K@Fs zS4S^jjy!n|3MdlZ?2h^ak-*cDGcS%_=^MLzBKl%4X);tmem!1QWh51B+_d81O>Fsc z)+6(Qe2@Rc#O8W;i?g1v&Bv~;ttMF%A0bOgwC}y>Q*W}~*48FxLw!?|2e|PDc8`=J zpB(J?yO+jJz7_EgU}GhM@w#GF?-J{B)=q60TM{o7QFIZ=DVpyv8b$VuKl#(KpT0Nt z#??q5HayWw$3_QVkJT$CN1uKrdh#9if%Yb^n-tsVt@pZso8upz1n#jvDQI?iJkU~4 zK^42LqM~9)8CyUa?({&;cKrMk_}o#(;@xPB9J>_ldM)zSv(e7;qEM8xijjfTqpw7J zyT*=Q9J_E7hU2p<{_!KPqY%B<#qMf%H*wBJQ4qVJ6{GK+1%i>6pBg*a&pzy0(cr~l zL{S5(knr;tBTx24-g{^KoMQN+UFYHV?>iqGf9HwlsdwPz){&>iFFiZ{BT2RAy3mPIPf{}2C$SM@o zZ-TL|ZRK1JMzG4vYxmT&>~0l|B-iTwFx3QW0(*rdEQxiHbf`70xqhFE$cVEU2sSxe z;4Y-M@vRNOekT#Cdfw~aUEkn^nysEat?j_6hSnCZyQSULpprBrJQ?^0Go8NO1TUv3 zAGH^hWa7oqjnsu$sScGlrcf!$r}AQXa!KiYichsyCw+}See{JGWtuvqI;3)_+lwF- z<|vD@;lc?p{^18OLPRCJ4x>H#{?XCPFd{z{%w&|EE@Tieo)L^5mzQ(xt_MjJjBby+ z1!%N1xCExj?ePlQ`nEP#3kTz&A+!YzDJf`00v_xDNX1v1S5~Zm)?MoLgT)cRD zwac^5+uF9B43BfYtDbLhw=_DN-7T%W_;I_3Z`e*GdV51F@7mssQz141NgFHL4hbfw ztJ&=pTg&G_T@Lu?nGd&4>eE?;H)qWsnl*pGa!nPQwW@Q?s4h7$x3_RuH!G~S982m- z3b;Pj=T4|8J$oC9Pz^GR;fFk2SK!CY!{v?-7vrOS%zlbr+kp}*8Xxfn>(KByH zp96&?NkFvw2+Z!7(gA%q{$T)S=h&0)L^~fNf}qqUf)>lQdxRtx&$sfpM^ITZjd1p9s<)+-|Qlv1w)L4r;*PmNp6^z9_%0p3S4|Peob?> zw{ak>u@M)m=TVLq!avV`xWN$H^KaT6Lv}}h#b80mzN~Y7SZDJ4hIBb$Yfew~kaf17 z88M{?xZdhv)9i5G9Dj8ntE=`CYgV{qVSouZZ`g`p_-*#lq|6?sw{5!{^2%ch@}l-JyvE0HDo&kEwSWcX(|BbiuvgixSm_I~=S9th7#*d0aA@1B zAj9~Z1CbA&1#2Sm#Jl4Ie+OzERfr`5m1e)0 zRydSa7%aMLuridkymM_>m)w&%q+`SRtX~%}4(W5kjLE-lh{?Rk~_$b!}926oxweWI;KEj{~ zRa3`Q4XQ?}LA3+Kt3%~ebz);<3AOQM~C{99PT-g@=9 z=*4s6AD)Rk@d{X@<8NLi>Z8Hy-XEK!#LPtN3k-y4-w_ZgVxW=Fe0vKjc&sZn9&BX= zF+j*zvZCE28aGJ&=quwN_JBPSx%x!p<;TYYKa**W4jvVkEm3uScI8YY@M^?=I)urQ@3$eOsDz5W|2NZx^TUg$d+ix^(+WK$+ZtRaN6ta|YDRplfkOH%WE*G;&+D zJ}v*4Pur!3)xRU5%|@v$Gq%|@Q=65PeA+wdJ+3Cx^?n9!nW1Jam*mr)p(d?cP1#-;t^Y#LIqRL*3ZwNkzQg)>2eqS}_HDR@a^YjnP@HWhZY6Nwe@I@x z4YwT0wW8UjmmEmJ49<(J#06bURf7kHjy1ZIy!SP=U?dj3(|ZVT3A88qa=fL$&7tNO z<2zb&%FL^wz#d2l!B4mERn;=byEow)a)_^C5esUq7Lx z=9Zq{dTwj~qoJbZeoNS751M9=n%Tbee(Rup*t}}QoY|x4Jsi#{3FpiUXXj1o7}hdL zF-g`5Gc|ibm@NylMduCY48$B~mwm2d(kv5@Zke!9=IkEl4O7|gisuIo_Nkun4Q%q? zHbt;4ppa=xRJa1Vkd*>mNKTowNHuf&oQ1)H%3$UFL3TZ61=|uvL7QZqv{6a3|2CtQ zH#eMH63(4{w&rwAU(3L*Q0`s83Y!sFVK5RK_BDpg2!1xs11<0Jw(~9I$7^Wz$CL*V zY`zAw&@KG^@Z{H_bfV46?qu0}W6s`mqRl@9nKoc<)GmMSq&_dC=2V0-)|@Ntd1xSc zpf$8`^)o;2D;>&M)4Ab~%r07EoVZU-nKOb+`k%jeKuu+?r9FBmdc;`3Ji`<)Qw$(< zl)#vvZ~s3qe9+?Vqz}`@Uk1ZLX|Y35y!f8!zgJT`y1-_q6^jAwl^rvg zYO;^sD;q8%MZ9ye3Dja%7#jec#|;4V2RIFyDBuq)`HuByCG#br9xa%1F*6JOiD}!Y zlgb{XJ;`7sOrJ|HvII!_pps41k13dpiN02KOTR!Z~(D4I6#O zAARf+z-0i9M~*!wn)n0`WMd%rhvy)JY^;kPqkUH*SB`;$VU%2*ayHW2FA`%B8-x`D zEdDnl-A|x@uyg0O>ecI4uU)ZebyeK4*s*h`qI}f<9FRyrvBB#Q+X_AdLi{^-ip9~b z-MNzmZ$Rm>6Kb`BG48JO1<-BY0XHG$QWJ-G0e;0LUeFMFf-ctP!^U_+d7Kc3Q?|#y zlx#mx>e#;H7CI6RBlzI;o_epB=Qkob1c-=s;8($$po3)>69u&gZYtgd025$<4SNJl zBj4KI2A=*}%%}1;2s&>ASulCht}RGt8>Z3CUvuyIYD~r3ui&5OcW~>ZK;i0bL0x9p znmVep_z(8v4(sOjFTGj1{6^{Wa7y;gl%nC3BLB*flFEU~x4pw9_XKvG+I@U?&z6%d z9~ECOxV}5M<&n_Jt^T!PYer95$U1k#v1nlB6=vA6EWn@Ie|&$>?vsyxwB&l__5Hyg zZVj!f3ozl#oKpvnAM7)SGRtpdt{g~vH|wpe!Q#-ul{aiFCk#~T+(|QK%?Rx2GxS%6 zEESz=M*y2F?p*}fWZ(LMwL^t>|5l$IHd~I>bkzhiO8b8pGJoGse_}F+Q`1f@IliQ4 z;fboCIWKIoihu$#UAh=}A*$^`c$pcGiYqwq>Oj2^k4Nj36&?6NG!I_EMoRh3!L-)_ z*^P0mVD!DCFli7$B%3H8_pvP(rCG^Fe)bQ57l`mdbbi`zd=kRY#%Z-7*s*B-=$0M|)MIkvTHD*!ZnC~02j zny@Y@V85Zu9qL&mR3MZk+!Q%l2tSShNCR9|f$9Ub<_2&E zAQ;L|DaI$;8$~b_Wj^eJ$GPiJ3@s7W8fZP-h>QJr^TpAt zN25J`k%7kncL9ElzW=m{YQ`B5Lt#j#u^UQYCEiAH0KNe{K}3>(CyPNU{}$j$9Dsm5 zAGD3g<)gZ6FiB&AI3s+Z?y`X!E4!M~`bWd&beDCn3|B~0!NQ<@D!05xMAmqvu*Wh^ z*@*%$2CSOT!K%@E<#iXJ1G7{c*daDpYtbD{xF98O`V^q}Pe4)UO_z%JbY6sf$t6*S zHC#6>!f?d2vgM zTzhfXAmQ>Eq*^)*Uc{}*g`@7%`wWtLxq#{%t{KPQHZ_0ThW#cW?_%=Tp`PyR4Ap%b zthA{Tm?Cj=#ulV@+3v8`X`~bbGnycnqr(Wn8kP=|w@B6=ve?U@}5!s7Niq>b8 zIE}KWkN}WMPPSlqx;r1FZ>~f${_HR-#?Soq@dK)N`^+6C8h}5BQc*sm&&2Dwj4m4h zK$$)hKxRyfCiXk)1vO{yvbpJt*;1=IlDxQxk;|u&dZPFy_wF#{@g+6V`0TLwEHl8A z`Z&ZoLk>e+kIaz6_*ZG<#B*@Dy_${G94c;)7gGE&fIz3phsJ9adkMXeA<@4owHlC_ zY07aFr_H`YF#4iSszu^Ccp@`_m49RIai(6TLA4b^oH{g*0++<+0r~)SP`#BpKqqjG zp{cD@fbO1@(j9rVJR(Ru?CDVaQoP*-H`qSM{Z$@3I|??z5CRjywiWnB5H}fRzbCu$akjK499wr+_419=bFs!I~KnyiG ztlPF??dsZ1RfknO*mhKSJ6JKwN8G(3^uR_#6J1EI=QuY;Z`3zA_Z<*7{lwKihT3@m z;+wAN7#-nAC+M7L`Jp~gpmSPx%l_7V04%OP*x+iz8hA8F`A6`U;A?D!ZvwN|)7m0v znp*2Q583P!GMgZ>sbLS|aTr=qA+?A{jVb5>O@u%N7e_k6qsnn4i&)2MtOFa3fCp}1 z3f9<0rbI%ZU4k0$KmJFU)rncre5MY80*1x7i}iRCBcLi1(XN@Du+vBc#4cK!bc@isQj*lmCo}AFr^dF!US}1QcD@~ zdN%j18!!y(t48#xfz`cL;mpEt=In5W9e}x9BLH&-(}cxlu!Pguo9V?v>Baro1CInt zmWR@R;9nCioOgcRxpn=AVOOC6NZPDE&C7?wb1K4f z%EJW?`1|wn{)a~j=AL()bG(F`xg{B}nVX*Zc`B9TfY5-nIg@iG_GpLoOJw#G0DDF< zvQN2>yHD(kGwDkUWmp_e&-5EdQgeDX{GZe^uj{{OC~fKW z#^9En!Oiu-`qtq3w(zXd{<>iP(n*av)#%qv>Zz>Up8HNbdXedOhDw$VWiGpxdCf4C zS>rbY25c{y3i~DeDkw%K1k3KdRuC#!9nPO~)_vL?ELjTi7x~LRH)#u~c5q3`BikGx~-hOqTc_cgktmCxfOzEUS zgQ%tkuw@ICS2Pt^PWT0|wfmeqxS;yluF%|?&kdS1%a>5n@|THZsQxdL3nr5K%{Cb?!xJ?hb`6u{JP?Dr6-U;u61P>Z*0Kvq9SyHE&kNl&MWdie}?gn^sqJ&B> zxRF-e_i*O}e`Fp+IFnNWY#wCnqDLAt1R=K~Itf<_@JLlh)TgK}wPZ9tsRo}Ey;AjV zh_;2m05IS;VmLrDOmxXBm4Y{w{;0TRjr#oAx8b`O+s8((zCQN!CAKt%dX4`b1dp9j zI#Z*APe-15R&?hOcVcmw5kuwx=z1y==*FNyd5=5hVf_r@FS#xwDmJo2pjStDiZ9c~ z+F^p2RcsniC&eM;F#1ww^dFCksWGTd;mAf`ctr&1g|NpCW2pF362O)UoS&*tbGwlKHmYqm2K4lXTg;l(i4y9xcgS(3%I^+V!$EtL9Idz9R zzO(O>Vex7zpxyygp}Y4sQ5ns#B39P zDesW-@6)G1yXYNYg@B6;{1Y#q){Dp|xg^>Il%9@1vhfT~C*oY>0w-I;$?*)}EPprJ zGGL!TTc>bn^$Oa0Zox!r%=p_Kvu4m~*HdV(l+duL0OaD;t2pI*{ZumxbefWRJH zFcNdbH*h1UxD&{krk1%gX}oCux_g@-X-RRnmN*U5jff@g49y_Z)+sL6bTn*n8eB3u z4H%q-+aWXaMRIHY`*{K`$u1cvG zpOsBXbMcPlIS%K^trIET;q13dT1Da;jn4NT23C&lSt6kGze5`kcm?f0asj*|0tKq> zJ_#rQAVHaK?MUL%d`UPy9cBVQBt4qc{TF~A;2DP>9N7wZ;TA@?P6r9_n;@Yb7r+FY zNYG(WKg4VDd$6b*Zv+^?rjQUH0zUAK@KaE^If50m<4Px*KH~NS!0QlFS;gXMj!gjI z$6y3O4GuBH3=!|wj0TN(zyin+#DZWSS_SqYBNRd^$(;~8jIf0xLj(-aS&yRzf`$i2 z-^DlMR|J-41PMe4p%t^V;jIO4gl&l6ieW(-Z+lyli~lixcpPqyltfU#&|H5I;-y?| z9)ZCJLJ6KWIKbkH!vXvOC@N@x1#S+anxXf+2fs)W(Xp*ozFn$D^f7FS)+pZr89a!$ zOQvD$*(renR%MVWq5|c_3rYAIhXoQ6JctXN0h?|rE-+%x>v5sa*KZtH8OmQ0vM=@P zNA?mvmE_J1KEQ=yw>>3 z=HNpcgPV5;_xyM`iyz6F)3^F!Rk*k^T)Z$mw;cZdym=r6p#*R|U)l$VKv_0~;Mj8@ zO2}T`zjvYLcnl}p}ryyXbq^B|xap~}tRxw1e; zW6uUM-wl@dBf=6BcT+hP6YFUzXU@0bWH>XQupykq-Vv@+ws?TQ^1b1*dwW)%U2}R( z-}idgf7Ez=SMcF2!AI&sYqkYeL!{WvoU);uGT6V)`F@~U$jq5gG4}iDaFOHuwsYG6 zgekhKXU#}e^|dX(-aK5jv46|Jir2ReZ`~f;(Gc3o_0)a|fCOaEV$WJnTVFGc6c+be zLWN7hj*1(OP#apX>RR=+jll&q@exkam1X~t(%EyqZ~${ZzlTcBnE0WJN-vtMQz@aJ zPqWwr#AHekRk&aZegY$ofQbeHlU1rjnBp&!Tj9Ak0VoQ3 z2sc)ViXjSxKaN@j&Dfm~O(Gq|nNDx;Cm_>y)DgHQdM;4>&k0qxj-fhWzsoD{;rh(+cU=9UjkvxhT)9&*p$|j;zH$`2Dy~-zGv@(uyuSnIyk#C)Q4?wy2c@r#O&WgYk z#A+EOdgAvxkNQX`NSF}s0-hX1=<(R4Tb*#exJ%BhG)lhZK^a9PisHD;s@CQf*p4M$C;w zR0-)=0&GrsuMX4UIEx;#Bt=)Bj`N!Vr;w~UPvDW8@x>s7QzO>Kw`X{&Gl^_)R4$h`4VG_ zGsQ#eUpqF;zXJJ(ap8IZZk^O*6=lorogcE!>%0$a*R0}TM)5C_*7fYl60=Bk5V4HQf!5)rJ}KO{QRoDd@Xrc;Sj`cMUTSC>kAT?eh&Y-}|j4 z3tA{u-&4@D<@CJ1rjPZDCe&D|mNKW^H02DLazduOAd~m!FILf1>3aIlUo4=K@ABX> z_nf)fq5b9E#_9?tcKBC}`$RmEI8yur@Fp;|a5}_6ll^IjF&;`TN;%olC5MQ~8zO_E zi-u+SSK$Wx%(%4;BGcZ`0Q=Jpi-N{{IY03o35^B#Z8qL89#EtfuOUvC78sOdX!{1p zci4c(+_!<|+d(8a-iog>R)P59e@3uv8>%n(4;aV;wuaVX1-M&7x_a0MrD6J$4l9D@ zNg|==plcm^aYKoeiZ>sfysy%cE;gZzh5rO$M~swM{58k`tmubFEwB-dI6KJ94kV&fpj0uH^GW~^h52Qb`=en0it5V1%3Z+8a*jN0Y0eFJkC7w=4F(nF9JX>s2l)(Sr!NcPo2yZ3TDAG{n zU?b725(5&7@QuI(58+!i-cWUty**L=qP8JhhQtUKRg?Z6(4_56uI2pOkP2Fe@_Zle zVARv}=c-hi`Lcwf=lq^B{gyKQJH?Jsl|xkJzfr}1Vl=esHU+monJK!4{x_=Pwq8T$ zP1?-#{K`T8R`T#*+poj+ml(sT~&|4*p# z4CK{FK}DnpoUzdwCX?_hVe9_~5~zLk literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/multi_agent_state.cpython-312.pyc b/src/agent/core/__pycache__/multi_agent_state.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d56cbc3c75a18302a2f4590f1e3006156c815eb GIT binary patch literal 7132 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/nodes.cpython-312.pyc b/src/agent/core/__pycache__/nodes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd97c8fe3dce73834edee85fe9a21e703dd525af GIT binary patch literal 6187 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 F{}1Nc3)TPt literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/nodes.cpython-313.pyc b/src/agent/core/__pycache__/nodes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9163d8b0716bce50ca056169a70a77578c2481f GIT binary patch literal 4461 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/plan.cpython-312.pyc b/src/agent/core/__pycache__/plan.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e060a312ffd402af63129bb19d56e128f503bcc9 GIT binary patch literal 2447 zcmZWqYfltc7@pmoy>n4fidEu(TC-lTO&V>env&9sDFUh5G<4fxvopf#?#yN`RvK)U zmdjEQFs?umK`IDDVQcCIUBn;IFPb#zW|Af(?948ofM~z;Q{OXZ!6i(xb1r-4y*$tJ zzUR;EYzF~PcKg2b5j#OFpvCHA#1q4-k6_YAFhmW(7#O495HQvl4QOuin`%rjl72E^ zt}*L-X1^t1t+5&i)>dO@tc<0ZtZ_7$TsC>sKoHe_4@YSeBkFAT((FL$aa5gaPTqLlo>K zVqFNKp^nttHMQ+&YW%h~)T53LXg5dTUqA($P)F{nEn^fPL{W&yAVLFMnCSvosw>JW z=`&U3Rdn^2Ri~=Y03xZTi24i4Qj0{j&Ze}X=o2j(fiRMzI>w8Lcb7qNdN_`kJi7Tx zSeweY1{m_IKdCG~S@{{5uBxERs=@eIWfiBrCiGeQ$%PTthF4*j^bry;kDv2tQLV1g zcsIQ`PiF4o`DIfh2(8i3Xk?6oq-0q=(P%mbQ^o}QBx7zwBD8?mY>b_8FixL=$*Ln5 zGm|~&U~&dcOzxoRG~wDLe+TZdn5YaTCT2PoLY-J{>q)u-cP#czFaFlQG#gf@ z`VqZpu~CV+8>lv7QJN1GQIv_0~8eF^Gcwr04sS)))z>L)DUE;;pFab|Q37pXYjXh9(n5=vGk`HD?oMb;yD z>lOPNac~fr`&6-ute@4Jj9dh2Pes!$F?FYDv8@f52RJhIDxZT>5NRSdmb~#m>l(wl zU5xdk?$b4qy3&K02BH#AhVe;gl9+OBq%|=!rMCX6^>-zn-Ab23xHs{9T8livjTwIq zxhe&Eny9bRL61~VzX=u9Xe#;utjJ&Ec(5t8r&DY0#=)$G0YE|k5J_^jH#K)v>ugKJ zMmL6W7zeQ?Rfs@FJqBA|wKm{#C`KP6z(qzVn|x4R0`AeY4!l|NNq$zb1O+}2loY$f z^M0E11XzXi2z8?3^l_pj$U5MPHLV4mAl`qGWn3o3$;(numasXSN0xX%p+U)6FL*de z3OsnSrYl?_QCWZ(NLltViW$A%S4{QnCBY1#x=6tWgMe;gC4Wr?>&h3JaS-CZ=q4&77%AIA%L(=7^Kw|*(Q zy}ZD>5sf9m24#D|a{;JCw_Q4OI-QlU7rbqQpZGnzLd5H0BJZuf{a5#2JNWmO;+I>B zLto72Q}IoO@q#_^t@~H(<^z_$$hZ4Hd`a$G&LKAMjOV%LcNE173g`3Ty*Iw+aD4l* zWh1fUV`IGVXncFoi`|!B6#N+9^=^DeVSH!N$~Nb&oE2;CX2*&(FUzt_S%iLEY#ZICYSMJL_AQQ?b%C!+U93anSTR84j^D zEub4q*rk660s)|Od&*WFo)QF4$tvoi&J^}y*U>!%($}GxB!qX6p*_fu?HH7V>}O91 uhhd4XJrRYsY%&-OD@MD4{C6v1*u6kF{~_{K;^=%8GZTt`IS1N=6 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/plan.cpython-313.pyc b/src/agent/core/__pycache__/plan.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb601fb33759184ddbbcf679f0488c09ec132b48 GIT binary patch literal 2497 zcmaJ@{ZCU@7`|<93$%bJN>FDk%#YN$`Z1@=wk%=Fbb>&*uq?65%}u##6?&WdvCJh~ zs-u7il&PQ~3Mfu=+TwJ474%=wWXYPFnV5Lbz3oq8%(7qio_k8yWyx;Rd(L^!Iq&6VkF1r+rX50)P*=~&)eht2@SP>iL9I&2XwZrr!lSVb$ewi?@E zTBJ=IU1LA&5FKvHmnY^k_Quj>SO2u{XS$-3as2PRi^==n`9h|E$zMmEq{X7+R*y4{ z{dz2_iwgz?nUe!TP^yEmttxmLnzDxKx&xor9b}v8>ucGnhK9!aFRE%8<&=e@oZ8}; z(Km%gCecK@Hs9wCwQ@nXJ~@sSqA$Z|wb|a(PiORVw^QRiD0V$P6wwk1)II)cp+}n= zC9|oiAB;+zy^`#0O_zEetz%dEI(E3fst>K61{8g^khs`dDZB#+8@hGwJYI z9D8J5ABw|Jn@!-&_iv@|PT<*_ed(b)JG8`2En!024gN;LsJ~CY(r1jv0Gcr|Prv{G z{rnvinbv0SYqQ~OOjwLMdX2n*%lW1g4o1M?)n?TU!JIEe#X4r`d9i4~d> z?$&@Ln~BKf5$~Oi=tEPO@5d3;eLX!sr=Rag-3D?pDFr{MCxZHa)*fF={W^#u4~>Tj zbfE_=UPR}5jqYhZd&xwhuXq0C?^B=NvAP+9@x{z=JeiJ>d95~U;v2GFHh@N zAL)Zr{{{y;CTZ~*qOnx`5uA`5UB5i0KO8{) zanwEOHlq=Dc>mlqO3bHXF_Ikp(j0U$G4-y&-27tqep{vn;ARE7o*2mwFup;f2EOA?e7HqfHd^63z- z7Ag`ivVI{b1G{Rz$V)sY`cJ3@94wS%4ve(SeBwr+n+5JP=v@zEW^Q0edL}#-T@j! z9Ya{2i(@1dBtm-5hYRIPy)5k5(jS`q+)Mb4y; zHHo*&0lAe|9U)O@3(0DMEC{V^kZa>rniG#psw)tbWKl79sCh!j{2G`E5?a6JTRb+^ zB`9)8k%_x}PLTy*;j~&D;)5-L;Bl5jgN3vxK%~6oB-3rY%z+qaMFAPu@q3SIJHdY= z+5xJE7W1JI@i$d1%c_9IBFO5*#f1R(*IX+INy0LA!=oPB|DmrL6PFSgGwh`bM{(HlCgu5IH^K`OC!Gbd*=rxG{g zXz-I1c!jAyR+U}ec46Da?Z2<8e734GT=SyLoh;dqEZ>w|z3p{@{T;_|^tvk_J)^fS z6;mtUN|t(FtglFxZ+KA#@6E|gJCbWZUb0f__gj-2_9fR=JbnASr{&)#H||WX-;jK( z;`JKW#^Tp`D^@yR=am*ZmZ(y@;|&C+JozR~stuB4awTFK#7g|aS5WmQ+-R-`RRBAt8+EnGi5GL?U8Y(x(?gsWn3Ns? zy{mR`9SEtb8)~cS*v4w6x~g&iAr}4{9BF7$OH6B1{ek+drK!5=2z#XdKy}7q5_*Rc zw_qfjFp~U_;UX!m{3qgW7{bSnlA7SPWV2W-&#B6nl+mS{&g{Z}Dn pcb=*K;owpcWm&US;#mD})5@h18<-r+tMVwz%0CO&TO4N8zX2caRlNWJ literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/routing.cpython-312.pyc b/src/agent/core/__pycache__/routing.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c64f493aa663482c2cc184b99c318f8cf1f9454 GIT binary patch literal 3708 zcmai%Z%`9S7QknckU#dK!hq;*mVW0-P1r2@4ju-eNkeS))am+GYOw$%eyc88GYGV3a2jYl zHVQ`396pNfiWNr}_iFT)*jPr$J zaj|nT85amk#1dx-jEgwyIg_)LTg2JmcWHB>!yfntO;LMYKF=QHYHDP(8-wxaz2yC| z#Fe`*r+U=;Lu$)?b^7Z>#~5=$ViM8I+Krodq)xXa21k>lx3r$K>Y2V5p|e(N;@fB< zdQ%{dn1Y``eP!m(>fCjO_OdtlK3B z5~Fq9Q_p`3ny9_g$-j-OonL6-Yucqw_0l!wV0Bh`wdFJI>bQ1sl<2Qs8q-?8Qtw{X zLc_ZLn?NzC(H{^v*6j!Vd;uO*%Iu3rCe>R#iQZ9d;6mDFVfD+i@yO-Gl@aDZ)gC7K z`PszTiI-EY_ylYcEb^!qNiy5S`#7)9!xFtkUUsEKUm4Td&w$)`z}3HChUf#5}zN5F2Q?>*Vph>xxxD~%>pAMMbWQ* zgh3Y-gn(E9BhG;P)3lt~8-zf)oeC!OX^=ja8pF0sLx3`(Bamj0^U_;`hTAF0 zr;Re!fwZ9USV4N6{pC#Qy$h`8rPoi=5`?suO6$p)kH1S%65al5nE(RIbiEFQT7j*#o|y1U0CZLn4MIpWeJ z2q8RLhIEIV?Vds|^invA0TV!BvwFdqFJoG0bcH z9RG5v7gkvooSJ3vy3Ps+z?V~DIBTviqyrPpH zco8}ikG7~|6X~Pzl8#g`8`w;1JFkY%XO#u=5(?%JMafTJWPQ3dwCb~VepAwAcFp| zUPA7Aa;x=jKjQ0=KLE7#R5Ud!_WuDeS_8){hd=2AbRlYZa1h)4qk%fZ0$Xa^ufj~+~v{yvC*J(DNC`3B+`vLu8F$?7C900+7G@~0Uddnc|I za&edr9w1`xG~@N1@E_*^ZdcUaek^OWUAy|7PF+a0$-$9ann=J;oC>`hy#3&^+IlfQ z)sq~%!Bk^rsOPU^Le>I7s%!SJAMfA4m#wO;J+S{!)!w=cHT3Ew%UI0ihK6);pyyhQ zQ@|X~$BcTXOKph&Znds~+%JWiN7tIE@%#9U_=T{#P`w3J71$vK@6L|o_-9bckdMLj zl-50_c7B3k%8P26IqXrd&H|ZQ(0wZPdYxN2$!8=N!b6C)_%x*%xGCaj; zneY$c@NncIJNzit>>VRqhoF_XTLqnjD+ugS3|b8%rEGz+4y}dNt@wqT(1oZU7^<-J znZ4why{y~N8U$e3mUM3Ku76_t#rV5Vx9*C#!jZao=3>VFomX8$0O1Fgz zp4mz-?P%T6y)0&99=-GajP3o|lFj4RNcG=O#!6~u3TtO=YX?3U-Z8jid|7PW_K4%* zrio48n`4#xXDSaqsyi679eU(+$87G|lJx_p#y^NO#!B|g6z-X~Q7bpi7gB}A=q=E( z#$iI3un3btF(Y0MAm6D~sY3E_ImsX88=3M{x-UQCFcWK?TAHpg59bt^N6_0?j!}Y4 zvkpe5mU3RVd>E!18K_W?FhSz7j%A94WjVi_Wfd#S9u2^$Jj)^sC&er`H$g%`IL9Dt zcx1+p%LMq>#og%g`k+8Zd__8#5$2#`$E)ODj|$S0oTikLJX1mTr%PRl+&OUpr8sAu zBtP8@%W@u*oY&+RyzXW_W3JK{A!!LxMJ6M%WUePxoVAE2d_@r(>pqkJq3GuV0>2Y& zhIL$qN?V|tH_|lygQ0*n{kxQ+*Ci;&W6GgYo1arZOHc=%Q{~U8%IDOtUYW~i`@ep< Iiq@~~zx;nvzyJUM literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/routing.cpython-313.pyc b/src/agent/core/__pycache__/routing.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..751a04d85567de419e120c290a8a5780859ccbd3 GIT binary patch literal 2203 zcmb7FL2MI86rJ_jUdL<4AvOsKgc?X=7D9kZgfu0QpirnyQk0b`V8d#=UN%eZU3Yd3 zBmyai9>S@p5>gI5gcE5`Il_q^IVKq7Xyo)#RfroX?YaFkYdevERvqpB`SbtZ`G4oX z|7Rl_4I>!8oEo?h4I=bAT{MT!W4Mj)fq8&rBy$;*<}jC}yf5QR^O#Tju|FNa0Vd-! z!L)z{#``m&bQ^ACd>|7}i&#uYaD=UcnP|Elw=-VI#L^wOgYluvwsa@%1U@3S^`aYo z9G9bVm~jb1l-gg@kR*YBFYNIcVRqC+we(4w_F*# z(w(}sqUt#2^%=#r?V_UJ*0l z4S-1YQEG9ymTi`k`D2YV>~DOm+6ZO2sfM&6WK+xH+J`LS=-dQ&@a3o@HnP5B4R02o zqj7mseyXj>Z_cv6X`6k&KG4*Uam`o?5N8q{LCwFAgQJ%aqSA@82;~^~s0YWFMEytv zTSM5C_9H1YNkSzrf6nW_VURF8aC)c?@mp$1Cw>(h4iPllx^ChU2~Yz^3KC&jbyU~I zcpG@YQA#2}2yf^v4gn|mNx-2AB_YVBtCSprM6r5nGjF7A)0-c@NWvhQ&qwoFN5@Wn zWccXtaQ?LJeCyifJOhYwURSYYT82_G0SKNj?_ezt5VYL9W@9~%Z2*g943%d{%$c?; z07i%&L=_$Pftz-Com)WW&{`~U|H^7Cx!AcH>z_MU7ux4#D_>u( z#;&YfGhW7wdUx-S;kN?Xee_KTb#&Lo_PO*1xJKGmjI}UZ2E0(rwlWNBqlIC>7OcII z^~t_w-ka7cq$2D4H}n>~{oZQ>_`M1E<+tFMZ(`2!XZ=$x1UF-Vs%lxteu_O6&NkOV z_oYM@9fMk=!^2=s^i~k(f9&@G{r{_i8xRdeCL8ZY!zuzv!X)kh3lGShz>#h5^eG&t z76~AdfV*1O4EEi0NsEmY0H(h$1#k=|5_X`vXqz(&xQANwQnC|>L%Re8d=l>hj&wtH zL6MYot3ct+3WIh|yce|eQ|BijbLclQz7~V0d%<__4j@+Syg#y#uZag%#RJRxYoDBf zhZ^+ksr3xL=oy@k))T3vXf-iB-?kDFK+DlQZ#|IW?A5T}~XP-yT zz7A<@oz)8uEcfgEkqDc_zZUtMjI~%u0F2%Y>YA~5n6!=p`NnMqK?5Jk9 zH!qglmRsd2-b44{9q2RrfScW3Ff}&^%fTdEOIMS3l9RTjOWR3UQLb0OnXV|9LXHI8 znKGOtri}&D!^Ut0!#NjK%h0A((}Jde^`U0_!xY^lMzsc3%hN-QvPMP{tho#^`{uRg zuspN0la4KV*73Z*@7f|}`7GN-Q=9SHzwc4P<)?PQIt%_y;XT*-y!%65w z$u3li`Zyi}J#7ch0U&R9j^ozR-d9LkMbaPW@H+bF6`EQ{sdY5Ejz0S{7~*1obwxNY GP5%G}Q~06) literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/runtime.cpython-312.pyc b/src/agent/core/__pycache__/runtime.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e99b13e80999c12dc3b682905e53b7818150477f GIT binary patch literal 20365 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 Dr6wer literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/runtime.cpython-313.pyc b/src/agent/core/__pycache__/runtime.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ba67fad0fbdc785b0fe31175c742b60a4f31a6b GIT binary patch literal 25743 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 literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/state.cpython-313.pyc b/src/agent/core/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f387c17a34787cdcf12ff99adb5af56dc8fd9aaa GIT binary patch literal 750 zcmYjPO>Yx15FKYfvPm1bv;umHDglawLUSNOD1cgeVFNv|aH=cIY~1Za;!VfiL-+$+ zx%5ZybGRV1Qcnn>-jD)!>^P}nX=mrnv**o>X5MNw!JOv@_daY9fS(quPT(9ee`A~r zNFX6YhzXLm^=rdg?4cL?=*IyDi)Iz(v?}8(9GQF(W0Fqt9F-pDQpm#4 zCa^XT6WC@<+3}=|YmNE)QVYcxwoHL@^Fj6U#S{8Y2~_lPzt`{6L!mzEVoDd?uvdbk z%w@(Vxh&8Hslt?+=}Aq~0tL1CMRz(wyABIu+gFh>pW(-i!FPXvTPy0Qvtj!=Ep>^e zAGA}hLOq)r5%ldXyE+)!@U1bIS$f8E$*fR!H6N^Bvl;T~S=ZSmjaJyzV!L$G+xA5? saASOiI|iDS1=Tj@k4Ff(gx%lJ`v$#V@bD7${)U7+{c`6YnBOA9e<)MWJ^%m! literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/todo_manager.cpython-312.pyc b/src/agent/core/__pycache__/todo_manager.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..581401192cf2ee9c221823e0df6839568803b7fa GIT binary patch literal 2487 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 EKM2tQDF6Tf literal 0 HcmV?d00001 diff --git a/src/agent/core/__pycache__/tool_policy.cpython-313.pyc b/src/agent/core/__pycache__/tool_policy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08578dcf319920625ab6ac23f05ef0e56cba3b7b GIT binary patch literal 2820 zcmb7G|8Eq>6`#HDzO%92V3T45YX{>TNB1qE&dV_W5`XR1MZLMORlHw9)b0zx0EL>$71V>nA4Q)|H% zmde&TYpo624B1v^uXSLDA=~SmwJz*pkC6IkIVD9Id>Jr^VvAhR?G|I`n`U=Z|v#(b$#f8_Q{yJ z#GX^w%y2w4I|~l_&B^rS?I-bzZnu7aPM;eglh;!VT{UhN1{t1UPo471SlolWh1%<36~Np+9JYQ>14+y*NbL$Jk@C9I2NB;?wynMl>za z4*@f6mq}t8_BVIVVjg&?b@!0YE`6?7`*egxGEM=msoC55)fu+&Fl_V*pusWNxtVTE zZeP-SIt=KfdrIrPOJ@4C_#G4a>nxe>0CPnMihLB1DBz)8{e9|-*-U$vzA&lvUrxLB?yTKjyXKJP4{<6)~#X zu?Rnq0ee(FD@qE*#i|Yd8eb#STJim~`pxQU-!DYvY*cRX?GK4kls_U0Sb|5wH-{w| zn@XR8{XWRO6!p<}Nmf9o49t=I`+jZX;(N)3E6D2`J3>`MO5<@}wa$CCNG z;|HHvtoGesy9!dSZ7Z&dWmm=M-mhHVzkm6{^qD&;sR=OY+Cjfk_<97o8Uy@@#MSw^xP zxBLtu8#)_U*bR&K5IV75$wuaFCKo9H7+Lfq^dIkrBUk5*_M9`fa+IcX!^okPtc_OA zxsK2lVgQfJn8O|YpHbv-?!DPeO14Je;FOUwBxJ?Ps;N$f4NBY$XlNo_h zfC7QuNJo46@@GIdCeN89_VGyiqXn(IJH0raBQb4Yl=O7zcc!1j+fK*e8S`Oy(AcCG z;PrJL&2pB0Z2{~_SD(4})A@upFs2QUXd`3ksap_4ADSc!eIzlEz6n&hq2b?xMO$@ncbxc)!pRjbTwO3J6h=|5Z%Ht-L=XD2iHi zUToqOAP@iPtfts(1~QmL!U}D?s>4*Ng&XRiI?T(U+T<2dQeC+$LMt$O5Fc%A5mltx zjUBzVEXRd_P;5}HNTCg6~wy1(z8_+>Z=E`yIkA1+G=-b7Eg z#W+GZ*lLuH7e5sJ@JB(G@J?9WP8}xT6GwmBit45G6)CWJ+%<%@s_fI3xjy2bk}m}uF>C2 zHziB=B|NL1(!r{vr`)uC<^X_efECqy9k?8RFSsE;?5~Z|;|frywm{e)RUKG_LP{65 zWX*sAPhs51D&5VM$e%>qxViEBjya#l^MMyaWpJ!Hv zVZODv8QXWIhBi3`0x^UeA)ZK656~}x+{r3fLvIL{%Wx^yVtBB OJ)_chMP8=F*ympdfBf12 literal 0 HcmV?d00001 diff --git a/src/agent/core/agent_config.json b/src/agent/core/agent_config.json new file mode 100644 index 0000000..67a19c5 --- /dev/null +++ b/src/agent/core/agent_config.json @@ -0,0 +1,26 @@ +[ + { + "name": "main", + "system_prompt": "你是一个多模态助手,能够直接理解用户上传的图片、语音等内容。\n 【关键行为准则】\n - 你的默认行为是直接回答用户问题,不要调用任何工具。\n - 只有当某个信息是你**绝对无法从当前对话或上传内容中获取**,且该信息必须通过外部工具才能获得时,才允许调用工具。\n - 对于任何图片/音频输入,请始终**直接**利用你的视觉/听觉能力回答,**绝对不要**调用截图、OCR、日期查询等无关工具。\n - 如果你不确定是否需要工具,选择**不调用**,直接给出你能给出的最佳答案。\n - 违反上述规则是一种严重错误。", + "max_iterations": 200, + "llm": { + "model": "qwen3.6-flash", + "api_key": "sk-a44b226ec79a40fd8fcdbbaa74ba133e", + "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", + "timeout": 60.0, + "max_retries": 0 + }, + "llm_model_id": "a44b226e", + "tools": [ + "web_search_class", + "date_query_class", + "screen_capture", + "browser_control", + "human_interaction", + "software_control", + "directory_listing" + ], + "enabled": true, + "role": "main" + } +] diff --git a/src/agent/core/approvals.py b/src/agent/core/approvals.py new file mode 100644 index 0000000..e94d348 --- /dev/null +++ b/src/agent/core/approvals.py @@ -0,0 +1,166 @@ +"""待确认操作(HITL)内存注册与 UI 元数据构建。 + +系统定位: + 维护进程内 ``PENDING_TOOL_APPROVALS`` 字典,在图因需确认工具而暂停时 + 保存上下文;``runtime.continue_after_human_action`` 消费并清理。 + +注意: + 当前为内存存储,进程重启后待确认项丢失;生产环境可改为 Redis/DB。 + +可扩展性: + - 增加 TTL、session 级清理、持久化后端。 + - ``build_*_meta`` 可与前端组件协议版本化。 +""" +from __future__ import annotations + +import json +import uuid +from typing import Any, Sequence + +from langchain_core.messages import BaseMessage +from langchain_core.messages import messages_to_dict + +from agent.core.human_interaction import normalize_human_interaction_args +from agent.utils.tool_call_utils import json_safe + +PENDING_TOOL_APPROVALS: dict[str, dict[str, Any]] = {} + + +def build_approval_meta(session_id: str, tool_call: dict[str, Any], messages: Sequence[BaseMessage]) -> dict[str, Any]: + """为需确认的普通工具调用构建 UI 元数据并注册 pending。 + + 输入: + session_id: 会话 ID。 + tool_call: normalize 后的工具调用 dict。 + messages: 当前图内消息序列(序列化后存入 pending)。 + + 输出: + ``{"pending_action": {...}}``,含 id、type=tool_call、args、options 等。 + + 系统定位: + ``routing.first_pending_tool_meta`` 对 confirm 类工具调用。 + + 可扩展性: + 可按工具名定制 title、风险提示、参数展示模板。 + """ + approval_id = uuid.uuid4().hex + tool_name = tool_call.get("name") or "unknown_tool" + args = json_safe(tool_call.get("args") or {}) + display_call = {"name": tool_name, "args": args} + instruction = json.dumps(display_call, ensure_ascii=False, indent=2) + PENDING_TOOL_APPROVALS[approval_id] = { + "session_id": session_id, + "type": "tool_call", + "tool_call": {"id": tool_call.get("id"), "name": tool_name, "args": args}, + "messages": messages_to_dict(list(messages)), + } + return { + "pending_action": { + "id": approval_id, + "type": "tool_call", + "title": f"待确认执行工具:{tool_name}", + "tool_name": tool_name, + "args": display_call, + "instruction": instruction, + "options": ["approve", "reject"], + "resolved": False, + } + } + + +def build_human_interaction_meta(session_id: str, tool_call: dict[str, Any], messages: Sequence[BaseMessage]) -> dict[str, Any]: + """为 human_interaction 工具构建 UI 元数据并注册 pending。 + + 输入: + session_id: 会话 ID。 + tool_call: 含 name=args 的工具调用。 + messages: 当前消息序列。 + + 输出: + ``{"pending_action": {...}}``,type=human_interaction,含 interaction_type 等。 + + 系统定位: + 模型主动请求用户补充信息/选择/审批时使用。 + + 可扩展性: + 可扩展表单 schema、多步向导式交互。 + """ + approval_id = uuid.uuid4().hex + args = normalize_human_interaction_args(tool_call.get("args") or {}) + interaction_type = args.get("interaction_type") or "information" + default_titles = { + "information": "请补充信息", + "selection": "请选择一个选项", + "approval": "需要人工审批", + } + PENDING_TOOL_APPROVALS[approval_id] = { + "session_id": session_id, + "type": "human_interaction", + "tool_call": {"id": tool_call.get("id"), "name": "human_interaction", "args": args}, + "messages": messages_to_dict(list(messages)), + } + return { + "pending_action": { + "id": approval_id, + "type": "human_interaction", + "interaction_type": interaction_type, + "title": args.get("title") or default_titles.get(str(interaction_type), "需要人工处理"), + "prompt": args.get("prompt") or "请处理后继续。", + "tool_name": "human_interaction", + "args": { + "交互类型": interaction_type, + "说明": args.get("prompt") or "请处理后继续。", + "可选项": args.get("options") or [], + }, + "instruction": args.get("suggested_response") or "", + "options": args.get("options") or [], + "resolved": False, + } + } + + +def pending_assistant_message(meta: dict[str, Any], content: str = "需要你确认后继续执行。") -> dict[str, Any]: + """构造带 pending_action 的 assistant 聊天消息 dict。 + + 输入: + meta: 通常含 pending_action 的字典。 + content: 展示给用户的提示文案。 + + 输出: + Web UI 使用的 ``{"role": "assistant", "content": ..., "meta": ...}``。 + + 系统定位: + ``runtime.execute_agent`` 检测到待确认时追加到 chat_history。 + + 可扩展性: + 可统一 output_type、国际化 content 模板。 + """ + return { + "role": "assistant", + "content": content, + "meta": {"output_type": "text", "autoplay": False, **meta}, + } + + +def remove_pending_ui_message(chat_history: list[dict[str, Any]], approval_id: str) -> None: + """在 chat_history 中将指定 approval_id 的 pending_action 标记为已解决。 + + 输入: + chat_history: 可变聊天历史列表(原地修改)。 + approval_id: 待确认项 ID。 + + 输出: + 无(副作用:匹配消息的 meta.pending_action.resolved = True)。 + + 系统定位: + ``runtime.continue_after_human_action`` 用户操作后更新 UI 状态。 + + 可扩展性: + 可改为从 history 中移除而非仅标记 resolved。 + """ + for msg in reversed(chat_history): + pending = ((msg.get("meta") or {}).get("pending_action") or {}) if isinstance(msg, dict) else {} + if pending.get("id") == approval_id: + pending["resolved"] = True + msg.setdefault("meta", {})["pending_action"] = pending + return diff --git a/src/agent/core/config_manager.py b/src/agent/core/config_manager.py new file mode 100644 index 0000000..7815c66 --- /dev/null +++ b/src/agent/core/config_manager.py @@ -0,0 +1,339 @@ +"""Agent 配置管理:从 JSON 文件加载/保存 agent、tool、env、model 配置。 + +系统定位: + 提供统一接口读写 ``agent/core/`` 下的 JSON 配置文件, + 使前端手动配置的 JSON 可以替代 core 底下的相应 .py 硬编码。 + +配置文件: + agent_config.json - Agent 运行时配置(名称、模型引用、工具列表、system_prompt) + tool_config.json - 工具权限与参数配置 + env_config.json - 环境变量与 LLM 模型列表 + +可扩展性: + 可增加版本号、校验 schema、多环境配置切换。 +""" +from __future__ import annotations + +import json +import os +import uuid +from dataclasses import dataclass, field, asdict +from pathlib import Path +from typing import Any + +# ---------- 配置文件路径 ---------- +_CORE_DIR = Path(__file__).resolve().parent +AGENT_CONFIG_PATH = _CORE_DIR / "agent_config.json" +TOOL_CONFIG_PATH = _CORE_DIR / "tool_config.json" +ENV_CONFIG_PATH = _CORE_DIR / "env_config.json" + + +# ---------- 模型配置 ---------- +@dataclass +class ModelConfig: + id: str = "" # 唯一标识 + name: str = "" # 显示名称 + api_key: str = "" + base_url: str = "" + model: str = "" # API 模型名 + timeout: float = 60.0 + max_retries: int = 0 + + +# ---------- LLM 配置结构(保留兼容) ---------- +@dataclass +class LLMConfig: + model: str = "" + api_key: str = "" + base_url: str = "" + timeout: float = 60.0 + max_retries: int = 0 + + +@dataclass +class AgentConfig: + name: str = "" + system_prompt: str = "" + max_iterations: int = 200 + llm: LLMConfig = field(default_factory=LLMConfig) # 保留兼容 + llm_model_id: str = "" # 新:引用 models 列表中的模型 ID + tools: list[str] = field(default_factory=list) + enabled: bool = True + role: str = "sub" # "main" | "sub" + + +@dataclass +class ToolConfig: + name: str = "" + display_name: str = "" + description: str = "" + permission: str = "confirm" # "direct" | "confirm" + auto_execute_rules: list[dict] = field(default_factory=list) # [{"parameter":"action","operator":"equals","value":"open"}, ...] + enabled: bool = True + + +# ---------- 通用 JSON 读写 ---------- +def _read_json(path: Path) -> dict | list | None: + """读取 JSON 文件,不存在或格式错误返回 None。""" + if not path.is_file(): + return None + try: + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + except (json.JSONDecodeError, OSError): + return None + + +def _write_json(path: Path, data: Any) -> bool: + """写入 JSON 文件,成功返回 True。""" + try: + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + return True + except OSError: + return False + + +# ---------- Agent 配置 ---------- +def _default_agent_config() -> list[dict]: + """生成默认 agent 配置(单 agent: main)。""" + from agent.memory.system_prompt import MAIN_SYSTEM_PROMPT + from agent.tools import get_all_available_tool_names + models = load_models() + default_model_id = models[0]["id"] if models else "" + return [ + { + "name": "main", + "system_prompt": MAIN_SYSTEM_PROMPT, + "max_iterations": 200, + "llm_model_id": default_model_id, + "tools": get_all_available_tool_names(), + "enabled": True, + "role": "main", + } + ] + + +def load_agent_configs() -> list[AgentConfig]: + """从 agent_config.json 加载 agent 配置列表,无文件时返回默认。""" + raw = _read_json(AGENT_CONFIG_PATH) + if not raw or not isinstance(raw, list): + raw = _default_agent_config() + _write_json(AGENT_CONFIG_PATH, raw) + + configs = [] + for item in raw: + llm_raw = item.get("llm", {}) + configs.append(AgentConfig( + name=item.get("name", ""), + system_prompt=item.get("system_prompt", ""), + max_iterations=item.get("max_iterations", 200), + llm=LLMConfig( + model=llm_raw.get("model", ""), + api_key=llm_raw.get("api_key", ""), + base_url=llm_raw.get("base_url", ""), + timeout=llm_raw.get("timeout", 60.0), + max_retries=llm_raw.get("max_retries", 0), + ), + llm_model_id=item.get("llm_model_id", ""), + tools=item.get("tools", []), + enabled=item.get("enabled", True), + role=item.get("role", "sub"), + )) + return configs + + +def save_agent_configs(configs: list[dict]) -> bool: + """将 agent 配置列表写入 agent_config.json。""" + return _write_json(AGENT_CONFIG_PATH, configs) + + +# ---------- Tool 配置 ---------- + +# 各工具的默认 auto_execute_rules —— 与 tool_policy.py 的硬编码规则对应 +_TOOL_DEFAULT_AUTO_RULES: dict[str, list[dict]] = { + "browser_control": [ + {"parameter": "action", "operator": "equals", "value": "open"}, + ], + "rag_tool": [ + {"parameter": "action_type", "operator": "equals", "value": "query"}, + ], +} + + +def _get_tool_parameters() -> dict[str, list[dict]]: + """获取每个工具的参数名和类型信息,供前端生成 auto_execute_rules 提示。 + + 返回: + {tool_name: [{"name": "action", "type": "Literal[open,close]"}, ...], ...} + """ + from agent.tools import _ALL_AVAILABLE_TOOL_CLASSES + import typing + result: dict[str, list[dict]] = {} + for name, cls in _ALL_AVAILABLE_TOOL_CLASSES.items(): + try: + inst = cls() + schema = getattr(inst, "args_schema", None) + if schema is None: + result[name] = [] + continue + fields_info = getattr(schema, "model_fields", {}) + params = [] + for fname, finfo in fields_info.items(): + annotation = finfo.annotation + type_str = str(annotation) if annotation is not None else "Any" + # 简化 Literal 显示 + import re + m = re.search(r"Literal\[(.+?)\]", type_str) + if m: + type_str = "Literal[" + m.group(1) + "]" + params.append({"name": fname, "type": type_str}) + result[name] = params + except Exception: + result[name] = [] + return result + + +def _default_tool_config() -> list[dict]: + """根据所有可用工具生成默认 tool 配置。""" + from agent.tools import get_all_available_tool_names, get_all_available_tool_descriptions + from agent.core.tool_policy import classify_tool_execution + all_names = get_all_available_tool_names() + all_descriptions = get_all_available_tool_descriptions() + result = [] + for tname in all_names: + display_name, desc = all_descriptions.get(tname, (tname, "")) + perm = classify_tool_execution(tname, {}) + default_rules = _TOOL_DEFAULT_AUTO_RULES.get(tname, []) + result.append({ + "name": tname, + "display_name": display_name, + "description": desc, + "permission": perm, + "auto_execute_rules": default_rules, + "enabled": True, + }) + return result + + +def load_tool_configs() -> list[ToolConfig]: + """从 tool_config.json 加载工具配置,无文件时返回默认。""" + raw = _read_json(TOOL_CONFIG_PATH) + if not raw or not isinstance(raw, list): + raw = _default_tool_config() + _write_json(TOOL_CONFIG_PATH, raw) + + configs = [] + for item in raw: + configs.append(ToolConfig( + name=item.get("name", ""), + display_name=item.get("display_name", ""), + description=item.get("description", ""), + permission=item.get("permission", "confirm"), + auto_execute_rules=item.get("auto_execute_rules", []), + enabled=item.get("enabled", True), + )) + return configs + + +def save_tool_configs(configs: list[dict]) -> bool: + """将 tool 配置列表写入 tool_config.json。""" + return _write_json(TOOL_CONFIG_PATH, configs) + + +# ---------- 强制权限工具(前端锁死,不可修改) ---------- +# 仅必须 confirm 的工具锁死;safe_tools 默认 direct 但用户可改为 confirm +_FORCED_CONFIRM_TOOLS = {"human_interaction"} + + +def get_tool_forced_permissions() -> dict[str, str]: + """返回强制权限的工具映射 {tool_name: "confirm"}, + 前端应对这些工具禁用权限下拉框和 auto_execute_rules 编辑。""" + return {name: "confirm" for name in _FORCED_CONFIRM_TOOLS} + + +# ---------- 模型管理 ---------- +def _default_models() -> list[dict]: + """生成默认模型列表,首次启动时从 .env 继承已有 LLM 配置。""" + import os + api_key = os.environ.get("LLM_API_KEY", "") + base_url = os.environ.get("LLM_BASE_URL", "") + model = os.environ.get("LLM_MODEL", "") + timeout = os.environ.get("LLM_TIMEOUT", "60") + if api_key and base_url and model: + return [ + { + "id": str(uuid.uuid4())[:8], + "name": "默认模型", + "api_key": api_key, + "base_url": base_url, + "model": model, + "timeout": float(timeout) if timeout else 60.0, + "max_retries": 0, + } + ] + return [] + + +def load_models() -> list[dict]: + """从 env_config.json 的 models 字段加载模型列表。""" + raw = _read_json(ENV_CONFIG_PATH) + if isinstance(raw, dict) and "models" in raw and isinstance(raw["models"], list) and raw["models"]: + return raw["models"] + return _default_models() + + +def save_models(models: list[dict]) -> bool: + """将模型列表写入 env_config.json(合并其他字段)。""" + current = load_env_config() + current["models"] = models + return _write_json(ENV_CONFIG_PATH, current) + + +# ---------- Env 配置 ---------- +def _default_env_config() -> dict: + """生成默认 env 配置(不含 models,models 由 load_models 独立处理)。""" + return { + "TTS_BASE_URL": "", + "ASR_BASE_URL": "", + "RAG_BASE_URL": "", + "LLM_CONTEXT_WINDOW": "131072", + "MINI_COMPRESS_RATE": "0.5", + "HARD_COMPRESS_RATE": "0.75", + "CURRENT_TURNS": "4", + "IMG_SIZE": "768", + "RAG_CHUNK_SIZE": "500", + "RAG_CHUNK_OVERLAP": "50", + "GROUNDING_WIDTH": "1000", + "GROUNDING_HEIGHT": "1000", + } + + +def load_env_config() -> dict: + """从 env_config.json 加载环境变量配置,无文件时返回默认。""" + raw = _read_json(ENV_CONFIG_PATH) + if not raw or not isinstance(raw, dict): + raw = _default_env_config() + raw["models"] = _default_models() + _write_json(ENV_CONFIG_PATH, raw) + return raw + # 确保有 models 字段(向后兼容旧配置文件) + if "models" not in raw or not isinstance(raw.get("models"), list): + raw["models"] = _default_models() + return raw + + +def save_env_config(config: dict) -> bool: + """将 env 配置写入 env_config.json(保留已有 models)。""" + # 保留已有的 models 列表(由 models API 单独管理) + existing = _read_json(ENV_CONFIG_PATH) + if isinstance(existing, dict) and "models" in existing: + config["models"] = existing["models"] + return _write_json(ENV_CONFIG_PATH, config) + + +# ---------- 获取所有已注册工具的基础信息 ---------- +def get_registered_tool_names() -> list[str]: + """返回所有可用工具名称(含数据库工具)。""" + from agent.tools import get_all_available_tool_names + return get_all_available_tool_names() diff --git a/src/agent/core/env_config.json b/src/agent/core/env_config.json new file mode 100644 index 0000000..bdb3a46 --- /dev/null +++ b/src/agent/core/env_config.json @@ -0,0 +1,25 @@ +{ + "TTS_BASE_URL": "", + "ASR_BASE_URL": "", + "RAG_BASE_URL": "", + "LLM_CONTEXT_WINDOW": "262144", + "MINI_COMPRESS_RATE": "0.4", + "HARD_COMPRESS_RATE": "0.6", + "CURRENT_TURNS": "4", + "IMG_SIZE": "768", + "RAG_CHUNK_SIZE": "500", + "RAG_CHUNK_OVERLAP": "50", + "GROUNDING_WIDTH": "1000", + "GROUNDING_HEIGHT": "1000", + "models": [ + { + "id": "a44b226e", + "name": "阿里云Qwen", + "api_key": "sk-a44b226ec79a40fd8fcdbbaa74ba133e", + "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", + "model": "qwen3.6-flash", + "timeout": 60, + "max_retries": 0 + } + ] +} \ No newline at end of file diff --git a/src/agent/core/graph_builder.py b/src/agent/core/graph_builder.py new file mode 100644 index 0000000..1168926 --- /dev/null +++ b/src/agent/core/graph_builder.py @@ -0,0 +1,53 @@ +"""LangGraph ReAct 工作流图构建。 + +系统定位: + 将 LLM(已 bind_tools)、工具列表组装为可编译的 StateGraph, + 供 ``agents/main_agent`` 及各子 Agent 的 ``AgentRuntime`` 使用。 + +图结构: + START -> agent -> (tools | END) + tools -> process_tool_artifact -> agent + +可扩展性: + - 可增加 checkpoint、interrupt 节点、子图路由。 + - 可为不同 Agent 注入不同的 conditional_edges 或额外节点(如 RAG 检索)。 +""" +from __future__ import annotations + +from typing import Any + +from langgraph.graph import END, START, StateGraph +from langgraph.prebuilt import ToolNode + +from agent.core.nodes import make_call_model_node, process_tool_artifact +from agent.core.routing import should_continue +from agent.core.state import AgentState + + +def build_agent_graph(tools: list[Any], model_with_tools: Any): + """构建并编译标准 ReAct Agent 图。 + + 输入: + tools: LangChain BaseTool 列表,供 ToolNode 执行。 + model_with_tools: 已 ``bind_tools`` 的聊天模型,供 agent 节点调用。 + + 输出: + 编译后的 LangGraph CompiledGraph,支持 ``invoke({"messages": ...})``。 + + 系统定位: + ``agents/main_agent`` 入口;子 Agent 可复用同一拓扑并替换 tools/model。 + + 可扩展性: + 返回前可 ``workflow.compile(checkpointer=...)`` 启用持久化; + 可参数化节点名以支持多 Agent 图合并。 + """ + workflow = StateGraph(AgentState) + workflow.add_node("agent", make_call_model_node(model_with_tools)) + workflow.add_node("tools", ToolNode(tools)) + workflow.add_node("process_tool_artifact", process_tool_artifact) + + workflow.add_edge(START, "agent") + workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END}) + workflow.add_edge("tools", "process_tool_artifact") + workflow.add_edge("process_tool_artifact", "agent") + return workflow.compile() diff --git a/src/agent/core/human_interaction.py b/src/agent/core/human_interaction.py new file mode 100644 index 0000000..f0bf1ba --- /dev/null +++ b/src/agent/core/human_interaction.py @@ -0,0 +1,75 @@ +"""人工交互(HITL)参数规范化与结果文案生成。 + +系统定位: + 连接 ``human_interaction`` 工具与 ``core/approvals`` / ``core/runtime``, + 将模型发起的人工交互请求转为 UI 可展示结构,并在用户操作后生成 + ToolMessage 内容供图继续执行。 + +可扩展性: + - 新增 interaction_type 时同步扩展 ``normalize_human_interaction_args`` + 与 ``human_interaction_result`` 的分支。 + - 可与前端约定更丰富的 payload(表单字段、文件上传等)。 +""" +from __future__ import annotations + +from typing import Any + +from agent.tools.tool import HumanInteraction + + +def normalize_human_interaction_args(args: dict[str, Any]) -> dict[str, Any]: + """规范化 human_interaction 工具参数为固定 schema。 + + 输入: + args: 模型传入的原始参数字典。 + + 输出: + 含 interaction_type、title、prompt、options、suggested_response 的字典; + interaction_type 限定为 information | selection | approval。 + + 系统定位: + ``approvals.build_human_interaction_meta`` 构建待确认 UI 前调用。 + + 可扩展性: + 可增加 locale、超时、默认值校验;options 可支持 label/value 结构。 + """ + normalized = dict(args or {}) + normalized["interaction_type"] = str(normalized.get("interaction_type") or "information") + if normalized["interaction_type"] not in {"information", "selection", "approval"}: + normalized["interaction_type"] = "information" + normalized["title"] = str(normalized.get("title") or "需要人工处理") + normalized["prompt"] = str(normalized.get("prompt") or "请处理后继续。") + normalized["options"] = HumanInteraction.normalize_options(normalized.get("options")) + normalized["suggested_response"] = str(normalized.get("suggested_response") or "") + return normalized + + +def human_interaction_result(pending: dict[str, Any], decision: str, instruction: str | None) -> str: + """根据用户决策生成写入 ToolMessage 的文本结果。 + + 输入: + pending: PENDING_TOOL_APPROVALS 中 type=human_interaction 的条目。 + decision: approve | reject | edit。 + instruction: 用户输入文本(补充信息、选项、修改说明等)。 + + 输出: + 供 LangGraph 继续推理的 ToolMessage 字符串内容。 + + 系统定位: + ``runtime.continue_after_human_action`` 在人工交互分支调用。 + + 可扩展性: + 可返回结构化 JSON 字符串,供下游节点解析;支持多语言模板。 + """ + tool_call = pending.get("tool_call") or {} + args = normalize_human_interaction_args(tool_call.get("args") or {}) + interaction_type = args.get("interaction_type") or "information" + text = (instruction or "").strip() + if interaction_type == "approval": + if decision == "reject": + return "用户拒绝" + return "用户同意" + if interaction_type == "selection": + selected = text or args.get("suggested_response") or "" + return f"用户选择:{selected}" + return f"用户补充信息如下:{text}" diff --git a/src/agent/core/llm.py b/src/agent/core/llm.py new file mode 100644 index 0000000..37c1db3 --- /dev/null +++ b/src/agent/core/llm.py @@ -0,0 +1,670 @@ +"""ChatQwen:基于 OpenAI 兼容 API 的多模态 LangChain 聊天模型。 + +系统定位: + 项目默认 LLM 单例 ``llm``,被 agents 绑定工具,并承担: + - 用户消息预处理(附件、ASR、图片 data URL) + - TTS 输出与音频元数据 + - 非标准 tool_call XML/JSON 解析 + - Vision 描述接口 + +依赖: + env_utils 中的 LLM_* / TTS_BASE_URL / ASR_BASE_URL + +可扩展性: + - 可子类化覆盖 _preprocess_messages、_create_chat_result + - 流式、重试、限流可在 _generate 层扩展 +""" +import io +import os +import tempfile +import wave +import pyaudio +from contextlib import closing +from typing import Any, List, Dict, Optional +from pydantic import Field +import re, json +from datetime import datetime as dt +import requests +from concurrent.futures import ThreadPoolExecutor, as_completed + +from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, SystemMessage, ToolCall +from langchain_core.outputs import ChatResult, ChatGeneration +from langchain_openai import ChatOpenAI + +from agent.utils.image_utils import image_bytes_to_openai_image_url_part +from agent.utils.env_utils import LLM_API_KEY, LLM_BASE_URL, LLM_MODEL, LLM_TIMEOUT +import agent.utils.env_utils as env_utils +from agent.utils.agent_utils import assistant_text, Documents_process, split_text, merge_wavs +from agent.utils.image_utils import image_path_to_openai_image_url_part, image_bytes_to_openai_image_url_part, IMAGE_FILE_EXTENSIONS +from agent.memory.system_prompt import VISION_SYSTEM_PROMPT + +class ChatQwen(ChatOpenAI): + """Qwen 系列模型的 LangChain 封装,扩展多模态与 TTS 能力。 + + 属性: + output_type: 默认输出模式 text | audio | both。 + stream_play: TTS 后是否本地播放。 + last_audio_kwargs: 最近一次 TTS 的文件路径与时长等元数据。 + + 系统定位: + agents/main_agent 的 llm 与 bind_tools 基底。 + + 可扩展性: + 新增厂商响应字段时在 _create_chat_result 中适配。 + """ + + output_type: str = Field(default="text") + stream_play: bool = Field(default=False) + last_audio_kwargs: Optional[dict] = Field(default=None) + + def __init__(self, **kwargs: Any) -> None: + """从环境变量或 kwargs 初始化 OpenAI 兼容客户端。 + + 输入: + kwargs: 可覆盖 model、api_key、base_url、timeout、max_retries。 + + 输出: + 无。 + + 系统定位: + 模块级 ``llm = ChatQwen()`` 单例构造。 + + 可扩展性: + 可增加 default_headers、proxy 等 ChatOpenAI 参数。 + """ + super().__init__( + model=kwargs.pop("model", os.environ.get("LLM_MODEL", "")), + api_key=kwargs.pop("api_key", os.environ.get("LLM_API_KEY", "")), + base_url=kwargs.pop("base_url", os.environ.get("LLM_BASE_URL", "")), + timeout=kwargs.pop("timeout", float(os.environ.get("LLM_TIMEOUT", 60))), + max_retries=kwargs.pop("max_retries", 0), + extra_body={ + "chat_template_kwargs": {"enable_thinking": False} + }, + **kwargs + ) + + @staticmethod + def play_audio_bytes(audio_bytes: bytes, chunk_size: int = 1024) -> None: + """通过 pyaudio 播放 WAV 字节流。 + + 输入: audio_bytes WAV 二进制;chunk_size 播放块大小。 + 输出: 无。 + 系统定位: _apply_tts 在 stream_play=True 时调用。 + 可扩展性: 可换用 sounddevice 或仅写文件不播放。 + """ + if not audio_bytes: + return + wf = wave.open(io.BytesIO(audio_bytes), 'rb') + p = pyaudio.PyAudio() + stream = p.open( + format=p.get_format_from_width(wf.getsampwidth()), + channels=wf.getnchannels(), + rate=wf.getframerate(), + output=True, + ) + data = wf.readframes(chunk_size) + while data: + stream.write(data) + data = wf.readframes(chunk_size) + stream.stop_stream() + stream.close() + p.terminate() + wf.close() + + @staticmethod + def _process_image_content(part: Dict[str, Any]) -> Dict[str, Any]: + """将 image_url 块中的本地路径或字节转为 data URL。 + + 输入: OpenAI 风格 content part dict。 + 输出: 处理后的 part 或失败时 text 占位。 + 系统定位: _preprocess_messages 多模态分支。 + 可扩展性: 委托 image_utils,保持单一图片处理逻辑。 + """ + image_type = part.get("type") + if image_type != "image_url": + return part + image_url = part.get("image_url", {}) + url = image_url.get("url", "") + if url.startswith(("http://", "https://", "data:")): + return part + if isinstance(url, str) and os.path.exists(url): + ext = os.path.splitext(url)[1].lower() + if ext in IMAGE_FILE_EXTENSIONS: + result = image_path_to_openai_image_url_part(url) + return result if result else {"type": "text", "text": "[图片处理失败]"} + if isinstance(url, bytes): + result = image_bytes_to_openai_image_url_part(url) + return result if result else {"type": "text", "text": "[图片处理失败]"} + return part + + @staticmethod + def _convert_audio_to_wav(audio_path: str) -> str: + """非 WAV 音频经 pydub 转为临时 WAV 文件路径。 + + 输入: audio_path 本地音频路径。 + 输出: WAV 文件路径(可能为临时文件,调用方负责清理)。 + 系统定位: _audio_to_text 前置格式统一。 + 可扩展性: 需系统安装 ffmpeg;可缓存转换结果。 + """ + ext = os.path.splitext(audio_path)[1].lower() + if ext == ".wav": + return audio_path + try: + from pydub import AudioSegment + except ImportError as exc: + raise RuntimeError("当前 ASR 仅支持 WAV,但未安装 pydub/ffmpeg") from exc + try: + audio = AudioSegment.from_file(audio_path) + except Exception as exc: + raise RuntimeError(f"无法读取音频文件: {exc}") from exc + fd, wav_path = tempfile.mkstemp(suffix=".wav", prefix="asr_") + os.close(fd) + try: + audio.export(wav_path, format="wav") + return wav_path + except Exception: + if os.path.exists(wav_path): + os.remove(wav_path) + raise + + def _audio_to_text(self, audio_path: str) -> str: + """调用 ASR 服务将音频转为文本。 + + 输入: audio_path 本地音频文件。 + 输出: 识别文本字符串。 + 系统定位: 用户消息含 audio part 时替换为 text。 + 可扩展性: ASR_BASE_URL 需配置;可支持流式识别。 + """ + if not os.path.exists(audio_path): + raise FileNotFoundError(f"音频文件不存在: {audio_path}") + + wav_path = self._convert_audio_to_wav(audio_path) + cleanup_wav = (wav_path != audio_path) + + try: + # 读取音频文件二进制内容 + with open(wav_path, "rb") as f: + audio_bytes = f.read() + + # 构建 Data URL + import base64 + b64 = base64.b64encode(audio_bytes).decode("ascii") + mime_type = "audio/wav" # 我们已经转换为 wav + data_url = f"data:{mime_type};base64,{b64}" + + payload = { + "messages": [ + { + "role": "user", + "content": [ + {"type": "audio_url", "audio_url": {"url": data_url}} + ] + } + ] + } + endpoint = f"{env_utils.ASR_BASE_URL.rstrip('/')}/chat/completions" + resp = requests.post( + endpoint, + headers={"Content-Type": "application/json"}, + json=payload, + timeout=120 + ) + resp.raise_for_status() + result = resp.json() + content = result["choices"][0]["message"]["content"] + return content.strip() + finally: + if cleanup_wav and os.path.exists(wav_path): + os.remove(wav_path) + + def _preprocess_messages(self, messages: List[BaseMessage]) -> List[BaseMessage]: + """入模前统一处理:JSON 列表、附件、ASR、图片、文档解析。 + + 输入: LangChain BaseMessage 列表。 + 输出: 规范化后的新消息列表(不修改原列表)。 + 系统定位: _generate 调用 super()._generate 之前的适配层。 + 可扩展性: 新增 part.type 时在此增加分支。 + """ + new_messages = [] + for msg in messages: + if isinstance(msg, HumanMessage) and isinstance(msg.content, str): + text = msg.content.strip() + if text.startswith("[") and text.endswith("]"): + try: + possible_list = json.loads(text) + if isinstance(possible_list, list): + msg = HumanMessage(content=possible_list, additional_kwargs=msg.additional_kwargs) + except (json.JSONDecodeError, ValueError): + pass + if isinstance(msg, HumanMessage) and isinstance(msg.content, dict): + # 尝试从 dict 中提取文本,否则序列化为字符串 + text = msg.content.get("text") or msg.content.get("content") or "" + if text: + msg = HumanMessage(content=str(text), additional_kwargs=msg.additional_kwargs) + else: + msg = HumanMessage(content=json.dumps(msg.content, ensure_ascii=False), additional_kwargs=msg.additional_kwargs) + elif isinstance(msg, HumanMessage) and isinstance(msg.content, list): + new_content: List[Dict[str, Any] | str] = [] + for part in msg.content: + if isinstance(part, dict): + if part.get("type") == "audio": + audio_path = part.get("audio_path") + if audio_path: + try: + text = self._audio_to_text(audio_path) + new_content.append({"type": "text", "text": text}) + except Exception as e: + new_content.append({"type": "text", "text": f"[语音识别失败: {e}]"}) + elif part.get("type") == "image_url": + new_content.append(self._process_image_content(part)) + elif part.get("type") == "text": + text = (part.get("text") or "").strip() + if text: + new_content.append({"type": "text", "text": text}) + elif part.get("path") or (part.get("file") or {}).get("path"): + file_path = part.get("path") or (part.get("file") or {}).get("path") + if isinstance(file_path, str) and os.path.isfile(file_path): + ext = os.path.splitext(file_path)[1].lower() + normalized_path = file_path.replace("\\", "/") + if ext in IMAGE_FILE_EXTENSIONS: + new_content.append(self._process_image_content({"type": "image_url", "image_url": {"url": file_path}})) + else: + try: + file_text, file_images = Documents_process.process(file_path) + new_content.append({"type": "text", "text": f"用户上传了一个名为\"{normalized_path}\"的文件。"}) + if file_text.strip(): + new_content.append({ + "type": "text", + "text": f"以下是文件 {normalized_path} 的内容:\n{file_text.strip()}" + }) + for image_bytes in file_images or []: + image_part = image_bytes_to_openai_image_url_part(image_bytes) + if image_part is not None: + new_content.append(image_part) + if not file_text.strip() and not file_images: + new_content.append({"type": "text", "text": f"[用户上传了附件文件: {normalized_path}]"}) + except Exception as e: + new_content.append({"type": "text", "text": f"[附件处理失败: {normalized_path}: {e}]"}) + else: + text = assistant_text(part) + if text: + new_content.append({"type": "text", "text": text}) + elif isinstance(part, str): + text = part.strip() + if text: + new_content.append(text) + if len(new_content) == 1 and isinstance(new_content[0], str): + msg = HumanMessage(content=new_content[0], additional_kwargs=msg.additional_kwargs) + else: + msg = HumanMessage(content=new_content, additional_kwargs=msg.additional_kwargs) + new_messages.append(msg) + return new_messages + + def _text_to_audio(self, text: str) -> bytes: + """调用 TTS 服务合成语音,长文本分块并行后 merge_wavs。 + + 输入: text 待朗读文本。 + 输出: WAV 二进制。 + 系统定位: _apply_tts 核心。 + 可扩展性: TTS_BASE_URL、并发 max_workers 可配置化。 + """ + chunks = split_text(text, min_effective_chars=20) + if len(chunks) == 1: + resp = requests.get(f"{env_utils.TTS_BASE_URL}/tts", params={"text": chunks[0]}) + resp.raise_for_status() + return resp.content + + # 多块并行请求 + audio_parts = [None] * len(chunks) + with ThreadPoolExecutor(max_workers=min(len(chunks), 4)) as pool: + future_to_idx = { + pool.submit( + lambda t: requests.get( + f"{env_utils.TTS_BASE_URL}/tts", params={"text": t} + ).content, + chunk + ): idx for idx, chunk in enumerate(chunks) + } + for future in as_completed(future_to_idx): + idx = future_to_idx[future] + try: + audio_parts[idx] = future.result() + except Exception as e: + raise RuntimeError(f"TTS request failed for chunk {idx}: {e}") + return merge_wavs(audio_parts) + + @staticmethod + def _get_wav_duration_seconds(audio_bytes: bytes) -> float | None: + """从 WAV 字节计算时长(秒)。 + + 输入: audio_bytes WAV 数据。 + 输出: 秒数或 None(解析失败)。 + 系统定位: _apply_tts 写入 last_audio_kwargs.duration_seconds。 + 可扩展性: 可支持 mp3 等格式。 + """ + if not audio_bytes: + return None + try: + with closing(wave.open(io.BytesIO(audio_bytes), "rb")) as wf: + frame_rate = wf.getframerate() + frame_count = wf.getnframes() + if frame_rate <= 0: + return None + return round(frame_count / frame_rate, 2) + except wave.Error: + return None + + def _apply_tts(self, result: ChatResult, output_type: str, audio_output_dir: str | None = None) -> ChatResult: + """根据 output_type 为 AI 回复合成语音并写入会话目录。 + + 输入: + result: ChatResult 含 AIMessage。 + output_type: audio | both。 + audio_output_dir: WAV 保存目录。 + + 输出: + 可能清空 audio 模式下 content 的 ChatResult;设置 last_audio_kwargs。 + + 系统定位: + _generate 在模型返回后的后处理。 + + 可扩展性: + 可上传对象存储而非本地路径。 + """ + if output_type not in ("audio", "both") or not audio_output_dir: + return result + os.makedirs(audio_output_dir, exist_ok=True) + for idx, gen in enumerate(result.generations): + if not isinstance(gen, ChatGeneration): + continue + msg = gen.message + if not isinstance(msg, AIMessage): + continue + text = assistant_text(msg.content) + if not text.strip(): + continue + try: + audio_bytes = self._text_to_audio(text) + timestamp = dt.now().strftime("%H%M%S") + audio_path = os.path.join(audio_output_dir, f"assistant_tts_{timestamp}_{idx}.wav") + with open(audio_path, "wb") as f: + f.write(audio_bytes) + duration_seconds = self._get_wav_duration_seconds(audio_bytes) + # 仅保存到实例属性,不再写入 additional_kwargs + if idx == 0: + self.last_audio_kwargs = { + "audio": { + "sample_rate": 24000, + "format": "wav", + "path": audio_path, + "duration_seconds": duration_seconds, + }, + "output_type": output_type, + } + if self.stream_play: + self.play_audio_bytes(audio_bytes) + if output_type == "audio": + msg.content = "" + except Exception as e: + print(f"[TTS ERROR] 合成语音失败: {e}") + # 发生错误时也清空 last_audio_kwargs,避免污染 + self.last_audio_kwargs = None + return result + + def _generate( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[Any] = None, + **kwargs: Any, + ) -> ChatResult: + """LangChain 生成入口:预处理 -> API 调用 -> 可选 TTS。 + + 输入: + messages: 对话消息。 + stop: 停止词。 + run_manager: 含 metadata.output_type、audio_output_dir。 + + 输出: + ChatResult。 + + 系统定位: + 所有 invoke/stream 最终经过此方法。 + + 可扩展性: + 可在此注入 token 统计回调、链路追踪 span。 + """ + metadata = {} + if run_manager: + metadata = getattr(run_manager, 'metadata', {}) + output_type = metadata.get("output_type", self.output_type) + audio_output_dir = metadata.get("audio_output_dir") + + # 移除自定义参数,防止它们传入 OpenAI API + kwargs.pop("output_type", None) + kwargs.pop("audio_output_dir", None) + + messages = self._preprocess_messages(messages) + + result = super()._generate(messages, stop=stop, run_manager=run_manager, **kwargs) + + if output_type in ("audio", "both"): + result = self._apply_tts(result, output_type, audio_output_dir=audio_output_dir) + if output_type == "audio": + for gen in result.generations: + if isinstance(gen, ChatGeneration) and isinstance(gen.message, AIMessage): + gen.message.content = "" + return result + + def _create_chat_result(self, response, generation_info=None) -> ChatResult: + """解析 API 响应:reasoning 合并、XML/JSON tool_call 提取。 + + 输入: + response: OpenAI 兼容 API 原始响应。 + generation_info: LangChain 生成元信息。 + + 输出: + ChatResult,AIMessage 可能含 tool_calls。 + + 系统定位: + 兼容非 OpenAI 标准 tool_calls 格式的关键适配点。 + + 可扩展性: + 新增模型格式时在 regex/JSON 分支扩展。 + """ + # ---------- 打印原始 tool_calls ---------- + try: + raw = response if isinstance(response, dict) else response.model_dump() + print("[RAW_RESPONSE]", json.dumps(raw, indent=2, ensure_ascii=False)) + choices = raw.get("choices", []) + if choices: + msg = choices[0].get("message", {}) + tool_calls = msg.get("tool_calls") + if tool_calls: + print("[RAW_TOOLCALLS]", json.dumps(tool_calls, indent=2, ensure_ascii=False)) + except Exception: + pass + + result = super()._create_chat_result(response, generation_info) + try: + dumped = response if isinstance(response, dict) else response.model_dump(exclude={"choices": {"__all__": {"message": {"parsed"}}}}) + for i, choice in enumerate(dumped.get("choices") or []): + if i >= len(result.generations): + break + lc_msg = result.generations[i].message + msg = choice.get("message") or {} + ak = lc_msg.additional_kwargs + # 优先处理 reasoning 内容 + merged = "" + for key in ("reasoning_content", "reasoning_details", "reasoning"): + merged = assistant_text(msg.get(key)) + if merged: + break + if merged: + ak.setdefault("reasoning_content", merged) + raw_content = assistant_text(msg.get("content")) + if not raw_content and merged: + lc_msg.content = merged + elif raw_content: + lc_msg.content = raw_content + + content_text = lc_msg.content if isinstance(lc_msg.content, str) else "" + + # 匹配 ... 块 + pattern = re.compile(r'\s*(.*?)\s*', re.DOTALL) + matches = pattern.findall(content_text) + if matches: + tool_calls = [] + for idx, match in enumerate(matches): + # 先尝试 JSON 解析(兼容旧格式) + try: + call_data = json.loads(match) + tool_calls.append({ + "id": f"call_{idx}", + "type": "function", + "function": { + "name": call_data.get("name", ""), + "arguments": json.dumps(call_data.get("arguments", {})) + } + }) + continue # 解析成功,跳过 XML 尝试 + except json.JSONDecodeError: + pass + + # 再尝试 XML 解析 + func_match = re.search(r'', match) + if not func_match: + continue + func_name = func_match.group(1) + + params = {} + for param_match in re.finditer(r'\s*(.*?)\s*', match, re.DOTALL): + key = param_match.group(1) + value = param_match.group(2).strip() + # 可选:智能类型转换,先保持简单 + params[key] = value + + tool_calls.append({ + "id": f"call_{idx}", + "type": "function", + "function": { + "name": func_name, + "arguments": json.dumps(params) + } + }) + + if tool_calls: + ak["tool_calls"] = tool_calls + # 构造 LangChain 的 ToolCall 对象列表 + lc_tool_calls = [] + for idx, tc in enumerate(tool_calls): + lc_tool_calls.append( + ToolCall( + id=tc["id"], + name=tc["function"]["name"], + args=json.loads(tc["function"]["arguments"]) + ) + ) + lc_msg.tool_calls = lc_tool_calls + # 清理内容中的 XML/JSON 工具调用痕迹 + cleaned = pattern.sub("", content_text).strip() + lc_msg.content = cleaned if cleaned else "" + except Exception: + pass + return result + + def describe_image(self, image_path_or_url: str, prompt: str = "请详细描述这张图片的内容。") -> str: + """Vision 便捷接口:单张图片 + 提示词 -> 文本描述。 + + 输入: + image_path_or_url: 路径或 http(s)/data URL。 + prompt: 用户侧提示。 + + 输出: + 模型描述文本。 + + 系统定位: + 工具或测试脚本直接调用,不经 LangGraph。 + + 可扩展性: + 可返回结构化 JSON(物体列表等)。 + """ + messages = [ + SystemMessage(content=[{"type": "text", "text": VISION_SYSTEM_PROMPT}]), + HumanMessage(content=[ + {"type": "image_url", "image_url": {"url": image_path_or_url}}, + {"type": "text", "text": prompt} + ]) + ] + result = self.invoke(messages) + return result.content + + def describe_image_bytes(self, image_bytes: bytes, prompt: str = "请详细描述这张图片的内容。") -> str: + """Vision 便捷接口:内存图片字节 -> 文本描述。 + + 输入: + image_bytes: 图片二进制。 + prompt: 提示词。 + + 输出: + 描述文本;处理失败返回固定错误文案。 + + 系统定位: + 截屏、工具 artifact 等无路径场景。 + + 可扩展性: + 与 describe_image 共用 VISION_SYSTEM_PROMPT。 + """ + img_part = image_bytes_to_openai_image_url_part(image_bytes) + if img_part is None: + return "图片处理失败" + messages = [ + SystemMessage(content=[{"type": "text", "text": VISION_SYSTEM_PROMPT}]), + HumanMessage(content=[img_part, {"type": "text", "text": prompt}]) + ] + result = self.invoke(messages) + return result.content + +llm = ChatQwen() + + +def reload_llm() -> None: + """热重载:重新从环境变量创建 LLM 实例。""" + global llm + llm = ChatQwen() + + +if __name__ == "__main__": + import sys + + # 使用原始字符串避免转义问题 + TEST_IMAGE_PATH = r"C:\Users\86166\Desktop\2026-05-22\20260522-DSC01513.jpg" + + if not os.path.exists(TEST_IMAGE_PATH): + print(f"❌ 图片不存在: {TEST_IMAGE_PATH}") + sys.exit(1) + + # 测试1: 纯图片识别 + print("=" * 50) + print("测试1: 纯图片识别(无文本)") + print("=" * 50) + result1 = llm.invoke([ + HumanMessage(content=[{"type": "image_url", "image_url": {"url": TEST_IMAGE_PATH}}]) + ]) + print(f"返回类型: {type(result1)}") + print(f"返回内容: {result1.content}") + print(f"所有属性: {[attr for attr in dir(result1) if not attr.startswith('_')]}") + + # 测试2: 图片 + 文本 + print("\n" + "=" * 50) + print("测试2: 图文混合输入") + print("=" * 50) + result2 = llm.invoke([ + HumanMessage(content=[ + {"type": "image_url", "image_url": {"url": TEST_IMAGE_PATH}}, + {"type": "text", "text": "这张图片里有什么?请详细描述"} + ]) + ]) + print(f"回答: {result2.content}") + diff --git a/src/agent/core/multi_agent_graph.py b/src/agent/core/multi_agent_graph.py new file mode 100644 index 0000000..a5f6ccf --- /dev/null +++ b/src/agent/core/multi_agent_graph.py @@ -0,0 +1,533 @@ +"""多 Agent 体系 SupervisorGraph:一主多子 / 主从工作流编排。 + +系统定位: + 为 "一主多子" 和 "主从" 架构提供统一的 LangGraph 图构建, + 支持主 Agent 向子 Agent 委派任务,子 Agent 完成后回传结果。 + +图结构:: + + START -> supervisor + supervisor -> main_agent | END + main_agent -> (根据 tool_calls 中的 agent_call) 路由到子 agent | supervisor + sub_agent_N -> supervisor (子 agent 完成后回到 supervisor) + +Agent 节点签名: + 每个 agent 节点接收 MultiAgentState,返回 {"messages": [...], "agent_status": {...}, ...} + +可扩展性: + - 子 Agent 可拥有独立图(自定义 build_graph),也可复用 ReAct。 + - 可增加并行子 Agent 执行、超时、重试等策略。 +""" +from __future__ import annotations + +import json +import time +from typing import Any, Callable + +from langgraph.graph import END, START, StateGraph +from langgraph.prebuilt import ToolNode + +from agent.core.multi_agent_state import ( + AgentStatusInfo, + GraphEvent, + MultiAgentState, + ToolCallEvent, +) +from agent.core.nodes import make_call_model_node, process_tool_artifact +from agent.core.routing import should_continue + + +# ---------- 事件发射 ---------- +def _emit_event(state: dict, event: GraphEvent) -> None: + """向 state 中追加一个图事件。""" + events: list[GraphEvent] = state.setdefault("graph_events", []) + # 从 dict 还原为 GraphEvent 对象 + if isinstance(events, list): + events.append(event) + + +def _update_agent_status(state: dict, name: str, status: str, error_msg: str = "") -> None: + """更新 state 中指定 agent 的运行状态。""" + agent_status: dict = state.setdefault("agent_status", {}) + if name in agent_status: + info = agent_status[name] + if isinstance(info, dict): + info["status"] = status + if error_msg: + info["error_message"] = error_msg + elif isinstance(info, AgentStatusInfo): + info.status = status # type: ignore[misc] + if error_msg: + info.error_message = error_msg + + +def _record_tool_call(state: dict, agent_name: str, tool_name: str, args: dict, result_preview: str = "", status: str = "success") -> None: + """记录工具调用到指定 agent 的工具调用历史。""" + history: dict = state.setdefault("tool_calls_history", {}) + if agent_name not in history: + history[agent_name] = [] + history[agent_name].append(ToolCallEvent( + tool_name=tool_name, + tool_args=args, + result_preview=result_preview[:200] if result_preview else "", + status=status, # type: ignore[arg-type] + timestamp=time.time(), + )) + + +# ---------- Supervisor 节点(调度器) ---------- +def make_supervisor_node( + agent_names: list[str], + main_agent_name: str = "main", +) -> Callable[[dict], dict]: + """创建 supervisor 调度节点。 + + Supervisor 职责: + 1. 首次进入:设定 active_agent 为 main,标记 main 为 running。 + 2. 子 agent 完成返回:若还有待处理,标记下一个 agent;否则标记 main 为 done。 + 3. 错误恢复:若子 agent 出错,回主 agent。 + + 输入: + agent_names: 所有 agent 名称列表(含 main)。 + main_agent_name: 主 agent 名称。 + + 输出: + 符合 LangGraph 节点签名的 supervisor 函数。 + """ + sub_agent_names = [n for n in agent_names if n != main_agent_name] + + def supervisor(state: dict) -> dict: + now = time.time() + agent_status: dict = state.get("agent_status", {}) + messages: list = state.get("messages", []) + + # 首次进入 + current_active: str | None = state.get("active_agent") + if current_active is None: + # 启动主 agent + _update_agent_status(state, main_agent_name, "running") + _emit_event(state, GraphEvent( + event_type="agent_start", + agent_name=main_agent_name, + timestamp=now, + )) + return { + "active_agent": main_agent_name, + "agent_status": agent_status, + "graph_events": state.get("graph_events", []), + } + + # 主 agent 执行完毕回来 + if current_active == main_agent_name: + # 检查最后一条消息是否含 agent_call(委派子 agent 的标记) + last_msg = messages[-1] if messages else None + delegated_agent = _extract_delegation(last_msg) + if delegated_agent and delegated_agent in sub_agent_names: + # 主 agent 委派子 agent + _update_agent_status(state, main_agent_name, "done") + _emit_event(state, GraphEvent( + event_type="agent_end", + agent_name=main_agent_name, + timestamp=now, + )) + _update_agent_status(state, delegated_agent, "running") + _emit_event(state, GraphEvent( + event_type="agent_start", + agent_name=delegated_agent, + timestamp=now, + )) + _emit_event(state, GraphEvent( + event_type="message_pass", + source_agent=main_agent_name, + target_agent=delegated_agent, + message_preview=f"主 Agent 委派任务给 {delegated_agent}", + timestamp=now, + )) + return { + "active_agent": delegated_agent, + "agent_status": agent_status, + "graph_events": state.get("graph_events", []), + } + else: + # 主 agent 结束,整体完成 + _update_agent_status(state, main_agent_name, "done") + _emit_event(state, GraphEvent( + event_type="agent_end", + agent_name=main_agent_name, + timestamp=now, + )) + return { + "active_agent": "__end__", + "agent_status": agent_status, + "graph_events": state.get("graph_events", []), + } + + # 子 agent 执行完毕回来 + if current_active in sub_agent_names: + _update_agent_status(state, current_active, "done") + _emit_event(state, GraphEvent( + event_type="agent_end", + agent_name=current_active, + timestamp=now, + )) + _emit_event(state, GraphEvent( + event_type="message_pass", + source_agent=current_active, + target_agent=main_agent_name, + message_preview=f"{current_active} 回传结果给主 Agent", + timestamp=now, + )) + # 回到主 agent + _update_agent_status(state, main_agent_name, "running") + _emit_event(state, GraphEvent( + event_type="agent_start", + agent_name=main_agent_name, + timestamp=now, + )) + return { + "active_agent": main_agent_name, + "agent_status": agent_status, + "graph_events": state.get("graph_events", []), + } + + return { + "active_agent": "__end__", + "agent_status": agent_status, + "graph_events": state.get("graph_events", []), + } + + return supervisor + + +def _extract_delegation(last_msg: Any) -> str | None: + """从最后一条 AI 消息中提取 agent_call 委派信息。 + + 委派格式: + - 在消息 content 中包含 ``[DELEGATE:agent_name]`` + - 或在 tool_calls 中有一条名为 ``agent_call`` 的工具调用 + """ + if last_msg is None: + return None + # 方式1: tool_calls 中的 agent_call + if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: + for tc in last_msg.tool_calls: + tc_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "") + if tc_name == "agent_call": + args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {}) + return args.get("agent_name") or args.get("agent") + # 方式2: 文本标记 + content = getattr(last_msg, "content", "") + if isinstance(content, str) and "[DELEGATE:" in content: + import re + m = re.search(r"\[DELEGATE:(\w+)\]", content) + if m: + return m.group(1) + return None + + +# ---------- 路由函数 ---------- +def supervisor_route(state: dict) -> str: + """Supervisor 之后的路由:根据 active_agent 决定进入哪个 agent 节点或 END。""" + active = state.get("active_agent", "") + if active == "__end__": + return END + if active: + return active + return END + + +# ---------- 子 Agent 节点工厂 ---------- +def make_sub_agent_node( + agent_graph: Any, + agent_name: str, + tools_by_name: dict[str, Any] | None = None, +) -> Callable[[dict], dict]: + """创建一个子 Agent 的图节点。 + + 子 Agent 节点: + 1. 用自己的图执行推理 + 2. 发出 agent_start / tool_call / agent_end 事件 + 3. 更新自己的工具调用历史 + + 输入: + agent_graph: 编译后的子 Agent LangGraph 图。 + agent_name: 子 Agent 名称。 + tools_by_name: 工具名 -> 工具实例映射(用于记录 tool_call)。 + + 输出: + LangGraph 节点函数。 + """ + tools_by_name = tools_by_name or {} + + def sub_agent_node(state: dict) -> dict: + now = time.time() + messages: list = state.get("messages", []) + + # 标记自己为 running + _update_agent_status(state, agent_name, "running") + _emit_event(state, GraphEvent( + event_type="agent_start", + agent_name=agent_name, + timestamp=now, + )) + + # 用子 Agent 图执行 + try: + result = agent_graph.invoke({"messages": messages}) + result_messages: list = result.get("messages", []) + except Exception as e: + _update_agent_status(state, agent_name, "error", str(e)) + _emit_event(state, GraphEvent( + event_type="error", + agent_name=agent_name, + message_preview=str(e)[:200], + timestamp=time.time(), + )) + return { + "messages": messages, + "agent_status": state.get("agent_status", {}), + "graph_events": state.get("graph_events", []), + "tool_calls_history": state.get("tool_calls_history", {}), + } + + # 记录工具调用 + for msg in result_messages: + if hasattr(msg, "tool_calls") and msg.tool_calls: + for tc in msg.tool_calls: + tc_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "") + tc_args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {}) + if isinstance(tc_args, str): + try: + tc_args = json.loads(tc_args) + except Exception: + pass + _record_tool_call(state, agent_name, tc_name, tc_args or {}) + _emit_event(state, GraphEvent( + event_type="tool_call", + agent_name=agent_name, + tool_name=tc_name, + tool_args=tc_args or {}, + timestamp=time.time(), + )) + + _update_agent_status(state, agent_name, "done") + _emit_event(state, GraphEvent( + event_type="agent_end", + agent_name=agent_name, + timestamp=time.time(), + )) + + # 追加 agent 标记信息到消息 + from langchain_core.messages import AIMessage + final_text = "" + for msg in reversed(result_messages): + if isinstance(msg, AIMessage) and not getattr(msg, "tool_calls", None): + content = msg.content + if isinstance(content, str): + final_text = content + break + + tag_msg = AIMessage( + content=f"[AGENT:{agent_name}] {final_text}" if final_text else f"[AGENT:{agent_name}] 任务完成", + additional_kwargs={"agent_name": agent_name}, + ) + new_messages = list(messages) + list(result_messages) + [tag_msg] + + return { + "messages": new_messages, + "agent_status": state.get("agent_status", {}), + "graph_events": state.get("graph_events", []), + "tool_calls_history": state.get("tool_calls_history", {}), + "agent_messages": state.get("agent_messages", {}), + } + + return sub_agent_node + + +# ---------- 主 Agent 节点工厂(多 Agent 版) ---------- +def make_main_agent_node( + model_with_tools: Any, + tools: list[Any], + agent_name: str = "main", + tools_by_name: dict[str, Any] | None = None, +) -> Callable[[dict], dict]: + """创建主 Agent 的图节点(含完整的 ReAct 循环 + 委派能力)。 + + 与单 agent 的 call_model 不同,主 agent 节点内含完整的 + agent -> tools -> process_tool_artifact -> agent 循环, + 并检测 agent_call 工具调用来委派子 agent。 + + 输入: + model_with_tools: bind_tools 后的 LLM。 + tools: 工具列表(含 agent_call 工具)。 + agent_name: 主 agent 名称。 + tools_by_name: 工具名映射。 + + 输出: + LangGraph 节点函数。 + """ + tools_by_name = tools_by_name or {} + + def main_agent_node(state: dict) -> dict: + now = time.time() + messages: list = list(state.get("messages", [])) + + _update_agent_status(state, agent_name, "running") + + # 执行完整的 ReAct 循环(限制步数防止无限循环) + max_steps = 50 + for step in range(max_steps): + # 调用 LLM + response = model_with_tools.invoke(messages) + messages.append(response) + + # 记录工具调用事件 + if hasattr(response, "tool_calls") and response.tool_calls: + for tc in response.tool_calls: + tc_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "") + tc_args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {}) + if isinstance(tc_args, str): + try: + tc_args = json.loads(tc_args) + except Exception: + pass + + # 检测 agent_call(委派) + if tc_name == "agent_call": + # 不在 ToolNode 中执行,由 supervisor 处理 + _record_tool_call(state, agent_name, tc_name, tc_args or {}, "委派子 agent", "pending") + _emit_event(state, GraphEvent( + event_type="tool_call", + agent_name=agent_name, + tool_name=tc_name, + tool_args=tc_args or {}, + timestamp=time.time(), + )) + _update_agent_status(state, agent_name, "done") + return { + "messages": messages, + "agent_status": state.get("agent_status", {}), + "graph_events": state.get("graph_events", []), + "tool_calls_history": state.get("tool_calls_history", {}), + } + + _record_tool_call(state, agent_name, tc_name, tc_args or {}) + _emit_event(state, GraphEvent( + event_type="tool_call", + agent_name=agent_name, + tool_name=tc_name, + tool_args=tc_args or {}, + timestamp=time.time(), + )) + + # 执行工具(非 agent_call) + for tc in response.tool_calls: + tc_name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "") + if tc_name == "agent_call": + continue + tc_args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {}) + tc_id = tc.get("id") if isinstance(tc, dict) else getattr(tc, "id", "") + tool = tools_by_name.get(tc_name) + if tool: + try: + result = tool.invoke(tc_args) + result_content = result[0] if isinstance(result, tuple) else str(result) + except Exception as e: + result_content = f"工具执行出错: {e}" + from langchain_core.messages import ToolMessage + messages.append(ToolMessage( + content=str(result_content), + tool_call_id=tc_id or f"tc_{step}", + name=tc_name, + )) + _record_tool_call(state, agent_name, tc_name, tc_args or {}, str(result_content)[:200]) + _emit_event(state, GraphEvent( + event_type="tool_call", + agent_name=agent_name, + tool_name=tc_name, + tool_args=tc_args or {}, + tool_result_preview=str(result_content)[:200], + timestamp=time.time(), + )) + else: + # 没有工具调用,主 agent 完成 + break + + _update_agent_status(state, agent_name, "done") + return { + "messages": messages, + "agent_status": state.get("agent_status", {}), + "graph_events": state.get("graph_events", []), + "tool_calls_history": state.get("tool_calls_history", {}), + } + + return main_agent_node + + +# ---------- 多 Agent 图构建 ---------- +def build_multi_agent_graph( + main_model_with_tools: Any, + main_tools: list[Any], + main_tools_by_name: dict[str, Any], + sub_agents: list[dict[str, Any]], + main_agent_name: str = "main", +) -> Any: + """构建一主多子 SupervisorGraph。 + + 输入: + main_model_with_tools: 主 Agent 的已 bind_tools LLM。 + main_tools: 主 Agent 工具列表(含 agent_call)。 + main_tools_by_name: 主 Agent 工具名映射。 + sub_agents: 子 Agent 配置列表,每项含: + - name: str + - graph: 编译后的子 Agent LangGraph 图 + - tools_by_name: dict(可选) + main_agent_name: 主 agent 名称。 + + 输出: + 编译后的 LangGraph CompiledGraph。 + + 图结构:: + + START -> supervisor + supervisor --路由--> main | sub_1 | sub_2 | ... | END + main -> supervisor + sub_N -> supervisor + """ + workflow = StateGraph(MultiAgentState) + + # 所有 agent 名称 + all_agent_names = [main_agent_name] + [sa["name"] for sa in sub_agents] + + # Supervisor 节点 + workflow.add_node("supervisor", make_supervisor_node(all_agent_names, main_agent_name)) + + # 主 Agent 节点 + workflow.add_node( + main_agent_name, + make_main_agent_node(main_model_with_tools, main_tools, main_agent_name, main_tools_by_name), + ) + + # 子 Agent 节点 + for sa in sub_agents: + sa_name = sa["name"] + sa_graph = sa["graph"] + sa_tools_by_name = sa.get("tools_by_name", {}) + workflow.add_node( + sa_name, + make_sub_agent_node(sa_graph, sa_name, sa_tools_by_name), + ) + + # 路由 + workflow.add_edge(START, "supervisor") + + route_map = {name: name for name in all_agent_names} + route_map[END] = END + workflow.add_conditional_edges("supervisor", supervisor_route, route_map) + + # 所有 agent 节点完成后回到 supervisor + workflow.add_edge(main_agent_name, "supervisor") + for sa in sub_agents: + workflow.add_edge(sa["name"], "supervisor") + + return workflow.compile() diff --git a/src/agent/core/multi_agent_state.py b/src/agent/core/multi_agent_state.py new file mode 100644 index 0000000..c236458 --- /dev/null +++ b/src/agent/core/multi_agent_state.py @@ -0,0 +1,172 @@ +"""多 Agent 体系共享状态:Agent 运行态、图事件、工具调用记录。 + +系统定位: + 为一主多子 / 主从 Agent 体系提供统一的运行时状态类型, + 被 ``multi_agent_graph`` 的 SupervisorGraph 使用。 + +状态字段: + - messages: 全局对话消息(所有 agent 共享)。 + - agent_status: 各 agent 当前运行状态(idle/running/done/error)。 + - active_agent: 当前正在执行的 agent 名称。 + - graph_events: 图执行事件时间线,供前端 graph 面板渲染。 + - tool_calls_history: 按 agent 分组的工具调用记录。 + - agent_messages: 按 agent 分组的主-子消息传递记录。 + +可扩展性: + - 可增加 token 用量统计、执行耗时、错误堆栈等字段。 +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Annotated, Any, Literal, Sequence, TypedDict + +from langchain_core.messages import BaseMessage +from langgraph.graph.message import add_messages + + +# ---------- Agent 运行状态 ---------- +AgentStatus = Literal["idle", "running", "done", "error"] + + +@dataclass +class AgentStatusInfo: + """单个 Agent 的运行时状态信息。 + + 字段: + name: Agent 名称。 + status: 当前状态:idle(等待)/ running(执行中)/ done(已完成)/ error(出错)。 + role: main | sub。 + icon: 前端展示图标(默认 🧠)。 + error_message: 出错时的错误信息。 + """ + name: str = "" + status: AgentStatus = "idle" + role: Literal["main", "sub"] = "sub" + icon: str = "🧠" + error_message: str = "" + + +# ---------- 图事件 ---------- +@dataclass +class GraphEvent: + """图执行过程中的单个事件,供前端 graph 面板实时更新。 + + 字段: + event_type: 事件类型(agent_start / agent_end / tool_call / message_pass / error)。 + source_agent: 源 agent 名称(message_pass 时为主 agent)。 + target_agent: 目标 agent 名称(message_pass 时为子 agent)。 + agent_name: 当前 agent 名称(非 message_pass 事件)。 + tool_name: 工具名称(tool_call 事件)。 + tool_args: 工具参数(tool_call 事件)。 + tool_result_preview: 工具返回摘要(tool_call 事件)。 + timestamp: 事件发生时间戳。 + message_preview: 消息传递内容摘要。 + """ + event_type: Literal["agent_start", "agent_end", "tool_call", "message_pass", "error"] = "agent_start" + source_agent: str = "" + target_agent: str = "" + agent_name: str = "" + tool_name: str = "" + tool_args: dict[str, Any] = field(default_factory=dict) + tool_result_preview: str = "" + timestamp: float = 0.0 + message_preview: str = "" + + +@dataclass +class ToolCallEvent: + """工具调用记录(持久化到历史)。""" + tool_name: str = "" + tool_args: dict[str, Any] = field(default_factory=dict) + result_preview: str = "" + status: Literal["success", "error", "pending"] = "success" + timestamp: float = 0.0 + + +# ---------- 多 Agent 状态 ---------- +class MultiAgentState(TypedDict, total=False): + """多 Agent 体系共享运行时状态。 + + 字段: + messages: 全局对话消息序列(add_messages 合并)。 + agent_status: {agent_name: AgentStatusInfo} 各 agent 运行状态。 + active_agent: 当前正在执行的 agent 名称。 + graph_events: 图执行事件时间线(追加)。 + tool_calls_history: {agent_name: [ToolCallEvent]} 工具调用记录。 + agent_messages: {agent_name: [dict]} 各 agent 收发的消息摘要。 + user_query: 本轮用户原始查询。 + final_response: 最终响应的文本内容。 + """ + messages: Annotated[Sequence[BaseMessage], add_messages] + agent_status: dict[str, AgentStatusInfo] + active_agent: str | None + graph_events: list[GraphEvent] + tool_calls_history: dict[str, list[ToolCallEvent]] + agent_messages: dict[str, list[dict[str, Any]]] + user_query: str + final_response: str + + +# ---------- 状态初始化辅助 ---------- +def init_agent_status( + agents: list[dict[str, str]], +) -> dict[str, AgentStatusInfo]: + """根据 agent 配置列表初始化 agent_status。 + + 输入: + agents: [{"name": "main", "role": "main", "icon": "🧠"}, ...] + + 输出: + {agent_name: AgentStatusInfo},所有 agent 初始为 idle。 + """ + result: dict[str, AgentStatusInfo] = {} + for a in agents: + name = a.get("name", "") + result[name] = AgentStatusInfo( + name=name, + status="idle", + role=a.get("role", "sub"), # type: ignore[arg-type] + icon=a.get("icon", "🧠"), + ) + return result + + +def init_multi_agent_state( + agents: list[dict[str, str]], + messages: list[BaseMessage] | None = None, + user_query: str = "", +) -> dict: + """创建 MultiAgentState 的初始输入字典。 + + 输入: + agents: agent 配置列表。 + messages: 初始消息列表。 + user_query: 用户查询文本。 + + 输出: + 可直接传入 graph.invoke() 的初始状态字典。 + """ + return { + "messages": messages or [], + "agent_status": init_agent_status(agents), + "active_agent": None, + "graph_events": [], + "tool_calls_history": {}, + "agent_messages": {}, + "user_query": user_query, + "final_response": "", + } + + +def create_empty_multi_agent_state() -> MultiAgentState: + """创建空的 MultiAgentState,用于类型标注时的默认值。""" + return MultiAgentState( + messages=[], + agent_status={}, + active_agent=None, + graph_events=[], + tool_calls_history={}, + agent_messages={}, + user_query="", + final_response="", + ) diff --git a/src/agent/core/nodes.py b/src/agent/core/nodes.py new file mode 100644 index 0000000..72e57eb --- /dev/null +++ b/src/agent/core/nodes.py @@ -0,0 +1,143 @@ +"""LangGraph 图节点:模型调用与工具产物后处理。 + +系统定位: + 实现 ReAct 循环中的 ``agent`` 节点工厂与 ``process_tool_artifact`` 节点, + 被 ``graph_builder`` 注册到 StateGraph。 + +可扩展性: + - ``make_call_model_node`` 可注入 streaming、重试、用量统计回调。 + - ``process_tool_artifact`` 可扩展 audio、文件、结构化 JSON 等 artifact 类型。 +""" +from __future__ import annotations + +import json +import time +from typing import Any, Callable + +from langchain_core.messages import HumanMessage, ToolMessage + +from agent.core.state import AgentState +from agent.utils.image_utils import image_bytes_to_openai_image_url_part +from agent.utils.tool_call_utils import normalize_tool_call + + +def make_call_model_node(model_with_tools: Any) -> Callable[[AgentState], dict]: + """创建调用已绑定工具的 LLM 的图节点闭包。 + + 输入: + model_with_tools: bind_tools 后的 LangChain 聊天模型。 + + 输出: + 符合 LangGraph 节点签名的 ``call_model(state) -> {"messages": [AIMessage]}``。 + + 系统定位: + 图中 ``agent`` 节点:读取 state.messages,调用模型,追加 AI 响应。 + + 可扩展性: + 可包装为 async 节点;可在此统一记录 token 用量到 session_meta。 + """ + + def call_model(state: AgentState) -> dict: + response = model_with_tools.invoke(state["messages"]) + + usage = response.usage_metadata if hasattr(response, "usage_metadata") else {} + if usage: + input_tokens = usage.get("input_tokens", "?") + output_tokens = usage.get("output_tokens", "?") + total_tokens = usage.get("total_tokens", "?") + print(f"[LLM] Token使用: input={input_tokens}, output={output_tokens}, total={total_tokens}") + + if hasattr(response, "tool_calls") and response.tool_calls: + for tc in response.tool_calls: + tc_normalized = normalize_tool_call(tc) + tool_name = tc_normalized.get("name", "unknown") + tool_args = tc_normalized.get("args", {}) + print(f"[LLM] 工具调用: {tool_name}, 参数: {tool_args}") + # 实时记录工具调用 + try: + from agent.history.tool_call_recorder import record_tool_call_live, get_current_session + sid = get_current_session() + if sid: + record_tool_call_live(sid, tool_name, tool_args) + except Exception: + pass + + return {"messages": [response]} + + return call_model + + +def process_tool_artifact(state: AgentState) -> dict: + last_msg = state["messages"][-1] + + if isinstance(last_msg, ToolMessage): + content_preview = str(last_msg.content) + if len(content_preview) > 200: + content_preview = content_preview[:200] + "..." + print(f"[Tool] 工具返回 (id={last_msg.tool_call_id}): {content_preview}") + + # 实时记录工具返回结果 + try: + from agent.history.tool_call_recorder import record_tool_result_live, get_current_session + sid = get_current_session() + if sid: + img_bytes = None + if last_msg.artifact: + if isinstance(last_msg.artifact, dict) and "image_bytes" in last_msg.artifact: + img_bytes = last_msg.artifact["image_bytes"] + elif isinstance(last_msg.artifact, list): + for item in last_msg.artifact: + if isinstance(item, dict) and "image_bytes" in item: + img_bytes = item["image_bytes"] + break + elif isinstance(item, bytes): + img_bytes = item + break + elif isinstance(last_msg.artifact, bytes): + img_bytes = last_msg.artifact + record_tool_result_live(sid, last_msg.name or "unknown", str(last_msg.content) if last_msg.content else "", img_bytes) + except Exception: + pass + + if not isinstance(last_msg, ToolMessage) or not last_msg.artifact: + return {} + + artifact = last_msg.artifact + img_bytes_list: list[bytes] = [] + + if isinstance(artifact, dict) and "image_bytes" in artifact: + img_bytes_list.append(artifact["image_bytes"]) + elif isinstance(artifact, list): + for item in artifact: + if isinstance(item, dict) and "image_bytes" in item: + img_bytes_list.append(item["image_bytes"]) + elif isinstance(item, bytes): + img_bytes_list.append(item) + elif isinstance(artifact, bytes): + img_bytes_list.append(artifact) + + if not img_bytes_list: + return {} + + content_parts = [] + failed_count = 0 + for img_bytes in img_bytes_list: + data_url = image_bytes_to_openai_image_url_part(img_bytes) + if data_url is None: + failed_count += 1 + continue + content_parts.append(data_url) + + if not content_parts: + human_msg = HumanMessage( + content="工具返回了截图,但图片处理失败,请向用户说明。", + additional_kwargs={"synthetic": True}, + ) + return {"messages": [human_msg]} + + content_parts.append({"type": "text", "text": "上面是工具返回的截图。"}) + if failed_count > 0: + content_parts[-1]["text"] += f"(其中 {failed_count} 张图片处理失败,请忽略)" + + human_msg = HumanMessage(content=content_parts, additional_kwargs={"synthetic": True}) + return {"messages": [human_msg]} diff --git a/src/agent/core/routing.py b/src/agent/core/routing.py new file mode 100644 index 0000000..8aa88fd --- /dev/null +++ b/src/agent/core/routing.py @@ -0,0 +1,77 @@ +"""LangGraph 条件路由:工具循环 vs 结束 vs 待确认暂停。 + +系统定位: + 连接 ``graph_builder`` 的条件边与 ``runtime.execute_agent`` 的 + 后处理逻辑,在模型输出 tool_calls 时决定图走向或提取 UI 待确认元数据。 + +可扩展性: + - ``should_continue`` 可扩展为多目标路由(如专用 RAG 节点)。 + - ``first_pending_tool_meta`` 可支持多个并行待确认项的优先级队列。 +""" +from __future__ import annotations + +from typing import Any, Literal, Sequence + +from langchain_core.messages import BaseMessage +from langgraph.graph import END + +from agent.core.approvals import build_approval_meta, build_human_interaction_meta +from agent.core.state import AgentState +from agent.core.tool_policy import classify_tool_execution +from agent.utils.tool_call_utils import normalize_tool_call + + +def should_continue(state: AgentState) -> Literal["tools", "__end__"]: + """根据最后一条 AI 消息中的 tool_calls 决定下一节点。 + + 输入: + state: 含 messages 的 AgentState。 + + 输出: + ``"tools"`` 进入 ToolNode;``"__end__"`` 结束图(含需确认工具场景)。 + + 系统定位: + ``graph_builder`` 中 agent 节点的 conditional_edges 路由函数。 + 若任一 tool_call 策略为 confirm,则直接 END,由 runtime 展示确认 UI。 + + 可扩展性: + 可先执行 direct 类工具、仅对 confirm 类暂停(需拆分 tool_calls 处理)。 + """ + last_msg = state["messages"][-1] + if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: + for raw_tool_call in last_msg.tool_calls: + tool_call = normalize_tool_call(raw_tool_call) + if classify_tool_execution(tool_call.get("name"), tool_call.get("args") or {}) == "confirm": + return END + return "tools" + return END + + +def first_pending_tool_meta(messages: Sequence[BaseMessage], session_id: str) -> dict[str, Any] | None: + """从图执行结果中提取第一个需 UI 处理的待确认项元数据。 + + 输入: + messages: 图 invoke 返回的完整消息列表。 + session_id: 当前会话 ID,写入 PENDING_TOOL_APPROVALS。 + + 输出: + 含 ``pending_action`` 的 meta 字典;无待确认项时返回 None。 + + 系统定位: + ``runtime.execute_agent`` 在图结束后调用,决定是否追加确认型 assistant 消息。 + + 可扩展性: + 可返回列表以支持批量确认;可按工具类型定制 meta 结构。 + """ + if not messages: + return None + last_msg = messages[-1] + if not hasattr(last_msg, "tool_calls") or not last_msg.tool_calls: + return None + for raw_tool_call in last_msg.tool_calls: + tool_call = normalize_tool_call(raw_tool_call) + if tool_call.get("name") == "human_interaction": + return build_human_interaction_meta(session_id, tool_call, messages) + if classify_tool_execution(tool_call.get("name"), tool_call.get("args") or {}) == "confirm": + return build_approval_meta(session_id, tool_call, messages) + return None diff --git a/src/agent/core/runtime.py b/src/agent/core/runtime.py new file mode 100644 index 0000000..8a605e8 --- /dev/null +++ b/src/agent/core/runtime.py @@ -0,0 +1,476 @@ +"""Agent 运行时:对话执行、历史持久化与人工确认续跑。 + +系统定位: + Web 层(如 ``web.ui_session``)与 LangGraph 之间的编排层。 + ``agents/*`` 组装 ``AgentRuntime`` 实例,本模块提供统一执行 API。 + +主要流程: + execute_agent: 用户消息 -> summarizer 压缩 -> graph.invoke -> 响应/待确认 + continue_after_human_action: 用户确认/拒绝 -> 工具执行或图续跑 + +可扩展性: + - ``AgentRuntime`` 可增加 checkpointer、tracing、多模态配置。 + - 可将 team 模式拆为独立 executor 类。 +""" +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +from langchain_core.messages import AIMessage, HumanMessage, ToolMessage +from langchain_core.messages import messages_from_dict +from langgraph.errors import GraphRecursionError + +from agent.core.approvals import PENDING_TOOL_APPROVALS, pending_assistant_message, remove_pending_ui_message +from agent.core.human_interaction import human_interaction_result +from agent.history.tool_call_recorder import save_turn, start_live_turn, end_live_turn, set_current_turn, set_current_session +from agent.memory.system_prompt import TEAM_MODE_PROMPT +from agent.core.routing import first_pending_tool_meta +from agent.core.tool_policy import classify_tool_execution +from agent.utils.agent_utils import assistant_text, get_history, get_session_meta, summarizer, update_session_meta +from agent.utils.tool_call_utils import normalize_tool_call +from web.ui_session import rewrite_user_message_files_to_session_dir, session_dir, sync_manifest_from_chat_history + +AgentMode = Literal["agent", "team"] + + +@dataclass +class AgentRuntime: + """单个 Agent 的运行时配置容器。 + + 字段: + name: Agent 标识,用于 session_meta 与日志。 + graph: 编译后的 LangGraph。 + llm: 原始 LLM 实例(压缩、TTS 等)。 + tools: 工具列表。 + tools_by_name: 工具名 -> 工具实例,供确认后手动 invoke。 + system_prompt: 系统提示词,传入 summarizer。 + max_iterations: 最大迭代次数,超过则输出 OOM(默认 200)。 + + 系统定位: + ``agents/main_agent`` 等模块的装配结果,传给 execute_agent。 + + 可扩展性: + 可增加 default_mode、专属 checkpointer 等字段。 + """ + + name: str + graph: Any + llm: Any + tools: list[Any] + tools_by_name: dict[str, Any] + system_prompt: str + max_iterations: int = 200 + + +_OOM_MESSAGE = "OOM: 已达到最大迭代次数限制,任务未能完成。请简化任务或增加 max_iterations 配置后重试。" + + +def _invoke_with_recursion_limit( + runtime: AgentRuntime, + graph: Any, + input_data: dict, + config: dict | None = None, +) -> dict: + """包装 graph.invoke,注入 recursion_limit 并捕获超限错误返回 OOM 消息。""" + cfg = dict(config or {}) + cfg["recursion_limit"] = runtime.max_iterations + try: + return graph.invoke(input_data, config=cfg) + except GraphRecursionError: + print(f"[OOM] Agent '{runtime.name}' 达到最大迭代次数 {runtime.max_iterations},输出 OOM。") + return {"messages": [AIMessage(content=_OOM_MESSAGE)], "_oom": True} + + +def save_messages_to_history(session_id: str, user_msg: HumanMessage, assistant_msg: AIMessage) -> None: + """将本轮用户与助手消息写入 SQLite 聊天历史。 + + 输入: + session_id: 会话 ID;空则跳过。 + user_msg: 用户 HumanMessage;含 synthetic 标记时不写入。 + assistant_msg: 助手 AIMessage。 + + 输出: + 无。 + + 系统定位: + execute_agent 正常回复结束后持久化对话。 + + 可扩展性: + 可改为异步写入、双写向量库、按 agent_name 分表。 + """ + if not session_id: + return + history = get_history(session_id) + if not user_msg.additional_kwargs.get("synthetic"): + history.add_message(user_msg) + history.add_message(assistant_msg) + + +def _message_content_equals(left: Any, right: Any) -> bool: + """宽松比较消息 content,兼容纯文本与多模态列表。""" + if left == right: + return True + return assistant_text(left) == assistant_text(right) + + +def _pending_action_id(meta: dict[str, Any] | None) -> str | None: + """从 assistant meta 中提取 pending_action.id。""" + pending_action = (meta or {}).get("pending_action") or {} + return pending_action.get("id") + + +def _remember_pending_user_content(pending_meta: dict[str, Any], user_content: Any) -> None: + """将本轮用户输入缓存到 PENDING_TOOL_APPROVALS,供确认续跑时对齐历史。""" + approval_id = _pending_action_id(pending_meta) + if approval_id and approval_id in PENDING_TOOL_APPROVALS: + PENDING_TOOL_APPROVALS[approval_id]["user_content"] = user_content + + +def _persist_messages_from_pending_turn( + session_id: str, + messages: list[Any], + pending: dict[str, Any], +) -> None: + if not session_id or not messages: + return + # 找到本轮用户消息 + user_content = pending.get("user_content") + start_index = 0 + if user_content is not None: + for idx in range(len(messages) - 1, -1, -1): + msg = messages[idx] + if isinstance(msg, HumanMessage) and not msg.additional_kwargs.get("synthetic"): + if _message_content_equals(msg.content, user_content): + start_index = idx + break + # 只提取: + # - 用户消息(跳过 synthetic) + # - 最终一条不包含 tool_calls 的助手消息 + user_msg = None + final_assistant = None + + for msg in messages[start_index:]: + if isinstance(msg, HumanMessage) and not msg.additional_kwargs.get("synthetic"): + user_msg = msg + if isinstance(msg, AIMessage) and not msg.tool_calls: + final_assistant = msg + + history = get_history(session_id) + if user_msg: + history.add_message(user_msg) + if final_assistant: + history.add_message(final_assistant) + + +def _persist_pending_rejection(session_id: str, pending: dict[str, Any], content: str) -> None: + """将用户拒绝待确认操作这一轮写入历史,避免下一轮上下文断层。""" + prior_messages = messages_from_dict(pending.get("messages") or []) + prior_messages.append(AIMessage(content=content)) + _persist_messages_from_pending_turn(session_id, prior_messages, pending) + + +def _invoke_after_manual_tool_result( + runtime: AgentRuntime, + messages: list[Any], + session_id: str, + *, + max_steps: int = 8, +) -> tuple[list[Any], dict[str, Any] | None]: + """人工确认后继续推进图,并在遇到新的待确认项时暂停。""" + current_messages = messages + for _ in range(max_steps): + result = _invoke_with_recursion_limit(runtime, runtime.graph, {"messages": current_messages}, {"configurable": {"session_id": session_id}}) + all_messages = result["messages"] + pending_meta = first_pending_tool_meta(all_messages, session_id) + if pending_meta: + return all_messages, pending_meta + + last_msg = all_messages[-1] if all_messages else None + if not isinstance(last_msg, AIMessage) or not last_msg.tool_calls: + return all_messages, None + + next_messages = [*all_messages] + progressed = False + for raw_tool_call in last_msg.tool_calls: + tool_call = normalize_tool_call(raw_tool_call) + tool_name = tool_call.get("name") + tool_args = tool_call.get("args") or {} + if classify_tool_execution(tool_name, tool_args) != "direct": + continue + tool = runtime.tools_by_name.get(tool_name or "") + if not tool: + continue + result = tool.invoke(tool_args) + tool_content = result[0] if isinstance(result, tuple) else result + call_id = tool_call.get("id") or f"manual_{session_id}_{len(next_messages)}" + next_messages.append(ToolMessage(content=str(tool_content), tool_call_id=call_id, name=tool_name or "unknown_tool")) + progressed = True + if not progressed: + return all_messages, None + current_messages = next_messages + return current_messages, None + + +def execute_agent( + runtime: AgentRuntime, + chat_history: list[dict[str, Any]], + session_id: str = "test", + output_type: str = "text", + agent_mode: AgentMode = "agent", +) -> list[dict[str, Any]]: + """处理用户新一轮输入,驱动 Agent 图执行并更新 chat_history。 + + 输入: + runtime: Agent 运行时配置。 + chat_history: Web UI 聊天历史(dict 列表);末条须为 user。 + session_id: 会话 ID,用于历史、文件目录、manifest 同步。 + output_type: text | audio | both,传入图 metadata。 + agent_mode: agent(默认)| team(占位提示)。 + + 输出: + 更新后的 chat_history;可能追加 assistant 消息或 pending_action。 + + 系统定位: + 对外主入口,由 Web/API 层在每轮用户发送后调用。 + + 可扩展性: + 可返回 generator 支持流式;可注入 middleware 做鉴权、限流。 + """ + chat_history = chat_history or [] + + if not chat_history or chat_history[-1].get("role") != "user": + return chat_history + + if session_id: + rewrite_user_message_files_to_session_dir(chat_history[-1], session_id) + + user_content = chat_history[-1]["content"] + + if agent_mode == "team": + chat_history.append({ + "role": "assistant", + "content": TEAM_MODE_PROMPT, + "meta": {"output_type": "text", "autoplay": False, "agent_mode": "team"}, + }) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + messages, context_exceeded = summarizer( + session_id=session_id, + agent_name=runtime.name, + system_prompt=runtime.system_prompt, + user_content=user_content, + llm=runtime.llm, + ) + + if context_exceeded: + chat_history.append({"role": "assistant", "content": "上下文已满,请新建会话继续对话。"}) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + graph_config = { + "configurable": {"session_id": session_id}, + "metadata": { + "output_type": output_type, + "audio_output_dir": session_dir(session_id) if session_id else None, + }, + } + + try: + # 开始实时记录(新 turn 隔离旧数据) + turn_id = "" + if session_id: + turn_id = start_live_turn(session_id) + set_current_session(session_id) + result = _invoke_with_recursion_limit(runtime, runtime.graph, {"messages": messages}, graph_config) + set_current_turn("") + set_current_session("") + all_messages = result["messages"] + pending_meta = first_pending_tool_meta(all_messages, session_id) + if pending_meta: + end_live_turn(session_id) + _remember_pending_user_content(pending_meta, user_content) + chat_history.append(pending_assistant_message(pending_meta)) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + end_live_turn(session_id) + + assistant_msg = all_messages[-1] if all_messages else AIMessage(content="") + text_content = assistant_text(assistant_msg.content) + + usage = assistant_msg.usage_metadata if hasattr(assistant_msg, "usage_metadata") else {} + total_tokens = usage.get("total_tokens", 0) if isinstance(usage, dict) else 0 + if total_tokens > 0: + meta = get_session_meta(session_id, runtime.name) + update_session_meta( + session_id=session_id, + agent_name=runtime.name, + cursor_index=meta["cursor_index"], + compressed_content=meta["compressed_content"], + last_total_tokens=total_tokens, + ) + + output_type_from_config = graph_config.get("metadata", {}).get("output_type", "text") + audio_data = runtime.llm.last_audio_kwargs or {} + if audio_data.get("output_type") != output_type_from_config: + audio_data = {} + + assistant_meta = { + "output_type": output_type_from_config, + "autoplay": False, + "audio": audio_data.get("audio"), + } + + if output_type_from_config == "audio" and assistant_meta.get("audio"): + assistant_content = [] + elif output_type_from_config == "both" and assistant_meta.get("audio"): + assistant_content = text_content + else: + assistant_content = text_content if text_content else "(空回复)" + + chat_history.append({"role": "assistant", "content": assistant_content, "meta": assistant_meta}) + save_messages_to_history(session_id, HumanMessage(content=user_content), AIMessage(content=text_content)) + runtime.llm.last_audio_kwargs = None + + # 保存工具调用记录 + if session_id and turn_id: + save_turn(session_id, turn_id, {"role": "user", "content": user_content}, all_messages) + + except Exception: + import traceback + set_current_turn("") + set_current_session("") + end_live_turn(session_id) + print("[ERROR] 调用模型时出错:") + traceback.print_exc() + chat_history.append({"role": "assistant", "content": "抱歉,我遇到了一些问题,请稍后再试。"}) + + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + +def continue_after_human_action( + runtime: AgentRuntime, + chat_history: list[dict[str, Any]], + session_id: str, + approval_id: str, + decision: Literal["approve", "reject", "edit"], + instruction: str | None = None, + output_type: str = "text", +) -> list[dict[str, Any]]: + """用户对待确认项做出决策后,继续执行或结束该轮交互。 + + 输入: + runtime: Agent 运行时。 + chat_history: 当前聊天历史。 + session_id: 会话 ID。 + approval_id: pending_action.id。 + decision: approve | reject | edit(edit 用于 human_interaction selection 提交文本)。 + instruction: selection 时为用户输入文本。 + output_type: 续跑后的回复输出类型。 + + 输出: + 更新后的 chat_history。 + + 系统定位: + UI 确认框回调入口;按 pending.type 分支: + human_interaction / tool_call。 + + 可扩展性: + 可增加 timeout 自动 reject;支持批量 approve 多个 tool_call。 + """ + chat_history = chat_history or [] + pending = PENDING_TOOL_APPROVALS.pop(approval_id, None) + if not pending: + chat_history.append({"role": "assistant", "content": "该待确认项已处理或已过期。", "meta": {"output_type": "text"}}) + return chat_history + + remove_pending_ui_message(chat_history, approval_id) + + if decision == "reject" and pending.get("type") != "human_interaction": + content = "已拒绝,本次待确认操作不会执行。" + chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": "text"}}) + _persist_pending_rejection(session_id, pending, content) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + if pending.get("type") == "human_interaction": + try: + tool_call = dict(pending.get("tool_call") or {}) + prior_messages = messages_from_dict(pending.get("messages") or []) + call_id = tool_call.get("id") or f"human_{approval_id}" + tool_content = human_interaction_result(pending, decision, instruction) + prior_messages.append(ToolMessage(content=tool_content, tool_call_id=call_id, name="human_interaction")) + all_messages, pending_meta = _invoke_after_manual_tool_result(runtime, prior_messages, session_id) + if pending_meta: + _remember_pending_user_content(pending_meta, pending.get("user_content")) + chat_history.append(pending_assistant_message(pending_meta)) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + assistant_msg = all_messages[-1] if all_messages else AIMessage(content="") + content = assistant_text(assistant_msg.content) or "(空回复)" + persisted_messages = all_messages + except Exception: + import traceback + traceback.print_exc() + content = "处理人工交互结果时遇到问题。" + persisted_messages = messages_from_dict(pending.get("messages") or []) + chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False}}) + _persist_messages_from_pending_turn(session_id, persisted_messages, pending) + + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + tool_call = dict(pending.get("tool_call") or {}) + tool_name = tool_call.get("name") + tool_args = tool_call.get("args") or {} + tool = runtime.tools_by_name.get(tool_name) + if not tool: + chat_history.append({"role": "assistant", "content": f"找不到工具:{tool_name}", "meta": {"output_type": "text"}}) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + + try: + result = tool.invoke(tool_args) + tool_content = result[0] if isinstance(result, tuple) else result + prior_messages = messages_from_dict(pending.get("messages") or []) + call_id = tool_call.get("id") or f"manual_{approval_id}" + prior_messages.append(ToolMessage(content=str(tool_content), tool_call_id=call_id, name=tool_name)) + all_messages, pending_meta = _invoke_after_manual_tool_result(runtime, prior_messages, session_id) + if pending_meta: + _remember_pending_user_content(pending_meta, pending.get("user_content")) + chat_history.append(pending_assistant_message(pending_meta)) + if session_id: + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history + assistant_msg = all_messages[-1] if all_messages else AIMessage(content="") + content = assistant_text(assistant_msg.content) or "(空回复)" + persisted_messages = all_messages + except Exception: + import traceback + traceback.print_exc() + content = "执行确认后的工具调用时遇到问题。" + prior_messages = messages_from_dict(pending.get("messages") or []) + call_id = tool_call.get("id") or f"manual_{approval_id}" + prior_messages.extend([ + ToolMessage(content=content, tool_call_id=call_id, name=tool_name or "unknown_tool"), + AIMessage(content=content), + ]) + persisted_messages = prior_messages + + chat_history.append({"role": "assistant", "content": content, "meta": {"output_type": output_type, "autoplay": False}}) + _persist_messages_from_pending_turn(session_id, persisted_messages, pending) + if session_id: + save_turn(session_id, pending.get("user_content") or {}, persisted_messages) + sync_manifest_from_chat_history(session_id, chat_history, "") + return chat_history diff --git a/src/agent/core/state.py b/src/agent/core/state.py new file mode 100644 index 0000000..aaf9c01 --- /dev/null +++ b/src/agent/core/state.py @@ -0,0 +1,32 @@ +"""LangGraph Agent 状态类型定义。 + +系统定位: + 定义图运行时共享状态 ``AgentState``,被 ``graph_builder``、``nodes``、 + ``routing`` 作为 StateGraph 的 schema 使用。 + +可扩展性: + - 可在此增加 ``session_id``、``agent_mode``、``pending_approval`` 等字段, + 需配合 reducer(如 add_messages)声明合并策略。 +""" +from __future__ import annotations + +from typing import Annotated, Sequence, TypedDict + +from langchain_core.messages import BaseMessage +from langgraph.graph.message import add_messages + + +class AgentState(TypedDict): + """LangGraph 图状态:消息列表为唯一状态字段。 + + 字段: + messages: 对话消息序列,通过 ``add_messages`` 追加合并。 + + 系统定位: + ReAct 循环(agent -> tools -> process_tool_artifact -> agent)的载体。 + + 可扩展性: + 扩展字段时保持 TypedDict 与 reducer 注解一致,避免并发更新冲突。 + """ + + messages: Annotated[Sequence[BaseMessage], add_messages] diff --git a/src/agent/core/tool_config.json b/src/agent/core/tool_config.json new file mode 100644 index 0000000..2d14630 --- /dev/null +++ b/src/agent/core/tool_config.json @@ -0,0 +1,58 @@ +[ + { + "name": "web_search_class", + "display_name": "WebSearch", + "description": "一个网络搜索工具,可以根据用户的查询返回相关的搜索结果。仅当需要查询近期知识或已有知识无法解答需要联网查询时调用,其余任何情况严禁调用。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "date_query_class", + "display_name": "DateQuery", + "description": "一个日期查询工具,可以返回当前的日期信息。仅当需要查询当前时间时调用,其余任何情况严禁调用。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "screen_capture", + "display_name": "ScreenCapture", + "description": "截取当前主屏幕画面。除了返回文字描述外,也会提供原始截图给模型进行视觉分析。注意:这个工具仅用于获取当前屏幕的实时画面,其余任何情况严禁调用。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "browser_control", + "display_name": "BrowserControl", + "description": "统一控制浏览器:打开/关闭浏览器、刷新/关闭标签页、打开指定 URL。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "human_interaction", + "display_name": "HumanInteraction", + "description": "当任务缺少关键信息、需要人类选择路径、或需要人类审批后才能继续时调用。三种类型分别是:information=请求人类补充缺失信息;selection=让人类在多个选项中选择且可编辑;approval=请求人类审批高影响决策或继续执行。工具返回必须被视为用户原始意图的纯字符串反馈。其余情况严禁调用。", + "permission": "confirm", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "software_control", + "display_name": "SoftwareControl", + "description": "操作本机已安装的软件。工具会自动扫描 Windows 开始菜单快捷方式、桌面快捷方式和 App Paths,建立软件索引后根据用户输入做模糊匹配。action=open 时打开软件;action=close 时仅做正常关闭;action=force_close 时才允许在正常关闭失败后强制结束进程。仅当用户明确要求打开、关闭或强制关闭某个本机软件时调用。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + }, + { + "name": "directory_listing", + "display_name": "DirectoryListing", + "description": "列出指定目录下的所有文件和子目录。可用于探索文件系统、查找文件位置。仅当需要获取目录内容或确认文件结构时调用,其余情况严禁调用。", + "permission": "direct", + "auto_execute_rules": [], + "enabled": true + } +] \ No newline at end of file diff --git a/src/agent/core/tool_policy.py b/src/agent/core/tool_policy.py new file mode 100644 index 0000000..af8dc3e --- /dev/null +++ b/src/agent/core/tool_policy.py @@ -0,0 +1,144 @@ +"""工具执行安全策略:直接执行 vs 需人工确认。 + +系统定位: + 在 ``core/routing.should_continue`` 中决定图是否进入 ToolNode, + 或在遇到需确认工具时提前结束图执行,交由 UI 与 ``runtime`` 处理。 + +自动执行规则 (auto_execute_rules): + 对于权限为 ``confirm`` 的工具,可通过 tool_config.json 中的 + ``auto_execute_rules`` 定义参数条件,匹配时自动升级为 ``direct``。 + 每条规则格式: ``{"parameter": "action", "operator": "equals", "value": "query"}`` + 含义: 当工具调用的 ``action`` 参数等于 ``"query"`` 时,跳过确认直接执行。 + +可扩展性: + - 可增加 operator 类型(contains、in、regex)。 + - 可返回 ``Literal["direct", "confirm", "deny"]`` 支持完全禁止类工具。 +""" +from __future__ import annotations + +from contextlib import contextmanager +from contextvars import ContextVar +from typing import Literal + +_FORCE_CONFIRM_TOOLS: ContextVar[bool] = ContextVar("force_confirm_tools", default=False) + +# 从 tool_config.json 加载的自动执行规则缓存 +_AUTO_EXECUTE_RULES_CACHE: dict[str, list[dict]] | None = None +# 从 tool_config.json 加载的工具权限缓存 +_TOOL_PERMISSIONS_CACHE: dict[str, str] | None = None + + +def _get_tool_permissions() -> dict[str, str]: + """懒加载 tool_config.json 中各工具的 permission 字段。 + + 返回: + {tool_name: "direct"|"confirm"} 映射;加载失败返回空字典。 + """ + global _TOOL_PERMISSIONS_CACHE + if _TOOL_PERMISSIONS_CACHE is not None: + return _TOOL_PERMISSIONS_CACHE + try: + from agent.core.config_manager import load_tool_configs + configs = load_tool_configs() + _TOOL_PERMISSIONS_CACHE = {} + for c in configs: + if c.enabled: + _TOOL_PERMISSIONS_CACHE[c.name] = c.permission + except Exception: + _TOOL_PERMISSIONS_CACHE = {} + return _TOOL_PERMISSIONS_CACHE + + +def _get_auto_execute_rules() -> dict[str, list[dict]]: + """懒加载 tool_config.json 中的 auto_execute_rules。 + + 返回: + {tool_name: [rule, ...]} 映射;加载失败返回空字典。 + """ + global _AUTO_EXECUTE_RULES_CACHE + if _AUTO_EXECUTE_RULES_CACHE is not None: + return _AUTO_EXECUTE_RULES_CACHE + try: + from agent.core.config_manager import load_tool_configs + configs = load_tool_configs() + _AUTO_EXECUTE_RULES_CACHE = {} + for c in configs: + if c.enabled and c.auto_execute_rules: + _AUTO_EXECUTE_RULES_CACHE[c.name] = c.auto_execute_rules + except Exception: + _AUTO_EXECUTE_RULES_CACHE = {} + return _AUTO_EXECUTE_RULES_CACHE + + +def reload_auto_execute_rules() -> None: + """清除缓存,下次 classify 时重新从 JSON 加载。""" + global _AUTO_EXECUTE_RULES_CACHE, _TOOL_PERMISSIONS_CACHE + _AUTO_EXECUTE_RULES_CACHE = None + _TOOL_PERMISSIONS_CACHE = None + + +@contextmanager +def force_confirm_tools(): + token = _FORCE_CONFIRM_TOOLS.set(True) + try: + yield + finally: + _FORCE_CONFIRM_TOOLS.reset(token) + + +def _match_auto_execute_rule(rule: dict, args: dict) -> bool: + """检查工具调用的 args 是否匹配单条自动执行规则。 + + 输入: + rule: {"parameter": str, "operator": str, "value": str} + args: 工具调用的参数 dict。 + + 输出: + 匹配则返回 True。 + + 支持的 operator: + - "equals": 参数值(转为字符串后)完全等于 rule value。 + """ + param = rule.get("parameter", "") + op = rule.get("operator", "equals") + expected = str(rule.get("value", "")) + actual = str(args.get(param, "")) + if op == "equals": + return actual == expected + return False + + +def classify_tool_execution(tool_name: str | None, args: dict) -> Literal["direct", "confirm"]: + """判定工具调用是否可在图内自动执行。 + + 判定优先级: + 1. 空名称 / force_confirm / human_interaction → 强制 confirm + 2. tool_config.json 中 permission == "direct" → direct + 3. tool_config.json 中 auto_execute_rules 匹配 → direct + 4. 其余 → confirm + + 注意: + 参数提取时使用安全默认值,避免 KeyError 或 None 导致的误判。 + """ + if not tool_name: + return "confirm" + if _FORCE_CONFIRM_TOOLS.get(): + return "confirm" + # human_interaction 必须人工确认,不可绕过 + if tool_name == "human_interaction": + return "confirm" + + # 读取 tool_config.json 中的权限(含 safe_tools 默认 direct,用户可改) + permissions = _get_tool_permissions() + if permissions.get(tool_name) == "direct": + return "direct" + + # 检查 JSON 配置的 auto_execute_rules + rules = _get_auto_execute_rules().get(tool_name) + if rules: + for rule in rules: + if _match_auto_execute_rule(rule, args): + return "direct" + + # 未匹配默认需确认 + return "confirm" diff --git a/src/agent/history/data_base/chat_history.db b/src/agent/history/data_base/chat_history.db deleted file mode 100644 index 597207190745435f4bf94b6f5c87d3a498017d21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57344 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 \"through education\" (更正式且准确)。\n * \"cit ies\" -> \"cities\", \"glib\" (圆滑通常含贬义) -> \"shrewd\" (精明) 或直接调整语境。\n * \"donate my body, my blood...\" -> \"dedicate my strength, my heart...\" (原表达在英语中听起来过于血腥或医疗化,改为“奉献”更得体)。\n\n2. **提升流畅度与节奏**:\n * 将 \"never never never back again\" 简化为 \"never return\",在演讲中更有力。\n * 将 \"More and more people who succeed in their domains...\" 简化为 \"Success stories are bringing...\",减少冗余。\n\n3. **优化对比表达**:\n * 关于美国/西方的部分,原句 \"in Garza\" 可能是拼写错误或指代不明(可能是指 Gaza 加沙,或者是特定的地缘政治事件,但在演讲中容易引发歧义或过于具体)。修改版将其概括为 \"self-serving actions some nations have taken...\",既保留了原意(反对以民主自由为名的损人利己),又让表达更通用、安全且有力。\n\n4. **强化结尾**:\n * 将 \"road wider, sky bluer...\" 的排比句保留并理顺,使其朗朗上口。\n\n这个版本更适合口语表达,听众更容易抓住重点,同时保留了演讲者从贫困到奋斗再到回馈家乡的感人逻辑。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - }, - { - "user": { - "content": [ - { - "type": "image_url", - "relpath": "20260527_215121/后端建议_20260527_215121_1.png", - "name": "后端建议.png" - }, - { - "type": "text", - "text": "帮我总结一下这张图片的内容" - } - ] - }, - "assistant": { - "text": "这张图片是一份关于**后端编程语言推荐**的个人建议清单,内容如下:\n\n**标题**:后端编程语言推荐 \n**副标题**:以下仅代表我个人建议\n\n**具体建议**:\n\n1. **Java**:如果你打算未来深耕 Web 后端开发,推荐选择 Java。理由是 Java 在国内 Web 开发领域的地位非常稳固,难以撼动。\n2. **Node.js**:如果你是前端程序员,想转行做后端,推荐选择 Node.js(因为前后端技术栈接近,上手快)。\n3. **Python**:除上述两种情况外,其他场景都推荐 Python。其最大优点是**简单、效率高**。\n4. **PHP**:明确建议“千万不要学 PHP”,表达了一种强烈的否定态度。\n\n✅ 总结: \n这是一份带有强烈个人倾向的后端语言选择指南,核心逻辑是:\n- 想稳扎稳打做企业级 Web 后端 → 选 Java;\n- 前端转后端 → 选 Node.js;\n- 其他通用或快速开发场景 → 选 Python;\n- PHP → 不推荐。\n\n⚠️ 注意:图中明确标注“仅代表我个人建议”,说明这不是权威结论,而是作者基于自身经验或市场观察给出的主观推荐。\n\n(注:虽然图中未提及 Go、C#、Rust 等现代后端语言,但这是“个人建议”,并非全面技术选型指南。)", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260527_215121/后端建议_20260527_215121_1.png b/src/agent/history/multi_inputs/20260527_215121/后端建议_20260527_215121_1.png deleted file mode 100644 index 4847cb58aa29c37587b7537092c37ba741ecb4d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 422238 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

<&f={DcJVHW=zNm>paa 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 &1ur65#0A;OO68j86czWC<%j*lqA# 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

Q3f;`=LK&S(sFqCG!Q_(i(=cj^L44_Y7eWXH82 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 diff --git a/src/agent/history/multi_inputs/20260527_215121/英语演讲——我的故事_20260527_215121_1.pdf b/src/agent/history/multi_inputs/20260527_215121/英语演讲——我的故事_20260527_215121_1.pdf deleted file mode 100644 index 0ccc36f0703bb2aef5537078b401d5f943556b66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84903 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 diff --git a/src/agent/history/multi_inputs/20260527_215307/22级-25年优秀学生干部_20260527_215307_1.xlsx b/src/agent/history/multi_inputs/20260527_215307/22级-25年优秀学生干部_20260527_215307_1.xlsx deleted file mode 100644 index 8cdc84e963eb911200cf3ea54543e40f2b414914..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9042 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 diff --git a/src/agent/history/multi_inputs/20260527_215307/ui_messages.json b/src/agent/history/multi_inputs/20260527_215307/ui_messages.json deleted file mode 100644 index 9da7e4e..0000000 --- a/src/agent/history/multi_inputs/20260527_215307/ui_messages.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "file", - "relpath": "20260527_215307/22级-25年优秀学生干部_20260527_215307_1.xlsx", - "name": "22级-25年优秀学生干部.xlsx" - }, - { - "type": "text", - "text": "帮我提取内容并加以说明" - } - ] - }, - "assistant": { - "text": "这份文件是一份关于**22级学生评选2025年优秀学生干部**的名单汇总表。\n\n### 📋 内容提取与整理\n\n该表格共包含9个班级/项目,每个单位评选出了一名**校级**优秀学生干部和一名**院级**优秀学生干部。具体名单如下:\n\n| 班级/项目 | 校级优秀学生干部 | 院级优秀学生干部 |\n| :--- | :--- | :--- |\n| **通信一班** | 刘和宝 | 郭思宇 |\n| **通信二班** | 董卓然 | 王雨萌 |\n| **通信三班** | 朱洪亿 | 李爽爽 |\n| **电科一班** | 李皓威 | 邱浩原 |\n| **电科二班** | 赵永顺 | 王子瑶 |\n| **光信** | 纪天泽 | 温致涵 |\n| **光创** | 王志成 | 曹晶晶 |\n| **微纳** | 王隆昊 | 张加祥 |\n| **崇新** | 庞开升 | 刘希言 |\n\n---\n\n### 💡 说明与分析\n\n1. **评选层级**:\n * **校级**:代表该班级/项目中最优秀的学生干部,将代表学院参加学校层面的评选或表彰。\n * **院级**:代表学院内部评选出的优秀学生干部,是校级评选的后备或并列荣誉。\n\n2. **涉及专业/班级**:\n * 名单涵盖了**通信工程**(分三个班)、**电子信息科学与技术**(分两个班)以及**光电信息**相关方向(光信、光创、微纳、崇新等创新班或项目班)。这表明该评选主要面向电子信息类相关专业的大二学生(22级)。\n\n3. **文件命名含义**:\n * `22级-25年优秀学生干部`:表明这是针对2022级入学学生,在2025年进行的优秀学生干部评选(通常评选周期可能跨越学年,或指2025年度的荣誉)。\n * `20260527_215307`:文件生成的时间戳显示为 **2026年5月27日 21:53:07**。这暗示该文件可能是基于未来时间设定的模拟数据,或者是系统时间设置在未来/测试环境下的产物。\n\n4. **数据用途**:\n * 该表格通常用于公示评选结果、录入教务系统、制作荣誉证书或作为档案留存。\n\n如果您需要将这些数据转换为特定的格式(如CSV、JSON)或进行统计分析,请随时告诉我!", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_121405/ui_messages.json b/src/agent/history/multi_inputs/20260603_121405/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_121405/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_121521/ui_messages.json b/src/agent/history/multi_inputs/20260603_121521/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_121521/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_121652/ui_messages.json b/src/agent/history/multi_inputs/20260603_121652/ui_messages.json deleted file mode 100644 index 6441e65..0000000 --- a/src/agent/history/multi_inputs/20260603_121652/ui_messages.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - }, - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_121729/ui_messages.json b/src/agent/history/multi_inputs/20260603_121729/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_121729/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_122026/ui_messages.json b/src/agent/history/multi_inputs/20260603_122026/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_122026/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_122112/ui_messages.json b/src/agent/history/multi_inputs/20260603_122112/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_122112/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_122357/ui_messages.json b/src/agent/history/multi_inputs/20260603_122357/ui_messages.json deleted file mode 100644 index 808aa20..0000000 --- a/src/agent/history/multi_inputs/20260603_122357/ui_messages.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "你好" - } - ] - }, - "assistant": { - "text": "抱歉,我遇到了一些问题,请稍后再试。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_172211/ui_messages.json b/src/agent/history/multi_inputs/20260603_172211/ui_messages.json deleted file mode 100644 index c995483..0000000 --- a/src/agent/history/multi_inputs/20260603_172211/ui_messages.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "帮我打开b站" - } - ] - }, - "assistant": { - "text": "\n\n\nopen\n\n\nhttps://www.bilibili.com\n\n\n", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - }, - { - "user": { - "content": [ - { - "type": "text", - "text": "现在几点了" - } - ] - }, - "assistant": { - "text": "\n\n\n", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/multi_inputs/20260603_175632/ui_messages.json b/src/agent/history/multi_inputs/20260603_175632/ui_messages.json deleted file mode 100644 index 999dab1..0000000 --- a/src/agent/history/multi_inputs/20260603_175632/ui_messages.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "version": 2, - "turns": [ - { - "user": { - "content": [ - { - "type": "text", - "text": "现在几点了" - } - ] - }, - "assistant": { - "text": "现在是 2026年6月3日 17:56:36。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - }, - { - "user": { - "content": [ - { - "type": "text", - "text": "帮我打开b站和虎牙" - } - ] - }, - "assistant": { - "text": "好的!从截图中可以看到:\n\n1. **B站已经打开了**:第5个标签页显示的是\"啊哈啊哈! - bilibili\"\n2. **虎牙也打开了**:当前页面正在浏览虎牙直播网站,可以看到虎牙的Logo、导航栏(首页、直播、分类、视频、游戏等),以及正在播放的直播内容和右侧的推荐直播间\n\n两个网站都已经成功打开。当前显示的是虎牙直播页面,如果您想查看B站,可以点击第5个标签页切换到B站。", - "output_type": "text", - "autoplay": false, - "audio_duration_seconds": null, - "audio_relpath": null - } - } - ] -} \ No newline at end of file diff --git a/src/agent/history/tool_call_recorder.py b/src/agent/history/tool_call_recorder.py new file mode 100644 index 0000000..7101914 --- /dev/null +++ b/src/agent/history/tool_call_recorder.py @@ -0,0 +1,263 @@ +"""工具调用历史记录器:按 session/turn 存储及实时推送。 + +设计: + 每次用户输入 → 新建一个 turn,图节点执行期间通过 live 字典实时推送, + 前端轮询显示;turn 结束后 persist 到 JSON 并清除 live 记录。 + 不同 turn 间数据完全隔离,不会泄漏到新对话中。 +""" +from __future__ import annotations + +import json +import os +import base64 +import time +import threading +from contextvars import ContextVar +from datetime import datetime +from pathlib import Path +from typing import Any + +from langchain_core.messages import BaseMessage, AIMessage, HumanMessage, ToolMessage + +_HISTORY_ROOT = Path(__file__).resolve().parent +TOOL_CALLING_ROOT = _HISTORY_ROOT / "tool_calling" + +# ---------- 当前 turn 上下文 ---------- +_current_turn_id: ContextVar[str] = ContextVar("current_turn_id", default="") +_current_session_id: ContextVar[str] = ContextVar("current_session_id", default="") + +# ---------- 实时记录(线程安全) ---------- +_live_lock = threading.Lock() +_live_records: dict[str, list[dict[str, Any]]] = {} # key = session_id + + +def set_current_turn(turn_id: str) -> None: + _current_turn_id.set(turn_id) + + +def get_current_turn() -> str: + return _current_turn_id.get() + + +def set_current_session(session_id: str) -> None: + _current_session_id.set(session_id) + + +def get_current_session() -> str: + return _current_session_id.get() + + +def start_live_turn(session_id: str) -> str: + """开始一个新 turn 并返回 turn_id。清除该 session 旧 live 记录。""" + turn_id = datetime.now().strftime("%Y%m%d_%H%M%S") + set_current_turn(turn_id) + with _live_lock: + _live_records[session_id] = [] + return turn_id + + +def record_tool_call_live(session_id: str, name: str, args: dict[str, Any]) -> None: + """图节点中调用:记录工具调用开始。""" + with _live_lock: + if session_id not in _live_records: + _live_records[session_id] = [] + _live_records[session_id].append({ + "name": name, + "args_summary": _short_args(args), + "status": "running", + "result_text": "", + "timestamp": time.time(), + }) + + +def record_tool_result_live(session_id: str, name: str, result_text: str, + image_bytes: bytes | None = None) -> None: + """图节点中调用:记录工具返回结果。""" + with _live_lock: + records = _live_records.get(session_id, []) + for rec in reversed(records): + if rec["name"] == name and rec["status"] == "running": + rec["status"] = "done" + rec["result_text"] = (result_text or "")[:500] + if image_bytes: + rec["image_base64"] = base64.standard_b64encode(image_bytes).decode("ascii") + return + + +def end_live_turn(session_id: str) -> list[dict[str, Any]]: + """结束实时记录并返回该 turn 所有记录。""" + set_current_turn("") + with _live_lock: + return _live_records.pop(session_id, []) + + +def get_live_tool_calls(session_id: str) -> list[dict[str, Any]]: + """轮询接口:当前执行中 turn 的实时工具调用列表。""" + with _live_lock: + return list(_live_records.get(session_id, [])) + + +def _short_args(args: dict[str, Any], max_len: int = 40) -> str: + s = json.dumps(args, ensure_ascii=False) + if len(s) > max_len: + return s[:max_len] + "..." + return s + + +# ---------- 持久化 ---------- +def _ensure_dir(path: Path) -> None: + path.mkdir(parents=True, exist_ok=True) + + +def session_tool_dir(session_id: str) -> Path: + return TOOL_CALLING_ROOT / session_id + + +def extract_tool_calls_from_messages(messages: list[BaseMessage]) -> list[dict[str, Any]]: + """从图执行后的消息列表中提取完整的工具调用链。""" + records: list[dict[str, Any]] = [] + pending: dict[str, dict[str, Any]] = {} + for msg in messages: + if isinstance(msg, AIMessage) and msg.tool_calls: + for tc in msg.tool_calls: + d = tc if isinstance(tc, dict) else {"name": tc.name, "args": tc.args, "id": tc.id} + if d.get("id"): + pending[d["id"]] = { + "name": d.get("name", "unknown"), + "args": _json_safe(d.get("args", {})), + "result_text": "", + "result_files": [], + } + elif isinstance(msg, ToolMessage) and msg.tool_call_id: + cid = msg.tool_call_id + if cid in pending: + entry = pending.pop(cid) + entry["result_text"] = str(msg.content) if msg.content else "" + if msg.artifact: + entry["result_files"] = _save_artifact_files(msg.artifact, msg.tool_call_id) + records.append(entry) + for entry in pending.values(): + records.append(entry) + return records + + +def _json_safe(value: Any) -> Any: + try: + json.dumps(value, ensure_ascii=False) + return value + except TypeError: + if isinstance(value, dict): + return {str(k): _json_safe(v) for k, v in value.items()} + if isinstance(value, (list, tuple)): + return [_json_safe(v) for v in value] + return str(value) + + +def _save_artifact_files(artifact: Any, base_name: str) -> list[str]: + files: list[str] = [] + if isinstance(artifact, dict) and "image_bytes" in artifact: + files.append(f"_artifact_{base_name}_0.png") + elif isinstance(artifact, list): + for i, item in enumerate(artifact): + if isinstance(item, dict) and "image_bytes" in item: + files.append(f"_artifact_{base_name}_{i}.png") + elif isinstance(item, bytes): + files.append(f"_artifact_{base_name}_{i}.png") + elif isinstance(artifact, bytes): + files.append(f"_artifact_{base_name}.png") + return files + + +def _find_artifact_bytes(messages: list[BaseMessage], fname: str) -> bytes | None: + for msg in messages: + if isinstance(msg, ToolMessage) and msg.artifact: + a = msg.artifact + if isinstance(a, dict) and "image_bytes" in a: + return a["image_bytes"] + if isinstance(a, list): + for item in a: + if isinstance(item, dict) and "image_bytes" in item: + return item["image_bytes"] + if isinstance(item, bytes): + return item + if isinstance(a, bytes): + return a + return None + + +def save_turn(session_id: str, turn_id: str, user_message: dict[str, Any], + messages: list[BaseMessage]) -> str | None: + """持久化一轮完整的工具调用记录。""" + tool_calls = extract_tool_calls_from_messages(messages) + if not tool_calls: + return None + + assistant_response = "" + for msg in reversed(messages): + if isinstance(msg, AIMessage) and not msg.tool_calls: + assistant_response = msg.content if isinstance(msg.content, str) else str(msg.content) + break + + session_dir = session_tool_dir(session_id) + turn_dir = session_dir / turn_id + _ensure_dir(turn_dir) + + for tc in tool_calls: + for fname in list(tc.get("result_files", [])): + img_bytes = _find_artifact_bytes(messages, fname) + if img_bytes: + (turn_dir / fname).write_bytes(img_bytes) + else: + tc["result_files"].remove(fname) + + record = { + "session_id": session_id, + "turn_id": turn_id, + "user_message": _json_safe(user_message), + "tool_calls": tool_calls, + "assistant_response": assistant_response, + "timestamp": turn_id, + } + json_path = session_dir / f"turn_{turn_id}.json" + with open(json_path, "w", encoding="utf-8") as f: + json.dump(record, f, ensure_ascii=False, indent=2) + return str(json_path) + + +def list_turns(session_id: str) -> list[dict[str, Any]]: + """列出会话下所有 turn(按时间倒序,最近5轮)。""" + session_dir = session_tool_dir(session_id) + if not session_dir.is_dir(): + return [] + turns = [] + for f in sorted(session_dir.glob("turn_*.json"), reverse=True): + try: + with open(f, "r", encoding="utf-8") as fh: + turns.append(json.load(fh)) + except (json.JSONDecodeError, OSError): + pass + return turns[:5] + + +def get_latest_turn(session_id: str) -> dict[str, Any] | None: + """获取最近一个 turn 的完整记录。""" + turns = list_turns(session_id) + return turns[0] if turns else None + + +def get_tool_calls_for_latest_turn(session_id: str) -> list[dict[str, Any]]: + """获取最近一个 turn 的工具调用(供 UI 渲染)。""" + latest = get_latest_turn(session_id) + if not latest: + return [] + turn_id = latest.get("turn_id", "") + result = [] + for tc in latest.get("tool_calls", []): + entry = dict(tc) + full_files = [] + for fname in tc.get("result_files", []): + full_files.append(str(session_tool_dir(session_id) / turn_id / fname)) + entry["result_files_full"] = full_files + entry["turn_id"] = turn_id + result.append(entry) + return result diff --git a/src/agent/llm.py b/src/agent/llm.py index 6f29272..a88b66b 100644 --- a/src/agent/llm.py +++ b/src/agent/llm.py @@ -1,489 +1,3 @@ -# agent/llm.py -import io -import os -import tempfile -import wave -import pyaudio -from contextlib import closing -from typing import Any, List, Dict, Optional -from pydantic import Field -import re, json -from datetime import datetime as dt -import requests -from concurrent.futures import ThreadPoolExecutor, as_completed - -from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, SystemMessage, ToolCall -from langchain_core.outputs import ChatResult, ChatGeneration -from langchain_openai import ChatOpenAI - -from agent.utils.image_utils import image_bytes_to_openai_image_url_part -from agent.utils.env_utils import LLM_API_KEY, LLM_BASE_URL, LLM_MODEL, LLM_TIMEOUT, TTS_BASE_URL, ASR_BASE_URL -from agent.utils.agent_utils import assistant_text, Documents_process, split_text, merge_wavs -from agent.utils.image_utils import image_path_to_openai_image_url_part, image_bytes_to_openai_image_url_part, IMAGE_FILE_EXTENSIONS -from agent.memory.system_prompt import VISION_SYSTEM_PROMPT - -class ChatQwen(ChatOpenAI): - output_type: str = Field(default="text") - stream_play: bool = Field(default=False) - last_audio_kwargs: Optional[dict] = Field(default=None) - def __init__(self, **kwargs: Any) -> None: - super().__init__( - model=kwargs.pop("model", LLM_MODEL), - api_key=kwargs.pop("api_key", LLM_API_KEY), - base_url=kwargs.pop("base_url", LLM_BASE_URL), - timeout=kwargs.pop("timeout", LLM_TIMEOUT), - max_retries=kwargs.pop("max_retries", 0), - **kwargs - ) - - @staticmethod - def play_audio_bytes(audio_bytes: bytes, chunk_size: int = 1024) -> None: - if not audio_bytes: - return - wf = wave.open(io.BytesIO(audio_bytes), 'rb') - p = pyaudio.PyAudio() - stream = p.open( - format=p.get_format_from_width(wf.getsampwidth()), - channels=wf.getnchannels(), - rate=wf.getframerate(), - output=True, - ) - data = wf.readframes(chunk_size) - while data: - stream.write(data) - data = wf.readframes(chunk_size) - stream.stop_stream() - stream.close() - p.terminate() - wf.close() - - @staticmethod - def _process_image_content(part: Dict[str, Any]) -> Dict[str, Any]: - image_type = part.get("type") - if image_type != "image_url": - return part - image_url = part.get("image_url", {}) - url = image_url.get("url", "") - if url.startswith(("http://", "https://", "data:")): - return part - if isinstance(url, str) and os.path.exists(url): - ext = os.path.splitext(url)[1].lower() - if ext in IMAGE_FILE_EXTENSIONS: - result = image_path_to_openai_image_url_part(url) - return result if result else {"type": "text", "text": "[图片处理失败]"} - if isinstance(url, bytes): - result = image_bytes_to_openai_image_url_part(url) - return result if result else {"type": "text", "text": "[图片处理失败]"} - return part - - @staticmethod - def _convert_audio_to_wav(audio_path: str) -> str: - ext = os.path.splitext(audio_path)[1].lower() - if ext == ".wav": - return audio_path - try: - from pydub import AudioSegment - except ImportError as exc: - raise RuntimeError("当前 ASR 仅支持 WAV,但未安装 pydub/ffmpeg") from exc - try: - audio = AudioSegment.from_file(audio_path) - except Exception as exc: - raise RuntimeError(f"无法读取音频文件: {exc}") from exc - fd, wav_path = tempfile.mkstemp(suffix=".wav", prefix="asr_") - os.close(fd) - try: - audio.export(wav_path, format="wav") - return wav_path - except Exception: - if os.path.exists(wav_path): - os.remove(wav_path) - raise - - def _audio_to_text(self, audio_path: str) -> str: - if not os.path.exists(audio_path): - raise FileNotFoundError(f"音频文件不存在: {audio_path}") - - wav_path = self._convert_audio_to_wav(audio_path) - cleanup_wav = (wav_path != audio_path) - - try: - # 读取音频文件二进制内容 - with open(wav_path, "rb") as f: - audio_bytes = f.read() - - # 构建 Data URL - import base64 - b64 = base64.b64encode(audio_bytes).decode("ascii") - mime_type = "audio/wav" # 我们已经转换为 wav - data_url = f"data:{mime_type};base64,{b64}" - - payload = { - "messages": [ - { - "role": "user", - "content": [ - {"type": "audio_url", "audio_url": {"url": data_url}} - ] - } - ] - } - endpoint = f"{ASR_BASE_URL.rstrip('/')}/chat/completions" - resp = requests.post( - endpoint, - headers={"Content-Type": "application/json"}, - json=payload, - timeout=120 - ) - resp.raise_for_status() - result = resp.json() - content = result["choices"][0]["message"]["content"] - return content.strip() - finally: - if cleanup_wav and os.path.exists(wav_path): - os.remove(wav_path) - - def _preprocess_messages(self, messages: List[BaseMessage]) -> List[BaseMessage]: - new_messages = [] - for msg in messages: - if isinstance(msg, HumanMessage) and isinstance(msg.content, str): - text = msg.content.strip() - if text.startswith("[") and text.endswith("]"): - try: - possible_list = json.loads(text) - if isinstance(possible_list, list): - msg = HumanMessage(content=possible_list, additional_kwargs=msg.additional_kwargs) - except (json.JSONDecodeError, ValueError): - pass - if isinstance(msg, HumanMessage) and isinstance(msg.content, dict): - # 尝试从 dict 中提取文本,否则序列化为字符串 - text = msg.content.get("text") or msg.content.get("content") or "" - if text: - msg = HumanMessage(content=str(text), additional_kwargs=msg.additional_kwargs) - else: - msg = HumanMessage(content=json.dumps(msg.content, ensure_ascii=False), additional_kwargs=msg.additional_kwargs) - elif isinstance(msg, HumanMessage) and isinstance(msg.content, list): - new_content: List[Dict[str, Any] | str] = [] - for part in msg.content: - if isinstance(part, dict): - if part.get("type") == "audio": - audio_path = part.get("audio_path") - if audio_path: - try: - text = self._audio_to_text(audio_path) - new_content.append({"type": "text", "text": text}) - except Exception as e: - new_content.append({"type": "text", "text": f"[语音识别失败: {e}]"}) - elif part.get("type") == "image_url": - new_content.append(self._process_image_content(part)) - elif part.get("type") == "text": - text = (part.get("text") or "").strip() - if text: - new_content.append({"type": "text", "text": text}) - elif part.get("path") or (part.get("file") or {}).get("path"): - file_path = part.get("path") or (part.get("file") or {}).get("path") - if isinstance(file_path, str) and os.path.isfile(file_path): - ext = os.path.splitext(file_path)[1].lower() - normalized_path = file_path.replace("\\", "/") - if ext in IMAGE_FILE_EXTENSIONS: - new_content.append(self._process_image_content({"type": "image_url", "image_url": {"url": file_path}})) - else: - try: - file_text, file_images = Documents_process.process(file_path) - new_content.append({"type": "text", "text": f"用户上传了一个名为\"{normalized_path}\"的文件。"}) - if file_text.strip(): - new_content.append({ - "type": "text", - "text": f"以下是文件 {normalized_path} 的内容:\n{file_text.strip()}" - }) - for image_bytes in file_images or []: - image_part = image_bytes_to_openai_image_url_part(image_bytes) - if image_part is not None: - new_content.append(image_part) - if not file_text.strip() and not file_images: - new_content.append({"type": "text", "text": f"[用户上传了附件文件: {normalized_path}]"}) - except Exception as e: - new_content.append({"type": "text", "text": f"[附件处理失败: {normalized_path}: {e}]"}) - else: - text = assistant_text(part) - if text: - new_content.append({"type": "text", "text": text}) - elif isinstance(part, str): - text = part.strip() - if text: - new_content.append(text) - if len(new_content) == 1 and isinstance(new_content[0], str): - msg = HumanMessage(content=new_content[0], additional_kwargs=msg.additional_kwargs) - else: - msg = HumanMessage(content=new_content, additional_kwargs=msg.additional_kwargs) - new_messages.append(msg) - return new_messages - - def _text_to_audio(self, text: str) -> bytes: - chunks = split_text(text, min_effective_chars=20) - if len(chunks) == 1: - resp = requests.get(f"{TTS_BASE_URL}/tts", params={"text": chunks[0]}) - resp.raise_for_status() - return resp.content - - # 多块并行请求 - audio_parts = [None] * len(chunks) - with ThreadPoolExecutor(max_workers=min(len(chunks), 4)) as pool: - future_to_idx = { - pool.submit( - lambda t: requests.get( - f"{TTS_BASE_URL}/tts", params={"text": t} - ).content, - chunk - ): idx for idx, chunk in enumerate(chunks) - } - for future in as_completed(future_to_idx): - idx = future_to_idx[future] - try: - audio_parts[idx] = future.result() - except Exception as e: - raise RuntimeError(f"TTS request failed for chunk {idx}: {e}") - return merge_wavs(audio_parts) - - @staticmethod - def _get_wav_duration_seconds(audio_bytes: bytes) -> float | None: - if not audio_bytes: - return None - try: - with closing(wave.open(io.BytesIO(audio_bytes), "rb")) as wf: - frame_rate = wf.getframerate() - frame_count = wf.getnframes() - if frame_rate <= 0: - return None - return round(frame_count / frame_rate, 2) - except wave.Error: - return None - - def _apply_tts(self, result: ChatResult, output_type: str, audio_output_dir: str | None = None) -> ChatResult: - if output_type not in ("audio", "both") or not audio_output_dir: - return result - os.makedirs(audio_output_dir, exist_ok=True) - for idx, gen in enumerate(result.generations): - if not isinstance(gen, ChatGeneration): - continue - msg = gen.message - if not isinstance(msg, AIMessage): - continue - text = assistant_text(msg.content) - if not text.strip(): - continue - try: - audio_bytes = self._text_to_audio(text) - timestamp = dt.now().strftime("%H%M%S") - audio_path = os.path.join(audio_output_dir, f"assistant_tts_{timestamp}_{idx}.wav") - with open(audio_path, "wb") as f: - f.write(audio_bytes) - duration_seconds = self._get_wav_duration_seconds(audio_bytes) - # 仅保存到实例属性,不再写入 additional_kwargs - if idx == 0: - self.last_audio_kwargs = { - "audio": { - "sample_rate": 24000, - "format": "wav", - "path": audio_path, - "duration_seconds": duration_seconds, - }, - "output_type": output_type, - } - if self.stream_play: - self.play_audio_bytes(audio_bytes) - if output_type == "audio": - msg.content = "" - except Exception as e: - print(f"[TTS ERROR] 合成语音失败: {e}") - # 发生错误时也清空 last_audio_kwargs,避免污染 - self.last_audio_kwargs = None - return result - - def _generate( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[Any] = None, - **kwargs: Any, - ) -> ChatResult: - metadata = {} - if run_manager: - metadata = getattr(run_manager, 'metadata', {}) - output_type = metadata.get("output_type", self.output_type) - audio_output_dir = metadata.get("audio_output_dir") - - # 移除自定义参数,防止它们传入 OpenAI API - kwargs.pop("output_type", None) - kwargs.pop("audio_output_dir", None) - - messages = self._preprocess_messages(messages) - - result = super()._generate(messages, stop=stop, run_manager=run_manager, **kwargs) - - if output_type in ("audio", "both"): - result = self._apply_tts(result, output_type, audio_output_dir=audio_output_dir) - if output_type == "audio": - for gen in result.generations: - if isinstance(gen, ChatGeneration) and isinstance(gen.message, AIMessage): - gen.message.content = "" - return result - - def _create_chat_result(self, response, generation_info=None) -> ChatResult: - # ---------- 打印原始 tool_calls ---------- - try: - raw = response if isinstance(response, dict) else response.model_dump() - print("[RAW_RESPONSE]", json.dumps(raw, indent=2, ensure_ascii=False)) - choices = raw.get("choices", []) - if choices: - msg = choices[0].get("message", {}) - tool_calls = msg.get("tool_calls") - if tool_calls: - print("[RAW_TOOLCALLS]", json.dumps(tool_calls, indent=2, ensure_ascii=False)) - except Exception: - pass - - result = super()._create_chat_result(response, generation_info) - try: - dumped = response if isinstance(response, dict) else response.model_dump(exclude={"choices": {"__all__": {"message": {"parsed"}}}}) - for i, choice in enumerate(dumped.get("choices") or []): - if i >= len(result.generations): - break - lc_msg = result.generations[i].message - msg = choice.get("message") or {} - ak = lc_msg.additional_kwargs - # 优先处理 reasoning 内容 - merged = "" - for key in ("reasoning_content", "reasoning_details", "reasoning"): - merged = assistant_text(msg.get(key)) - if merged: - break - if merged: - ak.setdefault("reasoning_content", merged) - raw_content = assistant_text(msg.get("content")) - if not raw_content and merged: - lc_msg.content = merged - elif raw_content: - lc_msg.content = raw_content - - content_text = lc_msg.content if isinstance(lc_msg.content, str) else "" - - # 匹配 ... 块 - pattern = re.compile(r'\s*(.*?)\s*', re.DOTALL) - matches = pattern.findall(content_text) - if matches: - tool_calls = [] - for idx, match in enumerate(matches): - # 先尝试 JSON 解析(兼容旧格式) - try: - call_data = json.loads(match) - tool_calls.append({ - "id": f"call_{idx}", - "type": "function", - "function": { - "name": call_data.get("name", ""), - "arguments": json.dumps(call_data.get("arguments", {})) - } - }) - continue # 解析成功,跳过 XML 尝试 - except json.JSONDecodeError: - pass - - # 再尝试 XML 解析 - func_match = re.search(r'', match) - if not func_match: - continue - func_name = func_match.group(1) - - params = {} - for param_match in re.finditer(r'\s*(.*?)\s*', match, re.DOTALL): - key = param_match.group(1) - value = param_match.group(2).strip() - # 可选:智能类型转换,先保持简单 - params[key] = value - - tool_calls.append({ - "id": f"call_{idx}", - "type": "function", - "function": { - "name": func_name, - "arguments": json.dumps(params) - } - }) - - if tool_calls: - ak["tool_calls"] = tool_calls - # 构造 LangChain 的 ToolCall 对象列表 - lc_tool_calls = [] - for idx, tc in enumerate(tool_calls): - lc_tool_calls.append( - ToolCall( - id=tc["id"], - name=tc["function"]["name"], - args=json.loads(tc["function"]["arguments"]) - ) - ) - lc_msg.tool_calls = lc_tool_calls - # 清理内容中的 XML/JSON 工具调用痕迹 - cleaned = pattern.sub("", content_text).strip() - lc_msg.content = cleaned if cleaned else "" - except Exception: - pass - return result - - def describe_image(self, image_path_or_url: str, prompt: str = "请详细描述这张图片的内容。") -> str: - messages = [ - SystemMessage(content=[{"type": "text", "text": VISION_SYSTEM_PROMPT}]), - HumanMessage(content=[ - {"type": "image_url", "image_url": {"url": image_path_or_url}}, - {"type": "text", "text": prompt} - ]) - ] - result = self.invoke(messages) - return result.content - - def describe_image_bytes(self, image_bytes: bytes, prompt: str = "请详细描述这张图片的内容。") -> str: - img_part = image_bytes_to_openai_image_url_part(image_bytes) - if img_part is None: - return "图片处理失败" - messages = [ - SystemMessage(content=[{"type": "text", "text": VISION_SYSTEM_PROMPT}]), - HumanMessage(content=[img_part, {"type": "text", "text": prompt}]) - ] - result = self.invoke(messages) - return result.content - -llm = ChatQwen() - -if __name__ == "__main__": - import sys - - # 使用原始字符串避免转义问题 - TEST_IMAGE_PATH = r"C:\Users\86166\Desktop\4.png" - - if not os.path.exists(TEST_IMAGE_PATH): - print(f"❌ 图片不存在: {TEST_IMAGE_PATH}") - sys.exit(1) - - # 测试1: 纯图片识别 - print("=" * 50) - print("测试1: 纯图片识别(无文本)") - print("=" * 50) - result1 = llm.invoke([ - HumanMessage(content=[{"type": "image_url", "image_url": {"url": TEST_IMAGE_PATH}}]) - ]) - print(f"返回类型: {type(result1)}") - print(f"返回内容: {result1.content}") - print(f"所有属性: {[attr for attr in dir(result1) if not attr.startswith('_')]}") - - # 测试2: 图片 + 文本 - print("\n" + "=" * 50) - print("测试2: 图文混合输入") - print("=" * 50) - result2 = llm.invoke([ - HumanMessage(content=[ - {"type": "image_url", "image_url": {"url": TEST_IMAGE_PATH}}, - {"type": "text", "text": "这张图片里有什么?请详细描述"} - ]) - ]) - print(f"回答: {result2.content}") +from agent.core.llm import ChatQwen, llm +__all__ = ["ChatQwen", "llm"] diff --git a/src/agent/memory/__pycache__/__init__.cpython-313.pyc b/src/agent/memory/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2d3427e173c3ebc3a395c09b5281cbc5516b3f2 GIT binary patch literal 175 zcmey&%ge<81Yy&dvOx4>5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iKvsFxJacWU< zjD?w@nOTfWYH@Z+enE_5dTL%tyiaOkQC?2KczG$)vkyYXgtXJVi4maGb1Bo5i^hl0O#;64FCWD literal 0 HcmV?d00001 diff --git a/src/agent/memory/__pycache__/system_prompt.cpython-312.pyc b/src/agent/memory/__pycache__/system_prompt.cpython-312.pyc index 0bfd68a5fd6a3e4d5fe3b9508ba26f803ea9fab8..81b095e8673c70b5545c7dcd7d77cb938f1e5d85 100644 GIT binary patch delta 543 zcmdm~b4tJdG%qg~0}#~97-sp2F)%y^abSQ6%J{ql$e7NM!jQ!XlS0s|q0A`86h^%A zOestl@_|6ja08fAm@yO}%d@1g;FV{sWYuKb=)f*oU%D8mkEPbaK@+O{mx>&3ct&!?<-*0KHBl$}5Z)KZ8oAY=VB*>15GmlS0dO!g8D61*i4;_B!d z@9Xd48Xpkk?;9|AwrEBfH_$VnfGM=VM^yYIJRO|G>|{DEvW4 zT7u=f8BEBWfl=&(4LeW>S(WGqAz@*b?`kkr>I{qmAGDZ7SiZACg+8(~FbWoN0xbdn DQgEfa delta 140 zcmX@5zf;HIG%qg~0}x2mvu0(8GB7*_abSQE%J?h-WK3sBVTfW#VT@u-VTxi(VUA+1 zWYJ{Zc!6D%*-w*Ya;I3(4BO~JnCU!BF3k*UZgrp^awCD#L Tb`UM{K}cAbrIEjg9jFih`5PLh diff --git a/src/agent/memory/__pycache__/system_prompt.cpython-313.pyc b/src/agent/memory/__pycache__/system_prompt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3d7fa1eec5c91acfa0396b7b2810ad8317b42f4 GIT binary patch literal 7491 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 literal 0 HcmV?d00001 diff --git a/src/agent/memory/system_prompt.py b/src/agent/memory/system_prompt.py index b49ef18..1e0040d 100644 --- a/src/agent/memory/system_prompt.py +++ b/src/agent/memory/system_prompt.py @@ -5,7 +5,7 @@ MAIN_SYSTEM_PROMPT = """你是一个多模态助手,能够直接理解用户 - 对于任何图片/音频输入,请始终**直接**利用你的视觉/听觉能力回答,**绝对不要**调用截图、OCR、日期查询等无关工具。 - 如果你不确定是否需要工具,选择**不调用**,直接给出你能给出的最佳答案。 - 违反上述规则是一种严重错误。 -""" +""".strip() SUMMARY_SYSTEM_PROMPT =""" 你是一个名为“Summary Agent”的专业摘要助手,专门负责对用户提供的文本内容进行高度凝练、准确的总结。 @@ -34,7 +34,7 @@ SUMMARY_SYSTEM_PROMPT =""" 你输出:“某公司于2026年5月发布了新一代量子芯片,性能较前代提升10倍,预计2027年量产。此举被业界视为量子计算商业化的关键突破,同时引发了对加密安全的讨论。” 现在,请以 Summary Agent 的身份,完成用户的摘要请求。 -""" +""".strip() VISION_SYSTEM_PROMPT = """ @@ -44,7 +44,7 @@ VISION_SYSTEM_PROMPT = """ 如果用户提供了多张图片,请分别编号描述(如「图1」「图2」等),便于后续问答引用。 看不清的内容请如实说明,不要臆测。 其次请认真回答用户的问题,结合图片内容进行分析推理,给出具体、简短、清晰、实用的回复; -""" +""".strip() DB_SYSTEM_PROMT = """ 你是一位敏捷的数据库分析助手,能够高效利用内置功能帮助用户从PostgreSQL数据库中获取答案。 @@ -65,4 +65,9 @@ DB_SYSTEM_PROMT = """ 5. **非数据库问题快速响应**:遇到问候、常识性问题或超出数据库范围的内容时,可以直接友好回应,不必强制调用功能。 请记住,你是一个协作伙伴,目标是让用户以最小的学习成本从数据库中获得有价值的信息。开始工作吧! -""" +""".strip() + +TEAM_MODE_PROMPT = """ +team 模式目前仅作为多 agent 协同的预留模式,尚未实现。 +请简短说明当前无法启动 team 协同,并建议用户改用 agent 模式。 +""".strip() diff --git a/src/agent/tools/__init__.py b/src/agent/tools/__init__.py index 17d29ff..d8191d6 100644 --- a/src/agent/tools/__init__.py +++ b/src/agent/tools/__init__.py @@ -1,34 +1,38 @@ from .tool import * -# from .tool_rag import RAGTool - -# from .tool_database import * -# from utils.database_utils import PostgreSQL_Manager - -# 创建数据库管理器实例 -# connection = "postgresql+psycopg2://postgres:12345678@localhost:5432/agent_controller" -# connection = "sqlite:///Agent/src/agent/history/data_base/chat_history.db" -# _db_manager = PostgreSQL_Manager(connection) - -# # 创建工具实例 -# _all_database_tools = [ -# Websearchclass(), -# DateQueryClass(), -# ListTableComment(db_manager=_db_manager), -# ListTableSchema(db_manager=_db_manager), -# SQLQuery(db_manager=_db_manager), -# SQLQueryChecker(db_manager=_db_manager) -# ] -# def get_sql_tools(): -# return _all_database_tools +from .tool_software_control import SoftwareControl +from .tool_rag import RAGTool _all_tools = [ WebSearch(), DateQuery(), ScreenCapture(), BrowserControl(), - # RAGTool(), + HumanInteraction(), + SoftwareControl(), + RAGTool(), DirectoryListing(), + TodoList(), ] +# 注册所有可用工具名称(供前端配置面板展示),从 _all_tools 实例推导 +_ALL_AVAILABLE_TOOL_CLASSES = {tool.name: type(tool) for tool in _all_tools} + + def get_tools(): + """返回当前 Agent 注册的全部 LangChain 工具列表。""" return _all_tools + + +def get_all_available_tool_names(): + """返回所有可用工具名称(含数据库工具,不管当前是否实例化)。""" + return list(_ALL_AVAILABLE_TOOL_CLASSES.keys()) + + +def get_all_available_tool_descriptions(): + """返回所有可用工具的名称和描述映射(用于前端展示)。""" + result = {} + for name, cls in _ALL_AVAILABLE_TOOL_CLASSES.items(): + inst = cls() + desc = getattr(inst, "description", "") + result[name] = (cls.__name__, (desc or "").split("\n")[0][:200]) + return result diff --git a/src/agent/tools/__pycache__/__init__.cpython-312.pyc b/src/agent/tools/__pycache__/__init__.cpython-312.pyc index d4f424723b971ebcf35b62b756c86585f01863e0..a75a70b1f2f12883bef7bfb0c44086bec22b2465 100644 GIT binary patch literal 1897 zcmZ`(TWk|Y6rEjmB>2ei$MPR)jMk&xv3aw?w)z< z+_~q@J%7~Ic@VT?t2gw2ZAa)KA5@yP0)wTyU?z})6hT8jivmZYCd9?w61Hi!IPS#) z5)rXamj4FqsKU-F?5e`gcOwrj1(oz+eeefbwZ_R=|t9WI2D%@lA16G zm7|85Ofa8b_WEp-i^?&{0I#e;Q7MTUNpPU4x}hdw%xxru@OqR3+fXQd~_WX=yT|(`ZEIz9T$7Jt6@Wsv$|Q4-7JQj2I;cv>A?H16_B4$)LF! z|1|c~Mi~4jXbclp&^$f}nv-BPL#&{Fc)-hB<+)6j_gfSlfL$euiX)lg#S!!LwfVET zG7sZp^FK~KzLhn{CyVE%iep#Jn`cT(k8h36-58s{dZk2c@xnE8{0H;urMcUu%+d3Y zZ;f#FYz*`Cu~kwq6q14;1b2mk+d|u={rmQXr0!5K9Nr#g_y`%)X#-rt3Y0bhVyihP zoOt<|tZH(fMl6P;gd8V2ZG$cApzEz*I8EL^eXrf~t)KC&&-*s!Yc@^eP5)=j0h=w> zZUdM=DjKSKF8zFES*!5XgAR5h6aj9BgdtHx>1V|`WHX-eoVF=~B2Jdi8Nz9?pL<-^ z()dF`=Taf|yO%A&P_b2VqhKDSVpkk1(IGL7(gM%*=yr_K{AoK=yJ7geyz!T9rOy0( zw0QdTqdQZ@3zyBQ6Oc*B?cB}JpcLjOubID`oB!@+DOapUAyCBXfK{7_37!RTfc7r@ z4mMz?oM~oP4HDOx6Otnv24ybEN{A#eyFQ?)M!?P-l;|nVV3=o$*`k`xu$q83fcg14 zXRDpJQkvI3C(TuN(P@C+0J{^jo)ac6NhI_qsbmCEA# z-j;FW!ol3Oy!VYPF05GfP3;%8myK^f%F%lrT{9hBH=6SuTW=hl=?LCkcX!it$ByaN zomofL__TJ>iCW%VbfMbD%pU9Y>)7~VIF8}qn% z7B}Z{%PelnWB)9!8QxWys2zT1vB^^}W;~0lQQJ#}6)zTA))rb{F0=&-D_$x1S~+NF dd(z}tBTl1LPgjaU%TtdiY+mv>g+`0le*sK~Km7mz delta 294 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 diff --git a/src/agent/tools/__pycache__/tool.cpython-313.pyc b/src/agent/tools/__pycache__/tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..baddf9d2a6d676e9538067f40953db051b7425b2 GIT binary patch literal 23065 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 Ql>epHFsKu>W7!h;KiFqmKL7v# literal 0 HcmV?d00001 diff --git a/src/agent/tools/__pycache__/tool_agent_call.cpython-312.pyc b/src/agent/tools/__pycache__/tool_agent_call.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc2aeb104f1dcb0dc52601b308513d521cf05ffd GIT binary patch literal 2549 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 literal 0 HcmV?d00001 diff --git a/src/agent/tools/__pycache__/tool_rag.cpython-312.pyc b/src/agent/tools/__pycache__/tool_rag.cpython-312.pyc index 848cac032f34936599774f2cf47ee60ecbb141d0..b32880f3827f5f4e7133cb335322b9b0b294a20e 100644 GIT binary patch delta 3621 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 literal 0 HcmV?d00001 diff --git a/src/agent/tools/__pycache__/tool_software_control.cpython-312.pyc b/src/agent/tools/__pycache__/tool_software_control.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91739235a5df93cbc79045fef240d7d6f483fcf4 GIT binary patch literal 26948 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 Yzj}Pxq}{CI{zj$SoTUC6GY|j&0VJ*Ps{jB1 literal 0 HcmV?d00001 diff --git a/src/agent/tools/__pycache__/tool_software_control.cpython-313.pyc b/src/agent/tools/__pycache__/tool_software_control.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2240ded7b07c23f17ff8be7a3653128e9215906a GIT binary patch literal 27608 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 z z34I7|hDl#BRq9JyGiJDG%nH=Eetrv+bVVp2BLGiNfN$@CU9&Fp)- z?QO@a9lnAlUq;io=Ht|gsjv7ecX%szcq(?Hz9zmnP`q+F$y2m?fm5t0{8$=gZ~2~g z%6#Q}e1&^F1$&Y1Hoh!SRyDP5AzqcTdO@kd&2P9Br{Lq%qRB)4;?3UT&7Pts6d+d`vFZQ49_f|H))99;g#yvxxq61JPnR`$GrvBp&$Kr_J>o`;H&2wrE zSPNf_KO670HNQIGvo-sy&7R!dsHBCzof>4`M-9T*>NrdOWEnF{`8Y9cLhnys=}lkh zNt1x#V)Jb{F>AxZDBban##f5#ICgmQ?xy>I?x6<7>ptFh*W!pjUf-br_< z%LPLI(tb#KE0KCHwvq?cGRN8_Sph-k%Zd5)fjrXgxt~JRquhrDl>uC>VoEw`pIo!R zaqA8#u5n!IfC6FDVFk>OkFeRUKPBwVf4ti6EJjVMlCddY8=hp6F z+4fR4<86v-dQG7Q*ShCk=QL^!ED_8Fqua*{e5(8fl_LI-0ti%X8c({;snnX^8+v%P z?hm_hQB~bF+*?&AwV2w8A+4u|v!UBdt)~!T_b!g5@j&dp#j(UWBDRNOwd7F=(2e1$ z9w*)64+#OT@`p~5;y+6!v;2-Q9JH|mOLrl=i*nvZb+ZOg=%(19ivEX0_|zY3?F?m; z+YQAW4&KY>0$Kdxf{*62`lZIP#AVhcjiJ_@q+&_DDzzpw9NQ6rlL!asA6iHs*oa6y zJ1X^z?hT0a`sT7i|J?x827M!&7||Z?^=Fw;jYEnuEAwdh?gA5EO=F^o%44F8LO3@o z2W2v{<#sui*7Z#_Nolvx|2*g&VWtFKqLRA%9r76Ux0OP_ChvLjE|K>sJZOc(|2PB` zk&2>8FeDOE!Rgfg-rj-!17aL|qbrK(jld#k9Kd~h-3MXfQ^EeJff!G*+My#o4xE4M z79n!rM7Hi|8Ak3;t_a)R}XrRz-!Vwv)F?q z_~YFF0w0K<=jXZn-*EbmxOnzw{)oc5kGOnc;DbvwR`qCyeh|si_*qVR{7$KeX8YC zrC!zQnRu^i>)X4%s=XfJF0bmY#c3nu_Ip)p7k|@E=KJD{e5&H946mwcAzq!N7&QVj zsyAQ*wU;$l(<*J#W2{ zoyjNte!Y=jcB4>@bS5K`6yDGCb>j#Bm80jU3t!Q5S~IqOJ}Z!#`L#VX&soxi*zf2y SJ2JSRXJ~g6DSuwb!~dU{2Y9&v literal 0 HcmV?d00001 diff --git a/src/agent/tools/doc_tool.py b/src/agent/tools/doc_tool.py new file mode 100644 index 0000000..e69de29 diff --git a/src/agent/tools/gui_tool.py b/src/agent/tools/gui_tool.py new file mode 100644 index 0000000..318d037 --- /dev/null +++ b/src/agent/tools/gui_tool.py @@ -0,0 +1 @@ +from agent.utils.env_utils import GROUNDING_WIDTH, GROUNDING_HEIGHT \ No newline at end of file diff --git a/src/agent/tools/tool.py b/src/agent/tools/tool.py index 920b23e..49b9a77 100644 --- a/src/agent/tools/tool.py +++ b/src/agent/tools/tool.py @@ -2,6 +2,7 @@ from __future__ import annotations import os import io +import json from typing import Optional, Literal, Tuple import sys import time @@ -13,8 +14,8 @@ from pathlib import Path from langchain.tools import BaseTool from pydantic import BaseModel, Field, create_model -from agent.llm import llm -from utils.env_utils import BROWSER_MAP +from agent.core.llm import llm +from agent.utils.env_utils import BROWSER_MAP class WebSearch(BaseTool): @@ -24,6 +25,7 @@ class WebSearch(BaseTool): description : str = "一个网络搜索工具,可以根据用户的查询返回相关的搜索结果。仅当需要查询近期知识或已有知识无法解答需要联网查询时调用,其余任何情况严禁调用。" def _get_search_wrapper(self): + """懒加载 Google Serper 搜索客户端。""" from langchain_community.utilities import GoogleSerperAPIWrapper SERPER_API_KEY = "d6348dcce0c72bb8102f0eacfd2f536f797da0c5" return GoogleSerperAPIWrapper(serper_api_key=SERPER_API_KEY, content_size=2048) @@ -142,6 +144,7 @@ class BrowserControl(BaseTool): # ---------- 辅助方法(与原版相同,无改动) ---------- def _get_browser_info(self, browser_name: Optional[str]): + """解析浏览器名称,返回 webbrowser 控制器与窗口标题关键字。""" if browser_name is None: return webbrowser.get(), None name_lower = browser_name.lower().strip() @@ -155,6 +158,7 @@ class BrowserControl(BaseTool): return controller, title_keyword def _activate_window(self, title_keyword: str) -> bool: + """在 Windows 上按标题关键字激活浏览器窗口(非 Win32 返回 False)。""" if sys.platform != "win32": return False try: @@ -171,10 +175,12 @@ class BrowserControl(BaseTool): return False def _send_keys(self, *keys): + """通过 pyautogui 发送组合键。""" pyautogui.hotkey(*keys) # ---------- 核心操作(返回操作结果描述) ---------- def _open_action(self, url: Optional[str], browser: Optional[str]) -> str: + """执行打开首页、刷新当前页或打开指定 URL。""" controller, title_keyword = self._get_browser_info(browser) if url is None: controller.open("about:blank") @@ -199,6 +205,7 @@ class BrowserControl(BaseTool): return f"已尝试打开 URL: {url}" def _close_action(self, url: Optional[str], browser: Optional[str]) -> str: + """执行关闭浏览器窗口或当前标签页。""" _, title_keyword = self._get_browser_info(browser) if url is None: if title_keyword: @@ -298,6 +305,109 @@ class DirectoryListing(BaseTool): async def _arun(self, path: Optional[str] = None, show_hidden: bool = False) -> str: return await asyncio.to_thread(self._run, path, show_hidden) -if __name__ == "__main__": - # 这里可以进行一些简单的测试,例如创建工具实例并调用方法 - pass + +class HumanInteractionInput(BaseModel): + interaction_type: Literal["information", "selection", "approval"] = Field( + default="information", + description="交互类型:information=人工补充信息,selection=人工选择,approval=人工审批。", + ) + title: str = Field(default="需要人工处理", description="展示给人类的交互标题。") + prompt: str = Field(default="请处理后继续。", description="需要人类理解并响应的具体问题或说明。") + options: Optional[list[str] | str] = Field( + default=None, + description="selection/approval 可选项。优先传字符串数组;如果传 JSON 字符串,系统会自动解析。", + ) + suggested_response: Optional[str] = Field(default=None, description="可选的建议回复、默认选择或默认审批意见。") + + +class HumanInteraction(BaseTool): + """向人类请求补充信息、选择或审批;实际 UI 确认由图级人工干预层承接。""" + + args_schema: type[BaseModel] = HumanInteractionInput + name: str = "human_interaction" + description: str = ( + "当任务缺少关键信息、需要人类选择路径、或需要人类审批后才能继续时调用。" + "三种类型分别是:information=请求人类补充缺失信息;selection=让人类在多个选项中选择且可编辑;" + "approval=请求人类审批高影响决策或继续执行。" + "工具返回必须被视为用户原始意图的纯字符串反馈。其余情况严禁调用。" + ) + + @staticmethod + def normalize_options(options: Optional[list[str] | str]) -> list[str]: + """将 options 参数规范化为字符串列表(支持 JSON 与逗号分隔)。""" + if options is None: + return [] + if isinstance(options, str): + raw = options.strip() + if not raw: + return [] + try: + parsed = json.loads(raw) + except json.JSONDecodeError: + return [item.strip() for item in raw.replace(",", ",").split(",") if item.strip()] + if isinstance(parsed, list): + return [str(item) for item in parsed] + return [str(parsed)] + return [str(item) for item in options] + + def _run( + self, + interaction_type: str = "information", + title: str = "需要人工处理", + prompt: str = "请处理后继续。", + options: Optional[list[str] | str] = None, + suggested_response: Optional[str] = None, + ) -> str: + normalized_options = self.normalize_options(options) + if interaction_type == "approval": + default = (suggested_response or "").strip() + return default or "用户同意" + if interaction_type == "selection": + default = (suggested_response or "").strip() + selected = default or (normalized_options[0] if normalized_options else "") + return f"用户选择:{selected}" if selected else "用户选择:" + response = (suggested_response or "").strip() + return f"用户补充信息如下:{response}" if response else "用户没有补充信息" + + async def _arun( + self, + interaction_type: str = "information", + title: str = "需要人工处理", + prompt: str = "请处理后继续。", + options: Optional[list[str] | str] = None, + suggested_response: Optional[str] = None, + ) -> str: + return await asyncio.to_thread(self._run, interaction_type, title, prompt, options, suggested_response) + + +class TodoListInput(BaseModel): + steps: list[str] = Field(default_factory=list, description="任务步骤列表,每项为一步要做什么的描述") + step: int = Field(default=-1, description="已完成的步骤索引(从0开始),-1表示无已完成步骤") + + +class TodoList(BaseTool): + """可选的任务列表工具,模型可主动调用以跟踪任务进度。""" + + args_schema: type[BaseModel] = TodoListInput + name: str = "todo_list" + description: str = ( + "可选的任务管理工具。当你需要拆分复杂任务为多个步骤并跟踪进度时调用。" + "输入 steps (list[str]) 为每一步的描述,step (int) 为已完成的步骤索引(从0开始)。" + "任务默认状态为 pending,根据 step 自动标记完成。工具返回当前 todo list 文本。" + "仅在需要跟踪多步骤任务进度时调用,简单任务不需要调用。" + ) + response_format: Literal["content", "content_and_artifact"] = "content" + + def _run(self, steps: list[str] | None = None, step: int = -1) -> str: + steps = steps or [] + step = max(-1, min(step, len(steps) - 1)) + lines = ["## 📋 任务列表"] + for i, s in enumerate(steps): + status = "✅ 已完成" if i <= step else "⏳ 待处理" + lines.append(f"- [{i+1}/{len(steps)}] {status}: {s}") + if not steps: + lines.append("(无任务)") + return "\n".join(lines) + + async def _arun(self, steps: list[str] | None = None, step: int = -1) -> str: + return self._run(steps, step) diff --git a/src/agent/tools/tool_agent_call.py b/src/agent/tools/tool_agent_call.py new file mode 100644 index 0000000..b705d4f --- /dev/null +++ b/src/agent/tools/tool_agent_call.py @@ -0,0 +1,52 @@ +"""Agent 委派工具:主 Agent 通过此工具将任务委派给子 Agent。 + +系统定位: + 作为主 Agent 工具集中的特殊工具,调用此工具将触发 + SupervisorGraph 将执行路由到对应子 Agent。 + +工具签名: + agent_call(agent_name, task_description) +""" +from __future__ import annotations + +from typing import Literal + +from langchain.tools import BaseTool +from pydantic import BaseModel, Field, create_model + + +class AgentCallInput(BaseModel): + """agent_call 工具的输入参数。""" + agent_name: str = Field( + description="要委派的子 Agent 名称,例如 'code_agent'、'research_agent'。" + ) + task_description: str = Field( + description="委派给子 Agent 的详细任务描述,子 Agent 将根据此描述执行任务。" + ) + + +class AgentCallTool(BaseTool): + """主 Agent 委派任务给子 Agent 的工具。 + + 调用后 SupervisorGraph 将暂停主 Agent、激活对应子 Agent, + 子 Agent 完成后结果回传给主 Agent。 + """ + + args_schema: type[BaseModel] = AgentCallInput + name: str = "agent_call" + description: str = ( + "将子任务委派给指定的子 Agent 执行。" + "仅在需要专业子 Agent 协助完成特定类型任务时调用。" + "可用的子 Agent 名称由系统配置决定。" + ) + + def _run(self, agent_name: str, task_description: str) -> str: + """占位实现:实际路由由 SupervisorGraph 处理。""" + return ( + f"已委派任务给子 Agent [{agent_name}]。\n" + f"任务描述: {task_description}\n" + f"等待子 Agent 返回结果..." + ) + + async def _arun(self, agent_name: str, task_description: str) -> str: + return self._run(agent_name, task_description) diff --git a/src/agent/tools/tool_rag.py b/src/agent/tools/tool_rag.py index a395a31..993d8e8 100644 --- a/src/agent/tools/tool_rag.py +++ b/src/agent/tools/tool_rag.py @@ -14,15 +14,16 @@ from langchain.tools import BaseTool from pydantic import BaseModel, Field, create_model from langchain_text_splitters import RecursiveCharacterTextSplitter -from utils.env_utils import RAG_BASE_URL, RAG_CHUNK_SIZE, RAG_CHUNK_OVERLAP -from utils.agent_utils import Documents_process -from ..llm import llm +from agent.utils.env_utils import RAG_BASE_URL, RAG_CHUNK_SIZE, RAG_CHUNK_OVERLAP +from agent.utils.agent_utils import Documents_process +from agent.core.llm import llm # ---------- 前置函数---------- ENDING_PUNCTUATION = set(".!?。!?…\"'”’);:;:…") def has_ending_punctuation(text: str) -> bool: + """判断文本是否以句末标点结尾(空文本视为已结束)。""" stripped = text.rstrip() if not stripped: return True @@ -30,6 +31,7 @@ def has_ending_punctuation(text: str) -> bool: def pre_process(text_content: str, list_image_byte: List[bytes]) -> Tuple[str, List[bytes]]: + """合并图文行并为 占位符生成 Vision 描述,供 RAG 入库前预处理。""" lines = text_content.split('\n') new_lines = [] i = 0 @@ -115,6 +117,7 @@ GLOBAL_COLLECTION_NAME = "rag_global" def get_chroma_client(): + """获取或懒初始化 Chroma 持久化客户端单例。""" global _chroma_client if _chroma_client is None: _chroma_client = chromadb.PersistentClient(path=CHROMA_PERSIST_DIR) @@ -122,6 +125,7 @@ def get_chroma_client(): def get_global_collection(): + """获取或创建全局 RAG 向量集合(cosine 空间)。""" global _global_collection if _global_collection is None: client = get_chroma_client() @@ -138,6 +142,7 @@ def get_global_collection(): def embed_texts(queries: List[str], documents: List[str] = None) -> tuple: + """调用 RAG 嵌入服务,返回 (query_embeddings, doc_embeddings)。""" payload = { "task_description": "Given a web search query, retrieve relevant passages that answer the query", "queries": queries, @@ -153,6 +158,7 @@ def embed_texts(queries: List[str], documents: List[str] = None) -> tuple: def rerank_documents(query: str, documents: List[str]) -> List[float]: + """调用 RAG 重排序服务,返回与 documents 对齐的相关度分数列表。""" payload = { "task_description": "Given a web search query, retrieve relevant passages that answer the query", "query": query, @@ -167,6 +173,7 @@ def rerank_documents(query: str, documents: List[str]) -> List[float]: def split_into_chunks(input_data) -> List[dict]: + """将文本/图文元组切分为 chunk 列表,图片与表格块标记为 protected。""" if isinstance(input_data, str): text = input_data image_bytes_list = [] @@ -256,6 +263,7 @@ class RAGTool(BaseTool): return await asyncio.to_thread(self._run, **kwargs) def _get_batch_chunks(self, coll, batch_id: str) -> dict: + """按 batch_id 拉取同批次全部 chunk,以 chunk_index 为键索引。""" res = coll.get(where={"batch_id": batch_id}) batch_chunks = {} if res and res["metadatas"] and res["documents"]: @@ -273,6 +281,7 @@ class RAGTool(BaseTool): # ---------- 查询(返回 artifact)---------- def _handle_query(self, query: str, filter: Optional[Union[str, List[str]]]) -> Tuple[str, list]: + """向量检索 + rerank,返回拼接文本与图片 artifact 列表。""" if not query: return "查询内容不能为空。", [] @@ -376,6 +385,7 @@ class RAGTool(BaseTool): # ---------- 添加文档 ---------- def _handle_add(self, content: Optional[str], source: Optional[Union[str, List[str]]]) -> Tuple[str, dict]: + """解析文本或文件、分块嵌入后写入 Chroma 全局集合。""" if not content and not source: return "必须提供 content 或 source。", {} if content and source: @@ -508,6 +518,7 @@ class RAGTool(BaseTool): # ---------- 清除 ---------- def _handle_clear(self, filter: Optional[Union[str, List[str]]]) -> Tuple[str, dict]: + """按 source 过滤条件删除知识库记录及关联图片文件。""" if not filter: return "清除操作必须提供 filter 条件。", {} diff --git a/src/agent/tools/tool_software_control.py b/src/agent/tools/tool_software_control.py new file mode 100644 index 0000000..8f33d63 --- /dev/null +++ b/src/agent/tools/tool_software_control.py @@ -0,0 +1,519 @@ +# pyright: reportMissingImports=false, reportAttributeAccessIssue=false +from __future__ import annotations + +import json +import os +import re +import subprocess +import time +import csv +import ctypes +from dataclasses import asdict, dataclass, field +from difflib import SequenceMatcher +from pathlib import Path + +from langchain.tools import BaseTool +from pydantic import BaseModel, Field, create_model + +try: + import win32com.client as win32com_client # type: ignore +except Exception: + win32com_client = None + +_windll = getattr(ctypes, "windll", None) +user32: object | None = _windll.user32 if os.name == "nt" and _windll is not None else None +WM_CLOSE = 0x0010 + + +_INDEX_PATH = Path(__file__).resolve().parents[1] / "history" / "data_base" / "software_index.json" +_SHORTCUT_ROOTS = [ + Path(os.environ.get("ProgramData", "")) / "Microsoft" / "Windows" / "Start Menu" / "Programs", + Path(os.environ.get("APPDATA", "")) / "Microsoft" / "Windows" / "Start Menu" / "Programs", + Path(os.environ.get("USERPROFILE", "")) / "Desktop", + Path(os.environ.get("PUBLIC", "")) / "Desktop", +] +_APP_PATH_KEYS = [ + r"Software\Microsoft\Windows\CurrentVersion\App Paths", +] +_SKIP_KEYWORDS = ( + "卸载", + "uninstall", + "help", + "manual", + "documentation", + "release notes", + "readme", + "website", + "faq", + "license", + "update", +) + + +@dataclass +class SoftwareRecord: + display_name: str + aliases: list[str] = field(default_factory=list) + launch_type: str = "" + launch_target: str = "" + shortcut_path: str = "" + working_dir: str = "" + process_names: list[str] = field(default_factory=list) + source: str = "" + + +def _normalize(text: str) -> str: + value = text.casefold().strip() + value = re.sub(r"\.lnk$", "", value) + value = re.sub(r"\.exe$", "", value) + value = re.sub(r"[\s\-_()()\[\]【】]+", "", value) + return value + + +def _should_skip(name: str) -> bool: + lowered = name.casefold() + return any(keyword in lowered for keyword in _SKIP_KEYWORDS) + + +def _add_aliases(*values: str) -> list[str]: + aliases: list[str] = [] + seen: set[str] = set() + for value in values: + if not value: + continue + cleaned = value.strip() + if not cleaned: + continue + for item in (cleaned, Path(cleaned).stem): + key = _normalize(item) + if not key or key in seen: + continue + seen.add(key) + aliases.append(item) + return aliases + + +def _resolve_shortcut(path: Path) -> tuple[str, str]: + if win32com_client is None: + return "", "" + try: + shell = win32com_client.Dispatch("WScript.Shell") + shortcut = shell.CreateShortCut(str(path)) + target = (shortcut.Targetpath or "").strip() + working_dir = (shortcut.WorkingDirectory or "").strip() + return target, working_dir + except Exception: + return "", "" + + +def _scan_shortcuts() -> list[SoftwareRecord]: + records: list[SoftwareRecord] = [] + for root in _SHORTCUT_ROOTS: + if not root.exists(): + continue + for shortcut in root.rglob("*.lnk"): + display_name = shortcut.stem.strip() + if not display_name or _should_skip(display_name): + continue + target, working_dir = _resolve_shortcut(shortcut) + process_names: list[str] = [] + if target: + process_names.append(Path(target).stem) + records.append( + SoftwareRecord( + display_name=display_name, + aliases=_add_aliases(display_name, Path(target).stem if target else ""), + launch_type="shortcut", + launch_target=str(shortcut), + shortcut_path=str(shortcut), + working_dir=working_dir, + process_names=process_names, + source="shortcut", + ) + ) + return records + + +def _scan_app_paths() -> list[SoftwareRecord]: + try: + import winreg as winreg_module # type: ignore + except Exception: + return [] + records: list[SoftwareRecord] = [] + for hive in (winreg_module.HKEY_LOCAL_MACHINE, winreg_module.HKEY_CURRENT_USER): + for subkey in _APP_PATH_KEYS: + try: + root = winreg_module.OpenKey(hive, subkey) + except OSError: + continue + try: + count = winreg_module.QueryInfoKey(root)[0] + for index in range(count): + name = winreg_module.EnumKey(root, index) + try: + child = winreg_module.OpenKey(root, name) + exe_path, _ = winreg_module.QueryValueEx(child, "") + except OSError: + continue + exe_path = str(exe_path).strip() + if not exe_path: + continue + display_name = Path(name).stem.strip() or Path(exe_path).stem.strip() + if not display_name or _should_skip(display_name): + continue + records.append( + SoftwareRecord( + display_name=display_name, + aliases=_add_aliases(display_name, Path(exe_path).stem), + launch_type="app_path", + launch_target=exe_path, + process_names=[Path(exe_path).stem], + source="app_path", + ) + ) + finally: + winreg_module.CloseKey(root) + return records + + +def _deduplicate(records: list[SoftwareRecord]) -> list[SoftwareRecord]: + merged: dict[tuple[str, str], SoftwareRecord] = {} + for record in records: + key = (record.launch_type, record.launch_target.casefold()) + existing = merged.get(key) + if existing is None: + merged[key] = record + continue + existing.aliases = sorted(set(existing.aliases + record.aliases), key=str.casefold) + existing.process_names = sorted(set(existing.process_names + record.process_names), key=str.casefold) + if not existing.display_name and record.display_name: + existing.display_name = record.display_name + return sorted(merged.values(), key=lambda item: item.display_name.casefold()) + + +def _write_index(records: list[SoftwareRecord]) -> None: + _INDEX_PATH.parent.mkdir(parents=True, exist_ok=True) + payload = { + "generated_at": int(time.time()), + "count": len(records), + "items": [asdict(record) for record in records], + } + _INDEX_PATH.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") + + +def _load_index() -> list[SoftwareRecord]: + if not _INDEX_PATH.exists(): + return [] + try: + payload = json.loads(_INDEX_PATH.read_text(encoding="utf-8")) + except Exception: + return [] + items = payload.get("items") or [] + records: list[SoftwareRecord] = [] + for item in items: + if not isinstance(item, dict): + continue + records.append( + SoftwareRecord( + display_name=str(item.get("display_name") or ""), + aliases=list(item.get("aliases") or []), + launch_type=str(item.get("launch_type") or ""), + launch_target=str(item.get("launch_target") or ""), + shortcut_path=str(item.get("shortcut_path") or ""), + working_dir=str(item.get("working_dir") or ""), + process_names=list(item.get("process_names") or []), + source=str(item.get("source") or ""), + ) + ) + return records + + +def _build_index() -> list[SoftwareRecord]: + records = _deduplicate(_scan_shortcuts() + _scan_app_paths()) + _write_index(records) + return records + + +def _match_score(query: str, candidate: str) -> float: + q = _normalize(query) + c = _normalize(candidate) + if not q or not c: + return 0.0 + if q == c: + return 1.0 + if q in c: + return 0.95 + if c in q and len(c) >= 4 and len(c) / max(len(q), 1) >= 0.6: + return 0.9 + return SequenceMatcher(None, q, c).ratio() + + +def _rank_matches(query: str, records: list[SoftwareRecord]) -> list[tuple[float, SoftwareRecord]]: + ranked: list[tuple[float, SoftwareRecord]] = [] + for record in records: + candidates = [record.display_name, *record.aliases] + score = max((_match_score(query, candidate) for candidate in candidates), default=0.0) + if score >= 0.55: + ranked.append((score, record)) + ranked.sort(key=lambda item: (-item[0], item[1].display_name.casefold())) + return ranked + + +def _open_record(record: SoftwareRecord) -> str: + if not hasattr(os, "startfile"): + raise RuntimeError("software_control 目前仅支持 Windows 本机软件启动") + target = record.shortcut_path or record.launch_target + if not target: + raise RuntimeError(f"未找到 {record.display_name} 的启动入口") + if not Path(target).exists(): + raise RuntimeError(f"启动入口不存在:{target}") + os.startfile(target) # type: ignore[attr-defined] + return target + + +def _coerce_bool(value: object) -> bool: + if isinstance(value, bool): + return value + if isinstance(value, str): + lowered = value.strip().casefold() + if lowered in {"true", "1", "yes", "y", "on"}: + return True + if lowered in {"false", "0", "no", "n", "off", ""}: + return False + return bool(value) + + +def _list_running_processes() -> list[dict[str, str]]: + try: + output = subprocess.check_output( + ["tasklist", "/FO", "CSV", "/NH"], + text=True, + encoding="gbk", + errors="ignore", + ) + except Exception: + return [] + processes: list[dict[str, str]] = [] + for row in csv.reader(output.splitlines()): + if len(row) < 2: + continue + image_name = row[0].strip() + pid = row[1].strip() + if not image_name or not pid: + continue + processes.append( + { + "image_name": image_name, + "pid": pid, + "stem": Path(image_name).stem, + } + ) + return processes + + +def _find_running_pids(record: SoftwareRecord) -> list[int]: + process_keys = {_normalize(name) for name in record.process_names if name} + process_keys.add(_normalize(record.display_name)) + process_keys.update(_normalize(alias) for alias in record.aliases if alias) + matched: list[int] = [] + for proc in _list_running_processes(): + if _normalize(proc["stem"]) in process_keys: + try: + matched.append(int(proc["pid"])) + except ValueError: + continue + return sorted(set(matched)) + + +def _find_visible_windows_for_pids(pids: list[int]) -> list[tuple[int, int, str]]: + u32 = user32 + if u32 is None: + return [] + pid_set = set(pids) + results: list[tuple[int, int, str]] = [] + + @ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_void_p) + def enum_proc(hwnd, lparam): + if not u32.IsWindowVisible(hwnd): + return True + length = u32.GetWindowTextLengthW(hwnd) + if length <= 0: + return True + buffer = ctypes.create_unicode_buffer(length + 1) + u32.GetWindowTextW(hwnd, buffer, length + 1) + title = buffer.value.strip() + if not title: + return True + pid = ctypes.c_ulong() + u32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid)) + if pid.value in pid_set: + results.append((pid.value, int(hwnd), title)) + return True + + u32.EnumWindows(enum_proc, 0) + return results + + +def _wait_for_pids_exit(pids: list[int], timeout_seconds: float = 5.0) -> list[int]: + deadline = time.time() + timeout_seconds + remaining = set(pids) + while time.time() < deadline and remaining: + current = set() + for proc in _list_running_processes(): + try: + pid = int(proc["pid"]) + except ValueError: + continue + if pid in remaining: + current.add(pid) + remaining = current + if remaining: + time.sleep(0.4) + return sorted(remaining) + + +def _close_windows(windows: list[tuple[int, int, str]]) -> None: + u32 = user32 + if u32 is None: + return + for _, hwnd, _ in windows: + u32.PostMessageW(hwnd, WM_CLOSE, 0, 0) + + +def _force_kill_pids(pids: list[int]) -> list[int]: + killed: list[int] = [] + for pid in pids: + try: + subprocess.run( + ["taskkill", "/PID", str(pid), "/F"], + check=False, + capture_output=True, + text=True, + encoding="gbk", + errors="ignore", + ) + killed.append(pid) + except Exception: + continue + return killed + + +class SoftwareControl(BaseTool): + args_schema: type[BaseModel] = create_model( + "SoftwareControlInput", + action=(str, Field(default="open", description="操作类型:open 表示打开软件,close 表示正常关闭软件,force_close 表示强制关闭软件。")), + software_name=(str, Field(description="要操作的软件名称,例如微信、Visual Studio Code、MATLAB。")), + rescan=(bool, Field(default=False, description="是否强制重新扫描本机软件并刷新索引。")), + force=(bool, Field(default=False, description="兼容旧参数。当前仅当 action=force_close 时才会真正触发强制关闭。")), + ) + name: str = "software_control" + description: str = ( + "操作本机已安装的软件。工具会自动扫描 Windows 开始菜单快捷方式、桌面快捷方式和 App Paths," + "建立软件索引后根据用户输入做模糊匹配。action=open 时打开软件;action=close 时仅做正常关闭;" + "action=force_close 时才允许在正常关闭失败后强制结束进程。仅当用户明确要求打开、关闭或强制关闭某个本机软件时调用。" + ) + + def _run(self, action: str = "open", software_name: str = "", rescan: bool = False, force: bool = False) -> str: + if os.name != "nt": + return "software_control 目前仅支持 Windows 本机软件操作。" + query = (software_name or "").strip() + if not query: + return "软件名称不能为空。" + action = (action or "open").strip().lower() + rescan = _coerce_bool(rescan) + force = _coerce_bool(force) + if action not in {"open", "close", "force_close"}: + return "action 仅支持 open、close 或 force_close。" + if force and action == "close": + action = "force_close" + force_requested = action == "force_close" + + records = _build_index() if rescan else _load_index() + if not records: + records = _build_index() + + ranked = _rank_matches(query, records) + if not ranked: + records = _build_index() + ranked = _rank_matches(query, records) + if not ranked: + return f"未找到与“{query}”匹配的软件,请换一个更具体的软件名。" + + best_score, best_record = ranked[0] + if best_score < 0.72: + return f"未找到与“{query}”足够接近的软件,请换一个更具体的软件名。" + if len(ranked) > 1 and best_score < 0.88 and (best_score - ranked[1][0]) < 0.08: + candidates = [ + f"{item.display_name} (score={score:.2f}, source={item.source})" + for score, item in ranked[:5] + ] + return ( + f"找到多个相近软件,暂不自动打开。你可以说得更具体一些。\n" + f"候选项:\n" + "\n".join(candidates) + ) + + if action == "open": + try: + opened_target = _open_record(best_record) + except Exception as exc: + return f"已匹配到软件“{best_record.display_name}”,但启动失败:{exc}" + + process_hint = ", ".join(best_record.process_names) if best_record.process_names else "unknown" + return ( + f"已打开软件:{best_record.display_name}\n" + f"匹配得分:{best_score:.2f}\n" + f"启动方式:{best_record.launch_type}\n" + f"启动入口:{opened_target}\n" + f"进程提示:{process_hint}\n" + f"索引文件:{_INDEX_PATH}" + ) + + running_pids = _find_running_pids(best_record) + if not running_pids: + process_hint = ", ".join(best_record.process_names) if best_record.process_names else "unknown" + return ( + f"已匹配到软件:{best_record.display_name}\n" + f"匹配得分:{best_score:.2f}\n" + f"但当前没有发现正在运行的对应进程。\n" + f"进程提示:{process_hint}" + ) + + windows = _find_visible_windows_for_pids(running_pids) + if windows: + _close_windows(windows) + remaining = _wait_for_pids_exit(running_pids, timeout_seconds=5.0) + if not remaining: + titles = ";".join(title for _, _, title in windows[:3]) + return ( + f"已正常关闭软件:{best_record.display_name}\n" + f"匹配得分:{best_score:.2f}\n" + f"关闭方式:窗口关闭请求\n" + f"涉及 PID:{', '.join(str(pid) for pid in running_pids)}\n" + f"窗口标题示例:{titles}" + ) + if not force_requested: + return ( + f"已向软件“{best_record.display_name}”发送正常关闭请求,但仍有进程未退出。\n" + f"剩余 PID:{', '.join(str(pid) for pid in remaining)}\n" + f"如需强制关闭,请明确使用 force_close 动作后重试。" + ) + killed = _force_kill_pids(remaining) + return ( + f"软件“{best_record.display_name}”正常关闭未完全成功,已执行强制关闭。\n" + f"强制结束 PID:{', '.join(str(pid) for pid in killed) if killed else 'none'}" + ) + + if not force_requested: + return ( + f"已找到软件“{best_record.display_name}”的运行进程,但未找到可见主窗口,暂未自动强制关闭。\n" + f"运行 PID:{', '.join(str(pid) for pid in running_pids)}\n" + f"如需强制关闭,请明确使用 force_close 动作后重试。" + ) + killed = _force_kill_pids(running_pids) + return ( + f"软件“{best_record.display_name}”未找到可见主窗口,已按进程强制关闭。\n" + f"强制结束 PID:{', '.join(str(pid) for pid in killed) if killed else 'none'}" + ) + + async def _arun(self, action: str = "open", software_name: str = "", rescan: bool = False, force: bool = False) -> str: + return self._run(action, software_name, rescan, force) diff --git a/src/agent/utils/__pycache__/__init__.cpython-313.pyc b/src/agent/utils/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99c40564027ecc73d4e9781c85db1bec58a17fe0 GIT binary patch literal 174 zcmey&%ge<81aI7~XM*U*AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl<-XRDad;?$zz z7z;B)GqV_%)Z*-t{DK(A^whkPc%Rh7qP)z!^!VJ&y!;}FU`%mQa!evfI;ONFGp9Hv rK0Y%qvm`!Vub}c4hfQvNN@-52T@fqLbdc@EAjU^#Mn=XWW*`dyXp1er literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/agent_utils.cpython-312.pyc b/src/agent/utils/__pycache__/agent_utils.cpython-312.pyc index 4e10deb55b8c083fae3e246368614a5e82701b86..1742c04a12ccaf56e89cc346adc0368fdfda8bd8 100644 GIT binary patch delta 10661 zcmb7K3v5)^nVvg-00x7>Jc2`#6G8xohZ7(PaUc;fCZPdCY(tu9(qZsi2e%&EodcZF%I7;4SQhZ>>E#O@i!6&A zk60dYEVe9mWLdI!|BUiS9ZM`raGf^gZ>D{gJsp3uyJuM*lg&TcuUz3!WDeloW9J;yz5pKFX; z9vi3JYlUU@dB&3~>>KR!k0o1HnoaV`&vbEs#{xOqo++o7CfgU{*Jt?sBHp*EG}ZnH z`c@l#i{&-;Ed2H;+DyB-be4SyeqU>!Bd05N`Y}^ZmFFzR?<)~ z1AfddHQTds{k+-qy2<{`F_V3jfx-*e{mdU#_-;Ls>FZCLnL2r^tclTQdC9((OF{DH zV4__6^StL-zQdR0Y<-8%9ZRunvage0mN!!mmfnmJ>oMY$!{+Q4JfDEwE$_<}ZZ=VU zCVbMbUcac;UcWtdCNy>?GI&;PsZ(1&|3h_6Xy|xoNNLnM>eZIU@YpBl!nM|WA#&-0 zcCIZn)S~vC(fqwybxq_(t$G{}97#(H2S>xBAE^UpLgUT3X$+q=Y~Q|;N~fIT^i;UZ z9P++>m`U#{FL&(Q$3i#GM!x7^E|0@ubC!K5JMnXr|=(X+L(#N&Y2 zE^C#uvP5>d^w}YTdbU$N-V+%g*E+AM-m~hZMzyJf6}`2i%q=sqA$7zDiJ1%I$|@_Y z4%uyEkzRjVT4-oA@^OvY-zFp%GC#LgAEwYYYmaK}0j+r;m%T5$tp^}irSmY$;dcoW z<}go{-R739@x5Hkb!!>Na}v(nxc#zKEHXYiQQw-Q*80&1z-geu)~{s(cD3(VxUDXi z-Gu`q9(u;qu~xwhUvPsM)_pl=;-lci`4d>Ks=Ukq(;++Gm#rVz4!S0>)}c;IQ(FhM zhELQh?OL^RDMq4@Dcs)?ZofEj%Bxn7MK0Aax#FPJ<1QXuKOH0^?A<3a!d?_H#h9&AQ*_lCib}N@W9_ zq!)A_&ps*H?Xd=9g#`OxLU&o2qpH&Bh9uV_^`EN8{aVANNN-bUs9L++dwZ-Ybn|Rv zutp(;&O2ap?-a|uq3a>=gNViglWEOK7=o;pk1&?&2hEw6WuZgha*>d z4f%CixELf#49qfnF4H~K$w7jqr#>=RmoQ8Lb;h{+fHiO~bfXcNsw3^{l_Q~{5tjGH zmSXE0+lz{K6~1HL`kHlHkuhl^Sqc8hWjiRti*VvyR(B=21%>;CaDPK&a8#{3tzu(? zXUPmmL*W}~J9p8eog9V~T2ljq3c&;`Y!3J=Z(ZERnR;SeZEFsHGBVM5m1d%eK1_4{ z1O_eV=H%pjExr4-l#}iI!K^nkN{E?f1T$HtSx*M9&dLHa|3S<^tkw=4#BRT!h>2Jb z8fpYh!pIGWXGr~UQrPybG08}}$-U�?#Ab&<-vI$gDm-!W__qc(b4d;@Pv8mAG?# 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 delta 3442 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 XXen+)nM8Bx7u3wjS%@l;XX^g|dD$g~ literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/database_utils.cpython-313.pyc b/src/agent/utils/__pycache__/database_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8303beadebdc900e39fc3585c50ee4a9558eb897 GIT binary patch literal 10881 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 E0fmOFKL7v# literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/env_utils.cpython-312.pyc b/src/agent/utils/__pycache__/env_utils.cpython-312.pyc index 7c785846df7e0538bd84ff5833d7fa0366f4e95f..dcd0fef0bc91dc0fe1eedbe4448f53a4b36a4f1c 100644 GIT binary patch literal 8166 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 delta 1319 zcmaJ>O>7%Q6n?Wmj@Liy#Eu=uaoo^`w40P<=EAD z(_k<-LLABoM~f6_K?*leDzy?9xN+jrOC+}kZ!2*s61P;na$sijBalm0ns49tz4y($ z-I?7#U;8?y-VcWZz^<_efonhaZK%l>6#zD20T7T>K<9U%LRJJ_SP^xRa4uG)6}WQn z+wi?~uxU;Ca{=K~`7?Qqtiea(yRb$y$@$&i>}Q7)sGozvA>fph)#xA#xmIs7Sck_U z)p?|BMBUKr?3{5fUoIF6rCE)z>U-g7MHBWXaGr$oDGS*5;6~?WiZjOfOLLb?g>u;_ z<>w2_&g*KLEIaS36A260iFMUd{w?GDg@xipqdcP*EazJ_?R=2F)#8Vdp5$C%;Qp=8YEFKc44_@*{WLpbi^v-7tQFXG{hf<4OP7l^U*A%vWiSQT~spuQA*e2_cU``2#XfAk&3ld;TG$p86DW zfwZ%`!0%hJE&GSmUi{1WZ-c*1?e=?#op;`Q<^@laPBW7wY$q}qu^Uf1os98zH>loh z+@xPMzHEGx+A4PQU?(asT?Ogli&D=(AXY}-s^-e6?iM+C|cBApO)xNrEZCE>F Z*?Xl<;!G!++kt^ypL6$ERyxC4{s&Jg2L}KE diff --git a/src/agent/utils/__pycache__/env_utils.cpython-313.pyc b/src/agent/utils/__pycache__/env_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcadbaa9bd067fa46faac1eade2febb1ed42ac5b GIT binary patch literal 2430 zcmaJ?O-vg{6rSDnFSggj*aS=h8$&{!l&Aqp8zo} zZB*wLrH43?qM)d1(i^=Y^+IpSEl1PM0Y*rzR8?wjL9|lUUOKbJP?d_2_M7*;neXkJ z=h^2SkEa2UA-rn1^}QW{Unr>$=dQ6c$pY{QoCFX+W&|KQvm?w67O^)t#0ivhBbFOh zWEFsD4FPC@)(AuVXP`~AK|6Vv9ugfoMEkDg6dnBz*boG_EVmfs6rDunB06`3MGYqN z>@t_h_+92UnHM&S9(W|eA>QP^cbQtQ$ZPVZJ>FqBsg$4k!YLDq3OifTH=(tlHqQ1B#=EDvrbU1B#YI6&+AGplCf*aRP=8 zD1xvPcEOYI6zqm?!SGodY7<$}CUWGl^|Mw|*BXk);OTC{W|{EYuW7lHVHR74)R`i- zrWfY(N|?c}ye4O*tX5PCi)q3g+Mg-4LcWMCGqPU94y1gtpduwZ zW1#_e2f1~CBhSt&0*}Ddt{Z^V?(e+kAVvsGGW9bREE&s80Duhl*6tRW2VjaG!5omu z03P(Q^?RZ)8FreyiKs~AjHL^-gA5xYR((eoI02??b^Vm1ZuPTvARh&wV4b4bngg;l zI^;4I@{YX-8LPrVvM*}QF*$IN>i{_>Y)O{76RNJOg;}9q0)doFMOtA_DHMf88L9Gg zUb!HYeDT7foL95NmtRnXoQ8xFdqD_WF{|m=HmlHDEii;a{~k7kfMKF)aJ$LEJ;!t zRb*5kN08>!f`)2NQAabPOqa!lqMFx5lAL5(XXlr&Ln7%)#D)CiB{T=UM4$||-2DCc zy?7;SxVy@+4ZHi!?QdGP`1b0F;g|lGwratM4zNOmMvyxr8x!Sv`tWH#0FB<%%=Yv(p^}pY6 zbjM#h`3=FEJqo}J&=~FJUNrimA?`)UN^I;N9Zg7AqcA3AQlnv}Rw);CM#tmQNbCdb zq$Dvm6dT2+KOIlR#xiNl4!jd)_iK#Z=``Gngxyh?`WH(@ukSM-jW{@#OvfhE(nLHt zG&Z3Ezyj<`#FKHdk{D0LAe2(kbZnO7PR7rdv2Qq<8v5VG-!5a%U?!D{CDT$mlS;zZ z^h?8zc;dPQ;~&OU`lVr><}f&%NsiQp#H{G2+2G+oOT zdz9>~QsNVJF2o2YZNWD45z^+AlIOan&E^$h(4=I+o+F=oPW!CnO=u-GpO=MeH3t>x za*o`4Y=h=ZJXUR9K{B?E(TVbDBrlSv<|Irb?Nf3&!!~T8X(0OlfVwHr&mWzp@HPSL zx|$lBfU%U6h>nN3Wb#`0IC9ekTXAWgTwp}I4KfR3tMx8Kdj{t8BElB3hqSM0P0O3N z7Lf)6P%{O3d9XD{dR`H=P)CqL8--(ZiBk(j8EaRbyu7ai}tnpFsPspnVg#D(N3V zaNFi&hM7v=8$jo0P0xzo25a_O*K_|5XKHqJQvAc*u-yV0TbB7%|GIae?B3*?E4NpQ z>wJINwdwX(rdImb-S3p0HGg(3u+CpDyS5uljp6Mk3h&Wn{=ZhBW!d+%^+{{B^S!lZ zqxFgrh*p7bGZ0+veHwWZsh*6i^%-rKjlh-vy8Df``tHW2%GJm5hw)`?l{ zdp2GCU37ox-qLdL88p0M!*#ms*!21;y^kXgBh}+QE4tCtXL$R|{H8BZnSQK3RI5VY zYRYInZ}=iraO8~*c+PF`;SF!k1|Qn+bZmIKw>|bVY}s`;UFobWEW>A=%aaejtolx^ Pd%AxIc7{dgNy+~KhJHV< literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/image_utils.cpython-312.pyc b/src/agent/utils/__pycache__/image_utils.cpython-312.pyc index 00adb6489f52051e2a82fa292bd1d3787a207413..1abe55573fd00ccd2b5794353a07f004b3687368 100644 GIT binary patch delta 4403 zcmb7HYfu!~74BvL1>+lhWTW0iv#UlSSywk};zpLZ221gk-9&d&GXvA=xWLTzG(IA3 z4+sJZ4B9+J6crR7FvH?1j{$0{QmL&E?|!JLddu_UvR1jupeN9?e|dJLgS4?sHzr=6@!uSIX$n;A3xT za=mQW-|@zfiqft7nqT0;H-N29=#mUANuJ? znOfOM&z95fX4>7PpDWde?}hpY$&^ewc7pfh7eelyjLi44-yvGCQEM*&msPpC3U)}ZoF|g&L!pou z*UPk?CVH+5Hyt>mHr$6@7T8Z%J}Pz z*$A!i={-F$Utj|OYpCBFLsJ{RK+F47@XD8P15GLc{=EDLhz~7)NINP>9+S+onD(CpE9uQrf=}OjTqt}JtTG|MuuTah(Rznqf2yxe>lkF9hek$2gSAFm z3v5!o;Z?78hWcywheq1;PaD;~F2E{gDz*zm6tUE=0hZ;&BZ-2&01y-kas>y`x~n2S zc8}1ynkY<4W8B~P?Y)AKpAVvuIydgBD;zC$B4;djawH^NJ60p;$S`Ql#aNHB&)+E zZ6-03Ksne1?Y{{`3-#B)g(jB{M6h%h9)=yIA9Wv|j3z#!VsPRSrW-&{?cU8$|0hp^ zt?K1F+SSh3BWUXt!|O%b+HB1Aqi*3alBCD-#yL3c(D|M+1RD$d@ zyzLWoSDPM$hDRZaF#x6n$TW|(aJGH`)**YH1%hrw-Up)6$URCUe3)TefJU`#mfV69ETzI*K5JsYW)Zu zctC5Ok(W)u{WVQAdt$aXo?H*y%>m)R+Xt(@8vPhO;B70fNn%hcW>iCO;qIw=***>)`JF5V#l!>&d?DZ)axg%p``_ zr#9DubD$?$%P<{mpmnvOU^6YRWYHx%_H5fe>5q6zJ}R>3JEfCb-uOOjXiv^|5*lsO z8d?mmjMfbAC8I>4mj~4rKkaCQ2+>O*)tXwz@>C83N$uH2JH&N^hNK zKr#`G1_q%rA&^0f@PD8vw*Vc$h@SCj_uIiuW2jm0=}_A`^@=-Mt)E^$Lw(Igg^y(K z+HT#SeQ;ZhIMZoOJIb`Wvw#ciggrepSgnt?LKp#F6A)w%;P6C#k=DczRI9w|<*LXg z$~#eFt7^b76G9N#$kWO}0!;-p|M_ArS62a-4R1Yoa?I)30{Klch;*c=Ah*!&%!iExPgqk4JELKu|bB#JM;-h-`C`8Jm zd9N+~%iM)w3!jkk-19A%E=~Ud*PFD(66Cii-gFgQ78i2nJd_=@t@f_=Q8Tv+gO%7^g71^9Xn!T^c1oSQjI`OO0NOZjl02N5t2 zJjcV=0^jl9D!UiH##@xqh1=zLho#uk$~oeT<6F75_|plFgyIAze>$<47ZZy4;zUQ{ zkKsR#8E`f8a@>AyB`2XT=CO(sS*r4}R81<{3qND3vKG=TMl4#9Msl}XD9p?@nth#}KD!g@>UAJF}6KzoFqJ%93x1bBjl$Y?2pRuBXEX*EmM|IKK%-mg}du=j#s z111F2ouCiPV#z3lHm~J%y@^bgl%|wrGLm*IDF)*0xUqOdRuo}=O``Zd zoWxulkoKY#2OAD{7}m@X3-B6khsDx%#gFg@wB(rqLj-lEcdOVV zl{ZsY%s&o4-o{lj3?Oyu6lbcqd_wBx!*uDa%oOgkl%z~z`D_KBN&23r&f??$3~8p! zS-es^& zSMwt*j>T(wr-5bJW>Yq#uEG`csy?%wBYiHFQ*hB+9Fl@BcMLXyct*Kn~UsS8^ zLN{w%yiL37qIl&PsDt7P99%fKIc_P(cww>z$su}R04_O{z~VLEJ%KakZ^!Uz4Gd#R zNGq$!OGYW>AB#87N4ESPzMhX^03lwjWG}hEpHjYB^6?@EOqrA&i+4ElBoQel%8G00 zGPwdS(JK!qDF?-r=by$U@$us+96#qPE(tz=<;Wv0m2zvJCC=n?_~u_e`2+md+*Y1{IbaD0y|cpR_2(AZ&s2#PY>6@HV>s LSrXidm|*_`jC;J& delta 1683 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 literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/log_utils.cpython-312.pyc b/src/agent/utils/__pycache__/log_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c236a806413c74edc28dfc72db158a1eb8b8510c GIT binary patch literal 1681 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 literal 0 HcmV?d00001 diff --git a/src/agent/utils/__pycache__/tool_call_utils.cpython-313.pyc b/src/agent/utils/__pycache__/tool_call_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b49d4f7eeec465e915ba86874f63d679e2d28e81 GIT binary patch literal 2848 zcmaJ@-ESMk7N6MWWH4z(e4vR7fE4MEVbq7+srH;00dpTcqSlrBC&d&aR=Xkxd(~V$Ejvcs`av<~vo7jVEDtLVil)Fel3ciFcVuC|V=Pq%CgC~E9 zzbFs^Y?2}b&^3XGic=9S&7rsyhowm~T-5y^7BGrUS=IG|p&Ge@PRC)+pV8+sm^oX+ z1y3F!ynYL;chNqSv^{8|l~vmSN*E;|H-$UNg+Pm-{U|BeehH~xA{0VN$+jd=(rN3% zt!M{Z2K5Yr2otjH-+T_YR3EyEuX2=+A8HiQ}oX)}dAX}$zz6r9kZhEHCC?7=AILeQohtJlb>9j<&v`RC%T-0p}*JwdE z#cXM|NKMz|Tv0nmNP(E{9L?#}Q1y&v3fWx7FvXmq%~F%61~FZ1SDLER;LPVJIE_*< zuSI!Nd`HcfG!tJp@jDdYfI^{=IT=5dN>Gi^)T<-MM@CX-HG17B6jNtrG~GyF)>NYB z^qKT*PA`zAF-1uxr80M_WaRQRWfThebVkkR)7JcGagM-cAxkin&^sXJ(HiFz1FO!C zkKH%jmEncbigT#p=`MFXS(UrjkqADyr=mTWtjpnLIoudN^?mZ&rT9iZ=Zi~VL|=u@=Eu?M^k_O+ZSKyh&Q)ozdBc)s>_F$<-@-oI#%<=z8PG2>&}I$ z`sw9w2LJlS7hCCwt$Mtb;g8;5arUjraNbk;G9>n)zYpDiDemBZlJ}n(a{hEgX8Oq> z$fe$%v7?TqAs5qMW%kD+2Dyz}BE*2p#Oq%GO7Eg%3#$x2$u_s1!Plt83$}O1P}*qM zF)-(OE85r}#Wv5fa8d}x0HOb94{sw+=dZ-J@&R~`h8Y-O=y;oli?uz^J89cmeoGX7 z%N=lkQycamR*9^7$xff>U~>C_O4NQ^o%`qU1ovzvig=W_Y7_&VHK0ZT?x2DS2`+|p zr~%{q+FWytn}V*+0y|VPLrpH1HF=`G8|6taY$kptATtzo5|$cawaesZG{clYsD?oZ zBQ#;iHpP5F%>p^ky_eC7to}v?)79p~W-wE-_>s#}R!D(&A=1tE`+!xmYCiYAmfqYI z0*?=V^al|0XjR%>i2^O8@Uj$cczQqn;Km2lt2IxgENq?CJ%<)KFnM3NH}uKSf>84g z-jM$E_$zY@2NokW&#|(w+M0!Ho}s$u<%eD{dHbr-g~`PeHE*mev7;gn_`0-rS=zhC z@vi-=zCHI&-8xmx)_eoyvuh6I3q0)pKJbHgSw8)^l0pD@8sw9EVEQHp5O zEx{|oi{McB*8YCjgP*&nP0^jV5Q9?`o<&Ij(Air;L*9H#_?xkF0098P(4<-8x5j1@ z)ddCaErt_9E2glc8393%DYFhkN*RV}iw+i+Ti9mwiAtsjD{@7P2n2dCGCdcSE8}Oi zOd;FsR2FxdEP`u8QIQO=WzK2n75`E**GV6nI*KZxTGo_G#jI**S;}f2qezP*y_P?% zWueJs(_8a81PdeZqd$U}M-ACum-kk~b@#xsd!XhXoR9x5cQrhF%APe&6!)y|?ztDZ z6{yB<2g{BPH*$I_2OdP~QgB%cHssw^e@zZ9TzWWCi=1rq4}AXi{kIq8TK|di`O3(R zaeG0dfAI6v{nX-tTL1BS|0@qCT5eBIxw9er=C3^2=s_L3>T+;d4z42}j`jL0@7+FH zmKrY{Tpaj009wUy!_)BiZ+5Lou;3}(4f%iNAM>Ln|G_xsA0rGhDiCHT48^8{Xhw None: + """创建历史目录、迁移旧库并初始化 SQLite 表结构。 + + 输入: 无。 + + 输出: 无(副作用:创建 history/data_base、message_store、session_meta 表)。 + + 系统定位: 模块 import 时自动执行一次,保证 SQLChatMessageHistory 可用。 + + 可扩展性: 可增加 schema 版本号与增量迁移脚本。 + """ os.makedirs(_DB_DIR, exist_ok=True) os.makedirs(MULTI_INPUTS_ROOT, exist_ok=True) # 迁移旧数据库 @@ -72,6 +97,18 @@ ensure_dirs_and_migrate_db() # ---------- 历史记录操作 ---------- def get_history(session_id: str) -> SQLChatMessageHistory: + """获取指定会话的 LangChain SQL 聊天历史对象。 + + 输入: + session_id: 会话唯一标识。 + + 输出: + SQLChatMessageHistory 实例,底层为 CHAT_HISTORY_DB_FS。 + + 系统定位: summarizer、save_messages_to_history 等读写对话记录。 + + 可扩展性: 可改为按 agent_name 分 session_id 命名空间。 + """ return SQLChatMessageHistory( session_id=session_id, connection="sqlite:///" + CHAT_HISTORY_DB_FS.replace("\\", "/"), @@ -79,6 +116,20 @@ def get_history(session_id: str) -> SQLChatMessageHistory: # ---------- session_meta 元数据管理 ---------- def get_session_meta(session_id: str, agent_name: str) -> dict: + """读取会话在指定 Agent 下的压缩元数据。 + + 输入: + session_id: 会话 ID。 + agent_name: Agent 名称(如 main)。 + + 输出: + dict: cursor_index, compressed_content, last_total_tokens; + 无记录时返回默认值(cursor=-1, 空摘要, tokens=0)。 + + 系统定位: summarizer 判断压缩档位与 runtime 更新 token 用量。 + + 可扩展性: 可增加 compress_version、embedding_id 等字段。 + """ conn = sqlite3.connect(CHAT_HISTORY_DB_FS) cur = conn.execute( "SELECT cursor_index, compressed_content, last_total_tokens FROM session_meta WHERE session_id=? AND agent_name=?", @@ -96,6 +147,20 @@ def get_session_meta(session_id: str, agent_name: str) -> dict: def update_session_meta(session_id: str, agent_name: str, cursor_index: int, compressed_content: str, last_total_tokens: int) -> None: + """写入或更新 session_meta 行(UPSERT)。 + + 输入: + session_id, agent_name: 联合主键。 + cursor_index: 已压缩消息截止索引。 + compressed_content: LLM 生成的历史摘要文本。 + last_total_tokens: 上一轮模型 reported total_tokens。 + + 输出: 无。 + + 系统定位: 压缩后与 execute_agent 记录 token 时调用。 + + 可扩展性: 可记录压缩时间戳便于调试与回滚。 + """ conn = sqlite3.connect(CHAT_HISTORY_DB_FS) conn.execute(""" INSERT INTO session_meta (session_id, agent_name, cursor_index, compressed_content, last_total_tokens) @@ -110,12 +175,37 @@ def update_session_meta(session_id: str, agent_name: str, # ---------- 工具函数 ---------- def _get_messages_since_cursor(history: SQLChatMessageHistory, cursor_index: int) -> List[BaseMessage]: + """返回游标之后未纳入摘要的消息切片。 + + 输入: + history: 聊天历史对象。 + cursor_index: 已压缩截止索引;-1 表示全部未压缩。 + + 输出: + BaseMessage 列表。 + + 系统定位: summarizer 构建发给模型的近期消息。 + + 可扩展性: 私有函数;对外行为通过 summarizer 暴露。 + """ all_msgs = history.messages if cursor_index < 0 or cursor_index >= len(all_msgs) - 1: return all_msgs return all_msgs[cursor_index + 1:] def _count_turns(messages: List[BaseMessage]) -> int: + """统计 human + ai/assistant 配对的对话轮数。 + + 输入: + messages: 消息列表。 + + 输出: + 整数轮数。 + + 系统定位: 轻量压缩时计算需保留的 CURRENT_TURNS 轮。 + + 可扩展性: 可将 tool 消息纳入轮次定义。 + """ turns = 0 i = 0 n = len(messages) @@ -132,7 +222,19 @@ def _count_turns(messages: List[BaseMessage]) -> int: # ---------- 压缩函数 ---------- def _lightweight_compress(llm, messages_to_compress: List[BaseMessage]) -> str: - """轻量压缩:保留关键事实、实体、决策,删除冗余""" + """轻量压缩:用 LLM 摘要待压缩消息,保留关键事实与决策。 + + 输入: + llm: 用于压缩的语言模型。 + messages_to_compress: 待压缩的 BaseMessage 列表。 + + 输出: + 摘要字符串。 + + 系统定位: summarizer 在 token 超过 MINI_COMPRESS_RATE 时触发。 + + 可扩展性: 可换用更小/专用摘要模型降低成本。 + """ prompt = ChatPromptTemplate.from_messages([ ("system", SUMMARY_SYSTEM_PROMPT), ("human", "请对以下对话进行轻量压缩,保留核心事实、实体、决策和对话流向。\n\n{chat}"), @@ -147,7 +249,20 @@ def _lightweight_compress(llm, messages_to_compress: List[BaseMessage]) -> str: return result.content def _hard_compress(llm, compressed_existing: str, new_chat_text: str) -> str: - """全量压缩:将已有压缩摘要和新对话内容融合为最终摘要""" + """全量压缩:合并已有摘要与游标后新对话为单一摘要。 + + 输入: + llm: 压缩用模型。 + compressed_existing: 先前摘要(可为空)。 + new_chat_text: 游标后对话的纯文本拼接。 + + 输出: + 融合后的最终摘要字符串。 + + 系统定位: token 超过 HARD_COMPRESS_RATE 时触发,游标移至末尾。 + + 可扩展性: 可分块摘要再合并,应对超长历史。 + """ prompt = ChatPromptTemplate.from_messages([ ("system", SUMMARY_SYSTEM_PROMPT), ("human", "这是之前的对话摘要:\n{previous_summary}\n\n以及后续对话内容:\n{new_chat}\n\n请生成包含所有信息的最终摘要,保留重要事实和决策。"), @@ -168,16 +283,25 @@ def summarizer( user_content: str, llm: Any ) -> Tuple[List[BaseMessage], bool]: - """ - 根据上下文窗口与上一轮 token 使用量,动态压缩历史消息并返回完整消息列表。 - :param session_id: 会话ID - :param agent_name: Agent 名称 - :param system_prompt: 系统提示词 - :param user_content: 当前用户输入文本(纯文本或已处理的多模态文本) - :param llm: 语言模型实例(用于压缩) - :return: (messages, context_exceeded) - messages: 构建好的消息列表 - context_exceeded: 是否超过 90% 窗口,需要提示新建会话 + """根据 token 占用动态压缩历史并组装本轮 LLM 输入消息列表。 + + 输入: + session_id: 会话 ID。 + agent_name: Agent 名称,用于 session_meta。 + system_prompt: 系统提示词(可追加历史摘要)。 + user_content: 当前用户输入文本。 + llm: 用于轻量/全量压缩的模型实例。 + + 输出: + (messages, context_exceeded): + - messages: SystemMessage + 未压缩近期消息 + 当前 HumanMessage + - context_exceeded: True 表示已超过 90% 窗口,应提示新建会话 + + 系统定位: + execute_agent 在调用 graph 前的上下文管理核心。 + + 可扩展性: + 阈值由 env_utils 配置;可接入 token 精确计数替代 last_total_tokens 估计。 """ meta = get_session_meta(session_id, agent_name) cursor_index = meta["cursor_index"] @@ -260,7 +384,20 @@ def summarizer( # ---------- 辅助函数 ---------- def assistant_text(value) -> str: - """把网关返回的 content、reasoning 等收成一段可见文本。""" + """将 LLM 响应的 content/reasoning 等多形态字段归一为可见纯文本。 + + 输入: + value: str | list | dict | None 等网关返回结构。 + + 输出: + 去首尾空白后的字符串;无法解析时 str(value) 或 JSON。 + + 系统定位: + runtime、llm、nodes 统一提取助手回复与 reasoning。 + + 可扩展性: + 新增厂商字段时在 key 列表中补充即可。 + """ if value is None: return "" if isinstance(value, str): @@ -290,13 +427,30 @@ def assistant_text(value) -> str: class Documents_process: - """文件分类与内容提取工具集""" + """多格式用户附件的分类、解析与多模态内容提取。 + + 系统定位: + core/llm._preprocess_messages 处理用户上传文件时调用 process()。 + + 可扩展性: + 新增格式:扩展 DOC_EXT_MAP 并实现 process_xxx 方法。 + """ @staticmethod def doc_classify(file_path: str) -> str: - """ - 根据扩展名和 MIME 类型返回内部处理类型标签。 - 可能的返回值:'plain_text'、'docx'、'pdf'、'image'、'tabular'。 + """根据扩展名与 MIME 返回内部类型标签。 + + 输入: + file_path: 本地文件路径。 + + 输出: + plain_text | docx | pdf | image | tabular。 + + 系统定位: + process() 分发入口。 + + 可扩展性: + 不支持类型抛出 ValueError,便于上层捕获并提示用户。 """ ext = pathlib.Path(file_path).suffix.lower() # 先检查文档映射表 @@ -320,12 +474,25 @@ class Documents_process: # ---------- 各类文件的具体处理方法 ---------- @staticmethod def process_plain_text(file_path: str) -> tuple[str, list]: + """读取 UTF-8 纯文本文件。 + + 输入: file_path 本地路径。 + 输出: (文本内容, 空图片列表)。 + 系统定位: plain_text 类型处理。 + 可扩展性: 可增加编码探测(chardet)。 + """ with open(file_path, "r", encoding="utf-8") as f: return f.read(), [] @staticmethod def process_docx(file_path: str) -> tuple[str, list]: - """按文档顺序提取文本、图片和表格,并将短标题与图表合并。""" + """按文档顺序提取 Word 文本、内嵌图片与表格。 + + 输入: file_path .docx 路径。 + 输出: (带 N、 标记的文本, 图片字节列表)。 + 系统定位: 用户上传 docx 附件解析。 + 可扩展性: 依赖 python-docx;可扩展页眉页脚、脚注。 + """ try: from docx import Document from docx.oxml.ns import qn @@ -434,6 +601,13 @@ class Documents_process: @staticmethod def process_pdf(file_path: str) -> tuple[str, list]: + """提取 PDF 文本与内嵌图片占位符。 + + 输入: file_path .pdf 路径。 + 输出: (分页拼接文本含 N, 图片字节列表)。 + 系统定位: PDF 附件入模前解析。 + 可扩展性: 依赖 pypdf;扫描件可接 OCR 管线。 + """ try: from pypdf import PdfReader except ImportError: @@ -468,10 +642,12 @@ class Documents_process: @staticmethod def process_tabular(file_path: str) -> tuple[str, list]: - """ - 处理 CSV 和 Excel(.xlsx) 文件。 - 将所有 sheet 的内容转化为
SheetName\n内容
格式的字符串。 - 图片列表固定为空。 + """将 CSV / xlsx 转为 标记的表格文本。 + + 输入: file_path .csv 或 .xlsx。 + 输出: (多 sheet 表格字符串, 空图片列表)。 + 系统定位: 结构化数据附件供 LLM 阅读。 + 可扩展性: 可支持 xls、parquet;大表可采样行数。 """ try: import csv @@ -507,12 +683,26 @@ class Documents_process: @staticmethod def process_image(file_path: str) -> tuple[str, list]: + """读取图片文件为字节,文本部分为空。 + + 输入: file_path 图片路径。 + 输出: ("", [图片字节])。 + 系统定位: 与 llm 中 image_url 分支配合多模态输入。 + 可扩展性: 可在此预调用 resize_image_if_needed。 + """ with open(file_path, "rb") as f: return "", [f.read()] # ---------- 统一处理入口 ---------- @staticmethod def process(file_path: str) -> tuple[str, list]: + """统一附件处理入口:分类后分发到对应 process_* 方法。 + + 输入: file_path 本地文件路径。 + 输出: (文本内容, 图片字节列表)。 + 系统定位: llm._preprocess_messages 文件 part 处理。 + 可扩展性: 新增类型在 doc_classify 与分支中注册。 + """ file_type = Documents_process.doc_classify(file_path) if file_type == "plain_text": @@ -527,13 +717,35 @@ class Documents_process: return Documents_process.process_tabular(file_path) else: raise ValueError(f"未处理的文件类型: {file_type}") -# 文本分块工具 + +###### 文本分块工具(多线程语音生成TTS) ###### SENT_SPLIT_PATTERN = re.compile(r'(?<=[。!?;,、])') REMOVE_PUNCT_PATTERN = re.compile(r'[。!?;,、:;“”()《》【】\-\_\—\~\.\,\!\?\s·]') def effective_length(text: str) -> int: + """计算去除标点空白后的有效字符长度。 + + 输入: text 字符串。 + 输出: 整数长度。 + 系统定位: split_text 合并短句时的阈值判断。 + 可扩展性: 可按语言调整 REMOVE_PUNCT_PATTERN。 + """ return len(REMOVE_PUNCT_PATTERN.sub('', text)) + def split_text(text: str, min_effective_chars: int = 20) -> list[str]: + """按中文标点分句并合并过短片段,供 TTS 分块请求。 + + 输入: + text: 待切分文本。 + min_effective_chars: 每块最少有效字符数。 + + 输出: + 字符串列表,每块适合单次 TTS 调用。 + + 系统定位: core/llm._text_to_audio 并行合成前分块。 + + 可扩展性: 可改为按 token 或时长分块。 + """ # 先按标点粗分 raw_chunks = [c.strip() for c in SENT_SPLIT_PATTERN.split(text) if c.strip()] if not raw_chunks: @@ -555,7 +767,18 @@ def split_text(text: str, min_effective_chars: int = 20) -> list[str]: return merged def merge_wavs(audio_parts: list[bytes]) -> bytes: - """将多个 WAV 字节串按顺序拼接成一个 WAV""" + """将多段 WAV 二进制按顺序拼接为单个 WAV 文件字节。 + + 输入: + audio_parts: WAV 格式字节列表(采样率等参数以首段为准)。 + + 输出: + 合并后的 WAV bytes。 + + 系统定位: llm._text_to_audio 多块 TTS 结果合并。 + + 可扩展性: 参数不一致时可增加重采样;可支持 mp3 拼接。 + """ if len(audio_parts) == 1: return audio_parts[0] import io, wave diff --git a/src/agent/utils/database_utils.py b/src/agent/utils/database_utils.py index 6b45d36..d1197c1 100644 --- a/src/agent/utils/database_utils.py +++ b/src/agent/utils/database_utils.py @@ -1,3 +1,16 @@ +"""PostgreSQL 元数据查询与只读 SQL 执行(供 Agent 工具层使用)。 + +系统定位: + 为数据库相关 Tool 提供安全封装的 schema introspection 与 SELECT 查询, + 结果统一 JSON 序列化便于模型消费。 + +安全策略: + execute_query / validate_query 仅允许 SELECT 与 WITH 语句。 + +可扩展性: + - 可配置 fetch 行数上限、连接池参数 + - 注释掉的 rename_table / update_table_comment 可按需启用并加权限控制 +""" from sqlalchemy import create_engine, inspect, text import json from typing import Optional, List @@ -5,8 +18,15 @@ from uuid import UUID from datetime import datetime class JSONEncoder(json.JSONEncoder): - """自定义JSON编码器,处理非标准序列化类型""" + """扩展 json.JSONEncoder,支持 UUID、datetime 及带 __dict__ 的对象。 + + 系统定位: PostgreSQL_Manager 各方法 dumps 时的默认编码器。 + + 可扩展性: 可增加 Decimal、date、Enum 等类型分支。 + """ + def default(self, obj): + """将 UUID、datetime 及带 __dict__ 的对象转为 JSON 可编码值。""" if isinstance(obj, UUID): return str(obj) elif isinstance(obj, datetime): @@ -16,7 +36,20 @@ class JSONEncoder(json.JSONEncoder): return super().default(obj) def make_json_serializable(obj): - """递归转换对象为JSON序列化格式""" + """递归将嵌套结构转为 JSON 可序列化形式。 + + 输入: + obj: dict | list | UUID | datetime | 任意对象。 + + 输出: + 仅含 JSON 兼容类型的树结构。 + + 系统定位: + get_table_schema / get_table_comments 返回前清洗 SQLAlchemy 反射结果。 + + 可扩展性: + 与 JSONEncoder.default 保持类型处理一致。 + """ if isinstance(obj, dict): return {k: make_json_serializable(v) for k, v in obj.items()} elif isinstance(obj, (list, tuple)): @@ -31,21 +64,46 @@ def make_json_serializable(obj): return obj class PostgreSQL_Manager: + """PostgreSQL 连接池管理与只读元数据/查询 API。 + + 系统定位: + Agent 数据库工具的后端实现类。 + + 可扩展性: + 可注入 engine、增加读写分离、审计日志。 + """ + def __init__(self, connection_params): - """ - PostgreSQL数据库管理类,提供连接和操作数据库的功能 - Args: - connection_params (dict): 连接参数字符串,格式为: - postgresql+psycopg2://用户名:密码@主机:端口/数据库名 - 本机默认为:postgresql+psycopg2://postgres:12345678@localhost:5432/agent_controller + """创建 SQLAlchemy 引擎与连接池。 + + 输入: + connection_params: SQLAlchemy URL,如 + postgresql+psycopg2://user:pass@host:5432/dbname + + 输出: + 无(设置 self.engine)。 + + 系统定位: + 工具初始化时构造单例或按请求构造。 + + 可扩展性: + pool_size、pool_recycle 可改为构造参数或环境变量。 """ self.engine = create_engine(connection_params, pool_size=5, pool_recycle=7200) def get_table_names(self) -> list[str]: - """ - 获取数据库中的所有表名 - Returns: - list: 数据库中的表名列表 + """列出当前库中所有表名。 + + 输入: 无(使用 self.engine)。 + + 输出: + 表名字符串列表;异常时返回空列表并打印错误。 + + 系统定位: + Agent 探索库结构的第一步。 + + 可扩展性: + 可过滤 schema、排除系统表。 """ try: # 使用SQLAlchemy的inspect功能获取数据库表名 @@ -56,12 +114,19 @@ class PostgreSQL_Manager: return [] def get_table_comments(self, table_name: str = None) -> str: - """ - 获取指定表/所有表的注释信息,并整理成JSON格式 - Args: - table_name (str, optional): 表名,如果为None则返回所有表 - Returns: - str: JSON格式的表的注释信息 + """获取表 COMMENT 元数据并返回 JSON 字符串。 + + 输入: + table_name: 指定表名;None 表示全部表。 + + 输出: + JSON 字符串;错误时含 error 字段。 + + 系统定位: + 供 LLM 理解业务表含义。 + + 可扩展性: + 可合并列级 comment。 """ try: inspector = inspect(self.engine) @@ -80,12 +145,19 @@ class PostgreSQL_Manager: return json.dumps(error_msg, ensure_ascii=False, indent=2) def get_table_schema(self, table_names: Optional[List[str]] = None) -> str: - """ - 获取指定表/所有表的schema信息,并整理成JSON格式 - Args: - table_names (List[str], optional): 表名列表,如果为None则返回所有表 - Returns: - str: JSON格式的表的schema信息 + """反射表结构(列、主键、外键、索引)并返回 JSON。 + + 输入: + table_names: 表名列表;None 表示所有表。 + + 输出: + JSON 字符串,每表含 columns/primary_keys/foreign_keys/indexes。 + + 系统定位: + Text-to-SQL 或数据分析前的 schema 上下文。 + + 可扩展性: + 可增加视图、物化视图、枚举类型信息。 """ try: inspector = inspect(self.engine) @@ -111,12 +183,19 @@ class PostgreSQL_Manager: return json.dumps(error_msg, ensure_ascii=False, indent=2) def execute_query(self, query: str) -> str: - """ - 执行任意SQL查询,并返回结果 - Args: - query (str): SQL查询语句 - Returns: - str: JSON格式的查询结果或错误信息 + """执行只读 SQL 并返回最多 100 行结果的 JSON。 + + 输入: + query: 必须以 select 或 with 开头(大小写不敏感)。 + + 输出: + JSON 结果数组或 error/message 对象。 + + 系统定位: + Agent 安全查询数据;禁止 DML/DDL。 + + 可扩展性: + fetch 行数、超时、只读事务隔离级别可配置。 """ # 安全检查,防止执行危险的SQL语句,仅允许select和with查询语句 if not query.strip().lower().startswith(('select', 'with')): @@ -145,12 +224,19 @@ class PostgreSQL_Manager: return json.dumps(error_msg, ensure_ascii=False, indent=2) def validate_query(self, query: str) -> str: - """ - 验证SQL查询语句的合法性 - Args: - query (str): SQL查询语句 - Returns: - bool: 查询语句是否合法 + """用 EXPLAIN(PostgreSQL)或试执行验证 SQL 合法性。 + + 输入: + query: 只读 SQL 字符串。 + + 输出: + JSON:成功含 message;失败含 error。 + + 系统定位: + 执行前的预检,减少无效查询。 + + 可扩展性: + 非 PG 方言可切换验证策略。 """ if not query or not query.strip(): error_msg = {"error": f"Error occurred while validating query: Query is must not be empty."} diff --git a/src/agent/utils/env_utils.py b/src/agent/utils/env_utils.py index fb675f3..6b8dad4 100644 --- a/src/agent/utils/env_utils.py +++ b/src/agent/utils/env_utils.py @@ -1,11 +1,41 @@ -import os -from typing import Dict, Tuple +"""环境变量加载与全局配置常量。 -from dotenv import load_dotenv -load_dotenv(override=True) +系统定位: + 项目配置入口,模块导入时从 ``core/env_config.json`` 加载配置, + 并向 ``core/llm``、``utils/agent_utils``、``utils/image_utils`` 等模块 + 提供 LLM、压缩策略、RAG、浏览器等运行时参数。 + +可扩展性: + - 新增配置项时,在此定义常量并注明默认值与类型转换逻辑。 + - 可将 ``BROWSER_MAP`` 扩展为从环境变量或配置文件动态加载。 +""" +import json +import os +from pathlib import Path +from typing import Dict, Tuple def _get_env(name: str, default: str | None = None, cast: type | None = None, required: bool = False): + """读取并可选类型转换单个环境变量。 + + 功能描述: + 从 ``os.environ`` 取值,支持必填校验与类型转换。 + + 输入: + name: 环境变量名。 + default: 缺省值;与 required=True 互斥使用。 + cast: 转换类型(如 int、float)。 + required: 为 True 且变量缺失时抛出 ``EnvironmentError``。 + + 输出: + 原始字符串或 cast 后的值;缺失且无 default 时为 None。 + + 系统定位: + 本模块内部私有辅助,所有模块级常量均通过此函数初始化。 + + 可扩展性: + 可扩展 bool 解析、路径规范化、密钥脱敏日志等。 + """ value = os.getenv(name, default) if value is None and required: raise EnvironmentError(f"Missing required environment variable: {name}") @@ -17,25 +47,84 @@ def _get_env(name: str, default: str | None = None, cast: type | None = None, re raise ValueError(f"Invalid value for {name}: {value}") from exc -LLM_BASE_URL = _get_env("LLM_BASE_URL", required=True) -LLM_API_KEY = _get_env("LLM_API_KEY", required=True) -LLM_MODEL = _get_env("LLM_MODEL", required=True) -LLM_TIMEOUT = _get_env("LLM_TIMEOUT", default="60", cast=float) -# LLM_REQUESTS_PER_SECOND = _get_env("LLM_REQUESTS_PER_SECOND", default="10", cast=float) -# LLM_MAX_BUCKET_SIZE = _get_env("LLM_MAX_BUCKET_SIZE", default="10", cast=int) -# LLM_CHECK_EVERY_N_SECONDS = _get_env("LLM_CHECK_EVERY_N_SECONDS", default="0.1", cast=float) -# ASR_TTS_BASE_URL = _get_env("ASR_TTS_BASE_URL") -TTS_BASE_URL = _get_env("TTS_BASE_URL") -ASR_BASE_URL = _get_env("ASR_BASE_URL") -RAG_BASE_URL = _get_env("RAG_BASE_URL") -LLM_CONTEXT_WINDOW = int(os.getenv("LLM_CONTEXT_WINDOW", 131072)) -MINI_COMPRESS_RATE = float(os.getenv("MINI_COMPRESS_RATE", 0.5)) -HARD_COMPRESS_RATE = float(os.getenv("HARD_COMPRESS_RATE", 0.75)) -CURRENT_TURNS = int(os.getenv("CURRENT_TURNS", 4)) -IMG_SIZE = int(os.getenv("IMG_SIZE", 768)) -RAG_CHUNK_SIZE=int(os.getenv("RAG_CHUNK_SIZE", 500)) -RAG_CHUNK_OVERLAP=int(os.getenv("RAG_CHUNK_OVERLAP", 50)) +def _refresh_globals(): + """刷新模块级常量(热重载用)。""" + global LLM_BASE_URL, LLM_API_KEY, LLM_MODEL, LLM_TIMEOUT + global TTS_BASE_URL, ASR_BASE_URL, RAG_BASE_URL + global LLM_CONTEXT_WINDOW, MINI_COMPRESS_RATE, HARD_COMPRESS_RATE, CURRENT_TURNS + global IMG_SIZE, RAG_CHUNK_SIZE, RAG_CHUNK_OVERLAP, GROUNDING_WIDTH, GROUNDING_HEIGHT + LLM_BASE_URL = _get_env("LLM_BASE_URL", required=True) + LLM_API_KEY = _get_env("LLM_API_KEY", required=True) + LLM_MODEL = _get_env("LLM_MODEL", required=True) + LLM_TIMEOUT = _get_env("LLM_TIMEOUT", default="60", cast=float) + TTS_BASE_URL = _get_env("TTS_BASE_URL") + ASR_BASE_URL = _get_env("ASR_BASE_URL") + RAG_BASE_URL = _get_env("RAG_BASE_URL") + LLM_CONTEXT_WINDOW = int(os.getenv("LLM_CONTEXT_WINDOW", 131072)) + MINI_COMPRESS_RATE = float(os.getenv("MINI_COMPRESS_RATE", 0.5)) + HARD_COMPRESS_RATE = float(os.getenv("HARD_COMPRESS_RATE", 0.75)) + CURRENT_TURNS = int(os.getenv("CURRENT_TURNS", 4)) + IMG_SIZE = int(os.getenv("IMG_SIZE", 768)) + RAG_CHUNK_SIZE = int(os.getenv("RAG_CHUNK_SIZE", 500)) + RAG_CHUNK_OVERLAP = int(os.getenv("RAG_CHUNK_OVERLAP", 50)) + GROUNDING_WIDTH = int(os.getenv("GROUNDING_WIDTH", 1000)) + GROUNDING_HEIGHT = int(os.getenv("GROUNDING_HEIGHT", 1000)) + + +def _load_config() -> dict: + """加载 env_config.json,不存在时回退到 .env 文件。 + + 优先级: env_config.json > .env > 默认值 + """ + _config_path = Path(__file__).resolve().parents[1] / "core" / "env_config.json" + + if _config_path.is_file(): + try: + with open(_config_path, "r", encoding="utf-8") as f: + config = json.load(f) + if isinstance(config, dict): + # 将非 models 的值写入 os.environ + for key, value in config.items(): + if key != "models" and value is not None: + os.environ[key] = str(value) + # 从 models 列表中提取第一个模型的 LLM 参数 + models = config.get("models") + if isinstance(models, list) and models: + m = models[0] + if m.get("api_key"): os.environ["LLM_API_KEY"] = str(m["api_key"]) + if m.get("base_url"): os.environ["LLM_BASE_URL"] = str(m["base_url"]) + if m.get("model"): os.environ["LLM_MODEL"] = str(m["model"]) + if m.get("timeout") is not None: os.environ["LLM_TIMEOUT"] = str(m["timeout"]) + _refresh_globals() + return config + except (json.JSONDecodeError, OSError): + pass + + # 回退:读取 .env 文件 + _env_path = Path(__file__).resolve().parents[3] / ".env" + if _env_path.is_file(): + try: + with open(_env_path, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, value = line.partition("=") + key = key.strip() + value = value.strip().strip("'").strip('"') + if key not in os.environ: # 不覆盖已设置的值 + os.environ[key] = value + except OSError: + pass + + return {} + + +_load_config() +_refresh_globals() # 确保无论配置加载成功与否,都有默认值 + +# ---------- 浏览器工具映射(内部名 -> 可执行名, 显示名) ---------- BROWSER_MAP: Dict[str, Tuple[str, str]] = { "edge": ("microsoft-edge", "Microsoft Edge"), "chrome": ("chrome", "Google Chrome"), diff --git a/src/agent/utils/image_utils.py b/src/agent/utils/image_utils.py index 55e92b4..58e0a88 100644 --- a/src/agent/utils/image_utils.py +++ b/src/agent/utils/image_utils.py @@ -1,4 +1,13 @@ -"""图片缩放、体积控制与 OpenAI 风格多模态 image_url 片段(供 LLM / Agent 使用)。""" +"""图片缩放、体积控制与 OpenAI 风格多模态 image_url 片段构建。 + +系统定位: + 多模态输入适配层,被 ``core/llm``、``core/nodes``、``utils/agent_utils`` + 调用,将本地路径或内存字节转为 LLM 可消费的 data URL 内容块。 + +可扩展性: + - 可接入 WebP/AVIF 编码、按模型 token 预算动态调整 ``max_size``。 + - ``SupportsChatCompletion`` 协议可扩展为统一多模态网关接口。 +""" import base64 import io import os @@ -7,17 +16,37 @@ from PIL import Image from agent.utils.env_utils import IMG_SIZE + class SupportsChatCompletion(Protocol): - """具备 ``chat_completion(messages, **kwargs)`` 的 LLM(如 ``ChatQwen35OpenAI``)。""" + """具备 chat_completion 方法的 LLM 协议(如 ChatQwen)。 + + 系统定位: 类型提示用,便于静态检查与依赖注入。 + + 可扩展性: 可补充 stream、embed 等方法签名。 + """ def chat_completion(self, messages: list[Any], **kwargs: Any) -> Any: ... + # 小写扩展名(含点),用于判断路径是否为支持的栅格图 IMAGE_FILE_EXTENSIONS = frozenset({".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp"}) def guess_image_mime(extension_without_dot: str) -> str: - """根据扩展名返回 image/* MIME,未知则回退为 image/jpeg。""" + """根据文件扩展名推断 image/* MIME 类型。 + + 输入: + extension_without_dot: 扩展名,可带或不带点(如 png、.jpg)。 + + 输出: + MIME 字符串;未知扩展名回退为 ``image/jpeg``。 + + 系统定位: + ``image_path_to_openai_image_url_part`` 构造 data URL 时使用。 + + 可扩展性: + 可改用 ``mimetypes`` 或 magic bytes 双重校验。 + """ ext = extension_without_dot.lower().lstrip(".") if ext in ("png", "jpg", "jpeg", "gif", "bmp", "webp"): return "image/jpeg" if ext == "jpg" else f"image/{ext}" @@ -25,10 +54,25 @@ def guess_image_mime(extension_without_dot: str) -> str: def resize_image_if_needed(image_path_or_bytes: str | bytes, *, max_size: int = IMG_SIZE) -> bytes: - """ - 控制图片体积,避免 data-url base64 触发超长 token。 - - 若图片尺寸过大,等比例缩放(默认最大边 max_size) - - 优先压成 JPEG(透明图保底转白底 RGB) + """等比缩放并压缩图片,控制 base64 体积与 token 消耗。 + + 功能描述: + - 超长边超过 max_size 时等比缩放(LANCZOS) + - RGBA/LA 透明图转白底后优先输出 JPEG + - JPEG quality=70,PNG 保留透明通道场景外的优化输出 + + 输入: + image_path_or_bytes: 本地路径或原始图片字节。 + max_size: 最大边长像素,默认来自环境变量 IMG_SIZE。 + + 输出: + 处理后的图片二进制(JPEG 或 PNG)。 + + 系统定位: + 所有图片入模前的统一预处理入口。 + + 可扩展性: + 可按用途(vision / thumbnail)传入不同 quality 与 max_size 配置。 """ if isinstance(image_path_or_bytes, str): img = Image.open(image_path_or_bytes) @@ -77,16 +121,42 @@ def resize_image_if_needed(image_path_or_bytes: str | bytes, *, max_size: int = def image_to_base64(image_content: str | bytes) -> str | None: - """将图片路径或原始字节转为 base64 字符串;失败返回 None。""" + """将图片路径或字节转为 base64 字符串。 + + 输入: + image_content: 文件路径或原始字节。 + + 输出: + base64 编码字符串;处理失败返回 None。 + + 系统定位: + 供路径类 image_url 块与简单编码场景使用。 + + 可扩展性: + 失败时可返回错误码或记录日志,便于排查。 + """ try: processed = resize_image_if_needed(image_content) return base64.b64encode(processed).decode("utf-8") - except Exception as e: + except Exception: return None def _mime_from_magic(data: bytes) -> str: - """根据压缩后图片文件头推断 MIME,用于内存字节构造 data URL。""" + """根据文件头魔数推断图片 MIME。 + + 输入: + data: 压缩后的图片字节。 + + 输出: + ``image/jpeg`` | ``image/png`` | ``image/gif``;默认 jpeg。 + + 系统定位: + 内存字节构造 data URL 时,扩展名不可用时的回退。 + + 可扩展性: + 可补充 WebP、BMP 等魔数识别。 + """ if len(data) >= 2 and data[:2] == b"\xff\xd8": return "image/jpeg" if len(data) >= 8 and data[:8] == b"\x89PNG\r\n\x1a\n": @@ -101,10 +171,26 @@ def image_bytes_to_openai_image_url_part( *, detail: str | None = None, ) -> dict | None: - """将内存中的图片字节(如截屏 PNG)转为 OpenAI 风格的 image_url 块;失败返回 None。""" + """将内存图片字节转为 OpenAI Chat Completions 的 image_url 内容块。 + + 输入: + image_bytes: 原始或已编码图片字节(如工具截屏 PNG)。 + detail: 可选,传给模型的 detail 级别(auto/low/high)。 + + 输出: + ``{"type": "image_url", "image_url": {"url": "data:..."}}``; + 失败返回 None。 + + 系统定位: + ``core/nodes.process_tool_artifact`` 将工具截图注入对话; + ``core/llm`` 处理附件内嵌图片。 + + 可扩展性: + 可支持 URL 引用模式,避免大 base64 内联。 + """ try: processed = resize_image_if_needed(image_bytes) - except Exception as e: + except Exception: return None mime = _mime_from_magic(processed) b64_str = base64.b64encode(processed).decode("utf-8") @@ -120,9 +206,20 @@ def image_path_to_openai_image_url_part( *, detail: str | None = None, ) -> dict | None: - """ - 从本地图片路径构造 OpenAI Chat Completions 的 image_url 内容块。 - 无法编码时返回 None。 + """从本地图片路径构造 OpenAI 风格 image_url 内容块。 + + 输入: + file_path: 本地图片绝对或相对路径。 + detail: 可选 detail 级别。 + + 输出: + 与 ``image_bytes_to_openai_image_url_part`` 相同结构;失败 None。 + + 系统定位: + ``core/llm._process_image_content`` 将用户上传图片路径转为模型输入。 + + 可扩展性: + 可增加文件大小上限校验、病毒扫描钩子。 """ b64_str = image_to_base64(file_path) if not b64_str: diff --git a/src/agent/utils/log_utils.py b/src/agent/utils/log_utils.py index b36cb9e..f9c138e 100644 --- a/src/agent/utils/log_utils.py +++ b/src/agent/utils/log_utils.py @@ -1,3 +1,13 @@ +"""基于 loguru 的全局日志封装。 + +系统定位: + 提供项目级单例 ``log``,供各模块统一输出结构化调试日志; + 默认输出到 stdout,级别 DEBUG。 + +可扩展性: + - 可在 ``MyLogger.__init__`` 中增加文件轮转、按模块过滤等 handler。 + - 可封装 ``log.bind(session_id=...)`` 以支持请求级追踪。 +""" import sys from loguru import logger @@ -5,7 +15,19 @@ from loguru import logger class MyLogger: + """loguru 日志器包装类,统一格式与输出目标。""" + def __init__(self): + """初始化并配置 loguru:移除默认 handler,添加 stdout DEBUG 输出。 + + 输入: 无。 + + 输出: 无(副作用:配置全局 logger)。 + + 系统定位: 模块加载时由 ``log = MyLogger().get_logger()`` 实例化一次。 + + 可扩展性: 可增加环境变量控制日志级别、JSON 格式输出等。 + """ self.logger = logger self.logger.remove() self.logger.add( @@ -21,6 +43,16 @@ class MyLogger: ) def get_logger(self): + """返回已配置的 loguru Logger 实例。 + + 输入: 无。 + + 输出: loguru.Logger。 + + 系统定位: 供模块导出 ``log`` 单例。 + + 可扩展性: 可改为返回带默认 context 的 bound logger。 + """ return self.logger diff --git a/src/agent/utils/tool_call_utils.py b/src/agent/utils/tool_call_utils.py new file mode 100644 index 0000000..21f2dfc --- /dev/null +++ b/src/agent/utils/tool_call_utils.py @@ -0,0 +1,121 @@ +"""工具调用规范化与人工编辑解析工具。 + +系统定位: + 位于 Agent 工具链与人工确认(HITL)之间的适配层,被 ``core/routing``、 + ``core/runtime``、``core/approvals`` 调用,负责将 LangChain 工具调用对象 + 统一为字典格式,并解析用户在 UI 中编辑后的指令。 + +可扩展性: + - 新增工具参数格式时,可在 ``normalize_tool_call`` 中扩展 args 解析逻辑。 + - ``parse_edited_instruction`` 可支持更多用户输入格式(如 YAML、键值对等)。 +""" +from __future__ import annotations + +import json +from typing import Any + + +def json_safe(value: Any) -> Any: + """将任意 Python 对象递归转换为 JSON 可序列化结构。 + + 功能描述: + 尝试直接序列化;若遇到不可序列化类型,则递归处理 dict/list/tuple, + 其余类型转为字符串。 + + 输入: + value: 任意 Python 对象。 + + 输出: + JSON 兼容的 dict / list / str / 基本类型;结构与原值对应。 + + 系统定位: + 供 ``core/approvals.build_approval_meta`` 在展示待确认工具参数时使用, + 避免 UI 层因不可序列化对象而报错。 + + 可扩展性: + 可为 datetime、Decimal、Enum 等类型增加专用分支,而非一律 ``str()``。 + """ + try: + json.dumps(value, ensure_ascii=False) + return value + except TypeError: + if isinstance(value, dict): + return {str(k): json_safe(v) for k, v in value.items()} + if isinstance(value, (list, tuple)): + return [json_safe(v) for v in value] + return str(value) + + +def normalize_tool_call(tool_call: Any) -> dict[str, Any]: + """将 LangChain ToolCall 或原始 dict 规范化为统一字典。 + + 功能描述: + 兼容 dict 与对象两种来源,解析 args(支持 JSON 字符串), + 输出固定字段 ``id``、``name``、``args``。 + + 输入: + tool_call: LangChain ``ToolCall`` 对象,或含 name/args/id 的 dict。 + + 输出: + ``{"id": str|None, "name": str|None, "args": dict}``。 + + 系统定位: + ``core/routing`` 在判断工具执行策略和提取待确认项时统一调用, + 屏蔽不同 LLM 返回格式的差异。 + + 可扩展性: + 可在此增加参数校验、默认值填充,或对接 JSON Schema 校验。 + """ + if isinstance(tool_call, dict): + name = tool_call.get("name") + args = tool_call.get("args") or {} + call_id = tool_call.get("id") + else: + name = getattr(tool_call, "name", None) + args = getattr(tool_call, "args", {}) or {} + call_id = getattr(tool_call, "id", None) + if isinstance(args, str): + try: + args = json.loads(args) + except Exception: + args = {"raw": args} + return {"id": call_id, "name": name, "args": args or {}} + + +def parse_edited_instruction(instruction: str, fallback: dict[str, Any]) -> dict[str, Any]: + """解析用户在「编辑」确认流程中提交的指令文本。 + + 功能描述: + 优先按 JSON 解析完整工具调用;失败则将原文本写入 ``args.instruction``; + 兼容 ``tool_name`` 别名字段。 + + 输入: + instruction: 用户编辑后的文本(可为 JSON 或纯文本)。 + fallback: 原始工具调用 dict,解析失败时作为回退。 + + 输出: + 规范化后的工具调用 dict,至少含 ``name`` 与 ``args``。 + + 系统定位: + ``core/runtime.continue_after_human_action`` 在 decision=edit 时调用, + 将 UI 编辑结果还原为可执行的工具调用。 + + 可扩展性: + 可增加 schema 校验、参数合并策略(仅覆盖用户修改的字段)。 + """ + raw = (instruction or "").strip() + if not raw: + return fallback + try: + parsed = json.loads(raw) + except json.JSONDecodeError: + updated = dict(fallback) + updated["args"] = {"instruction": raw} + return updated + if isinstance(parsed, dict): + if "name" not in parsed and "tool_name" in parsed: + parsed["name"] = parsed.get("tool_name") + if "args" not in parsed: + parsed["args"] = fallback.get("args") or {} + return parsed + return fallback diff --git a/src/agent/workspace/.todo b/src/agent/workspace/.todo new file mode 100644 index 0000000..9e86f67 --- /dev/null +++ b/src/agent/workspace/.todo @@ -0,0 +1,53 @@ +# Plan TodoList + +路径选择:路径一:使用浏览器控制 + +# 执行计划:读取 Bilibili 首页第一个视频标题并分析 + +**说明**:本计划旨在通过浏览器自动化控制访问 Bilibili 首页,提取首个视频标题,并提供基于当前网络热点和一般认知的理解。由于我无法直接“看到”实时变化的互联网内容,以下流程将指导如何获取最新数据并进行分析。在实际执行中,我会根据获取到的具体标题进行针对性解读。 + +## Todo List + +- [x] **步骤 1:环境准备与导航** · 状态:进行中 · 状态:已完成 + - **依赖**:无 + - **状态**:待开始 + - **任务描述**: + 1. 启动内置浏览器或模拟浏览器会话。 + 2. 导航至 Bilibili 首页 URL (`https://www.bilibili.com`)。 + 3. 等待页面完全加载,确保动态内容(如推荐流)已渲染完毕。 + +- [x] **步骤 2:定位首个视频元素** · 状态:进行中 · 状态:已完成 + - **依赖**:步骤 1 完成 + - **状态**:待开始 + - **任务描述**: + 1. 识别首页的主要视频推荐区域(通常是“推荐”标签页下的第一个卡片)。 + 2. 定位包含视频标题的 DOM 元素(通常位于视频封面下方或悬浮层中)。 + 3. *注意*:需排除广告位或非视频内容(如直播入口、专栏文章),确保选取的是标准的 UP 主投稿视频。 + +- [x] **步骤 3:提取视频标题文本** · 状态:进行中 · 状态:已完成 + - **依赖**:步骤 2 完成 + - **状态**:待开始 + - **任务描述**: + 1. 从定位到的元素中提取纯文本标题。 + 2. 清理多余的空白字符或隐藏的控制符。 + 3. 记录提取结果,若提取失败则重试或尝试备用选择器。 + +- [x] **步骤 4:信息分析与理解生成** · 状态:进行中 · 状态:已完成 + - **依赖**:步骤 3 完成 + - **状态**:待开始 + - **任务描述**: + 1. 基于提取的标题,结合当前的社会热点、B站社区文化及常见视频类型进行分析。 + 2. 判断视频可能的主题(如科技评测、生活 vlog、游戏实况、知识科普等)。 + 3. 评估标题是否具有吸引力(如是否使用悬念、热点词汇、情绪化表达)。 + 4. 给出简短的个性化理解和建议观看理由。 + +- [x] **步骤 5:输出最终报告** · 状态:进行中 · 状态:已完成 + - **依赖**:步骤 4 完成 + - **状态**:待开始 + - **任务描述**: + 1. 整合提取的标题和分析结果。 + 2. 以清晰的结构呈现给用户,包括“原始标题”、“内容推测”和“我的理解”。 + +--- + +**备注**:在执行过程中,我将根据实际抓取到的标题实时更新后续的分析内容。如果首页因登录状态或算法推荐显示不同内容,我将基于当时可见的第一个视频进行处理。 diff --git a/src/web/__pycache__/__init__.cpython-313.pyc b/src/web/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1537bedc0af33b97e30c350485d9ea5667525d92 GIT binary patch literal 256 zcmey&%ge<81O)~UG6R70V-N=h7@>^M96-iYhG2#whIB?vrcw_eH#{{-;b~{@^VREL z&Yk(Jq4CAmmS?*rJlWCrY|fgeb2cbA26!qwo74NWYvKDn9WRzQDkLNnrxul^79}Jo zJX^8w>D=k>_jEpPX!6r!yu}_LpOT*(AHR~}GswnUuFh65p+MEeF&1WqW@a%ism0kP z`2{hK>8W`o@jj`EMR}Qd>G8RldHF>U!IhV-lzyNO-?R1L3Wp@=zjLgq$KImV&Cp@zTchizxF=o)TvMb zyQk;%``rWCXV*DvKhN50t-bczYyY*wVbkFnF=u+sbBA=gU(pZcVsCUh;YJ2x$91Bv zTqg>mzE-Hym+J+J8)^;Z26i`=8`<4dZen+HxtZN9qeB1U~y}0R$X>^ zwxHANyvg32a{{k=&1|I!vHLkWS~Dm1Oo@HpXXPVN&M5Clr5vxdW|5Ld?7mBo3KGAm zxibc(#6%%J&vSbIhP5xL)?GKIynw}RwPWkXl^3$Oy>@)vgz^b2o?JVzuBg1IZc_QA zy2<5}>!y@Xshe6pwXV3lxUQtUq;6XIw7Ti#)9YrG�SOKC^CC`K*6!f0fVn&K4c} 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%0AO{GP)_! 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 pv~!SRzs-(3}y*l7QH92^6pB$ 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 literal 0 HcmV?d00001 diff --git a/src/web/__pycache__/ui_session.cpython-312.pyc b/src/web/__pycache__/ui_session.cpython-312.pyc index 388d1e6d5230fde5c5a49a1d954a6986ee2fa3ee..5aa0596537d4714467343757397661a52efb097c 100644 GIT binary patch delta 14453 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 delta 6852 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 literal 0 HcmV?d00001 diff --git a/src/web/app.css b/src/web/app.css index d6250d3..dc58921 100644 --- a/src/web/app.css +++ b/src/web/app.css @@ -487,6 +487,85 @@ body { background: var(--bubble-assistant); } +.pending-action-card { + margin-top: 0.65rem; + padding: 0.7rem; + border-radius: 12px; + border: 1px solid rgba(14, 116, 144, 0.22); + background: rgba(236, 254, 255, 0.28); + color: rgba(51, 65, 85, 0.82); + font-size: 0.78rem; +} + +.pending-action-card--resolved { + opacity: 0.58; + pointer-events: none; +} + +.pending-action-title { + font-weight: 700; + color: rgba(15, 23, 42, 0.72); + margin-bottom: 0.35rem; +} + +.pending-action-params { + margin: 0 0 0.45rem; + padding: 0.5rem; + max-height: 160px; + overflow: auto; + border-radius: 10px; + background: rgba(255, 255, 255, 0.32); + color: rgba(30, 41, 59, 0.72); + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.72rem; + white-space: pre-wrap; +} + +.pending-action-editor { + width: 100%; + resize: vertical; + border: 1px solid rgba(148, 163, 184, 0.42); + border-radius: 10px; + padding: 0.45rem 0.5rem; + background: rgba(255, 255, 255, 0.36); + color: rgba(15, 23, 42, 0.78); + font: inherit; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.72rem; +} + +.pending-action-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin-top: 0.5rem; +} + +.pending-action-btn { + border: 1px solid rgba(148, 163, 184, 0.45); + border-radius: 999px; + padding: 0.32rem 0.62rem; + background: rgba(255, 255, 255, 0.42); + color: #334155; + cursor: pointer; + font: inherit; + font-size: 0.75rem; +} + +.pending-action-btn:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.7); +} + +.pending-action-btn--approve { + border-color: rgba(20, 184, 166, 0.45); + color: #0f766e; +} + +.pending-action-btn--reject { + border-color: rgba(248, 113, 113, 0.45); + color: #b91c1c; +} + /* 图片在气泡内留出呼吸空间 */ .msg-bubble img { display: block; @@ -1379,4 +1458,1346 @@ body { .chat-messages { -webkit-overflow-scrolling: touch; } -} \ No newline at end of file +} +/* ===== 人工交互 / 工具审批卡片 ===== */ +.pending-action-card { + margin-top: 0.75rem; + padding: 0.75rem; + border: 1px solid rgba(14, 116, 144, 0.22); + border-radius: 12px; + background: rgba(255, 255, 255, 0.42); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08); +} + +.pending-action-card--resolved { + opacity: 0.68; +} + +.pending-action-title { + font-weight: 700; + margin-bottom: 0.45rem; + color: #0f172a; +} + +.pending-action-prompt { + margin-bottom: 0.55rem; + color: #334155; + line-height: 1.55; + white-space: pre-wrap; +} + +.pending-action-params { + margin: 0 0 0.55rem; + padding: 0.55rem; + max-height: 220px; + overflow: auto; + border-radius: 10px; + background: rgba(15, 23, 42, 0.88); + color: #e2e8f0; + font-size: 0.78rem; +} + +.pending-action-editor { + width: 100%; + resize: vertical; + border: 1px solid rgba(148, 163, 184, 0.55); + border-radius: 10px; + padding: 0.55rem 0.65rem; + font: inherit; + color: var(--text); + background: rgba(255, 255, 255, 0.65); +} + +.pending-action-editor:focus { + outline: 2px solid rgba(14, 116, 144, 0.28); + outline-offset: 1px; +} + +.pending-action-options { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin: 0.2rem 0 0.55rem; +} + +.pending-action-option, +.pending-action-btn { + border: 1px solid rgba(148, 163, 184, 0.55); + border-radius: 999px; + padding: 0.38rem 0.72rem; + font: inherit; + cursor: pointer; + background: rgba(255, 255, 255, 0.55); + color: #0f172a; +} + +.pending-action-option:hover:not(:disabled), +.pending-action-btn:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.78); +} + +.pending-action-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + margin-top: 0.55rem; +} + +.pending-action-btn--approve { + background: linear-gradient(165deg, #14b8a6, #0e7490); + border-color: rgba(14, 116, 144, 0.7); + color: #fff; +} + +.pending-action-btn--reject { + background: rgba(254, 226, 226, 0.86); + border-color: rgba(220, 38, 38, 0.35); + color: #991b1b; +} + +.pending-action-btn--edit { + background: rgba(254, 243, 199, 0.86); + border-color: rgba(217, 119, 6, 0.35); + color: #92400e; +} + +.pending-action-card--information .pending-action-btn--approve, +.pending-action-card--selection .pending-action-btn--approve { + min-width: 2.35rem; + font-size: 1rem; + font-weight: 700; +} + +/* ===== 设置按钮 ===== */ +.settings-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.2rem; + height: 2.2rem; + padding: 0; + border-radius: 999px; + border: 1px solid rgba(148, 163, 184, 0.45); + background: rgba(255, 255, 255, 0.35); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + cursor: pointer; + font-size: 1.15rem; + color: #334155; + transition: transform 0.15s, background 0.15s, box-shadow 0.15s; + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); +} + +.settings-btn:hover { + background: rgba(255, 255, 255, 0.65); + box-shadow: 0 4px 14px rgba(15, 23, 42, 0.14); + transform: rotate(30deg); +} + +/* ===== 设置模态框 ===== */ +.settings-overlay { + position: fixed; + inset: 0; + z-index: 100; + display: flex; + align-items: center; + justify-content: center; + background: rgba(15, 23, 42, 0.55); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + animation: fadeIn 0.18s ease; +} + +.settings-overlay[hidden] { + display: none; +} + +.settings-dialog { + width: min(920px, 94vw); + max-height: 90vh; + max-height: 90dvh; + background: rgba(248, 250, 252, 0.96); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + border-radius: 18px; + border: 1px solid rgba(148, 163, 184, 0.35); + box-shadow: 0 24px 64px rgba(15, 23, 42, 0.22); + display: flex; + flex-direction: column; + overflow: hidden; + animation: slideUp 0.22s ease; +} + +.settings-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.85rem 1.15rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.25); +} + +.settings-header h2 { + margin: 0; + font-size: 1.15rem; + color: #0f172a; +} + +.settings-close-btn { + width: 2rem; + height: 2rem; + padding: 0; + border-radius: 999px; + border: 1px solid rgba(148, 163, 184, 0.4); + background: rgba(255, 255, 255, 0.5); + cursor: pointer; + font-size: 1rem; + color: #475569; + display: inline-flex; + align-items: center; + justify-content: center; + transition: background 0.15s; +} + +.settings-close-btn:hover { + background: rgba(239, 68, 68, 0.15); + color: #991b1b; +} + +.settings-tabs { + display: flex; + gap: 0; + border-bottom: 1px solid rgba(148, 163, 184, 0.25); + padding: 0 1.15rem; + background: rgba(241, 245, 249, 0.6); +} + +.settings-tab { + padding: 0.55rem 1.15rem; + border: none; + border-bottom: 2px solid transparent; + background: none; + font: inherit; + font-size: 0.9rem; + font-weight: 600; + color: #64748b; + cursor: pointer; + transition: color 0.15s, border-color 0.15s; +} + +.settings-tab:hover { + color: #334155; +} + +.settings-tab.active { + color: var(--accent); + border-bottom-color: var(--accent); +} + +.settings-body { + flex: 1; + overflow-y: auto; + padding: 1rem 1.15rem; + min-height: 0; +} + +.settings-panel[hidden] { + display: none; +} + +.settings-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.85rem; + flex-wrap: wrap; + gap: 0.5rem; +} + +.settings-path { + font-size: 0.72rem; + color: #94a3b8; + max-width: 320px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.settings-save-btn { + margin-top: 0.85rem; +} + +/* ===== Agent 配置卡片 ===== */ +.agent-config-card { + border: 1px solid rgba(148, 163, 184, 0.3); + border-radius: 14px; + padding: 1.05rem; + margin-bottom: 0.8rem; + background: rgba(255, 255, 255, 0.65); + box-shadow: 0 1px 4px rgba(15, 23, 42, 0.04); + transition: box-shadow 0.15s; +} + +.agent-config-card:hover { + box-shadow: 0 3px 12px rgba(15, 23, 42, 0.08); +} + +.agent-config-card + .agent-config-card { + margin-top: 0.8rem; +} + +.agent-card-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + padding-bottom: 0.55rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.15); +} + +.agent-card-header h4 { + margin: 0; + font-size: 0.95rem; + color: #0f172a; + font-weight: 700; +} + +.agent-card-remove { + width: 1.6rem; + height: 1.6rem; + padding: 0; + border-radius: 999px; + border: 1px solid rgba(220, 38, 38, 0.3); + background: rgba(254, 226, 226, 0.5); + cursor: pointer; + font-size: 0.8rem; + color: #991b1b; + display: inline-flex; + align-items: center; + justify-content: center; + transition: background 0.15s, color 0.15s; +} + +.agent-card-remove:hover { + background: rgba(220, 38, 38, 0.15); + color: #7f1d1d; +} + +.setting-field { + margin-bottom: 0.6rem; +} + +.setting-field label { + display: block; + font-size: 0.75rem; + font-weight: 700; + color: #475569; + margin-bottom: 0.25rem; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.setting-field input, +.setting-field select, +.setting-field textarea { + width: 100%; + border: 1px solid rgba(148, 163, 184, 0.45); + border-radius: 8px; + padding: 0.45rem 0.55rem; + font: inherit; + font-size: 0.85rem; + background: rgba(255, 255, 255, 0.8); + color: var(--text); + transition: border-color 0.15s, box-shadow 0.15s; + box-sizing: border-box; +} + +.setting-field input:focus, +.setting-field select:focus, +.setting-field textarea:focus { + border-color: var(--accent, #0e7490); + box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.1); + outline: none; +} + +.setting-field textarea { + resize: vertical; + min-height: 68px; + font-size: 0.82rem; + line-height: 1.45; +} + +.setting-row { + display: flex; + gap: 0.55rem; +} + +.setting-row .setting-field { + flex: 1; + min-width: 0; +} + +/* ===== 工具配置卡片 ===== */ +.tool-config-card { + border: 1px solid rgba(148, 163, 184, 0.25); + border-radius: 12px; + padding: 0.8rem; + margin-bottom: 0.6rem; + background: rgba(255, 255, 255, 0.55); + transition: opacity 0.2s; +} + +.tool-config-card.disabled { + opacity: 0.45; +} + +.tool-config-card h4 { + margin: 0 0 0.4rem; + font-size: 0.9rem; + display: inline-flex; + align-items: center; + gap: 0.35rem; + color: #1e293b; +} + +.tool-card-desc { + font-size: 0.76rem; + color: #64748b; + margin-bottom: 0.5rem; + line-height: 1.4; +} + +.tool-card-row { + display: flex; + align-items: center; + gap: 0.6rem; + flex-wrap: wrap; +} + +.auto-rules-section { + border-top: 1px dashed rgba(148, 163, 184, 0.3); + padding-top: 0.35rem; + margin-top: 0.35rem; +} + +.auto-rules-list { + display: flex; + flex-direction: column; + gap: 0.12rem; +} + +/* ===== 环境变量配置 ===== */ +.env-config-item { + display: flex; + align-items: center; + gap: 0.6rem; + margin-bottom: 0.5rem; + flex-wrap: wrap; +} + +.env-config-item label { + min-width: 150px; + font-size: 0.8rem; + font-weight: 600; + color: #334155; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.env-config-item input { + flex: 1; + min-width: 200px; + border: 1px solid rgba(148, 163, 184, 0.4); + border-radius: 8px; + padding: 0.4rem 0.5rem; + font: inherit; + font-size: 0.84rem; + background: rgba(255, 255, 255, 0.7); + transition: border-color 0.15s, box-shadow 0.15s; +} + +.env-config-item input:focus { + border-color: var(--accent, #0e7490); + box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.08); + outline: none; +} + +/* ===== 设置页脚 ===== */ +.settings-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.7rem 1.15rem; + border-top: 1px solid rgba(148, 163, 184, 0.2); + background: rgba(241, 245, 249, 0.55); +} + +.settings-status { + font-size: 0.8rem; + font-weight: 500; + color: #475569; +} + +.settings-status.success { + color: #16a34a; +} + +.settings-status.error { + color: #dc2626; +} + +/* ===== 设置面板内联按钮 ===== */ +.settings-btn-inline { + padding: 0.25rem 0.75rem; + font-size: 0.78rem; + border-radius: 6px; + border: 1px solid rgba(148, 163, 184, 0.3); + background: rgba(255, 255, 255, 0.7); + color: #475569; + cursor: pointer; + font-weight: 500; + transition: all 0.15s; +} + +.settings-btn-inline:hover { + background: #fff; + border-color: var(--accent, #0e7490); + color: var(--accent, #0e7490); +} + +.settings-btn-inline.danger { + color: #991b1b; +} + +.settings-btn-inline.danger:hover { + background: #fef2f2; + border-color: #dc2626; + color: #dc2626; +} + +/* ===== 设置面板标题区 ===== */ +.settings-section-title { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.65rem; +} + +.settings-section-title h4 { + margin: 0; + font-size: 0.92rem; + font-weight: 700; + color: #1e293b; +} + +.settings-section-title .btn { + padding: 0.3rem 0.8rem; + font-size: 0.78rem; +} + +/* ===== 模型编辑弹窗 ===== */ +.model-modal-overlay { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.4); + z-index: 2000; + align-items: center; + justify-content: center; +} +.model-modal-overlay.show { + display: flex; +} +.model-modal-box { + background: #fff; + border-radius: 12px; + padding: 1.5rem; + width: 480px; + max-width: 90vw; + box-shadow: 0 20px 60px rgba(0,0,0,0.2); + animation: fadeIn 0.2s ease; +} +.model-modal-box h3 { + margin: 0 0 1rem; + font-size: 1rem; + font-weight: 600; + color: #1e293b; +} +.model-modal-body .modal-field { + margin-bottom: 0.6rem; +} +.model-modal-body .modal-field label { + display: block; + font-size: 0.78rem; + font-weight: 600; + color: #475569; + margin-bottom: 0.15rem; +} +.model-modal-body .modal-field input { + width: 100%; + border: 1px solid rgba(148,163,184,0.5); + border-radius: 6px; + padding: 0.4rem 0.5rem; + font-size: 0.82rem; + box-sizing: border-box; + transition: border-color 0.15s; +} +.model-modal-body .modal-field input:focus { + outline: none; + border-color: var(--accent, #4f46e5); + box-shadow: 0 0 0 3px rgba(79,70,229,0.08); +} +.model-modal-actions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + margin-top: 1.2rem; +} + +/* ===== 模型表格 ===== */ +.model-table { + border: 1px solid rgba(148,163,184,0.3); + border-radius: 10px; + overflow: hidden; + background: rgba(255,255,255,0.65); +} + +.model-table-head { + display: flex; + background: rgba(241,245,249,0.8); + font-size: 0.73rem; + font-weight: 700; + color: #475569; + padding: 0.5rem 0.75rem; + text-transform: uppercase; + letter-spacing: 0.04em; + gap: 0.5rem; +} + +.model-table-row { + display: flex; + padding: 0.5rem 0.75rem; + border-top: 1px solid rgba(148,163,184,0.12); + align-items: center; + font-size: 0.8rem; + transition: background 0.12s; + gap: 0.5rem; +} + +.model-table-row:hover { + background: rgba(241,245,249,0.45); +} + +/* 模型表格列宽 */ +.model-table .col-name { flex: 1.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: #1e293b; } +.model-table .col-model { flex: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #475569; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.78rem; } +.model-table .col-url { flex: 2.5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #94a3b8; font-size: 0.72rem; } +.model-table .col-actions { flex: 0 0 90px; display: flex; gap: 0.35rem; justify-content: flex-end; align-items: center; } + +/* 表格内操作按钮 */ +.model-table .col-actions button { + padding: 0.25rem 0.55rem; + font-size: 0.72rem; + border: 1px solid rgba(148,163,184,0.25); + border-radius: 5px; + cursor: pointer; + background: rgba(255,255,255,0.8); + color: #475569; + font-weight: 500; + transition: all 0.15s; + white-space: nowrap; +} + +.model-table .col-actions button:hover { + background: #fff; + border-color: var(--accent, #0e7490); + color: var(--accent, #0e7490); +} + +.model-table .col-actions button.danger { + color: #991b1b; + border-color: rgba(220,38,38,0.2); + background: rgba(254,226,226,0.3); +} + +.model-table .col-actions button.danger:hover { + background: #fef2f2; + border-color: #dc2626; + color: #dc2626; +} + +/* ===== 开关控件 ===== */ +.toggle-switch { + position: relative; + display: inline-block; + width: 42px; + height: 22px; + flex-shrink: 0; +} + +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.toggle-slider { + position: absolute; + cursor: pointer; + top: 0; left: 0; right: 0; bottom: 0; + background: #cbd5e1; + border-radius: 22px; + transition: background 0.2s; +} + +.toggle-slider::before { + content: ""; + position: absolute; + height: 16px; + width: 16px; + left: 3px; + bottom: 3px; + background: #fff; + border-radius: 50%; + transition: transform 0.2s; + box-shadow: 0 1px 3px rgba(0,0,0,0.15); +} + +.toggle-switch input:checked + .toggle-slider { + background: var(--accent, #0e7490); +} + +.toggle-switch input:checked + .toggle-slider::before { + transform: translateX(20px); +} + +.toggle-row { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.toggle-row .toggle-label { + font-size: 0.82rem; + font-weight: 600; + color: #475569; + cursor: pointer; + user-select: none; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(24px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ===== 图谱切换按钮 ===== */ +.graph-toggle-btn { + margin-left: 0.15rem; +} + +.graph-toggle-btn.graph-active { + background: rgba(14, 116, 144, 0.18); + border-color: rgba(14, 116, 144, 0.5); + color: #0e7490; +} + +/* ===== 主内容区(chat + graph 并排) ===== */ +.main-content-wrap { + flex: 1; + min-height: 0; + display: flex; + gap: 0; + overflow: hidden; +} + +/* 图谱开启时聊天面板缩窄,圆角只保留左侧 */ +.main.has-graph .chat-shell { + flex: 1 1 58%; + min-width: 320px; + border-radius: var(--radius) 0 0 var(--radius); +} + +/* ===== 图谱侧面板 ===== */ +.graph-panel { + display: flex; + flex-direction: column; + width: 42%; + min-width: 340px; + max-width: 520px; + border-left: 1px solid rgba(148, 163, 184, 0.35); + border-radius: 0 var(--radius) var(--radius) 0; + background: rgba(255, 255, 255, 0.25); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + overflow: hidden; + box-shadow: -4px 0 24px rgba(15, 23, 42, 0.06); + transition: width 0.3s ease, min-width 0.3s ease, max-width 0.3s ease, opacity 0.25s ease; +} + +.graph-panel[hidden] { + display: none; +} + +.graph-toolbar { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 0.4rem; + padding: 0.45rem 0.65rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.22); + background: rgba(255, 255, 255, 0.15); +} + +.graph-toolbar-title { + font-size: 0.82rem; + font-weight: 700; + color: #1e293b; + margin-right: 0.25rem; +} + +.graph-btn { + font-size: 0.7rem; + padding: 0.2rem 0.45rem; + border-radius: 6px; +} + +.graph-legend { + margin-left: auto; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.65rem; + color: #64748b; +} + +.graph-close-btn { + width: 1.65rem; + height: 1.65rem; + padding: 0; + border: 1px solid rgba(148, 163, 184, 0.4); + border-radius: 6px; + background: rgba(255, 255, 255, 0.4); + color: #64748b; + font-size: 0.75rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.graph-close-btn:hover { + background: rgba(255, 255, 255, 0.65); + color: #1e293b; +} + +.legend-dot { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + margin-right: 0.1rem; +} + +.legend-running { + background: #f59e0b; + box-shadow: 0 0 6px rgba(245, 158, 11, 0.5); + animation: legendPulse 1.2s ease-in-out infinite; +} + +.legend-done { + background: #22c55e; +} + +.legend-error { + background: #ef4444; +} + +.legend-idle { + background: #94a3b8; +} + +@keyframes legendPulse { + 0%, 100% { opacity: 0.7; box-shadow: 0 0 4px rgba(245, 158, 11, 0.3); } + 50% { opacity: 1; box-shadow: 0 0 10px rgba(245, 158, 11, 0.7); } +} + +/* 图谱画布 */ +.graph-canvas-wrap { + flex: 1; + min-height: 0; + overflow: hidden; + position: relative; + cursor: grab; + background: + radial-gradient(circle, rgba(148, 163, 184, 0.06) 1px, transparent 1px); + background-size: 24px 24px; +} + +.graph-canvas-wrap:active { + cursor: grabbing; +} + +.graph-canvas-wrap.panning { + cursor: grabbing; +} + +.graph-canvas { + width: 100%; + height: 100%; + display: block; + user-select: none; +} + +/* Agent 节点 */ +.graph-node { + cursor: pointer; + transition: transform 0.2s ease; +} + +.graph-node:hover .graph-node-rect { + filter: brightness(1.05); +} + +.graph-node-rect { + fill: rgba(255, 255, 255, 0.75); + stroke: rgba(148, 163, 184, 0.5); + stroke-width: 1.5; + rx: 16; + transition: fill 0.35s ease, stroke 0.35s ease, filter 0.35s ease; +} + +/* 状态颜色 */ +.graph-node.status-running .graph-node-rect { + fill: rgba(255, 200, 120, 0.45); + stroke: #f59e0b; + stroke-width: 2.5; + filter: drop-shadow(0 0 8px rgba(245, 158, 11, 0.35)); +} + +.graph-node.status-done .graph-node-rect { + fill: rgba(187, 247, 208, 0.45); + stroke: #22c55e; + stroke-width: 2; +} + +.graph-node.status-error .graph-node-rect { + fill: rgba(254, 202, 202, 0.5); + stroke: #ef4444; + stroke-width: 2.5; + filter: drop-shadow(0 0 6px rgba(239, 68, 68, 0.3)); +} + +.graph-node.status-idle .graph-node-rect { + fill: rgba(255, 255, 255, 0.5); + stroke: rgba(148, 163, 184, 0.4); +} + +/* Agent 节点内部文字 */ +.graph-node-icon { + font-size: 1.4rem; + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +} + +.graph-node-label { + font-size: 0.75rem; + font-weight: 600; + fill: #1e293b; + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +} + +.graph-node-role { + font-size: 0.6rem; + fill: #94a3b8; + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +} + +.graph-node-badge { + font-size: 0.6rem; + font-weight: 700; + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +} + +/* 连线 */ +.graph-edge { + transition: stroke 0.4s ease, stroke-width 0.4s ease, opacity 0.4s ease; +} + +.graph-edge.highlighted { + stroke: #f59e0b; + stroke-width: 2.5; + opacity: 1; + stroke-dasharray: none; +} + +.graph-edge.active { + stroke: #14b8a6; + stroke-width: 2; + opacity: 1; + stroke-dasharray: 6 3; + animation: edgeFlow 0.6s linear infinite; +} + +.graph-edge.normal { + stroke: rgba(148, 163, 184, 0.4); + stroke-width: 1.5; + opacity: 0.7; +} + +@keyframes edgeFlow { + from { stroke-dashoffset: 18; } + to { stroke-dashoffset: 0; } +} + +/* 工具调用面板 */ +/* 工具调用卡片 */ +.toolcall-card { + padding: 0.4rem 0.55rem; + border-radius: 10px; + border: 1px solid rgba(148, 163, 184, 0.3); + background: rgba(255, 255, 255, 0.5); + font-size: 0.72rem; +} + +.toolcall-card-head { + display: flex; + align-items: center; + gap: 0.4rem; + margin-bottom: 0.2rem; +} + +.toolcall-card-name { + font-weight: 700; + color: #1e293b; + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; +} + +.toolcall-card-status { + font-size: 0.62rem; + padding: 0.1rem 0.35rem; + border-radius: 999px; + font-weight: 600; +} + +.toolcall-card-status.success { + background: rgba(34, 197, 94, 0.15); + color: #15803d; +} + +.toolcall-card-status.error { + background: rgba(239, 68, 68, 0.15); + color: #b91c1c; +} + +.toolcall-card-status.pending { + background: rgba(245, 158, 11, 0.15); + color: #b45309; +} + +.toolcall-card-args { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.65rem; + color: #64748b; + white-space: pre-wrap; +} + +.toolcall-card-result { + margin-top: 0.25rem; + padding: 0.3rem 0.4rem; + border-radius: 6px; + background: rgba(241, 245, 249, 0.7); + font-size: 0.65rem; + color: #475569; + max-height: 48px; + overflow: hidden; + text-overflow: ellipsis; + white-space: pre-wrap; + word-break: break-all; +} + +/* 消息传递动画脉冲 */ +@keyframes msgPulse { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 1; } +} + +.graph-edge.active { + animation: edgeFlow 0.6s linear infinite, msgPulse 0.8s ease-in-out infinite; +} + +/* ===== 工具调用气泡(chat 视图) ===== */ +.tool-call-bubble-wrap { + margin-top: 0.5rem; + border: 1px solid rgba(148, 163, 184, 0.3); + border-radius: 10px; + overflow: hidden; + background: rgba(255, 255, 255, 0.18); + transition: background 0.3s; +} + +.tool-call-toggle { + display: block; + width: 100%; + padding: 0.35rem 0.65rem; + border: none; + background: rgba(255, 255, 255, 0.3); + font-size: 0.72rem; + font-weight: 600; + color: #475569; + cursor: pointer; + text-align: left; + transition: background 0.2s; + font-family: inherit; +} + +.tool-call-toggle:hover { + background: rgba(255, 255, 255, 0.5); +} + +.tool-call-collapsed { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; + padding: 0.4rem 0.65rem; +} + +.tool-call-bubble-wrap--expanded .tool-call-collapsed { + display: none; +} + +.tool-call-chip { + display: inline-block; + padding: 0.15rem 0.45rem; + border-radius: 8px; + background: rgba(79, 70, 229, 0.1); + font-size: 0.68rem; + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + color: #4f46e5; + font-weight: 500; + transition: opacity 0.5s; +} + +.tool-call-more { + display: inline-block; + padding: 0.15rem 0.45rem; + border-radius: 8px; + background: rgba(148, 163, 184, 0.15); + font-size: 0.65rem; + color: #94a3b8; + font-weight: 600; +} + +.tool-call-expanded { + display: none; + padding: 0.4rem 0.65rem 0.65rem; + max-height: 240px; + overflow-y: auto; +} + +.tool-call-bubble-wrap--expanded .tool-call-expanded { + display: block; +} + +.tool-call-detail { + margin-bottom: 0.5rem; + padding: 0.4rem 0.55rem; + border-radius: 8px; + background: rgba(241, 245, 249, 0.6); + border: 1px solid rgba(148, 163, 184, 0.2); +} + +.tool-call-detail-name { + font-weight: 700; + font-size: 0.72rem; + color: #1e293b; + margin-bottom: 0.25rem; +} + +.tool-call-detail-text { + margin: 0.2rem 0; + padding: 0.3rem 0.45rem; + border-radius: 6px; + background: rgba(148, 163, 184, 0.12); + font-size: 0.65rem; + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + color: #64748b; + white-space: pre-wrap; + word-break: break-all; + max-height: 80px; + overflow-y: auto; +} + +.tool-call-detail-result { + color: #334155; + background: rgba(148, 163, 184, 0.08); +} + +.tool-call-detail-img { + border-radius: 6px; + margin: 0.2rem; + max-width: 120px; + max-height: 90px; + object-fit: cover; + opacity: 0.6; + transition: opacity 0.3s; +} + +.tool-call-detail-img:hover { + opacity: 0.9; +} + +/* 淡出动画 */ +.tool-call-block--fading { + transition: opacity 0.8s ease-out; + opacity: 0 !important; + pointer-events: none; +} + +/* ===== 实时工具调用(thinking 气泡内) ===== */ +.tool-call-live-wrap { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; + margin-top: 0.5rem; + padding: 0.4rem 0.5rem; + border-top: 1px solid rgba(148, 163, 184, 0.2); +} + +.tool-call-live-chip { + display: inline-flex; + align-items: center; + gap: 0.2rem; + padding: 0.15rem 0.5rem; + border-radius: 8px; + background: rgba(99, 102, 241, 0.08); + font-size: 0.68rem; + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + color: #6366f1; + font-weight: 500; + transition: all 0.3s; +} + +.tool-call-live-chip--running { + background: rgba(245, 158, 11, 0.1); + color: #d97706; + animation: livePulse 1.2s ease-in-out infinite; +} + +@keyframes livePulse { + 0%, 100% { opacity: 0.7; } + 50% { opacity: 1; } +} + +/* 任务完成提示 */ +.tool-call-done-badge { + display: inline-block; + padding: 0.2rem 0.5rem; + border-radius: 999px; + background: rgba(34, 197, 94, 0.15); + color: #15803d; + font-size: 0.68rem; + font-weight: 600; + margin-bottom: 0.4rem; +} + +/* ===== Graph 节点工具调用缩略 ===== */ +.graph-node-tool-chip { + font-size: 0.55rem; + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + fill: #6366f1; + font-weight: 600; + text-anchor: start; +} + +/* ===== Agent 详情弹窗 ===== */ +.agent-detail-overlay { + position: fixed; + inset: 0; + z-index: 999; + background: rgba(15, 23, 42, 0.55); + display: flex; + align-items: center; + justify-content: center; +} + +.agent-detail-overlay[hidden] { + display: none; +} + +.agent-detail-dialog { + background: rgba(255, 255, 255, 0.92); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-radius: 16px; + width: min(520px, 92vw); + max-height: 80vh; + overflow: hidden; + display: flex; + flex-direction: column; + box-shadow: 0 20px 60px rgba(0,0,0,0.2); +} + +.agent-detail-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1.25rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.25); +} + +.agent-detail-header h2 { + margin: 0; + font-size: 1.1rem; + font-weight: 700; + color: #1e293b; +} + +.agent-detail-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: #64748b; + line-height: 1; + padding: 0.2rem; +} + +.agent-detail-body { + padding: 1rem 1.25rem; + overflow-y: auto; + flex: 1; +} + +.agent-detail-section { + margin-bottom: 1.2rem; +} + +.agent-detail-section h3 { + margin: 0 0 0.5rem; + font-size: 0.85rem; + font-weight: 700; + color: #475569; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.agent-detail-section code { + padding: 0.1rem 0.3rem; + border-radius: 4px; + background: rgba(79, 70, 229, 0.08); + font-size: 0.75rem; + color: #4f46e5; +} + + diff --git a/src/web/app.js b/src/web/app.js index a17e553..346c69d 100644 --- a/src/web/app.js +++ b/src/web/app.js @@ -23,6 +23,7 @@ const drawerBackdrop = $("drawerBackdrop"); const bgImageLayer = $("bgImageLayer"); const outputModeSelect = $("outputModeSelect"); + const agentModeSelect = $("agentModeSelect"); const dofRange = $("dofRange"); const dofValue = $("dofValue"); const vignetteRange = $("vignetteRange"); @@ -31,7 +32,6 @@ const STORAGE_DOF = "minor_web_bg_dof"; const STORAGE_VIG = "minor_web_bg_vignette"; const STORAGE_HEADER_COLLAPSED = "minor_web_header_collapsed"; - /** @type {string} */ let sessionId = ""; /** @type {string[]} */ @@ -43,6 +43,7 @@ let recorderStream = null; let recordedChunks = []; let recordingMimeType = ""; + let sidebarCollapsed = false; // 自动语音输入相关变量 let autoInputEnabled = false; let audioContext = null; @@ -60,7 +61,6 @@ /** 最近一次 renderMessages 的 JSON 可序列化快照 */ let lastRenderedMessages = []; let activeAudioController = null; - async function api(path, opts = {}) { const r = await fetch(path, { ...opts, @@ -720,6 +720,10 @@ }); } + if (msg.meta && msg.meta.pending_action) { + bubble.appendChild(renderPendingActionCard(msg.meta.pending_action)); + } + scrollChatToBottom(); } @@ -748,6 +752,16 @@ return wrap; } + function showPendingActionInThinking(typingEl, msg) { + if (!typingEl || !msg || !msg.meta || !msg.meta.pending_action) return false; + const bubble = typingEl.querySelector(".msg-bubble"); + if (!bubble) return false; + bubble.appendChild(renderPendingActionCard(msg.meta.pending_action)); + lastRenderedMessages = lastRenderedMessages.concat([JSON.parse(JSON.stringify(msg))]); + scrollChatToBottom(); + return true; + } + function renderAudioBubble(part, options = {}) { const { autoplay = false, @@ -952,6 +966,336 @@ return div; } + function renderPendingActionCard(pending) { + const card = document.createElement("div"); + const resolved = Boolean(pending && pending.resolved); + const pendingType = pending && pending.type ? String(pending.type) : "tool_call"; + const interactionType = pending && pending.interaction_type ? String(pending.interaction_type) : ""; + card.className = "pending-action-card" + (resolved ? " pending-action-card--resolved" : "") + ` pending-action-card--${pendingType}` + (interactionType ? ` pending-action-card--${interactionType}` : ""); + const title = document.createElement("div"); + title.className = "pending-action-title"; + title.textContent = pending.title || "待确认执行"; + card.appendChild(title); + + if (pending.prompt) { + const prompt = document.createElement("div"); + prompt.className = "pending-action-prompt"; + prompt.textContent = pending.prompt; + card.appendChild(prompt); + } + + const textarea = document.createElement("textarea"); + textarea.className = "pending-action-editor"; + textarea.value = pending.instruction || ""; + textarea.disabled = resolved; + + const isTextChoiceCard = pendingType === "human_interaction"; + if (isTextChoiceCard) { + if (interactionType === "selection" && Array.isArray(pending.options) && pending.options.length > 0 && pendingType === "human_interaction") { + const optionWrap = document.createElement("div"); + optionWrap.className = "pending-action-options"; + pending.options.forEach((option, idx) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = "pending-action-option"; + btn.textContent = String(option); + btn.disabled = resolved; + btn.addEventListener("click", () => { + textarea.value = String(option); + textarea.focus(); + }); + if (idx === 0 && !textarea.value) textarea.value = String(option); + optionWrap.appendChild(btn); + }); + card.appendChild(optionWrap); + } + textarea.placeholder = interactionType === "information" ? "在这里补充信息..." : "可直接编辑选择内容..."; + textarea.rows = Math.min(8, Math.max(2, textarea.value.split("\n").length)); + card.appendChild(textarea); + + const actions = document.createElement("div"); + actions.className = "pending-action-buttons"; + const makeActionBtn = (label, decision, className = decision) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = `pending-action-btn pending-action-btn--${className}`; + btn.textContent = label; + btn.disabled = resolved; + btn.addEventListener("click", () => handleHumanAction(pending.id, decision, textarea.value, card).catch((e) => alert(e.message || String(e)))); + return btn; + }; + + if (interactionType === "approval") { + textarea.readOnly = true; + actions.appendChild(makeActionBtn("同意", "approve")); + actions.appendChild(makeActionBtn("拒绝", "reject")); + } else { + const okBtn = makeActionBtn("✓", interactionType === "selection" ? "edit" : "approve", "approve"); + okBtn.title = interactionType === "information" ? "提交补充信息" : "确认"; + actions.appendChild(okBtn); + } + card.appendChild(actions); + return card; + } + + textarea.rows = Math.min(10, Math.max(4, textarea.value.split("\n").length)); + textarea.readOnly = true; + card.appendChild(textarea); + + const actions = document.createElement("div"); + actions.className = "pending-action-buttons"; + + const makeBtn = (label, decision) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.className = `pending-action-btn pending-action-btn--${decision}`; + btn.textContent = label; + btn.disabled = resolved; + btn.addEventListener("click", () => handleHumanAction(pending.id, decision, textarea.value, card).catch((e) => alert(e.message || String(e)))); + return btn; + }; + actions.appendChild(makeBtn("同意", "approve")); + actions.appendChild(makeBtn("拒绝", "reject")); + card.appendChild(actions); + return card; + } + + async function handleHumanAction(approvalId, decision, instruction, card) { + if (!approvalId || sending) return; + sending = true; + sendBtn.disabled = true; + if (card) card.classList.add("pending-action-card--resolved"); + const fd = new FormData(); + fd.append("session_id", sessionId); + fd.append("approval_id", approvalId); + fd.append("decision", decision); + fd.append("instruction", instruction || ""); + fd.append("output_type", outputModeSelect ? outputModeSelect.value : "text"); + let typingEl = appendThinkingIndicator(); + try { + const done = await api("/api/human-action", { method: "POST", body: fd }); + if (typingEl && typingEl.parentNode) typingEl.remove(); + typingEl = null; + sessionId = done.sessionId; + sessions = done.sessions || []; + renderSessions(); + const messages = done.messages || []; + const last = messages[messages.length - 1]; + if (last && last.role === "assistant") { + renderMessages(messages.slice(0, -1)); + await streamAssistantMessage(last); + } else { + renderMessages(messages); + } + } finally { + if (typingEl && typingEl.parentNode) typingEl.remove(); + sending = false; + sendBtn.disabled = false; + } + } + + // ===== 工具调用展示(chat 视图) ===== + let toolCallDataCache = { recent: [], detail: [], complete: false }; + let toolCallTimer = null; + + async function fetchToolCallData() { + if (!sessionId) return; + try { + const [recentRes, detailRes, pendingRes] = await Promise.all([ + api(`/api/tool-calls/${encodeURIComponent(sessionId)}/recent`), + api(`/api/tool-calls/${encodeURIComponent(sessionId)}/detail`), + api(`/api/tool-calls/${encodeURIComponent(sessionId)}/has-pending`), + ]); + toolCallDataCache = { + recent: recentRes.tool_calls || [], + detail: detailRes.details || [], + complete: pendingRes.is_complete, + }; + } catch { + /* ignore */ + } + } + + function startToolCallPolling() { + fetchToolCallData(); + toolCallTimer = setInterval(fetchToolCallData, 2000); + } + + function stopToolCallPolling(keepDisplay = false) { + if (toolCallTimer) { + clearInterval(toolCallTimer); + toolCallTimer = null; + } + if (!keepDisplay) { + // 清除过期工具调用显示(渐变消失) + document.querySelectorAll(".tool-call-block--fading").forEach((el) => { + el.addEventListener("transitionend", () => el.remove()); + el.style.opacity = "0"; + }); + } + } + + function renderToolCallBlock(call, index, total, isComplete) { + const block = document.createElement("div"); + block.className = "tool-call-block" + (isComplete ? " tool-call-block--fading" : ""); + + const header = document.createElement("div"); + header.className = "tool-call-header"; + const name = document.createElement("span"); + name.className = "tool-call-name"; + name.textContent = `🔧 ${escapeHtml(call.name || "")}`; + const args = document.createElement("span"); + args.className = "tool-call-args"; + args.textContent = call.args_summary || ""; + const status = document.createElement("span"); + status.className = "tool-call-status"; + status.textContent = call.status === "done" ? "✓" : "…"; + header.appendChild(name); + header.appendChild(args); + header.appendChild(status); + block.appendChild(header); + + return block; + } + + function renderToolCallDetail(call) { + const detail = document.createElement("div"); + detail.className = "tool-call-detail"; + + const name = document.createElement("div"); + name.className = "tool-call-detail-name"; + name.textContent = `🔧 ${escapeHtml(call.name || "unknown")}`; + detail.appendChild(name); + + if (call.args && Object.keys(call.args).length > 0) { + const argsEl = document.createElement("pre"); + argsEl.className = "tool-call-detail-text"; + argsEl.textContent = JSON.stringify(call.args, null, 2); + detail.appendChild(argsEl); + } + + if (call.result_text) { + const resultEl = document.createElement("pre"); + resultEl.className = "tool-call-detail-text tool-call-detail-result"; + resultEl.textContent = call.result_text; + detail.appendChild(resultEl); + } + + if (call.result_images && call.result_images.length > 0) { + call.result_images.forEach((imgUrl) => { + const img = document.createElement("img"); + img.src = imgUrl; + img.className = "tool-call-detail-img"; + img.alt = "工具返回图片"; + img.style.maxWidth = "160px"; + img.style.opacity = "0.5"; + detail.appendChild(img); + }); + } + + return detail; + } + + function injectLiveToolCalls(typingEl, liveCalls) { + if (!typingEl || !liveCalls.length) return; + // 查找或创建工具调用容器 + let liveWrap = typingEl.querySelector(".tool-call-live-wrap"); + if (!liveWrap) { + liveWrap = document.createElement("div"); + liveWrap.className = "tool-call-live-wrap"; + typingEl.appendChild(liveWrap); + } + // 更新显示 + liveWrap.innerHTML = ""; + liveCalls.forEach((call, i) => { + const chip = document.createElement("span"); + chip.className = "tool-call-live-chip"; + const statusIcon = call.status === "running" ? "⏳" : "✓"; + chip.textContent = `${statusIcon} ${escapeHtml(call.name || "")}`; + chip.style.opacity = Math.max(0.3, 1 - i * 0.15); + if (call.status === "running") chip.classList.add("tool-call-live-chip--running"); + liveWrap.appendChild(chip); + }); + scrollChatToBottom(); + } + + function buildToolCallBubble() { + const data = toolCallDataCache; + if (!data.recent.length && !data.detail.length) return null; + + const total = data.recent.length; + const visibleCount = Math.min(2, total); + const moreCount = total - visibleCount; + + const wrap = document.createElement("div"); + wrap.className = "tool-call-bubble-wrap"; + + // 折叠按钮 + const toggleBtn = document.createElement("button"); + toggleBtn.type = "button"; + toggleBtn.className = "tool-call-toggle"; + toggleBtn.textContent = `🔧 工具调用 (${total}) ▸`; + toggleBtn.addEventListener("click", () => { + const expanded = wrap.classList.toggle("tool-call-bubble-wrap--expanded"); + toggleBtn.textContent = `🔧 工具调用 (${total}) ${expanded ? "▾" : "▸"}`; + }); + + // 折叠态:只显示最近2个工具名称,渐变透明度动态消失 + const collapsed = document.createElement("div"); + collapsed.className = "tool-call-collapsed"; + data.recent.slice(-visibleCount).forEach((call, i) => { + const chip = document.createElement("span"); + chip.className = "tool-call-chip"; + chip.textContent = call.name || ""; + // 越旧的越透明 + chip.style.opacity = Math.max(0.25, 1 - (visibleCount - 1 - i) * 0.35); + collapsed.appendChild(chip); + }); + if (moreCount > 0) { + const more = document.createElement("span"); + more.className = "tool-call-more"; + more.textContent = `+${moreCount}`; + more.style.cursor = "pointer"; + more.addEventListener("click", () => { + wrap.classList.add("tool-call-bubble-wrap--expanded"); + toggleBtn.textContent = `🔧 工具调用 (${total}) ▾`; + }); + collapsed.appendChild(more); + } + + // 展开态:显示所有详情 + const expanded = document.createElement("div"); + expanded.className = "tool-call-expanded"; + data.detail.forEach((call) => { + expanded.appendChild(renderToolCallDetail(call)); + }); + + wrap.appendChild(toggleBtn); + wrap.appendChild(collapsed); + wrap.appendChild(expanded); + + return wrap; + } + + function injectToolCallDisplay(typingEl) { + if (!typingEl) return; + if (!toolCallDataCache.recent.length && !toolCallDataCache.detail.length) return; + + const existingBlock = typingEl.querySelector(".tool-call-bubble-wrap"); + if (existingBlock) { + // 更新现有显示 + const newBlock = buildToolCallBubble(); + if (newBlock) existingBlock.replaceWith(newBlock); + return; + } + + const block = buildToolCallBubble(); + if (block) { + typingEl.appendChild(block); + scrollChatToBottom(); + } + } + function renderMessage(msg, allowAutoplay = false) { const wrap = document.createElement("div"); wrap.className = "msg " + (msg.role === "user" ? "user" : "assistant") + (isAudioOnlyAssistantMessage(msg) ? " msg--audio-only" : ""); @@ -991,6 +1335,9 @@ } else { bubble.textContent = c == null ? "" : String(c); } + if (msg.meta && msg.meta.pending_action) { + bubble.appendChild(renderPendingActionCard(msg.meta.pending_action)); + } wrap.appendChild(bubble); return wrap; } @@ -1130,12 +1477,33 @@ revokeBlobUrlStack(blobUrls); typingEl = appendThinkingIndicator(); + // 清空上轮工具调用缓存(新 turn) + toolCallDataCache = { recent: [], detail: [], complete: false }; + + // 启动实时工具调用轮询 + let livePollTimer = null; + let livePollActive = true; + const pollLiveToolCalls = async () => { + if (!livePollActive) return; + try { + const liveData = await api(`/api/tool-calls/${encodeURIComponent(sessionId)}/live`); + if (liveData.tool_calls && liveData.tool_calls.length > 0 && typingEl) { + injectLiveToolCalls(typingEl, liveData.tool_calls); + } + } catch { /* ignore */ } + if (livePollActive) livePollTimer = setTimeout(pollLiveToolCalls, 800); + }; + pollLiveToolCalls(); + const fd2 = new FormData(); fd2.append("session_id", sessionId); fd2.append("output_type", outputModeSelect ? outputModeSelect.value : "text"); + fd2.append("agent_mode", agentModeSelect ? agentModeSelect.value : "agent"); const done = await api("/api/chat/complete", { method: "POST", body: fd2 }); - if (typingEl && typingEl.parentNode) typingEl.remove(); - typingEl = null; + + // 停止轮询 + livePollActive = false; + if (livePollTimer) clearTimeout(livePollTimer); sessionId = done.sessionId; sessions = done.sessions || []; @@ -1143,14 +1511,28 @@ const messages = done.messages || []; const last = messages[messages.length - 1]; - if (last && last.role === "assistant") { - const head = messages.slice(0, -1); - renderMessages(head); - revokeBlobUrlStack(blobUrls); - await streamAssistantMessage(last); + if (last && last.role === "assistant" && last.meta && last.meta.pending_action && typingEl) { + showPendingActionInThinking(typingEl, last); + typingEl = null; } else { - renderMessages(messages); - revokeBlobUrlStack(blobUrls); + if (typingEl && typingEl.parentNode) typingEl.remove(); + typingEl = null; + if (last && last.role === "assistant") { + const head = messages.slice(0, -1); + renderMessages(head); + revokeBlobUrlStack(blobUrls); + await streamAssistantMessage(last); + // 获取工具调用数据并注入到最后一条助手消息 + await fetchToolCallData(); + const lastBubble = chatMessages.querySelector(".msg.assistant:last-child .msg-bubble"); + if (lastBubble && toolCallDataCache.recent.length) { + const toolBlock = buildToolCallBubble(); + if (toolBlock) lastBubble.appendChild(toolBlock); + } + } else { + renderMessages(messages); + revokeBlobUrlStack(blobUrls); + } } } catch (e) { revokeBlobUrlStack(blobUrls); @@ -1347,6 +1729,1115 @@ }); loadHeaderPref(); + // ===== 系统配置模态框 ===== + const settingsBtn = $("settingsBtn"); + const settingsOverlay = $("settingsOverlay"); + const settingsCloseBtn = $("settingsCloseBtn"); + const settingsCancelBtn = $("settingsCancelBtn"); + const settingsStatus = $("settingsStatus"); + const agentsListEl = $("agentsList"); + const toolsListEl = $("toolsList"); + const envListEl = $("envList"); + const agentConfigPath = $("agentConfigPath"); + const toolConfigPath = $("toolConfigPath"); + const envConfigPath = $("envConfigPath"); + const addAgentBtn = $("addAgentBtn"); + + let cachedAgentConfigs = []; + let cachedToolConfigs = []; + let cachedEnvConfig = {}; + let cachedModels = []; + let registeredToolNames = []; + + // 模型编辑状态 + let editingModelIdx = -1; + + function setStatus(msg, type) { + if (!settingsStatus) return; + settingsStatus.textContent = msg; + settingsStatus.className = "settings-status" + (type ? " " + type : ""); + if (type === "success" || type === "error") { + setTimeout(() => { if (settingsStatus.textContent === msg) setStatus(""); }, 3000); + } + } + + function openSettings() { + settingsOverlay.hidden = false; + loadAllConfigs().catch((e) => setStatus("加载配置失败: " + e.message, "error")); + } + + function closeSettings() { + settingsOverlay.hidden = true; + setStatus(""); + } + + // Tab 切换 + document.querySelectorAll(".settings-tab").forEach((tab) => { + tab.addEventListener("click", () => { + const target = tab.getAttribute("data-tab"); + document.querySelectorAll(".settings-tab").forEach((t) => t.classList.remove("active")); + tab.classList.add("active"); + document.querySelectorAll(".settings-panel").forEach((p) => p.hidden = p.getAttribute("data-panel") !== target); + // 切换时重新渲染对应面板 + if (target === "agent") renderAgentConfigs(); + else if (target === "tool") renderToolConfigs(); + else if (target === "env") renderEnvConfig(); + }); + }); + + // ===== 加载所有配置 ===== + async function loadAllConfigs() { + const [agentRes, toolRes, envRes] = await Promise.all([ + api("/api/config/agents"), + api("/api/config/tools"), + api("/api/config/env"), + ]); + cachedAgentConfigs = agentRes.agents || []; + cachedToolConfigs = toolRes.tools || []; + cachedEnvConfig = envRes.env || {}; + cachedModels = envRes.models || []; + registeredToolNames = toolRes.registered_tool_names || []; + window._toolParameters = toolRes.tool_parameters || {}; + window._forcedPermissions = toolRes.forced_permissions || {}; + if (agentConfigPath) agentConfigPath.textContent = agentRes.config_path || ""; + if (toolConfigPath) toolConfigPath.textContent = toolRes.config_path || ""; + if (envConfigPath) envConfigPath.textContent = envRes.config_path || ""; + renderAgentConfigs(); + renderToolConfigs(); + renderEnvConfig(); + } + + // ===== Agent 配置渲染 ===== + function renderAgentConfigs() { + agentsListEl.innerHTML = ""; + cachedAgentConfigs.forEach((agent, idx) => { + agentsListEl.appendChild(buildAgentCard(agent, idx)); + }); + } + + function getModelLabel(modelId) { + if (!modelId) return "— 未选择 —"; + const m = cachedModels.find((mod) => mod.id === modelId); + return m ? `${m.name} (${m.model})` : modelId; + } + + function buildAgentCard(agent, idx) { + const card = document.createElement("div"); + card.className = "agent-config-card"; + card.dataset.index = idx; + + // 头部 + const header = document.createElement("div"); + header.className = "agent-card-header"; + const h4 = document.createElement("h4"); + h4.textContent = `Agent: ${agent.name || "(未命名)"}`; + const removeBtn = document.createElement("button"); + removeBtn.className = "agent-card-remove"; + removeBtn.textContent = "✕"; + removeBtn.title = "删除此 Agent"; + removeBtn.addEventListener("click", () => { + const wasMain = agent.role === "main"; + cachedAgentConfigs.splice(idx, 1); + if (wasMain && cachedAgentConfigs.length > 0) { + // 如果删的是主 agent,第一个自动升为主 agent + cachedAgentConfigs[0].role = "main"; + } + renderAgentConfigs(); + }); + header.appendChild(h4); + header.appendChild(removeBtn); + + // Name + const nameField = makeField("名称", "text", agent.name || "", (v) => { agent.name = v; }); + // max_iterations + const maxIterField = makeField("最大迭代次数", "number", agent.max_iterations || 200, (v) => { agent.max_iterations = parseInt(v) || 200; }); + // system_prompt + const spField = makeField("系统提示词 (system_prompt)", "textarea", agent.system_prompt || "", (v) => { agent.system_prompt = v; }); + + // LLM 模型选择 + const llmField = document.createElement("div"); + llmField.className = "setting-field"; + const llmLabel = document.createElement("label"); + llmLabel.textContent = "LLM 模型"; + const llmRow = document.createElement("div"); + llmRow.style.cssText = "display:flex;gap:0.4rem;align-items:center;"; + const llmSelect = document.createElement("select"); + llmSelect.style.cssText = "flex:1;"; + const emptyOpt = document.createElement("option"); + emptyOpt.value = ""; + emptyOpt.textContent = "— 未选择 —"; + llmSelect.appendChild(emptyOpt); + cachedModels.forEach((m) => { + const opt = document.createElement("option"); + opt.value = m.id; + opt.textContent = `${m.name} (${m.model})`; + if (m.id === agent.llm_model_id) opt.selected = true; + llmSelect.appendChild(opt); + }); + if (!cachedModels.some((m) => m.id === agent.llm_model_id) && agent.llm_model_id) { + const opt = document.createElement("option"); + opt.value = agent.llm_model_id; + opt.textContent = agent.llm_model_id + " (已删除)"; + opt.selected = true; + llmSelect.appendChild(opt); + } + llmSelect.addEventListener("change", () => { + agent.llm_model_id = llmSelect.value; + }); + llmRow.appendChild(llmSelect); + llmField.appendChild(llmLabel); + llmField.appendChild(llmRow); + + // 角色与启用 + const metaRow = document.createElement("div"); + metaRow.className = "setting-field"; + metaRow.style.cssText = "display:flex;gap:1rem;align-items:center;flex-wrap:wrap;"; + + // 角色 + const roleWrap = document.createElement("div"); + roleWrap.style.cssText = "display:flex;align-items:center;gap:0.4rem;"; + const roleLabel = document.createElement("label"); + roleLabel.textContent = "角色:"; + roleLabel.style.cssText = "font-size:0.82rem;font-weight:600;"; + const roleSelect = document.createElement("select"); + roleSelect.innerHTML = ''; + roleSelect.value = agent.role || "sub"; + roleSelect.addEventListener("change", () => { + if (roleSelect.value === "main") { + // 将所有其他 agent 设为 sub + cachedAgentConfigs.forEach((a) => { a.role = "sub"; }); + agent.role = "main"; + renderAgentConfigs(); + } else { + agent.role = "sub"; + // 确保至少有一个 main + const hasMain = cachedAgentConfigs.some((a) => a.role === "main"); + if (!hasMain) { + // 找第一个非此 agent 的设为 main + const other = cachedAgentConfigs.find((a, i) => i !== idx); + if (other) other.role = "main"; + renderAgentConfigs(); + } + } + }); + roleWrap.appendChild(roleLabel); + roleWrap.appendChild(roleSelect); + + // 启用(开关) + const enabledWrap = document.createElement("div"); + enabledWrap.className = "toggle-row"; + const toggleLabel = document.createElement("label"); + toggleLabel.className = "toggle-switch"; + const toggleInput = document.createElement("input"); + toggleInput.type = "checkbox"; + toggleInput.checked = agent.enabled !== false; + toggleInput.addEventListener("change", () => { agent.enabled = toggleInput.checked; }); + const toggleSlider = document.createElement("span"); + toggleSlider.className = "toggle-slider"; + toggleLabel.appendChild(toggleInput); + toggleLabel.appendChild(toggleSlider); + const enabledLabel = document.createElement("span"); + enabledLabel.className = "toggle-label"; + enabledLabel.textContent = "激活"; + enabledLabel.addEventListener("click", () => { toggleInput.click(); }); + enabledWrap.appendChild(toggleLabel); + enabledWrap.appendChild(enabledLabel); + + metaRow.appendChild(roleWrap); + metaRow.appendChild(enabledWrap); + + // 工具选择(列表式:下拉 + 添加/删除) + const toolsField = document.createElement("div"); + toolsField.className = "setting-field"; + const toolsLabel = document.createElement("label"); + toolsLabel.textContent = "工具列表"; + toolsField.appendChild(toolsLabel); + + // 已选工具列表 + const selectedToolsList = document.createElement("div"); + selectedToolsList.style.cssText = "display:flex;flex-wrap:wrap;gap:0.25rem;margin-bottom:0.35rem;min-height:1.5rem;"; + + function renderSelectedTools() { + selectedToolsList.innerHTML = ""; + (agent.tools || []).forEach((tname) => { + const chip = document.createElement("span"); + chip.style.cssText = "display:inline-flex;align-items:center;gap:0.2rem;padding:0.15rem 0.4rem;background:rgba(79,70,229,0.12);border-radius:6px;font-size:0.78rem;"; + chip.textContent = tname; + const delBtn = document.createElement("span"); + delBtn.textContent = "×"; + delBtn.style.cssText = "cursor:pointer;color:#991b1b;font-weight:700;margin-left:0.15rem;"; + delBtn.title = "移除此工具"; + delBtn.addEventListener("click", () => { + agent.tools = (agent.tools || []).filter((t) => t !== tname); + renderSelectedTools(); + }); + chip.appendChild(delBtn); + selectedToolsList.appendChild(chip); + }); + } + renderSelectedTools(); + toolsField.appendChild(selectedToolsList); + + // 添加工具行 + const addToolRow = document.createElement("div"); + addToolRow.style.cssText = "display:flex;gap:0.4rem;align-items:center;"; + const toolSelect = document.createElement("select"); + toolSelect.style.cssText = "flex:1;"; + const emptyToolOpt = document.createElement("option"); + emptyToolOpt.value = ""; + emptyToolOpt.textContent = "选择工具..."; + toolSelect.appendChild(emptyToolOpt); + registeredToolNames.forEach((tname) => { + if (!(agent.tools || []).includes(tname)) { + const opt = document.createElement("option"); + opt.value = tname; + opt.textContent = tname; + toolSelect.appendChild(opt); + } + }); + const addToolBtn = document.createElement("button"); + addToolBtn.type = "button"; + addToolBtn.className = "btn btn-ghost"; + addToolBtn.style.cssText = "padding:0.2rem 0.6rem;font-size:0.78rem;white-space:nowrap;"; + addToolBtn.textContent = "+ 添加"; + addToolBtn.addEventListener("click", () => { + const val = toolSelect.value; + if (!val) return; + if (!agent.tools) agent.tools = []; + if (!agent.tools.includes(val)) { + agent.tools.push(val); + renderSelectedTools(); + // 移除已选项 + Array.from(toolSelect.options).forEach((o) => { if (o.value === val) o.remove(); }); + toolSelect.value = ""; + } + }); + addToolRow.appendChild(toolSelect); + addToolRow.appendChild(addToolBtn); + toolsField.appendChild(addToolRow); + + card.appendChild(header); + card.appendChild(nameField); + card.appendChild(maxIterField); + card.appendChild(spField); + card.appendChild(llmField); + card.appendChild(metaRow); + card.appendChild(toolsField); + return card; + } + + function makeField(label, type, value, onChange) { + const div = document.createElement("div"); + div.className = "setting-field"; + const lbl = document.createElement("label"); + lbl.textContent = label; + let input; + if (type === "textarea") { + input = document.createElement("textarea"); + input.rows = 3; + } else { + input = document.createElement("input"); + input.type = type; + } + input.value = value ?? ""; + input.addEventListener("input", () => onChange(input.value)); + if (type === "number") input.step = "any"; + div.appendChild(lbl); + div.appendChild(input); + return div; + } + + addAgentBtn.addEventListener("click", () => { + // 新 agent 默认为 sub,除非是第一个 + const isFirst = cachedAgentConfigs.length === 0; + cachedAgentConfigs.push({ + name: "new_agent", + system_prompt: "", + max_iterations: 200, + llm: { model: "", api_key: "", base_url: "", timeout: 60, max_retries: 0 }, + llm_model_id: cachedModels.length > 0 ? cachedModels[0].id : "", + tools: [], + enabled: true, + role: isFirst ? "main" : "sub", + }); + renderAgentConfigs(); + }); + + // ===== Tool 配置渲染 ===== + function renderToolConfigs() { + toolsListEl.innerHTML = ""; + cachedToolConfigs.forEach((tool, idx) => { + toolsListEl.appendChild(buildToolCard(tool, idx)); + }); + } + + function buildToolCard(tool, idx) { + const card = document.createElement("div"); + card.className = "tool-config-card" + (tool.enabled === false ? " disabled" : ""); + + const title = document.createElement("h4"); + const enabledCheck = document.createElement("input"); + enabledCheck.type = "checkbox"; + enabledCheck.checked = tool.enabled !== false; + enabledCheck.addEventListener("change", () => { + tool.enabled = enabledCheck.checked; + card.classList.toggle("disabled", !tool.enabled); + }); + title.appendChild(enabledCheck); + title.appendChild(document.createTextNode(` ${tool.display_name || tool.name}`)); + + const desc = document.createElement("div"); + desc.className = "tool-card-desc"; + desc.textContent = tool.description || ""; + + const row = document.createElement("div"); + row.className = "tool-card-row"; + + // 权限选择 + const forcedPerm = (window._forcedPermissions || {})[tool.name]; + const permLabel = document.createElement("label"); + permLabel.textContent = "权限: "; + permLabel.style.fontSize = "0.82rem"; + permLabel.style.fontWeight = "600"; + const permSelect = document.createElement("select"); + permSelect.innerHTML = ''; + permSelect.value = tool.permission || "confirm"; + if (forcedPerm) { + permSelect.disabled = true; + permSelect.value = forcedPerm; + tool.permission = forcedPerm; + permLabel.textContent = "权限(锁定): "; + } + row.appendChild(permLabel); + row.appendChild(permSelect); + + card.appendChild(title); + card.appendChild(desc); + card.appendChild(row); + + // ---------- 自动执行规则(非强制 confirm 且含参数的工具) ---------- + const toolParams = (window._toolParameters || {})[tool.name] || []; + const hasParams = toolParams.length > 0; + const rulesWrap = document.createElement("div"); + + function updateRulesVisibility() { + const visible = !forcedPerm && tool.permission === "confirm" && hasParams; + rulesWrap.hidden = !visible; + } + + if (hasParams) { + rulesWrap.className = "auto-rules-section"; + const rulesLabel = document.createElement("div"); + rulesLabel.style.cssText = "font-size:0.78rem;font-weight:600;color:#475569;margin:0.5rem 0 0.3rem;"; + rulesLabel.textContent = "自动执行规则(满足任一条件则跳过确认):"; + rulesWrap.appendChild(rulesLabel); + + const rulesList = document.createElement("div"); + rulesList.className = "auto-rules-list"; + + const renderRules = () => { + rulesList.innerHTML = ""; + (tool.auto_execute_rules || []).forEach((rule, ri) => { + const ruleRow = document.createElement("div"); + ruleRow.className = "auto-rule-row"; + ruleRow.style.cssText = "display:flex;align-items:center;gap:0.3rem;margin-bottom:0.25rem;flex-wrap:wrap;"; + + const paramLabel = document.createElement("span"); + paramLabel.style.cssText = "font-size:0.74rem;font-weight:600;"; + paramLabel.textContent = "当"; + ruleRow.appendChild(paramLabel); + + const paramSelect = document.createElement("select"); + paramSelect.style.cssText = "flex:0 0 auto;min-width:0;"; + toolParams.forEach((p) => { + const opt = document.createElement("option"); + opt.value = p.name; + opt.textContent = p.name; + if (p.type) opt.title = `类型: ${p.type}`; + if (p.name === (rule.parameter || "")) opt.selected = true; + paramSelect.appendChild(opt); + }); + if (rule.parameter && !toolParams.some((p) => p.name === rule.parameter)) { + const opt = document.createElement("option"); + opt.value = rule.parameter; + opt.textContent = rule.parameter; + opt.selected = true; + paramSelect.appendChild(opt); + } + paramSelect.addEventListener("change", () => { rule.parameter = paramSelect.value; }); + ruleRow.appendChild(paramSelect); + + const opLabel = document.createElement("span"); + opLabel.style.cssText = "font-size:0.74rem;"; + opLabel.textContent = "="; + ruleRow.appendChild(opLabel); + + const valInput = document.createElement("input"); + valInput.type = "text"; + valInput.value = rule.value || ""; + valInput.placeholder = "期望值"; + valInput.style.cssText = "flex:1;min-width:80px;border:1px solid rgba(148,163,184,0.5);border-radius:6px;padding:0.2rem 0.35rem;font-size:0.8rem;"; + valInput.addEventListener("input", () => { rule.value = valInput.value; }); + ruleRow.appendChild(valInput); + + const delBtn = document.createElement("button"); + delBtn.type = "button"; + delBtn.style.cssText = "border:none;background:none;cursor:pointer;color:#991b1b;font-size:0.9rem;padding:0 0.2rem;"; + delBtn.textContent = "×"; + delBtn.title = "删除此规则"; + delBtn.addEventListener("click", () => { + tool.auto_execute_rules = (tool.auto_execute_rules || []).filter((_, i) => i !== ri); + renderRules(); + }); + ruleRow.appendChild(delBtn); + + rulesList.appendChild(ruleRow); + }); + }; + renderRules(); + + const addRuleRow = document.createElement("div"); + addRuleRow.style.cssText = "margin-top:0.35rem;"; + const addRuleBtn = document.createElement("button"); + addRuleBtn.type = "button"; + addRuleBtn.className = "btn btn-ghost"; + addRuleBtn.style.cssText = "padding:0.15rem 0.5rem;font-size:0.75rem;"; + addRuleBtn.textContent = "+ 添加规则"; + addRuleBtn.addEventListener("click", () => { + const defaultParam = toolParams[0].name; + tool.auto_execute_rules = [...(tool.auto_execute_rules || []), { parameter: defaultParam, operator: "equals", value: "" }]; + renderRules(); + }); + addRuleRow.appendChild(addRuleBtn); + rulesWrap.appendChild(rulesList); + rulesWrap.appendChild(addRuleRow); + } + + card.appendChild(rulesWrap); + updateRulesVisibility(); + + permSelect.addEventListener("change", () => { + tool.permission = permSelect.value; + updateRulesVisibility(); + }); + + return card; + } + + // ===== Env 配置渲染 ===== + function renderEnvConfig() { + envListEl.innerHTML = ""; + + // ---------- 模型管理区 ---------- + const modelsSection = document.createElement("div"); + modelsSection.style.cssText = "margin-bottom:1.5rem;"; + + const modelsHeader = document.createElement("div"); + modelsHeader.className = "settings-section-title"; + const modelsTitle = document.createElement("h4"); + modelsTitle.textContent = "LLM 模型列表"; + const addModelBtn = document.createElement("button"); + addModelBtn.type = "button"; + addModelBtn.className = "btn btn-primary"; + addModelBtn.textContent = "+ 添加模型"; + addModelBtn.addEventListener("click", () => openModelModal(-1)); + modelsHeader.appendChild(modelsTitle); + modelsHeader.appendChild(addModelBtn); + modelsSection.appendChild(modelsHeader); + + if (cachedModels.length === 0) { + const emptyHint = document.createElement("p"); + emptyHint.style.cssText = "color:#94a3b8;font-size:0.8rem;"; + emptyHint.textContent = "暂无模型,请添加至少一个 LLM 模型。"; + modelsSection.appendChild(emptyHint); + } else { + const table = document.createElement("div"); + table.className = "model-table"; + const thead = document.createElement("div"); + thead.className = "model-table-head"; + thead.innerHTML = '名称ModelBase URL操作'; + table.appendChild(thead); + cachedModels.forEach((m, mi) => { + const row = document.createElement("div"); + row.className = "model-table-row"; + row.innerHTML = `${escapeHtml(m.name)} + ${escapeHtml(m.model)} + ${escapeHtml(m.base_url)}`; + const actions = document.createElement("span"); + actions.className = "col-actions"; + const editBtn = document.createElement("button"); + editBtn.type = "button"; + editBtn.textContent = "编辑"; + editBtn.addEventListener("click", () => openModelModal(mi)); + const delBtn = document.createElement("button"); + delBtn.type = "button"; + delBtn.className = "danger"; + delBtn.textContent = "删除"; + delBtn.addEventListener("click", () => { + if (!confirm(`确定删除模型 "${m.name}"?`)) return; + cachedModels.splice(mi, 1); + // 清除引用该模型的 agent + cachedAgentConfigs.forEach((a) => { if (a.llm_model_id === m.id) a.llm_model_id = ""; }); + // 同时保存模型变更 + api("/api/config/models", { method: "POST", body: JSON.stringify({ models: cachedModels }) }).catch(() => {}); + api("/api/config/reload", { method: "POST", body: JSON.stringify({}) }).catch(() => {}); + renderEnvConfig(); + renderAgentConfigs(); + }); + actions.appendChild(editBtn); + actions.appendChild(delBtn); + row.appendChild(actions); + table.appendChild(row); + }); + modelsSection.appendChild(table); + } + envListEl.appendChild(modelsSection); + + // ---------- 模型编辑弹窗(内嵌在 env 面板中) ---------- + const modalOverlay = document.createElement("div"); + modalOverlay.className = "model-modal-overlay"; + modalOverlay.addEventListener("click", (e) => { if (e.target === modalOverlay) closeModelModal(); }); + const modalBox = document.createElement("div"); + modalBox.className = "model-modal-box"; + modalBox.innerHTML = '

模型配置

'; + const modalBody = document.createElement("div"); + modalBody.className = "model-modal-body"; + const modalFields = {}; + ["name", "model", "api_key", "base_url", "timeout"].forEach((key) => { + const div = document.createElement("div"); + div.className = "modal-field"; + const lbl = document.createElement("label"); + lbl.textContent = { name: "显示名称", model: "模型名 (model)", api_key: "API Key", base_url: "Base URL", timeout: "Timeout (秒)" }[key]; + const inp = document.createElement("input"); + inp.type = key === "timeout" ? "number" : key === "api_key" ? "password" : "text"; + if (key === "timeout") inp.step = "any"; + div.appendChild(lbl); + div.appendChild(inp); + modalFields[key] = inp; + modalBody.appendChild(div); + }); + modalBox.appendChild(modalBody); + const modalActions = document.createElement("div"); + modalActions.className = "model-modal-actions"; + const cancelBtn = document.createElement("button"); + cancelBtn.type = "button"; + cancelBtn.className = "btn btn-secondary"; + cancelBtn.textContent = "取消"; + cancelBtn.addEventListener("click", closeModelModal); + const saveBtn = document.createElement("button"); + saveBtn.type = "button"; + saveBtn.className = "btn btn-primary"; + saveBtn.textContent = "保存"; + saveBtn.addEventListener("click", async () => { + const data = { + id: editingModelIdx >= 0 && cachedModels[editingModelIdx] ? cachedModels[editingModelIdx].id : makeId(), + name: modalFields.name.value.trim(), + model: modalFields.model.value.trim(), + api_key: modalFields.api_key.value.trim(), + base_url: modalFields.base_url.value.trim(), + timeout: parseFloat(modalFields.timeout.value) || 60, + max_retries: 0, + }; + if (!data.name || !data.model) { alert("名称和模型名不能为空"); return; } + if (editingModelIdx >= 0) { + cachedModels[editingModelIdx] = data; + } else { + cachedModels.push(data); + } + await api("/api/config/models", { method: "POST", body: JSON.stringify({ models: cachedModels }) }); + closeModelModal(); + renderEnvConfig(); + renderAgentConfigs(); + setStatus("模型已保存", "success"); + }); + modalActions.appendChild(cancelBtn); + modalActions.appendChild(saveBtn); + modalBox.appendChild(modalActions); + modalOverlay.appendChild(modalBox); + envListEl.appendChild(modalOverlay); + + window._modelModalOverlay = modalOverlay; + window._modelModalFields = modalFields; + + // ---------- 分隔线 ---------- + const divider = document.createElement("div"); + divider.style.cssText = "border-top:1px solid rgba(148,163,184,0.25);margin:1rem 0;"; + envListEl.appendChild(divider); + + // ---------- 其他环境变量 ---------- + const otherTitle = document.createElement("h4"); + otherTitle.textContent = "其他环境变量"; + otherTitle.style.cssText = "margin:0 0 0.65rem;font-size:0.92rem;font-weight:700;color:#1e293b;"; + envListEl.appendChild(otherTitle); + + const entries = Object.entries(cachedEnvConfig); + entries.forEach(([key, val]) => { + const row = document.createElement("div"); + row.className = "env-config-item"; + const lbl = document.createElement("label"); + lbl.textContent = key; + const inp = document.createElement("input"); + inp.type = "text"; + inp.value = val ?? ""; + inp.addEventListener("input", () => { cachedEnvConfig[key] = inp.value; }); + row.appendChild(lbl); + row.appendChild(inp); + envListEl.appendChild(row); + }); + } + + function openModelModal(idx) { + editingModelIdx = idx; + const overlay = window._modelModalOverlay; + const fields = window._modelModalFields; + if (!overlay || !fields) return; + if (idx >= 0 && cachedModels[idx]) { + const m = cachedModels[idx]; + fields.name.value = m.name || ""; + fields.model.value = m.model || ""; + fields.api_key.value = m.api_key || ""; + fields.base_url.value = m.base_url || ""; + fields.timeout.value = m.timeout || 60; + } else { + fields.name.value = ""; + fields.model.value = ""; + fields.api_key.value = ""; + fields.base_url.value = ""; + fields.timeout.value = 60; + } + overlay.classList.add("show"); + } + + function closeModelModal() { + const overlay = window._modelModalOverlay; + if (overlay) overlay.classList.remove("show"); + } + + function makeId() { + return Math.random().toString(36).substring(2, 10); + } + + // ===== 保存回调 ===== + document.querySelectorAll(".settings-save-btn").forEach((btn) => { + btn.addEventListener("click", async () => { + const saveType = btn.getAttribute("data-save"); + setStatus("正在保存...", ""); + try { + if (saveType === "agents") { + const agents = cachedAgentConfigs.map((a) => ({ + name: a.name, + system_prompt: a.system_prompt, + max_iterations: a.max_iterations, + llm: { + model: (a.llm || {}).model || "", + api_key: (a.llm || {}).api_key || "", + base_url: (a.llm || {}).base_url || "", + timeout: (a.llm || {}).timeout || 60, + max_retries: (a.llm || {}).max_retries || 0, + }, + llm_model_id: a.llm_model_id || "", + tools: a.tools || [], + enabled: a.enabled !== false, + role: a.role || "sub", + })); + await api("/api/config/agents", { method: "POST", body: JSON.stringify({ agents }) }); + await api("/api/config/reload", { method: "POST", body: JSON.stringify({}) }); + } else if (saveType === "tools") { + const tools = cachedToolConfigs.map((t) => ({ + name: t.name, + display_name: t.display_name, + description: t.description, + permission: t.permission, + auto_execute_rules: t.auto_execute_rules || [], + enabled: t.enabled, + })); + await api("/api/config/tools", { method: "POST", body: JSON.stringify({ tools }) }); + await api("/api/config/reload", { method: "POST", body: JSON.stringify({}) }); + } else if (saveType === "env") { + // 保存 env + models + await api("/api/config/models", { method: "POST", body: JSON.stringify({ models: cachedModels }) }); + await api("/api/config/env", { method: "POST", body: JSON.stringify({ env: cachedEnvConfig }) }); + await api("/api/config/reload", { method: "POST", body: JSON.stringify({}) }); + } + await loadAllConfigs(); + setStatus("保存成功!配置已实时生效。", "success"); + } catch (e) { + setStatus("保存失败: " + e.message, "error"); + } + }); + }); + + // 事件绑定 + settingsBtn.addEventListener("click", openSettings); + settingsCloseBtn.addEventListener("click", closeSettings); + settingsCancelBtn.addEventListener("click", closeSettings); + settingsOverlay.addEventListener("click", (e) => { + if (e.target === settingsOverlay) closeSettings(); + }); + document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && !settingsOverlay.hidden) closeSettings(); + if (e.key === "Escape" && agentDetailOverlay && !agentDetailOverlay.hidden) closeAgentDetailModal(); + }); + + // ========== 图谱侧面板 ========== + const graphToggleBtn = $("graphToggleBtn"); + const graphPanel = $("graphPanel"); + const graphCloseBtn = $("graphCloseBtn"); + const graphCanvas = $("graphCanvas"); + const graphCanvasWrap = $("graphCanvasWrap"); + const graphEdgesLayer = $("graphEdgesLayer"); + const graphNodesLayer = $("graphNodesLayer"); + const graphRefreshBtn = $("graphRefreshBtn"); + const graphFitBtn = $("graphFitBtn"); + + // 图谱数据与状态 + let graphData = { agents: [], edges: [], events: [], tool_calls: {} }; + let graphOpen = false; + // 可拖拽偏移 + let panOffset = { x: 80, y: 60 }; + let isPanning = false; + let panStart = { x: 0, y: 0 }; + let panStartOffset = { x: 0, y: 0 }; + // 节点布局参数 + const NODE_W = 160; + const NODE_H = 100; + + // 图谱开关(始终可聊天) + function toggleGraph(open) { + graphOpen = open; + graphPanel.hidden = !open; + graphToggleBtn.classList.toggle("graph-active", open); + appRoot.classList.toggle("has-graph", open); + if (open) { + loadGraphData(); + } + } + + graphToggleBtn.addEventListener("click", () => toggleGraph(!graphOpen)); + graphCloseBtn.addEventListener("click", () => toggleGraph(false)); + + // Agent 详情弹窗事件 + const agentDetailOverlay = document.getElementById("agentDetailOverlay"); + const agentDetailClose = document.getElementById("agentDetailClose"); + if (agentDetailClose) agentDetailClose.addEventListener("click", closeAgentDetailModal); + if (agentDetailOverlay) agentDetailOverlay.addEventListener("click", (e) => { + if (e.target === agentDetailOverlay) closeAgentDetailModal(); + }); + + // 加载图谱数据 + async function loadGraphData() { + try { + const snap = await api(`/api/graph/snapshot?session_id=${encodeURIComponent(sessionId)}`); + graphData = snap; + renderGraph(); + } catch (e) { + console.error("加载图谱数据失败:", e); + } + } + + // 自动布局:主 agent 在中心,子 agent 环绕 + function computeLayout() { + const agents = graphData.agents || []; + if (agents.length === 0) return {}; + + const positions = {}; + const wrapW = graphCanvasWrap.clientWidth || 600; + const wrapH = graphCanvasWrap.clientHeight || 400; + const cx = wrapW / 2; + const cy = wrapH / 2; + + // 主 agent 在中心 + const mainAgent = agents.find(a => a.role === "main"); + if (mainAgent) { + positions[mainAgent.name] = { x: cx - NODE_W / 2, y: cy - NODE_H / 2 }; + } + + // 子 agent 均匀环绕 + const subs = agents.filter(a => a.role !== "main"); + const radius = Math.min(wrapW, wrapH) * 0.32; + subs.forEach((agent, i) => { + const angle = (2 * Math.PI * i) / subs.length - Math.PI / 2; + positions[agent.name] = { + x: cx + radius * Math.cos(angle) - NODE_W / 2, + y: cy + radius * Math.sin(angle) - NODE_H / 2, + }; + }); + + return positions; + } + + // 状态到 CSS 类 + 文字 + function statusInfo(status) { + switch (status) { + case "running": return { cls: "status-running", badge: "执行中" }; + case "done": return { cls: "status-done", badge: "已完成" }; + case "error": return { cls: "status-error", badge: "出错" }; + default: return { cls: "status-idle", badge: "等待" }; + } + } + + // 渲染图谱 + function renderGraph() { + const agents = graphData.agents || []; + const edges = graphData.edges || []; + const positions = computeLayout(); + + // 计算 SVG viewBox + const wrapW = graphCanvasWrap.clientWidth || 600; + const wrapH = graphCanvasWrap.clientHeight || 400; + graphCanvas.setAttribute("viewBox", `0 0 ${wrapW} ${wrapH}`); + + // --- 渲染连线 --- + graphEdgesLayer.innerHTML = ""; + edges.forEach((edge, i) => { + const srcPos = positions[edge.source]; + const tgtPos = positions[edge.target]; + if (!srcPos || !tgtPos) return; + + const x1 = srcPos.x + NODE_W / 2; + const y1 = srcPos.y + NODE_H / 2; + const x2 = tgtPos.x + NODE_W / 2; + const y2 = tgtPos.y + NODE_H / 2; + + // 计算从边缘出发(而非中心) + const dx = x2 - x1; + const dy = y2 - y1; + const dist = Math.sqrt(dx * dx + dy * dy) || 1; + const nx = dx / dist; + const ny = dy / dist; + + const sx = x1 + nx * (NODE_W / 2); + const sy = y1 + ny * (NODE_H / 2); + const ex = x2 - nx * (NODE_W / 2); + const ey = y2 - ny * (NODE_H / 2); + + let edgeClass = "normal"; + let markerEnd = "url(#arrowNormal)"; + if (edge.highlighted) { + edgeClass = "highlighted"; + markerEnd = "url(#arrowHighlight)"; + } + + const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); + line.setAttribute("x1", sx + panOffset.x); + line.setAttribute("y1", sy + panOffset.y); + line.setAttribute("x2", ex + panOffset.x); + line.setAttribute("y2", ey + panOffset.y); + line.setAttribute("class", `graph-edge ${edgeClass}`); + line.setAttribute("marker-end", markerEnd); + line.setAttribute("data-source", edge.source); + line.setAttribute("data-target", edge.target); + graphEdgesLayer.appendChild(line); + }); + + // --- 渲染节点 --- + graphNodesLayer.innerHTML = ""; + agents.forEach((agent) => { + const pos = positions[agent.name]; + if (!pos) return; + + const sinfo = statusInfo(agent.status || "idle"); + const g = document.createElementNS("http://www.w3.org/2000/svg", "g"); + g.setAttribute("class", `graph-node ${sinfo.cls}`); + g.setAttribute("transform", `translate(${pos.x + panOffset.x}, ${pos.y + panOffset.y})`); + g.setAttribute("data-agent", agent.name); + + // 圆角矩形 + const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + rect.setAttribute("width", NODE_W); + rect.setAttribute("height", NODE_H); + rect.setAttribute("class", "graph-node-rect"); + g.appendChild(rect); + + // 图标 + const iconText = document.createElementNS("http://www.w3.org/2000/svg", "text"); + iconText.setAttribute("x", 28); + iconText.setAttribute("y", NODE_H / 2); + iconText.setAttribute("class", "graph-node-icon"); + iconText.textContent = agent.icon || "🧠"; + g.appendChild(iconText); + + // 名称 + const labelText = document.createElementNS("http://www.w3.org/2000/svg", "text"); + labelText.setAttribute("x", 90); + labelText.setAttribute("y", NODE_H / 2 - 8); + labelText.setAttribute("class", "graph-node-label"); + labelText.textContent = agent.name; + g.appendChild(labelText); + + // 角色 + const roleText = document.createElementNS("http://www.w3.org/2000/svg", "text"); + roleText.setAttribute("x", 90); + roleText.setAttribute("y", NODE_H / 2 + 12); + roleText.setAttribute("class", "graph-node-role"); + roleText.textContent = agent.role === "main" ? "主 Agent" : "子 Agent"; + g.appendChild(roleText); + + // 状态标签(右上角) + const badgeText = document.createElementNS("http://www.w3.org/2000/svg", "text"); + badgeText.setAttribute("x", NODE_W - 8); + badgeText.setAttribute("y", 14); + badgeText.setAttribute("class", "graph-node-badge"); + const badgeColor = agent.status === "running" ? "#f59e0b" : agent.status === "done" ? "#22c55e" : agent.status === "error" ? "#ef4444" : "#94a3b8"; + badgeText.setAttribute("fill", badgeColor); + badgeText.textContent = sinfo.badge; + g.appendChild(badgeText); + + // 工具调用缩略(仅最近2轮,渐变透明) + const agentToolCalls = (graphData.tool_calls || {})[agent.name] || []; + const recentCalls = agentToolCalls.slice(0, 4); + recentCalls.forEach((tc, idx) => { + const chip = document.createElementNS("http://www.w3.org/2000/svg", "text"); + chip.setAttribute("x", 12 + idx * 38); + chip.setAttribute("y", NODE_H - 10); + chip.setAttribute("class", "graph-node-tool-chip"); + chip.setAttribute("opacity", Math.max(0.2, 1 - idx * 0.35)); + chip.textContent = (tc.tool_name || "").substring(0, 5); + g.appendChild(chip); + }); + + // 点击事件 → 弹出 Agent 详情 modal + g.style.cursor = "pointer"; + g.addEventListener("click", () => openAgentDetailModal(agent)); + + graphNodesLayer.appendChild(g); + }); + } + + // 渲染工具调用列表 → 已移至 Agent 点击弹窗 + function openAgentDetailModal(agent) { + const overlay = document.getElementById("agentDetailOverlay"); + const stateDiv = document.getElementById("agentDetailState"); + const configDiv = document.getElementById("agentDetailConfig"); + const toolCallsDiv = document.getElementById("agentDetailToolCalls"); + const titleEl = document.getElementById("agentDetailTitle"); + if (!overlay || !stateDiv || !configDiv || !toolCallsDiv) return; + + titleEl.textContent = `Agent: ${agent.name}`; + const sinfo = statusInfo(agent.status || "idle"); + + // 状态 + stateDiv.innerHTML = ` +

名称: ${escapeHtml(agent.name)}

+

角色: ${agent.role === "main" ? "主 Agent" : "子 Agent"}

+

状态: ${sinfo.badge}

+ ${agent.error_message ? `

错误: ${escapeHtml(agent.error_message)}

` : ""} + `; + + // 配置(只读) + const tools = agent.tools || []; + configDiv.innerHTML = ` +

工具列表: ${tools.length ? tools.map(t => `${escapeHtml(t)}`).join(", ") : "(无)"}

+ `; + + // 工具调用记录 + const calls = (graphData.tool_calls || {})[agent.name] || []; + if (calls.length === 0) { + toolCallsDiv.innerHTML = `

暂无工具调用记录

`; + } else { + toolCallsDiv.innerHTML = [...calls].reverse().slice(0, 6).map(c => ` +
+
+ ${escapeHtml(c.tool_name)} + ${c.status || 'success'} +
+ ${c.tool_args && Object.keys(c.tool_args).length > 0 ? `
${escapeHtml(JSON.stringify(c.tool_args, null, 1))}
` : ""} + ${c.result_preview ? `
${escapeHtml(c.result_preview)}
` : ""} +
+ `).join(""); + } + + overlay.hidden = false; + } + + // 关闭 Agent 详情弹窗 + function closeAgentDetailModal() { + const overlay = document.getElementById("agentDetailOverlay"); + if (overlay) overlay.hidden = true; + } + + + // 画布拖拽 + graphCanvasWrap.addEventListener("mousedown", (e) => { + if (e.target === graphCanvas || e.target === graphCanvasWrap || e.target.closest("svg")) { + isPanning = true; + panStart = { x: e.clientX, y: e.clientY }; + panStartOffset = { ...panOffset }; + graphCanvasWrap.classList.add("panning"); + e.preventDefault(); + } + }); + + window.addEventListener("mousemove", (e) => { + if (!isPanning) return; + const dx = e.clientX - panStart.x; + const dy = e.clientY - panStart.y; + panOffset = { + x: panStartOffset.x + dx, + y: panStartOffset.y + dy, + }; + renderGraph(); + }); + + window.addEventListener("mouseup", () => { + if (isPanning) { + isPanning = false; + graphCanvasWrap.classList.remove("panning"); + } + }); + + // 触摸拖拽 + graphCanvasWrap.addEventListener("touchstart", (e) => { + if (e.touches.length === 1) { + isPanning = true; + panStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }; + panStartOffset = { ...panOffset }; + graphCanvasWrap.classList.add("panning"); + } + }, { passive: false }); + + graphCanvasWrap.addEventListener("touchmove", (e) => { + if (!isPanning || e.touches.length !== 1) return; + const dx = e.touches[0].clientX - panStart.x; + const dy = e.touches[0].clientY - panStart.y; + panOffset = { + x: panStartOffset.x + dx, + y: panStartOffset.y + dy, + }; + renderGraph(); + e.preventDefault(); + }, { passive: false }); + + graphCanvasWrap.addEventListener("touchend", () => { + if (isPanning) { + isPanning = false; + graphCanvasWrap.classList.remove("panning"); + } + }); + + // 鼠标滚轮缩放(可选) + + // 按钮事件 + graphRefreshBtn.addEventListener("click", () => { + loadGraphData(); + }); + + graphFitBtn.addEventListener("click", () => { + panOffset = { x: 80, y: 60 }; + renderGraph(); + }); + + // 窗口大小变化时重新渲染 + let resizeGraphTimer = null; + window.addEventListener("resize", () => { + clearTimeout(resizeGraphTimer); + resizeGraphTimer = setTimeout(() => { + if (graphOpen) { + renderGraph(); + } + }, 200); + }); + bootstrap().catch((e) => { chatPlaceholder.hidden = false; chatPlaceholder.innerHTML = `

无法连接服务

${escapeHtml(e.message)}

请从 Agent 目录执行:uvicorn web.server:app --reload --host 127.0.0.1 --port 8765,并保证 PYTHONPATH 包含 src。

`; diff --git a/src/web/index.html b/src/web/index.html index cd18cf1..ac59600 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -77,15 +77,69 @@ > ▴ + + -
-
-

开始对话

-

输入文字,或上传附件 后点击发送。

-

支持用户音频输入、助手文本/语音/双模输出、会话本地媒体持久化。

-
- -
+
+ +
+
+

开始对话

+

输入文字,或上传附件 后点击发送。

+

支持用户音频输入、助手文本/语音/双模输出、会话本地媒体持久化。

+
+ +
+ + +
@@ -111,6 +165,11 @@ + +
+ + + + diff --git a/src/web/server.py b/src/web/server.py index 5268b20..840c7b9 100644 --- a/src/web/server.py +++ b/src/web/server.py @@ -1,3 +1,4 @@ +# server.py """ 为 ``web`` 目录下的静态前端提供 API:会话列表、多模态发送(与 ``chatbot_agent`` 一致)。 @@ -8,6 +9,7 @@ from __future__ import annotations import base64 +import json import mimetypes import os import shutil @@ -27,6 +29,14 @@ if str(_SRC) not in sys.path: from agent import chatbot_agent as ca # noqa: E402 from web import ui_session as ui # noqa: E402 from agent.utils.image_utils import IMAGE_FILE_EXTENSIONS # noqa: E402 +from agent.core.config_manager import ( # noqa: E402 + load_agent_configs, save_agent_configs, + load_tool_configs, save_tool_configs, + load_env_config, save_env_config, + load_models, save_models, + get_registered_tool_names, + AGENT_CONFIG_PATH, TOOL_CONFIG_PATH, ENV_CONFIG_PATH, +) _WEB_DIR = Path(__file__).resolve().parent _IMAGE_DIR = _WEB_DIR / "image" @@ -35,17 +45,21 @@ _AUDIO_EXT = frozenset({".wav", ".mp3", ".m4a", ".flac", ".ogg", ".webm"}) _IMAGE_EXT = frozenset(IMAGE_FILE_EXTENSIONS) -def _infer_upload_kind(fp: str) -> str | None: - ext = os.path.splitext(fp)[1].lower() - if ext in _IMAGE_EXT: - return "image" - if ext in _AUDIO_EXT: - return "audio" - return "file" - - def _data_url_for_file(fp: str) -> str | None: - """将本地文件读入为 data URL,供前端直接展示(不单独提供文件下载路由)。""" + """将本地文件读入为 data URL,供前端直接展示(不单独提供文件下载路由)。 + + 输入: + fp: 本地文件路径。 + + 输出: + data URL 字符串,失败返回 None。 + + 系统定位: + _serialize_content 将附件转为内联 data URL 以嵌入 API 响应。 + + 可扩展性: + 可增加缓存、大小限制、权限检查。 + """ if not fp or not os.path.isfile(fp): return None mime, _ = mimetypes.guess_type(fp) @@ -75,7 +89,22 @@ def _data_url_for_file(fp: str) -> str | None: def _serialize_content(content: Any, *, meta: dict[str, Any] | None = None, role: str | None = None) -> Any: - """将一条消息中的 content 转为 JSON 友好结构;文件以内联 data URL 输出。""" + """将一条消息中的 content 转为 JSON 友好结构;文件以内联 data URL 输出。 + + 输入: + content: 原始 content(str、list 或 dict)。 + meta: 消息的 meta 信息(如音频时长、自动播放等)。 + role: 消息角色(user/assistant),用于控制自动播放行为。 + + 输出: + 序列化后的 content(str 或 list of dict)。 + + 系统定位: + _serialize_messages 中逐条转换,供前端直接渲染。 + + 可扩展性: + 可支持更多文件类型(如视频)的 data URL 输出。 + """ meta = meta or {} if isinstance(content, str): return content @@ -121,6 +150,20 @@ def _serialize_content(content: Any, *, meta: dict[str, Any] | None = None, role def _serialize_messages(hist: list[dict[str, Any]] | None) -> list[dict[str, Any]]: + """将 chat_history 转为 API 响应格式,附件内联为 data URL。 + + 输入: + hist: 聊天历史(dict 列表)。 + + 输出: + 序列化后的消息列表,每条消息包含 role、content、meta。 + + 系统定位: + 所有返回给前端的 API 响应中,messages 字段均经过此函数转换。 + + 可扩展性: + 可支持更细粒度的权限控制或裁剪。 + """ hist = hist or [] # 找到最后一条 assistant 消息的索引,用于控制自动播放 last_assistant_idx = -1 @@ -178,6 +221,19 @@ def _serialize_messages(hist: list[dict[str, Any]] | None) -> list[dict[str, Any def _bootstrap() -> tuple[str, list[str], list[dict[str, Any]]]: + """初始化默认会话:取最近会话或新建时间戳 ID 及其消息列表。 + + 输入: 无。 + + 输出: + (session_id, sessions列表, messages列表) + + 系统定位: + API 启动时/删除会话后回退到默认会话。 + + 可扩展性: + 可增加用户偏好默认会话逻辑。 + """ ids = ui.list_session_ids_ordered() if ids: sid = ids[0] @@ -201,12 +257,35 @@ app.add_middleware( @app.get("/api/bootstrap") def api_bootstrap() -> dict[str, Any]: + """获取初始会话信息(默认会话 ID、会话列表、消息历史)。 + + 输入: 无。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 前端页面加载时调用的第一个 API,用于初始化界面。 + + 可扩展性: + 可返回用户配置、主题等元信息。 + """ sid, ids, hist = _bootstrap() return {"sessionId": sid, "sessions": ids, "messages": _serialize_messages(hist)} @app.post("/api/sessions/new") def api_new_session() -> dict[str, Any]: + """创建新会话(基于时间戳),返回会话信息。 + + 输入: 无。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": []} + + 系统定位: + 前端点击“新建会话”时调用。 + """ sid = ui.new_timestamp_session_id() ids = ui.list_session_ids_ordered() ids = [sid] + [x for x in ids if x != sid] @@ -215,6 +294,17 @@ def api_new_session() -> dict[str, Any]: @app.get("/api/sessions/{session_id}/messages") def api_session_messages(session_id: str) -> dict[str, Any]: + """切换会话:加载指定会话的消息列表与全局会话列表。 + + 输入: + session_id: 路径参数,目标会话 ID。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 前端切换会话时调用。 + """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") hist = ui.load_ui_messages_for_session(session_id) @@ -226,6 +316,17 @@ def api_session_messages(session_id: str) -> dict[str, Any]: @app.post("/api/sessions/clear") def api_clear_session(session_id: str = Form(...)) -> dict[str, Any]: + """清空当前会话的所有消息(重置会话)。 + + 输入: + session_id: 表单字段,会话 ID。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": []} + + 系统定位: + 前端“清空对话”按钮调用。 + """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") messages, _ = ui.clear_ui_and_manifest(session_id) @@ -237,6 +338,17 @@ def api_clear_session(session_id: str = Form(...)) -> dict[str, Any]: @app.post("/api/sessions/delete") def api_delete_session(session_id: str = Form(...)) -> dict[str, Any]: + """删除整个会话(含文件与历史),并回退到默认会话。 + + 输入: + session_id: 表单字段,会话 ID。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 前端“删除会话”按钮调用。 + """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") ui.delete_session(session_id) @@ -250,7 +362,24 @@ def _materialize_uploads_to_temp( audio: UploadFile | None, text: str, ) -> tuple[dict[str, Any], str]: - """将上传文件落盘到临时目录,返回 ``(query_dict, tmp_root)``;调用方须在 finally 中删除 ``tmp_root``。""" + """将上传文件落盘到临时目录,返回 ``(query_dict, tmp_root)``;调用方须在 finally 中删除 ``tmp_root``。 + + 输入: + images: 前端上传的图片文件列表。 + files: 前端上传的普通文件列表。 + audio: 前端上传的音频文件。 + text: 用户输入的文本。 + + 输出: + (query_dict, tmp_root) + query_dict 包含 "text", "files"(路径列表), "file_names"(原始文件名列表)。 + + 系统定位: + API 层处理用户多模态输入的统一临时落盘逻辑。 + + 可扩展性: + 可增加文件大小限制、病毒扫描钩子。 + """ tmp_root = tempfile.mkdtemp(prefix="web_mm_") file_paths: list[str] = [] file_names: list[str] = [] @@ -287,7 +416,21 @@ def _materialize_uploads_to_temp( def _chat_start_core(session_id: str, query: dict[str, Any]) -> dict[str, Any]: - """追加 user、复制附件到会话目录并写 manifest(不调用模型)。""" + """追加 user、复制附件到会话目录并写 manifest(不调用模型)。 + + 输入: + session_id: 会话 ID。 + query: {"text": str, "files": list[str], "file_names": list[str]} + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + /api/chat/start 与 /api/chat(兼容模式)的后端核心逻辑。 + + 可扩展性: + 可增加消息预处理钩子。 + """ hist = ui.load_ui_messages_for_session(session_id) before = len(hist or []) new_hist, cleared = ui.add_message(hist or [], query) @@ -309,15 +452,31 @@ def _chat_start_core(session_id: str, query: dict[str, Any]) -> dict[str, Any]: } -def _chat_complete_core(session_id: str, output_type: str = "text") -> dict[str, Any]: - """对 manifest 中末条 user 执行视觉解析与 ``chain_with_history.invoke``。""" +def _chat_complete_core(session_id: str, output_type: str = "text", agent_mode: str = "agent") -> dict[str, Any]: + """对 manifest 中末条 user 执行视觉解析与 ``chain_with_history.invoke``。 + + 输入: + session_id: 会话 ID。 + output_type: text | audio | both。 + agent_mode: agent | plan | team。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + /api/chat/complete 与 /api/chat(兼容模式)的后端核心逻辑。 + + 可扩展性: + 可支持异步执行、流式输出。 + """ hist = ui.load_ui_messages_for_session(session_id) if not hist or hist[-1].get("role") != "user": raise HTTPException( status_code=400, detail="没有待完成的用户消息,请先调用 /api/chat/start", ) - new_hist = ca.execute_agent([*hist], session_id, output_type=output_type) + normalized_mode = agent_mode if agent_mode in {"agent", "plan", "team"} else "agent" + new_hist = ca.execute_agent([*hist], session_id, output_type=output_type, agent_mode=normalized_mode) ui.prune_memory_session_ids() ids = ui.list_session_ids_ordered() if session_id not in ids: @@ -340,6 +499,19 @@ async def api_chat_start( """ 仅追加用户消息并持久化到 manifest(含附件落盘);不调用模型。 前端应先调用本接口再调用 ``/api/chat/complete``。 + + 输入: + session_id: 表单字段,会话 ID。 + text: 用户输入的文本。 + images: 上传的图片文件列表。 + files: 上传的普通文件列表。 + audio: 上传的音频文件。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 两步式对话的第一步,用于提前上传文件并显示用户消息。 """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") @@ -352,14 +524,27 @@ async def api_chat_start( @app.post("/api/chat/complete") -async def api_chat_complete( +def api_chat_complete( session_id: str = Form(...), output_type: str = Form("text"), + agent_mode: str = Form("agent"), ) -> dict[str, Any]: - """对当前会话最近一次 ``/api/chat/start`` 提交执行模型推理并返回完整消息列表。""" + """对当前会话最近一次 ``/api/chat/start`` 提交执行模型推理并返回完整消息列表。 + + 输入: + session_id: 表单字段,会话 ID。 + output_type: text | audio | both。 + agent_mode: agent | plan | team。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 两步式对话的第二步,执行 Agent 并返回结果。 + """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") - return _chat_complete_core(session_id, output_type=output_type) + return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode) @app.post("/api/chat") @@ -370,9 +555,25 @@ async def api_chat( files: list[UploadFile] | None = File(None), audio: UploadFile | None = File(None), output_type: str = Form("text"), + agent_mode: str = Form("agent"), ) -> dict[str, Any]: """ 兼容旧客户端:等价于依次 ``/api/chat/start`` + ``/api/chat/complete``(仍阻塞至推理结束)。 + + 输入: + session_id: 表单字段,会话 ID。 + text: 用户输入的文本。 + images: 上传的图片文件列表。 + files: 上传的普通文件列表。 + audio: 上传的音频文件。 + output_type: text | audio | both。 + agent_mode: agent | plan | team。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 向后兼容的一次性对话接口。 """ if not session_id: raise HTTPException(status_code=400, detail="缺少 session_id") @@ -380,20 +581,351 @@ async def api_chat( query, tmp_root = _materialize_uploads_to_temp(images, files, audio, text) try: _chat_start_core(session_id, query) - return _chat_complete_core(session_id, output_type=output_type) + return _chat_complete_core(session_id, output_type=output_type, agent_mode=agent_mode) finally: shutil.rmtree(tmp_root, ignore_errors=True) +@app.post("/api/human-action") +def api_human_action( + session_id: str = Form(...), + approval_id: str = Form(...), + decision: str = Form(...), + instruction: str = Form(""), + output_type: str = Form("text"), +) -> dict[str, Any]: + """用户对待确认操作(HITL)做出决策后,继续执行 Agent。 + + 输入: + session_id: 会话 ID。 + approval_id: 待确认项的 ID。 + decision: approve | reject | edit。 + instruction: 用户输入的补充文本(用于 edit 或 selection)。 + output_type: 回复的输出类型。 + + 输出: + {"sessionId": str, "sessions": list[str], "messages": list[dict]} + + 系统定位: + 前端确认框提交决策时调用的回调接口。 + """ + if not session_id: + raise HTTPException(status_code=400, detail="缺少 session_id") + if decision not in {"approve", "reject", "edit"}: + raise HTTPException(status_code=400, detail="不支持的人工处理结果") + hist = ui.load_ui_messages_for_session(session_id) + new_hist = ca.continue_after_human_action( + [*hist], + session_id=session_id, + approval_id=approval_id, + decision=decision, + instruction=instruction, + output_type=output_type, + ) + ids = ui.list_session_ids_ordered() + if session_id not in ids: + ids = [session_id, *ids] + return {"sessionId": session_id, "sessions": ids, "messages": _serialize_messages(new_hist)} + + +# ---------- 配置管理 API ---------- +@app.get("/api/config/agents") +def api_get_agent_configs() -> dict[str, Any]: + """获取所有 agent 配置列表。 + + 输出: {"agents": list[dict], "config_path": str} + """ + configs = load_agent_configs() + return { + "agents": [{"name": c.name, "system_prompt": c.system_prompt, "max_iterations": c.max_iterations, + "llm": {"model": c.llm.model, "api_key": c.llm.api_key, "base_url": c.llm.base_url, + "timeout": c.llm.timeout, "max_retries": c.llm.max_retries}, + "llm_model_id": c.llm_model_id, + "tools": c.tools, + "enabled": c.enabled, + "role": c.role} for c in configs], + "config_path": str(AGENT_CONFIG_PATH), + } + + +@app.post("/api/config/agents") +def api_save_agent_configs(data: dict[str, Any]) -> dict[str, Any]: + """保存 agent 配置列表。 + + 输入: {"agents": list[dict]} + 输出: {"success": bool, "config_path": str} + """ + agents = data.get("agents") if isinstance(data, dict) else None + if not isinstance(agents, list): + raise HTTPException(status_code=400, detail="缺少 agents 字段或格式不正确") + success = save_agent_configs(agents) + return {"success": success, "config_path": str(AGENT_CONFIG_PATH)} + + +@app.get("/api/config/tools") +def api_get_tool_configs() -> dict[str, Any]: + """获取所有工具配置列表。 + + 输出: {"tools": list[dict], "registered_tool_names": list[str], + "tool_parameters": dict, "forced_permissions": dict, "config_path": str} + """ + from agent.core.config_manager import _get_tool_parameters, get_tool_forced_permissions + configs = load_tool_configs() + return { + "tools": [{"name": c.name, "display_name": c.display_name, "description": c.description, + "permission": c.permission, "auto_execute_rules": c.auto_execute_rules, "enabled": c.enabled} + for c in configs], + "registered_tool_names": get_registered_tool_names(), + "tool_parameters": _get_tool_parameters(), + "forced_permissions": get_tool_forced_permissions(), + "config_path": str(TOOL_CONFIG_PATH), + } + + +@app.post("/api/config/tools") +def api_save_tool_configs(data: dict[str, Any]) -> dict[str, Any]: + """保存工具配置列表。 + + 输入: {"tools": list[dict]} + 输出: {"success": bool, "config_path": str} + """ + tools = data.get("tools") if isinstance(data, dict) else None + if not isinstance(tools, list): + raise HTTPException(status_code=400, detail="缺少 tools 字段或格式不正确") + success = save_tool_configs(tools) + # 保存后清除 tool_policy 的规则缓存,使新配置生效 + try: + from agent.core.tool_policy import reload_auto_execute_rules + reload_auto_execute_rules() + except Exception: + pass + return {"success": success, "config_path": str(TOOL_CONFIG_PATH)} + + +# ---------- 模型管理 API ---------- +@app.get("/api/config/models") +def api_get_models() -> dict[str, Any]: + """获取所有 LLM 模型配置。 + + 输出: {"models": list[dict]} + """ + return {"models": load_models()} + + +@app.post("/api/config/models") +def api_save_models(data: dict[str, Any]) -> dict[str, Any]: + """保存 LLM 模型配置列表。 + + 输入: {"models": list[dict]} + 输出: {"success": bool} + """ + models = data.get("models") if isinstance(data, dict) else None + if not isinstance(models, list): + raise HTTPException(status_code=400, detail="缺少 models 字段或格式不正确") + success = save_models(models) + return {"success": success} + + +@app.get("/api/config/env") +def api_get_env_config() -> dict[str, Any]: + """获取环境变量配置。 + + 输出: {"env": dict, "models": list[dict], "config_path": str} + """ + config = load_env_config() + models = config.get("models", []) + env_only = {k: v for k, v in config.items() if k != "models"} + return {"env": env_only, "models": models, "config_path": str(ENV_CONFIG_PATH)} + + +@app.post("/api/config/env") +def api_save_env_config(data: dict[str, Any]) -> dict[str, Any]: + """保存环境变量配置。 + + 输入: {"env": dict} + 输出: {"success": bool, "config_path": str} + """ + env = data.get("env") if isinstance(data, dict) else None + if not isinstance(env, dict): + raise HTTPException(status_code=400, detail="缺少 env 字段或格式不正确") + success = save_env_config(env) + return {"success": success, "config_path": str(ENV_CONFIG_PATH)} + # 先注册 API,再挂静态文件,避免 ``/`` 抢路由 @app.get("/api/health") def health() -> dict[str, str]: + """健康检查接口。 + + 输入: 无。 + + 输出: {"status": "ok"} + """ return {"status": "ok"} +@app.post("/api/config/reload") +def api_reload_configs() -> dict[str, Any]: + """热重载所有配置并重建 Agent 运行时,无需重启服务。 + + 输入: 无(或 {})。 + + 输出: {"success": bool, "agent": dict, "detail": str} + """ + import os as _os + try: + # 1. 重新读取 env_config.json 并写入 os.environ + from agent.utils.env_utils import _load_config + _load_config() + + # 2. 重建 LLM 实例 + from agent.core.llm import reload_llm + reload_llm() + + # 3. 清除并重新加载 tool_policy 缓存 + from agent.core.tool_policy import reload_auto_execute_rules + reload_auto_execute_rules() + + # 4. 重建主 Agent 运行时 + from agent.agents.main_agent import reload_main_agent_runtime + agent_info = reload_main_agent_runtime() + + return {"success": True, "agent": agent_info, "detail": "配置已热重载"} + except Exception as e: + return {"success": False, "agent": {}, "detail": str(e)} + + +# ---------- 多 Agent 图面板 API ---------- +@app.get("/api/graph/snapshot") +def api_get_graph_snapshot(session_id: str = "") -> dict[str, Any]: + """获取当前多 Agent 运行时的图结构快照,供前端 graph 面板渲染。""" + try: + from agent.agents.multi_agent_runtime import get_agent_graph_snapshot, build_multi_agent_runtime + from agent.agents.main_agent import main_agent_runtime + from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn + runtime = build_multi_agent_runtime(main_runtime=main_agent_runtime) + result = get_agent_graph_snapshot(runtime) + if session_id: + recorded = get_tool_calls_for_latest_turn(session_id) + if recorded: + existing = result.get("tool_calls", {}) or {} + main_calls = existing.get("main", []) + for r in recorded: + main_calls.append({ + "tool_name": r.get("name", ""), + "tool_args": {}, + "result_preview": r.get("result_text", "")[:80], + "status": r.get("status", "done"), + }) + result["tool_calls"] = {**existing, "main": main_calls} + return result + except Exception as e: + return { + "agents": [ + {"name": "main", "role": "main", "icon": "🧠", "status": "idle", "error_message": ""} + ], + "edges": [], + "events": [], + "tool_calls": {}, + "error": str(e), + } + + +@app.get("/api/graph/config") +def api_get_graph_config() -> dict[str, Any]: + """获取 graph 面板的初始配置(从 agent_config.json 读取各 agent 的 role/icon 等)。 + + 输出: + {"agents": [{"name": str, "role": str, "icon": str}, ...]} + """ + from agent.core.config_manager import load_agent_configs + configs = load_agent_configs() + role_icons = {"main": "🧠", "code": "💻", "research": "🔍", "doc": "📄", "gui": "🖥"} + agents_list = [] + for cfg in configs: + if cfg.enabled: + agents_list.append({ + "name": cfg.name, + "role": cfg.role, + "icon": role_icons.get(cfg.name, "🔧"), + "tools": cfg.tools, + }) + if not agents_list: + agents_list = [{"name": "main", "role": "main", "icon": "🧠", "tools": []}] + return {"agents": agents_list} + + +# ---------- 工具调用历史 API ---------- +@app.get("/api/tool-calls/{session_id}/live") +def api_get_live_tool_calls(session_id: str) -> dict[str, Any]: + """获取当前进行中的工具调用(图表执行期间实时轮询)。""" + try: + from agent.history.tool_call_recorder import get_live_tool_calls + return {"tool_calls": get_live_tool_calls(session_id)} + except Exception as e: + return {"tool_calls": [], "error": str(e)} + + +@app.get("/api/tool-calls/{session_id}/recent") +def api_get_recent_tool_calls(session_id: str) -> dict[str, Any]: + """获取最新 turn 的工具调用缩略(仅名称,无返回值)。""" + try: + from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn + calls = get_tool_calls_for_latest_turn(session_id) + result = [] + for c in calls: + result.append({ + "name": c.get("name", ""), + "args_summary": json.dumps(c.get("args", {}), ensure_ascii=False)[:40], + "status": "done", + "turn_id": c.get("turn_id", ""), + }) + return {"tool_calls": result} + except Exception as e: + return {"tool_calls": [], "error": str(e)} + + +@app.get("/api/tool-calls/{session_id}/detail") +def api_get_tool_call_detail(session_id: str) -> dict[str, Any]: + """获取最新 turn 的工具调用详情(含返回值和图片 base64)。""" + try: + from agent.history.tool_call_recorder import get_tool_calls_for_latest_turn + details = get_tool_calls_for_latest_turn(session_id) + # 图片文件转 data URL + import base64 + for d in details: + imgs = [] + for fp in d.get("result_files_full", []): + if os.path.isfile(fp): + with open(fp, "rb") as f: + b64 = base64.standard_b64encode(f.read()).decode("ascii") + ext = os.path.splitext(fp)[1].lower() + mime_map = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif"} + mime = mime_map.get(ext, "image/png") + imgs.append(f"data:{mime};base64,{b64}") + d["result_images"] = imgs + return {"details": details} + except Exception as e: + return {"details": [], "error": str(e)} + + +@app.get("/api/tool-calls/{session_id}/has-pending") +def api_has_pending_tool_calls(session_id: str) -> dict[str, Any]: + """检查是否有待处理的工具调用(thinking 状态时使用)。""" + try: + from agent.history.tool_call_recorder import list_turns + turns = list_turns(session_id) + has_tools = bool(turns) + # 判断最新 turn 是否已完成(有 assistant_response) + latest_complete = turns[0].get("assistant_response", "") if turns else "" + return {"has_recent": has_tools, "is_complete": bool(latest_complete)} + except Exception as e: + return {"has_recent": False, "is_complete": True, "error": str(e)} + + @app.get("/favicon.ico") def favicon() -> FileResponse: + """返回网站图标(favicon.ico/png/svg)。""" for name, mime in ( ("favicon.ico", "image/x-icon"), ("favicon.png", "image/png"), @@ -407,6 +939,14 @@ def favicon() -> FileResponse: @app.get("/image/{asset_name}") def serve_image_asset(asset_name: str) -> FileResponse: + """提供 web/image 目录下的静态图片资源。 + + 输入: + asset_name: 图片文件名。 + + 输出: + 图片文件响应。 + """ path = (_IMAGE_DIR / asset_name).resolve() try: path.relative_to(_IMAGE_DIR.resolve()) @@ -420,14 +960,17 @@ def serve_image_asset(asset_name: str) -> FileResponse: @app.get("/") def index_page() -> FileResponse: + """返回前端主页面 index.html。""" return FileResponse(_WEB_DIR / "index.html") @app.get("/app.css") def serve_css() -> FileResponse: + """返回样式文件 app.css。""" return FileResponse(_WEB_DIR / "app.css") @app.get("/app.js") def serve_js() -> FileResponse: + """返回前端脚本 app.js。""" return FileResponse(_WEB_DIR / "app.js") \ No newline at end of file diff --git a/src/web/ui_session.py b/src/web/ui_session.py index e18d021..099618f 100644 --- a/src/web/ui_session.py +++ b/src/web/ui_session.py @@ -1,3 +1,4 @@ +# ui_session.py """ Web/API 与会话 UI 状态:会话 id 列表、manifest、多模态 user 消息结构、附件落盘。 与 LangChain 链解耦;execute_agent 仍在 chatbot_agent 中,此处提供其依赖的同步与解析函数。 @@ -14,23 +15,58 @@ from agent.utils.agent_utils import ( CHAT_HISTORY_DB_FS, MULTI_INPUTS_ROOT, assistant_text, + get_history, ) from agent.utils.image_utils import IMAGE_FILE_EXTENSIONS -from agent.chatbot_agent import get_history _AUDIO_EXTENSIONS = {".wav", ".mp3", ".m4a", ".flac", ".ogg"} _memory_session_ids: set[str] = set() def manifest_path(session_id: str) -> str: + """返回会话 UI manifest 文件(ui_messages.json)的绝对路径。 + + 输入: + session_id: 会话 ID。 + + 输出: + manifest 文件的绝对路径字符串。 + + 系统定位: + 持久化 UI 层消息结构的唯一文件路径。 + + 可扩展性: + 可改为支持不同版本的 manifest 文件(如 version 4)。 + """ return os.path.join(MULTI_INPUTS_ROOT, session_id, "ui_messages.json") def session_dir(session_id: str) -> str: + """返回会话附件与 TTS 音频的存储目录绝对路径。 + + 输入: + session_id: 会话 ID。 + + 输出: + 会话目录的绝对路径字符串。 + + 系统定位: + 所有用户上传文件和助手生成的音频文件存放于此目录下。 + """ return os.path.join(MULTI_INPUTS_ROOT, session_id) def list_session_ids_ordered() -> list[str]: + """从 SQLite 按最近消息时间倒序列出所有 session_id。 + + 输入: 无。 + + 输出: + session_id 列表,按最后活跃时间降序排列。 + + 系统定位: + 用于前端展示会话列表,最近对话的会话排在前面。 + """ if not os.path.isfile(CHAT_HISTORY_DB_FS): return [] conn = sqlite3.connect(CHAT_HISTORY_DB_FS) @@ -44,6 +80,16 @@ def list_session_ids_ordered() -> list[str]: def new_timestamp_session_id() -> str: + """生成基于时间戳的唯一会话 ID,冲突时追加序号后缀。 + + 输入: 无。 + + 输出: + 新的会话 ID 字符串(如 "20260609_143022" 或 "20260609_143022_1")。 + + 系统定位: + 前端点击“新建会话”时调用的 ID 生成器。 + """ base = datetime.now().strftime("%Y%m%d_%H%M%S") existing = set(list_session_ids_ordered()) | _memory_session_ids if base not in existing: @@ -59,6 +105,15 @@ def new_timestamp_session_id() -> str: def prune_memory_session_ids() -> None: + """移除已写入数据库的内存 session_id 缓存,避免集合无限增长。 + + 输入: 无。 + + 输出: 无。 + + 系统定位: + 每次会话操作后调用,清理过期的内存缓存。 + """ persisted = set(list_session_ids_ordered()) for sid in list(_memory_session_ids): if sid in persisted: @@ -66,6 +121,16 @@ def prune_memory_session_ids() -> None: def delete_session(session_id: str) -> None: + """删除会话的 SQLite 消息、内存缓存、manifest 与附件目录。 + + 输入: + session_id: 要删除的会话 ID。 + + 输出: 无。 + + 系统定位: + API 层删除会话时彻底清理所有相关数据。 + """ if not session_id: return if os.path.isfile(CHAT_HISTORY_DB_FS): @@ -80,6 +145,17 @@ def delete_session(session_id: str) -> None: def _lc_content_for_ui_messages(content) -> str | list: + """将 LangChain 消息的 content 转为 UI 可展示的 str 或 list。 + + 输入: + content: LangChain 消息的 content 字段。 + + 输出: + str 或 list,供前端渲染。 + + 系统定位: + 从 SQLite 历史重建 UI 消息时的降级方案(无 manifest 时使用)。 + """ if isinstance(content, str): return content if isinstance(content, list): @@ -89,6 +165,17 @@ def _lc_content_for_ui_messages(content) -> str | list: def _load_ui_messages_from_db(session_id: str) -> list: + """从 SQLChatMessageHistory 重建简化版 UI 消息列表(无 manifest 元数据)。 + + 输入: + session_id: 会话 ID。 + + 输出: + 消息列表,每条包含 role 和 content(无 meta)。 + + 系统定位: + 当 manifest 文件缺失时,回退到 SQLite 历史展示。 + """ if not session_id: return [] rows: list[dict] = [] @@ -102,7 +189,17 @@ def _load_ui_messages_from_db(session_id: str) -> list: def _build_user_chat_content(query) -> str | list | None: - """根据上传文件扩展名直接生成多模态 content(与 LLM 输入格式一致)""" + """根据上传文件扩展名直接生成多模态 content(与 LLM 输入格式一致)。 + + 输入: + query: {"text": str, "files": list[str], "file_names": list[str]} + + 输出: + content 结构(str 或 list),无有效内容时返回 None。 + + 系统定位: + add_message 中将前端上传的原始 query 转为 LangChain 可识别的 content。 + """ parts: list = [] file_names = query.get("file_names") or [] for idx, filepath in enumerate(query.get("files", []) or []): @@ -127,6 +224,17 @@ def _build_user_chat_content(query) -> str | list | None: def _sanitize_filename_stem(name: str) -> str: + """清理文件名主干中的非法字符,空名回退为 file。 + + 输入: + name: 原始文件名。 + + 输出: + 清洗后的文件名主干(不含扩展名)。 + + 系统定位: + _next_session_copy_name 中生成安全的文件名前缀。 + """ stem = os.path.splitext(name or "")[0].strip() if not stem: return "file" @@ -141,6 +249,20 @@ def _sanitize_filename_stem(name: str) -> str: def _next_session_copy_name(dest_dir: str, session_id: str, original_name: str, ext: str) -> str: + """在会话目录内生成不冲突的附件副本文件名(含 session_id 与递增序号)。 + + 输入: + dest_dir: 会话目录。 + session_id: 会话 ID。 + original_name: 原始文件名。 + ext: 扩展名(含点)。 + + 输出: + 不冲突的文件名字符串。 + + 系统定位: + rewrite_user_message_files_to_session_dir 中为每个附件生成唯一副本名。 + """ ext = ext or "" base_name = _sanitize_filename_stem(original_name) prefix = f"{base_name}_{session_id}_" @@ -159,6 +281,17 @@ def _next_session_copy_name(dest_dir: str, session_id: str, original_name: str, def rewrite_user_message_files_to_session_dir(user_msg: dict, session_id: str) -> None: + """将 user 消息中的外部附件复制到会话目录并原地更新路径引用。 + + 输入: + user_msg: 用户消息字典(原地修改)。 + session_id: 会话 ID。 + + 输出: 无。 + + 系统定位: + 将临时上传的文件移动到持久化的会话目录,并修正消息中的路径。 + """ dest_dir = session_dir(session_id) os.makedirs(dest_dir, exist_ok=True) raw = user_msg.get("content") @@ -202,7 +335,18 @@ def rewrite_user_message_files_to_session_dir(user_msg: dict, session_id: str) - user_msg["content"] = new_list -def _serialize_user_msg_for_manifest(user_msg: dict) -> dict: +def _serialize_user_msg_for_manifest(user_msg: dict) -> dict: + """将 user 消息序列化为 manifest 可持久化的 content 列表结构。 + + 输入: + user_msg: 用户消息字典。 + + 输出: + 用于写入 manifest 的 user 条目字典(含相对路径引用)。 + + 系统定位: + sync_manifest_from_chat_history 中将 user 消息转为可持久化格式。 + """ raw = user_msg.get("content") content_list: list[dict] = [] if isinstance(raw, str): @@ -239,17 +383,26 @@ def _serialize_user_msg_for_manifest(user_msg: dict) -> dict: "relpath": rel.replace("\\", "/"), "name": piece.get("name") or os.path.basename(fp), }) - # 不再写入 image_description 字段 return {"content": content_list} def _manifest_user_entry_to_content(user_entry: dict) -> str | list: + """将 manifest 中的 user 条目还原为 UI/LLM 使用的 content(含附件绝对路径)。 + + 输入: + user_entry: manifest 中读取的 user 条目字典。 + + 输出: + content 结构(str 或 list),用于展示或模型输入。 + + 系统定位: + load_ui_messages_for_session 中从 manifest 重建用户消息。 + """ root_norm = os.path.normpath(MULTI_INPUTS_ROOT) parts_out: list = [] any_image_missing = False had_image_parts = False - # 新格式直接取 "content" content_data = user_entry.get("content") if content_data is None: return "" @@ -290,7 +443,6 @@ def _manifest_user_entry_to_content(user_entry: dict) -> str | list: else: parts_out.append({"type": "text", "text": f"[附件已丢失: {display_name}]"}) - # 不再处理 image_description(新格式中没有) if had_image_parts and any_image_missing: parts_out.append({"type": "text", "text": "[图片已丢失]"}) @@ -302,6 +454,14 @@ def _manifest_user_entry_to_content(user_entry: dict) -> str | list: def _serialize_assistant_entry(assistant_msg: dict | None) -> dict: + """将 assistant 消息及其 meta 序列化为 manifest assistant 条目。 + + 输入: + assistant_msg: assistant 消息字典。 + + 输出: + 用于写入 manifest 的 assistant 条目字典。 + """ ac = (assistant_msg or {}).get("content", "") meta = dict((assistant_msg or {}).get("meta") or {}) if isinstance(ac, str): @@ -323,36 +483,64 @@ def _serialize_assistant_entry(assistant_msg: dict | None) -> dict: "autoplay": bool(meta.get("autoplay") or (audio or {}).get("autoplay")), "audio_duration_seconds": (audio or {}).get("duration_seconds") if isinstance(audio, dict) else None, "audio_relpath": audio_relpath.replace("\\", "/") if audio_relpath else None, + "pending_action": meta.get("pending_action"), + "agent_mode": meta.get("agent_mode"), } +def _serialize_message_for_manifest(msg: dict) -> dict | None: + """按 role 分发单条 chat_history 消息到 user/assistant manifest 结构。 + + 输入: + msg: 消息字典。 + + 输出: + manifest 格式的消息条目,或 None(无法识别)。 + """ + role = msg.get("role") + if role == "user": + return {"role": "user", "user": _serialize_user_msg_for_manifest(msg)} + if role == "assistant": + return {"role": "assistant", "assistant": _serialize_assistant_entry(msg)} + return None + + def sync_manifest_from_chat_history(session_id: str, chat_history: list, latest_user_image_description: str) -> None: + """将当前 chat_history 全量写入 ui_messages.json(version 3 messages 格式)。 + + 输入: + session_id: 会话 ID。 + chat_history: 当前聊天历史列表。 + latest_user_image_description: 最新用户图片描述(保留参数,当前未使用)。 + + 输出: 无。 + + 系统定位: + 每轮对话结束后调用,持久化 UI 层消息结构。 + """ if not session_id: return mp = manifest_path(session_id) - turns_out: list[dict] = [] - ch = chat_history or [] - i = 0 - while i < len(ch): - if ch[i].get("role") != "user": - i += 1 - continue - user_msg = ch[i] - assistant_msg = ch[i + 1] if i + 1 < len(ch) and ch[i + 1].get("role") == "assistant" else None - - turns_out.append( - { - "user": _serialize_user_msg_for_manifest(user_msg), - "assistant": _serialize_assistant_entry(assistant_msg), - } - ) - i += 2 if assistant_msg else 1 + messages_out: list[dict] = [] + for msg in chat_history or []: + item = _serialize_message_for_manifest(msg) + if item is not None: + messages_out.append(item) os.makedirs(os.path.dirname(mp), exist_ok=True) with open(mp, "w", encoding="utf-8") as f: - json.dump({"version": 2, "turns": turns_out}, f, ensure_ascii=False, indent=2) + json.dump({"version": 3, "messages": messages_out}, f, ensure_ascii=False, indent=2) + def _assistant_manifest_entry_to_content(entry: dict) -> str | list: + """将 manifest assistant 条目还原为 UI content(文本与/或音频部件)。 + + 输入: + entry: manifest 中的 assistant 条目字典。 + + 输出: + content 结构(str 或 list),用于前端展示。 + """ text = (entry.get("text") or "").strip() output_type = entry.get("output_type") or "text" autoplay = bool(entry.get("autoplay")) @@ -386,7 +574,53 @@ def _assistant_manifest_entry_to_content(entry: dict) -> str | list: return parts +def _assistant_manifest_entry_to_message(asst_entry: dict) -> dict | None: + """将 manifest assistant 条目还原为完整 assistant 消息 dict(含 meta)。 + + 输入: + asst_entry: manifest 中的 assistant 条目字典。 + + 输出: + 消息字典,包含 role、content、meta;无有效内容时返回 None。 + """ + if not isinstance(asst_entry, dict): + asst_entry = {"text": str(asst_entry)} + asst_content = _assistant_manifest_entry_to_content(asst_entry) + if not str(asst_entry.get("text") or "").strip() and asst_content == "": + return None + + meta = { + "output_type": asst_entry.get("output_type", "text"), + "autoplay": asst_entry.get("autoplay", False), + } + if asst_entry.get("pending_action"): + meta["pending_action"] = asst_entry.get("pending_action") + if asst_entry.get("agent_mode"): + meta["agent_mode"] = asst_entry.get("agent_mode") + audio_relpath = asst_entry.get("audio_relpath") + if audio_relpath: + root_norm = os.path.normpath(MULTI_INPUTS_ROOT) + audio_path = os.path.normpath(os.path.join(root_norm, audio_relpath.replace("/", os.sep))) + if os.path.isfile(audio_path): + meta["audio"] = { + "path": audio_path, + "autoplay": asst_entry.get("autoplay", False), + "duration_seconds": asst_entry.get("audio_duration_seconds"), + "format": "wav", + "sample_rate": 24000, + } + return {"role": "assistant", "content": asst_content, "meta": meta} + + def _load_ui_messages_from_manifest(session_id: str) -> list | None: + """从 ui_messages.json 加载 UI 消息;文件缺失或解析失败返回 None。 + + 输入: + session_id: 会话 ID。 + + 输出: + 消息列表,或 None(表示无有效 manifest)。 + """ mp = manifest_path(session_id) if not os.path.isfile(mp): return None @@ -395,6 +629,19 @@ def _load_ui_messages_from_manifest(session_id: str) -> list | None: data = json.load(f) except (json.JSONDecodeError, OSError): return None + if "messages" in data: + out: list[dict] = [] + for item in data.get("messages") or []: + if not isinstance(item, dict): + continue + role = item.get("role") + if role == "user": + out.append({"role": "user", "content": _manifest_user_entry_to_content(item.get("user") or {})}) + elif role == "assistant": + assistant_msg = _assistant_manifest_entry_to_message(item.get("assistant") or {}) + if assistant_msg is not None: + out.append(assistant_msg) + return out if "turns" not in data: return None turns = data.get("turns") or [] @@ -405,34 +652,23 @@ def _load_ui_messages_from_manifest(session_id: str) -> list | None: for idx, t in enumerate(turns): out.append({"role": "user", "content": _manifest_user_entry_to_content(t.get("user") or {})}) asst_entry = t.get("assistant") or {} - if not isinstance(asst_entry, dict): - asst_entry = {"text": str(asst_entry)} - asst_content = _assistant_manifest_entry_to_content(asst_entry) - if idx == n - 1 and not str(asst_entry.get("text") or "").strip() and asst_content == "": + assistant_msg = _assistant_manifest_entry_to_message(asst_entry) + if assistant_msg is None and idx == n - 1: continue - - # 从扁平存储重建与 execute_agent 产物相同的 meta 字典 - meta = { - "output_type": asst_entry.get("output_type", "text"), - "autoplay": asst_entry.get("autoplay", False), - } - audio_relpath = asst_entry.get("audio_relpath") - if audio_relpath: - root_norm = os.path.normpath(MULTI_INPUTS_ROOT) - audio_path = os.path.normpath(os.path.join(root_norm, audio_relpath.replace("/", os.sep))) - if os.path.isfile(audio_path): - meta["audio"] = { - "path": audio_path, - "autoplay": asst_entry.get("autoplay", False), - "duration_seconds": asst_entry.get("audio_duration_seconds"), - "format": "wav", - "sample_rate": 24000, - } - out.append({"role": "assistant", "content": asst_content, "meta": meta}) + if assistant_msg is not None: + out.append(assistant_msg) return out def load_ui_messages_for_session(session_id: str) -> list: + """加载会话 UI 消息:优先 manifest,回退 SQLite 历史。 + + 输入: + session_id: 会话 ID。 + + 输出: + 消息列表(总是有效列表,可能为空)。 + """ if not session_id: return [] m = _load_ui_messages_from_manifest(session_id) @@ -442,11 +678,35 @@ def load_ui_messages_for_session(session_id: str) -> list: def rewrite_user_files_for_last_turn(chat_history: list, session_id: str) -> None: + """若末条为 user 消息,将其附件复制到会话目录。 + + 输入: + chat_history: 聊天历史列表。 + session_id: 会话 ID。 + + 输出: 无。 + + 系统定位: + API 层添加用户消息后立即调用,落盘附件。 + """ if session_id and chat_history and chat_history[-1].get("role") == "user": rewrite_user_message_files_to_session_dir(chat_history[-1], session_id) def add_message(chat_history, query): + """将上传 query 转为 user 消息追加到 chat_history;无有效内容时返回 None。 + + 输入: + chat_history: 现有聊天历史。 + query: {"text": str, "files": list[str], "file_names": list[str]} + + 输出: + (new_chat_history, cleared_query) 元组; + cleared_query 在旧 API 兼容中保留,新调用可忽略。 + + 系统定位: + API 层构造用户消息的核心函数。 + """ chat_history = chat_history or [] user_content = _build_user_chat_content(query) if user_content is None: @@ -456,6 +716,17 @@ def add_message(chat_history, query): def clear_ui_and_manifest(session_id: str): + """清空会话 manifest 并返回空 chat_history 与重置后的 query 状态。 + + 输入: + session_id: 会话 ID。 + + 输出: + (empty_chat_history, reset_query) 元组。 + + 系统定位: + 清空会话时重置 UI 状态。 + """ if session_id: mp = manifest_path(session_id) os.makedirs(os.path.dirname(mp), exist_ok=True)