Skip to main content

WeaveAgentDesign

WeaveAgent 编排架构设计

本文是 Agent/一种理想的智能体编排架构.md 的工程化落地设计,对应骨架代码 Agent/WeaveAgent.py。经 Q1–Q22 的逐项确认汇总而成。


1. 核心概念与层级关系

  • Frame:重量级思考 / 工作单元。每个 Frame 拥有独立的 Session,是思维树的节点。
  • Task:Frame 内的轻量 TODO 项,不是 Frame 的特例,是两个独立概念。
  • 根 Frameparent=None 的普通 Frame,结构上与其他 Frame 完全一致。其"根性"只体现在 parent 字段上,type 照常取 EXPLORE / FIXED / THINK 之一。
  • 思维树:所有 Frame 按父子关系构成的树,根 Frame 是唯一根。

Task 与 SubFrame 的区别

维度 Task(轻量) SubFrame(重量)
上下文 共享所在 Frame 的 Session 自己独立的 Session
场景 顺手做、顺序推进的步骤 需要独立思考上下文的工作
字段 describe, status, result, method, origin 完整的 Frame 六块字段
创建 LLM 输出 add_task 动作 LLM 输出 spawn_subframe 动作
转化 不会升级成 SubFrame,两条路径平行

Task 发现实际过复杂、需要独立上下文时的约定:不做升格机制。LLM 在同一次 Next() 里连续发两个动作:(1) 把该 Task 状态置 SKIP 并在 result 写下转移原因;(2) spawn_subframe 新建子 Frame 承接这件事。两个动作没有数据依赖,但顺序保留 LLM 意图痕迹。

Task 的 status:PENDING / RUNNING / DONE / SKIP


2. Frame 的字段六块

  1. 身份id, type, status, parent
  2. 配置 / 人格层 Prompt(创建时基本静态) system_msg, next_guide, summary_guide, reflect_guide
  3. 意图上下文origin, goal, background, plan
    • origin:本 Frame 创建原因(并入原 pre_msg 的"创建原因")
    • background:逻辑关系 / 依赖前置信息(并入原 pre_msg 的"逻辑关系")
    • goal:预期目标 / 预期效果(并入原 pre_msg 的"预期效果")
  4. 执行状态log, result, summary, logic_trace
  5. 子结构task_list, sub_frame_list
  6. 本地资源session、临时记忆、长时记忆引用、公共存储引用、ToolRegistry 引用 (Frame 不持有本地 tools,tools 全局)

额外:父→子通道 inbox(Message 队列)。


3. 思维树的组织

父子交互

  • 子 → 父:被动式 子 Frame 结束时只更新自身 status + result不直接调用父的方法。父 Frame 的唤醒由调度器通过 SUBFRAME_DONE 事件触发(见 §7),父被唤醒后在 Next() 里轮询 sub_frame_list 获取子的最终状态。换言之,"被动式"指的是代码层无直接回调,事件总线替子做了通知。
  • 父 → 子:inbox 激励 父 Frame 调 child.Stimulate(msg),消息入子 Frame 的 inbox 队列;子 Frame 被唤醒后下一次 Next() 从 inbox 取出合并处理。
    • Stimulate 推入的 Message 类型为 USER——MessageType.USER 语义扩展为"来自本 Frame 外部的驱动性输入",同时覆盖外部用户推入根 Frame 与父 Frame 推入子 Frame 两种情况。
  • 父子松耦合,通过事件记录器联动(见 §7)。

临时记忆可见性

  • 每个 Frame 独占一份短时记忆。
  • 父 Frame 能读取直接子 Frame 的短时记忆快照(通过 GetChildMemorySnapshot)。
  • 子 Frame 看不到父 Frame 的短时记忆。子 Frame 创建时通过 origin / background / goal 拿到静态"简报",运行期与父完全隔离。

4. Frame 状态机

PENDING → RUNNING ↔ BLOCKED → REFLECTING → DONE / FAILED

状态集:PENDING / RUNNING / BLOCKED / REFLECTING / DONE / FAILED

REFLECTING 是终结前的必经阶段:成功与失败都经过它,DONEFAILED 只能从 REFLECTING 进入,不允许从 RUNNING / BLOCKED 直接跳到终结态。

  • 成功路径:RUNNING → REFLECTING(提取方法学 / 总结收获)→ DONE
  • 失败路径:RUNNING → REFLECTING(提取教训 / 错误原因)→ FAILED

REFLECTING 严格单向:只能去 DONE / FAILED,不允许回 RUNNING。若反思后发现方向错误要继续干,按"失败 + 父 spawn 新 Frame"模式处理——走 REFLECTING → FAILED,在 summary / result 里写清教训,父 Frame 被 SUBFRAME_DONE 唤醒后读到子的产出,决定新方向并 spawn 新 SubFrame。

REFLECTING 阶段的 Guide 段会根据 LLM 声明的最终 status(DONE 或 FAILED)注入不同的自洽要求:成功强调"忠于历史、可泛化成 skill",失败强调"错误根因清晰、可作为下次的先验"。

转移驱动:LLM 在 Next() 的结构化输出里显式声明 next_status,本地代码(_handle_declare_status)校验合法性后 apply。

  • 被动类(BLOCKED 等待子/等待 inbox)也可由本地代码根据外部条件直接判断。
  • LLM 声明类(REFLECTING / DONE / FAILED)一定经过本地校验,非法转移(如 RUNNING→DONE 越过 REFLECTING)会被拒绝。

5. Frame.Next() 的职责

每次被调度器唤醒时执行:

  1. 读状态:status / task_list / sub_frame_list / logic_trace / plan / inbox …
  2. 本地动作筛选:按当前状态挑出本轮允许 LLM 选择的动作子集 (不是把全部 20+ 动作盲选交给 LLM)。
  3. 组装分层 prompt → 调 LLM → 拿结构化输出
  4. 派发动作
    • Frame 自处理:仅改 Frame 自身的结构性骨架(add_task / update_task / spawn_subframe / update_plan / append_log / declare_status
    • Tool 处理:一切记忆类与外部资源类操作,包括短时记忆、长时记忆、公共存储、文件、skill 存取等(memory/short/*, memory/long/*, public/*, file/*, skill/*…)

动作边界原则:Frame 自处理动作只修改 Frame 的结构性字段(状态、任务、子 Frame、计划、日志、逻辑链);所有记忆读写统一经 Tool(包括短时记忆),以便 LLM 只学一套记忆接口风格。logic_trace 由本地自动从每轮 reasoning 追加,不需要显式动作。

关于 Rollback:没有独立的 rollback 动作。思维树上"回退 / 走错方向"通过 Frame 终结机制实现:当前 Frame 的 LLM 判定方向错误 → 在 summary / result 里写下错误原因(作为教训)→ 声明 next_status=FAILED → Frame 结束 → 父 Frame 下次 Next() 轮询到子 FAILED + 错误记录 → 基于教训 spawn 新 SubFrame 走不同方向。Frame 内无真正的状态回滚。

5.1 LLM 输出 Schema

每次 Frame.Next() 调 LLM 的结构化返回:

{
  "reasoning": "当前子任务已完成,我应该先把结果总结到 summary,再进入 REFLECTING。",
  "actions": [
    {"type": "append_log",  "args": {"text": "..."}},
    {"type": "update_plan", "args": {"plan": "..."}}
  ],
  "next_status": "REFLECTING"
}

字段语义:

  • reasoning(必填):自然语言思维链,本地自动附加到 logic_trace(带时间戳)。没有独立的 record_logic 动作——logic_trace 的唯一来源就是每轮 reasoning。
  • actions(必填,可为空):有序,动作间无互相依赖(不支持引用前序返回值);任一失败即中断,错误写回 logic_trace,下一轮 Next() 时 LLM 可见并纠正。
  • next_status(可选):省略或 null 则保持当前;否则经 _handle_declare_status 校验转移合法性。

6. 分层 Prompt 组装

[System 段]
  1. system_msg                 — 人格(由 Persona 渲染)+ 自我 + 通用自洽
  2. 本轮动作集说明 + 可用 Tool 列表 + 结构化输出 schema

[Context 段]
  3. 意图上下文                  — origin(创建原因)/ background(逻辑关系/依赖)/ goal(预期效果)/ plan
  4. 状态快照                    — status / task_list 简表 / sub_frame_list 简表
  5. logic_trace 摘要            — 思维链路(可压缩)
  6. 父 inbox 新消息             — 如有
  7. 命中记忆 / skill            — 最近检索到的相关长时记忆条目

[Working 段]
  8. 本 Frame 的短时记忆(经 memory/short Tool 快照展开)
  9. Session 历史消息

[Guide 段]
 10. next_guide / summary_guide / reflect_guide 之一或组合
     + 按动作类型定制的自洽要求片段
     + REFLECTING 阶段按最终 status(DONE / FAILED)切换不同片段

自洽没有独立模块,按动作类型在 Guide 段注入定制片段(总结 / 创建子任务 / 回退 等各有各的自洽标准)。


7. 事件驱动调度

不采用周期性 tick。改为事件记录器 + 就绪队列

事件类型(6 类)

# 事件 触发时机
1 INBOX_NEW 父 Frame 调用 child.Stimulate(msg)
2 SUBFRAME_DONE 子 Frame 进入 DONE / FAILED,标脏父 Frame
3 TASK_CHANGE 异步 Tool 完成后更新某个 task 状态并唤醒其所属 Frame(只有异步路径触发;Frame 在 Next() 内同步完成的 task 推进不发此事件)
4 EXTERNAL_INPUT 用户输入推到根 Frame 的 inbox
5 TIMER Frame 主动请求 N 秒后再唤醒(ScheduleTimer
6 CREATED Frame 创建后首次入队自检

调度流程

事件发生 → EventLog.Log(event)
       → Scheduler.OnEvent(event)    # 标脏目标 Frame
       → ready queue 入队
       → Scheduler.Tick()           # 主循环取一个 Frame
       → frame.Next()

所有跨 Frame 的状态变化都统一成事件,可回溯可追踪。

7.1 并发模型

并发隔离完全依赖树结构,不做资源声明、不做子树互斥标记。

天然隔离点

  • 兄弟 Frame 彼此不可见(短时记忆天然隔离),跨兄弟协调必须经父 Frame 中转。
  • 父读子的短时记忆快照(只读 snapshot),父通过 Stimulate 推消息到子的 inbox 队列——无实时共享内存。
  • 子 Frame 的生命周期由父 Frame 控制:创建 / Stimulate / 感知 SUBFRAME_DONE 都是父 Frame 的行为。
  • 全局存储(长时记忆 / 公共存储)的写并发由存储层内部加锁,不由调度器介入。

调度器只做两件事

  1. per-Frame 锁:同一 Frame 不允许并发 Next()。就绪队列用 OrderedDict[frame_id, None] 实现——同一 Frame 被多事件标脏时仅入队一次(O(1) 去重 + 保持首次入队的 FIFO 顺序)。
  2. 全局并发上限 N:worker 池,N 个 Frame 可同时在 Next() 中。

Tick 时的 Frame 上下文注入:调度器从 ready 取 frame_id 后、调 frame.Next() 前,设置一个 async 上下文变量 current_frame = frame。Tool Call 在执行期可从该上下文拿到当前 Frame 引用,以定位 frame.short_memory 等 Frame 级资源——这是"短时记忆物理独占 + 接口统一经 Tool"组合的关键衔接点。

不需要:资源声明、子树互斥标记、Frame 预知自己写哪些 key。

7.2 定时器实现

调度器跑在 asyncio event loop 中:

  • ScheduleTimer(frame_id, delay)loop.call_later(delay, lambda: self.OnEvent(TIMER_event))
  • worker 池用 asyncio.Semaphore 控制并发上限 N
  • LLM 调用使用 async HTTP 客户端
  • 整个系统 async 化,零额外依赖

8. 记忆与存储

存储 归属 实现 访问路径
短时记忆 每个 Frame 独占(Frame.short_memory 字段) 通用 Memory 接口(CRUD) 统一经 Tool(memory/short/*)。Tool 执行时从 async 上下文变量 current_frame 定位到本 Frame 的字段。父可读直接子的 Snapshot(GetChildMemorySnapshot(child_id)
长时记忆 Agent 全局单例 基于关键词树的知识库(见 Agent/基于关键词的知识树系统设计方案.md 通过 Tool(memory/long/search, memory/long/create_keyword, memory/long/create_info, memory/long/link_info…)访问
公共存储 Agent 全局单例 简单 KV 通过 Tool(public/get, public/set)访问

长时记忆不是通用 KV,是"关键词树节点 + Info + 多对多关联"的结构化知识库。LongTermMemory 类是该库的薄封装。主要 API:Search, GetInfosOfKeyword, CreateKeyword, CreateInfo, LinkInfo, MoveKeyword, MergeKeywords, DeleteKeyword 等(完整见库设计文档)。无"热度排序"概念,组织靠关键词树本身。


9. Tool 与 Skill 的组织

Tool

  • 全局 ToolRegistry,挂在 Agent 上,分层路径组织(memory/*, frame/*, file/*, skill/*…)。
  • Frame 不持有本地 tools 实例,但持有全局 Registry 引用。
  • Frame.Next() 在 prompt 组装阶段按当前状态 / 动作集按路径前缀拉取可见工具子集,注入 System 段。

Skill

  • 不独立建模。Skill 就是长时记忆(关键词树知识库)里的一类 Info
  • 挂载约定:所有 Skill Info 挂在固定关键词子树下(如 root/methodology/skill),按领域可细分到 skill/memory, skill/coding 等子节点。
  • abstract_to_skill 动作调用 LongTermMemory.SaveSkill(content, category),内部 CreateInfo + LinkInfo 到对应 skill 关键词节点。
  • LongTermMemory.FindSkills(category)GetInfosOfKeyword("skill/<category>") 检索。
  • :LLM 在动作里给出 skill_info_id,本地代码把该 skill 的 prompt 模板拼进下一轮 prompt,或把其内容作为新 SubFrame 的 background 简报。
  • 演化:调用 UpdateInfo 改对应 Info 条目。

10. 人格与自洽

  • 人格:静态 Persona,由 Persona.RenderSystemPrompt() 渲染为文本,作为所有 Frame 的 system_msg 的一部分注入。人格不在运行期动态调度。
  • 自洽:没有独立检查模块;按当前动作类型在 prompt 的 Guide 段注入定制化的自洽要求(总结任务要求忠于历史、创建子任务要求目标可落地、回退要求原因清晰等)。

11. Agent 角色

Agent 是全局容器:

  • 持有 Persona / 根 Frame / 三大全局存储(LongTermMemory、PublicStore、ToolRegistry)
  • 持有 EventLog + Scheduler
  • 持有全局 Frame id 计数器id → Frame 注册表;所有 Frame 创建统一通过 Agent.CreateFrame(...) 入口注入自增 id 并注册,Frame.__init__ 不接受 id 参数
  • 构造签名:Agent(persona, root_goal="", root_origin="", root_background="")Agent.__init__ 内部调用 self.CreateFrame(parent=None, goal=root_goal, origin=root_origin, background=root_background) 生成根 Frame——根 Frame 走同一构造路径,无特例。root_goal / root_origin / root_background 允许在构造 Agent 时指定根 Frame 的初始意图(如"做个通用助手、维持自我意识",对应理念稿 §4.5.1)。
  • 对外入口:SubmitExternalInput(content)(推到根 Frame inbox 并发 EXTERNAL_INPUT 事件)、CreateFrame(...)(Frame 构造唯一入口)、Run()(启动调度)、FindFrame(id)

Frame 生命周期 / 资源清理策略:Frame 进入 DONE / FAILED 后不自动清理,其 session / short_memory / inbox / logic_trace 等资源保留至 Agent 停机。理由:父 Frame 可能在任意时刻回读子的 summary / result / 短时记忆快照作为决策输入;思维树作为可回溯的历史本身也有价值。长时运行场景下的压缩策略留待实现阶段按需引入,本设计不做预优化。


12. 对应代码结构

骨架文件:Agent/WeaveAgent.py

主要类:

  • Message / MessageType / Session
  • Memory / ShortTermMemory / LongTermMemory / PublicStore
  • Tool / ToolRegistry
  • Task / TaskStatus
  • Frame / FrameType / FrameStatus
  • Persona
  • Event / EventType / EventLog / Scheduler
  • Agent

文件目前只含字段与方法签名,所有方法体为 ...;实现阶段按本文档逐项落地。


13. 已关闭的历史议题

以下项曾作为待定列出,已在后续讨论中收敛为明确决策(汇总记录):

议题 结论 参考
LLM 输出 schema reasoning + actions[] + next_status 三字段,动作间无依赖 §5.1
Rollback 语义 不做状态回滚;改为"Frame 终结 + 父重试" §5 末尾提示
并发模型 隔离靠树结构;调度器只做 per-Frame 锁 + 全局并发上限 N §7.1
定时器实现 asyncio event loop + loop.call_later §7.2
短时记忆遗忘 Frame 生命期内不自动遗忘,REFLECTING 阶段筛选写长时 §8
长时记忆热度 不适用,关键词树库不使用热度概念 §8
Task 动态升格 不做升格机制;约定 SKIP + spawn_subframe 双动作组合 §1 末尾提示
REFLECTING 必经 DONE / FAILED 只能从 REFLECTING 进入,成败都反思 §4
REFLECTING 回退 严格单向,禁止 REFLECTING→RUNNING;要继续干走 FAILED + 父 spawn 新 Frame §4
TASK_CHANGE 范围 仅异步 Tool 完成时触发,同步路径不发 §7
FrameType.ROOT 去除;根 Frame 由 parent is None 判定 §1
pre_msg 字段 并入 origin / background / goal,不再单列 §2
短时记忆操作 走 Tool(memory/short/*),不作为 Frame 自处理动作;Tool 经 async 上下文 current_frame 定位 §5 / §7.1 / §8
record_logic 动作 去除;logic_trace 由每轮 reasoning 自动追加 §5 / §5.1
Frame id 分配 Agent 全局自增 + 统一 Agent.CreateFrame(...) 工厂 §11
Message 类型 父→子 Stimulate 用 USER,语义扩展为"外部驱动输入" §3
就绪队列去重 OrderedDict[frame_id, None],FIFO + O(1) 去重 §7.1
根 Frame 构造 Agent.__init__(persona, root_goal, root_origin, root_background)CreateFrame(parent=None, ...),无特例 §11
Frame 终结清理 不自动清理;资源保留至 Agent 停机 §11
result vs summary result 对外事实产出(父可读),summary 对内反思抽象(面向长时记忆) §2
Tool 字段命名 Tool.describe 更名为 Tool.description
长时记忆 LLM 参数 use_agentllm_expand_queryuse_agent_for_parentllm_auto_place