Главная / Статьи

Инструменты агента у входа на пользователя с промежуточным модулем LangChain Wrap

Один агент калькулятора, три уровня доступа: промежуточный слой в стиле LangChain заменяет список инструментов в зависимости от типа пользователя во время выполнения, вместо того чтобы клонировать три отдельных агента.

Издатель: REACTAPP.TOP4 мин чтения978 слов

Источники: 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)
    

    Алгоритм работы внутри обёртки:

    1. Читайте значение user_type из request.runtime.context
    2. Выбирайте список разрешённых инструментов для данного типа
  • Вызовите 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)
    

    Результат демонстрации

    Показ выполнения одного и того же сообщения в трех разных контекстах демонстрирует, что промежуточный слой выполняет свою функцию:

    1. Внутренний пользователь может суммировать и вычитать значения, но не умножать и не делить
    2. Внешний пользователь может только суммировать значения
    3. Администратор системы может использовать все четыре операции

    Основная схема

    Суть можно описать в нескольких строках:

    @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 на основе проверенной идентичности, а не только строк, предоставленных клиентом. Логирование того, какие инструменты были применены к каждому запросу, также помогает при аудите, когда кто-то утверждает, что модель «должна была» иметь привилегированный инструмент. Сохраняйте список разрешенных инструментов небольшим и пересматривайте его каждый раз, когда в агент калькулятора (или любого другого) добавляется новый инструмент.