Вигадані джерела: коли AI-агент вигадує посилання і дані

Hallucinated sources виникають, коли агент посилається на документи, лінки або факти, яких насправді не існує. Чому це стається і як це виявляти.
На цій сторінці
  1. Проблема
  2. Чому це стається
  3. Які збої трапляються найчастіше
  4. Неперевірені URL-цитати (Unfetched URL citations)
  5. Snippet замість доказу (Search-as-evidence)
  6. Дрейф цитат між кроками (Citation drift)
  7. Псевдоцитати без покриття тверджень (Claim-source mismatch)
  8. Як виявляти ці проблеми
  9. Як відрізнити hallucinated sources від просто неточної відповіді
  10. Як зупиняти такі збої
  11. Де це реалізується в архітектурі
  12. Самоперевірка
  13. FAQ
  14. Пов'язані сторінки

Проблема

Запит виглядає стандартним: зібрати короткий підсумок змін у політиці сервісу і додати джерела.

У трейсах видно інше: за один run агент повернув 7 citations, але перевірка показала, що 3 джерела ніколи не фетчились, а 2 ведуть у 404. Для користувача відповідь виглядає впевненою, але вона не відтворюється.

Система не падає.

Вона просто віддає правдоподібні citations без реальних доказів.

Аналогія: уяви аудитора, який у звіті ставить посилання на "папки в архіві", але цих папок ніхто не бачив. Документ виглядає професійно, поки хтось не перевірить джерела. Hallucinated sources в агентних системах працюють так само.

Чому це стається

Hallucinated sources зазвичай виникають не через одну помилку моделі, а через відсутність жорсткого citation-контролю в runtime.

LLM має сильний bias до "повних" відповідей, тому без жорсткої верифікації модель швидше вигадує citation, ніж повертає відповідь без джерела.

У production зазвичай так:

  1. агент генерує citations як частину "повної" відповіді;
  2. search snippets помилково сприймаються як докази, хоча сторінки не відкривали;
  3. source_id не прив'язані до evidence snapshots;
  4. без верифікації citations система пропускає unfetched або невалідні джерела;
  5. якщо fail-closed не налаштований, вигадані джерела доходять до користувача.

У trace це видно як ріст citations_count при одночасному падінні citation_validity_rate.

Проблема не в одному невдалому URL.

Runtime не блокує неперевірені citations до видачі відповіді.

Які збої трапляються найчастіше

У production найчастіше видно чотири повторювані патерни hallucinated sources.

Неперевірені URL-цитати (Unfetched URL citations)

Агент цитує URL, який ніколи не проходив через http.get або kb.read у цьому run.

Типова причина: citations не обмежені source_id з evidence store.

Snippet замість доказу (Search-as-evidence)

У відповідь потрапляють "джерела" із пошукової видачі, але агент не має підтвердження фактичного вмісту сторінки.

Типова причина: search results змішані з evidence шаром.

Дрейф цитат між кроками (Citation drift)

На ранньому кроці джерело було валідним, але після retry або truncation фінальна відповідь посилається на інший документ.

Типова причина: немає стабільного зв'язку claim -> source_id -> snapshot hash.

Псевдоцитати без покриття тверджень (Claim-source mismatch)

У відповіді є citation-блок, але ключові твердження не мають відповідних джерел.

Типова причина: валідація перевіряє лише "наявність посилань", а не покриття claims.

Як виявляти ці проблеми

Hallucinated sources добре видно по поєднанню citation- і retrieval-метрик.

МетрикаСигнал hallucinated sourcesЩо робити
citation_validity_rateпадає частка цитат, що проходять перевіркуввести fail-closed verification за source_id
unfetched_source_rateу відповідях багато unfetched URLзаборонити URL-citations без evidence snapshot
source_404_rateчастина джерел не відкриваєтьсяперевіряти статус і canonical URL під час fetch
claim_without_citation_rateтвердження без прив'язки до джереладодати claim-level coverage check
citation_stop_reason_rateчасті citations:invalid у runtimeперевірити retrieval quality і policy для tools

Як відрізнити hallucinated sources від просто неточної відповіді

Не кожна неточність тексту означає вигадані джерела. Ключове питання: чи можна технічно відтворити джерело для кожного критичного твердження.

Нормально, якщо:

  • кожна citation вказує на source_id, який існує в evidence store;
  • є snapshot metadata (URL, timestamp, hash);
  • перевірка claims показує, що джерела покривають ключові висновки.

Небезпечно, якщо:

  • у відповіді є URL, яких не було у fetch-кроці;
  • цитати існують "для форми", але не покривають основні claims;
  • відповіді не можна відтворити на рівні run (run_id -> source_id -> snapshot).

Як зупиняти такі збої

Практично це виглядає так:

  1. усі джерела проходять через evidence store (snapshot + hash + timestamp);
  2. модель повертає citations лише як source_id, а не довільні URLs;
  3. citation verifier перевіряє, що всі source_id існують, пройшли fetch і дозволені політикою;
  4. якщо перевірка не пройдена, runtime повертає stop reason і safe fallback.

Мінімальний guard для валідації citations:

PYTHON
from dataclasses import dataclass
import hashlib
import time


@dataclass(frozen=True)
class EvidenceMeta:
    source_id: str
    url: str
    fetched_at: float
    text_sha256: str


class EvidenceStore:
    def __init__(self):
        self.items: dict[str, EvidenceMeta] = {}

    def add_snapshot(self, source_id: str, url: str, text: str) -> None:
        self.items[source_id] = EvidenceMeta(
            source_id=source_id,
            url=url,
            fetched_at=time.time(),
            text_sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
        )

    def has(self, source_id: str) -> bool:
        return source_id in self.items


def verify_citations(cited_source_ids: list[str], store: EvidenceStore) -> str | None:
    # cited_source_ids are expected to come from structured output
    if not cited_source_ids:
        return "citations:missing"

    unknown = [sid for sid in cited_source_ids if not store.has(sid)]
    if unknown:
        return "citations:unknown_source_id"

    return None

Це базовий guard. У production його зазвичай доповнюють claim-level coverage check, allowlist для citation tools і окремим stop reason для unfetched URLs. verify_citations(...) викликають до фінального рендеру відповіді, щоб користувач не бачив невалідні джерела.

Де це реалізується в архітектурі

У production контроль hallucinated sources майже завжди розкладений між трьома шарами системи.

Tool Execution Layer відповідає за fetch evidence: статус відповіді, нормалізацію URL, snapshots і hash. Якщо цей шар не зберігає докази, citations неможливо надійно верифікувати.

Agent Runtime контролює structured output, citation verification, stop reasons і fail-closed fallback. Саме тут вирішується, чи потрапить відповідь до користувача.

Memory Layer тримає зв'язок між run і evidence: run_id, source_id, retention і відтворюваність. Без цього шару команда не зможе провести нормальний аудит інциденту.

Самоперевірка

Швидка перевірка перед релізом. Відмічайте пункти і дивіться статус нижче.
Це короткий sanity-check, а не формальний аудит.

Прогрес: 0/8

⚠ Є сигнали ризику

Бракує базових контролів. Закрийте ключові пункти цього чекліста перед релізом.

FAQ

Q: Чи можна просто попросити модель "завжди додавати джерела"?
A: Можна, але цього недостатньо. Без runtime-верифікації citations це лише формат відповіді, а не доказ.

Q: Search results можуть бути джерелом?
A: Зазвичай ні. Search дає лише кандидати на джерела. Доказом стає лише те, що пройшло fetch і збережене як snapshot.

Q: Чи обов'язково зберігати повний текст джерела?
A: Не завжди. Мінімум для аудиту: URL, timestamp, hash і стабільний source_id. Повний текст додають там, де потрібен replay або точні цитати.

Q: Що показувати користувачу, якщо citations невалідні?
A: Явний stop reason, що вже перевірили, і безпечний наступний крок: часткова відповідь без неперевірених джерел або повторний запуск із верифікацією.


Інцидент hallucinated sources майже ніколи не виглядає як гучна аварія. Це тиха втрата довіри, яку зазвичай помічають лише після перевірки джерел. Тому production-агентам потрібні не лише хороші відповіді, а й жорстка citation-дисципліна.

Пов'язані сторінки

Якщо ця проблема виникла у production, корисно також подивитися:

  • Чому AI агенти ламаються — загальна карта збоїв у production.
  • Context poisoning — як проблемний контекст підштовхує агента до хибних висновків.
  • Tool failure — як нестабільні інструменти псують evidence-ланцюг.
  • Agent Runtime — де ставити structured output verification і stop reasons.
  • Tool Execution Layer — де збирати snapshots і валідувати джерела.
  • Memory Layer — де тримати відтворюваність доказів між run'ами.
⏱️ 6 хв читанняОновлено 12 березня 2026 р.Складність: ★★☆
Реалізувати в OnceOnly
Guardrails for loops, retries, and spend escalation.
Використати в OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
controls:
  loop_detection:
    enabled: true
    dedupe_by: [tool, args_hash]
  retries:
    max: 2
    backoff_ms: [200, 800]
stop_reasons:
  enabled: true
logging:
  tool_calls: { enabled: true, store_args: false, store_args_hash: true }
Інтегровано: продакшен-контрольOnceOnly
Додай guardrails до агентів з tool-calling
Зашип цей патерн з governance:
  • Бюджетами (кроки / ліміти витрат)
  • Kill switch та аварійна зупинка
  • Audit logs та трасування
  • Ідемпотентність і dedupe
  • Дозволами на інструменти (allowlist / blocklist)
Інтегрована згадка: OnceOnly — контрольний шар для продакшен агент-систем.
Приклад policy (концепт)
# Example (Python — conceptual)
policy = {
  "budgets": {"steps": 20, "seconds": 60, "usd": 1.0},
  "controls": {"kill_switch": True, "audit": True},
}

Автор

Микола — інженер, який будує інфраструктуру для продакшн AI-агентів.

Фокус: патерни агентів, режими відмов, контроль рантайму та надійність систем.

🔗 GitHub: https://github.com/mykolademyanov


Редакційна примітка

Ця документація підготовлена з допомогою AI, із людською редакторською відповідальністю за точність, ясність і продакшн-релевантність.

Контент базується на реальних відмовах, постмортемах та операційних інцидентах у розгорнутих AI-агентних системах.