Разбор
AI-редакция: промпты, метрики, QA. Часть 2 — готовый конвейер с примерами кода
Скопируй промпты и запусти конвейер на своём сервере. Полные примеры для CrewAI, система scoring, метрики качества и типичные ошибки.
В Части 1 разобрали архитектуру. Теперь — боевые промпты и система качества.
Ниже — готовые examples, которые работают. Просто скопируй в свой CrewAI проект.
Инсайт-перевёртыш: промпт — это не просьба, это контракт
Думают: напишу в промпте «напиши статью про AI-редакцию» — AI поймёт и напишет хорошо.
На самом деле? Промпт — это техническая спецификация. Строгая, с примерами, с критериями оценки. Без неё агент будет писать вслепую.
Хороший промпт для Writer Agent включает:
- Определение роли и цели (выгляди редактором)
- Входные данные (бриф в JSON)
- Выходной формат (MDX с конкретной структурой)
- Примеры успешных статей (top-3 похожих статей в контексте)
- Чек-лист критериев (то, по чему агент себя проверит)
- Как выставить себе оценку (scoring rubric)
1. Writer Agent — скопируй и запусти
Промпт (копируй в crew.py)
WRITER_PROMPT = """
You are an expert content writer for a marketing blog (lekha-marketer-blog).
Your goal: write a detailed, fact-backed blog post from a brief.
INPUT:
You will receive a JSON brief with:
- topic: article topic
- target_keyword: SEO keyword to target (NOT keyword spam)
- outline: suggested structure (H1 > H2s > H3s)
- sources: key URLs to reference
- tone: conversational, expert, accessible (choose one)
OUTPUT:
Write a complete blog post in Markdown/MDX format:
1. Start with an immediate hook (1-2 sentences) + brief story or stat
2. Include "царь-цифра" (one striking stat in a highlighted block)
3. Add a "paradigm shift" section (old belief vs new reality)
4. Write main sections (H2s) with:
- Opening paragraph with a fact or quote
- 2-4 detailed paragraphs
- Actionable takeaway block ("⚡ Do this in 5 min")
- At least 1 external source per section
5. End with a bridge to next content (Telegram channel teaser)
6. Include internal links (minimum 5) naturally in text
EXAMPLES OF SUCCESS:
- Use 3-4 sentence paragraphs (mobile-first)
- Include tables, comparison blocks, accordion-style details
- Every claim > 20 words must have a source: [claim (Source, year)](url)
- No ChatGPT-style repetition ("It's important to note that...")
- Aim for 3500–4000 words
SCORING RUBRIC (rate yourself 0–100):
- 20 pts: Unique facts + proper citations (5+ sources)
- 20 pts: Structure matches outline (H1, H2s, Actionable Takeaways)
- 20 pts: Readability (avg sentence < 20 words, no jargon without explanation)
- 20 pts: Tone consistency (matches brief tone)
- 20 pts: No plagiarism (use Copyscape mentally — are phrases original?)
If score < 75, what's wrong? List issues and rewrite those sections.
"""
CrewAI Agent Definition
from crewai import Agent, Task
writer_agent = Agent(
role="Content Writer",
goal="Write informative, fact-backed blog posts with proper citations",
backstory="Expert blog writer with 5+ years in SaaS/marketing blogging. Knows how to hook mobile readers and include real data.",
llm="claude-opus", # Using Claude API
memory=True,
verbose=True,
)
write_task = Task(
description="Write a blog post based on brief: {brief}",
agent=writer_agent,
expected_output="Complete MDX blog post with H1, H2s, sources, internal links",
context=[trend_scout_task], # Depends on Trend Scout output
)
Quality Check (After Agent Completes)
import json
import re
def validate_writer_output(mdx_content: str) -> dict:
"""Check Writer Agent's output quality."""
issues = []
score = 0
# Check 1: H1, H2s structure
h1_count = len(re.findall(r'^# [^#]', mdx_content, re.MULTILINE))
h2_count = len(re.findall(r'^## [^#]', mdx_content, re.MULTILINE))
if h1_count != 1:
issues.append("Missing or multiple H1 headers")
else:
score += 10
if h2_count < 3:
issues.append(f"Too few H2 headers ({h2_count}, need 3+)")
else:
score += 10
# Check 2: External links with citations
citation_pattern = r'\([A-Za-z0-9\s\-,]+,\s\d{4}\)\('
citations = len(re.findall(citation_pattern, mdx_content))
if citations < 5:
issues.append(f"Too few citations ({citations}, need 5+)")
else:
score += 15
# Check 3: Word count
word_count = len(mdx_content.split())
if word_count < 3000:
issues.append(f"Too short ({word_count} words, need 3500+)")
elif word_count > 5000:
issues.append(f"Too long ({word_count} words, target 3500–4000)")
else:
score += 15
# Check 4: Internal links
internal_links = len(re.findall(r'\(/blog/|/calculators/|/ratings/', mdx_content))
if internal_links < 5:
issues.append(f"Too few internal links ({internal_links}, need 5+)")
else:
score += 15
# Check 5: Actionable Takeaway blocks
takeaway_blocks = len(re.findall(r'⚡ (Сделай|Do this)', mdx_content, re.IGNORECASE))
if takeaway_blocks < 2:
issues.append(f"Too few 'Actionable Takeaway' blocks ({takeaway_blocks}, need 2+)")
else:
score += 15
# Check 6: King number (hero stat)
hero_stat = re.search(r'царь-цифра|hero.stat', mdx_content, re.IGNORECASE)
if not hero_stat:
issues.append("Missing 'царь-цифра' (hero stat block)")
else:
score += 10
# Check 7: No ChatGPT clichés
cliches = ["It's important to note that", "As mentioned above", "In today's world"]
found_cliches = [c for c in cliches if c.lower() in mdx_content.lower()]
if found_cliches:
issues.append(f"ChatGPT clichés found: {found_cliches}")
score = max(0, score - 10)
return {
"score": score,
"passing": score >= 75,
"issues": issues,
"metrics": {
"h1_count": h1_count,
"h2_count": h2_count,
"citations": citations,
"word_count": word_count,
"internal_links": internal_links,
"takeaway_blocks": takeaway_blocks,
}
}
2. Editor Agent — редактура и фактчек
Промпт
EDITOR_PROMPT = """
You are an expert editor for a marketing blog. Your job: improve text, fix errors, check facts.
INPUT:
- blog_post: MDX text from Writer Agent
- sources: list of allowed sources (to validate citations)
YOUR TASK:
1. Grammar & Style:
- Fix typos, grammar errors
- Simplify complex sentences (target: avg 15–18 words)
- Remove repetition (check for the same idea > 2x in one section)
- Improve transitions between paragraphs
2. Fact-Checking:
- For each stat/claim (10+ words), check if it has a source
- If no source, mark as [NEEDS_CITATION]
- If source is dead link, mark as [BROKEN_LINK]
- If fact sounds suspicious, mark as [NEEDS_VERIFICATION]
3. Tone & Brand Voice:
- Must be conversational, not robotic
- Avoid "As we discussed above" / "It's important to note"
- Keep personal anecdotes if they add value
- Maintain consistent POV (first person if started, don't switch)
OUTPUT: JSON feedback
{
"editor_score": 0–100,
"issues": [
{"type": "grammar", "line": 42, "original": "...", "fix": "..."},
{"type": "fact", "line": 89, "claim": "...", "status": "OK|NEEDS_CITATION|BROKEN_LINK|NEEDS_VERIFICATION"},
...
],
"overall_feedback": "Short summary",
"passing": true|false
}
SCORING RUBRIC (0–100):
- 25 pts: Grammar/style (< 3 errors, readable)
- 25 pts: Citations complete (100% claims have sources)
- 25 pts: Facts verified (< 5% need manual check)
- 25 pts: Tone matches brand (personal, expert, conversational)
If score < 80: list what needs fixing. Writer Agent will revise.
"""
Quality Check Code
def validate_editor_output(editor_json: dict) -> dict:
"""Validate Editor's output."""
issues = editor_json.get("issues", [])
score = editor_json.get("editor_score", 0)
# Count issues by type
grammar_issues = [i for i in issues if i.get("type") == "grammar"]
fact_issues = [i for i in issues if i.get("type") == "fact"]
# Validation checks
checks = []
# Grammar should be < 5 issues
if len(grammar_issues) <= 3:
checks.append(("Grammar acceptable", True, 5))
else:
checks.append((f"Too many grammar issues ({len(grammar_issues)})", False, -10))
# Facts: < 20% need citation
needs_citation = [i for i in fact_issues if i.get("status") == "NEEDS_CITATION"]
if len(needs_citation) == 0:
checks.append(("All facts cited", True, 10))
elif len(needs_citation) <= 1:
checks.append((f"{len(needs_citation)} fact needs citation", True, 0))
else:
checks.append((f"Too many uncited facts ({len(needs_citation)})", False, -15))
# Broken links: should be 0
broken = [i for i in fact_issues if i.get("status") == "BROKEN_LINK"]
if len(broken) > 0:
checks.append((f"{len(broken)} broken links found", False, -20))
final_score = score + sum(c[2] for c in checks)
return {
"editor_score": max(0, min(100, final_score)),
"checks": checks,
"passing": final_score >= 80
}
3. SEO Optimizer Agent
Промпт
SEO_PROMPT = """
You are an SEO expert. Your job: optimize blog post for search without making it unreadable.
INPUT:
- blog_post: MDX from Editor
- target_keyword: "автоматизация контента AI агенты"
- similar_urls: URLs of top-3 ranking articles
YOUR TASK:
1. Keyword Placement:
- H1: Include target keyword naturally (1x)
- First 100 words: Include keyword variant once
- H2 headers: Include keyword or synonym in 2–3 headers
- Body: Keep density 1–2% (= ~1 mention per 100 words for 3500-word post)
- NOT SPAM: Never force keyword; readability > SEO
2. Internal Linking:
- Find 5–7 existing blog posts to link to
- Link early (first occurrence of relevant term)
- Use descriptive anchor text (not "click here")
- Examples: /blog/crewai-..., /calculators/content-roi/, /ratings/...
3. Schema Markup:
- Add JSON-LD for Article type
- Add FAQ schema (from frontmatter faq)
- Add HowTo if applicable
- Validate against schema.org
4. Meta Tags:
- Meta Title: < 70 chars, includes keyword + emotion/number
- Meta Description: 150–160 chars, includes keyword, CTR-friendly
- Example title: "AI-редакция: конвейер контента за 2 часа (80% дешевле)"
- Example description: "Автоматический конвейер: 5 AI-агентов, 100 статей в месяц, $1500/месяц вместо $500K/месяц на штат."
OUTPUT:
- Updated MDX with:
- Optimized H1, H2s
- Internal links (5–7)
- JSON-LD schema blocks
- Frontmatter: seo.metaTitle, seo.metaDescription
- SEO score (0–100)
SCORING RUBRIC:
- 20 pts: Keyword placement natural (not spam)
- 20 pts: Internal links (5–7, relevant, good anchor text)
- 20 pts: Schema markup valid & complete
- 20 pts: Meta title/description optimized
- 20 pts: Readability not decreased (score < 50)
If score < 75: what's wrong? (e.g., "Keyword forced in H2, removed it")
"""
Quality Check
def validate_seo_output(mdx_with_seo: str) -> dict:
"""Validate SEO Optimizer output."""
score = 0
issues = []
# 1. Check keyword density
import re
body_text = re.sub(r'^---.*?---', '', mdx_with_seo, flags=re.DOTALL)
keyword = "автоматизация контента"
keyword_count = len(re.findall(keyword, body_text.lower()))
word_count = len(body_text.split())
density = (keyword_count / word_count) * 100 if word_count > 0 else 0
if 1 <= density <= 2.5:
score += 20
elif density > 2.5:
issues.append(f"Keyword density too high ({density:.1f}%)")
else:
issues.append(f"Keyword density too low ({density:.1f}%)")
# 2. Check internal links
internal_links = len(re.findall(r'\[.*?\]\(/blog/|/calculators/|/ratings/', mdx_with_seo))
if 5 <= internal_links <= 8:
score += 20
else:
issues.append(f"Wrong number of internal links ({internal_links}, need 5–7)")
# 3. Check JSON-LD schema
if '"@type":"Article"' in mdx_with_seo and '"mainEntity"' in mdx_with_seo:
score += 20
else:
issues.append("Missing JSON-LD Article schema")
# 4. Check meta tags
meta_title = re.search(r'metaTitle:\s*"([^"]+)"', mdx_with_seo)
meta_desc = re.search(r'metaDescription:\s*"([^"]+)"', mdx_with_seo)
if meta_title and 30 <= len(meta_title.group(1)) <= 70:
score += 10
else:
issues.append("Meta title missing or wrong length")
if meta_desc and 150 <= len(meta_desc.group(1)) <= 160:
score += 10
else:
issues.append("Meta description missing or wrong length")
# 5. Check H1 placement and keyword
if re.search(rf'^# .*{keyword}', mdx_with_seo, re.MULTILINE | re.IGNORECASE):
score += 20
else:
issues.append("H1 doesn't include target keyword")
return {
"seo_score": score,
"passing": score >= 75,
"issues": issues,
"metrics": {
"keyword_density": f"{density:.1f}%",
"internal_links": internal_links,
}
}
4. Publisher Agent — финальная QA и публикация
PUBLISHER_PROMPT = """
You are the final QA checkpoint. Your job: validate everything before publishing.
CHECKLIST:
1. ✓ All images have src, width, height, loading="lazy", alt text
2. ✓ No broken links (check 200 status for all URLs)
3. ✓ Frontmatter valid YAML (no special chars breaking it)
4. ✓ No `<pre><code>` blocks (only fenced ```code blocks)
5. ✓ All `<div>` tags on separate lines with empty line after </div>
6. ✓ No `type="number"` inputs (only `type="text" inputmode="decimal"`)
7. ✓ `<script>` tags have `defer` attribute
8. ✓ MDX parses without errors (test parse in astro build)
9. ✓ og:image exists and is valid
10. ✓ publishedAt is valid ISO 8601 and < 20:00 UTC (not 21+ UTC = next day in MSK)
If any fail: return issues, don't publish.
If all pass: commit to git + push to remote + post to Telegram.
OUTPUT JSON:
{
"publisher_score": 0–100,
"all_checks": [true, true, true, ...],
"issues": [],
"action": "PUBLISH|HOLD",
"git_commit": "...",
"telegram_post_url": "..."
}
"""
Метрики: как измерить качество конвейера
Создай таблицу в Google Sheets (или просто логируй в SQLite):
| Дата | Topic | Writer Score | Editor Score | SEO Score | Publisher Score | Avg Score | Доработок (%) | Time (hours) | Traffic (7d) | Конверсия |
|---|---|---|---|---|---|---|---|---|---|---|
| 2026-03-26 | AI-редакция | 78 | 83 | 79 | 92 | 83 | 0 | 3.0 | 340 | 2.4% |
| 2026-03-27 | Конвейер контента | 72 | 81 | 76 | 85 | 78 | 1 | 4.5 | 210 | 1.8% |
Ключевые метрики:
-
Avg Score (среднее по 4 агентам)
- Target: > 82/100
- Если < 75 — пересмотри промпты
-
% Статей, нуждающихся в доработке
- Target: < 20%
- Если > 25% — что-то сломалось в конвейере
-
Time (идея → публикация)
- Target: 2–4 часа
- Если > 6 часов — узкое место (обычно Editor или SEO Agent)
-
Traffic (трафик в неделю)
- Track organic traffic от AI-статей vs человеческих
- Если AI-статьи получают 30%+ трафика от человеческих — success
-
Conversion Rate
- Track конверсию (email signup, calc usage, contact form)
- Target: ≥ 1.5% (зависит от ниши)
Чек-лист: запуск конвейера на своём сервере
- Python 3.10+ установлен
- CrewAI установлен:
pip install crewai - Claude API ключ получен (https://console.anthropic.com)
- Redis запущена (для очередей)
- Создана папка проекта:
mkdir content-factory && cd content-factory - Создан crew.py с агентами (скопируй примеры выше)
- Создан main.py с orchestration кодом
- Проверена интеграция с твоим CMS/git
- Протестирована на 1 идее (не запускай на 100)
- Настроена отправка в Telegram (@lexamarketolog как пример)
- Логирование настроено (scores в SQLite)
- Backup идей + брифов настроен (если API упадёт)
Типичные ошибки и как их избежать
❌ Ошибка 1: Слишком короткие или слишком строгие промпты
Проблема: Промпт на 5 строк → агент пишет вслепую → качество 50/100.
Решение: Промпт должен быть 300–500 слов. Включай:
- Определение роли
- Входные данные (структура JSON)
- Выходной формат (примеры)
- 3–5 примеров успешных работ (copypaste примеры хороших статей прямо в контекст)
- Scoring rubric (как себя оценить)
❌ Ошибка 2: Один LLM для всех задач (GPT-3.5 для всего)
Проблема: GPT-3.5 халтурит на редактуре, галлюцинирует факты.
Решение: Используй Claude для Writer + Editor (лучше на долгом контексте). GPT-4 оставь для Publisher (он快 и дешёв для финальной QA).
❌ Ошибка 3: Нет системы scoring между этапами
Проблема: Writer пишет так себе → Editor не знает, что делать → статья с ошибками выходит в свет.
Решение: Каждый агент выставляет себе score JSON в конце (не словесное описание). Если score < threshold — идёт на retry с feedback.
❌ Ошибка 4: Забыл про fact-checking перед публикацией
Проблема: AI выдумала цифру (GPT: “85% компаний используют AI”) без источника → попала в статью → потом жалобы читателей.
Решение: Fact-checking перед Publisher’ом. Каждый claim > 20 слов должен иметь источник. Если нет — блокируй публикацию.
❌ Ошибка 5: Тестируешь конвейер на 100 идеях одновременно
Проблема: API перегружена, очередь повисла, 50 статей с ошибками вышли в свет.
Решение: Start small: протестируй на 1 идее, потом 5, потом 20. Растить масштаб постепенно. Добавь rate limiting (максимум 10 одновременных Worker’ов).
Кейс: реальные числа (что я видел на production)
Компания: EdTech startup, 50K читателей в месяц До конвейера: 10 статей в месяц, 3 редактора, $15K/месяц на их зарплаты После конвейера (месяц 1): 45 статей в месяц, $1800 на ресурсы + CrewAI Качество: Average score 81/100, 18% доработок, 2–4 часа на статью
Результат (месяц 3):
- 120 статей в месяц
- Трафик вырос на 240% (с 50K до 120K UU/месяц)
- Конверсия + 15% (больше контента = больше точек входа)
- Экономия: $15K/месяц − $1.8K/месяц = $13.2K в месяц (158K/год)
Минус: 3% статей содержали ошибки (галлюцинации factов). Пришлось добавить human-in-the-loop для medical/finance контента.
Итоговая архитектура (для copy-paste в свой проект)
# crew_runner.py — главный оркестратор
from crewai import Crew
from agents import (
trend_scout_agent,
writer_agent,
editor_agent,
seo_agent,
publisher_agent
)
from tasks import (
trend_scout_task,
write_task,
edit_task,
seo_task,
publish_task
)
# Создай crew (оркестратор агентов)
crew = Crew(
agents=[
trend_scout_agent,
writer_agent,
editor_agent,
seo_agent,
publisher_agent
],
tasks=[
trend_scout_task,
write_task,
edit_task,
seo_task,
publish_task
],
verbose=True,
process="sequential", # Один за другим (не параллельно)
)
# Запусти конвейер
def run_content_factory():
ideas = crew.kickoff()
return ideas
if __name__ == "__main__":
results = run_content_factory()
# results → JSON с metrics по каждому этапу
print(results)
Что дальше?
Эта серия из 2 частей закрывает теорию + практику AI-редакции.
Что ещё нужно реальному бизнесу:
- Интеграция с Analytics — отслеживать трафик/конверсию AI-статей
- A/B тестирование — какой LLM лучше (Claude vs GPT)
- Fine-tuning промптов на своих данных (история успешных статей)
Всё это в Roadmap Q2 2026.
- Часть 1: Архитектура конвейера
- Часть 2: Промпты, метрики, QA (текущая)
📲 Обсудить и задать вопросы
В Telegram-канале @lexamarketolog выходят оперативные разборы — без воды, с кодом и реальными ошибками из production. Подпишись.
Также: видео на MAX · разборы в ВК · сторис @loading_express
Источники
Читайте также
Часто задаваемые вопросы
- Какой LLM выбрать для агентов: Claude, GPT или другой?
- Claude Opus рекомендуется как основной выбор для всех ролей (Writer, Editor, SEO, Publisher). Причины: лучше следует инструкциям, меньше галлюцинаций, поддерживает контекст до 200K токенов (можешь в одном вызове передать весь бриф + примеры + feedback). Стоимость примерно как GPT-4, но качество выше на 15–20%.
- Как настроить scoring между этапами конвейера?
- Каждый агент выставляет себе score по JSON-схеме: {score: 0–100, issues: [...], passing: boolean}. Если score < threshold (Writer 75, Editor 80, SEO 75, Publisher 90), статья идёт на retry с feedback. После 3 неудачных попыток — escalation на manual review. Сохраняй все scores в БД для анализа.
- Нужна ли человеческая проверка перед публикацией?
- Для экспертного контента (finance, medical, legal) — ДА, 100% human-in-the-loop перед Publisher Agent. Для lifestyle/news — можно обойтись if Publisher Score > 88. В промежуточных случаях (tech, marketing) — spot-check 10% статей в неделю.
- Как интегрировать с CrewAI: нужна ли база данных или сойдёт файловая система?
- Минимум: Redis для очередей + SQLite для логирования scores. Максимум: PostgreSQL + Redis + Kafka (для high-volume). Для старта подойдёт просто очередь в памяти (не подходит для production, но учи на нём). Код примеров ниже использует Redis.
- Как часто проверять качество конвейера? Какие метрики смотреть?
- Еженедельно: средний Writer/Editor/SEO/Publisher score, % статей > thresholds, % доработок. Ежемесячно: A/B test трафик & конверсию от AI-статей vs человеческих, дольить ошибки в конвейер. Ежеквартально: полный аудит 5–10 статей с экспертом (маркетолог + редактор).
Пока без комментариев. Будьте первым.