上一篇介紹了Agent與LangGraph的基礎技能Tool的必知必會
AI菜鳥向前飛 — LangChain系列之十三 - 關于Tool的必知必會
前面已經詳細介紹了Promp、RAG,終于來到Agent系列(別急后面還有LangGraph),大家可以先看下這張圖:? ??看完我這系列就都懂了:)
牛刀初試
????由于本篇是入門,我們直接邊看程序邊熟悉整個過程吧 先以BaseTool的方式實現一個Tool,代碼如下:
class search_article(BaseTool):name = "search_article"description = "查詢所有的文章來源"def _run(self, topic: str):return chain_rag.invoke({"question": topic})
關于chain_rag的內容,請參考我的這篇公眾號文章
LangChain實戰技巧之二:RunnablePassthrough.assign的兩則妙用
我們看看兩種Agent的“書寫”方式
-
第一種
agent = (RunnablePassthrough.assign(agent_scratchpad=lambda x: format_to_tool_messages(x["intermediate_steps"]))| hub.pull("hwchase17/openai-tools-agent")| model.bind_tools(tools=[search_article()])| ToolsAgentOutputParser()
)
-
第二種
agent = create_tool_calling_agent(prompt=hub.pull("hwchase17/openai-tools-agent"), llm=model, tools=[search_article()])
你喜歡哪種呢?
接下來你是不是想執行看看效果,結果會讓你大跌眼鏡、你沒有眼鏡的話配一個 先~
res = agent.invoke({"input": "AI菜鳥向前飛系列文章出自哪里?", "intermediate_steps": []})
這里為什么要加這個"intermediate_steps",不加會報錯,不信你試試,若要知道這個機制請看下篇:)
輸出結果
# 輸出了這樣一坨
[ToolAgentAction(tool='search_article', tool_input={'topic': 'AI菜鳥向前飛'}, log="\nInvoking: `search_article` with `{'topic': 'AI菜鳥向前飛'}`\n\n\n", message_log=[AIMessage(content='', additional_kwargs={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'd12bfa56-e394-48a0-bff1-97f76dabe92f', 'tool_calls': [{'id': '5617aa7b3ed74d1789befac4b6a9d573', 'function': {'name': 'search_article', 'arguments': '{"topic": "AI\\u83dc\\u9e1f\\u5411\\u524d\\u98de"}'}, 'type': 'function'}], 'token_count': {'output_tokens': 12}}, response_metadata={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'd12bfa56-e394-48a0-bff1-97f76dabe92f', 'tool_calls': [{'id': '5617aa7b3ed74d1789befac4b6a9d573', 'function': {'name': 'search_article', 'arguments': '{"topic": "AI\\u83dc\\u9e1f\\u5411\\u524d\\u98de"}'}, 'type': 'function'}], 'token_count': {'output_tokens': 12}}, id='run-592cded4-3fb7-48d3-99d7-87166d7bb232-0', tool_calls=[{'name': 'search_article', 'args': {'topic': 'AI菜鳥向前飛'}, 'id': '092624d480bd461daacc54fdda64c7b5'}])], tool_call_id='092624d480bd461daacc54fdda64c7b5')]
直截了當看結果
????通常的教法,應該是你需要引入AgentExecutor,而如果我也是這樣跟大家介紹的話,就不是我這個系列的風格了:)
????如果用AgentExecutor的話,代碼如下:
agent_executor = AgentExecutor(agent=agent, tools=[search_article()], verbose=True)
# 然后再invoke就能得到你想要的
agent_executor.invoke("AI菜鳥向前飛系列文章出自哪里?")
????直截了當
{'input': 'AI菜鳥向前飛系列文章出自哪里?', 'output': 'AI菜鳥向前飛系列文章出自Song榆錢兒的公眾號。'}
(換個方式)直奔Agent
????但是跑題了,我們主要講的是Agent,而不是AgentExecutor,也就是說如果僅用Agent呢?
????看到輸出內容
[ToolAgentAction(tool='search_article', tool_input={'topic': 'AI菜鳥向前飛'}, ……
????聰明的小伙伴可以用Python去解決問題,如下:
# 為啥這里要用這個,因為它是列表啊,為啥是列表,以后再介紹
for each in res:result = {"search_article": search_article()}[each.tool].invoke(each.tool_input)print(result)
????最后它確實被執行,結果如下:
content='AI菜鳥向前飛系列文章是出自公眾號"Song榆錢兒"的原創作品。截至目前,該系列已經有20多篇原創文章,并且擁有109名關注者。' additional_kwargs={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'f12940a2-38de-4bba-a7b0-254f40c90596', 'token_count': {'input_tokens': 156, 'output_tokens': 44}} response_metadata={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'f12940a2-38de-4bba-a7b0-254f40c90596', 'token_count': {'input_tokens': 156, 'output_tokens': 44}} id='run-f7cc290b-34f8-4eca-bcfa-85c3bcf2b74e-0'
也就是都是RAG的“功勞”,上面代碼提到的:
………………
return chain_rag.invoke({"question": topic})
柳暗花明
????各位看官會想,這跟我單獨執行Tool函數有啥區別,繞了一個大圈子,這秘密就存在于"intermediate_steps"當中(賣個關子,后面介紹)先看如下代碼:
intermediate_steps = []
while not isinstance(res := agent.invoke({"input": "AI菜鳥向前飛系列文章出自哪里?", "intermediate_steps": intermediate_steps}), AgentFinish):for each in res:func_ret = {"search_article": search_article()}[each.tool].invoke(each.tool_input)intermediate_steps.append((each, func_ret.content))
????是不是感覺跟用AgentExecutor一樣了:)
簡述ReAct
????最后分享一個聊到Agent 大部分博文都會提到的ReAct (Reason Act),以一個示例來演示下吧,各處重要內容,我都加上了注釋來為大家解釋:)
> Entering new AgentExecutor chain...
# 思考
Thought: I can answer this question by searching for the source of the "AI菜鳥向前飛" series.# 行動
Action: search_article
Action Input: AI菜鳥向前飛# 觀察
Observation content='AI菜鳥向前飛系列文章是出自Song榆錢兒的公眾號。該系列文章目前已經有20多篇原創文章,并且已有109人關注。' additional_kwargs={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'e07bf920-967a-4269-bae0-6acd4358a90f', 'token_count': {'input_tokens': 158, 'output_tokens': 38}} response_metadata={'documents': None, 'citations': None, 'search_results': None, 'search_queries': None, 'is_search_required': None, 'generation_id': 'e07bf920-967a-4269-bae0-6acd4358a90f', 'token_count': {'input_tokens': 158, 'output_tokens': 38}} id='run-efd02271-a86f-4dee-bd0e-0e9133efd7b7-0'
# 找到正解
Final Answer: AI菜鳥向前飛系列文章出自Song榆錢兒的公眾號。該系列文章目前已經有20多篇原創文章,并且已有109人關注。> Finished chain.
{'input': 'AI菜鳥向前飛系列文章出自哪里?', 'output': 'AI菜鳥向前飛系列文章出自Song榆錢兒的公眾號。該系列文章目前已經有20多篇原創文章,并且已有109人關注。'}
原理過程圖:關于這張圖,后面若大家有需要我再詳細深入講解
敬請期待下一篇:)