(四)比LangChain更优雅?DeepSeek Harness的插件化Agent设计
2026/8/23 23:14:19 网站建设 项目流程

(四)比LangChain更优雅?DeepSeek Harness的插件化Agent设计

项目地址:https://github.com/deepseek-ai/deepseek-harness
⭐ 88,000+ Stars | MIT License | 语言:TypeScript


开篇:当LangChain还在"链式调用"时,Harness已经"插件化"了

如果你用过LangChain,一定熟悉这样的代码:

fromlangchainimportOpenAI,LLMChain,PromptTemplatefromlangchain.chainsimportSimpleSequentialChain# 定义链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',newDeepSeekAdapter())// 插件B消费服务constllm=ctx.inject('llm')

对比LangChain

  • LangChain:组件直接实例化依赖,new Chain(dependency)
  • Harness:依赖通过Context注入,运行时动态绑定

2.2 Lifecycle(生命周期):插件的"出生到死亡"

Harness的每个插件都有完整的生命周期:

exportdefaultclassMyPluginextendsService{asyncsetup(){// 初始化:注册事件监听、分配资源this.ctx.on('turn/start',this.handleTurnStart)}asyncstart(){// 启动:开始处理业务}asyncstop(){// 停止:清理资源this.ctx.off('turn/start',this.handleTurnStart)}asyncdispose(){// 销毁:完全释放}}

关键能力

  • 热加载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())returnnext(request)})

对比LangChain

  • LangChain:通过回调函数(callbacks)实现拦截,但只能观察不能修改
  • Harness:waterfall事件允许完全拦截和修改数据流

三、实战对比:实现一个"日志记录"功能

3.1 LangChain方式

fromlangchain.callbacksimportBaseCallbackHandlerclassLoggingHandler(BaseCallbackHandler):defon_llm_start(self,serialized,prompts,**kwargs):print(f"[LOG] LLM调用开始:{prompts}")defon_llm_end(self,response,**kwargs):print(f"[LOG] LLM调用结束:{response}")# 使用:每个Chain都要手动传入chain=LLMChain(llm=llm,prompt=prompt,callbacks=[LoggingHandler()]# ← 每个链都要加)

问题

  • 每个Chain都要手动注入callback
  • 无法修改数据,只能观察
  • 回调顺序不可控

3.2 Harness方式

exportdefaultclassLoggingPluginextendsService{setup(){// 拦截LLM请求事件this.ctx.on('llm/request',async(req,next)=>{console.log('[LOG] LLM请求:',req.messages)returnnext(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',newLocalShellProvider())

现在你想把Agent部署到云端,但又不想改代码。Harness的做法:

// 换成云端执行,其他代码完全不变ctx.provide('shell',newE2BCloudProvider())

Seam的三角色

  1. Service Definition:接口定义(interface IShell
  2. Provider:实现者(LocalShellProviderE2BCloudProvider
  3. Consumer:使用者(BashToolFileEditor等)

4.2 对比LangChain的"工具替换"

LangChain中替换工具:

# 原来tools=[ShellTool(),FileTool()]# 改成云端:需要重写每个ToolclassCloudShellTool(BaseTool):def_run(self,command):returncall_e2b_api(command)# 每个工具都要改tools=[CloudShellTool(),CloudFileTool()]

Harness中替换:

// 只需要换Provider,所有Consumer自动切换ctx.provide('shell',newE2BProvider())// 一行代码

这就是"优雅"的差距


五、性能对比:谁更快?

指标LangChainHarness
启动时间快(直接实例化)稍慢(需初始化插件系统)
运行时扩展需重启热插拔
内存占用低(无运行时开销)稍高(事件总线)
复杂场景性能链式调用栈深事件驱动扁平化

结论

  • 简单场景:LangChain更快
  • 复杂/动态场景:Harness架构更优

六、什么时候选LangChain?什么时候选Harness?

选LangChain,如果你:

  • 快速原型验证
  • 团队熟悉Python
  • 需求相对固定
  • 不想引入运行时复杂度

选Harness,如果你:

  • 需要高度可扩展
  • 需求经常变化
  • 想构建"Agent平台"而非单个Agent
  • 需要热加载/热卸载能力
  • 重视代码可维护性

七、Harness插件开发实战

7.1 最小插件示例

// plugins/hello.tsimport{Service,Context}from'cordis'exportdefaultclassHelloPluginextendsService{constructor(ctx:Context){super(ctx,'hello')}asyncsetup(){// 注册一个命令this.ctx.command('hello').action(()=>'Hello from plugin!')}}

7.2 插件配置

# cordis.patch.ymlplugins: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篇,涵盖架构、源码、实战全流程。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询