CAMEL-Task2-Agent的構成組件
本文筆記主要關于2.7章節:CAMEL框架中agents如何使用工具。
一、工具說明
為什么要額外使用工具?
- agents本身的知識和能力有限,比如一些問題需要聯網搜索才能準確回答(而不是亂答,即“幻覺”),一些計算需要數學工具才能符合精度要求等。
- agents本身不能和世界交互,需要依賴接口。比如:agents可以基于LLM的語言理解和生成能力對問題給予回應,但無法訪問外部數據或執行具體的操作。
此時,工具的存在相當于為agents配備與外界交互的接口。工具的本質是一個具有名稱、描述、輸入參數和輸出的函數,當agents“認為”自己需要使用工具時,只要agents能夠正確地生成調用工具名稱、參數,對應的工具就會被調用,工具的輸出會成為agents繼續回答的依據。
CAMEL提供了各種常用工具(包),也支持自定義工具。
二、工具自定義
以數學計算為例子,展示如何通過自定義工具,讓agents的回答具備需要的精度。此處需要agents原生具備調用工具的能力(即支持function call)
無使用工具
from camel.agents import ChatAgent
from camel.models import ModelFactory
from camel.types import ModelPlatformTypeimport os
from dotenv import load_dotenv
import mathload_dotenv()# 定義系統消息
sys_msg = "你是一個數學大師,擅長各種數學問題。"# 初始化agent
api_key = os.getenv('QWEN_API_KEY')model = ModelFactory.create(model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,model_type="Qwen/Qwen2.5-72B-Instruct",url='https://api-inference.modelscope.cn/v1/',api_key=api_key
)# 創建agent并添加工具
agent = ChatAgent(system_message=sys_msg,model=model,output_language='中文',tools=[]
)# 定義用戶消息
usr_msg = "2的平方根是多少?"# 發送消息給agent
response = agent.step(usr_msg)
print(response.msgs[0].content)
此時得到的輸出:
2的平方根是 \(\sqrt{2}\),約等于 1.414。
雖然沒有錯誤,但精度不夠高。此時可以借助數學計算工具達到更高的精度。
使用(自定義)工具
此處以“計算一個數的平方根”為例進行工具定義。示例代碼(tool.py
)如下:
from camel.toolkits import FunctionTool
import mathdef calculate_sqrt(x: float) -> float:r"""計算一個數的平方根。Args:x (float): 需要計算平方根的數字。Returns:float: 輸入數字的平方根。"""return math.sqrt(x)# 用 FunctionTool 包裝該函數
sqrt_tool = FunctionTool(calculate_sqrt)print(sqrt_tool.get_function_name()) # 檢索函數的名稱
print(sqrt_tool.get_function_description()) # 獲取函數作用的描述
輸出:
calculate_sqrt
計算一個數的平方根。
接下來將工具sqrt_tool
引入,重新完成前述數學計算任務,代碼如下:
from camel.agents import ChatAgent
from camel.models import ModelFactory
from camel.types import ModelPlatformType
from tool import sqrt_toolimport os
from dotenv import load_dotenv
import mathload_dotenv()# 定義系統消息
sys_msg = "你是一個數學大師,擅長各種數學問題。"# 初始化agent
api_key = os.getenv('QWEN_API_KEY')model = ModelFactory.create(model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,model_type="Qwen/Qwen2.5-72B-Instruct",url='https://api-inference.modelscope.cn/v1/',api_key=api_key
)# 定義系統消息
sys_msg = "你是一個數學大師,擅長各種數學問題。當你遇到數學問題的時候,你要調用工具,將工具計算的結果作為答案"# 定義用戶消息
usr_msg = "2的平方根是多少?"# 創建agent并添加工具
tool_agent = ChatAgent(tools = [sqrt_tool], # 使用自定義的sqrt_toolsystem_message=sys_msg,model=model,output_language="中文")# 重新發送消息給toolagent
response = tool_agent.step(usr_msg)
print(response.msgs[0].content)
print(response.info['tool_calls']) # 檢查工具是否真的被調用
輸出:
2的平方根是1.4142135623730951。
[ToolCallingRecord(tool_name='calculate_sqrt', args={'x': 2}, result=1.4142135623730951, tool_call_id='call_7c18733f4d324bfb98944f', images=None)]
可以看到agents給出了更精確的結果,并且工具確實被調用。
三、使用CAMEL內置工具
此處以“查詢牛津大學校齡”為問題(假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。),考察agents在CAMEL框架下能否正確使用工具(SearchToolkit
,MathToolkit
)回答問題。
代碼如下:
from camel.toolkits import SearchToolkit,MathToolkit
from camel.types.agents import ToolCallingRecord
from camel.models import ModelFactory
from camel.types import ModelPlatformType
from camel.societies import RolePlaying
from camel.utils import print_text_animated
from colorama import Foreimport os
from dotenv import load_dotenvload_dotenv()
api_key = os.getenv('QWEN_API_KEY')#定義工具包
tools_list = [*SearchToolkit().get_tools(),*MathToolkit().get_tools()
]# 設置任務
task_prompt = ("假設現在是2024年,""牛津大學的成立年份,并計算出其當前年齡。""然后再將這個年齡加上10年。使用百度搜索工具。")# 創建模型
model = ModelFactory.create(model_platform=ModelPlatformType.OPENAI_COMPATIBLE_MODEL,model_type="Qwen/Qwen2.5-72B-Instruct",url='https://api-inference.modelscope.cn/v1/',api_key=api_key
)# 設置角色扮演
role_play_session = RolePlaying(assistant_role_name="搜索者",user_role_name="教授",assistant_agent_kwargs=dict(model=model,tools=tools_list, # 允許agent使用SearchToolkit,MathToolkit),user_agent_kwargs=dict(model=model,),task_prompt=task_prompt,with_task_specify=False,output_language='中文'
)# 設置聊天輪次限制
chat_turn_limit=10print(Fore.GREEN+ f"AI助手系統消息:\n{role_play_session.assistant_sys_msg}\n"
)
print(Fore.BLUE + f"AI用戶系統消息:\n{role_play_session.user_sys_msg}\n"
)print(Fore.YELLOW + f"原始任務提示:\n{task_prompt}\n")
print(Fore.CYAN+ "指定的任務提示:"+ f"\n{role_play_session.specified_task_prompt}\n"
)
print(Fore.RED + f"最終任務提示:\n{role_play_session.task_prompt}\n")n = 0
input_msg = role_play_session.init_chat()
while n < chat_turn_limit:n += 1assistant_response, user_response = role_play_session.step(input_msg)if assistant_response.terminated:print(Fore.GREEN+ ("AI助手終止。原因: "f"{assistant_response.info['termination_reasons']}."))breakif user_response.terminated:print(Fore.GREEN+ ("AI用戶終止。"f"原因: {user_response.info['termination_reasons']}."))break# 打印用戶的輸出print_text_animated(Fore.BLUE + f"AI用戶:\n\n{user_response.msg.content}\n")if "CAMEL_TASK_DONE" in user_response.msg.content:break# 打印助手的輸出,包括任何函數執行信息print_text_animated(Fore.GREEN + "AI助手:")tool_calls: list[ToolCallingRecord] = assistant_response.info['tool_calls']for func_record in tool_calls:print_text_animated(f"{func_record}")print_text_animated(f"{assistant_response.msg.content}\n")input_msg = assistant_response.msg
先安裝
beautifulsoup4
這個python包,否則后續調用工具時,會因為缺少這個包而無法調用。
輸出記錄
AI助手系統消息
BaseMessage(role_name='搜索者', role_type=<RoleType.ASSISTANT: 'assistant'>, meta_dict={'task': '假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。', 'assistant_role': '搜索者', 'user_role': '教授'}, content='===== RULES OF ASSISTANT =====\nNever forget you are a 搜索者 and I am a 教授. Never flip roles! Never instruct me!\nWe share a common interest in collaborating to successfully complete a task.\nYou must help me to complete the task.\nHere is the task: 假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。. Never forget our task!\nI must instruct you based on your expertise and my needs to complete the task.\n\nI must give you one instruction at a time.\nYou must write a specific solution that appropriately solves the requested instruction and explain your solutions.\nYou must decline my instruction honestly if you cannot perform the instruction due to physical, moral, legal reasons or your capability and explain the reasons.\nUnless I say the task is completed, you should always start with:\n\nSolution: <YOUR_SOLUTION>\n\n<YOUR_SOLUTION> should be very specific, include detailed explanations and provide preferable detailed implementations and examples and lists for task-solving.\nAlways end <YOUR_SOLUTION> with: Next request.\nRegardless of the input language, you must output text in 中文.', video_bytes=None, image_list=None, image_detail='auto', video_detail='auto', parsed=None)
AI用戶系統消息
BaseMessage(role_name='教授', role_type=<RoleType.USER: 'user'>, meta_dict={'task': '假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。', 'assistant_role': '搜索者', 'user_role': '教授'}, content='===== RULES OF USER =====\nNever forget you are a 教授 and I am a 搜索者. Never flip roles! You will always instruct me.\nWe share a common interest in collaborating to successfully complete a task.\nI must help you to complete the task.\nHere is the task: 假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。. Never forget our task!\nYou must instruct me based on my expertise and your needs to solve the task ONLY in the following two ways:\n\n1. Instruct with a necessary input:\nInstruction: <YOUR_INSTRUCTION>\nInput: <YOUR_INPUT>\n\n2. Instruct without any input:\nInstruction: <YOUR_INSTRUCTION>\nInput: None\n\nThe "Instruction" describes a task or question. The paired "Input" provides further context or information for the requested "Instruction".\n\nYou must give me one instruction at a time.\nI must write a response that appropriately solves the requested instruction.\nI must decline your instruction honestly if I cannot perform the instruction due to physical, moral, legal reasons or my capability and explain the reasons.\nYou should instruct me not ask me questions.\nNow you must start to instruct me using the two ways described above.\nDo not add anything else other than your instruction and the optional corresponding input!\nKeep giving me instructions and necessary inputs until you think the task is completed.\nWhen the task is completed, you must only reply with a single word <CAMEL_TASK_DONE>.\nNever say <CAMEL_TASK_DONE> unless my responses have solved your task.\nRegardless of the input language, you must output text in 中文.', video_bytes=None, image_list=None, image_detail='auto', video_detail='auto', parsed=None)
任務提示
原始任務提示:
假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。指定的任務提示:
None最終任務提示:
假設現在是2024年,牛津大學的成立年份,并計算出其當前年齡。然后再將這個年齡加上10年。使用百度搜索工具。Warning: No results found. Check if Baidu HTML structure has changed.
AI用戶
Instruction: 使用百度搜索工具查找牛津大學的成立年份。
Input: None
AI助手
Tool Execution: search_baiduArgs: {'query': '牛津大學 成立年份', 'number_of_result_pages': 1}Result: {'results': []}
Tool Execution: subArgs: {'a': 2024, 'b': 1167}Result: 857
看起來直接從百度搜索結果中沒有獲取到具體的信息,但我可以根據已知的資料來回答您的問題。牛津大學的成立年份普遍認為是在1096年或者1167年,但確切的日期并不確定,因為大學是一個逐漸形成的過程而不是一個具體的事件。如果我們假設牛津大學的成立年份為1167年,那么到2024年,牛津大學的年齡將是857年。接下來,我將這個年齡再加上10年。857 + 10 = 867年。因此,如果現在是2024年,那么牛津大學在2024年的年齡是857年,而再過10年后,它的年齡將會是867年。如果您需要進一步確認牛津大學的確切成立年份,請告訴我,我可以嘗試通過其他途徑查詢。 Next request.
AI用戶
Instruction: 確認牛津大學的成立年份是否為1167年,并計算其在2024年的年齡以及再過10年的年齡。
Input: 1167年
AI助手
Tool Execution: search_baiduArgs: {'query': '牛津大學 確切成立年份', 'number_of_result_pages': 1}Result: {'results': [{'result_id': 1, 'title': '...世界中最古老的大學。A哈佛大學B劍橋大學B劍橋大學C牛津大學D...', 'description': '', 'url': 'http://www.baidu.com/link?url=R4hnTfV9RxjVwHauc21YLg_AUDUwVSCLVJ2A6a7rLBeHkg2sIHQXi7D6NUMtvyLfliWl5mPj2NPg_HMkLVtjZdeWIw1jNGDYnh9L2YrL6nWt3HnADJ4UGP8oZsFTRsqWmKBPYaaYC5NumQRQYwRCmK'}]}
根據百度搜索的結果,雖然沒有直接明確指出牛津大學的具體成立年份,但根據歷史記載和學術界的共識,牛津大學的成立年份通常被認為是1167年。因此,按照1167年作為牛津大學的成立年份,我們可以計算出:
- 到2024年,牛津大學的年齡為857年(2024 - 1167 = 857)。
- 再過10年,即2034年,牛津大學的年齡將是867年(857 + 10 = 867)。所以,牛津大學在2024年的年齡是857年,而在2034年的年齡將是867年。 Next request.
AI用戶
Instruction: 確認上述計算結果是否正確,并完成任務。
Input: 857年,867年
AI助手
根據之前的計算和信息確認,牛津大學的成立年份為1167年。因此:- 2024年時,牛津大學的年齡為 \(2024 - 1167 = 857\) 年。
- 再過10年,即2034年時,牛津大學的年齡為 \(857 + 10 = 867\) 年。這些計算結果是正確的。至此,我們已經完成了任務,確認了牛津大學在2024年的年齡為857年,以及再過10年后其年齡為867年。如果您有其他任何問題或需要進一步的幫助,請隨時告知。任務已完成。 Next request.
AI用戶
CAMEL_TASK_DONE
觀察
此案例中同樣使用RolePlaying。由于使用的LLM基座不是很厲害,所以AI用戶和AI助手的對話其實會隨著每次運行的不同而改變,這里選取了一次確實調用了工具查證后再回答問題的對話記錄進行分析。
是的,有時候agents會假裝自己用了工具,但實際上工具函數的輸入參數是自己編的。
在AI用戶和AI助手的對話過程中,第一回合里面AI助手其實工具調用沒有返回有用的信息,但自己先假設了一個結果,再第二回合里再去驗證。驗證的時候,工具返回了有用的信息(可以通過response.info['tool_calls']
查看工具調用的具體信息),這使得后續對話內容更具有真實性。