Buckets:
| # Что такое Инструменты? | |
| Одним из важнейших аспектов AI Агентов является их способность предпринимать **действия**. Как мы видели, это происходит благодаря использованию **Инструментов**. | |
| В этом разделе мы узнаем, что такое Инструменты, как их эффективно разработать и как интегрировать их в вашего Агента с помощью Системного Сообщения. | |
| Предоставив своему агенту правильные инструменты и четко описав, как они работают, вы сможете значительно расширить возможности своего AI. Давайте погружаться! | |
| ## Что такое AI Инструменты? | |
| ** Инструмент - это функция, предоставленная LLM**. Эта функция должна выполнять **четкую цель**. | |
| Вот некоторые часто используемые в AI агентах инструменты: | |
| | Инструмент | Описание | | |
| |----------------|---------------------------------------------------------------| | |
| | Веб-поиск | Позволяет агенту получать актуальную информацию из Интернета. | | |
| | Генерация изображений | Создает изображения на основе текстовых описаний. | | |
| | Извлечение | Извлекает информацию из внешнего источника. | | |
| | | Интерфейс API | Взаимодействие с внешним API (GitHub, YouTube, Spotify и т. д.). | | |
| Это лишь примеры, поскольку на самом деле вы можете создать инструмент для любого случая использования! | |
| Хороший инструмент должен быть чем-то, что **дополняет возможности LLM**. | |
| Например, если вам нужно выполнить арифметические действия, то предоставление вашему LLM **калькулятора** обеспечит лучшие результаты, чем полагаться на собственные возможности модели. | |
| Кроме того, **LLM предсказывают завершение подсказки на основе своих обучающих данных**, что означает, что их внутренние знания включают только события, произошедшие до их обучения. Поэтому, если вашему агенту нужны свежие данные, вы должны предоставить их с помощью какого-либо инструмента. | |
| Например, если вы спросите у LLM напрямую (без инструмента поиска) о сегодняшней погоде, LLM потенциально может выдать случайную погоду в виде галлюцинаций. | |
| - Инструмент должен: | |
| - **иметь текстовое описание того, что делает функция**. | |
| - *быть Вызываемым (Callable)* (чем-то, что выполняет действие). | |
| - *иметь Аргументы* с типизацией. | |
| - (Необязательно) иметь Выходные данные с типизацией. | |
| ## Как работают инструменты? | |
| Как мы видели, **LLM могут только получать текстовые данные на вход и генерировать текстовые данные на выход. У них нет возможности самостоятельно вызывать инструменты. Когда мы говорим о _предоставлении инструментов агенту_, мы имеем в виду, что мы **обучаем** LLM существованию инструментов и просим модель генерировать текст, который будет вызывать инструменты, когда это необходимо. Например, если мы предоставим инструмент для проверки погоды в определенном месте из Интернета, а затем спросим LLM о погоде в Париже, LLM распознает этот вопрос как релевантную возможность использовать инструмент "weather", которой мы его научили. LLM сгенерирует _текст_ в виде кода, чтобы вызвать этот инструмент. Ответственность **Агента** заключается в том, чтобы проанализировать вывод LLM, распознать, что требуется вызов инструмента, и вызвать его от имени LLM. Выходные данные от инструмента будут отправлены обратно в LLM, которая составит окончательный ответ для пользователя. | |
| Выходные данные после вызова инструмента - это еще один тип сообщений в диалоге. Шаги вызова инструмента обычно не демонстрируются пользователю: агент извлекает диалог, вызывает инструмент(ы), получает выходные данные, добавляет их в новое сообщение диалога и снова отправляет обновленный диалог в LLM. С точки зрения пользователя это выглядит так, как будто LLM использовал инструмент, но на самом деле это сделал наш код приложения (**Агент**). | |
| Мы поговорим об этом процессе подробнее на следующих занятиях. | |
| ## Как мы даем инструменты LLM? | |
| Полный ответ может показаться непомерно сложным, но мы, по сути, используем системную подсказку для предоставления текстовых описаний доступных модели инструментов: | |
| Чтобы это сработало, мы должны быть очень точны и аккуратны в отношении: | |
| 1. **Что делает инструмент**. | |
| 2. **Каких именно входных данных он ожидает**. | |
| Именно по этой причине описания инструментов обычно предоставляются с использованием выразительных, но точных структур, таких как компьютерные языки или JSON. Не обязательно делать это именно так, подойдет любой точный и последовательный формат. | |
| Если это кажется слишком теоретическим, давайте разберемся на конкретном примере. | |
| Мы реализуем упрощенный **калькулятор**, который будет просто перемножать два целых числа. Это может быть наша реализация на Python: | |
| ```python | |
| def calculator(a: int, b: int) -> int: | |
| """Multiply two integers.""" | |
| return a * b | |
| ``` | |
| Итак, наш инструмент называется `calculator`, он **перемножает два целых числа**, и ему требуются следующие входные данные: | |
| - **`a`** (*int*): Целое число. | |
| - **`b`** (*int*): Целое число. | |
| На выходе получается другое целое число, которое можно описать следующим образом: | |
| - (*int*): Произведение `a` и `b`. | |
| Все эти детали очень важны. Давайте соберем их вместе в текстовую строку, которая описывает наш инструмент для понимания LLM. | |
| ```text | |
| Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int | |
| ``` | |
| > **Напоминание:** Это текстовое описание - *то, что мы хотим, чтобы LLM знала об инструменте*. | |
| Когда мы передадим предыдущую строку как часть входных данных в LLM, модель распознает ее как инструмент, и будет знать, что ему нужно передавать в качестве входных данных и что ожидать от выходных данных. | |
| Если мы хотим предоставить дополнительные инструменты, мы должны быть последовательными и всегда использовать один и тот же формат. Этот процесс может быть хрупким, и мы можем случайно упустить некоторые детали. | |
| Есть ли лучший способ? | |
| ### Автоформатирование секции Инструменты | |
| Наш инструмент написан на Python, и его реализация уже предоставляет все, что нам нужно: | |
| - Описательное название того, что он делает: `calculator`. | |
| - Более длинное описание, представленное в комментарии к docstring функции: `Multiply two integers.`. | |
| - Входные данные и их тип: функция явно ожидает два `int`. | |
| - Тип выходных данных. | |
| Люди не просто так используют языки программирования: они выразительны, кратки и точны. | |
| Мы могли бы предоставить исходный код Python в качестве _спецификации_ инструмента для LLM, но способ реализации инструмента не имеет значения. Важно лишь его название, то, что он делает, какие входные данные он ожидает и какие выходные данные он предоставляет. | |
| Мы воспользуемся возможностями интроспекции Python, чтобы изучить исходный код и автоматически составить описание инструмента. Все, что нам нужно, - это чтобы реализация инструмента использовала подсказки типов, строки документации и разумные имена функций. Мы напишем код для извлечения нужных фрагментов из исходного кода. | |
| После этого нам останется только использовать декоратор Python, чтобы указать, что функция `calculator` является инструментом: | |
| ```python | |
| @tool | |
| def calculator(a: int, b: int) -> int: | |
| """Multiply two integers.""" | |
| return a * b | |
| print(calculator.to_string()) | |
| ``` | |
| Обратите внимание на декоратор `@tool` перед определением функции. | |
| С помощью реализации, которую мы рассмотрим далее, мы сможем автоматически извлекать следующий текст из исходного кода с помощью функции `to_string()`, предоставляемой декоратором: | |
| ```text | |
| Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int | |
| ``` | |
| Как видите, это то же самое, что мы уже писали вручную! | |
| ### Универсальная реализация Инструмента | |
| Мы создаем общий класс `Tool`, который мы можем использовать каждый раз, когда нам нужно использовать инструмент. | |
| > **Отказ от ответственности:** Этот пример реализации является вымышленным, но очень похож на реальные реализации в большинстве библиотек. | |
| ```python | |
| class Tool: | |
| """ | |
| Класс, представляющий многократно используемый фрагмент кода (инструмент). | |
| Атрибуты: | |
| name (str): Имя инструмента. | |
| description (str): Текстовое описание того, что делает инструмент. | |
| func (вызываемый): Функция, которую оборачивает этот инструмент. | |
| arguments (список): Список аргументов. | |
| outputs (str или list): Возвращаемые обернутой функцией типы. | |
| """ | |
| def __init__(self, | |
| name: str, | |
| description: str, | |
| func: callable, | |
| arguments: list, | |
| outputs: str): | |
| self.name = name | |
| self.description = description | |
| self.func = func | |
| self.arguments = arguments | |
| self.outputs = outputs | |
| def to_string(self) -> str: | |
| """ | |
| Возвращает строковое представление инструмента, | |
| включая его название, описание, аргументы и выходные данные. | |
| """ | |
| args_str = ", ".join([ | |
| f"{arg_name}: {arg_type}" for arg_name, arg_type in self.arguments | |
| ]) | |
| return ( | |
| f"Tool Name: {self.name}," | |
| f" Description: {self.description}," | |
| f" Arguments: {args_str}," | |
| f" Outputs: {self.outputs}" | |
| ) | |
| def __call__(self, *args, **kwargs): | |
| """ | |
| Вызов базовой функции (вызываемой) с указанными аргументами. | |
| """ | |
| return self.func(*args, **kwargs) | |
| ``` | |
| Это может показаться сложным, но если мы медленно пройдемся по нему, то сможем понять, что он делает. Мы определяем класс **`Tool`**, который включает в себя: | |
| - **`name`** (*str*): Название инструмента. | |
| - **`description`** (*str*): Краткое описание того, что делает инструмент. | |
| - **`function`** (*callable*): Функция, которую выполняет инструмент. | |
| - **`arguments`** (*list*): Ожидаемые входные параметры. | |
| - **`outputs`** (*str* или *list*): Ожидаемые выходные данные инструмента. | |
| - **`__call__()`**: Вызывает функцию при вызове экземпляра инструмента. | |
| - **`to_string()`**: Преобразует атрибуты инструмента в текстовое представление. | |
| Мы можем создать инструмент с помощью этого класса, используя следующий код: | |
| ```python | |
| calculator_tool = Tool( | |
| "calculator", # имя | |
| "Multiply two integers.", # описание | |
| calculator, # функция для вызова | |
| [("a", "int"), ("b", "int")], # водные данные (имена и типы) | |
| "int", # выходные данные | |
| ) | |
| ``` | |
| Но мы также можем использовать модуль Python `inspect`, чтобы получить всю информацию за нас! Вот что делает декоратор `@tool`. | |
| > Если вам интересно, вы можете посмотреть на реализацию декоратора в следующем разделе. | |
| код декоратора | |
| ```python | |
| def tool(func): | |
| """ | |
| Декоратор, создающий экземпляр Tool из заданной функции. | |
| """ | |
| # Получение сигнатуры функции | |
| signature = inspect.signature(func) | |
| # Извлеките пары (param_name, param_annotation) для входных данных | |
| arguments = [] | |
| for param in signature.parameters.values(): | |
| annotation_name = ( | |
| param.annotation.__name__ | |
| if hasattr(param.annotation, '__name__') | |
| else str(param.annotation) | |
| ) | |
| arguments.append((param.name, annotation_name)) | |
| # Определите аннотацию возврата | |
| return_annotation = signature.return_annotation | |
| if return_annotation is inspect._empty: | |
| outputs = "No return annotation" | |
| else: | |
| outputs = ( | |
| return_annotation.__name__ | |
| if hasattr(return_annotation, '__name__') | |
| else str(return_annotation) | |
| ) | |
| # Используйте строку документации функции в качестве описания (по умолчанию, если None) | |
| description = func.__doc__ or "Описание не представлено." | |
| # Имя функции становится именем Инструмента | |
| name = func.__name__ | |
| # Возвращаем новый экземпляр Инструмента | |
| return Tool( | |
| name=name, | |
| description=description, | |
| func=func, | |
| arguments=arguments, | |
| outputs=outputs | |
| ) | |
| ``` | |
| Повторимся, что с этим декоратором мы можем реализовать наш инструмент следующим образом: | |
| ```python | |
| @tool | |
| def calculator(a: int, b: int) -> int: | |
| """Multiply two integers.""" | |
| return a * b | |
| print(calculator.to_string()) | |
| ``` | |
| И мы можем использовать метод `Tool` `to_string` для автоматического получения текста, подходящего для использования в качестве описания инструмента для LLM: | |
| ```text | |
| Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int | |
| ``` | |
| Описание **вставляется** в системную подсказку. Если взять пример, с которого мы начали этот раздел, то вот как он будет выглядеть после замены `tools_description`: | |
| В разделе [Действия](actions) мы узнаем, как агент может **вызвать** инструмент, который мы только что создали. | |
| --- | |
| Инструменты играют решающую роль в расширении возможностей AI агентов. | |
| Подводя итоги, мы узнали: | |
| - *Что такое инструменты*: Функции, которые предоставляют LLM дополнительные возможности, такие как выполнение вычислений или доступ к внешним данным. | |
| - *Как определить инструмент*: Предоставить четкое текстовое описание, входы, выходы и вызываемую функцию. | |
| - *Почему инструменты необходимы*: Они позволяют агентам преодолевать ограничения статического обучения модели, решать задачи в реальном времени и выполнять специализированные действия. | |
| Теперь мы можем перейти к [Рабочему процессу Агента](agent-steps-and-structure), где вы увидите, как Агент наблюдает, думает и действует. Это **собирает воедино все, что мы изучили до сих пор**, и закладывает основу для создания вашего собственного полнофункционального AI Агента. | |
| Но сначала - еще один короткий тест! | |
Xet Storage Details
- Size:
- 21.4 kB
- Xet hash:
- bdaaa82189d0fa14bbc20b20cab2938c5849e223d23e9d10090ede500d947dd2
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.