? ? ? ?這就是在LangGraph中創(chuàng)建代理執(zhí)行器的方式,和LangChain的執(zhí)行器功能類似。我們將進(jìn)一步探討狀態(tài)圖的接口以及返回結(jié)果的不同流式傳輸方法。

四、探索聊天代理執(zhí)行器

? ? ? ?我們將在LangGraph中探索聊天代理執(zhí)行器,這是一個(gè)設(shè)計(jì)用于處理基于聊天的模型的工具。此執(zhí)行器是唯一的,因?yàn)樗耆鶕?jù)輸入消息的列表進(jìn)行操作,通過向該列表中添加新消息來隨著時(shí)間的推移更新代理的狀態(tài)。

讓我們深入了解設(shè)置過程:

4.1 安裝軟件包:

? ? ? ?同樣需要LangChain軟件包,LangChain OpenAI用于模型,Tavily軟件包用于搜索工具,并為這些服務(wù)設(shè)置API密鑰。

!pip install --quiet -U langchain langchain_openai tavily-python
import osimport getpass
os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")os.environ["TAVILY_API_KEY"] = getpass.getpass("Tavily API Key:")os.environ["LANGCHAIN_TRACING_V2"] = "true"os.environ["LANGCHAIN_API_KEY"] = getpass.getpass("LangSmith API Key:")

4.2 設(shè)置工具和模型:

? ? ? ?我們將使用Tavily Search作為我們的工具,并設(shè)置一個(gè)工具執(zhí)行器來調(diào)用這些工具。對(duì)于模型,我們將使用LangChain集成中的Chat OpenAI模型,確保其在啟用流式進(jìn)行初始化。這使我們能夠流式返回tokens,并附加我們希望模型調(diào)用的函數(shù)。

from langchain_community.tools.tavily_search import TavilySearchResultsfrom langchain_openai import ChatOpenAIfrom langgraph.prebuilt import ToolExecutorfrom langchain.tools.render import format_tool_to_openai_functiontools = [TavilySearchResults(max_results=1)]tool_executor = ToolExecutor(tools)# We will set streaming=True so that we can stream tokens# See the streaming section for more information on this.model = ChatOpenAI(temperature=0, streaming=True)functions = [format_tool_to_openai_function(t) for t in tools]model = model.bind_functions(functions)

4.3 定義代理狀態(tài):

       代理狀態(tài)是一個(gè)簡單的字典,其中包含消息列表的鍵。我們將使用“add to”標(biāo)記,這樣隨著時(shí)間的推移,節(jié)點(diǎn)對(duì)此消息列表的任何更新都會(huì)累積。

from typing import TypedDict, Annotated, Sequenceimport operatorfrom langchain_core.messages import BaseMessage

class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add]

4.4 創(chuàng)建節(jié)點(diǎn)和邊:

       節(jié)點(diǎn)表示具體的工作任務(wù),邊連接節(jié)點(diǎn)。我們需要一個(gè)代理節(jié)點(diǎn)來調(diào)用語言模型并獲得響應(yīng),一個(gè)操作節(jié)點(diǎn)來查看是否有任何工具需要調(diào)用,以及一個(gè)函數(shù)來確定我們是否應(yīng)該繼續(xù)調(diào)用工具或完成。

from langgraph.prebuilt import ToolInvocationimport jsonfrom langchain_core.messages import FunctionMessage
# Define the function that determines whether to continue or notdef should_continue(state): messages = state['messages'] last_message = messages[-1] # If there is no function call, then we finish if "function_call" not in last_message.additional_kwargs: return "end" # Otherwise if there is, we continue else: return "continue"
# Define the function that calls the modeldef call_model(state): messages = state['messages'] response = model.invoke(messages) # We return a list, because this will get added to the existing list return {"messages": [response]}
# Define the function to execute toolsdef call_tool(state): messages = state['messages'] # Based on the continue condition # we know the last message involves a function call last_message = messages[-1] # We construct an ToolInvocation from the function_call action = ToolInvocation( tool=last_message.additional_kwargs["function_call"]["name"], tool_input=json.loads(last_message.additional_kwargs["function_call"]["arguments"]), ) # We call the tool_executor and get back a response response = tool_executor.invoke(action) # We use the response to create a FunctionMessage function_message = FunctionMessage(content=str(response), name=action.tool) # We return a list, because this will get added to the existing list return {"messages": [function_message]}

4.5 構(gòu)建圖:

       我們創(chuàng)建一個(gè)具有代理狀態(tài)的圖,為代理和動(dòng)作添加節(jié)點(diǎn),并將入口點(diǎn)設(shè)置為代理節(jié)點(diǎn)。條件邊是根據(jù)代理應(yīng)該繼續(xù)還是結(jié)束來添加的,并且正常邊總是在動(dòng)作后返回到代理。

from langgraph.graph import StateGraph, END# Define a new graphworkflow = StateGraph(AgentState)
# Define the two nodes we will cycle betweenworkflow.add_node("agent", call_model)workflow.add_node("action", call_tool)
# Set the entrypoint as agent# This means that this node is the first one calledworkflow.set_entry_point("agent")
# We now add a conditional edgeworkflow.add_conditional_edges( # First, we define the start node. We use agent. # This means these are the edges taken after the agent node is called. "agent", # Next, we pass in the function that will determine which node is called next. should_continue, # Finally we pass in a mapping. # The keys are strings, and the values are other nodes. # END is a special node marking that the graph should finish. # What will happen is we will call should_continue, and then the output of that # will be matched against the keys in this mapping. # Based on which one it matches, that node will then be called. { # If tools, then we call the tool node. "continue": "action", # Otherwise we finish. "end": END })
# We now add a normal edge from tools to agent.# This means that after tools is called, agent node is called next.workflow.add_edge('action', 'agent')
# Finally, we compile it!# This compiles it into a LangChain Runnable,# meaning you can use it as you would any other runnableapp = workflow.compile()

4.6 編譯和使用圖形:

       編譯圖形后,我們創(chuàng)建一個(gè)帶有消息鍵的輸入字典。運(yùn)行圖形將處理這些消息,將AI響應(yīng)、功能結(jié)果和最終輸出添加到消息列表中。

from langchain_core.messages import HumanMessage
inputs = {"messages": [HumanMessage(content="what is the weather in sf")]}app.invoke(inputs)

4.7 觀察執(zhí)行過程:

? ? ? ?使用LangSmith,我們可以看到我們的代理所采取的詳細(xì)步驟,包括對(duì)OpenAI的調(diào)用和由此產(chǎn)生的輸出。

流式功能:LangGraph還提供流式功能。

五、如何在循環(huán)中修改humans操作

? ? ? ?讓我們修改LangGraph中的聊天代理執(zhí)行器,使其包含一個(gè)“human in the loop”組件,這樣在執(zhí)行工具操作之前可以進(jìn)行人工驗(yàn)證。

       設(shè)置:初始設(shè)置保持不變。不需要額外安裝。我們將創(chuàng)建我們的工具,設(shè)置工具執(zhí)行器,準(zhǔn)備我們的模型,將工具綁定到模型,并定義代理狀態(tài)——所有這些都與我們?cè)谇耙粋€(gè)會(huì)話中所做的一樣。

       關(guān)鍵修改——調(diào)用工具功能:主要的變化來自調(diào)用工具功能。我們添加了一個(gè)步驟,系統(tǒng)在交互式IDE中提示用戶(即您?。儐柺欠窭^續(xù)執(zhí)行特定操作。如果用戶響應(yīng)“否”,則會(huì)引發(fā)錯(cuò)誤,進(jìn)程將停止。這是我們的人工驗(yàn)證步驟。

# Define the function to execute toolsdef call_tool(state): messages = state['messages'] # Based on the continue condition # we know the last message involves a function call last_message = messages[-1] # We construct an ToolInvocation from the function_call action = ToolInvocation( tool=last_message.additional_kwargs["function_call"]["name"], tool_input=json.loads(last_message.additional_kwargs["function_call"]["arguments"]), ) response = input(prompt=f"[y/n] continue with: {action}?") if response == "n": raise ValueError # We call the tool_executor and get back a response response = tool_executor.invoke(action) # We use the response to create a FunctionMessage function_message = FunctionMessage(content=str(response), name=action.tool) # We return a list, because this will get added to the existing list return {"messages": [function_message]}

       使用修改的執(zhí)行器:當(dāng)我們運(yùn)行這個(gè)修改的執(zhí)行程序時(shí),它會(huì)在執(zhí)行任何工具操作之前請(qǐng)求批準(zhǔn)。如果我們同意說“是”,它將正常進(jìn)行。然而,如果我們說“不”,則會(huì)引發(fā)錯(cuò)誤并停止該過程。

utput from node 'agent':---{'messages': [AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\n "query": "weather in San Francisco"\n}', 'name': 'tavily_search_results_json'}})]}
---
---------------------------------------------------------------------------ValueError Traceback (most recent call last)Cell In[10], line 4 1 from langchain_core.messages import HumanMessage 3 inputs = {"messages": [HumanMessage(content="what is the weather in sf")]}----> 4 for output in app.stream(inputs): 5 # stream() yields dictionaries with output keyed by node name 6 for key, value in output.items(): 7 print(f"Output from node '{key}':")

? ? ? ?這是一個(gè)基本的實(shí)現(xiàn)。在現(xiàn)實(shí)世界中,您可能希望用更復(fù)雜的響應(yīng)來代替錯(cuò)誤,并使用更用戶友好的界面,而不是Jupyter筆記本。但這讓您清楚地了解了如何將一個(gè)簡單而有效的人工循環(huán)組件添加到LangGraph代理中。

六、修改管理代理步驟

? ? ? ?讓我們來看看在LangGraph中修改聊天代理執(zhí)行器,以在處理消息時(shí)操縱代理的內(nèi)部狀態(tài)。

       本教程建立在基本的聊天代理執(zhí)行程序設(shè)置的基礎(chǔ)上,因此,如果您還沒有在基本筆記本中完成初始設(shè)置,請(qǐng)先完成。我們?cè)谶@里只關(guān)注新的修改。

       關(guān)鍵修改——過濾消息:我們引入的主要更改是過濾傳遞給模型的消息的方法。現(xiàn)在,您可以自定義代理考慮的消息。例如:

def call_model(state): messages = state['messages'][-5:] response = model.invoke(messages) # We return a list, because this will get added to the existing list return {"messages": [response]}

       此修改是一個(gè)小而強(qiáng)大的添加,允許您控制代理如何與其消息歷史進(jìn)行交互,并改進(jìn)其決策過程。

       使用修改的執(zhí)行器:實(shí)現(xiàn)非常簡單。僅有一條輸入消息不同,但重要的是,您希望應(yīng)用于代理步驟的任何邏輯都可以插入到這個(gè)新的修改部分。

       此方法非常適合修改聊天代理執(zhí)行器,但如果使用標(biāo)準(zhǔn)代理執(zhí)行器時(shí),同樣的原理也適用。

七、強(qiáng)制調(diào)用工具

? ? ? ?我們將對(duì)LangGraph中的聊天代理執(zhí)行器進(jìn)行簡單但有效的修改,確保始終首先調(diào)用一個(gè)工具。這是建立在基本的聊天代理執(zhí)行器筆記本上的,所以請(qǐng)確保您已經(jīng)檢查了背景信息。

       關(guān)鍵修改——強(qiáng)制工具調(diào)用優(yōu)先:我們這里的重點(diǎn)是設(shè)置聊天代理調(diào)用特定工具作為其第一個(gè)操作。為此,我們將添加一個(gè)新節(jié)點(diǎn),并將其命名為“first model node”。該節(jié)點(diǎn)將被編程為返回一條消息,指示代理調(diào)用特定工具,如“Tavil search results Json”工具,并將最新的消息內(nèi)容作為查詢。

# This is the new first - the first call of the model we want to explicitly hard-code some actionfrom langchain_core.messages import AIMessageimport json
def first_model(state): human_input = state['messages'][-1].content return { "messages": [ AIMessage( content="", additional_kwargs={ "function_call": { "name": "tavily_search_results_json", "arguments": json.dumps({"query": human_input}) } } ) ] }

        更新圖:我們將修改現(xiàn)有的圖,將這個(gè)新的“first agent”節(jié)點(diǎn)作為入口點(diǎn)。這樣可以確保始終首先調(diào)用第一個(gè)代理節(jié)點(diǎn),然后調(diào)用動(dòng)作節(jié)點(diǎn)。我們?cè)O(shè)置了一個(gè)從代理到動(dòng)作或結(jié)束的條件節(jié)點(diǎn),以及一個(gè)從動(dòng)作回到代理的直接節(jié)點(diǎn)。關(guān)鍵的添加是從第一個(gè)代理到操作的一個(gè)新節(jié)點(diǎn),確保工具調(diào)用一開始就發(fā)生。

from langgraph.graph import StateGraph, END# Define a new graphworkflow = StateGraph(AgentState)
# Define the new entrypointworkflow.add_node("first_agent", first_model)
# Define the two nodes we will cycle betweenworkflow.add_node("agent", call_model)workflow.add_node("action", call_tool)
# Set the entrypoint as agent# This means that this node is the first one calledworkflow.set_entry_point("first_agent")
# We now add a conditional edgeworkflow.add_conditional_edges( # First, we define the start node. We use agent. # This means these are the edges taken after the agent node is called. "agent", # Next, we pass in the function that will determine which node is called next. should_continue, # Finally we pass in a mapping. # The keys are strings, and the values are other nodes. # END is a special node marking that the graph should finish. # What will happen is we will call should_continue, and then the output of that # will be matched against the keys in this mapping. # Based on which one it matches, that node will then be called. { # If tools, then we call the tool node. "continue": "action", # Otherwise we finish. "end": END })
# We now add a normal edge from tools to agent.# This means that after tools is called, agent node is called next.workflow.add_edge('action', 'agent')
# After we call the first agent, we know we want to go to actionworkflow.add_edge('first_agent', 'action')
# Finally, we compile it!# This compiles it into a LangChain Runnable,# meaning you can use it as you would any other runnableapp = workflow.compile()

       使用修改的執(zhí)行器:當(dāng)我們運(yùn)行這個(gè)更新的執(zhí)行器時(shí),第一個(gè)結(jié)果會(huì)很快返回,因?yàn)槲覀兝@過了初始的語言模型調(diào)用,直接調(diào)用該工具。通過觀察LangSmith中的過程可以證實(shí)這一點(diǎn),在LangSmith中,我們可以看到工具是第一個(gè)被調(diào)用的東西,然后是最后的語言模型調(diào)用。

       這種修改是一種簡單而強(qiáng)大的方法,可以確保在聊天代理的工作流程中立即使用特定的工具。

參考文獻(xiàn):

[1] https://camunda.com/blog/2023/02/orchestration-vs-choreography/

[2] https://medium.com/@rajib76.gcp/langgraph-agent-orchestrator-9cb4da8179c3

[3] https://levelup.gitconnected.com/langgraph-create-a-hyper-ai-agent-0e74c61238cc

[4] https://python.langchain.com/docs/langgraph

[5] https://github.com/langchain-ai/langgraph/blob/main/examples/agent_executor/base.ipynb

[6] https://github.com/langchain-ai/langgraph/blob/main/examples/chat_agent_executor_with_function_calling/base.ipynb

[7] https://github.com/langchain-ai/langgraph/blob/main/examples/chat_agent_executor_with_function_calling/human-in-the-loop.ipynb

[8]?https://github.com/langchain-ai/langgraph/blob/main/examples/chat_agent_executor_with_function_calling/managing-agent-steps.ipynb

[9]?https://github.com/langchain-ai/langgraph/blob/main/examples/chat_agent_executor_with_function_calling/force-calling-a-tool-first.ipynb

文章轉(zhuǎn)自微信公眾號(hào)@ArronAI

上一篇:

LLM之LangChain(五)| 使用LangChain Agent分析非結(jié)構(gòu)化數(shù)據(jù)

下一篇:

LLM之LangChain(七)| 使用LangChain,LangSmith實(shí)現(xiàn)Prompt工程ToT
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)