Langgraph在自主软件工程AI应用中的使用。
我们现在想达到这种目的,通过langgraph倒回去纠正一些错误并尝试不同的方案,以后我们使用 agent
的时候肯定会非常普遍存在这个问题,毕竟大模型也有错误理解,给出错误答案的时候,我们该怎么解决呢。
在我们之前的典型机器人 chatbot
的工作路程中,用户可以与 chatbot
交互 1 次或多次以完成任务。我们在前面也通过添加内存和人工参与,以便能够检查我们的 Langgraph
状态并手动覆盖状态以控制未来的响应。
现在我们通过 get_state_history
来提供一种解决方案。
一、添加human节点
导入需要提前准备的库和配置文件
OPENAI_API_KEY = 'sk-XXXXXXXXXa'
LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
LANGCHAIN_API_KEY="XXXXXXXXXXXXXXXXXX"
LANGCHAIN_PROJECT="XXXXXXXXXX"
TAVILY_API_KEY = 'tvly-XXXXXXXXXXXX'
from dotenv import load_dotenv
load_dotenv()
from typing import Annotated
from langchain_openai import ChatOpenAI
from langchain_community.tools.tavily_search import TavilySearchResults
from typing_extensions import TypedDict
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from pydantic import BaseModel
定义graph的状态对象和是否需要人参与的咨询对象:
class State(TypedDict):
messages: Annotated[list, add_messages]
# This flag is new
ask_human: bool
class RequestAssistance(BaseModel):
request: str
大模型绑定工具:
tool = TavilySearchResults(max_results=2)
tools = [tool, RequestAssistance]
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools(tools)
定义 chatbot
节点,当大模型返回的 tool_calls
有 RequestAssistance
对象的时候,设置 ask_human = True
:
def chatbot(state: State):
response = llm_with_tools.invoke(state["messages"])
ask_human = False
if (
response.tool_calls
and response.tool_calls[0]["name"] == RequestAssistance.__name__
):
ask_human = True
return {"messages": [response], "ask_human": ask_human}
添加 chatbot
节点,和 tool
节点:
graph_builder = StateGraph(State)
graph_builder.add_node("chatbot", chatbot)
graph_builder.add_node("tools", ToolNode(tools=[tool]))
添加人类节点,这里会检查消息历史中的最后一条消息是否是工具返回的消息,当人类节点被调用后,设置 "ask_human": False,防止反复调用:
ef create_response(response: str, ai_message: AIMessage):
return ToolMessage(
content=response,
tool_call_id=ai_message.tool_calls[0]["id"],
)
def human_node(state: State):
new_messages = []
if not isinstance(state["messages"][-1], ToolMessage):
new_messages.append(
create_response("No response from human.", state["messages"][-1])
)
return {
"messages": new_messages,
"ask_human": False,
}
graph_builder.add_node("human", human_node)
二、设置条件边和建立graph
设置条件路由,当 ask_human = True
的时候,调用 human
节点:
def select_next_node(state: State):
if state["ask_human"]:
return "human"
return tools_condition(state)
建立graph结构:
graph_builder.add_conditional_edges(
"chatbot",
select_next_node,
{"human": "human", "tools": "tools", END: END},
)
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge("human", "chatbot")
graph_builder.add_edge(START, "chatbot")
memory = MemorySaver()
graph = graph_builder.compile(
checkpointer=memory,
interrupt_before=["human"],
)
这里我们可以把图完整的展示出来,方便我们清晰的看到当前 agent
执行流程:
from IPython.display import Image, display
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
# This requires some extra dependencies and is optional
pass
三、纠错
调用 graph
问问题:
config = {"configurable": {"thread_id": "1"}}
events = graph.stream(
{
"messages": [
("user", "我正在学习langgraph,你能给我依稀建议吗?")
]
},
config,
stream_mode="values",
)
for event in events:
if "messages" in event:
event["messages"][-1].pretty_print()
传入 None
继续执行任务:
config = {"configurable": {"thread_id": "1"}}
events = graph.stream(
None,
config,
stream_mode="values",
)
for event in events:
if "messages" in event:
event["messages"][-1].pretty_print()
继续与
agent
对话:
events = graph.stream(
{
"messages": [
("user", "对这个很有用,我是不是可以通过它建立一个agent!")
]
},
config,
stream_mode="values",
)
for event in events:
if "messages" in event:
event["messages"][-1].pretty_print()
到这里我们就可以通过
replay
完整的状态历史记录来查看前面发生的所有事情。我们把历史记录中的第二个检查点信息保存下来,待会好做状态恢复的哦这个检查点。
to_replay = None
for state in graph.get_state_history(config):
print("Num Messages: ", len(state.values["messages"]), "Next: ", state.next)
print("-" * 80)
if len(state.values["messages"]) == 2:
to_replay = state
打印
to_replay
,我们看一下里面存的是什么:
print(to_replay.next)
print(to_replay.config)
这里可以看出来里面保存的是当前检查点的配置,包含一个
checkpoint_id timestamp
。提供此 checkpoint_id
值会告诉 LangGraph
的检查点从该时刻加载状态。这个是我们回溯到某个节点的关键,方便我们进行纠错,让我们在下面尝试一下:
for event in graph.stream(None, to_replay.config, stream_mode="values"):
if "messages" in event:
event["messages"][-1].pretty_print()
可以看到这里
agent
再次回到了 human
节点向 human
问询答复,从 human
节点恢复执行,虽然没得到答复(由于这个时候 ask_human
已经是 false
),agent
直接调用大模型得到了最终答案,走向 END
。
四、总结
我们从上面的例子可以看出,我们已经实现了怎么给 agent
纠错,我们用的就是 LangGraph
中的时间检查点遍历的方式。这为我们做自主软件工程AI应用提供了强大灵活的处理方法。