第 2 章 · 认知打底与 Prompt 工程
本章目标:搞懂"大模型到底是什么",并能熟练地"指挥"它干活。不涉及任何神经网络公式。
2.1 大模型到底是什么
先破掉一个常见的错误直觉。很多人以为大模型是个"聪明的搜索引擎"或"知识库"。它不是。
大模型(LLM,Large Language Model)本质上做一件事:给定前面的一段文字,预测下一个最可能出现的词(token)。
举个例子,如果你给它:
"中国的首都是"它会基于海量训练数据,算出下一个词最可能是"北京"(概率约 99%),其次是"上海"等。它没有去"查询"一个数据库,而是"算"出了概率最高的续写。
这带来两个直接推论,也是你后面理解一切的钥匙:
- 它生成的是"最像"的答案,不是"正确"的答案。 所以它会一本正经地编造——这叫"幻觉"。
- 它的能力上限取决于训练数据。 训练数据截止之前的、公开的、高频的知识它知道;你公司的内部文档、你昨天才改的代码,它一概不知。这正是后面 RAG(第 4 章)要解决的问题。
所以,把 LLM 当成一个"会推理、但会出错的组件"来用,而不是当成真理来源。 这句话贯穿整个教程。
2.2 Token:模型的最小处理单位
模型不直接读"字",而是读 token。Token 是文本被切分后的最小单元。
- 英文里,一个常见单词 ≈ 1 个 token(如 "hello" ≈ 1 token,但 "unbelievable" 可能拆成 2-3 个)。
- 中文里,1 个汉字 ≈ 1~2 个 token(通常约 1.5 个)。所以"你好"大概 2~3 个 token。
为什么你必须懂 token?因为几乎所有东西都按 token 算:
- 计费:API 按"每百万 token"收费。输入 token 和输出 token 价格通常不同(输出更贵,一般是输入的 2~4 倍)。
- 上下文窗口:模型一次能处理的 token 总量是有限的(比如 128K)。
- 成本优化:一个请求里,输入 token 越多越贵。所以后面会学"怎么少喂、喂精"。
记一个量级:128K 上下文 ≈ 10 万~15 万汉字。一本 30 万字的小说约 40 万 token,超出了 128K 窗口。
2.3 上下文窗口:模型的"短期记忆"
上下文窗口(Context Window) 是模型一次能"同时看到"的 token 总数上限。
你一次请求里塞进去的所有东西——system 提示词 + 历史对话 + 用户问题 + 模型要输出的内容——全部加起来不能超过这个窗口。
两个必须知道的后果:
- 超了会截断。 如果对话太长,最早的内容会被挤出去,模型就"忘了"前面说过的话。这就是为什么你跟 ChatGPT 聊久了,它会忘记你一开始交代的事。
- 窗口不是越大越好用。 即使没超,内容塞得越长,模型越容易"注意力涣散"——关键信息被淹没在大量无关文字里。所以工程上讲究"把最相关的信息精炼地喂进去",而不是"全塞进去"。
对应到实战:长对话要主动做"记忆管理"(第 6 章),RAG 要"只检索最相关的几段"喂进去(第 4 章)。
2.4 三种角色:system / user / assistant
调用 API 时,messages 是一个数组,每个元素是一个 {role, content}。三种角色各有分工:
| 角色 | 作用 | 例子 |
|---|---|---|
| system | 定"人设"和全局规则,优先级最高 | "你是一个严谨的数据库专家,只写标准 SQL。" |
| user | 用户的具体提问/指令 | "把'上月入库总量'翻译成 SQL。" |
| assistant | 模型自己的回答(用于记录历史,或给模型做示范) | 模型上一条回答的内容 |
一个关键技巧:system 是"管住模型行为"的第一道闸。你把规则写进 system,比写进 user 更稳,模型更不容易跑偏。比如你要它"只输出 JSON、不要废话",写进 system 比每次在 user 里叮嘱更可靠。
另外,assistant 角色能用来"做示范"——你在历史里放几组"用户这样问、助手这样答"的例子,模型会模仿这个风格。这就是 Few-shot 的原理(见 2.7)。
2.5 核心参数:temperature 和 max_tokens
temperature(温度,控制随机性)
取值范围通常 0~2,决定模型输出的"发散程度":
- 越低(如 0):输出越确定、越保守,几乎每次都一样。适合需要稳定、准确的场景(SQL 生成、JSON 输出、抽取)。
- 越高(如 1 以上):输出越随机、越有创意。适合写诗、起名、头脑风暴。
经验法则:要"确定性"的任务(代码、SQL、结构化数据)用低温度;要"创意"的任务(文案、标题)用高温度。
max_tokens(最大输出长度)
限制模型最多输出多少 token。注意它只管输出,不管输入。设小了答案会被截断,设大了浪费钱。
其他参数(top_p、frequency_penalty 等)前期不必深究,需要时查文档即可。
2.6 幻觉:为什么模型会一本正经地胡说
幻觉不是 bug,是 LLM 的"本性"——因为它做的是"预测最像的续写",不是"查证事实"。
典型场景:
- 编造事实:你问"XX 公司的股价",它可能编一个数字。
- 编造引用:你让它列参考文献,它可能编出根本不存在的论文。
- 编造 API:你让它写代码,它可能调用一个不存在的函数(这也是为什么第 3 章要强调"结构化输出 + 人工校验")。
缓解幻觉的几条实用手段(按性价比排序):
- RAG(第 4 章):把真实资料检索出来喂给它,让它"基于资料回答",而不是凭空编。
- 约束输出:让它只输出你给定格式的内容,不给它自由发挥的空间。
- 降低温度:减少随机性,减少"脑补"。
- 让它承认不知道:在 prompt 里写"如果信息不足,直接说'我不知道',不要编造"。
- 人工/程序校验:关键数据(数字、SQL、接口)永远要程序二次验证。
2.7 Prompt 工程:怎么把话说清楚
Prompt(提示词)就是你和模型的"沟通语言"。写 Prompt 没有玄学,核心就一条:把"背景、任务、要求、输出格式"都说清楚,别让模型猜。
下面这套方法,按实用性从高到低排:
方法 1:结构化指令(最常用,先用起来)
一个清晰的 Prompt 通常包含四块:
- 角色:你是谁、擅长什么。
- 背景/上下文:这件事的前因后果、可用信息。
- 任务:要做什么,拆成清晰的步骤。
- 要求/输出格式:怎么输出、有什么约束。
差例子(信息全要模型猜):
帮我写个SQL好例子:
你是一名数据库工程师。
背景:我们有一张收粮入库表 grain_inbound,字段有:id、batch_no(批次号)、
weight_kg(入库重量公斤)、grain_type(粮食品种)、inbound_time(入库时间)。
任务:把用户的需求翻译成一条 MySQL 查询语句。
要求:只输出 SQL 代码,不要解释,不要用代码块包裹。差别一目了然:好例子给了表结构、给了输出约束,模型几乎不会跑偏。
方法 2:Few-shot(给例子)
模型特别擅长"模仿"。你给它 2~3 个"输入→输出"的例子,它就能照葫芦画瓢。
把用户需求翻译成 SQL,规则如下。
示例1:
用户:"查一下玉米的总入库量" → SELECT SUM(weight_kg) FROM grain_inbound WHERE grain_type='玉米'
示例2:
用户:"昨天入库了几批" → SELECT COUNT(*) FROM grain_inbound WHERE DATE(inbound_time)=CURDATE()-INTERVAL 1 DAY
现在处理:
用户:"上个月小麦一共进了多少吨?"给了例子,模型对"该怎么翻"有了明确的参照,准确率大幅提升。
方法 3:思维链(Chain of Thought,CoT)
对于需要推理的复杂任务,加一句"请一步一步思考,先分析再给结论",能让模型多"想"一会儿,显著减少低级错误。
用户问题:某批次玉米入库 5000 公斤,含水率 14%,按标准含水率 12% 折算,
应计多少公斤?
请一步步思考:先算水分差,再算折合重量,最后给出结论和计算过程。注意:现在很多模型(如 DeepSeek 的思考模式)自带"深度思考"能力,会自己输出思考过程。但对于普通 chat 模型,显式加 CoT 仍然有效。
方法 4:约束输出格式
让模型按你需要的格式吐结果,方便程序直接解析:
- JSON:
只输出 JSON,格式为 {"sql": "...", "说明": "..."} - Markdown 表格:
用 Markdown 表格输出,列为:品种、入库量、占比 - 列表:
用要点列出,每条一行
配合第 3 章的"结构化输出"技术,这是让 AI 从"聊天"变成"可用接口"的关键一步。
方法 5:反向提示 / 自我检查
让模型自己检查自己,能再压掉一批错误:
生成 SQL 后,请自查:
1. 字段名是否都存在于给定的表结构中?
2. 是否有 SQL 注入风险?
3. 能否进一步优化?2.8 动手:一个完整的 Prompt 实战
下面用 DeepSeek API 跑一个"会议纪要整理"的完整例子,把上面方法串起来。先确保装了 SDK:
pip install openaiimport os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("DEEPSEEK_API_KEY", "sk-你的key"), # 形如 sk-xxx
base_url="https://api.deepseek.com",
)
# 这是一段原始、杂乱的会议记录
raw_notes = """
老王说禾丰科技智粮系统这周要上线,卡在称重数据对不上,
小李说可能是地磅的串口数据有延迟,得加个重试。
张姐问财务对账功能啥时候能好,老王说下周。
最后大家定了:本周先修称重,下周财务对账,上线推到月底。
"""
response = client.chat.completions.create(
model="deepseek-v4-flash",
temperature=0.3, # 纪要要准确,温度调低
messages=[
{
"role": "system",
"content": (
"你是一名项目助理,擅长把杂乱的口语会议记录整理成结构化纪要。\n"
"要求:\n"
"1. 用 Markdown 输出,分「议题」「结论」「待办」三个部分;\n"
"2. 待办用列表,每条格式为「事项 — 负责人 — 截止时间」;\n"
"3. 只写记录里明确提到的信息,没有的不要脑补。"
),
},
{
"role": "user",
"content": f"请整理以下会议记录:\n{raw_notes}",
},
],
)
print(response.choices[0].message.content)这段代码里值得注意的点:
temperature=0.3:整理类任务要准确,温度压低。- 规则全部写进
system:让模型从第一条回复就守规矩。 - 明确要求"不要脑补":直接对抗幻觉。
2.9 本章小结与练习
你该记住的:
- LLM 是"预测下一个 token",所以会幻觉,所以要用 RAG、约束、校验来兜底。
- token 是一切计费和长度限制的单位;上下文窗口是模型的短期记忆上限。
- system 定规则、user 提需求、assistant 记录/示范。
- 确定性任务低温度,创意任务高温度。
- 写 Prompt 的四件套:角色、背景、任务、输出格式。
练习(照做才算学会):
- 用上面的代码,把
raw_notes换成你自己的一段工作记录,跑通并看输出。 - 写一个 Prompt,让模型把你一句话的需求翻译成 SQL,并对比"有表结构说明"和"没有表结构说明"两种写法的输出差异。
- 故意问模型一个它不知道的冷门公司数据,观察它会不会编造,然后再加一句"不知道就说不知道",看差异。
下一章:把模型调用的"工程细节"吃透——返回结构、流式输出、异常处理、结构化输出,然后做出第一个真正的工具。