跳转到内容

第 5 周:从问题合同做出确定性垂直薄片

直接答案: 第一个工程交付不应该是“搭好 AI 架构”,而应该是一个窄但完整的确定性薄片:一个获授权角色提交结构化任务,服务端校验输入和身份,从关系数据中取得当前适用且可追溯的政策;缺字段、冲突或特殊任务进入明确的复核状态。先证明普通软件能正确表达任务和失败,再决定以后是否需要模型。

本周只增加一个难度:做出第一个代码闭环

标题“本周只增加一个难度:做出第一个代码闭环”

第 4 周已经决定“为什么值得探索”;第 5 周只验证“能否用最简单、可解释的软件跑通目标任务”。

Discovery Brief v1
→ 一个角色和触发
→ 结构化输入
→ 服务端身份与业务校验
→ 关系数据查询
→ 成功或明确异常
→ 来源、版本、状态与测试证据

所谓垂直薄片,不是只写数据库、只画界面或只建 API。它从用户触发一直走到可见结果,并穿过实现所需的最少层次。所谓确定性,是同一版本、同一身份和同一输入产生可预测结果;不是“永远不会失败”。

先看完成品:北辰政策确认薄片

标题“先看完成品:北辰政策确认薄片”

以下公司、身份、政策、请求、状态和结果全部是固定合成教学示例

- Brief:northstar-discovery-brief-v1(课程模拟批准)
- 角色:获授权普通客服测试身份
- 任务:套餐升级后的差价政策确认
- 正常结果:返回唯一当前适用政策及来源
- 异常结果:缺字段、政策冲突或特殊任务进入 needs_review
- 非目标:生成回复、发送回复、主管批准、真实数据和生产部署
项目 完成示例
触发 客服在计费任务中请求核对政策
可信身份 服务端测试会话中的 support_agent_fixture;不接受请求体自报角色
必需输入 task_typeregionpurchased_at
业务查询 找到在购买时间和地区适用、当前有效且普通客服可见的政策版本
成功输出 eligible、政策 ID、版本、生效期、来源 ID 和可核验说明
异常输出 needs_review 加安全原因码;不猜测、不返回受限正文
数据变化 保存一次任务查询及终态,不发送客户回复
可验证证据 响应、数据库任务记录和测试对同一 request_id 一致
policy_id version task_type region effective_from effective_to access_level source_id
POL-PLAN-01 3 plan_change east 2026-08-01 2026-12-31 support KB-BILLING-2026-08
POL-PLAN-01 2 plan_change east 2026-01-01 2026-07-31 support KB-BILLING-2026-01
POL-REFUND-09 4 special_refund east 2026-07-01 2026-12-31 supervisor KB-REFUND-2026-07

它不代表真实政策。access_level 只是本周固定夹具中的最小字段;完整多角色权限矩阵留到第 7 周。

成功输入与预期结果

标题“成功输入与预期结果”

下面是与 HTTP 接口相似的行为示例。如果你使用函数、CLI 或其他接口,字段语义和可见结果保持一致即可。

{
"request_id": "req-w05-001",
"task_type": "plan_change",
"region": "east",
"purchased_at": "2026-08-10"
}

身份来自固定服务端测试会话,不出现在请求体。预期响应:

{
"request_id": "req-w05-001",
"status": "eligible",
"candidate": {
"policy_id": "POL-PLAN-01",
"version": 3,
"effective_from": "2026-08-01",
"effective_to": "2026-12-31",
"source_id": "KB-BILLING-2026-08"
},
"next_action": "review_source"
}

预期持久化状态:

request_id identity_fixture task_type result_status policy_id policy_version source_id
req-w05-001 support_agent_fixture plan_change eligible POL-PLAN-01 3 KB-BILLING-2026-08

返回政策候选不等于客服已经正确完成任务,更不等于客户得到了正确回复。本周只证明系统能够给出一个可追溯候选。

异常输入与预期结果

标题“异常输入与预期结果”

缺少地区:

{
"request_id": "req-w05-002",
"task_type": "plan_change",
"purchased_at": "2026-08-10"
}

预期结果:

{
"request_id": "req-w05-002",
"status": "needs_review",
"reason_code": "MISSING_REGION",
"candidate": null,
"next_action": "collect_required_field"
}

数据库不得偷偷保存一个默认地区,也不能返回看似正确的政策。

特殊退款由普通客服查询时:

{
"request_id": "req-w05-003",
"task_type": "special_refund",
"region": "east",
"purchased_at": "2026-08-10"
}

预期只返回安全状态:

{
"request_id": "req-w05-003",
"status": "needs_review",
"reason_code": "SPECIAL_TASK_REQUIRES_REVIEW",
"candidate": null,
"next_action": "escalate"
}

响应不能泄露主管政策正文,也不需要告诉普通客服“存在一份名为 POL-REFUND-09 的受限政策”。

从可信测试会话取得身份
→ 未知身份:denied
→ 校验必需字段
→ 缺失:needs_review / MISSING_FIELD
→ 查询当前适用且允许的政策
→ 恰好一条:eligible + 来源与版本
→ 零条:needs_review / NO_APPLICABLE_POLICY
→ 多条冲突:needs_review / CONFLICTING_POLICIES
→ 保存 request_id、输入摘要、终态和来源

这是行为规则,不是某种语言的代码。不要把完整政策正文放进普通日志。

用例 身份/输入 预期终态 必须核对
正常 已知客服;plan_change/east/2026-08-10 eligible 只返回 v3,来源和数据库一致
缺字段 已知客服;缺 region needs_review 无默认地区、无候选
特殊任务 已知客服;special_refund needs_review 不返回受限正文或记录 ID
未知身份 未知测试会话 denied 查询不执行,默认拒绝
版本边界 plan_change/east/2026-07-31 eligible 返回 v2,不错误使用 v3

本周最低要求独立实现正常路径和一条异常;完整示例的其他用例作为检查答案。第 7 周再增加真实的两角色动作闭环。

最小心智模型:先薄切任务,再选择组件

标题“最小心智模型:先薄切任务,再选择组件”

写代码前,用六个问题限制范围:

  1. 角色: 谁触发,身份从哪里可信取得?
  2. 输入: 做决定最少需要哪些字段?
  3. 规则: 哪些判断必须是确定性的?
  4. 结果: 用户能看到并核验什么?
  5. 异常: 什么情况下必须拒绝或升级?
  6. 非目标: 本周明确不做什么?

如果你需要十多个服务才能描述一条请求,先检查是不是把未来阶段塞进了本周。

为什么默认模块化单体

标题“为什么默认模块化单体”

本周的风险是任务和状态表达错,不是服务规模。一个进程或部署单元中仍可以分开接口、业务规则、数据访问和审计模块;这比先拆微服务更容易运行、测试和修改。

完成的 ADR 示例:

# ADR-001:使用模块化单体建立确定性基线
- 状态:接受(课程模拟)
- Brief:northstar-discovery-brief-v1
- 决定:用一个部署单元实现身份校验、政策查询和任务记录
- 原因:范围只有一个只读任务;需要快速验证业务状态和错误,而不是独立扩缩容
- 未选:微服务——当前没有独立团队、独立发布或扩缩容证据
- 未选:RAG/模型——输入和规则是结构化的,且尚未建立正式评测
- 代价:模块边界需要靠代码和测试维持
- 重新评审条件:出现独立发布、隔离、负载或组织所有权证据

第 1 天:写垂直薄片合同

标题“第 1 天:写垂直薄片合同”

discovery-brief-v1.md 逐项复制引用,不要重写问题:

# vertical-slice-contract
- 使用的 Brief 与版本:
- 本次角色和可信身份来源:
- 触发:
- 必需输入及业务含义:
- 成功终态与用户可见依据:
- 一个必须处理的异常:
- 允许的数据变化:
- 禁止的效果:
- 可观察测试:
- 什么结果会让我们返回 Brief:

北辰示例在发现“地区并非所有任务的可靠字段”时,必须回到 Brief 或记录新的未知,不能在代码中静默填默认值。

第 2 天:定义接口、关系和状态

标题“第 2 天:定义接口、关系和状态”

接口版本至少要能区分以后不兼容的变化。关系模型应保存:

  • 政策稳定 ID 与版本;
  • 任务类型、地区、生效和失效时间;
  • 来源 ID 与最小访问级别;
  • 请求 ID、可信测试身份、输入摘要和终态;
  • 创建时间和当前使用的 Brief/数据版本。

先写迁移,再插入固定合成夹具。约束应让缺少关键标识或非法日期关系尽早失败;不要只靠界面验证。

第 3 天:实现最小模块化单体

标题“第 3 天:实现最小模块化单体”

使用你已经熟悉的技术栈,按顺序实现:

  1. 从固定服务端会话取得受控测试身份;
  2. 校验结构化输入;
  3. 执行适用期、任务类型、地区和访问级别查询;
  4. 将零条、一条和多条候选映射到明确状态;
  5. 返回来源与版本;
  6. 保存安全的任务记录。

把运行应用所需的实际步骤写进自己的 README。因为本页没有提供代码仓库,所以不要把示例 JSON 当成已经存在的接口。

第 4 天:先写一个成功测试和一个失败测试

标题“第 4 天:先写一个成功测试和一个失败测试”

自动化测试必须同时核对:

  • 用户可见响应;
  • 数据库任务终态;
  • 候选政策和版本;
  • 缺字段或受限场景没有假成功;
  • 普通日志没有政策正文、密钥或个人信息。

只断言 HTTP 200 或函数没有抛异常,不算任务测试。

第 5 天:改变一个条件并记录 ADR

标题“第 5 天:改变一个条件并记录 ADR”

先演示 req-w05-001 完整路径,再任选一个变化独立完成:

  • 将购买日期改到版本边界;
  • 删除地区;
  • 制造两个同时适用版本;
  • 使用未知身份。

演示时按“输入 → 身份 → 查询 → 终态 → 数据库 → 测试”顺序,不只展示界面。

五天安排与可见产物

标题“五天安排与可见产物”
学习日 建议时间 当天动作 离开前必须有的结果
第 1 天 1.5 小时 从 Brief 写角色、输入、输出、异常和非目标 vertical-slice-contract.md
第 2 天 2 小时 定义接口、关系状态和首个迁移 接口契约、schema 与夹具
第 3 天 2–3 小时 实现最小应用和确定性查询 可按自有 README 运行的薄片
第 4 天 2 小时 实现成功和一条异常测试 响应、数据库与测试证据
第 5 天 1 小时 改一个条件,完成 ADR 和三层解释 演示记录、ADR、迁移练习

建议目录,不要求与你的框架目录完全相同:

fde-course/
└─ week-05/
├─ vertical-slice-contract.md
├─ api-contract/
├─ migrations/
├─ application/
├─ tests/
└─ adr-001-deterministic-baseline.md

本周通过需要同时满足:

  • 你自己的 README 写明实际运行方式,示例输入产生确定结果;
  • 接口、数据模型和测试都引用同一版 Brief;
  • 缺失、冲突和未知身份不会返回假成功;
  • 单一受控测试身份在服务端取得,未知身份默认拒绝;
  • 输出包含来源、版本和处理路径;
  • 自动化测试核对可见结果与数据库终态,而不只看状态码;
  • 没有模型、向量检索、Agent、MCP 或微服务拆分;
  • 没有把本地运行写成生产、采用或业务效果。
常见失败 诊断信号 恢复动作
先写大量代码再找任务 有很多模块,却说不清一个请求 删除旁支,退回薄片合同
一开始拆微服务 调试主要花在网络和部署 合并为模块化单体,保留重评条件
只有成功 Demo 缺字段仍得到政策 增加明确的拒绝或复核终态
请求体自报身份 修改 role 字段即可越权 从服务端测试会话取得身份,默认拒绝
输出看似正确但无来源 只有政策文字或“可退款” 返回来源 ID、版本、生效期和处理状态
为简化而填默认字段 缺地区被当作 east 进入 needs_review,回到任务和数据证据

独立迁移:供应链订单状态核对

标题“独立迁移:供应链订单状态核对”

不要复制政策字段。为“计划员核对订单当前状态”重新完成:

  1. 写明角色、触发、订单 ID、承运商和事件时间;
  2. 定义“已确认”“数据冲突”“来源过期”和“需要人工核对”状态;
  3. 提供一条成功输入、一条 ERP 与承运商状态冲突输入及预期结果;
  4. 说明为什么冲突时不能自动选择更新时间更晚的一方;
  5. 写一项会让你返回供应链 Brief 的新证据。

如果只是把 policy_id 改成 order_id,却没有改变时效、来源所有权和错误代价,迁移未通过。

用同一薄片向三类人解释

标题“用同一薄片向三类人解释”
  • 老板版: 这个薄片验证了哪项交付风险,为什么先不用 AI,下一步投入取决于什么。
  • 一线版: 系统只改变任务的哪一步,何时会拒绝或升级,用户怎样查看依据。
  • 工程版: Brief 版本怎样进入接口、关系状态、服务端身份、查询和测试;哪些能力明确留到以后。

第 6 周不扩大任务范围,只把本周固定夹具替换成有合同、来源、质量状态、幂等和重放的数据入口。

核验日期:2026-08-25。 北辰 Brief、接口、关系数据、状态、请求、响应、ADR 和测试矩阵均为本教程的合成材料或原创设计。它们不代表真实 API 标准、客户数据、生产结果、行业架构或任何模型效果。