0Pricing
AI Agents · 강의

출처 검증 및 인용

여러 출처에서 주장을 교차 검증하고 사실에 출처 URL을 연결합니다.

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

출처 검증이 중요한 이유

에이전트는 품질이 낮은 출처에서 거짓 주장을 가져와 사실처럼 제시할 수 있습니다. 검증이 없다면 리서치 에이전트는 품질 선별 기능이 없는 정교한 검색 엔진에 불과합니다.

검증은 여러 독립적인 출처가 주장을 뒷받침하는지 확인하는 계층을 추가합니다.

두 출처 규칙

주장이 최소 서로 독립적인 두 출처에 나타나면 검증된 것으로 간주합니다. ‘독립적’이라는 말은 서로 다른 도메인을 의미합니다. 같은 원문을 인용하는 두 사이트에서 같은 주장을 찾는 것은 해당하지 않습니다.

from urllib.parse import urlparse

def extract_domain(url: str) -> str:
    return urlparse(url).netloc.replace('www.', '')

def is_verified(fact: str, all_facts: list[dict]) -> bool:
    matching_domains = set()
    for f in all_facts:
        if fact.lower() in f['fact'].lower() or f['fact'].lower() in fact.lower():
            matching_domains.add(extract_domain(f['source']))
    return len(matching_domains) >= 2

# Example
facts = [
    {'fact': 'Inflation peaked at 9.1% in June 2022', 'source': 'https://bls.gov/cpi'},
    {'fact': 'US inflation hit 9.1% peak in mid-2022',  'source': 'https://reuters.com/article/a'}
]
print(is_verified('inflation peaked at 9.1%', facts))  # True

LLM 기반 의미 일치 판별

문자열 일치만으로는 바꾸어 표현한 주장을 놓칩니다. 두 사실을 뒷받침하는 출처로 세기 전에 의미상 같은지 LLM을 사용해 판단합니다.

import openai, json

client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')

def claims_are_equivalent(claim_a: str, claim_b: str) -> bool:
    prompt = (
        f'Are these two statements making the same factual claim?\n'
        f'A: "{claim_a}"\n'
        f'B: "{claim_b}"\n'
        f'Answer JSON: {{"equivalent": true/false}}'
    )
    resp = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        response_format={'type': 'json_object'}
    )
    return json.loads(resp.choices[0].message.content).get('equivalent', False)

print(claims_are_equivalent(
    'CPI hit 9.1% in June 2022',
    'US consumer prices rose 9.1 percent year-over-year in June 2022'
))  # True

출처 품질 점수 매기기

모든 출처의 품질이 같은 것은 아닙니다. 도메인 유형, 발행일, 알려진 신뢰도를 기준으로 각 출처에 점수를 매깁니다. 1등급 출처인 정부 통계와 동료 심사를 거친 학술지는 블로그나 소셜 미디어보다 더 큰 비중을 가져야 합니다.

from datetime import datetime, timezone

HIGH_AUTHORITY_DOMAINS = [
    'gov', 'edu', 'nature.com', 'science.org', 'pubmed.ncbi.nlm.nih.gov',
    'reuters.com', 'bbc.com', 'apnews.com', 'who.int'
]

def score_source(url: str, publication_date: str | None) -> float:
    score = 0.5   # baseline
    domain = extract_domain(url)
    tld = domain.split('.')[-1]

    # Domain authority
    if any(ha in domain for ha in HIGH_AUTHORITY_DOMAINS):
        score += 0.3
    elif tld in ('gov', 'edu', 'org'):
        score += 0.1

    # Recency
    if publication_date:
        try:
            pub = datetime.fromisoformat(publication_date)
            days_old = (datetime.now(timezone.utc) - pub.replace(tzinfo=timezone.utc)).days
            if days_old < 365:
                score += 0.1
            elif days_old > 1825:  # 5 years
                score -= 0.1
        except ValueError:
            pass

    return min(1.0, max(0.0, score))

출처와 주장 대조

에이전트가 최종 보고서에 포함하려는 각 주장을 가져온 모든 문서와 대조해 가장 잘 뒷받침하는 출처를 찾습니다.

def cross_reference(claim: str, all_facts: list[dict]) -> list[dict]:
    supporting = []
    seen_domains = set()

    for fact in all_facts:
        if claims_are_equivalent(claim, fact['fact']):
            domain = extract_domain(fact['source'])
            if domain not in seen_domains:  # only one per domain
                seen_domains.add(domain)
                supporting.append({
                    'source': fact['source'],
                    'domain': domain,
                    'fact':   fact['fact'],
                    'score':  score_source(fact['source'], fact.get('publication_date'))
                })

    return sorted(supporting, key=lambda x: x['score'], reverse=True)

저자 자격 확인

과학 또는 의학적 주장에서는 저자의 자격이 중요합니다. 공인 기관에 소속된 저자가 명시된 연구는 익명 블로그 게시물보다 신뢰할 만합니다. 저자 정보를 추출하고 익명 출처의 주장을 표시합니다.

def assess_authorship(url: str, page_text: str) -> dict:
    prompt = (
        f'From this webpage text, extract:\n'
        f'1. Author name(s) (or null)\n'
        f'2. Author affiliation/credentials (or null)\n'
        f'3. Publication name (or null)\n'
        f'Return JSON: {{"authors": [], "affiliation": null, "publication": null}}\n\n'
        f'TEXT:\n{page_text[:2000]}'
    )
    resp = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        response_format={'type': 'json_object'}
    )
    return json.loads(resp.choices[0].message.content)

검증되지 않은 주장 표시

서로 독립적인 두 출처와 대조할 수 없는 주장은 출력에서 검증되지 않음으로 표시해야 합니다. 보고서의 위험 수준에 따라 검증되지 않은 주장을 제외하거나 면책 문구와 함께 제시할 수 있습니다.

def classify_claims(claims: list[str], all_facts: list[dict]) -> list[dict]:
    classified = []
    for claim in claims:
        support = cross_reference(claim, all_facts)
        classified.append({
            'claim':       claim,
            'verified':    len(support) >= 2,
            'sources':     support[:3],  # top 3 supporting sources
            'confidence':  'high' if len(support) >= 3 else
                           'medium' if len(support) == 2 else
                           'low'
        })
    return classified

인용 형식: 본문 내 인용 및 참고문헌 목록

보고서에는 본문 내 인용(번호가 매겨진 위 첨자)과 마지막의 참고문헌 목록을 포함해야 합니다. 검증된 출처 메타데이터에서 두 가지를 모두 생성합니다.

def format_citations(classified_claims: list[dict]) -> tuple[list[str], list[str]]:
    ref_list = []
    ref_map = {}   # url -> citation number
    inline_claims = []

    for item in classified_claims:
        nums = []
        for src in item['sources']:
            url = src['source']
            if url not in ref_map:
                ref_map[url] = len(ref_list) + 1
                ref_list.append(f'[{ref_map[url]}] {url}')
            nums.append(f'[{ref_map[url]}]')

        citation_str = ''.join(nums)
        prefix = '' if item['verified'] else '[UNVERIFIED] '
        inline_claims.append(f'{prefix}{item["claim"]} {citation_str}')

    return inline_claims, ref_list

if __name__ == '__main__':
    demo_claims = [
        {'claim': 'Revenue grew 12% year over year', 'verified': True, 'sources': [{'source': 'https://example.com/report'}]},
        {'claim': 'The CEO plans to step down', 'verified': False, 'sources': [{'source': 'https://example.com/rumor'}]},
    ]
    inline, refs = format_citations(demo_claims)
    for line in inline:
        print(line)
    print('References:')
    for r in refs:
        print(' ', r)

출처 간 모순 감지

때로는 신뢰할 만한 두 출처가 서로 모순됩니다. 모순을 감지하고 한쪽을 조용히 선택하는 대신 양쪽의 주장을 제시합니다. 이는 의견이 엇갈리는 경험적 주장에 특히 중요합니다.

def find_contradictions(claim: str, all_facts: list[dict]) -> list[dict]:
    contradictions = []
    for fact in all_facts:
        if extract_domain(fact['source']) == extract_domain(claim):
            continue
        prompt = (
            f'Does fact B contradict fact A?\n'
            f'A: "{claim}"\nB: "{fact["fact"]}"\n'
            f'JSON: {{"contradicts": true/false}}'
        )
        resp = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=[{'role': 'user', 'content': prompt}],
            response_format={'type': 'json_object'}
        )
        result = json.loads(resp.choices[0].message.content)
        if result.get('contradicts'):
            contradictions.append(fact)
    return contradictions

검증 보고서 현황표

최종 보고서를 생성하기 전에 검증 요약을 작성합니다. 검증된 주장의 수, 검증되지 않은 주장의 수, 권위 점수가 높은 주요 출처, 감지된 모순을 포함합니다.

def verification_report_card(classified: list[dict]) -> dict:
    total     = len(classified)
    verified  = sum(1 for c in classified if c['verified'])
    all_sources = [
        src for c in classified for src in c['sources']
    ]
    top_domains = sorted(
        set(s['domain'] for s in all_sources),
        key=lambda d: max(s['score'] for s in all_sources if s['domain'] == d),
        reverse=True
    )[:5]
    return {
        'total_claims':    total,
        'verified':        verified,
        'unverified':      total - verified,
        'verification_rate': f'{verified/max(total,1)*100:.0f}%',
        'top_sources':     top_domains
    }

if __name__ == '__main__':
    demo_classified = [
        {'verified': True, 'sources': [{'domain': 'docs.python.org', 'score': 1.3}]},
        {'verified': False, 'sources': [{'domain': 'quora.com', 'score': 0.5}]},
    ]
    card = verification_report_card(demo_classified)
    for k, v in card.items():
        print(f'{k}: {v}')

신뢰도 기준 출력

게시하기 전에 최소 검증 기준을 설정합니다. 주장의 검증 비율이 70% 미만이면 검증되지 않은 주장을 특별히 대상으로 다음 반복을 실행하도록 리서치 반복 과정에 요청합니다.

MIN_VERIFICATION_RATE = 0.70

def should_run_more_research(classified: list[dict]) -> list[str]:
    unverified = [c for c in classified if not c['verified']]
    total = len(classified)
    if total == 0:
        return []
    rate = (total - len(unverified)) / total
    if rate >= MIN_VERIFICATION_RATE:
        return []  # Sufficient confidence — stop
    # Return search queries to verify remaining claims
    return [f'verify: {c["claim"]}' for c in unverified[:3]]

if __name__ == '__main__':
    demo_classified = [
        {'claim': 'A', 'verified': True},
        {'claim': 'B', 'verified': False},
        {'claim': 'C', 'verified': False},
    ]
    followups = should_run_more_research(demo_classified)
    print('Follow-up queries needed:', followups)

두 출처 검증 규칙에서 ‘독립적인’ 두 출처를 정의하는 기준은 무엇입니까?

독립성 기준은 여러 매체가 인용한 하나의 원출처로 같은 주장을 거슬러 올라가는 반향실식 검증을 방지합니다.

출처 검증 복습

검증된 리서치에는 서로 다른 도메인에 걸친 두 출처의 상호 뒷받침, 바꾸어 표현한 동등한 주장을 감지하는 LLM 의미 일치 판별, 출처 품질 점수 매기기(도메인 권위 + 최신성), 모순 감지, 그리고 검증 비율이 너무 낮을 때 추가 리서치를 실행하는 신뢰도 기준 출력이 필요합니다.

자주 묻는 질문

“출처 검증 및 인용” 강의는 무료인가요?

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

“출처 검증 및 인용”에서 뭘 배우나요?

여러 출처에서 주장을 교차 검증하고 사실에 출처 URL을 연결합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

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

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

“출처 검증 및 인용” 강의는 얼마나 걸리나요?

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

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

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

이 강의의 모든 강의

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