Strona główna / Artykuły / Ontology-Aware GraphRAG: Kiedy wektory wymagają typowanych relacji

Ontology-Aware GraphRAG: Kiedy wektory wymagają typowanych relacji

W jaki sposób ankiery identyfikatorów, umowy ontologiczne, fuzja rankingu oraz mechanizmy cytuj-albo-odrzuć naprawiają błędy RAG w przypadku CVE, problemów z wieloetapową własnością oraz niewyrażonych faktów grafowych.

2819 słów

Dense retrieval dobrze odpowiada na wiele pytań, ale nadal napotyka problemy w określonych sytuacjach: przy dokładnych identyfikatorach, relacjach wieloetapowych oraz faktach istniejących wyłącznie w formie struktury grafowej. Ontology-aware GraphRAG traktuje te problemy jako elementy projektu – nie jako powody do porzucenia wektorów, lecz jako uzasadnienie do dodania obok nich warstwy wiedzy typowanej.

Część 1 — Jak dokładnie działa RAG

Klasyczny RAG wstawia pytanie, pobiera najbliższe fragmenty tekstu i na ich podstawie generuje odpowiedź. W przypadku zapytania dotyczącego wrażliwości, takiego jak:

question: "path traversal apache httpd"

ranki wektorowe i leksykalne mogą się różnić:

VECTOR (cosine)                       LEXICAL (keyword overlap)
1. CVE-2021-41773  0.746  ← correct   1. CVE-2021-28544  6.60
2. CVE-2021-23797  0.735              2. CVE-2021-40525  6.47
3. CVE-2021-32643  0.728              5. CVE-2021-41773  5.57  ← correct, buried

Gdy prawidłowy CVE jest obecny, ale nie dominuje, system generuje odpowiedź bez żadnych wątpliwości. Domeny oparte na identyfikatorach (CVE, SKU, numery ticketów) szybko ujawniają tę lukę.

Część 2 — Mur, na który napotyka się RAG, mierzony

Błąd 1 — dokładne identyfikatory

Zgodność leksykalna pomaga, ale „hałaśliwi” sąsiedzi i tak wygrywają. Pobrany tekst prozatorski może nawet odrzucić takie ujęcie kwestii podatności:

[S1] "Rejected reason: This vulnerability does not meet the criteria for a
      security vulnerability…"

Następnie proces generowania odzwierciedla niewłaściwego sąsiada.

Błąd 2 — kompletność relacyjna

„Które produkty są dotknięte?” wymaga połączeń, a nie tylko najbliższego akapitu. Algorytmy podobieństwa zwracają powiązane CVE-y; nie przeszukują łączy typu AFFECTS.

Błąd 3 — fakty, których nikt nie zapisał

Część odpowiedzi istnieje jedynie jako przecięcia pomiędzy różnymi elementami — nigdy w formie zdania. Żaden fragment tekstu nie zawiera takiego połączenia; tylko graf może je przedstawić.

Część 3 — Co dodaje graf wiedzy

Węzły i krawędzie o określonym typie sprawiają, że identyfikatory i relacje są traktowane priorytetowo:

(:Vulnerability {cve_id: 'CVE-2021-41773', cvss_base_score: 9.8, cvss_severity: 'CRITICAL'})
   -[:AFFECTS {version: '2.4.49'}]-> (:Product {key: 'apache:http_server'})
   -[:HAS_WEAKNESS]->                (:Weakness {cwe_id: 'CWE-22'})

Przechodzenie po grafie umożliwia odpowiadanie na pytania relacyjne; wektory nadal są przydatne, gdy tekst prozatorski stanowi odpowiednie dowody.

Ontologia vs. graf wiedzy — różnica operacyjna

Ontologia to umowa określająca, jakie typy entytetów, jakie typy krawędzi oraz jakie właściwości są wymagane. GraphRAG to wypełniona instancja zgodna z tą umową. Bez ontologii procesy wydobywania informacji stają się niestabilne, a łączenie danych traci wiarygodność. Dzięki ontologii pipeline’y mogą weryfikować i odrzucać błędne trójki przed tym, jak zaszkodzą procesowi wyszukiwania.

Trzy znaczenia terminu „GraphRAG”

  1. Graf jako indeks — przechowywanie fragmentów tekstu, ale ich wydobywanie poprzez sąsiedztwo w grafie.
  2. Graf jako pamięć — entytety i krawędzie stanowią główne źródło danych; tekst służy jako dowód.
  3. Graf jako planer — agent planuje kroki działania, a następnie pobiera tekst.

Konstrukcje uwzględniające ontologię zazwyczaj łączą elementy (2) i (1): ustrukturyzowane fakty dla precyzji oraz tekst źródłowy w celu odwoływania się do źródeł.

Część 4 — Architektura, warstwa po warstwie

Proces pobierania wyodrębnia entity/relacje zgodnie z ontologią, zapisuje fakty grafu, przechowuje fragmenty źródłowe i jednocześnie tworzy indeks wektorowy/leksykalny na podstawie tekstu. Czas wyszukiwania umożliwia ustalenie identyfikatorów, rozszerza sąsiedztwa grafu, przeprowadza wyszukiwanie gęste/leksykalne, łączy ranki i tworzy prompt z oddzielnymi sekcjami FACTS i EVIDENCE wraz z zasadami cytowania.

Trzy decyzje wymuszone przez dane

  1. Identyfikatory służące jako punkty odniesienia przewyższają metody nieprecyzyjnego dopasowywania, gdy obecny jest token CVE/produktu.
  2. Fuzja ważona musi podnieść ranki grafu w zapytaniach dotyczących identyfikatorów, nie przytłaczając jednocześnie tekstu opisowego w zapytaniach narracyjnych.
  3. Kontrole wierności muszą odrzucić odpowiedzi, które odwołują się do brakujących tagów lub są sprzeczne z właściwościami grafu.

Część 5 — Jedno pytanie, od początku do końca

Pytanie:

Q: "which products are affected by CVE-2021-41773"

Rozwiązanie punktu odniesienia:

ANCHOR  Vulnerability  CVE-2021-41773  method=identifier  conf=1.00

Masy fuzji, gdy identyfikator służy jako punkt odniesienia:

weights = {graph: 2.0, vector: 1.0}     # identifier match
# a lexical product match would be 1.2; no anchor at all, 0.0

Listy konkurencyjne:

VECTOR  1. CVE-2021-21022 (Magento IDOR)   2. CVE-2021-27385  …
GRAPH   1. CVE-2021-41773 (anchor)         2. CVE-2021-25216 (shares netapp:cloud_backup)

Oceny fuzji ranków wzajemnych:

CVE-2021-41773   2.0/(60+1) = 0.03279   ← graph, rank 1
CVE-2021-21022   1.0/(60+1) = 0.01639   ← vector, rank 1

Fakty grafowe spakowane dla modelu:

FACTS (from the knowledge graph):
[G1] CVE-2021-41773 | CRITICAL 9.8 (CVSS 3.1) | CWE: CWE-22
     affects: apache:http_server 2.4.49, fedoraproject:fedora 34,
              fedoraproject:fedora 35, netapp:cloud_backup,
              oracle:instantis_enterprisetrack 17.1 / 17.2 / 17.3
     source: https://nvd.nist.gov/vuln/detail/CVE-2021-41773

Dowody źródłowe:

EVIDENCE (source text):
[S1] "A flaw was found in a change made to path normalization in Apache
      HTTP Server 2.4.49. An attacker could use a path traversal attack…"

Ustrukturyzowana odpowiedź z cytatami:

{"answer": "CVE-2021-41773 is CRITICAL with a CVSS base score of 9.8 [G1].
            It affects apache:http_server 2.4.49, fedoraproject:fedora 34,
            fedoraproject:fedora 35 [G1].",
 "sources": ["G1"],
 "entities": [{"label": "Vulnerability", "key": "CVE-2021-41773"}],
 "confidence": "high"}

Bramy po generowaniu:

✓ every cited tag exists in the context
✓ the answer cites something at all
✓ every CVE id in the answer appears in the context
✓ entity labels are real ontology classes
✓ numbers that look like CVSS scores match the graph facts
→ ACCEPTED

Zła odpowiedź, która nie przechodzi bram:

{"answer": "CVE-2021-41773 scores 4.3 and affects nginx [G1]."}

Powody odrzucenia:

✗ states 4.3 but the graph facts say [9.8]
✗ entity Product nginx:nginx is not in the context
→ REJECTED → one repair attempt → still bad → REFUSAL

To samo urządzenie, jeden krok dalej

Łączenie danych z magazynu przekształca „dotknięte produkty” w „dotknięte aplikacje pierwszego poziomu oraz związane z nimi zespoły”:

application        criticality  team            library  pinned    match_precision
checkout-web       tier1        payments        httpd    2.4.49    version-exact
log-aggregator     tier2        infrastructure  httpd    2.4.49    version-exact
api-gateway        tier1        platform-core   httpd    1.15.17   product-level

To samo urządzenie do punktów odniesienia i fuzji; jeden dodatkowy krok poprzez powiązania aplikacji.

Część 6 — Wyniki, koszty i błędy

W przypadku identyfikatorów i zestawów wieloetapowych, fuzja uwzględniająca ontologię poprawia dokładność w porównaniu z bazami opartymi wyłącznie na wektorach; przy pytaniach w formie zwykłego tekstu poprawa jest mniejsza — dlatego należy zachować wektory. Koszty wynikają z ekstrakcji, operacji na grafach oraz nieco dłuższych promptów, a nie z rezygnacji z embeddingów.

Błędy, ponieważ to one stanowią rzeczywistą treść

Typowe błędy w produkcji to: odchylenia ontologii (pojawienie się nowego typu krawędzi), halucynacje ekstraktora (błędna stopień poważności), błędy przy kopiowaniu wag fuzji, tagi cytacji, których nie ma w kontekście, oraz cache serwujące przestarzałe zrzuty grafu po aktualizacji CVE. Każdy błąd odpowiada określonemu teście: walidacji schematu, granic właściwości, ustawień fuzji, sprawdzania istnienia cytacji oraz TTL/invalidacji.

Część 7 — Kiedy to budować, a kiedy nie

Należy to stosować, gdy dane zawierają identyfikatory, pytania dotyczące właściciela lub wpływu w wieloetapowych procesach, lub fakty nigdy nie sformułowane jako zdania. Należy to pominąć, gdy zbiór danych jest na tyle mały, że wystarczy sama zaawansowana wyszukiwka hybrydowa, lub gdy nikt nie będzie utrzymywał ontologii. GraphRAG nie jest oznaką zaawansowania; to odpowiedź na określone sposoby awarii.

Pięć niezmienników, które warto zachować na każdą skalę

  1. Ontologia przede wszystkim — typy przed trójkami.
  2. Anchory dla identyfikatorów — dokładne dopasowanie przed metodą kosinusa.
  3. Rozdzielanie faktów i dowodów w instrukcjach.
  4. Cytowanie lub odrzucenie po wygenerowaniu odpowiedzi.
  5. Mierzenie efektywności na własnych pytaniach — a nie tylko na publicznych blogach z wynikami.

Te niezmienniki pozostają przydatne od demonstracji na laptopie po zintegrowane systemy bezpieczeństwa dla wielu użytkowników. Rozszerzanie zakresu powinno oznaczać rozbudowę ontologii i odpowiednich elementów, a nie dodawanie kolejnych instrukcji do nieuporządkowanej masy danych. Zachowaj metryki ekstrakcji (dokładność/wspomnienie dotyczące entytetów i krawędzi) obok metryk odpowiedzi; w przeciwnym razie „lepszy” model może potajemnie tworzyć relacje, które wydają się sensowne, ale powodują niepowodzenia w audytach. Wersjonuj ontologię jak API: zmiany dodatkowe są proste, przemianowanie wymaga procesów migracyjnych, a usuwanie wymaga oznaczeń pogrzebowych, aby stare kopie nie przywracały usuniętych krawędzi. W trybie czuwania uruchamiaj alerty przy gwałtownym wzroście liczby odrzuceń oraz przy wysokim wskaźniku błędów ekstraktora, a nie tylko przy opóźnieniach Gateway – te sygnały wykrywają awarie systemu wiedzy, zanim użytkownicy zauważą błędne oceny powagi CVE. Na początkowych miesiącach przeznacz budżet na ludzką weryfikację spornych CVE oraz mapowania produktów; takie etykiety mogą prowadzić do problemów w przyszłości.

Testy fuzji, które zapewniają wiarygodność wag fuzji w miarę rozwoju katalogu.

Podczas oceny dostawców lub frameworki zapytaj, w jaki sposób kodują one ograniczenia ontologii, jak łączą ranki grafów i wektorów oraz w jaki sposób sprawdzają wierność cytowań. Dema, które pokazują jedynie ładny interfejs graficzny bez tych trzech odpowiedzi, zazwyczaj tworzą ten sam problem typu RAG pod nową nazwą. Wolę nudne, zapisane w formie skryptów procesy z wyraźnymi punktami odniesienia niż magiczne „agentic graph reasoning”, które nie potrafią wyjaśnić, który krawędź uzasadniał daną ocenę powagi. To właśnie ten nudny podejście sprawia, że GraphRAG świadomy ontologii może być używany.

Traktuj wagę fuzji jako element konfiguracji poddawany testom: przechowuj je obok plików fixit, które ustalają pytanie, listy kandydatów oraz oczekiwany numer najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmiany kodu, która zaktualizuje te pliki fixit, aby regresje nie mogły ukrywać się w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły być ukrywane w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły ukrywać się w lokalnej wiedzy zespołu.

Traktuj wagi fuzji jako konfigurację pod testem: przechowuj je obok plików fixit, które ustalają treść pytania, listy kandydatów oraz oczekiwany identyfikator najwyższej pozycji. Gdy ktoś „dostosowuje” wagi w notatniku, wymagaj propozycji zmian, która zaktualizuje te pliki fixit, aby regresje nie mogły ukrywać się w lokalnej wiedzy zespołu.