AI Prompt 工程化设计最佳实践
核心理念把 Prompt 当作代码大多数开发者与 LLM 交互时是把 prompt 当作自然语言对话来写的❌ “帮我生成一张图片内容是一个苹果风格可爱”工程化的做法是把 prompt 当作代码来管理——有输入、有处理逻辑、有输出格式、有错误处理✅ 输入 → 分类 → 路由到对应模板 → 结构化参数注入 → 组装 → 最终 prompt核心心态转变你不是在和 AI “聊天”你是在编程驱动 AI。这引出了第一条也是最根本的原则。原则一Plan-and-Prompt 分离问题当 LLM 直接生成最终 prompt 时你无法控制哪些内容应该出现、哪些不应该约束是否被遵守输出格式是否一致模式┌──────────────────────────────────────────────────┐│ 你的代码 ││ ││ 输入 ──→ Plan阶段(LLM) ──→ 结构化结果 ││ │ ││ ▼ ││ Build阶段(纯代码) ││ │ ││ ▼ ││ 最终 Prompt ││ │ ││ ▼ ││ 执行阶段(LLM / 图像模型 / …) ││ │└──────────────────────────────────────────────────┘Plan 阶段LLM将高层次需求转为结构化 JSON。LLM 只做语义转换。Build 阶段纯代码将结构化规格确定性地组装成最终 prompt。不经过 LLM。Execute 阶段目标模型将最终 prompt 发送给图像/文本/代码模型。为什么这样做维度 LLM 直接生成 prompt Plan-and-Prompt 分离可控性 低——LLM 可能忽略约束 高——代码控制最终 prompt 的每个词可调试性 低——不知道是理解错了还是表达错了 高——可以分别检查 Plan 和 Final Prompt约束遵守 不可靠——LLM 经常忘记负向约束 代码保证约束一定出现在最终输出中一致性 低——每次输出格式可能不同 代码保证输出格式始终一致可测试性 难——只能端到端测试 易——Build 阶段可单独单元测试代码示例❌ 反模式让 LLM 直接生成最终 promptresponse llm.chat(fGenerate an image prompt for: {user_input})final_prompt response.text # 不可控✅ Plan-and-Prompt 分离plan llm.chat_json(system“Extract visual semantics from the input as JSON.”,useruser_input)plan {“mainSubject”: “a red apple”, “style”: “flat illustration”, …}final_prompt build_prompt(plan) # 纯代码确定性“A simple flat illustration of a red apple. Clean lines, centered…”适用场景任何需要精确控制最终 prompt 的场景文生图、文生视频、代码生成、文档生成需要遵守敏感词、合规、品牌安全规则的场景需要支持 A/B 测试 prompt 变体的场景Agent / Tool-calling 的中间步骤规划3. 原则二多阶段流水线问题单一 LLM 调用试图完成理解输入 规划内容 格式化输出三件事每一步的失败都会污染下一步。模式将复杂任务拆分成独立、可替换、可单独测试的阶段Stage 1 Stage 2 Stage 3 Stage 4┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│ Parse │───▶│ Classify │───▶│ Plan │───▶│ Build ││ (纯代码) │ │ (纯代码) │ │ (LLM) │ │ (纯代码) │└──────────┘ └──────────┘ └──────────┘ └──────────┘输入 类型 规格 最终格式每个阶段的职责阶段 执行者 职责 输入 → 输出Parse 纯代码 提取结构化信息 原始字符串 → 结构化对象Classify 纯代码 判断类型/意图 结构化对象 → 分类标签Plan LLM 语义转换、内容规划 结构化对象 分类 → JSON 规格Build 纯代码 Prompt 组装 JSON 规格 → 最终 prompt判断是否需要拆分的经验法则问题 答案这个判断能用 if/else 做吗 → 不要用 LLM用代码这个转换需要理解语义吗 → 用 LLM这个格式化有固定格式要求吗 → 用代码不要依赖 LLM 记忆格式这个约束绝对不能违反吗 → 用代码硬编码不交给 LLM代码示例❌ 反模式一个 prompt 做所有事response llm.chat(f“”Analyze this input: {user_input}Classify its typePlan the visual contentOutput a final image prompt“”)✅ 多阶段流水线parsed parse_input(user_input) # Stage 1: 纯代码解析category classify(parsed) # Stage 2: 纯代码分类plan llm_plan(parsed, category) # Stage 3: LLM 语义规划prompt build_prompt(plan, category) # Stage 4: 纯代码组装4. 原则三Schema 即约束核心洞察LLM 输出的 JSON Schema 不只是数据格式——它是对 LLM 行为的最强约束。Schema 里有什么字段LLM 就会去思考什么Schema 里没有的字段LLM 就不会考虑。你可以通过选择性地暴露或隐藏字段来精确控制 LLM 的注意力范围。实践按场景使用不同 Schema❌ 反模式万能 Schema——包含所有场景的字段universal_schema {“overlayText”: “…”, # 字母卡才需要“allowVisibleText”: True, # 大部分场景不需要“sceneDescription”: “…”,“characterCount”: 0, # 只有人物场景需要# … 10 个字段}→ LLM 会为所有字段分配注意力包括不相关的✅ 按场景使用精简 Schemaif category “letter_card”:schema {“letter”: “…”,“illustration”: “…”,“allowText”: True}elif category “scene_card”:schema {“action”: “…”,“setting”: “…”,“mood”: “…”}# 没有 allowText 字段 → LLM 根本不会考虑要不要写字为什么字段的存在与否比值为 false更有效如果 schema 中有 allowText: falseLLM 仍然看到了 allowText 这个字段名已经被提示了文字这个概念如果 schema 中根本没有 allowText 字段LLM 的注意力完全不会被引导到文字方向同样的逻辑适用于任何你不希望 LLM 考虑的维度政治、暴力、品牌、价格……Schema 设计检查清单每个字段都是当前任务必需的吗不需要的就删掉字段名是否在引导正确的思维方向避免 text, label, caption 等词当任务不需要文字时是否有冗余字段可以用一个字段替代字段顺序是否反映了优先级LLM 通常更关注前面的字段5. 原则四Prompt 模块化组装问题把整个 prompt 写成一个长字符串或一个模板难以增删某个约束根据条件切换某一段单独调试某一部分做 A/B 测试模式Section 化将 prompt 分解为语义独立的 Section每个 Section 是 List 中的一个元素sections [# Section 1: 全局格式硬约束前置——最重要“The output must be a valid JSON array.”,# Section 2: 风格指令正向引导 Use professional, concise language., # Section 3: 条件性指令仅在需要时加入 *([Include citations for each claim.] if require_citations else []), # Section 4: 负向约束放在后面——安全网角色 Do not include personal opinions. Do not speculate.,]组装过滤空字符串用换行符连接final_prompt “\n”.join(s for s in sections if s)Section 的组织原则位置 内容类型 原因最前前25% 全局格式要求、关键硬约束 LLM 对 prompt 前半部分关注度更高中间 任务描述、正向引导 告诉 LLM “要做什么”条件 根据上下文动态添加的指令 按业务逻辑判断是否加入靠后 负向约束、禁止列表 防止稀释主体指令充当安全网最后 输入数据 / 用户内容 避免被误解释为指令的一部分为什么不用模板引擎模板引擎Jinja / Handlebars适合文本填空但不适合 prompt 工程条件逻辑写在模板里会变得难以阅读{% if %} 嵌套模板不容易做 Section 级别的 A/B 测试模板的空白符控制经常出问题推荐做法在代码中构建 list[str]最后 join。每个 Section 是一行代码清晰、可调试、可单测。代码示例条件性 Sectiondef build_system_prompt(user_role: str, task_type: str, include_examples: bool) - str:sections [“You are a helpful assistant. Be concise and accurate.”]# 按角色定制 if user_role expert: sections.append(Use technical terminology appropriate for domain experts.) elif user_role beginner: sections.append(Explain concepts in simple terms. Avoid jargon.) # 按任务类型定制 if task_type creative: sections.append(Be imaginative and explore multiple angles. Up to 500 words.) elif task_type analytical: sections.append(Be rigorous and evidence-based. Cite sources. Up to 300 words.) # 可选示例 if include_examples: sections.append(Include 1-2 concrete examples in your response.) return \n\n.join(sections)