一、概要
在 LangChain 中,模型(Models) 是连接应用程序与各种大语言模型(LLM)的标准化组件。它通过统一的接口封装了不同提供商的 API 差异,使开发者能够以一致的方式调用、组合和扩展模型能力,而无需关心底层实现细节。
二、模型的核心作用
模型组件(Models)是 LangChain 1.0 的基础能力层,负责:
- 统一对接各类 LLM(OpenAI、Anthropic、通义千问、Llama 等)
- 提供标准化调用接口(invoke/batch/stream/ainvoke)
- 封装模型参数、流式、结构化输出、工具调用等高级能力
- 作为链(Chain)、智能体(Agent)、RAG 的底层推理单元
三、模型的三大分类
LangChain 将模型分为三类,以适应不同的应用场景:
| 类别 | 描述 | 典型类 | 输入 | 输出 |
|---|---|---|---|---|
| LLMs(纯文本模型) | 接受字符串提示并返回字符串补全的传统语言模型。 | OpenAI、Anthropic、Cohere | 字符串 | 字符串 |
| Chat Models(聊天模型) | 基于消息列表(角色:系统、用户、助手)的对话模型,通常具有更自然的交互。 | ChatOpenAI、ChatAnthropic、ChatGoogleGenerativeAI | List[BaseMessage] | AIMessage(包含内容及工具调用信息) |
| Embeddings(嵌入模型) | 将文本转换为高维向量,用于语义搜索、聚类、推荐。 | OpenAIEmbeddings、HuggingFaceEmbeddings | 字符串 | List[float](向量) |
现在做应用 95% 用 ChatModel,LLM 只用于老模型、简单补全场景。
3.1 语言模型(LLMs)
3.1.1 定义与特点
- 定义
- LLM 在 LangChain 中代表"文本补全模型"。这类模型的设计目标是根据给定的提示文本(prompt),续写最可能的后续文本。可以把它理解为:给一段文字,续写下去。
- 特点
- 输入:一个简单的字符串(prompt)。
- 输出:一个字符串(completion)。
- 无角色区分:所有输入都被视为连续的文本,没有系统、用户、助手等角色概念。
- 不原生支持对话、工具调用、结构化输出(需要自己封装)
3.1.2 最简使用示例(OpenAI 文本补全模型)
python
from langchain_openai import OpenAI
# 文本补全模型(不是聊天!)
llm = OpenAI(
model="gpt-3.5-turbo-instruct",
temperature=0,
max_tokens=256
)
# 输入是字符串
prompt = "请解释什么是大语言模型:"
# 输出是字符串
result = llm.invoke(prompt)
print(result)
3.1.3 标准接口与核心方法
在 LangChain 1.0 中,所有语言模型(无论是传统的 LLM 还是 ChatModel)都遵循统一的 Runnable 协议。这意味着它们拥有相同的基础方法签名,能够无缝集成到 LangChain Expression Language(LCEL)管道中。这种标准化的设计使得开发者可以轻松切换不同的模型提供商,而无需改变调用代码。
3.1.3.1 标准接口:BaseLLM
所有 LLM 实现都继承自 BaseLLM 抽象基类,提供统一的方法集。
| 方法 | 作用 | 同步版本 | 异步版本 |
|---|---|---|---|
| invoke | 单次调用,返回完整输出 | invoke(input) | ainvoke(input) |
| stream | 流式调用,逐个返回输出块 | stream(input) | astream(input) |
| batch | 批量调用 | batch(inputs) | abatch(inputs) |
-
特点:
- 所有方法都接受输入(字符串、消息列表或字典)并返回输出(字符串或消息)。
- 支持配置参数(如 temperature、max_tokens)通过调用时传递或使用 bind 预设。
- 内置重试、超时、回调等机制。
-
核心方法
-
invoke(prompt: str, **kwargs) -> str:给定提示,返回补全文本。
python# 简单字符串输入(底层自动转为 HumanMessage) result = llm.invoke("介绍下 LangChain 1.0 的核心特点") print("输出内容:", result.content) print("输出类型:", type(result)) # AIMessage 类型,包含 content/role 等属性输出示例:
python输出内容: LangChain 1.0统一Agent入口,基于LangGraph,支持中间件、状态管理,模块化更强,生产级能力更完善。 输出类型: <class 'langchain_core.messages.ai.AIMessage'> -
batch([prompt1, prompt2]) -> List[str]:批量处理。
python# 批量输入(列表形式) inputs = [ "LangChain 1.0 智能体的核心是什么?", "LangChain 1.0 对比 0.x 有哪些改进?" ] # 批量调用 results = llm.batch(inputs, max_concurrency=2) # max_concurrency:最大并发数 for i, res in enumerate(results): print(f"\n问题{i+1}:{inputs[i]}") print(f"回答:{res.content}")关键参数:
- max_concurrency:控制并发数,避免一次性请求过多导致 API 限流(建议设 2-5)。
- 适用场景:批量处理问答、文本生成、数据标注等。
-
stream(prompt) -> Iterator[str]:流式返回生成片段。
python# 流式调用,逐字输出 for chunk in llm.stream("用3句话介绍 LangChain 1.0"): print(chunk.content, end="", flush=True) # flush=True 确保实时输出输出效果(逐字打印):
pythonLangChain 1.0重构了Agent体系,统一create_agent入口,降低了开发门槛。 它基于LangGraph构建,原生支持状态管理、中间件等生产级特性。 同时保持了模块化设计,兼容主流大模型和第三方工具。
-
-
异步方法:
- ainvoke、abatch、astream:对应的异步版本。
-
生成方法(生成多个候选):
-
generate(prompts: List[str], **kwargs) -> LLMResult:返回包含多个生成结果的复杂对象(包括文本、token 用量等)。
pythonresult = llm.generate(["推荐一本好书"] * 3) # 生成3个候选 for generation in result.generations[0]: print(generation.text) print(result.llm_output) # 包含 token 用量等信息
-
3.1.4 核心参数分类及详解
3.1.4.1 生成控制参数
这些参数直接决定模型输出的随机性、多样性和长度。
| 参数名 | 作用 | 类型/范围 | 默认值 | 说明 |
|---|---|---|---|---|
| temperature | 控制随机性 | float (0~2) | 1.0 | 越高越随机,越低越确定 |
| top_p | 核采样,动态过滤低概率词 | float (0~1) | 1.0 | 与 temperature 通常二选一微调 |
| top_k | 限制候选词数量(部分模型支持) | int | - | 只从概率最高的 k 个词中选择 |
| max_tokens | 最大生成 token 数 | int | 模型相关 | 生成部分的最大长度,不含提示词 |
| stop | 停止序列 | str/list | None | 遇到该字符串时停止生成 |
| frequency_penalty | 频率惩罚 | float (-2~2) | 0 | 惩罚高频词,减少重复 |
| presence_penalty | 存在惩罚 | float (-2~2) | 0 | 惩罚已出现过的词,鼓励新话题 |
| logit_bias | 对数偏置 | dict | {} | 直接修改指定 token 的生成概率 |
| best_of | 候选项数(部分模型支持) | int | 1 | 生成多个候选,返回 log 概率最高的 |
| seed | 随机种子(部分模型支持) | int | None | 固定种子使结果可复现 |
| logprobs | 返回对数概率 | bool/int | False | 用于调试或置信度分析 |
-
Temperature(温度)
-
作用:控制生成结果的随机性,即模型选择下一个词时的"冒险程度"。
-
取值范围:通常 0.0 ~ 2.0(部分模型支持更高)。
-
默认值:1.0(不同模型可能略有差异)。
- 低温度(如 0.1):模型会更倾向于选择概率最高的词,输出更加确定、保守,适合事实性问答、代码生成等需要准确性的场景。
- 高温度(如 1.5):模型选择概率较低的词的几率增大,输出更加多样、有创意,但可能产生不连贯或偏离主题的内容,适合创意写作、头脑风暴。
-
示例:
python# OpenAI API 示例 response = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "讲一个关于猫的笑话"}], temperature=0.2 # 保守,可能重复常见笑话 ) -
调优建议:
- 如果希望每次输出都大致相同,使用低温度(0~0.3)。
- 如果希望有创意变化,使用中等温度(0.5~1.0)。
- 除非需要极端随机,否则不建议超过 1.5。
-
-
Top_p(核采样,Nucleus Sampling)
-
作用:动态选择概率累积和达到 p 的最小词集合,然后从中采样。与 temperature 类似,也是控制随机性,但机制不同。
-
取值范围:0.0 ~ 1.0。
-
默认值:1.0(考虑所有词)。
- top_p=0.9:只考虑概率累积和达到 90% 的最可能的词,忽略剩余的低概率词。这可以过滤掉不合理的词,同时保留一定多样性。
- top_p=0.1:只考虑概率最高的几个词,输出更加确定。
-
与 temperature 的关系:通常建议不要同时大幅调整两者------一般固定其中一个,微调另一个。实践中,top_p 常用于替代 temperature 实现更平滑的随机性控制。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "写一首关于春天的短诗"}], top_p=0.8 # 忽略概率最低的20%的词 )
-
-
Top_k
-
作用:从概率最高的 k 个词中随机选择下一个词。
-
取值范围:正整数,通常 0~100。
-
默认值:0(表示不启用,或模型默认设置)。
- top_k=40:只从概率最高的 40 个词中选择,忽略其他所有词。这可以避免生成极其罕见的词,同时保持一定多样性。
- top_k=1:等价于贪心解码(greedy decoding),总是选择概率最高的词,等同于 temperature=0。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "下一句是什么?"}], top_k=50 # 只从 top50 中选择 ) -
调优建议:top_k 通常与 temperature 或 top_p 配合使用,用于进一步限制候选范围。在需要高度可控的生成中(如代码补全),可以设置较小的 top_k。
-
-
Max Tokens(最大生成长度)
-
作用:限制模型生成的最大 token 数(包括提示词中的 token 吗?------注意:不同 API 定义可能不同。OpenAI 的 max_tokens 是指生成部分的最大 token 数,不包括提示词)。
-
取值范围:正整数,受模型上下文窗口限制(如 4096、8192、128k 等)。
-
默认值:由模型决定,通常是上下文窗口的剩余部分。
- 设置过小可能导致输出被截断。
- 设置过大可能浪费 tokens 或超出上下文限制。
- 1 英文 token ≈ 0.75 单词。
- 1 中文 token ≈ 1.5 字
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "请详细解释量子计算"}], max_tokens=500 # 最多生成 500 个 token )
-
-
Stop Sequences(停止序列)
-
作用:指定一个或多个字符串,当模型生成到这些字符串时,立即停止生成。返回的结果中不包含停止序列本身。
-
类型:字符串或字符串列表。
-
默认值:无。
- 常用于强制模型输出特定格式,如只生成 JSON 对象,当生成 } 时停止;或用于对话中,当生成 \nUser: 时停止,防止模型模拟用户。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "用 JSON 格式返回:{ \"name\": \"产品名\", \"price\": 价格 }"}], stop=["}"] # 生成到闭合括号时停止,避免额外内容 ) -
调优建议:在多轮对话生成中,常设置 stop=["\nUser:", "\nAssistant:"] 来控制角色切换。
-
-
Frequency Penalty(频率惩罚)
-
作用:根据词在已生成文本中出现的频率来惩罚该词,减少重复。
-
取值范围:-2.0 ~ 2.0。
-
默认值:0(无惩罚)。
- 0:不惩罚
- 0.1~0.5:轻微减少重复
- 1.0~2.0:几乎不重复,但可能不通顺
- 负值:鼓励重复(几乎不用)。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "写一篇关于环保的文章"}], frequency_penalty=0.5 # 减少重复词汇 ) -
效果:有助于避免模型反复使用相同的词语或短语,使文本更丰富。
-
-
Presence Penalty(存在惩罚)
-
作用:根据词是否已经出现过(不论出现次数)来惩罚该词,鼓励引入新话题。
-
取值范围:-2.0 ~ 2.0。
-
默认值:0。
- 正值:对已出现的词施加惩罚,促使模型谈论新内容。
- 负值:鼓励围绕已有话题展开。
-
与 frequency_penalty 的区别:
- frequency_penalty 惩罚出现次数多的词,次数越多惩罚越大;
- presence_penalty 只惩罚是否出现过,出现一次和多次惩罚相同。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "介绍北京的景点"}], presence_penalty=0.3 # 鼓励提及更多不同的景点 ) -
调优建议:在需要广泛覆盖内容的场景(如列举、头脑风暴)中,可适当提高 presence_penalty;在需要聚焦主题的场景,可设为 0 或负值。
-
-
Logit Bias(对数偏置)
-
作用:直接修改指定 token 的原始 logit 值(即模型生成该词的原始分数),从而影响其被选中的概率。
-
类型:字典,键为 token ID(整数),值为偏置值(-100 到 100)。
-
默认值:无。
- 正值增加该 token 出现的概率,负值降低概率。
- 可以用于强制模型输出特定词(如"是"或"否"),或屏蔽不良词汇(设置很大的负值)。
-
示例(强制模型回答"是"或"否"):
pythonimport tiktoken encoder = tiktoken.encoding_for_model("gpt-4") yes_token = encoder.encode("是")[0] no_token = encoder.encode("否")[0] response = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "苹果是水果吗?请用'是'或'否'回答。"}], logit_bias={yes_token: 50, no_token: 50}, # 增加这两个 token 的概率 max_tokens=10 ) -
注意:需要知道 token ID,通常需要借助分词器(如 tiktoken)。
-
-
Best Of(候选项数)
-
作用:生成 best_of 个候选结果,并返回其中 log 概率最高的那个(按 token 对数概率和排序)。
-
取值范围:正整数,通常 <= 20。
-
默认值:1。
- 这相当于内部做了多次生成,然后挑选最佳结果,但只输出一个。
- 可以提升输出质量,但会消耗更多 tokens(每个候选项都计费)。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "写一个吸引人的广告语"}], best_of=5 # 生成 5 条,选最佳 ) -
适用场景:需要高质量输出且愿意付出额外成本时使用。
-
-
Seed(随机种子)
-
作用:设置随机种子,使生成结果可复现。
-
取值范围:整数。
-
默认值:None(随机)。
- 设置相同的 seed,在相同模型版本、相同输入参数下,可以得到完全相同的输出(理论上是确定的,但受系统影响可能有微小差异)。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "说一个谜语"}], seed=42 ) -
注意:并非所有模型或 API 都支持 seed。OpenAI 的 ChatCompletion 支持 seed 参数(需要模型版本足够新)。
-
-
Logprobs(对数概率)
-
作用:返回生成 token 的对数概率信息,用于分析模型置信度、调试等。
-
取值范围:布尔值或整数(返回 top 几个 token 的概率)。
-
默认值:False。
- 设为 true 或指定 k 值(如 5),可以获取每个生成 token 及其候选 token 的对数概率。
-
示例:
pythonresponse = openai.ChatCompletion.create( model="gpt-4", messages=[{"role": "user", "content": "太阳从哪个方向升起?"}], logprobs=True ) print(response.choices[0].logprobs)
-
3.1.4.2 模型选择与标识参数
| 参数名 | 作用 | 示例值 | 说明 |
|---|---|---|---|
| model | 模型名称 | "gpt-4", "claude-3-opus" | 必须指定 |
| model_version | 模型版本(部分提供商支持) | "2024-05-13" | 如 OpenAI 的日期版本 |
| deployment_name | Azure 部署名称 | "my-gpt4-deploy" | Azure 专用 |
3.1.4.3 请求配置参数
| 参数名 | 作用 | 类型 | 默认值 | 说明 |
| timeout | 请求超时时间(秒) | float | 由客户端决定 | 防止长时间等待 |
| max_retries | 失败重试次数 | int | 2 | 网络错误时自动重试 |
| api_key | API 密钥 | str | 从环境变量读取 | 不同模型提供商标识可能不同 |
| base_url | 自定义 API 端点 | str | None | 用于代理或本地部署 |
| organization | OpenAI 组织 ID | str | None | OpenAI 专用 |
| user | 终端用户标识 | str | None | 用于跟踪和配额管理 |
3.1.4.4 流式与回调参数
| 参数名 | 作用 | 说明 |
|---|---|---|
| streaming | 是否启用流式输出 | bool,默认 False。在 1.0 中更推荐使用 .stream() 方法 |
| callbacks | 回调处理器列表 | 用于监听事件(如新 token、开始、结束) |
| metadata | 附加元数据 | 字典,用于追踪和日志 |
3.1.5 参数传递方式
-
在初始化时设置全局默认参数
pythonfrom langchain_openai import ChatOpenAI # 初始化时设置默认参数 model = ChatOpenAI( model="gpt-4o", temperature=0.7, max_tokens=500, timeout=30, max_retries=3, api_key="sk-..." # 也可从环境变量读取 ) # 调用时使用默认参数 response = model.invoke("讲个笑话") -
在调用时动态覆盖参数
python# 临时降低 temperature,获得更确定的回答 response = model.invoke( "中国的首都是哪里?", temperature=0.1 # 覆盖默认的 0.7 ) -
使用 bind 预设参数(LCEL 链中常用)
pythonfrom langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_template("用{style}风格写一首关于{subject}的诗") # 预设 temperature 和 max_tokens poet_chain = prompt | model.bind(temperature=0.9, max_tokens=200) result = poet_chain.invoke({"style": "古风", "subject": "月亮"}) -
在流式输出中使用参数
python# 启用流式(通过调用 .stream 自动处理) for chunk in model.stream("讲个长一点的故事", temperature=0.8): print(chunk.content, end="") -
结合回调记录参数
pythonfrom langchain_core.callbacks import StdOutCallbackHandler model.invoke( "你好", callbacks=[StdOutCallbackHandler()], metadata={"session_id": "abc123"} )
3.2 聊天模型(Chat Models)
3.2.1 定义与特点
- 定义
- 聊天模型是对底层大语言模型(如 GPT-4、Claude、Gemini 等)的一种封装,专门面向对话上下文。
- 聊天模型的输入是一组带有角色标签的消息(例如系统消息、用户消息、助手消息),输出是助手的一条回复消息。这种设计更贴合现代指令微调模型的使用方式。
- 特点
- 输入:List[BaseMessage](消息列表)
- 输出:AIMessage(可能包含文本内容或工具调用请求)
- 角色区分:通过不同的消息类型(system、user、assistant、tool)帮助模型理解上下文和意图。
- 原生支持工具调用:可以绑定工具并处理模型返回的工具调用请求。
- 与 LangChain 生态无缝集成:支持流式输出、缓存、回调、与 LangGraph 构建复杂智能体。
3.2.2 消息类型
所有消息都继承自 langchain_core.messages.BaseMessage。主要类型如下:
| 消息类型 | 角色 | 说明 |
|---|---|---|
| SystemMessage | system | 用于设定助手的行为、角色和全局指令。通常放在消息列表的开头。 |
| HumanMessage | user | 代表用户的输入(问题或指令)。 |
| AIMessage | assistant | 模型生成的回复。可能包含文本(content)或工具调用(tool_calls)。 |
| ToolMessage | tool | 工具执行后的结果。必须包含 tool_call_id 以关联到特定的工具调用。 |
消息构造示例:
python
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, ToolMessage
messages = [
SystemMessage(content="你是一个乐于助人的助手。用中文回答。"),
HumanMessage(content="今天天气怎么样?"),
AIMessage(content="抱歉,我无法获取实时天气信息,你可以告诉我你的城市,我帮你查询。"),
HumanMessage(content="北京"),
# 假设后面会调用工具,然后加入 ToolMessage
]
3.2.3 标准接口:BaseChatModel
LangChain 为所有聊天模型定义了统一的抽象基类 BaseChatModel。无论使用哪个模型提供商,调用方式保持一致。
-
核心方法
方法 说明 invoke(messages, **kwargs) -> AIMessage 同步发送消息列表,返回 AIMessage。 batch(messages_list, **kwargs) -> List[AIMessage] 批量处理多个输入。 stream(messages, **kwargs) -> Iterator[AIMessageChunk] 流式返回消息块。 ainvoke()、abatch()、astream() 对应的异步版本。 bind_tools(tools) 绑定工具列表,使模型能够请求调用这些工具。 with_structured_output(schema) 强制模型返回符合指定 schema 的结构化数据(如 Pydantic 模型)。 -
通用参数(实例化时常用)
- model:模型名称(如 "gpt-4o-mini"、"claude-3-5-sonnet-20240620")
- temperature:控制随机性(0~2,默认通常为 0.7)
- max_tokens 或 max_completion_tokens:限制生成的最大 Token 数
- timeout:请求超时时间
- max_retries:失败重试次数
- model_kwargs:传递给模型的其他参数(如 top_p、frequency_penalty)
3.2.4 模型集成
LangChain 1.0 通过各第三方集成包提供对具体模型的支持。以下是一些常见示例:
-
OpenAI
pythonfrom langchain_openai import ChatOpenAI llm = ChatOpenAI( model="gpt-4o-mini", temperature=0.7, max_tokens=1000, api_key="your-api-key" # 或通过环境变量 OPENAI_API_KEY 设置 ) -
Anthropic Claude
pythonfrom langchain_anthropic import ChatAnthropic llm = ChatAnthropic( model="claude-3-5-sonnet-20240620", temperature=0.7 ) -
Google Gemini
pythonfrom langchain_google_genai import ChatGoogleGenerativeAI llm = ChatGoogleGenerativeAI( model="gemini-1.5-pro", temperature=0.7 ) -
国产 Chat Models
python# 通义千问 from langchain_community.chat_models import ChatTongyi chat_qwen = ChatTongyi( model="qwen-max", dashscope_api_key="xxx", # 阿里云 AccessKey streaming=True ) # 文心一言 from langchain_community.chat_models import ChatERNIE chat_ernie = ChatERNIE( model="ernie-4.0", ernie_api_key="xxx", ernie_secret_key="xxx" )
3.2.5 调用方式详解
3.2.5.1 基本对话
python
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="用中文回答,保持简洁。"),
HumanMessage(content="什么是机器学习?")
]
response = llm.invoke(messages)
print(response.content) # 输出模型回复的文本
3.2.5.2 多轮对话
将历史消息和当前用户消息一起传入:
python
history = [
HumanMessage(content="你好!"),
AIMessage(content="你好!有什么可以帮助你的?"),
HumanMessage(content="我想了解 LangChain。")
]
response = llm.invoke(history)
3.2.5.3 工具调用
绑定工具后,模型可能会返回 AIMessage 并附带 tool_calls 属性。需要执行工具并将结果以 ToolMessage 返回给模型,让模型继续。
python
from langchain_core.tools import tool
@tool
def add(a: int, b: int) -> int:
"""计算两个数的和。"""
return a + b
llm_with_tools = llm.bind_tools([add])
# 第一轮:模型请求调用工具
response = llm_with_tools.invoke([HumanMessage("3加5等于多少?")])
if response.tool_calls:
# 假设只考虑第一个工具调用
tool_call = response.tool_calls[0]
tool_name = tool_call["name"]
tool_args = tool_call["args"]
tool_call_id = tool_call["id"] # 重要:用于关联 ToolMessage
# 执行工具(这里简单模拟)
if tool_name == "add":
result = add.invoke(tool_args) # 或直接调用函数
# 构造 ToolMessage 返回给模型
tool_message = ToolMessage(content=str(result), tool_call_id=tool_call_id)
# 第二轮:将工具结果和历史消息一起发送,让模型生成最终答案
final_response = llm_with_tools.invoke([
HumanMessage("3加5等于多少?"),
response, # 模型的第一轮回复(包含 tool_calls)
tool_message
])
print(final_response.content) # 输出:3加5等于8。
3.2.5.4 结构化输出
使用 with_structured_output 强制模型返回符合 Pydantic 模型或 JSON Schema 的对象。
python
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str = Field(description="人物的名字")
age: int = Field(description="人物的年龄")
structured_llm = llm.with_structured_output(Person)
person = structured_llm.invoke("鲁迅的信息")
print(person.name, person.age)
3.2.5.4 流式输出
每个 chunk 是 AIMessageChunk,支持累加合并:full_message = chunk1 + chunk2 + ...。
python
for chunk in llm.stream([HumanMessage("讲一个故事")]):
print(chunk.content, end="", flush=True)
3.2.5.5 异步调用
在异步环境(如 FastAPI)中使用 ainvoke 或 astream。
python
import asyncio
async def main():
response = await llm.ainvoke([HumanMessage("你好")])
print(response.content)
asyncio.run(main())
3.2.6 高级特性
3.2.6.1 与 LangGraph 结合
在 LangGraph 构建的智能体中,聊天模型通常作为图中的一个节点。状态中通常包含 messages 键,存储对话历史。
python
from langgraph.graph import StateGraph, MessagesState
def call_model(state: MessagesState):
# state["messages"] 是消息列表
response = llm.invoke(state["messages"])
# 将新消息追加到状态中
return {"messages": [response]}
# 构建图
graph = StateGraph(MessagesState)
graph.add_node("agent", call_model)
# ... 添加边、工具节点等
LangGraph 会自动处理消息的追加和流转,使得多轮工具调用和状态管理变得非常简单。同时,LangGraph 提供了 stream_mode="updates" 和 astream_events 来流式返回节点执行过程和 Token。
3.2.6.2 缓存
避免重复调用相同输入,节省费用和时间。
python
from langchain_core.caches import InMemoryCache
from langchain.globals import set_llm_cache
set_llm_cache(InMemoryCache())
llm.invoke([HumanMessage("你好")]) # 第一次,实际调用
llm.invoke([HumanMessage("你好")]) # 第二次,从缓存读取
3.2.6.3 回调
通过回调监听模型调用的各个阶段(开始、结束、错误、流块等)。
python
from langchain_core.callbacks import StdOutCallbackHandler
llm.invoke([HumanMessage("测试")], config={"callbacks": [StdOutCallbackHandler()]})
3.2.6.4 绑定运行时参数
使用 .bind(**kwargs) 可以在调用前绑定一些固定参数(如 response_format、stop 序列等)。
python
llm.bind(stop=["\n"]).invoke([HumanMessage("写一首诗")])
3.2.6.5 中间件系统(Middleware)
1.0 新增中间件能力,可拦截 / 修改 Chat Model 的请求 / 响应,实现日志、监控、脱敏等功能:
python
from langchain_core.middleware import BaseMiddleware
# 自定义日志中间件
class LoggingMiddleware(BaseMiddleware):
def invoke(self, input, config, next):
# 拦截输入
print(f"Chat Model 输入:{input}")
# 执行原调用
output = next(input, config)
# 拦截输出
print(f"Chat Model 输出:{output.content}")
return output
# 绑定中间件
chat_model_with_mw = chat_model.with_config({
"middleware": [LoggingMiddleware()]
})
3.2.6.6 动态模型选择
根据上下文 / 用户需求动态切换 Chat Model(成本 / 性能平衡):
python
from langchain_core.runnables import RunnableLambda
def select_chat_model(input):
"""根据问题复杂度选择模型"""
if "复杂分析" in input["question"]:
return ChatOpenAI(model="gpt-4o") # 高性能模型
else:
return ChatOpenAI(model="gpt-3.5-turbo") # 低成本模型
# 构建动态选择链
chain = RunnableLambda(select_chat_model) | (lambda llm, x: llm.invoke(x["messages"]))
3.2.6.7 统一初始化(init_chat_model)
1.0 推荐的最佳实践,简化多模型切换,降低接入成本:
python
from langchain_core.chat_models import init_chat_model
# 统一接口初始化任意 Chat Model
chat_model = init_chat_model(
model="gpt-4o",
model_provider="openai", # 厂商标识:openai/anthropic/tongyi
temperature=0,
api_key="sk-xxx",
streaming=True
)
3.2.6.8 上下文压缩
结合 trim_messages 实现上下文窗口管理,防止超限:
python
from langchain_core.messages import trim_messages
# 压缩上下文到 1000 token 以内
trimmer = trim_messages(
max_tokens=1000,
strategy="last", # 保留最后 N 条消息
token_counter=chat_model
)
# 压缩后的消息列表
trimmed_messages = trimmer.invoke(messages)
3.3 嵌入模型(Embeddings)
3.3.1 定义与作用
- 定义
- 嵌入模型是一种将文本(如单词、句子、段落)映射到固定长度数值向量(例如 384 维、768 维、1536 维)的模型。这些向量试图保留原始文本的语义信息,使得语义相似的文本在向量空间中的距离(如余弦相似度)较近。
- 作用
- 语义搜索:将查询和文档都转换为向量,通过向量相似度检索最相关的文档。
- 聚类:对文本向量进行聚类,发现主题分组。
- 分类:将向量作为特征输入机器学习模型。
- 推荐系统:基于用户行为或内容向量进行相似推荐。
- 异常检测:识别与其他文本向量差异较大的文本。
- 设计思想
- Query 与 Document 分离,很多 RAG 效果差,就是因为混用了 query/doc。
- 向量库无关性,Embedding 只管生成向量,不负责存储。
3.3.2 统一接口(所有模型一模一样)
LangChain 为所有嵌入模型定义了统一的抽象基类 Embeddings(位于 langchain_core.embeddings)。该接口包含两个核心方法:
-
embed_documents(texts: List[str]) -> List[List[float]]
将一批文档(多个文本)转换为向量列表。通常用于索引阶段,将知识库文档批量向量化。
-
embed_query(text: str) -> List[float]
将单个查询文本转换为向量。通常用于检索阶段,将用户问题向量化,以便与文档向量进行相似度比较。
异步方法:
- aembed_documents:异步批量嵌入文档。
- aembed_query:异步嵌入单个查询。
示例:
python
# 1. 初始化 embedding 模型
embeddings = SomeEmbeddingModel(...)
# 2. 生成查询向量
query_embedding = embeddings.embed_query("什么是RAG")
# 3. 生成文档向量
doc_embeddings = embeddings.embed_documents([
"RAG是检索增强生成",
"Embedding是把文本转向量"
])
3.3.3 核心参数
3.3.3.1 模型选择参数
| 参数名 | 作用 | 示例值 | 说明 |
|---|---|---|---|
| model / model_name | 指定嵌入模型 | "text-embedding-3-small" | 不同提供商命名可能不同(如 OpenAI 用 model,HuggingFace 用 model_name)。 |
| model_kwargs | 传递给底层模型的额外参数 | {"truncate": True} | 用于控制特定模型的行为,如是否截断长文本。 |
3.3.3.2 请求与网络参数
| 参数名 | 作用 | 默认值 | 说明 |
|---|---|---|---|
| batch_size | 批量嵌入时每批处理的文档数 | 取决于实现 | 控制内存和请求次数,过大可能导致超时。 |
| timeout | 请求超时时间(秒) | 由客户端决定 | 防止长时间等待。 |
| max_retries | 失败重试次数 | 2~3 | 网络错误时自动重试。 |
| show_progress_bar | 是否显示进度条(HuggingFace 等) | False | 用于长文档批量嵌入时的可视化。 |
3.3.3.3 认证与端点参数
| 参数名 | 作用 | 示例值 | 说明 |
|---|---|---|---|
| api_key | API 密钥 | "sk-..." | 从环境变量读取为佳。 |
| base_url / endpoint | 自定义 API 地址 | "http://localhost:8080" | 用于代理或本地部署。 |
| organization | OpenAI 组织 ID | "org-123" | OpenAI 专用。 |
| deployment | Azure 部署名称 | "my-embedding-deploy" | Azure OpenAI 专用。 |
3.3.3.4 输出维度参数
| 参数名 | 作用 | 示例值 | 说明 |
|---|---|---|---|
| dimensions | 输出向量维度 | 1536 | 部分模型(如 OpenAI text-embedding-3-*)支持降维输出,减少存储成本。 |
| embedding_ctx_length | 模型支持的最大输入 token 数 | 8191 | 超过此长度需截断或分块。 |
3.3.3.5 特定提供商参数
不同提供商有特有参数,LangChain 通过各集成包封装。
| 提供商 | 特有参数 | 作用 |
|---|---|---|
| OpenAI | encoding_format | 返回向量的格式,如 "float" 或 "base64"。 |
| Cohere | input_type | 指定输入类型(search_query、search_document 等),优化检索效果。 |
| HuggingFace | device | 指定运行设备(cpu、cuda)。 |
| HuggingFace | normalize_embeddings | 是否对向量归一化。 |
3.3.4 Embeddings 三大类
3.3.4.1 云端闭源 Embedding(最常用、最稳定)
- OpenAI Embeddings
- Azure OpenAI
- Anthropic Embeddings
- 通义千问 Embedding
- 文心一言 Embedding
- 讯飞星火 Embedding
优点: 不用管部署、精度高、稳定
缺点: 收费、有网络依赖
3.3.4.2 开源本地 Embedding(私有化首选)
- sentence-transformers 系列(bge、m3e、gte)
- Llama 2 / Mistral 做 embedding
- 本地量化 GGUF 模型
优点: 隐私、免费、内网可用
缺点: 需要 GPU / 内存、精度略低
3.3.4.3 自定义 Embedding
- 自己实现 BaseEmbeddings 即可接入任何模型。
3.3.5 最常用 Embedding 模型
LangChain 1.0 通过各个集成包支持多种嵌入模型提供商。以下是一些常见的示例:
-
OpenAI
pythonfrom langchain_openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings( model="text-embedding-3-small", # 或 "text-embedding-3-large", "text-embedding-ada-002" dimensions=1536, # 对于 v3 模型可以指定维度 api_key="your-api-key" ) -
通义千问
pythonfrom langchain_community.embeddings import ChatTongyiEmbeddings embeddings = ChatTongyiEmbeddings( model="text-embedding-v1", dashscope_api_key="..." ) -
文心一言
pythonfrom langchain_community.embeddings import ERNIEEmbeddings embeddings = ERNIEEmbeddings( model="ernie-embedding-v1", ernie_api_key="...", ernie_secret_key="..." ) -
Cohere
pythonfrom langchain_cohere import CohereEmbeddings embeddings = CohereEmbeddings( model="embed-english-v3.0", cohere_api_key="your-api-key" ) -
本地开源
pythonfrom langchain_community.embeddings import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings( model_name="BAAI/bge-small-zh-v1.5", # 中文最强轻量 model_kwargs={"device": "cpu"}, # cpu / cuda encode_kwargs={"normalize_embeddings": True} )
3.3.6 基本使用方法
-
同步嵌入文档
pythondocuments = ["今天天气真好", "机器学习是人工智能的一个分支", "苹果是一种水果"] doc_vectors = embeddings.embed_documents(documents) print(len(doc_vectors)) # 3 print(len(doc_vectors[0])) # 例如 1536(取决于模型) -
同步嵌入查询
pythonquery = "今天适合出去玩吗" query_vector = embeddings.embed_query(query) print(len(query_vector)) # 与文档向量维度相同 -
异步批量处理
pythonimport asyncio async def main(): doc_vectors = await embeddings.aembed_documents(documents) query_vector = await embeddings.aembed_query(query) asyncio.run(main()) -
与向量存储结合
嵌入模型通常与向量数据库(如 Chroma、FAISS、Pinecone、Weaviate、Qdrant 等)一起使用,构成 RAG 应用的基础。
示例:使用 Chroma 和 OpenAI 嵌入构建检索器
pythonfrom langchain_chroma import Chroma from langchain_openai import OpenAIEmbeddings # 初始化嵌入模型 embeddings = OpenAIEmbeddings() # 创建向量存储(自动嵌入文档) vectorstore = Chroma.from_texts( texts=["文档1内容", "文档2内容", "文档3内容"], embedding=embeddings, persist_directory="./chroma_db" ) # 创建检索器 retriever = vectorstore.as_retriever(search_kwargs={"k": 2}) # 检索与查询相似的文档 results = retriever.invoke("查询问题") -
在 LCEL 链中使用
pythonfrom langchain_core.runnables import RunnablePassthrough retriever = vectorstore.as_retriever() rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm )
3.3.7 最佳实践
- 区分文档和查询嵌入:如果模型支持,利用 embed_documents 和 embed_query 的差异化处理提升检索效果。
- 控制批大小:根据 API 限制和内存设置合理的 chunk_size。
- 使用缓存:对静态文档集合启用嵌入缓存,避免重复计算,降低成本和延迟。
- 选择合适的模型:根据语言、精度、成本、维度需求选择嵌入模型。
- 归一化:如果使用余弦相似度,确保向量已归一化(许多模型默认输出归一化向量,或在 encode_kwargs 中设置)。
- 监控 Token 消耗:对于付费 API,监控嵌入调用的 Token 使用量(部分集成返回 token 用量信息)。