Strona główna / Artykuły / RAG: Od podstawowych zasad do trzech kluczowych agentów

RAG: Od podstawowych zasad do trzech kluczowych agentów

Dlaczego modele zawodzą na danych prywatnych, jak działają embeddingi i kosinusowe wyszukiwanie oraz trzy stopnie rozwoju agentów od zarządzanych bazy wiedzy do samodzielnie zarządzanego pobierania danych.

4464 słów

Zamrożone modele podstawowe nie mogą samodzielnie odpowiadać na prywatne pytania biznesowe, opierając się wyłącznie na wagach. Proces szkolenia nigdy nie obejmował wewnętrznych zasad HR, umów ani instrukcji; data zamrożenia zatrzymuje czas, a gdy brakuje faktów, generator nadal generuje spójne domysły. Dlatego nauczanie sieci tego, co wie firma, staje się problemem systemowym, a nie tylko kwestią większych punktów kontrolnych.

Dlaczego same wagi zawodzą

Trzy strukturalne ograniczenia utrudniają funkcjonowanie systemów Q&A w przedsiębiorstwach:

  1. Prywatne zbiory danych nigdy nie zostały użyte do szkolenia. Zasady przechowywania i makra nie stanowią publicznego tekstu internetowego.
  2. Czas zatrzymuje się w momencie zamrożenia. Ostatnia aktualizacja prawnicza z poprzedniego miesiąca jest niewidoczna dla zamrożonej kopii.
  3. Cisza nie jest standardem. Predykcja następnego tokena preferuje wiarygodne kontynuacje zamiast szczerego braku informacji.

Trzy sposoby wprowadzania wiedzy – i które z nich są skalowalne

Opcja 1: Dostosowanie na korpusie

Dalsze szkolenie może nadać styl lub umiejętności, jednak rozmywa fakty pomiędzy parametrami bez odniesień i nie może zapewnić widoczności dla poszczególnych użytkowników, ponieważ wszyscy korzystają z tych samych wag. W przypadku systemów Q&A opartych na zasadach, które wymagają cytowania i kontrolowania dostępu, dostosowanie na korpusie jest niewłaściwym mechanizmem.

Opcja 2: Wklejenie wszystkiego do instrukcji systemu

Dostarczanie tekstu w momencie zadania pytania przekształca proces wyszukiwania w czytanie – co jest właściwym mechanizmem. Działa to przy małych, stabilnych zbiorach danych. Utraci efektywność, gdy liczba tokenów przekracza określony limit, gdy za każdy token pobierana jest opłata przy każdym zapytaniu, gdy odpowiedzi znajdujące się w środku promptu są niskiej jakości, oraz gdy jedno statyczne wklejenie nie pozwala filtrować treści według roli użytkownika.

Opcja 3: Wklejenie tylko tego, co ważne

Zachowaj mechanizm czytania w momencie zadania pytania; popraw skalę. Pobierz kilka istotnych akapitów, a następnie wygeneruj odpowiedź. To w jednym zdaniu opisuje generowanie wzbogacone o wyszukiwanie.

Czym jest RAG, od początku do końca

W trybie offline: pobieranie plików, dzielenie ich na segmenty, włączanie tych segmentów oraz przechowywanie wektorów (oraz często indeksów leksykalnych). W trybie online: włączanie pytania, pobieranie najważniejszych segmentów, pakowanie ich razem z pytaniem w instrukcję oraz generowanie odpowiedzi opartej na tych segmentach.

Najtrudniejszą częścią nie jest ostatnia operacja generowania — chodzi o to, aby pobieranie danych było precyzyjne, tanie i uwzględniające wymagania dotyczące uprawnień.

Wektory i embeddingi bez mistyki

Wektor to lista liczb, która umieszcza fragment tekstu w przestrzeni, gdzie elementy o podobnym znaczeniu znajdują się blisko siebie. Sieć embeddingowa mapuje tekst do tej przestrzeni. Podobieństwo kosinowe porównuje kierunek bardziej niż długość, dlatego dominuje w ocenie wyników wyszukiwania. Metryki odległości, które ignorują normalizację, działają gorzej przy nierównych długościach segmentów.

Wybór wymiarowości oraz narzędzia do embedowania to decyzja oparta na wzajemnym połączeniu różnych czynników: większa liczba wymiarów może lepiej oddzielać znaczenia, ale zwiększa zużycie pamięci i opóźnienia; łączenie różnych narzędzi embedowania w czasie tworzenia indeksu i wykonywania zapytań potajemnie zakłóca funkcjonowanie wyszukiwania najbliższych sąsiadów.

Arytmetyka podobieństwa

Kosinus to iloczyn skalarny wektorów jednostkowych. Dwa fragmenty dotyczące resetowania haseł, choć sformułowane inaczej, nadal będą się znajdować blisko siebie, jeśli narzędzie embedowania udało się przenieść intencję zawartą w tekście. Wyszukiwanie według słów kluczowych przeocza tę zależność; zaawansowane metody pobierania danych ją wykrywają – ale mogą pominąć dokładne identyfikatory produktów, chyba że do procesu dołączy hibrydowe wyszukiwanie leksykalne.

Budowanie trzech kompletnych agentów w oparciu o zarządzane usługi obliczeniowe chmury

Pozostała część tego przewodnika realizuje te koncepcje poprzez trzy stopniowo rozwijające się elementy w modelach zarządzanych w stylu AWS Bedrock: starannie przygotowaną bazę wiedzy, orkiestratora LangGraph oraz samodzielny pipeline do przetwarzania danych. Każdy z tych elementów stanowi kompletny szkic agenta – należy je traktować jako architekturę gotową do wdrożenia, a nie jako krótkie fragmenty kodu.

Agent 1 — ścieżka do zarządzanej bazy wiedzy

"""
AGENT 1: Managed Knowledge Base. The 30-line version.
Bedrock does the chunking, embedding, indexing and searching. You wire it up.
"""
import boto3
from langchain_aws import AmazonKnowledgeBasesRetriever, ChatBedrockConverse
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

REGION = "eu-west-1"
KB_ID = "ABCD1234EF"

retriever = AmazonKnowledgeBasesRetriever(
    knowledge_base_id=KB_ID,
    region_name=REGION,
    retrieval_config={
        "vectorSearchConfiguration": {
            "numberOfResults": 5,
            "overrideSearchType": "HYBRID",
        }
    },
)

llm = ChatBedrockConverse(
    model="eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
    region_name=REGION,
    temperature=0,
    max_tokens=1024,
)

PROMPT = ChatPromptTemplate.from_messages([
    ("system",
     "Answer only from the numbered context. Cite every claim as [n]. "
     "If the context does not contain the answer, say so plainly."),
    ("human", "Context:\n{context}\n\nQuestion: {question}"),
])


def format_docs(docs) -> str:
    return "\n\n".join(
        f"[{i}] source={d.metadata.get('location', {}).get('s3Location', {}).get('uri', '?')}\n"
        f"{d.page_content}"
        for i, d in enumerate(docs, start=1)
    )


chain = (
    RunnableParallel(context=retriever | format_docs, question=RunnablePassthrough())
    | PROMPT
    | llm
    | StrOutputParser()
)

if __name__ == "__main__":
    print(chain.invoke("How fast must we submit a shortlist for a niche engineering role?"))

Ścieżka ta opiera się na hostowanej bazie wiedzy: dokumenty trafiają do systemu przechowywania, zarządzany indeksator tworzy embeddingi, a agent odpowiada przy użyciu cytatów dostarczanych przez ten serwis. Zaletami są mniej kodu do samodzielnego tworzenia indeksów; wadami natomiast jest mniejsza kontrola nad granicami segmentów oraz procesem fuzji danych.

Agent 2 — orkiestracja za pomocą LangGraph

"""
AGENT 2: LangGraph agent that decides WHETHER and WHICH index to search.
Retrieval is a tool. Identity travels in typed context, never in the prompt.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any

import boto3
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware, ToolCallLimitMiddleware
from langchain.tools import ToolRuntime
from langchain_aws import ChatBedrockConverse
from langchain_core.tools import tool

REGION = "eu-west-1"
POLICY_KB = "ABCD1234EF"
ENGINEERING_KB = "WXYZ5678GH"

agent_runtime = boto3.client("bedrock-agent-runtime", region_name=REGION)


@dataclass
class RequestContext:
    """Built from validated JWT claims. The model cannot read or forge it."""
    subject: str
    roles: tuple[str, ...]
    region: str


def _retrieve(kb_id: str, query: str, ctx: RequestContext, k: int = 5) -> str:
    """Bedrock Retrieve API with a server-side metadata filter for tenant + residency."""
    resp = agent_runtime.retrieve(
        knowledgeBaseId=kb_id,
        retrievalQuery={"text": query},
        retrievalConfiguration={
            "vectorSearchConfiguration": {
                "numberOfResults": k,
                "overrideSearchType": "HYBRID",
                "filter": {
                    "andAll": [
                        {"in": {"key": "acl_roles", "value": list(ctx.roles)}},
                        {"in": {"key": "region", "value": [ctx.region, "GLOBAL"]}},
                    ]
                },
            }
        },
    )
    results: list[dict[str, Any]] = resp.get("retrievalResults", [])
    if not results:
        return "NO_AUTHORISED_RESULTS: nothing in this index that you may read."
    return "\n\n".join(
        f"[{i}] score={r.get('score'):.3f} "
        f"source={r.get('location', {}).get('s3Location', {}).get('uri', '?')}\n"
        f"{r['content']['text']}"
        for i, r in enumerate(results, start=1)
    )


@tool
def search_policy_docs(query: str, runtime: ToolRuntime[RequestContext]) -> str:
    """Search HR, compliance and commercial policy.

    Use for shortlist SLAs and service credits, candidate data retention and consent,
    GDPR erasure, statement-of-work approval thresholds, background check rules and
    automated decision governance.
    """
    return _retrieve(POLICY_KB, query, runtime.context)


@tool
def search_engineering_docs(query: str, runtime: ToolRuntime[RequestContext]) -> str:
    """Search platform runbooks and integration documentation.

    Use for the requisition sync service, Workday and SuccessFactors webhooks, Kafka
    topics and event schemas, idempotency and retry behaviour, and vector index refresh.
    """
    return _retrieve(ENGINEERING_KB, query, runtime.context)


agent = create_agent(
    model=ChatBedrockConverse(
        model="eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
        region_name=REGION,
        temperature=0,
    ),
    tools=[search_policy_docs, search_engineering_docs],
    system_prompt=(
        "You are an enterprise knowledge assistant.\n"
        "- Answer only from tool results and cite the [n] markers they return.\n"
        "- If a tool returns NO_AUTHORISED_RESULTS, say you cannot access a source and "
        "name the document owner. Never fill the gap from memory.\n"
        "- For questions spanning policy and platform, call both tools before answering."
    ),
    context_schema=RequestContext,
    middleware=[
        ModelCallLimitMiddleware(thread_limit=8, run_limit=6, exit_behavior="end"),
        ToolCallLimitMiddleware(thread_limit=6, run_limit=5, exit_behavior="end"),
    ],
)

if __name__ == "__main__":
    claims = {"sub": "asha@example.com", "roles": ["recruiter"], "region": "EU"}
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "How is the vector index refreshed?"}]},
        context=RequestContext(
            subject=claims["sub"],
            roles=tuple(claims["roles"]),
            region=claims["region"],
        ),
    )
    print(result["messages"][-1].content)

Tutaj przepływ sterowania ma postać wyraźnego grafu: klasyfikacja intencji, wywołanie narzędzi do wyszukiwania, opcjonalne zapytanie człowieka, a następnie odpowiedź. Funkcje checkpointingu i przerwań przekształcają długotrwałe wątki obsługi w trwałe sesje zamiast słowników pamięciowych.

Agent 3 — Samodzielne przetwarzanie i pobieranie danych

"""
AGENT 3: Self-managed pipeline: your chunking, your index, your fusion, your rerank.
Chosen when Bedrock KB's fixed options are not enough (custom fusion, per-stage timing,
an abstention gate, or a chunking rule the managed service will not express).
"""
from __future__ import annotations
import time
from typing import Annotated, Any, TypedDict

import boto3
from langchain_aws import BedrockEmbeddings, ChatBedrockConverse
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
from langgraph.graph import END, START, StateGraph

REGION = "eu-west-1"
CONFIDENCE_FLOOR = 0.34
CANDIDATE_K, CONTEXT_K = 20, 4

embeddings = BedrockEmbeddings(
    model_id="amazon.titan-embed-text-v2:0",
    region_name=REGION,
    model_kwargs={"dimensions": 1024, "normalize": True},
)
llm = ChatBedrockConverse(
    model="eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
    region_name=REGION,
    temperature=0,
)
rerank_client = boto3.client("bedrock-agent-runtime", region_name=REGION)


# ---------------------------------------------------------------- ingest
def chunk_policy_document(markdown: str, source: str, acl: list[str], region: str):
    """Structure-aware first, size-capped second. Headings are the natural unit for
    policy text; the recursive splitter only intervenes on oversized sections."""
    by_heading = MarkdownHeaderTextSplitter(
        headers_to_split_on=[("#", "title"), ("##", "section"), ("###", "clause")]
    ).split_text(markdown)

    capper = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=200)
    chunks: list[Document] = []
    for part in capper.split_documents(by_heading):
        heading = " > ".join(
            v for k, v in part.metadata.items() if k in ("title", "section", "clause")
        )
        chunks.append(
            Document(
                # contextual retrieval: the chunk carries its own scope
                page_content=f"{heading}\n{part.page_content}",
                metadata={**part.metadata, "source": source, "acl": acl, "region": region},
            )
        )
    return chunks


# ---------------------------------------------------------------- state
class State(TypedDict, total=False):
    question: str
    roles: list[str]
    region: str
    candidates: list[Document]
    context_docs: list[Document]
    confidence: float
    answer: str
    timings: Annotated[dict[str, float], lambda a, b: {**(a or {}), **(b or {})}]


def _ms(name: str, t0: float) -> dict[str, float]:
    return {name: round((time.perf_counter() - t0) * 1000, 1)}


# ---------------------------------------------------------------- nodes
def retrieve(state: State, *, store) -> State:
    """Hybrid search with an ACL pre-filter pushed into the index query."""
    t0 = time.perf_counter()
    docs = store.similarity_search(
        state["question"],
        k=CANDIDATE_K,
        filter={"acl": {"$in": state["roles"]}, "region": {"$in": [state["region"], "GLOBAL"]}},
    )
    return {"candidates": docs, "timings": _ms("retrieve", t0)}


def rerank(state: State) -> State:
    """Bedrock Rerank: a cross-encoder scoring each (query, chunk) pair jointly."""
    t0 = time.perf_counter()
    cands = state["candidates"]
    if not cands:
        return {"context_docs": [], "confidence": 0.0, "timings": _ms("rerank", t0)}

    resp = rerank_client.rerank(
        queries=[{"type": "TEXT", "textQuery": {"text": state["question"]}}],
        sources=[
            {"type": "INLINE", "inlineDocumentSource": {
                "type": "TEXT", "textDocument": {"text": d.page_content}}}
            for d in cands
        ],
        rerankingConfiguration={
            "type": "BEDROCK_RERANKING_MODEL",
            "bedrockRerankingConfiguration": {
                "numberOfResults": CONTEXT_K,
                "modelConfiguration": {
                    "modelArn": f"arn:aws:bedrock:{REGION}::foundation-model/amazon.rerank-v1:0"
                },
            },
        },
    )
    ranked = resp["results"]
    top = [cands[r["index"]] for r in ranked]
    return {
        "context_docs": top,
        "confidence": round(float(ranked[0]["relevanceScore"]), 3),
        "timings": _ms("rerank", t0),
    }


def generate(state: State) -> State:
    """Abstain below the floor rather than answering from weak context."""
    t0 = time.perf_counter()
    if not state["context_docs"] or state["confidence"] < CONFIDENCE_FLOOR:
        return {
            "answer": "I cannot ground an answer for that in a source you are permitted "
                      "to read. Routing to the document owner.",
            "timings": _ms("generate", t0),
        }
    context = "\n\n".join(
        f"[{i}] {d.metadata.get('source')}\n{d.page_content}"
        for i, d in enumerate(state["context_docs"], start=1)
    )
    prompt = ChatPromptTemplate.from_messages([
        ("system", "Answer only from the numbered context and cite each claim as [n]."),
        ("human", "Context:\n{context}\n\nQuestion: {question}"),
    ])
    reply = (prompt | llm).invoke({"context": context, "question": state["question"]})
    return {"answer": reply.content, "timings": _ms("generate", t0)}


def build_graph(store):
    g = StateGraph(State)
    g.add_node("retrieve", lambda s: retrieve(s, store=store))
    g.add_node("rerank", rerank)
    g.add_node("generate", generate)
    g.add_edge(START, "retrieve")
    g.add_edge("retrieve", "rerank")
    g.add_edge("rerank", "generate")
    g.add_edge("generate", END)
    return g.compile()

Ta wersja obejmuje dzielenie na fragmenty, wywołania funkcji embedding, przechowywanie wektorów oraz kompilację zapytań. Zespół wybiera ją wtedy, gdy polityka segmentacji, hybrydowa fuzja leksykalna i gęsta lub filtry dotyczące użytkowników muszą stanowić elementy pierwszorzędnego kodu, a nie tylko opcje dostępne w konsoli.

Wybór spośród trzech opcji

Rozwiązanie typu managed nadaje się, gdy ważna jest szybkość realizacji demonstracji i zasoby korpusów są niewielkie. Wybierz LangGraph, gdy dominują rozgałęzienia przepływu, przegląd przez człowieka oraz przechowywanie informacji między kolejnymi krokami. Wybierz samodzielne zarządzanie procesem, gdy jakość wyszukiwania identyfikatorów, uprawnień ACL lub segmentacji domeny staje się kluczowym elementem produktu.

Ocena, która rzeczywiście poprawia jakość

Śledź Recall@k i Precision@k na zamarzniętym zbiorze pytań, a także wierność odpowiedzi wobec pobranych fragmentów. Łącz każdą zmianę wielkości segmentu lub narzędzia embedding z wynikami przed i po zmianie. Odrzuć rozwiązanie, jeśli najważniejsze fragmenty są słabe — skalibrowana cisza jest lepsza od płynnej inwencji.

Kiedy nie używać RAG

Pomijaj proces pobierania informacji w przypadku faktów, które sieć bazowa już zna, przy czysto kreatywnych zadaniach, dla ciągle zmieniających się danych, które lepiej obsłużyć za pomocą API, oraz dla ścieżek o ultra-niskiej opóźnieniu, które nie mogą sobie pozwolić na dodatkowy krok pobierania danych. Czasami wystarczy lepsze polecenie lub niewielka dostosowanie formatu.

Zakończenie

RAG to systemy infrastrukturalne: czyste źródła danych, uczciwe pobieranie informacji, precyzyjne instrukcje generowania oraz mierzalne mechanizmy kontroli. Trzy agenty przedstawiają tę samą ideę na trzech poziomach kontroli. Wybierz poziom odpowiadający Twoim potrzebom — nie ten, który brzmi najbardziej zaawansowanie na slajdzie.

Lista kontrolna operacyjna: (1) jeden narzędzie do włączania treści do indeksu i wyszukiwania, (2) metadane dotyczące uprawnień dostępu oraz dat od pierwszego dnia, (3) wyszukiwanie hybrydowe w przypadku pojawienia się identyfikatorów, (4) ponowna klasyfikacja wyników, gdy ważna jest precyzja, (5) kluczowe pytania w procesie integracji ciągłej, (6) mechanizmy odbudowy indeksu w razie zmian źródeł. Traktuj płaszczyznę wiedzy jako produkt z odpowiedzialnymi osobami, celami operacyjnymi oraz procedurami odwracania zmian – a nie jako indeks tworzony w weekendy, który nikt nie monitoruje. Gdy grafy agentów się rozrastają, mierz koszt tokenów obok jakości odpowiedzi; orkiestracja, która zużywa piętnaście razy więcej zasobów niż prosty czat bez utraty wierności treści, to regres maskowany jako architektura. Przechowuj instrukcje obsługi przypadków braku wyników, problemów z przestarzałym indeksem oraz niezgodności narzędzi do włączania treści, aby osoby pełniące dyżur nie musiały improwizować na podstawie zapisów z czatów.

Dokumentuj każde źródło korpusu, wskazując osobę odpowiedzialną oraz częstotliwość aktualizacji; bezopieczne kontenerzy stają się źródłem cichego zepsucia.

Należy preferować wczesne nakładanie się segmentów; stosować rozdzielacze semantyczne tylko wtedy, gdy oceny wykażą, że stałe okna nie działają skutecznie.

Zapisywać identyfikatory pobierania wraz z odpowiedziami, aby złe odpowiedzi można było zdiagnozować jako błędy pobierania lub odchylenia w generowaniu treści.

Gdy to możliwe, oddzielać sędziów oceniających od sieci odpowiadającej, aby zmniejszyć stronniczość wynikającą z samoprzedpozycji.

Budżet na korzystanie z API do indeksowania należy traktować oddzielnie od kosztów obsługi interaktywnej, aby dział finansowy mógł śledzić obie kategorie wydatków.

Łączyć szablony promptów wersji z procesem budowy indeksu; niewielkie zmiany w promptach bez ponownej oceny są przyczyną pojawiania się regresji.

w przypadku systemów wielu najemców należy sprawdzić, czy filtry nie można obejść poprzez wstrzykiwanie promptów podających się za role administratora.

Należy zachować mechanizm awaryjny, który wyłącza generowanie treści i zwraca jedynie cytaty, gdy monitory wierności przestaną działać prawidłowo.

Należy ponownie przeanalizować hybrydowe wagi leksykalne za każdym razem, gdy kody produktów dominują w strukturze ruchu.

Szkolij personel wsparcia w czytaniu paneli cytowań przed przekazywaniem zgłoszeń o „niewłaściwym botie” na wyższe szczeble.

Dokumentuj każde źródło korpusu, wskazując osobę odpowiedzialną oraz częstotliwość aktualizacji; bezopieczne zbiory danych stają się źródłem ukrytych problemów.

Najlepiej wcześnie stosować nakładanie się segmentów; używaj separatorów semantycznych tylko wtedy, gdy oceny wykażą, że stałe okna czasowe nie działają skutecznie.

Zapisuj identyfikatory pobierania wraz z odpowiedziami, aby złe odpowiedzi można było zdiagnozować jako błędy pobierania lub odchylenia w generowaniu treści.

Gdy to możliwe, oddzielaj sędziów oceniających od sieci odpowiadającej, aby zmniejszyć stronniczość wynikającą z preferencji własnych.

Budżet na korzystanie z API do indeksowania traktuj oddzielnie od budżetu na obsługę interaktywną, aby dział finansowy mógł śledzić obie kategorie wydatków.

Wersjonuj szablony promptów razem z budową indeksu; niewielkie zmiany w promptach bez ponownej oceny prowadzą do pojawienia się regresji.

W przypadku systemów wielu najemców sprawdzaj, czy filtry nie można obejść poprzez wstrzykiwanie promptów podających się za role administratora.

Zachowaj funkcję wyłączania, która dezaktywuje generowanie i pokazuje tylko cytaty, gdy monitorowanie wierności spadnie.

Ponownie przeanalizuj hybrydowe wagi leksykalne, gdy kody produktów dominują strukturę ruchu.

Szkolą pracowników obsługi w czytaniu paneli z cytatami przed przekazywaniem zgłoszeń o „błędnym botie” na wyższy poziom.

Dokumentuj każde źródło korpusu wraz z osobą odpowiedzialną i częstotliwością aktualizacji; bezopieczne zbiory stają się źródłem cichego zepsucia.

Najlepiej wcześnie stosować nakładanie się segmentów; używaj separatorów semantycznych tylko wtedy, gdy oceny wykażą, że stałe okna nie działają.

Zapisuj identyfikatory pobierania wraz z odpowiedziami, aby złe odpowiedzi można było zdiagnozować jako błędy pobierania lub odchylenia w generowaniu.

Gdzie to możliwe, oddziel sędziów oceniających od sieci odpowiadającej, aby zmniejszyć uprzedzenia wynikające z samoprzedpozycji.

Budżet na korzystanie z API do indeksowania traktuj oddzielnie od kosztów interaktywnego serwowania, aby dział finansowy mógł widzieć obie kwoty.

Środowiska promptów wersji obok budów indeksu; nagłe zmiany promptów bez ponownej oceny to sposób, w jaki pojawiają się regresje.

Dla systemów wielu najemców sprawdź, czy filtry nie mogą zostać obejścia poprzez wstrzykiwanie promptów podających się za role administratora.

Zachowaj mechanizm wyłączania, który dezaktywuje generowanie i zwraca tylko cytaty, gdy monitory wierności przestaną działać.

Ponownie przeanalizuj hybrydowe wagi leksykalne, gdy kody produktów dominują w strukturze ruchu.

Szkolą personel obsługi w czytaniu paneli z cytatami przed przekazywaniem zgłoszeń o „niewłaściwym botie” na wyższy poziom.

Zachowaj sparowane oceny i metryki odrzuceń obok opóźnień, aby koszt i jakość były przedstawione na jednej tabeli.

Zachowaj sparowane oceny i metryki odrzuceń obok opóźnień, aby koszt i jakość były przedstawione na jednej tabeli.

Zachowaj sparowane oceny i metryki odrzuceń obok opóźnień, aby koszt i jakość były przedstawione na jednej tabeli.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.

Zachowaj sparowane oceny oraz metryki odrzuceń obok opóźnienia, aby koszt i jakość znajdowały się na jednej karcie.