ARD v0.91 + OKF namespace, NLWeb protocol, and complete table descriptions - #1
Merged
Conversation
…tions Discovery was picking the wrong table on ambiguous measures: "total revenue" scored the IRS 990 leaf and the us-gaap concept within 0.001 of each other, so which one won was luck. The cause was that leaves were embedded scope-blind — each source already declares an entityType, but only the classifier ever saw it, and descriptions were truncated to 200 chars. Every table now carries a full description naming its subject, and the index embeds that scope. Descriptors - tools/descriptions.py writes detailed descriptions from each leaf's label and definition, batched, cached and concurrent; generators use it. - tools/check_descriptions.py verifies generated text against the definition it was expanded from, screening deterministically before an LLM adjudication pass. Ground truth is descriptions_input.json, recorded at generation time, because most generated leaves are gitignored and have no committed "before". - repr_queries.py dedupes, strips template placeholders and caps at the spec's 2-5 examples, on cached entries as well as fresh ones. - registry/index.py embeds title + queries + scope + the FULL description, and the re-rank card drops the description (the prefilter has already used it). ARD v0.91 (PR #70) - Entries are domain-anchored URNs (urn:air:sec.gov:okf:sec-edgar.revenues), carry trustManifest from each source's trust block, and honour url XOR data. - An OKF namespace is declared per entry, first key, so a prefixed term is bound wherever the entry is copied — an envelope-only context breaks as soon as a consumer lifts one entry out of entries[]. - GET /agents, POST /explore, GET /agents/entry and /.well-known/ard.json join POST /search. Validated against the published ard-entry.schema.json. NLWeb - /ask now speaks NLWeb and nothing else: begin/intermediate/result/nlws/ complete/end, SSE by default, blocking on streaming=false, plus /sites and /health. An OKF source is an NLWeb site; an OKF table is an Item whose schema_object is the descriptor's own frontmatter. Instrumentation - Per-question LLM calls, tokens and cost, broken down by step and by model, with the Agent Finder reported separately since it is its own service. GET /costs reports the running total. Grant graph - Moves to managed Postgres (tools/grants_to_postgres.py), with per-thread connection pooling and precomputed population rollups: the by-cause join went from ~280s live to ~6ms, which is what makes the smallest SKU viable. Deployment - BIND_HOST/PORT, /healthz, a per-source daily cap on /ask, CORS, and DEPLOY.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Discovery was picking the wrong table on ambiguous measures. On the bare query
total revenue— which is what discovery actually searches, since the entity is expunged first — the IRS 990 leaf and the us-gaap concept scored within 0.001 of each other, so which one won was luck rather than ranking.The cause wasn't description length as such: every source already declares an
entityType, but only the classifier ever saw it. Leaves were embedded scope-blind, with descriptions truncated to 200 chars. The re-ranker was worse — its prompt tells the model to "match the table's SUBJECT and SCOPE," which the candidate cards never stated.Results
"total revenue"→ SEC vs 990tests/route_eval.pymeasures routing and discovery cost together, so both numbers are reproducible.Descriptors
tools/descriptions.pyexpands each leaf's one-line definition into a full description naming its subject and what it excludes. Batched, cached, concurrent.tools/check_descriptions.pyverifies generated text against the definition it came from — a deterministic screen, then an LLM adjudication of only the suspicious ones. Ground truth isdescriptions_input.json, recorded at generation time, because the generated leaves are gitignored and have no committed "before". Final rate: 38 unsupported of 6,171 (0.62%), each round fed back into the prompt.repr_queries.pydedupes, strips template placeholders and caps at the spec's 2–5 — on cached entries as well as fresh ones. This removed 19 queries that embedded literal{school}braces into the index.ARD v0.91 (ards-project/ard-spec#70)
urn:air:sec.gov:okf:sec-edgar.revenues. 8,925 URNs, no collisions.trustManifestfrom each source's existingtrustblock;urlXORdatahonoured.entries[], which is exactly what a registry does. Verified with a real JSON-LD processor: everyokf:term expands, nothing left unbound.GET /agents,POST /explore,GET /agents/entry,/.well-known/ard.jsonjoinPOST /search. All shapes validate against the publishedard-entry.schema.json.NLWeb
/askspeaks NLWeb and nothing else —begin/intermediate_message/result/nlws/complete/end, SSE by default, blocking onstreaming=false, plus/sitesand/health. An OKF source is an NLWeb site; an OKF table is an Item whoseschema_objectis the descriptor's own frontmatter.Instrumentation
Per-question LLM calls, tokens and cost, broken down by step (classify / resolve / check / synthesize) and by model. The Agent Finder is reported separately, since it's its own service.
GET /costsgives the running total.Grant graph
Moves to managed Postgres (
tools/grants_to_postgres.py), with per-thread connection pooling and precomputed population rollups. The by-cause join went from ~280s live to ~6ms — which is what makes the smallest SKU viable rather than merely cheap. Row counts, totals and the case-insensitive match path are all verified against the source before anything is dropped.Deployment
BIND_HOST/PORT,/healthz(no LLM call — a health probe that costs money per poll is a bill), a per-source daily cap on/ask, CORS, andDEPLOY.mdcovering what is not production-hardened.Full audit before commit: 8,925 documents, zero issues (junk tags, placeholders, duplicate or out-of-range queries, thin descriptions, missing titles). All seven source types answer end to end.
🤖 Generated with Claude Code