D1 · 工具调用与索引
本日安排:8 小时净学习。休息另计,算法可独立安排。
- 3 小时:AG01–AG08,逐题理解并画工具往返。
- 2 小时:DB01–DB03,索引与慢查询。
- 1.5 小时:追踪最小 Loop 与订单索引练习。
- 1.5 小时:闭卷口述、自测与纠错。
工具调用:从一个查询请求开始
Section titled “工具调用:从一个查询请求开始”假设用户问:“订单 O-17 发货了吗?”模型的训练数据里没有今天的订单状态。服务先告诉模型,有一个 get_order 工具,输入是订单编号。模型可以提出调用请求,由服务查询订单,再把结果送回模型。
这一过程里有三份不同的数据:用户的自然语言问题,模型生成的工具参数,以及订单服务返回的事实。把它们分开,才能判断错误发生在哪里。模型可能理解错编号,参数可能无法解析,也可能查不到订单。即使这些步骤都成功,模型最后仍可能把 paid 错写成“已发货”。
图中的顺序表示一次自定义函数调用,没有表示真实耗时。get_order 函数由应用运行。使用供应商托管工具时,执行方和结果回传方式要按该 API 的契约判断。[1]
先看消息怎么对应
Section titled “先看消息怎么对应”下面用 Chat Completions 的消息形状说明关联关系。它是教学示例,不是实时模型响应;不同 API 的字段和模型支持范围可能不同。
{ "role": "assistant", "tool_calls": [{ "id": "call_17", "type": "function", "function": { "name": "get_order", "arguments": "{\"order_id\":\"O-17\"}" } }]}arguments 是 JSON 编码的字符串。宿主先解析它,检查字段和值,再调用函数。查到订单后回填:
{ "role": "tool", "tool_call_id": "call_17", "content": "{\"ok\":true,\"result\":{\"order_id\":\"O-17\",\"status\":\"paid\"}}"}tool_call_id 对应上面的 id。一次模型响应可以提出两个独立查询,每个结果都要回到自己的调用上。假设 call_17 查订单,call_18 查物流,把物流结果绑定到 call_17 会让后续推理使用错误事实。供应商也可能直接拒绝不完整的消息序列。Responses API 使用自己的输出项和 call_id,不能只改 URL 就沿用这里的消息对象。[1]
参数格式正确后,还要检查什么
Section titled “参数格式正确后,还要检查什么”工具声明可以规定 order_id 是字符串、字段必填、不允许额外字段。声明帮助模型生成符合格式的请求,执行器仍需验证收到的内容。业务规则还要另查:订单属于谁,用户能否看这张订单,是否允许读取其中的地址。
模型传来 {"order_id":"O-29"},完全可能符合 Schema,却是另一个商家的订单。tenant_id 应由认证上下文提供,查询条件应同时限制 tenant_id 和 order_id。把租户作为模型自由填写的参数,会给它不必要的权限。
工具返回值也应按目的裁剪。如果问题只需要订单状态,返回状态和订单编号即可。把完整地址、内部备注、支付令牌一起送给模型,既增加上下文,也增加泄露风险。
Agent Loop:为什么会再次请求模型
Section titled “Agent Loop:为什么会再次请求模型”普通聊天请求收到文本后就结束。工具调用需要第二轮:模型先提出 get_order,宿主返回 paid,模型再把这个事实转换成“订单已付款,还未发货”。
可把循环写成下面的步骤:
装配消息与工具声明→ 请求模型→ 保存本轮响应→ 有工具调用:逐个校验、执行、回填,然后再请求模型→ 无工具调用:处理最终文本、拒答或空响应这里保存本轮 assistant 响应,是为了让模型看到“自己提出了什么调用”,随后才看到相应结果。只追加结果却丢掉调用请求,会破坏关联,也可能违反供应商协议。
模型连续要求工具时,循环可能一直运行。设置轮数上限能限制请求次数,但一次响应仍可能包含几十个调用,一个工具仍可能卡住。生产服务通常还要设置总 deadline、工具超时、最大工具次数和费用预算。遇到循环上限时返回“未完成”,不能把中间进度包装成成功。
例如教学限制为最多 8 轮、最多 12 次工具调用。如果前两轮共执行了 12 次工具,不能因为只用了 2 轮就再允许第 13 次。金额预算也不能只在下一轮开始时检查:并发发出的调用可能同时产生费用,需要考虑在途消耗。这些数值是练习参数,不是行业标准。
错误应该如何进入下一轮
Section titled “错误应该如何进入下一轮”查询不存在的订单,应返回稳定的错误类型,例如 NOT_FOUND。模型可以解释没有查到,或询问用户是否输错编号。不能把异常转换成“已发货”,也不能对所有错误无限重试。
区分能调整输入的错误和暂时性故障。缺少参数、未知工具、没有权限,需要拒绝或修正请求。暂时断连可以由负责这次调用的执行层有限重试。写操作超时可能已经成功,第四天会专门解释这种情况,今天先记住不能由模型随意换一个 ID 重做。
Workflow、Agent 与框架怎样配合
Section titled “Workflow、Agent 与框架怎样配合”订单状态查询的步骤很固定:认证、查询、格式化结果。一个普通 API 或固定 workflow 就能完成。若用户的问题可能需要查询订单、检查政策、追问缺少的信息,模型参与选择下一步才有实际用途。
常见的 ReAct 式循环让模型在每次观察后选择工具。Plan-and-Execute 先列计划再执行,适合任务较长、子任务可分解的情况,但计划遇到新事实仍需调整。Reflection 增加检查步骤,要明确检查依据;让同一个模型多说一遍,未必纠正原来的错误。
框架可以提供工具适配、状态图和 checkpoint。宿主应用仍需要实现业务授权、超时、额度和副作用控制。对这个小例子,先手写循环更容易看清消息协议。步骤多到需要分支和恢复时,再考虑状态图;需要跨进程持久执行时,检查存储与重试语义。不能从框架名称推断它能保证退款只发生一次。
并行也要看依赖。查询两个不同订单可以并发;“先查可退金额,再按该金额申请退款”有数据依赖。多 Agent 还会引入独立上下文、任务交接和结果合并,不能把一次响应中的两个工具调用直接叫作两个 Agent。
索引:订单查询为什么不需要逐行找
Section titled “索引:订单查询为什么不需要逐行找”订单表有几十万条记录,按编号查询时逐行比较会随着数据量增大而变慢。B+ 树把键按顺序组织在页面中,内部节点引导查找,叶子节点保存索引记录。一个页面可以容纳许多键,树的分支数较大,通常不需要为每条记录经过一次单独查找。
连续范围查询可以从第一个匹配叶子记录继续按键顺序读取。写入也有代价:索引页可能修改、分裂,额外索引占用存储和缓存。索引要服务真实查询,不能给每一列都建一个来保证速度。
InnoDB 的聚簇索引叶子存放行数据。有显式主键时,它通常就是聚簇索引。二级索引记录包含索引列和主键列;若查询还需要其他列,数据库会根据主键去聚簇索引取整行,这一步常称回表。[2]
假设主键是 order_id,二级索引为 (tenant_id, status, created_at)。查询只取 order_id、created_at,这些信息可能全部在二级索引中。查询再增加 amount,若索引不包含该列,就要读取行。覆盖索引减少这种额外访问,但仍不保证整个查询无需任何数据页处理。示意图没有画并发版本可见性等存储细节。
联合索引的顺序如何影响查询
Section titled “联合索引的顺序如何影响查询”把 (tenant_id, status, created_at) 想成按这三个字段逐层排序的表:先按租户排,同一租户里按状态排,同一状态里再按时间排。指定 tenant_id=7 和 status='paid',再限定时间范围,数据库可以定位到相应区间。只指定 created_at 时,时间分散在许多租户和状态区间,缺少同样的直接定位前缀。[3]
-- MySQL 8.4 / InnoDB,设计示例,未在本课运行执行计划CREATE INDEX idx_order_listingON orders(tenant_id, status, created_at);
SELECT order_id, created_atFROM ordersWHERE tenant_id = 7 AND status = 'paid' AND created_at >= '2026-10-01'ORDER BY created_at DESC, order_id DESCLIMIT 20;这个索引是候选方案。order_id 为主键时,二级索引已带它;具体排序能否使用索引,还要结合完整索引定义、扫描方向和优化器计划。新增 status IN (...),排序区间可能改变。新增 amount,覆盖情况改变。不要只看到 key 字段非空就结束排查。
“最左前缀”描述常规联合索引定位能力。遇到优化器选择 index merge、skip scan 或全索引扫描,要解释具体访问路径,不能把任何涉及后列的查询都称为“索引完全失效”。同样,范围条件后的列可能仍参与过滤、覆盖或特定排序用途,只是不能一般性地假定它们继续把扫描区间缩成一个等值范围。
慢查询:沿实际执行顺序排查
Section titled “慢查询:沿实际执行顺序排查”先明确哪条 SQL 慢、输入参数是什么、是否只是高峰时慢。ORM 的方法名不足以回答这些问题,需要拿到生成的 SQL。记录真实耗时和返回行数,再看执行计划中的访问类型、扫描估计、排序和连接顺序。适用时使用 EXPLAIN ANALYZE 观察实际执行,它会真正执行查询,不能随意对生产写操作使用。
假设接口只返回 20 行,数据库却扫描了 50 万行。可能原因包括过滤列没有合适索引、排序无法利用索引,或 OFFSET 丢弃了大量记录。深分页 LIMIT 100000,20 即使使用索引,也可能需要访问并跳过大量索引项。
对于稳定排序的列表,可以改为游标分页。上一页最后一项是 (created_at, order_id),下一页按严格小于这个排序键读取。需要处理相同时间的记录,所以排序和游标都带唯一键。并发插入、更新时间变化和需求是否允许跨页快照,是另外的契约,不由改成游标自动解决。
索引之外还要查等待:事务持锁、磁盘 I/O、连接池排队、统计信息偏差以及一次请求里的 N+1 查询。加索引不解决连接池被长时间占用,也不解决应用每行再查一次数据库。
仓库提供一个无模型、无网络的工具契约示例。它接收工具名和 JSON 参数,用固定订单数据验证字段、类型与租户归属。
python3 examples/tool_contract.pypython3 -m unittest discover -s examples -p 'test_*.py' -v先预测五个结果:正常查询、JSON 损坏、额外的 tenant_id、越权订单和未知工具。再运行测试,查看哪个检查拒绝了请求。练习代码用固定数据代替数据库,只验证宿主执行逻辑;它不测试模型能否选对工具。
索引练习不用先搭数据库。写出上述候选索引的前三组键顺序,再回答增加 amount 或把等值 status 改成多个状态后,哪些原有推断需要重做。若有自己的 MySQL 环境,可进一步运行执行计划,记录实际结果,不能把纸面推导称为实测。
以下均为单选,每题只有一个正确答案。
模型生成 {"order_id":"O-29"},参数符合 Schema,但订单属于另一个商家。应在哪里拒绝?
- A. 只要模型声明自己有权限,就允许查询。
- B. 宿主或订单服务依据认证主体检查订单归属。
- C. 在最终回答里不显示商家名字即可。
- D. 使用 Structured Outputs 后不必检查归属。
答案与解析
正确答案:B。认证主体和对象归属由服务端验证。A 把模型输出当成权限证明;C 查询已经发生,隐藏名字不解决越权;D 保证格式也不会证明用户有权查看这张订单。
同一 assistant 响应给出 call_17 和 call_18。回填结果应怎么做?
- A. 合并成一条没有关联 ID 的 user 消息。
- B. 只回填最后一个工具结果。
- C. 每个结果使用相应工具调用的关联 ID。
- D. 重新生成随机 ID,以免和旧消息重复。
答案与解析
正确答案:C。ID 把观察结果绑定到调用。A 丢掉协议语义;B 使另一调用缺少结果;D 的新 ID 没有对应原调用,无法正确关联。并发完成顺序不改变这项要求。
宿主最多请求模型 8 轮。哪个风险还需要单独控制?
- A. 一轮中产生很多工具调用,其中一个长时间阻塞。
- B. 模型第九轮一定不会被调用,因此所有成本已有上限。
- C. 每个工具都必定在第八轮自动取消。
- D. 达到上限就说明任务已经成功。
答案与解析
正确答案:A。轮数没有给单轮工具数量与耗时定义上限。B 混淆次数预算和费用预算;C 工具取消需要执行层实现,不能从轮数推断;D 上限只是停止条件,结果仍可能未完成。
InnoDB 表以 order_id 为主键,二级索引是 (tenant_id,status)。关于索引内容,哪个说法正确?
- A. 二级索引叶子必定保存整行所有字段。
- B. 二级索引记录包含主键信息,可以据此查聚簇索引。
- C. 二级索引与主键无关,回表靠数组下标。
- D. 只要用二级索引,永远无需读取数据行。
答案与解析
正确答案:B。二级索引通过主键定位行。A 把二级索引与聚簇索引叶子混淆;C 不符合 InnoDB 的定位方式;D 忽略查询需要索引外字段等情况。
联合索引 (tenant_id,status,created_at) 用于常规前缀定位。哪组条件最直接地使用前两列等值前缀?
- A. 仅指定
created_at >= ...。 - B. 仅指定
status='paid'。 - C. 指定
tenant_id=7 AND status='paid'。 - D. 仅指定没有出现在索引里的
amount=200。
答案与解析
正确答案:C。它限定了第一个字段,再限定同一租户内的状态。A、B 缺少相应左侧前缀,不能按本题所说的方式直接定位;特定优化器路径需要另行判断。D 不提供该索引的定位条件。
SQL 用了索引,只返回 20 行,仍很慢。下一步最有帮助的是哪项?
- A. 既然用了索引,就排除数据库问题。
- B. 立即对所有列加索引。
- C. 只看应用接口函数名。
- D. 检查实际扫描、排序、等待和 OFFSET/N+1 情况。
答案与解析
正确答案:D。返回行数与处理成本不同。A 没有检查实际执行;B 会增加写入和存储成本,也没有定位原因;C 看不到真实 SQL 和数据访问路径。
每题 4 分。先自己回答,再看参考解答;表达不同但机制正确可以得分。
用户问订单是否发货。请描述从模型提出 get_order 到最终回答的完整过程,并指出两个校验点。
参考解答与评分点
应用发送工具定义和问题,模型生成工具名、参数和关联 ID。宿主解析 JSON,检查允许的工具、字段和值;查询服务用认证租户与订单编号检查权限,再读取订单状态。宿主将执行结果绑定原关联 ID 回传,模型据此生成回答。最后要避免把 paid 当成 shipped。
- 工具声明、调用请求与真正执行的分工清楚,1 分。
- 包含参数校验和认证主体/对象归属两个检查,1 分。
- 说明保存调用请求、回填关联 ID 并继续模型请求,1 分。
- 区分工具事实和最终文本,处理查不到或解释错误,1 分。
一个循环限制 8 轮,但仍发生高费用和长时间阻塞。你会增加哪些控制?
参考解答与评分点
增加总 deadline 与每个工具超时,限制工具总次数与并发数,定义 token/金额预算并考虑在途费用。重试由明确的一层负责,区分能修正输入的错误与暂时性错误。达到限制后返回未完成状态,并保留已执行动作的结果;取消不能撤销已经完成的外部动作。
- 轮数与工具次数/单次耗时的区别,1 分。
- 总 deadline、工具超时或并发控制,1 分。
- 成本预算和在途/重试放大问题,1 分。
- 终止结果及既有副作用处理,1 分。
表主键为 order_id,二级索引为 (tenant_id,status,created_at)。查询先只取 order_id,created_at,后来增加 amount。解释两种查询的访问差别。
参考解答与评分点
二级索引记录已有三个索引列和主键信息,所以第一种查询可能由该索引覆盖。amount 不在索引中,第二种通常需要根据主键查聚簇索引取该列。是否扩宽索引,要比较读频率、回表量、额外写入和空间成本,并查看执行计划。覆盖描述字段可从索引获取,不能直接等同于全场景无需任何额外存储访问。
- 正确描述二级索引和主键信息,1 分。
- 说明第一种可能覆盖,1 分。
- 说明新增字段引起回表,1 分。
- 有验证与读写成本取舍,1 分。
接口返回 20 行却慢,ORM 里只有一个 findMany 调用。给出排查顺序,并解释为什么游标分页不能保证所有页面来自同一快照。
参考解答与评分点
先拿真实 SQL、参数和耗时,检查扫描行数、排序与执行计划,再查锁/I/O等待和连接池排队,确认是否有额外 N+1 请求。深 OFFSET 可考虑按 (created_at,order_id) 做游标分页。游标约束下一次读取的排序位置,数据库仍可能看到两次请求之间发生的插入或修改;若需求要求同一快照,需要额外设计快照或版本契约。
- 拿到 SQL、参数及可比较的耗时,1 分。
- 检查扫描/排序并覆盖等待原因,1 分。
- 描述唯一排序键与游标定位,1 分。
- 区分分页位置与一致快照,1 分。
阅读来源时,先查与本课对应的机制,不需要一次读完整手册。OpenAI 页面涉及多个 API,代码字段应跟所选模型与 API 对齐;MySQL 两页对应 InnoDB 的索引结构和联合索引前缀。
Sources
Section titled “Sources”[1] https://platform.openai.com/docs/guides/function-calling [2] https://dev.mysql.com/doc/refman/8.4/en/innodb-index-types.html [3] https://dev.mysql.com/doc/refman/8.4/en/multiple-column-indexes.html
下面保留原报告的题目和口述参考答案。先自行作答,再展开查看。
AG01|Agent、Workflow、普通聊天机器人有什么区别?〔P0〕
口述答案
我会按“谁决定下一步”来区分。普通聊天通常只生成一次响应;Workflow 由程序预先确定主要路径,模型负责其中的分类、抽取或生成;Agent 则根据目标与环境反馈,动态决定继续查什么、调用哪个工具、是否结束。它们不是互斥产品类别,一个系统可以用固定外层流程包住局部 Agent。
售后场景里,理解客户描述和查找相关政策可以动态探索;核算可退款金额、权限检查与支付执行不应交给自由规划。首先比较确定性流程、一次模型调用和 Agent 循环,在效果不足时才增加自主性。
追问怎么接
“能调用工具就叫 Agent 吗?”定义没有统一硬边界。一次固定工具调用也能完成任务,关键是是否存在基于反馈的动态决策,不是工具数量。“为什么不用 Agent 处理全部步骤?”动态决策增加不确定性、调用成本和验证难度;固定业务约束应保持可验证。
不要这样答:“Agent 就是 LLM + Memory + Tools + Planning,所以四个模块必须齐全。”这是常见组件描述,不是必要充分条件。
依据与延伸:Agent-Patterns。
AG02|Agent Runtime、Harness、SDK、模型分别负责什么?〔P0〕
口述答案
模型把给定上下文映射成文本或动作建议;SDK 封装模型 API、工具、会话和执行循环;Runtime 负责动作的实际调度、状态推进、错误和中断;Harness 通常指围绕模型的整套任务环境与控制设施,可能包含工具、提示、文件环境、权限、预算和评测。
这些词在不同项目中边界不同,面试时先声明自己的用法。我的设计里,模型不拥有数据库事务,不决定调用凭据,也不通过一句“完成了”修改任务终态;应用读取真实执行结果后推进状态。
追问怎么接
“框架替你做了什么?”说出具体契约:如何回传 tool result、何处保存状态、哪些异常可重试、谁维护审批中断、trace 如何关联。不要把“框架有 memory”解释成已经解决跨租户记忆、权限和持久存储。
**不要这样答:**把 SDK 的函数名当作系统架构;或者说用了 Agent 框架就不需要后端状态机。
依据与延伸:SDK-Run、LG-Overview。
AG03|ReAct、Plan-and-Execute、Reflection 怎么选择?〔P0〕
口述答案
ReAct 的核心是推理与行动交替,让观察结果影响下一步,而不是先凭空生成整条执行链。Plan-and-Execute 先产生计划再执行,适合需要显式分解的长任务,但执行中必须允许根据新事实修改计划。Reflection 是执行后进行检查或改进,检查器可以是规则、测试、人或另一个模型。
我会先用短循环解决任务。只有能观察到计划丢失、遗漏子任务或确实能被检查器纠正的错误,才加入规划或反思。计划文本是建议,不是已完成工作的证明。
追问怎么接
“是不是每轮都要输出思维链?”不需要。保存可观察的行动、证据、结果和必要的决策摘要即可,不依赖读取模型私有思维链。“反思一定提高准确率吗?”不保证;没有独立证据的自我批评可能重复错误,还增加延迟。要做同预算对照。
不要这样答:“多想一次就更可靠”“给模型加 planner 和 critic 就算架构升级”。
依据与延伸:ReAct、Agent-Patterns。
AG04|Function Calling 的完整执行链是什么?〔P0〕
口述答案
应用先提供工具名、用途和参数 schema;模型返回带调用标识的工具调用及参数;应用解析、校验、鉴权并执行真实函数;再把结果按原调用标识回传,模型据此继续调用或形成最终响应。对于自定义函数,生成调用请求和执行函数是两件事。 托管工具由谁执行则取决于所用 API 契约。
一轮可能有零个、一个或多个工具调用。执行器必须保留调用和结果的对应关系,区分模型响应未完成、拒答、工具错误和正常结束。对于要求回传额外响应项的模型/API,应保留规定的响应项,不能只拼接可见文本。
追问怎么接
“能边收到参数边执行吗?”一般先等完整参数与调用完成标志,再解析校验,避免拿半个 JSON 或后来被补全的金额执行写操作。“tool_call_id 就是幂等键吗?”不是;模型重试可能产生新调用 ID,业务操作要有跨重试稳定的身份。
不要这样答:“模型根据 JSON schema 自动执行了我的数据库函数。”以及把工具错误直接伪装成空查询结果。
AG05|JSON mode、Structured Outputs 和业务正确性有什么关系?〔P0〕
口述答案
JSON mode 主要解决输出可以作为 JSON 解析;按 schema 约束的 Structured Outputs 进一步限制结构和字段类型。Function Calling 用于表达工具调用意图,结构化最终输出用于让应用消费最终结果,两者用途不同,也可以组合。
结构正确不代表事实正确,更不代表有权限。即使 refund_amount 是合法整数,也可能超出订单余额;order_id 是字符串,也可能属于别的租户。因此仍要做服务端 schema 校验、资源归属校验、金额/币种约束和状态校验,并显式处理拒答、截断等非正常结果。
追问怎么接
“strict 后是否不需要验证?”仍需要。它约束模型输出结构,不校验实时业务事实,也不是跨所有 API/模型的统一承诺。“可选字段怎么写?”按对应供应商支持的 JSON Schema 子集设计,不能把某个 API 的 required/null 规则泛化成 JSON Schema 本身。
不要这样答:“用了 strict 就没有幻觉”“temperature=0 就能保证每次结果一致”。
依据与延伸:OpenAI-Structured、OpenAI-FC。
AG06|如何设计一个模型容易正确使用的工具?〔P0〕
口述答案
工具应表达有意义的业务动作,名称和参数能说明适用条件、限制与结果,而不是原样暴露所有内部 REST 接口。将只读查询与写操作分开;能从认证上下文或数据库推导的 tenant_id、权限、金额上限,不让模型自由填写。返回少量结构化事实、状态、来源和后续可用标识,避免把整张表塞回上下文。
错误结果要区分:参数不合法、无权限、业务冲突、可重试失败、结果未知。可以允许模型修正可理解的参数问题,但不得让它通过换个参数规避权限拒绝。
追问怎么接
“工具越细越好吗?”太细导致多轮调用和状态拼装错误;太粗隐藏高风险副作用。订单摘要适合聚合只读工具,退款应有明确执行边界。“工具返回 10 万行怎么办?”分页、过滤、聚合或保存 artifact handle,再按需查询,不先截断到可能误导模型的前几行。
不要这样答:“所有工具都包装成 execute_sql(sql) 最灵活”,或让模型通过工具参数选择管理员身份。
依据与延伸:Tool-Design、OWASP-Agency。
AG07|并行工具、异步工具、Code Mode、多 Agent 是一回事吗?〔P0〕
口述答案
不是。并行工具是在依赖允许时同时执行多个调用;异步工具允许模型或应用在工具结果尚未就绪时继续独立工作;Code Mode/程序化工具调用让模型生成一段有循环、分支和聚合的代码来编排工具;多 Agent 则涉及多个模型决策上下文。一个 Agent 完全可以并行执行多个工具,不需要多个 Agent。
对同一订单的“读取→核算→退款”有依赖,不该盲目并行。多个互不相关订单的只读检查可并行,但仍受全局与租户预算、下游配额限制。程序化批处理适合大量确定性数据处理,不自动赋予生成代码更多权限。
追问怎么接
“Code Mode 为什么可能更便宜?”中间大结果在执行环境聚合,只把必要结论送回模型,减少模型往返;这需要实验验证。“风险呢?”一段代码里藏了更多动作,仍要逐次授权、限时限资源、保留审计,并对部分成功设计恢复。
不要这样答:“Promise.all 就是多 Agent”“代码在沙箱里,因此调用任何业务工具都安全”。
依据与延伸:Programmatic、Async-Tools、OpenAI-FC。
AG08|怎样防止 Agent 死循环、反复重试和预算失控?〔P0〕
口述答案
执行器显式设置最大模型轮次、工具次数、总 token/费用预算、绝对截止时间,以及每个工具的超时和并发上限。预算耗尽进入确定终态或人工处理,不仅在提示词里说“不要运行太久”。
同时记录任务进展:重复调用相同参数是否得到新信息、是否反复遇到同一种拒绝、是否出现不可恢复错误。模型可以修正一次无效查询,但没有新证据时不无限循环。预算预留要覆盖已发出的并行请求,不能等返回账单后才发现总额超限。
追问怎么接
“一次参数错误也退出会不会太严格?”按错误分类:schema 错误可有限纠正;限流按策略退避;权限拒绝不应靠反复尝试绕过;已产生副作用但响应不明的请求先核查状态。“所有层都自动重试?”会乘法放大,需确定重试归属层。
**不要这样答:**只用 max_iterations 处理所有成本与时间风险;或者认为取消模型请求会自动取消所有已发出的工具。
依据与延伸:SDK-Run、AWS-Retry、Rate-Limits。
DB01|为什么用 B+ 树?聚簇、二级、覆盖索引和回表是什么?〔P0〕
口述答案
面试里说的 B+ 树索引,关键是多叉、按键有序、以页组织:较高扇出降低树高,适合页式存储的查找与范围扫描。哈希适合等值定位,但不直接提供这种有序范围能力;这不是说哈希在所有场景更差。
InnoDB 通常以主键作为聚簇索引,叶子层组织行数据;二级索引条目包含索引列与主键。查二级索引后再按主键找完整行就是“回表”。查询所需列能由索引提供时可利用覆盖索引减少回表;主键太长会放大二级索引空间。
追问怎么接
“索引越多越好吗?”增加写放大、空间和缓存压力。按实际过滤、排序和覆盖需求选择。“主键一定自增吗?”不是绝对要求,自增有局部性优势;分布式 ID 需考虑唯一性、写热点与键长度。“覆盖就永远不访问聚簇索引?”不要作跨所有可见性检查与执行计划的绝对承诺。
不要这样答:“索引是在内存里,所以查得快”“二级索引直接存数据行的物理地址”。
DB02|联合索引怎么设计?最左前缀、范围和深分页呢?〔P0〕
口述答案
联合索引按列顺序形成词典序。假设常查某租户、某状态的最新订单,我会考虑 (tenant_id, status, created_at, id),先匹配等值条件,再利用时间和 ID 排序。字段顺序要结合查询形态、选择性与排序,不是机械地“区分度最高放第一”。
范围条件会影响后续列能如何缩小扫描区间,但后续列仍可能参与索引条件过滤或覆盖。缺少最左列也不意味着数据库绝不使用该索引,可能有索引扫描或满足条件的 skip scan;最终看执行计划。
追问怎么接
“LIMIT 100000, 50 怎么优化?”可以用上一页最后的 (created_at,id) 做游标,避免大量跳过;排序必须稳定。若列表数据变化,游标不自动提供整个浏览过程的快照一致性,需按产品要求设计时间边界/快照。
不要这样答:“范围后面所有列都失效”“用了联合索引,就不需要 EXPLAIN”。
依据与延伸:DB-Composite、DB-Range。
DB02 的白板 SQL 示例(MySQL 8.4;参数绑定由驱动完成)
CREATE INDEX idx_order_page ON orders (tenant_id, status, created_at, id);
SELECT id, created_atFROM ordersWHERE tenant_id = ? AND status = ? AND (created_at < ? OR (created_at = ? AND id < ?))ORDER BY created_at DESC, id DESCLIMIT 50;这里后两个时间参数取同一个游标时间;首次查询去掉游标谓词。id 用来打破相同时间的排序歧义。它是待验证的索引设计,不是“无论数据分布都最优”的结论;用真实分布检查扫描量。DB-Composite DB-Explain
DB03|慢查询怎么排查?用了 ORM 怎么回答?〔P0〕
口述答案
先定位慢在哪里:等连接、等锁、数据库执行、网络传输还是应用处理;抓真实 SQL、参数形态、耗时与返回行数。再看执行计划的访问方式、索引、估计行数,必要时在安全环境用 EXPLAIN ANALYZE 对照实际行数、循环次数和耗时。
ORM 不改变这些判断。检查是否 N+1 查询、SELECT 取过多列、分页后再全表排序、缺索引或类型转换。优化后在代表性数据上测读写代价,不只看一次快查询。EXPLAIN ANALYZE 会实际执行语句,不能把它当无成本的静态分析。
追问怎么接
“没走索引就一定有问题?”小表或低选择性查询,全表扫描可能更划算。“加连接池能解决慢 SQL 吗?”只能影响建连/等待,不能修复错误计划,还可能把数据库压得更满。“N+1 怎么改?”按 key 批量查询或适当 join,再检查结果膨胀和分页语义。
不要这样答:“看到慢查询就加索引”“业务用 ORM,所以不需要懂 SQL”。
依据与延伸:DB-Explain、DB-Composite。