Кодовый агент на Kimi K3: вызов инструментов и долгие задачи

Kimi K3 силён в долгих кодовых агентах. Разберём вызов функций через tools, tool_choice, динамическую загрузку инструментов и ключевой подвох многоходовки с разбором рефакторинга репозитория.

В официальном позиционировании Kimi K3 на первом месте стоит «долгое программирование»: контекст в 1M вмещает весь репозиторий, всегда включённые рассуждения тянут многошаговое планирование, а нативный вызов инструментов управляет терминалом и внешними функциями. Это делает модель идеальной для кодового агента: он сам читает код, вызывает инструменты и связывает длинную цепочку действий, доводя задачу до конца. В этой статье разберём, как собрать агента на вызове инструментов K3, и главную ловушку, в которую чаще всего попадают на долгих задачах.

Основы вызова инструментов (function calling)

K3 совместим со спецификацией tools от OpenAI. Определите инструмент и дайте модели самой решать, когда его вызвать:

import os, json
from openai import OpenAI

client = OpenAI(api_key=os.environ["MOONSHOT_API_KEY"],
                base_url="https://api.moonshot.cn/v1")

tools = [{
    "type": "function",
    "function": {
        "name": "read_file",
        "description": "Прочитать содержимое файла в репозитории",
        "parameters": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
    },
}]

messages = [{"role": "user", "content": "Проверь, нет ли в auth.py захардкоженных ключей"}]

resp = client.chat.completions.create(
    model="kimi-k3", messages=messages, tools=tools,
)

Когда модель решает вызвать инструмент, в возвращённом сообщении assistant появляется tool_calls. Вы выполняете соответствующую функцию локально, отдаёте результат обратно как сообщение role:"tool" и запрашиваете следующий ход.

Ключевой подвох: в многоходовке возвращайте полное сообщение assistant без изменений

Именно здесь чаще всего спотыкаются те, кто мигрирует с K2: в многоходовых диалогах и вызовах инструментов добавляйте в следующий запрос полное сообщение assistant, которое вернул API, без изменений — не оставляйте только content.

У K3 рассуждения всегда включены, и сообщение assistant несёт помимо content ещё и состояние рассуждений / инструментов. Если вернуть только content, у модели наступает «амнезия»: цепочка рассуждений рвётся, а вызовы инструментов путаются. Правильный вариант:

# Добавляем всё сообщение assistant (включая tool_calls / состояние рассуждений) без изменений
messages.append(resp.choices[0].message)

# Выполняем инструменты и возвращаем результаты как сообщения tool
for call in resp.choices[0].message.tool_calls or []:
    result = run_tool(call.function.name, json.loads(call.function.arguments))
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

# Запрашиваем следующий ход с полной историей
resp = client.chat.completions.create(
    model="kimi-k3", messages=messages, tools=tools,
)

Запомните: добавляйте весь объект сообщения — не собирайте вручную новый только с content.

Принудительный вызов и динамическая загрузка инструментов

  • Принудительный вызов: чтобы модель обязательно использовала инструмент на этом ходу, задайте tool_choice="required" (или укажите конкретную функцию) — подходит для сценариев «сначала найди, потом отвечай».
  • Динамическая загрузка инструментов: когда инструментов много, можно положить их определения в сообщение system и выдавать по мере надобности, вместо того чтобы каждый ход набивать массив tools — это экономит контекст и даёт больше гибкости.
  • Официальные инструменты: Moonshot AI также предоставляет набор встроенных возможностей-инструментов, доступных через официальные эндпоинты вроде /tools и /fibers; за деталями обращайтесь к официальной документации.

Почему долгие задачи подходят K3

У кодового агента, который проходит множество ходов, контекст всё разрастается: содержимое репозитория + результаты инструментов ход за ходом + история рассуждений. Две особенности K3 как раз это подхватывают:

  • Контекст 1M вмещает весь репозиторий плюс длинную историю, так что вы не теряете контекст посреди прогона из-за переполнения окна (об организации длинного контекста см. руководство по контексту 1M);
  • Кэширование по умолчанию позволяет неизменному префиксу (репозиторий, system, определения инструментов) попадать в кэш снова и снова, опуская стоимость ввода долгого агента до ¥2/M — иначе пересылать префикс каждый ход выйдет дорого (расчёты — в разборе цен K3).

Поэтому, компонуя сообщения, выносите содержимое репозитория и определения инструментов как можно раньше и держите их стабильными, чтобы кэш работал на полную; каждый ход добавляйте в конец только то, что меняется.

Рефакторинг репозитория на практике

Типичный цикл агента для «рефакторинга всего репозитория»:

  1. Дайте всю картину: разом положите в контекст репозиторий (или релевантные части, извлечённые через RAG) + инструкции system;
  2. Дайте спланировать: сначала пусть K3 выдаст план рефакторинга (какие файлы менять, точки риска) — здесь всегда включённые рассуждения ценнее всего;
  3. Исполнение с инструментами: откройте read_file / write_file / run_tests и дайте ему шаг за шагом править и прогонять тесты;
  4. Возврат без изменений: каждый ход возвращайте полное сообщение assistant + результаты tool, сохраняя цепочку рассуждений;
  5. Финальная проверка: пусть сверится с исходным планом на предмет пропущенных правок и полностью зелёного набора тестов.

Весь этот поток можно поднять и на агрегирующем шлюзе — отдать планирование K3, а часть подзадач направить моделям вроде Claude Opus (сравнение см. в K3 против Claude и GPT) — всё под одним ключом.

Итог

Ключевая методика кодового агента на K3 сводится к трём пунктам: управляйте действиями через tools, сбивайте стоимость кэшем + выносом вперёд и в многоходовке возвращайте полное сообщение assistant без изменений. Соблюдите эти три пункта — и долгие задачи пойдут стабильно. Полную интеграцию и разбор ошибок см. в руководстве по API Kimi K3. Чтобы свободно распределять запросы между K3 и другими моделями под одним ключом, начните с консоли GetModel.