ethan-skill 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/cli/index.js +1019 -1
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/mcp/server.d.ts.map +1 -1
  4. package/dist/mcp/server.js +206 -1
  5. package/dist/mcp/server.js.map +1 -1
  6. package/dist/skills/27-tech-debt.d.ts +3 -0
  7. package/dist/skills/27-tech-debt.d.ts.map +1 -0
  8. package/dist/skills/27-tech-debt.js +149 -0
  9. package/dist/skills/27-tech-debt.js.map +1 -0
  10. package/dist/skills/28-api-mock.d.ts +3 -0
  11. package/dist/skills/28-api-mock.d.ts.map +1 -0
  12. package/dist/skills/28-api-mock.js +272 -0
  13. package/dist/skills/28-api-mock.js.map +1 -0
  14. package/dist/skills/29-data-migration.d.ts +3 -0
  15. package/dist/skills/29-data-migration.d.ts.map +1 -0
  16. package/dist/skills/29-data-migration.js +331 -0
  17. package/dist/skills/29-data-migration.js.map +1 -0
  18. package/dist/skills/30-llm-feature.d.ts +3 -0
  19. package/dist/skills/30-llm-feature.d.ts.map +1 -0
  20. package/dist/skills/30-llm-feature.js +328 -0
  21. package/dist/skills/30-llm-feature.js.map +1 -0
  22. package/dist/skills/31-threat-model.d.ts +3 -0
  23. package/dist/skills/31-threat-model.d.ts.map +1 -0
  24. package/dist/skills/31-threat-model.js +240 -0
  25. package/dist/skills/31-threat-model.js.map +1 -0
  26. package/dist/skills/32-green-code.d.ts +3 -0
  27. package/dist/skills/32-green-code.d.ts.map +1 -0
  28. package/dist/skills/32-green-code.js +346 -0
  29. package/dist/skills/32-green-code.js.map +1 -0
  30. package/dist/skills/33-service-catalog.d.ts +3 -0
  31. package/dist/skills/33-service-catalog.d.ts.map +1 -0
  32. package/dist/skills/33-service-catalog.js +334 -0
  33. package/dist/skills/33-service-catalog.js.map +1 -0
  34. package/dist/skills/34-mobile-review.d.ts +3 -0
  35. package/dist/skills/34-mobile-review.d.ts.map +1 -0
  36. package/dist/skills/34-mobile-review.js +390 -0
  37. package/dist/skills/34-mobile-review.js.map +1 -0
  38. package/dist/skills/35-data-pipeline.d.ts +3 -0
  39. package/dist/skills/35-data-pipeline.d.ts.map +1 -0
  40. package/dist/skills/35-data-pipeline.js +392 -0
  41. package/dist/skills/35-data-pipeline.js.map +1 -0
  42. package/dist/skills/36-ml-experiment.d.ts +3 -0
  43. package/dist/skills/36-ml-experiment.d.ts.map +1 -0
  44. package/dist/skills/36-ml-experiment.js +415 -0
  45. package/dist/skills/36-ml-experiment.js.map +1 -0
  46. package/dist/skills/index.d.ts +10 -0
  47. package/dist/skills/index.d.ts.map +1 -1
  48. package/dist/skills/index.js +41 -1
  49. package/dist/skills/index.js.map +1 -1
  50. package/dist/skills/pipeline.d.ts.map +1 -1
  51. package/dist/skills/pipeline.js +35 -0
  52. package/dist/skills/pipeline.js.map +1 -1
  53. package/dist/skills/skills.test.js +3 -3
  54. package/dist/skills/skills.test.js.map +1 -1
  55. package/package.json +1 -1
  56. package/rules/claude-code/CLAUDE.md +2963 -3
  57. package/rules/cline/.clinerules +2805 -2
  58. package/rules/codebuddy/CODEBUDDY.md +2913 -2
  59. package/rules/continue/.continuerules +2805 -2
  60. package/rules/copilot/copilot-instructions.md +2883 -2
  61. package/rules/cursor/.cursorrules +2952 -2
  62. package/rules/cursor/smart-flow.mdc +2952 -2
  63. package/rules/jetbrains/smart-flow.md +2883 -2
  64. package/rules/lingma/smart-flow.md +2904 -3
  65. package/rules/windsurf/.windsurf/rules/smart-flow.md +2884 -3
  66. package/rules/zed/smart-flow.rules +2794 -1
@@ -1,5 +1,5 @@
1
- # Ethan v1.11.0
2
- # Generated: 2026-04-06T14:12:22.298Z
1
+ # Ethan v1.12.0
2
+ # Generated: 2026-04-06T16:19:27.840Z
3
3
  # Source: https://github.com/aokiz-ek/smart-flow-skill
4
4
 
5
5
  Ethan。当用户输入以下触发词时,按对应 Skill 的步骤执行。
@@ -87,6 +87,16 @@ Ethan。当用户输入以下触发词时,按对应 Skill 的步骤执行。
87
87
  - **设计模式**: 设计模式 | design pattern | design patterns | 模式
88
88
  - **Spec Proposal**: spec proposal | openspec proposal | 生成提案 | 变更提案
89
89
  - **Spec Review(意图审查)**: spec review | intent review | 意图审查 | openspec review
90
+ - **技术债追踪**: 技术债 | tech debt | technical debt | 技术债追踪
91
+ - **API Mock 服务**: api mock | mock service | mock 服务 | 接口 mock
92
+ - **数据迁移助手**: 数据迁移 | data migration | schema migration | db migration
93
+ - **LLM 功能设计助手**: llm feature | ai feature | rag | llm 功能
94
+ - **威胁建模**: 威胁建模 | threat model | threat modeling | stride
95
+ - **绿色编码实践**: 绿色编码 | green code | green software | carbon footprint
96
+ - **服务目录管理**: 服务目录 | service catalog | service registry | backstage
97
+ - **移动端专项审查**: 移动端审查 | mobile review | ios review | android review
98
+ - **数据管道设计**: 数据管道 | data pipeline | etl | elt
99
+ - **ML 实验管理**: ml experiment | mlops | model training | 模型训练
90
100
 
91
101
  ---
92
102
 
@@ -3724,5 +3734,2798 @@ spec 中定义的场景或需求在代码中未体现:
3724
3734
 
3725
3735
  输出格式:Spec Review 报告:意图对齐矩阵 + 三级偏差列表(🔴意图偏差 / 🟡遗漏需求 / 💡超范围)+ 审查结论
3726
3736
 
3737
+ ## 技术债追踪
3738
+
3739
+ 触发:技术债 | tech debt | technical debt | 技术债追踪
3740
+ 说明:识别、量化、排序技术债,生成偿还路线图与预防机制
3741
+
3742
+ 1. 技术债识别与分类
3743
+ 扫描代码库,从以下维度识别技术债:
3744
+
3745
+ **四大技术债类型**
3746
+
3747
+ | 类型 | 检测手段 | 典型表现 |
3748
+ |------|---------|---------|
3749
+ | **代码债** | TODO/FIXME/HACK 注释统计 | 注释标记超过 50 个/千行 |
3750
+ | **设计债** | 圈复杂度 > 10,类长度 > 500 行 | 上帝类、面条代码、深层继承 |
3751
+ | **测试债** | 覆盖率 < 60%,测试缺失模块 | 核心模块无测试,回归靠手工 |
3752
+ | **依赖债** | 过时依赖、已弃用 API 调用 | 主版本落后 2+ 个版本 |
3753
+
3754
+ **识别命令参考**
3755
+ ```bash
3756
+ # 统计 TODO/FIXME
3757
+ grep -rn "TODO\|FIXME\|HACK\|XXX" src/ --include="*.ts" | wc -l
3758
+
3759
+ # 查找超长文件
3760
+ find src -name "*.ts" | xargs wc -l | sort -rn | head -20
3761
+
3762
+ # 重复代码检测(需安装 jscpd)
3763
+ npx jscpd src/ --min-lines 5 --reporters json
3764
+ ```
3765
+
3766
+ **输出**:技术债清单(分类 + 位置 + 初步评估)
3767
+
3768
+ 2. 债务量化评估
3769
+ 用"影响度 × 修复成本"矩阵评估每项技术债的优先级:
3770
+
3771
+ **评分维度**
3772
+
3773
+ | 维度 | 1分(低) | 3分(中) | 5分(高) |
3774
+ |------|---------|---------|---------|
3775
+ | **业务影响** | 边缘功能 | 常用功能 | 核心链路 |
3776
+ | **出错频率** | 极少触发 | 偶尔出现 | 频繁发生 |
3777
+ | **修复成本** | < 0.5天 | 0.5~2天 | > 2天 |
3778
+ | **扩散风险** | 独立模块 | 少量依赖 | 多处依赖 |
3779
+
3780
+ **优先级 = 影响度(业务影响 × 出错频率) ÷ 修复成本**
3781
+
3782
+ **T恤 Size 对照**
3783
+ - 🔴 **Critical(>8分)**:阻碍新功能开发,必须本 Sprint 处理
3784
+ - 🟠 **High(5-8分)**:影响团队效率,下个 Sprint 安排
3785
+ - 🟡 **Medium(3-5分)**:纳入季度技术改进计划
3786
+ - 🟢 **Low(<3分)**:有时间再处理,记录即可
3787
+
3788
+ **输出**:技术债优先级矩阵(标注 T恤 Size)
3789
+
3790
+ 3. 偿还路线图
3791
+ 按"高影响、低成本"优先原则,制定可落地的偿还计划:
3792
+
3793
+ **Sprint 规划原则**
3794
+ - 每个 Sprint 分配 **15-20% 时间**用于技术债偿还(债务预算)
3795
+ - Critical 债务:独立 Story,本 Sprint 必须完成
3796
+ - High 债务:与新功能并行,2 个 Sprint 内完成
3797
+ - Medium 债务:季度 Hackathon 集中处理
3798
+
3799
+ **路线图模板**
3800
+ ```
3801
+ Sprint N(当前)
3802
+ 🔴 [Critical] 拆分 UserService 上帝类 → 3个子服务 2天
3803
+ 🔴 [Critical] 修复登录模块 XXX 标记的并发 Bug 1天
3804
+
3805
+ Sprint N+1
3806
+ 🟠 [High] 提升 Payment 模块测试覆盖率至 80% 2天
3807
+ 🟠 [High] 替换废弃的 axios v0.21 → v1.x 1天
3808
+
3809
+ Q+1 季度计划
3810
+ 🟡 [Medium] 重构订单模块(圈复杂度>15的5个函数) 5天
3811
+ 🟡 [Medium] 清理 87 个 TODO 注释并创建对应 Issue 3天
3812
+ ```
3813
+
3814
+ **输出**:按 Sprint 排期的技术债偿还路线图
3815
+
3816
+ 4. 预防机制
3817
+ 建立长效机制,让技术债不再悄悄累积:
3818
+
3819
+ **自动化门禁(CI/CD 集成)**
3820
+ ```yaml
3821
+ # .github/workflows/debt-check.yml
3822
+ - name: 技术债扫描
3823
+ run: |
3824
+ # TODO 数量检查
3825
+ TODO_COUNT=$(grep -rn "TODO\|FIXME" src/ | wc -l)
3826
+ if [ $TODO_COUNT -gt $TODO_THRESHOLD ]; then
3827
+ echo "❌ TODO 数量超过阈值: $TODO_COUNT > $TODO_THRESHOLD"
3828
+ exit 1
3829
+ fi
3830
+ # 圈复杂度检查(使用 complexity-report 或 ESLint)
3831
+ npx eslint src/ --rule '{"complexity": ["error", 10]}'
3832
+ ```
3833
+
3834
+ **团队规范**
3835
+ - **Boy Scout 原则**:离开时让代码比你来时更干净(每次 PR 消灭 1 个技术债)
3836
+ - **TODO 转 Issue**:所有 TODO 必须关联 GitHub Issue,不得孤立存在
3837
+ - **债务预算制度**:每个 Sprint 强制预留 15% 时间偿还债务
3838
+ - **月度债务评审**:每月 Review 技术债清单,更新优先级
3839
+
3840
+ **技术债仪表盘指标**
3841
+ - TODO/FIXME 总数趋势(目标:每月下降 10%)
3842
+ - 平均圈复杂度(目标:< 8)
3843
+ - 测试覆盖率(目标:> 80%)
3844
+ - 依赖过时率(目标:0 个 Critical 漏洞)
3845
+
3846
+ **输出**:CI 门禁配置 + 团队规范文档 + 仪表盘指标定义
3847
+
3848
+ 输出格式:技术债地图(分级清单)+ 优先级矩阵(影响×成本)+ Sprint 偿还路线图 + CI 门禁配置 + 预防规范
3849
+
3850
+ ## API Mock 服务
3851
+
3852
+ 触发:api mock | mock service | mock 服务 | 接口 mock
3853
+ 说明:根据接口定义生成 MSW/JSON Server Mock 配置,支持动态数据与边界场景模拟
3854
+
3855
+ 1. 接口分析与 Mock 范围确定
3856
+ 分析需要 Mock 的接口,建立清单:
3857
+
3858
+ **接口信息收集**
3859
+ - OpenAPI/Swagger 文档路径(如 `api/swagger.json`)
3860
+ - 手工接口描述(方法、路径、请求/响应结构)
3861
+ - 需要模拟的特殊场景(401/403/500/超时/慢响应)
3862
+
3863
+ **Mock 范围分类**
3864
+ ```
3865
+ 📋 待 Mock 接口清单
3866
+ ├── 认证接口
3867
+ │ ├── POST /api/auth/login ← 成功 / 密码错误 / 账号锁定
3868
+ │ └── POST /api/auth/refresh ← 成功 / token 过期
3869
+ ├── 用户接口
3870
+ │ ├── GET /api/users ← 列表 / 空列表 / 分页
3871
+ │ └── GET /api/users/:id ← 成功 / 404
3872
+ └── 业务接口
3873
+ ├── POST /api/orders ← 成功 / 库存不足 / 支付失败
3874
+ └── GET /api/orders?status=... ← 各状态过滤
3875
+ ```
3876
+
3877
+ **输出**:接口 Mock 清单(含边界场景枚举)
3878
+
3879
+ 2. Mock 方案选型
3880
+ 根据项目特点选择最合适的 Mock 方案:
3881
+
3882
+ **选型矩阵**
3883
+
3884
+ | 方案 | 适用场景 | 优点 | 缺点 |
3885
+ |------|---------|------|------|
3886
+ | **MSW(推荐)** | React/Vue 前端项目 | 拦截真实网络请求,零侵入,支持 Jest/Vitest | 需要 Service Worker |
3887
+ | **JSON Server** | REST API 快速原型 | 零代码,自动 CRUD | 功能有限,不支持复杂逻辑 |
3888
+ | **Mirage.js** | Ember/复杂 SPA | 内置数据库,关系模型支持 | 包较大,配置复杂 |
3889
+ | **Nock** | Node.js 单元测试 | 精确控制 HTTP 请求 | 仅限 Node 环境 |
3890
+
3891
+ **推荐组合**:
3892
+ - 开发阶段:**MSW**(浏览器端拦截)
3893
+ - 单元测试:**MSW + @mswjs/data**(内存数据库)
3894
+ - 快速原型:**JSON Server**(5分钟启动)
3895
+
3896
+ **输出**:选型决策 + 安装命令
3897
+
3898
+ 3. MSW handlers 生成
3899
+ 生成 MSW(Mock Service Worker)拦截处理器:
3900
+
3901
+ **安装**
3902
+ ```bash
3903
+ npm install msw --save-dev
3904
+ npx msw init public/ --save
3905
+ ```
3906
+
3907
+ **handlers.ts 模板**
3908
+ ```typescript
3909
+ // src/mocks/handlers.ts
3910
+ import { http, HttpResponse, delay } from 'msw';
3911
+ import { faker } from '@faker-js/faker';
3912
+
3913
+ export const handlers = [
3914
+ // ─── 认证接口 ──────────────────────────────────────
3915
+ http.post('/api/auth/login', async ({ request }) => {
3916
+ const { email, password } = await request.json() as any;
3917
+
3918
+ // 模拟特殊场景
3919
+ if (password === 'wrong') {
3920
+ return HttpResponse.json(
3921
+ { code: 401, message: '密码错误' },
3922
+ { status: 401 }
3923
+ );
3924
+ }
3925
+ if (email === 'locked@test.com') {
3926
+ return HttpResponse.json(
3927
+ { code: 423, message: '账号已锁定,请联系管理员' },
3928
+ { status: 423 }
3929
+ );
3930
+ }
3931
+
3932
+ // 正常响应
3933
+ return HttpResponse.json({
3934
+ token: faker.string.uuid(),
3935
+ user: { id: faker.string.uuid(), email, name: faker.person.fullName() },
3936
+ });
3937
+ }),
3938
+
3939
+ // ─── 用户列表(分页)──────────────────────────────
3940
+ http.get('/api/users', ({ request }) => {
3941
+ const url = new URL(request.url);
3942
+ const page = Number(url.searchParams.get('page') ?? 1);
3943
+ const pageSize = Number(url.searchParams.get('pageSize') ?? 10);
3944
+
3945
+ const total = 87;
3946
+ const items = Array.from({ length: Math.min(pageSize, total - (page - 1) * pageSize) }, () => ({
3947
+ id: faker.string.uuid(),
3948
+ name: faker.person.fullName(),
3949
+ email: faker.internet.email(),
3950
+ createdAt: faker.date.past().toISOString(),
3951
+ }));
3952
+
3953
+ return HttpResponse.json({ items, total, page, pageSize });
3954
+ }),
3955
+
3956
+ // ─── 慢响应模拟 ────────────────────────────────────
3957
+ http.get('/api/slow-endpoint', async () => {
3958
+ await delay(3000); // 模拟 3 秒延迟
3959
+ return HttpResponse.json({ data: 'slow response' });
3960
+ }),
3961
+
3962
+ // ─── 网络错误模拟 ──────────────────────────────────
3963
+ http.get('/api/network-error', () => {
3964
+ return HttpResponse.error(); // 模拟网络断开
3965
+ }),
3966
+ ];
3967
+ ```
3968
+
3969
+ **browser.ts(浏览器初始化)**
3970
+ ```typescript
3971
+ // src/mocks/browser.ts
3972
+ import { setupWorker } from 'msw/browser';
3973
+ import { handlers } from './handlers';
3974
+ export const worker = setupWorker(...handlers);
3975
+ ```
3976
+
3977
+ **server.ts(Node.js/测试环境)**
3978
+ ```typescript
3979
+ // src/mocks/server.ts
3980
+ import { setupServer } from 'msw/node';
3981
+ import { handlers } from './handlers';
3982
+ export const server = setupServer(...handlers);
3983
+ ```
3984
+
3985
+ **输出**:完整 handlers.ts + browser.ts + server.ts
3986
+
3987
+ 4. JSON Server 配置
3988
+ 生成 JSON Server 快速 REST Mock 配置:
3989
+
3990
+ **安装与启动**
3991
+ ```bash
3992
+ npm install json-server --save-dev
3993
+ # 启动:json-server --watch db.json --port 3001 --routes routes.json
3994
+ ```
3995
+
3996
+ **db.json(数据库)**
3997
+ ```json
3998
+ {
3999
+ "users": [
4000
+ { "id": "1", "name": "张三", "email": "zhang@test.com", "role": "admin" },
4001
+ { "id": "2", "name": "李四", "email": "li@test.com", "role": "user" }
4002
+ ],
4003
+ "orders": [
4004
+ { "id": "101", "userId": "1", "status": "pending", "amount": 299.00, "createdAt": "2024-01-15" },
4005
+ { "id": "102", "userId": "2", "status": "completed", "amount": 599.00, "createdAt": "2024-01-16" }
4006
+ ],
4007
+ "products": [
4008
+ { "id": "P001", "name": "商品A", "price": 99.00, "stock": 100 }
4009
+ ]
4010
+ }
4011
+ ```
4012
+
4013
+ **routes.json(路由重写)**
4014
+ ```json
4015
+ {
4016
+ "/api/*": "/$1",
4017
+ "/api/v1/*": "/$1",
4018
+ "/api/users/:id/orders": "/orders?userId=:id"
4019
+ }
4020
+ ```
4021
+
4022
+ **package.json 脚本**
4023
+ ```json
4024
+ {
4025
+ "scripts": {
4026
+ "mock": "json-server --watch src/mocks/db.json --port 3001 --routes src/mocks/routes.json",
4027
+ "dev:mock": "concurrently \"npm run mock\" \"npm run dev\""
4028
+ }
4029
+ }
4030
+ ```
4031
+
4032
+ **输出**:db.json + routes.json + npm 脚本
4033
+
4034
+ 5. 动态数据与边界场景策略
4035
+ 用 faker.js 生成真实感数据,覆盖各类边界场景:
4036
+
4037
+ **faker.js 常用生成器速查**
4038
+ ```typescript
4039
+ import { faker } from '@faker-js/faker/locale/zh_CN'; // 中文数据
4040
+
4041
+ // 个人信息
4042
+ faker.person.fullName() // 王小明
4043
+ faker.internet.email() // user@example.com
4044
+ faker.phone.number() // 138-1234-5678
4045
+ faker.date.birthdate() // 生日
4046
+
4047
+ // 地址
4048
+ faker.location.city() // 上海
4049
+ faker.location.streetAddress() // 延安路 123 号
4050
+
4051
+ // 业务数据
4052
+ faker.string.uuid() // UUID
4053
+ faker.number.int({ min:1, max:100 }) // 随机整数
4054
+ faker.helpers.arrayElement(['pending', 'active', 'closed']) // 随机枚举
4055
+ faker.helpers.multiple(() => faker.person.fullName(), { count: 10 }) // 批量生成
4056
+ ```
4057
+
4058
+ **边界场景 Checklist**
4059
+ - [ ] 空列表(返回 `{ items: [], total: 0 }`)
4060
+ - [ ] 单条数据(边界分页)
4061
+ - [ ] 超长字符串(名称 > 100 字符)
4062
+ - [ ] 特殊字符(`<script>`、SQL 注入字符串)
4063
+ - [ ] 极大数字(金额 = 999999999.99)
4064
+ - [ ] 时区边界(UTC+8 vs UTC)
4065
+ - [ ] 并发响应竞态(两个请求同时返回)
4066
+ - [ ] 401 token 过期 → 自动刷新
4067
+ - [ ] 503 服务不可用 → 降级 UI
4068
+
4069
+ **测试集成(Vitest)**
4070
+ ```typescript
4071
+ // setupTests.ts
4072
+ import { beforeAll, afterEach, afterAll } from 'vitest';
4073
+ import { server } from './mocks/server';
4074
+
4075
+ beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
4076
+ afterEach(() => server.resetHandlers()); // 每个测试后重置
4077
+ afterAll(() => server.close());
4078
+ ```
4079
+
4080
+ **输出**:faker 数据工厂函数 + 边界场景 handlers + 测试集成配置
4081
+
4082
+ 输出格式:Mock 方案选型报告 + MSW handlers.ts + JSON Server db.json/routes.json + faker 数据工厂 + 边界场景 Checklist + 测试集成配置
4083
+
4084
+ ## 数据迁移助手
4085
+
4086
+ 触发:数据迁移 | data migration | schema migration | db migration
4087
+ 说明:评估迁移风险,生成 UP/DOWN 双向脚本,制定零停机迁移与回滚方案
4088
+
4089
+ 1. 迁移评估
4090
+ 在编写任何脚本之前,先全面评估迁移的范围和风险:
4091
+
4092
+ **评估清单**
4093
+
4094
+ | 维度 | 问题 | 影响 |
4095
+ |------|------|------|
4096
+ | **数据量** | 涉及多少行/多少 GB? | 决定迁移时长和窗口 |
4097
+ | **Schema 变更** | 新增/修改/删除了哪些列? | 影响向后兼容性 |
4098
+ | **外键约束** | 是否有级联影响? | 需要临时禁用约束 |
4099
+ | **业务流量** | 高峰期 QPS 是多少? | 影响迁移策略选择 |
4100
+ | **停机容忍** | 是否接受停机?接受多长? | 决定是否需要零停机方案 |
4101
+ | **数据一致性** | 允许最终一致性还是强一致? | 影响双写策略 |
4102
+
4103
+ **Schema 变更对比**
4104
+ ```sql
4105
+ -- 变更前
4106
+ CREATE TABLE users (
4107
+ id BIGINT PRIMARY KEY,
4108
+ username VARCHAR(50),
4109
+ created_at TIMESTAMP
4110
+ );
4111
+
4112
+ -- 变更后(新增 email 列,重命名 username → name)
4113
+ CREATE TABLE users (
4114
+ id BIGINT PRIMARY KEY,
4115
+ name VARCHAR(100), -- 原 username,扩大长度
4116
+ email VARCHAR(255), -- 新增,NOT NULL 需要默认值
4117
+ created_at TIMESTAMP,
4118
+ updated_at TIMESTAMP -- 新增审计列
4119
+ );
4120
+ ```
4121
+
4122
+ **停机时间估算**
4123
+ ```
4124
+ 数据行数: 5,000,000
4125
+ 迁移速度: ~10,000 行/秒(受磁盘 IO 限制)
4126
+ 估算时长: 5,000,000 ÷ 10,000 = 500 秒 ≈ 8.3 分钟
4127
+ 建议窗口: 低峰期(凌晨 2:00-4:00)
4128
+ ```
4129
+
4130
+ **输出**:迁移评估报告(范围 + 风险 + 时长估算 + 策略选择)
4131
+
4132
+ 2. 迁移脚本生成(UP/DOWN)
4133
+ 生成可逆的双向迁移脚本:
4134
+
4135
+ **Knex.js 迁移模板(Node.js)**
4136
+ ```typescript
4137
+ // migrations/20240115_add_email_rename_username.ts
4138
+ import { Knex } from 'knex';
4139
+
4140
+ export async function up(knex: Knex): Promise<void> {
4141
+ await knex.schema.alterTable('users', (table) => {
4142
+ // 1. 新增列(先加,允许 NULL,稍后回填)
4143
+ table.string('name', 100).nullable();
4144
+ table.string('email', 255).nullable();
4145
+ table.timestamp('updated_at').nullable();
4146
+ });
4147
+
4148
+ // 2. 数据回填(分批处理,避免锁表)
4149
+ const BATCH_SIZE = 1000;
4150
+ let offset = 0;
4151
+ while (true) {
4152
+ const rows = await knex('users')
4153
+ .select('id', 'username')
4154
+ .limit(BATCH_SIZE)
4155
+ .offset(offset);
4156
+ if (rows.length === 0) break;
4157
+
4158
+ await knex('users')
4159
+ .whereIn('id', rows.map((r) => r.id))
4160
+ .update((row: any) => ({ name: row.username }));
4161
+
4162
+ offset += BATCH_SIZE;
4163
+ }
4164
+
4165
+ // 3. 添加约束
4166
+ await knex.schema.alterTable('users', (table) => {
4167
+ table.string('name', 100).notNullable().alter();
4168
+ table.dropColumn('username'); // 危险操作!确认数据回填完成后执行
4169
+ });
4170
+ }
4171
+
4172
+ export async function down(knex: Knex): Promise<void> {
4173
+ // 回滚:恢复 username 列
4174
+ await knex.schema.alterTable('users', (table) => {
4175
+ table.string('username', 50).nullable();
4176
+ table.dropColumn('name');
4177
+ table.dropColumn('email');
4178
+ table.dropColumn('updated_at');
4179
+ });
4180
+
4181
+ // 回填 username(从备份表恢复)
4182
+ await knex.raw('UPDATE users u JOIN users_backup b ON u.id = b.id SET u.username = b.username');
4183
+ }
4184
+ ```
4185
+
4186
+ **Flyway SQL 迁移模板**
4187
+ ```sql
4188
+ -- V20240115__add_email_rename_username.sql
4189
+ BEGIN;
4190
+
4191
+ -- 阶段1:新增列
4192
+ ALTER TABLE users ADD COLUMN name VARCHAR(100);
4193
+ ALTER TABLE users ADD COLUMN email VARCHAR(255);
4194
+ ALTER TABLE users ADD COLUMN updated_at TIMESTAMP DEFAULT NOW();
4195
+
4196
+ -- 阶段2:数据回填
4197
+ UPDATE users SET name = username WHERE name IS NULL;
4198
+
4199
+ -- 阶段3:添加约束(回填完成后)
4200
+ ALTER TABLE users ALTER COLUMN name SET NOT NULL;
4201
+ CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
4202
+
4203
+ COMMIT;
4204
+ ```
4205
+
4206
+ **输出**:可执行的 UP/DOWN 迁移脚本
4207
+
4208
+ 3. 数据验证策略
4209
+ 迁移前后必须验证数据完整性:
4210
+
4211
+ **三阶段验证**
4212
+
4213
+ **① 迁移前基线(Baseline)**
4214
+ ```sql
4215
+ -- 记录迁移前状态
4216
+ CREATE TABLE migration_baseline AS
4217
+ SELECT
4218
+ COUNT(*) as total_rows,
4219
+ COUNT(DISTINCT id) as unique_ids,
4220
+ SUM(CASE WHEN email IS NOT NULL THEN 1 ELSE 0 END) as non_null_email,
4221
+ MD5(STRING_AGG(id::text, ',' ORDER BY id)) as row_checksum
4222
+ FROM users;
4223
+ ```
4224
+
4225
+ **② 迁移后验证(Post-Migration)**
4226
+ ```sql
4227
+ -- 行数对比
4228
+ SELECT
4229
+ (SELECT COUNT(*) FROM users) as after_count,
4230
+ (SELECT total_rows FROM migration_baseline) as before_count,
4231
+ (SELECT COUNT(*) FROM users) - (SELECT total_rows FROM migration_baseline) as diff;
4232
+
4233
+ -- 关键字段空值检查
4234
+ SELECT COUNT(*) as null_names FROM users WHERE name IS NULL;
4235
+ SELECT COUNT(*) as null_emails FROM users WHERE email IS NULL;
4236
+
4237
+ -- 数据一致性抽样(对比10%数据)
4238
+ SELECT u.id, u.name, b.username
4239
+ FROM users u
4240
+ JOIN users_backup b ON u.id = b.id
4241
+ WHERE u.name != b.username
4242
+ LIMIT 100;
4243
+ ```
4244
+
4245
+ **③ 业务验证(Smoke Test)**
4246
+ ```bash
4247
+ # 验证核心业务接口
4248
+ curl -X POST /api/auth/login -d '{"email":"test@example.com","password":"xxx"}'
4249
+ curl -X GET /api/users/1
4250
+ curl -X POST /api/orders -d '{"userId":1,"items":[...]}'
4251
+ ```
4252
+
4253
+ **验证通过标准**
4254
+ - [ ] 行数差异为 0(或符合预期的新增/删除)
4255
+ - [ ] 关键字段 NULL 率为 0
4256
+ - [ ] 数据抽样一致性 100%
4257
+ - [ ] Smoke Test 全部通过
4258
+ - [ ] 数据库慢查询无异常增加
4259
+
4260
+ **输出**:验证脚本套件 + 通过标准 Checklist
4261
+
4262
+ 4. 回滚方案
4263
+ 制定多层次回滚机制,确保任何阶段都可以安全撤退:
4264
+
4265
+ **三级回滚策略**
4266
+
4267
+ **Level 1:脚本级回滚(最快,< 5 分钟)**
4268
+ ```bash
4269
+ # 直接执行 DOWN 脚本
4270
+ npx knex migrate:down
4271
+ # 或
4272
+ flyway undo
4273
+ ```
4274
+ 适用条件:迁移后立即发现问题,数据量小
4275
+
4276
+ **Level 2:备份恢复(中速,5-30 分钟)**
4277
+ ```bash
4278
+ # 迁移前创建备份(必须!)
4279
+ pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME --table=users -f backup_users_$(date +%Y%m%d_%H%M%S).sql
4280
+
4281
+ # 恢复
4282
+ psql -h $DB_HOST -U $DB_USER -d $DB_NAME < backup_users_20240115_020000.sql
4283
+ ```
4284
+
4285
+ **Level 3:蓝绿数据库(最可靠,但成本高)**
4286
+ ```
4287
+ Green DB(新)→ 迁移成功 → 流量切到 Green
4288
+ Blue DB(旧)→ 保留 72 小时 → 确认无问题后关闭
4289
+ ```
4290
+
4291
+ **回滚决策树**
4292
+ ```
4293
+ 迁移执行中...
4294
+ ├── 数据验证失败?
4295
+ │ └── YES → 立即执行 Level 1 回滚(DOWN 脚本)
4296
+ ├── 业务 Smoke Test 失败?
4297
+ │ └── YES → Level 1 回滚 → 分析根因
4298
+ └── 迁移后 24 小时内发现数据异常?
4299
+ └── YES → Level 2 回滚(备份恢复)→ 通知用户
4300
+ ```
4301
+
4302
+ **输出**:多级回滚方案 + 回滚决策树 + 备份命令
4303
+
4304
+ 5. 零停机迁移四步法
4305
+ 对于无法停机的生产系统,使用"扩列→双写→切流→清旧"四步法:
4306
+
4307
+ **背景**:将 `username` 列重命名为 `name`,同时新增 `email` 列
4308
+
4309
+ **Step 1:扩列(向后兼容)**
4310
+ ```sql
4311
+ -- 新增 name 和 email 列,保留 username(双列共存)
4312
+ ALTER TABLE users ADD COLUMN name VARCHAR(100);
4313
+ ALTER TABLE users ADD COLUMN email VARCHAR(255);
4314
+ -- 应用代码:只读 username,不写 name(暂不变更代码)
4315
+ ```
4316
+
4317
+ **Step 2:双写 + 数据回填**
4318
+ ```typescript
4319
+ // 应用代码更新:同时写入 username 和 name
4320
+ await db.users.update({
4321
+ where: { id },
4322
+ data: {
4323
+ username: newName, // 旧列
4324
+ name: newName, // 新列(同步写入)
4325
+ email: email,
4326
+ },
4327
+ });
4328
+
4329
+ // 异步回填历史数据(后台任务,不影响主流程)
4330
+ async function backfillNames() {
4331
+ let cursor = 0;
4332
+ while (true) {
4333
+ const rows = await db.users.findMany({
4334
+ where: { name: null },
4335
+ take: 1000,
4336
+ });
4337
+ if (rows.length === 0) break;
4338
+ await db.users.updateMany({
4339
+ where: { id: { in: rows.map(r => r.id) } },
4340
+ data: rows.map(r => ({ name: r.username })),
4341
+ });
4342
+ }
4343
+ }
4344
+ ```
4345
+
4346
+ **Step 3:切流(读取切换到新列)**
4347
+ ```typescript
4348
+ // 确认回填完成后,切换读取源
4349
+ // 应用代码:读 name,写 name(不再写 username)
4350
+ const user = await db.users.findUnique({
4351
+ select: { id: true, name: true, email: true }, // 不再 select username
4352
+ });
4353
+ ```
4354
+
4355
+ **Step 4:清旧(移除废弃列)**
4356
+ ```sql
4357
+ -- 确认应用代码已完全切换(观察 1-2 周)
4358
+ ALTER TABLE users DROP COLUMN username;
4359
+ -- 观察指标:慢查询、错误日志中是否有 username 相关错误
4360
+ ```
4361
+
4362
+ **关键时间线**
4363
+ ```
4364
+ Day 0: Step 1(扩列)→ 部署新版本(双写)
4365
+ Day 1: Step 2(确认双写正常,后台回填完成)
4366
+ Day 3: Step 3(切换读取到新列)→ 部署
4367
+ Day 14: Step 4(确认无问题,删除旧列)
4368
+ ```
4369
+
4370
+ **输出**:零停机迁移四步代码示例 + 时间线 + 监控指标
4371
+
4372
+ 输出格式:迁移评估报告 + UP/DOWN 迁移脚本 + 数据验证 SQL 套件 + 多级回滚方案 + 零停机迁移四步代码示例
4373
+
4374
+ ## LLM 功能设计助手
4375
+
4376
+ 触发:llm feature | ai feature | rag | llm 功能
4377
+ 说明:设计 RAG/Agent/生成类 LLM 功能,输出 Prompt 模板、评估框架与降级方案
4378
+
4379
+ 1. LLM 功能定位与场景分类
4380
+ 明确 LLM 功能的类型和边界,选择合适的架构模式:
4381
+
4382
+ **五大 LLM 场景类型**
4383
+
4384
+ | 场景 | 描述 | 典型案例 | 推荐架构 |
4385
+ |------|------|---------|---------|
4386
+ | **RAG(检索增强生成)** | 基于私有知识库回答 | 内部文档问答、客服 | 向量检索 + LLM |
4387
+ | **Agent(自主决策)** | 多步骤工具调用 | 代码 Agent、任务自动化 | LLM + Function Calling |
4388
+ | **生成** | 创作/摘要/翻译 | 文案生成、会议纪要 | 直接调用 + 结构化输出 |
4389
+ | **分类** | 意图/情感/标签 | 工单分类、评论分析 | Fine-tune 或 Few-shot |
4390
+ | **提取** | 信息抽取/NER | 合同关键信息、表单填充 | Structured Output + JSON Schema |
4391
+
4392
+ **功能定位问卷**
4393
+ ```
4394
+ 1. 用户问题的答案在哪里?
4395
+ - 模型已知知识 → 直接生成
4396
+ - 私有文档/数据库 → RAG 架构
4397
+ - 需要实时操作 → Agent 架构
4398
+
4399
+ 2. 需要多步推理吗?
4400
+ - 是 → Agent(工具调用链)
4401
+ - 否 → 单次 Prompt
4402
+
4403
+ 3. 输出格式是否严格?
4404
+ - 是 → JSON Mode / Structured Output
4405
+ - 否 → 自然语言生成
4406
+
4407
+ 4. 延迟要求?
4408
+ - < 500ms → 小模型 + 缓存
4409
+ - < 3s → 标准调用
4410
+ - 可接受流式 → 流式输出
4411
+ ```
4412
+
4413
+ **输出**:LLM 功能定位文档(场景类型 + 架构选型建议)
4414
+
4415
+ 2. RAG 架构设计
4416
+ 为知识库问答类功能设计高质量 RAG 架构:
4417
+
4418
+ **RAG 核心组件**
4419
+ ```
4420
+ 文档摄入管道:
4421
+ Raw Docs → Chunking → Embedding → Vector Store
4422
+
4423
+ Metadata 过滤层(时间/来源/权限)
4424
+
4425
+ 查询管道:
4426
+ User Query → Query 改写/扩展 → 向量检索 → Rerank → LLM 生成
4427
+ ```
4428
+
4429
+ **Chunking 策略选型**
4430
+ ```typescript
4431
+ // 固定大小(适合均匀文本)
4432
+ const CHUNK_SIZE = 512; // tokens
4433
+ const CHUNK_OVERLAP = 50; // 上下文保留
4434
+
4435
+ // 语义分块(适合结构化文档,推荐)
4436
+ // 按段落/标题边界切分
4437
+ function semanticChunk(text: string): string[] {
4438
+ return text.split(/
4439
+ #{1,3}s/); // Markdown 标题分块
4440
+ }
4441
+
4442
+ // 递归分块(LangChain RecursiveCharacterTextSplitter)
4443
+ // 优先按段落 → 句子 → 词语拆分
4444
+ ```
4445
+
4446
+ **向量库选型矩阵**
4447
+ | 方案 | 适用规模 | 托管 | 特性 |
4448
+ |------|---------|------|------|
4449
+ | **Pinecone** | 100万+ | 云托管 | 生产级,自动扩容 |
4450
+ | **Weaviate** | 中大型 | 自托管/云 | 混合搜索,GraphQL |
4451
+ | **Chroma** | 开发/小型 | 本地 | 零配置,适合 PoC |
4452
+ | **pgvector** | 中型 | PostgreSQL | 复用现有 DB |
4453
+
4454
+ **Rerank 优化**
4455
+ ```typescript
4456
+ // 两阶段检索:向量粗召回 → 精排重排序
4457
+ const candidates = await vectorStore.search(query, { topK: 20 });
4458
+ const reranked = await rerankModel.rank(query, candidates, { topN: 5 });
4459
+ // 推荐:Cohere Rerank / BGE-Reranker
4460
+ ```
4461
+
4462
+ **输出**:RAG 架构图 + Chunking 策略 + 向量库选型建议
4463
+
4464
+ 3. Prompt 工程规范
4465
+ 制定高质量 Prompt 的设计规范:
4466
+
4467
+ **System Prompt 黄金结构**
4468
+ ```
4469
+ [角色定义] 你是 {产品名} 的 {角色},专注于 {领域}。
4470
+ [能力边界] 你可以 {能力列表}。你不能/不应该 {限制列表}。
4471
+ [输出格式] 请按照以下格式回复:{格式规范}
4472
+ [行为准则] {特定规则,如:始终用中文回复/引用来源}
4473
+ ```
4474
+
4475
+ **System Prompt 模板**
4476
+ ```typescript
4477
+ const SYSTEM_PROMPT = `
4478
+ 你是 Acme Corp 的智能客服助手,专注于解答产品使用和技术支持问题。
4479
+
4480
+ ## 能力
4481
+ - 查询订单状态、退换货政策、产品规格
4482
+ - 引导用户完成常见操作流程
4483
+ - 基于知识库提供准确答案
4484
+
4485
+ ## 限制
4486
+ - 不讨论竞争对手产品
4487
+ - 不提供具体价格承诺(引导至销售团队)
4488
+ - 不确定时明确说明,不猜测
4489
+
4490
+ ## 输出规范
4491
+ - 语言:与用户保持一致(中/英文)
4492
+ - 长度:简洁清晰,避免超过 200 字
4493
+ - 引用:若基于文档回答,在末尾标注 [来源:{文档名}]
4494
+ `.trim();
4495
+ ```
4496
+
4497
+ **Few-shot 示例设计**
4498
+ ```typescript
4499
+ const FEW_SHOT_EXAMPLES = [
4500
+ {
4501
+ role: 'user',
4502
+ content: '我的订单 #12345 什么时候发货?'
4503
+ },
4504
+ {
4505
+ role: 'assistant',
4506
+ content: '您好!根据订单信息,#12345 已于昨天完成备货,预计今日发出,3-5个工作日送达。[来源:订单管理文档]'
4507
+ }
4508
+ ];
4509
+ ```
4510
+
4511
+ **关键参数建议**
4512
+ ```typescript
4513
+ // 不同场景的推荐参数
4514
+ const PARAMS = {
4515
+ factual_qa: { temperature: 0.1, max_tokens: 500 }, // 事实问答,低随机性
4516
+ creative: { temperature: 0.8, max_tokens: 2000 }, // 创意生成
4517
+ classification:{ temperature: 0, max_tokens: 50 }, // 分类,确定性输出
4518
+ code_gen: { temperature: 0.2, max_tokens: 4000 }, // 代码生成
4519
+ };
4520
+ ```
4521
+
4522
+ **Token 预算管理**
4523
+ ```
4524
+ System Prompt: ~500 tokens (固定)
4525
+ RAG Context: ~2000 tokens (动态,按相关度截断)
4526
+ Conversation: ~1000 tokens (保留最近 N 轮)
4527
+ User Query: ~200 tokens (预留)
4528
+ Output: ~1000 tokens (max_tokens 控制)
4529
+ 总计: ~4700 tokens < 8K context window
4530
+ ```
4531
+
4532
+ **输出**:System Prompt 模板 + Few-shot 示例 + 参数配置建议
4533
+
4534
+ 4. LLM 评估方案
4535
+ 建立自动化 + 人工评估体系,持续监控 LLM 功能质量:
4536
+
4537
+ **三层评估框架**
4538
+
4539
+ **① 自动化 Evals(CI/CD 集成)**
4540
+ ```typescript
4541
+ // 评估维度
4542
+ interface EvalResult {
4543
+ accuracy: number; // 答案正确率(对比 Golden Set)
4544
+ faithfulness: number; // 回答与上下文一致性(RAG 专用)
4545
+ relevance: number; // 答案与问题相关度
4546
+ latency_p95: number; // P95 响应时间(ms)
4547
+ cost_per_query: number; // 平均 token 成本($)
4548
+ }
4549
+
4550
+ // 自动评估示例(LLM-as-Judge)
4551
+ async function evalWithLLM(question: string, answer: string, context: string) {
4552
+ const judgePrompt = `
4553
+ 问题:${question}
4554
+ 检索上下文:${context}
4555
+ 模型回答:${answer}
4556
+
4557
+ 请评估回答的准确性(0-1)和忠实度(0-1),返回 JSON:
4558
+ {"accuracy": 0.x, "faithfulness": 0.x, "reason": "..."}
4559
+ `;
4560
+ return await llm.json(judgePrompt);
4561
+ }
4562
+ ```
4563
+
4564
+ **② Golden Set 维护**
4565
+ ```json
4566
+ // evals/golden-set.json
4567
+ [
4568
+ {
4569
+ "id": "Q001",
4570
+ "question": "退货政策是什么?",
4571
+ "expected_keywords": ["30天", "unopened", "退款"],
4572
+ "expected_source": "return-policy.md",
4573
+ "difficulty": "easy"
4574
+ }
4575
+ ]
4576
+ ```
4577
+
4578
+ **③ 人工评分(A/B 测试)**
4579
+ ```
4580
+ 评分维度(1-5分):
4581
+ - Helpful(有帮助): 回答解决了用户问题
4582
+ - Accurate(准确): 信息与事实一致
4583
+ - Safe(安全): 无有害/不当内容
4584
+ - Concise(简洁): 无冗余信息
4585
+ ```
4586
+
4587
+ **评估指标基准**
4588
+ | 指标 | 可接受 | 良好 | 优秀 |
4589
+ |------|--------|------|------|
4590
+ | 准确率 | > 70% | > 85% | > 95% |
4591
+ | 忠实度 | > 75% | > 90% | > 97% |
4592
+ | P95 延迟 | < 5s | < 3s | < 1.5s |
4593
+ | 幻觉率 | < 15% | < 5% | < 2% |
4594
+
4595
+ **输出**:评估脚本 + Golden Set 模板 + A/B 测试方案
4596
+
4597
+ 5. 降级与安全策略
4598
+ 为 LLM 功能设计完善的降级和安全机制:
4599
+
4600
+ **五大风险与对策**
4601
+
4602
+ **① 幻觉控制**
4603
+ ```typescript
4604
+ // RAG 置信度检查:若无相关文档,拒绝回答
4605
+ const MIN_SIMILARITY = 0.75;
4606
+ const docs = await vectorStore.search(query, { topK: 3 });
4607
+ if (docs[0].score < MIN_SIMILARITY) {
4608
+ return { answer: '抱歉,我没有找到相关信息,请联系人工客服。', sources: [] };
4609
+ }
4610
+ ```
4611
+
4612
+ **② Fallback 降级链**
4613
+ ```typescript
4614
+ async function robustLLMCall(prompt: string) {
4615
+ try {
4616
+ // 主模型(高质量)
4617
+ return await gpt4o.complete(prompt, { timeout: 5000 });
4618
+ } catch (primaryError) {
4619
+ try {
4620
+ // 降级到备用模型(更快更便宜)
4621
+ return await gpt4oMini.complete(prompt, { timeout: 3000 });
4622
+ } catch (fallbackError) {
4623
+ // 最终降级:静态规则/人工兜底
4624
+ return { answer: FALLBACK_MESSAGES[detectIntent(prompt)], fallback: true };
4625
+ }
4626
+ }
4627
+ }
4628
+ ```
4629
+
4630
+ **③ 内容过滤**
4631
+ ```typescript
4632
+ // 输入/输出双向过滤
4633
+ const BLOCKED_PATTERNS = [/如何.*伤害/, /制作.*武器/];
4634
+ function isSafe(text: string): boolean {
4635
+ return !BLOCKED_PATTERNS.some(p => p.test(text));
4636
+ }
4637
+ // 推荐:使用 OpenAI Moderation API / Azure Content Safety
4638
+ ```
4639
+
4640
+ **④ 成本限制**
4641
+ ```typescript
4642
+ // 每用户每日 Token 限额
4643
+ const DAILY_TOKEN_LIMIT = 50000;
4644
+ // 请求频率限制
4645
+ const RATE_LIMIT = { requests: 10, window: '1m' };
4646
+ // 输出截断
4647
+ const MAX_OUTPUT_TOKENS = 1000;
4648
+ ```
4649
+
4650
+ **⑤ 可观测性**
4651
+ ```typescript
4652
+ // 每次 LLM 调用必须记录
4653
+ logger.info('llm_call', {
4654
+ model, prompt_tokens, completion_tokens, latency_ms,
4655
+ user_id, session_id, feature_flag,
4656
+ is_fallback, safety_triggered,
4657
+ });
4658
+ ```
4659
+
4660
+ **输出**:降级策略代码模板 + 内容安全配置 + 成本控制方案 + 可观测性埋点
4661
+
4662
+ 输出格式:LLM 功能定位文档 + RAG 架构图 + System Prompt 模板 + 评估框架(Golden Set + 自动 Evals)+ 降级与安全策略代码
4663
+
4664
+ ## 威胁建模
4665
+
4666
+ 触发:威胁建模 | threat model | threat modeling | stride
4667
+ 说明:运用 STRIDE 方法识别系统安全威胁,构建攻击树,制定缓解措施与残余风险评估
4668
+
4669
+ 1. 系统边界定义
4670
+ 明确系统的信任边界、数据流和外部实体:
4671
+
4672
+ **系统分解步骤**
4673
+
4674
+ 1. **识别外部实体**(系统外部的人/系统)
4675
+ - 终端用户(匿名用户、注册用户、管理员)
4676
+ - 外部系统(第三方 API、支付网关、邮件服务)
4677
+ - 内部其他服务(微服务间调用)
4678
+
4679
+ 2. **绘制数据流图(DFD Level 0)**
4680
+ ```
4681
+ [用户浏览器] ──HTTPS──> [Web 服务器] ──内网──> [应用服务器]
4682
+
4683
+ [数据库服务器]
4684
+
4685
+ [缓存 Redis]
4686
+
4687
+ 信任边界(虚线):
4688
+ - 边界1:Internet / DMZ(用户浏览器 ↔ Web 服务器)
4689
+ - 边界2:DMZ / 内网(Web 服务器 ↔ 应用服务器)
4690
+ - 边界3:内网 / 数据层(应用服务器 ↔ DB/Cache)
4691
+ ```
4692
+
4693
+ 3. **数据资产分类**
4694
+ ```
4695
+ 🔴 高度敏感:用户密码、信用卡信息、身份证号
4696
+ 🟠 敏感: 邮箱、手机号、订单信息、行为数据
4697
+ 🟡 普通: 商品信息、公开内容、系统配置
4698
+ ```
4699
+
4700
+ 4. **威胁面列举**
4701
+ - 对外 API 端点数量:___
4702
+ - 需要认证的接口:___
4703
+ - 数据库直连点:___
4704
+ - 第三方 SDK 集成:___
4705
+
4706
+ **输出**:DFD 图 + 信任边界标注 + 数据资产清单
4707
+
4708
+ 2. STRIDE 威胁识别
4709
+ 用 STRIDE 框架对每个数据流逐一检查六类威胁:
4710
+
4711
+ **STRIDE 威胁矩阵**
4712
+
4713
+ | 威胁类型 | 缩写含义 | 核心问题 | 安全属性 |
4714
+ |---------|---------|---------|---------|
4715
+ | **Spoofing(仿冒)** | 攻击者伪装成合法用户/系统 | 系统如何验证身份? | 认证 |
4716
+ | **Tampering(篡改)** | 恶意修改数据或代码 | 数据完整性如何保证? | 完整性 |
4717
+ | **Repudiation(否认)** | 用户否认执行过某操作 | 是否有可信的操作记录? | 不可否认性 |
4718
+ | **Information Disclosure(信息泄露)** | 敏感数据暴露给未授权方 | 数据在传输/存储中是否加密? | 机密性 |
4719
+ | **Denial of Service(拒绝服务)** | 使系统无法响应合法请求 | 系统能承受多大压力? | 可用性 |
4720
+ | **Elevation of Privilege(权限提升)** | 获得超出授权的权限 | 权限检查是否严格? | 授权 |
4721
+
4722
+ **针对登录接口的 STRIDE 分析示例**
4723
+
4724
+ ```
4725
+ 接口:POST /api/auth/login(外部 → 应用服务器)
4726
+
4727
+ S - 仿冒:
4728
+ 威胁:攻击者暴力破解或凭据填充(Credential Stuffing)
4729
+ 影响:高(账号被盗)
4730
+
4731
+ T - 篡改:
4732
+ 威胁:中间人攻击修改请求(非 HTTPS 场景)
4733
+ 影响:高(凭据被劫持)
4734
+
4735
+ R - 否认:
4736
+ 威胁:用户否认登录操作(无日志)
4737
+ 影响:中(无法审计)
4738
+
4739
+ I - 信息泄露:
4740
+ 威胁:错误信息泄露"用户不存在"vs"密码错误"
4741
+ 影响:中(可枚举用户名)
4742
+
4743
+ D - 拒绝服务:
4744
+ 威胁:大量登录请求耗尽服务器资源
4745
+ 影响:高(服务不可用)
4746
+
4747
+ E - 权限提升:
4748
+ 威胁:JWT 签名算法混淆(HS256 → None)
4749
+ 影响:严重(绕过认证)
4750
+ ```
4751
+
4752
+ **输出**:STRIDE 威胁识别矩阵(每个数据流 × 6 类威胁)
4753
+
4754
+ 3. 攻击树构建
4755
+ 为高风险威胁构建攻击树,量化攻击路径:
4756
+
4757
+ **攻击树符号**
4758
+ ```
4759
+ 目标(攻击根节点)
4760
+ ├── AND: 所有子节点都需满足
4761
+ └── OR: 任意子节点满足即可(默认)
4762
+ ```
4763
+
4764
+ **示例:攻击者获取用户数据攻击树**
4765
+ ```
4766
+ [目标] 获取用户敏感数据
4767
+ ├── OR
4768
+ │ ├── [路径1] 突破 Web 层
4769
+ │ │ ├── SQL 注入(概率: 中,影响: 严重)
4770
+ │ │ │ └── 防御:参数化查询 ✓
4771
+ │ │ └── XSS 窃取 Token(概率: 中,影响: 高)
4772
+ │ │ └── 防御:CSP + HttpOnly Cookie ✓
4773
+ │ ├── [路径2] 绕过认证
4774
+ │ │ ├── 暴力破解(概率: 高,影响: 高)
4775
+ │ │ │ └── 防御:Rate Limit + CAPTCHA ✓
4776
+ │ │ └── Token 伪造(概率: 低,影响: 严重)
4777
+ │ │ └── 防御:强签名算法(RS256)✓
4778
+ │ └── [路径3] 内部威胁
4779
+ │ └── 数据库直接访问(概率: 低,影响: 严重)
4780
+ │ └── 防御:最小权限 + 审计日志 ✓
4781
+ ```
4782
+
4783
+ **风险评分(DREAD 模型)**
4784
+ ```
4785
+ Damage(损害): 1-10
4786
+ Reproducibility(复现): 1-10
4787
+ Exploitability(利用难度): 1-10
4788
+ Affected Users(影响用户): 1-10
4789
+ Discoverability(发现难度): 1-10
4790
+
4791
+ 总分 = (D+R+E+A+D) / 5
4792
+ 8-10:严重 6-7.9:高 4-5.9:中 < 4:低
4793
+ ```
4794
+
4795
+ **输出**:攻击树图 + DREAD 风险评分表
4796
+
4797
+ 4. 安全控制措施
4798
+ 为每个已识别威胁制定具体的缓解控制措施:
4799
+
4800
+ **STRIDE → 控制措施对照表**
4801
+
4802
+ | 威胁 | 控制措施 | 实现示例 |
4803
+ |------|---------|---------|
4804
+ | **仿冒** | 多因素认证、OAuth 2.0 | `TOTP + SMS OTP` |
4805
+ | **篡改** | HTTPS 全程加密、数字签名 | `TLS 1.3 + HMAC 请求签名` |
4806
+ | **否认** | 不可篡改审计日志 | `Append-only Log + 时间戳签名` |
4807
+ | **信息泄露** | 最小权限、数据脱敏、加密 | `AES-256 静态加密 + 字段脱敏` |
4808
+ | **DoS** | 限流、WAF、自动扩容 | `Rate Limit 10 req/s/IP + CDN` |
4809
+ | **权限提升** | RBAC、输入验证 | `JWT RS256 + 接口级鉴权` |
4810
+
4811
+ **优先级实现路线图**
4812
+ ```
4813
+ P0(立即实现,影响上线):
4814
+ ✅ HTTPS 强制(HSTS)
4815
+ ✅ SQL 参数化查询
4816
+ ✅ 密码 bcrypt 哈希(cost factor ≥ 12)
4817
+ ✅ JWT 算法锁定(禁用 none/HS256→RS256)
4818
+
4819
+ P1(首个 Sprint 内):
4820
+ 🔲 API 限流(100 req/min/user)
4821
+ 🔲 账号锁定(5次失败锁定15分钟)
4822
+ 🔲 安全响应头(CSP/X-Frame-Options/HSTS)
4823
+ 🔲 审计日志(登录/权限变更/数据访问)
4824
+
4825
+ P2(次个 Sprint 内):
4826
+ 🔲 多因素认证(MFA)
4827
+ 🔲 字段级数据脱敏(API 响应)
4828
+ 🔲 依赖漏洞扫描(npm audit / Snyk)
4829
+ 🔲 渗透测试(OWASP Top 10 覆盖)
4830
+ ```
4831
+
4832
+ **输出**:安全控制措施清单 + 实现优先级路线图
4833
+
4834
+ 5. 残余风险评估
4835
+ 评估实施控制措施后的剩余风险,制定接受/处置策略:
4836
+
4837
+ **四种风险处置方式**
4838
+ - **接受(Accept)**:风险可接受,记录并监控
4839
+ - **降低(Mitigate)**:实施控制措施降低概率/影响
4840
+ - **转移(Transfer)**:购买保险或外包给第三方(如 Cloudflare DDoS 防护)
4841
+ - **规避(Avoid)**:不实现该功能/移除风险点
4842
+
4843
+ **残余风险登记册模板**
4844
+ ```
4845
+ | ID | 威胁描述 | 原始风险 | 控制措施 | 残余风险 | 处置策略 | 责任人 | 复查日期 |
4846
+ |----|---------|---------|---------|---------|---------|--------|---------|
4847
+ | T001 | 暴力破解密码 | 高(8) | Rate Limit + CAPTCHA | 低(2) | 接受 | @security | 2024-Q2 |
4848
+ | T002 | SQL注入 | 严重(9) | 参数化查询 | 极低(1) | 接受 | @backend | 2024-Q2 |
4849
+ | T003 | DDoS攻击 | 高(7) | Cloudflare | 低(3) | 转移 | @infra | 2024-Q2 |
4850
+ | T004 | 0-day漏洞 | 未知 | WAF + 快速响应 | 中(5) | 监控 | @security | 月度 |
4851
+ ```
4852
+
4853
+ **安全评审 Checklist**
4854
+ - [ ] 所有 P0 控制措施已实现并测试
4855
+ - [ ] 威胁模型已经过安全团队评审
4856
+ - [ ] 高风险威胁已获得业务方正式接受签字
4857
+ - [ ] 残余风险已录入风险登记册
4858
+ - [ ] 设定下次威胁模型复审日期(建议每季度或重大变更后)
4859
+
4860
+ **输出**:残余风险登记册 + 风险接受声明 + 复审计划
4861
+
4862
+ 输出格式:威胁模型文档(DFD + 信任边界)+ STRIDE 威胁矩阵 + 攻击树图 + 安全控制措施路线图 + 残余风险登记册
4863
+
4864
+ ## 绿色编码实践
4865
+
4866
+ 触发:绿色编码 | green code | green software | carbon footprint
4867
+ 说明:识别代码能耗热点,优化算法复杂度与云资源效率,估算并降低软件碳排放
4868
+
4869
+ 1. 能耗热点识别
4870
+ 扫描代码库,定位高能耗模式:
4871
+
4872
+ **六大能耗反模式**
4873
+
4874
+ | 反模式 | 表现 | 能耗级别 |
4875
+ |--------|------|---------|
4876
+ | **无效轮询** | `while(true) { check(); sleep(100) }` | 🔴 极高 |
4877
+ | **CPU 密集循环** | 嵌套循环处理大数据集 | 🔴 极高 |
4878
+ | **内存泄漏** | 对象无法被 GC 回收 | 🟠 高 |
4879
+ | **冗余网络请求** | 重复请求相同资源 | 🟠 高 |
4880
+ | **过度序列化** | 频繁 JSON.stringify 大对象 | 🟡 中 |
4881
+ | **阻塞 I/O** | 同步文件读写阻塞主线程 | 🟡 中 |
4882
+
4883
+ **识别工具命令**
4884
+ ```bash
4885
+ # Node.js — CPU Profile
4886
+ node --prof app.js
4887
+ node --prof-process isolate-*.log > profile.txt
4888
+
4889
+ # 内存快照(Node.js)
4890
+ node --heap-prof app.js
4891
+
4892
+ # 前端 — Chrome DevTools
4893
+ # Performance 面板 → Record → 分析 Long Tasks(>50ms)
4894
+
4895
+ # Python — cProfile
4896
+ python -m cProfile -o output.prof app.py
4897
+ python -m pstats output.prof # 查看热点函数
4898
+ ```
4899
+
4900
+ **无效轮询重构**
4901
+ ```typescript
4902
+ // ❌ 能耗高:每 100ms 轮询
4903
+ while (true) {
4904
+ const status = await checkJobStatus(jobId);
4905
+ if (status === 'done') break;
4906
+ await sleep(100);
4907
+ }
4908
+
4909
+ // ✅ 绿色:指数退避 + 最大间隔
4910
+ async function pollWithBackoff(jobId: string) {
4911
+ let delay = 500;
4912
+ const MAX_DELAY = 30000;
4913
+ while (true) {
4914
+ const status = await checkJobStatus(jobId);
4915
+ if (status === 'done') return;
4916
+ await sleep(Math.min(delay, MAX_DELAY));
4917
+ delay *= 1.5;
4918
+ }
4919
+ }
4920
+
4921
+ // ✅ 更绿色:WebSocket / SSE(服务端推送,零轮询)
4922
+ const ws = new WebSocket('/api/jobs/status');
4923
+ ws.onmessage = (e) => handleUpdate(JSON.parse(e.data));
4924
+ ```
4925
+
4926
+ **输出**:能耗热点清单(位置 + 类型 + 估算影响)
4927
+
4928
+ 2. 算法复杂度优化
4929
+ 用低复杂度算法替换高能耗实现:
4930
+
4931
+ **复杂度对碳排放的影响(1M 数据量对比)**
4932
+ ```
4933
+ O(n²) → 10^12 操作 → 约 100W CPU 秒 🔴
4934
+ O(n log n) → 2×10^7 操作 → 约 20ms 🟢
4935
+ O(n) → 10^6 操作 → 约 1ms 🟢
4936
+ O(1) → 1 操作 → 即时 🟢
4937
+ ```
4938
+
4939
+ **常见优化模式**
4940
+
4941
+ **① 嵌套循环 → 哈希查找**
4942
+ ```typescript
4943
+ // ❌ O(n²) — 大数据量极度消耗 CPU
4944
+ function findMatches(users: User[], orders: Order[]) {
4945
+ return orders.filter(order =>
4946
+ users.some(user => user.id === order.userId) // O(n) per order
4947
+ );
4948
+ }
4949
+
4950
+ // ✅ O(n) — 预建索引
4951
+ function findMatchesOptimized(users: User[], orders: Order[]) {
4952
+ const userMap = new Map(users.map(u => [u.id, u])); // 一次性 O(n)
4953
+ return orders.filter(order => userMap.has(order.userId)); // O(1) per order
4954
+ }
4955
+ ```
4956
+
4957
+ **② 重复计算 → 缓存/记忆化**
4958
+ ```typescript
4959
+ // ❌ 每次调用重新计算
4960
+ function expensiveCalc(n: number) { /* O(2^n) */ }
4961
+
4962
+ // ✅ 记忆化缓存
4963
+ const memo = new Map<number, number>();
4964
+ function cachedCalc(n: number): number {
4965
+ if (memo.has(n)) return memo.get(n)!;
4966
+ const result = expensiveCalc(n);
4967
+ memo.set(n, result);
4968
+ return result;
4969
+ }
4970
+ ```
4971
+
4972
+ **③ 大列表操作 → 流式处理**
4973
+ ```typescript
4974
+ // ❌ 全量加载到内存
4975
+ const allRecords = await db.findAll(); // 可能 100MB
4976
+ processAll(allRecords);
4977
+
4978
+ // ✅ 流式分批处理
4979
+ const BATCH = 1000;
4980
+ for await (const batch of db.findInBatches(BATCH)) {
4981
+ await processBatch(batch);
4982
+ }
4983
+ ```
4984
+
4985
+ **输出**:算法优化建议 + 重构前后复杂度对比
4986
+
4987
+ 3. 云资源效率优化
4988
+ 减少云资源浪费,降低运行能耗:
4989
+
4990
+ **云资源三大浪费来源**
4991
+ ```
4992
+ 1. Over-provisioning(过度配置):实际 CPU 利用率 < 20%,却购买了大型实例
4993
+ 2. 僵尸资源:停止的 EC2、未挂载的 EBS 卷、空闲的 NAT 网关
4994
+ 3. 低效架构:长时间运行的大实例 vs. Serverless 按需执行
4995
+ ```
4996
+
4997
+ **Right-sizing 实践**
4998
+ ```bash
4999
+ # AWS — 查看 CPU 利用率趋势(CloudWatch)
5000
+ aws cloudwatch get-metric-statistics --metric-name CPUUtilization --dimensions Name=InstanceId,Value=i-xxxx --start-time 2024-01-01T00:00:00 --end-time 2024-01-31T00:00:00 --period 86400 --statistics Average
5001
+
5002
+ # 建议:平均 CPU < 20% → 降低一档实例类型
5003
+ # m5.xlarge (4 vCPU) → m5.large (2 vCPU) = 节省 50% 成本和能耗
5004
+ ```
5005
+
5006
+ **自动伸缩配置(节能关键)**
5007
+ ```yaml
5008
+ # Kubernetes HPA — 按 CPU 自动伸缩
5009
+ apiVersion: autoscaling/v2
5010
+ kind: HorizontalPodAutoscaler
5011
+ metadata:
5012
+ name: api-server
5013
+ spec:
5014
+ minReplicas: 1 # 低峰期缩减到 1 个副本
5015
+ maxReplicas: 10
5016
+ metrics:
5017
+ - type: Resource
5018
+ resource:
5019
+ name: cpu
5020
+ target:
5021
+ type: Utilization
5022
+ averageUtilization: 60
5023
+ ```
5024
+
5025
+ **Serverless 适用场景**
5026
+ ```
5027
+ 适合 Serverless(事件驱动,闲时零消耗):
5028
+ ✅ 图片处理、文件转换
5029
+ ✅ 定时任务、数据同步
5030
+ ✅ Webhook 处理器
5031
+
5032
+ 不适合 Serverless(冷启动延迟不可接受):
5033
+ ❌ 实时游戏服务器
5034
+ ❌ 高频低延迟 API(< 100ms SLA)
5035
+ ```
5036
+
5037
+ **输出**:云资源优化建议(Right-sizing + 自动伸缩 + 架构选型)
5038
+
5039
+ 4. 前端绿色实践
5040
+ 减少前端资源消耗,降低设备端能耗:
5041
+
5042
+ **前端能耗五大优化**
5043
+
5044
+ **① JavaScript Bundle 瘦身**
5045
+ ```bash
5046
+ # 分析 Bundle 大小
5047
+ npx webpack-bundle-analyzer stats.json
5048
+ npx vite-bundle-visualizer
5049
+
5050
+ # 目标:首屏 JS < 150KB (gzipped)
5051
+ ```
5052
+
5053
+ ```typescript
5054
+ // ✅ Tree-shaking(只导入用到的函数)
5055
+ import { debounce } from 'lodash-es'; // 不要 import _ from 'lodash'
5056
+
5057
+ // ✅ 动态导入(按需加载非关键模块)
5058
+ const HeavyChart = lazy(() => import('./HeavyChart'));
5059
+
5060
+ // ✅ 外部化大型库(CDN 缓存复用)
5061
+ // vite.config.ts
5062
+ build: { rollupOptions: { external: ['react', 'react-dom'] } }
5063
+ ```
5064
+
5065
+ **② 图片优化**
5066
+ ```html
5067
+ <!-- ✅ 现代格式 + 响应式 -->
5068
+ <picture>
5069
+ <source srcset="hero.avif" type="image/avif">
5070
+ <source srcset="hero.webp" type="image/webp">
5071
+ <img src="hero.jpg" width="800" height="600"
5072
+ loading="lazy" decoding="async" alt="...">
5073
+ </picture>
5074
+ <!-- WebP 比 JPEG 小 30%,AVIF 比 JPEG 小 50% -->
5075
+ ```
5076
+
5077
+ **③ CSS 精简**
5078
+ ```bash
5079
+ # PurgeCSS — 移除未使用的 CSS
5080
+ npx purgecss --css dist/*.css --content dist/*.html dist/*.js --output dist/
5081
+ ```
5082
+
5083
+ **④ 减少不必要的动画**
5084
+ ```css
5085
+ /* 尊重用户的省电模式偏好 */
5086
+ @media (prefers-reduced-motion: reduce) {
5087
+ *, *::before, *::after {
5088
+ animation-duration: 0.01ms !important;
5089
+ transition-duration: 0.01ms !important;
5090
+ }
5091
+ }
5092
+ ```
5093
+
5094
+ **⑤ 高效 DOM 操作**
5095
+ ```typescript
5096
+ // ❌ 频繁触发 Reflow
5097
+ items.forEach(item => {
5098
+ item.style.width = container.offsetWidth + 'px'; // 每次读写触发 Reflow
5099
+ });
5100
+
5101
+ // ✅ 批量读后批量写
5102
+ const width = container.offsetWidth; // 一次读取
5103
+ items.forEach(item => { item.style.width = width + 'px'; }); // 批量写
5104
+ ```
5105
+
5106
+ **输出**:前端绿色实践清单 + Bundle 分析建议
5107
+
5108
+ 5. 碳排放估算
5109
+ 用 SCI(Software Carbon Intensity)公式量化软件碳排放:
5110
+
5111
+ **SCI 公式**
5112
+ ```
5113
+ SCI = (E × I) + M
5114
+
5115
+ E = 软件消耗的电能(kWh)
5116
+ I = 电网碳强度(gCO₂eq/kWh)— 取决于服务器所在地区
5117
+ M = 硬件制造碳排放(均摊到使用寿命)
5118
+ ```
5119
+
5120
+ **各地区电网碳强度参考**
5121
+ ```
5122
+ 地区 碳强度 (gCO₂eq/kWh)
5123
+ ─────────────────────────────────
5124
+ 西欧 / Nordic ~150-300 (可再生能源占比高)
5125
+ us-east-1 ~350-400
5126
+ ap-northeast-1 ~450-500 (日本,主要火电)
5127
+ cn-north-1 ~550-600 (中国,煤电为主)
5128
+ AWS GovCloud ~200 (承诺使用清洁能源)
5129
+ ```
5130
+
5131
+ **API 服务碳排放估算示例**
5132
+ ```
5133
+ 假设:
5134
+ - API 服务器:4 vCPU,平均 40% 利用率
5135
+ - TDP(热设计功耗):100W(4 vCPU 服务器)
5136
+ - 实际功耗:100W × 40% × PUE(1.2) = 48W
5137
+ - 每月运行:48W × 730h = 35 kWh
5138
+ - 碳排放(us-east-1,380 gCO₂/kWh):
5139
+ 35 × 380 = 13,300 gCO₂ = 13.3 kg CO₂/月
5140
+
5141
+ 优化后(CPU 利用率从 40% 优化到 20% 后缩容):
5142
+ 12W × 730h × 380 / 1000 = 3.3 kg CO₂/月
5143
+ 节省:10 kg CO₂/月(减少 75%)
5144
+ ```
5145
+
5146
+ **绿色目标设定**
5147
+ ```
5148
+ 当前基准: ___ gCO₂/1000 API 请求
5149
+ 目标(3个月): 降低 30%
5150
+ 目标(1年): 迁移到低碳区域(or 可再生能源数据中心)
5151
+ ```
5152
+
5153
+ **Carbon.txt 声明**(向用户公示绿色承诺)
5154
+ ```
5155
+ # /carbon.txt
5156
+ We are committed to reducing our software's carbon footprint.
5157
+ Hosting: AWS us-west-2 (powered by renewable energy)
5158
+ Current SCI: 15g CO₂eq per 1000 requests
5159
+ Target: < 10g CO₂eq per 1000 requests by 2025
5160
+ ```
5161
+
5162
+ **输出**:碳排放基准报告 + SCI 计算表 + 减碳路线图
5163
+
5164
+ 输出格式:能耗热点报告 + 算法优化建议 + 云资源 Right-sizing 方案 + 前端绿色实践 Checklist + 碳排放估算(SCI)
5165
+
5166
+ ## 服务目录管理
5167
+
5168
+ 触发:服务目录 | service catalog | service registry | backstage
5169
+ 说明:建立标准化服务目录,生成 Backstage 兼容的 catalog-info.yaml,可视化服务依赖与健康评分
5170
+
5171
+ 1. 服务元数据采集
5172
+ 系统化收集每个服务的关键元数据:
5173
+
5174
+ **服务元数据清单**
5175
+
5176
+ ```yaml
5177
+ # 必填字段
5178
+ name: 服务唯一标识(小写 + 连字符)
5179
+ display_name: 人类可读名称
5180
+ owner: 团队/个人(GitHub Team 或 email)
5181
+ tech_stack: 主要技术栈(Node.js/Go/Java 等)
5182
+ type: 服务类型(service/library/website/pipeline)
5183
+
5184
+ # 运维信息
5185
+ tier: 重要性等级(tier-1/tier-2/tier-3)
5186
+ sla: 可用性目标(99.9%/99.95%/99.99%)
5187
+ on_call: 值班联系方式(PagerDuty/Slack channel)
5188
+ runbook: 运维手册 URL
5189
+
5190
+ # 依赖关系
5191
+ dependencies: 依赖的其他服务列表
5192
+ consumers: 消费本服务的上游列表
5193
+ databases: 使用的数据库(类型 + 库名)
5194
+ external_apis: 调用的外部 API(如 Stripe/SendGrid)
5195
+
5196
+ # 文档与质量
5197
+ docs: 文档链接(Confluence/Notion)
5198
+ api_spec: OpenAPI/AsyncAPI 规范文件路径
5199
+ test_coverage: 当前测试覆盖率
5200
+ deployment_frequency: 发布频率(daily/weekly/monthly)
5201
+ ```
5202
+
5203
+ **批量采集脚本**
5204
+ ```bash
5205
+ #!/bin/bash
5206
+ # 扫描所有服务目录,检查是否有 catalog-info.yaml
5207
+ for dir in services/*/; do
5208
+ if [ ! -f "$dir/catalog-info.yaml" ]; then
5209
+ echo "❌ Missing: $dir/catalog-info.yaml"
5210
+ else
5211
+ echo "✅ Found: $dir/catalog-info.yaml"
5212
+ fi
5213
+ done
5214
+ ```
5215
+
5216
+ **输出**:服务元数据采集表(所有服务 + 完整度评分)
5217
+
5218
+ 2. 生成 catalog-info.yaml
5219
+ 生成 Backstage/内部开发者平台兼容的服务定义文件:
5220
+
5221
+ **完整 catalog-info.yaml 模板**
5222
+ ```yaml
5223
+ # services/order-service/catalog-info.yaml
5224
+ apiVersion: backstage.io/v1alpha1
5225
+ kind: Component
5226
+ metadata:
5227
+ name: order-service
5228
+ title: 订单服务
5229
+ description: 处理订单创建、支付和履约的核心服务
5230
+ tags:
5231
+ - nodejs
5232
+ - postgresql
5233
+ - kafka
5234
+ - tier-1
5235
+ annotations:
5236
+ # GitHub 集成
5237
+ github.com/project-slug: myorg/order-service
5238
+ # 监控集成
5239
+ prometheus.io/alert: 'order-service-alerts'
5240
+ grafana.com/dashboard: 'https://grafana.internal/d/orders'
5241
+ # 文档
5242
+ backstage.io/techdocs-ref: dir:.
5243
+ # PagerDuty
5244
+ pagerduty.com/service-id: P123456
5245
+ links:
5246
+ - url: https://runbook.internal/order-service
5247
+ title: Runbook
5248
+ icon: book
5249
+ - url: https://grafana.internal/d/orders
5250
+ title: Dashboard
5251
+ icon: dashboard
5252
+
5253
+ spec:
5254
+ type: service
5255
+ lifecycle: production # experimental | production | deprecated
5256
+ owner: group:backend-team
5257
+ system: ecommerce-platform
5258
+
5259
+ # 依赖声明
5260
+ dependsOn:
5261
+ - component:user-service
5262
+ - component:inventory-service
5263
+ - resource:orders-postgres
5264
+ - resource:payment-kafka-topic
5265
+
5266
+ # 对外提供的 API
5267
+ providesApis:
5268
+ - order-api-v2
5269
+ ```
5270
+
5271
+ **API 定义(关联)**
5272
+ ```yaml
5273
+ # order-api.yaml
5274
+ apiVersion: backstage.io/v1alpha1
5275
+ kind: API
5276
+ metadata:
5277
+ name: order-api-v2
5278
+ description: 订单服务 REST API v2
5279
+ spec:
5280
+ type: openapi
5281
+ lifecycle: production
5282
+ owner: group:backend-team
5283
+ definition:
5284
+ $text: ./openapi/order-api-v2.yaml
5285
+ ```
5286
+
5287
+ **Resource 定义(数据库/队列)**
5288
+ ```yaml
5289
+ apiVersion: backstage.io/v1alpha1
5290
+ kind: Resource
5291
+ metadata:
5292
+ name: orders-postgres
5293
+ description: 订单主数据库(PostgreSQL 14)
5294
+ spec:
5295
+ type: database
5296
+ owner: group:dba-team
5297
+ system: ecommerce-platform
5298
+ ```
5299
+
5300
+ **输出**:catalog-info.yaml + API 定义文件 + Resource 定义文件
5301
+
5302
+ 3. 服务依赖关系图
5303
+ 可视化服务间的上下游依赖关系:
5304
+
5305
+ **Mermaid 依赖图生成**
5306
+ ```typescript
5307
+ // scripts/gen-dependency-graph.ts
5308
+ function generateMermaid(services: Service[]): string {
5309
+ const lines = ['graph LR'];
5310
+
5311
+ for (const service of services) {
5312
+ for (const dep of service.dependencies) {
5313
+ lines.push(` ${service.name} --> ${dep}`);
5314
+ }
5315
+ }
5316
+
5317
+ return lines.join('\n');
5318
+ }
5319
+
5320
+ // 输出示例:
5321
+ // graph LR
5322
+ // order-service --> user-service
5323
+ // order-service --> inventory-service
5324
+ // order-service --> payment-service
5325
+ // api-gateway --> order-service
5326
+ // api-gateway --> user-service
5327
+ ```
5328
+
5329
+ **关键依赖分析**
5330
+ ```
5331
+ 依赖分析报告
5332
+ ════════════
5333
+
5334
+ 高扇入服务(被依赖最多,故障影响最广):
5335
+ user-service 被 8 个服务依赖 ⚠️ 单点风险
5336
+ auth-service 被 12 个服务依赖 🔴 关键路径
5337
+
5338
+ 高扇出服务(依赖最多,级联失败风险):
5339
+ order-service 依赖 6 个服务 ⚠️ 需熔断保护
5340
+ report-service 依赖 9 个服务 🔴 脆弱性高
5341
+
5342
+ 循环依赖检测:
5343
+ ✅ 未发现循环依赖
5344
+
5345
+ 孤立服务(无依赖/无被依赖):
5346
+ legacy-batch-job ⚠️ 是否可下线?
5347
+ ```
5348
+
5349
+ **依赖健康度 Checklist**
5350
+ - [ ] 高扇入服务(>5个依赖)配备了熔断器(Circuit Breaker)
5351
+ - [ ] 高扇出服务对所有依赖设置了超时(Timeout)
5352
+ - [ ] 关键路径服务有冗余/多活部署
5353
+ - [ ] 无循环依赖
5354
+
5355
+ **输出**:服务依赖关系图(Mermaid/DOT)+ 风险分析报告
5356
+
5357
+ 4. 服务健康评分
5358
+ 建立多维度服务健康评分体系,量化技术质量:
5359
+
5360
+ **服务健康评分卡(满分 100)**
5361
+
5362
+ ```
5363
+ 维度 权重 评分标准
5364
+ ─────────────────────────────────────────
5365
+ 📄 文档完整性 20分
5366
+ catalog-info.yaml 5分 (存在且完整)
5367
+ README.md 5分 (含部署、接口说明)
5368
+ API 规范 5分 (OpenAPI/AsyncAPI)
5369
+ Runbook 5分 (含常见故障处理)
5370
+
5371
+ 🧪 测试质量 25分
5372
+ 单元测试覆盖率 >80% 15分
5373
+ 集成测试存在 10分
5374
+
5375
+ 🚀 部署规范 25分
5376
+ CI/CD 流水线完整 10分
5377
+ 容器化(Dockerfile) 5分
5378
+ 健康检查接口 5分
5379
+ 优雅关闭实现 5分
5380
+
5381
+ 📊 可观测性 20分
5382
+ Metrics 暴露 8分 (/metrics endpoint)
5383
+ 结构化日志 7分 (JSON 格式 + trace ID)
5384
+ 告警规则配置 5分
5385
+
5386
+ 🔒 安全合规 10分
5387
+ 依赖漏洞扫描通过 5分 (npm audit/Snyk 0 High)
5388
+ 密钥未硬编码 5分
5389
+ ```
5390
+
5391
+ **评分等级**
5392
+ - 🟢 **优秀(90-100)**:可作为黄金路径参考
5393
+ - 🟡 **良好(70-89)**:有改进空间,下季度计划
5394
+ - 🟠 **待改进(50-69)**:需要专项提升
5395
+ - 🔴 **高风险(< 50)**:必须立即改进
5396
+
5397
+ **批量评分脚本示例**
5398
+ ```bash
5399
+ #!/bin/bash
5400
+ # 检查服务健康评分关键项
5401
+ check_service() {
5402
+ local dir=$1
5403
+ local score=0
5404
+
5405
+ [ -f "$dir/catalog-info.yaml" ] && score=$((score+5))
5406
+ [ -f "$dir/README.md" ] && score=$((score+5))
5407
+ [ -f "$dir/Dockerfile" ] && score=$((score+5))
5408
+ [ -f "$dir/.github/workflows/ci.yml" ] && score=$((score+10))
5409
+
5410
+ echo "$dir: $score / 25 (快速检查)"
5411
+ }
5412
+
5413
+ for dir in services/*/; do check_service "$dir"; done
5414
+ ```
5415
+
5416
+ **输出**:服务健康评分卡 + 改进优先级清单
5417
+
5418
+ 5. 服务生命周期管理
5419
+ 建立服务从孵化到下线的标准生命周期流程:
5420
+
5421
+ **四阶段生命周期**
5422
+ ```
5423
+ 孵化(Experimental)→ 成熟(Production)→ 维护(Maintenance)→ 下线(Deprecated)
5424
+ ```
5425
+
5426
+ **各阶段定义与要求**
5427
+
5428
+ | 阶段 | lifecycle 值 | 要求 | 颜色标记 |
5429
+ |------|-------------|------|---------|
5430
+ | 孵化 | experimental | 开发中,不可用于生产 | 🟡 |
5431
+ | 成熟 | production | 满足健康评分 ≥ 80 | 🟢 |
5432
+ | 维护 | maintenance | 仅修复 Bug,不加新功能 | 🟠 |
5433
+ | 下线 | deprecated | 已有替代服务,设定截止日 | 🔴 |
5434
+
5435
+ **服务下线流程**
5436
+ ```
5437
+ Day 0: 更新 lifecycle: deprecated,添加 deprecation notice
5438
+ 通知所有已知消费者(来自 catalog 的 consumers 列表)
5439
+
5440
+ Day 30: 确认所有消费者已迁移
5441
+ 关闭新注册/新依赖
5442
+
5443
+ Day 60: 停止接受新请求(返回 410 Gone)
5444
+ 保留 Read-only 访问用于历史数据查询
5445
+
5446
+ Day 90: 完全下线,数据归档
5447
+ 从 catalog 中标记 archived: true
5448
+ ```
5449
+
5450
+ **服务晋升 Checklist(Experimental → Production)**
5451
+ - [ ] 服务健康评分 ≥ 80
5452
+ - [ ] 通过 Load Test(目标 TPS 的 150% 压力)
5453
+ - [ ] 完成安全扫描(0 个 High/Critical 漏洞)
5454
+ - [ ] Runbook 已编写并通过演练
5455
+ - [ ] 监控告警已配置并验证(OnCall 团队确认)
5456
+ - [ ] 至少经过 2 周 Canary 发布观察
5457
+
5458
+ **输出**:服务生命周期文档 + 下线流程 + 晋升 Checklist
5459
+
5460
+ 输出格式:catalog-info.yaml 文件 + 服务依赖关系图(Mermaid)+ 服务健康评分卡 + 生命周期管理规范
5461
+
5462
+ ## 移动端专项审查
5463
+
5464
+ 触发:移动端审查 | mobile review | ios review | android review
5465
+ 说明:审查移动端应用的平台合规、性能基准、离线同步、无障碍与崩溃防护
5466
+
5467
+ 1. 平台合规审查
5468
+ 检查 iOS 和 Android 的合规要求,确保顺利通过审核:
5469
+
5470
+ **iOS App Store 合规 Checklist**
5471
+ ```
5472
+ □ 隐私权限声明
5473
+ - Info.plist 中每个权限 Key 必须有 Usage Description
5474
+ - NSCameraUsageDescription: "用于拍摄商品照片"
5475
+ - NSLocationWhenInUseUsageDescription: "用于查找附近服务"
5476
+ - NSPhotoLibraryUsageDescription: "用于选择头像图片"
5477
+
5478
+ □ App Tracking Transparency (ATT)
5479
+ - iOS 14+ 追踪用户需要弹窗授权
5480
+ - NSUserTrackingUsageDescription 必须填写
5481
+
5482
+ □ 隐私清单 (Privacy Manifest, iOS 17+)
5483
+ - PrivacyInfo.xcprivacy 文件
5484
+ - 声明使用的 Required Reason API(UserDefaults/File timestamp 等)
5485
+
5486
+ □ 网络安全配置
5487
+ - 所有网络请求使用 HTTPS(ATS 默认开启)
5488
+ - 自定义 Exception 需要合理理由
5489
+
5490
+ □ 支付合规
5491
+ - 数字商品/虚拟货币必须使用 In-App Purchase
5492
+ - 不得引导用户到外部网站购买
5493
+ ```
5494
+
5495
+ **Android Google Play 合规 Checklist**
5496
+ ```
5497
+ □ 权限最小化
5498
+ - 仅申请功能必需的权限
5499
+ - targetSdkVersion ≥ 33(Play Store 要求)
5500
+ - 危险权限在使用前实时申请(非安装时)
5501
+
5502
+ □ 数据安全表(Data Safety Section)
5503
+ - 明确声明收集的数据类型
5504
+ - 说明是否与第三方共享
5505
+
5506
+ □ 广告 ID(GAID)
5507
+ - Android 12+ 需要 AD_ID 权限
5508
+ - 儿童类应用禁止使用广告 ID
5509
+
5510
+ □ 64位支持
5511
+ - 所有 native library 提供 arm64-v8a 版本
5512
+ ```
5513
+
5514
+ **输出**:平台合规检查报告(iOS + Android,通过/未通过标注)
5515
+
5516
+ 2. 性能专项审查
5517
+ 评估移动端应用的关键性能指标:
5518
+
5519
+ **性能基准标准**
5520
+
5521
+ | 指标 | 优秀 | 可接受 | 需优化 |
5522
+ |------|------|--------|--------|
5523
+ | **冷启动时间** | < 1.5s | < 2.5s | > 2.5s |
5524
+ | **热启动时间** | < 0.5s | < 1.0s | > 1.0s |
5525
+ | **帧率(FPS)** | 60fps | 55fps | < 50fps |
5526
+ | **首屏渲染** | < 1.0s | < 2.0s | > 2.0s |
5527
+ | **内存占用(前台)** | < 150MB | < 250MB | > 250MB |
5528
+ | **安装包大小** | < 30MB | < 60MB | > 60MB |
5529
+ | **电池消耗** | < 5%/h | < 10%/h | > 10%/h |
5530
+
5531
+ **启动性能优化**
5532
+ ```swift
5533
+ // iOS — 减少 AppDelegate 启动时工作
5534
+ @main class AppDelegate: UIResponder, UIApplicationDelegate {
5535
+ func application(_ application: UIApplication,
5536
+ didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
5537
+
5538
+ // ❌ 避免在启动时进行:
5539
+ // - 同步网络请求
5540
+ // - 大量数据库初始化
5541
+ // - 复杂计算
5542
+
5543
+ // ✅ 启动时只做:
5544
+ setupCrashReporting() // 轻量
5545
+ configureDependencyInjection() // 必须
5546
+
5547
+ // ✅ 延迟到首屏渲染后
5548
+ DispatchQueue.main.async {
5549
+ self.setupAnalytics()
5550
+ self.prefetchData()
5551
+ }
5552
+ return true
5553
+ }
5554
+ }
5555
+ ```
5556
+
5557
+ ```kotlin
5558
+ // Android — 使用 App Startup 库延迟初始化
5559
+ class MyInitializer : Initializer<Unit> {
5560
+ override fun create(context: Context) {
5561
+ // 只初始化核心功能
5562
+ }
5563
+ override fun dependencies() = emptyList<Class<out Initializer<*>>>()
5564
+ }
5565
+ ```
5566
+
5567
+ **包大小优化**
5568
+ ```
5569
+ iOS:
5570
+ - 使用 Asset Catalog 而非散落图片
5571
+ - On-Demand Resources 下载非核心资源
5572
+ - App Thinning(Bitcode + Slicing)
5573
+
5574
+ Android:
5575
+ - App Bundle (.aab) 替代 APK(减少 20-40%)
5576
+ - R8/ProGuard 代码压缩
5577
+ - WebP 替换 PNG/JPEG
5578
+ - 动态功能模块(Dynamic Feature Modules)
5579
+ ```
5580
+
5581
+ **输出**:性能基准报告 + 优化建议清单
5582
+
5583
+ 3. 离线与数据同步
5584
+ 设计可靠的离线能力和数据同步策略:
5585
+
5586
+ **离线架构模式**
5587
+
5588
+ **① Cache-First(适合内容类 App)**
5589
+ ```typescript
5590
+ // React Native — 先读本地缓存,后台刷新
5591
+ async function getArticles(): Promise<Article[]> {
5592
+ const cached = await AsyncStorage.getItem('articles');
5593
+
5594
+ // 立即返回缓存(用户不等待)
5595
+ if (cached) {
5596
+ const data = JSON.parse(cached);
5597
+ // 后台静默刷新
5598
+ fetchAndCache().catch(console.error);
5599
+ return data;
5600
+ }
5601
+
5602
+ // 无缓存时等待网络
5603
+ return fetchAndCache();
5604
+ }
5605
+ ```
5606
+
5607
+ **② Optimistic Update(适合操作类 App)**
5608
+ ```typescript
5609
+ // 先更新本地,后台同步到服务器
5610
+ async function toggleLike(postId: string) {
5611
+ // 立即更新 UI(乐观更新)
5612
+ dispatch({ type: 'TOGGLE_LIKE', postId });
5613
+
5614
+ try {
5615
+ await api.post(`/posts/${postId}/like`);
5616
+ } catch (error) {
5617
+ // 失败时回滚
5618
+ dispatch({ type: 'REVERT_LIKE', postId });
5619
+ showToast('操作失败,已恢复');
5620
+ }
5621
+ }
5622
+ ```
5623
+
5624
+ **冲突解决策略**
5625
+ ```
5626
+ 策略 适用场景
5627
+ ─────────────────────────────────
5628
+ Last-Write-Wins 一般设置、非关键数据
5629
+ Server-Wins 金融数据、库存数量
5630
+ Client-Wins 用户个人偏好
5631
+ Merge(合并) 文档协作(CRDT)
5632
+ Ask User(提示用户) 重要数据冲突
5633
+ ```
5634
+
5635
+ **网络状态监听**
5636
+ ```typescript
5637
+ import NetInfo from '@react-native-community/netinfo';
5638
+
5639
+ NetInfo.addEventListener(state => {
5640
+ if (state.isConnected && hasPendingSync()) {
5641
+ syncPendingOperations(); // 联网后自动同步
5642
+ }
5643
+ });
5644
+
5645
+ // 队列离线操作
5646
+ async function queueOperation(op: Operation) {
5647
+ await OfflineQueue.push(op);
5648
+ if (await NetInfo.fetch().then(s => s.isConnected)) {
5649
+ await flushQueue();
5650
+ }
5651
+ }
5652
+ ```
5653
+
5654
+ **输出**:离线架构方案 + 冲突解决策略 + 代码模板
5655
+
5656
+ 4. 无障碍审查
5657
+ 确保应用对视障、听障、行动不便用户友好:
5658
+
5659
+ **无障碍审查 Checklist**
5660
+
5661
+ **① 屏幕阅读器(VoiceOver/TalkBack)**
5662
+ ```swift
5663
+ // iOS — 所有交互元素必须有 accessibilityLabel
5664
+ imageView.accessibilityLabel = "用户头像"
5665
+ imageView.accessibilityHint = "双击查看个人资料"
5666
+
5667
+ // 自定义控件
5668
+ button.isAccessibilityElement = true
5669
+ button.accessibilityTraits = .button
5670
+ button.accessibilityLabel = "发布文章"
5671
+
5672
+ // ❌ 避免:仅用图标表示功能,无文字描述
5673
+ // ✅ 正确:图标 + accessibilityLabel
5674
+ ```
5675
+
5676
+ ```kotlin
5677
+ // Android
5678
+ imageButton.contentDescription = "发布文章"
5679
+ // 装饰性图片应设置 importantForAccessibility = no
5680
+ decorativeImage.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
5681
+ ```
5682
+
5683
+ **② 触控目标大小**
5684
+ ```
5685
+ 最小触控目标:
5686
+ - iOS: 44pt × 44pt(Apple HIG 标准)
5687
+ - Android: 48dp × 48dp(Material Design 标准)
5688
+
5689
+ 检查方法:
5690
+ - iOS Accessibility Inspector: Xcode → Open Developer Tool
5691
+ - Android: 开发者选项 → 显示触控区域
5692
+ ```
5693
+
5694
+ **③ 色彩对比度**
5695
+ ```
5696
+ WCAG 2.1 标准:
5697
+ - 正文文字(<18pt):对比度 ≥ 4.5:1
5698
+ - 大文字(≥18pt):对比度 ≥ 3:1
5699
+ - 用户界面组件:对比度 ≥ 3:1
5700
+
5701
+ 工具:
5702
+ - Colour Contrast Analyser(桌面工具)
5703
+ - axe DevTools(Web)
5704
+ - Xcode Accessibility Inspector(iOS)
5705
+ ```
5706
+
5707
+ **④ 动态字体支持**
5708
+ ```swift
5709
+ // iOS — 支持系统字体大小调整
5710
+ label.font = UIFont.preferredFont(forTextStyle: .body)
5711
+ label.adjustsFontForContentSizeCategory = true
5712
+ ```
5713
+
5714
+ **⑤ 减弱动态效果**
5715
+ ```swift
5716
+ // 尊重"减弱动态效果"设置
5717
+ if UIAccessibility.isReduceMotionEnabled {
5718
+ // 使用简单的淡入淡出替代复杂动画
5719
+ UIView.animate(withDuration: 0.1) { view.alpha = 1 }
5720
+ } else {
5721
+ // 完整动画
5722
+ performFullAnimation()
5723
+ }
5724
+ ```
5725
+
5726
+ **输出**:无障碍审查报告(VoiceOver/TalkBack + 色彩 + 触控 + 字体)
5727
+
5728
+ 5. 崩溃与 ANR 防护
5729
+ 防止主线程阻塞、内存泄漏和崩溃:
5730
+
5731
+ **崩溃防护 Checklist**
5732
+
5733
+ **① 主线程保护(ANR / 卡顿)**
5734
+ ```kotlin
5735
+ // Android — 严格模式检测主线程 I/O(开发阶段)
5736
+ if (BuildConfig.DEBUG) {
5737
+ StrictMode.setThreadPolicy(
5738
+ StrictMode.ThreadPolicy.Builder()
5739
+ .detectDiskReads()
5740
+ .detectDiskWrites()
5741
+ .detectNetwork()
5742
+ .penaltyLog()
5743
+ .build()
5744
+ )
5745
+ }
5746
+ ```
5747
+
5748
+ ```swift
5749
+ // iOS — 所有耗时操作移到后台线程
5750
+ // ❌ 在主线程执行网络请求(卡 UI)
5751
+ let data = try! Data(contentsOf: url) // 同步,阻塞主线程
5752
+
5753
+ // ✅ 后台执行,主线程更新 UI
5754
+ Task {
5755
+ let data = await fetchData(url: url) // 异步
5756
+ await MainActor.run {
5757
+ updateUI(data) // 回主线程
5758
+ }
5759
+ }
5760
+ ```
5761
+
5762
+ **② 内存泄漏防护**
5763
+ ```swift
5764
+ // iOS — 使用 weak/unowned 避免循环引用
5765
+ class ViewController: UIViewController {
5766
+ var closure: (() -> Void)?
5767
+
5768
+ func setup() {
5769
+ // ❌ 强引用循环
5770
+ closure = { self.doSomething() }
5771
+
5772
+ // ✅ weak 引用
5773
+ closure = { [weak self] in self?.doSomething() }
5774
+ }
5775
+ }
5776
+
5777
+ // 使用 Instruments 的 Leaks 模板检测
5778
+ // Xcode → Product → Profile → Leaks
5779
+ ```
5780
+
5781
+ **③ 崩溃上报集成**
5782
+ ```typescript
5783
+ // React Native — 集成 Sentry
5784
+ import * as Sentry from '@sentry/react-native';
5785
+
5786
+ Sentry.init({
5787
+ dsn: 'https://xxx@sentry.io/xxx',
5788
+ tracesSampleRate: 0.2,
5789
+ beforeSend: (event) => {
5790
+ // 过滤敏感信息
5791
+ delete event.user?.email;
5792
+ return event;
5793
+ },
5794
+ });
5795
+
5796
+ // 自定义崩溃上下文
5797
+ Sentry.setContext('order', { orderId, userId });
5798
+ ```
5799
+
5800
+ **④ 崩溃率基准**
5801
+ ```
5802
+ 指标 目标值 警戒线
5803
+ ──────────────────────────────
5804
+ 崩溃率 < 0.1% > 0.5%
5805
+ ANR 率 < 0.05% > 0.2%
5806
+ 内存 OOM 率 < 0.01% > 0.1%
5807
+ ```
5808
+
5809
+ **输出**:崩溃防护代码模板 + ANR 检测配置 + 崩溃率监控方案
5810
+
5811
+ 输出格式:平台合规报告(iOS + Android)+ 性能基准报告 + 离线架构方案 + 无障碍审查报告 + 崩溃防护 Checklist
5812
+
5813
+ ## 数据管道设计
5814
+
5815
+ 触发:数据管道 | data pipeline | etl | elt
5816
+ 说明:设计 Batch/Streaming 数据管道,制定数据质量规则、容错策略与血缘追踪方案
5817
+
5818
+ 1. 架构选型
5819
+ 根据数据特征和业务需求选择合适的管道架构:
5820
+
5821
+ **四大架构模式决策矩阵**
5822
+
5823
+ | 架构 | 延迟 | 复杂度 | 适用场景 | 代表工具 |
5824
+ |------|------|--------|---------|---------|
5825
+ | **Batch(批处理)** | 小时~天 | 低 | 日报、数据仓库 ETL、离线训练 | Airflow + Spark |
5826
+ | **Streaming(流处理)** | 秒~毫秒 | 高 | 实时监控、欺诈检测、事件驱动 | Kafka + Flink |
5827
+ | **Lambda(λ)** | 两套 | 极高 | 需要批量精确性+流式时效性 | Kafka + Spark + Hive |
5828
+ | **Kappa(κ)** | 秒 | 中 | 一切皆流,重播历史数据 | Kafka + Flink |
5829
+
5830
+ **决策树**
5831
+ ```
5832
+ 数据新鲜度要求 < 1分钟?
5833
+ ├── YES → Streaming(Kafka + Flink)
5834
+ └── NO
5835
+ └── 需要精确的历史重算?
5836
+ ├── YES + 实时结果 → Lambda 架构
5837
+ ├── YES + 可接受重播 → Kappa 架构
5838
+ └── NO → Batch(Airflow + Spark/dbt)
5839
+ ```
5840
+
5841
+ **Source → Transform → Sink 数据流图**
5842
+ ```
5843
+ 数据源(Sources):
5844
+ MySQL / PostgreSQL(CDC)
5845
+ Kafka Topics
5846
+ REST API / Webhook
5847
+ 文件系统(S3/GCS)
5848
+
5849
+
5850
+ 变换层(Transform):
5851
+ 数据清洗(去重/标准化/脱敏)
5852
+ 业务逻辑计算
5853
+ 聚合/Join
5854
+
5855
+
5856
+ 目标层(Sinks):
5857
+ 数据仓库(BigQuery/Snowflake/Redshift)
5858
+ 数据湖(S3/GCS Parquet)
5859
+ 搜索引擎(Elasticsearch)
5860
+ 特征存储(Feature Store)
5861
+ ```
5862
+
5863
+ **技术栈推荐**
5864
+ ```
5865
+ 小团队/初创:
5866
+ Airflow + dbt + BigQuery(低运维成本)
5867
+
5868
+ 中大型团队:
5869
+ Kafka + Flink + Iceberg + Trino(高吞吐实时)
5870
+
5871
+ 全托管方案:
5872
+ Fivetran(Ingestion)+ dbt Cloud(Transform)+ Snowflake(Warehouse)
5873
+ ```
5874
+
5875
+ **输出**:架构选型报告 + 数据流图
5876
+
5877
+ 2. 数据质量规则
5878
+ 定义并实施数据质量检查规则:
5879
+
5880
+ **数据质量四维度**
5881
+
5882
+ | 维度 | 定义 | 检查方式 | 阈值示例 |
5883
+ |------|------|---------|---------|
5884
+ | **完整性** | 必填字段不为空 | NULL 率检查 | < 0.1% |
5885
+ | **准确性** | 值在合理范围内 | 范围/格式校验 | 异常值率 < 1% |
5886
+ | **一致性** | 跨系统数据一致 | 对账/交叉校验 | 差异率 < 0.01% |
5887
+ | **及时性** | 数据按时到达 | 延迟监控 | 最大延迟 < 2h |
5888
+
5889
+ **dbt 数据质量测试(推荐)**
5890
+ ```yaml
5891
+ # models/orders.yml
5892
+ version: 2
5893
+ models:
5894
+ - name: orders
5895
+ columns:
5896
+ - name: order_id
5897
+ tests:
5898
+ - not_null
5899
+ - unique
5900
+ - name: user_id
5901
+ tests:
5902
+ - not_null
5903
+ - relationships:
5904
+ to: ref('users')
5905
+ field: id
5906
+ - name: status
5907
+ tests:
5908
+ - accepted_values:
5909
+ values: ['pending', 'paid', 'shipped', 'completed', 'cancelled']
5910
+ - name: amount
5911
+ tests:
5912
+ - not_null
5913
+ - dbt_expectations.expect_column_values_to_be_between:
5914
+ min_value: 0
5915
+ max_value: 1000000
5916
+ ```
5917
+
5918
+ **Great Expectations 数据期望(Python)**
5919
+ ```python
5920
+ import great_expectations as gx
5921
+
5922
+ context = gx.get_context()
5923
+ suite = context.add_expectation_suite("orders_suite")
5924
+
5925
+ # 定义数据期望
5926
+ suite.add_expectation(
5927
+ gx.expectations.ExpectColumnValuesToNotBeNull(column="order_id")
5928
+ )
5929
+ suite.add_expectation(
5930
+ gx.expectations.ExpectColumnValuesToBeBetween(
5931
+ column="amount", min_value=0, max_value=1_000_000
5932
+ )
5933
+ )
5934
+ suite.add_expectation(
5935
+ gx.expectations.ExpectTableRowCountToBeBetween(
5936
+ min_value=1000, # 每日订单不少于1000
5937
+ max_value=1_000_000
5938
+ )
5939
+ )
5940
+
5941
+ # 运行验证
5942
+ result = context.run_checkpoint("daily_orders_checkpoint")
5943
+ if not result.success:
5944
+ raise DataQualityError("数据质量检查未通过!")
5945
+ ```
5946
+
5947
+ **数据质量告警**
5948
+ ```yaml
5949
+ # 数据质量 SLA
5950
+ - NULL 率超过 1%:🔴 阻断管道,Slack 告警
5951
+ - 行数下降 > 20%(对比昨日):🟠 告警,人工核查
5952
+ - P99 延迟 > 2 小时:🟡 警告,关注
5953
+ ```
5954
+
5955
+ **输出**:数据质量规则文档 + dbt/GE 测试配置
5956
+
5957
+ 3. 管道设计与代码模板
5958
+ 生成可复用的管道代码模板:
5959
+
5960
+ **Airflow DAG 模板(Batch)**
5961
+ ```python
5962
+ # dags/daily_orders_etl.py
5963
+ from airflow import DAG
5964
+ from airflow.operators.python import PythonOperator
5965
+ from airflow.providers.postgres.hooks.postgres import PostgresHook
5966
+ from datetime import datetime, timedelta
5967
+
5968
+ default_args = {
5969
+ 'owner': 'data-team',
5970
+ 'retries': 3,
5971
+ 'retry_delay': timedelta(minutes=5),
5972
+ 'on_failure_callback': alert_slack,
5973
+ }
5974
+
5975
+ with DAG(
5976
+ 'daily_orders_etl',
5977
+ default_args=default_args,
5978
+ schedule_interval='0 2 * * *', # 每日凌晨2点
5979
+ start_date=datetime(2024, 1, 1),
5980
+ catchup=False, # 不回填历史
5981
+ tags=['orders', 'daily'],
5982
+ ) as dag:
5983
+
5984
+ extract = PythonOperator(task_id='extract', python_callable=extract_orders)
5985
+ validate = PythonOperator(task_id='validate', python_callable=run_quality_checks)
5986
+ transform = PythonOperator(task_id='transform', python_callable=transform_orders)
5987
+ load = PythonOperator(task_id='load', python_callable=load_to_warehouse)
5988
+ notify = PythonOperator(task_id='notify', python_callable=send_completion_report)
5989
+
5990
+ extract >> validate >> transform >> load >> notify
5991
+ ```
5992
+
5993
+ **Kafka + Flink 流处理模板**
5994
+ ```python
5995
+ # Flink Python API
5996
+ from pyflink.datastream import StreamExecutionEnvironment
5997
+ from pyflink.datastream.connectors.kafka import KafkaSource, KafkaSink
5998
+
5999
+ env = StreamExecutionEnvironment.get_execution_environment()
6000
+ env.set_parallelism(4)
6001
+
6002
+ # Source
6003
+ kafka_source = KafkaSource.builder() .set_bootstrap_servers("kafka:9092") .set_topics("orders") .set_group_id("flink-order-processor") .set_value_only_deserializer(JsonRowDeserializationSchema()) .build()
6004
+
6005
+ stream = env.from_source(kafka_source, WatermarkStrategy.no_watermarks(), "Kafka Source")
6006
+
6007
+ # Transform(滚动窗口:每分钟统计)
6008
+ result = stream .key_by(lambda x: x['region']) .window(TumblingEventTimeWindows.of(Time.minutes(1))) .aggregate(OrderAggregateFunction())
6009
+
6010
+ # Sink
6011
+ result.sink_to(KafkaSink.builder()
6012
+ .set_bootstrap_servers("kafka:9092")
6013
+ .set_record_serializer(JsonRowSerializationSchema())
6014
+ .build())
6015
+
6016
+ env.execute("Order Stream Processing")
6017
+ ```
6018
+
6019
+ **输出**:Airflow DAG 模板 + Flink/Spark 流处理模板
6020
+
6021
+ 4. 容错与重试策略
6022
+ 确保管道在故障时可靠恢复:
6023
+
6024
+ **核心容错原则**
6025
+
6026
+ **① 幂等性设计(最重要)**
6027
+ ```python
6028
+ def load_orders_idempotent(batch_date: str, orders: list):
6029
+ """幂等加载:同一批次多次执行结果相同"""
6030
+ # 使用 INSERT ... ON CONFLICT DO NOTHING
6031
+ # 或 MERGE(UPSERT)语义
6032
+ pg_hook.run("""
6033
+ INSERT INTO orders_dw (order_id, batch_date, amount, status)
6034
+ VALUES %s
6035
+ ON CONFLICT (order_id) DO UPDATE SET
6036
+ amount = EXCLUDED.amount,
6037
+ status = EXCLUDED.status,
6038
+ updated_at = NOW()
6039
+ """, [(o['id'], batch_date, o['amount'], o['status']) for o in orders])
6040
+ ```
6041
+
6042
+ **② 死信队列(DLQ)**
6043
+ ```python
6044
+ # Kafka — 处理失败消息路由到 DLQ
6045
+ def process_message(msg):
6046
+ try:
6047
+ transform_and_load(msg)
6048
+ except Exception as e:
6049
+ # 路由到死信队列,保留原始消息 + 错误信息
6050
+ dlq_producer.send('orders-dlq', {
6051
+ 'original_message': msg,
6052
+ 'error': str(e),
6053
+ 'failed_at': datetime.utcnow().isoformat(),
6054
+ 'retry_count': msg.get('retry_count', 0) + 1
6055
+ })
6056
+ logger.error(f"Message sent to DLQ: {e}")
6057
+ ```
6058
+
6059
+ **③ 断点续传(Checkpoint)**
6060
+ ```python
6061
+ def process_large_dataset():
6062
+ checkpoint_file = '.etl_checkpoint'
6063
+ last_id = load_checkpoint(checkpoint_file)
6064
+
6065
+ for batch in fetch_in_batches(after_id=last_id, batch_size=1000):
6066
+ process_batch(batch)
6067
+ save_checkpoint(checkpoint_file, batch[-1]['id']) # 每批保存进度
6068
+ ```
6069
+
6070
+ **④ 指数退避重试**
6071
+ ```python
6072
+ import tenacity
6073
+
6074
+ @tenacity.retry(
6075
+ wait=tenacity.wait_exponential(multiplier=1, min=4, max=60),
6076
+ stop=tenacity.stop_after_attempt(5),
6077
+ retry=tenacity.retry_if_exception_type(TransientError),
6078
+ before_sleep=tenacity.before_sleep_log(logger, logging.WARNING)
6079
+ )
6080
+ def call_external_api(data):
6081
+ return requests.post(API_URL, json=data, timeout=30)
6082
+ ```
6083
+
6084
+ **输出**:容错策略文档 + 幂等加载模板 + DLQ 配置
6085
+
6086
+ 5. 数据血缘与可观测性
6087
+ 建立数据血缘追踪和管道监控体系:
6088
+
6089
+ **数据血缘(Data Lineage)**
6090
+ ```python
6091
+ # OpenLineage 标准(Marquez/Atlan/DataHub 支持)
6092
+ from openlineage.client import OpenLineageClient
6093
+ from openlineage.client.run import RunEvent, Job, Run, Dataset
6094
+
6095
+ client = OpenLineageClient.from_environment()
6096
+
6097
+ # 记录数据流转关系
6098
+ client.emit(RunEvent(
6099
+ eventType="COMPLETE",
6100
+ job=Job(namespace="etl", name="daily_orders_transform"),
6101
+ run=Run(runId=str(uuid4())),
6102
+ inputs=[Dataset(namespace="postgres", name="raw_orders")],
6103
+ outputs=[Dataset(namespace="bigquery", name="orders_dw.fact_orders")]
6104
+ ))
6105
+ ```
6106
+
6107
+ **管道健康仪表盘指标**
6108
+ ```
6109
+ 关键指标(Grafana 面板):
6110
+ ─────────────────────────────
6111
+ 延迟指标:
6112
+ - 数据新鲜度(最新记录时间戳)
6113
+ - Pipeline P95 执行时长
6114
+ - Kafka Consumer Lag
6115
+
6116
+ 质量指标:
6117
+ - 每日 NULL 率趋势
6118
+ - 行数异常检测(±30% 告警)
6119
+ - 数据质量测试通过率
6120
+
6121
+ 运维指标:
6122
+ - 管道成功率(目标 > 99%)
6123
+ - 重试次数分布
6124
+ - DLQ 消息积压
6125
+ ```
6126
+
6127
+ **Airflow SLA 监控**
6128
+ ```python
6129
+ with DAG(
6130
+ 'daily_orders_etl',
6131
+ sla_miss_callback=sla_miss_alert, # SLA 超时回调
6132
+ ...
6133
+ ) as dag:
6134
+ load_task = PythonOperator(
6135
+ task_id='load',
6136
+ python_callable=load_to_warehouse,
6137
+ sla=timedelta(hours=4), # 必须在4小时内完成
6138
+ )
6139
+ ```
6140
+
6141
+ **数据目录集成**
6142
+ ```yaml
6143
+ # dbt docs(自动生成)
6144
+ # 运行后访问:dbt docs serve
6145
+ # 包含:模型血缘图、列描述、测试结果
6146
+
6147
+ # 推荐工具
6148
+ 生产级:DataHub, Atlan, Alation
6149
+ 开源轻量:Marquez, OpenMetadata
6150
+ dbt 生态:dbt docs + Elementary
6151
+ ```
6152
+
6153
+ **输出**:数据血缘配置 + 监控仪表盘指标定义 + 数据目录方案
6154
+
6155
+ 输出格式:架构选型文档 + 数据流图 + 质量规则配置(dbt/GE)+ 管道代码模板 + 容错策略 + 血缘追踪方案
6156
+
6157
+ ## ML 实验管理
6158
+
6159
+ 触发:ml experiment | mlops | model training | 模型训练
6160
+ 说明:规范 ML 实验设计、追踪配置(MLflow/W&B)、数据版本控制、Model Card 生成与部署监控
6161
+
6162
+ 1. 实验设计
6163
+ 在开始训练前明确实验目标和控制变量:
6164
+
6165
+ **假设驱动实验设计**
6166
+ ```markdown
6167
+ ## 实验设计文档
6168
+
6169
+ ### 研究问题
6170
+ 我们假设:[变量 X] 会导致 [指标 Y] 提升 [Z%],
6171
+ 因为 [理论依据或先验知识]。
6172
+
6173
+ ### 实验配置
6174
+ - 控制组(Baseline):[现有模型/方法描述]
6175
+ - 实验组:[变更内容描述]
6176
+ - 控制变量:[保持不变的因素:数据集/超参/随机种子]
6177
+
6178
+ ### 评估指标
6179
+ - 主要指标:AUC-ROC(因为关注排序,不关注具体阈值)
6180
+ - 次要指标:Precision@K, Recall@K
6181
+ - 业务指标:CTR 提升(线上 A/B 验证)
6182
+
6183
+ ### 成功标准
6184
+ - 主要指标提升 ≥ 2%(统计显著性 p < 0.05)
6185
+ - 推理延迟不增加超过 20%
6186
+ - 训练成本不超过 $50
6187
+ ```
6188
+
6189
+ **实验矩阵(超参搜索)**
6190
+ ```python
6191
+ # 使用 Optuna 自动超参优化
6192
+ import optuna
6193
+
6194
+ def objective(trial):
6195
+ params = {
6196
+ 'learning_rate': trial.suggest_float('lr', 1e-5, 1e-1, log=True),
6197
+ 'batch_size': trial.suggest_categorical('batch_size', [16, 32, 64, 128]),
6198
+ 'dropout': trial.suggest_float('dropout', 0.1, 0.5),
6199
+ 'hidden_dim': trial.suggest_int('hidden_dim', 64, 512, step=64),
6200
+ }
6201
+ model = train_model(**params)
6202
+ return evaluate(model)['auc']
6203
+
6204
+ study = optuna.create_study(direction='maximize')
6205
+ study.optimize(objective, n_trials=50, timeout=3600)
6206
+ print(f"Best params: {study.best_params}")
6207
+ ```
6208
+
6209
+ **最小可行实验(MVE)原则**
6210
+ ```
6211
+ 先用 10% 数据快速验证假设(10min 训练)
6212
+ → 有提升信号 → 扩展到全量数据
6213
+ → 无提升信号 → 调整假设重新实验
6214
+ ```
6215
+
6216
+ **输出**:实验设计文档 + 评估指标定义 + 超参搜索配置
6217
+
6218
+ 2. 实验追踪配置
6219
+ 配置 MLflow 或 W&B 实现实验全自动追踪:
6220
+
6221
+ **MLflow 完整配置**
6222
+ ```python
6223
+ import mlflow
6224
+ import mlflow.pytorch
6225
+
6226
+ # 配置追踪服务器
6227
+ mlflow.set_tracking_uri("http://mlflow-server:5000")
6228
+ mlflow.set_experiment("recommendation-model-v2")
6229
+
6230
+ with mlflow.start_run(run_name="bert-finetune-lr1e-4") as run:
6231
+ # 记录所有超参
6232
+ mlflow.log_params({
6233
+ "model_name": "bert-base-chinese",
6234
+ "learning_rate": 1e-4,
6235
+ "batch_size": 32,
6236
+ "epochs": 10,
6237
+ "optimizer": "AdamW",
6238
+ "warmup_steps": 500,
6239
+ })
6240
+
6241
+ # 记录数据集信息
6242
+ mlflow.log_param("train_samples", len(train_dataset))
6243
+ mlflow.log_param("data_version", "v2024-01-15")
6244
+
6245
+ for epoch in range(config.epochs):
6246
+ train_loss = train_epoch(model, train_loader)
6247
+ val_metrics = evaluate(model, val_loader)
6248
+
6249
+ # 实时记录指标
6250
+ mlflow.log_metrics({
6251
+ "train_loss": train_loss,
6252
+ "val_loss": val_metrics['loss'],
6253
+ "val_auc": val_metrics['auc'],
6254
+ "val_f1": val_metrics['f1'],
6255
+ }, step=epoch)
6256
+
6257
+ # 注册最终模型
6258
+ mlflow.pytorch.log_model(
6259
+ model,
6260
+ "model",
6261
+ registered_model_name="recommendation-model",
6262
+ pip_requirements=["torch==2.1.0", "transformers==4.35.0"],
6263
+ )
6264
+
6265
+ # 记录评估报告
6266
+ mlflow.log_artifact("reports/confusion_matrix.png")
6267
+ mlflow.log_artifact("reports/feature_importance.html")
6268
+
6269
+ print(f"Run ID: {run.info.run_id}")
6270
+ ```
6271
+
6272
+ **W&B 配置(更丰富的可视化)**
6273
+ ```python
6274
+ import wandb
6275
+
6276
+ wandb.init(
6277
+ project="recommendation-v2",
6278
+ name="bert-finetune-lr1e-4",
6279
+ config={
6280
+ "learning_rate": 1e-4,
6281
+ "architecture": "bert-base",
6282
+ "dataset": "user-clicks-v2",
6283
+ },
6284
+ tags=["bert", "production-candidate"],
6285
+ )
6286
+
6287
+ # 自动记录梯度和权重(PyTorch)
6288
+ wandb.watch(model, log='all', log_freq=100)
6289
+
6290
+ # 训练循环中
6291
+ wandb.log({"train_loss": loss, "val_auc": auc}, step=epoch)
6292
+
6293
+ # 完成时
6294
+ wandb.finish()
6295
+ ```
6296
+
6297
+ **输出**:MLflow/W&B 实验追踪配置 + 训练脚本模板
6298
+
6299
+ 3. 数据版本控制
6300
+ 使用 DVC 管理数据集和特征工程的版本:
6301
+
6302
+ **DVC 初始化与数据版本管理**
6303
+ ```bash
6304
+ # 初始化 DVC(与 Git 协同)
6305
+ git init && dvc init
6306
+
6307
+ # 添加数据集到 DVC 管理
6308
+ dvc add data/train.parquet
6309
+ dvc add data/test.parquet
6310
+ git add data/.gitignore data/train.parquet.dvc data/test.parquet.dvc
6311
+ git commit -m "Add training data v1"
6312
+
6313
+ # 配置远程存储(S3/GCS/Azure)
6314
+ dvc remote add -d myremote s3://my-ml-bucket/dvc-store
6315
+ dvc push # 上传数据到远程
6316
+
6317
+ # 切换到不同数据版本
6318
+ git checkout v1.0-data-tag
6319
+ dvc pull # 下载对应版本数据
6320
+ ```
6321
+
6322
+ **特征工程管道(可复现)**
6323
+ ```python
6324
+ # dvc.yaml — 定义可复现的管道
6325
+ stages:
6326
+ prepare_data:
6327
+ cmd: python src/prepare.py --input data/raw --output data/prepared
6328
+ deps:
6329
+ - src/prepare.py
6330
+ - data/raw
6331
+ outs:
6332
+ - data/prepared
6333
+
6334
+ feature_engineering:
6335
+ cmd: python src/features.py --input data/prepared --output data/features
6336
+ deps:
6337
+ - src/features.py
6338
+ - data/prepared
6339
+ - params.yaml # 特征工程超参
6340
+ outs:
6341
+ - data/features
6342
+ metrics:
6343
+ - reports/feature_stats.json
6344
+
6345
+ train:
6346
+ cmd: python src/train.py
6347
+ deps:
6348
+ - src/train.py
6349
+ - data/features
6350
+ outs:
6351
+ - models/model.pkl
6352
+ metrics:
6353
+ - reports/metrics.json
6354
+ ```
6355
+
6356
+ ```bash
6357
+ # 运行完整管道(只重新执行有变化的阶段)
6358
+ dvc repro
6359
+
6360
+ # 对比不同版本的指标
6361
+ dvc metrics diff v1.0 v2.0
6362
+ # ┌──────────────┬───────┬───────┬────────┐
6363
+ # │ Metric │ HEAD │ v1.0 │ Change │
6364
+ # ├──────────────┼───────┼───────┼────────┤
6365
+ # │ val_auc │ 0.847 │ 0.831 │ +0.016 │
6366
+ # └──────────────┴───────┴───────┴────────┘
6367
+ ```
6368
+
6369
+ **输出**:DVC 配置 + 特征管道 dvc.yaml + 版本对比命令
6370
+
6371
+ 4. Model Card 生成
6372
+ 为每个发布的模型生成标准化的 Model Card:
6373
+
6374
+ **Model Card 模板**
6375
+ ```markdown
6376
+ # Model Card: 推荐模型 v2.1.0
6377
+
6378
+ ## 模型概述
6379
+ - **模型类型**:双塔召回模型(BERT + 协同过滤)
6380
+ - **任务**:电商商品推荐
6381
+ - **训练日期**:2024-01-20
6382
+ - **版本**:v2.1.0
6383
+ - **MLflow Run ID**:abc123def456
6384
+
6385
+ ## 预期用途
6386
+ ### 主要用途
6387
+ 为已登录用户生成个性化商品推荐,覆盖首页、详情页猜你喜欢。
6388
+
6389
+ ### 不适合的用途
6390
+ - 冷启动用户(注册 < 7 天)→ 使用热门推荐替代
6391
+ - 价格敏感决策(不提供价格预测)
6392
+
6393
+ ## 训练数据
6394
+ - **数据集**:user-click-events v2024-01
6395
+ - **训练样本**:5,200,000 条用户点击行为
6396
+ - **时间范围**:2023-07-01 ~ 2023-12-31
6397
+ - **数据版本**:DVC tag `data-v2024-01`
6398
+
6399
+ ## 性能指标
6400
+ | 指标 | 离线值 | 在线 A/B(1周) |
6401
+ |------|--------|---------------|
6402
+ | Recall@20 | 0.847 | 0.821 |
6403
+ | NDCG@20 | 0.623 | 0.598 |
6404
+ | CTR | — | +4.2%(vs 旧模型)|
6405
+ | Latency P99 | 45ms | 52ms |
6406
+
6407
+ ## 偏差与公平性
6408
+ - 对新品(上架 < 30 天)存在曝光不足偏差 → 已通过 Explore 策略缓解
6409
+ - 价格区间分布分析:低价商品点击率被高估(训练集偏差)→ 已加权修正
6410
+
6411
+ ## 限制
6412
+ - 模型不包含实时库存信息,需上层过滤下架商品
6413
+ - 不支持多语言商品(仅中文描述)
6414
+
6415
+ ## 负责任的 AI 声明
6416
+ - 不使用性别/年龄等受保护属性作为特征
6417
+ - 符合公司隐私政策,用户行为数据已脱敏处理
6418
+ ```
6419
+
6420
+ **自动生成脚本**
6421
+ ```python
6422
+ def generate_model_card(run_id: str, ab_results: dict) -> str:
6423
+ """从 MLflow run 自动生成 Model Card"""
6424
+ run = mlflow.get_run(run_id)
6425
+ params = run.data.params
6426
+ metrics = run.data.metrics
6427
+
6428
+ return MODEL_CARD_TEMPLATE.format(
6429
+ version=params.get('model_version'),
6430
+ train_date=run.info.start_time,
6431
+ recall_at_20=metrics.get('val_recall_at_20'),
6432
+ ctr_lift=ab_results.get('ctr_lift'),
6433
+ run_id=run_id,
6434
+ )
6435
+ ```
6436
+
6437
+ **输出**:Model Card 文档 + 自动生成脚本
6438
+
6439
+ 5. 部署与监控
6440
+ 模型上线、A/B 测试和生产监控完整方案:
6441
+
6442
+ **模型注册与晋升流程**
6443
+ ```python
6444
+ # MLflow 模型注册与阶段管理
6445
+ from mlflow.tracking import MlflowClient
6446
+
6447
+ client = MlflowClient()
6448
+
6449
+ # 注册模型
6450
+ model_uri = f"runs:/{run_id}/model"
6451
+ mv = client.create_model_version(
6452
+ name="recommendation-model",
6453
+ source=model_uri,
6454
+ run_id=run_id,
6455
+ )
6456
+
6457
+ # 模型阶段:None → Staging → Production
6458
+ # 先晋升到 Staging(测试)
6459
+ client.transition_model_version_stage(
6460
+ name="recommendation-model",
6461
+ version=mv.version,
6462
+ stage="Staging",
6463
+ )
6464
+
6465
+ # 验证通过后晋升到 Production
6466
+ client.transition_model_version_stage(
6467
+ name="recommendation-model",
6468
+ version=mv.version,
6469
+ stage="Production",
6470
+ archive_existing_versions=True, # 归档旧版本
6471
+ )
6472
+ ```
6473
+
6474
+ **A/B 测试配置**
6475
+ ```python
6476
+ # 按用户哈希分流
6477
+ def get_model_for_user(user_id: str) -> str:
6478
+ hash_val = int(hashlib.md5(user_id.encode()).hexdigest(), 16) % 100
6479
+ if hash_val < 10: # 10% 流量
6480
+ return "recommendation-model:v2.1.0"
6481
+ else:
6482
+ return "recommendation-model:v2.0.0"
6483
+
6484
+ # 记录 A/B 分组(用于统计分析)
6485
+ mlflow.log_param("ab_group", "control" if version == "v2.0.0" else "treatment")
6486
+ ```
6487
+
6488
+ **数据漂移监控(Evidently)**
6489
+ ```python
6490
+ from evidently.report import Report
6491
+ from evidently.metric_preset import DataDriftPreset, ModelPerformancePreset
6492
+
6493
+ # 对比训练数据分布 vs 生产数据分布
6494
+ report = Report(metrics=[
6495
+ DataDriftPreset(), # 特征分布漂移
6496
+ ModelPerformancePreset(), # 模型性能漂移
6497
+ ])
6498
+
6499
+ report.run(reference_data=train_df, current_data=production_df_last_7d)
6500
+ report.save_html("reports/drift_report.html")
6501
+
6502
+ # 自动回滚条件
6503
+ drift_detected = report.as_dict()['metrics'][0]['result']['dataset_drift']
6504
+ if drift_detected:
6505
+ alert_and_rollback(reason="Feature distribution drift detected")
6506
+ ```
6507
+
6508
+ **生产监控仪表盘**
6509
+ ```
6510
+ 关键指标(每日监控):
6511
+ ─────────────────────────────────
6512
+ 模型性能:
6513
+ 在线 CTR(实验 vs 对照)
6514
+ 推荐命中率(Recall@K)
6515
+
6516
+ 数据漂移:
6517
+ 用户特征分布变化(PSI > 0.2 告警)
6518
+ 商品特征新增/消失比率
6519
+
6520
+ 运维指标:
6521
+ 推理延迟 P99(< 100ms)
6522
+ 模型服务错误率(< 0.1%)
6523
+ 预测请求量(异常波动告警)
6524
+ ```
6525
+
6526
+ **输出**:模型注册流程 + A/B 测试配置 + 漂移监控脚本 + 自动回滚方案
6527
+
6528
+ 输出格式:实验设计文档 + MLflow/W&B 追踪配置 + DVC 数据管道 + Model Card + 部署 A/B 测试方案 + 漂移监控配置
6529
+
3727
6530
  ---
3728
6531
  规则:严格按步骤执行,不跳步,输出遵循各 Skill 的格式模板。