夜雨聆风学习资料网

ARTICLE · 1126039

技术面被问『会自己调工具的 AI Agent 怎么测』:三层框架,第三层最加分

技术面被问『会自己调工具的 AI Agent 怎么测』:三层框架,第三层最加分

关注 「软件测试就业联盟」公众号,陪你走好校招求职的每一步

面试官把屏幕转过来,浏览器里是个 Agent 演示页:一个输入框,背后挂着查库存、下单、发邮件三个工具。『它能自己查、自己下单、自己发通知,错一步就是真金白银。这东西,你怎么测?』你刚把背熟的三板斧立起来,对面第一问就砸了下来:『先别说方案——它到底调没调那个工具,调了几次,调错没有,你拿什么判断?』

这一问,直接把『怎么测聊天机器人』那套文本断法问穿了:聊天机器人的输出是一段字,错了顶多重说一遍;带工具的 Agent 输出的是一个动作,下单了就是真下单了。所以这道真题的答题骨架要换:第一层,输出层的新断法——不断字面回复,断不变量;第二层,工具调用层的老本行——把每个工具当被测边界,配可运行代码;第三层,追问预演——主动给回归设计,拉开差距的一层。

先分清被测对象:Agent 的输出是『动作』

多数应届生卡壳,是因为把『回答得对不对』直接搬了过来。你得先当着面试官把差异说破:对带工具的 Agent,真正要断言的是三件事——调用序列(该先查库存再下单的链路,不能反过来,更不能跳过查库存直接下单);越权边界(用户只是问一句『SKU-A 还有货吗』,一个写操作都不许被触发);失败降级(工具超时或报错时,安全退出并如实告知,而不是盲目重发、顺手多下一单)。

锚回你已有的知识:这招你其实早用过。接口测试里老手从来不断响应里的时间戳,断的是结构和关键字段;UI 流程测试里,页面文字随便改版都不算回归失败,真正要盯的是数据库状态有没有被写脏。不变量断言,就是把『断行为不断长相』这个/schema 思想搬进 Agent——字面回复可以天天换文案,三件事不能变。这一层说出来,面试官就知道你明白『它会动真实状态』。

第二层把每个工具当被测边界:动手造一个假工具层

工具调用层是测开基本功的密集展示区:参数校验、幂等、超时重试、并发下的重复提交——每条都是老课题的新战场。面试室现场没有真模型,也不需要:与其对着真订单网关许愿,不如当场把每个工具降级成一个带调用台账的假实现。下面两段代码,纯标准库加 pytest,两个文件存同一目录,终端跑 pytest -q 即全绿。

第一段是 mock 工具层:假库存/订单/邮件三个工具,每次调用都往 calls 台账里记一笔『谁、带什么参数、第几次』;订单工具带参数校验、幂等键、以及一个『注一次超时』的行为开关——这开关就是测试方的操纵杆,想让它超时它就超时。

# mock_tools.py —— 进程内 mock 工具层(演示代码,纯标准库,零外部依赖)
import zlib
import re

classMockToolService:
"""假库存/订单/邮件工具。关键设计:每次调用都记进 calls 台账,
    测 Agent 的第一证据不是它回了什么话,而是它动了哪些手。"""


def__init__(self):
        self.calls = []          # [(工具名, 参数dict)] —— 调用台账
        self.live_orders = []    # 真实生效的订单
        self._orders = {}        # 幂等键 -> 订单号
        self._fail_once = False# 行为注入开关:让下一次调用超时

def_log(self, name, **params):
        self.calls.append((name, params))

defquery_stock(self, sku):
        self._log("query_stock", sku=sku)
return {"sku": sku, "stock": 5}          # 演示数据

defcreate_order(self, sku, qty, idem_key=None):
        self._log("create_order", sku=sku, qty=qty, idem=idem_key)
if qty <= 0:
raise ValueError("参数校验:数量必须为正整数")
if self._fail_once:
            self._fail_once = False
raise TimeoutError("订单网关超时(演示注入)")
if idem_key and idem_key in self._orders:
return self._orders[idem_key]        # 幂等:同键永远返回同一单
        order_no = f"ORD-{len(self.live_orders) + 1:04d}"
        self.live_orders.append(order_no)
if idem_key:
            self._orders[idem_key] = order_no
return order_no

defsend_email(self, to, body):
        self._log("send_email", to=to)
returnTrue

defarm_timeout_once(self):
        self._fail_once = True

ORDER_RE = re.compile(r"(\d+)\s*件\s*(SKU-[A-Z]+)")

defrun_agent(tools, user_text):
"""被测的『决策端』:真实系统这里是大模型,演示版用规则替身。
    我们测的不是它聪不聪明,而是『决策』与『工具』之间的接口契约。"""

if any(w in user_text for w in ("删", "改价", "全额退款")):
return"该请求超出授权范围,已拒绝执行。"# 不变量二:不越权
    m = ORDER_RE.search(user_text)
if m and any(w in user_text for w in ("下单", "买", "来")):
        qty, sku = int(m.group(1)), m.group(2)
        stock = tools.query_stock(sku)                     # 不变量一:先查再下
if stock["stock"] < qty:
returnf"库存只有 {stock['stock']} 件,未为您下单。"
        idem_key = f"idem-{zlib.crc32(user_text.encode()):08x}"
try:
            order_no = tools.create_order(sku, qty, idem_key)
except (TimeoutError, ValueError):
return"下单未成功,已停止后续操作,请稍后重试。"# 不变量三:安全降级
if any(w in user_text for w in ("通知", "抄送", "邮件")):
            tools.send_email("user@demo.local", f"订单 {order_no} 已创建。")
returnf"已下单:{order_no}"
    m = re.search(r"查\s*(SKU-[A-Z]+)", user_text)
if m:
        tools.query_stock(m.group(1))
returnf"{m.group(1)} 当前库存 5 件。"
return"没听懂,请换个说法。"

第二段是 pytest:所有断言只盯台账 calls 和生效订单 live_orders,七组用例演示三层意思——不该调的没调、该调的序列和参数正确、重放与超时重试不重复下单。

# test_agent_tools.py —— 对着调用台账断言(本地即跑,不碰任何真实 API)
import pytest
from mock_tools import MockToolService, run_agent

@pytest.fixture
deftools():
return MockToolService()

defnames_of(tools):
return [c[0] for c in tools.calls]

deftest_纯查询不许碰任何写工具(tools):# 越权不变量
    run_agent(tools, "查 SKU-A 库存")
assert names_of(tools) == ["query_stock"]

deftest_该调的调用且序列参数都对(tools):# 序列 + 参数契约
    run_agent(tools, "帮我下单 2 件 SKU-A,并邮件通知我")
assert names_of(tools) == ["query_stock", "create_order", "send_email"]
    _, order = tools.calls[1]
assert order["sku"] == "SKU-A"and order["qty"] == 2
assert order["idem"], "写操作必须携带幂等键"

deftest_同一条指令重放两次只算一单(tools):# 用户双击/网络抖动重发
    r1 = run_agent(tools, "帮我下单 2 件 SKU-A")
    r2 = run_agent(tools, "帮我下单 2 件 SKU-A")
assert r1 == r2 and len(tools.live_orders) == 1

deftest_超时后带键重试不重复下单(tools):# 降级 + 幂等
    tools.arm_timeout_once()
assert"未成功"in run_agent(tools, "帮我下单 1 件 SKU-B")
    run_agent(tools, "帮我下单 1 件 SKU-B")             # 上层按同一意图重试
assert len(tools.live_orders) == 1

deftest_反例不带幂等键的重试会下成两单(tools):# 踩坑演示
    tools.create_order("SKU-C", 1)
    tools.create_order("SKU-C", 1)
assert len(tools.live_orders) == 2

deftest_数量为0的脏参数必须被工具层挡住(tools):# 参数校验兜底
with pytest.raises(ValueError):
        tools.create_order("SKU-D", 0)

deftest_越权指令不产生任何调用(tools):
    r = run_agent(tools, "把 SKU-A 删掉")
assert tools.calls == [] and"拒绝"in r

为什么这么写、踩了什么坑:第一版我把断言写在返回文本上——assert "已下单" in r,看起来全绿,实际全瞎:超时注入后文本照样客气,台账里却可能已经动了手。文本断言对『动作』天然失明,这就是测 Agent 必须断调用台账的原因,这段弯路值得原样讲给面试官。第二个坑在反例那组:我一度觉得幂等键是后端的事,把重试策略改成『失败就立刻重发』后它当场下成两单——工具超时不等于操作失败,请求可能已经生效,所以 Agent 的重试策略必须和工具的幂等契约绑在一起设计,这句话说出来,就是你和『只会背重试三次』候选人的分界线。序列断言则顺手逮住一个隐蔽 bug 形态:先发邮件后下单、甚至邮件发了单没下——用户视角是『被耍了』,台账视角一目了然。还能留钩子:并发同时重放、台账要不要记模型版本号——面试时主动抛出这两面,追问就会朝你有准备的方向走。

第三层,把追问提前到自己头上

前两层回答『这一次怎么测』,第三层要你自己往前递话:『如果这个 Agent 归我负责,我会固定一组锚点场景——纯查询、正常下单、下单加通知、库存不足、越权指令、模糊指令各留几条,每条记下期望的调用序列。以后不管换模型版本还是改提示词,全量跑一遍做 diff:序列漂了(该查了却直接下单)、多调了(不该发的通知邮件发了)、台账对不上了,我都有数据。』

这一句把答题从『我会测』升级成『我让下次的我还测得动』。锚回旧知识:这就是发版必跑的冒烟集,只是断言对象从状态码换成了调用序列;难点从来不在题量,在每次发版雷打不动地跑。测试工程师的日常工作就是回归,应届生肯把一次性探索说成可持续资产,比背五种测试方法值钱得多。

三层对照:各层展示什么,翻车答法长什么样

答题层
展示的能力信号
常见翻车答法
第一层 · 不变量断言
懂 Agent 的输出是动作、会改真实状态,会画『什么算过』
把测聊天机器人那套原样搬来:『输入问题看回答对不对』,第一问即碎
第二层 · 把每个工具当被测边界
测开基本功密集区:参数校验/幂等/超时重试/并发重放照用不误
只谈『决策聪不聪明』,说不清拿什么证据证明它调没调、调几次
第三层 · 锚点场景 + 序列 diff
回归意识:把一次测试做成可持续资产
等着被追问不发声;或空喊『多跑几次取统计』给不出抓手

这张表补一句防误读:三层不是平分秋色。第二层是护城河——工具契约是老测试工程的看家本领,能落地成代码的候选人凤毛麟角;但真正拉开档次的是第三层,因为绝大多数应届生根本想不到主动谈回归。只把第二层讲透、讲出上面那段代码的细节,也远胜三层各滑一遍。技术面打分看落点,不看名词表。

面试周之前:别背稿,跑通最小闭环

这类题随着校招技术面全面 AI 化会越来越多——2027 校招各大厂批次已在推进(具体批次与考法以各家官方校招页为准)。与其把三层框架当台词多背一遍,不如花半小时做一次最小闭环:两个文件跑全绿;然后动手做三次变异实验——把 run_agent 改成先 send_email 再 create_order,看序列断言怎么红;把幂等键去掉,看重放断言怎么红;把库存不足的分支删掉,越权和降级谁先抓住。做完这三次『故意弄坏再修绿』,你在面试室讲的就是亲手验过的经验,而不是台词。练完把两个文件存进自己的练习仓库,隔两周重跑,你连『回归怎么落地』的第二现场都有了。

这一套『出题—拆题—动手—复盘』的完整练习,霍格沃兹测开应届生训练营在『AI Agent 测试面试练习』方向备有一份材料:这类真题的拆题示范、调用台账断言的动手实验、追问链的复盘模板,都是能反复过轮的料——想省去找题排题的功夫,可以把它当成模拟面的题库用。

一句话记住这道题:测聊天机器人,断的是它『说了什么』;测会调工具的 Agent,断的是它『做了什么』——把动作变成可回放的台账,再把台账变成下次发版还敢跑的回归,这就是应届生能给出的最贵的答案。

七组断言你跑绿了几组?把幂等键拿掉看看哪组先红、又是怎么修绿的,欢迎回来交作业——想要成套的 Agent 拆题练习材料,也可以回后台关键词『Agent测试』拿霍格沃兹面试练习材料方向。

想及时获取2027届秋招第一手招聘消息、岗位汇总和内推信息,可以扫码加入秋招交流群。

关于我们

霍格沃兹测试开发学社,隶属于 测吧(北京)科技有限公司,是一个面向软件测试爱好者的技术交流社区。

学社围绕现代软件测试工程体系展开,内容涵盖软件测试入门、自动化测试、性能测试、接口测试、测试开发、全栈测试,以及人工智能测试与 AI 在测试工程中的应用实践。

我们关注测试工程能力的系统化建设,包括 Python 自动化测试、Java 自动化测试、Web 与 App 自动化、持续集成与质量体系建设,同时探索 AI 驱动的测试设计、用例生成、自动化执行与质量分析方法,沉淀可复用、可落地的测试开发工程经验。

在技术社区与工程实践之外,学社还参与测试工程人才培养体系建设,面向高校提供测试实训平台与实践支持,组织开展 “火焰杯” 软件测试相关技术赛事,并探索以能力为导向的人才培养模式,包括高校学员先学习、就业后付款的实践路径。

同时,学社结合真实行业需求,为在职测试工程师与高潜学员提供名企大厂 1v1 私教服务,用于个性化能力提升与工程实践指导。

相关学习资料