上一篇讲了 messages 的骨架。这一篇深入两个可调控的东西:system prompt 怎么写才有效,以及 API 的几个关键参数到底在控制什么。参数部分全部配实验,建议边跑边读。
一、system prompt:写给模型的" 岗位说明书"
1.1 一个对比例子
不用 system prompt:
ini
messages = [{"role": "user", "content": "帮我看看这段代码有什么问题:x = 5 / 0"}]
回答可能很啰嗦:先讲什么是除法,再讲异常处理体系,最后才说 "你除以零了"
用 system prompt:
ini
messages = [
{"role": "system", "content": "你是一个代码审查员。只指出问题和修法,每条不超过两行,不讲背景知识。"},
{"role": "user", "content": "帮我看看这段代码有什么问题:x = 5 / 0"},
]
回答:除以零错误。改为先判断除数是否为 0,或用 try/except 捕获 ZeroDivisionError。
同样的问题,输出天差地别。system prompt 决定模型" 用什么身份、什么方式" 回答。
1.2 一个实用的 system prompt 模板
有效的 system prompt 通常包含这四块(按需取舍):
ini
SYSTEM_PROMPT = """
# 角色
你是一个Python代码审查助手。
# 行为规则
1. 只指出问题,不重写整个文件
2. 每个问题格式:[严重程度] 位置 - 问题 - 建议修法
3. 不确定的地方明确说"不确定",禁止猜测
# 输出格式
用Markdown列表输出,最多5条,按严重程度排序。
# 边界
如果代码与Python无关,回复"仅支持Python代码"。
"""
四块分别是:角色(你是谁)→ 规则(怎么做)→ 格式(输出长什么样)→ 边界(什么时候拒绝)。
1.3 写 system prompt 的三个新手误区
- 误区一:堆形容词。"你是一个非常非常专业的专家"------ 没用。模型不因为夸它而变强。要写可执行的规则,不写情绪。
- 误区二:规则互相矛盾。"回答要详尽" + "不超过 50 字"------ 模型只能随机服从一个。写完通读一遍,删掉冲突项。
- 误区三:用否定句写期望行为。"不要啰嗦" 不如" 每条不超过两行"。模型对正向指令的服从度更高;否定句只在划红线时用("禁止编造 API 名称")。
二、参数详解:每个都做实验
2.1 temperature:随机性旋钮
原理一句话:模型每生成一个 token,其实是对所有候选词算出一个概率分布;temperature 决定从这个分布里抽样时的" 冒险程度"。
- 0:永远选概率最高的词 → 同样输入几乎得到同样输出(确定性)
- 越高(如 1.5):低概率词也有机会被选中 → 输出多变、有创意、也更不稳定
实验代码(同一个刁钻问题,不同 temperature 各跑 3 次):
ini
def ask(temp: float) -> str:
resp = client.chat.completions.create(
model="deepseek-chat",
temperature=temp,
messages=[{"role": "user", "content": "用一个比喻解释什么是递归,一个词的比喻也算"}],
)
return resp.choices[0].message.content[:30]
for t in (0, 0.7, 1.5):
print(f"--- temperature={t} ---")
for _ in range(3):
print(" ", ask(t))
你会观察到:t=0 时三次输出几乎相同;t=1.5 时三次各不相同,而且偶尔出现奇怪的比喻。
怎么选:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 代码生成、数据抽取、分类 | 0 ~ 0.3 | 要稳定可复现 |
| 日常对话、总结改写 | 0.5 ~ 0.8 | 平衡 |
| 创意写作、起名字 | 0.9 ~ 1.2 | 要发散 |
本系列属于 Agent 开发,几乎全程 0~0.3------ 你要的是一个可靠的执行者,不是艺术家。
2.2 max_tokens:输出长度上限
ini
resp = client.chat.completions.create(
model="deepseek-chat",
messages=[...],
max_tokens=100, # 回答最多100个token,超过就硬截断
)
两个用途:
- 省钱护栏:防止模型失控输出长文
- 强制造短回答:配合" 用一句话回答" 的提示效果更好
注意一个坑:如果输出被截断,resp.choices 0.finish_reason 会是 "length" 而不是 "stop"。调试时发现回答莫名断在半句话,先检查这个字段:
go
print(resp.choices[0].finish_reason) # "stop"=正常结束 "length"=被截断
2.3 model:选哪个模型
同一家通常有多个模型档位(如 deepseek-chat /deepseek-reasoner),规律:
- 普通对话模型:快、便宜,适合 Agent 里的常规节点
- 推理模型(reasoner 类):贵、慢,但复杂问题正确率高,第 09 篇详解
工程经验:不要全程用最贵的。路由 / 格式化 / 简单判断用便宜模型,关键决策才上推理模型 ------ 这是成本工程的基本功。
2.4 stream:流式开关
stream=True # 逐 token 返回(第 04 篇整篇讲它) 这里先记住参数名即可。
三、把参数和 system prompt 组合成一个可复用的函数
从这篇开始,我们会反复用到一个封装好的 chat () 函数,建议存成 llm_utils.py(_utils.py),后续文章直接 import:
python
# llm_utils.py
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url="https://api.deepseek.com",
)
def chat(
messages: list[dict],
model: str = "deepseek-chat",
temperature: float = 0.3,
max_tokens: int = 1024,
) -> str:
"""统一封装的LLM调用。Agent开发默认低temperature。"""
resp = client.chat.completions.create(
model=model,
messages=messages,
temperature=temperature,
max_tokens=max_tokens,
)
if resp.choices[0].finish_reason == "length":
print("[警告] 输出被max_tokens截断")
return resp.choices[0].message.content
# 用法示例:
if __name__ == "__main__":
print(chat([
{"role": "system", "content": "你是简洁的编程助手。"},
{"role": "user", "content": "一句话解释什么是异步编程。"},
]))
封装的好处:参数默认值统一(所有调用都是 0.3),截断警告自动打,换模型只改一处。
四、一个综合实验:观察" 规矩" 的服从度
测试 system prompt 在长对话中会不会失效:
css
system = "你回答任何问题都只能用'是'或'否'。"
history = [{"role": "system", "content": system}]
for q in ["Python是编程语言吗?", "天空是蓝的吗?", "详细介绍一下Python的历史", "详细讲讲图灵的故事"]:
history.append({"role": "user", "content": q})
a = chat(history, temperature=0)
history.append({"role": "assistant", "content": a})
print(f"Q: {q}\nA: {a[:50]}\n")
典型现象:前几轮严格答" 是 / 否",到后面开始" 越狱" 变长篇大论。结论:system prompt 不是硬约束,是软引导,上下文越长服从度越低。两个对策:
- 重要格式要求在最后一轮 user 消息里再强调一次(末尾指令服从度最高)
- 输出必须严格结构化时,用代码解析 + 失败重试兜底,而不是相信 prompt(第 15 篇讲 OutputParser 的动机就在这)
五、小结
- system prompt 四块结构:角色 → 规则 → 格式 → 边界;写正向可执行的规则
- temperature 0~0.3 用于 Agent;实验证明 0 确定性强
- max_tokens 是护栏,检查 finish_reason 判断截断
- 模型分档混用:便宜模型干杂活,推理模型干难活
- system prompt 会随对话变长而失效 ------ 重要约束要末尾重申 + 代码兜底
六、自测
- ☐ 用四块结构给" 外卖推荐助手" 写一个 system prompt
- ☐ 跑 temperature 实验,亲眼看到 0 和 1.5 的差别
- ☐ 把 max_tokens 设成 20 问一个长问题,观察 finish_reason
下一篇:04 流式输出 ------ AI 是一个字一个字想出来的,这个特性怎么用代码接住。
写在最后
如果想系统、完整地吃透 Harness、Hermes 整套前沿智能体开发体系,完成从只会调模型到可控、高质量、可落地的 AI 工程交付进阶,可以关注慕课网近期上新的《Harness&Hermes 多智能体开发特训营》。