惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
Arctic Wolf
WordPress大学
WordPress大学
月光博客
月光博客
J
Java Code Geeks
罗磊的独立博客
V
Visual Studio Blog
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog
GbyAI
GbyAI
The Cloudflare Blog
B
Blog
S
SegmentFault 最新的问题
T
Tenable Blog
P
Privacy International News Feed
爱范儿
爱范儿
V
Vulnerabilities – Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
量子位
博客园 - 三生石上(FineUI控件)
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Security Latest
Security Latest
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Proofpoint News Feed
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
C
CXSECURITY Database RSS Feed - CXSecurity.com
U
Unit 42
D
DataBreaches.Net
T
Threatpost
C
Cisco Blogs
Project Zero
Project Zero
K
Kaspersky official blog
MongoDB | Blog
MongoDB | Blog
C
Check Point Blog
A
About on SuperTechFans
The Register - Security
The Register - Security
C
Cyber Attacks, Cyber Crime and Cyber Security
S
Schneier on Security
L
Lohrmann on Cybersecurity
T
Threat Research - Cisco Blogs
I
InfoQ
Simon Willison's Weblog
Simon Willison's Weblog
F
Fortinet All Blogs
Recorded Future
Recorded Future
AWS News Blog
AWS News Blog
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Пишем кодинг‑агента на Swift с нуля: неочевидные сложности очевидной идеи
Иван Магда · 2026-06-18 · via Все публикации подряд на Хабре

Средний

8 мин

0

Я долго пользовался разными кодинг‑агентами, и на их фоне Claude Code для меня заметно выделялся: качеством решений, удобством работы и вниманием к деталям. В какой‑то момент мне захотелось не просто пользоваться таким инструментом, а понять, что на самом деле происходит у него под капотом. Так я сел писать собственного агента на Swift, с нуля, без использования готовых решений.

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

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

Два цикла: REPL и Agent Loop

У любого кодинг‑агента на самом деле два цикла, и у каждого своя задача.

REPL — это внешний цикл. Он читает пользовательский ввод, передает его агенту и ждет следующего запроса.

Agent Loop — это внутренний цикл. Он вызывает LLM API, выполняет запрошенные инструменты и продолжает работу до тех пор, пока модель не решит, что задача выполнена. Один ваш запрос в REPL может развернуться в десяток итераций внутреннего цикла.

Внешний цикл REPL и внутренний Agent Loop

Внешний цикл REPL и внутренний Agent Loop

Дальше нас интересует именно внутренний цикл. Снаружи он выглядит как магия, внутри как небольшой while.

Agent Loop

Вся «магия» кроется в этом небольшом цикле. Сначала мы добавляем запрос пользователя в историю сообщений. Дальше на каждой итерации собираем контекст и отправляем модели. Модель думает над следующим шагом и возвращает ответ. Если это финальный текст, мы закончили и выходим из цикла. Если модель попросила вызвать инструмент, мы его выполняем, возвращаем результат обратно в историю и идем на следующую итерацию. И так до тех пор, пока модель не решит, что задача выполнена.

func run(query: String) async throws -> String {
    messages.append(.user(query))

    while true {
        let request = APIRequest(
            model: model, system: systemPrompt, messages: messages, tools: tools
        )
        let response = try await apiClient.createMessage(request)
        messages.append(Message(role: .assistant, content: response.content))

        guard response.stopReason == .toolUse else {
            return response.content.textContent
        }

        var results = [ContentBlock]()
        for block in response.content {
            if case .toolUse(let id, let name, let input) = block {
                let output = await executeTool(name: name, input: input)
                results.append(.toolResult(toolUseId: id, content: output))
            }
        }
        messages.append(Message(role: .user, content: results))
    }
}
Agent Loop по шагам

Agent Loop по шагам

Один проход цикла: модель запрашивает инструмент, получает tool_result и идет на следующую итерацию, пока не вернет финальный текст.

И еще важный момент: этот цикл не специфичен для кодинг‑агента. Это общий паттерн — agentic loop. Цикл вообще не зависит от домена. Кодинговым агента делает не цикл, а инструменты, которые в него вкладываешь: чтение и запись файлов, shell, редактирование. Плюс системный промпт и окружение. Сам цикл одинаков и для кодинг‑агента, и для агента поддержки. Если вы уже строили агентские системы, то вам это знакомо.

За все время развития агента цикл концептуально не менялся. Все новое добавлялось вокруг него. И это не случайность: цикл стабилен именно потому, что отделяет оркестрацию от конкретных инструментов. Модель только заявляет намерение, а решение и выполнение остаются за обвязкой, за harness.

Bash is all you need?

Если задуматься, то shell‑команды это универсальный инструмент для взаимодействия с операционной системой. Давая агенту такой единственный bash инструмент, он может выполнять любые операции с файлами, запускать программы и управлять системой. Закономерно возникает вопрос, так зачем нам что‑то еще?

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

Агент правит файл многострочным sed через bash

Агент правит файл многострочным sed через bash и один лишний бэкслеш ломает файл

Поэтому появляются отдельные инструменты: read_file, write_file, edit_file. С известным форматом ввода и вывода, с лимитами на размер, с атомарной записью. Модель больше не изобретает каждый раз команду, она вызывает инструмент с понятными параметрами и получает предсказуемый ответ.

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

Sandbox и guardrails

Специализированные инструменты есть, модель умная. Кажется, она не полезет читать или удалять что‑нибудь за пределами текущей рабочей директории. На это хочется положиться, но на практике ответ модели непредсказуем. Она вполне может попросить абсолютный путь или выйти из проекта через ../../.

private static let dangerousPatterns = [
  "rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"
]

if let matched = dangerousPatterns.first(where: { command.contains($0) }) {
  throw ShellExecutorError.blockedCommand(matched)
}

let fullURL = relativePath.hasPrefix("/")
  ? URL(fileURLWithPath: relativePath).standardized
  : workDirURL.appendingPathComponent(relativePath).standardized

guard fullURL.path.hasPrefix(resolvedWorkDir.path + "/") ||
      fullURL.path == resolvedWorkDir.path else {
  return .failure(.executionFailed("Path escapes workspace"))
}

guard allowedTools.contains(name) else {
  results.append(.toolResult(
    toolUseId: id, content: "Tool '\(name)' is not allowed", isError: true
  ))
  continue
}

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

Path sandbox: резолвим путь и проверяем, что он остается внутри рабочей директории. Блокировка опасных команд: маленький список вроде rm -rf /, sudo, shutdown, который bash инструмент просто откажется выполнять. И то же самое с вызовами инструментов: даже если инструмента нет в конфиге, модель может нагаллюцинировать вызов с таким именем. Поэтому allowlist для инструментов тоже проверяется принудительно.

Песочница блокирует выход за пределы рабочей директории

Sandbox и guardrails в действии

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

Todo tool, или instruction‑following decay

Кажется, что модель достаточно умная и план держит в голове сама, хватит одной строчки в системном промпте «следуй плану». На коротких задачах так и есть.

На длинной задаче происходит интересное. Первые шаги модель делает уверенно, к середине начинает дрейфовать, к концу уже импровизирует. План растворяется в массе вызовов инструментов и их результатов. Модель не забывает в человеческом смысле, она просто перестает смотреть на старое: чем больше контекст, тем сильнее внимание уезжает к свежему вводу. У этого даже есть название — instruction‑following decay.

Дрейф плана без todo-инструмента и удержание с ним

Дрейф плана без todo‑инструмента и удержание с ним

Вывод простой: план не должен жить только в рассуждении модели. Todo tool — это список задач, который модель ведет сама для себя. Каждый вызов возвращает план как tool_result, то есть он физически попадает в конец контекста, туда, где внимание максимальное.

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

Сабагенты и загрязнение контекста

Один агент, единая история сообщений. Кажется естественным держать всё в одном контексте.

Простой пример на задаче поиска: «какой testing framework используется в проекте?». Чтобы ответить одним словом «XCTest», модель читает десяток файлов, грепает директории, вызывает различные команды. И все эти результаты остаются в контексте навсегда, хотя нам нужен был только вывод. Это и есть проблема загрязнения контекста: на длинной сессии массив сообщений набивается промежуточной информацией, которая вытесняет важное и усиливает тот самый дрейф внимания.

// Тот же самый agentLoop, другие настройки
let result = try await agentLoop(
  initialMessages: [Message.user(prompt)],
  config: .subagent
)

Решение — делегировать задачу с изолированным контекстом. Субагент получает чистый массив сообщений, делает работу и возвращает только финальный текстовый ответ. Вся его промежуточная история в контекст родителя не попадает. Цикл при этом тот же самый agentLoop, меняется только LoopConfig.

Главный агент и изолированный контекст субагента

Главный агент и изолированный контекст субагента

Главный агент получает только итог («XCTest»). Все 12 промежуточных сообщений и ~22k токенов остаются в изолированном контексте субагента.

Логичный вопрос: как система решает, что делегировать субагенту, а что выполнить в основном контексте? Никакого роутера в коде нет, решение принимает сама модель по описанию инструмента agent. Логика в описании простая: если задача создаст много промежуточного мусора и может быть изолирована, делегируем.

Субагент — это не про concurrency, а про делегирование с изоляцией контекста. Но даже с субагентами основной контекст все равно растет.

Context management

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

Главный источник мусора видно невооруженным глазом — это результаты вызовов инструментов. Один read_file на тысячу строк это тысячи токенов, и таких в длинной сессии десятки.

Отсюда базовый алгоритм. Micro‑compact работает в фоне перед каждым запросом: все tool_result, кроме N последних, заменяются на placeholder. Факт вызова остается, а громоздкий вывод удаляется. Работает это потому, что старые результаты модель уже прочитала и учла в своих ответах. В худшем случае будет один лишний вызов инструмента, а не потеря корректности.

// micro-compact, в фоне перед каждым запросом:
// старые tool_results → placeholder
let oldResults = toolResultLocations.dropLast(keepRecent)
// → .toolResult(toolUseId: id, content: "[Previous: used \(toolName)]")

// auto-compact при превышении порога
if estimateTokens(from: messages) > tokenThreshold {
  let path = try saveTranscript(messages)
  let summary = try await apiClient.createMessage(request: ...)
  messages = [.user(summary), .assistant("Continuing.")]
}
Накопление контекста, micro-compact и auto-compact

Накопление контекста, micro‑compact и auto‑compact

Контекст наполняется результатами вызовов. Micro‑compact сворачивает старые tool_result, а при переходе порога срабатывает auto‑compact.

Когда размер контекста все равно переходит порог, срабатывает auto‑compact: transcript сессии сохраняется на диск, модель делает summary, и заменяет им всю историю.

Теперь агенту можно доверить длинную задачу и он автономно доведёт её до конца.

Что я вынес для себя

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

А вот как поменялся мой собственный подход к работе с кодинг‑агентами:

  1. Когда агент делает что‑то не так, чаще всего проблема не в модели, а в обвязке: контекст, инструменты, промпт, архитектура, — и нужно искать проблему здесь.

  2. Я начинаю новую сессию раньше и чаще. Длинный контекст деградирует всегда, и чистая сессия выигрывает у длинной.

  3. Я держу инструкции компактными. Короткий CLAUDE.md или AGENTS.md с progressive disclosure работает лучше, чем простыня на все случаи жизни.

  4. И я не верю self‑review модели. Чтобы закрыть цикл обратной связи, агенту нужно дать возможность проверить себя объективно: тесты, линтер, компилятор.

Где взять код

Весь код и серия статей в формате code‑along лежат в репозитории на Гитхабе, если захотите построить своего агента или копнуть тему глубже.

Если строили своих агентов и встречали подводные камни, расскажите в комментариях.