# Beaver Backend Flow 这份文档只保留**树形运行结构**。 - 原理、参考项目边界、长期蓝图:看 `change.md` - 施工顺序、阶段目标、完成标准:看 `施工指南.md` --- ## 1. 总入口 ```text 用户输入(用户在不同入口发来一句话或一个任务) │ ├─ CLI(命令行入口) ├─ Web(网页前端入口) ├─ Gateway(消息通道入口,比如以后接 Slack / Telegram) └─ future channels(未来扩展入口) │ └─ AgentService(统一服务层:所有入口都先汇总到这里) ├─ Intent Agent / MainAgentRouter(第一层意图判断:simple chat / continue task / create task / close / abandon) ├─ load intent-agent-router skill(内部 skill 指引:只做路由,不回答用户,不使用工具) ├─ classify(...)(LLM 语义判断) ├─ session hidden event: intent_agent_decision_snapshotted(记录选择 simple_chat / create_task / continue_task 等) ├─ create_loop()(创建 AgentLoop 运行核心) ├─ start()(启动后台运行模式) ├─ submit_direct()(把任务提交到运行队列) ├─ process_direct()(直接处理一次任务,不走队列) ├─ submit_feedback()(记录聊天反馈并驱动内部 Task 状态) ├─ stop()(停止接收新任务,并等待队列收尾) ├─ shutdown()(停止运行并释放资源) └─ close()(关闭已经创建的 runtime) ``` --- ## 2. Boot / Loader ```text AgentService.create_loop()(服务层创建运行核心) │ └─ AgentLoop(profile, loader)(Agent 主循环:真正跑任务的核心对象) │ └─ AgentLoop.boot()(启动前装配依赖) │ └─ EngineLoader.load()(加载所有运行时模块) ├─ SessionManager(会话管理:保存聊天记录和隐藏事件) ├─ MemoryStore(长期记忆存储:真正落盘的 curated memory) ├─ MemoryService(记忆服务:运行时访问 memory 的唯一入口) ├─ RunMemoryStore(运行记录存储:保存每次 run 的结果) ├─ SkillLearningStore(技能学习存储:保存表现统计和学习候选) ├─ ToolRegistry(工具注册表:登记系统有哪些工具) ├─ ToolAssembler(工具选择器:决定本轮暴露哪些工具) ├─ ToolExecutor(工具执行器:真正调用工具) ├─ SkillsLoader(技能目录加载器:只加载可用技能) ├─ SkillAssembler(技能选择器:决定本轮激活哪些 skill) ├─ SkillSpecStore(技能生命周期存储:保存版本、草稿、审核) ├─ DraftService(草稿服务:创建 skill draft) ├─ ReviewService(审核服务:approve / reject draft) ├─ SkillPublisher(发布服务:publish / disable / rollback) ├─ EvidenceSelector(证据选择器:为学习闭环挑选历史证据) ├─ SkillDraftSynthesizer(草稿合成器:让 LLM 生成 skill draft) ├─ SkillLearningService(技能学习服务:生成学习候选和草稿) ├─ TaskService(内部 Task 服务:自动 Task 化、状态、事件、反馈) ├─ TaskExecutionPlanner(Task 执行规划器:决定 single / team) ├─ ValidationService(结果验证服务:Task run 完成后的自动验证) └─ ContextBuilder(上下文构建器:拼 system prompt 和 messages) ``` --- ## 3. Main Agent Routing / Internal Task ```text AgentService.process_direct / submit_direct(聊天入口统一进入服务层) │ ├─ resolve session_id(复用请求 session,或生成新 session) ├─ task_service.get_latest_open_task(session_id)(查找同会话未关闭 Task) ├─ MainAgentRouter.classify(message, active_task, recent_messages, intent-agent-router skill)(Intent Agent 语义分类) │ ├─ Intent Agent 只返回 JSON 路由结果,不直接回答用户 │ ├─ Intent Agent 没有 tools;凡是需要工具、实时/外部数据、文件、执行、验证的请求都应进入 Task │ ├─ session hidden event: intent_agent_decision_snapshotted(调试日志展示 choice / reason / short_title) │ ├─ simple(简单问题) │ │ └─ runner(message, include_skill_assembly=False, include_tools=False)(不创建 Task,不跑 skills/tools) │ │ │ ├─ 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 模式执行) ``` ```text TaskService(内部 Task 状态机) │ ├─ TaskRecord │ ├─ task_id │ ├─ session_id │ ├─ goal / description / constraints │ ├─ metadata.short_title(5-15 字左右的短标题,用于前端当前任务标识) │ ├─ status │ │ ├─ open │ │ ├─ running │ │ ├─ validating │ │ ├─ awaiting_feedback │ │ ├─ needs_revision │ │ ├─ closed │ │ └─ abandoned │ ├─ run_ids │ ├─ skill_names │ ├─ validation_result │ └─ feedback │ └─ TaskEvent ├─ created ├─ run_started ├─ run_completed ├─ validated ├─ feedback_satisfied ├─ feedback_revise └─ feedback_abandon ``` ```text Task Mode Execution(复杂任务执行) │ ├─ attempt 1 │ ├─ task_service.start_run(...) │ ├─ TaskExecutionPlanner.plan(...)(LLM 规划 single / team) │ ├─ session hidden event: task_execution_planned │ ├─ if plan.mode == team │ │ ├─ TeamService.run_team(parent_task_id=task_id, parent_session_id=session_id) │ │ ├─ sub-agent runs -> parent Task run_ids │ │ ├─ session hidden event: task_team_run_completed / task_team_run_failed │ │ └─ team summary + node results -> 主 Agent synthesis execution_context │ ├─ AgentLoop.process_direct / submit_direct(..., task_id, task_mode=True, attempt_index=1) │ ├─ ValidationService.validate_task_result(..., team_summaries=...) │ ├─ TaskService.record_validation(...) │ ├─ RunMemoryStore.update_run_record(validation_result=...) │ └─ session hidden event: task_validation_snapshotted │ ├─ if validation accepted │ └─ return result with task_id / task_status / validation_result │ └─ if validation failed ├─ session_manager.set_run_context_visible(run_id, false)(隐藏失败草稿尝试) ├─ attempt 2 重新规划 single / team ├─ revision request + team result -> 主 Agent synthesis execution_context └─ 第二次结果无论验证是否通过,都返回并等待用户反馈 ``` --- ## 4. Direct Run ```text AgentLoop.process_direct(task)(直接执行一轮用户任务) │ ├─ 生成 session_id(确定这句话属于哪个会话) ├─ 生成 run_id(给本次运行生成唯一编号) ├─ memory_service.capture_snapshot_for_run()(每个 run 捕获独立记忆快照) │ └─ fresh MemoryStore(root).load_from_disk()(不写共享 `_snapshot`,避免并发串记忆) │ ├─ session_manager.ensure_session(session_id)(确保会话存在) ├─ session_manager.append_message(event_type="run_started", hidden)(记录隐藏事件:本轮开始) │ ├─ make_provider_bundle()(装配模型 provider 组合) │ ├─ main_runtime(主模型配置) │ ├─ main_provider(主模型调用器) │ ├─ fallback_runtime(备用模型配置) │ ├─ fallback_provider(备用模型调用器) │ ├─ auxiliary_runtime(辅助模型配置) │ ├─ auxiliary_provider(辅助模型调用器,用于选 skill 等) │ └─ embedding_runtime(向量模型配置,用于语义召回) │ ├─ 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[](构造技能激活收据) ├─ session_manager.append_message(...)(记录隐藏事件:本轮用了哪些技能) │ ├─ event_type="skill_activation_snapshotted"(技能激活快照) │ ├─ hidden(不进入普通聊天上下文) │ └─ payload(隐藏数据) │ ├─ receipts(技能激活收据) │ └─ activation_messages(实际注入给模型的技能消息) │ ├─ tool_assembler.assemble(...)(选择本轮应该暴露哪些工具;simple_chat 默认跳过) │ ├─ always tools(默认总是可用的工具) │ ├─ activated skill tool hints(被激活技能推荐的工具) │ ├─ embedding retrieve tools(用向量召回相关工具) │ └─ 返回 selected ToolSpec[](返回本轮工具列表) │ ├─ session_manager.append_message(event_type="tool_selection_snapshotted", hidden)(记录隐藏事件:工具选择快照) │ ├─ ContextBuilder.build_messages(...)(构造发给模型的完整 messages) │ ├─ build_system_prompt()(构造 system prompt) │ │ ├─ base system prompt(基础系统提示词) │ │ ├─ session metadata(当前会话元信息) │ │ ├─ execution context(本轮额外执行上下文) │ │ └─ frozen memory snapshot(冻结记忆快照) │ ├─ insert activated skill messages(插入已激活技能正文) │ ├─ append visible history(追加可见历史聊天) │ └─ 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")(记录可见事件:用户消息) │ ├─ 进入 tool loop(进入模型回答和工具调用循环) │ ├─ 成功时(模型正常结束) │ ├─ session_manager.append_message(event_type="run_completed", hidden)(记录隐藏事件:运行完成) │ └─ _record_run_receipts(...)(记录运行证据,不生成学习候选) │ ├─ 失败时(运行中出现异常) │ ├─ append assistant error message(写入 assistant 错误消息) │ ├─ session_manager.append_message(event_type="run_failed", hidden)(记录隐藏事件:运行失败) │ └─ _record_run_receipts(...)(即使失败也记录运行证据) │ └─ return AgentRunResult(返回本轮结果) ├─ session_id(会话编号) ├─ run_id(运行编号) ├─ output_text(最终回复文本) ├─ finish_reason(结束原因) ├─ tool_iterations(工具循环次数) ├─ provider_name(模型供应商) ├─ model(模型名称) ├─ usage(token 用量) ├─ task_id(Task 模式下返回) ├─ task_status(Task 模式下返回) └─ validation_result(Task 模式下返回) ``` --- ## 5. Tool Loop ```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 回复) ├─ ContextBuilder.add_assistant_message(...)(把 assistant 回复追加到本轮 messages) │ ├─ if no tool calls(如果模型没有要求调用工具) │ └─ finish(结束本轮回答) │ └─ if tool calls(如果模型要求调用工具) ├─ ToolExecutor.execute_tool_call(...)(执行一个工具调用) ├─ session_manager.append_message(event_type="tool_result_recorded")(记录工具结果) ├─ ContextBuilder.add_tool_result(...)(把工具结果追加到 messages) └─ 回到 provider.chat(...)(带着工具结果继续问模型) ``` --- ## 6. Run Evidence / Skill Effect Recording ```text AgentLoop._record_run_receipts(...)(记录本轮运行证据;不直接学习) │ ├─ 构造 RunRecord(构造本轮运行记录) │ ├─ run_id(运行编号) │ ├─ session_id(会话编号) │ ├─ task_text(用户原始任务) │ ├─ task_id(内部 Task 编号,简单问题可为空) │ ├─ attempt_index(Task 模式下的尝试序号) │ ├─ started_at(开始时间) │ ├─ ended_at(结束时间) │ ├─ success(是否成功) │ ├─ finish_reason(结束原因) │ ├─ validation_result(Task 模式下的验证结果) │ ├─ feedback(用户反馈) │ └─ activated_skills(本轮激活过的技能收据) │ ├─ 构造 SkillEffectRecord[](构造技能效果记录) │ └─ 每个 activated skill 一条(每个被用到的技能都单独记一条) │ ├─ skill_learning_service.collect_run_receipts(...)(收集运行收据) │ ├─ RunMemoryStore.append_run_record(...)(把 RunRecord 写入 memory/runs/runs.jsonl) │ ├─ RunMemoryStore.append_skill_effect(...)(把 SkillEffectRecord 写入 memory/runs/skill-effects.jsonl) │ ├─ SkillLearningService.rescore_skill_versions()(重新统计每个技能版本表现) │ │ └─ SkillLearningStore.update_performance_snapshot(...)(更新表现快照) │ └─ never build learning candidates in runtime hot path(运行完成时永不生成候选) │ └─ session_manager.append_message(...)(记录隐藏事件:技能效果快照) ├─ event_type="skill_effects_snapshotted"(技能效果已快照) ├─ hidden(不进入普通聊天上下文) └─ payload(隐藏数据) ├─ run_record(本轮运行记录) ├─ skill_effects(技能效果记录) ├─ 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 ```text POST /api/chat/feedback(聊天反馈接口,不是 Task 管理 API) │ ├─ input │ ├─ session_id │ ├─ run_id │ ├─ feedback_type │ │ ├─ satisfied │ │ ├─ revise │ │ └─ abandon │ └─ comment? │ ├─ AgentService.submit_feedback(...) │ ├─ TaskService.get_task_by_run_id(run_id) │ ├─ reject if task/session mismatch │ ├─ reject conflicting feedback for same run │ ├─ same feedback is idempotent │ └─ TaskService.add_feedback(...) │ ├─ satisfied │ ├─ if validation accepted │ │ ├─ Task status -> closed │ │ └─ SkillLearningService.build_learning_candidates_for_task(task_id, trigger_run_id) │ └─ if validation not accepted │ └─ 记录人工接受但保留验证风险;不自动生成 learning candidate │ ├─ revise │ ├─ Task status -> needs_revision │ ├─ 更新 run / skill effect 为需修订证据 │ └─ 下一条用户消息默认复用该 Task;不生成 learning candidate │ └─ abandon ├─ Task status -> abandoned ├─ 更新 run / skill effect 为失败证据 ├─ 追加 task_failure_evidence_recorded 隐藏事件 └─ 默认不写主 memory,不生成成功 Skill draft ``` --- ## 8. Agent Team v1 / Local Coordinator ```text TeamService.run_team(...)(内部 team 执行入口,不暴露产品级 Task API) │ ├─ validate parent task(如果传 parent_task_id,先校验 Task 存在且 session 匹配) │ ├─ TeamGraphScheduler.run(...) │ ├─ graph.validate() │ │ ├─ v1 implemented strategies: sequence / parallel / dag │ │ └─ 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 生成的一次性 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(ephemeral guidance 只在本次 run 注入) │ └─ provider_bundle + node model/provider override 禁止混用 │ ├─ strategy execution │ ├─ sequence:前一节点成功输出进入后一节点 dependency_outputs │ ├─ parallel:同层节点 asyncio.gather 真并发执行 │ └─ dag:按依赖拓扑分批并发;失败节点会阻断依赖它的后续节点 │ ├─ node-level failure normalization │ ├─ provider factory / runner 普通异常 -> NodeRunResult(success=False, finish_reason="error") │ ├─ asyncio.CancelledError 继续抛出 │ └─ blocked dependency -> NodeRunResult(success=False, finish_reason="blocked") │ ├─ TeamRunResult │ ├─ success │ ├─ summary(只聚合成功节点输出;失败节点列入 Failed nodes) │ ├─ node_results │ ├─ run_ids │ ├─ session_ids │ └─ task_id(父 Task) │ └─ attach runs to parent Task └─ TaskService.append_run(parent_task_id, sub_run_id, skill_names=...) ``` ```text Team v1 scope(当前边界) │ ├─ 已实现 │ ├─ Beaver 自有 coordinator models │ ├─ sequence / parallel / dag 三个执行原语 │ ├─ pinned skill 继承 + open skill assembly │ ├─ per-run memory snapshot,支持真并发 prompt 构建 │ ├─ per-node provider factory 语义 │ ├─ parent Task 一致性校验 │ └─ 节点失败归一和 summary 失败区块 │ ├─ 已接入 Task mode 内部执行链 │ ├─ TaskExecutionPlanner 先决定 single / team │ ├─ team run 只作为内部 sub-agent 执行策略 │ ├─ TeamRunResult 不直接返回给用户 │ └─ 主 Agent synthesis run 生成用户可见最终回答 │ └─ 仍不暴露产品级 team / Task API └─ 外部仍只使用聊天入口和反馈入口 ``` --- ## 9. Session Module ```text SessionManager(会话管理门面) │ ├─ ensure_session(...)(确保会话存在) ├─ append_message(...)(追加一条事件或聊天消息) ├─ get_event_records(session_id)(获取完整事件流) ├─ get_run_event_records(session_id, run_id)(获取某次 run 的事件) ├─ update_latest_assistant_event_payload(...)(把 task/validation/feedback 状态投影到最新 assistant 消息) ├─ set_run_context_visible(session_id, run_id, visible)(隐藏失败重试草稿等 run) ├─ list_run_ids(session_id)(列出某个会话下所有 run_id) ├─ get_messages_as_conversation(session_id)(获取可作为聊天展示的消息) ├─ get_visible_history(session_id)(获取可进入 prompt 的历史) ├─ update_system_prompt(...)(更新当前会话 system prompt 快照) ├─ update_usage(...)(更新 token 用量) ├─ end_session(...)(结束会话) ├─ reopen_session(...)(重新打开会话) ├─ list_sessions_rich(...)(列出带摘要的会话) ├─ search_messages(...)(搜索历史消息) └─ resolve_session_id(...)(根据前缀解析 session_id) ``` ```text SessionStore (SQLite)(SQLite 会话数据库) │ ├─ sessions table(会话表) ├─ messages table(消息和事件表) ├─ messages_fts(全文搜索索引) ├─ WAL(SQLite 写入日志模式) ├─ parent_session_id(父会话字段,给未来分支会话用) └─ hidden / visible event split(隐藏事件和可见消息分离) ``` ```text hidden events(隐藏事件类型) │ ├─ run_started(运行开始) ├─ skill_activation_snapshotted(技能激活快照) ├─ tool_selection_snapshotted(工具选择快照) ├─ system_prompt_snapshotted(系统提示词快照) ├─ run_completed(运行完成) ├─ run_failed(运行失败) ├─ skill_effects_snapshotted(技能效果快照) ├─ task_validation_snapshotted(Task 验证快照) └─ task_feedback_recorded(Task 用户反馈快照) ``` --- ## 10. Memory Module ```text MemoryService(记忆服务) │ ├─ initialize()(初始化记忆存储) ├─ reload_for_new_run()(每轮开始前刷新记忆快照) ├─ get_snapshot()(获取本轮冻结记忆快照) └─ get_store()(获取底层 MemoryStore) ``` ```text MemoryStore(长期记忆存储) │ ├─ target: memory(项目/任务级长期记忆) ├─ target: user(用户偏好记忆) ├─ add(...)(新增记忆) ├─ replace(...)(替换记忆) ├─ remove(...)(删除记忆) ├─ load_from_disk()(从磁盘读取) ├─ save_to_disk()(保存到磁盘) └─ format_for_system_prompt(...)(格式化成 system prompt 段落) ``` ```text memory runtime semantics(记忆运行语义) │ ├─ run start(本轮开始) │ └─ refresh live state -> capture frozen snapshot(刷新 live memory,并冻结本轮快照) │ ├─ run middle(本轮进行中) │ ├─ memory tool may write durable state(memory 工具可以写入长期记忆) │ └─ current run prompt snapshot stays frozen(但本轮 prompt 里的记忆不变) │ └─ next run(下一轮) └─ newly written memory becomes visible(上一轮写入的新记忆开始可见) ``` --- ## 11. Skills Module ```text SkillsLoader(技能加载器) │ ├─ workspace published catalog(工作区正式发布的技能目录) ├─ workspace legacy skills/*/SKILL.md(旧格式技能文件) ├─ builtin skills(内置技能) ├─ list_skills()(列出运行时可见技能) ├─ list_published_skills()(只列正式发布技能) ├─ get_current_version()(获取当前正式版本) ├─ load_published_skill()(加载正式版本正文) ├─ get_skill_record()(获取技能元数据记录) ├─ get_skill_metadata()(获取 frontmatter 元数据) ├─ get_skill_tool_hints()(获取技能推荐工具) ├─ load_skills_for_context()(把多个技能加载成上下文块) ├─ build_skills_summary()(构造技能摘要索引) ├─ build_selection_candidates()(构造给 SkillAssembler 的候选摘要) ├─ list_skill_supporting_files()(列出技能支持文件) └─ get_always_skills()(获取 always 类型技能) ``` ```text SkillAssembler(技能选择器) │ ├─ input(输入) │ ├─ 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 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(技能正文) ├─ version(技能版本) ├─ content_hash(内容哈希) ├─ activation_reason(激活原因) └─ tool_hints(推荐工具) ``` ```text skills lifecycle baseline(技能生命周期基线) │ ├─ SkillSpecStore(技能生命周期文件存储) │ ├─ skill.json(技能总信息) │ ├─ current.json(当前版本指针) │ ├─ versions/(正式版本目录) │ ├─ drafts/(草稿目录) │ ├─ reviews/(审核记录目录) │ └─ archive/(归档目录) │ ├─ DraftService(草稿服务) │ ├─ create_new_skill_draft(...)(创建新技能草稿) │ ├─ create_revision_draft(...)(创建修订草稿) │ ├─ create_merge_draft(...)(创建合并草稿) │ ├─ create_retire_proposal(...)(创建退役提案) │ ├─ list_drafts(...)(列出草稿) │ └─ get_draft(...)(读取单个草稿) │ ├─ ReviewService(审核服务) │ ├─ submit_for_review(...)(提交审核) │ ├─ approve(...)(批准草稿) │ └─ reject(...)(拒绝草稿) │ └─ SkillPublisher(发布服务) ├─ publish(...)(发布 approved 草稿为正式版本) ├─ apply_retire_proposal(...)(应用退役提案,不创建新版本) ├─ disable(...)(禁用技能) └─ rollback(...)(回滚到旧版本) ``` --- ## 12. Tools Module ```text ToolRegistry(工具注册表) │ ├─ echo(回显工具) ├─ memory(写入/管理长期记忆) ├─ session_search(搜索会话历史) ├─ list_directory(列目录) ├─ read_file(读文件) └─ search_files(搜索文件) ``` ```text ToolAssembler(工具选择器) │ ├─ selected = always tools(先加入默认工具) ├─ selected += activated skill tool hints(再加入技能推荐工具) ├─ selected += embedding top-k tools(再用向量召回任务相关工具) └─ return ToolSpec[](返回本轮可用工具列表;不通过工具动态加载 skill) ``` ```text ToolExecutor(工具执行器) │ ├─ normalize tool call(规范化模型发来的工具调用) ├─ resolve tool(找到对应工具) ├─ invoke tool(执行工具) └─ return ToolResult(返回工具结果) ``` ```text filesystem tool boundary(文件系统工具边界) │ ├─ workspace scoped(只能访问 workspace 范围) ├─ realpath enforcement(用真实路径校验) ├─ reject path escape(拒绝路径逃逸) ├─ reject symlink escape(拒绝软链接逃逸) └─ reject binary file reads(拒绝读取二进制文件) ``` --- ## 13. Provider Module ```text make_provider_bundle(...)(创建模型调用组合) │ ├─ main_runtime(主模型配置) ├─ main_provider(主模型调用器) ├─ fallback_runtime(备用模型配置) ├─ fallback_provider(备用模型调用器) ├─ auxiliary_runtime(辅助模型配置) ├─ auxiliary_provider(辅助模型调用器) └─ embedding_runtime(向量模型配置) ``` ```text provider roles(模型角色分工) │ ├─ main(主模型) │ └─ assistant/tool loop(负责正常回答和工具循环) ├─ fallback(备用模型) │ └─ main failure recovery(主模型失败时兜底) ├─ auxiliary(辅助模型) │ └─ skill selection / future helper tasks(负责选择技能等辅助任务) └─ embedding(向量模型) └─ skill/tool semantic retrieval(负责 skill / tool 的语义召回) ``` --- ## 14. Service Lifecycle ```text AgentService(服务生命周期) │ ├─ MainAgentRouter(请求进入 AgentLoop 前先分类 simple / task) ├─ submit_feedback(...)(聊天反馈入口,内部更新 Task 状态) │ ├─ direct mode(直接模式:适合 CLI / 单次调用) │ └─ process_direct(...)(直接处理一次任务) │ └─ running mode(后台运行模式:适合 Web / Gateway) ├─ start()(启动 AgentLoop.run) ├─ submit_direct(...)(向队列提交任务) ├─ stop(timeout_seconds, force)(停止并等待任务收尾) ├─ shutdown(timeout_seconds, force)(停止并释放 runtime) └─ close()(关闭已停止的 loop) ``` ```text running mode semantics(后台运行语义) │ ├─ start()(启动) │ └─ AgentLoop.run()(进入队列消费循环) │ ├─ submit_direct()(提交任务) │ └─ enqueue _DirectRunRequest(把任务放入队列) │ ├─ stop()(停止) │ ├─ stop accepting new tasks(不再接收新任务) │ └─ drain queued tasks(等待已排队任务处理完) │ └─ close()(关闭) └─ requires loop already stopped(必须先 stop,才能 close) ``` --- ## 15. Task Team Registry / Process / Learning 闭环 ```text TaskExecutionPlanner(Task 内部执行规划) │ ├─ LLM planner │ ├─ 输出 single / team │ └─ team 只允许 sequence / parallel / dag │ ├─ TaskSkillResolver │ ├─ 从 published skill catalog 检索候选 │ ├─ 按 skill_query / required_capabilities / node task 选择 skill │ ├─ 命中 published skill 后写入 graph.nodes[].inherited_pinned_skills │ └─ 无命中时创建 ephemeral guidance,并写入 graph.nodes[].inherited_pinned_skill_contexts │ └─ TaskExecutionPlan ├─ graph.nodes[].agent 只是 generic runtime trace identity └─ to_event_payload() 写入 skill_queries / selected_skill_names / ephemeral_guidance_ids / skill_resolution_report ``` ```text Task mode attempt(每次 Task attempt) │ ├─ task_execution_planned(隐藏事件) │ └─ plan_mode / strategy / node_ids / skill_resolution_report │ ├─ team run(仅 team plan) │ ├─ sub-agent run_ids 回填父 Task │ ├─ team summary 只进入主 Agent synthesis context │ └─ task_team_run_completed / task_team_run_failed(隐藏事件) │ ├─ main Agent synthesis │ ├─ 输出最终用户可见回答 │ └─ task_synthesis_completed(隐藏事件) │ └─ validation ├─ task_validation_snapshotted(隐藏事件) ├─ 第一次失败隐藏草稿并重试一次 └─ 第二次或验证通过后等待反馈 ``` ```text Frontend process projection │ ├─ GET /api/sessions/{session_id}/process │ ├─ 读取隐藏 Task/team/validation 事件 │ ├─ 合并 run memory records │ └─ 输出 processRuns / processEvents / processArtifacts / agents │ └─ ChatWorkbench ├─ 桌面端显示 ProcessLane ├─ 移动端显示 Process tab └─ 不直接暴露隐藏事件原始 JSON ``` ```text Learning pipeline │ ├─ evidence recording │ ├─ every run -> RunRecord │ ├─ activated skills -> SkillEffectRecord │ └─ no candidates generated here │ ├─ feedback gate │ ├─ 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 │ ├─ worker/run-once -> draft │ ├─ draft -> safety report │ ├─ draft -> lightweight eval report │ ├─ safety_failed / eval_failed 阻断发布 │ ├─ draft -> submit review │ ├─ approve / reject │ ├─ approved + safety passed + eval not failed -> publish │ ├─ retire proposal -> apply retire │ └─ rollback / disable │ ├─ SkillLearningWorker │ ├─ 默认按配置定时扫描 open candidates │ ├─ 自动生成 draft_ready / safety_failed / eval_failed │ └─ 永不自动 approve / publish │ ├─ Web review workbench │ ├─ Candidates │ ├─ Draft detail │ ├─ Safety report │ └─ Eval report │ └─ Runtime catalog └─ 只有 published skill 进入运行时选择;draft 不生效 ``` --- ## 16. Web / Gateway ```text Web(网页入口) │ ├─ FastAPI lifespan(FastAPI 生命周期) │ ├─ create or receive AgentService(创建或接收 AgentService) │ ├─ start() when app owns lifecycle(如果 Web app 拥有生命周期,就启动 service) │ ├─ start SkillLearningWorker when enabled(按配置启动技能学习 worker) │ └─ shutdown() when app owns lifecycle(如果 Web app 拥有生命周期,就关闭 service / worker) │ ├─ GET /api/ping(健康检查接口) │ └─ return status / running / mode(返回状态、是否运行、运行模式) │ ├─ POST /api/chat(聊天接口) │ ├─ validate WebChatRequest(校验请求体) │ ├─ 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(...) └─ return WebChatFeedbackResponse Skills learning admin API │ ├─ GET /api/skills/candidates ├─ POST /api/skills/candidates/{candidate_id}/draft ├─ POST /api/skills/candidates/{candidate_id}/regenerate ├─ POST /api/skills/learning/run-once ├─ GET /api/skills/{skill_name}/drafts/{draft_id}/safety ├─ GET /api/skills/{skill_name}/drafts/{draft_id}/eval └─ POST /api/skills/{skill_name}/drafts/{draft_id}/publish └─ requires approved review + safety passed + eval not failed ``` ```text Gateway(消息通道入口) │ ├─ MessageBus(内部消息总线) ├─ ChannelAdapter(Telegram / Slack / Email / WhatsApp 等只作为 adapter) ├─ inbound -> AgentService.handle_inbound_message(...)(外部消息进入 AgentService) ├─ outbound <- OutboundMessage(AgentService 返回结构化输出消息) └─ ChannelManager(按 message.channel 分发 outbound) ``` --- ## 17. Bus Mode Skeleton ```text AgentLoop.run()(后台队列运行模式) │ ├─ create queue(创建任务队列) ├─ mark running(标记为运行中) ├─ consume _DirectRunRequest(消费一个任务请求) ├─ call _process_direct_impl(...)(调用真正的单轮执行逻辑) ├─ set future result / exception(把结果或异常写回等待方) ├─ stop() -> enqueue sentinel(停止时放入结束标记) └─ drain pending queue on exit(退出时清理未处理任务) ```