扒开 LangChain 源码:为什么 Agent 里的 Message 数组可以“混搭”传参?

张开发
2026/4/20 9:27:41 15 分钟阅读

分享文章

扒开 LangChain 源码:为什么 Agent 里的 Message 数组可以“混搭”传参?
扒开 LangChain 源码为什么 Agent 里的 Message 数组可以“混搭”传参如果你最近在学习使用 LangChain 构建 AI Agent你可能会在一些开源项目或者教程中看到类似于下面这样一段让人“强迫症发作”的代码# 调用 Agent发送消息responseagent.invoke({messages:[{role:system,content:你是一个热心的AI助手。},{role:user,content:你好我是曦煜。},{role:assistant,content:你好曦煜很高兴认识你。},{role:user,content:北京今天天气如何},SystemMessage(content请使用工具来获取天气信息。)]})如果你有一点传统的静态强类型语言如 Java 或 C基础看到这段代码一定会感到极其违和在一个messages数组列表里前四个元素是普通的 Python 字典Dict而最后一个元素竟然是一个实例化的对象SystemMessage。这种把字典和对象混放在同一个数组里的“大杂烩”写法如果是普通框架早就抛出类型异常了。为什么在 LangChain 中它却能平稳运行今天我们就用架构师的视角扒开 LangChain 的底层源码看看这个“黑魔法”究竟是怎么回事。1. 框架的“亲儿子”原生的 Message 对象在 LangChain 最纯正的血统架构中所有的聊天消息都被严格抽象为BaseMessage的子类。最常见的有三个SystemMessage系统提示词定基调HumanMessage人类用户的输入AIMessage大模型的回复当你传入SystemMessage(content...)时LangChain 底层一看“这是标准接口的亲儿子格式极其规范直接放行” 这种面向对象的写法也是 LangChain 官方最初唯一推荐的写法。2. 框架的“干儿子”OpenAI 风格的字典既然有亲儿子为什么还要允许字典存在呢仔细看代码里的前四行{role: user, content: 你好}如果你熟悉 OpenAI 的原生 API你会立刻认出这不就是 OpenAI 官方标准的请求格式吗在早期的 LangChain 版本中如果你敢直接传这种字典框架真的会无情报错。它会逼着你必须在文件开头from langchain_core.messages import HumanMessage, SystemMessage然后把所有从 OpenAI 官方文档里复制来的 Prompt一行行手动改写成 LangChain 的对象。这种极其死板的强类型约束很快引来了社区开发者的疯狂吐槽“为了用你 LangChain我迁移旧代码的成本也太高了吧简直反人类”3. 架构师揭秘底层的“自动翻译官”为了平息众怒同时实现对整个 OpenAI 生态的完美向下兼容LangChain 开发团队做出了妥协。他们在底层的消息入口处如agent.invoke的内部流转机制中加入了一个极其强大的自动拦截与类型强转Type Coercion机制。在 LangChain 源码中通常对应着一个名为convert_to_messages的工具函数。当agent.invoke接到你传进来的这堆“大杂烩”数组时它并不会直接发给大模型而是先跑一个内部的遍历清洗循环遇到字典时它会提取字典里的role字段。如果是system它就在后台默默帮你new一个SystemMessage如果是user它就偷偷帮你转成HumanMessage。遇到对象时它发现本来就是BaseMessage的子类比如代码中的SystemMessage无需转换直接保留。最终在这个数组被真正发给底层大模型如 GPT-4 或 Claude的物理链路之前它已经被 LangChain 统一“洗”成了一排整整齐齐的纯对象数组。4. 总结与最佳实践建议看懂了这个底层逻辑我们就明白了这两种写法同时存在本质上是 LangChain 框架在**“惯着开发者”**。传字典为了让你少引包、少敲键盘方便你直接从 OpenAI 代码库无缝迁移。传对象这是框架的标准和高阶玩法。当你需要用到多模态传图片或者需要携带复杂的 Tool Calls工具调用记录时简单的字典根本塞不下那么复杂的属性此时必须使用原生的HumanMessage等对象。防坑建议虽然框架底层有着强大的包容心能够完美兼容这种“混搭”写法但在我们自己的正规企业级项目里强烈建议保持代码风格的统一。要么全用字典要么全用 Message 对象。像示例中这种混着写的代码属于典型的“坏味道Bad Smell”它不仅会让 IDE 的类型推导失效更容易让接手代码的同事感到困惑。优雅的代码永远是从统一规范开始的

更多文章