0Pricing
AI Agents · レッスン

クロスモーダル推論のパターン

画像にテキストの主張をグラウンディングし、複数ソースのマルチモーダルコンテキストを統合します。

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

クロスモーダル推論

クロスモーダル推論とは、互いに一致、矛盾、補完する可能性がある2つ以上のモダリティ(テキスト、画像、グラフ、表)から得られた情報を、エージェントが突き合わせる必要がある状況を指します。たとえば、レポートのテキストでは収益が20%増加したと書かれているのに、添付されたグラフでは横ばいになっている場合です。

エージェントは、どの情報源が正しいかを判断するか、その不一致を人間による確認に回す必要があります。

テキストと画像のグラウンディング

テキストと画像のグラウンディングとは、テキストで述べられた主張が、付随する画像で視覚的に確認できることを検証することです。たとえば、商品の説明が商品写真と一致しているか、ドキュメントに記載された表が実際に画像内に存在するかを確認します。

import anthropic
import base64

def ground_text_in_image(
    text_claim: str,
    image_path: str
) -> dict:
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(image_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        f'Text claim: "{text_claim}"\n\n'
        'Does the image above support, contradict, or partially support this claim?\n'
        'Return JSON: {"verdict": "support|contradict|partial|insufficient_evidence", '
        '"confidence": 0.0, "evidence": "..."}'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=256,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    import json
    return json.loads(response.content[0].text)

グラフとテキストの矛盾検出

現実によくある状況として、財務レポートの文章ではある内容を述べているのに、ドキュメントに埋め込まれたグラフが異なる状況を示すことがあります。エージェントはグラフのデータを視覚的に抽出し、テキスト内の数値的な主張と比較できます。

def compare_chart_to_text(
    chart_image_path: str,
    text_with_claims: str
) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(chart_image_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        'The following text makes claims about data.\n'
        f'TEXT: {text_with_claims}\n\n'
        'Compare the chart image to the text claims. '
        'List any contradictions and agreements. '
        'Return JSON: {"agreements": [str], "contradictions": [str], '
        '"verdict": "consistent|inconsistent|partial"}'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=512,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    return json.loads(response.content[0].text)

シグナルの組み合わせ戦略

複数のモダリティから得られたシグナルが一致する場合は、信頼度を高めます。意見が一致しない場合は、競合解決戦略を適用します。より構造化された情報源を信頼する(数値については文章よりグラフを優先する)、人間による確認のためにフラグを立てる、またはどちらがより信頼できるかをLLMに推論させるといった方法があります。

SIGNAL_SOURCES = {
    'chart': 0.9,     # high trust for quantitative data
    'table': 0.85,
    'photo': 0.8,
    'prose': 0.6,     # lower trust — may be imprecise or outdated
    'caption': 0.7
}

def combine_signals(signals: list) -> dict:
    """
    signals: list of {'source': str, 'claim': str, 'supports': bool}
    Returns weighted verdict.
    """
    support_weight = 0.0
    total_weight = 0.0
    for sig in signals:
        w = SIGNAL_SOURCES.get(sig['source'], 0.5)
        total_weight += w
        if sig['supports']:
            support_weight += w

    confidence = support_weight / total_weight if total_weight > 0 else 0.0
    return {
        'confidence': round(confidence, 3),
        'verdict': 'supported' if confidence >= 0.6 else 'contested',
        'needs_review': 0.4 <= confidence < 0.6
    }

if __name__ == '__main__':
    signals = [
        {'source': 'chart', 'claim': 'Revenue grew 20%', 'supports': True},
        {'source': 'prose', 'claim': 'Revenue grew 20%', 'supports': False},
    ]
    print(combine_signals(signals))

テキストと画像の検証パイプライン

商品の検証パイプラインでは、商品の説明と写真を受け取り、テキストに記載されたすべての主要な属性が画像に写っているかを確認します。一致点と不一致点を構造化レポートとして返します。

def verify_product_description(
    description: str,
    product_image_path: str
) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(product_image_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        f'Product description:\n{description}\n\n'
        'For each attribute mentioned in the description (color, shape, material, '
        'size, features), check whether it is visible in the product photo.\n'
        'Return JSON: {"verified": [{"attribute": str, "status": str}], '
        '"unverified": [str], "overall_match": float}\n'
        'status: confirmed / contradicted / not_visible\n'
        'overall_match: 0.0-1.0'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=512,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    return json.loads(response.content[0].text)

ドキュメントと画像の相互照合

スキャンしたドキュメントを分析する場合、OCRテキストと元の画像の両方が存在します。両者を照合し、抽出されたテキストが視覚的に確認できる内容と一致するかを確認します。不一致は、修正が必要なOCRエラーを示している可能性があります。

def crossref_ocr_with_image(
    ocr_text: str,
    document_image_path: str
) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(document_image_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        f'The OCR system produced this text from the document image:\n\n'
        f'{ocr_text}\n\n'
        'Compare the OCR text to what you actually see in the image. '
        'Identify any OCR errors, missed text, or hallucinated characters.\n'
        'Return JSON: {"ocr_errors": [{"incorrect": str, "correct": str}], '
        '"missed_sections": [str], "accuracy_estimate": float}'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=512,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    return json.loads(response.content[0].text)

マルチソース推論エージェント

マルチソース推論エージェントは、各モダリティから体系的に証拠を集め、シグナルを組み合わせ、信頼度スコア付きの最終結論を生成します。結論を裏付けた情報源と、反証した情報源を明示します。

def multi_source_reasoning(
    claim: str,
    evidence_sources: list  # list of {'type': 'text'|'image', 'content': str|path}
) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    content_blocks = [
        {'type': 'text',
         'text': f'Evaluate this claim using ALL sources below:\nClaim: "{claim}"\n'}
    ]
    for i, src in enumerate(evidence_sources):
        if src['type'] == 'text':
            content_blocks.append(
                {'type': 'text', 'text': f'Source {i+1} (text): {src["content"]}'}
            )
        elif src['type'] == 'image':
            with open(src['content'], 'rb') as f:
                b64 = base64.standard_b64encode(f.read()).decode('utf-8')
            content_blocks.append(
                {'type': 'text', 'text': f'Source {i+1} (image):'}
            )
            content_blocks.append(
                {'type': 'image', 'source': {'type': 'base64',
                  'media_type': 'image/jpeg', 'data': b64}}
            )
    content_blocks.append({'type': 'text', 'text':
        'Return JSON: {"verdict": str, "confidence": float, '
        '"supporting_sources": [int], "contradicting_sources": [int]}'
    })
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=512,
        messages=[{'role': 'user', 'content': content_blocks}]
    )
    return json.loads(response.content[0].text)

画像からのテキスト用事実生成

画像があり、後続のテキストベースの推論に使用する構造化された事実をそこから生成したい場合があります。グラフや表から定量的な事実をJSONとして抽出し、テキストの主張と数学的に検証できるようにします。

def extract_facts_from_chart(chart_path: str) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(chart_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        'Extract all quantitative data from this chart. '
        'Return JSON with: chart_type, x_axis_label, y_axis_label, '
        'and data_points as [{label: str, value: float}].\n'
        'Also extract any trend: increasing/decreasing/stable/volatile.'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=512,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    return json.loads(response.content[0].text)

信頼度に基づくルーティング

クロスモーダル推論後は、信頼度に基づいて結果を振り分けます。高信頼度なら自動承認、中程度なら任意の確認用にフラグを立て、低信頼度なら人間にエスカレーションします。信頼度スコアにかかわらず、矛盾するシグナルを自動承認してはいけません。

def route_cross_modal_result(result: dict) -> str:
    confidence = result.get('confidence', 0.0)
    has_contradiction = bool(result.get('contradicting_sources'))

    if has_contradiction:
        return 'ESCALATE'
    if confidence >= 0.85:
        return 'AUTO_APPROVE'
    if confidence >= 0.6:
        return 'OPTIONAL_REVIEW'
    return 'ESCALATE'

# Example routing table:
# confidence >= 0.85, no contradiction -> AUTO_APPROVE
# confidence 0.6-0.85, no contradiction -> OPTIONAL_REVIEW
# any contradiction, or confidence < 0.6 -> ESCALATE

def handle_routing(claim: str, evidence_sources: list):
    result = multi_source_reasoning(claim, evidence_sources)
    action = route_cross_modal_result(result)
    print(f'Claim: "{claim}"')
    print(f'Verdict: {result["verdict"]} (confidence={result["confidence"]:.2f})')
    print(f'Action: {action}')
    return action, result

不十分な視覚的証拠への対処

画像の解像度が低すぎたり、ぼやけていたり、一部が遮られていたりして、有用な証拠を提供できない場合があります。エージェントはこの状況を検出し、根拠のないクロスモーダルな結論を生成するのではなく、結論を出さないようにする必要があります。

def assess_image_evidence_quality(
    image_path: str,
    claim: str
) -> dict:
    import anthropic, base64, json
    client = anthropic.Anthropic(api_key='YOUR_API_KEY')
    with open(image_path, 'rb') as f:
        b64 = base64.standard_b64encode(f.read()).decode('utf-8')

    prompt = (
        f'Claim to verify: "{claim}"\n\n'
        'Assess whether this image provides sufficient evidence to evaluate the claim.\n'
        'Consider: image quality, relevance, completeness, and readability.\n'
        'Return JSON: {"sufficient": bool, "quality_issues": [str], '
        '"usable_evidence": str}'
    )
    response = client.messages.create(
        model='claude-opus-4-5', max_tokens=256,
        messages=[{'role': 'user', 'content': [
            {'type': 'image', 'source': {'type': 'base64',
              'media_type': 'image/jpeg', 'data': b64}},
            {'type': 'text', 'text': prompt}
        ]}]
    )
    return json.loads(response.content[0].text)

クロスモーダル・ファクトチェッカーの構築

ここまでの内容を組み合わせると、テキストと埋め込み画像を含むレポートを受け取り、テキスト内のすべての定量的な主張を、それを裏付けるグラフや表と照合して検証するドキュメント用ファクトチェッカーを構築できます。構造化された検証レポートを返します。

def fact_check_report(
    report_text: str,
    image_paths: list
) -> dict:
    import re
    # Extract numeric claims from text (simple regex pattern)
    claim_pattern = r'[A-Z][^.!?]*[0-9][%$][^.!?]*[.!?]'
    claims = re.findall(claim_pattern, report_text)

    results = []
    for claim in claims:
        sources = [
            {'type': 'text', 'content': report_text},
        ] + [{'type': 'image', 'content': p} for p in image_paths]

        reasoning = multi_source_reasoning(claim, sources)
        action = route_cross_modal_result(reasoning)
        results.append({
            'claim': claim,
            'verdict': reasoning['verdict'],
            'confidence': reasoning['confidence'],
            'action': action
        })

    total = len(results)
    auto_approved = sum(1 for r in results if r['action'] == 'AUTO_APPROVE')
    return {
        'total_claims': total,
        'auto_approved': auto_approved,
        'escalated': total - auto_approved,
        'details': results
    }

知識チェック

テキストとグラフのシグナルが矛盾する場合、ベストプラクティスに従うと、クロスモーダルエージェントはどうすべきですか?

振り返り:クロスモーダル推論のパターン

このレッスンは完了です。主なポイント:

  • テキストと画像のグラウンディング:テキストの主張を視覚的な証拠と照合する
  • シグナルの組み合わせ:重み付きの信頼(数値ではグラフ > 文章)、構造化された情報源を優先する
  • 矛盾検出:常に人間にエスカレーションし、矛盾を自動解決しない
  • 信頼度に基づくルーティング:自動承認(高) → 任意の確認(中) → エスカレーション(低または矛盾)
  • 不十分な証拠:根拠のないクロスモーダルな結論を生成せず、結論を控える

次のコース:IoTとセンサー駆動型エージェント — MQTTを使用した現実世界のデータストリーム処理

よくある質問

「クロスモーダル推論のパターン」レッスンは無料ですか?

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

「クロスモーダル推論のパターン」で何を学びますか?

画像にテキストの主張をグラウンディングし、複数ソースのマルチモーダルコンテキストを統合します。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

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

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

「クロスモーダル推論のパターン」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

  1. Claude Vision と GPT-4V による画像・テキストエージェント
  2. 音声・テキストエージェントのワークフロー
  3. エージェントにおける動画理解
  4. クロスモーダル推論のパターン
← AI Agentsに戻る