跳到主要内容

内容块、工具调用状态机与权限

ACP 的设计哲学里有一条是 UX-first(architecture.mdx:13):协议要让编辑器能清晰渲染 agent 的意图——正在说什么、在调什么工具、改了哪个文件、要不要授权。本章就是这套「可渲染的语义」。

1. ContentBlock —— 一切可显示内容的统一载荷

它要解决的小问题。 prompt 里要带文本、图片、引用的文件;agent 的输出也是文本、图片、资源。如果各处各定义一套,就乱了。

思路。 定义一个 ContentBlock 枚举,prompt、流式输出、工具结果全都复用它(content.rs:25-31)。而且它刻意兼容 MCP 的 JSON 表示——这样 agent 能把 MCP 工具的输出原样转发,不用再转换一层(content.rs:30-31introduction.mdx:37)。

五种内容块(content.rs:38):

变体是什么备注
Text纯文本或 Markdown所有 agent 必须支持;client 应按 Markdown 渲染(content.rs:39-42)
ResourceLink指向 agent 可访问的资源的引用所有 agent 必须支持
Image图片在 prompt 中需 image 能力
Audio音频在 prompt 中需 audio 能力
Resource直接内嵌完整资源内容embeddedContext 能力;优先用它,省一次往返(content.rs:56-60)

tag = "type" 做区分(content.rs:35),线缆上长这样:{ "type": "text", "text": "..." }

2. SessionUpdate —— agent 往 UI 推的所有「实时进度」种类

回合进行中,agent 发的每条 sessionUpdate 通知里包一个 SessionUpdate(client.rs:99),用 sessionUpdate 字段区分类型。完整种类:

变体(线缆 sessionUpdate 值)含义
user_message_chunk用户消息的分片(回放/回显)
agent_message_chunkagent 回复的分片(主要的「正文流」)
agent_thought_chunkagent 内部思考的分片(可单独渲染)
tool_call新工具调用开始
tool_call_update工具调用状态/结果更新
planagent 的执行计划(见 §5)
available_commands_update可用命令变化
current_mode_update当前模式变化
config_option_update会话配置项更新
session_info_update会话元信息(标题、时间戳)更新
usage_update上下文窗口占用与花费更新

注意「思考」和「回复」是分开的变体——这样编辑器能把 agent 的内心独白折叠/灰显,和正式回复区分开。这就是 UX-first 的体现。

3. ToolCall —— 一个带状态机的「动作」

它要解决的小问题。 LLM 说「我要读文件 / 跑命令」,编辑器得把这件事作为一个有进度、有结果的实体画出来,而不是一行 log。

思路。 ToolCall(tool_call.rs:29)是个有 id、有标题、有 kind、有 status、有 content 的实体。它的状态会就地更新:agent 先发一个 tool_call,之后发若干 tool_call_update 改它的状态/内容(tool_call.rs:188 ToolCallUpdate,只带变化的字段)。

状态机(tool_call.rs:471 ToolCallStatus):

pending ───▶ in_progress ───▶ completed
(排队/等审批) (执行中) └──▶ failed

kind 给编辑器选图标用(tool_call.rs:431 ToolKind):read / edit / delete / move / search / execute / think / fetch / switch_mode / other(默认 other)。

locations 字段列出受影响的文件路径,用途注释写得很直白:「让 client 实现『跟随』功能」(tool_call.rs:50-51)——即编辑器能随 agent 自动跳到正在改的文件。

4. 工具结果的三种内容:普通 / diff / 终端

ToolCallContent(tool_call.rs:502)有三种,这是 ACP 「为编码 UX 定制」最明显的地方:

变体是什么关键字段
Content普通内容块(文本/图片/资源)包一个 ContentBlock
Diff文件修改,以差异形式展示path + oldText(新文件为 None)+ newText(tool_call.rs:627)
Terminal嵌入一个用 terminal/create 建的终端terminalId;注释提醒「必须在 terminal/release 之前嵌入」(tool_call.rs:507-512)

Diff 单列一种、而不是塞进文本,是因为编辑器要画成红绿对照的 diff 视图——这正是 introduction.mdx:37 说的「为有用的 agent 编码 UX 元素(如显示 diff)定制类型」。

5. Plan —— agent 的「待办清单」

复杂任务里,agent 可以发一个 Plan(plan.rs:33),让用户看到它打算分几步走。每个 PlanEntry(plan.rs:446)有:

  • content:这步要干什么
  • priority:high / medium / low(plan.rs:502)
  • status:pending / in_progress / completed…(plan.rs:518)

关键语义:更新计划时,agent 必须发完整的全量列表,client 用它整体替换旧计划(plan.rs:34-37)——不是增量打补丁。这把「计划同步」做成了无状态的幂等替换,简单可靠。

6. 权限请求 —— 「信任但要确认」

它要解决的小问题。 agent 要改文件、删东西、跑命令,这些是危险动作。ACP 的信任模型是「你在用编辑器跟一个你信任的模型对话,但你仍要对工具调用有控制权」(architecture.mdx:14)。

机制。 agent 在动手前发 session/request_permission(client.rs:653 RequestPermissionRequest),带上:

  • toolCall:要执行的工具调用详情(一个 ToolCallUpdate)
  • options:给用户选的若干 PermissionOption

每个选项有个 kind,告诉 UI 该用什么图标/措辞(client.rs:776 PermissionOptionKind):

kind含义
allow_once这次允许
allow_always允许并记住
reject_once这次拒绝
reject_always拒绝并记住

用户选完,Client 回 RequestPermissionResponse,outcome 是 Selected{optionId};若这轮 prompt 被取消,则回 Cancelled(client.rs:837 RequestPermissionOutcome,与 01 章 §7 的取消握手对应)。

7. 代码地图(本章)

主题文件符号
内容块(prompt/输出共用)…/src/v1/content.rsContentBlock / TextContent
流式更新种类…/src/v1/client.rsSessionUpdate / SessionNotification
工具调用实体…/src/v1/tool_call.rsToolCall / ToolCallUpdate
工具状态 / 类别…/src/v1/tool_call.rsToolCallStatus / ToolKind
工具结果(diff/终端)…/src/v1/tool_call.rsToolCallContent / Diff / Terminal
计划…/src/v1/plan.rsPlan / PlanEntry / PlanEntryStatus
权限请求…/src/v1/client.rsRequestPermissionRequest / PermissionOption / RequestPermissionOutcome