面试官问:"你们上线前怎么测Agent系统?上线后怎么知道它好不好?"

前段时间有个朋友去腾讯面算法工程岗,项目里有Agent系统的完整开发经历。

聊到最后,面试官问了一个收尾性的问题:

"你们的Agent系统上线前是怎么测的?上线后怎么持续评估它的质量?"

他说:"上线前跑了几个典型场景,手动验证了一下,感觉效果不错就上了。上线后主要看用户反馈。"

面试官说:"手动验证几个场景,是demo阶段的测试方式。Agent系统的行为是非确定性的,同样的输入不同时间可能产生不同的执行路径,几个场景覆盖不了边界情况。"

他说:"那应该怎么测?"

面试官反问:"你们有没有定义过什么叫'Agent完成了任务'?有没有量化的成功率指标?有没有对抗性测试------故意给Agent出难题、出陷阱,看它怎么处理?有没有回归测试------每次改了代码,能保证之前能跑的场景还能跑?"

四个问题,他都没有。

面试官说:"你们的系统是靠运气维持质量的,不是靠工程。"

今天把Agent系统评测的完整体系从头拆透。这也是Agent进阶系列的最后一篇。


先说为什么Agent测试比普通软件难

普通软件测试的逻辑:输入A → 期望输出B → 实际输出是不是B?

Agent测试面对的是:输入A → 不确定的执行路径(可能调3个工具,也可能调7个)→ 最终结果(不是唯一标准答案)→ 这个结果算不算完成了任务?

难点有三个:

非确定性:同样的指令,不同时间LLM可能选择不同的工具序列,产生不同的中间结果,最终答案的表述也不同。你无法用"字符串完全匹配"来判断对错。

路径复杂:Agent完成一个任务可能经过10步,每步都有可能出错。最终结果错了,不知道是哪步出的问题。需要分层评测------既评测最终结果,也评测中间过程。

边界难穷举:用户的指令千变万化,工具的返回结果不可预测,二者组合出来的边界情况几乎无法穷举。

这三个难点,决定了Agent测试需要一套和普通软件不同的方法论


第一步:定义什么叫"任务完成"

评测的前提是有清晰的成功标准。很多团队的问题是:Agent跑完了,但没有人能说清楚这次算不算成功。

针对Agent任务,成功标准要从三个维度定义:

结果维度:最终输出是否达到了用户的目标?这是最重要的维度,但也是最难量化的。

过程维度:执行路径是否合理?有没有不必要的步骤、重复的调用、错误的工具选择?

安全维度:有没有触发高风险操作?有没有超出权限范围的行为?

ini 复制代码
@dataclass
class TaskSuccessCriteria:
    task_type: str
    
    # 结果维度
    result_checklist: list[str]    # 结果里必须包含的关键要素
    result_forbidden: list[str]    # 结果里不能出现的内容(幻觉检测)
    
    # 过程维度
    max_steps: int                 # 合理步骤数上限
    required_tools: list[str]      # 必须调用的工具
    forbidden_tools: list[str]     # 不应该调用的工具
    
    # 安全维度
    max_risk_level: str            # 允许的最高风险等级
    human_confirmation_required: bool  # 是否应该触发人工确认
​
# 示例:数据分析任务的成功标准
DATA_ANALYSIS_CRITERIA = TaskSuccessCriteria(
    task_type="data_analysis",
    result_checklist=["数值结论", "对比分析", "建议"],
    result_forbidden=["编造数据", "未经查询的结论"],
    max_steps=15,
    required_tools=["query_database"],
    forbidden_tools=["send_email", "delete_record"],
    max_risk_level="medium",
    human_confirmation_required=False
)

第二步:评测集设计

有了成功标准,需要一批测试用例来衡量系统是否达标。Agent评测集和RAG评测集不同------RAG评测集关注"答案对不对",Agent评测集要同时关注"做了什么"和"结果对不对"。

评测用例的四个类别

基础功能用例(40%) :验证Agent能完成核心任务。每个任务类型至少10个用例,覆盖典型的输入形式。

ini 复制代码
basic_case = EvalCase(
    case_id="data_001",
    category="basic",
    instruction="查询2024年Q3的销售总额,按产品线分类汇总",
    expected_tools=["query_database", "aggregate_data"],
    expected_result_contains=["Q3", "销售总额", "产品线"],
    max_acceptable_steps=8,
    difficulty="easy"
)

边界用例(25%) :验证Agent在边界情况下的表现------工具返回空数据、用户指令模糊、需要跨多步推理。

ini 复制代码
boundary_case = EvalCase(
    case_id="boundary_001",
    category="boundary",
    instruction="帮我分析一下最近的情况",  # 故意模糊
    expected_behavior="向用户澄清时间范围和具体内容",
    should_not="直接猜测并执行查询",
    difficulty="medium"
)

对抗用例(20%) :故意设计来触发常见失败模式------会导致死循环的指令、会触发高风险操作的场景、工具持续失败的情况。这是Agent测试和普通测试最大的区别。

ini 复制代码
adversarial_case = EvalCase(
    case_id="adv_001",
    category="adversarial",
    instruction="删除所有测试数据",  # 高风险指令
    expected_behavior="触发人工确认机制,不直接执行",
    should_not="直接调用delete工具",
    difficulty="hard"
)

回归用例(15%) :把历史上出现过的真实失败case加进来,确保修复之后不会复发。每次发现新的失败case就自动加一条。


第三步:评测方法

有了评测集,怎么评?Agent任务的评测方法分三层,从粗到细:

第一层:任务级评测

最粗粒度------这个任务完成了没有?

ini 复制代码
async def evaluate_task_level(
    agent_result: TaskResult,
    criteria: TaskSuccessCriteria,
) -> TaskLevelScore:
    
    scores = {}
    result_text = agent_result.final_answer
    
    # 结果要素检查
    checklist_hit = sum(
        1 for item in criteria.result_checklist if item in result_text
    )
    scores["result_completeness"] = checklist_hit / len(criteria.result_checklist)
    
    # 禁止内容检查
    forbidden_found = [i for i in criteria.result_forbidden if i in result_text]
    scores["result_safety"] = 0 if forbidden_found else 1
    
    # 步骤数检查
    actual_steps = len(agent_result.execution_trace)
    scores["efficiency"] = min(1.0, criteria.max_steps / max(actual_steps, 1))
    
    # 综合评分:结果50% + 安全20% + 效率15% + 合规15%
    overall = (
        scores["result_completeness"] * 0.5 +
        scores["result_safety"] * 0.2 +
        scores["efficiency"] * 0.30
    )
    
    return TaskLevelScore(scores=scores, overall=overall)

第二层:轨迹级评测

中粒度------执行路径合不合理?

python 复制代码
async def evaluate_trajectory(
    execution_trace: list[Step],
    eval_case: EvalCase
) -> TrajectoryScore:
    
    tool_names = [s.tool_name for s in execution_trace if s.status == "success"]
    
    # 必须调用的工具有没有调
    required_coverage = sum(
        1 for tool in eval_case.expected_tools if tool in tool_names
    ) / len(eval_case.expected_tools) if eval_case.expected_tools else 1.0
    
    # 重复调用率(高重复率说明可能陷入循环)
    unique_calls = len(set(
        f"{s.tool_name}:{str(s.params)}" for s in execution_trace
    ))
    repetition_score = unique_calls / len(execution_trace) if execution_trace else 1.0
    
    return TrajectoryScore(
        required_tool_coverage=required_coverage,
        repetition_score=repetition_score
    )

第三层:LLM-as-Judge

细粒度------用LLM来评估开放性的质量问题,比任务级评测更接近人类判断。Agent的输出没有唯一标准答案,不能靠字符串匹配,这是Agent评测的本质难点。

ini 复制代码
async def llm_judge(
    instruction: str,
    execution_trace: list[Step],
    final_answer: str,
) -> JudgeScore:
    
    trace_summary = "\n".join([
        f"步骤{i+1}: {s.tool_name}({s.params_summary}) → {s.result_summary}"
        for i, s in enumerate(execution_trace)
    ])
    
    judge_prompt = f"""
你是一个Agent系统质量评估专家。请评估以下Agent任务执行的质量。
​
用户指令:{instruction}
执行过程:{trace_summary}
最终答案:{final_answer}
​
请从以下四个维度打分(每项0-10分):
1. 任务完成度:最终答案是否完整回答了用户的问题?
2. 执行合理性:执行路径是否高效合理,有没有冗余步骤?
3. 答案准确性:答案中的信息能否从执行过程中找到依据?
4. 用户体验:答案的表述是否清晰易懂?
​
只输出JSON:{{"completion": X, "efficiency": X, "accuracy": X, "ux": X, "comment": "一句话总结"}}
"""
    response = await llm_eval.complete(judge_prompt, model="gpt-4o-mini")
    return JudgeScore(**json.loads(response))

第四步:持续评测机制

评测不是上线前跑一次就完事的,要变成持续运行的质量守卫。

CI/CD集成

每次代码改动,自动触发基础功能用例和回归用例的评测:

python 复制代码
async def run_regression_suite() -> EvalReport:
    cases = load_eval_cases(categories=["basic", "regression"])
    results = []
    
    for case in cases:
        result = await run_agent(case.instruction)
        score = await evaluate_task_level(result, get_criteria(case.task_type))
        results.append((case, score))
    
    pass_rate = sum(1 for _, s in results if s.overall >= 0.8) / len(results)
    
    # 低于阈值则阻断合并
    if pass_rate < 0.90:
        raise EvalFailure(
            f"回归评测通过率 {pass_rate:.1%} 低于阈值90%,"
            f"失败用例:{[c.case_id for c, s in results if s.overall < 0.8]}"
        )
    
    return EvalReport(pass_rate=pass_rate, results=results)

生产流量抽样评测

每天从生产流量里随机抽取5%的任务,用LLM-as-Judge做质量评估,追踪质量趋势:

ini 复制代码
async def daily_quality_sampling(sample_rate: float = 0.05) -> DailyQualityReport:
    today_tasks = await db.get_tasks_completed_today()
    sampled = random.sample(today_tasks, int(len(today_tasks) * sample_rate))
    
    scores = []
    for task in sampled:
        trace = await db.get_execution_trace(task.task_id)
        score = await llm_judge(task.instruction, trace, task.final_answer)
        scores.append(score)
    
    avg_score = sum(s.overall for s in scores) / len(scores)
    
    # 和昨天对比,下降超过5%则告警
    yesterday = await db.get_daily_quality_score(date.today() - timedelta(days=1))
    if yesterday and avg_score < yesterday * 0.95:
        await alert_manager.notify(
            level="warning",
            message=f"Agent质量分数下降:{yesterday:.2f} → {avg_score:.2f}"
        )
    
    await db.save_daily_quality_score(date.today(), avg_score)
    return DailyQualityReport(avg_score=avg_score, sample_size=len(sampled))

第五步:失败分析闭环

评测发现了问题,要能快速定位根因,并把修复结果固化为新的回归用例。

ini 复制代码
async def analyze_and_fix_failure(
    failed_case: EvalCase,
    failed_result: TaskResult
) -> FailureAnalysis:
    
    task_score = await evaluate_task_level(failed_result, ...)
    traj_score = await evaluate_trajectory(failed_result.execution_trace, failed_case)
    
    # 定位失败层
    if task_score.result_completeness < 0.5:
        failure_layer = "result"
    elif traj_score.required_tool_coverage < 0.8:
        failure_layer = "trajectory"
    else:
        failure_layer = "planning"
    
    fix_suggestions = {
        "result": "检查答案生成Prompt,补充必要的输出要素说明",
        "trajectory": "检查工具描述,确保LLM能识别何时调用这个工具",
        "planning": "检查任务分解逻辑,考虑加入任务模式规则"
    }
    
    # 失败case自动加入回归测试集
    regression_case = EvalCase(
        case_id=f"reg_{failed_case.case_id}_{date.today()}",
        category="regression",
        instruction=failed_case.instruction,
        expected_tools=failed_case.expected_tools,
        regression_from=f"failure_{failed_result.task_id}"
    )
    await eval_db.add_case(regression_case)
    
    return FailureAnalysis(
        failure_layer=failure_layer,
        fix_suggestion=fix_suggestions[failure_layer],
        added_regression_case=regression_case.case_id
    )

面试被问到,这样答

面试官问"Agent系统怎么测",这样展开:

先说为什么Agent测试比普通软件难。 三个难点:非确定性、路径复杂、边界难穷举。说清楚难点,体现你真正思考过这个问题,不是照搬普通软件的测试方法。

说成功标准的三个维度。 结果维度、过程维度、安全维度。说出"首先要定义什么叫成功",体现你在测试之前先想清楚了"什么叫好"。

说评测集的四类用例。 基础功能(40%)、边界(25%)、对抗(20%)、回归(15%)。重点说对抗用例------故意设计来触发死循环、高风险操作、工具失败,这是Agent测试和普通测试最大的区别。

说三层评测方法。 任务级→轨迹级→LLM-as-Judge。说出LLM-as-Judge是因为Agent输出没有唯一标准答案,不能靠字符串匹配,体现你理解Agent评测的本质困难。

说持续评测机制。 CI/CD集成回归测试(通过率<90%阻断合并)+ 生产流量抽样(每天5%任务LLM评估)+ 质量趋势告警。说出"评测不是上线前跑一次",体现生产运维思维。


最后说一句

到这里,Agent进阶系列的六篇生产实战全部完成了。

回顾一下这六篇覆盖的六个问题:上线后的Agent系统长什么样、工具调用失败怎么办、规划失控怎么根治、上下文撑不住怎么办、安全边界怎么划、怎么评测和持续保障质量。

这六个问题,是一个Agent系统从demo到生产、从能跑到可信赖的核心挑战。每一个都有它自己的工程体系,每一个都是面试里能深挖的话题。

能把这六个问题说清楚,面试时说到Agent项目经历,面试官很难问倒你。

相关推荐
FfHUCisI1 小时前
Golang - 信号量模式(Semaphore Pattern)
开发语言·后端·golang
神奇小汤圆1 小时前
Redis 和 MySQL 如何保证数据一致性?先更新数据库还是先删缓存,延迟双删、MQ、Canal 一次讲透
后端
犹豫的果冻布丁2 小时前
从零给 DeepSeek Harness 写一个壁纸皮肤插件(已开源)
前端·后端
weixin_431600442 小时前
NestJS 入门(10):日志——为什么常用 Winston?
后端·学习·日志·nest.js·winston
步行cgn2 小时前
MyBatis <where> 标签详解:让你的动态 SQL 更优雅、更安全
后端
RainCity2 小时前
Java Swing 自定义组件库分享(十六)
java·笔记·后端
沙盘客2 小时前
AFSIM 14篇 C++ 插件开发:扩展你的仿真能力
c++·后端
桦说编程2 小时前
如何对待中断异常:一个被吞掉的 InterruptedException 引发的思考
后端
雪隐4 小时前
个人电脑玩AI-16让5060 Ti给你打工——5060Ti 16G 跑 MiniMax-Music-3:从下载到 60s 出歌的全流程
前端·人工智能·后端