# Service Guide Profile 强类型化实施方案

## 1. 目标

本方案聚焦于把当前 Service Guide 的 profile 从宽松 dict 逐步收敛为可验证、可演进的 API 契约。

本轮不追求“一次性把所有字段彻底锁死”，而是优先在 API 边界落地第一阶段强类型：

1. 把已经稳定且高频使用的字段改成显式模型
2. 保留未来扩展字段兼容性，避免阻塞抽取器迭代
3. 用测试验证 typed profile 不会破坏现有接口行为

## 1.1 当前已做的

截至 2026-03-17，当前已经完成以下内容：

1. API 边界首批强类型化
   - ServiceGuideDetailResponse 和 ServiceGuideExtractPreviewResponse 已切换为 typed profile。

2. 共享 profile 模型抽取
   - 已抽出 app.schemas.service_guide_profile，避免 core 依赖 API 层模型。

3. extractor 内部输出 typed 化
   - ServiceGuideExtractor 已直接返回 StandardServiceGuideProfile。

4. ingest pipeline 显式序列化
   - 写入 OpenSearch 前统一对 typed profile 做 model_dump，避免模型对象直接混入索引写入。

5. 定向回归测试已补齐
   - schema、API、extractor、ingest pipeline 的相关回归已覆盖并通过。

## 1.2 以后再做的

后续继续推进，但不作为当前阻塞项的内容如下：

1. root_fields / bindings / quality / artifacts 的进一步 typed 化
2. remedies、result_info、region_bindings 等低稳定字段继续细化
3. Guide detail / summary / related / quality / compare / admin 审核接口复用统一 typed contract
4. 前端 TypeScript 类型、OpenAPI 示例和接口 envelope 的进一步收敛
5. 评估 ServiceGuideDetailResponse 是否从“顶层摘要字段 + profile”收敛为仅保留 profile

## 2. 当前需要改进的

### 2.1 立即实施

1. API schema 目前仍把 profile 暴露为 dict
   - 问题：OpenAPI 无法展开字段，联调与文档价值有限。

2. 稳定字段缺少结构约束
   - 问题：materials、fees、consultation_and_supervision 等字段拼写或类型漂移只能在运行时发现。

3. preview schema 也没有复用同一份契约
   - 问题：正式详情与抽取预览容易逐步分叉。

4. 缺少针对 typed profile 的单测
   - 问题：即便接入了 model，也无法保证兼容未来扩展字段。

### 2.2 本轮不做但需要保留设计位

1. root_fields / bindings / quality 在内部继续彻底 typed 化
   - 原因：当前优先收紧 profile 主体，周边派生结构继续保持低风险演进。

2. 对所有嵌套字段做强约束校验
   - 原因：部分区块如 remedies、result_info、region_bindings 仍在演进，当前更适合保留弹性。

3. Guide compare / admin 审核 / PATCH 写回的 typed command model
   - 原因：这些接口尚未定型，应在页面和 API 收敛后再细化。

4. ServiceGuideDetailResponse 的 envelope 收敛
   - 当前保留 profile_id / doc_id / scene_type / guide_version 顶层字段，方便调用方无需 drill into profile 即可读取摘要标识。
   - 后续在 Guide detail / summary / related 系列接口定型后，再统一评估是否简化为仅保留 profile。

## 3. 分期策略

### Phase 1：API 边界首批强类型

本轮实施：

1. 新增 StandardServiceGuideProfile 顶层模型
2. 为 document_info、matter_identity、basic_info、process_info、materials、fees、legal_basis、consultation_and_supervision、service_windows、bindings、quality 建立嵌套模型
3. 模型统一开启 extra=allow，兼容未来字段
4. ServiceGuideDetailResponse / ServiceGuideExtractPreviewResponse 改为返回 typed profile
5. 增加 schema 与 API 单测

验收标准：

1. 详情接口返回的 profile 可按属性访问稳定字段
2. 未来扩展字段不会因为强类型而丢失
3. 现有 service guide API / extractor 相关测试保持通过

### Phase 2：入库链路 typed 化

本轮已完成基础落地，后续继续深化：

1. 已完成：抽取器内部 profile 输出改为 StandardServiceGuideProfile
2. 已完成：ingest pipeline 在写 ES 前统一显式 model_dump
3. 后续：让质量评分、warnings、bindings 在内部流转时也基于模型对象

### Phase 3：业务场景 typed 化

后续实施：

1. Guide detail / summary / related / quality API 使用统一 typed schema
2. Guide compare API 引入显式 diff model
3. Admin 审核、人工修正、PATCH 写回使用独立 command model
4. 字段级 evidence payload 也切换到 typed contract

## 4. 本次代码落地范围

本次改动已落地：

1. backend/app/api/schemas/service_guide.py
   - 新增首批强类型 profile 模型
   - 保留 extra=allow 以兼容未来扩展字段

2. backend/app/schemas/service_guide_profile.py
   - 抽出共享 profile 模型，供 API 和 core 复用，避免 core 依赖 API 层

3. backend/app/core/service_guide_extractor.py
   - extractor 直接返回 StandardServiceGuideProfile

4. backend/app/core/ingest_pipeline.py
   - 写入 OpenSearch 前统一对 typed profile 做 model_dump

5. backend/tests/test_service_guide_schema_unit.py
   - 验证 typed profile 能保留未来扩展字段
   - 验证 preview schema 会自动转成 typed profile

6. backend/tests/test_service_guide_api_unit.py
   - 验证 by-doc 详情接口返回 typed profile

7. backend/tests/test_ingest_pipeline_service_guide_unit.py
   - 验证 ingest pipeline 会把 typed profile 显式序列化后写入 ES

## 4.1 当前已确认保留的设计

1. ServiceGuideDetailResponse 仍保留顶层 profile_id / doc_id / scene_type / guide_version
   - 这是当前的 API 易用性设计，不是已知缺陷。

2. profile 内部保留同名字段
   - 这是因为 profile 本身代表完整 Guide 结构化对象，顶层字段只是摘要便捷取值层。

3. 当前不做 detail response 的 breaking change
   - 等 Guide detail 系列接口真正落地并出现明确消费模式后，再决定是否收敛响应形态。

## 5. 以后再实现的内容

1. remedies、result_info、region_bindings 等低稳定字段进一步细化类型
2. cross_region_service 从当前首批结构继续演进为更严格的地区对比模型
3. root_fields / bindings / quality / artifacts 的内部全链路 typed 化
4. compare API、admin 审核 API、人工修正命令模型
5. 基于 typed profile 生成更细的 OpenAPI 示例和前端 TS 类型同步
6. 在 Guide detail API 收敛阶段，统一评估 detail response 是否改为仅保留 profile

## 6. 结论

这次不是“把 dict 一次性彻底废掉”，而是先把最有价值的 API 边界收紧，建立正式契约，同时保留对未来字段的兼容。这样可以先拿到文档、校验、重构安全性三方面收益，再逐步向入库链路和业务接口继续推进。