Przeglądaj źródła

TG-220 TG-221 TG-222 #closed - Upgrade Ollama to Qwen2.5-7B, refactor backend prompts for XML scratchpad reasoning, and implement response parsing

Lange François 1 miesiąc temu
rodzic
commit
09c5304cb5

BIN
Project.pdf


BIN
Retro Planning.pdf


+ 64 - 14
app.py

@@ -30,8 +30,47 @@ import time
 
 import threading
 
+def strip_scratchpad(text: str) -> str:
+    import re
+    # Strip out the XML <scratchpad> tag and everything in between, non-greedily
+    clean_text = re.sub(r'<scratchpad>.*?</scratchpad>', '', text, flags=re.DOTALL)
+    return clean_text.strip()
+
+def filter_scratchpad_stream(stream):
+    buffer = ""
+    in_scratchpad = False
+    for chunk in stream:
+        content = chunk['message']['content']
+        buffer += content
+        
+        while True:
+            if not in_scratchpad:
+                start_idx = buffer.find("<scratchpad>")
+                if start_idx != -1:
+                    yield buffer[:start_idx]
+                    buffer = buffer[start_idx:]
+                    in_scratchpad = True
+                else:
+                    yield_len = max(0, len(buffer) - 11)
+                    if yield_len > 0:
+                        yield buffer[:yield_len]
+                        buffer = buffer[yield_len:]
+                    break
+            else:
+                end_idx = buffer.find("</scratchpad>")
+                if end_idx != -1:
+                    buffer = buffer[end_idx + 13:]
+                    in_scratchpad = False
+                else:
+                    keep_len = 12
+                    if len(buffer) > keep_len:
+                        buffer = buffer[-keep_len:]
+                    break
+    if not in_scratchpad and buffer:
+        yield buffer
+
 def pull_model_bg():
-    try: ollama.pull('llama3.2:3b')
+    try: ollama.pull('qwen2.5:7b')
     except: pass
 threading.Thread(target=pull_model_bg, daemon=True).start()
 
@@ -415,7 +454,7 @@ with tab_chat:
         
         try:
             temp_messages = [{"role": "system", "content": sys_prompt}] + [m for m in st.session_state.messages if m["role"] != "tool"]
-            response_stream = ollama.chat(model='llama3.2:3b', messages=temp_messages, stream=True)
+            response_stream = ollama.chat(model='qwen2.5:7b', messages=temp_messages, stream=True)
             
             with st.chat_message("assistant"):
                 ai_reply = st.write_stream(chunk['message']['content'] for chunk in response_stream)
@@ -622,7 +661,7 @@ with tab_explore:
                                 minimal_records = df_display[['product_name', 'Medical Warning']].head(10).to_dict('records')
                                 eval_prompt = f"The user has this profile: {profile_text}. Evaluate these top foods and state which are highly recommended or strictly forbidden: {minimal_records}. Provide a direct, readable clinical summary. Do not output raw JSON."
                                 try:
-                                    response_stream = ollama.chat(model='llama3.2:3b', messages=[{'role': 'user', 'content': eval_prompt}], stream=True)
+                                    response_stream = ollama.chat(model='qwen2.5:7b', messages=[{'role': 'user', 'content': eval_prompt}], stream=True)
                                     st.write_stream(chunk['message']['content'] for chunk in response_stream)
                                 except Exception as e:
                                     error_msg = str(e).lower()
@@ -818,22 +857,33 @@ with tab_planner:
             Dietary constraint: {diet_pref}. Additional notes: {extra_notes}.
             Health profile: {profile_text}. 
             
-            CRITICAL INSTRUCTIONS:
-            - You MUST formulate the menu using ONLY the following real database items retrieved for you: {db_context}
-            - Output the menu beautifully formatted as a Markdown Table.
-            - The Markdown table MUST strictly contain 5 columns separated by pipes (|).
-            - Columns MUST be exactly: | Meal Time | Exact Food | Portion Size | Calories | Protein |
-            - If you merge columns, the system will fail. Separate Calories and Protein with a pipe.
-            - Do NOT output JSON. Do NOT use tool calls.
-            - Provide a direct answer. Skip all thinking, reasoning, and pleasantries.
+            COGNITIVE SCRATCHPAD INSTRUCTIONS:
+            - You MUST perform all your intermediate thinking, unit conversions (e.g. converting cups, tablespoons, or ounces to exact metric grams based on food density), and calorie/protein mathematical additions inside a `<scratchpad>` tag.
+            - Format:
+              <scratchpad>
+              Calculations:
+              - 1.5 cups of Cheese = X grams (density Y). Calories = A, Protein = B.
+              - 2 tbsp of Peanut Butter = Z grams (density C). Calories = D, Protein = E.
+              - Summation: Total Calories = A + D = Z kcal (vs target {target_cal}kcal). Total Protein = B + E = Fg.
+              </scratchpad>
+              | Meal Time | Exact Food | Portion Size | Calories | Protein |
+              | --- | --- | --- | --- | --- |
+              ...
+            
+            CRITICAL FORMATTING INSTRUCTIONS:
+            - After the </scratchpad> closing tag, you MUST strictly output the menu formatted as a Markdown Table.
+            - The table MUST contain exactly 5 columns separated by pipes (|): | Meal Time | Exact Food | Portion Size | Calories | Protein |
+            - The items in the table MUST be selected strictly from: {db_context}
+            - Do NOT output JSON. Do NOT use tool calls. Skip pleasantries.
             """
             
             temp_messages = [{'role': 'system', 'content': sys_prompt}, {'role': 'user', 'content': 'Generate my meal plan as a markdown table.'}]
             
             # Stream the response instantly!
             try:
-                response_stream = ollama.chat(model='llama3.2:3b', messages=temp_messages, stream=True)
-                ai_reply = st.write_stream(chunk['message']['content'] for chunk in response_stream)
+                response_stream = ollama.chat(model='qwen2.5:7b', messages=temp_messages, stream=True)
+                clean_stream = filter_scratchpad_stream(response_stream)
+                ai_reply = st.write_stream(clean_stream)
                 
                 # PDF Generation
                 def generate_pdf(text):
@@ -916,7 +966,7 @@ with tab_planner:
                 
                 st.download_button(
                     label="📄 Download PDF Export",
-                    data=generate_pdf(ai_reply),
+                    data=generate_pdf(strip_scratchpad(ai_reply)),
                     file_name="Clinical_Meal_Plan.pdf",
                     mime="application/pdf",
                     type="primary"

BIN
docs/Backup_Procedure.pdf


BIN
docs/Data_Ingestion.pdf


BIN
docs/Final_Report.pdf


BIN
docs/Installation_Guide.pdf


+ 2 - 2
docs/Operator_Installation_Guide.md

@@ -27,7 +27,7 @@ To maximize CPU/GPU efficiency and secure database read/writes, services are dis
 | :--- | :--- | :--- |
 | **streamlit-app (app.py)** | Local WSL2 (Windows) | Low-latency rendering and direct client access |
 | **mysql (Database Node)** | Hyper-V VM (Server A) | Persistent enterprise-grade disk storage |
-| **ollama (NLP Llama3.2:3b Engine)** | VirtualBox VM (Server B) | Dedicated CPU/GPU virtualization allocation |
+| **ollama (NLP Qwen2.5:7b Engine)** | VirtualBox VM (Server B) | Dedicated CPU/GPU virtualization allocation |
 | **zabbix-server & web (Monitoring)** | Hyper-V VM (Server A) | Centralized SNMPv3 alert processing and logs |
 | **searxng (Meta-Search Gateway)** | Local WSL2 (Windows) | Dynamic browser-level loopbacks |
 
@@ -179,6 +179,6 @@ Run these test cases to verify the installation:
 | :--- | :--- | :--- | :---: |
 | **TC-OP-01** | Search 'Cheese' on Search Tab | 10+ records returned in <0.04s. Listeria warning flags on unpasteurized. | `[ ]` |
 | **TC-OP-02** | Enter '1.5 cups' in Plate Tab | Parsed and converted to metric grams based on density index. | `[ ]` |
-| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | Llama3.2:3b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
+| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | Qwen2.5:7b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
 | **TC-OP-04** | Trigger manual db backup | Timestamped compressed .sql.gz created inside backups/ folder. | `[ ]` |
 | **TC-OP-05** | Terminate Ollama Container | Zabbix PROBLEM active alert generated on dashboard in < 30 seconds. | `[ ]` |

BIN
docs/Operator_Installation_Guide.pdf


BIN
docs/Scrum_Artifacts.pdf


BIN
docs/Scrum_Daily.pdf


BIN
docs/Scrum_Plan.pdf


BIN
docs/Scrum_Retro.pdf


BIN
docs/Scrum_Review.pdf


BIN
docs/Scrum_Wiki.pdf


BIN
docs/Start_Stop_Procedures.pdf


BIN
docs/Test_Cases_Sprint8.pdf


+ 1 - 1
docs/User_Description.md

@@ -18,7 +18,7 @@ Allows practitioners to search the 24GB OpenFoodFacts dataset in real time (aver
 - **Flexible Column Customization**: Multi-select column headers to inspect specific macro and micro-nutrients.
 
 ### 💬 tab 2: AI Clinical Chat (💬 AI Chat)
-An interactive NLP dialogue interface powered by a local lightweight LLM (**Llama3.2:3b**).
+An interactive NLP dialogue interface powered by a local lightweight LLM (**Qwen2.5:7b**).
 - **RAG-Driven Precision**: The AI dietitian automatically retrieves and reviews local database records and private meta-search results before formulating an answer.
 - **Dynamic Medical Guardrails**: The user's active illnesses, diets, and conditions are injected into the AI's system prompt in the background, forcing the AI to strictly enforce clinical safety constraints.
 

BIN
docs/User_Description.pdf


+ 1 - 1
docs/User_Guide.md

@@ -8,4 +8,4 @@ Search for products using keywords. The system utilizes FULLTEXT matching to ins
 Add portion sizes of different foods to calculate cumulative nutritional intake. Use the 🗑️ icon to remove items.
 
 ## 3. Chat with AI
-Ask the `llama3.2:3b` model complex dietary questions. It natively utilizes RAG Tool Calling to silently search the database and formulate clinical answers.
+Ask the `qwen2.5:7b` model complex dietary questions. It natively utilizes RAG Tool Calling to silently search the database and formulate clinical answers.

BIN
docs/User_Guide.pdf


BIN
docs/WSL_Deployment.pdf


BIN
docs/Wiki_Home.pdf


BIN
docs/architecture.pdf


BIN
docs/disaster_recovery_plan.pdf


BIN
docs/distributed_deployment.pdf


BIN
docs/project_report.pdf


BIN
docs/retro_planning.pdf


BIN
docs/taiga_audit_report.pdf


BIN
docs/zabbix_monitoring.pdf


+ 4 - 4
generate_docs.py

@@ -148,7 +148,7 @@ Search for products using keywords. The system utilizes FULLTEXT matching to ins
 Add portion sizes of different foods to calculate cumulative nutritional intake. Use the 🗑️ icon to remove items.
 
 ## 3. Chat with AI
-Ask the `llama3.2:3b` model complex dietary questions. It natively utilizes RAG Tool Calling to silently search the database and formulate clinical answers.
+Ask the `qwen2.5:7b` model complex dietary questions. It natively utilizes RAG Tool Calling to silently search the database and formulate clinical answers.
 """,
     "Wiki_Home.md": """# $Id$
 # Documentation Home
@@ -241,7 +241,7 @@ Allows practitioners to search the 24GB OpenFoodFacts dataset in real time (aver
 - **Flexible Column Customization**: Multi-select column headers to inspect specific macro and micro-nutrients.
 
 ### 💬 tab 2: AI Clinical Chat (💬 AI Chat)
-An interactive NLP dialogue interface powered by a local lightweight LLM (**Llama3.2:3b**).
+An interactive NLP dialogue interface powered by a local lightweight LLM (**Qwen2.5:7b**).
 - **RAG-Driven Precision**: The AI dietitian automatically retrieves and reviews local database records and private meta-search results before formulating an answer.
 - **Dynamic Medical Guardrails**: The user's active illnesses, diets, and conditions are injected into the AI's system prompt in the background, forcing the AI to strictly enforce clinical safety constraints.
 
@@ -383,7 +383,7 @@ To maximize CPU/GPU efficiency and secure database read/writes, services are dis
 | :--- | :--- | :--- |
 | **streamlit-app (app.py)** | Local WSL2 (Windows) | Low-latency rendering and direct client access |
 | **mysql (Database Node)** | Hyper-V VM (Server A) | Persistent enterprise-grade disk storage |
-| **ollama (NLP Llama3.2:3b Engine)** | VirtualBox VM (Server B) | Dedicated CPU/GPU virtualization allocation |
+| **ollama (NLP Qwen2.5:7b Engine)** | VirtualBox VM (Server B) | Dedicated CPU/GPU virtualization allocation |
 | **zabbix-server & web (Monitoring)** | Hyper-V VM (Server A) | Centralized SNMPv3 alert processing and logs |
 | **searxng (Meta-Search Gateway)** | Local WSL2 (Windows) | Dynamic browser-level loopbacks |
 
@@ -535,7 +535,7 @@ Run these test cases to verify the installation:
 | :--- | :--- | :--- | :---: |
 | **TC-OP-01** | Search 'Cheese' on Search Tab | 10+ records returned in <0.04s. Listeria warning flags on unpasteurized. | `[ ]` |
 | **TC-OP-02** | Enter '1.5 cups' in Plate Tab | Parsed and converted to metric grams based on density index. | `[ ]` |
-| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | Llama3.2:3b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
+| **TC-OP-03** | Ask Chat: 'Can I eat sushi?' | Qwen2.5:7b retrieves database context and flags raw fish as forbidden for pregnancy. | `[ ]` |
 | **TC-OP-04** | Trigger manual db backup | Timestamped compressed .sql.gz created inside backups/ folder. | `[ ]` |
 | **TC-OP-05** | Terminate Ollama Container | Zabbix PROBLEM active alert generated on dashboard in < 30 seconds. | `[ ]` |
 """

+ 1 - 1
scripts/generate_project_report.py

@@ -187,7 +187,7 @@ def main():
 The **Local Food AI** capstone project has successfully completed all sprint iterations. The system stands fully verified, containerized, and documented. 
 
 ### What Has Been Done
-1. **Model Upgraded to Ollama Latest**: Transitioned from the lightweight `llama3.2:1b` model to the much more robust and recent **`llama3.2:3b`** model (2.0 GB). Programmatically downloaded and installed it natively inside the `food_project-ollama-1` container, and fully updated all application endpoints in `app.py`.
+1. **Model Upgraded to Ollama Latest**: Transitioned from the `llama3.2:3b` model to the much more robust, large reasoning-focused **`qwen2.5:7b`** model (4.7 GB) with structured XML Chain-of-Thought (CoT) calculations. Programmatically downloaded and installed it natively inside the `food_project-ollama-1` container, and fully updated all application endpoints in `app.py`.
 2. **Taiga Deliverables Synchronized**: Checked the live Taiga API on server `192.168.130.161`. All 30 User Stories, all technical tasks, and all issues in Project ID 21 (Sprint 7 Milestone) are **100% completed and officially closed**!
 3. **Database Architecture & Partitioning**: Loaded and vertically partitioned the 3GB OpenFoodFacts macro data into MySQL. Configured matching FULLTEXT engines to search records in less than **0.04s** (averaging 90% latency reduction).
 4. **DevSecOps Observability**: Completed SNMPv2c telemetry configuration, custom application traps, and configured automated trigger alerts directly inside Zabbix on `192.168.130.170`.