Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import json, csv, os, hashlib | |
| from datetime import datetime | |
| ADMIN_HASH = "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" # "admin" | |
| RESULTS = "vysledky.csv" | |
| FEEDBACK = "zpetna_vazba.csv" | |
| for f, cols in [(RESULTS, ['cas','kod','typ','skore','max','procenta','odpovedi']), | |
| (FEEDBACK, ['cas','kod','typ','obt_testu','sroz_testu','uzit_testu','obt_vyuky','sroz_vyuky','uzit_vyuky','komentar'])]: | |
| if not os.path.exists(f): | |
| with open(f, 'w', newline='', encoding='utf-8') as file: | |
| csv.writer(file).writerow(cols) | |
| TESTS = { | |
| "STAFF": { | |
| "nazev": "Test pro administrativní pracovníky (STAFF)", | |
| "questions": [ | |
| { | |
| "q": "Jaká je doporučená struktura efektivního promptu pro práci s AI?", | |
| "ctx": "Prompt je instrukce, kterou AI zadáváte. Jeho struktura ovlivňuje kvalitu odpovědí.", | |
| "opts": [ | |
| "A) Role + Kontext + Úkol + Formát", | |
| "B) Pouze úkol, ostatní AI domyslí sama", | |
| "C) Poskytnutí jasné role AI zlepšuje kvalitu odpovědí", | |
| "D) Formát výstupu není důležitý", | |
| "E) Kontext pomáhá AI lépe pochopit situaci" | |
| ], | |
| "ans": ["A","C","E"] | |
| }, | |
| { | |
| "q": "Co přesně znamená termín 'zero-shot prompting'?", | |
| "ctx": "Zero-shot je základní technika práce s AI, kdy modelu poskytujeme pouze instrukci bez dalších příkladů.", | |
| "opts": [ | |
| "A) Poskytnutí jednoho vzorového příkladu", | |
| "B) Zadání instrukce bez jakýchkoliv příkladů", | |
| "C) Poskytnutí tří vzorových příkladů", | |
| "D) Nejjednodušší forma práce s AI", | |
| "E) Vždy poskytuje nejlepší výsledky" | |
| ], | |
| "ans": ["B","D"] | |
| }, | |
| { | |
| "q": "Co je 'answer pressure testing' a k čemu slouží?", | |
| "ctx": "Answer pressure testing je technika, kterou testujeme spolehlivost odpovědí AI a snižujeme riziko chyb.", | |
| "opts": [ | |
| "A) Technika pro zrychlení generování odpovědí", | |
| "B) Žádost, aby AI zkontrolovala a zdůvodnila své odpovědi", | |
| "C) Příklad: 'Jsi si tímto výsledkem jistý? Zkontroluj svoje úvahy.'", | |
| "D) Pomáhá odhalit případné halucinace AI", | |
| "E) Snižuje kvalitu odpovědí tím, že AI zmate" | |
| ], | |
| "ans": ["B","C","D"] | |
| }, | |
| { | |
| "q": "Které z následujících úkolů lze bezpečně řešit pomocí AI v kancelářské práci?", | |
| "ctx": "AI je užitečná pro mnoho úkolů, ale ne všechny jsou pro ni vhodné nebo bezpečné.", | |
| "opts": [ | |
| "A) Shrnutí dlouhých dokumentů do kratší formy", | |
| "B) Extrakce konkrétních informací (jména, data, částky) z dokumentů", | |
| "C) Anonymizace osobních údajů v dokumentech", | |
| "D) Fyzická archivace papírových dokumentů", | |
| "E) Překlad textů a změna stylu komunikace" | |
| ], | |
| "ans": ["A","B","C","E"] | |
| }, | |
| { | |
| "q": "Co přesně jsou 'halucinace' v kontextu AI systémů?", | |
| "ctx": "Halucinace jsou jedním z nejzávažnějších rizik při práci s AI a je důležité jim porozumět.", | |
| "opts": [ | |
| "A) AI vymýšlí informace, které vypadají věrohodně, ale jsou nepravdivé", | |
| "B) Halucinace jsou vždy snadno rozpoznatelné", | |
| "C) Je nezbytné vždy ověřovat důležitá fakta z jiných zdrojů", | |
| "D) Answer pressure testing může pomoci odhalit halucinace", | |
| "E) Nové modely AI už halucinace nemají" | |
| ], | |
| "ans": ["A","C","D"] | |
| }, | |
| { | |
| "q": "Co je 'token' a proč je tento pojem důležitý?", | |
| "ctx": "Token je základní jednotka, se kterou AI systémy pracují. Pochopení tokenů je klíčové pro efektivní práci s AI.", | |
| "opts": [ | |
| "A) Token odpovídá přibližně 4 znakům textu", | |
| "B) Token je základní jednotka zpracování textu v AI", | |
| "C) Token je měna pro placení AI služeb", | |
| "D) AI modely mají limit na počet tokenů", | |
| "E) Token je totéž co jedno slovo" | |
| ], | |
| "ans": ["A","B","D"] | |
| }, | |
| { | |
| "q": "Jak správně pracovat s AI při respektování GDPR?", | |
| "ctx": "GDPR chrání osobní údaje občanů EU. Při práci s AI je nutné dodržovat tyto právní požadavky.", | |
| "opts": [ | |
| "A) Anonymizovat všechny osobní údaje před nahráním do AI", | |
| "B) GDPR se na práci s AI nevztahuje", | |
| "C) Nikdy neposílat citlivá data do veřejných AI systémů", | |
| "D) Znát politiku zpracování dat AI platformy", | |
| "E) Osobní údaje lze do AI nahrávat bez omezení" | |
| ], | |
| "ans": ["A","C","D"] | |
| }, | |
| { | |
| "q": "Jak správně pracovat s češtinou vs. angličtinou při použití AI?", | |
| "ctx": "Volba jazyka ovlivňuje kvalitu odpovědí AI systémů, protože byly trénovány primárně na anglických datech.", | |
| "opts": [ | |
| "A) Pro češtinu explicitně uvést 'v češtině'", | |
| "B) AI automaticky rozpozná jazyk vždy správně", | |
| "C) Pro vědu preferovat angličtinu", | |
| "D) Kvalita je stejná pro všechny jazyky", | |
| "E) Lze kombinovat jazyky podle potřeby" | |
| ], | |
| "ans": ["A","C","E"] | |
| }, | |
| { | |
| "q": "Co znamená 'chain-of-thought prompting' a kdy ho použít?", | |
| "ctx": "Chain-of-thought je pokročilá technika, kdy žádáme AI o postupné vysvětlení jednotlivých kroků řešení.", | |
| "opts": [ | |
| "A) Žádost o postupné vysvětlení kroků", | |
| "B) Nejrychlejší způsob generování", | |
| "C) Užitečné pro komplexní analýzy", | |
| "D) Příklad: 'Analyzuj: 1) témata 2) problémy 3) řešení'", | |
| "E) Nevhodné pro analytické úlohy" | |
| ], | |
| "ans": ["A","C","D"] | |
| }, | |
| { | |
| "q": "Co je 'role-playing' v kontextu práce s AI?", | |
| "ctx": "Role-playing je technika, kdy AI přiřazujeme konkrétní roli či identitu, což ovlivňuje styl a obsah odpovědí.", | |
| "opts": [ | |
| "A) AI přiřadíte konkrétní roli", | |
| "B) Příklad: 'Jsi zkušený HR manažer s 20 lety praxe'", | |
| "C) Role nijak neovlivňuje odpovědi", | |
| "D) Role pomáhá přizpůsobit jazyk a odbornost", | |
| "E) Používá se pouze pro zábavu" | |
| ], | |
| "ans": ["A","B","D"] | |
| } | |
| ] | |
| }, | |
| "ACADEMIC": { | |
| "nazev": "Test pro akademické pracovníky (ACADEMIC)", | |
| "questions": [ | |
| { | |
| "q": "Jaká je struktura akademického promptu?", | |
| "ctx": "Akademická práce vyžaduje přesnější a rigoróznější strukturu promptů než běžné použití AI.", | |
| "opts": [ | |
| "A) Role + Kontext + Úkol + Formát + Omezení", | |
| "B) Pouze úkol", | |
| "C) Požadavek na rozlišení fakta/interpretace", | |
| "D) Žádost o označení míry jistoty", | |
| "E) Formát není důležitý" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Co je 'answer pressure testing' a proč je v akademii kritický?", | |
| "ctx": "V akademickém kontextu je spolehlivost informací zásadní, proto je answer pressure testing klíčovou technikou.", | |
| "opts": [ | |
| "A) Technika systematického zpochybňování", | |
| "B) Příklad: 'Jsi si jistý? Odkud to? Alternativy?'", | |
| "C) Pomáhá odhalit halucinace", | |
| "D) V akademii méně důležitý než v kanceláři", | |
| "E) Nutí AI přiznat nejistotu" | |
| ], | |
| "ans": ["A","B","C","E"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Proč jsou halucinace v akademii zvlášť problematické?", | |
| "ctx": "V akademickém výzkumu může jediná chyba diskreditovat celou práci. Halucinace AI představují závažné riziko.", | |
| "opts": [ | |
| "A) Jedna vymyšlená citace diskredituje celou práci", | |
| "B) V nových modelech již vyřešeny", | |
| "C) AI prezentuje vymyšlené info přesvědčivě", | |
| "D) Týkají se především citací a faktů", | |
| "E) Jsou vždy snadno rozpoznatelné" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Rozdíl mezi 'asistencí' a 'nahrazením úsudku'?", | |
| "ctx": "V akademické práci je klíčové rozlišovat, kdy AI pomáhá a kdy za nás myslí. To má etické i metodologické důsledky.", | |
| "opts": [ | |
| "A) Asistence = AI strukturuje MOJE myšlenky", | |
| "B) Nahrazení = AI vytváří závěry které přebírám", | |
| "C) V akademii irelevantní", | |
| "D) Asistence = finální odpovědnost na mně", | |
| "E) Oba postupy stejně přijatelné" | |
| ], | |
| "ans": ["A","B","D"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Jak správně pracovat s AI a GDPR v akademickém výzkumu?", | |
| "ctx": "Akademický výzkum často pracuje s citlivými daty respondentů. GDPR stanovuje přísné požadavky na jejich ochranu.", | |
| "opts": [ | |
| "A) Anonymizovat data před nahráním", | |
| "B) Data z výzkumu lze nahrávat bez omezení", | |
| "C) Necitlivá data respondentů do veřejných AI", | |
| "D) Enterprise AI nabízí lepší ochranu", | |
| "E) GDPR se na akademii nevztahuje" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Proč je pro akademii doporučena angličtina?", | |
| "ctx": "AI modely byly trénovány primárně na anglických akademických textech, což má důsledky pro kvalitu odpovědí.", | |
| "opts": [ | |
| "A) AI trénovány primárně na anglických akademických textech", | |
| "B) Kvalita v češtině a angličtině je stejná", | |
| "C) Pro vědu přesnější odpovědi v angličtině", | |
| "D) Pro admin práci lepší čeština", | |
| "E) Angličtina není důležitá" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1 | |
| }, | |
| { | |
| "q": "Které použití AI je přijatelné a které ne?", | |
| "ctx": "V akademické práci existují jasné hranice mezi přijatelným a nepřijatelným použitím AI. Je klíčové je rozlišovat.", | |
| "opts": [ | |
| "A) PŘIJATELNÉ: Návrh struktury mých myšlenek", | |
| "B) PŘIJATELNÉ: Generování závěrů výzkumu", | |
| "C) PŘIJATELNÉ: Identifikace slabých míst argumentace", | |
| "D) NEPŘIJATELNÉ: Auto-psaní sekcí bez kontroly", | |
| "E) PŘIJATELNÉ: Delegování hodnotových soudů" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1.5 | |
| }, | |
| { | |
| "q": "Jak pracovat s epistemickou nejistotou?", | |
| "ctx": "AI systémy nemají skutečné porozumění a často si nejsou jisté. Musíme s touto nejistotou pracovat systematicky.", | |
| "opts": [ | |
| "A) Požadovat explicitní míru jistoty", | |
| "B) Důvěřovat autoritativnímu tónu AI", | |
| "C) Ověřovat tvrzení z primárních zdrojů", | |
| "D) Žádat alternativní interpretace", | |
| "E) AI jako konečný arbitr" | |
| ], | |
| "ans": ["A","C","D"], | |
| "pts": 1.5 | |
| }, | |
| { | |
| "q": "Jak správně pracovat se zdroji a citacemi?", | |
| "ctx": "AI systémy mají tendenci vymýšlet realisticky vypadající citace. To představuje závažné riziko pro akademickou integritu.", | |
| "opts": [ | |
| "A) AI často vymýšlí věrohodné citace", | |
| "B) Každou citaci ověřit v databázích", | |
| "C) Answer pressure: 'Odkud? Je DOI platné?'", | |
| "D) Citace z AI spolehlivé pro publikování", | |
| "E) Preferovat: PDF → shrnutí, ne 'cituj studie'" | |
| ], | |
| "ans": ["A","B","C","E"], | |
| "pts": 1.5 | |
| }, | |
| { | |
| "q": "Co musí akademik uvést ohledně použití AI?", | |
| "ctx": "Transparentnost je základem akademické integrity. Použití AI nástrojů musí být jasně deklarováno.", | |
| "opts": [ | |
| "A) Neuvádí se", | |
| "B) Transparentně uvést v metodologii", | |
| "C) AI je nástroj, ne autor - ale uvést použití", | |
| "D) Citovat AI podle standardů (APA)", | |
| "E) Závisí na pravidlech instituce" | |
| ], | |
| "ans": ["B","C","D","E"], | |
| "pts": 1.5 | |
| }, | |
| { | |
| "q": "Proč AI nemá 'skutečné porozumění'?", | |
| "ctx": "Pochopení limitů AI je klíčové pro její zodpovědné použití v akademickém kontextu.", | |
| "opts": [ | |
| "A) AI je statistický predikční model", | |
| "B) Předpovídá pokračování textu z vzorců", | |
| "C) Když neví, stejně něco vygeneruje", | |
| "D) Nové modely už mají lidské porozumění", | |
| "E) Proto nutná epistemická opatrnost" | |
| ], | |
| "ans": ["A","B","C","E"], | |
| "pts": 1.5 | |
| }, | |
| { | |
| "q": "Jaká je role AI v akademickém výzkumu?", | |
| "ctx": "Je důležité správně definovat roli AI v akademické práci - co může a co nemůže dělat.", | |
| "opts": [ | |
| "A) Pomocný nástroj pro strukturování", | |
| "B) Náhrada za odborný úsudek", | |
| "C) Kritický partner pro zpochybňování", | |
| "D) Autoritativní zdroj pravdy", | |
| "E) Nástroj pro podporu, ne nahrazení" | |
| ], | |
| "ans": ["A","C","E"], | |
| "pts": 1.5 | |
| } | |
| ] | |
| } | |
| } | |
| def check(pwd): | |
| return hashlib.sha256(pwd.encode()).hexdigest() == ADMIN_HASH | |
| def score(typ, ans): | |
| correct = sum(q.get("pts",1) for i,q in enumerate(TESTS[typ]["questions"]) | |
| if set(ans.get(f"q_{i}",[]))==set(q["ans"])) | |
| total = sum(q.get("pts",1) for q in TESTS[typ]["questions"]) | |
| return correct, total | |
| def save_result(kod, typ, sc, mx, ans): | |
| with open(RESULTS, 'a', newline='', encoding='utf-8') as f: | |
| csv.writer(f).writerow([datetime.now().isoformat(), kod, typ, sc, mx, | |
| f"{sc/mx*100:.1f}%", json.dumps(ans, ensure_ascii=False)]) | |
| def save_fb(kod, typ, ot, st, ut, ov, sv, uv, kom): | |
| with open(FEEDBACK, 'a', newline='', encoding='utf-8') as f: | |
| csv.writer(f).writerow([datetime.now().isoformat(), kod, typ, ot, st, ut, ov, sv, uv, kom]) | |
| def get_file(pwd, which): | |
| if not check(pwd): | |
| return None, "❌ Špatné heslo" | |
| file = RESULTS if which=="r" else FEEDBACK | |
| if not os.path.exists(file): | |
| return None, "📭 Zatím žádná data" | |
| return file, f"✅ {file} připraven ke stažení" | |
| with gr.Blocks(title="Test AI KTF") as app: | |
| gr.Markdown("# 🎓 Test znalostí: Generativní AI\n**Katolická teologická fakulta UK, Praha 2025**") | |
| kod_s, typ_s = gr.State(""), gr.State("") | |
| with gr.Tab("🔐 Start"): | |
| gr.Markdown("### Zahájení testu") | |
| gr.Markdown("Pro zahájení zadejte váš unikátní kód a vyberte typ testu.") | |
| kod = gr.Textbox(label="Váš unikátní kód", placeholder="KTF2025-001", info="Tento kód obdržíte od organizátora") | |
| typ = gr.Radio([ | |
| ("STAFF - Administrativní pracovníci (10 otázek)", "STAFF"), | |
| ("ACADEMIC - Výzkumní pracovníci (12 otázek)", "ACADEMIC") | |
| ], label="Typ testu", info="Vyberte test odpovídající vaší roli") | |
| go = gr.Button("Začít test", variant="primary", size="lg") | |
| msg = gr.Markdown("") | |
| with gr.Tab("📝 Test"): | |
| info = gr.Markdown("") | |
| qs = [] | |
| for i in range(12): | |
| with gr.Group(visible=False) as g: | |
| qt = gr.Markdown("") | |
| qctx = gr.Markdown("", elem_classes="question-context") | |
| qc = gr.CheckboxGroup(label="⚠️ Vyberte VŠECHNY správné odpovědi") | |
| qs.append((g,qt,qctx,qc)) | |
| send = gr.Button("Odeslat test", variant="primary", size="lg", visible=False) | |
| send_msg = gr.Markdown("") | |
| with gr.Tab("📊 Výsledky"): | |
| res = gr.Markdown("") | |
| gr.Markdown("---") | |
| gr.Markdown("### 💬 Zpětná vazba") | |
| gr.Markdown("*Vaše zpětná vazba je anonymní a pomůže nám zlepšit jak test, tak celou výuku.*") | |
| gr.Markdown("#### Hodnocení testu:") | |
| obt_t = gr.Radio(["Příliš snadný", "Přiměřený", "Příliš těžký"], label="Obtížnost testu") | |
| sroz_t = gr.Radio(["Výborná", "Dobrá", "Špatná"], label="Srozumitelnost otázek") | |
| uzit_t = gr.Radio(["Velmi užitečný", "Užitečný", "Málo užitečný"], label="Užitečnost testu pro praxi") | |
| gr.Markdown("#### Hodnocení výuky (kurzu AI):") | |
| obt_v = gr.Radio(["Příliš snadná", "Přiměřená", "Příliš těžká"], label="Obtížnost výuky") | |
| sroz_v = gr.Radio(["Výborná", "Dobrá", "Špatná"], label="Srozumitelnost výkladu") | |
| uzit_v = gr.Radio(["Velmi užitečná", "Užitečná", "Málo užitečná"], label="Užitečnost pro praxi") | |
| kom = gr.Textbox(label="Váš komentář (nepovinné)", placeholder="Sdílejte své postřehy k testu nebo výuce...", lines=3) | |
| fb = gr.Button("Odeslat zpětnou vazbu", variant="secondary") | |
| fb_msg = gr.Markdown("") | |
| with gr.Tab("👨💼 Admin"): | |
| gr.Markdown("### 🔒 Administrátorský přístup") | |
| gr.Markdown("*Pouze pro organizátory kurzu - stahování výsledků*") | |
| pwd = gr.Textbox(label="Admin heslo", type="password", placeholder="Výchozí: admin") | |
| with gr.Row(): | |
| dlr = gr.Button("📥 Stáhnout výsledky testů") | |
| dlf = gr.Button("📥 Stáhnout zpětnou vazbu") | |
| dl = gr.File(label="CSV soubor ke stažení") | |
| dlm = gr.Markdown("") | |
| gr.Markdown("*Pro změnu hesla upravte ADMIN_HASH v app.py*") | |
| def start(k, t): | |
| if not k or not t: | |
| return {msg: "❌ Prosím vyplňte kód i typ testu", kod_s: "", typ_s: ""} | |
| test = TESTS[t] | |
| upd = { | |
| msg: f"✅ Test byl zahájen! **→ Přejděte na záložku 'Test'**", | |
| kod_s: k, | |
| typ_s: t, | |
| info: f"## {test['nazev']}\n\n**Počet otázek:** {len(test['questions'])}\n\n⚠️ **Důležité:** U každé otázky označte VŠECHNY správné odpovědi!", | |
| send: gr.update(visible=True), | |
| send_msg: "" | |
| } | |
| for i in range(12): | |
| if i < len(test["questions"]): | |
| q = test["questions"][i] | |
| upd[qs[i][0]] = gr.update(visible=True) | |
| upd[qs[i][1]] = f"### Otázka {i+1}\n\n{q['q']}" | |
| upd[qs[i][2]] = f"*{q.get('ctx', '')}*" | |
| upd[qs[i][3]] = gr.update(choices=q["opts"], value=[]) | |
| else: | |
| upd[qs[i][0]] = gr.update(visible=False) | |
| return upd | |
| def submit(k, t, *a): | |
| if not k: | |
| return "❌ Chyba: Test nebyl správně zahájen", "" | |
| ans = {f"q_{i}": a[i] for i in range(len(TESTS[t]["questions"]))} | |
| sc, mx = score(t, ans) | |
| save_result(k, t, sc, mx, ans) | |
| ok = sc >= (7 if t=="STAFF" else 8) | |
| poz = "minimálně 7 bodů" if t=="STAFF" else "minimálně 8 bodů" | |
| result = f"""## {'✅ Gratulujeme! Test úspěšně splněn!' if ok else '❌ Test nesplněn'} | |
| **Váš výsledek:** {sc:.1f} / {mx} bodů ({sc/mx*100:.1f}%) | |
| **Požadavek pro splnění:** {poz} | |
| {'🎉 **Úspěšně jste absolvovali test!**' if ok else '😔 Bohužel jste nedosáhli minimálního počtu bodů. Doporučujeme prostudovat materiály a zkusit test znovu.'} | |
| --- | |
| **📊 Podrobnosti:** | |
| - Kód účastníka: {k} | |
| - Typ testu: {t} | |
| - Datum a čas: {datetime.now().strftime('%d.%m.%Y v %H:%M')} | |
| --- | |
| ### ⬇️ Co dál? | |
| **Prosím, poskytněte nám zpětnou vazbu níže** - pomůžete nám zlepšit jak test, tak celou výuku. Zpětná vazba je anonymní a zabere jen 2 minuty. Děkujeme! 🙏 | |
| """ | |
| nav_msg = "# ✅ Test odeslán!\n\n**→ Přejděte na záložku 'Výsledky' pro zobrazení výsledků a zpětnou vazbu**" | |
| return result, nav_msg | |
| go.click(start, [kod,typ], | |
| [msg,kod_s,typ_s,info,send,send_msg]+[x for g in qs for x in g]) | |
| send.click(submit, [kod_s,typ_s]+[q[3] for q in qs], [res, send_msg]) | |
| fb.click(lambda k,t,ot,st,ut,ov,sv,uv,c: (save_fb(k,t,ot,st,ut,ov,sv,uv,c), "✅ **Děkujeme za zpětnou vazbu!** Vaše připomínky jsou pro nás velmi cenné.")[1] if k else "❌ Nelze odeslat - test nebyl dokončen", | |
| [kod_s,typ_s,obt_t,sroz_t,uzit_t,obt_v,sroz_v,uzit_v,kom], fb_msg) | |
| dlr.click(lambda p: get_file(p,"r"), pwd, [dl,dlm]) | |
| dlf.click(lambda p: get_file(p,"f"), pwd, [dl,dlm]) | |
| app.launch() |