Skip to content

Latest commit

 

History

History
616 lines (437 loc) · 24.7 KB

File metadata and controls

616 lines (437 loc) · 24.7 KB

raguide 运维 Runbook

什么时候看这份文档? 收到 Alertmanager/Prometheus 告警、前端出现异常 SSE error、eval gate 失败、或值班时需要快速定位故障根因时,从告警速查表找到对应场景直接跳转。

本 runbook 覆盖 raguide 生产(demo 量级 docker compose 单机部署)的 9 条告警规则、9 个故障场景及对应降级路径,是 infra/prometheus/rules.yml 的配套操作手册。


SLO 表

SLO 目标 对应告警
可用性 99% BackendDown
错误率 <5% HighErrorRate
检索 p95 <10s HighRetrievalP95

告警速查表

告警名 级别 含义 场景锚
BackendDown critical 后端 /metrics 不可达超过 1 分钟,SLO 可用性破坏 场景1
HighErrorRate critical 请求错误率 >5% 持续 5 分钟,最常见原因是 LLM Provider 上游异常 场景1
HighRetrievalP95 warning 检索 p95 延迟 >10s 持续 5 分钟,Qdrant 或 TEI 异常 场景2
TokenSpike warning token 消耗速率异常飙升(>2000 tokens/s 滑动均值),可能触发成本熔断 场景6
SafetyCapTriggered warning 工具循环安全帽 10 分钟内触发 >3 次,Agent 行为异常信号 场景7
CRAGFallbackSurge warning CRAG SearXNG 兜底激增(15 分钟 >5 次),检索质量劣化信号 场景2
LiveQualityDrop warning 线上 done_hook judge 加权分 <0.6 持续 15 分钟,输出质量漂移 场景7
CostBudget80 warning 本月成本已达预算 80%,需关注是否需要调整预算 场景6
CostBudgetExhausted critical 本月成本已超预算,服务已熔断,前端收到 BUDGET_EXHAUSTED SSE error 场景6

场景1 LLM Provider 挂

触发告警: BackendDown、HighErrorRate

检测信号

  • Prometheus alerts 页(localhost:9099/alerts)出现 HighErrorRate / BackendDown firing
  • 后端日志出现 LLMError / UPSTREAM_LLM_ERROR 关键字
  • 前端对话无响应或持续收到 error SSE

诊断命令

# 1. 检查后端健康端点
curl -s localhost:8000/healthz

# 2. 查看后端日志,过滤 LLM 错误
# 后端日志 = 运行 uvicorn 的终端;若用 nohup/重定向则:
grep -E "LLMError|UPSTREAM_LLM_ERROR|error" <你的日志文件>

# 3. 检查 Prometheus 错误率指标
curl -s 'localhost:9099/api/v1/query?query=sum(rate(raguide_trace_status_total{status="error"}[5m]))/sum(rate(raguide_trace_status_total[5m]))'

缓解动作

  1. 登录 admin「模型设置」面板(web 侧栏 →「模型设置」对话框,无独立路由),切换至备用 Provider(M7 已落地:AnthropicAdapter / ConfigurableOpenAIAdapter / DeepSeekAdapter 三选)
  2. 若 Provider 密钥过期,在 .env 更新对应 API Key 后重启后端:Ctrl-C 终止 uvicorn 进程,再执行 cd apps/backend && uv run uvicorn raguide_backend.main:app --reload --port 8000
  3. 若为短暂上游抖动,等待 FallbackChain 自动恢复(见降级路径)

既有降级路径

FallbackChainapps/backend/src/raguide_backend/llm/registry.py,M2a):Provider 列表按优先级排列,当前 adapter 连续失败时自动 fallover 至下一个 adapter,无需人工干预。FallbackChain 遵循 Chain of Responsibility 模式,错误不会上浮直至所有 adapter 均失败。

演练命令

# 演练场景1:将 .env 中的 LLM API Key 设为无效值 → 看告警 → 恢复
# 注意:仅在 dev 环境演练,勿在有真实流量时操作

# 模拟步骤:
# 1. 备份并临时替换 API Key
# 2. 重启后端: Ctrl-C 终止 uvicorn 进程后重新运行
#    cd apps/backend && uv run uvicorn raguide_backend.main:app --reload --port 8000
# 3. 发一条测试查询,观察 localhost:9099/alerts HighErrorRate 进 pending→firing
# 4. 后端终端输出应出现: ops.alert firing name=HighErrorRate (webhook 链路通)
# 5. 恢复 API Key 后重启后端

场景2 Qdrant 挂

触发告警: HighRetrievalP95、CRAGFallbackSurge

检测信号

  • 检索 p95 延迟突破 10s 告警
  • CRAG SearXNG 兜底频率激增(Qdrant dense 路失败→兜底 Web 搜索)
  • 前端查询响应明显变慢

诊断命令

# 1. 查看 Qdrant 容器日志
docker logs raguide-qdrant --tail 50

# 2. Qdrant 健康检查
curl -s localhost:6333/healthz

# 3. 检查容器状态
docker ps | grep raguide-qdrant

# 4. 检索延迟 p95 当前值
curl -s 'localhost:9099/api/v1/query?query=histogram_quantile(0.95,sum+by+(le)(rate(raguide_retrieval_latency_seconds_bucket[5m])))'

缓解动作

# 重启 Qdrant 容器
docker restart raguide-qdrant

# 等待健康检查通过(通常 10-30s)
until curl -sf localhost:6333/healthz; do sleep 2; done && echo "Qdrant ready"

既有降级路径

三路 fanout 容错(apps/backend/src/raguide_backend/retrieval/concurrent_fanout.py):dense(Qdrant)/ BM25(Tantivy)/ Image 三路并发,任意一路失败不阻塞其他两路结果。Qdrant 挂时 BM25 稀疏检索仍提供结果,RRF fusion 对缺失路自动降权。CRAG SearXNG 作最终兜底。

演练命令

# 演练场景2:停 Qdrant → 看告警 → 恢复
docker stop raguide-qdrant
# localhost:9099/alerts 等 HighRetrievalP95/CRAGFallbackSurge 进 pending→firing
# 后端日志应出现: ops.alert firing name=... (webhook 链路通)
docker start raguide-qdrant

场景3 Redis 挂

触发告警: 后端日志告警(无独立 Prometheus 规则;Redis 故障 fail-open 不产生错误率告警)

检测信号

  • 后端日志出现 cost_guard.add fail-opencost_guard.spend fail-open
  • 成本 Prometheus Gauge(raguide_cost_monthly_cny)归零或停止更新
  • 注意:Redis 挂时服务不中断(fail-open 设计),用户无感知

诊断命令

# 1. 测试 Redis 连通性
docker exec raguide-redis redis-cli ping

# 2. 查看 Redis 容器日志
docker logs raguide-redis --tail 50

# 3. 检查容器状态
docker ps | grep raguide-redis

# 4. 查看后端 fail-open 日志
# 后端日志 = 运行 uvicorn 的终端;若用 nohup/重定向则:
grep "cost_guard" <你的日志文件>

缓解动作

# 重启 Redis 容器
docker restart raguide-redis

# 验证恢复
docker exec raguide-redis redis-cli ping
# 预期输出: PONG

# 验证月成本 key(Redis 恢复后新写入开始,历史少记无法追回)
docker exec raguide-redis redis-cli GET "raguide:cost:monthly:$(date +%Y-%m)"

既有降级路径

CostGuard.add / CostGuard.monthly_spend 均以 fail-open 模式处理 Redis 异常(apps/backend/src/raguide_backend/billing/cost_guard.py):异常时仅 logger.warning,服务继续正常运行,计费暂停累计。Redis 连接超时 2s,不拖慢后端启动。

注意: Redis 挂期间的 LLM 调用成本不会被记录,恢复后月累计会偏低。如需准确计费,管理员可参考 Langfuse trace 中的 token 消耗手动补偿预算。

演练命令

# 演练场景3:停 Redis → 确认 fail-open 日志 → 恢复
docker stop raguide-redis
# 发一条聊天查询
# 后端日志应出现: cost_guard.add fail-open / cost_guard.spend fail-open
# 服务仍正常响应(fail-open 设计)
docker start raguide-redis

场景4 Tantivy 挂

触发告警: CRAGFallbackSurge(间接),检索结果变稀(无独立 Prometheus 规则)

检测信号

  • 后端日志出现 Tantivy 相关错误
  • 检索结果数量明显减少(BM25 稀疏路缺失,仅剩 dense/image)
  • CRAGFallbackSurge 告警(检索质量下降→兜底频率上升)

诊断命令

# 1. 查看 Tantivy 容器日志
docker logs raguide-tantivy --tail 50

# 2. 检查容器状态
docker ps | grep raguide-tantivy

缓解动作

# 重启 Tantivy 容器
docker restart raguide-tantivy

# 若重启无效(索引损坏),需重建索引:
# M6 经验:重置索引目录后通过 KB 管理页全量重摄入
# 1. 停容器
docker stop raguide-tantivy
# 2. 清空索引数据(谨慎!不可逆)
# docker run --rm -v raguide_tantivy_data:/data alpine rm -rf /data/*
# 3. 重启容器(自动初始化空索引)
docker start raguide-tantivy
# 4. 在 KB 管理页(前端 /kb)触发全量重摄入,或通过 API 批量重摄

既有降级路径

三路 fanout 容错(apps/backend/src/raguide_backend/retrieval/concurrent_fanout.py):Tantivy 挂时自动降级为 dense-only 检索,BM25 路失败不阻塞 Qdrant dense 和 Image 路结果。RRF fusion 对缺失路自动降权,结果质量下降但服务可用。

演练命令

# 演练场景4:停 Tantivy → 确认 dense-only 降级 → 恢复
docker stop raguide-tantivy
# 发一条检索查询,观察后端日志(BM25 路报错,但查询仍返回结果)
# CRAGFallbackSurge 可能进 pending(视检索结果质量)
docker start raguide-tantivy

场景5 TEI Reranker 挂

触发告警: HighRetrievalP95(间接,rerank 超时拉高 p95)

检测信号

  • 后端日志出现 TEI rerank timed out(10s 超时)
  • 检索 p95 延迟升高
  • 响应中 evidence 顺序回退至 RRF fusion 原始排序

诊断命令

# 1. 查看 TEI 容器日志
docker logs raguide-tei --tail 50

# 2. 检查容器状态
docker ps | grep raguide-tei

# 3. 过滤后端 rerank 相关日志
# 后端日志 = 运行 uvicorn 的终端;若用 nohup/重定向则:
grep -iE "rerank|tei" <你的日志文件>

缓解动作

# 重启 TEI 容器(CPU 版镜像,启动需 30-60s 加载模型权重)
docker restart raguide-tei

# 等待模型加载完成
until docker logs raguide-tei 2>&1 | grep -q "Ready"; do sleep 5; done && echo "TEI ready"

既有降级路径

Reranker fallback(apps/backend/src/raguide_backend/retrieval/post_retrieval/reranker.py):TEI 返回空或超时时,自动取 RRF fusion 原始顺序的 top-5 结果(finding-017,M2c)。重排失败不阻断整个检索响应,仅损失重排质量提升。

演练命令

# 演练场景5:停 TEI → 确认 fallback top-5 日志 → 恢复
docker stop raguide-tei
# 发一条需要检索的查询
# 后端日志应出现: Reranker: TEI returned empty; falling back to top-5 of fused order
docker start raguide-tei

场景6 成本熔断触发

触发告警: CostBudget80(预警)、CostBudgetExhausted(已熔断)、TokenSpike(上游异常飙升)

检测信号

  • CostBudget80 firing:本月累计成本已达预算 80%,需关注
  • CostBudgetExhausted firing:已超预算,所有新请求返回 BUDGET_EXHAUSTED SSE error
  • 前端对话框收到错误提示:「本月 LLM 预算已用尽,服务暂停。请联系管理员在「模型设置→预算」调整。」

诊断命令

# 1. 查看当前预算配置
curl -s -H "X-Admin-Token: $ADMIN_TOKEN" localhost:8000/v1/admin/budget-config

# 2. 查看本月 Redis 累计成本(月 key 格式: raguide:cost:monthly:YYYY-MM)
docker exec raguide-redis redis-cli GET "raguide:cost:monthly:$(date +%Y-%m)"

# 3. 检查 Prometheus 成本 Gauge
curl -s 'localhost:9099/api/v1/query?query=raguide_cost_monthly_cny'
curl -s 'localhost:9099/api/v1/query?query=raguide_cost_budget_cny'

# 4. 查看 token 消耗速率(TokenSpike 时)
curl -s 'localhost:9099/api/v1/query?query=sum(rate(raguide_llm_tokens_total[5m]))'

缓解动作

# 方案A: 调高月预算上限(PUT 即热生效,无需重启)
curl -s -X PUT \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"monthly_budget_cny": 200.0, "enabled": true}' \
  localhost:8000/v1/admin/budget-config

# 方案B: 临时关闭熔断(紧急时)
curl -s -X PUT \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"monthly_budget_cny": 0.0, "enabled": false}' \
  localhost:8000/v1/admin/budget-config

# 确认配置已生效(BudgetConfig 进程内缓存 PUT 路径同步更新,无需重启)
curl -s -H "X-Admin-Token: $ADMIN_TOKEN" localhost:8000/v1/admin/budget-config

既有降级路径

成本熔断本身是设计行为(apps/backend/src/raguide_backend/api/agent.py):超预算时返回单条 BUDGET_EXHAUSTED SSE error,零 LLM 调用,服务优雅拒绝。BudgetConfig 支持 PUT 热生效(apps/backend/src/raguide_backend/billing/budget_config.py),调整预算后立即恢复服务,无需重启。

演练命令

# 演练场景6:设极低预算 → 发查询观察 BUDGET_EXHAUSTED → 恢复
# 步骤1: 设预算为 0.01 CNY(必触发)
curl -s -X PUT \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"monthly_budget_cny": 0.01, "enabled": true}' \
  localhost:8000/v1/admin/budget-config

# 步骤2: 发一条聊天查询,前端应收到 BUDGET_EXHAUSTED error SSE
# 步骤3: localhost:9099/alerts 观察 CostBudgetExhausted 进 firing
# 步骤4: 恢复正常预算
curl -s -X PUT \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"monthly_budget_cny": 100.0, "enabled": true}' \
  localhost:8000/v1/admin/budget-config

场景7 eval gate 失败

触发告警: SafetyCapTriggered(行为异常)、LiveQualityDrop(质量漂移)

检测信号

  • eval CI 运行 pytest packages/evalpython tests/eval/run_gold_set.py 返回 GATE FAILED
  • Prometheus raguide_eval_dimension_score{dim="weighted"} < 0.6 持续 15 分钟
  • tests/eval/_reports/ 目录下最新报告显示加权分低于 baseline

诊断命令

# 1. 优先排除机器睡眠污染(最常见的假阳性)
# 检查报告中 case_errors 数量
ls -lt tests/eval/_reports/ | head -5
# 读最新报告,检查 case_errors > 0 和日志时间线是否有大断层

# 2. 若有污染迹象(case_errors > 0 / 时间断层),用 caffeinate 重跑
caffeinate -is python tests/eval/run_gold_set.py

# 3. 无污染时,读报告失败表的 judge_reasoning 定位真实退化
cat tests/eval/_reports/$(ls -t tests/eval/_reports/ | head -1) | grep -A 5 "FAILED\|judge_reasoning"

# 4. 检查 Langfuse trace 中失败 case 的详细调用链
# Langfuse UI: localhost:3001

# 5. 检查 SafetyCapTriggered 对应的工具循环 trace
curl -s 'localhost:9099/api/v1/query?query=increase(raguide_safety_cap_triggered_total[10m])'

缓解动作

原则:勿盲目使用 --force-baseline--force-baseline 无条件覆盖 baseline,仅用于首次建基线或有意重置场景。--update-baseline 已在 GATE FAILED 时自动拒写。

  1. 机器睡眠污染(case_errors > 0 或时间线有大断层):

    caffeinate -is python tests/eval/run_gold_set.py
    # 若重跑 GATE PASSED,用 --update-baseline 更新 baseline
    python tests/eval/run_gold_set.py --update-baseline
  2. 真实退化(排除污染后仍 GATE FAILED):

    • judge_reasoning 定位具体维度(task_completion/output_quality/process_quality/efficiency/safety)
    • 在 Langfuse trace 中复现失败 case 的调用链
    • 修复对应组件后重跑评测
  3. 有意重置 baseline(架构重大变更后):

    # 确认当前分数合理后再执行(不可逆!)
    python tests/eval/run_gold_set.py --force-baseline

既有降级路径

eval gate 失败不影响生产服务本身,仅阻断 CI pipeline。--update-baseline 仅在 GATE PASSED 时写入,防止退化 baseline 被意外固化。线上质量漂移(LiveQualityDrop)由 done_hook LLM-as-Judge 实时监控,独立于 eval CI。

演练命令

# 演练场景7:触发 LiveQualityDrop 告警
# (在 dev 环境)临时将 judge 评分阈值改低,或注入低质量响应
# 观察 localhost:9099/alerts LiveQualityDrop 进 pending→firing
# 恢复后告警应自动 resolve(15 分钟窗口)

# 验证 eval gate 保护:
python tests/eval/run_gold_set.py
# GATE FAILED 时确认 baseline 未被更新(--update-baseline 自动拒写)

场景8 SSE 中断与首查询冷启动

触发告警: 无独立告警,依赖前端观察和后端日志

检测信号

  • 首次查询(后端刚启动后)SSE 流提前断开,前端无 token 输出
  • 后端日志未出现 embedder warm 完成标记
  • 再次发送同一查询正常响应(冷启动一次性问题)

背景

finding-027(M5):embedder(bge-m3 文本 + JinaCLIP-v2 图像)冷加载约需 23s,曾在首查询时阻塞事件循环导致 SSE 提前断开。现已修复:lifespan 后台预热 + 检索路径 off-thread(concurrent.futures 线程池),embedder 初始化不再阻塞 async 事件循环。

诊断命令

# 1. 检查后端启动日志,确认 embedder 预热完成时间
# 后端日志 = 运行 uvicorn 的终端;若用 nohup/重定向则:
grep -E "warm|embed|preload|startup" <你的日志文件>

# 2. 检查后端健康端点(预热未完成时可能返回 503)
curl -s localhost:8000/healthz

# 3. 查看 TEI 容器状态(embedding 依赖 TEI)
docker logs raguide-tei --tail 20 2>&1 | grep -E "Ready|Error|loaded"

缓解动作

# 若首查询 SSE 中断(预热未完成):
# 等待 30-60s 后重试(预热为后台异步任务,不阻断健康检查)

# 若预热长时间未完成,重启后端触发重新预热:
# 重启后端: Ctrl-C 终止 uvicorn 进程后重新运行
# cd apps/backend && uv run uvicorn raguide_backend.main:app --reload --port 8000

# 监控预热进度(在 uvicorn 终端观察输出):
# 若用 nohup/重定向则: tail -f <你的日志文件> | grep -E "warm|embed|ready"

既有降级路径

当前实现为 lifespan 后台预热(非阻塞):后端启动即可接受请求,embedder 在后台异步加载;首查询若 embedder 未就绪,检索路径会等待就绪或走 CRAG SearXNG 兜底(视具体实现)。预热完成后所有后续查询不受影响。

演练命令

# 演练场景8:模拟冷启动
# 重启后端,立即发送首条查询,观察 SSE 是否正常
# 重启后端: Ctrl-C 终止 uvicorn 进程后重新运行
# cd apps/backend && uv run uvicorn raguide_backend.main:app --reload --port 8000
# 等待 5s(不等预热完成)后立即发查询
# 后端终端输出应出现 embedder 预热相关日志
# 查询应能正常响应(lifespan 预热已修复 finding-027)

场景9 认证不可用与库升级 DDL

触发告警: 无独立 Prometheus 规则;依赖前端登录失败提示和后端日志观察。

检测信号

  • 前端登录/注册返回 503 AUTH_DISABLED(业主无法登录拿 admin JWT)
  • 现有 Postgres 库升级后,登录/注册或评测候选审批报缺表 / 缺列错误(users 表或 eval_candidate_cases.signals 列不存在)
  • 注意:AUTH_DISABLED 不影响游客功能——匿名查询、对话、检索照常(统一身份回退 X-Owner-Id

背景

  • JWT_SECRET 由 env 配置config.jwt_secret 读 env JWT_SECRET。留空时 /v1/auth/register/v1/auth/login 直接返回 503 AUTH_DISABLED,登录态相关功能(业主面板等)不可用,但游客零回归。
  • 库升级幂等 DDLinfrainit.sql 只在 Postgres 数据卷首次初始化时执行;对已存在的库,建表/加列靠 main.py lifespan 的幂等 DDL —— CREATE TABLE IF NOT EXISTS users + ALTER TABLE eval_candidate_cases ADD COLUMN IF NOT EXISTS signals JSONB(均 try/except 包裹,非致命,失败仅告警不阻断启动)。

诊断命令

# 1. 确认 JWT_SECRET 是否配置(只看是否为空,勿回显完整值到日志/截图)
grep -c '^JWT_SECRET=.\+' apps/backend/.env   # 1 = 已配置非空, 0 = 缺失/为空

# 2. 复现 503 AUTH_DISABLED(JWT_SECRET 为空时)
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8000/v1/auth/login \
  -H 'Content-Type: application/json' -d '{"username":"x","password":"y"}'
# 预期: JWT_SECRET 空 → 503; 已配置 → 401(防枚举的统一失败码)

# 3. 确认升级 DDL 已生效(users 表 + signals 列存在)
docker exec raguide-postgres psql -U raguide -d raguide -c "\d users"
docker exec raguide-postgres psql -U raguide -d raguide -c \
  "SELECT column_name FROM information_schema.columns WHERE table_name='eval_candidate_cases' AND column_name='signals';"

# 4. 查后端启动日志,确认 lifespan 幂等 DDL 段
# 后端日志 = 运行 uvicorn 的终端;若用 nohup/重定向则:
grep -iE "users|signals|lifespan|DDL|migrate" <你的日志文件>

缓解动作

# AUTH_DISABLED: 在 .env 配置 JWT_SECRET 后重启后端(凭据只走 .env,永不入库/日志/回显)
# 1. 编辑 apps/backend/.env,设置 JWT_SECRET=<足够长的随机串>
# 2. 重启后端: Ctrl-C 终止 uvicorn 进程后重新运行
#    cd apps/backend && uv run uvicorn raguide_backend.main:app --reload --port 8000

# 缺表/缺列: 重启后端触发 lifespan 幂等 DDL(对已存在库 ADD COLUMN/CREATE TABLE IF NOT EXISTS)
# 若 lifespan DDL 因权限等被 try/except 吞掉,手动补:
docker exec raguide-postgres psql -U raguide -d raguide -c \
  "ALTER TABLE eval_candidate_cases ADD COLUMN IF NOT EXISTS signals JSONB;"

既有降级路径

JWT_SECRET 缺失只熔断登录态功能(register/login → 503 AUTH_DISABLED),统一身份层对游客回退 X-Owner-Id,匿名访问零回归。lifespan 幂等 DDL 的 try/except 非致命:DDL 失败不阻断后端启动,只是登录/候选审批相关功能不可用,修复后重启即恢复。

凭据红线

JWT_SECRET 与各 provider API Key 只走 .env,永不入库、永不进日志、永不回显(DB 仅存 env 变量名)。诊断时只检查 secret 是否为空,勿打印完整值,勿提交到 git。


附录

端口速查

服务 宿主端口 容器端口 容器名
后端 FastAPI 8000 宿主进程(uvicorn)
Prometheus 9099 9090 raguide-prometheus
Alertmanager 9093 9093 raguide-alertmanager
Grafana 3002 3000 raguide-grafana
Langfuse 3001 3000 raguide-langfuse-web
Qdrant 6333 6333 raguide-qdrant
Redis 6379 6379 raguide-redis
PostgreSQL 5432 5432 raguide-postgres
SearXNG raguide-searxng
MinIO raguide-minio
Tantivy raguide-tantivy
TEI (Reranker/Embed) raguide-tei

常用 UI 入口:

  • Prometheus alerts: http://localhost:9099/alerts
  • Alertmanager: http://localhost:9093
  • Grafana 看板: http://localhost:3002
  • Langfuse trace: http://localhost:3001
  • Qdrant dashboard: http://localhost:6333/dashboard

成本口径说明

计入成本累计(被熔断保护)的调用:

  • 所有经由 /v1/agent/run API 的线上 serving 请求(IntentAgent / RetrievalAgent / AdvisorAgent LLM 调用)
  • done_hook LLM-as-Judge 评分调用

不计入成本累计(不被熔断影响)的调用:

  • eval CLI(tests/eval/run_gold_set.py)走 in-process run_graph,不经 API 路由,不经 CostGuard.add,不被熔断拦截
  • 管理后台连通性探测(probe_endpoint)

少记风险: 请求流中途异常时,已消耗的 token 成本可能漏记(方向=少记,不会误熔断)。

已知限制

限制 描述 影响
cost Gauge 单 worker 语义 raguide_cost_monthly_cny Prometheus Gauge 为进程内单例,多 worker 部署下各 worker 独立记录 当前 demo 单 worker,不影响;多 worker 时需改为 Redis-backed Gauge
incrbyfloat+expire 非原子 两次 Redis 调用间崩溃可导致月 key 无 TTL 永存 月 key 名含年月,下月 key 不同,计费逻辑不受影响;仅该 key 不自动过期(需手动清理或接受永存)
月 key 本地时区 raguide:cost:monthly:YYYY-MM 使用服务器本地时区 跨月瞬间约 1s 窗口内 add 与 exhausted 可能读写不同 key,fail-open 语义下可接受;多实例部署需统一 UTC
eval 污染风险 机器睡眠可导致 gold-set 评测超时,产生假 GATE FAILED 参见场景7诊断,用 caffeinate -is 规避
JWT_SECRET 缺失 env JWT_SECRET 为空时 register/login 返回 503 AUTH_DISABLED 仅登录态功能不可用,游客零回归(统一身份回退 X-Owner-Id);参见场景9
升级旧库需幂等 DDL init.sql 只在数据卷首次初始化跑;已存在库的 users 表 / eval_candidate_cases.signals 列靠 lifespan 幂等 DDL(CREATE/ALTER ... IF NOT EXISTS,try/except 非致命) 重启后端触发;DDL 被吞时手动补列;参见场景9