# CoT 思维链演示项目 — 学生阅读指南
## 一、这个项目是做什么的?
这是一个**真实调用阿里云百炼大模型**的教学演示项目,不是写死的假答案,也不是本地模拟。
程序会对**同一道题**分别用三种提示词策略去问 `qwen-plus`,让你亲眼看到:
| 对比维度 | 无 CoT | 有 CoT |
|----------|--------|--------|
| 输出形式 | 往往只有一行答案 | 有「第一步 / 第二步…」推理链 |
| 复杂题准确率 | 容易跳步、答错 | 分步计算,更不容易错 |
| 过程是否可检查 | 看不到推理过程 | 每一步都能审查、纠错 |
**课堂推荐先看第 1 题**(`9.9 和 9.10 谁大?`),无 CoT 容易答错,有 CoT 容易答对,对比最明显。
---
## 二、是不是真实运行?
**是的,100% 真实运行。**
每次你看到的「模型回复」,都是程序通过网络请求阿里云百炼接口得到的实时结果:
```
你的电脑 → dashscope SDK → 百炼云端 qwen-plus → 返回真实生成文本
```
证据:
1. 需要有效的 `DASHSCOPE_API_KEY`,否则会报错,无法运行
2. 每次调用都会消耗百炼账号的 Token(可在百炼控制台查看用量)
3. 同一道题多次运行,措辞可能略有不同(但 `temperature=0` 已尽量保持稳定)
4. 断网或 Key 无效时,程序会抛出 API 错误,不会假装成功
本项目**没有**以下东西:
- 没有写死的「假回复」
- 没有 `if question == xxx: return "4"` 这类硬编码
- 没有用本地规则引擎代替大模型
---
## 三、项目是怎么构建的?
### 3.1 目录结构
```
cot_demo/
├── main.py # 【入口】解析命令行、组织演示流程、打印对比小结
├── demo.py # 【核心】三种 CoT 模式的 messages 组装与调用
├── prompts.py # 【提示词】无CoT / 零样本CoT / 少样本CoT 的 system 和 user 模板
├── questions.py # 【题库】3 道经典演示题及参考答案
├── llm_client.py # 【API层】封装 dashscope.Generation.call,真正发网络请求
├── config.py # 【配置】模型名、温度、种子、API Key 读取
├── display.py # 【展示】终端输出格式化、项目说明打印
├── run.bat # 双击运行(默认第 1 题)
├── run_all.bat # 双击运行全部 3 题
├── requirements.txt # 依赖:dashscope
└── docs/
└── 项目说明.md # 本文件
```
### 3.2 模块分工(谁负责什么)
| 文件 | 职责 | 学生应关注什么 |
|------|------|----------------|
| `config.py` | 读取系统环境变量 `DASHSCOPE_API_KEY`,设置模型参数 | API Key 从哪来、为什么用 `qwen-plus` |
| `llm_client.py` | 调用 `dashscope.Generation.call()` 发请求 | **真正联网的地方**,所有模式最终都走这里 |
| `prompts.py` | 存放三种模式的提示词文本 | **改提示词就在这里**,对比实验的核心 |
| `questions.py` | 存放演示题目 | 可换成自己的题做实验 |
| `demo.py` | 把 prompts + question 拼成 messages 列表 | 理解 system/user/assistant 怎么组 |
| `main.py` | 控制运行顺序、提取答案、判断对错 | 理解一整道题的演示流程 |
| `display.py` | 美化终端输出 | 可忽略,不影响核心逻辑 |
### 3.3 设计原则
1. **提示词与代码分离**:改 CoT 效果主要改 `prompts.py`,不用动 API 调用逻辑
2. **一种 API 封装**:三种模式共用 `llm_client.call_llm()`,保证对比公平(同一模型、同一参数)
3. **参数稳定**:`temperature=0`、`seed=42`,课堂演示尽量可复现
---
## 四、程序运行流程(一步一步)
以 `python main.py`(默认第 1 题)为例,完整执行顺序如下:
```
第 0 步:启动 main.py
↓
第 1 步:check_env()
读取 os.getenv("DASHSCOPE_API_KEY")
确认系统环境变量里已配置百炼密钥
↓
第 2 步:print_intro()
打印 CoT 概念说明、三种模式介绍、项目是否真实运行
↓
第 3 步:run_one_question(1)
从 questions.py 取出第 1 道题
↓
第 4 步:依次执行三种模式(每种模式 = 1 次真实 API 调用)
│
├─ 模式 A:demo.run_direct()
│ prompts.py 取 SYSTEM_DIRECT + 题目
│ 组装 messages → **先打印【发送给模型的提示词】**
│ → llm_client.call_llm() → 百炼 API
│ 打印【模型回复】→ 提取最终答案 → 判断对错/是否有分步
│
├─ 模式 B:demo.run_zero_shot_cot()
│ prompts.py 取 SYSTEM_ZERO_SHOT_COT + 题目 +「请一步一步思考」
│ 组装 messages → llm_client.call_llm() → 百炼 API
│ 打印回复 → 提取最终答案 → 判断对错/是否有分步
│
└─ 模式 C:demo.run_few_shot_cot()
prompts.py 取 SYSTEM_FEW_SHOT_COT
+ 示例 user(罗杰网球题)
+ 示例 assistant(带推理链的标准答案)
+ 真正要答的新题目
组装 messages → llm_client.call_llm() → 百炼 API
打印回复 → 提取最终答案 → 判断对错/是否有分步
↓
第 5 步:打印「本题对比小结」
汇总 A/B/C 的提示词结构差异 + 最终答案、正确性、是否有分步推理
↓
第 6 步:演示结束
```
**跑 1 道题 = 3 次 API 调用;跑全部 3 题 = 9 次 API 调用。**
---
## 五、三种模式分别怎么工作?
### 模式 A:无 CoT(直接要答案)
**messages 结构:**
```python
[
{"role": "system", "content": "只输出一行最终答案,禁止写推理过程..."},
{"role": "user", "content": "9.9 和 9.10 谁大?"},
]
```
**作用:** 模拟「只要结果、不要过程」的提问方式。复杂题容易跳步出错。
---
### 模式 B:零样本 CoT(Zero-shot CoT)
**messages 结构:**
```python
[
{"role": "system", "content": "必须先写推理过程,按第一步/第二步组织..."},
{"role": "user", "content": "9.9 和 9.10 谁大?\n\n请一步一步思考..."},
]
```
**作用:** 不给示例,只靠提示词要求模型自己展开推理链。对应课程里的「给模型思考时间」。
---
### 模式 C:少样本 CoT(Few-shot CoT)
**messages 结构:**
```python
[
{"role": "system", "content": "请模仿示例的推理格式..."},
{"role": "user", "content": "罗杰有 5 个网球..."}, # 示例题
{"role": "assistant", "content": "第一步:...\n最终答案:11"}, # 示例推理链
{"role": "user", "content": "9.9 和 9.10 谁大?"}, # 真正要答的题
]
```
**作用:** 用「示例问题 + 示例推理」教会模型输出格式,再让它答新题。对应课程的「文本参考 / Few-shot」。
---
## 六、API 调用层详解(llm_client.py)
这是**唯一真正联网**的函数:
```python
response = dashscope.Generation.call(
api_key=API_KEY, # 从 os.getenv("DASHSCOPE_API_KEY") 读取
model="qwen-plus", # 百炼上的通义千问 Plus
messages=messages, # 上面三种模式拼好的对话列表
temperature=0, # 低随机性,结果更稳定
seed=42, # 固定种子,尽量可复现
result_format="message", # 返回标准 message 格式
)
return response.output.choices[0].message.content
```
如果 `status_code != 200`,程序会报错退出,不会返回假数据。
---
## 七、怎么运行?
### 前置条件
1. 已安装 Python 3.8+
2. 已执行 `pip install -r requirements.txt`
3. Windows 系统环境变量中已配置 `DASHSCOPE_API_KEY`(无需每次在终端手动设置)
### 网页版(推荐课堂演示)
```powershell
python web_app.py
# 或双击 run_web.bat
```
浏览器打开:**http://127.0.0.1:8765**
页面会实时展示:
1. 运行环境(模型、API Key 状态、是否真实调用)
2. 运行过程三步进度(模式 A → B → C)
3. 三列卡片:每列显示完整提示词 + 模型回复
4. 底部对比小结表(提示词差异 + 结果差异)
技术说明:前端通过 **SSE(Server-Sent Events)** 订阅 `/api/stream/<题号>`,
后端每完成一种模式就推送一次事件,学生能清楚看到「正在调用 API → 返回结果」的全过程。
### 终端版
```powershell
python main.py # 默认第 1 题
python main.py --all # 全部 3 题
python main.py --only 2 # 指定第 2 题
```
或直接双击 `run.bat` / `run_all.bat`。
---
## 八、建议的学习顺序
1. **先运行**:`python main.py`,看终端输出的 A/B/C 对比
2. **再读** `prompts.py`:对比三种提示词差在哪
3. **然后读** `demo.py`:看 messages 是怎么拼的
4. **最后读** `llm_client.py`:确认是真实 API 调用
5. **动手改**:试着改 `SYSTEM_ZERO_SHOT_COT` 里的措辞,重新运行看效果变化
---
## 九、常见问题
**Q:为什么模式 A 有时也写了很多字?**
A:早期版本提示词不够严格,模型会「偷偷推理」。当前版已要求「只输出一行」,模式 A 应只有一行答案。
**Q:为什么有时无 CoT 也答对了?**
A:简单题(如第 2、3 题)模型直接心算也能对。CoT 的价值在复杂题(如第 1 题)更明显。
**Q:运行报错「未读取到 DASHSCOPE_API_KEY」?**
A:检查 Windows 系统环境变量是否配置,配置后需**重新打开终端**。
**Q:和课程里的 OpenAI 库写法一样吗?**
A:思路一样(都是 `messages` 列表 + `system/user/assistant`),只是本项目按课堂要求改用 **DashScope 官方 SDK** 调用百炼。