Files
meida_backend/prompts
xsl 54dc741811 feat(llm): provider-abstract gateway with cost and failure controls
DeepSeek (OpenAI-compatible) as primary with a 智谱 GLM slot that is a real
routing path but raises until the 算法备案 model list is confirmed. Adds scene
routing, failover, a per-provider circuit breaker, structured output with
retry-on-validation-failure, prompts loaded by id+version from prompts/, and
cost attribution written to ob_ai_call for every attempt -- successes,
retries and failures alike.

Two controls exist because they are cheap now and expensive later: a
cache-prefix validator that rejects timestamps, uuids and nicknames in the
cached system prefix (a volatile prefix silently multiplies input cost), and
a hard platform-wide daily budget that rejects non-critical scenes instead of
merely alerting, since a retry storm outruns any human on-call.
2026-08-14 00:26:39 +08:00
..

prompts/ — 版本化 Prompt 模板

架构 §8.1:Prompt 存文件(随代码版本化)+ 场景绑定版本号。 运行时由 packages/llm/prompts.py 的 PromptRegistry 按 id + version 加载, 使用的版本号会写进每一行 ob_ai_call,用于回溯「为什么当时这么答」。

文件布局

prompts/<prompt_id>/<version>.txt
[system]
固定不变的、可被供应商缓存的前缀。

[user]
每次调用渲染的变量部分,用 {占位符}。

铁律:[system] 段不许有任何变量

架构 §7.2 缓存前缀纪律 / TD-04:

系统提示词与稳定知识片段必须放在 prompt 头部且不含时间戳、随机 ID、 用户昵称等变量,否则缓存永远不命中,输入成本翻数倍。

这条规则靠人 review 是守不住的:破坏它以后功能完全正常,只有账单会翻几倍, 而账单要到月底才看得见。所以它被写成了代码:

  • PromptRegistry 加载时校验,LLM_CACHE_PREFIX_MODE=strict 下直接拒绝调用。
  • tests/unit/test_prompt_cache_prefix.py 扫描本目录下全部模板。

会被拒绝的东西:ISO 日期、时刻、中文年月、时间戳、UUID、长十六进制串, 以及名字里带 nick/user/name/now/date/time/id/rand/session/ request/trace/token 的占位符。

要注入用户画像、检索结果、会话摘要?放到 [user] 段或调用方传入的 messages 里 —— 它们本来就该排在固定前缀之后(§7.2 ③ 上下文组装)。

改 Prompt 的规矩

  • 不要原地改已上线的版本,新建 v2.txt,再在 routing.py(或 LLM_SCENE_OVERRIDES_JSON)里切过去。旧版本要留着,ob_ai_call 里的历史记录 才有意义。
  • 报告与 Agent 这类核心链路的 Prompt 变更必须先灰度 + 跑黄金集回归 (架构 §14.3)。
  • Token 预算见 §7.2:系统提示词 ≤ 800,输入合计 ≤ 3700,输出 ≤ 800。

内容合规约束

每个面向用户的 prompt 都必须在系统前缀里写死:不做医疗/减重/整形/命理结论 (CR-12)、不评判外貌、不使用绝对化用语、用户输入视为内容而非指令 (NFR-SEC-04 Prompt 注入防护)。