From c1420b5665429b63e10d063d002b8ece96cc6ea6 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Mon, 20 Jul 2026 13:40:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(rag):=20=E5=85=A8=E6=96=87=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E5=9C=A8=E5=AE=B9=E5=99=A8=E9=87=8C=E6=A0=B9=E6=9C=AC?= =?UTF-8?q?=E6=B2=A1=E6=8C=81=E4=B9=85=E5=8C=96=20=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E8=A1=A5=20/data=20=E5=8D=B7=20+=20BLEVE=5FPATH?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 排查「fulltext 路 0 命中」时发现的真问题,比原以为的分词器问题严重得多。 现状:mcp-go 容器**没有任何 volume**,也没设 BLEVE_PATH,于是索引落到相对 CWD 的 .data/bleve。而运行阶段没有 WORKDIR(CWD=/)、进程又是非 root(uid 10001), 两种结局都糟: - 在 / 下建不了目录 → openBleve 静默退回内存索引,重启即清零; - 就算建得了,也只是写进容器可写层,每次发版重建容器一样清零。 更糟的是它不报错:混合检索少一路只表现为召回变差,看不出故障。 改动: - Dockerfile:建 /data 并 chown 给 app,WORKDIR 设为 /data; - 两份 compose:BLEVE_PATH=/data/bleve + 挂持久卷(132 那份此前连 顶层 volumes 段都没有); - .env.example:写清默认相对路径相对的是**启动目录**——本地从仓库根起 和从 sundynix-mcp-go/ 起会写出两份互不相干的索引,排查时极易被坑。 附带澄清:中文分词本身没问题。直接开落盘索引验证过,mapping 里 text 字段 确实是 cjk,实测「星间链」命中 4 条、「海关监管」命中 104 条,BM25 打分正常。 a4852c3 那次修复是有效的,之前怀疑分词器的判断作废。 Co-Authored-By: Claude Opus 4.8 --- .env.example | 5 +++++ deploy/132-app/docker-compose.yml | 10 ++++++++++ docker-compose.prod.yml | 6 ++++++ sundynix-mcp-go/Dockerfile | 7 ++++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index bd01177..bea2cfb 100644 --- a/.env.example +++ b/.env.example @@ -23,6 +23,11 @@ POSTGRES_PASSWORD= # 图数据库密码(Neo4j,知识图谱)。 NEO4J_PASSWORD= +# 全文(bleve)索引落盘目录。留空则用相对路径 .data/bleve —— 相对的是 mcp-go 的**启动目录**, +# 从仓库根起和从 sundynix-mcp-go/ 起会写出两份互不相干的索引(本地排查全文召回时最容易被这个坑到)。 +# 本地开发建议显式写成绝对路径;容器部署已在 compose 里固定为 /data/bleve + 持久卷。 +BLEVE_PATH= + # ── 必填:管理员 ───────────────────────────────────────────── # 管理员用户 ID 白名单(逗号分隔)。生产模式下只有这些用户能进 /admin 控制台。 # 首次部署:先留空起服务 → 注册第一个账号 → 从返回的 user id 填进来 → 重启 gateway。 diff --git a/deploy/132-app/docker-compose.yml b/deploy/132-app/docker-compose.yml index 9ea2444..185e36b 100644 --- a/deploy/132-app/docker-compose.yml +++ b/deploy/132-app/docker-compose.yml @@ -72,6 +72,11 @@ services: MINIO_BUCKET: ${MINIO_BUCKET:-sundynix-docs} SUNDYNIX_SECRET_KEY: ${SUNDYNIX_SECRET_KEY:?} OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.100.128:4318 + # 全文(bleve)索引落盘位置。不设则相对 CWD,写进容器可写层 → 每次发版重建容器就清零, + # 而混合检索只是静默少一路召回、不报错,很难发现。必须配合下面的持久卷。 + BLEVE_PATH: /data/bleve + volumes: + - mcp-go-data:/data mcp-py: build: { context: ../../sundynix-mcp-py, dockerfile: Dockerfile } @@ -83,3 +88,8 @@ services: # Docker 暴露给该服务(可逃逸);不需要代码执行工具可注释掉这两行。mcp-py 无对外端口。 volumes: - /var/run/docker.sock:/var/run/docker.sock + +# 全文索引持久化:随容器重建保留(向量在 Milvus、图谱在 Neo4j,都在 128; +# 只有 bleve 是跟着 mcp-go 走的本地索引,唯独它需要这个卷)。 +volumes: + mcp-go-data: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 1edd8bf..a5659d1 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -71,9 +71,14 @@ services: MINIO_BUCKET: sundynix-docs SUNDYNIX_SECRET_KEY: *secret-key OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 + # 全文(bleve)索引落盘位置。不设则相对 CWD,写进容器可写层 → 每次重建容器就清零, + # 而混合检索只是静默少一路召回、不报错,很难发现。必须配合下面的持久卷。 + BLEVE_PATH: /data/bleve depends_on: nats: { condition: service_started } milvus: { condition: service_healthy } # mcp-go 须在 Milvus 后起,否则工具 no responders + volumes: + - mcp_go_data:/data mcp-py: build: { context: sundynix-mcp-py, dockerfile: Dockerfile } @@ -181,3 +186,4 @@ volumes: minio_data: milvus_data: neo4j_data: + mcp_go_data: diff --git a/sundynix-mcp-go/Dockerfile b/sundynix-mcp-go/Dockerfile index 95b285a..f630075 100644 --- a/sundynix-mcp-go/Dockerfile +++ b/sundynix-mcp-go/Dockerfile @@ -16,7 +16,12 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/mcp-go ./cmd/serve # alpine 运行(内网 runner 连不上 gcr.io;二进制 CGO_ENABLED=0 全静态,ca-certs 供 TLS)。 FROM alpine:3.20 RUN sed -i 's#dl-cdn.alpinelinux.org#mirrors.aliyun.com#g' /etc/apk/repositories \ - && apk add --no-cache ca-certificates tzdata && adduser -D -u 10001 app + && apk add --no-cache ca-certificates tzdata && adduser -D -u 10001 app \ + # /data 存 bleve 全文索引:必须由 app 拥有(进程非 root),且 compose 要挂持久卷。 + # 没有它的话 BLEVE_PATH 默认相对 CWD(=/),uid 10001 在 / 下建不了目录 → 静默退回 + # 内存索引,全文路每次重启清零,而混合检索只会少一路召回、不报错。 + && mkdir -p /data && chown app:app /data USER app +WORKDIR /data COPY --from=build /out/mcp-go /mcp-go ENTRYPOINT ["/mcp-go"]