v0.7.x 把知识处理与检索的主链路打通之后,v0.8.0 聚焦另一件事:让这套服务能长期跑下去,并且在出问题时能定位、能恢复。这一版不新增面向用户的产品能力,而是补齐重试、重建、可观测性和资源保护,为进入 v1.0.0 发布验收做准备。
失败可以安全重试
导入链路上任何一步(解析、分块、embedding、索引、发布)都可能失败,之前失败后只能重建知识库。现在提供了三个层面的重试入口:
- 文档级重试:
POST /api/v1/.../documents/:id/retry,从失败的 revision 继续。 - 任务级重试:
POST /api/v1/.../jobs/:id/retry,针对单个 asynq 任务。 - MCP 重试:新增
document_retry工具,MCP 客户端也能触发。
重试走 Session 身份时需要 admin/owner 权限;API Key 走 ScopeDocumentsWrite。重试链路有一个关键契约:parse job 的 payload 必须携带当前 active 的 index_generation_id,这样 reindex 之后重试才能落到正确的 Generation 上;入队失败时有 FailReset 补偿,把 revision/job 标回 failed,不会留下半成品状态。
Generation 重建
知识库换模型或改分块配置后需要重建索引。v0.8.0 把 Generation reindex 接入了双缓冲机制:新 Generation 在 staging 构建完成后原子激活,旧 Generation 退役进入恢复窗口,active Generation 在整个过程中始终可查。重复请求和 worker 重启不会破坏当前对外服务的索引。
OpenTelemetry 可观测性
这是 v0.8.0 最重的部分。指标底层全面迁移到 OpenTelemetry SDK:
/metrics端点保留,Prometheus 抓取兼容;OTLP exporter 默认关闭,配置 endpoint 后才推送。- 指标名遵循固定契约(如
langhuan_http_requests_total),counter 不设WithUnit("1")(否则 exporter 会转成_ratio_total后缀),并启用 Prometheus legacy 名称校验,避免输出带点号的非法名。 - RAG 检索 span 遵循 gen_ai 语义约定(
gen_ai.operation.name=retrieval/embeddings、gen_ai.data_source.id、rag.retrieval.empty_result)。 - 查询原文不写入 span——无论是 attribute 还是 event,与日志脱敏策略一致。
- traces 默认开启,采样率可配(
observability.traces.sample_rate)。
有了分阶段耗时(upload / parse / chunk / embedding / index / publish)和关键计数,导入慢、解析失败、embedding 失败、索引失败和发布失败都能定位到具体阶段,并关联到 workspace / document / revision / job / generation。
asynq 任务治理
任务队列从「能跑」升级到「可控」:
- MaxRetry、Timeout、Retention 全部配置化,不再硬编码。
- 提供 Inspector 和
/admin/queues/*死信管理接口,可以看到积压、重试中和已终止的任务。 - dead-letter 检查策略明确,终止失败的任务不会无限重试占用 worker。
资源保护与清理
- chunk 数量、embedding 并发和 batch 大小都有上限配置,大文件、压缩炸弹和异常图片不会无限占用 worker 资源。
- failed / staging / retired 投影接入可控的定时 cleanup,保留 active Generation 和恢复窗口内的事实数据。
- 备份恢复 runbook 完成,在空数据库与空对象存储上可以重复走通安装 → 备份 → 恢复 → 单二进制 smoke 的完整流程。
这一版交付后,琅嬛具备了长期内部运行的可恢复性。剩余的已知技术债(S3 对象存储的 otelaws 埋点、Web Console 队列监控前端页)已记录在 docs/KNOWN_BUGS.md,不影响当前主链路。