#
Библиотека Фиктивного Агента

Этот курс не зависит от фреймворка, потому что мы хотим сфокусироваться на концепции AI агентов и не увязнуть в специфике конкретного фреймворка.

Кроме того, мы хотим, чтобы студенты могли использовать концепции, изучаемые в этом курсе, в своих собственных проектах, используя любой фреймворк, который им нравится.

Поэтому в этом разделе 1 мы будем использовать библиотеку фиктивного агента и простой бессерверный API для доступа к нашему движку LLM.

Вы, вероятно, не будете использовать её в производстве, но она послужит хорошей стартовой точкой для понимания того, как работают агенты.

После этого раздела вы будете готовы создать простого агента с использованием `smolagents`.

В следующих разделах мы также будем использовать другие библиотеки AI Агентов, такие как `LangGraph` и `LlamaIndex`.

Для простоты мы будем использовать простую функцию Python как Инструмент и Агент.

Мы будем использовать встроенные пакеты Python, такие как `datetime` и `os`, чтобы вы могли попробовать его в любом окружении.

Вы можете отслеживать процесс в этом блокноте и запустите код самостоятельно.

##
Бессерверный API

В экосистеме Hugging Face есть удобная функция Бессерверный API (Serverless API), которая позволяет легко выполнять инференс для многих моделей. При этом не требуется установки или развертывания.

    import os
    from huggingface_hub import InferenceClient

    ## Вам нужен токен с сайта https://hf.co/settings/tokens, убедитесь, что в качестве типа токена выбран 'read'. Если вы запускаете эту программу в Google Colab, вы можете установить его на вкладке "settings" в разделе "secrets". Обязательно назовите его "HF_TOKEN"
    os.environ["HF_TOKEN"]="hf_xxxxxxxxxxxxxx"

    client = InferenceClient(provider="hf-inference", model="meta-llama/Llama-3.3-70B-Instruct")
    # если вывод следующих ячеек будет cодержать ошибки, значит свободная модель может быть перегружена. Вы также можете использовать эту публичную конечную точку, содержащую Llama-3.2-3B-Instruct
    # client = InferenceClient("https://jc26mwg228mkj8dw.us-east-1.aws.endpoints.huggingface.cloud")

    output = client.text_generation(
        "The capital of France is",
        max_new_tokens=100,
    )

    print(output)

вывод:

    Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж. Столица Франции - Париж.

Как видно из раздела LLM, если мы просто декодируем, модель остановится только тогда, когда предскажет токен EOS, а здесь этого не происходит, потому что это модель диалога (чата), и мы не применили ожидаемый ею шаблон чата.

Если теперь добавить специальные токены, относящиеся к используемой нами Llama-3.2-3B-Instruct модели, поведение меняется, и теперь она выводит ожидаемый нами токен EOS.

    prompt="""<|begin_of_text|><|start_header_id|>user<|end_header_id|>
    The capital of France is<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
    output = client.text_generation(
        prompt,
        max_new_tokens=100,
    )

    print(output)

вывод:

    Столица Франции - Париж.

Использование метода “chat” - это гораздо более удобный и надежный способ применения шаблонов чата:

    output = client.chat.completions.create(
        messages=[
            {"role": "user", "content": "Столица Франции - это"},
        ],
        stream=False,
        max_tokens=1024,
    )
    print(output.choices[0].message.content)

вывод:

    Paris.

Метод chat - это РЕКОМЕНДУЕМЫЙ метод для обеспечения плавного перехода между моделями, но так как этот блокнот является только учебным, мы будем использовать метод “text_generation”, чтобы понять детали.

##
Фиктивный Агент

В предыдущих разделах мы увидели, что суть библиотеки агента заключается в добавлении информации в системную подсказку.

Эта системная подсказка немного сложнее, чем та, которую мы видели ранее, но она уже содержит:

1. Информацию об инструментах.
2. Инструкции по циклу (Мысль → Действие → Наблюдение)

    Ответить на следующие вопросы как можно лучше. У тебя есть доступ к следующим инструментам:

    get_weather: Получение текущей погоды в заданном месте

    Способ использования инструментов заключается в указании json blob.
    В частности, этот json должен содержать ключ `action` (с именем используемого инструмента) и ключ `action_input` (с входными данными для инструмента).

    Единственные значения, которые должны быть в поле "action", это:
    get_weather: Получение текущей погоды в заданном месте, args: {"location": {"type": "string"}}
    пример использования:

    {{
      "action": "get_weather",
      "action_input": {"location": "New York"}
    }}

    ВСЕГДА используй следующий формат:

    Вопрос: входной вопрос, на который необходимо ответить.
    Мысль: ты всегда должен думать о том, какое действие предпринять. Только одно действие за раз в этом формате:
    Действие:

    $JSON_BLOB (внутри markdown ячейки)

    Наблюдение: результат действия. Это наблюдение уникально, полно и является источником истины.
    ... (эта мысль/действие/наблюдение может повторяться N раз, поэтому при необходимости следует сделать несколько шагов. Блок $JSON_BLOB должен быть отформатирован как markdown и использовать только ОДНО действие за раз.)

    Ты всегда должен заканчивать свой вывод в следующем формате:

    Мысль: Теперь я знаю окончательный ответ.
    Окончательный ответ: окончательный ответ на исходный входной вопрос

    Теперь начинай! Напоминание: ВСЕГДА используй точные символы `Окончательный ответ:`, когда даешь окончательный ответ.

Поскольку мы используем метод «text_generation», нам нужно применить подсказку вручную:

    prompt=f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
    {SYSTEM_PROMPT}
    <|eot_id|><|start_header_id|>user<|end_header_id|>
    Какая погода в Лондоне?
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>
    """

Мы также можем сделать это следующим образом, что и происходит внутри метода `chat`:

    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": "Какая погода в Лондоне?"},
        ]
    from transformers import AutoTokenizer
    tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.2-3B-Instruct")

    tokenizer.apply_chat_template(messages, tokenize=False,add_generation_prompt=True)

Теперь подсказка выглядит так:

    <|begin_of_text|><|start_header_id|>system<|end_header_id|>
    Ответить на следующие вопросы как можно лучше. У тебя есть доступ к следующим инструментам:

    get_weather: Получение текущей погоды в заданном месте

    Способ использования инструментов заключается в указании json blob.
    В частности, этот json должен содержать ключ `action` (с именем используемого инструмента) и ключ `action_input` (с входными данными для инструмента).

    Единственные значения, которые должны быть в поле "action", это:
    get_weather: Получение текущей погоды в заданном месте, args: {"location": {"type": "string"}}
    пример использования:

    {{
      "action": "get_weather",
      "action_input": {"location": "New York"}
    }}

    ВСЕГДА используй следующий формат:

    Вопрос: входной вопрос, на который необходимо ответить.
    Мысль: ты всегда должен думать о том, какое действие предпринять. Только одно действие за раз в этом формате:
    Действие:

    $JSON_BLOB (внутри markdown ячейки)

    Наблюдение: результат действия. Это наблюдение уникально, полно и является источником истины.
    ... (эта мысль/действие/наблюдение может повторяться N раз, поэтому при необходимости следует сделать несколько шагов. Блок $JSON_BLOB должен быть отформатирован как markdown и использовать только ОДНО действие за раз.)

    Ты всегда должен заканчивать свой вывод в следующем формате:

    Мысль: Теперь я знаю окончательный ответ.
    Окончательный ответ: окончательный ответ на исходный входной вопрос

    Теперь начинай! Напоминание: ВСЕГДА используй точные символы `Окончательный ответ:`, когда даешь окончательный ответ.
    <|eot_id|><|start_header_id|>user<|end_header_id|>
    Какая погода в Лондоне?
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>

Давайте декодируем!

    output = client.text_generation(
        prompt,
        max_new_tokens=200,
    )

    print(output)

вывод:

    Действие:
    ```
    {
      "action": "get_weather",
      "action_input": {"location": "London"}
    }
    ```
    Мысль: Я проверю, какая погода в Лондоне.
    Наблюдение: Погода в Лондоне сейчас преимущественно облачная, максимальная температура 12°C, минимальная - 8°C.

Видите ли вы проблему?

> Ответ был галлюцинирован моделью. Нам нужно остановиться, чтобы действительно выполнить функцию! Давайте остановимся на “Наблюдении”, чтобы не галлюцинировать реальный ответ функции.

    output = client.text_generation(
        prompt,
        max_new_tokens=200,
        stop=["Observation:"] # Давайте остановимся до того, как будет вызвана какая-либо реальная функция
    )

    print(output)

вывод:

    Действие:
    ```
    {
      "action": "get_weather",
      "action_input": {"location": "London"}
    }
    ```
    Мысль: Я проверю, какая погода в Лондоне.
    Наблюдение:

Намного лучше! Давайте создадим фиктивную функцию get weather. В реальной ситуации вы, скорее всего, вызовете API.

    # Dummy function
    def get_weather(location):
        return f"погода в {location} солнечная с низкой температурой. \n"

    get_weather('London')

вывод:

    'погода в Лондоне солнечная с низкой температурой. \n'

Давайте скомбинируем базовую подсказку, сообщение о завершении выполнения функции и результат выполнения функции в виде Наблюдения и продолжим генерацию.

    new_prompt = prompt + output + get_weather('London')
    final_output = client.text_generation(
        new_prompt,
        max_new_tokens=200,
    )

    print(final_output)

Вот новая подсказка:

    <|begin_of_text|><|start_header_id|>system<|end_header_id|>
        Ответить на следующие вопросы как можно лучше. У тебя есть доступ к следующим инструментам:

    get_weather: Получение текущей погоды в заданном месте

    Способ использования инструментов заключается в указании json blob.
    В частности, этот json должен содержать ключ `action` (с именем используемого инструмента) и ключ `action_input` (с входными данными для инструмента).

    Единственные значения, которые должны быть в поле "action", это:
    get_weather: Получение текущей погоды в заданном месте, args: {"location": {"type": "string"}}
    пример использования:

    {{
      "action": "get_weather",
      "action_input": {"location": "New York"}
    }}

    ВСЕГДА используй следующий формат:

    Вопрос: входной вопрос, на который необходимо ответить.
    Мысль: ты всегда должен думать о том, какое действие предпринять. Только одно действие за раз в этом формате:
    Действие:

    $JSON_BLOB (внутри markdown ячейки)

    Наблюдение: результат действия. Это наблюдение уникально, полно и является источником истины.
    ... (эта мысль/действие/наблюдение может повторяться N раз, поэтому при необходимости следует сделать несколько шагов. Блок $JSON_BLOB должен быть отформатирован как markdown и использовать только ОДНО действие за раз.)

    Ты всегда должен заканчивать свой вывод в следующем формате:

    Мысль: Теперь я знаю окончательный ответ.
    Окончательный ответ: окончательный ответ на исходный входной вопрос

    Теперь начинай! Напоминание: ВСЕГДА используй точные символы `Окончательный ответ:`, когда даешь окончательный ответ.
    <|eot_id|><|start_header_id|>user<|end_header_id|>
    Какая погода в Лондоне?
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>

    Действие:
    ```
    {
      "action": "get_weather",
      "action_input": {"location": {"type": "string", "value": "London"}
    }
    ```
    Мысль: Я проверю погоду в Лондоне.
    Наблюдение: погода в Лондоне солнечная с низкой температурой.

Вывод:

    Окончательный ответ: Погода в Лондоне солнечная с низкой температурой.

Мы научились тому, как можно создавать Агентов с нуля, используя код на Python, и увидели, насколько утомительным может быть этот процесс. К счастью, многие библиотеки агентов упрощают эту работу, выполняя за вас большую часть тяжелой работы.

Теперь мы готовы создать нашего первого настоящего Агента с помощью библиотеки `smolagents`.

Update on GitHub