Головна / Статті

Десять способів усунути галюцинації без фін-тюнінгу

Десять корекцій на рівні системи, які зменшують галюцинації ШІ у електронному комерційному агенті: кращий контекст, пошук, графи, інструменти, перевірка та оцінка, без необхідності фін-тюнінгу.

Видавець: REACTAPP.TOP9 хв читання2156 слів

Джерела: 10 Ways to Reduce AI Hallucinations Without Fine-Tuning Your Model

Коли асистент повідомляє клієнту «Ваше повернення грошей оброблено», а насправді повернення не існує, причиною зазвичай не є сама модель. Система навколо неї мала прогалину, і модель заповнила її припущенням. Статус замовлення був відсутній, отримані дані були некоректними, контекст був занадто складним, виклики інструментів не перевірялися, а попередні успіхи були забуті.

Ви можете значно зменшити галюцинації, не торкаючись ваг моделі. Приклад, наведений тут, — це асистент для електронної комерції, який перевіряє замовлення, видає повернення грошей та оновлює інформацію про доставку, і ми будемо поступово зміцнювати його функціонал.

Проста версія

Клієнт пише: «Де моє замовлення №48291?» Перша версія додатку просто передає це запитання далі:

const response = await client.responses.create({
  model: "gpt-5",
  input: `
    Where is order #48291?
  `
});

console.log(response.output_text);

Модель може відповісти: «Ваше замовлення вже відправлене та буде доставлене сьогодні». Чудово, хіба що вона зовсім не знає, де знаходиться замовлення 48291. Вона сформувала правдоподібне речення на основі шаблонів у своїх даних навчання. Це галюцинація.

Те, що ви хочете, виглядає більш-менш так:

Customer
   ↓
AI Agent
   ↓
Order System
   ↓
Actual Order State
   ↓
AI Agent
   ↓
Response

Модель повинна шукати інформацію в реальності, а не вигадувати її.

1. Передайте контекст, який ви вже маєте

Перше правило: не змушуйте модель здогадуватися про те, що знає ваш бекенд. Скажіть, що бекенд зберігає цей запис:

{
  "orderId": "48291",
  "status": "SHIPPED",
  "carrier": "FedEx",
  "trackingNumber": "784512963",
  "estimatedDelivery": "2026-08-25"
}

Надішліть його разом із запитанням:

const context = {
  order: {
    id: "48291",
    status: "SHIPPED",
    carrier: "FedEx",
    trackingNumber: "784512963",
    estimatedDelivery: "2026-08-25"
  }
};

const response = await client.responses.create({
  model: "gpt-5",
  input: `
    Answer the customer using only the supplied order information.
    Context:
    ${JSON.stringify(context, null, 2)}
    Customer:
    Where is my order #48291?
  `
});

Тепер від моделі не вимагається знати стан замовлення. Її завдання скорочується до пояснення клієнту відомого факту, що є набагато безпечніше. Порівняйте ці два процеси:

Bad:

Customer → LLM → Answer

Better:
Customer
   ↓
Retrieve state
   ↓
Build context
   ↓
LLM
   ↓
Answer

Це є суттю інженерії контексту.

2. Спочатку отримайте інформацію, потім генеруйте

Інший клієнт запитує про політику повернення електроніки, купленої під час різдвяної розпродажу. Ця відповідь не знаходиться в моделі — вона зберігається у вашій базі даних політик, документації до продуктів, правилах промоцій та регіональних умовах. Тож отримайте її перед тим, як генерувати відповідь. Основний алгоритм RAG:

Customer Question
       ↓
    Retrieve
       ↓
     Rank
       ↓
Build Context
       ↓
      LLM
       ↓
    Answer

І мінімальна реалізація:

const results = await vectorStore.search({
  query: customerQuestion,
  topK: 10
});

const relevant = results
  .filter(item => item.score > 0.8)
  .slice(0, 5);
const context = relevant
  .map(item => item.content)
  .join("\n\n");
const answer = await generateAnswer(
  customerQuestion,
  context
);

Тут якість важливіша за кількість. Надання моделі 30 документів не робить її розумнішою. Якщо три документи мають значення, решта 27 — це зайвий шум.

3. Позбудьтеся зайвого

Уявіть собі, як асистент отримує цю купу документів:

20 retrieved documents
+
15 previous messages
+
10 previous tool responses
+
customer profile
+
product catalog
+
order history
+
promotion metadata

Тепер клієнт запитує лише: «Чи можу я повернути ці навушники?» Цій моделі не потрібні останні п’ятнадцять розмов, не пов’язані замовлення, інші лінійки продукції, закінчені пропозиції чи старі правила доставки. Краща система фільтрує інформацію перед тим, як ставити запитання:

Available Information
        ↓
Relevance Filtering
        ↓
Metadata Filtering
        ↓
Ranking
        ↓
Deduplication
        ↓
Context Compression
        ↓
LLM

У коді щось на кшталт:

const context = results
  .filter(x => x.score >= 0.82)
  .filter(x => x.metadata.category === "returns")
  .filter(x => x.metadata.region === customer.region)
  .sort((a, b) => b.score - a.score)
  .slice(0, 5)
  .map(x => x.content);

Ви не намагаєтесь максимізувати кількість токенів. Ви намагаєтесь підвищити співвідношення сигналу до шуму. Хороше правило: надайте моделі все, що їй потрібно, і нічого зайвого.

4. Використовуйте граф знань для структурованих даних

RAG підходить для документів. Проте багато запитань у електронній комерції зовсім не стосуються документів. Вони стосуються взаємозв’язків: який склад виконує це замовлення, яка оплата його оплатила, які товари беруть участь у акції, які замовлення будуть уражені, якщо склад вийде з ладу. Це проблеми графа. Спрощений граф:

Customer
   │
   └── PLACED → Order
                  │
                  ├── CONTAINS → Product
                  │
                  ├── PAID_BY → Payment
                  │
                  ├── FULFILLED_BY → Warehouse
                  │
                  └── SHIPPED_BY → Carrier

Коли користувач запитує, який склад обробляє його замовлення, система може пройти цей шлях:

Order #48291
      ↓
FULFILLED_BY
      ↓
Warehouse #17

запитом до графа на кшталт:

MATCH (o:Order {id: "48291"})
      -[:FULFILLED_BY]->
      (w:Warehouse)
RETURN w.id, w.name, w.location;

Результат стає надійним контекстом:

{
  "orderId": "48291",
  "warehouse": {
    "id": "WH-17",
    "name": "Delhi Fulfillment Center",
    "location": "Delhi"
  }
}

Розмістіть обидва підходи поруч. Без структури:

Without structured knowledge:

User → LLM
         ↓
      Guess

І з графом у циклі:

With Knowledge Graph:

User
 ↓
Entity Identification
 ↓
Graph Traversal
 ↓
Verified Relationship
 ↓
Context
 ↓
LLM

Це найбільш ефективно, коли асистент мусить міркувати, поєднуючи інформацію з кількох систем.

5. Прохайте надати докази

Не просіть лише відповіді. Попросіть відповідь разом із тим, що її підтримує. Структурована відповідь може виглядати так:

{
  "answer": "Your order is being fulfilled by Warehouse WH-17.",
  "confidence": 0.98,
  "evidence": [
    {
      "type": "order_record",
      "source": "orders_db",
      "orderId": "48291"
    },
    {
      "type": "warehouse_relationship",
      "source": "knowledge_graph",
      "warehouseId": "WH-17"
    }
  ]
}

Ви можете закріпити цю звичку за допомогою інструкцій:

For every factual claim:
1. Identify supporting evidence.
2. Use only available evidence.
3. Never invent a source.
4. If evidence is unavailable, say so.
5. Clearly distinguish facts from inference.

Тоді додаток може діяти відповідно до рівня впевненості:

if (result.confidence < 0.7) {
  return escalateToHuman(result);
}

Це змінює простий плин:

LLM → Answer

з перевірками на кожному кроці:

LLM
 ↓
Answer
 ↓
Evidence
 ↓
Confidence
 ↓
Decision

6. Надайте агенту інструменти замість того, щоб дозволяти йому здогадуватися

Іноді найкращим способом запобігання є зовсім не дозволяти ШІ відповідати. Якщо клієнт запитує, чи було здійснено повернення грошей, надайте можливість перевірки:

const tools = [{
  name: "get_refund_status",
  description: "Retrieve the current refund status for an order",
  parameters: {
    type: "object",
    properties: {
      orderId: {
        type: "string"
      }
    },
    required: ["orderId"]
  }
}];

Виконання відбувається наступним чином:

Customer
   ↓
LLM
   ↓
get_refund_status()
   ↓
Payment System
   ↓
Actual Refund State
   ↓
LLM
   ↓
Customer

Замість розпливчастої фрази „гроші мають повернутися протягом 5–7 днів“, агент може сказати, що процес повернення було розпочато вчора та він все ще у процесі, оскільки це дійсно так. Якщо реальний світ може дати відповідь, запитайте його.

7. Перевірка вхідних та вихідних даних інструменту

Лише виклик інструменту не робить агента надійним. Модель все ще може створити неправильний запит. Клієнт просить повернути 100 доларів за замовленням 48291, і модель формує такий вихід:

{
  "orderId": "48291",
  "amount": "one hundred",
  "currency": "dollars"
}

але ваш API оплати вимагає:

{
  "orderId": "48291",
  "amount": 100,
  "currency": "USD"
}

Додайте перевірку схеми, наприклад, за допомогою Zod:

import { z } from "zod";

const RefundRequest = z.object({
  orderId: z.string(),
  amount: z.number().positive(),
  currency: z.enum(["USD", "EUR", "GBP", "INR"])
});
const refundRequest = RefundRequest.parse(
  modelOutput
);

Не зупиняйтеся лише на типах. Перевірте також бізнес-правила:

if (refundRequest.amount > order.total) {
  throw new Error(
    "Refund amount exceeds order total"
  );
}

Тоді конвеєрний ланцюг стає таким:

LLM
 ↓
Schema Validation
 ↓
Business Validation
 ↓
Permission Check
 ↓
Execute
 ↓
Response Validation
 ↓
Accept / Retry / Escalate

Це має найбільше значення для агентів, які вживають реальних дій. Штучний інтелект ніколи не повинен мати остаточного слова щодо даних, які виконуються.

8. Розділяйте факти від міркувань

Тонкий, але ефективний трюк — зберігати те, що знає система, окремо від того, до чого дійшов модель:

{
  "facts": [
    "Order 48291 is shipped",
    "Order 48291 is fulfilled by Warehouse WH-17",
    "Warehouse WH-17 is currently operating"
  ],
"reasoning": [
    "The order is likely to remain on schedule"
  ],
  "conclusion": "The order is currently expected to arrive on time."
}

Вони несуть різний рівень впевненості. Перші твердження походили від систем, тоді як останнє є висновком. Коли кінцева відповідь виявляється неправильною, можна поставити одне просте запитання:

Was the fact wrong?

OR
Was the reasoning wrong?

Це набагато простіше для дебагування. Ви навіть можете відобразити внутрішнє розділення:

FACT
→ Order shipped

FACT
→ Estimated delivery: Aug 25
INFERENCE
→ Delivery is currently expected on schedule

Невелика перерва забезпечує високий рівень відстежуваності.

9. Вчіться на реальних ситуаціях, а не під час тренувань

Давайте змусимо агента покращуватися без додаткової налаштування. Клієнт просить скасувати замовлення 48291. Агент перевіряє замовлення:

{
  "orderId": "48291",
  "status": "PROCESSING",
  "cancelable": true
}

Потім виклик:

cancel_order(48291)

і отримує у відповідь:

{
  "success": true,
  "cancellationId": "CAN-83921"
}

Цей результат варто зберегти. Збережіть успішну спробу:

{
  "task": "Cancel order",
  "orderState": "PROCESSING",
  "action": "cancel_order",
  "result": "SUCCESS",
  "cancellationId": "CAN-83921"
}

Коли пізніше з’явиться схожий запит, спочатку шукайте відповідний попередній успішний випадок:

New Request
    ↓
Find Similar Successful Execution
    ↓
Retrieve Relevant Pattern
    ↓
Check Current Order State
    ↓
Generate Action
    ↓
Validate
    ↓
Execute

Невдачі також вчать. Спроба, яка закінчилася невдачею, залишає слід:

Attempt 1
    ↓
cancel_order()
    ↓
Rejected: Order already shipped
    ↓
Agent retrieves shipping information
    ↓
Explains cancellation is unavailable

Ці сліди перетворюються на знання, а цикл зворотного зв’язку виглядає так:

Execute
   ↓
Observe
   ↓
Evaluate
   ↓
Store Experience
   ↓
Improve Future Context

Назвімо це навчанням під час виконання. Повторне навчання не потрібне.

10. Оцінюйте кожну зміну

Наостанок перевірте, чи щось допомогло. Припустимо, ви змінили підхід до пошуку інформації. Фраза „Відповіді здаються кращими“ не є доказом. Створіть набір для оцінки:

[
  {
    "question": "Where is order 48291?",
    "expected": "SHIPPED"
  },
  {
    "question": "Which warehouse fulfills order 48291?",
    "expected": "WH-17"
  },
  {
    "question": "Can order 48291 be cancelled?",
    "expected": false
  }
]

Потім відстежуйте такі показники:

Answer Accuracy
Groundedness
Retrieval Precision
Tool Selection Accuracy
Tool Success Rate
Task Success Rate
Recovery Rate
Hallucination Rate
Latency
Cost

Таблиця «до» та «після» демонструє конкретні результати:

                      Before    After
Answer Accuracy        72%      95%
Groundedness           69%      97%
Tool Success           81%      98%
Hallucination Rate     17%       3%

Тепер ви не стверджуєте, що новий запит працює краще, ви це демонструєте. Саме тут інженерія ШІ починає нагадувати звичайну інженерію програмного забезпечення:

Change
  ↓
Test
  ↓
Measure
  ↓
Compare
  ↓
Improve

Усі десять шарів разом

Розташуйте десять шарів навколо LLM, і архітектура вийде ось так:

                         ┌──────────────────┐
                         │ Knowledge Graph  │
                         └────────┬─────────┘
                                  │
                         ┌────────▼─────────┐
                         │   RAG / Search   │
                         └────────┬─────────┘
                                  │
       Customer → Intent → Context Engine → LLM
                     ↑             │
                     │             ▼
                   Memory      Tool Selection
                     ↑             │
                     │             ▼
                     │          Validation
                     │             │
                     │             ▼
                     │        Real Systems
                     │             │
                     │             ▼
                     └─────── Feedback
                                   │
                                   ▼
                               Evaluation

Зверніть увагу на те, чого не сталося: модель не піддавалася доробцю. Система навколо неї покращилася.

Питання, які варто поставити перед тим, як звинувачувати модель

Першою реакцією на галюцинацію часто є думка про необхідність кращої моделі. Іноді це правильно. Однак спочатку перегляньте список перевірок.

  1. Чи був наданий правильний контекст?
  2. Чи була отримана правильна інформація?
  3. Чи був видалений непотрібний матеріал?
  4. Чи може структуровані знання дати однозначну відповідь?
  5. Чи може інструмент прочитати фактичний стан системи?
  6. Чи був перевірений результат роботи моделі?
  7. Чи були факти відокремлені від логічних міркувань?
  8. Чи навчилася система на попередніх запусках?
  9. Чи були надані докази?
  10. Чи вимірюються результати?

Ці перевірки зазвичай призводять до рішень, які є дешевшими, безпечнішими та легшими для контролю, ніж доробка моделі.

Модель — це лише один з компонентів

Корисна картинка:

              ┌───────────────────┐
              │ Knowledge + RAG   │
              └─────────┬─────────┘
                        ↓
Customer → Context → LLM → Tools → Real World
            ↑         ↓      ↓
          Memory   Reasoning Validation
            ↑         ↓
            └──── Feedback
                    ↓
                Evaluation

LLM створює можливості. Усе навколо нього забезпечує фактами, контекстом, обмеженнями, інструментами, перевіркою, пам’яттю та зворотним зв’язком, і саме це робить всю систему надійною.

Галюцинації часто є проблемою проектування системи, а не проблемою самого моделі. Кращий контекст, кращі знання, кращі інструменти, перевірений результат та навчання на основі того, що насправді сталося, допоможуть досягти більшого, ніж просто ще один етап навчання. Практичне питання зміщується від того, яку модель використовувати, до того, що модель має знати, що вона може робити та як визначити, чи вона правильно виконала завдання.