2026-10-21
AI
0

目录

开篇
五个基础层
代码地图
一次 Turn 的实际路径
状态归属
五条不变量
单会话单写者
工具调用必须有结果
完整事实与模型视图分离
上下文在明确边界冻结
审批不是最终安全边界
事件模型
系列导航
小结

系列文章:从零实现一个通用智能体
本篇主题:分层、状态所有权、执行链路与安全边界
代码版本:mx-claw 0.1.1

开篇

一次普通请求在 mx-claw 中会留下完整的执行轨迹:用户消息先写入 SQLite,模型以流式响应 返回文本或工具调用,Agent Core 校验工具参数、申请权限、执行工具并把结果交还模型,直到 模型不再请求工具或达到迭代上限。

这个过程同时涉及 CLI、Web、模型 Provider、文件系统、命令进程、权限规则、长期记忆和 Skill。只要状态没有单一归属,恢复、并发和审计很快就会互相冲突。mx-claw 的架构设计从 一开始就把不同状态放到不同层:

text
交互入口 -> Agent Core -> 工具 / 模型 / 存储 \-> 权限 / Hook / 沙箱

本文先给出系统全景。后续文章分别深入 Query Loop、上下文、工具、沙箱、权限、记忆、 Skill、SQLite 和多客户端。

五个基础层

mx-claw 可以按职责拆成五层。

层次当前实现主要职责
入口层apps/cli、apps/server、apps/web接收输入、启动进程、连接本地 API
交互层CLI REPL、Web 会话面板展示流式消息、工具状态与审批
编排层AgentCore、SessionStore会话生命周期、恢复、事件与租约
核心循环层AgentCore.submitTurn()模型请求、工具调用、结果回填与迭代
执行与通信层builtin-tools、model-provider文件、搜索、编辑、命令和模型 API

权限、Hook、沙箱、记忆和 Skill 不单独占用一个顺序层,而是横切到核心循环的不同节点:

text
模型返回工具调用 -> schema 校验 -> PreToolUse Hook -> 权限规则与人工审批 -> 工具二次校验 -> 沙箱命令 / 工作区文件操作 -> tool result -> PostToolUse Hook -> 模型下一轮

这些横切能力共同决定一次工具调用能否执行、如何执行,以及执行后留下什么证据。

代码地图

核心目录与职责如下:

text
apps/ cli/ CLI 入口、REPL、命令和审批展示 server/ 本地 HTTP API、SSE 与 Web 静态资源 web/ React 面板、会话、工作区和设置 packages/ agent-core/ 会话编排、Query Loop、压缩与恢复 protocol/ 消息、事件、工具和权限协议 model-provider/ OpenAI-compatible 通信与密钥配置 builtin-tools/ 文件、搜索、编辑、命令和沙箱适配 permissions/ 规则、模式、命令分类和审计 hooks/ PreToolUse / PostToolUse storage/ SQLite、文件存储、租约、事件和检索 memory/ USER.md、MEMORY.md 与候选审核 skills/ Skill catalog、提案与自主学习 mcp/ MCP 工具客户端 plugins/ 可信插件目录 lsp/ 只读代码诊断 agent-tools/ Subagent 与 worktree 委托

apps/cli 与 apps/server 不实现第二套 Agent 逻辑。两者都实例化 AgentCore,并通过同一 套协议消费事件。差别只在交互形式和进程边界。

一次 Turn 的实际路径

一个 turn 从 submitTurn() 开始。入口先取得会话的跨进程租约,然后执行以下步骤:

  1. 读取并修复已存储的工具调用历史。
  2. 新会话注入 system prompt、Skill 索引和长期记忆快照。
  3. 把用户消息写入会话事实源。
  4. 在每次模型请求前检查上下文窗口。
  5. 接收文本增量、工具调用和 usage 事件。
  6. 把工具调用转换成结构化 tool result。
  7. 将结果重新交给模型,进入下一轮。

循环退出有两种正常方式:

  • 模型返回的响应不包含工具调用;
  • 达到 maxIterations 后显式失败。

取消、租约丢失、Provider 异常和消费者提前停止都会进入失败恢复路径。会话不会停留在 一个没有执行者的 running 状态。

状态归属

架构能否恢复,取决于每类状态是否有明确归属。

状态唯一归属
消息、事件、待审批请求、租约SQLite 会话库
当前进程内的活跃 turn 与等待器AgentCore 内存
Provider 与默认模型config.json
API Key系统安全存储或加密文件
长期语义记忆memory/ 下的 Markdown 记录
程序性 Skillskills/active/
权限规则与审计SQLite 权限表

CLI 与 Web 可以共享数据库和磁盘状态,但各自运行独立的 Core 实例。配置在进程启动后形成 快照;一个进程修改默认 Provider,不会自动改写另一个已运行进程的内存快照。

五条不变量

实现中的大部分复杂度都服务于五条不变量。

单会话单写者

同一会话在同一时刻只能有一个 turn 执行。进程内使用 #activeTurns,跨进程使用 SQLite 租约。失去租约的循环会立即中止。

工具调用必须有结果

每个 assistant tool_call 都必须得到对应 tool result,包括参数错误、未知工具、权限拒绝 和执行异常。缺少结果的历史在发送给 Provider 前会被修复。

完整事实与模型视图分离

SQLite 保存完整 transcript。上下文压缩只改变 Provider 本轮看到的视图,不删除历史消息, 也不改写原始工具结果。

上下文在明确边界冻结

Skill 索引和长期记忆在会话首回合注入。会话进行中新增的 Skill 或记忆只影响后续新会话, 避免同一任务中的行为基准不断漂移。

审批不是最终安全边界

批准只表示该动作允许进入执行路径。工作区路径限制、灾难命令黑名单、环境变量白名单和 平台沙箱仍然生效。allow-all 也不能越过显式 deny 和工具内部硬校验。

事件模型

Agent Core 不直接更新界面。它产生带 sessionId、sequence 和 turnId 的有序事件:

ts
{ type: 'tool.started', sequence: 42, sessionId: 'session-1', turnId: 'turn-1', timestamp: '2026-10-23T00:00:00.000Z' }

CLI 将事件渲染成终端输出,HTTP 服务将事件转换成 SSE,Web 按 sequence 去重并恢复。 同一套事件协议使不同界面可以观察同一个执行过程。

系列导航

  • 第 02 篇:/model 命令、Provider 与 API Key 存储
  • 第 03 篇:Query Loop 与工具调用状态机
  • 第 04 篇:上下文压缩与冻结上下文
  • 第 05 篇:工具协议与工作区边界
  • 第 06 篇:命令沙箱与资源限制
  • 第 07 篇:权限模式、审批与 Hook
  • 第 08 篇:长期记忆与候选审核
  • 第 09 篇:Skill 自主学习与人工批准
  • 第 10 篇:SQLite、租约、事件与检索
  • 第 11 篇:CLI、Web 与多客户端状态同步

小结

mx-claw 的结构不是把模型、工具和界面塞进同一个对象,而是让每一层只拥有自己能够证明的 状态。Query Loop 决定下一步,工具层约束动作,存储层保存事实,权限与沙箱决定风险边界。

下一篇从 /model 命令开始,解释模型连接信息和密钥为什么不进入同一份配置。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:繁星

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!