AI / LLM
Tool calls: чтение кода и схем
Баги tool calling живут в схеме, диспетчере и цикле — не в модели. Читай каждый сниппет так, как читал бы на ревью, и выбирай фикс, который senior сделал бы первым.
Отработай петлю диагностики, которую ты запускаешь на каждом агенте: читай tool schema и код цикла, предскажи, как поведут себя модель и твой handler, и тянись к фиксу, который закрывает границу доверия или останавливает runaway.
Сниппет 1 — JSON schema tool
{
"name": "cancel_order",
"description": "Cancel an order.",
"input_schema": {
"type": "object",
"properties": {
"order_id": { "type": "string" },
"reason": { "type": "string" }
}
}
}
Мутирующий tool cancel_order выходит с этой схемой. В чём главная слабость и фикс с наибольшим рычагом?
Сниппет 2 — диспетчер parallel-вызовов
results = []
for block in response.tool_use_blocks: # их может быть несколько за ход
output = TOOLS[block.name](**block.input) # исполняем последовательно
results.append(tool_result(block.id, output))
send(messages + results)
Когда модель эмитит три независимых tool_use-блока за один ход, что этот диспетчер делает верно и что оставляет на столе?
Сниппет 3 — цикл без защиты
while True:
resp = model.create(messages=messages, tools=TOOLS)
if resp.stop_reason != "tool_use":
break
for b in resp.tool_use_blocks:
out = run_tool(b.name, b.input) # может бросить / зависнуть
messages.append(tool_result(b.id, out))
messages.append(resp.message)
Этот цикл работает в проде против client tools. Какие два дефекта укусят первыми при сбое и как их чинить?
Сниппет 4 — валидатор
def handle(block):
args = block.input
try:
validated = ToolArgs.model_validate(args) # pydantic: форма + типы
except ValidationError as e:
return tool_result(block.id, f"invalid arguments: {e}", is_error=True)
return tool_result(block.id, run(validated))
Этот handler валидирует по схеме через Pydantic и возвращает ошибки как tool_result. Для мутирующего tool против multi-tenant БД чего всё ещё не хватает?
Каждый баг tool calling читается в схеме, диспетчере, цикле или валидаторе. Слабая схема (нет required, нет enum, пустой description) даёт модели угадывать и не даёт тебе что валидировать. Последовательный диспетчер корректен, но теряет выигрыш по латентности от parallel-вызовов для независимых tools. while True без per-tool timeout — это runaway и зависание, ждущие случиться: ограничь итерации и тайм-боксь каждый tool. А валидация формы через Pydantic необходима, но недостаточна для мутирующего вызова: добавь проверку авторизации и существования и возвращай каждый отказ как tool_result, чтобы модель само-исправлялась.