添加完整的Outlook MCP集成,包括邮件和日历功能,通过AuthZ模式进行认证和权限管理, 支持邮箱连接、断开、状态检查和数据同步等功能。 fix(config): 统一配置文件路径从.nanobot到.beaver 将配置文件路径从/root/.nanobot统一更改为/root/.beaver,更新Dockerfile中的环境变量定义, 确保所有组件使用一致的配置目录结构。 feat(agent): 添加代理删除功能和助手身份提示 为代理注册表添加delete_agent方法,实现代理的动态删除功能;同时添加海狸助手身份提示, 确保AI助手在交互中保持一致的身份认知。 feat(engine): 增强引擎循环并添加意图决策快照 扩展AgentLoop类,添加intent_agent_decision参数用于意图驱动的代理决策,并在会话中记录 决策快照,便于后续分析和调试。 feat(authz): 扩展认证客户端功能 为AuthzClient添加设置权限、用户注册、后端注册和Outlook设置管理等新方法,增强系统 的认证和授权能力。
37 KiB
37 KiB
Beaver Backend Flow
这份文档只保留树形运行结构。
- 原理、参考项目边界、长期蓝图:看
change.md - 施工顺序、阶段目标、完成标准:看
施工指南.md
1. 总入口
用户输入(用户在不同入口发来一句话或一个任务)
│
├─ 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
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
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 模式执行)
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
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
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
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
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(学习候选;默认空)
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
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
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=...)
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
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)
SessionStore (SQLite)(SQLite 会话数据库)
│
├─ sessions table(会话表)
├─ messages table(消息和事件表)
├─ messages_fts(全文搜索索引)
├─ WAL(SQLite 写入日志模式)
├─ parent_session_id(父会话字段,给未来分支会话用)
└─ hidden / visible event split(隐藏事件和可见消息分离)
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
MemoryService(记忆服务)
│
├─ initialize()(初始化记忆存储)
├─ reload_for_new_run()(每轮开始前刷新记忆快照)
├─ get_snapshot()(获取本轮冻结记忆快照)
└─ get_store()(获取底层 MemoryStore)
MemoryStore(长期记忆存储)
│
├─ target: memory(项目/任务级长期记忆)
├─ target: user(用户偏好记忆)
├─ add(...)(新增记忆)
├─ replace(...)(替换记忆)
├─ remove(...)(删除记忆)
├─ load_from_disk()(从磁盘读取)
├─ save_to_disk()(保存到磁盘)
└─ format_for_system_prompt(...)(格式化成 system prompt 段落)
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
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 类型技能)
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(推荐工具)
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
ToolRegistry(工具注册表)
│
├─ echo(回显工具)
├─ memory(写入/管理长期记忆)
├─ session_search(搜索会话历史)
├─ list_directory(列目录)
├─ read_file(读文件)
└─ search_files(搜索文件)
ToolAssembler(工具选择器)
│
├─ selected = always tools(先加入默认工具)
├─ selected += activated skill tool hints(再加入技能推荐工具)
├─ selected += embedding top-k tools(再用向量召回任务相关工具)
└─ return ToolSpec[](返回本轮可用工具列表;不通过工具动态加载 skill)
ToolExecutor(工具执行器)
│
├─ normalize tool call(规范化模型发来的工具调用)
├─ resolve tool(找到对应工具)
├─ invoke tool(执行工具)
└─ return ToolResult(返回工具结果)
filesystem tool boundary(文件系统工具边界)
│
├─ workspace scoped(只能访问 workspace 范围)
├─ realpath enforcement(用真实路径校验)
├─ reject path escape(拒绝路径逃逸)
├─ reject symlink escape(拒绝软链接逃逸)
└─ reject binary file reads(拒绝读取二进制文件)
13. Provider Module
make_provider_bundle(...)(创建模型调用组合)
│
├─ main_runtime(主模型配置)
├─ main_provider(主模型调用器)
├─ fallback_runtime(备用模型配置)
├─ fallback_provider(备用模型调用器)
├─ auxiliary_runtime(辅助模型配置)
├─ auxiliary_provider(辅助模型调用器)
└─ embedding_runtime(向量模型配置)
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
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)
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 闭环
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
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(隐藏事件)
├─ 第一次失败隐藏草稿并重试一次
└─ 第二次或验证通过后等待反馈
Frontend process projection
│
├─ GET /api/sessions/{session_id}/process
│ ├─ 读取隐藏 Task/team/validation 事件
│ ├─ 合并 run memory records
│ └─ 输出 processRuns / processEvents / processArtifacts / agents
│
└─ ChatWorkbench
├─ 桌面端显示 ProcessLane
├─ 移动端显示 Process tab
└─ 不直接暴露隐藏事件原始 JSON
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
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
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
AgentLoop.run()(后台队列运行模式)
│
├─ create queue(创建任务队列)
├─ mark running(标记为运行中)
├─ consume _DirectRunRequest(消费一个任务请求)
├─ call _process_direct_impl(...)(调用真正的单轮执行逻辑)
├─ set future result / exception(把结果或异常写回等待方)
├─ stop() -> enqueue sentinel(停止时放入结束标记)
└─ drain pending queue on exit(退出时清理未处理任务)