LangChain 与单 Agent

要点

  • 还是用「AI 电子伴侣」这个场景
  • 做 LLM 应用时,最先碰到的问题通常不是 Prompt,而是「模型怎么接」
  • 用户说一句话,系统内部并不是原封不动地把这句话直接丢给模型
  • 只要不是最简单的 demo,请求处理过程通常都不止一步
  • 单纯调用模型时,系统只能「回答」

内容

1. 先从一个具体需求开始

还是用「AI 电子伴侣」这个场景。

现在我们先不讨论多 Agent,只看一个最常见的需求:用户发来一条消息,系统要在一轮请求里完成下面几件事:

  • 读懂用户这句话是什么意思
  • 带上当前的人设和对话上下文
  • 必要时查一下历史记忆
  • 如果需要,再调用某个工具
  • 最后把结果整理成一段自然回复

比如用户发来:

NOTE

我下周三下午要去看牙医,你帮我记一下。对了,我上次是不是说过我最怕看牙?

要处理好这条消息,系统至少得做这些事:

  1. 看懂这是一个「提醒 + 记忆确认」的混合请求
  2. 带上当前这个伴侣 Agent 的人设设定
  3. 取出最近的对话记录
  4. 必要时去查长期记忆
  5. 调用写日程的工具
  6. 最后生成一段自然的回复

LangChain 在这里负责的,就是把这一整套事情串起来。

所以这一篇不讲抽象定义,只讲一个很实际的问题:

当我们做一个单 Agent 应用时,LangChain 到底替我们处理了哪些工作。

2. 第一件事:把模型接进应用

做 LLM 应用时,最先碰到的问题通常不是 Prompt,而是「模型怎么接」。

如果不用框架,直接调用模型接口,你很快就会遇到这些细节:

  • 不同厂商的 SDK 不一样
  • 请求格式不一样
  • 返回结果结构不一样
  • 流式返回的处理方式也不一样

LangChain 先帮我们把这件事统一了。

在单 Agent 场景里,你最常做的一步就是给 Agent 指定一个模型:

// index.ts
import { createAgent } from 'langchain'
 
const agent = createAgent({
 
  model: 'openai:gpt-4.1-mini',
 
})

如果你想自己显式创建模型对象,也可以:

// index.ts
import { createAgent } from 'langchain'
 
import { ChatOpenAI } from '@langchain/openai'
 
const model = new ChatOpenAI({
 
  model: 'gpt-4.1-mini',
 
  temperature: 0.2,
 
})
 
const agent = createAgent({
 
  model,
 
})

这一步看起来很普通,但它解决的是最底层的接入问题。

从这里开始,后面的 Prompt、Tool、上下文、记忆,才有地方接进去。

在我们的 AI 伴侣场景里,这一步可以理解成:先把「会说话的大脑」接进来。

3. 第二件事:把输入整理成模型能理解的结构

用户说一句话,系统内部并不是原封不动地把这句话直接丢给模型。

真正送进模型之前,往往还要拼很多东西:

  • 当前这轮用户输入
  • 系统设定
  • 角色背景
  • 最近对话历史
  • 工具说明
  • 外部检索结果

这一层工作,如果手写字符串,也不是不能做,但很快就会乱。

LangChain 在这里负责两类事情:

第一类:消息结构。

聊天模型内部不是只吃一段字符串,而是吃一串有角色的消息。比如:

  • system
  • user
  • assistant
  • tool

LangChain 把这些消息组织方式统一起来,这样你在不同模型之间切换时,不用重新设计输入结构。

第二类:Prompt 组织。

Prompt 在项目里不是一句临时文案,而是一个长期维护的输入模板。你会逐步把不同来源的信息填进去,最后形成这一轮请求真正使用的上下文。

在 AI 伴侣场景里,这一步通常包括:

  • 固定人设
  • 当前情绪设定
  • 最近聊天记录
  • 用户这次发来的问题

所以从应用层看,LangChain 并不是只负责「调模型」,它还负责把输入整理成可维护的结构。

4. 第三件事:把多个步骤串成一条链

只要不是最简单的 demo,请求处理过程通常都不止一步。

还是刚才那条「下周三看牙」的消息,系统内部很可能会这样走:

  1. 先整理输入
  2. 判断是否需要查记忆
  3. 查询记忆
  4. 调用提醒工具
  5. 生成最终回复

如果这些步骤完全散落在普通函数里,代码会越来越像「拼装式脚本」。前一个函数的输出怎么交给下一个函数,哪个阶段失败了,哪里要改,都不够直观。

LangChain 在这里提供的是一套链路组织方式。

你可以把它理解成:把一堆零散动作,整理成一条明确的数据流。

最简单的链可能只有三步:

// index.ts
const chain = prompt.pipe(model).pipe(parser)

复杂一点时,这条链里还会加入:

  • 历史消息注入
  • 检索结果补充
  • 工具调用
  • 输出解析

在单 Agent 应用里,这件事很重要。因为随着功能一点点加上去,你最先需要的不是更高级的智能,而是更稳定的组织方式。

5. 第四件事:把外部能力接成工具

单纯调用模型时,系统只能「回答」。

一旦接入工具,系统才真正开始「做事」。

在 AI 伴侣场景里,常见工具包括:

  • 查询长期记忆
  • 写入提醒事项
  • 查询天气
  • 调用搜索接口

LangChain 在这里负责的是,把这些外部能力接成模型能调用的工具。

比如一个写提醒工具:

// index.ts
import { tool } from 'langchain'
 
import * as z from 'zod'
 
const createReminder = tool(
 
  async ({ title, time }) => {
 
    return `已创建提醒:${time} ${title}`
 
  },
 
  {
 
    name: 'create_reminder',
 
    description: '创建提醒事项',
 
    schema: z.object({
 
      title: z.string(),
 
      time: z.string(),
 
    }),
 
  }
 
)

这里 LangChain 做的不是帮你实现提醒逻辑。提醒逻辑还是你自己写。

它负责的是把这段能力包装成一个标准工具,让 Agent 能知道:

  • 这个工具叫什么
  • 它是干什么的
  • 它接受什么参数
  • 它返回什么结果

这一步做完以后,Agent 才有机会在一次请求里决定:

  • 现在需不需要创建提醒
  • 传什么参数给提醒工具
  • 拿到工具结果后,怎么继续回复用户

6. 第五件事:让一个 Agent 处理完整的一轮请求

把模型、输入结构、链路、工具都准备好后,LangChain 最终做的事就是:

把这些东西合成一个能真正接任务的 Agent。

这一层在代码里就体现为 createAgent()

// index.ts
import { createAgent } from 'langchain'
 
const agent = createAgent({
 
  model: 'openai:gpt-4.1-mini',
 
  tools: [createReminder, searchMemory],
 
  systemPrompt: '你是一个自然、克制、会照顾用户情绪的陪伴型助手。',
 
})

这个 Agent 建好后,一轮请求就能完整跑起来:

// index.ts
const result = await agent.invoke({
 
  messages: [
 
    {
 
      role: 'user',
 
      content: '帮我记一下下周三下午两点去看牙医,再告诉我我是不是以前提过怕看牙。',
 
    },
 
  ],
 
})

这一步里,LangChain 承担的是一个很实际的角色:

  • 接住用户输入
  • 组织这一轮上下文
  • 让模型判断是否要用工具
  • 执行工具调用
  • 把结果继续送回模型
  • 生成最后回复

这个流程如果完全手写,也能做,但会非常散。

LangChain 的价值就在于把这一整轮处理过程变成一种稳定的开发方式。

这里要单独提醒一下,避免把两种东西混在一起。

前面讲 prompt.pipe(model).pipe(parser) 时,我们说的是普通链路。那种链路里,步骤怎么串、什么时候查工具、什么时候结束,通常都由你自己写清楚。

这里讲 createAgent() 时,说的是 agent runtime。它和普通链最大的区别在于:模型可以在运行过程中决定要不要调用工具、先调用哪个工具、拿到工具结果后是否继续。

所以这两种能力不是互相冲突,而是层级不同:

  • 普通链更像你手动安排步骤
  • Agent 更像运行时自己在模型和工具之间做一轮调度

下图可以把这件事看得更清楚一些:

Drawing canvas 图里从左到右就是一轮常见请求:用户消息进入后,LangChain 先组织上下文,再把模型、工具、记忆这些能力接到同一条链路里,最后输出回复。

7. 第六件事:给单 Agent 应用补上记忆和检索

真实应用里,一个 Agent 很少是完全无状态的。

还是 AI 伴侣这个场景。如果用户说:

NOTE

我是不是以前说过我最怕看牙?

系统不能只看当前这一句话,它得知道过去聊过什么。

这时就会出现两类能力:

第一类是短期记忆,也就是对话上下文。

比如最近几轮消息,要不要带进本轮请求里。

这是短距离的上下文问题。

在最新版本里,这类能力更接近「当前线程里的状态管理」。

如果要跨轮次保留下来,通常会和 agent state、checkpointer 这套机制一起使用。

第二类是长期记忆或外部检索。

比如用户几周前说过的偏好、某次事件、某条固定资料。

这类信息通常不会原封不动一直塞在当前上下文里,而是需要在用到的时候再取出来,所以它更接近检索问题。

LangChain 在这里负责的是把这些能力和单 Agent 主链路接起来。

它不是替你设计记忆系统本身,而是让:

  • 历史消息
  • 检索结果
  • 外部知识片段

都可以作为这一轮输入的一部分,稳定进入 Agent 的处理流程。

所以这里最好把两件事分开看:

  • 短期记忆:当前线程里的上下文延续
  • 长期记忆 / 检索:需要时再取回来的信息

所以很多时候,LangChain 在单 Agent 应用里做的,正是这种「胶水工作」:

  • 把模型接起来
  • 把工具接起来
  • 把上下文接起来
  • 把检索结果接起来
  • 再把它们组织成一轮完整的请求处理

8. 总结

到这里为止,LangChain 的位置就比较清楚了

在单 Agent 应用里,它主要负责的是:

  • 模型接入
  • 输入组织
  • 链路编排
  • 工具接入
  • 一轮请求处理
  • 记忆和检索的接入

也就是说,LangChain 这一层最擅长解决的问题是:

怎样把一个单独的 Agent 做成一个真正能工作的应用单元