В статье про галлюцинации
мы договорились: арифметику и данные — не LLM, а Kotlin. Tool use
— это способ «подружить» эти два мира. Модель видит набор функций
(get_price, calculate_cuts,
save_order) и, когда считает нужным, возвращает
структурированный вызов. Твой код выполняет и отдаёт результат
обратно. Модель продолжает разговор уже с этим фактом.
tool_call с параметрами.// Описание инструмента (OpenAI-формат)
val tools = listOf(
mapOf(
"type" to "function",
"function" to mapOf(
"name" to "get_material_price",
"description" to "Возвращает цену единицы материала из прайса.",
"parameters" to mapOf(
"type" to "object",
"properties" to mapOf(
"material_code" to mapOf("type" to "string", "description" to "Код: oak_board_40mm и т.п.")
),
"required" to listOf("material_code")
)
)
)
)
Что здесь происходит: описали один инструмент. Ключевое —
description: по нему модель решает, когда его звать. Чем
яснее — тем точнее.
val response = openAi.chatCompletion {
model = "gpt-5.4-mini"
messages { system(SYSTEM_PROMPT); user(userMessage) }
this.tools = tools
}
val msg = response.choices.first().message
if (msg.toolCalls?.isNotEmpty() == true) {
val call = msg.toolCalls.first()
if (call.function.name == "get_material_price") {
val args = Json.decodeFromString<PriceArgs>(call.function.arguments)
val price = priceRepo.getUnitPrice(args.materialCode)
?: return respondError("price not found")
// отправляем результат обратно модели
val followUp = openAi.chatCompletion {
messages {
system(SYSTEM_PROMPT); user(userMessage)
assistant(msg)
tool(toolCallId = call.id, content = "{\"price\": $price}")
}
this.tools = tools
}
return followUp.choices.first().message.content
}
}
Что здесь происходит: проверили, не хочет ли модель позвать функцию → если хочет, распарсили параметры, достали цену из Postgres, отдали обратно. Модель видит результат и формирует финальный ответ.
get_material_price(material_code) — цена из прайса.get_material_stock(material_code) — сколько на складе.calculate_cuts(pieces, stock_lengths) — расчёт распила досок (bin packing). Это чисто Kotlin-алгоритм, LLM к нему только параметры подаёт.save_order(parsed_order_json) — сохранить заказ в БД.find_similar_orders(wood_type, floors) — найти похожие прошлые заказы (для оценки.).
У OpenAI и Anthropic есть strict: true — гарантия, что
параметры вызова будут точно по схеме. Для прода — включай всегда.
Дополнительно валидируй на Kotlin-стороне: даже если пришла
«правильная» строка, её значение может быть невалидным
(material_code = "unobtanium").
get_material_price).
Протестируй, убедись, что модель зовёт его в нужные моменты.
LlmToolService). Никогда не дёргай БД из «промпт-логики».
save_order, schedule_task) —
добавляй human-in-the-loop: модель предлагает, мастер подтверждает
кнопкой в UI.