第01章:Prompt的本质——如何与LLM真正沟通

第01章:Prompt的本质——如何与LLM真正沟通

大多数人把LLM当搜索引擎用。一旦你理解了LLM的预测机制,你会意识到它更像一个极度博学的角色扮演演员——你给的剧本质量,决定了表演质量。


1.1 LLM的工作原理(工程视角)

LLM(大型语言模型)的核心操作只有一个:预测下一个token

# 极度简化的LLM工作原理
def llm_predict(tokens_so_far: list[int]) -> int:
    """
    给定已有的token序列,预测下一个token
    返回:下一个token的ID(从词汇表中选概率最高的)
    """
    probabilities = model.forward(tokens_so_far)  # 神经网络前向传播
    return sample(probabilities, temperature=temperature)

这个看起来简单的操作,在规模足够大、训练数据足够多之后,产生了惊人的能力。

对Prompt工程的启示

LLM在预测下一个token时,会考虑上下文中所有已有的token。这意味着:

  • 你写的每个字都在影响输出
  • 顺序很重要(前面的内容影响后面的预测)
  • 清晰的结构让预测更"稳定"(减少歧义空间)

1.2 Token:Prompt的基本单位

理解token对于Prompt工程至关重要:

import tiktoken

def analyze_tokens(text: str, model: str = "gpt-4o") -> dict:
    """分析文本的token构成"""
    encoder = tiktoken.encoding_for_model(model)
    tokens = encoder.encode(text)
    decoded = [encoder.decode([t]) for t in tokens]
    
    return {
        "text": text,
        "token_count": len(tokens),
        "tokens": tokens,
        "decoded_tokens": decoded,
        "cost_estimate_usd": len(tokens) / 1_000_000 * 2.5  # GPT-4o输入价格
    }

# 测试示例
examples = [
    "Hello, world!",
    "你好,世界!",
    "ChatGPT",
    "chat_gpt",
    "chat gpt",
]

for text in examples:
    result = analyze_tokens(text)
    print(f"'{text}' → {result['token_count']} tokens: {result['decoded_tokens']}")

# 输出(大约):
# 'Hello, world!' → 4 tokens: ['Hello', ',', ' world', '!']
# '你好,世界!' → 7 tokens: ['你', '好', ',', '世', '界', '!'] (中文1字≈1token)
# 'ChatGPT' → 3 tokens: ['Chat', 'G', 'PT']
# 'chat_gpt' → 3 tokens: ['chat', '_', 'gpt']
# 'chat gpt' → 3 tokens: ['chat', ' gpt'] (实际2个)

重要Token规律

  1. 英文:约4字符 = 1 token
  2. 中文:约1-2个汉字 = 1 token(中文token成本比英文高)
  3. 专业术语/缩写:可能被分成多个token(ChatGPT → 3个token)
  4. 空格是token的一部分 gptgpt 是不同的token

1.3 温度(Temperature)的工程含义

from openai import OpenAI
client = OpenAI()

def compare_temperatures(prompt: str, temperatures: list[float], n_samples: int = 3):
    """对比不同温度下的输出"""
    
    for temp in temperatures:
        print(f"\n=== Temperature = {temp} ===")
        for i in range(n_samples):
            response = client.chat.completions.create(
                model="gpt-4o",
                messages=[{"role": "user", "content": prompt}],
                temperature=temp,
                max_tokens=50
            )
            print(f"样本{i+1}: {response.choices[0].message.content}")

# 测试
compare_temperatures(
    prompt="写一个创意的产品名称,针对AI代码编辑器",
    temperatures=[0.0, 0.7, 1.5],
    n_samples=3
)
Temperature 行为 适用场景
0.0 确定性输出,每次结果相同 数据提取、分类、代码生成
0.3–0.7 轻度创意,相对稳定 文本摘要、问答、翻译
0.7–1.0 较多变化,有创意 内容创作、头脑风暴
>1.0 高度随机,可能不连贯 极端创意探索(慎用)

实用规则

  • 分类/提取任务 → temperature = 0
  • 需要一致性的生产系统 → temperature ≤ 0.3
  • 创意任务 → temperature = 0.7–1.0

1.4 上下文窗口:你的工作空间

CONTEXT_WINDOWS = {
    "gpt-4o": 128_000,          # 约100,000字中文
    "gpt-4o-mini": 128_000,
    "claude-3-5-sonnet": 200_000, # 最大上下文
    "o1": 128_000,
    "gemini-1.5-pro": 1_000_000  # 百万token
}

def estimate_context_usage(
    system_prompt: str,
    conversation_history: list[dict],
    user_message: str,
    expected_output_tokens: int = 500
) -> dict:
    """估算上下文使用情况"""
    encoder = tiktoken.encoding_for_model("gpt-4o")
    
    system_tokens = len(encoder.encode(system_prompt))
    history_tokens = sum(
        len(encoder.encode(msg["content"])) 
        for msg in conversation_history
    )
    user_tokens = len(encoder.encode(user_message))
    
    total_input = system_tokens + history_tokens + user_tokens
    total_with_output = total_input + expected_output_tokens
    
    window = CONTEXT_WINDOWS["gpt-4o"]
    usage_pct = total_with_output / window * 100
    
    return {
        "system_prompt_tokens": system_tokens,
        "history_tokens": history_tokens,
        "user_message_tokens": user_tokens,
        "total_input_tokens": total_input,
        "estimated_total_tokens": total_with_output,
        "context_window": window,
        "usage_percentage": f"{usage_pct:.1f}%",
        "warning": "接近上下文限制" if usage_pct > 80 else "正常"
    }

1.5 Prompt的三层结构

每个Prompt都可以解构为三层:

PROMPT_ANATOMY = {
    "instruction_layer": {
        "description": "告诉LLM做什么",
        "components": ["任务描述", "输出格式要求", "约束条件"],
        "example": "你是一个JSON数据提取专家。从以下文本中提取所有日期,以ISO格式输出为JSON数组。"
    },
    "context_layer": {
        "description": "给LLM需要知道的背景信息",
        "components": ["角色背景", "领域知识", "相关文档", "对话历史"],
        "example": "以下是一份合同文本,签订日期在开头段落中..."
    },
    "input_layer": {
        "description": "LLM需要处理的具体输入",
        "components": ["用户查询", "待处理文档", "数据"],
        "example": "合同文本:{contract_content}"
    }
}

def build_prompt(instruction: str, context: str, user_input: str) -> list[dict]:
    """按三层结构构建Prompt"""
    return [
        {"role": "system", "content": instruction},
        {"role": "user", "content": f"背景信息:\n{context}\n\n输入:\n{user_input}"}
    ]

1.6 为什么有些Prompt"神奇地有效"

理解背后的原理:

# 案例1:为什么"一步一步思考"有效
"""
原始:计算 (25 × 48) + (33 × 17) = ?
改进:计算 (25 × 48) + (33 × 17),请一步一步思考

为什么有效:
- 下一个token预测时,"一步一步思考"后的上下文
  倾向于出现中间计算步骤
- 中间步骤作为上下文,让最终答案的预测更准确
"""

# 案例2:为什么给出示例有效
"""
原始:把以下文本的情绪分类为正面/负面
改进:把以下文本的情绪分类(正面/负面)
       示例:
       "这个产品真的很棒!" → 正面
       "质量太差了,完全不值这个价格。" → 负面

为什么有效:
- 示例缩小了LLM的"解释空间"
- LLM在训练中见过大量"输入→输出"对,示例激活了这个模式
"""

# 案例3:为什么角色设定有效
"""
原始:分析这段代码是否有安全漏洞
改进:你是一位有15年经验的网络安全专家,
       专注于OWASP Top 10漏洞检测...

为什么有效:
- 角色设定是一种隐式的few-shot:
  训练数据中,安全专家写的内容有特定的词汇、
  分析框架和输出风格
- "安全专家"这个token序列激活了相关的"权重"
"""

1.7 LLM的局限性:Prompt无法修复的问题

PROMPT_LIMITATIONS = {
    "knowledge_cutoff": {
        "problem": "训练数据有截止日期,不了解最新信息",
        "workaround": "RAG(第05章),或使用搜索工具"
    },
    "math_computation": {
        "problem": "LLM不擅长精确计算(token预测 ≠ 计算)",
        "workaround": "让LLM生成代码,用代码执行器计算"
    },
    "consistency_at_scale": {
        "problem": "在大量调用中,温度>0时输出不稳定",
        "workaround": "Temperature=0 + 结构化输出 + 验证层"
    },
    "context_length": {
        "problem": "超出上下文窗口的信息无法被利用",
        "workaround": "文本分块 + 摘要 + RAG检索"
    },
    "hallucination": {
        "problem": "LLM会以高置信度陈述错误信息",
        "workaround": "要求引用来源 + 事实核查工具 + 低temperature"
    }
}

本章小结

  1. LLM的核心是下一个token预测——上下文的每个字都在影响输出结果
  2. Token是计费和上下文的基本单位:英文约4字符/token,中文约1字符/token
  3. Temperature控制随机性:提取/分类任务用0,创意任务用0.7–1.0
  4. Prompt的三层结构:指令层(做什么)+ 上下文层(背景)+ 输入层(处理什么)
  5. Few-shot/角色设定/CoT有效的原因都是"激活训练数据中的相关模式"

行动项:用tiktoken分析你最常用的5个System Prompt的token数量,找出最冗余的部分,尝试压缩到原来的70%而不失效果。