第01章 · LLM 基础 —— 与模型对话的第一步
目标:理解 LLM API 的核心概念,能用 Python/TypeScript 发出第一个请求并读懂返回值。 这是整个教程的地基——后续 16 章的 Agent 循环、工具调用、记忆系统全都建立在此之上。
TL;DR
30 秒速读:LLM API 就是一个 HTTP 接口,你发 messages 数组,它返回补全文本,每次调用完全独立,模型不会记住任何历史。
如果只记一件事:messages 数组里的 role(system/user/assistant)决定了"谁在说话",system prompt 是你控制模型行为的最强杠杆。
1. LLM API 是什么?
大语言模型(LLM)本身是一个运行在服务器上的数学函数——你给它一段文字(prompt),它返回一段补全(completion)。 我们通过 HTTP API 调用它,就像调用任何 REST 接口一样。
你的代码 → HTTP POST → LLM 服务器 → HTTP Response → 拿到回答
好消息:几乎所有主流 LLM 提供商(OpenAI、DeepSeek、Qwen、Ollama)都兼容 同一套请求格式—— OpenAI Chat Completions API。学一次,到处能用。
请求的核心结构
{
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "你是一个任务助手"},
{"role": "user", "content": "帮我总结一下今天的待办"}
]
}
三个关键字段:
- model:用哪个模型(不同模型能力/价格不同)
- messages:对话历史,是一个有序数组
- role:每条消息的"说话人"——这是理解 LLM 的关键
2. 角色模型(Roles)
Chat Completions API 有三个角色,它们共同构成一次"对话":
| 角色 | 谁说的 | 作用 | 举例 |
|---|---|---|---|
system |
开发者(你) | 定义 AI 的"人格"和行为边界 | "你是一个任务助手,只回答与待办相关的问题" |
user |
终端用户 | 发送实际问题或指令 | "帮我列一下今天的待办" |
assistant |
AI 模型 | 模型的回答 | "好的,以下是你的待办清单:1. …" |
关键理解:system 消息不是"系统自动的",而是你写的。
它相当于给 AI 演员一份"角色说明书"——同一段 user 消息,不同的 system prompt 会得到截然不同的回答。
🎭 贯穿本教程的「任务助手 Agent」 就是通过 system prompt 定义的: 它被设定为一个"简洁、高效的任务管理助手",只回答任务相关的问题。
3. Tokens 与计费
LLM 不是按"字数"收费,而是按 token 收费。
什么是 Token?
Token 是模型处理文本的最小单位。大致规律: - 英文:1 个单词 ≈ 1–1.5 个 token - 中文:1 个汉字 ≈ 1.5–2 个 token - 代码:变量名、符号各占 token
"Hello world" → 2 tokens
"你好世界" → 2–3 tokens
"def hello():" → 4 tokens
为什么 Token 重要?
- 计费:API 按输入 token + 输出 token 分别计价
- 上下文窗口:模型能"看到"的 token 总量有限(下一节)
- 速度:输出 token 越多,等待越久
Token 用量在哪里看?
API 响应里有 usage 字段,精确告诉你用了多少 token:
{
"usage": {
"prompt_tokens": 25, // 输入(你的 messages)用了多少 token
"completion_tokens": 48, // 输出(模型的回答)用了多少 token
"total_tokens": 73 // 合计
}
}
本章代码会打印这个字段,让你亲眼看到每次调用的 token 消耗。
4. 上下文窗口(Context Window)
每个模型有一个上下文窗口限制——即它一次能处理的最大 token 总数(输入 + 输出)。
| 模型 | 上下文窗口 | 约等于 |
|---|---|---|
| GPT-4o-mini | 128K tokens | ~10 万字中文 |
| DeepSeek-Chat | 128K tokens | ~10 万字中文 |
| Qwen-Plus | 128K tokens | ~10 万字中文 |
| GPT-4o | 128K tokens | ~10 万字中文 |
超了会怎样?
如果你的 messages 总 token 超过窗口限制: - API 会返回 400 错误("context length exceeded") - 不会"自动截断"——你得自己处理
这对 Agent 意味着什么?
Agent 需要把整个对话历史塞进 messages。随着对话变长,历史消息会占满窗口。 解决方案(第05章讲):摘要、截断、滑动窗口。
⚠️ 反模式:以为模型"记住"了之前的对话。其实每次请求都是独立的—— 你能传多少历史消息,完全取决于你手动塞了多少进 messages 数组。
5. 温度与采样参数(Temperature & Sampling)
temperature 控制模型输出的"随机性":
| temperature | 行为 | 适用场景 |
|---|---|---|
0.0 |
几乎确定性,每次输出相同 | 代码生成、数据提取、分类 |
0.3 |
轻微随机 | 日常问答、摘要 |
0.7 |
中等随机 | 创意写作、头脑风暴 |
1.0 |
高随机性 | 诗歌、故事、发散思维 |
原理(简化版)
模型在每一步不是直接选"最可能的下一个 token",而是从一个概率分布中采样。 温度越高,概率分布越"平坦"——低概率的 token 也有机会被选中,输出就更多样。
temperature = 0.0 → 总是选概率最高的 token → 确定性输出
temperature = 1.0 → 按原始概率采样 → 多样化输出
本章代码会让同一个问题分别用 0.0 和 1.0 跑一次,让你对比差异。
其他采样参数(了解即可)
top_p(nucleus sampling):另一种控制随机性的方式,和 temperature 二选一调。max_tokens:限制输出最大 token 数,防止单次调用太贵。stop:指定停止词,模型遇到就停(比如"\n")。
本章只用 temperature,其余参数后续章节按需引入。
6. 流式 vs 非流式响应
非流式(默认)
API 等模型生成完全部 token 后,一次性返回完整响应。
发送请求 → 等待 3 秒 → 收到完整回答
优点:简单,拿到完整 JSON。 缺点:用户盯着空白屏幕等 3 秒,体验差。
流式(stream=True)
API 边生成边返回,每生成几个 token 就发一个 chunk。
发送请求 → 收到 "你" → 收到 "好" → 收到 "," → … → 收到 [DONE]
优点:用户立刻看到输出逐字出现,体验好。 缺点:代码稍复杂,需要处理 chunk 流。
什么时候用哪个?
| 场景 | 推荐 |
|---|---|
| 脚本/批处理 | 非流式(简单) |
| 对话 UI/聊天机器人 | 流式(用户体验) |
需要拿 usage 统计 |
非流式(流式时 usage 可能不全) |
| Agent 循环内部调用 | 非流式(代码简单,不需要实时显示) |
本章两种都演示,后续章节根据场景选择。
7. 反模式与常见误解
❌ "模型会记住之前的对话"
真相:每次 API 调用都是独立的。模型没有任何"记忆"。
你能传多少历史,取决于你手动把多少消息塞进 messages 数组。
→ 多轮对话的记忆管理是第05章的内容。
❌ "模型会精确算术"
真相:LLM 是概率模型,不是计算器。它可能算对 2+2,但对 123456 × 789 大概率出错。
需要精确计算时,应该让模型调用工具(如 Python 解释器)。
→ 工具调用是第03章的内容。
❌ "上下文窗口越大越好"
真相:大窗口不等于高质量。塞太多无关信息反而会稀释关键内容("lost in the middle" 问题)。 好的 Agent 需要上下文工程——精选哪些信息该进窗口。 → 上下文管理是第05、14章的内容。
❌ "temperature=0 就是100%确定"
真相:即使 temperature=0,模型在底层浮点运算中仍有微小随机性(取决于硬件/实现)。 但实际差异极小,可以认为是"确定性"的。
常见错误
概念懂了,实际写代码还是会踩坑。这些是初学者最常犯的错误。
| 错误 | 症状 | 解决 |
|---|---|---|
| 忘记传 API Key | AuthenticationError: Invalid API key 或 HTTP 401 |
检查 .env 文件中 OPENAI_API_KEY 是否填写,确认没有多余空格 |
| 上下文窗口超限 | context_length_exceeded HTTP 400 错误 |
检查 messages 总 token 数,用响应中的 usage 字段监控,必要时截断历史 |
| temperature 设错场景 | 分类任务每次结果不同,或创意写作千篇一律 | 代码生成/分类用 0.0,创意任务用 0.7 到 1.0 |
| 流式响应没处理 [DONE] | 程序卡住不退出,或拼接出乱码 | 检查 chunk 是否为 [DONE] 再停止循环,拼接时用 chunk.choices[0].delta.content |
8. 本章代码结构
01-llm-basics/
├── README.md ← 你在这里
├── python/
│ ├── main.py ← Python 演示代码
│ └── requirements.txt
├── typescript/
│ └── main.ts ← TypeScript 对等实现
└── exercises/
└── README.md ← 练习与参考答案
运行
# Python
cd ai-agent/01-llm-basics
python3 python/main.py
# TypeScript
npx tsx typescript/main.ts
两者输出内容等价:单轮对话 → token 用量 → 温度对比 → 流式输出。
9. 知识检查清单
学完本章,你应该能回答:
- [ ] Chat Completions API 的
messages数组里有哪些角色?各自作用? - [ ]
prompt_tokens和completion_tokens分别指什么? - [ ] 上下文窗口超了会怎样?
- [ ]
temperature=0和temperature=1的区别? - [ ] 流式响应的适用场景?
- [ ] 为什么模型不会"记住"之前的对话?
下一章:第02章 · Prompt 工程 —— 学会用 system prompt 精确控制模型行为。