(四)比LangChain更优雅?DeepSeek Harness的插件化Agent设计
(四)比LangChain更优雅?DeepSeek Harness的插件化Agent设计
项目地址:https://github.com/deepseek-ai/deepseek-harness
⭐ 88,000+ Stars | MIT License | 语言:TypeScript
开篇:当LangChain还在"链式调用"时,Harness已经"插件化"了
如果你用过LangChain,一定熟悉这样的代码:
from langchain import OpenAI, LLMChain, PromptTemplate
from langchain.chains import SimpleSequentialChain
# 定义链
llm = OpenAI(temperature=0.7)
template = PromptTemplate(...)
chain = LLMChain(llm=llm, prompt=template)
# 串起来
overall_chain = SimpleSequentialChain(chains=[chain1, chain2, chain3])
result = overall_chain.run(input)
链式调用——这是LangChain的核心抽象。但问题是:一旦业务复杂起来,链会变成一团乱麻。你想替换其中一个环节?可能要重写整个链。
DeepSeek Harness的做法完全不同:没有链,只有插件。
一、两种设计哲学的根本差异
1.1 LangChain:链式组合
Input → [Prompt] → [LLM] → [OutputParser] → [Tool] → Output
↑___________链式调用_____________↑
特点:
- 组件通过"链"硬编码组合
- 数据流向固定
- 扩展需要继承/重写Chain类
1.2 Harness:插件化事件驱动
Event: turn/start
↓
Plugin A (拦截/修改)
↓
Plugin B (处理)
↓
Event: turn/end
特点:
- 组件通过"事件"松耦合
- 数据流由事件驱动,动态可配
- 扩展只需注册新插件
二、Harness插件系统的三大核心机制
2.1 Context(上下文):插件的"共享内存"
Harness中,插件不直接调用彼此,而是通过Context交换数据:
// 插件A提供服务
ctx.provide('llm', new DeepSeekAdapter())
// 插件B消费服务
const llm = ctx.inject('llm')
对比LangChain:
- LangChain:组件直接实例化依赖,
new Chain(dependency) - Harness:依赖通过Context注入,运行时动态绑定
2.2 Lifecycle(生命周期):插件的"出生到死亡"
Harness的每个插件都有完整的生命周期:
export default class MyPlugin extends Service {
async setup() {
// 初始化:注册事件监听、分配资源
this.ctx.on('turn/start', this.handleTurnStart)
}
async start() {
// 启动:开始处理业务
}
async stop() {
// 停止:清理资源
this.ctx.off('turn/start', this.handleTurnStart)
}
async dispose() {
// 销毁:完全释放
}
}
关键能力:
- 热加载:
scope.plugin(MyPlugin)立即生效 - 热卸载:
scope.dispose()完全清理,无内存泄漏 - 可逆性:卸载后状态回到加载前
对比LangChain:
- LangChain:链一旦构建,无法动态增删组件
- Harness:运行时随时插拔插件
2.3 Event(事件):插件的"通信协议"
Harness使用事件总线实现插件间通信:
// 发布事件
ctx.emit('agent/message', { role: 'user', content: 'Hello' })
// 订阅事件
ctx.on('agent/message', async (msg) => {
console.log('收到消息:', msg.content)
})
四种事件模式:
| 模式 | 说明 | 用途 |
|---|---|---|
emit |
广播,不等待 | 日志、通知 |
parallel |
并行执行 | 独立副作用 |
serial |
串行执行 | 有序处理 |
bail |
返回首个非空 | 策略选择 |
| waterfall | 可拦截修改 | 中间件 |
waterfall是Harness的杀手锏:
// 拦截并修改LLM请求
ctx.on('llm/request', async (request, next) => {
// 修改请求参数
request.temperature = 0.5
// 继续传播(或调用next())
return next(request)
})
对比LangChain:
- LangChain:通过回调函数(callbacks)实现拦截,但只能观察不能修改
- Harness:waterfall事件允许完全拦截和修改数据流
三、实战对比:实现一个"日志记录"功能
3.1 LangChain方式
from langchain.callbacks import BaseCallbackHandler
class LoggingHandler(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
print(f"[LOG] LLM调用开始: {prompts}")
def on_llm_end(self, response, **kwargs):
print(f"[LOG] LLM调用结束: {response}")
# 使用:每个Chain都要手动传入
chain = LLMChain(
llm=llm,
prompt=prompt,
callbacks=[LoggingHandler()] # ← 每个链都要加
)
问题:
- 每个Chain都要手动注入callback
- 无法修改数据,只能观察
- 回调顺序不可控
3.2 Harness方式
export default class LoggingPlugin extends Service {
setup() {
// 拦截LLM请求事件
this.ctx.on('llm/request', async (req, next) => {
console.log('[LOG] LLM请求:', req.messages)
return next(req) // 继续执行
})
// 拦截LLM响应事件
this.ctx.on('llm/response', async (res) => {
console.log('[LOG] LLM响应:', res.content)
})
}
}
// 使用:注册一次,全局生效
ctx.plugin(LoggingPlugin)
优势:
- 注册一次,所有Agent生效
- 可以修改请求/响应
- 通过事件优先级控制顺序
四、Harness插件的"超能力":Seam交换
这是Harness独有的设计——能力边界(Capability Seam)。
4.1 什么是Seam?
想象你在开发一个Agent,需要执行Shell命令:
// 默认:本地执行
ctx.provide('shell', new LocalShellProvider())
现在你想把Agent部署到云端,但又不想改代码。Harness的做法:
// 换成云端执行,其他代码完全不变
ctx.provide('shell', new E2BCloudProvider())
Seam的三角色:
- Service Definition:接口定义(
interface IShell) - Provider:实现者(
LocalShellProvider、E2BCloudProvider) - Consumer:使用者(
BashTool、FileEditor等)
4.2 对比LangChain的"工具替换"
LangChain中替换工具:
# 原来
tools = [ShellTool(), FileTool()]
# 改成云端:需要重写每个Tool
class CloudShellTool(BaseTool):
def _run(self, command):
return call_e2b_api(command) # 每个工具都要改
tools = [CloudShellTool(), CloudFileTool()]
Harness中替换:
// 只需要换Provider,所有Consumer自动切换
ctx.provide('shell', new E2BProvider()) // 一行代码
这就是"优雅"的差距。
五、性能对比:谁更快?
| 指标 | LangChain | Harness |
|---|---|---|
| 启动时间 | 快(直接实例化) | 稍慢(需初始化插件系统) |
| 运行时扩展 | 需重启 | 热插拔 |
| 内存占用 | 低(无运行时开销) | 稍高(事件总线) |
| 复杂场景性能 | 链式调用栈深 | 事件驱动扁平化 |
结论:
- 简单场景:LangChain更快
- 复杂/动态场景:Harness架构更优
六、什么时候选LangChain?什么时候选Harness?
选LangChain,如果你:
- 快速原型验证
- 团队熟悉Python
- 需求相对固定
- 不想引入运行时复杂度
选Harness,如果你:
- 需要高度可扩展
- 需求经常变化
- 想构建"Agent平台"而非单个Agent
- 需要热加载/热卸载能力
- 重视代码可维护性
七、Harness插件开发实战
7.1 最小插件示例
// plugins/hello.ts
import { Service, Context } from 'cordis'
export default class HelloPlugin extends Service {
constructor(ctx: Context) {
super(ctx, 'hello')
}
async setup() {
// 注册一个命令
this.ctx.command('hello')
.action(() => 'Hello from plugin!')
}
}
7.2 插件配置
# cordis.patch.yml
plugins:
hello:
# 插件配置
greeting: 'Hi there!'
7.3 加载插件
npx @deepseek-ai/dsh web --patch ./cordis.patch.yml
结语:插件化是Agent框架的未来吗?
LangChain的链式调用是命令式编程——你告诉计算机"先做这个,再做那个"。
Harness的插件化事件驱动是声明式编程——你定义"当这个发生时,做那个",具体怎么流转,由框架决定。
在简单场景下,命令式更直观。但在复杂场景下,声明式的可组合性、可扩展性、可维护性优势明显。
DeepSeek Harness用88K Stars证明:插件化不是过度设计,而是Agent框架的进化方向。
下一篇预告:
(五)DeepSeek Harness来了!一个让你自己「拼」出AI Agent的开源神器
我们将进入实战环节,手把手教你用Harness搭建第一个Agent。
本文是「DeepSeek Harness源码分析」系列第4篇,系列共100篇,涵盖架构、源码、实战全流程。
更多推荐

所有评论(0)