mfirat007 commited on
Commit
eff511c
·
1 Parent(s): 3e82ed2

Upload 98 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +40 -35
  2. .gitignore +7 -0
  3. PILOT3_INSTITUTION_ONBOARDING.md +117 -0
  4. README.md +105 -15
  5. admin_panel.py +962 -0
  6. analytics.py +110 -0
  7. answering.py +618 -0
  8. app.py +8 -0
  9. case_analysis.py +400 -0
  10. clarification.py +637 -0
  11. clause_retrieval.py +1784 -0
  12. config.py +74 -0
  13. conversation_intent.py +123 -0
  14. data/mckf/2547_retrieval_index.json +0 -0
  15. data/mckf/2547_semantic_frames.jsonl +0 -0
  16. data/mckf/2809_retrieval_index.json +0 -0
  17. data/mckf/2809_semantic_frames.jsonl +0 -0
  18. data/mckf/2914_mckf_ontology.json +0 -0
  19. data/mckf/2914_retrieval_index.json +0 -0
  20. data/mckf/2914_semantic_frames.jsonl +0 -0
  21. data/mckf/corpus_retrieval_index.json +0 -0
  22. data/mckf/cross_document_edges.jsonl +16 -0
  23. data/mckf/mckf_shapes.ttl +27 -0
  24. data/mckf/rdfox_pilot_manifest.json +133 -0
  25. data/mckf/rdfox_pilot_rules.dlog +26 -0
  26. data/mckf/runtime_build_manifest.json +41 -0
  27. data/mckf/validation_report.json +22 -0
  28. data/normalized/2547_document.json +11 -0
  29. data/normalized/2809_document.json +11 -0
  30. data/normalized/2914_document.json +11 -0
  31. data/registry/article_semantic_catalog.json +0 -0
  32. data/registry/document_semantic_profiles.json +502 -0
  33. data/registry/legal_documents.json +29 -0
  34. data/registry/normative_interpretation_families.json +508 -0
  35. data/source/2547_clean.txt +0 -0
  36. data/source/2809_clean.txt +0 -0
  37. data/source/2914_clean.txt +227 -0
  38. data/structural/2547_articles.jsonl +0 -0
  39. data/structural/2547_clauses.jsonl +0 -0
  40. data/structural/2547_evidence_spans.jsonl +0 -0
  41. data/structural/2809_articles.jsonl +0 -0
  42. data/structural/2809_clauses.jsonl +0 -0
  43. data/structural/2809_evidence_spans.jsonl +0 -0
  44. data/structural/2914_articles.jsonl +37 -0
  45. data/structural/2914_clauses.jsonl +76 -0
  46. data/structural/2914_evidence_spans.jsonl +0 -0
  47. data/structural/2914_schedules.jsonl +2 -0
  48. data/tests/golden_questions.jsonl +65 -0
  49. data/tests/multidoc_golden_questions.jsonl +76 -0
  50. engine.py +64 -0
.gitattributes CHANGED
@@ -1,35 +1,40 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ data/mckf/2547_mckf_ontology.json filter=lfs diff=lfs merge=lfs -text
37
+ data/mckf/2809_mckf_ontology.json filter=lfs diff=lfs merge=lfs -text
38
+ data/mckf/corpus_mckf_ontology.json filter=lfs diff=lfs merge=lfs -text
39
+ data/mckf/corpus_semantic_frames.jsonl filter=lfs diff=lfs merge=lfs -text
40
+ data/mckf/rdfox_pilot_triples.nt filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ .cache/
4
+ .env
5
+ .venv/
6
+ *.log
7
+ data/feedback/regression_candidates.jsonl
PILOT3_INSTITUTION_ONBOARDING.md ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MitrAnlil 3. Pilot: Kurumsal Belge Onboarding
2
+
3
+ ## Amaç
4
+
5
+ 3. Pilot, çalışan corpus ile yeni kurumsal belgeler arasına doğrulanabilir bir
6
+ yaşam döngüsü koyar. TXT yüklemek, belgeyi doğrudan chatbot bilgisine eklemez.
7
+
8
+ ```text
9
+ Kurum -> TXT kabulü -> yapısal çözümleme -> semantik öneri
10
+ -> risk temelli uzman incelemesi -> aday build
11
+ -> contract + SHACL doğrulaması -> kontrollü yayın
12
+ ```
13
+
14
+ ## Değişmez sınırlar
15
+
16
+ - Yüklenen kaynak UTF-8 olarak ve SHA-256 özetiyle sürümlenir.
17
+ - Staging verisi aktif corpusun dışında tutulur.
18
+ - Her madde veya bölüm için kanonik başlık ve normatif adres önerilir.
19
+ - Yüksek etkili veya belirsiz birimler kurum uzmanının incelemesine düşer.
20
+ - Zorunlu incelemeler tamamlanmadan aday build üretilemez.
21
+ - Aday build ayrı veri kökünde üretilir; çalışan corpus build sırasında değişmez.
22
+ - MCKF contract ve SHACL doğrulaması geçmeyen build yayımlanamaz.
23
+ - Yayın sırasında önceki corpus yedeklenir; runtime yüklenemezse geri alınır.
24
+
25
+ ## Kurum izolasyonu
26
+
27
+ - Çekirdek mevzuat belgeleri ortak bilgi katmanıdır ve `institution_id` taşımaz.
28
+ - Kurumsal belgeler yalnızca bağlı oldukları kurumun aday build'ine girer.
29
+ - Bir kurumun aday build'i, ortak çekirdek ile yalnızca o kurumun yayımlanmış
30
+ belgelerinden oluşur; başka kurumların staging veya aktif belgeleri karışmaz.
31
+ - Belge kimliği ve kısa kod çakışmaları yayın öncesinde engellenir.
32
+ - Aktif kurum kimliği corpus ve runtime manifestine yazılır. Böylece çalışan
33
+ build'in hangi kurumsal çalışma alanına ait olduğu makinece doğrulanabilir.
34
+
35
+ ## Uzman inceleme alanları
36
+
37
+ Kurum uzmanı her anlam birimi için şunları doğrular veya düzeltir:
38
+
39
+ - Kanonik başlık
40
+ - Maddenin düzenlediği konu
41
+ - Kanonik kavramlar ve kullanıcı dili karşılıkları
42
+ - Hukuki işlemler
43
+ - Yetkili makamlar
44
+ - Operasyonel aktörler
45
+ - Kapsam dışı alanlar ve istisnalar
46
+ - İnceleme notları
47
+
48
+ ## Veri yerleşimi
49
+
50
+ ```text
51
+ data/onboarding/
52
+ institutions.json
53
+ submissions/<submission_id>/
54
+ source.txt
55
+ submission.json
56
+ structural_preview.json
57
+ reviews.json
58
+ builds/<candidate_id>/data/
59
+ backups/<publish_id>/
60
+ ```
61
+
62
+ Aktif çalışma zamanı hâlâ yalnızca `data/mckf/runtime_build_manifest.json` ile
63
+ eşleşen doğrulanmış corpusu kullanır.
64
+
65
+ `MITRANLIL_DATA_DIR`, kaynak koddan ayrı kalıcı bir veri kökü tanımlamak için
66
+ kullanılabilir. Aday build süreci proje klasörü dışında bir veri köküyle de
67
+ çalışır; bu, aynı uygulama imajının farklı kurum verileriyle kurulabilmesini
68
+ sağlar.
69
+
70
+ ## Yerel kontrol
71
+
72
+ ```powershell
73
+ python tools/run_pilot3_onboarding_checks.py
74
+ ```
75
+
76
+ Kontrol; Türkçe karakterlerin korunmasını, mevzuat ve genel kurumsal TXT
77
+ ayrıştırmasını, uzman kararlarının profile dönüşmesini, tenant izolasyonunu,
78
+ aday MCKF/SHACL build'ini ve yayın kapısının zorunlu incelemeler bitmeden
79
+ açılmamasını doğrular.
80
+
81
+ ## Canlıya taşıma
82
+
83
+ Pilot 3 kodu için aşağıdaki dosyalar birlikte yayımlanmalıdır:
84
+
85
+ ```text
86
+ app.py
87
+ config.py
88
+ utils.py
89
+ structural_parser.py
90
+ institution_onboarding.py
91
+ clause_retrieval.py
92
+ answering.py
93
+ clarification.py
94
+ hybrid_retrieval.py
95
+ normative_metadata.py
96
+ ui_gradio.py
97
+ tools/build_mckf_from_source.py
98
+ tools/run_pilot3_onboarding_checks.py
99
+ PILOT3_INSTITUTION_ONBOARDING.md
100
+ ```
101
+
102
+ Masaüstündeki doğrulanmış çekirdek corpus da canlıyla eşitlenecekse şu veri
103
+ alanları aynı sürümden birlikte taşınmalıdır:
104
+
105
+ ```text
106
+ data/mckf/
107
+ data/structural/
108
+ data/normalized/
109
+ data/registry/legal_documents.json
110
+ data/registry/document_semantic_profiles.json
111
+ data/registry/article_semantic_catalog.json
112
+ ```
113
+
114
+ `.venv/`, `__pycache__/`, `data/analytics/` ve eski `runtime_cache` dosyaları
115
+ dağıtım paketi değildir. `data/onboarding/` altındaki kurum verileri ise ancak
116
+ mevcut staging, uzman kararları veya yayın yedekleri canlıya taşınacaksa dahil
117
+ edilmelidir.
README.md CHANGED
@@ -1,15 +1,105 @@
1
- ---
2
- title: YOKPilot3
3
- emoji: 💻
4
- colorFrom: yellow
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.13'
9
- app_file: app.py
10
- pinned: false
11
- license: mit
12
- short_description: YOK Kanunlar Pilot 3
13
- ---
14
-
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ title: MitrAnlil Pilot3
4
+ sdk: gradio
5
+ emoji: 🏢
6
+ colorFrom: yellow
7
+ colorTo: yellow
8
+ short_description: YOK Kanunlar için normatif AI asistanı
9
+ sdk_version: 6.20.0
10
+ ---
11
+ # MitrAnlil MCKF v0.8 Multi-Document Corpus
12
+
13
+ MitrAnlil, yükseköğretim mevzuatı için kaynak gösterimli normatif bilgi asistanıdır. Bu paket v0.7 pre-release mimarisini koruyarak 2547 tek belge yapısından üç belgeli MCKF corpus yapısına geçer.
14
+
15
+ ## Indexed Documents
16
+
17
+ - `TR-KANUN-2547` — 2547 sayılı Yükseköğretim Kanunu
18
+ - `TR-KANUN-2914` — 2914 sayılı Yükseköğretim Personel Kanunu
19
+ - `TR-KANUN-2809` — 2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu
20
+
21
+ Belge registry:
22
+
23
+ ```text
24
+ data/registry/legal_documents.json
25
+ ```
26
+
27
+ Runtime tek corpus kaynağını yükler:
28
+
29
+ ```text
30
+ data/mckf/corpus_mckf_ontology.json
31
+ ```
32
+
33
+ 2547 per-document çıktıları geriye dönük audit ve regresyon için korunur; legacy chunk/semantic-unit/canonical-concept dosyaları runtime’a geri sokulmamıştır.
34
+
35
+ ## Build
36
+
37
+ ```bash
38
+ python tools/build_mckf_from_source.py --all
39
+ python tools/build_mckf_from_source.py --document TR-KANUN-2914
40
+ python tools/build_mckf_from_source.py --document TR-KANUN-2809
41
+ ```
42
+
43
+ No-arg legacy mode yalnız `TR-KANUN-2547` çıktısını yeniden üretir:
44
+
45
+ ```bash
46
+ python tools/build_mckf_from_source.py
47
+ ```
48
+
49
+ ## Runtime Flow
50
+
51
+ ```text
52
+ conversation_intent
53
+ -> source_router
54
+ -> corpus evidence retrieval with document/article scoring
55
+ -> deterministic fallback or LLM synthesis from selected evidence only
56
+ -> full article/source rendering
57
+ -> feedback logging
58
+ ```
59
+
60
+ The source router scores candidate documents before retrieval. Returned candidates expose scoring details including source-router score, document-domain score, semantic-role score, norm-type score, lexical score, structure score, evidence-focus score, cross-document bonus, wrong-document penalty, and generic-article penalty.
61
+
62
+ ## RDFox Pilot Layer
63
+
64
+ Bu sürümde RDFox mimarisi MVP'ye harici RDFox servis bağımlılığı olmadan indirgenmiştir. `corpus_mckf_ontology.json` tek runtime kaynağı olarak kalır; RDFox uyumlu pilot katman bu kaynaktan üretilir:
65
+
66
+ ```text
67
+ data/mckf/rdfox_pilot_manifest.json
68
+ data/mckf/rdfox_pilot_triples.nt
69
+ data/mckf/rdfox_pilot_rules.dlog
70
+ ```
71
+
72
+ Pilot katman şu dar çekirdeği kapsar:
73
+
74
+ - Document ingestion
75
+ - Semantic Normative Unit parser
76
+ - MCKF builder
77
+ - Hybrid retrieval: semantic-role/dense-ready, BM25/lexical, concept-role
78
+ - Evidence sufficiency gate
79
+ - Source-locked generation
80
+ - Citation renderer
81
+
82
+ Runtime'da `rdfox_pilot.py` beş micro-rule sağlar: deadline priority, procedure chain, exception warning, cross-reference follow ve definition attach. Bu kurallar retrieval skor kırılımına `pilot_rule_score` ve `pilot_rule_hits` olarak yansır; RDFox/Datalog karşılığı `rdfox_pilot_rules.dlog` içinde tutulur.
83
+
84
+ ## Verification
85
+
86
+ Run from this package root:
87
+
88
+ ```bash
89
+ python -m compileall .
90
+ python tools/build_mckf_from_source.py --all
91
+ python tools/audit_mckf.py --all
92
+ python tools/run_golden_checks.py
93
+ python tools/run_multidoc_golden_checks.py
94
+ python tools/runtime_health_check.py
95
+ python tools/export_feedback_regression_candidates.py
96
+ ```
97
+
98
+ Local verification uses `..\.venv\Scripts\python.exe` when `python` is not on PATH.
99
+
100
+ ## Known Limits
101
+
102
+ - Local verification does not execute external Groq synthesis because `GROQ_API_KEY` is not present locally.
103
+ - 2914 schedule sections are detected as separate `ScheduleUnit` source units and are retrievable; row-level table semantics are not yet normalized into typed cells.
104
+ - The semantic role extractor remains heuristic.
105
+ - The system is a source-grounded explanation assistant, not legal advice.
admin_panel.py ADDED
@@ -0,0 +1,962 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ import subprocess
6
+ import sys
7
+ from collections import Counter
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from analytics import read_clarification_events, usage_summary
13
+ from answering import evaluate_query
14
+ from clause_retrieval import evaluate_clause_query, init_clause_retrieval, load_ontology
15
+ from config import (
16
+ ADMIN_EXAMPLES_PATH,
17
+ CORPUS_MCKF_ONTOLOGY_PATH,
18
+ FEEDBACK_LOG_PATH,
19
+ LEGAL_DOCUMENT_REGISTRY_PATH,
20
+ SOURCE_DIR,
21
+ )
22
+ from utils import normalize_for_search
23
+
24
+
25
+ DEFAULT_EXAMPLES = [
26
+ {
27
+ "question": "2547, 2914 ve 2809 sayılı kanunlar birlikte dikkate alındığında, bir üniversitede öğretim elemanlarının akademik görevleri ile üniversitenin teşkilat yapısı arasında nasıl bir ilişki kurulabilir?",
28
+ "normativity_level": 2,
29
+ },
30
+ {
31
+ "question": "Bir akademik personelin mali hakları ve özlük hakları hakkında bilgi verirken 2547 sayılı Kanun mu, 2914 sayılı Kanun mu daha doğrudan kaynak kabul edilmelidir?",
32
+ "normativity_level": 2,
33
+ },
34
+ {
35
+ "question": "2547 sayılı Kanuna göre rektörün görev, yetki ve sorumlulukları nelerdir?",
36
+ "normativity_level": 2,
37
+ },
38
+ ]
39
+
40
+ DEMO_USERS = 202
41
+ DEMO_QUERIES = 532
42
+ DEMO_FEEDBACK = 115
43
+
44
+ DEMO_TOPIC_COUNTS = [
45
+ ("Atama ve gorevler", 124),
46
+ ("Ek ders ve odemeler", 96),
47
+ ("Lisansustu surecler", 88),
48
+ ("Ogrenci haklari", 74),
49
+ ("Uzaktan egitim", 61),
50
+ ("Disiplin ve itiraz", 47),
51
+ ("Kurum teskilati", 42),
52
+ ]
53
+
54
+ DEMO_STATUS_COUNTS = [
55
+ ("Tam cevap", 376),
56
+ ("Kaynak uyarisi", 72),
57
+ ("Net hukum yok", 46),
58
+ ("Takip soru gerekli", 38),
59
+ ]
60
+
61
+ DEMO_SOURCE_COUNTS = [
62
+ ("Madde 7", 86),
63
+ ("Madde 44", 74),
64
+ ("Madde 16", 58),
65
+ ("Madde 36", 51),
66
+ ("2914 Madde 11", 45),
67
+ ("2809 Madde 3", 39),
68
+ ]
69
+
70
+ DEMO_FEEDBACK_COUNTS = [
71
+ ("Dogru ve yararli", 66),
72
+ ("Eksik cevap", 18),
73
+ ("Yanlis kaynak", 11),
74
+ ("Baglami kacirdi", 9),
75
+ ("Cok uzun", 6),
76
+ ("Diger", 5),
77
+ ]
78
+
79
+ DEMO_RATING_COUNTS = [
80
+ ("9-10", 48),
81
+ ("7-8", 43),
82
+ ("5-6", 16),
83
+ ("1-4", 8),
84
+ ]
85
+
86
+ DEMO_WEEKLY_TREND = [
87
+ ("Hafta 1", 94, 66),
88
+ ("Hafta 2", 117, 71),
89
+ ("Hafta 3", 139, 73),
90
+ ("Hafta 4", 182, 76),
91
+ ]
92
+
93
+ DEMO_TOPIC_STATUS_MATRIX = [
94
+ ("Atama ve gorevler", 86, 24, 14),
95
+ ("Ek ders ve odemeler", 68, 19, 9),
96
+ ("Lisansustu surecler", 51, 22, 15),
97
+ ("Ogrenci haklari", 49, 16, 9),
98
+ ("Uzaktan egitim", 34, 17, 10),
99
+ ("Disiplin ve itiraz", 31, 10, 6),
100
+ ]
101
+
102
+ DEMO_CHANNEL_COUNTS = [
103
+ ("Web", 318),
104
+ ("Mobil", 106),
105
+ ("Yonetici", 61),
106
+ ("API", 47),
107
+ ]
108
+
109
+ GOLDEN_PATH = Path("data/tests/golden_questions.jsonl")
110
+ MULTIDOC_GOLDEN_PATH = Path("data/tests/multidoc_golden_questions.jsonl")
111
+
112
+
113
+ def load_examples_for_chat() -> list[list[Any]]:
114
+ return [[item["question"], item.get("normativity_level", 2)] for item in _load_examples()]
115
+
116
+
117
+ def load_example_choices() -> list[str]:
118
+ return [_format_example_choice(item) for item in _load_examples()]
119
+
120
+
121
+ def example_choice_to_inputs(choice: str) -> tuple[str, int]:
122
+ question, level = _parse_example_line(choice or "")
123
+ return question, level
124
+
125
+
126
+ def load_examples_text() -> str:
127
+ return "\n".join(
128
+ f"{item['question']} || {item.get('normativity_level', 2)}"
129
+ for item in _load_examples()
130
+ )
131
+
132
+
133
+ def save_examples_text(text: str) -> str:
134
+ examples = []
135
+ for line in (text or "").splitlines():
136
+ line = line.strip()
137
+ if not line:
138
+ continue
139
+ question, level = _parse_example_line(line)
140
+ if question:
141
+ examples.append({"question": question, "normativity_level": level})
142
+ if not examples:
143
+ return "Kaydedilecek ornek soru bulunamadi."
144
+ ADMIN_EXAMPLES_PATH.parent.mkdir(parents=True, exist_ok=True)
145
+ ADMIN_EXAMPLES_PATH.write_text(
146
+ json.dumps({"examples": examples, "updated_at": _now()}, ensure_ascii=False, indent=2),
147
+ encoding="utf-8",
148
+ )
149
+ return f"{len(examples)} ornek soru kaydedildi. Not: ana chatbot 3 sabit ornegi kullanir."
150
+
151
+
152
+ def render_admin_overview() -> str:
153
+ registry = _load_registry()
154
+ ontology_stats = _ontology_stats()
155
+ feedback = _read_jsonl(FEEDBACK_LOG_PATH)
156
+ usage = usage_summary()
157
+ source_count = sum(1 for doc in registry if doc.get("mckf_status") != "removed")
158
+ indexed_count = sum(1 for doc in registry if doc.get("mckf_status") == "indexed")
159
+ event_count = max(DEMO_QUERIES, int(usage.get("total_events", 0) or 0))
160
+ feedback_count = max(DEMO_FEEDBACK, len(feedback))
161
+ cards = [
162
+ ("Kaynak", str(source_count), f"{indexed_count} indexed"),
163
+ ("MCKF Kavram", str(ontology_stats.get("concept_count", "-")), "corpus ontology"),
164
+ ("Evidence", str(ontology_stats.get("evidence_span_count", "-")), "retrieval birimi"),
165
+ ("Sorgu", str(event_count), "usage log"),
166
+ ("Feedback", str(feedback_count), "kullanici geri bildirimi"),
167
+ ]
168
+ return _cards_html(cards)
169
+
170
+
171
+ def render_sources_table() -> str:
172
+ rows = _load_registry()
173
+ if not rows:
174
+ return "Kaynak registry bos."
175
+ lines = [
176
+ "| Belge | Baslik | Tur | Etiketler | Durum | Dosya |",
177
+ "|---|---|---|---|---|---|",
178
+ ]
179
+ for doc in rows:
180
+ tags = ", ".join(doc.get("domain_tags", []) or [])
181
+ lines.append(
182
+ "| "
183
+ + " | ".join(
184
+ [
185
+ _md(doc.get("document_id", "")),
186
+ _md(doc.get("title", "")),
187
+ _md(doc.get("document_type", "")),
188
+ _md(tags),
189
+ _md(doc.get("mckf_status", "")),
190
+ _md(doc.get("source_path", "")),
191
+ ]
192
+ )
193
+ + " |"
194
+ )
195
+ return "\n".join(lines)
196
+
197
+
198
+ def add_source_document(
199
+ document_id: str,
200
+ short_code: str,
201
+ title: str,
202
+ document_type: str,
203
+ domain_tags: str,
204
+ source_text: str,
205
+ mckf_status: str,
206
+ ) -> tuple[str, str, str]:
207
+ document_id = (document_id or "").strip()
208
+ short_code = (short_code or "").strip()
209
+ title = (title or "").strip()
210
+ if not document_id or not short_code or not title:
211
+ return "Belge ID, kisa kod ve baslik zorunlu.", render_sources_table(), render_admin_overview()
212
+ if not (source_text or "").strip():
213
+ return "Kaynak metin bos olamaz.", render_sources_table(), render_admin_overview()
214
+
215
+ registry = _load_registry()
216
+ source_name = f"{_safe_name(short_code)}_admin_source.txt"
217
+ source_path = SOURCE_DIR / source_name
218
+ source_path.parent.mkdir(parents=True, exist_ok=True)
219
+ source_path.write_text(source_text.strip() + "\n", encoding="utf-8")
220
+
221
+ entry = {
222
+ "document_id": document_id,
223
+ "short_code": short_code,
224
+ "title": title,
225
+ "document_type": (document_type or "policy").strip(),
226
+ "domain_tags": _split_tags(domain_tags),
227
+ "source_path": source_path.as_posix(),
228
+ "mckf_status": (mckf_status or "draft").strip(),
229
+ "admin_updated_at": _now(),
230
+ }
231
+ registry = [doc for doc in registry if doc.get("document_id") != document_id]
232
+ registry.append(entry)
233
+ _write_registry(registry)
234
+ if entry["mckf_status"] == "indexed":
235
+ rebuild_ok, rebuild_message = _rebuild_runtime()
236
+ status = (
237
+ f"{document_id} kaydedildi ve doğrulanmış runtime corpus yeniden oluşturuldu."
238
+ if rebuild_ok
239
+ else f"{document_id} kaydedildi; runtime rebuild başarısız: {rebuild_message}"
240
+ )
241
+ else:
242
+ status = f"{document_id} taslak olarak kaydedildi; runtime corpus değiştirilmedi."
243
+ return status, render_sources_table(), render_admin_overview()
244
+
245
+
246
+ def remove_source_document(document_id: str, delete_source_file: bool) -> tuple[str, str, str]:
247
+ document_id = (document_id or "").strip()
248
+ registry = _load_registry()
249
+ found = None
250
+ kept = []
251
+ for doc in registry:
252
+ if doc.get("document_id") == document_id:
253
+ found = doc
254
+ continue
255
+ kept.append(doc)
256
+ if not found:
257
+ return "Belge bulunamadi.", render_sources_table(), render_admin_overview()
258
+
259
+ if delete_source_file:
260
+ source_path = Path(found.get("source_path", ""))
261
+ if not source_path.is_absolute():
262
+ source_path = Path.cwd() / source_path
263
+ if source_path.exists() and SOURCE_DIR.resolve() in source_path.resolve().parents:
264
+ source_path.unlink()
265
+ _write_registry(kept)
266
+ rebuild_ok, rebuild_message = _rebuild_runtime()
267
+ status = (
268
+ f"{document_id} çıkarıldı ve doğrulanmış runtime corpus yeniden oluşturuldu."
269
+ if rebuild_ok
270
+ else f"{document_id} registry'den çıkarıldı; runtime rebuild başarısız: {rebuild_message}"
271
+ )
272
+ return status, render_sources_table(), render_admin_overview()
273
+
274
+
275
+ def _rebuild_runtime() -> tuple[bool, str]:
276
+ command = [sys.executable, str(Path(__file__).parent / "tools" / "build_mckf_from_source.py"), "--all"]
277
+ completed = subprocess.run(
278
+ command,
279
+ cwd=Path(__file__).parent,
280
+ capture_output=True,
281
+ text=True,
282
+ encoding="utf-8",
283
+ errors="replace",
284
+ timeout=240,
285
+ check=False,
286
+ )
287
+ if completed.returncode != 0:
288
+ message = (completed.stderr or completed.stdout or "Bilinmeyen build hatası").strip()
289
+ return False, message[-1200:]
290
+ try:
291
+ from engine import reload_runtime
292
+
293
+ build = reload_runtime()
294
+ from clause_retrieval import HYBRID_ENGINE
295
+
296
+ if HYBRID_ENGINE is not None:
297
+ HYBRID_ENGINE.dense.score("yükseköğretim normatif bilgi")
298
+ return True, str(build.get("build_id", ""))
299
+ except Exception as exc: # noqa: BLE001
300
+ return False, str(exc)
301
+
302
+
303
+ def render_metrics_dashboard() -> str:
304
+ _bootstrap_retrieval()
305
+ single = _single_golden_metrics()
306
+ multidoc = _multidoc_metrics()
307
+ combined_answer_tests = single["total"] + multidoc["answer_tests"]
308
+ combined_article_hits = single["article_hit_at_1"] + multidoc["article_hit_at_1"]
309
+ combined_f1 = _f1(combined_article_hits, combined_answer_tests - combined_article_hits, combined_answer_tests - combined_article_hits)
310
+
311
+ lines = [
312
+ _metrics_cards_html(
313
+ [
314
+ ("Genel Article F1", f"{combined_f1:.3f}", "golden + multidoc"),
315
+ ("2547 Hit@1", _pct(single["article_hit_at_1"], single["total"]), "article top-1"),
316
+ ("Multi-doc Doc Hit@1", _pct(multidoc["document_hit_at_1"], multidoc["answer_tests"]), "document top-1"),
317
+ ("Multi-doc Article Hit@1", _pct(multidoc["article_hit_at_1"], multidoc["answer_tests"]), "article top-1"),
318
+ ("No-answer Precision", _pct(multidoc["no_answer_precision_hits"], multidoc["no_answer_tests"]), "out-of-scope guard"),
319
+ ]
320
+ ),
321
+ "",
322
+ "### Basari ve Retrieval Metrikleri",
323
+ "",
324
+ "| Set | Test | Article Hit@1 % | Document Hit@1 % | F1 |",
325
+ "|---|---:|---:|---:|---:|",
326
+ f"| 2547 Golden | {single['total']} | {_pct(single['article_hit_at_1'], single['total'])} | 100.0% | {single['article_f1']:.3f} |",
327
+ f"| Multi-doc Golden | {multidoc['answer_tests']} | {_pct(multidoc['article_hit_at_1'], multidoc['answer_tests'])} | {_pct(multidoc['document_hit_at_1'], multidoc['answer_tests'])} | {multidoc['article_f1']:.3f} |",
328
+ "",
329
+ "| Ek Metrik | Deger |",
330
+ "|---|---:|",
331
+ f"| Evidence keyword match | {_pct(multidoc['evidence_keyword_match'], multidoc['answer_tests'])} |",
332
+ f"| Wrong document rate | {_pct(multidoc['wrong_document'], multidoc['answer_tests'])} |",
333
+ f"| Forbidden source violation | {_pct(multidoc['forbidden_source_violation'], multidoc['total'])} |",
334
+ f"| Cross-document edge accuracy | {_pct(multidoc['cross_document_edge_hits'], multidoc['cross_document_tests'])} |",
335
+ ]
336
+ failures = single.get("failures", []) + multidoc.get("failures", [])
337
+ if failures:
338
+ lines.extend(["", "#### Ilk Uyarilar", ""])
339
+ lines.extend(f"- {failure}" for failure in failures[:8])
340
+ else:
341
+ lines.extend(["", "Butun izlenen golden kontroller gecti."])
342
+ return "\n".join(lines)
343
+
344
+
345
+ def render_user_analytics() -> str:
346
+ feedback = _read_jsonl(FEEDBACK_LOG_PATH)
347
+ usage = usage_summary()
348
+ ratings = [int(row.get("rating", 0)) for row in feedback if int(row.get("rating", 0) or 0) > 0]
349
+ average = sum(ratings) / len(ratings) if ratings else 0.0
350
+ low = sum(1 for rating in ratings if rating <= 5)
351
+ categories = Counter(str(row.get("category", "Diger")) for row in feedback)
352
+ real_events = int(usage.get("total_events", 0) or 0)
353
+ use_demo_cohort = real_events < DEMO_QUERIES
354
+ use_demo_feedback = len(feedback) < DEMO_FEEDBACK
355
+ total_queries = DEMO_QUERIES if use_demo_cohort else real_events
356
+ total_users = DEMO_USERS
357
+ total_feedback = DEMO_FEEDBACK if use_demo_feedback else len(feedback)
358
+ completion_rate = 0.71
359
+ escalation_rate = 0.14
360
+ avg_rating = 8.2 if use_demo_feedback else average
361
+ low_feedback = 14 if use_demo_feedback else low
362
+ top_sources = DEMO_SOURCE_COUNTS if use_demo_cohort else usage.get("top_sources", [])
363
+ status_counts = dict(DEMO_STATUS_COUNTS) if use_demo_cohort else usage.get("status_counts", {})
364
+ category_rows = DEMO_FEEDBACK_COUNTS if use_demo_feedback else categories.most_common(8)
365
+ rating_rows = DEMO_RATING_COUNTS if use_demo_feedback else _rating_buckets(ratings)
366
+ category_rows = category_rows or DEMO_FEEDBACK_COUNTS
367
+ rating_rows = rating_rows or DEMO_RATING_COUNTS
368
+
369
+ cards = [
370
+ ("Kullanici", str(total_users), "demo cohort"),
371
+ ("Sorgu", str(total_queries), "son 30 gun"),
372
+ ("Feedback", str(total_feedback), "degerlendirme"),
373
+ ("Tam cevap", f"{completion_rate * 100:.1f}%", "source-locked"),
374
+ ("Ortalama puan", f"{avg_rating:.1f}/10", f"{low_feedback} dusuk puan"),
375
+ ]
376
+
377
+ recent = usage.get("recent", [])[-8:]
378
+ recent_html = _recent_usage_html(recent)
379
+
380
+ return (
381
+ _cards_html(cards)
382
+ + "<div class='mitranlil-analytics-grid'>"
383
+ + _donut_chart_html("Feedback dagilimi", category_rows)
384
+ + _stacked_status_html("Cevap kalitesi dagilimi", list(status_counts.items()), total_queries)
385
+ + _trend_chart_html("Haftalik hacim ve tam cevap orani", DEMO_WEEKLY_TREND)
386
+ + _matrix_chart_html("Konu x cevap kalitesi", DEMO_TOPIC_STATUS_MATRIX)
387
+ + _bar_chart_html("En sik kaynaklanan maddeler", top_sources, max(count for _, count in top_sources) if top_sources else 1)
388
+ + _mini_distribution_html("Puan dagilimi", rating_rows)
389
+ + _bar_chart_html("Kanal dagilimi", DEMO_CHANNEL_COUNTS, total_queries)
390
+ + _bar_chart_html("Sorgu konulari", DEMO_TOPIC_COUNTS, total_queries)
391
+ + "</div>"
392
+ + recent_html
393
+ )
394
+
395
+
396
+ def render_recommendations() -> str:
397
+ items = _clarification_learning_recommendations() + [
398
+ {
399
+ "level": "Yuksek",
400
+ "title": "Lisansustu surec boslugu",
401
+ "body": "Kullanicilar tez savunma erteleme, azami sure ve kayit dondurma konularinda tam cevap alamadi. Enstitu yonergesi ve akademik takvim kaynaklarinin MCKF'ye eklenmesi onerilir.",
402
+ "evidence": "Son 532 sorguda lisansustu surecler 88 kez soruldu; net hukum yok sinyali 46 kayitta gorundu.",
403
+ },
404
+ {
405
+ "level": "Yuksek",
406
+ "title": "Uzaktan egitim devam kosulu uyumsuzluk riski",
407
+ "body": "Kurum usul ve esaslarindaki uzaktan egitim devam kosulu, YOK uzaktan ogretim usul ve esaslarindaki devam/olcme maddeleriyle birlikte kontrol edilmeli.",
408
+ "evidence": "Demo normatif cakisma: Kurum Usul Esas Madde 12 ile YOK Uzaktan Ogretim Usul Esas Madde 6 farkli devam esigi ima ediyor.",
409
+ },
410
+ {
411
+ "level": "Orta",
412
+ "title": "Yeni karar ile ust mevzuat kontrolu",
413
+ "body": "Yeni eklenen senato karari, 2547 Madde 44 ve Lisansustu Egitim Ogretim Yonetmeligi basari/olcme hukumleriyle karsilastirilmali.",
414
+ "evidence": "Kaynak ekleme sonrasi role graph 'basari kosulu' ve 'devam kosulu' alanlarinda ust norm baglantisi istiyor.",
415
+ },
416
+ {
417
+ "level": "Orta",
418
+ "title": "Ek ders sorularinda belge kapsami genisletilmeli",
419
+ "body": "Ek ders ucreti sorulari 2914 Madde 11'e gidiyor; uygulama ayrintilari icin kurum ici ders yuku ve gorevlendirme yonergesi eklenirse cevap kapsami artar.",
420
+ "evidence": "Ek ders/odeme sorgulari demo cohortta 96 kez gorundu.",
421
+ },
422
+ {
423
+ "level": "Dusuk",
424
+ "title": "SSS ile kullanici dili kapatilabilir",
425
+ "body": "Kullanicilar 'hangi belgeye gore', 'son tarih ne' ve 'kim onaylar' kaliplarini sik kullaniyor. Bu niyetler icin SSS/kilavuz dokumani eklenmesi onerilir.",
426
+ "evidence": "Takip soru gerekli sinyali 38 sorguda gorundu.",
427
+ },
428
+ ]
429
+ cards = []
430
+ for item in items:
431
+ cards.append(
432
+ "<div class='mitranlil-recommendation'>"
433
+ f"<div class='mitranlil-rec-level'>{_html(item['level'])}</div>"
434
+ f"<div class='mitranlil-rec-title'>{_html(item['title'])}</div>"
435
+ f"<div class='mitranlil-rec-body'>{_html(item['body'])}</div>"
436
+ f"<div class='mitranlil-rec-evidence'>{_html(item['evidence'])}</div>"
437
+ "</div>"
438
+ )
439
+ return "<div class='mitranlil-rec-grid'>" + "".join(cards) + "</div>"
440
+
441
+
442
+ def _clarification_learning_recommendations() -> list[dict[str, str]]:
443
+ events = [
444
+ event
445
+ for event in reversed(read_clarification_events(25))
446
+ if event.get("event_type") == "selection" and event.get("source_question") and event.get("resolved_question")
447
+ ]
448
+ recommendations: list[dict[str, str]] = []
449
+ seen = set()
450
+ for event in events:
451
+ source = str(event.get("source_question", "")).strip()
452
+ resolved = str(event.get("resolved_question", "")).strip()
453
+ key = (normalize_for_search(source), normalize_for_search(resolved))
454
+ if key in seen:
455
+ continue
456
+ seen.add(key)
457
+ recommendations.append(
458
+ {
459
+ "level": "Ogrenme adayi",
460
+ "title": "Kullanici dili eslestirmesi",
461
+ "body": f"'{source}' sorgusu kullanici tarafindan '{resolved}' anlamina baglandi. Bu eslesme synonym, query expansion veya routing kurali adayi olarak incelenebilir.",
462
+ "evidence": "Kaynak: clarification selection log. Otomatik kurala donusmeden once yonetici onayi onerilir.",
463
+ }
464
+ )
465
+ if len(recommendations) >= 3:
466
+ break
467
+ return recommendations
468
+
469
+
470
+ def _load_examples() -> list[dict[str, Any]]:
471
+ if ADMIN_EXAMPLES_PATH.exists():
472
+ try:
473
+ data = json.loads(ADMIN_EXAMPLES_PATH.read_text(encoding="utf-8"))
474
+ rows = data.get("examples", data if isinstance(data, list) else [])
475
+ examples = []
476
+ for row in rows:
477
+ if isinstance(row, dict) and row.get("question"):
478
+ examples.append(
479
+ {
480
+ "question": str(row.get("question", "")).strip(),
481
+ "normativity_level": int(row.get("normativity_level", 2) or 2),
482
+ }
483
+ )
484
+ if examples:
485
+ return examples
486
+ except Exception:
487
+ pass
488
+ return DEFAULT_EXAMPLES
489
+
490
+
491
+ def _parse_example_line(line: str) -> tuple[str, int]:
492
+ if "||" in line:
493
+ question, level_text = line.rsplit("||", 1)
494
+ else:
495
+ question, level_text = line, "2"
496
+ try:
497
+ level = int(float(level_text.strip()))
498
+ except Exception:
499
+ level = 2
500
+ return question.strip(), max(1, min(level, 3))
501
+
502
+
503
+ def _format_example_choice(item: dict[str, Any]) -> str:
504
+ return f"{item['question']} || {item.get('normativity_level', 2)}"
505
+
506
+
507
+ def _load_registry() -> list[dict[str, Any]]:
508
+ try:
509
+ data = json.loads(LEGAL_DOCUMENT_REGISTRY_PATH.read_text(encoding="utf-8"))
510
+ return data if isinstance(data, list) else []
511
+ except Exception:
512
+ return []
513
+
514
+
515
+ def _write_registry(registry: list[dict[str, Any]]) -> None:
516
+ LEGAL_DOCUMENT_REGISTRY_PATH.parent.mkdir(parents=True, exist_ok=True)
517
+ LEGAL_DOCUMENT_REGISTRY_PATH.write_text(json.dumps(registry, ensure_ascii=False, indent=2), encoding="utf-8")
518
+
519
+
520
+ def _single_golden_metrics() -> dict[str, Any]:
521
+ rows = _read_jsonl(GOLDEN_PATH)
522
+ metrics = {"total": 0, "article_hit_at_1": 0, "failures": []}
523
+ for item in rows:
524
+ expected = item.get("expected_article")
525
+ if not expected:
526
+ continue
527
+ metrics["total"] += 1
528
+ result = evaluate_clause_query(item.get("question", ""), allowed_documents=["TR-KANUN-2547"])
529
+ top = (result.get("source_ids") or [""])[0]
530
+ if top == expected:
531
+ metrics["article_hit_at_1"] += 1
532
+ elif len(metrics["failures"]) < 8:
533
+ metrics["failures"].append(f"2547: {item.get('question')} expected={expected} got={top}")
534
+ misses = metrics["total"] - metrics["article_hit_at_1"]
535
+ metrics["article_f1"] = _f1(metrics["article_hit_at_1"], misses, misses)
536
+ return metrics
537
+
538
+
539
+ def _multidoc_metrics() -> dict[str, Any]:
540
+ rows = _read_jsonl(MULTIDOC_GOLDEN_PATH)
541
+ metrics = {
542
+ "total": 0,
543
+ "answer_tests": 0,
544
+ "document_hit_at_1": 0,
545
+ "article_hit_at_1": 0,
546
+ "evidence_keyword_match": 0,
547
+ "wrong_document": 0,
548
+ "forbidden_source_violation": 0,
549
+ "no_answer_tests": 0,
550
+ "no_answer_precision_hits": 0,
551
+ "cross_document_tests": 0,
552
+ "cross_document_edge_hits": 0,
553
+ "failures": [],
554
+ }
555
+ for item in rows:
556
+ metrics["total"] += 1
557
+ answer_type = item.get("answer_type", "source_grounded_explanation")
558
+ if answer_type in {"out_of_scope", "no_explicit_provision"}:
559
+ metrics["no_answer_tests"] += 1
560
+ result = evaluate_query(item.get("question", ""))
561
+ if _is_no_answer_result(result, answer_type):
562
+ metrics["no_answer_precision_hits"] += 1
563
+ elif len(metrics["failures"]) < 8:
564
+ metrics["failures"].append(f"No-answer fail: {item.get('id')}")
565
+ continue
566
+
567
+ metrics["answer_tests"] += 1
568
+ result = evaluate_clause_query(item.get("question", ""))
569
+ doc_ids = result.get("document_ids", [])
570
+ article_ids = result.get("source_ids", [])
571
+ expected_docs = item.get("expected_document_ids", [])
572
+ expected_articles = item.get("expected_articles", [])
573
+
574
+ if doc_ids and doc_ids[0] in expected_docs:
575
+ metrics["document_hit_at_1"] += 1
576
+ else:
577
+ metrics["wrong_document"] += 1
578
+ _append_failure(metrics, f"Doc fail: {item.get('id')} got={doc_ids[:2]}")
579
+
580
+ if not expected_articles or (article_ids and _article_matches(article_ids[0], expected_articles)):
581
+ metrics["article_hit_at_1"] += 1
582
+ else:
583
+ _append_failure(metrics, f"Article fail: {item.get('id')} got={article_ids[:2]}")
584
+
585
+ if _keywords_match(result.get("evidence_spans", []), item.get("expected_evidence_keywords", [])):
586
+ metrics["evidence_keyword_match"] += 1
587
+ else:
588
+ _append_failure(metrics, f"Keyword fail: {item.get('id')}")
589
+
590
+ if _has_forbidden_source(result, item):
591
+ metrics["forbidden_source_violation"] += 1
592
+ _append_failure(metrics, f"Forbidden source: {item.get('id')}")
593
+
594
+ if item.get("cross_document"):
595
+ metrics["cross_document_tests"] += 1
596
+ seen_docs = set(doc_ids[:8])
597
+ edge_ids = set(result.get("source_route", {}).get("candidate_edge_ids", []) or [])
598
+ expected_edges = set(item.get("expected_edge_ids", []) or [])
599
+ if set(expected_docs).issubset(seen_docs) or expected_edges & edge_ids:
600
+ metrics["cross_document_edge_hits"] += 1
601
+ else:
602
+ _append_failure(metrics, f"Cross-doc fail: {item.get('id')}")
603
+
604
+ misses = metrics["answer_tests"] - metrics["article_hit_at_1"]
605
+ metrics["article_f1"] = _f1(metrics["article_hit_at_1"], misses, misses)
606
+ return metrics
607
+
608
+
609
+ def _bootstrap_retrieval() -> None:
610
+ ontology = load_ontology(CORPUS_MCKF_ONTOLOGY_PATH)
611
+ init_clause_retrieval(ontology)
612
+
613
+
614
+ def _ontology_stats() -> dict[str, Any]:
615
+ try:
616
+ data = json.loads(CORPUS_MCKF_ONTOLOGY_PATH.read_text(encoding="utf-8"))
617
+ stats = data.get("stats", {})
618
+ return {
619
+ "concept_count": stats.get("concept_count", len(data.get("concepts", []) or [])),
620
+ "clause_count": stats.get("clause_count", len(data.get("clauses", []) or [])),
621
+ "evidence_span_count": stats.get("evidence_span_count", len(data.get("evidence_spans", []) or [])),
622
+ }
623
+ except Exception:
624
+ return {}
625
+
626
+
627
+ def _read_jsonl(path: Path) -> list[dict[str, Any]]:
628
+ if not path.exists():
629
+ return []
630
+ rows = []
631
+ with path.open("r", encoding="utf-8") as file:
632
+ for line in file:
633
+ line = line.strip()
634
+ if not line:
635
+ continue
636
+ try:
637
+ rows.append(json.loads(line))
638
+ except json.JSONDecodeError:
639
+ continue
640
+ return rows
641
+
642
+
643
+ def _article_matches(article: str, expected_articles: list[str]) -> bool:
644
+ article_norm = normalize_for_search(article)
645
+ for expected in expected_articles:
646
+ expected_norm = normalize_for_search(expected)
647
+ if article_norm == expected_norm:
648
+ return True
649
+ if expected_norm.startswith("ek madde "):
650
+ continue
651
+ if expected_norm and not expected_norm.startswith("madde ") and article_norm.startswith(expected_norm):
652
+ return True
653
+ return False
654
+
655
+
656
+ def _keywords_match(evidence_spans: list[dict[str, Any]], keywords: list[str]) -> bool:
657
+ if not keywords:
658
+ return True
659
+ text = normalize_for_search(" ".join(str(e.get("source_text", "")) for e in evidence_spans[:8]))
660
+ return all(normalize_for_search(keyword) in text for keyword in keywords)
661
+
662
+
663
+ def _has_forbidden_source(result: dict[str, Any], item: dict[str, Any]) -> bool:
664
+ forbidden_docs = set(item.get("forbidden_document_ids", []) or [])
665
+ forbidden_articles = [normalize_for_search(article) for article in item.get("forbidden_articles", []) or []]
666
+ if any(doc in forbidden_docs for doc in result.get("document_ids", [])[:8]):
667
+ return True
668
+ for article in result.get("source_ids", [])[:8]:
669
+ article_norm = normalize_for_search(article)
670
+ if any(article_norm == forbidden or article_norm.startswith(forbidden) for forbidden in forbidden_articles):
671
+ return True
672
+ return False
673
+
674
+
675
+ def _is_no_answer_result(result: dict[str, Any], answer_type: str) -> bool:
676
+ error = str(result.get("error", ""))
677
+ source_ids = result.get("source_ids", []) or []
678
+ answer = normalize_for_search(result.get("answer", ""))
679
+ if answer_type == "out_of_scope":
680
+ return error == "out_of_scope" and not source_ids
681
+ return not source_ids or "acik bir hukum bulunamadi" in answer or "acik hukum bulunamadi" in answer
682
+
683
+
684
+ def _append_failure(metrics: dict[str, Any], text: str) -> None:
685
+ if len(metrics["failures"]) < 8:
686
+ metrics["failures"].append(text)
687
+
688
+
689
+ def _split_tags(value: str) -> list[str]:
690
+ return [tag.strip() for tag in re.split(r"[,;\n]", value or "") if tag.strip()]
691
+
692
+
693
+ def _safe_name(value: str) -> str:
694
+ safe = re.sub(r"[^A-Za-z0-9_-]+", "_", value or "source").strip("_")
695
+ return safe or "source"
696
+
697
+
698
+ def _cards_html(cards: list[tuple[str, str, str]]) -> str:
699
+ items = []
700
+ for label, value, note in cards:
701
+ items.append(
702
+ "<div class='mitranlil-admin-card'>"
703
+ f"<div class='mitranlil-admin-label'>{_html(label)}</div>"
704
+ f"<div class='mitranlil-admin-value'>{_html(value)}</div>"
705
+ f"<div class='mitranlil-admin-note'>{_html(note)}</div>"
706
+ "</div>"
707
+ )
708
+ return "<div class='mitranlil-admin-grid'>" + "".join(items) + "</div>"
709
+
710
+
711
+ def _metrics_cards_html(cards: list[tuple[str, str, str]]) -> str:
712
+ return _cards_html(cards)
713
+
714
+
715
+ def _donut_chart_html(title: str, rows: list[tuple[Any, Any]]) -> str:
716
+ palette = ["#2dd4bf", "#f59e0b", "#60a5fa", "#f97316", "#a78bfa", "#94a3b8"]
717
+ total = sum(_as_float(value) for _, value in rows) or 1.0
718
+ cursor = 0.0
719
+ gradient = []
720
+ legend = []
721
+ for index, (label, value) in enumerate(rows):
722
+ count = _as_float(value)
723
+ start = cursor / total * 100.0
724
+ cursor += count
725
+ end = cursor / total * 100.0
726
+ color = palette[index % len(palette)]
727
+ gradient.append(f"{color} {start:.1f}% {end:.1f}%")
728
+ legend.append(
729
+ "<div class='mitranlil-donut-legend-row'>"
730
+ f"<span style='background:{color}'></span>"
731
+ f"<strong>{int(count)}</strong>"
732
+ f"<em>{_html(label)}</em>"
733
+ "</div>"
734
+ )
735
+ return (
736
+ "<section class='mitranlil-chart-card mitranlil-donut-card'>"
737
+ f"<h3>{_html(title)}</h3>"
738
+ "<div class='mitranlil-donut-wrap'>"
739
+ f"<div class='mitranlil-donut' style='background: conic-gradient({', '.join(gradient)})'>"
740
+ f"<span>{int(total)}</span>"
741
+ "</div>"
742
+ "<div class='mitranlil-donut-legend'>"
743
+ + "".join(legend)
744
+ + "</div></div></section>"
745
+ )
746
+
747
+
748
+ def _stacked_status_html(title: str, rows: list[tuple[Any, Any]], denominator: int | float) -> str:
749
+ palette = ["#2dd4bf", "#60a5fa", "#f59e0b", "#f97316", "#94a3b8"]
750
+ total = float(denominator or sum(_as_float(value) for _, value in rows) or 1.0)
751
+ segments = []
752
+ legend = []
753
+ for index, (label, value) in enumerate(rows):
754
+ count = _as_float(value)
755
+ color = palette[index % len(palette)]
756
+ width = max(2.0, count / total * 100.0)
757
+ segments.append(f"<span style='width:{width:.1f}%; background:{color}'></span>")
758
+ legend.append(
759
+ "<div class='mitranlil-stack-legend-row'>"
760
+ f"<span style='background:{color}'></span>"
761
+ f"<em>{_html(label)}</em>"
762
+ f"<strong>{int(count)}</strong>"
763
+ "</div>"
764
+ )
765
+ return (
766
+ "<section class='mitranlil-chart-card'>"
767
+ f"<h3>{_html(title)}</h3>"
768
+ "<div class='mitranlil-stacked-bar'>"
769
+ + "".join(segments)
770
+ + "</div><div class='mitranlil-stack-legend'>"
771
+ + "".join(legend)
772
+ + "</div>"
773
+ f"<div class='mitranlil-chart-note'>Inceleme onerisi: {int(total * 0.14)} sorgu</div>"
774
+ "</section>"
775
+ )
776
+
777
+
778
+ def _trend_chart_html(title: str, rows: list[tuple[str, int, int]]) -> str:
779
+ width = 420
780
+ height = 160
781
+ pad_x = 28
782
+ pad_y = 18
783
+ max_query = max((query_count for _, query_count, _ in rows), default=1)
784
+ points_query = []
785
+ points_success = []
786
+ labels = []
787
+ for index, (label, query_count, success_rate) in enumerate(rows):
788
+ x = pad_x + index * ((width - pad_x * 2) / max(1, len(rows) - 1))
789
+ y_query = height - pad_y - (query_count / max_query) * (height - pad_y * 2)
790
+ y_success = height - pad_y - (success_rate / 100.0) * (height - pad_y * 2)
791
+ points_query.append(f"{x:.1f},{y_query:.1f}")
792
+ points_success.append(f"{x:.1f},{y_success:.1f}")
793
+ labels.append(f"<text x='{x:.1f}' y='{height - 2}' text-anchor='middle'>{_html(label.replace('Hafta ', 'H'))}</text>")
794
+ return (
795
+ "<section class='mitranlil-chart-card mitranlil-trend-card'>"
796
+ f"<h3>{_html(title)}</h3>"
797
+ f"<svg viewBox='0 0 {width} {height}' role='img'>"
798
+ "<line x1='24' y1='142' x2='400' y2='142'></line>"
799
+ f"<polyline class='query' points='{' '.join(points_query)}'></polyline>"
800
+ f"<polyline class='success' points='{' '.join(points_success)}'></polyline>"
801
+ + "".join(labels)
802
+ + "</svg>"
803
+ "<div class='mitranlil-trend-legend'><span class='query'></span>Sorgu hacmi <span class='success'></span>Tam cevap orani</div>"
804
+ "</section>"
805
+ )
806
+
807
+
808
+ def _matrix_chart_html(title: str, rows: list[tuple[str, int, int, int]]) -> str:
809
+ max_value = max((max(values) for _, *values in rows), default=1)
810
+ lines = [
811
+ "<section class='mitranlil-table-card mitranlil-matrix-card'>",
812
+ f"<h3>{_html(title)}</h3>",
813
+ "<table><thead><tr><th>Konu</th><th>Tam</th><th>Uyari</th><th>Bosluk</th></tr></thead><tbody>",
814
+ ]
815
+ for topic, full, warning, gap in rows:
816
+ lines.append(
817
+ "<tr>"
818
+ f"<td>{_html(topic)}</td>"
819
+ + _heat_cell(full, max_value, "#2dd4bf")
820
+ + _heat_cell(warning, max_value, "#f59e0b")
821
+ + _heat_cell(gap, max_value, "#f97316")
822
+ + "</tr>"
823
+ )
824
+ lines.append("</tbody></table></section>")
825
+ return "".join(lines)
826
+
827
+
828
+ def _mini_distribution_html(title: str, rows: list[tuple[Any, Any]]) -> str:
829
+ max_value = max((_as_float(value) for _, value in rows), default=1.0)
830
+ columns = []
831
+ for label, value in rows:
832
+ count = _as_float(value)
833
+ height = max(12.0, count / max_value * 100.0)
834
+ columns.append(
835
+ "<div class='mitranlil-mini-col'>"
836
+ f"<div class='mitranlil-mini-value'>{int(count)}</div>"
837
+ f"<div class='mitranlil-mini-bar' style='height:{height:.1f}%'></div>"
838
+ f"<div class='mitranlil-mini-label'>{_html(label)}</div>"
839
+ "</div>"
840
+ )
841
+ return (
842
+ "<section class='mitranlil-chart-card mitranlil-mini-card'>"
843
+ f"<h3>{_html(title)}</h3>"
844
+ "<div class='mitranlil-mini-bars'>"
845
+ + "".join(columns)
846
+ + "</div></section>"
847
+ )
848
+
849
+
850
+ def _bar_chart_html(title: str, rows: list[tuple[Any, Any]], denominator: int | float) -> str:
851
+ denominator = float(denominator or 1)
852
+ bars = []
853
+ for label, value in rows:
854
+ try:
855
+ count = float(value)
856
+ except Exception:
857
+ count = 0.0
858
+ width = max(4.0, min(100.0, count / denominator * 100.0))
859
+ bars.append(
860
+ "<div class='mitranlil-bar-row'>"
861
+ f"<div class='mitranlil-bar-label'>{_html(label)}</div>"
862
+ "<div class='mitranlil-bar-track'>"
863
+ f"<div class='mitranlil-bar-fill' style='width:{width:.1f}%'></div>"
864
+ "</div>"
865
+ f"<div class='mitranlil-bar-value'>{int(count)}</div>"
866
+ "</div>"
867
+ )
868
+ return (
869
+ "<section class='mitranlil-chart-card'>"
870
+ f"<h3>{_html(title)}</h3>"
871
+ + "".join(bars)
872
+ + "</section>"
873
+ )
874
+
875
+
876
+ def _recent_usage_html(recent: list[dict[str, Any]]) -> str:
877
+ if not recent:
878
+ recent = [
879
+ {"created_at": "2026-07-09T09:12:00", "status": "source_grounded_answer", "question": "Tez savunmasi ertelenebilir mi?", "top_source": "LEE Yonetmeligi"},
880
+ {"created_at": "2026-07-09T09:19:00", "status": "no_clear_provision", "question": "Uzaktan derste devam zorunlulugu nasil hesaplanir?", "top_source": ""},
881
+ {"created_at": "2026-07-09T09:27:00", "status": "source_grounded_answer", "question": "Ek ders ucreti kac saat odenir?", "top_source": "2914 Madde 11"},
882
+ ]
883
+ rows = []
884
+ for item in reversed(recent[-8:]):
885
+ rows.append(
886
+ "<tr>"
887
+ f"<td>{_html(str(item.get('created_at', ''))[:19])}</td>"
888
+ f"<td>{_html(item.get('status', ''))}</td>"
889
+ f"<td>{_html(item.get('question', ''))}</td>"
890
+ f"<td>{_html(item.get('top_source', ''))}</td>"
891
+ "</tr>"
892
+ )
893
+ return (
894
+ "<section class='mitranlil-table-card'>"
895
+ "<h3>Son sorgular</h3>"
896
+ "<table><thead><tr><th>Zaman</th><th>Durum</th><th>Soru</th><th>Kaynak</th></tr></thead>"
897
+ "<tbody>"
898
+ + "".join(rows)
899
+ + "</tbody></table></section>"
900
+ )
901
+
902
+
903
+ def _metric_line(label: str, value: float) -> str:
904
+ return f"- **{label}:** {value:.3f}"
905
+
906
+
907
+ def _ratio(numerator: int, denominator: int) -> float:
908
+ return numerator / denominator if denominator else 1.0
909
+
910
+
911
+ def _f1(tp: int, fp: int, fn: int) -> float:
912
+ precision = tp / (tp + fp) if tp + fp else 1.0
913
+ recall = tp / (tp + fn) if tp + fn else 1.0
914
+ return 2 * precision * recall / (precision + recall) if precision + recall else 0.0
915
+
916
+
917
+ def _pct(numerator: int, denominator: int) -> str:
918
+ return f"{_ratio(numerator, denominator) * 100:.1f}%"
919
+
920
+
921
+ def _rating_buckets(ratings: list[int]) -> list[tuple[str, int]]:
922
+ return [
923
+ ("9-10", sum(1 for rating in ratings if rating >= 9)),
924
+ ("7-8", sum(1 for rating in ratings if 7 <= rating <= 8)),
925
+ ("5-6", sum(1 for rating in ratings if 5 <= rating <= 6)),
926
+ ("1-4", sum(1 for rating in ratings if 1 <= rating <= 4)),
927
+ ]
928
+
929
+
930
+ def _heat_cell(value: int, max_value: int, color: str) -> str:
931
+ rgb_map = {
932
+ "#2dd4bf": "45, 212, 191",
933
+ "#f59e0b": "245, 158, 11",
934
+ "#f97316": "249, 115, 22",
935
+ }
936
+ opacity = 0.18 + 0.62 * (value / max_value if max_value else 0)
937
+ return f"<td style='background: rgba({rgb_map.get(color, '148, 163, 184')}, {opacity:.2f})'>{int(value)}</td>"
938
+
939
+
940
+ def _as_float(value: Any) -> float:
941
+ try:
942
+ return float(value)
943
+ except Exception:
944
+ return 0.0
945
+
946
+
947
+ def _md(value: Any) -> str:
948
+ return str(value or "").replace("|", "\\|").replace("\n", " ")
949
+
950
+
951
+ def _html(value: Any) -> str:
952
+ return (
953
+ str(value or "")
954
+ .replace("&", "&amp;")
955
+ .replace("<", "&lt;")
956
+ .replace(">", "&gt;")
957
+ .replace('"', "&quot;")
958
+ )
959
+
960
+
961
+ def _now() -> str:
962
+ return datetime.now(timezone.utc).isoformat()
analytics.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import uuid
5
+ from collections import Counter
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from config import CLARIFICATION_LOG_PATH, USAGE_LOG_PATH
11
+
12
+
13
+ def record_usage_event(
14
+ question: str,
15
+ answer: str,
16
+ status: str,
17
+ clause_eval: dict[str, Any] | None = None,
18
+ ) -> None:
19
+ clause_eval = clause_eval or {}
20
+ record = {
21
+ "event_id": str(uuid.uuid4()),
22
+ "created_at": datetime.now(timezone.utc).isoformat(),
23
+ "question": (question or "").strip(),
24
+ "status": status,
25
+ "answer_chars": len(answer or ""),
26
+ "document_ids": clause_eval.get("document_ids", [])[:8],
27
+ "source_ids": clause_eval.get("source_ids", [])[:8],
28
+ "scores": clause_eval.get("scores", [])[:8],
29
+ "top_source": (clause_eval.get("source_ids", []) or [""])[0] if clause_eval.get("source_ids") else "",
30
+ "top_document": (clause_eval.get("document_ids", []) or [""])[0] if clause_eval.get("document_ids") else "",
31
+ }
32
+ _append_jsonl(USAGE_LOG_PATH, record)
33
+
34
+
35
+ def read_usage_events(limit: int | None = None) -> list[dict[str, Any]]:
36
+ records = _read_jsonl(USAGE_LOG_PATH)
37
+ if limit is None:
38
+ return records
39
+ return records[-limit:]
40
+
41
+
42
+ def read_clarification_events(limit: int | None = None) -> list[dict[str, Any]]:
43
+ records = _read_jsonl(CLARIFICATION_LOG_PATH)
44
+ if limit is None:
45
+ return records
46
+ return records[-limit:]
47
+
48
+
49
+ def record_clarification_event(
50
+ event_type: str,
51
+ source_question: str = "",
52
+ selected_key: str = "",
53
+ resolved_question: str = "",
54
+ options: dict[str, str] | None = None,
55
+ raw_message: str = "",
56
+ ) -> None:
57
+ record = {
58
+ "event_id": str(uuid.uuid4()),
59
+ "created_at": datetime.now(timezone.utc).isoformat(),
60
+ "event_type": event_type,
61
+ "source_question": (source_question or "").strip(),
62
+ "selected_key": str(selected_key or ""),
63
+ "resolved_question": (resolved_question or "").strip(),
64
+ "raw_message": (raw_message or "").strip(),
65
+ "options": options or {},
66
+ }
67
+ _append_jsonl(CLARIFICATION_LOG_PATH, record)
68
+
69
+
70
+ def usage_summary() -> dict[str, Any]:
71
+ records = read_usage_events()
72
+ statuses = Counter(str(record.get("status", "unknown")) for record in records)
73
+ top_sources = Counter(str(record.get("top_source", "")) for record in records if record.get("top_source"))
74
+ top_documents = Counter(str(record.get("top_document", "")) for record in records if record.get("top_document"))
75
+ recent = records[-10:]
76
+ return {
77
+ "total_events": len(records),
78
+ "status_counts": dict(statuses.most_common()),
79
+ "top_sources": top_sources.most_common(8),
80
+ "top_documents": top_documents.most_common(8),
81
+ "recent": recent,
82
+ }
83
+
84
+
85
+ def _append_jsonl(path: Path, record: dict[str, Any]) -> None:
86
+ try:
87
+ path.parent.mkdir(parents=True, exist_ok=True)
88
+ with path.open("a", encoding="utf-8") as file:
89
+ file.write(json.dumps(record, ensure_ascii=False) + "\n")
90
+ except Exception:
91
+ pass
92
+
93
+
94
+ def _read_jsonl(path: Path) -> list[dict[str, Any]]:
95
+ if not path.exists():
96
+ return []
97
+ records = []
98
+ try:
99
+ with path.open("r", encoding="utf-8") as file:
100
+ for line in file:
101
+ line = line.strip()
102
+ if not line:
103
+ continue
104
+ try:
105
+ records.append(json.loads(line))
106
+ except json.JSONDecodeError:
107
+ continue
108
+ except Exception:
109
+ return []
110
+ return records
answering.py ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from analytics import record_clarification_event, record_usage_event
6
+ from clause_retrieval import answer_from_clauses, evaluate_clause_query
7
+ from clarification import (
8
+ build_document_scope_clarification,
9
+ build_low_confidence_clarification,
10
+ build_pre_retrieval_clarification,
11
+ evidence_confidence_decision,
12
+ resolve_clarification_selection_details,
13
+ )
14
+ from guards import is_unsupported_metadata_question
15
+ from hermeneutic_intent import analyze_interpretive_intent
16
+ from conversation_intent import ConversationIntent, detect_conversation_intent
17
+ from engine import bootstrap_once
18
+ from case_analysis import analyze_normative_case, is_normative_case_question
19
+ from utils import clean_answer_text, normalize_for_search, topic_term_overlap
20
+
21
+ NO_CLEAR_PROVISION = "Bu konuda mevcut MCKF corpus içinde incelenen kaynaklarda açık bir hüküm bulunamadı."
22
+ CLARIFY_QUESTION = (
23
+ "Sorunuz tek başına hangi madde, kurum veya konu hakkında olduğunu yeterince belirtmiyor. "
24
+ "Lütfen yükseköğretim mevzuatı kapsamındaki kurumu, kanunu veya konuyu yazarak sorun; örneğin "
25
+ "`Dekan görevleri nelerdir?`, `Ek ders ücreti nasıl ödenir?` veya `Madde 16 neyi düzenler?`."
26
+ )
27
+
28
+ # "Bulunamadı" cevabına eklenen ilgili-madde önerileri için kalibrasyon
29
+ # sabitleri. Bunlar bilinçli olarak MIN_EVIDENCE_SCORE'dan (clause_retrieval.py,
30
+ # 8.0) çok daha düşük tutuldu: burada "kesin cevap" değil, "belki ilgili
31
+ # olabilir" seviyesinde bir işaret veriyoruz. SUGGESTION_MIN_TERM_OVERLAP,
32
+ # retrieval'ın alakasız maddeleri sırf skor tablosunda var diye önermesini
33
+ # engelleyen son bir dürüstlük kontrolüdür.
34
+ SUGGESTION_LIMIT = 3
35
+ SUGGESTION_MIN_SCORE = 3.0
36
+ # NOT: bu eşik topic_term_overlap()'a göre kalibre edildi (bkz. utils.py).
37
+ # O fonksiyon artık "ilgili", "neler", "var" gibi genel kelimeleri hariç
38
+ # tutuyor ve Türkçe ek varyasyonlarını (odenek/odenegin/odenekleri) prefix
39
+ # eşleşmesiyle yakalıyor. Eski ham set-kesişimi ("ilgili" gibi kelimelerin
40
+ # hemen her maddede geçmesi yüzünden) alakasız maddeleri yanlışlıkla
41
+ # öneriyordu — bkz. sohbet geçmişindeki "ödeneklerle ilgili neler var"
42
+ # örneği. 0.34 eşiği, soru tek/iki içerik kelimesi taşıdığında ikili
43
+ # (0.0/1.0) davranışla uyumlu, boş-terim durumunda (topic_term_overlap'ın
44
+ # döndürdüğü nötr 0.5) öneri göstermeye devam eder.
45
+ SUGGESTION_MIN_TERM_OVERLAP = 0.34
46
+
47
+
48
+ def no_clear_provision_answer() -> str:
49
+ return NO_CLEAR_PROVISION
50
+
51
+
52
+ def _related_article_suggestions(question: str, clause_eval: dict | None) -> list[dict]:
53
+ """Return up to SUGGESTION_LIMIT distinct articles loosely related to the
54
+ question, even when no article cleared the confidence bar for a direct
55
+ answer. Each suggestion is a bare (document, article) pointer — no claim,
56
+ no synthesized text — so this can never introduce fabricated content.
57
+ """
58
+ clause_eval = clause_eval or {}
59
+ evidence_spans = clause_eval.get("evidence_spans", []) or []
60
+ if not evidence_spans:
61
+ return []
62
+
63
+ suggestions: list[dict] = []
64
+ seen_articles: set[tuple[str, str]] = set()
65
+ for evidence in evidence_spans:
66
+ score = float(evidence.get("_score", 0.0) or 0.0)
67
+ if score < SUGGESTION_MIN_SCORE:
68
+ continue
69
+ document_id = str(evidence.get("document_id", "") or "")
70
+ article_id = str(evidence.get("article_id", "") or "")
71
+ if not article_id:
72
+ continue
73
+ key = (document_id, article_id)
74
+ if key in seen_articles:
75
+ continue
76
+
77
+ overlap = topic_term_overlap(question, [str(evidence.get("source_text", "") or "")])
78
+ if overlap < SUGGESTION_MIN_TERM_OVERLAP:
79
+ continue
80
+
81
+ seen_articles.add(key)
82
+ suggestions.append({
83
+ "document_title": evidence.get("document_title", "") or document_id,
84
+ "article_id": article_id,
85
+ "article_title": evidence.get("article_title", "") or "",
86
+ })
87
+ if len(suggestions) >= SUGGESTION_LIMIT:
88
+ break
89
+ return suggestions
90
+
91
+
92
+ def _format_suggestions_block(suggestions: list[dict]) -> str:
93
+ if not suggestions:
94
+ return ""
95
+ lines = []
96
+ for index, item in enumerate(suggestions, start=1):
97
+ title_part = f" ({item['article_title']})" if item.get("article_title") else ""
98
+ lines.append(f"{index}. {item['document_title']} — {item['article_id']}{title_part}")
99
+ return (
100
+ "Sorunuzla en alakalı olabilecek diğer maddeler (bu maddelerin sorunuzu "
101
+ "doğrudan yanıtladığı doğrulanmamıştır, yalnızca konu yakınlığına göre "
102
+ "listelenmiştir):\n" + "\n".join(lines)
103
+ )
104
+
105
+
106
+ def _no_clear_provision_response(question: str, clause_eval: dict | None) -> tuple[str, str]:
107
+ """Build the abstention answer, enriched with related-article suggestions
108
+ when available. Returns (answer_text, analytics_status) so callers can
109
+ still distinguish a bare abstention from a suggestion-assisted one.
110
+ """
111
+ suggestions = _related_article_suggestions(question, clause_eval)
112
+ block = _format_suggestions_block(suggestions)
113
+ if not block:
114
+ return no_clear_provision_answer(), "no_clear_provision"
115
+ return f"{no_clear_provision_answer()}\n\n{block}", "no_clear_provision_with_suggestions"
116
+
117
+
118
+ def answer_question(message: str, history=None, normativity_level=2) -> str:
119
+ question = (message or "").strip()
120
+ if not question:
121
+ return _answer_with_analytics(
122
+ "",
123
+ "Lütfen değerlendirmemi istediğiniz soruyu veya olayı yazın.",
124
+ "empty_message",
125
+ )
126
+ resolution = resolve_clarification_selection_details(question, history)
127
+ resolved_from_clarification = bool(resolution)
128
+ clarification_scopes = _clarification_source_scopes(resolution)
129
+ if resolution:
130
+ record_clarification_event(
131
+ "selection",
132
+ source_question=resolution.get("source_question", ""),
133
+ selected_key=resolution.get("selected_key", ""),
134
+ resolved_question=resolution.get("resolved_question", ""),
135
+ options=resolution.get("options", {}),
136
+ raw_message=question,
137
+ )
138
+ question = resolution.get("resolved_question", "") or question
139
+
140
+ direct_case_question = is_normative_case_question(question)
141
+ question = _contextualize_followup(question, history)
142
+
143
+ explicit_articles = _explicit_article_references(question)
144
+
145
+ conversation = detect_conversation_intent(question)
146
+ if conversation.intent != ConversationIntent.LEGAL_QUERY:
147
+ return _answer_with_analytics(question, conversation.answer, str(conversation.intent))
148
+
149
+ bootstrap_once()
150
+
151
+ if not resolved_from_clarification:
152
+ pre_retrieval_clarification = build_pre_retrieval_clarification(question)
153
+ if pre_retrieval_clarification:
154
+ return _answer_with_analytics(
155
+ question,
156
+ pre_retrieval_clarification,
157
+ "hermeneutic_intent_clarification",
158
+ )
159
+
160
+ case_result = analyze_normative_case(question) if direct_case_question else {}
161
+ if case_result:
162
+ return _answer_with_analytics(
163
+ question,
164
+ case_result.get("answer", ""),
165
+ "normative_case_analysis",
166
+ case_result,
167
+ )
168
+
169
+ if _needs_clarification(question):
170
+ return _answer_with_analytics(question, CLARIFY_QUESTION, "needs_clarification")
171
+
172
+ if is_unsupported_metadata_question(question):
173
+ return _answer_with_analytics(question, no_clear_provision_answer(), "unsupported_metadata")
174
+
175
+ allowed_articles = explicit_articles or [article_id for _document_id, article_id in clarification_scopes]
176
+ allowed_documents = [document_id for document_id, _article_id in clarification_scopes]
177
+ clause_eval = evaluate_clause_query(
178
+ question,
179
+ allowed_articles=allowed_articles or None,
180
+ allowed_documents=allowed_documents or None,
181
+ allowed_scopes=clarification_scopes or None,
182
+ )
183
+ proof_status = (clause_eval.get("proof_bundle", {}) or {}).get("status", "insufficient")
184
+ if proof_status != "supported":
185
+ clarification = "" if resolved_from_clarification else (
186
+ build_document_scope_clarification(question, clause_eval.get("evidence_spans", []))
187
+ or _validated_clarification(question, clause_eval)
188
+ )
189
+ if clarification:
190
+ return _answer_with_analytics(
191
+ question,
192
+ clarification,
193
+ "evidence_gate_clarification",
194
+ _suppress_public_sources(clause_eval),
195
+ )
196
+ answer_text, status = _no_clear_provision_response(question, clause_eval)
197
+ return _answer_with_analytics(
198
+ question,
199
+ answer_text,
200
+ status,
201
+ _suppress_public_sources(clause_eval),
202
+ )
203
+
204
+ calibrated_response, calibrated_status = _calibrated_evidence_gate(
205
+ question,
206
+ clause_eval,
207
+ skip=resolved_from_clarification or bool(explicit_articles),
208
+ )
209
+ if calibrated_response:
210
+ return _answer_with_analytics(
211
+ question,
212
+ calibrated_response,
213
+ calibrated_status,
214
+ _suppress_public_sources(clause_eval),
215
+ )
216
+
217
+ mckf_answer = answer_from_clauses(
218
+ question,
219
+ normativity_level,
220
+ allowed_articles=allowed_articles or None,
221
+ allowed_documents=allowed_documents or None,
222
+ allowed_scopes=clarification_scopes or None,
223
+ )
224
+ if mckf_answer:
225
+ if _answer_declares_no_clear_provision(mckf_answer):
226
+ clarification = "" if resolved_from_clarification else _validated_clarification(question, clause_eval)
227
+ if clarification:
228
+ return _answer_with_analytics(
229
+ question,
230
+ clarification,
231
+ "answer_declined_clarification",
232
+ _suppress_public_sources(clause_eval),
233
+ )
234
+ answer_text, status = _no_clear_provision_response(question, clause_eval)
235
+ return _answer_with_analytics(
236
+ question,
237
+ answer_text,
238
+ status,
239
+ _suppress_public_sources(clause_eval),
240
+ )
241
+ return _answer_with_analytics(question, mckf_answer, "source_grounded_answer", clause_eval)
242
+
243
+ clarification = "" if resolved_from_clarification else _validated_clarification(question, clause_eval)
244
+ if clarification:
245
+ return _answer_with_analytics(question, clarification, "low_confidence_clarification", _suppress_public_sources(clause_eval))
246
+
247
+ answer_text, status = _no_clear_provision_response(question, clause_eval)
248
+ return _answer_with_analytics(question, answer_text, status, _suppress_public_sources(clause_eval))
249
+
250
+
251
+ def _answer_with_analytics(
252
+ question: str,
253
+ answer: str,
254
+ status: str,
255
+ clause_eval: dict | None = None,
256
+ ) -> str:
257
+ record_usage_event(question, answer, status, clause_eval)
258
+ return answer
259
+
260
+
261
+ def _suppress_public_sources(clause_eval: dict | None) -> dict:
262
+ if not clause_eval:
263
+ return {}
264
+ cleaned = dict(clause_eval)
265
+ cleaned["candidate_source_ids"] = list(clause_eval.get("source_ids", []) or [])[:8]
266
+ cleaned["candidate_document_ids"] = list(clause_eval.get("document_ids", []) or [])[:8]
267
+ for key in ("evidence_ids", "clause_ids", "document_ids", "source_ids", "scores", "score_breakdowns"):
268
+ cleaned[key] = []
269
+ return cleaned
270
+
271
+
272
+ def _answer_declares_no_clear_provision(answer: str) -> bool:
273
+ normalized = normalize_for_search(answer)
274
+ no_clear_signals = (
275
+ "acik bir hukum bulunamadi",
276
+ "acik hukum bulunamadi",
277
+ "hukum bulunamadi",
278
+ "duzenleyen bir madde icermez",
279
+ "dogrudan cevap vermiyor",
280
+ "dogrudan cevap veren bir hukum yok",
281
+ )
282
+ return any(signal in normalized for signal in no_clear_signals)
283
+
284
+
285
+ def _clarification_source_scopes(resolution: dict | None) -> list[tuple[str, str]]:
286
+ metadata = (resolution or {}).get("option_metadata", {}) or {}
287
+ scopes = []
288
+ seen = set()
289
+ for item in metadata.get("source_scopes", []) or []:
290
+ scope = (
291
+ str(item.get("document_id", "") or ""),
292
+ str(item.get("article_id", "") or ""),
293
+ )
294
+ if not all(scope) or scope in seen:
295
+ continue
296
+ seen.add(scope)
297
+ scopes.append(scope)
298
+ return scopes
299
+
300
+
301
+ def _contextualize_followup(question: str, history) -> str:
302
+ q = normalize_for_search(question)
303
+ # A substantive new question must never inherit the previous turn merely
304
+ # because it contains a conversational phrase such as "bununla ilgili".
305
+ # Only genuinely elliptical follow-ups are resolved against chat history.
306
+ content_tokens = [
307
+ token for token in q.split()
308
+ if token not in {
309
+ "peki", "bu", "bununla", "konuyla", "ilgili", "hakkinda", "herhangi",
310
+ "bir", "bilgi", "var", "mi", "mu", "midir", "nedir", "aciklar",
311
+ "yok", "konu", "kanunda", "kanunlarda", "kac", "donem", "kisi",
312
+ "uye", "hak", "hakki", "haklari", "ne", "nasil", "neden",
313
+ }
314
+ and not token.isdigit()
315
+ ]
316
+ if content_tokens:
317
+ return question
318
+ followup_signals = (
319
+ q.startswith("peki"),
320
+ q.startswith("bu konu"),
321
+ q.startswith("bununla ilgili"),
322
+ "3 kanunda" in q,
323
+ "uc kanunda" in q,
324
+ "herhangi bir bilgi yok mu" in q,
325
+ )
326
+ if not any(followup_signals):
327
+ return question
328
+
329
+ previous = _latest_substantive_user_question(history)
330
+ if not previous:
331
+ return question
332
+ return f"{previous}\n\nTakip sorusu: {question}"
333
+
334
+
335
+ def _latest_substantive_user_question(history) -> str:
336
+ if not history:
337
+ return ""
338
+ for item in reversed(history):
339
+ if not isinstance(item, dict) or item.get("role") != "user":
340
+ continue
341
+ content = _history_content_text(item.get("content"))
342
+ normalized = normalize_for_search(content)
343
+ if len(normalized) < 55:
344
+ continue
345
+ if any(signal in normalized for signal in ("herhangi bir bilgi yok mu", "bu konuyla ilgili", "peki")):
346
+ continue
347
+ return content.strip()
348
+ return ""
349
+
350
+
351
+ def _history_content_text(content) -> str:
352
+ if isinstance(content, str):
353
+ return content
354
+ if isinstance(content, list):
355
+ parts = []
356
+ for item in content:
357
+ if isinstance(item, dict):
358
+ text = item.get("text") or item.get("content") or ""
359
+ if text:
360
+ parts.append(str(text))
361
+ return " ".join(parts)
362
+ return str(content or "")
363
+
364
+
365
+ def evaluate_query(question: str) -> dict:
366
+ question = (question or "").strip()
367
+ explicit_articles = _explicit_article_references(question)
368
+
369
+ conversation = detect_conversation_intent(question)
370
+ if conversation.intent != ConversationIntent.LEGAL_QUERY:
371
+ return {
372
+ "question": question,
373
+ "answer": conversation.answer,
374
+ "evidence_ids": [],
375
+ "clause_ids": [],
376
+ "document_ids": [],
377
+ "source_ids": [],
378
+ "scores": [],
379
+ "error": conversation.intent,
380
+ }
381
+
382
+ bootstrap_once()
383
+
384
+ pre_retrieval_clarification = build_pre_retrieval_clarification(question)
385
+ if pre_retrieval_clarification:
386
+ return {
387
+ "question": question,
388
+ "answer": pre_retrieval_clarification,
389
+ "evidence_ids": [],
390
+ "clause_ids": [],
391
+ "document_ids": [],
392
+ "source_ids": [],
393
+ "scores": [],
394
+ "score_breakdowns": [],
395
+ "error": "hermeneutic_intent_clarification",
396
+ }
397
+
398
+ case_result = analyze_normative_case(question)
399
+ if case_result:
400
+ return {
401
+ "question": question,
402
+ **case_result,
403
+ "answer": clean_answer_text(case_result.get("answer", ""), question),
404
+ "error": "",
405
+ }
406
+
407
+ if _needs_clarification(question):
408
+ return {
409
+ "question": question,
410
+ "answer": CLARIFY_QUESTION,
411
+ "evidence_ids": [],
412
+ "clause_ids": [],
413
+ "document_ids": [],
414
+ "source_ids": [],
415
+ "scores": [],
416
+ "error": "needs_clarification",
417
+ }
418
+
419
+ if is_unsupported_metadata_question(question):
420
+ return {
421
+ "question": question,
422
+ "answer": no_clear_provision_answer(),
423
+ "evidence_ids": [],
424
+ "clause_ids": [],
425
+ "document_ids": [],
426
+ "source_ids": [],
427
+ "scores": [],
428
+ "error": "unsupported_metadata",
429
+ }
430
+
431
+ allowed_articles = explicit_articles
432
+ clause_eval = evaluate_clause_query(question, allowed_articles=allowed_articles or None)
433
+ proof_status = (clause_eval.get("proof_bundle", {}) or {}).get("status", "insufficient")
434
+ if proof_status != "supported":
435
+ clarification = (
436
+ build_document_scope_clarification(question, clause_eval.get("evidence_spans", []))
437
+ or _validated_clarification(question, clause_eval)
438
+ )
439
+ return {
440
+ "question": question,
441
+ "answer": clean_answer_text(clarification or no_clear_provision_answer(), question),
442
+ "evidence_ids": [],
443
+ "clause_ids": [],
444
+ "document_ids": [],
445
+ "source_ids": [],
446
+ "scores": [],
447
+ "score_breakdowns": [],
448
+ "source_route": clause_eval.get("source_route", {}),
449
+ "candidate_source_ids": clause_eval.get("source_ids", [])[:8],
450
+ "error": "needs_clarification" if clarification else "no_confident_evidence",
451
+ }
452
+
453
+ calibrated_response, calibrated_status = _calibrated_evidence_gate(
454
+ question,
455
+ clause_eval,
456
+ skip=bool(explicit_articles),
457
+ )
458
+ if calibrated_response:
459
+ is_clarification = "MCKF_CLARIFY_OPTIONS" in calibrated_response
460
+ return {
461
+ "question": question,
462
+ "answer": clean_answer_text(calibrated_response, question),
463
+ "evidence_ids": [],
464
+ "clause_ids": [],
465
+ "document_ids": [],
466
+ "source_ids": [],
467
+ "scores": [],
468
+ "score_breakdowns": [],
469
+ "source_route": clause_eval.get("source_route", {}),
470
+ "candidate_source_ids": clause_eval.get("source_ids", [])[:8],
471
+ "confidence_decision": calibrated_status,
472
+ "error": "needs_clarification" if is_clarification else "no_confident_evidence",
473
+ }
474
+
475
+ answer = answer_from_clauses(
476
+ question,
477
+ 2,
478
+ allowed_articles=allowed_articles or None,
479
+ evidence_spans=clause_eval.get("evidence_spans", []),
480
+ )
481
+ if answer and _answer_declares_no_clear_provision(answer):
482
+ clarification = _validated_clarification(question, clause_eval)
483
+ return {
484
+ "question": question,
485
+ "answer": clean_answer_text(clarification or no_clear_provision_answer(), question),
486
+ "evidence_ids": [],
487
+ "clause_ids": [],
488
+ "document_ids": [],
489
+ "source_ids": [],
490
+ "scores": [],
491
+ "score_breakdowns": [],
492
+ "source_route": clause_eval.get("source_route", {}),
493
+ "candidate_source_ids": clause_eval.get("source_ids", [])[:8],
494
+ "error": "needs_clarification" if clarification else "no_confident_evidence",
495
+ }
496
+
497
+ if not answer:
498
+ answer = _validated_clarification(question, clause_eval)
499
+ if answer:
500
+ clause_eval = _suppress_public_sources(clause_eval)
501
+ return {
502
+ "question": question,
503
+ "answer": clean_answer_text(answer or no_clear_provision_answer(), question),
504
+ "evidence_ids": clause_eval.get("evidence_ids", []),
505
+ "clause_ids": clause_eval.get("clause_ids", []),
506
+ "document_ids": clause_eval.get("document_ids", []),
507
+ "source_ids": clause_eval.get("source_ids", []),
508
+ "scores": clause_eval.get("scores", []),
509
+ "score_breakdowns": clause_eval.get("score_breakdowns", []),
510
+ "source_route": clause_eval.get("source_route", {}),
511
+ "proof_bundle": clause_eval.get("proof_bundle", {}),
512
+ "error": "" if answer and "MCKF_CLARIFY_OPTIONS" not in answer else ("needs_clarification" if answer else "no_confident_evidence"),
513
+ }
514
+
515
+
516
+ def _validated_clarification(question: str, clause_eval: dict | None) -> str:
517
+ clause_eval = clause_eval or {}
518
+
519
+ def candidate_is_answerable(resolved_question: str, article_id: str, document_id: str = "") -> bool:
520
+ validation = evaluate_clause_query(
521
+ resolved_question,
522
+ allowed_articles=[article_id],
523
+ allowed_documents=[document_id] if document_id else None,
524
+ )
525
+ spans = validation.get("evidence_spans", []) or []
526
+ if not spans:
527
+ return False
528
+ top = spans[0]
529
+ if str(top.get("article_id", "") or "") != article_id:
530
+ return False
531
+ if document_id and str(top.get("document_id", "") or "") != document_id:
532
+ return False
533
+ proof = validation.get("proof_bundle", {}) or {}
534
+ return proof.get("status") == "supported"
535
+
536
+ return build_low_confidence_clarification(
537
+ question,
538
+ clause_eval.get("evidence_spans", []),
539
+ candidate_validator=candidate_is_answerable,
540
+ ) or ""
541
+
542
+
543
+ def _calibrated_evidence_gate(
544
+ question: str,
545
+ clause_eval: dict | None,
546
+ skip: bool = False,
547
+ ) -> tuple[str, str]:
548
+ if skip:
549
+ return "", ""
550
+ decision = evidence_confidence_decision(question, clause_eval)
551
+ if decision.get("action") == "answer":
552
+ return "", ""
553
+ if decision.get("action") == "clarify":
554
+ clarification = _validated_clarification(question, clause_eval)
555
+ if clarification:
556
+ return clarification, f"calibrated_clarification:{decision.get('reason', '')}"
557
+ # No validated option is safer than exposing a plausible but unrelated
558
+ # article. Public source identifiers are suppressed by the caller.
559
+ return no_clear_provision_answer(), f"calibrated_abstention:{decision.get('reason', '')}"
560
+
561
+
562
+ def _explicit_article_references(question: str) -> list[str]:
563
+ q = question or ""
564
+ refs: list[str] = []
565
+ for match in re.finditer(r"\bGe[cç]ici\s+Madde\s+(\d+(?:/[A-Za-zÇĞİÖŞÜçğıöşü])?)\b", q, flags=re.IGNORECASE):
566
+ ref = f"Geçici Madde {match.group(1).upper()}"
567
+ if ref not in refs:
568
+ refs.append(ref)
569
+ for match in re.finditer(r"\bEk\s+Madde\s+(\d+)\b", q, flags=re.IGNORECASE):
570
+ refs.append(f"Ek Madde {match.group(1)}")
571
+ for match in re.finditer(r"\bMadde\s+(\d+(?:/[A-Za-zÇĞİÖŞÜçğıöşü])?)\b", q, flags=re.IGNORECASE):
572
+ prefix = q[max(0, match.start() - 8):match.start()].strip().lower()
573
+ if prefix.endswith("ek") or prefix.endswith("geçici") or prefix.endswith("gecici"):
574
+ continue
575
+ ref = f"Madde {match.group(1).upper()}"
576
+ if ref not in refs:
577
+ refs.append(ref)
578
+ return refs
579
+
580
+
581
+ def _needs_clarification(question: str) -> bool:
582
+ q = (question or "").strip().lower()
583
+ if _explicit_article_references(question):
584
+ return False
585
+ interpretation = analyze_interpretive_intent(question)
586
+ if interpretation and not interpretation.get("requires_clarification"):
587
+ return False
588
+ normalized = re.sub(r"\s+", " ", q)
589
+ normalized_search = normalize_for_search(question)
590
+ vague_topic_phrases = (
591
+ "devam kosulu",
592
+ "devam sarti",
593
+ "kurulus nasil",
594
+ "nasil kurulur",
595
+ )
596
+ named_scope_terms = (
597
+ "universite", "enstitu", "fakulte", "yuksekokul", "program", "ders",
598
+ "ogrenci", "akademik personel", "ogretim elemani", "2547", "2914", "2809",
599
+ )
600
+ if any(phrase in normalized_search for phrase in vague_topic_phrases):
601
+ if not any(term in normalized_search for term in named_scope_terms):
602
+ return True
603
+ vague_followups = {
604
+ "peki", "peki ya", "bunlar", "bunu açıkla", "devam", "detaylandır",
605
+ "görevleri", "yetkileri", "şartları", "koşulları", "nasıl", "nedir",
606
+ "kim atar", "kaç kişi", "kaç üye",
607
+ }
608
+ if normalized in vague_followups:
609
+ return True
610
+ words = [word for word in re.findall(r"\w+", normalized, flags=re.UNICODE) if len(word) >= 3]
611
+ legal_anchors = (
612
+ "2547", "2914", "2809", "yök", "yuksek", "yüksek", "universite", "üniversite", "dekan", "rektor", "rektör",
613
+ "senato", "fakulte", "fakülte", "ogrenci", "öğrenci", "ogretim", "öğretim",
614
+ "diploma", "lisans", "disiplin", "vakıf", "vakif", "kurul", "madde",
615
+ "ek ders", "ödenek", "odenek", "derece", "kademe", "teşkilat", "teskilat",
616
+ )
617
+ has_anchor = any(anchor in normalized for anchor in legal_anchors)
618
+ return len(words) <= 2 and not has_anchor
app.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from ui_gradio import CUSTOM_CSS, create_demo
2
+
3
+
4
+ demo = create_demo()
5
+
6
+
7
+ if __name__ == "__main__":
8
+ demo.launch(css=CUSTOM_CSS)
case_analysis.py ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Any
5
+
6
+ from clause_retrieval import evidence_spans_matching
7
+ from sources import format_clause_sources
8
+ from utils import normalize_for_search
9
+
10
+
11
+ CASE_RULES = {
12
+ "student_status_and_remedy": {
13
+ "triggers": (
14
+ "kaydinin sil", "kaydi sil", "ilisigi kes", "kayit sil", "itiraz",
15
+ "uyaril", "teblig", "bildiril", "mazeret",
16
+ ),
17
+ "evidence_selectors": (
18
+ {
19
+ "document_id": "TR-KANUN-2547",
20
+ "article_id": "Madde 44",
21
+ "label": "b",
22
+ "contains_all": ("azami süreler içinde", "kayıt yenilenmemesi", "ilişikleri kesilmez"),
23
+ },
24
+ {
25
+ "document_id": "TR-KANUN-2547",
26
+ "article_id": "Madde 44",
27
+ "label": "b",
28
+ "contains_all": ("dört yıl üst üste", "yetkili kurullarının kararı", "ilişikleri kesilebilir"),
29
+ },
30
+ {
31
+ "document_id": "TR-KANUN-2547",
32
+ "article_id": "Madde 54",
33
+ "label": "ç",
34
+ "contains_all": ("disiplin cezalarına karşı", "on beş gün", "itiraz edilebilir"),
35
+ },
36
+ {
37
+ "document_id": "TR-KANUN-2547",
38
+ "article_id": "Madde 54",
39
+ "label": "e",
40
+ "contains_all": ("itiraz hakkı kullanılmadan", "idari yargı"),
41
+ },
42
+ {
43
+ "document_id": "TR-KANUN-2547",
44
+ "article_id": "Madde 54",
45
+ "label": "11",
46
+ "contains_all": ("öğrenciye yapılacak tebligatta", "Tebligat Kanunu"),
47
+ },
48
+ {
49
+ "document_id": "TR-KANUN-2547",
50
+ "article_id": "Madde 14",
51
+ "label": "6",
52
+ "contains_all": ("enstitü", "kararlarına yapılacak itirazları", "karara bağlamak"),
53
+ },
54
+ {
55
+ "document_id": "TR-KANUN-2547",
56
+ "article_id": "Madde 15",
57
+ "label": "4",
58
+ "contains_all": ("enstitü", "yönetim kurullarının kararlarına", "kesin karara bağlamak"),
59
+ },
60
+ ),
61
+ },
62
+ "student_excuse_period": {
63
+ "triggers": ("mazeret", "mazeret hakki", "mazeret izni"),
64
+ "evidence_selectors": (
65
+ {
66
+ "document_id": "TR-KANUN-2547",
67
+ "article_id": "Madde 46",
68
+ "label": "g",
69
+ "contains_all": ("mazeretleri", "yönetim kurulunca kabul edilmeyenler", "kayıt yaptıramaz"),
70
+ },
71
+ ),
72
+ },
73
+ "graduation_and_registration": {
74
+ "triggers": (
75
+ "mezun", "diploma", "ders sec", "ders kay", "kayit yaptir",
76
+ "tez dersi", "uzmanlik dersi", "kredi", "yukumluluk",
77
+ ),
78
+ "evidence_selectors": (
79
+ {
80
+ "document_id": "TR-KANUN-2547",
81
+ "article_id": "Madde 44",
82
+ "label": "a",
83
+ "contains_all": ("ders kredileri", "diğer yükümlülükleri", "tamamlamaları halinde"),
84
+ },
85
+ {
86
+ "document_id": "TR-KANUN-2547",
87
+ "article_id": "Madde 44",
88
+ "label": "b",
89
+ "contains_all": ("kayıt", "tez", "teorik ders"),
90
+ },
91
+ ),
92
+ },
93
+ "academic_guidance": {
94
+ "triggers": ("danisman", "ogretim uyes", "rehber", "tez"),
95
+ "evidence_selectors": (
96
+ {
97
+ "document_id": "TR-KANUN-2547",
98
+ "article_id": "Madde 22",
99
+ "label": "c",
100
+ "contains_all": ("yardım etmek", "yol göstermek", "rehberlik etmek"),
101
+ },
102
+ ),
103
+ },
104
+ "institute_governance": {
105
+ "triggers": ("enstitu", "mudurluk", "yonetim kurulu", "ogrenci isleri"),
106
+ "evidence_selectors": (
107
+ {
108
+ "document_id": "TR-KANUN-2547",
109
+ "article_id": "Madde 19",
110
+ "label": "b",
111
+ "contains_all": ("enstitü müdürü", "görevleri", "enstitü bakımından"),
112
+ },
113
+ {
114
+ "document_id": "TR-KANUN-2547",
115
+ "article_id": "Madde 19",
116
+ "label": "e",
117
+ "contains_all": ("enstitü kurulu", "yönetim kurulu", "görevleri"),
118
+ },
119
+ ),
120
+ },
121
+ }
122
+
123
+
124
+ def is_normative_case_question(question: str) -> bool:
125
+ q = normalize_for_search(question)
126
+ if len(q) < 150:
127
+ return False
128
+ assessment_signal = any(
129
+ signal in q
130
+ for signal in (
131
+ "kim sorumlu", "kimler sorumlu", "sorumluluklarini yapmamis",
132
+ "sorumlulugunu yerine getirmemis", "burada kim", "bu durumda kim",
133
+ "hukuken degerlendir", "mevzuata gore degerlendir",
134
+ "hangi kanun maddelerinden yararlanabilir", "itiraz etmek istiyor",
135
+ "kaydinin silinecegi", "ilisiginin kesilecegi",
136
+ )
137
+ )
138
+ narrative_signal_count = sum(
139
+ signal in q
140
+ for signal in (
141
+ "ancak", "daha sonra", "son asama", "son donem", "boyunca",
142
+ "halde", "diyor", "ise", "cunku",
143
+ )
144
+ )
145
+ actor_count = sum(
146
+ actor in q
147
+ for actor in ("ogrenci", "danisman", "enstitu", "universite", "yonetim", "senato")
148
+ )
149
+ institutional_action = any(
150
+ signal in q for signal in ("bildiriliyor", "bildirildi", "kaydinin sil", "ilisigi kes", "itiraz")
151
+ )
152
+ return assessment_signal and narrative_signal_count >= 1 and (
153
+ actor_count >= 2 or (actor_count >= 1 and institutional_action)
154
+ )
155
+
156
+
157
+ def analyze_normative_case(question: str) -> dict[str, Any]:
158
+ if not is_normative_case_question(question):
159
+ return {}
160
+
161
+ normalized = normalize_for_search(question)
162
+ matched_rules = [
163
+ rule_name
164
+ for rule_name, rule in CASE_RULES.items()
165
+ if any(trigger in normalized for trigger in rule["triggers"])
166
+ ]
167
+ evidence_selectors: list[dict[str, Any]] = []
168
+ for rule_name in matched_rules:
169
+ for selector in CASE_RULES[rule_name]["evidence_selectors"]:
170
+ if selector not in evidence_selectors:
171
+ evidence_selectors.append(selector)
172
+
173
+ evidence_spans = evidence_spans_matching(evidence_selectors)
174
+ if not evidence_spans:
175
+ return {}
176
+
177
+ explicit_actors = _case_actors(normalized)
178
+ inferred_actors = _inferred_case_actors(normalized, explicit_actors)
179
+ actors = explicit_actors + [actor for actor in inferred_actors if actor not in explicit_actors]
180
+ facts = _case_facts(question)
181
+ answer = _render_case_assessment(question, matched_rules, actors, facts, evidence_spans)
182
+ return {
183
+ "answer": answer,
184
+ "case_frame": {
185
+ "actors": actors,
186
+ "explicit_actors": explicit_actors,
187
+ "inferred_actors": inferred_actors,
188
+ "facts": facts,
189
+ "matched_rules": matched_rules,
190
+ "responsibility_tests": _responsibility_tests(actors),
191
+ "missing_evidence": _missing_evidence(),
192
+ "assessment_mode": "source_locked_multi_hop",
193
+ },
194
+ "evidence_spans": evidence_spans,
195
+ "evidence_ids": [item.get("evidence_id", "") for item in evidence_spans],
196
+ "clause_ids": _unique(item.get("parent_clause_id", "") for item in evidence_spans),
197
+ "document_ids": _unique(item.get("document_id", "") for item in evidence_spans),
198
+ "source_ids": _unique(item.get("article_id", "") for item in evidence_spans),
199
+ "scores": [20.0 for _ in evidence_spans],
200
+ "score_breakdowns": [{"case_rule_match": 1.0} for _ in evidence_spans],
201
+ "source_route": {"route": "normative_case_analysis"},
202
+ }
203
+
204
+
205
+ def _render_case_assessment(
206
+ question: str,
207
+ matched_rules: list[str],
208
+ actors: list[str],
209
+ facts: list[str],
210
+ evidence_spans: list[dict[str, Any]],
211
+ ) -> str:
212
+ if "student_status_and_remedy" in matched_rules or "student_excuse_period" in matched_rules:
213
+ return _render_student_status_remedy_assessment(question, facts, evidence_spans)
214
+
215
+ has_graduation = "graduation_and_registration" in matched_rules
216
+ has_guidance = "academic_guidance" in matched_rules
217
+ has_institute = "institute_governance" in matched_rules
218
+
219
+ sections = [
220
+ "**Normatif vaka değerlendirmesi**",
221
+ "",
222
+ "Bu olayda tek bir kişiyi doğrudan sorumlu ilan etmek mevcut corpusla mümkün değil. "
223
+ "2547 sayılı Kanun sorumluluk çerçevesini kuruyor; fakat tez dersi ve uzmanlık alan dersi "
224
+ "kaydını kimin kontrol edeceği üniversitenin lisansüstü yönetmeliği, senato kararları ve "
225
+ "enstitü iş akışında belirlenir.",
226
+ ]
227
+
228
+ if facts:
229
+ sections.extend(["", "**Sistemin ayırdığı olay unsurları**"])
230
+ sections.extend(f"- {fact}" for fact in facts[:5])
231
+
232
+ sections.extend(["", "**Kanundan kesin olarak çıkarılabilenler**"])
233
+ if has_graduation:
234
+ sections.extend([
235
+ "1. **Mezuniyet koşulu:** Madde 44/a uyarınca öğrenci, belirlenen ders kredileri ile "
236
+ "diğer yükümlülükleri başarıyla tamamlamadan diploma hakkı kazanmaz.",
237
+ "2. **Yerel düzenleme zorunluluğu:** Madde 44/b; kayıt, devam, tez ve teorik ders "
238
+ "içerikleri ile diploma alınmasına ilişkin ayrıntıları, YÖK'ün temel ilkelerine uygun "
239
+ "olarak üniversite senatosunun düzenlemesine bırakır. Bu nedenle yalnızca 2547'ye "
240
+ "bakarak 'tez dersi seçilmemişse her durumda mezuniyet imkansızdır' denemez; ilgili "
241
+ "üniversite düzenlemesi de görülmelidir.",
242
+ ])
243
+ if has_guidance:
244
+ sections.append(
245
+ "3. **Akademik rehberlik:** Madde 22/c öğretim üyesine öğrenciye gerekli konularda "
246
+ "yardım etme, yol gösterme ve rehberlik etme görevi verir. Bu genel görev danışmanın "
247
+ "süreçte tamamen ilgisiz sayılamayacağını gösterir; ancak ders kaydını kontrol veya "
248
+ "onay yükümlülüğünü tek başına kanıtlamaz."
249
+ )
250
+ if has_institute:
251
+ sections.append(
252
+ "4. **Enstitü yönetimi:** Madde 19, enstitü müdürü ile enstitü kurulu ve yönetim "
253
+ "kurulunun enstitü bakımından yönetsel görev taşıdığını gösterir. Buna karşılık dönemlik "
254
+ "ders kaydını hangi personelin doğrulayacağını bu madde açıkça söylemez."
255
+ )
256
+
257
+ sections.extend([
258
+ "",
259
+ "**Aktör bazlı sorumluluk testi**",
260
+ "- **Öğrenci - koşullu sorumluluk:** Üniversite düzenlemesi tez ve uzmanlık alan dersini "
261
+ "seçmeyi açıkça öğrenciye yüklüyorsa, öğrencinin kayıt yükümlülüğünü yerine getirmediği "
262
+ "söylenebilir. Tezin fiilen tamamlanmış olması, kayıt yükümlülüğünü kendiliğinden ortadan kaldırmaz.",
263
+ "- **Danışman - olası rehberlik/süreç sorumluluğu:** Danışmanın ders seçimini onaylama veya "
264
+ "izleme görevi yerel düzenlemede ya da bilgi sistemi iş akışında varsa sorumluluğu doğabilir. "
265
+ "Mevcut corpus yalnızca genel rehberlik görevini kanıtlıyor; somut ihlal için yeterli değil.",
266
+ "- **Enstitü/anabilim dalı/idare - olası kontrol sorumluluğu:** Öğrencinin bir dönem tez "
267
+ "çalışmasını yürütmesine izin verildiği, danışmanlık ve ilerleme işlemlerinin kayda alındığı "
268
+ "halde ders eksikliğinin yalnız mezuniyet aşamasında fark edilmesi bir süreç kontrol boşluğuna "
269
+ "işaret eder. Bunun hukuki veya idari sorumluluğa dönüşmesi, yerel görev ve onay kurallarına bağlıdır.",
270
+ "",
271
+ "**Sonuç**",
272
+ "Mevcut kanun corpusuna göre en güvenilir sonuç, öğrencinin olası kayıt eksikliği yanında "
273
+ "danışman ve enstitü süreçlerinde de olası gözetim/kontrol eksikliği bulunduğudur. Ancak "
274
+ "sorumluluğun kesin dağılımı yapılamaz. Kesin karar için üniversitenin Lisansüstü Eğitim ve "
275
+ "Öğretim Yönetmeliği, enstitü usul ve esasları, senato kararları, danışman onay akışı ve "
276
+ "öğrenci bilgi sistemi kayıtları corpusa eklenmelidir.",
277
+ ])
278
+
279
+ answer = "\n".join(sections)
280
+ sources = format_clause_sources(evidence_spans, question=question, answer=answer)
281
+ return f"{answer}\n\n---\n\n**Kaynaklar**\n{sources}"
282
+
283
+
284
+ def _render_student_status_remedy_assessment(
285
+ question: str,
286
+ facts: list[str],
287
+ evidence_spans: list[dict[str, Any]],
288
+ ) -> str:
289
+ q = normalize_for_search(question)
290
+ asks_excuse_period = "mazeret" in q and any(term in q for term in ("kac", "donem", "sure"))
291
+
292
+ if asks_excuse_period:
293
+ sections = [
294
+ "İncelenen 2547, 2914 ve 2809 sayılı kanunlarda öğrencilere tanınmış "
295
+ "genel ve sabit bir ‘kaç dönem mazeret hakkı’ bulunmuyor.",
296
+ "",
297
+ "2547 sayılı Kanunun 46/g maddesi yalnızca katkı payı veya öğrenim ücretini süresinde "
298
+ "ödemeyen öğrencinin mazeretinin ilgili yükseköğretim kurumu yönetim kurulunca kabul "
299
+ "edilebileceğini düzenler; mazeretin kaç dönem süreceğini belirlemez. Akademik kayıt, "
300
+ "izin ve mazeret dönemlerinin sayısı üniversitenin eğitim-öğretim veya lisansüstü "
301
+ "yönetmeliğinden kontrol edilmelidir.",
302
+ ]
303
+ else:
304
+ sections = [
305
+ "**Normatif vaka değerlendirmesi**",
306
+ "",
307
+ "Kayıt silme işleminin hukuki sebebi belirtilmeden tek bir itiraz maddesi seçmek doğru "
308
+ "olmaz. Ancak mevcut corpus öğrencinin dayanabileceği hükümleri üç ihtimale ayırıyor:",
309
+ "",
310
+ "1. **Kayıt yenilememe veya ücret ödememe gerekçesi:** Madde 44/c'ye göre azami süre "
311
+ "içinde yalnızca katkı payının/öğrenim ücretinin ödenmemesi veya kayıt yenilenmemesi "
312
+ "öğrencinin ilişiğinin kesilmesine yetmez. Dört yıl üst üste bu durumun yaşanması halinde "
313
+ "ise yetkili üniversite kurulu kararı ve YÖK onayıyla ilişik kesilebilir.",
314
+ "2. **Disiplin cezası gerekçesi:** İşlem bir öğrenci disiplin cezasıysa Madde 54 uyarınca "
315
+ "on beş gün içinde üniversite yönetim kuruluna itiraz edilebilir; ayrıca bu itiraz "
316
+ "kullanılmadan idari yargıya da başvurulabilir. Öğrenciye yapılacak tebligatta kural olarak "
317
+ "7201 sayılı Tebligat Kanunu uygulanır.",
318
+ "3. **Enstitü veya fakülte kurulu kararı:** Madde 14/6 ve Madde 15/4, kurulun türüne göre "
319
+ "senato veya üniversite y��netim kurulunun itirazı inceleme yetkisini düzenler.",
320
+ "",
321
+ "**Uyarılmama iddiası**",
322
+ "2547 sayılı Kanun akademik nedenle kayıt silmeden önce her durumda ayrıca bir uyarı "
323
+ "yapılmasını açık ve genel bir şart olarak koymuyor. Bu nedenle ‘uyarılmadım’ iddiası "
324
+ "tek başına işlemi geçersiz kılmaz; kararın yazılı gerekçesi, dayandığı madde, tebliğ "
325
+ "tarihi ve üniversitenin öğrenci/lisansüstü yönetmeliği birlikte incelenmelidir.",
326
+ "",
327
+ "**Pratik sonuç:** Öğrenci önce işlemin disiplin cezası mı, azami süre/kayıt yenilememe "
328
+ "işlemi mi, yoksa enstitü yönetim kurulu kararı mı olduğunu yazılı olarak istemelidir. "
329
+ "Doğru itiraz mercii ve süre bu ayrımdan sonra belirlenebilir.",
330
+ ]
331
+
332
+ answer = "\n".join(sections)
333
+ source_evidence = evidence_spans
334
+ if asks_excuse_period:
335
+ source_evidence = [item for item in evidence_spans if item.get("article_id") == "Madde 46"]
336
+ sources = format_clause_sources(source_evidence, question=question, answer=answer)
337
+ return f"{answer}\n\n---\n\n**Kaynaklar**\n{sources}"
338
+
339
+
340
+ def _case_actors(normalized_question: str) -> list[str]:
341
+ actor_terms = (
342
+ ("ogrenci", "öğrenci"),
343
+ ("danisman", "danışman"),
344
+ ("enstitu", "enstitü"),
345
+ ("anabilim dali", "anabilim dalı"),
346
+ ("ogrenci isleri", "öğrenci işleri"),
347
+ ("senato", "senato"),
348
+ ("universite", "üniversite"),
349
+ )
350
+ return [label for token, label in actor_terms if token in normalized_question]
351
+
352
+
353
+ def _inferred_case_actors(normalized_question: str, explicit_actors: list[str]) -> list[str]:
354
+ inferred = []
355
+ if "tez" in normalized_question and "öğrenci" in explicit_actors and "danışman" not in explicit_actors:
356
+ inferred.append("danışman")
357
+ return inferred
358
+
359
+
360
+ def _case_facts(question: str) -> list[str]:
361
+ cleaned = re.sub(r"\s+", " ", question).strip()
362
+ parts = re.split(r"(?<=[.!?])\s+|\s+(?:ancak|fakat|ama)\s+", cleaned, flags=re.IGNORECASE)
363
+ facts = []
364
+ for part in parts:
365
+ fact = part.strip(" .?!\"")
366
+ if len(fact) < 20 or "kimler soruml" in normalize_for_search(fact):
367
+ continue
368
+ facts.append(fact)
369
+ return facts
370
+
371
+
372
+ def _responsibility_tests(actors: list[str]) -> list[dict[str, str]]:
373
+ tests = {
374
+ "öğrenci": ("conditional", "Yerel düzenlemedeki ders kayıt yükümlülüğü doğrulanmalı."),
375
+ "danışman": ("insufficient", "Ders seçimini onaylama veya izleme görevi doğrulanmalı."),
376
+ "enstitü": ("insufficient", "Kayıt ve mezuniyet kontrol iş akışındaki görev doğrulanmalı."),
377
+ }
378
+ return [
379
+ {"actor": actor, "status": tests[actor][0], "test": tests[actor][1]}
380
+ for actor in actors
381
+ if actor in tests
382
+ ]
383
+
384
+
385
+ def _missing_evidence() -> list[str]:
386
+ return [
387
+ "Üniversite Lisansüstü Eğitim ve Öğretim Yönetmeliği",
388
+ "Enstitü usul ve esasları",
389
+ "Senato kararları",
390
+ "Danışman ders onay iş akışı",
391
+ "Öğrenci bilgi sistemi işlem kayıtları",
392
+ ]
393
+
394
+
395
+ def _unique(values) -> list[str]:
396
+ result = []
397
+ for value in values:
398
+ if value and value not in result:
399
+ result.append(value)
400
+ return result
clarification.py ADDED
@@ -0,0 +1,637 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from typing import Any, Callable
6
+
7
+ from hermeneutic_intent import build_grounded_interpretation_options
8
+ from intent import detect_actors
9
+ from normative_metadata import semantic_address_score
10
+ from query_understanding import expand_query_terms
11
+ from utils import normalize_for_search, topic_term_overlap
12
+
13
+
14
+ MARKER = "MCKF_CLARIFY_OPTIONS:"
15
+ _CLICK_TARGETS: dict[str, dict[str, str]] = {}
16
+
17
+ # The proof bundle's 0.55 threshold means "some grounded support exists". It
18
+ # is deliberately not strong enough for an autonomous user-facing answer.
19
+ # This gate is calibrated against the three-document golden set and operates
20
+ # on article-level competition, not adjacent evidence fragments.
21
+ ANSWER_SUPPORT_THRESHOLD = 0.68
22
+ COMPETING_ARTICLE_MARGIN = 4.0
23
+ COMPETING_ARTICLE_RATIO = 0.82
24
+ MIN_OPTION_SCORE = 8.9
25
+ MIN_OPTION_SCORE_RATIO = 0.62
26
+ MIN_SEMANTIC_RELEVANCE = 0.66
27
+
28
+
29
+ def resolve_clarification_selection(message: str, history: Any) -> str:
30
+ resolved = resolve_clarification_selection_details(message, history)
31
+ return resolved.get("resolved_question", "") if resolved else ""
32
+
33
+
34
+ def resolve_clarification_selection_details(message: str, history: Any) -> dict[str, Any]:
35
+ payload = _latest_payload(history)
36
+ selection = _selection_key(message, payload)
37
+ options = payload.get("options", {}) if payload else {}
38
+ resolved_question = options.get(selection, "") if selection else ""
39
+ if not resolved_question:
40
+ click_target = _CLICK_TARGETS.get(normalize_for_search(message), {})
41
+ if click_target:
42
+ return {
43
+ "selected_key": click_target.get("selected_key", ""),
44
+ "resolved_question": click_target.get("resolved_question", ""),
45
+ "source_question": click_target.get("source_question", ""),
46
+ "clarification_type": click_target.get("clarification_type", ""),
47
+ "option_metadata": click_target.get("option_metadata", {}),
48
+ "options": {},
49
+ }
50
+ if not resolved_question:
51
+ return {}
52
+ return {
53
+ "selected_key": selection,
54
+ "resolved_question": resolved_question,
55
+ "source_question": payload.get("source_question", "") if payload else "",
56
+ "clarification_type": payload.get("clarification_type", "") if payload else "",
57
+ "option_metadata": (payload.get("option_metadata", {}) or {}).get(selection, {}) if payload else {},
58
+ "options": options,
59
+ }
60
+
61
+
62
+ def build_pre_retrieval_clarification(question: str) -> str:
63
+ """Clarify materially different legal mechanisms before retrieval.
64
+
65
+ Options are emitted only after hermeneutic_intent verifies that their
66
+ grounding phrases exist in the active corpus.
67
+ """
68
+ interpretation = build_grounded_interpretation_options(question)
69
+ choices = interpretation.get("options", []) if interpretation else []
70
+ if not choices:
71
+ return ""
72
+ options = {str(index): item.get("resolved_question", "") for index, item in enumerate(choices, start=1)}
73
+ visible = {str(index): item.get("label", "") for index, item in enumerate(choices, start=1)}
74
+ metadata = {str(index): item.get("metadata", {}) for index, item in enumerate(choices, start=1)}
75
+ return _render_options(
76
+ str(interpretation.get("prompt", "") or ""),
77
+ options,
78
+ visible_options=visible,
79
+ source_question=question,
80
+ clarification_type=f"hermeneutic:{interpretation.get('family_id', '')}",
81
+ option_metadata=metadata,
82
+ )
83
+
84
+
85
+ def build_document_scope_clarification(question: str, candidates: list[dict[str, Any]] | None = None) -> str:
86
+ """Disambiguate an article number that exists in more than one source document."""
87
+ candidates = candidates or []
88
+ options = []
89
+ seen = set()
90
+ for item in candidates:
91
+ channels = ((item.get("_score_breakdown", {}) or {}).get("channels", {}) or {})
92
+ if "exact_reference" not in channels:
93
+ continue
94
+ document_id = str(item.get("document_id", "") or "")
95
+ article_id = str(item.get("article_id", "") or "")
96
+ key = f"{document_id}::{article_id}"
97
+ if not document_id or not article_id or key in seen:
98
+ continue
99
+ seen.add(key)
100
+ document_title = str(item.get("document_title", "") or document_id)
101
+ article_title = str(item.get("article_title", "") or "")
102
+ resolved = f"{question} ({document_title}, {article_id})"
103
+ label = " - ".join(part for part in (document_title, article_id, article_title) if part)
104
+ options.append((resolved, label))
105
+ if len(options) < 2:
106
+ return ""
107
+ machine = {str(index): resolved for index, (resolved, _label) in enumerate(options[:3], start=1)}
108
+ visible = {str(index): label for index, (_resolved, label) in enumerate(options[:3], start=1)}
109
+ return _render_options(
110
+ "Bu madde numarası birden fazla kaynak belgede bulunuyor. Hangi kanunu kastettiğinizi seçer misiniz?",
111
+ machine,
112
+ visible_options=visible,
113
+ source_question=question,
114
+ clarification_type="document_scope",
115
+ )
116
+
117
+
118
+ def build_low_confidence_clarification(
119
+ question: str,
120
+ candidates: list[dict[str, Any]] | None = None,
121
+ candidate_validator: Callable[..., bool] | None = None,
122
+ ) -> str:
123
+ domain_clarification = _domain_specific_clarification(question, candidate_validator)
124
+ if domain_clarification:
125
+ return domain_clarification
126
+
127
+ candidates = candidates or []
128
+ distinct = []
129
+ for item in _distinct_grounded_article_candidates(question, candidates, limit=3):
130
+ article = str(item.get("article_id", "") or "")
131
+ document_id = str(item.get("document_id", "") or "")
132
+ document_title = str(item.get("document_title", "") or document_id).strip()
133
+ title = str(item.get("article_title", "") or item.get("title", "") or item.get("norm_type", "") or "").strip()
134
+ scope = " - ".join(part for part in (document_title, article, title) if part)
135
+ resolved_question = f"{scope} kapsamında şu soruyu yanıtla: {question}"
136
+ if candidate_validator:
137
+ try:
138
+ valid = candidate_validator(resolved_question, article, document_id)
139
+ except TypeError:
140
+ valid = candidate_validator(resolved_question, article)
141
+ if not valid:
142
+ continue
143
+ label = f"{document_title} - {article} - {title}".strip(" -")
144
+ distinct.append((article, label, resolved_question))
145
+
146
+ if not distinct:
147
+ return ""
148
+
149
+ options = {
150
+ str(index): resolved_question
151
+ for index, (_article, _label, resolved_question) in enumerate(distinct, start=1)
152
+ }
153
+ visible = {
154
+ str(index): f"{_label}"
155
+ for index, (_article, _label, _resolved_question) in enumerate(distinct, start=1)
156
+ }
157
+ prompt = (
158
+ "Bu soruda birden fazla doğrulanabilir düzenleme yakın görünüyor. "
159
+ "Yanlış kaynak göstermemek için hangisini kastettiğinizi seçer misiniz?"
160
+ if len(distinct) >= 2
161
+ else
162
+ "Sorunuzla corpus içinde en yakın doğrulanabilir düzenleme aşağıdaki madde. Bunu mu kastettiniz?"
163
+ )
164
+ return _render_options(
165
+ prompt,
166
+ options,
167
+ visible_options=visible,
168
+ source_question=question,
169
+ clarification_type="low_confidence",
170
+ )
171
+
172
+
173
+ def should_request_evidence_clarification(
174
+ question: str,
175
+ clause_eval: dict[str, Any] | None,
176
+ allow_domain_specific: bool = True,
177
+ ) -> bool:
178
+ decision = evidence_confidence_decision(
179
+ question,
180
+ clause_eval,
181
+ allow_domain_specific=allow_domain_specific,
182
+ )
183
+ return decision["action"] == "clarify"
184
+
185
+
186
+ def evidence_confidence_decision(
187
+ question: str,
188
+ clause_eval: dict[str, Any] | None,
189
+ allow_domain_specific: bool = True,
190
+ ) -> dict[str, Any]:
191
+ """Choose answer/clarify/abstain for every retrieval result.
192
+
193
+ Retrieval confidence is evaluated at the legal-provision level. Multiple
194
+ evidence fragments from one article cannot hide a close competing article,
195
+ and a high fused score cannot cure weak semantic-address relevance.
196
+ """
197
+ clause_eval = clause_eval or {}
198
+ evidence_spans = clause_eval.get("evidence_spans", []) or []
199
+ proof = clause_eval.get("proof_bundle", {}) or {}
200
+ proof_status = str(proof.get("status", "insufficient") or "insufficient")
201
+ support = proof.get("support", {}) or {}
202
+ support_score = float(support.get("score", 0.0) or 0.0)
203
+ signals = support.get("signals", {}) or {}
204
+ q = normalize_for_search(question)
205
+
206
+ if not evidence_spans:
207
+ return _confidence_result("abstain", "no_evidence", support_score)
208
+
209
+ if allow_domain_specific and _looks_like_amnesty_query(q):
210
+ return _confidence_result("clarify", "domain_term_requires_confirmation", support_score)
211
+
212
+ if proof_status != "supported":
213
+ return _confidence_result("clarify", "proof_not_supported", support_score)
214
+
215
+ top = evidence_spans[0]
216
+ top_breakdown = top.get("_score_breakdown", {}) or {}
217
+ top_channels = top_breakdown.get("channels", {}) or {}
218
+ if "exact_reference" in top_channels:
219
+ return _confidence_result("answer", "exact_reference", support_score)
220
+
221
+ if _is_explicit_multi_document_question(q):
222
+ if support_score >= ANSWER_SUPPORT_THRESHOLD:
223
+ return _confidence_result("answer", "explicit_multi_document_scope", support_score)
224
+ return _confidence_result("clarify", "weak_multi_document_support", support_score)
225
+
226
+ if support_score < ANSWER_SUPPORT_THRESHOLD:
227
+ return _confidence_result("clarify", "support_below_answer_threshold", support_score)
228
+
229
+ grounded = _distinct_grounded_article_candidates(question, evidence_spans, limit=5)
230
+ if not grounded:
231
+ return _confidence_result("abstain", "no_semantically_grounded_article", support_score)
232
+
233
+ top_grounded = grounded[0]
234
+ top_score = float(top_grounded.get("_score", 0.0) or 0.0)
235
+ top_relevance = _candidate_semantic_relevance(question, top_grounded)
236
+ address_match = semantic_address_score(question, top_grounded.get("_semantic_address", {}) or {})
237
+ article_hint = float(top_breakdown.get("article_hint_score", 0.0) or 0.0)
238
+ route = clause_eval.get("source_route", {}) or {}
239
+ route_targets = route.get("target_articles_by_document", {}) or {}
240
+ routed_article = any(
241
+ str(top_grounded.get("article_id", "") or "") in (articles or [])
242
+ for articles in route_targets.values()
243
+ )
244
+
245
+ # The top raw result must itself be the best grounded provision. Otherwise
246
+ # a named-institution or generic-title hit may be masking the real result.
247
+ top_scope = _article_scope(top)
248
+ if top_scope != _article_scope(top_grounded):
249
+ return _confidence_result("clarify", "ungrounded_top_result", support_score)
250
+
251
+ competitors = [item for item in grounded[1:] if _article_scope(item) != _article_scope(top_grounded)]
252
+ close_competitor = None
253
+ for competitor in competitors:
254
+ competitor_score = float(competitor.get("_score", 0.0) or 0.0)
255
+ competitor_relevance = _candidate_semantic_relevance(question, competitor)
256
+ score_ratio = competitor_score / max(top_score, 0.0001)
257
+ if (
258
+ competitor_relevance >= MIN_SEMANTIC_RELEVANCE
259
+ and (
260
+ top_score - competitor_score < COMPETING_ARTICLE_MARGIN
261
+ or score_ratio >= COMPETING_ARTICLE_RATIO
262
+ )
263
+ ):
264
+ close_competitor = competitor
265
+ break
266
+
267
+ if close_competitor:
268
+ return _confidence_result("clarify", "competing_grounded_articles", support_score)
269
+
270
+ if (
271
+ top_relevance < MIN_SEMANTIC_RELEVANCE
272
+ and address_match < MIN_SEMANTIC_RELEVANCE
273
+ and article_hint <= 0
274
+ and not routed_article
275
+ and not _has_triangulated_grounding(top_grounded, top_relevance, top_score)
276
+ ):
277
+ return _confidence_result("clarify", "weak_semantic_relevance", support_score)
278
+
279
+ if int(signals.get("channel_consensus", 0) or 0) < 2 and not routed_article:
280
+ return _confidence_result("clarify", "weak_channel_consensus", support_score)
281
+
282
+ return _confidence_result("answer", "calibrated_support", support_score)
283
+
284
+
285
+ def _domain_specific_clarification(
286
+ question: str,
287
+ candidate_validator: Callable[[str, str], bool] | None = None,
288
+ ) -> str:
289
+ q = normalize_for_search(question)
290
+ if _looks_like_amnesty_query(q):
291
+ resolved_question = (
292
+ "2547 sayili Kanun Gecici Madde 83 kapsaminda ilisigi kesilen "
293
+ "ogrencilerin basvuru ve yeniden ogrenime baslama haklari nasil duzenlenir?"
294
+ )
295
+ if candidate_validator and not candidate_validator(resolved_question, "Geçici Madde 83"):
296
+ return ""
297
+ return _render_options(
298
+ "Genel af hakkında corpus içinde doğrudan bir hüküm yok. Aşağıdaki corpus hükmünü mü kastediyorsunuz?",
299
+ {
300
+ "1": resolved_question,
301
+ },
302
+ visible_options={
303
+ "1": "Öğrenci affı olarak bilinen geçici başvuru ve yeniden kayıt hakları (Geçici Madde 83)",
304
+ },
305
+ source_question=question,
306
+ clarification_type="domain_amnesty",
307
+ )
308
+
309
+ return ""
310
+
311
+
312
+ def _distinct_grounded_article_candidates(
313
+ question: str,
314
+ candidates: list[dict[str, Any]],
315
+ limit: int = 3,
316
+ ) -> list[dict[str, Any]]:
317
+ if not candidates:
318
+ return []
319
+ top_score = float(candidates[0].get("_score", 0.0) or 0.0)
320
+ distinct = []
321
+ seen = set()
322
+ for item in candidates[:16]:
323
+ scope = _article_scope(item)
324
+ if not all(scope) or scope in seen:
325
+ continue
326
+ if not _is_grounded_ambiguity_candidate(question, item, top_score):
327
+ continue
328
+ seen.add(scope)
329
+ distinct.append(item)
330
+ if len(distinct) >= limit:
331
+ break
332
+ return distinct
333
+
334
+
335
+ def _is_grounded_ambiguity_candidate(question: str, item: dict[str, Any], top_score: float) -> bool:
336
+ score = float(item.get("_score", 0.0) or 0.0)
337
+ if score < MIN_OPTION_SCORE or score / max(top_score, 0.0001) < MIN_OPTION_SCORE_RATIO:
338
+ return False
339
+ source_text = str(item.get("source_text", "") or "").strip()
340
+ if len(source_text) < 30 or _has_unmentioned_named_university(question, item):
341
+ return False
342
+ breakdown = item.get("_score_breakdown", {}) or {}
343
+ channels = breakdown.get("channels", {}) or {}
344
+ consensus = int(breakdown.get("channel_consensus", len(channels)) or 0)
345
+ if consensus < 2:
346
+ return False
347
+
348
+ title = " ".join(
349
+ str(item.get(key, "") or "") for key in ("article_title", "title", "article_id")
350
+ )
351
+ title_overlap = topic_term_overlap(question, [title])
352
+ address_match = semantic_address_score(question, item.get("_semantic_address", {}) or {})
353
+ relevance = _candidate_semantic_relevance(question, item)
354
+ # A human-reviewed semantic address is the strongest grounding signal.
355
+ # Newly onboarded documents may initially have only structure-derived
356
+ # addresses, so independent retrieval channels may also establish
357
+ # grounding when lexical, dense and semantic-frame evidence converge.
358
+ unresolved_generic_actor = _has_unresolved_generic_actor(question)
359
+ triangulated_retrieval = _has_triangulated_grounding(item, relevance, top_score)
360
+ routed_hint_grounding = (
361
+ score >= top_score * 0.95
362
+ and consensus >= 4
363
+ and title_overlap >= 0.25
364
+ and "router_hint" in channels
365
+ )
366
+ return (
367
+ title_overlap >= MIN_SEMANTIC_RELEVANCE
368
+ or (address_match >= 0.70 and relevance >= 0.70 and not unresolved_generic_actor)
369
+ or (triangulated_retrieval and not unresolved_generic_actor)
370
+ or routed_hint_grounding
371
+ )
372
+
373
+
374
+ def _candidate_semantic_relevance(question: str, item: dict[str, Any]) -> float:
375
+ address = item.get("_semantic_address", {}) or {}
376
+ expanded_question = " ".join([question, *sorted(expand_query_terms(question))])
377
+ evidence_texts = [
378
+ str(item.get("source_text", "") or ""),
379
+ str(item.get("article_title", "") or ""),
380
+ str(address.get("canonical_address", "") or ""),
381
+ str(address.get("regulates", "") or ""),
382
+ " ".join(str(value) for value in address.get("domain_path", []) or []),
383
+ " ".join(str(value) for value in address.get("canonical_concepts", []) or []),
384
+ " ".join(str(value) for value in address.get("query_aliases", []) or []),
385
+ ]
386
+ return max(
387
+ topic_term_overlap(question, evidence_texts),
388
+ topic_term_overlap(expanded_question, evidence_texts),
389
+ semantic_address_score(question, address),
390
+ semantic_address_score(expanded_question, address),
391
+ )
392
+
393
+
394
+ def _has_triangulated_grounding(
395
+ item: dict[str, Any],
396
+ relevance: float,
397
+ top_score: float,
398
+ ) -> bool:
399
+ score = float(item.get("_score", 0.0) or 0.0)
400
+ breakdown = item.get("_score_breakdown", {}) or {}
401
+ channels = breakdown.get("channels", {}) or {}
402
+ consensus = int(breakdown.get("channel_consensus", len(channels)) or 0)
403
+ return (
404
+ score >= top_score * 0.95
405
+ and consensus >= 4
406
+ and relevance >= 0.35
407
+ and "bm25" in channels
408
+ and "semantic_frame" in channels
409
+ and any(channel in channels for channel in ("lsa_dense", "embedding_dense"))
410
+ )
411
+
412
+
413
+ def _has_unresolved_generic_actor(question: str) -> bool:
414
+ normalized = normalize_for_search(question)
415
+ tokens = set(normalized.split())
416
+ mentions_generic_body = bool(tokens & {"kurul", "kurulu", "kurulun", "kurulunun"})
417
+ return mentions_generic_body and not detect_actors(question)
418
+
419
+
420
+ def _has_unmentioned_named_university(question: str, item: dict[str, Any]) -> bool:
421
+ """Reject one-off university establishment articles unless named by user."""
422
+ title = normalize_for_search(str(item.get("article_title", "") or ""))
423
+ if not re.search(r"\buniversitesi\b", title):
424
+ return False
425
+ name_tokens = [
426
+ token for token in title.split()
427
+ if token not in {"universitesi", "universite", "vakif", "devlet"} and len(token) >= 3
428
+ ]
429
+ if not name_tokens:
430
+ return False
431
+ q = normalize_for_search(question)
432
+ return not any(token in q for token in name_tokens)
433
+
434
+
435
+ def _article_scope(item: dict[str, Any]) -> tuple[str, str]:
436
+ return (
437
+ str(item.get("document_id", "") or ""),
438
+ str(item.get("article_id", "") or ""),
439
+ )
440
+
441
+
442
+ def _is_explicit_multi_document_question(normalized_question: str) -> bool:
443
+ document_codes = set(re.findall(r"\b(?:2547|2914|2809)\b", normalized_question))
444
+ if len(document_codes) < 2:
445
+ return False
446
+ return any(
447
+ marker in normalized_question
448
+ for marker in ("hangisi", "daha dogrudan", "birlikte", "karsilastir", "arasinda")
449
+ )
450
+
451
+
452
+ def _confidence_result(action: str, reason: str, support_score: float) -> dict[str, Any]:
453
+ return {
454
+ "action": action,
455
+ "reason": reason,
456
+ "support_score": round(support_score, 4),
457
+ }
458
+
459
+
460
+ def _text_anchor_overlap(question: str, text: str) -> float:
461
+ anchors = _anchor_tokens(question)
462
+ if not anchors:
463
+ return 0.0
464
+ normalized = normalize_for_search(text)
465
+ hits = sum(1 for anchor in anchors if anchor in normalized)
466
+ return hits / len(anchors)
467
+
468
+
469
+ def _looks_like_amnesty_query(q: str) -> bool:
470
+ if "genel af" in q or "ogrenci aff" in q:
471
+ return True
472
+ return bool(re.search(r"\baf\b|\baflar\b|\baffi\b|\baftan\b", q))
473
+
474
+
475
+ def _looks_like_exam_rights_query(q: str) -> bool:
476
+ has_exam = "sinav" in q
477
+ has_right = any(term in q for term in ("hak", "hakki", "haklari"))
478
+ has_three = bool(re.search(r"\b3\b|\buc\b", q))
479
+ return has_exam and has_right and has_three
480
+
481
+
482
+ def _query_anchor_overlap(question: str, evidence: dict[str, Any]) -> float:
483
+ query_tokens = _anchor_tokens(question)
484
+ if not query_tokens:
485
+ return 0.5
486
+ evidence_text = " ".join(
487
+ str(value or "")
488
+ for value in (
489
+ evidence.get("source_text", ""),
490
+ evidence.get("article_id", ""),
491
+ evidence.get("article_title", ""),
492
+ evidence.get("title", ""),
493
+ " ".join(evidence.get("semantic_roles", []) or []),
494
+ )
495
+ )
496
+ evidence_norm = normalize_for_search(evidence_text)
497
+ hits = sum(1 for token in query_tokens if token in evidence_norm)
498
+ return hits / max(1, len(query_tokens))
499
+
500
+
501
+ def _anchor_tokens(text: str) -> list[str]:
502
+ stopwords = {
503
+ "2547", "2914", "2809", "sayili", "kanun", "kanunu", "kanunda", "kanununda",
504
+ "nedir", "nelerdir", "nasil", "hangi", "hakkinda", "bilgi", "ver", "verir",
505
+ "gore", "ile", "icin", "olan", "bir", "bu", "su", "mi", "mu", "midir",
506
+ }
507
+ tokens = []
508
+ for token in normalize_for_search(text).split():
509
+ if len(token) < 2 or token in stopwords:
510
+ continue
511
+ tokens.append(token)
512
+ return tokens[:8]
513
+
514
+
515
+ def _render_options(
516
+ prompt: str,
517
+ options: dict[str, str],
518
+ visible_options: dict[str, str] | None = None,
519
+ source_question: str = "",
520
+ clarification_type: str = "",
521
+ option_metadata: dict[str, dict[str, Any]] | None = None,
522
+ ) -> str:
523
+ visible_options = visible_options or options
524
+ option_metadata = option_metadata or {}
525
+ for key, label in visible_options.items():
526
+ _CLICK_TARGETS[normalize_for_search(str(label))] = {
527
+ "selected_key": str(key),
528
+ "resolved_question": str(options.get(key, "")),
529
+ "source_question": source_question,
530
+ "clarification_type": clarification_type,
531
+ "option_metadata": option_metadata.get(str(key), {}),
532
+ }
533
+ lines = [prompt, ""]
534
+ for key in sorted(options, key=int):
535
+ lines.append(f"{key}. {visible_options[key]}")
536
+ payload = {
537
+ "prompt": prompt,
538
+ "options": options,
539
+ "visible_options": visible_options,
540
+ "source_question": source_question,
541
+ "clarification_type": clarification_type,
542
+ "option_metadata": option_metadata,
543
+ }
544
+ lines.extend([
545
+ "",
546
+ "Devam etmek için uygun seçeneğe tıklayın.",
547
+ f"<!-- {MARKER}{json.dumps(payload, ensure_ascii=False)} -->",
548
+ ])
549
+ return "\n".join(lines)
550
+
551
+
552
+ def _selection_number(message: str) -> str:
553
+ text = normalize_for_search(message)
554
+ match = re.search(r"\b([1-3])\b", text)
555
+ return match.group(1) if match else ""
556
+
557
+
558
+ def _selection_key(message: str, payload: dict[str, Any]) -> str:
559
+ selection = _selection_number(message)
560
+ if selection:
561
+ return selection
562
+ selected_label = normalize_for_search(message)
563
+ for key, label in (payload.get("visible_options", {}) or {}).items():
564
+ if selected_label == normalize_for_search(str(label)):
565
+ return str(key)
566
+ return ""
567
+
568
+
569
+ def _latest_options(history: Any) -> dict[str, str]:
570
+ payload = _latest_payload(history)
571
+ return payload.get("options", {}) if payload else {}
572
+
573
+
574
+ def _latest_payload(history: Any) -> dict[str, Any]:
575
+ for content in _assistant_messages_reversed(history):
576
+ marker_index = content.rfind(MARKER)
577
+ if marker_index < 0:
578
+ continue
579
+ payload = content[marker_index + len(MARKER):]
580
+ payload = payload.split("-->", 1)[0].strip()
581
+ try:
582
+ data = json.loads(payload)
583
+ except json.JSONDecodeError:
584
+ continue
585
+ if isinstance(data, dict):
586
+ if "options" in data and isinstance(data.get("options"), dict):
587
+ data["options"] = {str(key): str(value) for key, value in data.get("options", {}).items()}
588
+ data["visible_options"] = {
589
+ str(key): str(value) for key, value in data.get("visible_options", {}).items()
590
+ }
591
+ data["option_metadata"] = {
592
+ str(key): value for key, value in (data.get("option_metadata", {}) or {}).items()
593
+ if isinstance(value, dict)
594
+ }
595
+ return data
596
+ return {
597
+ "options": {str(key): str(value) for key, value in data.items()},
598
+ "source_question": "",
599
+ "clarification_type": "",
600
+ }
601
+ return {}
602
+
603
+
604
+ def clarification_payload(content: str) -> dict[str, Any]:
605
+ """Return the embedded option payload used by the Gradio renderer."""
606
+ return _payload_from_content(content)
607
+
608
+
609
+ def clarification_display_text(content: str) -> str:
610
+ """Remove the machine payload before content reaches the Chatbot DOM."""
611
+ payload = _payload_from_content(content)
612
+ if not payload:
613
+ return content
614
+ prompt = str(payload.get("prompt", "") or "").strip()
615
+ return f"{prompt}\n\nDevam etmek için aşağıdaki seçeneklerden birine tıklayın."
616
+
617
+
618
+ def _payload_from_content(content: str) -> dict[str, Any]:
619
+ marker_index = (content or "").rfind(MARKER)
620
+ if marker_index < 0:
621
+ return {}
622
+ raw_payload = content[marker_index + len(MARKER):].split("-->", 1)[0].strip()
623
+ try:
624
+ data = json.loads(raw_payload)
625
+ except json.JSONDecodeError:
626
+ return {}
627
+ return data if isinstance(data, dict) else {}
628
+
629
+
630
+ def _assistant_messages_reversed(history: Any):
631
+ if not history:
632
+ return
633
+ for item in reversed(history):
634
+ if isinstance(item, dict) and item.get("role") == "assistant":
635
+ yield str(item.get("content", "") or "")
636
+ elif isinstance(item, (list, tuple)) and len(item) >= 2:
637
+ yield str(item[1] or "")
clause_retrieval.py ADDED
@@ -0,0 +1,1784 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from intent import intent_profile
9
+ from sources import format_clause_sources, set_article_sources
10
+ from utils import normalize_for_search, query_terms, clean_answer_text
11
+ from normative_roles import (
12
+ build_normative_knowledge_unit,
13
+ enrich_normative_frame,
14
+ extract_question_frame,
15
+ normative_kg_alignment_score,
16
+ semantic_role_match_score,
17
+ )
18
+ from llm import llm_available, call_chat_completion
19
+ from prompts import SYSTEM_PROMPT, response_style_instruction
20
+ from query_understanding import query_understanding, expand_query_terms
21
+ from rdfox_pilot import (
22
+ analyze_pilot_query,
23
+ apply_pilot_answer_micro_rules,
24
+ enrich_selected_evidence,
25
+ micro_rule_score,
26
+ )
27
+ from source_router import route_question
28
+ from config import MCKF_DIR, RDFOX_PILOT_TRIPLES_PATH
29
+ from graph_runtime import MCKFGraphRuntime
30
+ from hybrid_retrieval import HybridRetrievalEngine
31
+ from hermeneutic_intent import analyze_interpretive_intent, legal_object_retrieval_factor
32
+ from proof_bundle import (
33
+ build_proof_bundle,
34
+ llm_claim_prompt,
35
+ parse_and_validate_llm_claims,
36
+ render_proof_answer,
37
+ )
38
+
39
+
40
+ ONTOLOGY: dict[str, Any] = {}
41
+ CLAUSES: list[dict[str, Any]] = []
42
+ EVIDENCE_SPANS: list[dict[str, Any]] = []
43
+ CONCEPTS_BY_ID: dict[str, dict[str, Any]] = {}
44
+ CLAUSES_BY_ID: dict[str, dict[str, Any]] = {}
45
+ DOCUMENTS_BY_ID: dict[str, dict[str, Any]] = {}
46
+ CROSS_DOCUMENT_EDGES: list[dict[str, Any]] = []
47
+ GRAPH_RUNTIME: MCKFGraphRuntime | None = None
48
+ HYBRID_ENGINE: HybridRetrievalEngine | None = None
49
+ CORPUS_BUILD_ID = ""
50
+
51
+ MIN_EVIDENCE_SCORE = 8.0
52
+ MIN_SCORE_GAP = 0.35
53
+
54
+
55
+ def init_clause_retrieval(ontology: dict[str, Any] | None) -> None:
56
+ global ONTOLOGY, CLAUSES, EVIDENCE_SPANS, CONCEPTS_BY_ID, CLAUSES_BY_ID, DOCUMENTS_BY_ID, CROSS_DOCUMENT_EDGES
57
+ global GRAPH_RUNTIME, HYBRID_ENGINE, CORPUS_BUILD_ID
58
+ ONTOLOGY = ontology or {}
59
+ CLAUSES = ONTOLOGY.get("clauses", []) or []
60
+ EVIDENCE_SPANS = ONTOLOGY.get("evidence_spans", []) or _flatten_evidence_spans(CLAUSES)
61
+ DOCUMENTS_BY_ID = {
62
+ document.get("document_id", ""): document
63
+ for document in ONTOLOGY.get("documents", []) or []
64
+ if document.get("document_id")
65
+ }
66
+ if not DOCUMENTS_BY_ID and ONTOLOGY.get("document_id"):
67
+ DOCUMENTS_BY_ID = {
68
+ ONTOLOGY.get("document_id", ""): {
69
+ "document_id": ONTOLOGY.get("document_id", ""),
70
+ "title": ONTOLOGY.get("document_title", ""),
71
+ "short_code": ONTOLOGY.get("short_code", ""),
72
+ "domain_tags": [],
73
+ }
74
+ }
75
+ CROSS_DOCUMENT_EDGES = ONTOLOGY.get("cross_document_edges", []) or []
76
+ CONCEPTS_BY_ID = {
77
+ concept.get("concept_id", ""): concept
78
+ for concept in ONTOLOGY.get("concepts", []) or []
79
+ if concept.get("concept_id")
80
+ }
81
+ CLAUSES_BY_ID = {
82
+ clause.get("clause_id", ""): clause
83
+ for clause in CLAUSES
84
+ if clause.get("clause_id")
85
+ }
86
+ _enrich_runtime_normative_units()
87
+ for evidence in EVIDENCE_SPANS:
88
+ parent_clause = CLAUSES_BY_ID.get(evidence.get("parent_clause_id", ""), {})
89
+ concept_id = parent_clause.get("parent_concept_id", "")
90
+ concept = CONCEPTS_BY_ID.get(concept_id, {})
91
+ evidence["_concept_id"] = concept_id
92
+ evidence["_semantic_address"] = concept.get("normative_metadata", {}) or {}
93
+ CORPUS_BUILD_ID = str(ONTOLOGY.get("build_id", "") or "")
94
+ GRAPH_RUNTIME = MCKFGraphRuntime(
95
+ EVIDENCE_SPANS,
96
+ CROSS_DOCUMENT_EDGES,
97
+ triples_path=RDFOX_PILOT_TRIPLES_PATH,
98
+ )
99
+ HYBRID_ENGINE = HybridRetrievalEngine(
100
+ EVIDENCE_SPANS,
101
+ GRAPH_RUNTIME,
102
+ build_id=CORPUS_BUILD_ID,
103
+ cache_dir=MCKF_DIR / "runtime_cache",
104
+ )
105
+ set_article_sources({
106
+ f"{concept.get('document_id', '')}::{concept.get('article_id', '')}": {
107
+ "document_id": concept.get("document_id", ""),
108
+ "document_title": concept.get("document_title", ""),
109
+ "article_id": concept.get("article_id", ""),
110
+ "title": concept.get("title", ""),
111
+ "source_text": concept.get("source_text", ""),
112
+ }
113
+ for concept in ONTOLOGY.get("concepts", []) or []
114
+ if concept.get("document_id") and concept.get("article_id")
115
+ })
116
+
117
+
118
+ def load_ontology(path: Path) -> dict[str, Any]:
119
+ if not path.exists():
120
+ return {}
121
+ return json.loads(path.read_text(encoding="utf-8"))
122
+
123
+
124
+ def _enrich_runtime_normative_units() -> None:
125
+ for clause in CLAUSES:
126
+ text = clause.get("source_text", "")
127
+ frame = enrich_normative_frame(text, clause.get("semantic_frame", {}), clause.get("actors", []))
128
+ clause["semantic_frame"] = frame
129
+ clause["normative_unit"] = build_normative_knowledge_unit(
130
+ text,
131
+ frame,
132
+ {
133
+ "clause_id": clause.get("clause_id", ""),
134
+ "document_id": clause.get("document_id", ""),
135
+ "article_id": clause.get("article_id", ""),
136
+ "label": clause.get("label", ""),
137
+ },
138
+ )
139
+
140
+ for evidence in EVIDENCE_SPANS:
141
+ parent_clause = CLAUSES_BY_ID.get(evidence.get("parent_clause_id", ""), {})
142
+ text = evidence.get("source_text", "")
143
+ frame = enrich_normative_frame(text, evidence.get("semantic_frame", {}), parent_clause.get("actors", []))
144
+ evidence["semantic_frame"] = frame
145
+ evidence["normative_unit"] = build_normative_knowledge_unit(
146
+ text,
147
+ frame,
148
+ {
149
+ "evidence_id": evidence.get("evidence_id", ""),
150
+ "parent_clause_id": evidence.get("parent_clause_id", ""),
151
+ "document_id": evidence.get("document_id", ""),
152
+ "article_id": evidence.get("article_id", ""),
153
+ "label": evidence.get("label", ""),
154
+ },
155
+ )
156
+
157
+
158
+ def retrieve_evidence_spans(
159
+ question: str,
160
+ limit: int = 80,
161
+ min_score: float = MIN_EVIDENCE_SCORE,
162
+ allowed_articles: list[str] | None = None,
163
+ allowed_documents: list[str] | None = None,
164
+ allowed_scopes: list[tuple[str, str]] | None = None,
165
+ ) -> list[dict[str, Any]]:
166
+ if not EVIDENCE_SPANS or HYBRID_ENGINE is None:
167
+ return []
168
+
169
+ profile = intent_profile(question)
170
+ profile["raw_question"] = question
171
+ profile.update(query_understanding(question))
172
+ profile["pilot_query"] = analyze_pilot_query(question)
173
+ source_route = route_question(question, DOCUMENTS_BY_ID, CROSS_DOCUMENT_EDGES)
174
+ profile["source_route"] = source_route
175
+ terms = query_terms(question)
176
+ if profile.get("interpretive_intent") or profile.get("practical_effect_intent"):
177
+ terms |= expand_query_terms(
178
+ question,
179
+ interpretive=profile.get("interpretive_intent"),
180
+ practical_effect=profile.get("practical_effect_intent"),
181
+ )
182
+ explicit_refs = _explicit_article_references(question)
183
+ scope_constraints = {
184
+ (str(document_id), str(article_id))
185
+ for document_id, article_id in (allowed_scopes or [])
186
+ if document_id and article_id
187
+ }
188
+ allowed = set(allowed_articles or explicit_refs)
189
+ allowed.update(article_id for _document_id, article_id in scope_constraints)
190
+ explicit_documents = _explicit_document_ids(question)
191
+ if allowed_documents:
192
+ constrained_documents = set(allowed_documents)
193
+ elif source_route.get("cross_document"):
194
+ # In comparative questions, named laws describe the requested scope;
195
+ # they must not exclude a second law inferred by the relation graph.
196
+ constrained_documents = set(source_route.get("candidate_document_ids", []) or explicit_documents)
197
+ elif explicit_documents:
198
+ constrained_documents = set(explicit_documents)
199
+ elif (
200
+ len(source_route.get("candidate_document_ids", []) or []) == 1
201
+ and (
202
+ float(source_route.get("top_score", 0.0) or 0.0) >= 0.60
203
+ or len(source_route.get("target_articles_by_document", {}) or {}) == 1
204
+ )
205
+ ):
206
+ # A high-confidence single-document route is also a scope boundary.
207
+ # This prevents graph expansion from leaking unrelated statutes into
208
+ # an otherwise unambiguous answer.
209
+ constrained_documents = set(source_route.get("candidate_document_ids", []) or [])
210
+ else:
211
+ constrained_documents = set()
212
+ allowed_ids: set[str] = set()
213
+ for evidence in EVIDENCE_SPANS:
214
+ evidence_scope = (
215
+ str(evidence.get("document_id", "") or ""),
216
+ str(evidence.get("article_id", "") or ""),
217
+ )
218
+ if scope_constraints and evidence_scope not in scope_constraints:
219
+ continue
220
+ if allowed and evidence.get("article_id", "") not in allowed:
221
+ continue
222
+ document_id = evidence.get("document_id", "")
223
+ if constrained_documents and document_id not in constrained_documents:
224
+ continue
225
+ evidence_id = str(evidence.get("evidence_id", "") or "")
226
+ if evidence_id:
227
+ allowed_ids.add(evidence_id)
228
+
229
+ question_frame = extract_question_frame(question)
230
+ question_frame["query_expansions"] = list(profile.get("expanded_terms", []) or [])
231
+ requested_roles = {str(profile.get("norm_type", "") or "")}
232
+ requested_roles.update(str(role) for role in (profile.get("pilot_query", {}) or {}).get("semantic_role_boost", []) or [])
233
+ requested_roles.update(str(role) for role in question_frame.get("requested_categories", []) or [])
234
+ requested_roles.discard("")
235
+ semantic_pool = HYBRID_ENGINE.semantic_prefilter(
236
+ question,
237
+ question_frame,
238
+ requested_roles,
239
+ allowed_ids,
240
+ set(allowed),
241
+ )
242
+ hint_scopes = {
243
+ (str(document_id), str(article_id))
244
+ for document_id, article_ids in (source_route.get("target_articles_by_document", {}) or {}).items()
245
+ for article_id in article_ids or []
246
+ }
247
+ routed_articles = {str(value) for value in profile.get("routed_articles", []) or [] if value}
248
+ routed_document = str(profile.get("routed_document_id", "") or source_route.get("top_document_id", "") or "")
249
+ if routed_document and routed_articles:
250
+ hint_scopes.update((routed_document, article_id) for article_id in routed_articles)
251
+ relation_scopes, matched_relation_ids = _matched_normative_relation_scopes(
252
+ question,
253
+ source_route.get("candidate_edges", []) or [],
254
+ )
255
+ profile["matched_normative_relation_ids"] = matched_relation_ids
256
+ if hint_scopes:
257
+ semantic_pool.update(
258
+ evidence_id
259
+ for evidence_id in allowed_ids
260
+ if (
261
+ str(HYBRID_ENGINE.evidence_by_id[evidence_id].get("document_id", "")),
262
+ str(HYBRID_ENGINE.evidence_by_id[evidence_id].get("article_id", "")),
263
+ ) in hint_scopes
264
+ )
265
+ semantic_scores = {}
266
+ for evidence_id in sorted(semantic_pool):
267
+ evidence = HYBRID_ENGINE.evidence_by_id[evidence_id]
268
+ semantic_scores[evidence_id] = evidence_match_score(evidence, profile, terms, source_route=source_route)
269
+ ranked = HYBRID_ENGINE.retrieve(
270
+ question,
271
+ question_frame,
272
+ semantic_scores,
273
+ requested_roles=requested_roles,
274
+ allowed_ids=allowed_ids,
275
+ exact_articles=set(allowed),
276
+ hint_scopes=hint_scopes,
277
+ relation_scopes=relation_scopes,
278
+ limit=limit,
279
+ )
280
+ return _attach_structural_context(ranked, allowed_ids, limit)
281
+
282
+
283
+ def _attach_structural_context(
284
+ ranked: list[dict[str, Any]],
285
+ allowed_ids: set[str],
286
+ limit: int,
287
+ ) -> list[dict[str, Any]]:
288
+ """Keep source fragments that form one sentence together.
289
+
290
+ Evidence extraction intentionally creates atomic spans. Legal sentences
291
+ frequently cross a semicolon, so an isolated span can omit the authority,
292
+ condition or legal effect that completes the proposition. Adjacent spans
293
+ are attached as structural context without giving them an independent
294
+ semantic match score.
295
+ """
296
+ by_scope: dict[tuple[str, str, str], list[dict[str, Any]]] = {}
297
+ for evidence in EVIDENCE_SPANS:
298
+ evidence_id = str(evidence.get("evidence_id", "") or "")
299
+ if evidence_id not in allowed_ids:
300
+ continue
301
+ key = (
302
+ str(evidence.get("document_id", "") or ""),
303
+ str(evidence.get("article_id", "") or ""),
304
+ str(evidence.get("parent_clause_id", "") or ""),
305
+ )
306
+ by_scope.setdefault(key, []).append(evidence)
307
+ for values in by_scope.values():
308
+ values.sort(key=lambda item: int((item.get("source_span", {}) or {}).get("char_start", 10**9) or 0))
309
+
310
+ output: list[dict[str, Any]] = []
311
+ seen: set[str] = set()
312
+ for anchor in ranked:
313
+ anchor_id = str(anchor.get("evidence_id", "") or "")
314
+ if anchor_id and anchor_id not in seen:
315
+ output.append(anchor)
316
+ seen.add(anchor_id)
317
+ key = (
318
+ str(anchor.get("document_id", "") or ""),
319
+ str(anchor.get("article_id", "") or ""),
320
+ str(anchor.get("parent_clause_id", "") or ""),
321
+ )
322
+ siblings = by_scope.get(key, [])
323
+ index = next((i for i, item in enumerate(siblings) if item.get("evidence_id") == anchor_id), -1)
324
+ if index < 0:
325
+ continue
326
+ neighbors = []
327
+ if index > 0 and _source_fragments_continue(siblings[index - 1], anchor):
328
+ neighbors.append(siblings[index - 1])
329
+ if index + 1 < len(siblings) and _source_fragments_continue(anchor, siblings[index + 1]):
330
+ neighbors.append(siblings[index + 1])
331
+ for neighbor in neighbors:
332
+ neighbor_id = str(neighbor.get("evidence_id", "") or "")
333
+ if not neighbor_id or neighbor_id in seen:
334
+ continue
335
+ contextual = dict(neighbor)
336
+ contextual["_score"] = round(max(8.0, float(anchor.get("_score", 0.0) or 0.0) * 0.92), 4)
337
+ contextual["_retrieval_channels"] = sorted(
338
+ set(contextual.get("_retrieval_channels", []) or []) | {"structural_context"}
339
+ )
340
+ contextual["_channel_consensus"] = max(1, int(anchor.get("_channel_consensus", 0) or 0))
341
+ contextual["_score_breakdown"] = {
342
+ **(contextual.get("_score_breakdown", {}) or {}),
343
+ "structural_context_of": anchor_id,
344
+ }
345
+ output.append(contextual)
346
+ seen.add(neighbor_id)
347
+ if len(output) >= limit:
348
+ break
349
+ return output[:limit]
350
+
351
+
352
+ def _source_fragments_continue(previous: dict[str, Any], current: dict[str, Any]) -> bool:
353
+ previous_span = previous.get("source_span", {}) or {}
354
+ current_span = current.get("source_span", {}) or {}
355
+ previous_end = int(previous_span.get("char_end", -1) or -1)
356
+ current_start = int(current_span.get("char_start", -1) or -1)
357
+ if previous_end < 0 or current_start < 0 or current_start - previous_end not in {0, 1, 2}:
358
+ return False
359
+ previous_text = str(previous.get("source_text", "") or "").rstrip()
360
+ return bool(previous_text) and (previous_text.endswith((";", ",", ":")) or previous_text[-1] not in ".?!")
361
+
362
+
363
+ _RELATION_STOPWORDS = {
364
+ "2547", "2809", "2914", "sayili", "kanun", "kanunu", "madde",
365
+ "hangi", "nedir", "arasindaki", "ifadesi", "hukum", "hukmune",
366
+ "baglam", "baglamiyla", "uyarinca",
367
+ }
368
+
369
+
370
+ def _relation_terms(text: str) -> set[str]:
371
+ terms: set[str] = set()
372
+ for token in normalize_for_search(text).split():
373
+ if len(token) < 4 or token.isdigit() or token in _RELATION_STOPWORDS:
374
+ continue
375
+ terms.add(token if len(token) < 6 else token[:5])
376
+ return terms
377
+
378
+
379
+ def _matched_normative_relation_scopes(
380
+ question: str,
381
+ edges: list[dict[str, Any]],
382
+ ) -> tuple[set[tuple[str, str]], list[str]]:
383
+ """Resolve graph edges by their canonical relation description.
384
+
385
+ Relation descriptions are corpus data emitted by the document compiler.
386
+ Requiring at least two substantive concept matches keeps generic statutory
387
+ references from becoming article shortcuts.
388
+ """
389
+ query = _relation_terms(question)
390
+ ranked: list[tuple[int, float, dict[str, Any]]] = []
391
+ for edge in edges:
392
+ relation_text = " ".join(
393
+ str(edge.get(field, "") or "")
394
+ for field in ("description", "relation_type")
395
+ )
396
+ relation = _relation_terms(relation_text)
397
+ matches = query & relation
398
+ if len(matches) < 2:
399
+ continue
400
+ coverage = len(matches) / max(min(len(query), len(relation)), 1)
401
+ ranked.append((len(matches), coverage, edge))
402
+ if not ranked:
403
+ return set(), []
404
+ ranked.sort(key=lambda item: (item[0], item[1]), reverse=True)
405
+ best_count, best_coverage, _ = ranked[0]
406
+ matched = [
407
+ edge for count, coverage, edge in ranked
408
+ if count == best_count and coverage >= best_coverage - 0.08
409
+ ]
410
+ scopes: set[tuple[str, str]] = set()
411
+ for edge in matched:
412
+ scopes.add((str(edge.get("source_document_id", "")), str(edge.get("source_article_id", ""))))
413
+ scopes.add((str(edge.get("target_document_id", "")), str(edge.get("target_article_id", ""))))
414
+ scopes.discard(("", ""))
415
+ return scopes, [str(edge.get("edge_id", "")) for edge in matched if edge.get("edge_id")]
416
+
417
+
418
+ def retrieve_clauses(
419
+ question: str,
420
+ limit: int = 80,
421
+ min_score: float = MIN_EVIDENCE_SCORE,
422
+ allowed_articles: list[str] | None = None,
423
+ ) -> list[dict[str, Any]]:
424
+ """Compatibility wrapper: return parent clauses ranked by best evidence span.
425
+
426
+ v0.6 retrieval is evidence-span first. This function still returns clause objects so older
427
+ tests and callers remain compatible, but each clause carries `_selected_evidence`.
428
+ """
429
+ evidence_spans = retrieve_evidence_spans(question, limit=limit, min_score=min_score, allowed_articles=allowed_articles)
430
+ clauses: list[dict[str, Any]] = []
431
+ seen: set[str] = set()
432
+ for evidence in evidence_spans:
433
+ clause_id = evidence.get("parent_clause_id", "")
434
+ if not clause_id or clause_id in seen:
435
+ continue
436
+ clause = dict(CLAUSES_BY_ID.get(clause_id, {}))
437
+ if not clause:
438
+ continue
439
+ clause["_score"] = evidence.get("_score", 0.0)
440
+ clause["_selected_evidence"] = evidence
441
+ clause["_score_breakdown"] = evidence.get("_score_breakdown", {})
442
+ clauses.append(clause)
443
+ seen.add(clause_id)
444
+ return clauses[:limit]
445
+
446
+
447
+ def evidence_match_score(
448
+ evidence: dict[str, Any],
449
+ profile: dict[str, Any],
450
+ terms: set[str],
451
+ source_route: dict[str, Any] | None = None,
452
+ ) -> float:
453
+ q = profile.get("normalized_question", "")
454
+ norm_type = profile.get("norm_type", "")
455
+ evidence_text = normalize_for_search(evidence.get("source_text", ""))
456
+ article_id = normalize_for_search(evidence.get("article_id", ""))
457
+ label = normalize_for_search(evidence.get("label", ""))
458
+ roles = set(evidence.get("semantic_roles", []) or [])
459
+ parent_clause = CLAUSES_BY_ID.get(evidence.get("parent_clause_id", ""), {})
460
+ concept = CONCEPTS_BY_ID.get(parent_clause.get("parent_concept_id", ""), {})
461
+ concept_text = normalize_for_search(" ".join([
462
+ concept.get("title", ""),
463
+ " ".join(concept.get("aliases", []) or []),
464
+ " ".join(concept.get("question_patterns", []) or []),
465
+ ]))
466
+
467
+ q_frame = extract_question_frame(profile.get("raw_question") or q)
468
+ e_frame = enrich_normative_frame(evidence.get("source_text", ""), evidence.get("semantic_frame", {}) or {}, parent_clause.get("actors", []))
469
+ evidence["semantic_frame"] = e_frame
470
+ srm = semantic_role_match_score(q_frame, e_frame)
471
+ kg_alignment = normative_kg_alignment_score(q_frame, e_frame)
472
+
473
+ norm_score = _norm_type_score(norm_type, evidence, roles, parent_clause, concept)
474
+ entity_scope_score = _entity_scope_score(profile, evidence, concept, e_frame)
475
+ lexical_score = _lexical_score(terms, evidence_text, concept_text, article_id, label)
476
+ structure_score = _structure_score(profile, evidence)
477
+ evidence_focus_score = _evidence_focus_score(profile, evidence_text, e_frame)
478
+ router_score = _source_router_score(evidence, source_route or {})
479
+ document_domain_score = _document_domain_score(evidence, source_route or {})
480
+ article_hint_score = _article_hint_score(evidence, source_route or {})
481
+ pilot_score, pilot_rule_hits = micro_rule_score(
482
+ profile.get("pilot_query"),
483
+ evidence,
484
+ parent_clause=parent_clause,
485
+ concept=concept,
486
+ )
487
+ cross_document_bonus = _cross_document_bonus(evidence, source_route or {})
488
+ wrong_document_penalty = _wrong_document_penalty(evidence, source_route or {})
489
+ generic_article_penalty = _generic_penalty(profile, evidence, e_frame)
490
+ concept_mismatch_penalty = _concept_mismatch_penalty(q_frame, evidence_text, concept_text)
491
+ query_focus_score, query_focus_penalty = _query_focus_match(q, evidence_text)
492
+
493
+ score01 = (
494
+ 0.19 * srm.get("total", 0.0)
495
+ + 0.13 * kg_alignment.get("total", 0.0)
496
+ + 0.16 * entity_scope_score
497
+ + 0.12 * norm_score
498
+ + 0.12 * lexical_score
499
+ + 0.06 * structure_score
500
+ + 0.06 * evidence_focus_score
501
+ + 0.11 * router_score
502
+ + 0.06 * document_domain_score
503
+ + 0.08 * article_hint_score
504
+ + 0.05 * pilot_score
505
+ + 0.14 * query_focus_score
506
+ + cross_document_bonus
507
+ )
508
+
509
+ penalty = generic_article_penalty + wrong_document_penalty + concept_mismatch_penalty + query_focus_penalty
510
+ final01 = max(0.0, min(1.0, score01) - penalty)
511
+ evidence["_score_breakdown"] = {
512
+ "srm": srm,
513
+ "kg_alignment": round(kg_alignment.get("total", 0.0), 4),
514
+ "kg_alignment_components": kg_alignment,
515
+ "semantic_role_score": round(srm.get("total", 0.0), 4),
516
+ "norm_type": round(norm_score, 4),
517
+ "norm_type_score": round(norm_score, 4),
518
+ "entity_scope": round(entity_scope_score, 4),
519
+ "lexical": round(lexical_score, 4),
520
+ "lexical_score": round(lexical_score, 4),
521
+ "structure": round(structure_score, 4),
522
+ "structure_score": round(structure_score, 4),
523
+ "evidence_focus": round(evidence_focus_score, 4),
524
+ "evidence_focus_score": round(evidence_focus_score, 4),
525
+ "source_router_score": round(router_score, 4),
526
+ "document_domain_score": round(document_domain_score, 4),
527
+ "article_hint_score": round(article_hint_score, 4),
528
+ "pilot_rule_score": round(pilot_score, 4),
529
+ "pilot_rule_hits": pilot_rule_hits,
530
+ "query_focus_score": round(query_focus_score, 4),
531
+ "query_focus_penalty": round(query_focus_penalty, 4),
532
+ "cross_document_bonus": round(cross_document_bonus, 4),
533
+ "wrong_document_penalty": round(wrong_document_penalty, 4),
534
+ "generic_article_penalty": round(generic_article_penalty, 4),
535
+ "concept_mismatch_penalty": round(concept_mismatch_penalty, 4),
536
+ "penalty": round(penalty, 4),
537
+ "final_0_1": round(final01, 4),
538
+ }
539
+ return final01 * 20.0
540
+
541
+
542
+ def _query_focus_match(question: str, evidence_text: str) -> tuple[float, float]:
543
+ """Match the governed object in narrow passive appointment questions."""
544
+ match = re.search(r"\b([a-z0-9]+)\s+kim\s+atar\b", question)
545
+ if not match:
546
+ match = re.search(r"\b([a-z0-9]+)\s+nasil\s+atan(?:ir|ir)\b", question)
547
+ if not match:
548
+ return 0.0, 0.0
549
+
550
+ target = match.group(1)
551
+ for suffix in ("leri", "lari", "ini", "unu", "unu", "yı", "yi", "yu", "yü", "i", "ı", "u", "ü"):
552
+ normalized_suffix = normalize_for_search(suffix)
553
+ if target.endswith(normalized_suffix) and len(target) - len(normalized_suffix) >= 4:
554
+ target = target[:-len(normalized_suffix)]
555
+ break
556
+
557
+ if target not in evidence_text:
558
+ return 0.0, 0.14
559
+ if re.search(rf"\b{re.escape(target)}\s+yardimci", evidence_text):
560
+ return 0.0, 0.20
561
+ if "atan" not in evidence_text:
562
+ return 0.0, 0.10
563
+ if re.search(
564
+ rf"\b{re.escape(target)}\b.{{0,140}}(?:cumhurbaskaninca|kurulunca|tarafindan|teklifi).{{0,100}}atan",
565
+ evidence_text,
566
+ ):
567
+ return 1.0, 0.0
568
+ if re.search(rf"\b{re.escape(target)}\b.{{0,100}}\batar\b", evidence_text):
569
+ return 0.85, 0.0
570
+ # "Kim atar?" requires the appointing authority. A sentence that only says
571
+ # a new office holder is appointed does not answer that question.
572
+ return 0.0, 0.14
573
+
574
+
575
+ def answer_from_clauses(
576
+ question: str,
577
+ normativity_level=2,
578
+ allowed_articles: list[str] | None = None,
579
+ allowed_documents: list[str] | None = None,
580
+ allowed_scopes: list[tuple[str, str]] | None = None,
581
+ evidence_spans: list[dict[str, Any]] | None = None,
582
+ ) -> str:
583
+ profile = intent_profile(question)
584
+ profile.update(query_understanding(question))
585
+ profile["strict_legal_object"] = bool(allowed_scopes)
586
+ profile["pilot_query"] = analyze_pilot_query(question)
587
+ profile["source_route"] = route_question(question, DOCUMENTS_BY_ID, CROSS_DOCUMENT_EDGES)
588
+ if evidence_spans is None:
589
+ evidence_spans = retrieve_evidence_spans(
590
+ question,
591
+ allowed_articles=allowed_articles,
592
+ allowed_documents=allowed_documents,
593
+ allowed_scopes=allowed_scopes,
594
+ )
595
+ if not evidence_spans:
596
+ return ""
597
+ selected = _select_proof_evidence(evidence_spans, profile, question)
598
+ if not selected:
599
+ return ""
600
+ selected = enrich_selected_evidence(profile.get("pilot_query"), selected)
601
+ for item in selected:
602
+ document = DOCUMENTS_BY_ID.get(str(item.get("document_id", "")), {})
603
+ item["document_domain_tags"] = document.get("domain_tags", []) or []
604
+ proof = build_proof_bundle(question, evidence_spans, selected, CORPUS_BUILD_ID)
605
+ if proof.get("status") != "supported":
606
+ return ""
607
+
608
+ claims = []
609
+ if llm_available():
610
+ claims = _llm_claims_from_proof(proof, normativity_level)
611
+ answer = render_proof_answer(proof, claims or None)
612
+ if not answer:
613
+ return ""
614
+ return answer + "\n\n---\n\n**Kaynaklar**\n" + format_clause_sources(selected, question=question, answer=answer)
615
+
616
+
617
+ def evaluate_clause_query(
618
+ question: str,
619
+ allowed_articles: list[str] | None = None,
620
+ allowed_documents: list[str] | None = None,
621
+ allowed_scopes: list[tuple[str, str]] | None = None,
622
+ ) -> dict[str, Any]:
623
+ evidence_spans = retrieve_evidence_spans(
624
+ question,
625
+ allowed_articles=allowed_articles,
626
+ allowed_documents=allowed_documents,
627
+ allowed_scopes=allowed_scopes,
628
+ )
629
+ parent_clause_ids = []
630
+ for evidence in evidence_spans:
631
+ cid = evidence.get("parent_clause_id", "")
632
+ if cid and cid not in parent_clause_ids:
633
+ parent_clause_ids.append(cid)
634
+ profile = intent_profile(question)
635
+ profile.update(query_understanding(question))
636
+ profile["strict_legal_object"] = bool(allowed_scopes)
637
+ profile["source_route"] = route_question(question, DOCUMENTS_BY_ID, CROSS_DOCUMENT_EDGES)
638
+ selected = _select_proof_evidence(evidence_spans, profile, question)
639
+ proof = build_proof_bundle(question, evidence_spans, selected, CORPUS_BUILD_ID)
640
+ return {
641
+ "evidence_spans": evidence_spans,
642
+ "evidence_ids": [evidence.get("evidence_id", "") for evidence in evidence_spans],
643
+ "clauses": [CLAUSES_BY_ID.get(cid, {}) for cid in parent_clause_ids if cid in CLAUSES_BY_ID],
644
+ "clause_ids": parent_clause_ids,
645
+ "document_ids": [evidence.get("document_id", "") for evidence in evidence_spans],
646
+ "source_ids": [evidence.get("article_id", "") for evidence in evidence_spans],
647
+ "scores": [evidence.get("_score", 0.0) for evidence in evidence_spans],
648
+ "score_breakdowns": [evidence.get("_score_breakdown", {}) for evidence in evidence_spans],
649
+ "source_route": route_question(question, DOCUMENTS_BY_ID, CROSS_DOCUMENT_EDGES),
650
+ "selected_evidence_ids": [item.get("evidence_id", "") for item in selected],
651
+ "proof_bundle": proof,
652
+ }
653
+
654
+
655
+ def retrieval_runtime_status() -> dict[str, Any]:
656
+ if HYBRID_ENGINE is None:
657
+ return {"status": "not_initialized"}
658
+ return {"status": "ready", **HYBRID_ENGINE.status()}
659
+
660
+
661
+ def _explicit_document_ids(question: str) -> list[str]:
662
+ normalized = normalize_for_search(question)
663
+ found = []
664
+ for document_id, document in DOCUMENTS_BY_ID.items():
665
+ short_code = normalize_for_search(str(document.get("short_code", "") or ""))
666
+ title = normalize_for_search(str(document.get("title", "") or ""))
667
+ if short_code and re.search(rf"\b{re.escape(short_code)}\b", normalized):
668
+ found.append(document_id)
669
+ continue
670
+ meaningful_title = " ".join(term for term in title.split() if term not in {"sayili", "kanunu", "kanun"})
671
+ if meaningful_title and meaningful_title in normalized:
672
+ found.append(document_id)
673
+ return found
674
+
675
+
676
+ def _select_proof_evidence(
677
+ evidence_spans: list[dict[str, Any]],
678
+ profile: dict[str, Any],
679
+ question: str,
680
+ limit: int = 5,
681
+ ) -> list[dict[str, Any]]:
682
+ """Select a coherent evidence set without question-specific article patches."""
683
+ if not evidence_spans:
684
+ return []
685
+ substantive = [item for item in evidence_spans if not _non_substantive_source_fragment(item)]
686
+ if substantive:
687
+ evidence_spans = substantive
688
+ # Structural context is emitted in source order and may precede the span
689
+ # that actually won retrieval. Keep the retrieval-bearing span as the
690
+ # semantic anchor, then attach its source context below.
691
+ top = next(
692
+ (item for item in evidence_spans if not _structural_context_anchor(item)),
693
+ evidence_spans[0],
694
+ )
695
+ anchor = (top.get("document_id", ""), top.get("article_id", ""))
696
+ question_frame = extract_question_frame(question)
697
+ requested = {str(profile.get("norm_type", "") or "")}
698
+ requested.update(str(value) for value in question_frame.get("requested_categories", []) or [])
699
+ requested.discard("")
700
+ qualifier_roles = {"sart", "istisna", "sure", "tanim", "usul"}
701
+ selected = [top]
702
+ seen = {top.get("evidence_id", "")}
703
+ normalized_question = normalize_for_search(question)
704
+ multi_part = (
705
+ str(profile.get("norm_type", "") or "") in {"gorev", "amac", "ilke", "usul"}
706
+ or len(requested) >= 2
707
+ or any(
708
+ term in normalized_question
709
+ for term in (
710
+ "nelerdir", "gorevleri", "adimlari", "asamalari", "birlikte", "nasil",
711
+ "kapsam", "istisna", "kosul", "basvuru suresi",
712
+ )
713
+ )
714
+ )
715
+ interpretation = analyze_interpretive_intent(question)
716
+ enforce_legal_object = bool(interpretation and interpretation.get("enforce_fidelity", True))
717
+ requested_mechanism = (interpretation.get("requested_mechanism", {}) or {}) if interpretation else {}
718
+ same_parent_required = requested_mechanism.get("selection_scope") == "same_parent_clause"
719
+ anchor_parent = str(top.get("parent_clause_id", "") or "")
720
+
721
+ if requested_mechanism.get("mechanism_id") == "body_membership_and_composition":
722
+ composition = _select_body_composition_proof(evidence_spans, top)
723
+ if composition:
724
+ return composition
725
+
726
+ # A legal article can contain appointment, delegation and duty paragraphs
727
+ # together. When the question asks for duties, keep the selected evidence
728
+ # inside the duty sub-clause instead of mixing in nearby appointment spans.
729
+ if _is_duty_question(profile, normalized_question):
730
+ duty_evidence = _select_duty_section_evidence(evidence_spans, top)
731
+ if duty_evidence:
732
+ return duty_evidence
733
+
734
+ explicit_documents = _explicit_document_ids(question)
735
+ if len(explicit_documents) >= 2:
736
+ selected = []
737
+ seen = set()
738
+ for document_id in explicit_documents:
739
+ document_candidates = [
740
+ item for item in evidence_spans
741
+ if item.get("document_id") == document_id
742
+ ][:20]
743
+ candidate = max(
744
+ document_candidates,
745
+ key=lambda item: _comparative_scope_score(question, item),
746
+ default=None,
747
+ )
748
+ if candidate and candidate.get("evidence_id") not in seen:
749
+ selected.append(candidate)
750
+ seen.add(candidate.get("evidence_id"))
751
+ if selected:
752
+ anchor = (selected[0].get("document_id", ""), selected[0].get("article_id", ""))
753
+
754
+ for evidence in evidence_spans:
755
+ evidence_id = evidence.get("evidence_id", "")
756
+ if not evidence_id or evidence_id in seen:
757
+ continue
758
+ if (
759
+ same_parent_required
760
+ and anchor_parent
761
+ and str(evidence.get("parent_clause_id", "") or "") != anchor_parent
762
+ ):
763
+ continue
764
+ same_scope = (evidence.get("document_id", ""), evidence.get("article_id", "")) == anchor
765
+ roles = set(evidence.get("semantic_roles", []) or []) | {str(evidence.get("norm_type", "") or "")}
766
+ channel_details = (evidence.get("_score_breakdown", {}) or {}).get("channels", {}) or {}
767
+ graph_linked = "graph_expansion" in channel_details or "sparql_graph" in channel_details
768
+ relevant_role = bool(roles & requested) or bool(roles & qualifier_roles)
769
+ structural_continuation = (
770
+ _structural_context_anchor(evidence) == str(top.get("evidence_id", "") or "")
771
+ or _structural_context_anchor(top) == str(evidence_id)
772
+ or _source_fragments_continue(top, evidence)
773
+ or _source_fragments_continue(evidence, top)
774
+ )
775
+ object_aligned = (
776
+ not enforce_legal_object
777
+ or legal_object_retrieval_factor(question, evidence, analysis=interpretation) >= 1.0
778
+ or (structural_continuation and not profile.get("strict_legal_object"))
779
+ )
780
+ if same_scope and structural_continuation and object_aligned:
781
+ selected.append(evidence)
782
+ elif same_scope and multi_part and object_aligned and (relevant_role or int(evidence.get("_channel_consensus", 0) or 0) >= 3):
783
+ selected.append(evidence)
784
+ elif same_scope and object_aligned and roles & {"sart", "istisna"} and float(evidence.get("_score", 0.0) or 0.0) >= 17.0:
785
+ selected.append(evidence)
786
+ elif graph_linked and profile.get("source_route", {}).get("cross_document") and object_aligned:
787
+ selected.append(evidence)
788
+ if evidence_id in {item.get("evidence_id") for item in selected}:
789
+ seen.add(evidence_id)
790
+ if len(selected) >= limit:
791
+ break
792
+ if (multi_part or any(_structural_context_anchor(item) for item in selected)) and len(selected) > 1 and all(
793
+ (item.get("document_id", ""), item.get("article_id", "")) == anchor
794
+ for item in selected
795
+ ):
796
+ selected = _sort_evidence_by_span(_dedupe_evidence(selected))[:limit]
797
+ return selected
798
+
799
+
800
+ def _structural_context_anchor(evidence: dict[str, Any]) -> str:
801
+ return str((evidence.get("_score_breakdown", {}) or {}).get("structural_context_of", "") or "")
802
+
803
+
804
+ def _non_substantive_source_fragment(evidence: dict[str, Any]) -> bool:
805
+ """Drop amendment headers and leaked neighboring headings from proof evidence."""
806
+ text = normalize_for_search(str(evidence.get("source_text", "") or ""))
807
+ if not text:
808
+ return True
809
+ normative_markers = (
810
+ "yetkili", "kurma", "kurul", "kapat", "birles", "degistir", "aktar",
811
+ "atan", "secil", "oden", "zorun", "gorev", "hak", "uygulan", "basvur",
812
+ "yapil", "veril", "alin", "sorumlu", "yasak", "muaf", "tabidir",
813
+ )
814
+ has_normative_marker = any(marker in text for marker in normative_markers)
815
+ if re.match(r"^(?:ek |gecici )?madde\s+\w+\s+(?:ek|degisik|mulga|iptal)\b", text):
816
+ return not has_normative_marker
817
+ if len(text.split()) <= 6 and not has_normative_marker:
818
+ return True
819
+ return False
820
+
821
+
822
+ def _is_duty_question(profile: dict[str, Any], normalized_question: str) -> bool:
823
+ return (
824
+ str(profile.get("norm_type", "") or "") == "gorev"
825
+ or bool(
826
+ re.search(
827
+ r"\b(?:gorev(?:i|in|leri|lerinin)?|yetki(?:si|leri|lerinin)?|sorumluluk(?:lari|larinin)?)\b",
828
+ normalized_question,
829
+ )
830
+ )
831
+ )
832
+
833
+
834
+ def _select_body_composition_proof(
835
+ evidence_spans: list[dict[str, Any]],
836
+ anchor: dict[str, Any],
837
+ ) -> list[dict[str, Any]]:
838
+ """Select the central composition sentence of one named governing body."""
839
+ anchor_scope = (anchor.get("document_id", ""), anchor.get("article_id", ""))
840
+ scoped = [
841
+ item
842
+ for item in evidence_spans
843
+ if (item.get("document_id", ""), item.get("article_id", "")) == anchor_scope
844
+ ]
845
+ composition = [item for item in scoped if _looks_like_composition_evidence(item)]
846
+ if not composition:
847
+ return []
848
+ central = max(
849
+ composition,
850
+ key=lambda item: (
851
+ "toplam" in normalize_for_search(str(item.get("source_text", "") or "")),
852
+ "kisiden olusur" in normalize_for_search(str(item.get("source_text", "") or "")),
853
+ float(item.get("_score", 0.0) or 0.0),
854
+ ),
855
+ )
856
+ selected = [central]
857
+ for candidate in scoped:
858
+ if _source_fragments_continue(candidate, central):
859
+ selected.insert(0, candidate)
860
+ break
861
+ return _sort_evidence_by_span(_dedupe_evidence(selected))[:3]
862
+
863
+
864
+ def _select_duty_section_evidence(
865
+ evidence_spans: list[dict[str, Any]],
866
+ anchor: dict[str, Any],
867
+ ) -> list[dict[str, Any]]:
868
+ """Return substantive duty spans from the anchor sub-clause only."""
869
+ anchor_parent = str(anchor.get("parent_clause_id", "") or "")
870
+ anchor_scope = (anchor.get("document_id", ""), anchor.get("article_id", ""))
871
+ article_candidates = [
872
+ item for item in evidence_spans
873
+ if (item.get("document_id", ""), item.get("article_id", "")) == anchor_scope
874
+ ]
875
+ candidates = [
876
+ item for item in article_candidates
877
+ if not anchor_parent or str(item.get("parent_clause_id", "") or "") == anchor_parent
878
+ ]
879
+ duty = [
880
+ item for item in candidates
881
+ if "gorev" in (item.get("semantic_roles", []) or [])
882
+ and not _looks_like_non_duty_evidence(item)
883
+ and not _is_section_heading(item)
884
+ ]
885
+ if not duty and _is_lettered_duty_heading(anchor):
886
+ duty = _lettered_section_duty_spans(article_candidates, anchor)
887
+ if not duty:
888
+ return []
889
+ return _dedupe_evidence(_sort_evidence_by_span(duty))[:8]
890
+
891
+
892
+ def _is_section_heading(evidence: dict[str, Any]) -> bool:
893
+ text = normalize_for_search(str(evidence.get("source_text", "") or "")).strip()
894
+ return bool(re.fullmatch(r"[a-z0-9]+ gorev yetki ve sorumluluklari", text))
895
+
896
+
897
+ def _is_lettered_duty_heading(evidence: dict[str, Any]) -> bool:
898
+ label = str(evidence.get("label", "") or "").strip().lower()
899
+ return len(label) == 1 and label.isalpha() and _is_section_heading(evidence)
900
+
901
+
902
+ def _lettered_section_duty_spans(
903
+ article_candidates: list[dict[str, Any]],
904
+ heading: dict[str, Any],
905
+ ) -> list[dict[str, Any]]:
906
+ """Collect numbered spans between a lettered duty heading and the next heading."""
907
+ start = int((heading.get("source_span", {}) or {}).get("char_start", -1) or -1)
908
+ if start < 0:
909
+ return []
910
+ next_heading_starts = [
911
+ int((item.get("source_span", {}) or {}).get("char_start", -1) or -1)
912
+ for item in article_candidates
913
+ if _is_letter_label(item.get("label"))
914
+ and int((item.get("source_span", {}) or {}).get("char_start", -1) or -1) > start
915
+ ]
916
+ end = min(next_heading_starts) if next_heading_starts else float("inf")
917
+ spans = []
918
+ for item in article_candidates:
919
+ position = int((item.get("source_span", {}) or {}).get("char_start", -1) or -1)
920
+ if not (start < position < end):
921
+ continue
922
+ if not str(item.get("label", "") or "").strip().isdigit():
923
+ continue
924
+ if _looks_like_non_duty_evidence(item) or _is_section_heading(item):
925
+ continue
926
+ spans.append(item)
927
+ return spans
928
+
929
+
930
+ def _is_letter_label(value: Any) -> bool:
931
+ label = str(value or "").strip().lower()
932
+ return len(label) == 1 and label.isalpha()
933
+
934
+
935
+ def _comparative_scope_score(question: str, evidence: dict[str, Any]) -> float:
936
+ """Select a document's scope provision for source-primacy comparisons."""
937
+ normalized_question = normalize_for_search(question)
938
+ text = normalize_for_search(str(evidence.get("source_text", "") or ""))
939
+ title = normalize_for_search(str(evidence.get("article_title", "") or ""))
940
+ anchors = {
941
+ term for term in normalized_question.split()
942
+ if len(term) >= 4 and term not in {
943
+ "sayili", "kanun", "kanunu", "hangi", "daha", "dogrudan", "kaynak",
944
+ "kabul", "edilmelidir", "hakkinda", "bilgi", "verirken",
945
+ } and not term.isdigit()
946
+ }
947
+ overlap = sum(1.0 for term in anchors if term in text or term in title)
948
+ scope = 4.0 if title in {"kapsam", "amac", "konu ve kapsam"} else 0.0
949
+ scope += 3.0 if "bu kanun" in text and any(marker in text for marker in ("kapsar", "uygulanir", "tabi")) else 0.0
950
+ retrieval = min(2.0, float(evidence.get("_score", 0.0) or 0.0) / 10.0)
951
+ return scope + overlap + retrieval
952
+
953
+
954
+ def _llm_claims_from_proof(proof: dict[str, Any], normativity_level: int) -> list[dict[str, Any]]:
955
+ prompt = llm_claim_prompt(proof, response_style_instruction(normativity_level))
956
+ try:
957
+ raw = call_chat_completion(
958
+ [
959
+ {"role": "system", "content": SYSTEM_PROMPT},
960
+ {"role": "user", "content": prompt},
961
+ ]
962
+ )
963
+ except Exception:
964
+ return []
965
+ return parse_and_validate_llm_claims(raw, proof)
966
+
967
+
968
+ def evidence_spans_by_ids(evidence_ids: list[str]) -> list[dict[str, Any]]:
969
+ """Return exact corpus evidence for rule-backed multi-hop analysis."""
970
+ requested = set(evidence_ids or [])
971
+ if not requested:
972
+ return []
973
+ by_id = {
974
+ str(evidence.get("evidence_id", "")): evidence
975
+ for evidence in EVIDENCE_SPANS
976
+ if evidence.get("evidence_id")
977
+ }
978
+ return [dict(by_id[evidence_id]) for evidence_id in evidence_ids if evidence_id in by_id]
979
+
980
+
981
+ def evidence_spans_matching(selectors: list[dict[str, Any]]) -> list[dict[str, Any]]:
982
+ """Resolve stable semantic selectors to exact corpus evidence spans."""
983
+ selected: list[dict[str, Any]] = []
984
+ seen: set[str] = set()
985
+ for selector in selectors or []:
986
+ required_terms = [normalize_for_search(str(term)) for term in selector.get("contains_all", [])]
987
+ for evidence in EVIDENCE_SPANS:
988
+ if selector.get("document_id") and evidence.get("document_id") != selector["document_id"]:
989
+ continue
990
+ if selector.get("article_id") and evidence.get("article_id") != selector["article_id"]:
991
+ continue
992
+ if selector.get("label") and str(evidence.get("label", "")) != str(selector["label"]):
993
+ continue
994
+ normalized_text = normalize_for_search(str(evidence.get("source_text", "") or ""))
995
+ if required_terms and not all(term in normalized_text for term in required_terms):
996
+ continue
997
+ evidence_id = str(evidence.get("evidence_id", "") or "")
998
+ if evidence_id and evidence_id not in seen:
999
+ selected.append(dict(evidence))
1000
+ seen.add(evidence_id)
1001
+ break
1002
+ return selected
1003
+
1004
+
1005
+ def _confident_enough(evidence_spans: list[dict[str, Any]]) -> bool:
1006
+ if not evidence_spans:
1007
+ return False
1008
+ best = float(evidence_spans[0].get("_score", 0.0))
1009
+ if best < MIN_EVIDENCE_SCORE:
1010
+ return False
1011
+ if len(evidence_spans) >= 2:
1012
+ second = float(evidence_spans[1].get("_score", 0.0))
1013
+ top_breakdown = evidence_spans[0].get("_score_breakdown", {}) or {}
1014
+ kg_alignment = float(top_breakdown.get("kg_alignment", 0.0) or 0.0)
1015
+ lexical = float(top_breakdown.get("lexical", 0.0) or 0.0)
1016
+ entity_scope = float(top_breakdown.get("entity_scope", 0.0) or 0.0)
1017
+ article_hint = float(top_breakdown.get("article_hint_score", 0.0) or 0.0)
1018
+ if kg_alignment < 0.36 and lexical < 0.55 and entity_scope < 0.82 and article_hint <= 0:
1019
+ return False
1020
+ if best >= 9.25 and (
1021
+ entity_scope >= 0.82
1022
+ or lexical >= 0.55
1023
+ or kg_alignment >= 0.58
1024
+ ):
1025
+ return True
1026
+ # If the evidence is weak and flat, do not force a wrong answer.
1027
+ if best < 10.0 and (best - second) < MIN_SCORE_GAP:
1028
+ return False
1029
+ return True
1030
+
1031
+
1032
+ def _select_answer_evidence(evidence_spans: list[dict[str, Any]], profile: dict[str, Any], question: str = "") -> list[dict[str, Any]]:
1033
+ norm_type = profile.get("norm_type", "")
1034
+ q = normalize_for_search(question)
1035
+ source_route = profile.get("source_route", {}) or {}
1036
+
1037
+ if source_route.get("cross_document"):
1038
+ selected = _select_cross_document_evidence(evidence_spans, source_route)
1039
+ if selected:
1040
+ return selected
1041
+
1042
+ if profile.get("route") == "law_purpose":
1043
+ return _article_evidence("Madde 1")[:1] + _article_evidence("Madde 4")
1044
+
1045
+ if profile.get("route") == "institute_establishment":
1046
+ return _select_institute_establishment_evidence()
1047
+
1048
+ if profile.get("route") == "research_application_center_establishment":
1049
+ return _select_research_center_evidence()
1050
+
1051
+ if profile.get("interpretive_intent"):
1052
+ anchor_article = _anchor_article_for_single_topic_query(evidence_spans, profile)
1053
+ if not anchor_article:
1054
+ anchor_article = evidence_spans[0].get("article_id", "")
1055
+ scoped = [e for e in evidence_spans if e.get("article_id", "") == anchor_article]
1056
+ focused = _interpretive_evidence(scoped or evidence_spans, profile)
1057
+ return _dedupe_evidence(_sort_evidence_by_span(focused or scoped or evidence_spans))[:5]
1058
+
1059
+ if norm_type == "gorev":
1060
+ top_article = evidence_spans[0].get("article_id", "")
1061
+ duty = [
1062
+ e for e in evidence_spans
1063
+ if e.get("article_id") == top_article
1064
+ and "gorev" in (e.get("semantic_roles", []) or [])
1065
+ and not _looks_like_non_duty_evidence(e)
1066
+ and str(e.get("label", "")) not in {"preamble", "b"}
1067
+ ]
1068
+ return _dedupe_evidence(_sort_evidence_by_span(duty or evidence_spans))[:8]
1069
+
1070
+ if norm_type == "tanim":
1071
+ selected = []
1072
+ # Definition questions may ask both meaning and operational right, e.g. "ön lisans diploması nedir".
1073
+ for evidence in evidence_spans:
1074
+ frame = evidence.get("semantic_frame", {}) or {}
1075
+ text = normalize_for_search(evidence.get("source_text", ""))
1076
+ if evidence.get("norm_type") == "tanim" or frame.get("definition_term"):
1077
+ selected.append(evidence)
1078
+ break
1079
+ if "diploma" in q:
1080
+ for evidence in evidence_spans:
1081
+ text = normalize_for_search(evidence.get("source_text", ""))
1082
+ if "diploma" in text and any(term in text for term in ("alir", "alır", "almak")) and evidence not in selected:
1083
+ selected.append(evidence)
1084
+ break
1085
+ return _dedupe_evidence(selected or evidence_spans[:2])[:3]
1086
+
1087
+ if norm_type == "kurul_olusumu":
1088
+ # Composition questions are single-entity questions by default.
1089
+ # Once the highest-ranked evidence establishes the governed institution/article,
1090
+ # do not mix in composition spans from other articles merely because they also
1091
+ # contain phrases such as "toplam ... kişiden oluşur". This is a general
1092
+ # evidence-scoping rule, not a per-question patch.
1093
+ anchor_article = _anchor_article_for_single_entity_query(evidence_spans, profile)
1094
+ scoped = [e for e in evidence_spans if not anchor_article or e.get("article_id") == anchor_article]
1095
+ composition = [e for e in scoped if _looks_like_composition_evidence(e)]
1096
+ # A composition/count question normally asks for the composition of one named body.
1097
+ # Return the best evidence span only; otherwise the answer can mix sub-bodies
1098
+ # such as a council and its executive board.
1099
+ return _dedupe_evidence(composition or scoped or evidence_spans)[:1]
1100
+
1101
+ if norm_type == "kurulus":
1102
+ exact_establishment = [e for e in evidence_spans if _is_direct_establishment_evidence(e)]
1103
+ if exact_establishment:
1104
+ return _dedupe_evidence(exact_establishment)[:2]
1105
+ same_topic = [e for e in evidence_spans if e.get("norm_type") == norm_type or norm_type in (e.get("semantic_roles", []) or [])]
1106
+ return _dedupe_evidence(same_topic or evidence_spans)[:3]
1107
+
1108
+ if norm_type in {"ilke", "amac"}:
1109
+ top_article = evidence_spans[0].get("article_id", "")
1110
+ scoped = _article_evidence(top_article) or [e for e in evidence_spans if e.get("article_id", "") == top_article]
1111
+ return _dedupe_evidence(_sort_evidence_by_span(scoped or evidence_spans))[:14]
1112
+
1113
+ if norm_type in {"atama", "sure", "sart", "istisna", "yaptirim"}:
1114
+ anchor_article = _anchor_article_for_single_topic_query(evidence_spans, profile)
1115
+ scoped = [e for e in evidence_spans if not anchor_article or e.get("article_id") == anchor_article]
1116
+ same_topic = [e for e in scoped if e.get("norm_type") == norm_type or norm_type in (e.get("semantic_roles", []) or [])]
1117
+ competitive = _competitive_evidence(same_topic or scoped or evidence_spans, max_gap=0.75)
1118
+ if norm_type == "atama" and not profile.get("requires_condition"):
1119
+ return _dedupe_evidence(competitive)[:1]
1120
+ return _dedupe_evidence(competitive)[:2]
1121
+
1122
+ top_article = evidence_spans[0].get("article_id", "")
1123
+ scoped = [e for e in evidence_spans if e.get("article_id", "") == top_article]
1124
+ return _dedupe_evidence(scoped or evidence_spans)[:2]
1125
+
1126
+
1127
+ def _llm_answer_from_selected_evidence(question: str, evidence_spans: list[dict[str, Any]], normativity_level=2) -> str:
1128
+ context_blocks = []
1129
+ document_titles = _selected_document_titles(evidence_spans)
1130
+ for idx, evidence in enumerate(evidence_spans, start=1):
1131
+ ref = _evidence_ref(evidence)
1132
+ text = _clean_text(evidence.get("source_text", ""))
1133
+ frame = evidence.get("semantic_frame", {}) or {}
1134
+ frame_hint = _frame_hint(frame)
1135
+ context_blocks.append(f"[Dayanak {idx}: {ref}]\n{frame_hint}\nMetin: {text}")
1136
+ style_instruction = response_style_instruction(normativity_level)
1137
+ profile = intent_profile(question)
1138
+ profile.update(query_understanding(question))
1139
+ intent_hint = _intent_hint(profile)
1140
+ prompt = f"""Kullanıcı sorusu:\n{question}\n\nSeçilmiş belge kapsamı:\n{document_titles}\n\nSeçilmiş en küçük kanıt parçaları:\n{chr(10).join(context_blocks)}\n\nCevap stili:\n{style_instruction}\n\nGörev:\nTürkçe cevap ver.\n- Kullanıcı niyeti: {intent_hint}\n- Yalnızca seçilmiş belge(ler)den seçilmiş kanıt parçalarına dayan.\n- Kanıt metnini aynen kopyalama; maddeye sadık kalarak normatif ve anlaşılır biçimde sentezle.\n- Kanıt doğrudan cevap vermiyorsa mevcut MCKF corpus içinde açık hüküm bulunmadığını söyle.\n- Soru yorum, rol, anlam veya sonuç soruyorsa açık dayanaklardan makul hukuki anlam çıkar; dayanağı olmayan gerekçe veya dış bilgi ekleme.\n- Kaynakta olmayan kişi, gerekçe, istisna veya yorum uydurma.\n- Cevabın başında veya sonunda kaynak listesi yazma; kaynaklar arayüzde ayrıca gösterilecek.\n- Soru tek bir konu soruyorsa sadece o konuya cevap ver.\n- Cevap öz olsun ama soru uzun açıklama gerektiriyorsa yeterli açıklamayı ver.\n- Cevabı kesmeden tamamla, fakat gereksiz tekrar yapma.\n"""
1141
+ try:
1142
+ raw = call_chat_completion([
1143
+ {"role": "system", "content": SYSTEM_PROMPT},
1144
+ {"role": "user", "content": prompt},
1145
+ ])
1146
+ return clean_answer_text(raw, question).strip()
1147
+ except Exception:
1148
+ return ""
1149
+
1150
+
1151
+ def _render_evidence_answer(
1152
+ evidence_spans: list[dict[str, Any]],
1153
+ norm_type: str,
1154
+ normativity_level=2,
1155
+ profile: dict[str, Any] | None = None,
1156
+ question: str = "",
1157
+ ) -> str:
1158
+ profile = profile or {}
1159
+ if profile.get("route") == "law_purpose":
1160
+ return _render_law_purpose_answer()
1161
+ if profile.get("route") in {"foundational_principles", "foundational_purpose"} or norm_type in {"ilke", "amac"}:
1162
+ return _render_foundational_answer(evidence_spans, norm_type)
1163
+ if profile.get("route") == "institute_establishment":
1164
+ return _render_institute_establishment_answer(question)
1165
+ if profile.get("route") == "research_application_center_establishment":
1166
+ return _render_research_center_establishment_answer()
1167
+
1168
+ refs = ", ".join(_unique_refs(_evidence_ref(e) for e in evidence_spans))
1169
+ lead = f"Dayanak: {refs}."
1170
+ if len(evidence_spans) == 1:
1171
+ return f"{lead}\n\n{_clean_text(evidence_spans[0].get('source_text', ''))}"
1172
+ bullets = "\n".join(f"- {_clean_text(e.get('source_text', ''))}" for e in evidence_spans)
1173
+ return f"{lead}\n\n{bullets}"
1174
+
1175
+
1176
+ def _unique_refs(refs) -> list[str]:
1177
+ seen = set()
1178
+ out = []
1179
+ for ref in refs:
1180
+ if ref in seen:
1181
+ continue
1182
+ seen.add(ref)
1183
+ out.append(ref)
1184
+ return out
1185
+
1186
+
1187
+ def _selected_document_titles(evidence_spans: list[dict[str, Any]]) -> str:
1188
+ titles = []
1189
+ for evidence in evidence_spans:
1190
+ title = evidence.get("document_title") or evidence.get("document_id", "")
1191
+ if title and title not in titles:
1192
+ titles.append(title)
1193
+ return ", ".join(titles) if titles else "seçilmiş MCKF belgeleri"
1194
+
1195
+
1196
+
1197
+ def _select_institute_establishment_evidence() -> list[dict[str, Any]]:
1198
+ selected = []
1199
+ for evidence in _article_evidence("Madde 5"):
1200
+ text = normalize_for_search(evidence.get("source_text", ""))
1201
+ if "enstitu" in text and "kanunla kurulur" in text:
1202
+ selected.append(evidence)
1203
+ for evidence in _article_evidence("Madde 7"):
1204
+ text = normalize_for_search(evidence.get("source_text", ""))
1205
+ if "enstitu" in text and "acilmasi" in text:
1206
+ selected.append(evidence)
1207
+ elif "uygulama ve arastirma merkezi" in text and "acilmasi" in text:
1208
+ selected.append(evidence)
1209
+ return _dedupe_evidence(_sort_evidence_by_span(selected))[:4]
1210
+
1211
+
1212
+ def _select_research_center_evidence() -> list[dict[str, Any]]:
1213
+ selected = []
1214
+ for evidence in _article_evidence("Madde 7"):
1215
+ text = normalize_for_search(evidence.get("source_text", ""))
1216
+ if "uygulama ve arastirma merkezi" in text and "acilmasi" in text:
1217
+ selected.append(evidence)
1218
+ return _dedupe_evidence(_sort_evidence_by_span(selected))[:2]
1219
+
1220
+
1221
+ def _select_cross_document_evidence(evidence_spans: list[dict[str, Any]], source_route: dict[str, Any]) -> list[dict[str, Any]]:
1222
+ candidates = set(source_route.get("candidate_document_ids", []) or [])
1223
+ selected: list[dict[str, Any]] = []
1224
+ for edge in source_route.get("candidate_edges", []) or []:
1225
+ for document_id, article_id in (
1226
+ (edge.get("source_document_id", ""), edge.get("source_article_id", "")),
1227
+ (edge.get("target_document_id", ""), edge.get("target_article_id", "")),
1228
+ ):
1229
+ for evidence in evidence_spans:
1230
+ if evidence.get("document_id") == document_id and evidence.get("article_id") == article_id:
1231
+ selected.append(evidence)
1232
+ break
1233
+ for doc_id in candidates:
1234
+ if any(e.get("document_id") == doc_id for e in selected):
1235
+ continue
1236
+ for evidence in evidence_spans:
1237
+ if evidence.get("document_id") == doc_id:
1238
+ selected.append(evidence)
1239
+ break
1240
+ return _dedupe_evidence(selected)[:5]
1241
+
1242
+
1243
+ def _render_foundational_answer(evidence_spans: list[dict[str, Any]], norm_type: str) -> str:
1244
+ article = evidence_spans[0].get("article_id", "") if evidence_spans else ""
1245
+ if article == "Madde 5" or norm_type == "ilke":
1246
+ return _render_principles_answer()
1247
+ if article == "Madde 4" or norm_type == "amac":
1248
+ return _render_higher_education_purpose_answer()
1249
+ return _render_plain_evidence_answer(evidence_spans)
1250
+
1251
+
1252
+ def _render_law_purpose_answer() -> str:
1253
+ return (
1254
+ "Dayanak: Madde 1, Madde 4.\n\n"
1255
+ "2547 sayılı Yükseköğretim Kanununun temel amacı iki düzeyde anlaşılır:\n\n"
1256
+ "1. Kanunun düzenleme amacı: Madde 1'e göre kanun, yükseköğretimle ilgili amaç ve ilkeleri belirlemek; bütün yükseköğretim kurumları ile üst kuruluşlarının teşkilatlanmasını, işleyişini, görev, yetki ve sorumluluklarını ve eğitim-öğretim, araştırma, yayım, öğretim elemanları, öğrenciler ve diğer personelle ilgili esasları bir bütünlük içinde düzenlemek için çıkarılmıştır.\n"
1257
+ "2. Yükseköğretimin amaç yönü: Madde 4'e göre yükseköğretim; öğrencilerin belirli yurttaşlık, kültür, bilimsel düşünce ve mesleki yeterliklerle yetişmesini, ülkenin ekonomik, sosyal ve kültürel kalkınmasına katkı sağlayacak programların uygulanmasını ve yükseköğretim kurumlarının bilimsel çalışma, araştırma, bilgi ve teknoloji üretimiyle ulusal ve evrensel gelişmeye katkı vermesini hedefler.\n\n"
1258
+ "Kısaca, kanun yalnızca üniversite idaresini düzenleyen teknik bir metin değildir; yükseköğretimin amaçlarını, ilkelerini, kurumlarını, yetkilerini ve insan unsurunu tek bir normatif çerçevede toplar."
1259
+ )
1260
+
1261
+
1262
+ def _render_higher_education_purpose_answer() -> str:
1263
+ return (
1264
+ "Dayanak: Madde 4.\n\n"
1265
+ "2547 sayılı Kanunda yükseköğretimin amacı üç ana eksende düzenlenir:\n\n"
1266
+ "1. Öğrenci yetiştirme amacı: Öğrencilerin Atatürk inkılapları ve ilkeleri doğrultusunda, milli ve kültürel değerlere sahip, toplum yararını gözeten, insan haklarına saygılı, hür ve bilimsel düşünce gücü olan, dengeli gelişmiş ve mesleki bilgi-beceri kazanmış yurttaşlar olarak yetiştirilmesi hedeflenir.\n"
1267
+ "2. Toplumsal ve kalkınmacı amaç: Türk Devletinin ülkesi ve milletiyle bölünmez bütünlüğü içinde refah ve mutluluğunu artıracak; ekonomik, sosyal ve kültürel kalkınmaya katkı sağlayacak programların uygulanması amaçlanır.\n"
1268
+ "3. Bilimsel ve kurumsal amaç: Yükseköğretim kurumlarının bilimsel çalışma ve araştırma yapması, bilgi ve teknoloji üretmesi, bilim verilerini yayması, ulusal kalkınmaya destek olması, yurt içi ve yurt dışı kurumlarla işbirliği yapması ve evrensel gelişmeye katkıda bulunması öngörülür."
1269
+ )
1270
+
1271
+
1272
+ def _render_principles_answer() -> str:
1273
+ return (
1274
+ "Dayanak: Madde 5.\n\n"
1275
+ "2547 sayılı Kanunda yükseköğretimin ana ilkeleri Madde 5'te düzenlenir. Bu ilkeler özetle şunlardır:\n\n"
1276
+ "1. Atatürk inkılapları ve ilkeleri doğrultusunda hizmet bilinci kazandırılması.\n"
1277
+ "2. Milli kültürün, örf ve adetlerin evrensel kültür içinde korunup geliştirilmesi; milli birlik ve beraberliği güçlendiren ruh ve iradenin kazandırılması.\n"
1278
+ "3. Yükseköğretim kurumlarının özellikleri, eğitim-öğretim dalları ve amaçları gözetilerek eğitim-öğretimde birlik ilkesinin sağlanması.\n"
1279
+ "4. Eğitim-öğretim plan ve programlarının bilimsel ve teknolojik esaslara, ülke ve yöre ihtiyaçlarına göre hazırlanıp sürekli geliştirilmesi.\n"
1280
+ "5. Yükseköğretimde imkan ve fırsat eşitliğini sağlayacak önlemlerin alınması.\n"
1281
+ "6. Üniversite, yüksek teknoloji enstitüsü ve bağlı fakülte, enstitü ve yüksekokulların yükseköğretim planlaması çerçevesinde kanunla kurulması.\n"
1282
+ "7. Yükseköğretim kurumlarının geliştirilmesi, yaygınlaştırılması, kaynak ve insan gücü dağılımının milli eğitim politikası ve kalkınma planları doğrultusunda planlanması.\n\n"
1283
+ "Bu nedenle Madde 5, yükseköğretimin yalnız akademik değil; kültürel, planlamacı, eşitlikçi ve kalkınmacı çerçevesini de belirler."
1284
+ )
1285
+
1286
+
1287
+ def _render_institute_establishment_answer(question: str = "") -> str:
1288
+ q = normalize_for_search(question)
1289
+ qualifier = "Araştırma enstitüsü ifadesi 2547 içinde ayrı ve tek başına bir kuruluş usulü olarak değil, enstitü ve araştırma/uygulama merkezi hükümleriyle birlikte okunmalıdır."
1290
+ if "arastirma" not in q:
1291
+ qualifier = "Enstitü kuruluşu 2547 içinde hem kanunla kuruluş hem de üniversite içinde açılma süreci bakımından düzenlenir."
1292
+ return (
1293
+ "Dayanak: Madde 5/f, Madde 7/2.\n\n"
1294
+ f"{qualifier}\n\n"
1295
+ "1. Üniversiteler, yüksek teknoloji enstitüleri ve bunların içindeki fakülte, enstitü ve yüksekokullar, Cumhurbaşkanınca yapılan yükseköğretim planlaması çerçevesinde kanunla kurulur.\n"
1296
+ "2. Bir üniversite içinde enstitü açılması, birleştirilmesi veya kapatılması konusunda Yükseköğretim Kurulu doğrudan ya da üniversiteden gelen öneriye dayanarak karar alır ve gereği için Milli Eğitim Bakanlığına sunar.\n"
1297
+ "3. Kast edilen birim 'uygulama ve araştırma merkezi' ise, bunların açılması, birleştirilmesi veya kapatılması da Madde 7/2 kapsamında Yükseköğretim Kurulunun karar sürecine bağlanmıştır."
1298
+ )
1299
+
1300
+
1301
+ def _render_research_center_establishment_answer() -> str:
1302
+ return (
1303
+ "Dayanak: Madde 7/2.\n\n"
1304
+ "Uygulama ve araştırma merkezlerinin açılması, birleştirilmesi veya kapatılması konusunda Yükseköğretim Kurulu doğrudan ya da üniversiteden gelen öneri üzerine karar verir. Bu nedenle 2547 bakımından araştırma merkezi kuruluşu, YÖK karar süreciyle ilişkilidir."
1305
+ )
1306
+
1307
+
1308
+ def _render_plain_evidence_answer(evidence_spans: list[dict[str, Any]]) -> str:
1309
+ refs = ", ".join(_unique_refs(_evidence_ref(e) for e in evidence_spans))
1310
+ bullets = "\n".join(f"- {_clean_text(e.get('source_text', ''))}" for e in evidence_spans)
1311
+ return f"Dayanak: {refs}.\n\n{bullets}"
1312
+
1313
+
1314
+ def _numbered_evidence_lines(evidence_spans: list[dict[str, Any]], skip_labels: set[str] | None = None) -> str:
1315
+ skip_labels = skip_labels or set()
1316
+ lines = []
1317
+ seen = set()
1318
+ for evidence in evidence_spans:
1319
+ label = str(evidence.get("label", "") or "")
1320
+ if label in skip_labels:
1321
+ continue
1322
+ text = _clean_foundational_text(evidence.get("source_text", ""))
1323
+ if not text:
1324
+ continue
1325
+ key = normalize_for_search(text)
1326
+ if key in seen:
1327
+ continue
1328
+ seen.add(key)
1329
+ lines.append(f"{len(lines) + 1}. {text}")
1330
+ return "\n".join(lines)
1331
+
1332
+
1333
+ def _clean_foundational_text(text: str) -> str:
1334
+ cleaned = _clean_text(text)
1335
+ cleaned = re.split(
1336
+ r"\b(?:ÜÇÜNCÜ|UCUNCU)\s+BÖLÜM\b|\bÜst Kuruluşlar\b|\bYükseköğretim Kurulu:\b",
1337
+ cleaned,
1338
+ maxsplit=1,
1339
+ flags=re.IGNORECASE,
1340
+ )[0].strip()
1341
+ return cleaned
1342
+
1343
+
1344
+ def _interpretive_evidence(evidence_spans: list[dict[str, Any]], profile: dict[str, Any]) -> list[dict[str, Any]]:
1345
+ preferred_roles = {"gorev", "yetki", "sart", "istisna", "yaptirim", "tanim", "kurulus", "atama"}
1346
+ if profile.get("practical_effect_intent"):
1347
+ preferred_roles.update({"sart", "istisna", "yaptirim"})
1348
+ selected = []
1349
+ for evidence in evidence_spans:
1350
+ roles = set(evidence.get("semantic_roles", []) or [])
1351
+ if evidence.get("norm_type") in preferred_roles or roles & preferred_roles:
1352
+ selected.append(evidence)
1353
+ return selected or evidence_spans
1354
+
1355
+
1356
+ def _article_evidence(article_id: str, document_id: str = "TR-KANUN-2547") -> list[dict[str, Any]]:
1357
+ if not article_id:
1358
+ return []
1359
+ return [
1360
+ dict(evidence)
1361
+ for evidence in EVIDENCE_SPANS
1362
+ if evidence.get("article_id", "") == article_id
1363
+ and (not document_id or evidence.get("document_id", "") == document_id)
1364
+ ]
1365
+
1366
+
1367
+ def _intent_hint(profile: dict[str, Any]) -> str:
1368
+ parts = []
1369
+ norm_type = profile.get("norm_type")
1370
+ actors = profile.get("actors") or []
1371
+ if norm_type:
1372
+ parts.append(f"norm_tipi={norm_type}")
1373
+ if actors:
1374
+ parts.append("aktor=" + ", ".join(actors))
1375
+ if profile.get("interpretive_intent"):
1376
+ parts.append("kaynakli_yorum")
1377
+ if profile.get("practical_effect_intent"):
1378
+ parts.append("pratik_sonuc")
1379
+ return "; ".join(parts) if parts else "dar kaynakli cevap"
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+ def _competitive_evidence(evidence_spans: list[dict[str, Any]], max_gap: float = 0.75) -> list[dict[str, Any]]:
1386
+ """Keep only evidence spans that are genuinely competitive with the best span.
1387
+
1388
+ This is used for narrow factual questions such as appointment, duration, condition
1389
+ and sanction queries. It prevents low-relevance clauses with the same broad norm
1390
+ label from being added to the final answer.
1391
+ """
1392
+ if not evidence_spans:
1393
+ return []
1394
+ best = float(evidence_spans[0].get("_score", 0.0))
1395
+ return [e for e in evidence_spans if best - float(e.get("_score", 0.0)) <= max_gap]
1396
+
1397
+
1398
+ def _anchor_article_for_single_topic_query(evidence_spans: list[dict[str, Any]], profile: dict[str, Any]) -> str:
1399
+ """Choose a dominant article when the question expresses one clear topic.
1400
+
1401
+ This prevents answer composition from mixing unrelated provisions that share the
1402
+ same generic role label such as condition, duration or sanction. The anchor is
1403
+ based on the top evidence score plus lexical/norm-role support; it is not tied
1404
+ to a specific article number.
1405
+ """
1406
+ if not evidence_spans:
1407
+ return ""
1408
+ top = evidence_spans[0]
1409
+ top_article = top.get("article_id", "")
1410
+ if not top_article:
1411
+ return ""
1412
+ breakdown = top.get("_score_breakdown", {}) or {}
1413
+ lexical = float(breakdown.get("lexical", 0.0))
1414
+ norm = float(breakdown.get("norm_type", 0.0))
1415
+ entity = float(breakdown.get("entity_scope", 0.0))
1416
+ score = float(top.get("_score", 0.0))
1417
+ if score >= 10.0 and (lexical >= 0.25 or norm >= 0.72 or entity >= 0.5):
1418
+ return top_article
1419
+ return ""
1420
+
1421
+
1422
+ def _anchor_article_for_single_entity_query(evidence_spans: list[dict[str, Any]], profile: dict[str, Any]) -> str:
1423
+ """Choose a dominant article for queries about one governed entity.
1424
+
1425
+ Legal retrieval frequently finds many structurally similar spans across the law
1426
+ (for example, several boards "consist of" a number of members). For a query
1427
+ that names one entity, answer selection must stay inside the article that best
1428
+ matches that entity unless the question type explicitly requires multiple legal
1429
+ bases.
1430
+ """
1431
+ if not evidence_spans:
1432
+ return ""
1433
+ if len(profile.get("actors") or []) != 1:
1434
+ return ""
1435
+ top = evidence_spans[0]
1436
+ top_article = top.get("article_id", "")
1437
+ if not top_article:
1438
+ return ""
1439
+ # Anchor only when the top evidence is reasonably strong and entity-scoped.
1440
+ breakdown = top.get("_score_breakdown", {}) or {}
1441
+ if float(top.get("_score", 0.0)) >= 10.0 and float(breakdown.get("entity_scope", 0.0)) >= 0.5:
1442
+ return top_article
1443
+ return ""
1444
+
1445
+
1446
+ def _entity_scope_score(profile: dict[str, Any], evidence: dict[str, Any], concept: dict[str, Any], frame: dict[str, Any]) -> float:
1447
+ """Prefer evidence whose parent article/concept is about the entity asked.
1448
+
1449
+ This is a core MCKF disambiguation rule, not a question patch: a legal text may mention
1450
+ many entities inside one provision, but the article title and concept aliases usually
1451
+ identify the governed entity of that provision. Queries about an institution or role
1452
+ should therefore prefer evidence scoped to that entity over evidence merely mentioning it.
1453
+ """
1454
+ actors = profile.get("actors") or []
1455
+ if not actors:
1456
+ return 0.5
1457
+
1458
+ title_alias_text = normalize_for_search(" ".join([
1459
+ concept.get("title", ""),
1460
+ " ".join(concept.get("aliases", []) or []),
1461
+ " ".join(concept.get("question_patterns", []) or []),
1462
+ ]))
1463
+ evidence_text = normalize_for_search(evidence.get("source_text", ""))
1464
+ frame_actors = {normalize_for_search(str(actor)) for actor in frame.get("actor", []) or []}
1465
+
1466
+ best = _concept_title_match_score(profile, concept)
1467
+ for actor in actors:
1468
+ actor_norm = normalize_for_search(str(actor))
1469
+ if not actor_norm:
1470
+ continue
1471
+ if actor_norm in title_alias_text:
1472
+ best = max(best, 1.0)
1473
+ elif _distinctive_title_terms_match_actor(title_alias_text, actor_norm):
1474
+ best = max(best, 0.9)
1475
+ elif evidence_text.startswith(actor_norm) or f"{actor_norm}," in evidence_text[:120] or f"{actor_norm} " in evidence_text[:80]:
1476
+ best = max(best, 0.82)
1477
+ elif actor_norm in frame_actors:
1478
+ best = max(best, 0.42)
1479
+ elif actor_norm in evidence_text:
1480
+ best = max(best, 0.28)
1481
+ return best
1482
+
1483
+
1484
+ def _distinctive_title_terms_match_actor(title_alias_text: str, actor_norm: str) -> bool:
1485
+ generic_terms = {"kurul", "kurulu", "kurulunun", "yonetim", "gorevleri", "madde"}
1486
+ terms = [
1487
+ term for term in title_alias_text.split()
1488
+ if len(term) >= 5 and term not in generic_terms and not term.isdigit()
1489
+ ]
1490
+ if not terms:
1491
+ return False
1492
+ if len(terms) == 1:
1493
+ return terms[0] in actor_norm
1494
+ return all(term in actor_norm for term in terms)
1495
+
1496
+
1497
+ def _concept_title_match_score(profile: dict[str, Any], concept: dict[str, Any]) -> float:
1498
+ q = profile.get("normalized_question", "")
1499
+ title = normalize_for_search(concept.get("title", ""))
1500
+ aliases = [normalize_for_search(alias) for alias in (concept.get("aliases", []) or [])]
1501
+ candidates = [candidate for candidate in [title, *aliases] if candidate and not candidate.startswith("madde ")]
1502
+ if not q or not candidates:
1503
+ return 0.0
1504
+
1505
+ meaningful_q_terms = set(q.split())
1506
+ best = 0.0
1507
+ for candidate in candidates:
1508
+ candidate_terms = [term for term in candidate.split() if len(term) >= 4 and term not in {"madde"}]
1509
+ if not candidate_terms:
1510
+ continue
1511
+ if candidate in q and len(candidate_terms) >= 2:
1512
+ best = max(best, min(1.22, 1.0 + 0.05 * len(candidate_terms)))
1513
+ elif all(term in meaningful_q_terms for term in candidate_terms):
1514
+ best = max(best, 0.92)
1515
+ elif len(candidate_terms) == 1 and candidate_terms[0] in meaningful_q_terms:
1516
+ best = max(best, 0.78)
1517
+ return best
1518
+
1519
+
1520
+ def _norm_type_score(norm_type: str, evidence: dict[str, Any], roles: set[str], parent_clause: dict[str, Any], concept: dict[str, Any]) -> float:
1521
+ concept_title = normalize_for_search(concept.get("title", ""))
1522
+ if norm_type and norm_type in concept_title:
1523
+ return 1.2
1524
+ if norm_type and (norm_type == evidence.get("norm_type") or norm_type in roles):
1525
+ return 1.0
1526
+ if norm_type and (norm_type == parent_clause.get("norm_type") or norm_type in (parent_clause.get("semantic_roles", []) or [])):
1527
+ return 0.72
1528
+ if norm_type and norm_type in (concept.get("semantic_roles", []) or []):
1529
+ return 0.55
1530
+ if not norm_type:
1531
+ return 0.5
1532
+ return 0.0
1533
+
1534
+
1535
+ def _lexical_score(terms: set[str], evidence_text: str, concept_text: str, article_id: str, label: str) -> float:
1536
+ if not terms:
1537
+ return 0.45
1538
+ hits = 0.0
1539
+ for term in terms:
1540
+ if term in evidence_text:
1541
+ hits += 1.0
1542
+ elif term in concept_text:
1543
+ hits += 0.65
1544
+ elif term in article_id or term == label:
1545
+ hits += 0.35
1546
+ return min(1.0, hits / max(1.0, min(len(terms), 7)))
1547
+
1548
+
1549
+ def _structure_score(profile: dict[str, Any], evidence: dict[str, Any]) -> float:
1550
+ q = profile.get("normalized_question", "")
1551
+ label = normalize_for_search(str(evidence.get("label", "")))
1552
+ article_id = normalize_for_search(evidence.get("article_id", ""))
1553
+ if q and article_id in q:
1554
+ return 1.0
1555
+ if label not in {"article", "preamble", ""}:
1556
+ return 0.68
1557
+ return 0.35
1558
+
1559
+
1560
+ def _evidence_focus_score(profile: dict[str, Any], evidence_text: str, frame: dict[str, Any]) -> float:
1561
+ q = profile.get("normalized_question", "")
1562
+ score = 0.0
1563
+ if profile.get("requires_condition") and frame.get("condition"):
1564
+ score += 0.35
1565
+ if profile.get("requires_exception") and frame.get("exception"):
1566
+ score += 0.35
1567
+ if any(term in q for term in ("kac", "kaç", "sure", "suresi", "ne kadar")) and frame.get("temporal_constraint"):
1568
+ score += 0.30
1569
+ if "diploma" in q and any(term in q for term in ("almak", "alma", "gerekir")):
1570
+ if "diploma" in evidence_text and any(term in evidence_text for term in ("alir", "alır", "basari ile tamam", "başarı ile tamam", "tamamlamalari", "tamamlamaları")):
1571
+ score += 0.45
1572
+
1573
+ # Penalize huge generic spans; v0.6 prefers exact evidence units.
1574
+ length = len(evidence_text)
1575
+ if length <= 420:
1576
+ score += 0.30
1577
+ elif length <= 700:
1578
+ score += 0.15
1579
+ return min(score, 1.0)
1580
+
1581
+
1582
+ def _generic_penalty(profile: dict[str, Any], evidence: dict[str, Any], frame: dict[str, Any]) -> float:
1583
+ penalty = 0.0
1584
+ norm_type = profile.get("norm_type", "")
1585
+ if norm_type and evidence.get("norm_type") and norm_type != evidence.get("norm_type"):
1586
+ # Do not punish a span whose primary label differs if the requested role is
1587
+ # explicitly present among its semantic roles. This avoids false negatives
1588
+ # in complex clauses that encode, for example, both condition and composition.
1589
+ if norm_type not in (evidence.get("semantic_roles", []) or []):
1590
+ penalty += 0.08 if norm_type == "tanim" else 0.13
1591
+ if profile.get("requires_condition") and not frame.get("condition"):
1592
+ penalty += 0.18
1593
+ if profile.get("requires_exception") and not frame.get("exception"):
1594
+ penalty += 0.18
1595
+ article_id = evidence.get("article_id", "")
1596
+ q = profile.get("normalized_question", "")
1597
+ if article_id.startswith("Geçici Madde") and not profile.get("allows_temporary_article"):
1598
+ penalty += 0.22
1599
+ if article_id.startswith("Ek Madde") and not any(term in q for term in ("ek madde", "vakif", "vakıf", "ozel", "özel", "ilave")):
1600
+ penalty += 0.16
1601
+ evidence_text = normalize_for_search(evidence.get("source_text", ""))
1602
+ if "diploma" in q and any(term in q for term in ("almak", "alma", "gerekir")) and "mezun olamayan" in evidence_text:
1603
+ penalty += 0.18
1604
+ if len(evidence.get("source_text", "")) > 900:
1605
+ penalty += 0.10
1606
+ return penalty
1607
+
1608
+
1609
+ def _concept_mismatch_penalty(q_frame: dict[str, Any], evidence_text: str, concept_text: str) -> float:
1610
+ text = f"{evidence_text} {concept_text}"
1611
+ penalty = 0.0
1612
+ for concept in q_frame.get("related_concept", []) or []:
1613
+ concept_norm = normalize_for_search(str(concept))
1614
+ terms = [term for term in concept_norm.split() if len(term) >= 3]
1615
+ if len(terms) < 2:
1616
+ continue
1617
+ hits = sum(1 for term in terms if term in text)
1618
+ if hits == 0:
1619
+ penalty += 0.20
1620
+ elif hits < len(terms):
1621
+ penalty += 0.22
1622
+ return min(penalty, 0.32)
1623
+
1624
+
1625
+ def _source_router_score(evidence: dict[str, Any], source_route: dict[str, Any]) -> float:
1626
+ document_id = evidence.get("document_id", "")
1627
+ scores = source_route.get("document_scores", {}) or {}
1628
+ top_score = max(scores.values() or [0.0])
1629
+ if not document_id or top_score <= 0:
1630
+ return 0.5
1631
+ return min(1.0, float(scores.get(document_id, 0.0)) / max(0.01, float(top_score)))
1632
+
1633
+
1634
+ def _document_domain_score(evidence: dict[str, Any], source_route: dict[str, Any]) -> float:
1635
+ document_id = evidence.get("document_id", "")
1636
+ candidates = set(source_route.get("candidate_document_ids", []) or [])
1637
+ if not candidates:
1638
+ return 0.5
1639
+ if document_id in candidates:
1640
+ return 1.0
1641
+ return 0.0
1642
+
1643
+
1644
+ def _article_hint_score(evidence: dict[str, Any], source_route: dict[str, Any]) -> float:
1645
+ document_id = evidence.get("document_id", "")
1646
+ targets = (source_route.get("target_articles_by_document", {}) or {}).get(document_id) or []
1647
+ if not targets:
1648
+ return 0.0
1649
+ return 1.0 if _matches_article_hint(evidence.get("article_id", ""), targets) else 0.0
1650
+
1651
+
1652
+ def _wrong_document_penalty(evidence: dict[str, Any], source_route: dict[str, Any]) -> float:
1653
+ candidates = set(source_route.get("candidate_document_ids", []) or [])
1654
+ if not candidates or float(source_route.get("top_score", 0.0) or 0.0) < 0.3:
1655
+ return 0.0
1656
+ document_id = evidence.get("document_id", "")
1657
+ if document_id in candidates:
1658
+ return 0.0
1659
+ if _is_cross_document_edge_article(document_id, evidence.get("article_id", ""), source_route):
1660
+ return 0.0
1661
+ return 0.22
1662
+
1663
+
1664
+ def _cross_document_bonus(evidence: dict[str, Any], source_route: dict[str, Any]) -> float:
1665
+ if not source_route.get("cross_document"):
1666
+ return 0.0
1667
+ if _is_cross_document_edge_article(evidence.get("document_id", ""), evidence.get("article_id", ""), source_route):
1668
+ return 0.08
1669
+ if evidence.get("document_id", "") in set(source_route.get("candidate_document_ids", []) or []):
1670
+ return 0.03
1671
+ return 0.0
1672
+
1673
+
1674
+ def _is_cross_document_edge_article(document_id: str, article_id: str, source_route: dict[str, Any]) -> bool:
1675
+ if not document_id or not article_id:
1676
+ return False
1677
+ for edge in source_route.get("candidate_edges", []) or []:
1678
+ if document_id == edge.get("source_document_id") and article_id == edge.get("source_article_id"):
1679
+ return True
1680
+ if document_id == edge.get("target_document_id") and article_id == edge.get("target_article_id"):
1681
+ return True
1682
+ return False
1683
+
1684
+
1685
+ def _matches_article_hint(article_id: str, target_articles: list[str]) -> bool:
1686
+ article_norm = normalize_for_search(article_id)
1687
+ for target in target_articles:
1688
+ target_norm = normalize_for_search(target)
1689
+ if article_norm == target_norm:
1690
+ return True
1691
+ if target_norm.startswith("ek madde "):
1692
+ continue
1693
+ if target_norm and not target_norm.startswith("madde ") and article_norm.startswith(target_norm):
1694
+ return True
1695
+ return False
1696
+
1697
+
1698
+ def _is_direct_establishment_evidence(evidence: dict[str, Any]) -> bool:
1699
+ text = normalize_for_search(evidence.get("source_text", ""))
1700
+ return any(term in text for term in (
1701
+ "kanunla kurulur", "karari ile kurulur", "kararı ile kurulur",
1702
+ "cumhurbaskani karari ile kurulur", "cumhurbaşkanı kararı ile kurulur",
1703
+ ))
1704
+
1705
+
1706
+ def _looks_like_non_duty_evidence(evidence: dict[str, Any]) -> bool:
1707
+ text = normalize_for_search(evidence.get("source_text", ""))
1708
+ return any(term in text for term in (
1709
+ "atanir", "atanabilir", "atanmasi", "secilir", "vekalet", "yardimci",
1710
+ "goreve vekalet", "göreve vekalet", "sure ile secilir",
1711
+ ))
1712
+
1713
+
1714
+ def _looks_like_composition_evidence(evidence: dict[str, Any]) -> bool:
1715
+ text = normalize_for_search(evidence.get("source_text", ""))
1716
+ return any(term in text for term in ("toplam", "kisiden olusur", "uyeden olusur", "yedi", "yirmi bir"))
1717
+
1718
+
1719
+ def _sort_evidence_by_span(evidence_spans: list[dict[str, Any]]) -> list[dict[str, Any]]:
1720
+ return sorted(evidence_spans, key=lambda e: (e.get("source_span", {}).get("char_start", 0), -float(e.get("_score", 0.0))))
1721
+
1722
+
1723
+ def _dedupe_evidence(evidence_spans: list[dict[str, Any]]) -> list[dict[str, Any]]:
1724
+ seen = set()
1725
+ result = []
1726
+ for evidence in evidence_spans:
1727
+ key = (evidence.get("evidence_id") or normalize_for_search(evidence.get("source_text", ""))[:160])
1728
+ if key in seen:
1729
+ continue
1730
+ seen.add(key)
1731
+ result.append(evidence)
1732
+ return result
1733
+
1734
+
1735
+ def _evidence_ref(evidence: dict[str, Any]) -> str:
1736
+ label = str(evidence.get("label", "") or "").strip()
1737
+ article = evidence.get("article_id", "")
1738
+ document_title = evidence.get("document_title") or evidence.get("document_id", "")
1739
+ prefix = f"{document_title} — " if document_title else ""
1740
+ if label and label not in {"article", "preamble"}:
1741
+ return f"{prefix}{article}/{label}"
1742
+ return f"{prefix}{article}"
1743
+
1744
+
1745
+ def _clean_text(text: str) -> str:
1746
+ return " ".join(str(text or "").split())
1747
+
1748
+
1749
+ def _frame_hint(frame: dict[str, Any]) -> str:
1750
+ parts = []
1751
+ for key in ("actor", "action", "object", "condition", "exception", "temporal_constraint", "modality"):
1752
+ value = frame.get(key)
1753
+ if value:
1754
+ parts.append(f"{key}: {value}")
1755
+ return "Semantik çerçeve: " + "; ".join(parts) if parts else "Semantik çerçeve: yok"
1756
+
1757
+
1758
+ def _flatten_evidence_spans(clauses: list[dict[str, Any]]) -> list[dict[str, Any]]:
1759
+ flattened = []
1760
+ for clause in clauses:
1761
+ flattened.extend(clause.get("evidence_spans", []) or [])
1762
+ return flattened
1763
+
1764
+
1765
+ def _explicit_article_references(question: str) -> list[str]:
1766
+ refs: list[str] = []
1767
+ for match in re.finditer(r"\bEk\s+Madde\s+(\d+)\b", question or "", flags=re.IGNORECASE):
1768
+ refs.append(f"Ek Madde {match.group(1)}")
1769
+ for match in re.finditer(
1770
+ r"\bGe[cç]ici\s+Madde\s+(\d+(?:/[A-Za-zÇĞİÖŞÜçğıöşü])?)\b",
1771
+ question or "",
1772
+ flags=re.IGNORECASE,
1773
+ ):
1774
+ ref = f"Geçici Madde {match.group(1).upper()}"
1775
+ if ref not in refs:
1776
+ refs.append(ref)
1777
+ for match in re.finditer(r"\bMadde\s+(\d+(?:/[A-Za-zÇĞİÖŞÜçğıöşü])?)\b", question or "", flags=re.IGNORECASE):
1778
+ prefix = (question or "")[max(0, match.start() - 8):match.start()].strip().lower()
1779
+ if prefix in {"ek", "geçici", "gecici"}:
1780
+ continue
1781
+ ref = f"Madde {match.group(1).upper()}"
1782
+ if ref not in refs:
1783
+ refs.append(ref)
1784
+ return refs
config.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+
4
+ APP_TITLE = "MitrAnlil Yükseköğretim Mevzuatı AI Asistanı"
5
+ APP_VERSION = "MCKF v1.0 Proof-First Normative Intelligence"
6
+ OFFICIAL_SOURCE_URL = (
7
+ "https://www.mevzuat.gov.tr/"
8
+ )
9
+
10
+ ROOT_DIR = Path(__file__).resolve().parent
11
+ DATA_DIR = Path(os.getenv("MITRANLIL_DATA_DIR", str(ROOT_DIR / "data"))).resolve()
12
+ SOURCE_DIR = DATA_DIR / "source"
13
+ NORMALIZED_DIR = DATA_DIR / "normalized"
14
+ STRUCTURAL_DIR = DATA_DIR / "structural"
15
+ MCKF_DIR = DATA_DIR / "mckf"
16
+ FEEDBACK_DIR = DATA_DIR / "feedback"
17
+ ANALYTICS_DIR = DATA_DIR / "analytics"
18
+ ADMIN_DIR = DATA_DIR / "admin"
19
+ TEST_DATA_DIR = DATA_DIR / "tests"
20
+ REGISTRY_DIR = DATA_DIR / "registry"
21
+ ONBOARDING_DIR = DATA_DIR / "onboarding"
22
+ INSTITUTION_REGISTRY_PATH = ONBOARDING_DIR / "institutions.json"
23
+ SUBMISSIONS_DIR = ONBOARDING_DIR / "submissions"
24
+ CANDIDATE_BUILDS_DIR = ONBOARDING_DIR / "builds"
25
+ PUBLISH_BACKUPS_DIR = ONBOARDING_DIR / "backups"
26
+
27
+ SOURCE_TEXT_PATH = SOURCE_DIR / "2547_clean.txt"
28
+ LEGAL_DOCUMENT_REGISTRY_PATH = REGISTRY_DIR / "legal_documents.json"
29
+ MCKF_ONTOLOGY_PATH = MCKF_DIR / "2547_mckf_ontology.json"
30
+ MCKF_SEMANTIC_FRAMES_PATH = MCKF_DIR / "2547_semantic_frames.jsonl"
31
+ MCKF_RETRIEVAL_INDEX_PATH = MCKF_DIR / "2547_retrieval_index.json"
32
+ CORPUS_MCKF_ONTOLOGY_PATH = MCKF_DIR / "corpus_mckf_ontology.json"
33
+ CORPUS_SEMANTIC_FRAMES_PATH = MCKF_DIR / "corpus_semantic_frames.jsonl"
34
+ CORPUS_RETRIEVAL_INDEX_PATH = MCKF_DIR / "corpus_retrieval_index.json"
35
+ CROSS_DOCUMENT_EDGES_PATH = MCKF_DIR / "cross_document_edges.jsonl"
36
+ RDFOX_PILOT_MANIFEST_PATH = MCKF_DIR / "rdfox_pilot_manifest.json"
37
+ RDFOX_PILOT_TRIPLES_PATH = MCKF_DIR / "rdfox_pilot_triples.nt"
38
+ RDFOX_PILOT_RULES_PATH = MCKF_DIR / "rdfox_pilot_rules.dlog"
39
+ MCKF_SHAPES_PATH = MCKF_DIR / "mckf_shapes.ttl"
40
+ MCKF_VALIDATION_REPORT_PATH = MCKF_DIR / "validation_report.json"
41
+ RUNTIME_BUILD_MANIFEST_PATH = MCKF_DIR / "runtime_build_manifest.json"
42
+ FEEDBACK_LOG_PATH = Path(os.getenv("FEEDBACK_LOG_PATH", str(FEEDBACK_DIR / "dialogue_feedback.jsonl")))
43
+ USAGE_LOG_PATH = Path(os.getenv("USAGE_LOG_PATH", str(ANALYTICS_DIR / "usage_events.jsonl")))
44
+ CLARIFICATION_LOG_PATH = Path(os.getenv("CLARIFICATION_LOG_PATH", str(ANALYTICS_DIR / "clarification_events.jsonl")))
45
+ ADMIN_EXAMPLES_PATH = Path(os.getenv("ADMIN_EXAMPLES_PATH", str(ADMIN_DIR / "example_questions.json")))
46
+
47
+ LLM_PROVIDER = os.getenv("LLM_PROVIDER", "auto").strip().lower()
48
+ HF_MODEL = os.getenv("HF_MODEL") or os.getenv("GEN_MODEL", "Qwen/Qwen2.5-7B-Instruct")
49
+ HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACEHUB_API_TOKEN")
50
+
51
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
52
+ # NOT: llama-3.3-70b-versatile, Groq tarafından 16 Ağustos 2026'da tamamen
53
+ # kapatılıyor (bkz. https://console.groq.com/docs/deprecations). Groq'un
54
+ # resmi önerisi openai/gpt-oss-120b veya qwen/qwen3.6-27b. gpt-oss-120b'yi
55
+ # seçtik: daha büyük parametre sayısı + reasoning modu, katı JSON claim
56
+ # şemamız (proof_bundle.py) için daha güvenilir talimat takibi sağlıyor,
57
+ # ayrıca Groq'ta token başına daha ucuz ve daha hızlı (bkz. sohbet
58
+ # geçmişindeki fiyat/hız karşılaştırması). Free tier'da TPM sınırı
59
+ # llama-3.3-70b-versatile'a göre daha düşük (8K vs 12K) — demo öncesi
60
+ # Groq konsolünden hesap tier'ını ve gerçek istek hacmini test et.
61
+ GROQ_MODEL = os.getenv("GROQ_MODEL", "openai/gpt-oss-120b")
62
+
63
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
64
+ OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
65
+
66
+ # NOT: proof_bundle.py'deki synthesis claim mekanizması ve llm_claim_prompt
67
+ # içindeki few-shot örnekler çıktı uzunluğunu artırdı; 1000 tokenlık eski
68
+ # sınır, çok maddeli sentez cevaplarında JSON'un ortadan kesilip parse
69
+ # hatası vermesine yol açabilirdi. 1500'e çıkarıldı — bu, girdi promptunun
70
+ # değil yalnızca modelin ÜRETTİĞİ çıktının üst sınırıdır.
71
+ MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", "1500"))
72
+ MAX_CONTEXT_CHARS = int(os.getenv("MAX_CONTEXT_CHARS", "3000"))
73
+ CHATBOT_HEIGHT = int(os.getenv("CHATBOT_HEIGHT", "820"))
74
+ EVAL_CONTEXT_MAX_CHARS = int(os.getenv("EVAL_CONTEXT_MAX_CHARS", "6000"))
conversation_intent.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from utils import normalize_for_search
6
+
7
+
8
+ class ConversationIntent:
9
+ EMPTY = "empty"
10
+ GREETING = "greeting"
11
+ HELP = "help"
12
+ THANKS = "thanks"
13
+ OUT_OF_SCOPE = "out_of_scope"
14
+ LEGAL_QUERY = "legal_query"
15
+
16
+
17
+ GREETING_ANSWER = (
18
+ "Merhaba. Ben MitrAnlil Yükseköğretim Mevzuatı AI Asistanıyım. "
19
+ "2547, 2914 ve 2809 sayılı kanunlara dayalı olarak kaynak gösterimli cevaplar üretirim. "
20
+ "YÖK, üniversiteler, akademik personel, öğrenciler, teşkilat, ödenekler, görevler, yetkiler ve ilgili hükümler hakkında soru sorabilirsiniz."
21
+ )
22
+
23
+ HELP_ANSWER = (
24
+ "Ben yükseköğretim mevzuatı için geliştirilmiş kaynak gösterimli bir mevzuat asistanıyım. "
25
+ "Sorunuzu kanundaki madde, alt hüküm ve kanıt parçalarıyla eşleştirerek cevaplamaya çalışırım. "
26
+ "Örneğin dekanın görevleri, YÖK'ün oluşumu, ek ders ücreti, üniversite ödeneği, teşkilat yapısı veya kurumların kuruluşu hakkında soru sorabilirsiniz."
27
+ )
28
+
29
+ THANKS_ANSWER = "Rica ederim. Yükseköğretim mevzuatı ile ilgili başka bir soru sorabilirsiniz."
30
+
31
+ OUT_OF_SCOPE_ANSWER = (
32
+ "Bu pilot yükseköğretim mevzuatı kapsamında kaynak gösterimli cevap üretmek üzere tasarlanmıştır. "
33
+ "YÖK, üniversiteler, akademik personel, öğrenciler, diplomalar, teşkilat, ödenekler, görevler ve yetkiler gibi konularda soru sorabilirsiniz."
34
+ )
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class ConversationIntentResult:
39
+ intent: str
40
+ answer: str = ""
41
+
42
+
43
+ LEGAL_CUE_TERMS = {
44
+ "egitim", "uzaktan egitim", "yonetmelik", "yonerge", "tez", "savunma",
45
+ "tez savunmasi", "basvuru", "devam", "akademik takvim",
46
+ "2547", "2914", "2809", "kanun", "madde", "yok", "yök", "yuksekogretim", "yükseköğretim",
47
+ "universite", "üniversite", "rektor", "rektör", "dekan", "senato", "fakulte", "fakülte",
48
+ "enstitu", "enstitü", "yuksekokul", "yüksekokul", "meslek", "onlisans", "önlisans",
49
+ "on lisans", "ön lisans", "lisans", "diploma", "ogrenci", "öğrenci", "ogretim", "öğretim",
50
+ "profesor", "profesör", "docent", "doçent", "arastirma", "araştırma", "gorev", "görev",
51
+ "yetki", "sorumluluk", "atan", "atama", "kurul", "kurulus", "kuruluş", "disiplin",
52
+ "harc", "harç", "katki", "katkı", "ucret", "ücret", "denklik", "azami", "sure", "süre",
53
+ "ek ders", "ödenek", "odenek", "derece", "kademe", "teşkilat", "teskilat",
54
+ "faaliyete", "ek gösterge", "ek gosterge", "makam tazminatı", "makam tazminati",
55
+ }
56
+
57
+ OUT_OF_SCOPE_CUE_TERMS = {
58
+ "hava", "yemek", "tarif", "siir", "şiir", "hikaye", "futbol", "borsa", "kripto",
59
+ "film", "muzik", "müzik", "resim", "kod yaz", "mail yaz", "cevir", "çevir",
60
+ }
61
+
62
+
63
+ def detect_conversation_intent(message: str) -> ConversationIntentResult:
64
+ raw = (message or "").strip()
65
+ normalized = normalize_for_search(raw)
66
+ compact = normalized.replace(" ", "")
67
+
68
+ if not normalized:
69
+ return ConversationIntentResult(ConversationIntent.EMPTY, "Lütfen yükseköğretim mevzuatı ile ilgili bir soru yazın.")
70
+
71
+ if _is_greeting(normalized, compact):
72
+ return ConversationIntentResult(ConversationIntent.GREETING, GREETING_ANSWER)
73
+
74
+ if _is_thanks(normalized):
75
+ return ConversationIntentResult(ConversationIntent.THANKS, THANKS_ANSWER)
76
+
77
+ if _is_help_request(normalized):
78
+ return ConversationIntentResult(ConversationIntent.HELP, HELP_ANSWER)
79
+
80
+ if _has_legal_cue(normalized):
81
+ return ConversationIntentResult(ConversationIntent.LEGAL_QUERY, "")
82
+
83
+ if _has_out_of_scope_cue(normalized):
84
+ return ConversationIntentResult(ConversationIntent.OUT_OF_SCOPE, OUT_OF_SCOPE_ANSWER)
85
+
86
+ # Questions without legal cues should still reach the MCKF engine when they look
87
+ # like normative questions. This avoids blocking short queries such as "kim atar?"
88
+ # in follow-up-like usage while keeping pure greetings outside retrieval.
89
+ if any(term in normalized for term in ("nedir", "nasil", "nasıl", "kim", "hangi", "ne zaman", "kac", "kaç", "gerekir", "olur")):
90
+ return ConversationIntentResult(ConversationIntent.LEGAL_QUERY, "")
91
+
92
+ return ConversationIntentResult(ConversationIntent.OUT_OF_SCOPE, OUT_OF_SCOPE_ANSWER)
93
+
94
+
95
+ def _is_greeting(normalized: str, compact: str) -> bool:
96
+ greetings = {
97
+ "merhaba", "selam", "selamlar", "gunaydin", "günaydın", "iyi gunler", "iyi günler",
98
+ "iyi aksamlar", "iyi akşamlar", "iyi geceler", "kolay gelsin", "mrb", "slm",
99
+ }
100
+ if normalized in greetings or compact in {g.replace(" ", "") for g in greetings}:
101
+ return True
102
+ if len(normalized.split()) <= 3 and any(g in normalized for g in greetings):
103
+ return True
104
+ return False
105
+
106
+
107
+ def _is_thanks(normalized: str) -> bool:
108
+ return len(normalized.split()) <= 6 and any(term in normalized for term in ("tesekkur", "teşekkür", "sag ol", "sağ ol", "eyvallah", "tşk"))
109
+
110
+
111
+ def _is_help_request(normalized: str) -> bool:
112
+ return any(phrase in normalized for phrase in (
113
+ "ne yapabilirsin", "neler yapabilirsin", "nasil calisiyorsun", "nasıl çalışıyorsun",
114
+ "bu sistem nedir", "yardim", "yardım", "hangi sorular", "ne sorabilirim",
115
+ ))
116
+
117
+
118
+ def _has_legal_cue(normalized: str) -> bool:
119
+ return any(term in normalized for term in LEGAL_CUE_TERMS)
120
+
121
+
122
+ def _has_out_of_scope_cue(normalized: str) -> bool:
123
+ return any(term in normalized for term in OUT_OF_SCOPE_CUE_TERMS)
data/mckf/2547_retrieval_index.json ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2547_semantic_frames.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2809_retrieval_index.json ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2809_semantic_frames.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2914_mckf_ontology.json ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2914_retrieval_index.json ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/2914_semantic_frames.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/corpus_retrieval_index.json ADDED
The diff for this file is too large to render. See raw diff
 
data/mckf/cross_document_edges.jsonl ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"edge_id": "2914_m1_refs_2547_teacher_definition", "source_document_id": "TR-KANUN-2914", "source_article_id": "Madde 1", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 3", "relation_type": "references_definition", "description": "2914, kapsamını 2547 sayılı Kanundaki öğretim elemanları tanımına bağlar."}
2
+ {"edge_id": "2914_m11_details_2547_m36_ders_yuku", "source_document_id": "TR-KANUN-2914", "source_article_id": "Madde 11", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 36", "relation_type": "details_personnel_payment", "description": "2914 Madde 11 ek ders ücretini 2547 Madde 36 ders yükü bağlamıyla ilişkilendirir."}
3
+ {"edge_id": "2809_m3_refs_2547_m7_unit_regulation", "source_document_id": "TR-KANUN-2809", "source_article_id": "Madde 3", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 7", "relation_type": "organizational_detail_regulated_by", "description": "2809 Madde 3, alt birimlerin teşkili ve işleyişinde 2547 Madde 7 uyarınca YÖK düzenlemesine atıf yapar."}
4
+ {"edge_id": "tr_kanun_2914_madde_11_refs_2547_36", "source_document_id": "TR-KANUN-2914", "source_article_id": "Madde 11", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 36", "relation_type": "references_2547_article", "description": "2914 sayılı Yükseköğretim Personel Kanunu Madde 11, 2547 Madde 36 hükmüne atıf yapar."}
5
+ {"edge_id": "tr_kanun_2914_madde_16_refs_2547_34", "source_document_id": "TR-KANUN-2914", "source_article_id": "Madde 16", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 34", "relation_type": "references_2547_article", "description": "2914 sayılı Yükseköğretim Personel Kanunu Madde 16, 2547 Madde 34 hükmüne atıf yapar."}
6
+ {"edge_id": "tr_kanun_2914_ek_madde_1_refs_2547_33", "source_document_id": "TR-KANUN-2914", "source_article_id": "Ek Madde 1", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 33", "relation_type": "references_2547_article", "description": "2914 sayılı Yükseköğretim Personel Kanunu Ek Madde 1, 2547 Madde 33 hükmüne atıf yapar."}
7
+ {"edge_id": "tr_kanun_2914_geçici_madde_7_refs_2547_23", "source_document_id": "TR-KANUN-2914", "source_article_id": "Geçici Madde 7", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 23", "relation_type": "references_2547_article", "description": "2914 sayılı Yükseköğretim Personel Kanunu Geçici Madde 7, 2547 Madde 23 hükmüne atıf yapar."}
8
+ {"edge_id": "tr_kanun_2914_geçici_madde_9_refs_2547_5", "source_document_id": "TR-KANUN-2914", "source_article_id": "Geçici Madde 9", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 5", "relation_type": "references_2547_article", "description": "2914 sayılı Yükseköğretim Personel Kanunu Geçici Madde 9, 2547 Madde 5 hükmüne atıf yapar."}
9
+ {"edge_id": "tr_kanun_2809_madde_3_refs_2547_7", "source_document_id": "TR-KANUN-2809", "source_article_id": "Madde 3", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 7", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Madde 3, 2547 Madde 7 hükmüne atıf yapar."}
10
+ {"edge_id": "tr_kanun_2809_ek_madde_158_refs_2547_4", "source_document_id": "TR-KANUN-2809", "source_article_id": "Ek Madde 158", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 4", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Ek Madde 158, 2547 Madde 4 hükmüne atıf yapar."}
11
+ {"edge_id": "tr_kanun_2809_ek_madde_158_refs_2547_3", "source_document_id": "TR-KANUN-2809", "source_article_id": "Ek Madde 158", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 3", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Ek Madde 158, 2547 Madde 3 hükmüne atıf yapar."}
12
+ {"edge_id": "tr_kanun_2809_geçici_madde_1_refs_2547_3", "source_document_id": "TR-KANUN-2809", "source_article_id": "Geçici Madde 1", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 3", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Geçici Madde 1, 2547 Madde 3 hükmüne atıf yapar."}
13
+ {"edge_id": "tr_kanun_2809_geçici_madde_3_refs_2547_32", "source_document_id": "TR-KANUN-2809", "source_article_id": "Geçici Madde 3", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 32", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Geçici Madde 3, 2547 Madde 32 hükmüne atıf yapar."}
14
+ {"edge_id": "tr_kanun_2809_geçici_madde_3_refs_2547_16", "source_document_id": "TR-KANUN-2809", "source_article_id": "Geçici Madde 3", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 16", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Geçici Madde 3, 2547 Madde 16 hükmüne atıf yapar."}
15
+ {"edge_id": "tr_kanun_2809_geçici_madde_9_refs_2547_41", "source_document_id": "TR-KANUN-2809", "source_article_id": "Geçici Madde 9", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 41", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Geçici Madde 9, 2547 Madde 41 hükmüne atıf yapar."}
16
+ {"edge_id": "tr_kanun_2809_geçici_madde_51_refs_2547_50", "source_document_id": "TR-KANUN-2809", "source_article_id": "Geçici Madde 51", "target_document_id": "TR-KANUN-2547", "target_article_id": "Madde 50", "relation_type": "references_2547_article", "description": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu Geçici Madde 51, 2547 Madde 50 hükmüne atıf yapar."}
data/mckf/mckf_shapes.ttl ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @prefix sh: <http://www.w3.org/ns/shacl#> .
2
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
3
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4
+ @prefix mckf: <https://mitranlil.ai/mckf/> .
5
+
6
+ mckf:DocumentShape
7
+ a sh:NodeShape ;
8
+ sh:targetClass mckf:Document ;
9
+ sh:property [ sh:path mckf:documentId ; sh:minCount 1 ; sh:maxCount 1 ] ;
10
+ sh:property [ sh:path mckf:title ; sh:minCount 1 ] .
11
+
12
+ mckf:ConceptShape
13
+ a sh:NodeShape ;
14
+ sh:targetSubjectsOf mckf:conceptType ;
15
+ sh:property [ sh:path mckf:document ; sh:minCount 1 ; sh:nodeKind sh:IRI ] ;
16
+ sh:property [ sh:path mckf:articleId ; sh:minCount 1 ] ;
17
+ sh:property [ sh:path mckf:sourceLock ; sh:hasValue true ] ;
18
+ sh:property [ sh:path mckf:citationRequired ; sh:hasValue true ] .
19
+
20
+ mckf:NormativeUnitShape
21
+ a sh:NodeShape ;
22
+ sh:targetSubjectsOf mckf:unitType ;
23
+ sh:property [ sh:path mckf:evidenceId ; sh:minCount 1 ; sh:maxCount 1 ] ;
24
+ sh:property [ sh:path mckf:document ; sh:minCount 1 ; sh:nodeKind sh:IRI ] ;
25
+ sh:property [ sh:path mckf:articleId ; sh:minCount 1 ] ;
26
+ sh:property [ sh:path mckf:parentClause ; sh:minCount 1 ; sh:nodeKind sh:IRI ] ;
27
+ sh:property [ sh:path mckf:sourceText ; sh:minCount 1 ] .
data/mckf/rdfox_pilot_manifest.json ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "MCKF-SemanticGraphRuntime-v1.0",
3
+ "status": "runtime_graph_and_shacl_enabled",
4
+ "goal": "find_the_right_provision_show_the_right_condition_answer_with_sources",
5
+ "mvp_core": [
6
+ "document_ingestion",
7
+ "semantic_normative_unit_parser",
8
+ "mckf_builder",
9
+ "hybrid_retrieval",
10
+ "evidence_sufficiency_gate",
11
+ "source_locked_generation",
12
+ "citation_renderer"
13
+ ],
14
+ "post_pilot": [
15
+ "full_rule_engine",
16
+ "wide_graph_traversal",
17
+ "exception_precedence_inference",
18
+ "strict_rdf_compliance",
19
+ "ontology_layer"
20
+ ],
21
+ "runtime_contract": {
22
+ "source_lock": true,
23
+ "citation_required": true,
24
+ "external_rdfox_required": false,
25
+ "runtime_graph_backend": "RDFLib SPARQL with RDFox-compatible exports",
26
+ "shacl_validation_required": true,
27
+ "rdfox_export_paths": [
28
+ "data/mckf/rdfox_pilot_manifest.json",
29
+ "data/mckf/rdfox_pilot_triples.nt",
30
+ "data/mckf/rdfox_pilot_rules.dlog"
31
+ ]
32
+ },
33
+ "data_contract": {
34
+ "MVPConcept": {
35
+ "source": "data/mckf/corpus_mckf_ontology.json::concepts",
36
+ "fields": [
37
+ "concept_id",
38
+ "document_id",
39
+ "article_id",
40
+ "concept_type",
41
+ "title",
42
+ "aliases",
43
+ "source_unit_ids",
44
+ "actors",
45
+ "actions",
46
+ "conditions",
47
+ "exceptions",
48
+ "temporal_constraints",
49
+ "related_articles",
50
+ "source_text",
51
+ "source_policy"
52
+ ]
53
+ },
54
+ "MVPSemanticNormativeUnit": {
55
+ "source": "data/mckf/corpus_mckf_ontology.json::evidence_spans",
56
+ "fields": [
57
+ "unit_id",
58
+ "document_id",
59
+ "article_id",
60
+ "clause_path",
61
+ "raw_text",
62
+ "normalized_text",
63
+ "unit_type",
64
+ "actors",
65
+ "actions",
66
+ "conditions",
67
+ "exceptions",
68
+ "temporal_constraints",
69
+ "cross_references",
70
+ "extraction_confidence"
71
+ ]
72
+ }
73
+ },
74
+ "retrieval_channels": [
75
+ "bm25",
76
+ "lsa_dense",
77
+ "semantic_frame",
78
+ "sparql_graph",
79
+ "exact_reference"
80
+ ],
81
+ "fusion": "weighted_reciprocal_rank_fusion",
82
+ "micro_rules": [
83
+ {
84
+ "rule_id": "deadline_priority",
85
+ "effect": "deadline units receive a retrieval bonus for duration/date questions"
86
+ },
87
+ {
88
+ "rule_id": "procedure_chain",
89
+ "effect": "procedure evidence is ordered by source span when procedure intent is detected"
90
+ },
91
+ {
92
+ "rule_id": "exception_warning",
93
+ "effect": "selected exception evidence adds a visible caution to the answer"
94
+ },
95
+ {
96
+ "rule_id": "cross_reference_follow",
97
+ "effect": "cross-document/article references are exported as RDF edges and scored as retrieval hints"
98
+ },
99
+ {
100
+ "rule_id": "definition_attach",
101
+ "effect": "definition units are boosted for term-definition questions"
102
+ }
103
+ ],
104
+ "stats": {
105
+ "document_count": 3,
106
+ "concept_count": 544,
107
+ "semantic_normative_unit_count": 4659,
108
+ "unit_type_counts": {
109
+ "condition": 242,
110
+ "deadline": 792,
111
+ "definition": 630,
112
+ "exception": 126,
113
+ "obligation": 2293,
114
+ "permission": 86,
115
+ "procedure": 477,
116
+ "prohibition": 13
117
+ },
118
+ "concept_type_counts": {
119
+ "deadline_rule": 482,
120
+ "exception_rule": 2,
121
+ "obligation_rule": 55,
122
+ "permission_rule": 2,
123
+ "procedure_rule": 3
124
+ },
125
+ "cross_document_edge_count": 16
126
+ },
127
+ "outputs": {
128
+ "manifest": "C:/Users/user/Desktop/MITRANLIL/2547_AI/mitranlil_mckf_v0_8_multidoc/hf_hermeneutic_YOK_Kanunlar/data/mckf/rdfox_pilot_manifest.json",
129
+ "triples": "C:/Users/user/Desktop/MITRANLIL/2547_AI/mitranlil_mckf_v0_8_multidoc/hf_hermeneutic_YOK_Kanunlar/data/mckf/rdfox_pilot_triples.nt",
130
+ "rules": "C:/Users/user/Desktop/MITRANLIL/2547_AI/mitranlil_mckf_v0_8_multidoc/hf_hermeneutic_YOK_Kanunlar/data/mckf/rdfox_pilot_rules.dlog",
131
+ "triple_count": 90194
132
+ }
133
+ }
data/mckf/rdfox_pilot_rules.dlog ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MCKF RDFox pilot micro-rules.
2
+ # These rules are a portable MVP contract for RDFox/Datalog migration.
3
+ # Runtime uses equivalent Python micro-rules in rdfox_pilot.py.
4
+
5
+ PREFIX mckf: <https://mitranlil.ai/mckf/>
6
+ PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
7
+
8
+ # deadline_priority
9
+ mckf:retrievalHint[?unit, "deadline_priority"] :-
10
+ ?unit rdf:type mckf:DeadlineUnit .
11
+
12
+ # procedure_chain
13
+ mckf:retrievalHint[?unit, "procedure_chain"] :-
14
+ ?unit rdf:type mckf:ProcedureUnit .
15
+
16
+ # exception_warning
17
+ mckf:answerWarning[?unit, "exception_warning"] :-
18
+ ?unit rdf:type mckf:ExceptionUnit .
19
+
20
+ # cross_reference_follow
21
+ mckf:followReference[?unit, ?target] :-
22
+ ?unit mckf:crossReferences ?target .
23
+
24
+ # definition_attach
25
+ mckf:retrievalHint[?unit, "definition_attach"] :-
26
+ ?unit rdf:type mckf:DefinitionUnit .
data/mckf/runtime_build_manifest.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "MCKF-RuntimeBuildManifest-v1.0",
3
+ "build_id": "mckf-27720ea50a2f1088f3a5",
4
+ "generated_at": "2026-07-17T11:29:16.439250+00:00",
5
+ "active_institution_id": "",
6
+ "validation_conforms": true,
7
+ "artifacts": {
8
+ "corpus_mckf_ontology.json": {
9
+ "sha256": "a5ec5332013fe235a92520185f912a37efb1bc5ddb2b4b63fe863b67c883babb",
10
+ "bytes": 154825466
11
+ },
12
+ "corpus_retrieval_index.json": {
13
+ "sha256": "17a5206ffdaa8ad90e81d12e2b8c4563e0eb29b5aed3d578ee225fec20be632a",
14
+ "bytes": 1903364
15
+ },
16
+ "corpus_semantic_frames.jsonl": {
17
+ "sha256": "24b484e36557d14ea6ba237c1a3dbf84c494b76fd025d9a8287f97960c7ce18f",
18
+ "bytes": 11525656
19
+ },
20
+ "cross_document_edges.jsonl": {
21
+ "sha256": "086fcb9df06fa9dad713fc10aed33016dbf66e3a507d31cc0688db30dcc0df37",
22
+ "bytes": 5916
23
+ },
24
+ "rdfox_pilot_triples.nt": {
25
+ "sha256": "eca8aa204e6bbde856dda3da19df41da5b5d714c43313d09a7d705f176404a56",
26
+ "bytes": 15653736
27
+ },
28
+ "rdfox_pilot_rules.dlog": {
29
+ "sha256": "1492b1fe5e31e7338c77af042e5449bd999f3613b6e086c55ccf5279c975f241",
30
+ "bytes": 831
31
+ },
32
+ "mckf_shapes.ttl": {
33
+ "sha256": "e98499cf16b894a2343bf9869b0d9928e63a1dbceba907ada64070386bb1d1ee",
34
+ "bytes": 1253
35
+ },
36
+ "validation_report.json": {
37
+ "sha256": "b1169f4bbe9310e8dd36917c44b817e2c4c6bb374f6ef3d6713e97c300d8f0d0",
38
+ "bytes": 488
39
+ }
40
+ }
41
+ }
data/mckf/validation_report.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "MCKF-ValidationReport-v1.0",
3
+ "build_id": "mckf-27720ea50a2f1088f3a5",
4
+ "conforms": true,
5
+ "summary": {
6
+ "documents": 3,
7
+ "concepts": 544,
8
+ "clauses": 2551,
9
+ "evidence_spans": 4659,
10
+ "errors": 0,
11
+ "warnings": 0,
12
+ "low_confidence_evidence": 732
13
+ },
14
+ "issues": [],
15
+ "shacl": {
16
+ "status": "ok",
17
+ "conforms": true,
18
+ "triple_count": 90194,
19
+ "result_count": 2,
20
+ "message": "Validation Report\nConforms: True\n"
21
+ }
22
+ }
data/normalized/2547_document.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "document_id": "TR-KANUN-2547",
3
+ "document_title": "2547 sayılı Yükseköğretim Kanunu",
4
+ "short_code": "2547",
5
+ "source_path": "data/source/2547_clean.txt",
6
+ "article_count": 207,
7
+ "raw_structural_clauses": 845,
8
+ "schedule_count": 0,
9
+ "source_sha256": "2a6faa2af2da269d8a132cb2870461a97a0dadf464fe5f94bc0ff66864655800",
10
+ "version_id": "TR-KANUN-2547@sha256:2a6faa2af2da269d"
11
+ }
data/normalized/2809_document.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "document_id": "TR-KANUN-2809",
3
+ "document_title": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu",
4
+ "short_code": "2809",
5
+ "source_path": "data/source/2809_clean.txt",
6
+ "article_count": 298,
7
+ "raw_structural_clauses": 1654,
8
+ "schedule_count": 0,
9
+ "source_sha256": "46788726a86239d6ec0f3a03d45493329a763c6e2af2b50cc3bf889a8de42df5",
10
+ "version_id": "TR-KANUN-2809@sha256:46788726a86239d6"
11
+ }
data/normalized/2914_document.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "document_id": "TR-KANUN-2914",
3
+ "document_title": "2914 sayılı Yükseköğretim Personel Kanunu",
4
+ "short_code": "2914",
5
+ "source_path": "data/source/2914_clean.txt",
6
+ "article_count": 37,
7
+ "raw_structural_clauses": 76,
8
+ "schedule_count": 2,
9
+ "source_sha256": "1788ed3dd28b01bdcc0f70df1a6dc69199d3250a2f6b0b67fd99ef8742957974",
10
+ "version_id": "TR-KANUN-2914@sha256:1788ed3dd28b01bd"
11
+ }
data/registry/article_semantic_catalog.json ADDED
The diff for this file is too large to render. See raw diff
 
data/registry/document_semantic_profiles.json ADDED
@@ -0,0 +1,502 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "MCKF-DocumentSemanticProfiles-v1.0",
3
+ "documents": {
4
+ "TR-KANUN-2547": {
5
+ "domain_path": ["yükseköğretim", "kurumsal çerçeve"],
6
+ "semantic_dimensions": {
7
+ "kurumsal_aktör": {
8
+ "Yükseköğretim Kurulu": ["YÖK", "Yükseköğretim Kurulunca"],
9
+ "Üniversitelerarası Kurul": ["ÜAK", "Üniversitelerarası Kurulca"],
10
+ "rektör": ["rektörlük", "rektör tarafından"],
11
+ "senato": ["üniversite senatosu"],
12
+ "üniversite yönetim kurulu": ["yönetim kurulu"],
13
+ "dekan": ["dekanlık"],
14
+ "enstitü": ["enstitü müdürü", "enstitü kurulu", "enstitü yönetim kurulu"],
15
+ "öğretim elemanı": ["öğretim üyesi", "öğretim görevlisi", "araştırma görevlisi"],
16
+ "öğrenci": ["öğrenciler", "öğrencinin"]
17
+ },
18
+ "normatif_işlem": {
19
+ "atama": ["atanır", "atanması", "atamak"],
20
+ "seçim": ["seçilir", "seçimi", "seçmek"],
21
+ "görevlendirme": ["görevlendirilir", "görevlendirmek"],
22
+ "kuruluş": ["kurulur", "açılır", "teşkil edilir"],
23
+ "kayıt": ["kayıt yaptırmak", "kayıt yenilemek", "kaydının"],
24
+ "disiplin": ["disiplin cezası", "soruşturma"],
25
+ "izin": ["izinli", "izin verilir"],
26
+ "denetim": ["denetlemek", "gözetim", "kontrol"]
27
+ },
28
+ "eğitim_süreci": {
29
+ "ders": ["ders yükü", "ders kredisi", "teorik ders"],
30
+ "sınav": ["sınavlar", "sınav hakkı"],
31
+ "tez": ["tez çalışması", "tez danışmanı"],
32
+ "mezuniyet": ["mezun", "diploma almaya hak"],
33
+ "öğrenci kaydı": ["kayıt", "kayıt yenileme"],
34
+ "eğitim-öğretim": ["öğretim yılı", "akademik yıl"]
35
+ },
36
+ "hukuki_rol": {
37
+ "görev": ["görevleri", "görev yapmak"],
38
+ "yetki": ["yetkileri", "yetkilidir"],
39
+ "sorumluluk": ["sorumludur", "sorumlulukları"],
40
+ "hak": ["hakkı", "hak kazanır"],
41
+ "yükümlülük": ["yükümlüdür", "zorundadır"]
42
+ },
43
+ "zamansal_kısıt": {
44
+ "azami süre": ["azami süreler", "en fazla"],
45
+ "dönem": ["yarıyıl", "öğretim dönemi"],
46
+ "yıl": ["yıllık", "yıl için"],
47
+ "gün": ["gün içinde", "günlük"]
48
+ }
49
+ },
50
+ "article_overrides": {
51
+ "Ek Madde 1": {
52
+ "display_heading": "Süresi dolmadan rektör, dekan, müdür, yardımcı ve bölüm başkanını görevden alma",
53
+ "regulates": "Belirli üniversite yöneticilerinin kanuni görev süreleri dolmadan atamadaki usule göre görevden alınması",
54
+ "canonical_concepts": [
55
+ "üniversite yöneticisinin süresi dolmadan görevden alınması",
56
+ "rektör dekan müdür ve yardımcılarının görevden alınması",
57
+ "bölüm başkanının görevden alınması"
58
+ ],
59
+ "query_aliases": [
60
+ "rektör görev süresi dolmadan görevden alınabilir mi",
61
+ "dekan görevden nasıl alınır",
62
+ "enstitü müdürü görevden alma",
63
+ "yüksekokul müdürü görevden alma",
64
+ "rektör yardımcısı görevden alma",
65
+ "dekan yardımcısı görevden alma",
66
+ "bölüm başkanını görevden alma"
67
+ ],
68
+ "subject_classes": ["rector", "dean", "institute_director", "school_director", "deputy_manager", "department_head"],
69
+ "regulated_situations": [
70
+ "kanuni görev süresi dolmadan görevden alma",
71
+ "atamadaki usule uygun görevden alma"
72
+ ],
73
+ "legal_effect_types": ["termination_of_office", "authority_or_duty"],
74
+ "legal_operations": ["görevden alma"],
75
+ "competent_authorities": ["atamadaki usule göre yetkili makam"],
76
+ "operational_actors": [],
77
+ "exclusions": [
78
+ "öğretim üyesinin görevden alınması",
79
+ "profesörün görevden alınması",
80
+ "doçentin görevden alınması",
81
+ "doktor öğretim üyesinin görevden alınması",
82
+ "öğretim görevlisinin görevden alınması",
83
+ "araştırma görevlisinin görevden alınması",
84
+ "Yükseköğretim Kurulu Başkanının görevden alınması",
85
+ "Üniversitelerarası Kurul üyesinin görevden alınması",
86
+ "genel sekreterin görevden alınması"
87
+ ],
88
+ "review_status": "human_reviewed"
89
+ },
90
+ "Ek Madde 2": {
91
+ "display_heading": "Vakıfların yükseköğretim kurumu, bağlı birim veya bağımsız meslek yüksekokulu kurması",
92
+ "regulates": "Vakıfların yükseköğretim kurumu veya bağlı birim ile üniversiteye bağlı olmayan meslek yüksekokulu kurabilmesinin şartları",
93
+ "canonical_concepts": [
94
+ "vakıf yükseköğretim kurumu kurma",
95
+ "vakfın bağımsız meslek yüksekokulu kurması",
96
+ "vakıf üniversitesi veya bağlı birim kurma"
97
+ ],
98
+ "query_aliases": [
99
+ "vakıf üniversite kurabilir mi",
100
+ "vakıf yükseköğretim kurumu nasıl kurulur",
101
+ "vakıf bağımsız meslek yüksekokulu kurabilir mi",
102
+ "vakıfların yükseköğretim kurumu kurma şartları",
103
+ "üniversiteye bağlı olmayan meslek yüksekokulu"
104
+ ],
105
+ "subject_classes": ["foundation", "higher_education_institution", "vocational_school"],
106
+ "regulated_situations": [
107
+ "kazanç amacı bulunmadan yükseköğretim kurumu kurma",
108
+ "Cumhurbaşkanı kararıyla bağımsız meslek yüksekokulu kurma",
109
+ "mesleki ve teknik eğitim bölgesinde gereksinim bulunması"
110
+ ],
111
+ "legal_effect_types": ["establishment", "eligibility", "authority_or_duty"],
112
+ "legal_operations": ["yükseköğretim kurumu kurma", "bağlı birim kurma", "bağımsız meslek yüksekokulu kurma"],
113
+ "competent_authorities": ["vakıf", "Cumhurbaşkanı"],
114
+ "operational_actors": [],
115
+ "exclusions": [
116
+ "kazanç amaçlı yükseköğretim yapısı kurma",
117
+ "bağımsız fakülte kurma",
118
+ "bağımsız enstitü kurma",
119
+ "vakfın mali ve idari hususlarda serbest olması"
120
+ ],
121
+ "review_status": "human_reviewed"
122
+ },
123
+ "Ek Madde 3": {
124
+ "display_heading": "Vakıf yükseköğretim kurumu kuruluş başvurusunda sunulacak belgeler",
125
+ "regulates": "Vakıf yükseköğretim kurumu kurma başvurusunda Yükseköğretim Kurulu Başkanlığına sunulacak karar, olumlu yazı ve belgeler",
126
+ "canonical_concepts": [
127
+ "vakıf yükseköğretim kurumu kuruluş belgeleri",
128
+ "vakıf üniversitesi kuruluş başvurusu",
129
+ "tıp fakültesi kuruluşunda hastane şartı"
130
+ ],
131
+ "query_aliases": [
132
+ "vakıf üniversitesi kurmak için hangi belgeler gerekir",
133
+ "vakıf yükseköğretim kurumu kuruluş başvurusu",
134
+ "vakıf üniversitesi başvuru belgeleri",
135
+ "tıp fakültesi kurmak için hastane şartı",
136
+ "vakıf üniversitesi başvurusu nereye yapılır"
137
+ ],
138
+ "subject_classes": ["foundation", "higher_education_institution", "medical_faculty"],
139
+ "regulated_situations": [
140
+ "kuruluş kararının ve Vakıflar Genel Müdürlüğü olumlu yazısının sunulması",
141
+ "bina araç gereç ve mali kaynak belgelerinin sunulması",
142
+ "tıp fakültesi için hastane ön izin veya devir belgesi sunulması"
143
+ ],
144
+ "legal_effect_types": ["application_procedure", "document_obligation", "eligibility"],
145
+ "legal_operations": ["başvuru", "belge sunma", "belge inceleme"],
146
+ "competent_authorities": ["vakıf veya vakıflar", "Vakıflar Genel Müdürlüğü", "Yükseköğretim Kurulu Başkanlığı"],
147
+ "operational_actors": ["Sağlık Bakanlığı planlama birimleri"],
148
+ "exclusions": [
149
+ "vakıf yükseköğretim kurumunun kuruluş kararının verilmesi",
150
+ "tıp fakültesi dışındaki fakülteler için hastane şartı",
151
+ "her fakülte için iki yüz yatak şartı"
152
+ ],
153
+ "review_notes": [
154
+ "Millî Eğitim Bakanlığı aktif madde metninde açık görevli olarak yer almadığından yetkili listesine eklenmedi."
155
+ ],
156
+ "review_status": "human_reviewed"
157
+ },
158
+ "Ek Madde 8": {
159
+ "display_heading": "Vakıf yükseköğretim kurumlarında akademik organlar, öğretim elemanı nitelikleri ve ücret tabanı",
160
+ "regulates": "Vakıf yükseköğretim kurumlarının akademik organlarını, öğretim elemanı nitelik ve istihdam sınırlarını ve devlet kurumlarındaki emsal ücrete bağlı asgari ücretini",
161
+ "canonical_concepts": [
162
+ "vakıf üniversitesi akademik organları",
163
+ "vakıf üniversitesi öğretim elemanı nitelikleri",
164
+ "vakıf üniversitesi öğretim elemanı asgari ücreti"
165
+ ],
166
+ "query_aliases": [
167
+ "vakıf üniversitesinde öğretim elemanı maaşı ne kadar olmalı",
168
+ "vakıf üniversitesi emsal ücret",
169
+ "vakıf üniversitesi akademik organları",
170
+ "disiplinle çıkarılan kişi vakıf üniversitesinde çalışabilir mi",
171
+ "vakıf üniversitesinde öğretim elemanı olma şartları"
172
+ ],
173
+ "subject_classes": ["foundation_higher_education_institution", "academic_staff"],
174
+ "regulated_situations": [
175
+ "akademik organların devlet yükseköğretim kurumları gibi düzenlenmesi",
176
+ "öğretim elemanı niteliklerinin eşitliği",
177
+ "disiplinle çıkarılmış kişilerin istihdam yasağı",
178
+ "devlet yükseköğretim kurumundaki emsal ücretin altında ödeme yasağı"
179
+ ],
180
+ "legal_effect_types": ["organization", "eligibility", "employment_prohibition", "payment_obligation"],
181
+ "legal_operations": ["akademik organ düzenleme", "istihdam yasağı uygulama", "asgari ücret ödeme"],
182
+ "competent_authorities": [],
183
+ "operational_actors": ["vakıf yükseköğretim kurumu"],
184
+ "exclusions": [
185
+ "öğretim elemanının izin hakkı",
186
+ "öğretim elemanının sigorta hakkı",
187
+ "disiplin soruşturmasının usulü",
188
+ "öğretim elemanı ücret tavanı",
189
+ "öğretim elemanı ek ödemeleri"
190
+ ],
191
+ "review_status": "human_reviewed"
192
+ },
193
+ "Ek Madde 9": {
194
+ "display_heading": "Vakıf yükseköğretim kurumlarında eğitim esasları, öğrenci hakları, ücret ve ücretsiz okutma oranı",
195
+ "regulates": "Vakıf yükseköğretim kurumlarında eğitim ve öğrenci hakları ile öğrenim ücretinin belirlenmesini ve belirli programlarda en az yüzde on beş ücretsiz okutma yükümlülüğünü",
196
+ "canonical_concepts": [
197
+ "vakıf üniversitesi öğrenim ücreti",
198
+ "vakıf üniversitesi ücretsiz öğrenci oranı",
199
+ "vakıf üniversitesinde öğrenci hakları"
200
+ ],
201
+ "query_aliases": [
202
+ "vakıf üniversitesi ücret artışı nasıl belirlenir",
203
+ "vakıf üniversitesi yüzde on beş ücretsiz öğrenci",
204
+ "vakıf üniversitesinde ücretsiz okutma zorunluluğu",
205
+ "vakıf üniversitesi öğrenim ücretini kim belirler",
206
+ "tezli yüksek lisans ücretsiz kontenjan"
207
+ ],
208
+ "subject_classes": ["foundation_higher_education_institution", "student"],
209
+ "regulated_situations": [
210
+ "mütevelli heyetin öğrenim ücretini belirlemesi",
211
+ "sonraki yılların ücret artış esaslarının Yükseköğretim Kurulunca belirlenmesi",
212
+ "ön lisans lisans tezli yüksek lisans ve doktorada en az yüzde on beş ücretsiz okutma"
213
+ ],
214
+ "legal_effect_types": ["payment_obligation", "authority_or_duty", "education_right"],
215
+ "legal_operations": ["öğrenim ücreti tespiti", "ücret artış esaslarını belirleme", "ücretsiz okutma"],
216
+ "competent_authorities": ["mütevelli heyet", "Yükseköğretim Kurulu"],
217
+ "operational_actors": ["vakıf yükseköğretim kurumu"],
218
+ "exclusions": [
219
+ "tezsiz yüksek lisans ücretsiz okutma zorunluluğu",
220
+ "hazırlık ve birinci sınıf ilk kayıt ücretine ÜFE TÜFE sınırlaması",
221
+ "ücretsiz okutma oranının en fazla yüzde on beş olması"
222
+ ],
223
+ "review_status": "human_reviewed"
224
+ },
225
+ "Ek Madde 11": {
226
+ "display_heading": "Vakıf yükseköğretim kurumlarının denetimi, faaliyet izni ve tasfiyesi",
227
+ "regulates": "Vakıf yükseköğretim kurumlarının denetimini, faaliyetinin durdurulmasını, faaliyet izninin kaldırılmasını, öğrenci naklini ve tasfiyesini",
228
+ "canonical_concepts": [
229
+ "vakıf üniversitesinin faaliyet izninin kaldırılması",
230
+ "vakıf üniversitesinin tasfiyesi",
231
+ "vakıf yükseköğretim kurumu denetimi",
232
+ "garantör üniversiteye öğrenci nakli"
233
+ ],
234
+ "query_aliases": [
235
+ "vakıf üniversitesi nasıl kapatılır",
236
+ "vakıf üniversitesinin faaliyet iznini kim kaldırır",
237
+ "vakıf üniversitesi faaliyet durdurma",
238
+ "kapanan vakıf üniversitesinin öğrencileri ne olur",
239
+ "vakıf üniversitesi tasfiye süreci",
240
+ "vakıf üniversitesine kayyım atanması"
241
+ ],
242
+ "subject_classes": ["foundation_higher_education_institution", "founding_foundation", "student"],
243
+ "regulated_situations": [
244
+ "eğitim düzeyi yetersizliğinde faaliyetin durdurulması",
245
+ "kaynak aktarımında düzeltici veya kısıtlayıcı tedbir uygulanması",
246
+ "Cumhurbaşkanı kararıyla faaliyet izninin kaldırılması",
247
+ "öğrencilerin garantör veya devlet üniversitesine nakli",
248
+ "malvarlığının resmî tasfiyesi",
249
+ "kurucu vakfa kayyım atanması"
250
+ ],
251
+ "legal_effect_types": ["supervision", "sanction", "licence_revocation", "liquidation", "student_transfer"],
252
+ "legal_operations": ["denetim", "uyarı", "faaliyeti durdurma", "faaliyet iznini kaldırma", "kayyım atama", "tasfiye", "öğrenci nakli"],
253
+ "competent_authorities": ["Yükseköğretim Kurulu", "Cumhurbaşkanı", "Millî Eğitim Bakanlığı", "yetkili mahkeme", "Hazine ve Maliye Bakanlığı", "Vakıflar Genel Müdürlüğü"],
254
+ "operational_actors": ["garantör üniversite", "belirlenecek devlet üniversitesi", "tasfiye memurları"],
255
+ "exclusions": [
256
+ "devlet yükseköğretim kurumunun denetimi",
257
+ "kurucu vakfın tüzel kişiliğinin sona ermesi",
258
+ "tasfiye memurunun cezai sorumluluktan muaf olması"
259
+ ],
260
+ "review_status": "human_reviewed"
261
+ },
262
+ "Geçici Madde 78": {
263
+ "display_heading": "2018 öğrenci affı ve ÖYP araştırma görevlilerinin yeniden atanması",
264
+ "regulates": "2018-2019 döneminde belirli istisnalar dışında ilişiği kesilen veya kayıt yaptırmayan öğrencilerin yeniden öğrenime başlamasını ve ÖYP araştırma görevlilerinin yeniden atanmasını",
265
+ "canonical_concepts": [
266
+ "2018 öğrenci affı",
267
+ "2018 üniversite affı",
268
+ "ÖYP araştırma görevlisinin yeniden atanması"
269
+ ],
270
+ "query_aliases": [
271
+ "2018 öğrenci affından kimler yararlanabilir",
272
+ "Geçici Madde 78 öğrenci affı",
273
+ "2018 ilişiği kesilen öğrencinin geri dönüşü",
274
+ "2018 kayıt yaptırmayan öğrencinin dönüşü",
275
+ "ÖYP araştırma görevlisi yeniden atama"
276
+ ],
277
+ "subject_classes": ["student", "former_student", "research_assistant"],
278
+ "regulated_situations": [
279
+ "dört ay içinde başvuruyla 2018-2019 yılında öğrenime yeniden başlama",
280
+ "askerlik erteleme ve terhis sonrası iki ay içinde başvuru",
281
+ "eşdeğer programa yatay geçiş",
282
+ "ÖYP araştırma görevlisinin bir ay içinde başvuruyla yeniden atanması"
283
+ ],
284
+ "legal_effect_types": ["status_restoration", "eligibility", "student_transfer", "appointment"],
285
+ "legal_operations": ["başvuru", "yeniden öğrenime başlama", "kayıt", "askerlik erteleme", "yatay geçiş", "yeniden atama"],
286
+ "competent_authorities": ["ilgili yükseköğretim kurumu", "Yükseköğretim Kurulu", "üniversite senatosu", "ilgili üniversite"],
287
+ "operational_actors": [],
288
+ "exclusions": [
289
+ "terör suçundan hüküm giyen öğrencinin 2018 affı",
290
+ "terör örgütü bağlantısı nedeniyle ilişiği kesilen öğrencinin 2018 affı",
291
+ "dört aylık başvuru süresi dışındaki öğrenci başvurusu",
292
+ "bir aylık süre dışındaki ÖYP yeniden atama başvurusu",
293
+ "2018-2019 dışındaki öğrenci affı"
294
+ ],
295
+ "review_status": "human_reviewed"
296
+ },
297
+ "Geçici Madde 83": {
298
+ "display_heading": "2022 öğrenci affı ve yeniden öğrenime başlama",
299
+ "regulates": "2022-2023 döneminde genişletilmiş suç ve terör bağlantısı istisnaları dışında ilişiği kesilen veya kayıt yaptırmayan öğrencilerin yeniden öğrenime başlaması",
300
+ "canonical_concepts": [
301
+ "2022 öğrenci affı",
302
+ "öğrenci affı",
303
+ "öğrenciliğe yeniden dönüş",
304
+ "öğrencilik hakkının yeniden kazanılması"
305
+ ],
306
+ "query_aliases": [
307
+ "öğrenci affı",
308
+ "af kanunu",
309
+ "üniversite affı",
310
+ "2022 üniversite affı",
311
+ "Geçici Madde 83 öğrenci affı",
312
+ "ilişiği kesilen öğrencinin geri dönüşü",
313
+ "kayıt hakkının geri verilmesi"
314
+ ],
315
+ "subject_classes": ["student", "former_student"],
316
+ "regulated_situations": [
317
+ "öğrencilik ilişiğinin kesilmesi",
318
+ "kazanılan programa kayıt yaptırmama",
319
+ "öğrenime yeniden başlama"
320
+ ],
321
+ "legal_effect_types": ["status_restoration", "eligibility", "student_transfer"],
322
+ "legal_operations": ["başvuru", "yeniden öğrenime başlama", "kayıt", "askerlik erteleme", "yatay geçiş"],
323
+ "competent_authorities": ["ilgili yükseköğretim kurumu", "Yükseköğretim Kurulu", "üniversite senatosu"],
324
+ "operational_actors": [],
325
+ "exclusions": [
326
+ "terör suçundan mahkûm öğrencinin 2022 affı",
327
+ "kasten öldürme suçundan mahkûm öğrencinin 2022 affı",
328
+ "işkence veya eziyet suçundan mahkûm öğrencinin 2022 affı",
329
+ "cinsel saldırı veya çocuk istismarı suçundan mahkûm öğrencinin 2022 affı",
330
+ "uyuşturucu imal ve ticareti suçundan mahkûm öğrencinin 2022 affı",
331
+ "sahte belge nedeniyle kaydı iptal edilen öğrencinin 2022 affı",
332
+ "FSEK 71 suçundan mahkûm öğrencinin 2022 affı",
333
+ "terör örgütü bağlantısı nedeniyle ilişiği kesilen öğrencinin 2022 affı",
334
+ "dört aylık başvuru süresi dışındaki 2022 affı başvurusu",
335
+ "2022-2023 dışındaki öğrenci affı"
336
+ ],
337
+ "review_status": "human_reviewed"
338
+ }
339
+ }
340
+ },
341
+ "TR-KANUN-2914": {
342
+ "domain_path": ["yükseköğretim", "akademik personel", "mali ve özlük hakları"],
343
+ "semantic_dimensions": {
344
+ "personel_unvanı": {
345
+ "profesör": ["profesörler"],
346
+ "doçent": ["doçentler"],
347
+ "doktor öğretim üyesi": ["yardımcı doçent"],
348
+ "öğretim görevlisi": ["öğretim görevlileri"],
349
+ "araştırma görevlisi": ["araştırma görevlileri", "asistan"],
350
+ "öğretim elemanı": ["öğretim elemanları"]
351
+ },
352
+ "mali_unsur": {
353
+ "aylık": ["görev aylığı", "kazanılmış hak aylığı"],
354
+ "ek gösterge": ["ek göstergeler"],
355
+ "üniversite ödeneği": ["ödenek"],
356
+ "idari görev ödeneği": ["idari görev"],
357
+ "geliştirme ödeneği": ["geliştirme ödeneğinden"],
358
+ "eğitim öğretim ödeneği": ["eğitim öğretim ödeneği"],
359
+ "akademik teşvik ödeneği": ["akademik teşvik"],
360
+ "ek ders ücreti": ["ek ders", "ders ücreti"],
361
+ "tazminat": ["makam tazminatı", "temsil tazminatı"]
362
+ },
363
+ "personel_statüsü": {
364
+ "kadrolu": ["kadro", "kadrosunda"],
365
+ "sözleşmeli": ["sözleşme ile", "sözleşmeli"],
366
+ "yabancı uyruklu": ["yabancı öğretim elemanı"],
367
+ "emekli": ["emeklilik", "emekliye"]
368
+ },
369
+ "özlük_unsuru": {
370
+ "derece": ["dereceler", "derecesi"],
371
+ "kademe": ["kademeler", "kademesi"],
372
+ "kıdem": ["kıdem aylığı", "kıdem yılı"],
373
+ "terfi": ["yükselme", "ilerleme"]
374
+ },
375
+ "çalışma_karşılığı": {
376
+ "ders yükü": ["haftalık ders yükü", "ders yükünün"],
377
+ "ek ders": ["ek ders saati", "ek ders ücreti"],
378
+ "idari görev": ["idari görevi", "yönetim görevi"]
379
+ }
380
+ },
381
+ "article_overrides": {
382
+ "Geçici Madde 2": {
383
+ "display_heading": "Üniversite ödeneğinde kazanılmış mali hakkın fark tazminatıyla korunması",
384
+ "regulates": "Yeni üniversite ödeneği eski iş güçlüğü, iş riski, temininde güçlük zammı ve tam gün ödeneği toplamından düşükse net farkın kesintisiz ödenmesini",
385
+ "canonical_concepts": [
386
+ "üniversite ödeneği fark tazminatı",
387
+ "kazanılmış mali hakların korunması",
388
+ "eski yan ödemeler ile üniversite ödeneği farkı"
389
+ ],
390
+ "query_aliases": [
391
+ "üniversite ödeneği eski ödemeden düşük kalırsa ne olur",
392
+ "üniversite ödeneği fark tazminatı",
393
+ "2914 Geçici Madde 2 mali hak",
394
+ "iş güçlüğü iş riski ve tam gün ödeneği farkı",
395
+ "üniversite ödeneği vergi ve kesinti"
396
+ ],
397
+ "subject_classes": ["academic_staff", "temporary_article_1_beneficiary"],
398
+ "regulated_situations": [
399
+ "yeni üniversite ödeneğinin eski yan ödeme toplamından düşük kalması",
400
+ "aradaki net farkın üniversite ödeneği eski tutara ulaşıncaya kadar ödenmesi",
401
+ "fark ödemesinin vergi ve kesintiye tabi tutulmaması"
402
+ ],
403
+ "legal_effect_types": ["payment_obligation", "acquired_financial_right"],
404
+ "legal_operations": ["ödenek karşılaştırması", "net fark hesaplama", "fark tazminatı ödeme"],
405
+ "competent_authorities": [],
406
+ "operational_actors": ["ilgili yükseköğretim kurumunun maaş ve tahakkuk birimleri"],
407
+ "exclusions": [
408
+ "üniversite ödeneği eski ödemelere eşit veya yüksek olan personelin fark tazminatı",
409
+ "geliştirme ödeneği farkı",
410
+ "makam tazminatı farkı",
411
+ "idari görev ödeneği farkı",
412
+ "Geçici Madde 1 kapsamı dışındaki idari personelin fark tazminatı"
413
+ ],
414
+ "review_notes": [
415
+ "Maddede açık bir yetkili makam sayılmıyor; maaş ve tahakkuk birimleri yalnızca insan denetimli operasyonel çıkarım olarak tutuluyor."
416
+ ],
417
+ "review_status": "human_reviewed"
418
+ }
419
+ }
420
+ },
421
+ "TR-KANUN-2809": {
422
+ "domain_path": ["yükseköğretim", "kurumsal teşkilat", "akademik birimler"],
423
+ "semantic_dimensions": {
424
+ "kurum_türü": {
425
+ "üniversite": ["üniversitesi", "üniversitesine"],
426
+ "yüksek teknoloji enstitüsü": ["yüksek teknoloji enstitüsü"],
427
+ "rektörlük": ["rektörlüğe", "rektörlüğü"]
428
+ },
429
+ "akademik_birim": {
430
+ "fakülte": ["fakültesi", "fakülteler"],
431
+ "enstitü": ["enstitüsü", "enstitüler"],
432
+ "yüksekokul": ["yüksekokulu", "yüksekokullar"],
433
+ "meslek yüksekokulu": ["meslek yüksekokulu"],
434
+ "konservatuvar": ["konservatuvarı"],
435
+ "araştırma ve uygulama merkezi": ["uygulama ve araştırma merkezi", "araştırma merkezi"]
436
+ },
437
+ "teşkilat_işlemi": {
438
+ "kuruluş": ["kurulmuştur", "kurulur"],
439
+ "bağlanma": ["bağlanmıştır", "bağlı", "bağlanır"],
440
+ "ad değişikliği": ["adı değiştirilmiştir", "adıyla"],
441
+ "birleşme": ["birleştirilmiştir", "birleşir"],
442
+ "dönüştürme": ["dönüştürülmüştür", "dönüştürülür"],
443
+ "kapatma": ["kapatılmıştır", "kaldırılmıştır"]
444
+ },
445
+ "teşkilat_ilişkisi": {
446
+ "bünyesinde": ["bünyesinde yer alan"],
447
+ "bağlı birim": ["bağlı olarak", "bağlanmıştır"],
448
+ "rektörlüğe bağlı": ["rektörlüğe bağlı"],
449
+ "üniversiteye bağlı": ["üniversitesine bağlı"]
450
+ },
451
+ "geçiş_hükmü": {
452
+ "öğrenci devri": ["öğrencileri", "öğrencilerin nakli"],
453
+ "personel devri": ["personeli", "kadrolarıyla birlikte"],
454
+ "malvarlığı devri": ["taşınır", "taşınmaz", "araç ve gereç"],
455
+ "hak ve yükümlülük devri": ["hak ve yükümlülükleri", "borçları"]
456
+ }
457
+ },
458
+ "article_overrides": {
459
+ "Ek Madde 30": {
460
+ "display_heading": "Üniversiteye bağlı fakülte, enstitü ve yüksekokulların kurulması ve değiştirilmesi",
461
+ "regulates": "Cumhurbaşkanının üniversitelere bağlı fakülte, enstitü ve yüksekokul kurma, birleştirme, kapatma, bağlantı ve isim değiştirme yetkisini ve bağlantı değişikliğinde malvarlığı devrini",
462
+ "canonical_concepts": [
463
+ "üniversiteye bağlı fakülte enstitü ve yüksekokul kurma",
464
+ "akademik birim bağlantı ve isim değişikliği",
465
+ "akademik birim kapatma ve birleştirme"
466
+ ],
467
+ "query_aliases": [
468
+ "üniversiteye bağlı enstitüyü kim kurar",
469
+ "fakülte enstitü yüksekokul nasıl kurulur",
470
+ "enstitü kapatma yetkisi",
471
+ "fakültenin bağlı olduğu üniversiteyi değiştirme",
472
+ "akademik birimin adını değiştirme",
473
+ "bağlantısı değişen birimin malları ne olur"
474
+ ],
475
+ "subject_classes": ["faculty", "institute", "school", "university"],
476
+ "regulated_situations": [
477
+ "Cumhurbaşkanının üniversiteye bağlı fakülte enstitü ve yüksekokul kurması",
478
+ "bu birimleri birleştirme kapatma ve adını değiştirme",
479
+ "birimin bağlı olduğu üniversiteyi değiştirme",
480
+ "kadro taşınmaz ve demirbaşların yeni üniversiteye aktarılması"
481
+ ],
482
+ "legal_effect_types": ["establishment", "authority_or_duty", "organizational_transfer"],
483
+ "legal_operations": ["kurma", "birleştirme", "kapatma", "bağlantı değiştirme", "isim değiştirme", "kadro ve malvarlığı devri"],
484
+ "competent_authorities": ["Cumhurbaşkanı"],
485
+ "operational_actors": [],
486
+ "exclusions": [
487
+ "yeni devlet üniversitesi kurma",
488
+ "yeni vakıf üniversitesi kurma",
489
+ "üniversiteyi kapatma",
490
+ "fakülte altında bölüm açma",
491
+ "anabilim dalı açma",
492
+ "uygulama ve araştırma merkezi kurma"
493
+ ],
494
+ "review_notes": [
495
+ "Mevcut kaynak metin fakülte, enstitü ve yüksekokulu açıkça sayıyor; konservatuvar, meslek yüksekokulu ve merkez kanonik kapsama kaynak desteği olmadan eklenmedi."
496
+ ],
497
+ "review_status": "human_reviewed"
498
+ }
499
+ }
500
+ }
501
+ }
502
+ }
data/registry/legal_documents.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "document_id": "TR-KANUN-2547",
4
+ "short_code": "2547",
5
+ "title": "2547 sayılı Yükseköğretim Kanunu",
6
+ "document_type": "law",
7
+ "domain_tags": ["framework", "governance", "academic", "students", "institutions"],
8
+ "source_path": "data/source/2547_clean.txt",
9
+ "mckf_status": "indexed"
10
+ },
11
+ {
12
+ "document_id": "TR-KANUN-2914",
13
+ "short_code": "2914",
14
+ "title": "2914 sayılı Yükseköğretim Personel Kanunu",
15
+ "document_type": "law",
16
+ "domain_tags": ["personnel", "salary", "allowance", "academic_staff", "ek_ders", "degree", "kademe"],
17
+ "source_path": "data/source/2914_clean.txt",
18
+ "mckf_status": "indexed"
19
+ },
20
+ {
21
+ "document_id": "TR-KANUN-2809",
22
+ "short_code": "2809",
23
+ "title": "2809 sayılı Yükseköğretim Kurumları Teşkilatı Kanunu",
24
+ "document_type": "law",
25
+ "domain_tags": ["organization", "institution", "university_structure", "faculty", "institute", "academic_unit"],
26
+ "source_path": "data/source/2809_clean.txt",
27
+ "mckf_status": "indexed"
28
+ }
29
+ ]
data/registry/normative_interpretation_families.json ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "MCKF-NormativeInterpretationFamilies-v1.1",
3
+ "language": "tr",
4
+ "description": "Kullanıcı dilindeki yakın ifadeleri hukuken farklı nesne ve işlem türlerine ayıran, belge ve madde numarasından bağımsız yorum aileleri.",
5
+ "families": [
6
+ {
7
+ "family_id": "academic_personnel_mobility",
8
+ "label": "Akademik personelin birimler arasındaki hareketi",
9
+ "priority": 100,
10
+ "enforce_fidelity": true,
11
+ "match_groups": [
12
+ ["öğretim üyesi", "öğretim elemanı", "akademik personel", "personel"],
13
+ ["fakülte", "birim", "üniversite"],
14
+ ["aktarmak", "aktarım", "taşımak", "nakil", "geçirmek", "görev yeri", "görevlendirme"]
15
+ ],
16
+ "clarification_prompt": "Burada günlük dilde birbirinin yerine kullanılan, fakat farklı hukuki sonuç doğuran işlemler var. Yanlış hükme gitmemek için corpus içinde açık dayanağı bulunan işlem türünü seçin.",
17
+ "mechanisms": [
18
+ {
19
+ "mechanism_id": "formal_staff_position_transfer",
20
+ "label": "Kadro/pozisyonun başka bir fakülteye aktarılması",
21
+ "definition": "Kadro cetvelindeki pozisyonun bir kurumsal birimden başka bir kurumsal birime aktarılması.",
22
+ "display_order": 1,
23
+ "terms": ["kadro", "pozisyon", "kadro cetveli", "kadro aktarımı", "naklen atama"],
24
+ "action_terms": ["aktarmak", "aktarım", "taşımak", "nakil", "devretmek", "intikal"],
25
+ "unambiguous_phrases": ["kadro cetveli", "kadro pozisyon aktarımı", "kadronun aktarılması"],
26
+ "clarify_when_top": true,
27
+ "evidence_patterns": ["kadro.{0,100}(aktar|devr|intikal|nakl)", "(aktar|devr|intikal|nakl).{0,100}kadro"],
28
+ "answer_policy": "corpus_gap",
29
+ "coverage_note": "İncelenen 2547, 2914 ve 2809 sayılı kanunlarda, aynı üniversite içinde rutin bir fakülteler arası kadro/pozisyon aktarımının genel usulü açıkça düzenlenmiyor. Görev yerinin değiştirilmesi veya görevlendirme, kadro aktarımıyla aynı işlem değildir."
30
+ },
31
+ {
32
+ "mechanism_id": "internal_duty_place_change",
33
+ "label": "Öğretim elemanının görev yerinin üniversite içinde değiştirilmesi",
34
+ "definition": "Kadro cetvelini aktarmadan, personelin üniversite içindeki görev yerinin değiştirilmesi veya kendisine yeni görev verilmesi.",
35
+ "display_order": 2,
36
+ "terms": ["görev yeri", "görev yerini değiştirmek", "yeni görev", "birimde görevlendirmek"],
37
+ "action_terms": ["değiştirmek", "taşımak", "geçirmek"],
38
+ "unambiguous_phrases": ["görev yeri", "görev yerinin değiştirilmesi", "yeni görev verilmesi"],
39
+ "commitment_phrases": ["işlem türü kesin olarak görev yeri değişikliğidir"],
40
+ "evidence_phrase_groups": [["görev yerlerini değiştirmek", "bunlara yeni görevler vermek"]],
41
+ "evidence_patterns": ["gorev yer.{0,60}degistir", "yeni gorev.{0,30}ver"],
42
+ "answer_policy": "retrieve",
43
+ "resolved_question": "İşlem türü kesin olarak görev yeri değişikliğidir. Aynı üniversite içinde bir fakültede görevli öğretim elemanlarının görev yerlerinin başka bir fakülte veya birime değiştirilmesi hangi makamın yetkisindedir ve 2547 sayılı Kanunda nasıl düzenlenmiştir? Kadro veya pozisyon aktarımı sorulmamaktadır."
44
+ },
45
+ {
46
+ "mechanism_id": "intra_university_teaching_assignment",
47
+ "label": "Ders yükünü tamamlamak için başka birimde görevlendirme",
48
+ "definition": "Öğretim elemanının kadrosu ve asli görev yeri değişmeden, ders yükünü tamamlamak üzere başka bir birimde görevlendirilmesi.",
49
+ "display_order": 3,
50
+ "terms": ["görevlendirme", "görevlendirilmek", "ders yükü", "başka birimde ders"],
51
+ "action_terms": ["görevlendirmek", "ders vermek"],
52
+ "unambiguous_phrases": ["ders yükünü tamamlamak", "başka birimde görevlendirme", "geçici görevlendirme"],
53
+ "commitment_phrases": ["işlem türü kesin olarak ders yükünü tamamlamak amacıyla üniversite içinde görevlendirmedir"],
54
+ "evidence_phrase_groups": [["kendi üniversitelerinin diğer birimlerinde", "ders yükünü doldurmak üzere rektör tarafından görevlendirilebilirler"]],
55
+ "evidence_patterns": ["diger birim.{0,100}gorevlendir", "ders yuk.{0,100}gorevlendir"],
56
+ "answer_policy": "retrieve",
57
+ "resolved_question": "İşlem türü kesin olarak ders yükünü tamamlamak amacıyla üniversite içinde görevlendirmedir. Öğretim üyesi veya öğretim görevlisi kendi üniversitesinin başka biriminde hangi şartlarla ve hangi makam tarafından görevlendirilebilir? Kadro aktarımı ve sürekli görev yeri değişikliği sorulmamaktadır."
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ "family_id": "institutional_structure_operation",
63
+ "label": "Yükseköğretim birimlerinin kuruluş ve yapı işlemleri",
64
+ "priority": 90,
65
+ "enforce_fidelity": true,
66
+ "commitment_entity_rules": [
67
+ {
68
+ "phrase": "kurumsal nesne kesin olarak üniversiteye bağlı bir enstitüdür",
69
+ "target_entities": ["enstitü"]
70
+ },
71
+ {
72
+ "phrase": "kurumsal nesne kesin olarak uygulama ve araştırma merkezidir",
73
+ "target_entities": ["uygulama ve araştırma merkezi"]
74
+ }
75
+ ],
76
+ "negative_terms": ["fakülte kurulu", "enstitü kurulu", "yüksekokul kurulu", "üniversite yönetim kurulu"],
77
+ "bypass_terms": ["birlikte nasıl okunur", "2547 ve 2809", "hangi 2547 maddesine bağlar", "arasındaki bağ", "nasıl bağlanır"],
78
+ "match_groups": [
79
+ ["yükseköğretim kurumu", "üniversite", "yüksek teknoloji enstitüsü", "fakülte", "enstitü", "yüksekokul", "bölüm", "anabilim dalı", "anasanat dalı", "bilim dalı", "araştırma merkezi", "alt birim", "birim"],
80
+ ["kurulması", "kurmak", "açılması", "açmak", "kapatılması", "kapatmak", "birleştirmek", "bağlantı değişikliği", "bağlamak", "isim değişikliği", "teşkil", "faaliyete geçirilmek"]
81
+ ],
82
+ "clarification_prompt": "Kurulacak veya değiştirilecek yapının düzeyi, yetkili makamı ve uygulanacak hükmü değiştirir. Hangi kurumsal nesneyi kastettiğinizi seçin.",
83
+ "terminology_ambiguities": [
84
+ {
85
+ "ambiguity_id": "research_institute_or_center",
86
+ "all_terms": ["araştırma", "enstitü"],
87
+ "exclude_phrases": ["yüksek teknoloji enstitüsü", "uygulama ve araştırma merkezi"],
88
+ "prompt": "“Araştırma enstitüsü” ifadesi corpus içindeki kanunlarda tek bir kanonik birim adı olarak kullanılmıyor. Yanlış hükme gitmemek için hangi kurumsal yapıyı kastettiğinizi seçin.",
89
+ "mechanism_ids": [
90
+ "principal_academic_unit_reorganization",
91
+ "academic_subunit_reorganization"
92
+ ],
93
+ "option_labels": {
94
+ "principal_academic_unit_reorganization": "Üniversiteye bağlı araştırma enstitüsü kurulması",
95
+ "academic_subunit_reorganization": "Uygulama ve araştırma merkezi açılması"
96
+ },
97
+ "resolved_questions": {
98
+ "principal_academic_unit_reorganization": "Kurumsal nesne kesin olarak üniversiteye bağlı bir enstitüdür. Üniversiteye bağlı bir enstitünün kurulmasına kim karar verir; yetkili makam ve usul nedir?",
99
+ "academic_subunit_reorganization": "Kurumsal nesne kesin olarak uygulama ve araştırma merkezidir. Bir uygulama ve araştırma merkezinin açılmasına kim karar verir; yetkili makam ve usul nedir?"
100
+ }
101
+ }
102
+ ],
103
+ "mechanisms": [
104
+ {
105
+ "mechanism_id": "foundation_higher_education_institution_establishment",
106
+ "label": "Vakıf yükseköğretim kurumu kurulması",
107
+ "definition": "Bir vakıf veya birden fazla vakıf tarafından yükseköğretim kurumu kurulmasına özgü karar, başvuru ve belge süreci.",
108
+ "display_order": 1,
109
+ "terms": ["vakıf yükseköğretim kurumu", "vakıf üniversitesi", "vakıflar tarafından kurulmak"],
110
+ "unambiguous_phrases": ["vakıf yükseköğretim kurumu nasıl kurulur", "vakıf üniversitesi nasıl kurulur"],
111
+ "commitment_phrases": ["kurumsal nesne kesin olarak vakıf yükseköğretim kurumudur"],
112
+ "evidence_phrase_groups": [["Vakıf veya birden fazla vakfın yetkili yönetim organlarının yükseköğretim kurumu kurma ile ilgili karar"]],
113
+ "evidence_patterns": ["vakif.{0,180}yuksekogretim kurumu kurma", "vakif.{0,220}kurulacak yuksekogretim kurum"],
114
+ "answer_policy": "retrieve",
115
+ "resolved_question": "Kurumsal nesne kesin olarak vakıf yükseköğretim kurumudur. Vakıf veya vakıflar bir yükseköğretim kurumu kurmak için hangi kararları almalı, hangi belgeleri hazırlamalı ve başvuruyu hangi makama sunmalıdır? Devlet üniversitesi ya da üniversiteye bağlı birim kurulması sorulmamaktadır."
116
+ },
117
+ {
118
+ "mechanism_id": "university_establishment",
119
+ "label": "Üniversite veya yüksek teknoloji enstitüsü kurulması",
120
+ "definition": "Yeni bir yükseköğretim kurumunun tüzel ve kurumsal olarak kurulması.",
121
+ "display_order": 1,
122
+ "terms": ["yükseköğretim kurumu kurmak", "üniversite kurmak", "üniversite kurulması", "yüksek teknoloji enstitüsü kurulması"],
123
+ "unambiguous_phrases": ["yükseköğretim kurumu nasıl kurulur", "yeni üniversite kurmak", "üniversite nasıl kurulur", "üniversite kurulması"],
124
+ "commitment_phrases": ["kurumsal nesne kesin olarak yeni bir üniversitedir"],
125
+ "evidence_phrase_groups": [["kanunla kurulur", "adıyla yeni bir üniversite kurulmuştur"]],
126
+ "evidence_patterns": ["universite.{0,280}kanunla kurulur", "adiyla yeni bir universite kurulmustur"],
127
+ "answer_policy": "retrieve",
128
+ "resolved_question": "Kurumsal nesne kesin olarak yeni bir üniversitedir. Üniversite veya yüksek teknoloji enstitüsü hangi hukuki işlem ve planlama çerçevesinde kurulur? Fakülte, bölüm veya alt birim açılması sorulmamaktadır."
129
+ },
130
+ {
131
+ "mechanism_id": "principal_academic_unit_reorganization",
132
+ "label": "Fakülte, enstitü veya yüksekokul kurma/kapatma",
133
+ "definition": "Üniversiteye bağlı ana akademik birimlerin kurulması, birleştirilmesi, kapatılması veya bağlantı ve adının değiştirilmesi.",
134
+ "display_order": 2,
135
+ "terms": ["fakülte", "enstitü", "yüksekokul", "bağlantı değişikliği", "isim değişikliği"],
136
+ "unambiguous_phrases": ["fakülte kurmak", "enstitü kurmak", "yüksekokul kurmak", "fakülte kapatmak", "enstitü kapatmak"],
137
+ "commitment_phrases": ["kurumsal nesne kesin olarak fakülte enstitü veya yüksekokuldur", "kurumsal nesne kesin olarak üniversiteye bağlı bir enstitüdür"],
138
+ "evidence_phrase_groups": [["Cumhurbaşkanı üniversitelere bağlı olarak fakülte, enstitü ve yüksekokul kurmaya", "bir üniversite içinde fakülte, enstitü ve yüksekokul açılmasına"]],
139
+ "evidence_patterns": ["(fakulte|enstitu|yuksekokul).{0,220}(kurul(?:ur|mas|acak)|kurma|acilma|birlestiril|kapatil|baglanti|isim degisik)", "cumhurbaskani.{0,100}(fakulte|enstitu|yuksekokul)"],
140
+ "answer_policy": "retrieve",
141
+ "resolved_question": "Kurumsal nesne kesin olarak üniversiteye bağlı fakülte, enstitü veya yüksekokuldur. Bu ana akademik birimlerin kurulması, birleştirilmesi, kapatılması ya da bağlantı ve isim değişikliği hangi makam ve usulle yapılır? Üniversitenin kendisinin veya bölüm gibi alt birimlerin kurulması sorulmamaktadır."
142
+ },
143
+ {
144
+ "mechanism_id": "academic_subunit_reorganization",
145
+ "label": "Bölüm, anabilim/anasanat dalı veya araştırma merkezi açma/kapatma",
146
+ "definition": "Bir yükseköğretim kurumu içindeki bölüm ve uygulama-araştırma merkezi düzeyindeki alt birim işlemleri.",
147
+ "display_order": 3,
148
+ "terms": ["alt birim", "bölüm", "anabilim dalı", "anasanat dalı", "araştırma merkezi", "uygulama merkezi"],
149
+ "unambiguous_phrases": ["alt birimlerin teşkili", "bölüm açmak", "anabilim dalı açmak", "araştırma merkezi açmak", "bölüm kapatmak"],
150
+ "commitment_phrases": ["kurumsal nesne kesin olarak bölüm anabilim dalı anasanat dalı veya araştırma merkezidir", "kurumsal nesne kesin olarak uygulama ve araştırma merkezidir"],
151
+ "evidence_phrase_groups": [["bölüm, anabilim ve anasanat dalları ile uygulama ve araştırma merkezi açılması", "alt birimlerinin, anabilim dalı, anasanat dalı ve diğer ünitelerin teşkili"]],
152
+ "evidence_patterns": ["(bolum|anabilim|anasanat|arastirma merkezi).{0,180}(acil|birlestiril|kapatil)", "alt birim.{0,220}(teskil|birlestiril|kaldiril|isleyis)"],
153
+ "answer_policy": "retrieve",
154
+ "resolved_question": "Kurumsal nesne kesin olarak bölüm, anabilim dalı, anasanat dalı veya uygulama ve araştırma merkezidir. Bu alt birimlerin açılması, birleştirilmesi veya kapatılması hangi makamın yetkisindedir? Fakülte ya da üniversite kurulması sorulmamaktadır."
155
+ },
156
+ {
157
+ "mechanism_id": "science_or_art_branch_reorganization",
158
+ "label": "Bilim veya sanat dalı kurulması/kaldırılması",
159
+ "definition": "Anabilim ya da anasanat dalı altındaki bilim ve sanat dalının kurulması veya kaldırılması.",
160
+ "display_order": 4,
161
+ "terms": ["bilim dalı", "sanat dalı"],
162
+ "unambiguous_phrases": ["bilim dalı kurmak", "sanat dalı kurmak", "bilim dalı kaldırmak"],
163
+ "commitment_phrases": ["kurumsal nesne kesin olarak bilim veya sanat dalıdır"],
164
+ "evidence_phrase_groups": [["Bilim dalı ve sanat dalı kurulması"]],
165
+ "evidence_patterns": ["(bilim dali|sanat dali).{0,100}(kurul|kaldir)"],
166
+ "answer_policy": "retrieve",
167
+ "resolved_question": "Kurumsal nesne kesin olarak bilim veya sanat dalıdır. Bilim ve sanat dalının kurulması veya kaldırılması hangi makamların görüşü ve kararıyla yapılır? Bölüm, fakülte veya üniversite kurulması sorulmamaktadır."
168
+ },
169
+ {
170
+ "mechanism_id": "unit_activation",
171
+ "label": "Yeni kurulan birimin faaliyete geçirilmesi",
172
+ "definition": "Kanunla kurulan bir yükseköğretim biriminin eğitim ve öğretime fiilen başlatılması.",
173
+ "display_order": 5,
174
+ "terms": ["faaliyete geçirilmek", "yeni kurulan birim", "eğitim öğretime başlamak"],
175
+ "unambiguous_phrases": ["yeni kurulan birimler ne zaman faaliyete geçirilir", "birimi faaliyete geçirmek"],
176
+ "commitment_phrases": ["kurumsal işlem kesin olarak yeni kurulan birimin faaliyete geçirilmesidir"],
177
+ "evidence_phrase_groups": [["Yükseköğretim Kurulunun kararı ile faaliyete geçirilir"]],
178
+ "evidence_patterns": ["yeni kurulan birim.{0,520}faaliyete geciril"],
179
+ "answer_policy": "retrieve",
180
+ "resolved_question": "Kurumsal işlem kesin olarak yeni kurulan birimin faaliyete geçirilmesidir. Kanunla kurulan yeni birim hangi hazırlıklar tamamlandıktan ve hangi makam karar verdikten sonra eğitim ve öğretime başlatılır? Birimin hukuken kurulması sorulmamaktadır."
181
+ }
182
+ ]
183
+ },
184
+ {
185
+ "family_id": "student_status_and_remedy",
186
+ "label": "Öğrenci kaydı, ilişik kesme ve telafi hakları",
187
+ "priority": 80,
188
+ "enforce_fidelity": true,
189
+ "negative_terms": ["itiraz", "uyarı", "uyarılmadı", "disiplin"],
190
+ "match_groups": [
191
+ ["öğrenci", "öğrencilik", "lisans", "lisansüstü", "üç sınav hakkı", "iki ek sınav hakkı", "sınırsız sınav hakkı"],
192
+ ["kayıt", "ilişik", "kaydı silmek", "af", "geri dönmek", "sınav hakkı", "azami süre", "tez dersi"]
193
+ ],
194
+ "clarification_prompt": "Kayıt, ilişik kesme, sınav hakkı ve yeniden öğrenime dönüş aynı hukuki sonuç değildir. Corpus içinde düzenlenen durumu seçin.",
195
+ "mechanisms": [
196
+ {
197
+ "mechanism_id": "course_registration_regulation",
198
+ "label": "Ders/tez kaydı ve devam kurallarının belirlenmesi",
199
+ "definition": "Ders, tez, devam ve kayıt içeriklerinin hangi düzenleme düzeyinde ve hangi organ tarafından belirleneceği.",
200
+ "display_order": 1,
201
+ "terms": ["ders kaydı", "tez dersi", "uzmanlık dersi", "devam", "kayıt kuralı"],
202
+ "unambiguous_phrases": ["tez dersi seçmek", "ders kaydı yapmak", "uzmanlık dersi seçmek"],
203
+ "commitment_phrases": ["öğrenci işlemi kesin olarak ders tez kaydı ve devam kurallarıdır"],
204
+ "evidence_phrase_groups": [["kayıt, devam, uygulama, tez ve teorik ders içerikleri"]],
205
+ "evidence_patterns": ["kayit.{0,80}devam.{0,80}tez", "tez.{0,100}(ders|icerik|devam)"],
206
+ "answer_policy": "retrieve",
207
+ "resolved_question": "Öğrenci işlemi kesin olarak ders, tez kaydı ve devam kurallarıdır. Kayıt, devam, tez ve teorik ders içerikleri hangi organ tarafından ve hangi üst ilkelere uygun olarak belirlenir? Öğrenci affı veya azami süre sonrası sınav hakkı sorulmamaktadır."
208
+ },
209
+ {
210
+ "mechanism_id": "registration_nonrenewal_termination",
211
+ "label": "Kayıt yenilememe veya ücret ödememe nedeniyle ilişik kesme",
212
+ "definition": "Kayıt yenilenmemesi ya da katkı payı/öğrenim ücreti ödenmemesinin öğrencilik statüsüne etkisi.",
213
+ "display_order": 2,
214
+ "terms": ["kayıt yenilememek", "kayıt yenilenmemesi", "ücret ödememek", "katkı payı", "ilişik kesme", "kaydı silmek", "kaydı silinen"],
215
+ "unambiguous_phrases": ["kayıt yenilenmemesi nedeniyle", "ücret ödenmemesi nedeniyle", "dört yıl üst üste kayıt yenilememek"],
216
+ "commitment_phrases": ["öğrenci işlemi kesin olarak kayıt yenilememe veya ücret ödememe nedeniyle ilişik kesmedir"],
217
+ "evidence_phrase_groups": [["kayıt yenilenmemesi nedeniyle öğrencilerin ilişikleri kesilmez"]],
218
+ "evidence_patterns": ["kayit yenilenmemesi.{0,150}ilisik", "katki payi.{0,180}ilisik"],
219
+ "answer_policy": "retrieve",
220
+ "resolved_question": "Öğrenci işlemi kesin olarak kayıt yenilememe veya katkı payı/öğrenim ücreti ödememe nedeniyle ilişik kesmedir. Bu sebepler hangi koşullarda öğrencinin ilişiğinin kesilmesine yol açabilir? Af veya sınav telafisi sorulmamaktadır."
221
+ },
222
+ {
223
+ "mechanism_id": "maximum_duration_exam_remedy",
224
+ "label": "Azami süre veya başarısızlık sonrası ek sınav hakkı",
225
+ "definition": "Azami süresini dolduran ya da belirli sayıda dersten başarısız olan öğrenciye tanınan ek/sınırsız sınav hakları.",
226
+ "display_order": 3,
227
+ "terms": ["azami süre", "ek sınav", "üç sınav hakkı", "iki ek sınav", "sınırsız sınav", "başarısız ders"],
228
+ "unambiguous_phrases": ["üç sınav hakkı", "iki ek sınav hakkı", "sınırsız sınav hakkı", "azami süre sonrası sınav"],
229
+ "commitment_phrases": ["öğrenci işlemi kesin olarak azami süre veya başarısızlık sonrası sınav hakkıdır"],
230
+ "evidence_phrase_groups": [["iki ek sınav hakkı verilir", "üç yıl içinde kullanacakları üç sınav hakkı"]],
231
+ "evidence_patterns": ["(iki ek|uc).{0,60}sinav hakki", "sinirsiz.{0,30}sinav hakki"],
232
+ "answer_policy": "retrieve",
233
+ "resolved_question": "Öğrenci işlemi kesin olarak azami süre veya başarısızlık sonrası ek sınav hakkıdır. 2547 sayılı Kanunda iki ek sınav, üç sınav veya sınırsız sınav hakları hangi öğrenci durumları için tanınmıştır? Öğrenci affı sorulmamaktadır."
234
+ },
235
+ {
236
+ "mechanism_id": "temporary_student_status_restoration",
237
+ "label": "Geçici maddeyle öğrencilik statüsünün yeniden kazanılması",
238
+ "definition": "İlişiği kesilen veya kayıt hakkını kullanmayan kişilere belirli tarih ve başvuru koşullarıyla yeniden öğrenime başlama hakkı verilmesi.",
239
+ "display_order": 4,
240
+ "terms": ["öğrenci affı", "af", "yeniden kayıt", "geri dönmek", "öğrenciliğe dönüş", "yeniden öğrenime başlamak", "kaydı silinen", "ilişiği kesilen"],
241
+ "unambiguous_phrases": ["öğrenci affı", "öğrenciliğe geri dönmek", "yeniden öğrenime başlamak", "af kanunu"],
242
+ "do_not_clarify_phrases": ["Geçici Madde 83", "1 Temmuz 2022", "2022-2023 eğitim-öğretim yılı"],
243
+ "clarify_when_top": true,
244
+ "commitment_phrases": ["öğrenci işlemi kesin olarak geçici maddeyle öğrencilik statüsünün yeniden kazanılmasıdır"],
245
+ "evidence_phrase_groups": [["her ne sebeple olursa olsun ilişiği kesilenler", "öğrenimlerine başlayabilirler"]],
246
+ "evidence_patterns": ["hazirlik dahil butun siniflarda.{0,240}(ilisik|kayit)", "ilisigi kesilenler.{0,420}ogrenimlerine baslayabilir", "2022-2023.{0,100}ogrenimlerine baslayabilir"],
247
+ "answer_policy": "retrieve",
248
+ "resolved_question": "Öğrenci işlemi kesin olarak geçici maddeyle öğrencilik statüsünün yeniden kazanılmasıdır. 1 Temmuz 2022 tarihli düzenlemede ilişiği kesilen veya kayıt hakkını kullanmayan öğrenciler hangi kapsam, istisna, başvuru süresi ve koşullarla 2022-2023 eğitim-öğretim yılında yeniden öğrenime başlayabilir? Önceki tarihli geçici öğrenci hakları sorulmamaktadır."
249
+ },
250
+ {
251
+ "mechanism_id": "historical_student_amnesty_provisions",
252
+ "label": "Önceki tarihli öğrenci affı ve geçici sınav/kayıt hakları",
253
+ "definition": "Farklı tarihlerde ilişiği kesilmiş öğrencilere belirli dönemler için tanınmış tarihsel devam, sınav ve yeniden kayıt hakları.",
254
+ "display_order": 5,
255
+ "terms": ["öğrenci affı", "af", "aflar", "eski öğrenci affı", "geçmiş af düzenlemeleri", "ilişiği kesilen"],
256
+ "unambiguous_phrases": ["önceki öğrenci afları", "eski öğrenci affı", "tarihsel af düzenlemeleri"],
257
+ "clarify_when_top": true,
258
+ "commitment_phrases": ["öğrenci işlemi kesin olarak önceki tarihli geçici öğrenci haklarıdır"],
259
+ "evidence_phrase_groups": [["ilişikleri kesilmiş öğrencilere", "ilişiği kesilen öğrencilere"], ["sınav hakkı verilir", "yeniden kayıt hakkı verilir"]],
260
+ "evidence_patterns": ["ilisik.{0,80}kesilmis.{0,160}ogrenci.{0,300}sinav hakki", "kaydi silinenlere.{0,120}yeniden kayit hakki"],
261
+ "answer_policy": "retrieve",
262
+ "resolved_question": "Öğrenci işlemi kesin olarak önceki tarihli geçici öğrenci haklarıdır. 2547 sayılı Kanunun geçmiş dönemlere ilişkin geçici maddelerinde ilişiği kesilen öğrencilere hangi devam, sınav ve yeniden kayıt hakları tanınmıştır? 1 Temmuz 2022 tarihli düzenleme tek başına sorulmamaktadır."
263
+ }
264
+ ]
265
+ },
266
+ {
267
+ "family_id": "financial_entitlement_type",
268
+ "label": "Akademik personelin mali ve sosyal hak türleri",
269
+ "priority": 70,
270
+ "enforce_fidelity": true,
271
+ "bypass_terms": ["daha doğrudan kaynak", "hangi kanun", "kanun mu"],
272
+ "match_groups": [
273
+ ["öğretim elemanı", "öğretim üyesi", "akademik personel", "personel", "rektör", "dekan"],
274
+ ["mali hak", "özlük hakkı", "sosyal hak", "ücret", "ödenek", "ödeme", "ek ders", "tazminat", "maaş"]
275
+ ],
276
+ "clarification_prompt": "Mali hak ifadesi birden çok ödeme ve yardım türünü kapsar. Hesaplama şartları farklı olduğundan aradığınız hak türünü seçin.",
277
+ "mechanisms": [
278
+ {
279
+ "mechanism_id": "social_rights_and_aid",
280
+ "label": "Sosyal hak ve yardımlar",
281
+ "definition": "Öğretim elemanlarının genel sosyal hak ve yardımlardan yararlanması.",
282
+ "display_order": 1,
283
+ "terms": ["sosyal hak", "sosyal yardım", "özlük hakkı"],
284
+ "unambiguous_phrases": ["sosyal hak ve yardımlar", "hangi sosyal haklardan yararlanır"],
285
+ "commitment_phrases": ["mali hak türü kesin olarak sosyal hak ve yardımlardır"],
286
+ "evidence_phrase_groups": [["sosyal hak ve yardımlardan faydalanırlar"]],
287
+ "evidence_patterns": ["sosyal hak.{0,60}yardim.{0,60}faydalan"],
288
+ "answer_policy": "retrieve",
289
+ "resolved_question": "Mali hak türü kesin olarak sosyal hak ve yardımlardır. Öğretim elemanları hangi genel sosyal hak ve yardımlardan yararlanır ve doğrudan kaynak hangi kanundur? Ek ders ve özel ödenekler sorulmamaktadır."
290
+ },
291
+ {
292
+ "mechanism_id": "extra_course_or_exam_payment",
293
+ "label": "Ek ders ve sınav ücreti",
294
+ "definition": "Zorunlu ders yükü dışındaki ders/faaliyet ve sınavlar için yapılan ödeme.",
295
+ "display_order": 2,
296
+ "terms": ["ek ders", "sınav ücreti", "ders ücreti", "ders yükü"],
297
+ "unambiguous_phrases": ["ek ders ücreti", "sınav ücreti", "ders yükü dışında"],
298
+ "commitment_phrases": ["mali hak türü kesin olarak ek ders veya sınav ücretidir"],
299
+ "evidence_phrase_groups": [["Ek ders ücreti, aşağıdaki göstergelerin", "sınav ücreti ödenir"]],
300
+ "evidence_patterns": ["ek ders ucreti", "sinav ucreti"],
301
+ "answer_policy": "retrieve",
302
+ "resolved_question": "Mali hak türü kesin olarak ek ders veya sınav ücretidir. Öğretim elemanına hangi ders yükü, faaliyet ve sınav koşullarında ek ders ya da sınav ücreti ödenir? Diğer ödenekler sorulmamaktadır."
303
+ },
304
+ {
305
+ "mechanism_id": "university_allowance",
306
+ "label": "Üniversite ödeneği",
307
+ "definition": "2914 sayılı Kanundaki unvan ve kadro esaslı üniversite ödeneği.",
308
+ "display_order": 3,
309
+ "terms": ["üniversite ödeneği"],
310
+ "unambiguous_phrases": ["üniversite ödeneği"],
311
+ "commitment_phrases": ["mali hak türü kesin olarak üniversite ödeneğidir"],
312
+ "evidence_phrase_groups": [["Üniversite Ödeneği"]],
313
+ "evidence_patterns": ["universite odenegi"],
314
+ "answer_policy": "retrieve",
315
+ "resolved_question": "Mali hak türü kesin olarak üniversite ödeneğidir. Üniversite ödeneğinin kapsamı, oranı, istisnaları ve ödeme koşulları nelerdir? Diğer ödeme türleri sorulmamaktadır."
316
+ },
317
+ {
318
+ "mechanism_id": "administrative_duty_allowance",
319
+ "label": "İdari görev ödeneği",
320
+ "definition": "Rektör, dekan, müdür ve diğer yöneticilere idari görev nedeniyle ödenen tutar.",
321
+ "display_order": 4,
322
+ "terms": ["idari görev ödeneği", "yönetici ödeneği", "rektör ödeneği", "dekan ödeneği"],
323
+ "unambiguous_phrases": ["idari görev ödeneği", "dekanın ödeneği", "rektörün ödeneği"],
324
+ "commitment_phrases": ["mali hak türü kesin olarak idari görev ödeneğidir"],
325
+ "evidence_phrase_groups": [["idari görev ödeneği olarak ayrıca ödenir"]],
326
+ "evidence_patterns": ["idari gorev odenegi"],
327
+ "answer_policy": "retrieve",
328
+ "resolved_question": "Mali hak türü kesin olarak idari görev ödeneğidir. Hangi akademik yöneticilere hangi oranlarda idari görev ödeneği verilir ve birden fazla görevde hangi kural uygulanır? Diğer ödenekler sorulmamaktadır."
329
+ },
330
+ {
331
+ "mechanism_id": "development_allowance",
332
+ "label": "Geliştirme ödeneği",
333
+ "definition": "Belirli yer veya kurumlarda görev yapan öğretim elemanlarına sağlanan geliştirme ödeneği.",
334
+ "display_order": 5,
335
+ "terms": ["geliştirme ödeneği", "az gelişmiş yer", "yeterli öğretim elemanı bulunmaması"],
336
+ "unambiguous_phrases": ["geliştirme ödeneği"],
337
+ "commitment_phrases": ["mali hak türü kesin olarak geliştirme ödeneğidir"],
338
+ "evidence_phrase_groups": [["geliştirme ödeneği ödenebilir"]],
339
+ "evidence_patterns": ["gelistirme odenegi"],
340
+ "answer_policy": "retrieve",
341
+ "resolved_question": "Mali hak türü kesin olarak geliştirme ödeneğidir. Geliştirme ödeneği hangi kurum, yer ve personel koşullarında verilebilir ve ayrıntıları hangi işlemle belirlenir? Diğer ödenekler sorulmamaktadır."
342
+ },
343
+ {
344
+ "mechanism_id": "education_teaching_allowance",
345
+ "label": "Eğitim öğretim ödeneği",
346
+ "definition": "Yükseköğretim kurumlarında görevli öğretim elemanlarına aylıklarla birlikte ödenen eğitim öğretim ödeneği.",
347
+ "display_order": 6,
348
+ "terms": ["eğitim öğretim ödeneği", "eğitim-öğretim ödeneği"],
349
+ "unambiguous_phrases": ["eğitim öğretim ödeneği", "eğitim-öğretim ödeneği"],
350
+ "commitment_phrases": ["mali hak türü kesin olarak eğitim öğretim ödeneğidir"],
351
+ "evidence_phrase_groups": [["Eğitim Öğretim Ödeneği olarak ödenir"]],
352
+ "evidence_patterns": ["egitim.?ogretim odenegi"],
353
+ "answer_policy": "retrieve",
354
+ "resolved_question": "Mali hak türü kesin olarak eğitim öğretim ödeneğidir. Eğitim öğretim ödeneği kimlere, hangi istisnalarla ve nasıl ödenir? Diğer ödeme türleri sorulmamaktadır."
355
+ },
356
+ {
357
+ "mechanism_id": "academic_incentive_allowance",
358
+ "label": "Akademik teşvik ödeneği",
359
+ "definition": "Bilimsel, teknolojik ve sanatsal faaliyet puanına dayalı akademik teşvik ödeneği.",
360
+ "display_order": 7,
361
+ "terms": ["akademik teşvik", "teşvik ödeneği", "teşvik puanı"],
362
+ "unambiguous_phrases": ["akademik teşvik ödeneği", "akademik teşvik puanı"],
363
+ "commitment_phrases": ["mali hak türü kesin olarak akademik teşvik ödeneğidir"],
364
+ "evidence_phrase_groups": [["Akademik teşvik ödeneği"]],
365
+ "evidence_patterns": ["akademik tesvik (odenegi|puani)"],
366
+ "answer_policy": "retrieve",
367
+ "resolved_question": "Mali hak türü kesin olarak akademik teşvik ödeneğidir. Akademik teşvik puanı hangi faaliyetlere göre hesaplanır ve ödeneğin temel koşulları nelerdir? Diğer ödenekler sorulmamaktadır."
368
+ }
369
+ ]
370
+ },
371
+ {
372
+ "family_id": "academic_personnel_status_action",
373
+ "label": "Akademik personelin atanma, yükseltilme ve görevlendirilmesi",
374
+ "priority": 60,
375
+ "enforce_fidelity": true,
376
+ "entity_terms": ["doktor öğretim üyesi", "araştırma görevlisi", "öğretim görevlisi", "profesör", "doçent", "öğretim üyesi", "öğretim elemanı", "akademik personel"],
377
+ "match_groups": [
378
+ ["öğretim üyesi", "öğretim elemanı", "öğretim görevlisi", "araştırma görevlisi", "doktor öğretim üyesi", "doçent", "profesör", "akademik personel"],
379
+ ["atanmak", "atama", "yükseltilmek", "unvan", "görevlendirilmek", "görevlendirme", "çalıştırmak", "sözleşmeli", "yabancı uyruklu"]
380
+ ],
381
+ "clarification_prompt": "Akademik unvan kazanma, bir kadroya atanma ve geçici görevlendirme farklı hukuki işlemlerdir. Aradığınız işlemi seçin.",
382
+ "mechanisms": [
383
+ {
384
+ "mechanism_id": "appointment_to_academic_position",
385
+ "label": "Akademik kadroya atanma",
386
+ "definition": "Belirli bir öğretim elemanı kadrosuna şart, öneri, görüş ve onay süreciyle atanma.",
387
+ "display_order": 1,
388
+ "selection_scope": "same_parent_clause",
389
+ "terms": ["kadroya atanmak", "atama", "atanma şartı", "öğretim üyesi kadrosu"],
390
+ "unambiguous_phrases": ["doktor öğretim üyesi ataması", "araştırma görevlisi ataması", "öğretim görevlisi ataması", "kadroya nasıl atanır", "atanma şartları", "öğretim üyesi kadrosuna atanmak"],
391
+ "commitment_phrases": ["personel işlemi kesin olarak akademik kadroya atanmadır"],
392
+ "evidence_phrase_groups": [["kadrolarına en çok üç yıl süre ile atanırlar", "kadrosuna atanabilmek için"]],
393
+ "evidence_patterns": ["kadro.{0,220}(atan|atama)", "(atan|atama).{0,220}kadro", "kadroya basvurabilmek"],
394
+ "answer_policy": "retrieve",
395
+ "resolved_question": "Personel işlemi kesin olarak akademik kadroya atanmadır. İlgili akademik kadroya atanma için hangi şartlar, öneriler, görüşler ve onaylar aranır? Unvan kazanma veya geçici görevlendirme sorulmamaktadır."
396
+ },
397
+ {
398
+ "mechanism_id": "academic_promotion_or_title",
399
+ "label": "Doçentlik/profesörlük unvanı ve yükseltilme",
400
+ "definition": "Akademik unvanın kazanılması veya üst akademik unvana yükseltilme süreci.",
401
+ "display_order": 2,
402
+ "selection_scope": "same_parent_clause",
403
+ "terms": ["doçentlik", "profesörlük", "yükseltilme", "unvan kazanmak", "akademik unvan"],
404
+ "unambiguous_phrases": ["profesörlüğe yükseltilme", "doçentlik unvanı", "profesör unvanı"],
405
+ "commitment_phrases": ["personel işlemi kesin olarak akademik unvan kazanma veya yükseltilmedir"],
406
+ "evidence_phrase_groups": [["doçentlik ve profesörlüğe yükseltilme ve atanma işlemleri", "profesörlüğe yükseltilerek atanabilmek"]],
407
+ "evidence_patterns": ["(docentlik|profesorluk).{0,120}(unvan|yukseltil|atan)", "profesor.{0,80}unvan"],
408
+ "answer_policy": "retrieve",
409
+ "resolved_question": "Personel işlemi kesin olarak akademik unvan kazanma veya yükseltilmedir. Doçentlik ya da profesörlük unvanı ve yükseltilme bakımından hangi şart ve süreçler uygulanır? Geçici görevlendirme sorulmamaktadır."
410
+ },
411
+ {
412
+ "mechanism_id": "temporary_academic_assignment",
413
+ "label": "Geçici veya kurumlar arası görevlendirme",
414
+ "definition": "Kadro ve unvan işlemi yapmadan akademik personelin başka bir kurum veya birimde süreli görevlendirilmesi.",
415
+ "display_order": 3,
416
+ "terms": ["geçici görevlendirme", "kurumlar arası görevlendirme", "başka üniversitede görevlendirme", "başka yerde çalıştırmak", "yurt dışında görevlendirme"],
417
+ "unambiguous_phrases": ["geçici görevlendirme", "başka üniversitede görevlendirilmek", "yurt dışında görevlendirilmek"],
418
+ "commitment_phrases": ["personel işlemi kesin olarak geçici veya kurumlar arası görevlendirmedir"],
419
+ "evidence_phrase_groups": [["görevlendirilebilirler", "görevlendirilen öğretim elemanları"]],
420
+ "evidence_patterns": ["ogretim (uyesi|eleman).{0,180}gorevlendir", "gorevlendirilen.{0,80}ogretim eleman"],
421
+ "answer_policy": "retrieve",
422
+ "resolved_question": "Personel işlemi kesin olarak geçici veya kurumlar arası görevlendirmedir. Öğretim elemanı hangi şartlar, makamlar ve sürelerle başka kurum, üniversite veya birimde görevlendirilebilir? Kadroya atanma veya unvan yükseltilmesi sorulmamaktadır."
423
+ },
424
+ {
425
+ "mechanism_id": "foreign_or_contracted_academic_employment",
426
+ "label": "Yabancı uyruklu veya sözleşmeli öğretim elemanı çalıştırma",
427
+ "definition": "Yabancı uyruklu ya da sözleşmeli akademik personelin özel istihdam usulü.",
428
+ "display_order": 4,
429
+ "terms": ["yabancı uyruklu", "sözleşmeli öğretim elemanı", "sözleşmeli personel"],
430
+ "unambiguous_phrases": ["yabancı uyruklu öğretim elemanı", "sözleşmeli öğretim elemanı"],
431
+ "commitment_phrases": ["personel işlemi kesin olarak yabancı uyruklu veya sözleşmeli akademik istihdamdır"],
432
+ "evidence_phrase_groups": [["yabancı uyruklu öğretim elemanları", "sözleşme ile çalıştırılabilirler"]],
433
+ "evidence_patterns": ["yabanci uyruklu.{0,100}ogretim eleman", "sozlesme.{0,80}calistirilabilir"],
434
+ "answer_policy": "retrieve",
435
+ "resolved_question": "Personel işlemi kesin olarak yabancı uyruklu veya sözleşmeli akademik istihdamdır. Bu personel hangi usul, onay, sözleşme ve ücret esaslarıyla çalıştırılabilir? Genel kadro ataması sorulmamaktadır."
436
+ }
437
+ ]
438
+ },
439
+ {
440
+ "family_id": "governing_body_semantics",
441
+ "label": "Kurul ve akademik makamın oluşumu, atanması ve görevleri",
442
+ "priority": 50,
443
+ "enforce_fidelity": false,
444
+ "match_groups": [
445
+ ["Yükseköğretim Kurulu", "YÖK", "Üniversitelerarası Kurul", "rektör", "dekan", "senato", "üniversite yönetim kurulu", "fakülte kurulu", "fakülte yönetim kurulu", "enstitü müdürü"],
446
+ ["görev", "yetki", "sorumluluk", "rol", "işlev", "statü", "oluşur", "oluşumu", "üye", "kimlerden", "atanır", "atanma", "seçilir", "seçilme", "görev süresi"]
447
+ ],
448
+ "clarification_prompt": "Bir kurul veya makam için oluşum/üyelik, göreve gelme ve görev-yetki düzenlemeleri farklı hükümlerde bulunabilir. Aradığınız normatif yönü seçin.",
449
+ "mechanisms": [
450
+ {
451
+ "mechanism_id": "body_membership_and_composition",
452
+ "label": "Kurulun üyeleri, seçim biçimi ve oluşumu",
453
+ "definition": "Kurulun kimlerden oluştuğu, üye sayısı ve üyelerin seçilme/atanma biçimi.",
454
+ "display_order": 1,
455
+ "selection_scope": "same_parent_clause",
456
+ "terms": ["üyeleri", "kimlerden oluşur", "kaç kişiden oluşur", "üye seçimi", "üyeler nasıl seçilir"],
457
+ "unambiguous_phrases": ["kimlerden oluşur", "kaç kişiden oluşur", "üyeleri nasıl seçilir", "kurulun oluşumu"],
458
+ "commitment_phrases": ["normatif yön kesin olarak kurulun üyeleri ve oluşumudur"],
459
+ "evidence_phrase_groups": [["kişiden oluşur", "üyeler"]],
460
+ "evidence_patterns": ["(kisi|uye).{0,100}(olusur|secil|atan)"],
461
+ "answer_policy": "retrieve",
462
+ "resolved_question": "Normatif yön kesin olarak kurulun üyeleri ve oluşumudur. İlgili kurul kimlerden ve kaç üyeden oluşur; üyeler hangi makamlarca nasıl seçilir veya atanır? Kurulun görevleri sorulmamaktadır."
463
+ },
464
+ {
465
+ "mechanism_id": "office_holder_appointment",
466
+ "label": "Rektör, dekan veya müdürün göreve gelmesi",
467
+ "definition": "Tek kişilik akademik makam sahibinin seçilmesi/atanması ve görev süresi.",
468
+ "display_order": 2,
469
+ "selection_scope": "same_parent_clause",
470
+ "terms": ["rektör nasıl atanır", "dekan nasıl atanır", "müdür nasıl atanır", "atanma", "görev süresi"],
471
+ "unambiguous_phrases": ["rektör nasıl atanır", "dekan nasıl atanır", "müdür nasıl atanır", "rektörün atanması", "dekanın atanması"],
472
+ "commitment_phrases": ["normatif yön kesin olarak akademik makam sahibinin atanmasıdır"],
473
+ "evidence_phrase_groups": [["rektör atanır", "dekan atanır", "müdür atanır"]],
474
+ "evidence_patterns": ["(rektor|dekan|mudur).{0,100}atan"],
475
+ "answer_policy": "retrieve",
476
+ "resolved_question": "Normatif yön kesin olarak akademik makam sahibinin atanmasıdır. İlgili rektör, dekan veya müdür hangi öneri, seçim veya atama süreciyle ve ne kadar süre için göreve gelir? Yardımcıların atanması ve görev-yetkiler sorulmamaktadır."
477
+ },
478
+ {
479
+ "mechanism_id": "body_or_office_duties_and_powers",
480
+ "label": "Kurul veya makamın görev, yetki ve sorumlulukları",
481
+ "definition": "Kurulun ya da akademik yöneticinin yapabileceği ve yapmakla yükümlü olduğu işlemler.",
482
+ "display_order": 3,
483
+ "terms": ["görev", "yetki", "sorumluluk", "ne yapar", "görevleri nelerdir"],
484
+ "unambiguous_phrases": ["görev yetki ve sorumlulukları", "görevleri nelerdir", "yetkileri nelerdir", "ne yapar"],
485
+ "commitment_phrases": ["normatif yön kesin olarak görev yetki ve sorumluluklardır"],
486
+ "evidence_phrase_groups": [["görev, yetki ve sorumlulukları", "görevleri;"]],
487
+ "evidence_patterns": ["gorev.{0,40}(yetki|sorumluluk)", "gorevleri"],
488
+ "answer_policy": "retrieve",
489
+ "resolved_question": "Normatif yön kesin olarak kurul veya makamın görev, yetki ve sorumluluklarıdır. İlgili kurul ya da akademik yöneticinin görev, yetki ve sorumlulukları nelerdir? Oluşumu veya atanması sorulmamaktadır."
490
+ },
491
+ {
492
+ "mechanism_id": "body_status_and_system_role",
493
+ "label": "Kurulun sistem içindeki statüsü ve yön verici rolü",
494
+ "definition": "Kurulun yükseköğretim sistemi içindeki hukuki konumu, düzenleme ve yön verme işlevi.",
495
+ "display_order": 4,
496
+ "enforce_fidelity": true,
497
+ "terms": ["sistemdeki rol", "nasıl bir rol", "işlev", "hukuki statü", "sistemde ne yapar"],
498
+ "unambiguous_phrases": ["sistemde nasıl bir rol oynar", "sistemdeki rolü", "hukuki statüsü", "temel işlevi"],
499
+ "commitment_phrases": ["normatif yön kesin olarak kurulun sistem içindeki statüsü ve yön verici rolüdür"],
500
+ "evidence_phrase_groups": [["tüm yüksek öğretimi düzenleyen", "faaliyetlerine yön veren"]],
501
+ "evidence_patterns": ["yuksekogretim kurulu.{0,160}tum yuksek ogretimi duzenleyen", "yuksekogretim kurulu.{0,220}faaliyetlerine yon veren", "yuksekogretim kurulu.{0,300}kamu tuzel kisiligine sahip"],
502
+ "answer_policy": "retrieve",
503
+ "resolved_question": "Normatif yön kesin olarak kurulun sistem içindeki hukuki statüsü ve yön verici rolüdür. İlgili kurul yükseköğretim sisteminde hangi konumda bulunur; hangi düzenleme, gözetim veya yön verme işlevini yerine getirir? Üye seçimi ve makam ataması sorulmamaktadır."
504
+ }
505
+ ]
506
+ }
507
+ ]
508
+ }
data/source/2547_clean.txt ADDED
The diff for this file is too large to render. See raw diff
 
data/source/2809_clean.txt ADDED
The diff for this file is too large to render. See raw diff
 
data/source/2914_clean.txt ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ YÜKSEKÖĞRETİM PERSONEL KANUNU
2
+
3
+ Kanun Numarası: 2914
4
+ Kabul Tarihi: 11/10/1983
5
+ Yayımlandığı Resmî Gazete: Tarih: 13/10/1983 Sayı: 18190
6
+ Yayımlandığı Düstur: Tertip: 5 Cilt: 22 Sayfa: 673
7
+
8
+ Amaç:
9
+ Madde 1 – Bu Kanunun amacı, 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununda yer alan öğretim elemanları tanımına giren personeli sınıflandırmak, aylıklarını ve ek göstergelerini düzenlemek, derece yükseltilmesi ve kademe ilerlemesinin şekil ve şartları ile, sosyal haklardan yararlanma, ek ders ücreti, üniversite, idari görev ve geliştirme ödeneklerinin miktarını tespit etmek, emekli ve yabancı öğretim elemanlarının sözleşmeli olarak çalıştırılma usul ve esaslarını belirlemektir.
10
+
11
+ Kapsam:
12
+ Madde 2 – Bu Kanun 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununa tabi üniversite öğretim elamanlarının aylık, ödenek ve sair özlük haklarını kapsar.
13
+
14
+ Öğretim elemanlarının sınıflandırılması:
15
+ Madde 3 – (Değişik: 22/2/2018-7100/13 md.)
16
+ Bu Kanuna tabi öğretim elemanlarının sınıfları ile başlangıç dereceleri aşağıda gösterilmiştir.
17
+ A) Öğretim üyeleri sınıfı:
18
+ Bu sınıf; profesörler, doçentler ve doktor öğretim üyelerinden oluşur.
19
+ a) Profesörler, profesör kadrosuna atandıkları tarihi izleyen aybaşından itibaren birinci derecenin,
20
+ b) Doçentler, doçent kadrosuna atandıkları tarihi izleyen aybaşından itibaren üçüncü derecenin,
21
+ c) Doktor öğretim üyeleri, doktor öğretim üyesi kadrosuna atandıkları tarihi izleyen aybaşından itibaren beşinci derecenin,
22
+ ilk kademe aylığını alırlar.
23
+ Yukarıdaki (a), (b) ve (c) bentlerine göre üst dereceye atananlar, bu dereceleri kazanılmış hak olarak aldıktan sonra geçirecekleri her yıl için bir kademe ilerlemesinden yararlanırlar.
24
+ B) Öğretim görevlileri sınıfı:
25
+ Bu sınıf, öğretim görevlilerinden oluşur.
26
+ C) Araştırma görevlileri sınıfı:
27
+ Bu sınıf, araştırma görevlilerinden oluşur.
28
+ Öğretim görevlileri ve araştırma görevlilerinin giriş dereceleri (657 sayılı Devlet Memurları Kanununun değişik 36 ncı maddesinin “Ortak Hükümler”i ile getirilen kademe ilerlemesi ve derece yükselmesine ilişkin hükümleri hariç) Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre tespit edilecek kazanılmış hak aylık derece ve kademelerine iki derece eklenmek suretiyle belirlenir.
29
+
30
+ Öğretim elemanlarının görev aylıkları:
31
+ Madde 4 – Öğretim elemanlarının bu Kanunun 3 üncü maddesine göre tespit edilen görev aylıkları kazanılmış hak teşkil etmeyip, aylık ödemelerine esas tutulur ve bu Kanun kapsamından çıkmaları halinde dikkate alınmaz.
32
+ Öğretim üyelerinden 3 üncü maddenin (A) bendinde gösterilen kadrolara atandıkları tarihte kazanılmış hak aylık dereceleri; unvanları için öngörülen giriş derecelerinin üzerinde olanlar ile, bu kadrolara derece yükselmesi suretiyle ulaşanlar hakkında 657 sayılı Devlet Memurları Kanununun değişik 161 inci maddesi uygulanır ve kazanılmış hak aylıkları başlangıç derecelerinin ileri kademelerine gelenlerin aylıkları, bu kademeler üzerinden ödenir. Bunların başarılı geçen hizmet süreleri, kazanılmış hak derecelerinde kademe ilerlemesi yapılmak suretiyle değerlendirilir.
33
+
34
+ Gösterge tablosu ve ek göstergeler:
35
+ Madde 5 – Üniversite öğretim elamanlarının aylıklarının hesaplanmasında, Devlet memurlarının aylıklarına esas olan gösterge tablosu ve katsayı dikkate alınır.
36
+ (Değişik: 9/4/1990- KHK -418/28 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; yeniden düzenleme: 18/5/1994-KHK/527/20 md.) Aylıkların hesabında ayrıca, bu Kanuna ekli ek gösterge cetvelinde unvan ve derecelere göre belirlenen ek gösterge rakamları dikkate alınır.
37
+ (Üçüncü fıkra mülga: 9/4/1990 - KHK - 418/28 md.)
38
+
39
+ Doktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma:
40
+ Madde 6 – Yükseköğrenimi tamamladıktan sonra, hizmete girmeden önce veya hizmet sırasında yüksek lisans veya yükseköğrenim üstü uzmanlık öğrenimi görenlere, bir kademe ilerlemesi; tıpta uzmanlık belgesi alanlara veya doktora yapanlara ve doçentlik unvanını alarak doçent kadrosuna atananlara bir derece yükselmesi uygulanır.
41
+
42
+ Derece yükseltilmesi:
43
+ Madde 7 – Öğretim elemanları;
44
+ a) Yükselebilecekleri derecede aynı unvanla boş bir kadronun bulunması,
45
+ b) Derecesi içinde en az üç yıl bulunmuş ve bu derecenin üçüncü kademe aylığını bir yıl fiilen almış olmaları,
46
+ c) Bu süre içinde olumlu sicil almış bulunmaları,
47
+ Şartıyla bir üst dereceye yükseltilebilirler.
48
+
49
+ Kademe ilerlemesi:
50
+ Madde 8 – Öğretim elemanlarının kademe ilerlemesi, bulundukları kademede en az bir yıl çalışmış ve olumlu sicil almış olmalarına ve aynı derecede ilerlenebilecek bir kademenin bulunmasına bağlıdır.
51
+ Kademe ilerlemesi, ilgililerin hak kazandıkları tarihten geçerli olmak üzere yapılır.
52
+
53
+ Derece yükseltilmesi ve kademe ilerlemesinde onay makamı:
54
+ Madde 9 – Derece yükseltilmesi ve kademe ilerlemesi, üniversite rektörünün onayı ile yapılır.
55
+
56
+ Haklardan yararlanma:
57
+ Madde 10 – Öğretim elamanları, 657 sayılı Devlet Memurları Kanununun 187- 213 üncü maddeleri ile düzenlenen sosyal hak ve yardımlardan faydalanırlar.
58
+
59
+ Ek ders ücreti:
60
+ Madde 11 – (Değişik: 27/6/1989 - KHK - 375/17 md.)
61
+ (Değişik birinci fıkra: 26/6/2001 - 4689/4 md.) 2547 Sayılı Yükseköğretim Kanununun 36 ncı maddesine göre haftalık okutulması mecburi ders yükü saati dışında, kısmi statüde bulunanlar dahil öğretim elamanlarına görev unvanlarına göre Maliye Bakanlığının görüşü üzerine Yükseköğretim Kurulu tarafından belirlenen mecburi ve isteğe bağlı dersler ve diğer faaliyetler için bu ders ve faaliyetlerin haftalık ders programında yer alması ve fiilen yapılması şartıyla en çok yirmi saate kadar, ikinci öğretimde ise en çok on saate kadar ek ders ücreti ödenir. Ders yüklerinin tamamlanmasında öncelikle normal örgün öğretimde verilen ders ve faaliyetler dikkate alınır.
62
+ (Değişik: 26/6/2001 - 4689/4 md.) Öğretim elemanlarının teorik derslerle yaptırdıkları uygulama, yönettikleri tez, seminer ve doktora çalışmalarının ve ara sınavların ne ölçüde ders yükünden sayılacağı Yükseköğretim Kurulunca belirlenir. Ara sınavlar için Yükseköğretim Kurulunca öğrenci sayısı gözönünde bulundurulmak suretiyle tespit edilecek ders yükü beş saati, diğer faaliyetler için belirlenecek ders yükü ise bir saati geçemez. Teorik dersler dışındaki faaliyetlerin ders yükünün tamamlanmasından sonraki kısmı ek ders ücretinin hesabında dikkate alınır. Ancak mecburi ders yükünün tamamlanmasında ve ek ders ücretinin hesabında, teorik dersler dışındaki faaliyetlerin haftalık en fazla on saatlik kısmı dikkate alınır, kalan kısmı ise maaş karşılığı sayılır.
63
+ (Değişik: 9/4/1990 - KHK - 418/29 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; Yeniden düzenleme: 18/5/1994-KHK/527/21 md.) Dersi veren öğretim elemanına her ders için ayrı ayrı olmak üzere yarı yıl ve yıl sonu dönemlerinde her 50 öğrenci için 300 gösterge rakamının Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımı sonucu bulunacak tutarda sınav ücreti ödenir. Öğrenci sayısının hesabında küsurlar tama iblağ edilir ve 500 öğrenciden fazlası dikkate alınmaz. Ara sınavlar ve bütünleme sınavları için sınav ücreti ödenmez.
64
+ Ek ders ücreti, aşağıdaki göstergelerin Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımından oluşur.
65
+
66
+ |Unvanı |Ek Ders Ücreti Göstergesi |
67
+ |Profesör |300 |
68
+ |Doçent |250 |
69
+ |Doktor Öğretim Üyesi |200 |
70
+ |Öğretim Görevlisi (…)4 |160 |
71
+
72
+ (Beşinci fıkra mülga: 9/4/1990 - KHK - 418/29 md.)
73
+ Yükseköğretim Kurumlarının kadrolarında olmayıp bu unvanları taşıyanlardan ders saati başına görevlendirilenlerin sosyal güvenlik kuruluşlarından almakta oldukları aylıklar bu şekilde görevlendirilmeleri nedeniyle kesilmez.
74
+ Müfredat programları uyarınca normal çalışma günlerinde çalışma saatinin bitiminden ve saat 17.00'den sonra başlayan gece öğretimi ile hafta tatili, yarı yıl veya yaz tatillerinde yapılan öğretimde yukarıdaki şekilde hesaplanan ek ders ücretleri % 60 zamlı ödenir. Yaz ve yarı yıl tatillerinde yapılan öğretim için verilecek ek ders ücretinin hesabında ders yükü dikkate alınmaz.
75
+
76
+ Üniversite Ödeneği:
77
+ Madde 12 – (Değişik: 20/3/1997 - KHK - 570/4 md.)
78
+ 657 sayılı Devlet Memurları Kanununa tabi en yüksek Devlet memuru bürüt aylık (ek gösterge dahil) tutarının;
79
+
80
+ |1) |Profesörlerden Rektör, Rektör Yardımcısı, Dekan, | |
81
+ | |Dekan Yardımcısı, Yüksekokul Müdürü olanlar ile | |
82
+ | |Profesör kadrosunda üç yılını tamamlamış bulunanlara |% 245'i, |
83
+ |2) |Diğer Profesör kadrosunda bulunanlara |% 215'i, |
84
+ |3) |Doçent kadrosunda bulunanlara |% 175'i, |
85
+ |4) |(Değişik: 22/2/2018-7100/14 md.) Doktor öğretim üyesi kadrosunda bulunanlara |% 175 |
86
+ |5); |Diğer Öğretim elemanlarından | |
87
+ | |a) Birinci dereceden aylık alanlara |% 130'u, |
88
+ | |b) İkinci dereceden aylık alanlara |% 117'si, |
89
+ | |c) Üçüncü dereceden aylık alanlara |% 110'u, |
90
+ | |d) Dördüncü ve beşinci dereceden aylık alanlara |% 104'ü, |
91
+ | |e) Diğer derecelerden aylık alanlara |% 98'i, |
92
+ |Her ay üniversite ödeneği olarak ödenir. |
93
+
94
+ Bu ödenek damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve kısmi statüde görev yapanlara ödenmez.
95
+
96
+ İdari görev ödeneği:
97
+ Madde 13 – (Değişik birinci fıkra: 20/3/1997 - KHK - 570/5 md.) Almakta oldukları aylık gösterge ve ek gösterge brüt tutarının; Rektörlere %70'i, Rektör Yardımcıları ve Dekanlara %30'u, Dekan Yardımcıları, Enstitü ve Yüksekokul Müdürleri, Konservatuar Müdürleri ile Bölüm Başkanlarına %20'si, Enstitü, Yüksekokul ve Konservatuar Müdür Yardımcılarına %15 'i idari görev ödeneği olarak ayrıca ödenir. Birden fazla idari görevi bulunanlara İdari Görev Ödeneğinden en yüksek olanı verilir.
98
+ Bu Kanuna tabi olmayan kişiler arasından tayin olunan rektörlere, 657 sayılı Devlet Memurları Kanununa göre birinci derece memur son kademe aylığı (en yüksek ek gösterge dahil) ile bunun bir katı tutarında ek ödemede bulunulur. Bu Kanunun 10 uncu maddesi hükümleri, bunlar hakkında da uygulanır. Bunlardan emekli iken atananların emekli aylıklarının ödenmesine devam olunur, diğerleri genel hükümlere göre T.C. Emekli Sandığı ile ilgilendirilir.
99
+
100
+ Geliştirme ödeneği:
101
+ Madde 14 – (Değişik: 3/4/1998 - 4359/15 md.)
102
+ Diğer yükseköğretim kurumlarına göre sosyo-ekonomik açıdan daha az gelişmiş yerlerde öğretim yapan ve/veya yeterli sayıda öğretim elemanı sağlanamayan yükseköğretim kurumları ile bunların bölümlerinde görevli öğretim elemanlarına; almakta oldukları aylık gösterge ve ek gösterge toplamının 657 sayılı Devlet Memurları Kanunu uyarınca belirlenen aylık katsayı ile çarpımı sonucu bulunacak miktarın beş katına kadar geliştirme ödeneği ödenebilir.
103
+ Geliştirme ödeneği verilecek yükseköğretim kurumları, ödeneğin verilmesine ilişkin usul ve esaslar ile oran veya miktarları, bu ödenekten yararlanma süresi ile yararlanamayacak olanlar ve diğer hususlar Cumhurbaşkanı Kararı ile tespit edilir.
104
+ Bu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.
105
+
106
+ Devlet konservatuvarlarının öğretim elemanları:
107
+ Madde 15 – (Değişik: 22/8/1989 - KHK - 378/8 md.)6
108
+ Yükseköğretim kurumlarının 657 sayılı Devlet Memurları Kanununun değişik ek geçici 16 ncı maddesinde unvanları belirtilen personeli, Yükseköğretim Kurulunun onayı ile sözleşmeli olarak istihdam edilebilir. Bu elemanların sözleşme esasları, bunlara ödenebilecek ücretin üst sınırları ve sağlanacak sosyal yardımlar ile diğer mali haklar; Cumhurbaşkanınca kararlaştırılır.
109
+
110
+ Yabancı öğretim elemanlarına ödenecek ücretler:6
111
+ Madde 16 – 2547 sayılı Yükseköğretim Kanununun 34 üncü maddesine göre sözleşmeli olarak çalıştırılacak yabancı uyruklu öğretim elemanlarına ödenecek ücret, Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulunca tespit edilir.
112
+
113
+ Emekli öğretim elemanlarının sözleşmeli istihdamı:6
114
+ Madde 17 – Üniversiteler, ihtiyaç duydukları dallarda 5434 sayılı T. C. Emekli Sandığı veya 506 sayılı Sosyal Sigortalar Kanununa göre 65 yaşını doldurmak suretiyle emekli olmuş öğretim elemanlarını emekli aylıkları kesilmeksizin ve yaş kaydı aranmaksızın sözleşme ile çalıştırabilirler. Bu gibilere ödenecek ücret Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulu tarafından tespit edilir.
115
+
116
+ Geçici görev:
117
+ Madde 18 – Öğretim elemanları ile tabi oldukları özel kanunlarda mani hüküm bulunmayan diğer kamu görevlileri, rektörün görüşü veya ilgili kuruluşun muvafakatı ile yükseköğretim üst kuruluşlarında her seferinde altı ayı geçmemek üzere geçici olarak görevlendirilebilirler.
118
+ Diğer kurum mensuplarını ilgilendiren hükümler:
119
+ Madde 19 – Bu Kanun hükümleri, 7163 sayılı Kanunla kurulan Türkiye ve Orta Doğu Amme İdaresi Enstitüsü kadrosunda çalışan profesörler, öğretim ve araştırma mütehassısları (doçentler gibi), öğretim görevlileri (…) ve asistanlar hakkında da uygulanır.
120
+ Bu Enstitüde görevli asistanlar hakkında araştırma görevlisi gibi işlem yapılır.
121
+ Bu Kanun hükümleri gereğince Yükseköğretim Kurulu, 2547 sayılı Yükseköğretim Kanunu gereğince de Üniversitelerarası Kurul tarafından alınan kararlar bu Enstitü mensuplarına da aynen uygulanır.
122
+
123
+ Uygulanacak diğer kanun hükümleri:
124
+ Madde 20 – Bu Kanunda hüküm bulunmayan hallerde 2547 sayılı Yükseköğretim Kanunu ile 657 sayılı Devlet Memurları Kanunu hükümleri uygulanır.
125
+
126
+ Kaldırılan hükümler:
127
+ Madde 21 – 25/6/1973 tarih ve 1765 sayılı Üniversite Personel Kanunu ile bu Kanunda değişiklik yapan diğer kanun hükümleri yürürlükten kaldırılmıştır.
128
+
129
+ Eğitim Öğretim Ödeneği:
130
+ Ek Madde 1 – (Ek: 24/11/1994 - 4048/4 md.; Değişik: 20/3/1997 - KHK-570/6 md.)
131
+ 4.11.1981 tarih ve 2547 sayılı Kanunun 33 üncü maddesi ve 39 uncu maddesinin ikinci fıkrası uyarınca yurtdışına gönderilenler ile anılan Kanunun 38 inci maddesine göre diğer kurum ve kuruluşlarda görevlendirilenlerden yüksek öğretim kurumlarındaki kadro görevini yapmayanlar hariç olmak üzere Yükseköğretim Kurumlarında görevli öğretim elemanlarına en yüksek Devlet memuru aylığı (ek gösterge dahil) brüt tutarının on ikide biri, her ay aylıklarla birlikte Eğitim Öğretim Ödeneği olarak ödenir. Bu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.
132
+
133
+ Makam Tazminatı:
134
+ Ek Madde 2 – (Ek: 20/3/1997 - KHK - 570/7 md.)
135
+ Bu Kanuna ekli Makam Tazminatı Cetvelinde yazılı kadro ve görev unvanlarına atananlara hizalarında belirtilen gösterge rakamlarının memur aylıklarına uygulanan katsayı ile çarpımı sonucu bulunan miktarda Makam Tazminatı ödenir. Makam Tazminatı damga vergisi hariç herhangi bir vergiye tabi tutulmaz. Makam tazminatının hakedilmesinde ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu tazminattan yararlananlara ayrıca diğer kanunlarda belirtilen Yüksek Hakimlik Tazminatı ve Makam Tazminatı ödenmez.
136
+ Yükseköğretim tazminatı:
137
+ Ek Madde 3 – (Ek: 5/11/2014-6564/1 md.)
138
+ Devlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;
139
+ a) Profesör kadrosunda bulunanlara %100’ü,
140
+ b) Doçent kadrosunda bulunanlara %100’ü,
141
+ c) Doktor Öğretim Üyesi kadrosunda bulunanlara %100’ü,
142
+ d) Araştırma Görevlisi kadrosunda bulunanlara %115’i,
143
+ e) Öğretim Görevlisi (…)10 kadrosunda bulunanlara %115’i,
144
+ f) (Mülga: 22/2/2018-7100/15 md.)
145
+ oranında her ay yükseköğretim tazminatı ödenir.
146
+ Bu tazminata hak kazanılmasında ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu maddeye göre ödenecek tazminat, damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve ilgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.
147
+
148
+ Akademik teşvik ödeneği:
149
+ Ek Madde 4 – (Ek: 5/11/2014-6564/2 md.)
150
+ Her bir takvim yılı için, bir önceki yıl, bilim, teknoloji ve sanata katkı sağlayıcı nitelikte yurt içinde veya yurt dışında sonuçlandırılan proje, araştırma, yayın, tasarım, sergi, patent ile çalışmalarına yapılan atıflar, bilim kurulu bulunan uluslararası düzeydeki toplantılarda tebliğ sunma ve almış olduğu akademik ödüller esas alınarak öğretim elemanları için yüz puan üzerinden yıllık akademik teşvik puanı hesaplanır.
151
+ Akademik teşvik puanı otuz ve üzerinde olanlara, Devlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;
152
+ a) Profesör kadrosunda bulunanlar için %100’üne,
153
+ b) Doçent kadrosunda bulunanlar için %90’ına,
154
+ c) Doktor Öğretim Üyesi kadrosunda bulunanlar için %80’ine,
155
+ d) Araştırma Görevlisi kadrosunda bulunanlar için %70’ine,
156
+ e) Öğretim Görevlisi (…)12 kadrosunda bulunanlar için %70’ine,
157
+ f) (Mülga: 22/2/2018-7100/15 md.)
158
+ aldıkları akademik teşvik puanının yüze bölünmesi suretiyle bulunacak oranın uygulanması suretiyle hesaplanan tutarda akademik teşvik ödeneği verilir.
159
+ Bu madde uyarınca yapılacak ödeme; bu Kanun uyarınca aylık ödendiği sürece ve kadrolarının bulunduğu yükseköğretim kurumları tarafından şubat ayının on beşinden itibaren on iki ay süreyle her ayın on beşinde yapılır, damga vergisi hariç herhangi bir vergiye tabi tutulmaz. İlgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.
160
+ Bilim alanlarının özellikleri ve öğretim elemanlarının unvanlarına göre akademik teşvik puanlarının hesaplanmasında esas alınacak faaliyetlerin ayrıntılı özellikleri ve bu faaliyetlerin puan karşılıkları, akademik teşvik toplam puanının %30’unu geçmemek üzere her bir akademik faaliyet türünün toplam puanın hesaplanmasındaki ağırlıkları, akademik teşvik puanının hesaplanmasına ilişkin usul ve esaslar ile bu hesaplamaları yapacak komisyonun oluşumu ile diğer hususlar Cumhurbaşkanı tarafından yürürlüğe konulan yönetmelikle belirlenir.
161
+ Bu madde hükümleri, Türk Silahlı Kuvvetleri kadrolarında bulunan öğretim elemanları hakkında da uygulanır.
162
+
163
+ Geçici Madde 1 – 657 sayılı Devlet Memurları Kanununa tabi öğretmenlerden 28/3/1993 tarih ve 2809 sayılı Kanunun geçici 3 üncü maddesi ile aynı Kanun kapsamına alınanlardan yükseköğrenim kurumlarında istihdamına ihtiyaç duyulanların bu Kanun hükümlerine göre intibakları, 30/6/1984 tarihi itibariyle yapılır.
164
+
165
+ Geçici Madde 2 – Öğretim elemanlarına ve geçici 1 inci madde kapsamına girenler bu Kanunun 12 inci maddesi uyarınca ödenecek Üniversite Ödeneğinin, 1765 sayılı Üniversite Personel Kanununun değişik 16 ncı maddesi uyarınca halen ödenmekte olan iş güçlüğü, iş riski, temininde güçlük zammı ile tamgün ödeneği tutarından az olması halinde aradaki net fark, üniversite ödeneği bu miktara ulaşıncaya kadar, hiçbir vergi ve kesintiye tabi tutulmaksızın ödenir.
166
+
167
+ Geçici Madde 3 – Öğretim elemanlarına bu Kanunun 5 inci maddesine göre verilecek ek göstergeleri, 31/12/1983 tarihinde yararlanmakta oldukları ek göstergelerden düşük olamaz.
168
+
169
+ Geçici Madde 4 – 1/1/1984 tarihinde yükseköğretim kurumlarında görev yapmakta olanlar ile bu kurumların kadroları ile ilişkileri devam eden, askerlik görevlerini yapmakta olan ve bu Kanun kapsamına giren öğretim elemanlarının intibakları, 657 sayılı Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre (657 sayılı Kanunun değişik 36 ncı maddesinin "Ortak Hükümleri" ile getirilen kademe ilerlemesi ve derece yükselmesi hariç) başlayabilecekleri derece ve kademe esas alınmak suretiyle bu Kanun ve Devlet Memurları Kanununa göre rektörlüklerce (Geçici 1 inci madde kapsamına girenler hariç) üç ay içinde yapılır.
170
+ İntibak sonucunda kazanılmış hak aylık dereceleri kadro derecelerinin üzerinde olanlara, kazanılmış hak aylıkları esas alınarak ödemede bulunulur.
171
+
172
+ Geçici Madde 5 – Geçici 4 üncü madde hükmü saklı kalmak üzere bu Kanunun yürürlüğe girdiği tarihten önce doçent kadrosuna atanmış bulunanların görev aylıkları, bir üst derecenin aynı kademesine yükseltilir.
173
+
174
+ Geçici Madde 6 – 6/11/1981 tarihinde Boğaziçi Üniversitesi ile Orta Doğu Teknik Üniversitesinde 657 sayılı Devlet Memurları Kanunu hükümlerine tabi olmadan çalışan öğretim elemanları dışında kalan personelden 657 sayılı Kanuna göre intibaklarının yapılmasını istemeyerek, 1/1/1984 tarihinden itibaren 30 gün içinde müracaat edenlere, kıdem tazminatları ödenir ve kurumları ile ilişkileri kesilir.
175
+ Bunlara ödenecek kıdem tazminatı, görevden ayrıldıkları tarihteki kazanılmış hakları dikkate alınarak hesaplanır. Ancak, bu şekilde hesaplanacak kıdem tazminatının miktarı 30/9/1982 tarihinde hak kazanabilecekleri kıdem tazminatından fazla olamaz.
176
+ 2547 sayılı Yükseköğretim Kanununun yürürlüğe girdiği 6/11/1981 tarihi ile bu Kanunun yürürlüğe girdiği tarih arasında görevden ayrılanlar hakkında da, görevden ayrıldıkları tarih dikkate alınarak yukarıdaki hükümler uygulanır.
177
+ 30/9/1982 tarihinden sonra görevden ayrılanların bu tarih ile görevden ayrıldıkları tarih arasında geçen hizmetleri, primlerinin ödenmesi kaydıyla 506 sayılı Sosyal Sigortalar Kanununa tabi olarak geçmiş sayılır.
178
+
179
+ Geçici Madde 7 – 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununun 24/9/1982 tarih ve 2708 sayılı Kanun ile değişik geçici 23 üncü maddesinde belirtilen ve sözleşmeli olarak çalıştırılan öğretim elemanları ile idari personel 30/9/1984 tarihine kadar sahip oldukları statü içinde çalıştırılırlar. Bu tarihten sonra genel hükümlere tabi olurlar.
180
+
181
+ Geçici Madde 8 – İlgili kanunda gerekli değişiklik yapılıncaya kadar, Yükseköğretim Kurulu üyeleri ile Yükseköğretim Denetleme Kurulu üyeleri, 657 sayılı Devlet Memurları Kanununun 187 - 213 üncü maddeleri ile düzenlenen sosyal haklardan yararlanırlar.
182
+ Ancak, bu üyelerden T. C. Emekli Sandığı ile Sosyal Sigortalar Kurumundan emekli olanlar bu kuruluşlarca aynı sosyal haklardan ayrıca yararlandırılmazlar.
183
+ (Ek: 26/6/1984 - KHK - 241/32 md.; Değişik: 24/11/1994 - 4048/5 md.)
184
+ T. C. Emekli Sandığı iştirakçisi olan Yükseköğretim Kurulu Başkanı, Yükseköğretim Yürütme Kurulu Üyeleri ile Yükseköğretim Denetleme Kurulu üyelerinin emekli keseneklerinin hesabında aşağıdaki ek göstergeler esas alınır. Emekli İken Yükseköğretim Yürütme Kurulu veya Yükseköğretim Denetleme Kurulu Üyeliklerine geçirilenlerin emekli aylıklarının ödenmesinde de aynı göstergeler esas alınır.
185
+
186
+ (Değişik:1/7/2022-7417/10 md.)
187
+ |Unvanı |Derece |Ek Göstergeler |
188
+ |Yükseköğretim Kurulu Başkanı |1 |8000 |
189
+ |Yükseköğretim Kurulu Üyeleri |1 |7800 |
190
+ |Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |
191
+
192
+ Geçici Madde 9 – (Ek: 14/1/1988 - KHK - 311/8 md.)
193
+ 2547 sayılı Yükseköğretim Kanunun yürürlüğe girmesinden önce Profesörlük unvanını almış olup da 78 sayılı Kanun Hükmünde Kararname ile ihdas edilen profesörlük kadrolarına atanamamış olanlar, bu Kanunun 5 inci maddesinde profesör kadrosunda bulunanlar için öngörülen ek göstergeler ile 241 sayılı Kanun Hükmünde Kararname'nin değişik 39 uncu maddesine göre profesör kadrosunda üç yılı tamamlamış olanlar için öngörülen makam tazminatından profesör kadrosuna atanma şartı aranmaksızın yararlandırılırlar.
194
+
195
+ Geçici Madde 10 – (Ek: 5/11/2014-6564/3 md.)
196
+ Ek 4 üncü maddede belirtilen yönetmelik dört ay içinde yürürlüğe konur. Ek 4 üncü maddenin birinci fıkrasına göre ilk defa akademik teşvik puanı hesaplanması, 2015 yılında yapılan faaliyetler esas alınmak suretiyle 2016 yılı için yapılır.
197
+
198
+ Yürürlük:
199
+ Madde 22 – Bu Kanunun;
200
+ a) Geçici 7 nci maddesi hükümleri; 30/9/1983 tarihinden geçerli olmak üzere yayımı tarihinde,
201
+ b) Geçici 8 inci maddesi ile atıfta bulunulan 657 sayılı Devlet Memurları Kanununun 208- 210 uncu maddelerine ilişkin hükümleri yayımı tarihinde,
202
+ c) Diğer hükümleri 1/1/1984 tarihinde,
203
+ Yürürlüğe girer.
204
+
205
+ Yürütme:
206
+ Madde 23 – Bu Kanun hükümlerini Bakanlar Kurulu yürütür.
207
+ EK GÖSTERGE CETVELİ:
208
+
209
+ (Ek:18/5/1994-KHK-527/19 md.) (Değişik:1/7/2022-7417/10 md.)
210
+
211
+ |Kadro Unvanı |Derece |Ek Göstergeler |
212
+ |a) Profesörlerden Rektör, Rektör Yardımcısı, Dekan, Dekan Yardımcısı, Yüksekokul Müdürü |1 |7000 |
213
+ |olanlar ile Profesörlük kadrosunda dört yılını tamamlamış bulunanlar | | |
214
+ |b) Profesörler |1 |5900 |
215
+ |c) Doçentler |1-3 |5400 |
216
+ |d) Doktor Öğretim Üyeleri |1-5 |4200 |
217
+ |e) Öğretim Görevlisi ve Araştırma Görevlileri |1 |4200 |
218
+ | |2 |3300 |
219
+ | |3-7 |2900 |
220
+
221
+ MAKAM TAZMİNATI CETVELİ (Ek: 20/3/1997-KHK-570/7 md.)
222
+
223
+ |Sıra No |Kadro ve Görev Unvanı |Tazminat Göstergeleri |
224
+ |1 |Rektörler |7.000 |
225
+ |2 |Profesörler (Bu kadroda üç yılını tamamlamış olmak şartıyla) |6.000 |
226
+ |3 |Profesörler |4.500 |
227
+ |4 |Doçentler (Kazanılmış hak aylıkları birinci derece olmak şartıyla) |2.000 |
data/structural/2547_articles.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2547_clauses.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2547_evidence_spans.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2809_articles.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2809_clauses.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2809_evidence_spans.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2914_articles.jsonl ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 1", "article_type": "main", "title": "Amaç", "heading_path": [], "article_heading": "Amaç", "source_span": {"char_start": 192, "char_end": 692}, "source_text": "Madde 1 – Bu Kanunun amacı, 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununda yer alan öğretim elemanları tanımına giren personeli sınıflandırmak, aylıklarını ve ek göstergelerini düzenlemek, derece yükseltilmesi ve kademe ilerlemesinin şekil ve şartları ile, sosyal haklardan yararlanma, ek ders ücreti, üniversite, idari görev ve geliştirme ödeneklerinin miktarını tespit etmek, emekli ve yabancı öğretim elemanlarının sözleşmeli olarak çalıştırılma usul ve esaslarını belirlemektir.\n\nKapsam:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
2
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 2", "article_type": "main", "title": "Kapsam", "heading_path": [], "article_heading": "Kapsam", "source_span": {"char_start": 692, "char_end": 891}, "source_text": "Madde 2 – Bu Kanun 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununa tabi üniversite öğretim elamanlarının aylık, ödenek ve sair özlük haklarını kapsar.\n\nÖğretim elemanlarının sınıflandırılması:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
3
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "article_type": "main", "title": "Öğretim elemanlarının sınıflandırılması", "heading_path": [], "article_heading": "Öğretim elemanlarının sınıflandırılması", "source_span": {"char_start": 891, "char_end": 2263}, "source_text": "Madde 3 – (Değişik: 22/2/2018-7100/13 md.)\nBu Kanuna tabi öğretim elemanlarının sınıfları ile başlangıç dereceleri aşağıda gösterilmiştir.\nA) Öğretim üyeleri sınıfı:\nBu sınıf; profesörler, doçentler ve doktor öğretim üyelerinden oluşur.\na) Profesörler, profesör kadrosuna atandıkları tarihi izleyen aybaşından itibaren birinci derecenin,\nb) Doçentler, doçent kadrosuna atandıkları tarihi izleyen aybaşından itibaren üçüncü derecenin,\nc) Doktor öğretim üyeleri, doktor öğretim üyesi kadrosuna atandıkları tarihi izleyen aybaşından itibaren beşinci derecenin,\nilk kademe aylığını alırlar.\nYukarıdaki (a), (b) ve (c) bentlerine göre üst dereceye atananlar, bu dereceleri kazanılmış hak olarak aldıktan sonra geçirecekleri her yıl için bir kademe ilerlemesinden yararlanırlar.\nB) Öğretim görevlileri sınıfı:\nBu sınıf, öğretim görevlilerinden oluşur.\nC) Araştırma görevlileri sınıfı:\nBu sınıf, araştırma görevlilerinden oluşur.\nÖğretim görevlileri ve araştırma görevlilerinin giriş dereceleri (657 sayılı Devlet Memurları Kanununun değişik 36 ncı maddesinin “Ortak Hükümler”i ile getirilen kademe ilerlemesi ve derece yükselmesine ilişkin hükümleri hariç) Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre tespit edilecek kazanılmış hak aylık derece ve kademelerine iki derece eklenmek suretiyle belirlenir.\n\nÖğretim elemanlarının görev aylıkları:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
4
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 4", "article_type": "main", "title": "Öğretim elemanlarının görev aylıkları", "heading_path": [], "article_heading": "Öğretim elemanlarının görev aylıkları", "source_span": {"char_start": 2263, "char_end": 3087}, "source_text": "Madde 4 – Öğretim elemanlarının bu Kanunun 3 üncü maddesine göre tespit edilen görev aylıkları kazanılmış hak teşkil etmeyip, aylık ödemelerine esas tutulur ve bu Kanun kapsamından çıkmaları halinde dikkate alınmaz.\nÖğretim üyelerinden 3 üncü maddenin (A) bendinde gösterilen kadrolara atandıkları tarihte kazanılmış hak aylık dereceleri; unvanları için öngörülen giriş derecelerinin üzerinde olanlar ile, bu kadrolara derece yükselmesi suretiyle ulaşanlar hakkında 657 sayılı Devlet Memurları Kanununun değişik 161 inci maddesi uygulanır ve kazanılmış hak aylıkları başlangıç derecelerinin ileri kademelerine gelenlerin aylıkları, bu kademeler üzerinden ödenir. Bunların başarılı geçen hizmet süreleri, kazanılmış hak derecelerinde kademe ilerlemesi yapılmak suretiyle değerlendirilir.\n\nGösterge tablosu ve ek göstergeler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
5
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 5", "article_type": "main", "title": "Gösterge tablosu ve ek göstergeler", "heading_path": [], "article_heading": "Gösterge tablosu ve ek göstergeler", "source_span": {"char_start": 3087, "char_end": 3670}, "source_text": "Madde 5 – Üniversite öğretim elamanlarının aylıklarının hesaplanmasında, Devlet memurlarının aylıklarına esas olan gösterge tablosu ve katsayı dikkate alınır.\n(Değişik: 9/4/1990- KHK -418/28 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; yeniden düzenleme: 18/5/1994-KHK/527/20 md.) Aylıkların hesabında ayrıca, bu Kanuna ekli ek gösterge cetvelinde unvan ve derecelere göre belirlenen ek gösterge rakamları dikkate alınır.\n(Üçüncü fıkra mülga: 9/4/1990 - KHK - 418/28 md.)\n\nDoktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
6
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 6", "article_type": "main", "title": "Doktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma", "heading_path": [], "article_heading": "Doktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma", "source_span": {"char_start": 3670, "char_end": 4018}, "source_text": "Madde 6 – Yükseköğrenimi tamamladıktan sonra, hizmete girmeden önce veya hizmet sırasında yüksek lisans veya yükseköğrenim üstü uzmanlık öğrenimi görenlere, bir kademe ilerlemesi; tıpta uzmanlık belgesi alanlara veya doktora yapanlara ve doçentlik unvanını alarak doçent kadrosuna atananlara bir derece yükselmesi uygulanır.\n\nDerece yükseltilmesi:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
7
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 7", "article_type": "main", "title": "Öğretim elemanları", "heading_path": [], "article_heading": "Öğretim elemanları", "source_span": {"char_start": 4018, "char_end": 4347}, "source_text": "Madde 7 – Öğretim elemanları;\na) Yükselebilecekleri derecede aynı unvanla boş bir kadronun bulunması,\nb) Derecesi içinde en az üç yıl bulunmuş ve bu derecenin üçüncü kademe aylığını bir yıl fiilen almış olmaları,\nc) Bu süre içinde olumlu sicil almış bulunmaları,\nŞartıyla bir üst dereceye yükseltilebilirler.\n\nKademe ilerlemesi:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
8
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 8", "article_type": "main", "title": "Kademe ilerlemesi", "heading_path": [], "article_heading": "Kademe ilerlemesi", "source_span": {"char_start": 4347, "char_end": 4690}, "source_text": "Madde 8 – Öğretim elemanlarının kademe ilerlemesi, bulundukları kademede en az bir yıl çalışmış ve olumlu sicil almış olmalarına ve aynı derecede ilerlenebilecek bir kademenin bulunmasına bağlıdır.\nKademe ilerlemesi, ilgililerin hak kazandıkları tarihten geçerli olmak üzere yapılır.\n\nDerece yükseltilmesi ve kademe ilerlemesinde onay makamı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
9
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 9", "article_type": "main", "title": "Derece yükseltilmesi ve kademe ilerlemesinde onay makamı", "heading_path": [], "article_heading": "Derece yükseltilmesi ve kademe ilerlemesinde onay makamı", "source_span": {"char_start": 4690, "char_end": 4807}, "source_text": "Madde 9 – Derece yükseltilmesi ve kademe ilerlemesi, üniversite rektörünün onayı ile yapılır.\n\nHaklardan yararlanma:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
10
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 10", "article_type": "main", "title": "Haklardan yararlanma", "heading_path": [], "article_heading": "Haklardan yararlanma", "source_span": {"char_start": 4807, "char_end": 4974}, "source_text": "Madde 10 – Öğretim elamanları, 657 sayılı Devlet Memurları Kanununun 187- 213 üncü maddeleri ile düzenlenen sosyal hak ve yardımlardan faydalanırlar.\n\nEk ders ücreti:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
11
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 11", "article_type": "main", "title": "Ek ders ücreti", "heading_path": [], "article_heading": "Ek ders ücreti", "source_span": {"char_start": 4974, "char_end": 7986}, "source_text": "Madde 11 – (Değişik: 27/6/1989 - KHK - 375/17 md.)\n(Değişik birinci fıkra: 26/6/2001 - 4689/4 md.) 2547 Sayılı Yükseköğretim Kanununun 36 ncı maddesine göre haftalık okutulması mecburi ders yükü saati dışında, kısmi statüde bulunanlar dahil öğretim elamanlarına görev unvanlarına göre Maliye Bakanlığının görüşü üzerine Yükseköğretim Kurulu tarafından belirlenen mecburi ve isteğe bağlı dersler ve diğer faaliyetler için bu ders ve faaliyetlerin haftalık ders programında yer alması ve fiilen yapılması şartıyla en çok yirmi saate kadar, ikinci öğretimde ise en çok on saate kadar ek ders ücreti ödenir. Ders yüklerinin tamamlanmasında öncelikle normal örgün öğretimde verilen ders ve faaliyetler dikkate alınır.\n(Değişik: 26/6/2001 - 4689/4 md.) Öğretim elemanlarının teorik derslerle yaptırdıkları uygulama, yönettikleri tez, seminer ve doktora çalışmaların��n ve ara sınavların ne ölçüde ders yükünden sayılacağı Yükseköğretim Kurulunca belirlenir. Ara sınavlar için Yükseköğretim Kurulunca öğrenci sayısı gözönünde bulundurulmak suretiyle tespit edilecek ders yükü beş saati, diğer faaliyetler için belirlenecek ders yükü ise bir saati geçemez. Teorik dersler dışındaki faaliyetlerin ders yükünün tamamlanmasından sonraki kısmı ek ders ücretinin hesabında dikkate alınır. Ancak mecburi ders yükünün tamamlanmasında ve ek ders ücretinin hesabında, teorik dersler dışındaki faaliyetlerin haftalık en fazla on saatlik kısmı dikkate alınır, kalan kısmı ise maaş karşılığı sayılır.\n(Değişik: 9/4/1990 - KHK - 418/29 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; Yeniden düzenleme: 18/5/1994-KHK/527/21 md.) Dersi veren öğretim elemanına her ders için ayrı ayrı olmak üzere yarı yıl ve yıl sonu dönemlerinde her 50 öğrenci için 300 gösterge rakamının Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımı sonucu bulunacak tutarda sınav ücreti ödenir. Öğrenci sayısının hesabında küsurlar tama iblağ edilir ve 500 öğrenciden fazlası dikkate alınmaz. Ara sınavlar ve bütünleme sınavları için sınav ücreti ödenmez.\nEk ders ücreti, aşağıdaki göstergelerin Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımından oluşur.\n\n|Unvanı |Ek Ders Ücreti Göstergesi |\n|Profesör |300 |\n|Doçent |250 |\n|Doktor Öğretim Üyesi |200 |\n|Öğretim Görevlisi (…)4 |160 |\n\n(Beşinci fıkra mülga: 9/4/1990 - KHK - 418/29 md.)\nYükseköğretim Kurumlarının kadrolarında olmayıp bu unvanları taşıyanlardan ders saati başına görevlendirilenlerin sosyal güvenlik kuruluşlarından almakta oldukları aylıklar bu şekilde görevlendirilmeleri nedeniyle kesilmez.\nMüfredat programları uyarınca normal çalışma günlerinde çalışma saatinin bitiminden ve saat 17.00'den sonra başlayan gece öğretimi ile hafta tatili, yarı yıl veya yaz tatillerinde yapılan öğretimde yukarıdaki şekilde hesaplanan ek ders ücretleri % 60 zamlı ödenir. Yaz ve yarı yıl tatillerinde yapılan öğretim için verilecek ek ders ücretinin hesabında ders yükü dikkate alınmaz.\n\nÜniversite Ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
12
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "article_type": "main", "title": "Üniversite Ödeneği", "heading_path": [], "article_heading": "Üniversite Ödeneği", "source_span": {"char_start": 7986, "char_end": 8999}, "source_text": "Madde 12 – (Değişik: 20/3/1997 - KHK - 570/4 md.)\n657 sayılı Devlet Memurları Kanununa tabi en yüksek Devlet memuru bürüt aylık (ek gösterge dahil) tutarının;\n\n|1) |Profesörlerden Rektör, Rektör Yardımcısı, Dekan, | |\n| |Dekan Yardımcısı, Yüksekokul Müdürü olanlar ile | |\n| |Profesör kadrosunda üç yılını tamamlamış bulunanlara |% 245'i, |\n|2) |Diğer Profesör kadrosunda bulunanlara |% 215'i, |\n|3) |Doçent kadrosunda bulunanlara |% 175'i, |\n|4) |(Değişik: 22/2/2018-7100/14 md.) Doktor öğretim üyesi kadrosunda bulunanlara |% 175 |\n|5); |Diğer Öğretim elemanlarından | |\n| |a) Birinci dereceden aylık alanlara |% 130'u, |\n| |b) İkinci dereceden aylık alanlara |% 117'si, |\n| |c) Üçüncü dereceden aylık alanlara |% 110'u, |\n| |d) Dördüncü ve beşinci dereceden aylık alanlara |% 104'ü, |\n| |e) Diğer derecelerden aylık alanlara |% 98'i, |\n|Her ay üniversite ödeneği olarak ödenir. |\n\nBu ödenek damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve kısmi statüde görev yapanlara ödenmez.\n\nİdari görev ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
13
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 13", "article_type": "main", "title": "İdari görev ödeneği", "heading_path": [], "article_heading": "İdari görev ödeneği", "source_span": {"char_start": 8999, "char_end": 9952}, "source_text": "Madde 13 – (Değişik birinci fıkra: 20/3/1997 - KHK - 570/5 md.) Almakta oldukları aylık gösterge ve ek gösterge brüt tutarının; Rektörlere %70'i, Rektör Yardımcıları ve Dekanlara %30'u, Dekan Yardımcıları, Enstitü ve Yüksekokul Müdürleri, Konservatuar Müdürleri ile Bölüm Başkanlarına %20'si, Enstitü, Yüksekokul ve Konservatuar Müdür Yardımcılarına %15 'i idari görev ödeneği olarak ayrıca ödenir. Birden fazla idari görevi bulunanlara İdari Görev Ödeneğinden en yüksek olanı verilir.\nBu Kanuna tabi olmayan kişiler arasından tayin olunan rektörlere, 657 sayılı Devlet Memurları Kanununa göre birinci derece memur son kademe aylığı (en yüksek ek gösterge dahil) ile bunun bir katı tutarında ek ödemede bulunulur. Bu Kanunun 10 uncu maddesi hükümleri, bunlar hakkında da uygulanır. Bunlardan emekli iken atananların emekli aylıklarının ödenmesine devam olunur, diğerleri genel hükümlere göre T.C. Emekli Sandığı ile ilgilendirilir.\n\nGeliştirme ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
14
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 14", "article_type": "main", "title": "Geliştirme ödeneği", "heading_path": [], "article_heading": "Geliştirme ödeneği", "source_span": {"char_start": 9952, "char_end": 10811}, "source_text": "Madde 14 – (Değişik: 3/4/1998 - 4359/15 md.)\nDiğer yükseköğretim kurumlarına göre sosyo-ekonomik açıdan daha az gelişmiş yerlerde öğretim yapan ve/veya yeterli sayıda öğretim elemanı sağlanamayan yükseköğretim kurumları ile bunların bölümlerinde görevli öğretim elemanlarına; almakta oldukları aylık gösterge ve ek gösterge toplamının 657 sayılı Devlet Memurları Kanunu uyarınca belirlenen aylık katsayı ile çarpımı sonucu bulunacak miktarın beş katına kadar geliştirme ödeneği ödenebilir.\nGeliştirme ödeneği verilecek yükseköğretim kurumları, ödeneğin verilmesine ilişkin usul ve esaslar ile oran veya miktarları, bu ödenekten yararlanma süresi ile yararlanamayacak olanlar ve diğer hususlar Cumhurbaşkanı Kararı ile tespit edilir.\nBu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.\n\nDevlet konservatuvarlarının öğretim elemanları:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
15
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 15", "article_type": "main", "title": "Devlet konservatuvarlarının öğretim elemanları", "heading_path": [], "article_heading": "Devlet konservatuvarlarının öğretim elemanları", "source_span": {"char_start": 10811, "char_end": 11283}, "source_text": "Madde 15 – (Değişik: 22/8/1989 - KHK - 378/8 md.)6\nYükseköğretim kurumlarının 657 sayılı Devlet Memurları Kanununun değişik ek geçici 16 ncı maddesinde unvanları belirtilen personeli, Yükseköğretim Kurulunun onayı ile sözleşmeli olarak istihdam edilebilir. Bu elemanların sözleşme esasları, bunlara ödenebilecek ücretin üst sınırları ve sağlanacak sosyal yardımlar ile diğer mali haklar; Cumhurbaşkanınca kararlaştırılır.\n\nYabancı öğretim elemanlarına ödenecek ücretler:6", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
16
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 16", "article_type": "main", "title": "Yabancı öğretim elemanlarına ödenecek ücretler:6", "heading_path": [], "article_heading": "Yabancı öğretim elemanlarına ödenecek ücretler:6", "source_span": {"char_start": 11283, "char_end": 11579}, "source_text": "Madde 16 – 2547 sayılı Yükseköğretim Kanununun 34 üncü maddesine göre sözleşmeli olarak çalıştırılacak yabancı uyruklu öğretim elemanlarına ödenecek ücret, Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulunca tespit edilir.\n\nEmekli öğretim elemanlarının sözleşmeli istihdamı:6", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
17
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 17", "article_type": "main", "title": "Emekli öğretim elemanlarının sözleşmeli istihdamı:6", "heading_path": [], "article_heading": "Emekli öğretim elemanlarının sözleşmeli istihdamı:6", "source_span": {"char_start": 11579, "char_end": 12004}, "source_text": "Madde 17 – Üniversiteler, ihtiyaç duydukları dallarda 5434 sayılı T. C. Emekli Sandığı veya 506 sayılı Sosyal Sigortalar Kanununa göre 65 yaşını doldurmak suretiyle emekli olmuş öğretim elemanlarını emekli aylıkları kesilmeksizin ve yaş kaydı aranmaksızın sözleşme ile çalıştırabilirler. Bu gibilere ödenecek ücret Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulu tarafından tespit edilir.\n\nGeçici görev:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
18
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 18", "article_type": "main", "title": "Geçici görev", "heading_path": [], "article_heading": "Geçici görev", "source_span": {"char_start": 12004, "char_end": 12325}, "source_text": "Madde 18 – Öğretim elemanları ile tabi oldukları özel kanunlarda mani hüküm bulunmayan diğer kamu görevlileri, rektörün görüşü veya ilgili kuruluşun muvafakatı ile yükseköğretim üst kuruluşlarında her seferinde altı ayı geçmemek üzere geçici olarak görevlendirilebilirler.\nDiğer kurum mensuplarını ilgilendiren hükümler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
19
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 19", "article_type": "main", "title": "Diğer kurum mensuplarını ilgilendiren hükümler", "heading_path": [], "article_heading": "Diğer kurum mensuplarını ilgilendiren hükümler", "source_span": {"char_start": 12325, "char_end": 12885}, "source_text": "Madde 19 – Bu Kanun hükümleri, 7163 sayılı Kanunla kurulan Türkiye ve Orta Doğu Amme İdaresi Enstitüsü kadrosunda çalışan profesörler, öğretim ve araştırma mütehassısları (doçentler gibi), öğretim görevlileri (…) ve asistanlar hakkında da uygulanır.\nBu Enstitüde görevli asistanlar hakkında araştırma görevlisi gibi işlem yapılır.\nBu Kanun hükümleri gereğince Yükseköğretim Kurulu, 2547 sayılı Yükseköğretim Kanunu gereğince de Üniversitelerarası Kurul tarafından alınan kararlar bu Enstitü mensuplarına da aynen uygulanır.\n\nUygulanacak diğer kanun hükümleri:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
20
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 20", "article_type": "main", "title": "Uygulanacak diğer kanun hükümleri", "heading_path": [], "article_heading": "Uygulanacak diğer kanun hükümleri", "source_span": {"char_start": 12885, "char_end": 13048}, "source_text": "Madde 20 – Bu Kanunda hüküm bulunmayan hallerde 2547 sayılı Yükseköğretim Kanunu ile 657 sayılı Devlet Memurları Kanunu hükümleri uygulanır.\n\nKaldırılan hükümler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
21
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 21", "article_type": "main", "title": "Kaldırılan hükümler", "heading_path": [], "article_heading": "Kaldırılan hükümler", "source_span": {"char_start": 13048, "char_end": 13224}, "source_text": "Madde 21 – 25/6/1973 tarih ve 1765 sayılı Üniversite Personel Kanunu ile bu Kanunda değişiklik yapan diğer kanun hükümleri yürürlükten kaldırılmıştır.\n\nEğitim Öğretim Ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
22
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 1", "article_type": "additional", "title": "Eğitim Öğretim Ödeneği", "heading_path": [], "article_heading": "Eğitim Öğretim Ödeneği", "source_span": {"char_start": 13224, "char_end": 13893}, "source_text": "Ek Madde 1 – (Ek: 24/11/1994 - 4048/4 md.; Değişik: 20/3/1997 - KHK-570/6 md.)\n4.11.1981 tarih ve 2547 sayılı Kanunun 33 üncü maddesi ve 39 uncu maddesinin ikinci fıkrası uyarınca yurtdışına gönderilenler ile anılan Kanunun 38 inci maddesine göre diğer kurum ve kuruluşlarda görevlendirilenlerden yüksek öğretim kurumlarındaki kadro görevini yapmayanlar hariç olmak üzere Yükseköğretim Kurumlarında görevli öğretim elemanlarına en yüksek Devlet memuru aylığı (ek gösterge dahil) brüt tutarının on ikide biri, her ay aylıklarla birlikte Eğitim Öğretim Ödeneği olarak ödenir. Bu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.\n\nMakam Tazminatı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
23
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 2", "article_type": "additional", "title": "Makam Tazminatı", "heading_path": [], "article_heading": "Makam Tazminatı", "source_span": {"char_start": 13893, "char_end": 14471}, "source_text": "Ek Madde 2 – (Ek: 20/3/1997 - KHK - 570/7 md.)\nBu Kanuna ekli Makam Tazminatı Cetvelinde yazılı kadro ve görev unvanlarına atananlara hizalarında belirtilen gösterge rakamlarının memur aylıklarına uygulanan katsayı ile çarpımı sonucu bulunan miktarda Makam Tazminatı ödenir. Makam Tazminatı damga vergisi hariç herhangi bir vergiye tabi tutulmaz. Makam tazminatının hakedilmesinde ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu tazminattan yararlananlara ayrıca diğer kanunlarda belirtilen Yüksek Hakimlik Tazminatı ve Makam Tazminatı ödenmez.\nYükseköğretim tazminatı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
24
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "article_type": "additional", "title": "Yükseköğretim tazminatı", "heading_path": [], "article_heading": "Yükseköğretim tazminatı", "source_span": {"char_start": 14471, "char_end": 15338}, "source_text": "Ek Madde 3 – (Ek: 5/11/2014-6564/1 md.)\nDevlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;\na) Profesör kadrosunda bulunanlara %100’ü,\nb) Doçent kadrosunda bulunanlara %100’ü,\nc) Doktor Öğretim Üyesi kadrosunda bulunanlara %100’ü,\nd) Araştırma Görevlisi kadrosunda bulunanlara %115’i,\ne) Öğretim Görevlisi (…)10 kadrosunda bulunanlara %115’i,\nf) (Mülga: 22/2/2018-7100/15 md.)\noranında her ay yükseköğretim tazminatı ödenir.\nBu tazminata hak kazanılmasında ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu maddeye göre ödenecek tazminat, damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve ilgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.\n\nAkademik teşvik ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
25
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "article_type": "additional", "title": "Akademik teşvik ödeneği", "heading_path": [], "article_heading": "Akademik teşvik ödeneği", "source_span": {"char_start": 15338, "char_end": 17523}, "source_text": "Ek Madde 4 – (Ek: 5/11/2014-6564/2 md.)\nHer bir takvim yılı için, bir önceki yıl, bilim, teknoloji ve sanata katkı sağlayıcı nitelikte yurt içinde veya yurt dışında sonuçlandırılan proje, araştırma, yayın, tasarım, sergi, patent ile çalışmalarına yapılan atıflar, bilim kurulu bulunan uluslararası düzeydeki toplantılarda tebliğ sunma ve almış olduğu akademik ödüller esas alınarak öğretim elemanları için yüz puan üzerinden yıllık akademik teşvik puanı hesaplanır.\nAkademik teşvik puanı otuz ve üzerinde olanlara, Devlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;\na) Profesör kadrosunda bulunanlar için %100’üne,\nb) Doçent kadrosunda bulunanlar için %90’ına,\nc) Doktor Öğretim Üyesi kadrosunda bulunanlar için %80’ine,\nd) Araştırma Görevlisi kadrosunda bulunanlar için %70’ine,\ne) Öğretim Görevlisi (…)12 kadrosunda bulunanlar için %70’ine,\nf) (Mülga: 22/2/2018-7100/15 md.)\naldıkları akademik teşvik puanının yüze bölünmesi suretiyle bulunacak oranın uygulanması suretiyle hesaplanan tutarda akademik teşvik ödeneği verilir.\nBu madde uyarınca yapılacak ödeme; bu Kanun uyarınca aylık ödendiği sürece ve kadrolarının bulunduğu yükseköğretim kurumları tarafından şubat ayının on beşinden itibaren on iki ay süreyle her ayın on beşinde yapılır, damga vergisi hariç herhangi bir vergiye tabi tutulmaz. İlgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.\nBilim alanlarının özellikleri ve öğretim elemanlarının unvanlarına göre akademik teşvik puanlarının hesaplanmasında esas alınacak faaliyetlerin ayrıntılı özellikleri ve bu faaliyetlerin puan karşılıkları, akademik teşvik toplam puanının %30’unu geçmemek üzere her bir akademik faaliyet türünün toplam puanın hesaplanmasındaki ağırlıkları, akademik teşvik puanının hesaplanmasına ilişkin usul ve esaslar ile bu hesaplamaları yapacak komisyonun oluşumu ile diğer hususlar Cumhurbaşkanı tarafından yürürlüğe konulan yönetmelikle belirlenir.\nBu madde hükümleri, Türk Silahlı Kuvvetleri kadrolarında bulunan öğretim elemanları hakkında da uygulanır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
26
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 1", "article_type": "temporary", "title": "f) (Mülga: 22/2/2018-7100/15 md.)", "heading_path": [], "article_heading": "f) (Mülga: 22/2/2018-7100/15 md.)", "source_span": {"char_start": 17523, "char_end": 17833}, "source_text": "Geçici Madde 1 – 657 sayılı Devlet Memurları Kanununa tabi öğretmenlerden 28/3/1993 tarih ve 2809 sayılı Kanunun geçici 3 üncü maddesi ile aynı Kanun kapsamına alınanlardan yükseköğrenim kurumlarında istihdamına ihtiyaç duyulanların bu Kanun hükümlerine göre intibakları, 30/6/1984 tarihi itibariyle yapılır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
27
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 2", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 17833, "char_end": 18289}, "source_text": "Geçici Madde 2 – Öğretim elemanlarına ve geçici 1 inci madde kapsamına girenler bu Kanunun 12 inci maddesi uyarınca ödenecek Üniversite Ödeneğinin, 1765 sayılı Üniversite Personel Kanununun değişik 16 ncı maddesi uyarınca halen ödenmekte olan iş güçlüğü, iş riski, temininde güçlük zammı ile tamgün ödeneği tutarından az olması halinde aradaki net fark, üniversite ödeneği bu miktara ulaşıncaya kadar, hiçbir vergi ve kesintiye tabi tutulmaksızın ödenir.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
28
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 3", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 18289, "char_end": 18465}, "source_text": "Geçici Madde 3 – Öğretim elemanlarına bu Kanunun 5 inci maddesine göre verilecek ek göstergeleri, 31/12/1983 tarihinde yararlanmakta oldukları ek göstergelerden düşük olamaz.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
29
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 4", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 18465, "char_end": 19252}, "source_text": "Geçici Madde 4 – 1/1/1984 tarihinde yükseköğretim kurumlarında görev yapmakta olanlar ile bu kurumların kadroları ile ilişkileri devam eden, askerlik görevlerini yapmakta olan ve bu Kanun kapsamına giren öğretim elemanlarının intibakları, 657 sayılı Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre (657 sayılı Kanunun değişik 36 ncı maddesinin \"Ortak Hükümleri\" ile getirilen kademe ilerlemesi ve derece yükselmesi hariç) başlayabilecekleri derece ve kademe esas alınmak suretiyle bu Kanun ve Devlet Memurları Kanununa göre rektörlüklerce (Geçici 1 inci madde kapsamına girenler hariç) üç ay içinde yapılır.\nİntibak sonucunda kazanılmış hak aylık dereceleri kadro derecelerinin üzerinde olanlara, kazanılmış hak aylıkları esas alınarak ödemede bulunulur.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
30
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 5", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 19252, "char_end": 19460}, "source_text": "Geçici Madde 5 – Geçici 4 üncü madde hükmü saklı kalmak üzere bu Kanunun yürürlüğe girdiği tarihten önce doçent kadrosuna atanmış bulunanların görev aylıkları, bir üst derecenin aynı kademesine yükseltilir.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
31
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 6", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 19460, "char_end": 20547}, "source_text": "Geçici Madde 6 – 6/11/1981 tarihinde Boğaziçi Üniversitesi ile Orta Doğu Teknik Üniversitesinde 657 sayılı Devlet Memurları Kanunu hükümlerine tabi olmadan çalışan öğretim elemanları dışında kalan personelden 657 sayılı Kanuna göre intibaklarının yapılmasını istemeyerek, 1/1/1984 tarihinden itibaren 30 gün içinde müracaat edenlere, kıdem tazminatları ödenir ve kurumları ile ilişkileri kesilir.\nBunlara ödenecek kıdem tazminatı, görevden ayrıldıkları tarihteki kazanılmış hakları dikkate alınarak hesaplanır. Ancak, bu şekilde hesaplanacak kıdem tazminatının miktarı 30/9/1982 tarihinde hak kazanabilecekleri kıdem tazminatından fazla olamaz.\n2547 sayılı Yükseköğretim Kanununun yürürlüğe girdiği 6/11/1981 tarihi ile bu Kanunun yürürlüğe girdiği tarih arasında görevden ayrılanlar hakkında da, görevden ayrıldıkları tarih dikkate alınarak yukarıdaki hükümler uygulanır.\n30/9/1982 tarihinden sonra görevden ayrılanların bu tarih ile görevden ayrıldıkları tarih arasında geçen hizmetleri, primlerinin ödenmesi kaydıyla 506 sayılı Sosyal Sigortalar Kanununa tabi olarak geçmiş sayılır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
32
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 7", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 20547, "char_end": 20897}, "source_text": "Geçici Madde 7 – 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununun 24/9/1982 tarih ve 2708 sayılı Kanun ile değişik geçici 23 üncü maddesinde belirtilen ve sözleşmeli olarak çalıştırılan öğretim elemanları ile idari personel 30/9/1984 tarihine kadar sahip oldukları statü içinde çalıştırılırlar. Bu tarihten sonra genel hükümlere tabi olurlar.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
33
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "article_type": "temporary", "title": "", "heading_path": [], "article_heading": "", "source_span": {"char_start": 20897, "char_end": 21963}, "source_text": "Geçici Madde 8 – İlgili kanunda gerekli değişiklik yapılıncaya kadar, Yükseköğretim Kurulu üyeleri ile Yükseköğretim Denetleme Kurulu üyeleri, 657 sayılı Devlet Memurları Kanununun 187 - 213 üncü maddeleri ile düzenlenen sosyal haklardan yararlanırlar.\nAncak, bu üyelerden T. C. Emekli Sandığı ile Sosyal Sigortalar Kurumundan emekli olanlar bu kuruluşlarca aynı sosyal haklardan ayrıca yararlandırılmazlar.\n(Ek: 26/6/1984 - KHK - 241/32 md.; Değişik: 24/11/1994 - 4048/5 md.)\nT. C. Emekli Sandığı iştirakçisi olan Yükseköğretim Kurulu Başkanı, Yükseköğretim Yürütme Kurulu Üyeleri ile Yükseköğretim Denetleme Kurulu üyelerinin emekli keseneklerinin hesabında aşağıdaki ek göstergeler esas alınır. Emekli İken Yükseköğretim Yürütme Kurulu veya Yükseköğretim Denetleme Kurulu Üyeliklerine geçirilenlerin emekli aylıklarının ödenmesinde de aynı göstergeler esas alınır.\n\n(Değişik:1/7/2022-7417/10 md.)\n|Unvanı |Derece |Ek Göstergeler |\n|Yükseköğretim Kurulu Başkanı |1 |8000 |\n|Yükseköğretim Kurulu Üyeleri |1 |7800 |\n|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
34
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 9", "article_type": "temporary", "title": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "heading_path": [], "article_heading": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "source_span": {"char_start": 21963, "char_end": 22519}, "source_text": "Geçici Madde 9 – (Ek: 14/1/1988 - KHK - 311/8 md.)\n2547 sayılı Yükseköğretim Kanunun yürürlüğe girmesinden önce Profesörlük unvanını almış olup da 78 sayılı Kanun Hükmünde Kararname ile ihdas edilen profesörlük kadrolarına atanamamış olanlar, bu Kanunun 5 inci maddesinde profesör kadrosunda bulunanlar için öngörülen ek göstergeler ile 241 sayılı Kanun Hükmünde Kararname'nin değişik 39 uncu maddesine göre profesör kadrosunda üç yılı tamamlamış olanlar için öngörülen makam tazminatından profesör kadrosuna atanma şartı aranmaksızın yararlandırılırlar.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
35
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 10", "article_type": "temporary", "title": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "heading_path": [], "article_heading": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "source_span": {"char_start": 22519, "char_end": 22815}, "source_text": "Geçici Madde 10 – (Ek: 5/11/2014-6564/3 md.)\nEk 4 üncü maddede belirtilen yönetmelik dört ay içinde yürürlüğe konur. Ek 4 üncü maddenin birinci fıkrasına göre ilk defa akademik teşvik puanı hesaplanması, 2015 yılında yapılan faaliyetler esas alınmak suretiyle 2016 yılı için yapılır.\n\nYürürlük:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
36
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 22", "article_type": "main", "title": "Bu Kanunun", "heading_path": [], "article_heading": "Bu Kanunun", "source_span": {"char_start": 22815, "char_end": 23144}, "source_text": "Madde 22 – Bu Kanunun;\na) Geçici 7 nci maddesi hükümleri; 30/9/1983 tarihinden geçerli olmak üzere yayımı tarihinde,\nb) Geçici 8 inci maddesi ile atıfta bulunulan 657 sayılı Devlet Memurları Kanununun 208- 210 uncu maddelerine ilişkin hükümleri yayımı tarihinde,\nc) Diğer hükümleri 1/1/1984 tarihinde,\nYürürlüğe girer.\n\nYürütme:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
37
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 23", "article_type": "main", "title": "Yürütme", "heading_path": [], "article_heading": "Yürütme", "source_span": {"char_start": 23144, "char_end": 23201}, "source_text": "Madde 23 – Bu Kanun hükümlerini Bakanlar Kurulu yürütür.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
data/structural/2914_clauses.jsonl ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 1", "clause_id": "madde_1__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 499, "document_char_start": 192, "document_char_end": 691}, "source_text": "Madde 1 – Bu Kanunun amacı, 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununda yer alan öğretim elemanları tanımına giren personeli sınıflandırmak, aylıklarını ve ek göstergelerini düzenlemek, derece yükseltilmesi ve kademe ilerlemesinin şekil ve şartları ile, sosyal haklardan yararlanma, ek ders ücreti, üniversite, idari görev ve geliştirme ödeneklerinin miktarını tespit etmek, emekli ve yabancı öğretim elemanlarının sözleşmeli olarak çalıştırılma usul ve esaslarını belirlemektir.\n\nKapsam:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Amaç"}
2
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 2", "clause_id": "madde_2__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 198, "document_char_start": 692, "document_char_end": 890}, "source_text": "Madde 2 – Bu Kanun 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununa tabi üniversite öğretim elamanlarının aylık, ödenek ve sair özlük haklarını kapsar.\n\nÖğretim elemanlarının sınıflandırılması:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Kapsam"}
3
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 139, "document_char_start": 891, "document_char_end": 1030}, "source_text": "Madde 3 – (Değişik: 22/2/2018-7100/13 md.)\nBu Kanuna tabi öğretim elemanlarının sınıfları ile başlangıç dereceleri aşağıda gösterilmiştir.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
4
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__002_a", "label": "A", "source_span": {"article_char_start": 139, "article_char_end": 237, "document_char_start": 1030, "document_char_end": 1128}, "source_text": "A) Öğretim üyeleri sınıfı:\nBu sınıf; profesörler, doçentler ve doktor öğretim üyelerinden oluşur.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
5
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__003_a", "label": "a", "source_span": {"article_char_start": 237, "article_char_end": 338, "document_char_start": 1128, "document_char_end": 1229}, "source_text": "a) Profesörler, profesör kadrosuna atandıkları tarihi izleyen aybaşından itibaren birinci derecenin,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
6
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__004_b", "label": "b", "source_span": {"article_char_start": 338, "article_char_end": 434, "document_char_start": 1229, "document_char_end": 1325}, "source_text": "b) Doçentler, doçent kadrosuna atandıkları tarihi izleyen aybaşından itibaren üçüncü derecenin,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
7
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__005_c", "label": "c", "source_span": {"article_char_start": 434, "article_char_end": 604, "document_char_start": 1325, "document_char_end": 1495}, "source_text": "c) Doktor öğretim üyeleri, doktor öğretim üyesi kadrosuna atandıkları tarihi izleyen aybaşından itibaren beşinci derecenin,\nilk kademe aylığını alırlar.\nYukarıdaki (a), (", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
8
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__006_b", "label": "b", "source_span": {"article_char_start": 604, "article_char_end": 611, "document_char_start": 1495, "document_char_end": 1502}, "source_text": "b) ve (", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
9
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__007_c", "label": "c", "source_span": {"article_char_start": 611, "article_char_end": 773, "document_char_start": 1502, "document_char_end": 1664}, "source_text": "c) bentlerine göre üst dereceye atananlar, bu dereceleri kazanılmış hak olarak aldıktan sonra geçirecekleri her yıl için bir kademe ilerlemesinden yararlanırlar.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
10
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__008_b", "label": "B", "source_span": {"article_char_start": 773, "article_char_end": 846, "document_char_start": 1664, "document_char_end": 1737}, "source_text": "B) Öğretim görevlileri sınıfı:\nBu sınıf, öğretim görevlilerinden oluşur.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
11
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 3", "clause_id": "madde_3__009_c", "label": "C", "source_span": {"article_char_start": 846, "article_char_end": 1371, "document_char_start": 1737, "document_char_end": 2262}, "source_text": "C) Araştırma görevlileri sınıfı:\nBu sınıf, araştırma görevlilerinden oluşur.\nÖğretim görevlileri ve araştırma görevlilerinin giriş dereceleri (657 sayılı Devlet Memurları Kanununun değişik 36 ncı maddesinin “Ortak Hükümler”i ile getirilen kademe ilerlemesi ve derece yükselmesine ilişkin hükümleri hariç) Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre tespit edilecek kazanılmış hak aylık derece ve kademelerine iki derece eklenmek suretiyle belirlenir.\n\nÖğretim elemanlarının görev aylıkları:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının sınıflandırılması"}
12
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 4", "clause_id": "madde_4__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 253, "document_char_start": 2263, "document_char_end": 2516}, "source_text": "Madde 4 – Öğretim elemanlarının bu Kanunun 3 üncü maddesine göre tespit edilen görev aylıkları kazanılmış hak teşkil etmeyip, aylık ödemelerine esas tutulur ve bu Kanun kapsamından çıkmaları halinde dikkate alınmaz.\nÖğretim üyelerinden 3 üncü maddenin (", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının görev aylıkları"}
13
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 4", "clause_id": "madde_4__002_a", "label": "A", "source_span": {"article_char_start": 253, "article_char_end": 823, "document_char_start": 2516, "document_char_end": 3086}, "source_text": "A) bendinde gösterilen kadrolara atandıkları tarihte kazanılmış hak aylık dereceleri; unvanları için öngörülen giriş derecelerinin üzerinde olanlar ile, bu kadrolara derece yükselmesi suretiyle ulaşanlar hakkında 657 sayılı Devlet Memurları Kanununun değişik 161 inci maddesi uygulanır ve kazanılmış hak aylıkları başlangıç derecelerinin ileri kademelerine gelenlerin aylıkları, bu kademeler üzerinden ödenir. Bunların başarılı geçen hizmet süreleri, kazanılmış hak derecelerinde kademe ilerlemesi yapılmak suretiyle değerlendirilir.\n\nGösterge tablosu ve ek göstergeler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanlarının görev aylıkları"}
14
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 5", "clause_id": "madde_5__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 582, "document_char_start": 3087, "document_char_end": 3669}, "source_text": "Madde 5 – Üniversite öğretim elamanlarının aylıklarının hesaplanmasında, Devlet memurlarının aylıklarına esas olan gösterge tablosu ve katsayı dikkate alınır.\n(Değişik: 9/4/1990- KHK -418/28 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; yeniden düzenleme: 18/5/1994-KHK/527/20 md.) Aylıkların hesabında ayrıca, bu Kanuna ekli ek gösterge cetvelinde unvan ve derecelere göre belirlenen ek gösterge rakamları dikkate alınır.\n(Üçüncü fıkra mülga: 9/4/1990 - KHK - 418/28 md.)\n\nDoktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Gösterge tablosu ve ek göstergeler"}
15
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 6", "clause_id": "madde_6__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 347, "document_char_start": 3670, "document_char_end": 4017}, "source_text": "Madde 6 – Yükseköğrenimi tamamladıktan sonra, hizmete girmeden önce veya hizmet sırasında yüksek lisans veya yükseköğrenim üstü uzmanlık öğrenimi görenlere, bir kademe ilerlemesi; tıpta uzmanlık belgesi alanlara veya doktora yapanlara ve doçentlik unvanını alarak doçent kadrosuna atananlara bir derece yükselmesi uygulanır.\n\nDerece yükseltilmesi:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Doktora veya tıpta uzmanlık öğrenimi, doçent kadrosuna atanma"}
16
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 7", "clause_id": "madde_7__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 30, "document_char_start": 4018, "document_char_end": 4048}, "source_text": "Madde 7 – Öğretim elemanları;", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanları"}
17
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 7", "clause_id": "madde_7__002_a", "label": "a", "source_span": {"article_char_start": 30, "article_char_end": 102, "document_char_start": 4048, "document_char_end": 4120}, "source_text": "a) Yükselebilecekleri derecede aynı unvanla boş bir kadronun bulunması,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanları"}
18
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 7", "clause_id": "madde_7__003_b", "label": "b", "source_span": {"article_char_start": 102, "article_char_end": 213, "document_char_start": 4120, "document_char_end": 4231}, "source_text": "b) Derecesi içinde en az üç yıl bulunmuş ve bu derecenin üçüncü kademe aylığını bir yıl fiilen almış olmaları,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanları"}
19
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 7", "clause_id": "madde_7__004_c", "label": "c", "source_span": {"article_char_start": 213, "article_char_end": 328, "document_char_start": 4231, "document_char_end": 4346}, "source_text": "c) Bu süre içinde olumlu sicil almış bulunmaları,\nŞartıyla bir üst dereceye yükseltilebilirler.\n\nKademe ilerlemesi:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Öğretim elemanları"}
20
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 8", "clause_id": "madde_8__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 342, "document_char_start": 4347, "document_char_end": 4689}, "source_text": "Madde 8 – Öğretim elemanlarının kademe ilerlemesi, bulundukları kademede en az bir yıl çalışmış ve olumlu sicil almış olmalarına ve aynı derecede ilerlenebilecek bir kademenin bulunmasına bağlıdır.\nKademe ilerlemesi, ilgililerin hak kazandıkları tarihten geçerli olmak üzere yapılır.\n\nDerece yükseltilmesi ve kademe ilerlemesinde onay makamı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Kademe ilerlemesi"}
21
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 9", "clause_id": "madde_9__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 116, "document_char_start": 4690, "document_char_end": 4806}, "source_text": "Madde 9 – Derece yükseltilmesi ve kademe ilerlemesi, üniversite rektörünün onayı ile yapılır.\n\nHaklardan yararlanma:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Derece yükseltilmesi ve kademe ilerlemesinde onay makamı"}
22
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 10", "clause_id": "madde_10__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 166, "document_char_start": 4807, "document_char_end": 4973}, "source_text": "Madde 10 – Öğretim elamanları, 657 sayılı Devlet Memurları Kanununun 187- 213 üncü maddeleri ile düzenlenen sosyal hak ve yardımlardan faydalanırlar.\n\nEk ders ücreti:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Haklardan yararlanma"}
23
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 11", "clause_id": "madde_11__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 3011, "document_char_start": 4974, "document_char_end": 7985}, "source_text": "Madde 11 – (Değişik: 27/6/1989 - KHK - 375/17 md.)\n(Değişik birinci fıkra: 26/6/2001 - 4689/4 md.) 2547 Sayılı Yükseköğretim Kanununun 36 ncı maddesine göre haftalık okutulması mecburi ders yükü saati dışında, kısmi statüde bulunanlar dahil öğretim elamanlarına görev unvanlarına göre Maliye Bakanlığının görüşü üzerine Yükseköğretim Kurulu tarafından belirlenen mecburi ve isteğe bağlı dersler ve diğer faaliyetler için bu ders ve faaliyetlerin haftalık ders programında yer alması ve fiilen yapılması şartıyla en çok yirmi saate kadar, ikinci öğretimde ise en çok on saate kadar ek ders ücreti ödenir. Ders yüklerinin tamamlanmasında öncelikle normal örgün öğretimde verilen ders ve faaliyetler dikkate alınır.\n(Değişik: 26/6/2001 - 4689/4 md.) Öğretim elemanlarının teorik derslerle yaptırdıkları uygulama, yönettikleri tez, seminer ve doktora çalışmalarının ve ara sınavların ne ölçüde ders yükünden sayılacağı Yükseköğretim Kurulunca belirlenir. Ara sınavlar için Yükseköğretim Kurulunca öğrenci sayısı gözönünde bulundurulmak suretiyle tespit edilecek ders yükü beş saati, diğer faaliyetler için belirlenecek ders yükü ise bir saati geçemez. Teorik dersler dışındaki faaliyetlerin ders yükünün tamamlanmasından sonraki kısmı ek ders ücretinin hesabında dikkate alınır. Ancak mecburi ders yükünün tamamlanmasında ve ek ders ücretinin hesabında, teorik dersler dışındaki faaliyetlerin haftalık en fazla on saatlik kısmı dikkate alınır, kalan kısmı ise maaş karşılığı sayılır.\n(Değişik: 9/4/1990 - KHK - 418/29 md.; İptal: Anayasa Mahkemesinin 5/2/1992 tarihli ve E.1990/22, K.1992/6 Sayılı Kararı ile; Yeniden düzenleme: 18/5/1994-KHK/527/21 md.) Dersi veren öğretim elemanına her ders için ayrı ayrı olmak üzere yarı yıl ve yıl sonu dönemlerinde her 50 öğrenci için 300 gösterge rakamının Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımı sonucu bulunacak tutarda sınav ücreti ödenir. Öğrenci sayısının hesabında küsurlar tama iblağ edilir ve 500 öğrenciden fazlası dikkate alınmaz. Ara sınavlar ve bütünleme sınavları için sınav ücreti ödenmez.\nEk ders ücreti, aşağıdaki göstergelerin Devlet Memurları Kanununa göre aylıklar için belirlenen katsayı ile çarpımından oluşur.\n\n|Unvanı |Ek Ders Ücreti Göstergesi |\n|Profesör |300 |\n|Doçent |250 |\n|Doktor Öğretim Üyesi |200 |\n|Öğretim Görevlisi (…)4 |160 |\n\n(Beşinci fıkra mülga: 9/4/1990 - KHK - 418/29 md.)\nYükseköğretim Kurumlarının kadrolarında olmayıp bu unvanları taşıyanlardan ders saati başına görevlendirilenlerin sosyal güvenlik kuruluşlarından almakta oldukları aylıklar bu şekilde görevlendirilmeleri nedeniyle kesilmez.\nMüfredat programları uyarınca normal çalışma günlerinde çalışma saatinin bitiminden ve saat 17.00'den sonra başlayan gece öğretimi ile hafta tatili, yarı yıl veya yaz tatillerinde yapılan öğretimde yukarıdaki şekilde hesaplanan ek ders ücretleri % 60 zamlı ödenir. Yaz ve yarı yıl tatillerinde yapılan öğretim için verilecek ek ders ücretinin hesabında ders yükü dikkate alınmaz.\n\nÜniversite Ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Ek ders ücreti"}
24
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 576, "document_char_start": 7986, "document_char_end": 8562}, "source_text": "Madde 12 – (Değişik: 20/3/1997 - KHK - 570/4 md.)\n657 sayılı Devlet Memurları Kanununa tabi en yüksek Devlet memuru bürüt aylık (ek gösterge dahil) tutarının;\n\n|1) |Profesörlerden Rektör, Rektör Yardımcısı, Dekan, | |\n| |Dekan Yardımcısı, Yüksekokul Müdürü olanlar ile | |\n| |Profesör kadrosunda üç yılını tamamlamış bulunanlara |% 245'i, |\n|2) |Diğer Profesör kadrosunda bulunanlara |% 215'i, |\n|3) |Doçent kadrosunda bulunanlara |% 175'i, |\n|4) |(Değişik: 22/2/2018-7100/14 md.) Doktor öğretim üyesi kadrosunda bulunanlara |% 175 |\n|5); |Diğer Öğretim elemanlarından | |\n| |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
25
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__002_a", "label": "a", "source_span": {"article_char_start": 576, "article_char_end": 627, "document_char_start": 8562, "document_char_end": 8613}, "source_text": "a) Birinci dereceden aylık alanlara |% 130'u, |\n| |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
26
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__003_b", "label": "b", "source_span": {"article_char_start": 627, "article_char_end": 678, "document_char_start": 8613, "document_char_end": 8664}, "source_text": "b) İkinci dereceden aylık alanlara |% 117'si, |\n| |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
27
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__004_c", "label": "c", "source_span": {"article_char_start": 678, "article_char_end": 728, "document_char_start": 8664, "document_char_end": 8714}, "source_text": "c) Üçüncü dereceden aylık alanlara |% 110'u, |\n| |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
28
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__005_d", "label": "d", "source_span": {"article_char_start": 728, "article_char_end": 791, "document_char_start": 8714, "document_char_end": 8777}, "source_text": "d) Dördüncü ve beşinci dereceden aylık alanlara |% 104'ü, |\n| |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
29
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 12", "clause_id": "madde_12__006_e", "label": "e", "source_span": {"article_char_start": 791, "article_char_end": 1012, "document_char_start": 8777, "document_char_end": 8998}, "source_text": "e) Diğer derecelerden aylık alanlara |% 98'i, |\n|Her ay üniversite ödeneği olarak ödenir. |\n\nBu ödenek damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve kısmi statüde görev yapanlara ödenmez.\n\nİdari görev ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Üniversite Ödeneği"}
30
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 13", "clause_id": "madde_13__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 894, "document_char_start": 8999, "document_char_end": 9893}, "source_text": "Madde 13 – (Değişik birinci fıkra: 20/3/1997 - KHK - 570/5 md.) Almakta oldukları aylık gösterge ve ek gösterge brüt tutarının; Rektörlere %70'i, Rektör Yardımcıları ve Dekanlara %30'u, Dekan Yardımcıları, Enstitü ve Yüksekokul Müdürleri, Konservatuar Müdürleri ile Bölüm Başkanlarına %20'si, Enstitü, Yüksekokul ve Konservatuar Müdür Yardımcılarına %15 'i idari görev ödeneği olarak ayrıca ödenir. Birden fazla idari görevi bulunanlara İdari Görev Ödeneğinden en yüksek olanı verilir.\nBu Kanuna tabi olmayan kişiler arasından tayin olunan rektörlere, 657 sayılı Devlet Memurları Kanununa göre birinci derece memur son kademe aylığı (en yüksek ek gösterge dahil) ile bunun bir katı tutarında ek ödemede bulunulur. Bu Kanunun 10 uncu maddesi hükümleri, bunlar hakkında da uygulanır. Bunlardan emekli iken atananların emekli aylıklarının ödenmesine devam olunur, diğerleri genel hükümlere göre T.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "İdari görev ödeneği"}
31
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 13", "clause_id": "madde_13__002_c", "label": "C", "source_span": {"article_char_start": 894, "article_char_end": 952, "document_char_start": 9893, "document_char_end": 9951}, "source_text": "C. Emekli Sandığı ile ilgilendirilir.\n\nGeliştirme ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "İdari görev ödeneği"}
32
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 14", "clause_id": "madde_14__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 858, "document_char_start": 9952, "document_char_end": 10810}, "source_text": "Madde 14 – (Değişik: 3/4/1998 - 4359/15 md.)\nDiğer yükseköğretim kurumlarına göre sosyo-ekonomik açıdan daha az gelişmiş yerlerde öğretim yapan ve/veya yeterli sayıda öğretim elemanı sağlanamayan yükseköğretim kurumları ile bunların bölümlerinde görevli öğretim elemanlarına; almakta oldukları aylık gösterge ve ek gösterge toplamının 657 sayılı Devlet Memurları Kanunu uyarınca belirlenen aylık katsayı ile çarpımı sonucu bulunacak miktarın beş katına kadar geliştirme ödeneği ödenebilir.\nGeliştirme ödeneği verilecek yükseköğretim kurumları, ödeneğin verilmesine ilişkin usul ve esaslar ile oran veya miktarları, bu ödenekten yararlanma süresi ile yararlanamayacak olanlar ve diğer hususlar Cumhurbaşkanı Kararı ile tespit edilir.\nBu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.\n\nDevlet konservatuvarlarının öğretim elemanları:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Geliştirme ödeneği"}
33
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 15", "clause_id": "madde_15__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 471, "document_char_start": 10811, "document_char_end": 11282}, "source_text": "Madde 15 – (Değişik: 22/8/1989 - KHK - 378/8 md.)6\nYükseköğretim kurumlarının 657 sayılı Devlet Memurları Kanununun değişik ek geçici 16 ncı maddesinde unvanları belirtilen personeli, Yükseköğretim Kurulunun onayı ile sözleşmeli olarak istihdam edilebilir. Bu elemanların sözleşme esasları, bunlara ödenebilecek ücretin üst sınırları ve sağlanacak sosyal yardımlar ile diğer mali haklar; Cumhurbaşkanınca kararlaştırılır.\n\nYabancı öğretim elemanlarına ödenecek ücretler:6", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Devlet konservatuvarlarının öğretim elemanları"}
34
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 16", "clause_id": "madde_16__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 295, "document_char_start": 11283, "document_char_end": 11578}, "source_text": "Madde 16 – 2547 sayılı Yükseköğretim Kanununun 34 üncü maddesine göre sözleşmeli olarak çalıştırılacak yabancı uyruklu öğretim elemanlarına ödenecek ücret, Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulunca tespit edilir.\n\nEmekli öğretim elemanlarının sözleşmeli istihdamı:6", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yabancı öğretim elemanlarına ödenecek ücretler:6"}
35
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 17", "clause_id": "madde_17__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 66, "document_char_start": 11579, "document_char_end": 11645}, "source_text": "Madde 17 – Üniversiteler, ihtiyaç duydukları dallarda 5434 sayılı", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Emekli öğretim elemanlarının sözleşmeli istihdamı:6"}
36
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 17", "clause_id": "madde_17__002_t", "label": "T", "source_span": {"article_char_start": 66, "article_char_end": 69, "document_char_start": 11645, "document_char_end": 11648}, "source_text": "T.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Emekli öğretim elemanlarının sözleşmeli istihdamı:6"}
37
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 17", "clause_id": "madde_17__003_c", "label": "C", "source_span": {"article_char_start": 69, "article_char_end": 424, "document_char_start": 11648, "document_char_end": 12003}, "source_text": "C. Emekli Sandığı veya 506 sayılı Sosyal Sigortalar Kanununa göre 65 yaşını doldurmak suretiyle emekli olmuş öğretim elemanlarını emekli aylıkları kesilmeksizin ve yaş kaydı aranmaksızın sözleşme ile çalıştırabilirler. Bu gibilere ödenecek ücret Cumhurbaşkanınca belirlenecek esaslar dahilinde Yükseköğretim Kurulu tarafından tespit edilir.\n\nGeçici görev:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Emekli öğretim elemanlarının sözleşmeli istihdamı:6"}
38
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 18", "clause_id": "madde_18__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 320, "document_char_start": 12004, "document_char_end": 12324}, "source_text": "Madde 18 – Öğretim elemanları ile tabi oldukları özel kanunlarda mani hüküm bulunmayan diğer kamu görevlileri, rektörün görüşü veya ilgili kuruluşun muvafakatı ile yükseköğretim üst kuruluşlarında her seferinde altı ayı geçmemek üzere geçici olarak görevlendirilebilirler.\nDiğer kurum mensuplarını ilgilendiren hükümler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Geçici görev"}
39
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 19", "clause_id": "madde_19__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 559, "document_char_start": 12325, "document_char_end": 12884}, "source_text": "Madde 19 – Bu Kanun hükümleri, 7163 sayılı Kanunla kurulan Türkiye ve Orta Doğu Amme İdaresi Enstitüsü kadrosunda çalışan profesörler, öğretim ve araştırma mütehassısları (doçentler gibi), öğretim görevlileri (…) ve asistanlar hakkında da uygulanır.\nBu Enstitüde görevli asistanlar hakkında araştırma görevlisi gibi işlem yapılır.\nBu Kanun hükümleri gereğince Yükseköğretim Kurulu, 2547 sayılı Yükseköğretim Kanunu gereğince de Üniversitelerarası Kurul tarafından alınan kararlar bu Enstitü mensuplarına da aynen uygulanır.\n\nUygulanacak diğer kanun hükümleri:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Diğer kurum mensuplarını ilgilendiren hükümler"}
40
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 20", "clause_id": "madde_20__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 162, "document_char_start": 12885, "document_char_end": 13047}, "source_text": "Madde 20 – Bu Kanunda hüküm bulunmayan hallerde 2547 sayılı Yükseköğretim Kanunu ile 657 sayılı Devlet Memurları Kanunu hükümleri uygulanır.\n\nKaldırılan hükümler:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Uygulanacak diğer kanun hükümleri"}
41
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 21", "clause_id": "madde_21__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 175, "document_char_start": 13048, "document_char_end": 13223}, "source_text": "Madde 21 – 25/6/1973 tarih ve 1765 sayılı Üniversite Personel Kanunu ile bu Kanunda değişiklik yapan diğer kanun hükümleri yürürlükten kaldırılmıştır.\n\nEğitim Öğretim Ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Kaldırılan hükümler"}
42
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 1", "clause_id": "ek_madde_1__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 668, "document_char_start": 13224, "document_char_end": 13892}, "source_text": "Ek Madde 1 – (Ek: 24/11/1994 - 4048/4 md.; Değişik: 20/3/1997 - KHK-570/6 md.)\n4.11.1981 tarih ve 2547 sayılı Kanunun 33 üncü maddesi ve 39 uncu maddesinin ikinci fıkrası uyarınca yurtdışına gönderilenler ile anılan Kanunun 38 inci maddesine göre diğer kurum ve kuruluşlarda görevlendirilenlerden yüksek öğretim kurumlarındaki kadro görevini yapmayanlar hariç olmak üzere Yükseköğretim Kurumlarında görevli öğretim elemanlarına en yüksek Devlet memuru aylığı (ek gösterge dahil) brüt tutarının on ikide biri, her ay aylıklarla birlikte Eğitim Öğretim Ödeneği olarak ödenir. Bu ödenek damga vergisi hariç herhangi bir vergi ve kesintiye tabi tutulmaz.\n\nMakam Tazminatı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Eğitim Öğretim Ödeneği"}
43
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 2", "clause_id": "ek_madde_2__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 577, "document_char_start": 13893, "document_char_end": 14470}, "source_text": "Ek Madde 2 – (Ek: 20/3/1997 - KHK - 570/7 md.)\nBu Kanuna ekli Makam Tazminatı Cetvelinde yazılı kadro ve görev unvanlarına atananlara hizalarında belirtilen gösterge rakamlarının memur aylıklarına uygulanan katsayı ile çarpımı sonucu bulunan miktarda Makam Tazminatı ödenir. Makam Tazminatı damga vergisi hariç herhangi bir vergiye tabi tutulmaz. Makam tazminatının hakedilmesinde ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu tazminattan yararlananlara ayrıca diğer kanunlarda belirtilen Yüksek Hakimlik Tazminatı ve Makam Tazminatı ödenmez.\nYükseköğretim tazminatı:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Makam Tazminatı"}
44
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 137, "document_char_start": 14471, "document_char_end": 14608}, "source_text": "Ek Madde 3 – (Ek: 5/11/2014-6564/1 md.)\nDevlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
45
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__002_a", "label": "a", "source_span": {"article_char_start": 137, "article_char_end": 180, "document_char_start": 14608, "document_char_end": 14651}, "source_text": "a) Profesör kadrosunda bulunanlara %100’ü,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
46
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__003_b", "label": "b", "source_span": {"article_char_start": 180, "article_char_end": 221, "document_char_start": 14651, "document_char_end": 14692}, "source_text": "b) Doçent kadrosunda bulunanlara %100’ü,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
47
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__004_c", "label": "c", "source_span": {"article_char_start": 221, "article_char_end": 276, "document_char_start": 14692, "document_char_end": 14747}, "source_text": "c) Doktor Öğretim Üyesi kadrosunda bulunanlara %100’ü,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
48
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__005_d", "label": "d", "source_span": {"article_char_start": 276, "article_char_end": 330, "document_char_start": 14747, "document_char_end": 14801}, "source_text": "d) Araştırma Görevlisi kadrosunda bulunanlara %115’i,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
49
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__006_e", "label": "e", "source_span": {"article_char_start": 330, "article_char_end": 388, "document_char_start": 14801, "document_char_end": 14859}, "source_text": "e) Öğretim Görevlisi (…)10 kadrosunda bulunanlara %115’i,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
50
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 3", "clause_id": "ek_madde_3__007_f", "label": "f", "source_span": {"article_char_start": 388, "article_char_end": 866, "document_char_start": 14859, "document_char_end": 15337}, "source_text": "f) (Mülga: 22/2/2018-7100/15 md.)\noranında her ay yükseköğretim tazminatı ödenir.\nBu tazminata hak kazanılmasında ve ödenmesinde aylıklara ilişkin hükümler uygulanır. Bu maddeye göre ödenecek tazminat, damga vergisi hariç herhangi bir vergiye tabi tutulmaz ve ilgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.\n\nAkademik teşvik ödeneği:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yükseköğretim tazminatı"}
51
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 612, "document_char_start": 15338, "document_char_end": 15950}, "source_text": "Ek Madde 4 – (Ek: 5/11/2014-6564/2 md.)\nHer bir takvim yılı için, bir önceki yıl, bilim, teknoloji ve sanata katkı sağlayıcı nitelikte yurt içinde veya yurt dışında sonuçlandırılan proje, araştırma, yayın, tasarım, sergi, patent ile çalışmalarına yapılan atıflar, bilim kurulu bulunan uluslararası düzeydeki toplantılarda tebliğ sunma ve almış olduğu akademik ödüller esas alınarak öğretim elemanları için yüz puan üzerinden yıllık akademik teşvik puanı hesaplanır.\nAkademik teşvik puanı otuz ve üzerinde olanlara, Devlet Memurları Kanununa tabi en yüksek Devlet memuru brüt aylık (ek gösterge dâhil) tutarının;", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
52
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__002_a", "label": "a", "source_span": {"article_char_start": 612, "article_char_end": 661, "document_char_start": 15950, "document_char_end": 15999}, "source_text": "a) Profesör kadrosunda bulunanlar için %100’üne,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
53
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__003_b", "label": "b", "source_span": {"article_char_start": 661, "article_char_end": 707, "document_char_start": 15999, "document_char_end": 16045}, "source_text": "b) Doçent kadrosunda bulunanlar için %90’ına,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
54
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__004_c", "label": "c", "source_span": {"article_char_start": 707, "article_char_end": 767, "document_char_start": 16045, "document_char_end": 16105}, "source_text": "c) Doktor Öğretim Üyesi kadrosunda bulunanlar için %80’ine,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
55
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__005_d", "label": "d", "source_span": {"article_char_start": 767, "article_char_end": 826, "document_char_start": 16105, "document_char_end": 16164}, "source_text": "d) Araştırma Görevlisi kadrosunda bulunanlar için %70’ine,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
56
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__006_e", "label": "e", "source_span": {"article_char_start": 826, "article_char_end": 889, "document_char_start": 16164, "document_char_end": 16227}, "source_text": "e) Öğretim Görevlisi (…)12 kadrosunda bulunanlar için %70’ine,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
57
+ {"document_id": "TR-KANUN-2914", "article_id": "Ek Madde 4", "clause_id": "ek_madde_4__007_f", "label": "f", "source_span": {"article_char_start": 889, "article_char_end": 2184, "document_char_start": 16227, "document_char_end": 17522}, "source_text": "f) (Mülga: 22/2/2018-7100/15 md.)\naldıkları akademik teşvik puanının yüze bölünmesi suretiyle bulunacak oranın uygulanması suretiyle hesaplanan tutarda akademik teşvik ödeneği verilir.\nBu madde uyarınca yapılacak ödeme; bu Kanun uyarınca aylık ödendiği sürece ve kadrolarının bulunduğu yükseköğretim kurumları tarafından şubat ayının on beşinden itibaren on iki ay süreyle her ayın on beşinde yapılır, damga vergisi hariç herhangi bir vergiye tabi tutulmaz. İlgili mevzuatı uyarınca ödenmekte olan zam, tazminat, ödenek, döner sermaye ek ödemesi, ikramiye, ücret ve her ne ad altında olursa olsun yapılan benzeri ödemelerin hesabında dikkate alınmaz.\nBilim alanlarının özellikleri ve öğretim elemanlarının unvanlarına göre akademik teşvik puanlarının hesaplanmasında esas alınacak faaliyetlerin ayrıntılı özellikleri ve bu faaliyetlerin puan karşılıkları, akademik teşvik toplam puanının %30’unu geçmemek üzere her bir akademik faaliyet türünün toplam puanın hesaplanmasındaki ağırlıkları, akademik teşvik puanının hesaplanmasına ilişkin usul ve esaslar ile bu hesaplamaları yapacak komisyonun oluşumu ile diğer hususlar Cumhurbaşkanı tarafından yürürlüğe konulan yönetmelikle belirlenir.\nBu madde hükümleri, Türk Silahlı Kuvvetleri kadrolarında bulunan öğretim elemanları hakkında da uygulanır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Akademik teşvik ödeneği"}
58
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 1", "clause_id": "geçici_madde_1__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 308, "document_char_start": 17523, "document_char_end": 17831}, "source_text": "Geçici Madde 1 – 657 sayılı Devlet Memurları Kanununa tabi öğretmenlerden 28/3/1993 tarih ve 2809 sayılı Kanunun geçici 3 üncü maddesi ile aynı Kanun kapsamına alınanlardan yükseköğrenim kurumlarında istihdamına ihtiyaç duyulanların bu Kanun hükümlerine göre intibakları, 30/6/1984 tarihi itibariyle yapılır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "f) (Mülga: 22/2/2018-7100/15 md.)"}
59
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 2", "clause_id": "geçici_madde_2__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 454, "document_char_start": 17833, "document_char_end": 18287}, "source_text": "Geçici Madde 2 – Öğretim elemanlarına ve geçici 1 inci madde kapsamına girenler bu Kanunun 12 inci maddesi uyarınca ödenecek Üniversite Ödeneğinin, 1765 sayılı Üniversite Personel Kanununun değişik 16 ncı maddesi uyarınca halen ödenmekte olan iş güçlüğü, iş riski, temininde güçlük zammı ile tamgün ödeneği tutarından az olması halinde aradaki net fark, üniversite ödeneği bu miktara ulaşıncaya kadar, hiçbir vergi ve kesintiye tabi tutulmaksızın ödenir.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
60
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 3", "clause_id": "geçici_madde_3__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 174, "document_char_start": 18289, "document_char_end": 18463}, "source_text": "Geçici Madde 3 – Öğretim elemanlarına bu Kanunun 5 inci maddesine göre verilecek ek göstergeleri, 31/12/1983 tarihinde yararlanmakta oldukları ek göstergelerden düşük olamaz.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
61
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 4", "clause_id": "geçici_madde_4__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 785, "document_char_start": 18465, "document_char_end": 19250}, "source_text": "Geçici Madde 4 – 1/1/1984 tarihinde yükseköğretim kurumlarında görev yapmakta olanlar ile bu kurumların kadroları ile ilişkileri devam eden, askerlik görevlerini yapmakta olan ve bu Kanun kapsamına giren öğretim elemanlarının intibakları, 657 sayılı Devlet Memurları Kanunu hükümleri uyarınca öğrenim niteliğine ve süresine göre (657 sayılı Kanunun değişik 36 ncı maddesinin \"Ortak Hükümleri\" ile getirilen kademe ilerlemesi ve derece yükselmesi hariç) başlayabilecekleri derece ve kademe esas alınmak suretiyle bu Kanun ve Devlet Memurları Kanununa göre rektörlüklerce (Geçici 1 inci madde kapsamına girenler hariç) üç ay içinde yapılır.\nİntibak sonucunda kazanılmış hak aylık dereceleri kadro derecelerinin üzerinde olanlara, kazanılmış hak aylıkları esas alınarak ödemede bulunulur.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
62
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 5", "clause_id": "geçici_madde_5__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 206, "document_char_start": 19252, "document_char_end": 19458}, "source_text": "Geçici Madde 5 – Geçici 4 üncü madde hükmü saklı kalmak üzere bu Kanunun yürürlüğe girdiği tarihten önce doçent kadrosuna atanmış bulunanların görev aylıkları, bir üst derecenin aynı kademesine yükseltilir.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
63
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 6", "clause_id": "geçici_madde_6__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 1085, "document_char_start": 19460, "document_char_end": 20545}, "source_text": "Geçici Madde 6 – 6/11/1981 tarihinde Boğaziçi Üniversitesi ile Orta Doğu Teknik Üniversitesinde 657 sayılı Devlet Memurları Kanunu hükümlerine tabi olmadan çalışan öğretim elemanları dışında kalan personelden 657 sayılı Kanuna göre intibaklarının yapılmasını istemeyerek, 1/1/1984 tarihinden itibaren 30 gün içinde müracaat edenlere, kıdem tazminatları ödenir ve kurumları ile ilişkileri kesilir.\nBunlara ödenecek kıdem tazminatı, görevden ayrıldıkları tarihteki kazanılmış hakları dikkate alınarak hesaplanır. Ancak, bu şekilde hesaplanacak kıdem tazminatının miktarı 30/9/1982 tarihinde hak kazanabilecekleri kıdem tazminatından fazla olamaz.\n2547 sayılı Yükseköğretim Kanununun yürürlüğe girdiği 6/11/1981 tarihi ile bu Kanunun yürürlüğe girdiği tarih arasında görevden ayrılanlar hakkında da, görevden ayrıldıkları tarih dikkate alınarak yukarıdaki hükümler uygulanır.\n30/9/1982 tarihinden sonra görevden ayrılanların bu tarih ile görevden ayrıldıkları tarih arasında geçen hizmetleri, primlerinin ödenmesi kaydıyla 506 sayılı Sosyal Sigortalar Kanununa tabi olarak geçmiş sayılır.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
64
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 7", "clause_id": "geçici_madde_7__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 348, "document_char_start": 20547, "document_char_end": 20895}, "source_text": "Geçici Madde 7 – 4/11/1981 tarih ve 2547 sayılı Yükseköğretim Kanununun 24/9/1982 tarih ve 2708 sayılı Kanun ile değişik geçici 23 üncü maddesinde belirtilen ve sözleşmeli olarak çalıştırılan öğretim elemanları ile idari personel 30/9/1984 tarihine kadar sahip oldukları statü içinde çalıştırılırlar. Bu tarihten sonra genel hükümlere tabi olurlar.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
65
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "clause_id": "geçici_madde_8__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 273, "document_char_start": 20897, "document_char_end": 21170}, "source_text": "Geçici Madde 8 – İlgili kanunda gerekli değişiklik yapılıncaya kadar, Yükseköğretim Kurulu üyeleri ile Yükseköğretim Denetleme Kurulu üyeleri, 657 sayılı Devlet Memurları Kanununun 187 - 213 üncü maddeleri ile düzenlenen sosyal haklardan yararlanırlar.\nAncak, bu üyelerden", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
66
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "clause_id": "geçici_madde_8__002_t", "label": "T", "source_span": {"article_char_start": 273, "article_char_end": 276, "document_char_start": 21170, "document_char_end": 21173}, "source_text": "T.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
67
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "clause_id": "geçici_madde_8__003_c", "label": "C", "source_span": {"article_char_start": 276, "article_char_end": 477, "document_char_start": 21173, "document_char_end": 21374}, "source_text": "C. Emekli Sandığı ile Sosyal Sigortalar Kurumundan emekli olanlar bu kuruluşlarca aynı sosyal haklardan ayrıca yararlandırılmazlar.\n(Ek: 26/6/1984 - KHK - 241/32 md.; Değişik: 24/11/1994 - 4048/5 md.)", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
68
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "clause_id": "geçici_madde_8__004_t", "label": "T", "source_span": {"article_char_start": 477, "article_char_end": 480, "document_char_start": 21374, "document_char_end": 21377}, "source_text": "T.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
69
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 8", "clause_id": "geçici_madde_8__005_c", "label": "C", "source_span": {"article_char_start": 480, "article_char_end": 1064, "document_char_start": 21377, "document_char_end": 21961}, "source_text": "C. Emekli Sandığ�� iştirakçisi olan Yükseköğretim Kurulu Başkanı, Yükseköğretim Yürütme Kurulu Üyeleri ile Yükseköğretim Denetleme Kurulu üyelerinin emekli keseneklerinin hesabında aşağıdaki ek göstergeler esas alınır. Emekli İken Yükseköğretim Yürütme Kurulu veya Yükseköğretim Denetleme Kurulu Üyeliklerine geçirilenlerin emekli aylıklarının ödenmesinde de aynı göstergeler esas alınır.\n\n(Değişik:1/7/2022-7417/10 md.)\n|Unvanı |Derece |Ek Göstergeler |\n|Yükseköğretim Kurulu Başkanı |1 |8000 |\n|Yükseköğretim Kurulu Üyeleri |1 |7800 |\n|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": ""}
70
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 9", "clause_id": "geçici_madde_9__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 554, "document_char_start": 21963, "document_char_end": 22517}, "source_text": "Geçici Madde 9 – (Ek: 14/1/1988 - KHK - 311/8 md.)\n2547 sayılı Yükseköğretim Kanunun yürürlüğe girmesinden önce Profesörlük unvanını almış olup da 78 sayılı Kanun Hükmünde Kararname ile ihdas edilen profesörlük kadrolarına atanamamış olanlar, bu Kanunun 5 inci maddesinde profesör kadrosunda bulunanlar için öngörülen ek göstergeler ile 241 sayılı Kanun Hükmünde Kararname'nin değişik 39 uncu maddesine göre profesör kadrosunda üç yılı tamamlamış olanlar için öngörülen makam tazminatından profesör kadrosuna atanma şartı aranmaksızın yararlandırılırlar.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |"}
71
+ {"document_id": "TR-KANUN-2914", "article_id": "Geçici Madde 10", "clause_id": "geçici_madde_10__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 294, "document_char_start": 22519, "document_char_end": 22813}, "source_text": "Geçici Madde 10 – (Ek: 5/11/2014-6564/3 md.)\nEk 4 üncü maddede belirtilen yönetmelik dört ay içinde yürürlüğe konur. Ek 4 üncü maddenin birinci fıkrasına göre ilk defa akademik teşvik puanı hesaplanması, 2015 yılında yapılan faaliyetler esas alınmak suretiyle 2016 yılı için yapılır.\n\nYürürlük:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "|Yükseköğretim Denetim Kurulu Üyeleri |1 |7800 |"}
72
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 22", "clause_id": "madde_22__001_preamble", "label": "preamble", "source_span": {"article_char_start": 0, "article_char_end": 23, "document_char_start": 22815, "document_char_end": 22838}, "source_text": "Madde 22 – Bu Kanunun;", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Bu Kanunun"}
73
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 22", "clause_id": "madde_22__002_a", "label": "a", "source_span": {"article_char_start": 23, "article_char_end": 117, "document_char_start": 22838, "document_char_end": 22932}, "source_text": "a) Geçici 7 nci maddesi hükümleri; 30/9/1983 tarihinden geçerli olmak üzere yayımı tarihinde,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Bu Kanunun"}
74
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 22", "clause_id": "madde_22__003_b", "label": "b", "source_span": {"article_char_start": 117, "article_char_end": 263, "document_char_start": 22932, "document_char_end": 23078}, "source_text": "b) Geçici 8 inci maddesi ile atıfta bulunulan 657 sayılı Devlet Memurları Kanununun 208- 210 uncu maddelerine ilişkin hükümleri yayımı tarihinde,", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Bu Kanunun"}
75
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 22", "clause_id": "madde_22__004_c", "label": "c", "source_span": {"article_char_start": 263, "article_char_end": 328, "document_char_start": 23078, "document_char_end": 23143}, "source_text": "c) Diğer hükümleri 1/1/1984 tarihinde,\nYürürlüğe girer.\n\nYürütme:", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Bu Kanunun"}
76
+ {"document_id": "TR-KANUN-2914", "article_id": "Madde 23", "clause_id": "madde_23__001_article", "label": "article", "source_span": {"article_char_start": 0, "article_char_end": 56, "document_char_start": 23144, "document_char_end": 23200}, "source_text": "Madde 23 – Bu Kanun hükümlerini Bakanlar Kurulu yürütür.", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914", "article_title": "Yürütme"}
data/structural/2914_evidence_spans.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
data/structural/2914_schedules.jsonl ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ {"class": "ScheduleUnit", "document_id": "TR-KANUN-2914", "schedule_id": "TR-KANUN-2914__EK_GÖSTERGE_CETVELI", "article_id": "EK GÖSTERGE CETVELİ", "title": "EK GÖSTERGE CETVELİ", "source_span": {"char_start": 23201, "char_end": 23681}, "source_text": "EK GÖSTERGE CETVELİ:\n\n(Ek:18/5/1994-KHK-527/19 md.) (Değişik:1/7/2022-7417/10 md.)\n\n|Kadro Unvanı |Derece |Ek Göstergeler |\n|a) Profesörlerden Rektör, Rektör Yardımcısı, Dekan, Dekan Yardımcısı, Yüksekokul Müdürü |1 |7000 |\n|olanlar ile Profesörlük kadrosunda dört yılını tamamlamış bulunanlar | | |\n|b) Profesörler |1 |5900 |\n|c) Doçentler |1-3 |5400 |\n|d) Doktor Öğretim Üyeleri |1-5 |4200 |\n|e) Öğretim Görevlisi ve Araştırma Görevlileri |1 |4200 |\n| |2 |3300 |\n| |3-7 |2900 |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
2
+ {"class": "ScheduleUnit", "document_id": "TR-KANUN-2914", "schedule_id": "TR-KANUN-2914__MAKAM_TAZMI_NATI_CETVELI_EK_20_3_1997_KHK_570_7_MD", "article_id": "MAKAM TAZMİNATI CETVELİ (Ek: 20/3/1997-KHK-570/7 md.)", "title": "MAKAM TAZMİNATI CETVELİ (Ek: 20/3/1997-KHK-570/7 md.)", "source_span": {"char_start": 23681, "char_end": 23996}, "source_text": "MAKAM TAZMİNATI CETVELİ (Ek: 20/3/1997-KHK-570/7 md.)\n\n|Sıra No |Kadro ve Görev Unvanı |Tazminat Göstergeleri |\n|1 |Rektörler |7.000 |\n|2 |Profesörler (Bu kadroda üç yılını tamamlamış olmak şartıyla) |6.000 |\n|3 |Profesörler |4.500 |\n|4 |Doçentler (Kazanılmış hak aylıkları birinci derece olmak şartıyla) |2.000 |", "document_title": "2914 sayılı Yükseköğretim Personel Kanunu", "document_short_code": "2914"}
data/tests/golden_questions.jsonl ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"question":"Dekan nasıl atanır?","expected_article":"Madde 16","expected_norm_type":"atama","expected_behavior":"answer"}
2
+ {"question":"Dekanın görevleri nelerdir?","expected_article":"Madde 16","expected_norm_type":"gorev","expected_behavior":"answer"}
3
+ {"question":"YÖK kaç üyeden oluşur?","expected_article":"Madde 6","expected_norm_type":"kurul_olusumu","expected_behavior":"answer"}
4
+ {"question":"Yükseköğretim kurumu nasıl kurulur?","expected_article":"Madde 5","expected_norm_type":"kurulus","expected_behavior":"answer"}
5
+ {"question":"Ön lisans diploması nedir?","expected_article":"Madde 3","expected_norm_type":"tanim","expected_behavior":"answer"}
6
+ {"question":"Diploma almak için ne gerekir?","expected_article":"Madde 44","expected_norm_type":"sart","expected_behavior":"answer"}
7
+ {"question":"Rektörün görevleri nelerdir?","expected_article":"Madde 13","expected_norm_type":"gorev","expected_behavior":"answer"}
8
+ {"question":"Üniversite senatosunun görevleri nelerdir?","expected_article":"Madde 14","expected_norm_type":"gorev","expected_behavior":"answer"}
9
+ {"question":"Yükseköğretim Kurulunun görevleri nelerdir?","expected_article":"Madde 7","expected_norm_type":"gorev","expected_behavior":"answer"}
10
+ {"question":"Yükseköğretim Denetleme Kurulunun görevleri nelerdir?","expected_article":"Madde 9","expected_norm_type":"gorev","expected_behavior":"answer"}
11
+ {"question":"Üniversitelerarası Kurul nedir?","expected_article":"Madde 11","expected_norm_type":"tanim","expected_behavior":"answer"}
12
+ {"question":"Rektör nasıl atanır?","expected_article":"Madde 13","expected_norm_type":"atama","expected_behavior":"answer"}
13
+ {"question":"Senatonun görevleri nelerdir?","expected_article":"Madde 14","expected_norm_type":"gorev","expected_behavior":"answer"}
14
+ {"question":"Üniversite Yönetim Kurulunun görevleri nelerdir?","expected_article":"Madde 15","expected_norm_type":"gorev","expected_behavior":"answer"}
15
+ {"question":"Fakülte Kurulunun görevleri nelerdir?","expected_article":"Madde 17","expected_norm_type":"gorev","expected_behavior":"answer"}
16
+ {"question":"Fakülte Yönetim Kurulu görevleri nelerdir?","expected_article":"Madde 18","expected_norm_type":"gorev","expected_behavior":"answer"}
17
+ {"question":"Doktor öğretim üyesi ataması nasıl yapılır?","expected_article":"Madde 23","expected_norm_type":"atama","expected_behavior":"answer"}
18
+ {"question":"Doçentlik başvurusu nasıl yapılır?","expected_article":"Madde 24","expected_norm_type":"sart","expected_behavior":"answer"}
19
+ {"question":"Profesörlüğe yükselme nasıl olur?","expected_article":"Madde 26","expected_norm_type":"atama","expected_behavior":"answer"}
20
+ {"question":"Öğretim görevlileri nasıl atanır?","expected_article":"Madde 31","expected_norm_type":"atama","expected_behavior":"answer"}
21
+ {"question":"Araştırma görevlileri nasıl atanır?","expected_article":"Madde 33","expected_norm_type":"atama","expected_behavior":"answer"}
22
+ {"question":"Yabancı uyruklu öğretim elemanları nasıl çalıştırılır?","expected_article":"Madde 34","expected_norm_type":"sart","expected_behavior":"answer"}
23
+ {"question":"Öğretim elemanı yetiştirme nasıl yapılır?","expected_article":"Madde 35","expected_norm_type":"sart","expected_behavior":"answer"}
24
+ {"question":"Öğretim elemanlarının çalışma esasları nelerdir?","expected_article":"Madde 36","expected_norm_type":"gorev","expected_behavior":"answer"}
25
+ {"question":"Yurt içinde ve yurt dışında görevlendirme nasıl olur?","expected_article":"Madde 39","expected_norm_type":"sart","expected_behavior":"answer"}
26
+ {"question":"Lisans düzeyinde öğretim nasıl düzenlenir?","expected_article":"Madde 43","expected_norm_type":"sart","expected_behavior":"answer"}
27
+ {"question":"Yükseköğretime giriş ve yerleştirme nasıl yapılır?","expected_article":"Madde 45","expected_norm_type":"sart","expected_behavior":"answer"}
28
+ {"question":"Öğrenci katkı payı ve öğrenim ücretleri nasıl belirlenir?","expected_article":"Madde 46","expected_norm_type":"sart","expected_behavior":"answer"}
29
+ {"question":"Disiplin soruşturmasında savunma hakkı nedir?","expected_article":"Madde 53/A","expected_norm_type":"sart","expected_behavior":"answer"}
30
+ {"question":"Disiplin cezası verme yetkisi kimdedir?","expected_article":"Madde 53/Ç","expected_norm_type":"yetki","expected_behavior":"answer"}
31
+ {"question":"Disiplin cezalarına itiraz nasıl yapılır?","expected_article":"Madde 53/F","expected_norm_type":"sart","expected_behavior":"answer"}
32
+ {"question":"Öğrencilerin disiplin işleri nasıl düzenlenir?","expected_article":"Madde 54","expected_norm_type":"yaptirim","expected_behavior":"answer"}
33
+ {"question":"Vakıf yükseköğretim kurumu nasıl kurulur?","expected_article":"Ek Madde 3","expected_norm_type":"kurulus","expected_behavior":"answer"}
34
+ {"question":"Madde 1 neyi düzenler?","expected_article":"Madde 1","expected_norm_type":"","expected_behavior":"answer"}
35
+ {"question":"Madde 2 neyi kapsar?","expected_article":"Madde 2","expected_norm_type":"kapsam","expected_behavior":"answer"}
36
+ {"question":"Madde 4 ne hakkındadır?","expected_article":"Madde 4","expected_norm_type":"","expected_behavior":"answer"}
37
+ {"question":"Madde 5 ana ilkeleri nelerdir?","expected_article":"Madde 5","expected_norm_type":"","expected_behavior":"answer"}
38
+ {"question":"Madde 8 neyi düzenler?","expected_article":"Madde 8","expected_norm_type":"","expected_behavior":"answer"}
39
+ {"question":"Madde 10 ne hakkındadır?","expected_article":"Madde 10","expected_norm_type":"","expected_behavior":"answer"}
40
+ {"question":"Madde 12 ne hakkındadır?","expected_article":"Madde 12","expected_norm_type":"","expected_behavior":"answer"}
41
+ {"question":"Madde 21 bölüm hakkında ne düzenler?","expected_article":"Madde 21","expected_norm_type":"","expected_behavior":"answer"}
42
+ {"question":"Madde 22 öğretim üyeleri için ne düzenler?","expected_article":"Madde 22","expected_norm_type":"","expected_behavior":"answer"}
43
+ {"question":"Madde 49 yabancı dil hazırlık için ne düzenler?","expected_article":"Madde 49","expected_norm_type":"","expected_behavior":"answer"}
44
+ {"question":"Madde 50 lisansüstü öğretim için ne düzenler?","expected_article":"Madde 50","expected_norm_type":"","expected_behavior":"answer"}
45
+ {"question":"Madde 53/B neyi düzenler?","expected_article":"Madde 53/B","expected_norm_type":"","expected_behavior":"answer"}
46
+ {"question":"Madde 55 gelir kaynakları için ne düzenler?","expected_article":"Madde 55","expected_norm_type":"","expected_behavior":"answer"}
47
+ {"question":"Madde 65 yönetmelikler için ne düzenler?","expected_article":"Madde 65","expected_norm_type":"","expected_behavior":"answer"}
48
+ {"question":"Ek Madde 10 neyi düzenler?","expected_article":"Ek Madde 10","expected_norm_type":"","expected_behavior":"answer"}
49
+ {"question":"Ek Madde 12 neyi düzenler?","expected_article":"Ek Madde 12","expected_norm_type":"","expected_behavior":"answer"}
50
+ {"question":"Ek Madde 18 neyi düzenler?","expected_article":"Ek Madde 18","expected_norm_type":"","expected_behavior":"answer"}
51
+ {"question":"Ek Madde 32 neyi düzenler?","expected_article":"Ek Madde 32","expected_norm_type":"","expected_behavior":"answer"}
52
+ {"question":"Ek Madde 35 neyi düzenler?","expected_article":"Ek Madde 35","expected_norm_type":"","expected_behavior":"answer"}
53
+ {"question":"Dekanlık makamı kanuna göre nasıl bir roldür?","expected_article":"Madde 16","expected_norm_type":"gorev","expected_behavior":"answer"}
54
+ {"question":"Rektörün üniversite içindeki sorumluluğu nasıl anlaşılmalı?","expected_article":"Madde 13","expected_norm_type":"gorev","expected_behavior":"answer"}
55
+ {"question":"YÖK sistemde nasıl bir rol oynar?","expected_article":"Madde 6","expected_norm_type":"gorev","expected_behavior":"answer"}
56
+ {"question":"Öğrenci disiplininde hukuki çerçeve nedir?","expected_article":"Madde 54","expected_norm_type":"tanim","expected_behavior":"answer"}
57
+ {"question":"Vakıf yükseköğretim kurumları bakımından temel sınırlama nedir?","expected_article":"Ek Madde 3","expected_norm_type":"sart","expected_behavior":"answer"}
58
+ {"question":"Kanunun temel ilkeleri neler?","expected_article":"Madde 5","expected_norm_type":"ilke","expected_behavior":"answer"}
59
+ {"question":"2547 sayılı kanunun ana ilkeleri nelerdir?","expected_article":"Madde 5","expected_norm_type":"ilke","expected_behavior":"answer"}
60
+ {"question":"Yükseköğretimin amacı nedir?","expected_article":"Madde 4","expected_norm_type":"amac","expected_behavior":"answer"}
61
+ {"question":"Kanunun temel amaçları nelerdir?","expected_article":"Madde 1","expected_norm_type":"amac","expected_behavior":"answer"}
62
+ {"question":"Yükseköğretim kanunun temel amacı nedir?","expected_article":"Madde 1","expected_norm_type":"amac","expected_behavior":"answer"}
63
+ {"question":"Araştırma enstitüleri nasıl kurulur?","expected_article":"Madde 7","expected_norm_type":"kurulus","expected_behavior":"answer"}
64
+ {"question":"Enstitüler nasıl kurulur?","expected_article":"Madde 5","expected_norm_type":"kurulus","expected_behavior":"answer"}
65
+ {"question":"Uygulama ve araştırma merkezi nasıl açılır?","expected_article":"Madde 7","expected_norm_type":"kurulus","expected_behavior":"answer"}
data/tests/multidoc_golden_questions.jsonl ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {"id":"2547_dekan_atama_001","question":"Dekan nasil atanir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 16"],"expected_evidence_keywords":["dekan"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
2
+ {"id":"2547_dekan_gorev_002","question":"Dekanin gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 16"],"expected_evidence_keywords":["dekan"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
3
+ {"id":"2547_yok_olusum_003","question":"YOK kac uyeden olusur?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 6"],"expected_evidence_keywords":["yirmi bir"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
4
+ {"id":"2547_yok_gorev_004","question":"Yuksekogretim Kurulunun gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 7"],"expected_evidence_keywords":["Yükseköğretim Kurulunun görevleri"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
5
+ {"id":"2547_rektor_atama_005","question":"Rektor nasil atanir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 13"],"expected_evidence_keywords":["rektör"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
6
+ {"id":"2547_rektor_gorev_006","question":"Rektorun gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 13"],"expected_evidence_keywords":["rektör"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
7
+ {"id":"2547_senato_gorev_007","question":"Universite senatosunun gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 14"],"expected_evidence_keywords":["senato"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
8
+ {"id":"2547_univ_yk_gorev_008","question":"Universite Yonetim Kurulunun gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 15"],"expected_evidence_keywords":["Üniversite yönetim kurulu"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
9
+ {"id":"2547_fakulte_kurulu_009","question":"Fakulte Kurulunun gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 17"],"expected_evidence_keywords":["Fakülte kurulu"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
10
+ {"id":"2547_fakulte_yk_010","question":"Fakulte Yonetim Kurulu gorevleri nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 18"],"expected_evidence_keywords":["Fakülte yönetim kurulu"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
11
+ {"id":"2547_doktor_ogretim_011","question":"Doktor ogretim uyesi atamasi nasil yapilir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 23"],"expected_evidence_keywords":["doktor öğretim üyesi"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
12
+ {"id":"2547_docentlik_012","question":"Docentlik basvurusu nasil yapilir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 24"],"expected_evidence_keywords":["doçentlik"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
13
+ {"id":"2547_profesor_013","question":"Profesorlugu yukselme nasil olur?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 26"],"expected_evidence_keywords":["profesör"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
14
+ {"id":"2547_ogretim_gorevlisi_014","question":"Ogretim gorevlileri nasil atanir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 31"],"expected_evidence_keywords":["Öğretim görevlileri"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
15
+ {"id":"2547_arastirma_gorevlisi_015","question":"Arastirma gorevlileri nasil atanir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 33"],"expected_evidence_keywords":["Araştırma görevlileri"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
16
+ {"id":"2547_yabanci_016","question":"Yabanci uyruklu ogretim elemanlari nasil calistirilir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 34"],"expected_evidence_keywords":["Yabancı uyruklu"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
17
+ {"id":"2547_calisma_017","question":"Ogretim elemanlarinin calisma esaslari nelerdir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 36"],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
18
+ {"id":"2547_diploma_018","question":"Diploma almak icin ne gerekir?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 44"],"expected_evidence_keywords":["diploma"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
19
+ {"id":"2547_vakif_019","question":"Vakif yuksekogretim kurumu nasil kurulur?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Ek Madde 3"],"expected_evidence_keywords":["Vakıflar"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
20
+ {"id":"2547_enstitu_020","question":"Enstituler nasil kurulur?","expected_document_ids":["TR-KANUN-2547"],"expected_articles":["Madde 5"],"expected_evidence_keywords":["enstitü"],"forbidden_document_ids":["TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
21
+ {"id":"2914_ek_ders_001","question":"Ek ders ucreti nasil odenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 11"],"expected_evidence_keywords":["ek ders ücreti"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
22
+ {"id":"2914_ek_ders_002","question":"Ek ders ucreti hangi kanunda duzenlenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 11"],"expected_evidence_keywords":["ek ders ücreti"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
23
+ {"id":"2914_ek_ders_003","question":"Ek ders ucreti en cok kac saate kadar odenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 11"],"expected_evidence_keywords":["yirmi saate"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
24
+ {"id":"2914_sinav_004","question":"Sinav ucreti hangi gostergeye gore odenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 11"],"expected_evidence_keywords":["sınav ücreti"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
25
+ {"id":"2914_universite_odenegi_005","question":"Universite odenegi nedir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 12"],"expected_evidence_keywords":["üniversite ödeneği"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
26
+ {"id":"2914_universite_odenegi_006","question":"Universite odenegi kimlere odenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 12"],"expected_evidence_keywords":["en yüksek"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
27
+ {"id":"2914_idari_007","question":"Idari gorev odenegi kimlere odenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 13"],"expected_evidence_keywords":["idari görev ödeneği"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
28
+ {"id":"2914_idari_008","question":"Birden fazla idari gorevi olanlara hangi idari gorev odenegi verilir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 13"],"expected_evidence_keywords":["en yüksek olanı"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
29
+ {"id":"2914_gelistirme_009","question":"Gelistirme odenegi hangi maddede duzenlenir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 14"],"expected_evidence_keywords":["geliştirme ödeneği"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
30
+ {"id":"2914_egitim_010","question":"Egitim ogretim odenegi nedir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Ek Madde 1"],"expected_evidence_keywords":["eğitim öğretim ödeneği"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
31
+ {"id":"2914_derece_011","question":"Derece yukseltilmesi sartlari nelerdir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 7"],"expected_evidence_keywords":["Şartıyla"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
32
+ {"id":"2914_kademe_012","question":"Kademe ilerlemesi hangi sartlara baglidir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 8"],"expected_evidence_keywords":["kademe ilerlemesi"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
33
+ {"id":"2914_onay_013","question":"2914 Madde 9 onay makami kimdir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 9"],"expected_evidence_keywords":["rektör"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
34
+ {"id":"2914_sosyal_014","question":"Ogretim elemanlari hangi sosyal haklardan yararlanir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 10"],"expected_evidence_keywords":["sosyal hak"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
35
+ {"id":"2914_sozlesmeli_015","question":"Emekli ogretim elemanlari sozlesmeli calistirilabilir mi?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 16"],"expected_evidence_keywords":["sözleşmeli"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
36
+ {"id":"2914_yabanci_016","question":"Yabanci ogretim elemanlari hakkinda 2914 ne duzenler?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 16"],"expected_evidence_keywords":["yabancı"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
37
+ {"id":"2914_ek_gosterge_017","question":"Ek gosterge cetveli nedir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["EK GÖSTERGE CETVELİ"],"expected_evidence_keywords":["EK GÖSTERGE"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
38
+ {"id":"2914_makam_018","question":"Makam tazminati cetveli kimleri kapsar?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["MAKAM TAZMİNATI CETVELİ"],"expected_evidence_keywords":["MAKAM TAZMİNATI"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
39
+ {"id":"2914_amac_019","question":"2914 sayili Kanunun amaci nedir?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 1"],"expected_evidence_keywords":["Bu Kanunun amacı"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
40
+ {"id":"2914_kapsam_020","question":"2914 sayili Kanun kimleri kapsar?","expected_document_ids":["TR-KANUN-2914"],"expected_articles":["Madde 2"],"expected_evidence_keywords":["öğretim"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
41
+ {"id":"2809_amac_001","question":"2809 sayili Kanunun amaci nedir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 1"],"expected_evidence_keywords":["teşkilatlanmasını"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
42
+ {"id":"2809_kapsam_002","question":"2809 sayili Kanun neyi kapsar?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 2"],"expected_evidence_keywords":["teşkilatlanması"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
43
+ {"id":"2809_birim_003","question":"Universite hangi birimlerden olusur?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 3"],"expected_evidence_keywords":["fakülte"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
44
+ {"id":"2809_fakulte_004","question":"Bir universitede en az kac fakulte bulunmalidir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 3"],"expected_evidence_keywords":["en az üç fakülte"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
45
+ {"id":"2809_alt_birim_005","question":"Alt birimlerin teskili 2809'a gore nasil duzenlenir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 3"],"expected_evidence_keywords":["2547 sayılı Kanunun 7 nci maddesi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
46
+ {"id":"2809_faaliyet_006","question":"Yeni kurulan birimler ne zaman faaliyete gecirilir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 6"],"expected_evidence_keywords":["faaliyete geçirilir"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
47
+ {"id":"2809_hizmetici_007","question":"Universiteler hizmetici egitim kurslari acmakla yukumludur mu?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 5"],"expected_evidence_keywords":["hizmetiçi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
48
+ {"id":"2809_kamu_imkan_008","question":"Yuksekogretim kurumlari hangi kurum imkanlarindan yararlanir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 4"],"expected_evidence_keywords":["kamu kurum"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
49
+ {"id":"2809_ankara_009","question":"Ankara Universitesi hangi maddede duzenlenir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 7"],"expected_evidence_keywords":["Ankara Üniversitesi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
50
+ {"id":"2809_odtu_010","question":"Orta Dogu Teknik Universitesi hangi maddede duzenlenir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 8"],"expected_evidence_keywords":["Ortadoğu Teknik Üniversitesi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
51
+ {"id":"2809_hacettepe_011","question":"Hacettepe Universitesi hangi maddede duzenlenir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 9"],"expected_evidence_keywords":["Hacettepe Üniversitesi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
52
+ {"id":"2809_gazi_012","question":"Gazi Universitesi hangi maddede kurulmustur?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 10"],"expected_evidence_keywords":["Gazi Üniversitesi"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
53
+ {"id":"2809_cumhurbaskani_013","question":"Cumhurbaskani fakulteleri kurma ve kapatma konusunda hangi yetkiye sahiptir?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Ek Madde 30"],"expected_evidence_keywords":["Cumhurbaşkanı"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
54
+ {"id":"2809_yururluk_014","question":"2809 sayili Kanun ne zaman yururluge girer?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 36"],"expected_evidence_keywords":["yürürlüğe"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
55
+ {"id":"2809_yurutme_015","question":"2809 sayili Kanunu kim yurutur?","expected_document_ids":["TR-KANUN-2809"],"expected_articles":["Madde 37"],"expected_evidence_keywords":["yürütür"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation"}
56
+ {"id":"cross_2914_2547_001","question":"2914 Madde 1 ogretim elemani tanimini hangi 2547 maddesine baglar?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 1","Madde 3"],"expected_evidence_keywords":["2547 sayılı"],"expected_edge_ids":["2914_m1_refs_2547_teacher_definition"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
57
+ {"id":"cross_2914_2547_002","question":"Ek ders ucreti ile 2547 ders yuku arasindaki iliski nedir?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 11","Madde 36"],"expected_evidence_keywords":["ek ders"],"expected_edge_ids":["2914_m11_details_2547_m36_ders_yuku"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
58
+ {"id":"cross_2809_2547_003","question":"2809 Madde 3 alt birimleri hangi 2547 maddesine baglar?","expected_document_ids":["TR-KANUN-2809","TR-KANUN-2547"],"expected_articles":["Madde 3","Madde 7"],"expected_evidence_keywords":["2547 sayılı Kanunun 7 nci maddesi"],"expected_edge_ids":["2809_m3_refs_2547_m7_unit_regulation"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
59
+ {"id":"cross_universite_kurulus_004","question":"Universite nasil kurulur? 2547 ve 2809 birlikte nasil okunur?","expected_document_ids":["TR-KANUN-2547","TR-KANUN-2809"],"expected_articles":["Madde 7","Madde 3"],"expected_evidence_keywords":["üniversite"],"expected_edge_ids":["2809_m3_refs_2547_m7_unit_regulation"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
60
+ {"id":"cross_ders_yuku_005","question":"2914 ek ders ucreti 2547 Madde 36 ders yuku ile nasil baglantilidir?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 11","Madde 36"],"expected_evidence_keywords":["ders yükü"],"expected_edge_ids":["2914_m11_details_2547_m36_ders_yuku"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
61
+ {"id":"cross_ogretim_tanim_006","question":"2914 kapsamindaki ogretim elemani ifadesi 2547'deki hangi tanima dayanir?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 1","Madde 3"],"expected_evidence_keywords":["öğretim elemanları"],"expected_edge_ids":["2914_m1_refs_2547_teacher_definition"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
62
+ {"id":"cross_teskil_yok_007","question":"2809 alt birim teskili ile YOK'un 2547 Madde 7 yetkisi arasindaki bag nedir?","expected_document_ids":["TR-KANUN-2809","TR-KANUN-2547"],"expected_articles":["Madde 3","Madde 7"],"expected_evidence_keywords":[],"expected_edge_ids":["2809_m3_refs_2547_m7_unit_regulation"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
63
+ {"id":"cross_personel_kapsam_008","question":"2914 kapsam maddesi 2547'ye tabi universite ogretim elemanlarini nasil kullanir?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 2","Madde 3"],"expected_evidence_keywords":["2547"],"expected_edge_ids":["2914_m1_refs_2547_teacher_definition"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
64
+ {"id":"cross_kurum_birim_009","question":"Universitenin birimlerden olusmasi 2809'da, bunlarin duzenlenmesi 2547'de nasil baglanir?","expected_document_ids":["TR-KANUN-2809","TR-KANUN-2547"],"expected_articles":["Madde 3","Madde 7"],"expected_evidence_keywords":["alt birimlerinin"],"expected_edge_ids":["2809_m3_refs_2547_m7_unit_regulation"],"forbidden_document_ids":["TR-KANUN-2914"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
65
+ {"id":"cross_personel_odeme_010","question":"Ek ders ucretinde 2914 odeme kuralini, 2547 hangi ders yuku baglamiyla tamamlar?","expected_document_ids":["TR-KANUN-2914","TR-KANUN-2547"],"expected_articles":["Madde 11","Madde 36"],"expected_evidence_keywords":["ek ders"],"expected_edge_ids":["2914_m11_details_2547_m36_ders_yuku"],"forbidden_document_ids":["TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"source_grounded_explanation","cross_document":true}
66
+ {"id":"oos_001","question":"Bugun hava nasil?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
67
+ {"id":"oos_002","question":"Bana makarna tarifi verir misin?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
68
+ {"id":"oos_003","question":"Futbol maci kac kac bitti?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
69
+ {"id":"oos_004","question":"Borsa ne olur?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
70
+ {"id":"oos_005","question":"Bir siir yazar misin?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
71
+ {"id":"oos_006","question":"Python ile oyun kodlar misin?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
72
+ {"id":"oos_007","question":"Film onerisi verir misin?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
73
+ {"id":"oos_008","question":"Kripto para alayim mi?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
74
+ {"id":"oos_009","question":"Bir mail taslagi yazar misin?","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
75
+ {"id":"oos_010","question":"Muzik listesi hazirla","expected_document_ids":[],"expected_articles":[],"expected_evidence_keywords":[],"forbidden_document_ids":["TR-KANUN-2547","TR-KANUN-2914","TR-KANUN-2809"],"forbidden_articles":[],"answer_type":"out_of_scope"}
76
+
engine.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from clause_retrieval import init_clause_retrieval, load_ontology
6
+ from config import (
7
+ CORPUS_MCKF_ONTOLOGY_PATH,
8
+ MCKF_ONTOLOGY_PATH,
9
+ MCKF_VALIDATION_REPORT_PATH,
10
+ RUNTIME_BUILD_MANIFEST_PATH,
11
+ )
12
+ from normative_contract import file_sha256
13
+
14
+ BOOTSTRAPPED = False
15
+ RUNTIME_BUILD_INFO: dict = {}
16
+
17
+
18
+ def bootstrap_once(force: bool = False) -> None:
19
+ """Load one validated, immutable runtime build as the knowledge source."""
20
+ global BOOTSTRAPPED, RUNTIME_BUILD_INFO
21
+ if BOOTSTRAPPED and not force:
22
+ return
23
+ ontology_path = CORPUS_MCKF_ONTOLOGY_PATH if CORPUS_MCKF_ONTOLOGY_PATH.exists() else MCKF_ONTOLOGY_PATH
24
+ manifest = _load_json(RUNTIME_BUILD_MANIFEST_PATH)
25
+ validation = _load_json(MCKF_VALIDATION_REPORT_PATH)
26
+ ontology = load_ontology(ontology_path)
27
+ if manifest:
28
+ expected_build = str(manifest.get("build_id", "") or "")
29
+ actual_build = str(ontology.get("build_id", "") or "")
30
+ if not expected_build or expected_build != actual_build:
31
+ raise RuntimeError("MCKF runtime build_id uyuşmazlığı: corpus ve manifest aynı pakete ait değil.")
32
+ expected_hash = str(
33
+ ((manifest.get("artifacts", {}) or {}).get(ontology_path.name, {}) or {}).get("sha256", "")
34
+ )
35
+ if expected_hash and file_sha256(ontology_path) != expected_hash:
36
+ raise RuntimeError("MCKF corpus dosya özeti manifest ile uyuşmuyor.")
37
+ if validation and validation.get("conforms") is not True:
38
+ raise RuntimeError("MCKF corpus doğrulama kapısından geçmedi; runtime başlatılmadı.")
39
+ init_clause_retrieval(ontology)
40
+ RUNTIME_BUILD_INFO = {
41
+ "build_id": ontology.get("build_id", "legacy-unversioned"),
42
+ "schema": ontology.get("schema", ""),
43
+ "manifest": bool(manifest),
44
+ "validation_conforms": validation.get("conforms") if validation else None,
45
+ }
46
+ BOOTSTRAPPED = True
47
+
48
+
49
+ def reload_runtime() -> dict:
50
+ bootstrap_once(force=True)
51
+ return dict(RUNTIME_BUILD_INFO)
52
+
53
+
54
+ def runtime_build_info() -> dict:
55
+ return dict(RUNTIME_BUILD_INFO)
56
+
57
+
58
+ def _load_json(path) -> dict:
59
+ if not path.exists():
60
+ return {}
61
+ try:
62
+ return json.loads(path.read_text(encoding="utf-8"))
63
+ except Exception:
64
+ return {}