0Pricing
AI Agents · 강의

구조화된 보고서 생성

경영진 요약, 발견 사항, 근거, 권고 사항으로 구성된 템플릿 보고서를 만듭니다.

구조화된 보고서 생성은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

사실에서 읽기 쉬운 보고서로

사실 목록만 출력하는 리서치 에이전트는 사용하기 어렵습니다. 의사 결정권자에게는 경영진 요약, 배경, 주요 결과, 근거, 권장 사항으로 구성된 구조화된 보고서가 필요합니다.

이 단원에서는 검증된 사실을 바탕으로 섹션별 전문 보고서를 생성하는 방법을 다룹니다.

보고서 서식

보고서 구조를 미리 정의합니다. 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))

마크다운으로 형식 지정

보고서 섹션을 마크다운으로 렌더링합니다. 그러면 출력을 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)

출처 링크가 포함된 보고서 생성

웹에 표시할 때 출처 주소를 하이퍼링크로 변환합니다. 또한 보고서 생성일, 출처 수, 검증 비율이 포함된 메타데이터 블록을 추가합니다.

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

다중 독자 보고서

같은 리서치라도 독자에 따라 다른 보고서 형식이 필요할 수 있습니다. 엔지니어를 위한 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

보고서 저장 및 버전 관리

보고서를 시간 기록과 리서치 질문 해시와 함께 저장합니다. 그러면 출처가 업데이트된 상태에서 리서치를 다시 실행했을 때 보고서 버전을 비교할 수 있습니다.

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)

보고서 구조에서 항상 독립적으로 이해 가능해야 하는 섹션은 무엇입니까?

보고서 구조는 시간이 제한된 독자가 전체 문서를 읽지 않고도 가치를 얻을 수 있도록 설계됩니다. 어떤 섹션이 단독으로 기능해야 하는지 이해하는 것은 보고서 설계에 중요합니다.

구조화된 보고서 생성 복습

고정된 서식(경영진 요약 → 배경 → 주요 결과 → 근거 → 권장 사항 → 출처)을 사용해 섹션별로 보고서를 생성합니다. 본문 내 인용을 삽입하고 품질 검사를 실행하며, 같은 사실 집합에서 다양한 독자용 형식을 지원합니다.

버전 관리를 위해 항상 시간 기록과 질문 해시를 포함해 보고서를 저장합니다.

자주 묻는 질문

“구조화된 보고서 생성” 강의는 무료인가요?

네 — “구조화된 보고서 생성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.

“구조화된 보고서 생성”에서 뭘 배우나요?

경영진 요약, 발견 사항, 근거, 권고 사항으로 구성된 템플릿 보고서를 만듭니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

AI Agents을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“구조화된 보고서 생성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 다단계 조사 루프 설계
  2. 출처 검증 및 인용
  3. 구조화된 보고서 생성
  4. 사실 확인 및 환각 방지
← AI Agents(으)로 돌아가기