[深度学习] 大模型学习9-LangChain核心概念详解
随着大语言模型(Large Language Model,LLM)的发展,开发者已不再满足于简单对话场景,而是希望将模型真正落地到业务系统中。开发初期,直接调用模型API即可实现问答、摘要或简单助手等基础功能。随着需求复杂化,系统开发复杂度迅速上升,开发者需要自行处理检索、上下文拼接、流程编排、工具调用、状态管理等任务,系统代码也变得越来越难以维护。为降低重复开发成本,LangChain、LlamaIndex、Semantic Kernel等框架应运而生,它们将常见能力抽象为可复用组件,提升复杂应用的可维护性与可控性。
在众多开发框架中,LangChain起步较早且应用广泛。它将常见场景抽象为可复用组件,例如可便捷实现知识库问答(Retrieval-Augmented Generation,RAG),也支持构建具备自主决策能力的智能体(Agent),使模型能够根据任务动态选择下一步操作并调用外部工具完成实际工作。由此,模型不再局限于文本生成,而是能够参与具体任务的执行过程。

当然,LangChain并非唯一选择,不同框架在数据处理和工程集成等方面各具优势。但从入门角度看,LangChain对RAG与Agent等核心范式覆盖较为全面。掌握其基本用法后,再迁移到其他框架时,整体思路大多相通,无需从头搭建体系。基于此,本文将以LangChain为例,介绍该类开发框架的基本使用方法。
⚠⚠⚠注意,本文定位于入门导读,不涉及完整的实战开发,而是围绕Chain、Agent、Memory、RAG等核心概念,系统梳理其应用场景与协作机制。建立基础认知后,再结合实战教程进行实践,将能够更高效地应用LangChain。
LangChain的GitHub仓库地址为:langchain-github。如需深入学习LangChain,可参考LangChain官方文档langchain-docs,以及技术文章GitHub 12w Star 神器!一文详解大模型集成框架 LangChain。
1 概述
1.1 LangChain介绍
基础介绍
LangChain于2022年10月作为面向LLM应用开发的开源项目发布,最初主要适配GPT-3,并在同年11月30日ChatGPT发布后迅速跟进,持续扩展以支持更多模型。其核心优势在于模块化设计,将复杂的LLM开发拆解为可复用组件,使开发者能够灵活组合。基于这一设计,LangChain不仅能够实现广为人知的RAG,还支持智能体、流程编排、工具调用以及多模型协作等功能,展现出良好的可扩展性。
为了支撑这些能力,LangChain对模型接口进行了抽象统一,主要面向三类核心模型:
- LLMs:通常指基于大规模参数和海量文本训练的Transformer模型,以单轮文本生成为核心,输入输出通常为纯字符串,更适合文本生成、翻译、摘要等一次性处理任务。
- Chat Models:面向多轮对话场景,输入输出采用结构化消息,能够更好地表达角色、上下文和对话状态,是当前主流的LLM应用形态。
- Embedding Models:用于将文本、图片等非结构化数据转换为向量表示,支持语义检索、相似度计算和向量数据库存储,是RAG系统中的关键组件。
LangChain通过统一模型接口屏蔽不同模型之间的API差异,简化复杂逻辑编排,并以链式结构和组件化设计让流程清晰可控。在这一基础之上,它逐步发展成为一套完整的生态体系,支持组件灵活组合,并提供Agent、RAG以及调试、观测和评估工具。相比之下,直接调用LLM API虽然上手更快,但接口不统一,复杂逻辑需要手动实现,工具链和RAG也要自行搭建,扩展和排查成本更高。
本文基于langchain 1.x版本介绍langchain基础功能,更多关于LangChain的系统性介绍可参阅:Langchain 1.0.2全栈开发教程。
生态结构
LangChain并不是一个单一库,而是一套面向LLM应用开发的框架体系,整个生态大致可以分为架构层、组件层和部署层。
架构层主要解决如何用统一方式构建应用。它包含三个基础包:langchain(提供Chains、Agents、检索等能力)、langchain-core(提供底层抽象)、langchain-community(集成第三方模型接口和工具扩展)。为整合上述能力,LangChain抽象出六类核心组件:
- Model I/O:处理模型输入与输出,包括提示词组织、模型调用和结果解析;
- Chains:将多个步骤串联起来,形成完整的任务流程;
- Memory:保存历史对话与上下文,支持多轮交互;
- Agents:让LLM根据任务目标选择并调用合适的工具;
- Retrieval:从外部数据中检索信息,是RAG的基础;
- Callbacks:记录和监控运行过程,用于日志、调试和流式输出等。
这些组件能支撑大多数基础场景,比如简单问答、结构化输出、多轮对话和知识库问答。但当流程变复杂,线性的Chains往往不够灵活,一旦涉及分支判断、循环执行、并行处理或人工介入,就需要引入组件层的能力,典型代表是LangGraph和LangSmith。LangGraph将Chains扩展为图结构,支持分支、循环、并行、状态持久化、断点恢复及人工介入,适合复杂流程和多智能体场景。LangSmith则侧重调试与评估,提供链路追踪、提示词管理、实验对比和运行监控等功能,帮助定位问题并优化效果。

应用构建完成后,还需要对外提供服务,这部分由部署层负责。LangServe可以将Chains或Agent封装为API,并接入现有系统。它基于FastAPI实现,部署成本较低,适合将LangChain应用快速服务化。
实际开发和学习可按LangChain起步、LangGraph应对复杂流程、LangSmith做调试优化的顺序推进。
| 顺序 | 学习内容 | 对应能力 |
|---|---|---|
| 1 | 模型调用 | 实现基础问答能力 |
| 2 | Prompt Template | 规范并复用模型输入模板 |
| 3 | Output Parser | 获取结构化输出结果 |
| 4 | Runnable/LCEL | 统一组合模型、提示模板、解析器等组件 |
| 5 | Chains | 构建多步骤串联处理流程 |
| 6 | Memory | 支持多轮对话与上下文记忆保持 |
| 7 | Tools | 接入搜索、数据库、API等外部工具能力 |
| 8 | Agents | 根据任务目标自主选择并调用工具执行 |
| 9 | Retrieval/RAG | 构建基于外部知识库的检索增强问答应用 |
| 10 | LangGraph | 编排包含分支、循环、并行及多智能体协作的复杂流程 |
| 11 | LangSmith | 对应用进行追踪、调试、评估与性能监控 |
| 12 | LangServe | 将应用封装为API服务并部署运行 |

1.2 环境准备
本文基于Python开发环境,采用LangChain 1.x版本进行实现。相较于早期0.x系列版本,LangChain 1.x在API设计上发生了较大变化。LangChain核心库可通过以下命令进行安装:
pip install langchain
在整体设计理念上,LangChain 1.x更加注重数据流与工作流的构建能力。其关注重点已不再局限于单次模型调用,而是聚焦于用户输入如何经过一系列可控的处理步骤,最终生成稳定且可靠的输出结果。也就是说,该版本更强调完整执行流程的组织与编排,而非单一的模型对话能力。
LangChain本身并不提供具体模型,而是作为统一的调用框架用于对接外部模型服务。为了支持不同的模型提供方,LangChain采用模块化设计,使开发者可根据实际需求灵活集成相关组件。

在具体使用中,如果需要对接常用的OpenAI或兼容OpenAI协议的模型服务,可以安装如下扩展包:
pip install langchain-openai
如果使用Ollama作为本地LLM或嵌入LLM服务,则可安装对应集成:
pip install langchain-ollama
此外,在应用开发过程中,若涉及文本拆分、向量存储等功能,还需要安装相关组件:
pip install langchain-text-splitters langchain-chroma
2 基础使用
2.1 基础模型
前文提到LangChain支持三类模型,本节主要介绍Chat Models和Embedding Models的使用,不再单独介绍LLMs。因为Chat Models已基本覆盖LLMs的使用场景,并更符合当前实际应用方式。

2.1.1 Chat Model
在LangChain中,与LLM的交互被统一抽象为一套接口,即Model I/O。它可以理解为应用程序与LLM之间的中间层,使开发者无需关心底层模型实现,只需通过一致的方式完成调用。
基于这一抽象,LangChain可以对接多种模型来源,包括OpenAI、各类第三方模型接口以及本地服务,如兼容OpenAI协议的Ollama等。尽管模型来源不同,但调用方式是统一的。以OpenAI为例,典型调用方式如下:
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
model_name=model_name, # 模型名称
base_url=base_url, # API服务地址
api_key=api_key # API密钥
)
其中model_name表示模型名称,base_url表示模型服务地址,api_key表示接口密钥。
同样地,本地模型服务也可以通过相同方式接入。例如使用Ollama时,可以先拉取并启动模型:
ollama pull qwen3.5:0.8b
ollama serve
Ollama的完整使用教程可参阅:推理部署框架llama.cpp与Ollama使用指北。Ollama默认监听地址为 http://localhost:11434 ,并原生兼容OpenAI协议的接口,只需调整参数即可在LangChain中调用:
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
model_name="qwen3.5:0.8b",
base_url="http://localhost:11434/v1",
api_key="any_value" # 本地服务通常不校验
)
response = chat_model.invoke("请介绍一下你自己")
print(response.content)
后续示例将统一使用该chat_model,并省略初始化过程。关于LangChain与Ollama的配合使用,可参考:LangChain 1.0光速入门!基于本地Ollama部署LLM。
从本质上看,上述调用过程正是Model I/O的具体体现,而一次完整的LLM交互可以拆分为三个步骤:首先对输入进行格式化,其次调用模型生成结果,最后对输出进行解析。这三个阶段分别对应Prompt Template、Model和Output Parser,从而形成统一且可扩展的调用流程。
2.1.2 Embedding Model
Embedding Model是Model I/O体系中的另一类核心模型,用来把文本、图片等非结构化数据表示为向量。在这个向量空间里,语义相近的内容会更接近,因此可以用来做相似度检索。在RAG任务中,它会分别将用户问题与知识库文本进行编码,通过相似度找到最相关的内容,提供给LLM作为上下文。
在LangChain中,可以通过Embedding模型完成文本向量化,不同来源的Embedding服务也可以通过统一接口使用。例如,使用本地部署的Ollama嵌入模型生成向量:
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(
model="qwen3-embedding:0.6b",
base_url="http://127.0.0.1:11434"
)
vector = embeddings.embed_query("hello world")
print("嵌入向量长度:", len(vector))
print("前10个值:", vector[:10])
代码得到的vector即为输入文本的向量表示,可用于后续的相似度计算或检索任务。
2.2 Message消息机制
在LangChain中,消息(Message)是Chat Model交互的基本载体,用于统一描述模型输入与输出的结构。一条消息通常由两部分组成:角色(Role)和内容(Content)。角色用于标识消息的来源或作用,内容则包含具体的文本信息。此外,消息还可以携带上下文信息和元数据,以支持更复杂的对话场景。
LangChain提供了多种常见的消息类型,以适配不同的交互需求:
| 消息类型 | 说明 |
|---|---|
| SystemMessage | 用于设定模型的行为规则、角色身份及上下文约束 |
| HumanMessage | 表示用户输入 |
| AIMessage | 表示模型输出 |
| ToolMessage/FunctionMessage | 表示工具执行结果,常用于Agent或函数调用场景 |
在典型的对话流程中,SystemMessage用于预设规则,HumanMessage提供输入问题,AIMessage负责生成回答。这种分工使对话结构更加清晰,也便于扩展到多轮对话和Agent等复杂应用场景。
示例代码:
from langchain_core.messages import SystemMessage, HumanMessage
question = "电风扇的工作原理是什么?"
# 风格1:标准说明
messages1 = [
SystemMessage(content="请使用标准说明文风格作答,要求表达清晰、客观严谨"),
HumanMessage(content=question)
]
# 风格2:文言文风格
messages2 = [
SystemMessage(content="请以文言文风格作答,语言简洁典雅"),
HumanMessage(content=question)
]
resp1 = chat_model.invoke(messages1)
resp2 = chat_model.invoke(messages2)
print("=== 标准说明 ===")
print(resp1.content)
print("\n=== 文言文风格 ===")
print(resp2.content)

2.3 Prompt Template提示词模板
Prompt是与LLM交互的输入,用于定义任务目标、背景和输出要求,其设计质量直接影响模型效果。在实际业务中,输入具有动态性,固定Prompt难以复用且适配性有限。为此,LangChain引入Prompt Template,将静态字符串抽象为可复用模板,并通过变量填充在运行时生成Prompt,从而提升灵活性与可维护性。有关Prompt的更多内容详见:提示词工程指北。
PromptTemplate(基础文本模板)
PromptTemplate是最基础的模板类型,适用于生成纯文本形式的Prompt,其核心能力是通过变量替换实现动态内容生成。PromptTemplate的具体使用见:LangChain PromptTemplate 全解析。
from langchain_core.prompts import PromptTemplate
prompt = PromptTemplate.from_template(
"请生成一段话:{task}"
)
prompt_text = prompt.invoke({
"task": "介绍良好阅读习惯的重要性"
}).text
response = chat_model.invoke(prompt_text)
print(response.content)
这种方式适用于结构简单的单轮任务。但随着任务复杂度提升,仅使用PromptTemplate会逐渐暴露出结构不清晰、可控性不足的问题。
ChatPromptTemplate(对话模板)
在对话模型场景中,直接拼接文本Prompt往往不够清晰,因此LangChain提供了ChatPromptTemplate,用于按角色组织Prompt结构。
from langchain_core.prompts import ChatPromptTemplate
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一名文言文写作助手,输出一段自然流畅的文字"),
("human", "请完成任务:{task}")
])
messages = chat_prompt.invoke({
"task": "介绍良好阅读习惯的重要性"
})
response = chat_model.invoke(messages)
print(response.content)
通过显式区分system和human角色,可以让Prompt结构更加清晰,同时也更贴合对话模型的训练方式。不过,当Prompt进一步复杂化时,这种方式仍然存在复用困难的问题。

MessagePromptTemplate(消息模板组件)
为了支持更复杂的Prompt设计,LangChain提供了MessagePromptTemplate,将Prompt拆分为更细粒度的组件。
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate
)
chat_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(
"你是一个擅长写说明性文字的助手,输出必须为一段完整的表述"
),
HumanMessagePromptTemplate.from_template(
"请生成一段话:{task}"
)
])
messages = chat_prompt.invoke({
"task": "介绍良好阅读习惯的重要性"
})
response = chat_model.invoke(messages)
print(response.content)
在这种方式下,不同角色的Prompt可以独立定义,并按需组合,适用于复杂系统和团队协作开发。但即使结构清晰,模型在输出格式上的稳定性仍难以完全保证。
FewShotPromptTemplate(少样本提示)
当需要模型稳定输出特定格式或风格时,仅依赖Prompt往往不够。FewShotPromptTemplate通过提供示例引导模型模仿既有格式,以示例替代规则,使输出更加稳定和可预测。关于相关概念及更详细的使用说明,可参考:Langchain学习入门。
简单示例如下:
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
example_template = PromptTemplate.from_template(
"单词:{word}, 英文翻译:{translation}"
)
examples_data = [
{"word": "苹果", "translation": "apple"},
{"word": "快乐", "translation": "happy"}
]
few_shot_template = FewShotPromptTemplate(
example_prompt=example_template,
examples=examples_data,
prefix="请给出一个单词及其英文翻译,以下是示例:",
suffix="单词:{input_word},英文翻译:",
input_variables=["input_word"]
)
prompt_text = few_shot_template.invoke(
{"input_word": "计算机"}
).to_string()
print(prompt_text)
response = chat_model.invoke(prompt_text)
print(response.content)
在对话场景中也可以使用基于消息结构的FewShotChatMessagePromptTemplate:
from langchain_core.prompts import (
ChatPromptTemplate,
FewShotChatMessagePromptTemplate,
)
examples = [
{"input": "你好", "output": "Hello"},
{"input": "谢谢你的帮助", "output": "Thank you for your help"},
]
example_prompt = ChatPromptTemplate.from_messages([
("human", "{input}"),
("ai", "{output}"),
])
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt,
examples=examples,
)
final_prompt = ChatPromptTemplate.from_messages([
(
"system",
"你是一个翻译助手,将中文翻译成英文。只输出翻译结果,不要提供额外解释。"
),
few_shot_prompt,
("human", "{input}"),
])
chain = final_prompt | chat_model
print(chain.invoke({"input": "今天天气很好"}).content)
print(chain.invoke({"input": "请稍等一下"}).content)
partial方法(Prompt部分填充)
通过模板实现动态生成Prompt后,常会遇到固定变量反复传递的问题,如目标语言、角色设定或业务背景,手动处理既繁琐又易出错。此时可用partial方法预先填入这些固定内容,相当于提前构建好一部分Prompt。这样便可通过一个通用模板衍生多个可直接使用的版本,简单又高效。
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "将以下文本翻译为{target_lang},保留原意"),
("human", "{text}")
])
zh_translator = template.partial(target_lang="中文")
en_translator = template.partial(target_lang="英文")
result_zh = zh_translator.invoke({"text": "Hello world"})
result_en = en_translator.invoke({"text": "你好,世界"})
print(result_zh.to_messages())
print(result_en.to_messages())
2.4 Output Parser输出解析器
LLM默认返回的是纯字符串,而实际开发中为了进行后续处理,通常需要结构化数据。为此,必须先对模型的输出结果进行解析,借助解析器将文本转换为特定的数据结构。在LangChain中,这类解析器被定义为Output Parser,并提供了多种实现以适配不同的输出格式。
| 解析器名称 | 作用 |
|---|---|
| StrOutputParser | 将模型输出直接解析为字符串 |
| JsonOutputParser | 将模型输出解析为JSON格式的结构化数据 |
| CommaSeparatedListOutputParser | 将逗号分隔的文本解析为Python列表 |
| XMLOutputParser | 将模型输出解析为XML格式的结构化数据 |
关于Output Parser的更多介绍见:How to Master Output Parsers,下面通过一个完整示例,演示不同解析器的使用方式:
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import (
StrOutputParser,
JsonOutputParser,
CommaSeparatedListOutputParser,
XMLOutputParser
)
# ===============================
# 1. 字符串解析器StrOutputParser(最基础用法)
# ===============================
str_prompt = PromptTemplate.from_template(
"用一句话介绍什么是{name}"
)
str_chain = str_prompt | chat_model | StrOutputParser()
print("StrOutput:", str_chain.invoke({"name": "LLM"}))
# ===============================
# 2. JSON解析器JsonOutputParser(结构化输出)
# ===============================
json_parser = JsonOutputParser()
json_prompt = PromptTemplate(
template="用一句话介绍什么是{name}。\n{format_instructions}",
input_variables=["name"],
partial_variables={
"format_instructions": json_parser.get_format_instructions()
}
)
json_chain = json_prompt | chat_model | json_parser
print("JSON Output:", json_chain.invoke({"name": "LLM"}))
# ===============================
# 3. 列表解析器CommaSeparatedListOutputParser(列表结构)
# ===============================
list_parser = CommaSeparatedListOutputParser()
list_prompt = PromptTemplate(
template="列出{name}的3个特点。\n{format_instructions}",
input_variables=["name"],
partial_variables={
"format_instructions": list_parser.get_format_instructions()
}
)
list_chain = list_prompt | chat_model | list_parser
print("List Output:", list_chain.invoke({"name": "LLM"}))
# ===============================
# 4. XML解析器XMLOutputParser(层级结构)
# ===============================
xml_parser = XMLOutputParser()
xml_prompt = PromptTemplate(
template="用XML格式输出什么是{name}。\n{format_instructions}",
input_variables=["name"],
partial_variables={
"format_instructions": xml_parser.get_format_instructions()
}
)
xml_chain = xml_prompt | chat_model | xml_parser
print("XML Output:", xml_chain.invoke({"name": "LLM"}))

2.5 Runnable的调用方式
Runnable是LangChain中的核心执行抽象,代表任何可接收输入并产生输出的可执行单元。可以把它理解为一个标准化的接口,无论LLM、提示词模板、输出解析器、检索器、工具还是LangGraph定义的工作流,只要遵循这一接口规范,就都成为了Runnable。这种统一让不同组件可以像积木一样自由拼接和编排,从而构建复杂的应用。
在LangChain 1.x中,这一抽象被进一步统一,Prompt、Model、Parser、Retriever以及Tool等核心组件,均被明确定义为Runnable。由于遵循相同的输入输出规范,一个组件的输出可以直接成为下一个组件的输入,实现模块间的无缝衔接与灵活组合。更详细的说明可阅读:深入解析Runnable接口的统一、组合与流式处理。
统一的执行抽象建立之后,核心问题便转向调用方式。Runnable为此提供了一组标准方法,以覆盖不同的使用场景:
| 方法 | 说明 |
|---|---|
| invoke | 同步单次调用 |
| stream | 流式输出 |
| batch | 批量处理 |
| ainvoke | 异步单次调用 |
| astream | 异步流式输出 |
| abatch | 异步批量处理 |
下面通过简单示例来介绍这些调用方式的实际用法:
import asyncio
def run_invoke():
response = chat_model.invoke("你好")
print("=== invoke ===")
print(response.content)
print(response.response_metadata)
def run_stream():
print("\n=== stream ===")
for chunk in chat_model.stream("写一篇500字文章"):
print(chunk.content, end="", flush=True)
def run_batch():
print("\n=== batch ===")
responses = chat_model.batch([
"翻译:hello",
"翻译:world",
"翻译:apple"
])
for r in responses:
print(r.content)
async def run_async():
print("\n=== ainvoke ===")
response = await chat_model.ainvoke("异步调用")
print(response.content)
print("\n=== astream ===")
async for chunk in chat_model.astream("讲一个简短故事"):
print(chunk.content, end="", flush=True)
print()
print("\n=== abatch ===")
responses = await chat_model.abatch([
"异步翻译:cat",
"异步翻译:dog"
])
for r in responses:
print(r.content)
if __name__ == "__main__":
run_invoke()
run_stream()
run_batch()
asyncio.run(run_async())
2.6 Bind方法
bind方法解决了一个常见需求:同一个模型,在不同任务中往往需要不同的运行参数。例如,做数学计算时希望输出稳定,写文案时又希望富有变化。如果每次都重新实例化模型来切换参数,既繁琐又低效。
bind的作用,就是在一个模型实例上临时附加一组运行参数,比如temperature、max_tokens或stop等,然后返回一个绑定了这些参数的新对象。该方法不会修改原始模型,而是在其基础上生成一个绑定了特定参数的副本,从而允许在不重新实例化模型的前提下灵活切换调用行为。
这一机制常用于根据任务需求动态调整模型策略,例如:
- 精确策略:绑定
temperature=0.0, max_tokens=200,提升输出稳定性与确定性,适用于信息抽取、数学计算等对准确性要求较高的任务; - 创意策略:绑定
temperature=0.9, max_tokens=1000,增强随机性与表达空间,适用于写作、对话生成等需要开放创造力的任务。
示例:
# bind 生成不同策略的实例,原模型不受影响
precise_strategy = chat_model.bind(temperature=0.3, max_tokens=200)
creative_strategy = chat_model.bind(temperature=0.9, max_tokens=1000)
result_precise = precise_strategy.invoke("12*9=?")
result_creative = creative_strategy.invoke("写一首关于夏天的诗")
print("=== 精确策略 ===")
print(result_precise.content)
print("\n=== 创意策略 ===")
print(result_creative.content)

3 核心组件
3.1 Chains与LCEL
在LangChain中,Chain可以理解为一条处理任务的流水线。数据会按照预先设定的步骤,依次经过提示词模板、LLM和输出解析器等组件处理,最终输出结果。关于Chain的详细介绍,可参考:LangChain最详细教程之Chains。
LCEL(LangChain Expression Language)用于以声明式方式描述这一结构,将各组件组合为可执行表达式,而不改变底层执行逻辑。最常见的写法是使用管道符|:
chain = prompt | chat_model | parser
该表达式自左向右执行:prompt生成输入,传递至chat_model进行推理,再由parser完成结构化处理,对应完整的Chain流程。可以理解为,Chain定义执行结构,LCEL负责表达结构,|用于连接各Runnable组件并传递数据流。LangChain中Chains与LCEL的关系可以参考:深度拆解LangChain Chains与LCEL。
在实现层面,参与组合的组件都需要实现Runnable接口。多个组件通过|连接后,新的Chain本身仍然是一个Runnable,因此既可以继续与其他组件组合,也可以通过invoke、stream和batch等方式执行任务。
示例:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
# Math Chain
math_prompt = ChatPromptTemplate.from_template(
"请计算下面数学问题:{input}"
)
math_chain = (
math_prompt
| chat_model
| StrOutputParser()
)
print(
math_chain.invoke({
"input": "25乘以8等于多少?"
})
)
# MultiPrompt / Router Chain
translate_prompt = ChatPromptTemplate.from_template(
"请翻译下面内容:\n{input}"
)
summary_prompt = ChatPromptTemplate.from_template(
"请总结下面内容:\n{input}"
)
translate_chain = (
translate_prompt
| chat_model
| StrOutputParser()
)
summary_chain = (
summary_prompt
| chat_model
| StrOutputParser()
)
def router(x):
text = x["input"]
if "翻译" in text or "translate" in text.lower():
return translate_chain
return summary_chain
router_chain = RunnableLambda(router)
print(
router_chain.invoke({
"input": "Technology makes life better."
})
)
# StuffDocumentsChain
prompt = ChatPromptTemplate.from_template(
"请总结下面内容:\n{context}"
)
docs = [
Document(page_content="LangChain是一个LLM开发框架。"),
Document(page_content="它支持Prompt、Chain和Agent。")
]
context = "\n".join([doc.page_content for doc in docs])
stuff_chain = (
prompt
| chat_model
| StrOutputParser()
)
print(
stuff_chain.invoke({
"context": context
})
)

3.2 Memory记忆机制
基础使用
实际上,LLM本身没有真正的记忆,每次调用都像一次全新的对话。如果只发送当前问题而不提供历史记录,模型并不知道之前聊过什么。因此,多轮对话的实现方式并不是让模型记住内容,而是由应用程序保存历史对话,并在每次提问时将历史记录和当前问题一起发送给LLM,使模型看起来像拥有持续记忆一样。
举一个更完整的例子,可以直观说明上下文在对话中的作用。假设我们有一个周末出行规划助手。在第一轮中,用户提出一个模糊需求:
用户:帮我规划一下这个周六的出行。
AI:好的,请问你想去哪里,大概几点出发?
此时,LLM已建立基本语境,即围绕周六出行规划展开,并引导用户补充关键信息。随后,用户提供细节:
用户:去植物园,上午10点左右。
如果这句话单独输入,它只是一个零散片段,由于缺少背景信息,模型难以准确判断其意图,可能将其理解为日程记录或简单描述。
但当上下文一并输入时,语境变得完整,模型可以基于前后关联信息正确理解其含义:
用户:帮我规划一下这个周六的出行。
AI:好的,请问你想去哪里,大概几点出发?
用户:去植物园,上午10点左右。
可以看到,对话之所以能够连贯,并不是因为模型具备记忆能力,而是因为每一次请求都会将必要的上下文一起传入模型,模型只是基于当前输入重新进行理解与生成。

记忆
在LangChain框架中,负责管理对话历史的组件称为Memory,其本质是一个专门保存聊天记录的地方,用于保存每一轮的用户输入与模型输出。例如,可以通过InMemoryChatMessageHistory类实现这一机制。基于这一设计,多轮对话的执行流程如下:
- 接收用户输入。
- 从Memory中读取历史对话。
- 将历史对话与当前问题组合成完整上下文。
- 调用模型生成回复。
- 将回复返回给用户。
- 将本轮对话写入Memory。
因此,多轮对话的关键不在于模型真正记住了过去的内容,而在于应用程序持续保存并提供历史对话。模型每次回答时,实际上都是基于当前问题和历史记录共同进行推理,从而保持对话的连贯性。关于长期记忆机制,可参考:深入解读LangChain 1.0 Agent长期记忆技术要点。
示例代码如下:
from langchain_core.chat_history import InMemoryChatMessageHistory
# 创建 Memory
history = InMemoryChatMessageHistory()
# 添加用户消息
history.add_user_message("您好,我喜欢每天跑步1小时")
# 添加回复消息
history.add_ai_message("我喜欢游泳")
# 添加用户消息
history.add_user_message("我每天跑步多久?")
response = chat_model.invoke(history.messages)
print(response.content)
记忆裁剪
在多轮对话里若不加以限制,历史记录会持续膨胀。常见做法是按轮数裁剪,只保留最近K轮对话,更早的内容直接丢弃:
from langchain_classic.memory import ConversationBufferWindowMemory
from langchain_classic.chains import ConversationChain
from langchain_community.llms.fake import FakeListLLM
llm = FakeListLLM(
responses=["OK"] * 10
)
memory = ConversationBufferWindowMemory(
k=3,
return_messages=True
)
conversation = ConversationChain(
llm=llm,
memory=memory
)
for i in range(5):
conversation.predict(
input=f"第{i+1}轮对话"
)
print(f"\n=== 第{i+1}轮后 ===")
history = memory.load_memory_variables({})["history"]
for message in history:
print(message.content)
以上代码本质上实现的是一个固定长度的滑动窗口。运行结果如下:
- 第1~3轮:正常累加
- 第4轮:第1轮被移除
- 第5轮:第2轮被移除
3.3 Tools工具
LLM擅长理解意图、推理和生成文本,但很多实际任务仅依赖生成能力并不足够,例如查询天气需要实时数据、查询订单需要访问业务系统,而这些能力通常不在模型内部,因此需要通过Tool机制将外部能力封装为可调用接口。Tool本质上是对某项功能的结构化描述和执行封装,类似函数调用,常见场景包括数学计算、时间获取、天气查询、数据库检索以及业务系统接口等。
一个Tool通常包含名称、描述、参数定义和执行逻辑,其中名称用于标识工具,描述帮助模型判断是否调用,参数定义约束输入格式,执行逻辑负责完成实际操作。在执行过程中,系统首先将工具注册给模型或Agent,当模型判断需要外部能力时,会根据用户输入和工具描述选择合适的工具并生成调用参数,待工具执行完成后再将结果返回给模型,最终由模型生成回答。对于这一机制的实现原理与实践方式,可以参考:How to Build a Custom AI Agent with LangChain and Python: A Step by Step Guide。

使用@tool定义工具
在LangChain中,可通过@tool装饰器将普通Python函数封装为Tool,函数名称、文档字符串和参数类型会自动提取为结构化元信息,用于描述工具能力。
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气信息"""
weather_data = {
"A": "晴天,25°C",
"B": "多云,22°C",
}
return weather_data.get(city, f"无法获取{city}天气")
# 直接调用工具,不经过 LLM
result = get_weather.invoke({"city": "A"})
print(result) # 晴天,25°C
# 查看工具元信息
print(f"工具名: {get_weather.name}")
print(f"描述: {get_weather.description}")
print(f"参数: {get_weather.args}")
通过@tool定义后,该函数既可以作为普通Python函数直接调用,也可以作为工具被模型或Agent使用,两者的区别在于前者不涉及模型参与,而后者会进入工具调用流程,由模型决定是否调用并生成参数。更多介绍可参考:LangChain 实战教程:从入门到实战。
将工具绑定到模型
工具定义完成后,可以通过bind_tools方法将多个工具绑定到模型,使模型在处理输入时能够感知这些工具。当用户输入到来时,模型先判断是否需要调用外部能力,如果需要,则选择合适工具并生成调用参数。整个过程中,模型只负责工具选择与参数生成,工具的实际执行由开发者完成并返回结果。
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""获取城市天气"""
return f"{city}:晴天 25°C"
@tool
def search_database(query: str) -> str:
"""搜索数据库信息"""
return f"关于'{query}'的搜索结果:示例数据"
tools = [get_weather, search_database]
llm_with_tools = chat_model.bind_tools(tools)
response = llm_with_tools.invoke("上海天气如何?")
if response.tool_calls:
for tc in response.tool_calls:
print(f"调用工具: {tc['name']},参数: {tc['args']}")
for tool in tools:
if tool.name == tc['name']:
print(f"结果: {tool.invoke(tc['args'])}")
Agent中使用工具
如果希望工具选择、调用与结果整合全部自动化,可以将工具交由Agent管理。Agent会根据输入自动选择工具、执行调用,并结合结果继续推理直至生成最终答案。
from langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
agent = create_agent(
model=chat_model,
tools=[multiply]
)
response = agent.invoke({
"messages": [
{
"role": "user",
"content": "12乘以9等于多少"
}
]
})
print(response["messages"][-1].content)
使用StructuredTool
除了使用@tool快速将函数封装为Tool外,还可以通过StructuredTool显式定义工具的名称、描述和参数结构,其中@tool更适合快速开发,StructuredTool更适合需要严格控制元信息的场景,但两者本质上都是将外部能力封装为模型可调用的Tool。
"""
StructuredTool 单独演示
"""
from langchain_core.tools import StructuredTool
# 使用StructuredTool将函数转换为工具
multiply_tool = StructuredTool.from_function(
func=multiply,
name="multiply",
description="Multiply two numbers"
)
# 调用工具
result = multiply_tool.invoke({"a": 5, "b": 3})
print(f"5 × 3 = {result}")
3.4 Agent智能体
3.4.1 Agent的基本机制
在Chain中,执行流程通常是预先固定的,按照既定步骤依次执行。而Agent的不同之处在于,它不依赖固定流程,而是根据用户输入动态决定下一步行为,并在需要时调用外部工具完成任务。
从能力分工来看,LLM主要负责理解与生成,而Agent是在其外层增加了一层任务决策与执行机制,使系统不仅能回答问题,还能根据任务需求选择合适工具,例如搜索、计算、查询数据库或调用外部接口,并在获取结果后完成整合输出。
因此,Agent的执行过程可以抽象为:
用户输入 → 理解任务 → 判断是否需要工具 → 调用工具 → 获取结果 → 生成回答
对应到LangChain中,一个Agent通常包含以下核心组成部分:
- 用户输入:任务的起点
- LLM:负责理解输入并进行决策
- Prompt Template:组织输入与上下文信息,使模型更容易处理任务
- Memory:保存历史对话,实现多轮交互
- Tools:提供外部能力,用于完成模型无法直接处理的任务
- Planning:在复杂任务中用于拆解步骤并调整执行路径
- Action:将模型决策转化为具体工具调用
- 返回结果:整合工具输出与上下文生成最终回答
在更复杂系统中,还可通过多Agent协作实现任务分工。有关Agent的详细介绍见:LangChain最详细教程之Agents。

3.4.2 Agent的运行模式与示例
Agent常见的运行方式包括Function Calling和ReAct。相关概念的详细介绍见:Function Call / MCP / ReAct / Skills 如何构成 AI Agent 的完整技术栈与从历史的角度梳理Agent-ReAct-Skills-MCP-Tool的关系。
Function Calling模式
Function Calling 是一种结构化的工具调用机制。开发者预先定义可调用函数,如天气查询、计算器、数据库检索等。当接收到用户请求时,模型会判断是否需要调用工具;若需要,则只返回函数名和参数,由外部系统执行并将结果返回模型,最终生成完整回复。
这种模式使模型能够获取实时信息并与外部系统交互,因此广泛应用于实时查询、数据处理和业务流程自动化等场景。更多内容可参考:深入解析Function Calling、MCP和Skills的本质差异与最佳实践。
ReAct模式
ReAct是Reasoning and Acting的缩写,表示推理与行动交替执行,其核心流程为:
思考 → 行动 → 观察 → 思考 → 行动 → ... → 最终答案
其中,思考用于分析任务与规划步骤,行动用于调用工具,观察用于接收工具返回结果并决定下一步操作。相比Function Calling,ReAct更适合多步任务,例如先检索信息,再进行计算,最后综合生成答案。

基础Agent示例
以下示例定义了天气查询、计算器和知识搜索三个工具。Agent会根据用户请求自动选择并调用相应工具。例如,当用户询问天气时,Agent 会识别天气查询意图,调用天气工具获取结果,并基于返回信息生成最终回复:
from langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""获取城市天气信息"""
weather_db = {
"北京": "晴天,15-25度",
"上海": "多云,18-28度",
"深圳": "小雨,20-30度",
}
return weather_db.get(city, f"{city}的天气信息暂不可用")
@tool
def calculator(expression: str) -> str:
"""执行数学计算"""
try:
result = eval(expression)
return f"计算结果: {expression} = {result}"
except Exception:
return "计算错误"
@tool
def search_knowledge(query: str) -> str:
"""搜索知识库"""
knowledge = {
"LangChain": "LangChain是一个用于开发LLM应用的框架,支持工具、代理、记忆等功能。",
"机器学习": "机器学习是AI的一个分支,使系统能够从数据中自动学习和改进。",
}
for key, value in knowledge.items():
if key in query:
return value
return f"未找到关于“{query}”的信息"
agent = create_agent(
model=chat_model,
tools=[get_weather, calculator, search_knowledge],
system_prompt="你是一个专业的中文助手,请分析用户问题并选择最合适的工具进行回答。"
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "今天上海天气怎么样?"}
]
})
final_message = result["messages"][-1]
print(f"回答: {final_message.content}")
带记忆的Agent示例
在多轮对话中,Agent可以通过Checkpointer或Memory保存上下文,从而实现状态保持。
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
agent = create_agent(
model=chat_model,
tools=[get_weather, calculator, search_knowledge],
system_prompt="你是一个专业助手,能够记住用户的历史对话。",
checkpointer=checkpointer
)
config = {"configurable": {"thread_id": "session_001"}}
result1 = agent.invoke(
{"messages": [{"role": "user", "content": "小明就是我"}]},
config
)
print("第一轮对话:", result1["messages"][-1].content)
result2 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
config
)
print("第二轮对话:", result2["messages"][-1].content)
通过thread_id标识会话,不同轮次的输入共享同一上下文,从而实现连续对话能力。
4 检索
4.1 Retrieval与RAG
在LangChain中,检索(Retrieval)是连接LLM与外部数据源的核心能力,也是实现RAG的基础。它能够根据用户问题,从文档、数据库或知识库中获取相关信息,并将其作为上下文提供给模型。
如果仅依赖LLM自身进行问答,通常会面临两个问题:一是知识截止,模型的知识停留在训练数据对应的时间点,无法获取最新信息;二是幻觉,模型在缺乏事实依据时,可能生成看似合理但并不准确甚至虚构的内容。在金融、医疗、法律以及企业知识管理等场景中,这些问题尤为突出,因为此类领域对结果的准确性、时效性和可追溯性要求极高。仅依赖模型参数中的知识,不仅难以及时更新,也无法有效说明答案来源,从而影响系统的可信度。
目前尚不存在能够彻底消除幻觉的通用方法,更常见的做法是为模型提供充分、可靠且与问题相关的信息,使生成过程建立在明确依据之上。RAG正是这一思路的典型实践。它在生成答案前先完成检索,将用户问题与外部知识一同提供给模型,使输出基于真实上下文。通过这种方式,既能缓解知识滞后问题,也能降低无依据生成的风险。想要完整学习RAG,可以阅读文章:RAG技术全景解析。
典型的RAG流程分为两个阶段:
-
第一阶段:入库阶段(离线准备)。系统从PDF、Word、网页、Markdown、数据库等数据源中提取文本,将长文档切分为较小的文本片段,使用Embedding模型将片段转换为向量表示,最后将向量及对应的文本、来源、标题、页码等元数据存入向量数据库,形成可检索的知识库。
-
第二阶段:在线阶段(实时推理)。用户提出问题后,系统先将问题转换为向量表示,并在向量数据库中查找语义相似的文本片段,这便是Retrieval的核心。对检索精度要求较高的场景,可在向量召回后引入Rerank模型对候选结果进行重排序,以筛选出更相关的内容。随后,系统将筛选后的文本片段与用户问题组合成Prompt,交由LLM生成最终答案。
在整个过程中,模型能力主要参与三个环节。向量化阶段使用Embedding模型,重排序阶段可按需引入Rerank模型,最终答案生成则由LLM完成。其中,检索、增强和生成构成RAG的核心链路。RAG的实践介绍可参考:Build a RAG agent with LangChain。
从工程实践看,RAG无需修改模型参数且接入成本低,知识更新只需维护外部知识库,因此比微调更适用于知识频繁变化、数据私有性强且需保留信息来源的场景。但其效果依赖检索质量,检索失准会影响回答准确性,同时还需维护多个检索组件并受上下文窗口限制。因此,检索质量和上下文利用效率是RAG落地的关键。

4.2 Retrieval流程
在基于LLM的知识库问答系统中,Retrieval是连接外部知识与模型生成能力的关键环节。它的核心流程是先获取外部数据,再进行统一加载、清洗切分、向量化和存储,最终形成可供检索的知识库,为后续问答提供上下文支持。更详细介绍见:让Langchain与你的数据对话。
典型的Retrieval数据准备流程通常包括五个阶段:Source、Load、Transform、Embed和Store。
-
阶段一:Source(数据源)
Source提供原始数据,常见来源包括文本文件、PDF、网页、数据库记录,以及从图片、音频或视频中提取的文本内容。数据源的质量会直接影响后续检索效果,因此在进入处理流程前,需要尽量保证内容完整、来源可靠、格式清晰。 -
阶段二:Load(加载)
Load负责将不同来源、不同格式的数据加载为统一结构。在LangChain等框架中,加载后的数据通常会被表示为Document对象。Document一般包含两部分内容:一部分是正文文本,用于后续切分、向量化和检索;另一部分是元数据,用于记录来源、标题、页码、时间等信息,便于结果追溯和引用。 -
阶段三:Transform(转换)
Transform负责对Document进行预处理,使其更适合后续向量化和检索。常见操作包括文本分块、噪声清理、内容去重、格式规范化和元数据整理。其中,文本分块是最重要的步骤之一。分块过大可能导致检索结果不够精确,分块过小则可能破坏语义完整性,因此需要根据文档类型和业务场景选择合适的切分策略。 -
阶段四:Embed(嵌入)
Embed用于将文本转换为向量表示,以捕获其语义特征。语义越相近的文本,在向量空间中的距离通常越近。检索时,系统会将用户问题和知识库内容分别向量化,并通过相似度计算召回相关文本。常见的相似度计算方法包括余弦相似度、欧氏距离和点积,其中余弦相似度因效果稳定、应用广泛,是文本检索中最常用的方法之一。 -
阶段五:Store(存储)
Store负责将文本向量、原始文本和元数据存入向量数据库,并提供高效的相似度检索能力。当用户提出问题时,系统会先将问题转换为向量,再在向量数据库中搜索最相关的文本片段。常见的向量存储方案包括FAISS、Milvus、Chroma等。
经过以上五个阶段,原始数据会被转换为可检索的向量化知识库。后续在线问答时,Retrieval组件便可以根据用户问题从知识库中召回相关内容,并将其作为上下文提供给LLM。相关工具的具体使用方法可参考:LangChain快速入门。
1. 数据源
在进入正式流程前,先生成一些日常生活中会用到的文件。以下代码会在当前目录下创建三个文件:一份健身日志(txt)、一份旅行开支记录(csv)和一份读书笔记(json)。后续示例将基于这些文件,依次演示加载、拆分、向量化、存储与检索流程。
import json
import csv
# 1. 健身日志 txt
with open("./fitness_log.txt", "w", encoding="utf-8") as f:
f.write("周一:跑步30分钟,深蹲4组。\n")
f.write("周二:瑜伽1小时,拉伸15分钟。\n")
f.write("周三:休息。\n")
f.write("周四:HIIT 20分钟,哑铃弯举3组。\n")
f.write("周五:户外骑行40分钟。\n")
# 2. 旅行开支 csv
with open("./travel_expenses.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["date", "category", "amount", "note"])
writer.writerow(["2025-05-01", "机票", 1200, "北京-上海"])
writer.writerow(["2025-05-02", "酒店", 450, "如家精选"])
writer.writerow(["2025-05-03", "餐饮", 88, "小笼包"])
# 3. 读书笔记 json
notes = [
{"title": "原子习惯", "author": "詹姆斯·克利尔", "key_point": "微小改变,惊人成果"},
{"title": "深度工作", "author": "卡尔·纽波特", "key_point": "专注力是稀缺资源"}
]
with open("./reading_notes.json", "w", encoding="utf-8") as f:
json.dump(notes, f, ensure_ascii=False, indent=2)
2. 文档加载器
文档加载器负责将不同格式的数据读取出来,并转换为统一的Document对象。这样,后续的文本拆分、向量化和检索流程就可以基于统一的数据结构进行处理。
import json
import pandas as pd
from langchain_core.documents import Document
# TXT
txt_docs = [
Document(
page_content=open("fitness_log.txt", "r", encoding="utf-8").read()
)
]
# CSV
df = pd.read_csv("travel_expenses.csv")
csv_docs = [
Document(page_content=row.to_json(force_ascii=False))
for _, row in df.iterrows()
]
# JSON
with open("reading_notes.json", "r", encoding="utf-8") as f:
json_docs = [
Document(page_content=item["title"])
for item in json.load(f)
]
print("", txt_docs[0].page_content[:50])
print("", csv_docs[0].page_content)
print("", json_docs[0].page_content)
得到文档列表后,通常需要将它们切分成更小的块,以便后续模型处理。
3. 文本拆分器
文本拆分器(Text Splitter)首先将文档拆分为句子等更小的语义单元,再按语义顺序组合成接近预设大小的文本块,并在相邻块之间保留一定重叠,以避免语义在边界处丢失。这样既能控制文本块长度、减少无关信息干扰,又有利于提升检索的精准度和召回效果。
LangChain提供多种拆分策略,常用方法包括按字符或Token长度拆分、递归拆分、按文档结构拆分、语义分块。详情可参考:Chunking Techniques with Langchain and LlamaIndex。以下以递归拆分为例,演示如何将文本切分成指定大小的文档块:
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
with open("./fitness_log.txt", "r", encoding="utf-8") as f:
txt_content = f.read()
txt_docs = [
Document(page_content=txt_content)
]
splitter = RecursiveCharacterTextSplitter(
chunk_size=32,
chunk_overlap=5,
separators=["\n\n", "\n", "。", ",", " ", ""]
)
txt_chunks = splitter.split_documents(txt_docs)
for i, chunk in enumerate(txt_chunks):
print(f"块{i}: {chunk.page_content}")
4. 文档嵌入模型
Embedding模型用于将文本映射为固定维度的向量表示,使语义相似的文本在向量空间中的距离更接近,从而支持相似度计算与语义检索。LangChain支持调用多种Embedding模型,例如本地部署的Ollama、开源的Hugging Face,以及OpenAI、DeepSeek等商业API。生成的向量通常需要存储在向量数据库中,以实现高效的语义搜索。下面代码使用Ollama中的Embedding模型,将查询文本转换为向量:
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(
model="qwen3-embedding:0.6b",
base_url="http://127.0.0.1:11434"
)
vec = embeddings.embed_query("跑步30分钟消耗多少热量")
print(f"向量维度: {len(vec)}")
print(f"前5个值: {vec[:5]}")
5. Vector Stores向量存储
向量数据库专门用于存储文档向量并提供高效的相似度搜索,LangChain集成了Chroma、FAISS等多种向量库。下面的例子将前面得到的健身日志文本块向量化后存入Chroma:
# pip install langchain_chroma
from langchain_chroma import Chroma
if len(txt_chunks) == 0:
print("错误:没有可用的文本块,请检查拆分器配置")
exit()
print(f"正在将 {len(txt_chunks)} 个文本块向量化并存入Chroma...")
vectorstore = Chroma.from_documents(
documents=txt_chunks,
embedding=embeddings,
persist_directory="./chroma_fitness"
)
print("向量存储创建完成!\n")
执行后,文本块、向量及相关信息会保存到./chroma_fitness目录中。在此基础上,简单场景直接使用基础搜索即可满足需求,但实际应用往往需要更复杂的召回逻辑,例如设置相似度阈值、多路融合等,此时可引入检索器组件来应对。检索库的选择可以见:LangChain Vector Stores。
6. 向量检索器
向量存储完成后,文本块、向量和元数据均已保存在向量库中,此时便可基于相似度搜索召回相关内容。检索器在向量库之上封装了统一接口,可配置召回数量和检索方式,便于将结果接入LLM。下面基于Chroma创建检索器,并构建一个简单的问答流程。
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma
embeddings = OllamaEmbeddings(
model="qwen3-embedding:0.6b",
base_url="http://127.0.0.1:11434"
)
vectorstore = Chroma(
persist_directory="./chroma_fitness",
embedding_function=embeddings
)
retriever = vectorstore.as_retriever(
search_kwargs={"k": 5},
search_type="similarity"
)
prompt = ChatPromptTemplate.from_template(
"根据以下信息回答问题:\n{context}\n\n问题:{question}"
)
def format_docs(docs):
return "\n".join([d.page_content for d in docs])
chain = (
{
"context": retriever | RunnableLambda(format_docs),
"question": RunnablePassthrough()
}
| prompt
| chat_model
)
response = chain.invoke("我星期几休息?")
print(response.content)
5 LangChain 1.x总结
LangChain的核心价值并不在于封装模型API,而在于将提示词构建、模型调用、结果解析、RAG、工具调用等能力模块化,并编排成完整的执行流程,使单次模型调用演进为可扩展、可维护的LLM应用。
LLM应用本质上是一个围绕上下文和状态持续演进的执行过程,包括上下文构建、模型推理、工具调用、知识检索和状态更新等环节,并循环运行直至任务完成。LangChain通过Prompt、Runnable、Tool、Retriever、Memory等组件将这些能力解耦,实现更灵活、更易维护的系统架构。
不过,对于单轮问答、轻量级RAG或简单函数调用等场景,直接使用模型SDK通常更加合适。这类场景业务逻辑简单,而主流LLM已经原生支持函数调用、结构化输出和基础Agent能力,引入LangChain往往会增加额外的抽象层和维护成本。

随着LLM厂商不断增强原生Agent能力,LangChain的定位也在逐步演变。它的价值不再主要体现在补充模型能力,而更多体现在统一编排、能力扩展和复杂系统集成上。对于标准化场景,模型原生能力通常已能满足大部分需求。而随着业务复杂度提升,LangChain在流程管理和工程化方面的优势才会逐渐显现。
从复杂度角度看,LLM应用大致可以分为三个层次:
- 低复杂度场景:单轮问答、简单RAG、基础工具调用,直接使用模型SDK即可。
- 中等复杂度场景:需要统一多模型接入,或增加日志、重试、限流等通用能力时,LangChain能够有效降低工程复杂度。
- 高复杂度场景:涉及多轮决策、长流程执行、状态持久化、故障恢复等需求时,仅依赖模型原生Agent或LangChain的链式编排通常已难以满足需求,需要采用LangGraph等具备状态建模与流程编排能力的框架。
不同复杂度场景下的Agent框架选型可参考:2026年应该了解的20个Agentic AI框架,特征、场景、优劣势一文看明白。
6 参考
- langchain-github
- langchain-docs
- GitHub 12w Star神器!一文详解大模型集成框架LangChain
- Langchain 1.0.2全栈开发教程
- 推理部署框架llama.cpp与Ollama使用指北
- LangChain 1.0光速入门!基于本地Ollama部署LLM
- 提示词工程指北
- LangChain PromptTemplate 全解析
- Langchain学习入门
- How to Master Output Parsers
- 深入解析Runnable接口的统一、组合与流式处理
- LangChain最详细教程之Chains
- 深度拆解LangChain Chains与LCEL
- How to Build a Custom AI Agent with LangChain and Python: A Step by Step Guide
- LangChain 实战教程:从入门到实战
- LangChain最详细教程之Agents
- Function Call / MCP / ReAct / Skills 如何构成 AI Agent 的完整技术栈
- 从历史的角度梳理Agent-ReAct-Skills-MCP-Tool的关系
- 深入解析Function Calling、MCP和Skills的本质差异与最佳实践
- RAG技术全景解析
- Build a RAG agent with LangChain
- 让Langchain与你的数据对话
- LangChain快速入门
- LangChain Vector Stores
本文来自博客园,作者:落痕的寒假,转载请注明原文链接:https://www.cnblogs.com/luohenyueji/p/20983358

浙公网安备 33010602011771号