Перейти к основному содержимому

1. От ИИ к агентным приложениям

Эта глава охватывает

  • Основы ИИ
  • ИИ-агенты и агентные приложения
  • Первый контакт с API ИИ и ИИ-агентами

Каждое приложение станет агентным приложением. От кодирующих агентов, работающих локально в вашей IDE или терминале, до корпоративных агентов, которые организуют действия в распределенных средах, агентное приложение — это приложение, которое работает автономно и непрерывно в стремлении к цели (Рисунок 1.1).


Figure 1.1

Рисунок 1.1: Агентное приложение действует автономно и непрерывно в стремлении к цели


Агентные приложения состоят из ИИ-агентов, а ИИ-агенты состоят из API ИИ. Чтобы эффективно проектировать эти системы, мы должны понимать их структуру и поведение снизу вверх. В этой главе мы начинаем с нижних уровней, чтобы понять, как токены, модели, обучение и вывод ограничивают то, что происходит на более высоких уровнях.

1.1 Основы ИИ

Трансформация традиционных приложений в агентные приложения происходит благодаря большим языковым моделям (LLM). В отличие от предыдущих технологий ИИ, которые являются узкими и специфичными для домена, LLM являются широкими и универсальными, способными рассуждать о целях и организовывать сложные действия. Следовательно, на протяжении всей этой книги мы будем изучать мир API ИИ, агентов и агентных приложений в первую очередь через призму LLM.

Концептуальная основа

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

LLM построены из четырех фундаментальных компонентов: токенов, моделей, обучения и вывода (см. Рисунок 1.2).


Figure 1.2

Рисунок 1.2: Конвейер больших языковых моделей, показывающий связь между токенами, моделями, обучением и выводом


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

1.1.1 Токены

LLM работают с текстом: они обучаются на тексте, получают текст в качестве входных данных и возвращают текст в качестве выходных данных. Однако LLM обрабатывают текст иначе, чем люди. Люди разделяют текст на символы или на слова (см. Рисунок 1.3).


Figure 1.3

Рисунок 1.3: Текст, разделенный на символы или слова людьми для людей.


LLM вместо этого разделяют текст на токены, то есть числовые идентификаторы для фрагментов текста (см. Рисунок 1.4).


Figure 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. Обучение с нуля: Изучение параметров модели, начиная с пустой модели. Требует много данных для обучения, вычислительных ресурсов и времени.

  2. Обучение на основе базовой модели (тонкая настройка): Изучение параметров модели, начиная с базовой модели. Требует меньше данных для обучения, вычислительных ресурсов и времени.

Выбор моделирования

Мы могли бы моделировать создание и обновление модели как две разные функции. Однако, представляя обе как одну функцию, мы можем уменьшить нашу когнитивную нагрузку и можем установить связь между моделями, все основанные на исходной модели (см. Рисунок 1.5).


Figure 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. Мы рассмотрим три различных типа моделей:

  • Модели завершения (также называемые базовыми моделями)
  • Разговорные модели (также называемые чат-моделями)
  • Модели с вызовом инструментов

Figure 1.6

Рисунок 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 генерирует завершение ответа.

запрос: Какая столица Франции?
ответ: Столица Франции - Париж.

Хотя разговорные модели могут взаимодействовать с пользователями, они не могут взаимодействовать с окружающей средой. Модели с вызовом инструментов устраняют этот разрыв.

1.2.3 Модели с вызовом инструментов

Модели с вызовом инструментов расширяют разговорные модели возможностью вызывать внешние функции. Их данные обучения включают определения инструментов в системном запросе и новую роль для ответов инструментов:

<BOS>
<|BOT role=system|>
Ты полезный помощник. Ты можешь вызывать инструменты:
- getWeather(location: string): возвращает текущую погоду.
<|EOT|>
<|BOT role=user|>
Какая погода в Париже?
<|EOT|>
<|BOT role=assistant|>
tool:getWeather("Paris")
<|EOT|>
<|BOT role=tool|>
28C солнечно
<|EOT|>
<|BOT role=assistant|>
Текущая погода в Париже 28C и солнечно.
<|EOT|>
<EOS>

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

Вы можете думать об этом типе модели и ее API генерации как о машине завершения разговоров с доступом к инструментам для наблюдений или запуска действий:

запрос: Какая погода в Париже?
ответ: tool:getWeather("Paris").
запрос: 28C солнечно
ответ: Текущая погода в Париже 28C и солнечно.

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

1.3 ИИ-агенты

Агенты добавляют оркестрацию и управление состоянием к генерации: в отличие от безсостоятельных, одноходовых API ИИ, агенты являются состоятельными, многоходовыми компонентами, способными постоянно преследовать цель.

1.3.1 Агент

Мы определяем агента A как кортеж модели M, набора инструментов T и системного запроса s:

A = (M, T, s)

Мы определяем экземпляр агента Ai (также называемый сессией или разговором) с идентификатором i как кортеж модели M, инструментов T, системного запроса s и истории h:

Ai = (M, T, s, h)

История h преобразует абстрактное определение агента в экземпляр агента или выполнение. История структурирована как последовательность обменов:

h = [(u₁, a₁), (u₂, a₂), (u₃, a₃), (u₄, a₄), ...]

где u представляет сообщение пользователя, а a представляет ответ агента.

Когда вовлечен вызов инструментов, история расширяется, включая вызовы инструментов (t) и их результаты (r):

h = [(u₁, a₁), (u₂, t₁), (r₁, a₂), (u₃, a₃), ...]

1.3.2 Цикл агента

ИИ-агенты структурированы вокруг центрального цикла оркестрации, который координирует между API ИИ, пользователем и инструментами, управляя состоянием разговора. Этот цикл агента представляет основную инженерную задачу в агентных приложениях. Цикл отвечает за управление состоянием, координацию асинхронных, долго выполняющихся операций и обработку восстановления в случае сбоя.

1.4 Агентные приложения

Агентные приложения варьируются от систем с одним агентом до мультиагентных систем, где агенты координируются с другими агентами. В мультиагентных системах агенты могут вызывать других агентов, создавая динамические графы вызовов (см. Рисунок 1.7).


Figure 1.7

Рисунок 1.7: Мультиагентные системы формируют динамические графы вызовов с агентами, вызывающими других агентов и инструменты


1.5 Первый контакт

Установив основы, давайте взаимодействовать с реальным API ИИ. Мы в основном будем использовать OpenAI для примеров, хотя паттерны применимы к другим поставщикам.

1.5.1 Базовый API

Листинг 1.7 иллюстрирует самое базовое взаимодействие с API. Мы предоставляем желаемую модель, контекст, то есть историю разговора и текущий запрос, и запрашиваем завершение.

import OpenAI from "openai";

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});

async function main() {
const completion = await openai.chat.completions.create({
model: "gpt-5",
messages: [{
role: "user", content: "Какая столица Франции?"
}]
});

console.log(completion);
}

main();

Листинг 1.7: Базовое взаимодействие с OpenAI API

API возвращает структурированный ответ (Листинг 1.8):

{
"id": "chatcmpl-C5rNhjKYS8nYoXdnZmTjK5T2FEsVX",
"object": "chat.completion",
"model": "gpt-5-2025-08-07",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Париж.",
"refusal": null,
"annotations": []
},
"finish_reason": "stop"
}
],
"usage": {
"total_tokens": 23,
"prompt_tokens": 12,
"completion_tokens": 11
}
}

Листинг 1.8: Ответ помощника

Однако в большинстве случаев в этой книге нас просто интересует ответ ИИ (Листинг 1.9)

const answer : string? = completion.choices[0]?.message?.content;

Листинг 1.9: Извлечение содержимого ответа помощника

1.5.2 Потоковый API

Многие API ИИ предлагают два режима работы: пакетный (возвращающий ответ сразу) и потоковый (возвращающий ответ постепенно, токен за токеном). Потоковый режим потенциально может улучшить пользовательский опыт: вместо ожидания пользователи видят формирующийся ответ в реальном времени, создавая естественное, разговорное ощущение и уменьшая воспринимаемую задержку (см. Листинг 1.10).

import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function main() {
const stream = await openai.chat.completions.create({
model: "gpt-4",
messages: [{
role: "user", content: "Расскажи мне о Париже"
}],
stream: true,
});

let answer = "";

for await (const chunk of stream) {
const content = chunk.choices?.[0]?.delta?.content;
if (content) {
process.stdout.write(content);
answer += content;
}
}

}

main();

Листинг 1.10: Потоковый API для ответов токен за токеном в реальном времени

Потоковая передача сопряжена с проблемами:

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

1.5.3 Вызов инструментов

Вызов инструментов расширяет разговорные модели возможностью вызывать внешние функции, позволяя ИИ взаимодействовать с миром за пределами генерации текста через структурированные вызовы функций (см. Листинг 1.11).

import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const tools = [
{
type: "function",
function: {
name: "get_current_weather",
description: "Получить текущую погоду в заданном месте",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description:
"Город, штат и страна, например, Берлин, Германия или Сан-Франциско, CA, США",
},
},
required: ["location"],
},
},
},
];

async function main() {
const completion = await openai.chat.completions.create({
model: "gpt-5",
messages: [
{
role: "user",
content: "Какая сейчас погода в Париже",
},
],
tools: tools,
});

console.log(JSON.stringify(completion));
}

main();

Листинг 1.11: API вызова инструментов

API возвращает ответ, содержащий вызов инструмента (Листинг 1.12):

{
"id": "chatcmpl-C76JQRfuc9HXpErPldbVyRuieZ8Lm",
"object": "chat.completion",
"created": 1755808596,
"model": "gpt-5-2025-08-07",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_hQF9XaYtq8ZO6LJl6S3WctoU",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\"location\":\"Париж, Франция\"}"
}
}
],
"refusal": null,
"annotations": []
},
"finish_reason": "tool_calls"
}
],
}

Листинг 1.12: Ответ помощника

Вызов инструментов сопряжен с проблемами:

  • API не выполняет инструменты напрямую. Вместо этого API возвращает структурированное представление предполагаемого вызова. Вызывающее приложение должно выполнить инструмент и вернуть результаты.

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

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

1.5.4 Простой агент

Листинг 1.13 демонстрирует переход от API ИИ к ИИ-агенту. В то время как предыдущие примеры показывали изолированные, одноходовые взаимодействия, где каждый вызов API существовал независимо, эта реализация показывает, как обертывание API ИИ в постоянный цикл преобразует его в разговорного агента. Ключевое понимание - память - поддерживая историю разговора между взаимодействиями, мы преобразуем безсостоятельные вызовы API в состоятельный диалог.

import OpenAI from "openai";
// peripherals.ts предоставляет простые утилиты консольного ввода/вывода
import { getUserInput, closeUserInput } from "./peripherals";

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});

async function main() {
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [{
role: "system", content: "Завершите свой финальный ответ с <EXIT>.",
}];

while (true) {
let prompt = await getUserInput(
"Пользователь (введите 'exit' для выхода):",
);

if (prompt.toLowerCase() === "exit") {
break;
}

messages.push({role: "user", content: prompt});

const completion = await openai.chat.completions.create({
model: "gpt-4",
messages: messages
});

const answer = completion.choices[0]?.message?.content;

messages.push({role: "assistant", content: answer});

console.log("Помощник:", answer);

if (answer.includes("<EXIT>")) {
break;
}
}

closeUserInput();
}

main();

Листинг 1.13: Простой разговорный агент с взаимодействием на основе цикла

Эта минимальная реализация раскрывает основную архитектуру, которая лежит в основе всех ИИ-агентов. Каждый агент должен решать фундаментальные проблемы:

  • Управление состоянием: Поддержание истории разговора между взаимодействиями. Здесь массив messages накапливает диалог, преобразуя безсостоятельные вызовы API в состоятельный разговор.

  • Управление идентичностью: Поддержание уникальной идентичности агента. Здесь управление идентичностью состоит только из опоры на запущенный процесс.

  • Управление жизненным циклом: Обработка инициализации, выполнения, приостановки, возобновления и завершения. Здесь управление жизненным циклом состоит только из базовых условий завершения (пользователь "exit", ИИ <EXIT>).

Хотя наш простой агент функционирует правильно, он выявляет фундаментальные проблемы, которые становятся критическими в масштабе. Агент проводит большую часть времени в простое, блокируясь на getUserInput(). Более критично, эта тесная связь между процессом и агентом создает хрупкость — если процесс падает, завершается или требует перезапуска, весь экземпляр агента исчезает, забирая с собой весь контекст разговора.

1.5.5 К постоянным агентам

Фундаментальный недостаток нашей простой архитектуры агента - привязка экземпляра агента к экземпляру процесса. Эта связь создает непреодолимые проблемы в производственных системах:

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

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

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

Листинг 1.14 представляет грубую попытку решить эти проблемы через файловую постоянность:

import OpenAI from "openai";
import fs from "fs/promises";
import path from "path";

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});

const SYSTEM = "Завершите свой финальный ответ символом <EXIT>.";

interface ConversationData {
messages: OpenAI.Chat.ChatCompletionMessageParam[];
}

async function loadConversation(
identifier: string,
): Promise<OpenAI.Chat.ChatCompletionMessageParam[]> {
const filePath = path.join(process.cwd(), `${identifier}.json`);

try {
const data = await fs.readFile(filePath, "utf-8");
const conversation: ConversationData = JSON.parse(data);
return conversation.messages;
} catch (error) {
// Файл не существует, возвращаем новый разговор с системным сообщением
return [
{
role: "system",
content: SYSTEM,
},
];
}
}

async function saveConversation(
identifier: string,
messages: OpenAI.Chat.ChatCompletionMessageParam[],
): Promise<void> {
const filePath = path.join(process.cwd(), `${identifier}.json`);
const conversation: ConversationData = { messages };
await fs.writeFile(filePath, JSON.stringify(conversation, null, 2));
}

async function main() {
// Разбор аргументов командной строки
const args = process.argv.slice(2);

if (args.length < 2) {
console.error("Использование: ts-node index-4.ts <идентификатор> <запрос>");
process.exit(1);
}

const identifier = args[0];
const prompt = args.slice(1).join(" ");

try {
// Загрузить существующий разговор или создать новый
const messages = await loadConversation(identifier);

// Добавить сообщение пользователя
messages.push({role: "user", content: prompt});

// Получить завершение от OpenAI
const completion = await openai.chat.completions.create({
model: "gpt-5",
messages: messages
});

const answer = completion.choices[0]?.message?.content;

if (answer) {
// Добавить ответ помощника
messages.push({role: "assistant", content: answer});

// Сохранить разговор
await saveConversation(identifier, messages);

// Вывести ответ
console.log("Помощник:", answer);
} else {
console.error("Нет ответа от OpenAI");
}
} catch (error) {
console.error("Произошла ошибка:", error);
process.exit(1);
}
}

// Запустить главную функцию
main();

Листинг 1.14: Постоянное состояние разговора с использованием файлового хранилища

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

1.6 Резюме

  • Токен - это числовой идентификатор для фрагмента текста.
  • Токенизатор поддерживает двунаправленные отображения между токенами и текстовыми фрагментами.
  • Модель - это упорядоченный набор параметров, который присваивает вероятности токенам с учетом контекста.
  • Модели характеризуются количеством параметров (емкость хранения информации) и контекстным окном (емкость обработки информации).
  • Обучение создает или обновляет параметры модели из наборов данных, либо с нуля, либо через тонкую настройку.
  • Вывод применяет модель для предсказания следующего токена, используя контролируемую случайность для разнообразных выходных данных.
  • Модели завершения генерируют продолжения текста из запросов.
  • Разговорные модели добавляют структуру на основе ролей для поддержания многоходового разговора.
  • Модели с вызовом инструментов генерируют структурированные вызовы функций, но не выполняют их напрямую.
  • Агенты объединяют модели, инструменты и системные запросы с постоянным управлением состоянием.
  • Экземпляры агентов поддерживают историю разговора для преобразования безсостоятельных API в состоятельные системы.
  • Создание производственных агентов требует сложной инфраструктуры для управления идентичностью, постоянства состояния и оркестрации процессов.