Skip to content

概述

要点

  • LangChain 的定位不是「让模型更聪明」,而是把模型、消息、Prompt、工具、链路和 Agent 组装成一套可维护的调用结构。
  • 单 Agent 的核心任务是:在一轮请求里理解输入、组织上下文、判断是否调用工具、执行工具并生成回复。
  • LangChain 的包结构分为四层:@langchain/core 定义协议,langchain 提供高层 API,@langchain/{provider} 接具体模型,@langchain/classic 保留旧抽象。
  • 一个 Agent 至少由模型、消息、Prompt、Runnable、Tool 和 createAgent() 组装而成,缺一不可。

1. 背景:为什么需要 LangChain

直接调用模型 API 时,代码会快速分成几块:模型调用、Prompt 拼接、工具调用、输出解析。每一块单独看都不复杂,但组合起来之后,缺少统一的组织方式。

以一条典型请求为例:

帮我记一下明天下午三点开会,再查一下上海明天会不会下雨。

要让模型正确处理这句话,系统内部至少要完成:

  1. 接住用户消息。
  2. 把系统设定、工具说明整理成模型可读的上下文。
  3. 把输入交给模型,让它判断是否需要调用工具。
  4. 执行工具(创建提醒、查询天气)。
  5. 把工具结果回送给模型。
  6. 生成最终回复。

这些步骤如果全部手写,功能一多就会散开。LangChain 的作用,是把它们放进一套统一的写法里,让模型、Prompt、工具、链路和 Agent 可以稳定组合。

2. LangChain 的包结构

LangChain 当前版本的包结构大致分为四层,每层承担的职责不同。

2.1 @langchain/core

@langchain/core 是基础抽象层,负责定义通用协议,让上层组件按同样的方式组合。它包括:

  • 消息类型:HumanMessageSystemMessageAIMessageToolMessage
  • Prompt 模板:ChatPromptTemplateMessagesPlaceholder
  • Runnable 协议:RunnableParallelRunnableSequence 等。
  • 输出解析器:StringOutputParserJsonOutputParser 等。
typescript
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

这一层也可以从 langchain 主包中重新导出,例如:

typescript
import { HumanMessage, SystemMessage } from "langchain";

两种导入方式在功能上是一致的,只是入口不同。如果代码只在 @langchain/core 中能找到对应类型,就从 core 导入;如果是常用高层类型,从 langchain 导入也可以。

2.2 langchain

langchain 是应用层最常用的入口,主要提供:

  • createAgent():组装一个可运行的 Agent。
  • tool():把函数包装成模型可调用的工具。
  • initChatModel():按模型名快速初始化模型。
  • middleware:运行时的中间件能力。
  • 常用消息类型和高层能力的 re-export。

单 Agent 场景下,这一章大多数代码都会从 langchain 包开始写。

2.3 @langchain/

这一层用来接具体模型厂商。例如:

  • @langchain/openai
  • @langchain/anthropic
  • @langchain/google-genai

如果只是快速开始,可以用 initChatModel()

typescript
import { initChatModel } from "langchain";

const model = await initChatModel("gpt-4.1-mini", {
  modelProvider: "openai",
});

如果需要更细的 provider 配置,再显式创建模型对象:

typescript
import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({
  model: "gpt-4.1-mini",
  temperature: 0.2,
});

2.4 @langchain/classic

老版本里常见的 chainsmemoryindexing 抽象,现在不少已经移到 @langchain/classic 包里。如果你在旧资料里看到 ConversationBufferMemory、老的 chain 封装或早期 retrieval 组合方式,需要知道它们已经不在 langchain 主包中了。

3. 一个最小可用的 Agent 例子

先从一个能跑起来的例子开始,再回过来拆结构。下面的代码实现了一个能创建提醒和查询天气的任务助手:

typescript
import * as z from "zod";
import { createAgent, initChatModel, tool } from "langchain";

const model = await initChatModel("gpt-4.1-mini", {
  modelProvider: "openai",
});

const createReminder = tool(
  async ({ title, time }) => `提醒已创建:${time} ${title}`,
  {
    name: "create_reminder",
    description: "创建提醒事项",
    schema: z.object({
      title: z.string().describe("提醒内容"),
      time: z.string().describe("提醒时间"),
    }),
  },
);

const getWeather = tool(
  async ({ city }) => `${city} 明天有小雨,出门记得带伞`,
  {
    name: "get_weather",
    description: "查询城市天气",
    schema: z.object({
      city: z.string().describe("要查询天气的城市"),
    }),
  },
);

const agent = createAgent({
  model,
  tools: [createReminder, getWeather],
  systemPrompt: "你是一个细心、自然的任务助手。",
});

const result = await agent.invoke({
  messages: [
    {
      role: "user",
      content:
        "我明天下午三点要开会,帮我记一下。顺便查一下上海明天会不会下雨。",
    },
  ],
});

console.log(result.messages.at(-1)?.content);

这段代码已经覆盖了 LangChain 单 Agent 场景里最重要的几样东西:

  • initChatModel():把模型接进来。
  • tool():把外部能力包装成工具。
  • createAgent():把模型、工具和系统设定组装成一个 Agent。
  • agent.invoke():把一轮消息交给 Agent 处理。

后面的章节会围绕这四件事展开。

4. 核心组件:用一个 Agent 的结构来看

为了不把内容讲散,我们继续盯着一个 Agent 的结构看。一个完整的 Agent 至少有下面这些部分:

  • 一个模型(大脑)
  • 一组工具
  • 一段系统设定
  • 一轮一轮收到的消息
  • 一个对外执行任务的方法 invoke()

LangChain 处理的,就是这些部分怎样接到一起。

4.1 模型:先把推理能力接进来

模型是最底层的推理能力来源。在 LangChain 里,模型接入之后,会尽量统一成相似的调用方式。这样做的直接好处是:换模型时,上层代码不至于全部推倒重来。

typescript
import { initChatModel } from "langchain";

const model = await initChatModel("gpt-4.1-mini", {
  modelProvider: "openai",
});
typescript
import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({
  model: "gpt-4.1-mini",
  temperature: 0.2,
});

前一种更适合快速开始;后一种适合需要显式控制 provider 细节的场景。

4.2 消息:聊天模型的输入不是纯字符串

聊天模型处理的核心输入,通常不是一段字符串,而是一组带角色的消息。例如:

  • system
  • user
  • assistant
  • tool

在 LangChain 里,消息可以写成对象字面量,也可以写成消息类。

typescript
const result = await agent.invoke({
  messages: [
    { role: "system", content: "你是一个细心的任务助手。" },
    { role: "user", content: "帮我记一下明天上午九点开会。" },
  ],
});

如果只是一次性生成,直接传字符串给模型也可以:

typescript
const response = await model.invoke("帮我写一句晚安留言");

但只要进入对话、工具调用或历史消息场景,消息结构就会比纯字符串更自然。

4.3 Prompt:把零散输入整理成可维护的上下文

单个 Agent 真正难写的地方,通常不是调用模型,而是怎么组织输入。在一个真实场景里,交给模型的内容往往包括:

  • 系统设定
  • 角色信息
  • 历史消息
  • 检索结果
  • 工具说明

如果靠模板字符串硬拼,后面会越来越难维护。LangChain 提供了 Prompt 相关抽象,让你可以把输入拆成稳定部分和动态部分。

typescript
import { ChatPromptTemplate } from "@langchain/core/prompts";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "你是{name},你的角色设定是:{persona}"],
  ["human", "{input}"],
]);

const messages = await prompt.invoke({
  name: "任务助手",
  persona: "说话自然、做事有条理",
  input: "我今天下班特别晚,有点累",
});

这一块在后面的 Prompt 章节会单独展开。

4.4 Runnable:把多个步骤串成链

模型、Prompt、输出解析这些组件放在一起之后,下一步就是把它们接成一条链。LangChain 里常用的方式是 Runnable 协议。只要组件遵守这套协议,就能用类似方式调用,也能用 .pipe() 串起来。

typescript
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "你是一个很会安慰人的助手。"],
  ["human", "{input}"],
]);

const chain = prompt.pipe(model).pipe(new StringOutputParser());

const reply = await chain.invoke({
  input: "今天工作压力有点大",
});

这里有一个边界要记住:

  • prompt.pipe(model).pipe(parser) 这种写法属于普通链路。
  • createAgent() 属于 agent runtime。

普通链路里,步骤怎么走,基本由你自己安排。Agent 则是在运行时让模型决定要不要调用工具、先调用哪个工具、拿到工具结果后要不要继续。这一层差别,后面写多工具 Agent 时会很明显。

4.5 Tool:让 Agent 从「会回答」变成「会做事」

如果没有工具,模型再聪明,也只能在已有上下文里生成内容。接上工具以后,Agent 才能开始动手。常见工具包括:

  • 创建提醒
  • 查询天气
  • 检索长期记忆
  • 查询日程

LangChain 提供的 tool() 主要做的是标准化包装。它把一个普通函数包装成 Agent 可识别的工具,并补上描述和参数 schema。

typescript
import * as z from "zod";
import { tool } from "langchain";

const searchMemory = tool(
  async ({ query }) => `检索到和「${query}」相关的历史记录`,
  {
    name: "search_memory",
    description: "检索与用户相关的长期记忆",
    schema: z.object({
      query: z.string().describe("要检索的内容"),
    }),
  },
);

这个工具的业务逻辑还是你自己实现。LangChain 负责的是把它包装成模型能理解、能调用的形式。

4.6 Agent:把这些能力装进一个可以接任务的对象

当前这条主线的终点,就是 createAgent()。你可以把它理解成:前面准备好的模型、工具、系统设定,最终都要在这里合体。

typescript
import { createAgent, initChatModel } from "langchain";

const model = await initChatModel("gpt-4.1-mini", {
  modelProvider: "openai",
});

const agent = createAgent({
  model,
  tools: [createReminder, getWeather, searchMemory],
  systemPrompt: "你是一个温和、细心、做事有条理的任务助手。",
});

const result = await agent.invoke({
  messages: [
    {
      role: "user",
      content:
        "帮我记一下周五晚上七点和朋友吃饭,再看看北京周五晚上会不会下雨。",
    },
  ],
});

这时候的运行过程,已经不是简单的「Prompt 进,文本出」了:

  1. Agent 接住消息。
  2. 模型判断需不需要工具。
  3. 如果需要,就发起工具调用。
  4. 工具执行完成后,把结果回给模型。
  5. 模型生成最终回复。

这正是 LangChain 这一章后面要逐步拆开的内容。

5. 总结

LangChain 的核心价值,是把模型、消息、Prompt、工具、链路和 Agent 组装成一套可维护的调用结构。它的包结构分为四层:

  • @langchain/core:定义协议和基础抽象。
  • langchain:提供 createAgent()tool()initChatModel() 等高层 API。
  • @langchain/{provider}:接具体模型厂商。
  • @langchain/classic:保留旧版本的链、记忆、索引等抽象。

一个 Agent 至少由模型、消息、Prompt、Runnable、Tool 和 createAgent() 组装而成。理解这六样东西的边界和连接方式,是后续学习 Prompt 模板、输出解析、链路编排、记忆持久化和多工具 Agent 的基础。

基于 MIT 协议开源