Skip to content

fix(models): 校验 Embedding 响应与输入逐条对应 - #1143

Merged
xerrors merged 3 commits into
xerrors:mainfrom
lianbie:fix/embedding-response-alignment
Nov 3, 2026
Merged

xerrors merged 3 commits into
xerrors:mainfrom
lianbie:fix/embedding-response-alignment

Conversation

@lianbie

@lianbie lianbie commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

修改内容

Embedding 响应必须与请求输入逐条对应,否则整批失败;修改见 backend/yuxi/modules/models/embed.py。

_extract_embeddings 原先只检查响应里有没有 data,就把 data[*].embedding 拼成列表;batch_encode / abatch_encode 逐批 extend 时也不校验该批条数。

MilvusKB._insert_chunks_to_stores 按列组装 entities,向量列与 chunk_id / content 列靠位置对应,而 pymilvus 只校验列数等于 schema 字段数、不校验各列长度一致。我用 pymilvus 3.0.2 实测:

column lengths: [3, 3, 3, 2]
prepare_data ACCEPTED the mismatch
    id -> 3 values
    content -> 3 values
    file_id -> 3 values
    vector -> 2 values

因此 Embedding 端点返回部分结果时不会抛错:缺向量的 chunk 和错位的向量会静默写入 Milvus,而 PostgreSQL 的 chunk 事实已先行写入(_insert_chunks_to_stores 注释写明「PG 为权威源,先落 chunk 事实」),属于无法自动发现的静默数据损坏。

与既有契约的关系

models/rerank.py 的 _batch_rerank 已在 #1016 建立同类契约:结果条数等于输入条数、index 为范围内唯一整数,否则整批抛出固定错误。本 PR 让 Embedding 遵守同一契约。

决策

  1. _extract_embeddings 接收请求条数,条数不符直接抛出带条数信息的 ValueError。
  2. 提供者返回 index 时按 index 归位;缺失、重复、越界(含超出请求条数)的索引整批失败,混用带与不带 index 的条目同样失败。
  3. 不返回 index 的既有响应继续按响应顺序接受,保持兼容。
  4. batch_encode / abatch_encode 在每批 extend 前校验该批条数,避免子集被拼进更大的错位列表。

不填零向量、不静默截断:两种做法都会让错配继续存在且不可见。

详情见 docs/develop-guides/decisions/implemented/2026-11-03-embedding-response-alignment.md。

验证

  • backend/test/unit/models/test_embed_alignment.py,8 个用例:条数不足、重复索引、越界索引、乱序归位、无索引兼容、空输入、encode 拒绝部分批次、abatch_encode 逐批拒绝部分结果。
  • 修复前实测:6 个失败、1 个通过;修复后 8 个全部通过。
  • ruff check、ruff format --check、ruff check --select I 在 backend/ 下对改动文件通过。

未覆盖

  • 未执行真实 Embedding 服务调用:本变更验证响应解析与批次累加,不验证模型服务质量。
  • 条数正确但向量内容错误的情况不在本契约范围内。
  • 本机为 Windows,test/unit 全量套件受 POSIX 专属路径标记阻塞(os.O_DIRECTORY),因此只在隔离环境运行了本 PR 新增用例;容器内的全量 test/unit 需由 CI 或维护者执行。

Fixes #1142

_extract_embeddings 只检查响应里有没有 data,就把 data[*].embedding 拼成列表;
batch_encode / abatch_encode 逐批 extend 时也不校验该批条数。

MilvusKB._insert_chunks_to_stores 按列组装 entities,向量列与 chunk 列靠位置
对应,而 pymilvus 只校验列数、不校验各列长度一致(实测 [3,3,3,2] 可被接受)。
Embedding 端点返回部分结果时,缺向量的 chunk 与错位的向量会静默写入 Milvus,
而 PostgreSQL 的 chunk 事实已先行写入。

按 rerank 在 xerrors#1016 建立的同类契约处理:响应条数必须等于请求条数,index 存在时
必须是范围内唯一整数,否则整批抛出固定错误;无 index 的既有响应保持按顺序接受。
批次累加前额外校验每批条数,避免子集被拼进更大的错位列表。

Fixes xerrors#1142
@xerrors

xerrors commented Nov 3, 2026

Copy link
Copy Markdown
Owner

已提交 6984d1f:精简两个包装函数,复用数量校验;明确拒绝 embedding: null,用索引映射独立判重,修复直接 encode/aencode 少返回向量及语义切分静默丢句的风险。保留无索引等既有兼容行为,补齐同步/异步、null+重复索引与畸形响应回归。独立 Reviewer 复核无阻塞。

验证:原实现新增回归 6 失败,修复后模型模块 129 通过;Ruff、工程契约(含 56 项测试)及文档构建通过。CI 待最新提交运行,通过且无阻塞后合并。

执行命令与环境限制
  • cd backend && .venv/bin/pytest test/unit/models -q:129 passed。
  • cd backend && .venv/bin/ruff check yuxi test/unit/models/test_embed_alignment.py、对应 ruff format ... --check 与 ruff check --select I ...:通过。
  • python3 scripts/ci/verify_engineering_contracts.py、python3 -m unittest scripts.ci.test_verify_engineering_contracts、git diff --check:通过。
  • cd docs && ./node_modules/.bin/vitepress build:通过。
  • docker compose exec api uv run --no-sync --group test pytest test/unit -m "not slow":独立工作区无实例配置,未能启动执行。
  • 替代执行 cd backend && .venv/bin/pytest test/unit -m "not slow" -q:3643 passed、36 failed、8 errors、1 skipped。此轮在补充最后 3 个形态测试之前运行;失败涉及 macOS 缺少 /proc、renameat2、路径差异及沙箱限制等,不能视作全量通过,完整验证交由 Linux CI。
  • 未调用真实 Embedding 服务,未验证供应商质量或历史数据修复。

@xerrors
xerrors merged commit fcdb5a3 into xerrors:main Nov 3, 2026
20 checks passed
xerrors pushed a commit that referenced this pull request Nov 4, 2026
_extract_embeddings 在 #1143 建立了「响应与输入逐条对应」的契约,但不校验每条
向量的长度;test_connection 只比对 embeddings[0],且仅用于连接探针。

MilvusKB._create_new_collection 用配置维度固定集合 schema,写入时不再比对;
pymilvus 只校验列数,实测 Prepare.prepare_data 接受 [4, 2, 4] 对 schema dim=4
的批次。供应商切换模型版本、截断向量或聚合网关路由到维度不同的后端时,维度
混杂的批次会静默写入集合,而 PostgreSQL 的 chunk 事实已先行写入。

在响应边界新增维度校验:_extract_embeddings 接收 dimension,逐条比对长度;
_require_one_vector_per_input 一并校验,供 batch_encode / abatch_encode 每批复用。
dimension 为 None 时保持既有行为。

Fixes #1144
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Embedding 返回条数与输入不一致时静默写入错位的 Milvus 向量

2 participants