AI智能体算法一面、二面、三面面经
完整信息
本篇目录
写在前面
【WeThinkIn出品】栏目专注于分享Rocky的认知思考与经验感悟,范围涵盖但不限于AI行业。
干货AIGC、AI Agent、LLM算法工程师/开发工程师面试面经秘籍分享(持续更新):WeThinkIn/AIGC-Interview-Book欢迎大家Star~
大家好,我是Rocky Ding。
智能体算法岗位的能力边界,已经延伸到模型、业务规则和执行系统的连接处。模型决定下一步值得尝试什么,运行时决定这个动作能否执行,评测体系判断任务是否真正完成。一个可上线的 Agent,需要把概率性的模型输出,接入有权限、有预算、有证据、可恢复的执行闭环。
这组三轮问答可以沿着一条主线理解:一面考察承载 Agent 的后端基础,二面关注任务闭环与多模态评测,三面进一步讨论失败恢复、模型路由与资源治理。Rocky 更看重的能力,是能否解释每项设计解决了什么问题、增加了什么代价,以及如何证明它在真实流量下有效。
目录
一面
- 1. 先做一下自我介绍,你最近主要关注哪类技术问题
- 2. JVM 中对象从创建到回收大致经历了哪些阶段?
- 3. 线上出现频繁 Full GC 时,你会怎样定位?
- 4. Java 中如何设计一个不会产生大量锁竞争的任务调度器?
- 5. Java 内存模型中的 happens-before 规则解决了什么问题?
- 6. 如果一个接口同时受到 CPU 和数据库 IO 的限制,你会怎样拆解瓶颈?
- 7. MySQL 的 B+Tree 索引为什么适合范围查询?
- 8. 联合索引 (tenant_id, status, created_at) 在哪些情况下无法充分利用?
- 9. 在高并发写入场景下,MySQL 的间隙锁可能造成什么问题?
- 10. Redis 的持久化机制应该如何选择?
- 11. Redis 分布式锁怎样避免锁被误删?
二面
- 1. 请先做一下自我介绍,聊聊你实习期承担过的工作。
- 2. 懂不懂Go,如果让你转Go能接受吗
- 3. 介绍下项目,如何设计从数据接入到结论生成的整体架构?
- 4. 如果 Agent 在调用工具时出现参数类型正确但业务含义错误,如何发现并阻止?
- 5. 如果工具调用一直没有达到成功条件,任务不断重试并最终卡死,你会如何处理?
- 6. CoT、ReAct、Plan-and-Execute 在工程实现上有什么区别?如何选择?
- 7. 在长上下文对话中,模型过早遗忘用户最初的硬性要求,你会怎样解决?
- 8. 如何设计一个能够避免循环调用的 Agent 状态机?
- 9. 如何判断一个图片理解 Agent 的评测指标是否足够完整?
- 10. 如果评测集上的准确率很高,但线上效果很差,你会怎样排查?
- 11. 如果用户说“只修改图片左上角的文字”,怎样避免模型改动其他区域?
三面
- 1. 请先做一下自我介绍
- 2. 基于 LangChain 开发 Agent 时,如果工具调用连续失败,如何设计一套不会失控的容错机制?
- 3. 如何设计 Qwen、视觉语言模型和轻量模型之间的动态路由策略?
- 4. 置信度阈值应该如何定义?为什么不能直接使用模型输出的概率?
- 5. 如果 Prompt 中给了明确的输出格式,但模型仍然经常生成非法 JSON,如何处理?
- 6. 图片审核系统在面对超大图片、切片图片和多模态并发请求时,主要会出现哪些问题?
一面
1. 先做一下自我介绍,你最近主要关注哪类技术问题
回答:
自我介绍应建立“个人能力与岗位需求”的对应关系。可以按照背景、实际负责的项目、解决过的问题、最近关注的方向来组织,用一到两分钟讲清楚。以下是需要替换成真实经历的模板,不能把示例当作自己的业绩:
“我目前是[学校/专业/工作背景],主要做[算法或开发方向]。最近参与的[项目名称]面向[用户与业务场景],我负责[明确模块],其中最有代表性的问题是[具体瓶颈]。我通过[方案],在[测试集/流量与资源条件]下,把[指标]从[基线]改善到[结果],同时控制了[成本或风险]。
“近期我关注 Agent 的可靠执行:模型输出如何转为可校验的工具参数,工具超时后如何判断是否已经执行,以及如何通过任务级评测定位失败。我也在补齐并发、数据库和可观测性,因为这些能力直接影响智能体的线上稳定性。”
值得展开的技术问题应当有证据。 例如,能展示一条失败轨迹,解释问题发生在检索、规划、参数、权限还是外部服务,再说明修复前后的对照结果。没有线上数据,就说明使用离线回放或模拟压测,不编造 QPS、成功率和业务收益。
2. JVM 中对象从创建到回收大致经历了哪些阶段?
回答:
以 HotSpot 中普通 new 对象为例,可以沿着“类的准备、分配、初始化、使用、可达性变化、回收”理解。类加载与解析并非每创建一个对象都重新执行;new 涉及的类在需要时完成加载、链接,并在首次主动使用前完成类初始化。类初始化执行静态初始化逻辑,与每个对象的构造方法不同。
- 分配内存。 常见路径先尝试线程本地分配缓冲区 TLAB,减少共享堆分配竞争;大对象、TLAB 不足或不同收集器会进入其他分配路径。具体使用指针碰撞还是空闲空间管理,取决于堆组织方式。
- 建立初始状态。 实例字段先获得零值,运行时设置对象头等信息;随后执行实例初始化和构造逻辑。零值初始化不等于执行了业务构造方法。
- 使用与发布。 对象被线程、集合或其他对象引用。跨线程传递仍需安全发布,不能认为“构造完了”就天然对另一线程可见。避免在构造函数中让
this提前逸出。 - 判断存活。 GC 从线程栈、静态字段等根集合出发分析可达性,并处理强、软、弱、虚引用等不同语义。循环引用本身不会阻止可达性分析回收。
- 回收或移动。 收集器释放不可达对象占据的空间,可能伴随复制、整理和引用更新。对象何时回收没有应用层的及时性保证。
并非所有对象都实际完成一次堆分配:JIT 的逃逸分析和标量替换可能消除分配,但这不意味着“所有不逃逸对象都直接放到栈上”。晋升老年代也不是所有对象必经的阶段,具体取决于收集器和存活情况。
finalize() 已被弃用并标记为待移除,不能用于可靠释放资源。文件、连接、锁应采用 try-with-resources、显式关闭或 finally 等确定性路径;Cleaner 也只是兜底,不能承诺及时执行。
3. 线上出现频繁 Full GC 时,你会怎样定位?
回答:
先确认“Full GC”在当前 JDK 和收集器中的实际含义,再判断是分配压力、存活对象增长、显式触发,还是收集器无法及时完成回收。G1 的 Mixed GC 不等同于 Full GC,不能仅凭监控面板名称下结论。
第一步记录 JDK、收集器、堆配置、容器内存上限、GC 原因及暂停时长,关联请求流量、发布和定时任务。重点观察 GC 前后堆占用,尤其是回收后的存活集趋势:回收后仍持续增长,才更支持泄漏或缓存持续积累;占用能大幅下降但很快再次填满,更像分配速率过高或堆配置不匹配。
jcmd <pid> VM.version
jcmd <pid> VM.flags
jcmd <pid> GC.heap_info
jstat -gcutil <pid> 1000 10
JDK 9 及以后的 HotSpot 可以预先配置统一 GC 日志,例如 -Xlog:gc*,safepoint:file=gc.log:time,uptime,level,tags,并设置合理轮转。jstat 的列与解释需要结合收集器,不能替代 GC 原因日志。
| 现象 | 优先假设 | 下一步证据 |
|---|---|---|
| GC 后存活集持续增加 | 无界缓存、集合持有、ThreadLocal、类加载器泄漏 | 对象占用趋势、支配树、GC Roots 引用链 |
| 回收后下降明显但触发很快 | 高分配速率、大批处理、堆偏小 | JFR 分配采样、对象大小分布、流量变化 |
| G1 evacuation failure 或大对象压力 | 预留空间不足、humongous 分配等 | Region 与 GC 原因日志 |
| 堆并未接近上限仍触发 | System.gc()、元空间压力等 |
触发原因、类加载统计 |
| RSS 很高但 Java 堆正常 | 直接内存、线程栈、原生库等 | NMT、进程内存、容器事件 |
直方图和堆转储是后续证据工具,jmap -histo:live 等操作可能触发 GC 或长暂停,不能在故障高峰无评估地执行。VM.native_memory 需要提前启用 NMT,且 NMT 不覆盖所有第三方原生分配。
先通过限流、缩小批量、隔离异常实例或回滚止血,再针对根因修复。增大堆只有在存活集、分配速率和暂停目标共同支持时才成立;否则只是延迟故障,并可能增加暂停或容器 OOM 风险。
4. Java 中如何设计一个不会产生大量锁竞争的任务调度器?
回答:
先明确约束:任务是否要求全局顺序、同一租户顺序、优先级、公平性和至少一次执行。锁竞争常常源于大量线程同时修改同一个队列或状态表,优化重点是减少共享写入,以及缩短必须串行的临界区。
可以采用“接入与准入控制 → 分片队列 → 执行器 → 结果持久化”的架构。按 tenant_id 或业务键稳定分片,让同一键进入同一队列;生产者只提交任务,消费者批量取出任务,耗时计算和网络调用放在锁外。分片只能保持分片内顺序,不能同时免费获得全局严格优先级。单一热点租户仍可能成为瓶颈,需要租户配额和公平调度。
CPU 密集型任务使用受限的计算并行度;阻塞 IO 任务可以用独立线程池或适合该 JDK 的虚拟线程,但数据库连接、外部接口和内存仍要通过信号量或并发上限保护。工作窃取适合可拆分计算,不宜把大量不可控阻塞 IO 直接塞进公共 ForkJoinPool。
队列必须有容量与超时约束。在稳定系统中,Little 定律给出:
$L=\lambda W$
其中 $L$ 是平均在途任务数,$\lambda$ 是平均到达率,$W$ 是平均逗留时间。这是容量估算起点,突发流量仍需压测与余量。CallerRunsPolicy 能形成背压,但会占用提交线程,若提交线程是事件循环或承担低延迟请求,就可能放大故障,不能直接套用。
对于不能丢失的任务,采用持久化任务表或消息队列,配合领取租约、幂等键、重试队列和死信处理;内存线程池无法保证进程崩溃后的可靠执行。最后用锁等待、队列长度、排队 p95/p99、吞吐、失败率及租户公平性验证设计。更少的锁只是手段,稳定完成任务才是目标。
追问:如何估算并发上限? 假设压测得到到达率为每秒 200 个任务、平均服务时间 80 毫秒,则仅正在服务的平均并发约为 16;若连同排队的平均逗留时间为 150 毫秒,平均在途数量约为 30。两者分别对应不同系统边界,不能把 30 直接当作 CPU 线程数。这个算例没有包含峰值、长尾和重试,实际配置还需满足数据库连接池、下游容量和内存预算。
采用分片执行器时,要明确顺序的含义。同一键按队列顺序“开始执行”,不保证异步任务按同一顺序“完成”;若后续任务依赖前一个提交结果,需要按键串行完成或使用版本检查。任务领取租约到期后的重复执行,也应通过业务幂等处理。
5. Java 内存模型中的 happens-before 规则解决了什么问题?
回答:
happens-before 是 Java 内存模型定义的可见性与顺序保证关系。若操作 A happens-before 操作 B,A 的相关效果必须按规范对 B 可见;编译器和处理器仍可重排,只要不破坏这些可观察保证。它不是墙上时钟意义上的“先执行”,也不自动把一组操作变成原子事务。
关键规则包括线程内程序顺序、同一监视器的解锁与后续加锁、同一 volatile 变量的写与后续读、线程 start() 与被启动线程的操作,以及线程中的操作与其他线程成功 join() 返回之间的关系。关系具有传递性,因此一次正确同步可以安全发布此前写入的普通字段。
class Holder {
int value;
volatile boolean ready;
}
// Thread A
holder.value = 42;
holder.ready = true;
// Thread B
if (holder.ready) {
System.out.println(holder.value);
}
在 holder 本身已安全共享、ready 从初始 false 被 A 一次性写为 true、且 value 没有其他竞争写入的条件下,B 观察到 ready == true 时应看到 value == 42。链路是普通字段写入 → volatile 写 → volatile 读 → 普通字段读取。
但 volatile int count 上的 count++ 仍包含读取、加一、写回,不具备整体原子性,需要原子类或锁。Thread.sleep() 不建立线程间 happens-before;“等一会儿应该就能看到”没有规范保证。多个字段的联合不变量也不能靠分别声明 volatile 自动保持。
6. 如果一个接口同时受到 CPU 和数据库 IO 的限制,你会怎样拆解瓶颈?
回答:
把请求拆成可测量的阶段,区分“正在工作”和“排队等待”。应用 CPU 时间、线程池排队、连接池等待、数据库执行、结果传输、序列化,都应该有独立观测。对于串行链路,可近似写成:
$T_{\mathrm{request}}=T_{\mathrm{queue}}+T_{\mathrm{cpu}}+T_{\mathrm{pool}}+T_{\mathrm{db}}+T_{\mathrm{network}}$
存在并行查询时,端到端耗时由关键路径决定,不能把所有 span 的时长相加。也不能把不同请求各阶段的 p99 相加当作整条链路的 p99。
先用分布式追踪定位慢请求,再用 CPU profile、线程状态、容器 CPU throttling、连接池等待、慢查询和执行计划相互印证。CPU 高不一定是业务计算重,也可能是 GC、锁自旋、序列化或不合理重试;数据库 span 长,也可能主要花在等待连接而不是执行 SQL。
处理上分三步:减少无效工作,例如批量查询、避免 N+1、减少重复序列化;改善单位请求效率,例如合适索引、投影必要字段、预计算或有失效策略的缓存;最后调整并发和资源配额。SQL 优化应同时看扫描行数、返回行数、锁等待与实际执行耗时。
用固定流量模型做阶梯压测,观察吞吐何时停止增长、排队何时陡升。应用扩容可能把压力转移到数据库,连接池扩容也可能增加锁争用和随机 IO。瓶颈治理要看端到端容量与尾延迟,不能把某个组件利用率下降当成整体优化成功。
7. MySQL 的 B+Tree 索引为什么适合范围查询?
回答:
B+Tree 把索引项按键有序组织,内部节点负责导航,叶子页保存索引记录,叶子页之间可顺序遍历。范围查询先定位下界,再扫描后续叶子记录,直到到达上界,从而复用相邻键的有序性,避免对每条结果重新从根查找。
InnoDB 的索引按页管理,较高扇出使树高通常较低。典型范围扫描的代价可理解为一次树下降,加上覆盖目标区间的叶子页访问;缓存、页分裂、记录宽度和磁盘布局都会影响实际 IO。逻辑相邻的叶子页不保证物理磁盘连续,不能把“链表连接”直接等同于完全顺序磁盘读。
聚簇索引的叶子记录包含整行数据;二级索引叶子记录包含二级键和主键等信息,查询非覆盖字段时通常需要回表。范围命中比例大、二级索引回表多时,优化器仍可能选择全表扫描。
哈希结构擅长等值定位,但不天然支持有序区间;B+Tree 同时服务等值、范围、部分排序和联合索引前缀访问。是否避免排序,还要看 ORDER BY、前导列条件和扫描方向是否匹配。适合范围查询的原因是有序页结构与范围扫描能力,不是“建了索引就一定更快”。
8. 联合索引 (tenant_id, status, created_at) 在哪些情况下无法充分利用?
回答:
这个索引按三列的字典序排列:先按租户,再按状态,最后按创建时间。要分别判断它能否缩小扫描区间、能否在索引层过滤、能否覆盖查询,以及能否提供所需顺序。“后面的列没用于区间定位”不等于“后面的列完全没用”。
| 查询形态 | 典型行为与边界 |
|---|---|
tenant_id = ? AND status = ? AND created_at >= ? |
等值前缀后接时间范围,适合构造窄扫描区间 |
status = ? AND created_at >= ? |
缺少租户前缀,通常无法直接定位连续小区间;部分符合条件的查询可能采用 skip scan,不能依赖它兜底 |
tenant_id = ? AND created_at >= ? |
可按租户定位,但时间条件通常跨多个状态分组;可能扫描租户较大区间后过滤 |
tenant_id = ? AND status > ? AND created_at = ? |
status 的范围通常限制继续用时间列收窄连续区间;时间列仍可能用于索引条件下推 |
tenant_id = ? ORDER BY created_at |
多个状态内分别有序,不保证租户下所有记录按时间全局有序,可能需要额外排序 |
status IN (...) 可以被优化为多个等值分支,不能简单归入“范围后面都失效”。多列范围端点的细节也应以执行计划为准。其他常见问题包括对索引列使用不匹配的函数表达式、某些隐式类型或字符集转换、低选择性导致大量回表、统计信息与真实分布偏差。
对于字符列,LIKE 'abc%' 有机会转为前缀范围,LIKE '%abc' 或 '%abc%' 通常无法用普通 B+Tree 定位起始范围,前者不能被泛称为“前缀匹配失效”。覆盖索引扫描仍可能发生。
用 EXPLAIN 或在可控环境使用会实际执行查询的 EXPLAIN ANALYZE,检查访问类型、使用的键部分、实际扫描和返回行数、Using index condition、回表及排序开销。根据租户规模、状态分布和查询负载决定是否增加 (tenant_id, created_at),同时计算索引维护的写入成本。
追问:分页为什么不能一直使用大 OFFSET? 对固定租户和状态的时间排序,深分页会扫描并丢弃大量索引记录。可以采用基于上一页末尾键值的游标分页,例如按 (created_at, id) 排序,并以这两个值作为下一页下界。id 用于打破时间相同的并列关系,排序方向与边界条件必须一致。InnoDB 二级索引通常包含主键,但是否满足该排序、是否还需调整索引,应看实际定义与执行计划;并发插入和更新时还要定义跨页快照语义。
9. 在高并发写入场景下,MySQL 的间隙锁可能造成什么问题?
回答:
InnoDB 间隙锁作用于索引记录之间的间隙,主要抑制向该区间插入记录。默认可重复读隔离级别下,部分锁定读和写操作会使用 next-key locking,即记录锁与间隙锁的组合,以保护搜索范围。普通一致性快照读通常通过 MVCC 工作,不应笼统说“范围 SELECT 都会加间隙锁”。
例如事务执行 SELECT ... WHERE tenant_id = 10 AND created_at >= ? FOR UPDATE,具体锁范围由实际使用的索引、扫描边界和记录分布共同决定,不是 SQL 文本中的逻辑条件自动映射为精确最小锁范围。没有合适索引或扫描范围过大,可能阻塞大量本来希望并发执行的插入。
高并发下主要出现锁等待、事务超时、队列堆积与死锁。不同事务持有的纯间隙锁可以兼容,但插入时申请的插入意向锁仍可能被阻塞;“双方都先锁住空区间,再尝试插入”就可能形成等待环。死锁不是只有更新同一行才会出现。
处理顺序是:查看 performance_schema.data_locks、data_lock_waits 和死锁记录;把等待映射到 SQL、索引与事务;缩短事务并缩小扫描范围,统一访问顺序,避免在持锁期间调用外部服务。唯一索引上满足条件的完整唯一键等值查找可减少间隙锁使用,但查找不存在的键及部分唯一键条件需要单独分析。
READ COMMITTED 会减少搜索扫描中的间隙锁,但外键和重复键检查等场景仍有例外,且业务可观察语义改变。必须先验证业务不变量。死锁受害事务需要回滚并进行有限、带随机退避的整事务重试,同时保证业务幂等。
10. Redis 的持久化机制应该如何选择?
回答:
先定义允许丢失多少数据,即 RPO,以及服务多久必须恢复,即 RTO,再选择持久化策略。Redis 是可丢弃缓存、会话存储还是任务协调状态,答案会不同。
| 机制 | 核心行为 | 主要代价与边界 |
|---|---|---|
| RDB | 保存某个时间点的数据快照 | 文件便于备份,恢复通常较快;快照之后的写入可能丢失 |
| AOF | 记录重建数据所需的写操作 | always、everysec 等刷盘策略在耐久性与延迟之间取舍 |
| RDB 与 AOF 结合 | 快照与增量日志配合恢复 | 需要核对混合持久化配置、AOF 重写与恢复流程 |
appendfsync everysec 通常把故障数据丢失窗口控制在约一秒量级,但这不是所有磁盘阻塞、操作系统和故障条件下的硬性上限;always 更强调刷盘耐久性,也带来更高写入成本。RDB 后台保存与 AOF 重写都可能产生 fork、写时复制和磁盘 IO 压力,需要为峰值内存留出余量。
较新的 Redis 使用多段 AOF 组织方式,备份与恢复必须保留完整文件集合和清单,不能只拷贝一个想象中的 appendonly.aof。同时启用 AOF/RDB 时,启动通常优先使用更完整的 AOF 路径恢复,仍应按部署版本演练。
可重建缓存可以弱化持久化,但要解决重启后的回源洪峰;不可轻易重建的状态则应加强持久化、备份与恢复测试。异步复制和自动故障转移不等于强一致耐久存储,主节点已确认写入也可能在切换中丢失。持久化、复制、备份和业务幂等是不同保障,必须一起设计。
11. Redis 分布式锁怎样避免锁被误删?
回答:
加锁时使用每次获取都不同的随机 token,并通过单条 SET key token NX PX ttl 原子设置排他条件和过期时间。释放时必须在 Redis 端原子地比较 token 并删除,不能在客户端先 GET 再 DEL:这两个请求之间锁可能过期并被别人重新获取。
-- KEYS[1]: lock key; ARGV[1]: token of this acquisition
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
end
return 0
在支持该命令的 Redis 8.4 及以后版本,也可以使用原生比较删除 DELEX key IFEQ token;实际部署需确认服务端版本与客户端支持。Lua 方案适合需要兼容较早版本的场景。
原子比较删除解决的是“A 的旧锁过期后,A 误删 B 新锁”。它没有解决“A 的租约过期后仍继续写业务数据”。例如 A 长时间暂停,B 已取得新锁并更新数据,A 恢复后依然可能把 B 的结果覆盖。
更强的保护是 fencing token:每次授予锁时生成单调递增的世代号,受保护的数据库或服务记录已经接受的最大世代,拒绝旧世代写入。这个检查必须与业务写入原子执行,不能只在 Agent 内存里比较。生成世代号的机制也必须满足实际故障模型下的单调性,不能忽略 Redis 异步复制回滚计数的风险。
续约也应原子校验持有者,但续约失败、网络分区和进程暂停仍需要明确的停止写入策略。对于跨服务关键一致性,优先考虑数据库条件更新、唯一约束或有合适一致性保证的协调服务。正确解锁保护锁的所有权;fencing 或事务约束保护业务结果,两者不能混为一谈。
配图文字:
锁所有权与业务写入,是两道不同的保护
随机token防误删;单调世代号帮助受保护资源拒绝陈旧持有者。
时刻1:A获得租约。A随机锁标识=token_A。业务写入世代=41。A随后发生长时间暂停。
时刻2:租约到期,B获得租约。B随机锁标识=token_B。业务写入世代=42。受保护资源记录已接受世代42。
时刻3:A恢复并尝试释放。原子比较token_A与当前值。不匹配→不删除B的锁。避免旧持有者误删新锁。
时刻4:A尝试写业务。资源原子检查世代41<42。拒绝陈旧请求,保护B的结果。同世代去重仍依赖幂等与业务约束。
示例数字不代表具体服务实现;世代生成与故障恢复必须满足所需一致性。
图示将“防误删”与“拒绝陈旧写入”分开。同一个有效世代可能提交多个不同操作,因此 fencing 本身也不替代业务动作的幂等键。锁、版本、幂等三种机制分别管理持有权、陈旧状态和重复动作。
二面
1. 请先做一下自我介绍,聊聊你实习期承担过的工作。
回答:
这一问要讲清楚自己的责任范围和交付,而不只是介绍整个团队做了什么。可以把真实经历组织成“业务链路 → 个人负责模块 → 一个关键难题 → 方案与验证 → 尚未解决的边界”。
例如:“我在[团队]实习期间参与[项目],主要负责[模块],上游接收[数据/请求],下游输出[结果]。当时的基线存在[问题],我通过[排查证据]定位到[原因],实现了[改动]。验证采用[固定测试集/影子流量/灰度],核心指标是[成功率、p95 时延、成本等],结果为[真实数据]。另外,我负责了[监控/失败恢复/回归测试],目前的限制是[真实限制]。”
如果做的是 Agent,最好能展开一次真实工具调用:为什么调用、参数如何产生、权限在哪里检查、超时如何恢复、最后如何判断任务完成。如果只是接入模型 API,就如实说接入与评测;如果没有训练模型,不把 Prompt 或 RAG 优化写成大模型训练。
技术深度来自自己能解释并验证的决策。 面试官追问指标提升时,应能说明基线、样本量、分布、随机性与资源预算;没有足够证据的收益,保留为观察,不扩大成结论。
2. 懂不懂Go,如果让你转Go能接受吗
回答:
先如实说明熟悉程度,再给出转语言的依据和学习方案。可以回答:“我目前最熟悉[真实语言],对 Go 的掌握到[实际程度]。如果岗位主要使用 Go,我愿意转,计划先交付一个带超时取消、并发控制、日志和测试的真实服务,再逐步熟悉项目里的工程规范。”没有实际使用经验,就不要声称写过生产 Go 服务。
从工程上看,语言迁移重点在运行时和生态。Go 的 goroutine 由运行时调度到操作系统线程,创建成本较低,但仍消耗栈、堆与调度资源;channel 是通信和同步手段,mutex 同样合理,不能认为“用了 channel 就没有数据竞争”。需要理解 context 取消传播、错误返回、接口、切片共享底层数组、map 并发限制和逃逸行为。
对 Python,应区分传统带 GIL 的 CPython 与可选的 free-threaded 构建及其扩展兼容性。传统 CPython 的线程不擅长并行执行纯 Python CPU 密集计算,但网络 IO、释放 GIL 的数值库、异步 IO 和多进程各有适用场景。Go 的并发便利也不意味着模型推理本身更快,真正推理通常由底层运行时和 GPU 服务承担。
在 Agent 系统里,Python 常用于算法实验、数据与模型生态,Go 可以承接网关、工具执行、并发服务和基础设施;具体按团队已有技术栈和接口边界选择。可以用 go test -race、基准测试和 pprof 检验并发正确性与性能,而不是以语言名称判断工程水平。
3. 介绍下项目,如何设计从数据接入到结论生成的整体架构?
回答:
项目介绍应基于真实经历。下面以“企业数据分析 Agent”为设计示例:输入业务问题,读取授权数据,执行可复现分析,输出带证据的结论。核心是让每条结论能够追溯到数据、计算与权限上下文。
| 层次 | 核心职责 | 必须保留的对象 |
|---|---|---|
| 数据接入 | 数据库、文档、图片与 API 接入,校验质量和访问范围 | 数据版本、时间戳、租户、访问控制 |
| 任务解析 | 提取目标、指标、实体、时间范围和禁止事项 | 结构化任务契约、待澄清项 |
| 检索与规划 | 选择数据源、生成依赖步骤,检查成本和权限 | 查询计划、工具白名单、执行预算 |
| 执行 | 执行只读查询、OCR、统计或受控工具 | 参数、幂等键、调用状态、结果证据 |
| 校验 | 检查指标口径、单位、数据完整性和跨步骤一致性 | 规则结果、缺失字段、冲突证据 |
| 结论生成 | 依据已验证证据组织答案 | 结论与证据映射、限制和不确定性 |
结构化指标优先通过确定性查询和计算获得,避免模型凭文本猜数;非结构化文档再采用检索、OCR 或视觉理解。SQL 工具要限制访问表、租户、行数、时长与只读权限,不能只依靠 Prompt 中“请勿修改数据”。
控制面管理任务状态、权限、预算和重试;数据面承载查询、模型和工具。异步任务应保存检查点,外部写入需要幂等与恢复查询,不能把“恢复对话历史”等同于“恢复执行事务”。每次运行记录模型、Prompt、工具与数据版本,方便复现。
结论校验至少检查三件事:数字能否重算、描述是否被证据支持、观察到的相关性是否被误写成因果。上线指标覆盖任务成功率、结论支持率、无权限访问拦截、尾延迟和单次成功任务成本。先建立可解释的基线,再决定是否真的需要多 Agent 协作。
配图文字:
Agent:模型提出动作,运行时保证边界
显式状态贯穿规划、工具执行、结果验证与故障恢复。
任务契约:目标/禁止事项/权限/预算。约束有版本,修订有依据。输入歧义先澄清。
执行关卡:Schema+业务语义+授权。写入绑定幂等键与资源版本。校验与提交之间防止竞态。
结果验证:工具返回成功≠业务完成。核对外部可观察后置条件。结论绑定数据、计算与证据。
恢复与终止:结果未知→对账后再决定重试。有界退避/有界重规划。明确DONE、FAILED与CANCELLED。
工程示意;检查点保存执行进度,业务幂等与事务仍需服务端保证。
一次任务的最小可复现记录,可以包括任务契约版本、检索数据版本、规范化工具参数、模型与提示词版本、调用状态、最终产物及验证结果。用户后来修改了条件,必须生成新版本并重新检查待执行动作,不能让旧计划带着已过期的授权继续执行。
4. 如果 Agent 在调用工具时出现参数类型正确但业务含义错误,如何发现并阻止?
回答:
JSON Schema 能限制类型、枚举和结构,也能表达部分静态值域,但无法自动知道当前订单归谁、操作人是否有权限、状态是否允许迁移。这类错误必须在模型之外设置语义验证与执行授权关卡。
第一层解析和协议校验:拒绝未知字段、非法枚举、缺失参数、超过范围的值,以及单位不明确的金额。第二层业务校验:读取权威状态,检查租户归属、资源版本、金额单位、合法状态迁移和时间范围。第三层授权:由服务端身份绑定权限,不信任模型自己填写的 tenant_id、is_admin 或审批结果。
高风险动作先生成具体预览,审批必须绑定操作内容、资源版本、有效期和请求主体。仅仅存在一个 approval_id 字符串,并不代表审批有效;参数变化后不能沿用旧审批。工具返回文本和检索文档属于数据,不能因其写着“管理员已批准”就提升权限。
还要避免检查与执行之间的竞态。比如校验时订单可取消,执行前已经发货,应在事务中再次确认,或者通过条件更新验证版本与状态:
UPDATE orders
SET status = 'CANCELLED', version = version + 1
WHERE tenant_id = :trusted_tenant
AND order_id = :order_id
AND version = :expected_version
AND status = 'PENDING';
该语句假设取消 PENDING 订单符合业务规则;仍须先校验身份、审批与业务幂等。影响行数为零表示条件未满足,要查明是状态冲突、版本变化还是资源不存在,不能让模型反复尝试不同订单号。
对模型解释出的业务意图也要验证。即使所有参数都合法,取消 A 订单与用户要求修改 B 订单仍然不一致。最终需将候选动作与用户任务契约对照;存在关键歧义时澄清,不猜测执行。
5. 如果工具调用一直没有达到成功条件,任务不断重试并最终卡死,你会如何处理?
回答:
先把“任务成功”定义为外部可验证的后置条件,再区分瞬时故障、参数错误、执行结果未知和规划无进展。不能让模型通过不断说“再试一次”延长任务,也不能用“状态没变”一个条件直接否决所有重试。
| 失败类型 | 处理方式 |
|---|---|
| 只读请求限流、暂时不可用 | 有界重试,遵守 Retry-After,指数退避加随机抖动 |
| 参数缺失或不符合约束 | 返回结构化错误,有限次数修正参数并重新校验 |
| 权限拒绝、明确不允许的业务操作 | 终止该动作,必要时等待正规授权 |
| 写操作超时,是否提交未知 | 先按幂等键查询执行状态,不立即重复写入 |
| 工具成功但任务仍无进展 | 检查计划假设和证据,有限重规划或澄清 |
任务运行时维护总体 deadline、调用次数、模型 token、费用、重规划次数和在途调用。预算要跨工具与模型统一累计,不能每次换工具就重置。指数退避可采用 full jitter:
$\Delta t_k\sim U!\left(0,\min(t_{\max},t_0 2^k)\right)$
其中 $k$ 是本轮重试序号。等待时间与下一次调用都必须落在剩余 deadline 内,取消应传播到可取消的底层请求。客户端停止等待不代表外部操作已经停止,应继续核对副作用状态。
“没有状态变化”要按任务解释:读取服务暂时 503 后状态未变,仍允许预算内重试;数据库异步任务正在运行,需要退避轮询;同一个模型反复输出相同无效参数,才更像无进展循环。动作指纹应结合工具、规范化参数、任务状态版本和证据变化判断。
超过预算后输出已完成步骤、确定失败项、状态未知项和恢复入口。能明确终止并保留现场的失败,比伪装成持续工作的死循环更可控。
6. CoT、ReAct、Plan-and-Execute 在工程实现上有什么区别?如何选择?
回答:
三者处于不同层次:CoT 是与分步推理相关的方法,ReAct 是推理与环境交互交替的模式,Plan-and-Execute 是将计划与执行职责分开的架构。它们可以组合,不是严格互斥的三个框架。
| 方法 | 运行方式 | 适用任务 | 主要边界 |
|---|---|---|---|
| CoT | 模型内部或单次生成中进行中间推理 | 有足够输入的一次性分析 | 不等于执行协议,文本解释不保证忠实反映真实计算 |
| ReAct | 观察结果后选择下一动作,逐步与环境交互 | 信息未知、需动态探索与查询 | 调用轮次、错误累积、成本和循环风险 |
| Plan-and-Execute | 生成结构化计划,执行器完成步骤,必要时重规划 | 长流程、有依赖、可并行与可恢复任务 | 计划可能过时,需要反馈、检查点和重规划 |
生产系统应依赖显式计划、工具参数、观察结果和验证状态,不要求输出模型隐藏推理过程,也不把自由文本思考当作授权依据。ReAct 可以通过结构化 tool call 实现,不需要在用户界面暴露内部思维链。
固定业务流程优先用工作流或状态机;开放式信息收集采用受预算约束的 ReAct;步骤较多的分析任务采用依赖图计划与执行,局部节点可以再使用 ReAct。高风险操作统一进入执行门禁,与使用哪种推理方式无关。
选择时固定模型、工具和预算,比较任务成功率、无效调用率、恢复能力、p95 延迟和单次成功成本。架构越复杂,不代表智能越强;需要证明额外的规划与交互确实减少了业务失败。
7. 在长上下文对话中,模型过早遗忘用户最初的硬性要求,你会怎样解决?
回答:
将用户硬性要求从聊天文本提升为持久化、版本化的任务状态。上下文窗口是供模型计算的工作区,不适合作为唯一的事实数据库。长窗口能容纳更多 token,但不能保证模型在所有位置都稳定遵守约束。
任务状态至少保存目标、禁止事项、权限范围、输出要求、关键实体和约束版本。金额、时区、资源 ID、禁止写入等字段以结构化值保存,附带来源消息标识;普通历史对话可以摘要,不能让摘要覆盖这些字段。用户后续更改约束时,保存修订并检查冲突,不能永远把旧要求当作不可更新规则。
每次构造上下文,按预算注入任务契约、当前状态、相关证据和近期结果,并预留输出与工具返回空间。历史工具结果可以外置,保留摘要与稳定引用,需要时按权限重新读取。检索出的“记忆”仍是数据,不应伪装成系统指令。
更重要的是在执行器再次检查约束。例如用户要求“只分析,不修改库存”,即使模型错误提出更新库存,工具权限仍只读;要求只编辑指定区域,输出端也要做区域一致性验证。约束在提示词和执行层双重落地,才不会完全依赖模型注意力。
验证时构造多轮长对话、摘要切换、无关干扰和约束修订场景,统计硬约束违例率、关键实体保留率、错误沿用旧约束的比例和任务成功率。长期记忆还应支持过期、用户删除与租户隔离,避免把一次性的敏感要求扩散到其他任务。
8. 如何设计一个能够避免循环调用的 Agent 状态机?
回答:
由运行时控制合法迁移,模型仅提出候选动作。可以设计如下路径,其中每个箭头都带有守卫条件和预算检查:
INIT -> PLAN -> VALIDATE_ACTION -> EXECUTE -> VERIFY_RESULT
| |
v v
WAIT_APPROVAL DONE / REPLAN
| |
+----> VALIDATE_ACTION <--+
任何活动状态 -> FAILED / CANCELLED / DEADLINE_EXCEEDED
执行结果未知 -> RECONCILE -> VERIFY_RESULT
状态中保存任务契约、计划版本、已完成步骤、证据版本、在途动作、幂等键、错误分类与剩余预算。检查点应落在可恢复位置;恢复后先核对在途工具的真实状态,防止进程崩溃把已经完成的外部写入再执行一次。
循环检测至少有三层:相同状态版本下的同参数无效重复;多个动作之间的往返循环;计划不断改写但成功条件距离没有改善。动作指纹用于告警与拦截,不能把所有重复查询视为非法,因为轮询和瞬时故障恢复本来就需要重复。
真正保证运行在资源意义上有界的是步数、时间、调用和费用预算,每个回到计划或执行的循环都必须消耗预算,不能在重规划时补回。每个外部调用有有限超时,等待审批有明确挂起与恢复状态,终止状态不得自动重新进入执行。
这能保证本地编排不无限执行,不能承诺任何任务都成功,也不能保证失联的远端操作已经取消。用故障注入验证重复参数、A/B 往返、工具永不返回、重启恢复和审批后状态变化,才算验证了状态机,而不只是画了一张流程图。
如何证明“不会无限循环”? 可以给所有活动状态定义非负整数预算 $B$。每条可能构成循环的迁移都消耗至少一个预算单位,且恢复和重规划不增加已消耗预算,则最多只会发生 $B_0$ 次这类迁移。时间预算再限制单步等待。这个证明只覆盖本地受控执行:人工审批可以持久化挂起,远端已经启动的作业则需要单独核对取消状态。
并发分支下,预算预留必须是原子的。例如总预算只剩一次工具调用,两个分支不能各自读到“剩余 1”后都发出请求;应由共享配额服务或事务先领取额度。检查点恢复时,也不能把未确认的在途调用简单当成未计费、未执行。
9. 如何判断一个图片理解 Agent 的评测指标是否足够完整?
回答:
先明确任务是图片问答、OCR、定位、审核还是编辑,再建立“感知 → 推理与工具 → 最终任务 → 成本与风险”的分层指标。分类准确率只能描述其中一小部分能力,不能代表整个 Agent 的可用性。
| 层次 | 指标示例 | 容易漏掉的问题 |
|---|---|---|
| 感知识别 | OCR 字符错误率、检测 AP、区域 IoU | 小字、旋转、低清、跨语言与遮挡 |
| 回答与证据 | 任务正确率、事实支持率、对象幻觉率 | 看不清仍肯定回答、证据与结论错配 |
| 工具轨迹 | 工具选择、参数正确率、恢复成功率 | 反复调用、坐标转换错误、执行越界 |
| 编辑约束 | 目标编辑成功、区域外差异、文字识别结果 | 非目标对象变形、文字残留、多轮漂移 |
| 审核决策 | 分类别 precision/recall、误放与误拦率 | 低频高风险类别被总体准确率掩盖 |
| 线上运行 | p95/p99 延迟、失败率、人工接管、成功任务成本 | 队列等待、超大图和并发显存不足 |
例如高风险内容只占 1%,一个全部判正常的系统也能达到 99% 准确率,却没有审核价值。需按类别、尺寸、语言、来源设备和困难样本切片,报告置信区间,并检查高风险类别的漏检。
图片编辑中的像素一致性、感知相似度和语义约束分别回答不同问题:PSNR/SSIM 高不代表没有改错关键文字,语义相似也不保证区域外像素未变。对于严格局部编辑,区域外应做直接差异检查;对于重新编码的输出,还要把编码误差与模型改动分开。
人工或模型评审应有明确 rubric、盲评和冲突复核。模型裁判可以扩展覆盖面,但要用人工标注评估偏差;相同图像或同源近重复不能跨训练集和测试集泄漏。完整性意味着覆盖真实失败模式,并将指标与业务损失关联,而不是把指标列表写得很长。
小样本怎样报告高可靠性? 假设在 $n$ 个独立、同分布且有代表性的测试中观察到零次失败,失败概率的一侧 95% 上界为:
$p_{upper}=1-0.05^{1/n}\approx \frac{3}{n}.$
因此 100 个样本零失败,仍不能证明失败率低于千分之一;要把这个理想化上界压到约千分之一,需要约 3000 个零失败样本。实际样本若来自同一用户或相似图片,相关性会使简单二项模型过于乐观,应采用按用户、来源或时间分组的抽样与区间估计。高风险场景还需单独检查困难切片,不能用总体样本量掩盖某个类别没有被测到。
10. 如果评测集上的准确率很高,但线上效果很差,你会怎样排查?
回答:
先对齐测量对象:离线评测的是最终任务还是单步模型,线上“差”表现为答错、拒答、超时、越权还是成本过高。离线把工具结果直接喂给模型,线上却要求自己检索和调用工具,两者即使都叫准确率,也不具可比性。
第一步做同请求回放,固定模型、Prompt、路由、工具版本、预处理和检索快照,比较离线与线上轨迹。这样可以区分模型本身、部署差异和外部依赖。重点检查图片缩放与 EXIF 方向、token 截断、量化、解码参数、超时预算、缓存键、工具数据时效与后处理。
第二步检查数据分布:线上是否多了低分辨率、长指令、罕见类别、多轮修改、恶意输入或边界请求。评测集是否来自容易样本,是否存在模板重复、答案泄漏、人工纠正后才评测,是否忽略超时和拒绝样本。拒答比例变化时,单看“已回答样本准确率”可能误判。
第三步建立有权重的线上抽样集、固定保留集与困难回归集。按用户、文档或时间分组切分,避免同源样本泄漏。人工标签要保留分歧和标注规范;训练回流集与最终验收集分开,不能不断用测试失败样本训练,再宣称原测试集提升证明泛化。
最后做消融与灰度,分别替换检索、模型、工具和路由,定位收益来源。对于新策略,还要观察被旧策略拒绝的请求,否则选择偏差会掩盖真实质量。报告任务成功率、覆盖率、分组表现、成本和尾延迟,必要时先回滚。上线效果差首先是可观测性和评测契约问题,不能直接归结为“模型不够大”。
11. 如果用户说“只修改图片左上角的文字”,怎样避免模型改动其他区域?
回答:
把“左上角的文字”转成确认后的空间区域,再通过局部生成、显式合成和结果验证实现硬约束。单靠 Prompt 中强调“其他地方不要改”,无法提供严格保障。
先统一 EXIF 方向、像素尺寸与颜色空间,使用 OCR/检测获得文字多边形,映射到原图坐标。如果左上角存在多段文字或用户没有说明替换成什么,要先澄清。批准的编辑范围要覆盖旧文字及必要的边缘,不能因为修补困难就默默扩大到其他内容。
让局部编辑模型生成候选图 $I_{\mathrm{gen}}$,随后将候选结果只合成到批准 mask 内:
$I_{\mathrm{out}}=M\odot I_{\mathrm{gen}}+(1-M)\odot I_{\mathrm{orig}}$
其中 $M\in{0,1}^{H\times W}$,对 RGB 等通道广播。二值 mask 外直接取原图,因此可以保证工作像素空间中未改动;若做羽化,羽化带也必须位于事先批准的可编辑区域内。模型内部即使改动了整图,最终合成仍能丢弃区域外改动。
import numpy as np
def composite_edit(original, generated, allowed_mask):
if original.ndim != 3 or original.shape != generated.shape:
raise ValueError('Image shape mismatch')
if original.dtype != generated.dtype:
raise ValueError('Image dtype mismatch')
if allowed_mask.shape != original.shape[:2]:
raise ValueError('Mask shape mismatch')
if allowed_mask.dtype != np.bool_:
raise ValueError('Mask must be boolean')
return np.where(allowed_mask[..., None], generated, original)
输出后检查目标文字 OCR、拼写与布局,验证非目标文字和对象未变,并计算 mask 外最大绝对像素差。严格要求像素不变时,使用无损输出并在重新解码后复验;JPEG 重新编码可能改变全图像素,不能将内存中的一致性直接推广到最终 JPEG 文件。
若只是替换文字,可以优先考虑 OCR 定位、背景修复和确定性字体排版,通常比整图生成更易控制字形和内容。多轮编辑保存原图与每轮 mask,明确相对于上一版还是原始版本的不变约束,避免逐轮漂移。
配图文字:
局部图像编辑:把“不要改其他区域”落到像素
批准区域、生成区域与最终导出文件必须使用一致的坐标和版本。
定位与确认:EXIF方向统一,OCR映射原图。确认替换内容与允许mask。羽化边界也包含在批准区域内。
生成候选:模型可利用上下文生成。允许内部候选改变整图。候选输出不能直接作为最终交付。
确定性合成:mask内采用候选结果。mask外直接保留原图像素。独立检查目标文字与版式。
导出后复验:无损编码后重新读取文件。mask外最大绝对像素差=0。验证多轮版本与累计编辑范围。
工作像素不变不等于JPEG导出后不变;严格验收应针对最终解码结果。
如果输出保存为 PNG,需要对最终文件解码后再比较,而不是只比较保存前的数组。验收记录应包含原图哈希、批准 mask、坐标变换、输出哈希以及区域外差异;颜色空间转换也应固定在合成之前,避免把全图色彩变化误认为合法局部修改。
三面
1. 请先做一下自我介绍
回答:
三面的自我介绍可以更强调技术判断和完整责任,但仍以真实经历为基础。建议沿着“我擅长什么 → 我独立做成了什么 → 我如何判断方案有效 → 我希望承担什么工作”展开。
可据实改写的模板是:“我的主要方向是[方向],在[项目]里负责过从[输入]到[最终交付]的完整链路。一个比较关键的决策是[具体取舍]:我们面对[约束],我比较了[方案 A/B],最后依据[实验或业务证据]选择[方案]。除了效果,我也负责[可靠性、成本或交付],当前还在解决[真实局限]。
“我希望继续做能把模型能力变成可用系统的工作,尤其关注 Agent 的工具执行可靠性、多模态任务评测和推理服务效率。这些方向都需要同时理解算法边界与工程约束。”
准备一例主动放弃复杂方案的经历会很有价值:例如固定流程用确定性状态机即可,为什么没有引入多个 Agent;或者小模型在本业务已经够用,如何通过误差和成本分析决定不升级。不要虚构“我主导了”或“提升了多少”,团队贡献、个人贡献和设计建议必须分清。
2. 基于 LangChain 开发 Agent 时,如果工具调用连续失败,如何设计一套不会失控的容错机制?
回答:
把容错分为工具适配层、编排层和业务提交层。LangChain 可以组织模型和工具调用,LangGraph 一类状态编排可以提供检查点与恢复能力,但框架本身不自动保证业务幂等、总预算或外部事务一致性。
工具适配层设置连接超时、读取超时和每次调用 deadline,返回结构化错误码。对网络抖动、限流或暂时不可用,按操作是否可重试进行退避;对参数错误,交给模型有限次数修正;对权限拒绝,不让模型通过变换参数绕过策略。404 是否可以重试要结合数据最终一致性等业务契约,不能只看 HTTP 码。
编排层保存任务状态、证据、预算与在途调用。重试由一层统筹,避免 SDK 重试、工具重试和 Agent 重规划相乘。比如三层各最多尝试三次,极端情况下会放大到 27 次下游请求。按依赖设置熔断与并发隔离,预算超限进入明确终止或人工接管状态。LangGraph 的递归/步数限制是兜底,不是主要业务停止条件。
业务提交层处理副作用:同一业务动作在重试和恢复中使用同一个幂等键,参数变化视为新动作并重新授权。请求发送后超时,先查询操作状态;幂等记录与业务更新应在服务端原子完成。仅在本地写入“已执行”后再调用远端,会产生漏执行;远端成功后才写本地状态,又会出现重复窗口。
可以把决策规则明确为:
cancelled or budget exhausted -> terminate
write outcome unknown -> reconcile by idempotency key
permission denied -> fail or await valid authorization
invalid arguments -> bounded repair + validation
safe transient failure -> bounded retry with backoff
repeated invalid trajectory -> bounded replan or escalate
verified postcondition -> finish
检查点记录了图执行进度,并不等于外部系统执行了“恰好一次”。恢复时必须核对副作用。在实现中使用与锁定版本相符的工具包装器、middleware 或图节点重试策略,避免把旧教程里的 API 名称当作跨版本通用接口。验收用故障注入覆盖请求前失败、执行后回包丢失、限流、进程重启与预算耗尽,同时检查请求放大率和重复写入数。
3. 如何设计 Qwen、视觉语言模型和轻量模型之间的动态路由策略?
回答:
先澄清分类维度:Qwen 是模型家族,家族内可以包含视觉语言模型和轻量模型,所以三者不是互斥类别。路由系统应维护具体模型与部署版本的能力注册表,描述模态、上下文长度、工具调用与结构化输出支持、测得质量、延迟、成本和可用状态。
第一步做硬约束过滤。输入包含图片且任务必须依赖视觉信息,就不能降级到纯文本模型后假装已经看图;数据驻留、最大上下文、工具协议和租户权限也必须满足。OCR 转文本可以是显式降级方案,但只能覆盖文字理解,不能等价替代空间关系或图像内容理解。
第二步在可行模型集合中进行成本与质量选择。一个简化目标是:
$m^{*}(x)=\arg\min_{m\in\mathcal{F}(x)} \left[\alpha\widehat{E}_m(x)+\beta\widehat{L}_m(x)+\gamma\widehat{C}_m(x)\right]$
其中 $\mathcal{F}(x)$ 是满足硬约束的模型集合,$\widehat{E}$ 表示估计错误损失,$\widehat{L}$ 表示含排队在内的时延,$\widehat{C}$ 表示成本。权重需按量纲和业务代价校准,高风险限制最好作为硬约束,而不是允许被成本权重抵消。
简单抽取、标准问答等经验证的小模型强项可以优先走轻量模型;视觉任务进入具有相应能力的 VLM;长链路推理按实测难度和质量需求升级。高风险任务即使交给更强模型,依旧需要规则校验、权限门禁或人工审批,不能把“大模型”当作授权机制。
采用级联时,小模型结果要经过可用的验证器或校准分数判断是否接受;不能用意图分类置信度替代答案正确率。考虑“先小后大”的总成本、最长时延和不必要升级率;严格时延场景可能适合直接路由。模型过载时仅在质量下限允许的候选中切换,必要时排队、拒绝或人工处理。
离线比较同一任务在各候选模型下的表现,线上先影子评估、再小流量灰度,持续监控选择偏差和分布漂移。模型名称、API 别名、上下文上限与视觉 token 策略随版本变化,应固化部署版本和实际压测结果,不把一个静态模型名列表写成永久路由策略。
级联什么时候真的省钱? 设轻量模型执行成本为 $C_s$,首轮验证成本为 $C_v$,升级比例为 $q$,升级分支包含重做与最终验证的平均成本为 $C_b$。简化的期望成本是:
$\mathbb E[C_{cascade}]=C_s+C_v+qC_b.$
它必须低于直接使用候选强模型的完整交付成本 $C_d$,并同时满足错误放行率与时延约束。比如仅为说明取 $C_s=0.08$、$C_v=0.12$、$C_b=C_d=1$,当 $q=0.3$ 时成本为 0.50;当 $q=0.9$ 时变成 1.10。这些是统一货币单位下的算例,不是具体模型实测。降低升级率只有在验收质量不退化时才有意义。
配图文字:
先小后大的前提:错误结果能够被识别
路由分类准确率、模型答案正确率与验证器可靠性需要分别衡量。
能力过滤:模态/上下文/工具协议。数据区域/租户权限/可用性。不满足硬条件的模型直接排除。
低成本执行与验证:轻量模型完成任务。验证器检查证据与后置条件。通过才交付,不能只看自报置信度。
升级路径:拒绝或不确定→强模型/人工。成本包含首次尝试、验证与重做。升级结果仍需满足相同验收标准。
共同优化目标:任务成功率/错误放行率。风险—覆盖率/人工接管率。成功任务总成本/P95交付时间。
说明性设计;在验证困难或严格时延场景,直接使用强模型可能更合适。
4. 置信度阈值应该如何定义?为什么不能直接使用模型输出的概率?
回答:
先定义事件:我们希望估计的是“整个任务满足验收条件”的概率,还是“这张图片属于高风险类别”的概率。大模型下一 token 概率描述的是给定上下文后的文本预测,不等于事实正确率;模型自己输出的 confidence: 0.95 也只是一个未经校准的声明。多个 token 概率相乘还会受答案长度、表达方式等因素影响。
若是固定类别分类器,可以在独立校准集上用温度缩放校准 logits:
$p_k(x;T)=\frac{\exp(z_k(x)/T)}{\sum_j\exp(z_j(x)/T)},\qquad T>0$
温度 $T$ 应通过校准数据上的负对数似然等目标拟合,而不是随意设置。温度缩放通常不改变类别排序,也不能补回模型缺失的判别能力。生成式任务没有天然的固定类别 logits 时,可以基于验证器、检索支持、工具状态等特征训练任务级正确性评分,再做 Platt scaling、isotonic regression 等校准。
阈值由决策成本确定。以二分类为例,令 $p=P(y=1\mid x)$,假定正确决策代价为零,误报成本为 $C_{\mathrm{FP}}$,漏报成本为 $C_{\mathrm{FN}}$。判正与判负的期望损失分别为:
$R(1\mid x)=C_{\mathrm{FP}}(1-p),\qquad R(0\mid x)=C_{\mathrm{FN}}p$
因此在这些假设下,判正阈值为:
$p>\frac{C_{\mathrm{FP}}}{C_{\mathrm{FP}}+C_{\mathrm{FN}}}$
对于“自动接受还是人工复核”,若错误接受代价是 $C_e$、理想化的复核代价是 $C_h$,且 $0<C_h<C_e$,则接受的简化条件为 $p_{\mathrm{correct}}\ge 1-C_h/C_e$。实际复核也会出错并有容量限制,需要纳入完整损失模型。
验证阈值时一起报告 reliability diagram、NLL/Brier、分组校准误差,以及风险—覆盖率、precision/recall 和人工接管率。ECE 会受分箱方式影响,不能单独充当质量指标。校准集、阈值选择集与最终测试集应合理隔离;分布漂移后历史阈值需要重估,高风险决策还可使用风险置信上界进行保守控制。
还要区分“高置信度”与“高覆盖率”:将阈值设得很高,可以通过只接受最简单的少量请求得到较低错误率,但大量请求被拒绝或交给人工。应在同一张风险—覆盖率曲线上比较策略,并分别看常见任务、困难任务和高损失类别。对拒绝样本也进行抽样标注,才能估计被路由策略隐藏的错误,避免只在已接受样本上证明自己正确。
5. 如果 Prompt 中给了明确的输出格式,但模型仍然经常生成非法 JSON,如何处理?
回答:
将问题分成语法、Schema 和业务三层。合法 JSON 不代表符合字段契约,字段契约正确也不代表内容可信。优先使用部署模型与服务端确实支持的结构化输出或约束解码,再做应用端严格验证,不能只在 Prompt 里反复强调“必须是 JSON”。
对于支持原生结构化输出的模型,可使用 provider 原生策略;否则使用 tool calling 或工具策略,并处理解析与 Schema 校验错误。LangChain 当前的结构化输出路径区分 ProviderStrategy 与 ToolStrategy,但是否支持完整 Schema、能否和其他工具同时使用,仍取决于具体模型、提供方和锁定版本。JSON mode、tool calling 与严格 Schema 约束不是等价承诺。
约束解码也可能遇到输出截断、拒绝或流式未完成,所以应在完整结果到达且结束状态合法后再解析。不要用正则截取首尾大括号,不要用 eval,也不要在部分 JSON 到达时就执行有副作用的操作。
下面用标准库演示一个限定为 intent 和 confidence 两个字段的严格应用契约,特意拒绝重复键、非有限数和把布尔值当数字的情况:
import json
import math
def parse_result(text):
if not isinstance(text, str) or len(text.encode('utf-8')) > 16384:
raise ValueError('Invalid input or payload too large')
def reject_constant(value):
raise ValueError('Non-finite JSON number')
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError('Duplicate key')
result[key] = value
return result
try:
data = json.loads(text, parse_constant=reject_constant,
object_pairs_hook=unique_object)
except (json.JSONDecodeError, RecursionError) as exc:
raise ValueError('Invalid JSON') from exc
if type(data) is not dict or set(data) != {'intent', 'confidence'}:
raise ValueError('Unexpected fields')
if type(data['intent']) is not str:
raise ValueError('Intent must be a string')
if data['intent'] not in {'QUERY', 'EDIT', 'CLARIFY'}:
raise ValueError('Unknown intent')
score = data['confidence']
if type(score) not in (int, float) or not 0 <= score <= 1:
raise ValueError('Confidence out of range')
if not math.isfinite(score):
raise ValueError('Confidence must be finite')
return data
该代码只验证数据契约,不会让模型自报的 confidence 自动变得可信;应由经过验证的评分或校准系统支撑。金额、租户、审批等另有服务端语义校验。生产端还应在网络接入层限制消息体,并限制嵌套深度和解析资源。
若失败,最多进行预算内的定向格式修复,修复后重新校验,并检查业务字段是否改变。缺少关键字段时不能编造默认金额、订单号或审批结果;应澄清或失败返回。高风险操作始终要求通过独立的授权与业务验证。
6. 图片审核系统在面对超大图片、切片图片和多模态并发请求时,主要会出现哪些问题?
回答:
它同时考验视觉信息保留、空间坐标一致性与资源调度。压缩文件很小不代表解码后很小:需要在接入阶段限制文件字节数、像素总量、长宽比、帧数和解码资源,校验实际格式,处理 EXIF 方向,并防止超大解压尺寸耗尽内存。
直接把大图缩小会丢失小字与小目标;全量高分辨率切片会增加视觉 token、预处理和模型推理成本。常用做法是低分辨率全局视图加高分辨率局部切片。但如果高风险小目标在低分辨率中完全消失,仅按全局模型的疑似区域裁剪仍会漏检,必须结合规则网格、覆盖保证或针对小目标的检测策略。
对于宽高 $W,H$、切片边长 $s$、相邻重叠宽度 $o$ 的规则切片,设 $0\le o<s$,可估计:
$N_x=\max!\left(1,1+\left\lceil\frac{W-s}{s-o}\right\rceil\right),\quad N_y=\max!\left(1,1+\left\lceil\frac{H-s}{s-o}\right\rceil\right),\quad N=N_xN_y$
切片数量还不等于视觉 token 数,不同模型的 patch、动态分辨率、压缩与处理器策略不同,需要按实际模型计算预算。过多重叠会提高成本,并放大重复检测与局部误报问题。
每个切片应携带图像 ID、裁剪原点、缩放比例和版本;输出框先映射回原图,再做适合任务的去重或合并。检测框可用 NMS 等方案,OCR 要考虑阅读顺序,审核标签则需要全局和局部证据联合判定。不能把所有切片分数直接平均:局部高风险内容可能被大量正常背景稀释;全部取最大值也可能随切片数增加而放大误报,需要按整图重新校准。
多模态并发下,按请求数限流通常不够。一张巨图或几十个切片可能比多条文本请求昂贵得多。应按像素、切片数、视觉 token、输出预算和显存估计做准入控制,把解码、预处理、模型 prefill 和 decode 的资源分别观测,并对大图设置独立队列或公平调度,避免队头阻塞。
显存占用不只有模型权重,还包括视觉编码器中间结果、批次激活、KV cache 和运行时工作区。采用兼容的动态批处理、分辨率预算、背压、超时取消与 OOM 降级;降级不能静默丢弃切片后仍输出“审核通过”,应标记处理不完整并进入补充审核。
验收应报告按图像尺寸、切片数和并发度分层的漏检、误报、完整处理率、p95/p99 延迟、峰值显存及单张成本。审核系统的质量下限,取决于高风险内容是否得到完整检查,以及资源不足时能否明确失败。
切片数量增加为什么容易放大误报? 仅在各切片误报相互独立、单片误报率固定为 $p$,且整图采用“任一切片报警即报警”的简化假设下,整图误报率为:
$P_{image}=1-(1-p)^N.$
例如 $p=0.01$、$N=100$ 时,整图误报率约为 63.4%。现实中相邻切片高度相关,这个数值不能直接当作真实预测,但它说明为什么必须按整图与切片数量重新评估聚合策略。对于切片未处理完的请求,应统计在完整处理率与服务失败中,不能从质量统计分母中静默删除。
本篇目录
当前问题0132
- 01
先做一下自我介绍,你最近主要关注哪类技术问题
- 02
先做一下自我介绍,你最近主要关注哪类技术问题
- 03
JVM 中对象从创建到回收大致经历了哪些阶段?
- 04
线上出现频繁 Full GC 时,你会怎样定位?
- 05
Java 中如何设计一个不会产生大量锁竞争的任务调度器?
- 06
Java 内存模型中的 happens-before 规则解决了什么问题?
- 07
如果一个接口同时受到 CPU 和数据库 IO 的限制,你会怎样拆解瓶颈?
- 08
MySQL 的 B+Tree 索引为什么适合范围查询?
- 09
联合索引 (tenant_id, status, created_at) 在哪些情况下无法充分利用?
- 10
在高并发写入场景下,MySQL 的间隙锁可能造成什么问题?
- 11
Redis 的持久化机制应该如何选择?
- 12
Redis 分布式锁怎样避免锁被误删?
- 13
请先做一下自我介绍,聊聊你实习期承担过的工作。
- 14
请先做一下自我介绍,聊聊你实习期承担过的工作。
- 15
懂不懂Go,如果让你转Go能接受吗
- 16
介绍下项目,如何设计从数据接入到结论生成的整体架构?
- 17
介绍下项目,如何设计从数据接入到结论生成的整体架构?
- 18
如果 Agent 在调用工具时出现参数类型正确但业务含义错误,如何发现并阻止?
- 19
如果工具调用一直没有达到成功条件,任务不断重试并最终卡死,你会如何处理?
- 20
CoT、ReAct、Plan-and-Execute 在工程实现上有什么区别?如何选择?
- 21
CoT、ReAct、Plan-and-Execute 在工程实现上有什么区别?如何选择?
- 22
在长上下文对话中,模型过早遗忘用户最初的硬性要求,你会怎样解决?
- 23
如何设计一个能够避免循环调用的 Agent 状态机?
- 24
如何判断一个图片理解 Agent 的评测指标是否足够完整?
- 25
如果评测集上的准确率很高,但线上效果很差,你会怎样排查?
- 26
如果用户说“只修改图片左上角的文字”,怎样避免模型改动其他区域?
- 27
请先做一下自我介绍
- 28
基于 LangChain 开发 Agent 时,如果工具调用连续失败,如何设计一套不会失控的容错机制?
- 29
如何设计 Qwen、视觉语言模型和轻量模型之间的动态路由策略?
- 30
置信度阈值应该如何定义?为什么不能直接使用模型输出的概率?
- 31
如果 Prompt 中给了明确的输出格式,但模型仍然经常生成非法 JSON,如何处理?
- 32
图片审核系统在面对超大图片、切片图片和多模态并发请求时,主要会出现哪些问题?