Appearance
工具调用让 Kimi 模型从"能说"变成"能做":开发者定义工具 JSON Schema,模型决定调用哪个工具并生成参数,开发者执行工具返回结果,模型基于结果给出最终回答。本文以联网搜索为例演示完整循环。
使用 Kimi API 完成工具调用(tool_calls)
工具调用的核心流程
1. 定义工具(JSON Schema 格式)
2. 传入 tools 参数,发送请求
3. 模型返回 finish_reason="tool_calls"
4. 解析 tool_calls,执行对应函数
5. 将执行结果以 role="tool" 传回
6. 模型根据结果给出最终回答与 OpenAI 对比:Kimi API 工具调用格式与 OpenAI 完全兼容,但不支持已废弃的
functions参数,且tool_choice不支持required。
定义工具
使用 JSON Schema 描述工具能力:
python
tools = [
{
"type": "function",
"function": {
"name": "search",
"description": "通过搜索引擎搜索互联网内容。当用户提问需要最新信息或你的知识无法回答时调用。",
"parameters": {
"type": "object",
"required": ["query"],
"properties": {
"query": {
"type": "string",
"description": "用户想要搜索的内容"
}
}
}
}
},
{
"type": "function",
"function": {
"name": "crawl",
"description": "获取指定 URL 网页的内容。",
"parameters": {
"type": "object",
"required": ["url"],
"properties": {
"url": {
"type": "string",
"description": "目标网页地址"
}
}
}
}
}
]完整 Agent 循环(Python)
python
import json
from openai import OpenAI
client = OpenAI(
api_key="MOONSHOT_API_KEY",
base_url="https://api.moonshot.cn/v1",
)
def search(query: str) -> str:
# 实际实现:调用搜索 API(如 Tavily、Brave Search)
return f"搜索结果:关于'{query}'的相关内容..."
def crawl(url: str) -> str:
# 实际实现:抓取网页内容
return f"网页内容:{url} 的正文..."
def run_agent(user_message: str) -> str:
messages = [
{"role": "system", "content": "你是一个能联网查询最新信息的助手。"},
{"role": "user", "content": user_message}
]
while True:
completion = client.chat.completions.create(
model="kimi-k2.6",
messages=messages,
tools=tools,
)
choice = completion.choices[0]
# 无工具调用,直接返回
if choice.finish_reason != "tool_calls":
return choice.message.content
# 将模型的工具调用消息加入历史
messages.append(choice.message)
# 执行所有工具调用
for tool_call in choice.message.tool_calls:
args = json.loads(tool_call.function.arguments)
if tool_call.function.name == "search":
result = search(**args)
elif tool_call.function.name == "crawl":
result = crawl(**args)
else:
result = "未知工具"
# 将工具结果传回
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
answer = run_agent("今天 AI 领域有什么重要新闻?")
print(answer)TypeScript 示例
typescript
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MOONSHOT_API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
async function runAgent(userMessage: string): Promise<string> {
const messages: OpenAI.ChatCompletionMessageParam[] = [
{ role: "system", content: "你是一个能联网查询最新信息的助手。" },
{ role: "user", content: userMessage },
];
while (true) {
const completion = await client.chat.completions.create({
model: "kimi-k2.6",
messages,
tools,
});
const choice = completion.choices[0];
if (choice.finish_reason !== "tool_calls") {
return choice.message.content ?? "";
}
messages.push(choice.message);
for (const toolCall of choice.message.tool_calls ?? []) {
const args = JSON.parse(toolCall.function.arguments);
let result: string;
if (toolCall.function.name === "search") {
result = `搜索结果:${args.query}...`; // 替换为真实搜索
} else {
result = "未知工具";
}
messages.push({
role: "tool",
tool_call_id: toolCall.id,
content: result,
});
}
}
}Thinking 模式下的注意事项
启用 thinking 模式(kimi-k2.6 默认开启)时:
tool_choice只能是"auto"或"none"- 每轮工具调用后,assistant 消息中的
reasoning_content必须原样保留并传入下一轮
常见问题
Q: 模型一定会调用工具吗?
A: 不一定,模型会根据上下文判断是否需要工具。如需强制调用,可在 system prompt 中明确指示,Kimi API 目前不支持 tool_choice="required"。
Q: 可以同时定义多少个工具?
A: 最多 128 个工具。
Q: 工具调用支持并行吗?
A: 支持,模型可以在一次响应中返回多个 tool_calls,每个有独立的 tool_call_id,需要逐个执行并分别回传结果。
Kimi API 的工具调用(tool_calls)让模型从"说话"变成"做事"。通过注册工具定义,模型可以决定何时调用哪个工具、传什么参数,开发者执行工具后将结果回传,模型再给出最终回复。本文覆盖完整循环流程。
使用 Kimi API 完成工具调用(tool_calls)
什么是 tool_calls
tool_calls 给予了 Kimi 模型执行具体动作的能力。模型能通过 tool_calls 帮你搜索互联网、查询数据库、甚至操作智能家居。
tool_calls由function_call进化而来,是其超集。由于 OpenAI 已将function_call标记为废弃,Kimi API 不再支持function_call,请统一使用tool_calls。
一次完整的工具调用流程:
- 用 JSON Schema 定义工具
- 通过
tools参数提交给模型 - 模型决定调用哪个工具(或不调用)
- 模型以 JSON 格式输出调用参数
- 开发者执行工具,获取结果
- 将结果回传给模型,模型给出最终回复
关键理解:模型本身不执行工具,它只输出"应该调用什么、传什么参数"。实际执行由开发者完成。
工具定义
以联网搜索为例,定义两个工具:search(搜索)和 crawl(抓取网页):
typescript
import OpenAI from "openai";
const tools: OpenAI.Chat.ChatCompletionTool[] = [
{
type: "function",
function: {
name: "search",
description: `通过搜索引擎搜索互联网上的内容。
当你的知识无法回答用户提出的问题,或用户请求联网搜索时,调用此工具。
搜索结果包含网站标题、地址(URL)和简介。`,
parameters: {
type: "object",
required: ["query"],
properties: {
query: {
type: "string",
description: "用户搜索的内容,从对话上下文中提取。",
},
},
},
},
},
{
type: "function",
function: {
name: "crawl",
description: "根据网站地址(URL)获取网页内容。",
parameters: {
type: "object",
required: ["url"],
properties: {
url: {
type: "string",
description: "需要获取内容的网站地址(URL)。",
},
},
},
},
},
];完整调用循环
typescript
const client = new OpenAI({
apiKey: process.env.MOONSHOT_API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
async function executeTool(name: string, args: Record<string, string>): Promise<string> {
if (name === "search") {
// 替换为真实的搜索实现
return JSON.stringify([{ title: "示例结果", url: "https://example.com", snippet: "..." }]);
}
if (name === "crawl") {
// 替换为真实的网页抓取实现
return `网页内容:${args.url} 的文字内容...`;
}
return "工具不存在";
}
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: "你是 Kimi,由 Moonshot AI 提供的人工智能助手。" },
{ role: "user", content: "请联网搜索 Context Caching,告诉我它是什么。" },
];
// 使用 while 循环处理多轮工具调用
while (true) {
const completion = await client.chat.completions.create({
model: "kimi-k2.6",
messages,
tools,
});
const choice = completion.choices[0];
messages.push(choice.message); // 必须把 assistant 消息加回去
if (choice.finish_reason === "stop") {
// 模型完成回复,退出循环
console.log(choice.message.content);
break;
}
if (choice.finish_reason === "tool_calls" && choice.message.tool_calls) {
// 执行模型请求的所有工具
for (const toolCall of choice.message.tool_calls) {
const args = JSON.parse(toolCall.function.arguments);
const result = await executeTool(toolCall.function.name, args);
messages.push({
role: "tool",
tool_call_id: toolCall.id, // 必须匹配对应的 tool_call id
content: result,
});
}
}
}messages 结构说明
工具调用的 messages 顺序必须正确:
system: 系统提示
user: 用户问题
assistant: tool_call(name=search, arguments={query: "..."}) ← 模型返回
tool: search_result(tool_call_id=...) ← 工具执行结果
assistant: tool_call(name=crawl, arguments={url: "..."}) ← 模型继续调用
tool: crawl_content(tool_call_id=...)
assistant: 最终回复(finish_reason=stop)常见错误:如果出现
tool_call_id not found错误,检查是否把完整的 assistant 消息(含tool_calls字段)加回了 messages。不能只加content,要加整个message对象。
流式输出下的工具调用
流式模式下,工具调用的判断方式有所不同:
typescript
const stream = await client.chat.completions.create({
model: "kimi-k2.6",
messages,
tools,
stream: true,
});
let toolCallChunks: Record<number, { id: string; name: string; arguments: string }> = {};
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta;
if (delta?.tool_calls) {
for (const tc of delta.tool_calls) {
const idx = tc.index ?? 0;
if (!toolCallChunks[idx]) {
toolCallChunks[idx] = { id: tc.id ?? "", name: tc.function?.name ?? "", arguments: "" };
}
toolCallChunks[idx].arguments += tc.function?.arguments ?? "";
}
}
}流式输出注意事项:
- 用
delta.tool_calls是否存在判断是否有工具调用(不要等finish_reason) delta.content先输出,delta.tool_calls后输出- 第一个数据块包含
tool_call.id和function.name,后续块只追加arguments - 多个并行工具调用时,用
index字段区分
token 消耗说明
tools 参数中的内容同样计入 token 消耗。工具定义越详细,每次请求的 token 越多。精简 description 可以降低成本。
常见问题
Q: 模型一定会调用工具吗?
A: 不一定。模型会根据上下文自主决定是否调用工具。如果你确定需要调用,可以在 prompt 中明确说明(如"请联网搜索"),或使用 tool_choice: "required" 强制调用。
Q: 可以同时注册多个工具吗?
A: 可以,tools 是数组,可以一次性提交多个工具定义。模型也可以在一次回复中选择多个工具并行调用。
Q: 工具调用的结果格式有要求吗?
A: role: "tool" 消息的 content 是字符串,通常传 JSON 字符串或纯文本。建议用结构化 JSON,方便模型解析。