🔥2026从零学Langchain——Python版本:
【Python版 2026 从零学Langchain 1.x】(一)快速开始和LCEL
【Python版 2026 从零学Langchain 1.x】(二)结构化输出和工具调用
【Python版 2026 从零学Langchain 1.x】(三)Agent和Memory
🔥2026从零学Langchain——TS版本:
【TS版 2026 从零学Langchain 1.x】(一)快速开始和LCEL
【TS版 2026 从零学Langchain 1.x】(二)结构化输出和工具调用
【TS版 2026 从零学Langchain 1.x】(三)Agent和Memory
一、智能体 Agent
我们前面已经在简单使用agent了。这部分我们详细聊聊agent.
和模型对话是一种 请求-响应的模式,这对于解决复杂问题显然不够,我们希望有一种机制能让模型自主解决问题,能记忆、调用工具、循环,于是Agent登场了。
一个简单的agent的创建如下。
agent = create_agent(
model,
system_prompt=SYSTEM_PROMPT,
tools=[get_user_location, get_weather_for_location],
context_schema=Context,
response_format=ResponseFormat,
checkpointer=checkpointer,
# debug=True, # 开启debug模式,会打印出agent的运行过程
middleware=[]
)
可以看出重点的部分:
model: Agent的推理引擎tools: Agent可以使用的工具(Tool calling)response_format: Agent的输出格式(结构化输出)checkpointer: Agent的状态检查点,用于保存和恢复Agent的状态(记忆)middleware: 中间件
agent 本质上是一个有状态的有限状态机 (FSM)。它会自动在“模型推理”和“工具执行”之间跳转/循环,直到问题解决。 而checkpointer会记录每次invoke后的完整状态(可理解为“存档”)。
1. 中间件
先介绍下「中间件」的概念,因为后面很多能力都依赖「中间件」的机制(比如动态模型、短期记忆)。
对于学过web框架的同学一定对「中间件」有所了解,Langchian也是借助了一些成熟的工程设计思路,将「中间件」概念集成到Langchain,解决了以往存在的痛点:
- 各种复杂配置
- 侵入修改agent的逻辑
无中间件的Agent
有中间件的Agent
可以看出,Langchain中间件的本质就是钩子,在agent流程的每个步骤前后设置回调钩子,能对执行步骤进行拦截处理,从而达到更细粒度流程控制。
哪些常见的中间件呢?
- 对话历史记录整理
- 日志记录
- 提示词转换
- 工具选择
- 重试、回退逻辑
- 速率限制
具体怎么做呢?
使用 装饰器 创建中间件, Langchain提供了下面两种类型风格的钩子。
1.Node-style hooks:
@before_agent- 代理启动前(每次invoke调用前执行一次)@before_model- 每次模型调用前@after_model- 每次模型响应后@after_agent- 代理完成时(每次invoke调用后执行一次) 2.Wrap-style hooks:@wrap_model_call- 在每个模型调用周围@wrap_tool_call- 在每个工具调用周围
下面我们会逐渐了解和学习到这些装饰器的使用。
2. 模型
模型的配置分为静态模型和动态模型。
- 静态模型:在agent创建时就确定好的模型,比如OpenAI的
gpt-3.5-turbo、gpt-4等。 - 动态模型:在agent运行时根据上下文动态切换的模型,比如根据用户输入的问题选择不同的模型。
我们重点看下动态模型是如何实现的,这里给出一个场景:根据用户问题的复杂度,动态选择模型。
- 使用
qwen_32b模型判断问题复杂度 - 简单,则使用
deepseek V3.2;复杂,则使用GLM 4.7 - 最终模型返回回答
动态模型的选择需要依靠「中间件」的方式来实现。
1.定义三个模型
glm_model = ChatOpenAI(
model=settings.glm_model,
base_url=settings.siliconflow_base_url,
api_key=settings.siliconflow_api_key,
temperature=0.9,
max_tokens=10000,
timeout=60,
)
ds_model = ChatOpenAI(
model=settings.ds_model,
base_url=settings.siliconflow_base_url,
api_key=settings.siliconflow_api_key,
temperature=0.9,
max_tokens=10000,
timeout=60,
)
qwen3_32b_model = ChatOpenAI(
model=settings.qwen3_32b_model,
base_url=settings.siliconflow_base_url,
api_key=settings.siliconflow_api_key,
temperature=0.9,
max_tokens=5000,
timeout=60,
)
2.工具函数,根据“模型响应”用来判断问题复杂度
def _extract_latest_user_text(messages: list) -> str:
for message in reversed(messages):
if isinstance(message, HumanMessage):
return str(message.content)
if not messages:
return ""
last = messages[-1]
content = getattr(last, "content", None)
if content is not None:
return str(content)
return str(last)
def _judge_complexity(user_text: str) -> Literal["simple", "complex"]:
router = qwen3_32b_model.bind(max_tokens=64)
res = router.invoke(
[
{
"role": "system",
"content": "你是问题复杂度分类器。根据用户问题判断复杂度:\n- simple:单一事实/常识问答、简单翻译/润色、很短的直接回答、无需多步推理或设计。\n- complex:需要多步推理、方案设计/架构、长文写作、复杂代码/调试、严谨数学推导、对比权衡。\n只输出:simple 或 complex。",
},
{"role": "user", "content": user_text},
]
)
text = str(getattr(res, "content", res)).strip().lower()
if text == "simple" or "simple" in text or "简单" in text:
return "simple"
if text == "complex" or "complex" in text or "复杂" in text:
return "complex"
return "complex"
3.定义中间件,拦截模型请求
@wrap_model_call
def dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse:
"""Choose model based on conversation complexity."""
user_text = _extract_latest_user_text(request.messages)
complexity = _judge_complexity(user_text)
selected_model = ds_model if complexity == "simple" else glm_model
return handler(request.override(model=selected_model))
4.测试
def test_dynamic_model_selection():
"""
"""
checkpointer = InMemorySaver()
agent = create_agent(
glm_model,
context_schema=Context,
checkpointer=checkpointer,
middleware=[dynamic_model_selection],
)
config = {"configurable": {"thread_id": "1"}}
r1 = agent.invoke(
{"messages": [{"role": "user", "content": "1.9 和1.11 哪个数字大?(这个问题有点难,请一步步思考)"}]},
config=config,
context=Context(user_id="1"),
)
# print(r1)
ai_message = r1["messages"][-1]
print("响应内容:", end="\n")
print(ai_message.content)
print(f"调用模型:\n {ai_message.response_metadata['model_name']}")
"""
响应内容:
...
所以答案是:**1.9 比 1.11 大**。
虽然 1.11 的数字位数更多,但在比较小数大小时,我们主要看数值本身。1.9 实际上比 2 只少 0.1,而 1.11 比 2 少 0.89,所以 1.9 明显更大。
调用模型:
deepseek-ai/DeepSeek-V3.2-Exp
"""
r2 = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "请用langchain 1.x 设计一个简单的问答系统,用户可以向系统咨询某地的天气信息,包括天气工具调用。",
}
]
},
config=config,
context=Context(user_id="1"),
)
ai_message = r2["messages"][-1]
print("响应内容:", end="\n")
print(ai_message.content)
print(f"调用模型:\n {ai_message.response_metadata['model_name']}")
"""
响应内容:
# LangChain 1.x 天气问答系统设计
下面是一个使用 LangChain 1.x 设计的简单天气问答系统完整示例:
...
调用模型:
Pro/zai-org/GLM-4.7
"""
3. 工具
Agent的工具部分,包括三种场景:
- 工具的错误处理
- ReAct循环中使用
- 动态工具
1.先说说工具的错误处理,有时候工具处理出错了,我们希望反馈给LLM 自定义的错误信息。
1.1 继续前面的例子中 “1.9 和1.11 哪个数字大?”的问题,有时候LLM回答不正确,那么我希望它能调用工具来回答,如下:
@tool
def compare_two_numbers(a: float, b: float) -> int:
"""
比较两个数字a,b的大小
Args:
a (float): 第一个数字
b (float): 第二个数字
Returns:
int: 比较结果. 如果a>b返回1,a<b返回-1,a=b返回0
"""
if a > b:
return 1
elif a < b:
return -1
else:
return 0
1.2 可能出现调用tool出错(比如传参错误,内部触发边界错误等等),那么,可以使用 @wrap_tool_call 定义tool调用阶段的钩子(中间件),来处理错误。
@wrap_tool_call
def handle_tool_errors(request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]) -> ModelResponse:
"""处理工具调用错误,返回自定义错误消息"""
try:
return handler(request)
except Exception as e:
# 返回自定义的错误消息给LLM
return ToolMessage(
content=f"Tool error: Please check your input and try again. ({str(e)})",
tool_call_id=request.tool_call["id"]
)
1.3 测试agent
def test_tool_compare_two_numbers():
"""
测试 compare_two_numbers 工具.
加入 handle_tool_errors 中间件
"""
checkpointer = InMemorySaver()
agent = create_agent(
ds_model,
context_schema=Context,
checkpointer=checkpointer,
tools=[compare_two_numbers],
middleware=[dynamic_model_selection, handle_tool_errors],
)
r = agent.invoke(
{"messages": [{"role": "user", "content": "1.9 和1.11 哪个数字大?"}]},
config={"configurable": {"thread_id": "1"}},
context=Context(user_id="1"),
)
ai_message = r["messages"][-1]
print("响应内容:", end="\n")
print(ai_message.content)
print(f"调用模型:\n {ai_message.response_metadata['model_name']}")
# 响应内容:
# 根据比较结果,**1.9 比 1.11 大**。
# 调用模型:
# deepseek-ai/DeepSeek-V3.2-Exp
"""
最后一次prompt:
Human: 1.9 和1.11 哪个数字大?
AI: 我来帮你比较这两个数字。\n\n[{'name': 'compare_two_numbers', 'args': {'a': 1.9, 'b': 1.11}, 'id': '019bf8eb9ca83173071547a05e3a3fe0', 'type': 'tool_call'}]
Tool: 1
"""
2.ReAct循环中使用。工具是可以在agent循环中被反复使用的。
2.1 上面的test_tool_compare_two_numbers就是一个例子:调用一次Tool compare_two_numbers后发现 可以得出答案,就停止循环了返回结果。如果发现问题还没解决就会继续 思考/调用工具 循环,直到有最终答案。
3.动态工具。我们可以通过@tool预先注册工具,然后根据上下文动态调用工具。
3.1 比如 我 希望不同用户角色,能调用的工具是不一样的。普通用户无法使用管理员才能调用的工具。 3.2 官方示例(拦截工具调用,替换tools):
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable
@wrap_model_call
def filter_tools(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""Filter tools based on user permissions."""
user_role = request.runtime.context.user_role
if user_role == "admin":
# Admins get all tools
tools = request.tools
else:
# Regular users get read-only tools
tools = [t for t in request.tools if t.name.startswith("read_")]
return handler(request.override(tools=tools))
agent = create_agent(
model="gpt-4o",
tools=[read_data, write_data, delete_data], # All tools pre-registered
middleware=[filter_tools],
)
4. 响应格式
1.create_agent提供了一个参数response_format,可以传入一个pydantic对象,用来约束输出,这块内容在 第二篇有介绍过。底层是LLM模型的结构化输出强约束和Langchain的校验兜底。
2.如果没有 response_format,需要从agent.invoke 的结果的messages中取最后一条消息,得到最终的回答,并且是字符串.
3.而使用 response_format后,agent.invoke 的结果会多一个structured_response字段, 并且是一个结构化对象(如 Pydantic 实例)。
4.我们继续之前的比较数字大小的例子,这次对响应结果加约束。
class CompareResult(BaseModel):
num1: float = Field(..., description="第一个数字")
num2: float = Field(..., description="第二个数字")
result: int = Field(..., description="比较结果,1 表示 num1 大于 num2,-1 表示 num1 小于 num2,0 表示相等")
def test_response_fomat():
"""
测试响应格式是否符合要求
"""
checkpointer = InMemorySaver()
agent = create_agent(
glm_model,
context_schema=Context,
checkpointer=checkpointer,
tools=[compare_two_numbers],
middleware=[handle_tool_errors],
response_format=CompareResult, # 约束格式
)
r = agent.invoke(
{"messages": [{"role": "user", "content": "1.9 和1.11 哪个数字大?"}]},
config={"configurable": {"thread_id": "1"}},
context=Context(user_id="1"),
)
print(r["structured_response"])
# 输出 CompareResult(num1=1.9 num2=1.11 result=1)
print(r)
"""r是字典,结构如下:
{
messages: [],
model_name: "deepseek-ai/DeepSeek-V3.2-Exp",
...
# response_format带来的字段(此时messages最后的AIMessage的content是空的)
structured_response: CompareResult(num1=1.9 num2=1.11 result=1)
}
"""
5. 状态检查点
checkpointer 是用来保存和恢复 agent 的状态。它具备以下能力
- 记忆能力
- 线程隔离
- 故障恢复和“时空旅行”
记录历史对话记录
如果你不传递checkpointer,那么agent是没有记忆能力的,下面例子中模型将无法记住你的名字
def test_no_checkpointer():
"""
测试 1: create_agent 不传递 checkpointer。
预期:没有之前交互的记忆。
"""
print("\n" + "="*50)
print("测试 1: create_agent 不带 checkpointer (应该无记忆)")
print("="*50)
# 1. 创建不带 checkpointer 的 agent
agent = create_agent(qwen3_32b_model, checkpointer=None)
# 2. 第一次交互
print("\n【步骤 1】\n [用户]: 嗨!我叫 Bob。")
response1 = agent.invoke(
{"messages": [{"role": "user", "content": "嗨!我叫 Bob。"}]},
{"configurable": {"thread_id": "1"}} # 没有 checkpointer 时 thread_id 可能被忽略,但为了安全起见还是传递
)
print(f"[Agent]: {response1['messages'][-1].content}")
# 3. 第二次交互
print("\n【步骤 2】\n [用户]: 我叫什么名字?")
# 注意:没有 checkpointer,如果我们想要记忆,必须手动传递对话历史。
# 但这里我们模拟一个新的回合而不传递历史,预期 agent 不知道名字,
# 因为状态没有被保存。
response2 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
{"configurable": {"thread_id": "1"}}
)
print(f"[Agent]: {response2['messages'][-1].content}")
下面例子中模型能记住你的名字。
def test_with_checkpointer():
"""
测试 2: create_agent 使用 checkpointer=InMemorySaver()。
预期:在同一个 thread_id 内有记忆。
"""
print("\n" + "="*50)
print("测试 2: create_agent 带 checkpointer (应该有记忆)")
print("="*50)
# 1. 创建带 checkpointer 的 agent
memory = InMemorySaver()
agent = create_agent(qwen3_32b_model, checkpointer=memory)
thread_config = {"configurable": {"thread_id": "thread-1"}}
# 2. 第一次交互
print("\n【步骤 1】\n [用户]: 嗨!我叫 Alice。")
response1 = agent.invoke(
{"messages": [{"role": "user", "content": "嗨!我叫 Alice。"}]},
thread_config
)
print(f"[Agent]: {response1['messages'][-1].content}")
# 3. 第二次交互
print("\n【步骤 2】\n [用户]: 我叫什么名字?")
response2 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
thread_config
)
print(f"[Agent]: {response2['messages'][-1].content}")
线程隔离
所谓线程隔离就是你可以用同一个agent 发起多个会话,每个会话是独立的,互不干扰的。
下面的例子演示了,什么是线程隔离.
def test_checkpointer_thread_isolation():
"""
测试 3: create_agent 使用 checkpointer 和不同的 thread_id。
预期:记忆通过 thread_id 隔离。
"""
print("\n" + "="*50)
print("测试 3: create_agent 线程隔离")
print("="*50)
memory = InMemorySaver()
agent = create_agent(qwen3_32b_model, checkpointer=memory)
# 1. 线程 A 交互
print("\n[线程 A] 用户: 嗨!我叫 Charlie。")
agent.invoke(
{"messages": [{"role": "user", "content": "嗨!我叫 Charlie。"}]},
{"configurable": {"thread_id": "thread-A"}}
)
# 2. 线程 B 交互
print("\n[线程 B] 用户: 你好!我叫 疯狂踩坑人")
agent.invoke(
{"messages": [{"role": "user", "content": "你好!我叫 疯狂踩坑人"}]},
{"configurable": {"thread_id": "thread-B"}}
)
# 3. 线程 A 交互 (问名字)
print("\n[线程 A] 用户: 我叫什么名字?")
response_a = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
{"configurable": {"thread_id": "thread-A"}}
)
print(f"[线程 A] Agent: {response_a['messages'][-1].content}")
# 4. 线程 B 交互 (问名字)
print("\n[线程 B] 用户: 我叫什么名字?")
response_b = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
{"configurable": {"thread_id": "thread-B"}}
)
print(f"[线程 B] Agent: {response_b['messages'][-1].content}")
检查点 checkpoint
运行下面代码,查看memory的变化
def test_checkpoints():
"""
测试 4: 检查 checkpointer 保存的 checkpoint 是否正确。
"""
print("\n" + "="*50)
print("测试 4: 检查 checkpointer 保存的 checkpoint")
print("="*50)
memory = InMemorySaver()
agent = create_agent(qwen3_32b_model, checkpointer=memory)
thread_config = {"configurable": {"thread_id": "thread-1"}}
print("\n[用户]: 嗨!我叫 疯狂踩坑人。")
response1 = agent.invoke(
{"messages": [{"role": "user", "content": "嗨!我叫 疯狂踩坑人。"}]},
thread_config
)
print(f"[Agent]: {response1['messages'][-1].content}")
checkpoints = list(memory.list(thread_config))
print(len(checkpoints), end="\n")
for checkpoint_tuple in checkpoints:
print(checkpoint_tuple.checkpoint, end='\n\n')
memory保存了很多检查点,每个检查点都有id、ts(时间)和channel_values.messages等信息
每个checkpoint代表一次存档,通过某一个checkpoint就可以恢复agent的状态,这样你可以穿越回去到之前invoke的任一个状态,这非常重要。
二、记忆 Memory
短期记忆 Short-term memory
短期记忆主要指在**单个会话(Session)**内,LLM 能够“记得”刚刚说过的话。它的目的是保持对话的连贯性。
管理消息 - SummarizationMiddleware
1.前面提到的checkpointer 会保存你的历史消息,但是这样会存在两个问题:
- LLM 上下文限制:发送给大模型的 Token 数量超过其最大窗口,导致报错。
- 成本与延迟增加:每次调用都会携带冗长的历史记录,消耗更多 Token 且模型响应变慢
2.针对这些问题,业界的处理方案基本就是:
- 裁剪最早的消息,保持一个固定大小的"窗口"
- 对裁剪的历史消息,使用LLM进行总结,作为一条
3.下面用一个例子来演示,Langchain是如何通过SummarizationMiddleware管理对话消息的。这个中间件做的事就是上面的方案:裁剪+总结。
SYSTEM_PROMPT = "你是一个人工智能助手"
def _build_models() -> tuple[ChatOpenAI, ChatOpenAI]:
base_kwargs = dict(
base_url=settings.siliconflow_base_url,
api_key=settings.siliconflow_api_key,
timeout=60,
)
agent_model = ChatOpenAI(
model=settings.qwen3_32b_model,
temperature=0.7,
max_tokens=2000,
**base_kwargs,
)
summarizer = ChatOpenAI(
model=settings.qwen3_32b_model,
temperature=0.2,
max_tokens=512,
**base_kwargs,
)
return agent_model, summarizer
def test_summarization_middleware() -> None:
"""
测试 SummarizationMiddleware 中间件。
"""
agent_model, summarizer = _build_models()
checkpointer = InMemorySaver()
agent = create_agent(
agent_model,
system_prompt=SYSTEM_PROMPT,
middleware=[
SummarizationMiddleware(
model=summarizer,
trigger=("messages", 4),
keep=("messages", 4),
summary_prefix="对话摘要:",
summary_prompt="请将以下对话历史压缩成简短的中文摘要,保留关键信息(事实、偏好、约束、决定、结论):\n{messages}",
)
],
checkpointer=checkpointer,
)
config: RunnableConfig = {"configurable": {"thread_id": "short-memory-demo"}}
user_inputs = [
"我叫小明,住在北京。",
"请记住我更喜欢用中文回答。",
"我这周想做一个 LangChain 的学习计划。简短控制在100字",
"计划要按天拆分,每天不超过1小时。简短控制在100字",
"顺便提醒我:周三晚上要健身。最后请把所有安排再用要点总结一次。",
]
# 自动进行对话
for idx, text in enumerate(user_inputs, start=1):
r = agent.invoke({"messages": [{"role": "user", "content": text}]}, config=config)
messages = r["messages"]
ai_message = messages[-1]
print(messages)
print(f"\n[Turn {idx}] 当前上下文消息数:{len(messages)}(trigger=('messages', 4), keep=('messages', 4))\n")
print(f"用户:{text}")
print(f"助手:{ai_message.content}")
4.trigger=("messages", 4),keep=("messages", 4) 表示当消息超过4条(不包含system_prompt)时触发裁剪,裁剪的信息保留4条。这样一来,「消息窗口」大小控制在4。
5.理论上keep可以大于trigger,这样总结消息和keep的消息在内容上就会有重叠。
6.trigger和keep 除了通过message控制,还可以通过tokens控制,比如超过多少tokens就压缩。
7.从第三轮开始,当前消息数:6. 这意味下一次对话前,就会触发压缩,实际消息窗口大小为4.
8。下面是第三轮对话的messages打印结果,可以看到前面总结的消息。可以发现Langchain默认的做法是: 使用了总结性提示'here is a summary of ...',将1,2,3,4次消息发给了LLM做总结'
此外,Langchain还提供了RemoveMessage 来删除指定的消息,这个结合Langgraph会比较实用。
存储介质
from langgraph.checkpoint.memory import InMemorySaver 中的InMemorySaver是将状态数据保存在内存中,那么程序已结束,这些状态就会丢失。所以官方更推荐在生产环境使用 PostgresSaver
pip install langgraph-checkpoint-postgres
# 或者用uv
uv add langgraph-checkpoint-postgres
在生产环境中,使用一个由数据库支持的检查点:
from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup() # auto create tables in PostgresSql
agent = create_agent(
"gpt-5",
tools=[get_user_info],
checkpointer=checkpointer,
)
当然,除了Postgres数据库介质,还有其他存储介质,见文档,比如Sqlite。
uv add langgraph-checkpoint-sqlite
from langgraph.checkpoint.sqlite import SqliteSaver
def test_sqlite_saver() -> None:
"""
测试 SQLiteSaver 检查点存储。
"""
agent_model, summarizer = _build_models()
conn = sqlite3.connect("checkpoints.sqlite", check_same_thread=False)
memory = SqliteSaver(conn)
agent = create_agent(
agent_model,
system_prompt=SYSTEM_PROMPT,
checkpointer=memory,
)
config: RunnableConfig = {"configurable": {"thread_id": "test_sqlite_saver"}}
# text = "你好,我叫“疯狂踩坑人”" # 第一次运行,用这个提示词
text = "请问我叫什么名字?" # 然后第二次,用这个提示词
r = agent.invoke({"messages": [{"role": "user", "content": text}]}, config=config)
messages = r["messages"]
print(f"[user] {messages[-2].content}")
print(f"[assistant] {messages[-1].content}")
# 我最近老是健忘!(尴尬地挠头) 不过既然是"疯狂踩坑人",那你...
你会发现运行目录下多出一个checkpoints.sqlite的数据库文件。
用sqlite客户端工具(比如vscode插件Database Client)打开查看checkpoints表,可以看到,这些数据都保存到了表里。
长期记忆 Long-term memory
长期记忆是指跨越多天、多周甚至数个不同会话,系统依然能记得用户的偏好、事实或历史背景。
-
核心机制: 检索增强生成(RAG)与外部数据库。它不直接塞进当前的 Prompt 窗口,而是通过“按需检索”的方式工作。
-
LangChain 实现方式:
- 向量数据库(Vector Stores): 如 Pinecone, Milvus, Chroma。将历史对话或知识切片并嵌入(Embedding),当用户提问时,通过语义搜索找回相关片段。
- VectorStoreRetrieverMemory: LangChain 特有的组件,允许将向量数据库作为记忆组件挂载。
- 实体记忆(Entity Memory): 提取对话中的特定实体(如“我的名字叫 疯狂踩坑人”)并存入结构化数据库。
-
存储位置: 外部持久化数据库(磁盘)。
-
优势: 理论上拥有无限容量,且不会占用不必要的 Token。只有当相关信息被触发时,才会被提取出来。
这个我们后面再聊,后面会慢慢介绍向量数据库和RAG。