隨著大語言模型(LLMs)的迅猛發展,“Function Calling”(函數調用)逐漸成為一個重要的能力,它使得模型不僅能聊天,還能像“中控大腦”一樣調用外部函數完成具體任務,比如查天氣、調用數據庫、控制機器人等。
本篇文章將以一個完整可運行的 LangChain 示例為背景,手把手教你如何:
- 定義外部工具函數(Tools)
- 將函數注冊到 LLM
- 構建帶有多輪消息歷史的對話流
- 自動識別并調用工具完成任務
- 再將結果交由模型處理,給出最終回復
一、什么是 Function Calling?
Function Calling 是指模型根據上下文,自動生成調用特定函數的指令(如 JSON 結構的函數名 + 參數),然后由程序調用真實的函數執行,最終再將結果交還給模型生成最終答復。
LangChain 對 Function Calling 的支持非常完善,封裝了 OpenAI、Qwen 等兼容 Function Calling 的模型接口,并提供工具注冊、調用與上下文維護機制。
二、項目結構預覽
我們實現的是一個簡單的對話助手,支持兩個工具:
get_weather(city: str)
:返回城市天氣add(a: int, b: int)
:返回兩個數的和
代碼結構如下:
├── main.py # 主程序,包含模型初始化、工具綁定、消息循環
三、代碼講解
下面逐步解析代碼中的關鍵部分。
1. 定義工具函數
使用 LangChain 提供的 @tool
裝飾器即可將普通函數注冊為 Tool:
from langchain_core.tools import tool@tool
def get_weather(city: str) -> str:"""返回指定城市的天氣信息"""return f"{city} 今天天氣晴,28°C,濕度30%"@tool
def add(a: int, b: int) -> int:"""加法函數,返回兩個整數的和"""return a + b
LangChain 會自動基于函數簽名和 docstring 為模型構建 JSON schema,模型即可調用它們。
2. 初始化模型
使用 Qwen 的 Function Calling 接口:
from langchain_openai import ChatOpenAIllm = ChatOpenAI(base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",api_key="your_secret_key",model="qwen-turbo",temperature=0
)
Qwen 模型支持 Function Calling,且支持阿里云 API 兼容模式。
3. 綁定工具到模型
通過 bind_tools
把工具綁定到模型上:
llm_with_tools = llm.bind_tools([get_weather, add])
綁定后,模型就具備了識別調用這兩個工具的能力。
4. 構建對話循環(含工具調用與多輪上下文)
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
import jsonmessages = [AIMessage(content="你好!我是你的AI助手,可以幫你查天氣、做加法,有什么可以幫你的?")
]
available_tools = {"get_weather": get_weather, "add": add}while True:user_input = input("你:").strip()if user_input.lower() in {"exit", "quit", ""}:print("再見!")breakmessages.append(HumanMessage(content=user_input))response = llm_with_tools.invoke(messages)if response.tool_calls:print("檢測到工具調用:")print(json.dumps(response.tool_calls, indent=2, ensure_ascii=False))messages.append(response)for tool_call in response.tool_calls:tool_func = available_tools.get(tool_call["name"])if tool_func:tool_result = tool_func.invoke(tool_call)messages.append(tool_result)print(f"執行 {tool_call['name']} → {tool_result.content}")else:messages.append(response)print("AI:", response.content)continuefinal_response = llm_with_tools.invoke(messages)messages.append(final_response)print("AI:", final_response.content)
🤖 工作流程解析:
- 用戶輸入問題,如“北京天氣如何?”
- 模型識別需要調用
get_weather(city=北京)
- 返回
tool_calls
,代碼中通過invoke()
實際執行函數 - 工具返回結果后,構造成
ToolMessage
添加進對話上下文 - 再次調用模型生成基于工具結果的自然語言回答
四、運行效果演示
你:北京天氣如何?
檢測到工具調用:
[{"name": "get_weather","args": {"city": "北京"},"id": "call_acfbc5447fa142899d4771","type": "tool_call"}
]
執行 get_weather → 北京 今天天氣晴,28°C,濕度30%
AI: 北京今天天氣晴朗,溫度是28°C,濕度為30%。
五、核心機制解析
1. 工具函數封裝為 Tool
對象
LangChain 使用 ToolMessage
封裝工具執行的返回值,供下一次模型使用。
ToolMessage(tool_call_id=..., content="返回結果")
2. invoke()
自動處理消息歷史
模型接收完整的消息序列(系統消息、Human消息、AI消息、Tool消息),并根據上下文判斷是否執行函數或直接回復。
六、總結
本文以一個簡潔易懂的例子,演示了如何使用 LangChain 實現 Function Calling:
- ? 支持多輪對話與上下文記憶
- ? 模型自動識別是否需要工具調用
- ? 工具結果可參與后續推理
- ? 可快速拓展更多工具,如數據庫查詢、圖像生成、郵件發送等
七、參考資料
- LangChain 官方文檔
- Qwen DashScope
- OpenAI Function Calling Guide
附完整項目代碼
import jsonfrom langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI# Step 1:定義工具函數
@tool
def get_weather(city: str) -> str:"""返回指定城市的天氣信息"""return f"{city} 今天天氣晴,28°C,濕度30%"@tool
def add(a: int, b: int) -> int:"""加法函數,返回兩個整數的和"""return a + b# Step 2:初始化模型
llm = ChatOpenAI(base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",api_key="your_secret_key",model="qwen-turbo",temperature=0
)# Step 3:綁定工具
llm_with_tools = llm.bind_tools([get_weather, add])# Step 4:初始化多輪消息歷史
messages = [AIMessage(content="你好!我是你的AI助手,可以幫你查天氣、做加法,有什么可以幫你的?")
]# Step 5:開始多輪對話循環
available_tools = {"get_weather": get_weather, "add": add}while True:user_input = input("你:").strip()if user_input.lower() in {"exit", "quit", ""}:print("再見!")break# 添加用戶輸入messages.append(HumanMessage(content=user_input))# 發送當前上下文給模型response = llm_with_tools.invoke(messages)# print(type(response))# print(response)# 判斷是否需要調用工具if response.tool_calls:print("檢測到工具調用:")# print(type(response.tool_calls))# print(response.tool_calls)print(json.dumps(response.tool_calls, indent=2, ensure_ascii=False))# 把 tool_call 消息添加到對話歷史messages.append(response)# 執行所有工具調用并添加結果for tool_call in response.tool_calls:tool_name = tool_call["name"]# print(tool_call["args"]["city"])# print(type(tool_call["args"]["city"]))tool_func = available_tools.get(tool_name)if tool_func:tool_result = tool_func.invoke(tool_call)messages.append(tool_result)print(f"執行 {tool_name} → {tool_result.content}")else:print(f"未找到工具:{tool_name}")else:# 如果沒有 tool_call,直接是 AI 回答messages.append(response)print("AI:", response.content)continue# 工具執行完后再次交給模型生成最終回答final_response = llm_with_tools.invoke(messages)messages.append(final_response)print("AI: ", final_response.content)