"""Export real RAGAS scores and explicitly state their evaluation scope.""" import argparse import csv import json import sys from pathlib import Path sys.path.insert(0,str(Path(__file__).resolve().parents[2])) from scripts.evaluation.ragas_data import METRICS, aggregate def main(): p=argparse.ArgumentParser(description=__doc__) p.add_argument("--run",type=Path,required=True) p.add_argument("--project",type=Path,required=True) args=p.parse_args() manifest=json.loads((args.run/"manifest.json").read_text(encoding="utf-8")) rows=[json.loads(s) for s in (args.run/"scores.jsonl").read_text(encoding="utf-8").splitlines() if s.strip()] latest=list({(r["case_id"],r["mode"],r["metric"]):r for r in rows}.values()) expected=len(manifest["identity"]["case_mode_pairs"])*len(METRICS) if len(latest)!=expected: raise SystemExit("RAGAS run is incomplete; refusing final export") summary=aggregate(latest) (args.run/"summary.json").write_text(json.dumps(summary,indent=2),encoding="utf-8") counts={status:sum(r["status"]==status for r in latest) for status in ["ok","error","undefined","skipped"]} unsuccessful_attempts=sum(r["status"]=="error" for r in rows) retry_budgets=sorted({r.get("llm_max_tokens",4096) for r in rows if r.get("llm_max_tokens",4096)>4096}) version=manifest["identity"]["ragas_version"] tex=[ r"Predmetom experimentu je prínos vyhľadávania v znalostnom grafe vytvorenom z dokumentov ADC pre odpovede lekárenského agenta. " r"Testovacie scenáre sa odosielali cez rozhranie navrhnutého systému do LightRAG. " r"V režimoch \texttt{local} a \texttt{hybrid} sa vyhľadávanie opiera o entity a vzťahy grafu; " r"režim \texttt{mix} kombinuje grafové a vektorové vyhľadávanie. Získaný kontext sa následne využíva pri generovaní odpovede. " r"Režim \texttt{naive} s vektorovým vyhľadávaním bez grafu a režim \texttt{bypass} bez vyhľadávania " r"tvoria porovnávacie konfigurácie na posúdenie prínosu grafovej zložky. " r"RAGAS hodnotí výstupy tohto procesu; nejde o samostatný test vedomostí jazykového modelu ani o priamy audit správnosti hrán grafu.", f"Na uložené odpovede bola aplikovaná knižnica RAGAS vo verzii {version} cez rozhranie " r"\path{ragas.metrics.collections} \cite{ragasdocs}. Odpovede ani referencie sa pri tomto kroku nemenili. " r"Z desiatich pilotných otázok je osem zodpovedateľných z určeného zdroja; pri piatich konfiguráciách ide o 40 odpovedí. " r"Dve otázky s nedostatkom údajov zostávajú v samostatnej rubrike odmietnutia, pretože štandardné metriky by mohli správne odmietnutie penalizovať.", r"Použité boli Faithfulness, Answer Relevancy, Context Recall a Answer Correctness. " r"Prvá metrika overuje podporu tvrdení odpovede v dodaných textoch, druhá relevantnosť cez spätne generované otázky a embeddingy. " r"Context Recall skúma podporu faktov referencie v nájdených textoch. Answer Correctness kombinuje faktickú zhodu a sémantickú podobnosť " r"s predvolenými váhami 0,75 a 0,25. Referenčný text sa nepridáva medzi vyhľadané kontexty.", r"Kontext tvorili výhradne pôvodné úryvky ADC vrátené API spolu s konkrétnou odpoveďou. " r"Úplný prompt generátora vrátane popisov grafových entít a vzťahov sa v pôvodnom pilote nearchivoval. " r"Faithfulness preto meria podporu odpovede voči uloženým zdrojovým úryvkom, nie vernosť celému vnútornému kontextu generátora. " r"Táto odlišnosť je podstatná pri interpretácii grafových režimov.", r"Hodnotiteľ používal školskú službu \texttt{qwen36-27}, teplotu 0, vypnuté generovanie úvah a limit výstupu 4\,096 tokenov. " r"Metriky pracovali s predvolenými anglickými promptmi nad slovenskými otázkami a odpoveďami. " r"Pre embeddingové metriky sa použil viacjazyčný MiniLM z existujúcej lokálnej služby. " r"Jeho uložená konfigurácia obmedzuje vstup na 128 tokenov, takže pri dlhých odpovediach a referenciách " r"môže sémantická zložka Answer Correctness vychádzať iba zo začiatku textu. " r"Model hodnotiteľa je rovnaký ako model generátora; toto hodnotenie nie je nezávislé odborné posúdenie.", r"\begin{table}[H]\centering\small", f"\\caption{{Výsledky RAGAS {version} na zodpovedateľných otázkach}}", r"\label{tab:ragas-measured}", r"\begin{tabularx}{\textwidth}{@{}lXXXX@{}}\toprule", r"Režim & Faithfulness & Answer relev. & Context recall & Correctness \\\midrule", ] for mode in ["bypass","naive","local","hybrid","mix"]: cells=[] for metric in METRICS: v=summary.get(mode,{}).get(metric,{"mean":None,"n_ok":0}) cells.append(f"{v['mean']:.3f} ({v['n_ok']})" if v["mean"] is not None else "--") tex.append(mode+" & "+" & ".join(cells)+r" \\") tex += [r"\bottomrule\end{tabularx}\end{table}", r"Hodnoty sú aritmetické priemery; počet platných meraní je uvedený v zátvorke. " r"Pomlčka pri bezkontextovom režime znamená, že metrika vyžadujúca vyhľadaný kontext sa nepočíta, nie nulové skóre.", r"Faithfulness 0,800 pri režime \texttt{mix} znamená, že priemerný podiel tvrdení odpovede " r"označených hodnotiteľom ako podporené uloženými úryvkami dosiahol 0,800. Neznamená to, že 80\,\% odpovedí bolo medicínsky správnych. " r"Context Recall 0,465 vyjadruje priemerný podiel tvrdení referencie podporených nájdenými úryvkami. " r"Answer Relevancy 0,612 je skóre podobnosti pôvodnej otázky so spätne vytvorenými otázkami, nie podiel správnych odpovedí. " r"Answer Correctness 0,433 je kombinované skóre faktickej a sémantickej zhody s referenciou, nie percentuálna klinická presnosť.", r"V tomto pilote dosiahol režim \texttt{naive} najvyššiu podporu tvrdení v uložených úryvkoch (0,954) " r"a najvyššie skóre Answer Correctness (0,441). Režim \texttt{mix} dosiahol najvyšší Context Recall (0,465). " r"Grafová zložka teda pri tejto konfigurácii neviedla k jednoznačnému zlepšeniu všetkých metrík. " r"Pri ôsmich otázkach ide o opis nameraného pilotu, nie o štatistický dôkaz prevahy niektorého prístupu.", f"Získaných bolo {counts['ok']} konečných číselných hodnôt; chybových výpočtov bolo {counts['error']} " f"a nedefinovaných hodnôt {counts['undefined']}. Ďalších {counts['skipped']} kombinácií bolo podľa protokolu vynechaných. " "Chyby ani nedefinované hodnoty sa do priemeru nezapočítavajú a nenahrádzajú sa nulou. " r"Individuálne skóre, presné vstupy, manifest verzií a odpovede hodnotiaceho API sú uložené v elektronickej prílohe " r"\path{outputs/ragas_pilot/}.", r"Rozsah ôsmich zodpovedateľných otázok, zdrojovo odvodené referencie, predvolené anglické prompty a spoločný model " r"generátora a hodnotiteľa obmedzujú zovšeobecnenie výsledkov. Vyššie skóre jednej metriky samo osebe " r"nepreukazuje klinickú správnosť ani všeobecnú prevahu grafového vyhľadávania."] if unsuccessful_attempts: note=f"Počet neúspešných pokusov pred konečným stavom: {unsuccessful_attempts}. " if retry_budgets: note+="Pri opakovaní neúspešných buniek bol výstupný limit zvýšený na "+", ".join(str(n) for n in retry_budgets)+" tokenov, aby sa štruktúrovaný verdikt neodrezal. Úspešné bunky sa neopakovali. " note+="História pokusov a parametre opakovania zostávajú uložené v manifeste a individuálnych záznamoch." tex.append(note) (args.project/"ragas_results.tex").write_text("\n\n".join(tex)+"\n",encoding="utf-8") with (args.run/"scores.csv").open("w",encoding="utf-8-sig",newline="") as f: writer=csv.DictWriter(f,fieldnames=["case_id","mode","metric","status","value","reason","seconds"],extrasaction="ignore") writer.writeheader(); writer.writerows(latest) print(json.dumps({"counts":counts,"summary":summary},indent=2)) if __name__=="__main__": main()