切換語言為:簡體

使用多級 tools 解決大模型中呼叫大規模 tools 的問題

  • 爱糖宝
  • 2024-06-06
  • 2098
  • 0
  • 0

前文

在業務中如果使用大模型的 tools 方式來解決問題,肯定會遇到一個問題,就是 tools 的數量太多了,tools 本身的描述加上引數的描述整個 token 的數量會超出大模型的輸入 token 限制,這種情況需要我們想辦法去解決。本文就是討論遇到這類問題的解決方案。

基本認識

首先我們要有以下幾點基本的認識:

  • 大模型的最強的最本質的能力是理解能力,理解能力是個很強的能力,理論上大模型什麼都可以幹,就像大腦一樣。

  • tools 本質上就是將遇到的問題進行分類,再往簡單說就是將業務上可能碰到的問題進行 if-else 劃分,每個 tool 會寫好具體的實現邏輯、入參、返回結果,我們只需要讓大模型幹兩件事情:第一步就是先讓大模型理解使用者的問題可以使用哪個 tool 來解決,第二個問題就是使用大模型從使用者的問題中抽取出適合該 tool 的入參。這樣可以保證在遇到 tools 可以解決的問題集合的時候,輸出都是穩定的。避免了原生的大模型的幻覺等原生缺點。

基礎思路

如果是通常情況下 tools 的數量比較少,其中的引數及其描述也比較少,使用最常見的方式進行即可,我這裏用 qwen 的 api 展示效果。可以看到我的 tools 中只有兩個:

  • 一個是獲取當前的時間 get_current_time ,其工具描述為“當你想知道現在的時間時非常有用”,這個 tool 沒有引數,直接返回結果即可。

  • 另一個是獲取某地的天氣 get_current_weather ,其工具描述為“當你想查詢指定城市的天氣時非常有用”,這個 tool 需要從問題中獲取引數 location

tools = [
    # 工具1 獲取當前時刻的時間
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "當你想知道現在的時間時非常有用。",
            "parameters": {}  # 因為獲取當前時間無需輸入引數,因此parameters為空字典
        }
    },  
    # 工具2 獲取指定城市的天氣
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "當你想查詢指定城市的天氣時非常有用。",
            "parameters": {  # 查詢天氣時需要提供位置,因此引數設定為location
                        "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或縣區,比如北京市、杭州市、餘杭區等。"
                    }
                }
            },
            "required": [
                "location"
            ]
        }
    }
]

messages = [
        {
            "content": '杭州天氣如何',
            "role": "user"
        }
]

response = Generation.call(  model='qwen-max',  messages=messages,  tools=tools,  seed=random.randint(1, 10000),   result_format='message' )

我們這裏問的問題是杭州天氣如何,那麼大模型就會同時去 tools 中檢視,自己透過理解問題選擇合適的工具為 get_current_weather ,並且提取出了引數“杭州”。日誌結果如下:

{
    "status_code": 200,
    "request_id": "bd803417-56a7-9597-9d3f-a998a35b0477",
    "code": "",
    "message": "",
    "output": {
        "text": null,
        "finish_reason": null,
        "choices": [
            {
                "finish_reason": "tool_calls",
                "message": {
                    "role": "assistant",
                    "content": "",
                    "tool_calls": [
                        {
                            "function": {
                                "name": "get_current_weather",
                                "arguments": "{"properties": {"location": "杭州"}, "type": "object"}"
                            },
                            "id": "",
                            "type": "function"
                        }
                    ]
                }
            }
        ]
    },
    "usage": {
        "input_tokens": 222,
        "output_tokens": 27,
        "total_tokens": 249
    }
}

進階思路

從上面的日誌結果中我們看到,裡面有一項是 input_tokens ,這個表示我們向大模型中傳入的 prompt 的 token 數量,包括了 tools 中的所有文字描述以及你的問題描述的 token 總和。 qwen-max 的上限是 6k ,而我在業務中需要用到的 tools 數量很大,而且引數的數量及其介紹也很多,這樣上面的基礎思路就明顯無法滿足我們的需求,因為一旦將所有的 tools 傳入大模型,肯定會報錯超出大模型的輸入限制。我們可以換一個思路,大模型接收所有的 tools 內容自動做了選 tool 和提引數這兩件事情,我可以手動控制分步做這兩件事:

  1. 第一步就是使用一個簡略的 tools ,這個裡面只有工具的名字和描述,沒有引數及其描述,先將這個 tools 傳入大模型讓大模型能夠先選擇出正確的 tool 。

  2. 第二步再將這個 tool 改造成長度只有 1 的 tools ,傳入大模型,讓其按照這個 tool 的預定引數來從問題中抽取入參即可。

這樣分兩步即可解決 tools 太多的問題。其實還可以擴充套件開來,我們將所有的 tools 進行多級分類,這樣前面的幾級只負責對問題識別和路由的過程,到最後的某個區域性分子樹或者葉子節點的時候對應的少量的 tools ,再解決問題就很簡單了。

我這裏簡單舉例,大家懂邏輯即可。首先我先簡單寫了一個 tool_choose_prompt 來讓大模型針對問題選擇合適的 tool 名字,然後根據名字再使用大模型做具體的任務。

tool_choose_prompt = [system_message(
    "你是一個擅長解析問題的助手,你可以根據問題解析出解決這個問題要用哪一類工具,具體有以下幾種工具:"
    "gxssln_tool、 dxtdpy_tool、 plan_tool,"
    "其中,gxssln_tool 用於解決管線問題、 dxtdpy_tool 用於解決地下通道問題、 plan_tool 用於解決計劃、專案、工程的問題。"
    "再返回結果時你只需要返回工具名即可。"),
    user_message(f"我現在的問題是【{question}】")
]
first_response = dashscope.Generation.call(model="qwen-max", messages=tool_choose_prompt, result_format='message')
content = str(first_response["output"]["choices"][0]["message"]["content"])
response = ""
if content == "gxssln_tool":
    response = dashscope.Generation.call(model="qwen-max", messages=tool_prompt, result_format='message', tools=gxssln_tool)
elif content == "dxtdpy_tool":
    response = dashscope.Generation.call(model="qwen-max", messages=tool_prompt, result_format='message', tools=dxtdpy_tool)
elif content == "plan_tool":
    response = dashscope.Generation.call(model="qwen-max", messages=tool_prompt, result_format='message', tools=plan_tool)
print(response['output']['choices'][0]['message']['tool_calls'])

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.