如果你要在裁判文书数据上盖一个产品——类案检索、案件研判、合规尽调、法律 RAG 问答——最先要打通的,不是模型,而是把一份干净、可过滤、可回链的判决数据稳定地拉进你的服务。这篇是写给后端工程师的接入实战:鉴权怎么放、检索怎么调、分页和过滤怎么写、原文怎么回链、限流和重试怎么兜底,Python 与 Node.js 两种语言都给可直接改的骨架。文中的接口路径与字段名一律用占位,以你拿到的接口文档为准,本文教的是接入模式,不是某个固定端点。
一、接入前先想清楚三件事
动手写代码之前,先把这三个决定定下来,能省掉后面大半返工:
- 你要的是"检索"还是"全量"?多数产品要的是按需检索(给关键词+条件拿命中),而训练/统计场景要的是批量导出。两种走法不一样:检索走 API 实时调用,批量走数据集交付或带游标的分页拉取。先确认自己属于哪类。
- 过滤维度有哪些?裁判文书真正值钱的不是全文搜索,而是能按案由、法院层级、地域、年份、判决结果精确切片。接入前列出你产品需要的过滤维度,确认数据源是否有对应的结构化字段。
- 要不要回链原文?任何严肃的法律产品,检索结果都必须能回链到原始判决供核对。接入时就要把原文链接 / 稳定 ID 一起存下来,别等产品上线才发现摘要无法溯源。
二、鉴权:API Key 怎么放才安全
裁判文书 API 通常用 API Key 鉴权(放在请求头里)。两条铁律:
- 永远不要把 Key 硬编码进代码或前端。放环境变量 / 密钥管理服务,前端永远不直接持 Key——让你自己的后端做代理转发,Key 只存在服务端。
- 区分试用 Key 与生产 Key。先用试用 Key 把链路跑通,上生产再换正式额度的 Key,并对调用方加你自己的鉴权与限额。
下面所有示例里的 API_BASE 与具体路径(如 /search、/judgments/{id})都是占位示意。实际的 base 地址、路径、请求头名称、字段命名,以接口文档 / 控制台为准,请勿照搬本文占位直接上线。
三、Python 接入示例(requests)
一个最小可用的检索 + 取详情 + 回链的骨架。生产里建议换成 httpx 并加连接池,这里用 requests 求清晰:
# pip install requests
import os, time, requests
API_BASE = "https://tob.wenshucha.com/api/v1" # 占位,以接口文档为准
API_KEY = os.environ["WENSHUCHA_API_KEY"] # 放环境变量,别硬编码
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {API_KEY}"})
def search_judgments(keyword, *, cause=None, court_level=None,
year_from=None, year_to=None, page=1, size=20):
# 字段名以接口文档为准,这里示意常见的过滤维度
params = {
"q": keyword,
"cause": cause, # 案由
"court_level": court_level, # 法院层级
"year_from": year_from,
"year_to": year_to,
"page": page,
"page_size": size,
}
params = {k: v for k, v in params.items() if v is not None}
resp = request_with_retry("GET", f"{API_BASE}/search", params=params)
return resp.json()
def get_judgment(doc_id):
resp = request_with_retry("GET", f"{API_BASE}/judgments/{doc_id}")
return resp.json()
def request_with_retry(method, url, *, params=None, max_retry=4):
# 对 429 / 5xx 做指数退避;对 4xx(鉴权/参数)直接抛
for attempt in range(max_retry):
r = session.request(method, url, params=params, timeout=15)
if r.status_code == 429 or r.status_code >= 500:
wait = min(2 ** attempt, 30)
time.sleep(wait)
continue
r.raise_for_status()
return r
r.raise_for_status()
return r
if __name__ == "__main__":
data = search_judgments("民间借贷 利息", cause="民间借贷纠纷",
year_from=2022, year_to=2025, size=10)
for hit in data.get("items", []): # 列表字段名以文档为准
print(hit.get("case_no"), hit.get("court"), hit.get("source_url"))
# source_url = 回链原文,务必存下来供用户核对
四、Node.js 接入示例(fetch)
等价骨架,Node 18+ 自带 fetch。BFF / 实时检索服务这样写最顺:
// Node 18+ 自带 fetch;旧版本用 node-fetch / axios
const API_BASE = "https://tob.wenshucha.com/api/v1"; // 占位,以接口文档为准
const API_KEY = process.env.WENSHUCHA_API_KEY; // 放环境变量
async function requestWithRetry(path, { params = {}, maxRetry = 4 } = {}) {
const qs = new URLSearchParams(
Object.entries(params).filter(([, v]) => v != null)
).toString();
const url = `${API_BASE}${path}${qs ? "?" + qs : ""}`;
for (let attempt = 0; attempt < maxRetry; attempt++) {
const r = await fetch(url, {
headers: { Authorization: `Bearer ${API_KEY}` },
signal: AbortSignal.timeout(15000),
});
if (r.status === 429 || r.status >= 500) {
await new Promise(s => setTimeout(s, Math.min(2 ** attempt, 30) * 1000));
continue;
}
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
throw new Error("max retry exceeded");
}
async function searchJudgments(keyword, opts = {}) {
return requestWithRetry("/search", {
params: { q: keyword, cause: opts.cause, court_level: opts.courtLevel,
year_from: opts.yearFrom, year_to: opts.yearTo,
page: opts.page ?? 1, page_size: opts.size ?? 20 },
});
}
const data = await searchJudgments("民间借贷 利息", {
cause: "民间借贷纠纷", yearFrom: 2022, yearTo: 2025, size: 10,
});
for (const hit of data.items ?? []) {
console.log(hit.case_no, hit.court, hit.source_url); // 字段名以文档为准
}
五、分页与批量:别一次性拉爆
检索结果上千上万条时,要用分页/游标逐页拉,别试图一把梭。两种常见模式:
| 模式 | 怎么用 | 适合 |
|---|---|---|
| 页码分页 | page + page_size 逐页递增,直到返回空 | 结果量中等、要跳页的交互检索 |
| 游标分页 | 每次带上一页返回的 cursor / next_token,顺序拉到底 | 大批量顺序导出,深翻页更稳 |
批量拉取时务必控制并发 + 尊重限流:并发开 2-4 路足够,命中 429 就退避,别把自己 IP 打进限流黑名单。把已拉到的 doc_id 落库去重,断点续传靠它。
六、把结果接进你的产品:三种典型用法
1)接进检索 / 类案功能
最直接:用户输入 → 调 search → 渲染命中列表(带案由、法院、年份、回链)。要做"类案"而非"关键词匹配",检索侧的召回与重排是关键,展开见我们的 类案检索:语义向量 vs 关键词。
2)接进法律 RAG / 问答
让大模型回答前先检索真实判决,把命中片段作为上下文喂给模型,并要求每个结论都回链到检索到的原文。绝不能让模型凭记忆生成判例——那会编造不存在的案号和裁判要旨。这是 grounding,不是 prompt 技巧。
3)接进批处理 / 实证统计
按案由+年份批量拉结构化字段做计量分析。这类场景对"覆盖可表征 + 可固定快照"要求高,展开见 法学实证研究数据接入指南。
七、REST API 还是 MCP?一张表说清
同一份数据,两种接入形态,按谁来消费选:
| 维度 | REST API | MCP |
|---|---|---|
| 消费方 | 你自己的后端 / 批处理 / RAG 管线 | Claude、ChatGPT、Cursor 等大模型 Agent |
| 谁决定何时检索 | 你的代码显式调用 | 模型自己判断、可链式多轮检索 |
| 控流控错 | 你完全掌控(重试/限流/缓存) | 交给 MCP 客户端,集成成本低 |
| 典型节奏 | 生产集成的主力 | 先做原型快速验证价值 |
常见做法是先 MCP 跑原型、再 REST API 上生产。MCP 怎么接见 5 分钟接入文书查 MCP:Claude Desktop 实战。
八、上生产前的检查清单
- Key 在服务端环境变量里,前端不直接持 Key,日志不打印 Key?
- 429 / 5xx 有指数退避,4xx 直接报错不空转?超时设了没?
- 分页拉全了没有漏页?
doc_id落库去重、能断点续传? - 每条结果都存了回链原文,用户能核对到原始判决?
- 展示给用户的字段做了必要脱敏,符合合规要求?
- 字段名、过滤参数都对齐了接口文档,没照搬本文占位?
涉密 / 政企内网场景:如果你的产品要服务政府、国企或处理敏感案件,数据不能出域,可走私有化部署——把 1.7 亿+ 裁判文书与检索能力整套落到你的机房,内网调用同样的接口形态,详见 私有化方案。
九、常见问题
Q:接口字段我能照着这篇文章写死吗?
A:不能。本文所有路径与字段名都是占位示意,目的是讲清接入模式。实际命名、过滤参数、分页机制以你拿到的接口文档 / 控制台为准,先用试用 Key 跑通真实返回结构再写映射。
Q:检索响应慢 / 偶发超时怎么办?
A:先确认是否命中了限流(429);其次给高频查询加一层结果缓存(同样的关键词+过滤短时间内别重复打);批量场景降并发、加退避。生产建议把检索服务和模型推理拆开,互不阻塞。
Q:能先小范围试再付费扩量吗?
A:可以。建议先申请试用 Key,用一个真实场景把"检索→回链→展示/grounding"全链路跑通,确认数据覆盖与字段满足产品需要,再谈正式额度与价格。
申请裁判文书 API 试用 Key
提供你的产品场景与大致调用量,我们给试用 key + 接口文档,1 个工作日内反馈。先跑通再谈扩量。
📞 联系商务 Jack · 131 6872 7779或邮件 chenjiaxin@wenshucha.com · 查看 API / MCP 定价
相关阅读:5 分钟接入文书查 MCP · 类案检索:语义 vs 关键词 · 法律 AI 创业团队怎么选数据底座 · 私有化部署方案