from datetime import datetime
from langchain_core.runnables import Runnable, RunnableConfig
from langchain_core.prompts import ChatPromptTemplate
primary_assistant_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful customer support assistant for Swiss Airlines. "
" Use the provided tools to search for flights, company policies, and other information to assist the user's queries. "
" When searching, be persistent. Expand your query bounds if the first search returns no results. "
" If a search comes up empty, expand your search before giving up."
"\n\nCurrent user:\n\n{user_info}\n "
"\nCurrent time: {time}.",
),
("placeholder", "{messages}"),
]
).partial(time=datetime.now())
tools = [fetch_user_flight_information,search_flights]
assistant_runnable = primary_assistant_prompt | llm.bind_tools(tools)
class Assistant:
def __init__(self, runnable: Runnable):
self.runnable = runnable
def __call__(self, state: State, config: RunnableConfig):
while True:
configuration = config.get("configurable", {})
passenger_id = configuration.get("passenger_id", None)
state = {**state, "user_info": passenger_id}
result = self.runnable.invoke(state)
# If the LLM happens to return an empty response, we will re-prompt it
# for an actual response.
if not result.tool_calls and (
not result.content
or isinstance(result.content, list)
and not result.content[0].get("text")
):
messages = state["messages"] + [("user", "Respond with a real output.")]
state = {**state, "messages": messages}
else:
break
print('\n result======\n',result)
return {"messages": result}
# Define nodes: these do the work
builder.add_node("assistant", Assistant(assistant_runnable))
这段代码是在创建一个用于航空公司客户支持的智能助手。它定义了助手的行为,包括如何处理用户输入、与语言模型(LLM)的交互,以下是对代码的详细解释:
导入必要的模块和类:
from datetime import datetime
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import Runnable, RunnableConfig
datetime
:用于获取当前日期和时间。ChatPromptTemplate
:用于创建聊天提示模板,帮助格式化和组织传递给语言模型的提示信息。Runnable
和 RunnableConfig
:用于定义可运行的对象和其配置,控制助手的执行行为。创建聊天提示模板:
primary_assistant_prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"You are a helpful customer support assistant for Swiss Airlines. "
"Use the provided tools to search for flights, company policies, and other information to assist the user's queries. "
"When searching, be persistent. Expand your query bounds if the first search returns no results. "
"If a search comes up empty, expand your search before giving up."
"\n\nCurrent user:\n\n{user_info}\n "
"\nCurrent time: {time}.",
),
("placeholder", "{messages}"),
]
).partial(time=datetime.now())
ChatPromptTemplate.from_messages
方法创建一个聊天提示模板。"{user_info}"
和 "{time}"
是占位符,将在实际使用时被替换为用户信息和当前时间。{messages}
,表示将在此处插入实际的用户和助手之间的对话消息。.partial(time=datetime.now())
方法将当前时间部分应用于模板。定义助手类:
class Assistant:
def __init__(self, runnable: Runnable):
self.runnable = runnable
def __call__(self, state: State, config: RunnableConfig):
while True:
configuration = config.get("configurable", {})
passenger_id = configuration.get("passenger_id", None)
state = {**state, "user_info": passenger_id}
result = self.runnable.invoke(state)
# If the LLM happens to return an empty response, we will re-prompt it
# for an actual response.
if not result.tool_calls and (
not result.content
or isinstance(result.content, list)
and not result.content[0].get("text")
):
messages = state["messages"] + [("user", "Respond with a real output.")]
state = {**state, "messages": messages}
else:
break
print('\n result======\n', result)
return {"messages": result}
Assistant
类,初始化时接受一个 Runnable
对象。__call__
方法用于处理助手的执行逻辑:
passenger_id
,并将其添加到状态的 user_info
中。self.runnable.invoke(state)
执行可运行对象,并传入当前状态。定义工具和可运行对象:
tools = [fetch_user_flight_information, search_flights]
assistant_runnable = primary_assistant_prompt | llm.bind_tools(tools)
tools
列表包含两个工具函数:fetch_user_flight_information
和 search_flights
,用于检索用户的航班信息和搜索航班。|
将 primary_assistant_prompt
和绑定了工具的语言模型(llm.bind_tools(tools)
)组合成一个可运行对象 assistant_runnable
。添加助手节点:
builder.add_node("assistant", Assistant(assistant_runnable))
builder.add_node
方法将助手节点添加到构建器中,节点名称为 "assistant"
,对应的可运行对象为 Assistant(assistant_runnable)
。示例说明:
假设用户请求查询从苏黎世到纽约的航班信息,助手的处理流程如下:
初始化助手:
assistant = Assistant(assistant_runnable)
设置状态和配置:
state = {"messages": [("user", "请帮我查询从苏黎世到纽约的航班信息。")]}
config = {"configurable": {"passenger_id": "12345"}}
调用助手:
response = assistant(state, config)
助手执行流程:
passenger_id
,并将其添加到状态的 user_info
中。primary_assistant_prompt
生成包含用户信息和当前时间的提示。通过上述步骤,助手能够根据用户的请求,使用提供的工具检索相关的航班信息,并在必要时调整搜索范围,以确保为用户提供准确且有用的支持。
messages = state["messages"] + [("user", "Respond with a real output.")]
state = {**state, "messages": messages}
这段代码的作用是向 state
字典中的 "messages"
键添加一条新的用户消息,提示模型返回实际的输出。具体操作如下:
添加新消息:
messages = state["messages"] + [("user", "Respond with a real output.")]
state["messages"]
:获取当前状态中 "messages"
键对应的列表。[("user", "Respond with a real output.")]
:创建一个包含新消息的列表,表示用户发送了一条内容为 “Respond with a real output.” 的消息。state["messages"] + [...]
:将现有的消息列表与新消息列表连接,生成一个新的列表,包含所有消息。更新 state
字典:
state = {**state, "messages": messages}
{**state, "messages": messages}
:使用字典解包的方式创建一个新的字典,其中包含原有的 state
键值对,但 "messages"
键的值被更新为新的 messages
列表。数值举例说明:
假设初始的 state
字典如下:
state = {
"messages": [
("user", "请帮我查询从苏黎世到纽约的航班信息。"),
("assistant", "好的,请稍等,我正在为您查询。")
],
"user_info": "12345"
}
执行上述代码后,state
字典将更新为:
state = {
"messages": [
("user", "请帮我查询从苏黎世到纽约的航班信息。"),
("assistant", "好的,请稍等,我正在为您查询。"),
("user", "Respond with a real output.")
],
"user_info": "12345"
}
在这个示例中,"messages"
列表中新增了一条用户消息,内容为 “Respond with a real output.”。这通常用于在模型未能返回有效响应时,提示模型生成实际的输出。
两个函数:handle_tool_error
和 create_tool_node_with_fallback
。
以下是对每个函数的详细解释:
handle_tool_error
函数功能:
该函数用于处理工具执行过程中的错误,将错误信息格式化为消息,并将其添加到聊天记录中。
参数:
state
:一个包含当前工具状态的字典,其中包括错误信息和工具调用记录。返回值:
ToolMessage
对象的列表,其中包含错误信息。详细解释:
首先,从 state
字典中获取键为 "error"
的值,即当前的错误信息。
然后,获取 state
字典中 "messages"
列表的最后一个元素的 tool_calls
属性,即最近的工具调用记录。
接下来,返回一个字典,其中包含一个 "messages"
键,其值是一个 ToolMessage
对象的列表。每个 ToolMessage
对象的 content
属性包含格式化的错误信息,并附加提示让用户修正错误;tool_call_id
属性对应工具调用的 ID。
示例:
假设 state
如下:
state = {
"error": "FileNotFoundError: 'data.csv' not found",
"messages": [
{
"tool_calls": [{"id": "12345"}]
}
]
}
调用 handle_tool_error(state)
将返回:
{
"messages": [
ToolMessage(
content="Error: FileNotFoundError: 'data.csv' not found\n please fix your mistakes.",
tool_call_id="12345"
)
]
}
create_tool_node_with_fallback
函数功能:
该函数创建一个具有回退错误处理功能的 ToolNode
对象。
参数:
tools
:一个工具列表,将包含在 ToolNode
中。返回值:
ToolNode
对象。详细解释:
首先,创建一个包含指定工具的 ToolNode
对象。
然后,使用 with_fallbacks
方法为该 ToolNode
配置回退函数,以处理在工具执行过程中可能发生的错误。这里,回退函数是通过 RunnableLambda
包装的 handle_tool_error
函数。
exception_key="error"
参数指定在发生异常时,将错误信息存储在状态字典的 "error"
键中。
示例:
假设有一个工具列表:
tools = ["tool_a", "tool_b"]
调用 create_tool_node_with_fallback(tools)
将返回一个配置了回退错误处理的 ToolNode
对象。当工具执行过程中发生错误时,handle_tool_error
函数将被调用,以处理并记录错误信息。
这两个函数的组合使用确保了在工具执行过程中发生错误时,系统能够捕获并处理这些错误,将其记录为消息,并添加到聊天记录中,以便用户了解并进行相应的修正。