DietrichGebert/ponytail: AI agent thinks like lazy senior dev
AI‑agent, inspired by the laziest senior developer, writes minimal code that just works, using clever shortcuts and automation to avoid unnecessary work.
Принцип «ленивого» senior dev в AI‑agent
### Оптимизация запросов через шаблоны
Для уменьшения количества токенов и ускорения взаимодействия с моделью удобно заранее готовить шаблоны запросов. Шаблон задаёт структуру диалога: системное сообщение описывает роль, пользователь — конкретную задачу, а при необходимости добавляются примеры few‑shot.
bash# пример шаблона в файле prompt_template.txt
SYSTEM: You are a helpful assistant that optimizes infrastructure code.
USER: <task_description>
ASSISTANT: <generated_response>
yaml# пример использования в скрипте на Python
import json
with open('prompt_template.txt') as f:
template = f.read()
payload = {
"messages": [
{"role": "system", "content": template.split('USER:')[0].strip()},
{"role": "user", "content": "<task_description>"}
]
}
# отправка запроса
resp = client.chat.completions.create(**payload)
Сохраняя шаблон в отдельный файл, вы избавляетесь от повторного ввода одинаковых системных инструкций и можете быстро менять их без перезапуска кода.
Локальные ИИ-агенты и инструменты описывают подходы к организации таких шаблонов в продакшене.
affaan-m/ECC: AI Agent Harness Performance Optimization показывает, как измерять влияние шаблонов на latency и throughput.
### Минимальный код — только необходимое
Ленивый senior dev пишет код, который делает ровно то, что требуется, и ничего лишнего. Пример — Dockerfile, где используется минимальный базовый образ и копируются только нужные файлы.
dockerfile# Dockerfile FROM alpine:3.18 # копируем только необходимый скрипт COPY optimize.sh /usr/local/bin/optimize.sh RUN chmod +x /usr/local/bin/optimize.sh # задаём точку входа ENTRYPOINT ["/usr/local/bin/optimize.sh"]
bash# одноимённый скрипт, минимальный набор команд
#!/usr/bin/env bash
# проверяем, что контейнер запущен с нужными переменными
if [[ -z "$TARGET_HOST" || -z "$TARGET_PORT" ]]; then
echo "Ошибка: переменные TARGET_HOST и TARGET_PORT не заданы" >&2
exit 1
fi
# основная логика в 5 строках
curl -s "http://$TARGET_HOST:$TARGET_PORT/health" | grep -q ok && echo "OK" || echo "FAIL"
В примерах выше видно, что каждый файл содержит минимум строк, а всё, что не влияет на результат, удалено.
Claw-code: автономный музейный экспонат на Rust демонстрирует, как в Rust‑проектах поддерживать минимальный набор зависимостей, что уменьшает размер образа и ускоряет сборку.
Оптимизация ИИ-агентов с помощью системы ECC описывает методы профилирования кода и выявления избыточных участков, которые можно убрать в pursuit минимальности.
Автоматизация рутинных задач
Скрипты‑заготовки в Ponytail
В Ponytail удобно держать набор готовых скриптов, которые решают типовые задачи без лишних уточнений.
Во‑первых, удобно хранить их в отдельном репозитории, чтобы можно было быстро копировать и адаптировать под конкретный сервер.
С практической точки зрения скрипт‑заготовка выглядит так:
bash#!/usr/bin/env bash
# backup_home.sh – создаёт резервную копию домашнего каталога на TrueNAS
set -euo pipefail
SRC="${HOME}"
DEST="backup@truenas:/mnt/backup/${HOSTNAME}/$(date +%Y%m%d)"
LOG="/var/log/ponytail/backup_home.log"
mkdir -p "$(dirname "$LOG")"
rsync -a --delete "$SRC/" "$DEST/" >>"$LOG" 2>&1
echo "$(date '+%F %T') – backup completed" >>"$LOG"
- Что делает скрипт:
- берёт переменную
SRC– каталог, который нужно защитить; - формирует целевой путь
DESTна TrueNAS, используя текущую дату; - логгирует действия в системный журнал.
- берёт переменную
Для автоматизации запуска удобно создать systemd‑таймер:
ini# /etc/systemd/system/ponytail-backup.service
[Unit]
Description=Ponytail backup service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup_home.sh
ini# /etc/systemd/system/ponytail-backup.timer
[Unit]
Description=Run Ponytail backup daily at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
После systemctl enable --now ponytail-backup.timer скрипт будет запускаться автоматически, а systemd будет следить за его состоянием и писать в journalctl.
Ссылка на материал о локальных ИИ‑агентах и инструментах – Локальные ИИ‑агенты и инструменты
Ссылка на статью о Claw‑Code – Автономная разработка на Rust с помощью Claw-Code
CI/CD без лишних шагов
Минимальная CI/CD‑конфигурация позволяет быстро проверять код, собирать образы и публиковать их без лишних стадий.
Во‑первых, достаточно одного workflow‑файла в .github/workflows/.
С практической точки зрения такой файл выглядит так:
yamlname: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Build and push image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: myregistry.example.com/ponytail-app:${{ github.sha }}
- Что делает workflow:
- запускается при пуше в
mainили PR; - проверяет код, собирает Docker‑образ с помощью Buildx;
- авторизуется в Docker‑реестре через секреты;
- пушит образ с тегом, содержащим короткий SHA‑коммит.
- запускается при пуше в
Для упрощения проверки тестов можно добавить pre‑commit hook, который запускает pytest локально перед коммитом. Пример .pre-commit-config.yaml:
yamlrepos:
- repo: local
hooks:
- id: run-tests
name: Run pytest
entry: pytest
language: system
pass_filenames: false
Ссылка на оптимизацию агентов с помощью системы ECC – Оптимизация ИИ‑агентов с помощью системы ECC
Ссылка на автономный музейный экспонат на Rust – Claw-code: автономный музейный экспонат на Rust
Итог:
Скрипты‑заготовки в Ponytail позволяют быстро решать рутинные задачи, а минимальная CI/CD‑конфигурация обеспечивает предсказуемость и надёжность сборки без лишних стадий. Оба подхода работают одинаково хорошо в домашней лаборатории и в продакшене.
Интеграция с Claude‑Code
Настройка prompt‑engineering
Во-первых, prompt‑engineering — это не просто «написать просьбу», а формировать запрос так, чтобы модель поняла точный формат и контекст результата. Минимальный, но информативный запрос выглядит так:
textGenerate a docker‑compose.yml file for a home lab containing: - plex service using the latest plexinc/plex-media-server image - nextcloud service using the nextcloud Docker image - backup service using alpine with a cron job that rsync /home to /backup Use environment variables for PLEX_CLAIM and NEXTCLOUD_ADMIN_USER.
Сокращённый вариант, который удобно копировать в интерфейс Claude‑Code:
bashcat > prompt.txt <<'EOF'
Generate a docker‑compose.yml for plex, nextcloud and a backup container.
Specify the latest images, set required environment variables, and expose ports.
EOF
Локальные ИИ‑агенты и инструменты описывают подробно, как формировать такие запросы, включая примеры для разных сценариев.
С практической точки зрения, при работе с claude‑code полезно задавать ограничение на количество строк в результате. Например, добавить в конец запроса:
Result must not exceed 30 lines.
Это предотвращает «размывание» вывода и упрощает последующее копирование в файл.
Автоматический генератор конфигураций
С deuxième perspective, генерация конфигураций вручную быстро выходит из-под контроля. Автоматизировать её можно с помощью небольших скриптов, которые берут шаблон и подставляют переменные. Ниже пример Bash‑скрипта, создающего конфиги Nginx для нескольких доменов:
bash#!/usr/bin/env bash
# generate_nginx_conf.sh
# Usage: ./generate_nginx_conf.sh example.com test.local
DOMAINS=("$@")
for DO in "${DOMAINS[@]}"; do
cat <<EOF > nginx_${DO}.conf
server {
listen 80;
server_name ${DO};
root /var/www/${DO};
index index.html;
}
EOF
done
Запуск выглядит так:
bash./generate_nginx_conf.sh example.com test.local
Полученные файлы можно сразу поместить в /etc/nginx/conf.d/ и выполнить nginx -s reload. Такой подход гарантирует, что каждый домен имеет одинаковую структуру, а изменения в шаблоне мгновенно отражаются во всех конфигах.
Для более сложных сценариев удобно использовать Python‑скрипт, который читает CSV‑файл со списком параметров и генерирует конфиги Docker‑Compose. Пример кода:
pythonimport csv
with open('services.csv') as f:
reader = csv.DictReader(f)
for row in reader:
print(f" {row['service']}:")
print(f" image: {row['image']}")
print(f" ports: [{row['host_port']}:{row['container_port']}]")
if row['env']:
print(f" environment: {row['env']}")
Файл services.csv может выглядеть так:
service,image,host_port,container_port,env
plex,plexinc/plex-media-server,32400,32400,PLEX_CLAIM=myclaim
nextcloud,nextcloud,8080,80,NEXTCLOUD_ADMIN_USER=admin
backup,alpine,9000,9000,CRON="0 2 * * * rsync -a /home/ /backup/"
affaan-m/ECC: AI Agent Harness Performance Optimization показывает, как такие скрипты могут быть интегрированы в CI/CD, обеспечивая проверку корректности сгенерированных файлов перед применением.
В итоге, чётко сформированный prompt и автоматизированный генератор конфигураций позволяют сократить время разработки и повысить предсказуемость серверных решений.
Практика в домашней лаборатории
Docker‑Compose для изоляции сервисов
Во-первых, Docker Compose позволяет описать весь стек приложения в одном файле и гарантировать, что каждый сервис работает в отдельном контейнере. Это избавляет от конфликтов зависимостей и упрощает масштабирование. С практической точки зрения я обычно выделяю сеть для каждого проекта, чтобы трафик не «переполз» в соседние сервисы.
yamlversion: "3.9"
services:
plex:
image: linuxserver/plex
container_name: plex
restart: unless-stopped
network_mode: "bridge"
ports:
- "32400:32400/tcp"
volumes:
- "/mnt/tank/media:/config"
- "/data/media:/data"
nextcloud:
image: linuxserver/nextcloud
container_name: nextcloud
restart: unless-stopped
network_mode: "bridge"
ports:
- "8080:80"
volumes:
- "/mnt/tank/nextcloud:/config"
- "/data/nextcloud:/var/www/html/data"
backup-agent:
image: ghcr.io/taikayes/taikachedule:latest
container_name: backup-agent
restart: unless-stopped
network_mode: "bridge"
environment:
- TZ=UTC
volumes:
- "/etc/backup:/etc/backup"
Команда docker compose up -d поднимает все сервисы в фоне, а docker compose down безопасно останавливает их без потери данных. Если нужно изолировать сервис от внешнего мира, достаточно добавить networks: и задать внутреннюю сеть без публикации портов.
Для справки, подробный пример использования Docker Compose в домашней лаборатории можно посмотреть в статье Локальные ИИ-агенты и инструменты.
ZFS‑хранилище с авто‑бэкапами
ZFS — это файловая система, которая сама управляет снимками (snapshot) и проверкой целостности данных. Автобэкапы делаются простым скриптом, который создаёт снимок, отправляет его на резервный хост и очищает старые снимки.
bash#!/usr/bin/env bash
# /usr/local/bin/zfs-snapshot.sh
set -euo pipefail
ZFS_POOL="tank"
DATASET="tank/data"
BACKUP_HOST="backup-host"
BACKUP_PATH="backup/tank/data"
# Текущая дата в формате YYYY-MM-DD
SNAP_NAME="${DATASET}@$(date +%Y-%m-%d)"
# Создаём снимок
zfs snapshot -r "${SNAP_NAME}"
# Отправляем снимок на резервный хост
zfs send -R "${SNAP_NAME}" | ssh "${BACKUP_HOST}" "zfs receive -F ${BACKUP_PATH}"
# Удаляем снимки старше 30 дней
zfs list -t snapshot -o name -s creation "${DATASET}" | \
grep -E "@[0-9]{4}-[0-9]{2}-[0-9]{2}$" | \
while read -r snap; do
if [[ $(date -d "$(echo "${snap}" | cut -d'@' -f2)" +%s) -lt $(date -d '30 days ago' +%s) ]]; then
zfs destroy "${snap}"
fi
done
Скрипт добавляем в cron, например:
0 2 * * * /usr/local/bin/zfs-snapshot.sh >> /var/log/zfs-snapshot.log 2>&1
Эта запись будет запускаться каждый день в 02:00 UTC, создавая новый снимок и отправляя его на резервный сервер. При необходимости можно добавить проверку доступности резервного хоста (ping или nc) перед запуском zfs send.
Для более глубокой настройки автобэкапов и мониторинга снимков полезна статья Оптимизация ИИ-агентов с помощью системы ECC, где рассматриваются аналогичные подходы в контексте автоматизации.
Если понадобится более детальная настройка сети Docker Compose или интеграция ZFS‑резервного хранилища с другими сервисами, дайте знать — я подготовлю дополнительные примеры.
Часто задаваемые вопросы
Как создать эффективный шаблон запроса для AI‑agent и сократить количество токенов?
Во‑первых, оформите системное сообщение в виде короткой роли, описывающей задачу (например, «оптимизировать Docker‑образ»).
Во‑вторых, оставьте описание задачи в USER‑сообщении максимально лаконичным, указывая только необходимые параметры.
В‑третьих, добавляйте примеры few‑shot лишь при сложных сценариях, иначе они увеличат токенов без пользы.
Что подразумевается под «минимальным кодом» в AI‑agent и как это выглядит на практике?
Во‑первых, минимальный код — это набор команд, которые делают ровно то, что требуется, без лишних слоёв и повторов.
Во‑вторых, в Dockerfile это значит выбрать самый лёгкий базовый образ, копировать только нужные файлы и объединять RUN‑инструкции там, где это возможно.
В‑третьих, такой подход уменьшает размер образа, ускоряет сборку и упрощает последующее обслуживание.
Насколько полезна оптимизация запросов через шаблоны с точки зрения производительности?
Справедливо, шаблоны позволяют фиксировать системные инструкции один раз и переиспользовать их, что снижает количество токенов в каждом запросе.
С другой стороны, излишне громоздкие шаблоны могут увеличить размер контекста и замедлить отклик модели.
Поэтому рекомендуется измерять latency и throughput, как показано в affaan‑mecc‑ECC, и корректировать шаблон в зависимости от результатов.
Какие подводные камни могут возникнуть при использовании «ленивого» подхода в AI‑agent?
Во‑первых, чрезмерная лаконичность может привести к упущению важных деталей, из‑за чего агент будет генерировать некорректный код.
Во‑вторых, отсутствие тестирования и проверки крайних случаев делает такие решения хрупкими в продакшене.
В‑третьих, без документирования шаблонов и минимального кода сложно поддерживать их в дальнейшем, что противоречит принципу предсказуемости.