Skip to main content
Glama
hworku24

PriorArt Triage Agent

by hworku24

PriorArt Triage Agent

Give it a plain-English invention description. It searches granted US patent claims and returns a prior-art memo with per-finding citations, then verifies every assertion against the raw source material and quarantines anything it cannot ground.

python -m agent "a solar-powered soil moisture sensor that predicts irrigation need six hours ahead"

Runs entirely locally. No API key, no per-token cost, no identity verification.

Why the verification pass exists

A model that searches, drafts, and then grades its own work in one context agrees with itself. The first three iterations of this project proved it: every supporting_text came back as the model's own paraphrase, and the model-based verifier passed all of them because it was assessing relevance rather than provenance. The safety stage was theater.

The fix was to take the model out of the quoting path entirely.

The model returns a patent number and an explanation of relevance. Nothing else. The citation text is then read out of the corpus by that ID. Two properties fall out for free:

  • A hallucinated patent number is not in the database, so the finding is dropped before verification runs.

  • Every surviving quote is byte-exact claim text, because no model ever typed it.

That leaves exactly one question worth a model's judgement: does this claim text actually support the assertion made about it? Findings that fail land in a Flagged section, never in the memo body.

The general lesson, and the reason this is the interesting part of the project: don't ask a model to do what a database already does correctly. Retrieval and provenance are code. Judgement is the model.

Related MCP server: uspto-patents-mcp

Architecture

invention description
        │
   [1] plan_search       schema-constrained call → SearchPlan
        │                 examiner vocabulary, not founder pitch language
   [2] draft_memo        hand-rolled agent loop over MCP tools → DraftMemo
        │                 model returns patent IDs + relevance, never quotes
        │                 code attaches claim text from the corpus by ID
   [3] verify_memo       per-finding: does this claim text support the assertion?
        │
   memo — grounded findings, flagged findings, gaps

Path

What it is

uspto/ingest.py

Builds the local corpus from USPTO bulk downloads

uspto/corpus.py

SQLite + FTS5 query layer over granted claims

uspto/server.py

MCP server exposing three tools over stdio

agent/llm.py

Ollama backend: chat, tool calling, schema-constrained output

agent/pipeline.py

The three stages and the agent loop

agent/models.py

Pydantic schemas

agent/grounding.py

Deterministic quote-provenance checking

agent/__main__.py

CLI and Markdown renderer

scripts/smoke_test.py

Gate: prove the corpus is queryable

evals/run_eval.py

Grounding rate, hit rate, term coverage

Setup

1. Python deps

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

2. Local model

brew install ollama
ollama serve &
ollama pull qwen2.5:7b

Override with OLLAMA_MODEL if you want a different one. It needs tool-calling support; qwen2.5 and llama3.1 both have it.

3. Corpus

Sign in to data.uspto.gov and download two files into data/:

File

Dataset

Size

g_claims_2025.tsv.zip

pvgpattxt

346 MB

g_patent.tsv.zip

pvgpatdis

232 MB

Then:

python -m uspto.ingest      # ~2 minutes, produces data/corpus.db
python scripts/smoke_test.py

Run it

python -m agent "your invention description"
python -m agent -f invention.txt -o memo.md

python evals/run_eval.py --plan-only    # planning stage only, fast
python evals/run_eval.py                # full run, writes evals/results.json

Why bulk data instead of the ODP API

USPTO splits access into two tiers:

Access

Requires

Bulk data downloads

USPTO.gov account + MFA

ODP API key

USPTO.gov account + verified and linked ID.me

The API key path requires identity verification through ID.me, which collects an SSN and biometric data. That is a real cost for a side project, and it buys the wrong data: the ODP API serves patent application file-wrapper records (titles, abstracts, CPC codes), not granted claim text.

Bulk downloads need only the account, and they include full granted claims. Claims are what a prior-art search actually reads.

Do not reach for PatentsView as an alternative. It migrated into ODP on 2026-03-20, its search.patentsview.org API is being retired, previously issued keys do not work against ODP, and there is no announced replacement date.

API routes, if you do go that way

Base URL https://api.uspto.gov/api/v1. Routes confirmed by probing without credentials: 401 means the route exists and needs a key. There are two distinct 403s, which matters when debugging. Body "Missing Authentication Token" means the route does not exist. Body "Forbidden" means the route is fine and your key was rejected.

Real: /patent/applications/search, /patent/applications/{id}, .../meta-data, .../continuity, .../transactions, .../associated-documents, /datasets/products/search.

Not real, despite appearing in older docs: /patent/applications/{id}/documents, /patent/grants/search, /bulk-data/products. The legacy Developer Hub at developer.uspto.gov was decommissioned 2026-06-05, so most tutorials point at a dead host.

Results

Ten invention descriptions, qwen2.5:7b throughout, full pipeline per case (python evals/run_eval.py).

cases run          10/10
findings drafted   42
findings grounded  19
grounding rate     45%
hit rate           90%
term coverage      88%

Case

Drafted

Grounded

Rate

soil-moisture-pinn

4

4

100%

rag-retrieval

5

4

80%

solar-tracker

5

3

60%

pill-dispenser

2

1

50%

cold-chain

5

2

40%

leak-detector

5

2

40%

handwriting-tablet

4

1

25%

shift-scheduling

5

1

20%

hearing-aid

5

1

20%

bike-lock

2

0

0%

The 45% is a rejection rate, not an error rate. The model drafted 42 findings; 23 did not survive verification and none of those reached a memo. Without that stage all 42 would have shipped as prior art. A number closer to 100% would mean the verifier was rubber-stamping, which is what the first three iterations of this project did.

Hit rate 90% — nine of ten cases returned at least one defensible finding.

The spread tracks the corpus, not the code. The strong cases (soil moisture at 100%, RAG at 80%) are ones where 2025 grants genuinely contain close prior art in standard vocabulary. The weak ones are not: shift-scheduling is a web application rather than a patented device, and bike-lock is a consumer product thinly represented in a single grant year. The verifier declined to manufacture findings that were not there, which is the intended behavior.

What this measurement does not tell you

The rejection rate shows the verifier is filtering, not that it is filtering correctly. A verifier that rejected everything would score 100% here and be worthless. Establishing that the rejections were right needs one of two things, and neither is done yet:

  • Known-item retrieval. Take N patents from the corpus, write each invention description from that patent's own claim, and measure whether the agent recovers it. Ground truth is known by construction, so the result is a hard recall number with no labeling and no judgement call.

  • Verifier confusion matrix. Hand-label the 42 drafted findings as relevant or not, then report the verifier's precision and false-negative rate. Converts "it rejected 55%" into "it rejected 55%, and was right about X% of them."

The first measures retrieval; the second measures the verification stage. Both are stronger claims than anything in the table above.

Future work: a larger model

Everything here ran on qwen2.5:7b. qwen2.5:14b is also free and open-weight, needs about 9 GB, and fits in 16 GB of RAM.

The expected benefit is a higher grounding rate through a smaller denominator. The 7b drafts up to five findings per case whether or not five exist, and the surplus is what verification throws away. A larger model should propose fewer speculative findings, so more of what it drafts survives.

Worth stating plainly: that is a hypothesis, not a measured result. It has not been run, and the number is not reported here until it is.

Known false positive

The example memo cites US 12327263, an AI smart-grid patent, as "moderately relevant" to an irrigation invention. It is not relevant, and the verifier passed it. Relevance judgement catches obvious mismatches and misses subtle ones. Provenance is airtight; relevance is not.

Stated limitations

The corpus is 2025 grants only — about 379k patents, 5.5M claims. Real prior-art search spans decades. This is a scope choice, not an oversight: adding years is one g_claims_YYYY download each (~350 MB) plus a re-ingest. The agent is required to call get_corpus_stats before drawing conclusions, and the memo footer states the range, so an empty result reads as "not in this corpus" rather than "does not exist."

Only exemplary claims are indexed. USPTO flags one representative claim per patent; indexing those rather than all ~14 claims cuts the index by an order of magnitude and keeps the claim that describes the invention. All claims are stored and retrievable once a patent is found.

Retrieval is lexical, not semantic. SQLite FTS5 with Porter stemming. It will miss a patent that describes the same idea in entirely different vocabulary. Mitigated by having the planning stage generate several phrasings, not solved.

A 7B is not a patent attorney. It drafts roughly twice as many findings as survive verification (see Results). A frontier model would draft fewer bad ones to begin with. The architecture is what transfers; the verification stage exists precisely because the drafting model is not trustworthy on its own.

Relevance judgement is the weak link. Provenance is guaranteed by construction. Whether a genuine patent is genuinely relevant is still a model call, and it gets that wrong sometimes in both directions.

Implementation notes

mcp >= 2.0 renamed FastMCP to MCPServer, and Tool.inputSchema to input_schema. The decorator and run() APIs are otherwise unchanged.

The agent loop is hand-rolled. Ollama has no managed tool runner, so draft_memo implements the loop directly: call, execute requested tools through MCP, append results, repeat to a turn cap. MCP is model-agnostic, so switching backends never touched the tool layer.

Tool results are truncated for the model but kept whole for verification. A 7B degrades badly past a few thousand tokens of context, so the model sees 6k characters per result while the verifier gets the full text. Verification would otherwise fail findings for being unable to see their own evidence.

Unruled findings default to ungrounded. A local model sometimes returns fewer verdicts than findings. Anything the verifier did not rule on is treated as failed, which is the safe direction.

Schema-constrained decoding, then validation anyway. Ollama's format parameter enforces JSON Schema during decoding, which guarantees parseable output but not sensible field contents. Pydantic validation and a retry still run.

Scope

Public USPTO bulk data only, downloaded through the documented portal with an account. No scraping.

This is a triage aid that tells an attorney what is worth reading. It is not a patentability opinion and does not attempt to be one.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Patent search, USPTO data, patent landscape & pgvector prior-art search for agents.

  • Autonomous research agent that pays every source it cites in USDC on Arc via x402 micropayments.

  • Verify citations in AI text: fetches each cited source, returns verdicts with evidence quotes.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hworku24/priorart-triage-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server