---
title: "W08 上机练习二：上市公司年报的向量检索与召回（RAG）"
---

# 🧪 练习二：上市公司年报的向量检索与召回（RAG）

[上机导航](https://ai.lingnan.top/materials/2026-autumn/w08/lab-manual) · [W08 材料入口](https://ai.lingnan.top/materials/2026-autumn/w08)

开始之前先看[上机导航](https://ai.lingnan.top/materials/2026-autumn/w08/lab-manual)“开始之前”一节：解压留底、隐藏文件夹、工作空间选到哪一层。

## 任务

`02_银行年报RAG/` 是一个做好的检索增强生成（RAG）小系统：资料是 30 家 A 股上市银行 2025 年年报的文字部分；切块、换成向量这两步已经算好，存在 `向量库/` 里，共 7638 段。你要做的是第四、五步：检索，再让它只凭检索结果回答。

贯穿这个练习的是一个问题：“农业银行 2025 年的净息差是多少？出处在哪一页？”这里有个坑。净息差和净利差差一个字，不是一回事：净息差是利息净收入除以生息资产平均余额，净利差是生息资产收益率减付息负债付息率。农行的年报里一次都没写“净息差”这三个字，它写的是“净利息收益率”。

你会看到：按字找，找不到农行；按意思找，找到了，但找回来的段落里两个数挨在一起，像不等于对。最后再问一个比大小的问题，看它会不会承认检索答不了。

### 🎬 讲解录播：净息差还是净利差，用 RAG 与不用

<video controls preload="metadata" playsinline style="display:block;width:100%;aspect-ratio:16/9;background:#000;border-radius:10px" src="/course-materials/2026-autumn/w08/lab-ex2.mp4"></video>

录播 5 分 33 秒，带讲解和字幕，播放器右下角可以全屏。

## 准备

材料包 `02_银行年报RAG/`：

| 文件 | 内容 |
| --- | --- |
| `CLAUDE.md`、`AGENTS.md` | 规则文件，两份同内容：回答只用 `检索.py` 打印出来的段落，每个数写出处，没找到就直说 |
| `问题清单.md` | 四个问题，下面几步用的就是这里的原话 |
| `资料库/` | 30 家银行年报的文字部分，每份按原报告页码分节 |
| `向量库/` | 切好的段落和算好的向量；另存了问题清单里四个问题、十几个常用词的向量 |
| `检索.py` | 给问题，返回最相关的几段和出处。默认按意思；`--按字` 按字打分；`--公司 农业银行` 只在这一家里找；`-k 8` 改取几段 |
| `相似度.py` | 几个词两两算余弦相似度，`--细节` 打出计算过程 |
| `向量化.py`、`嵌入接口.py` | 切块、向量化和调接口的脚本，这次不用改 |
| `硅基流动key.txt` | 放 API key 的地方，这次可以不填 |
| `招商银行2026半年报节选.md` | 选做：有 key 时用 `向量化.py --加` 追加进向量库 |

**不填 key 也能做**：问题清单里四个问题和常用词的向量都预先算好了，只要问的是清单里的原话（一字不差，连问号），检索就能离线跑。换一种问法，脚本会提示“预先算好的问题里也没有”，这时让它改回清单原话。

**要有 Python**：脚本只用 Python 自带的库，不用装包。打开终端输入 `python --version`，显示 3.8 以上即可；Mac 上没有 `python` 命令、只有 `python3` 的，在第一条指令后面加一句“本机用 python3 跑脚本”。

工作空间选 `02_银行年报RAG` 这一层（见“开始之前”）。

## 第一步：按意思找

**做什么**：新建对话，发送：

```txt
农业银行 2025 年的净息差是多少？出处在哪一页？只用 检索.py 找段落，照检索结果答，每个数写出处。
```

**做对了会看到**

- 它运行 `python 检索.py "农业银行 2025 年的净息差是多少？"`（Claude Code 里是一行 `Bash(python 检索.py ...)`，按 `Ctrl + O` 展开看输出）。
- 检索结果的前两段都是农行年报，第 26 页（余弦 0.777）和第 25 页（0.775）。第 25 页那段原文是：“净利息收益率 1.28%，净利差 1.16%，分别下降 14 个和 11 个基点”。第 26 页是半张表，指标名和数字分在不同行。
- 回答写 1.28%，说明原文叫“净利息收益率”，出处是农业银行 2025 年年度报告第 25 页（或第 25、26 页）；并且分清 1.16% 是净利差，不是净息差。

### ✅ 自查

- [ ] 它跑了 `检索.py`，没有绕开脚本直接去翻 `资料库/`（过程记录里没有直接读年报文件）
- [ ] 每个数都写了哪家银行、原报告第几页，照检索结果那一行抄
- [ ] 1.28% 和 1.16% 分清了，没有把净利差当净息差

它要是绕开检索脚本直接翻资料库，Claude Code 里按 `Esc` 打断，提醒它“只用 检索.py”。

## 第二步：按字找，再加公司条件

**做什么**：同一个对话，接着发送：

```txt
同一问，加 --按字 再查一次，只列前 5 段的银行和页码。再加上 --公司 农业银行，按字、按意思各查一次，看第 25 页那段各排第几。最后告诉我：按字为什么找不到农行的净息差？
```

**做对了会看到**

- 按字、不限公司：前 5 段是上海银行、民生银行、华夏银行、建设银行、渝农商行，一段农行都没有。
- 加上 `--公司 农业银行`：按意思时第 25 页那段排第 2；按字时前 5 段是第 12、10、49、95 页等，第 25 页那段不在其中。
- 它说得出原因：按字是数问题里相邻两个字在段落里出现几次，“净息”“息差”在农行年报里一次都没出现；农行写的是“净利息收益率”。叫法不同，意思一样，要靠向量才找得回来。

### ✅ 自查

- [ ] 按字、不限公司的前 5 段里没有农行，你自己在输出里核过
- [ ] 说得出按字为什么找不到农行的净息差

## 第三步：像不等于对

**做什么**：接着发送：

```txt
用 相似度.py 算一下 净息差、净利息收益率、净利差 三个词两两的相似度。“净息差”离哪个更近？这对刚才的回答意味着什么？
```

**做对了会看到**

- 它运行 `python 相似度.py 净息差 净利息收益率 净利差`，得到：净息差—净利差 0.892，净息差—净利息收益率 0.812。
- 在向量上，“净息差”离“净利差”比离它的同义词“净利息收益率”还近。它说得出：按意思找回来的段落里两个数挨在一起，找得像，不等于挑的数对；答案要回到原文按定义核。

### ✅ 自查

- [ ] 两个相似度数字是脚本算出来的（过程记录里有 `相似度.py`），不是它估的
- [ ] 说得出“找得像，不等于挑的数对”，以及答案要回原文按定义核

## 第四步：问一个检索答不了的

**做什么**：接着发送：

```txt
30 家银行里，2025 年末不良贷款率最低的是哪家？只凭 检索.py 的结果答；如果检索结果不够回答，直说不够，说明缺什么。
```

**做对了会看到**

- 它用问题清单的原话检索，前 5 段只落在 4 家银行（平安银行占两段，另有邮储、农业、杭州银行）。
- 它说明：前 5 段覆盖不了 30 家，凭这些比不出“最低”，没有硬答出一个银行名。做得好的还会提出换办法，比如用 `--公司` 一家一家查，或者回资料库逐份核。

### ✅ 自查

- [ ] 比大小那一问，它说明了检索不够、缺什么，没有硬答出一个银行名
- [ ] 它提出的换办法说得通，且没有自己动手绕开检索脚本

## 选做

- 问清单第二问“招商银行 2025 年末信用卡贷款不良率是多少？”，看检索回来的段落里有没有切块断开的表格：后半段只剩数字，看不出是哪一项、哪一年。
- 有硅基流动 API key 的，把 key 粘进 `硅基流动key.txt`（替换那一行说明），就能问自己的问题、算任意词的相似度；再用 `python 向量化.py --加 招商银行2026半年报节选.md` 追加一份新资料，问一个 2026 年的问题。key 只放在这个文件里，不贴进对话。
- 问第四问“哪几家银行在年报里写了用大模型做风控？”，看前 5 段能不能回答“哪几家”这种列名单的问题。

## 想一想

- 练习一的知识库是“问的时候让模型自己翻目录”，练习二是“问的时候让程序按像不像挑段落”。同一个“净息差”的问题，放进练习一那种带索引的文件夹，它会怎么找？哪种更容易错，错在哪？
- 比大小、列名单这类问题，RAG 为什么天生不擅长？换成什么做法更合适？

## 常见问题

**报错“没有设 API key，预先算好的问题里也没有……”。** 问的不是清单原话。让它照 `问题清单.md` 一字不差重跑，问号也要一样。

**报错 `python: command not found` 或打开了微软商店。** 本机的 Python 命令叫 `python3`，或者没装 Python。告诉它“用 python3 跑”；没装的按 W01 环境安装手册装好。

**Windows 上输出是乱码。** 让它在 PowerShell 里先运行 `chcp 65001` 再跑脚本，或者改用 VS Code 的终端。

**它每次跑脚本都要我确认。** Claude Code 读 `.claude/settings.json` 里的放行规则，只放行 `python 检索.py` 等三条；它用的是 `python3` 或加了别的参数时还会问，看清命令再允许。WorkBuddy 按它自己的权限设置。

**它打开了 `硅基流动key.txt`。** 规则文件写了不读这份文件，Claude Code 的设置里也禁了读取。它要是试图读，拒绝并提醒它；这份文件这次可以不填。
