docs: update HANDOFF.md — Stage 9 completed, Stage 10 next

This commit is contained in:
NSCT Agent
2026-08-24 12:38:47 +00:00
parent d87e2b4d14
commit 8582a8e60f
3 changed files with 143 additions and 104 deletions

View File

@@ -27,6 +27,7 @@ from typing import Any
from uuid import UUID
from nsct.models.schemas import SynthesisClaimModel, SynthesisReportModel
from pydantic import ValidationError as PydanticValidationError
logger = logging.getLogger(__name__)
@@ -178,15 +179,8 @@ def _extract_report_from_parsed(
parsed: dict[str, Any],
llm_model: str,
topic: str,
) -> dict[str, Any]:
"""Wandelt die geparste LLM-Antwort in ein SynthesisReportModel um.
Returns
-------
dict mit keys:
research_topic, summary, confident_findings, uncertain_areas,
contradictions, source_list, llm_model_used, generation_timestamp
"""
) -> SynthesisReportModel:
"""Wandelt die geparste LLM-Antwort in ein SynthesisReportModel um."""
summary = parsed.get("summary", "")
if not isinstance(summary, str):
summary = str(summary)
@@ -204,56 +198,56 @@ def _extract_report_from_parsed(
contradictions_raw = []
# confident_findings
confident: list[dict[str, Any]] = []
confident: list[SynthesisClaimModel] = []
for item in confident_raw:
if not isinstance(item, dict):
continue
try:
claim = {
"claim_text": str(item.get("claim_text", "")),
"evidence_type": str(item.get("evidence_type", "secondary_report")),
"source_independence_score": float(
claim = SynthesisClaimModel(
claim_text=str(item.get("claim_text", "")),
evidence_type=str(item.get("evidence_type", "secondary_report")),
source_independence_score=float(
item.get("source_independence_score", 0.5)
),
"cross_source_support": float(item.get("cross_source_support", 0.0)),
"contradiction_level": float(item.get("contradiction_level", 1.0)),
"evidence_directness": float(item.get("evidence_directness", 0.5)),
"source_id": item.get("source_id", ""),
"source_url": str(item.get("source_url", "")),
"source_title": item.get("source_title"),
"evidence_span": item.get("evidence_span"),
"confidence": float(item.get("confidence", 1.0)),
}
cross_source_support=float(item.get("cross_source_support", 0.0)),
contradiction_level=float(item.get("contradiction_level", 1.0)),
evidence_directness=float(item.get("evidence_directness", 0.5)),
source_id=item.get("source_id", ""),
source_url=str(item.get("source_url", "")),
source_title=item.get("source_title"),
evidence_span=item.get("evidence_span"),
confidence=float(item.get("confidence", 1.0)),
)
confident.append(claim)
except (ValueError, TypeError):
except (ValueError, TypeError, PydanticValidationError):
logger.warning("Skipping malformed confident finding: %s", item)
# uncertain_areas
uncertain: list[dict[str, Any]] = []
uncertain: list[SynthesisClaimModel] = []
for item in uncertain_raw:
if not isinstance(item, dict):
continue
try:
claim = {
"claim_text": str(item.get("claim_text", "")),
"evidence_type": str(item.get("evidence_type", "speculation")),
"source_independence_score": float(
claim = SynthesisClaimModel(
claim_text=str(item.get("claim_text", "")),
evidence_type=str(item.get("evidence_type", "speculation")),
source_independence_score=float(
item.get("source_independence_score", 0.5)
),
"cross_source_support": float(item.get("cross_source_support", 0.0)),
"contradiction_level": float(item.get("contradiction_level", 1.0)),
"evidence_directness": float(item.get("evidence_directness", 0.5)),
"source_id": item.get("source_id", ""),
"source_url": str(item.get("source_url", "")),
"source_title": item.get("source_title"),
"evidence_span": item.get("evidence_span"),
"confidence": float(item.get("confidence", 1.0)),
}
cross_source_support=float(item.get("cross_source_support", 0.0)),
contradiction_level=float(item.get("contradiction_level", 1.0)),
evidence_directness=float(item.get("evidence_directness", 0.5)),
source_id=item.get("source_id", ""),
source_url=str(item.get("source_url", "")),
source_title=item.get("source_title"),
evidence_span=item.get("evidence_span"),
confidence=float(item.get("confidence", 1.0)),
)
uncertain.append(claim)
except (ValueError, TypeError):
except (ValueError, TypeError, PydanticValidationError):
logger.warning("Skipping malformed uncertain area: %s", item)
# contradictions
# contradictions — keep as plain dicts
contradictions: list[dict[str, Any]] = []
for item in contradictions_raw:
if not isinstance(item, dict):
@@ -279,32 +273,32 @@ def _extract_report_from_parsed(
source_list: list[dict[str, Any]] = []
seen_urls: set[str] = set()
for claim in confident + uncertain:
url = claim.get("source_url", "")
url = claim.source_url if isinstance(claim, SynthesisClaimModel) else claim.get("source_url", "")
if url and url not in seen_urls:
seen_urls.add(url)
source_list.append({
"url": url,
"title": claim.get("source_title"),
"source_id": str(claim.get("source_id", "")),
"title": (
claim.source_title
if isinstance(claim, SynthesisClaimModel)
else claim.get("source_title")
),
"source_id": str(
claim.source_id
if isinstance(claim, SynthesisClaimModel)
else claim.get("source_id", "")
),
})
return {
"research_topic": topic,
"summary": summary,
"confident_findings": confident,
"uncertain_areas": uncertain,
"contradictions": contradictions,
"source_list": source_list,
"llm_model_used": llm_model,
"generation_timestamp": datetime.now(timezone.utc),
"methodology": (
"NSCT Stage 9: Neutral Synthesis Engine. "
"Bericht generiert aus evidenzbasierten Claims (Claims 0-8). "
"Trennung von Fakten und Interpretation. "
"Keine politischen Empfehlungen. "
"Jede Aussage ist mit Quellen verknuepft (Provenance)."
),
}
return SynthesisReportModel(
research_topic=topic,
summary=summary,
confident_findings=confident,
uncertain_areas=uncertain,
contradictions=contradictions,
source_list=source_list,
llm_model_used=llm_model,
)
# ---------------------------------------------------------------------------
@@ -595,14 +589,14 @@ class SynthesisStage(BaseStage):
logger.info(
"Stage 9: Synthesis complete - %d confident, %d uncertain, %d contradictions",
len(report["confident_findings"]),
len(report["uncertain_areas"]),
len(report["contradictions"]),
len(report.confident_findings),
len(report.uncertain_areas),
len(report.contradictions),
)
return StageResult(
success=True,
data=report,
data=report.model_dump(),
stage=self,
)