Skip to content

二面深度·补充:架构实战深化(16题) ​

原始 20 题基础上的深化题目,考察更细颗粒度的架构决策和实战经验 答题要求:四段式 "问题 → 方案 → 取舍 → 数据佐证" 建议每题答题时间:3-5分钟


一、Agent 架构深化(8题) ​

D1-1. 自研 Agent 架构与 LangGraph、AutoGen 结合使用的场景有哪些?如何实现二者的优势互补? ​

分档:二面深度

高分答题思路:

混合使用的三种模式:

模式结合方式适用场景优势
自研骨架 + LangGraph 节点自研框架做编排/监控/容错,复杂子流程用 LangGraph 实现图节点子流程有复杂分支循环,但主流程需要精细控制自研可控 + LangGraph 图表达能力
自研 + AutoGen 研究探索生产用自研,内部实验/原型用 AutoGen 快速验证多 Agent 协作新功能探索期需要快速验证多 Agent 协作效果生产稳定 + 探索灵活
LangGraph 编排 + 自研工具层LangGraph 做流程编排,工具/记忆/监控全部自研团队对 LangGraph 熟悉,但工具体系有定制需求编排效率 + 工具定制化

优势互补的关键设计:

  1. 接口标准化:自研部分和框架部分通过标准接口(输入/输出 Schema)通信,不深度耦合
  2. 可替换设计:LangGraph 节点可以替换为自研节点,反之亦然,通过适配层转换
  3. 监控统一:无论用哪部分,trace_id 贯穿,埋点格式统一

坚决不混用的场景:

  • 团队对两个框架都不熟 → 引入两个框架的维护成本 > 收益
  • 简单线性流程 → 自研已足够,引入框架是负优化
  • 高性能要求场景 → 框架叠加增加序列化/抽象开销

数据佐证模板: "我们的方案是自研编排骨架 + LangGraph 做复杂子流程。核心流程的延迟控制在 50ms 以内(自研),复杂子流程用 LangGraph 的图编排快速迭代开发,整体开发效率提升 40%,同时核心链路性能不受影响。"


D1-2. Master+Sub Agent 模式中,Master Agent 出现单点故障时,如何设计容灾方案保障业务不中断? ​

分档:二面深度

高分答题思路:

单点故障风险分析:

  • Master Agent 是所有请求的入口和编排者 → 挂了 = 全站不可用
  • Master 持有会话状态和编排上下文 → 挂了 = 进行中的任务丢失

四层容灾方案:

层级方案实现细节
L1 无状态化Master 不持有状态,状态全部存 Redis请求带 session_id,任意 Master 实例都能处理
L2 多实例部署Master 部署 3+ 实例,负载均衡Nginx/K8s Service 做健康检查,异常实例自动摘除
L3 状态持久化编排上下文(状态机快照)实时写 Redis实例故障后,新实例从 Redis 恢复上下文继续执行
L4 降级直通Master 全挂时,请求直接路由到默认 SubAgent用 Nginx fallback 配置,绕过 Master 走预设流程

故障切换流程:

Master 实例A 故障
    │
    ▼
健康检查失败(3次连续失败,间隔5s)
    │
    ▼
负载均衡摘除实例A
    │
    ▼
流量转移到实例B/C
    │
    ▼
实例B 从 Redis 读取 session 上下文
    │
    ▼
从中断点继续执行(状态机恢复)
    │
    ▼
实例A 恢复后自动重新加入

关键设计细节:

  • 会话亲和性:尽量让同一 session 路由到同一 Master 实例(减少 Redis 读取),但故障时自动切换
  • 幂等执行:状态恢复后重新执行最后一步时,必须幂等(工具调用可能已执行过)
  • 超时兜底:如果切换 + 恢复 > 10s,直接返回降级响应,不等恢复

加分点: "我们设计了一个'影子 Master'机制——正常情况下实例B/C 不接收流量,只做热备(同步 Redis 状态)。实例A 故障时,影子 Master 0ms 切换为主,比'从 Redis 恢复'方案快 10 倍。成本是多一倍实例,但核心场景值得。"


D1-3. 多 Agent 协作中存在信息冗余传递问题,你会通过哪些方式做信息压缩和关键信息提取? ​

分档:二面深度

高分答题思路:

信息冗余的三种来源:

  1. 全量传递:Master 把完整上下文传给每个 SubAgent → 大量无关信息
  2. 结果回传:SubAgent 把完整工具返回传回 Master → 结果可能很长
  3. 重复传递:多个 SubAgent 需要相同的基础信息,各自重复接收

压缩与提取方案:

方案适用场景效果
结构化提取传递前提取关键字段(实体/决策/约束)压缩 70-80%,信息损失 < 5%
分层传递基础信息只传一次(存共享池),SubAgent 按需读取避免重复传递
摘要压缩长文本用 LLM 生成摘要再传递压缩 60-70%,但引入摘要延迟
差量传递只传增量信息(与上次传递的差异)适合多轮协作,首轮仍需全量
引用机制传 reference_id,SubAgent 需要时再查延迟传递,最小化传输量

关键信息提取实现:

python
class ContextExtractor:
    """从完整上下文中提取关键信息"""
    
    EXTRACTION_RULES = {
        "user_query": "always",          # 原始用户问题,永远保留
        "user_constraints": "always",     # 用户明确约束(预算/时间/偏好)
        "decisions": "always",            # 已确认的决策
        "tool_results": "summarize",      # 工具结果做摘要
        "chitchat": "drop",              # 寒暄丢弃
        "intermediate_reasoning": "drop", # 中间推理过程丢弃
    }
    
    def extract(self, full_context: dict) -> dict:
        essential = {}
        for field, strategy in self.EXTRACTION_RULES.items():
            if strategy == "always":
                essential[field] = full_context[field]
            elif strategy == "summarize":
                essential[field] = self.summarize(full_context[field])
            # drop: 不传递
        return essential

量化效果:

  • 原始传递:8K token/Agent → 压缩后:1.5K token/Agent
  • SubAgent 推理准确率:从 82% → 84%(上下文更聚焦,减少干扰)
  • 推理成本:降低 55%

加分点: "关键洞察是'信息越多反而效果越差'——SubAgent 收到大量无关信息时,LLM 注意力被稀释,关键信息反而被忽略。所以压缩不只是省 token,更是提升推理质量。"


D1-4. 动态 Agent 编队场景下,如何根据任务复杂度自动增减子 Agent 数量,实现资源最优利用? ​

分档:二面深度

高分答题思路:

复杂度评估模型:

python
def assess_complexity(query: str, context: dict) -> str:
    score = 0
    # 维度1:意图数量(多个意图 = 高复杂度)
    score += len(detect_intents(query)) * 2
    # 维度2:工具数量预估
    score += len(estimate_required_tools(query))
    # 维度3:历史轮数(多轮积累 = 可能需要更多 Agent)
    score += min(context.get("turn_count", 0), 5)
    # 维度4:查询长度和信息密度
    score += min(len(query) // 100, 3)
    # 维度5:是否涉及跨领域
    score += 3 if is_cross_domain(query) else 0
    
    if score <= 4: return "simple"
    elif score <= 8: return "medium"
    else: return "complex"

动态编队策略:

复杂度Agent 配置典型场景
Simple1 个通用 Agent单意图、单工具(如简单问答)
Medium1 Master + 1-2 SubAgent多步骤但同领域(如搜索+总结)
Complex1 Master + 3-5 SubAgent跨领域、多工具协作(如数据分析+报告生成)

动态调整机制:

  1. 初始编队:根据复杂度评估结果分配初始 Agent 集
  2. 运行时扩容:Agent 执行中发现需要额外能力 → 动态启动新 SubAgent
  3. 运行时缩容:SubAgent 完成任务后立即释放,不保留到会话结束
  4. 超时降级:复杂度评估 > 阈值但实际执行简单 → 降级为单 Agent

资源优化效果:

  • 简单场景(占 60% 流量):单 Agent,资源消耗最低
  • 中等场景(30%):2-3 Agent,平衡效果和成本
  • 复杂场景(10%):4-6 Agent,保证效果

关键取舍:

  • 动态编队增加调度复杂度 → 需要完善的 Agent 注册/发现机制
  • 复杂度评估准确率直接影响效果 → 评估错误导致过度编队或编队不足
  • 动态启停 Agent 有冷启动延迟 → 预热池保持 1-2 个热 Agent

加分点: "我们统计发现 60% 的请求是简单场景,只需要单 Agent。动态编队后,平均每请求的 Agent 实例数从 3.2 降到 1.8,推理成本降低 40%,而任务完成率保持不变(因为简单场景本来不需要多 Agent)。"


D1-5. Agent 的长短期记忆如何结合使用?长时记忆的存储介质选型依据是什么? ​

分档:二面深度

高分答题思路:

长短期记忆协同模型:

用户请求进入
    │
    ▼
[短期记忆] ← 当前对话窗口(最近 5-10 轮,存 LLM Context)
    │ 检索:直接在 Context 中
    │ 写入:每轮对话自动加入
    │ 淘汰:超过窗口大小 → 触发摘要
    │
    ▼
[会话记忆] ← 当前会话的关键信息(存 Redis,TTL 24h)
    │ 检索:按 session_id 直接读取
    │ 写入:每轮提取关键信息写入
    │ 淘汰:会话结束 / TTL 过期
    │
    ▼
[长期记忆] ← 跨会话的持久记忆(存向量数据库 + 关系型数据库)
    │ 检索:向量相似度 + 关键词混合检索
    │ 写入:会话结束时,提取值得长期记住的信息
    │ 淘弃:LRU + 重要性评分衰减

协同工作流:

  1. 读记忆:短期记忆(Context 内)→ 会话记忆(Redis)→ 长期记忆(向量库),按需逐层检索
  2. 写记忆:每轮对话 → 更新短期记忆 → 提取关键信息写会话记忆 → 会话结束时筛选写长期记忆
  3. 记忆晋升:短期记忆被淘汰时 → 摘要后写入会话记忆 → 会话记忆过期时 → 筛选写入长期记忆

长时记忆存储介质选型:

维度向量数据库(Milvus/Faiss)关系型数据库(PostgreSQL)文档数据库(MongoDB)
检索方式语义相似度精确匹配/范围查询字段查询+全文索引
适合存储描述性记忆("用户喜欢简洁风格")结构化记忆(用户偏好键值对、决策历史)半结构化记忆(对话摘要)
检索精度模糊匹配,适合"相似场景"精确匹配,适合"具体查询"中等
写入成本需 Embedding 计算直接写入直接写入
扩展性高(向量索引优化)中(需要分表)高

选型决策框架:

记忆内容是什么?
  ├─ 结构化事实(偏好/设置/决策)→ PostgreSQL(精确查询)
  ├─ 描述性经验(对话摘要/教训/案例)→ 向量数据库(语义检索)
  └─ 时序事件(操作日志/行为序列)→ 时序数据库/PostgreSQL

混合存储实践(推荐):

  • PostgreSQL:用户画像表(结构化偏好、配置)+ 决策历史表
  • Milvus:经验记忆(对话摘要的 Embedding)+ 案例库
  • Redis:会话级热数据 + 记忆索引(快速判断是否有相关长期记忆)
  • 查询时:先查 Redis 索引 → 有则并行查 PG + Milvus → 合并结果

加分点: "记忆不是越多越好。我们做过实验:长期记忆条目从 100 增加到 1000,检索准确率反而从 85% 降到 72%——因为噪声记忆干扰了检索。所以记忆淘汰和记忆写入同样重要,我们用'重要性评分 + 时间衰减 + 访问频率'做综合淘汰。"


D1-6. 跨 Agent 任务交接时,如何保证上下文的连贯性和任务状态的无缝传递? ​

分档:二面深度

高分答题思路:

交接中的三类风险:

  1. 信息丢失:上下文摘要遗漏关键字段
  2. 状态不一致:源 Agent 和目标 Agent 对任务状态的理解不同
  3. 语义偏移:交接描述的语义与原始用户意图有偏差

无缝传递方案:

1. 结构化交接协议

python
class HandoffContext(BaseModel):
    """Agent 交接协议"""
    # 不可变字段(原始信息,永不丢失)
    task_id: str
    original_user_query: str
    user_constraints: list[str]          # 用户明确约束
    
    # 状态字段(当前进展)
    current_state: str                    # 当前状态机节点
    completed_steps: list[StepResult]     # 已完成步骤及结果
    pending_steps: list[Step]             # 待执行步骤
    
    # 上下文字段(辅助信息)
    key_decisions: list[Decision]         # 已确认的决策
    key_entities: list[Entity]            # 关键实体
    tool_results_summary: list[str]       # 工具结果摘要
    
    # 元信息
    source_agent: str
    handoff_reason: str                   # 为什么交接
    confidence_score: float               # 交接信息完整度

2. 交接校验机制

  • 完整性校验:接收方收到交接上下文后,检查 must_have 字段是否齐全
  • 语义校验:LLM 快速校验"交接描述与原始 Query 是否一致"
  • 状态校验:检查 current_state 是否在目标 Agent 的状态枚举中

3. 交接失败的兜底

  • 校验失败 → 接收方拒绝交接,回退到 Master 重新分配
  • 交接超时(> 5s)→ Master 介入,决定重试或降级
  • 连续交接失败 3 次 → 转人工处理

4. 交接审计

  • 每次交接记录:source → target、交接上下文摘要、校验结果
  • 支持 Badcase 回溯:如果最终结果异常,可以追踪哪次交接出了问题

数据佐证模板: "我们引入结构化交接协议后,跨 Agent 信息丢失率从 15% 降到 2% 以下。关键是'原始 Query 不可变'+ 'must_have 字段校验'这两条硬约束,解决了 80% 的交接问题。"


D1-7. 设计 Agent 时,如何平衡"自主决策能力"和"人工干预可控性",避免 Agent 做出偏离业务预期的操作? ​

分档:二面深度

高分答题思路:

自主性分级模型:

级别自主程度适用场景干预机制
L0 完全规则Agent 只执行预设流程合规要求高的场景(金融/医疗)人工定义所有规则
L1 建议模式Agent 给建议,人工确认后执行高风险决策(如发邮件/转账)关键操作必须人工 approve
L2 受限自主Agent 自主执行,但敏感操作需确认一般业务场景敏感操作列表触发确认
L3 完全自主Agent 全程自主,事后审计低风险场景(如信息查询)事后审计 + 异常告警

可控性设计:

1. 操作分级

python
class OperationLevel(Enum):
    SAFE = "safe"          # 只读操作,无需确认(搜索/查询)
    MODERATE = "moderate"  # 写入操作,需记录审计(保存/更新)
    SENSITIVE = "sensitive" # 外部影响,需人工确认(发送邮件/发布内容)
    CRITICAL = "critical"  # 不可逆操作,需双重确认(删除/支付)

# 每个工具标注操作级别
@register_tool("send_email", level=OperationLevel.SENSITIVE)
async def send_email(to, subject, body): ...

@register_tool("search", level=OperationLevel.SAFE)
async def search(query): ...

2. 护栏机制

  • 工具白名单:Agent 只能调用授权工具
  • 参数约束:工具参数有 Schema 约束(如金额上限、收件人白名单)
  • 输出审查:Agent 最终输出经安全过滤器检查后再返回用户
  • 行为边界:System Prompt 明确"不能做什么",配合后处理校验

3. 人工干预入口

  • 实时干预:敏感操作弹窗确认,人工 approve/reject
  • 异步审核:Agent 执行后记录审计日志,人工事后抽检
  • 紧急停止:管理员一键暂停所有 Agent(kill switch)

4. 偏离检测

  • 行为基线:统计 Agent 正常行为的模式(工具调用分布、响应长度等)
  • 异常检测:实时行为偏离基线 > 阈值时告警
  • 用户反馈回路:收集用户对 Agent 行为的反馈,持续修正

加分点: "核心设计原则是'风险与自主性成反比'——低风险操作给 Agent 充分自主性以提升效率,高风险操作必须有人工 checkpoint。我们的实践是 L2 级别覆盖 80% 场景,既不过度干预影响效率,又能在关键环节兜住底线。"


D1-8. 工具调用链过长导致的响应延迟问题,有哪些优化手段?如何做调用链路的超时管控和链路裁剪? ​

分档:二面深度

高分答题思路:

延迟问题分析:

  • 每个工具调用:网络 RTT + 执行时间 + 结果处理 = 500ms-3s
  • 5 步链式调用:2.5s-15s,用户体感差
  • LLM 决策时间:每步 500ms-2s,5 步 = 2.5-10s
  • 总延迟 = 工具执行 + LLM 决策 = 5-25s

优化手段:

手段效果实施难度
并行工具调用无依赖的工具并行执行,延迟降 40-60%中(需分析依赖关系)
流式输出首字延迟降 50%+,用户体感大幅改善低
工具结果缓存相同参数的工具调用缓存结果,命中率 10-20%中
预计算/投机执行预测下一步工具调用,提前执行高
链路裁剪跳过非必要步骤中
工具合并多个小工具合并为大工具,减少调用次数中
异步化长耗时工具异步执行,先返回部分结果中

超时管控设计:

全局超时预算:15s
    │
    ├─ LLM 决策预算:每步 2s(共 ~6s for 3 步)
    │   └─ 超时 → 跳过当前步骤,用上一步结果继续
    │
    ├─ 工具执行预算:每步 3s
    │   └─ 超时 → 重试 1 次 → 仍超时 → 降级/跳过
    │
    └─ 全局超时:15s
        └─ 超时 → 返回已完成部分结果 + 提示"部分结果"

链路裁剪策略:

  1. 必须步骤:核心路径不可裁剪(如必选的工具调用)
  2. 可选步骤:增强型步骤可裁剪(如结果格式化、额外验证)
  3. 奢侈步骤:锦上添花的步骤优先裁剪(如多角度验证、风格优化)
python
class ChainOptimizer:
    def optimize(self, planned_chain: list[Step], time_budget: float) -> list[Step]:
        essential = [s for s in planned_chain if s.priority == "must"]
        optional = [s for s in planned_chain if s.priority == "optional"]
        luxury = [s for s in planned_chain if s.priority == "luxury"]
        
        estimated_time = sum(s.estimated_duration for s in essential)
        
        # 时间预算内尽量加入 optional
        for step in optional:
            if estimated_time + step.estimated_duration <= time_budget:
                essential.append(step)
                estimated_time += step.estimated_duration
        
        # 剩余时间加入 luxury
        for step in luxury:
            if estimated_time + step.estimated_duration <= time_budget:
                essential.append(step)
                estimated_time += step.estimated_duration
        
        return essential

数据佐证模板: "我们通过'并行调用 + 链路裁剪 + 超时降级'三管齐下,把 5 步工具调用的平均延迟从 12s 降到 4.5s,P99 从 25s 降到 8s。关键是链路裁剪——70% 的场景只需要 3 步核心调用就能完成任务,额外 2 步是可选的增强。"


二、Prompt 工程深化(4题) ​

D2-1. 针对多轮对话中用户意图反复变更的场景,如何设计 Prompt 实现意图的实时校准和冲突检测? ​

分档:二面深度

高分答题思路:

意图校准 Prompt 设计:

你是一个意图追踪器。分析以下对话历史,检测意图变化。

## 对话历史
{conversation_summary}

## 当前用户消息
{current_message}

## 任务
1. 提取当前消息的意图
2. 与历史意图对比,检测是否发生意图漂移
3. 如果检测到意图冲突,输出冲突描述
4. 输出最新的有效意图(以最新用户消息为准,但保留历史约束)

## 输出格式
{
  "current_intent": "最新意图",
  "intent_changed": true/false,
  "conflict_type": "none|constraint_conflict|goal_shift|scope_change",
  "conflict_description": "冲突描述(如有)",
  "effective_constraints": ["当前生效的所有约束"],
  "clarification_needed": "需要向用户确认的问题(如有)"
}

冲突检测规则:

  • constraint_conflict:新约束与旧约束矛盾(如之前说"便宜的",现在说"要高端的")
  • goal_shift:核心目标变化(如从"搜索餐厅"变成"查看天气")
  • scope_change:范围扩大或缩小(如从"全北京"变成"朝阳区")

校准策略:

  • goal_shift → 以最新目标为准,清除旧目标的上下文
  • constraint_conflict → 向用户确认:"您之前提到要 X,现在要 Y,请问以哪个为准?"
  • scope_change → 自动更新,不需要确认(这是正常的对话演进)

加分点: "意图校准不是每轮都做——我们用了一个'意图稳定性评分',只在评分低于阈值时触发校准。这样避免过度敏感(用户只是在补充细节就触发冲突检测),也避免不够敏感(真正冲突时没检测到)。"


D2-2. 结构化 Prompt 设计中,如何兼顾"格式约束的严格性"和"模型生成的灵活性",避免格式错误导致任务失败? ​

分档:二面深度

高分答题思路:

核心矛盾:

  • 格式约束太严 → 模型生成僵化,内容质量下降
  • 格式约束太松 → 格式错误率高,下游解析失败

三层平衡方案:

层次策略效果
Prompt 层格式约束与内容生成分离:先自由生成内容,再格式化输出内容质量不受格式约束影响
Schema 层用 JSON Schema 定义"必须字段"和"可选字段"核心字段严格,非核心字段灵活
后处理层Pydantic 校验 + 自动修复 + 字段补默认值兜底保障,格式错误不阻塞

分离式 Prompt 设计:

## 第一步:自由思考(不受格式约束)
分析用户问题,思考解决方案。
{thinking_section}

## 第二步:结构化输出(严格格式)
基于以上思考,输出以下 JSON 格式:
{
  "answer": "你的回答(自然语言)",
  "confidence": 0.0-1.0,
  "sources": ["引用的信息来源"],
  "follow_up": "建议的后续问题(可选)"
}

注意:
- answer 字段可以是自然语言,不受格式限制
- confidence 和 sources 必须符合类型要求
- follow_up 可选,没有就不输出

关键设计原则:

  1. 必填字段最小化:只有下游必须依赖的字段标为 required
  2. 内容字段不约束格式:answer/reply 等自然语言字段不做格式约束
  3. 元数据字段严格约束:confidence/tool_name/status 等必须符合类型
  4. 容错设计:后处理层保证即使格式有错也能继续,不阻塞流程

加分点: "我们的格式错误率从 15% 降到 1% 以下,关键不是 Prompt 写得多好,而是后处理层兜底——Pydantic 校验 + 自动类型转换 + 缺失字段补默认值。Prompt 只负责'引导',不负责'保证'。"


D2-3. 上下文窗口有限的情况下,如何实现历史对话的智能裁剪,优先保留与当前任务相关的关键信息? ​

分档:二面深度

高分答题思路:

智能裁剪算法:

完整对话历史(10轮,8K token)
    │
    ▼
[Step 1: 重要性评分]
每轮对话评分:
  - 含用户明确约束 → 10分
  - 含关键决策 → 8分
  - 含工具调用结果 → 6分
  - 含用户偏好陈述 → 7分
  - 纯寒暄/确认 → 1分
  - 中间推理过程 → 2分
    │
    ▼
[Step 2: 相关性评分]
与当前 Query 的相关性:
  - 语义相似度(Embedding)
  - 实体重叠度
  - 意图关联度
    │
    ▼
[Step 3: 综合评分]
final_score = 重要性 × 0.6 + 相关性 × 0.4
    │
    ▼
[Step 4: 预算分配]
总预算:3K token
  - 保留 Top N 轮(综合评分最高的)
  - 低分轮次做摘要(1-2句话)
  - 丢弃评分 < 2 的轮次
    │
    ▼
优化后上下文(3K token)

实现代码框架:

python
class ContextPruner:
    def prune(self, history: list[Turn], current_query: str, 
              max_tokens: int = 3000) -> list[Turn]:
        # 1. 评分
        for turn in history:
            turn.importance = self.score_importance(turn)
            turn.relevance = self.score_relevance(turn, current_query)
            turn.final_score = turn.importance * 0.6 + turn.relevance * 0.4
        
        # 2. 排序
        sorted_turns = sorted(history, key=lambda t: t.final_score, reverse=True)
        
        # 3. 预算分配
        result = []
        remaining_budget = max_tokens
        for turn in sorted_turns:
            if turn.final_score < 2:
                continue  # 丢弃
            if remaining_budget < turn.token_count:
                # 摘要代替
                summary = self.summarize(turn)
                result.append(Turn(summary, is_summary=True))
                remaining_budget -= count_tokens(summary)
            else:
                result.append(turn)
                remaining_budget -= turn.token_count
        
        # 4. 按时间序恢复
        result.sort(key=lambda t: t.timestamp)
        return result

关键设计细节:

  • 原始 Query 永不裁剪:用户最初的请求完整保留
  • 约束信息永不裁剪:用户明确的偏好/约束(预算/时间/格式要求)
  • 摘要标注:被摘要的轮次标记 [摘要],让 LLM 知道这不是原始内容
  • 裁剪日志:记录被裁剪的内容,Badcase 分析时可回溯

数据佐证模板: "智能裁剪让我们的上下文从平均 8K token 降到 3K token,而任务完成率只降了 2%(从 88% → 86%)。关键是'约束信息永不裁剪'——被裁掉的都是低信息量的寒暄和中间过程。"


D2-4. 针对复杂任务,如何设计分层 Prompt,实现"拆解任务-执行任务-校验结果"的闭环引导? ​

分档:二面深度

高分答题思路:

三层闭环 Prompt 架构:

┌─────────────────────────────────────────┐
│  Layer 1: Planner Prompt(任务拆解)      │
│  "分析用户需求,拆解为子任务列表"          │
│  输出:[{step, tool, expected_output}]   │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│  Layer 2: Executor Prompt(逐步执行)     │
│  "执行第 N 步:{step},使用工具:{tool}"  │
│  输出:工具调用 + 执行结果                │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│  Layer 3: Validator Prompt(结果校验)     │
│  "检查执行结果是否满足:1)用户原始需求     │
│   2)本步骤预期输出 3)整体任务完成度"      │
│  输出:{passed: bool, issues: [], 
│         next_action: "proceed|retry|ask"}│
└──────────────────┬──────────────────────┘
                   │
            通过 → 下一步 / 全部完成
            失败 → 重试 / 调整计划 / 反问用户

Planner Prompt 核心:

你是任务规划专家。分析用户需求,拆解为可执行的子任务。

用户需求:{user_query}
可用工具:{tool_list}
已知约束:{constraints}

拆解原则:
1. 每步只做一件事
2. 步骤间依赖关系明确
3. 每步有明确的预期输出
4. 预估每步耗时,总耗时不超过预算

输出 JSON:
{
  "steps": [
    {
      "id": 1,
      "action": "搜索XX信息",
      "tool": "search",
      "expected_output": "包含XX的信息列表",
      "depends_on": [],
      "estimated_time": "2s"
    }
  ],
  "total_steps": 3,
  "can_parallelize": [1, 2]  // 可并行的步骤ID
}

Validator Prompt 核心:

你是结果校验专家。检查执行结果是否满足要求。

用户原始需求:{user_query}
当前步骤:{step_description}
预期输出:{expected_output}
实际输出:{actual_output}

校验维度:
1. 完整性:预期输出的关键信息是否都包含
2. 准确性:信息是否正确(与已知事实对比)
3. 充分性:是否足够支撑下一步或最终回答

输出:
{
  "passed": true/false,
  "completeness": 0.0-1.0,
  "issues": ["缺失的关键信息"],
  "next_action": "proceed" | "retry" | "replan" | "ask_user",
  "suggestion": "如果retry/replan,给出建议"
}

闭环控制逻辑:

  • Validator 通过 → 执行下一步
  • Validator 失败 → 重试当前步骤(最多 2 次)
  • 重试仍失败 → 回到 Planner 重新规划
  • 重新规划后仍失败 → 反问用户

加分点: "三层 Prompt 的核心价值是'可审计性'——每一步都有明确的输入/输出/校验结果,Badcase 可以精确定位是'计划不合理'还是'执行出错'还是'校验漏了'。这比一锅端的 Prompt 可调试性好 10 倍。"


三、评测与迭代深化(4题) ​

D3-1. Agent 任务完成率的量化评估标准如何制定?针对不同类型的任务,评估指标有哪些差异? ​

分档:二面深度

高分答题思路:

任务完成率的定义框架:

任务类型完成标准关键指标评估方式
问答型回答包含所有关键信息点信息覆盖率、准确率LLM-as-Judge + 人工
工具调用型正确工具 + 正确参数 + 成功执行工具准确率、参数准确率、执行成功率自动化比对
创作型满足约束 + 质量达标约束满足率、质量评分(1-5)LLM-as-Judge + 人工
多轮对话型最终完成 + 过程连贯任务完成率、多轮一致性、平均轮数端到端评测

分类型评估指标体系:

通用指标(所有任务):
  - 任务完成率:最终回复是否满足用户需求
  - 安全合规率:无违规内容
  - 用户满意度:点赞/点踩/反馈

问答型特有:
  - 信息覆盖率:标注的关键信息点被覆盖的比例
  - 准确率:事实性信息的正确率
  - 幻觉率:编造不存在信息的比例

工具调用型特有:
  - 工具选择准确率:选对了工具
  - 参数准确率:参数值正确
  - 执行成功率:工具调用成功
  - 链路完成率:多步工具调用全部完成

创作型特有:
  - 约束满足率:满足用户指定的格式/长度/风格约束
  - 创意度:LLM-as-Judge 评分
  - 可用性:产出是否可直接使用

多轮对话型特有:
  - 多轮一致性:各轮回复无矛盾
  - 意图理解准确率:正确理解用户的最终意图
  - 平均交互轮数:完成任务的效率

评估数据集构建差异:

  • 问答型:标注标准答案 + 关键信息点列表
  • 工具调用型:标注期望工具序列 + 期望参数
  • 创作型:标注约束条件 + 质量评分标准
  • 多轮对话型:标注完整对话脚本 + 分支路径

加分点: "我们最初用统一指标评估所有任务,发现创作型任务的'完成率'虚高(因为创作没有标准答案)。后来按任务类型分轨道评估,各轨道有独立的指标体系和数据集,评估准确度显著提升。"


D3-2. Badcase 聚类分析的核心维度有哪些?如何通过聚类结果快速定位高频问题场景? ​

分档:二面深度

高分答题思路:

聚类维度矩阵:

维度聚类方式示例类别
错误类型按根因分类工具选错/参数错误/信息缺失/格式错误/幻觉
意图类别按用户意图分类搜索/分析/创作/闲聊
工具组合按涉及的工具分类单工具/多工具/无工具
复杂度按对话轮数分类单轮/2-3轮/4+轮
用户画像按用户类型分类新用户/老用户/专业用户

聚类分析流程:

Badcase 池(1000条)
    │
    ▼
[多维度标注]
每条 Badcase 标注:错误类型 + 意图 + 工具 + 轮数 + 用户类型
    │
    ▼
[Embedding 聚类]
对 Badcase 文本做 Embedding → K-Means/层次聚类
    │
    ▼
[维度交叉分析]
错误类型 × 意图 = "搜索场景下的工具选错"占 35%
错误类型 × 工具 = "多工具场景的参数错误"占 20%
    │
    ▼
[优先级排序]
按 [频次 × 影响] 排序,Top 3 问题优先修复

快速定位高频问题:

  • 帕累托分析:通常 20% 的错误类型贡献了 80% 的 Badcase
  • 交叉热力图:错误类型 × 意图 的热力图,颜色越深优先级越高
  • 趋势分析:某类 Badcase 突增 → 可能是某次发布引入的回归

聚类结果示例:

Top 1 (35%):搜索意图 → 工具选择错误(选了搜索工具但应该用数据库查询)
Top 2 (20%):多工具场景 → 参数缺失(LLM 没有从上下文提取到必要参数)
Top 3 (15%):多轮对话 → 上下文丢失(第4轮忘记了第1轮的约束)

加分点: "聚类最大的价值不是发现已知问题,而是发现'未知的高频问题'。我们通过聚类发现了一个意想不到的高频 Badcase:用户用反问句表达需求时,Agent 会误解为确认。这个问题的占比 8%,但人工抽检时很难发现,因为不会主动去看这类表达模式。"


D3-3. Prompt 调优中,如何建立版本管理机制,避免"越调越差",且能快速回滚? ​

分档:二面深度

高分答题思路:

Prompt 版本管理方案:

Prompt 版本树:
  v1.0 (baseline)
    ├─ v1.1 (调整 Few-shot)
    │   └─ v1.1.1 (增加负面示例)
    ├─ v1.2 (修改 System Prompt)
    │   └─ v1.2.1 (回滚:效果下降 3%)
    └─ v1.3 (重构输出格式)

版本管理机制:

要素方案
版本号语义化版本:major.minor.patch
变更记录每次变更记录:修改内容、修改原因、预期效果
评测绑定每个版本绑定一组评测结果(准确率/完成率/格式合规率)
灰度发布新版本先灰度 5% → 指标不降 → 扩到 20% → 50% → 100%
快速回滚保留最近 3 个稳定版本,一键回滚

防止"越调越差"的策略:

  1. A/B 对比

    • 新版本必须在评测集上跑完整指标
    • 与上一个稳定版本对比,所有指标不降才能发布
    • 如果某个指标降了但其他升了 → 需人工评估 trade-off
  2. 回归测试

    • 维护"黄金测试集":100 条之前版本表现好的 case
    • 新版本必须在黄金测试集上不回归(准确率不降)
  3. 变更最小化

    • 每次只改一个维度(要么改 System Prompt,要么改 Few-shot,要么改格式)
    • 避免"大改"导致无法归因效果变化
  4. 效果监控

    • 上线后实时监控核心指标
    • 指标下降 > 2% → 自动告警 → 评估是否回滚

Prompt 调优流程:

假设 → 修改 Prompt → 跑评测集 → 对比基线
  ├─ 全面提升 → 灰度发布 → 监控 → 全量
  ├─ 部分提升部分下降 → 评估 trade-off → 决策
  └─ 全面下降 → 回滚 + 分析原因 → 调整假设

加分点: "我们把 Prompt 版本管理做成了类似 Git 的系统——分支、合并、PR review、CI(自动评测)。每次 Prompt 变更都需要通过 CI(评测指标不降)才能合并。这把'Prompt 越调越差'的概率从 30% 降到 5% 以下。"


D3-4. 当 Agent 效果遇到瓶颈时,如何通过"Prompt 调优+模型微调+工具扩充"的组合策略实现突破? ​

分档:二面深度

高分答题思路:

瓶颈诊断框架:

Agent 效果瓶颈
    │
    ▼
[根因分析]
    ├─ Prompt 已经穷尽优化 → 考虑微调
    ├─ 缺少必要能力/数据 → 考虑工具扩充
    ├─ 模型理解能力不足 → 考虑换模型/微调
    └─ 检索素材不足 → 考虑知识库扩充

组合策略决策矩阵:

瓶颈类型组合策略预期效果周期
格式/风格不稳定Prompt 强化 + 少量 SFT格式合规率 85%→98%1-2 周
领域知识不足工具扩充(RAG)+ Prompt 引导知识覆盖率 60%→90%2-3 周
工具选择不准Prompt 优化(工具描述重写)+ SFT(工具选择)准确率 72%→92%3-4 周
多步推理弱SFT(Plan-Solve 推理链)+ Prompt(分步引导)完成率 65%→85%4-6 周
综合瓶颈Prompt + SFT + 工具 + RAG 全面优化整体提升 15-25%6-8 周

组合策略执行顺序(ROI 优先):

Phase 1 (Week 1-2): Prompt 调优
  - 重写工具描述、优化 Few-shot、改进输出格式
  - 预期提升:5-10%
  - 成本:最低

Phase 2 (Week 2-3): 工具/知识扩充
  - 新增缺失工具、扩充知识库
  - 预期提升:5-15%
  - 成本:中

Phase 3 (Week 3-6): 模型微调
  - 基于 Phase 1-2 的 Badcase 构建微调数据
  - SFT 解决 Prompt 无法解决的格式/风格/推理问题
  - 预期提升:5-10%
  - 成本:最高

Phase 4 (持续): 效果监控 + 迭代
  - 上线后持续监控,发现新瓶颈回到 Phase 1

关键原则:

  • 不要同时改多个维度:无法归因效果提升来自哪个改动
  • 每阶段都跑评测:确认本阶段的改动有正向收益
  • 微调是最后手段:前两阶段能解决的不用微调

数据佐证模板: "我们的 Agent 准确率卡在 82% 两个月。通过组合策略:Phase 1 Prompt 优化(+4%)→ Phase 2 工具扩充(+6%)→ Phase 3 针对性 SFT(+5%),最终到 97%。关键是每个阶段都先归因再行动,而不是同时全改。"


四、工程底层深化(4题) ​

D4-1. Python 协程实现 Agent 高并发调度,协程与线程结合使用的场景和注意事项? ​

分档:二面深度

高分答题思路:

纯协程方案(推荐默认):

python
async def agent_scheduler(tasks: list[Task]):
    semaphore = asyncio.Semaphore(100)  # 并发上限
    
    async def run_one(task):
        async with semaphore:
            return await agent.execute(task)
    
    results = await asyncio.gather(*[run_one(t) for t in tasks])
    return results
  • 优势:单进程内 1000+ 并发,切换开销极小
  • 适用:I/O 密集(LLM API 调用、网络请求)

协程 + 线程结合场景:

场景为什么需要线程实现方式
CPU 密集混合Embedding 计算/文本处理阻塞事件循环loop.run_in_executor(ThreadPoolExecutor, func)
同步 SDK 调用第三方 SDK 不支持 async包装在线程池中调用
阻塞文件 I/O大文件读写阻塞aiofiles 或线程池

注意事项:

  1. 线程安全:协程内访问共享变量不需要锁(单线程),但线程池中的代码需要加锁
  2. GIL 影响:线程池中的 CPU 密集任务受 GIL 限制,真正并行需要多进程
  3. 资源控制:线程池大小要设上限(max_workers=10),避免线程爆炸
  4. 异常传播:线程中的异常不会自动传播到协程,需要手动捕获
python
# 正确的协程+线程结合模式
executor = ThreadPoolExecutor(max_workers=10)

async def agent_task(query):
    # 协程部分:LLM 调用(I/O 密集)
    llm_response = await call_llm(query)
    
    # 线程部分:Embedding 计算(CPU 密集)
    embedding = await asyncio.get_event_loop().run_in_executor(
        executor, compute_embedding, llm_response
    )
    
    # 回到协程:结果存储(I/O 密集)
    await store_result(embedding)

加分点: "我们踩过一个坑——第三方 SDK 的同步调用直接放在协程里,导致整个事件循环阻塞 2 秒,所有并发请求都卡住。解决方案是用 run_in_executor 包装同步调用,或者找/写 async 版本的 SDK。"


D4-2. 代码插桩在 Agent 链路监控中的应用?如何通过插桩实现每步操作的可追溯? ​

分档:二面深度

高分答题思路:

插桩方案:

方式实现侵入性灵活性
装饰器插桩@trace("tool_call")低高
中间件插桩在 Agent 执行管线中插入监控层中中
AST 插桩自动在 AST 层面插入埋点代码无(自动)最高
手动埋点在代码中直接写 logger.info()高最高

装饰器插桩实现:

python
def trace(step_name: str):
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            trace_id = get_current_trace_id()
            step_id = f"{trace_id}_{step_name}_{uuid4().hex[:8]}"
            
            # 前置埋点
            logger.info({
                "trace_id": trace_id,
                "step_id": step_id,
                "step": step_name,
                "phase": "start",
                "input": summarize(args),
                "timestamp": time.time()
            })
            
            start = time.time()
            try:
                result = await func(*args, **kwargs)
                # 后置埋点
                logger.info({
                    "trace_id": trace_id,
                    "step_id": step_id,
                    "step": step_name,
                    "phase": "end",
                    "output": summarize(result),
                    "duration_ms": (time.time() - start) * 1000,
                    "status": "success"
                })
                return result
            except Exception as e:
                logger.error({
                    "trace_id": trace_id,
                    "step_id": step_id,
                    "step": step_name,
                    "phase": "error",
                    "error": str(e),
                    "duration_ms": (time.time() - start) * 1000,
                    "status": "failed"
                })
                raise
        return wrapper
    return decorator

# 使用
class SearchAgent:
    @trace("search_tool_call")
    async def search(self, query):
        return await search_api(query)
    
    @trace("search_result_process")
    async def process(self, results):
        return filter_and_rank(results)

可追溯的完整链路:

trace_id: abc123
  ├─ step: route, duration: 50ms, input: "用户query", output: "search_agent"
  ├─ step: intent, duration: 80ms, input: "用户query", output: "搜索意图"
  ├─ step: search_tool_call, duration: 500ms, input: "query", output: "10条结果"
  ├─ step: search_result_process, duration: 30ms, input: "10条", output: "3条精选"
  └─ step: llm_generate, duration: 1500ms, input: "3条+用户query", output: "最终回复"

trace_id 传播:

  • 使用 contextvars.ContextVar 在协程间传递 trace_id
  • 不需要显式传参,装饰器自动获取

加分点: "插桩不是越多越好——我们曾经每个函数都埋点,日志量爆炸(1GB/天),反而影响性能。后来精简为'关键节点埋点 + 采样详细埋点':关键节点全量埋点(路由/工具/LLM),细节埋点采样 10%。日志量降 70%,排查能力基本不变。"


D4-3. Transformer 位置编码的实现方式有哪些?各自优缺点?如何适配不同长度输入? ​

分档:二面深度

高分答题思路:

方案原理优点缺点代表模型
Sinusoidalsin/cos 固定编码无参数、可外推效果一般原始 Transformer
Learnable PE每个位置一个可学习向量效果好不能外推到训练最大长度之外GPT-2/BERT
ALiBiAttention 分数加距离偏置无需位置编码、天然外推相对位置表达力稍弱BLOOM
RoPE对 Q/K 做旋转编码相对位置外推性好、计算高效、效果好实现稍复杂LLaMA/GPT-4

RoPE 详解(当前主流):

  • 对 Q 和 K 向量按维度分组,每组做 2D 旋转
  • 旋转角度与位置成正比:θ_i = pos / 10000^(2i/d)
  • Q·K 的内积自然包含相对位置信息
  • 优势:不引入额外参数,支持外推(通过 NTK-aware scaling)

长度适配方案:

问题方案
训练 max_len=2048,推理需要 4096RoPE + NTK-aware scaling(调整基频)
Learnable PE 不能外推插值法(位置插值 PI)或换 RoPE
超长文本(32K+)RoPE + 动态 NTK + 滑动窗口注意力

加分点: "RoPE 的外推不是无限的——我们的实验显示,从 4K 训练的模型外推到 16K 时效果开始下降。解决方案是在推理时用 NTK-aware scaling 调整旋转基频,可以把有效长度扩展到 32K 而效果几乎无损。"


D4-4. Python 中 Lock 和 RLock 的使用场景?如何避免死锁? ​

分档:二面深度

高分答题思路:

类型特点使用场景
Lock不可重入,同一线程不能重复 acquire保护独立资源
RLock可重入,同一线程可重复 acquire(计数器)递归函数、嵌套调用

Lock vs RLock:

python
# Lock:同一线程重复 acquire 会死锁
lock = threading.Lock()
def func():
    with lock:
        with lock:  # 死锁!
            pass

# RLock:同一线程可重复 acquire
rlock = threading.RLock()
def func():
    with rlock:
        with rlock:  # 正常
            pass

Agent 场景中的使用:

  • 工具注册表:多线程注册/查询工具 → 用 Lock
  • 会话状态:多线程读写同一 session → 用 Lock
  • 缓存更新:双检锁模式 → 用 Lock

死锁的四个必要条件及破坏方式:

条件破坏方式
互斥无法破坏(资源本身就是互斥的)
持有并等待一次性获取所有锁
不可剥夺获取锁时设超时(lock.acquire(timeout=5))
循环等待按固定顺序获取锁

避免死锁的实践:

  1. 锁排序:所有代码按固定顺序获取锁
  2. 超时机制:lock.acquire(timeout=5),超时走异常处理
  3. 锁粒度最小化:只锁真正需要保护的代码段
  4. 避免嵌套锁:能用一把锁的不用两把
  5. context manager:用 with lock: 而非手动 acquire/release(异常安全)

加分点: "在 Agent 场景中,其实大部分并发问题用 asyncio 就能解决——协程是单线程的,不需要锁。只有在协程+线程混合场景(如线程池中访问共享资源)才需要锁。我们的原则是:能不用锁就不用,必须用就只锁最小范围。"


答题策略补充 ​

要点说明
深化题的特点每道题都是"基础题的延伸",答题时先简述基础概念再展开深化
场景代入尽量用"我们的实践中..."开头,把回答从"理论上可以"变成"我们这样做过"
数据精度数据不需要精确到小数点,但量级要合理(提升 5-20%、延迟降 30-60%)
边界讨论主动提到方案的限制和适用边界,展示全面思考