LangChain.js:Message、Model 与 LCEL 核心解析
摘要: LangChain.js为构建大语言模型应用提供了三大核心组件:Message(标准化消息格式,如SystemMessage、HumanMessage等)、Model(统一LLM调用接口,支持同步/流式/批量处理)和LCEL(声明式组合语法)。通过.pipe()方法,开发者可将提示词、模型和解析器串联成可复用流程,并支持并行处理(如RunnableMap)。该框架显著简化了多模型集成、流式
在构建 LLM(大语言模型)应用时,LangChain 无疑是目前最强大的编排框架之一。虽然 Python 版本广为人知,但在全栈开发和 Serverless 环境中,LangChain.js (TypeScript) 的生态同样蓬勃发展。
本文将带你拆解 LangChain.js 的三大基石:
- Message:与模型沟通的通用语言。
- Model:驱动应用的大脑,涵盖同步与流式输出。
- LCEL:将组件串联成复杂应用的声明式“胶水”语法。
一、 Message:沟通的基石
在大模型交互中,单纯的字符串(String)已经无法满足复杂的上下文需求。LangChain 抽象出了一套标准的 Message 对象,用于在不同的模型提供商(OpenAI, Anthropic, Gemini 等)之间统一数据格式。
所有消息类型都继承自 BaseMessage,包含 content(内容)和 role(角色)等核心属性。
1. 核心消息类型详解
-
SystemMessage (系统消息):
-
作用: 设定 AI 的行为基准、角色设定或背景知识。通常作为对话历史的第一条。
-
示例:
new SystemMessage("你是一个精通 TypeScript 的资深架构师。") -
HumanMessage (人类消息):
-
作用: 代表来自用户的输入。
-
示例:
new HumanMessage("如何优化 React 的渲染性能?") -
AIMessage (AI 消息):
-
作用: 模型生成的回复。通常用于存储对话历史,让模型“记住”它之前说过的话。
-
示例:
new AIMessage("可以使用 React.memo 或 useMemo 进行优化...") -
ToolMessage (工具消息):
-
作用: 当模型调用外部工具(Function Calling)后,工具返回的结果需要封装在此消息中传回给模型。包含
tool_call_id以匹配请求。 -
ChatMessage (通用消息):
-
作用: 当你需要自定义非标准角色(如
role: "reviewer")时使用。 -
AIMessageChunk (AI 块消息):
-
作用: 专用于流式传输 (Streaming)。它不仅包含内容片段,还包含
tool_call_chunks等增量数据。在流式响应拼接时非常关键。
代码示例:构建对话历史
import { SystemMessage, HumanMessage, AIMessage } from "@langchain/core/messages";
const messages = [
new SystemMessage("你是一个乐于助人的翻译助手。"),
new HumanMessage("I love programming."),
new AIMessage("我喜欢编程。"),
new HumanMessage("What is LangChain?")
];
// 这些消息可以直接传给 ChatModel
二、 Model:不仅是输入输出
在 LangChain 中,ChatModel 是对底层 LLM API 的一层封装。它不仅统一了调用接口(不再需要分别去查 OpenAI 或 Claude 的 API 文档),还提供了丰富的功能。
1. 同步输出 (invoke)
这是最基础的调用方式,等待模型生成完整回复后一次性返回。
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
// 实例化模型
const model = new ChatOpenAI({
modelName: "gpt-4o",
temperature: 0.7,
});
// 执行调用
async function runSync() {
const response = await model.invoke([
new HumanMessage("给我讲一个关于程序员的冷笑话")
]);
// response 是一个 AIMessage 对象
console.log(response.content);
}
2. 流式输出 (stream) —— 提升用户体验的关键
在构建 Chatbot 时,等待几秒钟的空白期会让用户感到焦虑。stream 方法允许我们接收一个个 Token(字符片段),实现类似打字机的效果。
async function runStream() {
const stream = await model.stream([
new HumanMessage("请写一篇 500 字的科幻小说摘要")
]);
console.log("开始生成...");
// 这里的 chunk 是 AIMessageChunk 类型
for await (const chunk of stream) {
// process.stdout.write 可以不换行打印
process.stdout.write(chunk.content as string);
}
}
3. 进阶能力:Batch (批量处理)
除了同步和流式,LangChain 的 Model 还支持 batch,可以并发处理多个请求,这在处理大量数据分类或翻译时非常有用。
async function runBatch() {
const responses = await model.batch([
[new HumanMessage("你好")],
[new HumanMessage("再见")]
]);
console.log(responses.length); // 2
console.log(responses[0].content); // "你好!有什么我可以帮你的吗?"
}
三、 LCEL:声明式的魔法
LCEL (LangChain Expression Language) 是 LangChain 框架底层的核心语法。它的核心理念是Composable(可组合性)。
在 JavaScript/TypeScript 中,LCEL 主要通过 .pipe() 方法(类似于 Unix 的管道 |)来实现。它让我们可以将 Prompt(提示词)、Model(模型)、OutputParser(输出解析器)等组件像搭积木一样串联起来。
为什么使用 LCEL?
- 清晰的逻辑: 输入 -> 模板 -> 模型 -> 解析 -> 输出。
- 统一的接口: 每个组件都实现了
Runnable接口(拥有invoke,stream,batch方法)。 - 自动并行: LCEL 可以在可能的情况下自动并行执行步骤。
核心组件组合示例
让我们构建一个经典的链:PromptTemplate -> ChatModel -> StringOutputParser。
- PromptTemplate: 将用户输入转换为完整的 Prompt。
- StringOutputParser: 将模型输出的
AIMessage直接转为纯字符串。
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
// 1. 定义模型
const model = new ChatOpenAI({ modelName: "gpt-3.5-turbo" });
// 2. 定义提示词模板
const prompt = ChatPromptTemplate.fromMessages([
["system", "你是一个专业的{topic}专家。"],
["human", "{question}"]
]);
// 3. 定义输出解析器 (从 Message 中提取 content 字符串)
const outputParser = new StringOutputParser();
// 4. 使用 pipe 组合链 (LCEL)
// 数据流向:Input -> prompt -> model -> outputParser -> Output
const chain = prompt.pipe(model).pipe(outputParser);
// 5. 调用链
async function runChain() {
// invoke 的参数会自动注入到 prompt 的变量中
const result = await chain.invoke({
topic: "前端架构",
question: "什么是微前端?"
});
// 此时 result 已经是纯字符串,而非 AIMessage 对象
console.log(result);
}
LCEL 的强大之处:RunnableMap
LCEL 不仅仅是单链条,还可以通过 RunnableMap (在 JS 中通常通过对象结构或 RunnableSequence 实现) 并行处理数据。
例如,我们需要先检索上下文(Context),再回答问题:
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
// 假设我们有一个检索器函数 (模拟 RAG)
const retriever = async (input: string) => "一些相关的背景知识...";
const ragChain = RunnableSequence.from([
{
context: async (input: string) => await retriever(input), // 步骤1:获取上下文
question: new RunnablePassthrough() // 步骤1并行:透传原始问题
},
prompt, // 步骤2:结合 context 和 question 填充模板
model, // 步骤3:模型推理
outputParser // 步骤4:解析输出
]);
LangChain 的 LCEL 范式持续演进
如今,通过 model.withStructuredOutput(zodSchema),我们可以更简洁地定义输出结构,而无需手动编写解析逻辑。这并非取代 LCEL,而是 LCEL 在结构化数据场景下的自然延伸——让声明式链路更强大、更类型安全。
// 完整 LCEL 链 + 结构化输出
const movieChain = ChatPromptTemplate.fromTemplate(
"Extract movie info from the following text:\n{text}"
).pipe(
model.withStructuredOutput(Movie)
);
const output = await movieChain.invoke({
text: "Inception came out in 2010 and was directed by Christopher Nolan."
});
// output: { title: "Inception", year: 2010, director: "Christopher Nolan", rating: ... }
总结
- Message 解决了“怎么跟 AI 说话”的数据结构问题,区分了 System、Human 和 AI 的职责。
- Model 提供了统一的 API 网关,支持 Invoke(同步)、Stream(流式)和 Batch(批量)三种模式。
- LCEL 是将上述积木搭建成城堡的粘合剂,通过
.pipe()实现了代码的高可读性、模块化和易维护性。
更多推荐

所有评论(0)