feat(engine): 添加MCP连接管理和工具集成功能
- 集成MCP连接管理器,支持MCP服务器连接 - 添加多种内置工具:ClarifyTool、CronTool、DelegateTool、ExecuteCodeTool、 PatchFileTool、ProcessTool、SendMessageTool、SpawnTool、TerminalTool、 TodoTool、WebFetchTool、WebSearchTool、WriteFileTool等 - 实现工具注册和装配功能 - 添加技能选择上下文参数 - 支持思考模式控制参数thinking_enabled feat(coordinator): 重构任务执行计划器参数命名 - 将learning_candidate_enabled重命名为allow_candidate_generation - 更新TeamGraphScheduler中的参数传递 - 修改LocalAgentRunner中的相关参数处理 - 更新README文档中的相应描述 refactor(context): 标准化工具调用参数格式 - 添加_json导入用于参数序列化 - 实现_provider_tool_calls方法标准化OpenAI兼容的工具调用载荷 - 修复工具调用中参数非字符串类型的序列化问题 refactor(session): 优化消息历史记录过滤逻辑 - 修改get_messages_as_conversation为基于运行状态过滤消息 - 排除未完成、失败或错误结束的运行记录 - 改进对话历史的可见性控制机制 fix(store): 修复FTS索引重建逻辑 - 添加异常处理防止FTS索引创建失败 - 实现_rebuild_fts_index方法重新构建全文搜索索引 - 优化索引触发器和表的维护流程
This commit is contained in:
@ -18,7 +18,7 @@
|
||||
└─ future channels(未来扩展入口)
|
||||
│
|
||||
└─ AgentService(统一服务层:所有入口都先汇总到这里)
|
||||
├─ MainAgentRouter(自动判断 simple / task)
|
||||
├─ MainAgentRouter(LLM 语义判断 simple / continue task / new task / close / abandon)
|
||||
├─ create_loop()(创建 AgentLoop 运行核心)
|
||||
├─ start()(启动后台运行模式)
|
||||
├─ submit_direct()(把任务提交到运行队列)
|
||||
@ -73,15 +73,20 @@ AgentService.process_direct / submit_direct(聊天入口统一进入服务层
|
||||
│
|
||||
├─ resolve session_id(复用请求 session,或生成新 session)
|
||||
├─ task_service.get_latest_open_task(session_id)(查找同会话未关闭 Task)
|
||||
├─ MainAgentRouter.classify(message, active_task)(自动分类)
|
||||
├─ MainAgentRouter.classify(message, active_task, recent_messages)(LLM 语义分类)
|
||||
│ ├─ simple(简单问题)
|
||||
│ │ └─ runner(message)(直接走原有 AgentLoop,不创建 Task)
|
||||
│ │ └─ runner(message, include_skill_assembly=False, include_tools=False)(不创建 Task,不跑 skills/tools)
|
||||
│ │
|
||||
│ └─ task(复杂任务)
|
||||
│ ├─ if no active task or user starts new task
|
||||
│ │ └─ TaskService.create_task(...)(内部创建 Task)
|
||||
│ ├─ else
|
||||
│ │ └─ reuse active Task(复用 awaiting_feedback / needs_revision Task)
|
||||
│ ├─ continue_task(继续当前 Task)
|
||||
│ │ └─ reuse active Task(只要话题没有完全无关,就继续当前 open Task)
|
||||
│ │
|
||||
│ ├─ new_task(明确开启新任务)
|
||||
│ │ └─ TaskService.create_task(...)(内部创建 Task,并保存 short_title)
|
||||
│ │
|
||||
│ ├─ close_task / abandon_task(用户明确结束或放弃)
|
||||
│ │ └─ TaskService.close_task / abandon_task(关闭当前 Task)
|
||||
│ │
|
||||
│ └─ task execution
|
||||
│ └─ AgentService._run_task_mode(...)(进入 Task 模式执行)
|
||||
```
|
||||
|
||||
@ -92,6 +97,7 @@ TaskService(内部 Task 状态机)
|
||||
│ ├─ task_id
|
||||
│ ├─ session_id
|
||||
│ ├─ goal / description / constraints
|
||||
│ ├─ metadata.short_title(5-15 字左右的短标题,用于前端当前任务标识)
|
||||
│ ├─ status
|
||||
│ │ ├─ open
|
||||
│ │ ├─ running
|
||||
@ -167,17 +173,32 @@ AgentLoop.process_direct(task)(直接执行一轮用户任务)
|
||||
│ ├─ auxiliary_provider(辅助模型调用器,用于选 skill 等)
|
||||
│ └─ embedding_runtime(向量模型配置,用于语义召回)
|
||||
│
|
||||
├─ skill_assembler.assemble(...)(选择本轮应该激活哪些 skill)
|
||||
│ ├─ SkillsLoader.build_selection_candidates()(列出候选技能摘要)
|
||||
│ ├─ embedding retrieve skill candidates(用向量召回相关技能)
|
||||
│ ├─ LLM select activated skills(让模型从候选里选择技能)
|
||||
│ └─ 返回 activated skills(返回本轮被激活的技能)
|
||||
│ ├─ name(技能名称)
|
||||
│ ├─ content(技能正文)
|
||||
│ ├─ version(技能版本)
|
||||
│ ├─ content_hash(技能内容哈希,用于追踪)
|
||||
│ ├─ activation_reason(为什么激活)
|
||||
│ └─ tool_hints(技能建议使用哪些工具)
|
||||
├─ if include_skill_assembly=False(simple_chat 默认关闭)
|
||||
│ └─ skip SkillAssembler(不激活 skill,不注入 skill 正文)
|
||||
│
|
||||
├─ if include_skill_assembly=True(Task mode 默认开启,在 Task 创建/复用和规划之后执行)
|
||||
│ └─ skill_assembler.assemble(...)(选择本轮应该激活哪些 published skill)
|
||||
│ ├─ input task_description = skill_selection_context or current user input
|
||||
│ │ ├─ Task goal / description
|
||||
│ │ ├─ current user request
|
||||
│ │ ├─ attempt / revision / team synthesis phase
|
||||
│ │ ├─ validation feedback(重试时)
|
||||
│ │ ├─ team summary / plan(team synthesis 时)
|
||||
│ │ └─ previously activated skills(只作为 reuse bias,不是 pinned)
|
||||
│ ├─ SkillsLoader.build_selection_candidates()(列出候选技能摘要)
|
||||
│ ├─ embedding retrieve skill candidates(用向量召回相关技能)
|
||||
│ ├─ LLM shortlist candidate names(先用摘要粗选少量候选)
|
||||
│ │ └─ if retrieved candidates <= max_detailed_candidates -> skip shortlist
|
||||
│ ├─ SkillsLoader.load_published_skill(...)(系统侧内部读取粗选候选正文,不暴露 skill_view 给主 Agent)
|
||||
│ ├─ LLM final select activated skills(结合候选正文做最终选择)
|
||||
│ ├─ if no matching skill -> return [] and continue run without skills
|
||||
│ └─ 返回 activated skills(返回本轮被激活的技能)
|
||||
│ ├─ name(技能名称)
|
||||
│ ├─ content(技能正文)
|
||||
│ ├─ version(技能版本)
|
||||
│ ├─ content_hash(技能内容哈希,用于追踪)
|
||||
│ ├─ activation_reason(为什么激活)
|
||||
│ └─ tool_hints(技能建议使用哪些工具)
|
||||
│
|
||||
├─ ContextBuilder.build_skill_activation_messages(...)(把激活技能变成模型可读消息)
|
||||
├─ 构造 SkillActivationReceipt[](构造技能激活收据)
|
||||
@ -188,7 +209,7 @@ AgentLoop.process_direct(task)(直接执行一轮用户任务)
|
||||
│ ├─ receipts(技能激活收据)
|
||||
│ └─ activation_messages(实际注入给模型的技能消息)
|
||||
│
|
||||
├─ tool_assembler.assemble(...)(选择本轮应该暴露哪些工具)
|
||||
├─ tool_assembler.assemble(...)(选择本轮应该暴露哪些工具;simple_chat 默认跳过)
|
||||
│ ├─ always tools(默认总是可用的工具)
|
||||
│ ├─ activated skill tool hints(被激活技能推荐的工具)
|
||||
│ ├─ embedding retrieve tools(用向量召回相关工具)
|
||||
@ -207,6 +228,7 @@ AgentLoop.process_direct(task)(直接执行一轮用户任务)
|
||||
│ └─ append current user input(追加当前用户输入)
|
||||
│
|
||||
├─ session_manager.update_system_prompt(...)(把本轮 system prompt 快照写回会话)
|
||||
├─ session_manager.append_message(event_type="skill_selection_context_snapshotted", hidden)(完整记录 skill query)
|
||||
├─ session_manager.append_message(event_type="system_prompt_snapshotted", hidden)(记录隐藏事件:system prompt 快照)
|
||||
├─ session_manager.append_message(event_type="user_message_added")(记录可见事件:用户消息)
|
||||
│
|
||||
@ -214,12 +236,12 @@ AgentLoop.process_direct(task)(直接执行一轮用户任务)
|
||||
│
|
||||
├─ 成功时(模型正常结束)
|
||||
│ ├─ session_manager.append_message(event_type="run_completed", hidden)(记录隐藏事件:运行完成)
|
||||
│ └─ _record_skill_learning(...)(记录技能使用效果,进入学习闭环)
|
||||
│ └─ _record_run_receipts(...)(记录运行证据,不生成学习候选)
|
||||
│
|
||||
├─ 失败时(运行中出现异常)
|
||||
│ ├─ append assistant error message(写入 assistant 错误消息)
|
||||
│ ├─ session_manager.append_message(event_type="run_failed", hidden)(记录隐藏事件:运行失败)
|
||||
│ └─ _record_skill_learning(...)(即使失败也记录技能效果)
|
||||
│ └─ _record_run_receipts(...)(即使失败也记录运行证据)
|
||||
│
|
||||
└─ return AgentRunResult(返回本轮结果)
|
||||
├─ session_id(会话编号)
|
||||
@ -242,6 +264,7 @@ AgentLoop.process_direct(task)(直接执行一轮用户任务)
|
||||
```text
|
||||
tool loop(工具调用循环)
|
||||
│
|
||||
├─ session_manager.append_message(event_type="llm_request_snapshotted", hidden)(完整记录本次 provider messages / tools)
|
||||
├─ provider.chat(messages, tools=schemas)(把消息和工具 schema 发给模型)
|
||||
├─ session_manager.update_usage(...)(累计 token 用量)
|
||||
├─ session_manager.append_message(event_type="assistant_message_added")(记录 assistant 回复)
|
||||
@ -259,10 +282,10 @@ tool loop(工具调用循环)
|
||||
|
||||
---
|
||||
|
||||
## 6. Skills Learning Baseline
|
||||
## 6. Run Evidence / Skill Effect Recording
|
||||
|
||||
```text
|
||||
AgentLoop._record_skill_learning(...)(记录本轮技能效果)
|
||||
AgentLoop._record_run_receipts(...)(记录本轮运行证据;不直接学习)
|
||||
│
|
||||
├─ 构造 RunRecord(构造本轮运行记录)
|
||||
│ ├─ run_id(运行编号)
|
||||
@ -286,11 +309,7 @@ AgentLoop._record_skill_learning(...)(记录本轮技能效果)
|
||||
│ ├─ RunMemoryStore.append_skill_effect(...)(把 SkillEffectRecord 写入 memory/runs/skill-effects.jsonl)
|
||||
│ ├─ SkillLearningService.rescore_skill_versions()(重新统计每个技能版本表现)
|
||||
│ │ └─ SkillLearningStore.update_performance_snapshot(...)(更新表现快照)
|
||||
│ └─ optionally build learning candidates(默认不生成;只由反馈门控显式触发)
|
||||
│ ├─ revise_skill(建议修改已有技能)
|
||||
│ ├─ new_skill(建议创建新技能)
|
||||
│ ├─ merge_skills(建议合并相似技能)
|
||||
│ └─ retire_skill(建议退役长期不用的技能)
|
||||
│ └─ never build learning candidates in runtime hot path(运行完成时永不生成候选)
|
||||
│
|
||||
└─ session_manager.append_message(...)(记录隐藏事件:技能效果快照)
|
||||
├─ event_type="skill_effects_snapshotted"(技能效果已快照)
|
||||
@ -298,10 +317,23 @@ AgentLoop._record_skill_learning(...)(记录本轮技能效果)
|
||||
└─ payload(隐藏数据)
|
||||
├─ run_record(本轮运行记录)
|
||||
├─ skill_effects(技能效果记录)
|
||||
├─ learning_candidate_enabled(本轮是否允许生成候选,默认 false)
|
||||
├─ candidate_generation_allowed(本轮是否允许生成候选;runtime 固定 false)
|
||||
└─ learning_candidates(学习候选;默认空)
|
||||
```
|
||||
|
||||
```text
|
||||
runtime invariant(运行期不直接学习)
|
||||
│
|
||||
├─ run completed / run failed
|
||||
│ └─ 只写 RunRecord + SkillEffectRecord + performance snapshot
|
||||
│
|
||||
├─ simple chat
|
||||
│ └─ 不创建 Task,不触发 learning candidate
|
||||
│
|
||||
└─ Task attempt / sub-agent run
|
||||
└─ 只留下证据,等待 feedback gate 决定是否学习
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Chat Feedback / Learning Gate
|
||||
@ -328,17 +360,20 @@ POST /api/chat/feedback(聊天反馈接口,不是 Task 管理 API)
|
||||
├─ satisfied
|
||||
│ ├─ if validation accepted
|
||||
│ │ ├─ Task status -> closed
|
||||
│ │ └─ SkillLearningService.build_learning_candidates()
|
||||
│ │ └─ SkillLearningService.build_learning_candidates_for_task(task_id, trigger_run_id)
|
||||
│ └─ if validation not accepted
|
||||
│ └─ 记录人工接受但保留验证风险
|
||||
│ └─ 记录人工接受但保留验证风险;不自动生成 learning candidate
|
||||
│
|
||||
├─ revise
|
||||
│ ├─ Task status -> needs_revision
|
||||
│ └─ 下一条用户消息默认复用该 Task
|
||||
│ ├─ 更新 run / skill effect 为需修订证据
|
||||
│ └─ 下一条用户消息默认复用该 Task;不生成 learning candidate
|
||||
│
|
||||
└─ abandon
|
||||
├─ Task status -> abandoned
|
||||
└─ write Failure Memory(不生成成功 Skill draft)
|
||||
├─ 更新 run / skill effect 为失败证据
|
||||
├─ 追加 task_failure_evidence_recorded 隐藏事件
|
||||
└─ 默认不写主 memory,不生成成功 Skill draft
|
||||
```
|
||||
|
||||
---
|
||||
@ -356,15 +391,15 @@ TeamService.run_team(...)(内部 team 执行入口,不暴露产品级 Task A
|
||||
│ │ └─ reserved strategies: moa / hierarchy / heavy / group_chat / forest / maker / router
|
||||
│ ├─ provider_bundle_factory(node)(推荐:每个节点拿 fresh provider bundle)
|
||||
│ ├─ inherited_pinned_skills(主 agent 明确委派给 sub-agent 的 pinned skills)
|
||||
│ ├─ inherited_pinned_skill_contexts(missing skill draft 生成的 ephemeral skill guidance)
|
||||
│ └─ learning_candidate_enabled=False(默认只写 receipts,不绕过 Task feedback gate)
|
||||
│ ├─ inherited_pinned_skill_contexts(missing skill 生成的一次性 ephemeral guidance)
|
||||
│ └─ allow_candidate_generation=False(默认只写 receipts,不绕过 Task feedback gate)
|
||||
│
|
||||
├─ LocalAgentRunner.run(envelope)
|
||||
│ ├─ 生成 child_session_id
|
||||
│ ├─ parent_session_id -> 主 session(建立 session lineage)
|
||||
│ ├─ AgentLoop.process_direct / submit_direct(...)(复用主 AgentLoop / ContextBuilder / ToolAssembler / SkillAssembler / MemoryService)
|
||||
│ ├─ pinned_skill_names -> AgentLoop(published pinned skill 必须注入)
|
||||
│ ├─ pinned_skill_contexts -> AgentLoop(draft-only ephemeral skill 必须注入)
|
||||
│ ├─ pinned_skill_contexts -> AgentLoop(ephemeral guidance 只在本次 run 注入)
|
||||
│ └─ provider_bundle + node model/provider override 禁止混用
|
||||
│
|
||||
├─ strategy execution
|
||||
@ -522,7 +557,6 @@ SkillsLoader(技能加载器)
|
||||
├─ build_skills_summary()(构造技能摘要索引)
|
||||
├─ build_selection_candidates()(构造给 SkillAssembler 的候选摘要)
|
||||
├─ list_skill_supporting_files()(列出技能支持文件)
|
||||
├─ view_skill()(查看技能正文或支持文件)
|
||||
└─ get_always_skills()(获取 always 类型技能)
|
||||
```
|
||||
|
||||
@ -530,13 +564,17 @@ SkillsLoader(技能加载器)
|
||||
SkillAssembler(技能选择器)
|
||||
│
|
||||
├─ input(输入)
|
||||
│ ├─ task_description(用户任务描述)
|
||||
│ ├─ task_description(Task-aware query:Task 描述 / 当前用户消息 / previous skills / attempt context / validation revision context / team context)
|
||||
│ ├─ candidate skill summaries(候选技能摘要)
|
||||
│ ├─ embedding runtime(向量模型配置)
|
||||
│ └─ selector provider/model(用于选择技能的模型)
|
||||
│
|
||||
├─ embedding retrieve candidates(先用向量召回相关技能)
|
||||
├─ LLM select names(再让 LLM 选择技能名)
|
||||
├─ LLM shortlist names(用摘要粗选需要查看正文的候选)
|
||||
│ └─ skip when candidate count <= max_detailed_candidates(候选很少时直接读取正文)
|
||||
├─ internal load shortlisted SKILL.md(SkillAssembler 内部读取候选正文)
|
||||
├─ LLM final select names(结合候选正文选择最终技能名)
|
||||
├─ no match returns [](没有对应 published skill 时返回空,不阻塞任务)
|
||||
└─ return SkillContext[](返回技能上下文)
|
||||
├─ name(技能名)
|
||||
├─ content(技能正文)
|
||||
@ -586,7 +624,6 @@ ToolRegistry(工具注册表)
|
||||
│
|
||||
├─ echo(回显工具)
|
||||
├─ memory(写入/管理长期记忆)
|
||||
├─ skill_view(查看完整 skill)
|
||||
├─ session_search(搜索会话历史)
|
||||
├─ list_directory(列目录)
|
||||
├─ read_file(读文件)
|
||||
@ -599,7 +636,7 @@ ToolAssembler(工具选择器)
|
||||
├─ selected = always tools(先加入默认工具)
|
||||
├─ selected += activated skill tool hints(再加入技能推荐工具)
|
||||
├─ selected += embedding top-k tools(再用向量召回任务相关工具)
|
||||
└─ return ToolSpec[](返回本轮可用工具列表)
|
||||
└─ return ToolSpec[](返回本轮可用工具列表;不通过工具动态加载 skill)
|
||||
```
|
||||
|
||||
```text
|
||||
@ -703,11 +740,11 @@ TaskExecutionPlanner(Task 内部执行规划)
|
||||
│ ├─ 从 published skill catalog 检索候选
|
||||
│ ├─ 按 skill_query / required_capabilities / node task 选择 skill
|
||||
│ ├─ 命中 published skill 后写入 graph.nodes[].inherited_pinned_skills
|
||||
│ └─ 无命中时创建 draft-only skill,并写入 graph.nodes[].inherited_pinned_skill_contexts
|
||||
│ └─ 无命中时创建 ephemeral guidance,并写入 graph.nodes[].inherited_pinned_skill_contexts
|
||||
│
|
||||
└─ TaskExecutionPlan
|
||||
├─ graph.nodes[].agent 只是 generic runtime trace identity
|
||||
└─ to_event_payload() 写入 skill_queries / selected_skill_names / generated_skill_draft_ids / skill_resolution_report
|
||||
└─ to_event_payload() 写入 skill_queries / selected_skill_names / ephemeral_guidance_ids / skill_resolution_report
|
||||
```
|
||||
|
||||
```text
|
||||
@ -748,8 +785,21 @@ Frontend process projection
|
||||
```text
|
||||
Learning pipeline
|
||||
│
|
||||
├─ evidence recording
|
||||
│ ├─ every run -> RunRecord
|
||||
│ ├─ activated skills -> SkillEffectRecord
|
||||
│ └─ no candidates generated here
|
||||
│
|
||||
├─ feedback gate
|
||||
│ └─ validation accepted + satisfied 才生成 learning candidate
|
||||
│ ├─ validation accepted + satisfied -> scoped learning candidate
|
||||
│ ├─ validation rejected + satisfied -> 记录人工接受风险,不生成候选
|
||||
│ ├─ revise -> 保持 Task 打开,不生成候选
|
||||
│ └─ abandon -> 失败证据,不写主 memory,不生成成功候选
|
||||
│
|
||||
├─ scoped candidate generation
|
||||
│ ├─ source = current task run_ids
|
||||
│ ├─ no published skill -> new_skill
|
||||
│ └─ published skill used -> revise_skill
|
||||
│
|
||||
├─ SkillLearningPipelineService
|
||||
│ ├─ candidate -> queued / synthesizing
|
||||
@ -799,6 +849,12 @@ Web(网页入口)
|
||||
│ ├─ agent_service.submit_direct(...)(把用户消息提交给 AgentService)
|
||||
│ └─ return WebChatResponse(返回模型回复 + run/task/validation 元数据)
|
||||
│
|
||||
├─ WS /ws/{session_id}(网页 WebSocket 适配层)
|
||||
│ ├─ ping -> pong
|
||||
│ ├─ message -> agent_service.submit_direct(...)
|
||||
│ ├─ return status / assistant message(携带 run/task/validation 元数据)
|
||||
│ └─ return session_updated(通知前端刷新 session/process)
|
||||
│
|
||||
└─ POST /api/chat/feedback(聊天反馈接口)
|
||||
├─ validate WebChatFeedbackRequest
|
||||
├─ agent_service.submit_feedback(...)
|
||||
@ -820,8 +876,10 @@ Skills learning admin API
|
||||
Gateway(消息通道入口)
|
||||
│
|
||||
├─ MessageBus(内部消息总线)
|
||||
├─ ChannelAdapter(Telegram / Slack / Email / WhatsApp 等只作为 adapter)
|
||||
├─ inbound -> AgentService.handle_inbound_message(...)(外部消息进入 AgentService)
|
||||
└─ outbound <- OutboundMessage(AgentService 返回结构化输出消息)
|
||||
├─ outbound <- OutboundMessage(AgentService 返回结构化输出消息)
|
||||
└─ ChannelManager(按 message.channel 分发 outbound)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user