系统总览
模型循环不是系统边界
一次模型调用可以生成文本或发起工具调用,却无法独立承担一项长期工作。任务可能跨越多轮模型推理、外部操作、用户确认、网络中断和后台执行;如果系统只保存聊天文本,就无法可靠回答“现在进行到哪里”“哪些动作已经发生”“结果是否已经交付”。
aibuddy 因此把 Task 作为系统的稳定执行单元。模型循环负责推进当前步骤,Task 保存目标、身份、消息、状态、工作空间和最终结果。Chat、Team 与 Schedule 可以从不同入口触发工作,但不会各自维护一套 Agent 语义。
这张图也给出了“智能体”与“系统”两章的边界。“智能体”解释上下文、记忆、工具和协作为什么有效;“系统”解释这些能力如何围绕同一项 Task 装配、执行、持久化和恢复。
接口把修改要求变成可检查条件
编码 Agent 修改系统时,首先需要确定四件事:改动应进入哪一层、输入输出是什么、可以依赖哪些能力、哪些既有行为不能改变。如果这些答案只存在于实现细节或团队经验中,Agent 就必须从大量调用链中推断边界,也更容易在错误层级增加条件分支。
aibuddy 把这些答案写入共享接口和 Schema。接口给出允许的输入、输出与依赖,生命周期状态给出必须保持的行为;Web 与 Desktop 只在接口背后选择各自实现。Agent 因而可以先找到修改入口,再把改动限制在对应能力或平台适配器中。
Desktop 的 Task 流恢复是一个具体例子。StreamBuffer 先定义流生产、读取、恢复和状态查询语义;Web 可以提供 Redis 实现,Desktop 则提供进程内实现。增加 Desktop 恢复能力时,不需要复制或改写上层 Task 流程,只需实现同一端口并在 Desktop 组合根中注入。针对 resume() 的行为测试继续检查历史重放与实时数据之间没有缺口或重复。
不同契约分别回答 Agent 修改代码时的具体问题:
| Agent 需要确定 | 系统提供的依据 | 偏离后如何暴露 |
|---|---|---|
| 外部数据是否合法 | @aibuddy/common 的类型与 Zod Schema | 类型检查或请求边界解析失败 |
| 实现应放在哪一层 | TaskClient、Repository 接口与 AppDeps | 依赖无法装配,或缺少接口成员 |
| 一项能力如何调用 | ServerToolConfig、工具 Schema 与 Tool Registry | 定义校验或启动一致性检查失败 |
| 哪些行为必须保持 | Task、Run、Team、Delivery 与 Stream 生命周期 | 状态转换与行为测试失败 |
接口并不保证 Agent 的每次修改都正确。它的作用是减少需要猜测的隐含规则,并把“没有遵循架构”从代码审查中的主观判断,提前转化为编译、解析、启动或测试阶段的明确失败。
Task 把一次调用扩展为完整工作
Task 不等同于一条消息,也不等同于一个正在运行的进程。它是一份持久工作记录,至少连接五类事实:目标与执行身份、消息与工具活动、当前生命周期状态、任务工作空间,以及交付物与用量。
这种建模带来三个直接结果:
- 浏览器或 Desktop 窗口断开,不必同时终止任务。
- 模型可以暂停等待用户、外部事件或上游成员,而不必把等待伪装成失败。
- 运行进程退出后,系统可以依据已保存事实重新装配,而不是依赖仍然存在的内存对象。
任务运行时负责维持这些语义。它为同一工作单元建立互斥推进、传播取消信号,并在消息、交付物、用量和最终事件写入后才裁决终态。
每一步都重新装配工作现场
Agent Runtime 不会把系统拥有的全部信息和能力一次性放入模型上下文。每一步开始时,它根据 Task 当前状态装配输入:稳定规则位于前部,当前目标与进度反映最新事实,记忆、知识、Skills 和长尾工具先以索引出现,只有被选中后才加载详细内容。
渐进式披露在 aibuddy 中不是单个知识库技巧,而是一项跨模块约束:
| 能力 | 首先提供 | 确认相关后提供 |
|---|---|---|
| 用户记忆 | 类型、名称和描述组成的 manifest | 命中的记忆正文 |
| 知识库 | 文档地图与章节结构 | 搜索片段和限定章节正文 |
| Skills | 名称与适用范围 | 完整步骤、脚本和参考资料 |
| MCP 工具 | 服务器与工具摘要 | 具体工具 schema 与执行入口 |
这使能力库可以继续增长,而单次模型调用的选择空间和 token 开销仍由当前任务控制。模型未发现能力、能力未获授权和能力执行失败也因此成为三个可分别诊断的问题。
原始事实与运行时表示分开保存
长期任务需要压缩历史,但压缩不能改写已经发生的事实。aibuddy 保留原始消息和工具结果,并把压缩快照、卸载指针和任务状态作为面向后续运行的派生表示。下一步可以使用更紧凑的上下文,用户和系统仍能回到原始记录核查。
相同原则也用于交付。Agent 在沙箱中生成文件后,交付服务将确认过的文件物化到平台文件存储,并建立归属于 Task 的 Artifact 记录。下载与预览不依赖原任务沙箱继续存在。
运行时由此产生两类输出:一类面向用户,包括流式回复、工具活动和 Deliverables;另一类面向恢复与运营,包括消息、状态、用量、事件和文件索引。二者关联到同一 Task,但服务于不同查询,不能用运维 trace 替代用户实际看到的记录。
协作共享事实,不共享全部推理轨迹
子 Agent 和 Agent Team 都围绕 Task 工作。子 Agent 可以共享父任务工作区,却拥有独立上下文;完成后只返回摘要、证据和交付物位置。Agent Team 进一步使用成员、依赖和消息构成任务图,并在状态提交后唤醒可运行成员。
这种分离避免主 Agent 为每个并行分支重复携带完整轨迹,也保留最终责任边界:成员完成只代表局部分支交付,协调者仍需整合和验证整项任务。
平台适配器改变基础设施,不改变任务语义
Web 与 Desktop 共享 Task、消息、工具事件、Deliverable 和 Agent Runtime 的领域语义。Web 将这些接口连接到 Server、PostgreSQL、Redis、对象存储和 worker;Desktop 将它们连接到 Electron Main、SQLite、本地文件和本机执行。
共享接口不意味着平台能力完全对等。Web 可以通过 Redis 提供跨实例锁、跨实例流恢复和队列重试;Desktop 在主进程生命周期内提供进程内锁与内存流恢复,并拥有本机文件、设备凭证和 stdio MCP。系统文档会明确说明保证范围,不用同一个接口名称掩盖不同故障边界。
七项约束贯穿整个系统
| 系统约束 | 具体体现 |
|---|---|
| 契约先于实现 | 共享 Schema、服务端口、工具定义和生命周期转换先确定边界,Core 与宿主实现不能各自解释语义 |
| 单一推进者 | 锁和运行登记阻止同一工作单元形成两条并发轨迹 |
| 连接不等于运行 | UI 断开不直接改变后台任务状态 |
| 能力按需披露 | 记忆、知识、Skills 与 MCP 不以完整正文常驻上下文 |
| 原始事实不可被投影覆盖 | 压缩快照和交付索引与原始消息、文件分开保存 |
| 非关键增强可以降级 | 摘要、标题和后台记忆提取失败时不阻断主任务 |
| 完成晚于持久化收尾 | 消息、交付物、用量和终态事件保存后才对外完成 |
这些约束比某个模型或工具列表更能定义系统行为。模型、存储和传输实现可以替换,但违反其中任意一项都会降低任务的可恢复性或可验证性。
当前系统边界
- aibuddy 使用 AI Gateway 或用户配置的提供商,不提供模型权重。
- 当前可用执行后端是 Node sandbox;Daytona 与 E2B 仍属于未完成适配器。
- 记忆、知识、Prompt 和 Skills 已形成明确更新通道,但自动 eval、canary 与回滚尚未组成统一发布闭环。
- 管理侧评估页面仍包含样例数据,不能视为统一线上评估后端。
- Web 与 Desktop 共享领域行为,但队列、恢复、MCP 和身份能力必须按宿主分别判断。