第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规律:
- 英文:约4字符 = 1 token
- 中文:约1-2个汉字 = 1 token(中文token成本比英文高)
- 专业术语/缩写:可能被分成多个token(ChatGPT → 3个token)
- 空格是token的一部分:
gpt和gpt是不同的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"
}
}
本章小结
- LLM的核心是下一个token预测——上下文的每个字都在影响输出结果
- Token是计费和上下文的基本单位:英文约4字符/token,中文约1字符/token
- Temperature控制随机性:提取/分类任务用0,创意任务用0.7–1.0
- Prompt的三层结构:指令层(做什么)+ 上下文层(背景)+ 输入层(处理什么)
- Few-shot/角色设定/CoT有效的原因都是"激活训练数据中的相关模式"
行动项:用tiktoken分析你最常用的5个System Prompt的token数量,找出最冗余的部分,尝试压缩到原来的70%而不失效果。