Кодовый агент на 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).
Поэтому, компонуя сообщения, выносите содержимое репозитория и определения инструментов как можно раньше и держите их стабильными, чтобы кэш работал на полную; каждый ход добавляйте в конец только то, что меняется.
Рефакторинг репозитория на практике
Типичный цикл агента для «рефакторинга всего репозитория»:
- Дайте всю картину: разом положите в контекст репозиторий (или релевантные части, извлечённые через RAG) + инструкции system;
- Дайте спланировать: сначала пусть K3 выдаст план рефакторинга (какие файлы менять, точки риска) — здесь всегда включённые рассуждения ценнее всего;
- Исполнение с инструментами: откройте
read_file/write_file/run_testsи дайте ему шаг за шагом править и прогонять тесты; - Возврат без изменений: каждый ход возвращайте полное сообщение assistant + результаты tool, сохраняя цепочку рассуждений;
- Финальная проверка: пусть сверится с исходным планом на предмет пропущенных правок и полностью зелёного набора тестов.
Весь этот поток можно поднять и на агрегирующем шлюзе — отдать планирование K3, а часть подзадач направить моделям вроде Claude Opus (сравнение см. в K3 против Claude и GPT) — всё под одним ключом.
Итог
Ключевая методика кодового агента на K3 сводится к трём пунктам: управляйте действиями через tools, сбивайте стоимость кэшем + выносом вперёд и в многоходовке возвращайте полное сообщение assistant без изменений. Соблюдите эти три пункта — и долгие задачи пойдут стабильно. Полную интеграцию и разбор ошибок см. в руководстве по API Kimi K3. Чтобы свободно распределять запросы между K3 и другими моделями под одним ключом, начните с консоли GetModel.