Разработка субагентов для Qwen Code: расширяем возможности AI-программиста
Введение
Qwen Code — это открытый AI-агент для терминала, оптимизированный для моделей семейства Qwen3-Coder. Он помогает разработчикам понимать крупные кодовые базы, автоматизировать рутинные операции и ускорять процесс разработки. Однако настоящая сила этого инструмента раскрывается через систему субагентов (Subagents) — специализированных ИИ-ассистентов, которые берут на себя определённые типы задач, позволяя строить полноценные мультиагентные системы для программирования.
Что такое субагенты и зачем они нужны
Субагенты в Qwen Code — это независимые ИИ-ассистенты, каждый из которых настраивается под конкретную специализацию: написание тестов, рефакторинг кода, генерацию документации, код-ревью и многое другое. Они представляют собой изолированные экземпляры агентов с собственным контекстом, набором инструментов и системным промптом.
Ключевые характеристики субагентов:
- Специализация — каждый агент настраивается с помощью фокусированного системного промпта на определённый тип работы.
- Изоляция контекста — у субагента собственная история диалога, независимая от основного чата, что предотвращает «загрязнение» контекста.
- Контролируемый доступ к инструментам — можно ограничить набор инструментов, доступных каждому субагенту.
- Автономность — получив задачу, субагент работает самостоятельно до завершения.
- Параллельное выполнение — можно одновременно запускать несколько субагентов для независимых задач.
Архитектура системы субагентов
С архитектурной точки зрения система субагентов в Qwen Code построена вокруг `SubagentManager`, который управляет жизненным циклом конфигураций агентов. Конфигурации хранятся в виде Markdown-файлов с YAML frontmatter — такой формат удобен для редактирования в любом текстовом редакторе и легко читается человеком.
Субагенты могут храниться на нескольких уровнях:
- Проектный уровень (`.qwen/agents/`) — наивысший приоритет, агенты доступны только в рамках конкретного проекта.
- Пользовательский уровень `~/.qwen/agents/` — персональные агенты, доступные во всех проектах.
- Уровень расширений — агенты, предоставляемые установленными расширениями.
При запуске субагента через инструмент `Task` происходит следующее:
- Основной агент идентифицирует подзадачу, подходящую для делегирования.
- Создаётся специализированный субагент с описанием задачи, релевантным контекстом, доступом к инструментам и независимой историей диалога.
- Субагент автономно выполняет задачу, вызывая инструменты по мере необходимости.
- По завершении возвращается результат, созданные артефакты и статистика использования токенов.
Создание первого субагента
Быстрый старт через CLI-команды
Самый простой способ создать субагента — воспользоваться встроенной командой в Qwen Code:
/agents create
Эта команда запускает интерактивный мастер, который шаг за шагом проведёт вас через процесс создания специализированного агента. Для управления существующими агентами используется команда:
/agents manage
Она открывает диалоговое окно, где можно просматривать и настраивать уже созданные конфигурации.
Ручное создание через Markdown-файл
Для более тонкой настройки субагенты можно создавать вручную. Файл конфигурации использует формат Markdown с YAML frontmatter. Создайте файл, например, `.qwen/agents/code-reviewer.md`, со следующим содержимым:
```yaml --- name: code-reviewer description: Специализируется на ревью кода, анализе качества и выявлении проблем безопасности tools: - read - grep - glob - web_fetch model: inherit --- # Code Reviewer Agent Ты — эксперт по код-ревью, специализирующийся на: - Выявлении уязвимостей безопасности - Анализе производительности - Проверке соответствия стилю кода и лучшим практикам - Оценке читаемости и поддерживаемости Твоя задача — предоставлять конкретные, actionable рекомендации по улучшению кода. При проверке всегда обращай внимание на контекст проекта и существующие конвенции. Возвращай результат в формате: 1. Общая оценка 2. Критические проблемы (если есть) 3. Рекомендации по улучшению ```
Рассмотрим структуру конфигурации подробнее:
| Поле | Тип | Описание |
| name | string | Уникальный идентификатор (2-50 символов, буквы, цифры, дефисы) |
| description | string | Краткое описание назначения, используется для автоматического делегирования задач |
| tools | string[] | Опциональный список разрешённых инструментов; если не указан — наследуются все доступные |
| model | string | Выбор модели |
| systemPrompt | string | Тело Markdown-файла — это и есть системный промпт, определяющий поведение агента |
Типовые шаблоны субагентов
Вот несколько распространённых паттернов субагентов, которые можно взять за основу:
Test Writer — для генерации тестов:
```yaml --- name: test-writer description: Пишет юнит-тесты и интеграционные тесты с высоким покрытием tools: - read - grep - write_file - bash --- Ты — специалист по тестированию. Пишешь unit-тесты с высоким покрытием, интеграционные тесты для критических путей, обрабатываешь граничные случаи. Следуй принятым в проекте конвенциям тестирования. ```
Documentation Writer — для генерации документации:
```yaml --- name: docs-writer description: Создаёт и обновляет техническую документацию tools: - read - grep - glob - write_file --- Ты — технический писатель. Создаёшь понятную API-документацию, примеры использования, архитектурные диаграммы в Markdown и README-файлы. Придерживайся стиля существующей документации. ```
Refactoring Specialist — для рефакторинга кода:
```yaml --- name: refactor-specialist description: Улучшает структуру кода, устраняет запахи, выделяет переиспользуемые компоненты tools: - read - grep - edit - bash --- Ты — эксперт по рефакторингу. Идентифицируешь проблемы кода, выделяешь повторно используемые компоненты, улучшаешь именование и структуру. Делай небольшие, безопасные изменения. ```
Использование субагентов
Автоматическое делегирование
После настройки субагентов Qwen Code может автоматически делегировать им подходящие задачи. Достаточно сформулировать запрос, соответствующий специализации одного из агентов:
*Пользователь*: «Напиши исчерпывающие тесты для модуля аутентификации»
*AI*: «Я делегирую эту задачу твоему субагенту-специалисту по тестированию».
Основной агент анализирует запрос, сопоставляет его с полем `description` доступных субагентов и принимает решение о делегировании.
Явное делегирование через инструмент Task
Для программного или более точного управления можно использовать инструмент `Task` напрямую:
task( description: "Рефакторинг кода", prompt: "Выполни рефакторинг модуля аутентификации в src/auth/, заменив колбэки на async/await. Убедись, что все тесты проходят, и обнови документацию.", subagent_type: "general-purpose" )
Инструмент принимает три обязательных аргумента:
- `description` (string) — короткое описание задачи (3-5 слов) для отображения пользователю.
- `prompt` (string) — подробные инструкции для автономного выполнения.
- `subagent_type` (string) — тип субагента, соответствующий одной из доступных конфигураций.
Параллельное выполнение задач
Одно из главных преимуществ субагентов — возможность параллельного выполнения независимых задач:
```javascript // Параллельный запуск код-ревью и выполнения тестов task( description: "Код-ревью", prompt: "Проверь последние изменения в модуле управления пользователями на качество кода, безопасность и соответствие лучшим практикам.", subagent_type: "code-reviewer" ) task( description: "Запуск тестов", prompt: "Выполни полный набор тестов и проанализируй ошибки. Предоставь сводку по покрытию и рекомендации по улучшению.", subagent_type: "test-runner" ) ```
Субагенты работают независимо, и их результаты возвращаются по мере завершения. В режиме реального времени можно отслеживать статус выполнения, отдельные вызовы инструментов и возможные ошибки.
#Продвинутые техники и лучшие практики
Проектирование эффективных описаний
Поле `description` играет критическую роль в автоматическом делегировании — именно по нему основной агент решает, подходит ли субагент для задачи. Рекомендации:
- Используйте глаголы действия: «Пишет тесты...», «Анализирует код...», «Рефакторит...».
- Указывайте конкретную предметную область.
- Делайте описание достаточно подробным, чтобы отличить агента от похожих по назначению.
Стратегия ограничения инструментов
Ограничение доступных инструментов — мощный механизм контроля и безопасности. Например:
- Read-only агенты (code-reviewer, docs-writer) — только `read`, `grep`, `glob`, `web_fetch`. Это предотвращает случайные изменения кода.
- Агенты с правом записи (test-writer, refactor-specialist) — добавляются `write_file`, `edit`.
- Агенты с полным доступом (general-purpose) — могут использовать `bash` для выполнения команд и тестов.
Такой подход минимизирует риски и заставляет субагента фокусироваться на своей специализации.
Работа с переменными в промптах
Системные промпты поддерживают шаблонизацию с использованием переменных `${variable}`. Это позволяет создавать гибкие конфигурации, адаптирующиеся под контекст проекта:
```yaml --- name: project-specific-agent description: Адаптируется к технологическому стеку проекта --- Ты работаешь над проектом ${PROJECT_NAME}. Технологический стек: ${TECH_STACK}. Следуй код-стайлу, описанному в ${STYLE_GUIDE}. ```
Интеграция с расширениями
Субагенты можно упаковывать в расширения Qwen Code. Для этого в расширении создаётся директория `agents/`, а в `qwen-extension.json` указывается поле `agents`. При активации расширения агенты автоматически становятся доступными и появляются в диалоге `/agents manage` в секции «Extension Agents».
Отладка и мониторинг
При работе с субагентами полезно:
- Использовать `/agents manage` для просмотра всех доступных конфигураций.
- Отслеживать прогресс выполнения в реальном времени через UI Qwen Code.
- Анализировать возвращаемую субагентами статистику по использованию токенов.
- При проблемах с делегированием явно указывать имя агента в запросе: «Используй [Имя Агента] для [Задача]».
Заключение
Субагенты превращают Qwen Code из простого AI-ассистента в полноценную мультиагентную платформу для разработки. Возможность создавать специализированных агентов, контролировать их инструменты и запускать задачи параллельно открывает новые горизонты автоматизации программирования.
Ключевые преимущества использования субагентов:
- Параллелизация сложных задач на независимые подзадачи.
- Специализация — каждый агент заточен под конкретный тип работы.
- Изоляция контекста — чистота основного диалога и чёткое разделение ответственности.
- Переиспользуемость — один раз настроенный агент работает во всех проектах.
- Безопасность — тонкий контроль над тем, какие инструменты доступны каждому агенту.
Система субагентов в Qwen Code продолжает активно развиваться — в последних версиях появились улучшения в обработке системных промптов, поддержка проектных настроек и оптимизации параллельного выполнения. Следите за обновлениями и экспериментируйте с созданием собственных специализированных агентов для своих рабочих процессов.
Мыслетопливо
Конечно, нередко нам кажется, что заканчивается именно время, но на поверку это оказывается следствием опустошения бачка с мыслетопливом: сначала иссякает оно, потом врубается медленное мышление, как результат мы начинаем «тупить» и испытывать затруднения даже с элементарными вещами, делая их во много раз дольше обычного.
...
UML: Связи между сущностями (объектами)
Основные связи при проектировании Архитектуры
![]() |
Реализация протокола/интерфейса или Имплементация. |
![]() |
Явная связь |
![]() |
Композиция — внутренняя зависимость |
![]() |
Агрегация (инъекция зависимости) внешняя зависимость |
Понизить связанность кода путём скрытия конкретной реализции за протоколами и для этого в UML диаграмме нам подходит такая связь
...
Например: у нас есть протокол и объекты которые на него подписаны (реализующие этот протокол).

protocol Configurable { func configure(with model: Model) }
Driver

Дравйвер
классная вещь в RxSwift, позволяет:
- Восстанавливаться от ошибок.
- Является Observable
- Автоматически переводит подписку drive в Main thread, что делает его идеальным для UI
- Описывать обработку ошибок внутри, не передавая ошибку по цепочке.
... asDriver() .drive({ value in }) ...
Итерация по enum
enum State { case none case one case two }
Конформим State под протокол CaseIterable
extension State: CaseIterable {}
После этого мы можем итерироваться
State.allCases.forEach { print($0) }
Интересные Callback'и
Объявляем тип для callback замыкания
typealias RegStateCallbackType = @convention(c) (_ accountId: pjsua_acc_id) -> Void
var onRegStateCallback: RegStateCallbackType?
И можно тогда заюзать следующим образом
endpointCfg.cb.on_reg_state = onRegStateCallback
Swift String to C char *
void mi_connect_device(char *device_name);
func connect(to device: String) { device.withCString { let int8Pointer = UnsafeMutablePointer<Int8>(mutating: $0) mi_connect_device(int8Pointer) } }
Время сборки в Xcode
Хочу видеть время сборки проекта
Вариант 1:
defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES
Вариант 2:
в папке с проектом выполнить в консоле комманду
time xcodebuild
Запуск из консоли
xcodebuild -workspace CallKitExample.xcworkspace -scheme CallKitExample -showBuildTimingSummary
RxSwift кастомная View'ха
Пишем своё расширение кастомной вьюхи.
import UIKit import RxSwift import RxCocoa class CustomControl: UIControl { var value: Int = 0 { didSet { sendActions(for: .valueChanged)} } override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } func toggle() { if value == 0 { backgroundColor = .green value = 1 } else { backgroundColor = .red value = 0 } } } extension Reactive where Base: CustomControl { var value: ControlProperty<Int> { return base.rx.controlProperty(editingEvents: UIControlEvents.valueChanged, getter: { customView in return customView.value }, setter: { customView, newValue in customView.value = newValue }) } }
И теперь использовать можно так
customView.rx.value .subscribe(onNext: { value in print("changed my value -> \(value)") }) .disposed(by: bag)
Login screen
Реализовал Login screen на VIPER, хотя в несколько облегчённом варианте, скорее всего ещё допилю взаимодействие между модулями. Router — > Router + Presenter
VIPER
V — View (Views + ViewControllers)
I — Interactor — бизнес логика модуля
P — Presentor (ViewModels and prepare)
E — Entities — (Models)
R — Router
Следующие слова из статьи с New York Times
Слова:
- along — вместе
- being — являющийся
- accused — обвиняемый
- whistling — свистящий
- kindle — зажигать
- crumbling — рушиться
- wither — увядать
- apart — кроме
- neglected — пренебрегли
- slaughter — забой скота
Мои попытки учить английский самостоятельно.
Переодически говорю себе, что мне нужен английский. хотя кроме потребности иногда что-то прочитать профессионального по работе, казалось бы жесткой необходимости знать английский и нет.
Хотелось бы проходить собеседование на английском, говорить в кафе как нейтив.
Попробую каждый день читать быстро 5-10 минут по статье из New York Times или The Times
Взял статью.
Решил выписывать неизвестные обороты из неё.
They drew blood from him
Они вытянули кровь из него
caught up in a vast Chinese campaign of surveillance and oppression.
втянутый в обширную китайскую кампанию слежки и угнетения.
Посмотрим смогу ли я продержаться хотя бы неделю.
Оказалось что статья это много для 5 минут, учитывая что и сюда надо оформить пару строк, думаю стоит тогда брать пару абзацев. Возможно дойду до того что буду выписывать основные мысли статей.
Список слов:
Dreamy day — мечтательные дни
Swift KVO
Модель
import Foundation @objc class Card: NSObject { @objc dynamic var life: Int = 0 @objc dynamic var atack: Int = 0 @objc dynamic var defense: Int = 0 }
Задаём переменную для invalidate()
var cardObserver: NSKeyValueObservation?
Подписываемся на изменения и вешаем обработчик
let card = Card() cardObserver = card.observe(\Card.life, options: .old, changeHandler: { card, change in print("Changed life value \(card.life) -> ", change.oldValue ?? -1) })
Соответсвенно когда мы меняем значение переменной `life` то вызывается наш обработчик
card.life = 12 card.life = 1 card.life = 34
Swift: setTimeOut
DispatchQueue.main.asyncAfter(deadline: .now() + 0.600) { // some code }
RxSwift: создаём реактивное расширение
Расширение
extension Reactive where Base: NSView { public var alphaValue: ControlProperty<CGFloat> { let source = self.observeWeakly(CGFloat.self, "alphaValue", options: [.initial, .new]) .filter { $0 != nil }.map { $0! } .takeUntil(deallocated) let observer = Binder<CGFloat>(base) { control, value in control.alphaValue = value } return ControlProperty(values: source, valueSink: observer) } }
Применение
подписываемся на изменения переменной
view.rx.alphaValue.subscribe(onNext: { value in print("val->", value) }) .disposed(by: disposeBag)
меняем значение по слайдеру
slider.rx.value.subscribe({ [weak self] item in let newValue = Int( item.element ?? 0 ) self?.view.alphaValue = newValue }) .disposed(by: disposeBag)
RxSwift: Создание реактивной переменной
Код должен быть прозрачным
import RxSwift
Способ 1
Создаём переменную
var hitPoints = Variable<Int>(10)
Изменение значение переменной
card.hitPoints.value = hp
Подписка на изменение
card.hitPoints.asObservable().subscribe(onNext: { [weak self] value in print("Your hit points ->", value) self?.labelHitPoints.stringValue = "\(value)" }) .disposed(by: bag)
Способ 2
Создаём переменную
var rxMana = BehaviorRelay<Int>(value: 0)
Вешаем обработчик на реактивную переменную
self.rxMana.subscribe(onNext: { value in print("Your level mana ->", value) }) .disposed(by: disposeBag)
Ну и где-нить меняем значение переменной
rxMana.accept(newValue)
Как передать в vue filter контекст компоненты
Задача
Во vue фильтры являются чистыми функциями и они ничего не знают об окружении из которого вызываются, а обратиться к данным компоненты из них бывает очень полезно, а иногда просто жизненно необходимо.
Решение
в шаблоне компоненты
:value="value | filterNumber(this)"
в компоненте
... filters: { filterNumber: function(value, self) { console.log("this->", self.propName) return value; } }, ...
UX design
Один из основных навыков UX-дизайнера — это умение рассказывать о сложных вещах простыми словами.
Делая UX простым, вы сможете влиять на окружающих.


