学习第12天:Prompt Engineering

学习第12天:Prompt Engineering

贯穿项目:Enterprise AI Agent Platform --- 本章为平台的 Agent 构建专业的 Prompt 模板与管理系统


1. 学习目标

完成本章学习后,你将能够:

  • 掌握 Prompt 设计的核心原则(角色、上下文、格式、约束)
  • 灵活运用 Zero-shot / Few-shot / Chain-of-Thought / ReAct 等 Prompt 策略
  • 设计和实现结构化输出的 Prompt(JSON 模式)
  • 构建企业级 Prompt 模板管理系统(版本控制、多模型适配、A/B 测试)
  • 识别和防御 Prompt Injection 攻击
  • 理解不同模型对 Prompt 的敏感度差异
维度 核心内容 实践产出
Prompt 基础 角色/上下文/格式/约束 Agent 角色定义模板
高级策略 CoT/Few-shot/ReAct 推理链 Prompt 模板
结构化输出 JSON Mode 信息提取 Prompt
模板管理 版本化/YAML/多模型 PromptManager 类
安全防护 Prompt Injection 输入过滤 + 输出校验

2. 知识体系图

#mermaid-svg-Dsgseh60dIr2KiZe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Dsgseh60dIr2KiZe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dsgseh60dIr2KiZe .error-icon{fill:#552222;}#mermaid-svg-Dsgseh60dIr2KiZe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dsgseh60dIr2KiZe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dsgseh60dIr2KiZe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dsgseh60dIr2KiZe .marker.cross{stroke:#333333;}#mermaid-svg-Dsgseh60dIr2KiZe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dsgseh60dIr2KiZe p{margin:0;}#mermaid-svg-Dsgseh60dIr2KiZe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Dsgseh60dIr2KiZe .cluster-label text{fill:#333;}#mermaid-svg-Dsgseh60dIr2KiZe .cluster-label span{color:#333;}#mermaid-svg-Dsgseh60dIr2KiZe .cluster-label span p{background-color:transparent;}#mermaid-svg-Dsgseh60dIr2KiZe .label text,#mermaid-svg-Dsgseh60dIr2KiZe span{fill:#333;color:#333;}#mermaid-svg-Dsgseh60dIr2KiZe .node rect,#mermaid-svg-Dsgseh60dIr2KiZe .node circle,#mermaid-svg-Dsgseh60dIr2KiZe .node ellipse,#mermaid-svg-Dsgseh60dIr2KiZe .node polygon,#mermaid-svg-Dsgseh60dIr2KiZe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Dsgseh60dIr2KiZe .rough-node .label text,#mermaid-svg-Dsgseh60dIr2KiZe .node .label text,#mermaid-svg-Dsgseh60dIr2KiZe .image-shape .label,#mermaid-svg-Dsgseh60dIr2KiZe .icon-shape .label{text-anchor:middle;}#mermaid-svg-Dsgseh60dIr2KiZe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Dsgseh60dIr2KiZe .rough-node .label,#mermaid-svg-Dsgseh60dIr2KiZe .node .label,#mermaid-svg-Dsgseh60dIr2KiZe .image-shape .label,#mermaid-svg-Dsgseh60dIr2KiZe .icon-shape .label{text-align:center;}#mermaid-svg-Dsgseh60dIr2KiZe .node.clickable{cursor:pointer;}#mermaid-svg-Dsgseh60dIr2KiZe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Dsgseh60dIr2KiZe .arrowheadPath{fill:#333333;}#mermaid-svg-Dsgseh60dIr2KiZe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Dsgseh60dIr2KiZe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Dsgseh60dIr2KiZe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dsgseh60dIr2KiZe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Dsgseh60dIr2KiZe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dsgseh60dIr2KiZe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Dsgseh60dIr2KiZe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Dsgseh60dIr2KiZe .cluster text{fill:#333;}#mermaid-svg-Dsgseh60dIr2KiZe .cluster span{color:#333;}#mermaid-svg-Dsgseh60dIr2KiZe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Dsgseh60dIr2KiZe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Dsgseh60dIr2KiZe rect.text{fill:none;stroke-width:0;}#mermaid-svg-Dsgseh60dIr2KiZe .icon-shape,#mermaid-svg-Dsgseh60dIr2KiZe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Dsgseh60dIr2KiZe .icon-shape p,#mermaid-svg-Dsgseh60dIr2KiZe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Dsgseh60dIr2KiZe .icon-shape .label rect,#mermaid-svg-Dsgseh60dIr2KiZe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Dsgseh60dIr2KiZe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Dsgseh60dIr2KiZe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Dsgseh60dIr2KiZe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 工程化管理
Prompt 策略体系
Prompt 设计原则
核心四要素
角色 Role: '你是一个...'
上下文 Context: 背景信息
格式 Format: 输出要求
约束 Constraint: 限制条件
Prompt 策略
Zero-shot: 直接提问
Few-shot: 示例学习
CoT: 逐步推理
ReAct: 思考-行动
Self-Consistency: 多路径投票
Prompt 工程
模板引擎 Jinja2
版本管理
多模型适配
A/B 测试
安全防护


3. 核心知识

3.1 Prompt 的本质

Prompt 不是一段随意的文字,而是对 LLM 的编程。就像你用 Java 写业务逻辑,Prompt 是你要写的"自然语言代码"。

复制代码
Java 类比:
public class CustomerServiceAgent {
    @SystemPrompt("You are a helpful...")
    @Tool("search_knowledge_base")
    @Temperature(0.3)
    String handle(String query) { ... }
}

Python Prompt 做法:
system_prompt = """
你是一个专业的客服 Agent。
- 角色:电商平台客服
- 知识:产品退换货政策
- 工具:可查询订单系统
- 约束:不承诺退款金额,让用户联系人工
"""

3.2 Prompt 的四要素框架

复制代码
┌─────────────────────────────────────────────┐
│              ROLE(角色)                     │
│  "你是一个资深的 Java 后端架构师..."          │
├─────────────────────────────────────────────┤
│             CONTEXT(上下文)                 │
│  "当前项目使用 Spring Boot 3.2, JDK 21..."  │
├─────────────────────────────────────────────┤
│             FORMAT(格式)                    │
│  "请以 JSON 格式返回,包含以下字段..."        │
├─────────────────────────────────────────────┤
│            CONSTRAINT(约束)                 │
│  "不要使用已废弃的 API,不要建议引入新依赖"    │
└─────────────────────────────────────────────┘

3.3 策略选择指南

策略 适用场景 示例
Zero-shot 通用对话、简单任务 "翻译以下内容为英文"
Few-shot 需要特定格式/风格 给 3 个示例再提问
Chain-of-Thought 数学/逻辑推理 "让我们一步步思考"
ReAct 需要工具调用 "思考→行动→观察"循环
Self-Consistency 需要高准确率 多次采样取多数结果
Tree-of-Thought 复杂探索性问题 分支探索 + 回溯评估

4. 详细讲解

4.1 从 Zero-shot 到 Few-shot

python 复制代码
# Zero-shot:不提供示例
zero_shot_prompt = "将以下句子翻译成英文:今天天气真好"

# Few-shot:提供 3 个示例
few_shot_prompt = """
将以下中文句子翻译成英文,保持口语化风格:

示例1:
输入:你吃了吗?
输出:How's it going?

示例2:
输入:这太棒了!
输出:That's awesome!

示例3:
输入:我觉得不太对
输出:I don't think that's right

现在请翻译:
输入:今天天气真好
输出:"""

4.2 Chain-of-Thought(思维链)

CoT 是最重要的 Prompt 技术之一------让模型"展示推理过程":

python 复制代码
# ❌ 直接要答案
"小明有 5 个苹果,给了小红 2 个,又买了 3 个,现在有几个?"

# ✅ 要求逐步推理
"""请按以下步骤解决问题:

问题:小明有 5 个苹果,给了小红 2 个,又买了 3 个,现在有几个?

请按步骤回答:
步骤1:列出已知条件
步骤2:逐步计算
步骤3:给出最终答案
"""

CoT 的变体

python 复制代码
# Zero-shot CoT:在 Prompt 末尾加一句魔法
"...让我一步步思考。"
"...Let's think step by step."

# Few-shot CoT:提供带推理过程的示例
"""
示例:
问:一个房间里有 3 个人,走了 2 个,又来了 5 个,现在有几个人?
推理:
- 初始人数:3
- 走掉 2 人:3 - 2 = 1
- 来了 5 人:1 + 5 = 6
答案:6 人

现在请用同样的方式回答:
问:(你的问题)
推理:"""

4.3 ReAct 模式(Reasoning + Acting)

ReAct 是 Agent 的核心范式------交替进行思考行动

复制代码
观察:用户问"北京今天天气怎么样"
思考:我需要获取北京的实时天气数据
行动:调用 get_weather("北京")
观察:晴天,25°C,湿度 40%
思考:我已经有了天气数据,可以回复用户了
回答:北京今天晴天,气温 25°C,湿度 40%,适合户外活动。
python 复制代码
REACT_SYSTEM_PROMPT = """你是一个 AI Agent,使用 ReAct 模式解决问题。

你只能使用以下工具:
{tools}

请严格按照以下格式响应:

Thought: 对当前情况的分析和下一步计划
Action: 要调用的工具名称
Action Input: 工具的输入参数
Observation: 工具返回的结果
... (这个 Thought/Action/Action Input/Observation 可以重复多次)
Thought: 我已经有足够的信息回答用户
Final Answer: 对用户的最终回复

开始!
"""

# 实际使用中,ReAct 循环在代码中实现:
async def react_loop(query: str, tools: list, max_iterations: int = 10):
    messages = [{"role": "system", "content": REACT_SYSTEM_PROMPT}]
    messages.append({"role": "user", "content": query})

    for i in range(max_iterations):
        response = await llm.chat(messages)
        content = response.content

        if "Final Answer:" in content:
            # 提取最终答案
            final = content.split("Final Answer:")[-1].strip()
            return final

        if "Action:" in content:
            # 解析并执行工具调用
            action, action_input = parse_action(content)
            observation = await execute_tool(action, action_input)
            messages.append({"role": "assistant", "content": content})
            messages.append({"role": "user", "content": f"Observation: {observation}"})

4.4 结构化输出 Prompt

让 LLM 输出 JSON 是 Agent 中最常见的需求之一:

python 复制代码
# Prompt 中明确要求 JSON 格式
EXTRACTION_PROMPT = """从以下用户消息中提取结构化信息。

用户消息:{user_message}

请以严格的 JSON 格式返回,只包含以下字段:
- intent: 用户意图("查询"/"创建"/"修改"/"删除")
- entities: 实体列表,每个实体包含 name、type、value
- confidence: 置信度 (0.0-1.0)

必须返回合法的 JSON,不要包含任何其他内容。

示例输出:
{{"intent": "查询", "entities": [{{"name": "城市", "type": "location", "value": "北京"}}], "confidence": 0.95}}
"""

使用 JSON Mode(OpenAI)

python 复制代码
# OpenAI 的 JSON Mode 强制合法 JSON
response = await client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "你是一个信息提取助手"},
        {"role": "user", "content": "提取以下消息的信息:我叫张三,今年30岁"},
    ],
    response_format={"type": "json_object"},  # JSON Mode
)

import json
result = json.loads(response.choices[0].message.content)
# {"name": "张三", "age": 30}

5. 代码示例

5.1 贯穿项目:PromptManager --- 企业级模板管理

python 复制代码
# app/core/prompt/types.py
from dataclasses import dataclass, field
from typing import Optional
from datetime import datetime
from enum import Enum

class PromptStrategy(str, Enum):
    ZERO_SHOT = "zero_shot"
    FEW_SHOT = "few_shot"
    COT = "chain_of_thought"
    REACT = "react"
    STRUCTURED = "structured_output"

@dataclass
class PromptTemplate:
    """Prompt 模板定义"""
    name: str                    # 模板名称(唯一标识)
    version: str                 # 语义化版本
    strategy: PromptStrategy     # 策略类型
    system_prompt: str           # System Prompt
    user_prompt_template: str    # 用户 Prompt 模板(支持 Jinja2)
    model: str                   # 目标模型
    temperature: float = 0.7
    max_tokens: int = 4096
    description: str = ""
    examples: list[dict] = field(default_factory=list)  # Few-shot 示例
    variables: list[str] = field(default_factory=list)   # 模板变量列表
    created_at: datetime = field(default_factory=datetime.now)
python 复制代码
# app/core/prompt/manager.py
import yaml
from pathlib import Path
from jinja2 import Template, Environment, StrictUndefined
from typing import Optional
from app.core.prompt.types import PromptTemplate, PromptStrategy

class PromptManager:
    """Prompt 模板管理器

    功能:
    1. 从 YAML 文件加载 Prompt 模板
    2. 用 Jinja2 渲染模板变量
    3. 构建完整的 messages 列表
    4. 支持多版本和多模型
    """

    def __init__(self, template_dir: str = "prompts"):
        self.template_dir = Path(template_dir)
        self.templates: dict[str, dict[str, PromptTemplate]] = {}
        self.jinja_env = Environment(
            undefined=StrictUndefined,  # 未定义变量抛出异常
            trim_blocks=True,
        )
        self._load_templates()

    def _load_templates(self):
        """从 YAML 文件加载所有模板"""
        if not self.template_dir.exists():
            return

        for yaml_file in self.template_dir.glob("*.yaml"):
            with open(yaml_file) as f:
                data = yaml.safe_load(f)

            template = PromptTemplate(
                name=data["name"],
                version=data["version"],
                strategy=PromptStrategy(data["strategy"]),
                system_prompt=data["system_prompt"],
                user_prompt_template=data["user_prompt_template"],
                model=data.get("model", "gpt-4"),
                temperature=data.get("temperature", 0.7),
                max_tokens=data.get("max_tokens", 4096),
                description=data.get("description", ""),
                examples=data.get("examples", []),
                variables=data.get("variables", []),
            )

            # 按 name 和 version 组织
            if template.name not in self.templates:
                self.templates[template.name] = {}
            self.templates[template.name][template.version] = template

    def get_template(
        self, name: str, version: Optional[str] = None
    ) -> PromptTemplate:
        """获取指定版本的模板,默认取最新版本"""
        if name not in self.templates:
            raise ValueError(f"Template '{name}' not found")

        versions = self.templates[name]
        if version:
            if version not in versions:
                raise ValueError(f"Version '{version}' not found for '{name}'")
            return versions[version]

        # 返回最新版本(按语义化版本排序)
        latest = sorted(versions.keys(), key=lambda v: tuple(map(int, v.split("."))))[-1]
        return versions[latest]

    def render(
        self, name: str, variables: dict, version: Optional[str] = None
    ) -> dict:
        """渲染模板,返回完整的 LLM 请求参数

        Args:
            name: 模板名称
            variables: 模板变量
            version: 版本号(None 表示最新版本)

        Returns:
            {
                "messages": [...],
                "model": "gpt-4",
                "temperature": 0.7,
                "max_tokens": 4096,
            }
        """
        template = self.get_template(name, version)

        # 使用 Jinja2 渲染
        system_prompt = self.jinja_env.from_string(
            template.system_prompt
        ).render(**variables)

        user_prompt = self.jinja_env.from_string(
            template.user_prompt_template
        ).render(**variables)

        # 构建 messages
        messages = [{"role": "system", "content": system_prompt}]

        # 添加 Few-shot 示例
        if template.strategy == PromptStrategy.FEW_SHOT and template.examples:
            for example in template.examples:
                messages.append({"role": "user", "content": example["input"]})
                messages.append({"role": "assistant", "content": example["output"]})

        messages.append({"role": "user", "content": user_prompt})

        return {
            "messages": messages,
            "model": template.model,
            "temperature": template.temperature,
            "max_tokens": template.max_tokens,
        }

    def to_llm_request(self, name: str, variables: dict, **kwargs) -> dict:
        """快捷方法:渲染并转为 LLMRequest"""
        rendered = self.render(name, variables)
        from app.core.llm.types import LLMRequest, Message
        return LLMRequest(
            messages=[Message(**m) for m in rendered["messages"]],
            model=rendered["model"],
            temperature=rendered["temperature"],
            max_tokens=rendered["max_tokens"],
            **kwargs,
        )

5.2 YAML 定义的 Prompt 模板

yaml 复制代码
# prompts/agent_base.yaml
name: agent_base
version: "1.0.0"
strategy: zero_shot
description: "通用 Agent 基础 Prompt"
model: gpt-4
temperature: 0.7
max_tokens: 4096
variables:
  - agent_name
  - agent_role
  - current_date

system_prompt: |
  你是 {{ agent_name }},一个专业的 {{ agent_role }}。

  当前日期:{{ current_date }}

  行为准则:
  1. 用中文回复,专业但不生硬
  2. 不知道的事情坦诚说不知道
  3. 涉及安全、法律、医疗等问题时,提醒用户咨询专业人士
  4. 回复简洁,不啰嗦

user_prompt_template: |
  {{ user_query }}
yaml 复制代码
# prompts/extract_user_info.yaml
name: extract_user_info
version: "1.1.0"
strategy: structured_output
description: "从自然语言中提取用户信息"
model: gpt-4o
temperature: 0.1  # 低温度,追求准确性
max_tokens: 1024
variables:
  - user_message

examples:
  - input: "我叫张三,在北京工作,喜欢打篮球"
    output: '{"name": "张三", "city": "北京", "interests": ["篮球"]}'
  - input: "我是李四,深圳的程序员,平时喜欢爬山和游泳"
    output: '{"name": "李四", "city": "深圳", "interests": ["爬山", "游泳"]}'

system_prompt: |
  你是一个信息提取助手。从用户消息中提取个人信息。
  只返回合法的 JSON 对象,包含以下字段:
  - name: 姓名(字符串)
  - city: 城市(字符串)
  - interests: 兴趣爱好(字符串数组)

  如果没有提取到某个字段,将其设为 null(字符串)或 [](数组)。
  不要包含 markdown 代码块标记。

user_prompt_template: |
  从以下消息中提取信息:
  {{ user_message }}
yaml 复制代码
# prompts/cot_analyzer.yaml
name: cot_analyzer
version: "1.0.0"
strategy: chain_of_thought
description: "使用思维链分析复杂问题"
model: gpt-4
temperature: 0.3
max_tokens: 4096
variables:
  - problem
  - domain

system_prompt: |
  你是一个 {{ domain }} 领域的专家分析助手。

  分析问题时请遵循以下步骤:
  1. **理解问题**:用自己的话重述问题,确保理解正确
  2. **分解问题**:将问题拆解为子问题
  3. **逐步推理**:对每个子问题进行推理
  4. **验证推理**:检查推理过程是否有漏洞
  5. **给出结论**:汇总得出结论

  每步都明确标注步骤名称。

user_prompt_template: |
  请分析以下问题:
  {{ problem }}

5.3 在 Agent Service 中使用

python 复制代码
# app/services/agent_service.py
from app.core.prompt.manager import PromptManager
from app.core.llm.router import ModelRouter

class AgentService:
    def __init__(self, prompt_manager: PromptManager, router: ModelRouter):
        self.prompts = prompt_manager
        self.router = router

    async def handle_general_query(
        self, agent_name: str, agent_role: str, user_query: str
    ) -> str:
        """通用 Agent 查询"""
        request = self.prompts.to_llm_request(
            "agent_base",
            variables={
                "agent_name": agent_name,
                "agent_role": agent_role,
                "current_date": "2026-07-23",
                "user_query": user_query,
            },
        )
        response = await self.router.chat_with_fallback(request)
        return response.content

    async def extract_user_info(self, user_message: str) -> dict:
        """提取用户信息(带 Few-shot)"""
        request = self.prompts.to_llm_request(
            "extract_user_info",
            variables={"user_message": user_message},
        )
        response = await self.router.chat_with_fallback(request)
        import json
        return json.loads(response.content)

    async def analyze_problem(self, domain: str, problem: str) -> str:
        """CoT 分析问题"""
        request = self.prompts.to_llm_request(
            "cot_analyzer",
            variables={"domain": domain, "problem": problem},
        )
        response = await self.router.chat_with_fallback(request)
        return response.content

5.4 Prompt Injection 防护

python 复制代码
# app/core/prompt/security.py
import re
from typing import Optional

class PromptSecurity:
    """Prompt 注入攻击防护"""

    # 常见注入模式
    INJECTION_PATTERNS = [
        r"忽略.*指令",           # "忽略上面的指令"
        r"ignore.*instruction",
        r"disregard.*previous",
        r"忘记.*规则",
        r"forget.*(rules|prompt)",
        r"你.*新的.*角色",
        r"you are now",
        r"system.*prompt.*is",
        r"DAN\s",               # "DAN" (Do Anything Now) 越狱模式
        r"jailbreak",
    ]

    # 敏感信息模式
    SENSITIVE_PATTERNS = [
        r"sk-[a-zA-Z0-9]{32,}",  # OpenAI API Key
        r"sk-ant-[a-zA-Z0-9]{32,}",  # Anthropic API Key
    ]

    @classmethod
    def detect_injection(cls, user_input: str) -> Optional[str]:
        """检测 Prompt 注入尝试

        Returns:
            如果检测到注入返回警告信息,否则返回 None
        """
        lower_input = user_input.lower()

        for pattern in cls.INJECTION_PATTERNS:
            if re.search(pattern, lower_input):
                return f"检测到潜在的 Prompt 注入攻击(模式: {pattern})"

        return None

    @classmethod
    def sanitize(cls, user_input: str) -> str:
        """清理用户输入

        1. 移除潜在的注入指令
        2. 脱敏 API Key
        """
        # 脱敏 API Key
        for pattern in cls.SENSITIVE_PATTERNS:
            user_input = re.sub(pattern, "[REDACTED]", user_input)

        return user_input

    @classmethod
    def safe_wrap(cls, user_input: str) -> str:
        """安全包装用户输入

        在处理前检查注入,清理敏感信息
        """
        # 检测注入
        warning = cls.detect_injection(user_input)
        if warning:
            # 可以在日志中记录,但不阻断请求
            # 更好的做法:标记为高风险,降级处理
            pass

        # 清理输入
        return cls.sanitize(user_input)

6. 实战案例

案例1:多角色对话模拟

python 复制代码
# app/core/prompt/role_play.py

ROLE_PLAY_PROMPT = """你现在将扮演以下角色进行对话:

角色:面试官
场景:Java 后端工程师技术面试
公司:一家 AI 初创公司
风格:专业但友善,循序渐进地从基础到深入

你需要:
1. 先做简单自我介绍和公司介绍
2. 问 5 个技术问题,从基础到进阶
3. 对回答给出评价
4. 最后给出总体反馈

开始面试吧。
"""

async def run_interview_simulation(llm_service, resume: str):
    """模拟技术面试"""
    context = ROLE_PLAY_PROMPT + f"\n\n候选人简历:\n{resume}"
    messages = [{"role": "system", "content": context}]

    for _ in range(10):  # 最多 10 轮
        response = await llm_service.chat(messages)
        print(f"面试官: {response.content}")

        if "面试结束" in response.content:
            break

        user_answer = input("你的回答: ")
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": user_answer})

案例2:A/B Prompt 测试框架

python 复制代码
# app/core/prompt/ab_test.py
import asyncio
from dataclasses import dataclass
from typing import Callable

@dataclass
class PromptVariant:
    name: str
    system_prompt: str
    user_prompt_template: str
    weight: float = 0.5  # 流量分配比例

@dataclass
class ABTestResult:
    variant_name: str
    response: str
    latency_ms: float
    token_usage: int

class PromptABTester:
    """Prompt A/B 测试"""

    def __init__(self, variants: list[PromptVariant]):
        self.variants = variants

    async def run_test(
        self,
        llm_service,
        test_cases: list[dict],
        evaluator: Callable[[str, str], float],  # (response, expected) -> score
    ) -> dict:
        """运行 A/B 测试

        Args:
            llm_service: LLM 服务
            test_cases: 测试用例 [{"input": "...", "expected": "..."}]
            evaluator: 评分函数

        Returns:
            每个 variant 的统计结果
        """
        results = {v.name: {"scores": [], "latencies": [], "tokens": []}
                   for v in self.variants}

        for case in test_cases:
            for variant in self.variants:
                response = await llm_service.chat(
                    system=variant.system_prompt,
                    user=variant.user_prompt_template.format(**case),
                )

                score = evaluator(response.content, case["expected"])
                results[variant.name]["scores"].append(score)
                results[variant.name]["latencies"].append(response.latency_ms)
                results[variant.name]["tokens"].append(response.usage.total_tokens)

        # 计算统计信息
        import statistics
        summary = {}
        for name, data in results.items():
            summary[name] = {
                "avg_score": statistics.mean(data["scores"]),
                "avg_latency_ms": statistics.mean(data["latencies"]),
                "avg_tokens": statistics.mean(data["tokens"]),
            }

        return summary

7. Java 对比

维度 Java 做法 Python 做法
模板引擎 Thymeleaf / Freemarker Jinja2
配置格式 YAML (Spring Boot) YAML + PyYAML
模板管理 Spring Resource + PropertySource 自定义 PromptManager
策略实现 Strategy Pattern (DI) 函数式 + 数据驱动
结构化输出 @JsonSchema annotation Prompt 中描述 + JSON Mode
A/B 测试 需自建框架 轻量级类,几十行代码

关键差异:Java 生态中 Prompt 管理通常靠 LangChain4j 或 Spring AI,而 Python 因为 LLM 调用更轻量,自己用 YAML + Jinja2 实现比依赖框架更灵活。


8. 企业最佳实践

8.1 Prompt 版本管理策略

复制代码
版本号格式:MAJOR.MINOR.PATCH
- MAJOR:角色或策略根本变化
- MINOR:添加 Few-shot 示例、调整约束
- PATCH:措辞修正、错别字修复

8.2 Prompt 质量检查清单

  • 角色定义是否清晰明确?
  • 输出格式要求是否具体(JSON schema、markdown 等)?
  • 约束条件是否穷举了边界情况?
  • Few-shot 示例是否覆盖了多样化的输入?
  • Temperature 是否与任务匹配(事实任务低,创意任务高)?
  • 是否有防止 Prompt Injection 的保护?
  • 对不同模型是否有适配版本?

8.3 不同模型的 Prompt 差异

python 复制代码
# 同一功能,不同模型的 Prompt 差异
AGENT_PROMPTS = {
    "gpt-4": """You are a helpful assistant...""",
    "claude": """\n\nHuman: You are a helpful assistant...\n\nAssistant: Understood.""",
    "gemini": """Role: helpful assistant\nTask: ...""",
}

9. 常见错误与解决方案

错误 原因 解决
LLM 不按 JSON 格式输出 Prompt 没有明确要求 使用 JSON Mode 或添加严格格式约束
输出"幻觉"(编造信息) 模型缺乏相关知识 使用 RAG(下一章)提供上下文
忽略 System Prompt Prompt 太长或指令矛盾 精简 Prompt,将关键指令放在开头和结尾
Few-shot 反而降低质量 示例有偏见或与任务不匹配 审查示例质量,A/B 测试对比
Jinja2 渲染报错 变量名拼写错误 使用 StrictUndefined 提前发现
模型输出被截断 max_tokens 设置过小 合理估计输出长度 + 增加 max_tokens

10. 本章总结

本章完成了 Enterprise AI Agent Platform 的 Prompt 工程体系

  1. Prompt 设计原则:角色/上下文/格式/约束四要素框架
  2. 策略体系:Zero-shot → Few-shot → CoT → ReAct,逐级递进
  3. 结构化输出:JSON Mode + Schema 约束
  4. 企业级模板管理:YAML 定义 + Jinja2 渲染 + 版本控制的 PromptManager
  5. 安全防护:Prompt Injection 检测与输入清理

贯穿项目进度:现在 Agent 有了"灵魂"------专业的 Prompt 驱动其行为。下一章学习 Function Calling,让 Agent 拥有"手"------调用外部工具的能力。


11. 面试题

11.1 基础题

  1. 解释 Prompt 的四要素框架。为什么角色定义很重要?
  2. Zero-shot、Few-shot、Chain-of-Thought 各适用于什么场景?
  3. 如何让 LLM 输出合法的 JSON?有哪些方法?
  4. 什么是 Prompt Injection?如何防范?

11.2 进阶题

  1. ReAct 模式和纯粹的 CoT 有什么区别?在什么场景下 ReAct 不可或缺?
  2. 如果 LLM 在执行 ReAct 循环时陷入死循环(反复调用同一个工具但得不到有效结果),如何优雅地处理?
  3. 设计一个 Prompt 版本管理系统,要求支持:多模型适配、A/B 测试分流、灰度发布。讨论核心设计。

12. 练习

练习1:设计 Agent 角色 Prompt(难度:★★)

python 复制代码
"""要求:
为以下三个场景设计完整的 System Prompt(各一个):
1. 代码 Review 助手------检查代码质量、安全漏洞、性能问题
2. 客服 Agent------处理电商退换货咨询
3. 数据提取 Agent------从非结构化文本中提取结构化数据

每个 Prompt 必须包含:角色、上下文、格式、约束四个要素。
"""

练习2:实现 CoT Prompt(难度:★★★)

python 复制代码
"""要求:
设计一个用于代码调试的 CoT Prompt:
1. 输入是一段有 bug 的代码和错误日志
2. 输出应该包含:分析 → 定位 → 修复方案 → 修复后的代码
3. 提供 2 个 Few-shot 示例
4. 在真实代码上测试效果
"""

练习3:Prompt 管理 CLI 工具(难度:★★★★)

python 复制代码
"""要求:
实现一个命令行工具:
1. prompt list --- 列出所有模板
2. prompt show <name> --- 查看模板详情
3. prompt render <name> ---key=value --- 渲染并显示
4. prompt test <name> --- 在测试集上评估
5. prompt ab <name> ---v1=<path> ---v2=<path> --- A/B 测试
"""

13. 作业

作业1:为贯穿项目创建全部 Prompt 模板

  1. 创建 agent_base.yaml(通用 Agent)
  2. 创建 agent_customer_service.yaml(客服 Agent)
  3. 创建 agent_code_review.yaml(代码 Review Agent)
  4. 创建 extract_intent.yaml(意图识别)
  5. 每个模板提供至少 2 个 Few-shot 示例

作业2:构建 Prompt 评估数据集

  1. 为意图识别 Prompt 准备 50 个测试用例
  2. 每个测试用例包含:input、expected_intent、expected_entities
  3. 编写评估脚本,计算准确率

作业3:Prompt Injection 攻击与防御实验

  1. 列出 5 种常见的 Prompt Injection 攻击手段
  2. 对每种攻击设计防御方案
  3. 测试防御方案的有效性
  4. 写一份报告总结发现

14. 预习

下一章将学习:Function Calling 与 Tool Calling

预习要点:

  • OpenAI Function Calling API 细节
  • Tool 的定义(name、description、parameters JSON Schema)
  • Tool Calling 的完整调用循环
  • 并行 Tool Calling
  • 工具调用的错误处理
  • 如何设计好的 Tool Description

思考题:一个 Agent 需要同时查询天气和股票价格。如果这两个工具调用不相互依赖,能否并行执行?如果能,如何实现?


文档版本 :v1.0 | 创建日期 :2026-07-23 | 适用 Python 版本:3.12+

相关推荐
小弥儿7 小时前
GitHub今日热榜 | 2026-08-26:AI 大脑与知识管理扎堆
人工智能·学习·开源·github
荷蒲7 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
中科高级技工学校7 小时前
乌鲁木齐中医康复学习经历分享
大数据·学习
机构师7 小时前
AI编程实战:效率与成本,AI 编程的 ROI 怎么算
人工智能·prompt·ai编程·deepseek
JavaPub-rodert9 小时前
具身智能行业技术全景:从感知、规划、控制到 VLA 与机器人学习
学习·机器人
知识分享小能手10 小时前
概理论与数理统计学习教程,从入门到精通,在数理统计中应用R软件(22)
开发语言·学习·r语言
陈年老古董11 小时前
CentOS编译安装Python3.11完整教程
linux·笔记·学习·centos·python3.11
戈文波Geir-Wenbo Ge11 小时前
深度七步:从学习到主宰
学习
clorinda11 小时前
OpenCV实战学习记录:图像拼接与答题卡识别
人工智能·opencv·学习
red_redemption11 小时前
自由学习记录(221)
学习·renderdoc