前言
众所周知,code agent 是无法判断出生成的代码是否正确的。也就是只有生成环节,没有验证环节,所以需要我们一点点做 debug,这也是很多人最开始不看好 ai 的原因。
但反过来想,是不是解决了验证环节,ai 的体验就会获得大幅提升呢。
锁定问题
要想解决验证环节,可以先思考我们是如何做 debug 的。
对于前端,一般而言都是先安装依赖,看看代码有没有爆红。然后运行,看项目是否能正常启动,最后打开页面和控制台,点一点看哪里有问题。
拆解来看,其实分为了几个问题:
- 是否能正常安装依赖
- 代码是否直接在编辑器就出现了错误(静态检测/开发时错误)
- 项目是否能正常启动(构建时错误)
- 控制台是否报错(运行时错误)
所以,我们的目标是依次解决这些问题。
解决方案
能否正常安装依赖
这一步其实相对简单,运行 npm i 就行了。需要注意的点有:
- npm 安装依赖存在不少问题,它使用扁平化所有依赖,并逐个进行下载的做法,这可能会导致幽灵依赖(能引用到没安装 package.json 的部分包),安装缓慢(内存存储双爆炸),依赖冲突(子依赖使用了不同版本)等问题。为了解决这些问题,pnpm 出现了,目前大部分前端项目都会优先考虑使用 pnpm
- 关于锁文件的问题
真正决定安装版本的是锁文件,其生成依赖于 package.json。但不要手动修改锁文件。
安装会有很多种情况
情况1:当有锁文件,且与 package.json 中包一致时,执行安装,会完全按照锁文件进行安装,这是最常见且最安全的情况。
情况2:当有锁文件,且与 package.json 中包不一致(比如手动在 package.json 中更新了包),执行安装,锁文件会更新有变化的部分。锁文件更新后,后续再进行安装就会进入到情况1
情况3:当没有锁文件,执行安装依赖时,会按 package.json 里的依赖
- 精确版本号时,会选择精确版本("axios": "1.5.0" 会安装 1.5.0)
- 有 ^ 时,允许小版本自动更新("axios": "^1.5.0" 会选择最新且小于 2.0.0 的版本)
- 有 ~ 时,允许小特性自动更新("axios": "~1.5.0" 会安装最新且小于 1.6.0 的版本)
根据这些规则,会选择出所有适合的包,生成锁文件,接着进入情况1,使后续该项目使用的包稳定,不会出现突然升级的问题。
暂时无法在昆仑万维文档外展示此内容
最佳实践:安装/更新新依赖,最好还是走命令行
npm i xxx // 安装
npm i xxx@latest // 更新最新版,也可以指定版本如 xxx@1.0.0
这样能自动同时更新 package.json 和锁文件,且不影响其他包。
退而求其次,是手动修改 package.json,随后再进行 npm i 重新安装,这样也能让锁文件进行更新,
如果单独修改 package.json 文件,可能是无效的,比如:package.json 中
"axios": "^1.5.0",lock 中1.5.2,此时你将 package.json 的^1.5.0改为^1.5.1,由于 lock 中的1.5.2仍符合^1.5.1,所以是无事发生,不会产生任何更新。
是否直接在编辑器就出现了错误
ai 的很多错误,其实在前端通常都能很直接的看到,如下:
这里的报错其实有两个原因:eslint 报错与 ts 报错
我们可以通过这两个命令扫描出对应的错误:
npx eslint ./
npx tsc --noEmit -p tsconfig.app.json --strict
我们可以在对应的文件中,配置对应的规则集,eslint 的配置位于 eslint.config.js 中;ts 的配置位于 tsconfig.app.json。
严格的规则,有助于 ai 生成产物的稳定,更不容易出现白屏报错等问题,但相对的时间会更长,因为要解决更多 error。宽松的规则,则对应 token 的减少,时间更快。
在我们的项目中,使用了默认的规则集,基本上就够用了,不过有几个模型经常报错、但不影响主流程的,我们还是降低为了 warn,从而规避修复所需要花费的成本。
"@typescript-eslint/no-unused-vars": "warn", // 未使用变量降为 warn
"@typescript-eslint/no-empty-object-type": "warn", // 空对象类型降为 warn
"@typescript-eslint/no-explicit-any": "warn", // any 类型降为 warn,这个可能还有待商榷,过多的使用 any 本质上就是忽略报错
项目是否能正常启动
在大部分情况下,如果前两个步骤是正确的,最后一步 build 大概率不会有问题。即使有问题,模型也能在 build 后也能看到构建报错,从而直接进行修复。
如果想要验证也简单,跑一个构建命令就行了
npm run build
控制台是否报错
前面三个环节,都可以用命令行进行验证和 debug,也就是 agent 有办法感知到。但控制台的报错,属于运行时错误,很难报给 agent,所以我们需要设计一套上报机制来解决这个问题。
也就是这个需求 【Websites】增加手动Debug模式,简单来说,就是控制台的所有报错都暴露出来发给 agent
要实现这点,关键在于拦截所有报错,我们来看常见的报错,逐个进行解决。
- 普通 js 同步报错,也是最常见的一种,语法问题等引起
解法:可以用 window.onerror 进行回调处理
- promise reject 未处理报错,类似 try catch 问题
解法:可以使用 window.addEventListener("unhandledrejection", event => {}) 来做回调处理
- 接口报错 / 资源加载失败
虽然严格意义上,不算代码的问题,但是也有必要让 agent 感知到
解法:通信使用 axios 包,并且在其响应拦截器中做处理,如果不是 200,则进行 error 回调
主要的报错就这三种,我们可以基于此封装一套错误抛出的机制。不过 github 实际上有现成的更完备的库,可以考虑直接使用 Sentry 来做这件事。sentry 本身是用于错误上报的,不过我们可以将其拦截掉,在 callback 中只执行我们的逻辑即可。
Sentry.init({
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0', // 使用假 DSN 来启用错误捕获功能
enabled: true, // 启用 Sentry
attachStacktrace: true, // 附加堆栈跟踪
// 在发送前处理错误
beforeSend(event, hint) {
const error = hint.originalException || hint.syntheticException
dosomething(error, event)
// 返回 null 阻止实际上报到 Sentry
return null
},
})
当你的模版中接入了这套流程后,你就可以将错误向 agent 抛出了,比如说:
-
skywork 中,产物是在 iframe 里,那么我们可以利用 postMessage 将错误反馈到主应用,从而在输入框中引用到错误并发送给 agent
-
如果产物完全独立,可以考虑将错误打到接口中,之后的通信让 agent 先去访问这些错误,修复后再做操作
总结
所以最终这个机制有以下几个部分构成:
- 安装、更新依赖时走命令行(且最好使用 pnpm 而非 npm)
npm i xxx // 安装
npm i xxx@latest // 更新最新版,也可以指定版本如 xxx@1.0.0
2. build 前先扫描错误并修复(要使用 typescript 而非 js,要接入 eslint)
npx eslint ./
npx tsc --noEmit -p tsconfig.app.json --strict
3. 跑 build 看有没有问题
npm run build
4. 在模版中接入运行时错误监控,并将监控的结果传递给 agent
Sentry.init({
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0', // 使用假 DSN 来启用错误捕获功能
enabled: true, // 启用 Sentry
attachStacktrace: true, // 附加堆栈跟踪
// 在发送前处理错误
beforeSend(event, hint) {
const error = hint.originalException || hint.syntheticException
dosomething(error, event) // 自定义实现
// 返回 null 阻止实际上报到 Sentry
return null
},
})