Диспетчер инструментов
Реализуем ядро механики: реестр инструментов и диспетчер, который по имени вызывает нужную функцию.
Диспетчер инструментов (tool dispatcher) — код, который принимает имя инструмента и аргументы, находит соответствующую функцию в реестре и вызывает её, возвращая результат агенту.
Идея
Модель говорит текстом: «вызови calculator с аргументом 2 * 15». Наш код должен превратить эту строку в реальный вызов функции calculator("2 * 15"). Связующее звено — диспетчер. Внутри он держит реестр: словарь «имя → функция».
Запускаемый диспетчер
Соберём два инструмента (калькулятор и поиск-заглушку) и диспетчер, который выбирает их по имени. Это и есть тот код, что в настоящем агенте стоит между моделью и внешним миром.
def calculator(expression):
# простой и безопасный калькулятор: только цифры и операции
allowed = set("0123456789+-*/(). ")
if not set(expression) <= allowed:
return "ошибка: недопустимые символы"
return str(eval(expression))
def search(query):
# заглушка поиска: маленькая «база знаний»
db = {
"столица франции": "Париж",
"автор гамлета": "Уильям Шекспир",
}
return db.get(query.lower().strip(), "ничего не найдено")
# Реестр: имя инструмента -> функция
TOOLS = {"calculator": calculator, "search": search}
def dispatch(name, arg):
if name not in TOOLS:
return f"ошибка: инструмент '{name}' не найден"
return TOOLS[name](arg)
print(dispatch("calculator", "2 * (10 + 5)"))
print(dispatch("search", "столица Франции"))
print(dispatch("search", "погода завтра"))
print(dispatch("translator", "hello"))
Вывод:
30 Париж ничего не найдено ошибка: инструмент 'translator' не найден
Разберём, что произошло:
dispatch("calculator", ...)нашёл функцию в реестре и вызвал её — получили точный результат.searchпо известному запросу вернул факт, по неизвестному — «ничего не найдено».- Запрос несуществующего инструмента
translatorдал понятную ошибку, а не падение программы.
Почему так, а не «модель сама вызовет функцию»
Модель не выполняет код — она только просит. Диспетчер нужен, чтобы:
- контролировать, какие функции вообще доступны (модель не вызовет то, чего нет в реестре);
- в одном месте добавлять логирование, тайм-ауты, проверку прав;
- аккуратно обрабатывать ошибки (следующий урок).
Расширяемость
Добавить инструмент — это добавить одну запись в реестр. Агент сразу сможет его использовать, если упомянуть в описании для модели. Реестр — естественная точка роста системы.
def word_count(text):
return str(len(text.split()))
TOOLS = {"count": word_count}
def dispatch(name, arg):
if name not in TOOLS:
return f"ошибка: нет инструмента '{name}'"
return TOOLS[name](arg)
print("инструментов в реестре:", len(TOOLS))
print(dispatch("count", "агент рассуждает и действует"))
Вывод:
инструментов в реестре: 1 4
Итог
- Диспетчер связывает текстовую «просьбу» модели с реальным вызовом функции через реестр «имя → функция».
- Он же — единая точка для контроля доступа, логирования и обработки ошибок.
- Новый инструмент = новая запись в реестре; масштабировать агента просто.