百度

AI智能体算法一面、二面、三面面经

完整信息

32 条问法 · 29 道题

本篇目录

写在前面

【WeThinkIn出品】栏目专注于分享Rocky的认知思考与经验感悟,范围涵盖但不限于AI行业。

干货AIGC、AI Agent、LLM算法工程师/开发工程师面试面经秘籍分享(持续更新):WeThinkIn/AIGC-Interview-Book欢迎大家Star~

大家好,我是Rocky Ding。

智能体算法岗位的能力边界,已经延伸到模型、业务规则和执行系统的连接处。模型决定下一步值得尝试什么,运行时决定这个动作能否执行,评测体系判断任务是否真正完成。一个可上线的 Agent,需要把概率性的模型输出,接入有权限、有预算、有证据、可恢复的执行闭环。

这组三轮问答可以沿着一条主线理解:一面考察承载 Agent 的后端基础,二面关注任务闭环与多模态评测,三面进一步讨论失败恢复、模型路由与资源治理。Rocky 更看重的能力,是能否解释每项设计解决了什么问题、增加了什么代价,以及如何证明它在真实流量下有效。

目录

一面

二面

三面

一面

1. 先做一下自我介绍,你最近主要关注哪类技术问题

回答:

自我介绍应建立“个人能力与岗位需求”的对应关系。可以按照背景、实际负责的项目、解决过的问题、最近关注的方向来组织,用一到两分钟讲清楚。以下是需要替换成真实经历的模板,不能把示例当作自己的业绩:

“我目前是[学校/专业/工作背景],主要做[算法或开发方向]。最近参与的[项目名称]面向[用户与业务场景],我负责[明确模块],其中最有代表性的问题是[具体瓶颈]。我通过[方案],在[测试集/流量与资源条件]下,把[指标]从[基线]改善到[结果],同时控制了[成本或风险]。

“近期我关注 Agent 的可靠执行:模型输出如何转为可校验的工具参数,工具超时后如何判断是否已经执行,以及如何通过任务级评测定位失败。我也在补齐并发、数据库和可观测性,因为这些能力直接影响智能体的线上稳定性。”

值得展开的技术问题应当有证据。 例如,能展示一条失败轨迹,解释问题发生在检索、规划、参数、权限还是外部服务,再说明修复前后的对照结果。没有线上数据,就说明使用离线回放或模拟压测,不编造 QPS、成功率和业务收益。

2. JVM 中对象从创建到回收大致经历了哪些阶段?

回答:

以 HotSpot 中普通 new 对象为例,可以沿着“类的准备、分配、初始化、使用、可达性变化、回收”理解。类加载与解析并非每创建一个对象都重新执行;new 涉及的类在需要时完成加载、链接,并在首次主动使用前完成类初始化。类初始化执行静态初始化逻辑,与每个对象的构造方法不同。

  1. 分配内存。 常见路径先尝试线程本地分配缓冲区 TLAB,减少共享堆分配竞争;大对象、TLAB 不足或不同收集器会进入其他分配路径。具体使用指针碰撞还是空闲空间管理,取决于堆组织方式。
  2. 建立初始状态。 实例字段先获得零值,运行时设置对象头等信息;随后执行实例初始化和构造逻辑。零值初始化不等于执行了业务构造方法。
  3. 使用与发布。 对象被线程、集合或其他对象引用。跨线程传递仍需安全发布,不能认为“构造完了”就天然对另一线程可见。避免在构造函数中让 this 提前逸出。
  4. 判断存活。 GC 从线程栈、静态字段等根集合出发分析可达性,并处理强、软、弱、虚引用等不同语义。循环引用本身不会阻止可达性分析回收。
  5. 回收或移动。 收集器释放不可达对象占据的空间,可能伴随复制、整理和引用更新。对象何时回收没有应用层的及时性保证。

并非所有对象都实际完成一次堆分配: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%。现实中相邻切片高度相关,这个数值不能直接当作真实预测,但它说明为什么必须按整图与切片数量重新评估聚合策略。对于切片未处理完的请求,应统计在完整处理率与服务失败中,不能从质量统计分母中静默删除。

本篇目录
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
    基于 LangChain 开发 Agent 时,如果工具调用连续失败,如何设计一套不会失控的容错机制?
  29. 29
  30. 30
  31. 31
  32. 32

同作者面经

展开其余 6 篇收起其余 6 篇

相关公司