"""Task C: cross-source synthesis. Combines related signals from multiple documents/changes into a conclusion (e.g. "hiring battery engineers" + "filed a battery patent" + "announced a facility expansion" = "possible battery manufacturing initiative"), always with alternative explanations and an explicit accounting of what's still unknown. """ from __future__ import annotations from pydantic import BaseModel, Field from app.analysis.llm.base import LLMProvider from app.prompts.base import build_user_prompt SYSTEM_PROMPT = ( "You are a competitive intelligence analyst. Given a set of independently-extracted " "signals about a company, identify conclusions that multiple signals point toward " "together. Only draw a conclusion when the evidence actually supports it - state your " "confidence honestly, list plausible alternative explanations, and note what evidence " "would be needed to be more certain. Do not synthesize a conclusion from a single signal." ) class SynthesizedConclusion(BaseModel): conclusion: str evidence_summary: list[str] = Field(default_factory=list) source_count: int = Field(ge=0) confidence: float = Field(ge=0.0, le=1.0) alternative_explanations: list[str] = Field(default_factory=list) missing_information: list[str] = Field(default_factory=list) class SynthesisResult(BaseModel): conclusions: list[SynthesizedConclusion] = Field(default_factory=list) async def synthesize_signals( llm: LLMProvider, *, company_name: str, monitoring_focus: str | None, signals: list[dict], ) -> SynthesisResult: evidence = { "company_name": company_name, "monitoring_focus": monitoring_focus, "signals": signals, } user_prompt = build_user_prompt( "Identify conclusions supported by two or more of these signals together. Do not " "synthesize from a single signal alone.", evidence, ) return await llm.generate_structured(SYSTEM_PROMPT, user_prompt, SynthesisResult)