建立一個有效的代理與一個無效的代理之間有著天壤之別。我們該如何建立屬於後者的代理程式呢?在本指南中,我們將介紹建立代理程式的最佳做法。
如果您是建立代理商的新手,請務必先閱讀 引言 給代理商 和 smolagents 導覽.
最好的代理系統是最簡單的:盡可能簡化工作流程 #
在您的工作流程中給予 LLM 一些代理權會帶來一些出錯的風險。
編程良好的代理系統無論如何都有良好的錯誤記錄和重試機制,因此 LLM 引擎有機會自我糾正錯誤。但要最大限度地降低 LLM 出錯的風險,您應該簡化工作流程!
讓我們重溫 [intro_agents] 中的範例:一個為衝浪旅遊公司回答使用者查詢的機器人。與其讓代理每次詢問一個新的衝浪地點時,分別呼叫「旅途距離 API」和「天氣 API」,您可以只製作一個統一的工具「return_spot_information」,這個函數可以一次呼叫兩個 API,並將它們的串接輸出回傳給使用者。
這將降低成本、延遲及錯誤風險!
主要準則是:儘量減少 LLM 來電的次數。
這導致了一些啟示:
- 在可能的情況下,將 2 個工具合二為一,就像我們範例中的兩個 API。
- 只要有可能,邏輯就應該以確定的函數為基礎,而不是以代理人的決策為基礎。
改善 LLM 引擎的資訊流 #
請記住,您的 LLM 引擎就像是一個~智慧型~機器人,被困在一個房間裡,與外界唯一的溝通方式就是在門下傳紙條。
如果您沒有在提示中明確說明,它就不會知道發生了什麼事。
因此,首先要讓您的任務非常清楚!由於代理是由 LLM 驅動的,因此您的任務表達稍有不同,就可能產生完全不同的結果。
然後,在工具使用上改善對您的代理商的資訊流程。
需要遵循的特定指引:
- 每個工具都應該記錄(只需使用
列印
語句在工具的前言
方法)對 LLM 引擎有用的一切東西。- 特別是,記錄工具執行錯誤的詳細資訊會有很大幫助!
例如,這裡有一個工具可以根據位置和日期時間擷取天氣資料:
首先,這是一個差勁的版本:
複製
匯入日期時間 從 smolagents 匯入工具 def get_weather_report_at_coordinates(coordinates, date_time): # 模擬函數,傳回 [溫度 (攝氏度)、降雨風險 (0-1)、波高 (公尺)] 清單 返回 [28.0, 0.35, 0.85] def get_coordinates_from_location(location): # 返回假坐標 return [3.3, -42.0] @tool def get_weather_api(location: str, date_time: str) -> str: """ 傳回天氣報告。 Args: location:您想要天氣報告的地點名稱。 date_time:您想要報告的日期和時間。 """ lon, lat = convert_location_too_coordinates(location) date_time = datetime.strptime(date_time) return str(get_weather_report_at_coordinates((lon, lat), date_time))
為什麼不好?
- 沒有精確的格式應該用於
日期時間
- 沒有詳細說明如何指定位置。
- 沒有日誌機制可追蹤明確的失敗情況,例如位置格式不正確,或日期時間格式不正確。
- 輸出格式難以理解
如果工具呼叫失敗,記錄在記憶體中的錯誤軌跡可以幫助 LLM 逆向工程工具來修正錯誤。但為什麼要讓它做這麼多繁重的工作呢?
建立此工具的更好方法如下:
複製
@tool def get_weather_api(location: str, date_time: str) -> str: """ 傳回天氣報告。 Args: location:您想要天氣報告的地點名稱。應該是地名,接著可能是城市名稱,然後是國家,例如「Anchor Point, Taghazout, Morocco」。 date_time:您想要報告的日期和時間,格式為「%m/%d/%y %H:%M:%S」。 """ lon, lat = convert_location_too_coordinates(location) try: date_time = datetime.strptime(date_time) except Exception as e: raise ValueError("將 `date_time` 轉換成日期時間格式失敗,請確定提供格式為 '%m/%d/%y %H:%M:%S' 的字串。完整追蹤:"+ str(e)) temperature_celsius, risk_of_rain, wave_height = get_weather_report_at_coordinates((lon, lat), date_time) return f "Weather report for {location}, {date_time}:溫度將為 {temperature_celsius}°C,降雨風險為 {risk_of_rain*100:.0f}% ,波高為 {wave_height}m。"
一般而言,為了減輕您 LLM 的負擔,您可以問自己的好問題是:「如果我是啞巴,而且是第一次使用這個工具,那麼使用這個工具編程並自行糾正錯誤,對我來說有多容易?
給代理更多的論據 #
若要傳送一些額外的物件給您的代理,而不只是描述任務的簡單字串,您可以使用 additional_args
參數來傳遞任何類型的物件:
複製
from smolagents import CodeAgent, HfApiModel model_id = "meta-llama/Llama-3.3-70B-Instruct" agent = CodeAgent(tools=[], model=HfApiModel(model_id=model_id), add_base_tools=True) agent.run( "Why does Mike not know many people in New York?"、 additional_args={"mp3_sound_file_url":'https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3'} )
例如,您可以使用此 additional_args
參數,以傳送您希望代理利用的圖片或字串。
如何除錯您的代理程式 #
1.使用更強的 LLM #
在代理工作流程中,有些錯誤是實際錯誤,有些則是 LLM 引擎推理不當的過失。例如,考慮這個 CodeAgent
我要求製作一張汽車圖片:
複製
==================================================================================================== New task ==================================================================================================== 給我製作一張酷車圖片 ──────────────────────────────────────────────────────────────────────────────────────────────────── 新步驟 ──────────────────────────────────────────────────────────────────────────────────────────────────── Agent 正在執行下面的程式碼:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── image_generator(prompt="A cool, futuristic sports car with LED headlights, aerodynamic design, and vibrant color, high-res, photorealistic") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 程式碼片段的最後輸出:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png 步驟 1: - 所花時間16.35 秒 - 輸入符記:1,383 - 輸出符記:77 ──────────────────────────────────────────────────────────────────────────────────────────────────── 新步驟 ──────────────────────────────────────────────────────────────────────────────────────────────────── Agent 正在執行下列程式碼:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── final_answer("/var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 列印輸出: 程式碼片段的最後一次輸出:─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png 最終答案 /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png
使用者看到的不是回傳的圖片,而是回傳給他們的路徑。這可能看起來像是系統的錯誤,但事實上代理系統並沒有造成這個錯誤:只是 LLM 的大腦犯了一個錯誤,沒有將影像輸出儲存到變數中。因此,除了利用儲存圖片時記錄的路徑之外,它無法再次存取圖片,所以它會回傳路徑而非圖片。
因此,調試代理程式的第一步就是「使用功能更強大的 LLM」。替代方案如 Qwen2/5-72B-Instruct
就不會犯下這樣的錯誤。
2.提供更多指導 / 更多資訊 #
您也可以使用功能較弱的機型,只要您能更有效地引導它們。
設身處地為您的模型想一想:如果您是正在解決任務的模型,您是否會在可用的資訊(來自系統提示 + 任務制定 + 工具說明)中掙扎求存?
您是否需要一些補充說明?
為了提供額外的資訊,我們不建議立即變更系統提示:預設的系統提示有許多調整,除非您非常瞭解提示,否則您不希望搞砸。更好的方法是引導您的 LLM 引擎:
- 如果是關於要解決的任務:將所有這些細節加入任務中。任務可能長達 100 多頁。
- 如果是關於如何使用工具:您工具的描述屬性。
3.變更系統提示(一般不建議) #
如果上述說明不夠充分,您可以變更系統提示。
讓我們看看它是如何運作的。例如,讓我們檢查預設系統提示為 CodeAgent (以下版本跳過零射擊範例而簡短)。
複製
print(agent.system_prompt_template)
以下是您所獲得的內容:
複製
您是一位專家助理,可以使用代碼 Blob 解決任何任務。您將獲得一項任務,讓您盡力解決。 為此,您可以使用一系列的工具:這些工具基本上就是您可以用程式碼呼叫的 Python 函式。 為了解決任務,您必須規劃一系列步驟,以「思考:」、「編碼:」和「觀察:」的順序循環進行。 在每個步驟中,在「思考:」順序中,您應該先解釋您解決任務的理由以及您想要使用的工具。 然後,在「Code:」序列中,您應該使用簡單的 Python 程式碼。程式碼序列必須以''序列結束。 在每個中間步驟中,您可以使用「print()」來儲存隨後需要的任何重要資訊。 這些列印輸出隨後會出現在 'Observation:' 欄位中,可作為下一步的輸入。 最後,您必須使用 `final_answer` 工具傳回最終答案。 以下是一些使用概念工具的範例: --- {examples} 範例 以上範例使用的是你可能不存在的概念工具。在您建立的 Python 程式碼片段中執行計算之後,您只能使用這些工具: {{tool_descriptions}} 工具 {{managed_agents_descriptions}} 以下是您在解決任務時應該始終遵循的規則: 1.總是提供一個 'Thought:「 序列,以及一個以 」```「序列結束的 」Code:\n```py' 序列,否則您會失敗。 2.只使用您已定義的變數! 3.總是使用正確的工具參數。不要像 'answer = wiki({'query': "What is the place where James Bond lives?"})「那樣以 dict 的形式傳輸參數,而應該像 」answer = wiki(query="What is the place where James Bond lives?")'那樣直接使用參數。 4.注意不要在同一個程式碼區塊中串連太多連續的工具呼叫,尤其是當輸出格式無法預測時。例如,對 search 的呼叫有不可預測的回傳格式,因此不要在同一區塊中有另一個依賴於其輸出的工具呼叫:而應該使用 print() 輸出結果,以便在下一個區塊中使用它們。 5.僅在需要時才呼叫工具,切勿使用完全相同的參數重複之前的工具呼叫。 6.不要使用與工具相同的名稱來命名任何新變數:例如,不要命名變數為 'final_answer'。 7.永遠不要在我們的程式碼中建立任何名義上的變數,因為在您的記錄中出現這些變數可能會讓您偏離真正的變數。 8.您可以在程式碼中使用匯入,但只能從下列的模組清單中匯入:{{authorized_imports}} 9. 9.狀態會在程式執行之間持續存在:所以如果在某個步驟中您建立了變數或匯入了模組,這些都會持續存在。 10.不要放棄!你負責解決任務,而不是提供解決任務的方向。 現在開始!如果您正確地解決任務,您將獲得 $1,000,000 的獎勵。
如您所見,有一些占位符如 "{{tool_descriptions}}"
:這些會在代理程式初始化時使用,以插入某些自動產生的工具或受管代理程式描述。
因此,雖然您可以將自訂提示作為參數傳給 系統提示
參數,您的新系統提示必須包含下列占位符:
"{{tool_descriptions}}"
來插入工具說明。"{{managed_agents_description}}"
插入管理代理的描述(如果有)。- 適用於
CodeAgent
只是:"{{authorized_imports}}"
插入授權進口清單。
然後您可以變更系統提示,如下所示:
複製
從 smolagents.prompts 匯入 CODE_SYSTEM_PROMPT modified_system_prompt = CODE_SYSTEM_PROMPT + "\nHere you go!" # 在此變更系統提示 agent = CodeAgent( tools=[]、 model=HfApiModel()、 system_prompt=modified_system_prompt )
這也適用於 工具呼叫代理.
4.額外規劃 #
我們提供了一個輔助規劃步驟的模型,代理可以在正常的行動步驟之間定期執行這個步驟。在這個步驟中,沒有工具呼叫,只是要求 LLM 更新它所知道的事實清單,並思考根據這些事實它下一步應該採取什麼步驟。
複製
from smolagents import load_tool, CodeAgent, HfApiModel, DuckDuckGoSearchTool 從 dotenv 匯入 load_dotenv load_dotenv() # 從 Hub 匯入工具 image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True) search_tool = DuckDuckGoSearchTool() agent = CodeAgent( tools=[search_tool]、 model=HfApiModel("Qwen/Qwen2.5-72B-Instruct")、 planning_interval=3 # 這是您啟動規劃的地方! ) # 執行! result = agent.run( 「一隻獵豹以全速跑完亞歷山大三世橋長度需要多少時間?」、 )