feat: fundar plataforma mais humana
This commit is contained in:
146
src/mais_humana/evidence_index.py
Normal file
146
src/mais_humana/evidence_index.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""Searchable evidence index for generated human reports."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable, Sequence
|
||||
|
||||
from .models import Evidence, EvidenceKind, PlatformScan, as_plain_data
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class EvidenceRecord:
|
||||
evidence_id: str
|
||||
platform_id: str
|
||||
kind: str
|
||||
reference: str
|
||||
summary: str
|
||||
confidence: float
|
||||
tags: tuple[str, ...]
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return as_plain_data(self)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class EvidenceQuery:
|
||||
platform_id: str | None = None
|
||||
kind: str | None = None
|
||||
min_confidence: float = 0.0
|
||||
tag: str | None = None
|
||||
text: str | None = None
|
||||
|
||||
|
||||
def normalize_kind(kind: EvidenceKind | str) -> str:
|
||||
return kind.value if isinstance(kind, EvidenceKind) else str(kind)
|
||||
|
||||
|
||||
def record_from_evidence(platform_id: str, evidence: Evidence, index: int) -> EvidenceRecord:
|
||||
return EvidenceRecord(
|
||||
evidence_id=f"{platform_id}-{index:04d}-{normalize_kind(evidence.kind)}",
|
||||
platform_id=platform_id,
|
||||
kind=normalize_kind(evidence.kind),
|
||||
reference=evidence.reference,
|
||||
summary=evidence.summary,
|
||||
confidence=evidence.confidence,
|
||||
tags=tuple(evidence.tags),
|
||||
)
|
||||
|
||||
|
||||
def build_evidence_index(scans: Sequence[PlatformScan]) -> tuple[EvidenceRecord, ...]:
|
||||
records: list[EvidenceRecord] = []
|
||||
for scan in scans:
|
||||
for index, evidence in enumerate(scan.evidence, start=1):
|
||||
records.append(record_from_evidence(scan.platform.platform_id, evidence, index))
|
||||
records.sort(key=lambda item: (item.platform_id, item.kind, -item.confidence, item.reference))
|
||||
return tuple(records)
|
||||
|
||||
|
||||
def query_evidence(records: Sequence[EvidenceRecord], query: EvidenceQuery) -> tuple[EvidenceRecord, ...]:
|
||||
output: list[EvidenceRecord] = []
|
||||
text = query.text.lower() if query.text else None
|
||||
for record in records:
|
||||
if query.platform_id and record.platform_id != query.platform_id:
|
||||
continue
|
||||
if query.kind and record.kind != query.kind:
|
||||
continue
|
||||
if record.confidence < query.min_confidence:
|
||||
continue
|
||||
if query.tag and query.tag not in record.tags:
|
||||
continue
|
||||
if text and text not in f"{record.summary} {record.reference} {' '.join(record.tags)}".lower():
|
||||
continue
|
||||
output.append(record)
|
||||
return tuple(output)
|
||||
|
||||
|
||||
def evidence_counts_by_platform(records: Sequence[EvidenceRecord]) -> dict[str, int]:
|
||||
counts: dict[str, int] = {}
|
||||
for record in records:
|
||||
counts[record.platform_id] = counts.get(record.platform_id, 0) + 1
|
||||
return dict(sorted(counts.items()))
|
||||
|
||||
|
||||
def evidence_counts_by_kind(records: Sequence[EvidenceRecord]) -> dict[str, int]:
|
||||
counts: dict[str, int] = {}
|
||||
for record in records:
|
||||
counts[record.kind] = counts.get(record.kind, 0) + 1
|
||||
return dict(sorted(counts.items(), key=lambda item: (-item[1], item[0])))
|
||||
|
||||
|
||||
def strongest_evidence(records: Sequence[EvidenceRecord], limit: int = 25) -> tuple[EvidenceRecord, ...]:
|
||||
ordered = sorted(records, key=lambda item: (-item.confidence, item.platform_id, item.kind))
|
||||
return tuple(ordered[:limit])
|
||||
|
||||
|
||||
def weakest_evidence(records: Sequence[EvidenceRecord], limit: int = 25) -> tuple[EvidenceRecord, ...]:
|
||||
ordered = sorted(records, key=lambda item: (item.confidence, item.platform_id, item.kind))
|
||||
return tuple(ordered[:limit])
|
||||
|
||||
|
||||
def evidence_markdown(records: Sequence[EvidenceRecord]) -> str:
|
||||
lines = ["# Indice de evidencias humanas", ""]
|
||||
lines.append(f"Total de evidencias: `{len(records)}`")
|
||||
lines.append("")
|
||||
lines.append("## Por plataforma")
|
||||
lines.append("")
|
||||
for platform_id, count in evidence_counts_by_platform(records).items():
|
||||
lines.append(f"- {platform_id}: {count}")
|
||||
lines.append("")
|
||||
lines.append("## Por tipo")
|
||||
lines.append("")
|
||||
for kind, count in evidence_counts_by_kind(records).items():
|
||||
lines.append(f"- {kind}: {count}")
|
||||
lines.append("")
|
||||
lines.append("## Evidencias fortes")
|
||||
lines.append("")
|
||||
for record in strongest_evidence(records, limit=30):
|
||||
lines.append(f"- `{record.platform_id}` `{record.kind}` {record.confidence:.2f}: {record.reference} - {record.summary}")
|
||||
lines.append("")
|
||||
lines.append("## Evidencias fracas para revisar")
|
||||
lines.append("")
|
||||
for record in weakest_evidence(records, limit=20):
|
||||
lines.append(f"- `{record.platform_id}` `{record.kind}` {record.confidence:.2f}: {record.reference} - {record.summary}")
|
||||
return "\n".join(lines).strip() + "\n"
|
||||
|
||||
|
||||
def evidence_records_for_platform(records: Sequence[EvidenceRecord], platform_id: str) -> tuple[EvidenceRecord, ...]:
|
||||
return query_evidence(records, EvidenceQuery(platform_id=platform_id))
|
||||
|
||||
|
||||
def evidence_records_for_human_surface(records: Sequence[EvidenceRecord]) -> tuple[EvidenceRecord, ...]:
|
||||
kinds = {"ui_surface", "mcp_tool", "observability", "business_rule", "security"}
|
||||
return tuple(record for record in records if record.kind in kinds)
|
||||
|
||||
|
||||
def evidence_gap_summary(records: Sequence[EvidenceRecord], expected_platforms: Iterable[str]) -> tuple[str, ...]:
|
||||
counts = evidence_counts_by_platform(records)
|
||||
gaps: list[str] = []
|
||||
for platform_id in expected_platforms:
|
||||
count = counts.get(platform_id, 0)
|
||||
if count == 0:
|
||||
gaps.append(f"{platform_id}: nenhuma evidencia")
|
||||
elif count < 5:
|
||||
gaps.append(f"{platform_id}: poucas evidencias ({count})")
|
||||
return tuple(gaps)
|
||||
|
||||
Reference in New Issue
Block a user