# Conflicts: # docs/architecture.md # docs/config-catalog.md # docs/event-producer-consumer.md # docs/module-graph.md # examples/acp-agent/tests/snapshots/advanced-toolchain/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/bash-spill/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/both-mode-turn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/cancel-tool-calls/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/cancel/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/code-mode-turn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/code-mode-workspace-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/error-finish/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-edit/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-policy-reject/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-read-window/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-read/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-terminal-card/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-write-overwrite/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/fs-write/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-posttool-block/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-posttool-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-cc-stop-continue/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-codex-posttool-block/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-codex-posttool-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-codex-pretool-block/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/hook-codex-stop-continue/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/model-switching/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/multi-turn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/parallel-tool-calls/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/repeat-tool-guard/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/skill-load/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/subagent-depth-two-rejection/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/subagent-fork/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/subagent-mixed/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/subagent-multi/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/subagent-spawn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/text-turn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/todo-plan/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/tool-call-turn/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/workflow-run/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/workspace-context/stdout.expected.jsonl # examples/acp-agent/tests/snapshots/workspace-edit/stdout.expected.jsonl # examples/headless-agent/tests/headless.snapshot.ts # packages/examples/README.md # packages/examples/agent-spine-demo/README.md # packages/examples/agent-spine-demo/package.json # packages/ui/README.md # packages/ui/acp/README.md
16 KiB
DeepSeek Harness 架构
English | 中文
DeepSeek Harness SDK 使用 Cordis:一切皆插件,循环也不例外。
概览
每个 harness 都是一个 Cordis 上下文。各包(package)会贡献服务(ctx.llm、ctx.tools、ctx.sessions、ctx.sessionTitle)、类型化事件(agent/request、tools/pre-execute、session/event),以及可释放的提示词、工具、提供方、适配器和监听器。
packages/core/ 汇集默认的 agent(智能体)流程;其他功能均为一等的 Cordis 插件。
默认服务
| ctx 键 | 包 | 职责 |
|---|---|---|
| — | dsh-scope |
作用域上下文注册原语(库) |
ctx.sessions |
dsh-session |
内存中的事件溯源会话 |
ctx.systemPrompt |
dsh-system-prompt |
有序提示词片段、工具 schema 和提示词变量 |
ctx.tools |
dsh-tools |
工具注册表和执行流水线 |
ctx.agents |
dsh-agent |
活跃 agent、委托创建、agent/* 事件和进程内发起方作用域 |
ctx.agentLoop |
dsh-agent-loop |
实体 Agent 驱动器 |
功能服务
| ctx 键 | 包族 | 职责 |
|---|---|---|
ctx.llm |
llm/ |
适配器注册表和模型流式调用 |
ctx.tokenMeter |
llm/token-meter |
感知回放的单实例请求压力和会话表面压力 |
ctx.bash |
bash/ |
前台和后台命令执行 |
ctx.sandbox |
sandbox/ |
同一执行环境内的进程限制(argv 包装、逐调用策略) |
ctx.sandboxPolicy |
sandbox/ |
共享沙箱策略归属点 |
ctx.codeRuntime |
code-runtime/ |
执行模型编写的程序 |
ctx.fs |
fs/ |
文件系统提供方原语和策略事件 |
ctx.skills |
skill/ |
skill(技能)提供方注册表和渐进式披露 |
ctx.web |
web/ |
搜索与抓取提供方注册表 |
ctx.compact,ctx.toolResultPrune |
compact//compact-tool-result-prune |
摘要压缩(compaction);可选的无模型结果裁剪 |
ctx.subagents |
subagent/ |
具名委托提供方 |
ctx.tasks |
tasks/ |
后台任务注册表和通用 task_* 控制工具 |
ctx.workflows |
workflow/ |
脚本驱动的多 agent 编排 |
ctx.goals |
goal/ |
持久化的同会话目标 |
ctx.sessionPersistence |
session-persistence/ |
会话日志的持久存储 |
ctx.sessionQuery |
session-query/ |
实时优先的逻辑语料精确读取和关系追踪 |
ctx.sessionTitle |
session-title/ |
基于日志的回退标题和单个可选异步提供方 |
事件
事件构成服务的扩展 API;完整清单见事件目录和生产方与消费方映射。
事件域
- 会话事件是追加到日志并通过
session/event发出的持久事实。 - Agent 事件携带活跃
Agent,用于状态、提示词准入、请求塑形、验证和续跑。 - 功能事件让所属服务边界无需导入循环即可附加策略和适配器。
拦截语义
waterfall(瀑布式事件)的行为类似环绕中间件:监听器调用 next() 即表示委托,直接返回而不调用它则会否决或接管。完整规则见 Cordis waterfall 语义。
默认循环生命周期
已交付的循环通过插件可见的服务和事件,持续处理从提示词到检查点的工作。
会话是仅追加日志。每个普通轮次领取一项已排队的 send() 输入;注入不领取输入。后续轮次会等待上一项已领取轮次的检查点,但可以与其共用同一个 running 区间(决策)。模型和插件停止轮次时,该轮次结束。一个步骤包含一次模型请求及其工具。下文(时序配套文档)用引号标记持久事件。
未提供 id 时会生成 <config-id>-session-<uuid>;sessionId 用于恢复或创建会话,而 resumeSessionId 要求已有历史。恢复流程在发布前还原沿袭关系和委托深度。设置失败会发出 agent-loop/config-start-failed;拆卸过程保持静默。
轮次流程
choose declarative identity and fresh/resume path
-> prepare private session + agent.ctx -> await unpublished setup
-> enter session + agent -> session/created -> agent/created
-> enable driving -> agent/session-start(source) -> start driver
forever:
wait for a queued message
emit agent/status(running)
TURN:
'turn/start'
claimed message -> agent/prompt-submit
allowed prompt -> 'user/message' plus injected context
blocked prompt -> 'prompt/blocked' -> 'turn/end'(rejected)
STEP loop:
drain steering
assemble system prompt and tool schemas
agent/session-prefix (first step)
agent/pre-step
snapshot the derived messages (the reconstruction boundary)
'step/start'
agent/request (config only) -> log request/header -> llm/stream (frozen)
on final adapter-path or terminal in-band failure:
'step/end'
agent/request-error(original error, failure facts, immutable prior failures, signal)
retry in the next numbered step or preserve the original error
otherwise:
'assistant/chunk'
agent/step-result
'assistant/message' (transformed content or empty success anchor after step-result rejection)
schedule tool calls by ctx.tools.executionMode:
exclusive -> one-call barrier
parallel -> rolling pool, <= maxParallelToolCalls in flight; reclassify before start
each start -> 'tool/call' -> ordered tools/pre-execute -> concurrent tools/execute
each model-order result -> ordered tools/post-execute -> 'tool/result'
append accepted tool-batch context after all recorded results, then steering
agent/post-step
'step/end'
agent/turn-continuation
agent/turn-stop (terminal policy)
stop unless tools or continuation policy ask for another step
'turn/end'
checkpoint persistence and notify idle/running status
每个步骤都会组装有序提示词片段、工具 schema 和变量;未知引用会使该轮次失败。dsh-system-prompt 负责身份和角色设定,循环则提供 model 和 cwd(提示词归属)。
工具执行阶段的上下文,包括异步 agent.inject() 通知和工具执行后的 additionalContexts,会在结果产生后稳定。steering(中途引导)会排空;在信号关闭前,agent/post-step 会观察持久输出、结果、上下文和 steering。余留内容进入队列。终止型 agent/turn-stop 在续跑判断和 steering 折叠后运行,在关闭和刷写期间仍具有最终决定权,并丢弃后续 steering,同时保留排队提示词。
裁剪先于摘要;溢出重试必须取得持久进展。有界的瞬态重试在 agent/request-error 上组合;取消优先(压缩、重试)。
失败边界
轮次是故障隔离边界。适配器故障会关闭步骤,并携带准确的故障事实进入 agent/request-error。重试会开启一个有编号的步骤;重试耗尽后,故障存入 turn/end。失败分片不会提交任何消息或工具。
其他故障使用 agent/error。取消优先于恢复;尚未分派的调用会得到合成的 ABORTED 结果。实际生效的 cancel() 会在清空队列或中止前发出 agent/cancel-requested;观察方不能否决该操作,空闲状态下的调用不发出任何事件。dispose(资源释放)会等待系统停稳。
每个会话事件都包围在轮次内。重新加载会保留中断的日志尾部,并用合成的 interrupted 轮次结束事件将其闭合。持久轮次关闭后的故障只通过 agent/error 报告,因为此时已没有安全的轮次内位置。每个轮次有一个 TurnEndReason;各变体由 TurnEndReasonMap 统一定义。
Agent 句柄
ctx.agents 拥有活跃 agent,并返回 AgentHandle { agent, dispose() }。插件使用 send()、steer()、inject()、cancel() 和 whenIdle()。调用方 fiber、工厂提供方和消费方句柄通过同一个需等待完成的 disposer 共同拥有拆卸过程。
Agent 作用域
每个活跃 agent 都拥有一个作用域化的 agent.ctx。注册项会遮蔽全局项,只接收发往该 agent 的分派,并随 agent 一同撤销;系统会等待异步清理。CreateAgentOptions.setup(agentCtx) 在发布前完成组合。类型化解析器从合并后的 Events 签名和 scopeTarget 推导载体检查(语义门禁)。参见 agent 作用域和 subagent 组合。AgentLoop 会传播其发起方;私有编排会派生 agent.session,其他身份则保持显式(决策)。
状态
会话日志
会话日志是真源。deriveMessages() 将会话事件投影为发送给模型的 Message[];原始 assistant/chunk 事件留在日志中,以保证回放和 UI 保真。回放、fork、恢复、transcript(文本记录)渲染、遥测和持久化均派生自同一个事件流。
模型可见 ⟺ 已记录:日志可以重建每个请求,包括由请求头会话前缀置于开头的 step/start 时消息,以及通过折叠 request/header 得到的请求头;开发期不变量会断言这一点(可重建性)。
持久性由插件负责。后端会缓冲同步的 session/event 通知;循环等待轮次结束检查点。SessionPersistence 直接存储 SessionEvent,并将元数据存入 SessionHeader;JSONL 默认采用带校验和的 Zstandard,SQLite 则遵循同一契约。
插件所属的纯日志事件可选择使用 ctx.sessions.appendOutOfBand():事件会加入开放轮次,或获得一个平衡且已刷写的零步骤轮次。session/title 采用该路径并以后写覆盖方式折叠,同时记录源消息 seq 和来源信息。其首消息回退标题会立即产生;至多一个可选提供方可以异步替换该标题,而不会延迟 agent 响应。fork 会原样继承已记录的标题(决策)。
模型内容
消息包含类型化块(text、reasoning、tool-call、tool-result),这些块从可合并扩展的 ContentBlockMap 派生;MessageSource、FinishReason、TurnTrigger 和 TurnEndReason 也采用同一模式定义类型。新增块类型会将适配器、UI 桥接、压缩计价、token 计量和持久化协调成一项全仓库契约;回放计量类型见 token-meter.md。
流式输出使用原始分片和 BlockAssembler。一次 LlmAdapter.stream() 调用代表一次提供方尝试;适配器报告事实,恢复逻辑则位于 agent/request-error。循环会记录分片及成功结果的来源信息和回放状态。远程适配器使用逐次读取空闲看门狗。只有当路由共用同一个适配器实例时,回放状态才会到达目标(契约)。
扩展与组合
功能模式
可替换功能通常拆分为接口/实现/消费方:服务和事件、后端,以及面向模型的工具和提示词。Bash 是参考实现;功能图映射了每个包族。
例外情况会合并不同层次:LLM(大语言模型)合并接口和消费方,文件系统整合策略,web 使用注册表,skill 和 subagent 使用具名提供方。会话标题将内置回退方案与单提供方注册表和共享 LLM 辅助组件配对。subagent 可以通过 spawn 创建全新实例、fork 一个已完成轮次的前缀,或使用 ACP(Agent Client Protocol)子 agent(subagent.md)。
dsh-workspace-context 在 agent/session-prefix 上组合基线,并在通过 ctx.fs 发现嵌套变更后,于 tools/post-execute 追加这些变更;其决策记录了隔离方式。dsh-paths 负责共享路径。
组合包与应用
dsh-agent-spine-demo 组合默认主干,其中包含回退标题和可选的持久化目标;模型标题提供方仍需按需启用(README)。dsh-tui-demo 负责交互式全屏终端,并默认启用目标功能和 /goal;dsh-cli-demo 运行一个持久化的无界面轮次,并保持 stdout 格式纯净;dsh-acp-demo 通过 JSON-RPC 添加 stdout 纯净的 ACP,并启用相同的目标与命令栈(ui/)。dsh-jsonrpc-agent 启动外部 cordis.yml;Python SDK 仅在没有显式配置通道时提供默认项,并通过按行分隔的 JSON-RPC 驱动 dsh-jsonrpc(Python SDK)。部署保持为轻量叶节点,使用可替换后端和可选产品工具(examples/、可运行接线、图谱)。
新行为的归属位置
新行为附加到已有文档记录的扩展点;循环发生变更时,本架构图随之更新。
| 目标 | 机制 |
|---|---|
| 添加模型提供方 | 在 ctx.llm 上注册适配器 |
| 添加面向模型的功能 | 在 ctx.tools 上注册;schema 进入提示词组装流程 |
| 添加 shell 执行 | 实现并注册 ctx.bash 后端 |
| 添加用户命令 | 在 ctx.commands 上注册;适配器无需模型轮次即可发现并分派该命令 |
| 添加后台工作 | 在 ctx.tasks 上注册;通用 task_* 工具负责收集或停止 |
| 添加文件系统访问或策略 | 实现 ctx.fs 提供方,或监听 fs/* 策略事件 |
| 限制生成的进程 | 使用 ctx.sandbox 后端;消费方在生成进程前包装 argv |
| 拦截请求、工具或轮次 | 使用相应的 agent/* 或 tools/* 事件;agent/turn-stop 是串行终止判定点 |
| 添加历史记录之外的会话稳定前缀 | 组合 agent/session-prefix;请求头会记录该前缀 |
| 添加 UI 或编辑器集成 | 驱动 ctx.agents 并从 session/event 渲染 |
| 添加持久会话状态 | 添加一个 SessionEventMap 成员,并从日志渲染和回放 |
| 添加异步会话标题生成 | 在 ctx.sessionTitle 上注册唯一提供方 |
| 管理同会话目标 | 使用 ctx.goals;通过 Agent 和 agent/* 续跑 |
| fork 活跃会话 | 使用 ctx.sessions.fork(source, boundary?, childSessionId?) |
| 将注册项限定到单个 agent | 使用该 agent 的 agent.ctx(参见 Agent 作用域) |
扩展实操手册(cookbook)提供插件骨架和功能到服务边界的映射;分步指南涵盖包、工具、LLM 适配器和 vendored 包。
快速参考
- 术语表中的领域术语
- core-data-structures/ 中的类型定义
- 事件中的准确事件与服务签名
- 服务目录
- 包索引中的包契约
- Agent Note(agent 决策记录)