This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

140 lines
4.5 KiB
Python

"""
Assistant Service for RAG
Handles Q&A using LLM with retrieved context.
"""
import httpx
from typing import List, Optional, Dict, Any
import structlog
from .search import SearchService
logger = structlog.get_logger()
SYSTEM_PROMPT = """Du bist ein Experte für Datenschutz- und Compliance-Recht.
Beantworte Fragen basierend auf den bereitgestellten Rechtstexten.
Zitiere immer die relevanten Artikel und Paragraphen.
Antworte auf Deutsch.
Wenn du dir nicht sicher bist, sage das klar.
"""
class AssistantService:
"""Service for legal Q&A using RAG."""
def __init__(self, settings):
self.settings = settings
self.search_service = SearchService(settings)
async def ask(
self,
question: str,
context: Optional[str] = None,
regulation_codes: Optional[List[str]] = None,
include_citations: bool = True
) -> Dict[str, Any]:
"""Answer a legal question using RAG."""
# Search for relevant context
search_results = await self.search_service.search(
query=question,
regulation_codes=regulation_codes,
limit=5,
min_score=0.6
)
# Build context from search results
retrieved_context = "\n\n".join([
f"[{r['regulation_code']} Art. {r['article']}]: {r['content']}"
for r in search_results
])
# Add user-provided context if any
if context:
retrieved_context = f"{context}\n\n{retrieved_context}"
# Build prompt
prompt = f"""Kontext aus Rechtstexten:
{retrieved_context}
Frage: {question}
Beantworte die Frage basierend auf dem Kontext. Zitiere relevante Artikel."""
# Generate answer
answer = await self._generate_response(prompt)
# Extract citations
citations = []
if include_citations:
for result in search_results:
citations.append({
"regulation_code": result["regulation_code"],
"article": result.get("article", ""),
"text": result["content"][:200] + "...",
"relevance": result["score"]
})
return {
"answer": answer,
"citations": citations,
"confidence": self._calculate_confidence(search_results)
}
async def _generate_response(self, prompt: str) -> str:
"""Generate response using Ollama."""
try:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.settings.ollama_url}/api/generate",
json={
"model": self.settings.llm_model,
"prompt": prompt,
"system": SYSTEM_PROMPT,
"stream": False,
"options": {
"temperature": 0.3,
"top_p": 0.9
}
},
timeout=120.0
)
response.raise_for_status()
return response.json()["response"]
except httpx.TimeoutException:
logger.error("LLM request timed out")
return "Die Anfrage hat zu lange gedauert. Bitte versuchen Sie es erneut."
except Exception as e:
logger.error("LLM generation failed", error=str(e))
# Return fallback response
return self._generate_fallback_response(prompt)
def _generate_fallback_response(self, prompt: str) -> str:
"""Generate a fallback response without LLM."""
return """Basierend auf den verfügbaren Rechtstexten:
Die relevanten Regelungen finden sich in den zitierten Artikeln.
Für eine detaillierte rechtliche Bewertung empfehle ich die Konsultation
der vollständigen Gesetzestexte oder eines Rechtsbeistands.
Hinweis: Dies ist eine automatisch generierte Antwort.
Der LLM-Dienst war nicht verfügbar."""
def _calculate_confidence(self, search_results: List[Dict]) -> float:
"""Calculate confidence score based on search results."""
if not search_results:
return 0.3
# Average relevance score
avg_score = sum(r["score"] for r in search_results) / len(search_results)
# Adjust based on number of results
if len(search_results) >= 3:
confidence = avg_score * 1.1
else:
confidence = avg_score * 0.9
return min(confidence, 1.0)