外观
DeepSeek Harness 与 Pi 的架构拆解对比:两种 Agent Harness 哲学
摘要:从源码角度系统性对比 DeepSeek 官方的 DeepSeek Harness(dsh)与 earendil-works 的 Pi 两个开源 Agent Harness 项目。两者体量接近但架构哲学几乎处于两个极端:dsh 把"一切皆插件"做到字面意义的彻底(连 agent loop 本身都是可替换插件);Pi 走"精简 monorepo + 分层 package"路线,核心是可嵌入、可编程的 Agent 类。本文围绕会话持久化、工具执行边界、扩展点设计、安全模型四个维度拆解对比。
项目定位与基本面
| 维度 | DeepSeek Harness (dsh) | Pi |
|---|---|---|
| 仓库 | deepseek-ai/deepseek-harness | earendil-works/pi |
| Star / Fork | 187k+ / 20.8k | 95.8k / 11.9k |
| 自我定位 | "Everything is a Plugin" | "AI agent toolkit: unified LLM API, agent loop, TUI, coding agent CLI" |
| 底层框架 | Cordis(灵感来自论文《A Programming Paradigm for Spatiotemporal Composability》) | 五个独立 npm 包(pi-telemetry / pi-ai / pi-agent-core / pi-coding-agent / pi-tui) |
| 安装方式 | npx @deepseek-ai/dsh web | npm/bun/pnpm 全局安装 |
| 阶段 | developer preview(README 警告会有破坏性变更) | 稳定迭代(v0.84.1) |
关键差异:dsh 是一个大型 monorepo(12000+ commits),用 profile + bundle 组装产品形态;Pi 拆成五个可独立使用的包,pi-agent-core 可脱离 CLI 单独使用。
架构哲学:Cordis 插件树 vs 分层 package 显式契约
dsh:没有特权核心("There is no privileged core to patch")
- 扩展方式是把插件挂到插件树旁边,注册行为是可逆的 effect,卸载时自动回撤
- 组合单元是 profile 和 bundle:
- bundle = 分发格式,打包 Cordis 配置行和代码,插入内容可被上层 patch
- profile = Harness home 目录下的具名组合,列出叠加的 bundle 列表,持有
cordis.patch.yml
- 分层:
dsh-base(模型适配器/工具/持久化/沙箱审批/设置/凭证/遥测)→dsh-web-app(浏览器应用)→dsh-headless(无服务器 runner) - 任意配置可用
dsh --profile web --dump-config打印,再用 patch 替换 —— 应用装配变成可审计、可 diff、可 dry-run 的声明式过程,对企业交付友好
Pi:显式分层,Agent 是可编程对象
- 分层遵循关注点分离:
pi-ai(LLM 调用)/pi-agent-core(agent loop 和状态)/pi-tui(终端渲染)/pi-coding-agent(组装产品) - 核心可扩展性 = 把 Agent 做成纯粹可编程的类:构造函数接收函数式 hook(
streamFn、convertToLlm、transformContext、beforeToolCall/afterToolCall),而非往全局 context 挂服务 - 类型扩展用 TypeScript declaration merging 扩展
CustomAgentMessages
核心抽象:Turn / Step 与 Agent Loop
dsh 的 turn/step 状态机
- step = 一次模型请求 + 触发的工具调用;turn = 零到多个 step(第一个输入被 claim 时打开,无事"欠着"时关闭)
turn/*、step/*、user/message、assistant/*、tool/*是 durable session events(写日志、可重放);agent/pre-step、llm/stream、tools/*是 waterfall 事件(监听器必须调用next()传递,可短路/改写);agent/turn-stopping是 serial 事件(纯投票)- "model-visible means logged" 不变式:任何进入模型请求的内容必须能从 session log 重建,运行时断言校验 —— 审计留痕刚需级别的设计
Pi 的 runLoop:显式队列语义
- 五步循环:Context Transformation → LLM Streaming → Tool Execution → Steering Check → Graceful Stop
- 用
EventStream<AgentEvent>对外暴露事件(turn_start、message_update携带流式 delta 等)—— 更接近"UI 渲染友好"的事件模型 - 独有的 steering / follow-up 概念:steering 注入到"下一次 LLM 调用之前"(用户插话立即生效),follow-up 注入到"loop 本该退出之后"(自动化触发器让 loop 继续跑)
工具执行:并行策略与终止语义
- Pi:默认
"parallel"—— 工具顺序准备(校验参数、跑 beforeToolCall),被允许的工具并发执行;最终写入 transcript 的 toolResult 顺序永远与 assistant 请求的原始顺序一致(正确性保证);terminate: true需一批所有工具都要求终止才跳过后续 LLM 调用 - dsh:
tools/pre-execute → execute → post-execute三段瀑布管线;turn 收尾由agent/turn-stopping+ tool result 的concludesTurn字段控制 —— "Data decides, so listener order cannot change the outcome"(数据决定,监听器顺序不改变结果)
会话持久化与状态管理
dsh:事件溯源是唯一真相源
- Session 是 append-only 的 SessionEvent 日志;LLM 可见历史由
deriveMessages()从日志派生,不单独存 - 事件带
seq(单调递增)、time、type、data、sourceEventSeqs - 事件种类封闭为十二种(turn/step 起止、user/assistant/tool 消息、steering、todo、request/header)
SessionPersistence支持 JSONL 与 SQLite 后端;session/flush是 checkpoint,支持 fork(子 agent、历史分支的底层支撑)
Pi:JsonlSessionRepo + 分支树 + 显式压缩管线
- 会话持久化到 JSONL(
JsonlSessionRepo原子 append-only),树状结构存储,支持分支、fork、自动上下文压缩 AgentHarness(dsh 无直接对应物,概念类似 orchestration layer)把状态显式切成四类:Harness Config / Turn Snapshot / Session / Pending Session Writes(在 save point 统一 flush)- 用
Result<T, E>模式处理可预期失败(文件系统/shell 操作),比裸抛异常利于精细错误处理 - Compaction 是独立子系统(split-turn compaction),上下文裁剪问题给了工程化、模块化的答案
对企业级交付:两者都满足"崩溃恢复、会话可重放",但 dsh 的 "model-visible means logged" 运行时断言把审计完整性做成框架级强制检查 —— 监管敏感行业加分项。
事件系统与扩展点设计
dsh:三个事件域 × 三种派发模式
- 三个域:Session events(durable,写日志)/ Agent events(携带活 Agent 对象,观察或拦截进行中工作)/ Capability events(
fs/*、tools/*、telemetry/*,把策略和适配器挂到 seam) - 三种派发:waterfall(可短路改写)/ serial(纯投票)/ emit(普通广播)
- scope-filtered dispatch:事件签名带
this: Scoped<Agent>,注册在某 agent 作用域的监听器只收到该 agent 事件 —— 多租户/多子 agent 刚需 - 文档专门用表格回答"新功能挂在哪个扩展点"(16 种诉求对应明确
ctx.xxx服务)
Pi:Extension System + Hooks,运行时更轻
- jiti 动态加载 TS 扩展文件,
ExtensionAPI生命周期,可注册自定义工具、slash 命令、provider - 扩展点更贴近"给函数式 hook 传参数"(transformContext / beforeToolCall / afterToolCall / convertToLlm)
- 取舍:dsh 插件树任何层级(含 agent loop 本身)都可整体替换,代价是概念多、上手陡峭(官方建议用 agent 读代码库);Pi hook 模型大多数定制场景更直接,但替换 loop 调度逻辑的空间更小
LLM Provider 抽象层
- dsh:消息词表(Message/ContentBlock/StreamChunk)和 provider 适配器 seam 都在
packages/llm,ctx.llm是 context key;ContentBlockMap、MessageSourceMap、FinishReasonMap遵循 "…Map → derived-union" 扩展模式(declaration merging 加新变体,编译期保证 switch 覆盖所有分支) - Pi:独立发布
pi-ai包,核心是Modelsruntime(隔离的 provider 集合 + 显式 auth 解析 +CredentialStore),streamSimple是常用流式入口;处理 prompt caching、thinking/reasoning block、跨供应商能力交接、Provider.refreshModels动态发现 - Pi 内置 llama.cpp router(原生支持本地模型
/llama),dsh 无开箱即用支持但理论上可注册新ctx.llmadapter
部署形态与运行模式
- dsh:profile 驱动 ——
web(浏览器应用)/headless(一次性 runner),"重装配、轻代码" - Pi:四种代码路径 —— Interactive(TUI,差分渲染、grapheme-aware 多行编辑器、Kitty graphics)/ Print-JSON / RPC(JSON-RPC 2.0 over stdin/stdout,协议文档特别强调按
\n严格切分 JSONL,不要用 Node readline 等按 Unicode 分隔符切分的读取器)/ SDK(createAgentSessionRuntime()嵌入自己的应用)
安全与权限模型:两个项目最大分野
- dsh:沙箱与审批策略是
dsh-base标配能力(与模型适配器、工具、持久化并列);ctx.sandbox是可替换 backend,消费者 spawn 进程前用沙箱包装 argv —— 安全边界是框架一等公民 - Pi:README 明说 "Pi does not include a built-in permission system... by default it runs with the permissions of the user and process that launched it"。官方给出三种隔离方案:Gondolin extension(本体留宿主机,工具和
!命令路由进 Linux 微 VM)/ Docker 容器 / OpenShell 沙箱
责任划分哲学不同:dsh 内建安全边界(开箱即用有默认防护,代价是框架复杂度和攻击面变大);Pi 把问题推给部署方(代码路径更短、TCB 更小、更容易审计,但默认状态下"以启动进程身份不受限执行")。对接触客户生产环境的 agent 部署,显式沙箱与审批策略是第一票否决项。
工程治理与供应链
- Pi:供应链加固投入大 —— 直接依赖精确版本锁定(
.npmrc设save-exact=true和min-release-age=2防供应链投毒)、lockfile 唯一真相源 + pre-commit 拦截、CI 用npm ci --ignore-scripts、定期npm audit+audit signatures、发布带 shrinkwrap 锁定传递依赖;新贡献者 PR 默认自动关闭(防御 AI 刷 PR) - dsh:治理侧重点在架构文档 ——
docs/subsystems/core.md单份 1070 行/55.6KB,由scripts/gen-cordis-catalog.ts生成、CI 校验防漂移("文档即代码产物",适合鼓励用 agent 探索代码库的项目)
对比速览表
| 维度 | DeepSeek Harness (dsh) | Pi |
|---|---|---|
| 底层范式 | Cordis 插件树,一切皆插件 | 分层 npm package + 函数式 hook |
| 核心可扩展单元 | Service Definition / Provider / Consumer 的 capability seam | AgentOptions 里的 hook 函数 |
| 组合机制 | profile + bundle + 分层 patch | 无声明式组合层,靠代码组装 |
| 会话持久化 | append-only SessionEvent 日志,deriveMessages() 派生 | JsonlSessionRepo,树状分支 + 独立压缩子系统 |
| 类型扩展 | …Map → derived-union + declaration merging | CustomAgentMessages 声明合并 |
| 事件模型 | 三域 × 三种派发模式,scope-filtered | 单一 EventStream,UI 渲染导向 |
| 工具执行 | pre-execute→execute→post-execute 瀑布管线 | 并行(默认)/顺序,顺序保证写回 |
| 状态管理 | 无显式 harness 层,状态即日志 | AgentHarness 显式四分 + Result<T,E> |
| 安全模型 | 框架内建 ctx.sandbox、审批策略 | 无内置权限系统,要求外部容器化 |
| 部署形态 | profile 驱动(web / headless) | 代码路径驱动(interactive / print / RPC / SDK) |
| 供应链治理 | 详尽、CI 校验的架构文档 | 严格依赖锁定与审计流水线 |
| 上手曲线 | 陡峭,官方建议用 agent 读代码库 | 相对平缓,五个包职责清晰 |
两种哲学对企业级 agent 交付的启示
"一切皆插件"和"分层 package + 显式契约"不是谁更先进,而是对应两种不同的产品生命周期假设:
- dsh 的彻底插件化暗示它想成为长期演化、被大量第三方生态包围的平台 —— 连 agent loop 都能换,预期未来有人拿它做完全不同形态的产品。企业定制几乎总能通过写新 patch/bundle 满足(不用 fork 项目),但接手成本高(要搞清 profile/bundle 树)
- Pi 的克制是判断"大多数团队真正需要的定制用几个 hook 就能覆盖"—— 把复杂度让渡给外部生态,核心保持干净。对把 agent 能力嵌入已有企业系统(边界清晰、审计范围可控)更友好
一句话总结:先问客户要的是一个可以无限装配的平台(选 dsh),还是一个边界清晰、易于审计的组件(选 Pi)。选错了哲学,之后的每次客户定制都会变成与框架设计倾向的拉锯战。
参考资料
注:文中源码与架构描述均来自两个项目公开的 README 与 docs/ 架构文档,截至 2026 年 8 月。
关联词条
- Harness(智能体执行框架)(concept)
- Pi:极简、开源的终端 AI 编程 Agent(tool)
- DeepSeek Harness vs Codex Harness:发动机之争(comparison)