POST /reports 骨架:资格校验→终身配额预扣→tk_task→cpu 队列;动态装配 流水线(dotted-path 注册、缺阶段即 CONFIG_ERROR);中间态逐段落库、失败 退款、derived_features 跨重试/重生成复用(T-P3-07/09)。 实现:packages/report 的 pipeline.py(Stage 契约保持不变,新增 assemble_stages/ run_pipeline/StageFailureError,ReportContext 增可选 task_id 参数、向后兼容)、 repo.py、schemas.py、service.py(重放优先于资格校验、reserve+建行+建任务同事务、 提交后才投递、并发同键落败回滚);apps/api/routers/reports.py 填桩(send_task 按 任务名投递,不 import apps.worker);apps/worker/report_tasks.py(worker 侧自建 gateway 装配、状态归一 QUEUED→RUNNING、成功 commit 配额+成本聚合写 model_info、 基础设施异常重试一次、重试耗尽兜底失败退款)。测试:单测 6 个(装配缺失/空配置/ 协议违约/顺序执行/失败保留产物),集成 6 场景(预扣 lifetime/PENDING、成功 committed+两阶段键、同 request_key 幂等不重复扣、二次首生成 40920、阶段失败 refund+derived_features 保留、regenerate 走 m: 月配额 version+1 复用视觉中间态)。 偏差说明: 1) apps/worker/celery_app.py 的 include 只含 apps.worker.tasks(禁改文件),真 worker 进程注册 meida.report.generate 需协调者合并时把 include 补上 apps.worker.report_tasks;API 侧已按任务名 send_task,两侧只靠名字衔接,测试 不受影响。2) ReportContext 增加可选 task_id 关键字参数(失败结案要推动 tk_task),Stage 协议/DEFAULT_STAGE_PATHS/stage_names 未动,T16/T17 兼容。 3) JSONB 深变更用 flag_modified 显式标脏(重新赋相等新 dict 会被 flush 的按值 比较跳过,集成测试抓到过该坑)。4) POST 后 tk_task 保持 PENDING(任务文档 §4 场景 1 口径),QUEUED→RUNNING 由 worker 入口归一。
apps/ — 可部署的进程
| 目录 | 进程 |
|---|---|
api/ |
FastAPI 应用:路由、依赖注入、中间件 |
worker/ |
Celery 任务定义与四队列配置(gpu / cpu / io / beat) |
admin/ |
极简运营后台(占位,FR-ADM-01~05) |
API 层的职责边界(架构 §3.1 L3)
只做编排:认证鉴权、参数校验、幂等、配额校验与预扣、统一响应、OpenAPI。
不能放什么
- 业务规则。路由函数里出现
if判断业务条件,几乎总是应该下沉到packages/<域>/service.py或packages/domain/。 - 直接写 SQL 或用 ORM 查表。走域的
service。 - 直接调用外部 API。LLM 走
packages/llm;未来的审核、视觉、换装同理。 - 裸
dict返回。所有响应必须经过packages.shared.response.ok()/fail()。
契约先行(NFR-MAINT-01)
先在 apps/api/schemas.py 定义 Pydantic schema,再写实现。客户端类型由
OpenAPI 生成,schema 一变客户端立刻可见。
Worker 的规矩
- 每个长耗时任务都必须落
tk_task,走packages/task的状态机。 不要自己维护状态字段。 - 失败必须退还配额(
billing.refund(),FR-BILL-05)。apps/worker/tasks.py里的ping_task是形状示例。 - 队列按资源画像分:GPU 推理进
gpu,视觉/渲染进cpu,外部调用进io。 混队列会让「换装排队把报告拖慢」。