AthenaDev
AthenaDev — мой консалтинг по встраиванию AI в ежедневную работу продуктовых команд. Не «добавим чат-бот». Реальная интеграция воркфлоу: рассктка Claude Code на команды разработки, MCP-серверы для подключения LLM к внутренним инструментам, RAG-системы для корпоративной базы знаний, AI-ревью пайплайны.
Что делаем
Внедрение Claude Code в продуктовые команды
Перевожу команду из «слышали про Claude Code» в «половина PR стартует как agent-driven drafts». Настройка, кастомные slash-команды, project-specific hooks, MCP-интеграции и неинтересная-но-критическая часть обучения, от которой зависит adoption.
- →Project-level settings.json с разумными дефолтами по permissions
- →Кастомные skills, hooks и slash-команды под ваш стек
- →MCP-сервер, подключающий Claude к Linear/Jira/Slack/внутренним API
- →Дашборд adoption-метрик (active users, доля PR с AI-assist)
Кастомные MCP-серверы
Model Context Protocol — правильный примитив для связки LLM с вашим стеком. Делаю production-grade MCP-серверы на TypeScript или Python: auth, rate-limiting, observability, валидация схем. Self-hosted или managed service.
- →Двунаправленная интеграция (read + write)
- →Type-safe схемы тулов (Zod / Pydantic) с runtime-валидацией
- →Per-user auth-скоупинг (без инцидентов «агент действует как админ»)
- →Аудит-лог и дашборды по tool-call трафику
RAG поверх внутренних знаний
Production-RAG, не демо. Пайплайн ингеста документов, стратегия embeddings (чанкинг, иерархия, гибридный lexical + dense поиск), оценка качества retrieval, цитирование источников, output guardrails. На том vector-store, что подходит — Postgres + pgvector, Qdrant, Pinecone — выбор по стоимости/латентности, не по хайпу.
- →Ингест документов с версионированием и инкрементальными апдейтами
- →Гибридный retrieval (BM25 + dense embeddings) + reranking
- →Ответы с цитированием и ссылкой на источник
- →Evaluation-харнесс (precision/recall на размеченной выборке)
LLM observability и cost engineering
Большинство команд выпускают LLM-фичи вслепую. Ставлю observability-слой (Langfuse, OpenTelemetry, кастомные дашборды), cost-tracking (per-feature, per-user, per-model) и model-routing, который позволяет брать самую дешёвую модель, ещё проходящую ваш quality-bar.
- →Per-request traces с промптом, ответом, токенами, latency
- →Cost attribution по команде / фиче / клиенту
- →Smart model router (правила или learned)
- →Алерты на регрессии качества и cost spikes
Кейсы
Клиенты не называются явно — стандартная практика консалтинга. По NDA-описанию профиль и контекст можно сверить.
Fintech среднего размера (Series B, ~80 инженеров)
Проблема
Engineering leadership видел, что Claude Code используют отдельные разработчики ad-hoc, но не было организационного adoption, общей конфигурации и видимости. Какие-то команды собрали умные воркфлоу, какие-то — гоняли Claude в дефолтном режиме без guardrails.
Конкретные запросы: стандартизировать per-repo конфигурацию, собрать org-wide skills/slash-команды под их стек (Go + React + Postgres), подключить Claude к их внутренним инструментам (Linear, GitHub, внутренний feature-flag сервис), сделать метрики, которые engineering leadership сможет смотреть ежемесячно.
Подход
- Стартовали с одной пилотной командой из 12 инженеров. Написал baseline .claude/settings.json с allowlist Bash-permissions, деструктивные команды (rm -rf, force-push, db drop) запретил по умолчанию, их внутренний CLI — pre-approved.
- Собрал 6 кастомных skills: 'review-pr' (их линтер + тесты + кастомный static-analysis), 'add-feature-flag' (через MCP к их flag-сервису), 'rollback-migration', 'check-staging', 'ship-it' (PR + назначение ревьюеров по CODEOWNERS), 'why-flaky' (их плейбук по flaky-тестам).
- Сделал внутренний MCP-сервер на TypeScript, экспонирующий их Linear, GitHub и feature-flag API. Per-user OAuth — агент действует с правами инженера, не сервисного аккаунта.
- Hook-генерация PR description: на git commit пост-коммит хук вызывает Claude с diff-ом, чтобы сгенерить описание PR в их формате. Экономит ~5 мин на PR.
- Ежемесячный adoption review: дашборд по active users, сессиям на разработчика в неделю, доле PR с AI-assist, разбивке tool-calls по skills.
Решения
Per-user auth на каждом MCP-туле, не общий сервисный аккаунт. Цена — больше настройки; профит — отсутствие инцидентов «агент тихо сделал что-то на проде». Audit log привязан к конкретному инженеру.
Раскат сделали opt-in на первые 4 недели. Игнорировавшие — продолжили игнорить. Попробовавшие — стали внутренними евангелистами. Принудительный adoption отравляет колодец, network effect работает лучше.
Дашборд метрик собрали ДО раската, не после. Leadership заранее закоммитился на конкретные success-метрики. Избежали поздних споров «а это вообще помогло?».
Из кода
{
"permissions": {
"allow": [
"Bash(npm:*)",
"Bash(go test:*)",
"Bash(git status)",
"Bash(git diff:*)",
"Bash(./scripts/dev-cli:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push --force:*)",
"Bash(git push -f:*)",
"Bash(psql:*drop*)",
"Bash(make deploy:*)"
],
"ask": [
"Bash(git push:*)",
"Bash(./scripts/migrate:*)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "./scripts/run-precommit.sh"
}]
}
]
}
}server.tool(
"linear_search_issues",
"Search Linear issues with the current user's permissions.",
{
query: z.string().describe("Linear search query syntax"),
limit: z.number().int().min(1).max(50).default(20),
},
async ({ query, limit }, { session }) => {
// session.linearToken is the user's OAuth token, not a service account.
const client = new LinearClient({ accessToken: session.linearToken });
const issues = await client.issues({ filter: parseQuery(query), first: limit });
audit.log({
tool: "linear_search_issues",
userId: session.userId,
query,
resultCount: issues.nodes.length,
});
return issues.nodes.map((i) => ({
id: i.identifier,
title: i.title,
state: i.state?.name,
url: i.url,
}));
}
);Результат
Adoption в пилотной команде вырос с ~3 активных пользователей до 11 из 12 за время проекта. Среднее время до первого PR у новых сотрудников снизилось с 4 дней до 1.5. Skill 'review-pr' начал ловить класс багов (отсутствие миграции в PR), который их CI не был настроен ловить.
После проекта они растянули тот же плейбук ещё на 4 команды. Я остался на ретейнере для развития skills и поддержки MCP-сервера.
Юр.фирма среднего размера (~120 юристов, Россия + СНГ)
Проблема
У фирмы ~60 000 страниц внутреннего precedent: меморандумы, deal summaries, обзоры регуляторики, судебные документы. Хранилось в SharePoint, старой Confluence и личных дисках. Юристы тратили неприлично много времени на «у нас было что-то похожее в 2022, найди мне тот меморандум».
Требования: поиск по русскому и английскому, каждый ответ — с цитатой точного параграфа источника (никаких галлюцинаций на юр.контенте), уважение document-level access control (associates не видят partner-level меморандумы), и self-hosted инфра (data sovereignty).
Подход
- Self-hosted стек: Postgres + pgvector (без внешнего vector DB), open-source bge-m3 multilingual embeddings, Claude Sonnet через API для синтеза, со строгим требованием к цитированию в system prompt.
- Пайплайн ингеста: docling для layout-aware PDF-парсинга, semantic chunking (по заголовкам и параграфам, не по сырому char count), embedding на чанк плюс document-level summary embedding для иерархического retrieval.
- Hybrid retrieval: BM25 (tsvector в Postgres) + dense vectors, результаты сливаются через reciprocal rank fusion. Reranking через cross-encoder для top-50 кандидатов.
- ACL на уровне retrieval: у каждого чанка колонка access_level; SQL-запрос джойнит роль пользователя. Модель никогда не видит контент, который не должна.
- Принудительное цитирование: system prompt требует ответа в JSON с answer + citations[]. Валидатор реджектит ответы без цитат или с chunk_id, которых нет в retrieved set.
- Сделал evaluation-харнесс с 200 размеченными запросами (gold-ответ + ожидаемые цитаты). Трекал precision@5, recall@10 и citation accuracy еженедельно.
Решения
Hybrid retrieval, не чистый dense. Юр.текст сильно завязан на точную терминологию — названия дел, номера статей, defined terms. BM25 ловит то, что embeddings пропускают; и наоборот. Fusion дал ~12% выигрыш против каждого по отдельности на eval-сете.
Self-hosted embeddings на CPU-боксе (bge-m3, ~1B params). Экономия на API-костах и непередача privileged контента третьей стороне. Ингест медленнее, но идёт один раз на документ.
Без fine-tuning. Eval-метрики уже были крепкими за счёт улучшений retrieval и аккуратного промпта. Fine-tuning добавил бы ежеквартальное обслуживание с неочевидным ROI.
Из кода
-- Single query: BM25 + dense vector + ACL.
-- :q_tsv = websearch_to_tsquery('russian', :user_query)
-- :q_emb = embedding(:user_query)
-- :user_clearance = associate | partner
WITH bm25 AS (
SELECT chunk_id,
ts_rank_cd(content_tsv, :q_tsv) AS bm25_score
FROM chunks
WHERE content_tsv @@ :q_tsv
AND access_level <= :user_clearance
ORDER BY bm25_score DESC
LIMIT 100
),
dense AS (
SELECT chunk_id,
1 - (embedding <=> :q_emb) AS dense_score
FROM chunks
WHERE access_level <= :user_clearance
ORDER BY embedding <=> :q_emb
LIMIT 100
)
SELECT c.chunk_id, c.content, c.source_doc, c.page,
COALESCE(b.bm25_score, 0) * 0.3 +
COALESCE(d.dense_score, 0) * 0.7 AS hybrid_score
FROM chunks c
LEFT JOIN bm25 b USING (chunk_id)
LEFT JOIN dense d USING (chunk_id)
WHERE b.chunk_id IS NOT NULL OR d.chunk_id IS NOT NULL
ORDER BY hybrid_score DESC
LIMIT 20;class Citation(BaseModel):
chunk_id: str
source_doc: str
page: int
quote: str = Field(min_length=10)
class RagAnswer(BaseModel):
answer: str
citations: list[Citation] = Field(min_length=1)
confidence: Literal["high", "medium", "low"]
def validate_grounding(answer: RagAnswer, retrieved: set[str]) -> None:
"""Reject answers that cite chunks not in the retrieved set."""
unknown = [c.chunk_id for c in answer.citations if c.chunk_id not in retrieved]
if unknown:
raise UngroundedResponseError(
f"Citations refer to chunks not retrieved: {unknown}. "
"Possible hallucination — refusing to surface to user."
)
Результат
Из обратной связи пилотных пользователей: среднее время поиска релевантного прецедента упало с ~25 минут до меньше 2. Citation accuracy достигла 94% на eval-сете к 8-й неделе.
Фирма продлила на 6-месячный ретейнер для развития пайплайна ингеста и квартальных расширений eval-сета.
B2B SaaS-компания (~30 инженеров, Series A)
Проблема
Engineering lead запросил AI-ревьюера первой линии для PR на GitHub. Стадия trial-and-error уже прошла — они пилотировали три коробочных тула (Copilot Review, CodeRabbit, Greptile) и не были довольны соотношением сигнал/шум. Слишком много косметики, мало полезных catches.
Что им реально надо: ревьюер, знающий их code conventions (lint-правила, внутренние паттерны, deprecated утилиты), флагающий risky changes (auth, billing, миграции) и постящий ровно один коммент на PR — summary, не 14 отдельных inline-нитов.
Подход
- GitHub Action на PR open + synchronize. Подтягивает diff, изменённые файлы целиком и (для risky paths) callers задетых файлов через быстрый grep.
- Routed Claude Sonnet — Haiku был неточен на их TS-кодбазе; Opus избыточен для рутинных PR. Sonnet с 4K-token max response держал косты на ~$0.07/PR в среднем.
- Кастомный system prompt с их реальными conventions: 'use ourBigDecimal for money, never Number'; 'никогда не звать /v1/payments напрямую, идти через PaymentService'; 'миграции требуют reversible down() в течение 24 часов'. Около 1200 токенов repo-specific правил.
- Risk-tier классификация: PR в auth/, billing/, migrations/ → 'high', strict-prompt и тег @security-team. PR только в tests/docs → 'low', короткий шаблон ответа.
- Output: один структурированный коммент на PR — 'Summary', 'Risk', 'Issues found' (с severity), 'Suggestions'. Без inline-шума. Line-by-line ревью делают их же инженеры — Claude задаёт framing.
- PII/secret-фильтр на входе: строки, матчащие паттерны секретов (API keys, tokens), вырезаются до отправки в API. Security-команда подписала это до выпуска.
Решения
Один коммент на PR, не inline. У команды была усталость от предыдущих тулов; консолидация уважала их внимание. Один коммент можно проигнорить, если не согласен; 14 — нельзя.
Risk-tier routing вместо «прогонять каждый PR через самый строгий промпт». Косты упали в ~3 раза, false-positive ratio тоже — потому что high-tier промпт стал зарезервирован для реально требующих внимания изменений.
Выпустили с feedback-кнопкой (👍/👎 + опциональная причина) на каждом Claude-комменте. Negative feedback использовал еженедельно для обновления system prompt — большинство фиксов было «это правило не правило, наш кодбейз делает X».
Из кода
name: AI code review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with: { node-version: "20" }
- name: Classify risk tier
id: tier
run: node scripts/risk-tier.mjs >> "$GITHUB_OUTPUT"
- name: Strip secrets from diff
run: node scripts/scrub-secrets.mjs > /tmp/diff.txt
- name: Run AI reviewer
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
RISK_TIER: ${{ steps.tier.outputs.tier }}
run: node scripts/review.mjs /tmp/diff.txtconst HIGH_RISK_PATHS = [
/^src\/auth\//,
/^src\/billing\//,
/^src\/payments\//,
/^migrations\//,
/\.env(\..+)?$/,
];
const LOW_RISK_PATHS = [
/\.test\.tsx?$/,
/^docs\//,
/^\.github\/workflows\//,
];
export function classifyTier(changedFiles: string[]): "high" | "normal" | "low" {
if (changedFiles.some((f) => HIGH_RISK_PATHS.some((re) => re.test(f)))) {
return "high";
}
if (changedFiles.every((f) => LOW_RISK_PATHS.some((re) => re.test(f)))) {
return "low";
}
return "normal";
}Результат
После 4 недель в проде: 'useful catch' rate (👍 от инженера) — 71%. Time-to-first-human-review на PR снизилось с ~4ч до ~45мин (инженеры триажат Claude-summary, потом коммитятся на глубокое ревью).
Цитата engineering lead на закрытии: «это первый AI-тул, что мы пробовали, который реально ощущается как джун-ревьюер, а не как линтер, притворяющийся им».
Как мы работаем
Где вы реально сейчас с AI, что болит, как выглядит «хорошо». Без презентации, только вопросы.
Документ на 1-2 страницы с конкретными deliverables, таймлайном и success-метриками. Fixed-scope или ретейнер.
Выпускаем минимальное end-to-end, доказывающее подход. Пилотируем с одной командой, не на всю орг.
Конфигурация, кастомные skills, внутренняя документация, обучающие сессии. Adoption — 60% ценности, эту часть я тоже делаю.
Текущая поддержка, расширение на новые команды, ежемесячные ревью метрик. Большинство проектов продолжается тут.
Связаться
Если хочется обсудить, как Claude Code или RAG могут помочь именно вам — пишите. Диагностический созвон бесплатный, без обязательств.