diff --git a/app/models/dto.py b/app/models/dto.py index 860670f..b170f37 100644 --- a/app/models/dto.py +++ b/app/models/dto.py @@ -1,10 +1,9 @@ """ FILE: app/models/dto.py DESCRIPTION: Pydantic-Modelle (DTOs) für Request/Response Bodies. Definiert das API-Schema. -VERSION: 0.6.2 +VERSION: 2.6.0 (WP-22 Semantic Graph Routing & Lifecycle) STATUS: Active DEPENDENCIES: pydantic, typing, uuid -LAST_ANALYSIS: 2025-12-15 """ from __future__ import annotations @@ -12,7 +11,7 @@ from pydantic import BaseModel, Field from typing import List, Literal, Optional, Dict, Any import uuid -EdgeKind = Literal["references", "references_at", "backlink", "next", "prev", "belongs_to", "depends_on", "related_to", "similar_to"] +EdgeKind = Literal["references", "references_at", "backlink", "next", "prev", "belongs_to", "depends_on", "related_to", "similar_to", "caused_by", "derived_from", "based_on", "solves", "blocks", "uses", "guides"] # --- Basis-DTOs --- @@ -56,6 +55,11 @@ class QueryRequest(BaseModel): filters: Optional[Dict] = None ret: Dict = {"with_paths": True, "with_notes": True, "with_chunks": True} explain: bool = False + + # WP-22: Semantic Graph Routing + # Erlaubt dem Router, Kantengewichte dynamisch zu überschreiben. + # Format: {"caused_by": 3.0, "related_to": 0.5} + boost_edges: Optional[Dict[str, float]] = None class FeedbackRequest(BaseModel): @@ -97,7 +101,8 @@ class ScoreBreakdown(BaseModel): class Reason(BaseModel): """Ein semantischer Grund für das Ranking.""" - kind: Literal["semantic", "edge", "type", "centrality"] + # WP-22: 'lifecycle' hinzugefügt für Status-Begründungen (Draft vs Stable) + kind: Literal["semantic", "edge", "type", "centrality", "lifecycle"] message: str score_impact: Optional[float] = None details: Optional[Dict[str, Any]] = None @@ -151,6 +156,4 @@ class ChatResponse(BaseModel): sources: List[QueryHit] = Field(..., description="Die für die Antwort genutzten Quellen") latency_ms: int intent: Optional[str] = Field("FACT", description="WP-06: Erkannter Intent (FACT/DECISION)") - intent_source: Optional[str] = Field("Unknown", description="WP-06: Quelle der Intent-Erkennung (Keyword vs. LLM)") - - \ No newline at end of file + intent_source: Optional[str] = Field("Unknown", description="WP-06: Quelle der Intent-Erkennung (Keyword vs. LLM)") \ No newline at end of file diff --git a/app/routers/chat.py b/app/routers/chat.py index 03116eb..d03fc43 100644 --- a/app/routers/chat.py +++ b/app/routers/chat.py @@ -1,11 +1,10 @@ """ FILE: app/routers/chat.py DESCRIPTION: Haupt-Chat-Interface (RAG & Interview). Enthält Intent-Router (Keywords/LLM) und Prompt-Construction. -VERSION: 2.5.0 +VERSION: 2.6.0 (WP-22 Semantic Graph Routing) STATUS: Active DEPENDENCIES: app.config, app.models.dto, app.services.llm_service, app.core.retriever, app.services.feedback_service EXTERNAL_CONFIG: config/decision_engine.yaml, config/types.yaml -LAST_ANALYSIS: 2025-12-15 """ from fastapi import APIRouter, HTTPException, Depends @@ -281,12 +280,20 @@ async def chat_endpoint( # --- RAG MODE --- inject_types = strategy.get("inject_types", []) prepend_instr = strategy.get("prepend_instruction", "") + + # --- WP-22: Semantic Graph Routing --- + # Wir laden die konfigurierten Edge-Boosts für diesen Intent + edge_boosts = strategy.get("edge_boosts", {}) + if edge_boosts: + logger.info(f"[{query_id}] Applying Edge Boosts: {edge_boosts}") query_req = QueryRequest( query=request.message, mode="hybrid", top_k=request.top_k, - explain=request.explain + explain=request.explain, + # WP-22: Boosts weitergeben + boost_edges=edge_boosts ) retrieve_result = await retriever.search(query_req) hits = retrieve_result.results @@ -297,7 +304,9 @@ async def chat_endpoint( mode="hybrid", top_k=3, filters={"type": inject_types}, - explain=False + explain=False, + # WP-22: Boosts auch hier anwenden (Konsistenz) + boost_edges=edge_boosts ) strategy_result = await retriever.search(strategy_req) existing_ids = {h.node_id for h in hits} diff --git a/config/decision_engine.yaml b/config/decision_engine.yaml index 278f582..d5ab878 100644 --- a/config/decision_engine.yaml +++ b/config/decision_engine.yaml @@ -1,8 +1,8 @@ # config/decision_engine.yaml # Steuerung der Decision Engine (Intent Recognition) -# Version: 2.4.0 (Clean Architecture: Generic Intents only) +# Version: 2.5.0 (WP-22: Semantic Graph Routing) -version: 1.4 +version: 2.5 settings: llm_fallback_enabled: true @@ -37,6 +37,12 @@ strategies: description: "Reine Wissensabfrage." trigger_keywords: [] inject_types: [] + # WP-22: Definitionen & Hierarchien bevorzugen + edge_boosts: + part_of: 2.0 + composed_of: 2.0 + similar_to: 1.5 + caused_by: 0.5 # Kausalität ist hier oft Rauschen prompt_template: "rag_template" prepend_instruction: null @@ -53,6 +59,12 @@ strategies: - "abwägung" - "vergleich" inject_types: ["value", "principle", "goal", "risk"] + # WP-22: Risiken und Konsequenzen hervorheben + edge_boosts: + blocks: 2.5 # Blocker/Risiken sind kritisch + solves: 2.0 # Lösungen sind relevant + depends_on: 1.5 + risk_of: 2.5 prompt_template: "decision_template" prepend_instruction: | !!! ENTSCHEIDUNGS-MODUS !!! @@ -71,6 +83,12 @@ strategies: - "überfordert" - "müde" inject_types: ["experience", "belief", "profile"] + # WP-22: Weiche Assoziationen & Erfahrungen stärken + edge_boosts: + based_on: 2.0 # Werte-Bezug + related_to: 2.0 # Assoziatives Denken + experienced_in: 2.5 + blocks: 0.1 # Stressoren ausblenden prompt_template: "empathy_template" prepend_instruction: null @@ -88,6 +106,11 @@ strategies: - "yaml" - "bash" inject_types: ["snippet", "reference", "source"] + # WP-22: Technische Abhängigkeiten + edge_boosts: + uses: 2.5 # Tool-Nutzung + depends_on: 2.0 + implemented_in: 3.0 prompt_template: "technical_template" prepend_instruction: null @@ -108,6 +131,7 @@ strategies: - "idee speichern" - "draft" inject_types: [] + edge_boosts: {} # Kein Retrieval im Interview Modus prompt_template: "interview_template" prepend_instruction: null