# 简历复盘记录

本文档用于整理简历项目的面试复盘内容。简历是事实基准；`amms` 和 `ragent` 两个本地项目仅用于辅助理解实现，不代表个人职责或经历与仓库完全一致。涉及个人贡献、数据结果和业务背景时，以本人实际经历为准，尚未确认的内容先标记为待核实。

## 简历经历索引

| 经历 | 简历时间 | 简历角色 | 参考项目 |
| --- | --- | --- | --- |
| 八字健康科技集团有限公司 | 2026.06–2026.09 | AI 应用开发实习生 | `ragent`（参考） |
| 设备智能运维平台 | 2026.04–至今 | 后端开发 / 大模型应用 | `amms`（参考） |

## 使用约定

- 本文档与 [HTML 阅读版](简历复盘记录.html) 同步维护；内容确认后同时更新两个版本。
- 每节按“快速回顾 → 面试主回答 → 分类追问 → 易混点与回答检查”复习；每道追问先练“面试回答”，再查看“实现细节”中的补充说明、例子、参数和对照表，检查项用于自测。
- HTML 提供“表格速查”与关键词搜索；表格直接取自正文，点击搜索结果可展开并定位对应内容。
- HTML 可切换“只看问题”进行自测；表格速查适合集中回顾选型、参数、职责和异常处理。
- 先记录问题和回答，再补充追问、证据及需要核实的事实。
- 回答优先按“背景与目标、个人负责、方案与取舍、结果与验证”组织。
- 区分个人完成、参与协作和系统已有能力；不把参考项目代码直接当作个人贡献。
- 评测数据和性能结果应记录评测集、指标口径、对照条件及来源。
- 待确认信息使用 `待核实` 标记，确认后再改为事实陈述。

## 经历一：八字健康科技集团有限公司

### 项目概览

#### 项目背景与职责

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 服务对象 | 政府监管人员、养老机构、老人及家属 |
| 项目目标 | 政策、制度与服务资料查询，支持时效控制和答案溯源 |
| 个人负责 | 资料入库、混合检索、Agent 查询流程与离线评测 |

##### 面试主回答

背景：养老政策、机构制度和服务资料来源多、格式不一，而且政策和制度会更新。用户提出问题时，系统不仅要找到“相关内容”，还要尽量避免引用过期或无权查看的资料，并能说明答案来自哪份文档、哪个位置。因此，这个项目不仅要接入大模型做问答，还要把文档处理、检索、权限与时效控制、答案溯源串成一条可信的知识服务链路，供政府监管人员、养老机构以及老人和家属查询资料。

职责：我主要负责知识问答链路中的资料入库、检索和 Agent 查询流程。入库侧的关键问题是文档处理耗时较长、大文件上传可能中断，所以我把解析、分块和向量化放到后台任务中，并处理分片上传和续传，让资料提交与后续处理解耦。

检索侧需要同时兼顾“找到具体条款”和“理解不同说法表达的相似问题”。我采用 Parent-Child Chunk，让较小的子块负责精确召回，再补回父块上下文；同时融合 BM25 和 BGE-M3 召回结果，再用 RRF 与重排改善候选排序。政策有效期、文档版本、知识库范围和用户权限也需要参与筛选，避免只因为文本相似就把不适用的资料交给模型。

问答侧，我参与设计了 Agent 的检索和工具调用流程。问题信息不足或证据不够时，Agent 可以改写查询、继续检索或拆成多步处理；对话上下文则通过滑动窗口和历史摘要控制长度。最终回答保留 Evidence ID、文档和页码，便于核对依据。

我构建了 200 余道题的离线测评集，用来评估检索改动的效果；测评中，Recall@10 从 0.71 提升至 0.89，MRR@10 从 0.62 提升至 0.78。

##### 易混点与回答检查

> **易混点**：资料相关不等于政策适用；权限、时间、版本和引用共同决定证据能否用于回答。

**回答检查**

- [ ] 能按“服务对象 → 业务难点 → 个人负责 → 方案 → 评测结果”连贯说明。
- [ ] 能说明 200 余道题及两项指标对应实习项目，整体提升不能直接归因于单一组件。

### 简历要点复盘

#### 文档异步入库、分块上传与断点续传

简历表述：基于 Kafka 将解析、分块与向量化转为后台任务；结合 Redis Bitmap 与 MinIO 分片上传实现大文件断点续传。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 核心目的 | 上传与解析、分块、向量化解耦，支持大文件续传和任务恢复 |
| 处理链路 | MinIO 分片上传 → 合并确认 → MySQL 状态与 Outbox → Kafka → 后台处理 |
| 状态与恢复 | Redis Bitmap 记录进度；MinIO 校对实际分片；MySQL 保存阶段 checkpoint |
| 一致性 | 稳定业务键、条件更新、稳定 chunk ID upsert、Outbox 重试发布 |

##### 面试主回答

文档解析、分块和向量化耗时较长，如果放在上传请求里同步完成，用户等待时间会很长，处理失败也会影响上传接口。因此我用 Kafka 解耦文件上传和后台入库，上传完成后投递文档任务，由消费者执行后续处理。Kafka 的日志和消费位点便于追踪、恢复处理进度，消费者组也便于按负载扩展。

它并非所有场景下都优于其他 MQ；RabbitMQ 的任务队列和路由语义更直接，选型还要看任务规模、团队经验和现有基础设施。无论使用哪种 MQ，都要处理重复消费、失败重试和任务状态，不能把消息投递成功等同于文档已经可检索。

用户创建上传会话后，后端校验文件信息和访问权限，再向 MinIO 创建 Multipart Upload 并取得 `uploadId`。客户端按序号上传各分片，记录每片返回的 ETag；Redis Bitmap 记录分片进度，供续传时快速查询。网络中断后，客户端使用原会话补传未完成分片。

恢复时与 MinIO 的实际分片列表核对，避免 Redis 进度丢失或过期造成状态不一致。全部分片完成后，后端按序号和 ETag 请求 MinIO 合并对象，确认原文件可用后再更新任务状态并投递 Kafka 入库任务。上传完成和入库完成是两个不同状态：只有解析、分块和索引流程成功后，文档才可检索。

##### 原理与选型

###### 为什么选择 Kafka，而不是其他 MQ？

面试回答：

我们这条链路里，文件上传之后还要解析、分块和向量化，处理耗时比较长，所以需要把上传接口和后台任务解耦。我选择 Kafka，主要看中它的分区日志、消息保留和消费位点，任务失败后可以重新消费消息，消费者组也便于扩展处理能力。

上传完成后，系统发布文档入库任务，消费者根据数据库里的阶段状态继续处理。Kafka 负责消息传递和重放，具体恢复到哪一个阶段仍由业务 checkpoint 决定。RabbitMQ 也能完成这种异步任务，它的队列和路由语义更直接；我们这套方案更侧重消息重放和消费进度管理，因此使用 Kafka，并在业务侧补齐幂等和失败恢复。

实现细节：

Kafka 消费位点描述消息消费进度，业务 checkpoint 描述文档处理进度，两者需要配合。更换 MQ 也不能省掉任务状态与幂等控制。消息发布成功后，文档仍需完成索引才能进入可检索状态。

| 比较点 | Kafka | RabbitMQ |
| --- | --- | --- |
| 主要机制 | 分区日志、消费位点、消费者组 | 任务队列、消费确认与消息路由 |
| 本项目的考虑 | 便于任务追踪、消息重放和消费者扩展 | 也能承担异步任务，队列与路由语义更直接 |
| 共同需要处理 | 重复消费、失败恢复、业务任务状态 | 重复消费、失败恢复、业务任务状态 |
| 选择依据 | 任务规模、已有基础设施与团队经验 | 任务规模、已有基础设施与团队经验 |

##### 实现与恢复

###### MinIO 分片上传与断点续传的流程是什么？

面试回答：

整体分成创建会话、上传分片、合并确认和触发入库几个阶段。用户发起上传后，后端先校验文件和权限，再向 MinIO 创建 Multipart Upload，拿到 uploadId。客户端按分片序号上传，保存每片的 ETag，Redis Bitmap 用来快速记录哪些分片已经完成。

网络中断后沿用原来的上传会话，先与 MinIO 的实际分片列表核对，再补传缺失部分，避免只相信缓存进度。全部分片上传成功后，后端按序号和 ETag 请求合并，并确认目标对象可用。之后在数据库中更新文档状态、写入 Outbox 事件，再由发布器投递 Kafka。后台完成解析、分块和索引后，文档才会标记为可检索。

实现细节：

`uploadId` 标识 MinIO 的一次分片上传，`partNumber` 决定分片次序，ETag 用于合并时指定已上传的分片。Redis 进度可能过期或丢失，恢复以 MinIO 中实际存在的分片为校对依据。上传状态与索引处理状态分别维护。

| 步骤 | 关键动作 | 记录或检查 |
| --- | --- | --- |
| 1. 创建会话 | 校验文件与权限，创建 Multipart Upload | `uploadId` 与上传会话 |
| 2. 上传分片 | 按 partNumber 上传，保存返回 ETag | 分片序号、ETag、Redis Bitmap |
| 3. 中断续传 | 使用原会话，校对 MinIO 实际分片 | 补传缺失分片，纠正缓存进度 |
| 4. 合并文件 | 按序号与 ETag 提交合并 | 确认目标对象实际可用 |
| 5. 触发入库 | 更新文档状态并写入 Outbox 事件 | 由发布器投递 Kafka |
| 6. 完成索引 | 解析、分块、向量化与索引完成 | 推进为可检索状态 |

###### Kafka 重复消费如何保证幂等？

面试回答：

我按消息可能重复到达来设计消费逻辑，先用文档版本和处理阶段组成稳定业务键。消费者拿到消息后，先检查数据库里的阶段状态，已经完成的直接跳过，处理到一半的根据持久化 checkpoint 从未完成的位置继续。

对于最终的索引写入，我给每个子块生成稳定的 chunk ID，使用 upsert，重复处理时更新同一条记录，避免新增重复索引。每个阶段都在产物和状态落库后再提交消费进度。这样即使处理成功后提交位点失败，消息再次到达，也可以根据已有状态安全跳过。Redis 可以辅助加速判断，但持久化状态才是恢复依据。

实现细节：

阶段业务键使用 `documentVersionId + stage`，不能只用文档名称或本次消息 ID。阶段完成判断与持久化写入需要配合，避免并发处理时重复推进状态。存在非幂等的外部副作用时，另存调用状态，并通过结果核对或补偿处理。

| 层次 | 关键标识或机制 | 重复到达时 |
| --- | --- | --- |
| 任务阶段 | `documentVersionId + stage` | 查询持久化状态，跳过已完成阶段 |
| 阶段产物 | 持久化 checkpoint 与产物 | 从未完成位置继续 |
| 索引写入 | 稳定 chunk ID + upsert | 更新同一条索引记录 |
| 外部副作用 | 调用状态记录与补偿 | 先确认结果，避免重复执行 |

###### 消费失败如何重试和恢复？

面试回答：

消费失败后，我先判断错误能否通过重试恢复。模型超时、存储暂时不可用这类临时故障采用退避重试；文件格式错误、内容为空这类确定性问题，继续重试没有意义，就记录原因，停止自动重试并转人工或死信流程。

恢复依赖数据库里的阶段 checkpoint。解析、分块和向量化各阶段完成后，先保存产物和状态，再提交消费进度。Worker 重启或者消息重放时，先读取已完成阶段，从失败位置继续，避免每次重新处理整个文件。重复到达的消息仍然走同一套幂等逻辑，所以重试和恢复可以配合使用。

实现细节：

恢复前需要同时确认阶段状态与对应产物，不能仅凭一个“成功”标记跳过缺失结果。消费进度确认与业务阶段完成分开理解，阶段中断不能提前当作处理成功。

| 情况 | 判断或状态 | 处理方式 |
| --- | --- | --- |
| 模型超时、存储暂不可用 | 临时故障 | 退避重试 |
| 格式错误、内容为空 | 确定性错误 | 停止自动重试，记录原因并转人工或死信流程 |
| Worker 重启或消息重放 | 已保存阶段 checkpoint | 跳过已完成阶段，从未完成阶段继续 |
| 阶段执行成功 | 产物与状态均已持久化 | 再提交消费进度 |

###### 上传完成但 Kafka 消息投递失败如何补偿？

面试回答：

这里涉及数据库和 Kafka 两个系统，我使用 Transactional Outbox 保证任务不会因为发送失败而丢失。MinIO 文件合并确认成功后，在同一个数据库事务里更新文档状态，同时写入一条待发布事件，事务提交后再由独立发布器发送 Kafka。

Kafka 暂时不可用时，事件仍然保留在数据库，发布器后续继续重试；收到 Kafka 确认后才标记已发布。如果发送成功但标记前进程崩溃，事件可能再次投递，这部分交给消费端幂等处理。这样通过持久化待发布事件解决漏投递，通过幂等解决可能的重复投递。

实现细节：

Outbox 事务覆盖文档状态与事件记录，MinIO 对象合并发生在这一事务之前。发布器不能在得到 Kafka 确认前将事件标成已发布；发布结果不确定的事件保留重试资格。

| 环节 | 持久化或确认 | 失败后的行为 |
| --- | --- | --- |
| 数据库事务 | 同时更新文档状态与写入待发布事件 | 事务失败则一起回滚 |
| 发布器投递 | 扫描 Outbox 并发送 Kafka | 未获确认的事件保留并重试 |
| 确认发布 | 收到 Kafka 确认后标记已发布 | 标记前崩溃可能重复投递，由消费端幂等处理 |

###### 分片合并失败如何处理？

面试回答：

我先区分分片不完整、合并明确失败和合并结果不确定几种情况。合并前校验分片序号和 ETag，并和 MinIO 的分片列表核对；如果缺片，就保留上传会话，让客户端补传后再合并。

如果调用合并接口超时，不能直接认为文件没有生成。我会先查询目标对象，核对文件是否已经完整可用；已经成功就推进后续入库，确实未成功且原上传会话仍有效时再重试。会话已经失效就重新创建上传。只有确认原文件可用后才触发入库，失败过程中保留状态和原因，便于续传或清理遗留分片。

实现细节：

合并成功通常会结束对应 Multipart Upload，因此超时后再次找不到上传会话，不能单独据此判断失败，需要结合目标对象检查。长期未完成的会话和分片应清理，避免存储长期占用；清理前检查任务仍是否有效。

| 故障位置 | 判断依据 | 恢复方式 |
| --- | --- | --- |
| 分片不完整 | MinIO 实际分片与预期列表 | 保留会话，补传缺失分片 |
| 分片信息不一致 | partNumber 与对应 ETag | 校正后重新请求合并 |
| 合并请求超时 | 目标对象是否已经生成 | 先确认结果，再决定是否重试 |
| 上传会话过期未完成 | 会话有效期与处理状态 | 按清理策略回收 |
| 合并明确成功 | 对象已确认可用 | 推进数据库状态并触发入库 |

##### 易混点与回答检查

> **易混点**：上传成功、消息发布成功和文档可检索是不同状态；Redis Bitmap 用于进度加速，恢复仍要核对 MinIO。

**回答检查**

- [ ] 能讲清 uploadId、partNumber、ETag、Bitmap 各自的作用。
- [ ] 能分别回答重复消费、失败恢复、投递失败和合并结果不确定如何处理。

##### 补充记录

事实 / 代码依据：待补充。

#### Parent-Child Chunk 分层索引

简历表述：通过子块召回定位相关内容，回溯父块补全条款与上下文。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 核心思路 | 子块定位，父块补全；兼顾检索精度与条款完整性 |
| 处理链路 | 子块召回 → RRF → 子块重排 → 按父块去重 → 预算内补全上下文 |
| 调参参考 | 子块约 200～400 tokens；父块约 800～1500 tokens |
| 存储分工 | MySQL 保存正文与关系；全文索引保存子块文本；Milvus 保存子块向量 |

##### 面试主回答

Parent-Child Chunk 把用于检索的片段和交给模型的上下文分开处理。先按章节、条款等文档结构划分较大的父块，再在父块内部划分较小的子块。子块记录 `parentId`、文档版本和页码，检索时先用子块定位相关内容，命中后再回溯父块，补充回答需要的上下文。

例如，一份养老补贴政策的“申请条件”包含年龄要求、失能等级、户籍限制和例外情况，可以把这一节作为父块，再把具体条件划成子块。用户问“外地户籍的老人能不能申请”，先召回户籍要求对应的子块，再补回父块，让模型同时看到其他申请条件和例外条款，避免只凭一句话下结论。

选择这个方案是为了兼顾检索精度和条款完整性。固定切块过大时，一块混合多个主题，相关内容容易被其他信息稀释；切块过小时，又可能把适用范围、前置条件和例外说明分开。养老政策和机构制度对这些限制条件比较敏感，所以我们先对较小的子块做 BM25 和向量召回，经过 RRF 融合及 BGE Reranker 重排，再根据命中的子块回溯父块，补充完整依据。

回溯时按 `parentId` 去重，同一个父块只保留一次，并根据上下文预算控制补充范围，避免重复内容和过长上下文。父块尽量沿用章节或完整条款边界，不把互不相关的内容拼在一起。

##### 原理与选型

###### 为什么不直接用固定切块加 overlap？

面试回答：

固定切块加 overlap 可以补偿切分边界附近的信息，但政策的适用范围和例外说明可能相隔比较远，只增加相邻重叠，不一定能把这些条件一起带回来。overlap 过大还会增加重复文本，让相似片段占用召回名额。

所以我采用父子分块，让较小、主题集中的子块负责精确召回和重排，命中后再按父子关系补回完整条款。这样既能定位用户问的具体条件，也能让模型看到同一条款的前置条件和例外。子块仍然可以保留适量 overlap，两种方法配合使用，只是补充上下文的范围更多依据文档结构来确定。

实现细节：

父子分块需要维护 `parentId` 与版本关系，并控制回溯预算。选择该方案的业务依据是政策、制度具有章节与条款结构，且遗漏限制条件会影响答案适用性。

| 方案 | 主要作用 | 需要注意 |
| --- | --- | --- |
| 固定切块 | 控制片段长度，便于建立索引 | 可能拆开条件与例外，或混合多个主题 |
| 增加 overlap | 补偿切分边界附近的信息 | 增加重复内容，未必覆盖远处附注 |
| Parent-Child | 子块精确定位，父块补齐结构化上下文 | 需维护父子关系并控制回溯预算 |

##### 实现与调参

###### 父块和子块分别多大？怎么确定？

面试回答：

我们优先按文档结构切分，再设置长度上限。父块尽量对应一个完整条款或章节中的独立主题，子块再沿段落、句子边界细分，保证检索时主题集中。调参参考范围是子块约 200 到 400 tokens，父块约 800 到 1500 tokens，具体仍要结合条款结构。

选择大小时，我同时看检索效果和回答完整性。子块太大可能混入无关主题，太小又可能失去必要指代；父块太大则会增加上下文成本。我们通过离线测评比较不同粒度下的召回、排序、适用条件覆盖以及 token 开销，再选择合适的切分配置。

实现细节：

表中是调参参考范围。过长章节仍需继续拆分，不能为了保持章节完整而无限扩大父块。中文字符数与 token 数不等价，长度检查采用对应模型的 tokenizer，并保留标题和必要指代信息。

| 对象 | 调参参考范围 | 切分与检查重点 |
| --- | --- | --- |
| 子块 | 约 200～400 tokens | 按段落和句子边界，主题集中且可独立理解 |
| 父块 | 约 800～1500 tokens | 尽量覆盖完整条款、适用条件和例外 |
| 参数选择 | 参考范围，结合文档与评测确定 | 比较召回、排序、条件完整性和上下文成本 |

###### 父块和子块分别存在哪里？哪些内容需要向量化？

面试回答：

这套方案里，MySQL 保存文档版本、父子块正文和关联关系，子块上记录 parentId、章节路径和页码。用于检索的是子块，全文索引保存子块文本供 BM25 使用，Milvus 保存 BGE-M3 编码后的子块向量和必要过滤字段。

检索、融合和重排确定子块后，再根据文档版本和 parentId 从 MySQL 回查父块，补充完整上下文。父块读取走关联关系，不需要再做一遍父块向量检索。回查时还会核对当前版本和权限，避免索引更新滞后时，把已失效或无权访问的正文交给模型。

实现细节：

正文与父子关系以 MySQL 记录为准，全文索引和向量索引承担召回职责。父块回溯时同时使用文档版本与 `parentId`，防止不同版本之间错误关联。

| 组件 | 保存内容 | 主要用途 |
| --- | --- | --- |
| MySQL | 文档版本、父子块正文、parentId、章节及页码 | 回查正文、关系与当前状态 |
| 全文索引 | 子块文本及过滤字段 | BM25 关键词检索 |
| Milvus | BGE-M3 子块向量及过滤字段 | 语义召回 |
| 父块读取 | 按文档版本与 parentId 回查 | 补齐上下文，不另做一次父块向量检索 |

###### 重排在回溯父块之前还是之后？为什么？

面试回答：

我们先对子块重排，再回溯父块。子块内容比较集中，Reranker 更容易判断它是否直接回答问题，输入长度和计算成本也比较可控。等相关子块筛选出来后，再补回它所在的完整条款，供后续生成答案使用。

如果先扩展为父块，同一父块可能被多个候选重复带入，正文长度也会明显增加，反而容易把真正相关的内容截断。不过子块也不能完全失去上下文，所以切分时会保留标题、章节路径和必要指代信息，让重排模型能够理解这一段在讲什么。

实现细节：

重排输入是问题与候选子块，父块装配服务于后续回答。对子块中“符合上述条件者”一类表述，需要保留足够的标题或上下文信息，避免相关性判断失去依据。

| 顺序 | 收益 | 主要代价或风险 |
| --- | --- | --- |
| 先重排子块，再回溯父块 | 相关性判断集中，输入较短，成本可控 | 子块需要保留标题和必要指代信息 |
| 先回溯父块，再重排 | 可直接看到更大范围内容 | 重复与无关内容增加，相关条款可能被截断 |

###### 多个子块命中同一个父块，怎么去重和排序？

面试回答：

我先按文档版本和 parentId 聚合，同一个父块只放进上下文一次，同时保留命中的子块和位置。父块排序主要使用其中相关性最高的子块得分，不把所有子块分数简单相加，避免长章节因为子块多就天然排在前面。

如果同一父块的多个命中分别覆盖问题的不同条件，装配时会保留这些证据范围。重排后的十个子块也可能只对应两个父块，所以去重后还会检查证据是否过于集中，必要时从后续候选中补充不同父块，但仍然受相关性和上下文预算限制。

实现细节：

父块去重键包含版本，不能只用标题。保留子块位置既便于围绕命中区域读取，也便于把最终结论引用到实际证据页。补充不同父块时，不为凑数量引入低相关内容。

###### 父块太大，超过上下文预算怎么办？

面试回答：

父块过大时，我不会直接从开头截一段，而是围绕命中的子块补充所属条款，优先保留标题、适用条件和例外说明。同一父块中多个相邻或重叠的命中范围先合并，减少重复，不同父块再根据相关性分配 token 预算。

如果回答依赖的必要条件仍然装不下，就通过后续读取或补充检索分步查证，不能为了满足长度限制把关键条件删掉。证据仍不充分时，就缩小当前回答范围。这样控制的是上下文开销，同时保留足以支持结论的完整依据。

实现细节：

预算装配需要保留原文位置和版本信息，截取片段后仍能回到原文。跨父块比较时检查问题各维度是否覆盖，不能只按得分把同一维度的相似片段全部装满。

| 优先级 | 装配内容 | 控制方式 |
| --- | --- | --- |
| 先保留 | 命中片段、所属条款、标题、适用条件与例外 | 围绕命中位置读取，避免只截取开头 |
| 再去重 | 同一父块内相邻或重叠范围 | 合并后装配 |
| 分配预算 | 不同父块的相关证据 | 按相关性分配 token 预算 |
| 仍有缺口 | 回答必须依赖但未读到的内容 | 补充读取、分步查证或缩小回答范围 |

###### PDF 表格、跨页条款怎么切？

面试回答：

我按解析后的条款和表格结构切分，页码主要用于引用定位。一个条款即使跨了两页，只要编号、标题和正文是连续的，仍然需要关联起来，同时保留每个片段的来源页码。

表格要保留表名、表头、单位和附注。表格过大需要拆开时，每组数据行都带上必要表头，否则一个金额数字脱离照护等级和每人每月的单位就没有完整含义。跨页表格还要检查列结构和表头是否一致，不能只因为位置相邻就合并。最终引用指向真正支持结论的页面，涉及多页时保留多页来源。

实现细节：

结构切分关注条款编号、标题层级、正文连续性以及表格列结构。金额、比例、计量周期和适用对象都属于证据的一部分，不能只索引单元格中的数值。

##### 评测与结果

###### 怎么证明父子分块比原来的方案好？

面试回答：

我用控制变量的方式比较。固定同一批文档和版本、同一测评集，以及 embedding、检索和重排配置，主要改变分块与上下文补充方式。检索侧看 Recall@10、MRR@10，回答侧检查适用条件和例外是否遗漏、引用是否支持结论，同时记录上下文 token 数与耗时。

这里使用我们构建的 200 余道题测评集。因为不同分块方案的 chunk ID 会变化，标准证据标到原文条款或范围，再判断各方案有没有找到。简历里 0.71 到 0.89 的 Recall@10、0.62 到 0.78 的 MRR@10 是整套检索改进的结果，父子分块的贡献要看它自己的对照实验，不能直接用整体增幅代替。

实现细节：

分块对照固定检索和重排配置，混合检索对照则固定分块。证据命中依据原文范围映射；扩展父块后的条件覆盖与扩展前的检索排序分别观察，避免扩大上下文后混淆指标口径。

| 比较项 | 固定条件或观察参数 |
| --- | --- |
| 固定条件 | 文档及版本、评测集、embedding、检索与重排配置 |
| 主要变量 | 分块方式与上下文补充方式 |
| 证据覆盖与排序 | Recall@10、MRR@10 |
| 回答质量 | 适用条件是否遗漏、例外是否误用、引用是否支持结论 |
| 成本 | 上下文 token 数与耗时 |
| 归因 | 整体链路提升与父子分块独立收益分开判断 |

##### 易混点与回答检查

> **易混点**：先重排子块再回溯父块；10 个子块不等于 10 个父块；token 数不等于字符数。

**回答检查**

- [ ] 能说明子块负责定位、父块补全条件，以及为什么 overlap 不能完全替代父块回溯。
- [ ] 能说明版本 + parentId 去重、预算装配及独立对照实验。

##### 补充记录

事实 / 代码依据：待补充。

#### BM25 与 BGE-M3 混合检索、RRF 融合及重排

简历表述：融合 BM25 与向量检索，经 RRF 合并候选、BGE Reranker 重排，并按知识库范围和用户权限过滤；Recall@10 从 0.71 提升至 0.89，MRR@10 从 0.62 提升至 0.78。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 核心思路 | BM25 精确匹配 + BGE-M3 语义召回 → RRF 融合 → Reranker 精排 |
| 初始召回配置 | BM25 Top 50 + 向量 Top 50 → RRF 取 50 → 重排取 10 个子块 |
| RRF 初始参数 | 平滑常数 k=60，BM25 与向量权重 1:1 |
| RRF 调参范围 | k 比较 20、60、100；权重比较 1:1、1.5:1、1:1.5 |
| 输入预算参考 | Reranker 1024 tokens，受所选模型长度上限约束 |
| 测评集与结果 | 200 余道题；Recall@10：0.71 → 0.89；MRR@10：0.62 → 0.78 |

##### 面试主回答

我们采用混合检索，是因为养老政策查询同时存在精确术语和口语化表达。BM25 负责政策名称、条款编号等关键词匹配，BGE-M3 负责补充语义召回，两路并行检索子块。两路分数不在同一尺度，所以用 RRF 按排名融合，避免直接相加造成某一路占优势。融合后再用 BGE Reranker 对问题与候选正文联合评分，把直接相关的内容排到前面。

最后检查文档权限、有效期和版本，再回溯父块补全条款上下文。我们用 200 余道题的离线测评集评估整套改动，Recall@10 从 0.71 提升到 0.89，MRR@10 从 0.62 提升到 0.78，分别观察证据覆盖和首条正确证据的排序情况。

##### 原理与选型

###### 为什么要混合检索？

面试回答：

养老资料查询有两类表达需要同时照顾。一类包含明确的政策名称、条款编号或专业术语，BM25 的关键词匹配比较合适；另一类是用户用日常语言描述需求，和文档原文用词差异很大，需要 BGE-M3 的语义检索补充。

比如用户问“老人自己吃饭穿衣都困难，能申请什么帮助”，文档可能写的是生活自理能力、失能等级和照护补贴，纯关键词检索容易漏掉。反过来，纯向量检索也可能找到主题相近但条件不符的内容。所以我让两路并行召回，再用 RRF 融合和 Reranker 筛选，扩大证据覆盖后进一步判断相关性。

实现细节：

“养老机构等级评定的申请材料”这类含明确术语的查询适合检查 BM25；口语化需求适合检查语义召回。两路候选都需要经过地区、时间、权限等适用性检查，主题相关不等于能够回答。

| 检索方式 | 擅长处理 | 容易出现的问题 |
| --- | --- | --- |
| BM25 | 政策名称、条款编号、机构名称、明确术语 | 用户与原文用词差异大时可能漏召回 |
| BGE-M3 向量检索 | 同义表达、自然语言描述、语义相近问题 | 可能召回主题相近、具体条件却不匹配的内容 |

###### BM25 和 BGE-M3 分别怎么工作？

面试回答：

BM25 是基于倒排索引的关键词相关性计算，主要看查询词出现的频率、词的区分度和文档长度。少见且准确命中的词通常更有价值，同一个词重复出现带来的加分会逐渐饱和，避免长文档单纯靠重复词占优势。

BGE-M3 在这里使用 dense 向量能力。入库时把子块编码成向量，查询时把用户问题用兼容的模型和编码方式生成向量，再交给 Milvus 查找相近结果。BM25 更直接利用词面匹配，BGE-M3 补充不同表达之间的语义关联，两路返回后按同一子块 ID 进行融合。

实现细节：

BGE-M3 支持多种检索形式，本项目这一路使用 dense，关键词召回仍由 BM25 完成。查询与文档必须保持模型版本和编码配置兼容，否则向量距离无法按原有口径比较。

###### 为什么用 RRF，不能直接把两路分数相加吗？

面试回答：

BM25 分数和向量相似度的含义、数值范围都不同，直接相加会让数值范围更大的通道主导结果。我使用 RRF，把各路结果转换成排名贡献，再按稳定的子块 ID 合并，减少对原始分数尺度的依赖。

一个子块如果在两路里都排名靠前，就会得到两路加分；只被一路召回的候选也能保留。它的局限是看排名时会丢掉原始分数差距，无法区分第一名比第二名强很多还是只强一点。因此 RRF 用于融合候选，后面继续用 Reranker 联合读取问题和正文，做更细的相关性判断。

实现细节：

计算使用 `RRF(d) = Σᵢ wᵢ / (k + rankᵢ(d))`。排名从 1 开始，`wᵢ` 是通道权重，未出现在某一路中的候选不获得该路分数。`k` 是平滑常数，与召回 Top-K 数量无关。同一子块分别排第 2、第 4 时，融合分数为两路贡献之和。

###### 已经做了 RRF，为什么还要 BGE Reranker？

面试回答：

RRF 依据候选在两路中的排名来融合，不能直接判断正文是否完整回答了问题。BGE Reranker 会把问题和候选子块作为一对输入，让模型联合读取，进一步判断它们的相关性。

比如两段内容都讨论补贴，语义上都接近，但用户问的是申请材料，Reranker 可以更细地判断哪一段真正列出了材料要求。因为这种联合评分成本较高，我们只对融合后的有限候选使用。地区、权限、时间和版本仍然由硬条件过滤处理，相关性分数再高也不能让不适用的资料进入回答。

实现细节：

双塔向量召回分别编码查询和子块，重排则联合输入二者。两者的计算方式与成本不同。RRF 改变候选池，Reranker 在候选池内排序，候选阶段漏掉的依据无法靠精排补回。

| 环节 | 依据 | 作用与边界 |
| --- | --- | --- |
| RRF | 候选在各通道中的排名 | 合并不同通道，不读取正文判断具体条件 |
| BGE Reranker | 问题与候选文本联合输入 | 精细判断相关性，成本较高 |
| 硬条件过滤 | 权限、地区、时间和版本 | 确定证据是否适用，不能由相关性分数替代 |

##### 实现与调参

###### 完整检索链路与权限、时间过滤如何配合？

面试回答：

收到问题后，先确定用户可访问的知识库、地区和时间范围，把能够提前确定的过滤条件下推到两路检索。BM25 和 BGE-M3 并行召回子块，按子块 ID 对齐后做 RRF 融合，再交给 BGE Reranker 精排。

候选返回后，还会回查当前文档状态、版本和权限，防止索引更新滞后。确认可用后，按父块聚合、补充条款上下文，并按 token 预算装配证据，最终生成带 Evidence ID、文档和页码的回答。时间过滤根据用户问题确定，历史查询保留当时适用的版本，不能一律只取最新文件。

实现细节：

检索前过滤减少无效候选占位，检索后回查处理状态变化和索引滞后。发布、生效与失效时间分别记录；当前查询与历史查询采用各自的适用时间，具体链路见步骤。

1. 接收用户问题，确定知识库、权限和时间条件。
2. BM25 与 BGE-M3 向量检索并行召回子块。
3. 按子块 ID 对齐结果，进行 RRF 融合。
4. BGE Reranker 对候选子块精排。
5. 回查文档状态、版本和权限。
6. 按父块聚合，补充上下文并控制 token 预算。
7. 生成带 Evidence ID、文档和页码的回答。

###### 两路各召回多少条？

面试回答：

我们的初始配置是 BM25 和向量检索各取 Top 50，按子块 ID 去重后最多得到 100 个候选，RRF 融合后取前 50 个交给 Reranker，再保留前 10 个子块。之后回溯父块、去重，并根据上下文预算决定真正装入多少证据。

前面保留较多候选，是给后续融合和精排留筛选空间。Top 20 可能过早漏掉正确证据，继续扩大又会增加重排耗时，所以比较 20、50、100 等配置下的候选覆盖率、最终 Recall@10、MRR@10 和延迟。最终保留 10 个子块也是效果与成本的配置选择，父块去重后数量可能更少，并不等于给模型塞 10 个父块。

实现细节：

表中记录初始配置，不把某个 Top-K 当作所有数据集通用的最优值。两路召回名额可根据有效候选分布分别调整。子块排序指标在父块扩展前统计，扩展后的条件覆盖另行检查。

| 阶段 | 初始配置数量 | 输入与输出 |
| --- | --- | --- |
| BM25 | Top 50 | 查询 → 关键词候选子块 |
| BGE-M3 / Milvus | Top 50 | 查询向量 → 语义候选子块 |
| ID 对齐与 RRF | 去重后最多 100，取前 50 | 两路候选 → 重排候选 |
| BGE Reranker | 保留前 10 个子块 | 问题与候选正文 → 精排结果 |
| 父块回溯 | 由去重和 token 预算决定 | 10 个子块不等于 10 个父块 |

###### RRF 参数怎么调？

面试回答：

我主要调平滑常数 k 和两路权重，初始采用 k 等于 60、BM25 与向量权重 1 比 1。先固定召回和重排配置，比较 k 为 20、60、100 时的效果；k 小时更强调头部名次，k 大时头部与后部排名贡献的差距会缩小。

然后根据错误分析调整权重。精确名称、编号类问题不足时，比较偏向 BM25 的配置；口语化问题漏召回较多时，比较偏向向量检索的配置。每次少量改变参数，同时看重排前的证据覆盖和重排后的 Recall@10、MRR@10，避免只看最终排序，忽略候选池已经发生的变化。

实现细节：

权重比较范围包括 `1:1`、`1.5:1`、`1:1.5`。在调参集上选择配置，最终结果使用未参与调参的题目。RRF 的平滑常数不控制召回数量，不能将 `k=60` 理解为召回 60 条。

| 参数 | 初始配置 | 调参比较 | 主要观察 |
| --- | --- | --- | --- |
| 平滑常数 k | 60 | 20、60、100 | 头部与后部排名贡献的差距 |
| BM25 : 向量权重 | 1:1 | 1:1、1.5:1、1:1.5 | 精确术语与口语表达的覆盖 |
| 候选与最终结果 | 配置分别记录 | 分开调参，固定其他条件 | 重排前覆盖率、最终 Recall@10 与 MRR@10 |

###### Reranker 输入如何截断？

面试回答：

Reranker 的输入包含问题、文档标题、章节路径和子块正文。我用对应模型的 tokenizer 计算总长度，先给问题和标题预留空间，再分配正文预算。因为重排对象是子块，大部分输入本身较短，通常不需要直接处理整个父块。

遇到超长表格或片段时，按段落、句子或表格结构拆成多个窗口，每个窗口保留必要标题、表头，分别评分，再用最高分代表该候选的相关性。这样避免统一截取开头，把后面的关键条款删掉。原文和页码映射始终保留，后续生成时仍按需要读取原始证据。

实现细节：

1024 tokens 可以作为工程输入预算，但必须以所选 Reranker 的实际支持长度为前提，不能视为所有 BGE Reranker 的统一上限。正文预算扣除问题、标题和模型输入所需空间后计算，不能按字符数直接截断。

###### 某一路故障怎么降级？

面试回答：

两路检索设置独立超时，某一路失败时尽量保留另一路的有效结果。BM25 不可用就使用向量候选，向量编码或 Milvus 不可用就使用 BM25 候选；如果 Reranker 失败，就用 RRF 排序结果兜底，并记录实际走了哪条链路。

降级后仍要检查证据是否足够，必要时补查、澄清或说明缺少依据。正常检索无命中和通道故障分开处理，前者可以改写查询，后者做有限重试和降级。权限、版本校验属于证据放行条件，这些检查不可用时不能跳过，否则可用性恢复了，访问范围和答案适用性却失去保证。

实现细节：

记录通道错误、耗时和实际使用链路，便于判断质量变化来自模型还是降级。重试受总超时预算约束，不能反复等待单路服务拖垮整个请求。

| 情况 | 可用路径 | 限制 |
| --- | --- | --- |
| BM25 故障 | 使用向量候选继续处理 | 记录通道降级 |
| 向量编码或 Milvus 故障 | 使用 BM25 候选继续处理 | 记录通道降级 |
| Reranker 故障 | 使用 RRF 排序兜底 | 检查证据是否充分 |
| 正常返回但无命中 | 改写查询或澄清条件 | 区别于通道故障 |
| 权限或版本校验不可用 | 不放行未经校验的证据 | 不能为可用性跳过访问检查 |

##### 评测与结果

###### 评测集怎么构建？

面试回答：

我们围绕养老政策、机构制度和服务资料构建了 200 余道题，覆盖精确条款查询、口语化表达、多条件查询、时间版本和政策对比，也加入无答案、条件不足的题目。每道题记录问题、参考答案、文档版本、原文证据位置，以及回答必须覆盖的条件。

标注时区分“主题相关”和“足以回答”，不能把所有提到补贴的片段都算正确证据。检索评测检查原文依据有没有被召回，回答评测再看条件和引用是否完整。调参题和最终评测题分开，近似改写放在同一组，避免泄漏；无答案题单独检查证据不足与澄清行为，不混进普通 Recall 的分母。

实现细节：

标准证据标到原文条款或范围，方便不同分块方案映射。题型分别承担定位词匹配、语义匹配、条件完整性和版本适用性问题的作用，不能只报告总分而不检查错误类型。

###### 测评基线具体是什么？

面试回答：

我把整体效果比较和组件对照分开。整体比较对应原始系统与最终系统，简历里的 Recall@10 从 0.71 到 0.89、MRR@10 从 0.62 到 0.78，反映的是整套改进的收益。

为了判断各环节的作用，另外固定文档、父子分块、过滤规则和测评集，比较 BGE-M3 单路、BM25 单路、两路加 RRF，以及在混合检索上增加 Reranker。这样能分别观察单路特点、混合召回和精排的贡献。分块方式再单独做实验，每次保存模型版本、召回数量、RRF 参数和重排配置，避免同时改动多个环节后无法解释效果来源。

实现细节：

原始系统的完整检索、分块与重排配置需要和结果一起保存在实验记录中，不能仅凭两项旧指标反推它等于某个单路组。组件对照使用固定分块，父子分块收益使用另一组控制变量实验。

| 组别 | 检索配置 | 比较目的 |
| --- | --- | --- |
| A | BGE-M3 单路向量检索 | 观察语义单路表现 |
| B | BM25 单路检索 | 观察关键词单路表现 |
| C | BM25 + 向量 + RRF | 观察两路互补与融合收益 |
| D | C + Reranker | 观察精排收益 |
| 原始系统 → 最终系统 | 固定评测口径，另存完整配置 | Recall@10：0.71 → 0.89；MRR@10：0.62 → 0.78，为整体收益 |

##### 易混点与回答检查

> **易混点**：RRF 是排名融合，Reranker 是模型精排；RRF 的 k 与召回 Top-K 含义不同。

**回答检查**

- [ ] 能串起每路 50 → 最多 100 → 融合取 50 → 重排取 10 → 父块扩展。
- [ ] 能区分初始配置、调参范围和实测结果，并说明整体改进与分阶段收益的区别。
- [ ] 能回答通道故障如何降级，以及为什么权限和版本校验不能降级放行。

##### 补充记录

指标口径与证据：待核实。

#### ReAct Agent 与 Plan-and-Execute

简历表述：基于 ReAct 实现检索和工具调用，引入 Plan-and-Execute 支持多步任务规划与执行，并结合滑动窗口和历史摘要管理上下文。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| ReAct | 根据每次工具结果决定继续检索、补充条件、澄清或回答 |
| Plan-and-Execute | 将复杂任务拆成步骤，跟踪完成情况，按执行结果调整计划 |
| 配合方式 | 计划管理整个任务，步骤内部按需使用 ReAct |
| 执行边界 | 后端校验工具参数、权限、知识库范围和时间条件；设置执行上限 |
| 上下文管理 | 滑动窗口与历史摘要管理对话，计划、状态和证据单独跟踪 |
| 完成条件 | 覆盖问题要求，检查版本、必要条件和引用，说明缺失或冲突 |
| 计划与恢复 | MySQL 持久化结构化计划、checkpoint 和调用记录；恢复不重置累计预算 |
| 防止重复 | 复用仍适用的查询结果，检查证据增量，调整无进展的检索方向 |
| 摘要约束 | 地区、时间、对象和输出要求单独保存，摘要保留来源与确认状态 |

##### 面试主回答

我们在问答流程中使用 ReAct，让 Agent 根据工具结果判断下一步。它可以选择知识检索工具、补充查询条件，证据不足时继续检索，信息缺失时向用户澄清。工具执行前，后端统一检查参数、权限和检索范围。

对政策变化分析和机构制度对比这类任务，我们引入 Plan-and-Execute，先明确对比对象、时间和维度，再拆成查版本、提取条款、比较差异等步骤。每一步内部仍可以采用 ReAct 补充证据，执行结果也可以触发计划调整。

对话上下文通过滑动窗口和历史摘要控制长度，计划和证据单独跟踪。结束前检查问题要求是否覆盖、引用是否齐全，同时设置执行上限，避免重复检索和无限调用。最终答案关联 Evidence ID、文档和页码，方便回到原文核对。

##### 原理与选型

###### ReAct 是什么，怎么运行？

面试回答：

ReAct 是让模型判断和工具调用交替进行的一种 Agent 执行方式。在我们的养老知识问答里，模型会根据当前问题和已经拿到的证据，决定下一步调用哪个工具；工具返回结果后，再判断是否需要继续检索，还是已经可以回答。

比如用户询问能否申请护理补贴，Agent 会先检查地区、照护等级这些必要条件是否齐全，缺少时向用户澄清。查到政策后，如果申请条件还引用了另一份认定办法，就继续检索对应资料，补齐回答依据。

模型负责提出工具调用和参数，后端负责参数、权限及时间范围校验。同时设置调用次数和超时上限，结束前检查证据与引用是否齐全，避免反复检索或依据不足就给出结论。

实现细节：

执行循环为接收问题、判断证据缺口、生成调用、后端校验执行、回传结果、再次判断。后续动作取决于实际工具结果，工具错误也作为结果返回，不能预先假定每次检索都成功。

###### 为什么还需要 Plan-and-Execute？

面试回答：

ReAct 适合根据当前结果决定下一步，但政策变化分析通常包含多个版本和比较维度，如果一直局部补查，容易漏掉子问题，或者反复搜索同一部分。所以我们在复杂任务中引入 Plan-and-Execute，先拆解目标，再按步骤执行和检查完成情况。

比如比较政策调整前后的申请条件、补贴标准和材料要求，要先确认地区和时间，找到两个适用版本，再分别提取这些维度，最后核对差异和引用。计划会记录哪些步骤已完成、缺什么证据。如果执行中发现文件被替代或附件缺失，可以调整计划；某一步需要动态补查时，内部仍然使用 ReAct。

实现细节：

计划按任务目标、比较维度和依赖拆解，完成条件与证据关联。补充检索解决局部缺口，重规划处理目标分解或依赖发生变化的情况，并保留已有有效证据与执行记录。

###### 何时使用 ReAct、何时使用 Plan-and-Execute？

面试回答：

我主要根据对象数量、时间版本、输出维度和步骤依赖判断。查询一个明确条款时，直接检索就可以；需要根据工具结果继续查附件或补条件时，使用有执行上限的 ReAct 循环。

如果任务同时比较多个版本或机构制度，还要求从多个维度给出结论，就先用 Plan-and-Execute 管理整个任务，避免漏项。计划中的某一步仍然可以用 ReAct，比如查旧版申请条件时，先查正文，再根据引用补查认定办法。简单任务不额外生成计划，减少不必要的耗时和模型调用，判断依据也不能只是问题字数。

实现细节：

整体计划负责步骤、依赖和完成状态；步骤内部的 ReAct 负责根据工具结果继续行动。复杂度由任务结构决定，同样长度的问题可能具有完全不同的证据要求。

| 场景 | 处理方式 |
| --- | --- |
| 查询一个明确条款 | 直接检索，必要时补查 |
| 根据工具结果继续调查 | 有边界的 ReAct 循环 |
| 政策变化分析、多机构制度对比 | 先规划再执行，步骤内部按需使用 ReAct |

##### 工具与计划

###### 工具怎样定义？

面试回答：

我按明确的业务能力定义工具，比如知识检索、读取证据原文和查询文档版本。每个工具都提供名称、适用场景、输入 Schema 和结构化返回值，让模型知道什么时候调用、参数怎么填、结果代表什么。

以知识检索为例，输入包含查询文本、文档类型、时间条件和返回数量，成功时返回片段、Evidence ID、文档版本和页码；失败时返回错误类型和是否可重试。用户身份和可访问范围由后端认证上下文确定，模型填写参数不能扩大权限。结果正文也控制长度，先返回必要片段，需要完整条款时再调用原文读取工具。

实现细节：

工具描述应区分检索、原文读取与版本查询的用途。返回状态明确区分成功无命中、参数错误和服务异常，便于模型选择改写查询、修正参数或结束。数量与字段类型仍由后端检查。

| 部分 | 知识检索工具示例 |
| --- | --- |
| 工具名称 | `search_knowledge` |
| 用途 | 根据问题检索政策或机构制度证据 |
| 输入 | 查询文本、文档类型、时间条件、返回数量等 |
| 成功返回 | Evidence ID、文档版本、页码、片段及检索状态 |
| 失败返回 | 错误类型、是否可重试、错误说明 |

###### 模型如何选择工具？

面试回答：

后端会把当前任务允许使用的工具说明和参数 Schema 提供给模型。模型结合用户目标、当前步骤和已有结果，生成结构化的工具名和参数；后端根据工具名找到处理器，完成类型、范围与权限校验后执行，再把结果交回模型。

工具说明要写清能力边界，比如找资料用检索工具，已经拿到 Evidence ID 但需要完整条款时用原文读取工具，确认某个时间点适用哪个文件时用版本查询工具。模型提出的调用不会直接执行，未知工具、错误参数或越界数量会被拒绝并返回具体错误，允许模型在合法范围内修正。

实现细节：

工具是否可用由后端决定，模型选择只发生在当前允许的能力范围内。调用参数不能充当身份或权限凭据，结果再次进入上下文后才触发下一轮判断。

###### 计划用什么结构保存？

面试回答：

我们把计划作为结构化任务保存在 MySQL，包含目标、地区和时间等约束、计划版本、步骤列表及依赖。每个步骤记录自己的状态、完成条件、工具执行记录和 Evidence ID，根任务还保存已用步数、调用次数和截止时间。

这样恢复时能够明确知道哪些已经完成、下一步的依赖是否满足，而不必让模型重新解释一段自然语言计划。比较长的工具结果单独存，计划通过记录 ID 关联。需要重规划时保留旧计划和执行记录，生成新版本，已经拿到的证据经适用性检查后继续复用，避免整个任务从头再来。

实现细节：

任务身份和范围也随根任务持久化。计划状态、工具原始结果和对话摘要分别保存；摘要便于理解背景，不能承担步骤状态与恢复位置的全部职责。具体字段见下表。

| 层级 | 主要字段 |
| --- | --- |
| 根任务 | `taskId`、用户与权限范围、目标、状态 |
| 约束 | 地区、时间、文档范围、对比维度 |
| 计划 | `planVersion`、步骤列表 |
| 每个步骤 | `stepId`、目标、依赖、完成条件、状态 |
| 执行记录 | 工具及参数、结果、错误、Evidence ID |
| 执行预算 | 已用步数、模型与工具调用次数、截止时间 |

##### 执行与恢复

###### Agent 怎么知道任务完成了？

面试回答：

我把完成判断和用户要求关联起来，检查输出维度是否覆盖、证据是否适用、引用是否齐全，以及还有没有未解决项。比如政策对比要找到两个正确版本，申请条件、标准和材料都完成比较，每个差异都有对应原文，才能认为主要目标完成。

工具返回成功只说明这一次调用正常，不能直接当成任务完成。我们同时设置步数、调用次数和总超时上限。如果到上限仍缺资料，就保留已经确认的部分，明确说明缺哪项证据或条件。这样既有内容上的完成条件，也有执行预算，避免无限检索。

实现细节：

政策对比还要核对地区、适用时间及文件关系。附件缺失、条款冲突等未解决项需要显式记录；达到预算上限属于停止执行的原因，不代表所有问题已经得到回答。

###### 任务如何暂停、恢复和限制步数？

面试回答：

暂停时保存任务、步骤状态、checkpoint、已有证据和累计预算，并停止发起新调用。恢复时先加载这些记录，重新检查当前权限、文档版本和证据适用性，再从未完成的位置继续，不重复执行已确认完成的部分。

正在进行的外部调用是否能取消，要看工具支持情况，不能认为点击暂停就已经停止。对于已发出但结果不确定的调用，恢复时先核对调用记录或查询结果。步数、重规划次数、模型和工具调用次数以及总时间都按同一个任务累计，重试和恢复也计入，避免每次恢复重新获得完整额度。

实现细节：

暂停点应位于可保存状态、可判断后续行为的安全边界。证据失效或权限变化时，恢复位置需相应调整；较长结果通过记录 ID 读取，并继续遵守上下文预算。

| 环节 | 保存或检查 | 处理原则 |
| --- | --- | --- |
| 暂停 | 任务、步骤、checkpoint、证据与已用预算 | 保留已经完成的部分 |
| 恢复 | 当前权限、证据适用性与未完成位置 | 从可继续的位置执行 |
| 重试与重规划 | 累计步数、调用次数和截止时间 | 共用累计预算，不重新计零 |

###### 工具失败怎么处理？

面试回答：

我根据错误类型决定后续动作。参数错误就返回具体字段问题，让模型修正；超时和连接异常在剩余预算内有限重试；单路检索不可用时，使用其他可用通道。权限不足则停止这次访问，不能靠修改参数绕过。

工具正常执行但没有结果，要考虑改写查询、补条件或说明未找到，不能当作服务故障盲目重试。一次失败也不一定让整个任务重来，比如正文已经取得但附件获取失败，会保留正文证据，只继续处理附件缺口。必要依据始终拿不到时，返回已确认部分，并说明哪些结论暂时无法给出。

实现细节：

工具返回应包含错误类型、错误说明和可重试信息。重试与降级计入累计预算，已完成步骤和已取得证据保留；附件未取得时，不生成依赖附件内容的确定结论。

| 失败情况 | 处理方式 |
| --- | --- |
| 参数错误 | 返回明确字段错误，让模型修正 |
| 超时、连接异常 | 在剩余预算内有限重试 |
| 单路检索不可用 | 使用其他可用通道并记录降级 |
| 权限不足 | 停止该访问，不能通过换参数绕过 |
| 正常执行但无结果 | 改写查询、补充条件或说明未找到 |
| 必要证据无法取得 | 保留已完成部分，说明缺失项 |

###### 如何避免重复检索？

面试回答：

我会记录每次查询的文本、时间与知识库等过滤范围，以及返回的 Evidence ID。再次调用前，先检查相同请求是否已有可复用结果，权限和版本仍适用时直接复用。

另外还要看检索有没有推进任务。模型即使换了措辞，如果一直返回同一批证据，也没有覆盖新的问题维度，就需要调整检索方向。比如补贴标准已经查到，缺少申请材料，下一次就查材料或附件，不再宽泛地搜索补贴政策。判断重复也不能只看文本，同一句问题查询不同地区、时间或版本，范围不同，仍然属于不同请求。

实现细节：

请求复用关注查询与过滤条件是否一致，进展判断关注新增 Evidence ID 和未覆盖维度。缓存或复用前检查当前权限、版本与时间适用性，不能为了减少调用继续使用失效证据。

##### 上下文与摘要

###### 上下文如何裁剪？

面试回答：

我把当前问题、用户刚补充的条件、任务约束和当前步骤放在优先位置，再装配相关证据、近期消息和历史摘要。近期对话用滑动窗口保留，较早内容压缩为摘要；计划、步骤状态和证据记录单独跟踪，不完全依赖聊天历史。

检索结果先去重并提取相关片段，长工具结果移出上下文后保留记录 ID，需要时再读。计算预算时把工具 Schema、消息结构和引用信息也算进去，并给模型输出预留空间。裁剪不能丢掉条款的必要条件或工具调用与返回的对应关系，摘要里的政策描述也不能直接代替当前适用的原文证据。

实现细节：

上下文装配同时管理对话背景、任务状态和证据，三者用途见表。旧结果可通过记录 ID 恢复，最终回答重新确认权限、时间与文档版本，避免摘要延续过时结论。

| 内容 | 作用 |
| --- | --- |
| 最近消息与历史摘要 | 理解用户意图和对话背景 |
| 计划、步骤状态、已取得的证据 | 跟踪当前任务进展 |
| 文档、版本、页码和 Evidence ID | 核对结论依据 |

###### 摘要如何防止遗漏关键条件？

面试回答：

我们把地区、时间、对象和输出要求单独保存成结构化任务约束，每次装配上下文都明确提供给模型。摘要主要压缩对话背景，关键条件不依赖摘要模型自行记住，所以长对话压缩后仍能按同样范围检索和回答。

用户修改条件时，更新对应字段，后续查询使用新值；没有提供的信息继续保持缺失，不能让摘要自动补齐。摘要里还区分用户明确提供、工具已确认和尚未确认的内容，对重要事实保留来源消息或证据记录。发生不一致时，回到明确的用户要求和原始记录核对，避免错误摘要被不断沿用。

实现细节：

政策比较任务的结构化约束包括地区、适用时间、指定机构或版本、比较维度、引用要求和尚缺信息。任务状态与约束独立维护，不能把“已讨论过”误当成“已确认”。

##### 易混点与回答检查

> **易混点**：计划管理整个任务，ReAct 可执行其中一步；摘要不承担结构化计划与任务状态的全部职责。

**回答检查**

- [ ] 能说明什么时候直接检索、动态补查或先规划。
- [ ] 能讲清工具校验、计划恢复、累计预算、证据增量和关键约束保存。

##### 补充记录

事实 / 代码依据：待补充。

#### 政策变化分析与证据溯源

简历表述：支持查询改写与多轮补充检索，按指定时间、政策有效期和文档版本筛选证据，并关联 Evidence ID、文档和页码。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 分析目标 | 明确比较对象和时间，逐维度提取条款，每项差异关联新旧证据 |
| 处理链路 | 明确范围 → 定位适用版本 → 查正文及附件 → 提取条款 → 比较 → 引用回答 |
| 时间与版本 | 区分发布、生效、失效、入库时间；区分政策版本与索引构建版本 |
| 补充检索 | 查询改写保留约束，针对缺失条款、附件和补充通知补查 |
| 证据溯源 | Evidence ID 关联文档版本、原文位置、页码和片段 |
| 结果检查 | 核对引用有效性与结论支持情况，明确缺失证据和未解决冲突 |

##### 面试主回答

我们针对政策变化分析，把用户问题拆成政策对象、地区、时间和比较维度，再定位调整前后的适用文件，分别提取条件、标准和材料要求。查询改写保留这些约束，正文引用附件或补充通知时，再做针对性的补充检索。

版本处理会区分发布时间和生效时间，结合修订、替代及废止关系判断适用性，避免只取最新上传的文件。比较时按维度对齐条款，保留对象范围、单位和例外条件；遇到无法确认的冲突就保留原文说明。

每条证据关联 Evidence ID、文档版本、页码和原文片段，回答中的具体结论引用对应证据。生成后检查引用有效性和结论支持情况，让用户能回到原文核对；证据不足的部分明确说明，不补造差异。

##### 分析流程与检索

###### 政策变化分析具体在做什么？

面试回答：

政策变化分析要先确定比较的是哪个地区、哪项政策、哪个调整时间，再找到调整前后各自适用的文件。之后按照用户关心的维度提取条款，比如申请条件、补贴标准和申请材料，对齐后说明哪些新增、修改或保持不变。

每一项变化都要关联新旧证据，正文之外还检查附件和补充通知。Agent 负责组织这些查询和比较步骤，生成前再检查维度是否齐全、版本是否适用。不能只查到新文件就凭模型记忆描述旧规定，也不能把不同时间的条款拼成同一套当前政策。

实现细节：

完整处理顺序见步骤。输出保留比较维度和两侧依据，遇到旧文件或附件缺失时单独记录，不能因新版本未命中某条要求就认定它已经取消。

1. 明确地区、政策与调整时间。
2. 定位调整前后的适用文件。
3. 检查正文、附件和补充通知。
4. 分别提取申请条件、补贴标准和材料要求。
5. 按维度比较，为每项差异关联新旧证据。
6. 生成对比结果。

###### 为什么需要查询改写和多轮补充检索？

面试回答：

用户用语和政策原文经常不一致，而且回答依据可能分散在正文、附件和补充通知里。所以第一步把用户表达转成明确的检索目标，同时保留地区、时间、政策项目等约束，缺条件就先澄清。

比如“补贴变了多少”，需要明确比较金额、覆盖对象还是发放方式。第一轮查到正文后，如果材料要求写着详见附件，就继续读附件；金额规定引用补充通知，就针对那份通知补查。每一轮围绕具体证据缺口展开，避免不断搜索同一个宽泛问题，也不能为了扩大召回把地区和时间条件丢掉。

实现细节：

查询改写只改变检索表达，不擅自更改用户问题范围。补充检索以尚缺文档、条款或比较维度为目标，并复用已经取得且仍适用的证据。

##### 时间、版本与差异

###### 时间条件和文档版本怎么处理？

面试回答：

入库时我们分别记录发布时间、生效时间、失效时间，以及修订、补充、替代和废止关系。查询时先判断用户问当前政策、历史某个时间点，还是新旧对比，再按地区和适用时间选择对应证据。

比如文件六月发布、七月生效，用户问六月的申请规则，就不能直接采用新规定。历史查询也要允许读取当时有效、现在已失效的政策。另外，政策版本和索引构建版本分开管理，重新解析、分块或编码只是技术处理变化，不代表政策发生修订；刚入库的文件也不能直接认定为最新政策。

实现细节：

发布时间、生效时间、失效时间与入库时间的含义见表。版本选择还需要检查文件业务关系与适用对象，不能只按上传顺序或发布日期排序后取第一份。

| 时间 | 含义 |
| --- | --- |
| 发布时间 | 文件什么时候发布 |
| 生效时间 | 规定从什么时候开始适用 |
| 失效时间 | 规定从什么时候不再适用 |
| 入库时间 | 系统什么时候收录文件 |

###### 怎么判断变化，遇到版本冲突怎么办？

面试回答：

我按同一个比较维度对齐新旧条款，分别判断新增、删除、修改或未变化，保留对象范围、单位和例外条件。比如一个版本按每人每月补贴，另一个按服务次数结算，不能直接把两个数字相减，就认定补贴提高或降低。

遇到看起来冲突的文件，先核对地区、适用对象和时间，再检查明确的修订、替代或废止关系，以及正文和附件、一般规定和特殊情形的区别。发布日期更晚不能自动说明覆盖旧文件。关系仍无法确认时，保留两侧原文并说明冲突；新版没检索到某项要求，也会检查附件和关联文件，再判断是否真的取消。

实现细节：

变化分类与核对重点见表。删除判断需要文件关系或完整材料支持，检索未命中只是当前证据状态。比较金额前统一计量周期、对象与单位；无法统一的情形分别说明。

| 变化或冲突 | 判断重点 |
| --- | --- |
| 新增 | 新版本明确增加了什么要求，并保留依据 |
| 删除 | 核对修订或废止关系，不能只因新检索未命中就判断删除 |
| 修改 | 同一比较维度的新旧条款分别是什么 |
| 未变化 | 对应规定及适用范围保持一致 |
| 表面冲突 | 先检查地区、对象、时间、正文附件与文件关系，不能只按发布时间取最新 |

##### 引用与回答校验

###### Evidence ID 和引用如何绑定原文片段？

面试回答：

检索完成后，我把可用结果整理成结构化证据记录，每条分配 Evidence ID，关联文档及版本、父子块、原文片段、页码和适用时间。模型只引用本次提供的已有 ID，最终由后端映射成文档和页码，方便用户回到原文核对。

引用还要和具体结论对应。比如回答申请材料新增了某项证明，需要同时给出旧版清单和新版要求。生成后，后端检查引用 ID 是否存在、是否属于本次可用证据、当前是否仍可访问，还要核对原文是否支持这句话。只有 ID 正确但内容不支持结论，仍然不能算有效引用。

实现细节：

Evidence ID 标识证据记录，字段分工见表。跨页结论保留实际支持它的页码范围；模型不得自造 ID，版本和原文位置绑定后不随索引重建被错误替换。

| 字段 | 用途 |
| --- | --- |
| Evidence ID | 标识本条证据 |
| 文档 ID、版本 ID | 确认来源文件与版本 |
| 父子块 ID、原文位置 | 定位具体条款 |
| 页码或页码范围 | 跳转查看原文 |
| 原文片段 | 支持回答中的具体结论 |
| 适用时间、来源信息 | 判断证据适用性 |

###### 证据不足时如何响应？

面试回答：

我先判断缺的是用户条件还是文档依据。地区、时间或对象不明确时，向用户澄清；正文指向的附件、通知还没取得时，继续针对缺口检索。已经确认的部分可以正常回答，尚缺依据的部分单独说明。

如果达到执行预算仍未查齐，就明确告诉用户当前哪些结论有依据、哪些暂时无法判断，以及还缺什么材料。没有检索到某条规定，只能说明当前证据不足，不能直接说这项规定不存在，也不能让模型用常识补成确定的政策结论。这样回答能够保留已有价值，又让用户知道依据覆盖到了哪里。

实现细节：

停止补查的原因与结论可信程度分别记录。服务故障、正常无命中和缺少用户条件需要区分，避免把系统暂时不可用表述成政策没有相关要求。

##### 易混点与回答检查

> **易混点**：发布日期不等于生效日期；新检索未命中不等于条款被废止；引用存在不等于引用支持结论。

**回答检查**

- [ ] 能按比较维度对齐新旧版本，说明正文、附件及补充通知的作用。
- [ ] 能解释 Evidence ID 如何绑定版本、位置和页码，以及证据不足如何处理。

##### 补充记录

事实 / 代码依据：待补充。

## 经历二：设备智能运维平台

### 项目概览

#### 项目背景与职责

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 研究背景 | 课题组在农业机械领域的大模型智能体应用研究 |
| 应用环境 | 设备智能运维平台承载业务数据，验证智能体能力 |
| 场景目标 | 结合动态业务记录与专业资料，辅助知识查询、故障排查和调度方案准备 |
| 个人职责 | 后端业务与权限、Agent 工具调用、文档检索、记忆、图像识别及 MCP 接入 |
| 执行边界 | 平台检查权限、资源与状态，涉及业务写入的方案经人工确认后执行并审计 |
| 检索评测 | Recall@10：0.945；MRR@10：0.798；NDCG@10：0.839 |

##### 面试主回答

背景：

这个项目属于课题组在农业机械领域的大模型智能体应用研究，设备智能运维平台是承载业务数据和验证智能体能力的应用环境。平台围绕农机资产、作业、维保和成本等业务，探索智能体如何结合设备状态、业务记录和专业资料，辅助完成知识查询、故障排查及调度方案准备。

农机场景中的问题通常需要同时使用两类信息。一类是说明书、维修手册和操作规范，包含设备结构、故障原因和维护要求；另一类是平台中的动态业务数据，例如设备当前是否可用、是否存在维修工单、作业安排是否冲突。只查手册，无法判断具体设备的实际情况；只查业务数据，又缺少解释故障和提出维护建议的专业依据。

因此，项目将知识检索与业务工具结合，让智能体能够围绕具体问题获取信息、形成建议，并提供可核对的依据。

涉及调度和维保安排时，智能体生成的方案还需要经过资源检查和人工确认。平台负责权限、状态流转和执行审计，便于验证建议是否符合业务约束，以及确认后是否被正确执行。

职责：

我主要参与后端业务模块和大模型应用部分。一方面，使用 Spring Boot、MyBatis 和 MySQL 开发农机资产、作业任务、维保、成本及经营分析相关接口，结合组织、角色和成员数据范围实现多租户访问控制。这些业务能力也作为智能体的工具入口，让它能够查询实际记录，并通过已有业务流程准备和执行方案。

另一方面，我参与 Agent 工作流与工具调用的实现，将业务查询、知识检索和确定性校验串起来。例如准备调度方案时，需要先查询任务要求和可用设备，再检查资源与时间冲突；生成维保建议时，需要结合设备记录和专业资料。工具执行统一处理参数、权限、超时和执行预算，涉及业务写入的方案由人工确认后执行，并记录审计日志。

知识问答部分，我负责文档处理与检索链路，通过 RabbitMQ 异步处理 PDF 渲染和文本提取，使用 MinerU 补充 OCR，并基于 ColQwen 实现页面视觉检索，以保留维修资料中的表格、图示和版面信息。

回答前检查文档权限与版本，返回带页码的引用；同时通过离线测评观察检索效果，Recall@10、MRR@10 和 NDCG@10 分别达到 0.945、0.798 和 0.839。

我还参与了对话记忆、图像识别服务和 MCP Server 的接入。记忆用于保留近期上下文和召回历史信息，动态设备状态仍通过业务工具查询；图像识别返回候选机型及置信度，供人工复核后关联资产；MCP Server 则将机型查询、图像识别和知识问答等能力提供给支持该协议的客户端。

##### 易混点与回答检查

> **易混点**：研究背景是课题组在农业机械领域的大模型智能体应用研究，设备智能运维平台承载业务与能力验证。

**回答检查**

- [ ] 能区分动态业务查询、专业资料检索与需要人工确认的业务写入。
- [ ] 能串起个人负责的后端、Agent、记忆、文档、识别和 MCP 能力。

### 简历要点复盘

#### 后端业务模块与多租户权限

简历表述：负责农机资产、作业任务、维保、成本及经营分析等后端模块；基于 Spring Security、JWT 和组织/角色/成员范围实现隔离与访问控制。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 业务模块 | 农机资产、作业任务、维保管理、成本管理、经营分析 |
| 租户模型 | 组织作为租户，单数据库共享业务表，通过组织字段和应用层权限隔离 |
| 认证与授权 | Spring Security、JWT 建立身份；业务层检查成员关系、角色、数据范围和状态 |
| 权限判断 | 身份有效 + 成员有效 + 操作权限 + 数据范围 + 业务状态允许 |
| 查询与写入 | 同时限定资源 ID 和组织范围，按需附加成员范围及状态条件 |
| 异步与 Agent | 恢复发起人上下文，实际执行前重新校验，通过业务 Service 执行 |

##### 面试主回答

我参与了资产、作业、维保、成本和经营分析等后端模块，使用 Spring Boot、MyBatis 和 MySQL 实现接口、状态流转及统计查询。这些能力也通过业务 Service 提供给智能体工具，确保 Agent 与普通接口遵循相同规则。

多租户以组织为单位，采用共享表和应用层组织隔离。Spring Security 与 JWT 负责登录认证，业务层结合有效成员关系、角色、数据范围和资源状态完成授权。角色决定能做什么，数据范围决定能操作哪些记录。

查询和更新都限定组织范围，详情、导出、下载和批量操作同样校验。后台任务和 Agent 执行时恢复发起人上下文，并在实际操作前重新检查权限，避免任务创建后撤权或资源变化造成越权执行。

##### 业务划分

###### 后端业务模块怎么划分？

面试回答：

我围绕农机运营流程划分资产、作业、维保、成本和经营分析模块。资产模块维护机型与具体设备，作业模块管理任务和资源安排，维保模块处理维修保养流转，成本模块管理单据与审核，经营分析按统一时间和组织口径汇总数据。

接口层由 Controller 接收请求，Service 组织业务和校验，MyBatis 完成持久化。跨模块通过公开 Service 协作，比如调度时检查设备状态、已有任务和维保占用，不直接改其他模块的表。这样普通接口和 Agent 都能复用同一套权限、状态流转和资源规则，模型提出方案后仍由后端判断能否执行。

实现细节：

模块职责与主要规则见表。资产档案与标准机型区分，统计按有效业务记录和时间口径汇总；跨模块调用不能绕过对方业务状态与数据范围检查。

| 模块 | 主要职责 | 需要处理的规则 |
| --- | --- | --- |
| 农机资产 | 维护机型、具体设备档案及关联信息 | 资产归属、生命周期、可调度状态 |
| 作业任务 | 创建任务、安排设备与执行人、记录执行情况 | 资源可用性、时间冲突、任务状态 |
| 维保管理 | 保养计划、维修工单、检查与复机验收 | 工单流转、维修期间的设备使用限制 |
| 成本管理 | 成本单据、明细、提交与审核 | 单据状态、审核权限、统计口径 |
| 经营分析 | 汇总作业、维保和成本数据 | 组织范围、有效记录和时间口径 |

##### 租户与权限设计

###### 多租户具体指什么？

面试回答：

我们以组织作为租户，采用单数据库共享业务表，通过组织字段和应用层授权隔离数据。每个组织有自己的成员、设备、作业、维保和成本记录，用户在当前有效组织内查询和操作，知道其他组织的资源 ID 也不能访问。

同时区分平台公共资料和组织私有数据。标准机型、公共知识按规则共享，组织资产和私有知识只在授权范围内开放。服务端会核对当前成员关系与数据范围，不能直接相信请求里填写的组织 ID。平台管理员承担平台治理，也不会因为拥有平台角色就自动获得所有组织的业务明细。

实现细节：

组织隔离只是第一层，同一组织内部还可能存在成员或任务分配范围。公共标准机型与组织具体资产分别建模，避免把公共读取能力扩大成组织业务访问权。

###### Spring Security、JWT 和业务权限分别做什么？

面试回答：

JWT 用来携带可验证的登录身份，Spring Security 校验签名和有效期、建立认证上下文并控制请求入口。进入业务层后，还要检查账号和当前组织成员关系、角色操作权限、资源数据范围以及业务状态。

所以令牌有效只能证明登录凭据仍有效，不能代表用户当前仍有全部业务权限。比如用户登录后被移出组织，后续操作会按当前成员关系拒绝；用户在请求中切换组织，也要由服务端重新确认成员资格。查询和写入最终都基于经过验证的上下文限定范围，避免只在登录时检查一次权限。

实现细节：

检查次序为身份、账号及有效成员关系、操作权限、资源归属与数据范围、业务状态，最后执行。令牌内容和客户端传入的 `organizationId` 均不能独立替代当前业务授权。

| 环节 | 职责 | 不能替代的检查 |
| --- | --- | --- |
| Spring Security / JWT | 校验签名、有效期并建立请求身份 | 不能仅凭令牌有效就认定业务授权 |
| 成员与角色 | 检查当前组织成员关系及操作权限 | 仍需检查具体资源范围 |
| 资源与状态 | 核对归属、数据范围和当前业务状态 | 写入时按需附加预期状态并检查影响行数 |

###### 角色权限和数据范围有什么区别？

面试回答：

角色权限决定用户能进行哪类操作，数据范围决定这次操作能作用于哪些记录。比如农机手具备执行任务的权限，但只能处理分配给自己的任务；维保员可以处理工单，也只能访问授权范围内的工单。

之后还要检查业务状态。即使用户有权限、资源也属于本组织，已经结束且不允许修改的工单仍然不能更新。因此一次操作要同时满足身份与成员关系有效、具有操作权限、记录处于数据范围内、当前状态允许，不能只凭一个角色名称就放行。

实现细节：

操作权限、记录范围、状态规则和当前成员关系的区别见表。组织成员身份不意味着可以读取本组织全部记录，平台角色也不自动授予组织私有数据访问权。

| 检查层次 | 回答的问题 | 例子 |
| --- | --- | --- |
| 角色权限 | 能做什么操作 | 能否处理维保工单 |
| 数据范围 | 能对哪些记录操作 | 仅授权或分配给自己的工单 |
| 业务状态 | 这条记录现在是否允许操作 | 已结束且不允许修改的工单拒绝更新 |
| 组织成员关系 | 当前是否仍属于该组织 | 被移出组织后旧 JWT 不授予原权限 |

###### 资源状态如何校验？

面试回答：

资源状态校验放在业务 Service 中，与权限检查分别执行。比如修改维保工单，先确认用户有权操作且工单在授权范围内，再判断它当前是否处于允许修改的状态，已经结束的工单不能因为用户有权限就继续改。

对于检查后可能发生并发变化的操作，实际更新时还会带上预期状态，并检查影响行数。如果其他请求已经推进状态，当前更新就不能继续当作成功，需要返回状态已变化的结果。这样避免只在写入前查一次状态，随后被其他请求修改，却仍然按旧判断执行。

实现细节：

状态条件与资源 ID、组织范围共同进入需要保护的更新语句。更新影响零行时，不能直接返回成功，需要结合资源与状态检查说明原因；权限和状态各自负责不同约束。

##### 查询、写入与执行边界

###### 怎么防止跨租户查询和越权修改？

面试回答：

我把服务端验证过的组织和数据范围带进实际查询与更新条件。查详情时同时限定资源 ID、组织和有效状态，必要时再限定任务分配或成员范围；写入时同样带组织范围和所需的预期状态，并检查影响行数，避免查询有校验、更新却只按主键。

这套限制也覆盖导出、附件下载、统计和批量操作，不能只保护列表接口。批量请求逐项确认资源是否属于当前范围，请求里填写其他组织 ID 不能扩大权限。Agent 和异步任务调用业务 Service 时也复用这些规则，确保入口变了，资源边界仍然一致。

实现细节：

下面的 SQL 展示资源、组织与状态条件如何进入实际读写。`organizationId` 来自服务端验证的上下文，不能由模型或客户端自报决定；批量请求不能只验证其中一个 ID。

```sql
SELECT ...
FROM machinery_assets
WHERE id = #{assetId}
  AND organization_id = #{organizationId}
  AND deleted = 0;
```

```sql
UPDATE ...
SET ...
WHERE id = #{resourceId}
  AND organization_id = #{organizationId}
  AND status = #{expectedStatus};
```

###### 租户范围如何贯穿异步任务和 Agent？

面试回答：

创建任务时会保存发起人、组织和资源标识。HTTP 请求结束后，原线程的登录上下文不能长期代表后台执行权限，所以 Worker 真正执行时会重新加载有效身份与成员关系，建立本次执行上下文，再调用业务 Service。

Agent 也是模型先提出工具和参数，工具执行器校验用户范围，Service 再检查权限和业务状态。任务创建后如果用户被撤权、成员停用，或者设备状态发生变化，就按执行时的当前事实处理。人工确认只表示接受某份方案，不能替代权限校验，模型填写其他组织 ID 也不能越权。

实现细节：

模型调用、后台任务和普通 HTTP 请求最终进入相同业务边界。任务记录保存执行主体，恢复时重新验证；执行前同时检查草案确认、设备状态和资源冲突。

##### 易混点与回答检查

> **易混点**：JWT 有效不等于业务授权有效；人工确认不替代权限与审批；平台身份不自动获得组织私有数据。

**回答检查**

- [ ] 能按身份 → 成员关系 → 操作权限 → 数据范围 → 业务状态说明检查过程。
- [ ] 能说明查询、更新、导出、下载、批量操作及异步任务如何使用一致的范围控制。

##### 补充记录

事实 / 代码依据：待补充。

#### Redis 缓存与查询优化

简历表述：缓存业务数据、检索向量和近期对话，采用 Cache Aside 并设置 TTL。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 缓存对象 | 业务查询结果、查询向量和近期对话，数据可从事实源重建 |
| Cache Aside | 读缓存 → 未命中回源并回填；数据库事务提交后删除相关缓存 |
| 租户隔离 | 业务 Key 包含组织及数据范围；对话按组织、用户和会话隔离 |
| 向量缓存 | 按模型版本、编码配置和问题区分；命中后仍按当前权限和版本检索 |
| 一致性 | 主动失效与 TTL 控制陈旧时间，业务执行前重新校验当前事实 |
| 故障处理 | 受控回源，对热点 Key 合并重建请求，记录删除失败并补偿 |
| 优化指标 | 命中率、数据库查询次数、embedding 调用次数、平均耗时、P95 与更新可见延迟 |

##### 面试主回答

我们主要用 Redis 缓存业务查询结果、查询向量和近期对话。业务查询采用 Cache Aside，未命中时回源 MySQL 并设置 TTL；业务修改提交后删除相关缓存，让后续请求重建。

多租户缓存 Key 包含组织和必要的数据范围，避免不同用户共用不该共享的结果。查询向量缓存按模型版本、编码配置和问题区分，命中后仍执行当前权限和版本条件下的检索。近期对话按组织、用户和会话隔离，MySQL 保存完整消息。

TTL 按数据变化频率分别设置，Redis 故障时受控回源。涉及调度、审批和权限的操作重新校验当前事实，避免使用陈旧缓存执行。优化效果主要通过命中率、数据库查询次数、向量编码次数和请求 P95 来观察。

##### 缓存对象与读写流程

###### 为什么引入 Redis？

面试回答：

主要是减少几类重复开销。机型资料和部分统计结果会被反复查询，直接回源会重复执行 SQL 和聚合；知识检索中，同一个问题反复生成查询向量也会增加模型调用；多轮问答还需要频繁加载近期消息。

因此我用 Redis 缓存业务读结果、查询向量以及近期对话和常用偏好，让重复请求尽量复用已有结果。MySQL 仍保存业务记录、消息和来源，Redis 中的数据可以回源重建。真正调度、审批或者修改资源时，仍然查询当前业务事实并校验，避免把缓存中的旧状态直接当作执行依据。

实现细节：

业务查询缓存、查询向量缓存与上下文缓存分别减少数据库访问、编码调用和消息加载。评估效果要看对应的完整请求路径，不能仅用 Redis 单次读取速度判断收益。

###### 具体缓存哪些内容？

面试回答：

我把缓存分为三类。业务读缓存保存机型资料、部分查询和经营统计，减少重复 SQL；查询向量缓存保存问题的 embedding，避免重复编码；近期对话缓存保存组织、用户和会话范围内的近期消息，加快上下文装配。

缓存内容需要能够从持久化记录或模型计算重建。资产现在能否调度、任务有没有冲突、工单是否允许提交，这些在实际执行前仍然按当前事实检查。查询向量命中也只省掉编码步骤，后续知识检索依然要带当前权限、时间和版本条件，不能把向量缓存当成可直接复用的答案。

实现细节：

三类缓存的职责见表。查询向量不包含检索结果的访问授权；业务读缓存的范围需要随有效数据范围变化，命中后仍保留必要的访问检查。

| 缓存类型 | 缓存内容 | 主要作用 |
| --- | --- | --- |
| 业务读缓存 | 机型资料、部分业务查询和经营统计结果 | 减少重复 SQL 与聚合计算 |
| 查询向量缓存 | 问题经 embedding 模型编码后的向量 | 避免重复编码 |
| 近期对话缓存 | 当前用户、组织和会话的近期消息 | 加快上下文装配 |

###### Cache Aside 是怎样的流程？

面试回答：

读取时先查 Redis，命中就使用缓存结果，未命中则查 MySQL，再回填并设置 TTL。修改时先提交数据库事务，再删除受影响的缓存，由下一次读取重建。

选择删除而不逐一更新，是因为一次业务修改可能同时影响详情、列表和多个统计结果，全部重新计算容易复杂且遗漏。比如成本单据审核后，要让相关展示和统计失效。删除放在事务提交后，避免其他请求在新事务尚未提交时就查到旧值并重新缓存。并发回填和删除失败仍需单独处理，这个顺序本身不能保证强一致。

实现细节：

读路径为缓存、数据库、回填，写路径为事务提交、缓存失效。受影响缓存需要结合查询依赖确定；提交前删除可能让并发读取重新缓存尚未提交更新前的旧值。

##### Key 与过期策略

###### 多租户系统中，缓存 Key 怎么设计？

面试回答：

Key 要包含所有会影响结果的范围和查询参数。业务详情通常包含组织、资源类型和资源 ID；统计还要加有效数据范围、时间区间和条件；对话按组织、用户和会话隔离；查询向量则按模型版本、编码配置和查询文本区分。

同一个组织里，不同成员能看到的记录可能不同，所以只加组织 ID 还不够，涉及数据范围的结果要继续区分。命中缓存也不能跳过必要的访问校验。查询向量只是文本编码，可以在编码兼容时复用，但真正检索时仍然应用当前用户的知识库、权限、时间和版本条件。

实现细节：

各对象的 Key 组成见表。不要把用户可读结果与单纯查询向量混在同一种缓存里；权限范围不同的结果不能因查询文本相同而共用。

| 缓存对象 | Key 的组成 |
| --- | --- |
| 业务详情 | 组织 ID + 资源类型 + 资源 ID |
| 统计结果 | 组织 ID + 数据范围 + 时间区间 + 查询条件 |
| 近期对话 | 组织 ID + 用户 ID + 会话 ID |
| 查询向量 | embedding 模型版本 + 编码配置 + 查询文本 |

###### 查询向量缓存为什么要包含模型版本？

面试回答：

查询向量必须和文档索引处在兼容的向量空间。模型版本、维度或预处理方式变化后，即使查询文本一样，生成的向量也可能不兼容，所以 Key 中要包含模型和编码配置，升级时使用新的缓存命名空间。

文本规范化只处理无意义差异，比如首尾空白，不能把地区、日期、机型编号删掉，否则不同问题可能共用同一个向量。缓存的是问题编码，文档内容更新后，如果编码配置没变，查询向量仍可使用，再对当前索引执行检索；旧模型向量则不能直接与新索引混用。

实现细节：

缓存版本与文档索引编码版本需要配套管理。模型升级造成的兼容性变化，与普通文档新增、删除造成的内容变化分别处理，后者不自动使所有兼容查询向量失效。

###### TTL 怎么设置？

面试回答：

TTL 根据数据变化频率、回源成本和能接受的陈旧时间分别设置。相对稳定的机型资料可以长一些，频繁变化的业务查询和统计短一些，并配合写入后的主动失效。查询向量在模型不变时比较稳定，过期主要用于控制容量和淘汰旧问题。

近期对话同时限制会话过期时间和消息数量，避免无限增长。大量同类缓存再给基础 TTL 增加少量随机偏移，减少同时失效造成的回源高峰。TTL 只是限制旧值可能保留的时间，在有效期内也可能已经过时，所以执行操作前仍然检查当前业务事实。

实现细节：

不同缓存的过期考虑见表，不统一设置一个固定时长。TTL 与主动失效共同控制展示数据陈旧窗口，权限和执行条件不依赖缓存有效期来保证正确性。

| 缓存对象 | TTL 考虑 | 配合措施 |
| --- | --- | --- |
| 相对稳定的机型资料 | 可保留较长时间 | 变更后主动失效 |
| 频繁变化的业务查询与统计 | 较短时间，控制陈旧窗口 | 写入后主动失效 |
| 查询向量 | 主要控制容量与旧问题淘汰 | 模型或配置变化使用新命名空间 |
| 近期对话 | 会话级过期 | 消息数量上限 |
| 同类缓存 | 基础 TTL 增加少量随机偏移 | 避免集中失效；执行仍检查当前事实 |

##### 一致性与故障处理

###### 缓存和数据库怎么保持一致？

面试回答：

我们采用先提交数据库修改、再删除相关缓存，下一次读取回源重建。对普通展示数据，接受短时间最终一致，通过主动失效和 TTL 限制陈旧窗口；删除失败会记录并补偿，不能只打日志就结束。

这个方案仍然存在并发窗口，比如请求 A 查到旧值，请求 B 更新数据库并删除缓存，之后 A 才把旧值回填。TTL 能限制它存在多久，但不能消除这个窗口。因此调度、审批和权限判断在执行时重新查询当前事实，不直接根据展示缓存决定是否写入。我会把展示一致性的要求和业务执行的正确性分别处理。

实现细节：

需要关注事务提交前失效、提交后删除失败和并发旧值回填三类时序。补偿负责重做失效动作，TTL 负责限制残留旧值时间，二者都不等价于数据库与 Redis 的强一致事务。

###### Redis 不可用或热点缓存失效怎么办？

面试回答：

Redis 不可用时按缓存类型回源。业务读结果回 MySQL，查询向量重新调用编码服务，近期消息从 MySQL 按需要加载。同时限制超时和并发，避免缓存故障把所有压力直接转移到数据库或模型服务。

热点 Key 失效时，会合并同一个 Key 的重建请求，让一个请求加载，其他请求等待或受控回源，减少相同 SQL 和编码任务同时执行。这只是缓存重建保护，不能替代业务事务或状态锁。监控时也区分正常未命中和缓存服务故障，分别看命中率、回源耗时和错误数量。

实现细节：

各缓存的回源路径见表。近期消息读取遵守上下文预算，编码回源限制排队与等待。合并重建只减少同一 Key 的重复计算，业务写入仍按自身并发规则执行。

| 情况 | 回源或恢复 | 保护措施 |
| --- | --- | --- |
| 业务读缓存不可用 | 回源 MySQL | 超时、并发与数据库压力控制 |
| 查询向量缓存不可用 | 重新调用编码服务 | 限制编码并发与等待 |
| 近期消息缓存不可用 | 从 MySQL 加载 | 按上下文预算读取 |
| 热点 Key 失效 | 合并同一 Key 的重建请求 | 避免大量请求重复回源 |

##### 评测与结果

###### 怎么证明缓存带来了查询优化？

面试回答：

我会固定数据规模、请求分布和并发条件，对比启用缓存前后的完整请求。业务查询看缓存命中率、数据库查询次数，以及平均耗时和 P95；查询向量缓存再看实际 embedding 调用次数；同时检查数据更新后多久能在查询结果中看到。

实验还要区分未预热和已预热，避免把一次理想命中当成全部请求表现。只有重复 SQL 或编码次数下降、用户等待改善，并且结果陈旧时间在可接受范围内，才能说明缓存起到了作用。单看 Redis 读取很快，无法证明整个业务接口变快，更不能说明数据更新仍然正确。

实现细节：

指标定义见表。前后请求条件保持一致，预热状态单独记录；把查询性能收益和更新可见延迟一起观察，避免用陈旧结果换取表面上的低延迟。

| 指标 | 观察内容 |
| --- | --- |
| 缓存命中率 | 是否存在足够的重复访问 |
| 数据库查询次数 | 重复 SQL 和聚合查询是否减少 |
| embedding 实际调用次数 | 向量缓存是否减少重复编码 |
| 请求平均耗时、P95 | 用户等待是否改善 |
| 数据更新后的可见延迟 | 是否出现不可接受的陈旧结果 |

##### 易混点与回答检查

> **易混点**：查询向量缓存不是检索结果缓存；TTL 控制陈旧窗口，不能保证强一致；命中缓存不免除访问校验。

**回答检查**

- [ ] 能讲清 Cache Aside 读写顺序、Key 范围、模型版本和主动失效。
- [ ] 能说明并发回填旧值、删除失败补偿与热点回源保护，并用完整请求指标评估。

##### 补充记录

事实 / 代码依据：待补充。

#### Agent 工作流与工具调用

简历表述：覆盖作业调度、维保建议、经营分析和知识问答；校验资源可用性与任务冲突，生成人工确认后执行的草案并记录审计日志。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 工作流 | 理解目标 → 获取信息 → 准备方案 → 后端校验 → 人工确认 → 执行前复核 → 执行与审计 |
| 业务场景 | 作业调度、维保建议、经营分析和知识问答 |
| 模型与规则 | 模型理解目标、组织步骤和提出方案；后端判断权限、资源、冲突和状态 |
| 工具入口 | 统一处理参数、权限、超时和预算，调用已有业务 Service |
| 确认与执行 | 确认关联具体草案版本，关键参数变化后重新确认，确认不替代权限与审批 |
| 重复与失败 | 稳定执行标识、逐项回执，结果不确定时先核对业务状态 |
| 审计 | 关联发起人、草案、确认记录、工具调用、证据和实际业务记录 |

##### 面试主回答

我参与了 Agent 工作流和工具调用的实现，覆盖作业调度、维保建议、经营分析和知识问答。Agent 先理解目标，再调用业务查询和知识检索工具获取信息；涉及业务操作时，先准备草案，由后端检查资源可用性、任务冲突和权限。

工具执行器统一处理参数校验、权限、超时和调用预算，具体操作复用已有业务 Service，模型不直接修改数据库。调度和维保方案经用户确认后执行，确认关联具体草案版本，执行前再次检查当前状态。

写操作通过稳定执行标识和逐项回执处理重复请求及部分失败，结果不确定时先核对业务状态，避免盲目重试。全过程记录确认、工具调用和业务结果，保证建议、草案和实际执行能够区分并追溯。

##### 工作流与业务场景

###### Agent 工作流是什么样的？

面试回答：

这条工作流从理解用户目标开始，先补齐必要条件，再通过业务工具查询当前数据、通过知识工具取得专业资料，生成可检查的草案。后端先校验权限、资源和业务约束，把草案及依据展示给用户，人工确认后才进入实际执行。

比如用户要求安排下周作业并避开维修设备，Agent 先查询任务要求、设备、人员和已有安排，再准备调度方案。确认期间资源可能变化，所以执行前还会复核当前状态和冲突，通过业务 Service 写入，并记录结果与审计。模型负责理解和组织步骤，设备能否调度、用户是否有权操作由后端确定。

实现细节：

目标理解、信息获取、草案、校验、确认、执行与反馈分别记录状态。缺少时间、设备或作业要求时先补问；建议生成成功、草案确认与业务写入成功是不同结果。

###### 不同业务场景怎样调用工具？

面试回答：

我按需要取得的信息选择工具。作业调度查询任务、设备、人员和已有安排；维保建议结合设备档案、维修记录与手册检索；经营分析从业务记录中取得指标；知识问答则检索说明书、规范和资料页，保留引用。

动态状态通过业务工具查询，维修方法这类专业知识通过检索取得，不能把历史对话里的设备状态当成当前事实。金额、工时等指标由确定性的业务查询和计算产生，模型负责组织方案或解释结果，避免它根据零散记录自行估算。各场景最终返回草案、指标或带证据的答案，写操作再经过确认和业务校验。

实现细节：

各场景的信息来源和输出见表。统计结果同时保留组织、时间和有效记录口径；业务状态、专业知识和历史背景各有来源，不能互相替代。

| 场景 | 获取的信息 | 输出 |
| --- | --- | --- |
| 作业调度 | 任务要求、候选设备、人员及已有安排 | 调度草案、冲突与待补充条件 |
| 维保建议 | 设备档案、维保记录、相关手册 | 维保建议及资料依据 |
| 经营分析 | 指定期间的作业、维保和成本记录 | 指标、分析及数据口径 |
| 知识问答 | 说明书、维修资料和操作规范 | 带页码引用的回答 |

##### 工具设计与资源校验

###### 工具怎么定义，为什么不直接让模型操作数据库？

面试回答：

我把查询资产、查询任务、检查冲突、检索维修资料和准备调度草案等能力封装成工具，明确名称、说明、参数 Schema、读写属性、返回结构和幂等要求。执行器统一处理参数、权限、超时和预算，实际业务仍调用已有 Service。

这样可以复用普通接口的组织范围、状态流转和审计规则。比如创建维保工单，需要校验资产、用户权限和必填信息，不能让模型拼一条 SQL 就插入表。模型提出工具和参数，后端决定是否合法并执行；写操作还要关联用户确认和稳定执行标识，避免一次重试产生多条业务记录。

实现细节：

工具元数据的职责见表。数据库持久化留在业务层内部，工具暴露的能力范围应当足够清晰，让模型能区分查询、准备草案和真正执行，避免把建议结果误认为写入回执。

| 内容 | 作用 |
| --- | --- |
| 名称、说明和参数 Schema | 帮助模型选择工具并生成参数 |
| 读写属性与权限要求 | 控制调用范围 |
| 超时与重试规则 | 限制执行开销 |
| 结构化结果 | 返回证据、草案、错误或执行回执 |
| 幂等要求 | 避免重试产生重复业务记录 |

###### 调度方案如何检查资源和任务冲突？

面试回答：

后端根据草案中的设备、执行人和时间范围做确定性检查。先确认资源存在且在授权范围内，再看设备当前状态、维保占用、已有任务，以及人员和设备在相关时段是否冲突，同时核对作业要求和候选资源是否匹配。

失败时返回具体资源、冲突记录和原因，Agent 据此换设备、调时间或请求补齐条件。用户确认期间其他请求可能改变安排，所以真正写入前还要再次复核，并在业务写入环节控制并发，避免两个请求都查到空闲后同时占用。模型生成方案本身不能代表资源已经被预留。

实现细节：

检查项与处理方式见表。方案校验解决当前草案是否可行，执行复核与并发控制保护实际写入；两者缺一时，确认等待期间的状态变化都可能造成错误安排。

| 检查项 | 依据 | 失败后的处理 |
| --- | --- | --- |
| 资源与范围 | 设备、执行人及当前授权范围 | 返回具体资源和原因 |
| 可用状态 | 设备当前状态及维保占用 | 更换候选或调整安排 |
| 时间冲突 | 已有任务与相关时段占用 | 调整时间或候选 |
| 作业匹配 | 作业要求与候选资源条件 | 补齐条件或重新准备方案 |
| 写入前复核 | 当前安排与并发约束 | 避免检查后被其他请求占用 |

##### 人工确认与执行恢复

###### 人工确认确认的是什么？

面试回答：

用户确认的是一份内容确定的草案，里面明确设备、操作、时间和关键参数。后端把真实确认动作关联到具体草案版本，执行也使用这个版本；设备、时间或其他关键参数改动后，要重新展示并确认，不能沿用旧确认执行新方案。

确认表示用户接受这份安排，权限和审批仍按业务规则检查。执行时会重新核对成员关系、资源状态和任务冲突，模型生成一句“用户已同意”不能替代确认记录。这样既知道用户同意了什么，也能保证实际执行内容与确认内容一致。

实现细节：

草案展示、真实确认、关键参数变化和执行校验的关系见表。确认记录需要可核验，业务审批是否满足仍由业务流程判断，确认不会扩大角色或数据权限。

| 时点 | 检查内容 | 处理方式 |
| --- | --- | --- |
| 展示草案 | 资源、操作、时间与关键参数 | 关联具体草案版本 |
| 用户确认 | 真实用户操作及对应版本 | 保存可核验确认记录 |
| 关键参数变化 | 设备、时间等是否变化 | 重新展示并确认 |
| 实际执行 | 当前权限、资源状态、冲突及审批要求 | 重新检查，确认不扩大权限 |

###### 执行失败或重复调用怎么办？

面试回答：

每个业务动作都有稳定执行标识和执行记录。重复请求先查记录，已经成功的返回已有业务结果，避免再次创建。一个批量方案有部分成功、部分冲突时，逐项保存回执，只处理未完成或需要修正的项，不能整体重放。

写操作超时尤其要先核对执行记录和实际业务状态，因为可能已经写入，只是响应没回来。确认未执行后才考虑重试，重试时继续检查权限、草案版本和确认状态。只读查询可以在预算内有限重试；明确失败的写操作返回具体原因，必要时调整草案并重新确认。

实现细节：

明确成功、明确失败与结果不确定的后续动作见表。稳定执行标识关联具体动作与草案版本，不能用旧版本请求替代新方案；逐项回执保留生成或修改的业务记录 ID。

| 结果 | 后续处理 |
| --- | --- |
| 明确成功 | 保存业务记录 ID，重复请求返回已有结果 |
| 明确失败 | 返回原因，修正或重新准备方案 |
| 请求超时、结果不确定 | 先查询执行记录与业务状态，再决定是否重试 |

##### 审计与结果追踪

###### 审计日志记录什么？

面试回答：

审计要能够还原谁提出了什么目标、确认了哪份草案、调用过哪些工具，以及最终影响了哪些业务记录。因此会记录发起人、组织、任务和草案版本，关键参数与用户确认，工具调用时间、状态、错误、证据和执行回执。

我会区分建议、待确认草案和已执行结果。比如草案生成成功，只说明模型已经提出方案，不能记成调度成功；最终还要关联创建或修改的业务记录 ID，说明哪些动作完成、哪些失败。界面和日志采用同样的状态区分，方便定位失败、处理重复请求和回溯实际变更。

实现细节：

工具调用成功与业务操作成功分别记录，不能只保留一份模型对话。审计关联真实确认与业务回执，支持从任务查调用、从调用查结果，也能核对实际执行是否对应确认版本。

##### 易混点与回答检查

> **易混点**：建议、待确认草案和已执行结果是不同状态；写操作超时不等于没有执行成功。

**回答检查**

- [ ] 能讲清草案版本、用户确认、执行前复核和业务 Service 的边界。
- [ ] 能回答部分成功、重复调用与结果不确定时如何恢复并记录审计。

##### 补充记录

事实 / 代码依据：待补充。

#### 短期、长期及实体记忆

简历表述：MySQL 持久化消息与历史事件，Redis 存用户偏好并缓存近期对话，Qdrant 提供可重建的语义检索；通过滑动窗口与摘要压缩支持长期记忆召回。

##### 快速回顾

| 项目 | 内容 |
| --- | --- |
| 短期记忆 | 当前会话的近期消息与条件，滑动窗口结合历史摘要 |
| 长期记忆 | 有来源的历史背景、重要事件和明确偏好，支持跨会话召回 |
| 实体记忆 | 用稳定业务 ID 关联设备、机型与任务，辅助指代消解 |
| 存储分工 | MySQL 保存事实与来源；Redis 缓存近期对话和偏好；Qdrant 保存可重建向量 |
| 召回链路 | 范围过滤 → 语义召回 → 回查来源与权限 → 去重及冲突检查 → 预算内装配 |
| 更新与删除 | 区分临时指令和长期偏好，处理旧值替代、投影更新及遗忘控制 |
| 业务边界 | 记忆帮助理解背景与定位对象，动态状态和权限仍通过业务服务校验 |

##### 面试主回答

我们把记忆分为当前会话的短期上下文、跨会话的长期信息，以及帮助识别设备和任务的实体关联。MySQL 保存消息、历史事件和记忆来源，Redis 缓存近期对话与常用偏好，Qdrant 提供可重建的语义召回。

当前对话通过滑动窗口和历史摘要控制长度，计划与执行状态单独保存。长期记忆整理用户明确表达的偏好和历史任务背景，实体关联使用稳定业务 ID，遇到多个候选时再向用户确认。

召回时先按组织和用户过滤，再回查来源、权限与有效状态，最后按预算装配上下文。记忆用于理解背景和定位对象，设备状态、任务冲突和维修进展仍通过业务工具查询，避免旧信息直接影响当前操作。

##### 记忆类型与存储分工

###### 短期、长期及实体记忆分别是什么？

面试回答：

短期记忆服务于当前对话，比如保留刚确认的设备和时间，理解下一句里的“它”或“改到明天”。长期记忆保存跨会话可复用的背景、重要事件和明确偏好。实体记忆则把用户提到的机器、机型或工单关联到稳定业务对象，解决到底说的是谁。

例如先查资产 A 的维保记录，再问它为什么停机，近期对话帮助确定指代；过一段时间问上次那台收割机的维修进展，长期记忆找回讨论背景，实体关联定位资产，再用业务工具查当前进展。这些是用途上的区分，可以重叠，也不分别对应某一种数据库。

实现细节：

三类记忆的对象和目的见表。实体关联回答对象是谁，历史背景回答过去发生或表达过什么；设备当前能否调度和维修是否完成，仍以当前业务查询为准。

| 类型 | 保存什么 | 解决什么问题 |
| --- | --- | --- |
| 短期记忆 | 当前会话的近期消息、刚补充的条件 | 理解追问、指代和当前任务 |
| 长期记忆 | 历史任务背景、重要事件和明确偏好 | 跨会话找回相关信息 |
| 实体记忆 | 用户提到的设备、机型、任务等实体及关联 | 识别这台机器、上次那张工单等指代 |

###### MySQL、Redis 和 Qdrant 怎么分工？

面试回答：

MySQL 保存完整消息、历史事件、记忆记录、实体关联和来源，是可以回查的持久化记录。Redis 缓存近期对话与常用偏好，设置 TTL 和容量上限，加快上下文加载。Qdrant 保存记忆文本向量和过滤字段，用语义检索找出跨会话可能相关的历史信息。

召回后还会回查 MySQL，检查记录是否有效、来源是否可访问。Redis 过期只意味着缓存需要重建，Qdrant 向量丢失也可以从有效记录重新编码。记忆用的是文本 embedding，知识库文档页用 ColQwen 视觉多向量，两者的用途和向量空间不同，不能直接混在一起检索。

实现细节：

存储职责见表。清理缓存、删除长期记忆和重建向量投影是不同操作，不能仅通过 Redis Key 是否存在判断记忆是否仍有效。

| 组件 | 主要职责 |
| --- | --- |
| MySQL | 保存完整消息、历史事件、记忆记录、实体关联及来源 |
| Redis | 缓存近期对话和常用偏好，设置 TTL 与容量上限 |
| Qdrant | 保存记忆文本向量及过滤字段，支持跨会话语义检索 |

##### 记忆形成与实体关联

###### 短期记忆怎么管理？

面试回答：

短期记忆用滑动窗口保留最近的有效消息，较早内容压缩成历史摘要。窗口里优先保留用户刚修改的时间、设备编号和当前任务条件，裁剪按 token 预算做，避免只按消息条数限制，遇到单条很长的工具结果就失控。

长结果只提取相关内容，工具调用与返回保持对应。计划、草案、用户确认和执行结果单独持久化，恢复任务时读取这些结构化记录，不能只靠聊天摘要猜做到哪一步。这样近期对话负责理解追问，任务状态负责保证执行连续性，二者配合控制上下文长度。

实现细节：

窗口条数不能代表实际 token 数，工具 Schema、返回正文和摘要都要计入预算。用户新修改的条件覆盖当前任务旧值，重要约束与执行状态独立于摘要保存。

###### 长期记忆怎么形成，哪些内容值得保存？

面试回答：

长期记忆从历史消息和事件中整理可复用的信息，比如用户明确表达的展示偏好、历史任务背景、讨论过的设备和故障。每条保留来源、时间和组织用户范围，持久化后再为有效内容建立向量索引，便于后续语义召回。

记录时区分建议和事实。Agent 提议下周保养，不能记成保养已经安排，只有实际执行成功后才能关联对应业务记录。设备当时维修可以保留为历史背景，但下次调度还要查当前状态。长期保存的价值在于减少用户重复交代背景，同时让信息有来源可核对。

实现细节：

可保存明确的展示偏好，例如维保建议按紧急程度排序。记忆区分用户陈述、已确认业务事件与模型建议；动态状态保留时间属性，不能在后续对话中自动升级成当前事实。

###### 实体记忆怎么保存，如何避免认错设备？

面试回答：

实体关联用稳定业务 ID 保存，名称、别名、机型和来源消息辅助匹配。用户说“那台收割机”时，最终要关联到具体 assetId，不能只记住机型，因为同一机型可能对应多台资产，一台资产又可能有多张工单。

召回候选后再通过业务查询确认对象和当前访问权。多个设备都符合描述时，让用户选择或补充信息，不直接取第一条。关联完成后，记忆帮助后续指代定位，但不会授予权限，也不能把工单 ID、机型 ID 和资产 ID 混为同一个对象。

实现细节：

实体关联同时保留类型、稳定 ID 与来源，避免跨类型同名误配。访问权在使用时检查，用户过去讨论过某台设备，不代表现在仍可读取它的资料。

##### 召回、更新与隔离

###### 长期记忆怎么召回并放进上下文？

面试回答：

先根据当前问题和明确实体确定检索范围，按组织、用户和有效状态过滤，再从 Qdrant 召回相关历史信息。之后回查 MySQL 里的记录与来源权限，去重并检查时间和冲突，挑选有用片段放入上下文。

相似度高只说明可能相关，旧偏好已被修改、来源已撤权或记录失效时都不能继续使用。装配时优先保留当前问题、明确约束和当前任务证据，长期记忆控制在预算内，不能挤掉关键资料。偏好可以影响答案展示方式，但不能改变权限、审批规则或者确定性计算出来的指标。

实现细节：

召回顺序为范围过滤、语义检索、来源回查、有效性与冲突检查、预算装配。近期消息、历史摘要、长期信息和业务证据共同使用，但分别承担背景理解、任务执行与结论依据。

###### 新旧记忆冲突、用户修改偏好怎么办？

面试回答：

先区分信息类型。用户明确修改长期偏好时，更新可定位的偏好记录，保留版本和来源，用新值替代旧值，并同步 Redis 缓存和 Qdrant 投影。只针对当前任务的指令，就只影响这次，不自动覆盖长期设置。

比如默认展示未来两周维保，用户说“这次只看本周”，长期默认值不变。历史事件则按时间分别保存，设备发生第二次故障不能抹掉第一次；动态业务状态只作为当时背景，现在的状态重新查询。这样冲突处理依据来源、时间和作用范围，不能让所有相似记忆简单互相覆盖。

实现细节：

不同内容的更新方式见表。投影更新存在时间差时，召回回查当前有效记录，避免旧值继续生效；当前任务的新要求优先用于本次执行，但不自动形成永久偏好。

| 内容类型 | 例子 | 更新方式 |
| --- | --- | --- |
| 明确长期偏好 | 默认查看未来两周维保 | 新值替代旧值，保留来源与版本并更新投影 |
| 当前任务指令 | 这次只看本周 | 仅影响当前任务，不自动覆盖长期偏好 |
| 历史事件 | 同一设备先后发生两次故障 | 按时间分别保留，不互相覆盖 |
| 动态业务状态 | 设备当时正在维修 | 只作历史背景，当前状态重新查询 |

###### 多租户隔离和删除怎么处理？

面试回答：

记忆的缓存和向量过滤都带组织、用户及相应会话范围，召回后还会回查当前记录和来源权限。用户过去能读某段会话，不代表现在仍有权使用它的派生内容；来源已经不可读时，相关记忆也不能继续注入上下文。

删除或修改以 MySQL 有效记录为准，再同步清理缓存和向量投影。向量异步更新期间，回查可以阻止旧投影生效。用户明确要求遗忘时，还要保留对应遗忘控制，防止下一轮整理旧消息又抽取出同一条信息，仅删除当前一条向量无法完成这种持续遗忘。

实现细节：

删除覆盖持久化记忆、缓存与向量投影，来源保留场景下还需要阻止重新抽取。派生记忆的可访问性不能超出来源，向量过滤和回查共同处理多租户隔离与撤权。

##### 评测与结果

###### 怎么判断记忆有没有帮助？

面试回答：

我会对同一批历史任务，比较启用长期召回和只用近期对话的表现。先看是否找到了相关背景、是否定位到正确设备或工单，用户是否少重复说明信息，再看跨会话任务能不能正确完成。

同时检查过时和冲突信息有没有被错误注入，尤其不能让历史设备状态干扰当前调度，或者绕过权限检查。最后记录额外的上下文 token 和耗时，判断收益是否值得。如果只是召回更多历史文本，却没有改善对象定位和任务完成，甚至带来旧信息干扰，就不能认为记忆有效。

实现细节：

指标与核对内容见表。对照保持任务、历史来源和执行条件一致，分别观察背景复用、实体定位、当前事实正确性及成本，不用召回条数单独衡量效果。

| 指标 | 检查内容 |
| --- | --- |
| 召回相关性 | 历史信息是否与当前问题相关 |
| 实体定位准确率 | 是否关联正确资产、机型或工单 |
| 过时与冲突比例 | 是否注入被替代或不再适用的信息 |
| 跨会话任务完成情况 | 是否减少重复背景补充且正确完成任务 |
| 上下文 token 与耗时 | 记忆带来的额外成本 |

##### 易混点与回答检查

> **易混点**：记忆类型描述用途，不与数据库一一对应；历史设备状态不能当作当前事实；缓存过期不等于长期记忆删除。

**回答检查**

- [ ] 能说明稳定实体 ID、来源、时间、有效性和权限为何参与召回。
- [ ] 能区分临时指令、长期偏好与历史事件，并解释遗忘后如何防止再次抽取。

##### 补充记录

事实 / 代码依据：待补充。

#### 文档处理、视觉检索与问答评测

简历表述：RabbitMQ 异步处理 PDF 页面渲染与文本提取，MinerU 补充 OCR；ColQwen 实现页面视觉检索，校验文档权限和版本并生成带页码引用的回答；Recall@10、MRR@10、NDCG@10 分别为 0.945、0.798、0.839。

##### 快速回顾

| 复盘点 | 核心内容 |
| --- | --- |
| 业务问题 | 农机手册包含表格、结构图和操作示意，纯文本提取容易丢失版面和图文关系 |
| 入库链路 | 上传 → RabbitMQ 任务 → 页面渲染与文本提取 → 必要的 MinerU OCR → 页面编码与索引 → 校验后可检索 |
| 数据分工 | OSS 保存原文件和页面图片；MySQL 保存文档版本、页面和任务状态；Qdrant 保存检索向量 |
| 视觉检索 | ColQwen 对页面和查询生成多向量，通过后期交互匹配相关页面 |
| 生成依据 | 读取页面图片或对应文本，校验权限和版本，返回支持结论的页码引用 |
| 评测单位 | 按问题标注标准证据页，区分召回覆盖、排序质量和最终回答质量 |
| 测评结果 | Recall@10 = 0.945；MRR@10 = 0.798；NDCG@10 = 0.839 |

##### 面试主回答

我负责文档处理和知识问答链路，上传后通过 RabbitMQ 异步执行 PDF 页面渲染、原生文本提取和必要的 MinerU OCR。OSS 保存原文件和页面图片，MySQL 保存文档版本、页面及任务状态，Qdrant 保存检索向量。

农机资料包含较多结构图、表格和操作示意，纯文本提取容易丢失这些信息，所以我们使用 ColQwen 对页面编码，以多向量匹配查询和页面，找到相关证据。回答时读取页面图片或对应文本，检查文档权限和版本，并返回页码引用。

评测按问题标注标准证据页，分别检查召回覆盖和排序质量，Recall@10、MRR@10、NDCG@10 达到 0.945、0.798 和 0.839。同时区分检索效果与回答质量，进一步检查答案完整性和引用支持情况。

##### 文档处理与异步任务

###### 文档从上传到可检索，经过哪些步骤？

面试回答：

上传后先把 PDF 原文件存到 OSS，在 MySQL 创建文档版本和处理任务，再通过 RabbitMQ 交给后台 Worker。后台按页渲染图片、提取原生文本，扫描页或解析质量不足的页面用 MinerU 补充 OCR 和结构化解析，再用 ColQwen 编码页面，把多向量和过滤元数据写入 Qdrant。

处理完成后会校验页面、文本和索引产物，确认完整才把该版本标记为可检索。OSS 保留原文件与页面图片，MySQL 保存版本、页面关系和任务状态，Qdrant 是可重建的检索索引。各阶段持久化进度，失败从对应阶段或页面恢复，稳定标识保证重复执行更新同一份产物，避免重复建页和索引。

实现细节：

组件产物与处理顺序见表和步骤。上传成功只代表原文件保存完成，异步任务成功还需确认产物完整。页面、任务和索引之间保留稳定关联，以支持恢复和原文引用。

| 阶段 | 主要产物或状态 | 存储或处理组件 |
| --- | --- | --- |
| 原文件上传 | PDF、文档版本与任务记录 | OSS + MySQL |
| 页面处理 | 页面图片、原生文本 | 后台 Worker；图片进入 OSS |
| 补充解析 | 扫描页或低质量页面的 OCR / 结构化内容 | MinerU |
| 页面编码与索引 | 页面多向量及过滤元数据 | ColQwen + Qdrant |
| 结果校验 | 产物完整性、任务进度与可检索状态 | MySQL 保存状态，失败阶段可恢复 |

1. 上传 PDF，将原文件存入 OSS，在 MySQL 创建文档版本和处理任务。
2. 投递异步任务，解析 PDF 页数，渲染页面图片并提取原生文本。
3. 对扫描页、缺失文本或解析质量不足的页面，使用 MinerU 补充 OCR 和结构化解析。
4. 使用 ColQwen 编码页面，将检索向量及过滤元数据写入 Qdrant。
5. 校验页面、文本和索引产物，更新任务状态，使处理完成的文档版本可以被检索。

###### 为什么用 RabbitMQ 异步处理？

面试回答：

文档页面渲染、OCR 和模型编码耗时长，大文件、扫描件的资源消耗差异也很大。如果都放在上传接口里，会长时间占用请求线程，用户也不容易区分上传失败还是后台解析失败。所以接口上传后返回任务标识，后台按资源能力处理，用户查询进度。

这里采用 RabbitMQ，主要因为它的任务队列、消费确认和路由机制适合平台当前的任务分发需求，也与已有技术栈匹配。MySQL 保存任务状态，消息触发执行，产物和状态持久化后再确认消息。临时故障有限重试，损坏文件等确定性错误记录原因并终止无效重试。Kafka 也能承担异步处理，具体取舍要结合平台需求。

实现细节：

消费者并发按照页面渲染、OCR 和编码服务的资源能力控制。队列消息确认与业务可检索状态分开，处理成功后同时核对产物与状态，避免只确认消息却丢失阶段结果。

###### 已经提取文本，为什么还需要 MinerU？

面试回答：

PDF 提取到字符不代表内容已经可用于问答。有些是扫描页，根本没有原生文本；有些多栏页面会出现阅读顺序错乱；表格只剩连续文字后，行列、单位和附注关系也容易丢失。

所以我们先提取原生文本，再按页面质量决定是否使用 MinerU 补充 OCR 和结构化解析，重点检查空文本、明显乱码和缺失页面。页面图片始终保留，因为 OCR 提供可读文字，但结构图、操作示意和位置关系仍需要视觉信息。这样把文字解析和页面证据配合起来，避免仅凭一份质量不足的提取文本生成答案。

实现细节：

质量判断按页面进行，避免一份 PDF 中部分正常页面掩盖扫描页或异常页。表格解析需要保留行列与注释关系；视觉信息不能仅凭提取出了部件名称就认为已经完整恢复。

##### 视觉检索与证据使用

###### ColQwen 是什么，为什么适合这个场景？

面试回答：

ColQwen 是面向文档页面的多模态检索模型，可以把页面图片编码成多个向量，再与查询做细粒度匹配。农机资料中有大量表格、结构图和操作示意，纯文本提取可能丢掉布局或部件位置关系，页面检索能利用这些局部视觉信息查找相关页。

比如查询液压回路中某个阀的位置，只有部件名称的文本未必能还原它与管路的关系。我们用页面多向量定位证据，再读取对应文字、数值和图例核对。它的代价是页面编码、向量存储和匹配成本更高，因此要同时评估检索效果与资源开销；找到相关页也不等于结论已经正确。

实现细节：

常见文本检索与页面检索的差异见表。ColQwen 用于检索页面，不直接承担农机机型分类；具体型号、数值和图例仍需以页面原文核对。

| 比较点 | 常见文本向量检索 | ColQwen 页面检索 |
| --- | --- | --- |
| 检索对象 | 提取后的文本块 | 文档页面图片 |
| 向量表示 | 通常每个文本块一个向量 | 每个页面多个向量 |
| 主要保留的信息 | 提取出的文字语义 | 页面文字、布局和视觉信息 |
| 资源开销 | 通常较低 | 页面编码、存储和匹配开销更高 |

###### 多向量检索怎么匹配？Qdrant 保存什么？

面试回答：

查询和文档页面都编码成多向量，通过 MaxSim 后期交互计算相关性。可以理解成查询的每个向量去页面向量中找最相似的一项，再汇总这些最大相似度，这样保留问题不同部分与页面局部内容的匹配关系，不必先把整页压成单个向量。

Qdrant 保存页面多向量，以及文档、版本、页码和组织等过滤元数据。原文件与页面图片仍在 OSS，文档和页面业务关系在 MySQL，召回后要回查原始证据。查询与页面编码保持模型和配置兼容，升级导致向量空间变化时要重建相应索引，不能把不同模型的向量直接混用。

实现细节：

MaxSim 先对每个查询向量取页面侧最大相似度，再汇总得到页面分数。向量与元数据承担匹配和初筛，不能代替 OSS 中原文件或 MySQL 中当前有效的版本与访问状态。

###### 视觉检索和 OCR 是什么关系？

面试回答：

视觉检索主要负责定位相关页面，原生文本或 OCR 提供模型可读取的文字、表格与注释。比如用户查故障码，先找到故障码表所在页，再读对应行、列标题和适用条件，不能只命中这一页就直接生成结论。

生成模型支持图像时，可以提供页面图片和相关文本；只支持文本时，就提供原生文本、OCR 或结构化解析结果。页面检索向量只用于匹配，不能当作原文证据输入。视觉与文字结果通过稳定的文档版本和页面标识关联，如果两路同时召回，先对齐去重再融合，避免直接相加尺度不同的原始分数。

实现细节：

各环节的输入与职责见表。OCR 与视觉检索相互补充，结构图的位置关系可能需要图片，表格数值和条件需要读取正确行列，不能用检索分数代替证据核验。

| 环节 | 输入与输出 | 主要职责 |
| --- | --- | --- |
| 视觉检索 | 查询与页面多向量 → 相关页面 | 定位证据页 |
| 原生文本 / OCR | 页面 → 可读文本或结构化内容 | 提供文字、表格行列及注释信息 |
| 多模态生成 | 页面图片及相关文本 → 回答 | 结合视觉证据解释内容 |
| 纯文本生成 | 原生文本、OCR 或结构化内容 → 回答 | 需先提供可读证据，不能输入检索向量代替原文 |

###### 文档权限、版本和页码引用怎么处理？

面试回答：

召回前用 Qdrant 元数据按访问范围过滤，缩小候选集合；候选返回后再回查 MySQL 中当前权限、文档状态和版本，防止索引滞后时继续使用已撤权、已删除或不适用的资料。通过检查的页面才能进入生成上下文。

引用绑定具体文档版本和页面标识，并区分 PDF 的物理页序和文档印刷页码，确保用户能定位同一份文件里的同一页。每条结论关联实际支持它的页面，跨页条件保留多个页码。不能因为某页排第一，就把整份回答里的所有结论都引用到这一页。

实现细节：

索引元数据负责候选初筛，当前业务记录负责最终放行。原文跳转使用稳定版本与页面关联，显示页码和内部页序需要对应清楚，避免引用同名文件的其他版本。

##### 评测与结果

###### 离线评测集怎么构建？

面试回答：

我们从农机手册、技术规范和操作资料中整理问题，覆盖文字参数、表格行列、图示部件、跨页条件，以及无答案或信息不足的场景。题目既有直接查询，也有业务中的自然表达，比如保养周期、适用型号、部件位置和多页操作要求。

每道题标注文档版本、参考答案，以及真正支持回答的证据页或片段，不能只标来源文档，因为召回同一文件里的无关页仍然不算命中。调参集和测试集分开，同一道题的近似改写放在同一组；固定版本、证据和指标口径后比较检索配置，无答案题单独检查澄清和证据不足处理。

实现细节：

各题型的标注重点见表。跨页问题保留完整证据页集合，表格标注对应行列、单位与注释，图示题标明支持信息。农机评测集与实习项目的 200 余道养老题分开，不共用题量或结果。

| 题型 | 主要检查 | 标注重点 |
| --- | --- | --- |
| 文本查询 | 条文与参数是否召回 | 文档版本、证据页或片段 |
| 表格查询 | 对应行列、单位与注释 | 能够支持结论的表格位置 |
| 图示查询 | 部件、结构或操作图定位 | 图示所在页及支持信息 |
| 跨页查询 | 多项证据是否覆盖 | 完整证据页集合 |
| 无答案或信息不足 | 是否正确拒答或澄清 | 不足原因，单独评估 |

###### Recall@10、MRR@10 和 NDCG@10 分别说明什么？

面试回答：

Recall@10 看前十条结果找全了多少标准证据；MRR@10 看第一个正确结果有多靠前；NDCG@10 进一步看高相关内容在整体排序中的位置，按名次折损收益，再与理想排序比较。

比如一道题有三个证据页，前十条找到了两个，Recall 就是三分之二；第一条正确结果在第二位，倒数排名就是二分之一。项目测得 Recall@10 为 0.945、MRR@10 为 0.798、NDCG@10 为 0.839，这些反映检索覆盖和排序，不能当作回答准确率。最终问答还要检查事实、条件和引用是否正确，以及链路耗时和成本。

实现细节：

计算含义见表。MRR 在前 10 条没有相关结果时记 0，再按题平均；NDCG 可以采用二元或分级相关性，但对照时标注与计算方式必须一致。无答案题单独检查拒答和澄清，不混入普通证据召回分母。

| 指标 | 计算含义 | 关注点 |
| --- | --- | --- |
| Recall@10 | 前 10 条命中的标准证据数 ÷ 标准证据总数，再对问题取平均 | 证据是否找全 |
| MRR@10 | 第一个相关结果排名的倒数；前 10 条无相关结果记 0，再取平均 | 用户多久能看到第一个正确结果 |
| NDCG@10 | 对相关性收益按位置折损，再除以理想排序的收益 | 高相关结果是否整体排在前面 |

##### 易混点与回答检查

> **易混点**：视觉检索负责找页，OCR 提供可读内容；检索向量不能替代原文证据；检索指标不等于回答准确率。

**回答检查**

- [ ] 能说明 OSS、MySQL、Qdrant 的分工及页面多向量的 MaxSim 匹配。
- [ ] 能解释证据页标注、权限与版本回查、物理页序与印刷页码的区别。
- [ ] 能分别解释 Recall@10、MRR@10、NDCG@10，以及最终回答还需检查什么。

##### 补充记录

事实 / 代码依据：待补充。

#### 图像识别服务与 MCP Server

简历表述：接入 FastAPI/PyTorch 图像识别服务，返回 Top-K 候选机型和置信度供人工复核；开发 MCP Server 提供图像识别、知识检索与问答、标准机型查询和故障排查建议。

##### 快速回顾

| 复盘点 | 核心内容 |
| --- | --- |
| 业务目的 | 根据设备照片缩小机型范围，人工确认后用于资产关联和资料查询 |
| 识别模型 | EfficientNet；协调网络深度、宽度与输入分辨率，兼顾效果和计算开销 |
| 服务分工 | PyTorch 负责推理，FastAPI 提供内部接口，Java 后端负责认证与业务处理 |
| 返回结果 | Top-K 候选、置信度、模型版本和人工复核状态 |
| 结果边界 | 置信度不等于正确率；机型类别不等于具体资产身份 |
| 稳定性 | 输入校验、超时、并发限制、有限重试，识别不可用时支持手动选择 |
| MCP 能力 | 图片识别、标准机型查询、知识检索、知识问答、故障排查 |
| 调用边界 | 客户端模型选择工具，MCP Server 校验执行，后端统一控制权限与业务规则 |
| 权限与错误 | 受限服务身份访问平台公共能力，区分空结果、证据不足与执行失败 |

##### 面试主回答

我接入了基于 EfficientNet 的农机图像识别服务，用 PyTorch 完成模型推理，通过 FastAPI 提供内部接口，Java 后端负责认证、图片校验、候选机型映射和人工复核流程。

识别结果返回 Top-K 候选、置信度和模型版本，用户结合铭牌等信息确认后，再关联标准机型，并用于后续资料检索。服务调用设置了超时和并发限制，模型不可用时支持手动选择机型。

此外，我开发了 MCP Server，将图片识别、标准机型查询、知识检索与问答、故障排查封装为标准工具，供支持 MCP 的客户端调用。MCP 层复用后端能力，通过工具 Schema、受限服务身份和统一错误返回，控制调用边界。

##### 业务场景与模型选择

###### 图像识别在项目中解决什么问题？

面试回答：

用户录入农机设备或查资料时，不一定知道准确型号。我接入图片识别服务，根据照片返回候选机型和置信度，先缩小范围，再让用户结合铭牌、品牌和设备参数确认标准机型。确认后，就可以继续查对应说明书、保养要求或故障资料。

因为不同系列和配置外观相近，角度、遮挡和背景都会影响结果，所以保留 Top-K 候选，不自动认定第一名正确。另外，标准机型和组织里的具体资产分开，识别出某个型号还不能确定是哪一台机器，关联具体资产仍要用户确认。

实现细节：

图片识别用于辅助机型定位，再衔接知识问答。候选、模型版本和人工复核结果分别保存，不能把一次分类输出直接当作已核验的资产身份。

###### EfficientNet 是什么，为什么选择它？

面试回答：

EfficientNet 是一类卷积神经网络，经典方法通过复合缩放协调调整网络深度、宽度和输入分辨率，在识别能力与计算开销之间做平衡。深度影响特征层次，宽度影响表达能力，分辨率影响细节保留，单独扩大某一个维度不一定划算。

我们的任务是从已有农机类别中返回候选机型，适合封装成专门的图片分类服务。选择时既考虑相近型号的区分能力，也看服务延迟、模型大小和部署环境能承受的并发。独立服务的输入和输出比较稳定，后端可以统一映射机型和安排人工复核，模型升级也能单独进行。

实现细节：

缩放维度与代价见表。分类结果只覆盖模型已定义类别，机型标签映射与权重一同管理；EfficientNet 分类服务与 ColQwen 文档检索服务承担不同任务。

| 维度 | 含义 | 代价 |
| --- | --- | --- |
| 深度 | 增加网络层数，学习更复杂的特征 | 计算链路变长 |
| 宽度 | 增加通道数，表达更多特征 | 计算量与内存增加 |
| 分辨率 | 保留更多图像细节 | 计算量明显增加 |

##### 服务接入与结果处理

###### FastAPI、PyTorch 和 Java 后端分别负责什么？

面试回答：

Java 后端负责用户认证、文件与业务校验，调用内部模型服务后，把候选标签映射为标准机型，再处理人工复核和业务关联。FastAPI 提供内部 HTTP 接口，负责图片解码与预处理，PyTorch 加载 EfficientNet 权重并完成推理，返回 Top-K、分数和模型版本。

模型服务主要做计算，不直接读取组织资产或工单，这样模型资源和业务服务可以分别部署、限流和升级。预处理中的尺寸、颜色通道和归一化要与训练配置一致，类别索引与机型标签也随权重管理，否则模型即使正常返回，也可能被映射成错误名称。

实现细节：

组件职责和完整调用顺序见表与流程。分类计算结果经过 Java 后端转换后仍是候选，用户确认后才完成业务关联；模型服务的内部访问不能替代业务认证和资源校验。

| 组件 | 职责 | 关键约束 |
| --- | --- | --- |
| Java 后端 | 认证、文件校验、机型映射与复核流程 | 统一业务规则及访问范围 |
| FastAPI | 内部接口、图片解码与预处理 | 预处理与模型训练配置一致 |
| PyTorch / EfficientNet | 模型加载与分类推理 | 权重、类别映射和模型版本一致 |
| 用户确认 | 结合铭牌等信息确认候选 | 确认后再完成业务关联 |

```text
用户上传图片
    ↓
Java 后端：认证、文件校验、业务处理
    ↓
FastAPI：图片解码和预处理
    ↓
PyTorch / EfficientNet：模型推理
    ↓
返回 Top-K、置信度和模型版本
    ↓
Java 后端：关联标准机型、返回候选
    ↓
用户确认后完成业务关联
```

###### 为什么返回 Top-K？置信度能不能当成正确率？

面试回答：

农机相近型号的外观可能很接近，Top-K 可以保留多个候选，让用户结合铭牌和其他角度比较。模型输出的 Softmax 分数反映已知类别间的相对倾向，未经校准不能直接理解成这次识别正确的概率；一张不属于任何已知类别的图片也可能得到较高分。

所以除了 Top-1 分数，还结合前两名差距、图片清晰度、主体完整性和候选是否能映射有效机型来提示复核，阈值通过验证集确定。候选接近时请用户补照片或查看铭牌，即使分差大也保留确认。识别结果和模型版本记录下来，确认后再完成对应业务关联。

实现细节：

各类分数和身份信息的边界见表，复核检查项见列表。高分不能可靠排除未知类别，标准机型也不能唯一标识组织中的具体资产，不能把两种不确定性合成一个“已识别”。

| 信息 | 能帮助判断什么 | 不能据此推断什么 |
| --- | --- | --- |
| Top-K 候选 | 比较外观相近机型 | 第一名必然正确 |
| Softmax 分数 | 已知类别间的相对倾向 | 实际正确率或未知类别已被可靠排除 |
| Top-1 与 Top-2 差距 | 候选是否接近、是否需要重点复核 | 差距大就无需核对 |
| 型号与资产 | 缩小标准机型范围 | 仅凭外观确定组织中的具体资产 |

- Top-1 分数是否足够高。
- Top-1 与 Top-2 的差距是否明显。
- 图片是否清晰，设备主体是否完整。
- 候选类别是否能映射到有效标准机型。

###### 模型服务超时或者不可用，怎么处理？

面试回答：

我先区分输入问题和服务故障。格式错误、文件损坏或超出大小限制，就提示用户修改图片，重复推理没有意义；连接失败、暂时不可用可以在总时间预算内有限重试，过载时限制并发和等待队列，避免重试继续放大压力。

识别失败会返回明确状态，让用户重传、稍后重试或手动选择机型，不能把故障当成没有候选。推理成功但结果不明确则正常返回候选并要求复核。健康检查也看模型权重是否加载成功，不能只凭 FastAPI 接口已经启动就认为识别能力可用。

实现细节：

异常类型与界面处理见表。请求超时与服务内部失败分开记录，重试受整体预算约束；人工录入保留为可继续的业务路径，不依赖模型服务恢复后才能提交全部流程。

| 情况 | 处理方式 |
| --- | --- |
| 图片不符合要求 | 提示更换或压缩图片 |
| 模型未加载、服务不可用 | 返回暂不可识别，允许手动选择 |
| 推理完成但候选不明确 | 返回候选并要求人工复核 |
| 调用超时 | 提示重试或手动录入 |

##### MCP 工具与调用边界

###### MCP 是什么，为什么已经有 HTTP 接口，还要做 MCP Server？

面试回答：

MCP 的全称是 Model Context Protocol，用于让 AI 应用以统一方式连接外部工具和数据。普通 HTTP 接口已经提供业务能力，MCP Server 再把这些能力描述为可发现的工具，提供工具说明、参数 Schema 和调用方式，方便支持 MCP 的客户端接入。

我把图片识别、标准机型查询、知识检索、问答和故障排查等公共能力封装出来，实际仍复用 Java 后端的规则和访问范围。比如外部助手可以先识别图片，请用户确认机型，再调用知识问答查询保养要求。这样减少每个客户端分别理解和适配业务 API 的工作，业务实现和权限仍由原后端维护。

实现细节：

工具能力见表。MCP Server 提供发现和调用适配，HTTP 服务继续承载实际业务；模型根据工具描述提出调用发生在客户端编排层，协议本身不替模型决定该调用哪个工具。

| 工具能力 | 解决的问题 |
| --- | --- |
| 图片识别 | 根据照片获取候选机型 |
| 标准机型查询 | 确认品牌、型号和机型类型 |
| 知识检索 | 查找相关手册与证据 |
| 知识问答 | 根据资料生成带引用的回答 |
| 故障排查 | 结合现象、故障码与资料给出排查建议 |

###### MCP 工具怎么定义，模型怎么选择工具？

面试回答：

每个 MCP 工具都定义清楚名称、适用场景、输入 Schema 和返回结构，同时说明是否写入数据。客户端获取工具描述后，模型根据用户目标提出工具名和参数，客户端与 MCP Server 校验后执行，Java 后端再检查实际业务范围。

例如图片识别工具接收图片和文件信息，只返回候选、置信度与复核建议，不创建资产；知识检索返回证据，知识问答再基于证据生成回答。边界明确，模型才容易正确选择。选择发生在客户端模型和编排层，MCP Server 负责收到调用后的校验与执行，不能因为模型选了工具就跳过参数或权限检查。

实现细节：

下面的 JSON 展示名称、描述和一次调用输入，正式输入 Schema 另行约束字段类型、必填项和取值范围。工具调用结果区分证据、候选和业务回执；各参与方职责见表。

| 参与方 | 负责什么 | 调用边界 |
| --- | --- | --- |
| 客户端模型与编排层 | 根据目标和工具描述提出调用 | 只提出工具与参数 |
| MCP Server | 工具发现、Schema 校验与调用处理 | 不能因为模型选择了工具就省略校验 |
| Java 后端 | 实际业务能力与权限检查 | 不信任调用参数自行声明的权限 |
| 内部模型服务 | 执行图像等模型计算 | 不直接读取组织业务数据 |

```json
{
  "name": "farm_machinery_run_image_recognition",
  "description": "根据单张农机图片返回候选机型、置信度和人工复核建议",
  "input": {
    "image_base64": "...",
    "file_name": "tractor.jpg",
    "content_type": "image/jpeg"
  }
}
```

###### MCP 的权限和错误怎么处理？

面试回答：

外部调用方先认证，MCP Server 再使用受限服务身份调用后端，只开放平台公共标准机型和知识能力，不读取组织私有资产、工单或私有知识库。调用参数里填写组织 ID 不能扩大权限，实际范围由服务端身份和后端规则确定。

结果要区分成功但空结果、证据不足、参数或权限错误，以及超时和服务不可用。工具执行失败通过错误状态返回，让客户端决定修正输入、有限重试或向用户解释，不能把故障包装成正常空列表。问答保留引用和证据充分性，识别保留候选、模型版本及复核状态，帮助客户端正确解释结果。

实现细节：

返回分类与客户端动作见表和列表。服务身份的能力范围与外部请求参数分开，权限错误不能通过改写组织参数绕过。错误状态表达执行是否成功，证据充分性表达结果能否支撑结论，两者不能混用。

| 返回情况 | 含义 | 客户端后续动作 |
| --- | --- | --- |
| 成功但空结果 | 查询完成，没有匹配资料 | 调整条件或说明未找到 |
| 证据不足 | 现有资料无法支撑完整结论 | 补充信息或限制回答范围 |
| 参数或权限错误 | 请求无效或无权调用 | 修正输入或处理授权，不能绕过检查 |
| 超时或服务不可用 | 工具未能正常完成 | 在预算内有限重试或说明失败 |

- 查询成功但没有匹配结果。
- 找到了资料，但不足以支持回答。
- 参数不合法或没有访问权限。
- 下游服务不可用或请求超时。

##### 易混点与回答检查

> **易混点**：分类置信度不能直接解释成实际正确率，机型类别不等于具体资产；MCP 工具选择发生在客户端模型与编排层。

**回答检查**

- [ ] 能说明 EfficientNet、PyTorch、FastAPI 与 Java 后端的职责和人工复核流程。
- [ ] 能说明 MCP 工具 Schema、受限服务身份，以及空结果、证据不足和执行失败的区别。

##### 补充记录

事实 / 代码依据：待补充。

## 通用面试题

| 问题 | 回答要点 | 状态 |
| --- | --- | --- |
| 请介绍你最熟悉的项目 | 待补充 | 待整理 |
| 你最有挑战的一项工作是什么 | 待补充 | 待整理 |
| 如何证明效果提升来自你的改动 | 待补充 | 待整理 |
| 如果重新设计，你会调整什么 | 待补充 | 待整理 |

#### 请介绍你最熟悉的项目

##### 快速回顾

待补充。

##### 面试主回答

待补充。

#### 你最有挑战的一项工作是什么

##### 快速回顾

待补充。

##### 面试主回答

待补充。

#### 如何证明效果提升来自你的改动

##### 快速回顾

待补充。

##### 面试主回答

待补充。

#### 如果重新设计，你会调整什么

##### 快速回顾

待补充。

##### 面试主回答

待补充。

## 待核实事项

- 两段经历中本人独立负责、协作完成和仅了解的具体边界。
- 简历所列检索指标的评测集、基线、参数和实验记录。
- 项目中各技术组件与简历描述的实际对应关系。
- 业务规模、数据规模、耗时或成本等尚未在简历中给出依据的数字。
