經過前幾篇內容,我們已經學會如何利用 LangChain 串接模型、設計提示模板、控制輸出格式,甚至能透過 LCEL 將多個步驟組合成流程。不過,僅依靠 LLM 本身依然存在限制,例如它無法直接存取即時的網路資料、查詢資料庫,或執行程式邏輯。
為了突破這些限制,我們可以在 LangChain 中設計 工具(Tool),並透過 Tool Calling 讓模型能夠動態決定何時使用這些工具。這樣一來,模型就不只是單純的文字生成器,而能真正與外部世界互動,這也是邁向 AI Agent 的關鍵一步。
在 LangChain 裡,Tool 就是一個封裝好的功能單元,本質上可以想成是一個帶有說明文件的函式。它負責把外部世界的功能抽象成模型可以理解並調用的形式,讓 LLM 不只是文字生成器,而是具備行動能力的智慧助理。
一個 Tool 通常包含兩個核心元素:
以下是一個最簡單的 Tool 定義範例:
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
const echoTool = tool(
async ({ text }) => {
// 工具邏輯:回傳輸入內容
return `你輸入了:${text}`;
},
{
name: 'echo_tool', // 工具名稱(模型會用這個來呼叫)
description: '回傳輸入的文字內容', // 工具描述(幫助模型判斷何時使用)
schema: z.object({
text: z.string().describe('要回傳的文字內容'),
}),
}
);
在實際運作中,模型會先閱讀這些描述資訊,並在對話過程中依需求判斷是否需要使用某個 Tool。如果需要,它就會輸出一個「工具呼叫請求」(tool call),其中包含要使用的工具名稱與對應參數。應用程式接手後會執行對應邏輯,最後再把結果回傳給模型,讓模型整理後以自然語言回覆給使用者。
Tool Calling 指的是語言模型在回答問題時,能夠「自主判斷」是否需要呼叫外部工具(Tool),並自動填入所需的輸入參數,以完成特定任務。這讓 LLM 不再只是單純的回答器,而是具備實際執行力的智慧體。
在 OpenAI API 中,這個功能被稱為 Function Calling;而在 LangChain 中,則進一步做了抽象與封裝,提供統一的介面與多模型支援。換句話說,開發者不必再去處理不同 LLM 的細節差異,就能輕鬆整合多個工具,並快速擴充成更完整的 AI 系統。
要讓 Tool Calling 運作,通常需要經過以下幾個步驟:
tool
函式或裝飾器建立一個 Tool,將功能邏輯與輸入參數結構(schema)封裝起來。透過這套流程,Tool Calling 成為語言模型連接外部世界的關鍵橋樑,也是建構 AI Agent 時不可或缺的基礎設計。
LangChain 提供一套標準化的 Tool Calling 操作流程,讓語言模型可以透過自然語言指令,自主決定是否呼叫外部功能模組。整體流程分為四個步驟:工具建立 → 模型綁定 → 模型呼叫 → 工具執行。
設計一個 Tool 的推薦做法是使用 tool()
函式,這可以協助你將任意邏輯函式,轉換為符合 LangChain Tool 規範的結構。
以下是一個簡單的 加法工具 範例:
import { z } from 'zod';
import { tool } from '@langchain/core/tools';
export const add = tool(
({ a, b }: { a: number; b: number }): number => {
return a + b;
},
{
name: 'add',
description: '將兩個數字相加,回傳它們的和。',
schema: z.object({
a: z.number().describe('第一個加數'),
b: z.number().describe('第二個加數'),
}),
}
);
在這段程式碼中,我們完成了幾件事:
name
與 description
是模型判斷該工具是否適用的依據。zod
定義輸入 schema,LangChain 會自動轉換成 JSON Schema,讓模型知道必須填入哪些參數以及型別限制。有了這些資訊,模型才能在需要進行數字相加時,正確選擇並呼叫這個 add
工具。
工具設計完成後,下一步就是讓模型「知道它可以使用哪些工具」。這個動作可以透過 bindTools()
方法來完成,它會回傳一個新的模型實例,並附加上工具使用的能力。
以下示範將我們剛才定義的 add
工具綁定到模型:
import { ChatOpenAI } from '@langchain/openai';
import { add } from './tools/add';
const llm = new ChatOpenAI({
model: 'gpt-4o-mini',
});
const llmWithTools = llm.bindTools([add]);
在這段程式碼中:
llm
可以是 OpenAI 的 ChatOpenAI
,或任何支援 Tool Calling 的模型。bindTools([add])
會把我們定義好的 add
工具掛載到模型,讓它在需要時能主動選擇呼叫。這段程式碼中,model
可以是 OpenAI 的 ChatOpenAI
實例,或任何支援 Tool Calling 的模型。經過綁定後,模型就能自動選擇使用這些工具。
注意:並非所有模型都支援 Tool Calling。以 OpenAI 為例,必須使用
gpt-3.5-turbo-1106
或更新的版本,才能正確啟用並執行這項功能。
工具綁定後,模型的使用方式與一般情境相同,你依然可以透過 .invoke()
傳入自然語言輸入。不過,當輸入內容與某個工具的用途 語意高度相關 時,模型就會主動產生 Tool 呼叫,並依據工具的描述與參數結構,自動填入正確的參數。
模型是否會觸發 Tool Calling,取決於輸入是否符合工具的用途。例如:
add
這類計算工具,模型就可能選擇使用它。const result = await llmWithTools.invoke('哈囉,你好嗎?');
console.log(result.content);
// => '你好!很高興見到你。'
這類單純對話不涉及數字計算,因此模型會直接回覆文字,不會呼叫工具。
const result = await llmWithTools.invoke('請幫我計算 2 加 3 等於多少?');
這類輸入與 add
工具的描述高度吻合,因此模型會決定呼叫工具,並自動填入參數。回傳內容中會包含 tool_calls
欄位,清楚顯示它選擇了哪個工具與對應參數:
console.log(result.tool_calls);
/*
[
{
name: 'add',
args: { a: 2, b: 3 },
type: 'tool_call',
id: 'call_xxxxxxxxxxxxxxxxxxxxxxxx'
}
]
*/
工具是否會被呼叫,完全取決於輸入語意是否與工具的描述與參數格式「對得上」。因此,建議在設計工具時,務必讓名稱簡潔、用途描述明確,才能提升模型正確選擇並使用工具的成功率。
在 LangChain 中,每個 Tool 本質上都是一個符合 Runnable
介面的物件,因此可以直接用 .invoke()
來執行,就像呼叫一般函式一樣。
以下範例示範如何手動執行我們定義好的加法工具:
const toolResult = await add.invoke({ a: 2, b: 3 });
console.log(toolResult);
// => 5
在實際應用中,這個步驟通常會發生在模型產生了 tool_calls
之後。tool_calls
會告訴我們「模型打算呼叫哪個工具」以及「需要填入的參數」。工具的執行必須由應用程式來完成。流程大致如下:
tool_calls
中解析工具名稱與參數。add
)。.invoke()
執行工具並取得結果。換言之,工具執行代表模型「決定要用工具」之後,應用程式負責實際完成任務的階段。這樣的設計讓模型不需要真的會做運算或查資料,而是透過工具取得正確資訊,再以自然語言整合成回應提供給使用者。
在實際應用中,我們常常希望 AI 不只具備單一能力,而是能同時使用多個工具,並根據使用者的提問,自主判斷該呼叫哪些功能模組。
以下示範一個多工具的範例,讓 OpenAI 模型能在一次對話中同時調用 加法 與 乘法 兩個工具,並整合計算結果後生成最終回應:
import { z } from 'zod';
import { tool } from '@langchain/core/tools';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
const addTool = tool(
({ a, b }: { a: number; b: number }): number => {
return a + b;
},
{
name: 'add',
schema: z.object({ a: z.number(), b: z.number() }),
description: '計算兩個數字的加法',
},
);
const multiplyTool = tool(
({ a, b }: { a: number; b: number }): number => {
return a * b;
},
{
name: 'multiply',
schema: z.object({ a: z.number(), b: z.number() }),
description: '計算兩個數字的乘法',
},
);
const llmWithTools = new ChatOpenAI({
model: 'gpt-4o-mini',
temperature: 0,
}).bindTools([addTool, multiplyTool]);
const messages = [new HumanMessage('請幫我計算 3 乘以 12,還有 11 加 49。')];
const aiMessage = await llmWithTools.invoke(messages);
messages.push(aiMessage);
const toolsByName = { add: addTool, multiply: multiplyTool };
for (const toolCall of aiMessage.tool_calls) {
const selectedTool = toolsByName[toolCall.name];
const toolMessage = await selectedTool.invoke(toolCall);
messages.push(toolMessage);
}
const response = await llmWithTools.invoke(messages);
console.log(response.content);
以下說明程式執行時的流程:
multiply
與 add
兩個工具,並在回應中產生 tool_calls
。tool_calls
,依序執行對應工具,得到 36
與 60
,並將結果包裝成訊息加入對話上下文。3 乘以 12 的結果是 36,11 加 49 的結果是 60。
透過這樣的流程,LLM 不再只是單純的對話引擎,而是能自主判斷、選擇並呼叫外部功能,讓整個系統具備真正的執行力與擴充性。
要讓語言模型能穩定、準確地使用工具,設計方式至關重要。以下整理出幾項 LangChain 官方建議的最佳實踐,能有效提升模型的理解力與工具執行的正確率:
gpt-3.5-turbo-1106
之後的版本,這些模型已針對工具描述與 JSON Schema 理解進行優化,能更精準判斷何時需要調用工具,以及如何填寫正確的參數。multiply
比 calculate
更具體,而「查天氣」比「查資訊」更清楚。描述文字應直白說明工具的功能與限制,最好包含參數意圖與適用場景。operation
參數的通用計算器。這能避免模型混淆,也更容易自動產生正確參數格式。這些原則雖然直觀,但若能在設計初期就落實,將大幅提升 Tool Calling 的穩定性與可維護性,並讓整體系統更容易隨需求成長與演進。
今天我們學會了如何在 LangChain 中使用 Tool Calling,讓模型能夠主動呼叫外部功能,突破單純文字生成的限制:
tool()
封裝邏輯、定義清楚的名稱與用途、用 zod
建立輸入 schema。tool_calls
並填入正確參數。tool_calls
,執行工具並回傳結果,最後再由模型生成自然語言回覆。透過 Tool Calling,LLM 不再只是回答器,而能真正與外部世界互動,這正是邁向 AI Agent 的關鍵一步。