Skills工作流:对抗AI幻觉的生成-验证闭环实践指南
2026/9/7 16:50:17 网站建设 项目流程

1. 先搞清楚这个工作流到底解决什么实际问题

如果你用过一些 AI 工具,尤其是代码生成或文本生成类,大概率遇到过这种情况:AI 给出的答案看起来逻辑通顺,但仔细一查,函数名是编的、API 接口不存在、依赖库版本对不上,甚至整个解决方案都是虚构的。这就是典型的“AI 幻觉”(AI Hallucination)问题。

Matt Pocock 开源的 Skills 端到端工作流,核心目标就是对抗这种幻觉。它不是另一个代码生成工具,而是一套让 AI 输出结果更可靠、更可验证的流程框架。最直接的价值在于:当你用 AI 辅助开发、文档生成或自动化任务时,它能帮你把“看起来对”变成“实际跑得通”。

这个工作流适合三类人:

  • 经常用 AI 写代码,但被幻觉问题坑过,需要更高可靠性的开发者。
  • 团队内部希望建立 AI 辅助流程,但要求输出必须可测试、可复现的技术负责人。
  • 自己尝试过用 AI 处理复杂任务,但发现单次生成的结果经常需要大量人工修补的进阶用户。

和普通 AI 工具最大的区别是,Skills 工作流不追求一次生成完美答案,而是通过“生成-验证-迭代”的闭环,确保每一步输出都经过实际环境检验。如果你之前依赖 AI 输出时总得手动排查虚构内容,这个思路值得重点看。

2. 工作流的核心环节:怎么把“生成”和“验证”绑在一起

2.1 输入阶段就约束生成范围

很多 AI 幻觉源于问题描述太开放。比如你问“怎么实现用户登录”,AI 可能会编一个不存在的 OAuth 服务商。Skills 工作流的第一步,是把任务拆解成可验证的子问题,并对生成内容做范围限定。

例如,不是直接生成整个登录模块,而是先生成“验证邮箱格式的正则表达式”,并立即要求 AI 附带测试用例。正则表达式和测试用例都是可执行、可验证的代码块,能快速在本地或 CI 环境中运行。如果测试失败,要么重新生成,要么调整输入描述。

我一般会先用这个规则检查输入:

  • 任务是否可拆解为不超过 10 行代码的独立单元?
  • 这个单元是否有明确的输入和输出?
  • 能否用一组测试用例验证其正确性?

如果三个问题都是“是”,这个任务才适合进入工作流。如果任务本身过于宏大(比如“设计一个电商系统”),幻觉概率会急剧上升。

2.2 生成后立即执行验证脚本

Skills 工作流的关键,是在生成代码后自动插入验证环节。验证不靠人工阅读,而是靠预设的脚本运行。比如生成一个函数后,工作流会立即调用测试框架执行对应单元测试。

具体操作上,你需要提前准备两类验证脚本:

  • 基础语法验证:比如用 TypeScript 编译器检查类型错误,用 ESLint 检查代码风格。
  • 功能正确性验证:写一组最小测试用例,覆盖正常场景和边界情况。

验证脚本最好与生成环节解耦。也就是说,AI 生成代码后,由独立进程启动验证,避免生成环境干扰验证结果。如果验证失败,工作流会收集错误信息,作为下一次生成的改进依据。

2.3 迭代优化靠错误反馈驱动

单次生成不一定完美,但 Skills 工作流强调用验证错误驱动迭代。比如生成一个数组排序函数,第一次可能漏处理空数组,测试失败后,工作流会把错误信息(“输入空数组时抛出异常”)反馈给 AI,要求重生成。

这个环节最怕的是盲目重试。我的经验是,每次迭代必须记录:

  • 失败原因(测试报错、编译错误、运行时异常)。
  • 关联的输入条件(什么参数导致失败)。
  • 预期行为描述。

然后把这些信息结构化后喂给 AI。不要只是说“不对,再试一次”,而要明确指出现有结果在哪一步偏离预期。迭代次数最好设上限(比如 3 次),超过后转为人工介入,避免陷入死循环。

3. 本地环境怎么搭:从零开始跑通最小示例

3.1 准备环境和依赖

Skills 工作流本身不绑定特定 AI 模型或语言,但 Matt Pocock 的参考实现主要围绕 TypeScript 和 Node.js。如果你用其他语言,思路可以复用,但工具链要调整。

最小环境需要:

  • Node.js 18+(确保支持 ES Module 和顶层 await)。
  • 一个能调用 AI 模型的 API 密钥(如 OpenAI GPT-4、Claude 3 或本地部署的开源模型)。
  • 测试框架(Jest 或 Vitest 均可)。
  • 代码校验工具(TypeScript 编译器、ESLint)。

先创建一个空目录,初始化 package.json:

mkdir skills-workflow && cd skills-workflow npm init -y

安装核心依赖:

npm install typescript @types/node jest ts-jest --save-dev npm install openai # 如果使用 OpenAI 模型

配置 TypeScript 编译选项(tsconfig.json):

{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true } }

3.2 构建生成-验证闭环

工作流的核心是一个调度脚本,负责串联生成、验证、迭代。在 src/ 下创建 generator.js:

import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); export async function generateCode(prompt, context = '') { const fullPrompt = `${context} 任务要求:${prompt} 请生成 TypeScript 代码,并确保: 1. 函数导出为默认导出。 2. 包含 JSDoc 说明输入输出。 3. 代码必须能通过 TypeScript 严格模式检查。`; const response = await openai.chat.completions.create({ model: 'gpt-4', messages: [{ role: 'user', content: fullPrompt }], temperature: 0.2, // 低随机性,提高确定性 }); return response.choices[0].message.content; }

然后创建验证器 validator.js:

import { execSync } from 'child_process'; import { writeFileSync, unlinkSync } from 'fs'; export function validateCode(code) { const testFile = './test/temp.test.ts'; // 保存生成的代码 writeFileSync('./temp.ts', code); // 生成基础测试 const testCode = ` import func from '../temp.ts'; describe('Generated Function', () => { test('should work for basic case', () => { // 这里需要根据实际函数动态生成测试用例 // 示例:如果生成的是加法函数,则 expect(func(1, 2)).toBe(3); }); }); `; writeFileSync(testFile, testCode); try { // 运行 TypeScript 编译 execSync('npx tsc --noEmit', { stdio: 'inherit' }); // 运行测试 execSync('npx jest test/temp.test.ts', { stdio: 'inherit' }); return { success: true }; } catch (error) { return { success: false, error: error.message }; } finally { // 清理临时文件 unlinkSync('./temp.ts'); unlinkSync(testFile); } }

3.3 运行调试第一个任务

创建一个简单任务:生成一个函数,判断字符串是否为邮箱格式。

在 src/ 下创建 main.js:

import { generateCode } from './generator.js'; import { validateCode } from './validator.js'; async function main() { const prompt = '编写一个函数,验证输入字符串是否为有效邮箱地址。'; let maxRetries = 3; let context = ''; for (let attempt = 1; attempt <= maxRetries; attempt++) { console.log(`第 ${attempt} 次生成...`); const code = await generateCode(prompt, context); console.log('生成代码:', code); const result = validateCode(code); if (result.success) { console.log('✅ 验证通过'); return code; } else { console.log('❌ 验证失败:', result.error); context += `上一次生成失败原因:${result.error}。请避免同样错误。`; } } throw new Error('超过最大重试次数'); } main().catch(console.error);

运行前设置 API 密钥:

export OPENAI_API_KEY='你的密钥' node src/main.js

第一次运行可能会因为测试用例不完整而失败,但你能看到工作流如何自动重试。关键不是一次成功,而是观察迭代过程中代码如何逼近正确解。

4. 参数调优:平衡生成质量和验证成本

4.1 控制生成阶段的随机性

温度参数(temperature)直接影响幻觉概率。温度越高,创造性越强,但幻觉风险越大。在 Skills 工作流中,建议初始温度设为 0.2-0.3,优先保证可靠性。

如果连续多次生成都失败,可以适度提高到 0.5,引入更多多样性来突破局部最优。但一旦验证通过,后续类似任务应回归低温度。

另一个关键参数是最大生成长度(max_tokens)。对于代码生成任务,建议根据函数复杂度设置上限。简单函数 500-800 token 足够,复杂模块也不要超过 2000,否则容易生成冗余代码。

4.2 优化验证环节的执行效率

验证是工作流的性能瓶颈。全量编译和测试每次可能耗时几秒到几十秒。在实际使用中,可以分层验证:

  1. 语法检查:只运行 TypeScript 编译,不执行测试(最快)。
  2. 基础测试:运行一组核心用例,覆盖主要功能。
  3. 完整测试:运行全部边界情况(最慢)。

初次生成后先做语法检查,通过后再进入基础测试。只有在前几次迭代都失败时,才启动完整测试收集更详细的错误信息。

对于常用函数,可以建立验证缓存。如果生成的代码与之前通过的代码高度相似,直接复用验证结果,避免重复执行。

4.3 设置合理的迭代边界

无限重试不仅效率低,还可能陷入死循环。我的经验是:

  • 简单任务:最多重试 3 次。
  • 中等复杂度任务:最多 5 次。
  • 复杂任务:3 次失败后转为人工干预。

每次重试后,对比错误信息的变化。如果错误类型不变,说明问题可能出在任务描述或环境配置,而不是生成质量。这时应该暂停自动重试,先人工排查根本原因。

5. 常见问题排查:当工作流卡住时先看哪里

5.1 生成代码始终无法通过验证

如果连续多次生成都失败,按这个顺序排查:

  1. 检查任务描述是否明确:AI 是否真正理解你要什么?把任务描述拆解成更小的步骤,先验证最小单元。
  2. 查看验证脚本是否过于严格:测试用例是否覆盖了合理的边界?有时不是代码错了,而是测试条件太理想化。
  3. 确认环境依赖是否一致:生成的代码可能依赖特定库版本,但验证环境缺少这些依赖。在工作流开始时明确声明环境约束。

比如生成一个“获取当前天气”的函数,如果测试用例要求返回特定温度值,而函数实际调用的是真实 API,测试就会因网络或数据变化失败。这时应该用 Mock 数据替代真实调用。

5.2 工作流性能突然下降

当生成-验证周期明显变长时:

  1. 检查临时文件是否积累:每次验证后是否正确清理了临时文件?磁盘空间不足会拖慢整个流程。
  2. 监控 API 调用延迟:AI 服务商可能遇到性能波动。在代码中添加计时逻辑,区分生成时间和验证时间。
  3. 验证环节是否引入了不必要的开销:比如是否每次都在编译整个项目,而不是只编译生成的文件?

可以在工作流中加入简单的性能日志:

console.time('生成耗时'); const code = await generateCode(prompt); console.timeEnd('生成耗时'); console.time('验证耗时'); const result = validateCode(code); console.timeEnd('验证耗时');

这样能快速定位瓶颈在哪个环节。

5.3 生成的代码质量波动大

有时一次通过,有时多次重试仍失败,可能的原因:

  1. AI 模型本身的不确定性:即使是低温度,不同随机种子也会产生不同结果。对于关键任务,可以用同一输入生成多个候选,并行验证后选择最优解。
  2. 上下文窗口管理问题:如果多次迭代后上下文过长,AI 可能忽略早期的重要指令。定期清理上下文,只保留最近的关键错误信息。
  3. 任务复杂度超出模型能力:如果任务需要深度推理或专业知识,单靠生成-验证循环可能不够。这时需要人工拆解任务,提供中间步骤的示例。

6. 进阶应用:如何扩展到复杂项目和生产环境

6.1 批量处理多个相关任务

Skills 工作流不仅适用于单函数生成,还可以处理任务序列。例如,生成一个用户注册模块,可以拆解为:

  • 验证邮箱格式函数
  • 密码强度检查函数
  • 数据库插入函数
  • 发送欢迎邮件函数

每个函数独立生成和验证,最后组装成完整模块。关键是要定义清晰的接口契约,确保函数之间能正确协作。

批量处理时,任务调度器需要管理依赖关系(函数 B 依赖函数 A 的输出),并支持并行验证独立任务。可以用有向无环图(DAG)描述任务依赖,提高效率。

6.2 与现有开发流程集成

在生产环境中,Skills 工作流应该与现有工具链融合:

  • 版本控制:生成的代码在验证通过后,自动提交到特定分支,并打上生成标签。
  • 代码审查:虽然代码通过了自动化验证,但仍需要人工审查架构设计和业务逻辑。工作流可以生成变更说明,辅助审查。
  • 持续集成:把生成-验证环节作为 CI 流水线的一个阶段,只有通过验证的代码才能合并到主分支。

集成的关键是保持工作流的独立性。不要让它直接修改主代码库,而是通过 Pull Request 或 Merge Request 的方式引入变更,保留人工确认环节。

6.3 建立领域特定的技能库

长期使用后,你会积累大量通过验证的代码片段。这些可以组织成领域特定的技能库(Skills Library)。当接到新任务时,工作流先检索技能库中是否存在相似解决方案,直接复用或适配,减少重复生成。

技能库应该包含:

  • 代码实现
  • 验证用例
  • 使用示例
  • 适用场景描述
  • 性能特征数据

维护技能库需要定期回顾和更新,确保代码仍然符合当前项目标准和依赖版本。

7. 边界与局限:什么时候不适合用这个工作流

7.1 任务本身缺乏明确判断标准

如果一个问题没有清晰的正确性标准,验证环节就无法有效工作。比如:

  • 生成创意文案
  • 设计用户界面
  • 编写主观性较强的文档

这些任务需要人类审美和上下文判断,自动化验证容易导致过度优化到错误指标。

7.2 验证成本高于人工实现成本

对于极其简单的任务(如一行正则表达式),直接写比搭建工作流更高效。通常来说,任务需要超过 10 分钟人工实现时,才考虑使用自动化生成。

验证成本也要考虑。如果需要搭建复杂测试环境或准备大量测试数据,可能得不偿失。

7.3 强实时性要求的场景

生成-验证循环需要时间,从几秒到几分钟不等。对于用户交互等实时性要求高的场景,这种延迟不可接受。工作流更适合开发阶段、自动化脚本编写等离线任务。

7.4 安全关键型代码

尽管工作流能减少幻觉,但生成的代码仍可能包含潜在安全漏洞。对于支付、认证、数据保护等关键模块,建议只使用工作流生成初步原型,核心逻辑必须经过严格的安全审计。

Skills 工作流最大的价值不是完全替代人工,而是提供一个可靠性更高的 AI 辅助方案。它特别适合那些模式固定、验证直接、但实现细节繁琐的任务。当你需要批量生成工具函数、数据转换器、API 客户端等重复性代码时,这个思路能显著提升效率,同时保持输出质量可控。

实际落地时,我建议先从一个小而具体的任务开始,跑通整个流程后再逐步扩展到更复杂的场景。最重要的是建立“生成必验证”的习惯,不要让 AI 输出未经检验就直接进入生产环境。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询