Iris 拿到手后,第一个要回答的问题通常不是“答得好不好”,而是“链路上每一环是不是真的接上了”。建议按进程、模型、检索工具、串联这四步依次验证:前一步没有可见信号,就先不要进入下一步。否则最后只得到一条空回答或一条没有引用的回答,很难判断问题卡在模型、索引还是串联逻辑上。
判断 Iris 是否跑通,先看四个可见信号是否齐全:健康检查返回 200 且响应体带明确的存活字段、模型对固定提示返回预期文本、检索工具单独调用能返回有结构的候选片段、最终回答里的引用能与原文逐句对应。四步中任意一步缺信号,建议先在原地修,不要跳到串联调试。端口、路由、字段名以实际部署的配置和日志为准,本文给的是检查顺序与通用骨架,不是某一版的固定接口。
记录服务启动后暴露的端口与健康检查返回
这一步只解决一个问题:进程是在运行,还是启动后打印几行日志就退出了。建议先用前台方式启动,让日志直接打到终端,不要一上来就丢到后台或交给进程管理器。
# 启动骨架,参数名以实际 CLI 为准
python -m iris.server `--config` ./configs/dev.yaml `--host` 0.0.0.0 `--port` 8000
启动日志里重点看三件事:实际监听的地址与端口、加载的配置文件路径、模型 provider 与检索后端各自初始化到哪一步。日志停在“loading config”或“connecting vector store”之后就退出,属于启动阶段失败,跟模型能力无关,先回去查配置和依赖服务。
# 健康检查请求
curl -i http://127.0.0.1:8000/healthz
- 连接被拒或 curl 返回空,说明端口没有真正监听,回到启动日志找原因。
- 返回 200 且响应体是 JSON、含明确的存活字段(如 status、version、message),可以进入下一步。
- 返回 200 但响应体为空或只是一个静态字符串,说明健康检查只做保活、不检查下游依赖,后面几步要看得更细。
- 健康检查路由不一定是 /healthz,可以先看
curl -s http://127.0.0.1:8000/openapi.json里有没有 health 相关路径,或者直接查配置文件。
确认模型客户端能返回一段固定文本
把模型这一环先排除掉。方法是给一个不依赖检索、答案固定的提示,看返回内容是否稳定命中。提示里直接约束字数,便于肉眼判断是否被额外包装。
curl -s -X POST http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer $IRIS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"default","messages":[{"role":"user","content":"只回复四个字:链路正常"}],"max_tokens":16,"temperature":0}'
观察返回体里 choices 的第一条内容是否就是“链路正常”,model 字段是否是预期的模型名,usage 里是否有 token 计数。有 token 计数通常说明请求确实打到了模型,而不是被本地兜底逻辑接住。
失败时要区分类型,不要都当成“模型挂了”:
- 401 或 403:鉴权问题,检查 token、API Key、base_url 是否配套,最容易出现在换了环境但复用了旧配置的时候。
- 408、504 或 curl 报 timeout:网络不通或上游超时,可以先确认出网、DNS 和超时配置。
- 400:模型名或参数名不对,对照配置文件里的模型标识核对。
- 200 但 content 为空:优先排查 max_tokens 是否设得过小,以及是否被内容策略拦截。
确认检索工具能被单独调用并返回候选片段
检索和模型必须分开验证。直接把检索结果塞进提示里混合调试,出了问题分不清是谁的错。先单独调用检索工具,输入一条语料里确实存在的查询。
curl -s -X POST http://127.0.0.1:8000/tools/retrieve \
-H "Content-Type: application/json" \
-d '{"query":"报销流程需要哪些材料","top_k":5}'
返回结构通常是一个数组,或 {"hits":[...]} 这类包装,每个候选片段一般包含:片段正文、来源标识(文件名、路径或 URL)、片段序号、相似度分数。字段名以实际部署为准,建议先原样 curl 一次看清 key 名,再写后面的断言或脚本。
- 候选数量是否接近 top_k。明显偏少,通常是索引为空、只加载了部分文档,或过滤条件过严。
- 片段正文字段是否有实际内容,不能全是空白或只有标题。
- 来源标识能否定位到原始文件,后面核对引用要依赖这个字段。
- 相似度分数是否随查询变化,固定不变说明可能在走缓存或假数据。
返回空数组时,先查索引构建日志和文档数量,不要先改相似度阈值。
让模型基于检索结果作答并核对引用
前三步都看到信号后,再发起一条完整的问答请求,问题要选答案明确写在语料里的那种,方便逐句核对。
curl -s -X POST http://127.0.0.1:8000/v1/chat \
-H "Content-Type: application/json" \
-d '{"session_id":"check-001","message":"报销流程需要哪些材料?","cite":true}'
返回体里除了回答正文,应当有引用列表。核对引用建议照这几条做,不靠“读着像”:
- 把引用片段里的关键句拿到原始文档里搜索,逐句能对上,而不是意思相近。
- 引用条目的来源和片段序号,应当能对应到上一步检索返回的同一批候选,而不是凭空生成的标识。
- 回答里出现的具体条目、材料名、办理环节,在引用片段中都能找到对应句子。
- 做一个反向对照:关闭引用开关,或临时清空检索返回,再问同样的问题。如果两种情况下回答几乎一致,说明引用只是装饰,模型并没有真正使用检索结果,需要回头检查拼装提示的那段逻辑。
把这一条问答的请求、返回原文、对应的检索候选一起留存,作为后续换环境时的对照基线。
把四步整理成一份可重复执行的检查顺序
换机器、升级版本、调整索引之后,按同一顺序复验,任何一步不通过就停在那一步,不要往下跑。下面这张表可以直接抄成自己的检查记录表。
| 步骤 | 命令 | 观察点 | 通过标准 |
|---|---|---|---|
| 进程 | curl -i /healthz | 监听端口、配置路径、健康检查响应体 | 返回 200 且响应体含存活字段,进程持续存活 |
| 模型 | 最小 chat/completions 调用 | 返回正文、model 名、usage | 正文命中固定文本,无 401/超时 |
| 检索工具 | POST /tools/retrieve | 候选数量、片段正文、来源标识、分数 | 数量接近 top_k,正文非空且能定位原文件 |
| 串联 | POST /v1/chat 带 cite | 回答正文、引用列表、与检索候选的对应关系 | 引用片段与原文逐句对应,关闭检索后回答发生变化 |
每次复验都建议保留四份材料:启动日志片段、模型调用返回 JSON、检索候选列表、带引用的问答记录。四份材料齐全,后面再出问题可以直接对比,判断是模型侧、索引侧还是串联侧的变化,而不是重新从头排查一遍。