限时福利领取


背景痛点:Prompt 失效的 3 个“名场面”

第一次把 GitHub Copilot 接入我们组的老代码库时,我兴冲冲地敲下一行注释:

// 根据订单号生成财务凭证

结果 AI 返回了一段 Python 2.7 代码,还顺手 import 了它自己臆造出来的 finance 模块。那一刻我意识到:自由 Prompt 就像让实习生“随便发挥”——越自由,越脱轨

后来我把常见“翻车”场景总结成三类:

  1. 业务上下文丢失
    模型只看到当前文件,不知道公司有一套“订单→凭证”的映射表,结果把字段全猜错。
  2. 类型安全真空
    生成的代码里 amount 一会儿是 string,一会儿是 number,编译器直接红屏。
  3. 边界条件被忽略
    提示词没强调“并发场景”,AI 愉快地省略了分布式锁,上线后订单重复推送,财务小姐姐连夜加班。

痛点现场

技术对比:自由式 vs 结构化

维度 自由式 Prompt 结构化 Prompt(JSON Schema)
开发速度 一句话就能跑 前期写 Schema 慢 10 分钟
可维护性 靠“口头约定” Schema 即文档,新人秒懂
准确率 60%~70%,靠运气 90%+,字段对不上直接拒掉
版本回滚 靠 git diff 猜 Schema 版本号一翻就回到旧逻辑

结论:业务代码越核心,越要给 AI 戴“手铐”。JSON Schema 就是那只手铐,让模型在“可验证的笼子”里跳舞。

核心实现:三步把 Prompt 做成“产品级”

1. System Prompt:给 AI 发“工牌”

/**
 * 系统角色模板,定义 AI 的边界与行为
 * @constant
 */
export const SYSTEM_PROMPT = `
You are CodexFinDev, a senior TypeScript developer.
- 严格遵循现有类型定义,禁止新增未声明字段
- 拒绝访问私有 API,除非用户显式提供白名单
- 输出前自检:必须 pass tsc --noEmit
`;

把这段系统提示放在每次请求的最前面,相当于给 AI 戴上“紧箍咒”,后续所有用户提示都跑不出这个框架。

2. Function Calling:让 AI 按“签名”写代码

先定义接口,再让模型填空——先画靶子,再射箭

/**
 * 创建财务凭证函数签名
 * 时间复杂度:O(1),仅内存操作
 */
interface CreateVoucherParams {
  /** 订单号,全局唯一 */
  orderNo: string;
  /** 订单金额,单位分 */
  amountInCents: number;
  /** 货币代码,ISO-4217 */
  currency: 'CNY' | 'USD';
}

/**
 * 返回结构
 */
interface Voucher {
  voucherId: string;
  createdAt: string; // ISO-8601
}

Prompt 模板如下(可直接塞进 messages 数组):

{
  "role": "user",
  "content": "根据以下 Schema 生成 TypeScript 实现,要求:1. 使用上述接口 2. 捕获异常并返回 `Result<Voucher,Err>` 3. 打印审计日志",
  "function_call": {
    "name": "create_voucher",
    "parameters": {...}
  }
}

模型返回的代码天然带类型,tsc 一次过,再也不用全局替换 any

3. 错误重试:指数退避保平安

OpenAI 偶尔会 429,咱得优雅重试,别把 CI 跑挂。

/**
 * 带指数退避的重试封装
 * @param fn 需要执行的异步函数
 * @param maxAttempts 最大重试次数,默认 5
 * @returns Promise<T>
 * 时间复杂度:O(attempts) ≈ O(1)
 */
async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  maxAttempts = 5
): Promise<T> {
  for (let i = 1; i <= maxAttempts; i++) {
    try {
      return await fn();
    } catch (err: any) {
      if (err.status === 429 && i < maxAttempts) {
        const wait = 2 ** i * 100; // 200ms, 400ms...
        await new Promise(r => setTimeout(r, wait));
        continue;
      }
      throw err;
    }
  }
  throw new Error('Max retries exceeded');
}

把真正发请求的函数包一层,CI 失败率从 8% 降到 0.3%

重试效果

生产建议:让 Prompt 像代码一样被“治理”

1. 版本控制:Prompt 也要发版

  • 把 Prompt 文本抽成独立 .yaml 文件,命名规则 v{major}.{minor}.{patch}
  • 在 README 里写明兼容策略:major 升级必须回归测试;minor 可加字段,但不能删。
  • 发版打 tag,回滚只需 git checkout v1.2.0 -- prompts/

2. 敏感信息过滤:正则一把梭

/** 过滤密钥、手机号等敏感字段 */
const SENSITIVE_REGEX = [
  { pattern: /['"`]sk-[a-zA-Z0-9]{48}['"`]/g, desc: 'OpenAI Secret Key' },
  { pattern: /1[3-9]\d{9}/g, desc: 'Chinese Mobile' }
];

export function sanitizePrompt(raw: string): string {
  let cleaned = raw;
  SENSITIVE_REGEX.forEach(r => {
    cleaned = cleaned.replace(r.pattern, `[${r.desc}]`);
  });
  return cleaned;
}

日志和监控里先过一遍,再也不怕密钥被 AI 当素材“背”出去

3. 性能监控:盯紧 TP99

  • 在网关层记录 prompt_tokens, completion_tokens, total_latency
  • 告警规则:TP99 > 3s 或 4xx 比例 > 2% 就降级。
  • 降级预案:切到本地缓存的“静态 Prompt + 规则引擎”,保证核心链路不断。

完整工作流回顾

  1. 业务需求 → 2. 写 JSON Schema → 3. 生成结构化 Prompt → 4. 发请求 → 5. 回写单测 → 6. 版本打 tag → 7. 监控告警。

跑顺后,AI 生成代码的单元测试通过率从 58% 提到 93%,Code Review 时间减半。


开放问题

当生成代码需要访问私有 API 时,如何在安全性和便利性间取得平衡?
是继续把白名单硬编码到 System Prompt,还是让 AI 先返回“调用计划”由人工二次确认?
欢迎留言聊聊你的做法。

限时福利领取


Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐