iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
AI & Data

30 天從 0 至 1 建立一個自已的 AI 學習工具人系列 第 13

30-13: [知識] 讓我們 AI 工具人呼叫工具之 Functional Calling

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250927/200893582roFJ3aDHu.png

我們現在所建立的 AI 工具,基本上就只是呼叫 LLM 模型,然後請他回答問題,他事實上就大約是 2 ~ 3 年前 chat-gpt 剛出來的那樣,但是還有加上一些 Workflow。

接下來你會發現有很多事情它都不知道,例如最常見到的例子就是問天氣之類的,如果你問他台北天氣現在是什麼,他那時應該是會說不知道。

而這個就是我們今天有講的主題 :

> Function calling

🚀 Function Calling 的流程

大約在 2023 年時 OpenAI 提出了 Function Calling,最初的目的就是為了 :

讓 LLM 可以與外部介接,並且可以拿到外部的資料。

下圖是以我們要取得現在時間來當做它的範例,來說明他的流程。順到說一下這個功能真的有用,你有時後直接問 AI,他都會回奇怪的時間給你,像如果你用 gpt-4o-mini 每一次都給你很奇怪的時間,但 gpt-5 後就看起來都是正確的,反正這個只是範例。

然後還有幾個重點 :

  1. LLM 不會幫你執行 Function,而是只和你說用那個 Function
  2. 有沒有 Function Calling 的功能要看 LLM 有沒有提供

https://ithelp.ithome.com.tw/upload/images/20250927/20089358OzJEx2oCup.png

🤔 奇怪 ? 為什麼我用 LangChain 和 OpenAI SDK 這些可以直接回時間給我 ?

因為你用的是 Agent 或是 SDK 有自已實作,所以如果你只是單純的如下程式碼這樣 :

import OpenAI from "openai";

const client = new OpenAI();

const getWeather = async (city: string): Promise<string> => {
  return `The weather in ${city} is sunny!`;
};

const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "Get the weather for a given city. ex. Q:what is the weather in taipei? A:The weather in taipei is sunny!",
      parameters: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "The city to get weather for"
          }
        },
        required: ["city"]
      }
    }
  }
];

const response = await client.chat.completions.create({
  model: "gpt-5-nano",
  messages: [
    { role: "user", content: "what is the weather in taipei" }
  ],
  tools: tools,
  tool_choice: "auto"
});

console.log("Initial response:", response.choices[0].message.tool_calls);

然後我們的 console log 的結果就會如下,他就只會和你說要呼叫那個工具 :

Initial response: [
  {
    id: 'call_TSt3nZvE8lCmJ6tCoLaGhc82',
    type: 'function',
    function: { name: 'get_weather', arguments: '{"city":"taipei"}' }
  }
]

所以這裡幾點重點在於 :

  1. 使用 Function calling 的情況下,大部份是回傳說要用那個工具。
  2. 會不會自動幫你呼叫工具要看 SDK 與 Agent 有沒有幫你處理。

像如果你在 LangChain 用 createAgent 產生,那他會自動幫你呼叫,而用 Gemini SDK Python 版本的它也會幫你自動呼叫,例如下面的連結。

https://ai.google.dev/gemini-api/docs/function-calling?hl=zh-tw&example=meeting#javascript_2

🚀 完整的 Function Calling 範例 ( 用 LangChain 的 Agent 當範例 )

以下為 LangChain 的程式碼範例,由於 LangChain 的 createAgent 本身產出的 React Agent,所以他會自動的呼叫 tool 來執行。

https://docs.langchain.com/oss/javascript/langchain/agents

import { createAgent, tool } from "langchain";

const getCurrentTime = tool(
  async () => {
    return new Date().toISOString();
  },
  {
    name: "get_current_time",
    description: "Get the current time.",
  }
);

const agent = createAgent({
  model: "openai:gpt-5-nano",
  tools: [getCurrentTime],
});

const result = await agent.invoke({
    messages: [{ role: "user", content: "今天幾號" }],
});

console.log(result);

執行的結果,可以看出他整個流程是 :

  1. 我們問說,今天幾號呢。
  2. AI 回說要用 getCurrentTime 這個 function。
  3. Agent ( 在我們的程式碼中 ) 執行 getCurrentTime,然後將結果送給 LLM。
  4. 接下來 LLM 在根據 tool 的結果,來回傳結果,如下它就有回答今天的時間了。
{
  messages: [
    HumanMessage {
      "id": "9b7518d0-f8e1-451a-ad66-669cd481e567",
      "content": "今天幾號",
      "additional_kwargs": {},
      "response_metadata": {}
    },
    AIMessage {
      "id": "chatcmpl-CKJWCOelT9iEGqtTK8U30IpH1ZFjC",
      "content": "",
      "name": "model",
      "additional_kwargs": {
        "tool_calls": [
          {
            "id": "call_ry3Rc1pvI9XidWFgKm6Pfilh",
            "type": "function",
            "function": "[Object]"
          }
        ]
      },
      "response_metadata": {
        "tokenUsage": {
          "promptTokens": 128,
          "completionTokens": 600,
          "totalTokens": 728
        },
        "finish_reason": "tool_calls",
        "model_provider": "openai",
        "model_name": "gpt-5-nano-2025-08-07"
      },
      "tool_calls": [
        {
          "name": "get_current_time",
          "args": {
            "input": ""
          },
          "type": "tool_call",
          "id": "call_ry3Rc1pvI9XidWFgKm6Pfilh"
        }
      ],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "output_tokens": 600,
        "input_tokens": 128,
        "total_tokens": 728,
        "input_token_details": {
          "audio": 0,
          "cache_read": 0
        },
        "output_token_details": {
          "audio": 0,
          "reasoning": 576
        }
      }
    },
    ToolMessage {
      "id": "23c36162-247f-4dec-9863-75e6881ab0ea",
      "content": "2025-09-27T07:20:26.774Z",
      "name": "get_current_time",
      "additional_kwargs": {},
      "response_metadata": {},
      "tool_call_id": "call_ry3Rc1pvI9XidWFgKm6Pfilh"
    },
    AIMessage {
      "id": "chatcmpl-CKJWH4F0ZighUiqP0CyFpiOtj4dtp",
      "content": "今天是 2025 年 9 月 27 日。若以你所在的時區,時間可能會有差異。需要我幫你換算成你的時區嗎?",
      "name": "model",
      "additional_kwargs": {},
      "response_metadata": {
        "tokenUsage": {
          "promptTokens": 174,
          "completionTokens": 498,
          "totalTokens": 672
        },
        "finish_reason": "stop",
        "model_provider": "openai",
        "model_name": "gpt-5-nano-2025-08-07"
      },
      "tool_calls": [],
      "invalid_tool_calls": [],
      "usage_metadata": {
        "output_tokens": 498,
        "input_tokens": 174,
        "total_tokens": 672,
        "input_token_details": {
          "audio": 0,
          "cache_read": 0
        },
        "output_token_details": {
          "audio": 0,
          "reasoning": 448
        }
      }
    }
  ]
}

🚀 Best Practice For Function

下面這些 Best Practice 是我自已融合 OpenAI 與 Google Gemini 產生的,可以參考看看,但不一定要服用。

  1. 撰寫清楚詳盡的函式名稱、參數描述與指令,這個應該就不用多少,其中可以搭配 Prompt Engineering 那學到的東西。
  2. 套用軟體工程的最佳實務,這個我還蠻愛的,有提到最小驚訝原則 PLA,簡單的說就是你的 function 的命名與介面儘量直覺點,不要讓維護者看到你寫的 function 在想這個要衝啥小。
  3. 不要讓 LLM 想太多,這個包含了工具選取不要太多,還有就是能用程式處理的,就別丟給模型。
  4. Temperature 設為 0,但在 OpenAI 沒寫,不確定影響大不大,但他本質就是為了追求決策穩定度 ( 當一個冷酷無情的決策者吧 )。
  5. 風險大的驗證 Function 要等人類 Approve 後,才執行。
  6. 資安的處理 : 要有基本的權限驗證,不要將所有資料都送出去了。
  7. Token 限制 : 他這裡主要是說函式描述與指令如果達到 Token 的限制,就將它拆成小函數,當然也有不要讓他說太多廢話省 token 的用意。

🚀 其它進階的討論議題

🤔 不要讓同一個 Agent 塞那麼多 tool,那要如何處理呢 ?

事實上這題我們之前就有解過,只是他不是為了解 tool 這題,我們用的手法就是 :

每個 Agent 都有各自的職責,然後前面有一個 Route Agent

如下圖,我們是不是可以將這些 tool 放在各自職責的 Agent 中呢 ? 對吧,可以省 Token、又可以讓 LLM 要使用更精確。

https://ithelp.ithome.com.tw/upload/images/20250926/20089358eoXyJUpqvS.png

🤔 Function 內可以用 Cache 來優化效率與成本

都說是 function 了,實際上就我和我們常寫的 function 是相同的,所以當然如果你這個 function 是有處理一些很耗效能的東西,並且情況允許的話,當然可以使用。

有關 Cache 的一些問題可以看很久以前的這幾篇文章。

🤔 Function 的一些特殊設計重點

  • 除了 Get 外的方法,要有寡等性喔,這個應該很基本,就是不要,LLM 呼叫 2 次,最終狀態就有問題,例如使用者重複結帳相同商品的情況啊。
  • 要有 retry喔,如果是非 LLM Function Calling 我覺得就看用戶好不好 retry 與團隊政策,但在 LLM Function Calling 情境下,因為從 LLM 開始重試要花 token 所以在這種情況下,建議統一都有 retry 機制。

🚀 小總結

這篇文章我們學習到了以下幾個重點 :

  • Function Calling 基本的流程,要注意預設的情況下,它只會和你說要用那個工具。
  • Best Practice for Function。
  • Function 設計時需要考慮一些東西,例如寡等性、Retry、Cache。

它的概念事實上蠻簡單的,只是要注意一些小事情,接下來我們用它來為我們的 AI 工具人增加一些新功能。

🚀 參考資料


上一篇
30-12: [實作-5] 讓我們的 AI 工具人可以準確記住回答過的學生背景
下一篇
30-14: [實作-6] 透過 Function Calling 來實現取得昨日學習狀況
系列文
30 天從 0 至 1 建立一個自已的 AI 學習工具人14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言