AI-3.3-02 全域三语检索 · 实现策略 TDD

中日英三语检索——三端搜索链路改造 + 全量索引方案(PRD 需求编号 FR-3.3-02)

类型:技术设计文档(TDD) 条目编号:AI-3.3-02(溯源 FR-3.3-02) PRD 版本:V1.4(2026-10-22) 优先级:Must(P1:部分实现,补齐关键缺口) 深度分级:full full 波次:p1 文档版本:v1.0(2026-10-26) 源文件:docs/reports/ai-module-prd/tdd/AI-3.3-02-trilingual-search.md

摘要

PRD V1.4 §5.2 FR-3.3-02 验收要点:「跨语查询召回质量抽检合格率(基线与目标立项定);三语查询体验一致。」 现状判定为部分实现——后端 q 自由文本过滤已实现且参数化注入安全(backend/src/api/store/products/route.ts:258-272), 三语译文底座(Medusa Translation Module 译文表)已启用(backend/medusa-config.ts:55-56), 但三语跨语检索实际为零:查询链路仅搜原语言三列、索引 schema 单语、b2b/h2b 搜索输入框 disabled、 B2C「Algolia 列表」实为一次性拉取前 100 条 + 客户端过滤(R-search-100-cap 数据天花板)。

本卡 P1 为检索底座工程改造(零 AI/LLM 调用):已定档候选 A「深化 Medusa 路径」—— q 查询以 A1 EXISTS 子查询直查译文表实现跨语命中(事务同库、零同步时延); R-search-100-cap 以 C1 服务端分页先行修复;三端搜索链路启用与 locale 透传对齐 storefront 既有范式。 同义词注入仅预留 query_expansion 恒等占位(槽位契约归 AI-3.3-03 所有,本卡不设计术语库实体)。 范围口径承 findings key_finding:本条范围大于 PRD 原文表述——实际缺口为三端搜索链路改造 + 数据天花板修复,PRD 原文低估了前台侧缺口。

交付子任务
6
S1 分页修复(先行)/ S2 q 跨语扩展 / S3-S5 三端链路 / S6 扩展槽占位
验收标准
7
A1 跨语召回 … A7 注入安全,含超 100 条可见性回归(A3)
前置决策
2
F-search-backend-choice 已定档(解锁);DEC-5 抽检合格率基线立项定
Blocker
1
R-search-100-cap(code_defect):不先修复,任何召回率改进都被 limit=100 截断

关键概念

三语跨语检索
查询语言与商品源语言不一致时仍能命中——以 translation 表译文字段参与查询条件实现(日本买家用日文可搜到中文/英文发布的产品,反之亦然)。
R-search-100-cap
B2C 列表组件一次性拉取前 100 条(limit="100",AlgoliaProductsListing.tsx:192)+ 客户端过滤——目录超 100 条后,超出部分对搜索与计数均不可见的数据天花板。
A1 EXISTS 子查询
q 条件追加对 translation 表的 EXISTS 子查询(reference='product'、locale 白名单、translations jsonb 三字段 ILIKE),事务同库零同步时延——三候选对比后的选定方案。
C1 服务端分页
offset/limit 服务端分页 + 响应 count 全量计数,修复 100 条天花板;cursor 留作深分页演进位(目录量级进入 10⁴ 时切换键集分页)。
x-medusa-locale
Medusa 请求头,指定译文叠加语言。B2C 已在三处透传(storefront/src/lib/data/products.ts:128,197,360);本卡推广至 b2b/h2b,并新增参与检索 locale 过滤。
query_expansion 槽位
预留纯函数位 expandQuery(q): string[],现状恒返回 [q](零行为变化);注入格式由 AI-3.3-03 冻结,实体 schema 冻结前经两域联合评审(RK-14)。
TRANSLATION_REFERENCES
译文行 reference 稳定枚举(product / product_option / product_option_value / product_category,backend/src/utils/translation-references.ts:19-24)——查询链路直接复用,禁止新建字面量。
SUPPORTED_SOURCE_LOCALES
locale 白名单 ["ja-JP","zh-Hans","en-US"](backend/src/utils/locale.ts:36-40)——查询 locale 值域唯一合法词表,非法值回退三语全量不报错。

方案设计

设计红线声明(AGENTS.md §0.1 逐条对照)

红线对照
  • Workflow 承载业务逻辑:本卡全部为只读查询链路改造(q 检索/分页/locale 透传),无业务数据变更,无新增 Workflow;未来「同义词注入配置」类可写能力归 AI-3.3-03。
  • 不改上游源码:@medusajs/translation(译文表)与 @mercurjs/algolia(本卡不启用)只经官方扩展点消费——查询链路改造全部落在本地文件 backend/src/api/store/products/route.ts。
  • 后端单一数据源:译文以 translation 表为准,前端不缓存译文索引;三端仅透传 locale/q 参数消费后端结果。
  • 参数化 SQL:q 现状已是参数化绑定正面范式(route.ts:263-270 转义+三占位符);译文 EXISTS 扩展沿用同范式;jsonb ->> 操作符经 knex raw 时按红线转义(\? 转义或 raw 绑定)。

后端查询链路改造(选定分支 = 候选 A「深化 Medusa 路径」)

前置决策 F-search-backend-choice 已由 PILOT 决策一定档:已定档 选定候选 A;候选 B(启用 Algolia)不废弃,降级为 AI-3.3-04 演进重估项。以下为三候选对比(查询链路跨语化,字段级):

候选机制数据一致性改动面评估
A1. EXISTS 子查询直查译文表 q 条件追加 OR EXISTS (SELECT 1 FROM translation t WHERE t.reference='product' AND t.reference_id=p.id AND t.locale_code IN (?,?,?) AND (t.translations->>'title' ILIKE ? OR …)) 事务同库,译文提交即可查(零同步时延) 仅 route.ts 一个文件 + bindings 追加 ✅ 选定 复用 (reference, locale_code) 索引;无新表无同步管线;AI-3.5-02 管道译文即数据源,闭环最短
A2. 物化视图(译文摊平列) product_search_mv(product_id/ja/zh/en 三列拼接译文)定时/触发刷新 刷新窗口内陈旧 新表+刷新 job+查询改 MV ❌ 未选 一致性代价与刷新运维成本不成比例;P1 数据量下无性能必要性
A3. 独立检索表(自维护倒排/全文) product_search_index 表 + 商品/译文双事件同步 事件驱动,存在丢失风险 新表+双 subscriber+补偿 ❌ 未选 自建索引子系统,属 Algolia 路线的劣化复制;留作 AI-3.3-04 演进评估项

A1 字段级设计(backend/src/api/store/products/route.ts q 分支 :262-271 内扩展)

输入:filters.q: string(已 trim),filters 携带 locale 上下文
构造(全部参数化绑定,? 占位符序与 bindings.push 顺序严格一致):
  原语言条件(现状保留):(p.title ILIKE ? OR p.description ILIKE ? OR p.subtitle ILIKE ?)
  跨语条件(新增):
    EXISTS (
      SELECT 1 FROM translation t
      WHERE t.reference = ?                -- 'product',取 TRANSLATION_REFERENCES.PRODUCT 常量
        AND t.reference_id = p.id
        AND t.locale_code = ANY(?)         -- 目标语集合(文本数组参数),默认三语全量
        AND t.deleted_at IS NULL
        AND (
             t.translations->>'title'       ILIKE ?
          OR t.translations->>'description' ILIKE ?
          OR t.translations->>'subtitle'    ILIKE ?
        )
    )
  LIKE 转义:沿用 :265 escapedQ(% _ \ 转义),ILIKE pattern 复用同一 likePattern 值
合并:conditions.push(`(原语言条件 OR 跨语条件)`)
实现要点
  • locale 参数来源:与既有 x-medusa-locale 请求头同源——route 经 middleware 读取 locale 上下文后注入查询;值域校验复用 SUPPORTED_SOURCE_LOCALES 白名单,非法值回退三语全量不报错。
  • option/option_value 译文:P1 商品级三字段(title/description/subtitle)先行;option title 与 option value 译文(reference=product_option/product_option_value)留作查询链路第二扩展位(EXISTS 复用同结构),在 A1 合格率抽检不达标时启用——避免首版 jsonb OR 链过长拖累索引选择。
  • 分类译文:product_category reference 不进商品查询(分类检索走分类接口),不在本卡范围。
  • 性能护栏:EXISTS 走 (reference, locale_code) 索引前缀;P1 数据量(seed/demo 级)无全表扫描风险;translated_field_count 列可用于实现期验证覆盖率但查询不依赖。
  • 错误码:无新增(q 非法值沿用现状静默忽略语义;locale 非法回退全量)。
sequenceDiagram autonumber participant FE as 三端前台 storefront / b2b / h2b participant API as GET /store/products participant Q as q 查询构造 route.ts participant T as translation 译文表 FE->>API: GET ?q=词 + x-medusa-locale API->>Q: filters.q trim 非空进入 q 分支 Q->>Q: LIKE 通配符转义(% _ 等通配符)+ 参数化绑定 Q->>T: EXISTS 子查询(reference='product' AND locale_code IN 三语 AND translations jsonb 三字段 ILIKE) T-->>Q: 命中译文物料行 Q-->>API: 合并条件(原语言 OR 跨语) API-->>FE: 商品集合 + count 全量计数 Note over API,T: locale 非法 → 回退三语全量不报错;译文缺失 → 自然回落原语言条件(零阻断)
图 1. 跨语查询链路时序(A1 EXISTS 子查询,选定分支)

① b2b-storefront / h2b-storefront:补传 x-medusa-locale(对齐 storefront:128,197,360 范式)

文件改动函数(复核行号)改动内容
b2b-storefront/src/lib/data/products.ts getProductsById(:41)、getProductByHandle(:77)、listProducts(:107)、listProductsWithSort(:181) 各函数请求 headers 增加 'x-medusa-locale': await getRequestLocale()(与 storefront:128 同构);listDisplayProductHandles(:12) 仅取 handle 不受语言影响,不改(改动最小化)
h2b-storefront/src/lib/data/products.ts getProductsById(:45)、getProductByHandle(:72)、listProducts(:93)、listProductsWithSort(:159) 同上;getJapanAdaptationStatuses(:15) 为状态查询不改
  • getRequestLocale 工具按端既有 locale 解析实现复用(next-intl 上下文),不新建;
  • 效果:B2B/H2B 前台商品标题/描述按用户语言展示译文(Translation Module 读路径原生支持),与检索改造解耦、同批交付。

② b2b/h2b search-in-results/index.tsx:disabled 装饰框 → 真实搜索

两端 src/modules/store/components/refinement-list/search-in-results/index.tsx:13(<input :13、disabled :15):

  • 移除 disabled 与 hover:cursor-not-allowed,改为受控输入(useState + debounce ~300ms);
  • 提交/输入触发把 q 写入当前列表页 URL 查询参数(与 refinement-list 既有 refinement 状态管理同通道,挂载点不动);
  • 列表数据函数(①中 listProducts/listProductsWithSort)透传 q 给后端(配合 4.1 跨语查询);
  • i18n:复用既有三语 key(catalog.searchInProducts/catalog.searchInList/layout.searchDisabledTitle);searchDisabledTitle 提示 key 移除引用后保留在 messages(不删 key,回滚友好)。

③ storefront AlgoliaProductsListing.tsx:废除客户端过滤,升级服务端 q + 分页

位置(复核行号)现状改动
:190-195 URLSearchParams({ …, limit: "100", … }) limit 改为分页常量(PRODUCT_LIMIT,值随 4.3 选定)+ offset 参数化;新增 q: normalizedQuery 透传
:205-206 fetch("/store/products?…") 单次拉取 保留 fetch 形态,分页参数化(4.3);响应 count 直接用于 :308 total
:132,:153 query 参数读取与 normalize 参数名统一 q(后端契约),保留 query 兼容读取一个版本(缺口⑦)
:258-259 客户端 title.includes 过滤 删除(服务端 q 已覆盖 title/description/subtitle+三语译文,召回严格超集)
:296-300 排序后 count 计算 服务端 count 直采,删除客户端计数推导
  • 使用方页面(categories/page.tsx:85、categories/[category]/page.tsx:102)resolvedSearchParams.query 读取处同步映射 q;
  • admin-panel / vendor-panel / influencer-panel / referrer-panel:无改动(rg 确认无搜索链路触点;algolia-product.ts 类型文件属 B 路线资产,本卡不动)。

④ locale 白名单与 reference 集合全链路复用

唯一合法词表

查询侧 locale 值域 = backend/src/utils/locale.ts:36-40;译文表 reference 值域 = backend/src/utils/translation-references.ts:19-24——两常量为本卡查询链路与未来 AI-3.3-03 注入的唯一合法词表,禁止新建字面量。

全量索引/分页方案(R-search-100-cap,必须先行)

候选机制优点缺点评估
C1. 服务端分页(offset/cursor) 列表请求携带 limit/offset,后端 SQL 分页返回 count 总数 Medusa store products API 原生支持;改动集中 AlgoliaProductsListing.tsx 一个组件 offset 深翻页性能随页深衰减(P1 数据量无感) ✅ 选定 以 offset 起步(B2C 现有翻页 UI 语义不变);cursor 留作深分页演进位(目录量级进入 10⁴ 切 created_at,id 键集分页)
C2. 无限滚动增量拉取 前端 IntersectionObserver 增量 offset 拉取 体验顺滑 改造翻页 UI 与状态管理面大;SEO/可分享 URL 弱化;不改数据天花板本质 ❌ 未选 交互改造成本与检索目标无关,划出本卡
C3. 索引全量查询(Algolia 路线) 全量 reindex 后索引侧分页 检索体验最佳 三重前置(schema 三语化重构、插件源码二次确认、全量索引同步管线)——P1 成本不成比例 ❌ 未选 降级为 AI-3.3-04 演进重估项

选定 C1 字段级落地:

  • AlgoliaProductsListing.tsx:分页状态从 URL searchParams 读取(page 或 offset,与既有 refinement 参数同管理通道);:192 limit: "100" 改 limit: PRODUCT_LIMIT(组件级常量,默认 20——与集合页现有分页语义对齐,最终值随 UI 验收);每页渲染调 fetch(:205-206)带 offset=(page-1)*PRODUCT_LIMIT;:308 total 取响应 count;
  • 后端零改动(route.ts 现有 SQL 已支持 LIMIT/OFFSET 与 count);
  • 超 100 条可见性回归测试点(验收 A3):构造 >100 条商品数据集,断言第 101 条商品按其译文词搜索可命中且计数 ≥101(改造前必失败、改造后通过的判据用例)。

同义词扩展槽(碰撞约束:不设计共享术语库实体)

约束声明

本卡不得设计共享术语库实体——术语/同义词的实体、CRUD、消费 API、管理界面全部归 AI-3.3-03-shared-thesaurus.md 所有;索引 schema 与查询链路仅预留以下字段级扩展槽。

槽位位置字段级定义消费契约占位
查询侧 query_expansion 注入位 route.ts q 分支(4.1 构造点之前) 预留纯函数位 expandQuery(q: string): string[](现状恒返回 [q]——零行为变化);命中词集合 = 原词 ∪ 同义词展开词,ILIKE 条件按词集合生成(OR 链,仍全参数化) 注入格式(同义词对/行业词表→展开词)由 AI-3.3-03-shared-thesaurus.md 冻结;实体 schema 冻结前经翻译/检索两域消费契约联合评审(RK-14),评审前本函数为恒等占位,可独立合入
索引侧 synonym_expanded_query 可扩展列 当前无独立索引实体(A 路线);若未来启用 C3/Algolia 路线,algolia-product.ts schema 增加该可选列 synonym_expanded_query: z.array(z.string()).nullable().default([]) 仅作 B 路线 schema 演进记录,本卡不实施;同上由 AI-3.3-03 消费

实现顺序 DAG 与排期(分页先行,强约束)

flowchart TD S1["S1 服务端分页修复(4.3)<br/>R-search-100-cap · A3 判据用例先行"] --> S2["S2 q 跨语查询扩展(4.1 A1)<br/>SQL + locale 上下文 + 单测先行"] S2 --> S3["S3 b2b/h2b locale 透传(4.2①)"] S2 --> S4["S4 b2b/h2b 搜索框启用(4.2②)"] S2 --> S5["S5 storefront 客户端过滤废除(4.2③)"] S3 --> SM["三端冒烟 → 验收 A1 / A2"] S4 --> SM S5 --> SM S6["S6 query_expansion 恒等占位(4.4)<br/>可独立合入(零行为变化)"]
图 2. 子任务 DAG 与依赖(实现顺序强约束:分页修复必须先行)
子任务依赖估时
S1 分页修复 + A3 用例无1d
S2 q 跨语扩展(SQL+locale 上下文+单测)S1(验收有效性)2d
S3/S4/S5 三端链路(并行动作)S22d
S6 扩展槽占位无(可先行)0.5d
e2e 三语冒烟 + 抽检用例集S1-S51d

所属波次 p1(与 AI-3.5-02 打样同域衔接:本卡消费其译文物料,非串行阻塞)。交叉引用:AI-3.5-02(译文物料上游,pilot 波)、AI-3.3-03(A 波 P2,同义词槽消费契约)、AI-3.3-04(B 波 P2,查询理解与 Algolia 重估)、AI-3.9-04(无直接挂钩)。

数据模型 / API / Workflow

数据模型汇总

对象变更字段级内容
translation(上游 @medusajs/translation 表) 无变更(只读消费) 表结构:id(prefix trans) / reference_id text not null / reference text not null / locale_code text not null(BCP47)/ translations jsonb not null(键=实体字段名,值=译文)/ translated_field_count number default 0;索引含 unique(reference_id, locale_code)、(reference, locale_code) 等;查询仅读 reference/reference_id/locale_code/translations/deleted_at
product 等业务表 无变更 原语言 ILIKE 列沿用 title/description/subtitle
新表 无 A1 路线无新增持久化实体
i18n messages(三端) 仅 key 消费调整 见「前端改动面与 i18n」章节
erDiagram PRODUCT ||--o{ TRANSLATION : "三语译文行(reference=product)" TRANSLATION { text reference_id "实体 id(product.id)" text reference "实体类型(product 等四值枚举)" text locale_code "BCP47 语种(ja-JP / zh-Hans / en-US)" jsonb translations "字段名→译文(title / description / subtitle)" } PRODUCT { text id "商品 id" text title "原语言标题(ILIKE 列)" text description "原语言描述(ILIKE 列)" text subtitle "原语言副标题(ILIKE 列)" }
图 3. 数据关系——商品原语言列与 translation 译文行(A1 路线零新表,译文表只读消费)

API 契约汇总

路由方法变更权限域契约要点
/store/products GET 行为扩展(无 schema 破坏) store(publishable key) q:语义升级为「原语言 OR 三语译文」命中;limit/offset:既有参数语义恢复为真分页;响应 count:全量计数;x-medusa-locale 请求头:既有语义(译文叠加)+ 新增参与检索 locale 过滤;错误码无新增(400 校验沿用 validators.ts)
/store/products(其余参数) GET 无改动 store category_id/collection_id/排序等 filter 逻辑不动(route.ts 各分支)

Workflow 设计

无新增 Workflow

本卡零数据变更(见设计红线对照)。检索配置类可写能力(同义词注入配置等)归 AI-3.3-03,其 Workflow 由该 TDD 设计。

AI 能力接入点:无 LLM 调用声明

本卡 P1 交付物(跨语查询/分页/链路改造)为纯工程检索能力,零 AI/LLM 调用——与 backend/src/modules/translation/client.ts 通路无调用关系,仅有数据依赖:AI-3.5-02 翻译管道(LLM 调用唯一发生点)产出三语译文行 → translation 表(同一 Postgres 库)→ EXISTS 子查询只读(事务同库零同步时延)→ GET /store/products?q=… 跨语命中。预留接口假设:语种识别/查询理解/语义转换归 AI-3.3-04(P2),其输入位即本卡查询上下文(q + x-medusa-locale + 用户会话 locale);本卡预留的 query_expansion 注入位同时是 AI-3.3-04「解析结果→结构化检索条件」的落点位。

前端改动面与 i18n

端改动i18n 文件路径新增 key
storefront AlgoliaProductsListing 改造(4.2③/4.3);搜索结果页计数/分页语义修复 storefront/messages/{en,ja,zh-Hans}.json catalog.searchResultsCount(「{count} 件ヒット / {count} 条结果 / {count} results」示意);既有 placeholder key 复用
b2b-storefront products.ts locale 透传(4.2①)+ search-in-results 启用(4.2②)+ q 透传 b2b-storefront/messages/{en,ja,zh-Hans}.json catalog.searchResultsCount 同名新增;layout.searchDisabledTitle 保留不删(回滚友好)
h2b-storefront 与 b2b 同构 h2b-storefront/messages/{en,ja,zh-Hans}.json 同 b2b
admin-panel 无改动 — —
vendor / influencer / referrer panel 无改动 — —

三语值随实现提交;key 命名沿用各端既有 catalog/layout 命名空间,禁止并行新建第三套。

验收与测试

验收标准(PRD 要点 + 技术口径)

#验收标准映射
A1 跨语查询召回:以「源词→三语译词」用例集抽检——日文查询命中 zh/en 源商品、中文查询命中 ja/en 源商品、英文查询命中 ja/zh 源商品三向用例均通过;合格率口径与数值基线立项定(DEC-5) PRD 验收要点①
A2 三语查询体验一致:同一查询词 + 同一 locale,storefront/b2b/h2b 三端命中集合一致(同后端查询路径的证据:三端均走 GET /store/products?q=…);b2b/h2b 搜索输入框可用(非 disabled)且三语文案正确 PRD 验收要点②
A3 超 100 条可见性回归:>100 条数据集下,第 101+ 条商品可被搜索命中(含其译文词命中),结果计数 ≥101——修复 R-search-100-cap 判据用例 4.3 / 缺口⑤
A4 分页正确性:limit/offset 翻页无重复无遗漏;count 与实际命中总数一致(:308 total 语义修复) 4.3
A5 locale 透传:b2b/h2b 商品列表按用户语言返回译文(x-medusa-locale 对齐 storefront:128 范式);locale 非法值回退三语全量不报错 4.2① / 5.3
A6 降级路径:译文为空库时搜索行为与现状逐字节等价(仅原语言列命中)——AI-3.5-02 未接入/失败零阻断 5.3 / NFR-3.8-01
A7 注入安全:q 含 % _ \ 通配符与 ' 引号时按字面量命中(转义+参数化不破坏) 4.1 / AGENTS.md 红线

测试策略(分层)与 L0 门禁

L0 门禁:本文档十要素章节齐备、关键 code anchor 字符串可 rg 命中、backend products route 既有单测可回归(backend/src/api/store/products/__tests__/route.unit.spec.ts、h2b-visibility.unit.spec.ts——实现期复跑)。

层范围明细
既有回归(2 文件) 现网行为守卫 route.unit.spec(含 q 转义既有用例)、h2b-visibility.unit.spec(H2B 合规过滤与 q 分支共存性)
新增单测(后端) 4.1 A1 ① 跨语命中:三语译文词各自命中(ja/zh-Hans/en-US × reference=product);② 译文缺失回落原语言;③ locale 白名单校验与非法回退;④ LIKE 转义与参数化(A7);⑤ jsonb ->> 经 knex raw 的转义正确性;⑥ (reference, locale_code) 索引命中计划(EXPLAIN 断言,防全表扫描回归)
新增单测(前端逻辑层) 4.2③/4.3 ⑦ q/query 参数兼容映射;⑧ 分页 offset 计算与 count 透传;⑨ 客户端过滤删除后排序语义不变(sortProducts 输入从 filtered→服务端结果)
e2e(三端冒烟) A1/A2/A3 三端各 1 条跨语搜索用例 + >100 条可见性用例(Playwright,复用 pnpm db:reset:demo 种子)
降级路径测试 A6 空译文库/译文管道失败两类前置下的搜索等价性

量化指标占位口径:跨语召回抽检合格率(基线/目标)=基线立项定(DEC-5);检索时延指标名 p95_query_latency(数据源:route 耗时日志)随实现纳入,数值同属 DEC-5,本卡不预设。

风险与降级

Blocker 与风险卡

Blocker R-search-100-cap 数据天花板(code_defect)
阻塞检索优化验收有效性——不先解决,任何召回率改进都被 limit=100 截断(改前第 101 条起不可见)。缓解:全量索引/分页方案先行(4.3 C1 服务端分页为 4.2③/4.1 验收的前置步骤,实现顺序:分页修复 → q 跨语 → 三端链路);A3 回归用例先行编写(改造前必失败)。
Medium option/option_value 译文首版不参与查询
避免首版 jsonb OR 链过长拖累索引选择,P1 仅商品级三字段先行。缓解:EXISTS 复用同结构的第二扩展位已预留,A1 合格率抽检不达标时启用。
Medium 搜索参数命名不一致(query vs q)
B2C 搜索词经 ?query= 进 categories 页(categories/page.tsx:85),后端约定参数为 q(route.ts:262)——query 被后端静默忽略。缓解:前端改造时统一 q 参数名透传,保留 query 兼容映射一个版本。
Low offset 深翻页性能衰减
P1 数据量(seed/demo 级)无感;记录演进位:目录量级进入 10⁴ 时切换 created_at,id 键集分页(cursor)。

降级与跳过路径(NFR-3.8-01 范式)

「未配置/故障即跳过不阻断主业务」
  • 译文缺失:EXISTS 不命中 → 查询自然回落原语言列条件(单 OR 结构,无需额外开关)——译文管道(AI-3.5-02)未跑/失败时,搜索行为等于现状,零阻断;
  • q 为空/纯空白:沿用现状语义(route.ts:262 trim 判空跳过整个 q 分支);
  • locale 非法:回退三语全量(4.1),不报错;
  • 未来 AI 查询理解(AI-3.3-04)不可用:expandQuery 恒等回退 [q],检索不阻断。

迁移与回滚

无新增 schema migration(A1 路线零新表零加列;translation 表为上游模块既有建表)。pnpm db:reset / db:reset:demo 兼容:查询链路改造对种子数据零依赖变更——空译文库时行为与现状等价(A6),demo 种子的译文行(AI-3.5-02 管道产物)自动被跨语查询受益。

变更回滚路径
q 跨语扩展(4.1)代码回退 q 分支至原三列 ILIKE(route.ts:272 现状)——单文件 git revert 粒度;无数据回滚
服务端分页(4.3)AlgoliaProductsListing.tsx 回退;limit=100 旧行为无数据残留
三端 locale 透传/搜索框启用前端回退即恢复原语言展示与 disabled 态
query_expansion 占位(4.4)恒等函数,删除无副作用
查询参数兼容映射(q/query)保留 query 兼容读取至少一个版本后移除(4.2③)
  • ALGOLIA_ENABLED 既有开关沿用且维持 false(backend/.env.template:56;未选 B 路线,无索引同步管线需要开关);
  • 本卡查询改造不新增 flag(行为升级属缺陷修复+能力补齐性质;如实现期需灰度,允许以 env 只读开关 TRILINGUAL_SEARCH_ENABLED(缺省 true)包裹 EXISTS 分支——回退语义与代码回退等价,二选一,不并存两套回滚机制);
  • reindex 说明:A 路线无索引重建需求(数据即译文表,事务同库);若未来启用 Algolia 路线,全量 reindex 步骤随届时 TDD 交付,本卡仅留演进记录。

前置决策点

前置决策置顶声明:F-search-backend-choice 已定档

  • 决策本体归 PILOT 决策一所有(打样 run-64960599a217,2026-10-25),本卡消费其结论;
  • 选定分支声明:候选 A「深化 Medusa 路径」——q ILIKE 检索扩展至三语译文字段 + 翻译联动(4.1 A1 EXISTS 方案);ALGOLIA_ENABLED=false 维持默认关;本卡全部字段级设计按 A 分支冻结;
  • 未选分支备选记录:候选 B(启用 Algolia 改索引 schema)不废弃——降级为 AI-3.3-04 语义检索演进时的重估项(届时先做 @mercurjs/algolia 插件源码 node_modules 二次确认);备选资产留存:algolia-product.ts:5-31 单语 schema(三语化时加 locale/译文/synonym_expanded_query 字段)、medusa-config.ts:67-77 条件注册(启用开关现成);
  • 本决策解锁本卡字段级设计冻结(PILOT 解锁声明原文)。若未来复审改判 B 路线,4.1 A1 查询设计与 4.3 C1 分页方案仍为 B 路线的对照基线,不作废。

Blocker 二分表(阻塞什么 / 可并行做什么)

Blocker类型阻塞什么可并行做什么
F-search-backend-choice 已定档→解锁 technical_decision_free 字段级设计冻结已由 PILOT 决策一解锁;无残留阻塞 全卡可开工(本 TDD 即冻结产物);若未来复审改判 B 路线,A1 查询设计与 C1 分页方案仍为对照基线
R-search-100-cap Blocker code_defect(天花板) 阻塞检索优化验收有效性——不先解决,任何召回率改进都被 limit=100 截断(改前第 101 条起不可见) 全量索引/分页方案先行:4.3 C1 服务端分页为 4.2③/4.1 验收的前置步骤(实现顺序:分页修复 → q 跨语 → 三端链路);A3 回归用例先行编写(改造前必失败)

内部依赖与下游消费

方向条目关系
上游 AI-3.5-02(动态内容翻译) 数据依赖:译文产出管道(跨语召回质量的天花板在译文覆盖率,不在本卡查询链路);非串行阻塞——译文未就绪时搜索回落原语言,管道译文越全召回越好
上游 PILOT 决策记录 / TDD 模板 决策与骨架来源
下游 AI-3.3-03(共享术语库) 同义词注入消费本卡 query_expansion 槽位(4.4);实体 schema 冻结前两域联合评审(RK-14)
下游 AI-3.3-04(P2) 查询理解演进:消费 q+locale 上下文与 expandQuery 落点位;Algolia 重估项所有方

开放数值决策

  • DEC-5 跨语查询召回抽检合格率的基线与目标数值——基线立项定,本卡仅引用编号不内联臆造数值(外部决策点引用规范)。
  • DEC-5 检索时延指标 p95_query_latency 的目标数值——同属 DEC-5,随实现纳入。

附录:溯源表

本文档由 docs/reports/ai-module-prd/tdd/AI-3.3-02-trilingual-search.md(v1.0,2026-10-26)可读化重组生成。章节映射与处理方式如下:

本文档章节源文档章节处理方式
摘要文档头(元数据/depth_baseline/范围口径)+ §1 溯源与现状Summarized
关键概念§2 代码现状底座 + §3 缺口与目标状态Summarized
方案设计 · 设计红线§4.0 设计红线声明Preserved
方案设计 · 后端查询链路改造(A1)§4.1(三候选对比 + A1 字段级设计)+ §7.0 决策置顶Preserved
方案设计 · 三端搜索链路改造清单§4.2(①②③④)Preserved
方案设计 · 全量分页方案§4.3(C1/C2/C3 对比 + C1 落地)Preserved
方案设计 · 同义词扩展槽§4.4Preserved
方案设计 · 实现顺序 DAG§9 波次与排期适配(S1-S6 DAG + 估时表)Preserved
数据模型 / API / Workflow§4.5 数据模型汇总 + §4.6 API 契约汇总 + §4.8 Workflow 设计 + §5 AI 能力接入点Preserved
前端改动面与 i18n§4.7 前端触点与 i18n 三语 key 规划Preserved
验收与测试§8.1 验收标准(A1-A7)+ §8.2 测试策略与 L0 门禁Preserved
风险与降级(Blocker/风险卡/降级/迁移回滚)§3.2 缺口清单 + §5.3 降级与跳过路径 + §7.1 Blocker 二分表 + §10 迁移与回滚Preserved
前置决策点§7.0 前置决策置顶声明 + §7.1 Blocker 二分表 + §7.2 内部依赖Preserved
图 1 跨语查询链路时序§4.1 A1 字段级设计 + §5.1 数据依赖图Inferred(图示为对源文字与数据依赖图的转绘)
图 2 实现顺序 DAG§9 子任务 DAG 文字图Inferred(文字图转绘为流程图,依赖关系一致)
图 3 数据关系 ER 图§2.1① 译文表 schema + §4.5 数据模型汇总Inferred(按源 schema 字段转绘)
锚点复核约定

源 TDD 全部 file:line 锚点在其生成 run 中以 rg/sed 逐条复核;上游原始锚点与复核实际行号存在漂移处以「来源锚点(复核:实际行号)」双写标注,内容实质均验证成立(漂移双写共 7 处,详见源文档尾注)。本页沿用源文档复核后的锚点表述。