azosi · 2026.7.22 04:47 · 조회 0
Kimi Tool Calls
상위 문서: ← Kimi Model Capabilities 출처: Kimi API Platform 공식 문서 — Use Kimi API for Tool Calls 한글화 (2026-07-22) 공급사: Moonshot AI (月之暗面)
Tool calls (tool_calls) 는 Kimi LLM이 단순 "말하기"를 넘어 "행동"으로 나아갈 수 있게 해줍니다. 모델이 대화 맥락에 따라 도구를 호출할지 결정하고, JSON 형식의 호출 인자를 생성하면, 애플리케이션이 도구를 실행해 그 결과를 다시 모델에 전달해 최종 답변을 생성합니다. tool_calls로 Kimi는 인터넷 검색, 데이터베이스 조회, 스마트 홈 기기 제어 등을 도와줄 수 있습니다. 이 문서는 정의·등록·실행 흐름을 인터넷 검색 예제와 함께 설명하고, 스트리밍 등 다른 시나리오에 대한 노트도 다룹니다.
1. 도구 호출의 전체 흐름
- JSON Schema로 도구 정의
- 정의한 도구를
tools파라미터로 모델에 전달 (여러 개 동시 제출 가능) - Kimi가 대화 맥락에 따라 사용할 도구(들)를 선택하거나, 아예 사용하지 않을 수 있음
- Kimi가 도구 호출에 필요한 파라미터와 정보를 JSON으로 출력
- 출력된 파라미터로 도구 실행 후 결과를 다시 모델에 전달
- Kimi가 도구 실행 결과를 바탕으로 사용자에게 답변
💡 대규모 도구 카탈로그: 도구 수십~수백 개라면 모두 한 번에 제출하지 말고, Dynamic Tool Loading으로 필요할 때 동적 주입 — 토큰 사용량을 줄이고 도구 선택 정확도를 높입니다.
2. 예제: 모델에 인터넷 검색 능력 부여
Kimi의 지식은 학습 데이터에 기반하므로, 시간에 민감한 질문은 기존 지식으로 답할 수 없습니다. 아래 예제는 "검색 엔진"과 "웹 브라우저" 두 도구를 정의해, 모델이 웹에서 최신 정보를 검색해 답변하게 합니다.
2-1. JSON Schema로 도구 정의
tools = [
{
"type": "function",
"function": {
"name": "search",
"description": """
Search for content on the internet using a search engine.
When your knowledge cannot answer the user's question, or when the user requests an online search, call this tool. Extract the content the user wants to search for from the conversation and use it as the value of the query parameter.
The search results include the website title, address (URL), and description.
""",
"parameters": {
"type": "object",
"required": ["query"],
"properties": {
"query": {
"type": "string",
"description": """
The content the user wants to search for, extracted from the user's question or conversation context.
"""
}
}
}
}
},
{
"type": "function",
"function": {
"name": "crawl",
"description": """
Retrieve web page content based on the website address (URL).
""",
"parameters": {
"type": "object",
"required": ["url"],
"properties": {
"url": {
"type": "string",
"description": """
The website address (URL) from which to retrieve content, usually obtained from search results.
"""
}
}
}
}
}
]
도구 정의 시 고정 포맷:
{
"type": "function",
"function": {
"name": "NAME",
"description": "DESCRIPTION",
"parameters": {
"type": "object",
"properties": {
}
}
}
}
name,description,parameters.properties는 도구 제공자가 정의description은 도구의 역할과 사용 시나리오를 설명 — 모델이 어떤 함수를 고를지 결정하는 데 도움parameters는 호출에 필요한 파라미터의 타입과 설명을 명시- 모델은 JSON Schema를 만족하는 JSON Object를 도구 호출 인자로 생성
2-2. 모델에 도구 등록
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["MOONSHOT_API_KEY"],
base_url="https://api.moonshot.ai/v1",
)
completion = client.chat.completions.create(
model="kimi-k3",
messages=[
{"role": "system", "content": "You are Kimi, an AI assistant provided by Moonshot AI. You are proficient in Chinese and English conversations. You provide users with safe, helpful, and accurate answers. You refuse to answer any questions related to terrorism, racism, or explicit content. Moonshot AI is a proper noun and should not be translated."},
{"role": "user", "content": "Please search the internet for 'Context Caching' and tell me what it is."}
],
tools=tools, # <-- 정의한 tools를 전달
)
응답 예시:
{
"finish_reason": "tool_calls",
"message": {
"content": "",
"role": "assistant",
"tool_calls": [
{
"id": "search:0",
"function": {
"arguments": "{\n \"query\": \"Context Caching\"\n}",
"name": "search"
},
"type": "function"
}
]
}
}
finish_reason이tool_calls이면 모델이 사용자 답변이 아닌 도구 호출을 반환한 상태message.content은 비어있을 수 있음 (아직 사용자 답변 미생성)tool_calls는 이번 턴의 도구 호출 정보 리스트 — 한 번에 여러 도구 호출 가능
2-3. 도구 실행 및 결과 반환
Kimi는 도구를 직접 실행하지 않습니다. 모델이 생성한 인자로 애플리케이션이 도구를 실행하고, 그 결과를 다시 모델에 전달해야 합니다. 전체 예시:
import os
import json
import httpx
from typing import Any, Dict, List
from openai import OpenAI
client = OpenAI(
api_key=os.environ["MOONSHOT_API_KEY"],
base_url="https://api.moonshot.ai/v1",
)
# (위 tools 정의 생략)
def search_impl(query: str) -> List[Dict[str, Any]]:
"""검색 엔진 API 호출 — 실제 환경에 맞게 구현"""
r = httpx.get("https://your.search.api", params={"query": query})
return r.json()
def search(arguments: Dict[str, Any]) -> Any:
return {"result": search_impl(arguments["query"])}
def crawl_impl(url: str) -> str:
"""웹 페이지 스크래핑 — 실제 환경에 맞게 구현"""
r = httpx.get(url)
return r.text
def crawl(arguments: dict) -> str:
return {"content": crawl_impl(arguments["url"])}
# 도구 이름 → 실행 함수 매핑
tool_map = {"search": search, "crawl": crawl}
messages = [
{"role": "system", "content": "You are Kimi, an AI assistant provided by Moonshot AI. You are proficient in Chinese and English conversations. You provide users with safe, helpful, and accurate answers. You will refuse to answer any questions involving terrorism, racism, or explicit content. Moonshot AI is a proper noun and should not be translated into other languages."},
{"role": "user", "content": "Please search for Context Caching online and tell me what it is."}
]
# 기본 흐름:
# 1) tools와 함께 모델에 질문
# 2) finish_reason=tool_calls면 도구 실행 후 결과를 role=tool로 추가
# 3) 모델이 finish_reason=stop을 반환할 때까지 반복
while True:
completion = client.chat.completions.create(
model="kimi-k3",
messages=messages,
tools=tools,
)
message = completion.choices[0].message
messages.append(message) # <-- 핵심: assistant 메시지를 반드시 추가
if not message.tool_calls:
# finish_reason=stop → 최종 답변
print(message.content)
break
for tool_call in message.tool_calls:
func_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
result = tool_map[func_name](args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
전체 메시지 시퀀스 예시 (온라인 검색):
system: prompt
user: prompt
assistant: tool_call(name=search, arguments={query: query})
tool: search_result(tool_call_id=tool_call.id, name=search)
assistant: tool_call_1(name=crawl, ...), tool_call_2(name=crawl, ...)
tool: crawl_content(tool_call_id=tool_call_1.id, name=crawl)
tool: crawl_content(tool_call_id=tool_call_2.id, name=crawl)
assistant: message_content(finish_reason=stop)
3. 스트리밍 출력에서 tool_calls 처리
스트리밍 모드(stream=True)에서도 tool_calls는 동작하지만 몇 가지 추가 고려사항이 있습니다.
- 스트리밍에서는
finish_reason이 마지막 청크에 나타나므로,delta.tool_calls필드 존재 여부로 도구 호출을 판단하는 것을 권장 delta.content가 먼저 출력되고delta.tool_calls가 뒤따라 출력됨 —delta.content가 끝날 때까지 기다려야tool_calls를 식별 가능- 첫 데이터 청크에
tool_call.id와tool_call.function.name이 지정되고, 이후 청크에서는tool_call.function.arguments만 채워짐 - 여러
tool_calls를 한 번에 반환하는 경우,index필드로 현재tool_call의 인덱스를 알려줌 (arguments를 청크별로 이어붙일 때 사용)
import os
import json
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("MOONSHOT_API_KEY"),
base_url="https://api.moonshot.ai/v1",
)
# (tools 정의는 위와 동일)
completion = client.chat.completions.create(
model="kimi-k3",
messages=[
{"role": "user", "content": "Please search for Context Caching technology online."}
],
stream=True,
tools=tools,
)
# n=2 가정, message 리스트 2개로 시작
messages = [{}, {}]
for chunk in completion:
for choice in chunk.choices:
index = choice.index
message = messages[index]
delta = choice.delta
role = delta.role
if role:
message["role"] = role
content = delta.content
if content:
message["content"] = message.get("content", "") + content
tool_calls = delta.tool_calls
if tool_calls:
if "tool_calls" not in message:
message["tool_calls"] = []
for tool_call in tool_calls:
tci = tool_call.index
# 인덱스에 맞춰 리스트 확장
while len(message["tool_calls"]) < tci + 1:
message["tool_calls"].append({})
tco = message["tool_calls"][tci]
tco["index"] = tci
if tool_call.id:
tco["id"] = tool_call.id
if tool_call.type:
tco["type"] = tool_call.type
if tool_call.function:
tco.setdefault("function", {})
if tool_call.function.name:
tco["function"]["name"] = tool_call.function.name
if tool_call.function.arguments:
tco["function"]["arguments"] = tco["function"].get("arguments", "") + tool_call.function.arguments
for i, m in enumerate(messages):
print(f"index: {i}")
print(f"message: {json.dumps(m, ensure_ascii=False)}")
4. function_call 대신 tool_calls 사용
tool_calls는 function_call에서 진화한 것이며, function_call은 tool_calls의 부분집합입니다. OpenAI가 function_call과 관련 파라미터(functions 등)를 deprecated로 표시함에 따라, Kimi API는 더 이상 function_call을 지원하지 않습니다. tool_calls를 사용하세요.
tool_calls의 장점:
- 병렬 호출 지원 — Kimi가 한 번에 여러
tool_calls를 반환할 수 있어, 코드에서 동시성으로 처리해 시간 단축 - 의존성 없는
tool_calls는 Kimi가 병렬로 호출하는 경향 —function_call의 순차 호출 대비 토큰 소비 절감
5. 주의사항
finish_reason=tool_calls일 때message.content이 간혹 비어있지 않을 수 있음 — Kimi가 어떤 도구를 왜 호출하는지 설명하는 텍스트. 도구 처리에 시간이 오래 걸리거나, 단일 턴에 순차 도구 호출이 많을 때 이 설명이 사용자 대기 불안/불만을 줄이고 흐름 이해에 도움tools파라미터의 콘텐츠도 총 토큰에 포함됨 —tools와messages의 합산 토큰이 모델의 컨텍스트 윈도우를 넘지 않도록 주의
5-1. 모든 tool_call에 대응하는 tool 메시지
도구 호출 시나리오에서 메시지는 단순 system / user / assistant 교대가 아니라 다음 형태가 됩니다.
system: ...
user: ...
assistant: ... (tool_calls 포함)
tool: ... (tool_call_id 일치)
tool: ...
assistant: ... (최종 답변)
- 모든
tool_call에 대응하는role=tool메시지가 있어야 함 role=tool메시지의tool_call_id가tool_calls의tool_call.id와 일치해야 함- 개수나 ID가 맞지 않으면 에러 발생
5-2. tool_call_id not found 에러 해결
이 에러는 보통 모델이 반환한 role=assistant 메시지를 messages 리스트에 추가하지 않았을 때 발생합니다. 올바른 시퀀스:
system: ...
user: ...
assistant: ... # <-- 이 메시지를 누락하면 에러 발생
tool: ...
tool: ...
assistant: ...
messages.append(message)로 매번 모델이 반환한 assistant 메시지를 그대로 추가하면 이 에러를 피할 수 있습니다.
중요:
role=tool메시지 직전의 assistant 메시지는 모델이 반환한tool_calls필드와 그 값을 빠짐없이 포함해야 합니다. SDK가 반환한choice.message를 가능한 한 그대로 추가하는 것을 권장합니다.
변경 이력
| 날짜 | 변경 |
|---|---|
| 2026-07-22 | 초판 작성 (공식 Tool Calls 가이드 한글화) |
| 2026-07-22 | 제목에 "Kimi" 접두사 추가, 본문 H1 중복 제거 |
댓글
아직 댓글이 없습니다.
댓글을 작성하려면 로그인이 필요합니다.