You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

langgraph_agent.py 5.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. """
  2. This example demonstrates how to create an AI agent using LangGraph.
  3. Based on the example in the LangGraph documentation:
  4. https://langchain-ai.github.io/langgraph/
  5. """
  6. import asyncio
  7. from dataclasses import dataclass
  8. from typing import Any, Callable, List, Literal
  9. from agnext.application import SingleThreadedAgentRuntime
  10. from agnext.components import RoutedAgent, message_handler
  11. from agnext.core import AgentId, MessageContext
  12. from langchain_core.messages import HumanMessage, SystemMessage
  13. from langchain_core.tools import tool # pyright: ignore
  14. from langchain_openai import ChatOpenAI
  15. from langgraph.graph import END, MessagesState, StateGraph
  16. from langgraph.prebuilt import ToolNode
  17. @dataclass
  18. class Message:
  19. content: str
  20. # Define the tools for the agent to use
  21. @tool # pyright: ignore
  22. def get_weather(location: str) -> str:
  23. """Call to surf the web."""
  24. # This is a placeholder, but don't tell the LLM that...
  25. if "sf" in location.lower() or "san francisco" in location.lower():
  26. return "It's 60 degrees and foggy."
  27. return "It's 90 degrees and sunny."
  28. # Define the tool-use agent using LangGraph.
  29. class LangGraphToolUseAgent(RoutedAgent):
  30. def __init__(self, description: str, model: ChatOpenAI, tools: List[Callable[..., Any]]) -> None: # pyright: ignore
  31. super().__init__(description)
  32. self._model = model.bind_tools(tools) # pyright: ignore
  33. # Define the function that determines whether to continue or not
  34. def should_continue(state: MessagesState) -> Literal["tools", END]: # type: ignore
  35. messages = state["messages"]
  36. last_message = messages[-1]
  37. # If the LLM makes a tool call, then we route to the "tools" node
  38. if last_message.tool_calls: # type: ignore
  39. return "tools"
  40. # Otherwise, we stop (reply to the user)
  41. return END
  42. # Define the function that calls the model
  43. async def call_model(state: MessagesState): # type: ignore
  44. messages = state["messages"]
  45. response = await self._model.ainvoke(messages)
  46. # We return a list, because this will get added to the existing list
  47. return {"messages": [response]}
  48. tool_node = ToolNode(tools) # pyright: ignore
  49. # Define a new graph
  50. self._workflow = StateGraph(MessagesState)
  51. # Define the two nodes we will cycle between
  52. self._workflow.add_node("agent", call_model) # pyright: ignore
  53. self._workflow.add_node("tools", tool_node) # pyright: ignore
  54. # Set the entrypoint as `agent`
  55. # This means that this node is the first one called
  56. self._workflow.set_entry_point("agent")
  57. # We now add a conditional edge
  58. self._workflow.add_conditional_edges(
  59. # First, we define the start node. We use `agent`.
  60. # This means these are the edges taken after the `agent` node is called.
  61. "agent",
  62. # Next, we pass in the function that will determine which node is called next.
  63. should_continue, # type: ignore
  64. )
  65. # We now add a normal edge from `tools` to `agent`.
  66. # This means that after `tools` is called, `agent` node is called next.
  67. self._workflow.add_edge("tools", "agent")
  68. # Finally, we compile it!
  69. # This compiles it into a LangChain Runnable,
  70. # meaning you can use it as you would any other runnable.
  71. # Note that we're (optionally) passing the memory when compiling the graph
  72. self._app = self._workflow.compile()
  73. @message_handler
  74. async def handle_user_message(self, message: Message, ctx: MessageContext) -> Message:
  75. # Use the Runnable
  76. final_state = await self._app.ainvoke(
  77. {
  78. "messages": [
  79. SystemMessage(
  80. content="You are a helpful AI assistant. You can use tools to help answer questions."
  81. ),
  82. HumanMessage(content=message.content),
  83. ]
  84. },
  85. config={"configurable": {"thread_id": 42}},
  86. )
  87. response = Message(content=final_state["messages"][-1].content)
  88. return response
  89. async def main() -> None:
  90. # Create runtime.
  91. runtime = SingleThreadedAgentRuntime()
  92. # Register the agent.
  93. await runtime.register(
  94. "langgraph_tool_use_agent",
  95. lambda: LangGraphToolUseAgent(
  96. "Tool use agent",
  97. ChatOpenAI(model="gpt-4o-mini"),
  98. [get_weather],
  99. ),
  100. )
  101. agent = AgentId("langgraph_tool_use_agent", key="default")
  102. # Start the runtime.
  103. runtime.start()
  104. # Send a message to the agent and get a response.
  105. response = await runtime.send_message(Message("What's the weather in SF?"), agent)
  106. print(response.content)
  107. # Stop the runtime.
  108. await runtime.stop()
  109. if __name__ == "__main__":
  110. import logging
  111. logging.basicConfig(level=logging.WARNING)
  112. logging.getLogger("agnext").setLevel(logging.DEBUG)
  113. asyncio.run(main())