Race Bot (HKJC) — 项目深挖防守稿
项目:香港赛马会(HKJC)垂类对话机器人
时间:2024年09月 – 2025年02月
角色:设计与开发负责人
技术核心:Agentic Workflow + Text2SQL + 合成数据 + Verl 两阶段训练(SFT + GRPO)
面试权重:★★★
来源:CV;未做一手考证
相关知识点:01-fundamentals/02-post-training/(SFT / GRPO / 数据工程)、04-agent-architecture/(tool use / ReAct)
1. 三十秒叙事#
「这是给香港赛马会做的垂类对话机器人,服务公众用户。难点在于用户问题跨度极大 —— 从『明天第三场谁会赢』这种闲聊,到『近三年沙田 1200 米草地赛、骑师 X 的胜率是多少』这种需要查结构化数据库的复杂分析。
我的方案是 Agentic Workflow 编排:安全检测 → 话题分类 → 查询改写 → 意图识别 → 分流到知识库检索 / 联网搜索 / Text2SQL → 结果后处理。
其中最大的技术投入在 Text2SQL:垂域标注数据稀缺,我搭了合成数据流水线造训练数据,然后用 Verl 对 Qwen-32B 做两阶段训练(SFT 学语法和业务逻辑,再用 GRPO 做强化学习对齐),把端到端数据问答准确率从 63% 提到 92%。」
为什么这么开场:一句话里埋了四个可挖的钩子(Agentic Workflow / 合成数据 / Text2SQL 工程 / RL 训练)。面试官通常会挑最有兴趣的那个挖,而这四个你都有准备。
2. 架构与关键决策#
2.1 为什么用 Agentic Workflow 而不是一个大 prompt#
用户请求的类型差异太大 —— 闲聊、事实查询、结构化聚合分析,走的是完全不同的工具链。塞进一个 prompt 会导致:工具描述冗长、模型选错工具、无法对单步做评测和优化。
拆成编排的好处是每一步可独立替换和评测:意图识别错了就只修意图识别,不用重训整个模型。这是工程上的核心决策。
2.2 Text2SQL 为什么不用端到端生成#
直接让 LLM 生成 SQL 在这个场景下有三个硬伤:
| 问题 | 具体表现 | 对策 |
|---|---|---|
| 实体模糊匹配 | 用户说「告东尼」还是「Tony Cruz」,数据库里是标准写法 | Elasticsearch 做模糊实体匹配,先标准化再进 prompt |
| Schema 太大 | 全表结构塞不进 prompt | 表/列召回(recall),只注入相关 schema |
| 语法/业务逻辑出错 | 生成的 SQL 跑不通,或跑通了但业务含义错 | retry-and-fix(Reflection)重试机制 + 结果后处理 |
所以最终方案是多阶段流水线:关键词与实体抽取 → Elasticsearch 模糊匹配 → 表列召回 → 基于语义的 few-shot 范例检索 → SQL 生成 → 自修正重试 → 结果后处理。
注意这里的取舍:多阶段流水线比端到端更复杂、延迟更高,但可控性和可调试性远胜。对政府/公共服务的场景,可控性优先级高于极致延迟。
2.3 合成数据为什么要「多策略」#
垂域标注数据稀缺,且真实分布极度不均衡(简单问题多、复杂聚合少)。单一合成策略会造出同质数据,模型在边缘 case 上崩。
采用的三种策略各有分工:
- 主题引导生成(topic-guided):按业务主题覆盖,保证广度
- 字段聚合(field-based aggregation):专门造统计/聚合类查询
- 困难样本构造(hard-case construction):针对已知的失败模式定向补数据
关键技术点:合成数据必须是 (自然语言问题, ground-truth SQL) 成对的,而且 ground-truth SQL 要实际执行验证过、能返回非空结果 —— 否则你是在教模型写一种语法正确但语义错误的 SQL。
2.4 为什么要 SFT + GRPO 两阶段,而不是只 SFT#
这是最可能被深挖的地方,想清楚这个链条:
- 只做 SFT:模型学会了 SQL 的语法和业务逻辑模板,但 SFT 是模仿学习 —— 它只能模仿训练数据里出现过的模式。对于未见过的查询组合,它没有「尝试—修正」的能力。
- 加 GRPO:让模型在多个候选 SQL 之间,通过奖励信号(SQL 能否执行、结果是否正确)学会朝着正确方向调整。这补上了 SFT 缺失的探索能力。
SFT 先建立「能产出合理格式 SQL」的起点 —— 这一步不做,RL 阶段奖励信号无从谈起(模型一开始全在产垃圾 SQL,全是负奖励,梯度没有信息量)。
GRPO 相对 PPO 的选择理由:GRPO 用同一 prompt 采样一组回答的组内相对优势替代价值网络(critic),省掉一个和策略同规模的模型,显存和工程复杂度都显著下降。在 Verl 框架里这是现成支持的。
⚠️ 边界必须自己先说:这个项目是单轮 RLVR —— 一个 prompt 采样多条 SQL,奖励由「能否执行 + 结果是否与 ground truth 等价」判定。那正好是 GRPO 好用的场景(短 horizon、强 verifier、一条 response 就是一次决策)。
它不覆盖多轮工具调用的 RL。 多轮场景下会同时冒出四个问题:credit 太粗(整条轨迹共用一个 advantage)、组内 advantage 常归零、组内比较掺进环境噪声、rollout 成本与 policy lag —— 即 Agentic RL,见
01-fundamentals/02-post-training/08-agentic-rl.md。面试官顺口问「那多轮呢」,正确答法是先划边界再讲理解:「我跑的是单轮,多轮我没有一手经验,我的理解来自……」。等被问出来再解释,比主动说清要吃亏。
3. 面试官会怎么往下挖#
这是本篇最重要的部分。按挖掘深度分层,每层都要能接住。
第一层:澄清与动机#
Q:63% 到 92% 这个数字,具体测的是什么?
⚠️ 这题必被问,且答不上口径会让整个项目失分。
要能立刻说清:① 指标定义(端到端问答准确率 = 最终答案与标准答案一致的比例,不是 SQL 语法正确率,也不是 SQL 执行成功率);② 测试集构成(规模、是否与训练集去重、是否含困难样本);③ baseline 是什么(63% 是 SFT 之后的版本,还是原始 prompt 版本 —— 这个一定要说准)。
诚实策略:如果测的是内部构造的评测集而非公开 benchmark,主动说明「这是我们按业务场景自建的评测集,不是公开 benchmark,所以我不能声称它能横向对比」。主动说比被问出来好。
Q:为什么不直接用现成的 Text2SQL 方案 / 更大的模型?
答:业务 schema 私有且复杂、领域实体需要专门标准化、且成本上要给公众用户扛量。现成方案在通用 benchmark 上强,但在 HKJC 的表结构和赛马术语上直接上会大面积出错。
第二层:技术细节#
Q:合成数据会不会泄漏到测试集?怎么保证评测可信?
⚠️ 这是最尖锐的一问。 必须能答:去重(问题级和 SQL 级双重去重)、主题/实体级别的切分、以及是否做过「留出真实用户数据做验证」。
如果确实存在污染风险而你没处理 —— 承认它,并说清「如果重做我会怎么切分」。面试官吃这一套,硬撑不吃。
Q:GRPO 的奖励信号具体怎么设计的?
要点:可执行性(SQL 能否跑通)+ 结果正确性(执行结果是否匹配 ground truth)+ 格式合规。要能说清每一项的权重和理由,以及当奖励可被 hack 时你怎么办(例如模型学会生成总是返回空集但永远能执行的 SQL —— 所以「执行成功」不能单独作为奖励,必须绑定结果比对)。
Q:Text2SQL 的 retry-and-fix 循环怎么防止无限重试或者越修越错?
要点:重试次数上限、每次是否注入上次的错误信息、以及兜底策略(重试失败后返回什么 —— 降级到澄清追问,还是返回部分结果)。
Q:Elasticsearch 模糊匹配是怎么做的?为什么不用向量检索?
要点:实体匹配需要精确的字符级相似(人名、马名),BM25 + 编辑距离这类稀疏方法在专有名词上往往比稠密向量更稳;且实体名是有限闭集,可以配合同义词表。也可以答「其实两者结合,向量负责语义召回,ES 负责精确匹配」—— 但只说你真做了的部分。
Q:Agentic Workflow 里意图识别错了怎么办?错误会怎么传播?
要点:错误传播(cascading error)是编排式 agent 的核心风险。对策:每步设置置信度阈值、低置信时降级为澄清追问而不是硬走错分支、以及端到端兜底。
第三层:工程与生产#
Q:线上延迟是多少?用户能接受吗?
有数字给数字,没数字就讲你是怎么平衡的:多阶段流水线天然延迟高,所以做了哪些优化(并行化独立步骤、缓存高频查询、小模型做意图识别而不用 32B)。呼应 QueWI 里做模型路由的经验。
Q:这个系统上线了吗?有多少用户?
⚠️ 红线:如实说。涉及政府/马会客户,很多数据不能披露 —— 就说「具体数据受客户保密约束,我能讲的是技术方案和它的效果验证方式」。不要编数字。
Q:出错了怎么排查?
要点:链路追踪(每一步的输入输出都记)、SQL 生成失败的 case 归档、以及定期从线上失败 case 里挖新的困难样本回灌训练集 —— 这个闭环非常加分,说明你理解「上线不是终点」。
第四层:反思与延伸#
Q:现在重做这个项目,你会改什么?
⚠️ 这题一定要有准备,它考察的是反思能力,答「没什么要改的」等于自杀。
可用的方向:① 合成数据的去重和评测切分做得更严格;② 用更强的基座或加 self-consistency 采样;③ 把意图识别和 Text2SQL 的耦合解开,让 Text2SQL 模块能被其他项目复用;④ 引入更系统的离线回归评测,防止迭代时能力回退。
Q:如果用户问的是「预测下一场谁会赢」,你怎么处理?
考察边界意识。赛马预测涉及合规和风险,正确答法是「这类请求应该在话题分类阶段就识别出来并走安全分支 —— 系统做的是信息查询和统计,不做预测性建议」。展示你懂得哪些不该做。
4. 线上故障复盘#
米哈游面经:「所有技术点必须绑定业务场景 + 线上问题 + 量化结果」。这一节按
README.md《故障题的四段结构》准备。⚠️ 下面三类是这套架构必然会长出来的故障(由设计推导),不是替你编的经历。你要做的是把每一类对应到自己真实遇到的那一次;确实没遇到的,就如实说没遇到,再讲你为它加了什么预防。
| # | 故障类型 | 现象(先给可观测的表象) | 该往哪查 | 修法与复盘要点 |
|---|---|---|---|---|
| 1 | 实体匹配错了人 / 马 | SQL 执行成功、结果非空,但答的是另一个骑师的胜率 —— 系统「成功地」答非所问 | 把「SQL 执行成功」与「结果与 ground truth 等价」拆成两个指标分别统计;看空结果率与实体命中率 | ES 模糊匹配补同义词表 + 置信度阈值,低置信转澄清追问;奖励里把可执行性绑定结果等价。复盘:只奖励可执行性会训出「永远跑得通但答非所问」的策略 |
| 2 | Reward hacking:恒真 / 空集 SQL | 训练 reward 稳定上涨,但人工抽检看不出模型变好;线上空结果回复增多 | 抽高分样本看 SQL 结构:恒真条件、缺 WHERE、极端 LIMIT | 空结果与异常行数加惩罚项;每一项奖励都做对抗检验(问「模型怎样不完成任务却拿到这一项的分」)。复盘:奖励分项本身也要当被测对象 |
| 3 | 零梯度组吃掉算力 | loss 曲线平、reward 不动、迭代很慢、GPU 利用率低 —— 但看起来「没报错」 | 统计同组奖励全同(全对 / 全错)的比例,看 pass rate 分布 | 按 pass rate 过滤或分层采样,让 batch 落在「有对有错」区间;把「零梯度组占比」做成一等监控指标。复盘:训练不动常常是数据难度分布问题,不是超参问题 |
面试官还会接着问#
Q:上线后发现某类问题答得特别差,你会先怀疑哪一环?
按流水线顺序逐环排除,不要直接改 prompt:① 实体标准化(ES 命中了吗);② 表列召回(需要的表进 prompt 了吗);③ few-shot 范例(召回的范例和问题同类吗);④ SQL 生成;⑤ 结果后处理。关键是先定位到环,再动手 —— 直接调 prompt 是最常见也最没依据的做法。
Q:你怎么知道模型上线后没有悄悄变差?
固定回归评测集 + 线上失败 case 回流训练集。但必须主动承认局限:垂域问题分布会漂移(赛事、马匹、骑师都在变),所以评测集需要按业务周期更新,不能建一次用一年。
5. 数字口径速查#
被问到数字时,按这个顺序确认(先确认定义,再给数字):
| 数字 | 口径必须说清 |
|---|---|
| 63% → 92% | 端到端问答准确率;自建评测集(说明规模与去重方式);baseline = SFT 后版本 |
| Qwen-32B | 训练的是 SQL 生成组件,不是整个机器人 |
| SFT + GRPO 两阶段 | 框架是 Verl;第一阶段学语法与业务逻辑,第二阶段 RL 对齐 |
| 合成数据策略 | 主题引导 / 字段聚合 / 困难样本,三者分别覆盖广度、聚合、已知失败模式 |
⚠️ 一个数字如果说不清口径,宁可不说,改说趋势和方向。 面试官对「大概提升了很多」的容忍度,高于对「92%,呃,具体怎么测的我记不清了」的容忍度。
6. 红线#
不要 claim:
- ❌ 不要把所有工作说成自己一个人做的 —— 你是负责人,但合成数据流水线、后处理模块可能有他人参与。被问细节时明确边界是加分项。
- ❌ 不要声称这是公开可验证的 benchmark 结果。
- ❌ 不要透露客户的具体业务数据、用户量、内部系统细节。
- ❌ 不要把「SQL 执行成功率」和「答案准确率」混着说 —— 面试官一追问就露馅。
不要主动说:
- 不要一上来就讲 92% 这个数字。先讲问题和方案,让面试官问出数字,这样你是在回答而不是在炫耀,且你有机会先铺垫口径。
- 不要主动提客户名以外的敏感信息(政府/马会的合规敏感度高)。
要主动说:
- ✅ 主动讲清楚权衡和失败过的方案(比如「一开始试过端到端生成 SQL,失败在实体匹配上」)。
- ✅ 主动指出当前的局限和「如果重做会怎么改」。
- ✅ 主动划 RL 的边界:这是单轮 RLVR,不覆盖多轮工具调用的 RL(Agentic RL)。自己先说,比被问出来强 —— 而且能顺势展示「知道 GRPO 在哪失效」这一层(见 §2.4 的边界框与
08-agentic-rl.md)。 - ✅ 主动提一个具体的线上故障(见 §4)—— 这是「真做过」最有效的证据。
7. 自测#
1. 面试官问「92% 是怎么测的」,请给出一个完整的、经得起追问的回答。
答案要点
必须包含四点:① 指标定义 —— 端到端问答准确率,即最终自然语言答案与标准答案一致的比例,并明确它不是 SQL 语法正确率、也不是 SQL 执行成功率;② 测试集 —— 规模、与训练集的去重方式(问题级 + SQL 级)、是否含困难样本;③ baseline —— 63% 对应的是哪个版本(通常指仅 SFT 后);④ 局限 —— 主动说明是自建评测集而非公开 benchmark,因此不声称可横向对比。
2. 为什么不能只用 SFT,还需要 GRPO?请从「SFT 的能力边界」角度回答。
答案要点
SFT 是模仿学习,只能学会训练数据中出现过的模式,对未见过的查询组合缺乏「尝试—修正」的能力。GRPO 通过组内相对优势提供探索信号,让模型在多个候选 SQL 之间朝着可执行且结果正确的方向调整。同时,SFT 是 RL 的必要前置:没有 SFT 建立合理的输出格式起点,RL 阶段奖励全为负、梯度没有信息量。
3. 如果你的合成数据污染了评测集,会表现出什么症状?如何在流程上避免?
答案要点
症状:评测指标异常高且与线上表现严重不符,或模型在评测集上表现远好于同分布的留出集。避免:问题级与 SQL 级双重去重、按主题/实体切分而非随机切分、并用真实用户数据留出集做交叉验证。
4. 把「SQL 执行成功」单独作为 RL 奖励会产生什么问题?
答案要点
奖励可被 hack:模型会学会生成永远能执行但返回空集或无意义的 SQL(例如恒真/恒假条件),因为这类 SQL 执行成功率最高。因此执行成功必须与结果正确性绑定,并对空结果、异常行数做惩罚。
5. 面试官问「这个系统上线后有多少用户」,而数据受客户保密约束,你怎么答?
答案要点
先明确说明受客户保密约束无法给出具体数字,然后把话题转向可讲的东西:技术方案、效果验证方式、以及上线后通过失败 case 回灌训练集的迭代闭环。绝对不要编造数字 —— 一旦被追问细节就不可挽回。