LangChain系列
文章目录
- LangChain系列
- 前言
- [一、为什么需要LangChain?------ 破解原生LLM的应用困境](#一、为什么需要LangChain?—— 破解原生LLM的应用困境)
-
- [1.1 原生LLM的核心应用痛点](#1.1 原生LLM的核心应用痛点)
- [1.2 LangChain的核心价值:一站式解决所有痛点](#1.2 LangChain的核心价值:一站式解决所有痛点)
- 1.3、链式结构
- [二、LangChain 引入](#二、LangChain 引入)
-
- [2.1 LangChain Runnable 接口与 LCEL 概念整理](#2.1 LangChain Runnable 接口与 LCEL 概念整理)
- 三、聊天模型
-
- [3.1、通过 API 定义聊天模型](#3.1、通过 API 定义聊天模型)
- 3.2、聊天模型--调用工具
-
- 3.2.1、定义工具
- 3.2.2、工具的选择于调用
- [3.2.3、LangChain 提供的工具](#3.2.3、LangChain 提供的工具)
- 3.2.4、结构化输出
- 3.2.5、流式输出
- 3.3、LangSmith
前言
在大语言模型(LLM)飞速发展的今天,越来越多开发者希望将强大的AI能力嵌入实际应用,但原生LLM在复杂场景中往往面临诸多困境。LangChain作为一款专为LLM驱动应用开发设计的框架,应运而生,它像一个"连接器",将LLM与各类工具、数据无缝整合,让初学者也能快速搭建功能完善的AI应用。本文将从LangChain的核心价值、架构组成、关键技术及实操入门四个维度,带你走进LangChain的世界,轻松开启AI应用开发之路。
本片文章并不包含LangChain环境的搭建
一、为什么需要LangChain?------ 破解原生LLM的应用困境
使用过原生大模型(如GPT-3.5、GPT-4、Claude等)的开发者都有过这样的体验:单独调用LLM API能完成简单的问答、生成任务,但当需要将其嵌入实际应用时,一系列难题便会浮现,这些痛点也正是LangChain的核心解决方向。
1.1 原生LLM的核心应用痛点
- 幻觉问题显著:简单的提示词容易引发LLM生成错误信息(例如在编程场景中提供不存在的API)。
- 提示词缺乏规范:针对不同功能编写的提示词风格、质量不一,导致应用行为不可预测。
- 模型切换成本高:应用代码与特定LLM API强耦合,若从GPT-3.5切换到GPT-5,几乎需要重写所有交互代码。
- 非结构化输出难交互:LLM输出多为自然语言,无法直接与需要结构化数据的程序接口对接,需额外编写复杂解析逻辑。
- 知识陈旧且封闭:LLM训练数据有截止日期,无法获取实时信息;同时无法直接调用外部知识库,难以处理私人数据(如公司内部文档)。
1.2 LangChain的核心价值:一站式解决所有痛点
对LangChain进行简要介绍,关于如何解决LLM痛点问题将在编码实现中具体呈现。
LangChain是一个用于开发LLM驱动应用的开源框架,其核心思想是将NLP流程拆解为标准化组件(如:文本解析组件,分词组件,提示词模板组件...),通过"链式"整合,让开发者自由组合组件、定制工作流,无需从零搭建复杂系统。它就像一个"AI应用工具箱",提供了所有所需的核心模块,让开发者聚焦业务逻辑,而非底层架构。
Chain是LangChain的核心设计理念,用于将多个组件串联成完整的工作流,无需单独调用每个组件,只需执行一次链即可完成整个任务。例如,一个简单的"问答链"可串联"提示词模板"和"LLM模型",用户输入问题后,链会自动生成标准化提示词,调用LLM并返回结果;复杂的"RAG链"则可串联"文本分割""嵌入生成""向量存储""检索""LLM生成"等多个组件,实现基于外部知识库的精准问答。
如何理解链式:举个最简单的例子,如果想通过提示词向大语言模型提问,在LangChain中至少需要定义两个核心组件:1、提示词模板组件 2、大模型组件
由此可见,LangChain能够有效规范提示词的使用。
相当于提示词模板组件和大模型组件各执行了一次。而链式执行只需执行一次链即可:
1.3、链式结构
下面我们通过两个例子来简单的认识,非链式调用LLM和链式调用LLM
python
from langchain_core.messages import SystemMessage, HumanMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 1.定义一个OpenAi模型
# 如果不指定key,会默认去系统环境变量中读取
# 适配 DeepSeek 模型:指定 base_url 和模型名称
model=ChatOpenAI(
model="deepseek-v4-flash", # 你购买的 DeepSeek 模型
base_url="https://api.deepseek.com/v1", # DeepSeek 官方接口
# api_key=None # 不指定key,会默认去系统环境变量中读取 DEEPSEEK_API_KEY
)
# 2.定义消息体
# 用户消息 HumanMessage
# 系统提示消息 SystemMessage 作为第一条消息传入
message=[
SystemMessage(content="请帮我将英文翻译为中文!"),
HumanMessage(content="Hello!")
]
# 3.调用大模型
result=model.invoke(message)
print(result)
# 4.定义输出解释器并调用
parser=StrOutputParser()#把 AI 返回的复杂对象 → 自动提取成干净的字符串(纯文本)
print(parser.invoke(result))
print("------------------------------")
# 5.定义链
chain=model | parser
print(chain.invoke(message))

这个实例直观体现了LangChain中Chain的核心思想:model是大模型调用组件,parser是文本格式化组件,传统方式需要先将message交给大模型得到结果,再将结果传给parser才能得到纯文本输出;而通过|将两个组件组成链后,只需把消息交给链并调用一次chain.invoke(),就能直接拿到格式化结果,完美解决了LLM无法直接输出标准化结果的痛点,我们只需定义不同的解析组件,就能轻松获取字符串、JSON等多种格式的输出。
二、LangChain 引入
2.1 LangChain Runnable 接口与 LCEL 概念整理
Runnable 接口是使用 LangChain 各类组件的底层基础,LangChain 中绝大多数核心组件都是基于 Runnable 接口实现的。
Components(组件)概念
组件是构建 LangChain 应用程序的核心构建单元,为应用开发提供了标准化基础模块,常见包括:大语言模型、输出解析器、检索器等。
实现 Runnable 接口的组件将获得以下标准化功能:
-
单次调用(invoke)
接收单个输入,执行逻辑处理后返回对应输出
-
批处理(batch)
支持批量输入处理,高效完成批量转换并输出结果
-
流式传输(stream)
支持结果分段生成与实时返回,实现流式输出
-
信息检视(inspect)
可获取当前 Runnable 的输入/输出结构及配置元数据等原理图信息
-
组合编排(composed)
支持通过 LCEL 语法将多个 Runnable 组件自由组合,构建复杂业务处理管道
前文示例中定义的语言模型 model、输出解析器 StrOutputParser,本质都是 Runnable 接口的实现实例,二者都具备 invoke 调用能力
LCEL 与 RunnableSequence
LangChain Expression Language(LCEL)是 LangChain 的表达式语言,采用声明式编程风格,能够基于现有 Runnable 对象构建新的 Runnable 对象。通过 LCEL 组合生成的新对象称为 RunnableSequence(可运行序列),也就是我们通常所说的"链"。
关键特性:
- RunnableSequence 同样实现了 Runnable 接口,完整继承了 Runnable 的所有标准能力
- 其使用方式与普通 Runnable 组件完全一致
LCEL 本质上是一套组件编排解决方案,任意两个 Runnable 实例都可以串联形成执行序列,前一个 Runnable 的输出会自动作为下一个 Runnable 的输入进行传递。
此外,还可通过以下两种方式定义链,此处不再赘述。
cpp
chain=RunnableSequence(first=model,last=parser)
chain = model.pipe(parser)
三、聊天模型
在LangChain概念体系中,LLM多指纯文本补全模型,接口接收字符串提示词并返回字符串结果(也包含多模态输入能力),而聊天模型底层仍依托LLM能力,只是专为对话场景做了适配优化,不接收单个字符串,而是接收结构化聊天消息列表,最终返回AI消息对象。
现代LLM大多通过聊天模型接口 进行调用,该接口以消息列表作为输入、以消息对象作为输出,区别于传统纯文本交互;

3.1、通过 API 定义聊天模型
3.1.1、ChatOpenAI
langchain_openai.chat_models.base.ChatOpenAI 是 LangChain 提供的 OpenAI 聊天模型(如 GPT-5、GPT-5-mini)的具体实现类。该类继承自 langchain_openai.chat_models.base.BaseChatOpenAI,而 BaseChatOpenAI 则实现了标准的 Runnable 接口。
ChatOpenAI 常用参数说明表
| 参数名 | 示例值 | 参数描述 |
|---|---|---|
model |
deepseek-v4-flash |
要使用的模型名称,需与 API 服务支持的模型名一致。 |
temperature |
0 |
采样温度,控制输出随机性。值越高回答越发散、创意性越强;值越低回答越保守、可复现性越强。 |
max_tokens |
None |
单次请求中模型可生成的最大 token 数,None 表示使用服务端默认上限。 |
timeout |
None |
HTTP 请求超时时间(秒),None 表示使用默认超时设置。 |
max_retries |
2 |
请求失败时的最大重试次数,用于网络抖动等场景的容错处理。 |
api_key / openai_api_key |
None |
OpenAI API 密钥。如果未显式传入,会自动从环境变量 OPENAI_API_KEY 中读取。 |
base_url |
https://api.openai.com/v1 |
API 请求的基础 URL,兼容 OpenAI 协议的第三方服务(如 DeepSeek)可通过此参数指定地址。 |
organization |
None |
OpenAI 组织 ID,多组织账号场景下使用。未传入时,会从环境变量 OPENAI_ORG_ID 中读取。 |
上述字段,大家可以直接尝试
3.1.3、init_chat_model
上面我们介绍的ChatOpenAI 用于创建 OpenAI 聊天模型的实例。而 init_chat_model() 是一个工厂函数,可以初始化多种支持的聊天模型,包括但不限于 OpenAI、Anthropic 和 FireworksAI 等。
该函数返回一个与指定 model_name 和 model_provider 匹配的 BaseChatModel 实例(可以用通过打印和调试两种方式获取模型类型,例如 ChatOpenAI 或 ChatAnthropic)。如果模型支持配置,则返回一个聊天模型模拟器,该模拟器会在运行时根据传入的配置初始化底层模型。
基本用法
python
from langchain.chat_models import init_chat_model
# 1.基本用法
# 这是langchain封装的更上层的定义模型的方法
deepseek_model=init_chat_model(model="deepseek-v4-flash",model_provider="deepseek",temperature=0.5)
print(f"deepseek-v4-flash:{deepseek_model.invoke("介绍你自己").content}")
init_chat_model是LangChain封装的高层模型定义方法,只需修改参数即可轻松切换不同模型,充分体现了LangChain在模型切换方面的便捷性。
定义可配置模型
LangChain 中的可配置模型 通过 init_chat_model 预先创建仅包含通用参数(如温度)的基础模型实例,依托 invoke() 方法实现核心调用:input 用于传入文本、消息列表等实际输入内容,也支持传递 Runnable 实例实现链式组合;config 参数作为动态配置核心,可在不重新创建模型对象 的前提下,通过 configurable 动态指定模型名称、服务提供商等运行参数,真正实现一次创建、多模型灵活切换。
python
from langchain.chat_models import init_chat_model
# 2.定义可配置模型
# # 这个模型还是
config_model=init_chat_model(temperature=0.5)
print(type(config_model))
print(f"config_model:{config_model.invoke(input="你是谁",config={"configurable": {"model": "deepseek-v4-flash"}}).content}")

可以看到,定义可配置模型 时,我们可以预先创建仅包含通用参数的基础模型实例 ,得到一个聊天模型模拟器 ;在实际调用时,通过 invoke() 方法的 config 参数,就能动态指定并切换具体模型,无需重新创建模型实例。
定义可配置模型设置默认配置
python
# 3.定义可配置模型(默认值)
config_model=init_chat_model(
model="deepseek-v4-flash",
model_provider="deepseek",
temperature=0.3,
max_tokens=300,
# 允许运行时动态修改哪些字段
configurable_fields=(
"model",
"model_provider",
"temperature",
"max_tokens",
),
# 配置前缀(用来区分多组配置)
config_prefix="first"
)
print(f"config_model:{config_model.invoke(input="编一个故事").content}")
print("----------------------------------")
# 动态调用:动态修改 max_tokens 和 temperature
print(f"config_model: {config_model.invoke(input="编一个故事",config={"configurable":{
"first_max_tokens":200,
"first_temperature":1,
}}).content}")
通过 init_chat_model 定义可配置模型,能够预先创建包含通用参数的基础模型实例;借助 configurable_fields 可以明确指定允许在运行时动态修改的配置项,实现无需重建模型即可灵活调整参数的效果;搭配 config_prefix 为配置组命名,能够有效区分多套模型配置,避免冲突,最终实现一次创建、动态调整、多配置隔离的完整可配置模型能力。
3.2、聊天模型--调用工具
工具调用的核心作用,是为大语言模型(LLM)打通与外部世界的交互通道,突破其仅依赖训练数据的封闭局限,从而:
- 扩展能力边界:让模型可借助工具完成计算、联网搜索、数据库查询等自身无法实现的任务;
- 保障信息实时性:通过调用外部工具获取最新数据,避免因训练数据滞后导致的回答过时或错误;
- 支撑复杂任务处理:将用户请求拆解为多步骤操作,通过工具协同完成,这一能力在智能体(Agent)中尤为关键;
- 实现系统集成 :可对接企业现有系统与API,让LLM成为自然语言驱动的统一交互入口,提升自动化与集成效率。

3.2.1、定义工具
使用 @tool 装饰器创建工具
在 LangChain 中,我们可以通过 @tool 装饰器快速将 Python 函数封装为大模型可调用的工具,其核心依赖工具 Schema 实现模型与工具的交互:
python
from langchain.tools import tool
@tool
def add(a: int, b: int) -> int:
"""
两数相加
Args
a : int
b : int
"""
return a + b
# 调用工具,传入参数字典
print(add.invoke({"a": 1, "b": 2}))
print("----------------------")
# 工具名称
print(add.name)
print("----------------------")
# 工具描述
print(add.description)
print("----------------------")
#=工具参数
print(add.args)
- 工具定义的核心要素
- 函数名:默认作为工具名称,让模型知道可调用的工具标识。
- 类型提示 :明确参数/返回值的类型(如
int、str),为工具 Schema 提供参数类型约束。 - 文档字符串(Docstring) :(包含功能说明、
Args参数描述、Returns返回值说明),作为工具的能力描述,让模型理解工具用途与调用方式。
工具 Schema 是由函数名、类型提示和文档字符串自动生成的工具"说明书",它以标准格式向大模型明确工具的名称、功能描述与调用参数规范,帮助模型理解工具能力、生成合规的调用请求,也能校验调用参数的格式与类型,保障模型与工具间的数据交互统一,最终支撑模型通过工具调用实现与外部世界的交互。
python
def add(
a:Annotated[int,...,"第一个参数"],
b:Annotated[int,...,"第二个参数"]
) -> int:
这样可以在,获取参数时,显示注释
依赖 Pydantic 类
python
from langchain.tools import tool
from pydantic import BaseModel, Field
#Pydantic 数据模型,作用是:规定一段数据必须是什么格式、什么类型
class AddInput(BaseModel):
"""两数相加"""
a:int=Field(description="第一个整数")
b:int=Field(description="第二个整数")
@tool(args_schema=AddInput)
def add(a: int, b: int) -> int:
return a + b
print(add.invoke({"a":1,"b":2}))
print("----------------------")
print(add.name)
print("----------------------")
print(add.description)
print("----------------------")
print(add.args)
AddInput 是我们自定义的一个Pydantic模型类 ,专门用来规范工具的输入参数格式。
它继承自 Pydantic 的 BaseModel,这是一个用于数据校验和结构化定义 的基类,让这个模型具备自动校验参数类型、格式是否合法的能力。
类中的文档字符串 """两数相加""" 用于说明这个模型的用途;Field() 则为每个参数添加文字描述和规则约束,让参数含义更明确。
在 @tool 装饰器中,这个模型会作为 args_schema 参数使用。当工具函数本身没有提供足够的参数描述、文档说明时,就可以通过这个 Pydantic 模型,统一、规范地定义工具需要的输入参数结构、类型和说明信息,让工具能够正确识别和使用传入的参数。
利用 StructuredTool 类提供的功能创建工具
python
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
# 方法一
# def add(a:int,b:int) -> int :
# """"两数相加"""
# return a+b
# # 把普通 Python 函数,自动包装成 LangChain 标准结构化工具对象;
# add_tool=StructuredTool.from_function(func=add)
# 方法二
class AddIput(BaseModel):
a: int=Field(description="第一个整数")
b: int=Field(description="第二个整数")
def add(a:int,b:int) -> int :
return a+b
# 把普通 Python 函数,自动包装成 LangChain 标准结构化工具对象;
add_tool=StructuredTool.from_function(
func=add,
name="ADD",
args_schema=AddIput,
description="两数相加")
print(add_tool.invoke({"a":1,"b":2}))
print("----------------------")
print(add_tool.name)
print("----------------------")
print(add_tool.description)
print("----------------------")
print(add_tool.args)
加⼊ response_format 配置
当我们希望工具区分供大模型读取的消息内容(content)与用于分析的原始数据(artifact)时,可通过在定义工具时指定 response_format="content_and_artifact" 参数,并让工具返回 (content, artifact) 格式的元组来实现:其中 content 是结构良好、简洁的文本结果,适配大模型的文本理解与推理能力;而 artifact 是字典或列表形式的原始结构化数据,专门供链中后续组件或函数使用,既避免了非文本/庞大数据干扰大模型,又保留了完整的执行过程,方便后续问题排查与数据处理。
python
# 导入LangChain结构化工具类
from langchain_core.tools import StructuredTool
# 导入pydantic基类和字段描述工具,用于定义工具参数模型
from pydantic import BaseModel, Field
# 导入类型注解:元组、列表
from typing import Tuple, List
# 定义工具的入参结构模型,用于约束参数类型、生成参数描述、给大模型解析用
class AddIput(BaseModel):
# 定义参数a为整型,并添加参数描述文本
a: int = Field(description="第一个整数")
b: int = Field(description="第二个整数")
# 定义工具内部执行的核心业务函数
# 返回值注解:返回元组,第一项字符串、第二项整数列表
def add(a:int,b:int) -> Tuple[str,List[int]]:
# 将传入的两个数字存入列表,作为计算过程数据
nums = [a,b]
# 拼接自然语言结果文本
content = f"{nums}两数相加的结果是{a+b}"
# 返回固定格式:(文本内容, 过程数据)
return content, nums
# 将普通Python函数手动包装为LangChain标准结构化工具对象
add_tool = StructuredTool.from_function(
func=add, # 指定要包装的底层业务函数
name="ADD", # 设置工具名称,大模型通过名称识别调用该工具
args_schema=AddIput, # 绑定自定义的参数模型,做参数校验和生成描述
description="两数相加", # 工具功能描述
response_format="content_and_artifact" # 指定返回格式:分为展示内容content和过程数据artifact
)
# 模拟大模型输出的标准工具调用格式,直接传入invoke执行调用并打印结果
print(add_tool.invoke(
{
"name":"ADD", # 工具名称标识
"args":{"a":1,"b":2},# 工具需要传入的实际参数
"type":"tool_call", # 调用类型标识
"id":"111", # 本次工具调用唯一标识id
}
))
id是工具调用的唯一标识,主要作用是将大模型发出的工具调用请求与工具执行返回的结果精准绑定,尤其在大模型同时发起多个工具调用时,能通过唯一 ID 确保每个请求和对应结果不混淆,让系统清晰识别每条返回结果归属哪次调用,保证工具调用流程的准确对应与正常执行。
3.2.2、工具的选择于调用
工具调用的完整流程可以简述为以下 4 个核心步骤:
- 定义工具 :用
@tool装饰器或StructuredTool.from_function,将普通Python函数封装成大模型可识别的工具,并配置参数描述、功能说明等元信息。 - 绑定工具 :通过
model.bind_tools(tools)将工具列表注册到大模型实例,让模型知晓可用工具的名称、功能与调用格式。 - 工具选择 :大模型根据用户输入的问题,结合工具描述,判断是否需要调用工具、调用哪个工具,并生成标准的
tool_call格式请求(含工具名、参数、唯一ID)。 - 调用工具 :解析模型生成的
tool_call请求,提取参数执行工具函数,将结果(含content文本和artifact过程数据)返回给大模型,用于后续生成最终回答。
python
from langchain.tools import tool
from langchain_openai import ChatOpenAI
@tool
def add(a: int, b: int) -> int:
"""
两数相加
Args
a : int
b : int
"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""
两数想乘
Args
a : int
b : int
"""
return a * b
model=ChatOpenAI(model="deepseek-v4-flash")
# 定义工具列表,将加法、乘法工具放入列表
tools=[add,multiply]
# 将工具绑定到大模型,让大模型具备调用这些工具的能力
model_with_tool=model.bind_tools(tools)
# model_with_tool = model.bind_tools(
tools=tools,
tool_choice="any" # 强制每次调用都使用工具,无论是否与工具相关
)
# 调用绑定工具后的大模型,传入问题,大模型会自动判断是否需要调用工具、调用哪个工具、传入什么参数
print(model_with_tool.invoke("2乘3等于多少"))
message = [
HumanMessage(content="2乘3等于多少?6加6等于多少?")
]
# 模型调用,生成工具调用指令
# AIMessage:若调用了工具,result 将包含一个 tool_calls 属性,该属性提供了执行该工具所需的所有信息(上面示例例中,我们模拟大模型调用攻击提供的参数)。
ai_msg = model_with_tools.invoke(message)
# 将AI的消息(工具调用指令)加入消息列表
message.append(ai_msg)
# 遍历执行每个工具调用
for tool_call in ai_msg.tool_calls:
# 根据工具名称选择对应的工具
selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()]
# 调用工具,生成工具消息
tool_msg = selected_tool.invoke(tool_call)
# 将工具执行结果消息加入消息列表
message.append(tool_msg)
# 打印完整消息链
print(message)
# 将完整消息(含工具结果)传给模型,生成最终回答
print(model.invoke(message).content)
# 注释掉的调试代码
# tool_result = multiply.invoke(ai_msg.tool_calls[0])
# print(tool_result)
# print(model_with_tools.invoke("你是谁"))
print(model_with_tool.invoke("你是谁"))
当我们将工具与大模型绑定后,用户提问时,模型会根据问题内容智能选择是否调用工具。若问题与工具功能无关,系统将自动跳过工具调用环节。用户可通过响应内容中的特定字段来判断工具是否被实际调用。

当前演示仅展示工具选择环节,所得结果由大模型自行推测生成,并未实际调用我们提供的工具来计算用户请求。
聊天模型通常不会直接处理单个字符串输入,而是接收聊天消息(xxMessage)列表。因此,我们需要将工具返回的结果封装成ToolMessage格式,再传递给聊天模型。
3.2.3、LangChain 提供的工具
其实不用所有工具都自己手动编写,LangChain 官方已经内置了大量现成的工具与工具包,这类预制工具主要用于对接框架集成的各类第三方组件,涵盖搜索、数据库、网页浏览等场景,并且 LangChain 里的工具和工具包本质上都继承自 BaseTool 和 BaseToolkit 基类。
LangChain工具官网
TavilySearch
TavilySearch 可实现搜索功能,Tavily是专为AI打造的搜索引擎,适配智能检索与推理需求,不仅支持可编程API,还能以结构化格式返回结果,方便后续大模型推理和任务生成使用。
TavilySearch官网
想要使用这些工具,首先要去官网创建Key,这里不做详细介绍
python
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langchain_tavily import tavily_search, TavilySearch
# 定义大模型
model=ChatOpenAI(
model="deepseek-chat"
)
# 定义工具
# 最多返回 3 条最相关的搜索结果
tool=TavilySearch(max_results=3)
# 绑定工具
tools=[tool]
model_with_tools=model.bind_tools(tools=tools)
# 定义消息列表
message=[
HumanMessage("今天北京的天气怎么样?")
]
ai_msg=model_with_tools.invoke(message)
message.append(ai_msg)
# 调用工具
for tool_call in ai_msg.tool_calls:
tool_msg=tool.invoke(tool_call)
message.append(tool_msg)
# 将消息结果交给大模型
print(model.invoke(message).content)
3.2.4、结构化输出
deepseek不支持with_structured_output结构化输出,下面介绍的都是model支持的,后面会介绍langchain的输出解析器
python
from typing import Optional, List
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# 定义模型
model=ChatOpenAI(model="deepseek-v4-flash")
# pydantic对象
class Joke(BaseModel):
"""给用户讲的一个笑话"""
setup: str = Field(description="这个笑话的开头")
punchline: str = Field(description="这个笑话的妙语")
rating: Optional[int] = Field(default=None, description="从1-10分,给这个笑话评分")
class Data(BaseModel):
"""获取关于笑话的数据列表"""
jokes: List[Joke]=Field(description="存放生成的笑话")
# 返回一个Runnable,类似于支持结构化输出的model
model_with_structured = model.with_structured_output(Data)
print(model_with_structured.invoke("分别讲一个关于唱歌和跳舞的笑话"))
python
# TypedDict
class Joke(TypedDict):
"""给用户讲的一个笑话"""
setup: Annotated[str, ..., "这个笑话的开头"]
punchline: Annotated[str, ..., "这个笑话的妙语"]
rating: Annotated[Optional[int], None, "从1-10分,给这个笑话评分"]
# 返回一个Runnable,类似于支持结构化输出的model
# model_with_structured = model.with_structured_output(Data)
model_with_structured = model.with_structured_output(Joke,include_raw=True)
# 输出结构化结果的同时,返回ai分析的过程
python
json_schema = {
"title": "joke",
"description": "给⽤⼾讲⼀个笑话。",
"type": "object",
"properties": {
"setup": {
"type": "string",
"description": "这个笑话的开头",
},
"punchline": {
"type": "string",
"description": "这个笑话的妙语",
},
"rating": {
"type": "integer",
"description": "从1到10分,给这个笑话评分",
"default": None,
},
},
"required": ["setup", "punchline"],
}
structured_model = model.with_structured_output(json_schema)
result = structured_model.invoke("给我讲⼀个关于唱歌的笑话")
让LLM模型有选择的选择数据返回格式
python
class FinalResponse(BaseModel):
final_output: Union[Joke, ConversationalResponse]
structured_model = model.with_structured_output(FinalResponse)
3.2.5、流式输出
流式输出 是LLM应用里一种实时响应机制,不同于一次性返回全部内容的非流式调用:普通invoke方式要等待模型把完整回答全部生成完毕后,才一次性展示给用户,模型思考、生成耗时越久,用户等待空白时间就越长;而流式输出会让模型逐段、逐步生成内容,不用等完整响应准备好,就实时分段传输、逐步展示给用户。
stream() 同步传输
LangChain 聊天模型提供了.stream()方法来实现流式响应效果。该方法返回一个迭代器,能够实时生成输出消息块。开发者可以通过for循环逐个处理这些消息块,实现同步响应效果。
python
from langchain_openai import ChatOpenAI
model=ChatOpenAI(model="deepseek-v4-flash",temperature=1)
# 流式输出
# 返回一个迭代器产生消息块
for chunk in model.stream("帮我写一篇800字关于卡通人物GGBong的作文"):
print(chunk.content,end="|",flush=True)
astream异步输出
使用.astream()方法实现异步流式响应,该方法专为支持非阻塞工作流程设计。在异步代码中调用该方法可获得相同的实时流式处理效果。
python
async def async_astream():
async for chunk in model.astream("帮我写一篇800字关于卡通人物GGBong的作文"):
print(chunk.content, end="|", flush=True)
asyncio.run(async_astream())
使⽤ StrOutputParser 解析模型的输出
python
from typing import Iterator, List
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 组件1:聊天模型
model=ChatOpenAI(model="deepseek-v4-flash",temperature=1)
# 组件2:输出解析器(str)
parser=StrOutputParser()
# 组件3:自定生成器
def split_into_list(input:Iterator[str]) -> Iterator[List[str]]:
buffer=""
for chunk in input:
buffer += chunk
# 遇到 。 需要刷新
while "。" in buffer:
# 找到 。的位置
stop_index = buffer.index("。")
# yield 用于创造生成器
yield [buffer[:stop_index].strip()]
buffer = buffer[stop_index + 1:]
# 处理buffer最后几个字
yield [buffer.strip()]
# 定义链
chain = model | parser | split_into_list
# 返回一个迭代器,产生的消息块
for chunk in chain.stream("写一段关于爱情的歌词,需要5句话,每句话用中文句号隔开。"):
# 使用 parser,结果就是 str
print(chunk, end="|", flush=True)
聊天模型和输出解析器等组件都实现了 LangChain 的 Runnable 接口,它们都是该接口的具体实例。Runnable 接口定义了一套标准规范,使实现该接口的组件能够具备特定功能。值得注意的是,流式传输能力并非聊天模型独有的特性,而是所有实现 Runnable 接口的实例都具备的通用功能。
SSE(Server-Sent Events)是一种基于HTTP协议的轻量级单向通信技术,通过让客户端建立长连接并保持连接开放,实现服务器向客户端主动推送流式数据,客户端会自动断线重连,且数据以特定格式持续发送;而LangChain本身不定义底层传输协议,它依赖大模型服务商(如OpenAI)和Web框架(如FastAPI)提供的流式传输能力,将大模型返回的流式数据处理为AIMessageChunk对象,从而实现流式输出。
3.3、LangSmith
使用 LangSmith 追踪 LLM 应用
在基于 LangChain 开发的应用程序中,往往包含多个步骤和频繁的 LLM 调用。随着应用复杂度提升,开发者需要深入了解链式调用或代理内部的运行情况变得尤为重要。此时,LangSmith 是最佳解决方案。
作为框架无关的工具,LangSmith 既可与 langchain 和 langgraph 配合使用,也能独立运行。这个专为构建生产级 LLM 应用而设计的平台,能够提供全面的监控和评估功能。
php
LANGSMITH_TRACING="true"
LANGSMITH_API_KEY="你的 LangSmith API Key"
使用之前,需要创建Key,并配置在环境变量中

