1. System Prompt 的结构

System Prompt 是 LLM 交互中优先级最高的指令层。一个工程化的 System Prompt 应包含以下模块:

┌──────────────────────────────────┐
│  1. 角色定义 (Role)              │
│  2. 任务描述 (Task)              │
│  3. 行为约束 (Constraints)       │
│  4. 输出格式 (Output Format)     │
│  5. 知识边界 (Knowledge Boundary)│
│  6. 示例 (Examples)              │
│  7. 异常处理 (Error Handling)    │
└──────────────────────────────────┘

1.1 完整模板

# 角色
你是一个高级数据分析师,擅长从结构化数据中提取洞察。

# 任务
用户会提供数据描述或 CSV 数据,你需要:
1. 理解数据结构和业务含义
2. 进行统计分析
3. 生成可视化建议
4. 输出分析报告

# 约束
- 只基于用户提供的数据分析,不编造数据
- 如果数据不足,明确说明需要什么数据
- 不输出投资建议或确定性预测
- 统计方法需标注假设条件

# 输出格式
```json
{
  "summary": "一句话摘要",
  "statistics": {"mean": 0, "median": 0, "std": 0},
  "insights": ["洞察1", "洞察2"],
  "visualization": {"type": "bar", "reason": "原因"},
  "limitations": ["局限1"]
}

知识边界

  • 熟悉:统计学、Python pandas、SQL
  • 不熟悉:用户的具体业务上下文(需用户补充)

异常处理

  • 数据为空 → 询问用户提供数据
  • 数据格式错误 → 指出格式问题并给出正确格式示例
  • 分析超出能力范围 → 明确说明并建议替代方案

## 2. 角色定义的科学方法

### 2.1 角色三要素

```python
ROLE_TEMPLATE = """
# 身份
你是{profession},拥有{experience}年经验。

# 专长
{expertise_list}

# 思维方式
{thinking_style}

# 沟通风格
{communication_style}
"""

# 示例
role = ROLE_TEMPLATE.format(
    profession="资深安全工程师",
    experience="15",
    expertise_list="- Web 安全\n- 逆向工程\n- 安全架构设计",
    thinking_style="先评估威胁模型,再设计方案,最后验证有效性",
    communication_style="简洁直接,用数据和事实说话,避免模糊表述"
)

2.2 角色粒度对比

角色定义效果Token
“你是助手”泛化回答~5
“你是程序员”技术导向回答~10
“你是10年经验的Python后端工程师,熟悉高并发架构”精准技术回答~30
完整角色三要素最精准,风格一致~100

关键发现:角色定义越具体,输出质量和一致性越高,但超过一定粒度后边际收益递减。

2.3 反模式:过度角色设定

# ❌ 过度设定
你是一个名叫Alice的AI助手,今年28岁,毕业于MIT计算机系,
喜欢喝拿铁,有一只叫Tensor的猫,性格内向但工作热情,
暗恋同事Bob但不敢表白...

# 这些信息对任务毫无帮助,浪费 Token 且分散注意力

原则:角色设定的每一句话都应服务于任务目标。

3. 约束设定

3.1 约束类型

CONSTRAINTS = """
# 硬约束(必须遵守)
- 只使用中文回答
- 输出必须是合法 JSON
- 单次回答不超过 500 字
- 不讨论政治、宗教敏感话题

# 软约束(尽量遵守)
- 优先使用专业术语
- 引用来源时标注可信度
- 如果不确定,明确说明

# 条件约束(满足条件时触发)
- 如果用户问代码问题 → 必须提供可运行代码
- 如果用户提供数据 → 必须先描述数据特征
- 如果回答涉及医学/法律 → 必须加免责声明
"""

3.2 约束的 LLM 遵守率

约束类型GPT-4 遵守率GPT-3.5 遵守率
语言限制98%92%
格式限制95%78%
长度限制82%61%
内容限制91%74%
条件触发88%65%

提升遵守率的技巧:在 System Prompt 末尾重复关键约束。

4. 输出格式控制

4.1 格式指定方法

# 方法1:JSON Schema 描述
OUTPUT_FORMAT = """
输出必须符合以下结构:
{
  "classification": "string (one of: A, B, C)",
  "confidence": "number (0-1)",
  "reasoning": "string (max 200 chars)",
  "evidence": ["string", "string"]
}
"""

# 方法2:XML 标签
OUTPUT_FORMAT = """
<result>
  <classification>分类结果</classification>
  <confidence>置信度</confidence>
  <reasoning>推理过程</reasoning>
</result>
"""

# 方法3:Markdown 模板
OUTPUT_FORMAT = """
## 分类结果
{{classification}}

## 置信度
{{confidence}}/10

## 推理过程
{{reasoning}}
"""

4.2 格式可靠性对比

格式方法可靠性可解析性适用场景
JSON Schema95%API 集成
XML 标签92%文档处理
Markdown88%人类阅读
Function Calling99%+极高结构化 API

5. 多轮对话管理

5.1 状态追踪

class ConversationManager:
    def __init__(self, system_prompt):
        self.system_prompt = system_prompt
        self.history = []
        self.state = {
            "topic": None,
            "user_intent": None,
            "facts_collected": {},
            "turn_count": 0
        }
    
    def update_state(self, user_input, assistant_output):
        """每轮对话后更新状态"""
        self.state["turn_count"] += 1
        
        # 话题检测
        new_topic = self.detect_topic(user_input)
        if new_topic != self.state["topic"]:
            self.state["topic"] = new_topic
            self.state["facts_collected"] = {}  # 话题切换,重置事实
    
    def build_prompt(self, user_input):
        """构造带状态追踪的 Prompt"""
        context = f"""
当前对话状态:
- 话题:{self.state['topic']}
- 已收集信息:{json.dumps(self.state['facts_collected'], ensure_ascii=False)}
- 对话轮次:{self.state['turn_count']}

用户输入:{user_input}
"""
        return context

5.2 对话摘要注入

长对话中,上下文窗口会溢出。解决方案:定期摘要并注入

def manage_context(messages, max_tokens=6000):
    """上下文窗口管理"""
    total = count_tokens(messages)
    
    if total <= max_tokens:
        return messages
    
    # 保留 system prompt + 最近 N 轮
    system = messages[0]
    recent = messages[-6:]  # 最近3轮
    
    # 中间部分摘要
    middle = messages[1:-6]
    summary = summarize_conversation(middle)
    
    return [system, 
            {"role": "system", "content": f"对话摘要:{summary}"},
            *recent]

6. 版本管理

6.1 Prompt 版本控制

# prompts/v2_analyst.yaml
version: "2.1.0"
parent: "2.0.0"
changelog:
  - "增加了 JSON 输出格式约束"
  - "收紧了长度限制到 500 字"
  - "增加了条件约束:数据为空时的处理"

system_prompt: |
  # 角色
  你是高级数据分析师...
  
  # 约束
  - 输出必须是合法 JSON
  ...

metadata:
  author: "prompt-engineering-team"
  created: "2026-06-25"
  tags: ["analyst", "json", "v2"]
  metrics:
    accuracy: 0.91
    latency_p95: 2.3s
    user_satisfaction: 4.2

6.2 版本管理工具

import yaml
from pathlib import Path

class PromptRegistry:
    """Prompt 版本注册中心"""
    
    def __init__(self, registry_dir="prompts/"):
        self.dir = Path(registry_dir)
    
    def load(self, name, version="latest"):
        if version == "latest":
            versions = sorted(self.dir.glob(f"{name}_v*.yaml"))
            if not versions:
                raise FileNotFoundError(f"No prompts found for {name}")
            path = versions[-1]
        else:
            path = self.dir / f"{name}_v{version}.yaml"
        
        with open(path) as f:
            return yaml.safe_load(f)
    
    def diff(self, name, v1, v2):
        """比较两个版本的差异"""
        p1 = self.load(name, v1)["system_prompt"]
        p2 = self.load(name, v2)["system_prompt"]
        import difflib
        return list(difflib.unified_diff(
            p1.splitlines(), p2.splitlines(),
            lineterm="", n=2
        ))

7. A/B 测试

7.1 测试框架

import random
from dataclasses import dataclass
from typing import List

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

@dataclass
class TestResult:
    variant: str
    quality_score: float  # 1-5 评分
    latency_ms: float
    token_count: int
    user_feedback: str

class PromptABTest:
    def __init__(self, variants: List[PromptVariant]):
        # 归一化权重
        total = sum(v.weight for v in variants)
        self.variants = [
            PromptVariant(v.name, v.system_prompt, v.weight / total)
            for v in variants
        ]
        self.results: List[TestResult] = []
    
    def assign(self) -> PromptVariant:
        """流量分配"""
        r = random.random()
        cumulative = 0
        for v in self.variants:
            cumulative += v.weight
            if r <= cumulative:
                return v
        return self.variants[-1]
    
    def record(self, result: TestResult):
        self.results.append(result)
    
    def report(self):
        """生成 A/B 测试报告"""
        from statistics import mean, stdev
        report = {}
        for v in self.variants:
            v_results = [r for r in self.results if r.variant == v.name]
            if not v_results:
                continue
            report[v.name] = {
                "sample_size": len(v_results),
                "quality_mean": mean(r.quality_score for r in v_results),
                "quality_std": stdev(r.quality_score for r in v_results) if len(v_results) > 1 else 0,
                "latency_p50": sorted(r.latency_ms for r in v_results)[len(v_results)//2],
                "avg_tokens": mean(r.token_count for r in v_results),
            }
        return report

7.2 A/B 测试示例

# 定义两个变体
variant_a = PromptVariant(
    name="v2.0_concise",
    system_prompt="你是数据分析师。简洁回答,不超过200字。",
    weight=0.5
)

variant_b = PromptVariant(
    name="v2.1_structured",
    system_prompt="你是数据分析师。按JSON格式回答,包含analysis、recommendations字段。",
    weight=0.5
)

# 运行测试
test = PromptABTest([variant_a, variant_b])

for query in test_queries:
    variant = test.assign()
    start = time.time()
    output = llm.generate(variant.system_prompt, query)
    latency = (time.time() - start) * 1000
    
    # 人工或自动评分
    score = quality_evaluator.score(output, query)
    
    test.record(TestResult(
        variant=variant.name,
        quality_score=score,
        latency_ms=latency,
        token_count=count_tokens(output),
        user_feedback=""
    ))

# 查看报告
print(json.dumps(test.report(), indent=2))

7.3 统计显著性判断

from scipy import stats

def is_significant(results_a, results_b, alpha=0.05):
    """判断 A/B 测试结果是否统计显著"""
    scores_a = [r.quality_score for r in results_a]
    scores_b = [r.quality_score for r in results_b]
    
    # 样本量检查
    if len(scores_a) < 30 or len(scores_b) < 30:
        return False, "样本量不足(至少需要30)"
    
    # 正态性检验
    _, p_normal = stats.shapiro(scores_a + scores_b)
    if p_normal < 0.05:
        # 非正态分布,用 Mann-Whitney U 检验
        stat, p_value = stats.mannwhitneyu(scores_a, scores_b)
    else:
        # 正态分布,用 t 检验
        stat, p_value = stats.ttest_ind(scores_a, scores_b)
    
    return p_value < alpha, f"p={p_value:.4f}"

8. 总结

System Prompt 工程化的核心是把 Prompt 当软件来管理:

软件工程实践Prompt 工程对应
架构设计Prompt 结构(角色/约束/格式)
代码规范Prompt 模板与风格指南
版本控制Prompt Registry
单元测试Golden Set 测试
CI/CD自动化评测流水线
A/B 测试流量分配与统计检验

核心原则:Prompt 不是一次性的文本,而是需要持续迭代、测试、版本管理的工程产物。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。