深色模式
教学版项目 · 总览:我们复刻什么、简化什么
前 8 章 + 8 个 Demo 让你"用懂"了 DeepSeek Harness。从这一篇开始,我们反过来走一遍:从零亲手实现一个 mini-harness。代码在
final-project/,每一步都对应前面的一个机制。
为什么要亲手再写一遍
"读过"和"会写"之间隔着一条河。当你写自己的实现时,会被迫回答那些阅读时被忽略的问题:
- 事件总线怎么处理"监听器抛错"?
- 插件卸载时,它注册的监听器和定时器怎么一个不漏地拆掉?
- 工具执行失败时,错误怎么回到模型那边?
- turn 被用户取消时,日志怎么保证闭环?
这些问题,dsh 用 2700 行 Cordis + 1000 行 session 回答了。我们用约 900 行核心代码回答一个诚实简化版——保留骨架思想,砍掉生产级复杂度。
复刻清单(什么必须留)
| 机制 | 对应 dsh | 在 mini-harness 里 |
|---|---|---|
| 一切皆插件 | Cordis | 插件三形态 + inject 依赖等待 |
| 可逆副作用 | ctx.effect / fiber | ctx.effect() 记录 disposer,卸载逆序执行 |
| 服务容器 | ctx.<key> 服务解析 | ctx.provide/get,每个服务唯一提供者 |
| 四种分发模式 | emit/parallel/serial/waterfall | 同语义,实现约 60 行 |
| 类型化事件 | declare module 声明合并 | 同款写法(@mini/harness/events) |
| LLM 接缝 | LlmRuntime + LlmAdapter | 同结构 + llm/stream 瀑布 |
| 统一词汇表 | StreamChunk 协议义务 | 同协议:usage 先于 finish、block-end 收尾 |
| Agent 循环 | turn/step + 事件瀑布 | agent/pre-step、agent/request、agent/turn-stopping |
| 会话日志 | append-only + deriveMessages | 同思想 + fork |
| 工具管线 | pre/execute/post 瀑布 | 同四道闸门(ask 降级为 deny) |
| 组合 | profile/bundle/patch | 简化为 bootstrap() 函数式组装(见第 5 步) |
简化清单(什么先砍掉)
| dsh 有而我们没有 | 为什么可以砍 |
|---|---|
| Context 的 Proxy 代理 | 教学版直接对象访问,语义不变 |
| isolate/intercept 作用域 | 服务名全局唯一即可,作用域是进阶主题 |
| fiber 状态机与 HMR | 不需要热重载,插件生命周期只有 start/dispose |
| 配置 schema 校验(schemastery) | 类型系统 + 手工检查够用 |
| 持久化(JSONL/SQLite) | 日志留在内存;fork 保留 API 演示血缘 |
| 沙箱 / 审批面 / 遥测 | 属于产品层而非框架内核 |
| subagent / workflow / goals | 高阶编排,超出本教程范围 |
每砍一项,我们都在正文里说清楚"真实 dsh 为什么需要它"——简化是为了教学,不是为了偷懒。
端到端效果
跑起来长这样(npm run demo 的节选输出):
text
[mock-adapter] provider "mock" 已注册
[echo-tool] echo 工具已注册
== 任务 2:触发工具循环 ==
[observer] 日志事件:turn/start
[observer] 日志事件:step/start
[observer] 第 2 次模型调用:mock/mock-1
[observer] 日志事件:tool/call
[observer] 日志事件:tool/result
[observer] 日志事件:step/end
[observer] 日志事件:step/start ← 工具循环后自动开第二个 step
[observer] 第 3 次模型调用:mock/mock-1
[observer] 日志事件:step/end
[observer] 日志事件:turn/end以及浏览器界面(React + Node 后端):
六步路线
| 步骤 | 内容 | 对应文件 |
|---|---|---|
| 第一步 | 迷你 Cordis:Context、事件总线、插件与 effect | src/context.ts src/events.ts src/plugin.ts src/service.ts |
| 第二步 | LLM 接缝:词汇表、适配器注册表、两个适配器 | src/llm/* |
| 第三步 | 会话日志与 Agent 循环 | src/agent/session.ts src/agent/agent.ts |
| 第四步 | 工具注册表与四道闸门 | src/agent/tools.ts |
| 第五步 | Node 后端:bootstrap 组装 + SSE | apps/server/* |
| 第六步 | React 前端:聊天与事件面板 | apps/web/* |
先跑起来再读代码:
sh
cd final-project
npm install
npm run demo # 核心库示例(第 1–4 步的合体)
npm run dev:server # 后端(第 5 步)
npm run dev:web # 前端(第 6 步)