Генерация структурированных отчётов
Шаблонные отчёты: резюме для руководства, результаты, доказательства и рекомендации.
«Генерация структурированных отчётов» — бесплатный урок AI Agents на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
От фактов к удобному для чтения отчету
Агент research, который выдает только список фактов, неудобен в использовании. Лицам, принимающим решения, нужны структурированные отчеты: резюме для руководства, справочная информация, основные выводы, доказательства и рекомендации.
На этом уроке рассматривается создание профессиональных отчетов по разделам на основе проверенных фактов.
Шаблон отчета
Заранее определите структуру отчета. LLM обобщает материал по одному разделу за раз, сохраняя фокус каждого раздела и избегая повторений.
REPORT_SECTIONS = [
'executive_summary',
'background',
'key_findings',
'evidence',
'recommendations',
'sources'
]
SECTION_PROMPTS = {
'executive_summary': 'Write a 2-3 sentence executive summary of the key findings. Business audience.',
'background': 'Provide background context for the research topic (3-5 sentences).',
'key_findings': 'List 3-5 key findings as bullet points, each with one supporting fact.',
'evidence': 'Summarize the evidence for each finding with inline source citations.',
'recommendations': 'Based on the findings, provide 2-4 actionable recommendations.',
'sources': 'List all sources cited in the report, formatted as numbered URLs.'
}
if __name__ == '__main__':
print('Report sections:', REPORT_SECTIONS)
for section in REPORT_SECTIONS:
print(f'{section}: {SECTION_PROMPTS[section]}')
Создание по одному разделу за раз
Создание всего отчета в одном запросе ненадежно: LLM теряет из виду факты и структуру. Создавайте отчет по разделам, передавая предыдущие разделы в качестве контекста.
import openai
client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')
def generate_section(section_name: str, question: str,
facts: list[dict], prior_sections: dict) -> str:
facts_text = '\n'.join(
f'- {f["fact"]} (source: {f["source"]})' for f in facts[:25]
)
prior_text = '\n\n'.join(
f'## {k.replace("_"," ").title()}\n{v}'
for k, v in prior_sections.items()
)
prompt = (
f'Research question: "{question}"\n\n'
f'Verified facts:\n{facts_text}\n\n'
f'Report so far:\n{prior_text}\n\n'
f'Now write the "{section_name}" section.\n'
f'Instructions: {SECTION_PROMPTS[section_name]}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}]
)
return resp.choices[0].message.contentИтеративное создание отчета
Последовательно обработайте все разделы, передавая накапливающийся контекст. Каждый раздел учитывает предыдущие, что обеспечивает связность и предотвращает противоречия внутри отчета.
def build_report(question: str, facts: list[dict]) -> dict:
report = {}
sections_to_generate = [s for s in REPORT_SECTIONS if s != 'sources']
for section in sections_to_generate:
print(f'Generating section: {section}...')
report[section] = generate_section(
section_name=section,
question=question,
facts=facts,
prior_sections={k: v for k, v in report.items()}
)
# Sources section: generate citation list from fact URLs
unique_sources = list(dict.fromkeys(f['source'] for f in facts))
report['sources'] = '\n'.join(
f'{i+1}. {url}' for i, url in enumerate(unique_sources[:20])
)
return reportДобавление встроенных ссылок на источники
Заменяйте ссылки на факты в созданных разделах пронумерованными ссылками на источники, ведущими к списку источников. Благодаря этому каждое утверждение можно отследить.
import re
def inject_citations(section_text: str, facts: list[dict]) -> str:
source_index = {} # url -> int
for i, fact in enumerate(facts):
url = fact['source']
if url not in source_index:
source_index[url] = len(source_index) + 1
annotated = section_text
for fact in facts:
if fact['fact'] in annotated:
num = source_index[fact['source']]
annotated = annotated.replace(
fact['fact'],
f'{fact["fact"]} [{num}]',
1 # replace first occurrence only
)
return annotated
if __name__ == '__main__':
demo_text = 'Revenue grew 12% last quarter. The team also launched two new products.'
demo_facts = [{'fact': 'Revenue grew 12% last quarter', 'source': 'https://example.com/report'}]
print(inject_citations(demo_text, demo_facts))
Форматирование в Markdown
Представляйте разделы отчета в формате Markdown. Это позволяет преобразовать результат в HTML и PDF или напрямую отображать его в таких инструментах, как Notion и Confluence.
def render_markdown(report: dict, title: str) -> str:
section_titles = {
'executive_summary': 'Executive Summary',
'background': 'Background',
'key_findings': 'Key Findings',
'evidence': 'Evidence',
'recommendations': 'Recommendations',
'sources': 'Sources'
}
lines = [f'# {title}', '']
for key in REPORT_SECTIONS:
if key in report:
lines.append(f'## {section_titles[key]}')
lines.append('')
lines.append(report[key])
lines.append('')
return '\n'.join(lines)
# Usage:
# md = render_markdown(report, 'Causes of Inflation in 2024')
# with open('report.md', 'w') as f: f.write(md)Создание отчета со ссылками на источники
При подготовке отчета для интернета преобразуйте URL-адреса источников в гиперссылки. Также добавляйте блок метаданных с датой создания отчета, количеством источников и долей проверенных утверждений.
from datetime import date
def render_html_report(report: dict, title: str,
facts: list[dict], question: str) -> str:
meta = (
f'<p><em>Generated: {date.today()} | '
f'Sources: {len(set(f["source"] for f in facts))} | '
f'Research question: {question}</em></p>'
)
html_parts = [f'<h1>{title}</h1>', meta]
section_titles = {
'executive_summary': 'Executive Summary',
'background': 'Background',
'key_findings': 'Key Findings',
'evidence': 'Evidence',
'recommendations': 'Recommendations',
'sources': 'Sources'
}
for key in REPORT_SECTIONS:
if key in report:
content = report[key].replace('\n', '<br>')
html_parts.append(f'<h2>{section_titles[key]}</h2><p>{content}</p>')
return '\n'.join(html_parts)Проверка качества перед публикацией
Перед передачей отчета выполните автоматические проверки качества: минимальное количество слов в каждом разделе, не менее N ссылок на источники, все рекомендации должны начинаться с глаголов действия, а текста-заполнителя вроде «[INSERT]» быть не должно.
def quality_check(report: dict) -> list[str]:
issues = []
for section in ['executive_summary', 'background', 'key_findings']:
word_count = len(report.get(section, '').split())
if word_count < 30:
issues.append(f'{section} too short: {word_count} words (min 30)')
if report.get('sources', '').count('http') < 3:
issues.append('Less than 3 cited sources')
if '[INSERT]' in str(report) or 'TODO' in str(report):
issues.append('Report contains placeholder text')
recs = report.get('recommendations', '')
if recs and not any(verb in recs.lower()
for verb in ['should', 'recommend', 'consider', 'implement']):
issues.append('Recommendations may not be actionable')
return issues
if __name__ == '__main__':
demo_report = {
'executive_summary': 'Too short.',
'background': 'Also short.',
'key_findings': 'Short too.',
'sources': 'http://a.com',
'recommendations': 'Looks fine as is.',
}
for issue in quality_check(demo_report):
print('-', issue)
Резюме для руководства: строгие ограничения
Резюме для руководства должно быть самодостаточным: человек, прочитавший только этот раздел, должен понять основной вывод и рекомендуемое действие. Используйте строгий запрос с ограничением на количество слов.
def generate_executive_summary(question: str, facts: list[dict],
max_words: int = 80) -> str:
top_facts = '\n'.join(f'- {f["fact"]}' for f in facts[:10])
prompt = (
f'Research question: "{question}"\n\n'
f'Key facts:\n{top_facts}\n\n'
f'Write an executive summary in EXACTLY {max_words} words or fewer.\n'
f'Format: [Main finding]. [Why it matters]. [Recommended action].'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}]
)
return resp.choices[0].message.contentОтчеты для разных аудиторий
Для одной и той же research могут потребоваться разные стили отчета для разных аудиторий: подробный technical-разбор для инженеров, резюме для руководства и изложение простым языком для неспециалистов. Создавайте каждый вариант на основе одного и того же набора фактов.
AUDIENCE_STYLES = {
'executive': 'Brief, strategic. Avoid jargon. Focus on business impact and decisions.',
'technical': 'Detailed, precise. Include methodology, caveats, and data sources.',
'general': 'Plain language. Avoid technical terms. Use analogies where helpful.'
}
def generate_for_audience(facts: list[dict], question: str, audience: str) -> str:
style = AUDIENCE_STYLES.get(audience, AUDIENCE_STYLES['general'])
facts_text = '\n'.join(f'- {f["fact"]}' for f in facts[:20])
prompt = (
f'Synthesize these facts into a report for a {audience} audience.\n'
f'Style guide: {style}\n'
f'Question: "{question}"\n\n'
f'Facts:\n{facts_text}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}]
)
return resp.choices[0].message.contentСохранение отчетов и управление версиями
Сохраняйте отчеты с временной меткой и хешем вопроса research. Это позволяет сравнивать версии отчетов, если research запускается повторно с обновленными источниками.
import hashlib, json, os
from datetime import datetime, timezone
def save_report(report: dict, question: str, output_dir: str = '/tmp/reports'):
os.makedirs(output_dir, exist_ok=True)
q_hash = hashlib.md5(question.encode()).hexdigest()[:8]
timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M')
filename = f'{output_dir}/report_{q_hash}_{timestamp}.json'
with open(filename, 'w') as f:
json.dump({
'question': question,
'generated': timestamp,
'sections': report
}, f, indent=2)
print(f'Report saved: {filename}')
return filename
if __name__ == '__main__':
import tempfile
demo_dir = tempfile.mkdtemp()
save_report({'executive_summary': 'AI agent adoption grew significantly in 2026.'},
'What are the key AI agent trends in 2026?', output_dir=demo_dir)
Какой раздел всегда должен быть самодостаточным в структуре отчета
Структура отчета спроектирована так, чтобы читатели, ограниченные во времени, могли извлечь пользу, не читая весь документ. Понимание того, какой раздел должен быть самостоятельным, важно для design отчета.
Итоги создания структурированных отчетов
Создавайте отчеты по одному разделу за раз по фиксированному шаблону: резюме для руководства → справочная информация → основные выводы → доказательства → рекомендации → источники. Добавляйте встроенные ссылки на источники, выполняйте проверки качества и поддерживайте разные стили для разных аудиторий на основе одного набора фактов.
Всегда сохраняйте отчеты с временными метками и хешами вопросов для управления версиями.
Часто задаваемые вопросы
Урок «Генерация структурированных отчётов» бесплатный?
Да — полный текст урока «Генерация структурированных отчётов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Генерация структурированных отчётов»?
Шаблонные отчёты: резюме для руководства, результаты, доказательства и рекомендации. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Генерация структурированных отчётов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Проектирование многоэтапного исследовательского цикла
- Проверка источников и цитирование
- Генерация структурированных отчётов
- Проверка фактов и предотвращение галлюцинаций