AI生成3D特效网页不稳定?用Skill固化工程规则,稳定输出Three.js页面
2026/9/20 2:37:36 网站建设 项目流程

写 3D 特效页面前,先问一句:你是不是也经历过这个场景?

让 AI 写一个“3D 粒子星空页面”,它很快就给了你一个.html文件。你满心欢喜双击打开,结果要么黑屏、要么粒子到处乱飞、要么鼠标一拖显卡风扇就开始起飞。再让它改,它又给你换了一套完全不搭的风格。问题出在哪?不是模型不会写 Three.js,而是你每一次都在让 AI“从零开始猜”你想要什么。

这个问题的解法,就是现在编程代理工具里流行起来的一个概念:Skill

这篇文章不打算停留在“Skill 很酷”这个层面。我会先讲清楚 Skill 到底是什么,它和普通提示词有什么区别,然后给出一份可以直接复制的SKILL.md,把它应用到“生成 3D 特效网页”这个具体任务上。读完你不仅会得到一个带交互的 3D 粒子星系网页,还能掌握一套“把 AI 生成质量稳定下来”的方法。

1. 为什么用 AI 写 3D 页面,总是一张“开箱即崩”的图

先还原一个典型过程。

你给 AI 说:“帮我写一个 3D 地球效果网页,有星空背景、可以旋转缩放。”于是它生成了一个index.html,里面塞了三四个库,从 CDN 加载了一堆脚本。你本地打开,发现:

  • 页面黑屏,控制台报了一堆 CORS 错误;
  • 地球是有了,但旋转的坐标系是歪的;
  • 窗口一缩放,3D 画布直接变形;
  • 手机上打开,帧率低得没法看。

这些问题的技术原因各不相同,但根子上只有一个:AI 在生成时没有一套固定的工程约束。它知道你“想要 3D 效果”,但它不知道你“能接受的加载时间”“必须支持移动端”“纹理不能太大”“需要降级提示”这些隐含要求。你每次都要重新把需求描述一遍,而且每次描述的完整度还不一样,于是生成的代码质量全凭运气。

1.1 普通提示词为什么管不住 3D 网页

3D 网页特效和普通页面不一样。一个简单的管理后台,风格偏差一点问题不大;但 3D 页面涉及相机、光照、材质、粒子数量、交互控制、像素比、动画帧率、资源加载、WebGL 兼容性,参数多到一次性 Prompt 根本写不完。

就算你把要求写进 Prompt 里,比如“保持 60fps”“支持移动端”,AI 在生成长代码时依然可能忘掉这些约束。原因很简单:Prompt 是一次性的,上下文窗口会被大量代码淹没。写到最后,模型只记得“生成粒子系统”,忘了“保持 60fps”。

真正的问题是:你缺乏一个机制,把“3D 特效网页”这个任务的所有要求、步骤、模板、质量清单固化下来,让 AI 每次执行时都先读取这套规则,而不是凭记忆发挥。

Skill 补的正是这一环。

2. Skill 是什么:它和普通提示词、Agent 的区别

2.1 Skill 的通俗解释

你可以把 Skill 理解成给编程代理(Agent)用的“岗位说明书 + 操作手册 + 工具箱”。

  • 岗位说明书:告诉 Agent 这个 Skill 什么时候该用、解决什么问题;
  • 操作手册:告诉 Agent 具体怎么做,用什么技术栈、按什么步骤来;
  • 工具箱:里面放的是脚本、模板、资源文件,Agent 可以复制使用,而不是重新发明一遍。

普通 Prompt 是一次性的,而 Skill 是放在固定目录里的结构化文件。每次需要生成 3D 网页时,Agent 会先去读这个 Skill,再开始写代码。这就好比你把“小张,帮我做一版 3D 页面”换成“小张,请按照这份设计规范手册执行”,结果自然稳定得多。

2.2 Skill 和 Prompt、Agent 的关系

很多读者会把三者混在一起,我用一张表拆清楚:

概念本质生命周期在 3D 网页任务中的表现
Prompt一次性的自然语言指令用完即焚“帮我写一个 3D 粒子页面”
Skill可复用的任务规则和资源包长期存在、可版本管理一份 SKILL.md,规定技术栈、步骤、质量要求
Agent执行任务的智能体一个运行环境读取 Skill,按规则生成代码并自检

三者关系可以这样理解:Agent 是执行者,Prompt 是临时指令,Skill 是长期沉淀下来的工作方法。Skill 起到的作用,是把“这次碰巧生成得不错”变成“每次都能生成得不错”。

2.3 为什么说 Skill 是稳定性的关键

在 3D 特效这个领域,Skill 的价值最明显。

你让 AI 生成一个普通 CRUD 页面,即使没约束,它大概率也能跑通。但 3D 页面只要有一个环节不对——比如 CDN 版本太旧、缺少OrbitControls、没有设置setPixelRatio、纹理使用了大图——整个页面就会黑屏或者卡顿。这些细节恰恰是模型最容易遗漏的。

Skill 把这些细节写进了规则里,每次执行都会带着这套规则走。所以它的价值不是让 AI 变得“更聪明”,而是让 AI 的输出变得“更可控”。

3. 一个适合 3D 特效网页的 Skill 应该包含什么

3.1 Skill 的目录结构

不同编程代理工具对 Skill 目录的命名不完全一样,常见的位置有:

.agent/skills/ web-3d-effect/ SKILL.md assets/ scripts/ examples/

有的工具使用.claude/skills,有的使用.codex/skills,还有的放在全局用户目录下。具体路径以你使用的工具官方文档为准,但核心文件都是SKILL.md

web-3d-effect/ SKILL.md # 技能定义,Agent 首先读取它 assets/ # 可复用的静态资源、纹理、模型 scripts/ # 可复用的生成脚本、转换脚本 examples/ # 示例代码,Agent 可以直接参考

SKILL.md是灵魂。它的作用不是给 AI“讲道理”,而是给出可执行的动作清单。AI 判断是否使用某个 Skill,靠的是文件头部的元信息;AI 之后怎么执行,靠的是正文里的步骤、约束清单和自检项。

3.2 SKILL.md 的元信息怎么写

写 Skill 最容易犯的错,是把description写得太抽象。比如:“用于生成 3D 网页”。这种描述会导致 Agent 在用户提到任何“页面”“效果”时都触发它,反而干扰其他任务。

比较好的写法是明确触发场景:

  • 当用户要求“3D 效果”“粒子动画”“3D 地球”“WebGL 展示”时使用;
  • 当用户需要“三维可视化”“模型展示”时使用;
  • 当用户只是做普通图表页面时,不使用。

触发条件写清楚,Skill 才能被 Agent 精确调用。

3.3 3D 特效场景特有的约束

普通网页 Skill 可以不太关心性能,但 3D 网页不行。SKILL.md里应该至少包含这些约束:

  • 技术选型:优先 Three.js,不引入大型游戏引擎;
  • 渲染性能:开启antialias,限制devicePixelRatio不超过 2;
  • 资源体积:纹理和模型文件不宜过大,优先程序生成纹理;
  • 交互体验:默认提供拖拽旋转和滚轮缩放,移动端支持触摸;
  • 兼容性:WebGL 不可用时给出降级提示,而不是让页面白屏;
  • 模板代码:把常用的初始化代码放进examples/,供 Agent 直接复制。

这些约束不是可有可无的“建议”,而是应该写进 Skill 里的硬性步骤。

4. 环境准备与前置条件

写 Skill 和验证 3D 网页,需要一个最小的本地环境。

4.1 准备支持 Skill 的编程代理工具

你需要一个支持 Skill 机制的编程代理工具,例如 Claude Code、Codex、OpenCode 等。不同工具对 Skill 的目录名、配置方式略有不同,但你只需要明白核心逻辑:把 Skill 文件放到工具识别的目录里,它就能在任务匹配时自动读取

如果工具还没有配置好,先看官方文档完成基础配置。版本号不是本文的重点,以你当前环境为准。

4.2 准备 Node.js 和本地服务器

3D 页面涉及模块加载和资源请求,直接用file://打开可能会遇到跨域问题。推荐装好 Node.js,后面用npx serve启动本地静态服务器。

node -v npm -v

如果你更习惯 Python,也可以直接用:

python3 -m http.server 8080

4.3 准备浏览器调试工具

建议使用 Chrome 或 Edge。打开开发者工具的 Console 面板,用来检查报错;切换到 Device Mode 可以模拟移动端触摸,验证自适应效果。

环境准备到此为止,接下来直接进入核心:写一个能生成 3D 特效网页的 Skill。

5. 完整示例:写一个 web-3d-effect 的 Skill

这一节给出可直接使用的 Skill 文件,然后通过一个“3D 粒子星系”页面演示完整的调用和验证流程。

5.1 编写 SKILL.md

将下面的内容保存为web-3d-effect/SKILL.md

--- name: web-3d-effect description: 生成基于 Three.js 的 3D 交互网页特效,包括粒子系统、3D 地球、模型展示和可视化场景。当用户提到 3D 效果、粒子动画、3D 地球、WebGL 展示、三维可视化时使用。 when_to_use: 用户需要创建 3D 特效页面、3D 可视化、粒子星空、3D 场景或模型展示网页时 version: 1.0.0 --- # 3D 特效网页生成指南 ## 目标 生成一个可在浏览器中直接打开并交互的 3D 网页特效,默认提供旋转和缩放控制。 ## 技术选型 - 优先使用 Three.js,不引入大型游戏引擎 - 如需动画,使用原生 requestAnimationFrame,减少不必要的依赖 - 粒子系统使用 Points + BufferGeometry - 交互控制使用 OrbitControls ## 必做步骤 1. 使用 importmap 或 CDN 引入 Three.js,锁定一个稳定主版本 2. 创建 WebGLRenderer 时设置 antialias: true,并限制像素比不超过 2 3. 添加 resize 事件,窗口变化时同步更新 camera 和 renderer 4. 添加 OrbitControls,开启阻尼效果 5. 在页面底部展示操作提示:拖拽旋转、滚轮缩放 6. 在 WebGL 不可用时,显示降级提示而不是白屏 7. 生成一份 README 或在页面注释中说明启动本地服务器的方法 ## 质量要求 - 首屏加载时间控制在 3 秒内 - 动画帧率不低于 30fps - 页面支持移动端触摸操作 - 不使用过大的贴图和模型文件 - 纹理能程序生成时,不使用外链图片 ## 自检清单 - [ ] 页面在 Chrome 中打开无报错 - [ ] 窗口缩放后 3D 画布自适应 - [ ] 鼠标拖拽旋转、滚轮缩放正常 - [ ] 未引入体积过大的资源和多余依赖 - [ ] 无 WebGL 环境时有降级提示

这一段信息量不小。重点看三个地方:

一是description。它决定了 Agent 什么时候触发这个 Skill。你可以把常见的 3D 相关词都写进去,帮助 Agent 准确匹配。

二是“必做步骤”。这些不是建议,而是每次生成都要执行的动作。模型有了这个清单,就不容易遗漏画布自适应、像素比设置这些关键点。

三是“自检清单”。Agent 生成完代码后会按这个清单检查自己,相当于把人工验收环节前移到了生成环节。

5.2 生成结果示例:一个 3D 粒子星系页面

把下面的内容保存为项目根目录的index.html。这是一个简单的 3D 粒子星系页面,展示了 SKILL.md 中要求的大部分要素。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>3D Galaxy - Agent Skill Demo</title> <style> body { margin: 0; overflow: hidden; background: #0a0a1a; color: #fff; font-family: "PingFang SC", "Microsoft YaHei", sans-serif; } #info { position: absolute; top: 20px; left: 20px; z-index: 10; font-size: 14px; line-height: 1.6; opacity: 0.8; } #tips { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 10; font-size: 12px; color: #666; } #fallback { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; color: #999; font-size: 14px; z-index: 20; background: #0a0a1a; } </style> </head> <body> <div id="info">3D Galaxy<br /><small>由 Agent Skill 生成</small></div> <div id="tips">拖拽旋转 · 滚轮缩放</div> <div id="fallback">当前浏览器不支持 WebGL,无法显示 3D 特效。</div> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; let renderer; try { renderer = new THREE.WebGLRenderer({ antialias: true }); } catch (e) { document.getElementById('fallback').style.display = 'flex'; throw e; } renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); document.body.appendChild(renderer.domElement); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 10, 22); const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.08; // 粒子:螺旋星系 const count = 15000; const positions = new Float32Array(count * 3); const colors = new Float32Array(count * 3); for (let i = 0; i < count; i++) { const radius = Math.pow(Math.random(), 0.6) * 16; const angle = radius * 0.8 + (Math.random() - 0.5) * 0.5; const y = (Math.random() - 0.5) * 1.2 * Math.min(radius / 4, 1); positions[i * 3] = radius * Math.cos(angle); positions[i * 3 + 1] = y; positions[i * 3 + 2] = radius * Math.sin(angle); const brightness = 0.6 + 0.4 * (1 - radius / 16); colors[i * 3] = 0.5 * brightness; colors[i * 3 + 1] = 0.8 * brightness; colors[i * 3 + 2] = 1.0 * brightness; } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); const material = new THREE.PointsMaterial({ size: 0.12, vertexColors: true, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false }); const points = new THREE.Points(geometry, material); scene.add(points); window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); function animate() { requestAnimationFrame(animate); points.rotation.y += 0.0008; controls.update(); renderer.render(scene, camera); } animate(); </script> </body> </html>

这个页面对应了 SKILL.md 里的哪几条规则?我拆开看一下:

  • setPixelRatio(Math.min(window.devicePixelRatio, 2))对应“限制像素比”;
  • WebGLRenderer创建失败时的降级提示对应“兼容性”;
  • resize事件对应“画布自适应”;
  • OrbitControls对应“交互控制”;
  • 粒子颜色用程序计算而不是外链贴图,对应“纹理能程序生成时,不使用外链图片”。

这正是 Skill 的意义。普通 Prompt 可能只告诉你“写一个粒子页面”,但 Sketch 里的这些规则让 AI 把每个工程细节都落实到了代码里。

5.3 调用 Skill 生成页面

Skill 写好后,你不需要手动“加载”它。在支持 Skill 的编程代理工具中,直接描述任务,Agent 会通过description判断是否调用这个 Skill。

请使用 web-3d-effect 技能,生成一个 3D 粒子星系页面。 要求: - 暗色背景,粒子呈螺旋星系形状 - 支持鼠标拖拽旋转和滚轮缩放 - 自适应窗口大小 - 移动端可正常浏览

写好任务说明后,Agent 会读取SKILL.md,按照“必做步骤”依次生成页面,并用“自检清单”检查结果。

如果你使用命令行形态的编程代理工具,调用方式大致如下(具体以你使用的工具文档为准):

# 以 Claude Code / Codex 的命令行形态为例 claude "使用 web-3d-effect skill 生成一个 3D 粒子星系页面,保存为 index.html" codex "调用 web-3d-effect skill 制作一个 3D 地球效果网页"

需要注意的是,不同工具对 Skill 的位置和命令格式要求不同。核心机制是相通的:Skill 文件放在工具可识别的目录中,你只需要在任务描述里触发它。

6. 运行结果与效果验证

页面生成后,最怕的就是双击打开,然后看着黑屏发呆。不要直接用file://打开,先启动一个本地服务器。

6.1 启动本地服务器

cd 你的项目目录 npx serve .

或者用 Python:

python3 -m http.server 8080

然后在浏览器中访问:

http://localhost:8080

6.2 预期效果

打开页面后,你应该看到:

  • 暗色星空背景下,一个由 15000 个粒子组成的螺旋星系;
  • 星系缓慢自转,粒子颜色呈现蓝紫渐变;
  • 鼠标拖拽可以旋转视角,滚轮可以缩放;
  • 缩放浏览器窗口,画布不会拉伸变形;
  • 页面左下角有“拖拽旋转 · 滚轮缩放”的提示。

6.3 如何判断生成质量

不要只看“能打开”就认为任务完成。按 SKILL.md 里的自检清单逐项核对:

  1. 打开浏览器控制台,确认没有报错;
  2. 缩放窗口,观察画布是否自适应;
  3. 用浏览器 Device Mode 模拟手机,确认触摸拖拽正常;
  4. 查看网络面板,确认资源体积没有过分夸张;
  5. 如果关闭 WebGL 硬件加速,页面应该显示降级提示而不是白屏。

这里真正容易踩坑的是第 5 点,很多 AI 生成的页面没有降级逻辑。WebGL 一旦不可用,页面直接黑屏,用户会以为是自己浏览器坏了。把降级提示写进SKILL.md,就能从源头避免这个问题。

7. 常见问题与排查思路

即使有了 Skill,代码也不会永远一次成功。下面这几个问题出现频率最高:

问题现象可能原因排查方式解决方案
Agent 没有自动调用 Skilldescription 描述不清,或 Skill 目录放错位置检查 Skill 目录路径,查看 Agent 日志在 description 中补充关键词,或手动指定 Skill 名称
页面打开黑屏WebGL 上下文创建失败、脚本报错打开控制台查看报错信息检查 CDN 和 importmap 地址,确认浏览器硬件加速已开启
纹理或模型加载失败使用file://打开页面,触发 CORS查看控制台网络错误改用本地 HTTP 服务器运行
移动端页面变形或卡顿缺少 viewport 设置,或像素比没有限制检查 head 中的 meta 标签,检查代码中的像素比设置添加 viewport,限制devicePixelRatio不超过 2
生成结果风格不稳定SKILL.md 缺少具体的质量要求和自检项查看 SKILL.md 是否包含步骤和自检清单补充技术栈、性能目标、交互要求和自检项
页面加载过慢使用了过大的模型或贴图资源打开网络面板查看资源大小改用程序生成的纹理,或者压缩模型资源

排查顺序也很重要。遇到问题时,优先看浏览器的 Console 和 Network 面板,确认是脚本报错还是资源加载失败。然后再去看 SKILL.md 是否有遗漏的规则,补齐后让 Agent 重新生成一次。

8. 最佳实践与工程建议

Skill 看起来只是写一个 Markdown 文件,但在实际项目中,设计和维护 Skill 的方式决定了它最终好不好用。

8.1 Skill 要“单文件主义”

一个 Skill 只聚焦一类任务。web-3d-effect只负责 3D 特效页面的生成,不要在里面塞“登录页面生成”或“表单校验”的内容。Skill 职责越单一,触发越精准,Agent 执行时也越不容易混淆。

8.2 description 写清楚触发条件

这是决定 Skill 能不能被正确调用的关键。写 description 时,不要只写“用于生成 3D 网页”,要把典型触发词都列出来,比如“3D 粒子”“3D 地球”“WebGL 展示”“三维可视化”。同时可以说明不适用的情况,比如“普通图表页面不要使用”。

8.3 把常用代码片段放进 examples

SKILL.md 是规则说明,examples 目录则是给 Agent 的参考实现。把一套稳定的 Three.js 初始化代码放进examples/,Agent 生成时会优先复制这套代码,而不是自己重新写一遍。这样能显著减少语法错误和版本兼容问题。

8.4 把 Skill 纳入版本管理

Skill 不是一次性配置,它会随着项目需求和踩坑记录不断迭代。建议把整个 Skill 目录放到 Git 仓库里统一管理。发现问题后,把修复措施补充到 SKILL.md 的“自检清单”中,让后续生成避开同一个坑。

8.5 注意安全边界

Skill 里如果包含脚本,要确保脚本只做声明范围内的事,不执行未经验证的下载内容,不请求未知的外部接口。如果让 Agent 生成页面时引入了第三方资源,先确认资源来源可信,再纳入项目。对涉及生产环境的操作,始终遵循最小权限原则——Skill 也一样,给它最小的执行范围,它就不会越界。

8.6 Skill 需要持续迭代

第一次写的 Skill 大概率不完美。用几次之后,你会发现某些地方没说清楚,某些生成结果还是不稳定。这时候不要急着骂 AI,回去改 SKILL.md,把新问题写进“必做步骤”或“自检清单”。Skill 本质上是一份被持续更新的“团队经验库”,迭代几次之后,它的稳定性会越来越高。

9. 总结:Skill 是把“一次性生成”变成“工程能力”的中间层

回到开头的问题:为什么用 AI 写 3D 特效网页时,生成结果总是不稳定?

因为普通 Prompt 是一次性的,模型每次都要重新猜测你的需求。而 Skill 把“3D 特效网页该怎么做”沉淀成了固定的规则和资源包,Agent 每次执行时都先读规则,再动手写代码。它换来的不是一次性的“炫酷”,而是可复现的“稳定”。

这篇文章里,我拆了 Skill 和 Prompt、Agent 的区别,给了一个可直接复制的web-3d-effectSkill,并用一个 3D 粒子星系页面演示了从调用、生成到验证的完整流程。核心知识点有三个:

  1. Skill 的本质是给 Agent 用的工作手册,重点不是“写很多字”,而是把步骤、约束、自检清单写清楚;
  2. 3D 网页特效比普通页面更依赖 Skill,因为它的工程细节太多,靠一次性 Prompt 管不住;
  3. 验证生成结果时,不要只看“能打开”,要按 SKILL.md 里的自检清单逐项核对。

接下来你可以做两件事:第一,把文中的SKILL.md放到你自己的工具目录里,跑通一次粒子星系页面;第二,根据你的项目需求去改这个 Skill——比如加入“3D 地球”“数字人展示”“数据可视化大屏”等细分场景的规则。

Skill 不会让 AI 一步到位地解决所有问题,但它能把“偶尔成功”变成“稳定成功”。剩下那些 AI 做不好的部分,仍然需要你来补齐——而这正是工程师的价值所在。

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

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

立即咨询