LangChain.js 实践:从 LCEL 到 Agent
LangChain.js 提供了一套围绕模型、提示词、工具、检索器和输出解析器的可组合抽象。它最重要的设计不是某一个 Agent 模板,而是统一的 Runnable 接口。
LangChain.js
在应用层,我们通常要处理提示词模板、模型调用、结构化输出、流式响应、重试和链路追踪。LangChain.js 把这些能力组织为可以独立测试和组合的模块。
import { ChatOpenAI } from "@langchain/openai";
export const model = new ChatOpenAI({
model: "gpt-4.1-mini",
temperature: 0,
});LCEL
LCEL(LangChain Expression Language)使用管道式组合表达数据流。链中的每一步接收上一步输出,并返回下一步可消费的结果。
import { StringOutputParser } from "@langchain/core/output_parsers";
import { ChatPromptTemplate } from "@langchain/core/prompts";
const prompt = ChatPromptTemplate.fromTemplate(
"请用三句话总结以下内容:\n\n{content}",
);
export const summaryChain = prompt.pipe(model).pipe(new StringOutputParser());
const result = await summaryChain.invoke({
content: "一段需要总结的技术文章……",
});flowchart LR
A[用户输入] --> B[Prompt Template]
B --> C[Chat Model]
C --> D[Output Parser]
D --> E[结构化结果]Runnable
Runnable 是统一协议。常用方法包括:
| 方法 | 场景 |
|---|---|
invoke | 单次输入、单次输出 |
batch | 批量并发调用 |
stream | 流式返回结果 |
pipe | 串联多个 Runnable |
统一接口意味着自定义业务步骤也能参与 LCEL 组合。
import { RunnableLambda } from "@langchain/core/runnables";
const normalize = RunnableLambda.from((input: string) => ({
question: input.trim(),
requestedAt: new Date().toISOString(),
}));Agent
固定链适合路径已知的任务;Agent 适合需要模型动态选择下一步动作的任务。一个典型循环包含规划、工具调用、观察结果和生成最终答案。
stateDiagram-v2
[*] --> Planning
Planning --> ToolCall: 需要外部信息
ToolCall --> Observation
Observation --> Planning
Planning --> FinalAnswer: 信息已充分
FinalAnswer --> [*]Tool 调用
Tool 应该有清晰的名称、窄职责、准确描述和可验证的输入 Schema。模型负责选择工具,应用仍要负责权限、超时、幂等和结果校验。
import { tool } from "@langchain/core/tools";
import { z } from "zod";
export const weatherTool = tool(
async ({ city }) => {
return `当前查询城市:${city}`;
},
{
name: "get_weather",
description: "查询指定城市的实时天气",
schema: z.object({
city: z.string().describe("城市名称"),
}),
},
);Agent 的可靠性来自边界设计和可观测性,而不只是更复杂的提示词。
工程建议
- 优先用固定 Runnable 链解决确定性流程。
- 只有在步骤必须动态决策时才引入 Agent。
- Tool 输入必须验证,敏感动作必须增加授权检查。
- 为模型调用、工具调用和最终输出记录可追踪的运行信息。
- 使用小型评测集验证正确性、延迟和成本。