Инструменты агента у входа на пользователя с промежуточным модулем LangChain Wrap
Один агент калькулятора, три уровня доступа: промежуточный слой в стиле LangChain заменяет список инструментов в зависимости от типа пользователя во время выполнения, вместо того чтобы клонировать три отдельных агента.
Источники: Wrap-Style Middleware in LangChain
Динамически контролировать, какие инструменты агента видны во время выполнения из контекста пользователя.
Искусственный интеллект-агент взаимодействует с внешним миром через инструменты — небольшие функции для арифметических операций, поиска или других действий. По умолчанию каждый разговор с этим агентом использует один и тот же список инструментов. Однако это редко соответствует потребностям продукта. Лобби здания открыто для посетителей, офисы — для сотрудников, а серверная — только для администраторов. Одно здание; разные уровни доступа.
Тот же принцип применим и к агентам: одно определение агента, но разные наборы инструментов для разных пользователей.
Пример
Агент-калькулятор предоставляет четыре инструмента: сложение, вычитание, умножение и деление.
Три уровня доступа:
- Внутренний — сложение и вычитание
- Внешний — только сложение
- Администратор системы — все четыре
Очевидный (плохой) подход
Запустите три отдельных агента, по одному на уровень. Этот подход работает до тех пор, пока не произойдут изменения в системных настройках, параметрах модели или реализации инструментов. Каждое изменение необходимо повторять три раза. Такая дублирование — неверный подход.
Лучший вариант: промежуточное решение типа «обертка»
Оставьте один агент. Разместите небольшой чекпоинт перед вызовом модели. При поступлении запроса чекпоинт определяет, кто является отправителем, и перед запуском модели подключает только разрешенные инструменты.
Этот чекпоинт и является промежуточным решением. Тип «обертка» означает, что он окружает обычный вызов модели: сначала анализируется запрос, затем он корректируется, после чего происходит дальнейшая обработка.
Эскиз реализации
Импорты
Полезные компоненты:
create_agent— создает агента@wrap_model_call— декоратор промежуточного решения типа «обертка»
ModelRequest / ModelResponse — форматы входящих запросов и выходящих ответовHumanMessage — текст от пользователя@tool — преобразует функцию в инструментКлюч API
openai_api_key = os.getenv("OPENAI_API_KEY")
if not openai_api_key:
raise ValueError(
"Please set the OPENAI_API_KEY environment variable."
)
Контекст выполнения
user_type указывает уровень доступа: internal, external или sys_admin. Передавайте его при вызове, например context={"user_type": "internal"}.
Инструменты
Определите четыре калькуляторных инструмента в виде обычных функций, оформленных как инструменты.
Промежуточное программирование
@wrap_model_call
def dynamic_tools(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
user_type = request.runtime.context["user_type"]
if user_type == "internal":
tools = [
addition,
subtraction,
]
elif user_type == "external":
tools = [
addition,
]
elif user_type == "sys_admin":
tools = [
addition,
subtraction,
multiplication,
division,
]
else:
raise ValueError(f"Unknown user type: {user_type}")
# Dynamically change the tools available to the model
modified_request = request.override(
tools=tools
)
return handler(modified_request)
Алгоритм работы внутри обёртки:
- Читайте значение
user_typeизrequest.runtime.context - Выбирайте список разрешённых инструментов для данного типа
request.override(tools=tools), чтобы скопировать запрос с новым спискомhandler(), чтобы вызов модели продолжилсяoverride() копирует запрос и заменяет только те поля, которые вы указали — в данном случае tools.
Модель чата
temperature=0 делает демонстрации детерминированными.
Создание агента
Зарегистрируйте все четыре инструмента в агенте. Мидлвэрь определяет, какой набор инструментов фактически видит модель для каждого запроса. Системное сообщение должно указать модели использовать доступные инструменты и сообщать, когда запрашиваемая операция недоступна, вместо того чтобы самой вычислять арифметические операции.
Общее сообщение для тестирования
Используйте одно и то же HumanMessage для всех уровней; меняется только user_type в контексте.
Вызовы
## INTERNAL USER
print("\n" + "=" * 70)
print("USER TYPE: INTERNAL")
print("=" * 70)
result = agent.invoke(
{
"messages": [human_message]
},
context={
"user_type": "internal"
},
)
print(result["messages"][-1].content)
## EXTERNAL USER
print("\n" + "=" * 70)
print("USER TYPE: EXTERNAL")
print("=" * 70)
result = agent.invoke(
{
"messages": [human_message]
},
context={
"user_type": "external"
},
)
print(result["messages"][-1].content)
## SYS_ADMIN USER
print("\n" + "=" * 70)
print("USER TYPE: SYS_ADMIN")
print("=" * 70)
result = agent.invoke(
{
"messages": [human_message]
},
context={
"user_type": "sys_admin"
},
)
print(result["messages"][-1].content)
Результат демонстрации
Показ выполнения одного и того же сообщения в трех разных контекстах демонстрирует, что промежуточный слой выполняет свою функцию:
- Внутренний пользователь может суммировать и вычитать значения, но не умножать и не делить
- Внешний пользователь может только суммировать значения
- Администратор системы может использовать все четыре операции
Основная схема
Суть можно описать в нескольких строках:
@wrap_model_call
def dynamic_tools(request, handler):
user_type = request.runtime.context["user_type"]
tools = choose_tools(user_type)
modified_request = request.override(
tools=tools
)
return handler(modified_request)
Промежуточный слой оборачивает вызов модели, считывает тип текущего пользователя, выбирает необходимые инструменты, изменяет запрос и передает его дальше. В этом эксперименте меняется только список инструментов — что достаточно для демонстрации контроля через оборачивание.
Вывод
Промежуточный слой в стиле оборачивания позволяет продукту изменять запрос к модели до того, как его увидит сама модель. Здесь доступ к инструментам регулируется по параметру user_type, что позволяет одному агенту обслуживать запросы с разными уровнями доступа. Ключевая строка заключается в следующем:
modified_request = request.override(tools=tools)
Это позволяет создавать для каждого запроса объект ModelRequest, у которого набор инструментов соответствует месту использования — лобби, офису или серверной, без необходимости использования трех копированных агентов.
В производственной среде его следует сочетать с механизмом аутентификации, который определяет значение user_type на основе проверенной идентичности, а не только строк, предоставленных клиентом. Логирование того, какие инструменты были применены к каждому запросу, также помогает при аудите, когда кто-то утверждает, что модель «должна была» иметь привилегированный инструмент. Сохраняйте список разрешенных инструментов небольшим и пересматривайте его каждый раз, когда в агент калькулятора (или любого другого) добавляется новый инструмент.