仓库:
deepseek-ai/deepseek-harness(本地:~/git/deepseek-harness) 一句话:"一切皆插件"的 AI Agent 框架,用 TypeScript 把 Spring 那套模块化 + 依赖注入 + 事件驱动的思想,搬到了 AI Agent 领域。
对 Java 后端来说,最快理解方式是这套类比:
| DeepSeek Harness (DSH) | Java / Spring 类比 |
|---|---|
Cordis Context(ctx) |
ApplicationContext(服务注册表 + 事件总线) |
Plugin(apply(ctx, config)) |
@Configuration + 一个 Spring Boot Starter |
ctx.effect() / ctx.on() |
@EventListener + 生命周期自动清理 |
| Service Definition | Java interface(能力接口) |
| Service Provider | 接口的实现类(@Service) |
| Consumer | 注入并使用接口的代码 |
| Capability Seam | SPI(ServiceLoader 机制) |
ctx.tools.register() |
注册一个 Bean 到工具注册表 |
| Session log | Event Sourcing 的 append-only 事件日志 |
| Bundle / Profile | Spring Boot Starter / 启动配置组合 |
| Agent loop | 一个 while 循环(ReAct 模式) |
核心思想:整个产品——模型适配器、工具注册表、会话日志、甚至 agent 循环本身——全部都是插件,没有"特权核心"。你想改任何一部分,都是"挂一个插件"而不是"改框架源码"。
vendor/packages/
├── core/ # 产品 API 脊柱
│ ├── session/ # append-only 会话事件日志
│ ├── system-prompt/ # prompt 段 + 工具 schema 组装
│ ├── tools/ # 工具注册表 + 受保护的执行管道
│ ├── agent/ # Agent 接口 + 实时注册表
│ └── agent-loop/ # 默认驱动(实现 Agent 接口,就是那个 while 循环)
├── llm/ # LLM 能力:Service Definition + DeepSeek 适配器
├── shell/ # bash 能力:服务定义 + 本地/pwsh 提供者 + 工具 Consumer
├── fs/ # 文件系统能力 + 策略
├── web/ # web 搜索/抓取能力
├── subagent/ # 子代理能力
├── workflow/ todo/ plan/ compaction/ ... # 其他能力
└── bundle/ # 可分发的 profile 补丁层
代码在 packages/core/agent-loop/src/agent.ts,类名 ReactLoopAgent。
用伪代码还原它的逻辑,就是:
// 类比 Java 写法
void kick() {
while (turn()) {} // 一直跑,直到没活干
}
boolean turn() {
appendEvent("turn/start");
while (true) {
// ① 组装 prompt + 工具 schema,从会话日志推导历史
var assembly = systemPrompt.assemble(...);
// ② 调模型(流式)
var message = llm.stream(request); // assistant/message
// ③ 解析出工具调用
var toolCalls = message.toolCalls();
if (toolCalls.isEmpty()) {
return true; // 没有工具调用 = 这一轮结束
}
// ④ 执行工具,结果回填
executeToolCalls(toolCalls);
// ⑤ 回到 while 循环,带着工具结果再次调模型
}
appendEvent("turn/end");
}
这就是经典的 ReAct(Reason + Act)循环:调模型 → 模型说要调工具 → 执行工具 → 结果回填 → 再调模型 → 直到模型不再调工具。
关键点(Java 开发者容易忽略的): - 一个 step = 一次模型请求 + 它触发的所有工具调用;一个 turn = 0 或多个 step - 模型能看到的所有上下文,都必须来自会话日志(Session log)——"Model-visible means logged"。这相当于:模型的历史不是存在内存变量里,而是从 append-only 的事件日志里"重放/投影"出来的。这个设计让 fork、resume、回放、telemetry 全都基于同一条事件流。
apply每个插件就是一个导出的 apply(ctx, config) 函数,没有魔法。以 bash 工具为例(packages/shell/tool-bash/src/index.ts):
export function apply(ctx: Context, config: Config = {}): void {
// ① 往 prompt 里加一段说明
ctx.systemPrompt.section({ name: 'tool:bash', order: 105, text: '...' });
// ② 注册一个工具(这是核心)
ctx.tools.register(defineTool({
name: 'bash',
description: 'Execute a bash command...', // 给模型看的描述
parameters: { command: {...}, timeoutMs: {...} }, // JSON Schema
async execute(args, exec) {
const result = await ctx.shell.run(...); // 调底层 shell 能力
return { kind: 'foreground', ...result };
},
}));
}
对比 Java:apply() ≈ 一个 @Configuration 类,defineTool() ≈ 定义一个 @Bean 并注册,ctx.tools.register() ≈ 把这个 Bean 放进工具注册表(模型通过 JSON Schema 知道有哪些工具可用)。
这是 DSH 最值得学的一点,叫做 Seam(接缝),由三个角色组成:
Service Definition(接口) → Service Provider(实现) → Consumer(使用方,通常是面向模型的工具)
例子:shell 能力
- Service Definition:ctx.shell 的接口(run、start、resolve 等)
- Service Provider:本地 bash 实现 / pwsh 实现 / 远程沙箱实现
- Consumer:tool-bash 这个工具(调用 ctx.shell.run())
威力:换一个 Provider,整个产品就变了。文件系统和子进程共享同一个"执行世界",把它们指向远程沙箱,Bash、PTY、LSP 一起跟着变,不用 fork 任何 Provider。
对比 Java:这就是标准 SPI 的升级版——接口、实现、使用方严格分离,且实现可被配置动态替换(类比 @ConditionalOnMissingBean + 多种实现按 profile 切换)。
| 事件域 | 作用 | Java 类比 |
|---|---|---|
| Session events | 持久事实,写入日志(turn/start、user/message、assistant/message、tool/result) |
Event Sourcing 的事件 |
| Agent events | 进行中的工作(agent/pre-step、agent/request) |
应用内事件 |
| Capability events | 挂策略和适配器(fs/*、tools/*) |
领域事件 |
其中 agent/pre-step、agent/request、llm/stream、tools/* 是 waterfall(瀑布):监听者必须调 next() 才能放行(类比责任链/拦截器)。agent/turn-stopping 是串行的,没有 next()。
对比 Java:ctx.effect()/ctx.on() ≈ 事件监听,waterfall ≈ HandlerInterceptor 责任链。
按这个顺序看,能最快上手:
docs/architecture.md(本仓库,含 turn flow 的时序)——建立全局图docs/cordis-primer.md——理解 Cordis 的 ctx.effect() / ctx.on() / Service 机制(类比 Spring IoC 入门)packages/core/agent-loop/src/agent.ts——这是核心,就是那个 while 循环packages/shell/tool-bash/src/index.ts)——理解"怎么加一个能力"shell 包的 Definition/Provider/Consumer)——理解"怎么替换一个能力"看完这 5 个,你对"AI Agent 框架怎么设计"就有了完整的工程认知,剩下的 30 个包都是同构的重复。
本梳理基于仓库
deepseek-ai/deepseek-harness当前代码(README、AGENTS.md、docs/architecture.md、core/agent-loop/src/agent.ts、shell/tool-bash/src/index.ts 精读)。开发者预览阶段,结构仍在快速迭代。