Инструменты и агенты
run_tool_loop выполняет весь цикл работы с инструментами: вызывает модель,
запускает запрошенные функции из dispatch и повторяет процесс, пока модель не
вернёт окончательный ответ без новых вызовов инструментов.
def get_weather(city: str) -> str:
return f"В {city} сейчас 20°C"
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}
]
broker = llmbroker.Broker()
reply = llmbroker.run_tool_loop(
broker,
[{"role": "user", "content": "Какая погода в Москве?"}],
tools=tools,
dispatch={"get_weather": get_weather},
)
print(reply.text, "— ответила", reply.llm_name)
Функция возвращает результат последнего обращения к модели в том же формате, что
и chat: текст, имя модели и данные usage для этого обращения. Каждое
предыдущее обращение записывается в журнал отдельно. Поэтому usage в результате
относится только к последнему обращению, а общие значения за весь цикл нужно
суммировать по журналу.
Для AsyncBroker используйте асинхронную функцию
await llmbroker.arun_tool_loop(...).
Число обращений ограничено параметром max_steps, равным 8 по умолчанию. Если на
последнем шаге модель снова запрашивает инструмент, возникает
llmbroker.ToolLoopLimitError. Увеличьте max_steps или обработайте исключение,
если хотите использовать промежуточный результат.
Параметры вызовов брокера
Дополнительные параметры, включая operation=, trace_id= и wait=, передаются
в каждый вызов chat внутри цикла. Задавайте их так же, как при отдельном вызове
chat. Без operation= все обращения относятся к общей категории, поэтому
оценки качества не смогут различать задачи:
reply = llmbroker.run_tool_loop(
broker,
messages,
tools=tools,
dispatch={"get_weather": get_weather},
operation="weather-agent",
trace_id=request_id,
)
Одинаковый trace_id объединяет в журнале все обращения одного цикла. При записи
оценки по этому идентификатору будет выбран только последний успешный вызов. Чтобы
оценить другой вызов из того же цикла, сохраните его call_id. Подробнее см. в
разделе Оценка качества.
Конкретная модель
Вместо брокера цикл принимает и прямой клиент. Тогда каждое обращение идёт к одной выбранной модели:
broker = llmbroker.Broker(direct=["opus"])
reply = llmbroker.run_tool_loop(
broker.direct("opus"),
[{"role": "user", "content": "Какая погода в Москве?"}],
tools=tools,
dispatch={"get_weather": get_weather},
params={"temperature": 0},
)
print(reply.text, reply.usage)
Результат — последний DirectResult с полями text, tool_calls и usage. В
нём нет llm_name и call_id: модель выбрана вами, а прямой вызов не
записывается в журнал. После ошибки цикл не переходит к другой модели, и
исключение передаётся приложению. Дополнительные параметры передаются в метод
chat клиента, поэтому здесь используются timeout= и params=, а не
operation=, trace_id= или wait=.
Псевдониму из каталога не нужны параметры, чтобы участвовать в цикле: параметры,
которые его модели требуются вместе с инструментами, берутся из каталога и
отправляются в каждом раунде. Ваши params имеют над ними приоритет. См.
Прямые вызовы модели.
Обработка ошибок инструментов
Исключения из функций в dispatch не передаются вызывающему коду. Если функция
завершилась с ошибкой, модель получает сообщение
Tool <имя> failed: <ошибка> и может повторить запрос с другими аргументами.
Если запрошенной функции нет в dispatch, модель получает сообщение
Unknown tool <имя>. Успешный результат функции также преобразуется с помощью
str(), поэтому из инструмента следует возвращать текст или готовую строку JSON,
а не произвольный объект.
Если функция сама преобразовала ошибку в обычный результат, цикл продолжится и
потратит на это один шаг. Чтобы ошибка инструмента завершала весь запрос,
обработайте её в функции и верните модели однозначный признак отказа либо
реализуйте цикл самостоятельно. Метод chat(messages, tools=...) возвращает
tool_calls, после чего приложение может выбрать дальнейшее поведение.
Области данных
Первым аргументом может быть любой объект с методом chat, принимающим tools=:
брокер, прямой клиент или объект из broker.for_scope(user). С объектом из
for_scope каждое обращение в цикле использует ключи этого пользователя и
записывается в журнал с его областью. Подробнее см. в разделе
Многопользовательские приложения.