Files
mindfulness/spec_kit/Personalized Reco/modules/integration-api-worker/plan.md
2026-02-02 16:47:37 +08:00

6.8 KiB
Raw Blame History

IntegrationFastAPI API + Celery WorkerPlan

对应规范:spec_kit/Personalized Reco/modules/integration-api-worker/spec.md

依赖(已实现):

  • server/app/features/personalized_reco/reco_engine/:统一引擎入口 recommend(...)
  • server/app/features/personalized_reco/content_repository/SqlAlchemyContentRepository
  • server/app/db/session.pyget_db / AsyncSessionLocal
  • server/app/worker.pycelery_app

本计划已按确认项固化:

  • API 路由:按场景拆分(/v1/reco/feed/v1/reco/push/v1/reco/widget
  • localeAccept-Language 解析并映射到 en/tc,缺省 en
  • 限流:按客户端 IP1 分钟 10 次
  • Celery实现 tasks.reco.generate + tasks.reco.push_once
  • Celery 内调用 async使用 asyncio.run(...)
  • now 注入:支持 Header X-Now(方案 B并保留请求体 nowspec 已定义)

1. 目标与交付物

1.1 目标

  • 对外提供推荐能力:
    • FastAPI:客户端同步获取推荐结果。
    • Celery后台任务式生成推荐Push/Widget 的定时/批处理)。
  • 不复制推荐逻辑API 与任务均只调用同一 Reco Engine
  • 提供基础可用的 IP 限流1 分钟 10 次)。
  • 支持 now 注入 以实现确定性回归测试。
  • 多语言仅支持 EN/TC,不允许语言回退。

1.2 交付物tasks 阶段落地)

  • 新增 API 路由文件(建议):
    • server/app/api/v1/reco.py
  • main.py 注册路由:
    • app.include_router(reco_router)
  • 新增 Celery 任务:
    • server/app/tasks/reco.py(包含 tasks.reco.generatetasks.reco.push_once
  • 新增限流中间件/依赖:
    • server/app/api/limits.py(或 server/app/core/ratelimit.py
  • 单元/集成测试(至少):
    • API schema 校验(请求/响应模型)
    • 限流行为(同 IP 超过阈值返回 429
    • Celery 任务能跑通 ping -> reco.generate

2. FastAPI 设计

2.1 路由与接口

新增推荐路由:

  • POST /v1/reco/feed
  • POST /v1/reco/push
  • POST /v1/reco/widget

说明:

  • 每个路由内部将 scene 固定为对应场景,避免客户端传错。
  • k 若未传按引擎默认建议feed=30push/widget=1此默认可在 API 层写死或由调用方显式传入)。

2.2 请求/响应模型(建议)

请求体 RecoRequestpydantic

  • k: Optional[int]
  • user_profile: UserProfileV1_2
  • already_recommended_ids: list[str|int] = []
  • touched_or_viewed_ids: list[str|int] = []
  • now: Optional[datetime] = None(用于测试;生产通常不传)

响应体 RecoResponsepydantic

  • items: list[RecommendedItem]
  • meta: RecoMeta

说明:可以直接复用引擎的 RecoEngineResult/RecommendedItem/RecoMeta 作为 response_model减少重复。

2.3 now 注入优先级(确定性)

支持两种注入:

  • HeaderX-NowISO8601 字符串,如 2026-02-02T12:00:00Z
  • Bodynow

建议优先级:

  1. X-Now 存在且可解析 → 使用 header 的时间
  2. 否则若 body.now 存在 → 使用 body.now
  3. 否则 → 使用服务端 datetime.now(timezone.utc)

2.4 locale 获取与映射EN/TC

来源HTTP Header Accept-Language

建议解析规则(无需额外依赖):

  • 若 header 缺失/空 → "en"
  • 若包含 zh-TW/zh-HK/tc"tc"
  • 否则默认 "en"

随后调用 content_repository.types.normalize_locale(locale) 做严格校验(保证只出 en/tc)。

2.5 依赖注入与数据库会话

API 层使用 Depends(get_db) 注入 AsyncSession

  • 在 handler 内创建 SqlAlchemyContentRepository(session)
  • 调用 reco_engine.recommend(repo=..., scene=..., ...)

2.6 限流(按 IP1 分钟 10 次)

实现方式V1 推荐:无外部依赖、内存版):

  • 在 FastAPI 层添加一个依赖或中间件:
    • Request.client.host 取 IP若有反代需后续支持 X-Forwarded-ForV1 先不做)
    • 使用滑动窗口或固定窗口计数(推荐固定窗口:按分钟 bucket
    • 超过阈值:返回 HTTP 429,响应体包含 detail="rate_limited"

注意与取舍:

  • 内存限流在多进程/多实例下不共享V1 可接受);后续可升级为 Redis 限流。

3. Celery Worker 设计

3.1 任务列表

  • tasks.reco.generate
    • 输入:与 API 等价,但建议 payload 小user_profile + ids + scene + 可选 now/locale
    • 输出默认忽略结果worker 已配置 task_ignore_result),但函数可返回 items/meta 用于调试
  • tasks.reco.push_once
    • 输入:尽量只包含 push 需要字段user_profile + ids + 可选 now/locale
    • 行为:内部调用 tasks.reco.generate(scene="push")并预留“写入下游”的占位函数V1 不接真实推送系统)

3.2 任务内调用推荐引擎async → sync

由于 Reco Engine 为 asyncCelery task 为 sync采用

  • asyncio.run(_run_reco_async(...))

其中 _run_reco_async 负责:

  • async with AsyncSessionLocal() as session:
  • repo = SqlAlchemyContentRepository(session)
  • await recommend(repo=repo, ...)

说明:

  • Celery 环境通常没有运行中的事件循环,asyncio.run 可用。
  • 若未来引入 async worker/或在已有 loop 环境中调用,再考虑改为“可复用事件循环工具”。

3.3 locale 与 now

  • locale
    • Celery 输入可直接传 "en"/"tc",缺省 "en"
    • 仍通过 normalize_locale 严格校验
  • now
    • 任务输入支持传入 now(用于回归测试/离线批处理),否则用服务端当前时间

4. 一致性策略API vs Celery

必须保证:

  • API 与任务都只调用 reco_engine.recommend
  • 对同一份输入(含固定 now/locale输出 items 结果一致(允许浮点微差)

建议做一个对比测试:

  • 在测试中构造固定 now,用同样的 repo/同样的输入分别走 API handler 与 Celery 的 _run_reco_async,断言 content_id 列表一致。

5. 错误处理与返回规范

5.1 API 错误处理

  • 请求体校验失败FastAPI 422
  • locale 不支持:返回 200 但 items 为空(由引擎兜底)或 400可选
    • V1 建议:保持与引擎一致,返回空 items + meta并在 meta.config_snapshot 记录错误 stage
  • 限流触发429

5.2 Celery 错误处理

  • 任务内部捕获异常并记录日志
  • 默认不回写结果,避免 Redis 占用
  • 必要时将错误信息写入任务日志或后续的可观测系统V2

6. 安全与性能V1

  • 限流:按 IP 10/min保护服务与数据库
  • Payload 控制:
    • Celery 输入避免传大数组;历史集合若过大,后续演进为“传引用 ID”
  • 多语言:只支持 EN/TC不做语言回退与 repository 口径一致)