{
"event": "PreToolUse",
"tool_name": "mcp__mcp-tasks__tasks_update",
"tool_input": {
"id": 294,
"description": "## Ziel\nBei der Content-Generierung im Content Studio sollen Semantik-Daten (Entitäten, Relationen) und Taxonomie-Daten in den LLM-Prompt injiziert werden.\n\n## Tiefenanalyse (abgeschlossen)\n\n### Aktuelle Architektur\n```\nBriefing → get_rag_context() → Qdrant Search → Chunks\n ↓\n build_generation_prompt()\n ↓\n call_llm()\n```\n\n### Datenstruktur (ki_content)\n| Tabelle | Zeilen | Beschreibung |\n|---------|--------|--------------|\n| entities | 251 | Konzepte, Personen, Orte |\n| entity_relations | 254 | PART_OF, USED_IN, RELATED_TO |\n| chunk_entities | 11649 | **Verknüpfung Chunks ↔ Entities** |\n| taxonomy_terms | 11 | Prozess, Haltung, Entwicklung, etc. |\n| document_taxonomy | 35 | Dokumente ↔ Taxonomie |\n\n### Schlüsselerkenntnis\nQdrant-Payload enthält bereits `chunk_id` (embed.py:147), aber `get_rag_context()` (generate.py:78-95) ignoriert es. Die `chunk_entities`-Tabelle verknüpft Chunks mit Entitäten inkl. `relevance_score`.\n\n## Implementierungsplan\n\n### Phase 1: chunk_id durchreichen\n**Datei:** `\/var\/www\/scripts\/pipeline\/generate.py`\n\n```python\n# get_rag_context() erweitern (Zeile 87-92)\ncontext_items.append({\n \"content\": result[\"payload\"].get(\"content\", \"\"),\n \"source\": result[\"payload\"].get(\"document_title\", \"Unknown\"),\n \"score\": round(result[\"score\"], 4),\n \"chunk_id\": result[\"payload\"].get(\"chunk_id\"), # NEU\n \"document_id\": result[\"payload\"].get(\"document_id\"), # NEU\n})\n```\n\n### Phase 2: Neue Funktionen\n\n```python\ndef get_semantic_context(chunk_ids):\n \"\"\"Lädt Entitäten und Relationen basierend auf chunk_ids.\"\"\"\n # 1. Entitäten via chunk_entities laden\n entities = db.execute(\"\"\"\n SELECT DISTINCT e.id, e.name, e.type, e.description,\n AVG(ce.relevance_score) as relevance\n FROM chunk_entities ce\n JOIN entities e ON ce.entity_id = e.id\n WHERE ce.chunk_id IN (%s)\n GROUP BY e.id\n ORDER BY relevance DESC\n LIMIT 10\n \"\"\", chunk_ids)\n \n # 2. Relationen zwischen gefundenen Entitäten\n relations = db.execute(\"\"\"\n SELECT e1.name as source, er.relation_type, e2.name as target\n FROM entity_relations er\n JOIN entities e1 ON er.source_entity_id = e1.id\n JOIN entities e2 ON er.target_entity_id = e2.id\n WHERE e1.id IN (%s) AND e2.id IN (%s)\n \"\"\", entity_ids, entity_ids)\n \n return {\"entities\": entities, \"relations\": relations}\n\ndef get_taxonomy_context(document_ids):\n \"\"\"Lädt Taxonomie-Terme für Dokumente.\"\"\"\n return db.execute(\"\"\"\n SELECT tt.name, tt.slug, dt.confidence\n FROM document_taxonomy dt\n JOIN taxonomy_terms tt ON dt.taxonomy_term_id = tt.id\n WHERE dt.document_id IN (%s)\n ORDER BY dt.confidence DESC\n \"\"\", document_ids)\n```\n\n### Phase 3: Prompt erweitern\n\n```python\ndef build_generation_prompt(briefing, context, profile, contract, structure, semantic=None, taxonomy=None):\n # ... existierender Code ...\n \n # NEU: Semantischer Kontext\n semantic_text = \"\"\n if semantic:\n if semantic.get(\"entities\"):\n semantic_text += \"\\n## Relevante Konzepte\\n\"\n for e in semantic[\"entities\"][:10]:\n semantic_text += f\"- **{e['name']}** ({e['type']}): {e['description']}\\n\"\n \n if semantic.get(\"relations\"):\n semantic_text += \"\\n## Konzept-Beziehungen\\n\"\n for r in semantic[\"relations\"][:10]:\n semantic_text += f\"- {r['source']} → {r['relation_type']} → {r['target']}\\n\"\n \n # NEU: Taxonomie-Kontext\n taxonomy_text = \"\"\n if taxonomy:\n taxonomy_text = \"\\n## Thematische Einordnung\\n\"\n taxonomy_text += \", \".join([t[\"name\"] for t in taxonomy])\n```\n\n### Phase 4: generate_content() anpassen\n\n```python\ndef generate_content(order_id, model=\"anthropic\", collection=\"documents\", context_limit=5):\n # ... existierend ...\n context = get_rag_context(order[\"briefing\"], collection, context_limit)\n \n # NEU: Semantik basierend auf chunk_ids\n chunk_ids = [c[\"chunk_id\"] for c in context if c.get(\"chunk_id\")]\n doc_ids = list(set([c[\"document_id\"] for c in context if c.get(\"document_id\")]))\n \n semantic = get_semantic_context(chunk_ids) if chunk_ids else None\n taxonomy = get_taxonomy_context(doc_ids) if doc_ids else None\n \n prompt = build_generation_prompt(\n order[\"briefing\"], context, profile, contract, structure,\n semantic=semantic, taxonomy=taxonomy # NEU\n )\n```\n\n## Betroffene Dateien\n\n| Datei | Änderung |\n|-------|----------|\n| `\/var\/www\/scripts\/pipeline\/generate.py` | Hauptlogik (4 Funktionen) |\n| `\/var\/www\/scripts\/pipeline\/db.py` | ggf. Prepared Statement Helpers |\n\n## Keine UI-Änderung nötig\nDie Semantik wird automatisch basierend auf den RAG-Chunks geladen - keine zusätzlichen UI-Optionen erforderlich.\n\n## Testplan\n1. Content-Auftrag mit Briefing \"Auftragsklärung im Coaching\"\n2. Prüfen ob Entitäten (Coaching, Auftragsklärung, Kontrakt) im Prompt erscheinen\n3. Prüfen ob Relationen (PART_OF, USED_IN) korrekt eingefügt werden\n4. Qualität des generierten Contents vergleichen (mit\/ohne Semantik)"
}
}
{
"tool_response": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"task\": {\n \"id\": 294,\n \"uuid\": \"a13e39da-e9fd-4122-9d40-51c2abd3dbcd\",\n \"title\": \"Semantik und Taxonomie in Content-Generierung einbauen\",\n \"description\": \"## Ziel\\nBei der Content-Generierung im Content Studio sollen Semantik-Daten (Entitäten, Relationen) und Taxonomie-Daten in den LLM-Prompt injiziert werden.\\n\\n## Tiefenanalyse (abgeschlossen)\\n\\n### Aktuelle Architektur\\n```\\nBriefing → get_rag_context() → Qdrant Search → Chunks\\n ↓\\n build_generation_prompt()\\n ↓\\n call_llm()\\n```\\n\\n### Datenstruktur (ki_content)\\n| Tabelle | Zeilen | Beschreibung |\\n|---------|--------|--------------|\\n| entities | 251 | Konzepte, Personen, Orte |\\n| entity_relations | 254 | PART_OF, USED_IN, RELATED_TO |\\n| chunk_entities | 11649 | **Verknüpfung Chunks ↔ Entities** |\\n| taxonomy_terms | 11 | Prozess, Haltung, Entwicklung, etc. |\\n| document_taxonomy | 35 | Dokumente ↔ Taxonomie |\\n\\n### Schlüsselerkenntnis\\nQdrant-Payload enthält bereits `chunk_id` (embed.py:147), aber `get_rag_context()` (generate.py:78-95) ignoriert es. Die `chunk_entities`-Tabelle verknüpft Chunks mit Entitäten inkl. `relevance_score`.\\n\\n## Implementierungsplan\\n\\n### Phase 1: chunk_id durchreichen\\n**Datei:** `\/var\/www\/scripts\/pipeline\/generate.py`\\n\\n```python\\n# get_rag_context() erweitern (Zeile 87-92)\\ncontext_items.append({\\n \\\"content\\\": result[\\\"payload\\\"].get(\\\"content\\\", \\\"\\\"),\\n \\\"source\\\": result[\\\"payload\\\"].get(\\\"document_title\\\", \\\"Unknown\\\"),\\n \\\"score\\\": round(result[\\\"score\\\"], 4),\\n \\\"chunk_id\\\": result[\\\"payload\\\"].get(\\\"chunk_id\\\"), # NEU\\n \\\"document_id\\\": result[\\\"payload\\\"].get(\\\"document_id\\\"), # NEU\\n})\\n```\\n\\n### Phase 2: Neue Funktionen\\n\\n```python\\ndef get_semantic_context(chunk_ids):\\n \\\"\\\"\\\"Lädt Entitäten und Relationen basierend auf chunk_ids.\\\"\\\"\\\"\\n # 1. Entitäten via chunk_entities laden\\n entities = db.execute(\\\"\\\"\\\"\\n SELECT DISTINCT e.id, e.name, e.type, e.description,\\n AVG(ce.relevance_score) as relevance\\n FROM chunk_entities ce\\n JOIN entities e ON ce.entity_id = e.id\\n WHERE ce.chunk_id IN (%s)\\n GROUP BY e.id\\n ORDER BY relevance DESC\\n LIMIT 10\\n \\\"\\\"\\\", chunk_ids)\\n \\n # 2. Relationen zwischen gefundenen Entitäten\\n relations = db.execute(\\\"\\\"\\\"\\n SELECT e1.name as source, er.relation_type, e2.name as target\\n FROM entity_relations er\\n JOIN entities e1 ON er.source_entity_id = e1.id\\n JOIN entities e2 ON er.target_entity_id = e2.id\\n WHERE e1.id IN (%s) AND e2.id IN (%s)\\n \\\"\\\"\\\", entity_ids, entity_ids)\\n \\n return {\\\"entities\\\": entities, \\\"relations\\\": relations}\\n\\ndef get_taxonomy_context(document_ids):\\n \\\"\\\"\\\"Lädt Taxonomie-Terme für Dokumente.\\\"\\\"\\\"\\n return db.execute(\\\"\\\"\\\"\\n SELECT tt.name, tt.slug, dt.confidence\\n FROM document_taxonomy dt\\n JOIN taxonomy_terms tt ON dt.taxonomy_term_id = tt.id\\n WHERE dt.document_id IN (%s)\\n ORDER BY dt.confidence DESC\\n \\\"\\\"\\\", document_ids)\\n```\\n\\n### Phase 3: Prompt erweitern\\n\\n```python\\ndef build_generation_prompt(briefing, context, profile, contract, structure, semantic=None, taxonomy=None):\\n # ... existierender Code ...\\n \\n # NEU: Semantischer Kontext\\n semantic_text = \\\"\\\"\\n if semantic:\\n if semantic.get(\\\"entities\\\"):\\n semantic_text += \\\"\\\\n## Relevante Konzepte\\\\n\\\"\\n for e in semantic[\\\"entities\\\"][:10]:\\n semantic_text += f\\\"- **{e['name']}** ({e['type']}): {e['description']}\\\\n\\\"\\n \\n if semantic.get(\\\"relations\\\"):\\n semantic_text += \\\"\\\\n## Konzept-Beziehungen\\\\n\\\"\\n for r in semantic[\\\"relations\\\"][:10]:\\n semantic_text += f\\\"- {r['source']} → {r['relation_type']} → {r['target']}\\\\n\\\"\\n \\n # NEU: Taxonomie-Kontext\\n taxonomy_text = \\\"\\\"\\n if taxonomy:\\n taxonomy_text = \\\"\\\\n## Thematische Einordnung\\\\n\\\"\\n taxonomy_text += \\\", \\\".join([t[\\\"name\\\"] for t in taxonomy])\\n```\\n\\n### Phase 4: generate_content() anpassen\\n\\n```python\\ndef generate_content(order_id, model=\\\"anthropic\\\", collection=\\\"documents\\\", context_limit=5):\\n # ... existierend ...\\n context = get_rag_context(order[\\\"briefing\\\"], collection, context_limit)\\n \\n # NEU: Semantik basierend auf chunk_ids\\n chunk_ids = [c[\\\"chunk_id\\\"] for c in context if c.get(\\\"chunk_id\\\")]\\n doc_ids = list(set([c[\\\"document_id\\\"] for c in context if c.get(\\\"document_id\\\")]))\\n \\n semantic = get_semantic_context(chunk_ids) if chunk_ids else None\\n taxonomy = get_taxonomy_context(doc_ids) if doc_ids else None\\n \\n prompt = build_generation_prompt(\\n order[\\\"briefing\\\"], context, profile, contract, structure,\\n semantic=semantic, taxonomy=taxonomy # NEU\\n )\\n```\\n\\n## Betroffene Dateien\\n\\n| Datei | Änderung |\\n|-------|----------|\\n| `\/var\/www\/scripts\/pipeline\/generate.py` | Hauptlogik (4 Funktionen) |\\n| `\/var\/www\/scripts\/pipeline\/db.py` | ggf. Prepared Statement Helpers |\\n\\n## Keine UI-Änderung nötig\\nDie Semantik wird automatisch basierend auf den RAG-Chunks geladen - keine zusätzlichen UI-Optionen erforderlich.\\n\\n## Testplan\\n1. Content-Auftrag mit Briefing \\\"Auftragsklärung im Coaching\\\"\\n2. Prüfen ob Entitäten (Coaching, Auftragsklärung, Kontrakt) im Prompt erscheinen\\n3. Prüfen ob Relationen (PART_OF, USED_IN) korrekt eingefügt werden\\n4. Qualität des generierten Contents vergleichen (mit\/ohne Semantik)\",\n \"type\": \"ai_task\",\n \"status\": \"pending\",\n \"created_by\": \"mcp-tasks\",\n \"created_by_type\": \"ai\",\n \"parent_task_id\": null,\n \"due_date\": null,\n \"created_at\": \"2025-12-24T09:30:53.341728\",\n \"updated_at\": \"2025-12-24T09:37:29.900658\",\n \"completed_at\": null,\n \"metadata\": {}\n },\n \"message\": \"Task #294 updated\"\n}"
}
]
}