Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,24 @@
" )\n",
" return response.choices[0].message.content.strip()\n",
" except Exception as e:\n",
" return f\"⚠️ Failed to explain result: {e}\""
" return f\"⚠️ Failed to explain result: {e}\"\n",
"\n",
"# 🧠 Use LLM to explain manual guidance in plain language\n",
"def explain_manual_guidance(question, anomaly_text, emergency_text):\n",
" try:\n",
" context = f\"Anomaly Detection:\\n{anomaly_text}\\n\\nEmergency Protocol:\\n{emergency_text}\"\n",
" response = openai_client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo\",\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant that explains machine manuals in plain language.\"},\n",
" {\"role\": \"user\", \"content\": f\"The user asked: {question}\\n\\nHere is the relevant manual content:\\n\\n{context}\\n\\nPlease provide a clear answer based on the manual.\"}\n",
" ],\n",
" temperature=0.3,\n",
" max_tokens=300\n",
" )\n",
" return response.choices[0].message.content.strip()\n",
" except Exception as e:\n",
" return f\"⚠️ Failed to generate manual-based explanation: {e}\""
]
},
{
Expand Down Expand Up @@ -1146,39 +1163,40 @@
{
"cell_type": "code",
"execution_count": null,
"id": "a9d328af",
"id": "782359be",
"metadata": {},
"outputs": [],
"source": [
"def get_combined_answer(question):\n",
" # Step 1: Fetch table schemas dynamically\n",
" schema_motor = fetch_table_schema(\"motor_readings\")\n",
" schema_manual = fetch_table_schema(\"machine_manuals\")\n",
"\n",
" # Step 2: Analyze question to extract context\n",
" machine_id = extract_machine_id_from_query(question)\n",
" needs_manual = detect_manual_intent(question)\n",
"\n",
" # Step 3: Optionally load manual and extract anomaly/emergency info\n",
" manual_context = \"\"\n",
" anomaly_text, emergency_text = None, None\n",
" sql2 = None # placeholder for optional manual SQL\n",
"\n",
" if needs_manual and machine_id is not None:\n",
" manual = fetch_machine_manual(machine_id)\n",
" if manual:\n",
" anomaly_text, emergency_text = extract_anomaly_info(manual)\n",
" manual_context = f'''\n",
" try:\n",
" # Step 1: Fetch table schemas dynamically\n",
" schema_motor = fetch_table_schema(\"motor_readings\")\n",
" schema_manual = fetch_table_schema(\"machine_manuals\")\n",
"\n",
" # Step 2: Analyze question to extract context\n",
" machine_id = extract_machine_id_from_query(question)\n",
" needs_manual = detect_manual_intent(question)\n",
"\n",
" # Step 3: Optionally load manual and extract anomaly/emergency info\n",
" manual_context = \"\"\n",
" anomaly_text, emergency_text = None, None\n",
" sql2 = None # placeholder for optional manual SQL\n",
"\n",
" if needs_manual and machine_id is not None:\n",
" manual = fetch_machine_manual(machine_id)\n",
" if manual:\n",
" anomaly_text, emergency_text = extract_anomaly_info(manual)\n",
" manual_context = f'''\n",
"Manual Guidance for Machine {machine_id}:\n",
"--- Anomaly Detection ---\n",
"{anomaly_text}\n",
"--- Emergency Protocol ---\n",
"{emergency_text}\n",
"'''\n",
" sql2 = f\"SELECT manual FROM machine_manuals WHERE machine_id = {machine_id};\"\n",
" sql2 = f\"SELECT manual FROM machine_manuals WHERE machine_id = {machine_id};\"\n",
"\n",
" # Step 4: Build LLM prompt\n",
" prompt = f'''\n",
" # Step 4: Build LLM prompt\n",
" prompt = f'''\n",
"You are a CrateDB data assistant. Your job is to answer questions using telemetry data and machine manuals.\n",
"\n",
"Tables:\n",
Expand All @@ -1188,10 +1206,11 @@
"{manual_context}\n",
"\n",
"Instructions:\n",
"- For telemetry questions (e.g., performance metrics), generate SQL and explain the result.\n",
"- For telemetry questions (e.g., performance metrics), generate SQL that directly supports answering the user's question.\n",
" - If the question implies a condition (e.g., \"Is the machine overheating?\"), retrieve the most recent values and select only the relevant columns (e.g., temperature, timestamp).\n",
" - Prefer checking the condition using WHERE clauses when thresholds are known.\n",
"- For manual-related queries, summarize anomaly and emergency procedures using the context above.\n",
"- If the question involves conditions (e.g., overheating, excessive vibration), \n",
" retrieve **both telemetry and manual**. Compare telemetry values against safe thresholds.\n",
"- If the question involves conditions (e.g., overheating, excessive vibration), retrieve **both telemetry and manual**. Compare telemetry values against safe thresholds.\n",
"- Use the following format:\n",
"\n",
"---\n",
Expand All @@ -1206,78 +1225,75 @@
"<your explanation>\n",
"'''\n",
"\n",
" # Step 5: Query the LLM with the prompt and question\n",
" response = openai_client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo\",\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant working with CrateDB telemetry and manuals.\"},\n",
" {\"role\": \"user\", \"content\": prompt + f\"\\n\\nUser question: {question}\"}\n",
" ],\n",
" temperature=0,\n",
" max_tokens=700\n",
" )\n",
" # Step 5: Query the LLM with the prompt and question\n",
" response = openai_client.chat.completions.create(\n",
" model=\"gpt-3.5-turbo\",\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful assistant working with CrateDB telemetry and manuals.\"},\n",
" {\"role\": \"user\", \"content\": prompt + f\"\\n\\nUser question: {question}\"}\n",
" ],\n",
" temperature=0,\n",
" max_tokens=700\n",
" )\n",
"\n",
" output = response.choices[0].message.content.strip()\n",
" output = response.choices[0].message.content.strip()\n",
"\n",
" # Step 6: Parse and clean LLM output\n",
" parsed_sql1, parsed_sql2, answer = extract_sql_blocks(output)\n",
" sql1 = patch_cratedb_sql(parsed_sql1) if parsed_sql1 else None\n",
" sql2 = patch_cratedb_sql(parsed_sql2) if parsed_sql2 else sql2\n",
" # Step 6: Parse and clean LLM output\n",
" parsed_sql1, parsed_sql2, answer = extract_sql_blocks(output)\n",
" sql1 = patch_cratedb_sql(parsed_sql1) if parsed_sql1 else None\n",
" sql2 = patch_cratedb_sql(parsed_sql2) if parsed_sql2 else sql2\n",
"\n",
" # Step 7: Execute queries\n",
" df1, df2 = None, None\n",
" # Step 7: Execute queries\n",
" df1, df2 = None, None\n",
"\n",
" try:\n",
" if sql1:\n",
" df1 = pd.read_sql(sql1, con=engine)\n",
" except Exception as e:\n",
" print(\"❌ Failed to execute SQL 1:\", sql1)\n",
" print(e)\n",
" try:\n",
" df1 = pd.read_sql(sql1, con=engine)\n",
" except Exception as e:\n",
" print(\"❌ Failed to execute SQL 1:\", sql1)\n",
" print(e)\n",
"\n",
" try:\n",
" if sql2:\n",
" df2 = pd.read_sql(sql2, con=engine)\n",
" except Exception as e:\n",
" print(\"❌ Failed to execute SQL 2:\", sql2)\n",
" print(e)\n",
" try:\n",
" df2 = pd.read_sql(sql2, con=engine)\n",
" except Exception as e:\n",
" print(\"❌ Failed to execute SQL 2:\", sql2)\n",
" print(e)\n",
"\n",
" # ✅ Step 8: Construct final answer\n",
" if needs_manual and anomaly_text and emergency_text:\n",
" final_answer = explain_manual_guidance(question, anomaly_text, emergency_text)\n",
" elif df1 is not None and not df1.empty:\n",
" final_answer = explain_result_with_llm(question, df1)\n",
" else:\n",
" final_answer = answer or \"No answer could be generated.\"\n",
"\n",
" # Step 8: Construct final natural language explanation\n",
" if needs_manual and anomaly_text and emergency_text:\n",
" final_answer = f'''\n",
"📌 Manual Guidance for Machine {machine_id}\n",
" print(\"✅ Finished get_combined_answer\") # ✅ DEBUG\n",
" return sql1, sql2, df1, df2, final_answer\n",
"\n",
"--- Anomaly Detection ---\n",
"{anomaly_text}\n",
"\n",
"--- Emergency Protocol ---\n",
"{emergency_text}\n",
"'''.strip()\n",
" elif df1 is not None and not df1.empty:\n",
" final_answer = explain_result_with_llm(question, df1)\n",
" else:\n",
" final_answer = answer or \"No answer could be generated.\"\n",
"\n",
" return sql1, sql2, df1, df2, final_answer"
" except Exception as e:\n",
" print(\"❌ ERROR inside get_combined_answer:\", e)\n",
" return None"
]
},
{
"cell_type": "markdown",
"id": "cc65dc9e",
"id": "d26052c9",
"metadata": {},
"source": [
"### Ask a Question (Natural Language Interface)\n",
"\n",
"This is your main interaction point with the assistant. Just type in a natural language question, and the assistant will:\n",
"\t\tAnalyze your query\n",
"\t\tDecide if telemetry data or manual context is needed\n",
"\t\tGenerate and run SQL queries\n",
"\t\tExplain the results in plain language\n",
"\t\tOptionally summarize emergency protocols from manuals\n",
"- Analyze your query\n",
"- Decide if telemetry data or manual context is needed\n",
"- Generate and run SQL queries\n",
"- Explain the results in plain language\n",
"- Optionally summarize emergency protocols from manuals\n",
"\n",
"\n",
"Here are some example queries and what kind of answers you can expect:\n",
"\n",
"| 💬 Question | 🧠 Assistant Behavior |\n",
"| Question | Assistant Behavior |\n",
"|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------|\n",
"| `What was the average temperature for machine 3 last week?` | Retrieves average temperature from telemetry and explains the result. |\n",
"| `Is machine 4 overheating?` | Checks the latest temperature and compares it with manual thresholds if present.|\n",
Expand All @@ -1289,10 +1305,12 @@
{
"cell_type": "code",
"execution_count": null,
"id": "c03bb3ca",
"id": "eb35d155",
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import display, HTML\n",
"\n",
"# Ask a combined question\n",
"# Example questions:\n",
"# \n",
Expand All @@ -1303,7 +1321,7 @@
"# Show me the maintenance steps for machine 1.\n",
"\n",
"# question = \"Type your question here and push ▶️\"\n",
"question = \"Give me the max and min vibration for machine 6 when RPM > 1600\"\n",
"question = \"Is machine 4 overheating?\"\n",
"\n",
"sql1, sql2, df1, df2, final_answer = get_combined_answer(question)\n",
"\n",
Expand All @@ -1330,8 +1348,25 @@
"output.append(\"\\n🧾 Final Answer:\")\n",
"output.append(final_answer.strip() if final_answer else \"No answer returned.\")\n",
"\n",
"# Join and print once\n",
"print(\"\\n\".join(output))"
"# Join all parts of the output\n",
"joined_output = \"\\n\".join(output)\n",
"\n",
"# Display in dark theme box\n",
"display(HTML(f\"\"\"\n",
"<div style=\"\n",
" background-color: #1e1e1e;\n",
" color: #f0f0f0;\n",
" border: 1px solid #444;\n",
" padding: 16px;\n",
" border-radius: 6px;\n",
" white-space: pre-wrap;\n",
" font-family: 'Courier New', monospace;\n",
" font-size: 14px;\n",
" line-height: 1.6;\n",
"\">\n",
"{joined_output}\n",
"</div>\n",
"\"\"\"))"
]
},
{
Expand Down