Initial commit: CI Agent competitive-intelligence monitoring app
FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
"""Task E: change significance narrative.
|
||||
|
||||
Severity itself stays deterministic (app/change_detection/scoring.py) - see
|
||||
ARCHITECTURE.md: "the LLM narrates why a change matters; it does not decide
|
||||
how much it matters." This task supplies the narrative explanation and a
|
||||
second, independent opinion on whether the change looks real/meaningful and
|
||||
notification-worthy, which the notification layer (Phase 8) can use as an
|
||||
extra signal alongside the deterministic severity - it never overrides it.
|
||||
"""
|
||||
|
||||
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 reviewing one detected change between two "
|
||||
"snapshots of a company's public information. Explain in plain language why this change "
|
||||
"would or wouldn't matter to someone monitoring this company, given their stated focus. "
|
||||
"Be honest about uncertainty - do not overstate a routine wording tweak as significant, "
|
||||
"and do not undersell a genuine signal."
|
||||
)
|
||||
|
||||
|
||||
class ChangeSignificanceAssessment(BaseModel):
|
||||
is_real_change: bool = Field(description="Does this look like a genuine change, not noise?")
|
||||
is_meaningful: bool
|
||||
why_it_matters: str
|
||||
confidence: float = Field(ge=0.0, le=1.0)
|
||||
should_notify: bool
|
||||
|
||||
|
||||
async def assess_change_significance(
|
||||
llm: LLMProvider,
|
||||
*,
|
||||
company_name: str,
|
||||
monitoring_focus: str | None,
|
||||
change_type: str,
|
||||
change_summary: str,
|
||||
added_text: list[str],
|
||||
removed_text: list[str],
|
||||
deterministic_severity: str,
|
||||
deterministic_confidence: float,
|
||||
) -> ChangeSignificanceAssessment:
|
||||
evidence = {
|
||||
"company_name": company_name,
|
||||
"monitoring_focus": monitoring_focus,
|
||||
"change_type": change_type,
|
||||
"change_summary": change_summary,
|
||||
"text_added": added_text[:20],
|
||||
"text_removed": removed_text[:20],
|
||||
"deterministic_severity": deterministic_severity,
|
||||
"deterministic_confidence": deterministic_confidence,
|
||||
}
|
||||
user_prompt = build_user_prompt(
|
||||
"Assess this detected change and explain why it does or doesn't matter.", evidence
|
||||
)
|
||||
return await llm.generate_structured(SYSTEM_PROMPT, user_prompt, ChangeSignificanceAssessment)
|
||||
Reference in New Issue
Block a user