1. От ИИ к агентным приложениям
Эта глава охватывает
- Основы ИИ
- ИИ-агенты и агентные приложения
- Первый контакт с API ИИ и ИИ-агентами
Каждое приложение станет агентным приложением. От кодирующих агентов, работающих локально в вашей IDE или терминале, до корпоративных агентов, которые организуют действия в распределенных средах, агентное приложение — это приложение, которое работает автономно и непрерывно в стремлении к цели (Рисунок 1.1).
Рисунок 1.1: Агентное приложение действует автономно и непрерывно в стремлении к цели
Агентные приложения состоят из ИИ-агентов, а ИИ-агенты состоят из API ИИ. Чтобы эффективно проектировать эти системы, мы должны понимать их структуру и поведение снизу вверх. В этой главе мы начинаем с нижних уровней, чтобы понять, как токены, модели, обучение и вывод ограничивают то, что происходит на более высоких уровнях.
1.1 Основы ИИ
Трансформация традиционных приложений в агентные приложения происходит благодаря большим языковым моделям (LLM). В отличие от предыдущих технологий ИИ, которые являются узкими и специфичными для домена, LLM являются широкими и универсальными, способными рассуждать о целях и организовывать сложные действия. Следовательно, на протяжении всей этой книги мы будем изучать мир API ИИ, агентов и агентных приложений в первую очередь через призму LLM.
Хотя мы используем конкретные примеры от различных поставщиков ИИ, мы фокусируемся на построении концептуальной основы, которая охватывает существенное поведение, общее для разных систем. Механика может отличаться, но паттерны более высокого уровня остаются согласованными и обеспечивают надежные основы для проектирования агентных приложений
LLM построены из четырех фундаментальных компонентов: токенов, моделей, обучения и вывода (см. Рисунок 1.2).
Рисунок 1.2: Конвейер больших языковых моделей, показывающий связь между токенами, моделями, обучением и выводом
Системные инженеры не реализуют эти низкоуровневые компоненты, но понимание этих основ, даже на концептуальном уровне, необходимо для создания надежных и масштабируемых агентных приложений.
1.1.1 Токены
LLM работают с текстом: они обучаются на тексте, получают текст в качестве входных данных и возвращают текст в качестве выходных данных. Однако LLM обрабатывают текст иначе, чем люди. Люди разделяют текст на символы или на слова (см. Рисунок 1.3).
Рисунок 1.3: Текст, разделенный на символы или слова людьми для людей.
LLM вместо этого разделяют текст на токены, то есть числовые идентификаторы для фрагментов текста (см. Рисунок 1.4).
Рисунок 1.4: Текст, разделенный на токены токенизатором GPT-4o и их числовые значения.
Различные токенизаторы присваивают различные числовые значения фрагментам текста. Для наших целей мы абстрагируем токенизацию до ее основного интерфейса (см. Листинг 1.1):
// Токен - это числовое представление фрагмента текста
type Token = number
interface Tokenizer {
// Абстрактная функция для представления перевода текста в токены
function encode(String) : Token[]
// Абстрактная функция для представления перевода токенов в текст
function decode(Token[]) : String
}
Листинг 1.1: Абстрактное представление токенизатора как интерфейса с функциями encode и decode
Токенизатор поддерживает отображение токенов на связанные с ними текстовые фрагменты. Кроме того, он может определять специальные токены или управляющие токены (аналогично управляющим символам, таким как возврат каретки в ASCII или Unicode). Набор всех токенов также называется алфавитом или словарем.
1.1.2 Модели
Модели являются публичным лицом ИИ. Новые релизы от OpenAI, Anthropic и других поставщиков встречаются с большим ожиданием, широко обсуждаются, хвалятся и критикуются. Сегодня релиз - это культурное событие, демонстрации становятся вирусными, а анекдоты об удивительном или разочаровывающем поведении быстро распространяются.
Под шумихой модели на удивление обыденны: упорядоченный набор параметров (см. Листинг 1.2).
// Тип для представления параметра модели
type Param = number;
// Тип для представления модели
type Model = Param[];
// Длина контекстного окна
function length(model : Model) : number
Листинг 1.2: Представление модели как массива параметров
У ра зных поставщиков LLM характеризуются двумя свойствами:
Количество параметров: Количество параметров, которые модель может изучить во время обучения. Это означает, сколько информации модель может хранить в целом. Текущие модели варьируются от миллиардов до триллионов параметров.
Контекстное окно: Количество токенов, которые модель может обработать во время вывода. Это означает, сколько информации модель может рассмотреть за раз. Текущие модели варьируются от десятков тысяч до более 2 миллионов токенов.
По сути, эти параметры формируют гигантскую таблицу поиска. Для любой последовательности токенов до контекстного окна модель производит вектор вероятности, который присваивает каждому токену в словаре вероятность быть следующим. Это единственная функция модели: учитывая контекст, предсказать следующий токен.
Миллиарды параметров кодируют паттерны, изученные из данных обучения. Эти паттерны охватывают все, от базовых грамматических правил до сложных стратегий рассуждения, фактических знаний и стилистических предпочтений. Вместе они определяют как возможности модели, так и ее ограничения.
Используя формализм, такой как TLA+, Временная логика действий, мы можем формализовать модель как:
# Словарь большой языковой модели
TOKENS == { ... }
# Длина контекстного окна
LENGTH == 1000000
# Модель отображает каждую последовательность токенов на вероятность для каждого токена
MODELS ==
[ { s ∈ Seq(TOKENS) : Len(s) ≤ LENGTH } → [TOKENS → [0.0 .. 1.0]] ]
Модели шокируют и вызывают благоговение через огромные ресурсы, которые они требуют во время обучения, и возможности, которые они демонстрируют во время вывода.
1.1.3 Обучение
Обучение - это функция создания или обновления модели, или более конкретно параметров модели, на основе набора данных (см. Листинг 1.3).
// Переменная для представления начальной, пустой или исходной модели
const init: Model = [];
// Абстрактная функция для представления обучения
function train(model: Model, dataset: Set<Token[]>): Model
Листинг 1.3: Сигнатура абстрактной функции обучения
Существует два варианта обучения:
-
Обучение с нуля: Изучение параметров модели, начиная с пустой модели. Требует много данных для обучения, вычислительных ресурсов и времени.
-
Обучение на основе базовой модели (тонкая настройка): Изучение параметров модели, начиная с базовой модели. Требует меньше данных для обучения, вычислительных ресурсов и времени.
Мы могли бы моделировать создание и обновление модели как две разные функции. Однако, представляя обе как одну функцию, мы можем уменьшить нашу когнитивную нагруз ку и можем установить связь между моделями, все основанные на исходной модели (см. Рисунок 1.5).
Рисунок 1.5: Отношения моделей, показывающие, как все модели происходят от начальной исходной модели через обучение
Вы можете думать об обучении как о недетерминированном выборе модели в пространстве моделей. Здесь недетерминированный выбор полностью абстрагируется от любой механики обучения.
# Мы абстрагируем обучение недетерминированным выбором модели в
# пространстве моделей
train(dataset) ==
CHOOSE model ∈ MODELS : TRUE
Из-за значительных требований к ресурсам обучение с нуля возможно только для лабораторий ИИ, в то время как тонкая настройка возможна для многих команд.
1.1.4 Вывод
Вывод - это функция применения модели к последовательности токенов для получения следующего токена (см. Листинг 1.4).
function infer(model : Model, context : Token[]) : Token
Листинг 1.4: Сигнатура абстрактной функции вывода
Модели являются детерминированными математическими функциями: при одинаковых входных данных они всегда производят одинаковое распределение вероятностей. Однако вместо того, чтобы всегда выбирать токен с наивысшей вероятностью, вывод производит выборку из распределения вероятностей, используя стратегии, такие как top-k выборка. Эта контролируемая случайность делает выходные данные разнообразными и кажущимися творческими. Например, при запросе "Столица Франции это", вывод может (итеративно) дать либо "Париж", либо "город Париж".
Выборка не является действительно случайной. Выборка опирается на генераторы псевдослучайных чисел, инстанцированные с начальным значением. При одинаковом начальном значении и одинаковом контексте вывод производит идентичные результаты каждый раз. Однако этот параметр начального значения часто не выставляется в API интерфейсах, поэтому мы должны думать о выводе как о случайном по умолчанию.
infer выбирает top-k токены, то есть токены с наибольшей вероятностью, и делает недетерминированный выбор
# Мы абстрагируемся над выводом, недетерминированно выбирая
# следующий токен из 10 наиболее вероятных токенов с учетом контекста
Infer(model, context) ==
CHOOSE token ∈ TOPK(model[context], 10)
1.2 Модели и API ИИ
Основные компоненты, токены, модели, обучение и вывод, объединяются для создания практических API ИИ. Итерируя вывод, предсказывая по одному токену за раз, мы преобразуем распределения вероятностей в связный текст. Различные подходы к обучению дают различные возможности API. Мы рассмотрим три различных типа моделей:
- Модели завершения (также называемые базовыми моделями)
- Разговорные модели (также называемые чат-моделями)
- Модели с вызовом инструментов
Рисунок 1.6: Типы моделей и их отношения обучения/тонкой настройки
1.2.1 Модели завершения
Модели завершения обучаются завершать текст. Их данные обучения состоят из последовательностей токенов, обернутых в специальные граничные маркеры:
- BOS—Начало последовательности. Отмечает начало последовательности токенов.
- EOS—Конец последовательности. Отмечает конец последовательности токенов.
BOS и EOS являются важными компонентами обучения и позволяют модели кодировать начало и конец последовательностей. Так модели учатся генерировать полные, ограниченные ответы, а не продолжать бесконечно.
<BOS>
Столица Франции - Париж.
<EOS>
Модели завершения завершают текст, итеративно генерируя следующий токен, пока мы не достигнем стоп-токена (см. Листинг 1.5)
// Предполагает глобальный токенизатор со специальными токенами BOS и EOS
function generate(model: Model, promptTokens: Token[]): Token[] {
const answerTokens: Token[] = [];
while (true) {
const next = infer(model, [
tokenizer.BOS,
...promptTokens,
...answerTokens
]);
if (next == tokenizer.EOS) {
break;
}
answerTokens.push(next);
}
return answerTokens;
}
function complete(model: Model, prompt: string): string {
const promptTokens: Token[] = tokenizer.encode(prompt);
const answerTokens: Token[] = generate(model, promptTokens);
return tokenizer.decode(answerTokens);
}
Листинг 1.5: Функции генерации токенов и завершения текста для базовых моделей
Вы можете думать об этом типе модели и ее API генерации как о машине завершения предложений: при заданном запросе API генерирует завершение запроса.
запрос: Столица Франции
ответ: это Париж
Модели завершения были первыми доступными LLM. Хотя они ограничены по сравнению с сегодняшними разговорными моделями и моделями с вызовом инструментов, они остаются концептуальной основой: каждое взаимодействие все еще сводится к итеративному предсказанию токенов.
1.2.2 Разговорные модели
Разговорные модели - это модели завершения, тонко настроенные для завершения разговора при следовании инструкциям. Их данные обучения добавляют маркеры ролей для различения между системными инструкциями и участниками:
<BOS>
<|BOT role=system|>
Ты полезный помощник.
<|EOT|>
<|BOT role=user|>
Какая столица Франции?
<|EOT|>
<|BOT role=assistant|>
Столица Франции - Париж.
<|EOT|>
<EOS>
Маркеры ролей представляют собой решающий прогресс: появление структурированного протокола. Модель учится не просто завершать текст, а участвовать в многоходовом разговоре, поддерживая контекст между говорящими и следуя инструкциям системного уровня.
Как и модели завершения, разговорные модели генерируют токены итеративно, пока мы не достигнем стоп-токена (см. Листинг 1.6):
type Turn = {
role: "SYSTEM" | "USER" | "ASSISTANT"
text: string
}
function converse(model : Model, prompt: Turn[]) : Turn {
const promptTokens: Token[] = prompt.flatMap(turn => [
tokenizer.BOT(turn.role),
...tokenizer.encode(turn.text),
tokenizer.EOT()
]);
const answerTokens: Token[] = generate(model, promptTokens)
// Разбирает текст ответа для извлечения хода помощника
return Turn.parse(tokenizer.decode(answerTokens))
}
Листинг 1.6: Функция разговора для чат-моделей с ходами на основе ролей
Вы можете думать об этом типе модели и ее API генерации как о машине завершения разговоров: при заданном запросе API генерирует завершение ответа.
запрос: Какая столица Франции?
ответ: Столица Франции - Париж.
Хотя разговорные модели могут взаимодействовать с пользователями, они не могут взаимодействовать с окружающей средой. Модели с вызовом инструментов устраняют этот разрыв.