PriorArt Triage Agent
Enables prior-art searches on granted US patent claims. Given an invention description, it returns a grounded memo with citations, verifying each finding against the source and quarantining unverifiable claims.
README
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.
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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。