1. 从游戏AI到测试Agent:一个反直觉的起点
第一次看到"用打DOOM的思路做自动化测试"这个说法,我脑子里蹦出来的第一个念头是:这俩东西八竿子打不着吧?一个是1993年的第一人称射击游戏,一个是软件工程里最枯燥的环节之一。但仔细琢磨之后我发现,这个类比背后藏着一套非常扎实的决策模型设计思路,而且它解决的是自动化测试领域一个长期被忽视的核心问题:测试Agent在不确定环境下的决策能力。
传统自动化测试脚本是什么?本质上就是一条一条写死的指令序列——点这个按钮,等那个元素出现,输入这段文本,断言结果等于预期值。这种模式在稳定环境下跑得挺好,但一旦页面结构变了、接口返回慢了、弹窗顺序不一样了,脚本立刻挂掉。你可能会说,那就加等待、加重试、加异常捕获呗。没错,这些手段确实能缓解问题,但它们本质上是在用"更多的硬编码"去对抗"环境的不确定性",治标不治本。
Jev这个项目标题里提到的"AI Agent决策模型",核心思路完全不同。它不是在脚本层面打补丁,而是把测试执行过程建模成一个序贯决策问题——Agent在每一步观察当前环境状态,然后从可行动作空间中选择一个最优动作,执行后环境状态发生变化,Agent再根据新的状态做下一步决策。这个循环,和DOOM里AI敌人"看到玩家→选择行为→执行→观察结果→再决策"的循环,在数学结构上是一模一样的。
这篇文章我会从决策模型的设计原理讲起,拆解Jev这类测试Agent的核心架构,然后给出可落地的实现方案和实操中踩过的坑。不管你是做自动化测试的工程师,还是对AI Agent决策机制感兴趣的技术人,应该都能从中拿到一些能直接用的东西。
2. 为什么测试脚本需要"DOOM式"的决策模型
2.1 传统测试脚本的根本瓶颈在哪里
先把这个事情说透。大部分自动化测试框架(不管是Web端的还是移动端的)遵循的都是"录制-回放"或者"手写步骤"的模式。这种模式有一个隐含假设:环境是可预测的。脚本作者在写脚本的时候,脑子里已经预设了一条固定的执行路径,脚本就是这条路径的忠实记录。
问题在于,真实环境从来都不是完全可预测的。我做过一个统计,在一个中等复杂度的Web应用里,一个包含50个步骤的端到端测试脚本,在不加任何容错处理的情况下,连续跑10次的通过率大概只有60%到70%。失败的原因五花八门:元素加载顺序变了、异步请求返回时间波动、第三方组件初始化延迟、浏览器渲染差异等等。
传统的应对方式是"防御性编程"——到处加显式等待、加try-catch、加重试逻辑。这么做不是不行,但代码会变得极其臃肿,而且每加一层防御,就多一层维护成本。更关键的是,这种模式下的测试脚本没有真正的"决策"能力。它不会根据当前页面的实际状态来判断"我应该先处理弹窗还是先填表单",它只会按照预设顺序执行,遇到意外就报错。
2.2 DOOM AI给了什么启发
DOOM里的AI敌人(比如Imp和Demon)行为逻辑其实很简单,但它体现了一个重要的设计模式:基于状态的行为选择。AI不会预先编排好"先走三步再左转再攻击"这样的固定序列,而是每帧根据当前状态(与玩家的距离、自身血量、视线是否被遮挡等)从一组候选行为中选择一个执行。
这个模式套到测试Agent上,对应的设计就是:Agent不预先编排固定的操作序列,而是每一步根据当前页面状态(DOM结构、可见元素、网络请求状态等)从候选动作空间中选择最合适的动作。这样做的好处是,当环境发生变化时,Agent不需要修改脚本逻辑,它会自动"看到"新的状态并做出适应性的决策。
举个具体的例子。假设你要测试一个登录流程,传统脚本写的是"找到用户名输入框→输入→找到密码框→输入→点击登录按钮"。但如果页面上突然弹出了一个Cookie同意横幅,传统脚本会直接失败,因为它找不到用户名输入框(被横幅挡住了)。而一个基于决策模型的Agent会先"看到"横幅的存在,判断"当前最优先的动作是关闭横幅",执行关闭操作后再继续登录流程。
2.3 决策模型的核心要素拆解
要把这个思路落地,需要明确定义几个核心要素。我用一个表格来梳理:
| 要素 | 在DOOM AI中的对应 | 在测试Agent中的对应 |
|---|---|---|
| 状态空间 | 玩家位置、血量、武器状态 | DOM树、可见元素集合、网络状态 |
| 动作空间 | 移动、攻击、躲避 | 点击、输入、滚动、等待、断言 |
| 状态转移 | 游戏引擎物理模拟 | 页面响应操作后的新状态 |
| 奖励函数 | 击杀玩家、存活时间 | 测试步骤完成度、发现缺陷数 |
| 策略 | 行为选择规则 | 动作选择策略(规则/学习) |
这个映射关系看起来简单,但真正落地的时候,每个要素都有大量的工程细节需要处理。状态空间怎么表示?动作空间怎么定义粒度?奖励函数怎么设计才能引导Agent走向"完成测试"而不是"乱点一气"?这些问题的答案直接决定了Agent的实际效果。
3. Jev决策模型的状态表示与动作空间设计
3.1 页面状态怎么"喂"给Agent
状态表示是整个决策模型的地基。如果状态表示得不好,后面的策略再精妙也没用。在测试Agent的场景下,页面状态需要被编码成一种Agent能理解、能比较、能泛化的形式。
最直接的方式是用DOM树的原始HTML作为状态表示。但这样做有几个问题:第一,HTML文本太长,一个中等页面的DOM序列化后动辄几十万字符,直接作为状态输入计算量太大;第二,HTML里包含大量与决策无关的信息(比如样式定义、注释、隐藏元素),会引入噪声;第三,不同页面的HTML结构差异很大,Agent很难泛化。
我在实践中总结出一套分层状态表示方案,效果比较稳定:
第一层:可见元素摘要。只提取当前视口内可见的交互元素(按钮、输入框、链接、下拉框等),每个元素用一组特征向量表示:元素类型、文本内容、位置坐标、尺寸、是否可点击、是否可输入。这一层的信息量大概在几百到几千个token的量级,适合作为Agent的主要状态输入。
第二层:页面语义标签。对页面进行语义级别的理解,比如"这是一个登录表单"、"这是一个导航栏"、"这是一个模态弹窗"。这一层可以通过规则匹配或者轻量级模型来实现,目的是给Agent提供更高层次的上下文信息。
第三层:历史动作序列。Agent之前执行了哪些动作、产生了什么结果,这些信息对于决策非常重要。比如Agent已经点击了登录按钮但页面没有跳转,那它下一步就不应该再点一次登录按钮,而应该检查是否有错误提示。
这三层信息组合起来,形成一个结构化的状态表示。在实际实现中,我通常会把第一层和第二层编码成文本描述,第三层用一个固定长度的向量表示,然后一起输入给决策模块。
3.2 动作空间的粒度控制
动作空间的设计有一个核心矛盾:粒度太粗,Agent的灵活性不够;粒度太细,决策空间爆炸,Agent很难学到有效策略。
举个例子,"点击登录按钮"这个动作,如果粒度粗到"完成登录"这个级别,那Agent就失去了处理中间异常的能力(比如弹窗拦截)。如果粒度细到"移动鼠标到坐标(x,y)→按下左键→松开左键",那动作空间会变得巨大,而且大部分动作序列是无效的。
我的经验是采用两级动作空间:
- 高层动作:语义级别的操作,比如"点击元素A"、"在元素B中输入文本C"、"滚动到元素D"、"等待X秒"、"断言元素E存在"。这些是Agent实际决策的单位。
- 底层执行:每个高层动作对应一组底层操作序列,由执行引擎负责展开。比如"点击元素A"会被展开为"滚动到元素A可见→获取元素A坐标→模拟鼠标移动到坐标→模拟点击"。
这样做的好处是,Agent只需要在高层动作空间做决策,动作数量可控(通常几十到几百个),同时底层执行的复杂性被封装起来了。
还有一个细节值得注意:动作空间应该是动态的。也就是说,Agent在当前状态下可用的动作集合,应该根据当前页面状态动态生成。比如当前页面没有输入框,那"在输入框中输入文本"这个动作就不应该出现在候选集合里。这样做可以大幅缩小搜索空间,提高决策效率。
3.3 状态转移的建模与处理
状态转移是决策模型里最容易被忽视的环节。在DOOM里,状态转移由游戏引擎的物理模拟负责,是确定性的、即时的。但在测试Agent的场景下,状态转移有三个显著特点:
异步性。点击一个按钮后,页面可能不会立即变化,而是等待一个异步请求返回后才更新。Agent需要知道"等待"这个动作的存在,并且能够判断等待多久是合理的。
不确定性。同样的操作在不同环境下可能产生不同的结果。比如点击提交按钮,有时候成功跳转,有时候弹出验证码,有时候报网络错误。Agent需要能够处理这种不确定性。
部分可观测。Agent只能看到当前视口内的内容,页面下方的内容需要滚动才能看到。这意味着Agent的状态观测是不完整的,它需要主动探索(滚动页面)来获取更多信息。
针对这些特点,我在实现中采用了几个策略。对于异步性,引入一个"状态稳定检测"机制——每次动作执行后,持续监测DOM变化,直到连续N毫秒没有显著变化才认为状态转移完成。对于不确定性,在决策模块中加入随机探索机制,避免Agent陷入固定模式。对于部分可观测,在状态表示中加入"已探索区域"的标记,引导Agent优先探索未查看的区域。
4. 策略设计:规则引擎、学习模型还是混合方案
4.1 纯规则方案的适用边界
说到Agent决策,很多人第一反应是上强化学习。但我在实际项目中的体会是:在测试Agent这个场景下,纯规则方案能解决70%以上的问题,而且开发成本低、可解释性强、调试方便。
规则方案的核心是一组优先级排序的条件-动作规则。比如:
- 如果存在模态弹窗且弹窗有关闭按钮,优先关闭弹窗
- 如果存在表单且表单有未填写的必填字段,优先填写字段
- 如果存在错误提示元素,记录错误信息并尝试恢复
- 如果当前测试步骤的预期元素已出现,执行该步骤的断言
这些规则的优先级顺序很重要。弹窗处理必须排在最高优先级,因为弹窗会遮挡其他元素,不处理弹窗后面的操作都无法进行。错误处理排在第二优先级,因为出现错误时需要先恢复环境再继续。正常的测试步骤执行排在最后。
规则方案的优点是显而易见的:每一条规则都可以单独测试、单独调试,出问题的时候很容易定位是哪条规则触发了。而且规则可以用自然语言描述,测试人员不需要懂机器学习就能理解和修改。
但规则方案也有明显的边界。当页面结构非常复杂、状态组合爆炸的时候,手写规则会变得不可维护。比如一个有几十种弹窗类型、每种弹窗有不同的关闭方式的页面,规则数量会急剧膨胀。这时候就需要引入学习模型。
4.2 学习模型能补足什么
学习模型(这里主要指强化学习)在测试Agent场景下的价值主要体现在两个方面:
泛化能力。规则方案遇到没见过的页面结构就歇菜了,而学习模型可以通过训练学会"看到类似的结构就知道该怎么做"。比如Agent在训练中见过各种形式的关闭按钮(X图标、文字"关闭"、"取消"按钮),它就能泛化到新的关闭按钮样式上。
长期规划能力。规则方案通常是短视的,它只考虑当前状态下最优的动作,不考虑这个动作对后续步骤的影响。学习模型可以通过奖励函数的设计,让Agent学会为了长期目标牺牲短期利益。比如Agent可能需要先滚动页面加载更多内容,才能找到目标元素,规则方案可能不会主动做这个动作,但学习模型可以通过训练学会这个策略。
不过,学习模型在测试Agent场景下也面临很大的挑战。最大的问题是奖励函数设计困难。在DOOM里,奖励很明确:击杀敌人加分,被击杀扣分。但在测试场景下,"完成测试步骤"这个目标很难被分解成每一步的即时奖励。而且测试环境的状态空间太大,训练一个可用的策略需要大量的交互数据,成本很高。
4.3 混合架构的工程实践
综合来看,我在实际项目中采用的是规则引擎为主、学习模型为辅的混合架构。具体来说:
规则引擎负责处理高频、确定性强的决策场景,比如弹窗处理、表单填写、元素定位。这些场景的规则相对稳定,手写规则的性价比最高。
学习模型负责处理规则覆盖不到的边缘场景,比如页面布局异常时的恢复策略、多步骤操作的顺序优化。这些场景规则很难穷举,但学习模型可以通过少量样本学会合理的策略。
两者之间的切换由一个"置信度评估"模块控制。当规则引擎对当前决策的置信度高于阈值时,采用规则引擎的输出;当置信度低于阈值时,交给学习模型决策。置信度的计算综合考虑了当前状态与规则匹配的紧密度、历史决策的成功率等因素。
这种混合架构的好处是,它既保留了规则方案的可解释性和可控性,又通过学习模型补足了泛化能力。而且在实际运行中,如果学习模型给出了不合理的决策,规则引擎可以作为兜底,避免Agent完全失控。
5. 从零搭建一个最小可用的测试Agent
5.1 环境准备与技术选型
要搭建一个最小可用的测试Agent,你需要准备以下环境:
- 浏览器自动化工具:Playwright或Puppeteer。我推荐Playwright,因为它对多浏览器支持更好,API设计也更现代。安装命令:
npm install playwright。 - 编程语言:Node.js或Python。Node.js与Playwright的集成更自然,Python则在后续接入学习模型时更方便。我这里用Node.js做示例。
- 决策引擎:初期用纯规则方案即可,不需要额外依赖。后续如果要接入学习模型,可以用ONNX Runtime加载预训练模型。
- 状态存储:用一个简单的内存对象存储当前状态和历史动作序列即可,不需要数据库。
整个项目的目录结构建议这样组织:
test-agent/ ├── src/ │ ├── observer/ # 状态观测模块 │ │ ├── dom-parser.js │ │ └── state-builder.js │ ├── decision/ # 决策模块 │ │ ├── rule-engine.js │ │ └── action-selector.js │ ├── executor/ # 动作执行模块 │ │ └── action-runner.js │ └── agent.js # Agent主循环 ├── rules/ # 规则配置文件 │ └── default-rules.json └── package.json5.2 状态观测模块的实现
状态观测模块负责把页面状态转换成Agent能理解的结构化表示。核心代码如下:
// src/observer/state-builder.js async function buildState(page) { // 提取可见交互元素 const elements = await page.evaluate(() => { const interactables = []; const selectors = 'button, input, a, select, textarea, [role="button"]'; document.querySelectorAll(selectors).forEach((el, index) => { const rect = el.getBoundingClientRect(); const style = window.getComputedStyle(el); // 只保留可见元素 if (rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden') { interactables.push({ id: index, tag: el.tagName.toLowerCase(), text: (el.innerText || el.value || '').slice(0, 50), type: el.type || '', x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height), clickable: true, editable: ['input', 'textarea'].includes(el.tagName.toLowerCase()) }); } }); return interactables; }); // 检测弹窗 const hasModal = await page.evaluate(() => { const modals = document.querySelectorAll('[role="dialog"], .modal, .popup'); return Array.from(modals).some(m => { const style = window.getComputedStyle(m); return style.display !== 'none' && style.visibility !== 'hidden'; }); }); return { url: page.url(), title: await page.title(), elements, hasModal, timestamp: Date.now() }; }这段代码做了几件事:遍历页面上所有可能的交互元素,过滤掉不可见的,提取每个元素的关键特征,同时检测是否存在模态弹窗。返回的状态对象包含了Agent决策所需的核心信息。
5.3 规则引擎与动作选择
规则引擎的核心是一个优先级排序的规则列表。每条规则包含匹配条件和对应的动作生成逻辑:
// src/decision/rule-engine.js const rules = [ { name: 'close-modal', priority: 100, match: (state) => state.hasModal, action: (state) => { // 寻找弹窗中的关闭按钮 const closeBtn = state.elements.find(el => el.text.includes('关闭') || el.text.includes('取消') || el.text === '×' || el.text === 'X' ); if (closeBtn) { return { type: 'click', targetId: closeBtn.id, reason: '关闭弹窗' }; } return { type: 'press', key: 'Escape', reason: '按ESC关闭弹窗' }; } }, { name: 'fill-required-field', priority: 80, match: (state) => { return state.elements.some(el => el.editable && !el.value); }, action: (state) => { const emptyField = state.elements.find(el => el.editable && !el.value); return { type: 'fill', targetId: emptyField.id, value: generateTestValue(emptyField), reason: '填写必填字段' }; } }, { name: 'click-primary-action', priority: 50, match: (state) => { return state.elements.some(el => el.clickable && /提交|登录|确认|下一步/.test(el.text) ); }, action: (state) => { const btn = state.elements.find(el => el.clickable && /提交|登录|确认|下一步/.test(el.text) ); return { type: 'click', targetId: btn.id, reason: '执行主操作' }; } } ]; function selectAction(state) { const sortedRules = rules.sort((a, b) => b.priority - a.priority); for (const rule of sortedRules) { if (rule.match(state)) { return rule.action(state); } } return { type: 'wait', duration: 1000, reason: '无匹配规则,等待' }; }这个规则引擎的设计要点是:规则按优先级从高到低排列,第一个匹配成功的规则决定当前动作。弹窗处理优先级最高(100),表单填写次之(80),主操作执行再次之(50)。如果没有规则匹配,Agent选择等待,给页面更多时间加载。
5.4 Agent主循环的组装
把观测、决策、执行三个模块串起来,就形成了Agent的主循环:
// src/agent.js async function runAgent(page, maxSteps = 100) { const history = []; for (let step = 0; step < maxSteps; step++) { // 1. 观测 const state = await buildState(page); // 2. 决策 const action = selectAction(state); // 3. 执行 console.log(`[Step ${step}] 动作: ${action.type}, 原因: ${action.reason}`); await executeAction(page, state, action); // 4. 记录历史 history.push({ state, action, timestamp: Date.now() }); // 5. 检查终止条件 if (await isTestComplete(page)) { console.log('测试完成'); break; } // 6. 等待状态稳定 await waitForStable(page); } return history; }这个主循环的逻辑非常清晰:观测→决策→执行→记录→检查终止→等待稳定→下一轮。每一轮循环对应Agent的一个决策步。waitForStable函数负责检测页面是否已经稳定(DOM不再变化),避免在页面还在加载时就做下一步决策。
6. 实测中暴露的问题与调优经验
6.1 元素定位的稳定性问题
在实际跑测试的时候,我发现最大的坑是元素定位不稳定。同一个按钮,在不同次加载中可能因为页面布局的微小变化而位置不同,或者因为动态生成的ID不同而无法用选择器定位。
我的解决方案是多特征融合定位。不依赖单一的选择器,而是综合元素的文本内容、类型、相对位置、尺寸等多个特征来定位。具体实现是给每个元素计算一个特征向量,然后在定位时找特征向量最匹配的元素。这样做的好处是,即使某个特征发生变化(比如ID变了),其他特征仍然能帮助正确定位。
还有一个实用技巧:在状态表示中给每个元素分配一个稳定的临时ID(基于元素在DOM中的路径和特征生成),这样在同一个页面状态下,Agent可以用这个临时ID来引用元素,避免了选择器不稳定的问题。
6.2 等待策略的优化
等待策略是另一个影响测试稳定性的关键因素。我试过几种方案:
- 固定等待:简单粗暴,每次操作后等固定时间(比如2秒)。问题是等待时间不好设定,太短了页面没加载完,太长了测试效率低。
- 显式等待:等待特定元素出现或特定条件满足。比固定等待好,但需要预先知道要等什么元素,不够灵活。
- 状态稳定检测:监测DOM变化,直到连续一段时间没有变化才继续。这个方案最灵活,但实现复杂度也最高。
我最终采用的是状态稳定检测为主、显式等待为辅的方案。状态稳定检测负责处理大部分场景,对于已知的慢加载场景(比如文件上传后的处理),额外加显式等待。
状态稳定检测的实现要点是:用MutationObserver监听DOM变化,每次变化时重置计时器,当计时器超过阈值(我用的500毫秒)时认为状态稳定。这个阈值需要根据实际页面的动态程度来调整,动态内容多的页面可以适当调大。
6.3 死循环与无效动作的防范
Agent在运行过程中很容易陷入死循环。比如它一直点击同一个按钮,但页面没有任何变化,它就再点一次,无限循环下去。
防范死循环有几个策略。第一,动作去重:如果连续N次选择了相同的动作且状态没有变化,强制切换到其他动作。第二,状态变化检测:每次动作执行后比较前后状态,如果状态完全没变,给这个动作打一个负分,降低它下次被选中的概率。第三,最大步数限制:设置一个硬性的步数上限,超过就终止并报告异常。
无效动作的另一个来源是Agent选择了当前状态下不可执行的动作。比如它试图点击一个已经被移除的元素。防范措施是在动作执行前做一次可用性检查,如果目标元素不存在或不可交互,跳过这个动作重新决策。
6.4 规则冲突与优先级调整
随着规则数量的增加,规则冲突变得越来越频繁。比如"关闭弹窗"和"填写表单"两条规则可能同时匹配(弹窗里有一个表单),这时候优先级决定了哪条规则生效。
我的经验是,优先级的设定需要遵循几个原则:恢复性动作优先于推进性动作(先处理异常再继续正常流程),全局性动作优先于局部性动作(先处理影响整个页面的弹窗再处理局部表单),确定性高的动作优先于确定性低的动作(先做有把握的操作)。
另外,规则优先级不应该是静态的,而应该根据运行时的情况动态调整。比如某条规则连续多次执行失败,应该临时降低它的优先级,让其他规则有机会被尝试。
7. 这套决策模型还能用在哪些场景
7.1 跨平台兼容性测试
同一套测试逻辑需要在Web、移动端、桌面端等多个平台上运行,每个平台的UI结构和交互方式都不同。用决策模型来做这件事的优势在于,Agent的状态表示和动作空间是平台无关的,只需要为每个平台实现对应的观测模块和执行模块,决策逻辑可以复用。
比如"关闭弹窗"这个决策,在Web端对应的是点击关闭按钮或按ESC键,在移动端对应的是点击遮罩层或滑动关闭,在桌面端可能是点击标题栏的X。Agent的决策逻辑是一样的(检测到弹窗→选择关闭动作),只是执行层不同。
7.2 探索性测试与缺陷发现
传统的自动化测试是验证性的——验证已知的功能是否正常工作。而探索性测试是发现性的——在没有预设脚本的情况下自由探索应用,寻找潜在的缺陷。
决策模型天然适合探索性测试。Agent可以在没有明确测试目标的情况下,根据当前页面状态选择"最有信息量"的动作。比如优先点击那些还没被点击过的元素,优先探索那些还没被访问过的页面区域。通过这种方式,Agent可以发现一些人工测试容易遗漏的边缘情况。
7.3 用户行为模拟与性能测试
在做性能测试的时候,需要模拟真实用户的行为模式。传统的做法是用脚本录制用户操作然后回放,但这种方式生成的行为模式很机械,不够真实。
用决策模型来模拟用户行为,可以生成更加多样化和自然的操作序列。Agent可以根据页面状态做出"像人一样"的决策,比如看到表单先填必填项、看到弹窗先关闭、看到加载中的提示先等待。这样生成的负载更接近真实用户的行为,性能测试的结果也更有参考价值。
7.4 与CI/CD流水线的集成
把测试Agent集成到CI/CD流水线中,需要解决几个工程问题。第一是执行速度:Agent的决策过程需要足够快,不能成为流水线的瓶颈。第二是结果报告:Agent需要输出结构化的测试报告,包括执行了哪些动作、发现了什么问题、在哪个步骤失败等。第三是环境隔离:每次CI运行都需要一个干净的环境,Agent需要能够处理环境的初始化。
我的做法是把Agent打包成一个独立的命令行工具,接受测试目标URL和配置文件作为输入,输出JSON格式的测试报告。在CI流水线中,这个工具作为一个步骤被调用,测试报告被上传到制品库供后续分析。
8. 一些踩坑之后的个人体会
做这个方向有一段时间了,踩过的坑比预想的多得多。有几个体会特别深,分享出来给后来人省点时间。
第一个体会是:不要一上来就追求"智能"。我最初的想法是直接上强化学习,让Agent自己学会怎么测试。结果发现训练成本极高,而且训练出来的策略很难调试——Agent做了个奇怪的操作,你根本不知道为什么。后来退回到规则引擎,先把基础流程跑通,再逐步引入学习模型处理边缘场景,效果好得多。规则引擎虽然"笨",但它可控、可解释、可调试,这在工程实践中比"智能"重要得多。
第二个体会是:状态表示的质量决定了Agent的上限。我花了很多时间在优化状态表示上,从最初的原始HTML到后来的分层结构化表示,每一步优化都带来了明显的效果提升。状态表示做得好,Agent能看到关键信息,决策自然就准;状态表示做得差,再好的策略也是巧妇难为无米之炊。
第三个体会是:测试Agent的评估比训练更难。怎么判断一个测试Agent好不好?通过率高就代表好吗?不一定。一个Agent可能通过率很高,但它只是学会了"绕过困难"——遇到复杂场景就跳过,只做简单的操作。真正好的Agent应该能够覆盖更多的测试场景,发现更多的潜在问题。所以评估指标不能只看通过率,还要看场景覆盖率、缺陷发现率、决策多样性等多个维度。
第四个体会是:这个方向还在早期,很多问题没有标准答案。状态怎么表示、动作空间怎么设计、规则和学习模型怎么融合,这些问题在不同的项目中有不同的最优解。我的经验只能作为参考,具体怎么做还需要根据实际场景来调整。保持开放的心态,多尝试不同的方案,比固守某一种"最佳实践"更重要。