LangChain教程(一):提示词工程与任务编排

前言:本文涉及到的所有代码均可从LangChain教程中获取

一、LangChain组件介绍与快速入门

1. Langchain简介

LangChain 是一个开源的 Python AI 应用开发框架, 它提供了构建基于大模型的 AI 应用所需的模块和工具。通过 LangChain, 开发者可以轻松地与大型语言模型 (LLM) 集成, 完成文本生成、问答、翻译、对话等任务。LangChain 降低了 AI 应用开发的门槛, 让任何人都可以基于 LLM 构建属于自己的创意应用。

2. LangChain 特性

(1). LLM 和提示(Prompt)

  • API 抽象与统一接口
    LangChain 通过 Models 组件(如 LLMsChat ModelsEmbeddings Models)为不同大模型(如 OpenAI、DeepSeek、阿里通义等)提供统一接口。开发者无需关心底层 API 差异,只需通过配置即可切换模型。例如,使用阿里通义模型时,只需设置 DASHSCOPE_API_KEY 并调用 ChatTongyi 类。
  • Prompt 模板管理
    LangChain 的 PromptTemplate 支持动态变量插入与多模板组合。例如,通过 PipelinePromptTemplate 可将系统角色、示例对话和用户输入拼接为完整提示,提升代码复用性。此外,ExampleSelectors 会根据输入长度或语义相似度动态选择示例,优化 Token 使用。

(2). 链(Chain)

  • 预置链与自定义链
    LangChain 内置了常见的任务链(如问答链、摘要链),也支持通过 BaseChain 自定义逻辑。例如,create_sql_query_chain 可以将自然语言转换为 SQL 语句,再通过 QuerySQLDataBaseTool 执行查询,最终用自然语言解释结果。这种链式设计将复杂流程分解为可组合的步骤,适合构建端到端应用。
  • 链的灵活性
    开发者可通过 SimpleSequentialChain 串联多个链,每个步骤可使用不同模型或工具。例如,先调用 LLM 解析用户意图,再调用搜索 API 获取数据,最后生成响应。

(3). LCEL(LangChain Expression Language)

  • 工作流编排
    LCEL 通过声明式语法定义 AI 任务流程。例如,使用 RunnablePassthrough 传递上下文,结合 | 运算符连接组件(如提示模板、模型调用、输出解析器),实现流水线处理。LCEL 还支持异步、流式处理和错误重试等功能。

(4). 数据增强生成(RAG)

  • 解决 Token 限制与数据实时性
    LangChain 通过 Document Loaders 加载外部数据(如 PDF、网页),使用 Text Splitters 分割长文本,再通过 Embeddings 模型向量化后存入向量数据库(如 FAISS、Chroma)。查询时,检索相关段落注入提示词,增强模型回答的准确性与时效性。

(5). Agents

  • LLM 作为决策引擎
    Agent 将 LLM 作为“大脑”,根据用户目标自主调用工具(如计算器、API、数据库)完成不同的工作。例如,Pandas Agent 可解析自然语言指令,生成 Pandas 代码操作 DataFrame,而 SQL Agent 能将“统计 7 月 APP 渠道访客数”的指令转换为 SQL 查询并执行。
  • 动态决策流程
    Agent 通过 ReAct 框架(Reasoning and Acting)循环执行“思考-行动-观察”步骤,直至完成任务。例如,用户提问“明天天气如何?”时,Agent 可能先调用天气 API,再结合结果生成回复。

(6). 模型记忆(Memory)

  • 上下文管理
    LangChain 提供多种记忆组件:ConversationBufferMemory 存储完整对话历史,ConversationSummaryMemory 压缩历史为摘要,EntityMemory 提取关键实体。这些组件可集成到链或 Agent 中,支持多轮对话场景。

3. LangChain 框架组成

LangChain框架组成

LangChain 框架由几个部分组成,包括

  • LangChain 库:Python 和 JavaScript 库。包含接口和集成多种组件的运行时基础,以及现成的链和代理的实现。
  • LangChain 模板:Langchain 官方提供的一些 AI 任务模板。
  • LangServe:通过 REST API 部署链,支持快速集成到现有系统。例如,将 SQL 查询链发布为 API,供前端调用。
  • LangSmith:提供全生命周期管理,包括:调试时追踪链的执行步骤,生产环境监控性能与异常,还可以通过评估工具优化提示词。

4. LangChain 库 (Libraries)

LangChain 库本身由几个不同的包组成。

  • langchain-core:基础抽象和 LangChain 表达语言。

  • langchain-community:第三方集成,主要包括 langchain 集成的第三方组件。

  • langchain:主要包括链 (chain)、代理(agent) 和检索策略。

5. langchain 任务处理流程

LangChain处理流程

如上图,langChain 提供一套提示词模板 (prompt template) 管理工具,负责处理提示词,然后传递给大模型处理,最后处理大模型返回的结果,LangChain 对大模型的封装主要包括 LLM 和 Chat Model 两种类型。

  • LLM - 问答模型,模型接收一个文本输入,然后返回一个文本结果。

  • Chat Model - 对话模型,接收一组对话消息,然后返回对话消息,类似聊天消息一样。

6. LangChain核心概念

  1. LLMs:LangChain 封装的基础模型,模型接收一个文本输入,然后返回一个文本结果。

  2. Chat Models:聊天模型(或者成为对话模型),与 LLMs 不同,这些模型专为对话场景而设计。模型可以接收一组对话消息,然后返回对话消息,类似聊天消息一样。

  3. 消息(Message):指的是聊天模型(Chat Models)的消息内容,消息类型包括包括 HumanMessage、AIMessage、SystemMessage、FunctionMessage 和 ToolMessage 等多种类型的消息。

  4. 提示 (prompts) :LangChain 封装了一组专门用于提示词 (prompts) 管理的工具类,方便我们格式化提示词 (prompts) 内容。

  5. 输出解析器 (Output Parsers) :如上图介绍,Langchain 接受大模型 (llm) 返回的文本内容之后,可以使用专门的输出解析器对文本内容进行格式化,例如解析 json、或者将 llm 输出的内容转成 python 对象。

  6. Retrievers :为方便我们将私有数据导入到大模型(LLM), 提高模型回答问题的质量,LangChain 封装了检索框架 (Retrievers),方便我们加载文档数据、切割文档数据、存储和检索文档数据。

  7. 向量存储 (Vector stores) :为支持私有数据的语义相似搜索,langchain 支持多种向量数据库。

  8. Agents :智能体 (Agents),通常指的是以大模型(LLM)作为决策引擎,根据用户输入的任务,自动调用外部系统、硬件设备共同完成用户的任务,是一种以大模型(LLM)为核心的应用设计模式。

7. LangChain应用场景

  • 对话机器人: 构建智能的对话助手、客服机器人、聊天机器人等。

  • 知识库问答: 结合知识图谱, 进行开放域问题的问答服务。

  • 智能写作: 如文章写作、创意写作、文本摘要等

8. LangChain快速入门

安装LangChain

我们可以使用Pip和Conda安装LangChain相关依赖。以下是LangChain相关库以及后续学习时所需要的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
pip install langchain
pip install langchain-openai
pip install langchain-community
pip install chromadb
pip install defusedxml
pip install wikipedia
pip install faiss-cpu
pip install langchain_chroma
pip install toml
pip install streamlit==1.39.0
pip install -U langgraph
pip install python-dotenv
初始化模型

在使用LangChain之前,需要导入LangChain x OpenAI集成包,并设置deepseek的API密钥作为环境变量或直接传递给OpenAI LLM类。

首先,我们需要先获取deepseek密钥,可以通过创建账户并访问此链接来获取。然后,可以将API密钥设置为环境变量,方法如下,首先在项目的根目录中创建.env文件,然后添加如下信息:

1
2
DEEPSEEK_API_KEY=your_api_key
DEEPSEEK_BASE_URL=https://api.deepseek.com

接下来,初始化模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI

# 加载 .env 文件中的环境变量
load_dotenv()

llm = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)
使用LLM

使用LLM来回答问题非常简单。可以直接调用LLM的invoke方法,并传入问题作为参数。此外,还可以通过提示模板(prompt template)生成提示词,用于向模型(LLM)发送指令。

下面演示了如何构建一个简单的LLM链(chains):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langchain_core.prompts import ChatPromptTemplate

# 创建一个提示模板(prompt template)
# 这里以对话模型的消息格式为例子,不熟悉openai对话模型消息格式,建议先学习OpenAI的API教程
# 下面消息模板,定义两条消息,system消息告诉模型扮演什么角色,user消息代表用户输入的问题,这里用了一个占位符{input} 代表接受一个模版参数input。
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个著名的宋词研究学者"),
("user", "{input}")
])

# 基于LCEL 表达式构建LLM链,lcel语法类似linux的pipeline语法,从左到右按顺序执行
# 下面编排了一个简单的工作流,首先执行prompt完成提示词模板(prompt template)格式化处理, 然后将格式化后的prompt传递给llm模型执行,最终返回llm执行结果。
chain = prompt | llm

# 调用LLM链并设置模板参数input, invoke会把调用参数传递给prompt提示模板,开始chain定义的步骤开始逐步执行。
result = chain.invoke({"input": "以怀才不遇,壮志难酬为主题写一首词牌名为”水调歌头“的词"})
print(result)

模型响应如下,可见deepseek相较于国产的其他模型来说还是非常强大的:

1
content='《水调歌头·书愤》\n\n长剑倚天啸,孤影立寒秋。\n十年磨尽霜刃,空负少年头。\n欲挽银河洗甲,却叹冯唐易老,壮志几时酬?\n醉眼望星斗,清泪落吴钩。\n\n匣中鸣,弦上恨,总难休。\n男儿意气,何日能破玉门囚?\n纵使封侯无分,也要昆仑勒石,浩气贯神州。\n莫道书生拙,风雨会中流。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 112, 'prompt_tokens': 32, 'total_tokens': 144, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 32}, 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_3d5141a69a_prod0225', 'id': 'b097c427-a719-4deb-a642-ae719bc339a9', 'finish_reason': 'stop', 'logprobs': None} id='run-6f7de997-5dc6-43d2-baa0-45924f48e226-0' usage_metadata={'input_tokens': 32, 'output_tokens': 112, 'total_tokens': 144, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
输出转换

LLM的输出通常是一条消息,为了更方便处理结果,可以将消息转换为字符串。下面展示如何将LLM的输出消息转换为字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import os
from dotenv import load_dotenv
# 引入langchain聊天场景的提示词模版
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

load_dotenv()

llm = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)

# 根据message生成提示词模版
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个著名的宋词研究学者"),
("user", "{input}")
])
# 创建一个字符串输出解析器
output_parser = StrOutputParser()

# 通过langchain的链式调用,生成一个chain
chain = prompt | llm | output_parser

result = chain.invoke({"input": "以怀才不遇,壮志难酬为主题写一首词牌名为”水调歌头“的词"})
print(result)
'''
《水调歌头·书愤》

长剑倚天啸,孤影对残灯。
十年磨就霜刃,无处试锋棱。
欲驾长鲸碧海,却困蓬蒿荻苇,
风雨暗相惊。
醉眼挑灯看,匣底作龙鸣。

射斗牛,光焰动,为谁明?
男儿意气,空负燕颔虎头形。
纵使封侯有骨,无奈时乖命蹇,
白发已丛生。
且尽杯中物,卧听晚潮声。

注:本词通过"长剑"、"霜刃"等意象,表现志士的英武气概;以"困蓬蒿"、"白发丛生"等语,道出英雄失路的悲愤。下阕连用"射斗牛"、"燕颔虎头"典故,强化怀才不遇主题。结句"卧听晚潮"以景结情,留下苍凉余韵。全词跌宕起伏,符合词牌声情特点。
'''

通过上面短短十几行的代码我们就可以利用LangChain框架让大模型给我们“打工了”,可见解决Langchain使用大模型还是非常简单的。

二、LangChain提示词工程应用实践

1. 什么是提示词模板?

上面也有介绍,语言模型以文本作为输入 - 这个文本通常被称为提示词(prompt)。在开发过程中,对于提示词通常不能直接硬编码,不利于提示词管理,而是通过提示词模板进行维护,类似开发过程中遇到的短信模板、邮件模板等等。提示词模板本质上跟平时大家使用的邮件模板、短信模板没什么区别,就是一个字符串模板,模板可以包含一组模板参数,通过改变参数值可以替换模板对应的参数。

一个提示词模板可以包含以下内容:

  • 发给大语言模型(LLM)的指令。

  • 一组问答示例,以提醒AI以什么格式返回请求。

  • 发给语言模型的问题。

2. 如何创建提示词模板(prompt template)

创建一个普通提示词模板

我们可以使用 PromptTemplate 类创建简单的提示词。提示词模板可以内嵌任意数量的模板参数,然后通过参数值格式化模板内容。

1
2
3
4
5
6
7
8
9
10
from langchain.prompts import PromptTemplate

# 定义一个提示模板,包含adjective和content两个模板变量,模板变量使用{}包括起来
prompt_template = PromptTemplate.from_template(
"给我讲一个关于{content}的{adjective}笑话。"
)

# 通过模板参数格式化提示模板
result = prompt_template.format(content="书生求学", adjective="冷")
print(result)

模板输出结果:

1
给我讲一个关于书生求学的冷笑话。
创建聊天消息提示词模板

聊天模型(Chat Model)以聊天消息列表作为输入,这个聊天消息列表的消息内容也可以通过提示词模板进行管理。这些聊天消息与原始字符串不同,因为每个消息都与“角色(role)”相关联。

例如,在OpenAI的Chat Completion API中,Openai的聊天模型,给不同的聊天消息定义了三种角色类型分别是助手(assistant)、人类(human)或系统(system)角色:

  • 助手(Assistant) 消息指的是当前消息是AI回答的内容

  • 人类(user)消息指的是你发给AI的内容

  • 系统(system)消息通常是用来给AI身份进行描述。

创建聊天消息模板例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#导入langchain提示词模版库
from langchain_core.prompts import ChatPromptTemplate

# 通过一个消息数组创建聊天消息模板
# 数组每一个元素代表一条消息,每个消息元组,第一个元素代表消息角色(也成为消息类型),第二个元素代表消息内容。
# 消息角色:system代表系统消息、human代表人类消息,ai代表LLM返回的消息内容
# 下面消息定义了2个模板参数name和user_input
chat_template = ChatPromptTemplate.from_messages(
[
("system", "你是一名人工智能助手,你的名字是{name}。"),
("human", "你好"),
("ai", "我很好,谢谢!"),
("human", "{user_input}"),
]
)

# 通过模板参数格式化模板内容
messages = chat_template.format_messages(name="SmallScholar", user_input="你的名字叫什么?")
print(messages)
# [SystemMessage(content='你是一名人工智能助手,你的名字是SmallScholar。', additional_kwargs={}, response_metadata={}), HumanMessage(content='你好', additional_kwargs={}, response_metadata={}), AIMessage(content='我很好,谢谢!', additional_kwargs={}, response_metadata={}), HumanMessage(content='你的名字叫什么?', additional_kwargs={}, response_metadata={})]

另外一种消息格式例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain.prompts import HumanMessagePromptTemplate
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate

# 使用langchain定义的SystemMessage、HumanMessagePromptTemplate等工具类定义消息,跟前面的例子类似,下面定义了两条消息
chat_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=(
"你是一个乐于助人的助手"
)
),
HumanMessagePromptTemplate.from_template("{text}"),
]
)

# 使用模板参数格式化模板
messages = chat_template.format_messages(text="书生喜欢古诗词")
print(messages)
# [SystemMessage(content='你是一个乐于助人的助手,可以润色内容,使其看起来起来更简单易读。', additional_kwargs={}, response_metadata={}), HumanMessage(content='我不喜欢吃好吃的东西', additional_kwargs={}, response_metadata={})]

通常我们不会直接使用format_messages函数格式化提示模板(prompt templae)内容, 而是交给Langchain框架自动处理。

在创建模板时使用MessagesPlaceholder

这个提示模板负责在特定位置添加消息列表。 在上面的 ChatPromptTemplate 中,我们看到了如何格式化两条消息,每条消息都是一个字符串。 但是,如果我们希望用户传入一个消息列表,我们将其插入到特定位置,该怎么办? 这就是 MessagesPlaceholder 的作用。

1
2
3
4
5
6
7
8
9
10
11
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, SystemMessage

prompt_template = ChatPromptTemplate.from_messages([
("system", "你是宋书生的人工智能助手"),
#可以传入一组消息
MessagesPlaceholder("msgs")
])
result = prompt_template.invoke({"msgs": [HumanMessage(content="你好!"),HumanMessage(content="你好,我是宋书生!")]})
print(result)
#messages=[SystemMessage(content='你是宋书生的人工智能助手', additional_kwargs={}, response_metadata={}), HumanMessage(content='你好!', additional_kwargs={}, response_metadata={}), HumanMessage(content='你好,我是宋书生!', additional_kwargs={}, response_metadata={})]

以上代码将生成两条消息,第一条是系统消息,第二条是我们传入的 HumanMessage。 如果我们传入了 5 条消息,那么总共会生成 6 条消息(系统消息加上传入的 5 条消息)。 这对于将一系列消息插入到特定位置非常有用。 另一种实现相同效果的替代方法是,不直接使用 MessagesPlaceholder 类,而是:

1
2
3
4
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是宋书生的人工智能助手"),
("placeholder", "{msgs}") # <-- 这是更改的部分
])
提示词追加示例

提示词中包含交互样本的作用是为了帮助模型更好地理解用户的意图,从而更好地回答问题或执行任务。小样本提示模板是指使用一组少量的示例来指导模型处理新的输入。这些示例可以用来训练模型,以便模型可以更好地理解和回答类似的问题。

例子:

1
2
3
4
5
6
7
8
Q: 什么是宋书生?
A: 宋书生是一个励志成为精通LLM的愣头青。

Q: 什么是宋铁柱?
A: 未知。

Q: 什么是语言模型?
A:

告诉模型根据,Q是问题,A是答案,按这种格式进行问答交互。

下面讲解的就是Lanchain针对在提示词中插入少量交互样本提供的工具类。

3. 使用示例集

创建示例集

下面定义一个examples示例数组,里面包含一组问答样例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate


examples = [
{
"question": "珠穆朗玛峰和富士山哪一座山的首次登顶时间更早?",
"answer":
"""
这里需要跟进问题吗:是的。
跟进:珠穆朗玛峰的首次登顶时间是什么时候?
中间答案:珠穆朗玛峰于1953年5月29日首次被登顶。
跟进:富士山的首次登顶时间是什么时候?
中间答案:富士山在公元663年已有朝圣者登顶记录。
所以最终答案是:富士山
"""
},
{
"question": "特斯拉和SpaceX的CEO是否曾就读于同一所大学?",
"answer":
"""
这里需要跟进问题吗:是的。
跟进:特斯拉的CEO是谁?
中间答案:特斯拉的CEO是Elon Musk。
跟进:Elon Musk曾就读于哪所大学?
中间答案:他曾在宾夕法尼亚大学学习。
跟进:SpaceX的CEO是谁?
中间答案:SpaceX的CEO也是Elon Musk。
所以最终答案是:是
"""
},
{
"question": "《哈利波特》系列和《魔戒》系列的作者是否都出生于英国?",
"answer":
"""
这里需要跟进问题吗:是的。
跟进:《哈利波特》的作者是谁?
中间答案:J.K. Rowling。
跟进:J.K. Rowling的出生地是哪里?
中间答案:她出生于英国格洛斯特郡。
跟进:《魔戒》的作者是谁?
中间答案:J.R.R. Tolkien。
跟进:J.R.R. Tolkien的出生地是哪里?
中间答案:他出生于南非奥兰治自由邦。
所以最终答案是:不是
"""
}
]
创建小样本示例的格式化程序

通过PromptTemplate对象,我们可以非常简单的在提示词模板中插入样例。

1
2
3
4
example_prompt = PromptTemplate(input_variables=["question", "answer"], template="问题:{question}\\n{answer}")

# 提取examples示例集合的一个示例的内容,用于格式化模板内容
print(example_prompt.format(**examples[0]))

返回:

1
2
3
4
5
6
7
问题:珠穆朗玛峰和富士山哪一座山的首次登顶时间更早?\n
这里需要跟进问题吗:是的。
跟进:珠穆朗玛峰的首次登顶时间是什么时候?
中间答案:珠穆朗玛峰于1953529日首次被登顶。
跟进:富士山的首次登顶时间是什么时候?
中间答案:富士山在公元663年已有朝圣者登顶记录。
所以最终答案是:富士山
将示例和格式化程序提供给FewShotPromptTemplate

通过FewShotPromptTemplate对象,批量插入示例内容。

1
2
3
4
5
6
7
8
9
10
#接收examples示例数组参数,通过example_prompt提示词模板批量渲染示例内容
#suffix和input_variables参数用于在提示词模板最后追加内容, input_variables用于定义suffix中包含的模板参数
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="问题:{input}",
input_variables=["input"]
)

print(prompt.format(input="《魔戒》的作者是谁?"))

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
问题:珠穆朗玛峰和富士山哪一座山的首次登顶时间更早?\n
这里需要跟进问题吗:是的。
跟进:珠穆朗玛峰的首次登顶时间是什么时候?
中间答案:珠穆朗玛峰于1953529日首次被登顶。
跟进:富士山的首次登顶时间是什么时候?
中间答案:富士山在公元663年已有朝圣者登顶记录。
所以最终答案是:富士山


问题:特斯拉和SpaceX的CEO是否曾就读于同一所大学?\n
这里需要跟进问题吗:是的。
跟进:特斯拉的CEO是谁?
中间答案:特斯拉的CEO是Elon Musk。
跟进:Elon Musk曾就读于哪所大学?
中间答案:他曾在宾夕法尼亚大学学习。
跟进:SpaceX的CEO是谁?
中间答案:SpaceX的CEO也是Elon Musk。
所以最终答案是:是


问题:《哈利波特》系列和《魔戒》系列的作者是否都出生于英国?\n
这里需要跟进问题吗:是的。
跟进:《哈利波特》的作者是谁?
中间答案:J.K. Rowling。
跟进:J.K. Rowling的出生地是哪里?
中间答案:她出生于英国格洛斯特郡。
跟进:《魔戒》的作者是谁?
中间答案:J.R.R. Tolkien。
跟进:J.R.R. Tolkien的出生地是哪里?
中间答案:他出生于南非奥兰治自由邦。
所以最终答案是:不是


问题:《魔戒》的作者是谁?

4. 使用示例选择器

将示例提供给ExampleSelector

这里重用前一部分中的示例集和提示词模板(prompt template)。但是,不会将示例直接提供给FewShotPromptTemplate对象,把全部示例插入到提示词中,而是将它们提供给一个ExampleSelector对象,插入部分示例。

这里我们使用SemanticSimilarityExampleSelector类。该类根据与输入的相似性选择小样本示例。它使用嵌入模型计算输入和小样本示例之间的相似性,然后使用向量数据库执行相似搜索,获取跟输入相似的示例。

提示:这里涉及向量计算、向量数据库,在AI领域这两个主要用于数据相似度搜索,例如:查询相似文章内容、相似的图片、视频等等,这里使用了google的向量数据库Chroma以及阿里的embedding模型text-embedding-v3,目前先做了解,会在后面教程中详细介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain_community.vectorstores import Chroma
from langchain_openai import DashScopeEmbeddings

# 使用阿里的embedding,使用语义相似性示例选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
# 这是可供选择的示例列表。
examples,
# 这是用于生成嵌入的嵌入类,该嵌入用于衡量语义相似性。
DashScopeEmbeddings(
model="text-embedding-v3",
# other params...
),
# 这是用于存储嵌入和执行相似性搜索的VectorStore类。
Chroma,
# 这是要生成的示例数。
k=1
)

# 选择与输入最相似的示例。
question = "《哈利波特》的作者是哪个国家的?"
selected_examples = example_selector.select_examples({"question": question})
print(f"最相似的示例:{question}")
for example in selected_examples:
print("\\n")
for k, v in example.items():
print(f"{k}{v}")

这里匹配了跟问题相似的例子,下面是返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
最相似的示例:《哈利波特》的作者是哪个国家的?
\n
answer:
这里需要跟进问题吗:是的。
跟进:《哈利波特》的作者是谁?
中间答案:J.K. Rowling。
跟进:J.K. Rowling的出生地是哪里?
中间答案:她出生于英国格洛斯特郡。
跟进:《魔戒》的作者是谁?
中间答案:J.R.R. Tolkien。
跟进:J.R.R. Tolkien的出生地是哪里?
中间答案:他出生于南非奥兰治自由邦。
所以最终答案是:不是

question:《哈利波特》系列和《魔戒》系列的作者是否都出生于英国?
将示例选择器提供给FewShotPromptTemplate

最后,创建一个FewShotPromptTemplate对象。根据前面的example_selector示例选择器,选择一个跟问题相似的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 选择与输入最相似的示例。
question = "珠穆朗玛峰的首次登山时间?"
selected_examples = example_selector.select_examples({"question": question})
example_prompt = PromptTemplate(input_variables=["question", "answer"], template="问题:{question}\\n{answer}")

prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
suffix="问题:{input}",
input_variables=["input"]
)

print(prompt.format(input="珠穆朗玛峰的首次登山时间?"))

返回:

1
2
3
4
5
6
7
8
9
10
问题:珠穆朗玛峰和富士山哪一座山的首次登顶时间更早?\n
这里需要跟进问题吗:是的。
跟进:珠穆朗玛峰的首次登顶时间是什么时候?
中间答案:珠穆朗玛峰于1953529日首次被登顶。
跟进:富士山的首次登顶时间是什么时候?
中间答案:富士山在公元663年已有朝圣者登顶记录。
所以最终答案是:富士山


问题:珠穆朗玛峰的首次登山时间?

通过上面的讲解大家可以发现,LangChain中的提示词模板使用起来简单又方便,我们仅需要少量的代码就可以显著的增强大模型在特定领域的能力,还是非常推荐大家在以后使用大模型或者进行AIGC开发时多多使用提示词工程,毕竟性价比贼高。

三、LangChain工作流编排

1. LCEL介绍

LCEL(LangChain Expression Language) 是一种强大的工作流编排工具,可以通过基本组件构建复杂任务链条(chain),并支持诸如流式处理、并行处理和日志记录等开箱即用的功能。

LCEL 从一开始就被设计为支持将原型投入生产,无需更改代码,从最简单的“提示 + LLM”链到最复杂的链,甚至有人成功地在生产中运行了包含数百步的 LCEL 链。以下是你可能想要使用 LCEL 的一些原因:

  • 一流的流式支持:通过 LCEL 构建的链式流程,能够实现首个标记极速响应(即从请求发出到获取第一个输出块的最小延迟)。对于支持流式处理的链,系统可直接将大模型生成的原始标记实时传输至流式解析器,最终输出速率与模型原生输出速率完全同步。这种机制尤其适合需要实时交互的场景(如对话式 AI)。

  • 异步支持:所有基于 LCEL 构建的链,均支持同步与异步双模式调用

    • 同步模式:适用于 Jupyter 等交互式开发环境,便于快速原型验证。
    • 异步模式:可无缝集成至 LangServe 服务端,实现高并发请求处理。
      开发者可使用同一套代码完成从实验到生产的全流程,兼顾开发效率与线上性能。
  • 优化的并行执行:LCEL 自动识别链中可并行化的环节(例如同时从多个检索器获取文档),并在同步/异步接口中自动启用并行执行,最大限度降低整体延迟。此特性在复杂工作流中可提升 30%-50% 的吞吐效率。

  • 重试和回退:

    • 重试与回退策略:可为链的任意环节配置自动重试规则和备用降级方案,显著增强系统在规模化场景下的鲁棒性。
    • 流式兼容性(开发中):未来版本将支持重试操作与流式输出的无缝结合,实现可靠性提升与零延迟代价的兼得。
  • 访问中间结果: 在复杂链执行过程中,开发者可实时获取中间步骤的结果流。此功能既可用于终端用户的状态提示(如显示“正在检索文档”),也能辅助开发阶段的调试追踪。该特性已全面支持 LangServe 服务端。

  • 输入和输出模式: LCEL 为每个链自动生成基于 Pydantic 和 JSONSchema 的数据契约

    • 输入校验:拒绝不符合模式的非法请求。
    • 输出标准化:确保响应结构符合预期,降低下游系统集成成本。
      此特性是 LangServe API 服务的核心基础,也是构建企业级应用的关键保障。

2. Runable interface介绍

LangChain 通过 Runnable 协议 标准化组件交互接口,大幅降低自定义链开发复杂度。该协议覆盖以下核心组件:

  • 基础模型:聊天模型 (Chat Models)、大语言模型 (LLMs)
  • 数据处理模块:输出解析器 (Output Parsers)、检索器 (Retrievers)
  • 模板工具:提示模板 (Prompt Templates)
  • 扩展组件:支持开发者自定义实现的任意可运行对象

标准接口方法

方法 功能描述 适用场景
invoke 同步调用链,输入单个参数并返回完整结果 简单请求的同步处理
stream 流式传输响应数据块,逐块生成输出结果 实时交互/长文本生成
batch 批量处理输入列表,返回对应结果集合 高吞吐批量任务

异步接口扩展

为支持高并发场景,所有方法均提供异步版本,需配合 asyncio 使用:

异步方法 核心特性 版本要求
ainvoke 异步单请求处理,非阻塞执行 基础支持
astream 异步流式输出,支持实时数据传输 基础支持
abatch 异步批量处理,优化资源利用率 基础支持
astream_log 流式返回中间过程日志及最终结果,实现执行过程可视化 基础支持
astream_events Beta 级事件流接口,实时推送链执行事件(如工具调用、模型响应) langchain-core ≥0.1.1

其中输入类型输出类型 因组件而异:

组件 输入类型 输出类型
提示 字典 提示值
聊天模型 单个字符串、聊天消息列表或提示值 聊天消息
LLM 单个字符串、聊天消息列表或提示值 字符串
输出解析器 LLM 或聊天模型的输出 取决于解析器
检索器 单个字符串 文档列表
工具 单个字符串或字典,取决于工具 取决于工具

所有实现 Runnable 接口的对象均通过以下机制确保数据规范性:

  • input_schema: 基于组件结构自动生成的 Pydantic 输入模型

  • output_schema: 通过运行时类型推导生成的 Pydantic 输出模型

流式处理能力是构建响应式 LLM 应用的关键,LangChain 通过统一接口实现全链路流式支持:如聊天模型输出解析器提示模板检索器代理都实现了 LangChain Runnable 接口。 该接口提供了两种通用的流式内容方法:

  1. 同步 stream 和异步 astream:流式传输链中最终输出的默认实现。

  2. 异步 astream_events 和异步 astream_log:前者为事件驱动流主要用途为实时监控、审计日志,后者为过程监控流主要用途为开发调试、用户进度提示。 接下来我们将学习这两种流式方法。

3. Stream(流)

所有 Runnable 对象都实现了名为 stream 的同步方法和名为 astream 的异步方法。这两个方法旨在以数据块的形式流式传输最终输出,尽可能快地返回每个处理单元。流式传输的实现前提是程序中的所有处理步骤都支持流式输入处理,即能够逐个处理输入数据块并生成对应的输出块。处理复杂度根据场景不同存在差异,从简单的任务(如输出 LLM 生成的令牌)到复杂场景(如在完整生成 JSON 结果前流式传输部分内容)均有覆盖。让我们从 LLM 应用程序的核心组件开始探索——LLM 本身!

LLM 和聊天模型

大型语言模型及其聊天模型变体是构建 LLM 应用程序的主要性能瓶颈。大型语言模型生成完整响应通常需要数秒时间,远超保持应用响应性所需的 200-300 毫秒阈值。提升应用响应性的核心策略在于实时展示中间处理进度,即通过逐个令牌流式传输模型的输出。以下为聊天模型的流式传输示例演示:

让我们从同步 stream API 开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
model = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)

chunks = []
for chunk in model.stream("宋词的前身是什么?"):
chunks.append(chunk)
print(chunk.content, end="|", flush=True)


如果你在异步环境中工作,可以考虑使用异步 astream API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
model = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)

# 异步流处理
async def async_stream():
chunks = []
async for chunk in model.astream("宋词的前身是什么?"):
chunks.append(chunk)
# 判断chunks长度为1的时候,打印chunks[0]
if len(chunks) == 2:
print(chunks[1])
print(chunk.content, end="|", flush=True)
# 运行异步流处理
asyncio.run(async_stream())

让我们检查其中一个块:

1
2
3
4
chunks[1]


content='宋' additional_kwargs={} response_metadata={} id='run-4fb547d7-43ea-4c1d-88a4-f81c763e8f3e'

可以得到了一个称为 AIMessageChunk 的东西。该块表示 AIMessage 的一部分。 消息块是可叠加的——可以简单地将它们相加以获得到目前为止的响应状态!

1
2
3
chunks[0] + chunks[1] + chunks[2] + chunks[3] + chunks[4]

content='宋词的前身' additional_kwargs={} response_metadata={} id='run-c1bfd630-ec2e-4bf8-8cb2-79287cdbdc59'
Chain(链)

绝大多数 LLM 应用程序都包含多步骤操作流程,而非仅执行单一的语言模型调用。我们通过 LangChain 表达式语言 (LCEL) 构建一个基础处理链,该链整合提示模板、模型组件及解析器模块,并验证流式传输机制的有效性。采用 StrOutputParser 进行模型输出解析。该解析器通过从 AIMessageChunk 中提取 content 字段内容,实现模型生成 token 的标准化输出。

LCEL 采用声明式编程范式,通过组合 LangChain 基础组件构建完整工作流程。基于 LCEL 创建的链自动支持 streamastream 方法,天然具备最终输出的流式传输能力。值得注意的是,所有 LCEL 构建的链均完整实现了标准 Runnable 接口规范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
prompt = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话")
model = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)
parser = StrOutputParser()
chain = prompt | model | parser


async def async_stream():
async for chunk in chain.astream({"topic": "工作"}):
print(chunk, end="|", flush=True)

# 运行异步流处理
asyncio.run(async_stream())

# 输出
'''
|好的|!|这是一个|关于|工作的|经典|笑话|:

|---

|**|老板|**|:|我们|公司|崇尚|平等|,|在这里|没有|等级|观念|!|
|**|新|员工|**|:|太好了|!|那|我可以|直接|叫|你|名字|吗|?|
|**|老板|**|:|当然|可以|。|
|**|新|员工|**|:|好的|,|老王|。|
|**|老板|**|:|……|叫我|王|总|。|

|(|冷笑|话|Bonus|:|平等|的前提|是|“|总|”|得|有人|加班|。)|

|---|

|希望|让你|会|心|一笑|~| 😄||
'''

需特别指出,即使我们在上述处理链末端添加了 parser 解析器组件,仍能保持流式输出能力。解析器会对每个流式数据块进行即时处理。许多 LCEL 基础组件均原生支持这种增量式流式传递特性,极大提升了应用构建效率。

开发者可设计返回生成器的自定义函数,以此实现流式数据操作能力。需注意,部分可运行对象(如提示模板和聊天模型)因需聚合前序步骤的完整结果,无法处理独立数据块,此类组件将中断流式处理流程。

LangChain 表达式语言(LCEL)实现了链式架构与调用模式的解耦(如同步/异步、批处理/流式等)。对于无需复杂编排的场景,开发者仍可采用命令式编程范式:通过 invokebatchstream 方法调用各组件,将执行结果存入变量后传递至下游处理。

使用输入流

若需要实现 JSON 的增量流式解析,传统方案将面临技术挑战:直接使用 json.loads 解析不完整的 JSON 片段会因格式无效导致解析失败。表面上看,这似乎宣告了 JSON 流式解析的不可行性。

实际上,通过构建支持输入流处理的解析器,可实现”智能补全”机制:该解析器持续接收输入流片段,动态推测并补全 JSON 结构至有效状态。以下通过实际运行演示解析器的核心工作机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
model = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)
chain = (
model | JsonOutputParser()
# 由于Langchain旧版本中的一个错误,JsonOutputParser未能从某些模型中流式传输结果
)

async def async_stream():
async for text in chain.astream(
"以JSON 格式输出中国、美国和印度的国家及其人口列表。"
'使用一个带有“countries”外部键的字典,其中包含国家列表。'
"每个国家都应该有键`name`和`population`"
):
print(text, flush=True)

# 运行异步流处理
asyncio.run(async_stream())

# 输出
'''
{}
{'countries': []}
{'countries': [{}]}
{'countries': [{'name': ''}]}
{'countries': [{'name': '中国'}]}
{'countries': [{'name': '中国', 'population': 141}]}
{'countries': [{'name': '中国', 'population': 141175}]}
{'countries': [{'name': '中国', 'population': 141175000}]}
{'countries': [{'name': '中国', 'population': 1411750000}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': ''}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国'}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': ''}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': '印度'}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': '印度', 'population': 138}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': '印度', 'population': 138000}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': '印度', 'population': 138000000}]}
{'countries': [{'name': '中国', 'population': 1411750000}, {'name': '美国', 'population': 331000000}, {'name': '印度', 'population': 1380000000}]}
'''

4. Stream events(事件流)

现在我们已经了解了streamastream的工作原理,让我们进入事件流的世界。🏞️

事件流是一个beta API。这个API可能会根据用户反馈略微更改。

本指南演示了V2 API,并且需要 langchain-core >= 0.2。对于与旧版本 LangChain 兼容的V1 API,请参阅这里

1
2
3
# 查看langchain-core版本
import langchain_core
langchain_core.__version__

为确保 astream_events 的正常运行,需严格遵循以下实现原则:

  • 异步优先:在代码架构中优先采用 async 异步编程模式
  • 回调传播:定义自定义函数/可运行对象时需确保回调函数的完整传递
  • 流式强制:未使用 LCEL 时,对 LLM 组件调用 .astream() 而非 .ainvoke 以激活流式令牌生成
事件参考

下表列举不同可运行对象可能触发的事件类型:

当流式处理正确实现时,可运行对象的输入信息通常只能在输入流完全处理完成后确定。这意味着 inputs 字段通常仅出现在结束阶段事件中,而不会包含在开始阶段事件中。

事件 名称 输入 输出
on_chat_model_start [模型名称] {“messages”: [[SystemMessage, HumanMessage]]}
on_chat_model_end [模型名称] {“messages”: [[SystemMessage, HumanMessage]]} AIMessageChunk(content=”hello world”)
on_llm_start [模型名称] {‘input’: ‘hello’}
on_llm_stream [模型名称] ‘Hello’
on_llm_end [模型名称] ‘Hello human!’
on_chain_start format_docs
on_chain_stream format_docs “hello world!, goodbye world!”
on_chain_end format_docs [Document(…)] “hello world!, goodbye world!”
on_tool_start some_tool {“x”: 1, “y”: “2”}
on_tool_end some_tool {“x”: 1, “y”: “2”}
on_retriever_start [检索器名称] {“query”: “hello”}
on_retriever_end [检索器名称] {“query”: “hello”} [Document(…), ..]
on_prompt_start [模板名称] {“question”: “hello”}
on_prompt_end [模板名称] {“question”: “hello”} ChatPromptValue(messages: [SystemMessage, …])
聊天模型

首先让我们看一下聊天模型产生的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
model = ChatOpenAI(
model='deepseek-chat',
max_tokens=1024,
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_BASE_URL")
)

# 异步流处理
async def async_stream():
events = []
async for event in model.astream_events("hello", version="v2"):
events.append(event)
print(events)

# 运行异步流处理
asyncio.run(async_stream())
# 输出
'''
[{'event': 'on_chat_model_start', 'data': {'input': 'hello'}, 'name': 'ChatOpenAI', 'tags': [], 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content='', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content='Hello', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content='!', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' 😊', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' How', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' can', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' I', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' assist', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' you', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content=' today', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content='?', additional_kwargs={}, response_metadata={}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661')}, 'parent_ids': []}, {'event': 'on_chat_model_stream', 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'data': {'chunk': AIMessageChunk(content='', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_3d5141a69a_prod0225'}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661', usage_metadata={'input_tokens': 4, 'output_tokens': 11, 'total_tokens': 15, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}, 'parent_ids': []}, {'event': 'on_chat_model_end', 'data': {'output': AIMessageChunk(content='Hello! 😊 How can I assist you today?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_3d5141a69a_prod0225'}, id='run-d106e0f1-7079-4e3f-a65a-822856c9a661', usage_metadata={'input_tokens': 4, 'output_tokens': 11, 'total_tokens': 15, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}, 'run_id': 'd106e0f1-7079-4e3f-a65a-822856c9a661', 'name': 'ChatOpenAI', 'tags': [], 'metadata': {'ls_provider': 'openai', 'ls_model_name': 'deepseek-chat', 'ls_model_type': 'chat', 'ls_temperature': None, 'ls_max_tokens': 1024}, 'parent_ids': []}]
'''

关于接口中 version="v2" 参数的技术注解:此为 Beta 版 API,其规范可能发生变更(实际已进行过多次调整)。版本参数的设置旨在最大限度降低代码兼容性风险,当前的设计旨在减少未来版本升级对现有代码的破坏性影响。
📌 注意:v2 版本仅支持 langchain-core>=0.2.0,建议通过 pip install langchain-core --upgrade 更新依赖。

四、结语

LangChain 作为开源框架,通过模块化设计、灵活的链式调用(LCEL)和强大的工具集成能力,为开发者提供了构建大语言模型(LLM)应用的高效路径。其核心价值体现在:

  1. 技术整合:统一多种模型(如 GPT-4、DeepSeek等)的 API 接口,支持 RAG、Agents 等复杂场景,解决了 LLM 实时性与数据局限性的矛盾。
  2. 工程化能力:通过 LangSmith 的监控调试、LangServe 的 API 部署,实现从开发到生产的全生命周期管理。
  3. 生态扩展:活跃的社区与丰富的第三方集成(如向量数据库、文档加载器)降低了开发门槛,推动 AI 应用的快速迭代。

本文介绍了LangChain的相关组件,特性,框架组成、核心概念等内容。此外,还着重介绍了提示词工程应用实践、工作流编排等内容,展示了如何使用 LangChain 降低 AI 应用开发门槛,实现与不同 LLM 的轻松集成。如果我的文章内容对你有所帮助,请 不要吝啬您的点赞、收藏和关注,期待你的“一键三连”,咱们下个文章见。