第 1 卷
张嘉熙 · DeepSeek Harness 前沿工程实践




你好,我是张嘉熙。
核心篇(第 05–12 讲)讲的是 dsh 会什么,编排篇讲的是怎么在 dsh 里把这些能力编排起来。这一讲先做第一步:把本讲要装的六个包组装成一个 Agent,再驱动起来。
在 dsh 里,Agent 是怎么组成的?
组装好之后,从哪一刻算一个 Agent?
一个轮次是怎么跑的?
循环怎么一轮轮跑下去?
前两问管装配,装出来的是 Agent;后两问管驱动,靠的是 Agent Loop。这一讲就按这个顺序往下走:先装 Agent,再装上 Agent Loop,让它自己跑起来。
package.json,npm install --legacy-peer-deps 装上即可。注意 peer 依赖必须显式写进 package.json,漏了会报 ERR_MODULE_NOT_FOUND,dsh-settings 正
是这种。
关于示例:npm run start:ch13 跑一次完整轮次(含一次工具调用)与两轮之间的衔接。示例用一个按脚本回答的假模型适配器,不需要任何 API Key。
Agent 的组装:六个包的装配与 Agent 构成
第一个问题:在 dsh 里,Agent 是怎么组成的?
本讲要装的零件就是下表这六个包,它们各管一段,谁也替不了谁。每个包装好以后会在 ctx 上挂一个服务(最后一列),本讲中,两种叫法按这个分工来用:说“装的是哪个包”用
包名(如 dsh-llm),说“装上来以后怎么引用”用引用方式(如 ctx.llm);这一讲省掉前缀简写成 llm、agent-loop,看语境分辨即可。两者一一对应:
| 包名 | 职责 | 引用方式 |
|---|---|---|
| dsh-llm | 把请求发给模型、把响应流成chunk | ctx.llm |
| dsh-session | 会话日志:每一步都记进只追加的日志 | ctx.sessions |
| dsh-system-prompt | 组装请求前缀(人设、提示词段落、工具表) | ctx.systemPrompt |
| dsh-tools | 工具注册表与分发流水线 | ctx.tools |
| dsh-agent | Agent接口+注册表+ agent/*事件词汇 | ctx.agents |
| dsh-agent-loop | 驱动器:唯一含循环逻辑的包 | ctx.agentLoop |
最后两行要合起来看:dsh-agent 提供 Agent 的接口、注册表与事件词汇,不含循环逻辑;跑循环的是 dsh-agent-loop,它是 dsh 里唯一含循环逻辑的包。两者是单向依赖,
组装一个运行环境,代码就是这一串:ctx.plugin() 一次装一个插件(插件由对应的包导出),注释标注的是它挂上来的引用方式:
复制代码 1const ctx = new Context()
| 2 | awaitctx.plugin(LlmRuntime) | // ctx.llm |
|---|---|---|
| 3 | awaitctx.plugin(SessionStore) | // ctx.sessions |
| 4 | awaitctx.plugin(SystemPrompt, {}) | // ctx.systemPrompt |
| 5 | awaitctx.plugin(ToolRuntime) | // ctx.tools |
| 6 | awaitctx.plugin(AgentRegistry) | // ctx.agents |
| 7 | awaitctx.plugin(AgentLoop, {agents: [] }) | // ctx.agentLoop |
这个顺序只是自然的写法,不强制。inject 声明的依赖由框架持续跟踪,服务没就绪时插件会先挂起、等它上线才激活(第 2 讲的响应式依赖),所以调换次序也能跑通。
六个插件装好,这台机器就就绪了;往注册表(ctx.agents)里登记一个 agent,它就能跑起来。先看这个 agent 身上有什么:核心的五样(其中 agent.status 和 agent.inbox
后面讲循环时会再碰到;另有 agent.options 记它用哪个 provider/model,本讲不涉及):