Skip to content

二面深度:架构与实战(20题) ​

考察重点:实战深度、架构权衡、坑位经验 答题要求:四段式 "问题 → 方案 → 取舍 → 数据佐证",必须展示真实踩坑经验 建议每题答题时间:3-5分钟


一、Agent 架构深度 ​

1. Master-SubAgent 模式的通信方案:共享上下文池/消息队列/函数调用传参,各自取舍? ​

分档:二面深度

高分答题思路:

三种方案对比:

方案实现优点缺点适用场景
共享上下文池Redis/内存存储共享 Context 对象信息完整、SubAgent 可获取全量上下文上下文膨胀、并发写冲突、信息过载上下文较小的强关联任务
消息队列SubAgent 通过 MQ 发送/接收结构化消息解耦、异步、可扩展序列化开销、消息延迟、调试链路长跨进程/跨服务 Agent 协作
函数调用传参Master 直接在调用 SubAgent 时传入结构化参数简单直接、类型安全、易调试参数需预先定义、扩展性差同进程内简单主从模式

生产实践推荐:混合方案

  • Master → SubAgent:函数调用传参(精确控制传什么)
  • SubAgent → Master:回调函数返回结构化结果(不反向污染 Master 上下文)
  • 共享状态:Redis 存只读的会话级上下文(如用户画像、历史决策),SubAgent 按需读取
  • 异步通知:消息队列处理跨 Agent 的异步事件(如 SubAgent 完成后通知 Master)

关键取舍维度:

  1. 上下文隔离度:传参隔离最好,共享池最差
  2. 开发复杂度:传参最简单,MQ 最复杂
  3. 信息完整性:共享池最完整,传参需要预先判断需要什么
  4. 性能:内存传参最快,MQ 有网络开销

数据佐证模板: "我们最初用共享上下文池,发现 SubAgent 上下文经常膨胀到 8K+ token,导致 LLM 推理质量下降。改为函数传参后,平均上下文降到 2K token,推理准确率提升 15%。"


2. 单 Agent 承载复杂业务会出现上下文膨胀,你有哪些拆分 Agent 的划分标准? ​

分档:二面深度

高分答题思路:

拆分标准(5 个维度):

  1. 工具集正交性:如果两组工具完全无交集(如搜索 vs 代码执行),应拆分为不同 Agent
  2. 上下文隔离需求:不同子任务需要不同领域的背景知识,混在一起会稀释有效信息
  3. Prompt 冲突:一个 Agent 的 System Prompt 如果需要同时扮演"搜索专家"和"代码专家",角色冲突导致效果下降
  4. 延迟容忍度:实时交互部分 vs 可异步处理部分应拆分,避免长任务阻塞交互
  5. 可独立评测:拆分后的每个 Agent 应该能独立定义评测指标和 Badcase 归因

拆分模式:

  • 垂直拆分(按领域):搜索 Agent、代码 Agent、数据分析 Agent
  • 水平拆分(按流程阶段):理解 Agent → 规划 Agent → 执行 Agent → 验证 Agent
  • 主从拆分:Master Agent 做路由和编排,SubAgent 做专业执行

上下文膨胀的量化信号:

  • 单次 LLM 调用 token > 4K 且有效信息占比 < 30%
  • 工具数量 > 10 个,LLM 工具选择准确率下降
  • 多轮对话超过 5 轮后,模型开始忽略早期关键信息

数据佐证模板: "我们把一个 15 工具的全能 Agent 拆成 3 个垂直 Agent 后,工具调用准确率从 72% 提升到 89%,平均上下文从 6K 降到 2K token,单次推理成本下降 60%。"


3. 多 Agent 之间存在任务依赖时,怎么实现异步等待、任务回调? ​

分档:二面深度

高分答题思路:

方案一:Future/Promise 模式(同进程)

python
async def master_agent():
    # 并行启动两个子任务
    search_future = asyncio.create_task(search_agent(query))
    analysis_future = asyncio.create_task(analysis_agent(data))
    
    # 等待两个任务都完成
    search_result, analysis_result = await asyncio.gather(
        search_future, analysis_future
    )
    # 合并结果继续处理

方案二:事件驱动回调(跨进程)

  • SubAgent 完成后向消息队列发送结果事件
  • Master 订阅对应 topic,收到事件后继续处理
  • 优势:Master 不阻塞等待,可同时处理其他请求

方案三:状态机 + 轮询(跨服务)

  • SubAgent 启动时返回 task_id,Master 将状态机切换到 WAITING
  • Master 定期查询 task 状态(或通过 webhook 回调)
  • task 完成 → Master 状态机推进到下一状态

关键设计考量:

  • 超时处理:每个异步等待必须设 timeout,超时走降级路径
  • 依赖编排:DAG 描述任务依赖关系,拓扑排序确定执行顺序
  • 失败传播:上游任务失败时,下游依赖任务如何处理(跳过/重试/降级)
  • 幂等性:回调可能被重复触发,下游处理必须幂等

加分点: "我们用 Temporal 做跨服务的 Agent 编排——它的优势是内置了重试、超时、补偿机制,Agent 间依赖用 DAG 描述,不用自己造轮子。自研方案在依赖管理上很容易出 bug。"


4. 如何设计 Agent 路由器:根据用户 query 自动分发到对应子 Agent,路由准确率怎么提升? ​

分档:二面深度

高分答题思路:

路由器架构设计:

用户 Query
    │
    ▼
┌─────────────┐
│  路由器 LLM  │ ← 粗粒度:领域分类(Intent Classification)
└──────┬──────┘
       │
    ┌──┴──┬───────┬───────────┐
    ▼     ▼       ▼           ▼
搜索Agent 代码Agent 数据Agent 闲聊Agent

两级路由设计:

  • L1 粗路由:轻量分类模型/BERT 分类器,按领域分发(准确率高、延迟低)
  • L2 细路由:Agent 内部根据子意图进一步分发到具体工具

路由准确率提升方案:

手段效果成本
Few-shot 示例+5-10%低(仅需标注示例)
路由意图训练数据+10-15%中(需标注数据 + 训练分类模型)
路由置信度阈值避免低置信度误路由低(设阈值,低于阈值走兜底 Agent)
路由反馈回路持续优化中(收集用户反馈,定期更新路由模型)
多路由投票+3-5%高(多次推理取多数,延迟翻倍)
路由前 Query 改写+5-8%中(先标准化 Query 再路由)

兜底机制:

  • 置信度 < 0.6 → 走通用 Agent
  • 路由到错误 Agent → Agent 内部检测到不匹配 → 回退到路由器重新路由
  • 连续路由失败 → 直接走人工/规则兜底

数据佐证模板: "我们的路由器从纯 Prompt 路由升级到 BERT 分类器 + 置信度阈值后,路由准确率从 81% 提升到 94%,误路由导致的用户投诉下降 70%。"


5. 工具调用的容错设计:函数参数缺失、参数类型错误、接口超时怎么重试/反问用户? ​

分档:二面深度

高分答题思路:

三层容错体系:

层次问题方案
前置校验参数缺失/类型错误Pydantic Schema 校验 → 缺必填参数时让 LLM 反问用户或从上下文补充
执行容错接口超时/错误指数退避重试(3次)→ 熔断 → 降级返回缓存/默认值
后置处理结果异常结果 Schema 校验 → 空结果/异常结果 → LLM 判断是否换工具重试

参数缺失处理流程:

LLM 生成工具调用 → Pydantic 校验参数
  ├─ 参数完整 → 执行工具
  ├─ 必填参数缺失 → LLM 判断:能否从上下文补充?
  │   ├─ 能 → 补充后执行
  │   └─ 不能 → 反问用户:"请提供 XXX 参数"
  └─ 参数类型错误 → 尝试类型转换 → 转换失败走缺失流程

反问用户的 Prompt 设计原则:

  • 一次只问一个问题(避免反复追问)
  • 给出选项而非开放问题("您想搜索的是 A 还是 B?")
  • 说明为什么需要这个信息("为了精确搜索,需要您提供时间范围")

超时重试策略:

  • 超时 3s → 重试 1 次
  • 超时 10s → 熔断 30s,期间走降级
  • 连续 3 次熔断 → 告警 + 降级到备选工具

加分点: "我们设计了一个'工具调用自愈'机制:LLM 调用工具失败后,把错误信息喂回给 LLM,让它自己决定是修改参数重试、换工具、还是反问用户。这比硬编码重试逻辑灵活得多。"


6. 工具自动注册、工具权限隔离怎么实现?多租户场景如何管控 Agent 可用工具? ​

分档:二面深度

高分答题思路:

工具自动注册:

python
# 装饰器注册模式
tool_registry = {}

def register_tool(name, description, schema):
    def decorator(func):
        tool_registry[name] = {
            "func": func,
            "description": description,
            "schema": schema,
            "permissions": getattr(func, "_permissions", [])
        }
        return func
    return decorator

@register_tool("search", "搜索知识库", {"query": "str", "top_k": "int"})
async def search(query: str, top_k: int = 5):
    ...
  • 启动时扫描 tools/ 目录,自动加载所有 @register_tool 标注的函数
  • 生成 OpenAI Function Calling 格式的 tool 定义,自动注入 LLM Prompt

权限隔离方案:

隔离层次方案
租户级每个租户有独立的工具配置表,Agent 初始化时按租户加载可用工具列表
Agent 级每个 Agent 类型绑定允许使用的工具白名单
用户级基于角色的工具权限(RBAC),普通用户 vs 管理员可用工具不同
运行时工具执行前校验调用者权限,敏感操作需二次确认

多租户工具管控架构:

租户配置中心 → 工具权限矩阵
  租户A: [search, summary, translate]
  租户B: [search, code_exec, data_analysis]
         │
    Agent 初始化时按租户过滤工具列表
         │
    LLM 只看到被授权的工具定义
         │
    工具执行时二次校验权限

加分点: "工具权限不只是'能不能用',还包括'参数限制'——比如搜索工具,普通租户最多返回 10 条,企业版租户可以返回 100 条。权限模型要支持参数级别的管控。"


7. 复杂链式工具调用(工具A结果作为工具B入参)链路怎么管理? ​

分档:二面深度

高分答题思路:

核心挑战:

  • 中间结果可能过大(上下文溢出)
  • 链路某一步失败需要决定回退还是终止
  • LLM 可能在不该链式调用时强行链式

管理方案:

  1. DAG 编排 + 数据流管道
search(query) → results[20条]
    │
    ▼ (摘要截断:top 5 + summary)
filter(results) → filtered[5条]
    │
    ▼ (结构化提取)
analyze(filtered) → report
  1. 中间结果管理
  • 每步结果存储在 context 中,带 step_id
  • 大结果做摘要/截断后再传给下一步
  • LLM 只看到当前步骤需要的输入,不看全部中间结果
  1. 失败处理
  • 每一步定义 fallback 值或 fallback 工具
  • 链式失败时记录已完成的步骤,支持断点续跑
  • 超过最大链路深度(如 5 步)强制终止
  1. 链路监控
  • 每一步记录:工具名、入参摘要、出参摘要、耗时、状态
  • 链路完成后输出完整 trace,方便 Badcase 定位

数据佐证模板: "我们做了一个'工具链路编排器',用 DAG 定义工具依赖关系。LLM 只负责决策'要不要调工具',调用的顺序和数据传递由编排器控制。这样避免了 LLM 自行链式调用时的上下文膨胀和不可控问题。"


8. 工具返回超长文本,如何做摘要截断再喂给 LLM,避免上下文溢出? ​

分档:二面深度

高分答题思路:

多级截断策略:

级别策略触发条件
L1结构化提取关键字段返回 > 2K token
L2滑动窗口摘要返回 > 4K token
L3丢弃低权重内容返回 > 8K token
L4拒绝并提示 LLM 换工具返回 > 16K token

具体实现:

python
async def handle_tool_result(result: str, max_tokens: int = 2000):
    result_tokens = count_tokens(result)
    
    if result_tokens <= max_tokens:
        return result  # 无需处理
    
    # L1: 结构化提取
    if is_structured(result):
        extracted = extract_key_fields(result)  # 提取 title, summary, key_points
        if count_tokens(extracted) <= max_tokens:
            return extracted
    
    # L2: 分段摘要
    chunks = split_into_chunks(result, chunk_size=1000)
    summaries = await asyncio.gather(*[
        llm_summarize(chunk) for chunk in chunks[:5]  # 最多摘要前5段
    ])
    return "\n".join(summaries)
    
    # L3: 截断 + 末尾提示
    # return result[:max_tokens] + "\n[...内容已截断...]"

关键设计原则:

  • 截断不是删除:原始结果存入 context store,LLM 如果需要可以调 get_full_result(step_id) 获取
  • 摘要保信息:摘要时保留结构化信息(数字、名称、时间),压缩描述性文字
  • 提示 LLM:截断后在末尾追加 "[结果已截断,如需完整内容请调用 get_detail 工具]"

加分点: "我们做了'懒加载'机制:工具结果先存 Redis,只给 LLM 返回摘要 + 一个 reference_id。LLM 如果判断需要更多细节,通过 reference_id 调 get_detail 获取完整内容。这样大多数场景下上下文都很精简。"


9. 上下文窗口优化:滑动窗口、关键信息留存、无用历史过滤实现方案? ​

分档:二面深度

高分答题思路:

三层优化方案:

  1. 滑动窗口(最外层)

    • 保留最近 N 轮对话(如 5 轮),更早的丢弃或摘要
    • 每轮包含:user message + assistant response + tool calls
  2. 关键信息留存(中间层)

    • 在滑动窗口淘汰前,提取关键信息存入"关键信息池"
    • 关键信息定义:用户明确陈述的偏好、已确认的决策、工具返回的核心结论
    • 实现:每轮结束时 LLM 做一次信息提取:"这轮对话中有什么信息需要长期记住?"
  3. 无用历史过滤(最内层)

    • 识别并移除:寒暄语、重复确认、工具调用的中间日志
    • 工具调用结果在已被后续推理使用后,替换为简短摘要
    • 保留:用户的核心诉求、Agent 的关键决策、未解决的疑问

实现架构:

完整对话历史
    │
    ▼
[滑动窗口] 保留最近 5 轮
    │
    ▼
[关键信息池] 从被淘汰的轮次中提取
    │
    ▼
[当前上下文] = System Prompt + 关键信息池 + 最近 5 轮 + 当前 User Query

量化效果:

  • 原始 10 轮对话 ~8K token → 优化后 ~3K token
  • 关键信息留存保留了 90%+ 的有效信息
  • 推理质量无显著下降(因为过滤掉的是低信息量内容)

加分点: "我们做了 A/B 测试:优化前后 Agent 任务完成率从 87% → 85%(-2%),但推理成本下降 55%。在成本敏感场景下这个 trade-off 是值得的。关键是找到信息保留率和 token 压缩率的平衡点。"


10. 多轮对话中用户需求漂移、需求冲突,怎么检测并主动澄清? ​

分档:二面深度

高分答题思路:

需求漂移检测:

  • 意图变化检测:每轮记录当前意图标签,如果连续两轮意图标签变化 > 阈值,触发漂移检测
  • 实体冲突检测:用户第 3 轮说"搜索北京的餐厅",第 5 轮说"我要上海的",实体变化需确认
  • 约束变化检测:用户之前说"预算 100 以内",后来说"便宜的就行",需要确认预算是否变化

需求冲突处理:

检测到冲突
    │
    ▼
LLM 分析冲突类型
    ├─ 约束冲突(预算/时间/地点矛盾)→ 列出冲突项让用户选择
    ├─ 目标冲突(先说要A后说要B,A和B互斥)→ 确认最新意图
    └─ 隐式冲突(新需求与之前决策矛盾)→ 提示矛盾点 + 询问是否推翻之前决策

主动澄清的 Prompt 设计:

你是一个需求澄清助手。检测到以下对话中存在需求冲突:

[对话历史摘要]
[冲突点描述]

请用以下格式输出澄清话术:
1. 简要说明检测到的冲突(一句话)
2. 给出 2-3 个选项让用户快速选择
3. 不要反复追问,一次澄清解决一个核心冲突

避免反复追问的原则:

  • 一次只澄清一个核心冲突
  • 给选项而非开放问题
  • 澄清后立即推进任务,不等用户补充其他信息
  • 已澄清的信息立即写入上下文,避免重复冲突

加分点: "我们用了一个'需求稳定性评分':每轮计算当前需求与历史需求的一致性分数,低于 0.6 时触发澄清。这样避免过早澄清(用户只是在补充细节)和过晚澄清(已经走了三步弯路)。"


11. RAG 检索前 Query 改写怎么做?如何避免改写偏离用户原始诉求? ​

分档:二面深度

高分答题思路:

Query 改写的 5 个维度:

维度示例
实体补充"苹果发布会" → "苹果 2024 秋季发布会 iPhone"
同义扩展"怎么提高销量" → "如何提升销售额 营收增长策略"
拆分子查询"比较 A 和 B 的优缺点" → ["A 的优缺点", "B 的优缺点", "A B 对比"]
时间约束"最新的研究" → "2024 年发表的 [主题] 研究论文"
条件补充"便宜的餐厅" → "人均 50 元以下 评分 4.0+ 餐厅"

改写流程:

原始 Query
    │
    ▼
LLM 改写(带改写规则 Prompt)
    │
    ├─ 生成改写 Query(1-3 个变体)
    ├─ 保留原始 Query
    │
    ▼
多路检索(原始 + 改写并行检索)
    │
    ▼
结果融合去重(RRF 算法)
    │
    ▼
相关性过滤 → 最终送入 LLM 的 Context

避免改写偏离的保障措施:

  1. 始终保留原始 Query 参与检索:改写是补充不是替代
  2. 改写约束 Prompt:明确要求"改写后的 Query 必须与原始 Query 语义一致,只是补充或规范化表达"
  3. 改写置信度:LLM 输出改写置信度,低于 0.7 的改写不使用
  4. 改写回退:如果改写后的检索结果比原始 Query 检索结果更差(相关性评分更低),自动回退到原始 Query

数据佐证模板: "Query 改写让我们的检索召回率从 68% 提升到 82%,但早期改写偏离问题导致 5% 的 case 检索结果完全错误。加入'原始 Query 兜底 + 置信度过滤'后,偏离率降到 1% 以下。"


12. 意图分层识别:粗粒度领域分类 + 细粒度子意图,串行/并行实现优劣? ​

分档:二面深度

高分答题思路:

串行方案(粗 → 细):

Query → 粗分类器(领域)→ 细分类器(子意图,条件:已知领域)
  • 优点:细分类器可以聚焦特定领域,准确率更高
  • 缺点:两步延迟叠加(粗分类 50ms + 细分类 50ms = 100ms);粗分类错误会传播到细分类

并行方案(粗 + 细同时):

Query → 粗分类器 ─┐
       → 细分类器 ──┤→ 合并结果
  • 优点:延迟低(max(50ms, 50ms) = 50ms)
  • 缺点:细分类器需要覆盖所有领域的所有子意图,模型更大、训练数据更多

混合方案(推荐):

  • L1 用轻量规则/关键词做快速粗分类(< 10ms)
  • L2 用 BERT 分类器做细分类(30-50ms)
  • 总延迟 ~50ms,准确率接近串行方案

选型建议:

  • 延迟敏感(< 100ms)→ 并行或混合
  • 准确率优先 → 串行
  • 领域差异大(每个领域的子意图体系不同)→ 串行(每个领域有专门的细分类器)

加分点: "我们用了一个 trick:粗分类器输出 top-2 领域,细分类器只在这两个领域内做细分类。这样既限制了细分类器的搜索空间(准确率更高),又避免了串行方案的全量等待。"


13. Agent 自动化评测数据集怎么构建?自动评测指标怎么计算? ​

分档:二面深度

高分答题思路:

评测数据集构建流程:

  1. 线上日志采样:从真实用户对话中采样,覆盖各意图/各工具/各场景
  2. 人工标注:标注员为每条样本标注:期望意图、期望工具调用序列、期望关键信息点、期望回复要点
  3. 难度分级:简单(单工具单轮)/ 中等(多工具单轮)/ 困难(多工具多轮/需求漂移/冲突)
  4. 数据均衡:确保各意图/各工具/各难度都有足够样本(最少 50 条/类)
  5. 定期更新:每月补充新 Badcase 到评测集,保持数据集时效性

自动评测指标:

指标计算方式目标
意图准确率预测意图 == 标注意图 / 总数> 95%
工具调用准确率调用的工具集合 == 标注集合 / 总数> 90%
工具参数准确率关键参数值 == 标注值 / 总参数数> 85%
任务完成率最终回复包含所有标注要点 / 总数> 80%
多轮一致性各轮决策无矛盾 / 总数> 90%
平均轮数完成任务所需的平均对话轮数越少越好
安全合规率无违规输出 / 总数100%

自动评测实现:

python
def auto_evaluate(agent_response, ground_truth):
    scores = {}
    # 1. 意图准确率
    scores["intent"] = agent_response.intent == ground_truth.intent
    # 2. 工具调用准确率(集合匹配)
    pred_tools = set(agent_response.tool_calls)
    gt_tools = set(ground_truth.expected_tools)
    scores["tools"] = pred_tools == gt_tools
    # 3. 回复关键信息覆盖率(LLM 辅助评判)
    scores["coverage"] = llm_judge_coverage(
        agent_response.reply, ground_truth.key_points
    )
    return scores

加分点: "纯自动评测有天花板——我们用'自动评测 + LLM-as-Judge + 人工抽检'三层:自动评测筛出 fail case,LLM-as-Judge 做语义级别评判,人工只抽检 10% 做最终确认。这样人工成本降了 80% 但覆盖率反而更高。"


14. 完整链路分层埋点:用户输入→路由→工具调用→LLM生成,如何一键定位坏 case 环节? ​

分档:二面深度

高分答题思路:

分层埋点设计:

[用户输入]
  └─ trace_id: uuid, user_query, timestamp, user_id

[路由层]
  └─ route_decision, route_confidence, route_latency_ms

[意图识别层]
  └─ intent_label, intent_confidence, intent_latency_ms

[工具调用层](每个工具一条)
  └─ tool_name, tool_input, tool_output_summary, tool_latency_ms, tool_status

[LLM 生成层]
  └─ prompt_tokens, completion_tokens, model_name, generation_latency_ms, 
     prompt_hash(用于 Debug prompt 变更)

[输出层]
  └─ final_response, user_feedback(如有), quality_score(如有)

一键定位方案:

python
def diagnose_badcase(trace_id: str):
    trace = get_trace(trace_id)
    issues = []
    
    # 1. 路由层检查
    if trace.route_confidence < 0.7:
        issues.append(f"⚠️ 路由置信度低: {trace.route_confidence}, "
                      f"路由到 {trace.route_decision}")
    
    # 2. 意图层检查
    if trace.intent_confidence < 0.6:
        issues.append(f"⚠️ 意图置信度低: {trace.intent_label} "
                      f"(confidence={trace.intent_confidence})")
    
    # 3. 工具层检查
    for call in trace.tool_calls:
        if call.status != "success":
            issues.append(f"❌ 工具调用失败: {call.tool_name}, "
                          f"error={call.error_msg}")
        if call.latency_ms > 5000:
            issues.append(f"⏰ 工具延迟高: {call.tool_name}, "
                          f"{call.latency_ms}ms")
        if not call.tool_output_summary:
            issues.append(f"📭 工具返回空: {call.tool_name}")
    
    # 4. LLM 层检查
    if trace.generation_latency_ms > 3000:
        issues.append(f"⏰ LLM 生成慢: {trace.generation_latency_ms}ms")
    if trace.prompt_tokens > 4000:
        issues.append(f"📏 上下文过长: {trace.prompt_tokens} tokens")
    
    return issues if issues else ["✅ 各环节指标正常,需人工分析回复质量"]

关键设计原则:

  • 统一 trace_id:贯穿全链路的唯一 ID,支持日志聚合查询
  • 可回放:每个环节的输入/输出都存储,支持完整 replay
  • 告警联动:某环节指标异常时自动告警,不等用户反馈
  • 采样策略:全量埋点关键指标,详细 I/O 采样 10%(降低存储成本)

加分点: "我们做了一个 Badcase 诊断 Dashboard:输入 trace_id 就能看到全链路瀑布图 + 各环节指标 + 异常高亮。从'用户反馈到定位问题环节'从平均 30 分钟降到 3 分钟。"


15. Badcase 根因归类:各自修复手段?微调、prompt 工程、检索增强、工具扩充四者迭代优先级? ​

分档:二面深度

高分答题思路:

根因分类与修复手段:

根因类型占比(典型)修复手段见效速度
Prompt 问题30-40%优化 Prompt 指令/Few-shot/输出格式快(1-2天)
工具缺失15-20%新增工具/扩展现有工具能力中(1-2周)
模型能力不足15-20%换更强模型/微调慢(2-4周)
检索素材错误10-15%优化知识库/检索策略/数据清洗中(1周)
Agent 分工不合理5-10%重新拆分 Agent/调整路由策略中(1-2周)
数据分布变化5-10%更新训练数据/评测集慢(2-4周)

四者迭代优先级(ROI 排序):

1. Prompt 工程(最高优先级)
   - ROI 最高:改动小、见效快、可回滚
   - 先穷尽 Prompt 优化空间再考虑其他

2. 检索增强(第二优先级)
   - 对知识密集型 Badcase 效果显著
   - 优化知识库质量 + 检索策略,不改模型

3. 工具扩充(第三优先级)
   - 当 Prompt 和检索都无法解决时,考虑新增工具
   - 开发成本中等,但能解决全新类别的 case

4. 微调(最后考虑)
   - 成本最高、周期最长、不可逆性强
   - 只有前三种手段都穷尽后才考虑
   - 适合:特定格式/风格/领域知识无法通过 Prompt 注入的场景

决策流程图:

Badcase → 根因分析
  ├─ Prompt 不清晰 → 优化 Prompt ✅
  ├─ 缺少知识 → 补充检索素材 ✅
  ├─ 缺少能力 → 新增工具 ✅
  ├─ 模型理解能力不足 → 换更强模型 ✅
  └─ 以上都不行 → 微调(最后手段)

加分点: "我们建立了一个 Badcase 决策树——每个 Badcase 先过根因分类器(规则 + LLM 辅助),自动推荐修复手段。80% 的 case 在 Prompt/检索层就能解决,真正需要微调的不到 5%。"


16. SFT 微调数据集怎么清洗、去重、均衡正负样本?如何避免数据泄露? ​

分档:二面深度

高分答题思路:

数据清洗流程:

  1. 格式清洗

    • 去除乱码、HTML 标签、不可见字符
    • 统一编码(UTF-8)、统一换行符
    • 校验 JSON/对话格式合法性
  2. 内容清洗

    • 去除重复样本(精确去重 + MinHash 模糊去重)
    • 去除过短/过长样本(< 10 token 或 > 4096 token)
    • 去除低质量样本(困惑度过高、回复与问题不相关)
    • 敏感信息脱敏(手机号、邮箱、地址)
  3. 去重策略

    • 精确去重:hash(query + response) 完全匹配
    • 语义去重:Embedding 相似度 > 0.95 的只保留一条
    • 任务级去重:同一任务模板生成的样本,每类最多保留 N 条
  4. 正负样本均衡

    • 不是简单的 1:1 均衡,而是按任务类型/难度/工具组合分层均衡
    • 过采样少数类(SMOTE 变体)或欠采样多数类
    • 确保每个意图/每个工具/每个难度等级都有足够样本

数据泄露防范:

  • 时间隔离:训练数据时间截止 > 评测数据最早时间,避免模型"见过未来"
  • 评测集隔离:评测集严格保密,只在评测时使用,不进入任何训练流程
  • 相似度过滤:训练集与评测集的 Embedding 相似度 > 0.9 的训练样本移除
  • 人工抽查:随机抽 5% 训练样本人工检查是否与评测集过于相似

加分点: "我们踩过一个坑:早期微调后发现评测指标虚高,排查发现是训练集和评测集有 15% 的语义重复样本。加了 Embedding 相似度过滤后,评测指标回归真实水平(从'虚高 92%'降到'真实 81%'),后续迭代方向才准确。"


17. SFT 微调后出现灾难性遗忘怎么缓解?LoRA 和全量微调选型依据? ​

分档:二面深度

高分答题思路:

灾难性遗忘的缓解方案:

方案效果成本
混入通用数据微调数据中混入 20-30% 通用对话数据,保持基础能力低(只需数据混合)
LoRA 微调只训练低秩矩阵,原始权重不变,天然缓解遗忘中(需调 rank/alpha)
正则化约束L2 正则化限制权重偏移 / EWC 约束重要参数中
渐进式学习学习率 warmup + cosine decay,避免大幅权重更新低
多任务联合训练将微调任务和通用任务联合训练高(需平衡多任务 loss)

LoRA vs 全量微调选型:

维度LoRA全量微调
参数量0.1-1% 可训练参数100% 可训练参数
显存需求低(8-16GB 可微调 7B)高(需多卡 A100)
效果上限略低于全量(差距 < 5%)最高
遗忘程度轻微(原始权重保留)严重
部署灵活性可随时切换/合并不同 LoRA只能部署一个版本
适用场景领域适配/风格调整/格式约束根本性能力提升/全新领域

选型决策树:

微调目标是什么?
  ├─ 格式/风格/特定任务适配 → LoRA(够用且灵活)
  ├─ 领域知识注入 → LoRA + RAG(LoRA 调风格,RAG 补知识)
  ├─ 全新能力(如代码生成→数学推理) → 全量微调
  └─ 资源受限 → LoRA(显存不够时的唯一选择)

加分点: "我们的实践是:90% 的场景用 LoRA 就够了。只有当我们需要让模型学会一个全新的推理范式(比如从对话式变成 Plan-Solve 式)时,才考虑全量微调。而且 LoRA 的好处是可以为不同租户维护不同的 LoRA 权重,部署时动态加载。"


18. 面对冲突样本:一类样本优化后另一类恶化,除了扩充均衡样本还有什么方案? ​

分档:二面深度

高分答题思路:

冲突样本的几种类型:

  • 格式冲突:样本A要求JSON输出,样本B要求自然语言输出
  • 风格冲突:样本A要求简洁,样本B要求详细
  • 知识冲突:样本A的领域知识覆盖了样本B的领域
  • 任务冲突:样本A训练"主动追问",样本B训练"自主决策"

解决方案矩阵:

方案适用冲突类型效果
数据均衡扩充数量不均衡导致的偏移缓解数量偏移,不解决本质冲突
条件 Prompt格式/风格冲突在 Prompt 中加条件分支,不同条件不同行为
多 LoRA 分治任务冲突不同任务用不同 LoRA,部署时按任务加载
课程学习难度冲突先学简单再学复杂,减少梯度冲突
样本加权重要性差异重要样本权重高,减少被覆盖
数据重新标注标注质量冲突人工复核冲突样本,修正错误标注
分阶段微调知识冲突先微调领域A,再混入领域B数据微调(加通用数据保底)

推荐策略(组合方案):

  1. 先分析冲突根因(是数据问题还是任务本质冲突)
  2. 数据问题 → 重新标注/清洗
  3. 任务本质冲突 → 多 LoRA 分治或条件 Prompt
  4. 无法分治 → 接受 trade-off,选择更重要的任务优先

加分点: "我们遇到的最典型的冲突是'工具调用 vs 自由对话'——训练工具调用后模型变得过度工具化,闲聊场景也试图调工具。解决方案不是平衡数据,而是加了一个路由前置判断:先判断是否需要工具,需要才走工具调用模式。这比在模型层面解决冲突更高效。"


19. Continuous Batching 和静态 batch 区别,线上峰值怎么动态调整批大小? ​

分档:二面深度

高分答题思路:

核心区别:

维度静态 BatchContinuous Batching
批次组成请求同时到达才组批请求随时加入/随时离开
GPU 利用率低(等满 batch 或超时)高(持续满载)
尾延迟高(最慢的请求拖住整个 batch)低(请求完成即返回)
实现复杂度简单复杂(需要细粒度调度)

Continuous Batching 原理:

  • 每次迭代步(iteration step)检查:是否有新请求可加入?是否有请求已完成可移除?
  • 已完成的请求立即返回结果,空出的 slot 给等待中的新请求
  • 类似"公交车"模式:乘客随时上下,车持续运行

动态批大小调整策略:

python
def dynamic_batch_size(current_load, gpu_memory):
    # 基础策略:根据 GPU 显存和当前负载动态调整
    if gpu_memory.usage < 0.7:
        target_batch = min(current_load.waiting_count, MAX_BATCH)
    elif gpu_memory.usage < 0.85:
        target_batch = current_batch * 0.8  # 降负载
    else:
        target_batch = current_batch * 0.5  # 大幅降负载
    
    # 峰值保护:不超过 MAX_BATCH(防止 OOM)
    return min(target_batch, MAX_BATCH)

峰值应对:

  1. 请求队列 + 优先级:高优先级请求优先入批,低优先级排队
  2. 自动扩容:QPS 超过阈值 → 自动启动新 Worker
  3. 降级策略:队列过长时,部分请求降级到更小/更快的模型
  4. 超时丢弃:排队超过 N 秒的请求直接返回降级结果,避免雪崩

加分点: "我们用 vLLM 的 Continuous Batching + 自定义调度器。峰值 QPS 从 50 到 200 时,通过动态调整 batch size(16→64→32 自适应)和自动扩容 Worker,99 分位延迟保持在 2s 以内。关键是不让 GPU 空跑,也不让 OOM 发生。"


20. 推理服务高可用:多模型负载均衡、熔断降级、限流怎么设计? ​

分档:二面深度

高分答题思路:

整体架构:

                    ┌─────────────┐
  请求 →  API Gateway │  限流 + 路由 │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
         模型A(主力)   模型B(备选)   模型C(降级)
         vLLM×3副本    vLLM×2副本    小模型×1
              │            │            │
              └────────────┼────────────┘
                           │
                    ┌──────┴──────┐
                    │  熔断 + 降级  │
                    └─────────────┘

负载均衡策略:

  • 加权轮询:主力模型权重高,备选模型权重低
  • 一致性哈希:相同前缀的请求路由到同一节点(利用 KV Cache 前缀复用)
  • 最短队列:路由到等待队列最短的节点
  • 健康检查:定期探测节点健康状态,异常节点摘除

熔断降级设计:

层级触发条件动作
L1 单实例熔断单实例错误率 > 10%摘除该实例,流量转移到其他实例
L2 模型熔断某模型所有实例错误率 > 20%切换到备选模型
L3 全局降级所有模型不可用或 QPS 超限降级到小模型/规则回复/缓存回复
L4 拒绝服务队列超过上限返回 503 + 预计恢复时间

限流设计:

  • 令牌桶限流:按 QPS 和 token/s 双维度限流
  • 分级限流:免费用户 10 QPS,付费用户 100 QPS,内部服务不限流
  • 排队机制:超限请求进入队列,而非直接拒绝(等待 < 3s 才排队)

缓存加速:

  • Prompt 缓存:相同 System Prompt + Few-shot 的请求共享前缀 KV Cache
  • 结果缓存:完全相同的请求缓存结果(TTL 5min),命中率 10-15%
  • 语义缓存:Embedding 相似度 > 0.95 的请求复用结果(需验证不会产生错误)

加分点: "我们设计了'三级降级链':GPT-4 → GPT-3.5 → 自部署 7B 模型 → 规则模板。每一级降级都是自动的,但会记录降级日志用于后续分析。关键设计是降级要快——从检测到异常到完成降级 < 5 秒,用户几乎无感。"


答题策略总结 ​

要点说明
四段式问题背景 → 解决方案 → 取舍权衡 → 数据佐证
展示踩坑主动提到"我们踩过的坑",展示真实经验
数据说话尽量给出量化指标(准确率提升 X%、延迟降低 Y ms)
取舍意识每个方案都有代价,主动分析 trade-off
递进追问准备二面面试官会追问细节,答到能被追问的颗粒度