from langgraph.graph import StateGraph from langgraph.graph.message import add_messages
classState(TypedDict): # Messages have the type "list". The `add_messages` function # in the annotation defines how this state key should be updated # (in this case, it appends messages to the list, rather than overwriting them) messages: Annotated[list, add_messages]
# The first argument is the unique node name # The second argument is the function or object that will be called whenever # the node is used. graph_builder.add_node("chatbot", chatbot)
我们的 State 中的 add_messages 函数将把 LLM
的响应消息附加到该状态中已经存在的任何消息上
添加顶点
接下来,添加一个入口点
entry,这告诉我们的流程图每次运行时从哪里开始工作
1
graph_builder.set_entry_point("chatbot")
类似的添加一个终点 finish,这指示流程图 any time
this node is run, you can exit.
1
graph_builder.set_finish_point("chatbot")
构建
最后能够运行我们的图,在图形生成器上调用 compile
这将创建一个 CompiledGraph,我们就可以进行
invoke 调用了
1
graph = graph_builder.compile()
最终的流程图结构应该为
Run
最后使用 invoke 或 stream 方法进行执行
1 2 3 4 5 6 7 8
whileTrue: user_input = input("User: ") if user_input.lower() in ["quit", "exit", "q"]: print("Goodbye!") break for event in graph.stream({"messages": ("user", user_input)}): for value in event.values(): print("Assistant:", value["messages"][-1].content)
Part.2 使用工具增强
这里我们使用维基百科的查询工具
1 2 3 4 5
from langchain_community.tools import WikipediaQueryRun from langchain_community.utilities import WikipediaAPIWrapper
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()) tools = [wikipedia]
LLM 绑定工具
我们需要将工具先和 LLM 绑定,即将工具调用以 JSON schema 的方式请求
LLM
1 2
model = Azure.chat_model_4o llm = model.bind_tools(tools)
defroute_tools( state: State, ) -> Literal["tools", "__end__"]: """ Use in the conditional_edge to route to the ToolNode if the last message has tool calls. Otherwise, route to the end. """ ifisinstance(state, list): ai_message = state[-1] elif messages := state.get("messages", []): ai_message = messages[-1] else: raise ValueError(f"No messages found in input state to tool_edge: {state}") ifhasattr(ai_message, "tool_calls") andlen(ai_message.tool_calls) > 0: return"tools" return"__end__"
# The `tools_condition` function returns "tools" if the chatbot asks to use a tool, and "__end__" if # it is fine directly responding. This conditional routing defines the main agent loop. graph_builder.add_conditional_edges( "chatbot", route_tools, # The following dictionary lets you tell the graph to interpret the condition's outputs as a specific node # It defaults to the identity function, but if you # want to use a node named something else apart from "tools", # You can update the value of the dictionary to something else # e.g., "tools": "my_tools" {"tools": "tools", "__end__": "__end__"}, ) # Any time a tool is called, we return to the chatbot to decide the next step graph_builder.add_edge("tools", "chatbot") graph_builder.set_entry_point("chatbot") graph = graph_builder.compile()
# The only difference is we change the `thread_id` here to "2" instead of "1" events = graph.stream( {"messages": [("user", user_input)]}, # here {"configurable": {"thread_id": "2"}}, stream_mode="values", ) for event in events: event["messages"][-1].pretty_print()
get state
可以通过 config 随时检查给定配置的流程图状态
1 2 3
snapshot = graph.get_state(config) snapshot snapshot.next# (since the graph ended this turn, `next` is empty. If you fetch a state from within a graph invocation, next tells which node will execute next)
graph = graph_builder.compile( checkpointer=memory, # This is new! interrupt_before=["tools"], # Note: can also interrupt __after__ actions, if desired. # interrupt_after=["tools"] )
1 2 3 4 5 6 7 8 9
user_input = "I'm learning LangGraph. Could you do some research on it for me?" config = {"configurable": {"thread_id": "1"}} # The config is the **second positional argument** to stream() or invoke()! events = graph.stream( {"messages": [("user", user_input)]}, config, stream_mode="values" ) for event in events: if"messages"in event: event["messages"][-1].pretty_print()
# `None` will append nothing new to the current state, letting it resume as if it had never been interrupted events = graph.stream(None, config, stream_mode="values") for event in events: if"messages"in event: event["messages"][-1].pretty_print()
answer = ( "LangGraph is an apple." ) new_messages = [ # The LLM API expects some ToolMessage to match its tool call. We'll satisfy that here. ToolMessage(content=answer, tool_call_id=existing_message.tool_calls[0]["id"]), # And then directly "put words in the LLM's mouth" by populating its response. AIMessage(content=answer), ]
new_messages[-1].pretty_print() graph.update_state( # Which state to update config, # The updated values to provide. The messages in our `State` are "append-only", meaning this will be appended # to the existing state. We will review how to update existing messages in the next section! {"messages": new_messages}, )
graph.update_state( config, {"messages": [AIMessage(content="I'm an AI expert!")]}, # Which node for this function to act as. It will automatically continue # processing as if this node just ran. as_node="chatbot", )
snapshot = graph.get_state(config) existing_message = snapshot.values["messages"][-1] print("Original") print("Message ID", existing_message.id) print(existing_message.tool_calls[0]) new_tool_call = existing_message.tool_calls[0].copy() new_tool_call["args"]["query"] = "Where is China?" new_message = AIMessage( content=existing_message.content, tool_calls=[new_tool_call], # Important! The ID is how LangGraph knows to REPLACE the message in the state rather than APPEND this messages id=existing_message.id, )
from langchain_anthropic import ChatAnthropic from langchain_community.tools.tavily_search import TavilySearchResults from langchain_core.messages import BaseMessage from typing_extensions import TypedDict
from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.graph import StateGraph from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition
classState(TypedDict): messages: Annotated[list, add_messages] # This flag is new ask_human: bool
定义请求协助工具
接下来定义一个 schema(tool)来让模型决定是否请求帮助
1 2 3 4 5 6 7 8 9
from langchain_core.pydantic_v1 import BaseModel
classRequestAssistance(BaseModel): """Escalate the conversation to an expert. Use this if you are unable to assist directly or if the user requires support beyond your permissions. To use this function, relay the user's 'request' so the expert can provide the right guidance. """
defhuman_node(state: State): new_messages = [] ifnotisinstance(state["messages"][-1], ToolMessage): # Typically, the user will have updated the state during the interrupt. # If they choose not to, we will include a placeholder ToolMessage to # let the LLM continue. new_messages.append( create_response("No response from human.", state["messages"][-1]) ) return { # Append the new messages "messages": new_messages, # Unset the flag "ask_human": False, }
# The rest is the same graph_builder.add_edge("tools", "chatbot") graph_builder.add_edge("human", "chatbot") graph_builder.set_entry_point("chatbot") memory = SqliteSaver.from_conn_string(":memory:") graph = graph_builder.compile( checkpointer=memory, # We interrupt before 'human' here instead. interrupt_before=["human"], )
""" 第一步 """ config = {"configurable": {"thread_id": "1"}} events = graph.stream( { "messages": [ ("user", "Where is China?") ] }, config, stream_mode="values", ) for event in events: if"messages"in event: event["messages"][-1].pretty_print()
""" 第二步 """ events = graph.stream( { "messages": [ ("Where is its capital?") ] }, config, stream_mode="values", ) for event in events: if"messages"in event: event["messages"][-1].pretty_print()
这里放一下结果吧
1 2 3 4 5 6 7 8 9 10 11 12
================================ Human Message =================================
Where is China? ================================== Ai Message ==================================
China is a country located in East Asia. It is the world's most populous country, with a population of over 1.4 billion people. China is bordered by 14 countries: Afghanistan, Bhutan, India, Kazakhstan, Kyrgyzstan, Laos, Mongolia, Myanmar, Nepal, North Korea, Pakistan, Russia, Tajikistan, and Vietnam. To the east, it has a coastline along the East China Sea, Korea Bay, Yellow Sea, and South China Sea. The capital city of China is Beijing. ================================ Human Message ================================= Where is its capital? ================================== Ai Message ================================== The capital of China is Beijing. Beijing is located in the northern part of the country. It is situated in the northern tip of the North China Plain, near the western coast of the Bohai Sea. The city is bordered by Hebei Province to the north, west, south, and a small section to the east, and by Tianjin Municipality to the southeast. Beijing is one of the most populous cities in the world and serves as the political, cultural, and educational center of China.
回看历史
上面让流程图进行了几次调用
下面可以通过 get_state_history 获取所有历史,并且保存
to_replay
1 2 3 4 5 6 7
to_replay = None for state in graph.get_state_history(config): print("Num Messages: ", len(state.values["messages"]), "Next: ", state.next) print("-" * 80) iflen(state.values["messages"]) == 2: # We are somewhat arbitrarily selecting a specific state based on the number of chat messages in the state. to_replay = state
# The `thread_ts` in the `to_replay.config` corresponds to a state we've persisted to our checkpointer. for event in graph.stream({ "messages": [ ("Where is its economic center?") ] }, to_replay.config, stream_mode="values"): if"messages"in event: event["messages"][-1].pretty_print()
1 2 3 4 5 6 7 8
================================ Human Message =================================
Where is its economic center? ================================== Ai Message ==================================
China's economic center is often considered to be Shanghai. Shanghai is the largest city in China by population and one of the largest urban areas in the world. It is a global financial hub and a major center for commerce, trade, and industry. The city hosts the Shanghai Stock Exchange, which is one of the largest stock exchanges in the world by market capitalization. Shanghai's strategic location on the Yangtze River Delta and its extensive port facilities make it a key player in both domestic and international trade. The city is also known for its modern skyline, including landmarks such as the Shanghai Tower and the Oriental Pearl TV Tower.