这篇博客将和大家分享如何快速实现一个运行逻辑相较于传统链式RAG(用户询问 -> 检索相关信息作为上下文 -> LLM推理回复)更为智能、适应性更强的Agentic RAG Chatbot(实现思路参考 Langgraph Agentic RAG 实现官方文档教程)。该 Chatbot 基于Langgraph、Ollama以及提供搜索引擎服务的Tavily作为可调用的外部工具进行实现。
我们希望达成的目标是,该Chatbot可以:1. 根据用户询问判断是否需要调用外部工具(Tavily 搜索引擎);2. 若使用工具,需要基于调用工具后拿取到的信息,用LLM进一步判断用该信息作为回答用户询问的上下文是否足够;3. 如果获取的上下文信息不足,则进入到rewrite node对用户提问进行优化重写;4. 重写提问后,继续回到第一步判断,直到上下文信息足够,LLM进行推理回复用户提问,结束本轮对话。
Graph 图如下所示:
Graph 图中有四个关键节点,分别是 agent
, retrieve
, rewrite
和 generate
,以及由 agent
和 retrieve
这两个节点延展出的conditional edges。接下来对这些节点各自的功能和条件边做一下简单介绍:
Node
agent
:基于支持tool使用的LLM,根据用户询问判断是否需要调用外部工具;若不需要则直接推理回复。retrieve
:外部工具,Tavily 搜索引擎,用于获取回复用户提问需要的外部信息。rewrite
:基于LLM和提示词工程实现,将用户初始提问进行re-phrase。generate
:基于LLM和提示词工程,将retrieve
节点获取到的信息作为context,回复用户提问。
Conditional Edges
- from
agent
:判断是否需要调用工具 - from
retrieve
:判断目前获取到的信息是否足以很好回答用户询问,如不是则走到rewrite
节点;否则走到generate
节点。
代码实现
- 导入所需python库;
from langchain_ollama import ChatOllama
from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing import Annotated, Literal, Sequence
from typing_extensions import TypedDict
from langchain import hub
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
# from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langgraph.prebuilt import tools_condition
from langgraph.graph import END, StateGraph, START
from langgraph.prebuilt import ToolNode
import pprint
from langchain_community.tools.tavily_search import TavilySearchResults
- 定义用于表示graph状态的类;
关于创建State类的详细解释,有需要可移步至 基于LangGraph、Groq和Tavily打造可以调用外部搜索引擎工具的对话机器人 这篇博客进行了解。
class AgentState(TypedDict):# The add_messages function defines how an update should be processed# Default is to replace. add_messages says "append"messages: Annotated[Sequence[BaseMessage], add_messages]
- 创建名为
retrieve
的 tool node;
# 调用 Tavily 检索服务,max_results 参数值设置为2,即每次调用返回两个检索结果
tool = TavilySearchResults(max_results=2)
tools = [tool]
retrieve = ToolNode(tools)
- 用函数的方式创建
agent
节点(注释由 chatGPT-4o 生成);
在这个函数中,我们使用了 Ollama 提供LLM推理服务。需要特别注意的是关于模型的选择,需要能够支持 Tool 使用的,比如 llama3.1
,mistral-nemo
等等。具体哪些模型支持可以在 Ollama 官网 上进行查询和选择。
def agent(state):"""调用代理模型根据当前状态生成响应。根据问题,它将决定使用检索工具检索信息,或简单地结束。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中附加了代理响应到消息中"""print("---CALL AGENT---") # 打印调用代理的标识信息messages = state["messages"] # 从状态中提取当前消息列表# 创建一个ChatOllama模型实例,指定使用"llama3.1:latest"版本,并设置温度参数为0.8model = ChatOllama(model="llama3.1:latest", temperature=0.8)# 将工具绑定到模型中,以便模型可以使用这些工具进行信息检索model = model.bind_tools(tools)# 调用模型以生成响应,传入当前的消息列表response = model.invoke(messages)# 返回一个字典,其中“messages”键对应一个列表,该列表包含新生成的响应# 这个列表将被添加到现有消息列表中return {"messages": [response]}
- 用函数的方式创建
rewrite
节点(注释由 chatGPT-4o 生成);
在 rewrite
节点的功能实现中,也用到了LLM推理,这里的模型不需要具备 tool 的支持能力,所以可以使用和 agent
节点中不一样的模型。(示例代码中还是使用了 llama3.1
)
def rewrite(state):"""转换查询以生成一个更好的问题。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中包含重新措辞的问题"""print("---TRANSFORM QUERY---") # 打印转换查询的标识信息messages = state["messages"] # 从状态中提取当前消息列表question = messages[0].content # 获取消息列表中的第一个问题的内容# 构建一个新的消息列表,包含用户输入提示msg = [HumanMessage(content=f""" \n 查看输入并尝试推理其潜在的语义意图/含义。 \n 这是最初的问题:\n ------- \n{question} \n ------- \n制定一个改进后的问题: """,)]# 创建一个ChatOllama模型实例,指定使用"llama3.1:latest"版本,并设置温度参数为0.8model = ChatOllama(model="llama3.1:latest", temperature=0.8)# 调用模型以生成响应,传入构建的消息列表response = model.invoke(msg)# 返回一个字典,其中“messages”键对应一个列表,该列表包含模型生成的改进问题return {"messages": [response]}
- 用函数的方式创建
generate
节点(注释由 chatGPT-4o 生成);
和上一步创建的 rewrite
节点同理,这里面使用的LLM也不需要具备 tool 的支持能力,可以根据需要选择其他模型。
def generate(state):"""生成答案。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中包含生成的答案"""print("---GENERATE---") # 打印生成过程的标识信息messages = state["messages"] # 从状态中提取当前消息列表question = messages[0].content # 获取消息列表中的第一个问题的内容last_message = messages[-1] # 获取消息列表中的最后一条消息docs = last_message.content # 从最后一条消息中提取内容作为文档# 提示prompt = hub.pull("rlm/rag-prompt") # 从hub中拉取名为“rlm/rag-prompt”的提示# 大语言模型 (LLM)llm = ChatOllama(model="llama3.1:latest", temperature=0.8) # 创建一个ChatOllama模型实例# 后处理函数def format_docs(docs):# 将文档中的页面内容用两个换行符分隔并连接成字符串return "\n\n".join(doc.page_content for doc in docs)# 链式调用rag_chain = prompt | llm | StrOutputParser() # 创建一个链式结构,将提示、LLM和输出解析器连接# 运行response = rag_chain.invoke({"context": docs, "question": question}) # 调用链式结构并传入上下文和问题# 返回一个字典,其中“messages”键对应一个列表,该列表包含生成的响应return {"messages": [response]}
- 创建出发自
agent
节点的 condition edges;
代码实现见后续graph的搭建。因为 agent
节点的条件边是判断是否需要使用tool,可以直接使用 langgraph
中提前实现的 tools_condition
,在第1步中已经导入。
from langgraph.prebuilt import tools_condition
- 创建出发自
retrieve
节点的conditional edges(注释由 chatGPT-4o 生成);
retrieve
节点的条件边用来决定 which is the next node to go,具体是通过 grade_documents()
这个函数来实现的。该函数通过提示词工程以及将LLM输出结构化等技术,将其作为一个基于用户提问,判断上下文信息是否与提问相关的二元评分器;然后基于评分结果,决定下一个node的选择,是 generate
还是 rewrite
。
def grade_documents(state) -> Literal["generate", "rewrite"]:"""判断检索到的文档是否与问题相关。参数:state (messages): 当前状态返回:str: 决定文档是否相关的结果"""print("---CHECK RELEVANCE---") # 打印检查相关性的标识信息# 数据模型class grade(BaseModel):"""用于相关性检查的二元评分。"""binary_score: str = Field(description="相关性评分 'yes' 或 'no'")# 大语言模型 (LLM)model = ChatOllama(model="llama3.1:latest", temperature=0.8) # 创建一个ChatOllama模型实例# 使用工具和验证的LLMllm_with_tool = model.with_structured_output(grade) # 将模型与结构化输出结合# 提示模板prompt = PromptTemplate(template="""你是一名评估员,负责评估检索到的文档与用户问题的相关性。 \n 这是检索到的文档: \n\n {context} \n\n这是用户的问题: {question} \n如果文档包含与用户问题相关的关键词或语义含义,请将其评定为相关。 \n给出一个二元评分 'yes' 或 'no',以指示文档是否与问题相关。""",input_variables=["context", "question"],)# 链式调用chain = prompt | llm_with_tool # 创建一个链式结构,将提示和带工具的模型连接messages = state["messages"] # 从状态中提取当前消息列表last_message = messages[-1] # 获取消息列表中的最后一条消息question = messages[0].content # 获取消息列表中的第一个问题的内容docs = last_message.content # 从最后一条消息中提取文档内容scored_result = chain.invoke({"question": question, "context": docs}) # 调用链式结构并传入问题和上下文score = scored_result.binary_score # 获取评分结果if score == "yes":print("---DECISION: DOCS RELEVANT---") # 打印文档相关的决策信息return "generate" # 返回“generate”表示文档相关else:print("---DECISION: DOCS NOT RELEVANT---") # 打印文档不相关的决策信息print(score) # 打印评分return "rewrite" # 返回“rewrite”表示文档不相关
- 创建 Graph(注释由 chatGPT-4o 生成);
# 定义一个新的状态图
workflow = StateGraph(AgentState)# 定义我们将在其中循环的节点
workflow.add_node("agent", agent) # 代理节点
retrieve = ToolNode(tools) # 检索节点,使用工具
workflow.add_node("retrieve", retrieve) # 添加检索节点
workflow.add_node("rewrite", rewrite) # 重新措辞问题的节点
workflow.add_node("generate", generate
) # 在确认文档相关后生成响应的节点# 调用代理节点以决定是否进行检索
workflow.add_edge(START, "agent") # 从起始节点到代理节点的边# 决定是否进行检索
workflow.add_conditional_edges("agent",# 评估代理决策tools_condition,{# 将条件输出转换为我们图中的节点"tools": "retrieve", # 如果条件满足,转到检索节点END: END, # 否则,结束},
)# 在调用`retrieve`节点后采取的边
workflow.add_conditional_edges("retrieve",# 评估文档相关性grade_documents,
)workflow.add_edge("generate", END) # 生成节点到结束的边
workflow.add_edge("rewrite", "agent") # 重新措辞节点返回代理节点# 编译
graph = workflow.compile() # 编译状态图以生成可执行的工作流
- 提问,检验问答效果;
用英文询问当前北京的天气如何。
user_query = "how is the current weather in Beijing city?"inputs = {"messages": [("user", user_query),]
}
for output in graph.stream(inputs):for key, value in output.items():pprint.pprint(f"Output from node '{key}':")pprint.pprint("---")pprint.pprint(value, indent=2, width=80, depth=None)pprint.pprint("\n---\n")
状态图中被执行后的节点产生的 messages
内容打印如下:
(可以看出graph的运行路径是:input -> agent节点 -> retrieve节点 -> generate节点 -> END;暂时没有涉及到 rewrite。)
---CALL AGENT---
"Output from node 'agent':"
'---'
{ 'messages': [ AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'llama3.1:latest', 'created_at': '2025-02-18T07:29:16.232338Z', 'done': True, 'done_reason': 'stop', 'total_duration': 5865025625, 'load_duration': 1112864375, 'prompt_eval_count': 197, 'prompt_eval_duration': 3894000000, 'eval_count': 24, 'eval_duration': 855000000, 'message': Message(role='assistant', content='', images=None, tool_calls=None)}, id='run-9e83acda-bfc5-4991-a9d2-265e187c801d-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'Beijing current weather'}, 'id': 'a1717377-a6ac-4c72-84f2-cbbda0685b23', 'type': 'tool_call'}], usage_metadata={'input_tokens': 197, 'output_tokens': 24, 'total_tokens': 221})]}
'\n---\n'
---CHECK RELEVANCE---
---DECISION: DOCS RELEVANT---
"Output from node 'retrieve':"
'---'
{ 'messages': [ ToolMessage(content='[{"url": "https://www.weatherapi.com/", "content": "{\'location\': {\'name\': \'Beijing\', \'region\': \'Beijing\', \'country\': \'China\', \'lat\': 39.9289, \'lon\': 116.3883, \'tz_id\': \'Asia/Shanghai\', \'localtime_epoch\': 1739863764, \'localtime\': \'2025-02-18 15:29\'}, \'current\': {\'last_updated_epoch\': 1739862900, \'last_updated\': \'2025-02-18 15:15\', \'temp_c\': 6.3, \'temp_f\': 43.3, \'is_day\': 1, \'condition\': {\'text\': \'Sunny\', \'icon\': \'//cdn.weatherapi.com/weather/64x64/day/113.png\', \'code\': 1000}, \'wind_mph\': 7.8, \'wind_kph\': 12.6, \'wind_degree\': 330, \'wind_dir\': \'NNW\', \'pressure_mb\': 1029.0, \'pressure_in\': 30.39, \'precip_mm\': 0.0, \'precip_in\': 0.0, \'humidity\': 11, \'cloud\': 0, \'feelslike_c\': 3.7, \'feelslike_f\': 38.7, \'windchill_c\': 3.4, \'windchill_f\': 38.1, \'heatindex_c\': 6.0, \'heatindex_f\': 42.8, \'dewpoint_c\': -23.3, \'dewpoint_f\': -9.9, \'vis_km\': 10.0, \'vis_miles\': 6.0, \'uv\': 1.1, \'gust_mph\': 9.2, \'gust_kph\': 14.8}}"}, {"url": "https://www.meteoprog.com/weather/Beijing/month/february/", "content": "Beijing weather in February 2025 ⋆ Beijing temperature in February ≡ Weather forecast in China | METEOPROG Weather in China Weather Widgets Русский (UA) Русский WEATHER 7-day Forecast Weather on a map Weather Widget Русский (UA) Русский 2 Weeks Beijing weather in February 2025 February in Beijing Max. temperature +23°C Min. temperature -24.1°C +1° +7° -3° +4° +17° +17° -3° -4° +4° +3° +18° -1° -14° +17° Weather Weather in China Monthly weather forecast in Beijing Weather in February 2 Weeks Weather Widgets Editorial policy Hyperlink to Meteoprog.com is required when using materials from the site. The editorial staff of the project may not share the opinion of the authors and is not responsible for copyrighted materials. Русский (UA) Русский"}]', name='tavily_search_results_json', id='14b19aee-92ad-4b80-b146-b4d3ed05ca67', tool_call_id='a1717377-a6ac-4c72-84f2-cbbda0685b23', artifact={'query': 'Beijing current weather', 'follow_up_questions': None, 'answer': None, 'images': [], 'results': [{'title': 'Weather in Beijing', 'url': 'https://www.weatherapi.com/', 'content': "{'location': {'name': 'Beijing', 'region': 'Beijing', 'country': 'China', 'lat': 39.9289, 'lon': 116.3883, 'tz_id': 'Asia/Shanghai', 'localtime_epoch': 1739863764, 'localtime': '2025-02-18 15:29'}, 'current': {'last_updated_epoch': 1739862900, 'last_updated': '2025-02-18 15:15', 'temp_c': 6.3, 'temp_f': 43.3, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, 'wind_mph': 7.8, 'wind_kph': 12.6, 'wind_degree': 330, 'wind_dir': 'NNW', 'pressure_mb': 1029.0, 'pressure_in': 30.39, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 11, 'cloud': 0, 'feelslike_c': 3.7, 'feelslike_f': 38.7, 'windchill_c': 3.4, 'windchill_f': 38.1, 'heatindex_c': 6.0, 'heatindex_f': 42.8, 'dewpoint_c': -23.3, 'dewpoint_f': -9.9, 'vis_km': 10.0, 'vis_miles': 6.0, 'uv': 1.1, 'gust_mph': 9.2, 'gust_kph': 14.8}}", 'score': 0.9327485, 'raw_content': None}, {'url': 'https://www.meteoprog.com/weather/Beijing/month/february/', 'title': 'Beijing weather in February 2025 - Meteoprog.com', 'content': 'Beijing weather in February 2025 ⋆ Beijing temperature in February ≡ Weather forecast in China | METEOPROG Weather in China Weather Widgets Русский (UA) Русский WEATHER 7-day Forecast Weather on a map Weather Widget Русский (UA) Русский 2 Weeks Beijing weather in February 2025 February in Beijing Max. temperature +23°C Min. temperature -24.1°C +1° +7° -3° +4° +17° +17° -3° -4° +4° +3° +18° -1° -14° +17° Weather Weather in China Monthly weather forecast in Beijing Weather in February 2 Weeks Weather Widgets Editorial policy Hyperlink to Meteoprog.com is required when using materials from the site. The editorial staff of the project may not share the opinion of the authors and is not responsible for copyrighted materials. Русский (UA) Русский', 'score': 0.8934678, 'raw_content': None}], 'response_time': 1.76})]}
'\n---\n'
---GENERATE---
"Output from node 'generate':"
'---'
{ 'messages': [ 'The current weather in Beijing city is sunny with a ''temperature of 6.3°C, according to the provided weather data ''from WeatherAPI. The wind is blowing at 12.6 km/h from the ''NNW direction. The humidity is 11%.']}
'\n---\n'
如果使用了 langsmith 则可以看到更为直观的状态图运行路径和每个被运行节点的输入输出内容。
完整代码
from langchain_ollama import ChatOllama
from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from typing import Annotated, Literal, Sequence
from typing_extensions import TypedDict
from langchain import hub
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
from langgraph.prebuilt import tools_condition
from langgraph.graph import END, StateGraph, START
from langgraph.prebuilt import ToolNode
import pprint
from langchain_community.tools.tavily_search import TavilySearchResults# 工具定义,使用Tavily搜索结果,最多返回两个结果
tool = TavilySearchResults(max_results=2)
tools = [tool]# 定义AgentState类,管理消息的状态
class AgentState(TypedDict):messages: Annotated[Sequence[BaseMessage], add_messages] # 添加消息的策略是追加# 定义函数判断文档是否与问题相关
def grade_documents(state) -> Literal["generate", "rewrite"]:"""判断检索到的文档是否与问题相关。参数:state (messages): 当前状态返回:str: 决定文档是否相关的结果"""print("---CHECK RELEVANCE---")# 数据模型class grade(BaseModel):"""用于相关性检查的二元评分。"""binary_score: str = Field(description="相关性评分 'yes' 或 'no'")# 创建一个ChatOllama模型实例model = ChatOllama(model="llama3.1:latest", temperature=0.8)llm_with_tool = model.with_structured_output(grade) # 使用工具和验证的LLM# 提示模板prompt = PromptTemplate(template="""你是一名评估员,负责评估检索到的文档与用户问题的相关性。 \n 这是检索到的文档: \n\n {context} \n\n这是用户的问题: {question} \n如果文档包含与用户问题相关的关键词或语义含义,请将其评定为相关。 \n给出一个二元评分 'yes' 或 'no',以指示文档是否与问题相关。""",input_variables=["context", "question"],)# 创建链式结构,将提示和带工具的模型连接chain = prompt | llm_with_toolmessages = state["messages"] # 从状态中提取当前消息列表last_message = messages[-1] # 获取消息列表中的最后一条消息question = messages[0].content # 获取消息列表中的第一个问题的内容docs = last_message.content # 从最后一条消息中提取文档内容scored_result = chain.invoke({"question": question, "context": docs}) # 调用链式结构并传入问题和上下文score = scored_result.binary_score # 获取评分结果if score == "yes":print("---DECISION: DOCS RELEVANT---") # 打印文档相关的决策信息return "generate" # 返回“generate”表示文档相关else:print("---DECISION: DOCS NOT RELEVANT---") # 打印文档不相关的决策信息print(score) # 打印评分return "rewrite" # 返回“rewrite”表示文档不相关# 定义函数用于调用代理模型生成响应
def agent(state):"""调用代理模型根据当前状态生成响应。根据问题,它将决定使用检索工具检索信息,或简单地结束。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中附加了代理响应到消息中"""print("---CALL AGENT---")messages = state["messages"] # 从状态中提取当前消息列表model = ChatOllama(model="llama3.1:latest", temperature=0.8) # 创建一个ChatOllama模型实例model = model.bind_tools(tools) # 将工具绑定到模型中response = model.invoke(messages) # 调用模型以生成响应return {"messages": [response]} # 返回一个字典,其中“messages”键对应一个列表,该列表包含新生成的响应# 定义函数用于转换查询生成更好的问题
def rewrite(state):"""转换查询以生成一个更好的问题。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中包含重新措辞的问题"""print("---TRANSFORM QUERY---")messages = state["messages"] # 从状态中提取当前消息列表question = messages[0].content # 获取消息列表中的第一个问题的内容# 构建一个新的消息列表,包含用户输入提示msg = [HumanMessage(content=f""" \n 查看输入并尝试推理其潜在的语义意图/含义。 \n 这是最初的问题:\n ------- \n{question} \n ------- \n制定一个改进后的问题: """,)]model = ChatOllama(model="llama3.1:latest", temperature=0.8) # 创建一个ChatOllama模型实例response = model.invoke(msg) # 调用模型以生成响应return {"messages": [response]} # 返回一个字典,其中“messages”键对应一个列表,该列表包含模型生成的改进问题# 定义函数用于生成答案
def generate(state):"""生成答案。参数:state (messages): 当前状态返回:dict: 更新后的状态,其中包含生成的答案"""print("---GENERATE---")messages = state["messages"] # 从状态中提取当前消息列表question = messages[0].content # 获取消息列表中的第一个问题的内容last_message = messages[-1] # 获取消息列表中的最后一条消息docs = last_message.content # 从最后一条消息中提取内容作为文档# 提示prompt = hub.pull("rlm/rag-prompt") # 从hub中拉取名为“rlm/rag-prompt”的提示llm = ChatOllama(model="llama3.1:latest", temperature=0.8) # 创建一个ChatOllama模型实例# 后处理函数def format_docs(docs):# 将文档中的页面内容用两个换行符分隔并连接成字符串return "\n\n".join(doc.page_content for doc in docs)# 链式调用rag_chain = prompt | llm | StrOutputParser() # 创建一个链式结构,将提示、LLM和输出解析器连接# 运行response = rag_chain.invoke({"context": docs, "question": question}) # 调用链式结构并传入上下文和问题return {"messages": [response]} # 返回一个字典,其中“messages”键对应一个列表,该列表包含生成的响应print("*" * 20 + "Prompt[rlm/rag-prompt]" + "*" * 20)
prompt = hub.pull("rlm/rag-prompt").pretty_print() # 打印提示模板# 定义一个新的状态图
workflow = StateGraph(AgentState)# 定义我们将在其中循环的节点
workflow.add_node("agent", agent) # 代理节点
retrieve = ToolNode(tools) # 检索节点,使用工具
workflow.add_node("retrieve", retrieve) # 添加检索节点
workflow.add_node("rewrite", rewrite) # 重新措辞问题的节点
workflow.add_node("generate", generate) # 在确认文档相关后生成响应的节点# 调用代理节点以决定是否进行检索
workflow.add_edge(START, "agent") # 从起始节点到代理节点的边# 决定是否进行检索
workflow.add_conditional_edges("agent",# 评估代理决策tools_condition,{# 将条件输出转换为我们图中的节点"tools": "retrieve", # 如果条件满足,转到检索节点END: END, # 否则,结束},
)# 在调用`action`节点后采取的边
workflow.add_conditional_edges("retrieve",# 评估文档相关性grade_documents,
)
workflow.add_edge("generate", END) # 生成节点到结束的边
workflow.add_edge("rewrite", "agent") # 重新措辞节点返回代理节点# 编译状态图以生成可执行的工作流
graph = workflow.compile()user_query = "how is the current weather in Beijing city?"
# user_query = "What does Lilian Weng say about the types of agent memory?"inputs = {"messages": [("user", user_query),]
}
# 处理用户输入并输出结果
for output in graph.stream(inputs):for key, value in output.items():pprint.pprint(f"Output from node '{key}':")pprint.pprint("---")pprint.pprint(value, indent=2, width=80, depth=None)pprint.pprint("\n---\n")
相关阅读
[1] LangSmith 简单使用说明
[2] LangGraph 通过in-memory持久化实现多轮对话能力Chatbot
[3] 使用LangChain自定义tools的三种方式