开启左侧

简单的LangGraph示例

[复制链接]
米落枫 发表于 7 小时前 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
作者:CSDN博客
在学习智能体,然后又接触到LangGraph,参照文档尝试了一个简单的LangGraph demo。
一、环境准备:
pip install langchainpip install langchain_openaipip install langgraph二、代码:
  1. from typing import TypedDict, Annotated, Sequence
  2. import operator
  3. from langchain_core.messages import BaseMessage
  4. from langchain.tools.render import format_tool_to_openai_function
  5. from langchain_openai import ChatOpenAI
  6. from langgraph.prebuilt import ToolExecutor
  7. from langchain_community.tools.tavily_search import TavilySearchResults
  8. from langgraph.prebuilt import ToolInvocation
  9. import json
  10. from langchain_core.messages import FunctionMessage
  11. from langgraph.graph import StateGraph, END
  12. from langchain_core.messages import HumanMessage
  13. # Import things that are needed generically
  14. from langchain.pydantic_v1 import BaseModel, Field
  15. from langchain.tools import BaseTool, StructuredTool, tool
  16. # 加载 .env 到环境变量,这样就能读取到 .env文件中的 OPENAI_API_KEY和OPENAI_BASE_URL这个设置
  17. from dotenv import load_dotenv, find_dotenv
  18. _ = load_dotenv(find_dotenv())
  19. # 自定义工具
  20. @tool
  21. def search(query: str) -> str:
  22.     """Look up things online."""
  23.     print(f"search: {query}")
  24.     return "sunny"
  25.    
  26.    
  27. @tool
  28. def multiply(a: int, b: int) -> int:
  29.     """Multiply two numbers."""
  30.     return a * b   
  31. tools = [search,multiply]
  32. tool_executor = ToolExecutor(tools)
  33. # We will set streaming=True so that we can stream tokens
  34. # See the streaming section for more information on this.
  35. model = ChatOpenAI(temperature=0, streaming=True)
  36. functions = [format_tool_to_openai_function(t) for t in tools]
  37. model = model.bind_functions(functions)
  38. class AgentState(TypedDict):
  39.     messages: Annotated[Sequence[BaseMessage], operator.add]
  40.    
  41. # Define the function that determines whether to continue or not
  42. def should_continue(state):
  43.     messages = state['messages']
  44.     last_message = messages[-1]
  45.     # If there is no function call, then we finish
  46.     if "function_call" not in last_message.additional_kwargs:
  47.         return "end"
  48.     # Otherwise if there is, we continue
  49.     else:
  50.         return "continue"
  51. # Define the function that calls the model
  52. def call_model(state):
  53.     messages = state['messages']
  54.     response = model.invoke(messages)
  55.     # We return a list, because this will get added to the existing list
  56.     return {"messages": [response]}
  57. # Define the function to execute tools
  58. def call_tool(state):
  59.     messages = state['messages']
  60.     # Based on the continue condition
  61.     # we know the last message involves a function call
  62.     last_message = messages[-1]
  63.     # We construct an ToolInvocation from the function_call
  64.     action = ToolInvocation(
  65.         tool=last_message.additional_kwargs["function_call"]["name"],
  66.         tool_input=json.loads(last_message.additional_kwargs["function_call"]["arguments"]),
  67.     )
  68.     # We call the tool_executor and get back a response
  69.     response = tool_executor.invoke(action)
  70.     print(f"response:{response}")
  71.     # We use the response to create a FunctionMessage
  72.     function_message = FunctionMessage(content=str(response), name=action.tool)
  73.     print(f"function_message:{function_message}")
  74.     # We return a list, because this will get added to the existing list
  75.     return {"messages": [function_message]}   
  76.    
  77.    
  78. # Define a new graph
  79. workflow = StateGraph(AgentState)
  80. # Define the two nodes we will cycle between
  81. workflow.add_node("agent", call_model)
  82. workflow.add_node("action", call_tool)
  83. # Set the entrypoint as `agent`
  84. # This means that this node is the first one called
  85. workflow.set_entry_point("agent")
  86. # We now add a conditional edge
  87. workflow.add_conditional_edges(
  88.     # First, we define the start node. We use `agent`.
  89.     # This means these are the edges taken after the `agent` node is called.
  90.     "agent",
  91.     # Next, we pass in the function that will determine which node is called next.
  92.     should_continue,
  93.     # Finally we pass in a mapping.
  94.     # The keys are strings, and the values are other nodes.
  95.     # END is a special node marking that the graph should finish.
  96.     # What will happen is we will call `should_continue`, and then the output of that
  97.     # will be matched against the keys in this mapping.
  98.     # Based on which one it matches, that node will then be called.
  99.     {
  100.         # If `tools`, then we call the tool node.
  101.         "continue": "action",
  102.         # Otherwise we finish.
  103.         "end": END
  104.     }
  105. )
  106. # We now add a normal edge from `tools` to `agent`.
  107. # This means that after `tools` is called, `agent` node is called next.
  108. workflow.add_edge('action', 'agent')
  109. # Finally, we compile it!
  110. # This compiles it into a LangChain Runnable,
  111. # meaning you can use it as you would any other runnable
  112. app = workflow.compile()   
  113. #inputs = {"messages": [HumanMessage(content="what is the weather in Beijing?")]}
  114. inputs = {"messages": [HumanMessage(content="3乘以5等于多少,输出最终的结果")]}
  115. response = app.invoke(inputs)
  116. print(type(response))
  117. print(f"last result:{response}")
  118. # 输出如下信息:
  119. # {'messages': [HumanMessage(content='3乘以5等于多少'), AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\n  "a": 3,\n  "b": 5\n}', 'name': 'multiply'}}, response_metadata={'finish_reason': 'function_call'}, id='run-bbf18160-747f-48ac-9a81-6c1ee3b70b07-0'), FunctionMessage(content='15', name='multiply'), AIMessage(content='3乘以5等于15。', response_metadata={'finish_reason': 'stop'}, id='run-0d1403cf-4ddb-4db2-8cfa-d0965666e62d-0')]}
复制代码
关于状态机、节点、边、有向无环图等概念可以去参照相关文档,在这里就不赘述了。
上面代码添加了2个节点,其分别为agent和action,还添加了1个条件边。
三、解释一下几个函数:
3.1. add_node(key,action):
        添加节点。节点是要做处理的。
        key 是节点的名字,后面会根据这个名字去确定这个节点的。
        action是一个函数或者一个LCEL runnable,这个函数或者 LCEL runnable 应该接收一个和状态对象一样的字典作为输入,
        其输出也是以状态对象中的属性为key的一个字典,从而更新状态对象中对应的值。
3.2. add_edge(start_key, end_key)
        在两个节点之间添加边(连线),从前往后
        start_key 开始节点的名字
        end_key   结束节点的名字

3.3. add_conditional_edges(source, path, path_map=None, then=None)
        添加条件边
        source (str) – 开始节点名
        path (Union[Callable, Runnable]) – 决定下一个节点的回调函数
        path_map (Optional[dict[str, str]], default: None ) – 映射下一个节点的字典.
        then (Optional[str], default: None ) – 执行完选择的节点后的下一个节点名

  3.4. set_entry_point(key)
        设置开始节点,图将从这个节点开始运行  
  3.5. compile(checkpointer=None, interrupt_before=None, interrupt_after=None, debug=False)
        编译state graph为CompiledGraph对象

原文地址:https://blog.csdn.net/happyweb/article/details/138366645
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

发布主题
阅读排行更多+

Powered by Discuz! X3.4© 2001-2013 Discuz Team.( 京ICP备17022993号-3 )