ZCode 与 DeepSeek Harness:工作台、运行时与 Agent 工程
想做一个自己的 Agent 产品时,读成熟的开源项目是很好的学习方式。但读源码也很容易“误读”:把 ACK 当成任务完成,把会话恢复当成操作可以重放,把“只读工具”当成“整个 Agent 只读”。这些误读一旦被带进自己的产品,就会变成真实的 bug 和安全问题。
这篇对比两个开源项目:
- 研究一个完整的编程工作台怎么搭,重点看 ZCode;
- 研究 Agent 运行能力怎么组合和替换,重点看 DeepSeek Harness。
两者都包含模型调用、工具、会话和用户界面,不能简单理解成“一个只有界面、一个只有循环”。
读完这篇你会
- 说清两个项目各自的代码组织方式和阅读重点
- 理解“状态归谁所有”“写意图与读视图分离”“实时事件与已提交事实”这些设计
- 避开读源码时最容易出现的几种误读
- 知道开发业务 Agent 时分别可以借鉴什么
核对范围
本文依据官方仓库做静态分析,核对日期为 2026-10-23:
两个版本号属于各自的分发体系,不能据此比较成熟度。本次没有安装、运行这两个项目,也没有调用模型;文中没有速度、成本或任务成功率的实测排名。开发预览、发行功能和平台支持范围,以各自的快照和后续官方说明为准。
1. 原理:整车和可换零件的动力平台
打个比方:
- ZCode 像一辆整车:桌面端、Web 端、服务端、共享界面、Agent 命令行和运行时都在同一个仓库里。读它时,可以沿着“用户的一次操作怎样变成任务,结果又怎样回到界面”追踪完整的产品链路。ZCode 仓库说明
- Harness 像一个可以更换零件的动力平台:用 Cordis 把模型适配、工具注册、会话日志和默认的 Agent 循环组织成插件,再通过 profile 和 bundle 组合出 Web、headless、SDK、ACP 等不同入口。读它时,重点追踪“一项能力由谁定义、由谁提供、被哪些工具使用”。Harness 架构说明
| 比较角度 | ZCode | DeepSeek Harness |
|---|---|---|
| 源码主线 | 工作台、宿主服务和 Agent 协议的协作 | 插件服务、事件和运行组合 |
| 主要入口 | 桌面、Web、TUI 和非交互 CLI | Web、headless、SDK、ACP 等 profile |
| 会话展示 | CLI 的权威投影,经协议送到客户端 | 会话事件日志及其投影 |
| 扩展切入点 | 技能、命令、Hooks、MCP、UI Plugin | Cordis 插件、服务提供方、工具和事件 |
| 读源码时首先问 | 状态归谁?跨进程怎样恢复? | 能力怎样组合、替换和卸载? |
这张表说的是阅读重点,不是互斥的功能清单:ZCode 的 Agent 运行时同样开源;Harness 也有自己的 Web 应用和客户端扩展。
2. 沿一次任务,看 ZCode 怎么分层
以桌面工作台为例,主链路可以简化为:
text
用户输入与界面草稿
→ UI 服务接口
→ 窗口作用域的 Host Process
→ 工作区对应的 zcode app-server --stdio
→ Agent、模型与工具执行
→ CLI 会话投影
→ snapshot / delta
→ 界面呈现界面不负责启动和管理 Agent 子进程,也不直接解析 stdio 数据帧。桌面 Host 负责协议连接和进程管理;一个窗口可以承载多个工作区,每个工作区对应自己的聊天 CLI 进程;远端工作区通过相应的 Remote Host 连接执行环境。当前集成说明
这种拆分最值得学习的是明确了每种状态归谁所有:
| 状态 | 归属 |
|---|---|
| 会话正文、阶段、队列、待处理的交互、用量 | CLI 的权威投影 |
| 草稿、滚动位置、焦点、面板展开 | 客户端 |
| 置顶、归档、分组等列表组织状态 | 宿主索引 |
如果把这些全塞进一个前端 store,断线恢复和多端同步时就越来越难判断“哪个才是真的”。状态所有权
对业务产品的启发:界面可以乐观地显示“正在发送”,但不能自己宣布“模型已启动”“工具已完成”“数据已保存”。最终状态要由执行方确认后再回到界面。
3. ZCode 怎样处理命令和会话恢复
ZCode 当前的 V4 协议把写意图和读视图分开:
- 客户端发送 command(写意图),执行侧裁决后返回 ACK;
- 会话内容通过 topic 的 snapshot(快照)和 delta(增量)同步(读视图);
- 命令带有稳定的
commandId,部分操作还要求版本基线,重试时复用原来的标识; - 客户端按序号连续应用增量,发现缺口时重新订阅,获取一个可以收敛的完整视图。
这里有两个非常容易误读的地方:
- ACK 不等于任务完成。
accepted只表示命令被接受了;最终展示以权威会话投影为准,不能一收到 ACK 就显示“文件修改成功”。 - 恢复记录不等于恢复执行。协议文档明确区分了“输入登记”和“持久执行队列”:CLI 重启后,尚未开始执行的已接收输入会变为丢弃状态,由用户确认是否重新发送。会话恢复也不会让外部服务或工作区自动回到历史时刻。协议队列语义、CLI 会话说明
同样,Agent 协议层面的命令去重,不能代替业务层面的事务。工具向外部服务创建文档、提交订单、发布内容时,还需要目标业务自己提供幂等和结果查询能力。
4. Harness 怎样把同一个闭环做成可替换的能力
text
profile / bundle 决定插件组合
→ 模型适配、工具、会话和执行策略就绪
→ Agent 组装上下文并调用模型
→ 工具请求进入校验与策略管线
→ 执行工具,记录结果
→ 继续模型请求或结束任务Cordis 提供依赖注入、类型化事件和作用域生命周期。工具、模型适配器、会话持久化都可以单独扩展,注册随插件作用域一起释放。需要替换文件访问方式、模型提供方或工具策略的产品,这种边界设计很值得研究。插件与能力边界
它还区分了执行中的实时事件和已经提交的会话事实:流式片段用于界面即时显示,完整的消息或失败的尝试在结算后才写入会话日志,再由日志投影出模型的历史。进程在结算前突然中断时,不保证每个临时片段都已经持久化。会话日志与实时流
值得借鉴的是“执行事实先有明确归属,再派生出界面和模型上下文”。但不能进一步推断为“日志恢复后,所有工具动作都能自动重做”。
5. 扩展工具和扩展界面,是两项不同的工作
两个项目都支持“插件”,但这个词覆盖的层次不一样。
ZCode:普通插件可以分发技能、自定义命令、Hook 和 MCP 服务;UI Plugin 再增加可交互的页面,页面通过 MCP Apps 与宿主通信,业务数据由插件服务管理。当前官方说明把交互面板的范围限定在 Desktop 本地工作区;Web、手机和远端工作区只保留普通的工具记录。不能因为插件名相同,就认为交互界面在所有地方都可用。插件指南、UI Plugin 能力与平台边界
Harness:工具定义把“模型传入的参数”“规范的返回值”和“给模型看的呈现”分开,并提供执行前策略、最终拒绝 guard 和执行包装等扩展点。宿主工具和客户端展示各有自己的契约,给工具加一个返回值,不等于已经接入了业务页面。工具执行管线
比如开发“检索笔记”能力时:鉴权、范围和结果结构先放在业务服务里,再包装成工具;检索进度、引用卡片、预览对比和保存按钮,要在实际的客户端里实现。模型工具、页面接口、业务保存接口,各有各的调用方。
6. 权限和数据边界:按实际入口判断
最需要比较的不是“弹几次确认框”,而是谁执行、谁授权、数据发给谁。
ZCode:权限默认值随入口变化。当前非交互的 --prompt 在没有显式指定模式时采用 yolo,常规工具操作可能不会提示;交互工具、独立的 Plan 状态和 Hook 等各有自己的规则。plan 也不是操作系统级别的只读沙箱。CLI 权限说明、非交互默认模式源码
Harness:权限取决于完整的插件组合、执行策略和底层提供方。自己新增一个只读工具,不代表 profile 中原有的文件修改和命令执行能力也被关闭。官方明确把当前版本标为开发预览,不能仅依赖它的沙箱或审批机制处理不可信的任务。Harness 安全说明
| 需要核对的边界 | 对开发者的实际影响 |
|---|---|
| 执行位置 | 本地窗口、Web 服务、SSH 主机、容器可能对应不同的账号和文件权限 |
| 模型请求 | 界面在本地运行、会话保存在本地,不代表模型请求只在本地处理 |
| 插件与 Hook | 安装、连接、启动、执行工具是不同阶段,审批范围不能混为一谈 |
| 会话和诊断日志 | 可能包含代码、提示词和工具结果,需要明确访问权限和保留期限 |
ZCode 的 NOTICE.md 具体说明了官方模型网关转发、本地日志、远端环境中配置和凭据的同步等行为,说明数据最终流向哪里,要沿着实际的请求链去判断。其开源的 Computer Use 包是不可用的占位实现,不能把官方产品演示的能力当作开源构建已经提供的功能。ZCode 执行、数据与发行边界
许可方面,ZCode 第一方代码采用 Apache-2.0,Harness 采用 MIT;依赖、原生组件和资源仍需分别核对对应的声明。ZCode 许可说明、Harness LICENSE
7. 读源码时最容易误读的几件事
| 容易误读成 | 实际上 | 带进自己产品的后果 |
|---|---|---|
| 收到 ACK = 任务完成 | ACK 只表示命令被接受 | 界面显示“已完成”,实际还在执行或已经失败 |
| 会话能恢复 = 操作能重放 | 未开始执行的输入会被丢弃,外部世界不会回到过去 | 重启后自动重放写操作,造成重复提交 |
| 协议命令去重 = 业务写入幂等 | 协议去重只管协议层 | 外部系统里出现重复的订单或文档 |
| 新增了只读工具 = Agent 只读 | profile 里原有的写入、执行能力仍然存在 | 以为安全,实际 Agent 可以改文件、执行命令 |
plan 模式 = 只读沙箱 | 不是操作系统级别的隔离 | 在不可信任务上放松了警惕 |
| 本地界面 = 数据不出本机 | 模型请求可能经过外部网关 | 敏感代码和数据被发送到了外部 |
| 同名插件 = 处处可用 | 交互面板只在特定环境可用 | 在不支持的端上功能缺失 |
| 退出码 0 / 最后说“已完成” = 成功 | 只说明进程正常结束 | 没有核对实际产物,问题被带到下游 |
8. 开发笔记 Agent 时,可以分别借鉴什么
下面是结合上述源码边界给出的工程建议,不是两个官方项目已经为笔记系统实现的功能。
向 ZCode 借鉴客户端和运行时的分工:让服务端成为任务状态的权威,前端只维护输入、视图和待确认的动作;为任务、工具调用和用户的采纳操作分别建立标识。断线重连时获取可以恢复的状态,而不是让前端根据聊天文字猜测任务是否完成。
向 Harness 借鉴受限的能力组合:把搜索和读取拆成两个工具,由可信的任务上下文提供用户身份、知识库范围和预算,模型只填写必要的参数。需要更换模型或检索实现时,尽量保持业务契约稳定,减少客户端的跟随修改。
业务保存继续交给笔记系统:Agent 返回带来源版本的候选稿,用户预览后选择插入或另存;保存时检查目标版本、权限和格式。“来源资料过期”“目标被并发修改”“保存结果不确定”是三种不同的情况。会话恢复和工具循环都不能绕过这些业务边界。
text
选定资料范围
→ search_notes / read_note
→ 带来源的候选稿
→ 用户预览并选择采纳方式
→ 业务保存检查版本,适配层补齐操作去重
→ 确认本次提交结果采纳操作的持久去重属于需要补齐的适配能力,不能从现有会话协议推断出业务写入已经幂等。如果目标是给已有的 Vue 产品增加这个能力,可以先做受限工具和候选稿适配,再接入自己的预览和保存页面,不必一开始就复制完整的编程工作台。详细案例见 AI Agent 开发实战:检索笔记、预览采纳与保存。
9. 怎样比较实际效果
源码分析能说明架构和实现边界,但回答不了“哪一个在你的任务上更快、更省、更可靠”。需要做性能或选型结论时,用同一组脱敏任务,固定模型、工具权限、资料、版本和预算,再记录这些证据:
| 评估项 | 检查依据 |
|---|---|
| 完成质量 | 答案和来源、代码差异、业务约束和实际产物 |
| 执行成本 | 模型请求次数、工具调用次数、总耗时、实际用量 |
| 失败处理 | 无结果、工具被拒绝、取消、超时、断线恢复 |
| 副作用控制 | 越界请求是否被拒绝,重复请求是否产生重复写入 |
| 客户端一致性 | 迟到的事件、重连时的缺口、已确认的状态能否正确收敛 |
不同入口、权限模式和插件组合要分别记录。先验证固定任务的完整过程,再决定你需要的是一个完整的工作台、一个可组合的运行时,还是一条更简单的“检索 + 生成”流水线。
检查清单
- [ ] 读源码时分清“当前已有的能力”和“自己的推断”
- [ ] 自己的产品里,每种状态都有明确的所有者
- [ ] 界面只展示执行方确认过的最终状态
- [ ] 会话恢复不会自动重放写操作,业务写入有自己的幂等
- [ ] 按完整的插件组合和实际入口评估权限,而不是只看自己写的工具
- [ ] 沿实际请求链确认数据会被发送到哪里
- [ ] 用固定任务和完整证据比较方案,而不是只看最后一句“已完成”
延伸阅读
- AI Agent 开发实战:基于 Harness 的完整开发流程
- Agent 架构与工具调用:通用的循环与工具契约
- LLM 应用安全:权限与数据边界