【Agent】我们怎样与agent交互,纠错agent

280 阅读4分钟

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_callsRequestAssistance 对象的时候,设置 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应用提供了强大灵活的处理方法。