前面十一篇文章一直站在“怎样用 SDK 构建 Agent”的角度讨论 Deep Agents。这一篇把视角切换到产品和协议:如果希望直接在终端使用一个编码 Agent,或者让编辑器接入自己编写的 Deep Agent,deepagents、Deep Agents Code 和 ACP 分别负责什么?ACP、MCP、A2A 又应该怎样组合?
先说明成熟度边界:PyPI 将 deepagents-code 标记为 Beta,deepagents-acp 仍是快速演进的早期适配器。本文通过锁定版本、阅读安装包源码和真实 stdio 子进程测试说明锁定版本的行为,但不把它们描述成已经稳定的长期契约。
本文完成四项可复核工作:
- 隔离安装 deepagents-code==0.1.41,检查真实 CLI、SDK 依赖和运行方式;
- 梳理 Code 产品怎样组合 Memory、Skills、SubAgent、Sandbox、审批与会话恢复;
- 使用 deepagents-acp==0.0.9 建立可独立运行的 ACP stdio Server;
- 用真实子进程验证初始化、Session、Prompt、事件、权限、路径隔离和取消。
1. Deep Agents Code、SDK 与 ACP 的定位
1.1 Code 是产品,SDK 是 Harness
deepagents 是 Agent Harness:它在 LangChain Tool Calling 循环和 LangGraph Runtime 之上,预装任务规划、文件系统、上下文管理、SubAgent 等能力。开发者通过 create_deep_agent() 构建自己的 Agent。
Deep Agents Code 是基于这个 Harness 组装好的终端产品。它增加了终端 UI、会话恢复、Web Search、项目 Memory、Skills、SubAgent、远程 Sandbox、非交互模式、人工审批和配置管理。目标是“直接让 Agent 在项目中工作”时使用 Code;目标是“构建企业数据分析 Agent”时仍应使用 SDK。
ACP,即 Agent Client Protocol,是客户端与 Agent 之间的协议。它定义 Session、Prompt、增量更新、Tool 展示、权限请求和取消,不负责替模型思考,也不定义业务 Tool 怎样实现。
| 层次 | 主要对象 | 本文对应实现 |
|---|---|---|
| 产品 | TUI、线程、配置、Sandbox、审批 | dcode |
| Agent Harness | Model、Middleware、Tool、State、SubAgent | deepagents |
| 协议适配 | Session、Prompt、Update、Permission | deepagents-acp |
| 协议 Schema/Transport | JSON-RPC、Content Block、stdio | agent-client-protocol |
1.2 ACP、MCP 与 A2A 连接不同的边
最容易记住、也最容易被过度简化的一句话是:ACP 连接客户端,MCP 连接工具,A2A 连接 Agent。更准确的区别是:
- ACP 让编辑器或其他交互客户端控制 Agent 会话,并接收结构化执行过程;
- MCP 让 Agent Host 发现并调用 Tool、Resource 和 Prompt;
- A2A 面向独立 Agent 服务之间的任务、消息和产物协作。
三种协议可以同时出现在一个系统里,但协议名字相同或都使用 JSON,并不代表可以互相替代。

1.3 本文锁定环境与版本证据
本文同时涉及系列示例环境和 Code 产品环境,必须隔离:
| 环境 | 锁定版本 | 证据与用途 |
|---|---|---|
| Python | 3.12.11 | 本机实测 |
| deepagents | 0.6.12 | 系列示例主线 |
| deepagents-acp | 0.0.9 | requirements-lock.txt 与安装元数据 |
| agent-client-protocol | 0.11.0 | ACP Python SDK,协议常量为 1 |
| deepagents-code | 0.1.41 | 本机独立安装 |
| Code 内部 deepagents | 0.7.0a7 | dcode –version 实测 |
实际输出为:
deepagents-code 0.1.41
deepagents (SDK) 0.7.0a7
这张表同时揭示了环境隔离问题:Code 内部使用预发布 SDK,不能安装进系列锁定的 .venv_deepagent,否则前十一篇的稳定回归环境会被升级。
协议版本相同也不代表实现完全兼容。两个实现都报告 protocolVersion=1,仍可能在 Content Block、Session Resume、Permission Option、Tool Update 粒度和实验性扩展方面不同。必须同时记录协议、SDK、适配器、Agent、客户端和模型版本。
2. 安装和运行 Deep Agents Code
2.1 使用独立工具环境安装
官方快速安装命令是:
curl -LsSf https://langch.in/dcode | bash
这适合个人快速体验,但生产环境不应直接把网络响应送入 Shell。至少先下载、计算摘要、人工或自动审查,再执行:
installer="$(mktemp)"
curl -LsSf https://langch.in/dcode -o "$installer"
shasum -a 256 "$installer"
less "$installer"
bash "$installer"
rm -f "$installer"
安装脚本没有经过本文验证的 DEEPAGENTS_CODE_VERSION 参数,因此不使用“给 curl 设置版本环境变量再 pipe 给 bash”的写法。Shell 中 VAR=value curl … | bash 只把变量传给 curl,也不会自动传给管道另一侧的 bash。
本文使用可验证的 PyPI 版本和 uv tool 隔离安装:
tool_root="$(mktemp -d /tmp/deepagents-code-tool.XXXXXX)"
bin_root="$(mktemp -d /tmp/deepagents-code-bin.XXXXXX)"
UV_TOOL_DIR="$tool_root" \
UV_TOOL_BIN_DIR="$bin_root" \
uv tool install 'deepagents-code==0.1.41'
"$bin_root/dcode" --version
团队构建还应把 wheel、依赖锁文件和摘要保存在制品仓库,以免安装同一顶层版本时解析出不同的间接依赖。

2.2 doctor 检查的是哪一个环境
dcode doctor 会输出版本、平台、Commit、安装位置、Tracing 和配置目录:
home="$(mktemp -d /tmp/dcode-home.XXXXXX)"
HOME="$home" "$bin_root/dcode" doctor
rm -rf "$home"
核心输出如下:
Diagnostics ✓
├ deepagents-code: 0.1.41
├ deepagents (SDK): 0.7.0a7
├ Commit hash: d46a2cb
├ Python: 3.12.11
├ Platform: darwin-arm64
└ Install method: other
隔离 HOME 的目的只是验证命令能在干净目录启动,它不会读取真实用户配置、凭据或 Session 数据。排查真实配置问题时,要在真实 HOME 下执行 dcode doctor 和 dcode config show,并在分享输出前脱敏。
2.3 三种运行方式与权限范围
交互模式直接启动 TUI:
dcode
非交互模式适合 CI:
dcode -n "运行测试并解释失败原因" \
--max-turns 10 \
--timeout 120 \
-S "pytest,git"
当前 0.1.41 的真实帮助信息表明:–timeout 超时时退出码为 124;本地 Shell 默认不开放;-S 是 Shell 命令允许列表。允许 python、node、bash 等解释器仍然等于允许执行大量任意逻辑,所以 Allowlist 不是 Sandbox。
ACP Server 模式由编辑器或 ACP Client 启动:
dcode --acp
-y/–auto-approve 与 -S all 作用范围不同:前者自动批准交互模式中的 Tool Call,后者允许非交互模式调用任意 Shell 命令。它们都需要谨慎使用,但不能混写成同一个开关。对不可信仓库,应优先使用远程 Sandbox,而不是扩大本机权限。
2.4 配置、Memory、Skills 与 SubAgent
用以下命令查看有效配置及来源:
dcode config show --verbose
dcode config get interpreter.memory_limit_mb
dcode config path
dcode doctor
模型解析要结合当前 CLI 帮助和 config show 判断。显式 –model 用于本次运行,默认模型和最近模型由 Code 的配置与状态管理,Provider 凭据还可能触发自动发现。不要只看一个环境变量就断言最终模型。
Code 使用两套数据目录:
- ~/.deepagents/ 和项目 .deepagents/ 存放 Code 专用配置、Agent、Skills、Memory 与状态;
- ~/.agents/ 和项目 .agents/ 存放可以被其他 AI 工具复用的 Skills。
项目指令会合并包内基础 Prompt、用户级 AGENTS.md、项目 .deepagents/AGENTS.md 与项目根 AGENTS.md。Memory 应保存构建命令、目录职责和安全规则等稳定事实,不应保存一次任务的临时 Todo。
项目 Skill 可以放在 .deepagents/skills/
2.5 Sandbox 不是“换台机器跑命令”
dcode --sandbox daytona
dcode --sandbox daytona --sandbox-id existing-id
远程 Sandbox 的价值是建立本机与不可信代码之间的边界。仍需限制 CPU、内存、磁盘、网络出口、凭据、最长运行时间和产物下载。使用 –sandbox-id 附着已有环境时,Code 通常不会为本次任务创建和销毁它;旧进程、文件、环境变量与缓存可能污染新任务,必须额外设计清理和过期策略。
3. ACP 的核心模型
3.1 Client、Agent 与 stdio Transport
本文的四个角色如下:
| 角色 | 本文实现 | 职责 |
|---|---|---|
| ACP Client | RecordingClient | 发送请求、记录 Update、回答 Permission |
| ACP Agent | AgentServerACP | 管理 Session,桥接 ACP 与 LangGraph |
| Transport | SDK 的 stdio Transport | 按 ACP Python SDK 的 JSON-RPC framing 收发数据 |
| Deep Agent/Graph | CompiledStateGraph | 执行模型、Tool 和状态流转 |
不要手工用 print(json.dumps(…)) 拼协议。SDK 会处理字段别名、联合类型、请求 ID 和连接关闭;手写报文极易在 Schema 升级后失效。
stdio Server 的 stdout 只能输出协议 Frame,日志必须进入 stderr:
import logging
logging.basicConfig() # 默认写入 stderr
logging.getLogger(__name__).info("server started")
这个规则必须在编写 Server 前确定。第三方库如果把进度条或调试信息写到 stdout,Client 可能报 JSON 解析错误或连接关闭。
3.2 Session 与 Prompt Turn

一个最小 Turn 包含:
- initialize 协商协议版本和 Capability;
- session/new 创建 Session,并提交工作目录;
- session/prompt 发送 Content Block;
- Agent 通过 session/update 回传文本、计划和 Tool 进度;
- 敏感 Tool 通过权限请求等待客户端决策;
- Prompt 返回 Stop Reason;客户端也可发送 session/cancel。
Session 与 Prompt Turn 不是同一生命周期。一个 Session 可以包含多个 Turn;取消当前 Turn 不等于关闭、删除或吊销 Session。生产系统还应明确 Session 的关闭、过期、轮换、Fork/Resume 所有权与重新认证规则。
3.3 Capability、Adapter 和模型能力必须分开
当前安装的 deepagents-acp==0.0.9 在 initialize 中声明:
PromptCapabilities(image=True)
这只证明适配器对 ACP 图片 Content Block 提供协议转换路径,不证明实际模型能看图,也不授权 Agent 读取任意本地图片。本文测试只覆盖文本 Prompt,没有做端到端视觉断言,因此不把 image=True 写成“已验证图片理解”。
当前 ACP SDK Schema 还包含其他 Content Block;是否可以使用,要分别检查协议版本、客户端、适配器转换、模型和安全策略。本文未验证音频链路,所以不宣称支持音频。
Session Update 同样要分三层描述:
| 能力层 | 示例 | 本文结论 |
|---|---|---|
| ACP Schema 定义 | message、plan、tool、usage 等 Update 类型 | 代表协议可表达 |
| 0.0.9 Adapter 发出 | 取决于 LangGraph Stream 和适配代码分支 | 需要源码与场景验证 |
| 本文自动化测试收到 | agent_message_chunk、Permission Request | 已实测 |
agent_thought_chunk 即使出现,也只应称为可公开的分析/状态信息,不能暗示客户端拿到了模型私有 Chain-of-thought。
3.4 Permission 是结构化决策,不是聊天确认
ACP 的 Permission Response 选择 Server 本次请求提供的 optionId。approve、reject 和 approve_always 是 deepagents-acp==0.0.9 当前适配器生成的选项 ID,不是所有 ACP Agent 必须实现的通用枚举。
客户端不能固定返回 approve,而应先验证这个选项确实存在:
async def request_permission(self, **kwargs):
option_ids = {option.option_id for option in kwargs["options"]}
if self.decision not in option_ids:
raise ValueError(f"permission option was not offered: {self.decision}")
return RequestPermissionResponse(
outcome=AllowedOutcome(
outcome="selected",
optionId=self.decision,
)
)
Capability 不是授权,用户批准也不是访问控制。可靠权限链至少包含:认证用户、Session 所有权、Policy、参数摘要、HITL、Tool 内部强制检查、Sandbox 与审计。

3.5 Cancel 是控制面信号
session/cancel 表示客户端不再希望当前执行继续。本文安装的适配器在流式循环中检查取消标记,测试得到 stop_reason=”cancelled”。这不等于 Provider HTTP 请求、子进程、Sandbox 命令或已发出的数据库写入一定停止,更不等于副作用回滚。
因此还要为下游调用设置超时和取消传播,为子进程设置进程组终止,为写操作使用幂等键、事务、状态查询与补偿操作。当前 0.0.9 的取消标记还是适配器实例级状态;多 Session 并发取消行为必须在真实部署拓扑中单独回归。
4. 使用 deepagents-acp 暴露 Deep Agent
4.1 可独立运行的 Server
examples/p12_code_acp/acp_server.py 的正式入口如下:
def build_server() -> AgentServerACP:
# 独立 stdio 进程不会像 Agent Server 那样注入 checkpointer。
agent = create_deep_agent(
model=build_model(),
checkpointer=InMemorySaver(),
)
return AgentServerACP(agent=agent)
if __name__ == "__main__":
demo_kind = os.environ.get("P12_ACP_DEMO")
server = build_demo_server(demo_kind) if demo_kind else build_server()
asyncio.run(run_agent(server))
这里必须传具体 Checkpointer。checkpointer=True 表示期待运行时注入持久层;独立 stdio 进程没有该注入,可能出现握手成功但 Prompt 返回 Internal error。教学示例使用 InMemorySaver,生产环境再替换为经过同一 ACP/HITL 测试的持久 Saver。
4.2 为什么保留确定性 Graph
协议测试要验证子进程、JSON-RPC、Content Block、Session Update 和权限,而不是赌模型今天会不会调用 Tool。本文保留一个真实 LangGraph,只把输出设计成确定性结果:
def build_echo_graph():
def reply(state: MessagesState) -> dict[str, list[AIMessage]]:
content = state["messages"][-1].content
if not isinstance(content, str):
text_parts = [
str(block.get("text", ""))
for block in content
if isinstance(block, dict) and block.get("type") == "text"
]
content = "".join(text_parts)
return {"messages": [AIMessage(content=f"ACP echo: {content}")]}
builder = StateGraph(MessagesState)
builder.add_node("reply", reply)
builder.add_edge(START, "reply")
builder.add_edge("reply", END)
return builder.compile(checkpointer=MemorySaver())
这里只接受明确的文本块,不对未知字典或任意对象调用 str() 后当成用户正文。
4.3 自定义 Tool 必须自己做路径授权
自定义 save_note 不会自动继承内置文件 Backend 的权限。修正后的实现要求从可信部署配置提供工作区根目录,再拒绝路径穿越:
@tool
def save_note(path: str, content: str) -> str:
"""Save a UTF-8 note below the trusted workspace root."""
root_value = os.environ.get("P12_ACP_WORKSPACE")
if not root_value:
raise RuntimeError("P12_ACP_WORKSPACE is required")
root = Path(root_value).resolve(strict=True)
requested = Path(path)
target = requested.resolve() if requested.is_absolute() else (root / requested).resolve()
if not target.is_relative_to(root):
raise ValueError(f"path escapes workspace: {path}")
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(content, encoding="utf-8")
return f"saved:{target}"
Path.is_relative_to() 在本系列 Python 3.12 中可用。这段代码能阻止常见 ../ 和符号链接逃逸,但高安全场景还要防止检查后路径被替换的 TOCTOU 问题,使用目录文件描述符、容器挂载和操作系统权限建立更强边界。
4.4 真正从子进程连接
测试不是直接调用 AgentServerACP.initialize(),而是启动真实子进程:
async with spawn_agent_process(
client,
sys.executable,
"-m",
"p12_code_acp.acp_server",
env=env,
cwd=os.getcwd(),
) as (connection, process):
initialized = await connection.initialize(PROTOCOL_VERSION)
session = await connection.new_session(cwd=os.getcwd())
response = await connection.prompt(
session_id=session.session_id,
prompt=[text_block("hello protocol")],
)
链路实际经过 ClientSideConnection -> stdin -> Python 子进程 -> AgentServerACP -> LangGraph -> stdout -> Client。测试结束后还检查子进程退出码为 0,stderr 不包含 Traceback,从而捕获 stdout 污染、导入失败和粗暴退出。
客户端提交的 cwd、附加目录和 MCP 配置都属于外部输入。生产 Server 要将规范化路径限制在允许根目录,并把 Session 与经过认证的用户、租户和 Workspace 绑定。
5. ACP 协议回归测试
5.1 九个测试项覆盖了什么
执行:
cd source/_posts/deepagent/examples
.venv_deepagent/bin/python -m compileall -q p12_code_acp
.venv_deepagent/bin/ruff check p12_code_acp
.venv_deepagent/bin/python -m pytest p12_code_acp -q
实测结果:
All checks passed!
......... [100%]
这是 9 个 Pytest Test Item,不是“完整覆盖 ACP 的 9 类能力”。它们验证:
- 正式 Server 与 Demo Server 可构造;
- 非法 Demo 类型被拒绝;
- 真实 initialize 与 session/new 握手;
- 真实 Prompt 回传 agent_message_chunk;
- session/cancel 使当前慢 Turn 返回 cancelled;
- Client 拒绝选择 Server 未提供的 Permission Option;
- save_note 拒绝工作区外路径;
- approve 执行一次文件副作用;
- reject 阻止文件副作用。
权限测试还断言 Permission Request 中的 session_id 是当前 Session,tool_call.raw_input 与待执行路径、内容一致。它证明的是单 Session、单 Tool Call 场景下结构化决策控制副作用;它没有证明并发 Session、双 Tool Call、错误 Tool Call ID、Response 重放和参数变化后的批准失效。
5.2 演示输出
.venv_deepagent/bin/python -m p12_code_acp.run_demo
真实输出:
echo: protocol=1 stop=end_turn permissions=0 message=ACP echo: hello protocol
approve: stop=end_turn permissions=1 file_exists=True message=权限决策已经处理。
reject: stop=end_turn permissions=1 file_exists=False message=权限决策已经处理。
“拒绝”是正常业务结果,不是 Transport 或 Protocol 异常。Agent 应能接收拒绝结果并调整方案。
5.3 真实模型普通 Prompt 的 ACP stdio 验证
live_demo.py 不设置 P12_ACP_DEMO,因此 Server 内部创建真实在线 Deep Agent。使用 OpenAI 兼容网关时,网关返回模型 ID qwen3.7-plus,脱敏结果为:
{
"scene": "p12_live_acp_stdio",
"status": "passed",
"model": "qwen3.7-plus",
"latency_ms": 4912,
"protocol_version": 1,
"stop_reason": "end_turn",
"permission_requests": 0,
"final_answer": "ACP 在线会话成功"
}
这条在线记录只证明“真实模型普通文本 Prompt 经过 ACP stdio 完成”。permission_requests=0 清楚表明它没有验证真实模型 Tool Calling、HITL、Diff、MCP、Cancel、图片或 Session Resume;这些协议行为由确定性回归测试覆盖,不能把两类证据合并成更大的结论。
5.4 当前版本的已知边界
| 边界 | 来源 | 影响 |
|---|---|---|
| 自由格式 interrupt() 不能直接映射为固定 Permission UI | 0.0.9 安装源码 | 应使用标准 HITL action_requests |
| Cancel 使用适配器内标记 | 源码与本文实测 | 不保证下游任务和副作用停止 |
| approve_always 使用 Session 内缓存 | 源码 | 不是企业 Policy Engine |
| 某些异步持久 Checkpointer 曾报告 Interrupt 状态竞态 | Issue #4358,已关闭 | 不能从 MemorySaver 测试推导生产 Saver 可靠 |
| 嵌套 SubAgent HITL 到父 task Tool Call 的映射需按版本验证 | Issue #4033 | 生产前必须增加嵌套场景测试 |
锁定安装源码包含 PR #4542 所描述的“离开流迭代后再读取 Interrupt State”处理,但这不替代真实持久 Saver 测试。升级适配器前后都应重跑 Permission、Cancel、进程重启、并发 Session 和嵌套 SubAgent 用例。
6. ACP、MCP 与 A2A 的系统边界
6.1 按连接双方和生命周期选择
| 维度 | ACP | MCP | A2A |
|---|---|---|---|
| 连接双方 | Client/IDE ↔ Agent | Agent Host ↔ Tool/Resource Server | Agent Client ↔ 远程 Agent |
| 核心对象 | Session、Prompt、Update、Permission | Tool、Resource、Prompt | Agent 能力、Message、Task、Artifact |
| 生命周期 | 人机 Session 与 Turn | 能力发现和调用 | 独立远程任务与协作 |
| 本文传输 | stdio | 可为 stdio/HTTP,取决于 MCP 配置 | 未实现;以采用的 A2A 规范版本为准 |
| 本文验证 | 是 | 第 3 篇验证 | 否 |
假设用户在编辑器中提出“分析订单下降原因并生成修复补丁”:
- 编辑器通过 ACP 创建 Agent Session;
- Agent 通过 MCP 调用订单指标服务;
- Agent 通过 A2A 或业务任务协议委派远程数据 Agent;
- 主 Agent 修改文件,通过 ACP 回传 Tool 状态或 Diff;
- 编辑器用 ACP Permission 请求用户批准测试命令;
- Agent 在 Sandbox 中执行测试并回传结果。
6.2 ACP Session 中携带 MCP 配置
ACP Schema 可以让 session/new 携带 MCP Server 配置,但只有 Agent 声明并实现相应 Capability 时客户端才能使用。即便配置通过 ACP 送达:
- ACP 只负责将配置交给 Agent;
- Agent 内部仍建立 MCP Client;
- MCP Server 仍按 MCP 协议初始化和调用;
- MCP 的认证、授权、结果解析和生命周期仍独立存在。
stdio MCP 配置能启动本地进程,本身就是代码执行入口。HTTP MCP 还要验证 TLS、Server Identity、OAuth 流程、Redirect URI 和 Token 存储,不能因为它“只是工具配置”就降低信任要求。
6.3 什么时候可以把远程 Agent 包装成 MCP Tool
远程 Agent 完全可以包装为 MCP Tool。短时、单请求单响应、无独立状态、无取消、无 Artifact 的能力,这样做通常更简单。
当远程端需要能力发现、长任务状态、查询/取消、异步消息、独立 Artifact、跨组织认证时,包装成一个 Tool 会隐藏这些一等任务语义。这时应考虑 A2A 或明确的业务任务协议。不是“技术上不能包装”,而是包装后是否丢失系统需要的生命周期。
6.4 dcode –acp 还是自定义 Server
| 需求 | 推荐方案 |
|---|---|
| 直接把通用编码 Agent 接入兼容客户端 | dcode –acp |
| 使用 Code 的 Memory、Skills、Sandbox 和配置 | dcode –acp |
| 暴露自定义业务 Tool、State 和策略 | 自定义 AgentServerACP |
| 自定义 Session 身份、审计和租户隔离 | 自定义 Server/适配器 |
| 面向通用业务方提供成熟 HTTP API | 当前优先考虑 LangGraph Agent Server |
| 远程 ACP Transport | 按所采用的 ACP RFD/规范单独实现和验收 |
本文实现的是 stdio ACP,不把结论扩大成“ACP 永远只能本地使用”,也不把 stdio Server 直接暴露为公网 HTTP 服务。
7. 安全、生产治理与排错
7.1 Session ID 不是认证凭据
多用户系统至少要校验:
(tenant_id, user_id, session_id) -> session owner + workspace + policy
Session 需要创建、恢复、轮换、关闭、过期和吊销规则。Fork 与 Resume 必须重新检查所有权,不能因知道随机 ID 就获得访问权。stdio 的攻击面比公开网络服务小,但本机恶意进程、插件、环境变量和项目 Prompt Injection 仍然存在。
7.2 Permission、Policy 与副作用
权限 UI 至少展示 Tool 名、真实参数、工作目录、目标路径、Diff、网络目标和批准范围。参数进入终端、HTML 或 Markdown UI 前还要处理 ANSI/控制字符、HTML、Markdown 注入和双向文本控制字符。
本文安装的 0.0.9 为 Permission Request 生成 approve、reject、approve_always。其源码还会对 execute 做命令类型提取,并对部分危险 Shell 模式避免自动批准。这只是特定版本的缓解措施,不是 Shell 安全证明:同名二进制、PATH、环境变量、脚本内容、子命令和工作目录都可能改变真实效果。高风险操作不应提供永久批准。
企业权限缓存至少绑定用户、租户、Session、Tool Call、参数摘要、工作目录、可执行文件身份、策略版本和过期时间。参数一旦变化,旧批准必须失效;重复响应与跨 Session 响应必须拒绝。
7.3 审计与错误分层
建议记录但不泄露以下关联字段:
trace_id, tenant_id, user_id, session_id, prompt_turn_id,
tool_call_id, permission_decision, policy_version,
acp_protocol_version, adapter_version, agent_version,
client_implementation, model_id, session_mode, sandbox_id
API Key、Authorization Header、Prompt 中的个人信息、文件 Secret、Diff 和 Sandbox 环境变量需要脱敏。可观测性不是把所有内容永久记录下来。
错误至少分五层:
| 错误层 | 示例 | 推荐处理 |
|---|---|---|
| Transport | 子进程退出、Frame 损坏 | 重启,保留脱敏 stderr |
| Protocol | 未知方法、参数不合法 | JSON-RPC 结构化错误 |
| Authorization/Policy | Session 越权、Tool 被禁止 | 稳定拒绝码,不执行副作用 |
| Agent | 模型失败、上下文超限 | 可重试、换模型或结束 Turn |
| Tool/业务 | 文件不存在、外部 API 失败 | Tool 结果或业务错误 |
7.4 编辑器/客户端配置
具体字段以客户端官方文档为准,通用原则是使用绝对解释器路径和固定工作目录:
{
"name": "DeepAgent Python",
"command": "/absolute/path/.venv_deepagent/bin/python",
"args": ["-m", "p12_code_acp.acp_server"],
"cwd": "/absolute/path/source/_posts/deepagent/examples",
"env": {}
}
不要把 API Key 写进编辑器 JSON。配置可能被同步、备份、提交或收集进错误报告;凭据应由系统钥匙串、客户端 Secret Store 或受控启动器注入。
7.5 常见故障
Client 提示 Connection closed
检查解释器路径、模块导入、stdout 污染和模型初始化错误。直接运行 Server 后没有普通输出是正常的,因为它在等待 stdin 请求;诊断信息应看 stderr。
握手成功但 Prompt 返回 Internal error
确认图有具体 Checkpointer。独立 stdio 进程不能依赖 checkpointer=True 的运行时注入;先用 InMemorySaver() 验证,再接入持久 Saver。
权限窗口没有出现
确认敏感 Tool 在执行前由 HumanInTheLoopMiddleware 产生标准 action_requests,并检查实际持久 Saver 是否保存了 Interrupt。自由格式 interrupt() 不能自动等同于固定权限 UI。
dcode 找不到或模型不符合预期
uv tool dir --bin
dcode --version
dcode doctor
dcode config show --verbose
不要只根据 PATH 中存在一个 dcode 就判断运行的是目标环境。
8. 总结
Deep Agents Code、Deep Agents SDK 和 ACP 位于三个不同层次:Code 是可以直接使用的编码 Agent 产品,SDK 是构建 Agent 的 Harness,ACP 是客户端控制 Agent 会话的协议适配边界。
本篇最重要的结论有七点:
- Code 0.1.41 使用 deepagents 0.7.0a7,应与系列锁定的 deepagents 0.6.12 隔离;
- ACP 的完整链路包括 Session、Prompt、Update、Permission、Cancel 和 Stop Reason,不只是握手;
- Capability、适配器转换和模型真实能力是三件不同的事;
- 权限测试必须验证副作用,也必须绑定 Session、Tool Call 与参数;
- HITL、Policy、Tool 内访问控制和 Sandbox 是不同防线;
- ACP、MCP 与 A2A 分别服务不同连接边,远程 Agent 是否包装为 Tool 取决于是否需要独立任务语义;
- 协议版本匹配只代表可以协商 Schema,生产兼容性仍依赖锁版本、源码审查和端到端回归测试。
本文代码位于 examples/p12_code_acp/,配图的 .drawio 与 .png 均保存在 assets/ 并保持同名。下一篇将把 Backend、Sandbox、Skills、Memory、SubAgent、Middleware、Rubric 和人工审批组合成一个可复核的企业级数据分析智能体。