lawgent
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Lawgent — an open-source legal AI agent. Grounded, multi-jurisdiction (CN · US · EU · ET · ICAO), citation-verified: every pinpoint round-tripped against the primary source.
⚖️ Lawgent
An open-source legal AI agent — grounded, multi-jurisdiction, citation-verified.
Plans the matter, fans out specialist sub-agents, pulls primary law from official sources,
round-trips every quotation against the document it came from, and returns a filed-ready
memo, redline, workbook, or deck.
[!NOTE]
A little side project. I built this for my girlfriend, a practising lawyer, to take
the grind out of the research-and-citation part of the job. It is open-sourced in case it
is useful to anyone else in the same position — a solo practitioner, an in-house team of
one, or anyone who has to be right about the law in more than one country. It stands on a
lot of other people's work — Anthropic's legal skills, their Chinese-law adaptation, and
plenty more in acknowledgements. It is a personal project, not a
product: expect rough edges, and read the known gaps before you
rely on it.
[!IMPORTANT]
Not legal advice. This harness assists with legal workflows. Every output must be
reviewed by qualified counsel before it is relied on for advice, filings, negotiations,
or operations. Citation verification is a safety net, not a substitute for a lawyer.
Table of contents
- What it is
- What it looks like
- Architecture
- Quickstart
- Multi-nation: one harness, many legal systems
- Multi-area: skills and domain packs
- The tool surface
- The grounding stack
- Runtime internals
- Web UI and HTTP API
- Configuration
- Repository layout
- Testing
- Status and known gaps
- Acknowledgements
What it is
Most "legal AI" is a chat box in front of a general model: it answers fluently, invents
article numbers, and knows one legal system. This repository is the other thing — the
harness around the model that makes legal output checkable.
Three commitments shape every design decision:
1. Primary sources, not recollection. Statutes, regulations, and judgments are fetched
live from official databases — 国家法律法规数据库, PKULaw, eCFR, the Federal Register,
GovInfo, CourtListener, EUR-Lex, ECAA, CAAC — through in-process connectors and MCP
servers. Model memory is allowed only when it is tagged as such.
2. Every claim carries a pinpoint, and every pinpoint is verified. Answers end with a## Sources / ## 资料来源 block in which each entry names a 条款 / article / section / §.
A grounding pass re-fetches the cited documents and round-trips the quoted language against
them; citations that do not survive are flagged, not quietly kept.
3. Jurisdiction is a first-class axis. The source hierarchy, citation style, output
language, and visible tool set all change when the jurisdiction changes. PRC law is the
default frame (法律 → 行政法规 → 部门规章 → 规范性文件 → 司法解释 → 指导案例); US and EU
are first-class comparative systems; ET and ICAO ship with the aviation pack.
Around those commitments sit a planner that sizes the work to the question, a sub-agent
runtime that keeps each specialist's context clean, durable project memory for matters
that outlive a single chat, a document layer that writes real DOCX tracked changes and
native PPTX shapes, and a per-turn cost ledger.
What it looks like
Real runs against live sources, captured from the web UI. (In-product branding is
法务助手 / Legal Helper — Lawgent is the project name.)
The answer. A comparative CN/US question. The chip above the answer reports the run:
elapsed time, number of specialist tasks, tool calls, and distinct sources. Note what the
comparison table does in the CAAC column — it says pinpoint unavailable instead of
inventing an article number, because no official CCAR-121 text made it into the record.

Tool use, live. While the run works, every connector call appears as its own card with
running / completed / error state — eCFR, GovInfo, PKULaw, the local corpus, hosted web
search. Nothing about the retrieval is hidden from the reviewer.

Sources with pinpoints. Each authority carries its pinpoint (14 CFR §§ 212.2, 212.9(a),49 U.S.C. §§ 44709(a)–(c)) and a note on what it is and is not good for. Entry [10] is the
one that matters: the harness would rather publish pinpoint unavailable than a plausible
citation it could not verify.

Settings and the cost ledger. Switch provider, model, and reasoning effort mid-matter;
the month-to-date token and cost ledger is broken out per model, priced with each provider's
own cache-billing semantics.

Architecture
flowchart TB
U["User · CLI · Web UI · HTTP API"] --> P["Planner<br/>routes and sizes the matter"]
P --> C{"Complexity<br/>dial"}
C -->|simple| O["Orchestrator<br/>run_skill(name, task)"]
C -->|standard / complex| W["Workflow plan<br/>N specialist tasks + dependencies"]
W --> O
PM[("Project memory<br/>brief · typed memory · rolling summary")] -.-> O
O --> A["Specialist sub-agents · forked context<br/>15 skills — review-contract · compliance-check · litigation-analysis · cite-check · …"]
A --> G
subgraph G["Grounded retrieval — tools filtered by jurisdiction + active packs"]
direction LR
CN["CN<br/>PKULaw<br/>flk.npc.gov.cn"] ~~~ US["US<br/>eCFR · FR<br/>GovInfo · CourtListener"] ~~~ EU["EU<br/>EUR-Lex<br/>EASA"] ~~~ ET["ET / ICAO<br/>ECAA · CAAC · FAA"] ~~~ RAG["Local RAG<br/>bge-m3 + Qdrant<br/>+ rerank"] ~~~ WEB["Hosted<br/>web_search<br/>web_fetch"]
end
G --> QC["Citation QC<br/>extract → validate → round-trip quotes → provenance tags"]
QC --> OUT["Deliverable<br/>memo · redline DOCX · XLSX grid · PPTX · PDF"]
OUT -.-> LED[("Usage ledger<br/>tokens + cost per turn")]
Both providers see the same tool surface: connectors, MCP-discovered tools, RAG,
citation tools, and document writers are all registered as plain function tools. No
provider-native MCP path, no Claude-only or GPT-only branch in the CLI, server, or skills.
Quickstart
# 1. Environment (Python + Node toolchains live side by side)
conda create -n llm python=3.11 && conda activate llm
pip install -r requirements.txt
npm install # web UI + mermaid CLI for flowchart rendering
# 2. Keys — at minimum one provider key
cp .env.example .env # ANTHROPIC_API_KEY / OPENAI_API_KEY / PKULAW_API_TOKEN / …
# 3. Ask it something
python -m legal_helper run --task "Review this NDA for PRC-law exposure" \
--input examples/general/sample_commercial_nda.md
# 4. Or use it interactively
python -m legal_helper chat # streaming REPL
python -m legal_helper serve --port 8010 # FastAPI + React UI
Switch legal systems and domains per invocation:
python -m legal_helper run --jurisdiction US --task "Does 14 CFR 121 permit this wet lease?"
python -m legal_helper run --domain-pack aviation \
--task "Review this dry-lease against the playbook" \
--input examples/aviation/sample_aircraft_lease.md
Switch providers with one environment variable — everything else is identical:
MODEL_PROVIDER=anthropic python -m legal_helper chat
MODEL_PROVIDER=openai python -m legal_helper chat
API keys and what unlocks what
| Variable | Unlocks | Required? |
|---|---|---|
ANTHROPIC_API_KEY |
Claude provider path, hosted web search/fetch | one provider key required |
OPENAI_API_KEY |
GPT provider path, hosted web search/fetch | one provider key required |
PKULAW_API_TOKEN |
The nine PKULaw MCP sub-services (primary PRC lookup) | optional — falls back to flk.npc.gov.cn |
GOVINFO_API_KEY |
GovInfo (US Code, CFR, bills, CRS) | optional |
COURTLISTENER_API_TOKEN |
Higher CourtListener rate limits | optional |
No key is ever read from the repository: they come from .env or the process environment.
Multi-nation: one harness, many legal systems
Jurisdiction is not a prompt hint — it is a runtime filter. filter_for(jurisdictions, packs) in legal_helper/connectors/__init__.py decides which connectors a sub-agent can
even see, so a PRC question is never answered out of the Federal Register by accident, and
an EU question surfaces EUR-Lex and EASA rather than eCFR.
| Jurisdiction | Primary-source stack | Tools the agent gets |
|---|---|---|
| 🇨🇳 CN (default) | PKULaw (statutes, 司法案例, 检察文书, 律所文章), 国家法律法规数据库 | pkulaw_* (9 MCP sub-services), flk_npc_search, flk_npc_fetch, caac_local_search/fetch |
| 🇺🇸 US | eCFR, Federal Register, GovInfo, CourtListener | ecfr_search/fetch, federal_register_search/fetch, govinfo_search/fetch, courtlistener_search/fetch, faa_title14_search, drs_search |
| 🇪🇺 EU | EUR-Lex (CELEX + SPARQL), EASA | eurlex_search/fetch, easa_ad_search/fetch, easa_ear_index |
| 🇪🇹 ET | Federal Negarit Gazeta mirror, ECAA | ethiopia_law_search/fetch, ecaa_index, ecaa_fetch |
| ✈️ ICAO | Chicago Convention + annexes, ICAO docs, Cape Town / IDERA | local RAG collections icao_doc, aviation_treaties |
| 🇬🇧 UK / 🇭🇰 HK | Configurable jurisdiction codes; served today by hosted web search + fetch with grounding | web_search, web_fetch, fetch_url_to_artifact |
Comparative work is the normal case. Ask "compare CCAR-121 and 14 CFR Part 121 wet-lease
approval" and the planner opens one research task per legal system, each with its own
connectors, then an integration task that reconciles them into a single memo — with the PRC
and US pinpoints kept separate and separately verified.
Three more things travel with the jurisdiction:
- Citation style —
gb_t_7714(PRC),bluebook(US),oscola(UK), converted bylegal_helper/citations/format.py. - Language — user-facing output follows the user's language; primary sources are quoted
in the original with a translation added when the languages differ. Internal reasoning is
free to run in whichever language fits the material. - Source hierarchy — encoded in
legal_helper/playbook/general_playbook.md, so a PRC
answer ranks 法律 above 部门规章 above 规范性文件 without being told each time.
Multi-area: skills and domain packs
15 practice skills
Each skill is a progressive-disclosure bundle: a SKILL.md under 100 lines (the manifest a
sub-agent starts with), references/ prose it pulls only when needed, resources/ output
templates, and scripts/ deterministic helpers. Context stays small until depth is actually
required.
| Skill | What it does |
|---|---|
review-contract |
Clause-by-clause review against a configurable playbook, with deviations, business impact, and redlines |
triage-nda |
Fast NDA classification — GREEN (sign) / YELLOW (counsel) / RED (full review) |
draft-agreement |
Drafts bilingual PRC-first agreements and litigation documents from deal terms |
docx-redline |
Native Word tracked changes (w:ins/w:del) plus anchored margin comments |
compliance-check |
Statutes, approvals, filings, and reporting duties for a proposed action, product, or data flow |
legal-risk-assessment |
Severity × likelihood matrix with escalation triggers |
litigation-analysis |
案情分析 / 争议焦点归纳 — claims and defenses mapped to legal elements with verified authority |
legal-response |
Structured replies to regulators, enforcement letters, and counterparty demands |
meeting-briefing |
Negotiation and authority-meeting preparation packs |
brief |
Daily scan, topic research, or incident briefing |
vendor-check |
Third-party diligence — standing, sanctions screening, certificates, ESG |
signature-request |
Multi-party closing and execution packages with a signature matrix |
tabular-review |
Document grids — one row per document, one column per question, pinpoint cite in every cell |
cite-check |
Round-trips every citation and quotation against its primary source |
flowchart |
Process maps and decision trees as editable PPTX shapes or standalone images |
Domain packs
A domain pack layers subject-matter depth on top of the generic skills without forking
them. A pack is a pack.yaml (jurisdictions, allowed connectors, RAG collections), aplaybook.md, references/, and overlays/<skill>.md deltas merged into the skill manifest
when the pack is active.
legal_helper/domains/aviation/
├── pack.yaml # CN · US · EU · ET · ICAO, connectors, RAG collections
├── playbook.md
├── references/ethiopia.md
└── overlays/ # 13 per-skill deltas
├── review-contract.md # → lease/purchase clause taxonomy, Cape Town, IDERA
├── compliance-check.md # → CAAC / FAA / EASA / ECAA approval chains
└── …
Aviation is the populated pack today: CAAC / MOT / FAA / EASA / ICAO, Cape Town
Convention and IDERA, sales and leasing, charter and wet-lease, MRO / Part-145, hull and
liability insurance, accident investigation, ITAR / EAR, CORSIA. Everything aviation-specific
lives inside that directory — the core stays domain-agnostic, and an anatomy test enforces it.
Adding a pack (healthcare, fintech, employment, data protection) means creating a directory
with the same four pieces; it is auto-discovered on the next process start. No core code
changes.
The tool surface
Around 80 in-process function tools, plus the nine PKULaw MCP services and the hosted
web search / fetch pair. They are assembled per turn from the active skill, jurisdictions,
and packs — never all at once, so provider tool schemas stay small.
| Tool | Purpose |
|---|---|
legal_source_search |
Jurisdiction-aware fan-out across the connector layer |
ecfr_search · ecfr_fetch |
US Code of Federal Regulations, current and historical |
federal_register_search · federal_register_fetch |
Rules, proposed rules, notices |
govinfo_search · govinfo_fetch |
US Code, CFR, bills, committee and CRS material |
courtlistener_search · courtlistener_fetch |
US case law and dockets |
eurlex_search · eurlex_fetch |
EU law by CELEX / title / SPARQL |
flk_npc_search · flk_npc_fetch |
国家法律法规数据库 (free PRC fallback) |
pkulaw_* (9 MCP services) |
PRC statutes by 条号, semantic statute and case search, 案号 recognition, 法条 traceback, citation validation, doc linking, NL search |
ethiopia_law_search/fetch · ecaa_index/fetch |
Ethiopian proclamations and ECAA regulations |
faa_title14_search · drs_search · easa_ad_* · caac_local_* |
Aviation regulators (pack-gated) |
retrieve_legal |
Local RAG over ingested corpora |
web_search · web_fetch · fetch_url_to_artifact |
Hosted search, hosted fetch, and pinned-to-disk fetch |
| Tool | Purpose |
|---|---|
extract_citations_tool |
eyecite (US) + PRC regex (案号, 法释, 国函, 国办发, 部令, 《…》第X条) |
validate_citations_tool |
Structural and existence checks per jurisdiction |
quote_roundtrip_tool |
Does the quoted language actually appear in the source? |
ground_answer_tool |
Full pass: find cites → fetch each source → round-trip every quote → grounding score |
provenance_audit_tool |
Enforces provenance tags ([PKULaw], [CourtListener], [model knowledge — verify], [settled — last confirmed YYYY-MM-DD]) |
cite_check_report_tool |
Reviewer-facing verdict table |
verification_log_append_tool |
Append-only audit trail of what was checked, when, against what |
| Family | Tools |
|---|---|
| Read / inspect | read_document, inspect_docx, inspect_pdf, inspect_xlsx, inspect_xlsx_range, inspect_pptx, extract_pdf_tables, extract_clauses |
| Write | write_docx, write_pdf, write_xlsx, write_pptx, write_pptx_from_html (lay slides out in HTML/CSS, measured in headless Chromium and mapped onto native PowerPoint shapes/text/tables), read_deck_stylesheet |
| Edit in place | edit_docx_text, edit_xlsx_cells, edit_xlsx_cells_checked (guarded, with protected ranges), edit_pptx_text, copy_xlsx_sheet, reshape_docx/xlsx/pptx |
| Prove the edit | diff_xlsx, render_docx_pages, render_xlsx_pages, render_pptx_slides, render_pdf_pages, view_image — the render_* tools return the rendered pages as images, so the model inspects its own output instead of trusting a path |
| PDF surgery | merge_pdfs, split_pdf, rotate_pdf_pages |
| Diagrams | render_flowchart_image, native PPTX flowchart shapes |
| Redlining | Real w:ins / w:del revision marks plus w:commentRangeStart-anchored comments — the file a partner can accept or reject in Word |
| Tool | Purpose |
|---|---|
run_skill |
Dispatch a task to a specialist sub-agent with a forked context |
list_skill_sections · read_skill_section · list_skill_references · read_skill_reference |
Progressive disclosure of skill depth |
read_playbook_section |
Pull the relevant playbook section only |
project_memory_write · project_memory_search · project_brief_read |
Durable cross-chat matter memory |
list_format_recipes · read_format_recipe |
House formatting rules for deliverables |
The grounding stack
Citation discipline is layered, because a single check catches a single failure mode.
| Layer | Failure it catches | Implementation |
|---|---|---|
| Extraction | — | eyecite + reporters-db for US; regex for PRC 案号 / 法释 / statute pinpoints; EU CELEX |
| Structural validation | Malformed or impossible citations | citations/validate.py, per-jurisdiction |
| Existence check | Cites to documents that do not exist | Connector / PKULaw round-trip; PRC cites stay could_not_check until a real lookup promotes them to verified |
| Quote round-trip | Real source, invented quotation | citations/groundedness.py — fetch the source, match the quoted language |
| Support check | Real source, real quote, wrong proposition | cite-check Mode 2 — does the passage support the claim it is cited for? |
| Provenance tags | Model recall presented as retrieval | citations/provenance.py — retrieval tags vs. verify tags |
| Pinpoint audit | "See the Company Law" with no article | audit_citations requires a pinpoint per entry, or an explicit pinpoint unavailable / 无法获得具体条款 |
The workflow runs citation QC as its own task when the answer's stakes warrant it, and the
web UI renders the verdict as a reviewer-facing card: what was checked, what passed, what
needs a human.
Runtime internals
Planner and complexity dial. A routing pass classifies each request simple /standard / complex and emits a WorkflowPlan of agent tasks with dependencies. Simple
questions get one agent and a direct answer; a comparative multi-jurisdiction memo gets a
fan-out sized to the question plus an integration task. Fan-out is derived from the matter,
never a fixed count.
Sub-agent runtime. Each specialist starts from a compact manifest in a forked context
and pulls depth on demand. Only its final block returns to the orchestrator; its internal
turns stay in the JSONL log under the same parent run_id. The orchestrator, not the
specialist, authors the user-facing answer.
Provider parity. One Provider protocol over Anthropic (claude-opus-5, alsoclaude-opus-4-8 / 4-7; fast tier claude-haiku-4-5) and OpenAI (gpt-5.6-terra or-sol, fast tier gpt-5.6-luna). Streaming, tool calls, reasoning traces, prompt caching,
and hosted web search all reach parity, and tests/test_provider_parity.py asserts the tool
surfaces match.
Two kinds of memory. context.py keeps a single long chat inside the window with a
CJK-aware token estimator and budgeted recent-message selection — so a 40-page pasted
contract is not truncated to 3 000 characters. projects.py keeps many chats on one matter
coherent: a hand-authored brief re-injected every turn, append-only typed memory (fact /
decision / task / open_question / glossary / artifact / risk / source) ranked by salience, a
rolling summary, and compaction.
Both rolling summaries are maintained by delta, not rewrite. Re-summarizing a summary
erodes a little detail every pass — a failure mode named context collapse — so instead the
fast model is asked only for what changed, as + Section | … / - Section | … lines against
a sectioned playbook (Goals / Facts / Conclusions / Artifacts / Open), and a deterministic
merge applies them. Bullets accumulate and disappear only when the model says so or a section
hits its cap, so loss is bounded and visible rather than emergent. A delta that will not parse
leaves the prior playbook untouched.
Compaction is tier-aware. When to fold older turns into the summary depends on the model,
not on a universal constant: strong models do better carrying the raw transcript further and
isolating side quests in sub-agents, while cheaper models degrade sooner and benefit from
summarizing early. Frontier models compact at 75 % of the window, the fast tier at 55 %.
Setting chat_compaction_threshold to any positive value pins it for every model instead.
Documents: inject one, navigate many. A single attached document is pasted into the
prompt — that is where full context genuinely wins. A corpus of them is not: past a total
size threshold each text attachment is replaced by a navigable outline (its headings and
第X条 / Article N labels, drawn from the same patterns the RAG chunker uses) plus theread_document call that opens it, so the model reads the parts it needs instead of
everything. LEGAL_HELPER_INLINE_CORPUS_BYTES=0 turns this off.
Local RAG. bge-m3 embeddings and an embedded Qdrant under state/qdrant/, with a
legal-aware hierarchical chunker that respects 第X条 / Article X / § X / clause-numbering
boundaries. Retrieval is hybrid dense + sparse with RRF fusion, over-retrieves 3–4×, then
re-ranks locally with a cross-encoder. Nothing in the retrieval hot path leaves the machine.
python -m legal_helper.rag.ingest --collection general --seed
python -m legal_helper.rag.ingest --collection aviation --seed
python -m legal_helper.rag.ingest --collection user --path ~/my_legal_docs/
Cost ledger. Every provider turn — both providers, both the run and streaming paths —
appends normalized token usage to state/usage/usage-YYYY-MM.jsonl, priced with
per-provider billing semantics: Anthropic bills cache reads and writes as separate buckets;
OpenAI's cached tokens are a discounted subset of input, and a prompt over 272 K input tokens
is billed at 2× input / 1.5× output for the whole request. That long-context surcharge is
easy to overlook and expensive to ignore — it is modelled, and long_context_requests in the
month summary says how often it fired. GET /api/usage serves the summary and the UI renders
it. Recording never breaks a turn.
Trajectory failure profile. Outcome-level checks cannot see a correct answer reached
through a broken chain. Every workflow failure or degradation event is tagged with a subclass
across two layers — substantive (the law is wrong) and procedural (the agent misbehaved)
— and citations/trajectory.py aggregates a per-run profile, including a
right-answer-wrong-reason flag for runs that produced clean output on a chain that broke.
Observability. JSONL event and turn records under logs/ capture provider, model,
messages, tool calls, usage, latency, and the parent/sub run_id tree — enough to replay
why an answer said what it said.
Web UI and HTTP API
python -m legal_helper serve --port 8010 starts FastAPI plus a React 19 + Vite front end
with SSE streaming.
- Live trajectory — plan, per-agent tasks, tool calls, and reasoning as they happen
- Sources rail — every retrieved authority with its provenance tag and pinpoint
- Citation audit card — the cite-check verdict inline with the answer
- Artifacts — generated DOCX / PDF / XLSX / PPTX attached to the message that produced them
- Projects — brief, memory, and compaction controls for long-running matters
- Cost center — month-to-date tokens and spend by model
- Uploads — drop a contract or PDF into the chat and cite it
Selected endpoints:
POST /api/chats GET /api/chats/{id}
POST /api/chats/{id}/messages/stream GET /api/chats/{id}/runs/{run_id}/stream
POST /api/chats/{id}/upload GET /api/chats/{id}/artifacts/{artifact_id}
GET /api/projects POST /api/projects/{id}/memory
POST /api/projects/{id}/compact GET /api/usage
GET /api/skills GET /domains GET /jurisdictions
Configuration
config.yaml holds runtime defaults; .env and the process environment override it.
provider: openai # anthropic | openai
default_jurisdiction: CN # CN | US | EU | UK | HK | ET | ICAO
secondary_jurisdictions: [US, EU] # comparative systems surfaced when relevant
default_language: zh # user-facing default; follows the user at runtime
citation_style: gb_t_7714 # gb_t_7714 | bluebook | oscola
active_domain_packs: [] # e.g. [aviation]
anthropic_model: claude-opus-5
openai_model: gpt-5.6-terra # never the bare "gpt-5.6" alias — it routes to Sol
max_iterations: 12 # tool-loop ceiling per agent
max_concurrent_agents: auto
max_tokens: 32000 # per-turn output ceiling
chat_context_token_budget: 16000
chat_compaction_threshold: 0.0 # 0 = derive from the model tier; any value pins it
enable_web_search: true
enable_web_fetch: true
Three environment switches have no config.yaml equivalent:
| Variable | Default | Effect |
|---|---|---|
LEGAL_HELPER_MCP_ENV |
llm |
conda env that bare stdio MCP commands resolve against |
LEGAL_HELPER_INLINE_CORPUS_BYTES |
400000 |
total attachment text above which documents are outlined instead of inlined; 0 disables |
LEGAL_HELPER_ANTHROPIC_CLEAR_TOOLS |
unset | opt in to Anthropic server-side tool-result clearing (1, or a token count to set the trigger) |
LEGAL_HELPER_ANTHROPIC_CLEAR_TOOLS is off by default on purpose. Clearing old tool results
roughly halves peak tokens on long runs, but it invalidates the cached prompt prefix each
time it fires — and this provider leans on prompt caching — so on a cache-heavy workload it
can cost more than it saves. Measure before turning it on.
.mcp.json is the MCP registry — remote HTTP servers (the nine PKULaw sub-services), stdio
servers (EUR-Lex), and the in-tree ccar_aviation server. Activation is gated by
jurisdiction and active packs. US federal and case-law sources are direct-API connectors
in legal_helper/connectors/, not MCP servers — nothing to install for them.
stdio entries name commands bare (python, npx) and registry.resolve_command() resolves
them: python becomes the running interpreter, everything else is looked up on PATH and
then in the conda bin directories. A bare command left to the OS resolves against whatever
PATH the launcher happened to have, which is how a server ends up running under a system
interpreter with no mcp package and dying as an opaque CONNECTION_CLOSED. Unresolvable
commands are now reported by name instead.
Repository layout
The project is Lawgent; the Python package it ships is still legal_helper (the name it
was born with), so every import, CLI invocation, and env var below reads legal_helper.
legal_helper/
├── agent.py # orchestrator + skill sub-agents
├── workflow.py # planner-driven executor for the chat surface
├── server.py # FastAPI + SSE streaming + chat/project/usage API
├── cli.py # run · chat · serve · project
├── config.py # settings, jurisdiction codes, citation styles
├── context.py # within-chat token budgeting + compaction
├── projects.py # cross-chat brief, typed memory, rolling summary
├── usage.py # monthly token/cost ledger
├── skills/ # 15 generic, domain-agnostic skills
├── domains/aviation/ # domain pack: pack.yaml, playbook, overlays, references
├── playbook/ # PRC-first global defaults + format recipes
├── connectors/ # in-process primary-source connectors (CN/US/EU/ET/aviation)
├── mcp/ # external MCP boundary: registry, manager, adapter, servers
├── citations/ # extract · validate · round-trip · ground · provenance · audit
├── rag/ # chunker · embeddings · store · hybrid retrieve · rerank · ingest
├── documents/ # docx/pdf/xlsx/pptx writers, redline, diagrams
└── tools/ # function-tool registry assembled per turn
src/ # React 19 + Vite web UI
tests/ # anatomy, parity, connectors, citations, RAG, workflow suites
examples/ # fictitious sample contracts and filings
scripts/ # corpus fetchers, expiry flagging, monthly harness review
Testing
pytest -q # full suite
pytest -q tests/test_skill_anatomy.py # SKILL.md < 100 lines, valid frontmatter,
# no domain strings leaking into the core
pytest -q tests/test_provider_parity.py # identical tool surface across providers
pytest -q tests/test_citations.py # extraction + validation
pytest -q tests/test_grounding.py # quote round-trip
pytest -q tests/test_rag_hybrid.py # hybrid retrieval + rerank
LEGAL_HELPER_LIVE=1 pytest tests/test_integration_live.py -m live # Anthropic
LEGAL_HELPER_LIVE_OPENAI=1 pytest tests/test_integration_live_openai.py -m live # OpenAI
The anatomy test is the architectural guardrail: it fails if a skill grows past its manifest
budget, if frontmatter is malformed, or if domain-specific vocabulary leaks out of a pack and
into the generic core.
Status and known gaps
Honest notes, so nobody discovers these the hard way:
- Aviation is the only populated domain pack. The pack mechanism is general; the content
is not yet. - UK and HK are configured jurisdictions without dedicated connectors. They currently run
on hosted web search plus grounding rather than an official-database connector. - Two aviation connectors are disabled in the registry —
ccar_search/ccar_fetch(the
CAAC search endpoint moved behind a JS-rendered form) anddrs_fetch(drs.faa.gov is
SPA-only). Both are commented inconnectors/__init__.pywith the reason and the workaround. - PRC citations stay
could_not_checkuntil a live PKULaw round-trip. That is deliberate:
structural plausibility is not verification. - Corpus binaries are not committed.
legal_helper/rag/corpus/*/manifest.yamland the
fetch scripts are; the PDFs are pulled on demand viascripts/fetch_aviation_corpus.sh. - Flowchart rendering needs Chrome for
@mermaid-js/mermaid-cli.
Acknowledgements
Almost nothing here is a new idea. The legal-skill structure, the citation-verification
discipline, the retrieval stack, and the chat UI all came from other people's work; what
this repo contributes is the assembly, the PRC-first framing, and the connector layer.
Directly adapted
| Project | License | What this repo took from it |
|---|---|---|
| anthropics/claude-for-legal | Apache-2.0 | The backbone. Provenance-tag taxonomy (citations/provenance.py), the groundedness framing (citations/groundedness.py), the verification-log format (citations/log.py), and the "say so when the source is unavailable" discipline |
| CSlawyer1985/claude-for-legal-ZH | Apache-2.0 | 《Claude for Legal — 中国法版本》. The reference for carrying those skills into 中国法 practice — PRC source hierarchy, 法条 pinpointing, and what a Chinese-practice skill actually needs to say |
| sboghossian/master-claude-for-legal | see repo | The citation-verifier skill — the two-mode (exists / supports) verification procedure and the verification-report format, extended here with PRC equivalents |
| anthropics/skills | see repo | The Agent Skills SKILL.md format itself, and the document-skill script pattern behind documents/writers/scripts/ |
| anthropics/claude-cookbooks | MIT | Prompting patterns distilled into skills/flowchart/references/cookbook_patterns.md |
| vercel/ai-elements + shadcn/ui | see repos | The entire chat UI component layer — conversation, message, reasoning, plan, task, tool, sources, inline-citation |
Shaped the design
Surveyed rather than copied, but each changed a decision here:
- harveyai/harvey-labs — the Legal Agent Benchmark; the reason grounding is an automated pass rather than a prompt instruction
- lawve-ai/awesome-legal-skills — the curated map of the legal Agent Skills ecosystem
- anylegal-ai/anylegal-oss — a multi-LLM harness that loads
SKILL.mdunmodified; a useful check on the provider-parity contract - Open-Source-Legal/OpenContracts — documents as a programmable citation graph
- iflow-mcp/lawmotion-ai-vibe-lawyering — 法律人的 AI 工具箱; its 北大法宝 MCP notes helped wire the PKULaw path
- blevinstein/aviation-mcp — prior art for MCP servers over aviation APIs
- legislation/legislation-mcp-ts — The National Archives' official legislation.gov.uk MCP; where a real UK connector should start
- Evaluation work that set the bar for "is this actually right": CSHaitao/LegalAgentBench, SKYLENAGE-AI/PLawBench,
XMUDeepLIT/LegalGraphRAG, and
lexpath-project/LexPath
Built on
Citations & case law — eyecite,
reporters-db, and
CourtListener from the Free Law Project.
Retrieval — Qdrant, BAAI bge-m3,
sentence-transformers.
Tool boundary — the Model Context Protocol.
Documents — python-docx, python-pptx, openpyxl, XlsxWriter, pptxgenjs,
anydoc, markitdown, WeasyPrint, PyMuPDF, pypdf,
Playwright, mermaid-cli.
App — FastAPI, React, Vite, Tailwind, streamdown, lucide.
Legal data — PKULaw / 北大法宝, 国家法律法规数据库, eCFR, the Federal Register, GovInfo,
CourtListener, EUR-Lex, EASA, CAAC, ECAA and the Federal Negarit Gazeta. Their terms apply
to anything retrieved through them; see NOTICE.
If your project belongs on this list and is missing, that is an oversight rather than a
judgement — open an issue and I will add it.
Lawgent · MIT licensed · Built for practitioners who have to be right.
Not legal advice. Have qualified counsel review every output before relying on it.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi