比LangChain更优雅?DeepSeek Harness的插件化Agent设计)
四比LangChain更优雅DeepSeek Harness的插件化Agent设计项目地址https://github.com/deepseek-ai/deepseek-harness⭐ 88,000 Stars | MIT License | 语言TypeScript开篇当LangChain还在链式调用时Harness已经插件化了如果你用过LangChain一定熟悉这样的代码fromlangchainimportOpenAI,LLMChain,PromptTemplatefromlangchain.chainsimportSimpleSequentialChain# 定义链llmOpenAI(temperature0.7)templatePromptTemplate(...)chainLLMChain(llmllm,prompttemplate)# 串起来overall_chainSimpleSequentialChain(chains[chain1,chain2,chain3])resultoverall_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消费服务constllmctx.inject(llm)对比LangChainLangChain组件直接实例化依赖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()完全清理无内存泄漏可逆性卸载后状态回到加载前对比LangChainLangChain链一旦构建无法动态增删组件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.temperature0.5// 继续传播或调用next()returnnext(request)})对比LangChainLangChain通过回调函数callbacks实现拦截但只能观察不能修改Harnesswaterfall事件允许完全拦截和修改数据流三、实战对比实现一个日志记录功能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都要手动传入chainLLMChain(llmllm,promptprompt,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的三角色Service Definition接口定义interface IShellProvider实现者LocalShellProvider、E2BCloudProviderConsumer使用者BashTool、FileEditor等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}fromcordisexportdefaultclassHelloPluginextendsService{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篇涵盖架构、源码、实战全流程。