Download evidence/retrieve.py from Snapkitty/david: direct link, hf CLI and curl.
- Browser
- Download file 11.8 kB
-
https://huggingface.co/Snapkitty/david/resolve/main/evidence/retrieve.py
- Command line
-
hf download hf://Snapkitty/david/evidence/retrieve.py
-
curl -L -o retrieve.py https://huggingface.co/Snapkitty/david/resolve/main/evidence/retrieve.py
11.8 kB
| """Hybrid evidence retrieval — dense + lexical + metadata + provenance. | |
| In-memory stand-in for sql/001_agent_and_evidence_indexes.sql. | |
| Production SQL uses vector(1536); tests use fixed dim-8 vectors (no live DB). | |
| Merge / rerank rule (deterministic): | |
| score = dense_cosine + lexical_overlap + source_authority - penalties | |
| sort by (-score, evidence_id) | |
| penalties: missing required parent_evidence when policy requires it | |
| Hybrid note: a chunk with weak dense but strong lexical can still rank | |
| if lexical_overlap compensates (no hard dense floor). | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import math | |
| import re | |
| from dataclasses import dataclass, field | |
| from typing import Any, Iterable, Sequence | |
| # Production pgvector column is vector(1536); test corpus uses tiny fixed vectors. | |
| EMBEDDING_DIM_TEST = 8 | |
| EMBEDDING_DIM_PRODUCTION = 1536 | |
| class EvidenceRecord: | |
| """Fields mirror the SQL ``evidence`` table.""" | |
| evidence_id: str | |
| source_id: str | |
| source_type: str | |
| document_id: str | None | |
| location: str | None | |
| content: str | |
| content_hash: str | |
| embedding: list[float] | None | |
| source_authority: float = 0.5 | |
| parent_evidence: tuple[str, ...] = () | |
| created_at: str | None = None | |
| def _content_hash(text: str) -> str: | |
| return hashlib.sha256(text.encode("utf-8")).hexdigest()[:16] | |
| def _tokenize(text: str) -> set[str]: | |
| return {t for t in re.findall(r"[a-z0-9]+", text.lower()) if len(t) > 1} | |
| def cosine_similarity(a: Sequence[float], b: Sequence[float]) -> float: | |
| if not a or not b or len(a) != len(b): | |
| return 0.0 | |
| dot = sum(x * y for x, y in zip(a, b)) | |
| na = math.sqrt(sum(x * x for x in a)) | |
| nb = math.sqrt(sum(y * y for y in b)) | |
| if na == 0.0 or nb == 0.0: | |
| return 0.0 | |
| return dot / (na * nb) | |
| def lexical_score(query: str, content: str) -> float: | |
| """Token overlap stand-in for ``content_tsv @@ plainto_tsquery``.""" | |
| q = _tokenize(query) | |
| if not q: | |
| return 0.0 | |
| c = _tokenize(content) | |
| if not c: | |
| return 0.0 | |
| return len(q & c) / len(q) | |
| def empty_bundle() -> dict[str, Any]: | |
| """EvidenceBundle-shaped dict with no hits (contracts.md).""" | |
| return { | |
| "facts": [], | |
| "sources": [], | |
| "provenance": [], | |
| "retrieval_metadata": [], | |
| } | |
| def seed_cobol_corpus() -> list[EvidenceRecord]: | |
| """Fixed COBOL-modernization corpus (dim-8 embeddings for tests). | |
| Axes (approx): [cobol, balance, payment, schema, pricing, vol, greek, junk] | |
| """ | |
| # Strong COBOL / balance / payment cluster | |
| copybook = EvidenceRecord( | |
| evidence_id="ev_copybook_01", | |
| source_id="src_copybook_acct", | |
| source_type="copybook", | |
| document_id="ACCTREC.cpy", | |
| location="01-ACCOUNT-BALANCE", | |
| content=( | |
| "01 ACCOUNT-RECORD. " | |
| "05 ACCOUNT-BALANCE PIC S9(9)V99. " | |
| "05 PAYMENT-AMOUNT PIC S9(7)V99. " | |
| "05 LAST-PAYMENT-DATE PIC X(8)." | |
| ), | |
| content_hash="", | |
| embedding=[0.95, 0.90, 0.85, 0.20, 0.05, 0.0, 0.0, 0.0], | |
| source_authority=0.95, | |
| parent_evidence=("ev_provenance_root",), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| cobol_src = EvidenceRecord( | |
| evidence_id="ev_cobol_src_01", | |
| source_id="src_cobol_pay", | |
| source_type="cobol_source", | |
| document_id="PAYROLL.cbl", | |
| location="PROCEDURE DIVISION / CALC-BALANCE", | |
| content=( | |
| "COMPUTE ACCOUNT-BALANCE = ACCOUNT-BALANCE - PAYMENT-AMOUNT. " | |
| "IF ACCOUNT-BALANCE < ZERO THEN PERFORM OVERDRAFT-CHECK. " | |
| "MOVE PAYMENT-AMOUNT TO WS-LAST-PAYMENT." | |
| ), | |
| content_hash="", | |
| embedding=[0.92, 0.88, 0.90, 0.15, 0.05, 0.0, 0.0, 0.0], | |
| source_authority=0.90, | |
| parent_evidence=("ev_provenance_root",), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| db2 = EvidenceRecord( | |
| evidence_id="ev_db2_schema_01", | |
| source_id="src_db2_acct", | |
| source_type="db2_schema", | |
| document_id="ACCT.DDL", | |
| location="TABLE ACCOUNT_BALANCES", | |
| content=( | |
| "CREATE TABLE ACCOUNT_BALANCES (" | |
| "ACCT_ID CHAR(12), BALANCE DECIMAL(11,2), " | |
| "LAST_PAYMENT DECIMAL(9,2), UPDATED_TS TIMESTAMP)." | |
| ), | |
| content_hash="", | |
| embedding=[0.70, 0.80, 0.55, 0.95, 0.05, 0.0, 0.0, 0.0], | |
| source_authority=0.85, | |
| parent_evidence=("ev_provenance_root",), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| # Should NOT win on a COBOL balance query (pricing / quant cluster) | |
| quant = EvidenceRecord( | |
| evidence_id="ev_quant_pricing_01", | |
| source_id="src_quant_report", | |
| source_type="quant_report", | |
| document_id="PRICING_Q3.pdf", | |
| location="section:monte_carlo", | |
| content=( | |
| "Monte Carlo pricing of exotic options. " | |
| "Implied volatility surface and greek sensitivities. " | |
| "No COBOL or account balance fields." | |
| ), | |
| content_hash="", | |
| embedding=[0.05, 0.05, 0.05, 0.05, 0.95, 0.90, 0.85, 0.10], | |
| source_authority=0.80, | |
| parent_evidence=("ev_provenance_root",), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| # Low-authority junk for provenance filter tests | |
| junk = EvidenceRecord( | |
| evidence_id="ev_junk_lowauth_01", | |
| source_id="src_web_scrape", | |
| source_type="web_scrape", | |
| document_id="random-blog.html", | |
| location="body", | |
| content=( | |
| "Someone said COBOL payment balance might work like a spreadsheet. " | |
| "Unverified forum post." | |
| ), | |
| content_hash="", | |
| embedding=[0.60, 0.55, 0.50, 0.10, 0.10, 0.0, 0.0, 0.9], | |
| source_authority=0.15, | |
| parent_evidence=(), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| # Weak dense (far from COBOL cluster) but strong lexical on balance/payment/cobol | |
| # — hybrid: lexical can lift it into results when dense alone would not. | |
| lexical_lift = EvidenceRecord( | |
| evidence_id="ev_lexical_lift_01", | |
| source_id="src_glossary", | |
| source_type="glossary", | |
| document_id="LEGACY-GLOSSARY.txt", | |
| location="entry:PAYMENT-BALANCE", | |
| content=( | |
| "cobol payment balance glossary entry: " | |
| "PAYMENT and BALANCE fields on ACCOUNT records in legacy batch." | |
| ), | |
| content_hash="", | |
| # Oriented away from COBOL cluster → weak cosine vs COBOL query | |
| embedding=[0.10, 0.15, 0.10, 0.05, 0.40, 0.35, 0.30, 0.20], | |
| source_authority=0.70, | |
| parent_evidence=("ev_provenance_root",), | |
| created_at="2026-01-01T00:00:00Z", | |
| ) | |
| records = [copybook, cobol_src, db2, quant, junk, lexical_lift] | |
| for r in records: | |
| r.content_hash = _content_hash(r.content) | |
| return records | |
| # Canonical COBOL payment/balance query embedding (dim 8) | |
| COBOL_BALANCE_QUERY = "cobol account payment balance CALC-BALANCE" | |
| COBOL_BALANCE_QUERY_VEC = [0.93, 0.91, 0.88, 0.25, 0.05, 0.0, 0.0, 0.0] | |
| class EvidenceIndex: | |
| """In-memory evidence index (no Postgres required).""" | |
| records: list[EvidenceRecord] = field(default_factory=list) | |
| def __post_init__(self) -> None: | |
| if not self.records: | |
| self.records = seed_cobol_corpus() | |
| self._by_id = {r.evidence_id: r for r in self.records} | |
| def add(self, record: EvidenceRecord) -> None: | |
| if not record.content_hash: | |
| record.content_hash = _content_hash(record.content) | |
| self.records.append(record) | |
| self._by_id[record.evidence_id] = record | |
| def retrieve( | |
| query: str, | |
| *, | |
| query_embedding: Sequence[float] | None = None, | |
| index: EvidenceIndex | None = None, | |
| source_types: Iterable[str] | None = None, | |
| document_ids: Iterable[str] | None = None, | |
| min_authority: float = 0.0, | |
| require_parent_evidence: bool = False, | |
| top_k: int = 10, | |
| ) -> dict[str, Any]: | |
| """Hybrid retrieve → EvidenceBundle-shaped dict (facts/sources/provenance/…). | |
| Agents never receive bare text without provenance fields on each source. | |
| """ | |
| if not query or not query.strip(): | |
| return empty_bundle() | |
| idx = index or EvidenceIndex() | |
| st_filter = set(source_types) if source_types is not None else None | |
| doc_filter = set(document_ids) if document_ids is not None else None | |
| q_emb = list(query_embedding) if query_embedding is not None else None | |
| scored: list[tuple[float, EvidenceRecord, dict[str, float]]] = [] | |
| for rec in idx.records: | |
| # Metadata filters | |
| if st_filter is not None and rec.source_type not in st_filter: | |
| continue | |
| if doc_filter is not None and rec.document_id not in doc_filter: | |
| continue | |
| # Provenance / authority | |
| if rec.source_authority < min_authority: | |
| continue | |
| if require_parent_evidence and not rec.parent_evidence: | |
| continue | |
| dense = 0.0 | |
| if q_emb is not None and rec.embedding is not None: | |
| dense = cosine_similarity(q_emb, rec.embedding) | |
| lex = lexical_score(query, rec.content) | |
| authority = float(rec.source_authority) | |
| penalty = 0.0 | |
| if require_parent_evidence and not rec.parent_evidence: | |
| penalty += 1.0 # defensive; already filtered above | |
| score = dense + lex + authority - penalty | |
| parts = { | |
| "dense": dense, | |
| "lexical": lex, | |
| "source_authority": authority, | |
| "penalty": penalty, | |
| "score": score, | |
| } | |
| scored.append((score, rec, parts)) | |
| # Deterministic rerank: score desc, then evidence_id asc | |
| scored.sort(key=lambda t: (-t[0], t[1].evidence_id)) | |
| top = scored[:top_k] | |
| if not top: | |
| return empty_bundle() | |
| facts: list[dict[str, Any]] = [] | |
| sources: list[dict[str, Any]] = [] | |
| provenance: list[dict[str, Any]] = [] | |
| retrieval_metadata: list[dict[str, Any]] = [] | |
| for score, rec, parts in top: | |
| facts.append( | |
| { | |
| "evidence_id": rec.evidence_id, | |
| "summary": rec.content[:160], | |
| "source_type": rec.source_type, | |
| } | |
| ) | |
| sources.append( | |
| { | |
| "evidence_id": rec.evidence_id, | |
| "source_id": rec.source_id, | |
| "source_type": rec.source_type, | |
| "document_id": rec.document_id, | |
| "location": rec.location, | |
| "content_hash": rec.content_hash, | |
| "source_authority": rec.source_authority, | |
| "parent_evidence": list(rec.parent_evidence), | |
| "content": rec.content, | |
| "retrieval_score": score, | |
| } | |
| ) | |
| provenance.append( | |
| { | |
| "evidence_id": rec.evidence_id, | |
| "source_id": rec.source_id, | |
| "content_hash": rec.content_hash, | |
| "parent_evidence": list(rec.parent_evidence), | |
| "source_authority": rec.source_authority, | |
| } | |
| ) | |
| retrieval_metadata.append( | |
| { | |
| "evidence_id": rec.evidence_id, | |
| "dense": parts["dense"], | |
| "lexical": parts["lexical"], | |
| "source_authority": parts["source_authority"], | |
| "penalty": parts["penalty"], | |
| "score": parts["score"], | |
| "embedding_dim_note": ( | |
| f"test vectors dim={EMBEDDING_DIM_TEST}; " | |
| f"production SQL uses vector({EMBEDDING_DIM_PRODUCTION})" | |
| ), | |
| } | |
| ) | |
| return { | |
| "facts": facts, | |
| "sources": sources, | |
| "provenance": provenance, | |
| "retrieval_metadata": retrieval_metadata, | |
| } | |