前言
在上篇文章中langchain系列之基于create_react_agent 创建一个Agent-CSDN博客,开发了一个基于商品下单支付的智能体,但是存在一个问题是,就是在支付和下单的时候,智能体直接去创建订单支付了,在下单支付的时候,并没有询问用户的意图,基于这个原因,现在用langGraph完善上文中的功能,加入询问用户意图的功能
示例代码
备注:下面用到的商品查询、订单创建、下单的工具就是langchain系列之基于create_react_agent 创建一个Agent-CSDN博客 这个里面的三个工具
1、导入库
import json
import time
import os
from dotenv import load_dotenv
from langchain_core.tools import tool
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
from langchain.agents import create_react_agent
from typing import TypedDict,Union,Annotated,List
from langchain_core.messages import BaseMessage
from langchain_core.agents import AgentAction,AgentFinish
import operator
from langchain_core.agents import AgentAction,AgentFinish
from langgraph.graph import StateGraph,END
from langgraph.checkpoint.memory import MemorySaver
2、定义State以及模型
#定义graph的输入
class AgentState(TypedDict):
#定义用户的输入字符串
input: str
user_input: str
#定义用户的历史会话信息
chat_history: list[BaseMessage]
#对代理的给定调用的结果,需要"None"作为有效类型,因为这就是它的开头
agent_outcome: Union[AgentAction, AgentFinish, None]
# 行动和相应意见清单
# 在这里,我们用"operator.add"对此进行注释,以指示对的操作
# 该状态应添加到现有值中(而不是覆盖它)
intermediate_steps: Annotated[List[tuple[AgentAction, str]], operator.add]
# 工具列表
tools = [
query_goods,
create_order,
order_pay
]
llm_ds = init_chat_model(
"deepseek-chat",
api_key = os.getenv("DEEPSEEK_API_KEY"),
base_url = os.getenv("DEEPSEEK_URL"),
model_provider = "deepseek"
)
3、定义智能体
def react_agent(data):
print("***********************************************************react_agent***********************************************************")
print("我是智能体数据:",data)
# if ("agent_outcome" in data) and (isinstance(data["agent_outcome"], AgentFinish)):
# return {"agent_outcome": data["agent_outcome"]}
react_prompt = ChatPromptTemplate.from_template("""
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {input}
{agent_scratchpad}
注意:
agent_outcome中的的log是英语文的话,所以需要将log中的英文翻译成中文
"""
)
# 创建ReAct代理
agent = create_react_agent(
llm=llm_ds,
tools=tools,
prompt=react_prompt
#handle_parsing_errors=True # 添加错误处理
)
# 传递完整的数据,包含intermediate_steps
result = agent.invoke(data)
return {"agent_outcome": result}
4、定义工具执行器
def execute_tools(data):
agent_action = data["agent_outcome"]
# 如果是AgentAction才执行工具
if isinstance(agent_action, AgentAction):
# 获取工具名称和输入
tool_name = agent_action.tool
tool_input = agent_action.tool_input
# 对于create_order和order_pay工具,先询问用户
if tool_name in ["create_order", "order_pay"]:
agent_finish = handle_user_interaction(data, tool_name)
if agent_finish:
# 用户不同意,直接返回AgentFinish状态,这样should_continue会返回"end"
# return {"agent_outcome": agent_finish, "intermediate_steps": [],"input":agent_finish.return_values["output"]}
return {"input":agent_finish.return_values["output"],"intermediate_steps": []}
# 找到对应的工具函数
tool_func = next((t for t in tools if t.name == tool_name), None)
if tool_func:
# 执行工具函数
try:
tool_output = tool_func.invoke(tool_input)
return {"intermediate_steps": [(agent_action, str(tool_output))]}
except Exception as e:
return {"intermediate_steps": [(agent_action, f"Error: {str(e)}")]}
else:
return {"intermediate_steps": [(agent_action, f"Tool {tool_name} not found")]}
else:
# 如果不是AgentAction,返回空列表
return {"intermediate_steps": []}
5、定义条件边判断以及用户确认入口
# 定义下一步应该如何走的逻辑
def should_continue(data):
# 如果代理结果是AgentFinish,则返回"end"字符串
if isinstance(data["agent_outcome"], AgentFinish):
action_flag = "end"
else:
action_flag = "continue"
return action_flag
# 用于处理用户交互
def handle_user_interaction(state, action_type):
print("***********************************************************handle_user_interaction***********************************************************")
print("我的判断状态值:", state)
order_log = state["agent_outcome"].log
print("【购买信息】:", order_log)
if action_type == "create_order":
user_input = input("是否为您【创建订单】,输入'Y'则继续,输入'N'或其他则结束,并说明您的理由: ")
if user_input.upper() == "Y":
print("正在创建订单----------------------------------------")
return None
else:
# 创建AgentFinish对象,结束流程
return AgentFinish(
return_values={"output": f"订单创建被用户取消,理由是: {user_input},流程结束"},
log=""
)
elif action_type == "order_pay":
user_input = input("订单已经创建完成,是否为您【进行支付】,输入'Y'则继续,输入'N'或其他则结束,并说明您的理由: ")
if user_input.upper() == "Y":
print("正在支付订单----------------------------------------")
return None
else:
# 创建AgentFinish对象,结束流程
return AgentFinish(
return_values={"output": f"订单支付被用户取消,理由是: {user_input},流程结束"},
log=""
)
return None
6、创建图谱
# 持久化信息
memory = MemorySaver()
# 定义图
def langgraph():
# 定义图
workflow = StateGraph(AgentState)
# 添加节点
workflow.add_node("agent", react_agent)
workflow.add_node("action", execute_tools)
# 定义入口节点
workflow.set_entry_point("agent")
# 增加条件边
workflow.add_conditional_edges(
# 定义开始节点,使用agent,即意味着从agent开始
source="agent",
# 传入函数,来判定执行下一个执行节点
path=should_continue,
# 最后我们传入一个映射。
# key是字符串,value是其他节点。
# END是一个特殊的节点,标记图形应该完成。
# 我们将调用"should_continue",然后输出将与此映射中的键相匹配。
# 根据它匹配的节点,然后将调用该节点。
path_map={
"continue":"action",
"end":END,
}
)
# 添加从action节点回到agent节点的边,形成循环
workflow.add_edge("action", "agent")
app = workflow.compile(checkpointer=memory)
# print(app.get_graph().print_ascii())
return app
thread_config = {"configurable": {"thread_id": "my_thread_id"}}
app = langgraph()
inputs = {
"input": "我要买一部手机,我的预算是2000元,帮我推荐一款合适的手机,并进行下单支付",
"chat_history": [],
"user_input": None
# "intermediate_steps": [],
# "agent_outcome": None,
}
# 移除直接调用,只通过print_result函数运行
# result = app.invoke(inputs, config=thread_config)
# print("最终结果:", result['agent_outcome'].return_values['output'])
def print_result(inputs):
for msg in app.stream(inputs, thread_config, stream_mode="values"):
stateSnapshot = app.get_state(thread_config)
# if ("agent_outcome" in stateSnapshot.values) and (isinstance(stateSnapshot.values["agent_outcome"], AgentAction)):
# tool = stateSnapshot.values["agent_outcome"].tool
# if tool == "query_goods":
# print("正在查询商品信息=========================================")
if ("agent_outcome" in stateSnapshot.values) and (isinstance(stateSnapshot.values["agent_outcome"], AgentFinish)):
print("最终结果:", stateSnapshot.values['agent_outcome'].return_values['output'])
# 主程序入口
if __name__ == "__main__":
# 用户可以选择运行主流程或测试
# 取消注释下面的代码以运行主流程
app = langgraph()
inputs = {
"input": "我要买一部手机,我的预算是2000元,帮我推荐一款合适的手机,并进行下单支付",
"chat_history": [],
"user_input": None
}
print_result(inputs)
演示结果
1、完整的下单流程演示
进行下单确认
进行支付确认
2、下单和支付拒绝操作
拒绝下单
拒绝支付
写在最后
上面是一个简单的可控的智能体功能,完成了基本的用户询问操作。里面可以继续添加其他的操作功能,例如支付的时候用户输入支付方式、订单创建以后,取消订单以及变成可是的web页面操作等。