做了一个月 LangGraph Agent 后,我们为什么用两周切到了 Skill?
前言:真正耗时的,是构建一个“能测”的组件页面
在前端组件测试的完整流程中,最耗时的环节往往并不是“执行测试”本身。
无论是验证新功能、完成版本回归,还是复现真实用户场景,自动化测试开始前都必须先构建一个完整的组件测试页面。它的作用是将抽象的组件能力还原为可运行的真实场景:准备测试数据、组合相关组件、配置 API 参数、实现交互逻辑,并将关键状态暴露给自动化断言。测试页面准备不足,测试脚本便缺少稳定、可靠的验证对象。
组件产品的通用性会进一步放大这项工作的复杂度。以 Wijmo 等企业级前端组件库为例,即使只是“验证表格编辑功能”,也可能需要覆盖嵌套编辑器、筛选、排序、冻结等 API 组合,处理事件边界和真实用户场景中的交互顺序,并兼顾 PureJS、Angular、React、Vue 等框架差异。组件越通用,功能与 API 的组合空间越大,测试页面的构建成本也越高。
因此,测试页面并非普通的功能 Demo。Demo 只需展示功能可用;测试页面则必须做到:结果可重复、状态可观察、行为可断言。它既要符合组件 API 的标准使用方式,也要适配现有测试工程的依赖管理、目录结构与自动化测试规范。
我们的测试环境采用由 SystemJS 在运行时加载模块的无构建方案。通用大模型更熟悉 Vite、Webpack 等现代前端构建流程,生成的代码往往“符合通用前端开发习惯”,却无法直接适配现有工程体系。每次开发前,工程师都要重复同步组件知识、工程约束和测试规范,效率因此被进一步拖慢。
这正是 AI 代码生成容易失效的场景:它很容易生成一个“看起来像 Demo 的页面”,却难以直接产出一个“能接入工程体系、能稳定回归、能自动断言”的合格测试页面。
为解决这个问题,我们先用一个多月搭建了基于 LangGraph 的自定义 Agent,希望实现端到端流程的完全可控;随后又用约两周,将组件测试页面生成与验证这条核心生产链路切换到 Skill 方案。
在随后的一个季度内,我们完成了 30 个任务,并观察到:复杂场景的首轮通过率从约 60% 提升至 92%,典型页面的交付周期从约 4 小时缩短至约 30 分钟。
需要先说明边界:这并非将同一批任务在两种方案上重复运行的严格 A/B 实验。两个阶段的模型、文档、样例和验证能力都在持续演进,因此下文数据反映的是生产链路整体演进后的阶段性结果,不能将全部增益简单归因于 Skill 本身。即便如此,这次演进仍回答了一个具体的工程问题:面对规则密集的组件测试任务,应该先自建复杂的 Agent 运行时,还是先将领域知识和验收标准沉淀为可执行能力?
1. 为什么一开始要搭 LangGraph Agent?
最初选择 LangGraph 并非一时冲动。我们需要处理的是一条完整的端到端链路:理解自然语言需求、查询组件资料、生成测试页面、准备并运行工程、分析错误,甚至将外部问题的最小复现 Demo 转换为回归测试页面。
当时,团队决定自建一套可控的 Agent 流程。我们希望每一步都能接入相应的资料和工具,并能根据结果分支、重试或进入失败处理流程。因此,我们选择了 LangGraph:它可以将模型、工具和业务逻辑拆分为独立节点,再用状态和条件边描述流程如何继续、结束或回退。
自然语言需求
-> 需求分析
-> 组件/API 查询
-> 测试页面生成
-> 构建与运行
-> 结果判断 / 下一步处理
在一个多月里,我们围绕这条链路搭建了一个自定义 Agent,主要能力包括:
- 自然语言生成测试页面:输入组件、功能与交互描述,Agent 即可分析需求并生成相应的页面代码。
- 组件知识查询:将组件 API 和使用说明纳入检索范围,辅助模型理解属性、事件和实现方式。
- 自主构建与运行:处理示例工程或生成结果的依赖准备、运行和地址提取;遇到构建失败时,尝试由模型分析错误并给出修复方向。
- 缺陷场景转换:将外部问题的最小复现 Demo 转换为自动化测试工程中的回归测试页面。
1.1 当时真正需要维护的,是一份状态契约
图结构本身并不难,难在让每个节点对同一任务形成一致理解。这类 Agent 的状态可以抽象为:
TaskState
requirement // 结构化需求与验收范围
apiFacts // 已查证的 API 事实及来源
constraints // 工程、框架、可测性约束
artifact // 生成的文件与测试接口
assertionPlan // 不随修复阶段改写的验收项
assertionResults // 每项断言的实际结果和日志
repairBudget // 剩余修复次数
这份契约带来两个直接好处:问题可以沿状态链路追踪,失败不再只剩一段模糊的自然语言描述;文件操作、构建和测试等确定性步骤也可以封装为受限工具,无须让模型临场猜测执行命令。
但它也暴露了后来的问题:将 apiFacts、constraints 写入状态,并不意味着生成节点一定会将其视为不可违背的前提。如果“生成代码”节点未被要求以这些事实和约束为输入,并产出可检查的执行证据,那么即使在图上增加检索或反思节点,也无法自动让代码变得更正确。
LangGraph 的检查点与恢复能力也需要被正确理解:配置持久化存储(checkpointer)后,它可以保存流程状态,便于调试、回溯和恢复;但解压、安装依赖、启动服务等外部副作用,仍需由业务代码设计为幂等、可检测或可清理。恢复状态并不能自动消除外部世界中已经发生的操作。
即便后来没有继续以 LangGraph 作为默认入口,这段探索仍留下两条值得保留的工程经验:为关键状态定义清晰的输入/输出契约,以及为工具调用划定受限边界。后续的 Skill 设计并未抛弃这些原则,而是将其纳入更轻量的任务封装。
2. Agent 能跑起来,但核心问题没有消失
2.1 隐性领域知识没有变成硬约束
为了让 Agent 理解组件 API,我们加入了向量检索。但组件资料来源并不统一,网页与离线文档的同步、切分和更新都需要维护;不同的切片算法和检索逻辑也可能得到不同结果。检索只能提高资料的可获得性,无法将这些分散经验自动转化为工程约束。
问题的根源不在于缺少一个反思节点,而在于组件测试依赖大量隐性领域知识:什么样的数据才能保证结果可重复,哪些状态必须以可读方式对外暴露,哪些样例可以跨任务复用,以及不同框架下哪些工程结构绝不能改动。这些知识零散分布在提示词、检索结果和开发者经验中,模型每次生成代码时都可能遗漏其中一部分。
2.2 RAG 能检索资料,却未必驱动代码生成
RAG 面临的问题并不止于资料维护。即使检索到了正确内容,也无法保证生成阶段会将其视为必须遵守的事实。
这暴露了一个常见误区:“有 RAG”不等于“代码生成时拥有可靠知识”。 如果资料未被组织成明确的规则、模板或输入契约,检索结果仍只是上下文中的一段普通文本。更具体地说,若生成节点无需列出所依据的 API 事实,后续也没有断言检查这些事实是否被正确使用,那么检索链路只能提高“模型看见资料”的概率,无法保证“模型按资料实现”。
2.3 页面能启动,不等于功能已经被验证
Agent 可以完成构建和运行,并得到可访问的页面地址,但这只能说明环境链路已经走通。它无法证明筛选功能确实改变了数据状态、编辑操作确实写回了数据模型,或冻结和事件逻辑确实符合需求。
早期流程缺少完整的自动检测闭环,生成结果仍需人工逐项检查。对测试工程而言,最核心的验收工作并未消除:页面“能打开”,不代表它已是可交付的合格测试页面。
2.4 自建运行时放大了维护成本
自定义 Agent 的灵活性背后,是大量需要自行负责的部分:状态字段、节点职责、路由逻辑、工具权限、错误分支、知识库更新、模型行为变化、日志和回归测试。每增加一项规则或功能,都可能牵动流程、状态和提示词中的多个位置。
这一个多月里,我们完成的并非一份简单配置,而是一套需要长期维护的运行时系统。对于动态、跨系统的任务,这种投入可能值得;但面对当前高频、规则密集的组件测试页面生成任务,它逐渐显现出过度设计的特征:流程很灵活,代码生成的准确性和验证问题却没有被优先解决,迭代速度也被维护成本拖慢。
3. 复盘后的转折:不是继续加节点,而是换一个问题
复盘后,我们不再优先追求“能处理一切的通用 Agent”,而是先解决“如何稳定生成符合组件测试规范的页面”这一核心问题。
这里需要澄清:LangGraph 与 Skill 并不是同一抽象层的直接替代品。LangGraph 是自定义 Agent 的流程编排层,主要负责状态、路由、工具调用和失败分支;Skill 则是面向领域任务的能力封装方案,将任务说明、知识、规则、模板、脚本和评测组织为可复用能力,由宿主 Agent 执行。
第一阶段,我们将“企业级”理解为尽可能多地自建编排能力;完成这轮探索后才更清楚地看到,当前核心任务的约束大多稳定且重复。随着通用编码 Agent 和工具链已能承担通用执行动作,业务团队不必再为每个领域任务重建一层运行时。于是,注意力从“让流程无所不能”转向“让领域约束可验证”。
| 维度 | LangGraph 自定义 Agent | Skill 方案 |
|---|---|---|
| 团队主要维护什么 | 节点、状态、路由、工具、重试、检索和监控 | 知识、规则、模板、脚本、评测与领域适配 |
| 擅长解决什么 | 跨系统、长流程、动态路由、审批与恢复 | 高频重复、边界稳定、上下文密集的领域任务 |
| 稳定性的来源 | 团队自行建设检查点、权限、观测和回归体系 | 由任务契约、模板、规则和验证保证领域上下文与验收的一致性;运行时可靠性仍依赖宿主环境 |
| 主要限制 | 运行时维护面广,迭代一个领域规则常会牵动多处 | 受宿主 Agent 和工具权限边界限制,不适合承载所有动态流程 |
| 本项目的适配度 | 灵活,但超出了核心任务的复杂度 | 直接围绕“生成并验证规范测试页面”沉淀能力 |
这并非“LangGraph 不如 Skill”的结论。真正的选型依据在于任务的不确定性来自哪里:如果不确定性主要来自跨系统流程和动态决策,编排层的投入值得;如果不确定性主要来自领域知识、工程约束和验收规则,优先将这些资产产品化通常更有效。
4. 用 Skill 把隐性经验变成工程资产
Skill 不等于把长提示词简单保存成文件。针对组件测试页面生成这一场景,我们将前一阶段反复出现的知识和约束沉淀为四类工程资产,并额外增加了一个横切的工具适配层。每类资产都直接对应 LangGraph 阶段的一个痛点。
4.1 知识体系化内置:先查证,再生成
我们将数百个组件和 API 条目、架构说明,以及经过生产验证的使用模式,整理成可按需读取的结构化参考资料。生成代码前,系统会先核验 API 签名、语义、多框架差异和使用边界,明确关键前提。
关键不在于“资料很多”,而在于将查询输出转化为结构化事实。例如,一个生成任务在编码前至少要明确:目标框架、组件版本、需要使用的属性/事件、已验证的样例模式、不可改动的工程入口,以及必须暴露的测试状态。资料查询阶段只负责确认这些事实,不直接产出代码;代码生成阶段则必须以这些事实为依据完成实现。
这样一来,知识不再只是 RAG 返回的一段背景文本,而会成为生成前的强约束事实依据;团队也减少了为代码生成单独维护复杂向量知识库链路的投入。
4.2 行为规则化约束:缩小错误的自由度
复杂组件生成效果不佳,本质上是因为模型的可选解空间过大,容易偏离工程规范。我们将易被遗漏的经验写成清晰、可执行的规则:测试数据必须确定,依赖来源必须受控,全局事件必须限定作用域,关键的非可视状态必须具备可读取的测试接口。
规则当然不能神奇地保证结果正确,却能有效缩小模型自由发挥的范围。面对组件嵌套、事件联动和数据状态等复杂场景,AI 无须每次重新猜测项目约定,而是在固定边界内实现需求特有的差异部分。
一条好的规则,最好同时说明“为什么”和“如何验”。例如,“数据必须确定”不能只写成禁止随机数,还要使验证能够重放同一组数据;“状态可读”也不等同于随意读取组件私有字段,而应约定使用公开 API 或显式暴露的测试接口。只有这样,规则才是可执行的约束,而非空泛的愿望清单。
4.3 产出模板化驱动:让模型只处理真正变化的部分
我们为 PureJS、Angular、React、Vue 等技术栈分别准备了基础模板。模板负责统一目录结构、初始化骨架和共同约束;模型只需处理本次需求特有的组件组合、数据和交互逻辑。
这使生成过程中的错误集中在真正需要针对性思考的功能实现上,而不再反复出现在文件结构、基础初始化等通用环节。模板也不应只是可复制的样板文件:它需要保留稳定的测试挂点、生命周期位置、依赖加载方式和清理逻辑,使生成结果从一开始就进入可验证的轨道。
4.4 流程标准化管控:把验收标准锁在代码生成之外
我们将流程固定为一条可检查的闭环:
环境预检
-> 需求与特性拆解
-> API/模式查证
-> 样例与模板定位
-> 模板约束下生成
-> 基于 Playwright 的测试脚本执行预定义断言
-> 通过:结构化结果报告
-> 失败:根因分析 -> 定向修复 -> 回到同一组断言
这里最重要的设计,不是“多跑几轮”,而是让验收标准独立于实现代码。需求会预先拆分为带优先级的特性清单:P0 是阻断交付的核心行为,P1 是高复杂度任务必须覆盖的关键路径,P2 是补充优化项。代码生成后,修复可以参考既定需求、断言结果和错误日志,但不得改写原始需求、期望值或验收脚本。未执行的验收项必须在报告中标记为“未覆盖”,不能计作通过;环境错误和超时同样属于“未完成验证”,而非任务成功。
“独立”不等于一定要换一套模型,而是要保证被测实现代码无法反向改写验收依据。为使这一边界可执行,P0 特性及其断言需要在生成代码前由需求方或经评审的固定模板确认,并存入受版本保护的只读资产;修复环只允许改动页面实现和明确允许修改的生成文件。每轮修复后,执行器都会先校验验收资产的哈希或 diff 白名单,再重跑完整的 P0/P1 基线。模型可以提出功能实现的候选方案,却不能既定义 P0 的期望值,又在失败后自行改写它。
对于高复杂度任务,验证清单会在生成代码前确定并固化;对于低、中复杂度任务,则只验证核心路径。这样可以避免让同一个生成过程临时发明实现、临时修改测试,再宣布自己通过。
验证也不只是检查页面能否加载,而要检查三个层次:动作是否发生、状态是否变化、最终行为是否符合预期。筛选输入框可以输入文字,不代表筛选功能已经实现;程序化断言还需检查筛选后的记录数和数据状态是否确实发生变化。
失败不是流程终点,而是下一轮修复的输入。我们会将失败归为环境/依赖、框架生命周期、API 语义、测试接口或业务交互等类别,再将失败断言、实际状态和错误日志带回相应的实现环节。每次修复都应更贴近原始需求、保持代码质量并解决根因;不得通过删除功能、修改预期结果或绕过异常制造“通过”的假象。
自动修复最多尝试 3 轮。这个数字是防止无意义循环的安全上限,而非通用的最优参数;若达到上限后任务仍失败,系统会保留失败项、已尝试的修复方案和未覆盖范围,交由人工接管。
一个典型的端到端例子
以一个典型的表格测试页为例,原始需求应拆分为下面的特性契约,而非直接将一段自然语言交给模型:
| 特性 | 验收条件 | 验证方式 |
|---|---|---|
| F1:确定性数据 | 页面加载后固定展示 10 行、6 列数据 | 读取公开数据源或表格公开 API |
| F2:初始冻结 | 初始冻结前 2 行和第 1 列 | 读取组件公开的冻结配置 |
| F3:行冻结切换 | 点击按钮后冻结行在约定的两种状态间切换 | 真实点击后读取冻结行数 |
| F4:列冻结切换 | 点击按钮后冻结列在约定的两种状态间切换 | 真实点击后读取冻结列数 |
| F5:冻结区域样式 | 冻结区域使用约定的背景色 | 定位代表性单元格并校验计算样式 |
一次典型失败可能是:页面可以正常打开,行冻结按钮也能被点击,但 F3 的实际值仍为 0。根因不应被笼统描述为“按钮不工作”后就让模型盲改,而要落到可验证的具体类别,例如初始化发生在目标框架的组件实例就绪之前,或按钮只更新了局部变量而未更新真实的表格实例。修复完成后,系统会重新运行 F1–F5 的全量断言,而非只重跑 F3,从而及时发现修复是否意外破坏了初始冻结或样式逻辑。
这个例子说明,一份合格的 AI 测试页任务至少应具备“自然语言需求 -> 特性契约 -> 实现 -> 独立断言 -> 根因修复 -> 全量回归”这条可追踪的完整链路。
4.5 通用浏览器工具不够时:为被测对象补上领域语义
Skill 的正常运行离不开浏览器测试工具的支撑。实践中,我们评估了 Playwright 的两类接入方式:通过 Model Context Protocol(MCP)暴露通用工具能力,或在 Skill 中组织工具调用、探测脚本和领域断言。二者并不互斥,Skill 也可以调用 MCP;我们调整的是工具抽象边界,并非否定 MCP 协议本身。
MCP 解决的是工具与 Agent 的连接方式,并不会自动补齐领域语义。本文中的断言由 Playwright 测试脚本执行;Playwright MCP 只是让 Agent 调用浏览器操作的可选传输层,不能与 Playwright Test 的断言运行时混为一谈。以 Playwright MCP 服务为例,它适合页面导航、点击、输入和 DOM 查询,但其默认抽象以浏览器和 DOM 为中心。面对 SpreadJS 等以 Canvas 渲染为主的组件,单元格、选区和工作表状态未必能从 DOM 中可靠推导;仅靠通用点击和 DOM 结构,也很难完成具备业务语义的编辑与验证。
部分 Canvas 组件也会提供 ARIA、DOM 代理或公开 API;问题不在于“Canvas 必然不可测”,而在于不能仅凭通用 DOM 可靠推导全部领域状态。
当然,我们也可以为这类能力单独开发自定义 MCP 服务。但在这个聚焦的测试工作流中,我们选择让领域适配器随 Playwright Skill 一起版本化,而非维护一个独立服务。这并未消除探针的契约、权限和兼容性成本,只是降低了当前单一工作流的接口协调成本;如果同一能力需要被多个 Agent 或应用复用,自定义 MCP 服务反而可能是更合适的边界。
在 Playwright Skill 中,我们将 Playwright 的浏览器操控能力与领域探针一并封装:在受控的页面上下文中执行专门的查询/编辑脚本,并将结果以结构化数据返回给断言步骤。
一个 Canvas 组件的领域探针可以提供类似下面的契约:
readSelection() -> { row, column, rowCount, columnCount }
readCell(row, column) -> { value, formula, displayText }
setCell(row, column, value) -> { changed, currentValue }
探针只能调用组件公开 API 或显式暴露的测试接口,不能读取或修改私有实现。需要验证真实用户输入路径时,仍优先使用鼠标、键盘等真实浏览器交互;探针用于准备可重复的前置状态,或作为状态判定依据(oracle)检验交互后的业务结果。这样既不会将 Canvas 测试退化为“直接改内部状态”,也避免仅凭像素或 DOM 猜测业务状态。
| 工具形态 | 适合的能力 | 在本场景中的边界 |
|---|---|---|
| 通用 Playwright MCP | 页面导航、DOM 交互、通用浏览器操作 | 不能天然理解 Canvas 内部的工作表、选区、单元格等领域语义 |
| Playwright Skill + 领域探针 | 浏览器操控 + 组件状态查询/编辑 + 专项断言 | 需要维护探针契约,并随组件版本和测试规范持续更新 |
这次尝试带来的经验是:工具“能调用”不等于它“理解被测对象”。当被测产品超出通用 DOM 模型时,应为其补充领域适配层。这里为 SpreadJS 构建的是独立的 Canvas 领域探针;其他内部组件或业务层前端测试也可以按同样方式扩展,而不必等待通用浏览器工具覆盖所有语义。
4.6 运行边界:可执行不等于可无限授权
在企业工程中,Skill 还需要在宿主 Agent 之外划定清晰的运行边界。生成和验证只能在目标工作区内读写;环境检查、启动和测试应使用明确的脚本或命令白名单;工具输出应尽量结构化,避免将无关目录、凭据或配置带入上下文;任务结束后,无论成功或失败都要清理浏览器和临时进程。
这些约束不属于“提示词优化”,而是工程职责分层:Skill 负责领域能力,宿主负责工具权限、上下文隔离、重试和报告。没有这层边界,任何看似方便的自主构建或浏览器控制,都会重新带来维护和安全风险。
5. 最终切换:更快落地,也更接近核心验收标准
最终,我们将组件测试页面生成与验证这条核心生产链路切换至 Skill。这里的“切换”并不表示 LangGraph 在所有场景都不再需要,而是指日常高频的页面生成与验证不再以自建 Agent 图作为默认入口。
5.1 相关成效:阶段性运营观察
统计范围为一个季度内完成的 30 个前端组件测试页面任务。我们将涉及多组件协作、API 组合、嵌套编辑或跨框架适配的任务归为“复杂场景”。下文的“首轮通过”按任务统计,指首轮产出在未修改代码的情况下,通过该任务规定的运行检查、工程规范、核心组件交互/API 组合及验证检查。
仍需强调两个限制:第一,LangGraph 阶段的验证自动化覆盖并不完整,早期结果包含人工核对;Skill 阶段则将更多核对项转为可执行断言。第二,30 个真实需求并非成对的同源样本,模型版本、知识和模板资产的成熟度、组件版本及任务结构变化都可能共同影响结果。
| 指标(阶段性运营观察) | LangGraph 自定义 Agent 阶段 | Skill 阶段 | 统计口径与边界 |
|---|---|---|---|
| 复杂场景首轮通过率 | 约 60% | 92% | 首轮无需人工修改代码即可通过该任务约定验收的任务占比;前期含人工核对,后期更多由断言执行 |
| 典型测试页面交付周期 | 约 4 小时 | 约 30 分钟 | 从需求确认到页面首次通过验收的端到端耗时;来自固定类型页面的代表性可比任务,不代表所有任务的平均值 |
| 人工复核投入 | 约 2 小时 | 约 5 分钟 | 仅统计工程师阅读产物、处理验证结果并决定是否需要修改代码的有效工作时间;自动化执行、环境启动和等待时间不计入该项,也不包含前期资产建设成本 |
这组数字中,最值得关注的不是“生成速度”,而是工程师的人工复核投入由约 2 小时降至约 5 分钟。这意味着更多判断由可执行断言承担,并不等同于整套验证流程的机器执行耗时也只有 5 分钟。相应地,Skill 的维护成本并未消失,只是从维护图节点和运行时,转移为维护知识、规则、模板、断言和领域探针。
5.2 代表性案例:覆盖 50+ 组件的 Vue 到 Angular 测试页面迁移
除单个页面生成外,我们还完成过一项跨框架迁移:将一个覆盖 50+ 组件的测试页面从 Vue 迁移至 Angular。这并非简单的语法替换;迁移过程中需要同时处理两种框架在组件组织、数据绑定和生命周期上的差异,并逐一排查组件 API、页面结构和测试行为带来的错误。
团队估算的纯人工工期至少为 5 个工作日;在 Skill 辅助下,实际用 2 个工作日完成。
Skill 在其中承担的并非“一键转换”角色,而是将框架模板、组件知识、既有实现模式和验证步骤串成可重复执行的流程:先识别原页面的组件和功能点,再匹配目标框架的模板与实现方式,生成迁移结果,最后通过自动化断言发现并修复差异。
一个典型的框架差异是组件实例的可用时机。Vue 页面中依赖 ref 和挂载后回调的初始化逻辑,不能机械地替换为 Angular 的类字段;若在视图子节点尚未就绪时读取组件实例,页面虽能渲染、按钮也可显示,但实例配置和测试接口未必真正就绪。特性验收会将“组件已初始化”和“交互后实例状态变化”拆成独立断言;失败后,再将初始化逻辑迁移到正确的生命周期或组件就绪回调,并重新执行整页验收清单。
这正是迁移中最耗时的部分:不是简单替换模板语法,而是识别框架生命周期、绑定语义和组件实例边界。在这次迁移中,Skill 将高频排错路径固化下来,减少了团队重新查找资料和试错的次数;这一案例用于说明工作方式,不代表其他框架或组件组合都能获得同等收益。
6. 可迁移的工程原则
这次实践的价值不只在于换了一套方案。无论使用哪种组件库、Agent 或 Skill,下面几条都可作为通用经验。
- 先固定验收合同,再让模型写代码。 将自然语言需求拆成可验证特性,区分 P0/P1/P2,并将 P0/P1 的期望值固定在修复环之外。代码生成与测试生成不能共用一份可随意改写的“答案”。
- 把确定性动作交给脚本和模板。 环境检查、模板初始化、格式检查和测试执行不应每次都由模型临场决定。将它们封装为受限脚本,在前提不满足时快速失败并返回结构化结果,能减少随机性和无效重试。
- 把知识拆成事实、规则和样例,而不是堆进长 Prompt。 将 API 事实放入参考资料,将工程边界写成规则,将代码结构固化为模板,并将已验证页面作为模式参考。每类资产都有独立的更新节奏,也更容易定位某次质量回退的来源。
- 按复杂度分配验证深度,并诚实报告覆盖范围。 简单任务验证核心路径;复杂组件组合先生成检查清单,再逐项执行。已验证通过、验证失败和未覆盖项必须分开报告,超时和环境错误不能默认成功。
- 为非 DOM 组件建立公开、受控的领域探针。 优先使用真实用户交互,再通过公开 API 或显式测试接口读取业务状态;不要靠截图判断全部逻辑,也不要通过组件私有字段“验证”成功。
- 把 Skill 当作需要版本化的工程资产。 每次改动知识、规则、模板或探针,都应在一组脱敏或合成的基准任务上回归;同时限制可访问的工作区和命令范围。没有评测和权限边界,Skill 很快也会变成另一种难维护的自定义系统。
结语
从自建 LangGraph Agent 到 Skill,我们经历的不只是一次技术迭代,更是一次问题聚焦:从追求完全可控的通用流程,转向优先解决高频、稳定、规则密集的组件测试任务。
LangGraph 仍适合跨系统协调、复杂审批、长时运行和动态任务路由;Skill 则更适合将稳定的领域知识和验收流程封装为可重复执行的能力。两者也可以组合:由编排层处理真正复杂的跨系统流程,由 Skill 负责具体领域任务。关键不在于一开始搭出所有能力,而在于先判断瓶颈究竟在流程,还是在领域知识与验收。
对前端组件测试而言,最终衡量的不是 Agent 是否拿到了运行地址,而是测试页面能否接入工程、需求是否真正实现、断言是否真实执行,以及失败是否被完整保留。这也是我们用两周切到 Skill 的原因:它更直接地解决了当时最昂贵的问题。
浙公网安备 33010602011771号