LangChain 模型 I/O 深度解构:提示词、结构化输出与 LCEL 链式调用全解析
目录一、 提示词模板Prompts告别手动拼装的“填空题”1. 为什么不用 Python 的 f-string2. 四大核心模板类型分别用在哪A. PromptTemplate基础字符串模板B. ChatPromptTemplate聊天模板C. MessagesPlaceholder历史记忆插槽D. FewShotPromptTemplate少样本模板二、 输出解析器Output Parsers把 AI 的“废话”变成结构化数据策略一靠 Prompt 道德约束JsonOutputParser策略二降维打击——厂商原生支持与 Pydantic 校验三、 链式调用Chains 与 LCEL把零件拼成自动流水线1. 万物皆可 Runnable2. LCEL 的魔法管道符 |3. 高阶流水线车间Runnable 组合器A. 任务分发器RunnableParallel (并行分叉)B. 隐形背包RunnablePassthrough (输入传递)C. 万能转换头RunnableLambda (自定义逻辑)四、 走向实战打造强韧的企业级链条1. 挂载外置脑核添加对话历史2. 打不死的小强重试与回退机制这篇教程我们将进入 LangChain 最核心、也是最硬核的“三大件”提示词模板Prompts、输出解析器Output Parsers以及链式调用Chains/LCEL。很多新手在跑通了上一篇的基础调用后立刻会面临一个残酷的现实如果我想做一个自动爬取网页、总结摘要、并将其存入数据库的自动化工具光靠llm.invoke()是远远不够的。大模型总是喜欢在输出结果前后加一句“好的这是为您生成的 JSON...”这就足以让你的数据库入库代码全线崩溃。为了把大模型从一个“只会聊天的玩具”变成“严谨运转的流水线引擎”我们需要完整的工程化抽象。今天咱们不讲废话用最接地气的大白话把这套流水线的底层逻辑给你盘得清清楚楚。一、 提示词模板Prompts告别手动拼装的“填空题”1. 为什么不用 Python 的f-string很多初学者最开始写提示词喜欢直接用 Python 的f你好{name}来拼接字符串。在简单的单次请求中这没问题但如果你要开发一个复杂的 AI 应用这会带来一场灾难。提示词模板Prompt Template的核心思想是将 Prompt 中的“固定部分”与“可变部分”彻底分离。 它的核心价值在于复用与参数化一次定义到处复用像调用函数一样传入变量 。标准化与版本控制把所有的提示词抽离出来统一管理方便后续做 A/B 测试和提示词工程调优 。在 LangChain 的设计中所有的提示词模板都有一个标准的工作流你传入一个变量字典比如{topic: AI}模板把变量塞进占位符生成一个中间格式PromptValue最后这个格式会自动适配并发送给大模型 。2. 四大核心模板类型分别用在哪LangChain 提供了多种模板不要被一堆英文类名吓到它们分别对应着不同的业务场景 A.PromptTemplate基础字符串模板最古老、最基础的模板最终生成的是一段纯文本字符串。适用于那些不需要角色扮演的简单任务 。from langchain_core.prompts import PromptTemplate # 定义一个带占位符的模板 prompt PromptTemplate.from_template(讲一个关于{topic}的{adjective}故事) # 传入变量生成最终提示词 formatted prompt.invoke({topic:程序员, adjective:悲伤的})高阶心法partial部分变量固定有时候你的模板里有 3 个变量但有两个变量是早就确定好的只有一个变量需要用户临时输入。这时候就可以用partial来“预填充”。用partial()方法适合在程序运行中途获取到某个值后临时固定下来 。用partial_variables参数适合在代码一开头定义模板时就把团队约定的“全局常量”比如固定的输出风格写死在里面 。B.ChatPromptTemplate聊天模板这是现代大模型开发绝对的主力。因为像 GPT-4、DeepSeek 这些现代模型它们接收的不是一段长文本而是带有角色标签系统 System、人类 Human、AI的消息列表 。from langchain_core.prompts import ChatPromptTemplate # 推荐使用元组列表来极速构建 chat_prompt ChatPromptTemplate.from_messages([ (system, 你是一个精通{language}的资深架构师。), # 设定人设 (human, 请解释一下{topic}的核心原理。), # 用户提问 (ai, 好的我将用最简单的语言为您解释。), # 模拟AI历史回复 (human, {question}) # 追问 ])这种写法极为清晰system用来定规则human用来提需求ai用来做历史对话占位 。C.MessagesPlaceholder历史记忆插槽在做上下文连续聊天的机器人时用户的历史聊天记录长度是不固定的。你不能用一个简单的字符串占位符来放历史记录。MessagesPlaceholder相当于在模板里挖了一个“大黑洞”专门用来把整个列表的历史消息整体塞进去 。from langchain_core.prompts import MessagesPlaceholder prompt ChatPromptTemplate.from_messages([ (system, 你是AI助手), MessagesPlaceholder(variable_namehistory), # 给历史消息留的专属插槽 (human, {input}) ])提示在最新版本中你也可以直接用元组语法(placeholder, {history})来代替上面那行冗长的代码效果完全一样 。D.FewShotPromptTemplate少样本模板大模型有时候是个“死脑筋”你把规则写得再清楚它也会乱输出。最好的教导方式是“举个栗子”。少样本模板就是用来把“示例数据”和“用户问题”优雅地拼接在一起的 。通过提供多个输入输出的示例Few-Shot模型能瞬间领悟你的真实意图精准作答 。二、 输出解析器Output Parsers把 AI 的“废话”变成结构化数据业务代码比如 Python 或 Java是极其严谨的它们只认识 JSON 或对象。但大模型本质上是一个“顺口溜机器”它即使给出了 JSON前后也极大概率会带上“根据您的要求下面是结果”这样的寒暄废话。输出解析器就是用来扒掉这层废话外衣提取纯净数据的。目前行业内有两种主流策略策略一靠 Prompt 道德约束JsonOutputParser这是一种传统做法它会在你的提示词末尾悄悄加上一段极其严厉的指令“你必须且只能输出 JSON绝对不能包含任何其他多余的文字不能使用 Markdown 格式……” 。 这种方法通用性强任何破模型都能用。但在参数较小的模型上模型有时依然会“翻车”输出错乱格式 。关键参数避坑partial当你设置partialTrue时解析器允许“半成品”。哪怕模型的话还没说完JSON 没闭合它也会尽力把已生成的部分提取出来。这在“打字机流式输出”时非常有用 。默认partialFalse时解析器化身铁面判官。只要 JSON 缺一个逗号立刻报错抛异常绝不姑息。适合最终结果的严格校验 。策略二降维打击——厂商原生支持与 Pydantic 校验现在主流的大厂OpenAI, Gemini 等都在 API 底层直接支持了“强制结构化输出”。这已经不再是靠语言威胁模型了而是在生成机制上做限制 。在 LangChain 中你只需要掌握一招终极必杀技with_structured_output()。只要用它无论底层是哪家模型代码永远一致 。在用这个必杀技前你必须了解 Python 的神级数据校验库Pydantic。 Pydantic 允许你定义一个数据模型类它会自动进行类型校验和转换比如把字符串30自动转成整数30。如果数据不合法它会抛出详细的错误 。结合 Pydantic你的代码可以写成这样极其优雅的形态from pydantic import BaseModel, Field, field_validator # 1. 定义极其严谨的数据结构这不仅是代码约束也会被翻译给模型听 class MovieReview(BaseModel): title: str Field(description电影标题) rating: int Field(description评分, 1-10分) # 甚至可以手写复杂的自定义校验逻辑 field_validator(rating) def validate_rating(cls, v): if v 1 or v 10: raise ValueError(评分必须在1-10之间) return v # 2. 直接在模型对象上调用必杀技 structured_llm llm.with_structured_output(schemaMovieReview) # 3. 调用模型 result structured_llm.invoke(评价一下电影《黑客帝国》) # 4. 拿到的不再是字符串而是完美的 Python 对象 print(result.title) # 黑客帝国 print(result.rating) # 9原理解析with_structured_output实际上是在底层把 Pydantic 类翻译成了严格的 JSON Schema告诉大模型的底层 API“就按这个骨架生成”。拿到结果后再用 Pydantic 严丝合缝地做二次校验 。三、 链式调用Chains 与 LCEL把零件拼成自动流水线前面的 Prompt 和 Parser 都是独立的零件怎么把它们无缝衔接起来这就是 LangChain 最核心的革命性设计LCELLangChain 表达式语言。1. 万物皆可 Runnable在 LangChain 的底层逻辑中无论是提示词、大模型还是解析器它们都被强制实现了一个统一的接口Runnable可执行体。 只要你是Runnable你就必然拥有以下能力 invoke同步执行一次 。batch并发批量执行多条数据 。stream像打字机一样流式输出 。ainvoke支持 Pythonasync异步调用 。接口的完全统一让不同组件之间具备了“即插即用”的能力 。2. LCEL 的魔法管道符|你不再需要写恶心的嵌套调用代码了。LCEL 借用了 Linux 命令行的管道符|概念将上一个组件的输出直接作为下一个组件的输入 。# 这就是最经典的 LCEL 链式调用一目了然 chain prompt | model | output_parser # 链条本身也是一个 Runnable可以直接 invoke result chain.invoke({question: 什么是量子计算})第一步把{question: ...}喂给prompt生成提示文案。 第二步提示文案自动流进model得到 AI 原始回复。 第三步原始回复流进output_parser被扒皮抽筋变成纯净数据 。3. 高阶流水线车间Runnable 组合器真实的业务流比这复杂得多。LangChain 提供了几个强大的“调度员”组合器 A. 任务分发器RunnableParallel(并行分叉)如果你的输入需要同时做两件事比如一边算字符串长度一边转大写你可以用字典语法{}。在 LCEL 中只要链条里出现一个字典系统就会自动开启并发多线程处理效率极高 。# 字典语法自动编译为 RunnableParallel parallel_chain { 赏析意境: paragraph_1_chain, 赏析含义: paragraph_2_chain } | summary_chain # 两个分支并发跑完后一起汇总给 summary_chain这种设计让耗时的网络请求比如同时去两个不同的库检索资料实现了完美的并发等待 。B. 隐形背包RunnablePassthrough(输入传递)有时候第一步的输入在第二步还要用。比如 RAG知识库问答中用户问了“公司年假几天”你的第一步去查了规章制度文档。在最后交给模型时你不仅需要“规章制度文档”还需要保留最初的那个“公司年假几天”的问题。RunnablePassthrough()就像一个隐形背包它能把原始输入原封不动地传递给下一环同时允许你在旁边塞入新检索到的上下文 。C. 万能转换头RunnableLambda(自定义逻辑)如果你想在链条中插入一段自己手写的 Python 代码比如把某个字符串切片、清洗特殊字符怎么把它塞进 LCEL 呢 用RunnableLambda将你的普通函数“包装”成标准组件它就可以和prompt、model一起用|缝合了 。四、 走向实战打造强韧的企业级链条在企业级应用中我们还需要解决两个极其现实的问题记性不好和网络不稳。1. 挂载外置脑核添加对话历史纯粹的 LCEL 链是没有记忆的。用户问第二句时它早就忘了第一句。 使用RunnableWithMessageHistory你可以给这条链挂载一个数据库通过session_id区分不同用户。每次用户提问时这个组件会自动去库里把该用户的历史聊天记录挖出来塞进我们前面提到的MessagesPlaceholder专属插槽里等模型回答完它又会自动把最新的对话存入库中。完全无需你手动管理数据存取 。2. 打不死的小强重试与回退机制大模型的 API 是极其不稳定的。超时、限流、格式生成错误是家常便饭。在 LCEL 中防御机制被简化到了极致无限复活重试chain.with_retry(stop_after_attempt3)。遇到报错自动重试 3 次专治偶尔的网络抽风 。备胎计划回退primary_llm.with_fallbacks([fallback_llm])。比如主链用的是昂贵的 GPT-4o一旦 OpenAI 服务器宕机系统无缝切换到备用链比如国产的 DeepSeek进行处理保证业务永不中断 。至此从“怎么把话说清楚Prompts”到“怎么把数据抠出来Parsers”再到“怎么把它们串成自动流水线Chains”LangChain 最核心的工程化内功你已经全部掌握。接下来就是发挥想象力用这些积木去构建真正改变行业的 AI 应用了。