fix: T-21 评审闭环(P1-1 MILVUS_URI 遮蔽)——milvus_service 环境防御顺序重排: settings 先初始化(干净读 .env)→os.environ 归位合法 http 占位(仅替换文件 URI)→import pymilvus; 原顺序下 pydantic 读到被污染 env 会把 .env 的 MILVUS_URI 一并遮蔽(最坏 import 顺序真连接事故); 最坏顺序断言+全量 425 绿+端到端检索通过

This commit is contained in:
2026-09-07 12:24:46 +08:00
parent 8fbbca6bb2
commit 9cc2e59bb7
+22 -9
View File
@@ -5,12 +5,13 @@ Collection 设计(冻结):docs/项目框架设计/表设计/03-milvus-coll
检索必带溯源字段(source_doc_id + source_version),且只返回已生效文档
(effective_date <= today,§2.3 合规约束)。
环境坑(2026-09-07 实测):pymilvus 3.x 在 **import 阶段** load_dotenv()
并急切解析环境变量 MILVUS_URI,文件 URI(如本项目 .env 的
./data/milvus.db)会让 ``import pymilvus`` 直接抛 Illegal uri。因此本模块
在 import pymilvus **之前**用 setdefault 预置一个合法 URI(dotenv 不覆盖
已存在变量);真实连接由 MilvusClient(uri=...) 显式传入 settings.milvus_uri,
不经过全局单例。若未来接 Standalone(Docker),.env 设合法 http URI 即可。
环境坑(2026-09-07 实测,含评审 P1 修正):pymilvus 3.x 在 **import 阶段**
load_dotenv() 并急切解析环境变量 MILVUS_URI,文件 URI(如 .env 的
./data/milvus.db)会让 ``import pymilvus`` 直接抛 Illegal uri。本模块的
防御顺序:先初始化 settings(干净读 .env),再把 os.environ 的 MILVUS_URI
归位为合法 http 占位(仅替换文件 URI),最后 import pymilvus——任何其他
顺序都会以 import 炸 / settings 遮蔽(真连接事故)两种方式踩坑。真实连接
恒走 MilvusClient(uri=...) 显式传参,不经过全局单例。
测试:单测用真 Milvus Lite(tempfile 临时 uri,module 级 fixture 共享),
本机实测可用(0904 交接 + 0907 复测);不做 mock 双轨。
@@ -20,10 +21,22 @@ from __future__ import annotations
import os
# ---- 必须先于 from pymilvus import:防 .env 的文件 URI 炸掉 import(见模块注释)----
os.environ.setdefault("MILVUS_URI", "http://localhost:19530")
from app.config.settings import settings # noqa: E402
from pymilvus import DataType, MilvusClient
# ---- 环境防御(顺序敏感,评审 P1 修正):必须在 settings 初始化之后、
# from pymilvus import 之前。原因:pymilvus 3.x import 时 load_dotenv() +
# 急切解析 MILVUS_URI,.env 的文件 URI 会炸 import;但若先改 os.environ 再
# 初始化 settings,pydantic 会读到被污染的环境变量、把 .env 的 MILVUS_URI
# 也遮蔽掉(import 顺序敏感的真连接事故)。故口径:
# ① settings 先初始化(此时环境干净,milvus_uri 拿到 .env 正确值);
# ② 再把 os.environ 的 MILVUS_URI 归位为合法 http 占位(仅文件 URI 被换,
# 合法 http 值保留),pymilvus 全局单例不再炸 import;
# ③ 真实连接恒走 MilvusClient(uri=settings.milvus_uri) 显式传参。
_milvus_env_uri = os.environ.get("MILVUS_URI")
if _milvus_env_uri is None or not _milvus_env_uri.startswith(("http://", "https://")):
os.environ["MILVUS_URI"] = "http://localhost:19530"
from pymilvus import DataType, MilvusClient # noqa: E402
from app.config.settings import settings