from langgraph.graph import MessagesState, StateGraph
graph_builder = StateGraph(MessagesState)
Human: "什么是任务分解?"
AI: "任务分解涉及将复杂的任务分解为更小、更简单的步骤,以便代理或模型更易于管理。"
Human: "常见的做法有哪些?"
涉及到详细的做法的时候,模型可以生成诸如“任务分解的常用方法”之类的query,tools call 可以自然地做到。就像RAG 前面的教程的查询分析部分一样,这可以让model将用户query重写为更有效的搜索查询。
因为是使用的model生成query,它还支持不涉及检索步骤的直接响应(例如,响应来自用户的hello问候)。
tools
from langchain_core.tools import tool
@tool(response_format="content_and_artifact")
def question_process(query: str):
"""All questions are processed by this method."""
retrieved_docs = vector_store.similarity_search(query, k=2)
serialized = "\n\n".join(
(f"Source: {doc.metadata}\n" f"Content: {doc.page_content}")
for doc in retrieved_docs
)
return serialized, retrieved_docs
from langchain_core.messages import SystemMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import MessagesState
# Step 1: 生成一个可能包含要发送的tool-call的AIMessage。
def query_or_respond(state: MessagesState):
"""为检索生成tools_call或者response."""
llm_with_tools = llm_model.bind_tools([question_process])
response = llm_with_tools.invoke(state["messages"])
# MessagesState 将消息appends到状态而不是overwriting,
# 相当于每次都是在原有的基础上append,实现了历史消息也就是上下文的累计
return {"messages": [response]}
# Step 2: 执行检索
tools = ToolNode([question_process])
# Step 3: 用检索的内容生成响应
def generate(state: MessagesState):
"""生成一个回答"""
# 获取生成所有的 ToolMessages
recent_tool_messages = []
for message in reversed(state["messages"]):
if message.type == "tool":
recent_tool_messages.append(message)
else:
break
tool_messages = recent_tool_messages[::-1]
# 格式化为 prompt
docs_content = "\n\n".join(doc.content for doc in tool_messages)
system_message_content = (
"你是一个用于问答任务的助手。"
"用下面的检索到的内容来回答问题。"
"如果你不知道答案,就说你不知道。"
"最多用三句话回答问题,保持简洁。"
"\n\n"
f"{docs_content}"
)
conversation_messages = [
message
for message in state["messages"]
if message.type in ("human", "system")
or (message.type == "ai" and not message.tool_calls)
]
prompt = [SystemMessage(system_message_content)] + conversation_messages
# Run
response = llm_model.invoke(prompt)
return {"messages": [response]}
from langgraph.graph import END
from langgraph.prebuilt import tools_condition
graph_builder.add_node(query_or_respond)
graph_builder.add_node(tools)
graph_builder.add_node(generate)
# 设置入口点
graph_builder.set_entry_point("query_or_respond")
# 添加条件边
graph_builder.add_conditional_edges(
# 从 query_or_respond 到 tools 或者 直接结束
"query_or_respond",
tools_condition,
{END: END, "tools": "tools"},
)
# 添加tools到generate的边
graph_builder.add_edge("tools", "generate")
# 添加generate到END的边
graph_builder.add_edge("generate", END)
graph = graph_builder.compile()
for step in graph.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
):
step["messages"][-1].pretty_print()
================================ Human Message =================================
你好
================================== Ai Message ==================================
你好!有什么可以帮助你的吗?
input_message = "为什么选择猫粮?"
for step in graph.stream(
{"messages": [{"role": "user", "content": input_message}]},
stream_mode="values",
):
step["messages"][-1].pretty_print()
================================ Human Message =================================
为什么选择猫粮?
================================== Ai Message ==================================
Tool Calls:
question_process (c833852d-4e41-4ce9-8e9d-3643d673d1cb)
Call ID: c833852d-4e41-4ce9-8e9d-3643d673d1cb
Args:
question: 为什么选择猫粮?
================================= Tool Message =================================
Name: question_process
("Source: {'source': 'https://soulteary.com/2018/07/23/experience-in-breeding-cat.html', 'section': 'beginning'}\nContent: 必须有眼缘、性格好\n最好有白手套\n最好有白肚皮\n最好是开脸\n最好能够是红虎斑(俗称橘猫)\n脸形要突出有层次,第一只猫最好不是扁脸\n\n正巧看到 58心宠 有一家官方认证的宠物店有不少英短,对其中一只小公主颇为满意,于是打车前往,期望能抱得小猫归。\n实施领猫计划\n到了猫舍直奔主题,然而和英短小公主缘分不够:\n我、女票、宠物店主都抱不住它(不能安静的抱在怀里,只想挣脱)。\n猫舍是一个三居室,里面有很多双层的大铁笼,和其他宠物店无二,类似日本胶囊公寓里装满了:英短、美短、折耳。\n想着过来花费了不少时间,看看再走吧,于是就对小猫们逐个看了起来,最终在次卧靠窗的笼子里看到了俩兄弟。\n俩兄弟据说是寄卖在宠物店,只剩他俩了,当晚会再来一位顾客,领走兄弟之一,如果选择的话,只能带走一只。\n但是和其他猫不同的是,抱着俩兄弟到怀里的一瞬间,都会响起“柴油发动机”类似的呼噜声,俩兄弟活似小狐狸,茂密的毛发摸起来很爽。\n考虑一来女票一直以来很喜欢布偶猫,二来当时两个人上班都不在望京,担心小猫一个人在家寂寞,于是和宠物店主简单沟通了一下,“夺人之爱”的一次性购入俩兄弟,虽说不符合之前的领猫硬标准,但是女票开心,也就罢了。\n从望京五点出发,折腾到八点准备回家,来回车程70公里,到家快十一点,不过带着俩主子累并快乐着。\n\n品质待商榷的问题猫粮\n带回来后使用了店主强力推荐的“METZ”玫斯猫粮,第一次装满饭缸,看到他们狼吞虎咽。\n我和女票笑着说:“宠物店是不是太抠门了,把它们饿成这样。”\n记得我还和公司养猫的小姐姐推荐过这个牌子,谁知道这里居然有天坑,在喂了一周左右,和周围养猫的朋友沟通养猫经验,大家均表示没有喂过也不知道这个牌子,虽然猫粮品牌众多,但是还是让我心生疑窦,于是回家各种搜索引擎换着用,发现了一个我不想接受的事实:\n这个猫粮被曝光许久,是一个“假洋牌”,据说掺入了“诱食剂”。\n加上家里的宠物粮包装袋出现了很多猫咬过的痕迹,有网友说家里的同款猫粮也被宠物疯狂咬食过。", [Document(id='3a4569de-b4dd-4aa9-b926-42954723d450', metadata={'source': 'https://soulteary.com/2018/07/23/experience-in-breeding-cat.html', 'section': 'beginning'}, page_content='必须有眼缘、性格好\n最好有白手套\n最好有白肚皮\n最好是开脸\n最好能够是红虎斑(俗称橘猫)\n脸形要突出有层次,第一只猫最好不是扁脸\n\n正巧看到 58心宠 有一家官方认证的宠物店有不少英短,对其中一只小公主颇为满意,于是打车前往,期望能抱得小猫归。\n实施领猫计划\n到了猫舍直奔主题,然而和英短小公主缘分不够:\n我、女票、宠物店主都抱不住它(不能安静的抱在怀里,只想挣脱)。\n猫舍是一个三居室,里面有很多双层的大铁笼,和其他宠物店无二,类似日本胶囊公寓里装满了:英短、美短、折耳。\n想着过来花费了不少时间,看看再走吧,于是就对小猫们逐个看了起来,最终在次卧靠窗的笼子里看到了俩兄弟。\n俩兄弟据说是寄卖在宠物店,只剩他俩了,当晚会再来一位顾客,领走兄弟之一,如果选择的话,只能带走一只。\n但是和其他猫不同的是,抱着俩兄弟到怀里的一瞬间,都会响起“柴油发动机”类似的呼噜声,俩兄弟活似小狐狸,茂密的毛发摸起来很爽。\n考虑一来女票一直以来很喜欢布偶猫,二来当时两个人上班都不在望京,担心小猫一个人在家寂寞,于是和宠物店主简单沟通了一下,“夺人之爱”的一次性购入俩兄弟,虽说不符合之前的领猫硬标准,但是女票开心,也就罢了。\n从望京五点出发,折腾到八点准备回家,来回车程70公里,到家快十一点,不过带着俩主子累并快乐着。\n\n品质待商榷的问题猫粮\n带回来后使用了店主强力推荐的“METZ”玫斯猫粮,第一次装满饭缸,看到他们狼吞虎咽。\n我和女票笑着说:“宠物店是不是太抠门了,把它们饿成这样。”\n记得我还和公司养猫的小姐姐推荐过这个牌子,谁知道这里居然有天坑,在喂了一周左右,和周围养猫的朋友沟通养猫经验,大家均表示没有喂过也不知道这个牌子,虽然猫粮品牌众多,但是还是让我心生疑窦,于是回家各种搜索引擎换着用,发现了一个我不想接受的事实:\n这个猫粮被曝光许久,是一个“假洋牌”,据说掺入了“诱食剂”。\n加上家里的宠物粮包装袋出现了很多猫咬过的痕迹,有网友说家里的同款猫粮也被宠物疯狂咬食过。')])
[SystemMessage(content='你是一个用于问答任务的助手。只能用下面的检索到的内容来回答问题。如果你不知道答案,就说你不知道。最多用三句话回答问题,保持简洁。\n\n("Source: {\'source\': \'https://soulteary.com/2018/07/23/experience-in-breeding-cat.html\', \'section\': \'beginning\'}\\nContent: 必须有眼缘、性格好\\n最好有白手套\\n最好有白肚皮\\n最好是开脸\\n最好能够是红虎斑(俗称橘猫)\\n脸形要突出有层次,第一只猫最好不是扁脸\\n\\n正巧看到 58心宠 有一家官方认证的宠物店有不少英短,对其中一只小公主颇为满意,于是打车前往,期望能抱得小猫归。\\n实施领猫计划\\n到了猫舍直奔主题,然而和英短小公主缘分不够:\\n我、女票、宠物店主都抱不住它(不能安静的抱在怀里,只想挣脱)。\\n猫舍是一个三居室,里面有很多双层的大铁笼,和其他宠物店无二,类似日本胶囊公寓里装满了:英短、美短、折耳。\\n想着过来花费了不少时间,看看再走吧,于是就对小猫们逐个看了起来,最终在次卧靠窗的笼子里看到了俩兄弟。\\n俩兄弟据说是寄卖在宠物店,只剩他俩了,当晚会再来一位顾客,领走兄弟之一,如果选择的话,只能带走一只。\\n但是和其他猫不同的是,抱着俩兄弟到怀里的一瞬间,都会响起“柴油发动机”类似的呼噜声,俩兄弟活似小狐狸,茂密的毛发摸起来很爽。\\n考虑一来女票一直以来很喜欢布偶猫,二来当时两个人上班都不在望京,担心小猫一个人在家寂寞,于是和宠物店主简单沟通了一下,“夺人之爱”的一次性购入俩兄弟,虽说不符合之前的领猫硬标准,但是女票开心,也就罢了。\\n从望京五点出发,折腾到八点准备回家,来回车程70公里,到家快十一点,不过带着俩主子累并快乐着。\\n\\n品质待商榷的问题猫粮\\n带回来后使用了店主强力推荐的“METZ”玫斯猫粮,第一次装满饭缸,看到他们狼吞虎咽。\\n我和女票笑着说:“宠物店是不是太抠门了,把它们饿成这样。”\\n记得我还和公司养猫的小姐姐推荐过这个牌子,谁知道这里居然有天坑,在喂了一周左右,和周围养猫的朋友沟通养猫经验,大家均表示没有喂过也不知道这个牌子,虽然猫粮品牌众多,但是还是让我心生疑窦,于是回家各种搜索引擎换着用,发现了一个我不想接受的事实:\\n这个猫粮被曝光许久,是一个“假洋牌”,据说掺入了“诱食剂”。\\n加上家里的宠物粮包装袋出现了很多猫咬过的痕迹,有网友说家里的同款猫粮也被宠物疯狂咬食过。", [Document(id=\'3a4569de-b4dd-4aa9-b926-42954723d450\', metadata={\'source\': \'https://soulteary.com/2018/07/23/experience-in-breeding-cat.html\', \'section\': \'beginning\'}, page_content=\'必须有眼缘、性格好\\n最好有白手套\\n最好有白肚皮\\n最好是开脸\\n最好能够是红虎斑(俗称橘猫)\\n脸形要突出有层次,第一只猫最好不是扁脸\\n\\n正巧看到 58心宠 有一家官方认证的宠物店有不少英短,对其中一只小公主颇为满意,于是打车前往,期望能抱得小猫归。\\n实施领猫计划\\n到了猫舍直奔主题,然而和英短小公主缘分不够:\\n我、女票、宠物店主都抱不住它(不能安静的抱在怀里,只想挣脱)。\\n猫舍是一个三居室,里面有很多双层的大铁笼,和其他宠物店无二,类似日本胶囊公寓里装满了:英短、美短、折耳。\\n想着过来花费了不少时间,看看再走吧,于是就对小猫们逐个看了起来,最终在次卧靠窗的笼子里看到了俩兄弟。\\n俩兄弟据说是寄卖在宠物店,只剩他俩了,当晚会再来一位顾客,领走兄弟之一,如果选择的话,只能带走一只。\\n但是和其他猫不同的是,抱着俩兄弟到怀里的一瞬间,都会响起“柴油发动机”类似的呼噜声,俩兄弟活似小狐狸,茂密的毛发摸起来很爽。\\n考虑一来女票一直以来很喜欢布偶猫,二来当时两个人上班都不在望京,担心小猫一个人在家寂寞,于是和宠物店主简单沟通了一下,“夺人之爱”的一次性购入俩兄弟,虽说不符合之前的领猫硬标准,但是女票开心,也就罢了。\\n从望京五点出发,折腾到八点准备回家,来回车程70公里,到家快十一点,不过带着俩主子累并快乐着。\\n\\n品质待商榷的问题猫粮\\n带回来后使用了店主强力推荐的“METZ”玫斯猫粮,第一次装满饭缸,看到他们狼吞虎咽。\\n我和女票笑着说:“宠物店是不是太抠门了,把它们饿成这样。”\\n记得我还和公司养猫的小姐姐推荐过这个牌子,谁知道这里居然有天坑,在喂了一周左右,和周围养猫的朋友沟通养猫经验,大家均表示没有喂过也不知道这个牌子,虽然猫粮品牌众多,但是还是让我心生疑窦,于是回家各种搜索引擎换着用,发现了一个我不想接受的事实:\\n这个猫粮被曝光许久,是一个“假洋牌”,据说掺入了“诱食剂”。\\n加上家里的宠物粮包装袋出现了很多猫咬过的痕迹,有网友说家里的同款猫粮也被宠物疯狂咬食过。\')])', additional_kwargs={}, response_metadata={}), HumanMessage(content='为什么选择猫粮?', additional_kwargs={}, response_metadata={}, id='931d1538-ee49-4c15-b469-318da61f0f24')]
================================== Ai Message ==================================
店主强力推荐了“METZ”玫斯猫粮,但后来发现这个品牌存在问题,疑似掺入了诱食剂,并且品质待商榷。