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. 도구 호출의 전체 흐름

  1. JSON Schema로 도구 정의
  2. 정의한 도구를 tools 파라미터로 모델에 전달 (여러 개 동시 제출 가능)
  3. Kimi가 대화 맥락에 따라 사용할 도구(들)를 선택하거나, 아예 사용하지 않을 수 있음
  4. Kimi가 도구 호출에 필요한 파라미터와 정보를 JSON으로 출력
  5. 출력된 파라미터로 도구 실행 후 결과를 다시 모델에 전달
  6. 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_reasontool_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.idtool_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_callsfunction_call에서 진화한 것이며, function_calltool_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 파라미터의 콘텐츠도 총 토큰에 포함됨 — toolsmessages의 합산 토큰이 모델의 컨텍스트 윈도우를 넘지 않도록 주의

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_idtool_callstool_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 중복 제거

댓글

아직 댓글이 없습니다.

댓글을 작성하려면 로그인이 필요합니다.