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:
2026-05-14 09:43:48 +08:00
parent 8a12c30141
commit 30ab74ffb2
149 changed files with 12293 additions and 2812 deletions

View File

@ -18,7 +18,7 @@
└─ future channels未来扩展入口
└─ AgentService统一服务层所有入口都先汇总到这里
├─ MainAgentRouter自动判断 simple / task
├─ MainAgentRouterLLM 语义判断 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_title5-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=Falsesimple_chat 默认关闭
skip SkillAssembler不激活 skill不注入 skill 正文
├─ if include_skill_assembly=TrueTask 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 / planteam 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_contextsmissing skill draft 生成的 ephemeral skill guidance
│ └─ learning_candidate_enabled=False默认只写 receipts不绕过 Task feedback gate
│ ├─ inherited_pinned_skill_contextsmissing 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 -> AgentLooppublished pinned skill 必须注入)
│ ├─ pinned_skill_contexts -> AgentLoopdraft-only ephemeral skill 必须注入)
│ ├─ pinned_skill_contexts -> AgentLoopephemeral 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_descriptionTask-aware queryTask 描述 / 当前用户消息 / 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.mdSkillAssembler 内部读取候选正文)
├─ 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 @@ TaskExecutionPlannerTask 内部执行规划)
│ ├─ 从 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内部消息总线
├─ ChannelAdapterTelegram / Slack / Email / WhatsApp 等只作为 adapter
├─ inbound -> AgentService.handle_inbound_message(...)(外部消息进入 AgentService
─ outbound <- OutboundMessageAgentService 返回结构化输出消息)
─ outbound <- OutboundMessageAgentService 返回结构化输出消息)
└─ ChannelManager按 message.channel 分发 outbound
```
---