问题排查¶
按现象查原因。先确认一件事:CG_RAG 的 HTTP 200 不等于业务成功。 检索成功但生成失败时,HTTP 仍然是 200,失败信息在 body 里。
先跑这两条命令¶
# 服务活着吗?配置齐吗?
curl --noproxy '*' -sS http://127.0.0.1:8864/cg-rag/health
# 有哪些检索范围可用?
curl --noproxy '*' -sS http://127.0.0.1:8864/cg-rag/profiles
health 里要看的几项:
| 字段 | 期望 | 不对时说明 |
|---|---|---|
ok |
true |
服务整体不健康,看 config_issues |
config_issues |
[] |
有内容就是配置缺失,照着改 |
available_scopes |
含你要用的 scope | 语料或索引路径没配好 |
generation.configured |
true |
文本生成没配,rag / constrained-generate 会失败 |
vision.configured |
true |
视觉没配,只影响 vision/* |
按错误码查¶
generation_not_configured¶
生成端点没配。检索接口不受影响,但 rag、constrained-generate 用不了。
需要这四个环境变量:
export CG_RAG_GENERATION_ENDPOINT='https://example.com/v1/chat/completions'
export CG_RAG_GENERATION_MODEL='your-model'
export CG_RAG_API_KEY='server-side-secret'
export CG_RAG_LLM_PROVIDER='deepseek' # 或 poly / openai_compatible
改完重启服务,再用 health 确认 generation.configured 变成 true。
vision_not_configured¶
视觉上游没配。检索和文本生成都正常,只有 vision/* 会失败。
export CG_RAG_VISION_ENDPOINT='https://example.com/v1/chat/completions'
export CG_RAG_VISION_MODEL='your-vlm'
export CG_RAG_VISION_API_KEY='server-side-secret'
部署顺序
如果有上层应用依赖 CG_RAG 的视觉能力,先重启 CG_RAG 并确认 health 里 vision.configured=true,再重启上层应用。反过来会让上层在一段时间内对所有图片请求返回失败。
no_stable_article¶
检索到了候选,但模型没能稳定选出条文。这不是崩溃,是「这批候选里没有能确定回答该问题的条文」。
常见原因和处理:
- 问题太宽泛(「安全怎么做」)→ 问得具体一点。
- scope 选窄了 → 从
usual换成full。 topk太小 → 调大到 10~20,给模型更多候选。
generation_timeout / generation_request_failed¶
到上游模型的请求超时或失败。先确认上游本身可用:
curl -sS -o /dev/null -w '%{http_code}\n' \
-X POST "$CG_RAG_GENERATION_ENDPOINT" \
-H "Authorization: Bearer $CG_RAG_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"'"$CG_RAG_GENERATION_MODEL"'","messages":[{"role":"user","content":"ping"}],"max_tokens":1}'
如果上游正常而 CG_RAG 报超时,调大 CG_RAG_TIMEOUT_SECONDS(默认 120 秒),并把客户端超时同步调大。当前生效值见 health 的 generation.timeout_seconds。
generation_response_invalid¶
上游返回了内容,但不是能解析的形状——通常是模型没按要求输出 JSON。多见于把 CG_RAG_LLM_PROVIDER 配错(例如上游是 OpenAI-compatible 却配成 deepseek),导致 thinking 参数注入方式不匹配。
vision_upstream_error¶
视觉上游调用失败。图片过大、格式不支持、上游过载都会走到这里。先把图片压到 2 MB 以内、转成标准 JPEG/PNG 再试。
invalid_image¶
base64 解不开,或者不是支持的图片格式。检查:
data字段是纯 base64,不带data:image/jpeg;base64,前缀。mime_type和实际内容一致。
import base64
encoded = base64.b64encode(open("site.jpg", "rb").read()).decode("ascii")
payload = {"images": [{"data": encoded, "mime_type": "image/jpeg"}]}
invalid_request¶
请求体没通过校验。最常踩的两个坑:
include_debug和generation_enable_thinking必须是 JSON boolean。"true"、1、0都会被拒。retrieval_docs不能是空数组。
capacity.exceeded(HTTP 429)¶
并发超过服务端限流。检索和生成分别限流,所以只有一类会先满。
- 非流式请求:等待超时后返回 HTTP 429。
/rag/stream:如果响应已经开始,容量错误通过 SSE 的error+final表达,HTTP 状态仍是 200。
客户端应该退避重试,而不是立刻重发。
按现象查¶
第一次请求特别慢,之后就快了¶
正常。首次访问某个 scope 要加载索引。上线后先 warmup。
改了索引/语料,但结果还是旧的¶
检索缓存的 key 里带 profile_fingerprint,配置真的变了就不会命中旧缓存。如果结果没变,多半是配置没有真正生效——对比 health 里的 profile_fingerprint_summary 和你期望的值。
pred_indices 的序号对不上我的文档¶
pred_indices 是 1-based,对应 retrieval.retrieval_docs 的位置。取文档时要 docs[i - 1]。
SSE 卡住不动 / 收不到 token¶
按顺序排查:
- 请求头带了
Accept: text/event-stream吗? - 中间有没有反向代理做了缓冲?Nginx 需要
proxy_buffering off;。 - 受约束生成本来就不是真流——答案在服务端拼好之后才被切成几段重放,所以「很久没动静、然后一下子出完」是正常的。想看真正边生成边到达的输出,用视觉接口或自由生成。
视觉智能生成要一两分钟¶
正常。它会多轮检索法规再作答。要快就用 vision/answer(自由生成),代价是没有法规引用。
明明配了代理,请求却连不上本机服务¶
curl 会读 http_proxy / https_proxy 环境变量,把发往 127.0.0.1 的请求也送进代理。示例里的 --noproxy '*' 就是为此。Python 的 requests 同样受影响,可以:
还是不行¶
带上这三样去查日志,能省很多时间:
- 响应头里的
X-Request-ID(或 body 里的request_id)。 - 完整的请求体(去掉图片 base64)。
health的完整输出。
请求 ID 会串联服务端日志,可以直接定位到那一次调用。你也可以自己指定:
服务端不会在响应里返回 API key、完整本地路径或上游响应正文;日志同样做了脱敏。