一文徹底搞懂智能體Agent基于Function Calling的工具調用

前言
AI智能體是指具備一定自主性、能感知環境并通過智能決策執行特定任務的軟件或硬件實體。它結合了人工智能技術(如機器學習、自然語言處理、計算機視覺等),能夠獨立或協作完成目標。基于大語言模型(LLM)的Function Calling可以令智能體實現有效的工具使用和與外部API的交互。
并非所有的LLM模型都支持Function Calling。支持Function Calling的模型(如gpt-4,qwen-plus等)能夠檢測何時需要調用函數,并輸出調用函數的函數名和所需參數的JSON格式結構化數據。
Function Calling提高了輸出穩定性,并簡化了提示工程的復雜程度。對于不支持Function Calling的模型,可通過ReACT的相對較為復雜的提示詞工程,要求模型返回特定格式的響應,以便區分不同的階段(思考、行動、觀察)。
Function Calling主要有兩個用途:
- 獲取數據:例如根據關鍵字從知識庫檢索內容、通過特定API接口獲取業務數據
- 執行行動:例如通過API接口修改業務狀態數據、執行預定業務操作
本文包含如下內容:
- 詳細介紹Function Calling工具調用流程和涉及的交互消息
- 手搓Agent代碼實現Function Calling工具調用
Function Calling工具調用流程和交互消息
我們以查詢北京和廣州天氣為例,LLM采用通義千問qwen-plus。查詢天氣的流程如下圖:

1. 發起查詢請求
向LLM發起查詢時,messages列表只有一條消息(role為user, content為用戶查詢內容)。另外,還需要帶上tools定義。
tools定義包含如下內容:
- name: 函數名
- description: 函數描述
- parameters: 參數定義
本例中,定義了函數get_weather(location)。
我們用curl發起POST請求,body的JSON結構可參考https://platform.openai.com/docs/api-reference/chat/create
#!/bin/bash
export OPENAI_API_BASE="https://dashscope.aliyuncs.com/compatible-mode/v1"
export OPENAI_API_KEY="sk-xxx"# 替換為你的key
curl ${OPENAI_API_BASE}/chat/completions \
-H"Content-Type: application/json" \
-H"Authorization: Bearer $OPENAI_API_KEY" \
-d'{
"model": "qwen-plus",
"messages": [
{
"role": "user",
"content": "北京和廣州天氣怎么樣"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "location"
}
},
"required": ["location"]
}
}
}
],
"tool_choice": "auto"
}'2. LLM返回tool_calls獲取北京天氣
LLM經過推理,發現需要調用函數獲取北京天氣,回復的消息帶上tool_calls信息。
本例中,需要調用函數get_weather,參數名為location, 參數值為北京。
完整的JSON響應如下:
{ "choices": [ { "message": { "content": "", "role": "assistant", "tool_calls": [ { "index": 0, "id": "call_3ee91e7e0e0b420d811165", "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\": \"北京\"}" } } ] }, "finish_reason": "tool_calls", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 166, "completion_tokens": 17, "total_tokens": 183, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1745131660, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-7c4fc4c8-92fa-90cc-aaf6-f673d7ab4220"}3. 處理函數調用獲取北京天氣
解析處理LLM的tool_calls獲得函數名和參數列表,調用相應的API接口獲得結果。
例如:通過http://weather.cma.cn/api/now/54511可獲得北京的天氣情況。
完整的JSON響應如下:
{ "msg": "success", "code": 0, "data": { "location": { "id": "54511", "name": "北京", "path": "中國, 北京, 北京" }, "now": { "precipitation": 0.0, "temperature": 24.3, "pressure": 1007.0, "humidity": 35.0, "windDirection": "西南風", "windDirectionDegree": 207.0, "windSpeed": 2.7, "windScale": "微風" }, "alarm": [], "lastUpdate": "2025/04/20 14:25" }}4. 把上下文信息以及函數調用結果發給LLM
發給LLM的messages列表有3條messages:
- 第1條role為
user,是用戶的輸入 - 第2條role為
assistant,是LLM的tool_calls響應get_weather('北京') - 第3條role為
tool,是工具調用get_weather('北京')的結果
#!/bin/bash
export OPENAI_API_BASE="https://dashscope.aliyuncs.com/compatible-mode/v1"
export OPENAI_API_KEY="sk-xxx"# 替換為你的key
curl ${OPENAI_API_BASE}/chat/completions \
-H"Content-Type: application/json" \
-H"Authorization: Bearer $OPENAI_API_KEY" \
-d'{
"model": "qwen-plus",
"messages": [
{
"role": "user",
"content": "北京和廣州天氣怎么樣"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_3ee91e7e0e0b420d811165",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"北京\"}"
}
}
]
},
{
"role": "tool",
"content": "{\"msg\":\"success\",\"code\":0,\"data\":{\"location\":{\"id\":\"54511\",\"name\":\"北京\",\"path\":\"中國, 北京, 北京\"},\"now\":{\"precipitation\":0.0,\"temperature\":24.3,\"pressure\":1007.0,\"humidity\":35.0,\"windDirection\":\"西南風\",\"windDirectionDegree\":207.0,\"windSpeed\":2.7,\"windScale\":\"微風\"},\"alarm\":[],\"lastUpdate\":\"2025/04/20 14:25\"}}",
"tool_call_id": "call_3ee91e7e0e0b420d811165"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "location"
}
},
"required": [
"location"
]
}
}
}
],
"tool_choice": "auto"
}'5. LLM返回tool_calls獲取廣州天氣
LLM經過推理,發現需要調用函數獲取廣州天氣,回復的消息帶上tool_calls信息。
本例中,需要調用函數get_weather,參數名為location, 參數值為廣州。
完整的JSON響應如下:
{ "choices": [ { "message": { "content": "", "role": "assistant", "tool_calls": [ { "index": 0, "id": "call_4a920a1bb9d54f8894c1ac", "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\": \"廣州\"}" } } ] }, "finish_reason": "tool_calls", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 312, "completion_tokens": 19, "total_tokens": 331, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1745132731, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-5e002b5b-7220-927e-9637-554355f80658"}6. 處理函數調用獲取廣州天氣
解析處理LLM的tool_calls獲得函數名和參數列表,調用相應的API接口獲得結果。
例如:通過http://weather.cma.cn/api/now/59287可獲得廣州的天氣情況。
完整的JSON響應如下:
{ "msg": "success", "code": 0, "data": { "location": { "id": "59287", "name": "廣州", "path": "中國, 廣東, 廣州" }, "now": { "precipitation": 0.0, "temperature": 30.1, "pressure": 1002.0, "humidity": 64.0, "windDirection": "東南風", "windDirectionDegree": 167.0, "windSpeed": 2.4, "windScale": "微風" }, "alarm": [], "lastUpdate": "2025/04/20 14:25" }}7. 把上下文信息以及函數調用結果發給LLM
發給LLM的messages列表有5條messages:
- 第1條role為
user,是用戶的輸入 - 第2條role為
assistant,是LLM的tool_calls響應get_weather('北京') - 第3條role為
tool,是工具調用get_weather('北京')的結果 - 第4條role為
assistant,是LLM的tool_calls響應get_weather('廣州') - 第5條role為
tool,是工具調用get_weather('廣州')的結果
#!/bin/bash
export OPENAI_API_BASE="https://dashscope.aliyuncs.com/compatible-mode/v1"
export OPENAI_API_KEY="sk-xxx"# 替換為你的key
curl ${OPENAI_API_BASE}/chat/completions \
-H"Content-Type: application/json" \
-H"Authorization: Bearer $OPENAI_API_KEY" \
-d'{
"model": "qwen-plus",
"messages": [
{
"role": "user",
"content": "北京和廣州天氣怎么樣"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_3ee91e7e0e0b420d811165",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"北京\"}"
}
}
]
},
{
"role": "tool",
"content": "{\"msg\":\"success\",\"code\":0,\"data\":{\"location\":{\"id\":\"54511\",\"name\":\"北京\",\"path\":\"中國, 北京, 北京\"},\"now\":{\"precipitation\":0.0,\"temperature\":24.3,\"pressure\":1007.0,\"humidity\":35.0,\"windDirection\":\"西南風\",\"windDirectionDegree\":207.0,\"windSpeed\":2.7,\"windScale\":\"微風\"},\"alarm\":[],\"lastUpdate\":\"2025/04/20 14:25\"}}",
"tool_call_id": "call_3ee91e7e0e0b420d811165"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_4a920a1bb9d54f8894c1ac",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"廣州\"}"
}
}
]
},
{
"role": "tool",
"content": "{\"msg\":\"success\",\"code\":0,\"data\":{\"location\":{\"id\":\"59287\",\"name\":\"廣州\",\"path\":\"中國, 廣東, 廣州\"},\"now\":{\"precipitation\":0.0,\"temperature\":30.1,\"pressure\":1002.0,\"humidity\":64.0,\"windDirection\":\"東南風\",\"windDirectionDegree\":167.0,\"windSpeed\":2.4,\"windScale\":\"微風\"},\"alarm\":[],\"lastUpdate\":\"2025/04/20 14:25\"}}",
"tool_call_id": "call_4a920a1bb9d54f8894c1ac"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "location"
}
},
"required": [
"location"
]
}
}
}
],
"tool_choice": "auto"
}'8. LLM生成最終回復
LLM生成最終的回復:
北京的當前天氣狀況如下:
- 溫度:24.3℃
- 濕度:35%
- 風向:西南風
- 風速:微風
廣州的當前天氣狀況如下:
- 溫度:30.1℃
- 濕度:64%
- 風向:東南風
- 風速:微風
以上信息均來自最近更新,希望對你有幫助!完整的JSON響應如下:
{ "choices": [ { "message": { "content": "北京的當前天氣狀況如下:\n- 溫度:24.3℃\n- 濕度:35%\n- 風向:西南風\n- 風速:微風\n\n廣州的當前天氣狀況如下:\n- 溫度:30.1℃\n- 濕度:64%\n- 風向:東南風\n- 風速:微風 \n\n以上信息均來自最近更新,希望對你有幫助!", "role": "assistant" }, "finish_reason": "stop", "index": 0, "logprobs": null } ], "object": "chat.completion", "usage": { "prompt_tokens": 460, "completion_tokens": 105, "total_tokens": 565, "prompt_tokens_details": { "cached_tokens": 0 } }, "created": 1745133460, "system_fingerprint": null, "model": "qwen-plus", "id": "chatcmpl-fd1edc89-3ddb-9e27-9029-d2be2c81f3c1"}手搓Agent代碼實現Function Calling工具調用
1. 創建python環境
uv init agent
cd agent
uv venv
.venv\Scripts\activate
uv add openai requests python-dotenv2. 設置API Key
創建.env,.env內容如下(注意修改OPENAI_API_KEY為您的key)
OPENAI_API_KEY=your_api_key_here
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1把.env添加到.gitignore
3. 實現Agent代碼
基于openai sdk實現agent的主體代碼邏輯是:在允許的迭代次數范圍內,循環處理,發起chat completions直至沒有tool_calls, 迭代結束,輸出結果。
偽代碼:
maxIter = 5 # 最大迭代次數for iterSeq in range(1, maxIter+1): 構造chat completion請求(帶tools列表和tool_choice) 迭代次數達到最大值,tool_choice設置為none(不再調用工具) 否則tool_choice設置為auto(根據需要調用工具) 獲取chat completion結果 如果chat completion結果帶有tool_calls 解析并調用相應函數 添加消息到消息列表,繼續迭代 否則,表明無需調用工具,迭代結束,輸出結果完整的main.py代碼如下:
import jsonimport osimport requestsimport urllib.parsefrom typing import Iterablefrom openai import OpenAIfrom openai.types.chat.chat_completion_message_param import ChatCompletionMessageParamfrom openai.types.chat.chat_completion_message_tool_call import ( ChatCompletionMessageToolCall,)from openai.types.chat.chat_completion_user_message_param import ( ChatCompletionUserMessageParam,)from openai.types.chat.chat_completion_tool_message_param import ( ChatCompletionToolMessageParam,)from openai.types.chat.chat_completion_assistant_message_param import ( ChatCompletionAssistantMessageParam,)
# 加載環境變量from dotenv import load_dotenvload_dotenv()
api_key = os.getenv("OPENAI_API_KEY")base_url = os.getenv("OPENAI_API_BASE")model = "qwen-plus"client = OpenAI(api_key=api_key, base_url=base_url)
# 工具定義tools = [ { "type": "function", "function": { "name": "get_weather", "description": "Get weather", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "location"} }, "required": ["location"], }, }, }]
# 實現獲取天氣def get_weather(location: str) -> str: url = "http://weather.cma.cn/api/autocomplete?q=" + urllib.parse.quote(location) response = requests.get(url) data = response.json() if data["code"] != 0: return "沒找到該位置的信息" location_code = "" for item in data["data"]: str_array = item.split("|") if ( str_array[1] == location or str_array[1] + "市" == location or str_array[2] == location ): location_code = str_array[0] break if location_code == "": return "沒找到該位置的信息" url = f"http://weather.cma.cn/api/now/{location_code}" return requests.get(url).text
# 實現工具調用def invoke_tool( tool_call: ChatCompletionMessageToolCall,) -> ChatCompletionToolMessageParam: result = ChatCompletionToolMessageParam(role="tool", tool_call_id=tool_call.id) if tool_call.function.name == "get_weather": args = json.loads(tool_call.function.arguments) result["content"] = get_weather(args["location"]) else: result["content"] = "函數未定義" return result
def main(): query = "北京和廣州天氣怎么樣" messages: Iterable[ChatCompletionMessageParam] = list() messages.append(ChatCompletionUserMessageParam(role="user", cnotallow=query)) maxIter = 5 # 最大迭代次數 for iterSeq in range(1, maxIter+1): print(f">> iterSeq:{iterSeq}") print(f">>> messages: {messages}") # 當迭代次數達到最大值,不再調用工具 toolChoice = "auto" if iterSeq < maxIter else "none" # 向LLM發起請求 chat_completion = client.chat.completions.create( messages=messages, model=model, tools=tools, tool_choice=toolChoice ) tool_calls = chat_completion.choices[0].message.tool_calls content = chat_completion.choices[0].message.content if isinstance(tool_calls, list): # LLM的響應信息有tool_calls信息 messages.append( ChatCompletionAssistantMessageParam( role="assistant", tool_calls=tool_calls, cnotallow="" ) ) for tool_call in tool_calls: print(f">>> tool_call: {tool_call}") result = invoke_tool(tool_call) print(f">>> tool_call result: {result}") messages.append(result) else: # LLM的響應信息沒有tool_calls信息,迭代結束,獲取響應文本 print(f">>> final result: \n{content}") returnmain()運行代碼:uv run .\main.py
輸出日志如下:
>> iterSeq:1
>>> messages: [{'role': 'user', 'content': '北京和廣州天氣怎么樣'}]
>>> tool_call: ChatCompletionMessageToolCall(id='call_db29421754a8447590d99d', functinotallow=Function(arguments='{"location": "北京"}', name='get_weather'), type='function', index=0)
>>> tool_call result: {'role': 'tool', 'tool_call_id': 'call_db29421754a8447590d99d', 'content': '{"msg":"success","code":0,"data":{"location":{"id":"54511","name":"北京","path":"中國, 北京, 北京"},"now":{"precipitation":0.0,"temperature":24.5,"pressure":1006.0,"humidity":34.0,"windDirection":"西南風","windDirectionDegree":191.0,"windSpeed":2.8,"windScale":"微風"},"alarm":[],"lastUpdate":"2025/04/20 15:35"}}'}
>> iterSeq:2
>>> messages: [{'role': 'user', 'content': '北京和廣州天氣怎么樣'}, {'role': 'assistant', 'tool_calls': [ChatCompletionMessageToolCall(id='call_db29421754a8447590d99d', functinotallow=Function(arguments='{"location": "北京"}', name='get_weather'), type='function', index=0)], 'content': ''}, {'role': 'tool', 'tool_call_id': 'call_db29421754a8447590d99d', 'content': '{"msg":"success","code":0,"data":{"location":{"id":"54511","name":"北京","path":"中國, 北京, 北京"},"now":{"precipitation":0.0,"temperature":24.5,"pressure":1006.0,"humidity":34.0,"windDirection":"西南風","windDirectionDegree":191.0,"windSpeed":2.8,"windScale":"微風"},"alarm":[],"lastUpdate":"2025/04/20 15:35"}}'}]
>>> tool_call: ChatCompletionMessageToolCall(id='call_ae1c03437392444c869cbf', functinotallow=Function(arguments='{"location": "廣州"}', name='get_weather'), type='function', index=0)
>>> tool_call result: {'role': 'tool', 'tool_call_id': 'call_ae1c03437392444c869cbf', 'content': '{"msg":"success","code":0,"data":{"location":{"id":"59287","name":"廣州","path":"中國, 廣東, 廣州"},"now":{"precipitation":0.0,"temperature":30.4,"pressure":1001.0,"humidity":64.0,"windDirection":"東南風","windDirectionDegree":165.0,"windSpeed":2.2,"windScale":"微風"},"alarm":[],"lastUpdate":"2025/04/20 15:35"}}'}
>> iterSeq:3
>>> messages: [{'role': 'user', 'content': '北京和廣州天氣怎么樣'}, {'role': 'assistant', 'tool_calls': [ChatCompletionMessageToolCall(id='call_db29421754a8447590d99d', functinotallow=Function(arguments='{"location": "北京"}', name='get_weather'), type='function', index=0)], 'content': ''}, {'role': 'tool', 'tool_call_id': 'call_db29421754a8447590d99d', 'content': '{"msg":"success","code":0,"data":{"location":{"id":"54511","name":"北京","path":"中國, 北京, 北京"},"now":{"precipitation":0.0,"temperature":24.5,"pressure":1006.0,"humidity":34.0,"windDirection":"西南風","windDirectionDegree":191.0,"windSpeed":2.8,"windScale":"微風"},"alarm":[],"lastUpdate":"2025/04/20 15:35"}}'}, {'role': 'assistant', 'tool_calls': [ChatCompletionMessageToolCall(id='call_ae1c03437392444c869cbf', functinotallow=Function(arguments='{"location": "廣州"}', name='get_weather'), type='function', index=0)], 'content': ''}, {'role': 'tool', 'tool_call_id': 'call_ae1c03437392444c869cbf', 'content': '{"msg":"success","code":0,"data":{"location":{"id":"59287","name":"廣州","path":"中國, 廣東, 廣州"},"now":{"precipitation":0.0,"temperature":30.4,"pressure":1001.0,"humidity":64.0,"windDirection":"東南風","windDirectionDegree":165.0,"windSpeed":2.2,"windScale":"微風"},"alarm":[],"lastUpdate":"2025/04/20 15:35"}}'}]
>>> final result:
北京的當前天氣狀況如下:
- 溫度:24.5°C
- 濕度:34%
- 風向:西南風
- 風速:微風 (2.8 m/s)
- 最后更新時間:2025/04/20 15:35
廣州的當前天氣狀況如下:
- 溫度:30.4°C
- 濕度:64%
- 風向:東南風
- 風速:微風 (2.2 m/s)
- 最后更新時間:2025/04/20 15:35





























