半年前,我把一个基于 LangChain 封装好的客服工作流迁到 AutoGen 时,被“碎片化”这件事狠狠上了一课。表面上看只是换框架,实际上提示词、模型调用、工具注册、记忆状态、人机交互回调全都要重新适配,真正属于业务逻辑的部分反而占不到两成。做 Agent 项目的人应该都有同感:框架越多,沉没成本越高,迁移时真正疼的不是业务代码,而是被框架“腌制”过的胶水层。
后来我把 Docker 的交付思路搬到 Agent 框架这件事上,做了一个内部代号叫 GitAgent 的迁移封装方案,核心想法很朴素:像容器镜像一样构建 Agent,像 OCI 运行时一样接框架,把框架降级为“运行层”,让业务逻辑真正可移植。实测迁移类似工作流时,传统方案估算要 10 个人日,GitAgent 方案只花 2 个人日左右,节省 80% 不是口号,是在内部项目里测出来的数字。这篇文章我会把这套思路完整拆开,包含设计理念、目录规范、实操步骤和跨 LangChain/AutoGen 的迁移案例,也会把我踩过的坑一并说出来。适合正在选型 Agent 框架、或者已经被框架绑死想“搬家”的开发者参考。
1. 框架碎片化不是选择困难,是环境绑定问题
1.1 换框架真正贵在哪里
这两年 AI Agent 框架数量增长得很快,LangChain/LangGraph、AutoGen、CrewAI、LlamaIndex 的 Agent 模块、各种低代码平台里的 Agent 编排,还有大量自研框架,几乎每半年就会冒出新选择。很多人把这个问题理解成“选择困难”,实际上选择困难只是最表层的东西,真正严重的是 Agent 的核心资产已经被框架数据结构绑死了。
迁移过一个框架的人都会发现,业务逻辑拆出来反而容易,难的是这些东西:
- 消息类型与上下文格式。LangChain 里传入模型的 Message 对象,和 AutoGen 里以 Conversation 为中心的 message dict 并不是一回事,提示词里 inject 历史消息的方式也完全不同。
- 工具调用协议。同样是让模型调用工具,不同框架对 tool name、参数 JSON Schema、工具返回内容的封装层次不同。有的框架需要你写
@tooldecorator,有的框架需要你传function_map,有的框架走 function calling 标准,有的框架走 Anthropic tool use 风格,而这些细节直接决定模型能不能正确调用。 - 状态与记忆的存储方式。LangGraph 里面有显式的 graph state 和 checkpointer,AutoGen 把上下文放在 GroupChatManager 里,CrewAI 又把记忆分散到 Task 和 Crew 的 context 里。你在一个框架里积累的会话周期管理、上下文截断策略,到了另一个框架几乎等于重做。
- 人机交互与审批回调。有的框架把 tool 执行前的审批做成 callback,有的框架需要你在 Tool 内部自己调用
input(),这种流程差异在真实项目里比 API 差异更折磨人。
我见过很多团队评估新框架之后放弃迁移,不是新框架不好,而是大家算完重写成本之后被吓住了。重写业务逻辑可能只需要两周,但重写周边胶水层往往还要一个月。这种成本本质上不是技术债,而是把“框架”和“应用环境”耦合在一起造成的环境绑定问题。
1.2 容器能解决类似问题
做后端开发的人都体会过 Docker 带来的改变。以前部署一个 Python 应用,最怕的是测试环境和生产环境版本不一致,同一个 requirements.txt 装出来都可能跑出不同结果。Docker 的做法不是消灭环境差异,而是把应用和它依赖的环境一起打包成镜像,运行的时候通过容器运行时挡掉宿主机的差异。
这个思路的关键在于 镜像是一次性构建的密封交付物。你在镜像里写死 Python 3.11、装好某个版本的 system library、把启动命令固化下来,之后不管放到开发机、CI 机器还是生产服务器,只要 Docker Runtime 在,运行行为就一致。应用本身不需要关心底层是 CentOS 还是 Alpine,也不需要关心 Docker Desktop 和 Containerd 之间的差异。
Agent 框架的问题其实和服务器环境问题非常像。你写了一个 Agent 之后,真正有价值的资产是意图识别、规划逻辑、工具调用逻辑、业务工具实现和提示词,这些相当于“应用代码”。而框架提供的模型封装、会话调度、消息循环、状态管理,相当于“运行环境”。现在的问题是,大部分 Agent 工程的业务代码直接写死在某个框架环境里,搬到一个新环境就要重新适配环境、调整目录、甚至重写执行逻辑。
1.3 “80%迁移成本”到底藏在哪一层
我复盘那一次客服工作流迁移时,把修改过的文件分成两类:一类是业务定义文件,比如工具函数内部实现、提示词内容、业务状态的字段含义;另一类是框架耦合文件,比如 LangGraph 的 StateGraph 定义、节点函数之间的 state 传递写法、AutoGen 里 ConversableAgent 的构造和 reply 逻辑。
统计结果很扎心:业务定义类的改动只占大约 20%,剩下 80% 都是在处理框架差异。而且这部分 80% 是高度重复的,重复表现为“同样一个工具函数,在这个框架里要包一层装饰器,在另一个框架里要写 function schema,再换一个框架还要处理 event 回调”。只要有一个中间抽象层横在业务工具和框架执行器之间,把这层运行时差异统一收口,80% 的重复成本就有机
