# 06 · 工具选型与使用说明
本文专门回答三个问题:
1. **项目里到底有哪些工具?是真实的吗?**
2. **为什么选这些工具?(选型思路)**
3. **怎么用?(学生演示、模型调用、开发者扩展)**
---
## 一、现有文档分别写什么?
| 文档 | 侧重点 | 和本文的关系 |
|------|--------|--------------|
| [02-工具清单与官方链接.md](./02-工具清单与官方链接.md) | 每个工具的 **API 原链接**、请求参数、返回示例 | 本文偏「为什么选、怎么用」,02 偏「链接与接口细节」 |
| [04-运行流程详解.md](./04-运行流程详解.md) | 点击运行后 **整体流程** | 本文聚焦「工具」这一层 |
| [05-课堂演示与自学指南.md](./05-课堂演示与自学指南.md) | 讲课顺序、练习题 | 本文是工具专题讲义 |
**建议**:先读本文建立整体认识,需要查官方 URL 时翻 02。
---
## 二、工具是真实的吗?实际调用了吗?
**结论:是真实的,且在 ReAct 模式下会实际执行。**
| 工具 | 真实吗 | 何时执行 | 数据从哪来 |
|------|--------|----------|------------|
| DashScope `qwen-plus` | 是 | 每次运行必调 | 阿里云百炼 |
| `get_current_datetime` | 是 | 模型发出 `tool_calls` 时 | 本机系统时钟 |
| `calculate` | 是 | 模型发出 `tool_calls` 时 | 本地 `safeCalculate()` |
| `get_weather` | 是 | 模型发出 `tool_calls` 时 | Open-Meteo HTTP 请求 |
| `get_exchange_rate` | 是 | 模型发出 `tool_calls` 时 | Frankfurter HTTP 请求 |
**不是假数据**:天气、汇率来自公开 API;计算结果是程序算出来的;时间是系统时间。
**不会乱调**:只有模型在 Function Calling 里返回 `tool_calls` 时,`react-loop.ts` 才会调用 `executeTool()`。普通模式故意不传 `tools`,所以**永远不会**走工具。
---
## 三、工具选型思路(为什么选这四个?)
### 3.1 选型总原则
教学演示选工具,要满足 **「对比明显、零门槛、可验证」**:
| 原则 | 含义 | 本项目怎么做 |
|------|------|--------------|
| 对比明显 | 无工具时模型容易错 | 大数计算、实时天气、汇率、当前时间 |
| 零门槛 | 学生只需 DashScope Key | 天气/汇率 API 免费、无需再注册 |
| 可验证 | 结果能核对 | 计算器可手算;天气可对照手机天气 App |
| 稳定 | 课堂少翻车 | 选文档完善、CORS 友好的公开 API |
| 覆盖典型场景 | 代表常见 Agent 能力 | 计算 / 检索 / 时间 / 结构化查询 |
### 3.2 为什么没选搜索、数据库、Coze 插件?
| 未选方案 | 原因 |
|----------|------|
| 网页搜索 API | 多数要额外 Key;结果格式不稳定,不利于课堂讲解 |
| 假天气(随机数) | 无法体现「Observation 必须真实」 |
| 后端 Python 执行 | 课程统一 React + 浏览器演示,工具放前端更直观 |
| 更多工具 | 四个已覆盖主要类型,太多反而分散注意力 |
### 3.3 四个工具各自「代表」什么能力
```mermaid
mindmap
root((ReAct 工具箱))
精确计算
calculate
模型算术弱点
实时检索
get_weather
外部事实查不到
结构化查询
get_exchange_rate
数值型 API 结果
环境感知
get_current_datetime
模型不知道此刻
```
| 工具 | 代表能力 | 无工具时典型问题 |
|------|----------|------------------|
| `calculate` | 确定性计算 | `12345×6789` 算错 |
| `get_weather` | 实时外部事实 | 编造「深圳今天晴 28°C」 |
| `get_exchange_rate` | 动态数据查询 | 用过时汇率 |
| `get_current_datetime` | 当前环境状态 | 不知道现在几点、星期几 |
### 3.4 外部 API 为什么选 Open-Meteo 和 Frankfurter?
| API | 选中理由 | 官方入口 |
|-----|----------|----------|
| **Open-Meteo** | 免费、无需 Key、支持中文城市、浏览器可直连(CORS) | https://open-meteo.com/ |
| **Frankfurter** | 免费、无 Key、汇率数据清晰、请求简单 | https://www.frankfurter.app/ |
---
## 四、每个工具怎么用?
### 使用方式分三层
```mermaid
flowchart LR
A[① 学生在页面提问] --> B[② 模型选择工具]
B --> C[③ executeTool 执行]
C --> D[④ 结果展示在 Observation]
```
---
### 工具 1:`calculate`(数学计算)
**中文名**:数学计算
**代码**:`src/lib/tools.ts` → `safeCalculate()`
#### 什么时候会被调用?
用户问题包含**精确算术**且数字较大或易错时,例如:
- 「请计算 12345 × 6789 等于多少?」
#### 模型传入的参数
```json
{ "expression": "12345 * 6789" }
```
支持:`+ - * / ^ % ( )`,`×` 需在模型侧转为 `*`。
#### 返回示例
```text
12345 * 6789 = 83810205
```
#### 你怎么验证它真执行了?
1. 看右侧 **Action** 是否出现 `calculate`
2. 看 **Observation** 数字是否等于手算结果
3. 命令行:`node scripts/test-tools.mjs`
---
### 工具 2:`get_weather`(查询天气)
**中文名**:查询天气
**代码**:`src/lib/tools.ts` → `fetchWeather()`
#### 什么时候会被调用?
问**某城市当前天气**,例如:
- 「深圳现在的天气怎么样?」
#### 模型传入的参数
```json
{ "city": "深圳" }
```
#### 背后真实请求(两步)
1. 地理编码:
`https://geocoding-api.open-meteo.com/v1/search?name=深圳&...`
2. 天气预报:
`https://api.open-meteo.com/v1/forecast?latitude=...&longitude=...`
#### 返回示例
```text
深圳(中国)当前天气:多云
温度:25.2°C
湿度:78%
风速:12.5 km/h
```
#### 你怎么验证?
- 浏览器 F12 → Network → 过滤 `open-meteo`
- 对比手机天气 App 温度是否接近(不必完全一致,数据源可能不同)
#### 注意
城市名要常见(深圳、北京、上海);生僻地名可能「未找到城市」。
---
### 工具 3:`get_exchange_rate`(汇率换算)
**中文名**:汇率换算
**代码**:`src/lib/tools.ts` → `fetchExchangeRate()`
#### 什么时候会被调用?
涉及**货币兑换、汇率**,例如:
- 「100 美元现在能换多少人民币?」
#### 模型传入的参数
```json
{
"from_currency": "USD",
"to_currency": "CNY",
"amount": "100"
}
```
`amount` 可省略,默认按 1 换算。
#### 背后真实请求
`https://api.frankfurter.app/latest?from=USD&to=CNY`
#### 返回示例
```text
汇率日期:2026-06-08
1 USD = 6.7819 CNY
100 USD = 678.1900 CNY
```
#### 你怎么验证?
- Network 里看到 `frankfurter.app` 请求
- 汇率日期应为近期工作日
---
### 工具 4:`get_current_datetime`(获取时间)
**中文名**:获取时间
**代码**:`src/lib/tools.ts` → `get_current_datetime` 分支
#### 什么时候会被调用?
问**现在几点、星期几、今天日期**,例如:
- 「现在北京时间是几点?星期几?」
#### 模型传入的参数
```json
{ "timezone": "Asia/Shanghai" }
```
#### 返回示例
```text
时区 Asia/Shanghai 的当前时间:2026/06/09 星期二 14:30:25
```
#### 你怎么验证?
对照电脑右下角系统时间。
#### 说明
此工具**不访问互联网**,用的是运行 demo 的那台电脑/浏览器的时钟。
---
## 五、在页面上怎么用?(学生操作步骤)
### 步骤 1:启动项目
```bash
cd 06_代码/Day_02/react-demo
npm run dev
```
打开 http://localhost:5173 ,填入 DashScope API Key。
### 步骤 2:选对问题
点击带 **「需工具」** 标签的示例,或自己输入同类问题:
| 想测哪个工具 | 推荐问题 |
|--------------|----------|
| calculate | 请计算 12345 × 6789 等于多少? |
| get_weather | 深圳现在的天气怎么样? |
| get_exchange_rate | 100 美元现在能换多少人民币? |
| get_current_datetime | 现在北京时间是几点?星期几? |
| 不调用工具 | 用一句话解释什么是 ReAct? |
### 步骤 3:开启对比模式,点击「运行演示」
- **左侧**:普通模式,无工具
- **右侧**:ReAct 模式,应出现 Thought → Action → Observation → Answer
### 步骤 4:看摘要行
顶部应显示类似:
```text
普通模式:0 次工具调用
ReAct 模式:1 次工具调用 · 2 轮推理
```
---
## 六、在代码里工具怎么接入?(开发者视角)
每个工具在代码里都有 **两份定义**:
### 6.1 给模型看的:`TOOL_DEFINITIONS`
在 `src/lib/tools.ts` 中,描述工具名、用途、参数 Schema。
发给 DashScope 时放在请求体的 `tools` 字段,模型据此决定调谁。
### 6.2 给程序执行的:`executeTool()`
同一文件中的 `switch (name)`,根据模型返回的 `function.name` 和 `arguments` **真正执行**。
```text
模型说:call calculate({"expression":"12345*6789"})
↓
executeTool("calculate", '{"expression":"12345*6789"}')
↓
返回:"12345 * 6789 = 83810205"
↓
作为 role:"tool" 消息塞回 messages
↓
模型读 Observation,写最终 Answer
```
### 6.3 想新增第五个工具?
1. 在 `TOOL_DEFINITIONS` 加一条 `function` 描述
2. 在 `executeTool()` 加 `case` 分支
3. 在 `TOOL_LABELS` 加中文显示名
4. 在 `thought.ts` 的 `reasonByTool` 加选型说明(可选,用于 Thought 文案)
5. 在 `questions.ts` 加一道示例题
---
## 七、命令行单独测工具(不打开浏览器)
```bash
# 只测四个业务工具,不需要 DashScope Key
node scripts/test-tools.mjs
# 测「普通模式 + ReAct 模式」完整链路,需要 DASHSCOPE_API_KEY
node scripts/test-react.mjs
```
预期 `test-tools.mjs` 输出:
```text
✅ calculate: 12345 * 6789 = 83810205
✅ get_current_datetime: ...
✅ get_exchange_rate: 1 USD = x.xxxx CNY
✅ get_weather: 深圳 ...
工具测试: 4/4 通过
```
---
## 八、常见问题
**Q:工具是老师写死的假回答吗?**
A:不是。除时间/计算在本地算外,天气和汇率都是实时 HTTP 请求。
**Q:模型一定会调用工具吗?**
A:不保证。靠 System Prompt 引导;简单题模型可能直接答。课堂建议用「需工具」示例题。
**Q:为什么有时看不到 Thought 原文?**
A:DashScope 调工具时常不返回思考文字,程序会用 `thought.ts` **补全**教学用 Thought,Observation 数据仍是真实的。
**Q:VPN 会影响吗?**
A:可能影响访问 DashScope;Open-Meteo / Frankfurter 一般不受影响。课堂建议关 VPN 测大模型。
---
## 九、随堂检查
1. 本项目四个工具分别解决模型的什么弱点?
2. `TOOL_DEFINITIONS` 和 `executeTool` 各给谁用?
3. 如何证明 `get_weather` 真的发了网络请求?
<details>
<summary>参考答案</summary>
1. 算术错误、不知实时天气、不知实时汇率、不知当前时间。
2. 前者给大模型选工具;后者在收到 tool_calls 后由程序执行。
3. 浏览器 Network 面板看到 `open-meteo.com` 请求,或运行 `test-tools.mjs`。
</details>
---
相关文档:
- API 原链接详情 → [02-工具清单与官方链接.md](./02-工具清单与官方链接.md)
- 整体运行流程 → [04-运行流程详解.md](./04-运行流程详解.md)