0Pricing
AI Agents · レッスン

情報源の検証と引用

複数の情報源で主張を相互参照し、事実に出典 URL を付けます。

「情報源の検証と引用」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。

情報源の検証が重要な理由

エージェントは、質の低い情報源から根拠のない主張を取得し、それを事実として提示することがあります。検証がなければ、調査エージェントは品質フィルターを持たない、高機能な検索エンジンに過ぎません。

検証によって、主張が複数の独立した情報源で裏付けられているかどうかを確認する層が加わります。

2つの情報源のルール

主張が独立した2つの情報源に掲載されている場合、その主張は検証済みと見なします。「独立」とは異なるドメインを意味します。同じ元記事を引用している2つのサイトで同じ主張を見つけても、条件を満たしません。

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による意味的マッチング

文字列の一致だけでは、言い換えられた主張を見落とします。2つの事実を裏付けとなる情報源として数える前に、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

情報源の品質スコアリング

すべての情報源が同等とは限りません。ドメインの種類、公開日、既知の信頼性に基づいて各情報源を評価します。政府統計や査読済み学術誌などのTier 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)

未検証の主張へのフラグ付け

独立した2つの情報源と照合できない主張には、出力上で未検証のフラグを付けます。レポートのリスクレベルによっては、未検証の主張を除外するか、免責事項付きで提示します。

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)

情報源間の矛盾の検出

信頼できる2つの情報源が互いに矛盾することがあります。矛盾を検出したら、一方を黙って選ぶのではなく、両方の見解を提示します。これは、意見が分かれている実証的な主張で特に重要です。

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%未満しか検証されていない場合は、未検証の主張を特に対象として、調査ループをもう1回実行するよう促します。

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)

2つの情報源による検証ルールでいう「独立した」情報源とは何ですか

独立性の基準によって、多くの媒体が同じ元の情報源を引用しているだけの、エコーチェンバー的な検証を防げます。

情報源検証の振り返り

調査を検証済みにするには、異なるドメインにまたがる2つの情報源による裏付け、言い換えられた同等の主張を検出するLLMによる意味的マッチング、情報源の品質スコアリング(ドメインの権威性+新しさ)、矛盾の検出、そして検証率が低すぎる場合に追加調査を実行する信頼度に基づく出力制御が必要です。

よくある質問

「情報源の検証と引用」レッスンは無料ですか?

はい。「情報源の検証と引用」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。

「情報源の検証と引用」で何を学びますか?

複数の情報源で主張を相互参照し、事実に出典 URL を付けます。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Agentsを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「情報源の検証と引用」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Agentsレッスンでコードを書いて実行できますか?

はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 複数ステップのリサーチループ設計
  2. 情報源の検証と引用
  3. 構造化レポートの生成
  4. ファクトチェックとハルシネーション防止
← AI Agentsに戻る