学习第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 工程体系:
- Prompt 设计原则:角色/上下文/格式/约束四要素框架
- 策略体系:Zero-shot → Few-shot → CoT → ReAct,逐级递进
- 结构化输出:JSON Mode + Schema 约束
- 企业级模板管理:YAML 定义 + Jinja2 渲染 + 版本控制的 PromptManager
- 安全防护:Prompt Injection 检测与输入清理
贯穿项目进度:现在 Agent 有了"灵魂"------专业的 Prompt 驱动其行为。下一章学习 Function Calling,让 Agent 拥有"手"------调用外部工具的能力。
11. 面试题
11.1 基础题
- 解释 Prompt 的四要素框架。为什么角色定义很重要?
- Zero-shot、Few-shot、Chain-of-Thought 各适用于什么场景?
- 如何让 LLM 输出合法的 JSON?有哪些方法?
- 什么是 Prompt Injection?如何防范?
11.2 进阶题
- ReAct 模式和纯粹的 CoT 有什么区别?在什么场景下 ReAct 不可或缺?
- 如果 LLM 在执行 ReAct 循环时陷入死循环(反复调用同一个工具但得不到有效结果),如何优雅地处理?
- 设计一个 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 模板
- 创建
agent_base.yaml(通用 Agent) - 创建
agent_customer_service.yaml(客服 Agent) - 创建
agent_code_review.yaml(代码 Review Agent) - 创建
extract_intent.yaml(意图识别) - 每个模板提供至少 2 个 Few-shot 示例
作业2:构建 Prompt 评估数据集
- 为意图识别 Prompt 准备 50 个测试用例
- 每个测试用例包含:input、expected_intent、expected_entities
- 编写评估脚本,计算准确率
作业3:Prompt Injection 攻击与防御实验
- 列出 5 种常见的 Prompt Injection 攻击手段
- 对每种攻击设计防御方案
- 测试防御方案的有效性
- 写一份报告总结发现
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+