← Все статьи

400 строк, после которых агенты перестают быть магией

Гайд Торстена Балла «How to Build an Agent» мне советовали, наверное, раз десять. Обычно с формулировкой «прочитай, там всё просто». Я откладывал: ну что нового мне расскажет статья, если я и так каждый день работаю с агентами? В итоге сел и реализовал описанное в статье локально, в редакторе, на Go, как в оригинале. Ниже — мой опыт: что там на самом деле, что работает, а что нет, и стоит ли тратить на это вечер.

Что в статье

Тезис автора: агент, редактирующий код, — это «LLM, цикл и достаточное количество токенов». Никакого фреймворка, никакого графа состояний, меньше 400 строк.

Структура повествования простая и хорошая: сначала простой чат в терминале (~90 строк), потом инфраструктура для инструментов, потом три основных инструмента — read_file, list_files, edit_file. Всё.

Ядро выглядит максимально просто — цикл, в котором мы гоняем историю сообщений и разбираем ответ модели:

for {
    message, err := a.runInference(ctx, conversation)
    if err != nil {
        return err
    }
    conversation = append(conversation, message.ToParam())
 
    toolResults := []anthropic.ContentBlockParamUnion{}
    for _, content := range message.Content {
        switch content.Type {
        case "text":
            fmt.Printf("Claude: %s\n", content.Text)
        case "tool_use":
            result := a.executeTool(content.ID, content.Name, content.Input)
            toolResults = append(toolResults, result)
        }
    }
    if len(toolResults) == 0 {
        // модель ничего не делает — отдаём ввод пользователю
        ...
    }
    conversation = append(conversation, anthropic.NewUserMessage(toolResults...))
}

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

type ToolDefinition struct {
    Name        string
    Description string
    InputSchema anthropic.ToolInputSchemaParam
    Function    func(input json.RawMessage) (string, error)
}

А самый «мощный» инструмент из трёх — edit_file — внутри это буквально strings.Replace по old_strnew_str плюс создание файла, если его нет. Всё редактирование кода, ради которого мы здесь собрались, — одна замена подстроки.

Момент, ради которого это стоит пройти

Он наступает не на строке 400, а примерно на 250-й. Ты дописываешь read_file, запускаешь, спрашиваешь у модели что-то про файл в папке — и она сама, без единой инструкции в системном промпте, решает вызвать инструмент, читает файл и отвечает. Ты нигде не писал «если пользователь спросил про файл, то...». Ты просто описал инструмент словами.

Именно здесь до меня дошло, что самостоятельность агента живёт не в коде вокруг модели, а в самой модели: она обучена вызывать инструменты и делать это, когда ей необходимо. Дальше был второй пример — задачка про ROT13 в файле, где модель сама прошлась по файлам, написала скрипт-декодер и предложила его запустить. И вот тут окончательно понимаешь, что за сложным поведением модели, например, в Claude Code стоит тот же самый цикл, который ты только что написал руками.

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

Что сломалось и что пришлось дописывать

Теперь проблемы с которыми я столкнулся по ходу прочтения и реализации кода из статьи.

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

Агент правит файлы, ничего не спрашивая. В гайде edit_file вызывается и просто применяется. Довольно быстро я поймал себя на том, что мне не хочется давать процессу неограниченный доступ на запись в рабочую папку. Это, кстати, не только моё ощущение: Кевин Янк, переписавший гайд на TypeScript, сделал ровно то же самое — добавил запрос на подтверждение от человека. Пять строк, которых в оригинале не хватает:

func (a *Agent) confirm(name string, input json.RawMessage) bool {
    fmt.Printf("run: %s(%s) [y/N]: ", name, string(input))
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return strings.TrimSpace(strings.ToLower(scanner.Text())) == "y"
}

strings.Replace — это не редактор. На тестовом файле fizzbuzz.js всё прекрасно. На реальном файле модель регулярно ошибается: подставляет old_str, который встречается дважды, или отличается от файла одним пробелом/табом. Замена либо применяется не туда, либо просто ломает всего агента. Отсюда, кстати, пошли все эти diff-форматы и дополнительные шаги валидации в настоящих агентах — не от любви к сложности.

За контекст платишь на каждом шаге. Гайд отправляет всю историю целиком при каждой итерации цикла. Пока диалог короткий — незаметно. Как только агент сделал пять чтений файлов подряд, история распухает, и каждый следующий шаг дороже предыдущего. Ни сжатия, ни обрезки, ни счётчика токенов в 400 строках, разумеется, нет.

Ошибка инструмента — это просто текст. Возвращаешь модели «file not found», она сразу пробует другой путь. Работает очень хорошо и одновременно означает, что без ограничений агент может долго и увлечённо жечь токены. Таймаута и лимита итераций в цикле нет.

Написать агента легко, сделать из него инструмент — тяжело

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

Права и границы. Пока агент живёт в песочнице с тремя файлами, вопрос «а можно?» не стоит. Как только он оказывается в рабочем репозитории, ты начинаешь проектировать режимы подтверждений, белые списки файлов, изоляцию окружения и то, что происходит, когда агент создает других агентов. Здесь же самое неприятное: любая проверка, которая живёт внутри процесса модели, — это не защита, а договорённость; модель рано или поздно найдёт обходной путь через другой доступный инструмент или подпроцесс. Реальные ограничения приходится ставить снаружи, и именно на это уходит большая часть кода — в одном из разборов эту долю оценивают в 98% всей реализации агента, и после прочтения статьи с этим сложно не согласиться.

Контекст. Цикл, который каждый раз отправляет всю историю, работает ровно до первой большой задачи. Дальше нужно решать, что модель видит на каждом шаге, что сжимается, что выкидывается и как не потерять смысл при сжатии. Это и есть основная работа: «context engineering и есть agent engineering», сам цикл при этом остаётся тем же самым.

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

Обратная связь и остановка. Агент должен уметь узнать, что он сделал плохо: тесты, компилятор, линтер, ошибка внутри инструмента, не понравилось человеку. И должен уметь остановиться — по числу шагов, по времени, по деньгам. В статье всего этого нет, потому что работа агента в статье всегда заканчивается успехом.

Справедливости ради, сам Балл в финале пишет примерно то же.

Мой вердикт

Гайд стоит вечера, но с правильными ожиданиями.

Это не туториал «сделай себе Claude Code». Это способ убрать из темы магию, и с этой задачей он справляется лучше всех материалов, что я читал: пошагово, без фреймворков, с работающим кодом на каждом этапе. Он покрывает ровно то, что обещает, — минимальное ядро. Ценность не в том, что у вас в конце будет агент (агент у вас будет так себе), а в том, что после него меняется восприятие агентов. Вы перестаёте относиться к агентам как к чёрному ящику, начинаете осмысленно читать чужие исходники и, главное, лучше оцениваете новинки в индустрии: видно, что именно они делают, и стоит ли это тащить в проект.

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

Кому не советую: тем, кто ждёт архитектурного гайда для продакшн-реди агента.

А вы читали этот гайд? Делитесь мнениями в комментариях.

Обсуждение

0 комментариев

Комментариев пока нет.