# FDE W2D1 评测题 · Tool Calling 工具调用

> 候选人背景：36 岁，Java + 大数据 + 医疗保险方向。示例围绕医疗保险 / 特药理赔 / 保险知识库。
> 共 13 题，分 L1 基础（3）、L2 进阶（4）、L3 深度（4）、L4 场景（2）。
> 每题含「考察点」「评分维度」「折叠参考答案」。末尾有评分汇总表与达标线。

---

## L1 基础（概念辨析）

### Q1. 模型能直接执行工具吗？Tool Calling 的本质是什么？
- **考察点**：对 Tool Calling 本质的理解（模型只产出调用意图，执行权在开发者）。
- **评分维度**：
  - 能否明确"模型不执行，只输出 tool_call"。（4 分）
  - 能否说明执行、回填由开发者代码完成。（3 分）
  - 能否点出"这是安全边界/可控性来源"。（3 分）
<details>
<summary>参考答案</summary>

不能。模型只输出 tool_call（函数名 + JSON 参数），真正的执行由后端代码完成，再把结果回填给模型。执行权在开发者手里，这是可控性与安全边界的根本。在保险理赔里，模型可以"决定查保单、算金额"，但真正查库、落库的动作由受控代码执行。

</details>

### Q2. 一个 Tool Schema 由哪几部分组成？请写出核心字段。
- **考察点**：Tool Schema 结构（name / description / parameters）。
- **评分维度**：
  - 写出 name、description、parameters 三项。（5 分）
  - 说明 parameters 用 JSON Schema（type/required/enum）。（3 分）
  - 说明 description 是写给模型看的。（2 分）
<details>
<summary>参考答案</summary>

三部分：① `name` 函数名；② `description` 给模型看的自然语言说明（写得清楚降低误调用）；③ `parameters` 参数 JSON Schema，含 type、required、enum、description。示例：query_policy 工具，parameters 含 policy_no(string, required)、fields(array of enum)。

</details>

### Q3. 什么是多轮（agentic）工具调用？和单轮的区别？
- **考察点**：单轮 vs 多轮工具调用的区分。
- **评分维度**：
  - 单轮：一问一调一发答案。（3 分）
  - 多轮：模型连续决策、多次调工具直到停。（4 分）
  - 能点出"多轮有循环、需轮数上限"。（3 分）
<details>
<summary>参考答案</summary>

单轮：用户问 → 模型调一次工具 → 执行回填 → 模型给答案。多轮：模型可能连续调多次工具（查保单→查目录→算金额），形成"调工具→回填→再决策"的循环，直到模型不再调工具、输出最终答案。多轮必须设最大轮数上限防死循环。

</details>

---

## L2 进阶（流程与机制）

### Q4. 请完整描述一次多轮工具调用的循环过程。
- **考察点**：达标线①——多轮调用闭环（核心考点）。
- **评分维度**：
  - 模型输出 tool_call（name+args）。（2 分）
  - 代码按 name 查注册表执行，校验参数。（2 分）
  - 以 role:tool + tool_call_id 回填。（3 分）
  - 带完整历史再请求模型，循环直到无 tool_calls。（2 分）
  - 强调要存"模型回复+结果"、设轮数上限。（1 分）
<details>
<summary>参考答案</summary>

1) 模型输出 tool_call（函数名 + JSON 参数），temperature 设低保证参数稳定；2) 代码用 name 在注册表找到函数，解析并校验参数后执行；3) 以 `role:"tool"` + `tool_call_id` 把结果追加进 messages（必须带上完整历史，且把模型的 tool_call 消息本身也存进去）；4) 再请求模型，它结合结果继续——要么再发 tool_call，要么直接给答案；5) 当响应没有 tool_calls 时循环结束。必须设最大轮数上限防死循环。

</details>

### Q5. 工具返回结果为什么要"标准化"？请给出统一结构并解释。
- **考察点**：工具返回结果标准化（ok/data/error/trace_id）。
- **评分维度**：
  - 给出统一结构（ok/data/error/trace_id）。（4 分）
  - 说明统一便于模型消费与可观测。（3 分）
  - 说明失败返回错误码而非堆栈。（3 分）
<details>
<summary>参考答案</summary>

统一成 `{ok, data, error, trace_id}` 结构再回填：成功返回干净业务数据，失败返回 `ok:false` + 简洁错误码（绝不返回堆栈），trace_id 贯穿调用便于排障和评测。标准化让"模型理解"和"你做可观测"两全，且回填前要裁剪（别把 10 万行结果塞进上下文）。

</details>

### Q6. 模型给的工具参数不可信，应该怎么校验？
- **考察点**：参数校验与类型转换（JSON 解析 + Pydantic + 业务校验）。
- **评分维度**：
  - JSON 解析（防非法 JSON）。（2 分）
  - Pydantic/JSON Schema 校验形（类型/必填/枚举）。（4 分）
  - 业务校验（格式正则、范围、语义）。（2 分）
  - 失败反馈模型而非自己猜。（2 分）
<details>
<summary>参考答案</summary>

三步：① JSON.parse/json.loads 确保能解析，失败则反馈重试；② Pydantic/JSON Schema 校验类型、必填、枚举、范围；③ 业务规则校验（保单号格式、金额非负、枚举同义词映射）。失败不是自己猜着用，而是把校验错误喂回模型让它修正重调。

</details>

### Q7. 工具的超时和失败重试该怎么设计？
- **考察点**：工具超时与失败重试分类处理。
- **评分维度**：
  - 每个工具设超时。（2 分）
  - 分类：网络/超时退避重试。（3 分）
  - 参数错反馈模型改。（3 分）
  - 永久错降级。（2 分）
<details>
<summary>参考答案</summary>

每个工具调用都有超时（如 3~10s）。失败分类处理：网络抖动/5xx 指数退避重试 2~3 次；参数错误（模型填错）把校验错误反馈模型改参数重调（无脑重试没用）；下游永久故障停止重试降级（返回暂不可用/转人工）。重试带退避且计入成本预算。

</details>

---

## L3 深度（设计与安全）

### Q8. 为什么"把异常堆栈回填给模型"是错的？正确做法是什么？
- **考察点**：达标线②——异常隔离（不泄露给模型）。
- **评分维度**：
  - 安全风险（暴露架构/密钥/PII）。（4 分）
  - 噪声风险（干扰决策）。（3 分）
  - 正确做法：try/except 转业务错误码、脱敏、引导改。（3 分）
<details>
<summary>参考答案</summary>

两个风险：① 安全——堆栈可能暴露内部架构、IP、密钥、PII；② 噪声——模型被无关技术细节干扰可能胡乱决策。正确做法：工具代码内部 try/except，把异常转成业务层错误码（如 policy_not_found），只把"模型能用的信息"（错误类型+建议动作）回填；回填前对 PII 脱敏；错误反馈要引导模型改而非只说"出错了"。

</details>

### Q9. 并行工具调用和串行怎么区分？依赖关系怎么判断？
- **考察点**：多工具路由与并行/串行。
- **评分维度**：
  - 无依赖并行（省延迟）。（3 分）
  - 有依赖串行（下一轮）。（3 分）
  - 依赖通常由模型判断，强顺序可代码固化。（4 分）
<details>
<summary>参考答案</summary>

无依赖的工具（如同时查保单 + 查药品库）并行执行降延迟；有依赖的（先查额度再算金额）串行，在下一轮。依赖关系通常由模型自己判断（它发现需要 A 的结果才能决定 B），但强顺序业务（理赔先查后算）可在代码层固化编排，只把不确定的交给模型。并行时要注意全部执行完再统一回填。

</details>

### Q10. Qwen / DeepSeek / Claude 三家的工具调用机制有何异同？
- **考察点**：各家实现差异（OpenAI 系 vs Claude）。
- **评分维度**：
  - 三家本质相同（请求带 tools → 响应给 tool_calls → 执行 → 回填 → 再问）。（4 分）
  - 回填格式差异：OpenAI 用 role:tool+tool_call_id，Claude 用 tool_result。（4 分）
  - 建议用适配层屏蔽差异。（2 分）
<details>
<summary>参考答案</summary>

本质相同：请求带 tools 定义 → 响应带回 tool_calls（name+arguments）→ 你执行 → 用对应格式回填 → 再请求。差异在回填格式：OpenAI 系（Qwen/DeepSeek 兼容）用 `role:"tool"` + `tool_call_id`；Claude 用 `tool_result` 块（强类型）。建议用 LangChain 或自封装适配层屏蔽厂商差异，业务代码只面向统一 Tool 接口。

</details>

### Q11. 生产级工具调用需要哪些防护措施？
- **考察点**：工程落地与可观测性。
- **评分维度**：
  - 轮数上限、超时重试上限。（3 分）
  - 工具白名单、危险操作人工审批。（3 分）
  - 全链路 Trace、PII 脱敏与权限校验。（4 分）
<details>
<summary>参考答案</summary>

三件套 + 防护：① 最大轮数上限（防循环）；② 每个工具超时 + 重试上限；③ 全链路 Trace（每轮选了啥工具、传啥参、耗时、是否降级）；④ 工具白名单（只能调注册过的）；⑤ 危险写操作（如真正提交理赔）需二次确认/人工审批；⑥ PII 脱敏与权限校验（谁能查谁的保单）。没有 Trace 你无法回答"这次理赔为什么拒了"。

</details>

---

## L4 场景（医疗保险综合）

### Q12. 场景题：用户说"帮我看下 PA-2024-123456 的特药理赔能报多少，药是奥希替尼"。
请描述 Agent 应走的工具调用链路，并说明每步模型在做什么、代码在做什么。
- **考察点**：综合应用多轮工具调用于理赔场景。
- **评分维度**：
  - 链路完整：查保单→查特药目录→算金额→输出。（4 分）
  - 每步区分"模型决策"与"代码执行"。（3 分）
  - 提到参数校验、低 T、轮数上限、异常隔离。（3 分）
<details>
<summary>参考答案</summary>

模型：理解诉求，决定先查保单（调 query_policy，参数 PA-2024-123456）。代码：校验参数→查只读库→返回额度/有效期。模型：看到额度，决定查"奥希替尼"是否在特药目录（调 query_drug_db）。代码：返回命中+目录价。模型：决定算金额（调 calc_claim，参数 发票额/额度/目录命中）。代码：规则引擎算出 pay_amount、risk_level、命中依据。模型：综合生成自然语言答案。全程：temperature≈0.1 保证参数稳定、每轮存完整历史、设 8 轮上限、工具失败按分类处理、异常隔离不泄露堆栈。

</details>

### Q13. 场景题：calc_claim 工具调用后超时了，且重试一次仍超时；但用户还在等答案。你怎么处理？
- **考察点**：失败降级与用户体验兜底（达标线②应用）。
- **评分维度**：
  - 不无限重试，超时即停。（3 分）
  - 降级策略：返回"暂不可用/转人工"或分步给已得信息。（4 分）
  - 异常隔离 + 记 Trace + 不卡死流程。（3 分）
<details>
<summary>参考答案</summary>

超时重试 1 次仍超即停止（不无限重试烧资源）。降级：因为前面已查到保单额度和目录命中，可先返回"已确认药品在目录、额度充足，金额计算服务暂繁忙，已为您转人工/稍后推送"，而不是卡死或报错堆栈。全程异常隔离（只给业务错误），记 Trace（calc_claim 超时、重试 1 次、已降级转人工），保证流程不中断、用户有预期。

</details>

---

## 评分汇总表

| 题号 | 层级 | 考察点 | 满分 | 建议权重 |
|------|------|--------|------|----------|
| Q1 | L1 | Tool Calling 本质 | 10 | 基础 |
| Q2 | L1 | Tool Schema 结构 | 10 | 基础 |
| Q3 | L1 | 单轮 vs 多轮 | 10 | 基础 |
| Q4 | L2 | 多轮调用循环（达标线①） | 10 | **核心** |
| Q5 | L2 | 结果标准化 | 10 | 进阶 |
| Q6 | L2 | 参数校验 | 10 | 进阶 |
| Q7 | L2 | 超时与重试 | 10 | 进阶 |
| Q8 | L3 | 异常隔离（达标线②） | 10 | **核心** |
| Q9 | L3 | 并行/串行路由 | 10 | 深度 |
| Q10 | L3 | 各家实现差异 | 10 | 深度 |
| Q11 | L3 | 生产防护 | 10 | 深度 |
| Q12 | L4 | 理赔场景链路 | 10 | 场景 |
| Q13 | L4 | 超时降级兜底 | 10 | 场景 |

**总分 130 分**（每题满分 10，按维度给分）。

## 达标线

- **L1 基础达标**：Q1~Q3 总分 ≥ 24/30，能说清"模型只出意图、Schema 三件套、单轮多轮区别"。
- **达标线①（多轮循环）**：Q4 ≥ 9/10，能完整讲清"模型输出 tool_call→执行→带 id 回填→再决策→停止"，并提到存历史和轮数上限。
- **达标线②（失败处理/异常隔离）**：Q8 ≥ 9/10，能说清"分类重试、参数错反馈模型、永久错降级、异常隔离不泄露堆栈/PII"。
- **综合达标（可进下一阶段）**：总分 ≥ 100/130，且 Q4、Q8 均达标，且 L4 两题均 ≥ 7/10（能落地到医疗保险场景）。
- **高风险失分项**：把模型当"直接执行者"、回填漏 tool_call_id、把异常堆栈给模型、无脑重试参数错、无轮数上限——任一出现且不能自圆其说，建议补学 W2D1 后再面。
