Agent實(shí)戰(zhàn)-JSON結(jié)構(gòu)化智能
隨著AI應(yīng)用工程的飛速發(fā)展,我們不難發(fā)現(xiàn)為大語(yǔ)言模型(LLMs)提供額外工具能大大增強(qiáng)其功能。
舉例來說,GPT3.5版本通過集成Bing搜索和Python解釋器實(shí)現(xiàn)了能力的躍遷。GPTs則直接將api調(diào)用作為工具進(jìn)行了集成,LLM會(huì)決定是直接作出回應(yīng),還是先調(diào)用它提供的工具。這些工具不僅限于獲取額外信息,它們還能發(fā)揮其他功能,比如幫用戶訂餐。
盡管OpenAI已經(jīng)用它的專門模型讓我們享受了工具使用的便捷,大多數(shù)其他LLM在函數(shù)調(diào)用和工具使用方面仍不及OpenAI的水平。我嘗試了Ollama上的多數(shù)模型,大多數(shù)在持續(xù)生成可用于代理的預(yù)定義結(jié)構(gòu)化輸出方面表現(xiàn)不佳。另一方面,也有一些模型是專為函數(shù)調(diào)用優(yōu)化的。但這些模型要么是采用難以理解的自定義提示架構(gòu),要么除了函數(shù)調(diào)用別無它用。
智能代理LLM與圖數(shù)據(jù)庫(kù)的交互示意圖
今天我們要探討的是如何實(shí)施一個(gè)基于JSON格式的LLM智能代理。
語(yǔ)義層的工具
LangChain文檔中的示例(JSON代理,HuggingFace示例)使用單字符串輸入的工具。但因?yàn)檎Z(yǔ)義層的工具需要稍微復(fù)雜一些的輸入,我需要進(jìn)行一些深入研究。下面是推薦工具的示例輸入:
all_genres = [
"Action",
"Adventure",
"Animation",
"Children",
"Comedy",
"Crime",
"Documentary",
"Drama",
"Fantasy",
"Film-Noir",
"Horror",
"IMAX",
"Musical",
"Mystery",
"Romance",
"Sci-Fi",
"Thriller",
"War",
"Western",
]
class RecommenderInput(BaseModel):
movie: Optional[str] = Field(descriptinotallow="用來推薦的電影")
genre: Optional[str] = Field(
descriptinotallow=("用于推薦的電影類型??蛇x項(xiàng)有:" f"{all_genres}")
)
推薦工具有兩個(gè)可選的輸入項(xiàng):電影和類型,并且我們?yōu)轭愋吞峁┝艘幌盗锌蛇x的值。雖然這些輸入項(xiàng)并不特別復(fù)雜,但比單一字符串輸入要高級(jí)一些,因此實(shí)現(xiàn)起來也略有不同。
基于JSON的LLM智能代理提示
在我的實(shí)現(xiàn)中,我深受現(xiàn)有的??hwchase17/react-json?
?提示的啟發(fā),這一提示可以在LangChain hub中找到。提示使用以下系統(tǒng)消息:
盡你所能回答下面的問題。你可以使用以下工具:
{tools}
你可以通過指定一個(gè)JSON塊來使用工具。
具體而言,這個(gè)JSON應(yīng)該包含一個(gè)`action`鍵(用來指定要使用的工具名稱)和一個(gè)`action_input`鍵(工具的輸入在這里)。
"action"鍵里的值應(yīng)當(dāng)僅為:{tool_names}
$JSON_BLOB應(yīng)該只包含單一的動(dòng)作,請(qǐng)不要返回一個(gè)列表包含多個(gè)動(dòng)作。以下是一個(gè)有效$JSON_BLOB的示例:
```
{{
"action": $TOOL_NAME,
"action_input": $INPUT
}}
```
每次回答都要遵循以下格式:
Question: 你需要回答的問題
Thought: 你應(yīng)該在思考要做什么
Action:
```
$JSON_BLOB
```
Observation: 動(dòng)作的結(jié)果
...(這種思考/動(dòng)作/觀察的過程可以重復(fù)N次)
Thought: 我現(xiàn)在知道最終答案了
Final Answer: 對(duì)原本提問的最終回答
開始!請(qǐng)記住每次回答時(shí)都要精確使用`Final Answer`這個(gè)詞。
提示的開始部分通過定義可用的工具來設(shè)定,后面我們將深入討論。提示中最關(guān)鍵的部分是對(duì)LLM輸出預(yù)期的指示。當(dāng)LLM需要使用工具時(shí),它應(yīng)該使用以下JSON結(jié)構(gòu):
{{
"action": $TOOL_NAME,
"action_input": $INPUT
}}
這就是為什么它被稱作基于JSON的代理:我們指導(dǎo)LLM在希望使用任何可用工具時(shí)生成一個(gè)JSON。然而,這只是輸出定義的一小部分。完整的輸出應(yīng)遵循以下結(jié)構(gòu):
Thought: 你應(yīng)該在思考要做什么
Action:
```
$JSON_BLOB
```
Observation: 動(dòng)作的結(jié)果
...(這可以重復(fù)N次)
Final Answer: 對(duì)原本提問的最終回答
LLM在輸出中總是需要解釋它正在做什么,即"Thought"部分。當(dāng)它想要使用任何可用的工具時(shí),它應(yīng)以JSON塊的形式提供動(dòng)作輸入。"Observation"部分留給工具的輸出,而當(dāng)代理決定可以回答用戶提出的問題時(shí),它應(yīng)使用"Final Answer"關(guān)鍵詞。以下是電影智能代理使用此結(jié)構(gòu)的一個(gè)實(shí)例。
在這個(gè)例子中,我們讓代理推薦一部喜劇片。由于代理的一個(gè)可用工具是推薦工具,它決定利用推薦工具,并提供了用JSON寫的輸入語(yǔ)法。幸運(yùn)的是,LangChain有一個(gè)內(nèi)置的JSON智能代理輸出解析器,我們無需操心其實(shí)現(xiàn)細(xì)節(jié)。然后,LLM從工具得到回應(yīng),并在提示語(yǔ)中作為觀察結(jié)果使用。由于工具提供了所有必要的信息,LLM認(rèn)為已經(jīng)有了足夠的信息來構(gòu)建可以交給用戶的最終答案。
我注意到對(duì)Mixtral的提示工程經(jīng)常失敗,它不總是只在需要工具時(shí)使用JSON語(yǔ)法。在我的測(cè)試中,當(dāng)它不想使用任何工具時(shí),有時(shí)它會(huì)使用如下的JSON動(dòng)作輸入:
{{
"action": Null,
"action_input": ""
}}
如果動(dòng)作為null或類似的,LangChain的輸出解析函數(shù)并不會(huì)忽視這個(gè)動(dòng)作,而是會(huì)報(bào)錯(cuò)說沒有定義null這個(gè)工具。我嘗試對(duì)此進(jìn)行提示修改,但沒能一直做到。因此,我決定增加一個(gè)假設(shè)性的閑聊工具,以便用戶想要進(jìn)行閑聊時(shí)代理可以調(diào)用。
response = (
"創(chuàng)建一個(gè)最終回答它們是否有任何關(guān)于電影或演員的問題"
)
class SmalltalkInput(BaseModel):
query: Optional[str] = Field(descriptinotallow="用戶提問")
class SmalltalkTool(BaseTool):
name = "Smalltalk"
description = "當(dāng)用戶打招呼或想要閑聊時(shí)適用"
args_schema: Type[BaseModel] = SmalltalkInput
def _run(
self,
query: Optional[str] = None,
run_manager: Optional[CallbackManagerForToolRun] = None,
) -> str:
"""使用該工具。"""
return response
如此,代理在用戶打招呼時(shí)可以決定使用一個(gè)假的Smalltalk工具,我們?cè)僖膊粫?huì)因?yàn)榻馕鰊ull或者缺失工具名而遇到問題了。
這樣的臨時(shí)彌補(bǔ)方法很管用,所以我選擇留用它。像之前說的,大多數(shù)模型并未被訓(xùn)練以產(chǎn)生操作輸入或者在不需要?jiǎng)幼鲿r(shí)生成文本,因此我們必須利用現(xiàn)有資源。至于操控模型以便它只在有必要時(shí)產(chǎn)生JSON動(dòng)作輸入,有時(shí)是成功的,有時(shí)則依賴情況而定。但像smalltalk工具這樣給它提供一個(gè)備選項(xiàng),可以避免出現(xiàn)異常。
在系統(tǒng)提示中定義工具輸入
如前所述,我需要弄清楚如何定義略微復(fù)雜的工具輸入,這樣LLM才能正確解釋它們。好笑的是,在我實(shí)現(xiàn)了一個(gè)自定義功能后,我找到了一個(gè)現(xiàn)成的LangChain功能,這個(gè)功能可以將自定義的Pydantic工具輸入定義轉(zhuǎn)換成Mixtral能識(shí)別的JSON對(duì)象。
from langchain.tools.render import render_text_description_and_args
tools = [RecommenderTool(), InformationTool(), Smalltalk()]
tool_input = render_text_description_and_args(tools)
print(tool_input)
它產(chǎn)生了以下的字符串描述:
"Recommender":"當(dāng)你需要推薦一部電影時(shí)使用",
"args":{
{
"movie":{
{
"title":"Movie",
"description":"用于推薦的電影",
"type":"string"
}
},
"genre":{
{
"title":"Genre",
"description":"用于推薦的電影類型。可選項(xiàng)有:['Action', 'Adventure', 'Animation', 'Children', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy', 'Film-Noir', 'Horror', 'IMAX', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western']",
"type":"string"
}
}
}
},
"Information":"當(dāng)你需要回答關(guān)于各種演員或電影問題時(shí)使用",
"args":{
{
"entity":{
{
"title":"Entity",
"description":"問題中提到的電影或人名",
"type":"string"
}
},
"entity_type":{
{
"title":"Entity Type",
"description":"實(shí)體的類型。可選項(xiàng)為'movie'或'person'",
"type":"string"
}
}
}
},
"Smalltalk":"當(dāng)用戶打招呼或想要閑聊時(shí)使用",
"args":{
{
"query":{
{
"title":"Query",
"description":"用戶提問",
"type":"string"
}
}
}
}
我們只需將這些工具描述復(fù)制粘貼到系統(tǒng)提示中,Mixtral就能正確使用這些提前定義的工具,這非常方便。
結(jié)論
為實(shí)現(xiàn)這個(gè)基于JSON的智能代理,Harrison Chase和LangChain團(tuán)隊(duì)已經(jīng)完成了大部分工作,我對(duì)此表示由衷的感謝。我只需要把碎片拼湊起來即可。正如所說,不要期待與GPT-4同等水平的性能。然而,我相信像Mixtral這樣更強(qiáng)大的開源LLMs可以立即當(dāng)做智能代理使用(比起GPT-4來可能需要更多的異常處理)。我期待未來會(huì)有更多開源LLMs被優(yōu)化以作為智能代理使用。
References
- Langchain模板:https://github.com/langchain-ai/langchain/tree/master/templates/neo4j-semantic-ollama?ref=blog.langchain.dev
- Jupyter筆記本版本:https://github.com/tomasonjo/blogs/blob/master/llm/ollama_semantic_layer.ipynb?ref=blog.langchain.dev
本文轉(zhuǎn)載自 ??AI小智??,作者: AI小智
