Tool & Function Calling

Tool calls (còn gọi là function calls) cho phép một LLM truy cập các tool bên ngoài. LLM không trực tiếp gọi tool, mà chỉ đề xuất tool cần gọi. Người dùng sẽ tự gọi tool bằng cách riêng rồi đưa kết quả trở lại cho LLM. Cuối cùng, LLM dựa vào kết quả đó để tạo ra câu trả lời cho câu hỏi ban đầu của người dùng.

Trạm AI chuẩn hóa giao diện tool calling trên mọi model và provider, giúp người dùng dễ dàng tích hợp tool bên ngoài với bất kỳ model nào được hỗ trợ.

Model được hỗ trợ: Người dùng có thể tìm model hỗ trợ tool calling bằng cách lọc tại tram.ai/models?supported_parameters=tools.

Ví dụ về Request Body

Tool calling với Trạm AI gồm ba bước chính. Dưới đây là định dạng request body cốt lõi cho từng bước:

Bước 1: Inference Request kèm Tools

expandable lines
1{
2 "model": "google/gemini-3-flash-preview",
3 "messages": [
4 {
5 "role": "user",
6 "content": "What are the titles of some James Joyce books?"
7 }
8 ],
9 "tools": [
10 {
11 "type": "function",
12 "function": {
13 "name": "search_gutenberg_books",
14 "description": "Search for books in the Project Gutenberg library",
15 "parameters": {
16 "type": "object",
17 "properties": {
18 "search_terms": {
19 "type": "array",
20 "items": {"type": "string"},
21 "description": "List of search terms to find books"
22 }
23 },
24 "required": ["search_terms"]
25 }
26 }
27 }
28 ]
29}

Bước 2: Tool Execution (phía Client)

Sau khi nhận được response của model kèm tool_calls, hãy thực thi tool được yêu cầu ngay tại máy cục bộ và chuẩn bị kết quả:

lines
1// Model responds with tool_calls, you execute the tool locally
2const toolResult = await searchGutenbergBooks(["James", "Joyce"]);

Bước 3: Inference Request kèm Kết quả Tool

expandable lines
1{
2 "model": "google/gemini-3-flash-preview",
3 "messages": [
4 {
5 "role": "user",
6 "content": "What are the titles of some James Joyce books?"
7 },
8 {
9 "role": "assistant",
10 "content": null,
11 "tool_calls": [
12 {
13 "id": "call_abc123",
14 "type": "function",
15 "function": {
16 "name": "search_gutenberg_books",
17 "arguments": "{\"search_terms\": [\"James\", \"Joyce\"]}"
18 }
19 }
20 ]
21 },
22 {
23 "role": "tool",
24 "tool_call_id": "call_abc123",
25 "content": "[{\"id\": 4300, \"title\": \"Ulysses\", \"authors\": [{\"name\": \"Joyce, James\"}]}]"
26 }
27 ],
28 "tools": [
29 {
30 "type": "function",
31 "function": {
32 "name": "search_gutenberg_books",
33 "description": "Search for books in the Project Gutenberg library",
34 "parameters": {
35 "type": "object",
36 "properties": {
37 "search_terms": {
38 "type": "array",
39 "items": {"type": "string"},
40 "description": "List of search terms to find books"
41 }
42 },
43 "required": ["search_terms"]
44 }
45 }
46 }
47 ]
48}

Lưu ý: Tham số tools phải có mặt trong mọi request (Bước 1 và Bước 3) để router xác thực được schema của tool ở từng lần gọi.

Ví dụ về Tool Calling

Dưới đây là đoạn code Python cho phép LLM gọi một API bên ngoài — ở đây là Project Gutenberg, để tìm kiếm sách.

Trước tiên, hãy thiết lập một vài phần cài đặt cơ bản:

1import OpenAI from 'openai';
2
3const TRAM_AI_API_KEY = "<TRAM_AI_API_KEY>";
4
5// You can use any model that supports tool calling
6const MODEL = "google/gemini-3-flash-preview";
7
8const openai = new OpenAI({
9 baseURL: 'https://api.staging.tram.ai.vn/v1',
10 apiKey: TRAM_AI_API_KEY,
11});
12
13const task = "What are the titles of some James Joyce books?";
14
15const messages = [
16 {
17 role: "system",
18 content: "You are a helpful assistant."
19 },
20 {
21 role: "user",
22 content: task,
23 }
24];

Định nghĩa Tool

Tiếp theo, chúng ta định nghĩa tool muốn gọi. Hãy nhớ rằng tool do LLM yêu cầu, nhưng đoạn code chúng ta viết ở đây mới là phần thực sự thực thi lệnh gọi và trả kết quả về cho LLM.

1async function searchGutenbergBooks(searchTerms: string[]): Promise<Book[]> {
2 const searchQuery = searchTerms.join(' ');
3 const url = 'https://gutendex.com/books';
4 const response = await fetch(`${url}?search=${searchQuery}`);
5 const data = await response.json();
6
7 return data.results.map((book: any) => ({
8 id: book.id,
9 title: book.title,
10 authors: book.authors,
11 }));
12}
13
14const tools = [
15 {
16 type: 'function',
17 function: {
18 name: 'searchGutenbergBooks',
19 description:
20 'Search for books in the Project Gutenberg library based on specified search terms',
21 parameters: {
22 type: 'object',
23 properties: {
24 search_terms: {
25 type: 'array',
26 items: {
27 type: 'string',
28 },
29 description:
30 "List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)",
31 },
32 },
33 required: ['search_terms'],
34 },
35 },
36 },
37];
38
39const TOOL_MAPPING = {
40 searchGutenbergBooks,
41};

Lưu ý rằng “tool” chỉ là một function bình thường. Tiếp đó chúng ta viết một “spec” JSON tương thích với tham số function calling của OpenAI, rồi truyền spec đó cho LLM để nó biết tool này khả dụng và biết cách dùng. Khi cần, LLM sẽ yêu cầu tool kèm theo các arguments. Cuối cùng, chúng ta xử lý lệnh gọi tool tại máy cục bộ, thực thi function, và trả kết quả về cho LLM.

Sử dụng tool và kết quả tool

Hãy thực hiện lệnh gọi Trạm AI API đầu tiên tới model:

1const result = await openai.chat.completions.create({
2 model: 'google/gemini-3-flash-preview',
3 tools,
4 messages,
5 stream: false,
6});
7
8const response_1 = result.choices[0].message;

LLM trả về finish reason là tool_calls, kèm một mảng tool_calls. Trong một trình xử lý response LLM tổng quát, hãy kiểm tra finish_reason trước khi xử lý tool call, nhưng ở đây, hãy cứ giả định đó đúng là trường hợp này. Hãy tiếp tục với việc xử lý tool call:

1// Append the response to the messages array so the LLM has the full context
2// It's easy to forget this step!
3messages.push(response_1);
4
5// Now we process the requested tool calls, and use our book lookup tool
6for (const toolCall of response_1.tool_calls) {
7 const toolName = toolCall.function.name;
8 const { search_params } = JSON.parse(toolCall.function.arguments);
9 const toolResponse = await TOOL_MAPPING[toolName](search_params);
10 messages.push({
11 role: 'tool',
12 toolCallId: toolCall.id,
13 name: toolName,
14 content: JSON.stringify(toolResponse),
15 });
16}

Mảng messages giờ đã có:

  1. Request ban đầu
  2. Response của LLM (chứa một yêu cầu gọi tool)
  3. Kết quả của tool call (một json trả về từ Project Gutenberg API)

Giờ chúng ta có thể thực hiện lệnh gọi Trạm AI API thứ hai, và hy vọng nhận được kết quả!

1const response_2 = await openai.chat.completions.create({
2 model: 'google/gemini-3-flash-preview',
3 messages,
4 tools,
5 stream: false,
6});
7
8console.log(response_2.choices[0].message.content);

Đầu ra sẽ trông giống như:

lines
Here are some books by James Joyce:
* *Ulysses*
* *Dubliners*
* *A Portrait of the Artist as a Young Man*
* *Chamber Music*
* *Exiles: A Play in Three Acts*

Hoàn tất! Chúng ta đã sử dụng thành công một tool trong prompt.

Interleaved Thinking

Interleaved thinking cho phép model lập luận xen giữa các tool call, nhờ đó đưa ra quyết định tinh tế hơn sau khi nhận được kết quả tool. Tính năng này giúp model nối nhiều tool call với các bước lập luận xen kẽ, và ra quyết định có chiều sâu hơn dựa trên kết quả trung gian.

Quan trọng: Interleaved thinking làm tăng mức sử dụng token và độ trễ của response. Hãy cân nhắc budget và yêu cầu hiệu năng trước khi bật tính năng này.

Cách Interleaved Thinking hoạt động

Với interleaved thinking, model có thể:

  • Lập luận về kết quả của một tool call trước khi quyết định bước tiếp theo
  • Nối nhiều tool call với các bước lập luận xen kẽ
  • Đưa ra quyết định có chiều sâu hơn dựa trên kết quả trung gian
  • Trình bày minh bạch quá trình lập luận khi lựa chọn tool

Ví dụ: Nghiên cứu nhiều bước kèm lập luận

Dưới đây là ví dụ minh họa cách một model dùng interleaved thinking để nghiên cứu một chủ đề trên nhiều nguồn:

Request ban đầu:

expandable lines
1{
2 "model": "anthropic/claude-sonnet-4.5",
3 "messages": [
4 {
5 "role": "user",
6 "content": "Research the environmental impact of electric vehicles and provide a comprehensive analysis."
7 }
8 ],
9 "tools": [
10 {
11 "type": "function",
12 "function": {
13 "name": "search_academic_papers",
14 "description": "Search for academic papers on a given topic",
15 "parameters": {
16 "type": "object",
17 "properties": {
18 "query": {"type": "string"},
19 "field": {"type": "string"}
20 },
21 "required": ["query"]
22 }
23 }
24 },
25 {
26 "type": "function",
27 "function": {
28 "name": "get_latest_statistics",
29 "description": "Get latest statistics on a topic",
30 "parameters": {
31 "type": "object",
32 "properties": {
33 "topic": {"type": "string"},
34 "year": {"type": "integer"}
35 },
36 "required": ["topic"]
37 }
38 }
39 }
40 ]
41}

Lập luận và Tool Calls của Model:

  1. Suy nghĩ ban đầu: “Tôi cần nghiên cứu tác động môi trường của xe điện. Hãy bắt đầu với các bài báo học thuật để có nghiên cứu đã được bình duyệt.”
  2. Tool Call đầu tiên: search_academic_papers({"query": "electric vehicle lifecycle environmental impact", "field": "environmental science"})
  3. Sau Kết quả Tool đầu tiên: “Các bài báo cho thấy kết quả trái chiều về tác động của khâu sản xuất. Tôi cần số liệu thống kê hiện tại để bổ sung cho nghiên cứu học thuật này.”
  4. Tool Call thứ hai: get_latest_statistics({"topic": "electric vehicle carbon footprint", "year": 2024})
  5. Sau Kết quả Tool thứ hai: “Giờ tôi đã có cả nghiên cứu học thuật lẫn dữ liệu hiện tại. Hãy tìm các nghiên cứu chuyên về khâu sản xuất để giải quyết những khoảng trống mà tôi đã phát hiện.”
  6. Tool Call thứ ba: search_academic_papers({"query": "electric vehicle battery manufacturing environmental cost", "field": "materials science"})
  7. Phân tích cuối cùng: Tổng hợp toàn bộ thông tin đã thu thập thành một response toàn diện.

Các thực hành tốt nhất cho Interleaved Thinking

  • Mô tả Tool rõ ràng: Cung cấp mô tả chi tiết để model lập luận được về thời điểm nên dùng từng tool
  • Tham số có cấu trúc: Dùng schema tham số được định nghĩa rõ ràng để model thực hiện tool call chính xác
  • Bảo toàn ngữ cảnh: Duy trì ngữ cảnh hội thoại qua nhiều lần tương tác với tool
  • Xử lý lỗi: Thiết kế tool sao cho trả về thông báo lỗi có ý nghĩa, giúp model điều chỉnh cách tiếp cận

Những điều cần cân nhắc khi triển khai

Khi triển khai interleaved thinking:

  • Model có thể mất nhiều thời gian phản hồi hơn do các bước lập luận bổ sung
  • Mức sử dụng token sẽ cao hơn vì quá trình lập luận
  • Chất lượng lập luận phụ thuộc vào năng lực của model
  • Một số model phù hợp với cách tiếp cận này hơn các model khác

Một vòng lặp Agentic đơn giản

Trong ví dụ trên, các lệnh gọi được thực hiện một cách tường minh và tuần tự. Để xử lý nhiều loại đầu vào khác nhau của người dùng cùng các tool call khác nhau, người dùng có thể dùng một vòng lặp agentic.

Dưới đây là ví dụ về một vòng lặp agentic đơn giản (dùng cùng toolsmessages ban đầu như ở trên):

1async function callLLM(messages: Message[]): Promise<ChatResponse> {
2 const result = await openai.chat.completions.create({
3 model: 'google/gemini-3-flash-preview',
4 tools,
5 messages,
6 stream: false,
7 });
8
9 messages.push(result.choices[0].message);
10 return result;
11}
12
13async function getToolResponse(response: ChatResponse): Promise<Message> {
14 const toolCall = response.choices[0].message.toolCalls[0];
15 const toolName = toolCall.function.name;
16 const toolArgs = JSON.parse(toolCall.function.arguments);
17
18 // Look up the correct tool locally, and call it with the provided arguments
19 // Other tools can be added without changing the agentic loop
20 const toolResult = await TOOL_MAPPING[toolName](toolArgs);
21
22 return {
23 role: 'tool',
24 toolCallId: toolCall.id,
25 content: toolResult,
26 };
27}
28
29const maxIterations = 10;
30let iterationCount = 0;
31
32while (iterationCount < maxIterations) {
33 iterationCount++;
34 const response = await callLLM(messages);
35
36 if (response.choices[0].message.toolCalls) {
37 messages.push(await getToolResponse(response));
38 } else {
39 break;
40 }
41}
42
43if (iterationCount >= maxIterations) {
44 console.warn("Warning: Maximum iterations reached");
45}
46
47console.log(messages[messages.length - 1].content);

Các thực hành tốt nhất và mẫu thiết kế nâng cao

Hướng dẫn định nghĩa Function

Khi định nghĩa tool cho LLM, hãy tuân theo những thực hành tốt nhất sau:

Tên rõ ràng và mang tính mô tả: Dùng tên function mang tính mô tả, thể hiện rõ mục đích của tool.

lines
1// Good: Clear and specific
2{ "name": "get_weather_forecast" }
lines
1// Avoid: Too vague
2{ "name": "weather" }

Mô tả đầy đủ: Cung cấp mô tả chi tiết giúp model hiểu khi nào và dùng tool như thế nào.

lines
1{
2 "description": "Get current weather conditions and 5-day forecast for a specific location. Supports cities, zip codes, and coordinates.",
3 "parameters": {
4 "type": "object",
5 "properties": {
6 "location": {
7 "type": "string",
8 "description": "City name, zip code, or coordinates (lat,lng). Examples: 'New York', '10001', '40.7128,-74.0060'"
9 },
10 "units": {
11 "type": "string",
12 "enum": ["celsius", "fahrenheit"],
13 "description": "Temperature unit preference",
14 "default": "celsius"
15 }
16 },
17 "required": ["location"]
18 }
19}

Streaming với Tool Calls

Khi dùng response streaming kèm tool call, hãy xử lý phù hợp với từng content type khác nhau:

expandable lines
1const stream = await fetch('https://api.staging.tram.ai.vn/v1/chat/completions', {
2 method: 'POST',
3 headers: {
4 Authorization: `Bearer ${TRAM_AI_API_KEY}`,
5 'Content-Type': 'application/json',
6 },
7 body: JSON.stringify({
8 model: 'anthropic/claude-sonnet-4.5',
9 messages: messages,
10 tools: tools,
11 stream: true
12 })
13});
14
15const reader = stream.body.getReader();
16let toolCalls = [];
17
18while (true) {
19 const { done, value } = await reader.read();
20 if (done) {
21 break;
22 }
23
24 const chunk = new TextDecoder().decode(value);
25 const lines = chunk.split('\n').filter(line => line.trim());
26
27 for (const line of lines) {
28 if (line.startsWith('data: ')) {
29 const data = JSON.parse(line.slice(6));
30
31 if (data.choices[0].delta?.tool_calls) {
32 toolCalls.push(...data.choices[0].delta.tool_calls);
33 }
34
35 if (data.choices[0].finish_reason === 'tool_calls') {
36 await handleToolCalls(toolCalls);
37 } else if (data.choices[0].finish_reason === 'stop') {
38 // Regular completion without tool calls
39 break;
40 }
41 }
42 }
43}

Cấu hình Tool Choice

Kiểm soát việc dùng tool bằng tham số tool_choice:

lines
1// Let model decide (default)
2{ "tool_choice": "auto" }
lines
1// Disable tool usage
2{ "tool_choice": "none" }
lines
1// Force specific tool
2{
3 "tool_choice": {
4 "type": "function",
5 "function": {"name": "search_database"}
6 }
7}

Parallel Tool Calls

Kiểm soát việc nhiều tool có được gọi đồng thời hay không bằng tham số parallel_tool_calls (mặc định là true với hầu hết model):

lines
1// Disable parallel tool calls - tools will be called sequentially
2{ "parallel_tool_calls": false }

Khi parallel_tool_callsfalse, mỗi lần model chỉ yêu cầu một tool call, thay vì có thể thực hiện nhiều lệnh gọi song song.

Quy trình làm việc đa Tool

Thiết kế các tool phối hợp tốt với nhau:

expandable lines
1{
2 "tools": [
3 {
4 "type": "function",
5 "function": {
6 "name": "search_products",
7 "description": "Search for products in the catalog"
8 }
9 },
10 {
11 "type": "function",
12 "function": {
13 "name": "get_product_details",
14 "description": "Get detailed information about a specific product"
15 }
16 },
17 {
18 "type": "function",
19 "function": {
20 "name": "check_inventory",
21 "description": "Check current inventory levels for a product"
22 }
23 }
24 ]
25}

Cách này cho phép model nối các thao tác một cách tự nhiên: tìm kiếm → lấy chi tiết → kiểm tra tồn kho.

Theo dõi độ tin cậy

Trạm AI theo dõi độ tin cậy của mỗi provider khi hoàn thành tool call và thể hiện thông tin này dưới dạng Tool Call Error Rate trên tab Performance của mọi trang model. Cùng tín hiệu đó cũng quyết định thứ tự provider của Auto Exacto đối với các request tool calling. Để tìm hiểu về validator chính xác, bản nháp JSON Schema, ngữ nghĩa regex, và cách phân loại theo từng tool call, hãy xem How Tool-Calling Success Rate Is Measured.

Để biết thêm chi tiết về định dạng message và tham số tool của Trạm AI, hãy xem API Reference.