由 Claude 从英文原文翻译。

这篇文章最初由我发布于 Medium 的 OpusClip Engineering
原文:Bridging the Gap: How Engineers and PMs Ship Winning LLM Features Together

写给 PM 的 LLM 工程知识点

TL;DR:

  • Prompt 放在配置里,不要写死在代码里。
  • 动态变量尽量后置,最大化 KV cache 命中。
  • Schema 管格式,Prompt 管语义。

我们在 OpusClip 落地这三件事后,prompt 迭代从按天变成按分钟,API 成本明显下降,线上稳定性也更好。下面按实践展开。

1) PM 到 Prompt 的距离(PM-to-Prompt Distance)

定义: 从产品诉求到模型最终收到的 prompt,中间经历了多少层转译。

Tao Zhang first defines this metric in Manus.

一个常见链路是:

  1. PM 写诉求:助手要“专业,但别生硬”。
  2. PRD 转译:正式语气里允许少量口语化表达。
  3. 工程实现:写成系统 prompt。
  4. 运行时再拼接:用户信息、历史上下文等动态内容。

链路越长,问题越容易出现:

  • 迭代慢:改一行文案,要跨多个环节。
  • 意图丢失:每一次转译都可能偏离原始需求。
  • 排查难:线上看到的最终 prompt 和代码里的模板往往不是一回事。

落地建议:

  • Prompt 放到动态配置(或 prompt 管理平台)。
  • 做版本化管理,支持灰度和 A/B。
  • 线上日志保留“最终拼装后的 prompt”用于回溯。

2) KV cache:把静态内容放前面

KV cache 是什么? 简单说,就是把已经算过的上下文结果缓存起来,后续请求可以复用,不用每次都从头计算。

对产品和业务最直接的影响有两点:

  • 延迟更低:响应更快。
  • 成本更低:重复计算更少,token 费用更低。

在主流模型计费里,cached token 通常明显便宜于新 token。以 GPT-5 为例,输入 token 与 cached token 的单价存在约 10x 差异。

实操规则只有一条: 静态指令前置,动态变量后置。

不推荐结构(缓存命中低):

User: {{user_name}}
Question: {{user_question}}
Conversation history: {{chat_history}}

You are a customer support agent for TechCorp.
Guidelines:
- Be empathetic and professional
- Check our knowledge base before answering
- Escalate billing issues to human agents
- Always verify account details first

推荐结构(缓存命中高):

You are a customer support agent for TechCorp.
Guidelines:
- Be empathetic and professional
- Check our knowledge base before answering
- Escalate billing issues to human agents
- Always verify account details first

User: {{user_name}}
Question: {{user_question}}
Conversation history: {{chat_history}}

在高频场景里,这个调整通常是“低风险、高收益”。例如每天 10,000 次客服对话,光这个改动就可能带来可见的日成本下降。

3) Structured Output:Schema 管格式,Prompt 管语义

定义: 不是让模型返回自由文本,而是让它按 schema 返回结构化 JSON。

关键点:

  • Structured output 是 API 能力,不是 prompt 小技巧。
  • schema 在代码里定义,运行时负责校验。
  • prompt 不再描述“长什么样”,只描述“每个字段该填什么”。

下面是一个示例:

import OpenAI from "openai"
import { z } from "zod"
import { zodTextFormat } from "openai/helpers/zod"

// 1) Schema 留在代码中(Zod)
const RelevancyItem = z.object({
  clipId: z.string(),
  relevant: z.boolean(),
  relevantReason: z.string(),
  advertisement: z.boolean(),
})
const RelevancyArray = z.array(RelevancyItem)
type Relevancy = z.infer<typeof RelevancyArray>

// 2) 构建纯语义 prompt(不写输出格式)
const prompt = promptTemplate.join("\n").replace(INPUT_REPLACE, input)

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })

// 3) Responses API 自动按 schema 解析和验证
const response = await openai.responses.parse({
  model: "gpt-4o-mini",
  input: [
    { role: "system", content: "Judge search-trend relevancy using the definitions provided." },
    { role: "user", content: prompt },
  ],
  text: {
    format: zodTextFormat(RelevancyArray, "relevancy_results"),
  },
})

// 4) 结果已完成解析和校验
const results: Relevancy = response.output_parsed

// 5) 后置过滤
const relatedResults = results.filter(
  (r) => r.relevant && r.evergreen && !r.advertisement
)

常见错误: 代码里已经有 schema,prompt 里又写一遍输出格式要求。

这样会带来三个后果:

  • 校验失败和重试增多,延迟和成本上升。
  • 模型在“内容”和“格式指令”之间来回拉扯,质量下降。
  • 下游 typed 解析更容易崩。

一句话:Schema 决定长相,Prompt 决定内容。

不推荐这样写 prompt:

Analyze these search results and return a JSON object with:
- videoId: the video identifier
- isRelevant: boolean indicating if it matches
- relevanceReason: explanation string
- isPaid: boolean for sponsored content
Format as valid JSON with these exact field names.

推荐这样写 prompt:

Analyze these search results for relevance to the user's query.

For relevance assessment:
- Consider semantic match, not just keyword overlap
- Educational content is preferred over entertainment
- Recent content (last 6 months) is more relevant

For paid content detection:
- Look for "Sponsored", "Ad", or "#ad" markers
- Check if the channel name includes "Official" or "Brand"

Provide clear reasoning for why content is or isn't relevant.

另外一个非常实际的坑:工程 schema 用 videoId,需求文案写成 video_idclip_id,不对齐就会直接影响稳定性。

把三件事串起来

  1. 缩短 PM 到 Prompt 的距离:让迭代更快。
  2. 优化 KV cache 命中:让每次迭代更便宜。
  3. 用好结构化输出:让结果稳定可解析。

下一步

阅读完这篇文章的你可以做什么?

  1. 盘点现状:prompt 在哪、变量是否后置、是否混写了格式指令。
  2. 先改一个最高收益点:通常从 prompt 外置或变量后置开始。
  3. 建立指标:追踪迭代周期、单次请求成本、解析失败率。

如果你有任何的疑问,欢迎评论。