LangChain 架构演进问答

Agent|字数 6,493|阅读时长 ≈ 17 分钟

版本:2026-07-04

定位:这是一篇基于 langchain-learning-guide.md 改写的问答式学习文档。它用“遇到问题 -> 做出抽象 -> 又遇到新问题 -> 继续迭代”的方式,讲清楚 LangChain 为什么需要 Model、Prompt Template、Runnable、LCEL、Output Parser、RAG、Tools、Agent、LangGraph 和 LangSmith。

说明:本文是架构学习推演,不是 LangChain 官方真实历史时间线。它更像一次框架设计复盘:把现在的 LangChain 能力倒推成一串自然的问题和回答。

0. 先给一个结论

问:LangChain 到底是什么?

答:LangChain 是一个构建 LLM 应用和 Agent 应用的框架。它的核心价值不是“帮你调用一次模型”,而是把模型、提示词、检索、工具、输出解析、运行时控制和可观测性组织成可组合的工程链路。

一句话:

LangChain 解决的是:当你的 LLM 应用从一个 prompt 变成一套系统时,怎么让它还能被组合、调试、替换和维护。

问:为什么要用问答方式理解它?

答:因为 LangChain 的组件很多,如果从名词开始背,很容易进入“PromptTemplate、Runnable、Retriever、AgentExecutor、OutputParser,字我都认识,系统我不认识”的状态。

换个方式:每个组件都先问一句“没有它会怎样坏掉?”架构就清楚多了。

1. 最开始的问题:我只是想调用一次大模型

问:最早的 LLM 应用长什么样?

答:通常很朴素:

Code
用户输入 -> 拼接 prompt -> 调用模型 API -> 返回文本

用代码想象一下:

Codepython
prompt = f"请回答:{question}"answer = call_model(prompt)print(answer)

这非常合理。做 demo、写脚本、验证想法,都够用。

问:那为什么还需要 LangChain?

答:因为 demo 变产品后,问题会排队进门:

  • Prompt 越写越长,字符串拼接开始失控。
  • 想换模型供应商,业务代码到处都是 provider SDK。
  • 模型输出有时是 JSON,有时是散文,有时是“我觉得吧”。
  • 需要接知识库,不能只靠模型记忆。
  • 需要调用工具,模型不能只说不做。
  • 需要记录每次调用为什么错、慢、贵。
  • 需要批处理、流式、重试、fallback、评估。

这时你会发现:真正复杂的不是 call_model(),而是围绕它的一整圈工程问题。

问:第一条经验是什么?

答:LLM 应用的复杂度不是从“模型很难调用”开始的,而是从“模型调用周围的一切都要长期维护”开始的。

2. 第一次迭代:把模型调用封装成 Model

问:直接用 OpenAI、Anthropic、Ollama 的 SDK 不行吗?

答:可以。但如果业务代码直接依赖某个 provider SDK,就会出现几个问题:

  • 换模型需要改大量调用点。
  • 不同 provider 的参数、消息格式、流式协议不一致。
  • 工具调用和结构化输出支持度不同。
  • 线上排查时难以统一记录模型名称、温度、token、延迟和错误。

问:LangChain 怎么做?

答:抽象出 Model 接口。业务链路不直接关心底层 API 细节,而是把模型当成可替换组件。

Code
Prompt -> Model -> Output

真实项目里,Model 可以来自 OpenAI、Anthropic、Google、Ollama、AWS Bedrock 等 provider。

问:这个抽象解决了什么?

答:它让“调用某个模型”和“业务链路怎么组织”分离。

问题Model 抽象的价值
想换模型尽量少改业务链路
想记录调用统一收集参数、token、错误和延迟
想对比模型同一条 chain 可以跑不同 model
想做 fallback主模型失败后切到备用模型

问:新发现是什么?

答:模型是核心,但不应该是代码里的“地基水泥”。它应该是一个可替换零件。

不然换模型时,你会像在装修时发现承重墙是用 prompt 写的。

3. 第二个问题:Prompt 不是普通字符串

问:Prompt 不就是一段字符串吗?

答:一开始是。后来它会长出变量、格式、规则、上下文、输出要求和版本。

比如:

Code
请根据下面资料回答问题。 资料:{context}问题:{question} 要求:1. 只根据资料回答。2. 不知道就说不知道。3. 输出 JSON。

这已经不是随手拼字符串了,它更像一个应用接口。

问:如果继续手写字符串拼接,会有什么问题?

答:

  • 变量名不清楚。
  • 少传一个变量,运行时才炸。
  • 多人协作时难以知道 prompt 的输入契约。
  • RAG 场景容易把 context、question、history 混在一起。
  • Prompt 版本难比较。

问:LangChain 怎么解决?

答:引入 Prompt Template,例如 ChatPromptTemplate。模板声明输入变量,把自然语言提示词变成可复用、可测试的组件。

Code
输入变量 -> Prompt Template -> 模型消息

问:这个抽象的关键是什么?

答:Prompt 不再只是文本,而是链路中的一个节点。

它有:

  • 输入变量。
  • 模板内容。
  • 输出消息格式。
  • 可测试性。
  • 可复用性。

问:新发现是什么?

答:在 LLM 应用里,Prompt 是接口,不是文案。

文案可以灵感一来就改;接口这么改,后面的 parser、retriever、测试和业务系统都会在旁边沉默地看着你。

4. 第三个问题:模型输出不是给人看的就完了

问:模型返回一段自然语言,不就可以展示了吗?

答:如果只是聊天,可以。但如果结果要进入业务系统,自由文本就危险了。

你可能希望模型返回:

Codejson
{  "answer": "...",  "sources": ["doc-1", "doc-3"],  "confidence": 0.82}

但模型可能返回:

Code
当然可以!下面是 JSON:...

热情是热情,程序先哭了。

问:LangChain 怎么解决?

答:引入 Output Parser 和结构化输出。它们负责把模型结果转换成下游系统可消费的格式。

Code
Model output -> Output Parser -> Python object / JSON / schema

问:为什么这一步重要?

答:因为生产系统不能靠猜。

没有 Parser有 Parser / Structured Output
下游从文本里正则抠字段下游拿稳定结构
字段缺失晚发现可以校验和重试
Prompt 一变就解析坏输出契约更明确
测试困难可写 schema 测试

问:新发现是什么?

答:模型输出不是终点,而是另一个系统的输入。只要它要被程序继续处理,就应该有结构、有校验、有失败策略。

5. 第四个问题:多个步骤怎么串起来?

问:Model、Prompt、Parser 都有了,怎么组合?

答:最自然的组合是:

Code
Input -> Prompt -> Model -> Parser -> Output

如果全靠普通函数也能写:

Codepython
prompt_text = build_prompt(input)raw = model(prompt_text)result = parse(raw)

但随着链路变复杂,会遇到新问题:

  • 有的步骤要并行。
  • 有的步骤要批处理。
  • 有的步骤要流式输出。
  • 有的步骤要重试或 fallback。
  • 有的步骤是 retriever,不是 model。
  • 有的步骤是普通函数,但也想放进链路。

问:LangChain 怎么解决?

答:抽象出 Runnable,并用 LCEL 组合。

Runnable 是统一接口。Prompt、Model、Parser、Retriever、函数包装器,都可以当成 Runnable。

LCEL 常见写法:

Codepython
chain = prompt | model | parserresult = chain.invoke(input)

问:为什么管道符有价值?

答:它让链路结构一眼可见。

Code
prompt | model | parser

比一堆嵌套函数更容易看出数据如何流动。不是因为符号多高级,而是因为结构清楚。

问:Runnable 统一了哪些执行方式?

答:

方法解决什么
invoke单个输入
batch多个输入
stream流式输出
组合 Runnable复杂链路拆成可测试小块

问:新发现是什么?

答:当 LLM 应用超过三步时,最怕的不是多一步,而是每一步都用不同姿势执行。

Runnable 的价值就是把这些姿势统一一下,让代码少做瑜伽。

6. 第五个问题:模型不知道我的私有知识

问:模型不是已经很聪明了吗?为什么还需要 RAG?

答:模型知道的是训练时学到的公开或历史知识。它不知道你的内部文档、最新政策、业务数据、项目代码、用户手册,也不应该凭空编。

于是问题来了:

怎么让模型基于可控资料回答,而不是靠记忆和想象?

问:RAG 怎么解决?

答:RAG 把外部资料检索出来,作为上下文交给模型。

Code
用户问题 -> Retriever -> 相关文档用户问题 + 相关文档 -> Prompt -> Model -> Answer

它通常有两段:

阶段做什么
Indexing加载文档、切分文本、生成 embedding、写入向量库或搜索索引
Retrieval + Generation根据问题检索相关片段,放入 Prompt 生成答案

问:RAG 第一版一般怎么写?

答:常见第一版:

Code
把所有文档切块 -> 向量化 -> top_k 检索 -> 塞进 prompt -> 回答

这版能跑,而且很有成就感。

问:然后会遇到什么新问题?

答:

  • 切块太大,召回片段冗长。
  • 切块太小,语义断裂。
  • top_k 太少,漏信息。
  • top_k 太多,prompt 变胖。
  • 检索到相似但不相关的内容。
  • 答案没有引用,用户无法核查。
  • 模型拿着错误资料认真回答。

RAG 的尴尬时刻是:你以为在减少幻觉,结果只是给幻觉递了参考资料。

问:怎么改进?

答:

  • 调整 chunk size 和 overlap。
  • 使用 metadata、tags、filters。
  • 做 rerank。
  • 输出 source id。
  • 对答案做 groundedness 检查。
  • 准备 RAG 测试集。
  • 记录 retrieved docs 和最终答案。

问:新发现是什么?

答:RAG 的核心不是“向量库接上了”,而是“检索结果是否真的支撑答案”。

检索质量往往比 Prompt 花活更重要。

7. 第六个问题:助手不能只会回答,还要能行动

问:RAG 能回答问题,但如果我要它查库存、发邮件、调用 API 呢?

答:这就需要 Tools。

Tool 是模型可以调用的外部能力,比如:

  • 搜索网页。
  • 查询数据库。
  • 调用业务 API。
  • 执行计算。
  • 读取文件。
  • 创建工单。

问:Tool 和普通函数有什么区别?

答:普通函数是给程序员调用的;Tool 是给模型理解和选择的。

所以 Tool 必须写清楚:

  • 名称。
  • 作用。
  • 参数类型。
  • docstring。
  • 返回格式。
  • 失败情况。

问:工具接入后会遇到什么问题?

答:

  • 模型可能选错工具。
  • 参数可能填错。
  • 工具可能超时。
  • 工具结果可能太长。
  • 工具可能有副作用。
  • 高风险工具可能误操作。

问:怎么改进?

答:

问题改进
工具选择错工具描述更清晰,减少无关工具暴露
参数错类型标注、schema、校验
超时timeout、重试、降级
结果太长摘要、分页、限制返回字段
有副作用读写分离、人工确认、权限控制
难追踪记录 tool input/output/error

问:新发现是什么?

答:工具让 Agent 变强,也让它变危险。能力边界越真实,权限边界越不能靠“模型会懂事”。

模型很聪明,但它不应该当门禁管理员。

8. 第七个问题:什么时候需要 Agent?

问:有 chain 和 tools 了,为什么还要 Agent?

答:Chain 适合固定流程。Agent 适合开放流程。

固定流程像:

Code
问题 -> 检索 -> 回答 -> 解析

开放流程像:

Code
用户目标 -> 模型判断下一步 -> 调工具 -> 观察结果 -> 再判断 -> 完成

问:Agent 解决什么问题?

答:它解决“下一步不固定”的问题。

比如用户说:

帮我查一下今天上海天气,如果会下雨就提醒我带伞,再给我推荐通勤穿搭。

系统可能需要:

  1. 识别城市和日期。
  2. 调天气工具。
  3. 判断降雨概率。
  4. 调衣柜或偏好工具。
  5. 组织最终回答。

这些步骤可以写死,但任务一变,流程就变。Agent 让模型参与决策。

问:Agent 有什么风险?

答:

  • 循环过长。
  • 工具乱用。
  • 成本不可控。
  • 中间状态难追踪。
  • 错误观察导致错误行动。
  • 高风险动作需要确认。

问:什么时候不要用 Agent?

答:

  • 流程固定。
  • 输出格式严格。
  • 风险高且必须可预测。
  • 简单 chain 已经能解决。

此时硬上 Agent,像买菜开挖掘机。不是不能到菜市场,主要是停车不方便。

问:新发现是什么?

答:Agent 不是高级版 chain,而是适合不确定流程的执行 harness。能用确定性流程解决的,优先用确定性流程。

9. 第八个问题:复杂流程怎么更可控?

问:Agent loop 很灵活,但复杂任务会不会失控?

答:会。尤其当流程需要:

  • 分支。
  • 循环。
  • 人工确认。
  • 持久化状态。
  • 中断恢复。
  • 多角色协作。

单纯靠一个 Agent loop 会变得难以观察和控制。

问:这时怎么办?

答:引入 LangGraph。

LangGraph 更偏底层状态机和图编排。它适合把复杂 Agent 工作流拆成节点、边、状态和 checkpoint。

Code
State -> Node A -> Branch -> Node B / Node C -> Checkpoint -> Resume

问:LangChain 和 LangGraph 怎么分工?

答:

名称适合什么
LangChain组合模型、Prompt、工具、检索和基础 Agent harness
LangGraph构建复杂状态机、分支、循环、持久化工作流

问:新发现是什么?

答:当流程复杂到需要“看得见状态、恢复得了现场、控制得住分支”时,图比一条长链更合适。

链路适合流水线,图适合交通网。别让一条水管承担整个城市早高峰。

10. 第九个问题:线上出错怎么知道错在哪?

问:LLM 应用为什么难 debug?

答:因为错误可能出在很多地方:

  • 用户输入模糊。
  • Prompt 不清楚。
  • 检索召回错。
  • 模型输出跑偏。
  • Parser 解析失败。
  • Tool 返回异常。
  • 模型版本变化。
  • 上下文太长。
  • 结构化输出不稳定。

而最终用户只看到一句:“答案不对。”

问:需要记录什么?

答:至少要记录:

  • input。
  • prompt version。
  • model name。
  • model params。
  • retrieved docs。
  • tool calls。
  • raw output。
  • parsed output。
  • latency。
  • token usage。
  • error。

问:LangSmith 解决什么?

答:LangSmith 用于 tracing、debug、评估和监控。它让你能复现一次调用链路,比较不同 Prompt 或模型版本,做测试集评估。

问:新发现是什么?

答:没有 tracing 的 LLM 应用,出错时就像在黑箱里找钥匙。你知道钥匙在房间里,但房间还关着灯,地上还有一堆 prompt。

11. 第十个问题:并发怎么处理?

问:LangChain 应用会遇到哪些并发问题?

答:

  • 多个用户同时发起请求。
  • 批量处理大量文档或问题。
  • 流式输出和后端任务同时运行。
  • 同一个会话中多轮请求互相覆盖状态。
  • Agent 多次调用工具,耗时不可预测。
  • RAG 检索和模型生成都可能成为瓶颈。

问:Runnable 对并发有什么帮助?

答:Runnable 提供统一执行接口,例如 batch 可以处理多个输入,stream 可以流式返回结果。链路结构统一后,更容易做批处理、限流、超时和监控。

问:但 LangChain 会自动解决所有并发吗?

答:不会。框架给的是组合和执行抽象,应用仍然要设计:

  • 请求级状态隔离。
  • session id。
  • 限流。
  • 超时。
  • 幂等。
  • 重试策略。
  • 工具资源池。
  • 数据库连接池。

问:Agent 场景有什么特别问题?

答:Agent 的执行长度不固定。一次请求可能调用 0 个工具,也可能调用多个工具,还可能因为错误重试。

所以要控制:

  • 最大循环次数。
  • 最大工具调用次数。
  • 最大执行时间。
  • 最大 token 成本。
  • 高风险工具审批。

问:新发现是什么?

答:LLM 应用的并发不是“多开几个 worker”就结束了。还要管状态、工具副作用和成本边界。

否则系统不是并发高,是同时花钱高。

12. 第十一个问题:稳定性怎么做?

问:LLM 应用不稳定主要表现在哪?

答:

  • 同样输入,不同时间输出不同。
  • JSON 格式偶尔坏。
  • 检索结果偶尔不相关。
  • 工具偶尔超时。
  • 模型 provider 偶尔失败。
  • Prompt 一改,旧测试全飘。
  • 上下文过长导致截断或 overflow。

问:怎么逐步改进?

答:

不稳定点改进方式
输出格式飘结构化输出、parser、schema 校验
模型失败retry、fallback、错误分类
检索不准测试集、rerank、metadata filter、source 引用
工具失败timeout、重试、清晰错误信息
Prompt 回归prompt version、golden tests、评估集
上下文溢出截断、摘要、检索式上下文
成本失控token 记录、模型分级、缓存、限额

问:为什么测试很重要?

答:因为 LLM 应用不是“看一次回答觉得不错”就稳定。你需要一组固定问题,比较不同模型、Prompt、retriever、parser 的表现。

问:新发现是什么?

答:稳定性不是让模型永远不犯错,而是让错误可发现、可复现、可收敛。

这听起来没那么浪漫,但上线后非常让人睡得着。

13. 第十二个问题:性能问题在哪里?

问:LangChain 应用慢,通常慢在哪里?

答:

  • 模型响应慢。
  • Prompt 太长。
  • 检索慢。
  • embedding 生成慢。
  • vector store 查询慢。
  • 工具调用慢。
  • Agent 循环太多。
  • Parser 或后处理复杂。
  • 没有缓存。

问:怎么优化?

答:

性能压力优化方向
Prompt 太长减少无用上下文、摘要、压缩、top_k 调整
模型慢选择更合适模型、streaming、并行非依赖步骤
RAG 慢索引预构建、metadata filter、rerank 控制数量
embedding 慢离线索引、批处理、缓存
工具慢timeout、结果缓存、异步任务
Agent 慢限制循环、减少工具集、固定可预测子流程
成本高小模型处理简单任务,大模型处理复杂任务

问:最常见的误区是什么?

答:以为性能优化只能换更快的模型。实际上很多时候,是 prompt 塞了太多无关内容,retriever 召回了太多片段,Agent 拿了太多工具。

换句话说,别怪模型跑得慢,先看看你是不是让它背着一整个仓库去散步。

14. 第十三个问题:安全和权限怎么办?

问:LangChain 自己会保证工具安全吗?

答:不会自动替你完成所有安全治理。LangChain 可以组织 tool 和 agent,但你仍然要设计权限、审计、确认和隔离。

问:工具调用的安全风险有哪些?

答:

  • 模型误调用写操作。
  • 用户 prompt injection 诱导工具调用。
  • RAG 文档中包含恶意指令。
  • 工具参数被构造为危险输入。
  • Agent 调用外部 API 泄露敏感信息。
  • 高权限凭据暴露给不该调用的链路。

问:怎么改进?

答:

  • 工具最小权限。
  • 读写工具拆分。
  • 高风险动作人工确认。
  • 工具参数校验。
  • 对 RAG 文档做指令隔离。
  • 不把密钥放进 prompt。
  • 记录 tool call。
  • 对不同用户做权限隔离。

问:新发现是什么?

答:Prompt 可以提醒模型,但权限要靠系统控制。安全边界不能写在“请不要”里。

“请不要删除生产库”是一句话,不是访问控制。

15. 第十四个问题:版本变化为什么让人头疼?

问:为什么 LangChain 学习时经常遇到导入路径变化?

答:因为 LangChain 迭代很快,生态也拆分成多个包,例如 core、provider integrations、community integrations 等。旧教程里的导入路径可能已经不适用。

问:怎么降低版本风险?

答:

  • 优先看官方当前文档。
  • 固定依赖版本。
  • 记录 Python 版本要求。
  • 把核心 chain 封装在少数模块里。
  • 避免业务代码到处散落 provider 细节。
  • 为关键链路写测试。

问:这对学习有什么启发?

答:不要只背导入路径,要理解组件边界。路径会变,Model、Prompt、Runnable、Parser、Retriever、Tool 这些概念更稳定。

背 API 是记门牌号,理解架构是知道城市怎么走。

16. 第十五个问题:什么时候不该用 LangChain?

问:LangChain 是不是所有 LLM 应用都该用?

答:不是。

不一定需要 LangChain 的情况:

  • 只有一个短 prompt。
  • 一次性脚本。
  • 流程高度固定,用普通函数更清楚。
  • 团队还没准备好维护框架依赖。
  • 你只想快速验证一个模型 API。

问:什么时候适合引入?

答:

  • Prompt、Model、Parser 开始重复组合。
  • 需要接多个 provider。
  • 需要 RAG。
  • 需要 tools / agent。
  • 需要 structured output。
  • 需要 tracing、evaluation、fallback。
  • 团队需要统一 LLM 应用开发方式。

问:新发现是什么?

答:框架不是越早越好,也不是越晚越好。最好的时机是复杂度刚开始重复出现,而不是已经长成一团线。

17. 第十六个问题:最终架构为什么是这样?

问:能把 LangChain 的演进压缩成一张表吗?

答:可以。

阶段遇到的问题设计回应
直接调模型provider SDK 和业务逻辑耦合Model 抽象
手写 prompt字符串拼接失控Prompt Template
自由文本输出下游系统难解析Output Parser / Structured Output
多步骤调用链路组合混乱Runnable / LCEL
私有知识缺失模型不知道业务资料RAG / Retriever
只能回答不能行动需要连接外部系统Tools
流程不固定下一步需要模型决策Agent
Agent 流程复杂分支、循环、状态、恢复LangGraph
线上难排查不知道错在哪LangSmith tracing / evaluation
并发和成本批处理、流式、限流、状态隔离Runnable 执行接口 + 应用层治理
稳定性输出飘、检索错、工具失败schema、测试、retry、fallback、评估
性能上下文长、工具慢、循环多context 控制、缓存、streaming、模型分级

问:LangChain 的核心思想是什么?

答:把 LLM 应用拆成可组合组件。

Code
Input  -> Prompt  -> Model  -> Parser  -> Output

再逐步加上:

Code
RetrieverToolsAgentTracingEvaluationStateful workflow

每加一个组件,都不是为了显得高级,而是为了解决一个真实问题。

18. 复述模板

问:如果要快速介绍 LangChain,怎么说?

答:可以这样说:

Code
LangChain 是一个用于构建 LLM 应用和 Agent 应用的框架。它把模型调用、Prompt 模板、输出解析、检索、工具调用和 Agent harness 抽象成可组合组件。最小链路通常是 Prompt -> Model -> Parser;接入知识库后加入 Retriever 形成 RAG;需要模型自主选择外部能力时加入 Tools 和 Agent;当流程需要复杂状态、分支、循环和恢复时使用 LangGraph;当应用进入调试、评估、监控和生产质量管理阶段时使用 LangSmith。LangChain 的价值不是替你写业务逻辑,而是让 LLM 应用的关键环节可组合、可替换、可调试、可评估。

19. 学习路线:按问题而不是按名词学

问:学习 LangChain 应该从哪里开始?

答:按这个顺序:

  1. 先写一个普通函数版 prompt -> mock model -> parser
  2. 再换成 ChatPromptTemplate
  3. 再用 LCEL 写 prompt | model | parser
  4. 再加一个简单 retriever,做最小 RAG。
  5. 再给答案加 source id 和结构化输出。
  6. 再把一个普通函数包装成 tool。
  7. 再尝试 create_agent
  8. 再为固定测试问题做评估。
  9. 最后再考虑 LangGraph 和 LangSmith。

问:每一步要问什么?

答:

  • 这个组件解决了什么问题?
  • 如果没有它,系统会怎么坏?
  • 它的输入输出是什么?
  • 它有没有引入新风险?
  • 它能不能单独测试?

问:最重要的提醒是什么?

答:不要一上来就写复杂 Agent。先把基础链写清楚,再加 RAG,再加工具,再加 Agent。

架构像搭积木。先把底座放平,再往上搭。反过来当然也能搭,只是会比较像在桌边挑战命运。

0