dp-zp-agent/experiments/user_questions_v1/build_cases.py

207 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Construct the preregistered question set from raw Markdown, not an index.
All line ranges/requirements below were reviewed against the original files.
This is evaluation-only code; it is never imported by production answering.
"""
import hashlib,json,re
from pathlib import Path
import frontmatter
ROOT=Path(__file__).resolve().parents[2]
WIKI=ROOT.parent/'zpwiki'
OUT=Path(__file__).parent
# slug, work, topic line, assignment start/end, latest complete meeting and source range
CONFIG=[
('adrian_ondra','Bakalársky projekt 2027',15,20,22,'2026-11-05',24,39),
('samuel_stucka','Bakalársky projekt 2027',15,28,33,'2026-09-29',35,45),
('sebastian_kiss','Bakalársky projekt 2027',15,21,23,'2026-10-23',26,36),
('yehor_piliavin','Bakalársky projekt 2027',15,25,27,'2026-10-15',36,49),
('kristian_sokolsky','Bakalársky projekt 2027',15,19,21,'2026-10-23',23,36),
('ronald_zalacko','Bakalárska práca 2026',22,29,33,'2025-12-08',35,55),
('simona_bobrovcanova','Bakalárska práca 2026',18,22,25,'2026-02-27',33,48),
('denis_le_thanh','Bakalárska práca 2026',24,29,32,'2025-10-03',35,52),
('oleksandr_dorybohov','Bakalárska práca 2026',20,29,32,'2026-03-23',40,54),
('rostyslav_rodzhuk','Bakalárska práca 2026',20,24,27,'2026-02-06',64,78),
]
# Denis's assignment is one explicit goal, not the subsequent meeting instructions.
CONFIG[7]=('denis_le_thanh','Bakalárska práca 2026',24,29,29,'2025-10-03',44,58)
def profile(slug):
p=next((WIKI/'pages/students').glob('*/'+slug+'/README.md'))
raw=p.read_text();page=frontmatter.loads(raw)
assert page.get('published') is not False
return {'name':page['title'],'path':str(p.relative_to(WIKI)),'lines':raw.splitlines(),
'sha256':hashlib.sha256(raw.encode()).hexdigest(),'tags':page.get('taxonomy',{}).get('tag',[])}
def evidence(p,start,end,requirements,terms):
assert 1<=start<=end<=len(p['lines'])
return {'path':p['path'],'sha256':p['sha256'],'url':'https://zp.kemt.fei.tuke.sk/'+p['path'].removeprefix('pages/').removesuffix('/README.md'),
'start':start,'end':end,'quote':'\n'.join(p['lines'][start-1:end]),
'requirements':requirements if isinstance(requirements,list) else [requirements],
'retrieval_terms':[[t] if isinstance(t,str) else t for t in terms]}
cases=[]
def add(category,q,refs,status='ok',requirements=(),max_words=None):
c={'id':f'T{len(cases)+1:03d}','category':category,'question':q,'expected_status':status,'evidence':refs,
'response_requirements':list(requirements)}
if max_words:c['max_words']=max_words
cases.append(c)
P=[profile(c[0]) for c in CONFIG]
# 20 metadata questions: values come directly from heading/topic lines.
for i,(p,c) in enumerate(zip(P,CONFIG)):
slug,label,title,start,end,date,mstart,mend=c
topic=p['lines'][title-1]
ref=evidence(p,min(title,13),max(title,15),
f"For {p['name']}, identify {label} with topic/title exactly '{topic}' and work year {label[-4:]}. Do not substitute study start.",[topic,label[-4:]])
add('title_year',f'Pri osobe {p["name"]} over názov záznamu "{label}" a pripoj evidovaný rok práce.',[ref])
refs=[]
for line_no,line in enumerate(p['lines'],1):
if re.match(r'^#+\s+(?:Bakalársk|Diplomov|Vedeck|Výskumn|Tímov)',line):
heading=line.lstrip('# ').strip()
refs.append(evidence(p,line_no,min(line_no+5,len(p['lines'])),
f"Include the distinct academic record '{heading}' of {p['name']}; title only if explicit, do not invent it.",[heading]))
add('work_inventory',f'Zostav prehľad evidovaných prác a projektov osoby {p["name"]}; pri záznamoch rozlíš typ, rok a doložený názov.',refs)
# 20 goals and planned actions. These do not establish completed work.
GOALS=[]
for p,c in zip(P,CONFIG):
slug,label,title,start,end,date,mstart,mend=c
goals=[line.strip() for line in p['lines'][start-1:end] if line.strip()]
refs=[evidence(p,n,n,f"{p['name']}, {label}: present this source goal as intended work, not a completed result: {g}",[re.sub(r'^\s*(?:[-*]|\d+[.)])\s*','',g)]) for n,g in enumerate(p['lines'],1) if start<=n<=end and g.strip()]
GOALS.append(refs)
add('goals',f'Aké sú ciele záznamu "{label}" osoby {p["name"]}? Zhrň všetky doložené body vlastnými slovami a nezamieňaj ich za hotové výsledky.',refs)
# All follow-up tasks of the selected meeting, without giving their answers in the question.
task_header=next(n for n,line in enumerate(p['lines'],1) if mstart<=n<=mend and line.strip().startswith('Úlohy'))
task_lines=[(n,line) for n,line in enumerate(p['lines'],1) if task_header<n<=mend and line.strip().startswith('- ')]
refs=[evidence(p,n,n,f"{p['name']}: this is an assigned follow-up task, not a delivered result: {line}",[line.strip('- ')]) for n,line in task_lines]
assert refs,(slug,'no tasks')
year,month,day=date.split('-')
add('planned_task',f'Aké úlohy dostal {p["name"]} podľa záznamu zo stretnutia {int(day)}.{int(month)}.{year}? Odlíš budúce úlohy od toho, čo už bolo urobené.',refs)
# 12 specific recorded results, methods and problems; independently located lines.
ACTIVITIES=[
('adrian_ondra',29,30,'Stanza/dependency parsing and spaCy are mentioned; tried SlovakBERT-NER, no proof of building a new model.', ['stanza','SlovakBERT-NER'],'metódy'),
('kristian_sokolsky',27,28,'Downloaded/parsed several MK pages and attempted fine-tuning; an attempt is not proven successful training.', ['Stiahnutie','pokus'],'pokrok'),
('ronald_zalacko',38,39,'React frontend; Flask backend; Postgres, SQLAlchemy, Vite JS, Websocket/socket.io, JWT auth; application works.', ['React','Flask','Postgres'],'metódy'),
('simona_bobrovcanova',37,38,'Small-model context limit and tested hierarchical summarization (chunks then summary of summaries).',['kontext','hierarchická'],'problémy'),
('simona_bobrovcanova',55,58,'Prepared court data and LoRA script, evaluated with ROUGE/BLEU and fine-tuned/evaluated the explicitly named models.', ['ROUGE','BLEU'],'výsledky'),
('oleksandr_dorybohov',74,78,'Local demo works with local model; searches court decisions/proceedings; OLLAMA gpt-oss:20B; written text compilation problem.', ['lokálne','OLLAMA'],'stav'),
('oleksandr_dorybohov',61,62,'Dockerfile/compose; httpx replaces requests, streamlit changes to chainlit; improved system prompt.', ['httpx','chainlit'],'metódy'),
('rostyslav_rodzhuk',65,68,'Internet fallback and option to add own documents are recorded; text has no progress.', ['fallback','dokumenty'],'pokrok'),
('yevhenii_medushivskyi',102,102,'Crawler based on Scrapy uses redis, Mongo, Trafilatura, Playwright and Celery.', ['Scrapy','Trafilatura'],'metódy'),
('yevhenii_medushivskyi',44,52,'Approximately 450 MB collected text. Plan: mine domains, continue collection to at least 5GB, improve deduplication/boilerplate removal e.g. justext. Do not claim 5GB achieved.', ['450MB','5GB'],'výsledky'),
('samuel_dzurina',57,58,'First chatbot searches PDF and internet via DUCKDUCK, streamlit UI; local RAG me5large indexing two CMR versions through langchain.', ['DUCKDUCK','langchain'],'metódy'),
('denis_le_thanh',48,58,'Studied LLM, transformers, anaconda, Okapi, open instruct and ollama. Planned PEFT-QLORA, Slovak Mistral, TRL, unsloth, LLaMA-Factory and GPU/Colab/server sequence are not proven deployment.', ['Naštudované','ollama','PEFT-QLORA'],'pokrok'),
]
ACTIVITY_QUESTIONS=[
'Aký model a ďalšie nástroje sú doložené pri práci Adriána Ondru na bakalárskom projekte 2027? Neoznačuj vyskúšanie za vytvorenie nového modelu.',
'Ako pokročil Kristián Sokolský s bakalárskym projektom 2027? Rozlišuj vykonaný zber dát od pokusu o dotrénovanie.',
'Aké technológie má Ronald Zalacko na frontende, backende a v databáze a čo je doložené o funkčnosti aplikácie?',
'S akým problémom pri dlhých textoch sa stretla Simona Bobrovčanová a aký prístup už vyskúšala na jeho riešenie?',
'Ktoré modely a vyhodnocovacie metriky sú zaznamenané pri bakalárskej práci Simony Bobrovčanovej podľa záznamu z 5.12.2025? Odlíš vykonané experimenty od plánov.',
'Čo už vie právny agent Oleksandra Dorybohova, ktorý model využíva a aký technický problém sa týkal písomnej časti?',
'Čo sa zmenilo v nasadení a používateľskom rozhraní Oleksandra Dorybohova podľa záznamu z 19.12.2025?',
'Aké možnosti internetového vyhľadávania a práce s vlastnými dokumentmi mal agent Rostyslava Rodzhuka podľa záznamu zo 6.2.2026?',
'Na akých technológiách je postavený webový crawler Yevheniiho Medushivského? Uveď doložené použité nástroje.',
'Koľko textu mal Yevhenii Medushivskyi získaného podľa záznamu z 13.3.2026 a čo mal ešte urobiť? Cieľ nezamieňaj za už dosiahnutý objem.',
'Aké vyhľadávanie, rozhranie, model a lokálny RAG mal Samuel Džurina podľa záznamu zo 17.12.2025? Čo sa indexovalo?',
'Čo mal Denis Le Thanh naštudované a čo mal ešte vyskúšať podľa záznamu z 3.10.2025? Štúdium nie je dôkaz hotového nasadenia.'
]
for qi,(slug,start,end,requirement,terms,facet) in enumerate(ACTIVITIES):
p=profile(slug);line=p['lines'][start-1]
# Fail authoring rather than silently select the wrong lines after a source edit.
assert all(any(t.lower() in part.lower() for part in p['lines'][start-1:end]) for t in terms),(slug,start,p['lines'][start-1:end])
label=next((c[1] for c in CONFIG if c[0]==slug),'Bakalárska práca 2026')
ref=evidence(p,start,end,f'{p["name"]}, {label}: {requirement}',terms)
add('recorded_'+facet,ACTIVITY_QUESTIONS[qi],[ref])
# 8 dates: ask for complete recorded dates, not inferred execution dates.
for p,c in list(zip(P,CONFIG))[:7]+[(P[8],CONFIG[8])]:
slug,label,title,start,end,date,mstart,mend=c
ref=evidence(p,mstart,mend,
f"Latest meeting with an explicitly complete date is {date}; explain its source-recorded state/tasks for {p['name']}. Record date is not the exact date of execution. Undated records do not acquire its year.", [date[:4]] )
add('latest_record',f'Kedy je posledné stretnutie osoby {p["name"]} s úplným dátumom a čo sa pri ňom riešilo? Dátum záznamu odlíš od vykonania práce.',[ref])
# 8 comparisons of requested goals; per-side requirements remain separate.
for a,b in [(0,1),(2,3),(3,4),(5,6),(6,8),(8,9),(0,2),(5,9)]:
add('comparison',f'Porovnaj zadanie osoby {P[a]["name"]} pri "{CONFIG[a][1]}" so zadaním osoby {P[b]["name"]} pri "{CONFIG[b][1]}". Vysvetli rozdiel v cieľoch a uveď oba zdroje.',[*GOALS[a],*GOALS[b]])
# 6 relations/joins, directly checked against YAML and explicit work headings.
for a,b in [(3,4),(6,7),(8,9)]:
common=sorted(set(P[a]['tags'])&set(P[b]['tags']))
refs=[evidence(P[x],1,8,f"Tags of {P[x]['name']} are {P[x]['tags']}; shared tags of this pair are exactly {common}.",common) for x in [a,b]]
add('shared_tags',f'Ktoré tagy spájajú osoby {P[a]["name"]} a {P[b]["name"]}? Uveď iba spoločný prienik a zdroje.',refs)
for a,b in [(0,1),(2,4),(5,6)]:
year=CONFIG[a][1][-4:];assert year==CONFIG[b][1][-4:]
refs=[evidence(P[x],13,24,f"{P[x]['name']} has {CONFIG[x][1]}. Common recorded work year of the pair is {year}, not study-start year.",[year]) for x in [a,b]]
add('shared_years',f'Ktorý rok práce je spoločný pre osoby {P[a]["name"]} a {P[b]["name"]}? Najprv uveď spoločný rok, potom príslušné záznamy.',refs)
# 6 robustness/absence checks. Unknown-world claims are never the expected result.
for i,variant in [(2,'Sebastián Kiš'),(5,'Ronald Zalackoo')]:
c=CONFIG[i];p=P[i];line=c[2]
ref=evidence(p,1,max(24,line),f"Resolve typo to {p['name']}, report the recognized name and the source title/work year, without inventing a new person.",[c[1][-4:]])
add('name_typo',f'Zisti názov práce alebo projektu osoby {variant} a evidovaný rok. Pri oprave mena povedz, koho si rozpoznal.',[ref])
for i in [0,8]:
p=P[i]
assert not any('1991' in line for line in p['lines'])
ref=evidence(p,1,24,f"No 1991 academic record occurs in this profile; report absent evidence for this person/year, never substitute {CONFIG[i][1][-4:]}.",['1991'])
add('missing_year',f'Čo je doložené o práci osoby {p["name"]} z roku 1991? Nepouži záznam iného roka.',[ref],'no_answer')
add('unknown_person','Na čom pracoval Quirin Nenájdený a aké výsledky dosiahol?',[],status='no_answer',requirements=['No matching source person; ask for a real full name without global unrelated results or world-level nonexistence.'])
sam=[profile('samuel_stucka'),profile('samuel_dzurina'),profile('samuel_horani'),profile('samuel_vasil'),profile('samuel_sirotnik')]
add('ambiguous_person','Čo sa podarilo Samuelovi pri jeho práci?',[evidence(p,1,8,'Several actual people named Samuel exist; ask which full name is meant, never guess one.',[]) for p in sam],status='ambiguous')
# Open-set discovery/similarity: reference coverage is not exhaustive peer recall.
def ref_line(slug,needle,req,terms):
p=profile(slug);n=next(i for i,line in enumerate(p['lines'],1) if needle in line)
return evidence(p,n,n,req,terms)
for slug,needle in [('adrian_ondra','Vyskúšaný model SlovakBERT-NER'),
('simona_bobrovcanova','Vyskúšaná hierarchická sumarizácia'),
('samuel_dzurina','Urobené lokálne RAG'),
('yevhenii_medushivskyi','Vytvorený crawler na báze Scrapy')]:
p=profile(slug)
ref=ref_line(slug,needle,f"Reference person {p['name']} has this documented topic/activity. Return at least one other actually sourced person with a substantive related topic; distinguish assigned, planned and actual work. No collaboration inference.",[])
add('similarity_open_set',f'Kto sa zaoberal obsahovo podobnou témou ako {p["name"]}? Vysvetli konkrétne prepojenie a dolož obe osoby.',[ref],requirements=['At least one peer supported by its own original source. Generic Python/LLM study alone is not sufficient.'])
cases[-1]['retrieval_reference_mode']='anchor_coverage_not_exhaustive_peer_recall'
DISCOVERY=[
('simona_bobrovcanova','Vyskúšaná hierarchická sumarizácia','Kto už vyskúšal hierarchickú sumarizáciu na problém s dlhým kontextom? Stačí jeden doložený príklad.', 'One actual tested hierarchical summarization example, not an unexecuted task.'),
('yevhenii_medushivskyi','Vytvorený crawler na báze Scrapy','U koho je doložené vytvorenie webového crawlera pomocou Scrapy? Uveď konkrétny príklad a použitý technologický základ.', 'One actual built Scrapy crawler with a correctly bound person and stack.'),
('rostyslav_rodzhuk','Inteligentný agent pre podporu práce špeciálneho pedagóga','Ktorý študent sa venoval agentovi pre podporu špeciálneho pedagóga? Priblíž zameranie, bez domýšľania výsledkov.', 'One person/work focused on a special educator assistant; topic/assignment is not implementation proof.'),
('yehor_piliavin','Vyvinúť metódu na vytváranie riadiacich vektorov','Kto sa venoval riadeniu generovania veľkého jazykového modelu pomocou steering vectors? Rozlíš doložené ciele od výsledkov.', 'A documented steering-vectors project; planned method development is not completed evaluation.')]
for slug,needle,q,req in DISCOVERY:
ref=ref_line(slug,needle,req+' This reference is a verified example; another independently sourced correct example is acceptable.',[])
add('discovery_open_set',q,[ref]);cases[-1]['retrieval_reference_mode']='example_coverage_not_exhaustive_discovery_recall'
for a,b,start,end in [(0,4,'1.9.2026','6.11.2026'),(6,8,'1.1.2026','30.4.2026'),
(5,None,'1.9.2025','31.12.2025'),(9,7,'1.9.2025','28.2.2026')]:
other=profile('yevhenii_medushivskyi') if b is None else P[b]
ca=CONFIG[a];refs=[evidence(P[a],ca[6],ca[7],f"For {P[a]['name']}, provide a documented topic/activity in a record within {start}–{end}. Work year is not record/event date; an undated note cannot acquire a year.",[])]
if b is None:refs.append(ref_line('yevhenii_medushivskyi','Vytvorený crawler na báze Scrapy',f"For {other['name']}, give one actually recorded activity in {start}–{end}; source example is the 7.11.2025 crawler record.",[]))
else:refs.append(evidence(other,CONFIG[b][6],CONFIG[b][7],f"For {other['name']}, give one documented record/activity within {start}–{end}; exact execution time is not known from a meeting date.",[]))
add('record_period',f'Aké témy alebo činnosti sú zaznamenané pri osobách {P[a]["name"]} a {other["name"]} od {start} do {end}? Uveď pre každého jeden doložený príklad. Rozlišuj rok práce od dátumu záznamu.',refs)
add('supervisor_role','Kto vedie bakalársku prácu Ronalda Zalacka a kto je pri profile uvedený ako autor dokumentu? Tieto roly odlíš.',
[evidence(P[5],1,22,'Supervisor explicitly Matúš Pleva; document author Daniel Hladek. Do not infer that Hladek supervises this thesis.',['Matúš Pleva','Daniel Hladek'])])
add('advisor_roles','Kto je vedúci a kto konzultuje bakalársku prácu Denisa Le Thanha? Uveď doložené roly, nie odhad z autora stránky.',
[evidence(P[7],1,29,'Supervisor Tomáš Kormaník; consultations Kristián Sopkovič. Document author Daniel Hladek is not supervision evidence.',['Tomáš Kormaník','Kristián Sopkovič'])])
add('study_status','Ukončil Rostyslav Rodzhuk štúdium úspešne, alebo máme iba záznam o dokončených výstupoch práce? Vysvetli, čo je naozaj doložené.',
[ref_line('rostyslav_rodzhuk','Dokončené texty aj program.','Texts and program finished, under an undated-year record 11.5.; this does NOT prove successful thesis defense or graduation.',['Dokončené texty'])],status='no_answer',requirements=['Do not infer failed graduation or that the study is still ongoing either.'])
add('time_roles','Pri Yehorovi Piliavinovi odlíš rok začiatku štúdia, rok bakalárskeho projektu a najnovší úplný dátum stretnutia. Znamená rok projektu dátum vykonania činnosti?',
[evidence(P[3],1,40,'Study start 2024; bachelor project 2027; most recent fully dated record 2026-10-15. Project year does not date actual execution.',['2024','2027','2026'])])
# Stronger closed-set tests replace redundant single-record inventories/tasks.
def replace_case(index,category,q,refs,status='ok'):
cases[index]={'id':cases[index]['id'],'category':category,'question':q,'expected_status':status,
'evidence':refs,'response_requirements':[]}
for index,person_index,count in [(3,1,1),(19,9,2)]:
p=P[person_index]
refs=[evidence(p,n,n,f"Exactly {count} distinct explicit work/project headings are recorded in this profile; count records, not meetings or study-start years. This heading is one of the records.",[line.lstrip('# ').strip()])
for n,line in enumerate(p['lines'],1) if re.match(r'^#+\s+(?:Bakalársk|Diplomov|Vedeck|Výskumn|Tímov)',line)]
assert len(refs)==count
replace_case(index,'record_count',f'Koľko rôznych prác a projektov má evidovaných {p["name"]}? Spočítaj akademické záznamy, nie stretnutia ani podúlohy.',refs)
shared=[i for i,c in enumerate(cases) if c['category']=='shared_years']
replace_case(shared[0],'no_shared_year',f'Majú {P[0]["name"]} a {P[5]["name"]} spoločný rok evidovanej práce alebo projektu? Ak nie, nezamieňaj ich odlišné roky.',
[evidence(P[x],13,24,f"{P[x]['name']} has only work year {CONFIG[x][1][-4:]}; the two profiles have no common academic work year.",[CONFIG[x][1][-4:]]) for x in [0,5]],'no_answer')
replace_case(shared[2],'shared_years',f'V ktorom roku majú {P[5]["name"]} a {P[9]["name"]} obaja evidovanú prácu? Najprv uveď prienik rokov, potom príslušné záznamy.',
[evidence(P[x],13,24,'Common work year is 2026. The 2025 scientific project belongs only to Rostyslav and must not be returned as shared.',['2026']) for x in [5,9]])
for slot,tag,indices in [(21,'alignment',[3,4]),(27,'nlp',[0,1])]:
refs=[evidence(P[x],1,27,f"This person has tag {tag} AND an explicit Bachelor project 2027. The complete matching set is {[P[i]['name'] for i in indices]}. Preserve person-level join, work type and year; include the documented topic if available.",[tag,'2027']) for x in indices]
replace_case(slot,'tag_work_year_join',f'Ktorí študenti majú tag {tag} a zároveň bakalársky projekt z roku 2027? Pri každom uveď meno a doložený názov alebo tému projektu.',refs)
assert len(cases)==96,len(cases)
value={'schema':1,'split':'TEST','authoring':'AI-authored and source-checked BEFORE execution; no human validation claimed.',
'sampling':'Predeclared user-task strata, same existing public corpus; exact questions excluded from historical question files. These are not unseen-document tests.',
'target':.94,'cases':cases}
if __name__=='__main__':
(OUT/'test.json').write_text(json.dumps(value,ensure_ascii=False,indent=2)+'\n')
print('Wrote',len(cases),'cases')