Spaces:
Sleeping
Sleeping
mayar-waleed commited on
Commit ·
a52abbb
1
Parent(s): 101a9d7
fastapi
Browse files- EVALUATION_README.md +0 -153
- EVALUATION_SETUP.md +0 -173
- app/config.py +34 -0
- app/deps.py +31 -0
- app/main.py +96 -0
- app/rag_pipeline.py +423 -0
- app/schemas.py +29 -0
- app/utils.py +35 -0
- app_final.py +0 -625
- app_final_pheonix.py +0 -838
- app_final_updated.py +0 -704
- data/Egyptian_Civil.json +0 -0
- data/Egyptian_Constitution_legalnature_only.json +0 -304
- data/Egyptian_Labour_Law.json +0 -0
- data/Egyptian_Personal Status Laws.json +0 -0
- data/Technology Crimes Law.json +0 -0
- data/قانون_الإجراءات_الجنائية.json +0 -0
- evaluate.py +0 -620
- evaluate_rag.py +9 -12
- evaluation_results.json +0 -9
- models.py +0 -27
- rag.py +0 -840
- rag_test_questions_100.json +0 -1380
- rag_test_questions_10_mixed.json +0 -172
- rag_test_runner.py +0 -270
- ragas_dataset_100.csv +0 -101
- ragas_eval.py +0 -106
EVALUATION_README.md
DELETED
|
@@ -1,153 +0,0 @@
|
|
| 1 |
-
# RAG Evaluation with Ragas
|
| 2 |
-
|
| 3 |
-
## 📊 Evaluation Metrics
|
| 4 |
-
|
| 5 |
-
This evaluation uses **Ragas** library to measure RAG system quality:
|
| 6 |
-
|
| 7 |
-
| Metric | Description | Range | Goal |
|
| 8 |
-
|--------|-------------|-------|------|
|
| 9 |
-
| **faithfulness** | Is the answer grounded in the context? | 0-1 | Higher |
|
| 10 |
-
| **answer_relevancy** | Does the answer relate to the question? | 0-1 | Higher |
|
| 11 |
-
| **context_precision** | How much retrieved context was relevant? | 0-1 | Higher |
|
| 12 |
-
| **context_recall** | Did we retrieve all needed information? | 0-1 | Higher |
|
| 13 |
-
| **context_relevancy** | Overall relevance of context to question | 0-1 | Higher |
|
| 14 |
-
|
| 15 |
-
## 🚀 How to Run
|
| 16 |
-
|
| 17 |
-
### 1. Prerequisites
|
| 18 |
-
|
| 19 |
-
Make sure you have:
|
| 20 |
-
- ✅ Ragas installed: `pip install ragas datasets`
|
| 21 |
-
- ✅ OpenAI API key set in `.env` file (Ragas uses it for evaluation)
|
| 22 |
-
- ✅ Your RAG system working (test with `streamlit run app_final.py`)
|
| 23 |
-
|
| 24 |
-
### 2. Prepare Test Dataset
|
| 25 |
-
|
| 26 |
-
Edit `test_dataset.json` with your test questions:
|
| 27 |
-
|
| 28 |
-
```json
|
| 29 |
-
[
|
| 30 |
-
{
|
| 31 |
-
"question": "Your question in Arabic",
|
| 32 |
-
"ground_truth": "Expected correct answer (optional but recommended)"
|
| 33 |
-
}
|
| 34 |
-
]
|
| 35 |
-
```
|
| 36 |
-
|
| 37 |
-
### 3. Run Evaluation
|
| 38 |
-
|
| 39 |
-
```bash
|
| 40 |
-
python evaluate_rag.py
|
| 41 |
-
```
|
| 42 |
-
|
| 43 |
-
### 4. Check Results
|
| 44 |
-
|
| 45 |
-
The script will generate:
|
| 46 |
-
- `evaluation_results.json` - Summary metrics
|
| 47 |
-
- `evaluation_detailed.json` - Full Q&A pairs with contexts
|
| 48 |
-
|
| 49 |
-
## 📈 Understanding Results
|
| 50 |
-
|
| 51 |
-
### Good Scores (Target)
|
| 52 |
-
- **faithfulness**: > 0.7 (answers stay within context)
|
| 53 |
-
- **answer_relevancy**: > 0.8 (answers address the question)
|
| 54 |
-
- **context_precision**: > 0.6 (low noise in retrieval)
|
| 55 |
-
- **context_recall**: > 0.7 (retrieved all needed info)
|
| 56 |
-
- **context_relevancy**: > 0.7 (context matches question)
|
| 57 |
-
|
| 58 |
-
### If Scores Are Low
|
| 59 |
-
|
| 60 |
-
**Low Faithfulness (<0.5)**
|
| 61 |
-
- LLM is hallucinating or adding external info
|
| 62 |
-
- Solution: Adjust prompt to be stricter about using only context
|
| 63 |
-
|
| 64 |
-
**Low Answer Relevancy (<0.6)**
|
| 65 |
-
- Answers don't match questions
|
| 66 |
-
- Solution: Check if retrieval is getting right documents
|
| 67 |
-
|
| 68 |
-
**Low Context Precision (<0.4)**
|
| 69 |
-
- Too much irrelevant context retrieved
|
| 70 |
-
- Solution: Tune retrieval k parameter, adjust reranker top_n
|
| 71 |
-
|
| 72 |
-
**Low Context Recall (<0.5)**
|
| 73 |
-
- Missing important information
|
| 74 |
-
- Solution: Increase k in retrievers, check if documents have the info
|
| 75 |
-
|
| 76 |
-
**Low Context Relevancy (<0.5)**
|
| 77 |
-
- Retrieved documents don't match question
|
| 78 |
-
- Solution: Check embeddings, tune hybrid search weights
|
| 79 |
-
|
| 80 |
-
## 🔧 Customization
|
| 81 |
-
|
| 82 |
-
### Add More Test Questions
|
| 83 |
-
|
| 84 |
-
Edit `test_dataset.json`:
|
| 85 |
-
```json
|
| 86 |
-
{
|
| 87 |
-
"question": "ما هي شروط الترشح للبرلمان؟",
|
| 88 |
-
"ground_truth": "يجب أن يكون مصرياً، متمتعاً بحقوقه المدنية والسياسية..."
|
| 89 |
-
}
|
| 90 |
-
```
|
| 91 |
-
|
| 92 |
-
### Change Metrics
|
| 93 |
-
|
| 94 |
-
Edit `evaluate_rag.py` and modify the metrics list:
|
| 95 |
-
```python
|
| 96 |
-
evaluation_results = evaluate(
|
| 97 |
-
dataset,
|
| 98 |
-
metrics=[
|
| 99 |
-
faithfulness,
|
| 100 |
-
answer_relevancy,
|
| 101 |
-
# Add or remove metrics here
|
| 102 |
-
],
|
| 103 |
-
)
|
| 104 |
-
```
|
| 105 |
-
|
| 106 |
-
### Use Different LLM for Evaluation
|
| 107 |
-
|
| 108 |
-
Ragas supports different LLMs. Edit the evaluation call:
|
| 109 |
-
```python
|
| 110 |
-
from ragas.llms import LangchainLLMWrapper
|
| 111 |
-
from langchain_groq import ChatGroq
|
| 112 |
-
|
| 113 |
-
# Use Groq instead of OpenAI for evaluation
|
| 114 |
-
evaluator_llm = LangchainLLMWrapper(ChatGroq(model="llama-3.1-70b-versatile"))
|
| 115 |
-
|
| 116 |
-
evaluation_results = evaluate(
|
| 117 |
-
dataset,
|
| 118 |
-
metrics=[...],
|
| 119 |
-
llm=evaluator_llm
|
| 120 |
-
)
|
| 121 |
-
```
|
| 122 |
-
|
| 123 |
-
## 📝 Notes
|
| 124 |
-
|
| 125 |
-
1. **OpenAI API Cost**: Ragas uses OpenAI API for evaluation (GPT-3.5/4). Each evaluation costs ~$0.01-0.10 depending on dataset size.
|
| 126 |
-
|
| 127 |
-
2. **Ground Truth**: While optional, providing ground_truth improves `context_recall` metric accuracy.
|
| 128 |
-
|
| 129 |
-
3. **Arabic Support**: Ragas works with Arabic text, but evaluation quality depends on the LLM used (GPT-4 is better for Arabic than GPT-3.5).
|
| 130 |
-
|
| 131 |
-
4. **Batch Size**: For large datasets (>50 questions), consider splitting into batches to avoid API rate limits.
|
| 132 |
-
|
| 133 |
-
## 🐛 Troubleshooting
|
| 134 |
-
|
| 135 |
-
**Error: "OpenAI API key not found"**
|
| 136 |
-
- Add `OPENAI_API_KEY=sk-...` to your `.env` file
|
| 137 |
-
|
| 138 |
-
**Error: "Rate limit exceeded"**
|
| 139 |
-
- Wait a few minutes or reduce test dataset size
|
| 140 |
-
|
| 141 |
-
**Error: "Import error"**
|
| 142 |
-
- Run: `pip install ragas datasets openai langchain-openai`
|
| 143 |
-
|
| 144 |
-
**Metrics return NaN or 0**
|
| 145 |
-
- Check if answers/contexts are empty
|
| 146 |
-
- Ensure ground_truth is provided for context_recall
|
| 147 |
-
- Try with a smaller dataset first (2-3 questions)
|
| 148 |
-
|
| 149 |
-
## 📚 Resources
|
| 150 |
-
|
| 151 |
-
- [Ragas Documentation](https://docs.ragas.io/)
|
| 152 |
-
- [Ragas GitHub](https://github.com/explodinggradients/ragas)
|
| 153 |
-
- [RAG Evaluation Best Practices](https://docs.ragas.io/en/latest/concepts/metrics/index.html)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EVALUATION_SETUP.md
DELETED
|
@@ -1,173 +0,0 @@
|
|
| 1 |
-
# RAG Evaluation Setup - Summary
|
| 2 |
-
|
| 3 |
-
## 📁 Files Created
|
| 4 |
-
|
| 5 |
-
1. **evaluate_rag.py** - Full evaluation script with detailed explanations
|
| 6 |
-
2. **quick_eval.py** - Simple evaluation script (faster to run)
|
| 7 |
-
3. **test_dataset.json** - Test questions and ground truth answers
|
| 8 |
-
4. **EVALUATION_README.md** - Complete documentation
|
| 9 |
-
|
| 10 |
-
## 🎯 Quick Start (3 Steps)
|
| 11 |
-
|
| 12 |
-
### Step 1: Add OpenAI API Key
|
| 13 |
-
Add to your `.env` file:
|
| 14 |
-
```
|
| 15 |
-
OPENAI_API_KEY=sk-your-key-here
|
| 16 |
-
```
|
| 17 |
-
|
| 18 |
-
### Step 2: Edit Test Questions (Optional)
|
| 19 |
-
Edit `test_dataset.json` to add/modify test questions
|
| 20 |
-
|
| 21 |
-
### Step 3: Run Evaluation
|
| 22 |
-
```bash
|
| 23 |
-
# Option A: Quick evaluation
|
| 24 |
-
python quick_eval.py
|
| 25 |
-
|
| 26 |
-
# Option B: Full evaluation with explanations
|
| 27 |
-
python evaluate_rag.py
|
| 28 |
-
```
|
| 29 |
-
|
| 30 |
-
## 📊 What Gets Evaluated
|
| 31 |
-
|
| 32 |
-
### The 6 Ragas Metrics:
|
| 33 |
-
|
| 34 |
-
1. **faithfulness** (0-1, higher better)
|
| 35 |
-
- Is answer grounded in context?
|
| 36 |
-
- Checks if model added external information
|
| 37 |
-
|
| 38 |
-
2. **answer_relevancy** (0-1, higher better)
|
| 39 |
-
- Does answer match the question?
|
| 40 |
-
- Checks if answer is on-topic
|
| 41 |
-
|
| 42 |
-
3. **context_precision** (0-1, higher better)
|
| 43 |
-
- How much retrieved context was useful?
|
| 44 |
-
- Measures retrieval signal-to-noise ratio
|
| 45 |
-
|
| 46 |
-
4. **context_recall** (0-1, higher better)
|
| 47 |
-
- Did we retrieve all needed info?
|
| 48 |
-
- Requires ground_truth to measure
|
| 49 |
-
|
| 50 |
-
5. **context_relevancy** (0-1, higher better)
|
| 51 |
-
- Overall context relevance to question
|
| 52 |
-
- Measures retrieval quality
|
| 53 |
-
|
| 54 |
-
6. **response_relevancy** (included in answer_relevancy)
|
| 55 |
-
- Similar to answer_relevancy
|
| 56 |
-
|
| 57 |
-
## 📈 Expected Scores
|
| 58 |
-
|
| 59 |
-
### Good Performance:
|
| 60 |
-
- faithfulness: > 0.7
|
| 61 |
-
- answer_relevancy: > 0.8
|
| 62 |
-
- context_precision: > 0.6
|
| 63 |
-
- context_recall: > 0.7
|
| 64 |
-
- context_relevancy: > 0.7
|
| 65 |
-
|
| 66 |
-
### If Scores Are Low:
|
| 67 |
-
|
| 68 |
-
**Low Faithfulness?**
|
| 69 |
-
→ Tighten prompt to avoid hallucinations
|
| 70 |
-
|
| 71 |
-
**Low Answer Relevancy?**
|
| 72 |
-
→ Check retrieval quality
|
| 73 |
-
|
| 74 |
-
**Low Context Precision?**
|
| 75 |
-
→ Reduce k in retrievers or increase reranker top_n
|
| 76 |
-
|
| 77 |
-
**Low Context Recall?**
|
| 78 |
-
→ Increase k in retrievers, check if info exists
|
| 79 |
-
|
| 80 |
-
**Low Context Relevancy?**
|
| 81 |
-
→ Adjust hybrid search beta weights
|
| 82 |
-
|
| 83 |
-
## 🔧 Tuning Your System
|
| 84 |
-
|
| 85 |
-
Based on evaluation results, you can tune:
|
| 86 |
-
|
| 87 |
-
1. **In app_final.py:**
|
| 88 |
-
- Line 172: `base_retriever k` (semantic search)
|
| 89 |
-
- Line 208: `bm25_retriever k` (keyword search)
|
| 90 |
-
- Line 260: `metadata_retriever k` (metadata filter)
|
| 91 |
-
- Line 335: `beta_semantic, beta_keyword, beta_metadata` (hybrid weights)
|
| 92 |
-
- Line 427: `compressor top_n` (reranker final count)
|
| 93 |
-
- Line 440: `temperature` (LLM creativity)
|
| 94 |
-
|
| 95 |
-
2. **Test Changes:**
|
| 96 |
-
```bash
|
| 97 |
-
# After tuning parameters
|
| 98 |
-
python quick_eval.py
|
| 99 |
-
# Compare new scores with previous results
|
| 100 |
-
```
|
| 101 |
-
|
| 102 |
-
## 💡 Tips
|
| 103 |
-
|
| 104 |
-
1. **Start Small**: Test with 3-5 questions first
|
| 105 |
-
2. **Add Ground Truth**: Improves context_recall accuracy
|
| 106 |
-
3. **Compare Before/After**: Save results before making changes
|
| 107 |
-
4. **Use GPT-4**: Set `OPENAI_MODEL=gpt-4` for better Arabic evaluation
|
| 108 |
-
|
| 109 |
-
## 📝 Example Workflow
|
| 110 |
-
|
| 111 |
-
```bash
|
| 112 |
-
# 1. Baseline evaluation
|
| 113 |
-
python quick_eval.py
|
| 114 |
-
# Save as: evaluation_baseline.json
|
| 115 |
-
|
| 116 |
-
# 2. Tune parameter (e.g., increase reranker top_n from 5 to 7)
|
| 117 |
-
# Edit app_final.py line 427: top_n=7
|
| 118 |
-
|
| 119 |
-
# 3. Re-evaluate
|
| 120 |
-
python quick_eval.py
|
| 121 |
-
# Save as: evaluation_tuned.json
|
| 122 |
-
|
| 123 |
-
# 4. Compare scores
|
| 124 |
-
# If improved → keep change
|
| 125 |
-
# If worse → revert and try different parameter
|
| 126 |
-
```
|
| 127 |
-
|
| 128 |
-
## 🐛 Common Issues
|
| 129 |
-
|
| 130 |
-
**"OpenAI API key not found"**
|
| 131 |
-
```bash
|
| 132 |
-
# Check .env file has:
|
| 133 |
-
OPENAI_API_KEY=sk-...
|
| 134 |
-
```
|
| 135 |
-
|
| 136 |
-
**"Rate limit exceeded"**
|
| 137 |
-
```bash
|
| 138 |
-
# Wait 1 minute and retry, or reduce test dataset size
|
| 139 |
-
```
|
| 140 |
-
|
| 141 |
-
**"Import ragas failed"**
|
| 142 |
-
```bash
|
| 143 |
-
pip install ragas datasets openai langchain-openai
|
| 144 |
-
```
|
| 145 |
-
|
| 146 |
-
**Scores all 0 or NaN**
|
| 147 |
-
```bash
|
| 148 |
-
# Check:
|
| 149 |
-
# 1. Answers are being generated (not empty)
|
| 150 |
-
# 2. Contexts are being retrieved
|
| 151 |
-
# 3. Ground truth is provided for context_recall
|
| 152 |
-
```
|
| 153 |
-
|
| 154 |
-
## 📚 Output Files
|
| 155 |
-
|
| 156 |
-
After running evaluation:
|
| 157 |
-
- `evaluation_results.json` - Summary metrics
|
| 158 |
-
- `evaluation_detailed.json` - Full Q&A with contexts
|
| 159 |
-
- Console output - Formatted metrics display
|
| 160 |
-
|
| 161 |
-
## 🎓 Next Steps
|
| 162 |
-
|
| 163 |
-
1. Run baseline evaluation
|
| 164 |
-
2. Identify lowest-scoring metric
|
| 165 |
-
3. Tune relevant parameters
|
| 166 |
-
4. Re-evaluate and compare
|
| 167 |
-
5. Repeat until satisfied
|
| 168 |
-
|
| 169 |
-
---
|
| 170 |
-
|
| 171 |
-
**Need Help?**
|
| 172 |
-
- Ragas Docs: https://docs.ragas.io/
|
| 173 |
-
- Example Notebooks: https://github.com/explodinggradients/ragas/tree/main/docs/examples
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/config.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
|
| 9 |
+
load_dotenv()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass(frozen=True)
|
| 13 |
+
class Settings:
|
| 14 |
+
base_dir: str = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 15 |
+
data_dir: str = os.path.join(base_dir, "data")
|
| 16 |
+
chroma_dir: str = os.path.join(base_dir, "chroma_db")
|
| 17 |
+
|
| 18 |
+
groq_api_key: str = os.getenv("GROQ_API_KEY", "")
|
| 19 |
+
groq_model_name: str = os.getenv("GROQ_MODEL_NAME", "llama-3.3-70b-versatile")
|
| 20 |
+
|
| 21 |
+
reranker_model_path: str = os.getenv("RERANKER_MODEL_PATH", "")
|
| 22 |
+
|
| 23 |
+
semantic_k: int = int(os.getenv("SEMANTIC_K", "10"))
|
| 24 |
+
bm25_k: int = int(os.getenv("BM25_K", "10"))
|
| 25 |
+
meta_k: int = int(os.getenv("META_K", "10"))
|
| 26 |
+
hybrid_top_k: int = int(os.getenv("HYBRID_TOP_K", "12"))
|
| 27 |
+
rrf_k: int = int(os.getenv("RRF_K", "60"))
|
| 28 |
+
|
| 29 |
+
temperature: float = float(os.getenv("LLM_TEMPERATURE", "0.2"))
|
| 30 |
+
max_tokens: int = int(os.getenv("LLM_MAX_TOKENS", "2048"))
|
| 31 |
+
top_p: float = float(os.getenv("LLM_TOP_P", "0.85"))
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
settings = Settings()
|
app/deps.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# ============================================
|
| 3 |
+
# file: app/deps.py
|
| 4 |
+
# ============================================
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import threading
|
| 8 |
+
from typing import Any, Optional
|
| 9 |
+
|
| 10 |
+
from .config import settings
|
| 11 |
+
from .rag_pipeline import build_qa_chain
|
| 12 |
+
|
| 13 |
+
_lock = threading.RLock()
|
| 14 |
+
_chain: Optional[Any] = None
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def get_chain():
|
| 18 |
+
global _chain
|
| 19 |
+
with _lock:
|
| 20 |
+
if _chain is None:
|
| 21 |
+
_chain = build_qa_chain(settings)
|
| 22 |
+
return _chain
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def reload_chain():
|
| 26 |
+
global _chain
|
| 27 |
+
with _lock:
|
| 28 |
+
_chain = build_qa_chain(settings)
|
| 29 |
+
return _chain
|
| 30 |
+
|
| 31 |
+
|
app/main.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ============================================
|
| 2 |
+
# file: app/main.py
|
| 3 |
+
# ============================================
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
from typing import List
|
| 7 |
+
|
| 8 |
+
import anyio
|
| 9 |
+
from fastapi import FastAPI, HTTPException
|
| 10 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
+
|
| 12 |
+
from .deps import get_chain, reload_chain
|
| 13 |
+
from .schemas import AskRequest, AskResponse, SourceDoc
|
| 14 |
+
from .utils import convert_to_eastern_arabic
|
| 15 |
+
|
| 16 |
+
app = FastAPI(title="Legal RAG API", version="1.0.0")
|
| 17 |
+
|
| 18 |
+
# Optional: allow frontend calls
|
| 19 |
+
app.add_middleware(
|
| 20 |
+
CORSMiddleware,
|
| 21 |
+
allow_origins=["*"], # tighten later
|
| 22 |
+
allow_credentials=True,
|
| 23 |
+
allow_methods=["*"],
|
| 24 |
+
allow_headers=["*"],
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@app.on_event("startup")
|
| 29 |
+
def _startup():
|
| 30 |
+
# preload once
|
| 31 |
+
get_chain()
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@app.get("/health")
|
| 35 |
+
def health():
|
| 36 |
+
return {"status": "ok"}
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@app.post("/reload")
|
| 40 |
+
def reload():
|
| 41 |
+
reload_chain()
|
| 42 |
+
return {"status": "reloaded"}
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _dedupe_sources(docs) -> List[SourceDoc]:
|
| 46 |
+
if not docs:
|
| 47 |
+
return []
|
| 48 |
+
seen = set()
|
| 49 |
+
out: List[SourceDoc] = []
|
| 50 |
+
for doc in docs:
|
| 51 |
+
article_num = str(doc.metadata.get("article_number", "")).strip()
|
| 52 |
+
if article_num and article_num in seen:
|
| 53 |
+
continue
|
| 54 |
+
if article_num:
|
| 55 |
+
seen.add(article_num)
|
| 56 |
+
out.append(
|
| 57 |
+
SourceDoc(
|
| 58 |
+
article_id=str(doc.metadata.get("article_id", "")) or None,
|
| 59 |
+
article_number=article_num or None,
|
| 60 |
+
law_name=str(doc.metadata.get("law_name", "")) or None,
|
| 61 |
+
legal_nature=str(doc.metadata.get("legal_nature", "")) or None,
|
| 62 |
+
keywords=str(doc.metadata.get("keywords", "")) or None,
|
| 63 |
+
part=str(doc.metadata.get("part", "")) or None,
|
| 64 |
+
chapter=str(doc.metadata.get("chapter", "")) or None,
|
| 65 |
+
page_content=str(doc.page_content or ""),
|
| 66 |
+
)
|
| 67 |
+
)
|
| 68 |
+
return out
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@app.post("/ask", response_model=AskResponse)
|
| 72 |
+
async def ask(payload: AskRequest):
|
| 73 |
+
chain = get_chain()
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
# LangChain invoke is sync; run in worker thread
|
| 77 |
+
result = await anyio.to_thread.run_sync(chain.invoke, payload.query)
|
| 78 |
+
except Exception as e:
|
| 79 |
+
raise HTTPException(status_code=500, detail=str(e)) from e
|
| 80 |
+
|
| 81 |
+
answer = result.get("answer", "")
|
| 82 |
+
sources_docs = result.get("context", []) if payload.include_sources else []
|
| 83 |
+
sources = _dedupe_sources(sources_docs)
|
| 84 |
+
|
| 85 |
+
if payload.eastern_arabic_numerals:
|
| 86 |
+
answer = convert_to_eastern_arabic(answer)
|
| 87 |
+
if payload.include_sources:
|
| 88 |
+
for s in sources:
|
| 89 |
+
s.page_content = convert_to_eastern_arabic(s.page_content)
|
| 90 |
+
if s.article_number:
|
| 91 |
+
s.article_number = convert_to_eastern_arabic(s.article_number)
|
| 92 |
+
|
| 93 |
+
return AskResponse(answer=answer, sources=sources, raw=result)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
|
app/rag_pipeline.py
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ============================================
|
| 2 |
+
# file: app/rag_pipeline.py
|
| 3 |
+
# ============================================
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import logging
|
| 8 |
+
import os
|
| 9 |
+
import re
|
| 10 |
+
import warnings
|
| 11 |
+
from collections import defaultdict
|
| 12 |
+
from concurrent.futures import ThreadPoolExecutor
|
| 13 |
+
from typing import Dict, List, Set
|
| 14 |
+
|
| 15 |
+
import numpy as np
|
| 16 |
+
from langchain_chroma import Chroma
|
| 17 |
+
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 18 |
+
from langchain_core.documents import Document
|
| 19 |
+
from langchain_core.output_parsers import StrOutputParser
|
| 20 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 21 |
+
from langchain_core.retrievers import BaseRetriever
|
| 22 |
+
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
|
| 23 |
+
from langchain_groq import ChatGroq
|
| 24 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 25 |
+
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 26 |
+
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 27 |
+
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 28 |
+
from rank_bm25 import BM25Okapi
|
| 29 |
+
|
| 30 |
+
from .config import Settings
|
| 31 |
+
from .utils import arabic_tokenize
|
| 32 |
+
|
| 33 |
+
os.environ["TRANSFORMERS_NO_PROGRESS_BAR"] = "1"
|
| 34 |
+
warnings.filterwarnings("ignore")
|
| 35 |
+
|
| 36 |
+
logger = logging.getLogger(__name__)
|
| 37 |
+
logging.basicConfig(level=logging.INFO)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _load_json_folder(folder_path: str) -> List[dict]:
|
| 41 |
+
all_items: List[dict] = []
|
| 42 |
+
for filename in os.listdir(folder_path):
|
| 43 |
+
if not filename.lower().endswith(".json"):
|
| 44 |
+
continue
|
| 45 |
+
file_path = os.path.join(folder_path, filename)
|
| 46 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 47 |
+
obj = json.load(f)
|
| 48 |
+
|
| 49 |
+
wrapper_law_name = ""
|
| 50 |
+
|
| 51 |
+
if isinstance(obj, list):
|
| 52 |
+
articles: List[dict] = []
|
| 53 |
+
for entry in obj:
|
| 54 |
+
if isinstance(entry, dict) and "data" in entry and isinstance(entry["data"], list):
|
| 55 |
+
wrapper_law_name = entry.get("law_name", "")
|
| 56 |
+
for art in entry["data"]:
|
| 57 |
+
art.setdefault("_law_name", wrapper_law_name)
|
| 58 |
+
articles.extend(entry["data"])
|
| 59 |
+
elif isinstance(entry, dict) and "articles" in entry and isinstance(entry["articles"], list):
|
| 60 |
+
wrapper_law_name = entry.get("law_name", "")
|
| 61 |
+
for art in entry["articles"]:
|
| 62 |
+
art.setdefault("_law_name", wrapper_law_name)
|
| 63 |
+
articles.extend(entry["articles"])
|
| 64 |
+
elif isinstance(entry, dict):
|
| 65 |
+
if not entry.get("_law_name"):
|
| 66 |
+
aid = entry.get("article_id", "")
|
| 67 |
+
entry["_law_name"] = "الدستور المصري" if "CONST" in str(aid).upper() else ""
|
| 68 |
+
articles.append(entry)
|
| 69 |
+
all_items.extend(articles)
|
| 70 |
+
elif isinstance(obj, dict):
|
| 71 |
+
wrapper_law_name = obj.get("law_name", "")
|
| 72 |
+
if "data" in obj and isinstance(obj["data"], list):
|
| 73 |
+
for art in obj["data"]:
|
| 74 |
+
art.setdefault("_law_name", wrapper_law_name)
|
| 75 |
+
all_items.extend(obj["data"])
|
| 76 |
+
elif "articles" in obj and isinstance(obj["articles"], list):
|
| 77 |
+
for art in obj["articles"]:
|
| 78 |
+
art.setdefault("_law_name", wrapper_law_name)
|
| 79 |
+
all_items.extend(obj["articles"])
|
| 80 |
+
else:
|
| 81 |
+
obj.setdefault("_law_name", wrapper_law_name)
|
| 82 |
+
all_items.append(obj)
|
| 83 |
+
else:
|
| 84 |
+
logger.warning("Unsupported JSON format in: %s", file_path)
|
| 85 |
+
|
| 86 |
+
return all_items
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def build_qa_chain(settings: Settings):
|
| 90 |
+
"""
|
| 91 |
+
Builds and returns:
|
| 92 |
+
qa_chain: Runnable that returns {"context": [Document...], "input": str, "answer": str}
|
| 93 |
+
"""
|
| 94 |
+
if not os.path.exists(settings.data_dir):
|
| 95 |
+
raise FileNotFoundError(f"Data folder not found: {settings.data_dir}")
|
| 96 |
+
|
| 97 |
+
data = _load_json_folder(settings.data_dir)
|
| 98 |
+
|
| 99 |
+
# de-dup
|
| 100 |
+
unique: Dict[str, dict] = {}
|
| 101 |
+
for item in data:
|
| 102 |
+
key = str(item.get("article_id") or item.get("article_number") or hash(json.dumps(item, ensure_ascii=False)))
|
| 103 |
+
unique[key] = item
|
| 104 |
+
data = list(unique.values())
|
| 105 |
+
|
| 106 |
+
docs: List[Document] = []
|
| 107 |
+
for item in data:
|
| 108 |
+
article_number = item.get("article_number")
|
| 109 |
+
original_text = item.get("original_text")
|
| 110 |
+
simplified_summary = item.get("simplified_summary")
|
| 111 |
+
if not article_number or not original_text or not simplified_summary:
|
| 112 |
+
continue
|
| 113 |
+
|
| 114 |
+
law_name = item.get("law_name") or item.get("_law_name", "")
|
| 115 |
+
part_bab = item.get("part (Bab)", "")
|
| 116 |
+
chapter_fasl = item.get("chapter (Fasl)", "")
|
| 117 |
+
section = item.get("section", "")
|
| 118 |
+
|
| 119 |
+
page_content = f"""القانون: {law_name}
|
| 120 |
+
رقم المادة: {article_number}
|
| 121 |
+
الباب: {part_bab}
|
| 122 |
+
الفصل: {chapter_fasl}
|
| 123 |
+
القسم: {section}
|
| 124 |
+
النص الأصلي: {original_text}
|
| 125 |
+
الشرح المبسط: {simplified_summary}"""
|
| 126 |
+
|
| 127 |
+
metadata = {
|
| 128 |
+
"article_id": item.get("article_id") or str(article_number),
|
| 129 |
+
"article_number": str(article_number),
|
| 130 |
+
"law_name": law_name,
|
| 131 |
+
"legal_nature": item.get("legal_nature", ""),
|
| 132 |
+
"keywords": ", ".join(item.get("keywords", []) or []),
|
| 133 |
+
"part": part_bab,
|
| 134 |
+
"chapter": chapter_fasl,
|
| 135 |
+
}
|
| 136 |
+
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 137 |
+
|
| 138 |
+
if not docs:
|
| 139 |
+
raise RuntimeError("No valid documents found in data folder (missing required fields).")
|
| 140 |
+
|
| 141 |
+
embeddings = HuggingFaceEmbeddings(model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1")
|
| 142 |
+
|
| 143 |
+
# vector store reuse
|
| 144 |
+
db_exists = os.path.exists(settings.chroma_dir) and os.listdir(settings.chroma_dir)
|
| 145 |
+
if db_exists:
|
| 146 |
+
vectorstore = Chroma(
|
| 147 |
+
persist_directory=settings.chroma_dir,
|
| 148 |
+
embedding_function=embeddings,
|
| 149 |
+
)
|
| 150 |
+
stored_count = vectorstore._collection.count()
|
| 151 |
+
if stored_count == 0 or abs(stored_count - len(docs)) > 5:
|
| 152 |
+
import shutil
|
| 153 |
+
|
| 154 |
+
shutil.rmtree(settings.chroma_dir, ignore_errors=True)
|
| 155 |
+
db_exists = False
|
| 156 |
+
|
| 157 |
+
if not db_exists:
|
| 158 |
+
vectorstore = Chroma.from_documents(
|
| 159 |
+
docs,
|
| 160 |
+
embeddings,
|
| 161 |
+
persist_directory=settings.chroma_dir,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
base_retriever = vectorstore.as_retriever(search_kwargs={"k": settings.semantic_k})
|
| 165 |
+
|
| 166 |
+
# -----------------------------
|
| 167 |
+
# BM25 retriever
|
| 168 |
+
# -----------------------------
|
| 169 |
+
class BM25Retriever(BaseRetriever):
|
| 170 |
+
corpus_docs: List[Document]
|
| 171 |
+
bm25: BM25Okapi = None
|
| 172 |
+
tokenized_corpus: list = None
|
| 173 |
+
k: int = 10
|
| 174 |
+
|
| 175 |
+
class Config:
|
| 176 |
+
arbitrary_types_allowed = True
|
| 177 |
+
|
| 178 |
+
def __init__(self, **data):
|
| 179 |
+
super().__init__(**data)
|
| 180 |
+
self.tokenized_corpus = [arabic_tokenize(doc.page_content) for doc in self.corpus_docs]
|
| 181 |
+
self.bm25 = BM25Okapi(self.tokenized_corpus)
|
| 182 |
+
|
| 183 |
+
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 184 |
+
tokenized_query = arabic_tokenize(query)
|
| 185 |
+
if not tokenized_query:
|
| 186 |
+
return []
|
| 187 |
+
scores = self.bm25.get_scores(tokenized_query)
|
| 188 |
+
top_indices = np.argsort(scores)[::-1][: self.k]
|
| 189 |
+
return [self.corpus_docs[i] for i in top_indices if scores[i] > 0]
|
| 190 |
+
|
| 191 |
+
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 192 |
+
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 193 |
+
|
| 194 |
+
bm25_retriever = BM25Retriever(corpus_docs=docs, k=settings.bm25_k)
|
| 195 |
+
|
| 196 |
+
# -----------------------------
|
| 197 |
+
# Metadata filter retriever
|
| 198 |
+
# -----------------------------
|
| 199 |
+
class MetadataFilterRetriever(BaseRetriever):
|
| 200 |
+
corpus_docs: List[Document]
|
| 201 |
+
keyword_index: Dict[str, Set[int]] = None
|
| 202 |
+
law_name_index: Dict[str, Set[int]] = None
|
| 203 |
+
k: int = 10
|
| 204 |
+
|
| 205 |
+
class Config:
|
| 206 |
+
arbitrary_types_allowed = True
|
| 207 |
+
|
| 208 |
+
def __init__(self, **data):
|
| 209 |
+
super().__init__(**data)
|
| 210 |
+
self.keyword_index = defaultdict(set)
|
| 211 |
+
self.law_name_index = defaultdict(set)
|
| 212 |
+
for idx, doc in enumerate(self.corpus_docs):
|
| 213 |
+
kw_text = (
|
| 214 |
+
str(doc.metadata.get("keywords", ""))
|
| 215 |
+
+ " "
|
| 216 |
+
+ str(doc.metadata.get("legal_nature", ""))
|
| 217 |
+
+ " "
|
| 218 |
+
+ str(doc.metadata.get("part", ""))
|
| 219 |
+
+ " "
|
| 220 |
+
+ str(doc.metadata.get("chapter", ""))
|
| 221 |
+
)
|
| 222 |
+
for token in arabic_tokenize(kw_text):
|
| 223 |
+
self.keyword_index[token].add(idx)
|
| 224 |
+
|
| 225 |
+
for token in arabic_tokenize(str(doc.metadata.get("law_name", ""))):
|
| 226 |
+
self.law_name_index[token].add(idx)
|
| 227 |
+
|
| 228 |
+
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 229 |
+
query_tokens = arabic_tokenize(query)
|
| 230 |
+
if not query_tokens:
|
| 231 |
+
return []
|
| 232 |
+
|
| 233 |
+
scores = defaultdict(float)
|
| 234 |
+
for token in query_tokens:
|
| 235 |
+
for idx in self.keyword_index.get(token, set()):
|
| 236 |
+
scores[idx] += 3.0
|
| 237 |
+
for idx in self.law_name_index.get(token, set()):
|
| 238 |
+
scores[idx] += 4.0
|
| 239 |
+
|
| 240 |
+
if not scores:
|
| 241 |
+
return []
|
| 242 |
+
|
| 243 |
+
top = sorted(scores.items(), key=lambda x: x[1], reverse=True)[: self.k]
|
| 244 |
+
return [self.corpus_docs[idx] for idx, _ in top]
|
| 245 |
+
|
| 246 |
+
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 247 |
+
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 248 |
+
|
| 249 |
+
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs, k=settings.meta_k)
|
| 250 |
+
|
| 251 |
+
# -----------------------------
|
| 252 |
+
# Hybrid RRF retriever (parallel)
|
| 253 |
+
# -----------------------------
|
| 254 |
+
class HybridRRFRetriever(BaseRetriever):
|
| 255 |
+
semantic_retriever: BaseRetriever
|
| 256 |
+
bm25_retriever: BM25Retriever
|
| 257 |
+
metadata_retriever: MetadataFilterRetriever
|
| 258 |
+
beta_semantic: float = 0.5
|
| 259 |
+
beta_keyword: float = 0.3
|
| 260 |
+
beta_metadata: float = 0.2
|
| 261 |
+
k: int = 60
|
| 262 |
+
top_k: int = 12
|
| 263 |
+
|
| 264 |
+
class Config:
|
| 265 |
+
arbitrary_types_allowed = True
|
| 266 |
+
|
| 267 |
+
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 268 |
+
with ThreadPoolExecutor(max_workers=3) as pool:
|
| 269 |
+
fut_sem = pool.submit(self.semantic_retriever.invoke, query)
|
| 270 |
+
fut_bm = pool.submit(self.bm25_retriever.invoke, query)
|
| 271 |
+
fut_meta = pool.submit(self.metadata_retriever.invoke, query)
|
| 272 |
+
|
| 273 |
+
semantic_docs = fut_sem.result()
|
| 274 |
+
bm25_docs = fut_bm.result()
|
| 275 |
+
metadata_docs = fut_meta.result()
|
| 276 |
+
|
| 277 |
+
rrf_scores: Dict[str, float] = {}
|
| 278 |
+
all_docs: Dict[str, Document] = {}
|
| 279 |
+
|
| 280 |
+
for weight, doc_list in [
|
| 281 |
+
(self.beta_semantic, semantic_docs),
|
| 282 |
+
(self.beta_keyword, bm25_docs),
|
| 283 |
+
(self.beta_metadata, metadata_docs),
|
| 284 |
+
]:
|
| 285 |
+
for rank, doc in enumerate(doc_list, start=1):
|
| 286 |
+
doc_id = doc.metadata.get("article_id") or doc.metadata.get("article_number") or str(hash(doc.page_content))
|
| 287 |
+
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0.0) + weight / (self.k + rank)
|
| 288 |
+
all_docs.setdefault(doc_id, doc)
|
| 289 |
+
|
| 290 |
+
sorted_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 291 |
+
return [all_docs[did] for did, _ in sorted_ids[: self.top_k] if did in all_docs]
|
| 292 |
+
|
| 293 |
+
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 294 |
+
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 295 |
+
|
| 296 |
+
hybrid_retriever = HybridRRFRetriever(
|
| 297 |
+
semantic_retriever=base_retriever,
|
| 298 |
+
bm25_retriever=bm25_retriever,
|
| 299 |
+
metadata_retriever=metadata_retriever,
|
| 300 |
+
beta_semantic=0.5,
|
| 301 |
+
beta_keyword=0.3,
|
| 302 |
+
beta_metadata=0.2,
|
| 303 |
+
k=settings.rrf_k,
|
| 304 |
+
top_k=settings.hybrid_top_k,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
# -----------------------------
|
| 308 |
+
# Reranker (CrossEncoder)
|
| 309 |
+
# -----------------------------
|
| 310 |
+
if not settings.reranker_model_path:
|
| 311 |
+
raise RuntimeError("RERANKER_MODEL_PATH is not set in .env (must point to your local reranker folder).")
|
| 312 |
+
if not os.path.exists(settings.reranker_model_path):
|
| 313 |
+
raise FileNotFoundError(f"Reranker path not found: {settings.reranker_model_path}")
|
| 314 |
+
|
| 315 |
+
cross_encoder = HuggingFaceCrossEncoder(model_name=settings.reranker_model_path)
|
| 316 |
+
compressor = CrossEncoderReranker(model=cross_encoder, top_n=5)
|
| 317 |
+
compression_retriever = ContextualCompressionRetriever(
|
| 318 |
+
base_compressor=compressor,
|
| 319 |
+
base_retriever=hybrid_retriever,
|
| 320 |
+
)
|
| 321 |
+
|
| 322 |
+
# -----------------------------
|
| 323 |
+
# LLM + prompt
|
| 324 |
+
# -----------------------------
|
| 325 |
+
if not settings.groq_api_key:
|
| 326 |
+
raise RuntimeError("GROQ_API_KEY is missing in .env")
|
| 327 |
+
|
| 328 |
+
llm = ChatGroq(
|
| 329 |
+
groq_api_key=settings.groq_api_key,
|
| 330 |
+
model_name=settings.groq_model_name,
|
| 331 |
+
temperature=settings.temperature,
|
| 332 |
+
max_tokens=settings.max_tokens,
|
| 333 |
+
model_kwargs={"top_p": settings.top_p},
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
system_instructions = """
|
| 337 |
+
<role>
|
| 338 |
+
أنت "المساعد القانوني الذكي"، مستشار قانوني متخصص في القوانين المصرية التالية:
|
| 339 |
+
- الدستور المصري
|
| 340 |
+
- القانون المدني المصري
|
| 341 |
+
- قانون العمل المصري
|
| 342 |
+
- قانون الأحوال الشخصية المصري
|
| 343 |
+
- قانون مكافحة جرائم تقنية المعلومات
|
| 344 |
+
- قانون الإجراءات الجنائية المصري
|
| 345 |
+
|
| 346 |
+
مهمتك الأساسية: الإجابة بدقة استناداً إلى "السياق التشريعي" المرفق أدناه.
|
| 347 |
+
عند وجود نص قانوني في السياق، هو مصدرك الأول والأهم.
|
| 348 |
+
</role>
|
| 349 |
+
|
| 350 |
+
<decision_logic>
|
| 351 |
+
حلّل سؤال المستخدم ثم اتبع أول حالة ينطبق شرطها:
|
| 352 |
+
|
| 353 |
+
━━━ الحالة ١ — الإجابة موجودة في السياق (الأولوية القصوى) ━━━
|
| 354 |
+
الشرط: توجد مادة أو أكثر في السياق تتناول موضوع السؤال بشكل مباشر أو وثيق الصلة.
|
| 355 |
+
الفعل:
|
| 356 |
+
• أجب من السياق مباشرةً دون مقدمات.
|
| 357 |
+
• وثّق كل معلومة بذكر اسم القانون ورقم المادة (مثال: «وفقاً للمادة (٥٢) من قانون العمل...»).
|
| 358 |
+
• استخرج ما يجيب السؤال تحديداً — لا تنسخ المادة كاملة.
|
| 359 |
+
• لا تضف معلومات من خارج السياق في هذه الحالة.
|
| 360 |
+
|
| 361 |
+
━━━ الحالة ٢ — السياق يغطي الموضوع جزئياً ━━━
|
| 362 |
+
الشرط: توجد مواد ذات صلة لكنها لا تجيب السؤال بالكامل.
|
| 363 |
+
الفعل:
|
| 364 |
+
• اذكر أولاً ما تنص عليه المواد المتاحة (مع التوثيق).
|
| 365 |
+
• ثم أضف توضيحاً عملياً مختصراً يساعد المستخدم، مع التنبيه بعبارة:
|
| 366 |
+
«ملاحظة عملية:» أو «من الناحية التطبيقية:» قبل أي إضافة.
|
| 367 |
+
• لا تخترع أرقام مواد أو تنسب نصوصاً لقوانين لم ترد في السياق.
|
| 368 |
+
|
| 369 |
+
━━━ الحالة ٣ — السياق لا يحتوي الإجابة + السؤال إجرائي/عملي ━━━
|
| 370 |
+
الشرط: لا توجد مادة في السياق تتعلق بالموضوع، لكن السؤال عن إجراءات عملية (بلاغ، محضر، حادث، طلاق، تعامل مع الشرطة...).
|
| 371 |
+
الفعل:
|
| 372 |
+
• ابدأ بعبارة: «بناءً على الإجراءات القانونية المتعارف عليها في مصر (وليس استناداً لنص قانوني محدد من قاعدة البيانات):»
|
| 373 |
+
• قدم خطوات عملية مرقمة ومختصرة.
|
| 374 |
+
• لا تذكر أرقام مواد — لا تختـرع مراجع.
|
| 375 |
+
• أنهِ بـ«يُنصح بمراجعة محامٍ متخصص لتأكيد الإجراءات.»
|
| 376 |
+
|
| 377 |
+
━━━ الحالة ٤ — السياق لا يحتوي الإجابة + السؤال عن نص قانوني بعينه ━━━
|
| 378 |
+
الشرط: المستخدم يسأل عن مادة محددة أو حكم قانوني معين ولم تجده في السياق.
|
| 379 |
+
الفعل:
|
| 380 |
+
• قل: «عذراً، لم يرد ذكر لهذا الموضوع في النصوص القانونية المتاحة حالياً في قاعدة البيانات.»
|
| 381 |
+
• لا تجب من ذاكرتك لتجنب الخطأ في النصوص القانونية.
|
| 382 |
+
• يمكنك اقتراح موضوع مشابه إن وجد في السياق.
|
| 383 |
+
|
| 384 |
+
━━━ الحالة ٥ — محادثة ودية (تحية، شكر، وداع) ━━━
|
| 385 |
+
• رد بتحية لطيفة مقتضبة.
|
| 386 |
+
• أضف: «أنا مستشارك القانوني الذكي — اسألني عن أي موضوع في القوانين المصرية.»
|
| 387 |
+
|
| 388 |
+
━━━ الحالة ٦ — خارج نطاق القانون تماماً ━━━
|
| 389 |
+
• اعتذر بلطف: «تخصصي هو القوانين المصرية فقط.»
|
| 390 |
+
• وجّه المستخدم لطرح سؤال قانوني.
|
| 391 |
+
</decision_logic>
|
| 392 |
+
|
| 393 |
+
<quality_rules>
|
| 394 |
+
- **الدقة أولاً**: عند وجود نص في السياق، التزم به حرفياً ولا تحرّف المعنى.
|
| 395 |
+
- **المرونة عند الحاجة**: إذا لم يغطِّ السياق الموضوع بالكامل، قدّم إرشاداً عملياً مع التمييز الواضح بينه وبين النص القانوني.
|
| 396 |
+
- **لا تخترع مراجع**: لا تنسب أي معلومة إلى مادة أو قانون لم يرد في السياق.
|
| 397 |
+
- **الإيجاز مع الشمول**: أجب بقدر ما يحتاج السؤال — لا تختصر حتى يضيع المعنى ولا تطيل دون فائدة.
|
| 398 |
+
</quality_rules>
|
| 399 |
+
|
| 400 |
+
<formatting_rules>
|
| 401 |
+
- لا تكرر هذه التعليمات في ردك.
|
| 402 |
+
- ادخل في صلب الموضوع فوراً بدون عبارات مثل «بناءً على السياق المرفق».
|
| 403 |
+
- استخدم فقرات قصيرة مفصولة بسطر فارغ.
|
| 404 |
+
- لا تكرر نفس المعلومة أو نفس المادة.
|
| 405 |
+
- عند ذكر أكثر من مادة، رتّبها ترتيباً منطقياً (إما بالرقم أو حسب الأهمية).
|
| 406 |
+
- التزم باللغة العربية الفصحى المبسطة.
|
| 407 |
+
</formatting_rules>
|
| 408 |
+
"""
|
| 409 |
+
|
| 410 |
+
prompt = ChatPromptTemplate.from_messages(
|
| 411 |
+
[
|
| 412 |
+
("system", system_instructions),
|
| 413 |
+
("system", "السياق التشريعي المتاح (المصدر الأساسي):\n{context}"),
|
| 414 |
+
("human", "سؤال المستفيد:\n{input}"),
|
| 415 |
+
]
|
| 416 |
+
)
|
| 417 |
+
|
| 418 |
+
qa_chain = (
|
| 419 |
+
RunnableParallel({"context": compression_retriever, "input": RunnablePassthrough()})
|
| 420 |
+
.assign(answer=(prompt | llm | StrOutputParser()))
|
| 421 |
+
)
|
| 422 |
+
return qa_chain
|
| 423 |
+
|
app/schemas.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import Any, Dict, List, Optional
|
| 4 |
+
from pydantic import BaseModel, Field
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class AskRequest(BaseModel):
|
| 8 |
+
query: str = Field(..., min_length=1, description="User question in Arabic")
|
| 9 |
+
include_sources: bool = Field(default=True, description="Return retrieved source docs")
|
| 10 |
+
eastern_arabic_numerals: bool = Field(
|
| 11 |
+
default=False, description="Convert digits 0-9 to Eastern Arabic numerals"
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class SourceDoc(BaseModel):
|
| 16 |
+
article_id: Optional[str] = None
|
| 17 |
+
article_number: Optional[str] = None
|
| 18 |
+
law_name: Optional[str] = None
|
| 19 |
+
legal_nature: Optional[str] = None
|
| 20 |
+
keywords: Optional[str] = None
|
| 21 |
+
part: Optional[str] = None
|
| 22 |
+
chapter: Optional[str] = None
|
| 23 |
+
page_content: str
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class AskResponse(BaseModel):
|
| 27 |
+
answer: str
|
| 28 |
+
sources: List[SourceDoc] = Field(default_factory=list)
|
| 29 |
+
raw: Dict[str, Any] = Field(default_factory=dict)
|
app/utils.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ============================================
|
| 2 |
+
# file: app/utils.py
|
| 3 |
+
# ============================================
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
+
import re
|
| 7 |
+
from typing import List, Set
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
_ARABIC_STOPWORDS: Set[str] = {
|
| 11 |
+
"في", "من", "على", "إلى", "عن", "أن", "هذا", "هذه", "التي", "الذي",
|
| 12 |
+
"ما", "لا", "أو", "و", "كل", "ذلك", "بين", "كان", "قد", "هو", "هي",
|
| 13 |
+
"لم", "بل", "ثم", "إذا", "حتى", "لكن", "منه", "فيه", "عند", "له",
|
| 14 |
+
"بها", "لها", "منها", "فيها", "التى", "الذى", "ولا", "وفى", "كما",
|
| 15 |
+
"تلك", "هنا", "أي", "دون", "ليس", "إلا", "أما", "مع", "عليه",
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def arabic_tokenize(text: str) -> List[str]:
|
| 20 |
+
"""
|
| 21 |
+
Arabic tokenization used by BM25 + metadata index.
|
| 22 |
+
Removes diacritics, keeps Arabic letters/spaces, removes stopwords.
|
| 23 |
+
"""
|
| 24 |
+
text = re.sub(r"[\u064B-\u065F\u0670]", "", text) # strip tashkeel
|
| 25 |
+
text = re.sub(r"[^\u0600-\u06FF\s]", " ", text) # keep Arabic only
|
| 26 |
+
tokens = text.split()
|
| 27 |
+
return [t for t in tokens if t not in _ARABIC_STOPWORDS and len(t) > 1]
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def convert_to_eastern_arabic(text: str) -> str:
|
| 31 |
+
"""Converts 0123456789 to ٠١٢٣٤٥٦٧٨٩"""
|
| 32 |
+
western = "0123456789"
|
| 33 |
+
eastern = "٠١٢٣٤٥٦٧٨٩"
|
| 34 |
+
return text.translate(str.maketrans(western, eastern))
|
| 35 |
+
|
app_final.py
DELETED
|
@@ -1,625 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
import os
|
| 3 |
-
import sys
|
| 4 |
-
import json
|
| 5 |
-
from dotenv import load_dotenv
|
| 6 |
-
import streamlit as st
|
| 7 |
-
import logging
|
| 8 |
-
import warnings
|
| 9 |
-
|
| 10 |
-
# Suppress progress bars from transformers/tqdm
|
| 11 |
-
os.environ['TRANSFORMERS_NO_PROGRESS_BAR'] = '1'
|
| 12 |
-
warnings.filterwarnings('ignore')
|
| 13 |
-
|
| 14 |
-
# 1. Loaders & Splitters
|
| 15 |
-
from langchain_core.documents import Document
|
| 16 |
-
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 17 |
-
from langchain_core.retrievers import BaseRetriever
|
| 18 |
-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 19 |
-
from typing import List
|
| 20 |
-
from rank_bm25 import BM25Okapi
|
| 21 |
-
import numpy as np
|
| 22 |
-
|
| 23 |
-
# 2. Vector Store & Embeddings
|
| 24 |
-
from langchain_chroma import Chroma
|
| 25 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 26 |
-
|
| 27 |
-
# 3. Reranker Imports
|
| 28 |
-
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 29 |
-
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 30 |
-
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 31 |
-
|
| 32 |
-
# 4. LLM
|
| 33 |
-
from langchain_groq import ChatGroq
|
| 34 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 35 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 36 |
-
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
|
| 37 |
-
|
| 38 |
-
# Configure logging
|
| 39 |
-
logging.basicConfig(level=logging.INFO)
|
| 40 |
-
logger = logging.getLogger(__name__)
|
| 41 |
-
|
| 42 |
-
load_dotenv()
|
| 43 |
-
|
| 44 |
-
# ==========================================
|
| 45 |
-
# 🎨 UI SETUP (CSS FOR ARABIC & RTL)
|
| 46 |
-
# ==========================================
|
| 47 |
-
st.set_page_config(page_title="المساعد القانوني", page_icon="⚖️")
|
| 48 |
-
|
| 49 |
-
# This CSS block fixes the "001" number issue and right alignment
|
| 50 |
-
st.markdown("""
|
| 51 |
-
<style>
|
| 52 |
-
/* Force the main app container to be Right-to-Left */
|
| 53 |
-
.stApp {
|
| 54 |
-
direction: rtl;
|
| 55 |
-
text-align: right;
|
| 56 |
-
}
|
| 57 |
-
|
| 58 |
-
/* Fix input fields to type from right */
|
| 59 |
-
.stTextInput input {
|
| 60 |
-
direction: rtl;
|
| 61 |
-
text-align: right;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
/* Fix chat messages alignment */
|
| 65 |
-
.stChatMessage {
|
| 66 |
-
direction: rtl;
|
| 67 |
-
text-align: right;
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
/* Ensure proper paragraph spacing */
|
| 71 |
-
.stMarkdown p {
|
| 72 |
-
margin: 0.5em 0 !important;
|
| 73 |
-
line-height: 1.6;
|
| 74 |
-
word-spacing: 0.1em;
|
| 75 |
-
}
|
| 76 |
-
|
| 77 |
-
/* Ensure numbers display correctly in RTL */
|
| 78 |
-
p, div, span, label {
|
| 79 |
-
unicode-bidi: embed;
|
| 80 |
-
direction: inherit;
|
| 81 |
-
white-space: normal;
|
| 82 |
-
word-wrap: break-word;
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
/* Force all content to respect RTL */
|
| 86 |
-
* {
|
| 87 |
-
direction: rtl !important;
|
| 88 |
-
}
|
| 89 |
-
|
| 90 |
-
/* Preserve line breaks and spacing */
|
| 91 |
-
.stMarkdown pre {
|
| 92 |
-
direction: rtl;
|
| 93 |
-
text-align: right;
|
| 94 |
-
white-space: pre-wrap;
|
| 95 |
-
word-wrap: break-word;
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
/* Hide the "Deploy" button and standard menu for cleaner look */
|
| 99 |
-
#MainMenu {visibility: hidden;}
|
| 100 |
-
footer {visibility: hidden;}
|
| 101 |
-
|
| 102 |
-
</style>
|
| 103 |
-
""", unsafe_allow_html=True)
|
| 104 |
-
|
| 105 |
-
# Put this at the top of your code
|
| 106 |
-
def convert_to_eastern_arabic(text):
|
| 107 |
-
"""Converts 0123456789 to ٠١٢٣٤٥٦٧٨٩"""
|
| 108 |
-
if not isinstance(text, str):
|
| 109 |
-
return text
|
| 110 |
-
western_numerals = '0123456789'
|
| 111 |
-
eastern_numerals = '٠١٢٣٤٥٦٧٨٩'
|
| 112 |
-
translation_table = str.maketrans(western_numerals, eastern_numerals)
|
| 113 |
-
return text.translate(translation_table)
|
| 114 |
-
|
| 115 |
-
st.title("⚖️ المساعد القانوني الذكي (دستور مصر)")
|
| 116 |
-
|
| 117 |
-
# ==========================================
|
| 118 |
-
# 🚀 CACHED RESOURCE LOADING (THE FIX)
|
| 119 |
-
# ==========================================
|
| 120 |
-
# This decorator tells Streamlit: "Run this ONCE and save the result."
|
| 121 |
-
@st.cache_resource
|
| 122 |
-
def initialize_rag_pipeline():
|
| 123 |
-
print("🔄 Initializing system...")
|
| 124 |
-
print("📥 Loading data...")
|
| 125 |
-
|
| 126 |
-
# 1. Load JSON
|
| 127 |
-
json_path = "Egyptian_Constitution_legalnature_only.json"
|
| 128 |
-
if not os.path.exists(json_path):
|
| 129 |
-
raise FileNotFoundError(f"File not found: {json_path}")
|
| 130 |
-
|
| 131 |
-
with open(json_path, "r", encoding="utf-8") as f:
|
| 132 |
-
data = json.load(f)
|
| 133 |
-
|
| 134 |
-
# Create a mapping of article numbers for cross-reference lookup
|
| 135 |
-
article_map = {str(item['article_number']): item for item in data}
|
| 136 |
-
|
| 137 |
-
docs = []
|
| 138 |
-
for item in data:
|
| 139 |
-
# Build cross-reference section
|
| 140 |
-
cross_ref_text = ""
|
| 141 |
-
if item.get('cross_references') and len(item['cross_references']) > 0:
|
| 142 |
-
cross_ref_text = "\nالمواد ذات الصلة (المراجع المتقاطعة): " + ", ".join(
|
| 143 |
-
[f"المادة {ref}" for ref in item['cross_references']]
|
| 144 |
-
)
|
| 145 |
-
|
| 146 |
-
# Construct content
|
| 147 |
-
page_content = f"""
|
| 148 |
-
رقم المادة: {item['article_number']}
|
| 149 |
-
النص الأصلي: {item['original_text']}
|
| 150 |
-
الشرح المبسط: {item['simplified_summary']}{cross_ref_text}
|
| 151 |
-
"""
|
| 152 |
-
metadata = {
|
| 153 |
-
"article_id": item['article_id'],
|
| 154 |
-
"article_number": str(item['article_number']),
|
| 155 |
-
"legal_nature": item['legal_nature'],
|
| 156 |
-
"keywords": ", ".join(item['keywords']),
|
| 157 |
-
"part": item.get('part (Bab)', ''),
|
| 158 |
-
"chapter": item.get('chapter (Fasl)', ''),
|
| 159 |
-
"cross_references": ", ".join([str(ref) for ref in item.get('cross_references', [])]) # Convert list to string
|
| 160 |
-
}
|
| 161 |
-
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 162 |
-
|
| 163 |
-
print(f"✅ Loaded {len(docs)} constitutional articles")
|
| 164 |
-
|
| 165 |
-
# 2. Embeddings
|
| 166 |
-
print("Loading embeddings model...")
|
| 167 |
-
embeddings = HuggingFaceEmbeddings(
|
| 168 |
-
model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1"
|
| 169 |
-
)
|
| 170 |
-
print("✅ Embeddings model ready")
|
| 171 |
-
|
| 172 |
-
# 3. No splitting - keep articles as complete units
|
| 173 |
-
chunks = docs
|
| 174 |
-
|
| 175 |
-
# 4. Vector Store
|
| 176 |
-
print("Building vector database...")
|
| 177 |
-
vectorstore = Chroma.from_documents(
|
| 178 |
-
chunks,
|
| 179 |
-
embeddings,
|
| 180 |
-
persist_directory="chroma_db"
|
| 181 |
-
)
|
| 182 |
-
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 15})
|
| 183 |
-
print("✅ Vector database ready")
|
| 184 |
-
|
| 185 |
-
# 5. Create BM25 Keyword Retriever
|
| 186 |
-
class BM25Retriever(BaseRetriever):
|
| 187 |
-
"""BM25-based keyword retriever for constitutional articles"""
|
| 188 |
-
corpus_docs: List[Document]
|
| 189 |
-
bm25: BM25Okapi = None
|
| 190 |
-
k: int = 15
|
| 191 |
-
|
| 192 |
-
class Config:
|
| 193 |
-
arbitrary_types_allowed = True
|
| 194 |
-
|
| 195 |
-
def __init__(self, **data):
|
| 196 |
-
super().__init__(**data)
|
| 197 |
-
# Tokenize corpus for BM25
|
| 198 |
-
tokenized_corpus = [doc.page_content.split() for doc in self.corpus_docs]
|
| 199 |
-
self.bm25 = BM25Okapi(tokenized_corpus)
|
| 200 |
-
|
| 201 |
-
def _get_relevant_documents(
|
| 202 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 203 |
-
) -> List[Document]:
|
| 204 |
-
# Tokenize query
|
| 205 |
-
tokenized_query = query.split()
|
| 206 |
-
# Get BM25 scores
|
| 207 |
-
scores = self.bm25.get_scores(tokenized_query)
|
| 208 |
-
# Get top k indices
|
| 209 |
-
top_indices = np.argsort(scores)[::-1][:self.k]
|
| 210 |
-
# Return documents
|
| 211 |
-
return [self.corpus_docs[i] for i in top_indices if scores[i] > 0]
|
| 212 |
-
|
| 213 |
-
async def _aget_relevant_documents(
|
| 214 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 215 |
-
) -> List[Document]:
|
| 216 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 217 |
-
|
| 218 |
-
bm25_retriever = BM25Retriever(corpus_docs=docs, k=15)
|
| 219 |
-
print("✅ BM25 keyword retriever ready")
|
| 220 |
-
|
| 221 |
-
# 6. Create Metadata Filter Retriever
|
| 222 |
-
class MetadataFilterRetriever(BaseRetriever):
|
| 223 |
-
"""Metadata-based filtering retriever"""
|
| 224 |
-
corpus_docs: List[Document]
|
| 225 |
-
k: int = 15
|
| 226 |
-
|
| 227 |
-
class Config:
|
| 228 |
-
arbitrary_types_allowed = True
|
| 229 |
-
|
| 230 |
-
def _get_relevant_documents(
|
| 231 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 232 |
-
) -> List[Document]:
|
| 233 |
-
query_lower = query.lower()
|
| 234 |
-
scored_docs = []
|
| 235 |
-
|
| 236 |
-
for doc in self.corpus_docs:
|
| 237 |
-
score = 0
|
| 238 |
-
# Match keywords
|
| 239 |
-
keywords = doc.metadata.get('keywords', '').lower()
|
| 240 |
-
if any(word in keywords for word in query_lower.split()):
|
| 241 |
-
score += 3
|
| 242 |
-
|
| 243 |
-
# Match legal nature
|
| 244 |
-
legal_nature = doc.metadata.get('legal_nature', '').lower()
|
| 245 |
-
if any(word in legal_nature for word in query_lower.split()):
|
| 246 |
-
score += 2
|
| 247 |
-
|
| 248 |
-
# Match part/chapter
|
| 249 |
-
part = doc.metadata.get('part', '').lower()
|
| 250 |
-
chapter = doc.metadata.get('chapter', '').lower()
|
| 251 |
-
if any(word in part or word in chapter for word in query_lower.split()):
|
| 252 |
-
score += 1
|
| 253 |
-
|
| 254 |
-
# Match in content
|
| 255 |
-
if any(word in doc.page_content.lower() for word in query_lower.split()):
|
| 256 |
-
score += 1
|
| 257 |
-
|
| 258 |
-
if score > 0:
|
| 259 |
-
scored_docs.append((doc, score))
|
| 260 |
-
|
| 261 |
-
# Sort by score and return top k
|
| 262 |
-
scored_docs.sort(key=lambda x: x[1], reverse=True)
|
| 263 |
-
return [doc for doc, _ in scored_docs[:self.k]]
|
| 264 |
-
|
| 265 |
-
async def _aget_relevant_documents(
|
| 266 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 267 |
-
) -> List[Document]:
|
| 268 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 269 |
-
|
| 270 |
-
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs, k=15)
|
| 271 |
-
print("✅ Metadata filter retriever ready")
|
| 272 |
-
|
| 273 |
-
# 7. Create Hybrid RRF Retriever
|
| 274 |
-
class HybridRRFRetriever(BaseRetriever):
|
| 275 |
-
"""Combines semantic, BM25, and metadata retrievers using Reciprocal Rank Fusion"""
|
| 276 |
-
semantic_retriever: BaseRetriever
|
| 277 |
-
bm25_retriever: BM25Retriever
|
| 278 |
-
metadata_retriever: MetadataFilterRetriever
|
| 279 |
-
beta_semantic: float = 0.6 # Weight for semantic search
|
| 280 |
-
beta_keyword: float = 0.2 # Weight for BM25 keyword search
|
| 281 |
-
beta_metadata: float = 0.2 # Weight for metadata filtering
|
| 282 |
-
k: int = 60 # RRF constant (typically 60)
|
| 283 |
-
top_k: int = 15
|
| 284 |
-
|
| 285 |
-
class Config:
|
| 286 |
-
arbitrary_types_allowed = True
|
| 287 |
-
|
| 288 |
-
def _get_relevant_documents(
|
| 289 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 290 |
-
) -> List[Document]:
|
| 291 |
-
# Get results from all three retrievers
|
| 292 |
-
semantic_docs = self.semantic_retriever.invoke(query)
|
| 293 |
-
bm25_docs = self.bm25_retriever.invoke(query)
|
| 294 |
-
metadata_docs = self.metadata_retriever.invoke(query)
|
| 295 |
-
|
| 296 |
-
# Apply Reciprocal Rank Fusion
|
| 297 |
-
rrf_scores = {}
|
| 298 |
-
|
| 299 |
-
# Process semantic results
|
| 300 |
-
for rank, doc in enumerate(semantic_docs, start=1):
|
| 301 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 302 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_semantic / (self.k + rank)
|
| 303 |
-
|
| 304 |
-
# Process BM25 results
|
| 305 |
-
for rank, doc in enumerate(bm25_docs, start=1):
|
| 306 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 307 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_keyword / (self.k + rank)
|
| 308 |
-
|
| 309 |
-
# Process metadata results
|
| 310 |
-
for rank, doc in enumerate(metadata_docs, start=1):
|
| 311 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 312 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_metadata / (self.k + rank)
|
| 313 |
-
|
| 314 |
-
# Create document lookup
|
| 315 |
-
all_docs = {}
|
| 316 |
-
for doc in semantic_docs + bm25_docs + metadata_docs:
|
| 317 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 318 |
-
if doc_id not in all_docs:
|
| 319 |
-
all_docs[doc_id] = doc
|
| 320 |
-
|
| 321 |
-
# Sort by RRF score
|
| 322 |
-
sorted_doc_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 323 |
-
|
| 324 |
-
# Return top k documents
|
| 325 |
-
result_docs = []
|
| 326 |
-
for doc_id, score in sorted_doc_ids[:self.top_k]:
|
| 327 |
-
if doc_id in all_docs:
|
| 328 |
-
result_docs.append(all_docs[doc_id])
|
| 329 |
-
|
| 330 |
-
return result_docs
|
| 331 |
-
|
| 332 |
-
async def _aget_relevant_documents(
|
| 333 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 334 |
-
) -> List[Document]:
|
| 335 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 336 |
-
|
| 337 |
-
# Create hybrid retriever with tuned beta weights
|
| 338 |
-
hybrid_retriever = HybridRRFRetriever(
|
| 339 |
-
semantic_retriever=base_retriever,
|
| 340 |
-
bm25_retriever=bm25_retriever,
|
| 341 |
-
metadata_retriever=metadata_retriever,
|
| 342 |
-
beta_semantic=0.5, # Semantic search gets highest weight (most reliable)
|
| 343 |
-
beta_keyword=0.3, # BM25 keyword search (good for exact term matches)
|
| 344 |
-
beta_metadata=0.2, # Metadata filtering (supporting role)
|
| 345 |
-
k=60,
|
| 346 |
-
top_k=20
|
| 347 |
-
)
|
| 348 |
-
print("✅ Hybrid RRF retriever ready with β weights: semantic=0.5, keyword=0.3, metadata=0.2")
|
| 349 |
-
|
| 350 |
-
# 8. Create Cross-Reference Enhanced Retriever
|
| 351 |
-
class CrossReferenceRetriever(BaseRetriever):
|
| 352 |
-
"""Enhances retrieval by automatically fetching cross-referenced articles"""
|
| 353 |
-
base_retriever: BaseRetriever
|
| 354 |
-
article_map: dict
|
| 355 |
-
|
| 356 |
-
class Config:
|
| 357 |
-
arbitrary_types_allowed = True
|
| 358 |
-
|
| 359 |
-
def _get_relevant_documents(
|
| 360 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 361 |
-
) -> List[Document]:
|
| 362 |
-
# Get initial results
|
| 363 |
-
initial_docs = self.base_retriever.invoke(query)
|
| 364 |
-
|
| 365 |
-
# Collect all related article numbers
|
| 366 |
-
all_article_numbers = set()
|
| 367 |
-
for doc in initial_docs:
|
| 368 |
-
if 'article_number' in doc.metadata:
|
| 369 |
-
all_article_numbers.add(doc.metadata['article_number'])
|
| 370 |
-
# Parse cross_references (now stored as comma-separated string)
|
| 371 |
-
cross_refs_str = doc.metadata.get('cross_references', '')
|
| 372 |
-
if cross_refs_str:
|
| 373 |
-
cross_refs = [ref.strip() for ref in cross_refs_str.split(',')]
|
| 374 |
-
for ref in cross_refs:
|
| 375 |
-
if ref: # Skip empty strings
|
| 376 |
-
all_article_numbers.add(str(ref))
|
| 377 |
-
|
| 378 |
-
# Build enhanced document list
|
| 379 |
-
enhanced_docs = []
|
| 380 |
-
seen_numbers = set()
|
| 381 |
-
|
| 382 |
-
# Add initially retrieved documents
|
| 383 |
-
for doc in initial_docs:
|
| 384 |
-
enhanced_docs.append(doc)
|
| 385 |
-
seen_numbers.add(doc.metadata.get('article_number'))
|
| 386 |
-
|
| 387 |
-
# Add cross-referenced articles not yet retrieved
|
| 388 |
-
for article_num in all_article_numbers:
|
| 389 |
-
if article_num not in seen_numbers and article_num in self.article_map:
|
| 390 |
-
article_data = self.article_map[article_num]
|
| 391 |
-
cross_ref_text = ""
|
| 392 |
-
if article_data.get('cross_references'):
|
| 393 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join(
|
| 394 |
-
[f"المادة {ref}" for ref in article_data['cross_references']]
|
| 395 |
-
)
|
| 396 |
-
|
| 397 |
-
page_content = f"""
|
| 398 |
-
رقم المادة: {article_data['article_number']}
|
| 399 |
-
النص الأصلي: {article_data['original_text']}
|
| 400 |
-
الشرح المبسط: {article_data['simplified_summary']}{cross_ref_text}
|
| 401 |
-
"""
|
| 402 |
-
|
| 403 |
-
enhanced_doc = Document(
|
| 404 |
-
page_content=page_content,
|
| 405 |
-
metadata={
|
| 406 |
-
"article_id": article_data['article_id'],
|
| 407 |
-
"article_number": str(article_data['article_number']),
|
| 408 |
-
"legal_nature": article_data['legal_nature'],
|
| 409 |
-
"keywords": ", ".join(article_data['keywords']),
|
| 410 |
-
"cross_references": ", ".join([str(ref) for ref in article_data.get('cross_references', [])])
|
| 411 |
-
}
|
| 412 |
-
)
|
| 413 |
-
enhanced_docs.append(enhanced_doc)
|
| 414 |
-
seen_numbers.add(article_num)
|
| 415 |
-
|
| 416 |
-
return enhanced_docs
|
| 417 |
-
|
| 418 |
-
async def _aget_relevant_documents(
|
| 419 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 420 |
-
) -> List[Document]:
|
| 421 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 422 |
-
|
| 423 |
-
cross_ref_retriever = CrossReferenceRetriever(
|
| 424 |
-
base_retriever=hybrid_retriever,
|
| 425 |
-
article_map=article_map
|
| 426 |
-
)
|
| 427 |
-
print("✅ Cross-reference retriever ready (using hybrid RRF base)")
|
| 428 |
-
|
| 429 |
-
# 9. Reranker
|
| 430 |
-
print("Loading reranker model...")
|
| 431 |
-
local_model_path = r"D:\FOE\Senior 2\Graduation Project\Chatbot_me\reranker"
|
| 432 |
-
|
| 433 |
-
if not os.path.exists(local_model_path):
|
| 434 |
-
raise FileNotFoundError(f"Reranker path not found: {local_model_path}")
|
| 435 |
-
|
| 436 |
-
model = HuggingFaceCrossEncoder(model_name=local_model_path)
|
| 437 |
-
compressor = CrossEncoderReranker(model=model, top_n=5)
|
| 438 |
-
|
| 439 |
-
compression_retriever = ContextualCompressionRetriever(
|
| 440 |
-
base_compressor=compressor,
|
| 441 |
-
base_retriever=cross_ref_retriever
|
| 442 |
-
)
|
| 443 |
-
print("✅ Reranker model ready")
|
| 444 |
-
|
| 445 |
-
# 7. LLM - Balanced for consistency with slight creativity
|
| 446 |
-
# 7. LLM Configuration
|
| 447 |
-
llm = ChatGroq(
|
| 448 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 449 |
-
model_name="llama-3.1-8b-instant",
|
| 450 |
-
temperature=0.3, # Slightly increased to allow helpful general advice
|
| 451 |
-
model_kwargs={"top_p": 0.9}
|
| 452 |
-
)
|
| 453 |
-
|
| 454 |
-
# ==================================================
|
| 455 |
-
# 🛠️ THE FIX: SEPARATE SYSTEM INSTRUCTIONS FROM USER INPUT
|
| 456 |
-
# ==================================================
|
| 457 |
-
|
| 458 |
-
# ==================================================
|
| 459 |
-
# 🧠 PROMPT ENGINEERING: DECISION TREE LOGIC
|
| 460 |
-
# ==================================================
|
| 461 |
-
|
| 462 |
-
system_instructions = """
|
| 463 |
-
<role>
|
| 464 |
-
أنت "المساعد القانوني الذكي"، خبير متخصص في الدستور المصري والقوانين الإجرائية.
|
| 465 |
-
مهمتك: تقديم إجابات دقيقة بناءً على "السياق التشريعي" المرفق أولاً، أو تقديم نصائح إجرائية عامة عند الضرورة.
|
| 466 |
-
</role>
|
| 467 |
-
|
| 468 |
-
<decision_logic>
|
| 469 |
-
عليك تحليل "سؤال المستخدم" و"السياق التشريعي" وتصنيف الحالة واختيار الرد المناسب بناءً على القواعد التالية بدقة:
|
| 470 |
-
|
| 471 |
-
🔴 الحالة الأولى: (الإجابة موجودة في السياق التشريعي)
|
| 472 |
-
الشرط: إذا وجدت معلومات داخل "السياق التشريعي المتاح" تجيب على السؤال.
|
| 473 |
-
الفعل:
|
| 474 |
-
1. استخرج الإجابة من السياق فقط.
|
| 475 |
-
2. ابدأ الإجابة مباشرة دون مقدمات.
|
| 476 |
-
3. يجب توثيق الإجابة برقم المادة (مثال: "نصت المادة (50) على...").
|
| 477 |
-
4. توقف هنا. لا تضف أي معلومات خارجية.
|
| 478 |
-
|
| 479 |
-
🟡 الحالة الثانية: (السياق فارغ/غير مفيد + السؤال إجرائي/عملي)
|
| 480 |
-
الشرط: إذا لم تجد الإجابة في السياق، وكان السؤال عن إجراءات عملية (مثل: حادث، سرقة، طلاق، تحرير محضر، تعامل مع الشرطة).
|
| 481 |
-
الفعل:
|
| 482 |
-
1. تجاهل السياق الفارغ.
|
| 483 |
-
2. استخدم معرفتك العامة بالقانون المصري.
|
| 484 |
-
3. ابدأ وجوباً بعبارة: "بناءً على الإجراءات القانونية العامة في مصر (وليس ن��اً دستورياً محدداً):"
|
| 485 |
-
4. قدم الخطوات في نقاط مرقمة واضحة ومختصرة (1، 2، 3).
|
| 486 |
-
5. تحذير: لا تذكر أرقام مواد قانونية (لا تخترع أرقام مواد).
|
| 487 |
-
|
| 488 |
-
🔵 الحالة الثالثة: (السياق فارغ + السؤال عن نص دستوري محدد)
|
| 489 |
-
الشرط: إذا سأل عن (مجلس الشعب، الشورى، مادة محددة) ولم تجدها في السياق.
|
| 490 |
-
الفعل:
|
| 491 |
-
1. قل بوضوح: "عذراً، لم يرد ذكر لهذا الموضوع في المواد الدستورية التي تم استرجاعها في السياق الحالي."
|
| 492 |
-
2. لا تحاول الإجابة من ذاكرتك لكي لا تخطئ في النصوص الدستورية الحساسة.
|
| 493 |
-
|
| 494 |
-
🟢 الحالة الرابعة: (محادثة ودية)
|
| 495 |
-
الشرط: تحية، شكر، أو "كيف حالك".
|
| 496 |
-
الفعل: رد بتحية مهذبة جداً ومقتضبة، ثم قل: "أنا جاهز للإجابة على استفساراتك القانونية."
|
| 497 |
-
|
| 498 |
-
⚫ الحالة الخامسة: (خارج النطاق تماماً)
|
| 499 |
-
الشرط: طبخ، رياضة، برمجة، أو أي موضوع غير قانوني.
|
| 500 |
-
الفعل: اعتذر بلطف ووجه المستخدم للسؤال في القانون.
|
| 501 |
-
</decision_logic>
|
| 502 |
-
|
| 503 |
-
<formatting_rules>
|
| 504 |
-
- لا تكرر هذه التعليمات في ردك.
|
| 505 |
-
- استخدم فقرات قصيرة واترك سطراً فارغاً بينها.
|
| 506 |
-
- لا تستخدم عبارات مثل "بناء على السياق المرفق" في بداية الجملة، بل ادخل في صلب الموضوع فوراً.
|
| 507 |
-
- التزم باللغة العربية الفصحى المبسطة والرصينة.
|
| 508 |
-
</formatting_rules>
|
| 509 |
-
"""
|
| 510 |
-
|
| 511 |
-
# We use .from_messages to strictly separate instructions from data
|
| 512 |
-
prompt = ChatPromptTemplate.from_messages([
|
| 513 |
-
("system", system_instructions),
|
| 514 |
-
("system", "السياق التشريعي المتاح (المصدر الأساسي):\n{context}"),
|
| 515 |
-
("human", "سؤال المستفيد:\n{input}")
|
| 516 |
-
])
|
| 517 |
-
|
| 518 |
-
# 9. Build Chain with RunnableParallel (returns both context and answer)
|
| 519 |
-
qa_chain = (
|
| 520 |
-
RunnableParallel({
|
| 521 |
-
"context": compression_retriever,
|
| 522 |
-
"input": RunnablePassthrough()
|
| 523 |
-
})
|
| 524 |
-
.assign(answer=(
|
| 525 |
-
prompt
|
| 526 |
-
| llm
|
| 527 |
-
| StrOutputParser()
|
| 528 |
-
))
|
| 529 |
-
)
|
| 530 |
-
|
| 531 |
-
print("✅ System ready to use!")
|
| 532 |
-
return qa_chain
|
| 533 |
-
|
| 534 |
-
# ==========================================
|
| 535 |
-
# ⚡ MAIN EXECUTION
|
| 536 |
-
# ==========================================
|
| 537 |
-
|
| 538 |
-
try:
|
| 539 |
-
# Only need the chain now - it handles all retrieval internally
|
| 540 |
-
qa_chain = initialize_rag_pipeline()
|
| 541 |
-
|
| 542 |
-
except Exception as e:
|
| 543 |
-
st.error(f"Critical Error loading application: {e}")
|
| 544 |
-
st.stop()
|
| 545 |
-
|
| 546 |
-
# ==========================================
|
| 547 |
-
# 💬 CHAT LOOP
|
| 548 |
-
# ==========================================
|
| 549 |
-
if "messages" not in st.session_state:
|
| 550 |
-
st.session_state.messages = []
|
| 551 |
-
|
| 552 |
-
# Display Chat History (with Eastern Arabic numerals)
|
| 553 |
-
for message in st.session_state.messages:
|
| 554 |
-
with st.chat_message(message["role"]):
|
| 555 |
-
# Convert to Eastern Arabic when displaying from history
|
| 556 |
-
st.markdown(convert_to_eastern_arabic(message["content"]))
|
| 557 |
-
|
| 558 |
-
# Handle New User Input
|
| 559 |
-
if prompt_input := st.chat_input("اكتب سؤالك القانوني هنا..."):
|
| 560 |
-
# Show user message
|
| 561 |
-
st.session_state.messages.append({"role": "user", "content": prompt_input})
|
| 562 |
-
with st.chat_message("user"):
|
| 563 |
-
st.markdown(prompt_input)
|
| 564 |
-
|
| 565 |
-
# Generate Response
|
| 566 |
-
with st.chat_message("assistant"):
|
| 567 |
-
with st.spinner("جاري التحليل القانوني..."):
|
| 568 |
-
try:
|
| 569 |
-
# Invoke chain ONCE - returns Dict with 'context', 'input', and 'answer'
|
| 570 |
-
result = qa_chain.invoke(prompt_input)
|
| 571 |
-
|
| 572 |
-
# Extract answer and context from result
|
| 573 |
-
response_text = result["answer"]
|
| 574 |
-
source_docs = result["context"] # Context is already in the result!
|
| 575 |
-
|
| 576 |
-
# Display Answer
|
| 577 |
-
response_text_arabic = convert_to_eastern_arabic(response_text)
|
| 578 |
-
st.markdown(response_text_arabic)
|
| 579 |
-
|
| 580 |
-
# Display Sources
|
| 581 |
-
if source_docs and len(source_docs) > 0:
|
| 582 |
-
print(f"✅ Found {len(source_docs)} documents")
|
| 583 |
-
# Deduplicate documents by article_number
|
| 584 |
-
seen_articles = set()
|
| 585 |
-
unique_docs = []
|
| 586 |
-
|
| 587 |
-
for doc in source_docs:
|
| 588 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 589 |
-
if article_num and article_num not in seen_articles:
|
| 590 |
-
seen_articles.add(article_num)
|
| 591 |
-
unique_docs.append(doc)
|
| 592 |
-
|
| 593 |
-
st.markdown("---") # Separator before sources
|
| 594 |
-
|
| 595 |
-
if unique_docs:
|
| 596 |
-
with st.expander(f"📚 المصادر المستخدمة ({len(unique_docs)} مادة)"):
|
| 597 |
-
st.markdown("### المواد الدستورية المستخدمة في التحليل:")
|
| 598 |
-
st.markdown("---")
|
| 599 |
-
|
| 600 |
-
for idx, doc in enumerate(unique_docs, 1):
|
| 601 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 602 |
-
legal_nature = doc.metadata.get('legal_nature', '')
|
| 603 |
-
|
| 604 |
-
if article_num:
|
| 605 |
-
st.markdown(f"**المادة رقم {convert_to_eastern_arabic(article_num)}**")
|
| 606 |
-
if legal_nature:
|
| 607 |
-
st.markdown(f"*الطبيعة القانونية: {legal_nature}*")
|
| 608 |
-
|
| 609 |
-
# Display article content
|
| 610 |
-
content_lines = doc.page_content.strip().split('\n')
|
| 611 |
-
for line in content_lines:
|
| 612 |
-
line = line.strip()
|
| 613 |
-
if line:
|
| 614 |
-
st.markdown(convert_to_eastern_arabic(line))
|
| 615 |
-
|
| 616 |
-
st.markdown("---")
|
| 617 |
-
else:
|
| 618 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 619 |
-
else:
|
| 620 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 621 |
-
|
| 622 |
-
# Persist the raw answer to avoid double conversion glitches on rerun
|
| 623 |
-
st.session_state.messages.append({"role": "assistant", "content": response_text})
|
| 624 |
-
except Exception as e:
|
| 625 |
-
st.error(f"حدث خطأ: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app_final_pheonix.py
DELETED
|
@@ -1,838 +0,0 @@
|
|
| 1 |
-
# === Phoenix Observability Setup ===
|
| 2 |
-
import os
|
| 3 |
-
from datetime import datetime
|
| 4 |
-
|
| 5 |
-
try:
|
| 6 |
-
# OpenTelemetry SDK + OTLP exporter (Phoenix consumes OTLP)
|
| 7 |
-
from opentelemetry import trace
|
| 8 |
-
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
| 9 |
-
from opentelemetry.sdk.trace import TracerProvider
|
| 10 |
-
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
| 11 |
-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
| 12 |
-
PHOENIX_AVAILABLE = True
|
| 13 |
-
except Exception:
|
| 14 |
-
PHOENIX_AVAILABLE = False
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
def setup_phoenix_tracing():
|
| 18 |
-
"""Configure OTLP tracing for Phoenix. Uses PHOENIX_OTLP_ENDPOINT env if set."""
|
| 19 |
-
if not PHOENIX_AVAILABLE:
|
| 20 |
-
return None
|
| 21 |
-
|
| 22 |
-
service_name = os.getenv("PHOENIX_SERVICE_NAME", "constitutional-assistant")
|
| 23 |
-
otlp_endpoint = os.getenv("PHOENIX_OTLP_ENDPOINT", "http://localhost:6006/v1/traces")
|
| 24 |
-
|
| 25 |
-
resource = Resource(attributes={SERVICE_NAME: service_name})
|
| 26 |
-
provider = TracerProvider(resource=resource)
|
| 27 |
-
exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
|
| 28 |
-
span_processor = BatchSpanProcessor(exporter)
|
| 29 |
-
provider.add_span_processor(span_processor)
|
| 30 |
-
trace.set_tracer_provider(provider)
|
| 31 |
-
return trace.get_tracer(service_name)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
# Create a module-level tracer
|
| 35 |
-
_phoenix_tracer = setup_phoenix_tracing()
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
class PhoenixSpan:
|
| 39 |
-
"""Context manager helper to create spans with proper parent-child hierarchy."""
|
| 40 |
-
def __init__(self, name: str, attributes: dict | None = None, kind: str = "INTERNAL"):
|
| 41 |
-
self.name = name
|
| 42 |
-
self.attributes = attributes or {}
|
| 43 |
-
self.kind = kind
|
| 44 |
-
self._span_context = None
|
| 45 |
-
self._span = None
|
| 46 |
-
self._start_time = None
|
| 47 |
-
|
| 48 |
-
def __enter__(self):
|
| 49 |
-
if _phoenix_tracer:
|
| 50 |
-
from opentelemetry.trace import SpanKind
|
| 51 |
-
import time
|
| 52 |
-
self._start_time = time.time()
|
| 53 |
-
|
| 54 |
-
# Map string kind to SpanKind enum
|
| 55 |
-
kind_map = {
|
| 56 |
-
"CLIENT": SpanKind.CLIENT,
|
| 57 |
-
"SERVER": SpanKind.SERVER,
|
| 58 |
-
"INTERNAL": SpanKind.INTERNAL,
|
| 59 |
-
}
|
| 60 |
-
span_kind = kind_map.get(self.kind, SpanKind.INTERNAL)
|
| 61 |
-
|
| 62 |
-
# Use start_as_current_span to establish parent-child relationships
|
| 63 |
-
self._span_context = _phoenix_tracer.start_as_current_span(
|
| 64 |
-
self.name,
|
| 65 |
-
kind=span_kind
|
| 66 |
-
)
|
| 67 |
-
self._span = self._span_context.__enter__()
|
| 68 |
-
for k, v in self.attributes.items():
|
| 69 |
-
try:
|
| 70 |
-
self._span.set_attribute(k, v)
|
| 71 |
-
except Exception:
|
| 72 |
-
pass
|
| 73 |
-
return self
|
| 74 |
-
|
| 75 |
-
def set_attr(self, key: str, value):
|
| 76 |
-
if self._span:
|
| 77 |
-
try:
|
| 78 |
-
self._span.set_attribute(key, value)
|
| 79 |
-
except Exception:
|
| 80 |
-
pass
|
| 81 |
-
|
| 82 |
-
def __exit__(self, exc_type, exc, tb):
|
| 83 |
-
if self._span_context:
|
| 84 |
-
try:
|
| 85 |
-
if exc_type:
|
| 86 |
-
self._span.record_exception(exc)
|
| 87 |
-
from opentelemetry.trace import Status, StatusCode
|
| 88 |
-
self._span.set_status(Status(StatusCode.ERROR, str(exc)))
|
| 89 |
-
else:
|
| 90 |
-
# Add duration as attribute
|
| 91 |
-
if self._start_time:
|
| 92 |
-
import time
|
| 93 |
-
duration = time.time() - self._start_time
|
| 94 |
-
self._span.set_attribute("duration_ms", round(duration * 1000, 2))
|
| 95 |
-
from opentelemetry.trace import Status, StatusCode
|
| 96 |
-
self._span.set_status(Status(StatusCode.OK))
|
| 97 |
-
self._span_context.__exit__(exc_type, exc, tb)
|
| 98 |
-
except Exception:
|
| 99 |
-
pass
|
| 100 |
-
|
| 101 |
-
# -*- coding: utf-8 -*-
|
| 102 |
-
import os
|
| 103 |
-
import sys
|
| 104 |
-
import json
|
| 105 |
-
from dotenv import load_dotenv
|
| 106 |
-
import streamlit as st
|
| 107 |
-
import logging
|
| 108 |
-
import warnings
|
| 109 |
-
|
| 110 |
-
# Suppress progress bars from transformers/tqdm
|
| 111 |
-
os.environ['TRANSFORMERS_NO_PROGRESS_BAR'] = '1'
|
| 112 |
-
warnings.filterwarnings('ignore')
|
| 113 |
-
|
| 114 |
-
# 1. Loaders & Splitters
|
| 115 |
-
from langchain_core.documents import Document
|
| 116 |
-
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 117 |
-
from langchain_core.retrievers import BaseRetriever
|
| 118 |
-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 119 |
-
from typing import List
|
| 120 |
-
from rank_bm25 import BM25Okapi
|
| 121 |
-
import numpy as np
|
| 122 |
-
|
| 123 |
-
# 2. Vector Store & Embeddings
|
| 124 |
-
from langchain_chroma import Chroma
|
| 125 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 126 |
-
|
| 127 |
-
# 3. Reranker Imports
|
| 128 |
-
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 129 |
-
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 130 |
-
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 131 |
-
|
| 132 |
-
# 4. LLM
|
| 133 |
-
from langchain_groq import ChatGroq
|
| 134 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 135 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 136 |
-
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
|
| 137 |
-
|
| 138 |
-
# Configure logging
|
| 139 |
-
logging.basicConfig(level=logging.INFO)
|
| 140 |
-
logger = logging.getLogger(__name__)
|
| 141 |
-
|
| 142 |
-
load_dotenv()
|
| 143 |
-
|
| 144 |
-
# ==========================================
|
| 145 |
-
# 🎨 UI SETUP (CSS FOR ARABIC & RTL)
|
| 146 |
-
# ==========================================
|
| 147 |
-
st.set_page_config(page_title="المساعد القانوني", page_icon="⚖️")
|
| 148 |
-
|
| 149 |
-
# This CSS block fixes the "001" number issue and right alignment
|
| 150 |
-
st.markdown("""
|
| 151 |
-
<style>
|
| 152 |
-
/* Force the main app container to be Right-to-Left */
|
| 153 |
-
.stApp {
|
| 154 |
-
direction: rtl;
|
| 155 |
-
text-align: right;
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
/* Fix input fields to type from right */
|
| 159 |
-
.stTextInput input {
|
| 160 |
-
direction: rtl;
|
| 161 |
-
text-align: right;
|
| 162 |
-
}
|
| 163 |
-
|
| 164 |
-
/* Fix chat messages alignment */
|
| 165 |
-
.stChatMessage {
|
| 166 |
-
direction: rtl;
|
| 167 |
-
text-align: right;
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
/* Ensure proper paragraph spacing */
|
| 171 |
-
.stMarkdown p {
|
| 172 |
-
margin: 0.5em 0 !important;
|
| 173 |
-
line-height: 1.6;
|
| 174 |
-
word-spacing: 0.1em;
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
/* Ensure numbers display correctly in RTL */
|
| 178 |
-
p, div, span, label {
|
| 179 |
-
unicode-bidi: embed;
|
| 180 |
-
direction: inherit;
|
| 181 |
-
white-space: normal;
|
| 182 |
-
word-wrap: break-word;
|
| 183 |
-
}
|
| 184 |
-
|
| 185 |
-
/* Force all content to respect RTL */
|
| 186 |
-
* {
|
| 187 |
-
direction: rtl !important;
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
/* Preserve line breaks and spacing */
|
| 191 |
-
.stMarkdown pre {
|
| 192 |
-
direction: rtl;
|
| 193 |
-
text-align: right;
|
| 194 |
-
white-space: pre-wrap;
|
| 195 |
-
word-wrap: break-word;
|
| 196 |
-
}
|
| 197 |
-
|
| 198 |
-
/* Hide the "Deploy" button and standard menu for cleaner look */
|
| 199 |
-
#MainMenu {visibility: hidden;}
|
| 200 |
-
footer {visibility: hidden;}
|
| 201 |
-
|
| 202 |
-
</style>
|
| 203 |
-
""", unsafe_allow_html=True)
|
| 204 |
-
|
| 205 |
-
# Put this at the top of your code
|
| 206 |
-
def convert_to_eastern_arabic(text):
|
| 207 |
-
"""Converts 0123456789 to ٠١٢٣٤٥٦٧٨٩"""
|
| 208 |
-
if not isinstance(text, str):
|
| 209 |
-
return text
|
| 210 |
-
western_numerals = '0123456789'
|
| 211 |
-
eastern_numerals = '٠١٢٣٤٥٦٧٨٩'
|
| 212 |
-
translation_table = str.maketrans(western_numerals, eastern_numerals)
|
| 213 |
-
return text.translate(translation_table)
|
| 214 |
-
|
| 215 |
-
st.title("⚖️ المساعد القانوني الذكي (دستور مصر)")
|
| 216 |
-
|
| 217 |
-
# ==========================================
|
| 218 |
-
# 🚀 CACHED RESOURCE LOADING (THE FIX)
|
| 219 |
-
# ==========================================
|
| 220 |
-
# This decorator tells Streamlit: "Run this ONCE and save the result."
|
| 221 |
-
@st.cache_resource
|
| 222 |
-
def initialize_rag_pipeline():
|
| 223 |
-
print("🔄 Initializing system...")
|
| 224 |
-
print("📥 Loading data...")
|
| 225 |
-
|
| 226 |
-
# 1. Load JSON
|
| 227 |
-
json_path = "Egyptian_Constitution_legalnature_only.json"
|
| 228 |
-
if not os.path.exists(json_path):
|
| 229 |
-
raise FileNotFoundError(f"File not found: {json_path}")
|
| 230 |
-
|
| 231 |
-
with open(json_path, "r", encoding="utf-8") as f:
|
| 232 |
-
data = json.load(f)
|
| 233 |
-
|
| 234 |
-
# Create a mapping of article numbers for cross-reference lookup
|
| 235 |
-
article_map = {str(item['article_number']): item for item in data}
|
| 236 |
-
|
| 237 |
-
docs = []
|
| 238 |
-
for item in data:
|
| 239 |
-
# Build cross-reference section
|
| 240 |
-
cross_ref_text = ""
|
| 241 |
-
if item.get('cross_references') and len(item['cross_references']) > 0:
|
| 242 |
-
cross_ref_text = "\nالمواد ذات الصلة (المراجع المتقاطعة): " + ", ".join(
|
| 243 |
-
[f"المادة {ref}" for ref in item['cross_references']]
|
| 244 |
-
)
|
| 245 |
-
|
| 246 |
-
# Construct content
|
| 247 |
-
page_content = f"""
|
| 248 |
-
رقم المادة: {item['article_number']}
|
| 249 |
-
النص الأصلي: {item['original_text']}
|
| 250 |
-
الشرح المبسط: {item['simplified_summary']}{cross_ref_text}
|
| 251 |
-
"""
|
| 252 |
-
metadata = {
|
| 253 |
-
"article_id": item['article_id'],
|
| 254 |
-
"article_number": str(item['article_number']),
|
| 255 |
-
"legal_nature": item['legal_nature'],
|
| 256 |
-
"keywords": ", ".join(item['keywords']),
|
| 257 |
-
"part": item.get('part (Bab)', ''),
|
| 258 |
-
"chapter": item.get('chapter (Fasl)', ''),
|
| 259 |
-
"cross_references": ", ".join([str(ref) for ref in item.get('cross_references', [])]) # Convert list to string
|
| 260 |
-
}
|
| 261 |
-
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 262 |
-
|
| 263 |
-
print(f"✅ Loaded {len(docs)} constitutional articles")
|
| 264 |
-
|
| 265 |
-
# 2. Embeddings
|
| 266 |
-
print("Loading embeddings model...")
|
| 267 |
-
embeddings = HuggingFaceEmbeddings(
|
| 268 |
-
model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1"
|
| 269 |
-
)
|
| 270 |
-
print("✅ Embeddings model ready")
|
| 271 |
-
|
| 272 |
-
# 3. No splitting - keep articles as complete units
|
| 273 |
-
chunks = docs
|
| 274 |
-
|
| 275 |
-
# 4. Vector Store
|
| 276 |
-
print("Building vector database...")
|
| 277 |
-
vectorstore = Chroma.from_documents(
|
| 278 |
-
chunks,
|
| 279 |
-
embeddings,
|
| 280 |
-
persist_directory="chroma_db"
|
| 281 |
-
)
|
| 282 |
-
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 15})
|
| 283 |
-
print("✅ Vector database ready")
|
| 284 |
-
|
| 285 |
-
# 5. Create BM25 Keyword Retriever
|
| 286 |
-
class BM25Retriever(BaseRetriever):
|
| 287 |
-
"""BM25-based keyword retriever for constitutional articles"""
|
| 288 |
-
corpus_docs: List[Document]
|
| 289 |
-
bm25: BM25Okapi = None
|
| 290 |
-
k: int = 15
|
| 291 |
-
|
| 292 |
-
class Config:
|
| 293 |
-
arbitrary_types_allowed = True
|
| 294 |
-
|
| 295 |
-
def __init__(self, **data):
|
| 296 |
-
super().__init__(**data)
|
| 297 |
-
# Tokenize corpus for BM25
|
| 298 |
-
tokenized_corpus = [doc.page_content.split() for doc in self.corpus_docs]
|
| 299 |
-
self.bm25 = BM25Okapi(tokenized_corpus)
|
| 300 |
-
|
| 301 |
-
def _get_relevant_documents(
|
| 302 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 303 |
-
) -> List[Document]:
|
| 304 |
-
# Tokenize query
|
| 305 |
-
tokenized_query = query.split()
|
| 306 |
-
# Get BM25 scores
|
| 307 |
-
scores = self.bm25.get_scores(tokenized_query)
|
| 308 |
-
# Get top k indices
|
| 309 |
-
top_indices = np.argsort(scores)[::-1][:self.k]
|
| 310 |
-
# Return documents
|
| 311 |
-
return [self.corpus_docs[i] for i in top_indices if scores[i] > 0]
|
| 312 |
-
|
| 313 |
-
async def _aget_relevant_documents(
|
| 314 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 315 |
-
) -> List[Document]:
|
| 316 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 317 |
-
|
| 318 |
-
bm25_retriever = BM25Retriever(corpus_docs=docs, k=15)
|
| 319 |
-
print("✅ BM25 keyword retriever ready")
|
| 320 |
-
|
| 321 |
-
# 6. Create Metadata Filter Retriever
|
| 322 |
-
class MetadataFilterRetriever(BaseRetriever):
|
| 323 |
-
"""Metadata-based filtering retriever"""
|
| 324 |
-
corpus_docs: List[Document]
|
| 325 |
-
k: int = 15
|
| 326 |
-
|
| 327 |
-
class Config:
|
| 328 |
-
arbitrary_types_allowed = True
|
| 329 |
-
|
| 330 |
-
def _get_relevant_documents(
|
| 331 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 332 |
-
) -> List[Document]:
|
| 333 |
-
query_lower = query.lower()
|
| 334 |
-
scored_docs = []
|
| 335 |
-
|
| 336 |
-
for doc in self.corpus_docs:
|
| 337 |
-
score = 0
|
| 338 |
-
# Match keywords (boosted)
|
| 339 |
-
keywords = doc.metadata.get('keywords', '').lower()
|
| 340 |
-
if any(word in keywords for word in query_lower.split()):
|
| 341 |
-
score += 4
|
| 342 |
-
|
| 343 |
-
# Match legal nature (boosted)
|
| 344 |
-
legal_nature = doc.metadata.get('legal_nature', '').lower()
|
| 345 |
-
if any(word in legal_nature for word in query_lower.split()):
|
| 346 |
-
score += 3
|
| 347 |
-
|
| 348 |
-
# Match part/chapter
|
| 349 |
-
part = doc.metadata.get('part', '').lower()
|
| 350 |
-
chapter = doc.metadata.get('chapter', '').lower()
|
| 351 |
-
if any(word in part or word in chapter for word in query_lower.split()):
|
| 352 |
-
score += 1
|
| 353 |
-
|
| 354 |
-
# Match in content
|
| 355 |
-
if any(word in doc.page_content.lower() for word in query_lower.split()):
|
| 356 |
-
score += 1
|
| 357 |
-
|
| 358 |
-
if score > 0:
|
| 359 |
-
scored_docs.append((doc, score))
|
| 360 |
-
|
| 361 |
-
# Sort by score and return top k
|
| 362 |
-
scored_docs.sort(key=lambda x: x[1], reverse=True)
|
| 363 |
-
return [doc for doc, _ in scored_docs[:self.k]]
|
| 364 |
-
|
| 365 |
-
async def _aget_relevant_documents(
|
| 366 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 367 |
-
) -> List[Document]:
|
| 368 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 369 |
-
|
| 370 |
-
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs, k=15)
|
| 371 |
-
print("✅ Metadata filter retriever ready")
|
| 372 |
-
|
| 373 |
-
# 7. Create Hybrid RRF Retriever
|
| 374 |
-
class HybridRRFRetriever(BaseRetriever):
|
| 375 |
-
"""Combines semantic, BM25, and metadata retrievers using Reciprocal Rank Fusion"""
|
| 376 |
-
semantic_retriever: BaseRetriever
|
| 377 |
-
bm25_retriever: BM25Retriever
|
| 378 |
-
metadata_retriever: MetadataFilterRetriever
|
| 379 |
-
beta_semantic: float = 0.6 # Weight for semantic search
|
| 380 |
-
beta_keyword: float = 0.25 # Weight for BM25 keyword search
|
| 381 |
-
beta_metadata: float = 0.15 # Weight for metadata filtering
|
| 382 |
-
k: int = 60 # RRF constant (typically 60)
|
| 383 |
-
top_k: int = 25
|
| 384 |
-
|
| 385 |
-
class Config:
|
| 386 |
-
arbitrary_types_allowed = True
|
| 387 |
-
|
| 388 |
-
def _get_relevant_documents(
|
| 389 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 390 |
-
) -> List[Document]:
|
| 391 |
-
# Get results from all three retrievers (no separate spans - details logged in hybrid_retrieval span)
|
| 392 |
-
semantic_docs = self.semantic_retriever.invoke(query)
|
| 393 |
-
bm25_docs = self.bm25_retriever.invoke(query)
|
| 394 |
-
metadata_docs = self.metadata_retriever.invoke(query)
|
| 395 |
-
|
| 396 |
-
# Apply Reciprocal Rank Fusion
|
| 397 |
-
rrf_scores = {}
|
| 398 |
-
|
| 399 |
-
# Process semantic results
|
| 400 |
-
for rank, doc in enumerate(semantic_docs, start=1):
|
| 401 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 402 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_semantic / (self.k + rank)
|
| 403 |
-
|
| 404 |
-
# Process BM25 results
|
| 405 |
-
for rank, doc in enumerate(bm25_docs, start=1):
|
| 406 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 407 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_keyword / (self.k + rank)
|
| 408 |
-
|
| 409 |
-
# Process metadata results
|
| 410 |
-
for rank, doc in enumerate(metadata_docs, start=1):
|
| 411 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 412 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_metadata / (self.k + rank)
|
| 413 |
-
|
| 414 |
-
# Create document lookup
|
| 415 |
-
all_docs = {}
|
| 416 |
-
for doc in semantic_docs + bm25_docs + metadata_docs:
|
| 417 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 418 |
-
if doc_id not in all_docs:
|
| 419 |
-
all_docs[doc_id] = doc
|
| 420 |
-
|
| 421 |
-
# Sort by RRF score
|
| 422 |
-
sorted_doc_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 423 |
-
|
| 424 |
-
# Return top k documents
|
| 425 |
-
result_docs = []
|
| 426 |
-
for doc_id, score in sorted_doc_ids[:self.top_k]:
|
| 427 |
-
if doc_id in all_docs:
|
| 428 |
-
result_docs.append(all_docs[doc_id])
|
| 429 |
-
|
| 430 |
-
# Log all retrieval details in one place (no nested spans to avoid hierarchy issues)
|
| 431 |
-
try:
|
| 432 |
-
with PhoenixSpan("hybrid_retrieval", {
|
| 433 |
-
"query": query[:200],
|
| 434 |
-
"beta_semantic": self.beta_semantic,
|
| 435 |
-
"beta_keyword": self.beta_keyword,
|
| 436 |
-
"beta_metadata": self.beta_metadata,
|
| 437 |
-
"rrf_k_constant": self.k,
|
| 438 |
-
"top_k_limit": self.top_k
|
| 439 |
-
}, kind="INTERNAL") as fusion_span:
|
| 440 |
-
# Semantic retrieval details
|
| 441 |
-
fusion_span.set_attr("semantic_input_count", len(semantic_docs))
|
| 442 |
-
if semantic_docs:
|
| 443 |
-
fusion_span.set_attr("semantic_top_5", ", ".join([d.metadata.get('article_number', 'N/A') for d in semantic_docs[:5]]))
|
| 444 |
-
|
| 445 |
-
# BM25 retrieval details
|
| 446 |
-
fusion_span.set_attr("bm25_input_count", len(bm25_docs))
|
| 447 |
-
if bm25_docs:
|
| 448 |
-
fusion_span.set_attr("bm25_top_5", ", ".join([d.metadata.get('article_number', 'N/A') for d in bm25_docs[:5]]))
|
| 449 |
-
|
| 450 |
-
# Metadata retrieval details
|
| 451 |
-
fusion_span.set_attr("metadata_input_count", len(metadata_docs))
|
| 452 |
-
if metadata_docs:
|
| 453 |
-
fusion_span.set_attr("metadata_top_5", ", ".join([d.metadata.get('article_number', 'N/A') for d in metadata_docs[:5]]))
|
| 454 |
-
|
| 455 |
-
# Fusion results
|
| 456 |
-
fusion_span.set_attr("unique_docs_before_fusion", len(all_docs))
|
| 457 |
-
fusion_span.set_attr("final_doc_count", len(result_docs))
|
| 458 |
-
if result_docs:
|
| 459 |
-
top_article_nums = [d.metadata.get('article_number', 'N/A') for d in result_docs[:10]]
|
| 460 |
-
fusion_span.set_attr("fused_top_10_articles", ", ".join(map(str, top_article_nums)))
|
| 461 |
-
# Show top 5 RRF scores
|
| 462 |
-
top_scores = [(doc_id, f"{score:.4f}") for doc_id, score in sorted_doc_ids[:5]]
|
| 463 |
-
fusion_span.set_attr("top_5_rrf_scores", str(top_scores))
|
| 464 |
-
fusion_span.set_attr("top_doc_preview", result_docs[0].page_content[:300])
|
| 465 |
-
except Exception:
|
| 466 |
-
pass
|
| 467 |
-
|
| 468 |
-
return result_docs
|
| 469 |
-
|
| 470 |
-
async def _aget_relevant_documents(
|
| 471 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 472 |
-
) -> List[Document]:
|
| 473 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 474 |
-
|
| 475 |
-
# Create hybrid retriever with tuned beta weights
|
| 476 |
-
hybrid_retriever = HybridRRFRetriever(
|
| 477 |
-
semantic_retriever=base_retriever,
|
| 478 |
-
bm25_retriever=bm25_retriever,
|
| 479 |
-
metadata_retriever=metadata_retriever,
|
| 480 |
-
beta_semantic=0.6, # Semantic search gets highest weight (most reliable)
|
| 481 |
-
beta_keyword=0.25, # BM25 keyword search (good for exact term matches)
|
| 482 |
-
beta_metadata=0.15, # Metadata filtering (supporting role)
|
| 483 |
-
k=60,
|
| 484 |
-
top_k=25
|
| 485 |
-
)
|
| 486 |
-
print("✅ Hybrid RRF retriever ready with β weights: semantic=0.6, keyword=0.25, metadata=0.15, top_k=25")
|
| 487 |
-
|
| 488 |
-
# 8. Create Cross-Reference Enhanced Retriever
|
| 489 |
-
class CrossReferenceRetriever(BaseRetriever):
|
| 490 |
-
"""Enhances retrieval by automatically fetching cross-referenced articles"""
|
| 491 |
-
base_retriever: BaseRetriever
|
| 492 |
-
article_map: dict
|
| 493 |
-
|
| 494 |
-
class Config:
|
| 495 |
-
arbitrary_types_allowed = True
|
| 496 |
-
|
| 497 |
-
def _get_relevant_documents(
|
| 498 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 499 |
-
) -> List[Document]:
|
| 500 |
-
with PhoenixSpan("cross_reference_expansion", {"query": query[:200]}, kind="INTERNAL") as xref_span:
|
| 501 |
-
# Get initial results
|
| 502 |
-
initial_docs = self.base_retriever.invoke(query)
|
| 503 |
-
xref_span.set_attr("initial_doc_count", len(initial_docs))
|
| 504 |
-
|
| 505 |
-
# Collect all related article numbers
|
| 506 |
-
all_article_numbers = set()
|
| 507 |
-
for doc in initial_docs:
|
| 508 |
-
if 'article_number' in doc.metadata:
|
| 509 |
-
all_article_numbers.add(doc.metadata['article_number'])
|
| 510 |
-
# Parse cross_references (now stored as comma-separated string)
|
| 511 |
-
cross_refs_str = doc.metadata.get('cross_references', '')
|
| 512 |
-
if cross_refs_str:
|
| 513 |
-
cross_refs = [ref.strip() for ref in cross_refs_str.split(',')]
|
| 514 |
-
for ref in cross_refs:
|
| 515 |
-
if ref: # Skip empty strings
|
| 516 |
-
all_article_numbers.add(str(ref))
|
| 517 |
-
|
| 518 |
-
# Build enhanced document list
|
| 519 |
-
enhanced_docs = []
|
| 520 |
-
seen_numbers = set()
|
| 521 |
-
|
| 522 |
-
# Add initially retrieved documents
|
| 523 |
-
for doc in initial_docs:
|
| 524 |
-
enhanced_docs.append(doc)
|
| 525 |
-
seen_numbers.add(doc.metadata.get('article_number'))
|
| 526 |
-
|
| 527 |
-
# Add cross-referenced articles not yet retrieved
|
| 528 |
-
for article_num in all_article_numbers:
|
| 529 |
-
if article_num not in seen_numbers and article_num in self.article_map:
|
| 530 |
-
article_data = self.article_map[article_num]
|
| 531 |
-
cross_ref_text = ""
|
| 532 |
-
if article_data.get('cross_references'):
|
| 533 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join(
|
| 534 |
-
[f"المادة {ref}" for ref in article_data['cross_references']]
|
| 535 |
-
)
|
| 536 |
-
|
| 537 |
-
page_content = f"""
|
| 538 |
-
رقم المادة: {article_data['article_number']}
|
| 539 |
-
النص الأصلي: {article_data['original_text']}
|
| 540 |
-
الشرح المبسط: {article_data['simplified_summary']}{cross_ref_text}
|
| 541 |
-
"""
|
| 542 |
-
|
| 543 |
-
enhanced_doc = Document(
|
| 544 |
-
page_content=page_content,
|
| 545 |
-
metadata={
|
| 546 |
-
"article_id": article_data['article_id'],
|
| 547 |
-
"article_number": str(article_data['article_number']),
|
| 548 |
-
"legal_nature": article_data['legal_nature'],
|
| 549 |
-
"keywords": ", ".join(article_data['keywords']),
|
| 550 |
-
"cross_references": ", ".join([str(ref) for ref in article_data.get('cross_references', [])])
|
| 551 |
-
}
|
| 552 |
-
)
|
| 553 |
-
enhanced_docs.append(enhanced_doc)
|
| 554 |
-
seen_numbers.add(article_num)
|
| 555 |
-
|
| 556 |
-
# Record expansion stats (OUTSIDE the loop, at the end)
|
| 557 |
-
expanded_articles = [doc.metadata.get('article_number') for doc in enhanced_docs if doc not in initial_docs]
|
| 558 |
-
xref_span.set_attr("cross_refs_added", len(expanded_articles))
|
| 559 |
-
xref_span.set_attr("final_doc_count", len(enhanced_docs))
|
| 560 |
-
if expanded_articles:
|
| 561 |
-
xref_span.set_attr("expanded_article_numbers", ", ".join(map(str, expanded_articles[:15])))
|
| 562 |
-
|
| 563 |
-
return enhanced_docs
|
| 564 |
-
|
| 565 |
-
async def _aget_relevant_documents(
|
| 566 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 567 |
-
) -> List[Document]:
|
| 568 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 569 |
-
|
| 570 |
-
cross_ref_retriever = CrossReferenceRetriever(
|
| 571 |
-
base_retriever=hybrid_retriever,
|
| 572 |
-
article_map=article_map
|
| 573 |
-
)
|
| 574 |
-
print("✅ Cross-reference retriever ready (using hybrid RRF base)")
|
| 575 |
-
|
| 576 |
-
# 9. Reranker
|
| 577 |
-
print("Loading reranker model...")
|
| 578 |
-
local_model_path = r"D:\FOE\Senior 2\Graduation Project\Chatbot_me\reranker"
|
| 579 |
-
|
| 580 |
-
if not os.path.exists(local_model_path):
|
| 581 |
-
raise FileNotFoundError(f"Reranker path not found: {local_model_path}")
|
| 582 |
-
|
| 583 |
-
model = HuggingFaceCrossEncoder(model_name=local_model_path)
|
| 584 |
-
compressor = CrossEncoderReranker(model=model, top_n=10)
|
| 585 |
-
|
| 586 |
-
# Wrap compression retriever to add Phoenix spans
|
| 587 |
-
class InstrumentedCompressionRetriever(BaseRetriever):
|
| 588 |
-
base_retriever: ContextualCompressionRetriever
|
| 589 |
-
|
| 590 |
-
class Config:
|
| 591 |
-
arbitrary_types_allowed = True
|
| 592 |
-
|
| 593 |
-
def _get_relevant_documents(
|
| 594 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 595 |
-
) -> List[Document]:
|
| 596 |
-
with PhoenixSpan("reranker_compression", {
|
| 597 |
-
"query": query[:200],
|
| 598 |
-
"model": "HuggingFaceCrossEncoder",
|
| 599 |
-
"top_n": 10
|
| 600 |
-
}, kind="INTERNAL") as rerank_span:
|
| 601 |
-
# Apply reranking (this will call cross_ref_retriever internally)
|
| 602 |
-
reranked_docs = self.base_retriever.invoke(query)
|
| 603 |
-
|
| 604 |
-
rerank_span.set_attr("output_doc_count", len(reranked_docs))
|
| 605 |
-
if reranked_docs:
|
| 606 |
-
output_articles = [d.metadata.get('article_number', 'N/A') for d in reranked_docs]
|
| 607 |
-
rerank_span.set_attr("reranked_articles", ", ".join(map(str, output_articles)))
|
| 608 |
-
rerank_span.set_attr("top_doc_preview", reranked_docs[0].page_content[:400] if reranked_docs else "")
|
| 609 |
-
|
| 610 |
-
return reranked_docs
|
| 611 |
-
|
| 612 |
-
async def _aget_relevant_documents(
|
| 613 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 614 |
-
) -> List[Document]:
|
| 615 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 616 |
-
|
| 617 |
-
base_compression_retriever = ContextualCompressionRetriever(
|
| 618 |
-
base_compressor=compressor,
|
| 619 |
-
base_retriever=cross_ref_retriever
|
| 620 |
-
)
|
| 621 |
-
compression_retriever = InstrumentedCompressionRetriever(base_retriever=base_compression_retriever)
|
| 622 |
-
print("✅ Reranker model ready")
|
| 623 |
-
|
| 624 |
-
# 7. LLM - More deterministic for relevance
|
| 625 |
-
# 7. LLM Configuration
|
| 626 |
-
llm = ChatGroq(
|
| 627 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 628 |
-
model_name="llama-3.1-8b-instant",
|
| 629 |
-
temperature=0.1,
|
| 630 |
-
model_kwargs={"top_p": 0.9}
|
| 631 |
-
)
|
| 632 |
-
|
| 633 |
-
# ==================================================
|
| 634 |
-
# 🛠️ THE FIX: SEPARATE SYSTEM INSTRUCTIONS FROM USER INPUT
|
| 635 |
-
# ==================================================
|
| 636 |
-
|
| 637 |
-
# ==================================================
|
| 638 |
-
# 🧠 PROMPT ENGINEERING: DECISION TREE LOGIC
|
| 639 |
-
# ==================================================
|
| 640 |
-
|
| 641 |
-
system_instructions = """
|
| 642 |
-
<role>
|
| 643 |
-
أنت "المساعد القانوني الذكي"، خبير متخصص في الدستور المصري والقوانين الإجرائية.
|
| 644 |
-
مهمتك: تقديم إجابات دقيقة بناءً على "السياق التشريعي" المرفق أولاً، أو تقديم نصائح إجرائية عامة عند الضرورة.
|
| 645 |
-
</role>
|
| 646 |
-
|
| 647 |
-
<decision_logic>
|
| 648 |
-
عليك تحليل "سؤال المستخدم" و"السياق التشريعي" وتصنيف الحالة واختيار الرد المناسب بناءً على القواعد التالية بدقة:
|
| 649 |
-
|
| 650 |
-
🔴 الحالة الأولى: (الإجابة موجودة في السياق التشريعي)
|
| 651 |
-
الشرط: إذا وجدت معلومات داخل "السياق التشريعي المتاح" تجيب على السؤال.
|
| 652 |
-
الفعل:
|
| 653 |
-
1. استخرج الإجابة من السياق فقط.
|
| 654 |
-
2. ابدأ الإجابة مباشرة دون مقدمات.
|
| 655 |
-
3. يجب توثيق الإجابة برقم المادة (مثال: "نصت المادة (50) على...").
|
| 656 |
-
4. توقف هنا. لا تضف أي معلومات خارجية.
|
| 657 |
-
|
| 658 |
-
🟡 الحالة الثانية: (السياق فارغ/غير مفيد + السؤال إجرائي/عملي)
|
| 659 |
-
الشرط: إذا لم تجد الإجابة في السياق، وكان السؤال عن إجراءات عملية (مثل: حادث، سرقة، طلاق، تحرير محضر، تعامل مع الشرطة).
|
| 660 |
-
الفعل:
|
| 661 |
-
1. تجاهل السياق الفارغ.
|
| 662 |
-
2. استخدم معرفتك العامة بالقانون المصري.
|
| 663 |
-
3. ابدأ وجوباً بعبارة: "بناءً على الإجراءات القانونية العامة في مصر (وليس نصاً دستورياً محدداً):"
|
| 664 |
-
4. قدم الخطوات في نقاط مرقمة واضحة ومختصرة (1، 2، 3).
|
| 665 |
-
5. تحذير: لا تذكر أرقام مواد قانونية (لا تخترع أرقام مواد).
|
| 666 |
-
|
| 667 |
-
🔵 الحالة الثالثة: (السياق فارغ + السؤال عن نص دستوري محدد)
|
| 668 |
-
الشرط: إذا سأل عن (مجلس الشعب، الشورى، مادة محددة) ولم تجدها في السياق.
|
| 669 |
-
الفعل:
|
| 670 |
-
1. قل بوضوح: "عذراً، لم يرد ذكر لهذا الموضوع في المواد الدستورية التي تم استرجاعها في السياق الحالي."
|
| 671 |
-
2. لا تحاول الإجابة من ذاكرتك لكي لا تخطئ في النصوص الدستورية الحساسة.
|
| 672 |
-
|
| 673 |
-
🟢 الحالة الرابعة: (محادثة ودية)
|
| 674 |
-
الشرط: تحية، شكر، أو "كيف حالك".
|
| 675 |
-
الفعل: رد بتحية مهذبة جداً ومقتضبة، ثم قل: "أنا جاهز للإجابة على استفساراتك القانونية."
|
| 676 |
-
|
| 677 |
-
⚫ الحالة الخامسة: (خارج النطاق تماماً)
|
| 678 |
-
الشرط: طبخ، رياضة، برمجة، أو أي موضوع غير قانوني.
|
| 679 |
-
الفعل: اعتذر بلطف ووجه المستخدم للسؤال في القانون.
|
| 680 |
-
</decision_logic>
|
| 681 |
-
|
| 682 |
-
<formatting_rules>
|
| 683 |
-
- لا تكرر هذه التعليمات في ردك.
|
| 684 |
-
- استخدم فقرات قصيرة واترك سطراً فارغاً بينها.
|
| 685 |
-
- لا تستخدم عبارات مثل "بناء على السياق المرفق" في بداية الجملة، بل ادخل في صلب الموضوع فوراً.
|
| 686 |
-
- التزم باللغة العربية الفصحى المبسطة والرصينة.
|
| 687 |
-
</formatting_rules>
|
| 688 |
-
"""
|
| 689 |
-
|
| 690 |
-
# We use .from_messages to strictly separate instructions from data
|
| 691 |
-
prompt = ChatPromptTemplate.from_messages([
|
| 692 |
-
("system", system_instructions),
|
| 693 |
-
("system", "السياق التشريعي المتاح (المصدر الأساسي):\n{context}"),
|
| 694 |
-
("human", "سؤال المستفيد:\n{input}")
|
| 695 |
-
])
|
| 696 |
-
|
| 697 |
-
# 9. Build Chain with RunnableParallel (returns both context and answer)
|
| 698 |
-
qa_chain = (
|
| 699 |
-
RunnableParallel({
|
| 700 |
-
"context": compression_retriever,
|
| 701 |
-
"input": RunnablePassthrough()
|
| 702 |
-
})
|
| 703 |
-
.assign(answer=(
|
| 704 |
-
prompt
|
| 705 |
-
| llm
|
| 706 |
-
| StrOutputParser()
|
| 707 |
-
))
|
| 708 |
-
)
|
| 709 |
-
|
| 710 |
-
print("✅ System ready to use!")
|
| 711 |
-
return qa_chain
|
| 712 |
-
|
| 713 |
-
# ==========================================
|
| 714 |
-
# ⚡ MAIN EXECUTION
|
| 715 |
-
# ==========================================
|
| 716 |
-
|
| 717 |
-
try:
|
| 718 |
-
# Only need the chain now - it handles all retrieval internally
|
| 719 |
-
qa_chain = initialize_rag_pipeline()
|
| 720 |
-
|
| 721 |
-
except Exception as e:
|
| 722 |
-
st.error(f"Critical Error loading application: {e}")
|
| 723 |
-
st.stop()
|
| 724 |
-
|
| 725 |
-
# ==========================================
|
| 726 |
-
# 💬 CHAT LOOP
|
| 727 |
-
# ==========================================
|
| 728 |
-
if "messages" not in st.session_state:
|
| 729 |
-
st.session_state.messages = []
|
| 730 |
-
|
| 731 |
-
# Display Chat History (with Eastern Arabic numerals)
|
| 732 |
-
for message in st.session_state.messages:
|
| 733 |
-
with st.chat_message(message["role"]):
|
| 734 |
-
# Convert to Eastern Arabic when displaying from history
|
| 735 |
-
st.markdown(convert_to_eastern_arabic(message["content"]))
|
| 736 |
-
|
| 737 |
-
# Handle New User Input
|
| 738 |
-
if prompt_input := st.chat_input("اكتب سؤالك القانوني هنا..."):
|
| 739 |
-
# Show user message
|
| 740 |
-
st.session_state.messages.append({"role": "user", "content": prompt_input})
|
| 741 |
-
with st.chat_message("user"):
|
| 742 |
-
st.markdown(prompt_input)
|
| 743 |
-
|
| 744 |
-
# Generate Response
|
| 745 |
-
with st.chat_message("assistant"):
|
| 746 |
-
with st.spinner("جاري التحليل القانوني..."):
|
| 747 |
-
try:
|
| 748 |
-
# Invoke chain ONCE - returns Dict with 'context', 'input', and 'answer'
|
| 749 |
-
with PhoenixSpan("chat_request", {
|
| 750 |
-
"question": prompt_input,
|
| 751 |
-
"question_len": len(prompt_input or ""),
|
| 752 |
-
"timestamp": datetime.utcnow().isoformat(),
|
| 753 |
-
}, kind="SERVER") as span:
|
| 754 |
-
result = qa_chain.invoke(prompt_input)
|
| 755 |
-
|
| 756 |
-
# Extract answer and context from result
|
| 757 |
-
response_text = result["answer"]
|
| 758 |
-
source_docs = result["context"]
|
| 759 |
-
|
| 760 |
-
# Attach detailed context attributes
|
| 761 |
-
try:
|
| 762 |
-
ctx_list = result.get("context", []) or []
|
| 763 |
-
ctx_count = len(ctx_list)
|
| 764 |
-
span.set_attr("context_count", ctx_count)
|
| 765 |
-
if ctx_count:
|
| 766 |
-
# Record all article numbers
|
| 767 |
-
article_nums = [doc.metadata.get("article_number", "N/A") for doc in ctx_list]
|
| 768 |
-
span.set_attr("context_articles", ", ".join(map(str, article_nums)))
|
| 769 |
-
# Record legal natures
|
| 770 |
-
legal_natures = [doc.metadata.get("legal_nature", "N/A") for doc in ctx_list]
|
| 771 |
-
span.set_attr("legal_natures", ", ".join(legal_natures[:5]))
|
| 772 |
-
# Add context preview (first doc)
|
| 773 |
-
span.set_attr("context_preview", ctx_list[0].page_content[:500])
|
| 774 |
-
except Exception:
|
| 775 |
-
pass
|
| 776 |
-
|
| 777 |
-
# Log LLM generation as a nested span (properly nested under chat_request)
|
| 778 |
-
with PhoenixSpan("llm_generation", {
|
| 779 |
-
"model": "llama-3.1-8b-instant",
|
| 780 |
-
"temperature": 0.1,
|
| 781 |
-
"top_p": 0.9,
|
| 782 |
-
"prompt_preview": prompt_input[:300]
|
| 783 |
-
}, kind="CLIENT") as llm_span:
|
| 784 |
-
llm_span.set_attr("response", response_text)
|
| 785 |
-
llm_span.set_attr("response_len", len(response_text))
|
| 786 |
-
llm_span.set_attr("response_preview", response_text[:500])
|
| 787 |
-
llm_span.set_attr("context_docs_used", len(source_docs))
|
| 788 |
-
|
| 789 |
-
# Display Answer
|
| 790 |
-
response_text_arabic = convert_to_eastern_arabic(response_text)
|
| 791 |
-
st.markdown(response_text_arabic)
|
| 792 |
-
|
| 793 |
-
# Display Sources
|
| 794 |
-
if source_docs and len(source_docs) > 0:
|
| 795 |
-
print(f"✅ Found {len(source_docs)} documents")
|
| 796 |
-
# Deduplicate documents by article_number
|
| 797 |
-
seen_articles = set()
|
| 798 |
-
unique_docs = []
|
| 799 |
-
|
| 800 |
-
for doc in source_docs:
|
| 801 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 802 |
-
if article_num and article_num not in seen_articles:
|
| 803 |
-
seen_articles.add(article_num)
|
| 804 |
-
unique_docs.append(doc)
|
| 805 |
-
|
| 806 |
-
st.markdown("---") # Separator before sources
|
| 807 |
-
|
| 808 |
-
if unique_docs:
|
| 809 |
-
with st.expander(f"📚 المصادر المستخدمة ({len(unique_docs)} مادة)"):
|
| 810 |
-
st.markdown("### المواد الدستورية المستخدمة في التحليل:")
|
| 811 |
-
st.markdown("---")
|
| 812 |
-
|
| 813 |
-
for idx, doc in enumerate(unique_docs, 1):
|
| 814 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 815 |
-
legal_nature = doc.metadata.get('legal_nature', '')
|
| 816 |
-
|
| 817 |
-
if article_num:
|
| 818 |
-
st.markdown(f"**المادة رقم {convert_to_eastern_arabic(article_num)}**")
|
| 819 |
-
if legal_nature:
|
| 820 |
-
st.markdown(f"*الطبيعة القانونية: {legal_nature}*")
|
| 821 |
-
|
| 822 |
-
# Display article content
|
| 823 |
-
content_lines = doc.page_content.strip().split('\n')
|
| 824 |
-
for line in content_lines:
|
| 825 |
-
line = line.strip()
|
| 826 |
-
if line:
|
| 827 |
-
st.markdown(convert_to_eastern_arabic(line))
|
| 828 |
-
|
| 829 |
-
st.markdown("---")
|
| 830 |
-
else:
|
| 831 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 832 |
-
else:
|
| 833 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 834 |
-
|
| 835 |
-
# Persist the raw answer to avoid double conversion glitches on rerun
|
| 836 |
-
st.session_state.messages.append({"role": "assistant", "content": response_text})
|
| 837 |
-
except Exception as e:
|
| 838 |
-
st.error(f"حدث خطأ: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app_final_updated.py
DELETED
|
@@ -1,704 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
import os
|
| 3 |
-
import sys
|
| 4 |
-
import json
|
| 5 |
-
from dotenv import load_dotenv
|
| 6 |
-
import logging
|
| 7 |
-
import warnings
|
| 8 |
-
|
| 9 |
-
# Suppress progress bars from transformers/tqdm
|
| 10 |
-
os.environ['TRANSFORMERS_NO_PROGRESS_BAR'] = '1'
|
| 11 |
-
warnings.filterwarnings('ignore')
|
| 12 |
-
|
| 13 |
-
# 1. Loaders & Splitters
|
| 14 |
-
from langchain_core.documents import Document
|
| 15 |
-
#from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 16 |
-
from langchain_core.retrievers import BaseRetriever
|
| 17 |
-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 18 |
-
from typing import List
|
| 19 |
-
from rank_bm25 import BM25Okapi
|
| 20 |
-
import numpy as np
|
| 21 |
-
|
| 22 |
-
# 2. Vector Store & Embeddings
|
| 23 |
-
from langchain_chroma import Chroma
|
| 24 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 25 |
-
|
| 26 |
-
# 3. Reranker Imports
|
| 27 |
-
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 28 |
-
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 29 |
-
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 30 |
-
|
| 31 |
-
# 4. LLM
|
| 32 |
-
from langchain_groq import ChatGroq
|
| 33 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 34 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 35 |
-
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
|
| 36 |
-
|
| 37 |
-
# Configure logging
|
| 38 |
-
logging.basicConfig(level=logging.INFO)
|
| 39 |
-
logger = logging.getLogger(__name__)
|
| 40 |
-
|
| 41 |
-
load_dotenv()
|
| 42 |
-
|
| 43 |
-
# ==========================================
|
| 44 |
-
# 🧭 RUNTIME MODE (UI vs CLI)
|
| 45 |
-
# ==========================================
|
| 46 |
-
RUN_MODE = os.getenv("RUN_MODE", "").strip().lower()
|
| 47 |
-
IS_CLI = RUN_MODE in {"cli", "terminal", "eval", "evaluation"}
|
| 48 |
-
|
| 49 |
-
if IS_CLI:
|
| 50 |
-
class _DummyStreamlit:
|
| 51 |
-
@staticmethod
|
| 52 |
-
def cache_resource(func=None, **_kwargs):
|
| 53 |
-
if func is None:
|
| 54 |
-
def decorator(f):
|
| 55 |
-
return f
|
| 56 |
-
return decorator
|
| 57 |
-
return func
|
| 58 |
-
|
| 59 |
-
st = _DummyStreamlit()
|
| 60 |
-
else:
|
| 61 |
-
import streamlit as st
|
| 62 |
-
|
| 63 |
-
# ==========================================
|
| 64 |
-
# 📁 PATHS (use project-relative folders)
|
| 65 |
-
# ==========================================
|
| 66 |
-
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 67 |
-
DATA_DIR = os.path.join(BASE_DIR, "data")
|
| 68 |
-
CHROMA_DIR = os.path.join(BASE_DIR, "chroma_db")
|
| 69 |
-
|
| 70 |
-
if not IS_CLI:
|
| 71 |
-
# ==========================================
|
| 72 |
-
# 🎨 UI SETUP (CSS FOR ARABIC & RTL)
|
| 73 |
-
# ==========================================
|
| 74 |
-
st.set_page_config(page_title="المساعد القانوني", page_icon="⚖️")
|
| 75 |
-
|
| 76 |
-
# This CSS block fixes the "001" number issue and right alignment
|
| 77 |
-
st.markdown("""
|
| 78 |
-
<style>
|
| 79 |
-
/* Force the main app container to be Right-to-Left */
|
| 80 |
-
.stApp {
|
| 81 |
-
direction: rtl;
|
| 82 |
-
text-align: right;
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
/* Fix input fields to type from right */
|
| 86 |
-
.stTextInput input {
|
| 87 |
-
direction: rtl;
|
| 88 |
-
text-align: right;
|
| 89 |
-
}
|
| 90 |
-
|
| 91 |
-
/* Fix chat messages alignment */
|
| 92 |
-
.stChatMessage {
|
| 93 |
-
direction: rtl;
|
| 94 |
-
text-align: right;
|
| 95 |
-
}
|
| 96 |
-
|
| 97 |
-
/* Ensure proper paragraph spacing */
|
| 98 |
-
.stMarkdown p {
|
| 99 |
-
margin: 0.5em 0 !important;
|
| 100 |
-
line-height: 1.6;
|
| 101 |
-
word-spacing: 0.1em;
|
| 102 |
-
}
|
| 103 |
-
|
| 104 |
-
/* Ensure numbers display correctly in RTL */
|
| 105 |
-
p, div, span, label {
|
| 106 |
-
unicode-bidi: embed;
|
| 107 |
-
direction: inherit;
|
| 108 |
-
white-space: normal;
|
| 109 |
-
word-wrap: break-word;
|
| 110 |
-
}
|
| 111 |
-
|
| 112 |
-
/* Force all content to respect RTL */
|
| 113 |
-
* {
|
| 114 |
-
direction: rtl !important;
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
/* Preserve line breaks and spacing */
|
| 118 |
-
.stMarkdown pre {
|
| 119 |
-
direction: rtl;
|
| 120 |
-
text-align: right;
|
| 121 |
-
white-space: pre-wrap;
|
| 122 |
-
word-wrap: break-word;
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
/* Hide the "Deploy" button and standard menu for cleaner look */
|
| 126 |
-
#MainMenu {visibility: hidden;}
|
| 127 |
-
footer {visibility: hidden;}
|
| 128 |
-
|
| 129 |
-
</style>
|
| 130 |
-
""", unsafe_allow_html=True)
|
| 131 |
-
|
| 132 |
-
# Put this at the top of your code
|
| 133 |
-
def convert_to_eastern_arabic(text):
|
| 134 |
-
"""Converts 0123456789 to ٠١٢٣٤٥٦٧٨٩"""
|
| 135 |
-
if not isinstance(text, str):
|
| 136 |
-
return text
|
| 137 |
-
western_numerals = '0123456789'
|
| 138 |
-
eastern_numerals = '٠١٢٣٤٥٦٧٨٩'
|
| 139 |
-
translation_table = str.maketrans(western_numerals, eastern_numerals)
|
| 140 |
-
return text.translate(translation_table)
|
| 141 |
-
|
| 142 |
-
if not IS_CLI:
|
| 143 |
-
st.title("⚖️ المساعد القانوني الذكي (دستور مصر)")
|
| 144 |
-
|
| 145 |
-
# ==========================================
|
| 146 |
-
# 🚀 CACHED RESOURCE LOADING (THE FIX)
|
| 147 |
-
# ==========================================
|
| 148 |
-
# This decorator tells Streamlit: "Run this ONCE and save the result."
|
| 149 |
-
@st.cache_resource
|
| 150 |
-
def initialize_rag_pipeline():
|
| 151 |
-
print("🔄 Initializing system...")
|
| 152 |
-
print("📥 Loading data...")
|
| 153 |
-
# 1. Load JSONs from ./data (supports multiple files)
|
| 154 |
-
def load_json_folder(folder_path: str):
|
| 155 |
-
all_items = []
|
| 156 |
-
for filename in os.listdir(folder_path):
|
| 157 |
-
if not filename.lower().endswith(".json"):
|
| 158 |
-
continue
|
| 159 |
-
file_path = os.path.join(folder_path, filename)
|
| 160 |
-
with open(file_path, "r", encoding="utf-8") as f:
|
| 161 |
-
obj = json.load(f)
|
| 162 |
-
|
| 163 |
-
# Support: list of articles, or dict with 'data'/'articles', or single dict article
|
| 164 |
-
if isinstance(obj, list):
|
| 165 |
-
all_items.extend(obj)
|
| 166 |
-
elif isinstance(obj, dict):
|
| 167 |
-
if "data" in obj and isinstance(obj["data"], list):
|
| 168 |
-
all_items.extend(obj["data"])
|
| 169 |
-
elif "articles" in obj and isinstance(obj["articles"], list):
|
| 170 |
-
all_items.extend(obj["articles"])
|
| 171 |
-
else:
|
| 172 |
-
all_items.append(obj)
|
| 173 |
-
else:
|
| 174 |
-
logger.warning(f"Unsupported JSON format in: {file_path}")
|
| 175 |
-
return all_items
|
| 176 |
-
|
| 177 |
-
if not os.path.exists(DATA_DIR):
|
| 178 |
-
raise FileNotFoundError(f"Data folder not found: {DATA_DIR}")
|
| 179 |
-
|
| 180 |
-
data = load_json_folder(DATA_DIR)
|
| 181 |
-
|
| 182 |
-
# Optional: de-duplicate (article_id preferred, fallback to article_number)
|
| 183 |
-
unique = {}
|
| 184 |
-
for item in data:
|
| 185 |
-
key = str(item.get("article_id") or item.get("article_number") or hash(json.dumps(item, ensure_ascii=False)))
|
| 186 |
-
unique[key] = item
|
| 187 |
-
data = list(unique.values())
|
| 188 |
-
|
| 189 |
-
# Create a mapping of article numbers for cross-reference lookup
|
| 190 |
-
article_map = {str(item['article_number']): item for item in data if 'article_number' in item}
|
| 191 |
-
|
| 192 |
-
docs = []
|
| 193 |
-
for item in data:
|
| 194 |
-
article_number = item.get("article_number")
|
| 195 |
-
original_text = item.get("original_text")
|
| 196 |
-
simplified_summary = item.get("simplified_summary")
|
| 197 |
-
|
| 198 |
-
if not article_number or not original_text or not simplified_summary:
|
| 199 |
-
logger.warning("Skipping item with missing fields (article_number/original_text/simplified_summary)")
|
| 200 |
-
continue
|
| 201 |
-
|
| 202 |
-
cross_refs = item.get("cross_references")
|
| 203 |
-
if not isinstance(cross_refs, list):
|
| 204 |
-
cross_refs = []
|
| 205 |
-
|
| 206 |
-
# Build cross-reference section
|
| 207 |
-
cross_ref_text = ""
|
| 208 |
-
if cross_refs:
|
| 209 |
-
cross_ref_text = "\nالمواد ذات الصلة (المراجع المتقاطعة): " + ", ".join(
|
| 210 |
-
[f"المادة {ref}" for ref in cross_refs]
|
| 211 |
-
)
|
| 212 |
-
|
| 213 |
-
# Construct content
|
| 214 |
-
page_content = f"""
|
| 215 |
-
رقم المادة: {article_number}
|
| 216 |
-
النص الأصلي: {original_text}
|
| 217 |
-
الشرح المبسط: {simplified_summary}{cross_ref_text}
|
| 218 |
-
"""
|
| 219 |
-
|
| 220 |
-
metadata = {
|
| 221 |
-
"article_id": item.get("article_id") or str(article_number),
|
| 222 |
-
"article_number": str(article_number),
|
| 223 |
-
"legal_nature": item.get("legal_nature", ""),
|
| 224 |
-
"keywords": ", ".join(item.get("keywords", []) or []),
|
| 225 |
-
"part": item.get("part (Bab)", ""),
|
| 226 |
-
"chapter": item.get("chapter (Fasl)", ""),
|
| 227 |
-
"cross_references": ", ".join([str(ref) for ref in cross_refs])
|
| 228 |
-
}
|
| 229 |
-
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 230 |
-
|
| 231 |
-
print(f"✅ Loaded {len(docs)} constitutional articles")
|
| 232 |
-
|
| 233 |
-
# 2. Embeddings
|
| 234 |
-
print("Loading embeddings model...")
|
| 235 |
-
embeddings = HuggingFaceEmbeddings(
|
| 236 |
-
model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1"
|
| 237 |
-
)
|
| 238 |
-
print("✅ Embeddings model ready")
|
| 239 |
-
|
| 240 |
-
# 3. No splitting - keep articles as complete units
|
| 241 |
-
chunks = docs
|
| 242 |
-
# 4. Vector Store (persist once, load on next runs)
|
| 243 |
-
if os.path.exists(CHROMA_DIR) and os.listdir(CHROMA_DIR):
|
| 244 |
-
print("📦 Loading existing vector database...")
|
| 245 |
-
vectorstore = Chroma(
|
| 246 |
-
persist_directory=CHROMA_DIR,
|
| 247 |
-
embedding_function=embeddings
|
| 248 |
-
)
|
| 249 |
-
print("✅ Loaded existing Chroma DB (no re-embedding)")
|
| 250 |
-
else:
|
| 251 |
-
print("🧱 Building vector database for the first time (this will create embeddings)...")
|
| 252 |
-
vectorstore = Chroma.from_documents(
|
| 253 |
-
chunks,
|
| 254 |
-
embeddings,
|
| 255 |
-
persist_directory=CHROMA_DIR
|
| 256 |
-
)
|
| 257 |
-
print("✅ Built Chroma DB and persisted to disk")
|
| 258 |
-
|
| 259 |
-
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 15})
|
| 260 |
-
# 5. Create BM25 Keyword Retriever
|
| 261 |
-
class BM25Retriever(BaseRetriever):
|
| 262 |
-
"""BM25-based keyword retriever for constitutional articles"""
|
| 263 |
-
corpus_docs: List[Document]
|
| 264 |
-
bm25: BM25Okapi = None
|
| 265 |
-
k: int = 15
|
| 266 |
-
|
| 267 |
-
class Config:
|
| 268 |
-
arbitrary_types_allowed = True
|
| 269 |
-
|
| 270 |
-
def __init__(self, **data):
|
| 271 |
-
super().__init__(**data)
|
| 272 |
-
# Tokenize corpus for BM25
|
| 273 |
-
tokenized_corpus = [doc.page_content.split() for doc in self.corpus_docs]
|
| 274 |
-
self.bm25 = BM25Okapi(tokenized_corpus)
|
| 275 |
-
|
| 276 |
-
def _get_relevant_documents(
|
| 277 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 278 |
-
) -> List[Document]:
|
| 279 |
-
# Tokenize query
|
| 280 |
-
tokenized_query = query.split()
|
| 281 |
-
# Get BM25 scores
|
| 282 |
-
scores = self.bm25.get_scores(tokenized_query)
|
| 283 |
-
# Get top k indices
|
| 284 |
-
top_indices = np.argsort(scores)[::-1][:self.k]
|
| 285 |
-
# Return documents
|
| 286 |
-
return [self.corpus_docs[i] for i in top_indices if scores[i] > 0]
|
| 287 |
-
|
| 288 |
-
async def _aget_relevant_documents(
|
| 289 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 290 |
-
) -> List[Document]:
|
| 291 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 292 |
-
|
| 293 |
-
bm25_retriever = BM25Retriever(corpus_docs=docs, k=15)
|
| 294 |
-
print("✅ BM25 keyword retriever ready")
|
| 295 |
-
|
| 296 |
-
# 6. Create Metadata Filter Retriever
|
| 297 |
-
class MetadataFilterRetriever(BaseRetriever):
|
| 298 |
-
"""Metadata-based filtering retriever"""
|
| 299 |
-
corpus_docs: List[Document]
|
| 300 |
-
k: int = 15
|
| 301 |
-
|
| 302 |
-
class Config:
|
| 303 |
-
arbitrary_types_allowed = True
|
| 304 |
-
|
| 305 |
-
def _get_relevant_documents(
|
| 306 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 307 |
-
) -> List[Document]:
|
| 308 |
-
query_lower = query.lower()
|
| 309 |
-
scored_docs = []
|
| 310 |
-
|
| 311 |
-
for doc in self.corpus_docs:
|
| 312 |
-
score = 0
|
| 313 |
-
# Match keywords
|
| 314 |
-
keywords = doc.metadata.get('keywords', '').lower()
|
| 315 |
-
if any(word in keywords for word in query_lower.split()):
|
| 316 |
-
score += 3
|
| 317 |
-
|
| 318 |
-
# Match legal nature
|
| 319 |
-
legal_nature = doc.metadata.get('legal_nature', '').lower()
|
| 320 |
-
if any(word in legal_nature for word in query_lower.split()):
|
| 321 |
-
score += 2
|
| 322 |
-
|
| 323 |
-
# Match part/chapter
|
| 324 |
-
part = doc.metadata.get('part', '').lower()
|
| 325 |
-
chapter = doc.metadata.get('chapter', '').lower()
|
| 326 |
-
if any(word in part or word in chapter for word in query_lower.split()):
|
| 327 |
-
score += 1
|
| 328 |
-
|
| 329 |
-
# Match in content
|
| 330 |
-
if any(word in doc.page_content.lower() for word in query_lower.split()):
|
| 331 |
-
score += 1
|
| 332 |
-
|
| 333 |
-
if score > 0:
|
| 334 |
-
scored_docs.append((doc, score))
|
| 335 |
-
|
| 336 |
-
# Sort by score and return top k
|
| 337 |
-
scored_docs.sort(key=lambda x: x[1], reverse=True)
|
| 338 |
-
return [doc for doc, _ in scored_docs[:self.k]]
|
| 339 |
-
|
| 340 |
-
async def _aget_relevant_documents(
|
| 341 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 342 |
-
) -> List[Document]:
|
| 343 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 344 |
-
|
| 345 |
-
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs, k=15)
|
| 346 |
-
print("✅ Metadata filter retriever ready")
|
| 347 |
-
|
| 348 |
-
# 7. Create Hybrid RRF Retriever
|
| 349 |
-
class HybridRRFRetriever(BaseRetriever):
|
| 350 |
-
"""Combines semantic, BM25, and metadata retrievers using Reciprocal Rank Fusion"""
|
| 351 |
-
semantic_retriever: BaseRetriever
|
| 352 |
-
bm25_retriever: BM25Retriever
|
| 353 |
-
metadata_retriever: MetadataFilterRetriever
|
| 354 |
-
beta_semantic: float = 0.6 # Weight for semantic search
|
| 355 |
-
beta_keyword: float = 0.2 # Weight for BM25 keyword search
|
| 356 |
-
beta_metadata: float = 0.2 # Weight for metadata filtering
|
| 357 |
-
k: int = 60 # RRF constant (typically 60)
|
| 358 |
-
top_k: int = 15
|
| 359 |
-
|
| 360 |
-
class Config:
|
| 361 |
-
arbitrary_types_allowed = True
|
| 362 |
-
|
| 363 |
-
def _get_relevant_documents(
|
| 364 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 365 |
-
) -> List[Document]:
|
| 366 |
-
# Get results from all three retrievers
|
| 367 |
-
semantic_docs = self.semantic_retriever.invoke(query)
|
| 368 |
-
bm25_docs = self.bm25_retriever.invoke(query)
|
| 369 |
-
metadata_docs = self.metadata_retriever.invoke(query)
|
| 370 |
-
|
| 371 |
-
# Apply Reciprocal Rank Fusion
|
| 372 |
-
rrf_scores = {}
|
| 373 |
-
|
| 374 |
-
# Process semantic results
|
| 375 |
-
for rank, doc in enumerate(semantic_docs, start=1):
|
| 376 |
-
doc_id = (doc.metadata.get('article_id') or doc.metadata.get('article_number') or str(hash(doc.page_content)))
|
| 377 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_semantic / (self.k + rank)
|
| 378 |
-
|
| 379 |
-
# Process BM25 results
|
| 380 |
-
for rank, doc in enumerate(bm25_docs, start=1):
|
| 381 |
-
doc_id = (doc.metadata.get('article_id') or doc.metadata.get('article_number') or str(hash(doc.page_content)))
|
| 382 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_keyword / (self.k + rank)
|
| 383 |
-
|
| 384 |
-
# Process metadata results
|
| 385 |
-
for rank, doc in enumerate(metadata_docs, start=1):
|
| 386 |
-
doc_id = (doc.metadata.get('article_id') or doc.metadata.get('article_number') or str(hash(doc.page_content)))
|
| 387 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_metadata / (self.k + rank)
|
| 388 |
-
|
| 389 |
-
# Create document lookup
|
| 390 |
-
all_docs = {}
|
| 391 |
-
for doc in semantic_docs + bm25_docs + metadata_docs:
|
| 392 |
-
doc_id = (doc.metadata.get('article_id') or doc.metadata.get('article_number') or str(hash(doc.page_content)))
|
| 393 |
-
if doc_id not in all_docs:
|
| 394 |
-
all_docs[doc_id] = doc
|
| 395 |
-
|
| 396 |
-
# Sort by RRF score
|
| 397 |
-
sorted_doc_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 398 |
-
|
| 399 |
-
# Return top k documents
|
| 400 |
-
result_docs = []
|
| 401 |
-
for doc_id, score in sorted_doc_ids[:self.top_k]:
|
| 402 |
-
if doc_id in all_docs:
|
| 403 |
-
result_docs.append(all_docs[doc_id])
|
| 404 |
-
|
| 405 |
-
return result_docs
|
| 406 |
-
|
| 407 |
-
async def _aget_relevant_documents(
|
| 408 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 409 |
-
) -> List[Document]:
|
| 410 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 411 |
-
|
| 412 |
-
# Create hybrid retriever with tuned beta weights
|
| 413 |
-
hybrid_retriever = HybridRRFRetriever(
|
| 414 |
-
semantic_retriever=base_retriever,
|
| 415 |
-
bm25_retriever=bm25_retriever,
|
| 416 |
-
metadata_retriever=metadata_retriever,
|
| 417 |
-
beta_semantic=0.5, # Semantic search gets highest weight (most reliable)
|
| 418 |
-
beta_keyword=0.3, # BM25 keyword search (good for exact term matches)
|
| 419 |
-
beta_metadata=0.2, # Metadata filtering (supporting role)
|
| 420 |
-
k=60,
|
| 421 |
-
top_k=20
|
| 422 |
-
)
|
| 423 |
-
print("✅ Hybrid RRF retriever ready with β weights: semantic=0.5, keyword=0.3, metadata=0.2")
|
| 424 |
-
|
| 425 |
-
# 8. Create Cross-Reference Enhanced Retriever
|
| 426 |
-
class CrossReferenceRetriever(BaseRetriever):
|
| 427 |
-
"""Enhances retrieval by automatically fetching cross-referenced articles"""
|
| 428 |
-
base_retriever: BaseRetriever
|
| 429 |
-
article_map: dict
|
| 430 |
-
|
| 431 |
-
class Config:
|
| 432 |
-
arbitrary_types_allowed = True
|
| 433 |
-
|
| 434 |
-
def _get_relevant_documents(
|
| 435 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 436 |
-
) -> List[Document]:
|
| 437 |
-
# Get initial results
|
| 438 |
-
initial_docs = self.base_retriever.invoke(query)
|
| 439 |
-
|
| 440 |
-
# Collect all related article numbers
|
| 441 |
-
all_article_numbers = set()
|
| 442 |
-
for doc in initial_docs:
|
| 443 |
-
if 'article_number' in doc.metadata:
|
| 444 |
-
all_article_numbers.add(doc.metadata['article_number'])
|
| 445 |
-
# Parse cross_references (now stored as comma-separated string)
|
| 446 |
-
cross_refs_str = doc.metadata.get('cross_references', '')
|
| 447 |
-
if cross_refs_str:
|
| 448 |
-
cross_refs = [ref.strip() for ref in cross_refs_str.split(',')]
|
| 449 |
-
for ref in cross_refs:
|
| 450 |
-
if ref: # Skip empty strings
|
| 451 |
-
all_article_numbers.add(str(ref))
|
| 452 |
-
|
| 453 |
-
# Build enhanced document list
|
| 454 |
-
enhanced_docs = []
|
| 455 |
-
seen_numbers = set()
|
| 456 |
-
|
| 457 |
-
# Add initially retrieved documents
|
| 458 |
-
for doc in initial_docs:
|
| 459 |
-
enhanced_docs.append(doc)
|
| 460 |
-
seen_numbers.add(doc.metadata.get('article_number'))
|
| 461 |
-
|
| 462 |
-
# Add cross-referenced articles not yet retrieved
|
| 463 |
-
for article_num in all_article_numbers:
|
| 464 |
-
if article_num not in seen_numbers and article_num in self.article_map:
|
| 465 |
-
article_data = self.article_map[article_num]
|
| 466 |
-
cross_ref_text = ""
|
| 467 |
-
cross_refs = article_data.get("cross_references")
|
| 468 |
-
if not isinstance(cross_refs, list):
|
| 469 |
-
cross_refs = []
|
| 470 |
-
if cross_refs:
|
| 471 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join(
|
| 472 |
-
[f"المادة {ref}" for ref in cross_refs]
|
| 473 |
-
)
|
| 474 |
-
|
| 475 |
-
page_content = f"""
|
| 476 |
-
رقم المادة: {article_data.get('article_number', '')}
|
| 477 |
-
النص الأصلي: {article_data.get('original_text', '')}
|
| 478 |
-
الشرح المبسط: {article_data.get('simplified_summary', '')}{cross_ref_text}
|
| 479 |
-
"""
|
| 480 |
-
|
| 481 |
-
enhanced_doc = Document(
|
| 482 |
-
page_content=page_content,
|
| 483 |
-
metadata={
|
| 484 |
-
"article_id": article_data.get("article_id") or str(article_data.get("article_number", "")),
|
| 485 |
-
"article_number": str(article_data.get("article_number", "")),
|
| 486 |
-
"legal_nature": article_data.get("legal_nature", ""),
|
| 487 |
-
"keywords": ", ".join(article_data.get("keywords", []) or []),
|
| 488 |
-
"cross_references": ", ".join([str(ref) for ref in cross_refs])
|
| 489 |
-
}
|
| 490 |
-
)
|
| 491 |
-
enhanced_docs.append(enhanced_doc)
|
| 492 |
-
seen_numbers.add(article_num)
|
| 493 |
-
|
| 494 |
-
return enhanced_docs
|
| 495 |
-
|
| 496 |
-
async def _aget_relevant_documents(
|
| 497 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 498 |
-
) -> List[Document]:
|
| 499 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 500 |
-
|
| 501 |
-
cross_ref_retriever = CrossReferenceRetriever(
|
| 502 |
-
base_retriever=hybrid_retriever,
|
| 503 |
-
article_map=article_map
|
| 504 |
-
)
|
| 505 |
-
print("✅ Cross-reference retriever ready (using hybrid RRF base)")
|
| 506 |
-
|
| 507 |
-
# 9. Reranker
|
| 508 |
-
print("Loading reranker model...")
|
| 509 |
-
local_model_path = r"D:\FOE\Senior 2\Graduation Project\Chatbot_me\reranker"
|
| 510 |
-
|
| 511 |
-
if not os.path.exists(local_model_path):
|
| 512 |
-
raise FileNotFoundError(f"Reranker path not found: {local_model_path}")
|
| 513 |
-
|
| 514 |
-
model = HuggingFaceCrossEncoder(model_name=local_model_path)
|
| 515 |
-
compressor = CrossEncoderReranker(model=model, top_n=5)
|
| 516 |
-
|
| 517 |
-
compression_retriever = ContextualCompressionRetriever(
|
| 518 |
-
base_compressor=compressor,
|
| 519 |
-
base_retriever=cross_ref_retriever
|
| 520 |
-
)
|
| 521 |
-
print("✅ Reranker model ready")
|
| 522 |
-
|
| 523 |
-
# 7. LLM - Balanced for consistency with slight creativity
|
| 524 |
-
# 7. LLM Configuration
|
| 525 |
-
llm = ChatGroq(
|
| 526 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 527 |
-
model_name="llama-3.1-8b-instant",
|
| 528 |
-
temperature=0.3, # Slightly increased to allow helpful general advice
|
| 529 |
-
model_kwargs={"top_p": 0.9}
|
| 530 |
-
)
|
| 531 |
-
|
| 532 |
-
# ==================================================
|
| 533 |
-
# 🛠️ THE FIX: SEPARATE SYSTEM INSTRUCTIONS FROM USER INPUT
|
| 534 |
-
# ==================================================
|
| 535 |
-
|
| 536 |
-
# ==================================================
|
| 537 |
-
# 🧠 PROMPT ENGINEERING: DECISION TREE LOGIC
|
| 538 |
-
# ==================================================
|
| 539 |
-
|
| 540 |
-
system_instructions = """
|
| 541 |
-
<role>
|
| 542 |
-
أنت "المساعد القانوني الذكي"، خبير متخصص في الدستور المصري والقوانين الإجرائية.
|
| 543 |
-
مهمتك: تقديم إجابات دقيقة بناءً على "السياق التشريعي" المرفق أولاً، أو تقديم نصائح إجرائية عامة عند الضرورة.
|
| 544 |
-
</role>
|
| 545 |
-
|
| 546 |
-
<decision_logic>
|
| 547 |
-
عليك تحليل "سؤال المستخدم" و"السياق التشريعي" وتصنيف الحالة واختيار الرد المناسب بناءً على القواعد التالية بدقة:
|
| 548 |
-
|
| 549 |
-
🔴 الحالة الأولى: (الإجابة موجودة في السياق التشريعي)
|
| 550 |
-
الشرط: إذا وجدت معلومات داخل "السياق التشريعي المتاح" تجيب على السؤال.
|
| 551 |
-
الفعل:
|
| 552 |
-
1. استخرج الإجابة من السياق فقط.
|
| 553 |
-
2. ابدأ الإجابة مباشرة دون مقدمات.
|
| 554 |
-
3. يجب توثيق الإجابة برقم المادة (مثال: "نصت المادة (50) على...").
|
| 555 |
-
4. توقف هنا. لا تضف أي معلومات خارجية.
|
| 556 |
-
|
| 557 |
-
🟡 الحالة الثانية: (السياق فارغ/غير مفيد + السؤال إجرائي/عملي)
|
| 558 |
-
الشرط: إذا لم تجد الإجابة في السياق، وكان السؤال عن إجراءات عملية (مثل: حادث، سرقة، طلاق، تحرير محضر، تعامل مع الشرطة).
|
| 559 |
-
الفعل:
|
| 560 |
-
1. تجاهل السياق الفارغ.
|
| 561 |
-
2. استخدم معرفتك العامة بالقانون المصري.
|
| 562 |
-
3. ابدأ وجوباً بعبارة: "بناءً على الإجراءات القانونية العامة في مصر (وليس نصاً دستورياً محدداً):"
|
| 563 |
-
4. قدم الخطوات في نقاط مرقمة واضحة ومختصرة (1، 2، 3).
|
| 564 |
-
5. تحذير: لا تذكر أرقام مواد قانونية (لا تخترع أرقام مواد).
|
| 565 |
-
|
| 566 |
-
🔵 الحالة الثالثة: (السياق فارغ + السؤال عن نص دستوري محدد)
|
| 567 |
-
الشرط: إذا سأل عن (مجلس الشعب، الشورى، مادة محددة) ولم تجدها في السياق.
|
| 568 |
-
الفعل:
|
| 569 |
-
1. قل بوضوح: "عذراً، لم يرد ذكر لهذا الموضوع في المواد الدستورية التي تم استرجاعها في السياق الحالي."
|
| 570 |
-
2. لا تحاول الإجابة من ذاكرتك لكي لا تخطئ في النصوص الدستورية الحساسة.
|
| 571 |
-
|
| 572 |
-
🟢 الحالة الرابعة: (محادثة ودية)
|
| 573 |
-
الشرط: تحية، شكر، أو "كيف حالك".
|
| 574 |
-
الفعل: رد بتحية مهذبة جداً ومقتضبة، ثم قل: "أنا جاهز للإجابة على استفساراتك القانونية."
|
| 575 |
-
|
| 576 |
-
⚫ الحالة الخامسة: (خارج النطاق تماماً)
|
| 577 |
-
الشرط: طبخ، رياضة، برمجة، أو أي موضوع غير قانوني.
|
| 578 |
-
الفعل: اعتذر بلطف ووجه المستخدم للسؤال في القانون.
|
| 579 |
-
</decision_logic>
|
| 580 |
-
|
| 581 |
-
<formatting_rules>
|
| 582 |
-
- لا تكرر هذه التعليمات في ردك.
|
| 583 |
-
- استخدم فقرات قصيرة واترك سطراً فارغاً بينها.
|
| 584 |
-
- لا تستخدم عبارات مثل "بناء على السياق المرفق" في بداية الجملة، بل ادخل في صلب الموضوع فوراً.
|
| 585 |
-
- التزم باللغة العربية الفصحى المبسطة والرصينة.
|
| 586 |
-
</formatting_rules>
|
| 587 |
-
"""
|
| 588 |
-
|
| 589 |
-
# We use .from_messages to strictly separate instructions from data
|
| 590 |
-
prompt = ChatPromptTemplate.from_messages([
|
| 591 |
-
("system", system_instructions),
|
| 592 |
-
("system", "السياق التشريعي المتاح (المصدر الأساسي):\n{context}"),
|
| 593 |
-
("human", "سؤال المستفيد:\n{input}")
|
| 594 |
-
])
|
| 595 |
-
|
| 596 |
-
# 9. Build Chain with RunnableParallel (returns both context and answer)
|
| 597 |
-
qa_chain = (
|
| 598 |
-
RunnableParallel({
|
| 599 |
-
"context": compression_retriever,
|
| 600 |
-
"input": RunnablePassthrough()
|
| 601 |
-
})
|
| 602 |
-
.assign(answer=(
|
| 603 |
-
prompt
|
| 604 |
-
| llm
|
| 605 |
-
| StrOutputParser()
|
| 606 |
-
))
|
| 607 |
-
)
|
| 608 |
-
|
| 609 |
-
print("✅ System ready to use!")
|
| 610 |
-
return qa_chain
|
| 611 |
-
|
| 612 |
-
if not IS_CLI:
|
| 613 |
-
# ==========================================
|
| 614 |
-
# ⚡ MAIN EXECUTION
|
| 615 |
-
# ==========================================
|
| 616 |
-
|
| 617 |
-
try:
|
| 618 |
-
# Only need the chain now - it handles all retrieval internally
|
| 619 |
-
qa_chain = initialize_rag_pipeline()
|
| 620 |
-
|
| 621 |
-
except Exception as e:
|
| 622 |
-
st.error(f"Critical Error loading application: {e}")
|
| 623 |
-
st.stop()
|
| 624 |
-
|
| 625 |
-
# ==========================================
|
| 626 |
-
# 💬 CHAT LOOP
|
| 627 |
-
# ==========================================
|
| 628 |
-
if "messages" not in st.session_state:
|
| 629 |
-
st.session_state.messages = []
|
| 630 |
-
|
| 631 |
-
# Display Chat History (with Eastern Arabic numerals)
|
| 632 |
-
for message in st.session_state.messages:
|
| 633 |
-
with st.chat_message(message["role"]):
|
| 634 |
-
# Convert to Eastern Arabic when displaying from history
|
| 635 |
-
st.markdown(convert_to_eastern_arabic(message["content"]))
|
| 636 |
-
|
| 637 |
-
# Handle New User Input
|
| 638 |
-
if prompt_input := st.chat_input("اكتب سؤالك القانوني هنا..."):
|
| 639 |
-
# Show user message
|
| 640 |
-
st.session_state.messages.append({"role": "user", "content": prompt_input})
|
| 641 |
-
with st.chat_message("user"):
|
| 642 |
-
st.markdown(prompt_input)
|
| 643 |
-
|
| 644 |
-
# Generate Response
|
| 645 |
-
with st.chat_message("assistant"):
|
| 646 |
-
with st.spinner("جاري التحليل القانوني..."):
|
| 647 |
-
try:
|
| 648 |
-
# Invoke chain ONCE - returns Dict with 'context', 'input', and 'answer'
|
| 649 |
-
result = qa_chain.invoke(prompt_input)
|
| 650 |
-
|
| 651 |
-
# Extract answer and context from result
|
| 652 |
-
response_text = result["answer"]
|
| 653 |
-
source_docs = result["context"] # Context is already in the result!
|
| 654 |
-
|
| 655 |
-
# Display Answer
|
| 656 |
-
response_text_arabic = convert_to_eastern_arabic(response_text)
|
| 657 |
-
st.markdown(response_text_arabic)
|
| 658 |
-
|
| 659 |
-
# Display Sources
|
| 660 |
-
if source_docs and len(source_docs) > 0:
|
| 661 |
-
print(f"✅ Found {len(source_docs)} documents")
|
| 662 |
-
# Deduplicate documents by article_number
|
| 663 |
-
seen_articles = set()
|
| 664 |
-
unique_docs = []
|
| 665 |
-
|
| 666 |
-
for doc in source_docs:
|
| 667 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 668 |
-
if article_num and article_num not in seen_articles:
|
| 669 |
-
seen_articles.add(article_num)
|
| 670 |
-
unique_docs.append(doc)
|
| 671 |
-
|
| 672 |
-
st.markdown("---") # Separator before sources
|
| 673 |
-
|
| 674 |
-
if unique_docs:
|
| 675 |
-
with st.expander(f"📚 المصادر المستخدمة ({len(unique_docs)} مادة)"):
|
| 676 |
-
st.markdown("### المواد الدستورية المستخدمة في التحليل:")
|
| 677 |
-
st.markdown("---")
|
| 678 |
-
|
| 679 |
-
for idx, doc in enumerate(unique_docs, 1):
|
| 680 |
-
article_num = str(doc.metadata.get('article_number', '')).strip()
|
| 681 |
-
legal_nature = doc.metadata.get('legal_nature', '')
|
| 682 |
-
|
| 683 |
-
if article_num:
|
| 684 |
-
st.markdown(f"**المادة رقم {convert_to_eastern_arabic(article_num)}**")
|
| 685 |
-
if legal_nature:
|
| 686 |
-
st.markdown(f"*الطبيعة القانونية: {legal_nature}*")
|
| 687 |
-
|
| 688 |
-
# Display article content
|
| 689 |
-
content_lines = doc.page_content.strip().split('\n')
|
| 690 |
-
for line in content_lines:
|
| 691 |
-
line = line.strip()
|
| 692 |
-
if line:
|
| 693 |
-
st.markdown(convert_to_eastern_arabic(line))
|
| 694 |
-
|
| 695 |
-
st.markdown("---")
|
| 696 |
-
else:
|
| 697 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 698 |
-
else:
|
| 699 |
-
st.info("📌 لم يتم العثور على مصادر")
|
| 700 |
-
|
| 701 |
-
# Persist the raw answer to avoid double conversion glitches on rerun
|
| 702 |
-
st.session_state.messages.append({"role": "assistant", "content": response_text})
|
| 703 |
-
except Exception as e:
|
| 704 |
-
st.error(f"حدث خطأ: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/Egyptian_Civil.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/Egyptian_Constitution_legalnature_only.json
CHANGED
|
@@ -1,8 +1,4 @@
|
|
| 1 |
[
|
| 2 |
-
{
|
| 3 |
-
"law_key": "egyptian_constitution",
|
| 4 |
-
"law_name": "الدستور المصري",
|
| 5 |
-
"data": [
|
| 6 |
{
|
| 7 |
"article_id": "EG-CONST-ART-001",
|
| 8 |
"article_number": "1",
|
|
@@ -16,7 +12,6 @@
|
|
| 16 |
"الديمقراطية",
|
| 17 |
"جمهورية مصر العربية"
|
| 18 |
],
|
| 19 |
-
"cross_references": [],
|
| 20 |
"legal_nature": "مبدأ دستوري"
|
| 21 |
},
|
| 22 |
{
|
|
@@ -32,7 +27,6 @@
|
|
| 32 |
"الشريعة الإسلامية",
|
| 33 |
"التشريع"
|
| 34 |
],
|
| 35 |
-
"cross_references": [],
|
| 36 |
"legal_nature": "مبدأ دستوري"
|
| 37 |
},
|
| 38 |
{
|
|
@@ -48,7 +42,6 @@
|
|
| 48 |
"اليهود",
|
| 49 |
"الشئون الدينية"
|
| 50 |
],
|
| 51 |
-
"cross_references": [],
|
| 52 |
"legal_nature": "مبدأ دستوري"
|
| 53 |
},
|
| 54 |
{
|
|
@@ -65,7 +58,6 @@
|
|
| 65 |
"المساواة",
|
| 66 |
"تكافؤ الفرص"
|
| 67 |
],
|
| 68 |
-
"cross_references": [],
|
| 69 |
"legal_nature": "مبدأ دستوري"
|
| 70 |
},
|
| 71 |
{
|
|
@@ -81,7 +73,6 @@
|
|
| 81 |
"الفصل بين السلطات",
|
| 82 |
"حقوق الإنسان"
|
| 83 |
],
|
| 84 |
-
"cross_references": [],
|
| 85 |
"legal_nature": "مبدأ دستوري"
|
| 86 |
},
|
| 87 |
{
|
|
@@ -97,7 +88,6 @@
|
|
| 97 |
"القانون",
|
| 98 |
"اكتساب الجنسية"
|
| 99 |
],
|
| 100 |
-
"cross_references": [],
|
| 101 |
"legal_nature": "حق أساسي/حرية"
|
| 102 |
},
|
| 103 |
{
|
|
@@ -113,7 +103,6 @@
|
|
| 113 |
"اللغة العربية",
|
| 114 |
"شيخ الأزهر"
|
| 115 |
],
|
| 116 |
-
"cross_references": [],
|
| 117 |
"legal_nature": "مبدأ دستوري"
|
| 118 |
},
|
| 119 |
{
|
|
@@ -129,7 +118,6 @@
|
|
| 129 |
"التكافل الاجتماعي",
|
| 130 |
"الحياة الكريمة"
|
| 131 |
],
|
| 132 |
-
"cross_references": [],
|
| 133 |
"legal_nature": "مبدأ دستوري"
|
| 134 |
},
|
| 135 |
{
|
|
@@ -144,7 +132,6 @@
|
|
| 144 |
"عدم التمييز",
|
| 145 |
"المواطنة"
|
| 146 |
],
|
| 147 |
-
"cross_references": [],
|
| 148 |
"legal_nature": "مبدأ دستوري"
|
| 149 |
},
|
| 150 |
{
|
|
@@ -161,7 +148,6 @@
|
|
| 161 |
"الأخلاق",
|
| 162 |
"الوطنية"
|
| 163 |
],
|
| 164 |
-
"cross_references": [],
|
| 165 |
"legal_nature": "مبدأ دستوري"
|
| 166 |
},
|
| 167 |
{
|
|
@@ -178,7 +164,6 @@
|
|
| 178 |
"المجالس النيابية",
|
| 179 |
"حماية الأمومة والطفولة"
|
| 180 |
],
|
| 181 |
-
"cross_references": [],
|
| 182 |
"legal_nature": "حق أساسي/حرية"
|
| 183 |
},
|
| 184 |
{
|
|
@@ -194,7 +179,6 @@
|
|
| 194 |
"العمل الجبري",
|
| 195 |
"الخدمة العامة"
|
| 196 |
],
|
| 197 |
-
"cross_references": [],
|
| 198 |
"legal_nature": "حق أساسي/حرية"
|
| 199 |
},
|
| 200 |
{
|
|
@@ -211,7 +195,6 @@
|
|
| 211 |
"السلامة المهنية",
|
| 212 |
"الفصل التعسفي"
|
| 213 |
],
|
| 214 |
-
"cross_references": [],
|
| 215 |
"legal_nature": "حق أساسي/حرية"
|
| 216 |
},
|
| 217 |
{
|
|
@@ -227,7 +210,6 @@
|
|
| 227 |
"حقوق الموظفين",
|
| 228 |
"التأديب"
|
| 229 |
],
|
| 230 |
-
"cross_references": [],
|
| 231 |
"legal_nature": "حق أساسي/حرية"
|
| 232 |
},
|
| 233 |
{
|
|
@@ -241,7 +223,6 @@
|
|
| 241 |
"الإضراب السلمي",
|
| 242 |
"القانون"
|
| 243 |
],
|
| 244 |
-
"cross_references": [],
|
| 245 |
"legal_nature": "حق أساسي/حرية"
|
| 246 |
},
|
| 247 |
{
|
|
@@ -258,7 +239,6 @@
|
|
| 258 |
"رعاية الأسر",
|
| 259 |
"المجتمع المدني"
|
| 260 |
],
|
| 261 |
-
"cross_references": [],
|
| 262 |
"legal_nature": "التزام/واجب على الدولة"
|
| 263 |
},
|
| 264 |
{
|
|
@@ -275,7 +255,6 @@
|
|
| 275 |
"العمالة غير المنتظمة",
|
| 276 |
"أموال التأمينات"
|
| 277 |
],
|
| 278 |
-
"cross_references": [],
|
| 279 |
"legal_nature": "حق أساسي/حرية"
|
| 280 |
},
|
| 281 |
{
|
|
@@ -292,7 +271,6 @@
|
|
| 292 |
"الطوارئ",
|
| 293 |
"القطاع الصحي"
|
| 294 |
],
|
| 295 |
-
"cross_references": [],
|
| 296 |
"legal_nature": "حق أساسي/حرية"
|
| 297 |
},
|
| 298 |
{
|
|
@@ -309,7 +287,6 @@
|
|
| 309 |
"الهوية الوطنية",
|
| 310 |
"جودة التعليم"
|
| 311 |
],
|
| 312 |
-
"cross_references": [],
|
| 313 |
"legal_nature": "حق أساسي/حرية"
|
| 314 |
},
|
| 315 |
{
|
|
@@ -325,7 +302,6 @@
|
|
| 325 |
"سوق العمل",
|
| 326 |
"الجودة العالمية"
|
| 327 |
],
|
| 328 |
-
"cross_references": [],
|
| 329 |
"legal_nature": "التزام/واجب على الدولة"
|
| 330 |
},
|
| 331 |
{
|
|
@@ -342,7 +318,6 @@
|
|
| 342 |
"الجامعات الأهلية",
|
| 343 |
"الجامعات الخاصة"
|
| 344 |
],
|
| 345 |
-
"cross_references": [],
|
| 346 |
"legal_nature": "حق أساسي/حرية"
|
| 347 |
},
|
| 348 |
{
|
|
@@ -358,7 +333,6 @@
|
|
| 358 |
"حقوق المعلمين",
|
| 359 |
"جودة التعليم"
|
| 360 |
],
|
| 361 |
-
"cross_references": [],
|
| 362 |
"legal_nature": "التزام/واجب على الدولة"
|
| 363 |
},
|
| 364 |
{
|
|
@@ -375,7 +349,6 @@
|
|
| 375 |
"اقتصاد المعرفة",
|
| 376 |
"المخترعين"
|
| 377 |
],
|
| 378 |
-
"cross_references": [],
|
| 379 |
"legal_nature": "حق أساسي/حرية"
|
| 380 |
},
|
| 381 |
{
|
|
@@ -392,7 +365,6 @@
|
|
| 392 |
"حقوق الإنسان",
|
| 393 |
"الأخلاق المهنية"
|
| 394 |
],
|
| 395 |
-
"cross_references": [],
|
| 396 |
"legal_nature": "مبدأ دستوري"
|
| 397 |
},
|
| 398 |
{
|
|
@@ -408,7 +380,6 @@
|
|
| 408 |
"المجتمع المدني",
|
| 409 |
"خطة شاملة"
|
| 410 |
],
|
| 411 |
-
"cross_references": [],
|
| 412 |
"legal_nature": "التزام/واجب على الدولة"
|
| 413 |
},
|
| 414 |
{
|
|
@@ -422,7 +393,6 @@
|
|
| 422 |
"الرتب المدنية",
|
| 423 |
"حظر"
|
| 424 |
],
|
| 425 |
-
"cross_references": [],
|
| 426 |
"legal_nature": "حظر دستوري"
|
| 427 |
},
|
| 428 |
{
|
|
@@ -439,7 +409,6 @@
|
|
| 439 |
"الشفافية",
|
| 440 |
"منع الاحتكار"
|
| 441 |
],
|
| 442 |
-
"cross_references": [],
|
| 443 |
"legal_nature": "حق أساسي/حرية"
|
| 444 |
},
|
| 445 |
{
|
|
@@ -456,7 +425,6 @@
|
|
| 456 |
"المشروعات الصغيرة",
|
| 457 |
"القطاع غير الرسمي"
|
| 458 |
],
|
| 459 |
-
"cross_references": [],
|
| 460 |
"legal_nature": "التزام/واجب على الدولة"
|
| 461 |
},
|
| 462 |
{
|
|
@@ -473,7 +441,6 @@
|
|
| 473 |
"دعم الفلاح",
|
| 474 |
"الإنتاج الزراعي"
|
| 475 |
],
|
| 476 |
-
"cross_references": [],
|
| 477 |
"legal_nature": "التزام/واجب على الدولة"
|
| 478 |
},
|
| 479 |
{
|
|
@@ -488,7 +455,6 @@
|
|
| 488 |
"حماية الصيادين",
|
| 489 |
"البيئة"
|
| 490 |
],
|
| 491 |
-
"cross_references": [],
|
| 492 |
"legal_nature": "التزام/واجب على الدولة"
|
| 493 |
},
|
| 494 |
{
|
|
@@ -503,7 +469,6 @@
|
|
| 503 |
"الأمن القومي",
|
| 504 |
"الاقتصاد الرقمي"
|
| 505 |
],
|
| 506 |
-
"cross_references": [],
|
| 507 |
"legal_nature": "التزام/واجب على الدولة"
|
| 508 |
},
|
| 509 |
{
|
|
@@ -520,7 +485,6 @@
|
|
| 520 |
"الاستثمار",
|
| 521 |
"حقوق الأجيال القادمة"
|
| 522 |
],
|
| 523 |
-
"cross_references": [],
|
| 524 |
"legal_nature": "مبدأ دستوري"
|
| 525 |
},
|
| 526 |
{
|
|
@@ -536,7 +500,6 @@
|
|
| 536 |
"الملكية التعاونية",
|
| 537 |
"حماية الملكية"
|
| 538 |
],
|
| 539 |
-
"cross_references": [],
|
| 540 |
"legal_nature": "مبدأ دستوري"
|
| 541 |
},
|
| 542 |
{
|
|
@@ -550,7 +513,6 @@
|
|
| 550 |
"الملكية العامة",
|
| 551 |
"حرمة المال العام"
|
| 552 |
],
|
| 553 |
-
"cross_references": [],
|
| 554 |
"legal_nature": "التزام/واجب على الدولة"
|
| 555 |
},
|
| 556 |
{
|
|
@@ -566,7 +528,6 @@
|
|
| 566 |
"نزع الملكية",
|
| 567 |
"التعويض العادل"
|
| 568 |
],
|
| 569 |
-
"cross_references": [],
|
| 570 |
"legal_nature": "حق أساسي/حرية"
|
| 571 |
},
|
| 572 |
{
|
|
@@ -580,7 +541,6 @@
|
|
| 580 |
"القطاع الخاص",
|
| 581 |
"المسؤولية الاجتماعية"
|
| 582 |
],
|
| 583 |
-
"cross_references": [],
|
| 584 |
"legal_nature": "التزام/واجب على الدولة"
|
| 585 |
},
|
| 586 |
{
|
|
@@ -595,7 +555,6 @@
|
|
| 595 |
"التعاونيات",
|
| 596 |
"استقلال التعاونيات"
|
| 597 |
],
|
| 598 |
-
"cross_references": [],
|
| 599 |
"legal_nature": "حق أساسي/حرية"
|
| 600 |
},
|
| 601 |
{
|
|
@@ -611,7 +570,6 @@
|
|
| 611 |
"العدالة الاجتماعية",
|
| 612 |
"التهرب الضريبي"
|
| 613 |
],
|
| 614 |
-
"cross_references": [],
|
| 615 |
"legal_nature": "التزام/واجب على الدولة"
|
| 616 |
},
|
| 617 |
{
|
|
@@ -625,7 +583,6 @@
|
|
| 625 |
"الادخار",
|
| 626 |
"حماية المدخرات"
|
| 627 |
],
|
| 628 |
-
"cross_references": [],
|
| 629 |
"legal_nature": "التزام/واجب على الدولة"
|
| 630 |
},
|
| 631 |
{
|
|
@@ -640,7 +597,6 @@
|
|
| 640 |
"المصادرة الخاصة",
|
| 641 |
"حماية الأموال"
|
| 642 |
],
|
| 643 |
-
"cross_references": [],
|
| 644 |
"legal_nature": "حظر دستوري"
|
| 645 |
},
|
| 646 |
{
|
|
@@ -656,7 +612,6 @@
|
|
| 656 |
"التنمية المستدامة",
|
| 657 |
"الموارد البشرية"
|
| 658 |
],
|
| 659 |
-
"cross_references": [],
|
| 660 |
"legal_nature": "التزام/واجب على الدولة"
|
| 661 |
},
|
| 662 |
{
|
|
@@ -673,7 +628,6 @@
|
|
| 673 |
"القطاع العام",
|
| 674 |
"التعاونيات"
|
| 675 |
],
|
| 676 |
-
"cross_references": [],
|
| 677 |
"legal_nature": "حقوق وواجبات"
|
| 678 |
},
|
| 679 |
{
|
|
@@ -688,7 +642,6 @@
|
|
| 688 |
"الممر المائي",
|
| 689 |
"التنمية الاقتصادية"
|
| 690 |
],
|
| 691 |
-
"cross_references": [],
|
| 692 |
"legal_nature": "التزام/واجب على الدولة"
|
| 693 |
},
|
| 694 |
{
|
|
@@ -704,7 +657,6 @@
|
|
| 704 |
"المياه الجوفية",
|
| 705 |
"حماية البيئة النهرية"
|
| 706 |
],
|
| 707 |
-
"cross_references": [],
|
| 708 |
"legal_nature": "حقوق وواجبات"
|
| 709 |
},
|
| 710 |
{
|
|
@@ -720,7 +672,6 @@
|
|
| 720 |
"الثروة السمكية والحيوانية",
|
| 721 |
"الرفق بالحيوان"
|
| 722 |
],
|
| 723 |
-
"cross_references": [],
|
| 724 |
"legal_nature": "التزام/واجب على الدولة"
|
| 725 |
},
|
| 726 |
{
|
|
@@ -736,7 +687,6 @@
|
|
| 736 |
"حقوق الأجيال القادمة",
|
| 737 |
"الموارد الطبيعية"
|
| 738 |
],
|
| 739 |
-
"cross_references": [],
|
| 740 |
"legal_nature": "حقوق وواجبات"
|
| 741 |
},
|
| 742 |
{
|
|
@@ -751,7 +701,6 @@
|
|
| 751 |
"الحضارة المصرية",
|
| 752 |
"التنوع الثقافي"
|
| 753 |
],
|
| 754 |
-
"cross_references": [],
|
| 755 |
"legal_nature": "التزام/واجب على الدولة"
|
| 756 |
},
|
| 757 |
{
|
|
@@ -767,7 +716,6 @@
|
|
| 767 |
"المناطق النائية",
|
| 768 |
"الترجمة"
|
| 769 |
],
|
| 770 |
-
"cross_references": [],
|
| 771 |
"legal_nature": "حق أساسي/حرية"
|
| 772 |
},
|
| 773 |
{
|
|
@@ -783,7 +731,6 @@
|
|
| 783 |
"حظر الاتجار",
|
| 784 |
"جريمة لا تسقط بالتقادم"
|
| 785 |
],
|
| 786 |
-
"cross_references": [],
|
| 787 |
"legal_nature": "التزام/واجب على الدولة"
|
| 788 |
},
|
| 789 |
{
|
|
@@ -800,7 +747,6 @@
|
|
| 800 |
"التعددية الثقافية",
|
| 801 |
"الحضارة المصرية القديمة"
|
| 802 |
],
|
| 803 |
-
"cross_references": [],
|
| 804 |
"legal_nature": "التزام/واجب على الدولة"
|
| 805 |
},
|
| 806 |
{
|
|
@@ -815,7 +761,6 @@
|
|
| 815 |
"حقوق الإنسان",
|
| 816 |
"احترام الدولة"
|
| 817 |
],
|
| 818 |
-
"cross_references": [],
|
| 819 |
"legal_nature": "حق أساسي/حرية"
|
| 820 |
},
|
| 821 |
{
|
|
@@ -830,7 +775,6 @@
|
|
| 830 |
"جريمة",
|
| 831 |
"التقادم"
|
| 832 |
],
|
| 833 |
-
"cross_references": [],
|
| 834 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 835 |
},
|
| 836 |
{
|
|
@@ -846,7 +790,6 @@
|
|
| 846 |
"مفوضية منع التمييز",
|
| 847 |
"الحض على الكراهية"
|
| 848 |
],
|
| 849 |
-
"cross_references": [],
|
| 850 |
"legal_nature": "حق أساسي/حرية"
|
| 851 |
},
|
| 852 |
{
|
|
@@ -863,7 +806,6 @@
|
|
| 863 |
"حق الدفاع",
|
| 864 |
"التعويض"
|
| 865 |
],
|
| 866 |
-
"cross_references": [],
|
| 867 |
"legal_nature": "حق أساسي/حرية"
|
| 868 |
},
|
| 869 |
{
|
|
@@ -879,7 +821,6 @@
|
|
| 879 |
"بطلان الاعتراف",
|
| 880 |
"حظر التعذيب"
|
| 881 |
],
|
| 882 |
-
"cross_references": [],
|
| 883 |
"legal_nature": "حق أساسي/حرية"
|
| 884 |
},
|
| 885 |
{
|
|
@@ -895,7 +836,6 @@
|
|
| 895 |
"الإشراف القضائي",
|
| 896 |
"الرعاية اللاحقة"
|
| 897 |
],
|
| 898 |
-
"cross_references": [],
|
| 899 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 900 |
},
|
| 901 |
{
|
|
@@ -911,7 +851,6 @@
|
|
| 911 |
"المراقبة",
|
| 912 |
"وسائل الاتصال"
|
| 913 |
],
|
| 914 |
-
"cross_references": [],
|
| 915 |
"legal_nature": "حق أساسي/حرية"
|
| 916 |
},
|
| 917 |
{
|
|
@@ -926,7 +865,6 @@
|
|
| 926 |
"تفتيش المنازل",
|
| 927 |
"إذن قضائي"
|
| 928 |
],
|
| 929 |
-
"cross_references": [],
|
| 930 |
"legal_nature": "حق أساسي/حرية"
|
| 931 |
},
|
| 932 |
{
|
|
@@ -941,7 +879,6 @@
|
|
| 941 |
"الأمن",
|
| 942 |
"الطمأنينة"
|
| 943 |
],
|
| 944 |
-
"cross_references": [],
|
| 945 |
"legal_nature": "حق أساسي/حرية"
|
| 946 |
},
|
| 947 |
{
|
|
@@ -956,7 +893,6 @@
|
|
| 956 |
"الاتجار بالأعضاء",
|
| 957 |
"التجارب الطبية"
|
| 958 |
],
|
| 959 |
-
"cross_references": [],
|
| 960 |
"legal_nature": "قاعدة إجرائية"
|
| 961 |
},
|
| 962 |
{
|
|
@@ -971,7 +907,6 @@
|
|
| 971 |
"زراعة الأعضاء",
|
| 972 |
"الوصية"
|
| 973 |
],
|
| 974 |
-
"cross_references": [],
|
| 975 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 976 |
},
|
| 977 |
{
|
|
@@ -987,7 +922,6 @@
|
|
| 987 |
"المنع من السفر",
|
| 988 |
"الإبعاد"
|
| 989 |
],
|
| 990 |
-
"cross_references": [],
|
| 991 |
"legal_nature": "حق أساسي/حرية"
|
| 992 |
},
|
| 993 |
{
|
|
@@ -1002,7 +936,6 @@
|
|
| 1002 |
"جريمة",
|
| 1003 |
"التقادم"
|
| 1004 |
],
|
| 1005 |
-
"cross_references": [],
|
| 1006 |
"legal_nature": "حظر وتجريم"
|
| 1007 |
},
|
| 1008 |
{
|
|
@@ -1018,7 +951,6 @@
|
|
| 1018 |
"دور العبادة",
|
| 1019 |
"الأديان السماوية"
|
| 1020 |
],
|
| 1021 |
-
"cross_references": [],
|
| 1022 |
"legal_nature": "حق أساسي/حرية"
|
| 1023 |
},
|
| 1024 |
{
|
|
@@ -1034,7 +966,6 @@
|
|
| 1034 |
"حرية التعبير",
|
| 1035 |
"النشر"
|
| 1036 |
],
|
| 1037 |
-
"cross_references": [],
|
| 1038 |
"legal_nature": "حق أساسي/حرية"
|
| 1039 |
},
|
| 1040 |
{
|
|
@@ -1049,7 +980,6 @@
|
|
| 1049 |
"الباحثين",
|
| 1050 |
"الابتكار"
|
| 1051 |
],
|
| 1052 |
-
"cross_references": [],
|
| 1053 |
"legal_nature": "حق أساسي/حرية"
|
| 1054 |
},
|
| 1055 |
{
|
|
@@ -1065,7 +995,6 @@
|
|
| 1065 |
"حظر الحبس في جرائم النشر",
|
| 1066 |
"النيابة العامة"
|
| 1067 |
],
|
| 1068 |
-
"cross_references": [],
|
| 1069 |
"legal_nature": "حق أساسي/حرية"
|
| 1070 |
},
|
| 1071 |
{
|
|
@@ -1081,7 +1010,6 @@
|
|
| 1081 |
"الوثائق القومية",
|
| 1082 |
"البيانات"
|
| 1083 |
],
|
| 1084 |
-
"cross_references": [],
|
| 1085 |
"legal_nature": "حق أساسي/حرية"
|
| 1086 |
},
|
| 1087 |
{
|
|
@@ -1095,7 +1023,6 @@
|
|
| 1095 |
"الملكية الفكرية",
|
| 1096 |
"حماية الحقوق"
|
| 1097 |
],
|
| 1098 |
-
"cross_references": [],
|
| 1099 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1100 |
},
|
| 1101 |
{
|
|
@@ -1111,7 +1038,6 @@
|
|
| 1111 |
"إصدار الصحف",
|
| 1112 |
"البث الإذاعي"
|
| 1113 |
],
|
| 1114 |
-
"cross_references": [],
|
| 1115 |
"legal_nature": "حق أساسي/حرية"
|
| 1116 |
},
|
| 1117 |
{
|
|
@@ -1127,7 +1053,6 @@
|
|
| 1127 |
"جرائم النشر",
|
| 1128 |
"وقت الحرب"
|
| 1129 |
],
|
| 1130 |
-
"cross_references": [],
|
| 1131 |
"legal_nature": "حظر دستوري"
|
| 1132 |
},
|
| 1133 |
{
|
|
@@ -1143,7 +1068,6 @@
|
|
| 1143 |
"الحياد",
|
| 1144 |
"تعدد الآراء"
|
| 1145 |
],
|
| 1146 |
-
"cross_references": [],
|
| 1147 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1148 |
},
|
| 1149 |
{
|
|
@@ -1159,7 +1083,6 @@
|
|
| 1159 |
"الاحتجاج السلمي",
|
| 1160 |
"الاجتماع الخاص"
|
| 1161 |
],
|
| 1162 |
-
"cross_references": [],
|
| 1163 |
"legal_nature": "حق أساسي/حرية"
|
| 1164 |
},
|
| 1165 |
{
|
|
@@ -1174,7 +1097,6 @@
|
|
| 1174 |
"حظر الأحزاب الدينية",
|
| 1175 |
"حل الأحزاب"
|
| 1176 |
],
|
| 1177 |
-
"cross_references": [],
|
| 1178 |
"legal_nature": "حق أساسي/حرية"
|
| 1179 |
},
|
| 1180 |
{
|
|
@@ -1190,7 +1112,6 @@
|
|
| 1190 |
"حرية النشاط",
|
| 1191 |
"حظر الجمعيات السرية"
|
| 1192 |
],
|
| 1193 |
-
"cross_references": [],
|
| 1194 |
"legal_nature": "حق أساسي/حرية"
|
| 1195 |
},
|
| 1196 |
{
|
|
@@ -1206,7 +1127,6 @@
|
|
| 1206 |
"استقلال النقابات",
|
| 1207 |
"الهيئات النظامية"
|
| 1208 |
],
|
| 1209 |
-
"cross_references": [],
|
| 1210 |
"legal_nature": "حق أساسي/حرية"
|
| 1211 |
},
|
| 1212 |
{
|
|
@@ -1221,7 +1141,6 @@
|
|
| 1221 |
"فرض الحراسة",
|
| 1222 |
"استقلال مهني"
|
| 1223 |
],
|
| 1224 |
-
"cross_references": [],
|
| 1225 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1226 |
},
|
| 1227 |
{
|
|
@@ -1237,7 +1156,6 @@
|
|
| 1237 |
"التخطيط العمراني",
|
| 1238 |
"العدالة الاجتماعية"
|
| 1239 |
],
|
| 1240 |
-
"cross_references": [],
|
| 1241 |
"legal_nature": "حقوق وواجبات"
|
| 1242 |
},
|
| 1243 |
{
|
|
@@ -1253,7 +1171,6 @@
|
|
| 1253 |
"السيادة الغذائية",
|
| 1254 |
"التنوع البيولوجي"
|
| 1255 |
],
|
| 1256 |
-
"cross_references": [],
|
| 1257 |
"legal_nature": "حق أساسي/حرية"
|
| 1258 |
},
|
| 1259 |
{
|
|
@@ -1270,7 +1187,6 @@
|
|
| 1270 |
"عمالة الأطفال",
|
| 1271 |
"قضاء الأحداث"
|
| 1272 |
],
|
| 1273 |
-
"cross_references": [],
|
| 1274 |
"legal_nature": "حق أساسي/حرية"
|
| 1275 |
},
|
| 1276 |
{
|
|
@@ -1286,7 +1202,6 @@
|
|
| 1286 |
"الدمج",
|
| 1287 |
"تخصيص فرص عمل"
|
| 1288 |
],
|
| 1289 |
-
"cross_references": [],
|
| 1290 |
"legal_nature": "حق أساسي/حرية"
|
| 1291 |
},
|
| 1292 |
{
|
|
@@ -1302,7 +1217,6 @@
|
|
| 1302 |
"التمكين",
|
| 1303 |
"العمل التطوعي"
|
| 1304 |
],
|
| 1305 |
-
"cross_references": [],
|
| 1306 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1307 |
},
|
| 1308 |
{
|
|
@@ -1317,7 +1231,6 @@
|
|
| 1317 |
"المعاشات",
|
| 1318 |
"رعاية المسنين"
|
| 1319 |
],
|
| 1320 |
-
"cross_references": [],
|
| 1321 |
"legal_nature": "حق أساسي/حرية"
|
| 1322 |
},
|
| 1323 |
{
|
|
@@ -1332,7 +1245,6 @@
|
|
| 1332 |
"الموهوبين رياضياً",
|
| 1333 |
"الهيئات الرياضية"
|
| 1334 |
],
|
| 1335 |
-
"cross_references": [],
|
| 1336 |
"legal_nature": "حق أساسي/حرية"
|
| 1337 |
},
|
| 1338 |
{
|
|
@@ -1346,7 +1258,6 @@
|
|
| 1346 |
"مخاطبة السلطات",
|
| 1347 |
"الأشخاص الاعتبارية"
|
| 1348 |
],
|
| 1349 |
-
"cross_references": [],
|
| 1350 |
"legal_nature": "حق أساسي/حرية"
|
| 1351 |
},
|
| 1352 |
{
|
|
@@ -1361,7 +1272,6 @@
|
|
| 1361 |
"الدفاع عن الوطن",
|
| 1362 |
"التجنيد الإجباري"
|
| 1363 |
],
|
| 1364 |
-
"cross_references": [],
|
| 1365 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1366 |
},
|
| 1367 |
{
|
|
@@ -1377,7 +1287,6 @@
|
|
| 1377 |
"نزاهة الانتخابات",
|
| 1378 |
"قاعدة بيانات الناخبين"
|
| 1379 |
],
|
| 1380 |
-
"cross_references": [],
|
| 1381 |
"legal_nature": "حقوق وواجبات"
|
| 1382 |
},
|
| 1383 |
{
|
|
@@ -1392,7 +1301,6 @@
|
|
| 1392 |
"تصويت المغتربين",
|
| 1393 |
"رعاية المصالح"
|
| 1394 |
],
|
| 1395 |
-
"cross_references": [],
|
| 1396 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1397 |
},
|
| 1398 |
{
|
|
@@ -1407,7 +1315,6 @@
|
|
| 1407 |
"الاتجار بالبشر",
|
| 1408 |
"الاستغلال القسري"
|
| 1409 |
],
|
| 1410 |
-
"cross_references": [],
|
| 1411 |
"legal_nature": "حظر دستوري"
|
| 1412 |
},
|
| 1413 |
{
|
|
@@ -1422,7 +1329,6 @@
|
|
| 1422 |
"استقلال الوقف",
|
| 1423 |
"شروط الواقف"
|
| 1424 |
],
|
| 1425 |
-
"cross_references": [],
|
| 1426 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1427 |
},
|
| 1428 |
{
|
|
@@ -1437,7 +1343,6 @@
|
|
| 1437 |
"حظر تسليم اللاجئين",
|
| 1438 |
"حقوق الإنسان"
|
| 1439 |
],
|
| 1440 |
-
"cross_references": [],
|
| 1441 |
"legal_nature": "حق أساسي/حرية"
|
| 1442 |
},
|
| 1443 |
{
|
|
@@ -1452,7 +1357,6 @@
|
|
| 1452 |
"عدم الانتقاص",
|
| 1453 |
"جوهر الحق"
|
| 1454 |
],
|
| 1455 |
-
"cross_references": [],
|
| 1456 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1457 |
},
|
| 1458 |
{
|
|
@@ -1467,7 +1371,6 @@
|
|
| 1467 |
"حقوق الإنسان",
|
| 1468 |
"قوة القانون"
|
| 1469 |
],
|
| 1470 |
-
"cross_references": [],
|
| 1471 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1472 |
},
|
| 1473 |
{
|
|
@@ -1483,7 +1386,6 @@
|
|
| 1483 |
"حصانة القضاء",
|
| 1484 |
"حماية الحريات"
|
| 1485 |
],
|
| 1486 |
-
"cross_references": [],
|
| 1487 |
"legal_nature": "مبدأ دستوري"
|
| 1488 |
},
|
| 1489 |
{
|
|
@@ -1499,7 +1401,6 @@
|
|
| 1499 |
"عدم الرجعية",
|
| 1500 |
"حكم قضائي"
|
| 1501 |
],
|
| 1502 |
-
"cross_references": [],
|
| 1503 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1504 |
},
|
| 1505 |
{
|
|
@@ -1516,7 +1417,6 @@
|
|
| 1516 |
"استئناف الجنايات",
|
| 1517 |
"حماية الشهود"
|
| 1518 |
],
|
| 1519 |
-
"cross_references": [],
|
| 1520 |
"legal_nature": "حق أساسي/حرية"
|
| 1521 |
},
|
| 1522 |
{
|
|
@@ -1532,7 +1432,6 @@
|
|
| 1532 |
"حظر المحاكم الاستثنائية",
|
| 1533 |
"الرقابة القضائية"
|
| 1534 |
],
|
| 1535 |
-
"cross_references": [],
|
| 1536 |
"legal_nature": "حق أساسي/حرية"
|
| 1537 |
},
|
| 1538 |
{
|
|
@@ -1547,7 +1446,6 @@
|
|
| 1547 |
"استقلال المحاماة",
|
| 1548 |
"المساعدة القانونية"
|
| 1549 |
],
|
| 1550 |
-
"cross_references": [],
|
| 1551 |
"legal_nature": "حق أساسي/حرية"
|
| 1552 |
},
|
| 1553 |
{
|
|
@@ -1564,7 +1462,6 @@
|
|
| 1564 |
"التعويض العادل",
|
| 1565 |
"المجلس القومي لحقوق الإنسان"
|
| 1566 |
],
|
| 1567 |
-
"cross_references": [],
|
| 1568 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1569 |
},
|
| 1570 |
{
|
|
@@ -1581,7 +1478,6 @@
|
|
| 1581 |
"الموظف العام",
|
| 1582 |
"الدعوى الجنائية"
|
| 1583 |
],
|
| 1584 |
-
"cross_references": [],
|
| 1585 |
"legal_nature": "حق أساسي/حرية"
|
| 1586 |
},
|
| 1587 |
{
|
|
@@ -1597,7 +1493,6 @@
|
|
| 1597 |
"الموازنة العامة",
|
| 1598 |
"الرقابة"
|
| 1599 |
],
|
| 1600 |
-
"cross_references": [],
|
| 1601 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1602 |
},
|
| 1603 |
{
|
|
@@ -1614,7 +1509,6 @@
|
|
| 1614 |
"النظام الانتخابي",
|
| 1615 |
"التعيين الرئاسي"
|
| 1616 |
],
|
| 1617 |
-
"cross_references": [],
|
| 1618 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1619 |
},
|
| 1620 |
{
|
|
@@ -1628,7 +1522,6 @@
|
|
| 1628 |
"تفرغ العضو",
|
| 1629 |
"الاحتفاظ بالوظيفة"
|
| 1630 |
],
|
| 1631 |
-
"cross_references": [],
|
| 1632 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1633 |
},
|
| 1634 |
{
|
|
@@ -1642,7 +1535,6 @@
|
|
| 1642 |
"اليمين الدستورية",
|
| 1643 |
"واجبات العضو"
|
| 1644 |
],
|
| 1645 |
-
"cross_references": [],
|
| 1646 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1647 |
},
|
| 1648 |
{
|
|
@@ -1656,7 +1548,6 @@
|
|
| 1656 |
"مكافأة العضوية",
|
| 1657 |
"عدم سريان الزيادة"
|
| 1658 |
],
|
| 1659 |
-
"cross_references": [],
|
| 1660 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 1661 |
},
|
| 1662 |
{
|
|
@@ -1670,7 +1561,6 @@
|
|
| 1670 |
"مدة العضوية",
|
| 1671 |
"موعد الانتخابات"
|
| 1672 |
],
|
| 1673 |
-
"cross_references": [],
|
| 1674 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1675 |
},
|
| 1676 |
{
|
|
@@ -1685,7 +1575,6 @@
|
|
| 1685 |
"محكمة النقض",
|
| 1686 |
"الطعون الانتخابية"
|
| 1687 |
],
|
| 1688 |
-
"cross_references": [],
|
| 1689 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 1690 |
},
|
| 1691 |
{
|
|
@@ -1699,7 +1588,6 @@
|
|
| 1699 |
"خلو المقعد",
|
| 1700 |
"الانتخابات التكميلية"
|
| 1701 |
],
|
| 1702 |
-
"cross_references": [],
|
| 1703 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1704 |
},
|
| 1705 |
{
|
|
@@ -1714,7 +1602,6 @@
|
|
| 1714 |
"إقرار الذمة المالية",
|
| 1715 |
"الهدايا"
|
| 1716 |
],
|
| 1717 |
-
"cross_references": [],
|
| 1718 |
"legal_nature": "قاعدة إجرائية"
|
| 1719 |
},
|
| 1720 |
{
|
|
@@ -1729,7 +1616,6 @@
|
|
| 1729 |
"أغلبية الثلثين",
|
| 1730 |
"فقد الثقة"
|
| 1731 |
],
|
| 1732 |
-
"cross_references": [],
|
| 1733 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1734 |
},
|
| 1735 |
{
|
|
@@ -1743,7 +1629,6 @@
|
|
| 1743 |
"الاستقالة",
|
| 1744 |
"قبول الاستقالة"
|
| 1745 |
],
|
| 1746 |
-
"cross_references": [],
|
| 1747 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1748 |
},
|
| 1749 |
{
|
|
@@ -1757,7 +1642,6 @@
|
|
| 1757 |
"الحصانة البرلمانية",
|
| 1758 |
"حرية الرأي"
|
| 1759 |
],
|
| 1760 |
-
"cross_references": [],
|
| 1761 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1762 |
},
|
| 1763 |
{
|
|
@@ -1772,7 +1656,6 @@
|
|
| 1772 |
"إذن المجلس",
|
| 1773 |
"رفع الحصانة"
|
| 1774 |
],
|
| 1775 |
-
"cross_references": [],
|
| 1776 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1777 |
},
|
| 1778 |
{
|
|
@@ -1787,7 +1670,6 @@
|
|
| 1787 |
"انعقاد الجلسات",
|
| 1788 |
"بطلان الاجتماع"
|
| 1789 |
],
|
| 1790 |
-
"cross_references": [],
|
| 1791 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1792 |
},
|
| 1793 |
{
|
|
@@ -1802,7 +1684,6 @@
|
|
| 1802 |
"دعوة الرئيس",
|
| 1803 |
"اعتماد الموازنة"
|
| 1804 |
],
|
| 1805 |
-
"cross_references": [],
|
| 1806 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1807 |
},
|
| 1808 |
{
|
|
@@ -1816,7 +1697,6 @@
|
|
| 1816 |
"اجتماع غير عادي",
|
| 1817 |
"دعوة طارئة"
|
| 1818 |
],
|
| 1819 |
-
"cross_references": [],
|
| 1820 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1821 |
},
|
| 1822 |
{
|
|
@@ -1832,7 +1712,6 @@
|
|
| 1832 |
"مدة الرئاسة",
|
| 1833 |
"إعفاء الرئيس"
|
| 1834 |
],
|
| 1835 |
-
"cross_references": [],
|
| 1836 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1837 |
},
|
| 1838 |
{
|
|
@@ -1846,7 +1725,6 @@
|
|
| 1846 |
"اللائحة الداخلية",
|
| 1847 |
"تنظيم العمل"
|
| 1848 |
],
|
| 1849 |
-
"cross_references": [],
|
| 1850 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1851 |
},
|
| 1852 |
{
|
|
@@ -1860,7 +1738,6 @@
|
|
| 1860 |
"حفظ النظام",
|
| 1861 |
"رئيس المجلس"
|
| 1862 |
],
|
| 1863 |
-
"cross_references": [],
|
| 1864 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1865 |
},
|
| 1866 |
{
|
|
@@ -1874,7 +1751,6 @@
|
|
| 1874 |
"علنية الجلسات",
|
| 1875 |
"جلسة سرية"
|
| 1876 |
],
|
| 1877 |
-
"cross_references": [],
|
| 1878 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1879 |
},
|
| 1880 |
{
|
|
@@ -1890,7 +1766,6 @@
|
|
| 1890 |
"القوانين المكملة للدستور",
|
| 1891 |
"أغلبية الثلثين"
|
| 1892 |
],
|
| 1893 |
-
"cross_references": [],
|
| 1894 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1895 |
},
|
| 1896 |
{
|
|
@@ -1905,7 +1780,6 @@
|
|
| 1905 |
"اللجان النوعية",
|
| 1906 |
"لجنة المقترحات"
|
| 1907 |
],
|
| 1908 |
-
"cross_references": [],
|
| 1909 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1910 |
},
|
| 1911 |
{
|
|
@@ -1920,7 +1794,6 @@
|
|
| 1920 |
"إصدار القوانين",
|
| 1921 |
"أغلبية الثلثين"
|
| 1922 |
],
|
| 1923 |
-
"cross_references": [],
|
| 1924 |
"legal_nature": "حق أساسي/حرية"
|
| 1925 |
},
|
| 1926 |
{
|
|
@@ -1936,7 +1809,6 @@
|
|
| 1936 |
"تعديل النفقات",
|
| 1937 |
"السنة المالية"
|
| 1938 |
],
|
| 1939 |
-
"cross_references": [],
|
| 1940 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1941 |
},
|
| 1942 |
{
|
|
@@ -1951,7 +1823,6 @@
|
|
| 1951 |
"الجهاز المركزي للمحاسبات",
|
| 1952 |
"الرقابة المالية"
|
| 1953 |
],
|
| 1954 |
-
"cross_references": [],
|
| 1955 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1956 |
},
|
| 1957 |
{
|
|
@@ -1965,7 +1836,6 @@
|
|
| 1965 |
"الأموال العامة",
|
| 1966 |
"التحصيل والصرف"
|
| 1967 |
],
|
| 1968 |
-
"cross_references": [],
|
| 1969 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1970 |
},
|
| 1971 |
{
|
|
@@ -1980,7 +1850,6 @@
|
|
| 1980 |
"الديون العامة",
|
| 1981 |
"موافقة البرلمان"
|
| 1982 |
],
|
| 1983 |
-
"cross_references": [],
|
| 1984 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1985 |
},
|
| 1986 |
{
|
|
@@ -1995,7 +1864,6 @@
|
|
| 1995 |
"المعاشات",
|
| 1996 |
"الخزانة العامة"
|
| 1997 |
],
|
| 1998 |
-
"cross_references": [],
|
| 1999 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2000 |
},
|
| 2001 |
{
|
|
@@ -2009,7 +1877,6 @@
|
|
| 2009 |
"توجيه الأسئلة",
|
| 2010 |
"الرقابة البرلمانية"
|
| 2011 |
],
|
| 2012 |
-
"cross_references": [],
|
| 2013 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2014 |
},
|
| 2015 |
{
|
|
@@ -2023,7 +1890,6 @@
|
|
| 2023 |
"الاستجواب",
|
| 2024 |
"المحاسبة السياسية"
|
| 2025 |
],
|
| 2026 |
-
"cross_references": [],
|
| 2027 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2028 |
},
|
| 2029 |
{
|
|
@@ -2038,7 +1904,6 @@
|
|
| 2038 |
"استقالة الحكومة",
|
| 2039 |
"المسؤولية التضامنية"
|
| 2040 |
],
|
| 2041 |
-
"cross_references": [],
|
| 2042 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2043 |
},
|
| 2044 |
{
|
|
@@ -2052,7 +1917,6 @@
|
|
| 2052 |
"طلب مناقشة عامة",
|
| 2053 |
"استيضاح السياسة"
|
| 2054 |
],
|
| 2055 |
-
"cross_references": [],
|
| 2056 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2057 |
},
|
| 2058 |
{
|
|
@@ -2065,7 +1929,6 @@
|
|
| 2065 |
"keywords": [
|
| 2066 |
"اقتراح برغبة"
|
| 2067 |
],
|
| 2068 |
-
"cross_references": [],
|
| 2069 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2070 |
},
|
| 2071 |
{
|
|
@@ -2079,7 +1942,6 @@
|
|
| 2079 |
"طلب إحاطة",
|
| 2080 |
"بيان عاجل"
|
| 2081 |
],
|
| 2082 |
-
"cross_references": [],
|
| 2083 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2084 |
},
|
| 2085 |
{
|
|
@@ -2094,7 +1956,6 @@
|
|
| 2094 |
"جمع الأدلة",
|
| 2095 |
"حق المعلومات"
|
| 2096 |
],
|
| 2097 |
-
"cross_references": [],
|
| 2098 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2099 |
},
|
| 2100 |
{
|
|
@@ -2108,7 +1969,6 @@
|
|
| 2108 |
"حضور الحكومة",
|
| 2109 |
"الاستماع للوزراء"
|
| 2110 |
],
|
| 2111 |
-
"cross_references": [],
|
| 2112 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2113 |
},
|
| 2114 |
{
|
|
@@ -2123,7 +1983,6 @@
|
|
| 2123 |
"الاستفتاء الشعبي",
|
| 2124 |
"انتخابات مبكرة"
|
| 2125 |
],
|
| 2126 |
-
"cross_references": [],
|
| 2127 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2128 |
},
|
| 2129 |
{
|
|
@@ -2138,7 +1997,6 @@
|
|
| 2138 |
"الشكاوى",
|
| 2139 |
"تواصل البرلمان"
|
| 2140 |
],
|
| 2141 |
-
"cross_references": [],
|
| 2142 |
"legal_nature": "حق أساسي/حرية"
|
| 2143 |
},
|
| 2144 |
{
|
|
@@ -2154,7 +2012,6 @@
|
|
| 2154 |
"استقلال الوطن",
|
| 2155 |
"احترام الدستور"
|
| 2156 |
],
|
| 2157 |
-
"cross_references": [],
|
| 2158 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2159 |
},
|
| 2160 |
{
|
|
@@ -2170,7 +2027,6 @@
|
|
| 2170 |
"إجراءات الانتخاب",
|
| 2171 |
"حظر المناصب الحزبية"
|
| 2172 |
],
|
| 2173 |
-
"cross_references": [],
|
| 2174 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2175 |
},
|
| 2176 |
{
|
|
@@ -2186,7 +2042,6 @@
|
|
| 2186 |
"السن القانوني",
|
| 2187 |
"الخدمة العسكرية"
|
| 2188 |
],
|
| 2189 |
-
"cross_references": [],
|
| 2190 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2191 |
},
|
| 2192 |
{
|
|
@@ -2201,7 +2056,6 @@
|
|
| 2201 |
"التوكيلات الشعبية",
|
| 2202 |
"شروط الترشح"
|
| 2203 |
],
|
| 2204 |
-
"cross_references": [],
|
| 2205 |
"legal_nature": "حق أساسي/حرية"
|
| 2206 |
},
|
| 2207 |
{
|
|
@@ -2215,7 +2069,6 @@
|
|
| 2215 |
"الاقتراع المباشر",
|
| 2216 |
"الأغلبية المطلقة"
|
| 2217 |
],
|
| 2218 |
-
"cross_references": [],
|
| 2219 |
"legal_nature": "قاعدة إجرائية"
|
| 2220 |
},
|
| 2221 |
{
|
|
@@ -2229,7 +2082,6 @@
|
|
| 2229 |
"اليمين الدستورية",
|
| 2230 |
"تولي المهام"
|
| 2231 |
],
|
| 2232 |
-
"cross_references": [],
|
| 2233 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2234 |
},
|
| 2235 |
{
|
|
@@ -2245,7 +2097,6 @@
|
|
| 2245 |
"إقرار الذمة المالية",
|
| 2246 |
"الهدايا"
|
| 2247 |
],
|
| 2248 |
-
"cross_references": [],
|
| 2249 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2250 |
},
|
| 2251 |
{
|
|
@@ -2261,7 +2112,6 @@
|
|
| 2261 |
"حل المجلس",
|
| 2262 |
"الوزارات السيادية"
|
| 2263 |
],
|
| 2264 |
-
"cross_references": [],
|
| 2265 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2266 |
},
|
| 2267 |
{
|
|
@@ -2276,7 +2126,6 @@
|
|
| 2276 |
"التعديل الوزاري",
|
| 2277 |
"موافقة البرلمان"
|
| 2278 |
],
|
| 2279 |
-
"cross_references": [],
|
| 2280 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2281 |
},
|
| 2282 |
{
|
|
@@ -2290,7 +2139,6 @@
|
|
| 2290 |
"تفويض السلطات",
|
| 2291 |
"التفويض الإداري"
|
| 2292 |
],
|
| 2293 |
-
"cross_references": [],
|
| 2294 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2295 |
},
|
| 2296 |
{
|
|
@@ -2304,7 +2152,6 @@
|
|
| 2304 |
"اجتماع الحكومة",
|
| 2305 |
"رئاسة الاجتماع"
|
| 2306 |
],
|
| 2307 |
-
"cross_references": [],
|
| 2308 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2309 |
},
|
| 2310 |
{
|
|
@@ -2318,9 +2165,6 @@
|
|
| 2318 |
"السياسة العامة",
|
| 2319 |
"بيان الرئيس"
|
| 2320 |
],
|
| 2321 |
-
"cross_references": [
|
| 2322 |
-
"الدستور"
|
| 2323 |
-
],
|
| 2324 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2325 |
},
|
| 2326 |
{
|
|
@@ -2335,12 +2179,6 @@
|
|
| 2335 |
"تعيين النائب",
|
| 2336 |
"اختصاصات النائب"
|
| 2337 |
],
|
| 2338 |
-
"cross_references": [
|
| 2339 |
-
"144",
|
| 2340 |
-
"141",
|
| 2341 |
-
"145",
|
| 2342 |
-
"173"
|
| 2343 |
-
],
|
| 2344 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2345 |
},
|
| 2346 |
{
|
|
@@ -2356,9 +2194,6 @@
|
|
| 2356 |
"الاستفتاء الشعبي",
|
| 2357 |
"حظر التنازل عن الأرض"
|
| 2358 |
],
|
| 2359 |
-
"cross_references": [
|
| 2360 |
-
"الدستور"
|
| 2361 |
-
],
|
| 2362 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2363 |
},
|
| 2364 |
{
|
|
@@ -2374,7 +2209,6 @@
|
|
| 2374 |
"إرسال القوات",
|
| 2375 |
"مجلس الدفاع الوطني"
|
| 2376 |
],
|
| 2377 |
-
"cross_references": [],
|
| 2378 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2379 |
},
|
| 2380 |
{
|
|
@@ -2389,7 +2223,6 @@
|
|
| 2389 |
"الموظفين العموميين",
|
| 2390 |
"الدبلوماسيين"
|
| 2391 |
],
|
| 2392 |
-
"cross_references": [],
|
| 2393 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2394 |
},
|
| 2395 |
{
|
|
@@ -2404,7 +2237,6 @@
|
|
| 2404 |
"موافقة البرلمان",
|
| 2405 |
"تمديد الطوارئ"
|
| 2406 |
],
|
| 2407 |
-
"cross_references": [],
|
| 2408 |
"legal_nature": "تنظيم م��سسي/هيكلي"
|
| 2409 |
},
|
| 2410 |
{
|
|
@@ -2419,7 +2251,6 @@
|
|
| 2419 |
"تخفيف العقوبة",
|
| 2420 |
"العفو الشامل"
|
| 2421 |
],
|
| 2422 |
-
"cross_references": [],
|
| 2423 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2424 |
},
|
| 2425 |
{
|
|
@@ -2434,7 +2265,6 @@
|
|
| 2434 |
"الضرورة",
|
| 2435 |
"مراجعة البرلمان"
|
| 2436 |
],
|
| 2437 |
-
"cross_references": [],
|
| 2438 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2439 |
},
|
| 2440 |
{
|
|
@@ -2448,9 +2278,6 @@
|
|
| 2448 |
"الاستفتاء الشعبي",
|
| 2449 |
"المصالح العليا"
|
| 2450 |
],
|
| 2451 |
-
"cross_references": [
|
| 2452 |
-
"الدستور"
|
| 2453 |
-
],
|
| 2454 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2455 |
},
|
| 2456 |
{
|
|
@@ -2463,7 +2290,6 @@
|
|
| 2463 |
"keywords": [
|
| 2464 |
"استقالة الرئيس"
|
| 2465 |
],
|
| 2466 |
-
"cross_references": [],
|
| 2467 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2468 |
},
|
| 2469 |
{
|
|
@@ -2479,7 +2305,6 @@
|
|
| 2479 |
"المحكمة الخاصة",
|
| 2480 |
"العزل"
|
| 2481 |
],
|
| 2482 |
-
"cross_references": [],
|
| 2483 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2484 |
},
|
| 2485 |
{
|
|
@@ -2495,7 +2320,6 @@
|
|
| 2495 |
"انتخابات رئاسية مبكرة",
|
| 2496 |
"تقييد الصلاحيات"
|
| 2497 |
],
|
| 2498 |
-
"cross_references": [],
|
| 2499 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2500 |
},
|
| 2501 |
{
|
|
@@ -2511,7 +2335,6 @@
|
|
| 2511 |
"حل البرلمان",
|
| 2512 |
"الاستفتاء"
|
| 2513 |
],
|
| 2514 |
-
"cross_references": [],
|
| 2515 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2516 |
},
|
| 2517 |
{
|
|
@@ -2525,7 +2348,6 @@
|
|
| 2525 |
"أسبقية الانتخابات",
|
| 2526 |
"استمرار المجلس"
|
| 2527 |
],
|
| 2528 |
-
"cross_references": [],
|
| 2529 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2530 |
},
|
| 2531 |
{
|
|
@@ -2540,7 +2362,6 @@
|
|
| 2540 |
"رئيس مجلس الوزراء",
|
| 2541 |
"الهيئة التنفيذية"
|
| 2542 |
],
|
| 2543 |
-
"cross_references": [],
|
| 2544 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2545 |
},
|
| 2546 |
{
|
|
@@ -2556,7 +2377,6 @@
|
|
| 2556 |
"الجنسية",
|
| 2557 |
"حظر الجمع بين المناصب"
|
| 2558 |
],
|
| 2559 |
-
"cross_references": [],
|
| 2560 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2561 |
},
|
| 2562 |
{
|
|
@@ -2570,7 +2390,6 @@
|
|
| 2570 |
"اليمين الدستورية",
|
| 2571 |
"تولي المهام"
|
| 2572 |
],
|
| 2573 |
-
"cross_references": [],
|
| 2574 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2575 |
},
|
| 2576 |
{
|
|
@@ -2586,7 +2405,6 @@
|
|
| 2586 |
"تعارض المصالح",
|
| 2587 |
"حظر الأعمال التجارية"
|
| 2588 |
],
|
| 2589 |
-
"cross_references": [],
|
| 2590 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2591 |
},
|
| 2592 |
{
|
|
@@ -2603,7 +2421,6 @@
|
|
| 2603 |
"الأمن القومي",
|
| 2604 |
"إعداد القوانين"
|
| 2605 |
],
|
| 2606 |
-
"cross_references": [],
|
| 2607 |
"legal_nature": "حق أساسي/حرية"
|
| 2608 |
},
|
| 2609 |
{
|
|
@@ -2618,7 +2435,6 @@
|
|
| 2618 |
"الوكيل الدائم",
|
| 2619 |
"الاستقرار المؤسسي"
|
| 2620 |
],
|
| 2621 |
-
"cross_references": [],
|
| 2622 |
"legal_nature": "ضمان/حماية"
|
| 2623 |
},
|
| 2624 |
{
|
|
@@ -2633,7 +2449,6 @@
|
|
| 2633 |
"مجلس النواب",
|
| 2634 |
"المساءلة"
|
| 2635 |
],
|
| 2636 |
-
"cross_references": [],
|
| 2637 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2638 |
},
|
| 2639 |
{
|
|
@@ -2648,7 +2463,6 @@
|
|
| 2648 |
"التفويض",
|
| 2649 |
"رئيس الوزراء"
|
| 2650 |
],
|
| 2651 |
-
"cross_references": [],
|
| 2652 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2653 |
},
|
| 2654 |
{
|
|
@@ -2663,7 +2477,6 @@
|
|
| 2663 |
"المصالح العامة",
|
| 2664 |
"قرارات تنظيمية"
|
| 2665 |
],
|
| 2666 |
-
"cross_references": [],
|
| 2667 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2668 |
},
|
| 2669 |
{
|
|
@@ -2677,7 +2490,6 @@
|
|
| 2677 |
"لوائح الضبط",
|
| 2678 |
"النظام العام"
|
| 2679 |
],
|
| 2680 |
-
"cross_references": [],
|
| 2681 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2682 |
},
|
| 2683 |
{
|
|
@@ -2692,9 +2504,6 @@
|
|
| 2692 |
"الجرائم الوظيفية",
|
| 2693 |
"الخيانة العظمى"
|
| 2694 |
],
|
| 2695 |
-
"cross_references": [
|
| 2696 |
-
"159"
|
| 2697 |
-
],
|
| 2698 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2699 |
},
|
| 2700 |
{
|
|
@@ -2707,7 +2516,6 @@
|
|
| 2707 |
"keywords": [
|
| 2708 |
"الاستقالة"
|
| 2709 |
],
|
| 2710 |
-
"cross_references": [],
|
| 2711 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2712 |
},
|
| 2713 |
{
|
|
@@ -2723,7 +2531,6 @@
|
|
| 2723 |
"المحافظات",
|
| 2724 |
"الوحدات المحلية"
|
| 2725 |
],
|
| 2726 |
-
"cross_references": [],
|
| 2727 |
"legal_nature": "قاعدة إجرائية"
|
| 2728 |
},
|
| 2729 |
{
|
|
@@ -2738,7 +2545,6 @@
|
|
| 2738 |
"نقل السلطات",
|
| 2739 |
"المرافق المحلية"
|
| 2740 |
],
|
| 2741 |
-
"cross_references": [],
|
| 2742 |
"legal_nature": "التزام/واجب على الدولة"
|
| 2743 |
},
|
| 2744 |
{
|
|
@@ -2753,7 +2559,6 @@
|
|
| 2753 |
"توزيع الموارد",
|
| 2754 |
"التنمية المحلية"
|
| 2755 |
],
|
| 2756 |
-
"cross_references": [],
|
| 2757 |
"legal_nature": "التزام/واجب على الدولة"
|
| 2758 |
},
|
| 2759 |
{
|
|
@@ -2768,7 +2573,6 @@
|
|
| 2768 |
"الضرائب المحلية",
|
| 2769 |
"الموارد المالية"
|
| 2770 |
],
|
| 2771 |
-
"cross_references": [],
|
| 2772 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 2773 |
},
|
| 2774 |
{
|
|
@@ -2783,7 +2587,6 @@
|
|
| 2783 |
"رؤساء الوحدات المحلية",
|
| 2784 |
"التعيين والانتخاب"
|
| 2785 |
],
|
| 2786 |
-
"cross_references": [],
|
| 2787 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2788 |
},
|
| 2789 |
{
|
|
@@ -2800,7 +2603,6 @@
|
|
| 2800 |
"الرقابة المحلية",
|
| 2801 |
"سحب الثقة"
|
| 2802 |
],
|
| 2803 |
-
"cross_references": [],
|
| 2804 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 2805 |
},
|
| 2806 |
{
|
|
@@ -2816,7 +2618,6 @@
|
|
| 2816 |
"تنازع الاختصاص",
|
| 2817 |
"مجلس الدولة"
|
| 2818 |
],
|
| 2819 |
-
"cross_references": [],
|
| 2820 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2821 |
},
|
| 2822 |
{
|
|
@@ -2830,7 +2631,6 @@
|
|
| 2830 |
"الموازنة المحلية",
|
| 2831 |
"الحساب الختامي"
|
| 2832 |
],
|
| 2833 |
-
"cross_references": [],
|
| 2834 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2835 |
},
|
| 2836 |
{
|
|
@@ -2844,7 +2644,6 @@
|
|
| 2844 |
"حل المجالس المحلية",
|
| 2845 |
"الحظر الإداري"
|
| 2846 |
],
|
| 2847 |
-
"cross_references": [],
|
| 2848 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2849 |
},
|
| 2850 |
{
|
|
@@ -2860,7 +2659,6 @@
|
|
| 2860 |
"التدخل في العدالة",
|
| 2861 |
"عدم التقادم"
|
| 2862 |
],
|
| 2863 |
-
"cross_references": [],
|
| 2864 |
"legal_nature": "مبدأ دستوري"
|
| 2865 |
},
|
| 2866 |
{
|
|
@@ -2876,7 +2674,6 @@
|
|
| 2876 |
"تعيين الرؤساء",
|
| 2877 |
"المجلس الأعلى للجهات القضائية"
|
| 2878 |
],
|
| 2879 |
-
"cross_references": [],
|
| 2880 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2881 |
},
|
| 2882 |
{
|
|
@@ -2892,7 +2689,6 @@
|
|
| 2892 |
"المساءلة التأديبية",
|
| 2893 |
"الندب"
|
| 2894 |
],
|
| 2895 |
-
"cross_references": [],
|
| 2896 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2897 |
},
|
| 2898 |
{
|
|
@@ -2907,7 +2703,6 @@
|
|
| 2907 |
"النطق بالحكم",
|
| 2908 |
"النظام العام"
|
| 2909 |
],
|
| 2910 |
-
"cross_references": [],
|
| 2911 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2912 |
},
|
| 2913 |
{
|
|
@@ -2922,7 +2717,6 @@
|
|
| 2922 |
"مجلس القضاء الأعلى",
|
| 2923 |
"الفصل في المنازعات"
|
| 2924 |
],
|
| 2925 |
-
"cross_references": [],
|
| 2926 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2927 |
},
|
| 2928 |
{
|
|
@@ -2938,7 +2732,6 @@
|
|
| 2938 |
"الدعوى الجنائية",
|
| 2939 |
"تعيين النائب العام"
|
| 2940 |
],
|
| 2941 |
-
"cross_references": [],
|
| 2942 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2943 |
},
|
| 2944 |
{
|
|
@@ -2954,7 +2747,6 @@
|
|
| 2954 |
"الإفتاء القانوني",
|
| 2955 |
"مراجعة التشريعات"
|
| 2956 |
],
|
| 2957 |
-
"cross_references": [],
|
| 2958 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2959 |
},
|
| 2960 |
{
|
|
@@ -2970,7 +2762,6 @@
|
|
| 2970 |
"الموازنة المستقلة",
|
| 2971 |
"الجمعية العامة"
|
| 2972 |
],
|
| 2973 |
-
"cross_references": [],
|
| 2974 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2975 |
},
|
| 2976 |
{
|
|
@@ -2986,7 +2777,6 @@
|
|
| 2986 |
"تنازع الاختصاص",
|
| 2987 |
"الأحكام المتناقضة"
|
| 2988 |
],
|
| 2989 |
-
"cross_references": [],
|
| 2990 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2991 |
},
|
| 2992 |
{
|
|
@@ -3002,7 +2792,6 @@
|
|
| 3002 |
"تعيين القضاة",
|
| 3003 |
"رئيس الجمهورية"
|
| 3004 |
],
|
| 3005 |
-
"cross_references": [],
|
| 3006 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3007 |
},
|
| 3008 |
{
|
|
@@ -3017,7 +2806,6 @@
|
|
| 3017 |
"استقلال القضاة",
|
| 3018 |
"المساءلة التأديبية"
|
| 3019 |
],
|
| 3020 |
-
"cross_references": [],
|
| 3021 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3022 |
},
|
| 3023 |
{
|
|
@@ -3033,7 +2821,6 @@
|
|
| 3033 |
"إلزامية الأحكام",
|
| 3034 |
"عدم الدستورية"
|
| 3035 |
],
|
| 3036 |
-
"cross_references": [],
|
| 3037 |
"legal_nature": "قيمة تعليمية/تثقيفية/ثقافية"
|
| 3038 |
},
|
| 3039 |
{
|
|
@@ -3050,7 +2837,6 @@
|
|
| 3050 |
"صياغة العقود",
|
| 3051 |
"التسوية الودية"
|
| 3052 |
],
|
| 3053 |
-
"cross_references": [],
|
| 3054 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3055 |
},
|
| 3056 |
{
|
|
@@ -3067,7 +2853,6 @@
|
|
| 3067 |
"الجزاءات التأديبية",
|
| 3068 |
"مجلس الدولة"
|
| 3069 |
],
|
| 3070 |
-
"cross_references": [],
|
| 3071 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3072 |
},
|
| 3073 |
{
|
|
@@ -3084,7 +2869,6 @@
|
|
| 3084 |
"استقلال المحاماة",
|
| 3085 |
"جهات التحقيق"
|
| 3086 |
],
|
| 3087 |
-
"cross_references": [],
|
| 3088 |
"legal_nature": "حق أساسي/حرية"
|
| 3089 |
},
|
| 3090 |
{
|
|
@@ -3101,7 +2885,6 @@
|
|
| 3101 |
"استقلال الخبراء",
|
| 3102 |
"الحماية القانونية"
|
| 3103 |
],
|
| 3104 |
-
"cross_references": [],
|
| 3105 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3106 |
},
|
| 3107 |
{
|
|
@@ -3118,7 +2901,6 @@
|
|
| 3118 |
"المجلس الأعلى للقوات المسلحة",
|
| 3119 |
"صون الدستور"
|
| 3120 |
],
|
| 3121 |
-
"cross_references": [],
|
| 3122 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3123 |
},
|
| 3124 |
{
|
|
@@ -3133,7 +2915,6 @@
|
|
| 3133 |
"القائد العام",
|
| 3134 |
"التعيين"
|
| 3135 |
],
|
| 3136 |
-
"cross_references": [],
|
| 3137 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3138 |
},
|
| 3139 |
{
|
|
@@ -3149,7 +2930,6 @@
|
|
| 3149 |
"اللجان القضائية العسكرية",
|
| 3150 |
"المنازعات الإدارية"
|
| 3151 |
],
|
| 3152 |
-
"cross_references": [],
|
| 3153 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3154 |
},
|
| 3155 |
{
|
|
@@ -3165,7 +2945,6 @@
|
|
| 3165 |
"تأمين البلاد",
|
| 3166 |
"رقم واحد"
|
| 3167 |
],
|
| 3168 |
-
"cross_references": [],
|
| 3169 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3170 |
},
|
| 3171 |
{
|
|
@@ -3181,7 +2960,6 @@
|
|
| 3181 |
"الجرائم العسكرية",
|
| 3182 |
"استقلال القضاء"
|
| 3183 |
],
|
| 3184 |
-
"cross_references": [],
|
| 3185 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3186 |
},
|
| 3187 |
{
|
|
@@ -3197,7 +2975,6 @@
|
|
| 3197 |
"إدارة الأزمات",
|
| 3198 |
"الكوارث"
|
| 3199 |
],
|
| 3200 |
-
"cross_references": [],
|
| 3201 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3202 |
},
|
| 3203 |
{
|
|
@@ -3212,7 +2989,6 @@
|
|
| 3212 |
"النظام العام",
|
| 3213 |
"الأمن"
|
| 3214 |
],
|
| 3215 |
-
"cross_references": [],
|
| 3216 |
"legal_nature": "حق أساسي/حرية"
|
| 3217 |
},
|
| 3218 |
{
|
|
@@ -3228,7 +3004,6 @@
|
|
| 3228 |
"المجلس الأعلى",
|
| 3229 |
"الأمن"
|
| 3230 |
],
|
| 3231 |
-
"cross_references": [],
|
| 3232 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3233 |
},
|
| 3234 |
{
|
|
@@ -3244,7 +3019,6 @@
|
|
| 3244 |
"المجلس الأعلى لتنظيم الإعلام",
|
| 3245 |
"الصحافة"
|
| 3246 |
],
|
| 3247 |
-
"cross_references": [],
|
| 3248 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3249 |
},
|
| 3250 |
{
|
|
@@ -3257,7 +3031,6 @@
|
|
| 3257 |
"keywords": [
|
| 3258 |
"الصحافة"
|
| 3259 |
],
|
| 3260 |
-
"cross_references": [],
|
| 3261 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3262 |
},
|
| 3263 |
{
|
|
@@ -3272,7 +3045,6 @@
|
|
| 3272 |
"الإعلام",
|
| 3273 |
"المجلس الأعلى لتنظيم الإعلام"
|
| 3274 |
],
|
| 3275 |
-
"cross_references": [],
|
| 3276 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3277 |
},
|
| 3278 |
{
|
|
@@ -3289,7 +3061,6 @@
|
|
| 3289 |
"ذوي الإعاقة",
|
| 3290 |
"الاستقلال المالي والإداري"
|
| 3291 |
],
|
| 3292 |
-
"cross_references": [],
|
| 3293 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3294 |
},
|
| 3295 |
{
|
|
@@ -3306,7 +3077,6 @@
|
|
| 3306 |
"الرقابة الإدارية",
|
| 3307 |
"الجهاز المركزي للمحاسبات"
|
| 3308 |
],
|
| 3309 |
-
"cross_references": [],
|
| 3310 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3311 |
},
|
| 3312 |
{
|
|
@@ -3322,7 +3092,6 @@
|
|
| 3322 |
"الاستقلال والحياد",
|
| 3323 |
"موافقة البرلمان"
|
| 3324 |
],
|
| 3325 |
-
"cross_references": [],
|
| 3326 |
"legal_nature": "حظر دستوري"
|
| 3327 |
},
|
| 3328 |
{
|
|
@@ -3338,7 +3107,6 @@
|
|
| 3338 |
"الرقابة البرلمانية",
|
| 3339 |
"إبلاغ النيابة"
|
| 3340 |
],
|
| 3341 |
-
"cross_references": [],
|
| 3342 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3343 |
},
|
| 3344 |
{
|
|
@@ -3354,7 +3122,6 @@
|
|
| 3354 |
"حماية المال العام",
|
| 3355 |
"الاستراتيجية الوطنية"
|
| 3356 |
],
|
| 3357 |
-
"cross_references": [],
|
| 3358 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3359 |
},
|
| 3360 |
{
|
|
@@ -3369,7 +3136,6 @@
|
|
| 3369 |
"الرقابة المالية",
|
| 3370 |
"الموازنة العامة"
|
| 3371 |
],
|
| 3372 |
-
"cross_references": [],
|
| 3373 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 3374 |
},
|
| 3375 |
{
|
|
@@ -3385,7 +3151,6 @@
|
|
| 3385 |
"إصدار النقد",
|
| 3386 |
"استقرار الأسعار"
|
| 3387 |
],
|
| 3388 |
-
"cross_references": [],
|
| 3389 |
"legal_nature": "حق أساسي/حرية"
|
| 3390 |
},
|
| 3391 |
{
|
|
@@ -3401,7 +3166,6 @@
|
|
| 3401 |
"سوق المال",
|
| 3402 |
"التأمين"
|
| 3403 |
],
|
| 3404 |
-
"cross_references": [],
|
| 3405 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3406 |
},
|
| 3407 |
{
|
|
@@ -3415,7 +3179,6 @@
|
|
| 3415 |
"العاصمة",
|
| 3416 |
"القاهرة"
|
| 3417 |
],
|
| 3418 |
-
"cross_references": [],
|
| 3419 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3420 |
},
|
| 3421 |
{
|
|
@@ -3431,7 +3194,6 @@
|
|
| 3431 |
"شعار الجمهورية",
|
| 3432 |
"إهانة العلم"
|
| 3433 |
],
|
| 3434 |
-
"cross_references": [],
|
| 3435 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3436 |
},
|
| 3437 |
{
|
|
@@ -3446,9 +3208,6 @@
|
|
| 3446 |
"القوانين السابقة",
|
| 3447 |
"إصدار القوانين"
|
| 3448 |
],
|
| 3449 |
-
"cross_references": [
|
| 3450 |
-
"الدستور"
|
| 3451 |
-
],
|
| 3452 |
"legal_nature": "حكم انتقالي"
|
| 3453 |
},
|
| 3454 |
{
|
|
@@ -3464,7 +3223,6 @@
|
|
| 3464 |
"الأثر الرجعي",
|
| 3465 |
"تاريخ العمل بالقانون"
|
| 3466 |
],
|
| 3467 |
-
"cross_references": [],
|
| 3468 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3469 |
},
|
| 3470 |
{
|
|
@@ -3480,7 +3238,6 @@
|
|
| 3480 |
"حظر التعديل",
|
| 3481 |
"إجراءات التعديل"
|
| 3482 |
],
|
| 3483 |
-
"cross_references": [],
|
| 3484 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3485 |
},
|
| 3486 |
{
|
|
@@ -3494,7 +3251,6 @@
|
|
| 3494 |
"وحدة الدستور",
|
| 3495 |
"التكامل"
|
| 3496 |
],
|
| 3497 |
-
"cross_references": [],
|
| 3498 |
"legal_nature": "مبدأ تفسيري"
|
| 3499 |
},
|
| 3500 |
{
|
|
@@ -3509,7 +3265,6 @@
|
|
| 3509 |
"الهيئة الوطنية للانتخابات",
|
| 3510 |
"إشراف قضائي"
|
| 3511 |
],
|
| 3512 |
-
"cross_references": [],
|
| 3513 |
"legal_nature": "حكم انتقالي"
|
| 3514 |
},
|
| 3515 |
{
|
|
@@ -3522,9 +3277,6 @@
|
|
| 3522 |
"keywords": [
|
| 3523 |
"انتخابات مجلس النواب"
|
| 3524 |
],
|
| 3525 |
-
"cross_references": [
|
| 3526 |
-
"102"
|
| 3527 |
-
],
|
| 3528 |
"legal_nature": "حكم انتقالي"
|
| 3529 |
},
|
| 3530 |
{
|
|
@@ -3538,7 +3290,6 @@
|
|
| 3538 |
"مواعيد الانتخابات",
|
| 3539 |
"إجراءات انتقالية"
|
| 3540 |
],
|
| 3541 |
-
"cross_references": [],
|
| 3542 |
"legal_nature": "حكم انتقالي"
|
| 3543 |
},
|
| 3544 |
{
|
|
@@ -3552,7 +3303,6 @@
|
|
| 3552 |
"مدة الرئاسة",
|
| 3553 |
"إعلان النتيجة"
|
| 3554 |
],
|
| 3555 |
-
"cross_references": [],
|
| 3556 |
"legal_nature": "حكم انتقالي"
|
| 3557 |
},
|
| 3558 |
{
|
|
@@ -3566,7 +3316,6 @@
|
|
| 3566 |
"الرئيس المؤقت",
|
| 3567 |
"تسليم السلطة"
|
| 3568 |
],
|
| 3569 |
-
"cross_references": [],
|
| 3570 |
"legal_nature": "حكم انتقالي"
|
| 3571 |
},
|
| 3572 |
{
|
|
@@ -3580,7 +3329,6 @@
|
|
| 3580 |
"خلو المنصب",
|
| 3581 |
"الرئيس المؤقت"
|
| 3582 |
],
|
| 3583 |
-
"cross_references": [],
|
| 3584 |
"legal_nature": "حكم انتقالي"
|
| 3585 |
},
|
| 3586 |
{
|
|
@@ -3595,7 +3343,6 @@
|
|
| 3595 |
"المجلس الأعلى للقوات المسلحة",
|
| 3596 |
"التعيين"
|
| 3597 |
],
|
| 3598 |
-
"cross_references": [],
|
| 3599 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3600 |
},
|
| 3601 |
{
|
|
@@ -3610,7 +3357,6 @@
|
|
| 3610 |
"حرية العقيدة",
|
| 3611 |
"المسيحيين"
|
| 3612 |
],
|
| 3613 |
-
"cross_references": [],
|
| 3614 |
"legal_nature": "التزام تشريعي"
|
| 3615 |
},
|
| 3616 |
{
|
|
@@ -3626,7 +3372,6 @@
|
|
| 3626 |
"المناطق الحدودية",
|
| 3627 |
"العدالة الاجتماعية"
|
| 3628 |
],
|
| 3629 |
-
"cross_references": [],
|
| 3630 |
"legal_nature": "حق أساسي/حرية"
|
| 3631 |
},
|
| 3632 |
{
|
|
@@ -3641,7 +3386,6 @@
|
|
| 3641 |
"تمويل الإرهاب",
|
| 3642 |
"تعويض الأضرار"
|
| 3643 |
],
|
| 3644 |
-
"cross_references": [],
|
| 3645 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3646 |
},
|
| 3647 |
{
|
|
@@ -3657,7 +3401,6 @@
|
|
| 3657 |
"الصحة",
|
| 3658 |
"التعليم الإلزامي"
|
| 3659 |
],
|
| 3660 |
-
"cross_references": [],
|
| 3661 |
"legal_nature": "حكم انتقالي"
|
| 3662 |
},
|
| 3663 |
{
|
|
@@ -3671,7 +3414,6 @@
|
|
| 3671 |
"ندب القضاة",
|
| 3672 |
"استقلال القضاء"
|
| 3673 |
],
|
| 3674 |
-
"cross_references": [],
|
| 3675 |
"legal_nature": "حكم انتقالي"
|
| 3676 |
},
|
| 3677 |
{
|
|
@@ -3685,7 +3427,6 @@
|
|
| 3685 |
"استئناف الجنايات",
|
| 3686 |
"درجات التقاضي"
|
| 3687 |
],
|
| 3688 |
-
"cross_references": [],
|
| 3689 |
"legal_nature": "حكم انتقالي"
|
| 3690 |
},
|
| 3691 |
{
|
|
@@ -3700,7 +3441,6 @@
|
|
| 3700 |
"المصالحة الوطنية",
|
| 3701 |
"تعويض الضحايا"
|
| 3702 |
],
|
| 3703 |
-
"cross_references": [],
|
| 3704 |
"legal_nature": "التزام تشريعي"
|
| 3705 |
},
|
| 3706 |
{
|
|
@@ -3714,7 +3454,6 @@
|
|
| 3714 |
"مدة الرئاسة",
|
| 3715 |
"تعديلات 2019"
|
| 3716 |
],
|
| 3717 |
-
"cross_references": [],
|
| 3718 |
"legal_nature": "قاعدة إجرائية"
|
| 3719 |
},
|
| 3720 |
{
|
|
@@ -3728,9 +3467,6 @@
|
|
| 3728 |
"الإدارة المحلية",
|
| 3729 |
"تطبيق تدريجي"
|
| 3730 |
],
|
| 3731 |
-
"cross_references": [
|
| 3732 |
-
"180"
|
| 3733 |
-
],
|
| 3734 |
"legal_nature": "حكم انتقالي"
|
| 3735 |
},
|
| 3736 |
{
|
|
@@ -3744,7 +3480,6 @@
|
|
| 3744 |
"تمثيل العمال والفلاحين",
|
| 3745 |
"مجلس النواب"
|
| 3746 |
],
|
| 3747 |
-
"cross_references": [],
|
| 3748 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3749 |
},
|
| 3750 |
{
|
|
@@ -3761,7 +3496,6 @@
|
|
| 3761 |
"ذوي الإعاقة",
|
| 3762 |
"المصريين بالخارج"
|
| 3763 |
],
|
| 3764 |
-
"cross_references": [],
|
| 3765 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3766 |
},
|
| 3767 |
{
|
|
@@ -3775,9 +3509,6 @@
|
|
| 3775 |
"كوتا المرأة",
|
| 3776 |
"سريان التعديلات"
|
| 3777 |
],
|
| 3778 |
-
"cross_references": [
|
| 3779 |
-
"102"
|
| 3780 |
-
],
|
| 3781 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3782 |
},
|
| 3783 |
{
|
|
@@ -3792,7 +3523,6 @@
|
|
| 3792 |
"مجلس النواب",
|
| 3793 |
"الموظفين"
|
| 3794 |
],
|
| 3795 |
-
"cross_references": [],
|
| 3796 |
"legal_nature": "حكم انتقالي"
|
| 3797 |
},
|
| 3798 |
{
|
|
@@ -3806,7 +3536,6 @@
|
|
| 3806 |
"إلغاء دستوري",
|
| 3807 |
"الإعلان الدستوري"
|
| 3808 |
],
|
| 3809 |
-
"cross_references": [],
|
| 3810 |
"legal_nature": "قيمة تعليمية/تثقيفية/ثقافية"
|
| 3811 |
},
|
| 3812 |
{
|
|
@@ -3820,7 +3549,6 @@
|
|
| 3820 |
"نفاذ الدستور",
|
| 3821 |
"الاستفتاء الشعبي"
|
| 3822 |
],
|
| 3823 |
-
"cross_references": [],
|
| 3824 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3825 |
},
|
| 3826 |
{
|
|
@@ -3836,7 +3564,6 @@
|
|
| 3836 |
"السلام الاجتماعي",
|
| 3837 |
"الحقوق والحريات"
|
| 3838 |
],
|
| 3839 |
-
"cross_references": [],
|
| 3840 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3841 |
},
|
| 3842 |
{
|
|
@@ -3853,7 +3580,6 @@
|
|
| 3853 |
"المعاهدات الدولية",
|
| 3854 |
"السياسة العامة"
|
| 3855 |
],
|
| 3856 |
-
"cross_references": [],
|
| 3857 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3858 |
},
|
| 3859 |
{
|
|
@@ -3869,7 +3595,6 @@
|
|
| 3869 |
"التعيين",
|
| 3870 |
"مدة العضوية"
|
| 3871 |
],
|
| 3872 |
-
"cross_references": [],
|
| 3873 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3874 |
},
|
| 3875 |
{
|
|
@@ -3885,7 +3610,6 @@
|
|
| 3885 |
"السن القانوني",
|
| 3886 |
"النظام الانتخابي"
|
| 3887 |
],
|
| 3888 |
-
"cross_references": [],
|
| 3889 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3890 |
},
|
| 3891 |
{
|
|
@@ -3899,7 +3623,6 @@
|
|
| 3899 |
"حظر الجمع",
|
| 3900 |
"مجلس النواب"
|
| 3901 |
],
|
| 3902 |
-
"cross_references": [],
|
| 3903 |
"legal_nature": "حظر دستوري"
|
| 3904 |
},
|
| 3905 |
{
|
|
@@ -3914,7 +3637,6 @@
|
|
| 3914 |
"الحكومة",
|
| 3915 |
"علاقة السلطات"
|
| 3916 |
],
|
| 3917 |
-
"cross_references": [],
|
| 3918 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3919 |
},
|
| 3920 |
{
|
|
@@ -3930,32 +3652,6 @@
|
|
| 3930 |
"الواجبات",
|
| 3931 |
"الإجراءات"
|
| 3932 |
],
|
| 3933 |
-
"cross_references": [
|
| 3934 |
-
"103",
|
| 3935 |
-
"104",
|
| 3936 |
-
"105",
|
| 3937 |
-
"107",
|
| 3938 |
-
"108",
|
| 3939 |
-
"109",
|
| 3940 |
-
"110",
|
| 3941 |
-
"111",
|
| 3942 |
-
"112",
|
| 3943 |
-
"113",
|
| 3944 |
-
"114",
|
| 3945 |
-
"115",
|
| 3946 |
-
"116",
|
| 3947 |
-
"117",
|
| 3948 |
-
"118",
|
| 3949 |
-
"119",
|
| 3950 |
-
"120",
|
| 3951 |
-
"121",
|
| 3952 |
-
"132",
|
| 3953 |
-
"133",
|
| 3954 |
-
"136",
|
| 3955 |
-
"137"
|
| 3956 |
-
],
|
| 3957 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3958 |
}
|
| 3959 |
-
]
|
| 3960 |
-
}
|
| 3961 |
]
|
|
|
|
| 1 |
[
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
{
|
| 3 |
"article_id": "EG-CONST-ART-001",
|
| 4 |
"article_number": "1",
|
|
|
|
| 12 |
"الديمقراطية",
|
| 13 |
"جمهورية مصر العربية"
|
| 14 |
],
|
|
|
|
| 15 |
"legal_nature": "مبدأ دستوري"
|
| 16 |
},
|
| 17 |
{
|
|
|
|
| 27 |
"الشريعة الإسلامية",
|
| 28 |
"التشريع"
|
| 29 |
],
|
|
|
|
| 30 |
"legal_nature": "مبدأ دستوري"
|
| 31 |
},
|
| 32 |
{
|
|
|
|
| 42 |
"اليهود",
|
| 43 |
"الشئون الدينية"
|
| 44 |
],
|
|
|
|
| 45 |
"legal_nature": "مبدأ دستوري"
|
| 46 |
},
|
| 47 |
{
|
|
|
|
| 58 |
"المساواة",
|
| 59 |
"تكافؤ الفرص"
|
| 60 |
],
|
|
|
|
| 61 |
"legal_nature": "مبدأ دستوري"
|
| 62 |
},
|
| 63 |
{
|
|
|
|
| 73 |
"الفصل بين السلطات",
|
| 74 |
"حقوق الإنسان"
|
| 75 |
],
|
|
|
|
| 76 |
"legal_nature": "مبدأ دستوري"
|
| 77 |
},
|
| 78 |
{
|
|
|
|
| 88 |
"القانون",
|
| 89 |
"اكتساب الجنسية"
|
| 90 |
],
|
|
|
|
| 91 |
"legal_nature": "حق أساسي/حرية"
|
| 92 |
},
|
| 93 |
{
|
|
|
|
| 103 |
"اللغة العربية",
|
| 104 |
"شيخ الأزهر"
|
| 105 |
],
|
|
|
|
| 106 |
"legal_nature": "مبدأ دستوري"
|
| 107 |
},
|
| 108 |
{
|
|
|
|
| 118 |
"التكافل الاجتماعي",
|
| 119 |
"الحياة الكريمة"
|
| 120 |
],
|
|
|
|
| 121 |
"legal_nature": "مبدأ دستوري"
|
| 122 |
},
|
| 123 |
{
|
|
|
|
| 132 |
"عدم التمييز",
|
| 133 |
"المواطنة"
|
| 134 |
],
|
|
|
|
| 135 |
"legal_nature": "مبدأ دستوري"
|
| 136 |
},
|
| 137 |
{
|
|
|
|
| 148 |
"الأخلاق",
|
| 149 |
"الوطنية"
|
| 150 |
],
|
|
|
|
| 151 |
"legal_nature": "مبدأ دستوري"
|
| 152 |
},
|
| 153 |
{
|
|
|
|
| 164 |
"المجالس النيابية",
|
| 165 |
"حماية الأمومة والطفولة"
|
| 166 |
],
|
|
|
|
| 167 |
"legal_nature": "حق أساسي/حرية"
|
| 168 |
},
|
| 169 |
{
|
|
|
|
| 179 |
"العمل الجبري",
|
| 180 |
"الخدمة العامة"
|
| 181 |
],
|
|
|
|
| 182 |
"legal_nature": "حق أساسي/حرية"
|
| 183 |
},
|
| 184 |
{
|
|
|
|
| 195 |
"السلامة المهنية",
|
| 196 |
"الفصل التعسفي"
|
| 197 |
],
|
|
|
|
| 198 |
"legal_nature": "حق أساسي/حرية"
|
| 199 |
},
|
| 200 |
{
|
|
|
|
| 210 |
"حقوق الموظفين",
|
| 211 |
"التأديب"
|
| 212 |
],
|
|
|
|
| 213 |
"legal_nature": "حق أساسي/حرية"
|
| 214 |
},
|
| 215 |
{
|
|
|
|
| 223 |
"الإضراب السلمي",
|
| 224 |
"القانون"
|
| 225 |
],
|
|
|
|
| 226 |
"legal_nature": "حق أساسي/حرية"
|
| 227 |
},
|
| 228 |
{
|
|
|
|
| 239 |
"رعاية الأسر",
|
| 240 |
"المجتمع المدني"
|
| 241 |
],
|
|
|
|
| 242 |
"legal_nature": "التزام/واجب على الدولة"
|
| 243 |
},
|
| 244 |
{
|
|
|
|
| 255 |
"العمالة غير المنتظمة",
|
| 256 |
"أموال التأمينات"
|
| 257 |
],
|
|
|
|
| 258 |
"legal_nature": "حق أساسي/حرية"
|
| 259 |
},
|
| 260 |
{
|
|
|
|
| 271 |
"الطوارئ",
|
| 272 |
"القطاع الصحي"
|
| 273 |
],
|
|
|
|
| 274 |
"legal_nature": "حق أساسي/حرية"
|
| 275 |
},
|
| 276 |
{
|
|
|
|
| 287 |
"الهوية الوطنية",
|
| 288 |
"جودة التعليم"
|
| 289 |
],
|
|
|
|
| 290 |
"legal_nature": "حق أساسي/حرية"
|
| 291 |
},
|
| 292 |
{
|
|
|
|
| 302 |
"سوق العمل",
|
| 303 |
"الجودة العالمية"
|
| 304 |
],
|
|
|
|
| 305 |
"legal_nature": "التزام/واجب على الدولة"
|
| 306 |
},
|
| 307 |
{
|
|
|
|
| 318 |
"الجامعات الأهلية",
|
| 319 |
"الجامعات الخاصة"
|
| 320 |
],
|
|
|
|
| 321 |
"legal_nature": "حق أساسي/حرية"
|
| 322 |
},
|
| 323 |
{
|
|
|
|
| 333 |
"حقوق المعلمين",
|
| 334 |
"جودة التعليم"
|
| 335 |
],
|
|
|
|
| 336 |
"legal_nature": "التزام/واجب على الدولة"
|
| 337 |
},
|
| 338 |
{
|
|
|
|
| 349 |
"اقتصاد المعرفة",
|
| 350 |
"المخترعين"
|
| 351 |
],
|
|
|
|
| 352 |
"legal_nature": "حق أساسي/حرية"
|
| 353 |
},
|
| 354 |
{
|
|
|
|
| 365 |
"حقوق الإنسان",
|
| 366 |
"الأخلاق المهنية"
|
| 367 |
],
|
|
|
|
| 368 |
"legal_nature": "مبدأ دستوري"
|
| 369 |
},
|
| 370 |
{
|
|
|
|
| 380 |
"المجتمع المدني",
|
| 381 |
"خطة شاملة"
|
| 382 |
],
|
|
|
|
| 383 |
"legal_nature": "التزام/واجب على الدولة"
|
| 384 |
},
|
| 385 |
{
|
|
|
|
| 393 |
"الرتب المدنية",
|
| 394 |
"حظر"
|
| 395 |
],
|
|
|
|
| 396 |
"legal_nature": "حظر دستوري"
|
| 397 |
},
|
| 398 |
{
|
|
|
|
| 409 |
"الشفافية",
|
| 410 |
"منع الاحتكار"
|
| 411 |
],
|
|
|
|
| 412 |
"legal_nature": "حق أساسي/حرية"
|
| 413 |
},
|
| 414 |
{
|
|
|
|
| 425 |
"المشروعات الصغيرة",
|
| 426 |
"القطاع غير الرسمي"
|
| 427 |
],
|
|
|
|
| 428 |
"legal_nature": "التزام/واجب على الدولة"
|
| 429 |
},
|
| 430 |
{
|
|
|
|
| 441 |
"دعم الفلاح",
|
| 442 |
"الإنتاج الزراعي"
|
| 443 |
],
|
|
|
|
| 444 |
"legal_nature": "التزام/واجب على الدولة"
|
| 445 |
},
|
| 446 |
{
|
|
|
|
| 455 |
"حماية الصيادين",
|
| 456 |
"البيئة"
|
| 457 |
],
|
|
|
|
| 458 |
"legal_nature": "التزام/واجب على الدولة"
|
| 459 |
},
|
| 460 |
{
|
|
|
|
| 469 |
"الأمن القومي",
|
| 470 |
"الاقتصاد الرقمي"
|
| 471 |
],
|
|
|
|
| 472 |
"legal_nature": "التزام/واجب على الدولة"
|
| 473 |
},
|
| 474 |
{
|
|
|
|
| 485 |
"الاستثمار",
|
| 486 |
"حقوق الأجيال القادمة"
|
| 487 |
],
|
|
|
|
| 488 |
"legal_nature": "مبدأ دستوري"
|
| 489 |
},
|
| 490 |
{
|
|
|
|
| 500 |
"الملكية التعاونية",
|
| 501 |
"حماية الملكية"
|
| 502 |
],
|
|
|
|
| 503 |
"legal_nature": "مبدأ دستوري"
|
| 504 |
},
|
| 505 |
{
|
|
|
|
| 513 |
"الملكية العامة",
|
| 514 |
"حرمة المال العام"
|
| 515 |
],
|
|
|
|
| 516 |
"legal_nature": "التزام/واجب على الدولة"
|
| 517 |
},
|
| 518 |
{
|
|
|
|
| 528 |
"نزع الملكية",
|
| 529 |
"التعويض العادل"
|
| 530 |
],
|
|
|
|
| 531 |
"legal_nature": "حق أساسي/حرية"
|
| 532 |
},
|
| 533 |
{
|
|
|
|
| 541 |
"القطاع الخاص",
|
| 542 |
"المسؤولية الاجتماعية"
|
| 543 |
],
|
|
|
|
| 544 |
"legal_nature": "التزام/واجب على الدولة"
|
| 545 |
},
|
| 546 |
{
|
|
|
|
| 555 |
"التعاونيات",
|
| 556 |
"استقلال التعاونيات"
|
| 557 |
],
|
|
|
|
| 558 |
"legal_nature": "حق أساسي/حرية"
|
| 559 |
},
|
| 560 |
{
|
|
|
|
| 570 |
"العدالة الاجتماعية",
|
| 571 |
"التهرب الضريبي"
|
| 572 |
],
|
|
|
|
| 573 |
"legal_nature": "التزام/واجب على الدولة"
|
| 574 |
},
|
| 575 |
{
|
|
|
|
| 583 |
"الادخار",
|
| 584 |
"حماية المدخرات"
|
| 585 |
],
|
|
|
|
| 586 |
"legal_nature": "التزام/واجب على الدولة"
|
| 587 |
},
|
| 588 |
{
|
|
|
|
| 597 |
"المصادرة الخاصة",
|
| 598 |
"حماية الأموال"
|
| 599 |
],
|
|
|
|
| 600 |
"legal_nature": "حظر دستوري"
|
| 601 |
},
|
| 602 |
{
|
|
|
|
| 612 |
"التنمية المستدامة",
|
| 613 |
"الموارد البشرية"
|
| 614 |
],
|
|
|
|
| 615 |
"legal_nature": "التزام/واجب على الدولة"
|
| 616 |
},
|
| 617 |
{
|
|
|
|
| 628 |
"القطاع العام",
|
| 629 |
"التعاونيات"
|
| 630 |
],
|
|
|
|
| 631 |
"legal_nature": "حقوق وواجبات"
|
| 632 |
},
|
| 633 |
{
|
|
|
|
| 642 |
"الممر المائي",
|
| 643 |
"التنمية الاقتصادية"
|
| 644 |
],
|
|
|
|
| 645 |
"legal_nature": "التزام/واجب على الدولة"
|
| 646 |
},
|
| 647 |
{
|
|
|
|
| 657 |
"المياه الجوفية",
|
| 658 |
"حماية البيئة النهرية"
|
| 659 |
],
|
|
|
|
| 660 |
"legal_nature": "حقوق وواجبات"
|
| 661 |
},
|
| 662 |
{
|
|
|
|
| 672 |
"الثروة السمكية والحيوانية",
|
| 673 |
"الرفق بالحيوان"
|
| 674 |
],
|
|
|
|
| 675 |
"legal_nature": "التزام/واجب على الدولة"
|
| 676 |
},
|
| 677 |
{
|
|
|
|
| 687 |
"حقوق الأجيال القادمة",
|
| 688 |
"الموارد الطبيعية"
|
| 689 |
],
|
|
|
|
| 690 |
"legal_nature": "حقوق وواجبات"
|
| 691 |
},
|
| 692 |
{
|
|
|
|
| 701 |
"الحضارة المصرية",
|
| 702 |
"التنوع الثقافي"
|
| 703 |
],
|
|
|
|
| 704 |
"legal_nature": "التزام/واجب على الدولة"
|
| 705 |
},
|
| 706 |
{
|
|
|
|
| 716 |
"المناطق النائية",
|
| 717 |
"الترجمة"
|
| 718 |
],
|
|
|
|
| 719 |
"legal_nature": "حق أساسي/حرية"
|
| 720 |
},
|
| 721 |
{
|
|
|
|
| 731 |
"حظر الاتجار",
|
| 732 |
"جريمة لا تسقط بالتقادم"
|
| 733 |
],
|
|
|
|
| 734 |
"legal_nature": "التزام/واجب على الدولة"
|
| 735 |
},
|
| 736 |
{
|
|
|
|
| 747 |
"التعددية الثقافية",
|
| 748 |
"الحضارة المصرية القديمة"
|
| 749 |
],
|
|
|
|
| 750 |
"legal_nature": "التزام/واجب على الدولة"
|
| 751 |
},
|
| 752 |
{
|
|
|
|
| 761 |
"حقوق الإنسان",
|
| 762 |
"احترام الدولة"
|
| 763 |
],
|
|
|
|
| 764 |
"legal_nature": "حق أساسي/حرية"
|
| 765 |
},
|
| 766 |
{
|
|
|
|
| 775 |
"جريمة",
|
| 776 |
"التقادم"
|
| 777 |
],
|
|
|
|
| 778 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 779 |
},
|
| 780 |
{
|
|
|
|
| 790 |
"مفوضية منع التمييز",
|
| 791 |
"الحض على الكراهية"
|
| 792 |
],
|
|
|
|
| 793 |
"legal_nature": "حق أساسي/حرية"
|
| 794 |
},
|
| 795 |
{
|
|
|
|
| 806 |
"حق الدفاع",
|
| 807 |
"التعويض"
|
| 808 |
],
|
|
|
|
| 809 |
"legal_nature": "حق أساسي/حرية"
|
| 810 |
},
|
| 811 |
{
|
|
|
|
| 821 |
"بطلان الاعتراف",
|
| 822 |
"حظر التعذيب"
|
| 823 |
],
|
|
|
|
| 824 |
"legal_nature": "حق أساسي/حرية"
|
| 825 |
},
|
| 826 |
{
|
|
|
|
| 836 |
"الإشراف القضائي",
|
| 837 |
"الرعاية اللاحقة"
|
| 838 |
],
|
|
|
|
| 839 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 840 |
},
|
| 841 |
{
|
|
|
|
| 851 |
"المراقبة",
|
| 852 |
"وسائل الاتصال"
|
| 853 |
],
|
|
|
|
| 854 |
"legal_nature": "حق أساسي/حرية"
|
| 855 |
},
|
| 856 |
{
|
|
|
|
| 865 |
"تفتيش المنازل",
|
| 866 |
"إذن قضائي"
|
| 867 |
],
|
|
|
|
| 868 |
"legal_nature": "حق أساسي/حرية"
|
| 869 |
},
|
| 870 |
{
|
|
|
|
| 879 |
"الأمن",
|
| 880 |
"الطمأنينة"
|
| 881 |
],
|
|
|
|
| 882 |
"legal_nature": "حق أساسي/حرية"
|
| 883 |
},
|
| 884 |
{
|
|
|
|
| 893 |
"الاتجار بالأعضاء",
|
| 894 |
"التجارب الطبية"
|
| 895 |
],
|
|
|
|
| 896 |
"legal_nature": "قاعدة إجرائية"
|
| 897 |
},
|
| 898 |
{
|
|
|
|
| 907 |
"زراعة الأعضاء",
|
| 908 |
"الوصية"
|
| 909 |
],
|
|
|
|
| 910 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 911 |
},
|
| 912 |
{
|
|
|
|
| 922 |
"المنع من السفر",
|
| 923 |
"الإبعاد"
|
| 924 |
],
|
|
|
|
| 925 |
"legal_nature": "حق أساسي/حرية"
|
| 926 |
},
|
| 927 |
{
|
|
|
|
| 936 |
"جريمة",
|
| 937 |
"التقادم"
|
| 938 |
],
|
|
|
|
| 939 |
"legal_nature": "حظر وتجريم"
|
| 940 |
},
|
| 941 |
{
|
|
|
|
| 951 |
"دور العبادة",
|
| 952 |
"الأديان السماوية"
|
| 953 |
],
|
|
|
|
| 954 |
"legal_nature": "حق أساسي/حرية"
|
| 955 |
},
|
| 956 |
{
|
|
|
|
| 966 |
"حرية التعبير",
|
| 967 |
"النشر"
|
| 968 |
],
|
|
|
|
| 969 |
"legal_nature": "حق أساسي/حرية"
|
| 970 |
},
|
| 971 |
{
|
|
|
|
| 980 |
"الباحثين",
|
| 981 |
"الابتكار"
|
| 982 |
],
|
|
|
|
| 983 |
"legal_nature": "حق أساسي/حرية"
|
| 984 |
},
|
| 985 |
{
|
|
|
|
| 995 |
"حظر الحبس في جرائم النشر",
|
| 996 |
"النيابة العامة"
|
| 997 |
],
|
|
|
|
| 998 |
"legal_nature": "حق أساسي/حرية"
|
| 999 |
},
|
| 1000 |
{
|
|
|
|
| 1010 |
"الوثائق القومية",
|
| 1011 |
"البيانات"
|
| 1012 |
],
|
|
|
|
| 1013 |
"legal_nature": "حق أساسي/حرية"
|
| 1014 |
},
|
| 1015 |
{
|
|
|
|
| 1023 |
"الملكية الفكرية",
|
| 1024 |
"حماية الحقوق"
|
| 1025 |
],
|
|
|
|
| 1026 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1027 |
},
|
| 1028 |
{
|
|
|
|
| 1038 |
"إصدار الصحف",
|
| 1039 |
"البث الإذاعي"
|
| 1040 |
],
|
|
|
|
| 1041 |
"legal_nature": "حق أساسي/حرية"
|
| 1042 |
},
|
| 1043 |
{
|
|
|
|
| 1053 |
"جرائم النشر",
|
| 1054 |
"وقت الحرب"
|
| 1055 |
],
|
|
|
|
| 1056 |
"legal_nature": "حظر دستوري"
|
| 1057 |
},
|
| 1058 |
{
|
|
|
|
| 1068 |
"الحياد",
|
| 1069 |
"تعدد الآراء"
|
| 1070 |
],
|
|
|
|
| 1071 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1072 |
},
|
| 1073 |
{
|
|
|
|
| 1083 |
"الاحتجاج السلمي",
|
| 1084 |
"الاجتماع الخاص"
|
| 1085 |
],
|
|
|
|
| 1086 |
"legal_nature": "حق أساسي/حرية"
|
| 1087 |
},
|
| 1088 |
{
|
|
|
|
| 1097 |
"حظر الأحزاب الدينية",
|
| 1098 |
"حل الأحزاب"
|
| 1099 |
],
|
|
|
|
| 1100 |
"legal_nature": "حق أساسي/حرية"
|
| 1101 |
},
|
| 1102 |
{
|
|
|
|
| 1112 |
"حرية النشاط",
|
| 1113 |
"حظر الجمعيات السرية"
|
| 1114 |
],
|
|
|
|
| 1115 |
"legal_nature": "حق أساسي/حرية"
|
| 1116 |
},
|
| 1117 |
{
|
|
|
|
| 1127 |
"استقلال النقابات",
|
| 1128 |
"الهيئات النظامية"
|
| 1129 |
],
|
|
|
|
| 1130 |
"legal_nature": "حق أساسي/حرية"
|
| 1131 |
},
|
| 1132 |
{
|
|
|
|
| 1141 |
"فرض الحراسة",
|
| 1142 |
"استقلال مهني"
|
| 1143 |
],
|
|
|
|
| 1144 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1145 |
},
|
| 1146 |
{
|
|
|
|
| 1156 |
"التخطيط العمراني",
|
| 1157 |
"العدالة الاجتماعية"
|
| 1158 |
],
|
|
|
|
| 1159 |
"legal_nature": "حقوق وواجبات"
|
| 1160 |
},
|
| 1161 |
{
|
|
|
|
| 1171 |
"السيادة الغذائية",
|
| 1172 |
"التنوع البيولوجي"
|
| 1173 |
],
|
|
|
|
| 1174 |
"legal_nature": "حق أساسي/حرية"
|
| 1175 |
},
|
| 1176 |
{
|
|
|
|
| 1187 |
"عمالة الأطفال",
|
| 1188 |
"قضاء الأحداث"
|
| 1189 |
],
|
|
|
|
| 1190 |
"legal_nature": "حق أساسي/حرية"
|
| 1191 |
},
|
| 1192 |
{
|
|
|
|
| 1202 |
"الدمج",
|
| 1203 |
"تخصيص فرص عمل"
|
| 1204 |
],
|
|
|
|
| 1205 |
"legal_nature": "حق أساسي/حرية"
|
| 1206 |
},
|
| 1207 |
{
|
|
|
|
| 1217 |
"التمكين",
|
| 1218 |
"العمل التطوعي"
|
| 1219 |
],
|
|
|
|
| 1220 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1221 |
},
|
| 1222 |
{
|
|
|
|
| 1231 |
"المعاشات",
|
| 1232 |
"رعاية المسنين"
|
| 1233 |
],
|
|
|
|
| 1234 |
"legal_nature": "حق أساسي/حرية"
|
| 1235 |
},
|
| 1236 |
{
|
|
|
|
| 1245 |
"الموهوبين رياضياً",
|
| 1246 |
"الهيئات الرياضية"
|
| 1247 |
],
|
|
|
|
| 1248 |
"legal_nature": "حق أساسي/حرية"
|
| 1249 |
},
|
| 1250 |
{
|
|
|
|
| 1258 |
"مخاطبة السلطات",
|
| 1259 |
"الأشخاص الاعتبارية"
|
| 1260 |
],
|
|
|
|
| 1261 |
"legal_nature": "حق أساسي/حرية"
|
| 1262 |
},
|
| 1263 |
{
|
|
|
|
| 1272 |
"الدفاع عن الوطن",
|
| 1273 |
"التجنيد الإجباري"
|
| 1274 |
],
|
|
|
|
| 1275 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1276 |
},
|
| 1277 |
{
|
|
|
|
| 1287 |
"نزاهة الانتخابات",
|
| 1288 |
"قاعدة بيانات الناخبين"
|
| 1289 |
],
|
|
|
|
| 1290 |
"legal_nature": "حقوق وواجبات"
|
| 1291 |
},
|
| 1292 |
{
|
|
|
|
| 1301 |
"تصويت المغتربين",
|
| 1302 |
"رعاية المصالح"
|
| 1303 |
],
|
|
|
|
| 1304 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1305 |
},
|
| 1306 |
{
|
|
|
|
| 1315 |
"الاتجار بالبشر",
|
| 1316 |
"الاستغلال القسري"
|
| 1317 |
],
|
|
|
|
| 1318 |
"legal_nature": "حظر دستوري"
|
| 1319 |
},
|
| 1320 |
{
|
|
|
|
| 1329 |
"استقلال الوقف",
|
| 1330 |
"شروط الواقف"
|
| 1331 |
],
|
|
|
|
| 1332 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1333 |
},
|
| 1334 |
{
|
|
|
|
| 1343 |
"حظر تسليم اللاجئين",
|
| 1344 |
"حقوق الإنسان"
|
| 1345 |
],
|
|
|
|
| 1346 |
"legal_nature": "حق أساسي/حرية"
|
| 1347 |
},
|
| 1348 |
{
|
|
|
|
| 1357 |
"عدم الانتقاص",
|
| 1358 |
"جوهر الحق"
|
| 1359 |
],
|
|
|
|
| 1360 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1361 |
},
|
| 1362 |
{
|
|
|
|
| 1371 |
"حقوق الإنسان",
|
| 1372 |
"قوة القانون"
|
| 1373 |
],
|
|
|
|
| 1374 |
"legal_nature": "التزام/واجب على الدولة"
|
| 1375 |
},
|
| 1376 |
{
|
|
|
|
| 1386 |
"حصانة القضاء",
|
| 1387 |
"حماية الحريات"
|
| 1388 |
],
|
|
|
|
| 1389 |
"legal_nature": "مبدأ دستوري"
|
| 1390 |
},
|
| 1391 |
{
|
|
|
|
| 1401 |
"عدم الرجعية",
|
| 1402 |
"حكم قضائي"
|
| 1403 |
],
|
|
|
|
| 1404 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1405 |
},
|
| 1406 |
{
|
|
|
|
| 1417 |
"استئناف الجنايات",
|
| 1418 |
"حماية الشهود"
|
| 1419 |
],
|
|
|
|
| 1420 |
"legal_nature": "حق أساسي/حرية"
|
| 1421 |
},
|
| 1422 |
{
|
|
|
|
| 1432 |
"حظر المحاكم الاستثنائية",
|
| 1433 |
"الرقابة القضائية"
|
| 1434 |
],
|
|
|
|
| 1435 |
"legal_nature": "حق أساسي/حرية"
|
| 1436 |
},
|
| 1437 |
{
|
|
|
|
| 1446 |
"استقلال المحاماة",
|
| 1447 |
"المساعدة القانونية"
|
| 1448 |
],
|
|
|
|
| 1449 |
"legal_nature": "حق أساسي/حرية"
|
| 1450 |
},
|
| 1451 |
{
|
|
|
|
| 1462 |
"التعويض العادل",
|
| 1463 |
"المجلس القومي لحقوق الإنسان"
|
| 1464 |
],
|
|
|
|
| 1465 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1466 |
},
|
| 1467 |
{
|
|
|
|
| 1478 |
"الموظف العام",
|
| 1479 |
"الدعوى الجنائية"
|
| 1480 |
],
|
|
|
|
| 1481 |
"legal_nature": "حق أساسي/حرية"
|
| 1482 |
},
|
| 1483 |
{
|
|
|
|
| 1493 |
"الموازنة العامة",
|
| 1494 |
"الرقابة"
|
| 1495 |
],
|
|
|
|
| 1496 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1497 |
},
|
| 1498 |
{
|
|
|
|
| 1509 |
"النظام الانتخابي",
|
| 1510 |
"التعيين الرئاسي"
|
| 1511 |
],
|
|
|
|
| 1512 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1513 |
},
|
| 1514 |
{
|
|
|
|
| 1522 |
"تفرغ العضو",
|
| 1523 |
"الاحتفاظ بالوظيفة"
|
| 1524 |
],
|
|
|
|
| 1525 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1526 |
},
|
| 1527 |
{
|
|
|
|
| 1535 |
"اليمين الدستورية",
|
| 1536 |
"واجبات العضو"
|
| 1537 |
],
|
|
|
|
| 1538 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1539 |
},
|
| 1540 |
{
|
|
|
|
| 1548 |
"مكافأة العضوية",
|
| 1549 |
"عدم سريان الزيادة"
|
| 1550 |
],
|
|
|
|
| 1551 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 1552 |
},
|
| 1553 |
{
|
|
|
|
| 1561 |
"مدة العضوية",
|
| 1562 |
"موعد الانتخابات"
|
| 1563 |
],
|
|
|
|
| 1564 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1565 |
},
|
| 1566 |
{
|
|
|
|
| 1575 |
"محكمة النقض",
|
| 1576 |
"الطعون الانتخابية"
|
| 1577 |
],
|
|
|
|
| 1578 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 1579 |
},
|
| 1580 |
{
|
|
|
|
| 1588 |
"خلو المقعد",
|
| 1589 |
"الانتخابات التكميلية"
|
| 1590 |
],
|
|
|
|
| 1591 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1592 |
},
|
| 1593 |
{
|
|
|
|
| 1602 |
"إقرار الذمة المالية",
|
| 1603 |
"الهدايا"
|
| 1604 |
],
|
|
|
|
| 1605 |
"legal_nature": "قاعدة إجرائية"
|
| 1606 |
},
|
| 1607 |
{
|
|
|
|
| 1616 |
"أغلبية الثلثين",
|
| 1617 |
"فقد الثقة"
|
| 1618 |
],
|
|
|
|
| 1619 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1620 |
},
|
| 1621 |
{
|
|
|
|
| 1629 |
"الاستقالة",
|
| 1630 |
"قبول الاستقالة"
|
| 1631 |
],
|
|
|
|
| 1632 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1633 |
},
|
| 1634 |
{
|
|
|
|
| 1642 |
"الحصانة البرلمانية",
|
| 1643 |
"حرية الرأي"
|
| 1644 |
],
|
|
|
|
| 1645 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1646 |
},
|
| 1647 |
{
|
|
|
|
| 1656 |
"إذن المجلس",
|
| 1657 |
"رفع الحصانة"
|
| 1658 |
],
|
|
|
|
| 1659 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1660 |
},
|
| 1661 |
{
|
|
|
|
| 1670 |
"انعقاد الجلسات",
|
| 1671 |
"بطلان الاجتماع"
|
| 1672 |
],
|
|
|
|
| 1673 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1674 |
},
|
| 1675 |
{
|
|
|
|
| 1684 |
"دعوة الرئيس",
|
| 1685 |
"اعتماد الموازنة"
|
| 1686 |
],
|
|
|
|
| 1687 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1688 |
},
|
| 1689 |
{
|
|
|
|
| 1697 |
"اجتماع غير عادي",
|
| 1698 |
"دعوة طارئة"
|
| 1699 |
],
|
|
|
|
| 1700 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1701 |
},
|
| 1702 |
{
|
|
|
|
| 1712 |
"مدة الرئاسة",
|
| 1713 |
"إعفاء الرئيس"
|
| 1714 |
],
|
|
|
|
| 1715 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1716 |
},
|
| 1717 |
{
|
|
|
|
| 1725 |
"اللائحة الداخلية",
|
| 1726 |
"تنظيم العمل"
|
| 1727 |
],
|
|
|
|
| 1728 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1729 |
},
|
| 1730 |
{
|
|
|
|
| 1738 |
"حفظ النظام",
|
| 1739 |
"رئيس المجلس"
|
| 1740 |
],
|
|
|
|
| 1741 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1742 |
},
|
| 1743 |
{
|
|
|
|
| 1751 |
"علنية الجلسات",
|
| 1752 |
"جلسة سرية"
|
| 1753 |
],
|
|
|
|
| 1754 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1755 |
},
|
| 1756 |
{
|
|
|
|
| 1766 |
"القوانين المكملة للدستور",
|
| 1767 |
"أغلبية الثلثين"
|
| 1768 |
],
|
|
|
|
| 1769 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 1770 |
},
|
| 1771 |
{
|
|
|
|
| 1780 |
"اللجان النوعية",
|
| 1781 |
"لجنة المقترحات"
|
| 1782 |
],
|
|
|
|
| 1783 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1784 |
},
|
| 1785 |
{
|
|
|
|
| 1794 |
"إصدار القوانين",
|
| 1795 |
"أغلبية الثلثين"
|
| 1796 |
],
|
|
|
|
| 1797 |
"legal_nature": "حق أساسي/حرية"
|
| 1798 |
},
|
| 1799 |
{
|
|
|
|
| 1809 |
"تعديل النفقات",
|
| 1810 |
"السنة المالية"
|
| 1811 |
],
|
|
|
|
| 1812 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1813 |
},
|
| 1814 |
{
|
|
|
|
| 1823 |
"الجهاز المركزي للمحاسبات",
|
| 1824 |
"الرقابة المالية"
|
| 1825 |
],
|
|
|
|
| 1826 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1827 |
},
|
| 1828 |
{
|
|
|
|
| 1836 |
"الأموال العامة",
|
| 1837 |
"التحصيل والصرف"
|
| 1838 |
],
|
|
|
|
| 1839 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1840 |
},
|
| 1841 |
{
|
|
|
|
| 1850 |
"الديون العامة",
|
| 1851 |
"موافقة البرلمان"
|
| 1852 |
],
|
|
|
|
| 1853 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1854 |
},
|
| 1855 |
{
|
|
|
|
| 1864 |
"المعاشات",
|
| 1865 |
"الخزانة العامة"
|
| 1866 |
],
|
|
|
|
| 1867 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 1868 |
},
|
| 1869 |
{
|
|
|
|
| 1877 |
"توجيه الأسئلة",
|
| 1878 |
"الرقابة البرلمانية"
|
| 1879 |
],
|
|
|
|
| 1880 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1881 |
},
|
| 1882 |
{
|
|
|
|
| 1890 |
"الاستجواب",
|
| 1891 |
"المحاسبة السياسية"
|
| 1892 |
],
|
|
|
|
| 1893 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1894 |
},
|
| 1895 |
{
|
|
|
|
| 1904 |
"استقالة الحكومة",
|
| 1905 |
"المسؤولية التضامنية"
|
| 1906 |
],
|
|
|
|
| 1907 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1908 |
},
|
| 1909 |
{
|
|
|
|
| 1917 |
"طلب مناقشة عامة",
|
| 1918 |
"استيضاح السياسة"
|
| 1919 |
],
|
|
|
|
| 1920 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1921 |
},
|
| 1922 |
{
|
|
|
|
| 1929 |
"keywords": [
|
| 1930 |
"اقتراح برغبة"
|
| 1931 |
],
|
|
|
|
| 1932 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1933 |
},
|
| 1934 |
{
|
|
|
|
| 1942 |
"طلب إحاطة",
|
| 1943 |
"بيان عاجل"
|
| 1944 |
],
|
|
|
|
| 1945 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1946 |
},
|
| 1947 |
{
|
|
|
|
| 1956 |
"جمع الأدلة",
|
| 1957 |
"حق المعلومات"
|
| 1958 |
],
|
|
|
|
| 1959 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1960 |
},
|
| 1961 |
{
|
|
|
|
| 1969 |
"حضور الحكومة",
|
| 1970 |
"الاستماع للوزراء"
|
| 1971 |
],
|
|
|
|
| 1972 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1973 |
},
|
| 1974 |
{
|
|
|
|
| 1983 |
"الاستفتاء الشعبي",
|
| 1984 |
"انتخابات مبكرة"
|
| 1985 |
],
|
|
|
|
| 1986 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 1987 |
},
|
| 1988 |
{
|
|
|
|
| 1997 |
"الشكاوى",
|
| 1998 |
"تواصل البرلمان"
|
| 1999 |
],
|
|
|
|
| 2000 |
"legal_nature": "حق أساسي/حرية"
|
| 2001 |
},
|
| 2002 |
{
|
|
|
|
| 2012 |
"استقلال الوطن",
|
| 2013 |
"احترام الدستور"
|
| 2014 |
],
|
|
|
|
| 2015 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2016 |
},
|
| 2017 |
{
|
|
|
|
| 2027 |
"إجراءات الانتخاب",
|
| 2028 |
"حظر المناصب الحزبية"
|
| 2029 |
],
|
|
|
|
| 2030 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2031 |
},
|
| 2032 |
{
|
|
|
|
| 2042 |
"السن القانوني",
|
| 2043 |
"الخدمة العسكرية"
|
| 2044 |
],
|
|
|
|
| 2045 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2046 |
},
|
| 2047 |
{
|
|
|
|
| 2056 |
"التوكيلات الشعبية",
|
| 2057 |
"شروط الترشح"
|
| 2058 |
],
|
|
|
|
| 2059 |
"legal_nature": "حق أساسي/حرية"
|
| 2060 |
},
|
| 2061 |
{
|
|
|
|
| 2069 |
"الاقتراع المباشر",
|
| 2070 |
"الأغلبية المطلقة"
|
| 2071 |
],
|
|
|
|
| 2072 |
"legal_nature": "قاعدة إجرائية"
|
| 2073 |
},
|
| 2074 |
{
|
|
|
|
| 2082 |
"اليمين الدستورية",
|
| 2083 |
"تولي المهام"
|
| 2084 |
],
|
|
|
|
| 2085 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2086 |
},
|
| 2087 |
{
|
|
|
|
| 2097 |
"إقرار الذمة المالية",
|
| 2098 |
"الهدايا"
|
| 2099 |
],
|
|
|
|
| 2100 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2101 |
},
|
| 2102 |
{
|
|
|
|
| 2112 |
"حل المجلس",
|
| 2113 |
"الوزارات السيادية"
|
| 2114 |
],
|
|
|
|
| 2115 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2116 |
},
|
| 2117 |
{
|
|
|
|
| 2126 |
"التعديل الوزاري",
|
| 2127 |
"موافقة البرلمان"
|
| 2128 |
],
|
|
|
|
| 2129 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2130 |
},
|
| 2131 |
{
|
|
|
|
| 2139 |
"تفويض السلطات",
|
| 2140 |
"التفويض الإداري"
|
| 2141 |
],
|
|
|
|
| 2142 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2143 |
},
|
| 2144 |
{
|
|
|
|
| 2152 |
"اجتماع الحكومة",
|
| 2153 |
"رئاسة الاجتماع"
|
| 2154 |
],
|
|
|
|
| 2155 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2156 |
},
|
| 2157 |
{
|
|
|
|
| 2165 |
"السياسة العامة",
|
| 2166 |
"بيان الرئيس"
|
| 2167 |
],
|
|
|
|
|
|
|
|
|
|
| 2168 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2169 |
},
|
| 2170 |
{
|
|
|
|
| 2179 |
"تعيين النائب",
|
| 2180 |
"اختصاصات النائب"
|
| 2181 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2182 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2183 |
},
|
| 2184 |
{
|
|
|
|
| 2194 |
"الاستفتاء الشعبي",
|
| 2195 |
"حظر التنازل عن الأرض"
|
| 2196 |
],
|
|
|
|
|
|
|
|
|
|
| 2197 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2198 |
},
|
| 2199 |
{
|
|
|
|
| 2209 |
"إرسال القوات",
|
| 2210 |
"مجلس الدفاع الوطني"
|
| 2211 |
],
|
|
|
|
| 2212 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2213 |
},
|
| 2214 |
{
|
|
|
|
| 2223 |
"الموظفين العموميين",
|
| 2224 |
"الدبلوماسيين"
|
| 2225 |
],
|
|
|
|
| 2226 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2227 |
},
|
| 2228 |
{
|
|
|
|
| 2237 |
"موافقة البرلمان",
|
| 2238 |
"تمديد الطوارئ"
|
| 2239 |
],
|
|
|
|
| 2240 |
"legal_nature": "تنظيم م��سسي/هيكلي"
|
| 2241 |
},
|
| 2242 |
{
|
|
|
|
| 2251 |
"تخفيف العقوبة",
|
| 2252 |
"العفو الشامل"
|
| 2253 |
],
|
|
|
|
| 2254 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2255 |
},
|
| 2256 |
{
|
|
|
|
| 2265 |
"الضرورة",
|
| 2266 |
"مراجعة البرلمان"
|
| 2267 |
],
|
|
|
|
| 2268 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2269 |
},
|
| 2270 |
{
|
|
|
|
| 2278 |
"الاستفتاء الشعبي",
|
| 2279 |
"المصالح العليا"
|
| 2280 |
],
|
|
|
|
|
|
|
|
|
|
| 2281 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2282 |
},
|
| 2283 |
{
|
|
|
|
| 2290 |
"keywords": [
|
| 2291 |
"استقالة الرئيس"
|
| 2292 |
],
|
|
|
|
| 2293 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2294 |
},
|
| 2295 |
{
|
|
|
|
| 2305 |
"المحكمة الخاصة",
|
| 2306 |
"العزل"
|
| 2307 |
],
|
|
|
|
| 2308 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2309 |
},
|
| 2310 |
{
|
|
|
|
| 2320 |
"انتخابات رئاسية مبكرة",
|
| 2321 |
"تقييد الصلاحيات"
|
| 2322 |
],
|
|
|
|
| 2323 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2324 |
},
|
| 2325 |
{
|
|
|
|
| 2335 |
"حل البرلمان",
|
| 2336 |
"الاستفتاء"
|
| 2337 |
],
|
|
|
|
| 2338 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2339 |
},
|
| 2340 |
{
|
|
|
|
| 2348 |
"أسبقية الانتخابات",
|
| 2349 |
"استمرار المجلس"
|
| 2350 |
],
|
|
|
|
| 2351 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2352 |
},
|
| 2353 |
{
|
|
|
|
| 2362 |
"رئيس مجلس الوزراء",
|
| 2363 |
"الهيئة التنفيذية"
|
| 2364 |
],
|
|
|
|
| 2365 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2366 |
},
|
| 2367 |
{
|
|
|
|
| 2377 |
"الجنسية",
|
| 2378 |
"حظر الجمع بين المناصب"
|
| 2379 |
],
|
|
|
|
| 2380 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2381 |
},
|
| 2382 |
{
|
|
|
|
| 2390 |
"اليمين الدستورية",
|
| 2391 |
"تولي المهام"
|
| 2392 |
],
|
|
|
|
| 2393 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2394 |
},
|
| 2395 |
{
|
|
|
|
| 2405 |
"تعارض المصالح",
|
| 2406 |
"حظر الأعمال التجارية"
|
| 2407 |
],
|
|
|
|
| 2408 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2409 |
},
|
| 2410 |
{
|
|
|
|
| 2421 |
"الأمن القومي",
|
| 2422 |
"إعداد القوانين"
|
| 2423 |
],
|
|
|
|
| 2424 |
"legal_nature": "حق أساسي/حرية"
|
| 2425 |
},
|
| 2426 |
{
|
|
|
|
| 2435 |
"الوكيل الدائم",
|
| 2436 |
"الاستقرار المؤسسي"
|
| 2437 |
],
|
|
|
|
| 2438 |
"legal_nature": "ضمان/حماية"
|
| 2439 |
},
|
| 2440 |
{
|
|
|
|
| 2449 |
"مجلس النواب",
|
| 2450 |
"المساءلة"
|
| 2451 |
],
|
|
|
|
| 2452 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2453 |
},
|
| 2454 |
{
|
|
|
|
| 2463 |
"التفويض",
|
| 2464 |
"رئيس الوزراء"
|
| 2465 |
],
|
|
|
|
| 2466 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2467 |
},
|
| 2468 |
{
|
|
|
|
| 2477 |
"المصالح العامة",
|
| 2478 |
"قرارات تنظيمية"
|
| 2479 |
],
|
|
|
|
| 2480 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2481 |
},
|
| 2482 |
{
|
|
|
|
| 2490 |
"لوائح الضبط",
|
| 2491 |
"النظام العام"
|
| 2492 |
],
|
|
|
|
| 2493 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2494 |
},
|
| 2495 |
{
|
|
|
|
| 2504 |
"الجرائم الوظيفية",
|
| 2505 |
"الخيانة العظمى"
|
| 2506 |
],
|
|
|
|
|
|
|
|
|
|
| 2507 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2508 |
},
|
| 2509 |
{
|
|
|
|
| 2516 |
"keywords": [
|
| 2517 |
"الاستقالة"
|
| 2518 |
],
|
|
|
|
| 2519 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2520 |
},
|
| 2521 |
{
|
|
|
|
| 2531 |
"المحافظات",
|
| 2532 |
"الوحدات المحلية"
|
| 2533 |
],
|
|
|
|
| 2534 |
"legal_nature": "قاعدة إجرائية"
|
| 2535 |
},
|
| 2536 |
{
|
|
|
|
| 2545 |
"نقل السلطات",
|
| 2546 |
"المرافق المحلية"
|
| 2547 |
],
|
|
|
|
| 2548 |
"legal_nature": "التزام/واجب على الدولة"
|
| 2549 |
},
|
| 2550 |
{
|
|
|
|
| 2559 |
"توزيع الموارد",
|
| 2560 |
"التنمية المحلية"
|
| 2561 |
],
|
|
|
|
| 2562 |
"legal_nature": "التزام/واجب على الدولة"
|
| 2563 |
},
|
| 2564 |
{
|
|
|
|
| 2573 |
"الضرائب المحلية",
|
| 2574 |
"الموارد المالية"
|
| 2575 |
],
|
|
|
|
| 2576 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 2577 |
},
|
| 2578 |
{
|
|
|
|
| 2587 |
"رؤساء الوحدات المحلية",
|
| 2588 |
"التعيين والانتخاب"
|
| 2589 |
],
|
|
|
|
| 2590 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2591 |
},
|
| 2592 |
{
|
|
|
|
| 2603 |
"الرقابة المحلية",
|
| 2604 |
"سحب الثقة"
|
| 2605 |
],
|
|
|
|
| 2606 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 2607 |
},
|
| 2608 |
{
|
|
|
|
| 2618 |
"تنازع الاختصاص",
|
| 2619 |
"مجلس الدولة"
|
| 2620 |
],
|
|
|
|
| 2621 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2622 |
},
|
| 2623 |
{
|
|
|
|
| 2631 |
"الموازنة المحلية",
|
| 2632 |
"الحساب الختامي"
|
| 2633 |
],
|
|
|
|
| 2634 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2635 |
},
|
| 2636 |
{
|
|
|
|
| 2644 |
"حل المجالس المحلية",
|
| 2645 |
"الحظر الإداري"
|
| 2646 |
],
|
|
|
|
| 2647 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2648 |
},
|
| 2649 |
{
|
|
|
|
| 2659 |
"التدخل في العدالة",
|
| 2660 |
"عدم التقادم"
|
| 2661 |
],
|
|
|
|
| 2662 |
"legal_nature": "مبدأ دستوري"
|
| 2663 |
},
|
| 2664 |
{
|
|
|
|
| 2674 |
"تعيين الرؤساء",
|
| 2675 |
"المجلس الأعلى للجهات القضائية"
|
| 2676 |
],
|
|
|
|
| 2677 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2678 |
},
|
| 2679 |
{
|
|
|
|
| 2689 |
"المساءلة التأديبية",
|
| 2690 |
"الندب"
|
| 2691 |
],
|
|
|
|
| 2692 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2693 |
},
|
| 2694 |
{
|
|
|
|
| 2703 |
"النطق بالحكم",
|
| 2704 |
"النظام العام"
|
| 2705 |
],
|
|
|
|
| 2706 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2707 |
},
|
| 2708 |
{
|
|
|
|
| 2717 |
"مجلس القضاء الأعلى",
|
| 2718 |
"الفصل في المنازعات"
|
| 2719 |
],
|
|
|
|
| 2720 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2721 |
},
|
| 2722 |
{
|
|
|
|
| 2732 |
"الدعوى الجنائية",
|
| 2733 |
"تعيين النائب العام"
|
| 2734 |
],
|
|
|
|
| 2735 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2736 |
},
|
| 2737 |
{
|
|
|
|
| 2747 |
"الإفتاء القانوني",
|
| 2748 |
"مراجعة التشريعات"
|
| 2749 |
],
|
|
|
|
| 2750 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2751 |
},
|
| 2752 |
{
|
|
|
|
| 2762 |
"الموازنة المستقلة",
|
| 2763 |
"الجمعية العامة"
|
| 2764 |
],
|
|
|
|
| 2765 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2766 |
},
|
| 2767 |
{
|
|
|
|
| 2777 |
"تنازع الاختصاص",
|
| 2778 |
"الأحكام المتناقضة"
|
| 2779 |
],
|
|
|
|
| 2780 |
"legal_nature": "اختصاص قضائي/محكمة"
|
| 2781 |
},
|
| 2782 |
{
|
|
|
|
| 2792 |
"تعيين القضاة",
|
| 2793 |
"رئيس الجمهورية"
|
| 2794 |
],
|
|
|
|
| 2795 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2796 |
},
|
| 2797 |
{
|
|
|
|
| 2806 |
"استقلال القضاة",
|
| 2807 |
"المساءلة التأديبية"
|
| 2808 |
],
|
|
|
|
| 2809 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2810 |
},
|
| 2811 |
{
|
|
|
|
| 2821 |
"إلزامية الأحكام",
|
| 2822 |
"عدم الدستورية"
|
| 2823 |
],
|
|
|
|
| 2824 |
"legal_nature": "قيمة تعليمية/تثقيفية/ثقافية"
|
| 2825 |
},
|
| 2826 |
{
|
|
|
|
| 2837 |
"صياغة العقود",
|
| 2838 |
"التسوية الودية"
|
| 2839 |
],
|
|
|
|
| 2840 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2841 |
},
|
| 2842 |
{
|
|
|
|
| 2853 |
"الجزاءات التأديبية",
|
| 2854 |
"مجلس الدولة"
|
| 2855 |
],
|
|
|
|
| 2856 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2857 |
},
|
| 2858 |
{
|
|
|
|
| 2869 |
"استقلال المحاماة",
|
| 2870 |
"جهات التحقيق"
|
| 2871 |
],
|
|
|
|
| 2872 |
"legal_nature": "حق أساسي/حرية"
|
| 2873 |
},
|
| 2874 |
{
|
|
|
|
| 2885 |
"استقلال الخبراء",
|
| 2886 |
"الحماية القانونية"
|
| 2887 |
],
|
|
|
|
| 2888 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2889 |
},
|
| 2890 |
{
|
|
|
|
| 2901 |
"المجلس الأعلى للقوات المسلحة",
|
| 2902 |
"صون الدستور"
|
| 2903 |
],
|
|
|
|
| 2904 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2905 |
},
|
| 2906 |
{
|
|
|
|
| 2915 |
"القائد العام",
|
| 2916 |
"التعيين"
|
| 2917 |
],
|
|
|
|
| 2918 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2919 |
},
|
| 2920 |
{
|
|
|
|
| 2930 |
"اللجان القضائية العسكرية",
|
| 2931 |
"المنازعات الإدارية"
|
| 2932 |
],
|
|
|
|
| 2933 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 2934 |
},
|
| 2935 |
{
|
|
|
|
| 2945 |
"تأمين البلاد",
|
| 2946 |
"رقم واحد"
|
| 2947 |
],
|
|
|
|
| 2948 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2949 |
},
|
| 2950 |
{
|
|
|
|
| 2960 |
"الجرائم العسكرية",
|
| 2961 |
"استقلال القضاء"
|
| 2962 |
],
|
|
|
|
| 2963 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 2964 |
},
|
| 2965 |
{
|
|
|
|
| 2975 |
"إدارة الأزمات",
|
| 2976 |
"الكوارث"
|
| 2977 |
],
|
|
|
|
| 2978 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 2979 |
},
|
| 2980 |
{
|
|
|
|
| 2989 |
"النظام العام",
|
| 2990 |
"الأمن"
|
| 2991 |
],
|
|
|
|
| 2992 |
"legal_nature": "حق أساسي/حرية"
|
| 2993 |
},
|
| 2994 |
{
|
|
|
|
| 3004 |
"المجلس الأعلى",
|
| 3005 |
"الأمن"
|
| 3006 |
],
|
|
|
|
| 3007 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3008 |
},
|
| 3009 |
{
|
|
|
|
| 3019 |
"المجلس الأعلى لتنظيم الإعلام",
|
| 3020 |
"الصحافة"
|
| 3021 |
],
|
|
|
|
| 3022 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3023 |
},
|
| 3024 |
{
|
|
|
|
| 3031 |
"keywords": [
|
| 3032 |
"الصحافة"
|
| 3033 |
],
|
|
|
|
| 3034 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3035 |
},
|
| 3036 |
{
|
|
|
|
| 3045 |
"الإعلام",
|
| 3046 |
"المجلس الأعلى لتنظيم الإعلام"
|
| 3047 |
],
|
|
|
|
| 3048 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3049 |
},
|
| 3050 |
{
|
|
|
|
| 3061 |
"ذوي الإعاقة",
|
| 3062 |
"الاستقلال المالي والإداري"
|
| 3063 |
],
|
|
|
|
| 3064 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3065 |
},
|
| 3066 |
{
|
|
|
|
| 3077 |
"الرقابة الإدارية",
|
| 3078 |
"الجهاز المركزي للمحاسبات"
|
| 3079 |
],
|
|
|
|
| 3080 |
"legal_nature": "إحالة للتنظيم التشريعي"
|
| 3081 |
},
|
| 3082 |
{
|
|
|
|
| 3092 |
"الاستقلال والحياد",
|
| 3093 |
"موافقة البرلمان"
|
| 3094 |
],
|
|
|
|
| 3095 |
"legal_nature": "حظر دستوري"
|
| 3096 |
},
|
| 3097 |
{
|
|
|
|
| 3107 |
"الرقابة البرلمانية",
|
| 3108 |
"إبلاغ النيابة"
|
| 3109 |
],
|
|
|
|
| 3110 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3111 |
},
|
| 3112 |
{
|
|
|
|
| 3122 |
"حماية المال العام",
|
| 3123 |
"الاستراتيجية الوطنية"
|
| 3124 |
],
|
|
|
|
| 3125 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3126 |
},
|
| 3127 |
{
|
|
|
|
| 3136 |
"الرقابة المالية",
|
| 3137 |
"الموازنة العامة"
|
| 3138 |
],
|
|
|
|
| 3139 |
"legal_nature": "مبدأ اقتصادي/مالي"
|
| 3140 |
},
|
| 3141 |
{
|
|
|
|
| 3151 |
"إصدار النقد",
|
| 3152 |
"استقرار الأسعار"
|
| 3153 |
],
|
|
|
|
| 3154 |
"legal_nature": "حق أساسي/حرية"
|
| 3155 |
},
|
| 3156 |
{
|
|
|
|
| 3166 |
"سوق المال",
|
| 3167 |
"التأمين"
|
| 3168 |
],
|
|
|
|
| 3169 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3170 |
},
|
| 3171 |
{
|
|
|
|
| 3179 |
"العاصمة",
|
| 3180 |
"القاهرة"
|
| 3181 |
],
|
|
|
|
| 3182 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3183 |
},
|
| 3184 |
{
|
|
|
|
| 3194 |
"شعار الجمهورية",
|
| 3195 |
"إهانة العلم"
|
| 3196 |
],
|
|
|
|
| 3197 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3198 |
},
|
| 3199 |
{
|
|
|
|
| 3208 |
"القوانين السابقة",
|
| 3209 |
"إصدار القوانين"
|
| 3210 |
],
|
|
|
|
|
|
|
|
|
|
| 3211 |
"legal_nature": "حكم انتقالي"
|
| 3212 |
},
|
| 3213 |
{
|
|
|
|
| 3223 |
"الأثر الرجعي",
|
| 3224 |
"تاريخ العمل بالقانون"
|
| 3225 |
],
|
|
|
|
| 3226 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3227 |
},
|
| 3228 |
{
|
|
|
|
| 3238 |
"حظر التعديل",
|
| 3239 |
"إجراءات التعديل"
|
| 3240 |
],
|
|
|
|
| 3241 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3242 |
},
|
| 3243 |
{
|
|
|
|
| 3251 |
"وحدة الدستور",
|
| 3252 |
"التكامل"
|
| 3253 |
],
|
|
|
|
| 3254 |
"legal_nature": "مبدأ تفسيري"
|
| 3255 |
},
|
| 3256 |
{
|
|
|
|
| 3265 |
"الهيئة الوطنية للانتخابات",
|
| 3266 |
"إشراف قضائي"
|
| 3267 |
],
|
|
|
|
| 3268 |
"legal_nature": "حكم انتقالي"
|
| 3269 |
},
|
| 3270 |
{
|
|
|
|
| 3277 |
"keywords": [
|
| 3278 |
"انتخابات مجلس النواب"
|
| 3279 |
],
|
|
|
|
|
|
|
|
|
|
| 3280 |
"legal_nature": "حكم انتقالي"
|
| 3281 |
},
|
| 3282 |
{
|
|
|
|
| 3290 |
"مواعيد الانتخابات",
|
| 3291 |
"إجراءات انتقالية"
|
| 3292 |
],
|
|
|
|
| 3293 |
"legal_nature": "حكم انتقالي"
|
| 3294 |
},
|
| 3295 |
{
|
|
|
|
| 3303 |
"مدة الرئاسة",
|
| 3304 |
"إعلان النتيجة"
|
| 3305 |
],
|
|
|
|
| 3306 |
"legal_nature": "حكم انتقالي"
|
| 3307 |
},
|
| 3308 |
{
|
|
|
|
| 3316 |
"الرئيس المؤقت",
|
| 3317 |
"تسليم السلطة"
|
| 3318 |
],
|
|
|
|
| 3319 |
"legal_nature": "حكم انتقالي"
|
| 3320 |
},
|
| 3321 |
{
|
|
|
|
| 3329 |
"خلو المنصب",
|
| 3330 |
"الرئيس المؤقت"
|
| 3331 |
],
|
|
|
|
| 3332 |
"legal_nature": "حكم انتقالي"
|
| 3333 |
},
|
| 3334 |
{
|
|
|
|
| 3343 |
"المجلس الأعلى للقوات المسلحة",
|
| 3344 |
"التعيين"
|
| 3345 |
],
|
|
|
|
| 3346 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3347 |
},
|
| 3348 |
{
|
|
|
|
| 3357 |
"حرية العقيدة",
|
| 3358 |
"المسيحيين"
|
| 3359 |
],
|
|
|
|
| 3360 |
"legal_nature": "التزام تشريعي"
|
| 3361 |
},
|
| 3362 |
{
|
|
|
|
| 3372 |
"المناطق الحدودية",
|
| 3373 |
"العدالة الاجتماعية"
|
| 3374 |
],
|
|
|
|
| 3375 |
"legal_nature": "حق أساسي/حرية"
|
| 3376 |
},
|
| 3377 |
{
|
|
|
|
| 3386 |
"تمويل الإرهاب",
|
| 3387 |
"تعويض الأضرار"
|
| 3388 |
],
|
|
|
|
| 3389 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3390 |
},
|
| 3391 |
{
|
|
|
|
| 3401 |
"الصحة",
|
| 3402 |
"التعليم الإلزامي"
|
| 3403 |
],
|
|
|
|
| 3404 |
"legal_nature": "حكم انتقالي"
|
| 3405 |
},
|
| 3406 |
{
|
|
|
|
| 3414 |
"ندب القضاة",
|
| 3415 |
"استقلال القضاء"
|
| 3416 |
],
|
|
|
|
| 3417 |
"legal_nature": "حكم انتقالي"
|
| 3418 |
},
|
| 3419 |
{
|
|
|
|
| 3427 |
"استئناف الجنايات",
|
| 3428 |
"درجات التقاضي"
|
| 3429 |
],
|
|
|
|
| 3430 |
"legal_nature": "حكم انتقالي"
|
| 3431 |
},
|
| 3432 |
{
|
|
|
|
| 3441 |
"المصالحة الوطنية",
|
| 3442 |
"تعويض الضحايا"
|
| 3443 |
],
|
|
|
|
| 3444 |
"legal_nature": "التزام تشريعي"
|
| 3445 |
},
|
| 3446 |
{
|
|
|
|
| 3454 |
"مدة الرئاسة",
|
| 3455 |
"تعديلات 2019"
|
| 3456 |
],
|
|
|
|
| 3457 |
"legal_nature": "قاعدة إجرائية"
|
| 3458 |
},
|
| 3459 |
{
|
|
|
|
| 3467 |
"الإدارة المحلية",
|
| 3468 |
"تطبيق تدريجي"
|
| 3469 |
],
|
|
|
|
|
|
|
|
|
|
| 3470 |
"legal_nature": "حكم انتقالي"
|
| 3471 |
},
|
| 3472 |
{
|
|
|
|
| 3480 |
"تمثيل العمال والفلاحين",
|
| 3481 |
"مجلس النواب"
|
| 3482 |
],
|
|
|
|
| 3483 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3484 |
},
|
| 3485 |
{
|
|
|
|
| 3496 |
"ذوي الإعاقة",
|
| 3497 |
"المصريين بالخارج"
|
| 3498 |
],
|
|
|
|
| 3499 |
"legal_nature": "التزام/واجب على الدولة"
|
| 3500 |
},
|
| 3501 |
{
|
|
|
|
| 3509 |
"كوتا المرأة",
|
| 3510 |
"سريان التعديلات"
|
| 3511 |
],
|
|
|
|
|
|
|
|
|
|
| 3512 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3513 |
},
|
| 3514 |
{
|
|
|
|
| 3523 |
"مجلس النواب",
|
| 3524 |
"الموظفين"
|
| 3525 |
],
|
|
|
|
| 3526 |
"legal_nature": "حكم انتقالي"
|
| 3527 |
},
|
| 3528 |
{
|
|
|
|
| 3536 |
"إلغاء دستوري",
|
| 3537 |
"الإعلان الدستوري"
|
| 3538 |
],
|
|
|
|
| 3539 |
"legal_nature": "قيمة تعليمية/تثقيفية/ثقافية"
|
| 3540 |
},
|
| 3541 |
{
|
|
|
|
| 3549 |
"نفاذ الدستور",
|
| 3550 |
"الاستفتاء الشعبي"
|
| 3551 |
],
|
|
|
|
| 3552 |
"legal_nature": "غير محدد / يحتاج مراجعة بشرية"
|
| 3553 |
},
|
| 3554 |
{
|
|
|
|
| 3564 |
"السلام الاجتماعي",
|
| 3565 |
"الحقوق والحريات"
|
| 3566 |
],
|
|
|
|
| 3567 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3568 |
},
|
| 3569 |
{
|
|
|
|
| 3580 |
"المعاهدات الدولية",
|
| 3581 |
"السياسة العامة"
|
| 3582 |
],
|
|
|
|
| 3583 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3584 |
},
|
| 3585 |
{
|
|
|
|
| 3595 |
"التعيين",
|
| 3596 |
"مدة العضوية"
|
| 3597 |
],
|
|
|
|
| 3598 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3599 |
},
|
| 3600 |
{
|
|
|
|
| 3610 |
"السن القانوني",
|
| 3611 |
"النظام الانتخابي"
|
| 3612 |
],
|
|
|
|
| 3613 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3614 |
},
|
| 3615 |
{
|
|
|
|
| 3623 |
"حظر الجمع",
|
| 3624 |
"مجلس النواب"
|
| 3625 |
],
|
|
|
|
| 3626 |
"legal_nature": "حظر دستوري"
|
| 3627 |
},
|
| 3628 |
{
|
|
|
|
| 3637 |
"الحكومة",
|
| 3638 |
"علاقة السلطات"
|
| 3639 |
],
|
|
|
|
| 3640 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3641 |
},
|
| 3642 |
{
|
|
|
|
| 3652 |
"الواجبات",
|
| 3653 |
"الإجراءات"
|
| 3654 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3655 |
"legal_nature": "تنظيم مؤسسي/هيكلي"
|
| 3656 |
}
|
|
|
|
|
|
|
| 3657 |
]
|
data/Egyptian_Labour_Law.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/Egyptian_Personal Status Laws.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/Technology Crimes Law.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
data/قانون_الإجراءات_الجنائية.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
evaluate.py
DELETED
|
@@ -1,620 +0,0 @@
|
|
| 1 |
-
# -*- coding: utf-8 -*-
|
| 2 |
-
"""
|
| 3 |
-
RAGAS Evaluation Script for Constitutional Legal Assistant
|
| 4 |
-
Evaluates: faithfulness, answer_relevancy, context_precision, context_recall
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import os
|
| 8 |
-
import json
|
| 9 |
-
from dotenv import load_dotenv
|
| 10 |
-
import logging
|
| 11 |
-
import warnings
|
| 12 |
-
|
| 13 |
-
# Suppress progress bars
|
| 14 |
-
os.environ['TRANSFORMERS_NO_PROGRESS_BAR'] = '1'
|
| 15 |
-
warnings.filterwarnings('ignore')
|
| 16 |
-
|
| 17 |
-
# Core imports
|
| 18 |
-
from langchain_core.documents import Document
|
| 19 |
-
from langchain_core.retrievers import BaseRetriever
|
| 20 |
-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 21 |
-
from typing import List
|
| 22 |
-
from rank_bm25 import BM25Okapi
|
| 23 |
-
import numpy as np
|
| 24 |
-
|
| 25 |
-
# Vector Store & Embeddings
|
| 26 |
-
from langchain_chroma import Chroma
|
| 27 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 28 |
-
|
| 29 |
-
# Reranker
|
| 30 |
-
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 31 |
-
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 32 |
-
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 33 |
-
|
| 34 |
-
# LLM
|
| 35 |
-
from langchain_groq import ChatGroq
|
| 36 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 37 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 38 |
-
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
|
| 39 |
-
|
| 40 |
-
# Evaluation
|
| 41 |
-
from datasets import Dataset
|
| 42 |
-
from ragas import evaluate
|
| 43 |
-
from ragas.metrics import (
|
| 44 |
-
faithfulness,
|
| 45 |
-
answer_relevancy,
|
| 46 |
-
context_precision,
|
| 47 |
-
context_recall,
|
| 48 |
-
)
|
| 49 |
-
from ragas.llms import LangchainLLMWrapper
|
| 50 |
-
from ragas.embeddings import LangchainEmbeddingsWrapper
|
| 51 |
-
|
| 52 |
-
# Configure logging
|
| 53 |
-
logging.basicConfig(level=logging.INFO)
|
| 54 |
-
logger = logging.getLogger(__name__)
|
| 55 |
-
|
| 56 |
-
load_dotenv()
|
| 57 |
-
|
| 58 |
-
# ==========================================
|
| 59 |
-
# 🚀 RAG PIPELINE INITIALIZATION
|
| 60 |
-
# ==========================================
|
| 61 |
-
|
| 62 |
-
def initialize_rag_pipeline():
|
| 63 |
-
"""Initialize the RAG pipeline for constitutional legal questions"""
|
| 64 |
-
print("🔄 Initializing RAG pipeline...")
|
| 65 |
-
print("📥 Loading data...")
|
| 66 |
-
|
| 67 |
-
# 1. Load JSON
|
| 68 |
-
json_path = "Egyptian_Constitution_legalnature_only.json"
|
| 69 |
-
if not os.path.exists(json_path):
|
| 70 |
-
raise FileNotFoundError(f"File not found: {json_path}")
|
| 71 |
-
|
| 72 |
-
with open(json_path, "r", encoding="utf-8") as f:
|
| 73 |
-
data = json.load(f)
|
| 74 |
-
|
| 75 |
-
# Create article mapping for cross-references
|
| 76 |
-
article_map = {str(item['article_number']): item for item in data}
|
| 77 |
-
|
| 78 |
-
docs = []
|
| 79 |
-
for item in data:
|
| 80 |
-
# Build cross-reference section
|
| 81 |
-
cross_ref_text = ""
|
| 82 |
-
if item.get('cross_references') and len(item['cross_references']) > 0:
|
| 83 |
-
cross_ref_text = "\nالمواد ذات الصلة (المراجع المتقاطعة): " + ", ".join(
|
| 84 |
-
[f"المادة {ref}" for ref in item['cross_references']]
|
| 85 |
-
)
|
| 86 |
-
|
| 87 |
-
# Construct document content
|
| 88 |
-
page_content = f"""
|
| 89 |
-
رقم المادة: {item['article_number']}
|
| 90 |
-
النص الأصلي: {item['original_text']}
|
| 91 |
-
الشرح المبسط: {item['simplified_summary']}{cross_ref_text}
|
| 92 |
-
"""
|
| 93 |
-
|
| 94 |
-
metadata = {
|
| 95 |
-
"article_id": item['article_id'],
|
| 96 |
-
"article_number": str(item['article_number']),
|
| 97 |
-
"legal_nature": item['legal_nature'],
|
| 98 |
-
"keywords": ", ".join(item['keywords']),
|
| 99 |
-
"part": item.get('part (Bab)', ''),
|
| 100 |
-
"chapter": item.get('chapter (Fasl)', ''),
|
| 101 |
-
"cross_references": ", ".join([str(ref) for ref in item.get('cross_references', [])])
|
| 102 |
-
}
|
| 103 |
-
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 104 |
-
|
| 105 |
-
print(f"✅ Loaded {len(docs)} constitutional articles")
|
| 106 |
-
|
| 107 |
-
# 2. Embeddings
|
| 108 |
-
print("Loading embeddings model...")
|
| 109 |
-
embeddings = HuggingFaceEmbeddings(
|
| 110 |
-
model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1"
|
| 111 |
-
)
|
| 112 |
-
print("✅ Embeddings ready")
|
| 113 |
-
|
| 114 |
-
# 3. Vector Store
|
| 115 |
-
print("Building vector database...")
|
| 116 |
-
vectorstore = Chroma.from_documents(
|
| 117 |
-
docs,
|
| 118 |
-
embeddings,
|
| 119 |
-
persist_directory="chroma_db"
|
| 120 |
-
)
|
| 121 |
-
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 15})
|
| 122 |
-
print("✅ Vector database ready")
|
| 123 |
-
|
| 124 |
-
# 4. BM25 Keyword Retriever
|
| 125 |
-
class BM25Retriever(BaseRetriever):
|
| 126 |
-
"""BM25-based keyword retriever"""
|
| 127 |
-
corpus_docs: List[Document]
|
| 128 |
-
bm25: BM25Okapi = None
|
| 129 |
-
k: int = 15
|
| 130 |
-
|
| 131 |
-
class Config:
|
| 132 |
-
arbitrary_types_allowed = True
|
| 133 |
-
|
| 134 |
-
def __init__(self, **data):
|
| 135 |
-
super().__init__(**data)
|
| 136 |
-
tokenized_corpus = [doc.page_content.split() for doc in self.corpus_docs]
|
| 137 |
-
self.bm25 = BM25Okapi(tokenized_corpus)
|
| 138 |
-
|
| 139 |
-
def _get_relevant_documents(
|
| 140 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 141 |
-
) -> List[Document]:
|
| 142 |
-
tokenized_query = query.split()
|
| 143 |
-
scores = self.bm25.get_scores(tokenized_query)
|
| 144 |
-
top_indices = np.argsort(scores)[::-1][:self.k]
|
| 145 |
-
return [self.corpus_docs[i] for i in top_indices if scores[i] > 0]
|
| 146 |
-
|
| 147 |
-
async def _aget_relevant_documents(self, query: str, **kwargs) -> List[Document]:
|
| 148 |
-
return self._get_relevant_documents(query)
|
| 149 |
-
|
| 150 |
-
bm25_retriever = BM25Retriever(corpus_docs=docs, k=15)
|
| 151 |
-
print("✅ BM25 retriever ready")
|
| 152 |
-
|
| 153 |
-
# 5. Metadata Filter Retriever
|
| 154 |
-
class MetadataFilterRetriever(BaseRetriever):
|
| 155 |
-
"""Metadata-based filtering retriever"""
|
| 156 |
-
corpus_docs: List[Document]
|
| 157 |
-
k: int = 15
|
| 158 |
-
|
| 159 |
-
class Config:
|
| 160 |
-
arbitrary_types_allowed = True
|
| 161 |
-
|
| 162 |
-
def _get_relevant_documents(
|
| 163 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 164 |
-
) -> List[Document]:
|
| 165 |
-
query_lower = query.lower()
|
| 166 |
-
scored_docs = []
|
| 167 |
-
|
| 168 |
-
for doc in self.corpus_docs:
|
| 169 |
-
score = 0
|
| 170 |
-
keywords = doc.metadata.get('keywords', '').lower()
|
| 171 |
-
if any(word in keywords for word in query_lower.split()):
|
| 172 |
-
score += 3
|
| 173 |
-
|
| 174 |
-
legal_nature = doc.metadata.get('legal_nature', '').lower()
|
| 175 |
-
if any(word in legal_nature for word in query_lower.split()):
|
| 176 |
-
score += 2
|
| 177 |
-
|
| 178 |
-
part = doc.metadata.get('part', '').lower()
|
| 179 |
-
chapter = doc.metadata.get('chapter', '').lower()
|
| 180 |
-
if any(word in part or word in chapter for word in query_lower.split()):
|
| 181 |
-
score += 1
|
| 182 |
-
|
| 183 |
-
if any(word in doc.page_content.lower() for word in query_lower.split()):
|
| 184 |
-
score += 1
|
| 185 |
-
|
| 186 |
-
if score > 0:
|
| 187 |
-
scored_docs.append((doc, score))
|
| 188 |
-
|
| 189 |
-
scored_docs.sort(key=lambda x: x[1], reverse=True)
|
| 190 |
-
return [doc for doc, _ in scored_docs[:self.k]]
|
| 191 |
-
|
| 192 |
-
async def _aget_relevant_documents(self, query: str, **kwargs) -> List[Document]:
|
| 193 |
-
return self._get_relevant_documents(query)
|
| 194 |
-
|
| 195 |
-
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs, k=15)
|
| 196 |
-
print("✅ Metadata retriever ready")
|
| 197 |
-
|
| 198 |
-
# 6. Hybrid RRF Retriever
|
| 199 |
-
class HybridRRFRetriever(BaseRetriever):
|
| 200 |
-
"""Combines semantic, BM25, and metadata using Reciprocal Rank Fusion"""
|
| 201 |
-
semantic_retriever: BaseRetriever
|
| 202 |
-
bm25_retriever: BM25Retriever
|
| 203 |
-
metadata_retriever: MetadataFilterRetriever
|
| 204 |
-
beta_semantic: float = 0.5
|
| 205 |
-
beta_keyword: float = 0.3
|
| 206 |
-
beta_metadata: float = 0.2
|
| 207 |
-
k: int = 60
|
| 208 |
-
top_k: int = 15
|
| 209 |
-
|
| 210 |
-
class Config:
|
| 211 |
-
arbitrary_types_allowed = True
|
| 212 |
-
|
| 213 |
-
def _get_relevant_documents(
|
| 214 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 215 |
-
) -> List[Document]:
|
| 216 |
-
semantic_docs = self.semantic_retriever.invoke(query)
|
| 217 |
-
bm25_docs = self.bm25_retriever.invoke(query)
|
| 218 |
-
metadata_docs = self.metadata_retriever.invoke(query)
|
| 219 |
-
|
| 220 |
-
rrf_scores = {}
|
| 221 |
-
|
| 222 |
-
for rank, doc in enumerate(semantic_docs, start=1):
|
| 223 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 224 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_semantic / (self.k + rank)
|
| 225 |
-
|
| 226 |
-
for rank, doc in enumerate(bm25_docs, start=1):
|
| 227 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 228 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_keyword / (self.k + rank)
|
| 229 |
-
|
| 230 |
-
for rank, doc in enumerate(metadata_docs, start=1):
|
| 231 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 232 |
-
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + self.beta_metadata / (self.k + rank)
|
| 233 |
-
|
| 234 |
-
all_docs = {}
|
| 235 |
-
for doc in semantic_docs + bm25_docs + metadata_docs:
|
| 236 |
-
doc_id = doc.metadata.get('article_number', str(hash(doc.page_content)))
|
| 237 |
-
if doc_id not in all_docs:
|
| 238 |
-
all_docs[doc_id] = doc
|
| 239 |
-
|
| 240 |
-
sorted_doc_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 241 |
-
result_docs = []
|
| 242 |
-
for doc_id, score in sorted_doc_ids[:self.top_k]:
|
| 243 |
-
if doc_id in all_docs:
|
| 244 |
-
result_docs.append(all_docs[doc_id])
|
| 245 |
-
|
| 246 |
-
return result_docs
|
| 247 |
-
|
| 248 |
-
async def _aget_relevant_documents(self, query: str, **kwargs) -> List[Document]:
|
| 249 |
-
return self._get_relevant_documents(query)
|
| 250 |
-
|
| 251 |
-
hybrid_retriever = HybridRRFRetriever(
|
| 252 |
-
semantic_retriever=base_retriever,
|
| 253 |
-
bm25_retriever=bm25_retriever,
|
| 254 |
-
metadata_retriever=metadata_retriever,
|
| 255 |
-
beta_semantic=0.5,
|
| 256 |
-
beta_keyword=0.3,
|
| 257 |
-
beta_metadata=0.2,
|
| 258 |
-
k=60,
|
| 259 |
-
top_k=20
|
| 260 |
-
)
|
| 261 |
-
print("✅ Hybrid RRF retriever ready (β: semantic=0.5, keyword=0.3, metadata=0.2)")
|
| 262 |
-
|
| 263 |
-
# 7. Cross-Reference Retriever
|
| 264 |
-
class CrossReferenceRetriever(BaseRetriever):
|
| 265 |
-
"""Enhances retrieval by fetching cross-referenced articles"""
|
| 266 |
-
base_retriever: BaseRetriever
|
| 267 |
-
article_map: dict
|
| 268 |
-
|
| 269 |
-
class Config:
|
| 270 |
-
arbitrary_types_allowed = True
|
| 271 |
-
|
| 272 |
-
def _get_relevant_documents(
|
| 273 |
-
self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None
|
| 274 |
-
) -> List[Document]:
|
| 275 |
-
initial_docs = self.base_retriever.invoke(query)
|
| 276 |
-
|
| 277 |
-
all_article_numbers = set()
|
| 278 |
-
for doc in initial_docs:
|
| 279 |
-
if 'article_number' in doc.metadata:
|
| 280 |
-
all_article_numbers.add(doc.metadata['article_number'])
|
| 281 |
-
cross_refs_str = doc.metadata.get('cross_references', '')
|
| 282 |
-
if cross_refs_str:
|
| 283 |
-
cross_refs = [ref.strip() for ref in cross_refs_str.split(',')]
|
| 284 |
-
for ref in cross_refs:
|
| 285 |
-
if ref:
|
| 286 |
-
all_article_numbers.add(str(ref))
|
| 287 |
-
|
| 288 |
-
enhanced_docs = []
|
| 289 |
-
seen_numbers = set()
|
| 290 |
-
|
| 291 |
-
for doc in initial_docs:
|
| 292 |
-
enhanced_docs.append(doc)
|
| 293 |
-
seen_numbers.add(doc.metadata.get('article_number'))
|
| 294 |
-
|
| 295 |
-
for article_num in all_article_numbers:
|
| 296 |
-
if article_num not in seen_numbers and article_num in self.article_map:
|
| 297 |
-
article_data = self.article_map[article_num]
|
| 298 |
-
cross_ref_text = ""
|
| 299 |
-
if article_data.get('cross_references'):
|
| 300 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join(
|
| 301 |
-
[f"المادة {ref}" for ref in article_data['cross_references']]
|
| 302 |
-
)
|
| 303 |
-
|
| 304 |
-
page_content = f"""
|
| 305 |
-
رقم المادة: {article_data['article_number']}
|
| 306 |
-
النص الأصلي: {article_data['original_text']}
|
| 307 |
-
الشرح المبسط: {article_data['simplified_summary']}{cross_ref_text}
|
| 308 |
-
"""
|
| 309 |
-
|
| 310 |
-
enhanced_doc = Document(
|
| 311 |
-
page_content=page_content,
|
| 312 |
-
metadata={
|
| 313 |
-
"article_id": article_data['article_id'],
|
| 314 |
-
"article_number": str(article_data['article_number']),
|
| 315 |
-
"legal_nature": article_data['legal_nature'],
|
| 316 |
-
"keywords": ", ".join(article_data['keywords']),
|
| 317 |
-
"cross_references": ", ".join([str(ref) for ref in article_data.get('cross_references', [])])
|
| 318 |
-
}
|
| 319 |
-
)
|
| 320 |
-
enhanced_docs.append(enhanced_doc)
|
| 321 |
-
seen_numbers.add(article_num)
|
| 322 |
-
|
| 323 |
-
return enhanced_docs
|
| 324 |
-
|
| 325 |
-
async def _aget_relevant_documents(self, query: str, **kwargs) -> List[Document]:
|
| 326 |
-
return self._get_relevant_documents(query)
|
| 327 |
-
|
| 328 |
-
cross_ref_retriever = CrossReferenceRetriever(
|
| 329 |
-
base_retriever=hybrid_retriever,
|
| 330 |
-
article_map=article_map
|
| 331 |
-
)
|
| 332 |
-
print("✅ Cross-reference retriever ready")
|
| 333 |
-
|
| 334 |
-
# 8. Reranker
|
| 335 |
-
print("Loading reranker model...")
|
| 336 |
-
local_model_path = r"D:\FOE\Senior 2\Graduation Project\Chatbot_me\reranker"
|
| 337 |
-
|
| 338 |
-
if not os.path.exists(local_model_path):
|
| 339 |
-
raise FileNotFoundError(f"Reranker path not found: {local_model_path}")
|
| 340 |
-
|
| 341 |
-
model = HuggingFaceCrossEncoder(model_name=local_model_path)
|
| 342 |
-
compressor = CrossEncoderReranker(model=model, top_n=5)
|
| 343 |
-
|
| 344 |
-
compression_retriever = ContextualCompressionRetriever(
|
| 345 |
-
base_compressor=compressor,
|
| 346 |
-
base_retriever=cross_ref_retriever
|
| 347 |
-
)
|
| 348 |
-
print("✅ Reranker ready (top_n=5)")
|
| 349 |
-
|
| 350 |
-
# 9. LLM Configuration
|
| 351 |
-
llm = ChatGroq(
|
| 352 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 353 |
-
model_name="llama-3.1-8b-instant",
|
| 354 |
-
temperature=0.3,
|
| 355 |
-
model_kwargs={"top_p": 0.9}
|
| 356 |
-
)
|
| 357 |
-
|
| 358 |
-
# 10. Prompt Template
|
| 359 |
-
system_instructions = """
|
| 360 |
-
<role>
|
| 361 |
-
أنت "المساعد القانوني الذكي"، خبير متخصص في الدستور المصري والقوانين الإجرائية.
|
| 362 |
-
مهمتك: تقديم إجابات دقيقة بناءً على "السياق التشريعي" المرفق أولاً، أو تقديم نصائح إجرائية عامة عند الضرورة.
|
| 363 |
-
</role>
|
| 364 |
-
|
| 365 |
-
<decision_logic>
|
| 366 |
-
عليك تحليل "سؤال المستخدم" و"السياق التشريعي" وتصنيف الحالة واختيار الرد المناسب:
|
| 367 |
-
|
| 368 |
-
🔴 الحالة الأولى: (الإجابة موجودة في السياق التشريعي)
|
| 369 |
-
- استخرج الإجابة من السياق فقط
|
| 370 |
-
- ابدأ الإجابة مباشرة دون مقدمات
|
| 371 |
-
- وثق الإجابة برقم المادة
|
| 372 |
-
- توقف، لا تضف معلومات خارجية
|
| 373 |
-
|
| 374 |
-
🟡 الحالة ��لثانية: (السياق فارغ + السؤال إجرائي/عملي)
|
| 375 |
-
- استخدم معرفتك العامة بالقانون المصري
|
| 376 |
-
- ابدأ بـ: "بناءً على الإجراءات القانونية العامة في مصر:"
|
| 377 |
-
- قدم الخطوات في نقاط مرقمة
|
| 378 |
-
|
| 379 |
-
🔵 الحالة الثالثة: (السياق فارغ + سؤال دستوري)
|
| 380 |
-
- قل: "عذراً، لم يرد ذكر لهذا في المواد المسترجاعة"
|
| 381 |
-
- لا تخترع نصوصاً دستورية
|
| 382 |
-
|
| 383 |
-
🟢 الحالة الرابعة: (تحية/شكر)
|
| 384 |
-
- رد بتحية مهذبة مختصرة
|
| 385 |
-
|
| 386 |
-
⚫ الحالة الخامسة: (خارج النطاق)
|
| 387 |
-
- اعتذر بلطف ووجه للقانون
|
| 388 |
-
</decision_logic>
|
| 389 |
-
|
| 390 |
-
<formatting_rules>
|
| 391 |
-
- استخدم فقرات قصيرة واترك سطراً فارغاً بينها
|
| 392 |
-
- التزم باللغة العربية الفصحى المبسطة
|
| 393 |
-
</formatting_rules>
|
| 394 |
-
"""
|
| 395 |
-
|
| 396 |
-
prompt = ChatPromptTemplate.from_messages([
|
| 397 |
-
("system", system_instructions),
|
| 398 |
-
("system", "السياق التشريعي المتاح:\n{context}"),
|
| 399 |
-
("human", "السؤال:\n{input}")
|
| 400 |
-
])
|
| 401 |
-
|
| 402 |
-
# 11. Build QA Chain
|
| 403 |
-
qa_chain = (
|
| 404 |
-
RunnableParallel({
|
| 405 |
-
"context": compression_retriever,
|
| 406 |
-
"input": RunnablePassthrough()
|
| 407 |
-
})
|
| 408 |
-
.assign(answer=(
|
| 409 |
-
prompt
|
| 410 |
-
| llm
|
| 411 |
-
| StrOutputParser()
|
| 412 |
-
))
|
| 413 |
-
)
|
| 414 |
-
|
| 415 |
-
print("✅ RAG pipeline initialized!\n")
|
| 416 |
-
return qa_chain
|
| 417 |
-
|
| 418 |
-
# ==========================================
|
| 419 |
-
# 📊 RAGAS EVALUATION
|
| 420 |
-
# ==========================================
|
| 421 |
-
|
| 422 |
-
def run_evaluation(test_file="test_dataset.json", output_file="evaluation_results.json"):
|
| 423 |
-
"""Run RAGAS evaluation on test dataset"""
|
| 424 |
-
|
| 425 |
-
print("\n" + "="*60)
|
| 426 |
-
print("📊 RAGAS EVALUATION")
|
| 427 |
-
print("="*60)
|
| 428 |
-
|
| 429 |
-
# Load test dataset
|
| 430 |
-
print(f"\n📂 Loading test dataset: {test_file}")
|
| 431 |
-
with open(test_file, "r", encoding="utf-8") as f:
|
| 432 |
-
test_questions = json.load(f)
|
| 433 |
-
print(f"✅ Loaded {len(test_questions)} test questions")
|
| 434 |
-
|
| 435 |
-
# Initialize RAG pipeline
|
| 436 |
-
print("\n📥 Initializing RAG pipeline...")
|
| 437 |
-
qa_chain = initialize_rag_pipeline()
|
| 438 |
-
|
| 439 |
-
# Generate answers
|
| 440 |
-
print("\n🤖 Generating answers for evaluation...")
|
| 441 |
-
results = {
|
| 442 |
-
"question": [],
|
| 443 |
-
"answer": [],
|
| 444 |
-
"contexts": [],
|
| 445 |
-
"ground_truth": []
|
| 446 |
-
}
|
| 447 |
-
|
| 448 |
-
for idx, item in enumerate(test_questions, 1):
|
| 449 |
-
question = item["question"]
|
| 450 |
-
ground_truth = item.get("ground_truth", "")
|
| 451 |
-
|
| 452 |
-
print(f" [{idx}/{len(test_questions)}] Processing question {idx}...")
|
| 453 |
-
|
| 454 |
-
try:
|
| 455 |
-
result = qa_chain.invoke(question)
|
| 456 |
-
answer = result["answer"]
|
| 457 |
-
contexts = [doc.page_content for doc in result["context"]]
|
| 458 |
-
|
| 459 |
-
results["question"].append(question)
|
| 460 |
-
results["answer"].append(answer)
|
| 461 |
-
results["contexts"].append(contexts)
|
| 462 |
-
results["ground_truth"].append(ground_truth)
|
| 463 |
-
|
| 464 |
-
except Exception as e:
|
| 465 |
-
print(f" ❌ Error: {str(e)[:100]}")
|
| 466 |
-
results["question"].append(question)
|
| 467 |
-
results["answer"].append("Error generating answer")
|
| 468 |
-
results["contexts"].append([])
|
| 469 |
-
results["ground_truth"].append(ground_truth)
|
| 470 |
-
|
| 471 |
-
# Run Ragas evaluation
|
| 472 |
-
print("\n⚙️ Running RAGAS metrics...")
|
| 473 |
-
dataset = Dataset.from_dict(results)
|
| 474 |
-
|
| 475 |
-
# Configure evaluation LLM (same as main app)
|
| 476 |
-
print(" 📌 Using Groq (llama-3.1-8b-instant, temp=0.3, top_p=0.9)")
|
| 477 |
-
evaluator_llm = LangchainLLMWrapper(ChatGroq(
|
| 478 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 479 |
-
model_name="llama-3.1-8b-instant",
|
| 480 |
-
temperature=0.3,
|
| 481 |
-
model_kwargs={"top_p": 0.9},
|
| 482 |
-
max_retries=2
|
| 483 |
-
))
|
| 484 |
-
|
| 485 |
-
# Configure evaluation embeddings (same as main app)
|
| 486 |
-
print(" 📌 Using HuggingFace (Omartificial-Intelligence-Space/GATE-AraBert-v1)")
|
| 487 |
-
evaluator_embeddings = LangchainEmbeddingsWrapper(HuggingFaceEmbeddings(
|
| 488 |
-
model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1"
|
| 489 |
-
))
|
| 490 |
-
|
| 491 |
-
try:
|
| 492 |
-
import time
|
| 493 |
-
print("\n ⏳ Evaluating each question separately with all metrics...")
|
| 494 |
-
print(" ⚠️ This will take ~10-15 minutes (120 sec delay between questions)\n")
|
| 495 |
-
|
| 496 |
-
# Evaluate each question separately to see results immediately
|
| 497 |
-
all_scores = {
|
| 498 |
-
"faithfulness": [],
|
| 499 |
-
"answer_relevancy": [],
|
| 500 |
-
"context_precision": [],
|
| 501 |
-
"context_recall": []
|
| 502 |
-
}
|
| 503 |
-
|
| 504 |
-
for q_idx in range(len(results["question"])):
|
| 505 |
-
print(f"\n 📋 Question {q_idx + 1}/{len(results['question'])}: {results['question'][q_idx][:60]}...")
|
| 506 |
-
|
| 507 |
-
# Create single-question dataset
|
| 508 |
-
single_q_data = {
|
| 509 |
-
"question": [results["question"][q_idx]],
|
| 510 |
-
"answer": [results["answer"][q_idx]],
|
| 511 |
-
"contexts": [results["contexts"][q_idx]],
|
| 512 |
-
"ground_truth": [results["ground_truth"][q_idx]]
|
| 513 |
-
}
|
| 514 |
-
single_dataset = Dataset.from_dict(single_q_data)
|
| 515 |
-
|
| 516 |
-
# Evaluate all metrics for this question
|
| 517 |
-
try:
|
| 518 |
-
q_result = evaluate(
|
| 519 |
-
single_dataset,
|
| 520 |
-
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
|
| 521 |
-
llm=evaluator_llm,
|
| 522 |
-
embeddings=evaluator_embeddings,
|
| 523 |
-
raise_exceptions=False
|
| 524 |
-
)
|
| 525 |
-
|
| 526 |
-
# Extract scores (handle if they're lists or single values)
|
| 527 |
-
def get_score(value):
|
| 528 |
-
if isinstance(value, list):
|
| 529 |
-
return value[0] if len(value) > 0 else 0.0
|
| 530 |
-
return float(value) if value is not None else 0.0
|
| 531 |
-
|
| 532 |
-
f_score = get_score(q_result['faithfulness'])
|
| 533 |
-
a_score = get_score(q_result['answer_relevancy'])
|
| 534 |
-
cp_score = get_score(q_result['context_precision'])
|
| 535 |
-
cr_score = get_score(q_result['context_recall'])
|
| 536 |
-
|
| 537 |
-
# Display scores for this question
|
| 538 |
-
print(f" Faithfulness : {f_score:.4f}")
|
| 539 |
-
print(f" Answer Relevancy : {a_score:.4f}")
|
| 540 |
-
print(f" Context Precision : {cp_score:.4f}")
|
| 541 |
-
print(f" Context Recall : {cr_score:.4f}")
|
| 542 |
-
|
| 543 |
-
all_scores["faithfulness"].append(f_score)
|
| 544 |
-
all_scores["answer_relevancy"].append(a_score)
|
| 545 |
-
all_scores["context_precision"].append(cp_score)
|
| 546 |
-
all_scores["context_recall"].append(cr_score)
|
| 547 |
-
|
| 548 |
-
except Exception as e:
|
| 549 |
-
print(f" ❌ Error evaluating this question: {str(e)[:80]}")
|
| 550 |
-
all_scores["faithfulness"].append(0.0)
|
| 551 |
-
all_scores["answer_relevancy"].append(0.0)
|
| 552 |
-
all_scores["context_precision"].append(0.0)
|
| 553 |
-
all_scores["context_recall"].append(0.0)
|
| 554 |
-
|
| 555 |
-
# Wait between questions to avoid rate limits
|
| 556 |
-
if q_idx < len(results["question"]) - 1:
|
| 557 |
-
print(f"\n ⏳ Waiting 120 seconds (2 min) before next question...")
|
| 558 |
-
time.sleep(120)
|
| 559 |
-
|
| 560 |
-
# Calculate average scores
|
| 561 |
-
eval_results = {
|
| 562 |
-
"faithfulness": sum(all_scores["faithfulness"]) / len(all_scores["faithfulness"]) if all_scores["faithfulness"] else 0.0,
|
| 563 |
-
"answer_relevancy": sum(all_scores["answer_relevancy"]) / len(all_scores["answer_relevancy"]) if all_scores["answer_relevancy"] else 0.0,
|
| 564 |
-
"context_precision": sum(all_scores["context_precision"]) / len(all_scores["context_precision"]) if all_scores["context_precision"] else 0.0,
|
| 565 |
-
"context_recall": sum(all_scores["context_recall"]) / len(all_scores["context_recall"]) if all_scores["context_recall"] else 0.0
|
| 566 |
-
}
|
| 567 |
-
|
| 568 |
-
# Display results
|
| 569 |
-
print("\n" + "="*60)
|
| 570 |
-
print("📈 EVALUATION RESULTS")
|
| 571 |
-
print("="*60)
|
| 572 |
-
|
| 573 |
-
for metric, score in eval_results.items():
|
| 574 |
-
if isinstance(score, (int, float)):
|
| 575 |
-
print(f" {metric:28s}: {score:.4f}")
|
| 576 |
-
|
| 577 |
-
# Save results to JSON
|
| 578 |
-
with open(output_file, "w", encoding="utf-8") as f:
|
| 579 |
-
results_dict = {
|
| 580 |
-
"metrics": {k: float(v) if isinstance(v, (int, float)) else str(v)
|
| 581 |
-
for k, v in eval_results.items()},
|
| 582 |
-
"test_samples": len(dataset),
|
| 583 |
-
"test_file": test_file
|
| 584 |
-
}
|
| 585 |
-
json.dump(results_dict, f, ensure_ascii=False, indent=2)
|
| 586 |
-
|
| 587 |
-
print(f"\n💾 Results saved to: {output_file}")
|
| 588 |
-
print("="*60 + "\n")
|
| 589 |
-
|
| 590 |
-
return eval_results
|
| 591 |
-
|
| 592 |
-
except Exception as e:
|
| 593 |
-
print(f"\n❌ Evaluation failed: {e}")
|
| 594 |
-
print("\n⚠️ Make sure:")
|
| 595 |
-
print(" 1. GROQ_API_KEY is set in .env")
|
| 596 |
-
print(" 2. You have valid Groq API credits")
|
| 597 |
-
print(" 3. Internet connection is available")
|
| 598 |
-
return None
|
| 599 |
-
|
| 600 |
-
# ==========================================
|
| 601 |
-
# 🎯 MAIN EXECUTION
|
| 602 |
-
# ==========================================
|
| 603 |
-
|
| 604 |
-
if __name__ == "__main__":
|
| 605 |
-
import sys
|
| 606 |
-
|
| 607 |
-
test_file = "test_dataset.json"
|
| 608 |
-
output_file = "evaluation_results.json"
|
| 609 |
-
|
| 610 |
-
# Check for command line arguments
|
| 611 |
-
if len(sys.argv) > 1:
|
| 612 |
-
test_file = sys.argv[1]
|
| 613 |
-
if len(sys.argv) > 2:
|
| 614 |
-
output_file = sys.argv[2]
|
| 615 |
-
|
| 616 |
-
print("\n" + "="*60)
|
| 617 |
-
print("🚀 Constitutional Legal Assistant - RAGAS Evaluation")
|
| 618 |
-
print("="*60)
|
| 619 |
-
|
| 620 |
-
run_evaluation(test_file, output_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evaluate_rag.py
CHANGED
|
@@ -46,8 +46,8 @@ from langchain_groq import ChatGroq
|
|
| 46 |
from langchain_huggingface import HuggingFaceEmbeddings
|
| 47 |
import logging
|
| 48 |
|
| 49 |
-
# Import the RAG pipeline initialization
|
| 50 |
-
from
|
| 51 |
|
| 52 |
# Suppress verbose API logging
|
| 53 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
@@ -200,21 +200,18 @@ def run_evaluation():
|
|
| 200 |
print(f"{'-'*60}")
|
| 201 |
|
| 202 |
try:
|
| 203 |
-
#
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
# Extract context text from documents
|
| 210 |
-
contexts = [doc.page_content for doc in context_docs]
|
| 211 |
-
|
| 212 |
# Store results
|
| 213 |
results["question"].append(question)
|
| 214 |
results["answer"].append(answer)
|
| 215 |
results["contexts"].append(contexts)
|
| 216 |
results["ground_truth"].append(ground_truth)
|
| 217 |
-
|
| 218 |
print(f"✅ Generated answer ({len(answer)} chars)")
|
| 219 |
print(f"✅ Retrieved {len(contexts)} context documents")
|
| 220 |
|
|
|
|
| 46 |
from langchain_huggingface import HuggingFaceEmbeddings
|
| 47 |
import logging
|
| 48 |
|
| 49 |
+
# Import the RAG pipeline initialization from the main RAG module
|
| 50 |
+
from rag import initialize_rag_pipeline, ask
|
| 51 |
|
| 52 |
# Suppress verbose API logging
|
| 53 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
|
|
| 200 |
print(f"{'-'*60}")
|
| 201 |
|
| 202 |
try:
|
| 203 |
+
# Use rag.ask which returns (answer, sources)
|
| 204 |
+
answer, sources = ask(question)
|
| 205 |
+
|
| 206 |
+
# sources is a list of dicts produced by rag._docs_to_sources
|
| 207 |
+
contexts = [s.get("content", "") for s in sources]
|
| 208 |
+
|
|
|
|
|
|
|
|
|
|
| 209 |
# Store results
|
| 210 |
results["question"].append(question)
|
| 211 |
results["answer"].append(answer)
|
| 212 |
results["contexts"].append(contexts)
|
| 213 |
results["ground_truth"].append(ground_truth)
|
| 214 |
+
|
| 215 |
print(f"✅ Generated answer ({len(answer)} chars)")
|
| 216 |
print(f"✅ Retrieved {len(contexts)} context documents")
|
| 217 |
|
evaluation_results.json
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"metrics": {
|
| 3 |
-
"faithfulness": 0.8375,
|
| 4 |
-
"answer_relevancy": 0.4021723436510297,
|
| 5 |
-
"context_precision": 0.9999999999583333,
|
| 6 |
-
"context_recall": NaN },
|
| 7 |
-
"test_samples": 2,
|
| 8 |
-
"test_file": "test_dataset.json"
|
| 9 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
models.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
from pydantic import BaseModel, Field
|
| 2 |
-
from typing import List, Optional, Dict, Any
|
| 3 |
-
|
| 4 |
-
class AskRequest(BaseModel):
|
| 5 |
-
question: str
|
| 6 |
-
|
| 7 |
-
class SourceItem(BaseModel):
|
| 8 |
-
law_key: Optional[str] = None
|
| 9 |
-
law_name: Optional[str] = None
|
| 10 |
-
article_number: Optional[str] = None
|
| 11 |
-
legal_nature: Optional[str] = None
|
| 12 |
-
keywords: Optional[str] = None
|
| 13 |
-
content: Optional[str] = None
|
| 14 |
-
metadata: Optional[Dict[str, Any]] = None
|
| 15 |
-
|
| 16 |
-
class ArticleItem(BaseModel):
|
| 17 |
-
law_key: Optional[str] = None
|
| 18 |
-
law_name: Optional[str] = None
|
| 19 |
-
article_number: Optional[str] = None
|
| 20 |
-
original_text: Optional[str] = None
|
| 21 |
-
simplified_summary: Optional[str] = None
|
| 22 |
-
legal_nature: Optional[str] = None
|
| 23 |
-
|
| 24 |
-
class AskResponse(BaseModel):
|
| 25 |
-
answer: Optional[str] = None
|
| 26 |
-
articles: Optional[List[ArticleItem]] = None
|
| 27 |
-
sources: List[SourceItem] = Field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rag.py
DELETED
|
@@ -1,840 +0,0 @@
|
|
| 1 |
-
# rag.py
|
| 2 |
-
import os
|
| 3 |
-
import json
|
| 4 |
-
import logging
|
| 5 |
-
import warnings
|
| 6 |
-
from typing import Any, Dict, List, Tuple, Optional, Set
|
| 7 |
-
|
| 8 |
-
from dotenv import load_dotenv
|
| 9 |
-
|
| 10 |
-
from langchain_core.documents import Document
|
| 11 |
-
from langchain_chroma import Chroma
|
| 12 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 13 |
-
|
| 14 |
-
from langchain_core.retrievers import BaseRetriever
|
| 15 |
-
from langchain_core.callbacks import CallbackManagerForRetrieverRun
|
| 16 |
-
|
| 17 |
-
from langchain_classic.retrievers.document_compressors import CrossEncoderReranker
|
| 18 |
-
from langchain_classic.retrievers import ContextualCompressionRetriever
|
| 19 |
-
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
|
| 20 |
-
|
| 21 |
-
from langchain_groq import ChatGroq
|
| 22 |
-
from langchain_core.prompts import ChatPromptTemplate
|
| 23 |
-
from langchain_core.output_parsers import StrOutputParser
|
| 24 |
-
|
| 25 |
-
# Optional deps (BM25)
|
| 26 |
-
try:
|
| 27 |
-
from rank_bm25 import BM25Okapi
|
| 28 |
-
import numpy as np
|
| 29 |
-
_HAS_BM25 = True
|
| 30 |
-
except Exception:
|
| 31 |
-
BM25Okapi = None
|
| 32 |
-
np = None
|
| 33 |
-
_HAS_BM25 = False
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
# ----------------------------
|
| 37 |
-
# Global config
|
| 38 |
-
# ----------------------------
|
| 39 |
-
os.environ["TRANSFORMERS_NO_PROGRESS_BAR"] = "1"
|
| 40 |
-
warnings.filterwarnings("ignore")
|
| 41 |
-
|
| 42 |
-
logger = logging.getLogger(__name__)
|
| 43 |
-
logging.basicConfig(level=logging.INFO)
|
| 44 |
-
|
| 45 |
-
load_dotenv()
|
| 46 |
-
|
| 47 |
-
THIS_FILE = os.path.abspath(__file__)
|
| 48 |
-
BASE_DIR = os.path.dirname(THIS_FILE)
|
| 49 |
-
|
| 50 |
-
DATA_DIR = os.path.join(BASE_DIR, "data")
|
| 51 |
-
CHROMA_DIR = os.path.join(BASE_DIR, "chroma_db")
|
| 52 |
-
RERANKER_DIR = os.path.join(BASE_DIR, "reranker")
|
| 53 |
-
|
| 54 |
-
_qa_chain = None
|
| 55 |
-
|
| 56 |
-
# Two retrievers One general retriever + many per-law retrievers
|
| 57 |
-
_retriever_general: Optional[BaseRetriever] = None
|
| 58 |
-
_retrievers_by_law: Dict[str, BaseRetriever] = {}
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
# Composite key map to avoid article_number collisions across laws
|
| 62 |
-
_article_map_all: Optional[Dict[str, dict]] = None
|
| 63 |
-
|
| 64 |
-
# Canonical keys (keep stable across datasets)
|
| 65 |
-
CANON_CONSTITUTION = "egyptian_constitution"
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
# ----------------------------
|
| 69 |
-
# Helpers (data loading)
|
| 70 |
-
# ----------------------------
|
| 71 |
-
def _load_json_folder(folder_path: str) -> List[dict]:
|
| 72 |
-
"""
|
| 73 |
-
Loads all json files and attaches _source_file so we can infer missing law_key/law_name.
|
| 74 |
-
Supports:
|
| 75 |
-
- list[dict] (either direct articles OR a wrapper dict that contains data/articles)
|
| 76 |
-
- dict with "data": list[dict]
|
| 77 |
-
- dict with "articles": list[dict]
|
| 78 |
-
- single dict
|
| 79 |
-
"""
|
| 80 |
-
all_items: List[dict] = []
|
| 81 |
-
|
| 82 |
-
for filename in os.listdir(folder_path):
|
| 83 |
-
if not filename.lower().endswith(".json"):
|
| 84 |
-
continue
|
| 85 |
-
|
| 86 |
-
file_path = os.path.join(folder_path, filename)
|
| 87 |
-
with open(file_path, "r", encoding="utf-8") as f:
|
| 88 |
-
obj = json.load(f)
|
| 89 |
-
|
| 90 |
-
def attach_source(x):
|
| 91 |
-
if isinstance(x, dict):
|
| 92 |
-
x["_source_file"] = filename
|
| 93 |
-
return x
|
| 94 |
-
|
| 95 |
-
def flatten_wrapper(wrapper: dict):
|
| 96 |
-
"""If wrapper has data/articles list, flatten it and propagate top-level law meta."""
|
| 97 |
-
if not isinstance(wrapper, dict):
|
| 98 |
-
return
|
| 99 |
-
top_lk = wrapper.get("law_key")
|
| 100 |
-
top_ln = wrapper.get("law_name")
|
| 101 |
-
|
| 102 |
-
if "data" in wrapper and isinstance(wrapper["data"], list):
|
| 103 |
-
for x in wrapper["data"]:
|
| 104 |
-
if isinstance(x, dict):
|
| 105 |
-
x.setdefault("law_key", top_lk)
|
| 106 |
-
x.setdefault("law_name", top_ln)
|
| 107 |
-
all_items.append(attach_source(x))
|
| 108 |
-
return
|
| 109 |
-
|
| 110 |
-
if "articles" in wrapper and isinstance(wrapper["articles"], list):
|
| 111 |
-
for x in wrapper["articles"]:
|
| 112 |
-
if isinstance(x, dict):
|
| 113 |
-
x.setdefault("law_key", top_lk)
|
| 114 |
-
x.setdefault("law_name", top_ln)
|
| 115 |
-
all_items.append(attach_source(x))
|
| 116 |
-
return
|
| 117 |
-
|
| 118 |
-
# Not a wrapper; treat as single article dict
|
| 119 |
-
all_items.append(attach_source(wrapper))
|
| 120 |
-
|
| 121 |
-
# Case 1: list
|
| 122 |
-
if isinstance(obj, list):
|
| 123 |
-
for elem in obj:
|
| 124 |
-
# elem might be a wrapper dict (law_key/law_name/data) OR an article dict
|
| 125 |
-
if isinstance(elem, dict):
|
| 126 |
-
flatten_wrapper(elem)
|
| 127 |
-
else:
|
| 128 |
-
# unexpected, but keep it
|
| 129 |
-
all_items.append(elem)
|
| 130 |
-
continue
|
| 131 |
-
|
| 132 |
-
# Case 2: dict
|
| 133 |
-
if isinstance(obj, dict):
|
| 134 |
-
flatten_wrapper(obj)
|
| 135 |
-
continue
|
| 136 |
-
|
| 137 |
-
logger.warning("Unsupported JSON format in: %s", file_path)
|
| 138 |
-
|
| 139 |
-
return all_items
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
def _canonicalize_law_key(law_key: Optional[str]) -> Optional[str]:
|
| 143 |
-
if not law_key:
|
| 144 |
-
return None
|
| 145 |
-
lk = str(law_key).strip()
|
| 146 |
-
if not lk:
|
| 147 |
-
return None
|
| 148 |
-
lk_low = lk.lower()
|
| 149 |
-
|
| 150 |
-
# Normalize common variants
|
| 151 |
-
if lk_low in {"egyptian_constitution", "egyptian-constitution", "constitution", "egypt_constitution"}:
|
| 152 |
-
return CANON_CONSTITUTION
|
| 153 |
-
|
| 154 |
-
return lk_low
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
def _infer_law_meta(item: dict) -> Tuple[Optional[str], Optional[str]]:
|
| 158 |
-
"""
|
| 159 |
-
Some datasets include law_key/law_name at the top-level only (or are missing entirely).
|
| 160 |
-
We infer robustly from:
|
| 161 |
-
- item["law_key"]/item["law_name"]
|
| 162 |
-
- article_id prefix
|
| 163 |
-
- source filename
|
| 164 |
-
"""
|
| 165 |
-
lk = item.get("law_key")
|
| 166 |
-
ln = item.get("law_name")
|
| 167 |
-
|
| 168 |
-
# If present, canonicalize law_key
|
| 169 |
-
lk_canon = _canonicalize_law_key(lk)
|
| 170 |
-
if lk_canon:
|
| 171 |
-
return lk_canon, (str(ln).strip() if ln else None)
|
| 172 |
-
|
| 173 |
-
src = str(item.get("_source_file") or "").lower()
|
| 174 |
-
aid = str(item.get("article_id") or "").upper()
|
| 175 |
-
|
| 176 |
-
if "constitution" in src or aid.startswith("EG-CONST"):
|
| 177 |
-
return CANON_CONSTITUTION, "الدستور المصري"
|
| 178 |
-
|
| 179 |
-
if "labour" in src or "labor" in src or aid.startswith("EG-LABOR"):
|
| 180 |
-
return "egyptian_labour_law", "قانون العمل"
|
| 181 |
-
|
| 182 |
-
if "civil" in src or aid.startswith("EG-CIVIL"):
|
| 183 |
-
return "civil_law", "القانون المدني المصري"
|
| 184 |
-
|
| 185 |
-
if "personal status" in src or "الأحوال" in src or aid.startswith("EG-PSL"):
|
| 186 |
-
return "personal_status", "قانون الأحوال الشخصية"
|
| 187 |
-
|
| 188 |
-
if "technology crimes" in src or "tech" in src or aid.startswith("EG-TECH"):
|
| 189 |
-
return "tech_crimes", "قانون مكافحة جرائم تقنية المعلومات"
|
| 190 |
-
|
| 191 |
-
if "الإجراءات" in src or "الاجراءات" in src or aid.startswith("EG-CRIM-PROC"):
|
| 192 |
-
return "criminal_law", "قانون الإجراءات الجنائية"
|
| 193 |
-
|
| 194 |
-
return None, (str(ln).strip() if ln else None)
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
def _dedupe_items(items: List[dict]) -> List[dict]:
|
| 198 |
-
unique: Dict[str, dict] = {}
|
| 199 |
-
for item in items:
|
| 200 |
-
law_key, _ = _infer_law_meta(item)
|
| 201 |
-
num = str(item.get("article_number", "")).strip()
|
| 202 |
-
key = str(item.get("article_id") or (f"{law_key}::{num}" if law_key and num else "") or hash(json.dumps(item, ensure_ascii=False)))
|
| 203 |
-
unique[key] = item
|
| 204 |
-
return list(unique.values())
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
def _build_documents(items: List[dict]) -> Tuple[List[Document], Dict[str, dict]]:
|
| 208 |
-
"""
|
| 209 |
-
Returns:
|
| 210 |
-
- docs: LangChain Documents
|
| 211 |
-
- article_map: key = "{law_key}::{article_number}" -> raw dict (for cross-ref fetching)
|
| 212 |
-
"""
|
| 213 |
-
article_map: Dict[str, dict] = {}
|
| 214 |
-
docs: List[Document] = []
|
| 215 |
-
|
| 216 |
-
for item in items:
|
| 217 |
-
law_key, law_name = _infer_law_meta(item)
|
| 218 |
-
num = str(item.get("article_number", "")).strip()
|
| 219 |
-
|
| 220 |
-
# composite key to avoid collisions
|
| 221 |
-
if law_key and num:
|
| 222 |
-
article_map[f"{law_key}::{num}"] = item
|
| 223 |
-
|
| 224 |
-
cross_refs = item.get("cross_references") or []
|
| 225 |
-
cross_ref_text = ""
|
| 226 |
-
if isinstance(cross_refs, list) and len(cross_refs) > 0:
|
| 227 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join([f"المادة {ref}" for ref in cross_refs])
|
| 228 |
-
|
| 229 |
-
page_content = f"""
|
| 230 |
-
رقم المادة: {item.get('article_number')}
|
| 231 |
-
النص الأصلي: {item.get('original_text')}
|
| 232 |
-
الشرح المبسط: {item.get('simplified_summary')}{cross_ref_text}
|
| 233 |
-
""".strip()
|
| 234 |
-
|
| 235 |
-
keywords_list = item.get("keywords", [])
|
| 236 |
-
keywords_str = ", ".join(keywords_list) if isinstance(keywords_list, list) else str(keywords_list or "")
|
| 237 |
-
|
| 238 |
-
metadata = {
|
| 239 |
-
"article_id": item.get("article_id"),
|
| 240 |
-
"article_number": num,
|
| 241 |
-
"legal_nature": item.get("legal_nature", ""),
|
| 242 |
-
"keywords": keywords_str,
|
| 243 |
-
"part": item.get("part (Bab)", item.get("part", "")),
|
| 244 |
-
"chapter": item.get("chapter (Fasl)", item.get("chapter", "")),
|
| 245 |
-
"cross_references": ", ".join([str(ref) for ref in (cross_refs or [])]),
|
| 246 |
-
"law_key": law_key,
|
| 247 |
-
"law_name": law_name,
|
| 248 |
-
}
|
| 249 |
-
|
| 250 |
-
docs.append(Document(page_content=page_content, metadata=metadata))
|
| 251 |
-
|
| 252 |
-
logger.info("Loaded %d documents", len(docs))
|
| 253 |
-
return docs, article_map
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
def _docs_to_context(docs: List[Document]) -> str:
|
| 257 |
-
parts = []
|
| 258 |
-
for d in docs:
|
| 259 |
-
num = str(d.metadata.get("article_number", "")).strip()
|
| 260 |
-
law_name = (d.metadata.get("law_name") or "").strip()
|
| 261 |
-
content = (d.page_content or "").strip()
|
| 262 |
-
header = f"[{law_name} - المادة {num}]" if (law_name and num) else (f"[المادة {num}]" if num else "")
|
| 263 |
-
parts.append(f"{header}\n{content}".strip())
|
| 264 |
-
return "\n\n---\n\n".join(parts)
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
def _is_constitutional_question(q: str) -> bool:
|
| 268 |
-
q = (q or "").strip()
|
| 269 |
-
keywords = ["الدستور", "دستوري", "دستورية", "في الدستور", "مواد الدستور", "نص المادة"]
|
| 270 |
-
return any(k in q for k in keywords)
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
def _preferred_law_keys(question: str) -> Optional[Set[str]]:
|
| 275 |
-
"""
|
| 276 |
-
Lightweight topic routing to reduce cross-law contamination.
|
| 277 |
-
Returns a set of preferred law_keys (canonical) or None (no preference).
|
| 278 |
-
"""
|
| 279 |
-
q = (question or "").strip().lower()
|
| 280 |
-
|
| 281 |
-
# Constitution
|
| 282 |
-
if _is_constitutional_question(question):
|
| 283 |
-
return {CANON_CONSTITUTION}
|
| 284 |
-
|
| 285 |
-
# Labour / workplace
|
| 286 |
-
labour_terms = [
|
| 287 |
-
"قانون العمل", "العام��", "العمال", "صاحب العمل", "منشأة", "المنشأة",
|
| 288 |
-
"الأجر", "الأجور", "إضراب", "الإضراب", "فصل تعسفي", "سخرة", "جبراً", "تحرش", "تنمر", "مكان العمل"
|
| 289 |
-
]
|
| 290 |
-
if any(term in q for term in labour_terms):
|
| 291 |
-
return {"egyptian_labour_law"}
|
| 292 |
-
|
| 293 |
-
# Tech crimes
|
| 294 |
-
tech_terms = [
|
| 295 |
-
"جرائم تقنية", "تقنية المعلومات", "اختراق", "هاكر", "حساب", "بريد إلكتروني", "ابتزاز", "انتحال", "فيسبوك",
|
| 296 |
-
"واتساب", "تلغرام", "نشر", "صور", "بيانات شخصية", "خصوصية", "الشبكة", "موقع", "منصة"
|
| 297 |
-
]
|
| 298 |
-
if any(term in q for term in tech_terms):
|
| 299 |
-
return {"tech_crimes"}
|
| 300 |
-
|
| 301 |
-
# Criminal procedure
|
| 302 |
-
proc_terms = [
|
| 303 |
-
"إجراءات جنائية", "الإجراءات الجنائية", "محضر", "بلاغ", "قسم", "شرطة", "نيابة", "تحقيق", "حبس احتياطي",
|
| 304 |
-
"ضبط", "تفتيش", "قبض", "تظلم"
|
| 305 |
-
]
|
| 306 |
-
if any(term in q for term in proc_terms):
|
| 307 |
-
return {"criminal_law"}
|
| 308 |
-
|
| 309 |
-
# Personal status
|
| 310 |
-
ps_terms = ["أحوال شخصية", "نفقة", "حضانة", "طلاق", "خلع", "رؤية", "استضافة", "مؤخر", "قائمة المنقولات"]
|
| 311 |
-
if any(term in q for term in ps_terms):
|
| 312 |
-
return {"personal_status"}
|
| 313 |
-
|
| 314 |
-
# Civil law
|
| 315 |
-
civil_terms = ["القانون المدني", "عقد", "التزام", "تعويض", "مسؤولية تقصيرية", "بطلان", "إبطال", "فسخ"]
|
| 316 |
-
if any(term in q for term in civil_terms):
|
| 317 |
-
return {"civil_law"}
|
| 318 |
-
|
| 319 |
-
return None
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
def _wants_penalty(question: str) -> bool:
|
| 324 |
-
"""
|
| 325 |
-
Returns True if the user explicitly asks about punishment/sanctions (criminal/administrative),
|
| 326 |
-
so we are allowed to include "العقوبات" articles. Otherwise, prefer substantive (non-penalty) rules.
|
| 327 |
-
"""
|
| 328 |
-
q = (question or "").strip()
|
| 329 |
-
penalty_terms = [
|
| 330 |
-
"عقوبة", "العقوبات", "يُعاقب", "يعاقب", "غرامة", "حبس", "سجن", "إغلاق", "غلق",
|
| 331 |
-
"جزاء", "جزاءات", "تجريم", "ما عقوبة", "ما هي عقوبة", "كم غرامة"
|
| 332 |
-
]
|
| 333 |
-
return any(term in q for term in penalty_terms)
|
| 334 |
-
|
| 335 |
-
def _is_procedural_question(q: str) -> bool:
|
| 336 |
-
q = (q or "").strip()
|
| 337 |
-
proc = ["محضر", "بلاغ", "قسم", "شرطة", "نيابة", "دعوى", "قضية", "طلاق", "نفقة", "حضانة", "إيصال", "شيك"]
|
| 338 |
-
return any(k in q for k in proc)
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
# ----------------------------
|
| 342 |
-
# Custom retrievers (BM25 / Metadata / Hybrid RRF / Cross-refs)
|
| 343 |
-
# ----------------------------
|
| 344 |
-
class BM25Retriever(BaseRetriever):
|
| 345 |
-
"""BM25-based keyword retriever"""
|
| 346 |
-
corpus_docs: List[Document]
|
| 347 |
-
k: int = 15
|
| 348 |
-
|
| 349 |
-
bm25: Any = None
|
| 350 |
-
|
| 351 |
-
class Config:
|
| 352 |
-
arbitrary_types_allowed = True
|
| 353 |
-
|
| 354 |
-
def __init__(self, **data):
|
| 355 |
-
super().__init__(**data)
|
| 356 |
-
if not _HAS_BM25:
|
| 357 |
-
raise RuntimeError("rank_bm25/numpy not installed. Install: pip install rank-bm25 numpy")
|
| 358 |
-
tokenized = [doc.page_content.split() for doc in self.corpus_docs]
|
| 359 |
-
self.bm25 = BM25Okapi(tokenized)
|
| 360 |
-
|
| 361 |
-
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 362 |
-
tokenized_query = query.split()
|
| 363 |
-
scores = self.bm25.get_scores(tokenized_query)
|
| 364 |
-
top_idx = np.argsort(scores)[::-1][: self.k]
|
| 365 |
-
return [self.corpus_docs[i] for i in top_idx if scores[i] > 0]
|
| 366 |
-
|
| 367 |
-
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 368 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
class MetadataFilterRetriever(BaseRetriever):
|
| 372 |
-
"""Metadata + light content scoring retriever"""
|
| 373 |
-
corpus_docs: List[Document]
|
| 374 |
-
k: int = 15
|
| 375 |
-
|
| 376 |
-
class Config:
|
| 377 |
-
arbitrary_types_allowed = True
|
| 378 |
-
|
| 379 |
-
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 380 |
-
q = (query or "").lower()
|
| 381 |
-
q_words = [w for w in q.split() if w.strip()]
|
| 382 |
-
scored = []
|
| 383 |
-
|
| 384 |
-
for doc in self.corpus_docs:
|
| 385 |
-
score = 0
|
| 386 |
-
keywords = (doc.metadata.get("keywords") or "").lower()
|
| 387 |
-
legal_nature = (doc.metadata.get("legal_nature") or "").lower()
|
| 388 |
-
part = (doc.metadata.get("part") or "").lower()
|
| 389 |
-
chapter = (doc.metadata.get("chapter") or "").lower()
|
| 390 |
-
content = (doc.page_content or "").lower()
|
| 391 |
-
law_name = (doc.metadata.get("law_name") or "").lower()
|
| 392 |
-
|
| 393 |
-
if any(w in keywords for w in q_words):
|
| 394 |
-
score += 3
|
| 395 |
-
if any(w in legal_nature for w in q_words):
|
| 396 |
-
score += 2
|
| 397 |
-
if any((w in part) or (w in chapter) for w in q_words):
|
| 398 |
-
score += 1
|
| 399 |
-
if any(w in content for w in q_words):
|
| 400 |
-
score += 1
|
| 401 |
-
if any(w in law_name for w in q_words):
|
| 402 |
-
score += 1
|
| 403 |
-
|
| 404 |
-
if score > 0:
|
| 405 |
-
scored.append((doc, score))
|
| 406 |
-
|
| 407 |
-
scored.sort(key=lambda x: x[1], reverse=True)
|
| 408 |
-
return [doc for doc, _ in scored[: self.k]]
|
| 409 |
-
|
| 410 |
-
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 411 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
class HybridRRFRetriever(BaseRetriever):
|
| 415 |
-
"""
|
| 416 |
-
Reciprocal Rank Fusion across:
|
| 417 |
-
- semantic retriever (Chroma)
|
| 418 |
-
- BM25 retriever
|
| 419 |
-
- metadata retriever
|
| 420 |
-
"""
|
| 421 |
-
semantic_retriever: BaseRetriever
|
| 422 |
-
bm25_retriever: Optional[BaseRetriever] = None
|
| 423 |
-
metadata_retriever: Optional[BaseRetriever] = None
|
| 424 |
-
|
| 425 |
-
beta_semantic: float = 0.5
|
| 426 |
-
beta_keyword: float = 0.3
|
| 427 |
-
beta_metadata: float = 0.2
|
| 428 |
-
|
| 429 |
-
rrf_k: int = 60
|
| 430 |
-
top_k: int = 20
|
| 431 |
-
|
| 432 |
-
class Config:
|
| 433 |
-
arbitrary_types_allowed = True
|
| 434 |
-
|
| 435 |
-
def _doc_id(self, doc: Document) -> str:
|
| 436 |
-
aid = str(doc.metadata.get("article_id") or "").strip()
|
| 437 |
-
if aid:
|
| 438 |
-
return aid
|
| 439 |
-
lk = str(doc.metadata.get("law_key") or "").strip()
|
| 440 |
-
num = str(doc.metadata.get("article_number") or "").strip()
|
| 441 |
-
if lk and num:
|
| 442 |
-
return f"{lk}::{num}"
|
| 443 |
-
return str(hash(doc.page_content or ""))
|
| 444 |
-
|
| 445 |
-
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 446 |
-
sem_docs = self.semantic_retriever.invoke(query) or []
|
| 447 |
-
|
| 448 |
-
bm_docs = []
|
| 449 |
-
if self.bm25_retriever is not None:
|
| 450 |
-
try:
|
| 451 |
-
bm_docs = self.bm25_retriever.invoke(query) or []
|
| 452 |
-
except Exception:
|
| 453 |
-
bm_docs = []
|
| 454 |
-
|
| 455 |
-
md_docs = []
|
| 456 |
-
if self.metadata_retriever is not None:
|
| 457 |
-
md_docs = self.metadata_retriever.invoke(query) or []
|
| 458 |
-
|
| 459 |
-
rrf_scores: Dict[str, float] = {}
|
| 460 |
-
doc_lookup: Dict[str, Document] = {}
|
| 461 |
-
|
| 462 |
-
def add_ranked(docs: List[Document], beta: float):
|
| 463 |
-
for rank, d in enumerate(docs, start=1):
|
| 464 |
-
did = self._doc_id(d)
|
| 465 |
-
rrf_scores[did] = rrf_scores.get(did, 0.0) + beta / (self.rrf_k + rank)
|
| 466 |
-
if did not in doc_lookup:
|
| 467 |
-
doc_lookup[did] = d
|
| 468 |
-
|
| 469 |
-
add_ranked(sem_docs, self.beta_semantic)
|
| 470 |
-
add_ranked(bm_docs, self.beta_keyword)
|
| 471 |
-
add_ranked(md_docs, self.beta_metadata)
|
| 472 |
-
|
| 473 |
-
sorted_ids = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
|
| 474 |
-
out = []
|
| 475 |
-
for did, _ in sorted_ids[: self.top_k]:
|
| 476 |
-
if did in doc_lookup:
|
| 477 |
-
out.append(doc_lookup[did])
|
| 478 |
-
return out
|
| 479 |
-
|
| 480 |
-
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 481 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
class CrossReferenceRetriever(BaseRetriever):
|
| 485 |
-
"""Fetches cross-referenced articles automatically (within the same law)."""
|
| 486 |
-
base_retriever: BaseRetriever
|
| 487 |
-
article_map: Dict[str, dict]
|
| 488 |
-
|
| 489 |
-
class Config:
|
| 490 |
-
arbitrary_types_allowed = True
|
| 491 |
-
|
| 492 |
-
def _build_doc_from_item(self, item: dict) -> Document:
|
| 493 |
-
law_key, law_name = _infer_law_meta(item)
|
| 494 |
-
|
| 495 |
-
cross_refs = item.get("cross_references") or []
|
| 496 |
-
cross_ref_text = ""
|
| 497 |
-
if isinstance(cross_refs, list) and len(cross_refs) > 0:
|
| 498 |
-
cross_ref_text = "\nالمواد ذات الصلة: " + ", ".join([f"المادة {ref}" for ref in cross_refs])
|
| 499 |
-
|
| 500 |
-
page_content = f"""
|
| 501 |
-
رقم المادة: {item.get('article_number')}
|
| 502 |
-
النص الأصلي: {item.get('original_text')}
|
| 503 |
-
الشرح المبسط: {item.get('simplified_summary')}{cross_ref_text}
|
| 504 |
-
""".strip()
|
| 505 |
-
|
| 506 |
-
keywords_list = item.get("keywords", [])
|
| 507 |
-
keywords_str = ", ".join(keywords_list) if isinstance(keywords_list, list) else str(keywords_list or "")
|
| 508 |
-
|
| 509 |
-
metadata = {
|
| 510 |
-
"article_id": item.get("article_id"),
|
| 511 |
-
"article_number": str(item.get("article_number", "")).strip(),
|
| 512 |
-
"legal_nature": item.get("legal_nature", ""),
|
| 513 |
-
"keywords": keywords_str,
|
| 514 |
-
"cross_references": ", ".join([str(ref) for ref in (cross_refs or [])]),
|
| 515 |
-
"law_key": law_key,
|
| 516 |
-
"law_name": law_name,
|
| 517 |
-
}
|
| 518 |
-
return Document(page_content=page_content, metadata=metadata)
|
| 519 |
-
|
| 520 |
-
def _doc_uid(self, d: Document) -> str:
|
| 521 |
-
aid = str(d.metadata.get("article_id") or "").strip()
|
| 522 |
-
if aid:
|
| 523 |
-
return aid
|
| 524 |
-
lk = str(d.metadata.get("law_key") or "").strip()
|
| 525 |
-
num = str(d.metadata.get("article_number") or "").strip()
|
| 526 |
-
if lk and num:
|
| 527 |
-
return f"{lk}::{num}"
|
| 528 |
-
return str(hash(d.page_content or ""))
|
| 529 |
-
|
| 530 |
-
def _get_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 531 |
-
initial_docs = self.base_retriever.invoke(query) or []
|
| 532 |
-
|
| 533 |
-
enhanced: List[Document] = []
|
| 534 |
-
seen: Set[str] = set()
|
| 535 |
-
|
| 536 |
-
# add initial docs
|
| 537 |
-
for d in initial_docs:
|
| 538 |
-
uid = self._doc_uid(d)
|
| 539 |
-
if uid in seen:
|
| 540 |
-
continue
|
| 541 |
-
seen.add(uid)
|
| 542 |
-
enhanced.append(d)
|
| 543 |
-
|
| 544 |
-
# add cross refs within same law
|
| 545 |
-
for d in initial_docs:
|
| 546 |
-
lk = str(d.metadata.get("law_key") or "").strip()
|
| 547 |
-
if not lk:
|
| 548 |
-
continue
|
| 549 |
-
|
| 550 |
-
cross_str = (d.metadata.get("cross_references") or "").strip()
|
| 551 |
-
if not cross_str:
|
| 552 |
-
continue
|
| 553 |
-
|
| 554 |
-
for ref in [x.strip() for x in cross_str.split(",")]:
|
| 555 |
-
if not ref:
|
| 556 |
-
continue
|
| 557 |
-
key = f"{lk}::{ref}"
|
| 558 |
-
if key in self.article_map:
|
| 559 |
-
new_doc = self._build_doc_from_item(self.article_map[key])
|
| 560 |
-
uid = self._doc_uid(new_doc)
|
| 561 |
-
if uid not in seen:
|
| 562 |
-
seen.add(uid)
|
| 563 |
-
enhanced.append(new_doc)
|
| 564 |
-
|
| 565 |
-
return enhanced
|
| 566 |
-
|
| 567 |
-
async def _aget_relevant_documents(self, query: str, *, run_manager: CallbackManagerForRetrieverRun = None) -> List[Document]:
|
| 568 |
-
return self._get_relevant_documents(query, run_manager=run_manager)
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
# ----------------------------
|
| 572 |
-
# Retriever builder
|
| 573 |
-
# ----------------------------
|
| 574 |
-
def _build_retriever(
|
| 575 |
-
docs_subset: List[Document],
|
| 576 |
-
article_map_all: Dict[str, dict],
|
| 577 |
-
semantic_retriever: BaseRetriever,
|
| 578 |
-
) -> BaseRetriever:
|
| 579 |
-
bm25_retriever = None
|
| 580 |
-
if _HAS_BM25:
|
| 581 |
-
try:
|
| 582 |
-
bm25_retriever = BM25Retriever(corpus_docs=docs_subset, k=15)
|
| 583 |
-
logger.info("BM25 enabled for subset=%d", len(docs_subset))
|
| 584 |
-
except Exception as e:
|
| 585 |
-
logger.warning("BM25 disabled: %s", e)
|
| 586 |
-
|
| 587 |
-
metadata_retriever = MetadataFilterRetriever(corpus_docs=docs_subset, k=15)
|
| 588 |
-
|
| 589 |
-
hybrid = HybridRRFRetriever(
|
| 590 |
-
semantic_retriever=semantic_retriever,
|
| 591 |
-
bm25_retriever=bm25_retriever,
|
| 592 |
-
metadata_retriever=metadata_retriever,
|
| 593 |
-
beta_semantic=0.5,
|
| 594 |
-
beta_keyword=0.3,
|
| 595 |
-
beta_metadata=0.2,
|
| 596 |
-
rrf_k=60,
|
| 597 |
-
top_k=20,
|
| 598 |
-
)
|
| 599 |
-
|
| 600 |
-
cross_ref = CrossReferenceRetriever(base_retriever=hybrid, article_map=article_map_all)
|
| 601 |
-
|
| 602 |
-
if not os.path.exists(RERANKER_DIR):
|
| 603 |
-
raise FileNotFoundError(f"Reranker folder not found: {RERANKER_DIR}")
|
| 604 |
-
|
| 605 |
-
cross_encoder = HuggingFaceCrossEncoder(model_name=RERANKER_DIR)
|
| 606 |
-
compressor = CrossEncoderReranker(model=cross_encoder, top_n=5)
|
| 607 |
-
|
| 608 |
-
return ContextualCompressionRetriever(base_compressor=compressor, base_retriever=cross_ref)
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
# ----------------------------
|
| 612 |
-
# Main initializer
|
| 613 |
-
# ----------------------------
|
| 614 |
-
def initialize_rag_pipeline():
|
| 615 |
-
global _retriever_general, _retrievers_by_law, _article_map_all
|
| 616 |
-
|
| 617 |
-
if not os.path.exists(DATA_DIR):
|
| 618 |
-
raise FileNotFoundError(f"Data folder not found: {DATA_DIR}")
|
| 619 |
-
|
| 620 |
-
data = _load_json_folder(DATA_DIR)
|
| 621 |
-
data = _dedupe_items(data)
|
| 622 |
-
|
| 623 |
-
docs_all, article_map_all = _build_documents(data)
|
| 624 |
-
_article_map_all = article_map_all
|
| 625 |
-
|
| 626 |
-
embeddings = HuggingFaceEmbeddings(model_name="Omartificial-Intelligence-Space/GATE-AraBert-v1")
|
| 627 |
-
|
| 628 |
-
# Build / load ONE Chroma DB for ALL docs
|
| 629 |
-
if os.path.exists(CHROMA_DIR) and os.listdir(CHROMA_DIR):
|
| 630 |
-
logger.info("Loading existing Chroma DB: %s", CHROMA_DIR)
|
| 631 |
-
vectorstore = Chroma(persist_directory=CHROMA_DIR, embedding_function=embeddings)
|
| 632 |
-
else:
|
| 633 |
-
logger.info("Building Chroma DB (first time) into: %s", CHROMA_DIR)
|
| 634 |
-
vectorstore = Chroma.from_documents(docs_all, embeddings, persist_directory=CHROMA_DIR)
|
| 635 |
-
|
| 636 |
-
# General semantic retriever
|
| 637 |
-
semantic_general = vectorstore.as_retriever(search_kwargs={"k": 15})
|
| 638 |
-
|
| 639 |
-
# ✅ Build general retriever over ALL docs
|
| 640 |
-
_retriever_general = _build_retriever(docs_all, article_map_all, semantic_general)
|
| 641 |
-
|
| 642 |
-
# ✅ Build a retriever for EACH law_key automatically
|
| 643 |
-
_retrievers_by_law = {}
|
| 644 |
-
law_keys = sorted({(d.metadata.get("law_key") or "").strip() for d in docs_all if (d.metadata.get("law_key") or "").strip()})
|
| 645 |
-
logger.info("Detected law_keys=%s", law_keys)
|
| 646 |
-
|
| 647 |
-
for lk in law_keys:
|
| 648 |
-
docs_subset = [d for d in docs_all if d.metadata.get("law_key") == lk]
|
| 649 |
-
if not docs_subset:
|
| 650 |
-
continue
|
| 651 |
-
|
| 652 |
-
# Use Chroma metadata filter to keep semantic search inside this law only
|
| 653 |
-
semantic_law = vectorstore.as_retriever(search_kwargs={"k": 15, "filter": {"law_key": lk}})
|
| 654 |
-
_retrievers_by_law[lk] = _build_retriever(docs_subset, article_map_all, semantic_law)
|
| 655 |
-
logger.info("Built retriever for law_key=%s docs=%d", lk, len(docs_subset))
|
| 656 |
-
|
| 657 |
-
# --- LLM setup (same as your existing code) ---
|
| 658 |
-
groq_key = os.getenv("GROQ_API_KEY")
|
| 659 |
-
if not groq_key:
|
| 660 |
-
raise ValueError("GROQ_API_KEY is missing. Add it to .env")
|
| 661 |
-
|
| 662 |
-
llm = ChatGroq(
|
| 663 |
-
groq_api_key=groq_key,
|
| 664 |
-
model_name="llama-3.1-8b-instant",
|
| 665 |
-
temperature=0.3,
|
| 666 |
-
model_kwargs={"top_p": 0.9},
|
| 667 |
-
)
|
| 668 |
-
system_instructions = """
|
| 669 |
-
<role>
|
| 670 |
-
أنت "المساعد القانوني الذكي"، خبير متخصص في الدستور المصري والقوانين الإجرائية.
|
| 671 |
-
مهمتك: تقديم إجابات دقيقة بناءً على "السياق التشريعي" المرفق أولاً، أو تقديم نصائح إجرائية عامة عند الضرورة.
|
| 672 |
-
</role>
|
| 673 |
-
|
| 674 |
-
<decision_logic>
|
| 675 |
-
🔴 الحالة الأولى: إذا وجدت معلومات داخل السياق تجيب على السؤال:
|
| 676 |
-
- استخرج الإجابة من السياق فقط.
|
| 677 |
-
- ابدأ الإجابة مباشرة دون مقدمات.
|
| 678 |
-
- وثّق الإجابة برقم المادة.
|
| 679 |
-
- لا تضف معلومات خارجية.
|
| 680 |
-
|
| 681 |
-
🟡 الحالة الثانية: إذا لم تجد الإجابة في السياق وكان السؤال إجرائياً:
|
| 682 |
-
- ابدأ بعبارة: "بناءً على الإجراءات القانونية العامة في مصر (وليس نصاً دستورياً محدداً):"
|
| 683 |
-
- قدّم خطوات مرقمة واضحة.
|
| 684 |
-
- لا تذكر أرقام مواد قانونية.
|
| 685 |
-
|
| 686 |
-
🔵 الحالة الثالثة: إذا كان السؤال دستورياً ولم تجد الإجابة في السياق:
|
| 687 |
-
- قل بوضوح أنه لم يرد ذكره في المواد المسترجعة.
|
| 688 |
-
- لا تجب من الذاكرة.
|
| 689 |
-
|
| 690 |
-
🟢 الحالة الرابعة: تحية/شكر:
|
| 691 |
-
- رد مهذب ومقتضب وقل: "أنا جاهز للإجابة على استفساراتك القانونية."
|
| 692 |
-
|
| 693 |
-
⚫ الحالة الخامسة: خارج النطاق:
|
| 694 |
-
- اعتذر ووجّه المستخدم لمجال القانون.
|
| 695 |
-
</decision_logic>
|
| 696 |
-
|
| 697 |
-
<formatting_rules>
|
| 698 |
-
- فقرات قصيرة وبينها سطر فارغ.
|
| 699 |
-
- لا تكرر هذه التعليمات.
|
| 700 |
-
- التزم بالعربية الفصحى المبسطة.
|
| 701 |
-
</formatting_rules>
|
| 702 |
-
""".strip()
|
| 703 |
-
|
| 704 |
-
prompt = ChatPromptTemplate.from_messages([
|
| 705 |
-
("system", system_instructions),
|
| 706 |
-
("system", "السياق التشريعي المتاح:\n{context}"),
|
| 707 |
-
("human", "سؤال المستفيد:\n{input}"),
|
| 708 |
-
])
|
| 709 |
-
|
| 710 |
-
qa_chain = (prompt | llm | StrOutputParser())
|
| 711 |
-
logger.info("RAG pipeline initialized successfully.")
|
| 712 |
-
return qa_chain
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
# ----------------------------
|
| 717 |
-
# Public API (matches main.py)
|
| 718 |
-
# ----------------------------
|
| 719 |
-
def get_chain():
|
| 720 |
-
global _qa_chain
|
| 721 |
-
if _qa_chain is None:
|
| 722 |
-
_qa_chain = initialize_rag_pipeline()
|
| 723 |
-
return _qa_chain
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
def _get_retriever_for_question(question: str) -> BaseRetriever:
|
| 727 |
-
get_chain() # ensure initialized
|
| 728 |
-
|
| 729 |
-
preferred = _preferred_law_keys(question)
|
| 730 |
-
if preferred:
|
| 731 |
-
# If multiple laws are returned, choose the first that exists
|
| 732 |
-
for lk in preferred:
|
| 733 |
-
r = _retrievers_by_law.get(lk)
|
| 734 |
-
if r is not None:
|
| 735 |
-
return r
|
| 736 |
-
|
| 737 |
-
return _retriever_general # fallback
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
def _docs_to_sources(docs: List[Document]) -> List[Dict[str, Any]]:
|
| 742 |
-
sources: List[Dict[str, Any]] = []
|
| 743 |
-
seen: Set[str] = set()
|
| 744 |
-
|
| 745 |
-
for d in docs:
|
| 746 |
-
md = getattr(d, "metadata", {}) or {}
|
| 747 |
-
uid = (
|
| 748 |
-
md.get("article_id")
|
| 749 |
-
or (str(md.get("law_key") or "") + "::" + str(md.get("article_number") or ""))
|
| 750 |
-
or str(hash(d.page_content or ""))
|
| 751 |
-
)
|
| 752 |
-
if uid in seen:
|
| 753 |
-
continue
|
| 754 |
-
seen.add(uid)
|
| 755 |
-
|
| 756 |
-
num = str(md.get("article_number", "")).strip()
|
| 757 |
-
|
| 758 |
-
sources.append({
|
| 759 |
-
"law_key": md.get("law_key"),
|
| 760 |
-
"law_name": md.get("law_name"),
|
| 761 |
-
"article_number": num or None,
|
| 762 |
-
"legal_nature": md.get("legal_nature"),
|
| 763 |
-
"keywords": md.get("keywords"),
|
| 764 |
-
"content": (getattr(d, "page_content", "") or "").strip()[:2000],
|
| 765 |
-
"metadata": md,
|
| 766 |
-
})
|
| 767 |
-
|
| 768 |
-
return sources
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
def ask(question: str) -> Tuple[str, List[Dict[str, Any]]]:
|
| 772 |
-
chain = get_chain()
|
| 773 |
-
retriever = _get_retriever_for_question(question)
|
| 774 |
-
|
| 775 |
-
docs = retriever.invoke(question) or []
|
| 776 |
-
|
| 777 |
-
# ✅ Topic routing filter: keep only documents from the most relevant law when possible.
|
| 778 |
-
preferred = _preferred_law_keys(question)
|
| 779 |
-
if preferred:
|
| 780 |
-
filtered = [d for d in docs if (d.metadata.get("law_key") in preferred)]
|
| 781 |
-
if filtered:
|
| 782 |
-
docs = filtered
|
| 783 |
-
|
| 784 |
-
# ✅ If top doc has a law_key, prefer staying within that law to avoid mixing.
|
| 785 |
-
if docs:
|
| 786 |
-
top_law = docs[0].metadata.get("law_key")
|
| 787 |
-
if top_law:
|
| 788 |
-
same_law = [d for d in docs if d.metadata.get("law_key") == top_law]
|
| 789 |
-
if same_law:
|
| 790 |
-
docs = same_law
|
| 791 |
-
|
| 792 |
-
# ✅ Penalty filter:
|
| 793 |
-
# If the user did NOT ask about "عقوبة/غرامة/حبس..." then avoid pulling "العقوبات" articles
|
| 794 |
-
# because they often contaminate substantive Q&A (like workplace harassment).
|
| 795 |
-
if docs and not _wants_penalty(question):
|
| 796 |
-
non_penalty = []
|
| 797 |
-
for d in docs:
|
| 798 |
-
md = d.metadata or {}
|
| 799 |
-
chapter = str(md.get("chapter") or "")
|
| 800 |
-
part = str(md.get("part") or "")
|
| 801 |
-
legal_nature = str(md.get("legal_nature") or "")
|
| 802 |
-
text = (chapter + " " + part + " " + legal_nature)
|
| 803 |
-
if "عقوب" in text or "قاعدة عقابية" in text or "تجريم" in text:
|
| 804 |
-
continue
|
| 805 |
-
non_penalty.append(d)
|
| 806 |
-
if non_penalty:
|
| 807 |
-
docs = non_penalty
|
| 808 |
-
|
| 809 |
-
context_text = _docs_to_context(docs)
|
| 810 |
-
|
| 811 |
-
# Hard guard: constitutional questions must be answered from constitution docs only.
|
| 812 |
-
preferred = _preferred_law_keys(question)
|
| 813 |
-
if preferred:
|
| 814 |
-
has_pref = any((d.metadata.get("law_key") in preferred) for d in docs)
|
| 815 |
-
if not has_pref:
|
| 816 |
-
msg = "عذراً، لم يرد ذكر لهذا الموضوع في المواد المسترجعة من القانون الأكثر صلة بالسؤال ضمن السياق الحالي."
|
| 817 |
-
return msg, _docs_to_sources(docs)
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
answer = chain.invoke({"context": context_text, "input": question})
|
| 821 |
-
|
| 822 |
-
# ✅ Post-guard 1: if we have ANY useful context, forbid procedural prefix.
|
| 823 |
-
if docs:
|
| 824 |
-
bad_prefix = "بناءً على الإجراءات القانونية العامة في مصر (وليس نصاً دستورياً محدداً):"
|
| 825 |
-
answer = (answer or "").replace(bad_prefix, "").strip()
|
| 826 |
-
answer = "\n".join([line for line in answer.splitlines() if line.strip()]).strip()
|
| 827 |
-
|
| 828 |
-
# Also remove common hallucination like: "لا توجد مواد قانونية محددة في السياق..."
|
| 829 |
-
halluc_lines = [
|
| 830 |
-
"لا توجد مواد قانونية محددة في السياق المرفق",
|
| 831 |
-
"لا توجد مواد قانونية محددة في السياق",
|
| 832 |
-
"لا توجد معلومات داخل السياق",
|
| 833 |
-
"لا توجد مواد داخل السياق",
|
| 834 |
-
]
|
| 835 |
-
for hl in halluc_lines:
|
| 836 |
-
answer = answer.replace(hl, "").strip()
|
| 837 |
-
answer = "\n".join([line for line in answer.splitlines() if line.strip()]).strip()
|
| 838 |
-
|
| 839 |
-
return answer, _docs_to_sources(docs)
|
| 840 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rag_test_questions_100.json
DELETED
|
@@ -1,1380 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"schema_version": "1.0",
|
| 3 |
-
"dataset_version": "2026-02-03",
|
| 4 |
-
"created_at": "2026-02-03T19:15:30.097990+00:00",
|
| 5 |
-
"description": "Benchmark set for Egyptian legal RAG assistant: constitutional Qs + multi-law retrieval + procedural gating.",
|
| 6 |
-
"laws_in_scope": [
|
| 7 |
-
{
|
| 8 |
-
"key": "constitution",
|
| 9 |
-
"path": "/mnt/data/Egyptian_Constitution_legalnature_only.json"
|
| 10 |
-
},
|
| 11 |
-
{
|
| 12 |
-
"key": "criminal_procedure",
|
| 13 |
-
"path": "/mnt/data/قانون_الإجراءات_الجنائية.json"
|
| 14 |
-
},
|
| 15 |
-
{
|
| 16 |
-
"key": "tech_crimes",
|
| 17 |
-
"path": "/mnt/data/Technology Crimes Law.json"
|
| 18 |
-
},
|
| 19 |
-
{
|
| 20 |
-
"key": "labor",
|
| 21 |
-
"path": "/mnt/data/Egyptian_Labour_Law.json"
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
"key": "personal_status",
|
| 25 |
-
"path": "/mnt/data/Egyptian_Personal Status Laws.json"
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"key": "civil",
|
| 29 |
-
"path": "/mnt/data/Egyptian_Civil.json"
|
| 30 |
-
}
|
| 31 |
-
],
|
| 32 |
-
"defaults": {
|
| 33 |
-
"k": 15,
|
| 34 |
-
"rerank_top_n": 5,
|
| 35 |
-
"recall_k": 5,
|
| 36 |
-
"pass_rule": "pass if recall@k>=1 for questions with expected articles; else pass if answer startswith expected_answer_prefix (if provided)."
|
| 37 |
-
},
|
| 38 |
-
"cases": [
|
| 39 |
-
{
|
| 40 |
-
"id": "Q001",
|
| 41 |
-
"category": "constitutional",
|
| 42 |
-
"question": "ما مضمون المادة 1 من الدستور المصري؟",
|
| 43 |
-
"expected": {
|
| 44 |
-
"law": "constitution",
|
| 45 |
-
"expected_article_numbers": [
|
| 46 |
-
"1"
|
| 47 |
-
],
|
| 48 |
-
"must_cite_articles": true,
|
| 49 |
-
"answer_should_not_add_external_info": true
|
| 50 |
-
},
|
| 51 |
-
"notes": "جمهورية مصر العربية دولة ذات سيادة، موحدة لا تقبل التجزئة، نظامها جمهوري ديمقراطي يقوم على المواطنة وسيادة القانون. الشعب المصري جزء من الأمة العربية والعالم الإسلامي والقارة الأفريقية."
|
| 52 |
-
},
|
| 53 |
-
{
|
| 54 |
-
"id": "Q002",
|
| 55 |
-
"category": "constitutional",
|
| 56 |
-
"question": "ما مضمون المادة 2 من الدستور المصري؟",
|
| 57 |
-
"expected": {
|
| 58 |
-
"law": "constitution",
|
| 59 |
-
"expected_article_numbers": [
|
| 60 |
-
"2"
|
| 61 |
-
],
|
| 62 |
-
"must_cite_articles": true,
|
| 63 |
-
"answer_should_not_add_external_info": true
|
| 64 |
-
},
|
| 65 |
-
"notes": "الإسلام دين الدولة، واللغة العربية لغتها الرسمية، ومبادئ الشريعة الإسلامية هي المصدر الرئيسي للتشريع."
|
| 66 |
-
},
|
| 67 |
-
{
|
| 68 |
-
"id": "Q003",
|
| 69 |
-
"category": "constitutional",
|
| 70 |
-
"question": "ما مضمون المادة 3 من الدستور المصري؟",
|
| 71 |
-
"expected": {
|
| 72 |
-
"law": "constitution",
|
| 73 |
-
"expected_article_numbers": [
|
| 74 |
-
"3"
|
| 75 |
-
],
|
| 76 |
-
"must_cite_articles": true,
|
| 77 |
-
"answer_should_not_add_external_info": true
|
| 78 |
-
},
|
| 79 |
-
"notes": "مبادئ شرائع المسيحيين واليهود هي المصدر الرئيسي لتشريعات أحوالهم الشخصية وشئونهم الدينية واختيار قياداتهم."
|
| 80 |
-
},
|
| 81 |
-
{
|
| 82 |
-
"id": "Q004",
|
| 83 |
-
"category": "constitutional",
|
| 84 |
-
"question": "ما مضمون المادة 4 من الدستور المصري؟",
|
| 85 |
-
"expected": {
|
| 86 |
-
"law": "constitution",
|
| 87 |
-
"expected_article_numbers": [
|
| 88 |
-
"4"
|
| 89 |
-
],
|
| 90 |
-
"must_cite_articles": true,
|
| 91 |
-
"answer_should_not_add_external_info": true
|
| 92 |
-
},
|
| 93 |
-
"notes": "السيادة للشعب وحده وهو مصدر السلطات، ويصون الوحدة الوطنية القائمة على المساواة والعدل وتكافؤ الفرص."
|
| 94 |
-
},
|
| 95 |
-
{
|
| 96 |
-
"id": "Q005",
|
| 97 |
-
"category": "constitutional",
|
| 98 |
-
"question": "ما مضمون المادة 5 من الدستور المصري؟",
|
| 99 |
-
"expected": {
|
| 100 |
-
"law": "constitution",
|
| 101 |
-
"expected_article_numbers": [
|
| 102 |
-
"5"
|
| 103 |
-
],
|
| 104 |
-
"must_cite_articles": true,
|
| 105 |
-
"answer_should_not_add_external_info": true
|
| 106 |
-
},
|
| 107 |
-
"notes": "يقوم النظام السياسي على التعددية السياسية والحزبية، والتداول السلمي للسلطة، والفصل بين السلطات، واحترام حقوق الإنسان."
|
| 108 |
-
},
|
| 109 |
-
{
|
| 110 |
-
"id": "Q006",
|
| 111 |
-
"category": "constitutional",
|
| 112 |
-
"question": "ما مضمون المادة 6 من الدستور المصري؟",
|
| 113 |
-
"expected": {
|
| 114 |
-
"law": "constitution",
|
| 115 |
-
"expected_article_numbers": [
|
| 116 |
-
"6"
|
| 117 |
-
],
|
| 118 |
-
"must_cite_articles": true,
|
| 119 |
-
"answer_should_not_add_external_info": true
|
| 120 |
-
},
|
| 121 |
-
"notes": "الجنسية حق لكل من يولد لأب مصري أو أم مصرية، ويكفل القانون الاعتراف القانوني به ومنحه أوراقاً رسمية، ويحدد شروط اكتساب الجنسية."
|
| 122 |
-
},
|
| 123 |
-
{
|
| 124 |
-
"id": "Q007",
|
| 125 |
-
"category": "constitutional",
|
| 126 |
-
"question": "ما مضمون المادة 7 من الدستور المصري؟",
|
| 127 |
-
"expected": {
|
| 128 |
-
"law": "constitution",
|
| 129 |
-
"expected_article_numbers": [
|
| 130 |
-
"7"
|
| 131 |
-
],
|
| 132 |
-
"must_cite_articles": true,
|
| 133 |
-
"answer_should_not_add_external_info": true
|
| 134 |
-
},
|
| 135 |
-
"notes": "الأزهر الشريف هيئة مستقلة وهو المرجع الأساسي في العلوم الدينية. تلتزم الدولة بتمويله، وشيخ الأزهر مستقل ولا يمكن عزله."
|
| 136 |
-
},
|
| 137 |
-
{
|
| 138 |
-
"id": "Q008",
|
| 139 |
-
"category": "constitutional",
|
| 140 |
-
"question": "ما مضمون المادة 8 من الدستور المصري؟",
|
| 141 |
-
"expected": {
|
| 142 |
-
"law": "constitution",
|
| 143 |
-
"expected_article_numbers": [
|
| 144 |
-
"8"
|
| 145 |
-
],
|
| 146 |
-
"must_cite_articles": true,
|
| 147 |
-
"answer_should_not_add_external_info": true
|
| 148 |
-
},
|
| 149 |
-
"notes": "يقوم المجتمع على التضامن الاجتماعي، وتلتزم الدولة بتحقيق العدالة الاجتماعية والتكافل لضمان حياة كريمة للمواطنين."
|
| 150 |
-
},
|
| 151 |
-
{
|
| 152 |
-
"id": "Q009",
|
| 153 |
-
"category": "constitutional",
|
| 154 |
-
"question": "ما مضمون المادة 9 من الدستور المصري؟",
|
| 155 |
-
"expected": {
|
| 156 |
-
"law": "constitution",
|
| 157 |
-
"expected_article_numbers": [
|
| 158 |
-
"9"
|
| 159 |
-
],
|
| 160 |
-
"must_cite_articles": true,
|
| 161 |
-
"answer_should_not_add_external_info": true
|
| 162 |
-
},
|
| 163 |
-
"notes": "تلتزم الدولة بتحقيق تكافؤ الفرص بين جميع المواطنين بلا تمييز."
|
| 164 |
-
},
|
| 165 |
-
{
|
| 166 |
-
"id": "Q010",
|
| 167 |
-
"category": "constitutional",
|
| 168 |
-
"question": "ما مضمون المادة 10 من الدستور المصري؟",
|
| 169 |
-
"expected": {
|
| 170 |
-
"law": "constitution",
|
| 171 |
-
"expected_article_numbers": [
|
| 172 |
-
"10"
|
| 173 |
-
],
|
| 174 |
-
"must_cite_articles": true,
|
| 175 |
-
"answer_should_not_add_external_info": true
|
| 176 |
-
},
|
| 177 |
-
"notes": "الأسرة هي أساس المجتمع وتقوم على الدين والأخلاق والوطنية، وتعمل الدولة على حمايتها واستقرارها."
|
| 178 |
-
},
|
| 179 |
-
{
|
| 180 |
-
"id": "Q011",
|
| 181 |
-
"category": "constitutional",
|
| 182 |
-
"question": "ما مضمون المادة 11 من الدستور المصري؟",
|
| 183 |
-
"expected": {
|
| 184 |
-
"law": "constitution",
|
| 185 |
-
"expected_article_numbers": [
|
| 186 |
-
"11"
|
| 187 |
-
],
|
| 188 |
-
"must_cite_articles": true,
|
| 189 |
-
"answer_should_not_add_external_info": true
|
| 190 |
-
},
|
| 191 |
-
"notes": "تكفل الدولة المساواة بين الرجل والمرأة في كافة الحقوق، وتضمن تمثيلها في المجالس النيابية وتوليها الوظائف العامة والقضائية، وتحميها من العنف وتوفر الرعاية للفئات الأكثر احتياجاً."
|
| 192 |
-
},
|
| 193 |
-
{
|
| 194 |
-
"id": "Q012",
|
| 195 |
-
"category": "constitutional",
|
| 196 |
-
"question": "ما مضمون المادة 12 من الدستور المصري؟",
|
| 197 |
-
"expected": {
|
| 198 |
-
"law": "constitution",
|
| 199 |
-
"expected_article_numbers": [
|
| 200 |
-
"12"
|
| 201 |
-
],
|
| 202 |
-
"must_cite_articles": true,
|
| 203 |
-
"answer_should_not_add_external_info": true
|
| 204 |
-
},
|
| 205 |
-
"notes": "العمل حق وواجب تكفله الدولة. يُمنع العمل الجبري إلا بقانون ولخدمة عامة وبمقابل عادل."
|
| 206 |
-
},
|
| 207 |
-
{
|
| 208 |
-
"id": "Q013",
|
| 209 |
-
"category": "constitutional",
|
| 210 |
-
"question": "ما مضمون المادة 13 من الدستور المصري؟",
|
| 211 |
-
"expected": {
|
| 212 |
-
"law": "constitution",
|
| 213 |
-
"expected_article_numbers": [
|
| 214 |
-
"13"
|
| 215 |
-
],
|
| 216 |
-
"must_cite_articles": true,
|
| 217 |
-
"answer_should_not_add_external_info": true
|
| 218 |
-
},
|
| 219 |
-
"notes": "تحافظ الدولة على حقوق العمال وتضمن علاقات عمل متوازنة والتفاوض الجماعي، وتحميهم من المخاطر والفصل التعسفي."
|
| 220 |
-
},
|
| 221 |
-
{
|
| 222 |
-
"id": "Q014",
|
| 223 |
-
"category": "constitutional",
|
| 224 |
-
"question": "ما مضمون المادة 14 من الدستور المصري؟",
|
| 225 |
-
"expected": {
|
| 226 |
-
"law": "constitution",
|
| 227 |
-
"expected_article_numbers": [
|
| 228 |
-
"14"
|
| 229 |
-
],
|
| 230 |
-
"must_cite_articles": true,
|
| 231 |
-
"answer_should_not_add_external_info": true
|
| 232 |
-
},
|
| 233 |
-
"notes": "الوظائف العامة حق للمواطنين بناءً على الكفاءة، وهي تكليف لخدمة الشعب. تحمي الدولة الموظفين ولا يجوز فصلهم إلا تأديبياً أو بالقانون."
|
| 234 |
-
},
|
| 235 |
-
{
|
| 236 |
-
"id": "Q015",
|
| 237 |
-
"category": "constitutional",
|
| 238 |
-
"question": "ما مضمون المادة 15 من الدستور المصري؟",
|
| 239 |
-
"expected": {
|
| 240 |
-
"law": "constitution",
|
| 241 |
-
"expected_article_numbers": [
|
| 242 |
-
"15"
|
| 243 |
-
],
|
| 244 |
-
"must_cite_articles": true,
|
| 245 |
-
"answer_should_not_add_external_info": true
|
| 246 |
-
},
|
| 247 |
-
"notes": "الإضراب السلمي هو حق ينظمه القانون."
|
| 248 |
-
},
|
| 249 |
-
{
|
| 250 |
-
"id": "Q016",
|
| 251 |
-
"category": "constitutional",
|
| 252 |
-
"question": "ما مضمون المادة 16 من الدستور المصري؟",
|
| 253 |
-
"expected": {
|
| 254 |
-
"law": "constitution",
|
| 255 |
-
"expected_article_numbers": [
|
| 256 |
-
"16"
|
| 257 |
-
],
|
| 258 |
-
"must_cite_articles": true,
|
| 259 |
-
"answer_should_not_add_external_info": true
|
| 260 |
-
},
|
| 261 |
-
"notes": "تلتزم الدولة بتكريم ورعاية شهداء الوطن ومصابي الثورة والعمليات الأمنية وأسرهم، وتوفير فرص عمل لهم، وتشجع المجتمع المدني على المساهمة."
|
| 262 |
-
},
|
| 263 |
-
{
|
| 264 |
-
"id": "Q017",
|
| 265 |
-
"category": "constitutional",
|
| 266 |
-
"question": "ما مضمون المادة 17 من الدستور المصري؟",
|
| 267 |
-
"expected": {
|
| 268 |
-
"law": "constitution",
|
| 269 |
-
"expected_article_numbers": [
|
| 270 |
-
"17"
|
| 271 |
-
],
|
| 272 |
-
"must_cite_articles": true,
|
| 273 |
-
"answer_should_not_add_external_info": true
|
| 274 |
-
},
|
| 275 |
-
"notes": "تكفل الدولة التأمين والضمان الاجتماعي للمواطنين لضمان حياة كريمة، خاصة للفئات الضعيفة. أموال التأمينات خاصة ومحمية وتدار بواسطة هيئة مستقلة."
|
| 276 |
-
},
|
| 277 |
-
{
|
| 278 |
-
"id": "Q018",
|
| 279 |
-
"category": "constitutional",
|
| 280 |
-
"question": "ما مضمون المادة 18 من الدستور المصري؟",
|
| 281 |
-
"expected": {
|
| 282 |
-
"law": "constitution",
|
| 283 |
-
"expected_article_numbers": [
|
| 284 |
-
"18"
|
| 285 |
-
],
|
| 286 |
-
"must_cite_articles": true,
|
| 287 |
-
"answer_should_not_add_external_info": true
|
| 288 |
-
},
|
| 289 |
-
"notes": "لكل مواطن الحق في الصحة والرعاية المتكاملة. تلتزم الدولة بتخصيص نسبة من الإنفاق للصحة وإقامة تأمين صحي شامل، وتجرم الامتناع عن علاج الطوارئ."
|
| 290 |
-
},
|
| 291 |
-
{
|
| 292 |
-
"id": "Q019",
|
| 293 |
-
"category": "constitutional",
|
| 294 |
-
"question": "ما مضمون المادة 19 من الدستور المصري؟",
|
| 295 |
-
"expected": {
|
| 296 |
-
"law": "constitution",
|
| 297 |
-
"expected_article_numbers": [
|
| 298 |
-
"19"
|
| 299 |
-
],
|
| 300 |
-
"must_cite_articles": true,
|
| 301 |
-
"answer_should_not_add_external_info": true
|
| 302 |
-
},
|
| 303 |
-
"notes": "التعليم حق لكل مواطن وإلزامي حتى الثانوية ومجاني في مؤسسات الدولة. تلتزم الدولة بتخصيص نسبة من الإنفاق للتعليم ومراقبة جودته."
|
| 304 |
-
},
|
| 305 |
-
{
|
| 306 |
-
"id": "Q020",
|
| 307 |
-
"category": "constitutional",
|
| 308 |
-
"question": "ما مضمون المادة 20 من الدستور المصري؟",
|
| 309 |
-
"expected": {
|
| 310 |
-
"law": "constitution",
|
| 311 |
-
"expected_article_numbers": [
|
| 312 |
-
"20"
|
| 313 |
-
],
|
| 314 |
-
"must_cite_articles": true,
|
| 315 |
-
"answer_should_not_add_external_info": true
|
| 316 |
-
},
|
| 317 |
-
"notes": "تلتزم الدولة بتشجيع وتطوير التعليم الفني والتقني والتدريب المهني وفق معايير الجودة واحتياجات السوق."
|
| 318 |
-
},
|
| 319 |
-
{
|
| 320 |
-
"id": "Q021",
|
| 321 |
-
"category": "constitutional",
|
| 322 |
-
"question": "ما مضمون المادة 21 من الدستور المصري؟",
|
| 323 |
-
"expected": {
|
| 324 |
-
"law": "constitution",
|
| 325 |
-
"expected_article_numbers": [
|
| 326 |
-
"21"
|
| 327 |
-
],
|
| 328 |
-
"must_cite_articles": true,
|
| 329 |
-
"answer_should_not_add_external_info": true
|
| 330 |
-
},
|
| 331 |
-
"notes": "تكفل الدولة استقلال الجامعات ومجانية التعليم الجامعي الحكومي وتخصيص نسبة من الإنفاق له. تشجع الجامعات الأهلية وتراقب جودة التعليم الخاص والأهلي."
|
| 332 |
-
},
|
| 333 |
-
{
|
| 334 |
-
"id": "Q022",
|
| 335 |
-
"category": "constitutional",
|
| 336 |
-
"question": "ما مضمون المادة 22 من الدستور المصري؟",
|
| 337 |
-
"expected": {
|
| 338 |
-
"law": "constitution",
|
| 339 |
-
"expected_article_numbers": [
|
| 340 |
-
"22"
|
| 341 |
-
],
|
| 342 |
-
"must_cite_articles": true,
|
| 343 |
-
"answer_should_not_add_external_info": true
|
| 344 |
-
},
|
| 345 |
-
"notes": "المعلمون هم ركيزة التعليم، وتكفل الدولة تنمية مهاراتهم ورعاية حقوقهم لضمان جودة التعليم."
|
| 346 |
-
},
|
| 347 |
-
{
|
| 348 |
-
"id": "Q023",
|
| 349 |
-
"category": "constitutional",
|
| 350 |
-
"question": "ما مضمون المادة 23 من الدستور المصري؟",
|
| 351 |
-
"expected": {
|
| 352 |
-
"law": "constitution",
|
| 353 |
-
"expected_article_numbers": [
|
| 354 |
-
"23"
|
| 355 |
-
],
|
| 356 |
-
"must_cite_articles": true,
|
| 357 |
-
"answer_should_not_add_external_info": true
|
| 358 |
-
},
|
| 359 |
-
"notes": "تكفل الدولة حرية البحث العلمي وتدعم الباحثين، وتخصص له نسبة من الإنفاق الحكومي، وتشجع مساهمة القطاع الخاص والمصريين بالخارج."
|
| 360 |
-
},
|
| 361 |
-
{
|
| 362 |
-
"id": "Q024",
|
| 363 |
-
"category": "constitutional",
|
| 364 |
-
"question": "ما مضمون ا��مادة 24 من الدستور المصري؟",
|
| 365 |
-
"expected": {
|
| 366 |
-
"law": "constitution",
|
| 367 |
-
"expected_article_numbers": [
|
| 368 |
-
"24"
|
| 369 |
-
],
|
| 370 |
-
"must_cite_articles": true,
|
| 371 |
-
"answer_should_not_add_external_info": true
|
| 372 |
-
},
|
| 373 |
-
"notes": "اللغة العربية والدين والتاريخ مواد أساسية في التعليم قبل الجامعي. تدرس الجامعات حقوق الإنسان والأخلاق المهنية."
|
| 374 |
-
},
|
| 375 |
-
{
|
| 376 |
-
"id": "Q025",
|
| 377 |
-
"category": "constitutional",
|
| 378 |
-
"question": "ما مضمون المادة 25 من الدستور المصري؟",
|
| 379 |
-
"expected": {
|
| 380 |
-
"law": "constitution",
|
| 381 |
-
"expected_article_numbers": [
|
| 382 |
-
"25"
|
| 383 |
-
],
|
| 384 |
-
"must_cite_articles": true,
|
| 385 |
-
"answer_should_not_add_external_info": true
|
| 386 |
-
},
|
| 387 |
-
"notes": "تلتزم الدولة بالقضاء على الأمية الهجائية والرقمية وفق خطة زمنية وبمشاركة المجتمع المدني."
|
| 388 |
-
},
|
| 389 |
-
{
|
| 390 |
-
"id": "Q026",
|
| 391 |
-
"category": "constitutional",
|
| 392 |
-
"question": "ما مضمون المادة 26 من الدستور المصري؟",
|
| 393 |
-
"expected": {
|
| 394 |
-
"law": "constitution",
|
| 395 |
-
"expected_article_numbers": [
|
| 396 |
-
"26"
|
| 397 |
-
],
|
| 398 |
-
"must_cite_articles": true,
|
| 399 |
-
"answer_should_not_add_external_info": true
|
| 400 |
-
},
|
| 401 |
-
"notes": "يحظر إنشاء الرتب المدنية."
|
| 402 |
-
},
|
| 403 |
-
{
|
| 404 |
-
"id": "Q027",
|
| 405 |
-
"category": "constitutional",
|
| 406 |
-
"question": "ما مضمون المادة 27 من الدستور المصري؟",
|
| 407 |
-
"expected": {
|
| 408 |
-
"law": "constitution",
|
| 409 |
-
"expected_article_numbers": [
|
| 410 |
-
"27"
|
| 411 |
-
],
|
| 412 |
-
"must_cite_articles": true,
|
| 413 |
-
"answer_should_not_add_external_info": true
|
| 414 |
-
},
|
| 415 |
-
"notes": "يهدف النظام الاقتصادي لتحقيق الرخاء والتنمية المستدامة والعدالة الاجتماعية. يلتزم بالشفافية والتنافسية ومنع الاحتكار وحماية المستهلك والعمال، وضمان حد أدنى للأجور."
|
| 416 |
-
},
|
| 417 |
-
{
|
| 418 |
-
"id": "Q028",
|
| 419 |
-
"category": "constitutional",
|
| 420 |
-
"question": "ما مضمون المادة 28 من الدستور المصري؟",
|
| 421 |
-
"expected": {
|
| 422 |
-
"law": "constitution",
|
| 423 |
-
"expected_article_numbers": [
|
| 424 |
-
"28"
|
| 425 |
-
],
|
| 426 |
-
"must_cite_articles": true,
|
| 427 |
-
"answer_should_not_add_external_info": true
|
| 428 |
-
},
|
| 429 |
-
"notes": "تلتزم الدولة بحماية الأنشطة الاقتصادية وزيادة تنافسيتها وتشجيع الاستثمار والإنتاج والتصدير. تهتم بالمشروعات الصغيرة وتنظيم القطاع غير الرسمي."
|
| 430 |
-
},
|
| 431 |
-
{
|
| 432 |
-
"id": "Q029",
|
| 433 |
-
"category": "constitutional",
|
| 434 |
-
"question": "ما مضمون المادة 29 من الدستور المصري؟",
|
| 435 |
-
"expected": {
|
| 436 |
-
"law": "constitution",
|
| 437 |
-
"expected_article_numbers": [
|
| 438 |
-
"29"
|
| 439 |
-
],
|
| 440 |
-
"must_cite_articles": true,
|
| 441 |
-
"answer_should_not_add_external_info": true
|
| 442 |
-
},
|
| 443 |
-
"notes": "الزراعة أساس الاقتصاد. تلتزم الدولة بحماية الأراضي الزراعية وتنمية الريف والإنتاج الزراعي والحيواني، وشراء المحاصيل بأسعار عادلة، ودعم صغار الفلاحين."
|
| 444 |
-
},
|
| 445 |
-
{
|
| 446 |
-
"id": "Q030",
|
| 447 |
-
"category": "constitutional",
|
| 448 |
-
"question": "ما مضمون المادة 30 من الدستور المصري؟",
|
| 449 |
-
"expected": {
|
| 450 |
-
"law": "constitution",
|
| 451 |
-
"expected_article_numbers": [
|
| 452 |
-
"30"
|
| 453 |
-
],
|
| 454 |
-
"must_cite_articles": true,
|
| 455 |
-
"answer_should_not_add_external_info": true
|
| 456 |
-
},
|
| 457 |
-
"notes": "تلتزم الدولة بحماية الثروة السمكية ودعم الصيادين مع الحفاظ على البيئة."
|
| 458 |
-
},
|
| 459 |
-
{
|
| 460 |
-
"id": "Q031",
|
| 461 |
-
"category": "constitutional",
|
| 462 |
-
"question": "ما مضمون المادة 31 من الدستور المصري؟",
|
| 463 |
-
"expected": {
|
| 464 |
-
"law": "constitution",
|
| 465 |
-
"expected_article_numbers": [
|
| 466 |
-
"31"
|
| 467 |
-
],
|
| 468 |
-
"must_cite_articles": true,
|
| 469 |
-
"answer_should_not_add_external_info": true
|
| 470 |
-
},
|
| 471 |
-
"notes": "أمن الفضاء المعلوماتي جزء من الأمن القومي والاقتصادي، وتلتزم الدولة بحمايته."
|
| 472 |
-
},
|
| 473 |
-
{
|
| 474 |
-
"id": "Q032",
|
| 475 |
-
"category": "constitutional",
|
| 476 |
-
"question": "ما مضمون المادة 32 من الدستور المصري؟",
|
| 477 |
-
"expected": {
|
| 478 |
-
"law": "constitution",
|
| 479 |
-
"expected_article_numbers": [
|
| 480 |
-
"32"
|
| 481 |
-
],
|
| 482 |
-
"must_cite_articles": true,
|
| 483 |
-
"answer_should_not_add_external_info": true
|
| 484 |
-
},
|
| 485 |
-
"notes": "الموارد الطبيعية ملك للشعب ويجب الحفاظ عليها واستغلالها بحكمة. تشجع الدولة الطاقة المتجددة وتصنيع المواد الأولية. يحدد القانون شروط استغلال الموارد والمرافق العامة."
|
| 486 |
-
},
|
| 487 |
-
{
|
| 488 |
-
"id": "Q033",
|
| 489 |
-
"category": "constitutional",
|
| 490 |
-
"question": "ما مضمون المادة 33 من الدستور المصري؟",
|
| 491 |
-
"expected": {
|
| 492 |
-
"law": "constitution",
|
| 493 |
-
"expected_article_numbers": [
|
| 494 |
-
"33"
|
| 495 |
-
],
|
| 496 |
-
"must_cite_articles": true,
|
| 497 |
-
"answer_should_not_add_external_info": true
|
| 498 |
-
},
|
| 499 |
-
"notes": "تحمي الدولة جميع أنواع الملكية: العامة والخاصة والتعاونية."
|
| 500 |
-
},
|
| 501 |
-
{
|
| 502 |
-
"id": "Q034",
|
| 503 |
-
"category": "constitutional",
|
| 504 |
-
"question": "ما مضمون المادة 34 من الدستور المصري؟",
|
| 505 |
-
"expected": {
|
| 506 |
-
"law": "constitution",
|
| 507 |
-
"expected_article_numbers": [
|
| 508 |
-
"34"
|
| 509 |
-
],
|
| 510 |
-
"must_cite_articles": true,
|
| 511 |
-
"answer_should_not_add_external_info": true
|
| 512 |
-
},
|
| 513 |
-
"notes": "للملكية العامة حرمة ويجب حمايتها ولا يجوز المساس بها."
|
| 514 |
-
},
|
| 515 |
-
{
|
| 516 |
-
"id": "Q035",
|
| 517 |
-
"category": "constitutional",
|
| 518 |
-
"question": "ما مضمون المادة 35 من الدستور المصري؟",
|
| 519 |
-
"expected": {
|
| 520 |
-
"law": "constitution",
|
| 521 |
-
"expected_article_numbers": [
|
| 522 |
-
"35"
|
| 523 |
-
],
|
| 524 |
-
"must_cite_articles": true,
|
| 525 |
-
"answer_should_not_add_external_info": true
|
| 526 |
-
},
|
| 527 |
-
"notes": "الملكية الخاصة والإرث مصونان. لا تُفرض الحراسة أو تُنزع الملكية إلا بالقانون وحكم قضائي وللمنفعة العامة مقابل تعويض عادل."
|
| 528 |
-
},
|
| 529 |
-
{
|
| 530 |
-
"id": "Q036",
|
| 531 |
-
"category": "constitutional",
|
| 532 |
-
"question": "ما مضمون المادة 36 من الدستور المصري؟",
|
| 533 |
-
"expected": {
|
| 534 |
-
"law": "constitution",
|
| 535 |
-
"expected_article_numbers": [
|
| 536 |
-
"36"
|
| 537 |
-
],
|
| 538 |
-
"must_cite_articles": true,
|
| 539 |
-
"answer_should_not_add_external_info": true
|
| 540 |
-
},
|
| 541 |
-
"notes": "تحفز الدولة القطاع الخاص للمشاركة في المسؤولية الاجتماعية."
|
| 542 |
-
},
|
| 543 |
-
{
|
| 544 |
-
"id": "Q037",
|
| 545 |
-
"category": "constitutional",
|
| 546 |
-
"question": "ما مضمون المادة 37 من الدستور المصري؟",
|
| 547 |
-
"expected": {
|
| 548 |
-
"law": "constitution",
|
| 549 |
-
"expected_article_numbers": [
|
| 550 |
-
"37"
|
| 551 |
-
],
|
| 552 |
-
"must_cite_articles": true,
|
| 553 |
-
"answer_should_not_add_external_info": true
|
| 554 |
-
},
|
| 555 |
-
"notes": "الملكية التعاونية مصونة وتدعم الدولة التعاونيات وتضمن استقلالها، ولا تحل إلا بحكم قضائي."
|
| 556 |
-
},
|
| 557 |
-
{
|
| 558 |
-
"id": "Q038",
|
| 559 |
-
"category": "constitutional",
|
| 560 |
-
"question": "ما مضمون المادة 38 من الدستور المصري؟",
|
| 561 |
-
"expected": {
|
| 562 |
-
"law": "constitution",
|
| 563 |
-
"expected_article_numbers": [
|
| 564 |
-
"38"
|
| 565 |
-
],
|
| 566 |
-
"must_cite_articles": true,
|
| 567 |
-
"answer_should_not_add_external_info": true
|
| 568 |
-
},
|
| 569 |
-
"notes": "النظام الضريبي يهدف لتنمية الموارد والعدالة. الضرائب لا تُفرض إلا بقانون وتكون تصاعدية. الدولة تلتزم بتطوير النظام الضريبي، والتهرب الضريبي جريمة."
|
| 570 |
-
},
|
| 571 |
-
{
|
| 572 |
-
"id": "Q039",
|
| 573 |
-
"category": "constitutional",
|
| 574 |
-
"question": "ما مضمون المادة 39 من الدستور المصري؟",
|
| 575 |
-
"expected": {
|
| 576 |
-
"law": "constitution",
|
| 577 |
-
"expected_article_numbers": [
|
| 578 |
-
"39"
|
| 579 |
-
],
|
| 580 |
-
"must_cite_articles": true,
|
| 581 |
-
"answer_should_not_add_external_info": true
|
| 582 |
-
},
|
| 583 |
-
"notes": "الادخار واجب وطني تحميه وتشجعه الدولة."
|
| 584 |
-
},
|
| 585 |
-
{
|
| 586 |
-
"id": "Q040",
|
| 587 |
-
"category": "constitutional",
|
| 588 |
-
"question": "ما مضمون المادة 40 من الدستور المصري؟",
|
| 589 |
-
"expected": {
|
| 590 |
-
"law": "constitution",
|
| 591 |
-
"expected_article_numbers": [
|
| 592 |
-
"40"
|
| 593 |
-
],
|
| 594 |
-
"must_cite_articles": true,
|
| 595 |
-
"answer_should_not_add_external_info": true
|
| 596 |
-
},
|
| 597 |
-
"notes": "المصادرة العامة للأموال ممنوعة، والخاصة لا تجوز إلا بحكم قضائي."
|
| 598 |
-
},
|
| 599 |
-
{
|
| 600 |
-
"id": "Q041",
|
| 601 |
-
"category": "criminal_procedure",
|
| 602 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 1؟",
|
| 603 |
-
"expected": {
|
| 604 |
-
"law": "criminal_procedure",
|
| 605 |
-
"expected_article_numbers": [
|
| 606 |
-
"1"
|
| 607 |
-
],
|
| 608 |
-
"must_cite_articles": true
|
| 609 |
-
},
|
| 610 |
-
"notes": "النيابة العامة هي الجهة الوحيدة المختصة برفع الدعوى الجنائية ومباشرتها، ولا يجوز تركها أو وقفها إلا وفقاً للقانون."
|
| 611 |
-
},
|
| 612 |
-
{
|
| 613 |
-
"id": "Q042",
|
| 614 |
-
"category": "criminal_procedure",
|
| 615 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 2؟",
|
| 616 |
-
"expected": {
|
| 617 |
-
"law": "criminal_procedure",
|
| 618 |
-
"expected_article_numbers": [
|
| 619 |
-
"2"
|
| 620 |
-
],
|
| 621 |
-
"must_cite_articles": true
|
| 622 |
-
},
|
| 623 |
-
"notes": "النائب العام أو أعضاء النيابة العامة يباشرون الدعوى الجنائية، ويجوز تعيين آخرين لأداء هذه الوظيفة وفقاً للقانون."
|
| 624 |
-
},
|
| 625 |
-
{
|
| 626 |
-
"id": "Q043",
|
| 627 |
-
"category": "criminal_procedure",
|
| 628 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 3؟",
|
| 629 |
-
"expected": {
|
| 630 |
-
"law": "criminal_procedure",
|
| 631 |
-
"expected_article_numbers": [
|
| 632 |
-
"3"
|
| 633 |
-
],
|
| 634 |
-
"must_cite_articles": true
|
| 635 |
-
},
|
| 636 |
-
"notes": "في جرائم معينة (مثل السب والقذف والزنا) لا ترفع الدعوى الجنائية إلا بشكوى من المجني عليه أو وكيله خلال ثلاثة أشهر من علمه بالجريمة."
|
| 637 |
-
},
|
| 638 |
-
{
|
| 639 |
-
"id": "Q044",
|
| 640 |
-
"category": "criminal_procedure",
|
| 641 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 4؟",
|
| 642 |
-
"expected": {
|
| 643 |
-
"law": "criminal_procedure",
|
| 644 |
-
"expected_article_numbers": [
|
| 645 |
-
"4"
|
| 646 |
-
],
|
| 647 |
-
"must_cite_articles": true
|
| 648 |
-
},
|
| 649 |
-
"notes": "شكوى أحد المجني عليهم تكفي لتحريك الدعوى، والشكوى ضد متهم واحد تعتبر شكوى ضد جميع المتهمين."
|
| 650 |
-
},
|
| 651 |
-
{
|
| 652 |
-
"id": "Q045",
|
| 653 |
-
"category": "criminal_procedure",
|
| 654 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 5؟",
|
| 655 |
-
"expected": {
|
| 656 |
-
"law": "criminal_procedure",
|
| 657 |
-
"expected_article_numbers": [
|
| 658 |
-
"5"
|
| 659 |
-
],
|
| 660 |
-
"must_cite_articles": true
|
| 661 |
-
},
|
| 662 |
-
"notes": "إذا كان المجني عليه قاصراً (أقل من 15 سنة) أو مصاباً بعاهة عقلية، يقدم الشكوى من له الولاية عليه."
|
| 663 |
-
},
|
| 664 |
-
{
|
| 665 |
-
"id": "Q046",
|
| 666 |
-
"category": "criminal_procedure",
|
| 667 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 6؟",
|
| 668 |
-
"expected": {
|
| 669 |
-
"law": "criminal_procedure",
|
| 670 |
-
"expected_article_numbers": [
|
| 671 |
-
"6"
|
| 672 |
-
],
|
| 673 |
-
"must_cite_articles": true
|
| 674 |
-
},
|
| 675 |
-
"notes": "إذا تعارضت مصلحة المجني عليه مع مصلحة ممثله أو لم يكن له ممثل، تنوب عنه النيابة العامة في تقديم الشكوى."
|
| 676 |
-
},
|
| 677 |
-
{
|
| 678 |
-
"id": "Q047",
|
| 679 |
-
"category": "criminal_procedure",
|
| 680 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 7؟",
|
| 681 |
-
"expected": {
|
| 682 |
-
"law": "criminal_procedure",
|
| 683 |
-
"expected_article_numbers": [
|
| 684 |
-
"7"
|
| 685 |
-
],
|
| 686 |
-
"must_cite_articles": true
|
| 687 |
-
},
|
| 688 |
-
"notes": "وفاة المجني عليه تنهي حقه في الشكوى، لكن إذا قدمت الشكوى قبل الوفاة تستمر الدعوى."
|
| 689 |
-
},
|
| 690 |
-
{
|
| 691 |
-
"id": "Q048",
|
| 692 |
-
"category": "criminal_procedure",
|
| 693 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 8؟",
|
| 694 |
-
"expected": {
|
| 695 |
-
"law": "criminal_procedure",
|
| 696 |
-
"expected_article_numbers": [
|
| 697 |
-
"8"
|
| 698 |
-
],
|
| 699 |
-
"must_cite_articles": true
|
| 700 |
-
},
|
| 701 |
-
"notes": "في بعض الجرائم (المواد 181 و182 عقوبات) يشترط طلب كتابي من وزير العدل لرفع الدعوى الجنائية."
|
| 702 |
-
},
|
| 703 |
-
{
|
| 704 |
-
"id": "Q049",
|
| 705 |
-
"category": "criminal_procedure",
|
| 706 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 8 مكرر؟",
|
| 707 |
-
"expected": {
|
| 708 |
-
"law": "criminal_procedure",
|
| 709 |
-
"expected_article_numbers": [
|
| 710 |
-
"8 مكرر"
|
| 711 |
-
],
|
| 712 |
-
"must_cite_articles": true
|
| 713 |
-
},
|
| 714 |
-
"notes": "جرائم المادة 116 مكرراً (أ) من قانون العقوبات لا ترفع إلا من النائب العام أو المحامي العام."
|
| 715 |
-
},
|
| 716 |
-
{
|
| 717 |
-
"id": "Q050",
|
| 718 |
-
"category": "criminal_procedure",
|
| 719 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 9؟",
|
| 720 |
-
"expected": {
|
| 721 |
-
"law": "criminal_procedure",
|
| 722 |
-
"expected_article_numbers": [
|
| 723 |
-
"9"
|
| 724 |
-
],
|
| 725 |
-
"must_cite_articles": true
|
| 726 |
-
},
|
| 727 |
-
"notes": "جرائم الإهانة (المادة 184 عقوبات) تتطلب طلباً من الهيئة المجني عليها، وبشكل عام لا يجوز ��لتحقيق في جرائم الشكوى إلا بعد تقديمها."
|
| 728 |
-
},
|
| 729 |
-
{
|
| 730 |
-
"id": "Q051",
|
| 731 |
-
"category": "criminal_procedure",
|
| 732 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 10؟",
|
| 733 |
-
"expected": {
|
| 734 |
-
"law": "criminal_procedure",
|
| 735 |
-
"expected_article_numbers": [
|
| 736 |
-
"10"
|
| 737 |
-
],
|
| 738 |
-
"must_cite_articles": true
|
| 739 |
-
},
|
| 740 |
-
"notes": "يجوز للشاكي التنازل عن الشكوى قبل صدور حكم نهائي وتنقضي الدعوى بالتنازل. التنازل لأحد المتهمين يمتد للباقين. لا ينتقل حق التنازل للورثة إلا في دعوى الزنا."
|
| 741 |
-
},
|
| 742 |
-
{
|
| 743 |
-
"id": "Q052",
|
| 744 |
-
"category": "criminal_procedure",
|
| 745 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 11؟",
|
| 746 |
-
"expected": {
|
| 747 |
-
"law": "criminal_procedure",
|
| 748 |
-
"expected_article_numbers": [
|
| 749 |
-
"11"
|
| 750 |
-
],
|
| 751 |
-
"must_cite_articles": true
|
| 752 |
-
},
|
| 753 |
-
"notes": "لمحكمة الجنايات إقامة الدعوى على متهمين آخرين أو وقائع مرتبطة وإحالتها للنيابة للتحقيق، ويجوز ندب أحد أعضائها للتحقيق مع تطبيق أحكام قاضي التحقيق عليه."
|
| 754 |
-
},
|
| 755 |
-
{
|
| 756 |
-
"id": "Q053",
|
| 757 |
-
"category": "criminal_procedure",
|
| 758 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 12؟",
|
| 759 |
-
"expected": {
|
| 760 |
-
"law": "criminal_procedure",
|
| 761 |
-
"expected_article_numbers": [
|
| 762 |
-
"12"
|
| 763 |
-
],
|
| 764 |
-
"must_cite_articles": true
|
| 765 |
-
},
|
| 766 |
-
"notes": "لمحكمة النقض عند نظر الموضوع للمرة الثانية حق إقامة الدعوى كمحكمة الجنايات، ولا يشترك من قرر الإقامة في نظر الطعن الثاني."
|
| 767 |
-
},
|
| 768 |
-
{
|
| 769 |
-
"id": "Q054",
|
| 770 |
-
"category": "criminal_procedure",
|
| 771 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 13؟",
|
| 772 |
-
"expected": {
|
| 773 |
-
"law": "criminal_procedure",
|
| 774 |
-
"expected_article_numbers": [
|
| 775 |
-
"13"
|
| 776 |
-
],
|
| 777 |
-
"must_cite_articles": true
|
| 778 |
-
},
|
| 779 |
-
"notes": "لمحكمة الجنايات أو النقض إقامة الدعوى على من يخل بأوامرها أو يؤثر في قضائها أو الشهود أثناء نظر الدعوى."
|
| 780 |
-
},
|
| 781 |
-
{
|
| 782 |
-
"id": "Q055",
|
| 783 |
-
"category": "criminal_procedure",
|
| 784 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 14؟",
|
| 785 |
-
"expected": {
|
| 786 |
-
"law": "criminal_procedure",
|
| 787 |
-
"expected_article_numbers": [
|
| 788 |
-
"14"
|
| 789 |
-
],
|
| 790 |
-
"must_cite_articles": true
|
| 791 |
-
},
|
| 792 |
-
"notes": "وفاة المتهم تنهي الدعوى الجنائية، لكن يجوز الحكم بالمصادرة إذا حدثت الوفاة أثناء نظر الدعوى."
|
| 793 |
-
},
|
| 794 |
-
{
|
| 795 |
-
"id": "Q056",
|
| 796 |
-
"category": "criminal_procedure",
|
| 797 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 15؟",
|
| 798 |
-
"expected": {
|
| 799 |
-
"law": "criminal_procedure",
|
| 800 |
-
"expected_article_numbers": [
|
| 801 |
-
"15"
|
| 802 |
-
],
|
| 803 |
-
"must_cite_articles": true
|
| 804 |
-
},
|
| 805 |
-
"notes": "تنقضي الدعوى الجنائية بالتقادم: 10 سنوات للجنايات، 3 سنوات للجنح، سنة للمخالفات. بعض الجرائم الخطيرة (التعذيب، الاعتداء على الحرية) لا تسقط بالتقادم."
|
| 806 |
-
},
|
| 807 |
-
{
|
| 808 |
-
"id": "Q057",
|
| 809 |
-
"category": "criminal_procedure",
|
| 810 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 16؟",
|
| 811 |
-
"expected": {
|
| 812 |
-
"law": "criminal_procedure",
|
| 813 |
-
"expected_article_numbers": [
|
| 814 |
-
"16"
|
| 815 |
-
],
|
| 816 |
-
"must_cite_articles": true
|
| 817 |
-
},
|
| 818 |
-
"notes": "مدة التقادم في الدعوى الجنائية لا تتوقف لأي سبب."
|
| 819 |
-
},
|
| 820 |
-
{
|
| 821 |
-
"id": "Q058",
|
| 822 |
-
"category": "criminal_procedure",
|
| 823 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 17؟",
|
| 824 |
-
"expected": {
|
| 825 |
-
"law": "criminal_procedure",
|
| 826 |
-
"expected_article_numbers": [
|
| 827 |
-
"17"
|
| 828 |
-
],
|
| 829 |
-
"must_cite_articles": true
|
| 830 |
-
},
|
| 831 |
-
"notes": "تنقطع مدة التقادم بإجراءات التحقيق أو الاتهام أو المحاكمة أو الأمر الجنائي، وتبدأ من جديد من يوم الانقطاع."
|
| 832 |
-
},
|
| 833 |
-
{
|
| 834 |
-
"id": "Q059",
|
| 835 |
-
"category": "criminal_procedure",
|
| 836 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 18؟",
|
| 837 |
-
"expected": {
|
| 838 |
-
"law": "criminal_procedure",
|
| 839 |
-
"expected_article_numbers": [
|
| 840 |
-
"18"
|
| 841 |
-
],
|
| 842 |
-
"must_cite_articles": true
|
| 843 |
-
},
|
| 844 |
-
"notes": "انقطاع مدة التقادم لأحد المتهمين يمتد أثره للباقين ما لم تتخذ ضدهم إجراءات مستقلة."
|
| 845 |
-
},
|
| 846 |
-
{
|
| 847 |
-
"id": "Q060",
|
| 848 |
-
"category": "criminal_procedure",
|
| 849 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 18 مكرر؟",
|
| 850 |
-
"expected": {
|
| 851 |
-
"law": "criminal_procedure",
|
| 852 |
-
"expected_article_numbers": [
|
| 853 |
-
"18 مكرر"
|
| 854 |
-
],
|
| 855 |
-
"must_cite_articles": true
|
| 856 |
-
},
|
| 857 |
-
"notes": "يجوز التصالح في المخالفات والجنح البسيطة بدفع ثلث الغرامة قبل رفع الدعوى أو ثلثيها بعد رفعها، وتنقضي الدعوى الجنائية بالتصالح."
|
| 858 |
-
},
|
| 859 |
-
{
|
| 860 |
-
"id": "Q061",
|
| 861 |
-
"category": "criminal_procedure",
|
| 862 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 18 مكرر (أ)؟",
|
| 863 |
-
"expected": {
|
| 864 |
-
"law": "criminal_procedure",
|
| 865 |
-
"expected_article_numbers": [
|
| 866 |
-
"18 مكرر (أ)"
|
| 867 |
-
],
|
| 868 |
-
"must_cite_articles": true
|
| 869 |
-
},
|
| 870 |
-
"notes": "يجوز للمجني عليه أو ورثته الصلح مع المتهم في جرائم معينة (الضرب، السرقة البسيطة، إتلاف المال) وتنقضي الدعوى بالصلح حتى بعد الحكم البات."
|
| 871 |
-
},
|
| 872 |
-
{
|
| 873 |
-
"id": "Q062",
|
| 874 |
-
"category": "criminal_procedure",
|
| 875 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 19؟",
|
| 876 |
-
"expected": {
|
| 877 |
-
"law": "criminal_procedure",
|
| 878 |
-
"expected_article_numbers": [
|
| 879 |
-
"19"
|
| 880 |
-
],
|
| 881 |
-
"must_cite_articles": true
|
| 882 |
-
},
|
| 883 |
-
"notes": "مادة ملغاة."
|
| 884 |
-
},
|
| 885 |
-
{
|
| 886 |
-
"id": "Q063",
|
| 887 |
-
"category": "criminal_procedure",
|
| 888 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 20؟",
|
| 889 |
-
"expected": {
|
| 890 |
-
"law": "criminal_procedure",
|
| 891 |
-
"expected_article_numbers": [
|
| 892 |
-
"20"
|
| 893 |
-
],
|
| 894 |
-
"must_cite_articles": true
|
| 895 |
-
},
|
| 896 |
-
"notes": "مادة ملغاة."
|
| 897 |
-
},
|
| 898 |
-
{
|
| 899 |
-
"id": "Q064",
|
| 900 |
-
"category": "criminal_procedure",
|
| 901 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 21؟",
|
| 902 |
-
"expected": {
|
| 903 |
-
"law": "criminal_procedure",
|
| 904 |
-
"expected_article_numbers": [
|
| 905 |
-
"21"
|
| 906 |
-
],
|
| 907 |
-
"must_cite_articles": true
|
| 908 |
-
},
|
| 909 |
-
"notes": "مأمورو الضبط القضائي يبحثون عن الجرائم ومرتكبيها ويجمعون الاستدلالات اللازمة للتحقيق."
|
| 910 |
-
},
|
| 911 |
-
{
|
| 912 |
-
"id": "Q065",
|
| 913 |
-
"category": "criminal_procedure",
|
| 914 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 22؟",
|
| 915 |
-
"expected": {
|
| 916 |
-
"law": "criminal_procedure",
|
| 917 |
-
"expected_article_numbers": [
|
| 918 |
-
"22"
|
| 919 |
-
],
|
| 920 |
-
"must_cite_articles": true
|
| 921 |
-
},
|
| 922 |
-
"notes": "مأمورو الضبط القضائي يتبعون النائب العام ويخضعون لإشرافه، وله طلب محاسبتهم تأديبياً وجنائياً."
|
| 923 |
-
},
|
| 924 |
-
{
|
| 925 |
-
"id": "Q066",
|
| 926 |
-
"category": "tech_crimes",
|
| 927 |
-
"question": "ما الذي تنظمه المادة 1 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 928 |
-
"expected": {
|
| 929 |
-
"law": "tech_crimes",
|
| 930 |
-
"expected_article_numbers": [
|
| 931 |
-
"1"
|
| 932 |
-
],
|
| 933 |
-
"must_cite_articles": true
|
| 934 |
-
},
|
| 935 |
-
"notes": "تحدد المادة التعريفات الأساسية في القانون مثل الجهاز والوزير المختص والبيانات الإلكترونية والبيانات الشخصية والحكومية، والمعالجة الإلكترونية وتقنية المعلومات، ومقدم الخدمة والمستخدم، والبرنامج والنظام"
|
| 936 |
-
},
|
| 937 |
-
{
|
| 938 |
-
"id": "Q067",
|
| 939 |
-
"category": "tech_crimes",
|
| 940 |
-
"question": "ما الذي تنظمه المادة 2 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 941 |
-
"expected": {
|
| 942 |
-
"law": "tech_crimes",
|
| 943 |
-
"expected_article_numbers": [
|
| 944 |
-
"2"
|
| 945 |
-
],
|
| 946 |
-
"must_cite_articles": true
|
| 947 |
-
},
|
| 948 |
-
"notes": "تُلزم المادة مقدمي الخدمة بحفظ سجلات وبيانات محددة لمدة 180 يومًا، والحفاظ على سريتها وتأمينها وعدم إفشائها إلا بأمر قضائي. كما تُلزمهم بتوفير بيانات تعريفية وترخيصية للمستخدمين والجهات المختصة، وتمكي"
|
| 949 |
-
},
|
| 950 |
-
{
|
| 951 |
-
"id": "Q068",
|
| 952 |
-
"category": "tech_crimes",
|
| 953 |
-
"question": "ما ا��ذي تنظمه المادة 3 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 954 |
-
"expected": {
|
| 955 |
-
"law": "tech_crimes",
|
| 956 |
-
"expected_article_numbers": [
|
| 957 |
-
"3"
|
| 958 |
-
],
|
| 959 |
-
"must_cite_articles": true
|
| 960 |
-
},
|
| 961 |
-
"notes": "تُطبق أحكام القانون على جرائم تقنية المعلومات المرتكبة خارج مصر من غير المصريين متى كان الفعل مجرمًا بالدولة التي وقع فيها، وذلك في حالات محددة مثل ارتباط الجريمة بوسيلة نقل مصرية، أو وجود مجني عليه م"
|
| 962 |
-
},
|
| 963 |
-
{
|
| 964 |
-
"id": "Q069",
|
| 965 |
-
"category": "tech_crimes",
|
| 966 |
-
"question": "ما الذي تنظمه المادة 4 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 967 |
-
"expected": {
|
| 968 |
-
"law": "tech_crimes",
|
| 969 |
-
"expected_article_numbers": [
|
| 970 |
-
"4"
|
| 971 |
-
],
|
| 972 |
-
"must_cite_articles": true
|
| 973 |
-
},
|
| 974 |
-
"notes": "تلتزم السلطات المصرية بتيسير التعاون الدولي وتبادل المعلومات وفق الاتفاقيات أو المعاملة بالمثل لمنع جرائم تقنية المعلومات والمساعدة في التحقيق وتتبع الجناة، ويكون مركز الاستعداد لطوارئ الحاسب والشبكات"
|
| 975 |
-
},
|
| 976 |
-
{
|
| 977 |
-
"id": "Q070",
|
| 978 |
-
"category": "tech_crimes",
|
| 979 |
-
"question": "ما الذي تنظمه المادة 5 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 980 |
-
"expected": {
|
| 981 |
-
"law": "tech_crimes",
|
| 982 |
-
"expected_article_numbers": [
|
| 983 |
-
"5"
|
| 984 |
-
],
|
| 985 |
-
"must_cite_articles": true
|
| 986 |
-
},
|
| 987 |
-
"notes": "يجوز لوزير العدل بالاتفاق مع الوزير المختص منح صفة الضبطية القضائية للعاملين بالجهاز أو لغيرهم ممن تحددهم جهات الأمن القومي، فيما يخص الجرائم المرتبطة بأعمال وظائفهم وفق هذا القانون."
|
| 988 |
-
},
|
| 989 |
-
{
|
| 990 |
-
"id": "Q071",
|
| 991 |
-
"category": "tech_crimes",
|
| 992 |
-
"question": "ما الذي تنظمه المادة 6 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 993 |
-
"expected": {
|
| 994 |
-
"law": "tech_crimes",
|
| 995 |
-
"expected_article_numbers": [
|
| 996 |
-
"6"
|
| 997 |
-
],
|
| 998 |
-
"must_cite_articles": true
|
| 999 |
-
},
|
| 1000 |
-
"notes": "تجيز المادة لجهة التحقيق إصدار أوامر مؤقتة مسببـة لمدة تصل إلى 30 يومًا قابلة للتجديد مرة واحدة لضبط البيانات والأنظمة وتتبعها، والبحث والتفتيش والدخول إلى البرامج وقواعد البيانات، وإلزام مقدم الخدمة "
|
| 1001 |
-
},
|
| 1002 |
-
{
|
| 1003 |
-
"id": "Q072",
|
| 1004 |
-
"category": "tech_crimes",
|
| 1005 |
-
"question": "ما الذي تنظمه المادة 7 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 1006 |
-
"expected": {
|
| 1007 |
-
"law": "tech_crimes",
|
| 1008 |
-
"expected_article_numbers": [
|
| 1009 |
-
"7"
|
| 1010 |
-
],
|
| 1011 |
-
"must_cite_articles": true
|
| 1012 |
-
},
|
| 1013 |
-
"notes": "تنظم المادة إجراءات حجب المواقع المرتبطة بجرائم تقنية المعلومات التي تهدد الأمن القومي، بما يشمل أمر الحجب القضائي وعرضه على المحكمة خلال مدد محددة، والحجب المؤقت في حالات الاستعجال مع تحرير محضر وعرض"
|
| 1014 |
-
},
|
| 1015 |
-
{
|
| 1016 |
-
"id": "Q073",
|
| 1017 |
-
"category": "tech_crimes",
|
| 1018 |
-
"question": "ما الذي تنظمه المادة 8 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 1019 |
-
"expected": {
|
| 1020 |
-
"law": "tech_crimes",
|
| 1021 |
-
"expected_article_numbers": [
|
| 1022 |
-
"8"
|
| 1023 |
-
],
|
| 1024 |
-
"must_cite_articles": true
|
| 1025 |
-
},
|
| 1026 |
-
"notes": "تحدد المادة حق التظلم من أوامر الحجب أمام محكمة الجنايات المختصة بعد مرور سبعة أيام، مع إمكانية تجديد التظلم كل ثلاثة أشهر إذا رُفض، وإجراءات إيداع التظلم وتحديد جلسة والفصل فيه خلال سبعة أيام."
|
| 1027 |
-
},
|
| 1028 |
-
{
|
| 1029 |
-
"id": "Q074",
|
| 1030 |
-
"category": "tech_crimes",
|
| 1031 |
-
"question": "ما الذي تنظمه المادة 9 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 1032 |
-
"expected": {
|
| 1033 |
-
"law": "tech_crimes",
|
| 1034 |
-
"expected_article_numbers": [
|
| 1035 |
-
"9"
|
| 1036 |
-
],
|
| 1037 |
-
"must_cite_articles": true
|
| 1038 |
-
},
|
| 1039 |
-
"notes": "تجيز المادة إصدار أمر مسبب بمنع السفر أو إدراج الاسم على قوائم ترقب الوصول عند الضرورة أو وجود أدلة كافية، مع حق التظلم خلال 15 يومًا وتجديده كل ثلاثة أشهر، والفصل القضائي خلال 15 يومًا، وإمكانية العد"
|
| 1040 |
-
},
|
| 1041 |
-
{
|
| 1042 |
-
"id": "Q075",
|
| 1043 |
-
"category": "tech_crimes",
|
| 1044 |
-
"question": "ما الذي تنظمه المادة 10 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 1045 |
-
"expected": {
|
| 1046 |
-
"law": "tech_crimes",
|
| 1047 |
-
"expected_article_numbers": [
|
| 1048 |
-
"10"
|
| 1049 |
-
],
|
| 1050 |
-
"must_cite_articles": true
|
| 1051 |
-
},
|
| 1052 |
-
"notes": "تنشئ المادة سجلين للخبراء بالجهاز (للعاملين وغير العاملين)، وتحدد خضوعهم لقواعد تنظيم الخبرة أمام القضاء، مع تطبيق قواعد المساءلة الإدارية والتأديبية للخبراء غير العاملين وفق قانونهم، وتُحال تفاصيل ال"
|
| 1053 |
-
},
|
| 1054 |
-
{
|
| 1055 |
-
"id": "Q076",
|
| 1056 |
-
"category": "labor",
|
| 1057 |
-
"question": "ما مضمون المادة 1 في قانون العمل المصري (قانون العمل)؟",
|
| 1058 |
-
"expected": {
|
| 1059 |
-
"law": "labor",
|
| 1060 |
-
"expected_article_numbers": [
|
| 1061 |
-
"1"
|
| 1062 |
-
],
|
| 1063 |
-
"must_cite_articles": true
|
| 1064 |
-
},
|
| 1065 |
-
"notes": "تعريفات شاملة لـ 38 مصطلحاً أساسياً في قانون العمل تشمل: تعريف العامل وصاحب العمل والمتدرج، تفصيل شامل لمكونات الأجر (الأساسي والمتغير بما في ذلك العمولة والعلاوات والمنح والمكافآت والبدلات ونصيب الأر"
|
| 1066 |
-
},
|
| 1067 |
-
{
|
| 1068 |
-
"id": "Q077",
|
| 1069 |
-
"category": "labor",
|
| 1070 |
-
"question": "ما مضمون المادة 2 في قانون العمل المصري (قانون العمل)؟",
|
| 1071 |
-
"expected": {
|
| 1072 |
-
"law": "labor",
|
| 1073 |
-
"expected_article_numbers": [
|
| 1074 |
-
"2"
|
| 1075 |
-
],
|
| 1076 |
-
"must_cite_articles": true
|
| 1077 |
-
},
|
| 1078 |
-
"notes": "تحديد السنة بـ365 يوماً والشهر بـ30 يوماً لأغراض تطبيق القانون، ما لم يتفق الطرفان على خلاف ذلك."
|
| 1079 |
-
},
|
| 1080 |
-
{
|
| 1081 |
-
"id": "Q078",
|
| 1082 |
-
"category": "labor",
|
| 1083 |
-
"question": "ما مضمون المادة 3 في قانون العمل المصري (قانون العمل)؟",
|
| 1084 |
-
"expected": {
|
| 1085 |
-
"law": "labor",
|
| 1086 |
-
"expected_article_numbers": [
|
| 1087 |
-
"3"
|
| 1088 |
-
],
|
| 1089 |
-
"must_cite_articles": true
|
| 1090 |
-
},
|
| 1091 |
-
"notes": "قانون العمل رقم 14 لسنة 2025 هو القانون العام المنظم لجميع علاقات العمل في مصر."
|
| 1092 |
-
},
|
| 1093 |
-
{
|
| 1094 |
-
"id": "Q079",
|
| 1095 |
-
"category": "labor",
|
| 1096 |
-
"question": "ما مضمون المادة 4 في قانون العمل المصري (قانون العمل)؟",
|
| 1097 |
-
"expected": {
|
| 1098 |
-
"law": "labor",
|
| 1099 |
-
"expected_article_numbers": [
|
| 1100 |
-
"4"
|
| 1101 |
-
],
|
| 1102 |
-
"must_cite_articles": true
|
| 1103 |
-
},
|
| 1104 |
-
"notes": "حظر السخرة والعمل الجبري والتحرش والتنمر والعنف بكافة أشكاله (اللفظي والجسدي والنفسي) ضد العمال، مع تحديد جزاءات تأديبية في لوائح المنشأة."
|
| 1105 |
-
},
|
| 1106 |
-
{
|
| 1107 |
-
"id": "Q080",
|
| 1108 |
-
"category": "labor",
|
| 1109 |
-
"question": "ما مضمون المادة 5 في قانون العمل المصري (قانون العمل)؟",
|
| 1110 |
-
"expected": {
|
| 1111 |
-
"law": "labor",
|
| 1112 |
-
"expected_article_numbers": [
|
| 1113 |
-
"5"
|
| 1114 |
-
],
|
| 1115 |
-
"must_cite_articles": true
|
| 1116 |
-
},
|
| 1117 |
-
"notes": "حظر التمييز في العمل لأي سبب، مع استثناء المزايا المقررة قانوناً للمرأة والطفل وذوي الإعاقة والأقزام بالقدر اللازم لحمايتهم ودمجهم."
|
| 1118 |
-
},
|
| 1119 |
-
{
|
| 1120 |
-
"id": "Q081",
|
| 1121 |
-
"category": "labor",
|
| 1122 |
-
"question": "ما مضمون المادة 6 في قانون العمل المصري (قانون العمل)؟",
|
| 1123 |
-
"expected": {
|
| 1124 |
-
"law": "labor",
|
| 1125 |
-
"expected_article_numbers": [
|
| 1126 |
-
"6"
|
| 1127 |
-
],
|
| 1128 |
-
"must_cite_articles": true
|
| 1129 |
-
},
|
| 1130 |
-
"notes": "بطلان أي شرط ينتقص من حقوق العامل، مع استمرار الشروط الأفضل حتى في حالة تغيير ملكية المنشأة."
|
| 1131 |
-
},
|
| 1132 |
-
{
|
| 1133 |
-
"id": "Q082",
|
| 1134 |
-
"category": "labor",
|
| 1135 |
-
"question": "ما مضمون المادة 7 في قانون العمل المصري (قانون العمل)؟",
|
| 1136 |
-
"expected": {
|
| 1137 |
-
"law": "labor",
|
| 1138 |
-
"expected_article_numbers": [
|
| 1139 |
-
"7"
|
| 1140 |
-
],
|
| 1141 |
-
"must_cite_articles": true
|
| 1142 |
-
},
|
| 1143 |
-
"notes": "إعفاء العمال من الرسوم القضائية والدمغة مع النفاذ المعجل وعدم اشتراط محامٍ."
|
| 1144 |
-
},
|
| 1145 |
-
{
|
| 1146 |
-
"id": "Q083",
|
| 1147 |
-
"category": "labor",
|
| 1148 |
-
"question": "ما مضمون المادة 8 في قانون العمل المصري (قانون العمل)؟",
|
| 1149 |
-
"expected": {
|
| 1150 |
-
"law": "labor",
|
| 1151 |
-
"expected_article_numbers": [
|
| 1152 |
-
"8"
|
| 1153 |
-
],
|
| 1154 |
-
"must_cite_articles": true
|
| 1155 |
-
},
|
| 1156 |
-
"notes": "امتياز حقوق العمال على جميع أموال صاحب العمل قبل أي ديون أخرى بما فيها ديون الخزانة العامة."
|
| 1157 |
-
},
|
| 1158 |
-
{
|
| 1159 |
-
"id": "Q084",
|
| 1160 |
-
"category": "labor",
|
| 1161 |
-
"question": "ما مضمون المادة 9 في قانون العمل المصري (قانون العمل)؟",
|
| 1162 |
-
"expected": {
|
| 1163 |
-
"law": "labor",
|
| 1164 |
-
"expected_article_numbers": [
|
| 1165 |
-
"9"
|
| 1166 |
-
],
|
| 1167 |
-
"must_cite_articles": true
|
| 1168 |
-
},
|
| 1169 |
-
"notes": "حماية حقوق العمال في حالة التصفية أو الإفلاس مع إشراف الجهة الإدارية المختصة."
|
| 1170 |
-
},
|
| 1171 |
-
{
|
| 1172 |
-
"id": "Q085",
|
| 1173 |
-
"category": "labor",
|
| 1174 |
-
"question": "ما مضمون المادة 10 في قانون العمل المصري (قانون العمل)؟",
|
| 1175 |
-
"expected": {
|
| 1176 |
-
"law": "labor",
|
| 1177 |
-
"expected_article_numbers": [
|
| 1178 |
-
"10"
|
| 1179 |
-
],
|
| 1180 |
-
"must_cite_articles": true
|
| 1181 |
-
},
|
| 1182 |
-
"notes": "المسؤولية التضامنية لأصحاب العمل المتعددين والوكلاء المفوضين."
|
| 1183 |
-
},
|
| 1184 |
-
{
|
| 1185 |
-
"id": "Q086",
|
| 1186 |
-
"category": "labor",
|
| 1187 |
-
"question": "ما مضمون المادة 11 في قانون العمل المصري (قانون العمل)؟",
|
| 1188 |
-
"expected": {
|
| 1189 |
-
"law": "labor",
|
| 1190 |
-
"expected_article_numbers": [
|
| 1191 |
-
"11"
|
| 1192 |
-
],
|
| 1193 |
-
"must_cite_articles": true
|
| 1194 |
-
},
|
| 1195 |
-
"notes": "استمرار عقود العمل رغم انتقال ملكية المنشأة بأي طريق."
|
| 1196 |
-
},
|
| 1197 |
-
{
|
| 1198 |
-
"id": "Q087",
|
| 1199 |
-
"category": "labor",
|
| 1200 |
-
"question": "ما مضمون المادة 12 في قانون العمل المصري (قانون العمل)؟",
|
| 1201 |
-
"expected": {
|
| 1202 |
-
"law": "labor",
|
| 1203 |
-
"expected_article_numbers": [
|
| 1204 |
-
"12"
|
| 1205 |
-
],
|
| 1206 |
-
"must_cite_articles": true
|
| 1207 |
-
},
|
| 1208 |
-
"notes": "علاوة سنوية لا تقل عن 3% من الأجر التأميني مع إمكانية التخفيض بموافقة المجلس القومي للأجور."
|
| 1209 |
-
},
|
| 1210 |
-
{
|
| 1211 |
-
"id": "Q088",
|
| 1212 |
-
"category": "labor",
|
| 1213 |
-
"question": "ما مضمون المادة 13 في قانون العمل المصري (قانون العمل)؟",
|
| 1214 |
-
"expected": {
|
| 1215 |
-
"law": "labor",
|
| 1216 |
-
"expected_article_numbers": [
|
| 1217 |
-
"13"
|
| 1218 |
-
],
|
| 1219 |
-
"must_cite_articles": true
|
| 1220 |
-
},
|
| 1221 |
-
"notes": "تحديد اختصاصات الجهة الإدارية المختصة بقرار من الوزير."
|
| 1222 |
-
},
|
| 1223 |
-
{
|
| 1224 |
-
"id": "Q089",
|
| 1225 |
-
"category": "labor",
|
| 1226 |
-
"question": "ما مضمون المادة 14 في قانون العمل المصري (قانون العمل)؟",
|
| 1227 |
-
"expected": {
|
| 1228 |
-
"law": "labor",
|
| 1229 |
-
"expected_article_numbers": [
|
| 1230 |
-
"14"
|
| 1231 |
-
],
|
| 1232 |
-
"must_cite_articles": true
|
| 1233 |
-
},
|
| 1234 |
-
"notes": "توزيع حصيلة الغرامات: ثلث للوزارة للخدمات والتدريب، والباقي للخزانة العامة."
|
| 1235 |
-
},
|
| 1236 |
-
{
|
| 1237 |
-
"id": "Q090",
|
| 1238 |
-
"category": "labor",
|
| 1239 |
-
"question": "ما مضمون المادة 15 في قانون العمل المصري (قانون العمل)؟",
|
| 1240 |
-
"expected": {
|
| 1241 |
-
"law": "labor",
|
| 1242 |
-
"expected_article_numbers": [
|
| 1243 |
-
"15"
|
| 1244 |
-
],
|
| 1245 |
-
"must_cite_articles": true
|
| 1246 |
-
},
|
| 1247 |
-
"notes": "تحصيل الرسوم والخدمات وفقاً لقانون وسائل الدفع غير النقدي."
|
| 1248 |
-
},
|
| 1249 |
-
{
|
| 1250 |
-
"id": "Q091",
|
| 1251 |
-
"category": "personal_status",
|
| 1252 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 1؟",
|
| 1253 |
-
"expected": {
|
| 1254 |
-
"law": "personal_status",
|
| 1255 |
-
"expected_article_numbers": [
|
| 1256 |
-
"1"
|
| 1257 |
-
],
|
| 1258 |
-
"must_cite_articles": true
|
| 1259 |
-
},
|
| 1260 |
-
"notes": "تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله."
|
| 1261 |
-
},
|
| 1262 |
-
{
|
| 1263 |
-
"id": "Q092",
|
| 1264 |
-
"category": "personal_status",
|
| 1265 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 2؟",
|
| 1266 |
-
"expected": {
|
| 1267 |
-
"law": "personal_status",
|
| 1268 |
-
"expected_article_numbers": [
|
| 1269 |
-
"2"
|
| 1270 |
-
],
|
| 1271 |
-
"must_cite_articles": true
|
| 1272 |
-
},
|
| 1273 |
-
"notes": "نفقة المطلقة التي تستحقها تعتبر ديناً من تاريخ الطلاق."
|
| 1274 |
-
},
|
| 1275 |
-
{
|
| 1276 |
-
"id": "Q093",
|
| 1277 |
-
"category": "personal_status",
|
| 1278 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 3؟",
|
| 1279 |
-
"expected": {
|
| 1280 |
-
"law": "personal_status",
|
| 1281 |
-
"expected_article_numbers": [
|
| 1282 |
-
"3"
|
| 1283 |
-
],
|
| 1284 |
-
"must_cite_articles": true
|
| 1285 |
-
},
|
| 1286 |
-
"notes": "مادة ملغاة."
|
| 1287 |
-
},
|
| 1288 |
-
{
|
| 1289 |
-
"id": "Q094",
|
| 1290 |
-
"category": "personal_status",
|
| 1291 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 4؟",
|
| 1292 |
-
"expected": {
|
| 1293 |
-
"law": "personal_status",
|
| 1294 |
-
"expected_article_numbers": [
|
| 1295 |
-
"4"
|
| 1296 |
-
],
|
| 1297 |
-
"must_cite_articles": true
|
| 1298 |
-
},
|
| 1299 |
-
"notes": "إذا امتنع الزوج عن الإنفاق وله مال ظاهر نفذ الحكم في ماله. وإن لم يكن له مال وأصر على عدم الإنفاق طلق عليه القاضي. وإن ادعى العجز وأثبته أمهله شهراً."
|
| 1300 |
-
},
|
| 1301 |
-
{
|
| 1302 |
-
"id": "Q095",
|
| 1303 |
-
"category": "personal_status",
|
| 1304 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 5؟",
|
| 1305 |
-
"expected": {
|
| 1306 |
-
"law": "personal_status",
|
| 1307 |
-
"expected_article_numbers": [
|
| 1308 |
-
"5"
|
| 1309 |
-
],
|
| 1310 |
-
"must_cite_articles": true
|
| 1311 |
-
},
|
| 1312 |
-
"notes": "إذا كان الزوج غائباً غيبة قريبة وله مال نفذ الحكم في ماله، وإن لم يكن له مال أعذر إليه القاضي. وإن كان بعيد الغيبة أو مفقوداً ولا مال له طلق عليه القاضي. وتسري الأحكام على المحبوس المعسر."
|
| 1313 |
-
},
|
| 1314 |
-
{
|
| 1315 |
-
"id": "Q096",
|
| 1316 |
-
"category": "personal_status",
|
| 1317 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 6؟",
|
| 1318 |
-
"expected": {
|
| 1319 |
-
"law": "personal_status",
|
| 1320 |
-
"expected_article_numbers": [
|
| 1321 |
-
"6"
|
| 1322 |
-
],
|
| 1323 |
-
"must_cite_articles": true
|
| 1324 |
-
},
|
| 1325 |
-
"notes": "التطليق لعدم الإنفاق يقع رجعياً، وللزوج مراجعة زوجته إذا ثبت يساره واستعد للإنفاق أثناء العدة."
|
| 1326 |
-
},
|
| 1327 |
-
{
|
| 1328 |
-
"id": "Q097",
|
| 1329 |
-
"category": "personal_status",
|
| 1330 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 7؟",
|
| 1331 |
-
"expected": {
|
| 1332 |
-
"law": "personal_status",
|
| 1333 |
-
"expected_article_numbers": [
|
| 1334 |
-
"7"
|
| 1335 |
-
],
|
| 1336 |
-
"must_cite_articles": true
|
| 1337 |
-
},
|
| 1338 |
-
"notes": "مادة ملغاة."
|
| 1339 |
-
},
|
| 1340 |
-
{
|
| 1341 |
-
"id": "Q098",
|
| 1342 |
-
"category": "personal_status",
|
| 1343 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 8؟",
|
| 1344 |
-
"expected": {
|
| 1345 |
-
"law": "personal_status",
|
| 1346 |
-
"expected_article_numbers": [
|
| 1347 |
-
"8"
|
| 1348 |
-
],
|
| 1349 |
-
"must_cite_articles": true
|
| 1350 |
-
},
|
| 1351 |
-
"notes": "إذا ظهر المفقود حياً فزوجته له، إلا إذا تمتع بها الثاني دون علم بحياة الأول فتكون للثاني ما لم يكن عقده في عدة وفاة الأول."
|
| 1352 |
-
},
|
| 1353 |
-
{
|
| 1354 |
-
"id": "Q099",
|
| 1355 |
-
"category": "personal_status",
|
| 1356 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 9؟",
|
| 1357 |
-
"expected": {
|
| 1358 |
-
"law": "personal_status",
|
| 1359 |
-
"expected_article_numbers": [
|
| 1360 |
-
"9"
|
| 1361 |
-
],
|
| 1362 |
-
"must_cite_articles": true
|
| 1363 |
-
},
|
| 1364 |
-
"notes": "للزوجة طلب التفريق إذا وجدت بزوجها عيباً مستحكماً كالجنون أو الجذام أو البرص، سواء كان قبل العقد أو بعده، ما لم تكن عالمة به أو رضيت به."
|
| 1365 |
-
},
|
| 1366 |
-
{
|
| 1367 |
-
"id": "Q100",
|
| 1368 |
-
"category": "personal_status",
|
| 1369 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 10؟",
|
| 1370 |
-
"expected": {
|
| 1371 |
-
"law": "personal_status",
|
| 1372 |
-
"expected_article_numbers": [
|
| 1373 |
-
"10"
|
| 1374 |
-
],
|
| 1375 |
-
"must_cite_articles": true
|
| 1376 |
-
},
|
| 1377 |
-
"notes": "الفرقة بسبب العيب تعتبر طلاقاً بائناً."
|
| 1378 |
-
}
|
| 1379 |
-
]
|
| 1380 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rag_test_questions_10_mixed.json
DELETED
|
@@ -1,172 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"schema_version": "1.0",
|
| 3 |
-
"dataset_version": "2026-02-03-mixed10",
|
| 4 |
-
"created_at": "2026-02-03T19:15:30.097990+00:00",
|
| 5 |
-
"description": "Benchmark set for Egyptian legal RAG assistant: constitutional Qs + multi-law retrieval + procedural gating. (Mixed 10-case smoke test across 5 categories)",
|
| 6 |
-
"laws_in_scope": [
|
| 7 |
-
{
|
| 8 |
-
"key": "constitution",
|
| 9 |
-
"path": "/mnt/data/Egyptian_Constitution_legalnature_only.json"
|
| 10 |
-
},
|
| 11 |
-
{
|
| 12 |
-
"key": "criminal_procedure",
|
| 13 |
-
"path": "/mnt/data/قانون_الإجراءات_الجنائية.json"
|
| 14 |
-
},
|
| 15 |
-
{
|
| 16 |
-
"key": "tech_crimes",
|
| 17 |
-
"path": "/mnt/data/Technology Crimes Law.json"
|
| 18 |
-
},
|
| 19 |
-
{
|
| 20 |
-
"key": "labor",
|
| 21 |
-
"path": "/mnt/data/Egyptian_Labour_Law.json"
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
"key": "personal_status",
|
| 25 |
-
"path": "/mnt/data/Egyptian_Personal Status Laws.json"
|
| 26 |
-
},
|
| 27 |
-
{
|
| 28 |
-
"key": "civil",
|
| 29 |
-
"path": "/mnt/data/Egyptian_Civil.json"
|
| 30 |
-
}
|
| 31 |
-
],
|
| 32 |
-
"defaults": {
|
| 33 |
-
"k": 15,
|
| 34 |
-
"rerank_top_n": 5,
|
| 35 |
-
"recall_k": 5,
|
| 36 |
-
"pass_rule": "pass if recall@k>=1 for questions with expected articles; else pass if answer startswith expected_answer_prefix (if provided)."
|
| 37 |
-
},
|
| 38 |
-
"cases": [
|
| 39 |
-
{
|
| 40 |
-
"id": "Q001",
|
| 41 |
-
"category": "constitutional",
|
| 42 |
-
"question": "ما مضمون المادة 1 من الدستور المصري؟",
|
| 43 |
-
"expected": {
|
| 44 |
-
"law": "constitution",
|
| 45 |
-
"expected_article_numbers": [
|
| 46 |
-
"1"
|
| 47 |
-
],
|
| 48 |
-
"must_cite_articles": true,
|
| 49 |
-
"answer_should_not_add_external_info": true
|
| 50 |
-
},
|
| 51 |
-
"notes": "جمهورية مصر العربية دولة ذات سيادة، موحدة لا تقبل التجزئة، نظامها جمهوري ديمقراطي يقوم على المواطنة وسيادة القانون. الشعب المصري جزء من الأمة العربية والعالم الإسلامي والقارة الأفريقية."
|
| 52 |
-
},
|
| 53 |
-
{
|
| 54 |
-
"id": "Q002",
|
| 55 |
-
"category": "constitutional",
|
| 56 |
-
"question": "ما مضمون المادة 2 من الدستور المصري؟",
|
| 57 |
-
"expected": {
|
| 58 |
-
"law": "constitution",
|
| 59 |
-
"expected_article_numbers": [
|
| 60 |
-
"2"
|
| 61 |
-
],
|
| 62 |
-
"must_cite_articles": true,
|
| 63 |
-
"answer_should_not_add_external_info": true
|
| 64 |
-
},
|
| 65 |
-
"notes": "الإسلام دين الدولة، واللغة العربية لغتها الرسمية، ومبادئ الشريعة الإسلامية هي المصدر الرئيسي للتشريع."
|
| 66 |
-
},
|
| 67 |
-
{
|
| 68 |
-
"id": "Q041",
|
| 69 |
-
"category": "criminal_procedure",
|
| 70 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 1؟",
|
| 71 |
-
"expected": {
|
| 72 |
-
"law": "criminal_procedure",
|
| 73 |
-
"expected_article_numbers": [
|
| 74 |
-
"1"
|
| 75 |
-
],
|
| 76 |
-
"must_cite_articles": true
|
| 77 |
-
},
|
| 78 |
-
"notes": "النيابة العامة هي الجهة الوحيدة المختصة برفع الدعوى الجنائية ومباشرتها، ولا يجوز تركها أو وقفها إلا وفقاً للقانون."
|
| 79 |
-
},
|
| 80 |
-
{
|
| 81 |
-
"id": "Q042",
|
| 82 |
-
"category": "criminal_procedure",
|
| 83 |
-
"question": "وفقاً لقانون الإجراءات الجنائية، ماذا تقرر المادة 2؟",
|
| 84 |
-
"expected": {
|
| 85 |
-
"law": "criminal_procedure",
|
| 86 |
-
"expected_article_numbers": [
|
| 87 |
-
"2"
|
| 88 |
-
],
|
| 89 |
-
"must_cite_articles": true
|
| 90 |
-
},
|
| 91 |
-
"notes": "النائب العام أو أعضاء النيابة العامة يباشرون الدعوى الجنائية، ويجوز تعيين آخرين لأداء هذه الوظيفة وفقاً للقانون."
|
| 92 |
-
},
|
| 93 |
-
{
|
| 94 |
-
"id": "Q066",
|
| 95 |
-
"category": "tech_crimes",
|
| 96 |
-
"question": "ما الذي تنظمه المادة 1 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 97 |
-
"expected": {
|
| 98 |
-
"law": "tech_crimes",
|
| 99 |
-
"expected_article_numbers": [
|
| 100 |
-
"1"
|
| 101 |
-
],
|
| 102 |
-
"must_cite_articles": true
|
| 103 |
-
},
|
| 104 |
-
"notes": "تحدد المادة التعريفات الأساسية في القانون مثل الجهاز والوزير المختص والبيانات الإلكترونية والبيانات الشخصية والحكومية، والمعالجة الإلكترونية وتقنية المعلومات، ومقدم الخدمة والمستخدم، والبرنامج والنظام"
|
| 105 |
-
},
|
| 106 |
-
{
|
| 107 |
-
"id": "Q067",
|
| 108 |
-
"category": "tech_crimes",
|
| 109 |
-
"question": "ما الذي تنظمه المادة 2 في قانون مكافحة جرائم تقنية المعلومات؟",
|
| 110 |
-
"expected": {
|
| 111 |
-
"law": "tech_crimes",
|
| 112 |
-
"expected_article_numbers": [
|
| 113 |
-
"2"
|
| 114 |
-
],
|
| 115 |
-
"must_cite_articles": true
|
| 116 |
-
},
|
| 117 |
-
"notes": "تُلزم المادة مقدمي الخدمة بحفظ سجلات وبيانات محددة لمدة 180 يومًا، والحفاظ على سريتها وتأمينها وعدم إفشائها إلا بأمر قضائي. كما تُلزم��م بتوفير بيانات تعريفية وترخيصية للمستخدمين والجهات المختصة، وتمكي"
|
| 118 |
-
},
|
| 119 |
-
{
|
| 120 |
-
"id": "Q076",
|
| 121 |
-
"category": "labor",
|
| 122 |
-
"question": "ما مضمون المادة 1 في قانون العمل المصري (قانون العمل)؟",
|
| 123 |
-
"expected": {
|
| 124 |
-
"law": "labor",
|
| 125 |
-
"expected_article_numbers": [
|
| 126 |
-
"1"
|
| 127 |
-
],
|
| 128 |
-
"must_cite_articles": true
|
| 129 |
-
},
|
| 130 |
-
"notes": "تعريفات شاملة لـ 38 مصطلحاً أساسياً في قانون العمل تشمل: تعريف العامل وصاحب العمل والمتدرج، تفصيل شامل لمكونات الأجر (الأساسي والمتغير بما في ذلك العمولة والعلاوات والمنح والمكافآت والبدلات ونصيب الأر"
|
| 131 |
-
},
|
| 132 |
-
{
|
| 133 |
-
"id": "Q077",
|
| 134 |
-
"category": "labor",
|
| 135 |
-
"question": "ما مضمون المادة 2 في قانون العمل المصري (قانون العمل)؟",
|
| 136 |
-
"expected": {
|
| 137 |
-
"law": "labor",
|
| 138 |
-
"expected_article_numbers": [
|
| 139 |
-
"2"
|
| 140 |
-
],
|
| 141 |
-
"must_cite_articles": true
|
| 142 |
-
},
|
| 143 |
-
"notes": "تحديد السنة بـ365 يوماً والشهر بـ30 يوماً لأغراض تطبيق القانون، ما لم يتفق الطرفان على خلاف ذلك."
|
| 144 |
-
},
|
| 145 |
-
{
|
| 146 |
-
"id": "Q091",
|
| 147 |
-
"category": "personal_status",
|
| 148 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 1؟",
|
| 149 |
-
"expected": {
|
| 150 |
-
"law": "personal_status",
|
| 151 |
-
"expected_article_numbers": [
|
| 152 |
-
"1"
|
| 153 |
-
],
|
| 154 |
-
"must_cite_articles": true
|
| 155 |
-
},
|
| 156 |
-
"notes": "تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله."
|
| 157 |
-
},
|
| 158 |
-
{
|
| 159 |
-
"id": "Q092",
|
| 160 |
-
"category": "personal_status",
|
| 161 |
-
"question": "في القانون رقم 25 لسنة 1920، ماذا تقرر المادة 2؟",
|
| 162 |
-
"expected": {
|
| 163 |
-
"law": "personal_status",
|
| 164 |
-
"expected_article_numbers": [
|
| 165 |
-
"2"
|
| 166 |
-
],
|
| 167 |
-
"must_cite_articles": true
|
| 168 |
-
},
|
| 169 |
-
"notes": "نفقة المطلقة التي تستحقها تعتبر ديناً من تاريخ الطلاق."
|
| 170 |
-
}
|
| 171 |
-
]
|
| 172 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rag_test_runner.py
DELETED
|
@@ -1,270 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
# -*- coding: utf-8 -*-
|
| 3 |
-
"""
|
| 4 |
-
RAG Test Runner (metrics + CSV)
|
| 5 |
-
--------------------------------
|
| 6 |
-
Runs a JSON test set against either:
|
| 7 |
-
1) a local Python module exposing `ask(question) -> (answer: str, sources: list[dict])`, OR
|
| 8 |
-
2) an HTTP endpoint that returns JSON {"answer": "...", "sources": [...]}
|
| 9 |
-
|
| 10 |
-
Outputs:
|
| 11 |
-
- summary metrics to stdout
|
| 12 |
-
- a CSV report with per-case results (pass/fail + recall@k + rank)
|
| 13 |
-
"""
|
| 14 |
-
from __future__ import annotations
|
| 15 |
-
|
| 16 |
-
import argparse
|
| 17 |
-
import csv
|
| 18 |
-
import importlib
|
| 19 |
-
import json
|
| 20 |
-
import re
|
| 21 |
-
from dataclasses import dataclass
|
| 22 |
-
from datetime import datetime
|
| 23 |
-
from typing import Any, Dict, List, Optional, Tuple
|
| 24 |
-
|
| 25 |
-
try:
|
| 26 |
-
import requests # optional for --mode http
|
| 27 |
-
except Exception: # pragma: no cover
|
| 28 |
-
requests = None # type: ignore
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
@dataclass
|
| 32 |
-
class CaseResult:
|
| 33 |
-
case_id: str
|
| 34 |
-
category: str
|
| 35 |
-
question: str
|
| 36 |
-
expected_articles: List[str]
|
| 37 |
-
got_articles: List[str]
|
| 38 |
-
recall_at_k: float
|
| 39 |
-
rank: Optional[int]
|
| 40 |
-
passed: bool
|
| 41 |
-
fail_reason: Optional[str]
|
| 42 |
-
answer_snippet: str
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
def normalize_article_number(x: Any) -> Optional[str]:
|
| 46 |
-
if x is None:
|
| 47 |
-
return None
|
| 48 |
-
s = str(x).strip()
|
| 49 |
-
s = re.sub(r"\s+", " ", s)
|
| 50 |
-
return s or None
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def extract_articles(sources: Any, key: str) -> List[str]:
|
| 54 |
-
out: List[str] = []
|
| 55 |
-
if not sources:
|
| 56 |
-
return out
|
| 57 |
-
if isinstance(sources, dict):
|
| 58 |
-
sources = [sources]
|
| 59 |
-
if not isinstance(sources, list):
|
| 60 |
-
return out
|
| 61 |
-
|
| 62 |
-
for s in sources:
|
| 63 |
-
if not isinstance(s, dict):
|
| 64 |
-
continue
|
| 65 |
-
val = s.get(key)
|
| 66 |
-
num = normalize_article_number(val)
|
| 67 |
-
if num:
|
| 68 |
-
out.append(num)
|
| 69 |
-
return out
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
def recall_and_rank(expected: List[str], retrieved: List[str], k: int) -> Tuple[float, Optional[int]]:
|
| 73 |
-
if not expected:
|
| 74 |
-
return 0.0, None
|
| 75 |
-
topk = retrieved[:k]
|
| 76 |
-
exp_set = set(expected)
|
| 77 |
-
for i, a in enumerate(topk, start=1):
|
| 78 |
-
if a in exp_set:
|
| 79 |
-
return 1.0, i
|
| 80 |
-
return 0.0, None
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
def run_local(module_name: str, question: str) -> Tuple[str, Any]:
|
| 84 |
-
mod = importlib.import_module(module_name)
|
| 85 |
-
if not hasattr(mod, "ask"):
|
| 86 |
-
raise AttributeError(f"Module '{module_name}' must expose ask(question) -> (answer, sources)")
|
| 87 |
-
answer, sources = mod.ask(question)
|
| 88 |
-
return str(answer), sources
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
def run_http(url: str, question: str, timeout: float = 60.0) -> Tuple[str, Any]:
|
| 92 |
-
if requests is None:
|
| 93 |
-
raise RuntimeError("requests is not installed. Install it or use --mode local.")
|
| 94 |
-
resp = requests.post(url, json={"question": question}, timeout=timeout)
|
| 95 |
-
resp.raise_for_status()
|
| 96 |
-
data = resp.json()
|
| 97 |
-
return str(data.get("answer", "")), data.get("sources", [])
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
def evaluate_case(
|
| 101 |
-
case: Dict[str, Any],
|
| 102 |
-
mode: str,
|
| 103 |
-
module_name: str,
|
| 104 |
-
url: str,
|
| 105 |
-
k: int,
|
| 106 |
-
source_article_key: str,
|
| 107 |
-
) -> CaseResult:
|
| 108 |
-
case_id = str(case.get("id", ""))
|
| 109 |
-
category = str(case.get("category", ""))
|
| 110 |
-
question = str(case.get("question", "")).strip()
|
| 111 |
-
|
| 112 |
-
expected = case.get("expected", {}) or {}
|
| 113 |
-
expected_articles = [normalize_article_number(x) for x in (expected.get("expected_article_numbers") or [])]
|
| 114 |
-
expected_articles = [x for x in expected_articles if x]
|
| 115 |
-
expected_prefix = expected.get("expected_answer_prefix")
|
| 116 |
-
|
| 117 |
-
# Run model
|
| 118 |
-
if mode == "local":
|
| 119 |
-
answer, sources = run_local(module_name, question)
|
| 120 |
-
else:
|
| 121 |
-
answer, sources = run_http(url, question)
|
| 122 |
-
|
| 123 |
-
got_articles = extract_articles(sources, source_article_key)
|
| 124 |
-
|
| 125 |
-
r_at_k, rank = recall_and_rank(expected_articles, got_articles, k)
|
| 126 |
-
|
| 127 |
-
# Pass/fail logic
|
| 128 |
-
passed = True
|
| 129 |
-
fail_reason = None
|
| 130 |
-
|
| 131 |
-
if expected_articles:
|
| 132 |
-
if r_at_k < 1.0:
|
| 133 |
-
passed = False
|
| 134 |
-
fail_reason = f"missed expected articles in top-{k}"
|
| 135 |
-
else:
|
| 136 |
-
# No expected articles => check prefix if provided (procedural general gating)
|
| 137 |
-
if expected_prefix:
|
| 138 |
-
if not answer.strip().startswith(str(expected_prefix).strip()):
|
| 139 |
-
passed = False
|
| 140 |
-
fail_reason = "answer did not follow expected procedural prefix"
|
| 141 |
-
|
| 142 |
-
snippet = answer.strip().replace("\n", " ")
|
| 143 |
-
if len(snippet) > 220:
|
| 144 |
-
snippet = snippet[:220] + "…"
|
| 145 |
-
|
| 146 |
-
return CaseResult(
|
| 147 |
-
case_id=case_id,
|
| 148 |
-
category=category,
|
| 149 |
-
question=question,
|
| 150 |
-
expected_articles=expected_articles,
|
| 151 |
-
got_articles=got_articles[:k],
|
| 152 |
-
recall_at_k=r_at_k,
|
| 153 |
-
rank=rank,
|
| 154 |
-
passed=passed,
|
| 155 |
-
fail_reason=fail_reason,
|
| 156 |
-
answer_snippet=snippet,
|
| 157 |
-
)
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
def main() -> int:
|
| 161 |
-
ap = argparse.ArgumentParser()
|
| 162 |
-
ap.add_argument("--suite", required=True, help="Path to JSON test suite")
|
| 163 |
-
ap.add_argument("--mode", choices=["local", "http"], default="local")
|
| 164 |
-
ap.add_argument("--module", default="rag", help="Python module name for local mode (default: rag)")
|
| 165 |
-
ap.add_argument("--url", default="http://127.0.0.1:8000/ask", help="HTTP endpoint for http mode")
|
| 166 |
-
ap.add_argument("--k", type=int, default=5, help="k for recall@k (default: 5)")
|
| 167 |
-
ap.add_argument("--out", default="rag_test_report.csv", help="Output CSV file path")
|
| 168 |
-
ap.add_argument(
|
| 169 |
-
"--source-article-key",
|
| 170 |
-
default="article_number",
|
| 171 |
-
help="Key used inside each source dict for the article number (default: article_number)",
|
| 172 |
-
)
|
| 173 |
-
args = ap.parse_args()
|
| 174 |
-
|
| 175 |
-
with open(args.suite, "r", encoding="utf-8") as f:
|
| 176 |
-
suite = json.load(f)
|
| 177 |
-
|
| 178 |
-
cases = suite.get("cases") if isinstance(suite, dict) else None
|
| 179 |
-
if not isinstance(cases, list):
|
| 180 |
-
raise ValueError("Invalid suite format: expected {'cases': [...]}")
|
| 181 |
-
|
| 182 |
-
results: List[CaseResult] = []
|
| 183 |
-
|
| 184 |
-
for case in cases:
|
| 185 |
-
if not isinstance(case, dict):
|
| 186 |
-
continue
|
| 187 |
-
|
| 188 |
-
try:
|
| 189 |
-
res = evaluate_case(case, args.mode, args.module, args.url, args.k, args.source_article_key)
|
| 190 |
-
results.append(res) # ✅ FIX: append success results
|
| 191 |
-
except Exception as e:
|
| 192 |
-
cid = str(case.get("id", ""))
|
| 193 |
-
results.append(
|
| 194 |
-
CaseResult(
|
| 195 |
-
case_id=cid,
|
| 196 |
-
category=str(case.get("category", "")),
|
| 197 |
-
question=str(case.get("question", "")),
|
| 198 |
-
expected_articles=[
|
| 199 |
-
str(x)
|
| 200 |
-
for x in (case.get("expected", {}) or {}).get("expected_article_numbers", [])
|
| 201 |
-
],
|
| 202 |
-
got_articles=[],
|
| 203 |
-
recall_at_k=0.0,
|
| 204 |
-
rank=None,
|
| 205 |
-
passed=False,
|
| 206 |
-
fail_reason=f"runner error: {e}",
|
| 207 |
-
answer_snippet="",
|
| 208 |
-
)
|
| 209 |
-
)
|
| 210 |
-
|
| 211 |
-
# Aggregate metrics
|
| 212 |
-
total = len(results)
|
| 213 |
-
passed_cnt = sum(1 for r in results if r.passed)
|
| 214 |
-
pass_rate = (passed_cnt / total) if total else 0.0
|
| 215 |
-
|
| 216 |
-
with_expected = [r for r in results if r.expected_articles]
|
| 217 |
-
avg_recall = (sum(r.recall_at_k for r in with_expected) / len(with_expected)) if with_expected else 0.0
|
| 218 |
-
|
| 219 |
-
# MRR on cases with expected
|
| 220 |
-
rr_vals = [(1.0 / r.rank) for r in with_expected if r.rank]
|
| 221 |
-
mrr = (sum(rr_vals) / len(with_expected)) if with_expected else 0.0
|
| 222 |
-
|
| 223 |
-
# Write CSV
|
| 224 |
-
fieldnames = [
|
| 225 |
-
"id",
|
| 226 |
-
"category",
|
| 227 |
-
"question",
|
| 228 |
-
"expected_articles",
|
| 229 |
-
"retrieved_topk_articles",
|
| 230 |
-
"recall_at_k",
|
| 231 |
-
"rank",
|
| 232 |
-
"passed",
|
| 233 |
-
"fail_reason",
|
| 234 |
-
"answer_snippet",
|
| 235 |
-
]
|
| 236 |
-
with open(args.out, "w", encoding="utf-8-sig", newline="") as f:
|
| 237 |
-
w = csv.DictWriter(f, fieldnames=fieldnames)
|
| 238 |
-
w.writeheader()
|
| 239 |
-
for r in results:
|
| 240 |
-
w.writerow(
|
| 241 |
-
{
|
| 242 |
-
"id": r.case_id,
|
| 243 |
-
"category": r.category,
|
| 244 |
-
"question": r.question,
|
| 245 |
-
"expected_articles": "|".join(r.expected_articles),
|
| 246 |
-
"retrieved_topk_articles": "|".join(r.got_articles),
|
| 247 |
-
"recall_at_k": f"{r.recall_at_k:.3f}",
|
| 248 |
-
"rank": "" if r.rank is None else r.rank,
|
| 249 |
-
"passed": "PASS" if r.passed else "FAIL",
|
| 250 |
-
"fail_reason": r.fail_reason or "",
|
| 251 |
-
"answer_snippet": r.answer_snippet,
|
| 252 |
-
}
|
| 253 |
-
)
|
| 254 |
-
|
| 255 |
-
print("=== RAG Test Summary ===")
|
| 256 |
-
print(f"Timestamp: {datetime.utcnow().isoformat()}Z")
|
| 257 |
-
print(f"Suite: {args.suite}")
|
| 258 |
-
print(f"Mode: {args.mode}")
|
| 259 |
-
print(f"k: {args.k}")
|
| 260 |
-
print(f"Total cases: {total}")
|
| 261 |
-
print(f"Passed: {passed_cnt} ({pass_rate*100:.1f}%)")
|
| 262 |
-
print(f"Avg recall@{args.k} (cases with expected): {avg_recall:.3f}")
|
| 263 |
-
print(f"MRR (cases with expected): {mrr:.3f}")
|
| 264 |
-
print(f"CSV report: {args.out}")
|
| 265 |
-
|
| 266 |
-
return 0 if passed_cnt == total else 1
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
if __name__ == "__main__":
|
| 270 |
-
raise SystemExit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ragas_dataset_100.csv
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
category,question,ground_truth,source_law_key,source_law_name,source_article_id,source_article_number,source_legal_nature
|
| 2 |
-
الدستور,ما الطبيعة القانونية لحق العمل في الدستور المصري؟,حق أساسي/حرية: العمل حق وواجب تكفله الدولة. يُمنع العمل الجبري إلا بقانون ولخدمة عامة وبمقابل عادل.,egyptian_constitution,الدستور المصري,EG-CONST-ART-012,12,حق أساسي/حرية
|
| 3 |
-
الدستور,ما الطبيعة القانونية لحرية الفكر والرأي والتعبير في الدستور المصري؟,حق أساسي/حرية: حرية الفكر والرأي والتعبير مكفولة بكافة الوسائل.,egyptian_constitution,الدستور المصري,EG-CONST-ART-065,65,حق أساسي/حرية
|
| 4 |
-
الدستور,ما الطبيعة القانونية لحق تنظيم الاجتماعات العامة والمواكب والتظاهرات في الدستور المصري؟,حق أساسي/حرية: حق التظاهر والاجتماع السلمي مكفول بالإخطار. الاجتماعات الخاصة لا تتطلب إخطاراً ولا يجوز مراقبتها.,egyptian_constitution,الدستور المصري,EG-CONST-ART-073,73,حق أساسي/حرية
|
| 5 |
-
الدستور,ما مضمون المادة الخاصة بحظر العمل الجبري في الدستور المصري؟,العمل حق، وواجب، وشرف تكفله الدولة. ولا يجوز إلزام أى مواطن بالعمل جبراً، إلا بمقتضى قانون، ولأداء خدمة عامة، لمدة محددة، وبمقابل عادل، ودون إخلال بالحقوق الأساسية للمكلفين بالعمل.,egyptian_constitution,الدستور المصري,EG-CONST-ART-012,12,حق أساسي/حرية
|
| 6 |
-
الدستور,هل يكفل الدستور حرية تكوين الجمعيات والمؤسسات الأهلية؟ وما شروط ذلك بإيجاز؟,تكوين الجمعيات الأهلية حق بالإخطار. تعمل بحرية ولا تُحل إلا بحكم قضائي. تحظر الجمعيات السرية أو العسكرية.,egyptian_constitution,الدستور المصري,EG-CONST-ART-075,75,حق أساسي/حرية
|
| 7 |
-
الدستور,ما الطبيعة القانونية لمبدأ سيادة القانون في الدستور المصري؟,مبدأ دستوري: سيادة القانون هي أساس الحكم، والدولة تخضع للقانون. استقلال القضاء وحصانته ضمانات أساسية للحقوق.,egyptian_constitution,الدستور المصري,EG-CONST-ART-094,94,مبدأ دستوري
|
| 8 |
-
الدستور,ما حدود تحريك الدعاوى لوقف أو مصادرة الأعمال الفنية والأدبية وفق الدستور؟,حرية الإبداع مكفولة. لا يجوز الحبس في جرائم النشر الفني (إلا في حالات محددة كالتحريض على العنف). الدعاوى ضد الأعمال الفنية تكون عبر النيابة العامة.,egyptian_constitution,الدستور المصري,EG-CONST-ART-067,67,حق أساسي/حرية
|
| 9 |
-
الدستور,هل يجوز الحبس في الجرائم المرتكبة بسبب علانية المنتج الفني أو الأدبي وفق الدستور؟,حرية الإبداع مكفولة. لا يجوز الحبس في جرائم النشر الفني (إلا في حالات محددة كالتحريض على العنف). الدعاوى ضد الأعمال الفنية تكون عبر النيابة العامة.,egyptian_constitution,الدستور المصري,EG-CONST-ART-067,67,حق أساسي/حرية
|
| 10 |
-
الدستور,ما مضمون المادة المتعلقة بالحق في التقاضي وعدم سقوط الدعوى بالتقادم في الاعتداء على الحقوق والحريات؟,كل اعتداء على الحرية الشخصية أو حرمة الحياة الخاصة للمواطنين، وغيرها من الحقوق والحريات العامة التى يكفلها الدستور والقانون، جريمة لا تسقط الدعوى الجنائية ولا المدنية الناشئة عنها بالتقادم، وللمضرور إقامة الدعوى الجنائية بالطريق المباشر. وتكفل الدولة تعويضا عادلا لمن وقع عليه الاعتداء، وللمجلس القومى لحقوق الإنسان إبلاغ النيابة العامة عن أى انتهاك لهذه الحقوق، وله أن يتدخل فى الدعوى المدنية منضماً إلى المضرور بناء على طلبه، وذلك كله على الوجه المبين بالقانون.,egyptian_constitution,الدستور المصري,EG-CONST-ART-099,99,غير محدد / يحتاج مراجعة بشرية
|
| 11 |
-
الدستور,ما الفرق بين حق الاجتماع الخاص وحق التظاهر وفق الدستور؟,حق ال��ظاهر والاجتماع السلمي مكفول بالإخطار. الاجتماعات الخاصة لا تتطلب إخطاراً ولا يجوز مراقبتها.,egyptian_constitution,الدستور المصري,EG-CONST-ART-073,73,حق أساسي/حرية
|
| 12 |
-
الدستور,اذكر بإيجاز مضمون المادة التي تقرر أن الوظائف العامة حق للمواطنين على أساس الكفاءة.,الوظائف العامة حق للمواطنين على أساس الكفاءة، ودون محاباة أو وساطة، وتكليف للقائمين بها لخدمة الشعب، وتكفل الدولة حقوقهم وحمايتهم، وقيامهم بأداء واجباتهم فى رعاية مصالح الشعب، ولا يجوز فصلهم بغير الطريق التأديبى، إلا فى الأحوال التى يحددها القانون.,egyptian_constitution,الدستور المصري,EG-CONST-ART-014,14,حق أساسي/حرية
|
| 13 |
-
الدستور,ما التزام الدولة تجاه حقوق العمال وعلاقات العمل وفق الدستور؟,تحافظ الدولة على حقوق العمال وتضمن علاقات عمل متوازنة والتفاوض الجماعي، وتحميهم من المخاطر والفصل التعسفي.,egyptian_constitution,الدستور المصري,EG-CONST-ART-013,13,حق أساسي/حرية
|
| 14 |
-
الدستور,هل يقر الدستور حق الإخطار لتكوين الجمعيات أم يتطلب ترخيصاً؟,تكوين الجمعيات الأهلية حق بالإخطار. تعمل بحرية ولا تُحل إلا بحكم قضائي. تحظر الجمعيات السرية أو العسكرية.,egyptian_constitution,الدستور المصري,EG-CONST-ART-075,75,حق أساسي/حرية
|
| 15 |
-
الدستور,ما القاعدة الدستورية بشأن تدخل الجهة الإدارية في شؤون الجمعيات الأهلية؟,تكوين الجمعيات الأهلية حق بالإخطار. تعمل بحرية ولا تُحل إلا بحكم قضائي. تحظر الجمعيات السرية أو العسكرية.,egyptian_constitution,الدستور المصري,EG-CONST-ART-075,75,حق أساسي/حرية
|
| 16 |
-
الدستور,ما مضمون المادة الخاصة بحرية الإبداع الفني والأدبي؟,حرية الإبداع الفنى والأدبى مكفولة، وتلتزم الدولة بالنهوض بالفنون والآداب، ورعاية المبدعين وحماية إبداعاتهم، وتوفير وسائل التشجيع اللازمة لذلك. ولا يجوز رفع أو تحريك الدعاوى لوقف أو مصادرة الأعمال الفنية والأدبية والفكرية أو ضد مبدعيها إلا عن طريق النيابة العامة، ولا توقع عقوبة سالبة للحرية فى الجرائم التى ترتكب بسبب علانية المنتج الفنى أو الأدبى أو الفكرى، أما الجرائم المتعلقة بالتحريض على العنف أو التمييز بين المواطنين أو الطعن فى أعراض الأفراد، فيحدد القانون عقوباتها. وللمحكمة فى هذه الأحوال إلزام المحكوم عليه بتعويض جزائى للمضرور من الجريمة، إضافة إلى التعويضات الأصلية المستحقة له عما لحقه من أضرار منها، وذلك كله وفقاً للقانون.,egyptian_constitution,الدستور المصري,EG-CONST-ART-067,67,حق أساسي/حرية
|
| 17 |
-
الدستور,في أي حالات يجيز الدستور تنظيم/تقييد بعض الحقوق بمقتضى القانون؟ اذكر الفكرة العامة فقط.,الحقوق والحريات اللصيقة بالمواطن لا تقبل التعطيل أو الانتقاص، ولا يجوز للقانون تقييد جوهرها.,egyptian_constitution,الدستور المصري,EG-CONST-ART-092,92,إحالة للتنظيم التشريعي
|
| 18 |
-
الدستور,ما الطبيعة القانونية لحرمة الحياة الخاصة في الدستور (وفق المادة المتعلقة بالاعتداء عليها)؟,غير محدد / يحتاج مراجعة بشرية: الاعتداء على الحريات جريمة لا تسقط بالتقادم، وللمتضرر حق التعويض وإقامة الدعوى المباشرة. للمجلس القومي لحقوق الإنسان حق التدخل والإبلاغ.,egyptian_constitution,الدستور المصري,EG-CONST-ART-099,99,غير محدد / يحتاج مراجعة بشرية
|
| 19 |
-
الدستور,ما مضمون المادة التي تنص على أن العمل حق وواجب وشرف؟,العمل حق، وواجب، وشرف تكفله الدولة. ولا يجوز إلزام أى مواطن بالعمل جبراً، إلا بمقتضى قانون، ولأداء خدمة عامة، لمدة محددة، وبمقابل عادل، ودون إخلال بالحقوق الأساسية ��لمكلفين بالعمل.,egyptian_constitution,الدستور المصري,EG-CONST-ART-012,12,حق أساسي/حرية
|
| 20 |
-
الدستور,هل الدستور يحظر التمييز/التحريض في سياق جرائم النشر الفني؟ اذكر القيد العام.,حرية الإبداع مكفولة. لا يجوز الحبس في جرائم النشر الفني (إلا في حالات محددة كالتحريض على العنف). الدعاوى ضد الأعمال الفنية تكون عبر النيابة العامة.,egyptian_constitution,الدستور المصري,EG-CONST-ART-067,67,حق أساسي/حرية
|
| 21 |
-
الدستور,ما علاقة مبدأ المواطنة بالنظام الجمهوري الديمقراطي وفق المادة الأولى؟,جمهورية مصر العربية دولة ذات سيادة، موحدة لا تقبل التجزئة، نظامها جمهوري ديمقراطي يقوم على المواطنة وسيادة القانون. الشعب المصري جزء من الأمة العربية والعالم الإسلامي والقارة الأفريقية.,egyptian_constitution,الدستور المصري,EG-CONST-ART-001,1,مبدأ دستوري
|
| 22 |
-
قانون العمل,ما حكم التحرش أو التنمر أو العنف ضد العامل في مكان العمل وفق قانون العمل؟,حظر السخرة والعمل الجبري والتحرش والتنمر والعنف بكافة أشكاله (اللفظي والجسدي والنفسي) ضد العمال، مع تحديد جزاءات تأديبية في لوائح المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-004,4,قاعدة قانونية
|
| 23 |
-
قانون العمل,ما حكم تشغيل العامل سخرة أو جبراً وفق قانون العمل؟,حظر السخرة والعمل الجبري والتحرش والتنمر والعنف بكافة أشكاله (اللفظي والجسدي والنفسي) ضد العمال، مع تحديد جزاءات تأديبية في لوائح المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-004,4,قاعدة قانونية
|
| 24 |
-
قانون العمل,ما أثر أي شرط أو اتفاق ينتقص من حقوق العامل وفق قانون العمل؟,بطلان أي شرط ينتقص من حقوق العامل، مع استمرار الشروط الأفضل حتى في حالة تغيير ملكية المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-006,6,قاعدة قانونية
|
| 25 |
-
قانون العمل,ما القاعدة التي تقرر استمرار المزايا أو الشروط الأفضل للعامل حتى مع انتقال ملكية المنشأة؟,بطلان أي شرط ينتقص من حقوق العامل، مع استمرار الشروط الأفضل حتى في حالة تغيير ملكية المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-006,6,قاعدة قانونية
|
| 26 |
-
قانون العمل,كيف ينظم قانون العمل حق الإضراب بشكل عام؟,الإضراب يوقف التزامات عقد العمل طوال مدة الإضراب.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-235,235,قاعدة قانونية
|
| 27 |
-
قانون العمل,ما شرط استنفاد التسوية الودية قبل إعلان الإضراب وفق قانون العمل؟,حق الإضراب مكفول بعد استنفاد التسوية الودية، ويُنظم عبر المنظمة النقابية أو المفوض العمالي وفق ضوابط القانون.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-231,231,قاعدة قانونية
|
| 28 |
-
قانون العمل,من الجهة التي تعلن وتنظم الإضراب وفق قانون العمل؟,الإضراب يوقف التزامات عقد العمل طوال مدة الإضراب.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-235,235,قاعدة قانونية
|
| 29 |
-
قانون العمل,متى يُحظر الإضراب في المنشآت الحيوية وفق قانون العمل؟,يُحظر الإضراب في المنشآت الحيوية والخدمات الأساسية أو الظروف الاستثنائية، ورئيس الوزراء يحددها بقرار.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-234,234,قاعدة قانونية
|
| 30 |
-
قانون العمل,من يصدر قرار تحديد المنشآت الحيوية والخدمات الأساسية المحظور الإضراب فيها؟,يُحظر الإضراب في المنشآت الحيوية والخدمات الأساسية أو الظروف الاستثنائية، ورئيس الوزراء يحددها بقرار.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-234,234,قاعدة قانونية
|
| 31 |
-
قانون العمل,اذكر بإيجاز تعريف العمل المؤقت في قانون العمل.,تعريف أنماط العمل الجديدة وذكر صورها الرئيسية مثل العمل عن بُعد، الجزئي، المرن، وتقاسم العمل.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-096,96,قاعدة قانونية
|
| 32 |
-
قانون العمل,اذكر بإيجاز تعريف العمل العرضي في قانون العمل.,تعريف أنماط العمل الجديدة وذكر صورها الرئيسية مثل العمل عن بُعد، الجزئي، المرن، وتقاسم العمل.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-096,96,قاعدة قانونية
|
| 33 |
-
قانون العمل,اذكر بإيجاز تعريف العمل الموسمي في قانون العمل.,تعريف أنماط العمل الجديدة وذكر صورها الرئيسية مثل العمل عن بُعد، الجزئي، المرن، وتقاسم العمل.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-096,96,قاعدة قانونية
|
| 34 |
-
قانون العمل,اذكر بإيجاز مفهوم الأجر التأميني كما ورد في تعريفات قانون العمل.,علاوة سنوية لا تقل عن 3% من الأجر التأميني مع إمكانية التخفيض بموافقة المجلس القومي للأجور.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-012,12,قاعدة قانونية
|
| 35 |
-
قانون العمل,ما المقصود بـ(موقع العمل) وفق تعريفات قانون العمل؟,تعريف أنماط العمل الجديدة وذكر صورها الرئيسية مثل العمل عن بُعد، الجزئي، المرن، وتقاسم العمل.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-096,96,قاعدة قانونية
|
| 36 |
-
قانون العمل,ما المقصود بـ(المنشأة) وفق تعريفات قانون العمل؟,تعريف المنشأة: أي مشروع/مرفق يملكه أو يديره أشخاص القانون العام أو الخاص.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-243,243,قاعدة قانونية
|
| 37 |
-
قانون العمل,ما المقصود بـ(المهنة أو الحرفة) وفق تعريفات قانون العمل؟,ينظم شكل ومحتوى اتفاق التدرج ومتطلبات المكافأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-029,29,قاعدة قانونية تنظيمية
|
| 38 |
-
قانون العمل,ما المقصود بـ(الوزارة المختصة) و(الجهة الإدارية المختصة) وفق قانون العمل؟,تحديد اختصاصات الجهة الإدارية المختصة بقرار من الوزير.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-013,13,قاعدة قانونية
|
| 39 |
-
قانون العمل,هل يجيز قانون العمل إبراء العامل من حقوقه أثناء سريان العقد؟,يلتزم صاحب العمل بتمكين العامل من الاطلاع على درجته الوظيفية وعناصر أجره، وإعطائه شهادة خبرة وكفاءة مجاناً عند الطلب أثناء سريان العقد أو بعد انتهائه.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-175,175,قاعدة قانونية
|
| 40 |
-
قانون العمل,كيف يتعامل قانون العمل مع الشروط الأفضل المقررة بالعقد أو اللائحة أو العرف؟,بطلان أي شرط ينتقص من حقوق العامل، مع استمرار الشروط الأفضل حتى في حالة تغيير ملكية المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-006,6,قاعدة قانونية
|
| 41 |
-
قانون العمل,ما القاعدة العامة بشأن حظر العنف اللفظي/الجسدي/النفسي على العامل؟,حظر السخرة والعمل الجبري والتحرش والتنمر والعنف بكافة أشكاله (اللفظي والجسدي والنفسي) ضد العمال، مع تحديد جزاءات تأديبية في لوائح المنشأة.,Egyptian_Labour_Law,قانون العمل,EG-LABOR-LAW-14-2025-ART-004,4,قاعدة قانونية
|
| 42 |
-
الإجراءات الجنائية,ما الخطوات العامة لتحرير محضر في قسم الشرطة في مصر؟,يحدد القانون مأموري الضبط القضائي وهم: أعضاء النيابة، ضباط الشرطة وأمناؤها، العمد، نظار المحطات، وغيرهم. ويجوز بقرار وزاري منح صفة الضبط القضائي لموظفين آخرين.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-023,23,تحديد اختصاص
|
| 43 |
-
الإجراءات الجنائية,ما الخطوات العامة لتقديم بلاغ للنيابة العامة؟,سلطة النيابة العامة في الإفراج عن المتهم في أي وقت.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-204,204,سلطة النيابة العامة
|
| 44 |
-
الإج��اءات الجنائية,ما الفرق العام بين محضر إثبات حالة ومحضر جنحة؟,سلطة قاضي التحقيق في الانتقال لإثبات حالة مسرح الجريمة والأدلة المادية.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-090,90,سلطة قاضي التحقيق
|
| 45 |
-
الإجراءات الجنائية,ما الإجراءات العامة للتظلم من الحبس الاحتياطي؟,حق النيابة العامة في طلب الحبس الاحتياطي في أي وقت.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-137,137,سلطة النيابة العامة
|
| 46 |
-
الإجراءات الجنائية,ما المقصود بالتلبس وما أثره الإجرائي بشكل عام؟,لمأمور الضبط القضائي في التلبس منع الحاضرين من المغادرة حتى تحرير المحضر واستدعاء من يفيد في التحقيق.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-032,32,سلطة الضبط القضائي
|
| 47 |
-
الإجراءات الجنائية,متى يجوز القبض بدون إذن في القواعد العامة للإجراءات الجنائية؟,في جرائم الشكوى المتلبس بها لا يجوز القبض إلا إذا قدمت الشكوى ممن يملكها، ويجوز تقديمها لرجل السلطة العامة الحاضر.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-039,39,قيد على القبض
|
| 48 |
-
الإجراءات الجنائية,ما هي الحقوق الإجرائية الأساسية للمتهم أثناء التحقيق؟,للمتضرر من الجريمة الادعاء بحقوق مدنية أثناء التحقيق، ويفصل قاضي التحقيق نهائياً في قبوله.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-076,76,حق قانوني
|
| 49 |
-
الإجراءات الجنائية,ما هي القواعد العامة لسماع الشهود في التحقيقات؟,جواز الطعن في الأحكام الصادرة على الشهود وفقاً للقواعد المقررة.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-120,120,حق قانوني
|
| 50 |
-
الإجراءات الجنائية,كيف يتم طلب ندب خبير في التحقيقات وفق الإجراءات العامة؟,لوزير العدل طلب ندب مستشار من محكمة الاستئناف للتحقيق في جرائم معينة بقرار من الجمعية العامة.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-065,65,قاعدة إجرائية
|
| 51 |
-
الإجراءات الجنائية,ما الفرق بين الاستدعاء والضبط والإحضار بشكل عام؟,سلطة قاضي التحقيق في إصدار أمر الحضور أو الضبط والإحضار.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-126,126,سلطة قاضي التحقيق
|
| 52 |
-
الإجراءات الجنائية,ما الإجراءات العامة لعمل توكيل لمحامٍ للحضور أمام النيابة؟,تطبيق أحكام الشهود أمام قاضي التحقيق على التحقيق أمام النيابة العامة.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-208,208,إحالة قانونية
|
| 53 |
-
الإجراءات الجنائية,ما الخطوات العامة للإبلاغ عن جريمة إلكترونية رسمياً؟,لكل شخص علم بوقوع جريمة عامة (لا تتطلب شكوى) أن يبلغ النيابة أو الضبط القضائي عنها.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-025,25,حق قانوني
|
| 54 |
-
الإجراءات الجنائية,ما الإجراءات العامة لطلب صلح/تصالح في جنحة (إن أمكن)؟,وجوب حضور المتهم أو نائبه عند التفتيش إن أمكن، وكذلك صاحب المنزل.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-092,92,ضمانة إجرائية
|
| 55 |
-
الإجراءات الجنائية,ما القواعد العامة لإعلان الخصوم في الدعاوى الجنائية؟,حق الخصوم في الاطلاع على أوراق الدعوى بعد إعلانهم.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-236,236,ضمانة إجرائية
|
| 56 |
-
الإجراءات الجنائية,ما المقصود بأمر الحفظ وأثره بشكل عام؟,يجب على النيابة إعلان أمر الحفظ للمجني عليه والمدعي بالحقوق المدنية أو ورثتهما.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-062,62,واجب إجرائي
|
| 57 |
-
الإجراءات الجنائية,ما الفرق بين قرار الإحالة وأمر ألا وجه لإقامة الدعوى بشكل عام؟,صدور الأمر بلا وجه لإقامة الدعوى من النيابة العامة والإفراج عن المتهم.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-209,209,سلطة النيابة العامة
|
| 58 |
-
الإجراءات الجنائية,ما الخطوات العامة للتظلم من قرار حفظ محضر؟,للنيابة العامة حفظ الأوراق إذا رأت عدم وجود محل للسير في الدعوى.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-061,61,سلطة النيابة العامة
|
| 59 |
-
الإجراءات الجنائية,كيف يتم إثبات الإصابات في محضر (الإجراءات العامة)؟,صدور الحكم علناً وإجراءات ضمان حضور المتهم.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-303,303,قاعدة إجرائية
|
| 60 |
-
الإجراءات الجنائية,ما إجراءات طلب استخراج شهادة تحركات/تحريات في قضية (بشكل عام)؟,حق الخصوم في المعارضة في سماع شهادة الشهود غير المعلنين.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-379,379,ضمانة إجرائية
|
| 61 |
-
الإجراءات الجنائية,ما الإجراءات العامة للتعامل مع بلاغ سرقة (من وقت البلاغ حتى التحقيق)؟,للنيابة العامة الاطلاع على أوراق التحقيق في أي وقت دون تأخير سير التحقيق.,criminal_law,قانون الإجراءات الجنائية,EG-CRIM-PROC-ART-080,80,سلطة النيابة العامة
|
| 62 |
-
جرائم تقنية المعلومات,ما حكم اختراق بريد إلكتروني أو حساب خاص وفق قانون مكافحة جرائم تقنية المعلومات؟,تجرم المادة إخفاء أو العبث بالأدلة الرقمية من قبل مدير موقع أو حساب أو بريد إلكتروني بقصد إعاقة عمل الجهات الرسمية.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-028,28,تجريم وعقوبة
|
| 63 |
-
جرائم تقنية المعلومات,ما العقوبة العامة لاختراق حساب خاص لشخص طبيعي وفق قانون جرائم تقنية المعلومات؟,تلتزم السلطات المصرية بتيسير التعاون الدولي وتبادل المعلومات وفق الاتفاقيات أو المعاملة بالمثل لمنع جرائم تقنية المعلومات والمساعدة في التحقيق وتتبع الجناة، ويكون مركز الاستعداد لطوارئ الحاسب والشبكات بالجهاز نقطة الاتصال الفنية.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-004,4,تعاون دولي
|
| 64 |
-
جرائم تقنية المعلومات,كيف تختلف العقوبة إذا كان الحساب يخص شخصاً اعتبارياً خاصاً وفق قانون جرائم تقنية المعلومات؟,تلتزم السلطات المصرية بتيسير التعاون الدولي وتبادل المعلومات وفق الاتفاقيات أو المعاملة بالمثل لمنع جرائم تقنية المعلومات والمساعدة في التحقيق وتتبع الجناة، ويكون مركز الاستعداد لطوارئ الحاسب والشبكات بالجهاز نقطة الاتصال الفنية.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-004,4,تعاون دولي
|
| 65 |
-
جرائم تقنية المعلومات,ما حكم نشر صور أو معلومات تنتهك خصوصية شخص دون رضاه عبر الإنترنت؟,تجرم المادة الاعتداء على القيم الأسرية أو الخصوصية عبر الرسائل الكثيفة دون موافقة، أو تسليم بيانات للترويج دون موافقة، أو نشر محتوى ينتهك الخصوصية سواء كان صحيحًا أو غير صحيح.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 66 |
-
جرائم تقنية المعلومات,ما حكم إرسال رسائل إلكترونية بكثافة لشخص دون موافقته؟,تجرم المادة الاعتداء على القيم الأسرية أو الخصوصية عبر الرسائل الكثيفة دون موافقة، أو تسليم بيانات للترويج دون موافقة، أو نشر محتوى ينتهك الخصوصية سواء كان صحيحًا أو غير صحيح.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 67 |
-
جرائم تقنية المعلومات,ما حكم منح بيانات شخصية لموقع للترويج دون موافقة صاحبها؟,تجرم المادة الاعتداء على القيم الأسرية أو الخصوصية عبر الر��ائل الكثيفة دون موافقة، أو تسليم بيانات للترويج دون موافقة، أو نشر محتوى ينتهك الخصوصية سواء كان صحيحًا أو غير صحيح.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 68 |
-
جرائم تقنية المعلومات,هل تُعاقب جريمة انتهاك الخصوصية حتى لو كانت المعلومات المنشورة صحيحة؟,تجرم المادة الاعتداء على القيم الأسرية أو الخصوصية عبر الرسائل الكثيفة دون موافقة، أو تسليم بيانات للترويج دون موافقة، أو نشر محتوى ينتهك الخصوصية سواء كان صحيحًا أو غير صحيح.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 69 |
-
جرائم تقنية المعلومات,ما نطاق الأفعال التي تجرمها المادة المتعلقة بحرمة الحياة الخاصة في قانون جرائم تقنية المعلومات؟,تلتزم السلطات المصرية بتيسير التعاون الدولي وتبادل المعلومات وفق الاتفاقيات أو المعاملة بالمثل لمنع جرائم تقنية المعلومات والمساعدة في التحقيق وتتبع الجناة، ويكون مركز الاستعداد لطوارئ الحاسب والشبكات بالجهاز نقطة الاتصال الفنية.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-004,4,تعاون دولي
|
| 70 |
-
جرائم تقنية المعلومات,هل يعتبر نشر محتوى ينتهك الحياة الخاصة عبر الشبكة المعلوماتية جريمة؟,تجرم المادة الاعتداء على القيم الأسرية أو الخصوصية عبر الرسائل الكثيفة دون موافقة، أو تسليم بيانات للترويج دون موافقة، أو نشر محتوى ينتهك الخصوصية سواء كان صحيحًا أو غير صحيح.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 71 |
-
جرائم تقنية المعلومات,ما الفرق بين اختراق الحساب وإتلاف/تعطيل البريد الإلكتروني وفق قانون جرائم تقنية المعلومات؟,تجرم المادة الاعتداء على البريد الإلكتروني أو المواقع أو الحسابات الخاصة بالإتلاف أو التعطيل أو الاختراق، وتشدد العقوبة إذا تعلق الأمر بشخص اعتباري خاص.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-018,18,تجريم وعقوبة
|
| 72 |
-
جرائم تقنية المعلومات,في حالة تعرض شخص لابتزاز إلكتروني، ما الخطوات القانونية العامة للإبلاغ وجمع الأدلة الرقمية؟,تمنح المادة للأدلة الرقمية المستخرجة من الأنظمة والأجهزة والوسائط الإلكترونية حجية الأدلة المادية في الإثبات الجنائي متى توافرت الشروط الفنية المحددة باللائحة التنفيذية.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-011,11,قاعدة إثبات
|
| 73 |
-
جرائم تقنية المعلومات,ما الدليل الرقمي الذي يُفضّل حفظه قبل الإبلاغ عن اختراق حساب؟,عند تعدد المستأجرين يفضل من سبق لوضع اليد أو سجل عقده بحسن نية، وإلا فطلب التعويض.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0573,573,حكم تقريري
|
| 74 |
-
جرائم تقنية المعلومات,ما حكم انتحال شخصية على الإنترنت والإجراءات العامة للتبليغ؟,العقوبة شخصية ولا تفرض إلا بقانون وحكم قضائي، ولا تسري القوانين العقابية بأثر رجعي.,egyptian_constitution,الدستور المصري,EG-CONST-ART-095,95,غير محدد / يحتاج مراجعة بشرية
|
| 75 |
-
جرائم تقنية المعلومات,إذا تم نشر بياناتك الشخصية بدون إذن، ما الإجراءات العامة لطلب إزالتها والتبليغ؟,لا يغير المستأجر العين بدون إذن إلا إذا لم يضر، وإلا ألزم بإعادة الحال والتعويض.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0580,580,التزام / جزاء
|
| 76 |
-
جرائم تقنية المعلومات,ما النص الذي يجرم الاعتداء على القيم الأسرية/حرمة الحياة الخاصة عبر الإنترنت (وما فكرته العامة)؟,يعاقب بالحبس مدة لا تقل عن ستة أشهر وبغ��امة لا تقل عن خمسين ألف جنيه ولا تجاوز مائة ألف جنيه، أو بإحدى هاتين العقوبتين، كل من اعتدى على أى من المبادئ أو القيم الأسرية فى المجتمع المصرى أو انتهك حرمة الحياة الخاصة، أو أرسل بكثافة العديد من الرسائل الإلكترونية لشخص معين دون موافقته، أو منح بيانات شخصية إلى نظام أو موقع إلكترونى للترويج للسلع أو الخدمات دون موافقته، أو نشر عن طريق الشبكة المعلوماتية أو إحدى وسائل تقنية المعلومات معلومات أو أخبارًا أو صورًا وما فى حكمها تنتهك خصوصية أى شخص دون رضاه سواء كانت المعلومات المنشورة صحيحة أو غير صحيحة.,tech_crimes,قانون مكافحة جرائم تقنية المعلومات,EG-TECH-ART-025,25,تجريم وعقوبة
|
| 77 |
-
الأحوال الشخصية,ما الشروط العامة لاستحقاق الزوجة النفقة وفق قانون الأحوال الشخصية؟,تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله.,personal_status,قانون الأحوال الشخصية,EG-PSL-1920-ART-001,1,حكم تنظيمي
|
| 78 |
-
الأحوال الشخصية,متى تسقط نفقة الزوجة؟ (بصياغة عامة),تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله.,personal_status,قانون الأحوال الشخصية,EG-PSL-1920-ART-001,1,حكم تنظيمي
|
| 79 |
-
الأحوال الشخصية,ما القواعد العامة لحضانة الصغير وترتيبها في قانون الأحوال الشخصية؟,تنفذ أحكام تسليم الصغير أو ضمه أو رؤيته أو سكناه وفقاً للمادتين 67 و69 من القانون 1/2000 بالإجراءات المبينة في هذا القرار.,personal_status,قانون الأحوال الشخصية,EG-PSL-2000-MD1087-ART-001,1,حكم إجرائي
|
| 80 |
-
الأحوال الشخصية,ما الحالات العامة التي تتيح للزوجة طلب الطلاق للضرر؟,كنايات الطلاق لا يقع بها الطلاق إلا إذا نوى الزوج الطلاق.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-004,4,حكم تنظيمي
|
| 81 |
-
الأحوال الشخصية,ما الفرق العام بين الطلاق والخلع من حيث الإجراءات والآثار؟,كنايات الطلاق لا يقع بها الطلاق إلا إذا نوى الزوج الطلاق.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-004,4,حكم تنظيمي
|
| 82 |
-
الأحوال الشخصية,ما القواعد العامة لإثبات الزواج أو الطلاق عند النزاع؟,كنايات الطلاق لا يقع بها الطلاق إلا إذا نوى الزوج الطلاق.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-004,4,حكم تنظيمي
|
| 83 |
-
الأحوال الشخصية,ما المبدأ العام في رؤية الصغير وتنظيمها؟,تنفذ أحكام تسليم الصغير أو ضمه أو رؤيته أو سكناه وفقاً للمادتين 67 و69 من القانون 1/2000 بالإجراءات المبينة في هذا القرار.,personal_status,قانون الأحوال الشخصية,EG-PSL-2000-MD1087-ART-001,1,حكم إجرائي
|
| 84 |
-
الأحوال الشخصية,ما هي المستندات العامة المطلوبة لرفع دعوى نفقة؟,لا تسمع دعوى نفقة العدة لأكثر من سنة من تاريخ الطلاق، ولا دعوى الإرث للمطلقة بعد سنة من الطلاق.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-017,17,حكم تنظيمي
|
| 85 |
-
الأحوال الشخصية,ما القواعد العامة لإثبات دخل الزوج في دعوى نفقة؟,تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله.,personal_status,قانون الأحوال الشخصية,EG-PSL-1920-ART-001,1,حكم تنظيمي
|
| 86 |
-
الأحوال الشخصية,ما هي الآثار العامة لثبوت النسب؟,لا تسمع دعوى النسب إذا ثبت عدم التلاقي بين الزوجين، أو أتت الزوجة بالولد بعد سنة من غيبة الزوج أو طلاقها أو وفاته.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-015,15,حكم تنظيمي
|
| 87 |
-
الأحوال الشخصية,ما الإجراءات العامة لعمل إعلام وراثة؟,لا يلزم اتباع إجراءات الحصر والتحفظ إذا كان المال لا يتجاوز 3000 جنيه.,personal_status,قانون الأحوال الشخصية,EG-PSL-2000-ART-035,35,حكم إجرائي
|
| 88 |
-
الأحوال الشخصية,كيف تُقدَّر نفقة الصغير بشكل عام؟,تنفذ أحكام تسليم الصغير أو ضمه أو رؤيته أو سكناه وفقاً للمادتين 67 و69 من القانون 1/2000 بالإجراءات المبينة في هذا القرار.,personal_status,قانون الأحوال الشخصية,EG-PSL-2000-MD1087-ART-001,1,حكم إجرائي
|
| 89 |
-
الأحوال الشخصية,ما الفرق بين نفقة الزوجة ونفقة الصغير؟,تجب النفقة للزوجة من تاريخ العقد الصحيح وتشمل الغذاء والكسوة والمسكن والعلاج. لا تجب النفقة إذا ارتدت أو امتنعت عن تسليم نفسها أو خرجت بدون إذن. نفقة الزوجة دين على الزوج ولها امتياز على أمواله.,personal_status,قانون الأحوال الشخصية,EG-PSL-1920-ART-001,1,حكم تنظيمي
|
| 90 |
-
الأحوال الشخصية,ما القواعد العامة للمتعة والمؤخر بعد الطلاق؟,كنايات الطلاق لا يقع بها الطلاق إلا إذا نوى الزوج الطلاق.,personal_status,قانون الأحوال الشخصية,EG-PSL-1929-ART-004,4,حكم تنظيمي
|
| 91 |
-
الأحوال الشخصية,متى تُمنع الحاضنة من الحضانة بشكل عام؟,يجوز للنيابة العامة إصدار قرار مسبب بتسليم الصغير مؤقتاً لمن تتحقق مصلحته معها عند وجود نزاع على الحضانة.,personal_status,قانون الأحوال الشخصية,EG-PSL-2000-ART-070,70,حكم إجرائي
|
| 92 |
-
القانون المدني,ما المقصود بالالتزام في القانون المدني؟,في الالتزام ببذل عناية يوفي المدين بالتزامه ببذل عناية الشخص العادي، ويبقى مسؤولاً عن الغش والخطأ الجسيم.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0211,211,التزام / حكم تقريري
|
| 93 |
-
القانون المدني,ما الفرق بين البطلان المطلق والبطلان النسبي في القانون المدني؟,العقد الباطل يجوز لكل ذي مصلحة التمسك ببطلانه وللمحكمة القضاء به تلقائياً، ولا يزول بالإجازة. وتسقط دعوى البطلان بـ15 سنة.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0141,141,تعريف / حكم تقريري
|
| 94 |
-
القانون المدني,متى يكون العقد قابلاً للإبطال؟,إذا بطل شق من العقد بطل وحده، إلا إذا تبين أن العقد ما كان ليتم بدونه فيبطل كله (انتقاص العقد).,civil_law,القانون المدني المصري,EG-CIVIL-ART-0143,143,تعريف / حكم تقريري
|
| 95 |
-
القانون المدني,ما هي أركان العقد في القانون المدني بإيجاز؟,إذا بطل العقد وتوافرت فيه أركان عقد آخر، يصح باعتباره ذلك العقد إذا كانت نية المتعاقدين تنصرف إليه (تحول العقد).,civil_law,القانون المدني المصري,EG-CIVIL-ART-0144,144,تعريف / حكم تقريري
|
| 96 |
-
القانون المدني,ما المقصود بالمسؤولية التقصيرية؟,كل خطأ سبب ضرراً للغير يُلزم مرتكبه بالتعويض (القاعدة العامة في المسؤولية التقصيرية).,civil_law,القانون المدني المصري,EG-CIVIL-ART-0163,163,التزام
|
| 97 |
-
القانون المدني,ما شروط استحقاق التعويض عن الفعل الضار؟,الالتزامات غير التعاقدية تخضع لقانون بلد وقوع الفعل المنشئ. لكن الأفعال الضارة في الخارج المشروعة في مصر لا تُعد غير مشروعة.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0021,21,تعريف / حكم تقريري
|
| 98 |
-
القانون المدني,ما الفرق بين الفسخ والبطلان في العقود؟,في العقود الملزمة للجانبين، عند عدم الوفاء يجوز للدائن بعد الإعذار طلب التنفيذ أو الفسخ مع التعويض. ويجوز للقاضي منح أجل أو رفض الفسخ إذا ك��ن الإخلال قليل الأهمية.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0157,157,حق / رخصة
|
| 99 |
-
القانون المدني,ما المقصود بالإثراء بلا سبب وما أثره؟,تسقط دعوى الإثراء بلا سبب بـ3 سنوات من العلم أو 15 سنة من نشوء الحق.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0180,180,إجراءات / تنظيم
|
| 100 |
-
القانون المدني,ما المقصود بالعيب في الرضا (غلط/تدليس/إكراه) بشكل عام؟,إذا أخطر المشتري البائع بالعيب في الوقت الملائم كان له الرجوع بالضمان وفق أحكام المادة 444.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0450,450,حق / إحالة تشريعية
|
| 101 |
-
القانون المدني,ما القاعدة العامة في تنفيذ العقد بحسن نية؟,للمستحق طلب تنفيذ العقد أو فسخه مع التعويض إذا كان بعوض ولم يقم المدين بالتزامه.,civil_law,القانون المدني المصري,EG-CIVIL-ART-0746,746,جزاء
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ragas_eval.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
| 1 |
-
# ragas_eval.py
|
| 2 |
-
# تقييم نظام RAG باستخدام Ragas
|
| 3 |
-
# الاستخدام:
|
| 4 |
-
# 1) ضعي الملف ragas_dataset_100.csv بجانب هذا السكربت (أو عدّلي DATASET_PATH)
|
| 5 |
-
# 2) تأكدي أن دالة ask() تعمل وتُرجع (answer, sources)
|
| 6 |
-
# 3) شغّلي: python ragas_eval.py
|
| 7 |
-
#
|
| 8 |
-
# ملاحظات:
|
| 9 |
-
# - لو ما عندك Ground Truth اتركي العمود ground_truth فاضيًا وسيتم تجاهل context_recall تلقائيًا.
|
| 10 |
-
# - Ragas يعتمد على LLM للتقييم؛ يمكنك ضبطه على نفس مزودك (Groq) أو أي LLM آخر.
|
| 11 |
-
|
| 12 |
-
import os
|
| 13 |
-
import pandas as pd
|
| 14 |
-
from datasets import Dataset
|
| 15 |
-
|
| 16 |
-
from rag import ask # <-- تأكدي أن اسم ملفك rag.py وبه ask()
|
| 17 |
-
|
| 18 |
-
from ragas import evaluate
|
| 19 |
-
from ragas.metrics import (
|
| 20 |
-
faithfulness,
|
| 21 |
-
answer_relevancy,
|
| 22 |
-
context_precision,
|
| 23 |
-
context_recall,
|
| 24 |
-
)
|
| 25 |
-
|
| 26 |
-
# (اختياري) استخدمي نفس LLM كمُقيّم
|
| 27 |
-
try:
|
| 28 |
-
from langchain_groq import ChatGroq
|
| 29 |
-
EVAL_LLM = ChatGroq(
|
| 30 |
-
groq_api_key=os.getenv("GROQ_API_KEY"),
|
| 31 |
-
model_name="llama-3.1-8b-instant",
|
| 32 |
-
temperature=0
|
| 33 |
-
)
|
| 34 |
-
except Exception:
|
| 35 |
-
EVAL_LLM = None
|
| 36 |
-
|
| 37 |
-
DATASET_PATH = "ragas_dataset_100.csv"
|
| 38 |
-
OUT_PATH = "ragas_results.csv"
|
| 39 |
-
|
| 40 |
-
def build_eval_rows(df: pd.DataFrame):
|
| 41 |
-
rows = []
|
| 42 |
-
for _, r in df.iterrows():
|
| 43 |
-
q = str(r["question"]).strip()
|
| 44 |
-
gt = str(r.get("ground_truth", "")).strip()
|
| 45 |
-
|
| 46 |
-
ans, sources = ask(q)
|
| 47 |
-
|
| 48 |
-
# contexts: النصوص المسترجعة (chunks)
|
| 49 |
-
contexts = []
|
| 50 |
-
for s in (sources or []):
|
| 51 |
-
c = (s.get("content") or "").strip()
|
| 52 |
-
if c:
|
| 53 |
-
contexts.append(c)
|
| 54 |
-
|
| 55 |
-
row = {
|
| 56 |
-
"question": q,
|
| 57 |
-
"answer": ans,
|
| 58 |
-
"contexts": contexts,
|
| 59 |
-
}
|
| 60 |
-
if gt:
|
| 61 |
-
row["ground_truths"] = [gt] # ragas expects list[str]
|
| 62 |
-
rows.append(row)
|
| 63 |
-
return rows
|
| 64 |
-
|
| 65 |
-
def main():
|
| 66 |
-
df = pd.read_csv(DATASET_PATH)
|
| 67 |
-
|
| 68 |
-
rows = build_eval_rows(df)
|
| 69 |
-
dataset = Dataset.from_list(rows)
|
| 70 |
-
|
| 71 |
-
# لو في أي Ground Truth فعلاً، نفعل context_recall، وإلا نشيله
|
| 72 |
-
has_gt = any(("ground_truths" in x) for x in rows)
|
| 73 |
-
metrics = [faithfulness, answer_relevancy, context_precision]
|
| 74 |
-
if has_gt:
|
| 75 |
-
metrics.append(context_recall)
|
| 76 |
-
|
| 77 |
-
kwargs = {}
|
| 78 |
-
if EVAL_LLM is not None:
|
| 79 |
-
kwargs["llm"] = EVAL_LLM
|
| 80 |
-
|
| 81 |
-
result = evaluate(dataset, metrics=metrics, **kwargs)
|
| 82 |
-
|
| 83 |
-
# تلخيص
|
| 84 |
-
print("\n=== RAGAS SUMMARY ===")
|
| 85 |
-
try:
|
| 86 |
-
print(result)
|
| 87 |
-
except Exception:
|
| 88 |
-
pass
|
| 89 |
-
|
| 90 |
-
# تفاصيل لكل سؤال
|
| 91 |
-
out_df = result.to_pandas()
|
| 92 |
-
out_df.to_csv(OUT_PATH, index=False, encoding="utf-8-sig")
|
| 93 |
-
print(f"\nSaved per-question results to: {OUT_PATH}")
|
| 94 |
-
|
| 95 |
-
# متوسطات
|
| 96 |
-
print("\n=== MEANS ===")
|
| 97 |
-
for col in out_df.columns:
|
| 98 |
-
if col.startswith("faithfulness") or col.startswith("answer_relevancy") or col.startswith("context_precision") or col.startswith("context_recall"):
|
| 99 |
-
try:
|
| 100 |
-
mean = out_df[col].mean()
|
| 101 |
-
print(f"{col}: {mean:.4f}")
|
| 102 |
-
except Exception:
|
| 103 |
-
pass
|
| 104 |
-
|
| 105 |
-
if __name__ == "__main__":
|
| 106 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|