介绍
LangChain 是一个用于开发由大型语言模型 (LLM) 提供支持的应用程序的框架。
LangChain 简化了 LLM 应用程序生命周期的每个阶段:
- 开发:使用 LangChain 的开源组件和第三方集成构建应用程序。 使用 LangGraph 构建具有一流流式处理和人机回圈支持的有状态代理。
- 产品化:使用 LangSmith 检查、监控和评估您的应用程序,以便您可以放心地持续优化和部署。
- 部署:使用 LangGraph 平台将您的 LangGraph 应用程序转变为生产就绪的 API 和助手。
核心模块 - 旧的(辅助理解)
- Model I/O :标准化各个大模型的输入和输出,包含输入模版,模型本身和格式化输出;
- Retrieval :检索外部数据,然后在执行生成步骤时将其传递到 LLM,包括文档加载、切割、Embedding等;
- Chains :链条,LangChain框架中最重要的模块,链接多个模块协同构建应用,是实际运作很多功能的高级抽象;
- Memory : 记忆模块,以各种方式构建历史信息,维护有关实体及其关系的信息;
- Agents : 目前最热门的Agents开发实践,未来能够真正实现通用人工智能的落地方案;
- Callbacks :回调系统,允许连接到 LLM 应用程序的各个阶段。用于日志记录、监控、流传输和其他任务;
核心模块 - 新的(v0.3)
- langchain-core:聊天模型和其他组件的基本抽象。
- 集成包(例如 、 等):重要的集成已被拆分为轻量级包,由 LangChain 团队和集成开发人员共同维护。langchain-openai,langchain-anthropic
- langchain:构成应用程序认知架构的链、代理和检索策略。
- langchain-community:由社区维护的第三方集成。
- langgraph:编排框架,用于将 LangChain 组件组合成具有持久化、流式处理和其他关键功能的生产就绪型应用程序。请参阅 LangGraph 文档。
一. 构建聊天机器人
从 LangChain v0.3 版本开始,我们建议 LangChain 用户利用 LangGraph 持久性来整合到新的 LangChain 应用程序中。memory
我们将通过一个示例来说明如何设计和实现 LLM 支持的聊天机器人。
这个聊天机器人将能够进行对话并记住之前与聊天模型的互动。
1、常规使用
import os
from dotenv import load_dotenv
load_dotenv()
True
os.environ.get("OPENAI_API_KEY")
'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'
1、初始化模型
import getpass
import os
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4o-mini", model_provider="openai")
我们首先直接使用模型。是 LangChain “Runnables” 的实例,这意味着它们公开了一个用于与它们交互的标准接口。要简单地调用模型,我们可以将消息列表传递给该方法。ChatModel.invoke
2、调用模型,输出结果
字符串方式,LangChain 会自动将它转换为 HumanMessage(content=“…”)。
response = model.invoke("什么是 LangChain!")
# 输出回复内容
print(response)
content='LangChain 是一个用于构建基于语言模型应用的框架。它提供了一系列工具和模块,帮助开发者将不同的语言模型(如 OpenAI 的 GPT 系列)与数据源、API、记忆、和其他功能集成,从而构建复杂的应用程序。这些应用程序可以包括对话代理、信息检索系统、自动化内容生成等。\n\nLangChain 的主要特点包括:\n\n1. **链式结构**:允许开发者将多个组件组合在一起,形成复杂的工作流。每个组件可以执行特定的任务,如文本生成、数据检索等。\n\n2. **集成能力**:可以轻松地连接和使用不同的数据源和 API,增强语言模型的能力,让它不仅依赖于预训练的知识,还能访问实时数据。\n\n3. **可扩展性**:支持插件和扩展,让开发者能够根据具体需求自定义和添加功能。\n\n4. **模块化设计**:提供了多个模块,如总结、提问、聊天、记忆等,方便开发者选择和组合使用。\n\n总的来说,LangChain 旨在使构建基于 AI 的应用程序变得更加高效和灵活。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 261, 'prompt_tokens': 12, 'total_tokens': 273, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZalt4KF3jan1nEoOpt18asMKReow', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='run--c52a8f16-6c9c-4e0a-8e80-d156e1ff7d3d-0' usage_metadata={'input_tokens': 12, 'output_tokens': 261, 'total_tokens': 273, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
print(response.content)
LangChain 是一个用于构建基于语言模型应用的框架。它提供了一系列工具和模块,帮助开发者将不同的语言模型(如 OpenAI 的 GPT 系列)与数据源、API、记忆、和其他功能集成,从而构建复杂的应用程序。这些应用程序可以包括对话代理、信息检索系统、自动化内容生成等。
LangChain 的主要特点包括:
1. **链式结构**:允许开发者将多个组件组合在一起,形成复杂的工作流。每个组件可以执行特定的任务,如文本生成、数据检索等。
2. **集成能力**:可以轻松地连接和使用不同的数据源和 API,增强语言模型的能力,让它不仅依赖于预训练的知识,还能访问实时数据。
3. **可扩展性**:支持插件和扩展,让开发者能够根据具体需求自定义和添加功能。
4. **模块化设计**:提供了多个模块,如总结、提问、聊天、记忆等,方便开发者选择和组合使用。
总的来说,LangChain 旨在使构建基于 AI 的应用程序变得更加高效和灵活。
from IPython.display import Markdown
display(Markdown(response.content))
LangChain 是一个用于构建基于语言模型应用的框架。它提供了一系列工具和模块,帮助开发者将不同的语言模型(如 OpenAI 的 GPT 系列)与数据源、API、记忆、和其他功能集成,从而构建复杂的应用程序。这些应用程序可以包括对话代理、信息检索系统、自动化内容生成等。
LangChain 的主要特点包括:
-
链式结构:允许开发者将多个组件组合在一起,形成复杂的工作流。每个组件可以执行特定的任务,如文本生成、数据检索等。
-
集成能力:可以轻松地连接和使用不同的数据源和 API,增强语言模型的能力,让它不仅依赖于预训练的知识,还能访问实时数据。
-
可扩展性:支持插件和扩展,让开发者能够根据具体需求自定义和添加功能。
-
模块化设计:提供了多个模块,如总结、提问、聊天、记忆等,方便开发者选择和组合使用。
总的来说,LangChain 旨在使构建基于 AI 的应用程序变得更加高效和灵活。
消息列表方式(List of BaseMessage)调用,这是 LangChain 标准推荐的格式。
from langchain_core.messages import HumanMessage
response = model.invoke([HumanMessage(content="什么是 LangGraph!")])
display(Markdown(response.content))
LangGraph 是一种新兴的技术或工具,它主要集中于语言处理和图形结构的结合。虽然截至我知识的截止日期(2023年10月),LangGraph 具体的定义和应用可能会有所不同,通常这类技术涉及利用图形数据库或图数据结构来增强自然语言处理(NLP)任务。
在这种框架下,LangGraph 可能用于以下几个方面:
-
知识图谱:通过将语言数据映射到图形结构,可以更好地表示和查询知识,比如实体和其属性之间的关系。
-
文本分析:利用图结构来分析文本中的关系,挖掘隐藏的信息和模式。
-
推理与问答:基于图形的结构进行更复杂的推理处理,提升问答系统的准确性。
-
多模态学习:结合图形和文本数据,进行综合分析,提升理解和生成的能力。
如果您有更具体的问题或者需要了解 LangGraph 在某个特定领域的应用,请告诉我!
3、多轮对话
将整个对话历史传递到模型中
from langchain_core.messages import AIMessage
response = model.invoke(
[
HumanMessage(content="嗨!我是文武"),
AIMessage(content="文武您好,有什么需要帮忙的吗?"),
HumanMessage(content="我叫什么名字?"),
]
)
display(Markdown(response.content))
你刚刚说你的名字是文武。有什么特别想聊的或者问的吗?
4、消息持久化
LangGraph 实现了一个内置的持久层,使其成为支持多个对话轮次的聊天应用程序的理想选择。
将我们的聊天模型包装在一个最小的 LangGraph 应用程序中,使我们能够自动持久化消息历史记录,从而简化多轮次应用程序的开发。
LangGraph 带有一个简单的内存检查点程序,我们在下面使用。有关更多详细信息,包括如何使用不同的持久化后端(例如 SQLite 或 Postgres),请参阅其相关文档。
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
# 定义一个新的 LangGraph 工作流图对象,使用消息状态作为状态结构
workflow = StateGraph(state_schema=MessagesState)
# 定义调用语言模型的函数,该函数接收当前对话消息状态,并调用模型生成回复
def call_model(state: MessagesState):
# 使用 LangChain 模型调用接口,根据已有对话生成回复
response = model.invoke(state["messages"])
# 返回一个包含新 AI 回复的状态字典(LangGraph 要求返回 dict)
return {"messages": response}
# 向图中添加边:从起始点 START 连接到 “model” 节点,表示流程开始后直接调用模型
workflow.add_edge(START, "model")
# 添加一个名为 “model” 的节点,绑定到前面定义的 call_model 函数
workflow.add_node("model", call_model)
# 配置图的“记忆模块”,用于在执行过程中保存和恢复对话状态(如缓存机制)
memory = MemorySaver()
# 编译工作流图,绑定记忆系统,得到一个可运行的对话图应用 app
app = workflow.compile(checkpointer=memory)
现在我们需要创建一个配置,每次都会传递给 Runnable 实例。这个配置包含一些不直接包含在输入中但仍然有用的信息。在本例中,我们希望包含一个 thread_id。它应该如下所示:
config = {"configurable": {"thread_id": "abc123"}}
这使我们能够使用单个应用程序支持多个对话线程,这是应用程序具有多个用户时的常见要求。
然后我们可以调用应用程序:
# 1、告诉他我的名字
query = "嗨!我是文武."
# 将用户输入的字符串封装为一条【人类消息(HumanMessage)】,以便传入对话状态
input_messages = [HumanMessage(query)]
# 调用编译好的 LangGraph 应用,将初始对话状态作为参数传入
# 这里的状态 state 字典中必须包含键 "messages",表示对话历史消息
# config 是调用过程的配置项(如运行名称、追踪标签、元数据等)
output = app.invoke({"messages": input_messages}, config)
# 输出最终对话中的最后一条消息,并以美观格式打印(pretty_print 会自动加颜色和格式)
# output["messages"] 是消息列表,[-1] 表示取最新的 AI 回复
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
嗨,文武!很高兴认识你!有什么我可以帮助你的吗?
# 2、问个其他问题
query = "什么是 LangSmith?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
LangSmith 是一个支持开发和管理大语言模型(LLM)应用的平台。它提供了一种工具和框架,帮助开发者更轻松地构建、测试和优化基于自然语言处理的应用。LangSmith 的核心功能通常包括:
1. **模型管理**:帮助用户管理不同版本的语言模型,确保在开发过程中可以灵活切换和调整。
2. **测试和验证**:提供工具来测试模型的性能,确保其在实际应用中的准确性和可靠性。
3. **反馈收集**:允许用户收集来自实际使用中的反馈,帮助改进和迭代模型。
4. **集成与部署**:简化将模型集成到实际应用中的过程,支持快速部署。
LangSmith 的目标是通过简化和优化 LLM 的开发流程,使开发者能够更高效地构建出智能应用。如果你对具体的功能或者使用案例感兴趣,欢迎告诉我!
# 3、隔了其他问题,然后在问我的名字
query = "我叫什么名字?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
你叫文武!如果你有其他问题或需要进一步的帮助,请随时告诉我!
成功记住了我的名字!我们的聊天机器人现在记住了关于我的信息。如果我们更改配置以引用不同的 ,我们可以看到它开始了全新的对话。thread_id
config = {"configurable": {"thread_id": "abc234"}}
query = "我叫什么名字?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
我不知道你的名字。如果你愿意,可以告诉我你的名字!
但是,我们总是可以返回到原始对话(因为我们将其持久化在数据库中)
我们在看看thread_id=abc123
config = {"configurable": {"thread_id": "abc123"}}
query = "我叫什么名字?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
你叫文武。如果你还有其他问题或需要更多的帮助,请告诉我!
5、异步方式
# 用于节点的异步函数:
async def call_model(state: MessagesState):
response = await model.ainvoke(state["messages"])
return {"messages": response}
# 像之前一样定义图:
workflow = StateGraph(state_schema=MessagesState)
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
app = workflow.compile(checkpointer=MemorySaver())
# 异步调用:
output = await app.ainvoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
2、提示词模版 Prompt Templates
提示模板有助于将原始用户信息转换为 LLM 可以处理的格式。在本例中,原始用户输入只是一条消息,我们会将其传递给 LLM。现在让我们让它变得更复杂一些。
- 首先,我们添加一条包含一些自定义指令的系统消息(但仍将消息作为输入)。
- 接下来,除了消息之外,我们还将添加更多输入。
1、简单使用
要添加系统消息,我们将创建一个 ChatPromptTemplate。我们将使用 MessagesPlaceholder 来传递所有消息。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 创建一个聊天提示模板,指定消息格式
prompt_template = ChatPromptTemplate.from_messages(
[
# 系统消息:设定 AI 的说话风格为“像海盗一样说话”
(
"system",
"你说话像海盗一样。请尽力回答所有问题。",
),
# 占位符:用于动态插入用户与 AI 的对话消息
MessagesPlaceholder(variable_name="messages"),
]
)
# 更新应用程序以合并此模板:
workflow = StateGraph(state_schema=MessagesState)
def call_model(state: MessagesState):
# 根据当前状态(包含消息)生成提示(prompt)
prompt = prompt_template.invoke(state)
# 使用模型对生成的提示进行推理,获得回复
response = model.invoke(prompt)
return {"messages": response}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
# 编译工作流为可调用的应用程序
app = workflow.compile(checkpointer=memory)
# 调用
config = {"configurable": {"thread_id": "abc345"}}
query = "嗨!我是吉姆。"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
Ahoy, 吉姆!欢迎 aboard!如何能够助你?需要寻找宝藏,还是讲述神秘的海盗故事?☠️🏴☠️
query = "我叫什么名字?"
input_messages = [HumanMessage(query)]
output = app.invoke({"messages": input_messages}, config)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
Ye be callin' yerself 吉姆, savvy? 还想问些什么,勇敢的水手?☠️🦜
2、复杂一点,带有变量
很好,现在让我们的提示稍微复杂一点。我们假设提示模板现在如下所示:
prompt_template = ChatPromptTemplate.from_messages(
[
(
"system",
"您是一位乐于助人的助手。请尽力用{language}回答所有问题。",
),
MessagesPlaceholder(variable_name="messages"),
]
)
请注意,我们在提示框中添加了新的语言[language 变量]输入。我们的应用现在有两个参数——输入消息和语言[language 变量](messages and language)。我们应该更新应用的状态以反映这一点:
from typing import Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing_extensions import Annotated, TypedDict
# 定义工作流的状态结构,包括消息序列和语言字段
class State(TypedDict):
# messages 是 BaseMessage 类型的序列,使用 Annotated 添加额外元信息(如支持追加消息)
messages: Annotated[Sequence[BaseMessage], add_messages]
# language 表示语言类型,例如 "英文" 或 "中文"
language: str
workflow = StateGraph(state_schema=State)
def call_model(state: State):
prompt = prompt_template.invoke(state)
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
config = {"configurable": {"thread_id": "abc456"}}
query = "你好,我叫文武"
# 指定目标语言为英文(用于提示模板中处理语言的逻辑)
language = "English"
input_messages = [HumanMessage(query)]
# 调用工作流应用程序,传入用户消息和语言,同时使用配置中的 thread_id
output = app.invoke(
{"messages": input_messages, "language": language},
config,
)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
Hello, Wenwu! How can I assist you today?
请注意,整个状态是持久的,因此我们可以省略参数,例如如果不需要更改:language
query = "我叫什么名字?"
input_messages = [HumanMessage(query)]
output = app.invoke(
{"messages": input_messages},
config,
)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
Your name is Wenwu. How can I help you today, Wenwu?
3、管理对话历史记录
构建聊天机器人时,需要理解的一个重要概念是如何管理对话历史记录。如果不加以管理,消息列表将无限增长,并可能溢出 LLM 的上下文窗口。因此,添加一个步骤来限制传入消息的大小非常重要。
重要的是,您需要在提示模板之前、但在从消息历史记录中加载之前的消息之后执行此操作。
我们可以通过在提示符前添加一个简单的步骤来实现这一点,该步骤会适当地修改 messages 键,然后将这个新的链包装在 Message History 类中。
LangChain 附带一些用于管理消息列表的内置助手。在本例中,我们将使用 trim_messages 助手来减少发送给模型的消息数量。修剪器允许我们指定要保留的标记数量,以及其他参数,例如是否要始终保留系统消息以及是否允许部分消息:
from langchain_core.messages import SystemMessage, trim_messages
# 创建消息修剪器(trimmer),用于在达到最大 token 数限制时裁剪对话历史
trimmer = trim_messages(
max_tokens=65, # 设置最大 token 数限制为 65
strategy="last", # 使用 “last” 策略,优先保留最新消息,删除较早的
token_counter=model, # 使用模型本身进行 token 计数(需支持 counting)
include_system=True, # 保留 SystemMessage(系统消息)
allow_partial=False, # 不允许截断一条消息的内容,只允许整条删除
start_on="human", # 从第一条 HumanMessage 开始考虑裁剪
)
# 构造一组模拟的对话消息(包括系统消息、人类消息和 AI 回复)
messages = [
SystemMessage(content="你是一个很棒的助手"), # 系统设定
HumanMessage(content="嗨!我是文武"), # 人类消息
AIMessage(content="你好!"), # AI 回复
HumanMessage(content="我喜欢香草味冰淇淋"),
AIMessage(content="不错哦"),
HumanMessage(content="2 + 2 等于多少"),
AIMessage(content="是 4"),
HumanMessage(content="谢谢你"),
AIMessage(content="不客气!"),
HumanMessage(content="你开心吗?"),
AIMessage(content="很开心!"),
]
# 调用修剪器,裁剪超过 token 限制的对话内容
trimmer.invoke(messages)
[SystemMessage(content='你是一个很棒的助手', additional_kwargs={}, response_metadata={}),
HumanMessage(content='2 + 2 等于多少', additional_kwargs={}, response_metadata={}),
AIMessage(content='是 4', additional_kwargs={}, response_metadata={}),
HumanMessage(content='谢谢你', additional_kwargs={}, response_metadata={}),
AIMessage(content='不客气!', additional_kwargs={}, response_metadata={}),
HumanMessage(content='你开心吗?', additional_kwargs={}, response_metadata={}),
AIMessage(content='很开心!', additional_kwargs={}, response_metadata={})]
trimmed = trimmer.invoke(messages)
for msg in trimmed:
print(msg)
content='你是一个很棒的助手' additional_kwargs={} response_metadata={}
content='2 + 2 等于多少' additional_kwargs={} response_metadata={}
content='是 4' additional_kwargs={} response_metadata={}
content='谢谢你' additional_kwargs={} response_metadata={}
content='不客气!' additional_kwargs={} response_metadata={}
content='你开心吗?' additional_kwargs={} response_metadata={}
content='很开心!' additional_kwargs={} response_metadata={}
为了在我们的链中使用它,我们只需要在将消息messages输入传递给提示之前运行修剪器。
workflow = StateGraph(state_schema=State)
def call_model(state: State):
# 调用修剪器,对传入的消息列表进行裁剪(控制 token 数量)
trimmed_messages = trimmer.invoke(state["messages"])
# 使用修剪后的消息和语言信息生成 prompt(输入提示)
prompt = prompt_template.invoke(
{
"messages": trimmed_messages, # 对话历史(已修剪)
"language": state["language"], # 指定语言(如 "English")
}
)
response = model.invoke(prompt)
return {"messages": [response]}
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(checkpointer=memory)
现在,如果我们尝试向模型询问我们的名字,它不会知道它,因为我们修剪了聊天记录的那部分:
config = {"configurable": {"thread_id": "abc567"}}
query = "我叫什么名字?"
# 指定对话语言为英文(这个字段可以用于 prompt 模板中控制语言风格)
language = "English"
# 将先前定义的一组对话消息(messages)与新的用户提问合并,构成输入消息列表
input_messages = messages + [HumanMessage(query)]
# 调用编译后的 LangGraph 应用,传入当前消息和语言,同时提供配置参数
output = app.invoke(
{"messages": input_messages, "language": language},
config,
)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
I'm sorry, but I don't know your name. If you'd like to share it, feel free!
但是,如果我们询问最后几次问答的信息,它会记住:
config = {"configurable": {"thread_id": "abc678"}}
query = "我问了什么数学题?"
language = "English"
input_messages = messages + [HumanMessage(query)]
output = app.invoke(
{"messages": input_messages, "language": language},
config,
)
output["messages"][-1].pretty_print()
==================================[1m Ai Message [0m==================================
I'm sorry, but I don't have access to previous conversations or questions. If you have a specific math question now, feel free to ask!
4、流式输出 Streaming
现在我们已经有了一个可以运行的聊天机器人。然而,对于聊天机器人应用程序来说,一个非常重要的用户体验考虑因素是流式传输。LLM 有时可能需要一段时间才能响应,因此为了提升用户体验,大多数应用程序都会在每个 token 生成时将其流式传输回来。这可以让用户看到进度。
其实这非常简单!
默认情况下,我们 LangGraph 应用程序中的 .stream 会流式传输应用程序步骤——在本例中,是模型响应的单个步骤。设置 stream_mode=“messages” 可以让我们改为流式传输输出 token:
config = {"configurable": {"thread_id": "abc789"}}
query = "嗨,我是文武,请给我讲个笑话。"
# 指定语言为中文
language = "中文"
# 构造输入消息(只包含当前这一轮的用户输入)
input_messages = [HumanMessage(query)]
# 使用 app.stream 进行流式处理,按消息粒度返回生成结果
for chunk, metadata in app.stream(
{"messages": input_messages, "language": language}, # 输入内容:用户消息和语言
config, # 配置参数(含 thread_id)
stream_mode="messages", # 设置流式模式为按“消息”流出
):
# 仅打印模型生成的回复内容(忽略其他非 AIMessage 类型)
if isinstance(chunk, AIMessage): # 过滤,只处理 AI 返回的消息块
print(chunk.content, end="|") # 输出内容,以 | 分隔不同消息块
|嗨|,|文|武|!|好的|,我|来|给|你|讲|个|笑|话|:
|有|一天|,一|只|乌|龟|走|进|一家|酒|吧|,|点|了一|杯|酒|。| bartender| |看到|乌|龟|进|来|,|感|到|很|惊|讶|,于|是|就|问|:“|你|为什么|会|走|得|这么|慢|?”|乌|龟|回答|:“|因为|我|不|想|被|你|们|的|酒|醉|了|!”
|希望|你|喜欢|这个|笑|话|!||
二、构建代理 End-to-end agent
语言模型本身无法执行操作——它们只能输出文本。LangChain 的一个重要用例是创建代理。代理是一种使用 LLM 作为推理引擎的系统,用于确定要采取的操作以及执行操作所需的输入。执行操作后,结果可以反馈给 LLM,以确定是否需要执行更多操作,或者是否可以完成。这通常通过工具调用来实现。
在本教程中,我们将构建一个可以与搜索引擎交互的代理。您将能够向该代理提问,观察它调用搜索工具,并与其进行对话。
1、端到端代理
以下代码片段展示了一个功能齐全的代理,它使用 LLM 来决定要使用的工具。它配备了通用搜索工具。它具有对话记忆功能,这意味着它可以用作多轮聊天机器人。
在本指南的其余部分,我们将介绍各个组件以及每个部分的功能 - 但如果您只想获取一些代码并开始使用,请随意使用它!
# 导入所需功能模块
from langchain.chat_models import init_chat_model, ChatOpenAI # 使用 OpenAI 的 GPT 模型
from langchain_anthropic import ChatAnthropic # 使用 Anthropic 的 Claude 模型
from langchain_community.tools.tavily_search import TavilySearchResults # 引入 Tavily 搜索工具
from langchain_core.messages import HumanMessage # 用于构造人类输入消息
from langgraph.checkpoint.memory import MemorySaver # 用于在图执行中保存中间状态
from langgraph.prebuilt import create_react_agent # 创建一个基于 ReAct 框架的智能体
# 创建智能体所需的记忆模块(用于记录 agent 的状态和步骤)
memory = MemorySaver()
# 实例化大模型
# model = ChatAnthropic(model_name="claude-3-sonnet-20240229")
model = init_chat_model("gpt-4o-mini", model_provider="openai")
# 创建 Tavily 搜索工具实例,设置最多返回2个搜索结果
search = TavilySearchResults(max_results=2)
# 将搜索工具打包成一个工具列表,供智能体调用
tools = [search]
# 使用模型、工具和记忆功能创建一个基于 ReAct 的 agent 执行器
agent_executor = create_react_agent(model, tools, checkpointer=memory)
# 使用 agent
config = {"configurable": {"thread_id": "abc123"}}
# 使用 agent 执行器以流式(streaming)模式处理用户输入
for step in agent_executor.stream(
{"messages": [HumanMessage(content="你好 我是文武,我住在北京。")]}, # 用户发送的消息内容
config, # 传入配置(如 thread_id)用于识别上下文
stream_mode="values", # 设置为流式返回值模式,逐步获取 agent 执行的每一步
):
# 打印当前步骤中的最后一条消息(通常是 agent 的响应)
step["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
你好 我是文武,我住在北京。
==================================[1m Ai Message [0m==================================
你好,文武!很高兴认识你。你住在北京,最近的天气怎么样?或者你有什么想聊的话题吗?
# 使用 agent 执行器以流式模式(streaming)处理用户提问
for step in agent_executor.stream(
# 构造输入消息,用户询问“我所在的地方天气如何?”
{"messages": [HumanMessage(content="我住的地方天气怎么样?")]},
# 上下文配置,包含线程 ID,可用于多轮对话的状态追踪
config,
# 设置流式返回模式为 "values",表示返回每一步的值(如模型生成、工具调用等)
stream_mode="values",
):
step["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
我住的地方天气怎么样?
==================================[1m Ai Message [0m==================================
Tool Calls:
tavily_search_results_json (call_OPWCGFLKobe72O1WrBI1PKyH)
Call ID: call_OPWCGFLKobe72O1WrBI1PKyH
Args:
query: 北京天气
=================================[1m Tool Message [0m=================================
Name: tavily_search_results_json
[{"title": "中国气象局-天气预报- 北京", "url": "https://weather.cma.cn/web/weather/54511.html", "content": "æ—¶é—´ | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00 | 08:00\n天气 | | | | | | | | \n气温 | 11.4℃ | 11.5℃ | 11℃ | 10.6℃ | 10.2℃ | 10.6℃ | 11℃ | 12.8℃\n陿°´ | 0.5mm | 11.3mm | 8mm | 4.3mm | 1.5mm | 0.4mm | æ— é™æ°´ | æ— é™æ°´\n风速 | 7.3m/s | 7.9m/s | 6.4m/s | 5.8m/s | 3.3m/s | 3m/s | 2.2m/s | 3.3m/s\né£Žå‘ | 东北风 | 东北风 | 东北风 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风 [...] é£Žå‘ | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西北风\n气压 | 1006.8hPa | 1005.1hPa | 1001.7hPa | 998.2hPa | 997.4hPa | 998.4hPa | 997.9hPa | 997.7hPa\n湿度 | 50.4% | 20.9% | 26.3% | 39.3% | 42.1% | 50.6% | 61.3% | 72.8%\näº‘é‡ | 3.8% | 3.3% | 10% | 10% | 10% | 78.8% | 56.4% | 27.1%\næ—¶é—´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | [...] 气压 | 1010.1hPa | 1010hPa | 1010.6hPa | 1012.4hPa | 1012.7hPa | 1010.6hPa | 1010.9hPa | 1012.3hPa\n湿度 | 93.5% | 95.8% | 98% | 96.9% | 95.8% | 88% | 89.3% | 83.9%\näº‘é‡ | 95.2% | 92.2% | 97.1% | 88.2% | 92.1% | 94.9% | 82.5% | 73.4%\næ—¶é—´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | \n气温 | 12.8℃ | 22.5℃ | 22.8℃ | 21.9℃ | 18.3℃ | 15.5℃ | 13.3℃ | 12.2℃", "score": 0.7576693}, {"title": "预报- 北京 - 中国天气网", "url": "https://www.weather.com.cn/weather/101010100.shtml", "content": "* 19日(今天)\n晴\n31/18℃\n<3级\n* 20日(明天)\n晴转多云\n33/20℃\n<3级\n* 21日(后天)\n多云\n29/18℃\n<3级\n* 22日(周四)\n阴转小雨\n26/17℃\n<3级\n* 23日(周五)\n晴\n23/16℃\n<3级\n* 24日(周六)\n晴\n26/18℃\n<3级\n* 25日(周日)\n多云\n27/19℃\n<3级\n分时段预报 生活指数\n蓝天预报\n蓝天预报综合天气现象、能见度、空气质量等因子,预测未来一周的天空状况。\n\n天空蔚蓝可见透彻蓝天,或有蓝天白云美景。\n天空淡蓝天空不够清澈,以浅蓝色为主。\n天空阴沉阴天或有雨雪,天空灰暗。\n天空灰霾出现霾或沙尘,天空灰蒙浑浊。\n\n11时 14时 17时 20时 23时 02时 05时 08时\n26℃ 28℃ 30℃ 25℃ 21℃ 19℃ 18℃ 22℃\n东南风 南风 南风 南风 东南风 东风 东北风 东北风\n<3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级\n\n\n少发 感冒指数\n感冒机率较低,避免长期处于空调屋中。\n\n\n较适宜 运动指数\n请适当减少运动时间,降低运动强度。", "score": 0.7304634}]
==================================[1m Ai Message [0m==================================
今天北京的天气预报如下:
- **今天(19日)**:晴,最高气温31°C,最低气温18°C,风速小于3级。
- **明天(20日)**:晴转多云,最高气温33°C,最低气温20°C,风速小于3级。
- **后天(21日)**:多云,最高气温29°C,最低气温18°C,风速小于3级。
- **22日(周四)**:阴转小雨,最高气温26°C,最低气温17°C,风速小于3级。
- **23日(周五)**:晴,最高气温23°C,最低气温16°C,风速小于3级。
- **24日(周六)**:晴,最高气温26°C,最低气温18°C,风速小于3级。
- **25日(周日)**:多云,最高气温27°C,最低气温19°C,风速小于3级。
今天的气温较高,适合外出,但要注意防晒。如果你想了解更详细的信息,可以查看[这里](https://www.weather.com.cn/weather/101010100.shtml)。你今天有什么计划吗?
2、定义工具 tools
首先,我们需要创建想要使用的工具。我们主要选择的工具是 Tavily 一个搜索引擎。LangChain 内置了一个工具,可以轻松使用 Tavily 搜索引擎。
from langchain_community.tools.tavily_search import TavilySearchResults
# 创建 Tavily 搜索工具实例,设置最多返回 2 条搜索结果
search = TavilySearchResults(max_results=2)
# 使用 search 工具执行一次查询,请求“北京的天气怎么样”
search_results = search.invoke("北京的天气怎么样")
# 打印搜索结果,通常是包含网页标题、摘要、链接等信息的列表
print(search_results)
[{'title': '中国气象局-天气预报- 北京', 'url': 'https://weather.cma.cn/web/weather/54511', 'content': '气温 | 20.8℃ | 23.8℃ | 25.8℃ | 25.6℃ | 20.5℃ | 18.3℃ | 16℃ | 12.2℃\n降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水\n风速 | 7.9m/s | 2.5m/s | 3.3m/s | 2.7m/s | 1.1m/s | 3.1m/s | 3.3m/s | 1.9m/s\n风向 | 西北风 | 西北风 | 西北风 | 西北风 | 东南风 | 东北风 | 东北风 | 东北风\n气压 | 1001.9hPa | 1001.8hPa | 1001.8hPa | 1004hPa | 1006.3hPa | 1007.9hPa | 1009.6hPa | 1010.6hPa\n湿度 | 40.2% | 22.7% | 22.9% | 23.8% | 34.7% | 37.7% | 17.4% | 24.3%\n云量 | 3.1% | 2.4% | 1.8% | 2.5% | 3.1% | 3.9% | 4.6% | 4.4% [...] 湿度 | 72.5% | 30% | 38% | 38.1% | 38.2% | 52% | 56.5% | 68.6%\n云量 | 3.8% | 10.1% | 10.1% | 79.9% | 10.1% | 10.1% | 36.6% | 17.8%\n时间 | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | \n气温 | 14.5℃ | 24℃ | 26.8℃ | 23.9℃ | 21℃ | 11.4℃ | 8.2℃ | 9.5℃\n降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水 | 无降水\n风速 | 3.2m/s | 7.7m/s | 6.9m/s | 9.9m/s | 10.7m/s | 7.9m/s | 5.3m/s | 6.1m/s\n风向 | 东南风 | 西南风 | 西南风 | 西南风 | 西北风 | 西北风 | 西北风 | 西北风 [...] 风速 | 4.5m/s | 4.5m/s | 3.9m/s | 7.9m/s | 4.8m/s | 7.9m/s | 7m/s | 5.6m/s\n风向 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风\n气压 | 1008.8hPa | 1004.9hPa | 1001.2hPa | 1002.5hPa | 1003.1hPa | 1001.6hPa | 1000.6hPa | 999.1hPa\n湿度 | 33% | 13.2% | 21.4% | 24.7% | 28.6% | 29.6% | 17.8% | 17.3%\n云量 | 6.4% | 10% | 10% | 10% | 10% | 10% | 10% | 10%\n时间 | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | \n气温 | 14.8℃ | 15.6℃ | 16.1℃ | 16.1℃ | 16.1℃ | 16.1℃ | 12.2℃ | 14.8℃', 'score': 0.72278196}, {'title': 'Beijing, Beijing, China Weather Forecast - AccuWeather', 'url': 'https://www.accuweather.com/en/cn/beijing/101924/weather-forecast/101924', 'content': 'Tonight 5/19 64°Lo Clear 1%Tue 5/20 90°69° Very warm with hazy sunshine Night: Mainly clear and warm 2%Wed 5/21 86°63° Mostly sunny Clouds with a shower late 8%Thu 5/22 71°58° Cloudy, showers; cooler Clear 96%Fri 5/23 78°54° Mostly sunny and warmer Mainly clear 0%Sat 5/24 82°56° Sunny and pleasant Partly cloudy 0%Sun 5/25 85°61° Sun through high clouds Mainly clear 0%Mon 5/26 86°62° Mostly cloudy Partly cloudy 0%Tue 5/27 82°61° Mostly cloudy Mainly clear 4%Wed 5/28 85°60° [...] Mostly sunny Mainly clear 5%', 'score': 0.6952351}]
# (可选)如果我们之后还想创建更多工具,可以继续实例化它们
# 例如创建多个搜索、数据库、计算或 API 工具
# 将所有需要使用的工具放入一个列表中,后续创建 agent 时会用到
tools = [search]
3、使用语言模型来调用工具
我们来学习如何使用语言模型来调用工具。LangChain 支持大多数的大语言模型!
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4o-mini", model_provider="openai")
from langchain_core.messages import HumanMessage
response = model.invoke([HumanMessage(content="你好!")])
response.content
'你好! 有什么我可以帮助你的吗?'
现在我们可以看看如何让这个模型进行工具调用。为了实现这一点,我们使用 .bind_tools 来为语言模型提供这些工具的知识。
- tools 是一个包含所有你希望模型能调用的工具(如搜索、计算器等)的列表。
- bind_tools() 会返回一个新的模型对象 model_with_tools,它在推理过程中具有调用这些工具的能力。
# 将工具绑定到语言模型上,使模型可以在推理过程中调用这些工具
model_with_tools = model.bind_tools(tools)
# 向绑定了工具的模型发送一条用户消息:"你好!"
# 如果模型认为需要调用某个工具(比如搜索天气),它会生成 tool_calls。
response = model_with_tools.invoke([HumanMessage(content="你好!")])
# 打印模型回复的内容部分(自然语言回答)
# 模型直接用自然语言生成的响应(适用于不需要工具调用时)。
print(f"ContentString: {response.content}")
# 打印模型生成的工具调用请求(如果有的话)
# 如果模型需要外部信息或执行某个操作,会填充这个字段,指示要调用哪个工具、传什么参数。
print(f"ToolCalls: {response.tool_calls}")
ContentString: 你好! 有什么我可以帮助你的吗?
ToolCalls: []
# 尝试一个能使用到工具的问题
response = model_with_tools.invoke([HumanMessage(content="北京天气怎么样?")])
print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
ContentString:
ToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': '北京天气'}, 'id': 'call_umCrDU89u1t7x3jxepLpRh29', 'type': 'tool_call'}]
我们可以看到现在没有文本内容,但有一个 tool 调用!它希望我们调用 Tavily 搜索工具。
这还不是在调用那个工具 - 它只是告诉我们这样做。为了实际调用它,我们需要创建我们的代理。
4、创建代理 agent
现在我们已经定义了工具和 LLM,可以创建代理了。我们将使用 LangGraph 来构建代理。目前,我们使用一个高级接口来构建代理,但 LangGraph 的优点在于,这个高级接口背后有一个高度可控的低级 API,方便您修改代理逻辑。
现在,我们可以使用 LLM 和工具来初始化代理。
请注意,我们传入的是模型,而不是 model_with_tools。这是因为 create_react_agent 会在后台为我们调用 .bind_tools。
from langgraph.prebuilt import create_react_agent
agent_executor = create_react_agent(model, tools)
运行代理
我们现在可以通过几个查询来运行代理!请注意,目前,这些都是无状态查询(它不会记住之前的交互)。请注意,代理将在交互结束时返回最终状态(包括任何输入,我们稍后将了解如何仅获取输出)。
让我们看看当不需要调用工具时它如何响应:
response = agent_executor.invoke({"messages": [HumanMessage(content="hi!")]})
response["messages"]
[HumanMessage(content='hi!', additional_kwargs={}, response_metadata={}, id='f2dc1a3a-2d82-43e6-9a3f-e169b62bffa9'),
AIMessage(content='Hello! How can I assist you today?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 81, 'total_tokens': 91, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZswMoh8XaPGKEWDIzq7u8bQLuMuF', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--3f992c09-8704-4f8e-b0e4-4059d15e1566-0', usage_metadata={'input_tokens': 81, 'output_tokens': 10, 'total_tokens': 91, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]
现在让我们在一个应该调用该工具的示例上尝试一下
response = agent_executor.invoke(
{"messages": [HumanMessage(content="北京的天气怎么样?")]}
)
response["messages"]
[HumanMessage(content='北京的天气怎么样?', additional_kwargs={}, response_metadata={}, id='5bd4466d-8cae-4e8e-bf75-eea44cc98acf'),
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_DZd4Veq9XHkveheGBaACY0wm', 'function': {'arguments': '{"query":"北京天气"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 84, 'total_tokens': 103, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZsy8eQNz3Zr9nn1w5PgT1xhfMnLY', 'service_tier': 'default', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--76c6bd94-4b9e-4bd3-8397-8342d7016454-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': '北京天气'}, 'id': 'call_DZd4Veq9XHkveheGBaACY0wm', 'type': 'tool_call'}], usage_metadata={'input_tokens': 84, 'output_tokens': 19, 'total_tokens': 103, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),
ToolMessage(content='SSLError(MaxRetryError("HTTPSConnectionPool(host=\'api.tavily.com\', port=443): Max retries exceeded with url: /search (Caused by SSLError(SSLEOFError(8, \'[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1006)\')))"))', name='tavily_search_results_json', id='670f1b60-6527-4aac-91b8-fa73496b5d72', tool_call_id='call_DZd4Veq9XHkveheGBaACY0wm', artifact={}),
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_sVT4WCEqAIBONTsOIXx6W6DI', 'function': {'arguments': '{"query":"北京天气预报"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 185, 'total_tokens': 206, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZsyBrQYqAfakBwOK86oVTosBOA82', 'service_tier': 'default', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--bbe34a5c-2548-43c2-9afe-f71fc1f1b63a-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': '北京天气预报'}, 'id': 'call_sVT4WCEqAIBONTsOIXx6W6DI', 'type': 'tool_call'}], usage_metadata={'input_tokens': 185, 'output_tokens': 21, 'total_tokens': 206, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),
ToolMessage(content='[{"title": "中国气象局-天气预报- 北京", "url": "https://weather.cma.cn/web/weather/54511.html", "content": "æ\x97¶é\x97´ | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00 | 08:00\\n天æ°\x94 | | | | | | | | \\næ°\x94温 | 11.4â\x84\x83 | 11.5â\x84\x83 | 11â\x84\x83 | 10.6â\x84\x83 | 10.2â\x84\x83 | 10.6â\x84\x83 | 11â\x84\x83 | 12.8â\x84\x83\\né\x99\x8dæ°´ | 0.5mm | 11.3mm | 8mm | 4.3mm | 1.5mm | 0.4mm | æ\x97\xa0é\x99\x8dæ°´ | æ\x97\xa0é\x99\x8dæ°´\\né£\x8eé\x80\x9f | 7.3m/s | 7.9m/s | 6.4m/s | 5.8m/s | 3.3m/s | 3m/s | 2.2m/s | 3.3m/s\\né£\x8eå\x90\x91 | ä¸\x9cå\x8c\x97é£\x8e | ä¸\x9cå\x8c\x97é£\x8e | ä¸\x9cå\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e [...] é£\x8eå\x90\x91 | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8c\x97é£\x8e\\næ°\x94å\x8e\x8b | 1006.8hPa | 1005.1hPa | 1001.7hPa | 998.2hPa | 997.4hPa | 998.4hPa | 997.9hPa | 997.7hPa\\n湿度 | 50.4% | 20.9% | 26.3% | 39.3% | 42.1% | 50.6% | 61.3% | 72.8%\\näº\x91é\x87\x8f | 3.8% | 3.3% | 10% | 10% | 10% | 78.8% | 56.4% | 27.1%\\næ\x97¶é\x97´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\\n天æ°\x94 | | | | | | | | [...] æ°\x94å\x8e\x8b | 1010.1hPa | 1010hPa | 1010.6hPa | 1012.4hPa | 1012.7hPa | 1010.6hPa | 1010.9hPa | 1012.3hPa\\n湿度 | 93.5% | 95.8% | 98% | 96.9% | 95.8% | 88% | 89.3% | 83.9%\\näº\x91é\x87\x8f | 95.2% | 92.2% | 97.1% | 88.2% | 92.1% | 94.9% | 82.5% | 73.4%\\næ\x97¶é\x97´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\\n天æ°\x94 | | | | | | | | \\næ°\x94温 | 12.8â\x84\x83 | 22.5â\x84\x83 | 22.8â\x84\x83 | 21.9â\x84\x83 | 18.3â\x84\x83 | 15.5â\x84\x83 | 13.3â\x84\x83 | 12.2â\x84\x83", "score": 0.80442166}, {"title": "预报- 北京 - 中国天气网", "url": "https://www.weather.com.cn/weather/101010100.shtml", "content": "* 19日(今天)\\n晴\\n31/18℃\\n<3级\\n* 20日(明天)\\n晴转多云\\n33/20℃\\n<3级\\n* 21日(后天)\\n多云\\n29/18℃\\n<3级\\n* 22日(周四)\\n阴转小雨\\n26/17℃\\n<3级\\n* 23日(周五)\\n晴\\n23/16℃\\n<3级\\n* 24日(周六)\\n晴\\n26/18℃\\n<3级\\n* 25日(周日)\\n多云\\n27/19℃\\n<3级\\n分时段预报 生活指数\\n蓝天预报\\n蓝天预报综合天气现象、能见度、空气质量等因子,预测未来一周的天空状况。\\n\\n天空蔚蓝可见透彻蓝天,或有蓝天白云美景。\\n天空淡蓝天空不够清澈,以浅蓝色为主。\\n天空阴沉阴天或有雨雪,天空灰暗。\\n天空灰霾出现霾或沙尘,天空灰蒙浑浊。\\n\\n11时 14时 17时 20时 23时 02时 05时 08时\\n26℃ 28℃ 30℃ 25℃ 21℃ 19℃ 18℃ 22℃\\n东南风 南风 南风 南风 东南风 东风 东北风 东北风\\n<3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级\\n\\n\\n少发 感冒指数\\n感冒机率较低,避免长期处于空调屋中。\\n\\n\\n较适宜 运动指数\\n请适当减少运动时间,降低运动强度。", "score": 0.7474137}]', name='tavily_search_results_json', id='a354cb06-dab4-4df7-aa4d-95bbef04acf3', tool_call_id='call_sVT4WCEqAIBONTsOIXx6W6DI', artifact={'query': '北京天气预报', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'url': 'https://weather.cma.cn/web/weather/54511.html', 'title': '中国气象局-天气预报- 北京', 'content': 'æ\x97¶é\x97´ | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00 | 08:00\n天æ°\x94 | | | | | | | | \næ°\x94温 | 11.4â\x84\x83 | 11.5â\x84\x83 | 11â\x84\x83 | 10.6â\x84\x83 | 10.2â\x84\x83 | 10.6â\x84\x83 | 11â\x84\x83 | 12.8â\x84\x83\né\x99\x8dæ°´ | 0.5mm | 11.3mm | 8mm | 4.3mm | 1.5mm | 0.4mm | æ\x97\xa0é\x99\x8dæ°´ | æ\x97\xa0é\x99\x8dæ°´\né£\x8eé\x80\x9f | 7.3m/s | 7.9m/s | 6.4m/s | 5.8m/s | 3.3m/s | 3m/s | 2.2m/s | 3.3m/s\né£\x8eå\x90\x91 | ä¸\x9cå\x8c\x97é£\x8e | ä¸\x9cå\x8c\x97é£\x8e | ä¸\x9cå\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e | 西å\x8c\x97é£\x8e [...] é£\x8eå\x90\x91 | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8d\x97é£\x8e | 西å\x8c\x97é£\x8e\næ°\x94å\x8e\x8b | 1006.8hPa | 1005.1hPa | 1001.7hPa | 998.2hPa | 997.4hPa | 998.4hPa | 997.9hPa | 997.7hPa\n湿度 | 50.4% | 20.9% | 26.3% | 39.3% | 42.1% | 50.6% | 61.3% | 72.8%\näº\x91é\x87\x8f | 3.8% | 3.3% | 10% | 10% | 10% | 78.8% | 56.4% | 27.1%\næ\x97¶é\x97´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天æ°\x94 | | | | | | | | [...] æ°\x94å\x8e\x8b | 1010.1hPa | 1010hPa | 1010.6hPa | 1012.4hPa | 1012.7hPa | 1010.6hPa | 1010.9hPa | 1012.3hPa\n湿度 | 93.5% | 95.8% | 98% | 96.9% | 95.8% | 88% | 89.3% | 83.9%\näº\x91é\x87\x8f | 95.2% | 92.2% | 97.1% | 88.2% | 92.1% | 94.9% | 82.5% | 73.4%\næ\x97¶é\x97´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天æ°\x94 | | | | | | | | \næ°\x94温 | 12.8â\x84\x83 | 22.5â\x84\x83 | 22.8â\x84\x83 | 21.9â\x84\x83 | 18.3â\x84\x83 | 15.5â\x84\x83 | 13.3â\x84\x83 | 12.2â\x84\x83', 'score': 0.80442166, 'raw_content': None}, {'url': 'https://www.weather.com.cn/weather/101010100.shtml', 'title': '预报- 北京 - 中国天气网', 'content': '* 19日(今天)\n晴\n31/18℃\n<3级\n* 20日(明天)\n晴转多云\n33/20℃\n<3级\n* 21日(后天)\n多云\n29/18℃\n<3级\n* 22日(周四)\n阴转小雨\n26/17℃\n<3级\n* 23日(周五)\n晴\n23/16℃\n<3级\n* 24日(周六)\n晴\n26/18℃\n<3级\n* 25日(周日)\n多云\n27/19℃\n<3级\n分时段预报 生活指数\n蓝天预报\n蓝天预报综合天气现象、能见度、空气质量等因子,预测未来一周的天空状况。\n\n天空蔚蓝可见透彻蓝天,或有蓝天白云美景。\n天空淡蓝天空不够清澈,以浅蓝色为主。\n天空阴沉阴天或有雨雪,天空灰暗。\n天空灰霾出现霾或沙尘,天空灰蒙浑浊。\n\n11时 14时 17时 20时 23时 02时 05时 08时\n26℃ 28℃ 30℃ 25℃ 21℃ 19℃ 18℃ 22℃\n东南风 南风 南风 南风 东南风 东风 东北风 东北风\n<3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级\n\n\n少发 感冒指数\n感冒机率较低,避免长期处于空调屋中。\n\n\n较适宜 运动指数\n请适当减少运动时间,降低运动强度。', 'score': 0.7474137, 'raw_content': None}], 'response_time': 2.97}),
AIMessage(content='今天北京的天气情况如下:\n\n- **19日(今天)**: 晴,最高温度 31℃,最低温度 18℃,风速小于3级。\n- **20日(明天)**: 晴转多云,最高温度 33℃,最低温度 20℃,风速小于3级。\n- **21日(后天)**: 多云,最高温度 29℃,最低温度 18℃,风速小于3级。\n- **22日(周四)**: 阴转小雨,最高温度 26℃,最低温度 17℃,风速小于3级。\n- **23日(周五)**: 晴,最高温度 23℃,最低温度 16℃,风速小于3级。\n- **24日(周六)**: 晴,最高温度 26℃,最低温度 18℃,风速小于3级。\n- **25日(周日)**: 多云,最高温度 27℃,最低温度 19℃,风速小于3级。\n\n详细信息可以参考[中国天气网](https://www.weather.com.cn/weather/101010100.shtml)。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 273, 'prompt_tokens': 1837, 'total_tokens': 2110, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZsyHGhCG5HgOj0BNKDx4GpThDix9', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--0d546cd9-3a9e-4ae6-a3cd-47c3028e730b-0', usage_metadata={'input_tokens': 1837, 'output_tokens': 273, 'total_tokens': 2110, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]
1、流式处理消息 Streaming Messages
我们已经了解了如何使用 .invoke 调用代理来获取最终响应。如果代理执行多个步骤,这可能需要一段时间。为了显示中间进度,我们可以在消息发生时流式地返回它们。
# 使用 agent 执行器以流式模式处理用户的问题:“北京的天气怎么样?”
for step in agent_executor.stream(
{"messages": [HumanMessage(content="北京的天气怎么样?")]},
stream_mode="values",# 使用“value流式”模式,逐步获取 Agent 推理过程中的每个阶段(如思考、调用工具、最终回答等)
):
# 取出每个步骤中的最后一条消息(一般是语言模型或工具的响应)
step["messages"][-1].pretty_print()
================================[1m Human Message [0m=================================
北京的天气怎么样?
==================================[1m Ai Message [0m==================================
Tool Calls:
tavily_search_results_json (call_1UbVHY97k0Cq7NpKlWVxn2rp)
Call ID: call_1UbVHY97k0Cq7NpKlWVxn2rp
Args:
query: 北京天气
=================================[1m Tool Message [0m=================================
Name: tavily_search_results_json
[{"title": "中国气象局-天气预报- 北京", "url": "https://weather.cma.cn/web/weather/54511.html", "content": "æ—¶é—´ | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00 | 08:00\n天气 | | | | | | | | \n气温 | 11.4℃ | 11.5℃ | 11℃ | 10.6℃ | 10.2℃ | 10.6℃ | 11℃ | 12.8℃\n陿°´ | 0.5mm | 11.3mm | 8mm | 4.3mm | 1.5mm | 0.4mm | æ— é™æ°´ | æ— é™æ°´\n风速 | 7.3m/s | 7.9m/s | 6.4m/s | 5.8m/s | 3.3m/s | 3m/s | 2.2m/s | 3.3m/s\né£Žå‘ | 东北风 | 东北风 | 东北风 | 西北风 | 西北风 | 西北风 | 西北风 | 西北风 [...] é£Žå‘ | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西å—风 | 西北风\n气压 | 1006.8hPa | 1005.1hPa | 1001.7hPa | 998.2hPa | 997.4hPa | 998.4hPa | 997.9hPa | 997.7hPa\n湿度 | 50.4% | 20.9% | 26.3% | 39.3% | 42.1% | 50.6% | 61.3% | 72.8%\näº‘é‡ | 3.8% | 3.3% | 10% | 10% | 10% | 78.8% | 56.4% | 27.1%\næ—¶é—´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | [...] 气压 | 1010.1hPa | 1010hPa | 1010.6hPa | 1012.4hPa | 1012.7hPa | 1010.6hPa | 1010.9hPa | 1012.3hPa\n湿度 | 93.5% | 95.8% | 98% | 96.9% | 95.8% | 88% | 89.3% | 83.9%\näº‘é‡ | 95.2% | 92.2% | 97.1% | 88.2% | 92.1% | 94.9% | 82.5% | 73.4%\næ—¶é—´ | 08:00 | 11:00 | 14:00 | 17:00 | 20:00 | 23:00 | 02:00 | 05:00\n天气 | | | | | | | | \n气温 | 12.8℃ | 22.5℃ | 22.8℃ | 21.9℃ | 18.3℃ | 15.5℃ | 13.3℃ | 12.2℃", "score": 0.7576693}, {"title": "预报- 北京 - 中国天气网", "url": "https://www.weather.com.cn/weather/101010100.shtml", "content": "* 19日(今天)\n晴\n31/18℃\n<3级\n* 20日(明天)\n晴转多云\n33/20℃\n<3级\n* 21日(后天)\n多云\n29/18℃\n<3级\n* 22日(周四)\n阴转小雨\n26/17℃\n<3级\n* 23日(周五)\n晴\n23/16℃\n<3级\n* 24日(周六)\n晴\n26/18℃\n<3级\n* 25日(周日)\n多云\n27/19℃\n<3级\n分时段预报 生活指数\n蓝天预报\n蓝天预报综合天气现象、能见度、空气质量等因子,预测未来一周的天空状况。\n\n天空蔚蓝可见透彻蓝天,或有蓝天白云美景。\n天空淡蓝天空不够清澈,以浅蓝色为主。\n天空阴沉阴天或有雨雪,天空灰暗。\n天空灰霾出现霾或沙尘,天空灰蒙浑浊。\n\n11时 14时 17时 20时 23时 02时 05时 08时\n26℃ 28℃ 30℃ 25℃ 21℃ 19℃ 18℃ 22℃\n东南风 南风 南风 南风 东南风 东风 东北风 东北风\n<3级 <3级 <3级 <3级 <3级 <3级 <3级 <3级\n\n\n少发 感冒指数\n感冒机率较低,避免长期处于空调屋中。\n\n\n较适宜 运动指数\n请适当减少运动时间,降低运动强度。", "score": 0.7304634}]
==================================[1m Ai Message [0m==================================
今天北京的天气情况如下:
- **日期**: 19日
- **天气**: 晴
- **温度**: 最高31°C,最低18°C
- **风速**: 小于3级
未来几天的天气预报:
- **20日** (明天): 晴转多云,最高33°C,最低20°C
- **21日** (后天): 多云,最高29°C,最低18°C
- **22日**: 阴转小雨,最高26°C,最低17°C
- **23日**: 晴,最高23°C,最低16°C
- **24日**: 晴,最高26°C,最低18°C
- **25日**: 多云,最高27°C,最低19°C
你可以查看更详细的天气信息 [这里](https://www.weather.com.cn/weather/101010100.shtml)。
2、流式处理 tokens(Streaming tokens)
除了流式返回消息之外,流式返回tokens也很有用。我们可以通过指定 stream_mode=“messages” 来实现。
# 使用 agent 执行器以消息流模式运行 agent 并逐步获取输出
for step, metadata in agent_executor.stream(
{"messages": [HumanMessage(content="北京天气怎么样?")]}, # 用户输入的消息
stream_mode="messages", # 设置流模式为 "messages",返回的是标准化的消息对象
):
# 只处理来自 "agent" 节点的输出(表示是语言模型本身输出的内容,而不是工具等其他中间节点)
# 同时提取该消息的文本内容(如果有的话)
if metadata["langgraph_node"] == "agent" and (text := step.text()):
# 打印文本内容,不换行,每段后加一个分隔符 "|"
print(text, end="|")
今天|北京|的|天气|情况|如下|:
|-| **|日期|**|:| |19|日|(|今天|)
|-| **|天气|**|:| 晴|
|-| **|最高|温|度|**|:| |31|°C|
|-| **|最低|温|度|**|:| |18|°C|
|-| **|风|速|**|:| 风|力|小|于|3|级|
|未来|几|天|的|天气|预|报|:
|-| **|20|日|**|:| 晴|转|多|云|,|最高|33|°C|,|最低|20|°C|
|-| **|21|日|**|:| 多|云|,|最高|29|°C|,|最低|18|°C|
|-| **|22|日|**|:| 阴|转|小|雨|,|最高|26|°C|,|最低|17|°C|
|-| **|23|日|**|:| 晴|,|最高|23|°C|,|最低|16|°C|
|建议|您|适|当|减少|运动|时间|,|尤其|是在|高|温|时|段|,|保持|水|分|补|充|。
|了解|更多|详细|信息|,可以|访问| [|中国|天气|网|](|https|://|www|.weather|.com|.cn|/weather|/|101|010|100|.shtml|)。|
5、添加内存
如前所述,此代理是无状态的。这意味着它不记得之前的交互。为了赋予它内存,我们需要传入一个检查点。传入检查点时,我们还必须在调用代理时传入一个 thread_id(以便它知道从哪个线程/对话恢复)。
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()
agent_executor = create_react_agent(model, tools, checkpointer=memory)
config = {"configurable": {"thread_id": "abc123"}}
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="你好,我是文武!")]}, config
):
print(chunk)
print("--------------------------------------------------------")
{'agent': {'messages': [AIMessage(content='你好,文武!有什么我可以帮你的吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 13, 'prompt_tokens': 162, 'total_tokens': 175, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZtCFYJne571Kax1fS7FGcAUbyz0n', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--fcea2037-04d5-485e-ab8a-4a43c180bfa6-0', usage_metadata={'input_tokens': 162, 'output_tokens': 13, 'total_tokens': 175, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}
--------------------------------------------------------
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="我叫什么名字?")]}, config
):
print(chunk)
print("--------------------------------------------------------")
{'agent': {'messages': [AIMessage(content='你叫文武!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 6, 'prompt_tokens': 186, 'total_tokens': 192, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_54eb4bd693', 'id': 'chatcmpl-BZtCG0rBm0a9N8NXnFyRiOJq1ERND', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--3d8305ea-92ca-402a-95bb-a033430a78af-0', usage_metadata={'input_tokens': 186, 'output_tokens': 6, 'total_tokens': 192, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}}
--------------------------------------------------------