FastAPI 写一个最小检索接口:先规定证据返回格式
检索接口不能只返回 answer 字符串,至少要返回 query_id、hits、权限判断、证据位置、拒答原因和审计字段。
FastAPI 写一个最小检索接口:先规定证据返回格式
检索接口不能只返回 answer 字符串,至少要返回 query_id、hits、权限判断、证据位置、拒答原因和审计字段。
文章属于行业研究与技术科普,不替代项目设计、合规审查或招投标技术文件;引用时应保留来源、标题和原文地址。
FastAPI 写一个最小检索接口:先规定证据返回格式

一个可运营的检索接口,首先是契约清楚,其次才是模型聪明。
只返回自然语言答案,会让权限、证据、拒答和审计都无处落脚。
从现场问题开始
前端调用检索接口后拿到一段回答,但无法知道用了哪些片段、哪些被权限过滤、为什么有些问题拒答,也无法把用户行为写入审计。
请求里要带身份和场景
query、user_id、role_ids、dept_id、purpose、limit 这些字段不是累赘。它们决定权限过滤、利用记录和后续审计。
响应里要返回证据列表
hits 应包含 archive_id、component_id、page_no、snippet、score、open_state 和 source_url。模型回答只是其中一层,不是全部结果。
拒答是正常状态
证据不足、权限不足、问题越界、系统错误应有不同 refusal_reason。把所有失败都包装成空答案,会误导用户,也不利于验收。
日志字段要提前设计
query_id、trace_id、filtered_count、returned_count、model_version、retriever_version 和 elapsed_ms 应进入日志。出了问题才能复测同一路径。
可复刻实验片段
from pydantic import BaseModel
from fastapi import FastAPI
class SearchRequest(BaseModel):
query: str
user_id: str
role_ids: list[str]
dept_id: str
purpose: str
limit: int = 5
class EvidenceHit(BaseModel):
archive_id: str
component_id: str
page_no: int
snippet: str
score: float
open_state: str
source_url: str
class SearchResponse(BaseModel):
query_id: str
answer: str | None
hits: list[EvidenceHit]
refusal_reason: str | None = None
app = FastAPI()
接口合同要先于模型回答
一个更稳的判断是先问三件事:系统到底处理了哪些对象,对象状态由谁确认,出现异常后有没有证据能回到原始材料。围绕 FastAPI 检索接口证据返回格式,最需要提前写清的对象包括:query_id、user_id、filters、hits、snippet、source_url、page_no、open_state、trace_id 和 review_required。这些对象不一定都要做成复杂系统,但必须在数据结构、接口、表格或日志里有位置。
常见误区是先返回一段文本,后面再补结构。它看起来节省时间,实际上会把风险推迟到更难处理的阶段。到那时,开发人员已经按旧字段写完接口,业务人员已经按旧口径组织材料,运维人员只能面对一堆没有上下文的截图和文件夹。
失败响应不是异常边角料
| 核查问题 | 应该看到什么 | 危险信号 |
|---|---|---|
| 对象是否清楚 | query_id、user_id、filters、hits、snippet、source_url、page_no、open_state、trace_id 和 review_required 有明确字段或清单 | 只写总量、截图或功能名称 |
| 规则是否前置 | 权限、状态、质量或异常规则进入处理链路 | 先处理,出问题后再人工解释 |
| 证据是否可回跳 | Pydantic response schema、失败响应格式、接口测试样例和日志 trace 约定 能指向原始材料或日志 | 只能打开首页、报表或演示截图 |
| 责任是否可交接 | 后端确认 schema,前端确认展示字段,档案人员确认 hits 是否满足复核,安全人员确认权限字段 | 只有开发人员知道怎么判断 |
这张表不需要写进所有对外方案,但项目内部必须有人拿它逐项核。尤其是涉及高水平数字档案馆、数字档案室或智能应用的内容,不能把 AI、机器人、检索、展示当成替代基础能力的捷径。它们最多是增强层,基础仍然是归档、检测、资源、权限、安全、长期保存和人工复核。
trace_id 让一次检索能被复盘
很多项目只保留成功样本,这是后期返工的来源。FastAPI 检索接口证据返回格式 至少要准备一组反例:权限不允许、状态不完整、对象不存在、日志缺失、校验失败、人工复核未通过。反例用于确认系统边界,避免错误路径被当作正常能力。
验收或自测时,可以把指标写得更硬一些:任何回答必须带 hits;空结果、权限不足、证据不足要有不同错误码;trace_id 能回到服务端日志。这类指标比“功能正常”“界面友好”更有价值,因为它能让项目团队知道问题出在数据、流程、接口、模型还是人员复核。
反例还应该进入后续运维。新增一批数据、调整一次规则、替换一个组件、升级一个模型,都要用旧反例复测。只要旧反例开始通过错误路径,说明系统边界已经被改坏。这个动作看似笨,但能避免很多演示顺利、上线失控的问题。
证据列表决定前端能不能讲清来源
FastAPI 检索接口证据返回格式 最后要回到真实工作分工。后端确认 schema,前端确认展示字段,档案人员确认 hits 是否满足复核,安全人员确认权限字段。如果一件事只能由写代码的人解释,说明它还没有进入组织能力;如果一件事只能由业务人员口头确认,说明它还没有进入系统证据;如果一件事只能靠验收前补材料,说明平时运行没有沉淀。
领至科技在这类项目里更愿意先把证据链拉直,再讨论智能化程度。因为智能能力越强,越需要解释它依据了什么、排除了什么、谁复核过、出了错如何回滚。没有这些边界,所谓效率提升很容易变成风险加速。
response schema 要让前端不用猜
检索接口如果只返回 answer,前端就只能展示一段文本。等业务要求展示来源、页码、权限、拒答原因和人工复核入口时,接口会不断返工。FastAPI 这类框架的价值,正是可以先把 response schema 写清。
class SearchHit(BaseModel):
archive_id: str
page_no: int
snippet: str
source_url: str
open_state: Literal["open", "controlled"]
score: float
class SearchResponse(BaseModel):
query_id: str
answer: str | None
hits: list[SearchHit]
review_required: bool
trace_id: str
这个结构让前端、日志和测试都围绕同一份合同工作。模型回答可以为空,但 hits 不能随意消失;需要人工复核时,字段要明确告诉前端。
拒答也要有业务语义
检索项目里,拒答不是失败。证据不足、用户无权限、问题超出范围、系统异常,应使用不同状态码或业务码。否则用户只看到“无法回答”,运维也不知道该修数据、改权限还是查服务。
| code | 含义 | 后续动作 |
|---|---|---|
| evidence_not_found | 没有可引用证据 | 补资料或调整检索 |
| permission_denied | 用户无权访问 | 走授权或利用审批 |
| out_of_scope | 问题不在资料范围 | 提示用户换问法 |
| service_unavailable | 组件异常 | 进入运维工单 |
这类拒答码比一段礼貌话术更重要。它能让系统把失败转成可处理事件。
trace_id 是运维生命线
一次 AI 检索会经过前端、API、权限、检索引擎、向量库、模型和日志系统。没有 trace_id,出错后只能靠时间和用户描述猜。接口应该从入口生成 trace_id,并在每个组件日志里传递。
验收时可以故意问一个证据不足的问题,再用 trace_id 查完整链路:请求参数、权限过滤、召回数量、模型输入、模型输出、最终拒答。能查到这条链路,说明接口合同不仅服务前端,也服务审计和运维。
接口测试要覆盖四类请求
检索接口至少要测四类请求:正常有证据、正常无证据、用户无权限、服务异常。很多项目只测第一类,所以演示顺利;一到真实使用,拒答、越权和异常就没有统一处理。
FastAPI 可以用测试客户端把这些情况固化下来。每次改 schema、改检索逻辑、改权限过滤,都跑同一批测试。接口合同一旦稳定,前端、模型和日志都能少返工。
一个最小测试清单可以这样写:
| 用例 | 输入条件 | 期望结果 |
|---|---|---|
| evidence_ok | 用户有权限,资料存在 | 返回 answer、hits、trace_id |
| evidence_empty | 用户有权限,但资料不足 | answer 为空,code 为 evidence_not_found |
| permission_denied | 资料存在,但用户无权 | hits 为空,code 为 permission_denied |
| backend_timeout | 检索组件超时 | 返回 service_unavailable,并记录 trace_id |
这四个用例能防止接口只服务“开心路径”。真实项目里,拒答和异常占很大比例,接口如果不提前定义,前端会自己拼提示,模型会自己猜边界,日志也会失去统一口径。
项目里最容易漏掉的一页纸
这页纸的价值,是让不同角色在同一张桌上说同一种话。档案人员看到的是业务口径,开发人员看到的是字段和接口,运维人员看到的是日志和恢复,项目负责人看到的是风险和优先级。只要四类人还在各说各话,系统就很难真正稳定。
实际推进时,可以把这页纸做成三段。第一段写“本次处理的对象是什么”,不要用笼统名词。第二段写“哪些情况不能自动通过”,把失败分支列出来。第三段写“验收或复盘时看什么证据”,明确日志、表格、截图、原文、工单或报告的位置。
如果一篇文章读完以后,项目组能多出这样一页纸,它就不是泛泛观点。它会进入需求评审、接口联调、验收准备和运维交接。领至科技后续做数字档案馆、数字档案室、AI 检索、机器人和开源社区内容,也会尽量把观点压到这种能被复用的材料上。
判断成熟度,看它能不能被别人接手
一个能力是否成熟,不看演示人员讲得多熟,而看换一个人能不能接手。换一个档案人员,是否知道怎么抽查;换一个开发人员,是否知道字段从哪里来;换一个运维人员,是否知道异常去哪查;换一个负责人,是否知道下一笔预算该投在哪里。
接手能力来自文档、数据和日志。只有文档没有数据,系统会变成纸面合规;只有数据没有日志,异常无法复盘;只有日志没有业务解释,项目负责人看不懂风险。三者合在一起,才算进入可运营状态。
response schema 要让前端不用猜
检索接口只返回 answer 时,前端只能展示一段文本。等业务要求展示来源、页码、权限、拒答原因和人工复核入口,接口会不断返工。FastAPI 的优势在于可以先把 response schema 写清,让前端、日志和测试都围绕同一份合同工作。
class SearchHit(BaseModel):
archive_id: str
component_id: str
page_no: int
snippet: str
source_url: str
open_state: Literal["open", "controlled"]
score: float
class SearchResponse(BaseModel):
query_id: str
answer: str | None
hits: list[SearchHit]
refusal_reason: str | None
review_required: bool
trace_id: str
模型回答可以为空,hits 不能随意消失。需要人工复核时,字段要明确告诉前端。验收时可以故意问一个证据不足的问题,再用 trace_id 查完整链路:请求参数、权限过滤、召回数量、模型输入、模型输出、最终拒答。能查到这条链路,说明接口合同服务的不只是页面,也服务审计和运维。
测试样例要固定在仓库里
接口合同写完后,至少把四组样例固定下来:有证据、有证据但无权限、无证据、检索服务异常。样例可以先用假数据,不必等真实向量库准备完。每次修改接口、索引字段或权限逻辑,都用同一批样例跑一遍。
这一步能提前发现很多小问题,比如 page_no 为空、source_url 拼错、refusal_reason 混用、filtered_count 没写日志。它们单看都不大,到了验收现场会直接影响复核效率。
接口文档里还应写清字段兼容策略。新增字段可以向后兼容,删除字段要有版本说明。前端、移动端、第三方系统都可能依赖旧字段,检索接口越早稳定,后续协同越省力。
如果系统要对接多个前端,还可以把示例响应放进接口文档。示例比文字说明更能减少误解,尤其是空结果、拒答和人工复核这些分支。
接口版本要写进响应头或日志
检索接口一旦被多个前端和脚本调用,就会出现版本兼容问题。可以在响应头或日志里记录 api_version、schema_version 和 retriever_version。出问题时,先看版本,再查数据。
版本字段很小,却能减少大量争论。用户说“昨天还能查到”,团队可以用版本记录判断是接口变化、索引变化,还是权限规则变化。