API 契约:层③规则试验台 — social-media-organic-tool

增量契约,只定义规则试验台 3 接口。照 docs/templates/COMPONENT-BACKEND.md §4。 其余接口见现有 API-CONTRACT.md。前端标准件 test-bench.js 消费本契约,前端模板不改。

通用约定


1. GET /api/rules

拉当前 tenant 的规则清单(R1–R6),试验台开面板时调。

Request:无 body。可选 ?id=R1 只取一条。

Response 200:

{ "success": true, "data": [
  { "id": "R1", "action": "搜索方向生成",
    "why": "方向太泛搜出全是meme/太窄漏爆款,营销方向性错则找爆款链从源头歪",
    "config": { "knobs": { "direction_count_min": 3, "direction_count_max": 5,
                           "anchor_intent": "commercial_review", "avoid_nsfw": true, "max_tokens": 1000 },
                "prompt_template": "You are a social media MARKETING researcher ..." },
    "version": 1, "state": "theory" }
  ], "error": null }

2. POST /api/rules/{id}/dryrun

传入的 config(可能是 Anderson 刚改、未存的调整值)对 sample 跑一次该动作,返回 output。不落库、不计正式账。

Request body:

{ "config": { "knobs": { "...": "..." }, "prompt_template": "..." },
  "sample": { "material_id": 123 } }

Response 200:

{ "success": true, "data": { "output": { "...": "该动作的产出" },
                             "usage": { "input_tokens": 0, "output_tokens": 0, "est_cost_usd": 0.0 } },
  "error": null }

花钱红线(硬约束,backend 必做):

  1. 复用 check_input_length(Gate-2)+ check_daily_cost(Gate-4)+ clamp_max_tokens(Gate-3,把 config 里的 max_tokens 夹到 .env 全局上限内);
  2. 复用 fastapi-limiter RateLimiter;
  3. 额外:每 tenant 每日 dryrun 次数上限(防刷爆费用),超限返回 429。

错误码表:

HTTPerror.code触发前端表现
400INVALID_CONFIGconfig 结构非法 / 缺必填旋钮 / prompt_template 非字符串提示"配置格式错误"
400INVALID_SAMPLEsample 缺字段 / material_id 不存在提示"样本无效"
401UNAUTHORIZED无 / 过期 token跳登录
404RULE_NOT_FOUND{id} 不是 R1–R6提示"规则不存在"
413INPUT_TOO_LONGLLM_MAX_INPUT_CHARS(Gate-2)提示"输入过长"
429RATE_LIMITED触限流(用户/IP)提示"操作太频繁,稍后再试"
429DRYRUN_QUOTA_EXCEEDED超当日 dryrun 次数上限提示"今日试跑次数已用完"
429DAILY_COST_CAP触 Gate-4 日成本上限提示"今日 AI 额度已用完"
502LLM_UPSTREAM_ERRORDeepSeek/上游异常提示"AI 服务暂时不可用"

3. POST /api/rules/{id}/versions

把 config 存为该规则新生效版本(version 自增,旧 active 归档),可回退。

Request body:

{ "config": { "knobs": { "...": "..." }, "prompt_template": "..." },
  "note": "把方向数调到 5、语气更商业" }

Response 201:

{ "success": true, "data": { "id": "R1", "version": 2, "status": "active", "updated_at": "ISO8601" },
  "error": null }

服务端事务(不变量):同一 (rule_id, tenant_id) 内:插入新行 version=旧max+1, status="active" 把旧 active 行置 archived,同一事务保证"只有一行 active"。

错误码表:

HTTPerror.code触发
400INVALID_CONFIGconfig 结构非法 / 缺必填旋钮
401UNAUTHORIZED无/过期 token
403FORBIDDEN角色无"改规则"权限(多租户只读角色)
404RULE_NOT_FOUND{id} 不是 R1–R6
409VERSION_CONFLICT并发写同版本(唯一约束冲突),前端重取后重试

4. (可选)GET /api/rules/{id}/versions

版本历史,供回退。

Response 200:

{ "success": true, "data": [
    { "version": 2, "status": "active", "note": "...", "updated_by": "anderson", "updated_at": "..." },
    { "version": 1, "status": "archived", "note": "seed", "updated_by": "system-seed", "updated_at": "..." }
  ], "error": null }

回退:前端选历史版本 → 调 POST /versions 用该 config 存为新 active 版本(不"原地复活",保持 append-only 血缘可追溯)。


降级

前端 test-bench.js 在无后端 / 401 时优雅降级为"只读提示",不白屏。

本次引用的 skills:api-designcost-aware-llm-pipelinemcp-server-patterns(JSON-RPC 契约参考)
由 v5 docs-to-html 离线生成 · 2026-07-14 23:03