{"data":{"kind":"file","path":"README.md","version_id":"m30s1amyk45p1fnu7d1hyiq4","entry":{"name":"README.md","path":"README.md","is_directory":false,"size":9628,"modified_at":"2026-07-06T00:50:33","content_hash":"f962ace67dd5e6fa46f86d6186036a2d1a3543c44afa999969b000942a226aaa"},"entries":[],"content":"# patent-office-action-v1\n\nRespond to a USPTO office action, against a simulated examiner — the v0 of the\nHarvey × Prime Intellect patent-prosecution environment, built on `verifiers` v1\n(`Taskset` + user simulator + MCP tools + judge), extending the `patent_search`\nfamily's retrieval surface with an interactive, multi-turn matter.\n\n**The loop** (one rollout = one matter):\n\n1. The agent is the **applicant's representative**. The task prompt is the role\n   plus the first non-final office action (rejections citing prior art), with the\n   application's claims and specification attached.\n2. The agent investigates the cited art with the **patents retrieval tools**\n   (`patents_search`, `patents_get_metadata`, `patents_get_abstract`,\n   `patents_view_sections`, `patents_read_section` — the `patent_search` surface\n   under v1's MCP prefixing) and files a response — claim amendments and\n   arguments — inside `<response></response>` tags.\n3. The **examiner** (a `vf.User` simulator, its own server process) adjudicates\n   each still-open rejection with one narrow LLM call — **withdraw / maintain /\n   narrow** — grounded in the rejection text, the full cited art, the original\n   claims, the specification, and the *previous* response (so restated arguments\n   don't earn withdrawal). It then either terminates (all withdrawn, or the round\n   budget is spent — the final response is captured either way) or sends **one\n   focused follow-up point** about a single still-open rejection.\n4. The finished rollout is scored by an **LLM judge** that grades the final\n   response against the task's **golden reference** (the real applicant response\n   that overcame the office action), guided by the task's rubric of atomic\n   `{id, point, pass_if}` criteria.\n\n**Reward and metrics.** The training reward is **graded** — the fraction of\nrubric criteria passed (a binary all-pass reward on a hard task gives almost no\nearly gradient). The strict LAB-style **all-pass** headline is recorded as a\nmetric, alongside per-criterion verdicts (`judge/<id>`), `criteria_pass_rate`,\n`rounds_to_resolution`, `all_rejections_withdrawn`, `examiner_withdraw_rate`\n(sanity that the examiner neither rubber-stamps nor stonewalls), and `malformed`.\n\n**Consistency by construction.** The examiner is a deterministic state machine\nover the rejections plus a narrow per-turn LLM judgment — not a persona that can\ndrift or over-guide — and its reasoning stays inside its server process; only\nthe rendered follow-up office action ever reaches the applicant.\n\n## Layout\n\n```\npatent_office_action_v1/\n├── pyproject.toml\n├── README.md\n├── configs/eval.toml                 # example eval config\n├── patent_office_action_v1/\n│   ├── __init__.py                   # exports PatentOfficeActionTaskset (plugin contract)\n│   ├── taskset.py                    # Task / Config / Taskset, final judge, rewards, metrics, stops\n│   ├── state.py                      # OfficeActionState: the per-rejection state machine + flags\n│   ├── models.py                     # typed task-record + prior-art models, JSONL loaders\n│   ├── parsing.py                    # <response> extraction\n│   ├── prompts.py                    # applicant / adjudicator / follow-up / final-judge prompts\n│   ├── data/tasks.jsonl              # the dummy task records (generated)\n│   ├── data/prior_art.jsonl          # the searchable corpus: cited art + distractors (generated)\n│   └── servers/\n│       ├── patents.py                # PatentCorpusToolset (vf.Toolset, MCP)\n│       └── examiner.py               # ExaminerUser (vf.User) + its adjudicator judge\n├── scripts/make_dummy_data.py        # regenerates data/ (the stand-in for the USPTO scraper)\n└── tests/                            # unit tests + a keyless full-stack e2e rollout\n```\n\n## The data contract\n\nOne JSONL row = one task record (see `models.OfficeActionRecord`), exactly the\nshape the future USPTO file-wrapper scraper will emit:\n\n| field | contents |\n| --- | --- |\n| `application` | claims + specification (+ bibliographic header fields) |\n| `office_action_text` | the full non-final office action, authentic form: PTOL-326-style header, Office Action Summary, DETAILED ACTION, AIA notice, verbatim 35 U.S.C. 102(a)(1)/103/112(b) statutory quotations, canonical rejection form language with pinpoint cites, Conclusion |\n| `rejections[]` | `{id, type, claims, cited_refs, text, status}`; each `text` block appears verbatim inside `office_action_text`; `status ∈ open/withdrawn/maintained/narrowed` is the examiner's state machine |\n| `prior_art_refs` | doc ids the action cites — all resolvable in the corpus (which also holds uncited distractors, so the agent must actually search) |\n| `reference` | the golden applicant response that overcame the action: a 37 CFR 1.121 amendment (complete claim listing with status identifiers and markings) + REMARKS traversing every rejection with support cites |\n| `criteria[]` | atomic rubric rows `{id, point, pass_if}` |\n| `max_rounds` | the examiner round budget (per task; overridable eval-wide) |\n\nPlain-text 1.121 markings (stated in the data, the applicant prompt, and the\noffice action's own reply guidance): additions between single underscores\n(`_added text_`), deletions in double brackets (`[[deleted text]]`).\n\nThe two packaged matters: a battery-thermal-management application (102(a)(1)\nanticipation over Okada, 103 over Okada + Steiner, and a 112(b) indefiniteness\nground) and an acoustic leak-detection application (102(a)(1) over Whitfield,\n103 over Whitfield + Marsh). Every quotation the golden references attribute to\na reference exists verbatim in that reference's corpus text, and every pinpoint\ncite in the office actions resolves — the tests enforce this.\n\nRegenerate (or point a real scraper at the same shape):\n\n```bash\npython scripts/make_dummy_data.py             # writes into the package data dir\npython scripts/make_dummy_data.py --out DIR   # or elsewhere; then --taskset.data-path\n```\n\n## Running\n\nInstall (alongside `verifiers`), then evaluate with any OpenAI-compatible\nendpoint. The examiner's adjudicator and the final judge default to the Prime\ninference endpoint (`PRIME_API_KEY`); every endpoint/model is a config field.\n\n```bash\nuv pip install -e .   # the dependency pins resolve a v1-capable verifiers from PyPI\n# (equivalently, track main directly:\n#  uv pip install \"verifiers @ git+https://github.com/PrimeIntellect-ai/verifiers\")\n\nuv run eval patent-office-action-v1 \\\n  -m openai/gpt-5-mini -n 2 -r 1 --max-turns 12 \\\n  --harness.id null \\\n  --taskset.judge.model openai/gpt-4.1-mini \\\n  --taskset.user.judge.model openai/gpt-4.1-mini\n```\n\nor from the example TOML: `uv run eval @ configs/eval.toml`. Useful knobs\n(every field of `OfficeActionConfig`, dotted): `--taskset.data-path`,\n`--taskset.max-rounds`, `--taskset.judge.*` (final judge),\n`--taskset.user.judge.*` (examiner adjudicator), `--taskset.tools.*` (corpus\nplacement — e.g. `--taskset.tools.shared true` once the corpus grows an index).\nGive the round-trip headroom: `--max-turns` of roughly\n`(max_rounds + 1) * (1 + expected tool calls per round)`.\n\nFor training, the same env drops into a prime-rl orchestrator block:\n\n```toml\n[[orchestrator.train.env]]\nname    = \"patent-office-action\"\ntaskset = { id = \"patent-office-action-v1\" }\nharness = { id = \"null\", runtime = { type = \"subprocess\" } }\n```\n\n## Tests\n\n```bash\nuv pip install -e \".[dev]\"\npython -m pytest tests -q               # everything (~15 s; no API keys needed)\npython -m pytest tests -q -m \"not e2e\"  # unit tests only (<1 s)\n```\n\nThe suite covers the data's form and cross-reference integrity, the parsing\ncontract, the retrieval tools, the examiner state machine (scripted\nadjudicator: all-withdrawn, partial + one-focused-point follow-ups,\nre-adjudication that sees the previous response, max-round exhaustion,\nmalformed handoffs, grounding fields), the judge prompt/verdict validation and\nevery reward/metric path — and one **keyless full-stack e2e**: a local fake\nOpenAI-compatible server plays applicant, adjudicator, and judge while the real\nframework runs the rollout (null harness, interception, the MCP patents server,\nthe examiner server with state sync, a genuine two-round exchange, `finalize`,\nand scoring).\n\n## v0 scope, and what comes next\n\nDeliberate v0 simplifications, in line with the design doc:\n\n* **Dummy data stands in for the scraper.** `make_dummy_data.py` emits the exact\n  record shape a USPTO Open Data / Patent Center file-wrapper pipeline will emit\n  (non-final action → applicant response → withdrawal cycles, golden reference =\n  the response the examiner's next action shows overcame the rejections).\n* **Keyword search stands in for the index.** The corpus is small, so\n  `patents_search` is a deterministic keyword scorer; swapping in the chroma\n  index used by `patent_search`/`wiki_search_v1` is a change to the toolset's\n  `setup`/`search` only.\n* **Hand-authored rubrics.** The packaged criteria were written by hand; at\n  scale the design doc's recipe applies — run a strong and a weak model on the\n  same office action, have a third model distill what the strong response did\n  that the weak one didn't into criteria, then refine against the USPTO record.\n* **Single judge, graded reward.** Multi-judge agreement, the open- vs\n  closed-book diagnostic, counterfactual task variants, solvability\n  `validate()`, and train/eval splits by filing date are the documented next\n  steps, not built here.\n","encoding":"utf-8","truncated":false,"total_bytes":9628},"status":null}