diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d6ad5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +node_modules/* diff --git a/README.md b/README.md index 8b958e1..60ed387 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,158 @@ -# 项目名称 +
+ Vibe-Coding +
+
+

Vibe-Coding

+ GitHub stars + GitHub forks + Language + GitHub Project +
-*这里写项目的各类介绍信息、背景、动机等内容* +
-## 项目受众 +[中文](./README.md) | [English](./README.md) -*注:这里写你的项目所面向的受众,本项目能给他们带来什么,对他们的基础能力要求有哪些* +
-## 目录 -*这里写你的项目目录,已完成的部分用添加上跳转链接* -- [第1章](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter1/chapter1.md) -- [第2章](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter2) - - [2.1 我是2.1的标题](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter2/chapter2_1.md) - - [2.2 我是2.2的标题](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter2/chapter2_2.md) -- [第3章](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter3) - - [3.1 我是3.1的标题](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter3/chapter3_1) - - [3.1.1 我是3.1.1的标题](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter3/chapter3_1/chapter3_1_1.md) - - [3.1.2 我是3.1.2的标题](https://github.com/datawhalechina/repo-template/blob/main/docs/chapter3/chapter3_1/chapter3_1_2.md) - - 3.2 我是3.2的标题 -- 第4章 - - 4.1 我是4.1的标题 - - 4.2 我是4.2的标题 +
+

📚 Vibe Coding 原生 AI 应用实践教程

+

从零开始掌握 vibe coding,动手构建你的第一个 AI 原生应用

+
-## 贡献者名单 +## 项目介绍 -| 姓名 | 职责 | 简介 | -| :----| :---- | :---- | -| 小明 | 项目负责人 | 一个理想主义者 | -| 小红 | 第1章贡献者 | 小明的朋友 | -| 小强 | 第2章贡献者 | 小明的朋友 | +> 这是一个面向所有想要学习 vibe coding 并构建原生 AI 应用的人的综合课程。本教程是 THU SIGS AIID 实践课程的一部分。受开源启发,所有资料向任何对 vibe coding 和原生 AI 应用感兴趣的人开放。我们希望这一资源能帮助每一个决心创造自己原生 AI 应用的人,并共同改变传统的编程习惯。我们相信未来的编程工作 95% 将由 AI 完成。 -*注:表头可自定义,但必须在名单中标明项目负责人* +本项目聚焦「原生 AI 应用开发」这一新范式,通过循序渐进的项目实战,帮助你在真实工程环境中掌握 vibe coding 的方法论与最佳实践。 + +### 项目受众 + +本项目主要面向希望系统学习 vibe coding 与原生 AI 应用开发的学习者,包括但不限于: + +- 计算机科学、人工智能及相关或跨学科专业的学生,希望通过实践经验系统地介绍 vibe coding 和原生 AI 开发 +- 具有基本编程技能的初中级开发人员,希望学习 vibe coding 并构建原生 AI 应用程序 +- 开源爱好者和独立开发者,希望降低 AI 原生开发的门槛,并利用免费资源创建衍生作品 +- 旨在快速制作原生 AI 应用程序原型的企业技术团队和 AI 初创公司 +- 非开发人员角色(如产品经理),希望掌握基本开发任务并为 AI 计划提供技术支持 + +### 你将收获什么? + +- 理解什么是 vibe coding 以及它与传统编程方式的差异 +- 掌握从零到一设计和实现原生 AI 应用的基本路径 +- 通过多个完整项目,熟悉游戏、工具类、产品原型等不同形态的 AI 应用开发 +- 了解并实践 Git、API、RAG、AI IDE、Zeabur 等关键工具与基础设施 +- 在实践中形成一套可复用的 AI 原生产品开发工作流 + +## 内容导航 + +### Project 部分 + +| 章节 | 关键内容 | 状态 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---- | +| [Project 1: 如何构建贪吃蛇游戏](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter1/chapter1-how-to-build-a-snake-game.md) | 结合 AI 与代码实现经典贪吃蛇游戏 | ✅ | +| [Project 2: 探索 AI 工具的能力边界](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md) | 系统体验与分析不同 AI 工具的能力边界 | ✅ | +| [Project 3: Dify 入门与知识库集成](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md) | 使用 Dify 搭建应用并完成知识库集成 | ✅ | +| [Project 4: 一起做霍格沃茨画像](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter4/chapter4-lets-build-hogwarts-portraits.md) | 基于多模态能力生成与迭代人物画像 | ✅ | +| [Project 5: 从数据库到 Supabase](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter5/chapter5-from-database-to-supabase.md) | 从传统数据库迁移到 Supabase 并完成应用接入 | ✅ | +| [Project 6: 别急着写代码,先想一个好点子](https://github.com/datawhalechina/vibe-coding/blob/main/docs/project/chapter6/chapter6-no-code-without-an-idea.md) | 产品思维入门,从 0 构思并实现一个原生 AI 应用原型,涉及第一个应用 | ✅ | +| Project 7: 构建第一个现代应用程序-UI设计 | 学会更高级的前端组件库的使用方式,前端编辑工具的深入用法,学习设计规范 | 🚧 | +| Project 8: 构建第一个现代应用程序-功能设计 | 市场调研、学习产品 PRD 构建、深入理解产品原型设计、构架多页面产品原型和代码 | 🚧 | +| Project 9: 构建第一个现代应用程序-全栈应用 | 构建与部署带鉴权与独立后端的应用、从参考到设计开发出可上线网页应用的完整闭环 | 🚧 | + +### 扩展知识部分 + +| 章节 | 关键内容 | 状态 | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---- | +| [扩展知识 1: 什么是 Git 和 GitHub](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra1/extra1-what-is-git-and-what-is-github.md) | 版本控制基础与 GitHub 使用入门 | ✅ | +| [扩展知识 2: 什么是 API](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra2/extra2-what-is-api.md) | API 基本概念与在 AI 应用中的使用方式 | ✅ | +| [扩展知识 3: AI 能力入门手册](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra3/extra3-ai-capability-starter-handbook.md) | 关于深度学习 AI 涉及到的所有主流模型与常见能力速览 | ✅ | +| [扩展知识 4: 什么是 AI IDE 和 Trae](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra4/extra4-what-is-ai-ide-and-trae.md) | 了解 AI IDE 概念并上手 Trae 辅助开发 | ✅ | +| [扩展知识 5: 什么是 RAG 以及它如何工作](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md) | RAG 原理、实现路径及未来演进方向 | ✅ | +| [扩展知识 6: Zeabur 与 Web 应用部署](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md) | 使用 Zeabur 将 Web 与 AI 应用部署上线 | ✅ | +| [扩展知识 7: CLI AI 编程工具与测试驱动开发原则](https://github.com/datawhalechina/vibe-coding/blob/main/docs/Extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md) | CLI AI 编程工具与测试驱动开发原则 | ✅ | +| 扩展知识 8: MCP 与 ClaudeCode skills | MCP 与 ClaudeCode skills 的概念与方式深入解析 | 🚧 | +| 扩展知识 9: 如何让 Coding Tools 长时间工作 | 如何通过配置让 ClaudeCode、Codex 等 CLI 工具连续工作数小时 | 🚧 | + +### 实践项目部分 + +该部分将会覆盖常见电脑应用形式的 0-1 Vibe Coding 开发过程。 + +| 章节 | 关键内容 | 状态 | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ---- | +| [Example 1: 如何构建微信小程序](https://github.com/datawhalechina/vibe-coding/blob/main/docs/examples/example1/example1-how-to-build-a-wechat-miniprogram.md) | 使用 vibe coding 从 0 到 1 构建微信小程序 | ✅ | +| Example 2: 如何构建微信小程序-包含后端 | 使用 vibe coding 从 0 到 1 构建具有后端数据库的微信小程序 | 🚧 | +| Example 3: 如何构建安卓程序 | 结合 Expo,使用 vibe coding 从 0 到 1 构建安卓应用并上架 | 🚧 | + +## 如何学习 + +- 建议具备基本编程经验(任意一门语言均可),并对 AI 与产品开发有兴趣 +- 按照 Project 模块从 0 到 6 依次实践,完成从小游戏到完整应用原型的进阶 +- 在 Extra 模块中补充 Git、API、RAG、部署等通识知识,完善你的 AI 开发知识图谱 +- 遇到问题时优先尝试自己排查与检索,再对照教程与源码进行比对和反思 + +你可以根据个人时间与需求,选择性地阅读和实践相关章节,但推荐至少完成全部 Project,以形成一套完整的实践闭环。 + +## 本地启动本课件 + +### 现代方案 + +在 AI IDE 对话框(vscode、cursor、trae 等)中,输入下列提示词启动本课件: + +``` +请你帮我运行这个项目的本地服务 +``` + +### 旧方案 + +1. npm install +2. npm run dev +3. 打开浏览器访问 `http://localhost:3000` 即可查看。 ## 参与贡献 -- 如果你发现了一些问题,可以提Issue进行反馈,如果提完没有人回复你可以联系[保姆团队](https://github.com/datawhalechina/DOPMC/blob/main/OP.md)的同学进行反馈跟进~ -- 如果你想参与贡献本项目,可以提Pull request,如果提完没有人回复你可以联系[保姆团队](https://github.com/datawhalechina/DOPMC/blob/main/OP.md)的同学进行反馈跟进~ -- 如果你对 Datawhale 很感兴趣并想要发起一个新的项目,请按照[Datawhale开源项目指南](https://github.com/datawhalechina/DOPMC/blob/main/GUIDE.md)进行操作即可~ +- 如果你发现了一些问题,可以提 Issue 进行反馈,如果提完没有人回复你可以联系[保姆团队](https://github.com/datawhalechina/DOPMC/blob/main/OP.md)的同学进行反馈跟进~ +- 如果你想参与贡献本项目,可以提 Pull Request,如果提完没有人回复你可以联系[保姆团队](https://github.com/datawhalechina/DOPMC/blob/main/OP.md)的同学进行反馈跟进~ +- 如果你对 Datawhale 很感兴趣并想要发起一个新的项目,请按照[Datawhale 开源项目指南](https://github.com/datawhalechina/DOPMC/blob/main/GUIDE.md)进行操作即可~ + +## 🙏 感谢每位贡献者 + +- [散步-项目负责人](https://github.com/sanbuphy) (Datawhale成员) +- 方可-指导老师(Datawhale成员, 清华大学) +- 赵芷霖(实践项目部分-清华大学) +- [李亦萱](https://yixuan20.github.io/)(页面美术设计-清华大学) +- AI Vibe Coding 101 内测群完整给建议体验的小伙伴们 + +### 特别感谢 + +- 感谢 [@Sm1les](https://github.com/Sm1les) 对本项目的帮助与支持 +- 感谢所有为本项目做出贡献的开发者们 ❤️ + +
+ + + +
## 关注我们

扫描下方二维码关注公众号:Datawhale

- +
## LICENSE -知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 - -*注:默认使用CC 4.0协议,也可根据自身项目情况选用其他协议* + + 知识共享许可协议 + +
+本作品采用 + + 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 + +进行许可。 diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..31f9efc Binary files /dev/null and b/assets/logo.png differ diff --git a/docs/README.md b/docs/README.md index 8b13789..57c5708 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1 +1,24 @@ +# Vibe Coding 101 课件 +欢迎来到 Vibe Coding 101,你可以从左侧的 Project 和 Extra 模块进入具体内容。 + +## Project +- [学习地图](project/chapter0-learning-map/chapter0-learning-map.md) +- [Project 1: 如何构建贪吃蛇游戏](project/chapter1/chapter1-how-to-build-a-snake-game.md) +- [Project 2: 探索 AI 工具的能力边界](project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md) +- [Project 3: Dify 入门与知识库集成](project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md) +- [Project 4: 一起做霍格沃茨画像](project/chapter4/chapter4-lets-build-hogwarts-portraits.md) +- [Project 5: 从数据库到 Supabase](project/chapter5/chapter5-from-database-to-supabase.md) +- [Project 6: 没有 idea 就没有代码](project/chapter6/chapter6-no-code-without-an-idea.md) + +## Extra +- [扩展知识 1: 什么是 Git 和 GitHub](extra/extra1/extra1-what-is-git-and-what-is-github.md) +- [扩展知识 2: 什么是 API](extra/extra2/extra2-what-is-api.md) +- [扩展知识 3: AI 能力入门手册](extra/extra3/extra3-ai-capability-starter-handbook.md) +- [扩展知识 4: 什么是 AI IDE 和 Trae](extra/extra4/extra4-what-is-ai-ide-and-trae.md) +- [扩展知识 5: 什么是 RAG 以及它如何工作](extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md) +- [扩展知识 6: Zeabur 与 Web 应用部署](extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md) +- [扩展知识 9: CLI AI Coding 工具与 TDD 原则](extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md) + +## Examples(完整实战项目) +- [完整实战项目 1: 如何写一个微信小程序](examples/example1/example1-how-to-build-a-wechat-miniprogram.md) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 0c0275a..3561d90 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,8 +1,22 @@ -- 第1章 [我是第1章的标题](chapter1/chapter1) -- 第2章 我是第2章的标题 - - [2.1 我是2.1的标题](chapter2/chapter2_1) - - [2.2 我是2.2的标题](chapter2/chapter2_2) -- 第3章 我是第3章的标题 - - 3.1 我是3.1的标题 - - 3.1.1 [我是3.1.1的标题](chapter3/chapter3_1/chapter3_1_1) - - 3.1.2 [我是3.1.2的标题](chapter3/chapter3_1/chapter3_1_2) +* [首页](/README.md) + +* Project + * [学习地图](/project/chapter0-learning-map/chapter0-learning-map.md) + * [Project 1: 如何构建贪吃蛇游戏](/project/chapter1/chapter1-how-to-build-a-snake-game.md) + * [Project 2: 探索 AI 工具的能力边界](/project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md) + * [Project 3: Dify 入门与知识库集成](/project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md) + * [Project 4: 一起做霍格沃茨画像](/project/chapter4/chapter4-lets-build-hogwarts-portraits.md) + * [Project 5: 从数据库到 Supabase](/project/chapter5/chapter5-from-database-to-supabase.md) + * [Project 6: 没有 idea 就没有代码](/project/chapter6/chapter6-no-code-without-an-idea.md) + +* Extra + * [扩展知识 1: 什么是 Git 和 GitHub](/extra/extra1/extra1-what-is-git-and-what-is-github.md) + * [扩展知识 2: 什么是 API](/extra/extra2/extra2-what-is-api.md) + * [扩展知识 3: AI 能力入门手册](/extra/extra3/extra3-ai-capability-starter-handbook.md) + * [扩展知识 4: 什么是 AI IDE 和 Trae](/extra/extra4/extra4-what-is-ai-ide-and-trae.md) + * [扩展知识 5: 什么是 RAG 以及它如何工作](/extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md) + * [扩展知识 6: Zeabur 与 Web 应用部署](/extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md) + * [扩展知识 9: CLI AI Coding 工具与 TDD 原则](/extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md) + +* Examples + * [完整实战项目 1: 如何写一个微信小程序](/examples/example1/example1-how-to-build-a-wechat-miniprogram.md) diff --git a/docs/chapter1/chapter1.md b/docs/chapter1/chapter1.md deleted file mode 100644 index 27733e6..0000000 --- a/docs/chapter1/chapter1.md +++ /dev/null @@ -1,24 +0,0 @@ -# 我是第1章的标题 -我是第1章的正文,下面给出插入图片的两种方式,分别为markdown语法和html语法。 - -markdown语法代码如下: -```markdown -![图1.1 我是图片名称](./images/1_1.jpeg) -``` -效果如下: -![图1.1 我是图片名称](./images/1_1.jpeg) - -markdown语法简洁明了,但是其无法控制图片的大小,因此有图片缩放需求时可使用html语法,html语法代码如下: -```html -
-< img width="300" src="./images/1_1.jpeg"/> -
-
图1.1 我是图片名称
-``` -效果如下: -
- -
-
图1.1 我是图片名称
- - diff --git a/docs/chapter1/images/1_1.jpeg b/docs/chapter1/images/1_1.jpeg deleted file mode 100644 index df9771b..0000000 Binary files a/docs/chapter1/images/1_1.jpeg and /dev/null differ diff --git a/docs/chapter1/images/test b/docs/chapter1/images/test deleted file mode 100644 index 58c9bdf..0000000 --- a/docs/chapter1/images/test +++ /dev/null @@ -1 +0,0 @@ -111 diff --git a/docs/chapter2/chapter2_1.md b/docs/chapter2/chapter2_1.md deleted file mode 100644 index fdb4742..0000000 --- a/docs/chapter2/chapter2_1.md +++ /dev/null @@ -1,2 +0,0 @@ -# 我是2.1的标题 -我是2.1的正文 diff --git a/docs/chapter2/chapter2_2.md b/docs/chapter2/chapter2_2.md deleted file mode 100644 index d0d5dd6..0000000 --- a/docs/chapter2/chapter2_2.md +++ /dev/null @@ -1,2 +0,0 @@ -# 我是2.2的标题 -我是2.2的正文 diff --git a/docs/chapter3/chapter3_1/chapter3_1_1.md b/docs/chapter3/chapter3_1/chapter3_1_1.md deleted file mode 100644 index 9692cc7..0000000 --- a/docs/chapter3/chapter3_1/chapter3_1_1.md +++ /dev/null @@ -1,2 +0,0 @@ -# 我是3.1.1的标题 -我是3.1.1的正文 diff --git a/docs/chapter3/chapter3_1/chapter3_1_2.md b/docs/chapter3/chapter3_1/chapter3_1_2.md deleted file mode 100644 index d6f76e3..0000000 --- a/docs/chapter3/chapter3_1/chapter3_1_2.md +++ /dev/null @@ -1,2 +0,0 @@ -# 我是3.1.2的标题 -我是3.1.2的正文 diff --git a/docs/examples/example1/example1-how-to-build-a-wechat-miniprogram.md b/docs/examples/example1/example1-how-to-build-a-wechat-miniprogram.md new file mode 100644 index 0000000..3ccfc42 --- /dev/null +++ b/docs/examples/example1/example1-how-to-build-a-wechat-miniprogram.md @@ -0,0 +1,447 @@ +# 如何构建一个最简单的微信小程序 +# 1. 什么是微信小程序和微信小程序开发 + +在这篇教程中,我们将完整跑通一条闭环:从脑海中的一个想法,到在微信里可以搜索、可以扫码打开的真实小程序。 + +开始动手之前,我们需要先建立两个基本认知。 + +第一个是 **本质**:微信小程序到底是什么?它和普通 App、网页有什么不同?为什么这么多产品会选择用小程序这种形态?只有理解了小程序的底层逻辑,你才能判断自己的想法是否适合用小程序来实现。 + +第二个是 **路径**:当你说「我要做一个小程序」时,从零到上线的完整路径是什么样的?这条路上有哪些关键节点——构思阶段要考虑什么、开发环境怎么搭建、如何用 AI 辅助开发提高效率、模拟器调试有哪些坑、测试号和正式发布各自解决什么问题。把整个流程在脑子里先跑一遍,后面实操时才不会迷路。 + +搞清楚这两个问题之后,我们就可以正式进入开发环节了。接下来,让我们先从第一个问题开始:微信小程序究竟是什么。 + +## 1.1 微信小程序 + +微信小程序可以看成藏在微信里的应用。它不需要你去应用商店搜索、下载、安装,只要在微信里搜名字、扫码,或者点开别人分享的卡片,就能马上使用。用完直接关掉,下次需要再打开即可,不会长期占据你的手机桌面和存储空间。 + +对普通用户来说,小程序解决的是很多「一件小事」:查快递、点咖啡、看订单、玩一局小游戏。打开速度快、入口统一在微信里,这是它最大的体验特征。 + +对企业和开发者来说,小程序是一种可以被搜索、被分享的「小应用形态」。只要在微信公众平台上注册、配置好信息,通过审核,小程序就能对所有微信用户开放。和传统 App 相比,它更容易获得第一批用户,因为大家已经习惯在微信里完成很多事情。 + +在本教程里,我们不会做复杂的业务系统,而是选一个很经典的例子——贪吃蛇小游戏。它体量小、逻辑清晰,却又包含了一个完整小程序需要具备的元素:多个页面、简单交互、状态变化、分数记录等,非常适合作为你的第一个作品。 + +## 1.2 微信小程序开发 + +理解了「小程序是什么」,接下来要回答的问题是:那开发一个小程序,到底要做什么? + +你需要有一个清晰的目标(例如:做一个可以随时玩一局的贪吃蛇),设计出用户会看到的界面,告诉系统在不同操作下应该发生什么,并最终把这个作品发布出去。 + +在传统的开发流程里,上面这些步骤往往都由程序员主导,需要写大量代码。而在 AI 辅助开发的场景下,这件事可以拆得更细:你负责讲清楚想做什么,AI 帮你完成大部分具体怎么写。 这也意味着,对于刚入门的人来说,最重要的能力不再是背多少语法,而是能不能把需求描述清楚、能不能读懂 AI 给出的结果。 + +## 1.3 微信小程序开发的几种方法 + +真正做小程序时,大家采用的技术路线并不完全一样。为了避免你一上来就被各种名词淹没,我们只做一个粗略分类,让你知道常见的几条路分别长什么样。 + +第一种方式,是直接使用微信官方提供的原生能力。在微信开发者工具中创建项目后,你会看到一组固定的文件类型,用它们来描述页面结构、样式和逻辑。这种方式贴近官方文档,控制力强,但对第一次接触前端的人来说,学习曲线会稍微曲折一些。 + +第二种方式,是利用多端框架,比如 uni-app 等。你在本地主要是写类似网页的代码(例如 .vue 文件),然后通过框架把这套代码转换成微信小程序可以识别的形式。这样的好处是:结构更统一,以后如果想把产品发布到其他平台(比如 H5、App),改动会相对少一些。 + +基于以上两种方式,本教程会重点讲述使用 AI 辅助开发工具的小程序开发SOP。比如把整个项目在 Trae 里打开,然后直接对内置的 AI 助手说: 请帮我在这个文件里加一个首页,有标题和按钮——请帮我写一个游戏页面,可以显示贪吃蛇和分数,AI 会在理解现有代码的基础上,为你生成新的代码片段,或者帮你修改、重构。 + +这三种方式并不是互斥的。你完全可以在一个 uni-app 项目里,借助 Trae 的 AI 功能来完成大部分编码工作。关键不是选哪一个方法,而是知道:自己现在处在什么位置,以及有哪些工具可以用。 + +## 1.4 本文介绍的微信小程序开发步骤(粗略预览) + +本教程会带着**从环境到成品**的节奏,专门围绕贪吃蛇这个例子,结合 Trae 的 vibecoding 方式,把整个过程拆成一条你可以反复复用的路线。整体上,你将在后面的章节里经历这样几个阶段: + +1. 先搭建认知地基:弄清楚什么是微信小程序、常见的开发方式有哪些,以及我们要做的这款贪吃蛇小程序面向谁、在什么场景被使用。 +2. 然后完成环境准备:注册小程序账号,安装 HBuilderX、Trae 和微信开发者工具,并用 HBuilderX 创建一个可以在微信开发者工具中跑起来的基础项目骨架,让屏幕上先出现一个最简单的页面。 +3. 接下来进入正式开发:在 Trae 中打开这个项目,用 vibecoding 的方式和 AI 对话,一步步生成首页和游戏页的布局,实现蛇移动、吃食物、游戏结束等核心玩法。 +4. 在功能跑通之后,学会把 AI 当成「调试和重构伙伴」:遇到 bug 时请它一起排查,觉得结构乱时让它帮忙整理,并逐渐加上开始 / 暂停、最高分记录、界面微调等细节体验。 +5. 最后进入发布环节:把项目构建成微信可识别的版本,在微信开发者工具中做预览和真机测试,先以测试号和体验版的形式上线验证流程,完成备案和审核后,再把小程序正式发布出去,让别人也能在微信里搜索到、玩到你的作品。 + +这一节只负责把全景图画出来,不展开具体命令和代码。你现在需要做的只是先大致记住这 5 步: **理解 → 搭环境 → vibecoding 开发 → 调试打磨 → 构建发布** 。后面的章节会在每一步上慢慢放大,告诉你要准备什么、要和 AI 说什么,以及在每个阶段你应该在屏幕上看到怎样的结果。 + +# 2. 环境准备 + +在开始写任何一行代码之前,我们先把开发环境准备好。 这一部分的目标,是让你在后面的章节里不再纠结 **去哪儿下载软件、为什么运行不了** ,而是可以直接把注意力放在和 AI 对话、实现需求上。 + +你只需要会打开浏览器、下载文件、双击安装程序,就可以完成本节全部步骤。 + +## 2.1 本教程会用到的三个工具 + +整个贪吃蛇小程序的开发,我们会同时用到三个工具,它们各自负责不同的环节: + +1. 第一个是 Trae。你可以把它理解为一款集成了 AI 的代码编辑器,既能像普通 IDE 一样打开项目文件,又能让你直接用自然语言和 AI 交流,请它帮你写代码、改代码、解释代码。本教程里,大部分「和 AI 一起写小程序」的操作,都会在 Trae 里完成。你可以在浏览器中访问 https://www.trae.cn 获取最新版本。 +2. 第二个是 HBuilderX。它是一款对 Vue 和 uni-app 支持特别好的编辑器,官方提供了很多现成的小程序项目模板。我们会用它来「一键生成」一个基础的小程序项目,相当于先打好地基,再把地基交给 Trae 和 AI 去改造。HBuilderX 的下载地址是 https://www.dcloud.io/hbuilderx.html 。 +3. 第三个是微信开发者工具。这是微信官方提供的专门用来开发和预览小程序的工具。它负责把你写好的项目在电脑上跑起来,并支持在手机上进行真机调试。你可以从 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 下载适合你操作系统的版本。 + +简单总结一下:HBuilderX 帮你快速建一个小程序项目,Trae 帮你和 AI 一起写代码,微信开发者工具帮你看到真正运行中的小程序。 + +## 2.2 注册微信公众平台账号并获取 AppID + +有了工具,还需要一个 **小程序身份** ,这一步在微信公众平台上完成。 如果你之前从来没有注册过微信小程序,可以按照下面的顺序来做: + +1. 在浏览器地址栏输入 https://mp.weixin.qq.com ,打开微信公众平台网页,用你的微信扫码登录。 + +![](images/image1.png) + +2. 在首页选择「小程序」,按照页面提示完成注册流程,填写邮箱、手机号以及主体类型(个人或企业)。 + ![](images/image2.png) +3. 注册成功并进入后台后,找到「开发管理」或「开发设置」页面,就能看到一个唯一的编号,名字叫 AppID 。这个编号后面会用在项目配置里,相当于你这个小程序在微信里的身份证。 + +![](images/image3.png) + +建议你把 AppID 记录在一个方便找到的地方。后续章节在配置项目时,我们会直接把这个值填进去,让本地项目和线上小程序对应起来。 + +## 2.3 安装微信开发者工具 + +接下来,我们需要一个地方实际运行和预览小程序,这就是微信开发者工具存在的意义。 + +1. 访问下载页面 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 。 在这个页面上,你会看到针对不同操作系统的多个版本,通常选择与你电脑系统匹配的稳定版即可,比如 Windows 64 位或 macOS 版本。 +2. 下载完成后,双击安装包,按照安装向导一步步点击下一步。如果你不清楚要改什么设置,保持默认选项就可以。 +3. 安装结束后,从桌面或开始菜单启动微信开发者工具。首次启动时,它会在屏幕上显示一个二维码,提示你用手机微信扫码登录。用自己的微信扫码并确认授权后,就可以进入主界面。 + +![](images/image4.png)![](images/image5.png) + +后面当我们在 Trae 中准备好项目文件之后,就会把构建好的小程序导入到微信开发者工具里,在这里看到真实的运行效果。 + +## 2.4 准备 Trae 和 HBuilderX + +最后,我们把真正负责写项目的两个工具安装好:Trae 和 HBuilderX。 + +你可以 **先安装 Trae** 。打开浏览器访问 https://www.trae.cn ,根据页面提示下载适合你系统的版本。安装过程和普通软件一样,双击安装包,按提示完成即可。安装完成后,你会得到一个可以打开本地文件夹、查看代码、和 AI 对话的 IDE,后续所有 vibecoding 步骤都会在这里进行。 + +![](images/image6.png) + + **接着安装 HBuilderX** 。访问 https://www.dcloud.io/hbuilderx.html ,选择对应操作系统的发行包下载。HBuilderX 的包体非常小,启动速度也很快。安装完成后,你可以先熟悉一下它的界面,不需要深入研究功能;在后面的章节中,我们会用它来创建一个 uni-app 小程序模板,作为整个项目的起点。 + +![](images/image7.png) + +完成本节的所有步骤之后,你已经具备了完整的开发环境:有微信小程序的账号和 AppID,有可以预览的小程序运行环境,也有可以和 AI 一起写代码的 IDE。在接下来的部分,我们会从**创建第一个小程序项目骨架**开始,让这些工具真正跑起来。 + +## 2.5基础文件准备 + +1. 点击新建项目 + +![](images/image8.png) + +2. 选择默认模板,给小程序起名,选择存放路径,带你及右下角的创建: + +![](images/image9.png) + +3. 显示创建成功! + +![](images/image10.png) + +4. 接着可以在对应的文件夹中找到该文件夹,在Trae里面打开该文件夹,可以看到地基文件已经全部建造好: + +![](images/image11.png) + +# 3. 小程序开发 + +前面两部分,我们已经搞清楚了「小程序是什么」、以及「环境怎么配、工具怎么装」。 从这一节开始,正式进入实战:不再停留在概念层面,而是让 AI 真正帮你把贪吃蛇小程序从无到有做出来。 + +这一节,你会完整走完一次「开发环节」的 SOP,大致包括几步: + +1. 在 Trae 里把当前项目打开,给 AI 下第一条完整指令,让它基于现有骨架设计并实现一个可运行的贪吃蛇版本。 +2. 让 Trae 直接改动真实项目文件,而不是只给你“示例代码”,并学会用回退功能在需要时恢复到修改前的状态。 +3. 回到 HBuilderX 和微信开发者工具,通过「运行到小程序模拟器」的方式在模拟器里试玩这一版,实现从“代码视角”到“用户视角”的切换。 +4. 根据试玩结果,用自然语言持续提出修改需求,让 AI 帮你从按键控制迭代到摇杆控制,顺便体验一次「发现问题 → 描述问题 → AI 修复 → 再次验证」的闭环。 + +你当然可以选择先在开发前,把每个页面、每个按钮都想得一清二楚,再交给 AI。 但对完全小白来说,小程序的界面和交互设计本身也是一个全新的领域(后面我们会教你怎么用 AI 帮忙做设计),所以在这一次,我们刻意采用另一种方式: 先开干 ——让 AI 先生成一个能跑起来的版本,再一边看效果、一边用自然语言慢慢打磨和调整。 + +## 3.1 把需求一次性说清楚:给 Trae 下第一条“总指令” + +打开 Trae,载入前面已经准备好的小程序项目之后,我先没有急着改某一行代码,而是对内置的 AI 助手说了这样一件事: + +**我向AI“发号施令”,说我现在需要基于现在的框架写一个贪吃蛇小程序,请给设计此小程序并我写一个prompt。** + +也就是说,我不是「一点点要求它写某个函数」,而是先抛出一个完整目标,让 AI 帮我规划,但是AI不仅帮我做了计划,还直接落地第一版实现。 + +Trae 收到这条指令之后,会自动阅读当前项目结构,判断应该在哪些文件里增加页面、在哪些地方补充逻辑,然后直接对项目中的文件或代码做出修改,而不需要你自己去手搓代码或者增删改查文件/文件夹。 + +## 3.2 让 AI 自动修改代码,而不是“手搓” + +当你在 Trae 中点击执行这条指令时,AI 会进入一个「帮你改工程」的流程。 在这个过程中,你可以看到几个关键点: + +1. 它会在对话区解释自己的思路,比如会在哪个目录下新增页面、打算如何组织游戏逻辑。 + +![](images/image12.png)![](images/image13.png) + +2. 它会直接对真实项目文件做增删改,而不是只给你一段「示例代码」让你自己拷贝。 +3. 修改完成后,Trae 会生成一个简短的小结,告诉你:这次它改了哪些文件,大致做了哪些事情。 + +如果你对这一轮修改不满意(或者觉得某一步有问题),也不用紧张。Trae 在你发出对话的对话框外的左上角提供了「回退」能力,你可以一键把工程恢复到本次指令执行之前的状态,相当于给这次操作加了一个安全的撤销键。 + +![](images/image14.png) + +![](images/image15.png) + +## 3.3 在 HBuilderX 和微信开发者工具中查看效果 + +AI 完成第一轮开发之后,代码已经落在项目里了,但这时候你还没有看到玩家视角的效果。 下一步,我们需要把它跑起来。 + +具体做法是:回到 HBuilderX,找到顶部菜单的「运行」选项,选择「运行到小程序模拟器」中的「微信开发者工具」。这一操作会触发项目编译,并将结果交给微信开发者工具打开。 + +![](images/image16.png) + +底部的输出窗口会显示编译的过程。如果最终状态是「ready」且没有报错,就说明构建成功,你可以切到微信开发者工具里查看这一版小程序的界面和功能。 + +![](images/image17.png) + +在大多数情况下,HBuilderX 会自动帮你打开微信开发者工具,让你直接看到新的小程序。如果没有自动打开,你可以按下面的方式处理: + +1. 先在 HBuilderX 中停止当前运行。 +2. 手动启动微信开发者工具,让它处于打开状态。 +3. 回到 HBuilderX,再次点击「运行 → 运行到小程序模拟器 → 微信开发者工具」。 + +这样我们就可以微信小程序开发者工具中看到我们Vibecoding的小程序: + +![](images/image18.png) + +## 3.4 用自然语言反复调整和完善小程序,直到我们满意 + +在这次实践中,AI 一开始给我生成的是按键控制方向的贪吃蛇:屏幕上有四个方向按钮,点击不同方向,蛇就会改变运动方向。 功能上完全可以玩,但我个人更喜欢用摇杆控制。对于你的调整需求(不仅限于功能、UI设计、界面,等你熟练后,你甚至可以用自然语言让AI帮你接入其他大模型的API或接入数据库)——再强调一遍,你只需要用自然语言告诉大模型即可。 + +这就是 vibecoding 的优势所在:你不必自己去翻代码,查找事件绑定的位置、计算坐标的逻辑,而是直接把想法告诉 AI。例如,你可以在 Trae 的对话框里这样描述: + + 把按键换成摇杆控制,并且用户松开摇杆时蛇保持同方向移动,直到用户再次松开摇杆。 + +只要你把需求讲得足够清楚,AI 会自动定位到对应界面和逻辑文件,替你完成控件样式、交互绑定和方向处理等改动。 + +![](images/image19.png) + +修改完成后,再回到微信开发者工具中查看。 如果没有立刻看到变化,可以尝试点击开发者工具上的「运行」按钮,或者刷新小程序预览窗口,让最新的构建结果生效。 仍然没有更新时,可以在 HBuilderX 中先停止运行,再重新执行一次「运行到小程序模拟器」,即可看到调整之后的小程序: + +![](images/image20.png) + +## 3.5 出现问题怎么办:继续用自然语言沟通 + +AI 生成的版本不一定一开始就完美。有时候你会遇到这些情况: + +* 运行时报错,小程序无法正常打开; +* 功能大致正确,但细节和你想象的不太一样; +* 界面可以用,但你觉得还可以更好看或更顺手。 + +在这些时候,不需要自己钻进代码里盲改,而是可以把遇到的问题直接用自然语言重新描述给 Trae 中的 AI 助手,例如: + + 现在摇杆控制已经生效了,但有时候蛇会突然停止不动,请帮我检查当前实现哪里有问题。 或者: 现在游戏可以玩,但界面有点拥挤,我希望在手机上显示时上下留出更多空白。请你帮我调整布局。 + +AI 会根据你当前的项目状态和描述,给出修改建议并直接应用在代码里。如果修改之后结果更糟或方向不对,你依然可以使用回退,把工程恢复到前一个稳定版本,再换一种说法尝试。 + +通过这几轮往返,你会从最初的“毛坯版本”,逐步打磨出一个更贴近自己喜好的摇杆版贪吃蛇小程序。例如我给出了一种图画风格,让AI根据此风格来调整小程序的UI风格: + +![](images/image21.png) + +## 3.6 最终成品与本节小结 + +经过一轮又一轮的 **自然语言叙述 → AI 修改 → 在微信开发者工具中预览 → 继续对话微调** ,我最终得到的是这样一个成品: + +* 有完整的游戏页面; +* 蛇可以顺畅移动并吃到食物; +* 支持摇杆控制; +* 在小程序模拟器中可以顺利运行。 + +最终开发成品如下: + +![](images/image22.png)![](images/image23.png)![](images/image24.png) + +在这一节里,你已经看到了一个完整的闭环: + +1. 在 Trae 中用一句清晰的指令,让 AI 搭出第一版贪吃蛇小程序; +2. 借助 HBuilderX 和微信开发者工具,从用户视角检查实际效果; +3. 用自然语言反复向 AI 提出修改需求,让它替你完成功能调整和界面优化; +4. 在任何一步出现问题时,都可以通过回退和重新运行来保证安全。 + +接下来,你可以按照同样的节奏去尝试自己的想法:不一定非要是贪吃蛇,也可以是一个工具小程序、一个活动页,甚至是你工作中真正需要的业务原型。你的主要任务,是把需求想清楚、说清楚,其余的交给 AI 和这些工具来配合完成。 + +# 4.小程序发布 + +前面三章,我们已经完成了从 **搭环境** ——**和 AI 一起开发**到**在本地模拟器里跑通贪吃蛇**的整个流程。 + +从这一章开始,我们关心的问题变成了:**怎样把这个作品真正挂到微信上,不只是一个小玩具,而是所有人都可以使用的微信小程序呢?** + +为了降低门槛,我们先走一条 **最短闭环** :只让它以**测试号**的形式上线,先自己和少数同学体验;等到你觉得功能和体验都足够稳定,再走正式上线流程。 + +本章先讲到 4.1,帮你完成**测试号上线**这条最短路径;关于面向所有用户的正式上线,会在 4.2 中再展开。 + +## 4.1 最短 SOP —— 测试号上线 + +这一小节的目标只有一个:让你在微信里,真的能以**体验版**的形式打开自己的贪吃蛇小程序。 + +整个流程可以理解为四件事: + +1. 在微信公众平台找到并确认自己的 AppID。 +2. 在项目里把这个 AppID 配置好。 +3. 用微信开发者工具上传当前版本。 +4. 回到公众平台,把这次上传的版本设置为「体验版」。 + +下面我们按照这个顺序来走一遍。 + +### 4.1.1 在微信公众平台确认 AppID + +第一步,是在微信公众平台上确认你的小程序 AppID。 + +这一步你之前在**2.环境准备**时已经做过一次,这里是把它真正用起来。 + +1. 打开浏览器,访问 `https://mp.weixin.qq.com`,登录你的小程序后台。 +2. 在左侧菜单中找到「开发管理」,进入其中的「开发设置」。 +3. 在页面上方,你会看到一块叫做「开发者 ID」的区域,里面有一行「AppID(小程序 ID)」——这就是你的小程序唯一编号。 + +这串编号需要和项目中的配置一一对应,否则微信会认为你上传的是「别人的小程序」,自然无法正常预览和发布。 + +![](images/image25.png) + +### 4.1.2 在项目中填写 AppID + +第二步是把这个 AppID 写进你的项目配置里,让本地构建出来的小程序和公众平台上的这个「账号」对应上。 + +如果你是用 uni-app 模板来做的项目,可以按照下面的方式操作: + +1. 打开 HBuilderX,载入你的贪吃蛇项目。 +2. 在左侧文件树中找到 `manifest.json`,双击打开。 +3. 下拉到「微信小程序配置」这一栏,你会看到一个输入框,提示类似「微信小程序 AppID(请在微信开发者工具中获取)」。 +4. 把刚才在公众平台上看到的 AppID 原样粘贴进来,保存文件。 + ![](images/image26.png) + +到这里为止,你的本地项目就已经认领了这个小程序身份。接下来,只要通过微信开发者工具上传版本,它就会被记在这个 AppID 名下。 + +### 4.1.3 在微信开发者工具中上传一个版本 + +前面我们已经用 HBuilderX 把项目运行到微信开发者工具里,看过模拟器中的效果。 + +现在要做的是:在开发者工具中,把当前这份代码“”打一个版本包”上传到服务器上。 + +大致步骤如下: + +1. 在微信开发者工具顶部工具栏的右侧,你会看到一个「上传」按钮,点击它。 +2. 弹出的窗口中,需要填写两个关键字段: + 1. 版本号:例如 `1.0.0`,只允许数字和小数点。 + 2. 项目备注:写一段简短说明,比如「完成基本功能的开发」。 +3. 检查无误后,点击「上传」按钮。下面的输出区域会显示编译过程,所有步骤变成绿色并提示上传完成,就说明这一版已经成功提交到了微信服务器。 + +![](images/image27.png) + +![](images/image28.png) + +![](images/image29.png)![](images/image30.png) + +### 4.1.4 在管理后台中把版本设为体验版 + +上传只是把代码送到了微信这边,还没告诉系统“这是一版可以试用的体验版本”。 + +最后一步,我们回到公众平台的小程序后台,完成这个闭环。 + +1. 再次打开 `https://mp.weixin.qq.com`,进入你的小程序后台。 +2. 在左侧找到「管理」下面的「版本管理」,点击进入。 +3. 在页面的「开发版本」一栏,你应该能看到刚刚上传的那个版本:版本号是 `1.0.0`,备注是你写的那一段说明,时间是刚刚的上传时间。 +4. 在这一行的右侧,会有一个下拉按钮或操作按钮,可以选择「设为体验版」,点击之后,确认操作,注意在这一步之前请确保你已经在首页-小程序类目设置好了你的主营类目。 + + ![](images/image31.png) + + ![](images/image32.png) + +完成之后,这个版本就变成了你的小程序「体验版」。你可以在后台生成体验版二维码,也可以把自己和同事加入「体验成员」,让大家用微信扫描后,在真机上体验这款贪吃蛇小程序。 + +到这里,我们就完成了从本地项目到测试号上线的最短闭环: + +你不需要一开始就面向所有微信用户开放,只是在一个安全的范围内,让真实的小程序跑在真实的微信环境里。这足够你用来测试功能、收集反馈、继续迭代。 + +## 4.2 小程序正式上线 + +体验版跑通之后,你已经可以在自己的微信里玩到这款贪吃蛇小程序了。 接下来要做的,就是把它从只有少数体验成员能用的状态,推进到全民可用正式微信小程序。 + +把这件事拆成几步:先补充信息,再选择类目,然后完成备案,最后提交审核。下面按照这个顺序走一遍。 + +### 4.2.1 进入小程序发布流程 + +首先回到微信公众平台后台,登录你的小程序账号。 在左侧导航里找到与「版本管理 / 发布」相关的入口(不同时间界面可能略有调整),展开后会看到「小程序发布流程」这一项。 + +点击进入之后,界面上方会显示一个进度条,下面依次列出几个步骤,例如: + +1. 小程序信息 +2. 小程序类目 +3. 运营信息 / 小程序备案 +4. 微信认证(视你的主体而定) + +一开始进度会显示 0%,随着你完成每一步,系统会自动把进度向前推进。 + +![](images/image33.png) + +### 4.2.2 填写小程序基本信息 + +第一步是把小程序的「名片」补充完整,这也是用户在微信里第一次看到你的时候会接触到的内容。 + +在「小程序信息」页面,你通常需要填写和确认以下内容: + +1. 小程序名称 这个名字会出现在搜索结果和小程序顶部,有长度限制,同时需要符合微信的命名规范。建议选择既能表达功能,又方便记忆的名称,例如「贪吃蛇 vibecoding 版」这一类。 +2. 功能介绍 / 简介 用一两句话说明这个小程序是做什么的,例如:「一款用 AI 辅助开发完成的贪吃蛇小游戏,适合在碎片时间玩一局。」 注意简介要和实际功能一致,避免使用夸张宣传语。 +3. 图标和展示图片 + 1. 图标一般要求为正方形图片,支持 PNG/JPG 等格式,大小和像素有明确限制(以页面说明为准),建议提供一张简洁、对比度高的图。 + 2. 展示图片可以上传几张小程序页面的截图,例如首页、游戏页面、设置界面等,这些会出现在详情页中,帮助用户了解内容。 +4. 其他必要信息 例如标签、服务区域等,根据页面提示填写即可。 原则只有一个:所有填写的内容都要和这款贪吃蛇小程序真实功能相符。 + +![](images/image34.png) + +全部填写完毕后,点击保存或下一步,发布流程中的第一步就完成了。 + +### 4.2.3 选择小程序服务类目 + +完成基本信息后,向导会引导你进入「小程序类目」步骤。 类目可以理解为小程序在微信里的「归属分类」,决定它在审核时会被归入哪一类应用,也会影响后续展示和运营。 + +![](images/image35.png) + +在这个页面,你会看到「添加类目」按钮。点击后,可以从系统提供的分类树中选择适合你小程序的方向,例如: + +![](images/image36.png) + +1. 先选择「教育」这一大类; +2. 再在下面选择「教育工具 / 教学辅助」等更具体的子类,本次我选择了教育器具,当做大家学习Vibecoding的教具吧~ + +你在自己的项目里,只需要根据实际用途选择最贴切的一项即可。 + +![](images/image37.png) + +![](images/image38.png) + +确认类目后,点击保存。如果页面提示「创建类目成功」,并在列表中显示你刚刚添加的那一项,就说明这一步已经完成。 + +### 4.2.4 完成小程序备案信息 + +接下来,发布流程会要求你完成「运营信息 / 小程序备案」部分。这一步是为了验证小程序的主体身份,确保上线的应用有明确责任人。 + +![](images/image39.png) + +在个人主体的示例下,大致会经历这样几个动作: + +1. 选择备案类型 页面会让你在不同主体类型之间进行选择,例如「个人」「企业」等。根据你注册小程序时的主体保持一致即可。 +2. 填写主体信息 包括姓名、证件类型、证件号码等基本信息。 这一部分需要与注册信息保持一致,否则可能会在审核时被退回。 +3. 上传证明材料 页面通常会要求你上传身份证照片或其他证明文件,具体格式、清晰度和大小要求会写在说明里。 按提示准备好图片后上传,确保内容清晰可辨。 + ![](images/image40.png) + +提交之后,系统会进入「审核中」状态,页面上会显示类似「信息已提交,请耐心等待」的提示。这个过程可能需要一定时间,你可以在后台随时查看备案进度。 + +![](images/image41.png) + +### 4.2.5 提交审核并等待正式发布 + +当「小程序信息」「小程序类目」「运营信息 / 备案」等步骤全部被勾选完成后,你就可以进行最后一个动作:提交审核。 + +1. 回到「小程序发布流程」总览页面,确认每一项都显示为已完成,进度条接近 100%。 +2. 根据页面提示,点击「提交审核」或类似按钮,把当前开发版本送交微信团队审核。 +3. 在「版本管理」中,你会看到这次提交的版本状态变为「审核中」。通过后,会变成「已发布」或可选择「上线」的状态。 + +备案审核不通过会打电话给开发者,提示不通过的部分。 + +备案会收到“工业和信息化部”发来的验证码,和核验链接,点击进入把验证码和个人信息填入就行(核验有效期是1天)备案通过会收到“工业和信息化部”发送的邮件和短信通知,并且告知备案号。微信认证:个人缴纳30元,公司企业貌似是300元,不管认证是否通过都钱都不退回,会收到认证通知,并且接到电话确认信息 + +提交进行审核,要上传操作视频和页面,填好信息提交即可 ,点击“提交发布”,就正式发布了 + +![](images/image42.png) + +# 5.总结 + +到这里,你已经完整跑完了一次**从0到1**的小程序开发闭环: 从认识微信小程序,到装好 Trae、HBuilderX 和微信开发者工具;从把想法丢给 AI,让它在代码里替你“搬砖”,到在模拟器里试玩第一版贪吃蛇;再到把作品打成体验版、走完备案和审核,真正在微信里让人使用——这条链路你已经亲手走通了一遍。 + +更重要的是,你不是靠死记硬背语法做到这一点的,而是靠清楚地表达需求 + 和 AI 有效沟通来实现的 。你已经体验过 **:一句自然语言指令,可以让AI完美满足你的开发要求** 。这种能力不会只停留在贪吃蛇上,它可以迁移到你以后想做的任何小程序——工具、活动页、教学应用,甚至是你工作中的真实项目。 + +如果要给你一个 **通用SOP** ,其实只有五步:** 想清楚一个小需求 → 在 Trae 里搭好项目骨架 → 用 vibecoding 和 AI 搭出第一版 → 在微信开发者工具里不断试玩和改进 → 上传、备案、审核、上线。** 每次你重复这五步,就会多一个真正能被人打开、能被分享的小程序,也会多一份「我可以用 AI 把点子变成产品」的信心。 + +接下来,你可以继续打磨这款贪吃蛇,也可以关掉它,重新起一个空项目,从你自己的想法出发。无论做什么,只要记住一点: 你不再只是一个「想做点东西」的人,而是已经跑通了全流程的 vibecoding 开发者。剩下的,就是多做几次,把这种能力变成习惯。 + +# 参考资料: + +- https://zhuanlan.zhihu.com/p/1889401120939567074 +- https://blog.csdn.net/2401_87407347/article/details/155193007 diff --git a/docs/examples/example1/images/image1.png b/docs/examples/example1/images/image1.png new file mode 100644 index 0000000..3400430 Binary files /dev/null and b/docs/examples/example1/images/image1.png differ diff --git a/docs/examples/example1/images/image10.png b/docs/examples/example1/images/image10.png new file mode 100644 index 0000000..7e7c364 Binary files /dev/null and b/docs/examples/example1/images/image10.png differ diff --git a/docs/examples/example1/images/image11.png b/docs/examples/example1/images/image11.png new file mode 100644 index 0000000..144108a Binary files /dev/null and b/docs/examples/example1/images/image11.png differ diff --git a/docs/examples/example1/images/image12.png b/docs/examples/example1/images/image12.png new file mode 100644 index 0000000..164b8c9 Binary files /dev/null and b/docs/examples/example1/images/image12.png differ diff --git a/docs/examples/example1/images/image13.png b/docs/examples/example1/images/image13.png new file mode 100644 index 0000000..7539c51 Binary files /dev/null and b/docs/examples/example1/images/image13.png differ diff --git a/docs/examples/example1/images/image14.png b/docs/examples/example1/images/image14.png new file mode 100644 index 0000000..13b638d Binary files /dev/null and b/docs/examples/example1/images/image14.png differ diff --git a/docs/examples/example1/images/image15.png b/docs/examples/example1/images/image15.png new file mode 100644 index 0000000..1cdb12e Binary files /dev/null and b/docs/examples/example1/images/image15.png differ diff --git a/docs/examples/example1/images/image16.png b/docs/examples/example1/images/image16.png new file mode 100644 index 0000000..db7d5c0 Binary files /dev/null and b/docs/examples/example1/images/image16.png differ diff --git a/docs/examples/example1/images/image17.png b/docs/examples/example1/images/image17.png new file mode 100644 index 0000000..7a2d1e4 Binary files /dev/null and b/docs/examples/example1/images/image17.png differ diff --git a/docs/examples/example1/images/image18.png b/docs/examples/example1/images/image18.png new file mode 100644 index 0000000..8015c6f Binary files /dev/null and b/docs/examples/example1/images/image18.png differ diff --git a/docs/examples/example1/images/image19.png b/docs/examples/example1/images/image19.png new file mode 100644 index 0000000..9c6e1d3 Binary files /dev/null and b/docs/examples/example1/images/image19.png differ diff --git a/docs/examples/example1/images/image2.png b/docs/examples/example1/images/image2.png new file mode 100644 index 0000000..2600155 Binary files /dev/null and b/docs/examples/example1/images/image2.png differ diff --git a/docs/examples/example1/images/image20.png b/docs/examples/example1/images/image20.png new file mode 100644 index 0000000..af4691b Binary files /dev/null and b/docs/examples/example1/images/image20.png differ diff --git a/docs/examples/example1/images/image21.png b/docs/examples/example1/images/image21.png new file mode 100644 index 0000000..60f2b54 Binary files /dev/null and b/docs/examples/example1/images/image21.png differ diff --git a/docs/examples/example1/images/image22.png b/docs/examples/example1/images/image22.png new file mode 100644 index 0000000..5379a94 Binary files /dev/null and b/docs/examples/example1/images/image22.png differ diff --git a/docs/examples/example1/images/image23.png b/docs/examples/example1/images/image23.png new file mode 100644 index 0000000..aaf9ef9 Binary files /dev/null and b/docs/examples/example1/images/image23.png differ diff --git a/docs/examples/example1/images/image24.png b/docs/examples/example1/images/image24.png new file mode 100644 index 0000000..c1814d9 Binary files /dev/null and b/docs/examples/example1/images/image24.png differ diff --git a/docs/examples/example1/images/image25.png b/docs/examples/example1/images/image25.png new file mode 100644 index 0000000..9c02a06 Binary files /dev/null and b/docs/examples/example1/images/image25.png differ diff --git a/docs/examples/example1/images/image26.png b/docs/examples/example1/images/image26.png new file mode 100644 index 0000000..38e57af Binary files /dev/null and b/docs/examples/example1/images/image26.png differ diff --git a/docs/examples/example1/images/image27.png b/docs/examples/example1/images/image27.png new file mode 100644 index 0000000..f8b30a3 Binary files /dev/null and b/docs/examples/example1/images/image27.png differ diff --git a/docs/examples/example1/images/image28.png b/docs/examples/example1/images/image28.png new file mode 100644 index 0000000..65b3c97 Binary files /dev/null and b/docs/examples/example1/images/image28.png differ diff --git a/docs/examples/example1/images/image29.png b/docs/examples/example1/images/image29.png new file mode 100644 index 0000000..4c4f73e Binary files /dev/null and b/docs/examples/example1/images/image29.png differ diff --git a/docs/examples/example1/images/image3.png b/docs/examples/example1/images/image3.png new file mode 100644 index 0000000..9b23ac1 Binary files /dev/null and b/docs/examples/example1/images/image3.png differ diff --git a/docs/examples/example1/images/image30.png b/docs/examples/example1/images/image30.png new file mode 100644 index 0000000..e04c07f Binary files /dev/null and b/docs/examples/example1/images/image30.png differ diff --git a/docs/examples/example1/images/image31.png b/docs/examples/example1/images/image31.png new file mode 100644 index 0000000..cf15683 Binary files /dev/null and b/docs/examples/example1/images/image31.png differ diff --git a/docs/examples/example1/images/image32.png b/docs/examples/example1/images/image32.png new file mode 100644 index 0000000..836273b Binary files /dev/null and b/docs/examples/example1/images/image32.png differ diff --git a/docs/examples/example1/images/image33.png b/docs/examples/example1/images/image33.png new file mode 100644 index 0000000..7494ee4 Binary files /dev/null and b/docs/examples/example1/images/image33.png differ diff --git a/docs/examples/example1/images/image34.png b/docs/examples/example1/images/image34.png new file mode 100644 index 0000000..0f53a63 Binary files /dev/null and b/docs/examples/example1/images/image34.png differ diff --git a/docs/examples/example1/images/image35.png b/docs/examples/example1/images/image35.png new file mode 100644 index 0000000..7494ee4 Binary files /dev/null and b/docs/examples/example1/images/image35.png differ diff --git a/docs/examples/example1/images/image36.png b/docs/examples/example1/images/image36.png new file mode 100644 index 0000000..101328c Binary files /dev/null and b/docs/examples/example1/images/image36.png differ diff --git a/docs/examples/example1/images/image37.png b/docs/examples/example1/images/image37.png new file mode 100644 index 0000000..2a7cb11 Binary files /dev/null and b/docs/examples/example1/images/image37.png differ diff --git a/docs/examples/example1/images/image38.png b/docs/examples/example1/images/image38.png new file mode 100644 index 0000000..55ad6fb Binary files /dev/null and b/docs/examples/example1/images/image38.png differ diff --git a/docs/examples/example1/images/image39.png b/docs/examples/example1/images/image39.png new file mode 100644 index 0000000..7494ee4 Binary files /dev/null and b/docs/examples/example1/images/image39.png differ diff --git a/docs/examples/example1/images/image4.png b/docs/examples/example1/images/image4.png new file mode 100644 index 0000000..84882f1 Binary files /dev/null and b/docs/examples/example1/images/image4.png differ diff --git a/docs/examples/example1/images/image40.png b/docs/examples/example1/images/image40.png new file mode 100644 index 0000000..b5afad6 Binary files /dev/null and b/docs/examples/example1/images/image40.png differ diff --git a/docs/examples/example1/images/image41.png b/docs/examples/example1/images/image41.png new file mode 100644 index 0000000..d7127ee Binary files /dev/null and b/docs/examples/example1/images/image41.png differ diff --git a/docs/examples/example1/images/image42.png b/docs/examples/example1/images/image42.png new file mode 100644 index 0000000..ad70349 Binary files /dev/null and b/docs/examples/example1/images/image42.png differ diff --git a/docs/examples/example1/images/image5.png b/docs/examples/example1/images/image5.png new file mode 100644 index 0000000..4dc556d Binary files /dev/null and b/docs/examples/example1/images/image5.png differ diff --git a/docs/examples/example1/images/image6.png b/docs/examples/example1/images/image6.png new file mode 100644 index 0000000..57d3355 Binary files /dev/null and b/docs/examples/example1/images/image6.png differ diff --git a/docs/examples/example1/images/image7.png b/docs/examples/example1/images/image7.png new file mode 100644 index 0000000..ce40757 Binary files /dev/null and b/docs/examples/example1/images/image7.png differ diff --git a/docs/examples/example1/images/image8.png b/docs/examples/example1/images/image8.png new file mode 100644 index 0000000..fdd6558 Binary files /dev/null and b/docs/examples/example1/images/image8.png differ diff --git a/docs/examples/example1/images/image9.png b/docs/examples/example1/images/image9.png new file mode 100644 index 0000000..fd2315a Binary files /dev/null and b/docs/examples/example1/images/image9.png differ diff --git a/docs/extra/extra1/extra1-what-is-git-and-what-is-github.md b/docs/extra/extra1/extra1-what-is-git-and-what-is-github.md new file mode 100644 index 0000000..278ae64 --- /dev/null +++ b/docs/extra/extra1/extra1-what-is-git-and-what-is-github.md @@ -0,0 +1,279 @@ +# 扩展知识 1 - 什么是 Git 和 GitHub + +在之前的课程中,我们学习了如何使用基于 Web 的 vibe coding 工具编写代码。每次对话都会创建一个新版本的代码。但是,让我们思考一个问题:如果我们想恢复到之前的修改,有没有方便的方法?有没有一种工具可以记录我们在不同阶段的代码,使我们能够随时在不同版本之间切换和修改? + +为了满足这一需求,版本控制软件应运而生。在这篇文章中,我们将介绍最著名的版本控制程序——Git——以及最好的代码托管平台——GitHub。我们将学习如何使用 Git 进行代码管理,如何从 GitHub 获取他人的代码,如何上传我们自己的代码,以及如何与他人合作进行大型项目。 + +无论是个人项目的版本跟踪,团队协作中的代码同步,还是为开源社区做贡献,Git 和 GitHub 都是现代开发者的必备工具。通过掌握它们,你将能够更高效地管理代码,根据需要创建检查点,在代码的不同阶段之间自由切换,并轻松处理从单个文件更改到开发大型项目的所有事务——使每一次代码迭代都可控且可追溯。 + +# 什么是 Git + +Git 是由 Linux 内核开发者 Linus Torvalds 于 2005 年创建的分布式版本控制系统。其核心功能是跟踪文件的修改历史,允许开发者随时查看和回滚到以前的版本,并在与他人协作时高效地合并更改。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image1.png) + +与早期的集中式版本控制系统相比,Git 的“分布式”特性允许每个开发者的本地设备存储代码仓库的完整历史记录。大多数操作(如提交、回滚和分支管理)都可以在不依赖中央服务器的情况下执行,这使得 Git 更灵活,更适合大规模协作和离线工作。 + +> 💡 在操作 Git 之前,让我们先了解一下什么是终端。 +> +> ## 什么是终端? +> +> 终端本质上是一个基于文本的“计算机接入点”。在早期,图形界面(没有图标,没有鼠标点击)出现之前,用户只能通过键入文本命令与计算机交互。这种方法代代相传,成为了我们要介绍的今天的终端。 +> +> 它不依赖花哨的界面,纯粹通过“命令 + 反馈”工作。这使其成为人机交互最基本和直接的方法之一。 +> +> 不同系统的终端有所不同。在 Windows 上,常见的是“命令提示符 (cmd)”和“PowerShell”。你可以通过在计算机的运行/搜索框中输入“cmd”或“powershell”来启动这些命令行程序。 +> +> ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image2.png) +> +> ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image3.png) +> +> 前者是一个较旧的工具,仅支持基本命令,如查看文件和复制,适合简单任务。后者是一个更高级的版本,可以处理复杂的操作,如进程管理和远程控制,并且还兼容 cmd 命令——使其在开发或系统管理场景中更常用。macOS 和 Linux 都默认带有“终端”,它们的命令逻辑相似,源自 Unix(贝尔实验室工程师在 20 世纪 60 年代末开发的经典计算机系统)。 +> +> 终端在今天仍然至关重要,因为它的效率和广泛的兼容性。例如,一条命令可以批量重命名文件,比用鼠标重复点击快得多。此外,本地服务器、云服务器和专业开发环境通常没有图形界面,因此终端操作是必要的。许多任务,如安装各种程序(如 Git、Python、系统工具或开发依赖项)、运行代码、管理计算机进程和配置系统参数,也需要终端命令。 +> +> 你可能会想,如果记不住所有那些终端命令该怎么办。事实上,随着大语言模型的快速发展,不再需要像以前那样死记硬背了。现在,你只需要在需要时询问模型(例如“如何用 Git 获取远程代码?”或“如何通过命令行删除文件夹或批量创建文件夹?”),然后从回复中复制有用的命令即可。 +> +> ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image4.png) + +## 如何安装 Git + +我们将演示在不同计算机操作系统上安装 Git 的三种方法。请根据你的系统版本按照说明进行操作: + +### Windows + +1. 前往 [Git 官方下载页面](https://git-scm.com/download/win) 并下载适合你系统的安装程序:[安装包](https://github.com/git-for-windows/git/releases/download/v2.51.0.windows.1/Git-2.51.0-64-bit.exe)。默认情况下,推荐使用 x64 安装程序。 +2. 双击安装程序并按照安装向导说明进行操作: + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image5.png) + + 1. 建议保持默认选项。如果你需要自定义,请注意以下几点:(在大多数情况下,你可以一直点击“Next”) + * 选择 Git 使用的默认编辑器:选择你喜欢的编辑器(如 VS Code)。你可以默认选择第一个选项,即 Vim(一个文本编辑器),或选择“Visual Studio Code as Git's default editor”选项(需要预先安装 VS Code)。你可以保持默认选择并点击“Next”继续。 + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image6.png) + * 选择如何使用 Git:这三个选项控制 Git 在系统中的可访问性。建议选择选项 2(“from command line and 3rd-party software”)——它将基本的 Git 工具添加到 PATH 中,让你可以在 Git Bash、命令提示符、PowerShell 和 IDE 中使用 Git,而不会使系统混乱。 + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image7.png) +3. 安装后,在桌面上右键单击。如果在菜单中看到“Git Bash Here”,则安装成功。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image8.png) + +### MacOS + +对于 macOS,你可以首先在终端中输入 `git --version` 来检查是否已经安装了 Git。如果没有,系统会提示你安装——只需按照说明完成安装即可。 + +1. 方法 1:通过 Homebrew 安装 + 如果你安装了 [Homebrew](https://brew.sh/)(Mac 包管理器),请打开终端并输入 + ```Bash + brew install git + ``` +2. 方法 2:(推荐)通过 Xcode 安装: https://developer.apple.com/xcode/ ,Xcode 内置了 Git。安装后,只需按照说明继续操作。 + +### Linux + +大多数 Linux 发行版可以通过其包管理器安装 Git: + +* Ubuntu/Debian: + +```Bash +sudo apt update +sudo apt install git +``` + +* CentOS/RHEL: + +```Bash +sudo yum install git +``` + +* 验证安装:在终端中输入 git --version。如果显示版本号,则安装成功。 + +## Git 初始化 + +安装 Git 后,你首先需要配置你的用户信息——这是使用 Git 进行版本控制的基本步骤。在终端中执行以下命令(将括号中的内容替换为你自己的信息): + +```Bash +# 设置全局用户名(将显示在提交记录中) +git config --global user.name "Your Name" + +# 设置全局邮箱(建议使用在 GitHub/GitLab 等平台上注册的邮箱) +git config --global user.email "your.email@example.com" +``` + +Git 会将此信息嵌入到每个提交记录中,作为每次修改的“作者信息”。查看版本历史记录(例如,使用 git log)时,你可以清楚地看到谁修改了每一行代码,便于追溯责任和沟通。在协作项目中,统一的身份信息使团队成员能够快速识别谁做了哪些更改,从而提高协作效率(例如通过提交记录找到相关开发人员讨论问题)。 + +你可以通过在命令行中输入 `git config --list` 来查看当前的 Git 配置信息,以确认设置成功。 + +# 什么是 GitHub + +GitHub 是一个基于 Git 的代码托管平台。它不仅为 Git 仓库提供远程存储,还包括协作工具(如 Issues、Pull Requests、Projects),使开发者更容易分享代码和协作。简而言之,Git 是一个本地版本控制工具,而 GitHub 是一个远程“代码仓库云盘 + 协作社区”。 + +GitHub 不仅是世界上最大的代码托管平台,也是全球最活跃、最具影响力的开源社区。这里“开源”的核心思想是任何人都可以下载并运行软件的源代码。这种模式允许世界各地的人们检查彼此的代码并进行修改,或基于此创建新项目。例如,你可以在 GitHub 上找到各种学习教程以及用于训练 GPT 模型的框架(如 PyTorch)的完整源代码。每天,无数人在全球范围内协作审查和改进代码。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image9.png) + +许多大公司在 GitHub 上开源他们的程序或教程,以获得行业竞争优势——这也可以看作是一种广告形式。在 GitHub 社区中,项目获得的“星标 (stars)”数量是衡量其价值的主要指标;项目或组织拥有的星标越多,其可信度和影响力就越大。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image10.png) + +在我们的课程中,支持资源和作业也将上传到专用的 GitHub 仓库。通过上传作业的过程,你将逐渐熟悉并掌握 GitHub 的使用,为未来应用程序开发中的版本控制打下坚实的基础。 + +## 注册 GitHub 账号 + +1. 访问 [GitHub 官网](https://github.com/) 并点击右上角的“Sign up”。 + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image11.png) +2. 输入你的电子邮件地址(建议使用常用邮箱,因为验证和通知将发送到那里),设置密码(必须包含字母、数字和特殊字符)。 +3. 完成人工验证,按照提示验证邮箱,你的账号就创建好了。 + +## 在 GitHub 上创建你的第一个仓库 + +接下来,我们将创建第一个存储文件夹,也称为仓库或“repo”。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image12.png)![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image13.png) + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image14.png) + +1. Repository name:向他人显示的仓库名称。 +2. Description:仓库的详细描述。 +3. Choose visibility:对于个人仓库,如果设置为 private,只有你和特别邀请的人可以看到。如果设置为 public,所有人都可以看到。 + 对于组织内的仓库,如果是 Private,只有组织内的人可以看到。 + 如果是 Public,组织外的人也可以看到。 +4. README:通常的惯例是每个仓库都应该有一个 README 文件。你可以把它看作是仓库的完整介绍,包括使用说明、文件列表和操作方法。 +5. Add .gitignore and license: + 1. .gitignore 文件告诉 Git 在上传到 GitHub 时忽略某些文件夹或文件,因此它们不会被跟踪或添加到暂存区。这对于临时测试文件、依赖包或大文件很有用。一旦指定,这些文件将不再被跟踪。 + 2. license 指的是你选择的开源许可证类型。不同的许可证详细规定了他人是否可以将你的代码用于商业目的,并包含其他条款和条件。 + +建议勾选“Add README”,将仓库可见性设置为“Private”,并根据自己的喜好填写仓库名称和描述,然后点击“Create repository”完成创建第一个远程仓库。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image15.png) + +之后,你将拥有一个没有任何额外文件的干净仓库。接下来你可以开始上传文件了。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image16.png) + +获取仓库的命令是 `git clone`,但它需要仓库地址。你可以通过点击绿色的“Code”按钮找到仓库地址,你会看到 HTTPS 和 SSH 选项。通常,你可以使用这两种方法中的任何一种将仓库下载到本地机器(只有这样你才能修改和上传文件)。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image17.png) + +一般来说,通过 HTTP 克隆的仓库适合临时下载和测试他人的仓库,但不建议用于自己的开发。为了更好的学习体验,你应该先设置 SSH 认证。 + +## 绑定本地 SSH + +在 GitHub 中,“SSH 协议绑定”本质上意味着将你本地设备的 SSH 公钥与你的 GitHub 账号关联,允许 GitHub 通过 SSH 协议识别你的设备。这使你能够安全地操作远程仓库,而无需密码(如 clone、push 或 pull 代码)。 + +简单来说:这就像给你的设备一张“GitHub 专属门禁卡”。绑定后,当你的设备通过 SSH 协议访问 GitHub 仓库时,GitHub 会验证这张“门禁卡”(你的 SSH 公钥)。一旦确认为你的授权设备,你就可以直接操作——不需要每次都输入账号密码。 + +> 💡 什么是 SSH + +### 为什么需要 SSH 协议绑定? + +GitHub 支持两种主要的仓库操作协议:HTTPS 协议和 SSH 协议: + +* HTTPS 协议:每次操作(如 push)都需要输入 GitHub 账号密码(或个人访问令牌 PAT)。验证过程繁琐,且存在密码泄露风险。 +* SSH 协议:身份验证通过“密钥对”完成,因此不需要重复输入密码,且加密传输更加安全。 + +“SSH 协议绑定”是启用 GitHub SSH 认证的前提步骤——只有将本地 SSH 公钥“绑定”到 GitHub 账号后,GitHub 才能识别你的设备并允许对仓库进行 SSH 操作。 + +### “绑定”的核心逻辑:SSH 密钥对的作用 + +SSH 认证依赖于密钥对(公钥 + 私钥),它们是匹配的加密文件。生成后,你需要将“公钥”提供给 GitHub(“绑定”),而“私钥”留在本地设备上: + +1. 私钥:存储在本地设备(如电脑)的指定目录中(通常是 ~/.ssh/),充当“你的专属钥匙”,绝不能与任何人分享。 +2. 公钥:这是一把可以公开分享的“锁”——你需要将其复制到 GitHub 账号的“SSH keys list”中(“绑定”操作)。 + +当你通过 SSH 操作 GitHub 仓库时(例如 git push git@github.com:xxx/xxx.git): + +* 你的本地设备使用私钥加密“操作请求”并发送给 GitHub; +* 收到请求后,GitHub 尝试使用你之前绑定的公钥进行解密; +* 如果解密成功,你的设备被确认为已授权,操作被允许;否则,访问被拒绝。 + +### “绑定”的具体步骤(核心流程) + +一旦你理解了原理,实际操作就很简单——核心是“生成密钥对 → 上传公钥到 GitHub”: + +1. 本地生成 SSH 密钥对 + 1. 使用 Trae 获取公钥(推荐) + 提示词:`Help me create the SSH key needed for GitHub login. My email is your_email@gmail.com , Please return the public key for me to copy` + + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image18.png) + + 输入提示词后,你还需要在左侧终端按 Enter 键,否则命令会一直等待而不执行。由于 Trae 无法帮你执行任何条件判断,我们只需要一直按 Enter 即可。 + + 最后,你会看到右侧的 Trae 返回了它读取的公钥。你只需复制它并准备在下一步中粘贴。 + + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image19.png) + 2. 手动获取公钥 + 打开你的本地终端(在 Windows 上使用 Git Bash 或 PowerShell;在 macOS/Linux 上使用终端),输入以下命令(将 your_email@example.com 替换为你注册 GitHub 账号时使用的邮箱): + + ```Bash + ssh-keygen -t ed25519 -C "your_email@example.com" + ``` + + 1. 按 Enter 接受默认值(默认文件路径,无密码,或根据需要设置密码)。这将在 ~/.ssh/ 目录中生成两个文件: + * id_ed25519:私钥(本地保存,**绝不分享**); + * id_ed25519.pub:公钥(需要上传到 GitHub)。 +2. 将公钥“绑定”到你的 GitHub 账号 + +这是核心绑定步骤——将本地公钥添加到 GitHub 账号的“SSH keys list”中: + +1. 复制公钥内容: + 1. Trae: + 2. Windows:用记事本打开 C:\Users\\.ssh\id_ed25519.pub 并复制其所有内容; + 3. macOS/Linux:在终端运行 cat ~/.ssh/id_ed25519.pub 并复制所有输出(从开头的 SSH-ed25519 到结尾的邮箱)。 +2. 登录 GitHub 并进入“SSH Key Management”页面: + 1. 点击右上角头像 → Settings → 左侧菜单 SSH and GPG keys → 点击 New SSH key。 + ![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image20.png)![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image21.png) + 2. 输入任何标题(例如,your local computer's SSH),然后将你刚刚获取的 SSH 公钥粘贴到这里。 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image22.png) + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image23.png) + +3. 验证绑定是否成功 + +在终端中输入以下命令(**Trae 也可以做以下操作**)来测试 GitHub 是否能识别你的设备: + +```Bash +ssh -T git@github.com +``` + +* 如果你看到类似 Hi [your GitHub username]! You've successfully authenticated... 的内容,说明你已成功绑定密钥; +* 如果遇到错误,通常是因为公钥复制不完整、私钥权限过高(你的本地 ~/.ssh/ 目录应仅由你读写)等。根据需要检查这些问题。 + +### 重要注意事项 + +如果你有多个设备(如笔记本电脑和台式机),你需要为每个设备生成单独的 SSH 密钥对,并将每个公钥绑定到同一个 GitHub 账号——每个设备都有自己的“门禁卡”。 + +切勿分享你的私钥(不要上传到 GitHub 或与他人分享),否则有人可能会冒充你操作你的仓库。如果私钥泄露,请立即从 GitHub 删除相应的公钥并生成新的密钥对。 + +绑定 SSH 后,使用 SSH 格式的仓库地址(例如 git@github.com:username/repository.git)进行操作,而不是 HTTPS 格式(例如 https://github.com/username/repository.git)。如果你之前用 HTTPS 克隆了仓库,可以用 git remote set-url origin `` 切换协议。 + +# 使用 Trae 进行 GitHub 操作 + +我们已经解释了什么是 Git,什么是 GitHub,什么是 SSH,以及如何配置它。现在你可以自由使用 Trae 执行 Git 操作。首先,让我们学习如何将远程仓库克隆到本地机器。 + +## Git clone : 下载现有仓库 + +你可以直接告诉它你想克隆的仓库地址 + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image24.png) + +## Git pull : 从远程仓库获取更新 + +每次更新仓库之前,由于它可能由多人维护,你需要先拉取最新的更改。之后,你可以修改并推送文件。 + +**记得包含文件夹名称及其相对或绝对路径,以避免推送到错误的仓库。** + +prompt:`Help me pull this repository AIID-TEST in ./AIID-TEST.` + +## Git commit & Git push : 暂存更新并推送到 GitHub + +一切准备就绪后,你可以尝试修改本地文件,在文件夹中添加或删除项目。然后,让 Trae 检测更改并帮你推送到 GitHub。 + +prompt:`I finished. Commit and push to the repository AIID-TEST in ./AIID-TEST.` + +![](../../../English/Extra/Extra%20Knowledge%201%20-%20What%20is%20Git%20and%20What%20is%20GitHub/images/image25.png) + +推送成功。现在你可以在 GitHub 上看到更新的内容了。 + +# 参考资料 + +* Pro Git book https://git-scm.com/book/en/v2 +* GitHub Docs https://docs.github.com/en diff --git a/docs/extra/extra2/extra2-what-is-api.md b/docs/extra/extra2/extra2-what-is-api.md new file mode 100644 index 0000000..bb95515 --- /dev/null +++ b/docs/extra/extra2/extra2-what-is-api.md @@ -0,0 +1,258 @@ +# 扩展知识 2 - 什么是 API + +在本教程中,我们将学习什么是 API,它是如何工作的,并了解它是如何驱动我们每天使用的数字体验的。 + +# 你将学到 + +* 使用简单、真实的类比来理解什么是 API。 +* API 请求和响应循环的基本步骤。 +* 如何识别你日常使用的应用中的 API,如天气、地图和社交媒体。 +* 为什么 API 对于现代技术的创新、安全和效率至关重要。 + +# **什么是 API? - 自动售货机类比** + +想象一下你想从自动售货机买一罐汽水。 + +1. **你不需要知道它内部是如何工作的:** 你不关心冷却系统、机械臂,或者它是如何验证你的钱的。对你来说,它是一个“黑盒子”。 +2. **它有清晰的操作按钮:** 机器上有一排排按钮,每个按钮都清楚地标明了它提供的饮料。这是你与它交流的唯一方式。你必须按下“汽水”按钮。 +3. **简单的“请求”和“响应”:** + 1. 你投入硬币并按下“汽水”按钮。这是你的 **“请求”**。 + 2. 机器发出嗡嗡声,一罐汽水掉了下来供你拿取。这是它的 **“响应”**。 + +在这个例子中: + +* 你是你的程序(比如一个移动应用)。 +* 自动售货机是另一个程序或服务(比如天气服务或地图服务)。 +* 机器上的按钮就是 API。 + +API,全称 Application Programming Interface(应用程序编程接口),就是那组“按钮”。它是不同软件组件之间进行通信的一种预定义的、直接的方式,允许一个程序轻松地从另一个程序获取它需要的东西(数据或功能),而无需了解其内部的复杂性。 + +API 连接了各种软件系统,为开发者和用户带来了巨大的好处。 + +* **创新:** 公共 API 允许任何开发者使用强大的功能(如支付处理或地图)来创建新的数字体验,而无需从头开始构建它们。 +* **自动化:** API 可以自动化重复性工作,如发送电子邮件或在系统之间共享数据,提高生产力,让人类可以专注于更有创造性的任务。 +* **安全性:** API 充当安全的守门人。它们可以要求对任何请求进行身份验证,增加一层保护,防止未经授权访问敏感数据。 +* **成本效率:** 企业可以使用第三方 API 来访问有用的工具和基础设施,帮助他们避免构建复杂内部系统的巨额费用。 + +API 是现代数字世界的基石。通过理解请求和响应这个简单的概念,你已经迈出了进入更广阔、互联的软件开发世界的第一步。 + +# API 是如何工作的? + +API 通过 **请求和响应** 循环在应用程序之间共享数据。让我们把它想象成在餐厅点餐。 + +在这个比喻中,你是 **API 客户端**(发出请求的应用程序),服务员是 **API**,厨房是 **API 服务器**(拥有数据或功能的系统)。 + +## **API 请求(你的订单)** + +客户端通过向服务器发送请求来开始对话。这就像告诉服务员你想要什么。一个典型的 API 请求包括: + +* **Endpoint(端点):** 指向资源的特定 URL。这就像菜单上的特定项目。对于天气应用,端点可能是 `/current-weather`。 +* **Method(方法):** 你想执行的操作。最常见的是 `GET`,意味着你想获取数据。其他的包括 `POST`(创建新数据)、`PUT`(更新数据)和 `DELETE`(删除数据)。 +* **Parameters(参数):** 指定请求的额外细节。如果你询问天气,你需要提供位置。例如:`city=London`。 +* **Request Body(请求体):** 创建或更新资源所需的实际数据。如果你要向社交媒体应用发布新照片,照片本身就在请求体中。 + +## **API 响应(你的食物到了)** + +在厨房(服务器)准备好你的订单后,服务员(API)将其带回给你。响应包括: + +* **Status Code(状态码):** 一个三位数的代码,指示结果。`200 OK` 表示请求成功。`404 Not Found` 表示找不到请求的资源。 +* **Response Body(响应体):** 你请求的实际数据或内容。对于天气请求,这将是包含温度、状况和湿度的结构化数据。 + +# 现实世界的例子:我们周围的 API + +API 无处不在,在后台默默工作。让我们看几个常见的“自动售货机”。 + +### **天气预报 API** + +这个 API 就像一个只卖天气信息的自动售货机。 + +* **它提供什么:** 当前天气、未来几天的预报、空气质量等。 +* **一个简单的‘请求’示例:** + * **Endpoint:** `/current-weather` + * **Parameters:** `city=London` & `apiKey=your_access_key`(证明你有权限) + +‘响应’(它返回的数据): + +```JSON +{ + "city": "London", + "temperature": "15°C", + "condition": "Cloudy", + "humidity": "70%" +} +``` + +* 你的天气应用获取这些数据并以用户友好的格式显示。 + +### **地图服务 API(如 Google Maps)** + +这个自动售货机专门提供地理信息。 + +* **它提供什么:** 行车路线、从地址查找坐标、搜索附近地点。 +* **一个简单的‘请求’示例:** + * **Endpoint:** `/directions` + * **Parameters:** `origin=Eiffel Tower` & `destination=Louvre Museum` & `mode=driving` + +```JSON +{ + "total_distance": "4.5 kilometers", + "estimated_time": "15 minutes", + "route_steps": [ + "1. Head east on Champ de Mars...", + "2. Turn left onto Quai Branly...", + "..." + ] +} +``` + +* 拼车或送货应用使用这些数据在地图上绘制路线并提供逐向导航指令。 + +### **社交媒体登录 API(如“使用 Google 登录”)** + +这是一个特殊的自动售货机,不卖产品;它帮助“验证你的身份”。 + +* **它提供什么:** + +确认用户身份并安全地向另一个应用提供基本信息(如姓名和头像)。 + +* **它是如何工作的:** + * 你在新的购物应用上点击“使用 Google 登录”。 + * 购物应用向 Google 的 API 发出请求:“嘿,有个用户想登录。你能确认他是谁吗?” + * Google 请求你的批准。 + * 一旦你批准,Google 的 API 向购物应用发送响应:“身份已确认。用户是‘John Doe’。” + 这样,购物应用就知道你是谁,而无需看到你的密码,既方便又安全。 + +### **大语言模型 API(如 OpenAI 或 DeepSeek)** + +这是一个 **“超级大脑”自动售货机**。你给它一个纯文本的命令或问题,它给你一个详细的、类似人类的文本回复。 + +* **它提供什么:** 为几乎任何任务生成原创文本,如回答问题、写邮件、总结文章或创建计算机代码。 +* **它是如何工作的:** + * 想象你正在使用一个新的“AI 写作助手”应用程序。你输入一个命令:“给我的团队写一封简短、专业的邮件,关于新项目的截止日期。” + * 写作应用向 OpenAI 的 API 发出请求:“嘿,有个用户想写封邮件。你能生成关于新项目截止日期的专业信息的文本吗?” + * OpenAI API 处理此命令,理解上下文和语气,并生成文本。 + * API 向写作应用发送响应:“请求完成。这是邮件文本:‘Hi Team, This is a quick update to let you know that the deadline for Project X has been moved to this Friday...’” + +通过这种方式,写作应用可以即时为用户提供强大的 AI 功能,而无需从头开始构建自己的大型语言模型。 + +# 在 Z.ai 上查看 LLM API + +接下来,我们将学习调用大语言模型 API 的基本方法。我们可以简单参考之前贪吃蛇游戏的 API 使用示例。 + +我们不需要自己搜索。我们需要做的就是向语言模型提出后续问题,例如:“在代码中,哪几行调用了语言模型和图像生成模型?”,“如果用 Python 代码写会是什么样?”通过问这些问题,你将能直接得到完整的答案。 + +![](../../../English/Extra/Extra%20Knowledge%202%20-%20What%20is%20API/images/image1.png) + +这些 API 是使用官方智谱 SDK(SDK 代表 **软件开发工具包**)调用的。接下来,让我们看看如何调用 OpenAI 的官方 API。 + +你稍后可能会遇到很多名词。不需要一下子掌握所有名词——**你只需要在这里大致了解它们的样子**。 + +### Python API + +要在 Python 中调用 OpenAI 的文本生成 API,可以使用官方的 openai 库。 + +**`base_url`** 参数指定 API 的端点。默认情况下,它指向 OpenAI 的服务器(例如 `https://api.openai.com/v1/`)。如果你使用代理、自托管服务器或兼容的提供商,你可能需要将此参数更改为相应的地址。 + +```Python +import openai + +openai.api_key = "YOUR_OPENAI_API_KEY" +# 如果你使用自定义 base URL(例如 Azure、代理或其他提供商) +# openai.base_url = "" + +response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello, how can I use the OpenAI API?"} + ] +) + +print(response.choices[0].message["content"]) +``` + +**注意:** + +* `openai.api_key` 是你的秘密 API 密钥。 +* `openai.base_url` 是可选的,仅当你未使默认 OpenAI 端点时才需要。 +* `model` 参数指定你想使用的语言模型。 + +许多其他流行的大语言模型都兼容 OpenAI API 调用格式。例如,如果你需要使用 [DeepSeek](https://platform.deepseek.com/sign_in)(中国最好的大语言模型之一),你可以通过更改 API 端点和模型名称以非常相似的方式进行操作。 + +以下是典型示例: + +```JavaScript +import openai + +openai.api_key = "YOUR_DEEPSEEK_API_KEY" +openai.base_url = "" # DeepSeek 的端点 + +response = openai.ChatCompletion.create( + model="deepseek-chat", # 更改为 DeepSeek 的模型名称 + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Tell me about DeepSeek."} + ] +) + +print(response.choices[0].message["content"]) +``` + +### 前端 API + +如果你想从 React 前端调用 OpenAI API,绝不应该直接在浏览器中暴露你的 API 密钥。 + +相反,使用 **后端代理**(例如 Express.js),或从服务器函数安全地调用 API。 + +```JavaScript +// 安装命令:npm install express axios dotenv +const express = require("express"); +const axios = require("axios"); +require("dotenv").config(); + +const app = express(); +app.use(express.json()); + +app.post("/api/chat", async (req, res) => { + try { + const userMessage = req.body.message; + + const response = await axios.post( + "", + { + model: "gpt-3.5-turbo", + messages: [ + { role: "system", content: "You are a helpful assistant." }, + { role: "user", content: userMessage } + ] + }, + { + headers: { + "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`, + "Content-Type": "application/json" + } + } + ); + + res.json({ reply: response.data.choices[0].message.content }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +app.listen(3001, () => { + console.log("Server listening on port 3001"); +});` +``` + +要测试这个 Node.js 程序,首先使用 npm install express axios dotenv 安装所需的包,并将你的 OpenAI API 密钥保存在 .env 文件中,格式为 `OPENAI_API_KEY=your_key_here`。然后,使用 node server.js 运行服务器。一旦运行起来,你可以通过使用工具如 curl 或 Postman 发送带有 JSON 体(如 `{"message": "Hello"}`)的 POST 请求到 [http://localhost:3001/api/chat](http://localhost:3001/api/chat) 来测试 /api/chat 端点,你应该会收到来自 OpenAI API 的回复。 + +```JavaScript +curl -X POST \\ + -H "Content-Type: application/json" \\ + -d '{"message":"Hello, who are you?"}' +``` + +# 参考资料 + +* https://www.postman.com/what-is-an-api/ diff --git a/docs/extra/extra3/extra3-ai-capability-starter-handbook.md b/docs/extra/extra3/extra3-ai-capability-starter-handbook.md new file mode 100644 index 0000000..4521578 --- /dev/null +++ b/docs/extra/extra3/extra3-ai-capability-starter-handbook.md @@ -0,0 +1,3267 @@ +# AI 能力手册:主流模型、产品形态与应用场景一览 + +随着生成式 AI 技术在各类产品和业务场景中的广泛落地,一个越来越现实的问题摆在每个我们面前: **到底有哪些 AI 能力可以用?** 在具体的需求里,又 **该选择哪一种能力、哪一类模型或哪一个产品来承载?** + +面对这种困惑,最直观的做法或许是 “临时抱佛脚”:**遇到需求再搜索市面上云服务厂商的产品 API,或者是对应模型,搜索市面上的商业级解决方案对照文档与 Demo进行处理** 。看到图片需求就想到图像生成,碰到文本任务就找来大模型,涉及语音交互就想起 ASR 和 TTS,再在海量 API 与服务中货比三家。然而,把零散的产品堆在一起,与在企业级场景中系统性地规划、选型和组合 AI 能力,是两件截然不同的事情。仅靠临时查资料与经验判断,会带来能力认知碎片化、方案设计随意、能力复用困难等一系列严峻挑战。 + +为了解决这些痛点,本文以“AI 能力全景图”为核心的整理思路应运而生。在这本手册里,我们想做的不是堆名词,而是帮你快速搞清楚三件事:**"这件事可以用什么 AI 能力做?大概该选哪一类模型或产品?接下来用哪些关键词去找 API、项目或服务来试?"** 通过从模态(文字、图像、音频、视频、3D、多模态)到架构层(模型、检索、Agent、平台工程)的系统梳理, **我们可以为每一类典型需求和场景找到对应的 AI 能力、代表性模型/产品,以及在真实业务中的常见用途** ,帮助团队以更低试错成本、更高决策效率和更强可复用性来建设 AI 体系。 + +在本篇手册中,我们将系统介绍当下主流的 AI 能力版图,从单一模态到多模态融合、从单点模型到平台与工程的整体框架,结合常见产品形态与应用场景,给出面向实践的能力选型参考。 + +> 由于 **内容较多** ,你可以在实践过程中遇到场景不知道如何选型的问题再查阅手册寻找参考;推荐你**根据具体应用方向,让 AI 参考该手册,给出可参考的模型选型建议、方案 API 调用建议即可。** + +如果你只想了解对应的类别,不想看具体内容,只需要看每个大章节的初始段内容即可,例如 1.1 、1.2 的内容,但不需要看 1.1.1 或者 1.1.2 的内容。 + +**推荐本手册只在需要时查阅对应部分或只浏览一级目录部分,若有兴趣再浏览全文。** + +**之后更新会在每个章节部分,推荐可尝试使用的模型 API 服务地址。** + +# 本节课你将学到 + +* AI 能力全景:从文本、图像、音频、视频、3D 到多模态、Agent、RAG、安全与平台工程的整体能力划分思路 +* 各能力对应的模型与产品:了解 Embedding、OCR、ASR、TTS、VLM、RAG 等关键能力背后的代表性模型与服务 +* 能力到场景的映射方法:掌握如何将“能力清单”转化为产品内容、搜索问答、智能客服、自动化运营等具体应用 + +完成本手册的学习后,你将对主流 AI 能力建立起入门级的系统化认知,不仅知道“市面上有哪些能力、常配哪些产品”,更能理解它们在整体架构中的位置和相互关系。知道在面对具体业务需求时,如何快速定位所需能力、做出有依据的选型,为构建 AI 能力体系打下坚实基础。 + +## 手册中涉及的模型参数 + +在进入具体能力地图之前,先澄清一个经常被提到、但又有点抽象的概念:到底什么算大模型?什么算小模型? + + **从学术上看** ,大模型通常指参数量在几十亿、上百亿乃至万亿级别的通用模型,小模型则是针对特定任务或场景、参数量更小(几千万到几亿级)的专用模型。 + + **从价格上看** ,如果一个模型的 API 调用非常便宜,比如按调用计费几厘钱、几分钱,或者只按每千 tokens 几厘到几分,而且没有特别强调通用大模型,那通常要么是典型的小模型(例如专门做 OCR、ASR、图片分类、内容审核的模型),要么是参数量较小的轻量版大模型(专门为了高并发、低成本做了压缩或蒸馏)。 如果单次调用价格明显偏高,比如一次调用就要几角甚至 1 元起步,那么大概率是大模型。 + +此外,如果产品文案里面会明确强调使用了大语言模型 LLM、通用大模型、多模态大模型,或提到端到端地完成从输入到输出的复杂任务(比如端到端对话机器人、端到端检索问答、端到端视频生成),那通常就可以把它视作是大模型。 + +相反,如果宣传重点在于某一个垂直能力,比如银行卡识别、发票识别、车牌识别、广告点击率预测、语音转写、内容安全审核,说明这个产品底层更可能是一个或一组小模型。 + +因此,在本文接下来的叙述中可以做个务实的约定: + +* 大模型更多指那类通用、可对话、可编程、往往价格略高的模型(包括它们的多模态版本,比如 GPT-4o、Gemini 1.5 Pro、Claude 3.5 Sonnet 等),它们能覆盖大部分通用文本、代码以及图像、音频、视频等多模态任务; +* 小模型则指那些为某个特定任务精调或定制的模型,通常价格更便宜、性能更稳定可控,但适用范围更窄,需要你在系统里主动组合与编排。 + +这里不妨补充一个关键的行业变化:手册中提到的很多模型能力,在 2021 年之前其实都是由 “小模型” 来承接的。针对特定场景、特定数据训练专属模型,以此满足精准需求。而**如今,绝大多数通用场景和任务已经可以直接调用大模型来解决** 。 + +从**精度与成本**的极致追求来看,小模型的训练与应用依然有其不可替代的价值;但**对于入门者而言,我们完全可以从学会找到并调用大模型 API 开始** ,再逐步深入高阶玩法。你只需要在成本、精度和延迟之间做权衡,再决定哪里要用通用大模型,哪里继续保留或引入专用小模型。 + +> **从一些常见产品认识**常用的文本和多模态通用大模型: +> +> * OpenAI 系列:GPT-4、GPT-4.1、GPT-4o、GPT-5.1 等 +> * Google 系列:Gemini 1.5 Pro、Gemini 1.5 Flash 等 +> * Anthropic 系列:Claude 3.5 Sonnet、Claude 3.5 Haiku 等 +> * 国内模型:通义千问 Qwen 系列、文心一言 ERNIE Bot 系列、GLM/智谱清言、百度的文心大模型家族、腾讯混元、讯飞星火、月之暗面的 Kimi 背后的大模型等 +> +> 更偏视觉和视频方向的大模型和服务,包括: +> +> * 图像生成:DALL·E、Midjourney、Stable Diffusion、SDXL、Flux 等 +> * 多模态视觉理解:GPT-4o、GPT-4.1 with Vision、Gemini 1.5(图文多模态)、Claude 3.5 Sonnet Vision、LLaVA 等 +> * 视频生成:Sora、Kling、Runway Gen-2、Pika、Luma、Veo 等 +> +> 语音和音频方向的大模型,包括: +> +> * 语音识别 ASR:Whisper 系列(Whisper、Whisper-large-v3 等)、Deepgram、各家云厂商的端到端 ASR 大模型(如讯飞、百度、火山、阿里等) +> * 语音多模态与语音对话:GPT-4o(端到端语音对话)、OpenAI Realtime、Gemini 1.5 的音频理解能力等 +> * TTS / 音频与音乐生成:OpenAI TTS、ElevenLabs、Suno、Udio、MusicGen 等 +> +> 3D / 空间方向的生成与理解模型,包括: +> +> * 文生 3D 和图生 3D:DreamFusion、Shap-E、GET3D、Zero-1-to-3、TripoSR 等 +> * NeRF / 神经渲染家族:Instant-NGP、NeRF 系列、Gaussian Splatting 相关模型等 + +# 1. 文本任务 (Text / NLP / LLM) + +在 AI 能力中,文字任务是最基础的功能。无论我们最终想做的是内容审核、搜索推荐、知识问答,还是写作助手、代码 Copilot,本质上都绕不开一个问题:机器如何真正看懂文字。 + +## 1.1 基础语言建模与表示 + +让我们从最底层的基础语言建模与表示讲起。它的作用是让机器先在统计意义上熟悉语言,并在此基础上为词、句子、文档找到一个稳定的向量矩阵表示,以便于后面的分类、匹配、抽取、生成等任务。不管未来要做什么文本相关任务,都或多或少需要先回答同一个问题:我怎么用一串数字,把这一段话表示出来? + +我们可以简单从场景、原理、模型三个角度来看这个问题的相关内容: + +* **场景** + * **检索搜索相关** + * 通用搜索引擎:用户随便输入一句话,得到含义相关的文档,而不是只做关键词精确匹配。 + * 站内搜索 / 电商搜索:用户用口语化的描述(比如“适合夏天通勤的白衬衫”),找到含义对应的商品。 + * 文档库 / 知识库检索:在技术文档、政策法规、企业知识库里,直接输入一句话获得相关条目。 + * **推荐排序相关** + * 信息流 / 内容推荐:根据用户最近看过、点过的内容,自动找出内容相近的其他内容继续推荐,而不是只靠人工规则或标签。 + * 电商 / 商品推荐:根据用户看过、买过、收藏过的商品描述,找到风格或用途相近的商品,做个性化推荐。 + * 用户兴趣建模:根据用户看过的标题、搜索过的词等,总结出几个主要兴趣方向,用来提升推荐和排序效果。 + * **问答助手相关** + * FAQ 问答:用户用不同说法问同一个问题(“怎么开发票?” vs “发票在哪里开?”),系统能跳到同一个答案。 + * 知识库问答 / 企业助手:用户用自然语言提问,系统到内部文档里按含义去匹配,找出最相关的段落回答。 + * **文本理解分析相关** + * 评论舆情分析:把大量评论、帖子按“在说什么 / 情绪怎样”大致分成几类。 + * 文本去重 / 相似检测:用于发现改写稿、伪原创文章。 + * 文档聚类 / 分组:把很多文章、报告按照内容相近分成几组,方便做导航、推荐或抽样检查。 + * **作为下游任务通用特征 (下游任务指的是用模型的基础能力,去实现更具体的文字处理任务)** + * 文本分类:情感分类、意图识别、垃圾内容识别等下游模型直接复用这一层的表示。 + * 信息抽取:实体识别、关系抽取在词 / 句子表示的基础上进行微调,而不是从头训练。 + * 文本生成:为摘要、改写、续写等生成任务提供语义表征输入,提升生成质量与可控性。 +* **原理** + 学习词、句子、文档的表示,为后续更复杂的任务作为基底。 + * 语言建模 + * 自回归语言模型:预测下一个 token(GPT 系列、LLaMA、Qwen 等) + * 掩码语言模型 (Masked LM):预测被遮盖 token(BERT、RoBERTa、ERNIE) + * 词 / 句子 / 段落表示 + * 静态词向量:Word2Vec、GloVe、FastText + * 上下文表征:BERT embedding、Sentence‑BERT 等 + * 文档级向量:用于语义检索、相似度匹配 +* **模型** + BERT / RoBERTa / ERNIE、GPT 家族、LLaMA / Qwen / Yi 等 LLM;各类 Embedding 模型(OpenAI text‑embedding‑3 系列、bge、E5、SimCSE 等)。 + +### **1.1.1 语言建模:通过“猜下一个词”学会语言** + +这一层的第一步,是先让模型在大量文本里 **熟悉语言规律** 。做法可以简单理解为:给模型出无数道“猜词题”,在看到一段话的上下文后,让它填上最合理的词(token)。练习题足够多、语料足够广,模型就会逐渐学会:一句自然的句子长什么样,哪些词经常一起出现,什么表达读起来别扭。这个过程叫“语言建模”,本质就是一套统一的 **猜词训练机制** 。 + +常见有两种出题方式,每种用一句话举个简单例子: + +1. **往后接(自回归)** :只给前面的内容,让模型猜“后面会怎么说”。 +2. 输入前缀:`今天下雨了,所以我` +3. 模型任务:猜下一个词,比如“ **带** (伞)”“ **没** (出去)”“ **打算** (在家)”等,然后再继续往后接。 + 这种方式主要锻炼模型对**续写、连贯性、常见表达**的把握。 +4. **挖空填词(掩码)** :把中间挖个洞,让模型利用前后文一起填空。 +5. 原句:`今天下雨了,所以我带了雨伞` +6. 训练句:`今天 [MASK] 了,所以我带了雨伞` +7. 模型任务:把 `[MASK]` 补成“ **下雨** ”这类合理的词。 + 这里模型必须同时看左边的“今天”“了”和右边的“所以我带了雨伞”,才能决定该填什么,更有利于学习 **整句语义** 。 + +通过在海量语料上反复做这两类“猜词题”,模型会逐渐积累起对语言的 **语感和统计常识** 。在此基础上,下一步我们再把这种能力显式地变成 **词、句子和文档的向量表示** ,为后续的检索、推荐和问答等任务打底。 + +### 1.1.2 词、句子与文档表示:把离散符号映射到语义空间 + +构建文本向量最早一代的方法是**静态词向量** :为每个词分配一份固定向量,训练好后不随上下文变化,直观、简单,但 **无法区分多义词在不同语境下的含义。** 为了解决这个问题,后来出现了基于上下文的动态表示方法:同一个词在不同句子中会生成不同的向量,完全由它所在的上下文决定。比如“苹果”在“苹果发布了新手机”中会更靠近“科技公司”的语义方向,而在“苹果富含维生素”中则更接近“水果”概念。 + +这种机制不仅提升了词层面的表达能力,也为句子和文档的向量化铺平了道路。对于句子,可以生成句向量;对于文档,可以整篇输入编码(如果长度允许),或分段编码后再通过注意力机制、层次化池化、对比学习等方式聚合出一个全局向量。近年来的专用 embedding 模型(如 bge、E5、text-embedding 系列)正是围绕“让语义相近的文本在向量空间中更近”这一目标持续优化,尤其在语义检索、相似匹配等任务上表现突出。 + +这套从上下文建模到句/文档向量生成的流程,已经成为搜索、推荐、问答等系统背后的核心基础设施,让我们回到前面提到的各类场景: + +* 检索搜索场景(通用搜索、电商搜索、知识库检索)都需要把用户输入和候选文档都编码成向量,然后在向量空间里做相似度匹配,找出语义最接近的结果,而不是只靠关键词精确匹配。 +* 推荐排序场景(信息流推荐、商品推荐、用户兴趣建模)需要把用户历史行为对应的内容转成向量,然后找到向量相近的新内容推荐给用户,实现"看过 A 推荐 B"的个性化效果。 +* 问答助手场景(FAQ 问答、知识库问答)需要把用户的提问和知识库里的问题或段落都编码成向量,通过向量相似度找到最匹配的答案。 +* 文本理解分析场景(评论舆情、去重、聚类)需要先把每条文本转成向量,再基于向量做聚类、相似度计算或分类。 +* 下游任务场景(文本分类、信息抽取、文本生成)则是直接把这一层的向量表示作为输入特征,喂给后续的分类器、抽取器或生成器,避免从头学习语义。 + +工程上,常见做法是封装成统一的"文本向量服务":输入任意一段文本,输出一串固定维度的向量,供搜索、推荐、问答等多个系统共享使用。在产品层面,这一层的能力主要体现在:搜索和推荐中的语义召回(不再只依赖关键词,而是通过向量相似度召回"说法不同但意思相近"的内容),以及面向企业知识库、FAQ、案例库的统一 embedding / 向量检索服务。 + +## 1.2 文本分类与文本匹配(Classification & Matching) + +在上一节中,我们通过基础语言建模与表示,为每一段文本找到了在语义空间中的“坐标”。但仅有坐标还不够,业务真正关心的问题往往是:这段文本属于哪一类?和另一段文本是不是讲同一件事?两句话之间在逻辑上是相互支持还是互相矛盾?你可以把它理解为:用分类和匹配这两个能力,把底层的向量表示转化为可以直接驱动业务决策的标签与相关性信号。我们仍然从场景、原理和模型三个角度来梳理这一层: + +* **场景** + * 内容理解与审核:给评论、帖子、文章打上主题、情感、风险等标签,用于审核、推荐、统计分析。 + * 推荐与排序:根据“用户兴趣标签”和“内容标签”的匹配程度,决定展示哪些内容、排在多前。 + * 搜索与 FAQ:用户随便输入一句自然语言问题,系统能够自动找到最相关的问题‑答案对或文档片段。 + * 相似内容识别:在大量文本中找到“内容相近”的条目,用于去重、合并统计、推荐“相关内容”。 + * 逻辑关系判断:判断两句话之间是互相支持、互相矛盾,还是无关,用于事实核查、多轮对话一致性检查等。 +* **原理** + 在语义表示的基础上,对整段文本或文本对进行整体判断: + * 文本分类:给单条文本打标签(如情感、主题、风险类型等); + * 文本匹配:判断两段文本之间的相似度、相关性,或“问题–答案”是否匹配; +* **模型** + 以预训练 encoder 为基础,接上简单的分类 / 匹配结构: + * 单文本分类:BERT / RoBERTa / DeBERTa + 全连接分类层; + * 文本匹配:Sentence‑BERT、SimCSE、双塔(Bi‑Encoder)、交叉编码器(Cross‑Encoder); + * 复杂判断:在 LLM 上通过指令微调,让模型直接输出标签或逻辑关系。 + +### 1.2.1 文本分类:从“懂内容”到“给内容定性” + +借助上一层的语义表示,我们可以非常自然地在其上方接一个简单的分类头,通过少量标注数据,让模型学会回答一个问题: **“这段文本属于哪一类?”** 。 + +最经典的是 **情感分类** 。用户的一句评价,可能是认可、抱怨,也可能只是陈述事实。模型在拿到这句话的向量表示之后,只需要再接一个 softmax 分类层,就能输出“正向 / 负向 / 中立”的概率。这类能力在电商、社交平台、应用市场等场景中,都已经非常成熟。 + +另一大类是 **主题 / 行业分类** 。新闻推荐里,我们希望知道一篇文章是体育、财经还是娱乐;企业内部的客服 / 工单系统,则更关心这是产品咨询、功能异常还是投诉建议。这些标签既可以帮助内容被更精准地路由到合适的流程中,也可以作为推荐排序阶段的重要特征。 + +更进一步,**风险 / 合规分类**则直接与平台安全相关。我们会针对广告导流、谩骂攻击、涉政敏感、低俗色情等类别设置专门的分类模型,配合人工审核,对高风险内容进行拦截或降权。可以说,绝大部分内容安全策略的第一道闸门,都是由这类分类器构成的。 + +可以看到,到这一层为止,我们已经能够把“抽象的语义表示”转化为若干业务可用的标签。接下来,我们要讨论的是:当文本之间产生关系时,我们又如何进行 **匹配与推断** 。 + +### 1.2.2 文本匹配:为一句话“找到最合适的另一句” + +与分类对“单个文本定性”不同,**文本匹配**关注的是“两段文本之间的相关性”。在很多产品里,这往往是实现“智能”的关键一环:用户说了一句话,系统能不能找到知识库里最合适的一条进行回应,完全取决于匹配质量。 + +最基础的是 **语义相似度计算** 。我们会先用上一层的 embedding 模型,把两个句子编码成向量,再通过余弦相似度、点积等方式,判断它们在语义空间里的距离。像 SimCSE、Sentence‑BERT 这类模型,就是通过对比学习的方式,专门把“相似的句子对”拉近,把“不相似的句子对”推远。 + +在此之上,**复述检测**和**抄袭检测**只是特定应用场景的匹配任务。前者用于内容去重,避免平台充斥着重复表达;后者则在教育、知识社区等场景中,用来识别高度相似的回答或文章。技术上,它们本质都是根据文本相似度来做二分类或排序。 + +一个非常重要的下游应用是 **问答匹配** 。当用户提出一个自然语言问题时,我们不会直接用关键词去匹配 FAQ,而是通过语义向量先做召回,再用更精细的匹配模型(如交叉编码器 Cross‑Encoder)对若干候选进行重排序,选出最可能对应的那一条。这一链路构成了 FAQ 机器人和文档问答系统的基础。 + +在这一层,我们已经具备了对“整段文本”进行分类和关系判断的能力。但在很多场景里,业务并不满足于此,而是进一步希望知道: **这段文本中具体提到了哪些实体、发生了什么事件** 。这就自然引出了下一节的主题—— **序列标注与信息抽取** 。 + +## 1.3 序列标注与信息抽取(Sequence Labeling & Information Extraction) + +在完成了对文本整体的分类和匹配之后,我们往往会遇到一个更细致的诉求:不仅要知道“这篇文章是关于什么的、风险高不高”,还要进一步知道“它具体提到了谁、在哪儿、什么时候、金额是多少”。这一节,就是在整体判断之上向“细粒度结构化”迈出的关键一步。你可以把它理解为:在已经知道“应该看哪一类文本、它大概讲什么”的前提下,从文本内部挖掘实体、关系、事件和各类字段,让非结构化文本可以直接被业务系统消费。我们同样从目标、原理、模型和产品四个方面来看这一层: + +* **场景** + * 行业文本结构化:从合同、报告、公告、病历、政策等文档中,抽取出人名、机构、金额、时间、条款等关键字段,用于入库和检索。 + * 知识图谱与关系网:从新闻、论文、问答中识别实体及其关系,构建“谁和谁有什么关系”的图谱,用于搜索、推荐和分析。 + * 票据与单据处理:对发票、对账单、报销单等,自动提取抬头、税号、金额、日期等字段,减少人工录入。 + * 舆情与事件分析:从海量文本中抽取“谁在什么时候在哪儿做了什么”,用于事件跟踪、风险预警与统计报表。 + * 日志与工单结构化:把客服对话、工单、系统日志等非结构化文本里的关键信息抽出来,方便统计、监控和自动化处理。 +* **原理** + 在 token / 短语层面,对文本进行细粒度标注与结构化: + * 序列标注:对每个 token 贴标签(如人名、地名、机构名、产品名等),实现命名实体识别、词性标注、短语切分等; + * 关系与事件抽取:在实体之上识别“实体‑实体”之间的关系,以及“谁在何时何地做了什么”的事件结构; + * 业务字段抽取:围绕具体业务 schema(如合同字段、票据字段),将长文档转成标准化的 key‑value 或记录表。 +* **模型** + 在预训练表示的基础上,通过序列标注或 span 抽取等结构完成信息提取: + * 序列标注模型:BiLSTM‑CRF、BERT + CRF / Softmax 等; + * Span‑based 抽取:直接预测实体 / 关系片段的起止位置; + * 文档级抽取:结合版式、布局的 DocIE 类模型; + * 基于 LLM 的抽取:通过 Prompt / Few‑shot,让大模型按指定格式抽取所需字段。 + +### 1.3.1 序列标注:给每个 token 和短语贴上语义“标签” + +在文本分类阶段,我们只关心整段文本属于哪一类;而在序列标注阶段,我们要对文本中的每一个 token、每一段短语进行标记。最典型的任务是命名实体识别(NER):识别人名、机构名、地名、产品名、疾病名等特定类型的实体。 + +* 例如,在句子“张三在北京加入某科技公司”中,把“张三”标为人名、“北京”标为地名、“某科技公司”标为机构。 + +从建模方式上看,传统的做法是使用 BiLSTM + CRF 这类序列标注结构,后续则更多采用 BERT + CRF 或 BERT + Softmax,利用预训练 encoder 的上下文表征能力,来判断每个 token 的标签(如 B‑ORG、I‑ORG、O 等)。在实践中,NER 模型往往是后续知识图谱、关系抽取的第一道“预处理”。 + +除了 NER 外,词性标注、短语切分也属于典型的序列标注任务。它们更多服务于底层语言分析,为后续更复杂的语法 / 语义任务提供基础结构。 + +* 比如对“快速 提升 模型 性能”标出“快速”为副词,“提升”为动词,“性能”为名词,用于下游分析。 + +### 1.3.2 关系与事件抽取:把“点”连成“线”和“故事” + +当我们通过序列标注识别出文本中的实体之后,一个顺理成章的问题是:这些实体之间到底是什么关系,它们共同构成了什么样的事件? + +关系抽取关注的是“实体对 + 关系类型”。例如,在一句“张三于 2024 年加入某科技公司担任 CTO”中,我们不仅要识别“张三”和“某科技公司”这两个实体,还要抽取它们之间的“就职于”关系。 + +* 简单来说,就是从“张三 – 某科技公司”这对实体上,贴上“任职”这类关系标签。 + +在关系之上,事件抽取则试图重建“谁在什么时候、什么地点,做了什么事情”。以一则新闻为例,一个标准的事件模板可能包含:事件类型(收购、合作、事故)、时间、地点、参与方、金额、后果等多个槽位。事件抽取模型需要从冗长的文本中自动填充这些槽位,从而构建出可被检索、统计和推理的“事件表”。 + +* 比如从“某公司以 5 亿元收购另一家公司”中抽出:事件类型=收购,金额=5 亿元,参与方=两家公司。 + +在建模方法上,除了传统的序列标注式抽取,我们还会采用 Span‑based IE(直接预测实体 / 关系 span 的起止位置)以及近年来兴起的 Prompt‑based IE 和基于 LLM 的 Few‑shot 抽取。后者的优势在于可以通过自然语言提示,快速适配新的 schema,减少大量重新标注和训练的成本。 + +从工程角度看,成熟的抽取系统往往会形成一条管线: + +* 上游 NER / 序列标注识别实体; +* 中间层做关系和事件结构建模; +* 下游把结果写入数据库或知识图谱,供搜索、分析和风控系统消费。 + +## 1.4 文本生成与编辑(Text Generation & Editing) + +在前面几节中,我们已经依次构建了“表示 → 分类匹配 → 序列标注与抽取”这条理解链路:模型不仅能把文本映射到语义空间,还能对整段文本做判断,并从中抽取出结构化信息。这一节要做的,是把这条理解链路“反向”再走一遍:在充分理解的基础上,让模型主动去生产、改写、压缩和润色文本。你可以把它理解为:在语义空间中进行“反向编码”,把内部表示重新变成高质量的自然语言输出,是整条文字模态能力链里最贴近用户感知的一层。我们依旧从目标、原理、模型和产品四个维度来拆解: + +* **场景** + * 日常写作与办公:生成邮件、通知、方案初稿,或对现有文本进行扩写、改写和润色。 + * 知识管理与总结:对长文档、报告、会议记录进行自动摘要,帮助快速抓住重点。 + * 客服与问答:根据用户问题和检索到的资料,自动生成结构清晰、口吻统一的回答。 + * 营销与创意内容:生成广告文案、社交媒体帖子、活动介绍、脚本等。 + * 多语言场景:在保持原意的基础上,完成翻译、本地化改写,适配不同语言和场景。 +* **原理** + 在语言建模的基础上,对文本进行“从无到有”和“基于已有内容的修改”: + * 自由生成:根据意图、提示词或大纲,从头生成一段完整的文本; + * 受控改写:在保持核心信息不变的前提下,调整风格、长度、结构(如摘要、扩写、风格转换); + * 纠错与润色:修正错别字、语法问题,优化表达顺序和逻辑结构。 +* **模型** + 以大规模预训练 + 指令微调的生成模型为主: + * 指令微调 LLM:GPT 系列、LLaMA / Qwen / GLM 等,用于通用生成与编辑; + * Seq2Seq 模型:T5、BART、mT5 等,用于摘要、翻译、格式转换等任务; + * 对齐与安全:通过 RLHF / RLAIF 等手段,让生成内容更加符合指令和安全要求。 + +由于这个部分基本等于提示词工程,故不再过多阐述,可以自行查看提示词工程部分的教程。 + +# 2. 图像模态(Image / Vision) + +在 AI 能力中,图像模态负责“用视觉理解世界”。不管最终想做的是安防监控、自动驾驶、短视频特效、电商智能修图,还是多模态问答、AI 画画,本质上都离不开一条路径:从原始像素出发,逐步获得对画面的结构化理解与可控生成能力。 + +## 2.1 底层视觉(Low‑Level Vision) + +在上一节中,我们从整体上介绍了视觉模态在多模态系统中的角色,以及它与语言、语音之间的衔接方式。但在真正进入目标检测、图像理解、视觉问答这些“高层语义任务”之前,还有一个往往被忽略、却至关重要的基础能力层——底层视觉。你可以把它理解为:在“看懂图里是什么”之前,系统需要先解决“这张图本身质量如何”“有哪些稳定的局部结构可以被上层复用”这两个问题,用一层通用的复原、增强和结构抽取,将原始像素转化为更干净、更稳定的图像表示。 + +从工程角度看,底层视觉既直接影响用户肉眼看到的“画质体验”,也决定了上层检测、识别、分割等任务的输入分布是否健康。如果这一层做得不好,后面所有模型都要在“噪声大、畸变重、光照极端”的环境下硬扛;相反,如果在这一层就把图像尽可能修好、结构信息提炼好,高层任务就可以在一个更友好的基座上发挥能力。下面我们同样从场景、原理和模型三个角度来梳理这一层: + +* **场景** + * 相机与拍摄设备:手机/相机的自动去噪、HDR、夜景模式、防抖,多帧融合提升细节和动态范围。 + * 内容平台与短视频:上传图片/视频的一键画质增强,去压缩块、提高清晰度和对比度,提升主观观感。 + * 老照片与文档修复:老照片的去噪、上色、超分辨率;拍歪、拍暗的票据、合同、书页自动拉正、增强,方便 OCR。 + * 监控与安防:低照度监控画面的降噪、去雾、防雨滴、提升分辨率,为后续人脸/车牌识别打基础。 + * AR/VR 与三维重建:为 SLAM、全景拼接、三维重建提供稳定的角点、边缘和局部描述子,保证跟踪与配准鲁棒性。 +* **原理** + 围绕“图像质量”和“局部结构”两个核心目标,对像素级信息进行物理与统计建模: + * 图像复原与增强:假设观测图像是理想图像经过噪声、模糊核、压缩和成像非线性等退化后得到,在这一假设下进行去噪、去模糊、去压缩伪影、低光照增强和超分辨率重建,使输出更接近真实场景成像,同时符合人眼感知习惯。 + * 结构特征抽取:在不引入具体语义标签的前提下,从像素梯度和纹理统计中提取边缘、角点、局部纹理、显著区域等特征,为后续的检测、配准、跟踪、分割提供“几何骨架”。 + * 几何与光照预处理:基于相机模型和简单几何线索(直线、消失点、对称性等)估计畸变与透视关系,通过去畸变、拉正、对比度与光照归一化等操作,将原始图像对齐到一个更标准、更稳定的输入空间。 +* **模型** + 综合使用经典图像处理方法和深度学习模型,在效率与效果之间做权衡: + * 传统图像处理:双边滤波、非局部均值、引导滤波、Retinex、直方图均衡、Canny/LoG 边缘检测、Harris/FAST 角点、SIFT/SURF/ORB 描述子、Hough 变换、相机标定与几何校正等。 + * 深度复原与增强模型:基于 CNN 或视觉 Transformer 的去噪、去模糊、超分辨率、去雨/去雾/去压缩伪影模型(如 EDSR、RCAN、SwinIR、ESRGAN 等),以及多帧/视频增强网络,用端到端方式学习从退化图到高质量图的映射,或使用现代的图像编辑模型实现例如即梦和 qwen 编辑模型。 + +### 2.1.1 图像复原与增强:从“看得见”到“看得清” + +在底层视觉里,图像复原与增强首先面对的是各种退化:噪声、模糊、压缩失真、低光照、动态范围不足等。很多真实场景下的原始图像并不“干净”:夜景和室内弱光会让画面布满颗粒和色斑,抓拍和监控画面常常因为运动、对焦不准而发虚,视频压缩会带来一块一块的方块噪声。复原与增强的目标,就是在不改变图像语义内容的前提下,尽可能恢复清晰的细节和自然的观感,把“模糊、灰暗、脏”的输入变得“清楚、明亮、舒适”。 + +典型任务包括去噪、去模糊、低光照增强和超分辨率等。去噪和去模糊需要在局部纹理和整体结构之间权衡:既要压制高频噪声、反卷积掉模糊核的影响,又不能把真实细节一起抹平;低光照增强则要在提升亮度与对比度的同时,避免暗部噪声被一并拉起,并校正偏色、压住过曝区域;超分辨率则侧重在放大的同时补出合理的高频信息,让放大后的图像既不显得“糊”和“塑料感严重”,又不过度“凭空捏造”细节。现代方法大多采用深度网络(CNN 或视觉 Transformer),在大量“退化–清晰”成对数据上学习从观测图像 y 到理想图像 x 的映射,同时使用包含像素误差、感知损失和对抗损失的组合目标,在“指标好看”和“人眼好看”之间取得平衡。 + +这些能力在产品中的呈现往往是隐性的:手机相机的夜景模式和 HDR 拍照、短视频平台的一键画质增强、老照片修复工具、监控系统的云端增强服务,本质上都依赖这一层的复原与增强模块。对业务而言,它们既直接影响用户对“画质”的主观感受,也间接决定了上层检测、识别、分割等算法的输入质量。可以说,越是复杂的上层视觉任务,越依赖底层有一个高质量、分布稳定的“图像地基”。 + +### 2.1.2 结构特征与预处理:为高层理解搭好“脚手架” + +当图像质量被修复到一个可用水平之后,底层视觉的第二项关键工作,是从像素中抽取出与具体语义暂时无关、但对几何结构和视觉感知非常重要的特征,并对几何和光照进行统一。这一步不会直接告诉你“这里是一辆车”或“这是某个人的脸”,但会回答“哪里有清晰的轮廓和拐角”“哪些区域纹理结构显著”“图像是否发生畸变或倾斜”等问题,为上层模型提供可靠的结构性输入。 + +在特征提取方面,边缘和角点是最基础的元素。通过 Canny、Sobel 等算子,系统可以在整张图上标出灰度或颜色变化最剧烈的“边缘”,这些往往对应物体轮廓、部件分界和纹理走向;角点检测(如 Harris、FAST)则找到局部梯度在多个方向上都变化显著的“拐角”,通常出现在物体的角、线条交汇处。进一步地,像 SIFT、SURF、ORB 这样的局部描述子,会在这些关键点周围编码一小片区域的纹理模式,使得同一物理点在不同视角、尺度和一定光照变化下仍然可以被匹配出来,这为图像配准、全景拼接、SLAM、AR 跟踪和三维重建提供了基础支撑。 + +与特征提取并行的,是各种几何和光照预处理操作。广角镜头带来的桶形/枕形畸变、拍摄文档时的倾斜和透视拉伸,都会通过直线检测、消失点估计等底层几何线索被识别出来,并通过去畸变、拉正、透视矫正等步骤被“拉回正常”;全局或自适应直方图均衡、对比度拉伸和光照归一化,则在保证细节不丢失的前提下,提升局部对比度、减弱光照不均和阴影的影响。颜色空间变换(RGB→HSV/Lab)与颜色直方图统计,为简单的基于颜色的分割、显著性区域检测、色偏校正等任务提供直接可用的输入。 + +在端到端深度学习成为主流之后,这些结构特征和预处理有一部分被“内化”到了网络前几层的卷积核和归一化策略中,不再以显式算子的形式出现在系统架构图上。但从功能上看,它们依然扮演着同样的角色:先用一层相对通用的、与具体类别无关的底层处理,把原始像素整理成在几何形态、光照条件和局部结构上更稳定的表示,再交给上层的分类、检测、分割和多模态模块去完成“理解这是什么”的任务。没有这层“脚手架”,上层模型就不得不在噪声大、畸变重、结构模糊的原始图上硬扛,整体系统的鲁棒性和泛化能力都会显著下降。 + +## 2.2 图像分类与识别(Image Classification & Recognition) + +在大部分图像任务中,业务方真正关心的问题是:**这张图整体属于哪一类?图里的这个人是谁?这名行人在不同摄像头下是不是同一个?** 你可以把这一层理解为:在一个统一、干净的输入空间上,为整张图像或者整个人/目标打上“类别标签”或“身份标签”,把视觉信号转化为最直接可用的识别结果。 + +从产品视角看,图像分类与识别是最早大规模落地的一批视觉能力,也是很多上层应用的“入口模块”。电商和内容平台用它来自动给图片打标签、识别主体品类;安防和门禁系统用它来确认“是不是同一个人”;行人重识别系统则在多路摄像头之间抽丝剥茧,找出同一目标的跨场景轨迹。下面我们同样从场景、原理和模型三个角度来梳理这一层: + +* **场景** + * 通用图片理解:为用户上传的图片自动打上“风景 / 美食 / 宠物 / 文档”等主题标签,用于检索、推荐、内容审核。 + * 人脸识别与门禁:在人脸门禁、考勤系统中,根据人脸图像识别个人身份,实现“刷脸通行”“刷脸打卡”。 + * 行人/人员重识别:在不同摄像头画面中判断是否为同一行人或同一人员,用于安防检索、轨迹分析。 + * 人体属性识别:在不直接确认身份的前提下,识别性别、年龄段、是否戴帽子/背包/穿制服等属性,为检索和行为分析提供线索。 +* **原理** + 在统一的视觉特征空间中,对整张图或整个人/目标进行判别式建模: + * 图像分类:以整张图像为输入,通过卷积网络或视觉 Transformer 提取全局特征,并在特征顶层接一个分类头,输出单标签或多标签的类别概率,用于回答“这是一张什么类型的图片”。 + * 身份/实例识别:将“是谁”的问题转化为特征空间中的度量学习问题,即学习一个嵌入空间,使同一身份的图像特征彼此接近,不同身份的特征彼此远离,然后用最近邻搜索或聚类完成识别与检索。 + * 属性识别:在共享的行人/人体特征之上,增加多任务输出头,预测性别、年龄段、衣着颜色、是否携带物品等属性标签,使得同一特征可以服务于多种下游检索与分析需求。 +* **模型** + 以深度卷积网络和视觉 Transformer 为主干,结合分类头或度量学习头实现不同类型的识别任务: + * 图像分类 Backbone:ResNet、DenseNet、EfficientNet、ConvNeXt、Vision Transformer (ViT)、Swin Transformer 等,通常在 ImageNet 等大规模数据集上进行预训练,再在具体业务数据上微调。 + * 通用分类结构:Backbone + 全连接分类层(Softmax / Sigmoid),用于单标签或多标签图像分类任务,可通过类别重加权、focal loss 等应对长尾分布。 + * 身份/实例识别:在 Backbone 的特征输出之上,使用 ArcFace、CosFace、SphereFace 等带角度约束的损失函数,显式拉大不同身份之间的类间间隔,提升在特征空间中的可分性,并通过向量检索(ANN)完成大规模库上的比对。 + * 行人/属性识别结构:针对行人 Re-ID 和人体属性识别,常见做法是采用共享 Backbone 提取行人特征,再在顶层分出“身份分支”和“属性分支”,既优化跨摄像头的身份区分能力,又兼顾多属性预测。 + +对应到具体产品形态,这一层的能力常以“图片内容识别 / 分类 API”“人脸识别 SDK / SaaS”“行人重识别平台”等方式对外提供。它们往往既直接驱动业务决策(如门禁放行、内容标签写入),又作为上游,为后续的检索、推荐、行为分析和多模态理解提供结构化标签与稳定的身份表征。下面,我们分别从图像分类和身份/属性识别两个角度展开。 + +### 2.2.1 图像分类:回答“这是一张什么图?” + +在最基础的图像分类任务中,系统面对的是整张图片,目标是给它贴上一个或若干个语义类别标签。最常见的是单标签分类,例如在 ImageNet 这样的数据集中,每张图被标注为“狗”“猫”“汽车”“飞机”等一个主类别;在业务场景中,这类能力被广泛用于给用户上传的图片加上“风景 / 美食 / 宠物 / 人像 / 文档”等主题标签,支持检索、推荐和内容审核。与文本分类类似,模型会在预训练 Backbone 提取的全局视觉特征之上接一个全连接 + Softmax 层,对所有候选类别输出一个概率分布。 + +在很多实际应用中,一张图往往同时属于多个类别,比如一张“海边日落自拍”图片,既可以是“风景”,也是“人像”,还可能被标注为“旅行”“海边”。这时就需要多标签分类(Multi‑label Classification):模型依然从整图特征出发,但输出层不再是互斥的 Softmax,而是对每个标签单独预测有/无的概率(Sigmoid),并采用多标签损失函数来训练。为了应对现实数据中大量“长尾类别”(冷门标签样本极少),多标签分类模型常会加入类别重加权、难例挖掘或标签结构建模等机制,提升对小众类别的召回。 + +在人机接口层面,图像分类通常以“图片内容识别 API”的形式对外提供。上游业务只需上传一张图片,即可获得一组类别标签及其置信度,用于后续的策略判断:比如广告投放系统可以根据图片内容限制某些敏感类目,电商平台可以利用图片分类辅助商品类目纠错,内容平台则用来丰富推荐特征和审核信号。虽然从技术上看,这类能力相对成熟,但它仍然是后续目标检测、实例分割、视觉问答等更复杂能力的基石。 + +### 2.2.2 图像识别与属性识别:回答“这是谁 / 这是什么实例?” + +与“这是一张什么类型的图”不同,图像识别更关心的是“图中的这个人/目标是谁”,也就是身份级、实例级的区分。典型代表是人脸识别和行人重识别:前者在门禁、考勤、支付等场景中判断“当前人脸与库中哪一个身份最接近”;后者则在多路摄像头与不同时间段的监控画面中,寻找是否存在同一行人,辅助案件回溯和轨迹分析。这类任务的核心,不再是简单的多分类,而是如何在特征空间中学习到一个“类内紧凑、类间分离”的嵌入,使同一身份在不同姿态、光照、摄像头下拍摄的图像仍能被聚到一起。 + +在模型设计上,人脸识别和行人重识别通常采用类似的范式:先用 ResNet、ConvNeXt、ViT、Swin 等 Backbone 提取以人脸/行人为中心的特征,再接上专门为度量学习设计的损失函数,如 ArcFace、CosFace 等。与普通分类损失不同,这些损失直接在角度空间或特征空间上约束类间边界,显式拉大不同身份特征之间的间隔,从而使得训练好之后的特征可以拿来做大规模向量检索,而不必局限于训练时见过的固定类别。在线服务时,系统会先对图库中每个身份的特征进行预计算和索引,再对上线查询的人脸/行人特征进行近似最近邻搜索,找到最相似的若干候选,并结合业务阈值和多模态信息做最终决策。 + +与“直接身份识别”相对应的,是不指向具体人的 **属性识别** 。在很多安防和零售场景下,系统只需要知道“是男性还是女性”“大概年龄段”“是否戴帽子/口罩”“衣服颜色和款式”“是否背包/拉行李”等属性,用于快速筛选目标,而不必、也不适合直接输出个人身份。这类任务通常在共享的行人/人体特征之上,接多个并行的属性头(头的意思是输出概率的位置,可以多几个概率输出的结果用于判断类别),每个头负责预测一个或一组属性标签,形成一个多任务学习框架。一方面,多任务训练可以让特征更加丰富、泛化更好;另一方面,属性本身也可以作为 Re-ID 或检索的辅助条件,提升系统在复杂场景下的可用性。 + +在产品形态上,这一类能力通常打包为“人脸识别 SDK/云服务”“行人重识别平台”“人体属性识别 API”等,被集成进门禁闸机、考勤机、安防平台和视频结构化系统。与通用图像分类相比,它们对数据安全和隐私保护要求更高,对误识率和召回率的权衡也更敏感,因此在算法之外,还会辅以质量检测(如是否为真人、是否为遮挡/翻拍)、活体检测、多模态交叉验证等机制,构成更完整、更负责任的身份识别方案。 + +## 2.3 目标检测(Object Detection) + +在前面的图像分类与识别中,我们只对“整张图”或“整个人”给出一个整体标签,而忽略了它在图中出现的位置和大小。然而,真实业务更常见的问题是:**这张图里有哪些物体?它们分别在什么位置?** 比如一张街景图中,我们希望同时标出所有的行人、车辆、交通标志牌;在工业产线上,需要在同一画面中标出所有瑕疵区域、零件位置。目标检测就是为这些需求而生的:它在单张图像或视频帧中,同时预测每一个物体的 **位置(bounding box)和类别** ,是众多下游视觉任务(跟踪、分割、行为分析、多目标计数等)的基础能力。 + +从工程使用角度看,目标检测是很多视觉系统的“第一步结构化”,把一张原始图分解为若干个带标签的矩形框,每个框都可以进一步送到其他模块做识别、跟踪、属性分析乃至语义生成。安防摄像头中行人/车辆的检测、无人零售货架上商品的检测、工业质检中缺陷/异物的检测、以及云厂商提供的「目标检测 / 物体检测」API,本质上都依赖这一层能力。下面我们从 **场景** 、**原理**和**模型**三个角度来梳理目标检测,并在后续小节中分别展开关键方向。 + +* **场景** + * 安防与交通监控:在摄像头画面中实时检测行人、车辆、非机动车、交通标志、逆行/占道目标等,为后续的行为分析和告警提供基础。 + * 工业质检与制造:在生产线上检测产品缺陷(划痕、破损、异物)、零部件位置、装配是否缺失,支持自动剔除与机器人定位。 + * 零售与物流:无人零售货架商品检测、结算;仓储中包裹、托盘、码垛的目标检测与定位,辅助库存盘点和机器人抓取。 + * 内容理解与审核:在图像/视频中检测人、logo、武器、敏感物品等,为内容审核、广告合规和品牌识别提供结构化信号。 +* **原理** + 目标检测的核心,是在图像上构建一个密集预测机制: + * 将输入图像通过 Backbone 提取为多尺度特征图,在这些特征图上,对每个“位置”(或候选区域)同时预测“是否有目标”“是什么类别”“对应的 bbox 参数”。 + * 按照架构划分,有先生成候选框再精修的 **双阶段检测(Two‑stage)** ,以及直接在特征图上做分类+回归的一体化 **单阶段检测(One‑stage)** ,两者在精度与速度上各有侧重。 + * 按候选框设计划分,有依赖预定义锚框(anchor)的 **anchor‑based** 方法,也有直接预测中心点/边界的 **anchor‑free** 与基于集合匹配的 **DETR 家族** 。 + * 为应对现实数据中的小目标、密集目标、遮挡和尺度变化,检测器通常会结合多尺度特征(FPN)、更高分辨率输入、特定损失函数与后处理策略(如 NMS 变体、多尺度测试)进行优化。 +* **模型** + 检测模型大体由**骨干网络 + 特征金字塔 / 头部结构 + 损失与后处理**三部分构成: + * 经典双阶段检测器:Faster R‑CNN、Mask R‑CNN 等,先通过 RPN 产生候选框,再对每个候选区域做精细分类与回归,精度高、结构清晰,适合对精度要求极高的场景。 + * 单阶段检测器:SSD、RetinaNet、YOLO 系列(YOLOv5/6/7/8、YOLOX、YOLOv10 等)等,在一个统一的网络中完成检测,结构紧凑、延迟低,是工业界实时检测的主力。 + * Anchor‑free / Transformer 检测器:FCOS、CenterNet、ATSS 等以像素点为中心直接预测框;DETR / Deformable DETR 等通过 Transformer 和集合匹配,将检测视为“从一组查询中生成一组目标”的问题,简化多种手工设计。 + * 视频检测与跟踪:在图像检测器的基础上,引入时序信息与关联策略(如跟踪头、光流、轨迹匹配),形成 Detection + Tracking 的统一框架,支撑长时间、多目标的行为分析。 + +综合来看,目标检测处于视觉能力谱系的“中枢位置”——它一方面承接底层视觉提供的干净图像输入,另一方面把图像解构成可供识别、跟踪、分割和多模态理解使用的“目标级”元素。下面,我们分别从 **单/双阶段检测架构** 、**Anchor‑based / Anchor‑free / Transformer 检测**以及**小目标与视频检测**三个方向展开。 + +### 2.3.1 单阶段与双阶段检测:精度–速度的结构权衡 + +从架构上看,目标检测最经典的划分是 **双阶段(Two‑stage)与单阶段(One‑stage)** 。二者的主要区别在于:是先“粗选一批候选框,再进行精修”,还是在特征图上“一次性预测完所有框和类别”。 + +双阶段检测以 Faster R‑CNN 为代表。它首先在 Backbone 特征图上通过 RPN(Region Proposal Network)生成一批“高概率包含目标”的候选框(第一阶段),然后对每个候选区域进行 RoI 对齐与特征提取,再做更精细的分类与边框回归(第二阶段)。这种设计的好处是:大量负样本在 RPN 阶段就被过滤掉,第二阶段可以集中精力在少数候选区域上做高质量的判别,因此在精度上往往更有优势,也更容易扩展到实例分割(Mask R‑CNN)、关键点检测(Keypoint R‑CNN)等任务。不过,多阶段结构带来的计算与实现复杂度相对较高,更适合对实时性要求不那么苛刻、但强调精度和可扩展性的离线或准实时场景。 + +单阶段检测则力图打通整个流程,在一个统一的网络中同时完成类别分类和边框回归。代表模型包括 SSD、RetinaNet 和 YOLO 系列等:它们直接在多尺度特征图的每个位置上预测若干候选框的“前景/背景 + 类别 + bbox”,省去了显式 proposal 阶段,更适合做端到端加速与部署。早期的单阶段检测器相对双阶段在精度上有一定差距,但凭借结构简单、速度快,在工业界迅速占据主导;随着 FPN、focal loss、IoU‑aware loss,以及更强 Backbone 和 Neck 的引入,RetinaNet、YOLOX、YOLOv7/8/10 等新一代模型已经在很多任务上实现了“接近甚至赶超双阶段”的精度–速度平衡。 + +在应用层面,工程上通常会根据需求在这两类架构间做取舍:对于云端批量离线分析、需要较高精度和可扩展性(如同时做检测+分割+关键点)的任务,双阶段检测仍然是一个稳定可靠的选择;而对于边缘设备、移动端应用、摄像头实时检测等延迟敏感场景,YOLO 系列等单阶段检测器几乎是默认首选,并且往往会结合量化、剪枝、蒸馏等技巧,以进一步压缩模型和提升吞吐。 + +### 2.3.2 Anchor‑based 与 Anchor‑free:从手工设定到端到端学习 + +在如何定义“候选框”这一问题上,检测方法又可以分为 **Anchor‑based 和 Anchor‑free** 两大类。早期主流方法(如 Faster R‑CNN、SSD、RetinaNet、YOLOv3/v4/v5 等)采用 Anchor‑based 思路:在特征图的每个位置预先定义若干具有不同尺度和长宽比的锚框(anchor),然后学习每个 anchor 对应的前景概率和 bbox 偏移量。这种方式实现简单、效果好,但需要人工对 anchor 的尺寸和比例进行较多调参,且在小目标、密集目标场景下容易出现 anchor 数量庞大、正负样本极度不平衡的问题。 + +Anchor‑free 方法则尝试摆脱对预定义 anchor 的依赖。以 FCOS、CenterNet、ATSS 等为代表,它们通常直接在特征图的每个像素点上预测“这里是否是某个目标的中心(或属于该目标)”以及对应的边界距离,从而完全避免了预设 anchor 的复杂性。这样的好处是:模型结构更简洁,训练样本分配策略可以更加自然,尤其在面对尺度变化大、目标形状复杂的真实场景时,具有更好的泛化和可扩展性。与此同时,Anchor‑free 检测器也推动了更多基于像素/点的统一框架,使得检测与关键点、分割等任务更易共同建模。 + +更进一步,DETR / Deformable DETR 等 Transformer‑based 检测器从另一个维度重新思考了检测问题:它们不在特征图上密集铺设 anchor,而是引入一组固定数量的“查询向量”(object queries),通过 Transformer 的自注意力和交叉注意力机制,从全局特征中“生成”一组目标预测,并通过匈牙利匹配(Hungarian Matching)实现一一对齐。这种集合预测(set prediction)的思路彻底消除了 NMS 和手工样本分配等传统组件,在概念上非常简洁,但在早期实现中存在收敛慢、对小目标不友好等问题;后续的 Deformable DETR 通过引入可变形注意力和多尺度机制,在收敛速度和性能上都有明显提升,逐渐在检测与多任务场景中获得更多应用。 + +对于工程实践而言,Anchor‑based、Anchor‑free 与 Transformer 检测并不是互斥的选择,而更像是一条演化链:从 heavily engineered 的 anchor 设计,到更为端到端的点/中心预测,再到完全基于集合预测与注意力的统一框架。当前工业落地中,YOLO 系列等成熟 Anchor‑based 模型依然是主力,Anchor‑free 和 DETR 家族则更多出现在对结构简洁性、多任务统一性、可扩展性要求较高的系统中。 + +### 2.3.3 小目标与视频检测:走向真实场景的鲁棒性 + +在公开数据集上的目标检测往往给人一种“问题已经基本解决”的错觉,但一旦进入真实场景,就会立刻遇到两类棘手问题:**小目标/密集目标**与 **视频中的稳健检测与跟踪** 。 + +小目标检测中,目标在原图中往往只占极少的像素区域,例如远处的行人、遥远的车辆、空中无人机,或者高分辨率工业图像上的微小瑕疵。随着 Backbone 下采样和特征图分辨率的降低,这些小目标在高层特征中很容易被“淹没”,导致漏检。为此,检测器通常会采用多尺度特征金字塔(FPN/PAFPN 等)、提高输入分辨率、在浅层特征图上增加检测头,甚至专门设计针对小目标的分支和损失加权策略。同时,在数据层面也需要通过裁剪、放大、小目标重采样等方式,提升模型对小尺度目标的感知与记忆能力。 + +密集目标(如拥挤人群、密集停车场、排列紧凑的商品/零件)则会暴露出锚框重叠、NMS 误杀、遮挡严重等问题。改进策略包括更精细的标签分配(如 ATSS 等自适应分配方法)、软 NMS 或基于学习的去重策略、以及通过中心点/密度图建模等方式缓解框间竞争。在工业质检中,许多系统还会结合检测与像素级分割,实现更精确的缺陷定位,以便后续自动处理。 + +当检测从单帧扩展到视频时,另一个挑战是 **时间连续性与目标稳定性** 。单帧检测器在每一帧上独立做出预测,难以避免短时丢检、ID 抖动和虚警,而现实应用中的告警、计数、轨迹分析往往需要跨帧一致的目标轨迹。为此,视频目标检测通常会叠加一个 Tracking 模块,把“检测 + 目标跟踪”打通:经典做法是以图像检测器为前端,在后端利用卡尔曼滤波、匈牙利匹配、外观特征相似度等实现多目标跟踪(如 SORT、DeepSORT 等);更进一步的做法是将跟踪头直接整合到检测网络中,联合学习检测与跨帧关联,提高短时遮挡、快速运动等场景下的鲁棒性。 + +在实际系统中,小目标、密集目标和视频检测往往不是孤立的问题,而是同时出现:例如城市道路监控中的远处行人/车辆、车站广场中的密集人群、产线视频中的高速运动零件。这也决定了,高质量的目标检测模块,除了在标准 benchmark 上有亮眼指标外,更需要在多尺度、多密度、长时间视频等真实条件下,经受住各种复杂因素的考验,才能真正支撑上层的行为分析、智能告警和多模态理解。 + +## 2.4 图像分割(Image Segmentation) + +有了目标检测,我们已经可以知道“图里有哪些物体、它们大致在哪里”,但很多任务还需要更精细的结构化理解:**精确到每一个像素,判断它属于哪一类、属于哪个实例** 。例如自动驾驶中要知道哪些像素是路、哪些是人和车;抠图工具要把头发丝和背景分得干干净净;医学图像里要精确描出肿瘤和器官的边界。这类任务统称为图像分割,它直接在像素层面输出语义或实例标签,相比检测提供了更细粒度的空间结构信息。 + +从产品角度看,图像分割是“像素级结构化”的核心能力:抠图和背景替换工具依赖它决定哪些像素需要保留;自动驾驶的感知模块依赖它构建精细的“可行驶区域 + 障碍物”地图;医学影像软件依赖它测量病灶大小、形状和体积;遥感平台依赖它区分农田、水体、建筑、道路等地物。下面我们从 **场景** 、**原理**和**模型**三个角度来梳理图像分割,并在后续子项中展开语义/实例/全景/大模型分割等方向。 + +* **场景** + * 内容编辑与抠图:人像抠图、头发丝级别的背景替换、物体抠出和分层编辑,用于图片美化、短视频特效、广告创意制作。 + * 自动驾驶与机器人:对每个像素标注路面、车道线、行人、车辆、护栏、建筑、天空等,用于路径规划、碰撞预警和环境建模。 + * 医学影像分析:在 CT、MRI、超声等图像中精确分割器官、肿瘤、病灶区域,支持辅助诊断、手术规划和疗效评估。 + * 遥感与地理信息:在卫星/航拍图中分割农田、水体、道路、建筑、林地等地物,支持国土规划、土地利用监测和灾害评估。 +* **原理** + 图像分割本质上是“密集预测”,对输入图像通过编码器(Backbone)提取多尺度特征,再通过解码器或上采样模块,将特征图逐步还原到与输入同尺寸的分割图,在每个像素位置上输出一个语义或实例标签。 + * **语义分割(Semantic Segmentation)** :为每个像素分配一个语义类别(如路、人、车、天空),不区分同类的不同个体,适合描述“场景组成”。 + * **实例分割(Instance Segmentation)** :在语义信息之上进一步区分同类不同实例,为“每一辆车、每一个人”生成独立掩膜,是检测与分割的结合。 + * **全景分割(Panoptic Segmentation)** :统一处理“可数的物体(thing,如人、车)”与“不可数的背景(stuff,如路、天空)”,为每个像素同时给出语义标签和实例 ID。 + 与检测相比,分割对空间细节与边界质量更加敏感,需要更丰富的多尺度上下文信息和更精细的上采样/融合策略。 +* **模型** + 经典到最新的分割模型大致沿着“FCN → 编码器–解码器 → 多尺度上下文 → 检测+分割一体化 → 大模型分割”的路线演化: + * 语义分割:FCN、U‑Net 及其变体、DeepLab 系列(DeepLabv3/v3+)、PSPNet 等,通过空洞卷积、金字塔池化、跳跃连接等方式获取多尺度上下文和精细边界。 + * 实例/全景分割:Mask R‑CNN、Panoptic FPN、Mask2Former 等,将检测头与分割头结合,实现目标级分割和全景分割。 + * 大模型与通用分割:Segment Anything Model (SAM) 等基础分割模型,将分割从“每个任务单独训练”提升为“一个模型适配多数分割场景”,支持交互式、提示驱动(prompt‑based)的分割。 + +总体而言,图像分割相比目标检测提供了更精细的空间结构表达,是构建高可靠感知系统和高级编辑工具时不可或缺的一环。下面,我们从 **语义分割与实例分割**, **全景分割与检测一体化**, 以及**通用分割**, **大模型**, **与无监督分割**三个方向展开。 + +### 2.4.1 语义分割与实例分割:从“像素类别”到“像素实例” + +**语义分割(Semantic Segmentation)** 的目标,是为图像中的每一个像素指定一个语义类别,使得网络学会“这片区域是路,那片区域是车,这里是人,那边是天空和建筑”。经典做法通常采用编码器–解码器结构:编码器(如 ResNet、EfficientNet、Swin Transformer 等)提取逐渐下采样的高层特征,解码器通过上采样、跳跃连接(skip connection)和多尺度融合,将粗糙的高层语义特征与底层细节结合,还原到原始分辨率。FCN 首次将这种密集预测形式系统化,U‑Net 通过对称的 U 型结构与大量 skip connection 在医学影像中取得了巨大成功;DeepLab 系列通过空洞卷积(dilated convolution)和 ASPP(金字塔空洞池化)在不降低分辨率的情况下扩大感受野;PSPNet 则通过金字塔池化获取全局上下文信息。这些模型共同推动了在道路场景、遥感、医学等领域的大规模应用。 + +**实例分割(Instance Segmentation)** 进一步在像素语义标签的基础上区分同类不同个体:不只要知道哪些像素是“车”,还要知道这些像素分别属于哪一辆车。最具代表性的模型是 Mask R‑CNN,它在 Faster R‑CNN 的检测框架上增加了一个并行的分割分支:先通过检测头预测每个候选框的类别和位置,再在每个框内生成一个二值掩膜,从而得到“框 + 掩膜”的目标级分割结果。与纯语义分割相比,这种方法能够很好地处理物体重叠和遮挡,是人像/商品抠图、多目标计数、细粒度编辑等任务的基础。后续的实例分割方法在 mask 质量、多尺度与速度上不断改进,也出现了基于 anchor‑free 和 Transformer 的新架构,但“检测 + 局部分割”的思路仍然非常主流。 + +在产品层面,语义分割通常出现在“场景级”的应用中,例如自动驾驶道路分割、遥感地物识别、医学器官分割等;实例分割则更常用于“物体级”抠图、计数和编辑,例如一键选中并分离每一辆车、每一个人、每一件商品。两者结合,可以为上层任务提供既精细又结构化的空间信息。 + +仅做语义分割会把同类对象混在一起(所有“车”像素都属于同一个类);仅做实例分割又往往只关注可数的“东西”(things,如人、车、动物),而忽视大面积的不可数“背景”(stuff,如路、草地、天空)。在很多场景中,我们既需要知道**每一个对象的实例级掩膜** ,又想了解 **整体场景构成** 。这就催生了**全景分割(Panoptic Segmentation)** :为每一个像素同时给出语义类和实例 ID,实现对 thing + stuff 的统一建模。 + +早期的全景分割系统通常通过“语义分割模型 + 实例分割模型 + 后处理合成”的方式实现:先用一个网络预测每个像素的语义类别,再用另一个网络输出各个实例的掩膜与类别,最后通过一套规则(如优先级、重叠处理)将两者合并为一个一致的全景分割结果。Panoptic FPN 代表了一条工程上更优雅的路径:在一个共享 Backbone 与特征金字塔(FPN)上,分别挂载语义分割头和实例分割头,通过联合训练与特征共享,同时得到两种输出,再通过轻量的后处理将它们融合。这样不仅提高了效率,也增强了语义和实例之间的一致性。 + +在模型层面,随着检测/分割一体化与 Transformer 架构的发展,出现了如 Mask2Former 等统一的全景分割框架:它们倾向于使用一套通用的“query + mask decoder”结构,在同一网络中同时预测语义、实例乃至其他下游任务的掩膜,从而在架构上大幅简化系统、方便多任务扩展。对于自动驾驶、机器人导航、AR 场景理解等复杂任务来说,全景分割提供了一种更接近“人眼主观感受”的完整场景描述,让上层决策和规划可以在更准确的空间语义上进行。 + +在产品形态上,全景分割往往内嵌在自动驾驶、机器人系统和高端视觉分析平台中,用户未必直接感知到“全景分割”这个概念,但会真实受益于更稳健的场景理解和更自然的交互体验。 + +### 2.4.2 通用分割与无监督分割:从任务定制到“Segment Anything” + +传统分割模型往往围绕特定数据集和任务训练:比如“道路场景 19 类语义分割”“某种肿瘤分割”“某几类商品分割”等,每换一个任务就要重新标注、重新训练。在实际业务中,这种强依赖精标数据的方式代价巨大,并且难以覆盖长尾类别和不断涌现的新场景。近年来,随着大规模预训练视觉模型和提示驱动(prompt‑based)范式的发展,出现了以 **Segment Anything Model (SAM)** 为代表的**通用分割大模型** ,试图把分割能力从“任务定制”提升为“基础设施”。 + +以 SAM 为例,它通过一个强大的图像编码器(通常是大规模预训练的 ViT)学习全图的通用特征,再通过轻量的提示编码器和掩膜解码器,将用户给出的点、框、文本提示等转化为分割结果。在训练阶段,SAM 利用了海量、多源、多任务的掩膜标注,使得模型学到的是一种“泛化的分割能力”,而不是对某个数据集标签的死记硬背;在使用阶段,用户只需给出极少量提示(一个点或者一个粗框),就能在各种未见过的图像类型和物体类别上得到质量较高的掩膜。这种范式大大降低了构建新分割应用的门槛,也为无监督/弱监督场景提供了强有力的工具。 + +与之相关的,是更广义的**无监督 / 自监督分割**方向:不依赖或极少依赖人工掩膜,通过图像内部的相似性、时序一致性、多视角约束等信号,自动将图像划分为若干有意义的区域。早期工作多侧重于“视觉聚类”和区域提议(proposal generation),如今则更多地被大模型内化为一种表征学习方式,为下游的分割任务提供良好的初始化。结合 CLIP 等文本–图像对比学习模型,越来越多的方法能够在“只给文本类别名称、不提供掩膜标注”的条件下,进行零样本或少样本分割,为冷启动场景和长尾类提供新解法。 + +在实际产品中,通用分割大模型往往以“交互式抠图工具”“智能选区”“一键抠背景”等形式出现,也逐步被整合进医学、遥感、工业等领域的专业软件中,作为半自动标注与辅助分割的加速器。与传统定制模型相比,它们不一定在某个特定任务上达到极致,但在“什么都能做一点、多场景快速落地”上有显著优势,也为后续构建真正的多模态基础视觉模型打下了基础。 + +## 2.5 关键点检测与动作识别(Keypoint Detection & Action Recognition) + +在分类、检测、分割之后,我们已经可以知道“图里有什么、在哪儿、每个像素属于什么”。但在很多真实任务中,业务关心的不仅是“物体存在与位置”,而是**姿态和动作** :一个人是在走路还是在奔跑?这只手是否举起、是否做出某个手势?工人是否正确佩戴安全设备、执行规范动作?运动员的技术动作是否标准?这些问题需要我们进一步理解 **物体内部的结构与时序变化** 。 + +关键点检测与动作识别就是面向这一需求的两层能力: + +* **关键点检测(Keypoint Detection)** :在图像或视频帧上,预测目标(通常是人体、手部、面部或特定机械结构)的若干“骨架点”(如关节、指尖、五官),得到一个精细的结构化姿态表示(pose)。 +* **动作识别(Action Recognition)** :在时序上分析这些关键点或外观特征随时间的变化,判断“这个人/这群人正在做什么动作或行为”。 + +从产品视角看,这一能力广泛服务于:人机交互(手势控制)、体育分析(技术动作评估)、安防(跌倒检测、打架/奔跑等异常行为识别)、工业安全(违规动作检测)、虚拟人驱动(依靠人体/面部关键点驱动 3D 骨骼与动画)等场景。下面我们从 **场景** 、**原理**和**模型**三个角度梳理这一层能力,并在子节中分别展开关键点检测与动作识别。 + +* **场景** + * 人机交互与 AR/VR:通过手势识别、身体姿态检测,实现“比划一下就能控制”的自然交互,或在 AR/VR 中实时驱动虚拟形象。 + * 体育训练与运动分析:对跑步、跳高、投篮、举重等动作进行关键点追踪与角度分析,给出技术动作评估与纠错建议。 + * 安防与公共安全:检测跌倒、打架、剧烈奔跑、翻越护栏等异常行为,用于及时告警;在工地、厂区中识别是否规范操作。 + * 工业与人机协作:检测工人是否按规范姿态操作、与机器人协作时的安全距离、是否出现危险动作。 + * 面部/表情驱动与虚拟人:通过面部关键点捕捉表情细节,用于表情迁移、数字人驱动、视频会议虚拟形象等。 +* **原理** + 两类任务分别侧重空间结构与时序变化,但本质上都是在高维特征空间中做结构化预测: + * 关键点检测:在图像上定位一组预定义关键点(如 17/25 个人体关节、21 个手部关节、68/106 个面部关键点),常用方式是在特征图上预测每个关键点的热力图(heatmap),再通过峰值位置反推坐标;多人的场景下,还需要进行“关节到人的组装”。 + * 单帧/短时动作识别:基于单张图或短时间窗口,通过人体姿态(关键点)和外观特征,判断该帧/该片段中发生的动作类别(如走、跑、举手、挥手、坐下等)。 + * 时序动作识别:在更长的时间尺度上,分析特征序列(图像特征、关键点序列或光流等),建模动作的起始、持续与结束,识别“正在打电话”“正在做俯卧撑”“两人互相推搡”等复杂行为。 + * 结构化表示:关键点序列提供了一种比原始像素更紧凑、更稳定的结构化表示,便于在动作识别中处理视角变化、背景干扰和外观差异。 +* **模型** + 常见模型大致沿着“卷积/Transformer 特征提取 + 关键点/时序头”这一统一范式发展: + * 关键点检测:OpenPose 系列、Hourglass Network、HRNet、基于自顶向下(先检测人再估计姿态)和自底向上(先检测关节再组装)两大分支;近年来也有基于 Transformer 的姿态估计器。 + * 视频动作识别:基于 2D/3D CNN 的视频模型(I3D、SlowFast 等)、基于骨架的 GCN 模型(ST‑GCN 等,直接在关键点图上建模时空关系)、以及基于视频 Transformer(Video Swin、TimeSformer 等)的端到端方案。 + * 统一多任务与大模型:在通用视觉 Backbone 上同时输出检测、分割、关键点和动作标签,或利用多模态大模型通过文本提示直接理解“这个人在做什么动作”,将结构化预测与语义理解连接起来。 + +下面我们分别从**关键点检测与姿态估计**以及**动作识别与行为理解**两个方向展开。 + +### 2.5.1 关键点检测与姿态估计:给人和物“画骨架” + +关键点检测(也常被称为姿态估计,Pose Estimation)关注的是 **单帧或单幅图像中的空间结构** :在二维图像中找到一组具有语义意义的关键点,并将它们连接成骨架。例如,在人体姿态估计中,我们通常需要检测头部、肩膀、肘、腕、髋、膝、踝等关节;在面部姿态中则是眼角、嘴角、鼻尖、脸廓等;在手部姿态中则是指根、指关节、指尖。对于机械臂、关节结构件等非人体对象,也可以同样定义一套关键点体系。 + +在模型设计上,关键点检测常用的是 **“特征提取 + 热力图预测”**范式: + +* 首先使用 CNN 或视觉 Transformer(如 ResNet、HRNet、Swin 等)对输入图像提取多尺度特征。 +* 然后通过一个解码头或多层卷积,为每一个关键点类型输出一张热力图(heatmap),其中每个像素值表示“该位置是该关键点的可能性”。 +* 推理阶段,通常取每张热力图的峰值位置作为关键点坐标,并通过双线性插值、局部拟合等方式进行亚像素级优化。 + +针对多人场景,姿态估计方法大致分为两路: + +* **自顶向下(Top‑down)** :先使用行人检测器在图中找到每个人的边界框,再对每个框内的图像分别做单人姿态估计。这种方式对单人精度高、框架简单,但在多人密集场景中计算代价大、对检测质量敏感。代表系统包括许多基于 Faster R‑CNN/YOLO + Hourglass/HRNet 的组合。 +* **自底向上(Bottom‑up)** :不先区分每个人,而是在全图上直接预测所有潜在关键点(及其类型),同时预测关键点之间的连接关系或亲和场(如 OpenPose 的 PAF)。然后通过图匹配/聚类算法,将关键点组装成多个独立的人体骨架。这类方法在多人密集场景中更高效、对人数规模更鲁棒,但组装过程复杂,对连接质量敏感。 + +近年来,基于 Transformer 的姿态估计模型也逐渐出现,将关键点检测看作一组“查询–响应”任务,与 DETR 类似,可以在架构上统一对象检测与姿态估计。在工程应用中,关键点检测能力通常被封装为“人体/手势/面部关键点 SDK 或 API”,上游应用只需传入图像或视频帧,即可获取结构化的骨架坐标,用于后续的动作识别、交互控制或动画驱动。 + +### 2.5.2 动作识别与行为理解:让“骨架”动起来 + +在得到关键点或高层视觉特征之后,下一步就是理解 **时间维度上的变化** ——也就是动作识别(Action Recognition)和行为分析(Behavior Understanding)。与关键点检测不同,动作识别不再局限于单帧;它关心的是一段时间内特征的演化模式:从“抬手”到“挥手”,从“走路”到“奔跑”,从“站立”到“跌倒”。 + +在输入表示上,大致有三条路线: + +* **基于原始** **视频帧** **/光流** :直接对视频帧序列建模,或额外引入光流(描述局部运动速度的场)作为输入,让模型从外观 + 运动信息中联合学习。 +* **基于骨架/关键点序列** :先用姿态估计得到人体关键点坐标序列,再在“时空骨架图”上建模,弱化背景与光照干扰,更关注人体结构与运动模式。 +* **多模态融合** :将视频特征、关键点序列、甚至音频、文本等多模态一起纳入,处理复杂行为场景(如多人互动、事件级动作)。 + +对应地,模型结构也呈现出多样化发展: + +* 早期的动作识别主要依赖 **2D CNN + 时间 n 池化** 或 **3D CNN** (如 I3D、C3D):前者对每一帧提特征再在时间维上做池化或 RNN;后者直接在空间和时间上做三维卷积,捕捉短时运动模式。 +* 针对骨架序列,典型方法是 **时空图卷积网络(ST ‑ GCN)** :把人体关键点看作图结构节点,关节之间的连接是边,在时间维上也连边,通过图卷积在时空图上传播信息,从而学习动作模式。这类方法轻量、对背景鲁棒,适合在资源有限的设备上部署。 +* 近年来, **视频 Transformer** (如 TimeSformer、Video Swin)在动作识别中表现突出,它们将视频切分为时空 patch,通过自注意力机制建模长时间依赖,能够更好地捕捉复杂动作与多目标交互。 + +在业务侧,动作识别往往会与检测、跟踪、关键点检测结合,形成端到端的行为分析系统: + +* 在安防中,先检测并跟踪人员,再对每条轨迹的关键点序列进行动作分类,实现跌倒检测、打架/奔跑识别等; +* 在体育和健身应用中,通过关键点序列分析动作是否标准、幅度是否合适,并给出纠正建议; +* 在人机交互场景中,对实时姿态流进行轻量级动作分类,实现挥手、比心、手势指令等交互; +* 在工业安全中,对工人操作动作进行持续监测,识别危险姿态(如俯身进入危险区、越过安全线等)。 + +面向未来,多模态大模型正在将“动作识别”提升为更高层的“事件与意图理解”:模型不仅可以标注“走路、跑步、打电话”,还能够回答“这个人似乎在示意招呼某人”“这两人正在发生争执”等更接近日常语言的描述。关键点检测和动作识别在其中,作为重要的结构化运动线索,与外观特征和语言提示一起,共同支撑更复杂的时空理解能力。 + +## 2.6 开放词汇 / 开放世界 / 开放域检测 + +(Open‑Vocabulary / Open‑World / Open‑Domain Detection) + +前面的检测与分割能力,基本都默认一个前提: **训练和推理时的类别集合是固定的** 。也就是说,模型在训练阶段就完整地见过“所有要识别的类别”,推理时只需要在这套封闭标签里做选择。但真实世界远比数据集复杂:新商品、新品牌、新路牌、新物种、新场景随时出现,不可能为每个新类都准备充足的标注数据重新训练检测器。这就催生了 **开放词汇 / 开放世界 / 开放域检测** :在训练数据只覆盖有限“已知类”的情况下,让模型在推理时仍然能够感知、定位和识别 **未见的新类** ,并且在视觉风格和拍摄域(domain)变化时保持鲁棒性。 + +你可以把这一层理解为:在传统检测之上,加入“对语言空间与开放世界的对齐和泛化能力”。模型不再只会说“这是 80 类 COCO 之一”,而是可以在任意文本描述的空间里理解和检索目标,例如“检测图里所有‘红色运动鞋’”“标出所有‘疑似小型飞行器’”,即便这些精细类别在训练集中从未显式出现。下面我们从 **场景** 、**原理**和**模型**三个角度来梳理这一层,并在子小节中分别展开开放词汇检测、开放世界检测和开放域泛化。 + +* **场景** + * 通用场景理解 API:用户给出任意自然语言描述(类别词或短句),系统在任意风格的图像中返回对应目标的检测框或分割掩膜,例如“图中所有安全帽”“所有疑似品牌 logo”“所有带轮子的物体”。 + * 大规模商品 / 物种识别:电商中不断上新的长尾商品、自然界中数量巨大的动植物物种,训练数据只能覆盖一部分已知类,但系统需要对海量新类进行定位与粗识别,并支持通过文本或图像检索。 + * 跨域安防 / 自动驾驶感知:训练数据多来自白天城市道路/少数摄像头视角,实际部署却面临不同城市、乡村、高速、极端天气、红外/鱼眼摄像头等“新域”,其中还会出现训练集中从未标注过的新型目标(新款车型、新交通设施、新类型障碍物)。 +* **原理** + 这类方法的核心,是用**视觉–语言对齐的嵌入空间**替代传统的“固定 one‑hot 类别头”,并通过多种机制处理“未见类”和“新域”: + * 开放词汇检测(Open‑Vocabulary Detection):在训练阶段,利用大规模图文对(image–text pairs)预训练得到类似 CLIP 的对齐空间,使得图像区域和文本嵌入可以直接在同一语义空间中做相似度匹配;检测头不再输出固定的类别 logit,而是输出一个区域特征向量,与任意文本描述向量进行对比,从而支持“训练只见部分类别,推理可指定任意文本类别”。 + * 开放世界检测(Open‑World Detection):进一步处理“训练集中完全没有标注的新类”,要求模型可以将这类目标检测为“未知类(unknown)”,并在后续通过交互标注或持续学习,把这些未知类逐步纳入已知类别集合,形成一个可以不断扩充类目的在线学习系统。 + * 开放域 / 跨域检测(Open‑Domain Detection):面对图像风格、成像设备、环境条件等大幅变化(domain shift),通过领域自适应(Domain Adaptation)、领域泛化(Domain Generalization)等技术,让检测器在未见过的新域中保持稳定检测性能;常见手段包括对抗性域对齐、多域训练、风格随机化、元学习等。 + * 分割与检测一体的开放词汇:将上述思路扩展到像素级,对任意文本描述生成分割掩膜(open‑vocabulary segmentation),通过 Region–Word 或 Mask–Word 对齐损失,实现“用自然语言描述一个区域/物体,就能得到对应 mask 或框”。 +* **模型** + 当前开放词汇 / 开放世界 / 开放域检测的主流技术路线,基本围绕“大规模视觉–语言预训练 + 检测头适配 + 域泛化机制”展开: + * CLIP‑based 检测器:以 CLIP 风格的图像编码器和文本编码器为基础,在区域级特征(ROI、特征图 patch、mask 区域)与文本嵌入之间应用对比学习和 Region–Word 对齐损失;典型实现包括在 Faster R‑CNN / RetinaNet / YOLO / DETR 等架构上替换或扩展分类头,使其以“cosine 相似度 + 文本嵌入”方式输出类别分数。 + * Caption‑driven / Prompt‑based Detection:利用大规模图文描述(caption)数据,为图像中的区域或 mask 自动生成文字描述,再用这些自动生成的文字与检测/分割区域对齐训练,从而减少对人工类别标签的依赖;推理时则通过自然语言 prompt(如“所有穿红色衣服的人”“所有电动车”)驱动检测/分割。 + * Open‑World Detection 系列工作:在传统检测框架中显式引入“未知类(unknown)”建模、渐进式类别扩展和增量学习机制,一部分方法通过度量空间的距离与不确定性估计来判断“是否为未知类”,另一部分引入记忆库与在线重训练,使系统能随时间积累新类别知识。 + * 域自适应 / 域泛化检测:在 Backbone 和检测头层面增加域判别器、对抗性损失、多域 batch normalization、风格随机化增强等模块,使检测器在不同域之间学习到更域不变的表示;也有工作在 Transformer 检测框架(如 Deformable DETR)上引入多源域训练和元学习策略,提升跨域泛化能力。 + * 通用 / Foundation 检测模型:把检测问题上升到“基础模型”层面,预训练一个在类别和域上都尽可能通用的 Detection Foundation Model,再通过轻量微调或文本 prompt 适配特定场景;这类模型通常结合大规模检测标注、多源图文对、甚至视频数据,目标是让“任意文本 + 任意风格图像”的通用理解成为可能。 + +在具体产品形态上,开放词汇/开放世界/开放域检测往往体现为“更自然、更少限制”的视觉接口:用户不必提前约定一小撮固定标签,而是可以用自然语言描述想找的目标;系统也不需要为每个业务场景从零开始重训检测器,而是基于统一的通用模型,通过 prompt 或少量样本快速适配。对于大规模商品 / 物种识别、全球化部署的安防与自动驾驶感知系统而言,这一层能力正在成为从“封闭数据集性能”走向“真实开放世界可用性”的关键跳板。 + +### 2.6.1 开放词汇检测:从固定类别头到文本驱动类别空间 + +**开放词汇检测(Open‑Vocabulary Detection)的出发点,是突破传统检测中“固定类别头”的限制。以往的检测器在顶层接一个大小固定的分类层(对应训练集中的 N 个类别),训练完成后只能在这 N 个类别中选择;而开放词汇检测则通过引入文本**, **编码器**, **和共享的语义嵌入空间,让检测头输出的区域特征可以与任意文本描述**进行相似度对比,从而在推理时接纳未见过的新类别。 + +典型做法是使用类似 CLIP 的视觉–语言预训练模型: + +* 文本端:对类别名称或自然语言描述(如“person”、“red sports car”、“yellow construction helmet”)进行编码,得到文本向量。 +* 视觉端:在检测框架(Faster R‑CNN、RetinaNet、YOLO、DETR 等)中,对每个候选区域或特征点提取区域特征向量。 +* 对齐训练:通过对比损失、Region–Word 对齐损失,使同一语义的文本和区域特征在嵌入空间中靠近,不同语义的向量远离。训练时即便只对一部分类别提供显式框标注,也可以利用图文对或图像 caption 扩展语义覆盖。 + +推理阶段,系统不再依赖训练时固定的一组类名,而是允许用户在线提供任意类别词或自然语言描述,通过文本编码器转为嵌入,再与区域特征做相似度匹配。这使得检测器可以在不重新训练的前提下,支持诸如“检测所有滑板”“检测所有绿植”“检测所有安全相关设备”等灵活需求,即便某些具体类目在训练集中从未出现过完整标注,只要语义上与预训练的图文空间有重叠,就能被一定程度地识别和定位。 + +在工程实践中,开放词汇检测需要在效果与效率之间平衡:一方面,保持与大规模预训练的视觉–语言 Backbone 的语义对齐;另一方面,又要承载检测任务对多尺度、实时性的要求。主流 CLIP‑based 检测器往往采用“预计算文本嵌入 + 高效向量相似度计算”的方式,避免在在线服务中反复编码文本,同时对区域特征进行量化或蒸馏,兼顾精度和推理速度。 + +### 2.6.2 开放世界检测:从“未见类”到“可学习的未知” + + **开放世界检测(Open‑World Detection)在开放词汇的基础上,进一步要求模型显式处理“未知类”** :训练数据中只标注了部分类别,其余物体要么未被标注,要么被统称为背景;推理时,这些“未被标注的真实物体”既不应该被简单视为背景,也不应被错误归入已知类别,而应作为“未知类(unknown)”被检测出来,并具备后续转化为“新已知类”的可能。 + +在建模上,开放世界检测通常需要解决三个问题: + +1. **未知类感知** :如何在训练阶段避免将所有未标注目标都学成“背景”?常见做法包括:引入显式“未知类”槽位,通过负例挖掘和不确定性建模让模型学会在低置信度区域输出“unknown”;或者利用无标注数据和自监督机制,对高置信度的潜在目标区域进行聚类和伪标签生成。 +2. **错误归类控制** :模型需要在“宁可判为 unknown,也不要错误归入错误已知类”之间做权衡,这涉及到损失设计(如 margin、开放集判别)、决策阈值和后处理策略。 +3. **渐进式类别扩展** :当业务方对一批“unknown”目标人工标注出新类别后,模型应能够通过增量学习将这些新类别纳入“已知类”集合,而不显著遗忘旧类。为此,很多工作引入了记忆库、蒸馏损失、参数隔离或重放机制,实现对新类别的稳定吸收。 + +从产品视角看,开放世界检测特别适合那些**类目不断增长、长尾极度严重**的场景,例如自然物种识别、新品快速上新的商品识别、复杂安防场景中的异常目标检测等。系统可以先用开放世界检测将“任何非背景的可疑目标”标出,并逐步通过人工或半自动标注,将其中有价值的聚类升级为正式类目,从而形成一个“类目可持续生长”的检测系统,而不是被固定数据集束缚。 + +### 2.6.3 开放域 / 开放分布检测:跨风格、跨设备、跨场景的鲁棒性 + +即使类别集合保持不变,检测器仍然会在现实部署中遭遇严重的 **域偏移(Domain Shift)** :训练数据可能来自少数城市的白天高清摄像头,而部署环境却包含不同国家、乡村、高速路、隧道、夜间、雨雪、低分辨率摄像头、鱼眼镜头甚至红外成像;电商商品摄影与用户实拍、广告图/插画/动漫风格之间也存在巨大差异。**开放域检测(Open‑Domain Detection)**关注的正是:在图像分布发生显著变化的条件下,保持检测性能的稳定与可靠。 + +典型的技术路径包括: + +* **领域自适应(Domain Adaptation)** :在拥有目标域无标注数据或少量标注数据的前提下,通过对抗性域对齐(在特征空间上混淆源域/目标域)、多级域对齐(图像风格、特征、检测头输出)、风格迁移(如将源域图像风格迁移到目标域)等方式,让模型学到对域不敏感的特征。 +* **领域泛化(Domain Generalization)** :在仅有多个源域数据、没有目标域数据的前提下,利用多域训练、风格随机化、特征扰动、元学习等手段,使模型在训练阶段就尽可能暴露于多样化分布,提升对未知新域的泛化能力。 +* **通用 / Foundation 检测模型** :通过在极大规模、多源、多风格数据上预训练检测 Backbone 和头部结构(包括自然图像、视频帧、合成数据、跨模态数据等),再在特定业务场景轻量微调,从而获得比“单域训练”更强的开放域鲁棒性。 + +这些开放域机制往往与开放词汇/开放世界能力相互叠加:一个面向真实世界的通用检测系统,既要能听懂用户的自然语言类别描述(开放词汇),又要能对新出现的目标给出合理的“未知”判断和渐进吸收(开放世界),还要能在不同国家、不同设备、不同天气和风格下保持性能(开放域)。在工程落地中,这三者并不是彼此孤立的研究方向,而是共同构成了从“封闭 benchmark”迈向“开放世界可用”的关键能力组合。 + +## 2.7 视觉–语言任务(Vision–Language Tasks) + +前面的章节主要围绕“单模态视觉”展开:输入是一张图像,输出是检测框、分割掩膜、类别标签或质量分数。而在很多真实应用中,视觉信息并不是孤立存在的——一张图往往伴随标题、说明文字、对话或搜索查询;用户想问的是“图里在讲什么”“这张图和这句话匹不匹配”。**视觉–语言任务**正是解决这类问题:它们以图像 + 文本为输入或输出,通过 **跨模态对齐与联合建模** ,让系统能够“看图说话”“看图回答问题”“用文字找图 / 用图找文”。 + +从产品视角看,视觉–语言模型(VLM)是多模态系统的中枢能力:搜索引擎依赖它实现“以文搜图 / 以图搜文”;内容平台用它做智能配图、广告审核、图文一致性检查;多模态助手则将其作为基础能力,实现“看图聊天”“对文档/截图提问”等功能。下面我们从 **场景** 、**原理**和**模型**三个角度梳理这一层,并在后续小节中分别展开图像描述、视觉问答与图文检索。 + +* **场景** + * 图像描述(Image Captioning):为图片自动生成一两句自然语言描述,用于无障碍辅助阅读、智能相册说明、搜索索引丰富。 + * 图像问答(VQA):用户针对图片提出自然语言问题(“这个人拿着什么?”“车牌号是多少?”),系统给出精准回答,可用于教育、辅助决策和多模态助手。 + * 图文检索(Cross‑modal Retrieval):以文本检索相关图片(Text‑to‑Image)、以图片检索相关文本(Image‑to‑Text),支撑“以文搜图 / 以图搜文”搜索、创意选图和广告投放审核。 + * 图文一致性与审核:判断图片与标题/广告语是否相符,有没有“图文不符”“诱导性描述”等风险,用于内容审核和品牌安全。 +* **原理** + 核心问题是:如何把图像和文本映射到 **同一个语义空间** ,并在这个空间内进行对齐与推理: + * 跨模态对齐:通过联合训练的图像编码器和文本编码器,让对应的“图–文对”在表示空间中彼此靠近,不相关对彼此远离(典型如 CLIP);这为检索、匹配提供了基础。 + * 联合理解与生成:在对齐的表示基础上,引入跨模态注意力,让语言模型在“看着图像特征”的前提下生成文本(图像描述)、推理和回答问题(VQA)。 + * 提示化与指令化:用自然语言指令统一描述多种视觉–语言任务(“为这张图写标题”“回答关于这张图的问题”“判断这段文字是否描述了图片”),让一个模型通过不同提示完成多种任务。 +* **模型** + 主流视觉–语言模型大致演化为两类:**对比学习型 VLM** 与 **生成式多模态** **大模型** : + * 对比学习型:CLIP、ALIGN 等,将图像和文本分别编码成向量,通过大规模图–文配对训练,使其在检索和匹配任务上表现出色,是“以文搜图 / 以图搜文”的基础。 + * 视觉–语言生成模型:BLIP / BLIP‑2、Flamingo、Kosmos、LLaVA 等,将视觉编码器与大语言模型(LLM)衔接,通过跨模态注意力和指令微调,支持图像描述、VQA、多轮对话等复杂任务。 + * 通用多模态大模型:如 GPT‑4.1 with Vision、Gemini 1.5 等,进一步将视觉与更多模态(语音、代码等)统一在一个大模型中,通过统一的接口完成检索、问答、推理和生成。 + +总体而言,视觉–语言任务标志着“视觉不再是一个单独的感知通道”,而是与语言共同参与到更高层的知识表达和推理之中。下面,我们从 **图像描述与视觉问答** 、**图文检索与跨模态对齐**两个方向展开(这里按内容合并为两小节)。 + +### 2.7.1 图像描述与视觉问答:从“看图说话”到“看图推理” + +**图像描述(Image Captioning)**的目标,是输入一张图像,输出一段自然语言描述,比如“一个小女孩在草地上放风筝”。传统做法通常采用“CNN + RNN”结构:用卷积网络提取整图特征,再用 LSTM/GRU 逐词生成描述;随着 Transformer 和预训练 VLM 的出现,主流范式逐渐转向“图像编码器 + 文本解码器”结构,如 BLIP / BLIP‑2、ViT + GPT 等。训练上,模型通常在大量图–文对上进行自回归训练,有时还会采用强化学习或对比损失,优化描述的多样性与正确性。在产品层面,图像描述被广泛用于无障碍阅读(为盲人读屏软件生成图片说明)、智能相册自动加标题,以及为搜索系统提供更多文本索引。 + + **视觉问答(VQA)则进一步把人类交互引入进来:模型的输入不再是“图 + 空白提示”,而是“图 + 问题”,输出一个简短答案或者自然语言解释。与图像描述相比,VQA 更强调可控性与推理能力** :问题可以关注局部细节(“男人的帽子是什么颜色?”)、关系(“哪辆车离路口更近?”)、计数(“有几只狗?”),甚至需要外部知识(“这道菜属于哪种菜系?”)。早期 VQA 模型通常使用图像编码器 + 问题编码器 + 融合模块(如双线性池化、注意力)+ 分类头,输出一个有限词表中的答案;现代多模态大模型则直接用图像编码器 + LLM,在“看图”的基础上进行自然语言生成,在开放式回答和多轮对话上有明显优势。 + +两者在统一的 VLM 框架下可以被视为不同的“提示模板”: + +* Captioning:`<图像> + "Describe this image in one sentence."` → 文本; +* VQA:`<图像> + "Q: ... A:"` → 文本。 + +通过指令微调(Instruction Tuning),同一个多模态大模型可以兼容描述、问答、解释、打标签等多种任务,这也是现代 VLM 产品(多模态助手、图像问答机器人等)的基础工程思路。 + +### 2.7.2 图文检索与跨模态对齐:以文搜图 & 以图搜文 + +**图文检索(Cross‑modal Retrieval)**解决的是另一个高频需求:给定一段文本,找到匹配的图片(Text‑to‑Image Retrieval);或给定一张图,找到相关的文字描述、商品信息、新闻报道等(Image‑to‑Text Retrieval)。这些能力构成了“以文搜图 / 以图搜文”“看图找商品”“给新闻配图”等产品的核心。 + +核心技术是 **跨模态对齐** :以 CLIP 为代表的模型,对图像和文本分别使用各自的编码器(如 ViT 和 Transformer 文本编码器),在大规模图–文配对数据上使用对比学习训练: + +* 对于同一对(图像,文本),让它们的向量在嵌入空间中彼此靠近; +* 对于不匹配的图–文对,则推远它们的向量。 + +训练完成后,只需将所有图片和文本编码成向量,就可以通过向量检索(最近邻搜索)在共享空间中进行快速匹配: + +* Text‑to‑Image:文本 → 文本向量 → 最近的图像向量; +* Image‑to‑Text:图像 → 图像向量 → 最近的文本向量。 + +在工程实践中,这类模型通常采用两阶段结构: + +* 第一阶段用轻量快速的双编码器(Bi‑Encoder,如 CLIP)做粗检索,在亿级图像库中快速筛选出一小部分候选; +* 第二阶段可选用更强的交叉编码器(Cross‑Encoder)或多模态大模型对候选进行精排与重排序,以提升相关性和鲁棒性。 + +在产品侧,图文检索与跨模态对齐被广泛用于:图片搜索、广告检索(根据广告文案找到合适图片)、合规审核(检查广告图文是否一致)、内容推荐(基于用户阅读文本历史向其推荐相关图片/视频)等。随着多模态大模型的兴起,这类检索能力也逐渐被统一进更大的多模态框架中,以“自然语言指令 + 多模态记忆/向量库”的形式,对外提供统一接口。 + +## 2.8 光学字符识别(OCR) + +在很多业务中,最重要的信息既不体现在“画面里的物体和场景”,也不在自然语言对图像的描述里,而是直接写在图像上的 **文字** :合同条款、发票金额、路牌名称、仪表读数、屏幕截图上的错误信息等。**光学字符识别(OCR)**就是围绕“图像 + 文档版式”的结构化理解任务:从复杂的视觉输入中,自动检测并识别文字内容,理解文档的布局和结构,进而支持搜索、统计、自动录入和智能问答。 + +从产品视角看,OCR 是“把纸质/图像信息变成可计算文本”的关键桥梁,是电子化、自动化与智能化办公的基础设施:合同审阅、票据入账、政企档案数字化、办公软件中的 PDF 转 Word、文档问答助手等,都建立在 OCR 能力之上。下面从 **场景** 、**原理**和**模型**三个角度梳理 OCR 体系,并在后续小节中展开核心方向。 + +* **场景** + * 场景文本识别:街景中店铺招牌、路牌、广告牌、包装盒文案等,用于导航、搜索、零售洞察和合规审核。 + * 文档 OCR:扫描件、传真件、PDF、照片版合同/发票/报告等的文字识别与结构化,还原成可编辑文本。 + * 专用场景:车牌识别、仪表盘读数(电表、水表、气表)、屏幕截图文字提取、试卷/表单识别等。 + * 文档理解:在布局复杂的长文档中,抽取标题、段落、表格、注释等结构,为搜索、摘要、问答奠定基础。 +* **原理** + OCR 体系通常分成几个关键步骤: + * 文本检测:在图像上检测出所有文字区域(文本行或文本块),输出定位框(水平或四点多边形),这是后续识别的输入。 + * 文本识别:对每个检测到的文字区域进行序列识别,将像素序列转化为字符序列(如中文、英文、数字、符号等)。 + * 版式分析(Layout Analysis):在文档场景中,识别各区域的角色(标题、正文、图片、表格、页眉页脚等),恢复阅读顺序和层次结构。 + * 表格结构识别:对表格区域进行行列划分、单元格边界解析、合并单元格恢复,重建逻辑表格结构。 + * 文档问答(DocVQA):在 OCR 和版式理解的基础上,让模型能够回答“这份合同的付款日期是什么?”“发票的金额是多少?”这类跨区域、多步骤推理的问题。 +* **模型** + 工程上常见的是“专用 OCR 模块 + 文档理解模型 + 多模态大模型”组合: + * 文本检测与识别: + * 检测:EAST、DBNet/DBNet++ 等基于分割或边缘学习的方法,擅长处理弯曲文本和复杂背景; + * 识别:CRNN、RARE、SAR 等序列模型(CNN + RNN/Attention + CTC 或自回归解码),支持多语种和多字体。 + * 文档版式与结构理解: + * LayoutLM / LayoutLMv2/v3、DocFormer 等,将文本内容(token)、位置信息(bounding box)和视觉特征联合编码; + * Donut 等“端到端文档理解”模型,直接从图像到结构化输出(如 JSON / Markdown),弱化传统 OCR 的边界。 + * 文档问答与多模态理解: + * 在布局模型基础上,叠加任务头进行 DocVQA; + * 或直接使用多模态大模型(VLM)读取文档图像,在自然语言层面完成问答和摘要,同时隐式利用 OCR 能力。 + +综合来看,OCR 已经从早期“简单的字符识别”发展为涵盖**文字 + 版式 + 结构 + 问答**的整体文档理解体系,是企业数字化、政务档案管理和智能办公的关键支柱。下面,我们从 **文本检测与识别** 、 **文档版式与表格结构分析** 、**文档问答与多模态 DocVQA**三个方向展开。 + +### 2.8.1 文本检测与识别:从像素到可用文本 + +OCR 的第一步是 **文本检测** :在输入图像中找到所有包含文字的区域。街景/场景文本面临字体多样、倾斜扭曲、光照复杂、背景干扰严重等挑战;文档场景则强调对密集文本和多栏排版的鲁棒支持。EAST、DBNet 等方法通过将检测问题转化为“像素级分割 + 边缘学习”,在特征图上预测文本概率和几何参数,再通过后处理获得精确的文本框(可为水平框或任意四边形/多边形),兼顾精度和速度。 + +**文本识别**则把每个检测出的文本区域切下来,转化为字符序列。经典做法以 CRNN 为代表:先用 CNN 提取特征,再通过 RNN 或 Transformer 进行序列建模,最后使用 CTC 或注意力解码输出字符序列。对于不定长文本、弯曲文字和复杂语言(中英文混排、多语种),识别模型需要在视觉特征建模和字符语言建模上同时发力。诸如 RARE、SAR 等方法会引入空间变换网络(STN)或注意力对齐机制,以纠正几何畸变、提升对复杂布局的适应能力。 + +在工程系统中,检测与识别通常作为两个解耦的服务组成一条 OCR pipeline:前端检测将图像拆成若干文本行/块,后端识别对每个块做字符识别,并可叠加语言模型做错误纠正(如拼写修复、数字/金额校验)。对于车牌、仪表读数等特定场景,还会使用专门微调的检测/识别模型,以利用场景先验(固定字体、有限字符集)换取更高精度和更低延迟。 + +### 2.8.2 文档版式与表格结构分析:还原“文档的形状” + +单纯把文字识别出来还不够,尤其在长文档、报告、合同和票据等场景中,**版式结构**往往决定了信息的含义和重要性:标题与正文的层级关系、图表与配文的位置、页眉页脚的作用、表格内外文段的逻辑顺序等。**文档版式分析(Document Layout Analysis)**的目标,就是在二维页面上识别出不同区域的角色和边界,并恢复出合理的阅读顺序与层级结构。 + +LayoutLM / LayoutLMv2/v3、DocFormer 等模型,将每个文本 token 的内容(文本 embedding)、空间位置(bounding box 坐标)以及局部视觉特征(来自 CNN/ViT)联合编码,通过 Transformer 建模 token 间的语义–空间关系。通过在带版式标注的数据集上训练,模型可以学会区分“标题/段落/列表/表格/图片说明/页眉页脚”等多种区域类型,并在输出中给出对应标签和层级。这类模型通常作为“中间层”,为合同审阅系统、报告解析、档案数字化平台提供结构化的文档骨架。 + +**表格结构识别(Table Structure Recognition)** 是版式分析中特别关键的一支:它不仅要检测出表格区域,还要进一步解析行列边界、单元格坐标和合并单元格,最终重建一份逻辑表格(通常表示为 HTML、Markdown 表、或带坐标的结构化 JSON)。实现方法包括: + +* 基于规则/视觉:使用线检测、分割网络、对象检测等手段提取表格线和单元格区域,再进行拓扑建图; +* 基于 Transformer:将表格区域的文本块与几何信息编码成序列,直接预测单元格结构和关联关系。 + +在产品上,这些能力支撑了“PDF 转 Word/Excel”“票据/发票结构化录入”“报表解析与指标抽取”等高价值场景,是政企办公自动化的关键组件。 + +### 2.8.3 文档问答与 DocVQA:从“读文档”到“问文档” + +当 OCR 与版式分析能力足够强时,下一步自然需求就是: **不再让人自己翻阅文档,而是直接“问文档”** 。这就是 **文档问答(DocVQA)** :模型在合同、报告、票据、说明书等复杂文档上回答问题,比如“这份合同的生效日期是什么时候?”“这页报表中 2023 年 Q4 的净利润是多少?”“发票上的购方名称是谁?”。 + +传统 DocVQA 系统通常以“OCR + 版式模型 + QA 头”的方式构建: + +* 先使用 OCR 提取文本及坐标; +* 用 LayoutLM / DocFormer 等建模文本–版式–视觉三模态关系; +* 最后在这个表示上叠加任务头(分类 / 抽取 / span 预测),根据问题在文档中定位答案或相关片段。 + +随着多模态大模型的发展,越来越多系统开始直接使用“文档图像 + 问题”作为输入,让一个 VLM 或多模态 LLM 直接生成答案或带引用的解释。在这种架构下,OCR、版式、语义理解和推理能力在模型内部以端到端的方式协同工作:模型既能看到原始版式和视觉线索,又能利用语言世界知识和推理模式完成复杂问题的解答。 + +在产品形态上,DocVQA 通常以“合同审阅助手”“发票/报表问答”“长文档智能问答”形式出现,帮助用户从大量文档中快速定位关键信息、自动生成摘要、进行条款比对等,大幅减轻人工审阅和信息检索的负担。 + +## 2.9 图像生成与编辑(Image Generation & Editing) + +前面介绍的视觉能力大多是“判别式”的:输入图像,输出标签、框、掩膜或文本;而近年来快速发展的另一条主线是 **生成式视觉** :模型不再只是理解图像,而是 **创造或修改图像** ,在给定文本/图像条件下生成高质量、多风格的视觉内容。**图像生成与编辑**正是这一方向的核心能力,支撑了从 AIGC 绘图平台到智能修图/特效工具的大量产品。 + +从业务视角看,生成式视觉已经从“技术演示”变成切实可用的生产力工具:设计师用它做灵感草图和细化稿;营销团队用它批量生成海报和广告素材;普通用户用它制作头像、插画、壁纸;视频创作者用它做抠图、背景替换和特效。下面我们从 **场景** 、**原理**和**模型**三个角度梳理这一层,并在后续小节中展开文本生成图像、图像到图像与编辑能力。 + +* **场景** + * 文本生成图像:用户输入一段描述(“赛博朋克风的夜景城市”),系统自动生成符合描述的多张图片,支持选图与迭代修改。 + * 风格迁移与图像翻译:将真实照片转换为动漫/素描/油画/水彩风格,或在不同领域间做映射(白天 ↔ 夜晚、夏天 ↔ 冬天)。 + * 条件重绘与扩展:在原图的局部进行重绘(Inpainting)、对画面外扩(Outpainting),用于修补瑕疵、移除/添加对象、扩展构图。 + * 文本驱动编辑:用自然语言指令修改图像(“把天空改成日落”“让这辆车变成红色跑车”),用户无需掌握复杂的图像编辑软件。 +* **原理** + 生成式视觉模型主要通过学习“图像分布”和“条件控制”来完成生成与编辑: + * 分布建模:GAN、扩散模型(Diffusion)、Flow Matching 等从大量图像中学习高维分布,使得模型能从随机噪声中逐步“采样”出逼真的图像。 + * 条件生成:在纯图像分布建模基础上,引入文本/草图/分割图/关键点/深度图等条件,使生成过程受到外部信号约束(Text‑to‑Image、Image‑to‑Image、ControlNet 等)。 + * 可控编辑:在已有图像的潜在空间中,通过文本或局部 mask 对局部特征进行引导和修改,实现局部重绘、风格变化、构图调整等。 +* **模型** + 当前主流图像生成与编辑模型以**扩散模型 + 条件控制**为主: + * GAN 系列:StyleGAN 等在高分辨率人脸和样式控制方面表现突出;但训练不稳定、难以覆盖复杂多模态分布。 + * 扩散模型:Stable Diffusion、Imagen、DALL·E 系列等,通过“正向加噪 + 反向去噪”的过程进行采样,兼具质量和多样性,是当前 Text‑to‑Image 的主力方向。 + * 可控生成与编辑:ControlNet、T2I‑Adapter 等,在基础扩散模型上叠加条件通道(边缘、姿态、分割等),实现精确控制;结合文本引导的 Inpainting/Outpainting 实现局部编辑和画面扩展。 + * Flow Matching 与新一代生成模型:通过学习连续流场将噪声分布变换到图像分布,在效率、可控性与稳定性上探索新的平衡。 + +在产品层面,这些技术以即梦、阿里 qwen 图像模型、FLUX、OpenAI 或者 Gemini nanobanana、Stable Diffusion 生态、Photoshop Generative Fill、Canva AI、剪映/CapCut 智能抠图与特效等形态面向用户,逐步从“玩具”演进为内容生产链条中的正式环节。下面,我们从 **文本生成图像** 、**图像到图像翻译**和**文本驱动编辑**三个方向展开。 + +### 2.9.1 文本生成图像(Text‑to‑Image):从一句话到一张画 + +**文本生成图像(Text‑to‑Image)** 的核心任务是:给定一段自然语言描述,生成一张尽可能匹配其语义和风格的图像。现代 Text‑to‑Image 模型主要基于扩散架构: + +* 首先使用文本编码器(如 CLIP Text Encoder 或 T5/LLM)将输入文本编码为条件向量; +* 然后在图像潜空间中,从高噪声状态开始,通过多步反向去噪采样,在每一步都利用文本条件引导生成方向; +* 最终得到符合描述的高分辨率图像,可进一步放大或后处理。 + +Stable Diffusion、Imagen、DALL·E 系列等方法在大规模图–文对上进行训练,使模型既掌握视觉谱系(形状、纹理、构图、光影),又获得一定程度的语言–视觉对齐能力(理解“风格”“材质”“构图”等复杂描述)。在产品层面,这种能力让“不会画画的人也能画图”:用户只需用自然语言描述想法,系统就能给出多种视觉实现,支持迭代试探和细化。 + +Text‑to‑Image 模型通常同时支持多风格、多分辨率输出:通过在训练或推理时加入风格 token、尺寸条件等,使同一个模型在“写实照片风、扁平插画风、3D 渲染风”等不同风格之间切换。工程上常用的技巧包括: + +* 文本提示工程(Prompt Engineering),用于细化和稳定输出风格; +* LoRA / DreamBooth 等轻量微调技术,在通用模型上快速适配特定人物、IP 或品牌风格。 + +### 2.9.2 图像到图像(Image‑to‑Image):翻译、风格迁移与局部重绘 + +**Image‑to‑Image** 任务在给定输入图像的基础上,生成另一个“受其约束”的图像版本:既保留原图的整体结构或内容,又实现某种转换或增强。典型形态包括: + +* 图像翻译 / 风格迁移:在不同视觉域之间进行映射,如“照片 → 动漫”“夏天 → 冬天”“白天 → 夜晚”“素描 → 彩色图像”。早期多基于 GAN(CycleGAN、Pix2Pix 等),现在也可以用扩散模型在条件控制下完成。 +* 条件生成:以草图、分割图、深度图、边缘图等为条件,通过 ControlNet、T2I‑Adapter 等模块引导扩散过程,让生成图严格遵守几何/布局条件,同时在纹理、光影、风格上自由发挥。 +* Inpainting / Outpainting:在原图上划定某个区域,将其视为待重绘部分(inpainting),或在画面外延展生成新内容(outpainting),实现“填坑”“扩图”等操作。 + +这类任务的关键是 **在保留约束的前提下创造新内容** 。扩散模型在这方面表现突出:在 inpainting 中,模型只对 mask 区域进行采样,而在未被遮挡的区域保持原图不变,通过语义理解与上下文信息,使新内容与周围区域在风格与光影上自然融合。对于风格迁移,模型在保留输入结构的同时,从目标风格分布中采样纹理和颜色,实现“换壳不换骨”。 + +在产品里,Image‑to‑Image 能力支撑了大量创意工具:风格滤镜、漫画化、一键天空替换、自动美颜、旧照修复、局部修图等,通常以高度可视化的界面呈现给用户。 + +### 2.9.3 文本驱动图像编辑:自然语言当“画笔” + +在传统图像编辑软件中,用户需要掌握图层、蒙版、选区、滤镜等一整套专业概念;而**文本驱动图像编辑(Text‑guided Editing)** 尝试用自然语言替代大部分专业操作: + +* “把背景换成夜晚城市天际线”; +* “让这个人穿黑色西装”; +* “把这辆车变成蓝色跑车,增加运动模糊效果”。 + +技术上,文本驱动编辑通常建立在 Text‑to‑Image 扩散模型之上,通过几种方式实现: + +* 在原图附近的潜空间中搜索或采样,使编辑后的图与原图保持高相似度,只在受文本影响的局部发生变化; +* 使用显式 mask(用户圈定区域),将编辑范围限制在特定区域(这就是许多工具中的“选中区域后输入文本指令”); +* 引入“指令控制”模块(如 ControlNet、可学习控制 token),增强模型对编辑请求的可控性与稳定性。 + +即梦、FLUX、阿里 qwen 图像模型、Stable Diffusion 生态、Canva AI 等产品都提供了类似能力:用户通过简单文字和少量交互即可完成复杂编辑。对专业用户而言,这成为加速创作流程的“智能助手”;对普通用户而言,则极大降低了图像编辑的门槛。 + +## 2.10 图像质量评估(Image Quality Assessment, IQA) + +在底层视觉增强、压缩编码、图像生成与编辑等任务中,我们经常需要回答一个看似主观的问题: **“这张图看起来好不好?”** 。手工检查显然无法规模化,而像 PSNR 这类传统指标又常常与人眼主观感受不一致。**图像质量评估(Image Quality Assessment, IQA)** 的目标,就是建立一套自动化机制,对图像的主观/客观质量进行评分或排序,成为连接“底层算法输出”和“用户真实体验”的关键环节。 + +从系统角度看,IQA 是很多流水线中的“看门人”和“调参参考”:电商/内容平台用它筛掉模糊、噪声重、压缩过度的上传图片;手机相机/相册用它在连拍中挑出“最好的一张”;云端增强和压缩服务用它进行前后对比评估,以指导模型迭代。下面从 **场景** 、**原理**和**模型**三个维度梳理 IQA,并在后续小节中展开评估类型与指标/学习范式。 + +* **场景** + * 上传质检与审核:对用户上传的图片/视频做质量评分,过滤严重模糊、曝光异常、噪声明显和压缩伪影严重的内容。 + * 智能选片与去重:在手机相册、相机应用中,从多张相似照片中选择清晰度、表情、构图更好的版本,同时识别质量差或冗余图片用于清理。 + * 增强/压缩算法评估:在图像增强、降噪、超分辨率、编解码等算法 A/B 测试中,用 IQA 指标客观衡量“哪种策略更好”,辅助参数搜索与模型选择。 + * 海报/缩略图自动选取:在视频或多图集合中自动选择视觉质量和吸引力更高的帧作为封面或海报候选。 +* **原理** + IQA 的核心是从两个维度刻画图像质量:**相对于参考图的失真程度**与 **人眼主观感知的好坏** : + * 全参考 IQA(FR‑IQA):在有高质量参考图的前提下,将待评估图与参考图进行逐像素或特征对比,衡量失真程度,用于算法研发和实验评估。 + * 无参考 IQA(NR‑IQA / Blind IQA):实际场景中更常见,没有参考图,只能从单张图的统计特征或深度特征中推断质量,需要模型从大量图像与主观评分中学习到“人眼喜欢什么样的图”。 + * 伪参考 / 降采样参考:在某些场景中,可以使用压缩前的低分辨率版本、模型预测的“理想图”等作为近似参考,兼顾可实现性与评估精度。 +* **模型** + IQA 模型大致分为**传统手工特征指标**与**深度学习****式质量预测**两大类: + * 传统指标: + * FR‑IQA:PSNR、SSIM、MS‑SSIM、FSIM 等,侧重结构、对比度和相位信息,对简单退化(如加噪、模糊)较敏感。 + * 感知指标:LPIPS、DISTS 等,在深度特征空间衡量图像间感知差异,与人眼主观感受有更高相关性。 + * 无参考 / 学习式 IQA: + * 早期方法:BRISQUE、NIQE、BLIINDS 系列等,从自然场景统计(NSS)和手工特征出发,训练浅层模型预测质量分数。 + * 深度 NR‑IQA:RankIQA、DBCNN、HyperIQA、MUSIQ 等,直接用 CNN / ViT 从图像中抽取特征,并在 MOS(Mean Opinion Score,主观评分均值)数据上监督训练,使输出质量分数尽可能拟合人眼评价。 + * 预训练表征:利用 CLIP、ViT 等大模型的特征,作为质量预测网络的输入或 backbone,在有限 MOS 数据上微调,提升对复杂失真类型的泛化能力。 + +整体来看,IQA 并不是“越高越好”的单一指标,而是一套与具体业务目标相关的评估体系:在某些场景(如监控增强)中,保留细节和可识别性比视觉自然更重要;在内容创作平台中,主观观感和审美标准则占主导。因此,工业界常见做法是:在通用 IQA 模型基础上,通过少量业务数据微调或学习加权,构建“任务感知”的质量评估器。 + +### 2.10.1 评估类型:有参考、无参考与伪参考 + +按照是否存在高质量参考图,IQA 可以分为三类: **全参考(FR‑IQA)** 、 **无参考(NR‑IQA)和伪参考** 。 + +在 **全参考 IQA** 中,我们假设存在一张理想的高质量参考图像,待评估图是其经过压缩、传输或处理后的退化版本。模型通过对两者进行逐像素或特征级比较,量化失真程度。PSNR 是最简单的度量(基于均方误差),SSIM/MS‑SSIM/FSIM 等进一步考虑亮度、对比度、结构或相位信息,在一定程度上更接近人眼感受。这类指标非常适合在算法开发阶段评估编解码、超分辨率、去噪等方法,但在真实业务中往往缺乏参考图,应用场景有限。 + +**无参考 IQA(Blind IQA)** 是实际系统中更常见的设定:只有待评估图像本身,没有任何参考。早期无参考方法(如 BRISQUE、NIQE、BLIINDS 等)主要基于自然场景统计:假设高质量自然图像在某些统计分布上有稳定形态,失真会引起统计特征变化,从而可以训练模型根据这些特征预测质量分数。深度学习时代,NR‑IQA 模型通常直接利用 CNN / ViT 提取特征,并在带有人眼主观评分(MOS)的数据集上回归质量分数或学习排序关系,使其能够覆盖噪声、模糊、压缩伪影、曝光异常等多种失真类型。 + +**伪参考 / 降采样参考 IQA** 介于两者之间:在没有真正高质量参考的情况下,使用某种可获得的近似版本(如压缩前低分辨率图、模型预测的“干净图”)作为参考,对退化程度进行估计。这种方式常见于在线视频质量监控、编解码优化任务中,可以在成本与精度之间取得平衡。 + +### 2.10.2 指标与学习范式:从 PSNR 到感知质量预测 + +在具体实现层面,IQA 采用多种指标和学习范式来逼近人眼主观感受。 + +**传统指标**方面: + +* PSNR 直接基于像素级误差,简单高效,但对人眼不敏感的变化(如轻微平移、结构保持的滤波)也会给出较大惩罚; +* SSIM、MS‑SSIM、FSIM 等从亮度、对比度、结构、相位等多个维度建模图像相似性,对结构性失真更敏感,也一定程度反映人眼对结构信息的偏好。 + +**感知指标**方面:LPIPS、DISTS 等通过在预训练深度网络(VGG、AlexNet、ViT 等)内部特征层计算向量差异,并按照不同层的重要性加权,得到一种“特征空间中的距离”,与主观感知相似性有更高相关性。它们特别适合作为生成式任务(超分、生成、编辑)的训练目标或评估指标,用来衡量“看起来像不像”。 + +**学习式质量预测**方面,深度 NR‑IQA 模型(如 RankIQA、DBCNN、HyperIQA、MUSIQ 等)直接对图像打分或排序: + +* 训练数据中,每张图像附带一组主观评分(MOS),模型以此为监督训练质量回归或排序网络; +* 模型结构上,多采用 CNN/ViT + 全局池化 + MLP 输出质量分数,或输出一组质量分布再取期望; +* 有些方法还利用对比学习或排序学习(pairwise ranking),让模型更关注“相对好/坏”的关系,而不是绝对分数。 + +随着大规模预训练视觉模型的普及,越来越多 IQA 方法采用“预训练 Backbone + 轻量头”的范式:利用 CLIP、ViT 等丰富的视觉表征,在较少 MOS 数据上进行微调,从而在跨失真类型、跨场景上保持良好的泛化。 + +在工程落地中,通常会将上述多种指标组合使用:例如 FR‑IQA 指标用于实验阶段评估算法改进;深度 NR‑IQA 模型用于线上实时质检;感知指标用于生成任务的内部优化。通过 A/B 实验将这些自动指标与真实用户数据(点击率、完播率、投诉率等)对齐,逐步构建起与业务目标高度相关的“感知质量度量体系”。 + +# 3. 3D / 空间模态(3D / Spatial / XR) + +随着应用从“平面图像/视频”走向自动驾驶、机器人、AR/VR/XR 等场景,系统不再满足于只看“2D 像素”,而是需要理解 **真实世界的三维结构、尺度和位姿关系** 。这类任务统称为 3D / 空间模态:既包括对几何与拓扑的精确建模,也包括在 3D 空间中的语义理解、定位导航与内容生成。它一端连接 LiDAR、RGB‑D、IMU 等多种传感器,另一端连接自动驾驶感知模块、机器人导航系统、ARKit/ARCore 环境模型、手机 3D 扫描建模应用以及数字孪生平台等。 + +## 3.1 3D 感知与重建(3D Perception & Reconstruction) + +在 2D 视觉里,我们只看到了“拍成照片后的世界”;而在自动驾驶、机器人、AR/VR 等场景中,更关键的是: **真实世界在 3D 空间中的位置、形状和结构** 。3D 感知与重建就是要从多种传感器(相机、LiDAR、深度相机等)出发,恢复环境的三维几何信息,并以点云、体素、网格(Mesh)、隐式场等形式表达出来,为路径规划、物理仿真、数字孪生和 3D 内容生成提供基础。 + +在工程实践中,这一层涵盖从**点云处理**到**多视角几何重建**再到**神经辐射场 / 神经场渲染**等多个技术方向,对应着自动驾驶 3D 感知模块、ARKit/ARCore 环境建模、手机 3D 扫描/建模应用以及数字孪生城市/园区建模平台等产品形态。下面从 **场景** 、 **原理** 、**模型**三个角度展开,并进一步细分几个关键子方向。 + +* **场景** + * 自动驾驶与辅助驾驶:从车载 LiDAR 点云和多摄像头图像中感知车辆、行人、路沿、车道线、交通设施等 3D 结构,用于路径规划和安全决策。 + * 室内/室外环境扫描:利用手机/平板(结构光 / ToF / 双目)或手持扫描仪采集多视角数据,实时构建房间、楼宇、街区的 3D 模型,用于 AR 建模、家装设计、数字孪生。 + * 数字孪生与 BIM:将实际工厂、园区、城市通过多视角影像和点云重建成高精度 3D 模型,用于运维管理、仿真与可视化。 + * 消费级 3D 扫描:手机 3D 扫描 App、一键“拍照变 3D 模型”工具,为 3D 打印、虚拟试穿、游戏/影视资产制作提供原始几何。 +* **原理** + * 点云处理:将 LiDAR 或多视角重建得到的稀疏/稠密点集合视作 3D 采样点集,对其进行滤波、配准、下采样和特征学习,再做分类、语义/实例分割或 3D 目标检测。 + * 多视角几何与三维重建:通过 SfM(Structure‑from‑Motion)估计多张图像之间的相机位姿和稀疏 3D 点云,再通过 MVS(Multi‑View Stereo)生成稠密点云,随后进行网格重建与纹理贴图。 + * 神经辐射场 / 神经隐式场:使用 NeRF、Instant‑NGP、Gaussian Splatting 等方法,把 3D 场景表示为连续的体密度/颜色场或高斯粒子集合,通过体渲染或光栅化生成图像,从多视图监督中学习;训练好后可以进行新视角渲染和几何提取。 +* **模型** + * 点云网络:PointNet / PointNet++、PointCNN、DGCNN、MinkowskiNet 等直接在点或稀疏体素上学习特征,用于点云分类、分割与 3D 检测。自动驾驶中常用 VoxelNet、SECOND、CenterPoint 等 3D 检测框架,将点云转换为体素或 BEV(鸟瞰图)特征后进行检测。 + * 几何重建工具链:COLMAP、OpenMVG / OpenMVS 等传统 SfM/MVS 系统,可从多视角照片恢复相机位姿和稠密点云,构建出高质量 Mesh。 + * 神经场重建与渲染:NeRF / Instant‑NGP、Gaussian Splatting 及大量改进模型,把场景编码在神经网络或高斯云中,实现高保真的新视角合成与 3D 场景重建,并逐步形成工程化产品。业界也出现了如「混元 3D」「Tripo」这类面向开发者和内容生产的 3D AI 服务,将 NeRF/高斯等技术封装成云端 API 或交互工具。 + +从这一层开始,传统几何与深度学习、隐式表示与显式网格密切交织,既要解决「如何准确还原真实世界」的问题,又要兼顾实时性和可用性,服务更上层的 3D 场景理解、3D 生成与编辑。 + +### 3.1.1 点云处理与 3D 目标检测 + +对于自动驾驶、机器人和高精度测绘而言,LiDAR 点云是最关键的 3D 传感信息之一。点云是一组三维坐标(有时附带反射强度、时间戳等)构成的稀疏点集,没有规则的栅格结构,给传统卷积带来了挑战。点云处理的目标,是从这些非结构化的点中提取有用的几何与语义信息,例如“这里是一辆车”“这里是路沿/地面”“这里是一栋建筑物”。 + +在**点云分类与分割**任务中,我们往往关注:某个点(或点簇)属于哪一类结构,如车、行人、地面、路沿、建筑、植被等,或者对场景做语义/实例分割。从建模方式看,可以粗略分为三类: + +1. 直接点云网络:PointNet / PointNet++、PointCNN、DGCNN 等直接在点集上定义“对点集排列不敏感”的运算,通过局部邻域聚合构建层级特征,适合中小规模点云的分类与分割。 +2. 体素与稀疏卷积:将点云栅格化为 3D 体素,再用稀疏 3D CNN(如 VoxelNet、MinkowskiNet)进行卷积,兼顾结构规整性与空间稀疏性,在自动驾驶 3D 检测中应用广泛。 +3. 投影与多视图:将点云投影到 BEV(鸟瞰图)、前视深度图或多视角视图,再用 2D CNN 提取特征,相对易于与成熟的 2D 检测网络结合。 + +在**3D 目标检测**中,目标不再是单纯地给点打标签,而是要预测 3D 边界框(位置、尺寸、朝向)及其类别,这是自动驾驶环境感知的核心。典型方法如 VoxelNet、SECOND、PointPillars 和 CenterPoint 等,它们通常将点云转换为体素或柱状表示,在 BEV 或 3D 空间上进行检测回归。CenterPoint 等方法通过“中心点检测”范式,直接在 BEV 上检测目标中心及其尺寸/方向,兼具精度和速度。随着深度学习与传感器硬件的演进,3D 检测已能在车规级芯片上实现实时推理,成为自动驾驶感知栈的基础模块之一。 + +### 3.1.2 多视角几何与三维重建:从照片到 Mesh + +如果没有 LiDAR,是否仍能“看懂”3D?答案是可以的——多视角几何与三维重建依赖的是“多张照片 + 摄像机运动”。通过在不同视角拍摄同一场景,我们可以利用几何约束恢复相机位姿和空间结构,这就是经典的 SfM/MVS 管线。 + +**SfM(Structure‑from‑Motion)** 主要解决两个问题: + +1. 从多张成对或多视角图像中,估计每一张图像的相机外参(位置和朝向); +2. 在统一坐标系下恢复一组稀疏 3D 特征点。 + +典型工具如 COLMAP、OpenMVG,通过特征提取与匹配(SIFT/ORB 等)、增量或全局 BA(Bundle Adjustment),可以从无标定图像集合中自动恢复稀疏点云和相机位姿。 + 在此基础上,**MVS(Multi‑View Stereo)** 会利用多视角的光度一致性,生成稠密点云:对每个像素/视线进行深度估计,逐步填充场景的几何细节。 + +获得稠密点云后,下一步是 **网格重建(Mesh Reconstruction)** : + +* 通过 Poisson Surface Reconstruction、Marching Cubes 或基于学习的方法,将散乱的点云“包裹”成连续曲面,形成带拓扑结构的 Mesh。 +* 后续通常还会进行孔洞填补、平滑、边界优化,并进行纹理贴图(Texture Mapping),得到可直接用于渲染和编辑的 3D 模型。 + +在产品形态上,这一整套管线已通过桌面软件、云服务和 SDK 的形式下沉。例如:手机上的 3D 扫描应用,会在后台调用类似 SfM/MVS 的流程,给用户“绕一圈拍照”或“扫一圈视频”之后自动输出一个可导入到游戏引擎的网格模型;数字孪生平台则在城市/园区尺度上,用航摄影像 + 街景数据跑大规模重建,生成可交互的 3D 场景。 + +### 3.1.3 神经辐射场与体渲染:NeRF、Gaussian 与新一代 3D 重建 + +传统的 SfM/MVS/网格重建,可以得到结构良好的显式几何,但在渲染质量、视角连续性和细节表现上仍有局限;而神经辐射场(NeRF)及其后续工作则以**隐式场 + 体渲染**的方式重新定义了 3D 重建和新视角合成。 + +在 NeRF 中,整个 3D 场景被建模为一个连续函数: + +![](https://ecn00p15ubf1.feishu.cn/space/api/box/stream/download/asynccode/?code=ZjYyZTc5MWFhY2QxM2FjNTI1MDFhNDM5NTEwNTBkNGFfM3RvSngwZnhwc1hMRFQxaXVXMkFNem5RSFFqUkppdkdfVG9rZW46TVltUGJUUWRib1NGV2V4dklHZ2NYandjbkJlXzE3NjcxMDU4ODM6MTc2NzEwOTQ4M19WNA) + +给定三维空间中的一个点位置 x 和观察方向 d,网络会输出该点对应的体密度 σ 与颜色 c。沿着相机视线方向对这个映射函数做体渲染积分运算,我们就能得到该相机位姿下的像素颜色;反过来,只要给定一组多视角照片及其相机参数,我们就能通过最小化渲染结果与真实图像的误差,求解出模型的参数 θ。待模型训练完成后,只需改变相机位姿,就能合成那些 “从未被真实拍摄过” 的新视角图像(Novel View Synthesis)。 + +传统 NeRF 训练和渲染速度都偏慢,后续如 **Instant‑NGP** 通过多分辨率哈希网格编码等手段,大幅加快了收敛与推理速度;**Gaussian Splatting** 则用 3D 高斯粒子替代表达场景,通过高效的光栅化策略,实现了高质量、实时的新视角渲染。与此同时,大量工作还围绕 NeRF/高斯做了可编辑、多模态、可组合等扩展,使其逐渐从研究原型走向工程体系。 + +在产品化层面,NeRF/高斯类技术已经嵌入到多种 3D AI 产品中: + +* 手机/PC 端的“多视角视频 → 3D 场景”工具,底层往往基于神经场或高斯粒子完成重建和渲染; +* 游戏/影视资产管线中,利用神经场进行快速场景捕捉和光照还原,再导出为 Mesh + 纹理供传统 DCC 工具使用; +* 各大云厂商和内容平台推出的 3D AI 服务,如腾讯系的「混元 3D」、Tripo 等,通常支持“多视图照片/短视频 → 可编辑 3D 模型/场景”,在内部则综合运用神经辐射场、SDF/Gaussian 表示与后续显式重建,把高质量 3D 结果打包为对开发者友好的 API 或交互式产品。 + +## 3.2 3D 场景理解与定位(3D Scene Understanding & SLAM) + +如果说 3D 感知与重建回答的是“这个世界长什么样”,那么 3D 场景理解与定位则进一步回答:“ **我在这个世界的哪里?这个世界中哪些地方可以走,哪些是障碍?** ” 对于扫地机器人、AGV 机器人、无人机、AR 导航和室内定位系统来说,能够在 3D 环境中自定位、自建图、自主规划路径,是生存的前提。 + +这部分工作主要围绕**3D 语义理解**与**SLAM(Simultaneous Localization and Mapping)**展开:前者在重建的 3D 场景中进行语义分割和可通行区域识别,后者则利用视觉/IMU/LiDAR 等传感器进行相机/机器人位姿估计与地图构建。在工程上,这一层通常以 SDK 或算法模块的形式嵌入到机器人底盘、无人机飞控或移动端 AR 引擎中。 + +* **场景** + * 家用与服务机器人:扫地机器人、送餐/巡检机器人在室内环境中构建地图、识别房间类型和障碍物,实现自动规划清扫或巡逻路径。 + * 仓储与物流:AGV/AMR 机器人在仓库中进行自主导航,识别货架、通道与禁入区域,完成搬运和盘点任务。 + * 无人机与户外机器人:在室外环境中构建 3D 地图,避开建筑、树木、电线等障碍,执行巡检、测绘与安防任务。 + * AR 导航与室内定位:手机/AR 眼镜通过 SLAM 获取相机位姿,并在语义地图上叠加导航箭头、房间信息和 POI,实现沉浸式导览与导航。 +* **原理** + * 3D 语义分割与场景理解:在点云或体素表示上进行语义分割,区分墙壁、地面、桌椅、货架、门窗等结构,同时识别可通行区域和障碍物,为导航和行为决策提供语义层信息。 + * 位姿估计与 SLAM:通过 Visual SLAM(单目/双目 / RGB‑D)或 LiDAR‑SLAM,从连续传感数据中估计相机/机器人的 6D 位姿,处理回环检测与地图优化,必要时融合 IMU、轮速、GNSS 等多源信息提高鲁棒性。 + * 地图构建与导航:在局部/全局地图上叠加几何和语义信息,形成 2D/3D/拓扑/语义地图,并在此基础上进行路径规划、避障和任务分配。 +* **模型** + * SLAM 系统:经典的特征点法 ORB‑SLAM 系列、直接法 DSO,以及融合惯导的 VINS‑Mono / VINS‑Fusion,通过前端特征跟踪 + 后端优化实现精确位姿估计与稠密/半稠密地图。LiDAR/视觉‑LiDAR 融合中常见 LIO‑SAM 等框架。 + * 3D 语义分割网络:3D U‑Net、MinkowskiNet 等 3D CNN,以及基于点云的 PointNet++ / KPConv / SparseConv 系列,用于点云/体素的语义分割与实例分割。 + * 多传感器融合定位:基于图优化或滤波(EKF/UKF)的方法,将视觉、IMU、LiDAR、里程计等多源信息在统一状态空间中融合,提升在恶劣光照、纹理缺失或动态环境中的定位稳定性。 + +整体上,3D 场景理解与定位构成了机器人“能动起来”的基础:既要在复杂三维世界中构建可靠的自我定位框架,又要让地图变得“有意义”,从而支持高层任务规划与人机交互。 + +### 3.2.1 3D 语义分割与可通行区域理解 + +在纯几何地图中,所有结构只是无差别的点/体素;而在真实应用中,我们关心的是:哪里是地面、哪里是墙、哪里有桌子或货架、哪里可以通行。**3D 语义分割**就是要为每一个点或体素赋予语义标签,将“纯几何”转化为“几何 + 语义”。 + +在室内/室外场景中,典型目标包括: + +* 固定结构:墙、地面、天花板、楼梯、柱子、道路、路沿等; +* 家具与设施:桌椅、柜子、货架、门窗、扶手等; +* 可通行/不可通行区域:机器人可行走区域、需绕行的障碍物、禁入区域等。 + +建模上,3D 语义分割常采用: + +* 体素/稀疏卷积方案:把点云体素化后,用 3D U‑Net、MinkowskiNet 等稀疏 CNN 学习体素级特征,兼顾局部细节和全局结构。 +* 点云直接方案:PointNet++、KPConv 等点云网络,对局部邻域做特征聚合,实现点级别的语义预测。 + +在扫地机器人、AGV 机器人等应用中,语义分割的结果会被进一步抽象成 **语义地图** :例如把房间划分为卧室/客厅/厨房,把仓库内空间划分为货架区域/通道/禁行区。机器人不仅知道“哪里可以走”,还可以根据房间类型定制不同策略(如卧室避开地毯区域、仓库中优先覆盖某些货区)。 + +### 3.2.2 位姿估计、SLAM 与多传感器融合定位 + +**SLAM(Simultaneous Localization and Mapping)** 的目标是:在未知环境中,一边移动一边估计自身轨迹,同时构建环境地图。对于没有高精度外部定位(如 RTK‑GNSS)支持的室内环境来说,SLAM 是绝大多数机器人和 AR 引擎的首选方案。 + +在视觉 SLAM 中,以 ORB‑SLAM、DSO、VINS‑Mono/VINS‑Fusion 为代表的方法,通常分为几个关键模块: + +* 前端:从连续图像中提取和跟踪关键点/图像块,估计相邻帧之间的相对位姿。 +* 后端:在滑动窗口或全局图中进行 BA 或图优化,处理漂移、回环检测与重定位。 +* 地图:根据位姿和深度信息构建稠密或半稠密地图,为后续导航或渲染提供基础。 + +纯视觉在纹理缺失、光照剧烈变化时容易失效,因此实践中一般会采用 **多传感器融合定位** : + +* 视觉 + IMU:VINS‑Mono/VINS‑Fusion 等框架将 IMU 的高频短时精度与视觉的尺度和几何约束结合,大幅提高短时和急转弯场景的稳定性。 +* LiDAR + IMU + 视觉:如 LIO‑SAM 等里程计框架在 LiDAR‑SLAM 中引入惯导与可选视觉信息,利用三者互补的特性实现鲁棒定位,在自动驾驶和高精度测绘中广泛使用。 + +在产品层面,这些方法通常被封装为机器人底盘控制器、无人机飞控、AR 引擎(如 ARKit/ARCore 中的 Visual‑Inertial SLAM)或室内定位 SDK 的一部分,对上层应用屏蔽了复杂的状态估计和图优化逻辑,让开发者可以直接拿到“实时位姿 + 地图”。 + +### 3.2.3 语义地图、导航与避障 + +有了稳定的位姿估计和几何/语义地图,下一步是让机器人“聪明地动起来”。这部分主要涉及 **语义地图构建、路径规划和避障** 。 + +* **语义地图构建** :在几何地图上叠加语义信息(房间类型、POI、区域标签),形成适合高层决策的地图表征。例如: +* 家庭场景中,将地图划分为卧室、客厅、厨房、卫生间等区域; +* 仓储场景中,标注货架位置、装卸区、危险区域等; +* 大型商场/展馆中,标注店铺、服务台、洗手间等 POI,用于 AR 导航和导览。 +* **路径规划与避障** :在地图上构建栅格图或拓扑图,利用 A*、D* Lite、RRT 等规划算法为机器人找到从起点到目标点的可行路径;同时结合实时感知(前方障碍物、动态行人/车辆),进行局部重规划和避障,保证运行安全与效率。 +* **导航行为与任务调度** :在 AGV 机器人和无人机中,还会在导航之上叠加任务调度与多机协同模块:分配任务、避免拥堵、优化整体路径与能耗。 + +AR 导航与室内定位系统本质上也依赖类似的语义地图和路径规划,只不过“执行者”从机器人变成了人:系统通过 SLAM 获取用户设备的位姿,在语义地图上规划行走路径,再以增强现实的形式把路径可视化叠加到真实世界视图中。 + +## 3.3 3D 生成与编辑(3D Generation & Editing) + +如果说 3D 感知和 SLAM 是从真实世界“采集并理解”几何,那么 3D 生成与编辑则是站在内容生产的角度: **如何用 AI 自动生产和改造 3D 资产** 。这直接面向游戏、影视、数字人、虚拟空间、电商展示、3D 打印等巨大的内容需求。 + +最近两三年,随着 NeRF/Gaussian、SDF 表示、多模态扩散模型等技术的突破,3D 生成进入快速发展期:从文本、图像、视频一键生成 3D 模型或场景已经成为现实,各大云厂商和创业团队推出了如「混元 3D」、Tripo、DreamFusion / Magic3D 系列方法落地为在线工具,使 3D 生产逐渐向“人人可用”的方向演进。3D 生成与编辑大致可以拆成四类能力:文生 3D、图/视频生 3D、模型优化与编辑,以及绑定与动画。 + +* **场景** + * 游戏 / 影视资产制作:为角色、道具、建筑、场景快速生成可用的 3D 模型,大幅降低美术工作量。 + * 电商与产品展示:根据产品文案或照片自动生成 3D 展示模型,用于 3D 看样、AR 试摆、交互式广告。 + * 数字人与虚拟内容:快速生成虚拟人、虚拟试衣模特、虚拟主播场景等 3D 资产,支持直播、短视频和互动应用。 + * 3D 打印与个性化建模:从草图/照片/文本生成可打印模型,实现个性化礼品、原型设计与教育场景应用。 +* **原理** + * 文生 3D(Text‑to‑3D):将文本描述编码为语义向量,再通过多阶段优化或扩散过程生成 3D 表示(NeRF/SDF/Gaussian/Mesh),通常借助强大的 2D 文生图模型做“评分器”或先验。 + * 图 / 视频生 3D:利用单张或多张图像、多视角视频作为监督,结合 NeRF、SDF 或隐式/显式混合表示,重建出带几何和纹理的 3D 模型。 + * 3D 模型优化与编辑:对已有模型进行重拓扑、简模、细节增强、LOD 生成、UV 展开和贴图生成,以及基于语言/图像的形变与风格化。 + * 绑定与动画:为 3D 角色自动推断骨骼结构并完成 Rigging,支持骨骼动画和物理模拟(布料、软体、刚体),形成可驱动的动态资产。 +* **模型** + * 3D 生成基础表示:NeRF / Instant‑NGP、SDF(隐式表面)、Gaussian Splatting 以及 Mesh‑based 生成网络,构成 3D 数据的表达空间。 + * Text‑to‑3D 方法:DreamFusion、Magic3D、Fantasia3D 等典型路线,通过“2D 文生图模型 + 3D 优化”或“3D 扩散模型”完成从文本到 3D 的端到端生成,为后来的混元 3D、Tripo 等产品奠定技术基础。 + * 图/视频生 3D 模型:基于 NeRF/SDF/Gaussian 的重建与优化框架,从多视图一致性和单视图先验中恢复稳定的 3D 几何与纹理。 + * 绑定与动画算法:自动骨骼提取、骨骼权重预测、基于深度学习的 Retargeting 与运动生成,为虚拟人/角色动画提供一键化工具。 + +在这一层,传统 3D DCC(Maya/Blender/3ds Max 等)与 AI 工具链逐步融合:许多 3D AI 服务以插件或云端接口的形式嵌入现有生产流程,让建模师/美术可以在人机协作中迅速迭代资产。 + +### 3.3.1 文生 3D 与场景草模 + +**文生 3D(Text‑to‑3D)** 的目标是:给出一句自然语言描述,例如“一个卡通风格的黄色小鸭玩具,带有蓝色围巾,适合儿童玩具展示”,系统自动生成一个可编辑的 3D 模型(Mesh/NeRF/SDF/Gaussian 等)。这是将大语言模型/多模态模型与 3D 表示结合的典型应用。 + +典型技术路径包括: + +1. **基于 2D 文生图模型的优化** (如 DreamFusion、Magic3D): +2. 使用强大的 Text‑to‑Image 模型(如扩散模型)作为“评估器”,给定 3D 表示在某一视角下渲染出的图像,评估它与文本描述的匹配程度。 +3. 通过梯度优化或扩散过程,迭代调整 3D 表示(NeRF/SDF/Mesh),使得从多个视角渲染出的图像都符合文本语义。 +4. **3D 扩散模型 / 直接生成** : +5. 将 3D 数据(点云、体素、隐式场参数、Gaussian 粒子等)作为扩散模型的生成目标,在大规模 3D 数据集上预训练; +6. 通过文本条件控制,实现端到端的 Text‑to‑3D 采样。 + +在场景级别,**场景草模**能力允许用户用自然语言或粗略草图描述空间布局,例如“一个带落地窗的客厅,左边一张 L 型沙发,中间一张茶几,右侧有书架和电视柜”,系统自动搭建出一个几何和语义合理的 3D 布局草图。后续可以在 DCC 工具中细化模型与材质,或直接通过混元 3D、Tripo 等工具中的“场景生成”能力快速产出可用的场景原型。 + +当前,多家平台已经推出面向设计师和开发者的 Text‑to‑3D 产品: + +* 「混元 3D」等将文生 3D、多视图生成与重建能力整合进统一界面,支持从文本快速生成角色、道具和场景再导出到游戏引擎; +* Tripo 类产品则强调“多模态输入 + 一键 3D 输出”,支持简单文本和参考图像混合,引导生成满足风格与结构需求的 3D 资产。 + +### 3.3.2 图 / 视频生 3D 与模型优化编辑 + +与纯文本相比,从图像或视频生成 3D 模型对几何约束更强,在视觉上一致性也更好。因此,大量 3D AI 产品支持 **图生 3D / 视频生 3D** : + +* 单张照片 → 粗 3D:利用单视图先验(如人脸、人体、常见物体类别的形状先验),推断大致的 3D 几何,生成可用于预览或简单交互的 3D 模型。 +* 多张照片 / 短视频 → 高质量 3D:综合使用 NeRF/SDF/Gaussian 重建、多视角几何和后处理,将数十张照片或几秒钟视频转换为高保真的 3D 模型,适合游戏/影视资产或高质量电商展示。 + +生成出 3D 几何只是第一步,后续还需要大量**模型优化与编辑**工作: + +* 重拓扑与简模:将隐式场或高多边形 Mesh 转换为结构规整、面数可控的拓扑,以便于绑定、动画和实时渲染。 +* LOD 生成:自动生成多级细节模型(Level of Detail),在远处用低模、近处用高模,兼顾画质与性能。 +* UV 展开与贴图生成:自动为模型展开 UV、生成或优化法线贴图、位移贴图、粗糙度/金属度贴图等 PBR 材质;有些模型还支持从文本或参考图自动生成风格化纹理。 +* 几何与风格编辑:基于语言或示例图进行局部修改,如“让这个椅子腿变短一点”“把这栋楼改成赛博朋克风格”,底层通常通过形状潜空间操作或神经场编辑实现。 + +混元 3D、Tripo 等产品往往将上述流程打通:用户从照片/视频或简单文本出发,系统内部完成重建、重拓扑、贴图与导出,让非专业用户也能在几分钟内获得“即插即用”的 3D 模型,大幅缩短从概念到资产的时间。 + +### 3.3.3 绑定、动画与动态 3D 资产 + +静态模型只是内容的一半,“能动起来”的 3D 资产在游戏、影视、虚拟人和交互应用中更为关键。这涉及**骨骼绑定(Rigging)、权重绘制、动画与物理模拟**等环节,传统上都是高门槛的专业工作,如今也逐渐被 AI 工具辅助甚至半自动完成。 + +* **自动 Rigging** :给定一个角色 Mesh,系统自动推断骨骼层级结构(脊柱、四肢、手指等)和骨骼在模型中的位置,并预测每个顶点相对于各个骨骼的权重。近年来的深度学习方法可以在大规模带骨骼标注的角色数据集上学习这一映射,实现一键骨骼绑定。 +* **动画与动作生成** :在已有骨骼上叠加动作数据(Mocap 或 AI 生成),完成走路、跑步、表情、手势等动画;基于深度学习的动作生成与 Retargeting 可以将视频中的人体动作或其他角色的动作迁移到新角色上。 +* **物理模拟** :对布料、软体、刚体等进行物理模拟,使头发、衣服、旗帜、柔软物体的运动更自然。有些系统利用神经网络加速或近似物理,使实时引擎中的物理效果更逼真。 + +在产品与生态上,这些能力常常内嵌于: + +* 游戏 / 影视资产工具链:为建模师提供一键 Rigging、自动权重分配和基础动作库,大幅减少重复劳动; +* 虚拟人 / 数字资产制作平台:从人物照片或扫描开始,经由 3D 重建 + 自动 Rigging + 动作驱动,输出可在直播、短视频、互动应用中驱动的虚拟人; +* 3D AI 平台(如混元 3D、Tripo 及同类产品):在 3D 生成之后,进一步增加绑定与简单动画功能,让用户“生成的角色可以立刻动起来”,而不需要复杂的 DCC 工具操作。 + +随着 3D 生成与编辑技术的成熟,整个 3D 内容生产流程正在从“以专业 DCC 工具为中心”演化为“AI 驱动的人机协作”:AI 负责生成与大量基础工作,人类更多在风格定义、品控和关键设计节点上做决策。混元 3D、Tripo 等新一代 3D AI 产品正是这一趋势的集中体现,为上层的游戏、影视、AR/VR、数字孪生和虚拟人应用提供了更快、更易用的 3D 基础设施。 + +# 4. 音频(Audio / Speech) + +在整体技术栈中,“音频”对应的是对声学信号的感知与生成:既包括对原始波形和频谱的处理,也包括把语音转为文字、理解“谁在说”“说了什么”,以及进一步对声音、音乐进行创作和合成。与视觉类似,音频也可以被拆成多层:底层的**波形与频谱处理**负责“听清楚”;中层的**语音识别与说话人技术**负责“听懂是谁在说什么”;在此之上,是更抽象的**音频/音乐理解**与 **语音、音乐生成** 。这一整块能力共同支撑了会议实时字幕、语音助手、播客后期修音、智能音箱、声学安防监控、音乐推荐与生成等产品。 + +## 4.1 波形层面音频处理:从“听得清”开始 + +在音频技术的最底层,我们首先关心的并不是“说了什么”“是谁在说”“音乐是什么风格”,而是 **这个声音本身干不干净、听不听得清** 。这一层主要在波形和频谱层面工作,通过重采样、增强、降噪、分离等操作,把嘈杂、失真、混在一起的原始声音加工成更适合后续识别、分析和生成的“干净信号”。可以把它类比到视觉里的“图像增强 + 去噪 +分离前景/背景”,更多是在做声学层面的清理,而不直接处理语义。 + +从产品角度看,这一层几乎“隐身”在所有音频产品背后:会议软件的实时降噪、播客/短视频后期修音、录音笔和手机里的“语音增强模式”、直播平台里的“美声开关”,以及给 ASR/声纹模型做的前端预处理,都是波形层面音频处理的直接体现。下面依旧从 **场景** 、**原理**和**模型**三个角度来梳理,并在后续小节具体展开预处理 & 特征提取、增强与降噪、声源分离三个关键方向。 + +* **场景** + * 在线沟通与会议:Zoom、腾讯会议等在嘈杂办公室、开放工位、家中环境下,实时压制键盘声、敲击声、街噪、回声,让语音更清晰。 + * 内容创作与后期修音:播客、短视频、直播后期中,自动消除底噪、电流声、房间混响,修补录音爆音和频段缺失,提高整体听感。 + * 录音与转写前端:录音笔、智能字幕、会议转写服务在进入 ASR 之前,通过 VAD、降噪、响度归一等处理,提升后端识别鲁棒性。 + * 终端与 IoT:智能音箱、车机、摄像头等设备上的“远场拾音”与“降噪模式”,在复杂声场中尽量捕获到主说话人或关键声源。 +* **原理** + 波形层面处理通常不直接理解语义,而是围绕频谱结构和统计特性做信号优化: + * 在时间域和频率域之间来回变换(如 STFT → 频谱/梅尔频谱 → iSTFT),对噪声频带、混响特征或背景声进行抑制或建模。 + * 通过 VAD 和能量/谱特征,区分“有语音的片段”和“静音/噪声片段”,减少无效片段对后端的影响。 + * 使用深度学习或经典滤波方法估计“干净语音谱”和“噪声谱”的掩码或增益函数,对频谱进行加权,达到增强与降噪的目的。 + * 在多声源混合的场景中,通过端到端分离网络或稀疏表示,将不同说话人、人声与伴奏、前景与背景环境声解混到独立的轨道。 +* **模型** + 波形/频谱层面的模型大致可分为两类:**频谱域模型**和 **时域端到端模型** : + * 频谱/梅尔频谱上的 U‑Net 系列:Spectrogram‑based U‑Net、DCCRN 等,在时–频平面上做“图像式”的卷积与编码–解码,是语音增强、歌声分离等任务的常用方案。 + * 波形端到端模型:Wave‑U‑Net、Conv‑TasNet、Demucs 等,直接在时域波形上建模,避免显式 STFT/ISTFT,往往在主观听感和时域保真度上效果更好。 + * 经典信号处理方法:谱减、Wiener 滤波等传统频域方法,在轻量级设备或对延迟极敏感的场景中仍然广泛存在,常与深度增强网络结合形成“混合方案”。 + +### 4.1.1 预处理与特征提取:为后端“清场搭台” + +任何后续的 ASR、声纹识别、事件检测、TTS 等模型,都需要一个尽量统一、干净、结构化的音频输入,这就是预处理与特征提取层的职责。它负责做最基础却又极其关键的“清场”和“格式统一”,为上游音频模型搭好舞台。 + +在预处理阶段,首先会对采集到的音频做 **采样率转换和声道转换** :比如把 48kHz 立体声转换为 16kHz 单声道,以满足下游模型的输入规格,并降低计算成本。随后,会对响度进行归一化、去直流分量、简单滤波等,使不同设备、不同场景下录得的音频在能量尺度上更加一致。 + +**语音端点检测(VAD)** 则是预处理中的另一个关键环节。它尝试在音频流中自动划分“有语音的片段”和“静音/纯噪声片段”,常基于帧能量、谱熵、零交叉率或小型神经网络判别。VAD 的好处是:可以显著减少送入 ASR/声纹模型的无效数据,降低计算量,同时避免静音段干扰识别(例如误识为长串空格或奇怪字符)。在实时通信中,VAD 还可以驱动“语音活动指示灯”和自动静音逻辑。 + +在特征提取层面,最常见的是将时域波形转为**频谱**或 **梅尔频谱** 。通过短时傅里叶变换(STFT),音频被分解为随时间变化的频率分布;再通过梅尔滤波器组,可以得到更符合人耳感知的梅尔频谱或梅尔倒谱特征(如 log Mel‑spectrogram、MFCC)。这些时–频特征为后续的识别、分离与生成提供了一种“二维表示”,类似视觉里的灰度图或多通道特征图,便于卷积、注意力等结构处理。随着端到端建模的发展,也有越来越多模型直接在波形上学习特征(如 Wav2Vec 2.0 ),但在工程实践中,STFT + 梅尔特征的组合仍然是最普遍、最稳妥的前端。 + +### 4.1.2 增强与降噪:把“糊音”修成“干声” + +在真实环境中,声音几乎总是在噪声和混响中传播:空调声、键盘敲击、路噪、人群嘈杂、房间回声,都在不同程度上降低了语音和音乐的可懂度与主观质量。**语音增强与降噪**的目标,就是在尽量保持语音自然度和完整度的前提下,抑制这些背景干扰,把“糊掉”的声音尽可能修成“干净”的声音。 + +在传统方法中,这一任务主要通过谱减、Wiener 滤波等频域技术实现:先估计噪声谱,然后在频谱上按一定规则“减去”噪声或进行频带增益调整。虽然实现简单、实时性好,但在强噪声、非平稳噪声和复杂混响场景下容易产生明显的“音乐噪声”和伪影。 + +深度学习方法则通过在频谱或波形上学习一个 **映射** :给定带噪语音,预测一个时间–频率掩码或直接预测干净波形。常见方案包括在梅尔/线性频谱上使用 **Spectrogram‑based U‑Net、DCCRN** 等编码–解码结构,对每一帧的频谱进行细致修复;也有直接在时域波形上用 **Conv‑TasNet、Demucs、Wave‑U‑Net** 等模型进行端到端的波形增强。这些方法在语音电话、在线会议、录音修复等场景中,能显著提高语音清晰度和主观听感。 + +在内容创作和后期制作中,“录音修复”往往还涉及减少爆音(plosives)、削减齿音(sibilance)、补偿频段缺失以及均衡(EQ)和动态处理(压缩器/限幅器)等更“音频工程师味”的操作。越来越多的工具将这些传统处理与深度模型结合,提供一键“修音”和“音频美化”能力,服务播客、视频创作者和直播平台。 + +### 4.1.3 声源分离:把“混音”拆开 + +如果说增强与降噪是“让主声更突出、背景更安静”,那么**声源分离**则进一步尝试将混合在一起的多个声源完全拆分成独立轨道。例如:会议录音中多位说话人同时讲话;音乐中人声与伴奏混在一起;环境录音中主事件(如警报、喊叫)掩埋在背景噪声里。声源分离的目标,是从单条或多条混合信号中,恢复出每个独立声源的波形或频谱。 + +在语音领域,**多说话人分离**是一个核心应用:模型需要在没有单独麦克风分轨的情况下,根据声纹、时频结构和说话人特征,将多个重叠语音分到不同通道。这类能力不仅能提升多说话人 ASR 的表现,还可为说话人分离与标注(Diarization)提供更干净的输入。在音乐领域,**人声/伴奏分离(歌声分离)**则可以从一首混音好的歌曲中分离出清晰的人声轨和纯伴奏轨,用于翻唱、Remix、卡拉 OK、音乐分析等。类似地,**环境音/前景声分离**可用于安防与 IoT 场景,从复杂背景中提取关键事件声(如玻璃破碎、冲突声)。 + +在模型层面,声源分离通常采用比普通增强更强的建模能力和更复杂的架构。**Conv‑TasNet、Demucs、Wave‑U‑Net** 等端到端网络可以直接在时域进行多声源分解;在频谱域上,则常见多分支 U‑Net、注意力、掩码估计等结构,分别为不同声源预测专门的掩码或频谱。随着训练数据和计算资源的增长,现代声源分离模型已经能在相当复杂的混响和噪声环境下,输出可用于实际创作与分析的高质量分轨,为直播美声、多说话人会议、音乐制作和音频检索提供了坚实基础。 + +## 4.2 语音识别与说话人技术(ASR & Speaker) + +在波形层面完成了预处理、增强和分离之后,我们终于可以开始问更高层的问题:**“音频里说了什么?”“是谁在说?”“什么时候谁在说?”** 这一层聚焦的是各种围绕语音本身的“理解与标注”任务:自动语音识别(ASR)、说话人识别与验证、说话人分离与标注(Diarization),以及面向交互的热词与关键词检测(KWS)。 + +从产品形态看,这一层是绝大多数“语音产品”的核心:语音输入法、会议转写、客户服务录音分析、智能客服质检、智能音箱和车机语音交互、电话机器人、金融场景声纹验证等,几乎都直接依赖这些技术。它们把前一层“干净的声音”转化为文字序列、说话人标签或关键词事件,是音频到语义世界的最重要桥梁之一。 + +* **场景** + * 自动语音识别(ASR):实时字幕、语音输入法、会议与课堂记录、客服通话转写,为用户提供“听觉到文本”的即时通道。 + * 说话人识别与验证:手机/银行/呼叫中心中的“声纹解锁”“声纹验证”,以及在海量录音中检索某一特定说话人。 + * 说话人分离与标注(Diarization):在会议、访谈、圆桌讨论中,自动回答“谁在什么时候说话”,实现“分说话人转写”。 + * 热词与关键词检测(KWS):智能音箱/车机唤醒词检测(“Hey Siri”“OK Google”),以及在客服录音、质检中捕捉关键短语(如“投诉”“退款”“要升级”等)。 +* **原理** + 这一层的大部分任务都可以被统一视为对音频序列进行 **时间对齐与序列标注** : + * ASR:给定一段语音,学习从声学特征到文本序列的映射,常使用 CTC、RNN‑Transducer(RNN‑T)或基于注意力的端到端结构;现代模型多采用大规模预训练(如 Wav2Vec 2.0、Whisper 等)再微调。 + * 说话人识别:从音频中提取一个固定维度的 **说话人嵌入** (speaker embedding,如 x‑vector、ECAPA‑TDNN),在这个嵌入空间中,同一人的语音彼此接近,不同人的语音彼此远离,再结合度量或分类模型完成识别与验证。 + * 说话人分离与标注(Diarization):综合利用声纹嵌入、VAD、分段聚类或端到端网络(EEND),为每一段时间片分配说话人标签,从而拼出“时间轴上的多说话人时间线”。 + * KWS:在连续音频流上进行低延迟的小模型检测,对预定义的唤醒词或关键词进行局部模式匹配和置信度评估,兼顾低算力与高召回。 +* **模型** + ASR 与说话人技术的模型谱系既包括端到端架构,也包括专门的嵌入模型与聚类方法: + * ASR:Wav2Vec 2.0、Conformer、Whisper、RNN‑T、Citrinet 等,大多采用卷积 + 自注意力或纯自注意力结构,支持多语种、大词表和长上下文。 + * 说话人嵌入:ECAPA‑TDNN、x‑vector、i‑vector 等,通过对大量说话人数据进行分类训练或度量学习,得到稳健的说话人特征空间。 + * Diarization:从 VAD + 分段 + 聚类的传统流程,到 End‑to‑End Diarization(EEND)这类直接输出“时刻 × 说话人”矩阵的端到端方法。 + * 热词/关键词检测:轻量级 CNN/RNN/Transformer 前端组合 CTC 或门控机制,嵌入在设备本地,以超低算力、低延迟实现常开监听。 + +### 4.2.1 自动语音识别(ASR):把“声音”变成“文字” + + **自动语音识别(ASR)是“音频→文本”的主通路:无论是语音输入法,还是会议转写、智能字幕、客服录音分析,第一步都是要把用户说的话准确地转成文字。现代 ASR 系统多采用端到端架构** :从声学特征(如梅尔频谱或直接波形)出发,经过一系列深度网络(如 Conformer、Citrinet、基于 Transformer 的 Encoder),直接输出文字序列或对应的 token 序列。 + +在建模上,ASR 的难点主要包括长时依赖、多语种与方言、口音变化、重叠语音、背景噪声以及领域内专有名词。为此,当前主流方向是利用大规模无标注音频做自监督预训练(如 Wav2Vec 2.0、HuBERT),或在多语种、多任务数据上做大规模监督训练(如 Whisper),再通过相对少量的领域数据进行微调,从而在不同语言、口音和场景下达到较好的鲁棒性。 + +在产品层面,ASR 通常被打包为“语音输入法 SDK”“云端语音识别 API”“会议转写服务”等能力输出:前端可以是实时流式识别(RNN‑T、流式 Transformer 等),后端可通过热词注入、自定义词表、上下文约束来强化对特定人名、地名、品牌名和业务术语的识别。这些识别结果往往是后续 NLP、对话系统和数据分析的基础。 + +### 4.2.2 说话人识别与分离标注:回答“是谁”与“何时在说话” + +与“说了什么”相比,**“是谁在说”在很多应用中同样重要:金融、政务、客服、安防等场景需要通过声纹识别**来验证身份或排查风险;而会议与访谈场景则需要知道“每一句是谁说的”,以支持分说话人转写、发言统计和行为分析。 + +在**说话人识别/验证(Speaker Recognition)** 任务中,系统的目标是:给定一段语音,判断说话人是谁,或者判断是否与某个注册说话人属于同一人。现代系统通常通过 ECAPA‑TDNN、x‑vector 等模型,从语音段中提取一个固定维度的说话人嵌入向量。在训练阶段,以说话人分类与度量学习的组合,保证同一人的嵌入更为聚集、不同人之间的嵌入距离更大;在推理阶段,再采取最近邻或后端判别器(如 PLDA、Cosine scoring with margin)进行验证与识别。这样,系统就能在电话、麦克风、噪声环境下,以一定置信度回答“是不是同一个人”。 + +**说话人分离与标注(Diarization)** 则进一步回答“谁在什么时候说话”。传统方案通常包含三个步骤:先用 VAD 找出有语音的片段,再将长音频切成短 segments,为每个 segment 提取说话人嵌入,最后在嵌入空间中做聚类和时间拼接,得到一条多说话人时间轴。更先进的 **End‑to‑End Diarization (EEND)** 类方法则尝试直接从音频特征输出“时间 × 说话人”布尔矩阵,端到端学习重叠语音、说话人切换等复杂模式。Diarization 在会议、访谈节目、法庭记录、电话客服等场景中极具价值,常与 ASR 结合形成“带说话人标签的文字记录”。 + +### 4.2.3 热词与关键词检测:面向交互和监控的“耳朵” + +在持续的音频流中,不是每一秒都值得被完整识别和存储。**热词与关键词检测(KWS)**的角色,就是一个始终在线的“守门员”: + +* 在智能音箱、车机、手机助手中,KWS 模块负责检测唤醒词(如“Hey Siri”“OK Google”“小爱同学”),一旦检测到唤醒词,就把音频流交给更昂贵的 ASR 与对话系统处理。 +* 在智能客服、质检和合规场景中,KWS 会对录音或实时通话中出现的关键短语(如“投诉”“退货”“维权”“欺诈”)进行标记和告警,为后端分析和质检策略提供触发点。 + +在技术实现上,KWS 通常需要在**极低算力和低延迟**的约束下运行,尤其是本地设备上的唤醒词检测:模型往往是一个小型 CNN/RNN/Transformer 前端,接 CTC 或门控判别头,对特定词的声学模式进行检测,并利用滑动窗口和置信度平滑避免误唤醒。对于关键词质检场景,则可以采用更强的 ASR + 关键词匹配/正则 + 统计分析,或者直接训练端到端关键词 tagging 模型。无论哪种形态,KWS 本质上是在语音流上加了一层“事件级”的语义筛选,是连接音频世界与交互逻辑的重要接口。 + +## 4.3 音频/音乐理解(Audio Event & Music Understanding) + +并非所有音频都以“语音”为中心。现实中有大量与环境声、事件声、音乐相关的场景,它们更关注的是:**“发生了什么声音事件?”“当前环境是什么声景?”“这首歌是什么风格、用了哪些乐器、节奏和调是什么?”** 这部分能力统称为音频/音乐理解,主要围绕声音事件检测、环境/场景分类和音乐属性理解展开。 + +从产品视角看,音频理解技术支撑了安防声学监控、IoT 声学传感器、智能设备的环境自适应、音乐推荐与分类、音乐版权识别、音乐检索和创作辅助等广泛应用。与图像中的“图像分类 + 细粒度分类”类似,这一层把原本连续、复杂的声音空间结构化成离散的事件标签、多维属性向量和风格描述。 + +* **场景** + * 声音事件检测:检测警报声、玻璃破碎、婴儿哭声、撞击声等,用于安防监控、智慧楼宇、车辆安全系统和工业告警。 + * 环境/场景分类:识别“室内/室外”“办公室/车内/街道/地铁”等声景,为智能设备的降噪策略、自适应增益、模式切换提供依据。 + * 音乐理解与音乐信息检索(MIR):曲风分类、乐器识别、节奏与调性分析,支撑音乐推荐、歌单生成、音乐检索、版权识别和创作助手。 +* **原理** + 音频/音乐理解大多基于**时–频特征 + 深度神经网络**进行分类或多标签标注: + * 使用 log Mel‑spectrogram 等特征,将音频转化为“声学图像”,再利用 CNN、CRNN 或 Transformer 等结构进行时–频模式识别。 + * 对于声音事件检测,往往采用多标签、多时序输出,对每种事件在时间轴上进行存在性预测,有时还会结合弱监督标签和多实例学习。 + * 对环境/场景分类,则更注重长时间统计特征和背景格局,往往需要在较长窗口上建模。 + * 音乐理解任务则结合音乐理论知识,对节奏(BPM)、拍点、调性、和弦和结构进行建模,部分任务通过自监督或对比学习预训练音乐嵌入,再做下游微调。 +* **模型** + 常见的音频理解模型多在公开数据集(如 AudioSet)上预训练,再迁移到具体任务: + * VGGish、YAMNet、PANNs 等 CNN/CRNN 模型,在大规模有声数据上预训练后,可用于多种音频事件与声景任务。 + * AST(Audio Spectrogram Transformer)等 Transformer‑based 模型,直接在频谱图上使用自注意力,获得更强的全局时–频建模能力。 + * 针对音乐的 MusicTagging / MIR 模型,会在百万级歌曲上预训练标签模型或嵌入模型,用于风格/情感/乐器标签、音乐检索和推荐。 + +### 4.3.1 声音事件与环境声景:让设备“听得懂环境” + +在安防、IoT、智慧城市、车载系统中,光靠摄像头并不足以全面理解环境状态。**声音事件检测**的目标,就是让系统“听得懂”关键事件:当发生玻璃破碎、警报拉响、婴儿哭泣、碰撞、尖叫、打斗、破坏行为时,系统能够在音频信号中识别并发出告警。与语音识别不同,这类事件往往是短促、非语言的,频率范围和能量形态各异,且可能和背景噪声高度重叠。 + +**环境/场景分类**则更关注持续性的声景(acoustic scene):是安静办公室、热闹街道、车内、高铁站还是咖啡馆?系统可以根据声景自动调整降噪强度、回声抵消参数、麦克风阵列波束指向,甚至改变交互策略(例如在车内通过更简短的反馈交互,在嘈杂街道上提高输出音量)。在 IoT 场景中,多个声音传感器组成的“声学网络”可用于对环境状态进行长期监控和统计分析。 + +在技术实现上,这两类任务都大多采用**多标签分类 + 时序建模**方案:将音频转换为梅尔频谱,使用 VGGish、PANNs、AST 或类似模型进行特征抽取,再用时序池化或序列模型输出每个标签在时间轴上的激活情况。由于很多数据集只提供“片段级标签”(weak labels),模型常需通过多实例学习、自注意力池化等方式,在弱监督下学习事件的时间定位。 + +### 4.3.2 音乐理解与标签:从“歌单标签”到“结构分析” + +在音乐领域,音频理解的目标不仅仅是“这是一首什么歌”,更是要回答:**“这首歌什么风格?用到了哪些乐器?节奏快慢如何?调性与大致和声结构是什么?”** 这些信息一方面支撑音乐推荐与歌单编排,另一方面也为创作者和生成模型提供结构化“音乐元数据”。 + +**曲风分类**任务会根据歌曲整体声学特征与结构,将其归入流行、摇滚、古典、嘻哈、电子、Lo‑Fi 等不同风格;**乐器识别**则在时–频特征上区分鼓、贝斯、吉他、钢琴、弦乐等不同乐器的声学指纹,可用于乐器统计、音乐检索和混音分析。**节奏/调性分析**则是对 BPM、拍点位置、拍号、主调(Key)等进行估计,为节奏匹配、自动和声、DJ 混音、游戏音轨同步等任务提供基础。 + +在模型上,音乐理解多沿用通用音频模型(如 PANNs、AST),但也有大量专门面向音乐信息检索(MIR)的模型与预训练嵌入。典型做法是在大规模音乐数据集上进行 **多标签音乐标签学习** (genre、mood、instrument、era 等),得到音乐嵌入空间,再在上述具体任务上微调或做零样本推断。结合这些模型,音乐平台可以更智能地完成音乐分类与推荐,版权平台可以强化音乐指纹与相似性检索,而创作工具则可以利用这些理解能力,为用户推荐合适的伴奏、扩展相似风格或自动生成音乐结构。 + +## 4.4 语音与音频生成(TTS / VC / Music Generation) + +在完成了对音频的“清理”“识别”和“理解”之后,下一层自然的问题是:**“我们能否直接让机器‘说话’、‘唱歌’甚至‘作曲’?”** 这就是语音与音频生成的世界:从文本到语音(TTS),从一种声音到另一种声音(VC / Voice Cloning),到更大范围的音乐与音效生成,再到可以演唱歌词和旋律的歌声合成。与图像生成类似,这一层不再只是在已有数据上打标签或提取结构,而是主动“创造”新的声音内容。 + +在产品层面,这一层能力已经渗透到各类应用:OpenAI TTS、ElevenLabs、火山引擎、minimax等语音产品线为应用提供高质量合成语音;Suno、Udio 等音乐生成平台为创作者甚至普通用户提供从文案到完整音乐的能力;游戏、视频、虚拟主播和数字人依赖这些模型进行配音和歌唱,极大降低了内容制作的门槛。 + +* **场景** + * 文本转语音(TTS):新闻播报、导航播报、智能客服语音回复、学习类 App 朗读内容、无障碍读屏等,需要将任意文本转换为自然、清晰、可控的语音。 + * 语音转换 / 语音克隆(VC / Voice Cloning):在保持语义和韵律的前提下,改变说话人音色,实现“换声说话”或“少样本声纹克隆”(在严格合规条件下)。 + * 音乐与音效生成:为短视频、游戏、广告、播客等生成合适的背景音乐与音效(环境声、UI 声效、过场音)。 + * 歌声合成与翻唱:给定旋律与歌词,让虚拟歌手演唱,或在合规前提下生成某种风格/音色的翻唱版本。 +* **原理** + 语音与音频生成通常采用**“高层表示 → 低层波形”** 的分层建模思路: + * TTS 中,先将文本转为音素/音节/字级序列,再通过序列到声学特征(如梅尔谱)的模型(Tacotron、FastSpeech、VITS 等),最后用神经声码器(WaveNet、WaveRNN、HiFi‑GAN 等)从特征生成高保真波形。 + * Voice Conversion 中,通过解耦“说什么(内容)”与“谁在说(音色)”,从源语音提取内容表示,再与目标说话人嵌入或声码条件结合,生成新的语音波形。 + * 音乐与音效生成可基于 token 化的表示(如音符、MIDI、编码后的频谱/codec token),采用自回归、扩散(Diffusion)或神经 codec 生成模型,从文本、参考音频或结构参数中采样出新音频。 + * 歌声合成在 TTS 的基础上引入更精细的韵律、音高轨迹和歌唱控制,通常对音高、时值、连音、颤音等有显式或隐式建模。 +* **模型** + 当前语音与音频生成的主流技术路线包括: + * TTS:Tacotron / Tacotron2、FastSpeech 系列(非自回归 TTS)、VITS 等负责从文本到梅尔谱或 codec token;WaveNet、WaveRNN、HiFi‑GAN、WaveGlow 等作为 vocoder 或解码器负责从特征到波形。最近的 Diffusion‑based TTS 和 Neural Codec 模型在自然度和多样性上进一步提升。 + * Voice Conversion / Cloning:基于 speaker embedding + content encoder 的 VC 框架,以及利用神经 codec 的语音转换模型,支持少样本音色克隆和跨语言说话人迁移。这类技术目前已被多家平台商用落地,提供便捷的语音克隆调用服务,国内常见平台包括火山引擎、minimax、科大讯飞开放平台、百度智能云千帆大模型平台、阿里云智能语音交互平台等;海外则有 ElevenLabs、Resemble.ai、Play.ht 等主流平台。其中,火山引擎的语音克隆能力支持少量音频样本快速训练,适配智能客服、有声读物等多场景的商用调用;minimax 则依托其大模型技术优势,实现了克隆音色与文本内容的自然适配,同时支持跨语言的说话人音色迁移;科大讯飞开放平台的语音克隆在中文发音的清晰度和情感表现力上具备显著优势,广泛服务于教育、广电等领域。 + * 音乐与音效生成:MusicLM、MusicGen、以及 Suno / Udio 类模型,通常基于文本和/或参考音频条件,使用自回归或扩散架构在离散 codec token 上生成长时音频。 + +### 4.4.1 文本转语音(TTS):让机器“自然开口说话” + +**文本转语音(TTS)**是最直观的语音生成任务:输入一段文本,输出一段自然流畅的语音,理想状态下可以与人声几乎难以区分。现代 TTS 系统通常分为两个主要阶段:文本到声学特征(如梅尔频谱),以及声学特征到波形。 + +在第一个阶段,模型需要处理分词、音素化、多音字消歧、标点与停顿、韵律预测等问题。典型模型包括基于注意力的 Tacotron 系列和基于长度预测的 FastSpeech 系列,后者通过非自回归架构显著加速合成、提升稳定性。近年来,VITS 等端到端模型将声学建模和声码器融合在一个统一框架中,进一步简化了系统。 + +在第二个阶段,神经声码器(Neural Vocoder)如 WaveNet、WaveRNN、HiFi‑GAN、WaveGlow 等负责将梅尔谱或其他中间表示转换为高保真波形。训练良好的声码器不仅可以生成自然清晰的语音,还能很好地还原不同音色、情感和风格。现代 TTS 系统还支持 **多说话人建模** (通过 speaker embedding)、音色/语速/情绪控制(如“兴奋”“平静”“播音腔”),以及跨语种 TTS,为各类应用提供高度定制化的声音能力。 + +### 4.4.2 语音转换与声纹克隆:改变“谁在说” + +在很多创作和辅助场景中,我们希望在**不改变内容与韵律**的前提下,改变说话人的音色或风格,这就是**语音转换(VC)**和**语音克隆(Voice Cloning)**的任务。前者主要解决“把 A 的话变成 B 的声音”;后者则进一步强调“少样本甚至几句语音就能学到新的音色”。 + +技术上,VC 通常采用“内容–音色解耦”的思路:通过一个内容编码器提取说话内容与韵律信息(可以是基于 ASR 的离散单位,也可以是自监督的连续表示),再通过一个条件生成器结合目标说话人嵌入或 codec 条件,生成目标音色但语义与节奏基本不变的新语音。如引入神经 codec,则可以在编解码空间直接编辑语音,实现高保真转换。 + +**语音克隆**在 VC 的基础上强调少样本与泛化能力:模型需要从几个样本甚至几秒音频中提取稳定的说话人表示,并据此生成风格一致、音色接近的合成语音。这一能力在虚拟人设、个性化助手、游戏角色定制、配音加速等方面非常有用,但也需要严格遵守法律与伦理规范,确保只在合规授权、充分知情和安全控制的前提下使用,避免滥用或身份冒充风险。 + +### 4.4.3 音乐与音效生成:从提示到完整声景 + +相比语音生成,**音乐与音效生成**在结构与时间尺度上更为复杂:音乐往往持续时间更长,内部结构(段落、旋律、和声、节奏)更加丰富;音效则种类繁多,从自然环境(雨声、风声、海浪)到拟声(UI 点击、提示音、游戏技能音效)都有各自模式。近年来,基于神经 codec、序列建模和扩散的模型使得“从文本生成完整音乐/音效”成为现实。 + +在音乐生成中,像 MusicLM、MusicGen、Suno、Udio 等模型通常将音频编码为离散的 codec token 序列,再在这一离散空间上训练文本条件或多模态条件的生成模型。用户只需提供一段文本描述(如“节奏适中、温暖治愈的 Lo‑Fi 背景音乐,适合学习专注”“紧张的电子管弦配乐,适合科幻预告片”),或上传一段参考音乐片段,模型就能生成长度达几十秒甚至数分钟的高质量音乐。对于创作者,这既是灵感来源,也是快速打样和背景音乐生成的利器。 + +在音效生成上,类似的技术可以根据文本提示生成 UI 声效、通知音、游戏环境声等,帮助产品与游戏团队快速迭代声音设计。结合前一层的音频理解能力,还可以做到风格对齐与场景自适应,例如根据画面或游戏关卡自动匹配音效风格。 + +无论是语音还是音乐与音效生成,这一层能力都在快速演进:从早期合成味浓重的机器音,到现在与人声、专业音乐难以区分的高保真内容。与此同时,围绕版权、合规、溯源和可控性的问题也变得尤为重要——如何在提供强大创作工具的同时,保护创作者和使用者的合法权益,将是这一层技术持续需要面对的关键议题。 + +# 5. 视频(Video) + +在多模态 AI 体系中,**视频模态**负责理解和生成“随时间变化的视觉信号”。相比单帧图像,视频不仅包含空间维度上的纹理、形状和布局信息,还携带丰富的 **时间维度线索** :动作的起落、物体的运动轨迹、镜头的切换节奏等。无论是安防监控中的行为识别、体育训练中的动作分析,还是短视频平台的一键剪辑、长视频的智能解析,本质上都依赖于一整套围绕“帧序列”展开的理解与生成能力。 + +从工程视角看,视频能力大体可以分为几层:**底层的视频增强与复原**负责保证“能看清”;**视频理解与结构分析**负责回答“发生了什么”;在此基础上,**视频 + 语言多模态任务**将视频内容转化为文本可用的结构化描述和检索接口;进一步的,**视频生成与编辑**则反过来从文本或示例视频出发,用可控的方式生成或重组视频内容;而以**数字人 / 虚拟人**为代表的一类应用,则将语音、语言、动作和视频渲染综合在一起,构成面向交互与内容生产的新形态。 + +下面我们同样从分层能力出发,对视频相关能力进行梳理。 + +## 5.1 传统视频处理:从“能播”到“好看、好用” + +在视频技术的最底层,我们首先关心的,并不是“画面里是谁”“发生了什么事件”,而是这段视频本身是否稳定、清晰、舒适:画面抖不抖、糊不糊、噪点多不多、比例是否适合目标终端播放。**传统视频处理**这一层,主要在帧序列和时空像素层面工作,通过增强、修复、超分辨率、插帧和重定帧等操作,把嘈杂、抖动、分辨率不足或比例不合适的原始视频,转换为更适合观看和后续分析的“高质量时序信号”。可以把它类比为图像模态中的“图像复原与增强 + 几何校正”,只不过这里额外引入了时间维度上的平滑与一致性。 + +从产品角度看,这一层能力几乎“隐身”在所有视频产品背后:剪辑软件的一键画质增强、短视频平台的自动画质升级、电视盒子和播放器的智能超分与插帧、老影片修复服务,以及给上游检测/识别模型做的多帧预处理,都是传统视频处理的直接体现。下面依然从 **场景** 、**原理**和**模型**三个角度来梳理,并在后续小节中展开视频增强与修复、超分与插帧几个关键方向。 + +* **场景** + 在线视频平台、剪辑工具、监控系统和终端设备中,传统视频处理主要出现在以下典型场景: + * 内容平台与剪辑工具:短视频、长视频在上传或编辑时,通过一键画质增强、稳像、防抖、降噪,让用户“拿起手机就能拍、拍完就能用”;老视频素材在导入剪辑工程时,通过修复和补帧,使其与新素材在观感上更一致。 + * 影视与老影片修复:对历史胶片、早期电视节目和标清素材进行数字修复,去除划痕、噪点和抖动,恢复色彩和细节,为重映、再发行和数字档案保存提供更高质量的版本。 + * 视频监控与行车记录:对弱光、雨雾、压缩严重的监控画面进行降噪、去雾、增强对比度和稳像,提升后续检测和识别模块的鲁棒性,便于取证和溯源。 + * 终端播放与设备侧增强:电视、机顶盒、手机播放器本地集成超分和插帧功能,将存量的 720p/1080p、24/30fps 内容在播放端“升级”为近似 4K、60/120fps 的视觉效果。 + * 多终端适配与分发:为同时覆盖手机竖屏、平板横屏和大屏电视,对同一视频进行横竖屏适配、智能裁剪和多比例重定帧,减少手工剪辑和多版本维护成本。 +* **原理** + 传统视频处理通常不直接理解语义类别,而是围绕画质、稳定性和时间一致性在时空信号层面做建模和优化: + * 时空联合建模:在单帧图像增强的基础上,引入时间维度的信息,通过光流估计、相机运动建模或时空卷积,把前后帧作为额外“观测”,在时间轴上做多帧融合与噪声抑制。 + * 稳像与防抖:将相机抖动建模为一段时间上的几何变换序列(平移、旋转、缩放等),通过估计全局或局部运动轨迹,将其平滑后重新投影到输出视频中,从而达到去抖和稳定的效果。 + * 视频超分与插帧:视频超分通过多帧对齐和细节重建,在提升空间分辨率的同时保持时间一致性;插帧则通过光流估计或时空生成网络,在两帧之间合成中间帧,用更高帧率呈现运动,提高流畅度。 + * 重定帧与自动构图:通过检测和追踪视频中的主体(人物、物体),在时间轴上估计主体轨迹,再结合目标分辨率的长宽比,为每一帧选择合适的裁剪窗口,并对裁剪窗口的运动进行时间平滑,保证观感自然。 + * 质量与效率权衡:在云端离线处理可以追求最优画质和复杂模型,而在手机、播放器和实时场景中则需要控制模型参数量、计算复杂度和延迟,在算法结构和推理框架上做精细折中。 +* **模型** + 在具体实现上,传统视频处理综合使用经典视频信号处理方法和深度学习模型,在效果、效率与部署形态之间寻找平衡: + * 经典视频处理方法:基于光流的稳像与插帧、时域滤波与多帧融合、基于块匹配的去噪和去压缩伪影等,仍然广泛应用于算力受限或对可解释性有要求的场景。 + * 深度视频复原与增强模型:以 EDVR、BasicVSR / BasicVSR++、Real‑ESRGAN 视频版等为代表的多帧超分与增强网络,通过对齐与时空特征聚合,在去噪、去模糊、细节恢复和去压缩伪影方面显著优于传统方法。 + * 深度插帧模型:如 DAIN、RIFE、FILM 等插帧网络,通过显式或隐式光流估计与中间特征融合生成中间帧,相比传统光流 + 重采样方法在复杂运动和遮挡场景中更稳定。 + * 基于 Transformer 的视频复原:利用时空注意力统一处理空间纹理与时间依赖,在复杂镜头运动、多物体场景下具备更强的建模能力,同时在推理时通过稀疏注意力、滑动窗口等机制控制计算量。 + * 实际产品与系统:剪映 / CapCut 的智能增强、Topaz Video Enhance 等商用增强软件,B 站及各短视频平台的画质增强管线、老影片修复 SaaS 服务等,通常会将多种模型与策略级联,按素材类型和终端条件动态选择最优处理路径。 + +综合来看,这一层更多是在“语义之前”为视频打好物理与感知基础:既帮助用户获得更舒适的观感,也为上游检测、识别和生成模型提供更干净、更稳定的输入。下面,我们分别从 **视频增强与修复** 、**超分辨率与插帧等**子方向展开。 + +### 5.1.1 视频增强与修复:把“能看”打磨到“好看” + +在真实拍摄条件下,视频往往并不“干净”:手持设备造成的剧烈抖动、弱光下的高噪点和涂抹感、网络压缩带来的块状伪影和色带、老旧设备录制的褪色和划痕,都让视频质量明显低于理想状态。视频增强与修复的目标,就是在不改变视频语义内容的前提下,最大程度恢复稳定、清晰、自然的观感,把“勉强能看”的素材打磨到“看起来顺眼甚至好看”的水准。 + +在时域上,增强与修复首先要解决的是稳定性问题。通过对连续帧进行特征匹配或光流估计,可以分离出全局相机运动和局部物体运动,再利用平滑后的相机轨迹重新渲染输出帧,从而抑制快速抖动与微小晃动,避免观众在观看过程中产生眩晕感。在此基础上,画面级的去噪、去模糊和去伪影则更多集中在空间–时间联合建模:多帧联合去噪利用前后帧冗余信息,在时间方向上进行类似“多曝光融合”的处理,在保留细节纹理的同时有效抑制高 ISO 噪声和压缩噪声;对轻微运动模糊,则通过估计模糊核或使用端到端深度网络,在帧序列上进行反卷积式的清晰化处理,使静态背景和运动主体都更锐利。 + +对于老影片和低质量素材,修复还涉及色彩和结构层面的“重建”。胶片老化会导致画面泛黄、对比度下降、局部划痕和污点显著,早期数字视频则常见分辨率低、压缩严重和边缘锯齿等问题。现代修复流程往往采用多步协同:先利用检测和分割模型定位划痕、污点等局部损坏区域,再通过时空补全网络在邻近帧和邻近空间像素中“借料填坑”;同时进行色彩还原和对比度重塑,使整体色调接近原始拍摄或设定的风格参考。对于严重压缩的视频,还会引入针对块效应和振铃伪影的专用去伪影网络,在不过度平滑的前提下改善边缘和细节。 + +这些增强与修复能力在产品中的体现往往是“一键式”的:用户只需勾选“稳像”“画质增强”或“老视频修复”,系统便会在后台自动选择合适的模型和参数组合,对视频帧序列做多阶段处理。对业务而言,这一层既直接决定了观众对画质的主观评价,也间接影响上游分析模型的表现:更干净、更稳定的视频输入,往往意味着更可靠的人脸/车牌识别、更准确的行为检测和更少的误报。 + +### 5.1.2 超分辨率与插帧:从“能看清”到“更流畅” + +在显示设备不断升级、用户对细节和流畅度要求不断提高的背景下,大量存量视频内容在分辨率和帧率上显得“先天不足”:1080p 在 4K 屏幕上显得不够锐利,24/30fps 在大屏和快速运动场景中容易出现拖影或卡顿感。超分辨率与插帧技术正是为了解决这两个问题:前者在空间维度上“补细节”,后者在时间维度上“补过程”,共同把“勉强能看清”的视频提升为“细节丰富、播放顺滑”的观感。 + +视频超分辨率相比单帧图像超分多了一个关键维度:时间。简单的逐帧放大容易导致相邻帧细节不一致,出现闪烁和纹理抖动。因此,主流方法都会利用前后多帧的信息,通过光流估计或特征级对齐,将邻近帧中的细节对齐到目标帧上,再在对齐后进行细节重建。像 EDVR、BasicVSR / BasicVSR++、Real‑ESRGAN 视频版等模型,会先在特征空间对多帧进行对齐和聚合,再用深度网络推断高分辨率细节,避免简单插值带来的“糊”和“塑料感”。在这一过程中,如何在“物理合理”和“感官好看”之间平衡,是损失设计和训练策略的核心:既要提升客观指标(如 PSNR、SSIM),也要保证主观观感自然,没有过度锐化和伪细节。 + +插帧则聚焦在时间轴上的“补帧”。传统方法依赖光流估计,先预测前后两帧之间每个像素的运动,再按照一定规则在中间位置插值生成新帧。然而在快速运动、多物体遮挡或纹理复杂区域,光流往往不够准确,容易出现拖影、重影或局部形变。深度插帧模型如 DAIN、RIFE、FILM 等,通过端到端网络同时学习光流、深度或中间特征的融合策略,直接输出插值帧,在复杂场景下的稳定性和视觉质量明显提升。对于体育赛事、动作游戏录屏和慢动作创作,插帧可以将 24/30fps 的原始视频平滑提升到 60/120fps,既保留运动细节,又减少卡顿和残影。 + +在工程实践中,超分和插帧常常结合使用:对低分辨率、低帧率的存量内容先做时序插帧,再进行空间超分,或两者在统一的时空网络中一体化实现。部署形态上,云端离线处理适合对画质要求极高的影视修复和平台级“画质升级”服务,而端侧实时推理则更多见于电视盒子、播放器 App 和游戏/运动相机中,需要通过模型压缩和硬件加速保证低延迟。无论以何种形态呈现,超分与插帧已经成为“高清/超高清体验”的重要基建,使旧内容在新终端上焕发“第二春”。 + +## 5.2 视频理解与结构分析(Video Understanding) + +如果说传统视频处理更多停留在“画质与稳定性”层面,那么**视频理解与结构分析**则开始回答“视频里在发生什么”这一类语义问题:谁在做什么、在哪里做、持续了多久、是否存在异常行为等。这里的目标,是在时间轴上对视频进行结构化拆解:识别动作与行为、检测与跟踪目标、分割前景与背景、划分场景与镜头,并抽取出可供下游决策、检索与告警使用的高层语义信号。 + +从产品视角看,这一层能力已经深入到各类智慧安防平台、运动训练分析系统、智能行车记录仪和工业质检视频分析系统中:在监控中识别打架、摔倒、徘徊等异常;在体育和健身场景中分析动作规范性和技术细节;在交通与工业环境下追踪车辆和人员轨迹、监控生产流程是否正常。下面依然从 **场景** 、**原理**和**模型**三个角度梳理这类能力,并在后续小节中重点展开几个代表性方向。 + +* **场景** + * 安防与公共安全:在城市监控、园区和楼宇中,识别打架、摔倒、聚集、奔跑、翻越围栏等行为,对徘徊、深夜逗留等异常模式提前告警。 + * 交通与出行:对行人、车辆、自行车在路口、隧道和高速上的轨迹进行检测和追踪,分析闯红灯、逆行、占道、超速等行为,为交管和事故溯源提供依据。 + * 体育与运动训练:分析篮球投篮、网球发球、瑜伽体式等动作的关键阶段与姿态质量,为运动员和大众用户提供技术分析和纠错建议。 + * 工业生产与质检:监控生产线上的作业步骤是否规范,检测装配过程中是否存在漏装、错装或异常动作,为安全生产和良率提升提供基础数据。 + * 内容结构化与检索:对长视频进行镜头拆分、场景分类和重要片段标记,为后续检索、推荐和剪辑提供结构化索引。 +* **原理** + 视频理解与结构分析的关键,是在时间维度上对空间目标和语义进行联合建模: + * 动作识别与行为分析:基于 2D/3D 卷积、时序池化或 Transformer,对一段视频片段进行整体编码,识别其中发生的动作类别;进阶方法结合人体关键点序列与骨架拓扑,更细粒度地分析动作质量与模式。 + * 目标检测与追踪:在每一帧上做检测的同时,引入跨帧关联机制(外观特征、运动轨迹等),将同一目标在不同时刻的检测框串联为连续轨迹,得到多目标跟踪结果。 + * 视频语义分割与场景分析:在像素级别上对视频中的每一帧进行语义分割或实例分割,并利用时间连续性平滑预测;同时对镜头切换和场景边界进行检测,实现长视频的结构拆解。 + * 高层事件与异常检测:在基础的动作与轨迹特征之上,利用时序建模和模式识别方法,对罕见事件和异常模式进行检测,往往结合无监督或弱监督学习缓解标注稀缺问题。 +* **模型** + 在模型选择上,视频理解与结构分析通常采用“空间特征 + 时间建模”的组合架构: + * 基于 3D 卷积和 Two‑Stream 的经典模型,如 I3D 等,通过在空间和时间维度同时卷积,对短视频片段进行端到端动作识别。 + * 基于多路径与多时间尺度的 SlowFast 系列模型,通过慢路径捕捉语义、快路径捕捉运动细节,在计算量和精度之间取得更好平衡。 + * 基于 Transformer 的视频模型,如 TimeSformer、Video Swin Transformer 等,利用时空注意力机制对长时间范围的视频进行建模,更适合捕捉复杂事件和多主体互动。 + * Tube‑based 检测器与时空卷积 / Transformer 模型,将检测框在时间上扩展为“tube”,在空间–时间联合特征上做行为检测与时空分割。 + * 多目标跟踪(MOT)方法,如 DeepSORT 等,将帧级检测结果与外观嵌入、运动预测结合,在视频中稳定关联目标身份。 + +整体上,这一层能力把视频从“高质量像素流”进一步抽象为“行为与事件流”,为上游的多模态理解、检索与决策奠定结构基础。下面,我们从 **动作识别与行为分析** 、 **目标检测与追踪** 、**事件与异常检测**三个方向展开。 + +### 5.2.1 动作识别与行为分析:从帧序列到“谁在做什么” + +动作识别与行为分析关注的是“在一段时间窗口内,主体在做什么事”。在安防场景中,这意味着从视频中识别出“走路、奔跑、摔倒、打架”等行为;在体育和健身中,则对应“投篮、发球、深蹲是否标准”“瑜伽体式是否到位”等更细粒度动作。技术上,早期方法主要依赖 2D 卷积 + 光流或手工特征,将若干帧堆叠后整体分类;现代方法则更多采用 3D 卷积(I3D、一系列 3D ResNet 变体)、SlowFast 这类多时间尺度结构,或 TimeSformer、Video Swin Transformer 等基于时空注意力的模型,对空间纹理与时间变化进行联合建模。 + +在许多需要高精度姿态分析的场景中,直接对 RGB 片段分类并不足够,还会结合人体姿态估计和骨架序列建模:先从每一帧中提取 2D/3D 关键点,再将关键点序列送入 RNN、时序卷积或 GCN/Transformer 网络,分析动作的时序结构和空间协调性。这种“姿态先验 + 时序建模”的方式,对背景、光照和服装变化更鲁棒,适合瑜伽、健身、工业操作规范性评估等对动作细节要求较高的应用。 + +### 5.2.2 目标检测与追踪:从“这一帧在哪”到“整段轨迹” + +单帧目标检测可以告诉我们“这一帧里有哪些目标、在哪儿”,而现实中的许多任务需要的是“这辆车 / 这个人从哪里来、到哪里去、中间做了什么”。目标检测与追踪模块正是为了把帧级检测串成时间上的连续轨迹:一方面在每一帧上运行检测器,给出候选目标框;另一方面基于外观特征(ReID 嵌入)、运动预测(卡尔曼滤波)和空间重叠等线索,将相邻帧上的框进行匹配与关联,得到多目标跟踪(MOT)结果。 + +在工程实践中,一个典型的流水线是:“强健的行人 / 车辆检测 + DeepSORT 一类的关联算法”,部署在监控或行车记录仪上,实时输出每个 ID 的运动轨迹。在更复杂的系统中,这些轨迹还会结合区域语义(车道、区域划分)与业务逻辑规则,进一步推断逆行、长时间逗留、频繁进出等高层行为模式,为上游安防、交通流量分析和工业流程监控提供连续时序信号。 + +### 5.2.3 事件与异常检测:从“常态模式”中找出“不对劲” + +在大部分业务场景中,真正需要重点关注的往往是“少数异常”和“关键事件”:例如安防中的打架、摔倒、聚集,工业生产中的异常停机或违规操作,交通中的危险驾驶行为等。这类事件相对罕见,标注成本高、样本极不平衡,给模型建构带来了额外挑战。 + +常见的做法,是在基础的动作识别、目标跟踪和场景分割之上,构建一个时序异常检测模块:要么通过有监督方式直接学习少量已标注的异常样本;要么采用无监督/弱监督方法,对“正常模式”的运动与行为分布进行建模,一旦新观测与历史分布明显偏离,就发出告警。在模型层面,会结合时序自编码器、对比学习、图神经网络或时序 Transformer,将空间关系和时间依赖统一编码,从而捕捉更复杂的群体行为模式和长程依赖。 + +## 5.3 视频 + 语言多模态任务(Video‑Language) + +如果说视频理解解决的是“视频本身理解清楚了”,那么**视频 + 语言多模态任务**关注的是“如何用自然语言去描述、问答、检索视频内容”,以及“如何在长视频时间轴上,围绕文本需求快速定位关键信息”。这类任务需要同时处理视觉、语音与文本信号:一方面提取视频中的画面与声音特征,另一方面对接语言模型的推理与生成能力,把时空内容压缩成适合人类消费和机器调用的文本摘要、问答结果与语义索引。 + +从产品视角看,这一层能力已经深入长视频自动生成字幕与时间轴、短视频剪辑平台的“智能打点 / 关键片段抽取”、企业培训和会议视频的问答助手等场景:用户不必再“从头看到尾”,而是可以通过自然语言直接对视频内容进行检索、提问和重组。下面依然从 **场景** 、**原理**和**模型**三个角度展开。 + +* **场景** + * 字幕与摘要生成:对课程、演讲、会议和长视频内容自动生成多语言字幕,并在此基础上生成章节级摘要、看点列表与时间轴。 + * 视频问答与知识访问:对教学视频、操作演示、企业培训内容构建“视频问答助手”,支持用户用自然语言提问,如“这个步骤怎么做”“这个人最后把手机放哪了”。 + * 视频内容检索与片段定位:在大规模视频库中支持“文字 → 视频片段”的精确检索,例如“找出提到价格的部分”“找到讲解某个公式的片段”;在单个长视频内自动打点标注精彩片段与关键信息。 + * 内容生产与编辑辅助:结合视频内容理解与语言生成功能,自动生成标题、文案、分镜脚本,辅助创作者快速剪辑和重组素材。 +* **原理** + 视频–语言多模态系统的核心,是在统一嵌入空间中对齐时序视觉特征与文本表示,并在这一基础上进行检索、生成与推理: + * 多模态特征抽取与对齐:对视频帧/片段提取时空特征(CNN/ViT/Video Transformer),对文本提取语言嵌入(预训练 LLM 或文本编码器),通过对比学习或多模态预训练对齐两种模态。 + * 语音与文本管线:对包含语音的内容,通常先用 ASR 生成时间戳对齐的转写文本,再与视觉特征联合建模,既可以用文本直接驱动检索,也可以做跨模态对照与纠错。 + * 时间建模与片段定位:对于长视频,需要在时间轴上学习“片段级”表示,通过注意力或时序 RAG 在局部片段和全局上下文之间动态切换,实现对问题相关区间的精确定位。 + * 生成与推理:在对齐后的多模态表示上接入大语言模型,进行自然语言生成(字幕、摘要、解释),或进行多轮问答与逻辑推理。 +* **模型** + 在模型形态上,视频–语言多模态任务经历了从“专用编码器 + 简单头”到“统一多模态大模型”的演进: + * 早期视频–语言模型:如 VideoBERT 等,在预训练阶段联合建模视觉与文本 token,通过掩码预测和对比学习获得可迁移的视频–语言表征。 + * All‑in‑One Video‑Language Models:将视频、文本(及语音)统一纳入一个多模态 Transformer 中,通过共享或部分共享参数,实现描述生成、检索、QA 等多任务统一处理。 + * 长视频多模态模型:如具备视频能力的 Gemini、Claude、GPT 等,通过长上下文与分层时序建模,对数十分钟乃至数小时视频进行整体理解,支持时间轴级别的摘要与问答。 + * 时序 RAG + VLM:在视频上构建“时序向量索引”,先用 VLM 对视频片段进行编码建立数据库,再在查询时检索相关片段,结合 LLM 进行答案综合与可解释推理。 + +总体来看,这一层将视频从“机器理解”进一步提升到“人机对话与协作”层面:用户可以像问人一样向视频提问,系统则在背后完成复杂的视觉、语音与语言对齐与推理。 + +### 5.3.1 字幕、摘要与时间轴:把长视频压缩成可浏览文本 + +对于课程、讲座、会议和长内容视频,最迫切的需求往往是“快速知道讲了什么、哪里是重点”,而不是从头到尾完整观看。自动字幕与摘要系统通过“ASR + 文本处理 + 视觉辅助”的组合,将音频内容转写为时间戳对齐的文本,再在此基础上生成结构化大纲与精简摘要,实现从“小时级视频”到“分钟级阅读”的信息压缩。 + +在实现层面,ASR 模块负责稳定、高质量地给出多语言转写和时间轴对齐;文本侧则利用大语言模型对原始转写进行纠错、分句和语义重整,提取章节标题、关键信息和问题–答案对。在一些场景中,还会结合视觉线索(如 PPT 页面变化、场景切换)来辅助划分章节边界与重点片段,保证摘要结构与真实内容节奏更加一致。 + +### 5.3.2 视频问答与语义检索:用自然语言“操纵”视频 + +在字幕与摘要之上,更进一步的需求是能够针对特定视频内容进行问答和检索:例如“这个人最后把手机放在哪里”“哪一段讲到了价格策略”“演示这个步骤的是第几分钟”。这类任务需要在时间轴上对问题进行语义定位:既要理解问题本身涉及的人物、物体和动作,也要在视频时序表示中找到对应的片段。 + +具体做法上,通常会先离线为视频构建多粒度索引:对固定长度的片段提取多模态表示(画面 + 文本/语音),建立向量索引或图结构。在在线交互时,将用户问题编码为文本向量,与索引中的片段表征进行匹配,找出最相关的时间区间;随后,将这些片段的内容(关键帧截图描述、转写文本等)与问题一起送入 LLM,由模型生成自然语言答案或返回对应时间点。对于大规模视频库,可以在相同机制下支持“跨视频检索”,例如在企业培训知识库或电商商品视频中跨集合查找相关片段。 + +### 5.3.3 多模态编辑辅助:从理解到“帮你剪好” + +当系统能够稳定地理解视频中的内容和语义结构后,自然的下一步就是反向利用这些理解结果来辅助创作与编辑。视频–语言多模态模型可以根据创作者提供的脚本或提示词,在现有素材中自动选取符合语义的片段,生成粗剪时间线;也可以根据视频内容自动生成标题、封面文案、章节标签,甚至对镜头节奏和配乐提出建议。 + +在工作流中,这类能力通常以“智能推荐”和“自动粗剪”的形式出现:创作者上传素材后,系统自动完成分析、分镜、打点,并给出若干候选版本(如不同节奏、不同时长的剪辑方案);创作者可以在此基础上微调,而无需从零开始逐帧筛选。对于企业级应用,系统还可以结合知识库和品牌规范,确保生成的文案、字幕和剪辑风格符合既定的业务要求和合规标准。 + +## 5.4 视频生成与编辑(Video Generation & Editing) + +在拥有了稳定的理解和结构分析能力之后,**视频生成与编辑**则迈向了“主动创造内容”的阶段:不再只是提升画质或做结构化分析,而是根据文本脚本、参考图像或已有视频,生成全新的镜头,或对原始视频进行结构化编辑与重组。这里既包括从无到有的文生视频(Text‑to‑Video),也包括基于已有图像/视频的风格迁移、扩展与重排,以及面向对象级别的精细编辑与替换。 + +产品上,这一层能力已经通过即梦视频、 minimax 视频、Sora、Runway Gen‑2、Pika、Kling 等一系列产品进入内容创作主流:广告片、概念片、动画、剧情分镜可以在不依赖大型拍摄团队和复杂后期的情况下快速生成;创作者可以通过自然语言脚本驱动镜头和风格;传统的视频剪辑流程则开始与结构化生成工具深度融合。下面依然从 **场景** 、**原理**和**模型**的角度进行梳理。 + +* **场景** + * 文案、剧本到短视频:品牌广告、小剧场、剧情片段和概念动画,根据脚本自动生成或半自动生成可播放的视频草稿。 + * 图像 / 视频到视频:为插画或角色设计生成动态版本,为现实拍摄素材进行风格迁移(现实 → 动漫 / 插画),或在时间与空间上扩展/重组已有视频。 + * 结构化编辑与后期:在不改变整体内容语义的前提下,实现人物换脸、口型同步、对象擦除与替换、文本驱动的剪辑重排等精细操作。 +* **原理** + 当前主流视频生成与编辑方法多以扩散模型(Diffusion)或其变体为核心,在高维的时空潜空间中逐步“去噪”生成视频: + * 文本条件建模:通过文本编码器(如 T5/CLIP 文本塔或专用语言模型)将脚本映射为条件向量,引导视频解码器在风格、内容和运动模式上对齐文本描述。 + * 时空一致性与运动控制:在扩散过程或后验优化中加入时空卷积、时序注意力或 4D 表达(NeRF/GS 等),保证视频在时间轴上的连贯性与物理合理性。 + * 图 / 视频条件生成:在输入图像或视频的特征空间上启动扩散过程,通过控制噪声注入、遮罩区域和条件通道,实现“保留已给部分 + 生成新内容”的受控编辑或扩展。 + * 结构化控制信号:结合姿态骨架、分割掩膜、深度图、相机轨迹等结构信息,使生成视频在主体动作和视角变化上更可控。 +* **模型** + 代表性的模型与方向包括: + * Diffusion‑based Text‑to‑Video 模型(Sora、Runway Gen‑2、Pika、Kling 等),通过大规模视频–文本对进行预训练,在复杂场景、多镜头运动和多样风格上具备较强生成能力。 + * Image‑to‑Video 扩散模型:以单帧图像为条件,预测后续帧的动态演化,实现“单图 → 动画 / 动效”;或对短视频进行续写、扩展、旋转视角等操作。 + * NeRF / 4D 表达与关键帧 + 插值方法:利用 3D 场景表示或关键帧 + 时序插值,将生成与几何、一致性建模结合,实现更稳定的视角漫游与复杂运动。 + +这些能力并非孤立存在,而是逐步渗入剪辑与后期流水线:文案到分镜、分镜到粗剪、粗剪到风格化与局部编辑,越来越多环节被“文本 + 结构化控制”所驱动。 + +### 5.4.1 文生视频:从脚本到“可看”的镜头序列 + +文生视频(Text‑to‑Video)希望实现的是:用户用自然语言描述一个场景、镜头或故事片段,系统自动生成一段连贯的视频。与图像生成相比,文生视频增加了时间维度的难题:不仅要在单帧层面保持画面质量和风格一致,还要保证跨帧的主体身份、光照、背景和运动轨迹的连贯性。 + +典型的扩散式文生视频模型会先在大规模视频–文本配对数据上预训练:文本编码器提取语义条件,视频解码器在潜空间中对一段“噪声视频”反复去噪,逐渐收敛到与文本一致的时空信号。在此过程中,会通过时序注意力、3D 卷积或 4D 表达等结构,将时间依赖显式建入网络,以避免出现“帧间跳变”“角色重置”等问题。部分系统还支持对镜头运动(推拉摇移)和构图节奏进行控制,使生成结果更接近真实拍摄语言。 + +### 5.4.2 图 / 视频到视频:在已有内容上“生长”与“变形” + +另一条重要路线是基于已有图像或视频进行生成与编辑:例如,将一张插画或概念设定图“动起来”,将真人视频风格化为动漫,或在保持结构不变的前提下更换背景、调整天气和时间。技术上,这类方法往往在扩散过程上增加“参考通道”:将输入图像或视频编码为特征,作为条件或初始状态参与去噪,同时通过遮罩、显式几何约束等机制控制“哪些区域可以被改变、哪些必须保持”。 + +对于风格迁移场景,模型会在保留原始运动和构图的前提下,重绘纹理和光影,使其匹配目标风格;对于视频扩展与重组,则通过在时间两端或中间“续写”新帧,实现水平/垂直场景扩展、视角绕行或情节补充。这类能力非常适合与传统剪辑流程结合:剪辑师先给出关键镜头和节奏,模型再在这些“锚点”之间自动生成过渡和变体。 + +### 5.4.3 结构化视频编辑:对象级的精细控制 + +在许多业务场景中,完全重生视频并非刚需,更关键的是对已有画面进行精细、可控的结构化编辑:比如换脸、改口型、擦除不需要的物体、替换广告位内容,或者根据文本脚本重排镜头顺序。结构化视频编辑正是沿着这一思路发展:在视频理解的基础上,引入对象级分割、跟踪和参数化表示,使编辑操作可以稳定绑定到特定目标和时间段。 + +人物换脸和口型同步(Lip‑sync)是这一方向中最典型的应用:模型需要在保证头部姿态与整体表情自然连贯的前提下,将目标人物的身份映射到原视频的表演上,并根据新语音信号精确控制口型运动。对象擦除 / 替换则依赖高质量的分割和时空补全:先在每一帧中分割并移除目标对象,再利用邻近帧与上下文纹理填补空洞,避免出现明显“打补丁”的痕迹。文本驱动剪辑则通过将“脚本结构”与视频时间轴对齐,自动选取和拼接符合脚本语义的片段,实现更高层的自动化编辑。 + +## 5.5 数字人 / 虚拟人(Digital Human / Avatar) + +**数字人 / 虚拟人(Digital Human / Avatar)** 可以看作是视频生成、语音合成、多模态理解和图形渲染的一次“系统级整合”:它不只是生成一段视频,而是基于文本或语音输入,持续、可控地驱动一个虚拟形象“开口说话、做表情、摆动作”,并在越来越多场景下实现准实时甚至实时的交互。相比一般的视频生成,数字人更强调三点: **身份与形象的长期一致性、语音—表情—动作的精细对齐、以及端到端系统的实时性与稳定性** 。 + +从产品视角看,数字人已经广泛出现在**内容生产平台、虚拟客服 / 智能前台 / 虚拟导览、教育培训与在线课堂、品牌虚拟 IP / 虚拟偶像、为创作者提供的虚拟主播 / 数字分身工具**等场景:企业可以批量生产带有固定形象和风格的视频内容,政府和企业服务可以用虚拟前台 7×24 小时接待用户,个人创作者可以完全不露脸但持续产出“有人出镜”的视频。下面依然从 **场景** 、**原理**和**模型**三个维度来梳理,并在后续小节展开驱动与表达、形象与视频生成、实时交互与系统集成三个方向。 + +* **场景** + * 内容生产与在线传播:企业宣传片、产品功能讲解、课程录制、新闻播报,使用数字人替代真人上镜,大量减少拍摄场地、灯光设备和人力成本。 + * 虚拟客服与导览:在银行网点、政务大厅、景区、博物馆等场所,用数字人承担迎宾、问询、业务咨询和路线指引,兼顾形象统一与 7×24 小时服务。 + * 品牌虚拟 IP / 虚拟偶像:围绕某一虚拟形象长期运营短视频、直播、电商内容,在不同平台上保持统一人设和视觉风格。 + * 虚拟主播与数字分身:为不愿出镜或需要多身份运营的创作者,提供可配置的虚拟主播 / 数字分身,与真实声音或合成声音绑定,实现“只用说话 / 打字,就能稳定出镜”。 +* **原理** + 数字人系统本质上是一个“语音 / 文本驱动 + 形象建模 + 视频 / 渲染输出”的多模态流水线,在离线与实时场景下略有差异,但核心组件相似: + * 语音与语言驱动:根据脚本直接用 TTS 合成语音,或接入 ASR + LLM,从用户语音 / 文本中生成回复文本,再用 TTS 输出语音;语音特征(如 mel 频谱)作为驱动信号控制嘴型与表情时间轴。 + * 形象与动作空间建模:为虚拟形象构建可控的几何与外观表示,例如 2D 人像 / 插画、基于骨骼和 Blendshape 的 3D Avatar、或基于 NeRF / 4D 高斯的可渲染体积表示;并定义一组“驱动参数”(如关键点、姿态骨架、Blendshape 系数),用来编码表情与姿态。 + * 语音 → 表情 / 动作映射:通过专门的“语音驱动”模型,将语音特征映射为人脸和上半身的驱动参数,实现口型同步(Lip‑sync)、表情细节和头肩动作;实时数字人会要求这一映射端到端低延迟且稳定。 + * 渲染与合成:根据当前帧驱动参数,对虚拟形象进行图像或 3D 渲染,输出连续视频流或实时画面;可叠加背景、道具、字幕等元素,与传统视频剪辑流程结合。 +* **模型** + 在具体模型上,数字人系统往往综合使用多类专用模型与通用多模态模型: + * Audio‑driven Talking Head 模型:如 Wav2Lip 一类的口型同步模型,通过学习语音与口腔区域像素 / 几何之间的对齐关系,在保证身份一致的前提下生成自然的嘴部运动。 + * 实时 / 轻量级数字人模型:如 Ultralight‑Digital‑Human、轻量级 Talking Head 模型等,在结构上大幅压缩参数与计算量,使得在 CPU / 移动端 / WebGPU 上也能实现接近实时的驱动与渲染。 + * NeRF / 4D 表达模型:如 ER‑NeRF(Explicit / Efficient / Editable 方向的数字人 NeRF 方案)等,通过在 3D 空间中建模人物形象与表情变化,使视角、光照和动作更自然连贯,适合高保真和多机位场景。 + * 语音驱动与多模态对齐模型:如 MuseTalk 一类“语音 → 面部表情 / 说话头”模型,将音频特征和视觉特征对齐,在不依赖大量 3D 标注的情况下实现逼真的讲话表情与头部动作。 + * 语音与对话模型:高自然度多说话人 TTS、端到端语音对话模型(ASR + LLM + TTS 一体化),为数字人提供多风格、多语种的声音和对话能力。 + +综合来看,数字人既是一组模型,也是一套完整系统:它将语言理解、语音、视觉生成与实时推理整合起来,从而在“屏幕前”呈现出一个可交互的虚拟角色。下面,我们从 **驱动与表达** 、**形象与视频生成**和**实时交互与系统集成**三个方向展开。 + +### 5.5.1 驱动与表达:从脚本 / 语音到“会说话、会表情”的人 + +在数字人流水线中,**驱动与表达**负责回答一个核心问题:在给定脚本或语音的前提下,虚拟形象在每一帧应该呈现什么样的嘴型、表情和头肩动作。这里既包括离线批量生产的场景,也包括对实时对话的响应。 + +在离线内容生产中,常见链路是“文本脚本 → TTS → 语音驱动”:业务侧提供播报文案,TTS 模块生成目标音色(如品牌虚拟代言人)的语音,再将语音特征输入到“语音 → 动作”模型。**Wav2Lip 类模型**就是这一环节的重要代表: + +* 它以参考人像帧和对应语音片段为输入,通过一个卷积 / 注意力网络预测出与语音精细对齐的嘴部区域,再与原始人像进行融合,从而在保持身份和大部分表情不变的前提下,精确修改嘴型。 +* 训练时,通过语音–视频对齐数据监督网络学会不同音素对应的口腔形态,并在时间上保持连续性,避免嘴型跳变或延迟感。 + +相比早期纯口型同步方案,新一代的语音驱动模型(如 MuseTalk 一类的方法)进一步扩展到了 **全脸表情和头部姿态** : + +* 这类模型通常将语音特征映射到一个低维的“情绪 / 表达潜空间”,再通过解码器生成关键点、Blendshape 系数或直接生成图像特征,带动眉毛、眼睛、颊部等区域的细微变化,使“说话表情”更生动。 +* 有的模型还会将语音内容的语义信息(如疑问、强调、感叹)编码进去,结合 LLM 分析的句法 / 语用信号,在语调变化处增加点头、皱眉、手势等动作,提升表达的自然度和感染力。 + +在更高维度上,**驱动与表达**也可以结合外部控制信号:例如将姿态骨架、手势轨迹、视线方向等作为附加输入,使数字人可以模仿特定演讲者的风格,或根据脚本中的“指示动作”(如“指向屏幕”“双手张开”)执行预定义的动作模板。无论是 Wav2Lip 这样的局部口型驱动,还是 MuseTalk / 实时骨架驱动等更全身的表达建模,它们共同实现了从语音 / 文本到面部与上半身动作的连续映射,是数字人“看起来像在认真说话”的关键一环。 + +### 5.5.2 形象与视频生成:从“一个模型”到“一个可塑的角色” + +驱动链路解决了“怎么动”,而**形象与视频生成**则决定了“谁在动、在哪里动、以什么风格动”。这里既包含高保真写实数字人,也包含二次元、卡通和低多边形 Avatar 等风格化形象,以及面向实时和离线渲染的不同技术选型。 + +在 2D 人像与插画场景中,典型做法是基于少量参考图像和短视频训练一个 **Talking Head 生成模型** : + +* 模型将人物的身份信息编码为一个“外观向量”或风格特征,将驱动参数(如语音隐向量、关键点、表情编码)作为条件输入,在图像空间中合成新的帧。 +* 与纯 Wav2Lip 只改口型不同,这类模型可以在姿态上做小幅度摆动、在表情上叠加情绪变化,从而让数字人看起来不那么“僵硬”。 + +在追求更高真实感、更自由视角和多机位切换的场景中,越来越多方案采用基于 **NeRF / 4D 表达**的数字人建模(如 ER‑NeRF 一类方法): + +* 通过多视角拍摄或视频,先重建人物头部 / 上半身的 3D 体积或高斯场,将不同表情和嘴型对应的状态编码为可插值的隐空间; +* 驱动时,将语音 / 表情参数映射到这一隐空间,在 3D 中进行体积渲染或高斯渲染,再投影到屏幕上。 +* 这种做法的优势在于:视角、光照和背景更自然,可以支持“环绕视角”“虚拟摄影机”运动,对 VR/AR、虚拟直播间和高端广告制作尤为友好。 + +在强调跨端部署与实时性的业务中,还会采用 **Ultralight‑Digital‑Human** 这类轻量化方案: + +* 通过结构剪枝、算子重构和模型蒸馏,将 Talking Head 或 Avatar 渲染网络压缩到移动端 / WebGPU 也能运行的规模; +* 在几毫秒级别完成从驱动参数到一帧图像的生成,与实时语音流或控制信号对齐,实现“低延迟数字人”,适合互动终端、自助机和 Web 前端应用。 + +在完整视频生产层面,形象与视频生成还要与背景、道具和镜头语言结合:一个常见的工作流是: + +* 先为品牌或个人定制一个数字人形象(2D 或 3D); +* 预设若干虚拟场景(演播厅、办公室、教室、展厅等); +* 在生产内容时,系统根据脚本自动选择合适场景和机位,生成数字人画面,并与 PPT、演示视频、产品画面进行多画面编排。 + 这使得数字人不只是一个“说话头”,而是可以自然融入各种节目和内容形态的“角色”。 + +### 5.5.3 实时数字人与系统集成:从离线视频到“屏幕里的同事” + +随着 ASR、TTS、LLM 和轻量级视频生成模型的成熟,越来越多数字人系统开始从**离线批量出片**走向 **实时交互** :用户在终端开口说话或输入文本,屏幕上的数字人在几百毫秒到几秒内“听懂—思考—回应—开口说话”,形成类似真人客服 / 导览 / 主持的体验。这里的关键不只是模型本身,还包括如何把多模态链路 **压缩到可接受的端到端延迟** 。 + +在一个典型的实时数字人闭环中: + +* **前端输入** :ASR 模块将用户语音实时转为文本,或直接接收用户文本输入。 +* **语义理解与决策** :LLM 结合业务知识库和工具(RAG、数据库查询、流程编排)生成回复文本,以及必要的结构化指令(如需要展示哪一页 PPT、播放哪个视频片段)。 +* **语音与驱动** :TTS 将回复文本转换为目标音色的语音,语音流一边生成、一边被 Wav2Lip / MuseTalk / 实时骨架驱动模型消费,逐段输出对应的口型与表情参数。 +* **渲染输出** :Ultralight‑Digital‑Human 类型的轻量渲染网络或基于 GPU 的 NeRF / Avatar 渲染引擎,将驱动参数实时转换成视频帧,通过 WebRTC、RTMP 或本地渲染直接输出到屏幕。 + +为了在多终端上提供一致体验,系统还需要在**延迟、带宽与算力**之间做细致权衡: + +* 在云端渲染方案中,绝大部分计算(LLM、TTS、驱动与渲染)在服务器完成,终端只负责播放视频流,适合算力有限的 Web / App 和线下大屏,但对网络稳定性有依赖; +* 在“云 + 端混合”方案中,ASR 和部分 LLM 推理在云端完成,轻量化驱动与渲染在本地进行,可以显著降低音画交互延迟,适合移动设备与自助终端; +* 在强算力终端(如高性能 PC、专用工作站)上,还可以将大部分链路下沉本地,实现弱网环境下的稳定互动。 + +在模型侧,**实时数字人**也对结构设计提出了额外要求: + +* 语音驱动模型需要具备流式推理能力,能够在获得一小段语音后就给出口型与表情预测,而不是等整句结束; +* 渲染网络需要尽可能减少依赖大卷积核和全局注意力,采用局部卷积、轻量自注意力、分辨率金字塔等结构控制计算量; +* 对于基于 NeRF / 4D 的高保真方案,则需要通过网格缓存、视锥裁剪、稀疏体积和 GPU 优化等手段,把每帧渲染控制在几毫秒到几十毫秒内。 + +在系统集成层面,实时数字人往往还要与**业务知识、人格设定与对话策略**紧密绑定: + +* 通过知识库和 RAG 管理行业知识、业务流程和 FAQ,确保“说得对、说得全”; +* 通过人设配置和话术模板控制说话风格和表达边界,确保“说得像这个人(或这个品牌)”; +* 通过多轮对话策略与会话状态管理,使数字人可以记住用户上下文、在合适时机确认和追问,呈现出“像一个真正的同事 / 导游 / 讲师”的交互感。 + +总体而言,加入了 Wav2Lip、MuseTalk、ER‑NeRF、Ultralight‑Digital‑Human 等专门为口型同步、表情驱动与实时渲染设计的模型之后,数字人正从“离线视频模板工具”加速演化为 **可实时响应、有稳定人格和专业知识的虚拟实体** ,成为视频技术体系中最具综合性和应用张力的一环。 + +# 6. 时间序列与时序决策(Time Series & Sequential Decision) + +在前面的视觉和结构化建模中,我们更多是在“静态”空间下思考问题:一张图、一条记录、一段文本。而在真实业务中,极大一部分核心指标都是随时间演化的:销售量和流量每天在波动,服务器负载和传感器读数每秒在变化,金融价格与宏观指标则在政策和事件驱动下不断调整。**时间序列与时序决策**这层,关注的就是:在时间轴上预测未来、识别异常、刻画结构突变,并在此基础上做出有前瞻性的决策与控制。 + +从产品视角看,这类能力贯穿运营、规划、风控和调度等关键环节:传统 BI / 报表系统中嵌入的指标预测模块、财务与供应链规划工具中的需求预测和安全库存建议、量化研究分析软件中的宏观关联分析和因果关系挖掘、电商和出行平台上的流量与运力预测、运维 AIOps 中的指标异常检测与告警,都是这一层的典型落地形态。下面我们从 **经典统计方法** 、 **深度学习时间序列建模** 、**异常与变点检测**以及**时空序列建模**四个方向展开。 + +## 6.1 经典时间序列建模(Statistical TS Modeling) + +在很多业务里,“时间”是天然的主线:销售量按日/周变化、网站流量随活动波动、设备负载跟着用户行为起伏、传感器读数反映着系统状态的细微变化。**经典统计时间序列建模**就是在这种时序结构上,利用相对可解释、可分析的统计模型去回答三个核心问题:**未来会怎样?变量之间如何关联?系统当前所处的状态是什么?** 尽管深度学习已经在许多场景中崭露头角,但 ARIMA、协整分析、卡尔曼滤波等传统方法,仍然在金融、供应链、运营、风控等领域长期服役,并常常作为更复杂系统的“基线”和解释工具。 + +从应用视角看,经典时间序列模型广泛存在于传统 BI/报表系统的指标预测模块、财务与供应链规划工具、以及各类量化研究软件中。它们可以直接对单个或多个时间序列给出未来预测区间,也可以用来分析宏观指标之间的协同变化与长期均衡关系,并通过状态空间建模对轨迹和隐藏状态进行估计。下面,我们从 **场景** 、**原理**和**模型**三个维度来梳理这类方法的典型用法,再分别展开具体方向。 + +* **场景** + * 指标预测:对销售量、网站流量、CPU 负载、传感器读数等按时间变化的数值进行短期或中期预测,用于库存备货、产能安排、运维调度等决策。 + * 宏观经济与金融分析:研究 GDP、通胀率、利率、汇率、资产价格等宏观和市场指标之间的长期关联和短期动态,辅助政策研究与量化策略开发。 + * 过程与轨迹估计:在定位、导航、目标跟踪和设备监控中,对随时间变化的轨迹、速度、状态进行估计与平滑,并在噪声环境中尽可能还原“真实过程”。 +* **原理** + 经典时间序列方法普遍基于“ **统计假设 + 参数化结构** ”的思路: + * 假定时间序列满足一定的平稳性或弱平稳性条件,通过自相关结构(自相关函数 ACF、偏自相关函数 PACF)刻画“当前值由过去多少阶的历史决定”。 + * 在多变量情形中,通过协整与向量自回归(VAR)模型,刻画多个时间序列之间的长期均衡关系与短期偏离修正。 + * 对于噪声严重、状态不可直接观测的系统,引入隐含状态(latent state)与观测方程组成状态空间模型,用贝叶斯推断或递推滤波(如卡尔曼滤波)进行在线估计与预测。 +* **模型** + 这类方法的模型族相对明确、结构清晰,便于解释和调参: + * 单变量与多变量 AR/MA/ARIMA/SARIMA 系列,用于平稳/季节性时间序列建模,是 BI 系统和传统预测模块的“常驻成员”。 + * VAR/协整模型,用于多维宏观和金融时间序列的联合建模和因果关系检验,适合政策和策略层面的关联分析。 + * 状态空间模型与卡尔曼滤波、隐马尔可夫模型(HMM)等,用于轨迹估计、设备状态估计以及隐藏状态的推断,是工程控制与信号处理中的基础工具。 + +综合来看,经典时间序列建模的优势在于 **可解释性、可诊断性和工程可控性** :建模流程、假设检验、残差分析都有成熟规范,很容易融入现有 BI 与规划系统。下面,我们从单/多变量预测、协整与因果、状态空间三个方向展开。 + +### 6.1.1 单变量/多变量时间序列预测:从 ARIMA 到 VAR + +在最典型的业务场景中,我们首先面对的是一条或若干条按时间排序的指标曲线:例如某商品每日销量、站点每小时 PV、机房每分钟 CPU 使用率、设备传感器每秒读数。目标是根据历史走势对未来的短期或中期区间给出预测,并给出合理的置信区间。**AR/MA/ARMA/ARIMA/SARIMA** 系列模型正是为此设计的标准工具。 + +对单变量序列来说,ARIMA 类模型假设“当前值由过去若干期的历史值和随机扰动线性决定”,通过对序列做差分、季节差分来消除趋势和季节性,使其趋于平稳: + +* AR(自回归)部分刻画“自身滞后对当前值的影响”; +* MA(滑动平均)部分捕捉“历史误差项对当前值的影响”; +* I(差分)部分负责去除趋势; +* 加上季节项后得到 SARIMA,可以显式描述周度、月度等周期性结构。 + +在工程使用中,通常会先做平稳性检验(如 ADF)、观察 ACF/PACF 图,再通过信息准则(AIC/BIC)和残差诊断选取合理的阶数。对于具有明显季节性的指标(如电商日销量、节假日流量)尤其适合 SARIMA 建模,配合假日特征或外生变量可以进一步改善预测性能。 + +当我们希望一次性建模多条相关时间序列时,可以引入 **多变量时间序列模型** 。代表方法是 VAR(向量自回归)与其变体。VAR 将多个序列视为一个联合向量,用自身及彼此的滞后项共同解释当前值,从而捕捉不同指标之间的相互影响。例如,在宏观经济分析中,可以将 GDP 增速、通胀率、利率、汇率等纳入同一个 VAR 模型,研究冲击响应和传导路径;在业务运营中,也可以用 VAR 描述“一个渠道的流量变化如何影响其他渠道”“促销强度与销量之间的动态关系”,为资源调配提供参考。 + +在产品化形态上,这一类单/多变量预测能力通常嵌入在**传统 BI / 报表系统的预测功能、财务与供应链规划工具**中:用户选定某条或若干条时间序列,系统自动完成建模与预测,并提供预测区间、残差分析和模型诊断报告,用于辅助决策,而不必深入理解决策背后的所有数学细节。 + +### 6.1.2 协整与因果关系:宏观指标之间的长期均衡 + +在经济与金融领域,很多时间序列表面看似随机游走,但在更长的时间尺度上存在某种 **稳定的长期均衡关系** 。典型例子包括汇率与利差、股指与宏观盈利、商品价格与成本指数等。单独看每条序列,可能都是非平稳的;但某种线性组合却在长期内围绕一个稳定水平波动。这种现象被称为 **协整(cointegration)** ,它为我们理解宏观指标之间的结构性关系提供了重要线索。 + +在工程实践中,协整分析通常包括几个步骤: + +1. 对各个时间序列进行单位根检验,确认其为同阶单整(例如都为 I(1)); +2. 进行协整检验(如 Engle-Granger 两步法、Johansen 检验等),判断是否存在非平凡的线性组合使得该组合平稳; +3. 若发现协整关系,可以构建误差修正模型(ECM),刻画“短期偏离长期均衡时,系统如何逐步修正回到平衡状态”。 + +与协整相关的,是 **Granger 因果关系检验** 。它并不是严格意义上的哲学“因果”,而是一种基于预测能力的统计定义:如果变量 X 的历史信息可以显著提高对变量 Y 的预测精度,则称“X Granger 导致 Y”。通过在 VAR 或回归框架下比较有/无某个变量滞后项时的预测误差,可以评估不同宏观或市场指标之间的方向性影响。在量化研究和宏观分析中,这种检验常用于甄别潜在的领先指标、构建因子、或者验证策略假说。 + +从产品视角看,协整与因果分析更多出现在**量化研究分析软件、宏观经济分析平台和金融研究工具**中。它们帮助研究者从成堆的时间序列中抽取出相对稳健的结构关系,并将这些关系映射到更高层次的业务概念(如“利率对汇率的长期约束”“不同资产之间的价差回归”),成为策略设计与风险管理的重要依据。 + +### 6.1.3 状态空间模型与隐状态估计:卡尔曼滤波与 HMM + +在许多真实系统中,我们观测到的时间序列只是 **噪声污染后的表象** ,而真正感兴趣的是背后随时间演化的“系统状态”:例如车辆的真实位置和速度、设备的健康状态、用户的潜在行为模式等。此时,如果仍然只在观测序列上做 ARIMA 式建模,就很难充分利用对系统结构的理解。**状态空间模型(State Space Models)**正是为这种“隐状态 + 噪声观测”的问题而提出。 + +状态空间模型通常由两部分构成: + +* 状态转移方程:描述隐藏状态如何随时间演化,可以是线性的也可以是非线性的; +* 观测方程:描述隐藏状态如何生成带噪声的观测值。 + +在线性高斯假设下,这个框架可以通过**卡尔曼滤波(Kalman Filter)和平滑器(Smoother)** 实现对状态的递推估计与预测:每一步分为“预测”和“更新”两大阶段,将上一时刻的状态分布与当前观测结合,得到新的状态估计。这在导航与定位(如轨迹估计、目标跟踪)、金融时间序列(如波动率估计)、设备状态估计(如健康监控、剩余寿命预测)中极其常见。 + +与连续状态空间模型相邻的,是 **隐马尔可夫模型(HMM)** 。HMM 假设系统在若干个离散的隐状态之间随时间转移,每个隐状态下生成观测数据的概率分布不同。通过前向–后向算法和 Viterbi 算法,HMM 可以估计隐状态序列、计算观察序列概率,并对下一步状态与观测做预测。HMM 早期广泛用于语音识别、文本标注,也常用于简单的行为模式识别与事件序列建模,在某些工业与金融场景中仍有其优势——结构可解释、训练稳定、与领域经验易于结合。 + +在系统层面,状态空间建模、卡尔曼滤波和 HMM 常作为**轨迹估计、设备状态估计、金融与工程控制系统**的底层模块,被封装在更大的工具链中。它们不一定直接暴露给终端用户,但在导航、目标跟踪、工业控制、风险计量等产品背后,长期扮演着“隐形引擎”的角色。 + +## 6.2 深度学习时间序列建模(Deep TS Forecasting) + +随着数据规模和场景复杂度的持续上升,单纯依赖线性、平稳性假设的经典模型在很多应用中开始显得“力不从心”:大量非线性模式、长跨度依赖、复杂的多变量交互、突发行为与周期叠加等特点,使得我们需要更灵活、更高容量的模型结构。**深度学习时间序列建模**正是在这一背景下发展起来的:从 RNN/LSTM/GRU,到 Temporal CNN/TCN,再到时序专用 Transformer、混合与分层模型,它们共同构成了现代时序预测与建模的主力工具箱。 + +从应用视角来看,深度时序模型已经广泛部署在**电商流量 & 销量预测平台、供需/运力/排班预测系统、云资源负载预测与容量规划工具**中,用于在多品类、多门店、多城市、甚至多业务线的复杂结构下,给出统一而灵活的预测方案。与经典模型相比,它们更强调“端到端表示学习”和“全局模式建模”,更擅长处理长序列、高维、多变量场景。下面,我们同样从 **场景** 、**原理**和**模型**三个维度展开。 + +* **场景** + * 大规模多序列预测:成千上万条商品、门店、城市维度的销量/流量序列,需要在一个统一模型下同时建模,并支持冷启动与长尾序列。 + * 复杂运营与调度:供电/供水/运力/排班等系统中,需求受多维特征影响(天气、节假日、价格、活动),且存在多层级结构(门店/城市/全国),需要同时兼顾全局模式与局部差异。 + * 云资源与基础设施:大规模服务器集群、容器平台、网络与存储负载,呈现高度非线性和多峰结构,需要高频预测与容量规划支撑 SLO。 +* **原理** + 深度时序模型的核心在于 **自动从历史序列与协变量中学习多尺度模式与长期依赖** : + * RNN/LSTM/GRU 通过循环结构显式地在时间维度上传递“记忆”,适合捕获顺序依赖与局部时间结构。 + * Temporal CNN / TCN 使用一维卷积和膨胀卷积,在保证因果性的前提下扩大感受野,实现并行训练与稳定梯度传播。 + * 时序 Transformer 与专门设计的变体(Informer、Autoformer、TimesNet 等)利用自注意力机制,在长序列、多变量设置下建模复杂依赖和周期性模式。 + * 混合与分层模型进一步引入“全局 + 局部”“多层级时间序列”的结构假设,在统一框架中同时学习全局模式与个体特征。 +* **模型** + 在具体实现上,深度时序建模涌现出一系列具有代表性的架构: + * 经典深度序列模型:RNN/LSTM/GRU 以及基于它们的 DeepAR 等自回归概率预测模型。 + * 分解与预测一体化模型:N‑BEATS 等通过显式趋势/季节分解模块增强可解释性。 + * 基于注意力的时序模型:Temporal Fusion Transformer(TFT)等结合注意力、门控、变量选择,适用于多变量、有丰富协变量的业务场景。 + * 长序列 Transformer 模型:Informer、Autoformer、TimesNet、PatchTST 等,围绕长序列效率与多尺度建模做出专门设计。 + +下面,我们从深度序列模型、卷积与 Transformer、以及混合与分层建模三个方向展开。 + +### 6.2.1 深度 RNN/LSTM/GRU:从单序列到 DeepAR + +在深度学习进入时间序列领域初期,**RNN/LSTM/GRU** 是最自然的选择。与文本和语音建模类似,它们通过在时间步之间传递隐状态来“记忆”历史信息,允许捕捉比传统线性模型更复杂的非线性和长期依赖。对于单条或少量时间序列,简单的 LSTM/GRU 在有足够数据时就可以取得不错的预测效果;而在大规模多序列场景中,则可以采用 **共享参数的 RNN/LSTM/GRU 模型** ,在所有序列上进行联合训练,从而学习到通用的时序模式。 + +在此基础上,类似 **DeepAR** 的自回归概率模型为深度时序建模提供了一个标准框架:它将历史观测和协变量输入一个共享的 RNN/LSTM/GRU 网络,在每个时间步上输出序列值的条件分布参数(如高斯、负二项分布等),并通过最大似然训练实现端到端的概率预测。这样的设计使模型能够自然生成预测区间、处理不规则的尺度和多序列混合,有利于在电商销量、需求预测等场景中落地。 + +然而,RNN 类模型存在典型问题:长序列上的梯度衰减,以及在训练阶段无法完全并行化。虽然门控机制(LSTM/GRU)缓解了部分问题,但在特别长的时间跨度和高频数据下,训练与推理效率仍然是需要权衡的因素。这也促使业界和学术界探索更加并行友好的结构,如 TCN 和 Transformer。 + +### 6.2.2 Temporal CNN 与 Transformer:从局部卷积到长序列注意力 + +为了解决 RNN 在长序列上的效率和稳定性问题,**Temporal CNN / TCN** 引入了一维卷积和膨胀卷积来建模时间依赖:通过堆叠多层因果卷积、逐层扩大感受野,它在不破坏时间因果性的前提下,实现了对远距离历史的建模。相比 RNN,TCN 在训练时可以高度并行,梯度传播路径更短,因此在训练稳定性和效率上表现突出,适合用在高频数据、需要较大感受野的工业时序预测场景中。 + +在更高的复杂度层级上,**Transformer 与时序专用结构**成为近年来长序列、多变量时间序列建模的主角。直接使用标准 Transformer 会遇到计算复杂度随序列长度平方级增长的问题,因此涌现出一系列面向时序的改造方案: + +* **Informer** 通过概率稀疏自注意力等机制,降低长序列上的计算负担,并针对预测任务优化结构。 +* **Autoformer** 将趋势与季节性分解融入自注意力框架,试图在保持长序列建模能力的同时提升可解释性和稳定性。 +* **TimesNet** 通过在时间–频率域或多尺度展开中增强对周期与模式的感知,更好地处理复杂、多周期的长序列。 +* **PatchTST** 借鉴 Vision Transformer 的“patch”思想,将连续子序列视作补丁,提高长序列时的建模效率与泛化能力。 + +这类模型往往特别适合**长序列、多变量、高维协变量**的复杂时序场景,如大规模云资源负载、多区域能源需求、多渠道流量预测等。它们可以在一个统一架构中同时建模多维输入、静态特征和时间相关变量,并通过注意力权重为后续解释与诊断提供一定线索。 + +### 6.2.3 混合与分层模型:全局 + 局部、多层级时间序列 + +在实际业务中,时间序列很少是“孤立”的:它们往往具有明显的 **层级结构与共享模式** ——例如门店/城市/区域/全国的销售层级,SKU/品类/品牌的商品层级,或业务线/产品/渠道的组织结构。如果简单地为每条序列单独建模,很难利用到这一层次结构;而直接把所有序列混在一起,又会忽略各自的个性化差异。**混合与分层模型**正是为解决这类问题而设计。 + +一类常见思路是 **全局 + 局部模型** :通过一个共享的“全局模型”学习所有序列的共性模式(如总体趋势、节假日效应、季节性),同时为每条序列或每个子群体引入局部参数或嵌入向量,捕捉个体特性。这种结构既避免了为长尾序列单独训练模型导致的数据稀疏问题,又保留了在热门序列上进行精细建模的能力。 + +另一类是 **多层级时间序列(hierarchical TS)建模** :在预测过程中显式考虑层级约束(如子层级之和需要与上层级预测一致),通过自顶向下、自底向上或中间层级的联合优化,使各层级预测在数值和结构上保持一致。在深度时序框架下,这通常表现为在输入编码中加入层级特征、为不同层级设计多头输出,或使用分层损失函数进行训练。 + +从产品视角看,这类混合与分层建模广泛应用于**电商销量预测平台、供需/运力/排班预测系统**等场景:系统需要同时给出“单店单品”“城市级别”“全国总量”等不同粒度的预测,并在资源规划和 KPI 拆解过程中保持上下层的一致性。深度模型的灵活结构,使得这类约束可以通过端到端方式嵌入建模过程,而不必完全依赖事后修正。 + +## 6.3 异常检测与变点检测(Anomaly & Change Point Detection) + +在时间序列场景中,“预测未来”只是问题的一部分,另一部分同样关键的是: **实时发现异常与结构变化** 。无论是设备运行、业务指标、交易行为,还是运维监控,异常检测与变点检测都是保障系统稳定、识别风险机会的核心能力。传统上,统计阈值法、EWMA、CUSUM 等方法广泛使用;随着数据维度和复杂度提升,各类机器学习与深度学习方法(孤立森林、One‑Class SVM、AutoEncoder/VAE、时序 GAN、GNN + 时序模型)也开始扮演重要角色。 + +从产品形态来看,这类能力往往内嵌在**设备故障预警系统、业务指标异常报警平台(如转化率突降)、安全攻击与欺诈检测系统、运维 AIOps 告警引擎**中,通过实时监控多维时序信号,自动标记可疑点和结构变更,并与规则、知识库和人工决策流程结合。下面继续从 **场景** 、**原理**和**模型**三个角度展开。 + +* **场景** + * 设备与工业系统:监控温度、振动、电流、压力等传感器数据,提前发现故障与退化趋势,减少停机和损失。 + * 业务与运营指标:监控 PV/UV、转化率、订单量、延迟、错误率等关键指标,快速发现突降、突升、异常波动,为运营和技术团队提供告警。 + * 安全与风控:分析登录行为、交易序列、访问模式等时间序列,识别潜在攻击、作弊和欺诈行为。 +* **原理** + 异常与变点检测本质上是在“正常模式”上寻找显著偏离和结构突变: + * 对于点异常和序列异常,可以通过统计分布拟合、密度估计或边界学习,判断当前观测是否落在“正常区域”之外。 + * 对于变点,则关注时间序列统计特性(均值、方差、相关结构、分布等)在时间轴上的突变,并尝试定位变化发生的时间位置。 + * 在高维和多点网络中,需要将多条时间序列之间的依赖结构(如拓扑、相关性)纳入建模,避免将局部异常与整体趋势混淆。 +* **模型** + 从方法族来看,可以大致分为统计方法、单类/孤立学习方法、重构式深度模型和图 + 时序组合模型: + * 统计异常检测:阈值、EWMA、CUSUM 等,对单变量或简单场景极其高效,是传统监控系统的基础。 + * 机器学习方法:Isolation Forest、One‑Class SVM 等,用于在多维特征空间中刻画“正常区域”,对异常样本进行孤立。 + * 深度重构模型:AutoEncoder / VAE / 时序 GAN,通过学习重构正常序列,在重构误差较大时标记异常。 + * 图神经网络 + 时序模型:在传感器网络、微服务指标等场景中,引入图结构和时序模型共同学习正常模式,强化对拓扑相关异常的识别。 + +下面,我们围绕点/序列异常、变点检测、多维与图结构三个方向展开。 + +### 6.3.1 点异常与序列异常:从统计阈值到重构式模型 + +最直观的异常检测形式是 **点异常** :某个时间点的观测值远离历史正常范围(如 CPU 使用率突然飙到 100%、交易金额异常增大、传感器读数瞬间跳变)。传统方法中,最常见的做法是对历史正常数据拟合一个统计分布或滑动统计量(均值、方差、分位数),在此基础上设定阈值或控制图(如 EWMA、CUSUM),当当前观测超出可接受区间时发出告警。优点是实现简单、计算代价低、易于解释,因此在大量运维监控和工业系统中仍然广泛使用。 + +当维度提升或模式变得更复杂时,可以引入**孤立森林(Isolation Forest)、One‑Class SVM** 等单类/孤立学习方法:它们通过在“正常样本”上学习一个聚合区域(或边界),将落在该区域之外的点视为异常。通过在序列的滑动窗口上提取统计特征(如窗口均值、方差、频域特征等),这类方法也可以用于识别局部“序列异常”(即一段时间内行为偏离正常模式),适用于多维指标和难以精确定义分布形态的场景。 + +在深度学习框架下,**基于重构误差的 AutoEncoder / VAE / 时序 GAN** 等方法则提供了更灵活的选择: + +* 使用 AutoEncoder 或 VAE 在大量正常序列上训练“压缩–重建”模型,使其学会重构正常模式; +* 在在线监控时,将新的时间窗口输入模型,如果重构误差显著增大,则认为该区间存在异常; +* 时序 GAN 类方法则通过学习生成正常序列,在判别器的判定结果或生成误差中寻找异常信号。 + +这些方法可以适应高度非线性的模式和复杂的协变量结构,特别适合在**多维业务指标、复杂设备传感器数据**上构建统一异常检测引擎。 + +### 6.3.2 变点检测:结构突变与事件生效 + +与点异常和局部异常不同,**变点检测(Change Point Detection)**关注的是时间序列在结构上的突变:例如均值从一个水平跃迁到另一个水平、波动率发生改变、周期和相关结构出现调整。这类变化往往对应现实世界中的某种事件或状态切换,如配置变更、生效新策略、政策调整、生产工艺改变、市场 regime 切换等,对业务诊断和因果分析极为关键。 + +传统统计方法中,变点检测常借助似然比检验、CUSUM、Bayesian Online Change Point Detection(BOCPD)等技术: + +* 通过在不同时间点前后拟合不同参数的模型(如不同均值/方差),比较“无变点假设”和“有变点假设”的拟合优度; +* 在在线场景中,对每个时间点递推更新“当前段落为止是否出现变点”的后验概率,一旦超过设定阈值则触发告警。 + +在更复杂的设置下,可以结合深度表示学习与分段模型,将变点检测视作 **序列分段问题** :用神经网络提取特征,再在特征空间中寻找段落边界,或者直接训练模型预测某一时间点属于“变点”的概率。这对于存在多种形态变化(不仅是均值/方差变化)、且难以用简单统计假设刻画的业务指标尤其有用。 + +在产品体系中,变点检测通常被集成在**业务指标分析平台、A/B 实验分析系统、配置与策略变更监控工具**中:当关键指标呈现结构性变化时,系统可以自动标记潜在变点,并关联相应的变更事件(如版本发布、参数调整、政策落地),为后续根因分析提供线索。 + +### 6.3.3 多维时序与图结构:GNN + 时序模型的联合建模 + +在现代分布式系统和物联网场景中,我们往往面对的是 **多点、多维、具有关联拓扑结构的时间序列** :例如传感器网络中的多个测点、微服务架构中的各个服务指标、配电网/交通网中的多个节点和边。此时,单独、逐条地对每个时间序列做异常检测,很容易误判局部波动或忽略整体模式——真正的异常往往是“局部–整体不一致”或“拓扑结构中不协调”的表现。 + +为此,近年来出现了大量**图神经网络(GNN) + 时序模型**的组合方法: + +* 首先根据现实拓扑(物理连接、网络拓扑)或基于数据估计出的相关图,构建一个表示多点之间关系的图结构; +* 在每个时间步上,用 GNN 对节点特征(各点的时序值及其局部上下文)进行消息传递,学习空间关联特征; +* 再将图编码后的表示输入 RNN、TCN 或 Transformer 等时序模型,捕捉时间维度上的动态模式; +* 最终在联合表示上进行异常评分或变点检测,实现 **时空联合的异常识别** 。 + +这种框架在**传感器网络监控、微服务指标异常检测、城市计算中的时空异常检测**等场景中尤其适用:它能够分辨“全局性变化”(如整个系统负载上升)与“局部异常”(如某个节点异常拥塞),也能更好地识别拓扑结构相关的异常模式(如链路级问题、区域性网络故障)。 + +在工程层面,这类方法通常作为**运维 AIOps 告警系统、安全与风控平台、设备群监控系统**的高阶能力出现,结合基础统计监控、规则系统和专家知识,为复杂系统提供更智能、更上下文感知的异常发现机制。 + +## 6.4 时空序列(Spatio-Temporal Modeling) + +在很多关键业务场景里,仅仅建模“时间”是不够的: **“什么时候”与“在哪里”并行存在** ,而且二者高度耦合。城市交通流量受路网结构和时间规律共同影响,气象与空气质量既依赖时间演化,也依赖地理邻近与大气流场;物流、共享单车与网约车调度则需要同时考虑需求的时空分布和道路/区域结构。**时空序列建模(Spatio‑Temporal Modeling)** 正是针对这类“时间 + 空间”联合建模问题的系统方法。 + +与纯时间序列模型相比,时空模型需要显式把**空间依赖结构**纳入考虑:相邻路段的交通流量、邻近监测站的空气质量、相连节点的负载与状态,通常比相隔较远的点更具相关性。为此,图神经网络(GNN)、卷积 LSTM(ConvLSTM)等结构被广泛用于结合空间与时间两个维度的特征学习。对应到产品层面,这类能力支撑着**城市计算平台(交通/人流预测)、气象/环境预测系统、物流路径规划与共享单车/网约车调度平台**等大量关键应用。 + +* **场景** + * 交通流量与人流预测:在路网或地铁网结构上,对不同时段的车流、人流进行预测,辅助信号灯优化、拥堵管理和调度决策。 + * 气象与环境监测:在地理网格或监测站网络上,预测未来的温度、降雨、风力、空气质量等时空分布,为预报和决策提供支撑。 + * 物流与出行调度:在城市区域或路网结构上预测订单需求、车辆分布、仓库/站点的负载情况,为路径规划、车辆调度和运力分配提供依据。 +* **原理** + 时空序列建模的核心是 **在统一框架中同时学习空间相关性与时间动态** : + * 在空间维度上,通过图结构或卷积结构刻画“谁与谁相关”,并基于此进行消息传递与特征聚合; + * 在时间维度上,利用 RNN、TCN、Transformer 或特化的时序结构刻画动态变化; + * 两者可以串联(先做空间,再做时间),也可以交织或同时作用(如时空卷积、时空注意力)。 +* **模型** + 典型时空模型大多采用“GNN + 时序模型”或“卷积 + LSTM”的组合形态: + * 图神经网络 + 时序模型:ST‑GCN、DCRNN、Graph WaveNet、ST‑Transformer 等,通过图卷积或图注意力捕捉空间依赖,再用时序结构捕捉时间动态。 + * 卷积 LSTM 类模型:ConvLSTM、Conv‑TT‑LSTM 等,在时序递推中嵌入空间卷积门控,实现对时空局部特征的联合建模。 + +下面,我们从时空任务与数据表示、GNN + 时序模型、卷积 LSTM 与时空卷积三个方向展开。 + +### 6.5.1 时空任务与数据表示:从路网到地理网格 + +在进入具体模型之前,时空序列建模首先要解决的是 **如何表示空间结构** 。与一维时间轴不同,空间结构可以是规则网格(grid)、不规则图(graph)、或者混合形式。 + +* 在交通场景中,道路与交叉口天然构成一个有向或无向图:节点表示路段或路口,边表示道路连接与行驶方向;每个节点在每个时间步上有一组特征,如车流量、平均速度、拥堵指数等。 +* 在气象与空气质量预测中,可以使用规则地理网格(如经纬度网格),或将监测站点之间的邻接关系构建为图结构,基于地理距离、风向或相关性定义边权。 +* 在物流与共享出行场景中,可以将城市划分为网格或区域单元,每个单元在时间上具有订单量、活跃车辆数等特征,同时在空间上通过邻接关系或实际道路距离相连。 + +这种“ **空间结构 + 时间序列** ”的统一表示,使得很多不同场景可以被建模为类似的问题:给定历史时空序列,预测未来若干时间步上每个节点或网格的状态。后续模型设计(无论是 GNN + 时序模型,还是 ConvLSTM)都是在这一统一视角上展开。 + +在产品层面,这一层的抽象往往封装在**城市计算平台、气象/环境预测系统、路径规划与调度平台**的数据层与建模层:业务方只需要知道“我们在路网/网格上预测未来流量/需求如何”,而底层的数据表达与时空融合由建模框架统一处理。 + +### 6.5.2 图神经网络 + 时序模型:ST‑GCN、DCRNN、Graph WaveNet 等 + +在图结构上建模时空序列,目前最主流的路线是“ **图神经网络(GNN) + 时序模型** ”的组合。代表模型包括 **ST‑GCN、DCRNN、Graph WaveNet、ST‑Transformer** 等,它们的共同特点是: + +* 在空间维度上使用图卷积(GCN)、图注意力(GAT)或谱域卷积等方法,对每个时间步的节点特征进行“邻域聚合”,从而捕捉空间依赖与拓扑结构的影响; +* 在时间维度上,通过 RNN(如 GRU/LSTM)、TCN、或 Transformer 对节点级特征进行序列建模,捕捉时间趋势和周期性; +* 通过交替堆叠或联合设计,使得模型能够在多个时空尺度上学习局部与全局模式。 + +例如,**DCRNN(Diffusion Convolutional RNN)** 将图卷积与门控循环单元结合起来,使用扩散卷积来模拟信息在路网上的传播,再通过 RNN 捕捉时间维度的动态,非常适合交通流量预测等任务。**Graph WaveNet** 则在图卷积和时间卷积的基础上,引入自适应图结构学习和多尺度建模,提高对复杂路网和非规则拓扑的适应性。**ST‑Transformer** 等模型则把自注意力机制引入时空建模,通过时空注意力模块同时考虑不同时间和空间位置之间的相关性。 + +在实际系统中,这一类 GNN + 时序模型广泛部署在**城市交通与人流预测平台、共享出行调度系统、复杂 IoT 网络监控**等产品中。它们通常作为核心预测引擎之一,与规则系统、仿真模型和业务策略共同组成闭环,使得调度与规划既能考虑全局结构,又能响应局部变化。 + +### 6.5.3 卷积 LSTM 与时空卷积:ConvLSTM、Conv‑TT‑LSTM 等 + +另一条重要路线是基于**卷积 LSTM(ConvLSTM)**及其变体的时空建模。与标准 LSTM 在时间步之间传递一维向量不同,ConvLSTM 在门控结构中使用卷积算子,使得隐藏状态和输入都保持为多维张量(如空间网格上的特征图)。这样,在每个时间步的状态更新中,既包含了时间上的递推,也在空间维度上进行了局部卷积聚合,实现了对时空局部模式的自然建模。 + +在此基础上,**Conv‑TT‑LSTM 等改进模型**尝试通过张量分解、参数分享、多尺度卷积等机制,提升模型的表达能力和效率,适应更大规模、更复杂的时空数据。例如,在气象预测中,可以使用 ConvLSTM 堆叠多层,对多通道气象要素图(温度、湿度、风向等)进行时空递推,从历史若干帧预测未来几小时或数天的空间分布;在交通和环境监测中,也可以将路网或监测点映射到规则网格上,使用 ConvLSTM 等模型进行预测。 + +与 GNN + 时序模型相比,ConvLSTM 系列在**规则网格结构、局部空间平滑性明显**的场景中使用较多,如气象雷达回波预测、空气质量网格预报、视频帧级预测等。其优势在于实现相对直接、易于利用现有卷积网络基础设施进行加速和部署,也容易与 CNN/ViT 等视觉模型协同使用,如在遥感影像时空建模中结合卷积特征和时序递推。 + +在产品形态上,这一方向的模型多用于**气象/环境预测系统、遥感时空分析平台、视频与影像时空预测**等,常常以“未来时空场景预测图”的形式向上游暴露能力,成为业务决策与可视化分析的重要输入。 + +# 7. Agent 与工具调用层(Agents & Tool Use) + +在前面的视觉、语言等能力层中,模型大多还是“被动回答”的形态——接收输入、给出输出。而在很多真实业务里,我们需要的是一个 **可以主动规划、调用外部工具、串联工作流的智能体(Agent)** :它不仅能看懂/读懂/听懂,还能自己“决定下一步做什么”,比如去查资料、跑代码、读写文件、调用内部系统,然后再把结果整合、解释并反馈给用户。 + +这一层可以被理解为“把基础模型变成可行动系统”的关键粘合层:通过 **结构化工具调用接口、工作流编排、多 Agent 协作以及人类在环机制** ,把 LLM 从一个强大的“认知内核”扩展为能够完成端到端任务的“数字员工”。 + +## 7.1 工具调用与执行(Tool Calling / Function Calling) + +在只读不写、只说不做的纯文本时代,LLM 更像一个“超级对话者”:可以理解问题、给出建议、写代码、列方案,但所有“真正执行”的工作——查数据库、跑脚本、生成文件、调云服务——仍然要人工接手完成。而**工具调用 / Function Calling** 的出现,让模型第一次可以在安全边界内“动手”:根据自然语言自动生成结构化参数,去调用搜索引擎、数据库、计算引擎、图像/音频/视频生成服务等外部能力,再把执行结果整理返回,从而形成“理解 → 决策 → 执行”的闭环。 + +从产品角度看,工具调用是绝大多数 Agent 系统的“底盘能力”:OpenAI Assistants API、LangChain、LlamaIndex、AutoGen、各类云厂商的 Agent 平台,实质上都是在 LLM 之上,围绕**如何定义工具、如何让模型正确选工具、如何处理出错与重试**搭建一层运行时。下面同样从 **场景** 、**原理**和**模型**三个角度梳理这一层能力,并在后续小节中分别展开“工具调用接口设计”“工具选择与策略”“典型工具类型”三个方向。 + +* **场景** + * 智能问答与检索增强:模型根据用户问题自动决定是否调用检索工具(向量/关键词搜索)、查企业内部知识库或公网搜索,并将查到的文档、FAQ 整合进最终回答。 + * 数据与报表自动化:面对“帮我查这段时间的销售额并画图”“给我算一下这个投资组合的风险指标”之类请求,模型自动生成 SQL 或分析参数,调用数据库和计算引擎,返回图表与结论。 + * 文档与文件操作:自动读取 PDF/Word/Excel/数据库表,抽取和汇总关键信息,或按指令生成新文件(如报表、合同、方案),并通过工具上传/存储到指定位置。 + * 媒体生成与处理:根据文本指令调用图像/音频/视频/3D 生成服务,或对现有媒体做剪辑、压缩、转码、水印等操作,形成一键“文案 + 设计 + 导出”的内容流水线。 +* **原理** + 工具调用的核心是: **用自然语言驱动结构化函数调用** 。 + * 首先以 JSON Schema 或函数签名的形式,将外部工具的名称、说明、参数结构(类型、必填项、枚举值等)暴露给 LLM。 + * 当用户发出请求时,LLM 不仅要理解语义,还要判断“是否需要调用某个工具”“需要哪个(些)工具”“这些工具的参数应该怎么填”。 + * 一旦模型决定调用某个工具,就生成一段结构化参数(通常是 JSON),由运行时去真正执行外部 API / 程序,并把执行结果以结构化形式返回给模型,让模型基于结果继续推理或生成最终回答。 + * 为保证安全与鲁棒性,系统需要在这一过程中处理参数校验、超时、错误返回、重试与回退,并对可能涉及安全/隐私的调用做权限与审计控制。 +* **模型** + 支撑这一能力的模型与框架主要包括三类: + * 支持 Function Calling 的 LLM:如 GPT‑4.1 / o 系列等,原生在解码层面理解“工具签名 + JSON Schema”,能够在合适时机主动或被动地产生结构化调用参数。 + * 工具增强推理范式:如 ReAct、Toolformer,将“思考 + 工具调用”编织进同一推理链条,将工具使用视作中间步骤的一部分,而不是简单的前/后处理。 + * 工程框架与运行时:OpenAI Assistants API、LangChain、LlamaIndex、AutoGen、各云厂商 Agent 平台等,为工具定义、调用路由、状态管理、错误处理与日志审计提供基础设施,让开发者可以聚焦在“暴露哪些工具”和“抽象怎样的业务 API”上,而不必从零搭建运行时。 + +### 7.1.1 工具调用接口:从自然语言到结构化函数调用 + +一个可用的工具调用系统,首先需要一个清晰、规范、对 LLM 友好的“工具接口层”。它承担着把外部世界的 API、脚本、服务包装成模型可理解、可安全调用的“函数”的职责,让模型可以像写伪代码一样“说出”自己希望调用的工具及其参数。 + +* **工具定义与参数模式** + 在接口层,通常会用类似 JSON Schema 或函数签名的结构定义每个工具:包括名称(name)、说明(description)、参数字段(properties)、类型(string / number / boolean / array / object)、是否必填(required)、取值范围或枚举等。 + 这些信息一方面被用来驱动前端/SDK 的类型检查,另一方面也直接提供给 LLM,帮助模型“学会”如何正确填写参数。描述越清晰、约束越合理,模型生成的调用就越规范,出错率越低。 +* **LLM 生成结构化参数** + 当用户提出“帮我查 2024 年 Q3 的营收并画一张按地区拆分的柱状图”这类请求时,模型需要先推理出:这至少需要一个“报表查询工具”(访问数据)、可能还需要一个“图表生成工具”(画图)。对每个工具,它要从原始语言中抽取并映射结构化参数,如时间范围(start_date/end_date)、维度(region)、指标(revenue)、图表类型(bar)、输出格式等,然后以 JSON 输出交给运行时。 + 这个过程中,模型本质上在做“自然语言 → 任务规划 → 参数抽取 / 填充”的一体化推理,因此工具描述的自然语言提示、参数示例和 few‑shot 样例都非常关键。 +* **工具执行与结果回传** + 运行时接收到模型产出的 JSON 调用后,会先进行参数校验与安全检查,再去真正调用后端 API 或程序。执行完成之后,将结果封装为结构化对象(如查询结果表格、文件 URL、媒体资源 ID 等)返回给模型。 + 随后,模型会把这些原始结果转化为用户可读的解释或进一步加工,如总结报表、生成自然语言分析、嵌入图表标注说明等。对于模型而言,工具结果只是中间信息的一部分,它仍然要负责“理解结果 + 解释结果”。 + +### 7.1.2 工具选择与策略:在多工具世界里做决策 + +当系统中只有一个工具时,“要不要用工具”是唯一的问题。但在现实 Agent 应用中,往往会有几十甚至上百个工具:不同数据源的检索、不同部门的业务 API、不同技术域的生成/分析能力,这就引出了一个新的挑战: **模型如何在多工具环境下做合理的选择和编排** 。 + +* **工具选择与路由** + 首先,模型需要判断“当前请求是否需要调用工具”,以及“需要调用哪一个(或哪几个)工具”。这通常通过在系统提示中列出可用工具的说明,并提供典型示例,让模型学会根据用户意图选择合适工具。 + 对于工具数量较多、描述相似度较高的场景,很多框架会引入“工具路由器”(如基于向量检索或规则的前置筛选),先从大列表中筛出若干候选工具,再暴露给 LLM 选择,从而降低模型负担和误选概率。 +* **多工具顺序与组合** + 复杂任务往往需要多个工具协同完成。例如“调研某行业主要上市公司,并生成一份包含财务对比图表的报告”,可能涉及搜索引擎、财报数据库、计算引擎、图表生成工具、文档导出工具等。 + 在这种情况下,模型需要做一个轻量级的任务规划:先用哪个工具获取列表,再对列表逐个查询详细信息,之后合并数据、做计算与可视化,最后调用导出工具生成报告。典型实践包括 ReAct/Planner‑Executor 思路,让模型在“思考(Plan)—调用(Act)—反思(Reflect)”的循环中,逐步完成工具组合调用。 + +### 7.1.3 典型工具类型:从检索到媒体生成的能力拼图 + +不同类型的工具,为 Agent 系统提供了不同维度的“外接大脑”。从工程实践来看,以下几类工具几乎是所有复杂应用的“标配”。 + +* **检索工具:向量与关键词搜索** + 检索工具负责把“记忆”扩展到外部世界: + * 关键词搜索适合结构化较好、字段清晰的传统文档和业务数据库。 + * 向量搜索则通过嵌入(embedding)为非结构化文本、代码、对话记录、甚至多模态数据建立语义索引,支持“模糊但语义相关”的检索。 + 在 RAG 场景中,LLM 通过检索工具拉取与用户问题相关的上下文,再在此基础上进行推理与生成,大幅提升回答的时效性和准确性。 +* **代码执行与计算引擎** + 代码执行类工具(如 Python/JS 沙箱、Notebook 执行器)让 LLM 可以“写一段代码并立即跑起来”,解决复杂计算、数据处理、数值模拟、可视化等问题。 + 模型负责产出代码与输入参数,执行环境负责安全隔离、资源限制与结果收集。这类工具在数据分析、量化研究、自动化报表、科学计算以及 Agent 自我验证(模型生成答案后用代码校验)等场景中非常关键。 +* **文件与数据源访问** + 文件读写工具负责将外部文件系统和数据源引入到 Agent 视野中:读取 PDF/Word/Excel、访问数据库表、调用内部业务 API 等。模型通过这些工具获取真实业务数据,再进行归纳、对比和报告生成。 + 与之配套的还有文件写入与管理工具:将生成的报告、图表、PPT、代码等持久化存储,并返回链接或 ID,方便用户后续访问与集成。 +* **媒体生成与处理工具** + 媒体生成工具则为 Agent 增添了“创作”和“设计”的手臂: + * 图像/视频生成与编辑:根据文案自动生成配图、海报、分镜,或对已有媒体进行裁剪、上字幕、加水印等。 + * 音频生成与处理:TTS、配音、音乐生成、音频增强与剪辑。 + * 3D / 工程类工具:生成简单 3D 场景、CAD 草图、UI 原型等。 + 在内容生产、营销设计、教育培训、游戏与多媒体应用中,这类工具让“从想法到成品”更接近一条自动化流水线。 + +综合来看,工具调用与执行把 LLM 从“语言模型”扩展为“具备行动接口的通用控制器”:模型通过语言理解需求与环境,通过工具执行真实操作,通过反馈不断修正策略。搭配合适的工作流编排与多 Agent 协作(见 7.2),就构成了新一代智能应用的基础架构。 + +## 7.2 工作流编排与多 Agent 协作(Workflow & Orchestration) + +有了工具调用能力,LLM 不再只是一个“回答问题的人”,而可以成为面向具体任务的“执行单元”。但现实业务往往远比单次对话复杂:一个完整的诉讼分析、一次市场调研、一轮 A/B 实验配置、一次端到端运维处理流程,通常都需要多步操作、多种工具、甚至多方角色长期参与。这时,单一 LLM + 工具的模式就显得吃力,需要进一步的 **工作流编排与多 Agent 协作** 。 + +从系统视角看,这一层的职责是: **把一个复杂的、多步骤、多参与方的业务流程,抽象成可被 LLM 理解与操控的工作流图** ,然后在这个图上调度一个或多个 Agent,配合人类干预,共同完成任务。典型实现包括 Planner‑Executor 型 Agent 架构、具备反思 / 自我修正能力的 Agent、以及基于图结构的 Workflow Orchestrator;相应的产品形态则是各类自动报告生成与运营自动化平台、低代码工作流 + LLM 集成、复杂业务流程机器人、自动运维系统等。 + +* **场景** + * 报告与内容流水线:从“接收需求 → 检索与数据拉取 → 分析和可视化 → 撰写报告 → 审核修改 → 导出与分发”,将多步内容生产流程自动化或半自动化。 + * 业务流程自动化:如电商运营中的“商品分析 → 竞品监控 → 活动策略生成 → 落地配置”,运维场景中的“监控告警 → 根因分析 → 缓解措施执行 → 复盘报告”等。 + * 跨角色协作:让不同领域 Agent(法律、财务、技术、运营)围绕一个复杂项目协同工作,例如并购尽调、投融资材料准备、大型项目标书编制。 +* **原理** + 工作流与多 Agent 协作的核心,是在 LLM 之上再加一层 **结构化控制与状态管理** : + * 将复杂任务拆分为若干有依赖关系的子任务,用 DAG / 状态机 / 有向图等结构表示,并为每个节点配置触发条件、输入输出和所需 Agent/工具。 + * 由 Planner 型 Agent 或上层 orchestrator 决定何时触发哪个节点、用哪个 Agent 或工具,并根据执行结果动态调整后续路径(条件分支、循环、错误回退)。 + * 在关键环节引入人类在环(Human‑in‑the‑loop),对高风险决策和关键输出进行人工确认与编辑,并将人类反馈回流到系统,用于更新策略或微调模型。 +* **模型** + 支撑这一层的主要技术方向包括: + * Planner‑Executor 型 Agent 架构:由一个“规划 Agent”负责任务分解与路径设计,一个或多个“执行 Agent”负责具体步骤的落地实施。 + * 反思 / 自我修正 Agent:在执行过程中不断回顾自己的表现,对不合理的中间结果进行反思和修正,减少“自信错误”的静默扩散。 + * Graph‑based Workflow Orchestrator:将整个任务流程建模为图结构,引入节点状态、边条件、并行/串行控制等机制,使 LLM 调用变成图中的一个或多个节点,而不是唯一的控制中心。 + +### 7.2.1 任务分解与规划:从“一句话需求”到可执行流程 + +用户给 Agent 的通常是一句高度压缩的自然语言需求,例如“帮我做一个关于新能源车行业的市场调研并输出 PPT”,背后实际包含了检索、筛选、分析、可视化、排版、多轮修改等大量步骤。如何从这句话出发,自动构建一条清晰、可执行的工作流,是工作流编排的第一步。 + +* **从自然语言到子任务图** + Planner 型 Agent 首先需要把需求“展开”:结合内置模板、历史案例、以及工具清单,识别出关键阶段(如信息收集、数据分析、结构设计、内容撰写、审校与导出),并进一步细化为可执行子任务(如“检索 5 篇近一年权威行业报告”“拉取近 3 年销量数据并按车型细分”“生成 3 张对比图表”等)。 + 这些子任务之间的依赖关系和调度逻辑,会被显式表示为一张图或一个状态机:哪些可以并行、哪些必须顺序执行、在哪些节点需要人工确认、在什么条件下需要回退或重试。 +* **条件分支、循环与异常路径** + 真实流程往往并不是线性流水线,而是包含 **条件分支** (如“如果检索不到足够高质量报告则换关键词或换数据源”)、 **循环** (如“持续尝试改写和压缩,直到报告长度满足限制”)和 **异常路径** (如“某个数据源不可达时,切换到备选源或采用估算方法”)。 + 这要求工作流编排层能够在图结构上表达 if/else、while/for、try/catch 等控制流语义,并允许 Planner Agent 或上层 orchestrator 在运行过程中根据实时结果做决策,而不仅仅在开始时一次性规划好所有步骤。 +* **与工具调用的衔接** + 任务分解与规划与 7.1 中的工具调用是紧密相连的:Planner 在生成子任务时,往往会同时指定“该任务需要用到哪些工具/Agent”和“该节点的输入输出格式”,为后续自动参数填充和工具执行打基础。 + 一些系统会采用“Plan + Execute”显式两阶段:先由 Planner 输出一个机器可读的计划(如 JSON 工作流描述),再由 Executor 严格按计划调用工具与 Agent;也有系统采用 ReAct 风格,将“思考–工具调用–观察–再思考”编织在同一对话中,以获得更灵活的自适应执行。 + +### 7.2.2 多 Agent 协作:让“虚拟团队”各司其职 + +单个大模型固然强大,但在复杂业务场景中,不同领域往往需要不同的知识结构、风格偏好和安全策略。**多 Agent 协作**的思路,是把一个“大而全”的智能拆解为多个“专而精”的角色:有人负责规划,有人负责执行,有人负责审校,有人负责领域专业判断,形成一个由 Agent + 工具 + 人类共同组成的虚拟团队。 + +* **角色分工:规划、执行与审校** + 在一个典型的多 Agent 流程中,常见角色包括: + * 规划 Agent:负责理解用户需求、设计整体计划、拆分子任务,并在执行过程中根据结果动态调整路径。 + * 执行 Agent:围绕某些工具或子领域进行深度优化(如检索 Agent、数据分析 Agent、内容撰写 Agent),按规划要求完成具体步骤。 + * 审校 Agent:从结构性、逻辑性、风格一致性和风险控制等角度,对中间和最终产出进行检查和修订,类似“虚拟编辑/Reviewer”。 +* **领域专家 Agent 协同** + 对于法律、金融、技术、运营等专业性极强的领域,可以进一步细分出领域专家 Agent:如“法律顾问 Agent”“投研分析 Agent”“云原生运维 Agent”“广告投放优化 Agent”等。 + 它们可以基于领域专用知识库、工具、甚至专门微调模型,参与项目式协作:例如在一份投融资材料中,由技术 Agent 负责技术可行性部分,财务 Agent 负责财务模型与估值,法律 Agent 负责合规与风险披露,运营 Agent 负责市场与增长策略,再由总控 Agent 汇总和统一风格。 +* **协作协议与消息路由** + 多 Agent 协作的关键,还在于“谁在什么时候跟谁说话”。系统需要一个消息路由与协调机制: + * 决定某条用户请求或中间结果应当被哪个 Agent 处理。 + * 维护共享上下文与各自的私有记忆。 + * 控制并行与串行执行,以及冲突解决(如不同 Agent 提出相互矛盾的建议时如何仲裁)。 + 这类能力通常由上层 orchestrator 或“管理 Agent”提供,而 LangChain、AutoGen 等框架则在工程层面提供了对话路由、多 Agent 会话、角色设定等基础设施。 + +### 7.2.3 人类在环(Human‑in‑the‑loop):把风险关口握在手里 + +即便工作流与多 Agent 协作再智能,真实业务中仍然无法完全脱离人类判断,尤其在**高风险、高成本、高敏感度**的场景下,如法律合规、金融决策、医疗建议、大规模生产变更、舆情响应等。**人类在环(Human‑in‑the‑loop)** 的设计,正是要在自动化与可控性之间找到平衡:该自动的自动,该人工确认的一定要停下来让人看一眼。 + +* **关键步骤人工确认** + 在工作流图中,通常会显式标记若干“人工审批/确认节点”: + * 例如在自动生成合同时,在签发前需要法务和业务负责人双重确认; + * 在自动运维系统中,对涉及生产环境变更、批量重启、配置修改的操作,必须有值班工程师点击确认; + * 在内容生成场景中,对大量公开发布或品牌敏感的内容,需要人工审稿。 + Orchestrator 会在这些节点暂停自动执行,将中间结果发送给对应人类角色,并在收到反馈后再继续后续流程。 +* **反馈驱动的策略更新** + 人类不仅在某一时刻“按下通过或驳回”,更重要的是反馈的内容可以被系统吸收: + * 将人工修改后的版本与原始输出对比,作为“正负样例”记录下来,用于后续的提示优化或模型微调。 + * 基于统计分析,识别出哪些类型的任务/步骤最容易被人工反复修改,进而优化对应 Agent 的提示词、工具组合或工作流设计。 + * 在极端或异常案例中,人工可以添加“黑名单 / 白名单 / 特殊规则”,直接影响系统在类似情况中的策略选择。 +* **风险分级与可观测性** + 最后,人类在环还需要一套清晰的风险分级和可观测性机制: + * 根据任务类型、影响面、金额规模、涉及的敏感信息等维度,将流程分为不同风险等级,对应不同强度的人类介入(如只读审阅、强制审批、多级审批)。 + * 通过日志、审计、可视化看板等方式,让运营/管理人员能够随时追踪哪些任务在跑、跑到哪一步了、哪些地方触发了人工介入、历史上出现过哪些失败与人工修正。 + 这些能力不仅提高了系统在企业内的可接受度,也为后续的合规审查和责任划分提供了基础。 + +综合来看,工具调用与执行(7.1)解决的是“单步行动”的问题,而工作流编排与多 Agent 协作(7.2)则试图回答“如何把很多步串起来,让不同角色长期协作并可控运行”。两者叠加,再加上人类在环与良好的工程实践,构成了面向真实业务场景的新一代智能应用底座。 + +# 8. 检索增强与知识层(Retrieval & Knowledge) + +在前面的视觉与理解层中,模型主要依赖“自身参数里学到的知识”来理解和生成内容。但在真实业务里,很多问题并不能只靠“记忆”解决:企业内部制度每天在变、法规和行业标准持续更新、某个客户的历史记录只存在于内部数据库。这时,仅靠模型“背过”的知识远远不够,更关键的是能否在 **外部知识库、结构化数据和图谱上进行高效检索与推理** 。 + +可以把这一层理解为:在模型能力之上,再加一层“会查资料、会用数据库的外脑”。当用户提出问题时,系统不再直接生成答案,而是先去合适的数据源里“翻资料”:文档库、数据库、搜索引擎、知识图谱、日志与业务系统……然后再让模型基于真实检索到的内容来给出回答与决策。这样不仅能显著提升准确性和时效性,还能在很大程度上提升可解释性和合规性(例如可引用出处、保留执行 SQL 记录等)。 + +围绕这一层,常见能力大致可以分为两个方向:一是 **检索增强生成(RAG)** ,主要面向“自然语言问答 + 文档/知识库检索”;二是 **结构化数据与知识图谱(Structured Data & KG)** ,负责对数据库、图数据库和领域知识中台进行更精准、可控的访问与推理。下面分别展开。 + +## 8.1 检索增强生成(RAG) + +RAG(Retrieval‑Augmented Generation)可以看作是“会查资料的 LLM”。与纯粹依赖模型内部参数不同,RAG 在回答每一个问题前,都会先去外部知识库做检索,把与问题最相关的若干段文档片段(chunk)找出来,然后再把这些检索到的内容作为“上下文”喂给 LLM,让它在“看过资料”的基础上生成答案。对于企业知识库问答、行业报告搜索、法律/医疗/金融专业问答、内部文档搜索机器人等场景,RAG 已经成为默认范式。 + +在系统架构上,典型 RAG 可以拆解为三层: **索引构建层、检索层、生成层** 。前两层主要是“查得准”,后一层则负责“说得清”。下面从这三层来展开,并在二级小节中进一步细化核心设计与实践。 + +* **场景** + * 企业内部知识问答:员工用自然语言提问制度流程、技术文档、项目资料,系统基于内部文档与 Wiki 检索相关内容后,由 LLM 生成清晰回答并附带引用。 + * 行业报告与研究搜索:在大量 PDF、报告和论文中检索某个行业问题的相关内容(如“新能源车补贴政策变化”),并自动总结、对比和列出处。 + * 法律 / 医疗 / 金融领域问答:基于法规条文、判决文书、临床指南、产品说明书等权威材料进行检索增强,降低“胡编乱造”的风险。 + * 内部文档 / 工单搜索机器人:帮助运营、客服、研发快速在知识库、工单和变更记录中定位答案,并以自然语言总结结果。 +* **原理** + RAG 的核心思想是把“知识存贮在外部,推理交给模型”: + * 将非结构化文档(PDF、网页、Word、技术文档等)拆成适合检索的文档块(chunk),用 Embedding 模型将其映射到向量空间,并构建向量索引(如 FAISS、Milvus、PGVector 等)。 + * 在用户查询时,同时利用语义向量检索与关键词检索(Hybrid Search),找到与问题最相关的若干文档块,并根据相关性和覆盖度做重排序(Re‑ranking)。 + * 将检索到的上下文、用户提问、以及必要的系统指令/格式约束一起输入 LLM,由模型在“可见证据”的约束下进行回答,并在输出中引用出处(source citation),以提升可解释性和可审计性。 +* **模型** + 典型 RAG 系统往往是一个 **模型组合架构** : + * Embedding 模型:用于将查询和文档块编码到同一个语义空间,是向量检索效果的关键(包括通用 Embedding 和领域定制 Embedding)。 + * 检索与重排模型:Hybrid Search(如 BM25 + Vector)负责第一轮召回,Cross‑Encoder Re‑ranker 或 LLM 本身用于对召回结果做更精细的重排序。 + * 生成模型:LLM 在给定检索上下文的前提下进行回答;在更复杂的 RAG / HyDE / ReAct + RAG 中,LLM 还会参与“伪文档生成”“多轮工具调用”“思考 + 检索交替”等过程,以提高召回、减少遗忘和增强推理能力。 + +### 8.1.1 索引构建与知识资产整理 + +在任何 RAG 系统中,索引构建都是基础。没有高质量的索引,后续再强大的 LLM 也只是“巧妇难为无米之炊”。索引构建的目标,是把杂乱无章的文档资源转化为“可检索、可维护、可扩展的知识资产”。 + +从流程上看,典型索引构建包括以下几个关键步骤: + +1. **文档分块与预处理** + 文档往往是长篇 PDF、PPT、Word 或网页,如果直接对整篇文档做向量化,既容易造成“稀释”(一篇文档包含多个主题),也不利于高效检索。因此需要: + 1. 按段落、标题、页码、章节结构进行分块,平衡“语义完整度”和“块大小”; + 2. 处理格式问题(表格、公式、图片中的文字 OCR)、去噪(页眉页脚、目录、版权信息等); + 3. 为每个块生成“上下文标签”(如所属文档、章节标题、页码),为后续解释与引用做好准备。 +2. **Embedding 与向量索引** + 在分块基础上,对每个文档块生成语义向量: + 1. 选择合适的 Embedding 模型(如通用语义 Embedding、领域微调模型),确保对目标语言和领域术语有良好表达能力; + 2. 使用 FAISS、Milvus、PGVector 等构建高维向量索引,支持大规模数据下的近似最近邻检索; + 3. 处理多版本与增量更新:当文档更新时,需要支持增量重建索引、版本记录和旧版本清理策略。 +3. **元信息索引与过滤** + 单纯的语义向量并不足以应对复杂过滤需求,通常还需要构建 **元信息索引** : + 1. 为每个文档块补充时间、作者、来源、文档类型、业务线、敏感级别等元数据; + 2. 支持在检索时基于元信息进行预过滤(如时间范围、部门、权限等级),减少无关结果; + 3. 为权限控制与审计打下基础,避免 RAG 在回答中泄露用户无权访问的内容。 + +### 8.1.2 检索与重排序:从“召回相关”到“找到最合适的证据” + +在索引构建完成后,当用户发起查询,就进入检索与重排序阶段。这里的关键不只是“找一些相关文档”,而是要尽可能找到 **既相关又覆盖充分、且支持推理的证据组合** 。 + +1. **Hybrid 检索:向量 + 关键词的互补** + 纯向量检索擅长捕捉语义相似度,但对于精确术语、代号、表格字段等,关键词检索(如 BM25)往往更稳健。因此工程实践中普遍采用 Hybrid Search: + 1. 首先对查询分别进行向量检索和关键词检索,得到两组候选文档块; + 2. 使用加权打分或学习到的融合策略,将两路候选合并; + 3. 在一些场景中,可根据查询类型(FAQ 问答 vs. 法条定位)动态调节向量与关键词检索的权重。 +2. **重排序(Re‑ranking):更精细地挑选“证据集”** + 初始检索结果往往包含不少“边缘相关”或“冗余”文档块,需要重排序来提升最终 Top‑K 的质量: + 1. 使用 Cross‑Encoder(交叉编码器)对“查询–文档块”对进行双向编码和相关性打分,相比双塔 Embedding 模型精度更高,但开销较大,适合作为二阶段重排; + 2. 在性能允许时,引入 LLM 进行轻量级重排,让模型基于更丰富的语义和上下文信息来判断哪些块真正“有用”; + 3. 同时考虑覆盖度与多样性,避免所有检索块都集中在同一文档或同一段落,从而导致回答视野过窄。 +3. **检索–生成闭环优化** + 更高级的实践中,检索和生成不再是单向流程,而是形成闭环: + 1. 利用 LLM 对检索结果的“使用情况”进行分析(哪些块被引用、哪些块总是被忽略),反向指导索引和分块策略的优化; + 2. 利用对话日志中的“追问/纠错”信号,对召回失败、误召回的样本进行标注和再训练,提高系统对模糊查询、长尾问题的鲁棒性。 + +### 8.1.3 生成与引用:在“证据约束下”回答问题 + +最后一环是生成层,它直接决定了用户体验。这里的目标不是让模型“随心所欲”地发挥,而是让它在 **检索证据的约束下,给出清晰、有边界、有引用的回答** 。 + +1. **基于检索上下文的受控生成** + 在 RAG 架构中,LLM 接收到的不只是用户问题,还包括多段检索到的文档块以及系统指令。系统通常会: + 1. 通过 Prompt 约束模型“只根据给定文档回答”“如果文档中找不到答案就明确说明缺失”; + 2. 对检索上下文进行结构化组织(分段、编号、标注来源),方便模型理解与引用; + 3. 控制输出格式(列表、表格、分点说明等),适配下游系统或前端展示。 +2. **引用与可解释性(Source Citation)** + 为了便于审计与追溯,尤其在法律、医疗、金融、企业内部制度等高风险领域,回答中往往需要附带明确引用: + 1. 在输出中标注引用来源,如“[文档 A,第 3 章,第 2 节]”“[法规 X 第 12 条]”; + 2. 在前端界面中支持一键跳转到原文位置,便于用户核查和进一步阅读; + 3. 在后台保存“问题–检索结果–引用块–最终回答”的完整链路日志,为后续风控和模型改进提供数据。 +3. **先进 RAG 变体:HyDE / ReAct + RAG 等** + 为进一步提升难题场景下的效果,实践中还会使用更复杂的 RAG 变体: + 1. HyDE:由 LLM 先根据问题生成一个“假想答案文档”,再用该文档向量去检索真实文档,从而提高召回质量; + 2. ReAct + RAG:LLM 以“思考(Reasoning)+ 行动(Action)”的方式,在推理中多次调用检索工具,逐步细化问题、补充证据,类似“边思考边查资料”; + 3. 多轮 RAG:在对话过程中,保留历史检索结果和回答,形成上下文感知的长期知识会话,而不仅是“单问单检索”。 + +## 8.2 结构化数据与知识图谱(Structured Data & KG) + +如果说 RAG 主要解决“如何在大规模非结构化文档中查资料”,那么结构化数据与知识图谱这一层,则更多面向“如何优雅地用好数据库、报表系统和图数据库中的结构化知识”。 + +在企业环境中,真正关键的业务数据——订单、客户、合同、库存、行为日志——往往以关系数据库、数据仓库、OLAP 引擎或图数据库的形式存在。这些系统在查询能力、计算效率和审计方面已经非常成熟,但对于业务人员而言,直接写 SQL / DSL 仍然门槛较高。**Text‑to‑SQL / Text‑to‑DSL** 与 **知识图谱问答与推理** ,就是要让 LLM 在不破坏这些系统稳定性的前提下,作为“自然语言界面”和“推理协作伙伴”插入进来。 + +* **场景** + * BI 智能问答与自助分析:业务人员用自然语言发问(如“帮我看看最近 3 个月华东地区新客的复购率趋势”),系统自动生成 SQL,查询数据仓库,然后用自然语言和可视化图表返回结果。 + * 运营 / 销售分析助手:运营同学可以用对话的方式探索数据(“这个活动转化率为什么下降”“哪些渠道贡献了最多高价值用户”),在多轮对话中逐步细化条件和维度。 + * 领域知识中台:将实体、概念、规则和案例组织为知识图谱,支持围绕某个实体进行上下游关系探索和合规性检查。 + * 图数据库问答与推理系统:在风险控制、反洗钱、供应链分析等场景中,通过图数据库与 LLM 联合,对“关系链条”和“多跳推理”类问题进行回答与解释。 +* **原理** + 这一层的核心,是把 LLM 从“直接给答案的人”变成“会调用数据库与图数据库的助手”: + * 在数据库问答中,模型需要理解用户的自然语言意图,结合数据库 schema(表结构、字段含义、约束等),生成正确的 SQL / GraphQL / 内部 DSL,再对执行结果进行解释与可视化。 + * 在知识图谱场景中,系统需要先从文档和日志中抽取实体和关系,构建结构化图谱;然后在问答时由 LLM 负责把自然语言问题转译为图查询(如 Cypher),并基于查询结果进行多跳推理和解释。 + * 与 RAG 不同,这里强调的是 **对结构化数据与图结构的精确访问** ,一方面要保证语义正确、语法严谨,另一方面要控制侧写攻击、敏感数据暴露和高成本查询。 +* **模型** + 典型方案通常是“LLM + 专用组件”的多模块架构: + * Text‑to‑SQL 模型:在大规模 SQL 语料上预训练或微调的模型(如 PICARD、DIN‑SQL 等),侧重语法正确性与 schema 对齐,有时会搭配执行反馈进行自我修正。 + * 信息抽取与图谱构建 pipeline:通过实体识别(NER)、关系抽取、事件抽取等模块,从文本和日志中构建和更新知识图谱;LLM 可以参与难例抽取、边界模糊关系的辅助判断。 + * LLM + 图数据库联合问答:LLM 负责问题解析、查询生成与结果解释,图数据库(如 Neo4j 等)负责高效执行与多跳关系搜索,两者通过工具调用协议或中间 DSL 对接。 + +### 8.2.1 数据库问答(Text‑to‑SQL / DSL)实践 + +数据库问答的目标,是让业务人员“用自然语言问数据”,而系统在背后自动完成查询语句生成、执行与解释。要把这件事做好,关键在于兼顾 **语义准确性、语法正确性和执行安全性** 。 + +1. **自然语言到 SQL / DSL 的转换** + 在最基础的链路中,系统需要: + 1. 解析用户意图:识别出查询对象(如“华东地区新客”)、过滤条件(时间、地区、渠道)、聚合方式(总数、平均值、同比/环比)和展示需求(趋势、排行、Top‑N); + 2. 结合数据库 schema:理解哪些表与字段可以表达上述概念,如何进行关联(join)、分组(group by)和排序; + 3. 生成可执行的 SQL / GraphQL / 内部 DSL,并通过语法校验器或专门的 Text2SQL 模型(PICARD、DIN‑SQL 等)确保结构合法。 +2. **执行结果的自然语言解释与可视化** + 查询执行后,系统还需把“冷冰冰的结果集”变成“可理解的洞察”: + 1. 对简单结果进行文本解释,如“过去 3 个月华东地区新客的复购率整体呈上升趋势,从 15% 提升到 21%”; + 2. 对复杂结果选择合适的可视化形式(折线图、柱状图、饼图、分布图等),并给出简要分析; + 3. 支持用户基于当前结果继续追问(如“这波增长主要来自哪些渠道?”),自动在历史 SQL 和上下文的基础上构造新的查询。 +3. **安全与控制:防止“乱查”和“越权”** + 由于 LLM 生成的 SQL 具有高度灵活性,必须有一层安全与治理机制: + 1. 基于用户角色与权限,对可查询的库、表、字段和时间范围做严格限制; + 2. 为模型生成的 SQL 配备静态/动态审查规则,过滤危险操作(如大范围扫描、高成本 join、跨租户查询等); + 3. 将“自然语言问题–生成 SQL–执行结果–最终回答”完整记录,用于审计与异常分析。 + +### 8.2.2 知识图谱构建与查询 + +知识图谱试图把散落在文本、表格、日志中的知识,组织成“实体–关系–属性–事件”的结构化网络,从而更好地支持 **关系探索、多跳推理和复杂问答** 。在这一方向上,LLM 与传统信息抽取、图数据库形成了良好的互补。 + +1. **从文档中抽取实体和关系构建图谱** + 构建知识图谱通常采用多阶段 pipeline: + 1. 信息抽取:利用 NER、关系抽取、事件抽取等模型,从文本中识别实体(人、机构、产品、地名、概念等)、它们之间的关系(隶属、合作、依赖、因果)以及关键事件(交易、风险、变更); + 2. 规范化与对齐:将同一实体的不同表述(简称、别名、拼写变体)进行归一,对齐到统一 ID; + 3. 图谱更新与版本管理:支持增量更新、冲突解决和错误纠正,确保图谱在长期演化中保持质量与一致性。LLM 可以在歧义消解、关系类型细化、规则归纳等环节辅助传统算法。 +2. **LLM + 图数据库(Neo4j 等)的查询与推理** + 当图谱构建完毕,图数据库负责高效存储和检索,而 LLM 则可以扮演“自然语言入口 + 推理控制器”的角色: + 1. 问题解析与图查询生成:将自然语言问题转译为图查询语句(如 Neo4j 的 Cypher),包括确定起点实体、关系类型、路径长度与过滤条件; + 2. 多跳推理:通过图查询得到的路径和局部子图,再由 LLM 进行解释与归纳,如“客户 A 与高风险实体 B 之间通过三家公司间接相连”; + 3. 结果可视化与可解释性:将图查询结果以可视化网络形式呈现,同时由 LLM 给出口头说明,帮助用户理解复杂关系结构。 +3. **领域知识中台与统一服务** + 在更大规模的企业或行业级应用中,知识图谱往往作为“领域知识中台”存在: + 1. 为上层业务系统(风控、合规、客户 360 视图、供应链分析等)提供统一的实体和关系视角; + 2. 与 RAG、数据库问答共同构成统一的知识服务层,由统一的 LLM 编排逻辑决定当前问题该访问文档索引、关系数据库还是图数据库; + 3. 在安全和合规要求下,通过图谱层面的访问控制和脱敏策略,进一步降低敏感信息泄露的风险。 + +这一层的共同目标,是把“模型会说话”升级为“模型既会说话,又真正接上了企业的真实数据与知识资产”。当 RAG、Text‑to‑SQL、知识图谱与传统数据基础设施有效结合之后,AI 系统才能在复杂业务环境中既保持智能和灵活性,又具备可控性、可解释性和长期演化能力。 + +# 9. 安全、对齐与评估(Safety / Alignment / Evaluation) + +在前面的章节里,我们更多从“模型能做什么”出发:能看图、能写代码、能和用户对话。但在真实的大模型系统中,仅仅“有能力”远远不够:**怎么证明这些能力是稳定、可靠、可控的?怎么确保输出符合价值观和合规要求?在长周期运营中如何持续监控、迭代与回归?** + 这一层关注的就是: **能力评估与基准测试、价值对齐与训练、内容安全与合规、以及鲁棒性与幻觉控制** ,共同构成一个可持续运营的大模型“基础设施层”。 + +从产品视角看,这些能力贯穿模型全生命周期:模型在实验室阶段需要标准 Benchmark 与专业评估;上线前要通过对齐训练与安全审查;上线后依赖内容安全网关、日志审计与 A/B 测试持续监控;面对新场景与新威胁时,又要回到评估与对齐环节重新训练和验证。下面我们从**能力评估与基准测试、价值对齐与训练、内容安全与合规、鲁棒性与幻觉控制**四个方向展开。 + +## 9.1 能力评估与基准测试(Capability Evaluation & Benchmarks) + +在大模型研发和落地过程中,**能力评估与基准测试**是把“模型能力”转化为“可观测信号”的关键一环:既要回答“这个模型总体水平怎么样”,也要回答“在某个专业领域、某种真实业务场景下表现如何”。一方面,我们通过标准化的基准集与自动评测体系,去衡量模型在**语言理解与生成、推理与数学、知识与事实性**等通用维度上的表现;另一方面,还需要针对**医疗、法律、金融、教育**等专业领域构建专门评测,并在**真实用户对话、AB 测试和业务指标(Task Success Rate、CSAT、工单关闭率等)中不断验证与修正。整体上,这一层最终会沉淀为内部的能力评估平台**与对外的“ **能力说明书** ”,并为多版本、多租户、多场景的模型选型提供统一决策依据。下面从 **场景** 、 **原理** 、**模型**三个角度展开。 + +* **场景** + * **通用能力评估场景** :在基础模型或大版本更新时,需要系统性地评估其在阅读理解、摘要、翻译、对话质量等**语言理解与生成**任务上的表现,以及在算术、多步推理、代码/逻辑题等**推理与数学**任务中的能力,同时通过事实问答、开放域 QA、知识覆盖度任务衡量其**知识与事实性**水平,用于判断“新模型是否整体抬升”。 + * **专业领域评估场景** :对于医疗、法律、金融、教育等细分领域,需要设计专业问答与决策模拟,比如疾病问答与分诊建议、法律条文理解与案例归类、投融资分析与风控判断、教学答疑与作业辅导,并在**多语言、多文化环境**下测试模型的一致性与稳定性,确认其能否在高风险环境中“说对话、说适当的话”。 + * **真实场景与业务指标评估场景** :在产品上线和持续运营阶段,通过用户对话日志回放、线上 AB 测试等方式,将模型表现映射到 **任务完成率(Task Success Rate)** 、 **用户满意度(CSAT)** 、**工单关闭率**等业务指标;此时评估对象实际是“模型 + 策略 + 产品流程”的整体系统,用于指导版本回滚、策略调优和新功能放量。 +* **原理** + 能力评估体系可以看作一个分层的“测量系统工程”,其核心原理包括: + * **标准基准集:公共刻度与可复现实验** + * 语言 / 推理:使用 **MMLU** 、**BIG-Bench** 等综合性任务,配合 **GSM8K** 、**MATH** 等数学与逻辑题目,构建对语言理解、知识掌握、多步推理的统一刻度。 + * 编程:通过 **HumanEval** 、 **MBPP** 、**Codeforces** 题库等,量化代码生成、程序修复与问题求解能力。 + * 多模态:利用 **VQA** 、 **MMBench** 、 **ScienceQA** 、**MathVista** 等基准测试图文理解、视觉问答和图像中的数学推理。 + 这些基准强调 **标准化、可复现、可对比** ,便于跨模型、跨机构进行横向对比和对外披露。 + * **自动评测:规模化与持续回归** + * **LLM-as-a-Judge** :用更强或专门训练的模型对回答进行打分/排序,评价正确性、完整性、风格和安全性,实现大规模自动主观评测。 + * **基于规则的度量** :如 BLEU / ROUGE / BERTScore 衡量文本相似度,Pass@k 衡量代码题通过率等,使得在固定数据集上可以快速比较不同版本的差异。 + 自动评测的关键在于 **稳定性与一致性** ,即便不完美,只要“偏差一致”,就可以在持续集成(CI)中可靠地反映模型相对变化。 + * **人工评测:对齐人类感知与业务目标** + * **Pairwise 对比与打分标注** :由标注员对 A/B 两个模型回答做 pairwise 选择或多维度打分(helpful / honest / harmless 等),是训练 RLHF / RLAIF 奖励模型的重要数据来源。 + * **线上用户实验** :通过对话助手、搜索/推荐等落地场景做 AB 测试,直接观察不同模型 / 策略对用户满意度、转化率等指标的影响。 + 人工评测既用于 **校准自动评测** ,也是对外“解释模型行为”时的重要依据。 +* **模型** + 在工程实践中,能力评估会沉淀为一套相对完整的“平台 + 流程 + 指标体系”: + * **内部能力评估平台与 CI 流水线** :统一管理各类基准集、评测脚本、LLM-as-a-Judge 配置与人工标注工具,支持新模型或新策略提交后一键触发 Benchmark 回归;自动汇总不同任务和维度的指标变化,提供可视化 Dashboard 与回归告警。 + * **对外“能力说明书”与模型画像** :将内部评估结果整理为对外可消费的“能力说明书”,包括代表性基准成绩、推荐适用场景(如通用对话、代码辅助、多模态理解等)、已知局限与不适用场景,帮助客户形成正确预期,也为合规和责任划分提供依据。 + * **多租户 / 多版本模型统一评测与选型工具** :在同一套评估体系下,统一比较不同尺寸、不同对齐策略或不同架构的模型,支持按行业、地区、SLA 要求配置权重,自动生成“性能–成本–延迟”综合评分,帮助产品和业务方做模型选型与灰度发布决策。 + +### 9.1.1 通用与专业能力评估:从 Benchmark 到场景验证 + +通用与专业能力评估是整个评估体系的“第一层地基”,重点在于:先用统一刻度衡量模型的 **基础能力** ,再在专业场景中验证其 **可用性与风险** 。 + +在通用能力评估中,通常会将任务拆分为语言理解与生成、推理与数学、知识与事实性三个维度:前者通过阅读理解、摘要、翻译、对话质量任务,检查模型是否能准确理解上下文、控制风格并输出连贯文本;中者通过算术、多步推理、代码 / 逻辑题,评估模型在复杂推理链和程序结构上的能力;后者则通过事实问答和开放域 QA 度量知识覆盖度和事实性水平。在专业领域评估中,则需要邀请行业专家参与数据设计:如医疗问答中设定病史、化验结果等上下文,要求模型在回答中给出风险提示和就医建议边界;法律任务中设计条文检索、案例比对、法律适用分析;金融与教育中则聚焦合规披露与教学引导。这一层评估往往结合标准基准集与自建数据集,既追求可对比性,也兼顾业务相关性。 + +### 9.1.2 自动评测与 LLM-as-a-Judge:让评估可扩展 + +当任务规模和模型版本数迅速增长后,仅依赖人工已经难以支撑评估需求,此时需要通过自动评测体系实现 **规模化与高频回归** 。 + +一类做法是利用传统的基于规则度量:在翻译、摘要等任务上,用 BLEU / ROUGE / BERTScore 与参考答案对比,在代码任务上用 Pass@k 测试在多个生成样本中是否至少有一个通过单测。这类指标实现简单、可高度自动化,但对答案多样性与风格细节不敏感。另一类更具代表性的做法是 **LLM-as-a-Judge** :将更强或专门训练的模型用作“打分裁判”,根据预定义的评分 Rubric,对被测模型输出进行维度化打分或 Pairwise 排序。这允许我们在没有标准答案、回答多样的开放问答和对话任务中也进行高效自动评估。实际工程中,LLM-as-a-Judge 的评分标准和 Prompt 需要经过人工标注数据校准与迭代,以确保其与人类评委的一致性。 + +### 9.1.3 人工评测与业务指标:闭环到真实用户体验 + +再完备的离线指标,也只能近似真实用户体验。为了把能力评估闭环到业务,需要引入人工评测与线上实验两类手段。 + +人工评测侧,常见的是 Pairwise 对比:让标注员在看不到模型身份的前提下,基于 helpful / honest / harmless 等维度,对 A/B 两个回答做偏好选择或打分,从而得到高质量偏好数据,一方面用于直接评估,另一方面可以为 RLHF / RLAIF 训练奖励模型提供数据。在业务侧,则通过线上 AB 测试,对比不同模型、提示词、策略配置版本对任务完成率、用户满意度(CSAT)、工单关闭率等关键指标的影响,辅以用户对话日志回放和人工抽检,持续监控模型上线后的真实表现。这一层评估的输出又会反过来指导能力评估平台的重点方向和权重调整,形成“离线指标—人工评测—线上指标”的闭环。 + +## 9.2 价值对齐与训练(Value Alignment & Training) + +在拥有强大基础能力之后,大模型要成为“安全、可靠、可控”的产品,还必须经历 **价值对齐与训练** 。这一层关注的不再是模型“能不能回答”,而是“ **回答得是否有用、诚实、无害** ”以及“在不同角色和行业中应该如何说话”。从工程角度看,对齐过程大致包括三步:首先通过文档与规范明确 **对齐目标定义(What to Align)** ,将有用(Helpful)、诚实(Honest)、无害(Harmless)拆解为可标注、可训练的标准;其次构建覆盖广泛的 **指令数据与安全数据** ,涵盖正常任务、灰区案例与不合适回答;最后通过 **SFT、RLHF / RLAIF、拒答/重定向策略建模** 等方法,将这些偏好与规则“写进”模型行为中,并辅以上游对话管理与策略引擎,实现端到端的安全对齐。下面同样从 **场景** 、 **原理** 、**模型**三个角度展开。 + +* **场景** + * **通用 C 端助手场景** :面向大众用户的聊天助手、信息检索助手,需要在广谱话题下保持“ **友好、有帮助、不越界** ”:既要回答得专业、聚焦任务,又要在不确定时坦诚表达局限,对明显不当需求进行拒答或柔性引导。 + * **专业行业助手场景** :在医疗、法律、金融、教育等领域,除了基础安全,还要叠加行业规范:例如医疗助手需要反复强调“非诊断性质 + 风险提示 + 建议就医”,法律助手要避免提供违法规避建议,金融助手要遵守投资合规披露要求,教育助手要考虑未成年保护与适龄内容。 + * **B 端可配置对齐层场景** :企业往往希望在通用安全基线之上,进一步嵌入自身的行业要求、品牌语气和内部政策,因此需要一个 **可配置的对齐层** ,允许客户自行配置安全阈值、敏感类别和话术风格,而不必重训底层大模型。 +* **原理** + 价值对齐可以理解为“用人类和组织的价值观约束模型的行为空间”,其核心原理包括: + * **对齐目标定义(What to Align)** + * **有用(Helpful)** :回答应高质量、专业、结构清晰、聚焦任务目标,不过度发散和闲聊。 + * **诚实(Honest)** :尽量不胡编乱造,在知识缺失或理解不清时主动承认不确定性、给出估计范围或建议查证渠道。 + * **无害(Harmless)** :遵守法律与平台政策,避免生成仇恨、歧视、自残鼓励、违法犯罪指导等内容,并尊重用户的尊严与边界。 + 这些目标会被写入标注指南与策略文档,成为后续数据构建、奖励建模和评测的统一标准。 + * **对齐训练数据构建** + * **指令数据(Instruction)** :设计覆盖广泛的任务指令与理想回答,涵盖问答、写作、总结、代码、规划等多种场景,教会模型在“正常请求”下的最佳行为。 + * **安全数据(Safety)** :构建“好的回答 vs 不合适回答”对照样本,特别注重灰色边界(gray zone),如科普信息 vs 具体操作、情绪支持 vs 自残鼓励、合法辩论 vs 仇恨煽动等,为模型提供细粒度的边界示例。 + * **对齐训练方法** + * **SFT(Supervised Fine-Tuning)** :在高质量对话 / 指令数据上进行有监督微调,是塑造模型基准行为和语气的第一步。 + * **RLHF / RLAIF** :通过人类或模型打分构建偏好数据,训练奖励模型,然后进行策略优化,让模型在生成时倾向于被“偏好”的回答(更有用、更安全、更诚实)。 + * **拒答 / 重定向策略建模** :针对高风险或不适当请求,训练模型不仅会拒答,还能给出合理解释并引导用户到安全替代方案(例如提供求助资源、鼓励咨询专业人士等)。 +* **模型** + 在系统设计上,价值对齐通常体现为“ **底层对齐训练 + 上层策略护栏** ”的组合: + * **SFT + RLHF / RLAIF 对齐模型** :SFT 阶段让模型学会理想回答的基本模式;RLHF / RLAIF 阶段则通过偏好学习进一步“收紧”行为,使其更贴近人类偏好与安全标准。在安全维度上,可以单独为有害性构建奖励头或分类器,用于在策略优化中施加惩罚。 + * **Constitutional AI / Policy-based Alignment** :通过先撰写一套“宪法(Constitution)”或 Policy 文档,再让模型根据这套规则进行自我批评与重写,生成大量“自监督批改数据”,在减少人工成本的同时强化模型对规则的内化。 + * **对话管理与意图检测协同** :在产品管线中,将安全 / 对齐逻辑部分上移到对话管理层,通过意图识别、槽位填充、任务路由决定请求是否交给大模型、是否需要额外的安全过滤或模板化回复。这样可以形成“模型对齐 + 策略护栏”的双重保险。 + * **内部对齐平台与角色配置** :建设内部对齐平台,提供标注 / 打分工具、策略版本管理和训练流水线;同时支持为不同角色(客服、医疗建议、教育辅导等)配置差异化对齐目标和话术风格,使同一底座模型在不同产品中展现出截然不同但可控的一致人格。 + +### 9.2.1 对齐目标与训练数据:把价值变成可学习信号 + +价值对齐的第一步,是把“抽象价值观”转译成模型可以学习的信号,而这离不开对齐目标定义和训练数据构建。 + +在对齐目标层面,团队通常会输出一套详细的行为规范文档,将 Helpful / Honest / Harmless 拆解为具体条款,如:禁止给出某类高危操作的具体步骤、对于医疗/法律建议必须附带免责声明和风险提示、在涉及争议话题时保持中立与多视角呈现等。接着,在指令数据阶段,会围绕这些指标构建多样化任务与理想回答,涵盖聊天、写作、代码、问答等场景,并融合多语言、多文化背景;在安全数据阶段,则针对有害内容、高风险领域与灰色地带,构建成对的“好 / 坏回答”示例,为后续偏好学习和安全分类器提供训练素材。通过这种方式,价值目标被“翻译”为实际数据分布,成为模型训练可以直接感知的信号。 + +### 9.2.2 SFT、RLHF / RLAIF 与拒答策略:塑形模型行为 + +有了对齐目标和数据之后,下一步是通过多阶段训练过程将这些目标写入模型行为。 + +在 SFT 阶段,模型在高质量人类示范数据上进行有监督微调,这类似于“教科书式学习”:它决定了模型在绝大多数正常请求下的语气、结构和解决问题的标准范式。随后,通过 **RLHF**** / RLAIF** 进行偏好优化:先利用人类标注或更大 LLM 产生的偏好标签训练奖励模型,再使用策略优化算法(如 PPO 等)调整模型,使其在生成中倾向于获得更高奖励。这样,模型不仅“知道正确答案长什么样”,还知道“哪种答案更符合人类偏好和安全要求”。在此基础上,还会专门建模各种 **拒答与重定向策略** :对于明显违法、极高风险或不适合由 AI 回答的问题,模型应该学会给出清晰的拒绝与解释,并提供安全的替代路径(如求助热线、专业咨询等),而不是简单沉默或随意搪塞。 + +### 9.2.3 策略层与对齐平台:让对齐可配置、可演进 + +即便底层模型已经进行了充分对齐训练,在实际系统中仍需要**策略层与对齐平台**来实现更细粒度的可控性和可演进性。 + +策略层通常包含意图识别、风险评估与路由逻辑:当用户输入到达系统时,先由轻量模型判断其意图、领域和风险等级,再决定是否直接调用大模型、是否需要额外安全过滤、是否落入模板回复或转人工渠道。对于不同行业和客户,策略层可以加载不同的 Policy 配置,实现对敏感类别、拒答风格和品牌语气的定制。与此同时,内部对齐平台会管理所有对齐相关资产:标注/打分工具、奖励模型版本、策略变更记录、在线 A/B 结果等,使团队可以在不频繁重训底座模型的前提下,对对齐策略进行快速迭代和灰度发布,从而保持对模型行为的持续掌控。 + +## 9.3 内容安全与合规(Content Safety & Compliance) + +随着大模型被嵌入到搜索、对话、内容创作、社交平台乃至企业内部系统中,**内容安全与合规**从“附加功能”变成了“准入门槛”。这一层关注的是:模型在生成文本、图像、音视频时,是否会产生违法有害内容;系统在处理用户数据时,是否符合所在国家/地区和所属行业的法律法规;以及在面对审计与监管时,能否给出清晰可追溯的证据链。为此,我们需要构建覆盖**多模态内容审核、区域与行业合规、本地隐私与数据保护**的完整技术与治理体系,并将其封装为 SaaS 内容安全服务、企业合规中台和行业安全网关等产品形态。下面同样从 **场景** 、 **原理** 、**模型**三个角度展开。 + +* **场景** + * **多模态内容审核与过滤场景** :在对话产品、UGC 平台、社区与社交应用中,大模型会生成或接收大量文本、图像、音视频内容,需要通过统一的**多模态审核**能力,实时识别并拦截涉及个人隐私、违法犯罪指导、仇恨煽动、极端暴力、色情与未成年人不当内容等高风险输出。 + * **合规约束与本地化场景** :不同国家/地区的法律法规对数据保护、未成年人保护、内容监管等有不同要求;不同行业(医疗、金融、教育、广告等)也有细化的合规规范。因此系统必须支持按**地区与行业**加载不同策略模板,以符合当地监管要求。 + * **用户隐私与数据保护场景** :在模型训练和在线服务过程中,需要处理大量用户对话和业务数据,如何实现数据匿名化、脱敏和最小采集,同时在训练和推理阶段通过技术和制度手段保护隐私,是内容安全与合规体系的另一根支柱,尤其在金融、医疗等高敏感行业。 +* **原理** + 内容安全与合规的底层原理可以分为策略、过滤和隐私三个层面: + * **安全策略系统(Policy Engine)** + * 将法律法规、平台规则、行业规范 **形式化为可执行策略** ,通过规则引擎结合模型打分,对内容进行风险分级(安全 / 灰区 / 高危)。 + * 支持按场景和客户选择不同策略模板,例如为青少年产品、专业社区或跨国企业配置不同的敏感类别与阈值。 + * **多级内容过滤:事前–事中–事后** + * **事前** :对用户 Prompt 做拦截与重写(Prompt Shielding),在请求进入大模型前阻断明显违法或高度敏感的意图,或将其引导为较为安全的表达方式。 + * **事中** :在模型生成输出时,利用安全分类模型与规则对内容进行实时审查(Real-time Safety Filter),对高风险内容进行截断、替换、打码或触发拒答。 + * **事后** :对对话和生成日志做抽样审计与人审复核,对发现的问题进行溯源分析,进而更新策略和模型,并为外部监管提供可追溯的记录。 + * **隐私保护技术与****数据治理** + * 在数据存储和训练前,对用户对话数据进行 **匿名化与脱敏处理** ,移除或替换姓名、身份证号、手机号、地址等敏感字段,并遵循**最小采集原则**只保留必要信息。 + * 在某些场景中采用**差分隐私(DP)**限制单个样本对模型参数的影响,或者通过**联邦学习(FL)**将训练留在本地数据域,避免原始数据上云。 + * 利用 **RBAC**** / ****ABAC** 等访问控制机制,严格限制谁可以访问什么级别的日志与敏感数据,并配合审计日志保证访问路径可追踪。 +* **模型** + 从产品与系统设计角度看,内容安全与合规最终会演化为一系列可复用的“安全服务与中台”: + * **SaaS 内容安全服务** :将文本 / 图像 / 音视频审核能力封装为统一 API,对接上游应用;输入内容,输出风险类型、分级和处理建议(放行、拦截、人审),帮助开发者快速集成安全模块。 + * **企业内部合规中台** :为大型企业提供集中管理的合规策略配置、审计报表和风险告警能力,对接内部的业务系统和人审团队,使各业务线在统一策略下执行自定义规则,并满足外部监管报告需求。 + * **高风险行业专用安全网关与日志审计系统** :在金融、医疗等高风险行业,通过专用安全网关代理所有大模型调用,对流量进行实时检查与脱敏,将关键日志留存在本地或合规区域,提供详尽的访问审计和事件追溯能力,满足严格的监管要求。 + +### 9.3.1 多模态审核与策略引擎:把规则变成“可执行的代码” + +实际的内容安全系统,首先要能“看懂”来自不同渠道与模态的内容,然后才能将策略落地到每一次请求与响应上。 + +在多模态审核方面,系统通常会构建文本、图像、视频等多种检测模型:文本侧模型识别敏感关键词、上下文语境和隐晦表达;图像和视频侧则检测暴力、色情、未成年人、仇恨符号和违法物品等内容,并在必要时结合 OCR、ASR 和视觉特征进行联合判断。策略引擎则把这些模型输出与法规要求绑定在一起:例如,在某一地区对赌博或政治内容有更严格限制,就可以在对应策略模板中提高相关检测类别的敏感度,或对命中这些分类的内容强制转人工复核。通过把抽象规则转化为规则链、阈值和动作(放行/拦截/人审/打码),Policy Engine 让合规要求真正“跑起来”。 + +### 9.3.2 多级过滤与日志审计:构建端到端安全闭环 + +单一环节的拦截很难覆盖所有风险,因此内容安全体系普遍采用**事前–事中–事后**三层防线的设计。 + +在事前阶段,系统会对用户输入进行快速检测,对明显违规或高度敏感的 Prompt 直接拒绝或重写,引导用户以安全方式提问;对于边界尝试和模糊请求,也可以主动补充声明和风险提示。在事中阶段,模型输出会经过实时安全过滤组件:该组件会利用文本分类和规则匹配,对潜在高危输出进行剪裁、替换或触发拒答流程,确保最终呈现给用户的内容落在可接受范围内。事后阶段,则通过日志审计与抽检机制,由安全团队或可信的自动系统定期回放与检查会话,分析误判、漏判和新型风险样式,并据此更新策略、训练数据和检测模型。这样形成一个持续演进的安全闭环,而不是“一次性配置”。 + +### 9.3.3 隐私保护与行业安全网关:让数据安全“可证明” + +在高敏感行业中,仅仅“不输出有害内容”还远远不够,还要证明“内部对用户数据的使用同样安全、合规、可追踪”。 + +隐私保护从数据进入系统开始:在采集和存储阶段就尽量进行匿名化和脱敏,确保即使日志泄露也难以直接关联到具体个人;在训练阶段,则通过差分隐私、采样策略或联邦学习减少单个用户数据对最终模型的影响和外泄风险。对于模型推理流量,则通过**安全网关**进行统一接入管控:所有请求与响应都要经过网关的内容检查、权限校验和审计记录,必要时根据业务线和用户角色应用不同的访问策略与数据视图。最终,这些日志和策略变更记录会沉淀为可供内部审计和外部监管查看的“证据链”,使企业不仅在事实上合规,而且在形式上“可证明自己合规”。 + +# 10. AI for Science(AI4Science) + +当深度学习和大模型从“推荐广告、理解自然语言”走向 **科学问题本身** ,目标不再只是预测一个指标或做一个分类,而是要真正参与到**发现规律、设计实验、加速仿真与推理**之中。AI4Science 试图把“统计模式识别”与“物理定律 / 生物化学规律 / 数学结构”结合起来,让模型在分子设计、蛋白工程、材料发现、物理仿真、数学推理等环节中充当“可编程的科学助手”。 + +在工程实践中,这一层一端连接量子化学软件、分子动力学(MD)、CFD/FEA 仿真器、自动定理证明器、文献数据库和自动化实验室(Robotic Lab)等“传统科学基础设施”,另一端连接制药公司、材料企业、能源公司、科研机构的真实科研工作流。下面从 **场景** 、 **原理** 、**模型**三个角度展开,并在若干关键方向上进一步细分。 + +* **场景** + * 分子与药物设计:从海量小分子 / 片段出发,预测性质与 ADMET,设计针对特定靶点的候选药物,并通过虚拟筛选和多目标优化缩小实验空间。 + * 蛋白质与生物结构建模:预测蛋白及复合物的三维结构,辅助抗体、酶、蛋白药物设计,评估突变对功能与稳定性的影响。 + * 物理仿真与工程设计:用深度替代模型加速 CFD / FEA / 分子动力学等高成本仿真,为航空航天、汽车、能源等领域提供快速评估与优化工具。 + * 材料发现与晶体设计:在庞大化学 / 材料空间中进行虚拟筛选和逆设计,加速电池、光伏、催化剂、合金等关键材料的研发。 + * 数学与符号推理:在形式系统中做自动定理证明、符号计算和方程求解,增强大模型在数学题、工程推导中的严谨推理能力。 + * 科学工作流与自动化实验:对接文献、数据库与自动化实验平台,构建“自驱动实验室(Self‑Driving Lab)”,让模型参与实验设计、执行与结果分析。 +* **原理** + * 结构化表示与图建模:用图(Graph)、晶体图(Crystal Graph)、分子图等结构表征复杂对象,在图神经网络或 E(3)-等变网络上建模几何与拓扑关系。 + * 物理 / 化学归纳偏置:通过守恒定律、对称性(平移 / 旋转 / 反射)、PDE 约束(PINN)、能量势函数等方式,将物理先验融入模型结构与损失函数。 + * 生成与逆设计:利用 VAE、GAN、Diffusion、RL 等生成式建模方法,支持从“目标性质 / 约束条件”反推结构,实现分子 / 材料 / 结构的逆设计。 + * 代理模型与多尺度耦合:用深度代理模型近似昂贵的量子化学 / 连续介质 / 结构力学仿真,并将微观–中观–宏观模型拼接起来,实现多尺度建模。 + * 工具增强与 Agent 工作流:将 LLM 与模拟器、符号计算器、自动定理证明器、文献检索系统和实验机器人组合,构建可自动规划和执行科学任务的 Agent。 +* **模型** + * 分子与材料表征模型:SchNet、DimeNet、PhysNet、CGCNN、MEGNet、ALIGNN 等 E(3)-等变网络与图网络,ChemBERTa、MolBERT、MoleculeSTM 等分子语言模型。 + * 结构生物学模型:AlphaFold / AlphaFold2 / AlphaFold3、RoseTTAFold、OpenFold、ProteinMPNN、ESM‑IF、ESM 系列蛋白语言模型与结构生成模型。 + * 物理仿真与算子学习:PINN、DeepONet、Fourier Neural Operator (FNO) 及 Neural Operator 家族、DeepMD、NequIP 等势能面与算子学习模型。 + * 数学与符号推理模型:Minerva、Gödel、GPT‑f、Lean‑Dojo 等数学 / 证明专用模型,以及 LLM + SymPy/Mathematica/Lean/Coq 的工具增强系统。 + * 科学 Agent 与工作流系统:结合检索、代码生成、仿真调用与实验控制接口,为制药、材料、物理、化学等领域封装的“AI 科学助手”和自驱动实验平台。 + +从这一层开始,传统科学计算与深度学习、大模型深度交织:既要尊重物理 / 化学 / 生物 / 数学的严格约束,又要利用数据驱动的强拟合能力提升效率,最终目标是让 AI 成为科研中的“合作者”,而不仅仅是一个预测黑盒。 + +--- + +## 10.1 分子与药物设计(Molecular Modeling & Drug Discovery) + +在传统药物研发中,从靶点发现到临床试验往往需要 10+ 年和数十亿美元成本,而极大一部分时间与资金耗费在早期的分子设计、性质预测和虚拟筛选阶段。AI 驱动的分子建模与药物设计,旨在用**数据驱动 + 生成式建模**加速这一过程:从结构或文本描述出发,预测分子性质与 ADMET,设计针对特定靶点的候选化合物,并通过多目标优化与虚拟筛选显著减少湿实验负担。 + +这一方向一端连接量子化学软件(DFT、ab initio)、生物活性实验、HTS(High‑Throughput Screening)等数据来源,另一端连接药企内部的 Small Molecule Design 平台、性质预测 SaaS、材料 / 化学品设计工具。下面从 **场景** 、 **原理** 、**模型**三个维度展开。 + +* **场景** + * 早期虚拟筛选与 Hit 发现:面对数百万到数十亿规模的虚拟分子库,通过 AI 快速预测活性 / ADMET,对候选分子排序,筛出少量高价值 Hit 进入实验环节。 + * 分子性质与 ADMET 评估:在先导化合物优化(Lead Optimization)阶段,持续预测溶解度、毒性、代谢稳定性以及口服生物利用度等指标,为药代动力学和安全性评估提供参考。 + * 靶点导向分子生成:给定蛋白靶点信息(口袋特征、已知配体)或目标性质约束,自动生成结构多样、具有高活性且可合成的候选小分子。 + * 材料与化学品分子设计:面向非药物场景,如涂料、溶剂、电解液、界面活性剂等分子,设计满足特定物性(黏度、极性、界面能等)的配方分子。 +* **原理** + * 分子表征与性质预测: + * **结构表示** :常见有 SMILES 序列、分子图(原子为节点、键为边)、3D 坐标及量子特征等;模型需要从这些表示中抽取可泛化的语义与几何信息。 + * **性质预测** :通过 GNN(GCN、GAT、MPNN)或 3D‑等变网络(SchNet、DimeNet、PhysNet 等),从分子图或 3D 结构中学习到能量、偶极矩、轨道能级等量子性质,以及溶解度、LogP、毒性、代谢稳定性等 ADMET 属性。 + * **表征学习与预训练** :基于大规模分子库(如 ZINC、ChEMBL、PubChem)进行掩码预测、对比学习或自回归预训练,得到可迁移的通用分子表示,为下游 QSAR / ADMET 提供特征。 + * 结构生成与分子优化: + * **生成建模** :利用 VAE、GAN、Flow、Diffusion 等生成式模型,在 SMILES 或分子图空间中采样新分子,要求保证化学结构合法性(价态、环结构等)与多样性。 + * **条件生成** :引入条件向量(目标活性、理化性质、结构片段、靶点口袋描述等),在给定约束下生成候选分子,实现性质导向或片段补全式的设计。 + * **多目标优化与 RL** :通过强化学习(如 MolDQN 等)在分子空间中进行“编辑”操作(加原子、改键、替换片段),从而在活性、毒性、合成可行性、专利避让等多个目标之间权衡。 + * 蛋白 – 小分子相互作用建模: + * **结合位点与打分函数** :通过 3D 卷积 / 图网络 / 互作图建模蛋白口袋与配体的空间关系,预测结合位点及结合亲和力(Binding Affinity)。 + * **对接与 Binding Pose 预测** :将 Docking 中的构象搜索与深度模型结合,用深度打分函数或 Diffusion 式生成预测稳定构象,提高对接准确率并降低计算成本。 +* **模型** + * 分子表征模型: + * **GNN 与 3D 网络** :DimeNet / DimeNet++、SchNet、PhysNet 等考虑角度 / 距离的 3D 等变模型,GCN/GAT/MPNN 等通用图神经网络,适用于性质预测与 QSAR。 + * **基于 SMILES 的 Transformer** :将分子视为“化学语言句子”,用 Transformer 做自回归或掩码语言建模,为生成与性质预测提供序列表示。 + * 生成与优化模型: + * 图生成模型:GraphVAE、Junction Tree VAE、GraphAF 等在图 / 片段空间生成分子,强调结构合法性与可解释性(片段级构造)。 + * 扩散模型:Diffusion for Molecules 通过在图或 3D 结构空间添加 / 去除噪声生成新分子或构象,可与条件向量结合实现定制生成。 + * 强化学习优化:MolDQN 等基于 RL 的方法,将分子优化视作在“分子编辑”状态空间中的序列决策问题,用奖励函数编码多目标指标。 + * 分子大模型与多模态方向: + * **分子语言模型** :ChemBERTa、MolBERT 等在大规模 SMILES 语料上预训练,支持零样本或小样本转移至下游任务。 + * **多模态分子模型** :MoleculeSTM 等整合结构(图 / 3D)、文本描述(合成路线、文献摘要)、分子属性,实现跨模态检索与联合预测。 + * 产品与应用形态: + * 面向药企的早期药物筛选平台与内部 Small Molecule Design 平台,提供虚拟筛选、分子生成、ADMET 预测等一体化能力。 + * 面向研发人员的性质预测 SaaS:通过 Web 或 API 方式快速查询分子性质、ADMET、分子相似度等。 + * 面向材料与化学品设计的分子级设计工具,用于涂料、溶剂、电解液等分子体系的定制开发。 + +从这一子方向开始,药物设计流程正在从“专家 + 高通量实验”走向“专家 + 模型 + 自动化实验”的闭环,AI 不只是给出分数,而是逐渐参与从“提出想法”到“生成候选”再到“筛选与优化”的完整环节。 + +### 10.1.1 分子表征与性质 / ADMET 预测 + +在药物与材料研发中,一个基础能力是: **给定一个分子,快速且准确地预测其性质与行为** ,包括量子化学性质(能量、轨道、偶极矩)、理化性质(溶解度、LogP)、以及药代 / 毒性相关的 ADMET 指标。这一问题的本质,是如何从不同形式的分子表示中学习到 **既符合化学规律,又具备泛化能力的表征** 。 + +* 在**分子表征**层面,常见的表示包括: + * **SMILES / SELFIES 等字符串** :把分子视为序列,天然适合用 RNN / Transformer 进行语言建模。 + * **分子图表示** :原子为节点、键为边,节点和边带有类型、价态、芳香性等特征;适合用 GNN、MPNN 等建模邻域与拓扑。 + * **3D 几何表示** :基于量子化学或力场优化得到的 3D 坐标、键角、二面角等信息,为 E(3)-等变网络捕捉空间结构提供基础。 +* 在**性质与 ADMET 预测**层面,目标任务包括: + * 小分子量子性质预测:能量、偶极矩、HOMO/LUMO 能级等,用以替代昂贵的 DFT / ab initio 计算。 + * QSAR / 活性预测:给出化合物对特定靶点的活性(IC50、Ki)、选择性等,用于筛选潜在候选。 + * ADMET 相关指标:溶解度、渗透性、毒性、代谢稳定性、CYP 抑制等,是药物可成药性评估的关键。 + +典型模型路径为:用 DimeNet / SchNet / PhysNet / GNN 等在分子结构上提取高维表征,再通过多任务学习同时预测多种性质;在大规模公开或企业内部数据上进行预训练,提高小数据场景的建模能力。对外则以 ADMET 预测 SaaS 或内部平台 API 的形式提供服务,为项目组提供快速的“虚拟实验”能力。 + +### 10.1.2 结构生成与分子优化:从 SMILES / Graph 到候选药物 + +在具备了可靠的分子表征与性质预测模型之后,更进一步的目标是 **主动生成“更好”的分子** :不再只是评估给定化合物,而是围绕靶点与性质约束,直接设计出新的候选分子。这一方向通常被称为 **分子生成与分子优化** 。 + +在**结构生成**方面,研究与工程实践主要围绕三类路径: + +1. **基于 SMILES 的序列生成** + 将分子视作字符串,使用 VAE、GAN 或自回归 Transformer 在 SMILES 空间中采样新结构;通过语法约束(如 SELFIES)或后处理保证化学有效性。 +2. **基于图 / 片段的生成** + GraphVAE、Junction Tree VAE、GraphAF 等模型直接在分子图或基元片段(Fragement / Motif)层面构造结构,更贴近化学合成思维,有利于控制环、基团与骨架结构。 +3. **基于扩散与 3D 生成** + Diffusion for Molecules 等方法在图或 3D 坐标空间进行扩散与去噪,可同时考虑空间构象,适用于生成对 3D 形状敏感的配体或材料单元。 + +在**分子优化**方面,关键是引入 **目标与约束** : + +* **条件生成** :把目标活性、理化性质或片段锚定作为条件向量输入模型,使其在生成时偏向满足这些条件。 +* **强化学习与多目标优化** :以性质预测模型为“环境”,用 RL 在分子空间中做序列决策(如 MolDQN),在活性、毒性、合成可行性、专利风险等多维指标上设置奖励与惩罚,实现多目标权衡。 +* **合成可行性与化学先验** :在生成与优化过程中融入合成路径预测模型、合成复杂度指标(如 SA score),避免产生难以合成或不稳定的结构。 + +在产品化上,这一类模型常被封装进药企内部的“AI 药物设计平台”中:给定靶点、已知先导结构和优化方向,平台自动提出若干批次候选分子,项目组再结合实验、专利和商业考量逐步筛选与迭代,实现“模型–实验–模型”的闭环优化。 + +## 10.2 蛋白质与生物结构建模(Protein & Structural Biology) + +在生命科学中,**结构决定功能** 是一条近乎教条的原则:蛋白质如何折叠成三维结构、如何与其他分子装配成复合物,直接决定了其在细胞中的功能表现。传统结构解析依赖 X‑ray 晶体学、NMR、冷冻电镜等实验手段,周期长、成本高且存在“难结晶、难解析”的巨大盲区。以 AlphaFold 为代表的深度学习模型,把“从序列直接到结构”的能力大幅推前,使得在全基因组尺度上获得高质量结构成为可能。 + +这一方向一端连接 UniProt / PDB 等序列与结构数据库、组学实验与结构组学项目,另一端连接生物制药、合成生物学、酶工程等产业界的结构设计与分析平台。下面同样从 **场景** 、 **原理** 、**模型** 三个角度展开,并进一步拆分关键子方向。 + +* **场景** + * 靶点结构注释与筛选:在基因组层面预测大量蛋白的结构,辅助靶点发现、功能注释与通路分析;结合变异信息评估潜在致病机理。 + * 抗体 / 蛋白药物设计:对抗体可变区(CDR)、受体结合结构域等关键区域进行精细建模与设计,优化亲和力、特异性和免疫原性。 + * 酶与生物催化设计:基于酶三维结构和活性位点环境,设计突变与变体库,提升催化效率、底物范围与稳定性。 + * 复合物与相互作用研究:预测蛋白–蛋白、蛋白–核酸、蛋白–小分子复合物结构,解析界面互作模式,为药物设计与信号通路建模提供基础。 + * 突变效应与耐药性分析:评估自然变异或人工突变对结构稳定性、功能和配体结合的影响,分析耐药突变的结构基础。 +* **原理** + * 蛋白质结构预测: + * **序列 → 结构** :从氨基酸序列(单序列或包含多序列对齐 MSA)出发,建模残基两两之间的几何约束(距离、角度、接触图),再通过几何重建模块生成全原子 3D 结构。 + * **协同进化信号** :利用同源序列之间的协同突变模式(co‑evolution),推断潜在的残基接触关系,为折叠约束提供强先验。 + * **结构精修与不确定性估计** :对预测结构进行局部精修(relax、repack),并输出置信度评分(如 pLDDT、PAE),指导后续应用中的“可信区域”选择。 + * 复合物与分子装配建模: + * **多链联合建模** :将多个蛋白链或蛋白 + 核酸序列作为输入,引入链识别与接口约束,直接输出完整复合物结构。 + * **界面预测与装配** :基于已知单体结构,通过图模型或扩散模型预测最可能的界面构型与装配方式。 + * 蛋白设计与突变效应预测: + * **反向折叠(Inverse Folding)** :给定三维骨架结构或拓扑约束,生成能稳定折叠成该结构的氨基酸序列,实现 de novo 蛋白设计。 + * **突变效应建模** :结合蛋白语言模型与结构模型,预测特定突变对稳定性(ΔΔG)、活性或结合亲和力的影响,辅助定向进化与变体筛选。 +* **模型** + * 结构预测: + * AlphaFold / AlphaFold2 / AlphaFold3:以注意力机制和几何模块为核心,从 MSA、模板结构与序列特征中预测高精度蛋白结构,并输出不确定性估计。 + * RoseTTAFold、OpenFold:采用多轨道(sequence / pair / structure)表示与多尺度注意力机制,为开源与产业化落地提供基础实现。 + * 复合物与界面建模: + * AlphaFold‑Multimer:在多链场景下直接建模蛋白–蛋白复合物结构,兼顾单体折叠与界面互作。 + * RFdiffusion:基于扩散模型在 3D 空间生成或优化蛋白骨架与复合物接口,实现复杂装配与对称体设计。 + * DiffDock 等方法:在蛋白–小分子系统中,用扩散或深度打分函数预测 Binding Pose 与结合模式。 + * 设计与突变模型: + * ProteinMPNN:在给定结构的条件下生成兼容的序列,用于稳定骨架与界面设计。 + * ESM‑IF、ESMFold / ESM‑2 系列:基于大规模蛋白序列预训练的语言模型,具备从序列推断结构、功能与突变效应的能力。 + * 产品与应用: + * 公有云上的蛋白结构预测服务与数据库(如 AlphaFold DB),为科研提供大规模结构注释与下载接口。 + * 生物制药公司内部结构设计平台:集成蛋白结构预测、抗体设计、酶工程、蛋白–配体对接等模块。 + * 生物技术 SaaS:提供结合位点预测、界面热力学评估、亲和力与免疫原性评估工具,服务于抗体药物、生物制剂开发。 + +从这一子方向开始,AI 不仅在“解读”自然存在的蛋白结构,更在“创造”全新的蛋白与复合物架构,使结构生物学从“被动测量时代”进入“主动设计时代”。 + +### 10.2.1 蛋白质结构预测与复合物装配 + +蛋白质结构预测是结构生物学与 AI 结合最具代表性的突破之一。其核心问题是:**能否从序列出发,在不依赖或少依赖实验数据的情况下,预测出接近实验分辨率的 3D 结构?** 而在真实应用中,单体结构往往只是起点,更关键的是蛋白如何与其他分子装配成复合物。 + +在 **单体结构预测** 中,典型流程包括: + +1. **序列 / MSA 编码** :通过序列特征提取和多序列对齐挖掘协同进化信号。 +2. **几何约束推断** :预测残基对之间的距离分布、接触概率与相对取向,形成“伪测量”的几何场。 +3. **结构构建与迭代精修** :在几何约束下用结构模块(如旋转平移不变块、内坐标更新)构建 3D 结构,并多次迭代 refinement 以降低几何违背。 +4. **不确定性与质量评估** :输出逐残基置信度(pLDDT)、残基对误差估计(PAE)等指标,为后续建模与筛选提供参考。 + +在 **复合物与装配预测** 中,问题进一步扩展为“多条链如何在空间中组织与相互作用”: + +* 对于 **蛋白–蛋白复合物** ,通常在多链输入的基础上,使用专门的多链建模策略(如 AlphaFold‑Multimer)直接输出装配结构。 +* 对于 **蛋白–核酸 / 蛋白–小分子体系** ,一类路径是先预测各自结构,再通过对接与界面打分函数预测装配方式;另一类则是用扩散模型或联合建模在 3D 空间内直接生成复合物构象。 +* 在多亚基、大型装配体场景中,还需要结合对称性约束、低分辨率 EM 密度图等信息,进行分层与多尺度装配。 + +在产品实践中,结构预测与装配常被封装为云端服务或本地工具链,为蛋白功能注释、相互作用网络建模、药物靶点验证提供基础结构信息。 + +### 10.2.2 蛋白设计与突变效应预测:从结构到功能调控 + +在掌握“序列 → 结构”的映射之后,下一步是反向问题:**如何在给定结构或功能需求的情况下,设计出合适的蛋白序列与突变方案?** 这就是蛋白设计与突变效应预测的核心。 + +在 **蛋白设计** 中,关键任务包括: + +* **反向折叠(Inverse Folding)** :给定目标骨架(backbone)或整体拓扑结构,生成能够稳定折叠成该结构的氨基酸序列,这一过程可通过 ProteinMPNN、ESM‑IF 等结构条件生成模型实现。 +* **功能导向设计** :在保持整体结构稳定的前提下,针对活性位点、结合口袋、界面区域进行定向设计,优化亲和力、特异性与催化效率。 +* **可制造性与免疫原性约束** :在序列设计过程中,引入表达可行性、翻译后修饰、免疫原性风险等约束,保证候选序列在生物制剂开发中的可落地性。 + +在 **突变效应预测** 中,关注的是: + +* **稳定性变化(ΔΔG)** :给定野生型结构与突变位点,预测单点或多点突变对折叠稳定性的影响,用于定向进化和耐药突变分析。 +* **活性与亲和力变化** :结合结构与蛋白语言模型,评估突变对酶学活性、配体亲和力与信号通路调控的影响。 +* **大规模变体库设计** :在体内 / 体外筛选实验之前,用模型对庞大突变空间进行预筛选,保留高潜力变体,降低实验成本。 + +在工程与产品层面,蛋白设计与突变效应预测常被集成为生物制药 / 合成生物学公司内部的“结构设计与优化模块”:从候选骨架结构出发,自动提出多轮突变与变体库设计方案,与高通量筛选实验形成数据驱动的闭环。 + +## 10.3 物理仿真与加速计算(Physics Simulation & Surrogate Modeling) + +在航空航天、汽车、土木工程、能源、化工等领域, **高精度仿真是设计与验证的核心环节** 。然而 CFD(计算流体力学)、FEA(有限元分析)、分子动力学(MD)以及各类 PDE 求解往往计算昂贵,难以支持大规模参数扫描、实时控制或在线优化。AI 驱动的物理仿真与代理建模,试图用深度网络来近似数值求解器或算子本身,在保证物理一致性和可解释性的前提下,实现数量级的加速。 + +这一方向一端连接传统仿真软件(ANSYS、Fluent、COMSOL、自研求解器)、实验测量与传感器数据,另一端连接工程设计平台、自动驾驶与航天气动设计、化工过程模拟与优化系统。下面从 **场景** 、 **原理** 、**模型** 三个角度展开。 + +* **场景** + * 工程仿真加速:在给定几何与工况下,用深度代理模型快速预测压力场、速度场、温度场、应力 / 应变分布等,为多轮设计迭代和优化提供支持。 + * 复杂过程模拟与工艺优化:在化工、能源等流程工业中,通过 ML 近似机理模型或黑箱过程模型,实现快速评估与实时控制。 + * 分子 / 材料尺度模拟:用 ML 势能面(Neural Network Potential)替代高成本的 ab initio 势能与力计算,加速分子动力学与材料相行为模拟。 + * 多尺度与跨学科耦合:通过深度代理模型把微观–中观–宏观模型拼接起来,构建端到端的多尺度仿真与优化链路。 +* **原理** + * 替代模型 / 代理模型(Surrogate Models): + * 从数值仿真或实验数据中学习“输入参数 → 输出场 / 指标”的映射,作为高保真求解器的近似。 + * 在高维参数空间下,结合主动学习与贝叶斯优化,自动选择最有信息量的样本点进行高保真仿真或实验,持续提高代理模型质量。 + * 物理知晓神经网络(PINN): + * 将 PDE、初始 / 边界条件与物理守恒定律写入损失函数,利用自动微分技术在连续空间上求解物理场。 + * 支持正向问题(求解状态场)与逆问题(由稀疏观测反推源项、材料参数等),特别适用于传统数值方法难以处理的复杂几何与边界。 + * 算子学习与 Neural Operator: + * 不只拟合“具体条件下的解”,而是学习从函数到函数的映射(算子),如“边界条件 / 源项 → 整个解场”。 + * 代表方法如 Fourier Neural Operator (FNO)、DeepONet 等,通过频域变换或特定网络架构,提升对不同网格密度与几何形状的泛化能力。 + * 多尺度建模: + * 在微观模拟数据上训练中观 / 宏观层级的有效参数或本构关系,由深度代理模型承担“尺度桥接层”角色。 + * 对复杂材料、流固耦合与多相流等问题,用深度模型在不同尺度与物理模块间传递信息。 +* **模型** + * 通用物理神经网络: + * PINN 系列:通过在时空域采样点上最小化 PDE 残差来求解,适用于 Navier‑Stokes、Maxwell、弹性力学等方程。 + * DeepONet、FNO、Neural Operator 家族:直接学习 PDE 求解器的“算子级”近似,在多工况、多几何下快速推理。 + * 分子 / 材料尺度势能模型: + * DeepMD、SchNet、NequIP、SpookyNet 等:构建高精度 ML 势能面,在接近 ab initio 准确度的前提下,大幅加速力与能量计算。 + * 与传统 MD 引擎耦合,实现大体系、长时间尺度的高精度分子动力学。 + * CFD / 结构力学代理模型: + * U‑Net / UNet++ 等 Encoder‑Decoder 网络:在规则网格上从几何 / 边界条件预测流场或温度场。 + * 图神经网络 on Mesh:在非结构化网格上对节点 / 单元进行消息传递与更新,适合复杂几何和多物理场耦合场景。 + * Neural Operator for CFD:在不同雷诺数、来流条件、几何参数下泛化流场预测。 + * 产品与应用: + * 工业仿真软件中的 AI 加速模块:在传统求解器外层提供快速预估和敏感性分析功能。 + * 化工 / 能源过程模拟与优化平台:把机理模型 + 代理模型 + 优化算法组合成一体化工艺优化工具。 + * 自动驾驶 / 航空航天气动设计:在气动外形设计中进行大规模设计变量扫描与自动形状优化。 + +### 10.3.1 替代模型与物理知晓神经网络(PINN) + +**替代模型(Surrogate Models)** 与 **物理知晓** **神经网络** **(PINN)** 是物理仿真 AI 化的两条互补路径:前者从数据出发近似仿真映射,后者从物理出发构造学习目标。 + +在 **替代模型** 场景中,典型流程是: + +1. 通过高保真数值仿真或实验采集一批样本数据(输入参数、边界条件、几何 → 输出物理量)。 +2. 训练深度网络(如 MLP、卷积网络、GNN、Neural Operator)近似这一映射函数。 +3. 在设计优化、参数扫描或实时控制中,用代理模型替代昂贵的求解器进行快速评估。 + +在 **PINN** 场景中,模型不再以大量监督标签为主,而是通过最小化 PDE 残差与边界条件违背构建损失函数: + +* 在空间 / 时间采样点上,用神经网络输出物理量(如速度、压力、位移场等),自动微分得到梯度与导数。 +* 将这些导数代入 PDE 中,形成残差,并与边界条件、初始条件的误差一起构成总损失。 +* 通过优化使 PDE 残差与边界误差尽可能接近 0,从而得到满足物理方程的近似解。 + +两者可以结合使用:在有部分高保真数据时,用数据误差 + 物理残差共同约束训练,提高精度与泛化能力。在工程应用中,PINN 特别适合处理逆问题与数据驱动建模,如从传感器观测反推材料参数、源项或缺陷位置。 + +### 10.3.2 Neural Operator 与多尺度物理建模 + +**Neural Operator** 将物理建模从“点到点 / 参数到解”的映射提升到“函数到函数”的层面:它学习的是“给定一类 PDE 与边界条件,求解其解场”的统一算子近似,而非单一工况下的特定解。这为多工况、多几何与跨网格分辨率的泛化提供了新的可能。 + +在 **算子学习** 中,典型做法是: + +* 以函数(如源项、边界条件、材料参数场等)作为输入,用网络(如 FNO、DeepONet)输出整个解场函数。 +* 通过在不同网格、不同参数与不同几何上的样本训练,让模型学习到 PDE 求解器的“公共模式”。 +* 部署时,只需给出新的输入函数(如新的边界条件、几何),就能快速推理得到近似解场。 + +在 **多尺度建模** 场景中: + +* 在微观尺度(如分子动力学、晶体塑性)产生的大量数据上训练 Neural Operator,学习微观结构与宏观响应之间的映射。 +* 在宏观连续介质模型中,用这一映射作为本构关系或有效参数计算模块,实现微–宏耦合。 +* 对于流固耦合、多相流、反应流等复杂系统,可以对不同物理场分别建模并通过共享接口变量(如通量、界面力等)耦合。 + +在工程实践中,Neural Operator 逐渐从研究原型走向应用,成为 CFD、地球物理、气候建模等场景中“加速求解器 + 多尺度桥接”的重要技术方向。 + +## 10.4 材料发现与晶体设计(Materials Science & Crystal Design) + +在材料科学中,一个核心矛盾是: **设计空间几乎无穷大,而实验与高精度计算成本极高** 。如何在巨大的化学与结构组合空间中高效地找到满足特定性能要求的候选材料,是新能源、电子、结构、功能材料等领域的关键问题。AI 驱动的材料发现与晶体设计,通过图神经网络、生成模型与高通量虚拟筛选,将“试错式”研发逐步转向“数据驱动 + 逆设计”。 + +这一方向一端连接 Materials Project、OQMD、AFLOW 等材料数据库与 DFT / MD 计算结果,另一端连接电池、光伏、催化、半导体、合金等应用场景的材料研发平台。下面从 **场景** 、 **原理** 、**模型** 三个角度展开。 + +* **场景** + * 性能导向的材料筛选:给定晶体结构或化学式,预测能带结构、带隙、载流子迁移率、热 / 电 / 磁性质等,为材料筛选与组合优化提供依据。 + * 新能源材料研发:面向电池电解质、电极材料、固态离子导体、光伏吸收层与催化剂等体系,预测离子电导率、稳定性、电化学窗口与活性等。 + * 高通量虚拟筛选(HTVS):在构建的大规模候选库中,通过 ML 模型快速评估,筛出潜力材料,再用少量 DFT / 实验验证与校准。 + * 晶体结构与成分逆设计:从目标性质出发,反向搜索满足性能与工艺约束的晶体结构 / 成分组合。 +* **原理** + * 材料与晶体表示: + * 将周期性晶体结构表示为晶体图(Crystal Graph):节点为原子,边为原子间近邻关系,结合晶格参数与空间群信息。 + * 对于非晶或复杂多相材料,可通过局部环境描述符(如 SOAP)、Voronoi 特征或多尺度图结构表示其微结构。 + * 性质预测: + * 在 CGCNN、MEGNet、ALIGNN 等 GNN 模型上对晶体图进行卷积 / 消息传递,预测能量、带隙、弹性模量、热导等。 + * 利用 Mat2Vec 等基于文献和化学式的嵌入,在低数据场景下实现迁移学习与零样本估计。 + * 高通量虚拟筛选: + * 构建候选库(通过组合枚举、结构生成、经验规则等) → 使用 ML 模型快速预测性质 → 筛选出少量 Top 候选进行 DFT 或实验校准 → 更新模型与筛选策略,形成主动学习闭环。 + * 生成与逆设计: + * 利用扩散模型、VAE 或 GNN 生成模型在晶体结构空间采样新结构,可施加成分、空间群、密度等约束。 + * 结合代理模型与贝叶斯优化,从目标性质出发搜索合适的结构 / 成分组合,实现 inverse design。 +* **模型** + * 表征与预测: + * CGCNN(Crystal Graph Convolutional Neural Network):在晶体图上进行卷积,用于能量、带隙等无机材料性质预测。 + * MEGNet、ALIGNN:融合图结构与边 / 角度信息,在多种材料家族上具备更强的泛化与精度。 + * Mat2Vec + 轻量 ML:通过对化学式和元素信息的向量化,快速训练用于特定性质预测的小模型。 + * 生成与逆设计: + * Diffusion for Crystals:在晶格参数与原子位置组成的高维空间中进行扩散 / 去噪,生成满足一定约束的晶体结构。 + * GNN‑based Generative Models:通过逐步添加 / 修改原子和键或操作晶格,实现从随机初始化到目标性质附近的结构搜索。 + * Surrogate + Bayesian Optimization:用 ML 模型作为“结构 → 性质”的近似黑箱,在其上做贝叶斯优化,寻找最优结构或成分。 + * 数据平台与工具链: + * Materials Project、OQMD、AFLOW:提供大量结构与 DFT 计算数据,是训练与评估材料 ML 模型的基础。 + * 企业内部材料数据库与模型:结合公司实验数据与工艺信息,构建领域特化的材料 AI 设计平台。 + * 产品与应用: + * 新能源材料研发加速平台:为电池、电催化、光伏等团队提供一体化的性质预测、HTVS 与 inverse design 能力。 + * 虚拟筛选软件与 SaaS:为合金、半导体、功能陶瓷等提供数字化筛选工具,减少早期试错成本。 + * 材料公司内部的 AI 设计工具:与实验室信息管理系统(LIMS)与生产线数据对接,形成从“模型 → 实验 → 生产”的闭环。 + +### 10.4.1 材料性质预测与高通量虚拟筛选(HTVS) + +在材料研发流程中,**快速而可靠的性质预测** 是一项基础能力:给定一个候选结构或成分,能否在不做昂贵 DFT / 实验的情况下,大致判断其是否值得深入探索。基于 GNN 与材料数据库的性质预测模型,为高通量虚拟筛选提供了可能。 + +在 **性质预测** 层面: + +* 使用晶体图表示周期性结构,通过 CGCNN、MEGNet、ALIGNN 等模型学习原子与邻域间的相互作用。 +* 针对不同任务(能量、带隙、弹性常数、热导、电导、磁性等)进行单任务或多任务训练,在 Materials Project 等数据集上达到接近 DFT 精度的预测性能。 +* 在工业场景中,常结合内部实验数据进行再训练或领域自适应,以提升对特定材料家族与工艺条件的适配度。 + +在 **高通量虚拟筛选(HTVS)** 场景中,典型流程为: + +1. 构建大规模候选库(组合枚举、结构生成或从现有数据库扩展)。 +2. 使用 ML 模型快速预测每个候选的目标性质与辅助性质(稳定性、安全性、成本相关指标等)。 +3. 按目标性质与多约束条件筛选排名,选出 Top‑K 候选进行高保真 DFT 计算或实验验证。 +4. 将验证结果反哺模型,更新参数与不确定性估计,形成“筛选–验证–再筛选”的主动学习闭环。 + +这一工作流在电池材料、光伏吸收层、催化剂与结构材料等多个领域已进入实用阶段,成为材料研发团队的“前置筛选引擎”。 + +### 10.4.2 晶体生成与逆设计:从目标性质到候选结构 + +在具备了可靠的性质预测与 HTVS 能力之后,更进一步的目标是 **直接从目标性质与约束出发,提出新的晶体结构与成分候选** ,即材料的逆设计与生成。 + +在 **晶体生成** 中,关键问题包括: + +* 如何在周期性约束下生成物理合理的晶格与原子排列? +* 如何在生成过程中显式或隐式地施加成分、对称性与密度等约束? +* 如何保证生成结构在经过简单松弛后依然稳定? + +为此,研究与工程实践常采用: + +* **Diffusion for Crystals** :在晶格参数 + 原子位置的联合空间中添加 / 去除噪声,实现从随机初始到结构样本的渐进生成,可在噪声过程或条件向量中融入目标性质与成分约束。 +* **GNN** **‑based Generative Models** :在图结构上逐步添加原子与连接关系,或对已有结构进行编辑,生成满足约束的候选结构。 + +在 **逆设计** 中,通常与代理模型与优化方法结合: + +* 将性质预测模型视作“结构 → 性质”的黑箱函数。 +* 通过贝叶斯优化、进化算法或 RL 在结构空间中探索,使预测性质逐步逼近目标值,同时满足稳定性、安全性、成本等约束。 +* 对搜索得到的候选结构进行 DFT / 实验验证,并将结果用于更新代理模型与搜索策略。 + +在工程应用中,逆设计模块往往被集成到材料 AI 平台中,为研发人员提供“设定目标性质 → 系统自动提出候选结构”的交互界面,显著提升新材料探索的效率。 + +## 10.5 数学与符号推理(Mathematics & Symbolic Reasoning) + +数学是高度形式化、可精确验证的语言,这让它在 AI 时代同时具备“难度极高”和“潜在回报巨大”两种属性。一方面,复杂的定理证明与高阶推理对模型能力提出了极高要求;另一方面,数学推理与符号计算的结果可以被严格验证,天然适合与程序化工具协同。AI 在数学与符号推理方向的目标,是构建能够在形式系统中**进行可靠推理与计算**的模型,并将其融入教育、科研与工程应用。 + +这一方向一端连接 Lean / Coq / Isabelle 等交互式定理证明器,SymPy / Mathematica / Maple 等计算机代数系统(CAS),以及大型数学题库与文献语料;另一端连接数学教育产品、辅助研究工具与工程 / 金融等领域的公式推导与风险分析需求。下面从 **场景** 、 **原理** 、**模型** 三个角度展开。 + +* **场景** + * 自动定理证明与辅助证明:在形式化系统中自动给出定理证明,或生成可读的证明草稿,由人类进一步审阅与完善。 + * 表达式操作与符号计算:自动化简表达式、求导、积分、级数展开、变换与方程求解,为工程建模与金融风险分析提供符号工具。 + * 数学题理解与解题步骤生成:从自然语言或图片中的题目提取结构化表示,给出严谨、可检查的解题步骤,服务于教育与训练场景。 + * 数学推理能力增强:通过数学专向微调与工具增强,提高大模型在算术、代数、几何、组合等领域的多步推理与严谨性。 +* **原理** + * 形式系统与搜索: + * 在 Lean / Coq / Isabelle 等系统内,数学对象与定理被形式化为项与类型,证明过程对应于在规则约束下构建证明树。 + * 证明搜索可以视为“在极大状态空间中寻找满足约束的路径”,适合采用强化学习、MCTS(蒙特卡洛树搜索)与策略网络 / 价值网络等方法。 + * 神经 – 符号协同: + * LLM 负责从自然语言或非结构化输入中提取问题结构与求解思路,将其翻译为符号表达(如 SymPy 代码、Lean 证明脚本)。 + * 计算机代数系统与定理证明器负责执行严格的符号计算与形式验证,对 LLM 输出进行校验与纠错。 + * 数学推理能力提升: + * 通过在大规模数学文本与题库上做专向预训练或微调(如 Minerva、Gödel),提升模型对数学语言的理解与推理风格的掌握。 + * 采用 Tool‑Augmented LLM 框架,将符号求解器、数值计算库、绘图工具与证明器作为外部工具,让模型在复杂推理中学会“调用工具”而非“死记结果”。 +* **模型** + * 自动定理证明: + * AlphaZero‑style 证明器:将证明进程视为博弈过程,使用策略网络和价值网络引导搜索,逐步构造形式证明。 + * GPT‑f、Lean‑Dojo 等:在大规模形式化定理与证明语料上训练,用于在 Lean 等系统中自动生成证明。 + * 数学大模型与工具增强: + * Minerva、Gödel 等:在数学教材、论文、题库等语料上微调的大模型,在证明题、竞赛题和高阶推理任务上表现更强。 + * LLM + SymPy / Mathematica / Lean / Coq:由 LLM 做问题解析与策略规划,调用符号计算与证明工具做精确操作与验证。 + * 产品与应用: + * 教育产品中的“数学助教 / 解题助手”,提供个性化讲解与多种解法路径。 + * 辅助研究工具:帮助研究者构造猜想、生成证明草稿、搜索相关定理与引理,加速理论探索。 + * 工程 / 金融领域的公式推导与风险模型分析:将复杂模型形式化,进行符号敏感性分析与合规性审查。 + +### 10.5.1 自动定理证明与形式化推理 + +**自动定理证明(ATP)与交互式定理证明(ITP)** 是数学与计算机科学交叉的重要方向。AI 介入这一领域的核心任务,是在形式系统中自动构造或辅助构造证明,减少人类在低层次细节上的负担,使其更多地专注于高层次思路。 + +在 **形式化系统** 中: + +* 定理被编码为需要构造的目标类型(goal),证明对应为构造某个项,使其类型为该目标类型。 +* 证明过程由一系列战术(tactics)或推理步骤组成,每一步都在严格的逻辑规则下推进。 + +AI 在其中可以承担多种角色: + +1. **战术选择与参数推荐** :在当前证明状态下,预测下一步应使用的战术及其参数,减少人工尝试与回溯。 +2. **引理与定理检索** :从庞大的库中检索与当前目标最相关的引理 / 定理,缩小搜索空间。 +3. **端到端证明生成** :在给定定理与上下文的情况下,直接生成完整或局部证明脚本,再由证明器验证其正确性。 + +AlphaZero‑style 证明器、GPT‑f、Lean‑Dojo 等工作,通过在大规模形式化语料上训练策略与价值网络或语言模型,实现了在 Lean / Coq 等系统上自动完成相当比例定理的证明。在产品方向上,这类能力有望演化为“形式化验证助手”,用于软件 / 硬件验证、加密协议分析和高可靠系统设计。 + +### 10.5.2 符号计算与数学问题求解:LLM + CAS + +相比定理证明,**符号计算与数学问题求解** 更贴近工程与教育场景。其目标是: **从自然语言问题出发,自动构造符号表达、执行计算并给出可解释的解题步骤** 。 + +在这一方向上,典型的神经 – 符号协作流程为: + +1. **问题理解与抽象** :LLM 将自然语言或图片中的题目解析为结构化数学表达(方程、约束、目标函数等)。 +2. **符号表达生成** :将抽象结果翻译为 CAS 代码(如 SymPy 表达式、Mathematica 命令)。 +3. **调用 ****CAS** ** 执行** :使用 CAS 进行精确的代数运算、求导、积分、求解方程组、极限等。 +4. **结果解释与步骤生成** :LLM 基于 CAS 的计算结果,生成符合人类习惯的解题步骤与解释。 + +这一模式有几个关键优势: + +* 通过 CAS 保障计算的正确性,避免 LLM 在长算式上的“错位运算”与累积错误。 +* 通过 LLM 提供自然语言理解与表达,降低 CAS 的使用门槛,使非专业用户也能调用强大的符号工具。 +* 在教育场景中,可以控制解题的详细程度与风格,生成适合不同学习阶段的讲解。 + +在工程 / 金融场景中,这一能力可以扩展到复杂模型的公式化与分析:自动从文档与代码中提取模型结构,构造符号表示,并进行敏感性分析、边界情况分析与风险识别。 + +## 10.6 科学工作流与自动化实验(Scientific Workflow & Lab Automation) + +前面的子方向大多聚焦于“单点能力”:预测一个性质、生成一个结构、证明一个定理。然而在真实的科研与工业研发中,更关键的是如何把这些能力**串联成完整的** **工作流** ,并与文献、数据库、仿真平台与自动化实验设备打通。科学工作流与自动化实验方向,旨在构建面向科学场景的 **Agent + 工具 + 机器人** 一体化系统,让 AI 从“会算”进化到“会做实验、会做研究”。 + +这一方向一端连接论文与专利数据库(如 PubMed、arXiv)、科学数据仓库、领域知识图谱与仿真平台,另一端连接自动化实验室(Robotic Lab)、高通量筛选设备与科研流程管理系统。下面从 **场景** 、 **原理** 、**模型** 三个角度展开。 + +* **场景** + * 科学文献挖掘与知识库构建:从海量论文中自动提取化合物、蛋白、材料、反应条件、实验结果等信息,构建结构化知识库与知识图谱。 + * 实验设计与 Self‑Driving Lab:在 AI 提出的实验计划指导下,由机器人实验平台自动执行配制、反应、测量与数据采集,实现“闭环”优化。 + * 科学数据管理与可重复性保障:自动整理仿真与实验数据、元数据与代码脚本,生成标准化实验记录与报告,提高可追溯性与复现性。 + * 领域“AI 实验助手”:为药企、材料公司与科研机构提供一站式的文献检索、方案设计、实验规划与结果分析支持。 +* **原理** + * 文献挖掘与领域 LLM: + * 利用 SciBERT、BioBERT、PubMedBERT 等领域预训练模型进行命名实体识别、关系抽取、反应式解析与实验条件抽取。 + * 在此基础上训练 Bio‑LM、Chem‑LM、Materials‑LM 等领域 LLM,提升对专业术语、实验语句与隐含假设的理解与推理能力。 + * 实验设计与 Self‑Driving Lab: + * 将实验空间(配方、温度、时间、添加顺序等)视为优化变量,由 LLM + RL 或贝叶斯优化策略提出下一组实验条件。 + * 实验机器人与仪器按照计划执行,采集数据并实时回传,由模型更新参数与不确定性估计,形成主动学习闭环。 + * 工作流编排与 Agent: + * 在 Agent & Tool Use 框架下,将文献检索、代码生成、仿真调用、数据分析、可视化与报告生成工具统一纳入。 + * Agent 根据任务目标(如“寻找高导电电解质配方”),自动规划任务分解、调用工具顺序与结果整合。 +* **模型** + * 文献与知识挖掘模型: + * SciBERT、BioBERT、PubMedBERT 等:针对科学与生医文献进行预训练的模型,用于实体 / 关系抽取、分类与问答。 + * Galactica、领域特化 LLM:以科学语料为主进行训练,支持综述生成、代码草稿、实验设计建议等。 + * 实验规划与控制模型: + * LLM + RL / Bayesian Optimization:结合领域先验、模型不确定性与实验成本,对实验空间进行高效探索与 exploitation。 + * 与 Robotic Lab 控制接口集成的 Agent:将自然语言实验描述转换为结构化实验步骤与仪器控制命令。 + * 科学 Agent 与工作流系统: + * 在 7 章 Agent & Tool Use 能力基础上,构建面向科学场景的“多工具 Agent”:能够检索文献、生成代码、调用仿真、处理数据、绘制图表并写出报告初稿。 + * 产品与应用: + * 药企 / 材料公司内部的“AI 实验助手”与自动化实验台:用于加速配方开发、工艺优化与候选筛选。 + * 领域科学搜索引擎与知识图谱(Bio / Chem / Materials / Physics Knowledge Graph):支持语义检索、交互式探索与知识推理。 + * 科研流程管理平台:集成实验规划、数据记录、版本管理、可视化与报告自动生成,提高科研团队的效率与结果的可复现性。 + +### 10.6.1 科学文献挖掘与领域知识库构建 + +科学知识的绝大部分首先以论文与报告的形式出现。要让 AI 真正参与科研,就必须让其“读得懂论文,并从中提炼结构化知识”。 **科学文献挖掘与知识库构建** ,正是从非结构化文本出发,构建可查询、可推理的知识基础设施。 + +在这一方向中,核心任务包括: + +* **实体识别与标准化** :识别文献中的化合物、蛋白、材料、反应物、产物、实验设备与条件等实体,并与标准数据库(如 ChEMBL、Uniprot、Materials Project)对齐。 +* **关系与事件抽取** :从文本中抽取“谁与谁如何相互作用”“什么条件下产生了什么结果”等关系与事件,例如反应方程、配方–性能对应关系等。 +* **知识图谱** **构建** :将实体与关系组织为图结构,支持复杂查询(如“在某条件下提高某性能的所有已报道方法”)与路径推理。 + +为实现上述目标,常采用: + +* SciBERT、BioBERT、PubMedBERT 等预训练模型进行 NER(实体识别)、RE(关系抽取)与文档级事件抽取。 +* 在此基础上构建领域特化 LLM(Bio‑LM、Chem‑LM、Materials‑LM),用于进行更复杂的问题回答、综述生成与知识补全。 + +构建好的领域知识库与知识图谱不仅可以为研发人员提供更智能的检索与推荐服务,也为后续的实验设计、材料 / 药物逆设计提供数据与先验支撑。 + +### 10.6.2 Self‑Driving Lab 与科学工作流 Agent:从“读论文”到“做实验” + +在具备文献挖掘、建模与优化能力之后,下一步就是把这些能力与 **自动化实验平台** 结合,构建真正意义上的 **Self‑Driving Lab(自驱动实验室)** 与科学工作流 Agent。 + +在 Self‑Driving Lab 中,典型工作闭环为: + +1. **目标设定** :研究者给出宏观目标(如“提高某材料在特定条件下的导电率”)与约束条件(成本、安全性、工艺限制等)。 +2. **文献与知识检索** :Agent 调用文献检索与知识图谱,了解现有工作与经验规律,形成初始假设与实验设计空间。 +3. **实验规划与优化策略** :基于 LLM + RL / 贝叶斯优化策略,提出首批实验条件(配方、温度、时间、环境等)。 +4. **机器人执行与数据采集** :自动化实验台(Robotic Lab)执行实验,实时采集结果并回传。 +5. **模型更新与下一轮设计** :代理模型根据新数据更新参数与不确定性估计,再提出下一轮更有信息量或更有潜力的实验条件。 + +在更广义的 **科学****工作流**** Agent** 中,这一闭环会扩展到仿真、数据分析与报告生成等环节: + +* Agent 可以自动生成仿真代码或调用现有仿真工具,对某些实验条件进行前置评估; +* 在数据分析阶段,自动完成数据清洗、可视化与统计检验; +* 在项目阶段总结时,生成结构化的实验记录与报告草稿,附带图表与参考文献。 + +在产品形态上,这类系统往往以平台形式落地:提供一套统一的界面与 API,对接文献库、仿真引擎与实验设备,让科学家和工程师在高层用自然语言与可视化界面制定目标,其余环节由 Agent + 工具链自动编排与执行。 + +从这一子方向开始,AI 在科学中的角色真正从“离线分析工具”转向“在线科研合作者”:不仅能读论文、写代码、算模型,更能与机器人一起,完成一项项真实的实验与发现。 + +# 11. 平台与工程能力(MLOps / Infra) + +大模型从实验室走向企业生产,绝不仅是“模型本身足够好”就可以,而是要依托一整套稳定、可扩展、可运维的 **平台与工程体系** 。这套体系需要贯穿模型的**训练与微调、部署与推理优化、数据与模型运维、监控与成本管理、安全与合规、以及中台与应用支撑能力**等环节,把原本零散的技术点串成一个可持续运转的闭环。 + +从业务视角看,平台与工程能力往往决定了一个组织是否能“规模化地、安全且低成本地”使用大模型:同样的底层模型,如果没有良好的 MLOps 体系,很可能只能停留在 Demo 与试点阶段;而一旦具备完善的平台,企业就能在多个 BU、多个国家 / 区域、多个行业场景中快速复制与演进高质量应用。下面我们将分别从**模型训练与微调平台、部署与推理优化、数据与模型运维、监控与成本可靠性、安全与合规基础设施、以及上层应用与中台能力**六个方向展开阐述 + +## 11.1 模型训练与微调(Training & Fine-tuning) + +在基础模型层面,大部分组织不会从零开始训练千亿参数模型,而是基于开源或商用基座做 **继续预训练 + 微调** 。这一层的核心问题是:如何高效利用算力和数据,把通用大模型“拉近”到具体行业、企业和任务上,同时又要保证多模型、多版本的工程可管理性。 + +从工程视角看,这一层通常包含三块: **预训练与继续预训练** 、**微调****范式****与工具链**以及**大规模****分布式** **训练基础设施** 。 + +* **场景** + * 通用大模型底座研发:云厂商 / 大厂自研通用语言 / 多模态基座模型,用于对外 API 和内部多业务共享。 + * 行业大模型与专有模型:围绕金融、医疗、法律、制造、能源、游戏等特定领域,构建行业基座模型或“企业自有大模型”。 + * 企业级模型定制:为单一大客户(银行、保险、政府、制造集团等)基于其内部数据定制专属微调模型或 LoRA 权重。 + * 多租户模型市场:SaaS / 云平台为众多中小客户提供“一客一模型”的微调与托管能力,每个租户一套权重或适配层。 + * 一键微调平台:对非算法团队开放的“上传数据 → 选择底座模型 → 自动微调 → 一键部署”全托管产品。 +* **原理** + * 预训练与继续预训练: + * 在海量通用文本、代码、多模态数据上进行大规模预训练,使模型获得 **通用语言理解、世界知识与基本推理能力** 。 + * 对于特定行业,通过 **Domain‑adaptive Pretraining(DAPT)** 在通用模型之上继续预训练,引入行业专有术语、写作风格和知识分布。 + * 多语言 / 多模态预训练通过共享语义空间与联合训练,使模型具备**跨语言迁移**与**图文 / 语音 / 结构化数据融合**能力。 + * 微调范式: + * **全参数微调** :在目标任务与预训练分布差异极大、且有充足算力和数据时,直接更新全部参数,获得最高上限性能。 + * **参数高效微调(PEFT)** :通过 Adapter、LoRA / QLoRA、Prefix / P‑Tuning 等方式,仅训练极少量“增量参数”,适合多任务、多客户、频繁更新场景。 + * **指令** **微调与任务微调** :用“指令 + 示例”的方式让模型学会理解自然语言任务描述;既可以面向单一垂直任务,也可以在统一模型上承载多任务。 + * **RLHF** ** / RLAIF** :通过人类或 AI 反馈训练奖励模型,进一步用强化学习对齐模型行为(礼貌性、安全性、拒答策略、价值观)。 + * 分布式训练与工程体系: + * 使用 **数据并行、模型并行、流水线** **并行** **、** **张量****并行**等策略,将超大模型和大规模数据拆分至集群多节点、多卡协同训练。 + * 通过 ZeRO / FSDP 等技术**降低****显存** **占用、提升训练吞吐** ,配合高效调度(Kubernetes + Slurm / Ray)实现大规模集群训练。 + * 依托标准化的数据 pipeline(数据集加载、清洗、去重、分片、缓存)与微调框架(Transformers Trainer、DeepSpeed、Lightning 等)减少重复造轮子。 +* **模型** + * 预训练与继续预训练工具链: + * 训练框架:PyTorch、TensorFlow、JAX。 + * 大规模训练加速:DeepSpeed、Megatron‑LM、Colossal‑AI、Fairscale。 + * 分布式训练策略:数据并行(DP)、模型并行(MP)、流水线并行(PP)、张量并行;ZeRO / FSDP、Megatron(TP+PP)、DeepSpeed ZeRO。 + * 集群调度与管理:Kubernetes + Slurm / Ray / Horovod / TorchElastic。 + * 数据 pipeline:Hugging Face Datasets、WebDataset、Petastorm、tf.data、Arrow;对象存储(S3 / OSS / GCS)+ 本地 cache;数据清洗与去重工具。 + * 微调与 PEFT 工具: + * 微调框架:Hugging Face Transformers + Trainer / Accelerate、PyTorch Lightning、DeepSpeed、Colossal‑AI。 + * PEFT 工具集:PEFT(LoRA / QLoRA / Prefix Tuning / Prompt Tuning 等)、LLaMA‑Adapter 及各类 LoRA 工具链。 + * 指令与数据构建:Self‑Instruct、Alpaca / Dolly 风格 pipeline,各类数据增强与对话重写工具。 + * RLHF / RLAIF 工具链: + * TRL(Transformers Reinforcement Learning)、trlx、DeepSpeed‑RLHF、自研 RLHF pipeline。 + * 奖励模型训练、排序 / 评分模型、拒答策略与对齐策略模板。 + +在产品形态上,这一层往往体现为: **模型底座研发平台、企业级“代训+定制”服务、一键微调平台与模型市场(Model Hub / Model Store)** ,支撑从“通用模型”到“千企千模”的生产化路径。 + +### 11.1.1 预训练与继续预训练:从通用能力到行业基座 + +预训练是现代大模型能力的“源头工程”:通过对海量未标注文本、代码和多模态数据的自监督学习,模型逐渐获得语言建模、世界知识、基本推理与表示学习能力。在此基础上,继续预训练(特别是 **Domain‑adaptive Pretraining, DAPT** )则承担了“把模型拉向某个垂直领域”的任务。 + +在**通用预训练**阶段,核心关注点包括: + +1. **语料规模与多样性** :混合网页文本、书籍、代码、对话、多语种内容以及图文对等多模态数据,尽可能覆盖广泛的知识与表达形式。 +2. **训练目标与多任务混合** :除了经典的自回归语言建模外,有时会加入填空、下一句预测、对比学习、图文对齐等目标,提升模型的语义对齐与多模态理解。 +3. **多语言与对齐** :通过共享词表或子词编码,以及跨语种平行语料或对齐任务,使模型在统一向量空间中对不同语言进行建模,实现 **跨语言迁移与翻译** 。 + +在**行业继续预训练(DAPT)** 阶段,重点转向: + +1. **行业语料构建** :从医疗病历与指南、法律判决书与法规条文、金融研报与交易数据、制造 / 能源 / 游戏设计文档等渠道构建专有语料。 +2. **风格与术语适配** :通过大量领域内语料的继续预训练,使模型自然掌握行业术语、固定表达、专业写作风格与隐性知识(如临床表述习惯、法律措辞)。 +3. **企业级专有知识注入** :对于大型企业或机构,可在通用 + 行业语料之外进一步加入企业内部文档、知识库、工单记录等,训练“企业专有大模型”作为统一智能底座。 + +在工程实践中,预训练与继续预训练会配合大规模分布式框架(Megatron‑LM、DeepSpeed ZeRO 等)以及高效的数据 pipeline(WebDataset / HF Datasets + 对象存储)运行,形成 **稳定可复用的训练流水线** 。对于云厂商或大厂,这一流水线往往会被封装为内部平台,支持周期性增量预训练和多行业基座并行迭代。 + +### 11.1.2 微调范式与 RLHF:从“能说话”到“懂业务、守边界” + +在拥有强大的预训练基座之后,如何让模型“对业务有用”并“行为可控”,关键在于微调与对齐阶段。这里既包括传统意义上的监督微调(SFT),也包括指令微调、多任务微调和基于反馈的强化学习(RLHF / RLAIF)。 + +在**微调范式**层面,可以大致分为: + +1. **全参数微调(Full Fine‑tuning)** + 在任务分布与预训练差异很大,或对极致性能有刚性要求且算力充足的场景(如特定编程语言模型、特定语言 / 行业对话模型)中,直接更新全部参数可以获得最大性能上限。但其成本高、版本管理复杂,一般只在少数核心模型上使用。 +2. **参数高效微调(PEFT)** + 通过 Adapter、LoRA / QLoRA、Prefix / P‑Tuning 等方法,仅对插入的“小块增量参数”或权重低秩增量进行训练,原始大模型权重保持冻结。这带来了三点工程优势: + 1. 多任务 / 多客户可以共享同一基座,只切换不同的 Adapter / LoRA 权重。 + 2. 显著降低显存与算力需求,支持在中小型 GPU 集群或单机环境中完成微调。 + 3. 更新频繁、回滚简单,便于快速试错与 A/B 实验。 +3. **指令微调与任务微调** + 1. **指令微调(Instruction Tuning)** :通过“自然语言指令 + 输入 + 期望输出”的样本,让模型学会理解“帮我…”“请解释…”等人类指令形式,从而摆脱任务特定模板。 + 2. **单任务微调** :如仅针对客服问答、代码补全、法律咨询等垂直任务进行微调,最大化该任务表现。 + 3. **多任务微调** :在统一模型上同时承载多种任务(问答、摘要、翻译、代码、推荐理由生成等),提升模型通用性和资源利用率。 + +在**行为对齐与安全性**层面,**RLHF / RLAIF** 起到关键作用: + +1. **奖励模型(Reward Model)训练** :收集人类或 AI 对模型多种候选回答的偏好(排序 / 打分),训练一个能评估“回答好坏”的奖励模型。 +2. **强化学习(如 PPO)优化基座模型** :在奖励模型的指导下,通过强化学习调整模型参数,使其更符合人类偏好和平台价值观,例如: +3. 更礼貌、中立、专业; +4. 对危险、违规、隐私相关请求进行拒答或安全改写; +5. 在有不确定性时表明不确定,而非虚构事实。 +6. **RLAIF 与自监督对齐** :在部分场景下,使用强基座模型作为反馈者,或结合规则与自动化评估,对微调过程进行半自动对齐,降低人工标注成本。 + +工具链方面,Hugging Face Transformers + PEFT、TRL / trlx、DeepSpeed‑RLHF 等框架,已经基本形成了从 SFT → RM 训练 → RLHF 的**标准工业工作流** 。在产品定义上,这一层典型落地为:**模型定制 / 代训服务、一键微调平台、多租户模型市场与行业 / 企业专有大模型工程平台** 。 + +## 11.2 模型部署与推理(Serving & Optimization) + +在训练好大模型之后,如何以 **高可用、** **低延迟** **、可扩展、可降本**的方式提供推理服务,是 AI 工程体系的第二根支柱。部署与推理层一端连接 GPU / NPU 等算力集群,另一端连接 API 网关、企业应用和对外开放平台,其核心职责包括: **部署架构设计、模型路由策略、推理性能优化与硬件利用** 。 + +从整体来看,这一层要解决三个问题: **用什么架构对外服务** 、 **如何让推理更快更便宜** 、 **如何在多模型、多地域、多租户环境下保持高可用与可治理** 。 + +* **场景** + * 企业内部 AI 中台 / 模型服务总线:统一为各业务线提供大模型 API,屏蔽底层模型和硬件差异。 + * 对外开放云 API:向外部开发者与生态伙伴提供标准化的推理接口,支持多模型选择与版本管理。 + * 高 QPS 在线业务:客服助手、搜索、推荐、办公助手等对延迟和稳定性要求极高的场景。 + * 低成本离线生成:广告 / 游戏文案、知识库生成、代码批量重构等以吞吐与成本为主、对实时性要求不高的批处理任务。 + * 跨地域、多集群部署:为全球或多区域用户提供就近访问,同时支持多云或混合云形态。 +* **原理** + * 部署架构与模型路由: + * **单模型服务** :在早期或简单场景下,以一个主模型对外提供统一服务,架构简单,但难以兼顾延迟与成本。 + * **多模型服务与路由** :针对不同任务、延迟要求、成本约束、用户等级等维度,配置不同大小或不同专长的模型,并通过规则或 Meta‑model 进行请求路由(包括 A/B 测试、多臂老虎机 / Bandit 策略等)。 + * **多租户隔离与 ****SLA** ** 管理** :在多客户场景中,通过资源配额、QPS 限制、访问鉴权和 SLA 分级确保不同租户之间在性能与安全上的隔离。 + * **弹性扩容与高可用** :借助 Kubernetes / Service Mesh 等基础设施,实现自动扩缩容、多副本部署、灰度发布、蓝绿部署和跨区域容灾。 + * 推理性能优化: + * **模型压缩与加速** :通过量化(INT8 / INT4 / NF4 / GPTQ / AWQ)、剪枝 / 稀疏化、知识蒸馏等手段减少模型计算量与显存占用。 + * **系统级优化** :利用 KV Cache 缓存注意力键值,加速长对话与连续推理;通过批处理(Batching)、并行 token 生成和流式输出平衡吞吐与延迟;通过算子融合和图优化减少内存访问和内核启动开销。 + * **异构硬件利用** :针对 GPU、CPU、NPU、FPGA、ASIC 等不同硬件构建适配的 Runtime 与调度策略,在单机多卡、多机多卡场景下通过 NVLink / RDMA 等高速互联提升整体效率。 + * 工程与运维: + * 使用 vLLM、TGI、Triton 等专用推理框架,显著降低自研成本。 + * 通过 ONNX Runtime、TensorRT、TVM、OpenVINO 等编译器与 Runtime 进行跨平台部署与算子级优化。 + * 借助 Kubernetes、Ray、Service Mesh 和 API 网关构建统一的 **在线推理集群与流量调度层** 。 +* **模型** + * Serving 框架与推理服务: + * vLLM、TGI(Text Generation Inference)、Triton Inference Server。 + * Ray Serve、KServe、TorchServe、SageMaker Endpoint、Vertex AI Endpoint 等。 + * 集群与调度: + * Kubernetes(K8s)、Kubeflow、Ray、Slurm。 + * Service Mesh:Istio / Linkerd(支持灰度、限流、熔断、回退等流量治理)。 + * API 网关与鉴权: + * Kong、NGINX / APISIX / Envoy。 + * IAM / Keycloak / Auth0、云厂商 API Gateway、OAuth2 / OIDC 等。 + * 模型压缩与性能库: + * 量化:NVIDIA TensorRT‑LLM / TensorRT、Intel Neural Compressor、OpenVINO(PTQ / QAT)、BitsAndBytes、GPTQ、AWQ、AutoGPTQ。 + * 剪枝 / 稀疏:PyTorch Sparse、TensorFlow Model Optimization Toolkit、SparseML、Neural Magic。 + * 蒸馏:DistilBERT / TinyBERT 等参考方案,或基于 Hugging Face Trainer + 自定义 distillation loss 的蒸馏 pipeline。 + * 推理引擎 / Runtime 与图优化: + * ONNX Runtime、TensorRT、OpenVINO Runtime、TVM、MNN、NCNN。 + * 大模型专用推理引擎:Sglang、vLLM、FasterTransformer、TGI、LMDeploy、DeepSpeed‑Inference。 + * 编译与图优化:TVM、XLA(JAX/TF)、TensorRT Graph Optimizer、TorchDynamo / TorchInductor、MLIR、Glow、ONNX Graph Optimizer、Intel NNCF 等。 + * 硬件与异构支持: + * GPU:CUDA / cuDNN / cuBLAS、ROCm(AMD)。 + * CPU:oneDNN(MKL‑DNN)、OpenBLAS、Eigen。 + * NPU / 专用加速卡:Ascend CANN、Habana Gaudi、Graphcore IPU 等 SDK。 + +在产品侧,这一层常以 **企业 AI 中台 / 模型服务总线、对外云 ** **API** **、统一推理** **网关** **、高 ****QPS**** 在线推理集群、低成本****批处理****平台与****算力****利用率优化方案** 的形态出现,是支撑大模型能力规模化落地的运行时“操作系统”。 + +### 11.2.1 部署架构与模型路由:从单模型到多模型服务网格 + +在早期尝试阶段,很多团队会选择以一个“大而全”的模型作为**单一入口**提供服务:所有请求都经由同一个模型处理。这种模式架构简单、维护成本低,适合 POC 与低流量场景。但随着业务扩展和成本压力上升,单模型架构的不足会迅速暴露: + +1. 不同任务对延迟 / 成本 / 质量的要求并不相同,用同一个大模型处理所有请求会造成**算力** **浪费** 。 +2. 面向不同行业、不同客户需要提供差异化能力,例如行业专有模型、客户专属微调权重,很难在“单模型”模式下统一管理。 +3. 灰度发布、A/B 测试、跨地域灾备等场景要求能够在多个模型版本之间灵活调度。 + +因此,成熟的大模型服务体系往往会演进为**多模型服务与智能路由**架构: + +1. **多模型池与模型目录** :同时维护多种大小(small / base / large / ultra)、多种专长(通用 / 代码 / 多模态 / 行业专用)、多种版本(v1 / v1.1 / 客户定制等)的模型,并在服务层对其进行统一注册与管理。 +2. **路由策略** : +3. **规则路由** :基于请求参数(任务类型、用户等级、延迟 / 成本偏好等)以及业务规则(某行业某区域强制使用特定模型)进行显式选择。 +4. **模型选择器(** **Meta** **‑model)** :使用一个轻量级模型根据输入内容、历史效果、实时指标自动选择最优模型(如快速小模型 vs. 慢速大模型)。 +5. **A/B / Bandit 路由** :在新旧模型或不同配置之间进行在线实验,根据 CTR、用户满意度、任务成功率等指标自动收敛到更优方案。 +6. **多租户隔离与配额管理** : +7. 在模型路由之上叠加租户维度的配额控制、QPS 限制、访问鉴权与 SLA 分级,确保不同客户之间的资源与数据隔离。 +8. 通过**逻辑隔离 + 物理隔离(独占集群或专用节点)** n应对金融 / 医疗 / 政务等高合规场景。 +9. **弹性扩缩容与高可用** : +10. 基于 Kubernetes HPA / VPA、Cluster Autoscaler 实现按流量自动扩缩容。 +11. 通过多副本部署、负载均衡、灰度发布、蓝绿部署和多区域容灾保证服务稳定性。 + +技术上,往往会采用 **Kubernetes + Service Mesh(Istio / Linkerd)+ ****API** **网关** **(Kong / APISIX / ** **Envoy** **)+ 模型服务框架(vLLM / TGI / Triton / Ray Serve / KServe)** 的组合,形成一个既支持多模型、多租户,又支持流量治理与灰度发布的 **服务网格化推理平台** 。 + +### 11.2.2 推理性能优化与硬件加速:把“推理一次多少钱”压到最低 + +在大模型大规模商用场景中,推理成本往往是最大的持续支出之一。如何在保证体验的前提下,将**单位请求成本(Cost per Request / per Token)和端到端延迟**压缩到可接受范围,是部署层的核心技术挑战。 + +在 **模型侧** ,常见手段包括: + +1. **量化(Quantization)** + 通过将权重和激活从 FP16 / BF16 压缩到 INT8 / INT4 / NF4 等低比特格式,显著降低显存占用和带宽开销。 + 1. 训练后量化(PTQ):如 GPTQ、AWQ、BitsAndBytes 等,对已有模型进行离线量化。 + 2. 量化感知训练(QAT):在训练 / 微调阶段考虑量化误差,提升量化后精度。 +2. **剪枝** **与稀疏化(** **Pruning**** & Sparsity)** + 通过结构化 / 非结构化剪枝去除不重要的权重或通道,使模型稀疏化,并结合硬件友好的稀疏算子(如 NVIDIA 稀疏矩阵加速)提高推理速度。 +3. **蒸馏(Distillation)** + 使用大模型作为教师,将知识蒸馏到更小的学生模型或任务特定模型上,在大幅降低参数规模的同时保持接近的任务性能,适合对延迟极敏感的在线业务或边缘部署。 + +在 **系统与 Runtime 侧** ,关键优化点包括: + +1. **KV** ** Cache 与长上下文优化** : + 在自回归生成中缓存历史 token 的注意力键值,避免重复计算,从而提高长对话与多轮请求的效率;结合分块计算和动态裁剪策略控制显存开销。 +2. **批处理****与****并行** **生成** : + 通过对多个请求进行动态批处理、分组调度和并行 token 生成,在不显著增加 P95 延迟的前提下提高整体吞吐;结合流式输出(Streaming)改善前端交互体验。 +3. **算子与图优化** : + 使用编译器和 Runtime(如 TensorRT、TVM、ONNX Runtime、TorchInductor)进行算子融合、内存布局优化、静态图编译,减少 kernel 启动和内存访问开销。 +4. **异构硬件调度** : + 根据不同任务的计算特性与延迟要求,在 GPU、CPU、NPU、FPGA 等异构资源之间做合理分配: +5. 极度延迟敏感和高并发的对话 / 搜索请求优先调度到 GPU / NPU。 +6. 批量生成、离线评估、日志回放等任务可以调度到 CPU 或低成本 GPU / NPU。 + +工具与框架上,TensorRT‑LLM、SgLang、vLLM、FasterTransformer、LMDeploy、DeepSpeed‑Inference 等已经形成了一套相对成熟的**大模型** **推理加速生态** 。在业务侧,这些优化最终体现为:**高 ** **QPS** **、** **低延迟** **的在线推理集群、低成本批量生成平台、** **算力****利用率优化方案与 MaaS / ****API** ** 计费和成本核算系统** 。 + +## 11.3 数据与模型运维(Data / Model Ops) + +大模型一旦进入生产环境,就不再是“一次性交付”的静态资产,而是需要在**数据、模型、配置、版本和实验**五个维度持续迭代的动态系统。数据与模型运维层(Data / Model Ops)就是围绕这一现实构建的工程范式:从数据飞轮、模型生命周期管理到在线实验和自动化发布,为模型能力的**可持续提升与可控演进**提供基础。 + +这一层一端连接数据湖 / 数仓、日志与采集系统,另一端连接训练平台、评估体系和在线服务网关,是打通“数据–模型–业务反馈”闭环的中枢。 + +* **场景** + * 企业级数据中台 + 模型训练一体化平台:打通数据采集、清洗、标注、管理到训练 / 微调的全链路,支撑多模型持续迭代。 + * 面向 C 端 / B 端 AI 应用的“效果持续提升机制”:依赖用户反馈和使用数据驱动的数据飞轮。 + * 标注团队与算法团队共用的数据管理与标注工作台:支持任务分配、质检、版本回溯。 + * 集团级 ModelOps 平台:统一记录和管理所有模型版本、评估结果与发布状态。 + * 在线业务实验与灰度体系:支持 A/B 测试、多模型小流量试运行和自动择优放量。 + * 模型托管服务:为合作伙伴 / 客户提供“一处上传,多环境部署,多版本管理”的模型管理能力。 +* **原理** + * 数据管理与数据飞轮: + * **数据采集与治理** :从业务日志、用户对话、公开数据、合作方数据中采集样本,对其进行去重、降噪、脱敏、格式统一和质量评估。 + * **标注与反馈闭环** :通过专家标注与众包结合、配合质检机制构建高质量标注数据;将用户的点赞 / 点踩、纠错、人工复核等反馈回流至训练样本池。 + * **数据飞轮(Data Flywheel)** :模型上线后,持续收集真实使用数据 → 从中挑选高价值样本(如模型错误、低信度、高收益任务)→ 再训练或微调 → 模型效果提升 → 新一轮使用,形成正反馈循环。 + * 模型生命周期与发布: + * **模型版本管理** :为每个模型维护清晰的版本号(大小版本)、训练数据版本、配置参数、评估结果、安全报告与变更记录。 + * **CI/CD** ** 与自动化流水线** :训练完成后自动触发评估与安全检查,通过回归测试和阈值门控,只有在关键指标不过度退化的情况下才允许灰度发布与全量上线。 + * **实验与流量分配** :使用 A/B 测试、多臂老虎机等在线实验方法,对多版本模型进行对比,按实时业务指标(例如任务成功率、工单解决率、用户满意度)自动择优。 +* **模型** + * 数据湖与数仓: + * Delta Lake、Apache Hudi、Iceberg、Hive、BigQuery、Snowflake 等,用于统一存储与管理大规模结构化 / 非结构化数据。 + * 流式数据处理: + * Kafka、Pulsar、Flink、Spark Streaming 等,用于实时日志、用户对话和事件流接入。 + * 特征与样本管理: + * Feast 等 Feature Store、自研样本仓、ML Metadata Store,用于记录样本、特征和训练元数据。 + * 标注与质检平台: + * Label Studio、Scale‑like 平台、自研标注系统,支持多任务标注、质检与人员管理。 + * MLOps / ModelOps 平台: + * MLflow、Kubeflow、SageMaker、Vertex AI、Azure ML、Weights & Biases 等,用于管理训练实验、参数、指标和模型 artifact。 + * 模型注册与版本管理: + * MLflow Model Registry、SageMaker Model Registry、W&B Artifacts 等。 + * CI/CD 工具: + * GitHub Actions、GitLab CI、Jenkins、Argo CD、Flux 等,用于构建模型持续交付管线。 + +### 11.3.1 数据飞轮与训练闭环:让模型“越用越聪明” + +在传统软件开发中,版本升级往往由开发计划驱动;而在大模型时代,**数据与反馈**成为迭代的主要驱动力。数据飞轮的目标,就是把“模型使用 → 数据沉淀 → 再训练 → 模型升级”变成一条自动滚动的闭环,让模型在实际业务中 **越用越好用** 。 + +核心环节包括: + +1. **在线数据采集与筛选** + 在对话机器人、Copilot、搜索问答、代码助手等应用中,每一次用户交互都是潜在的高价值训练样本。通过日志系统和事件追踪,将请求、模型回答、用户行为(点击、采纳与否)结构化采集下来,并在采集端就进行隐私脱敏与字段裁剪,确保不额外引入合规风险。 +2. **高价值样本挖掘** + 在海量日志中筛选出对训练最有价值的一小部分样本,例如: + 1. 明显错误或被用户点踩的回答,用于“纠错式”再训练。 + 2. 高难度长问题、复杂工作流任务样本,用于提升模型在“长链推理 / 多步工具调用”上的能力。 + 3. 典型业务案例、高价值工单,用于构建行业 / 企业专有能力。 +3. **标注与质量控制** + 对候选样本进行人工或半自动标注(包括期望回答、优劣排序、安全性标签等),并通过多轮质检、复核和抽检手段确保标注质量,为后续 SFT 或 RLHF 提供可靠数据。 +4. **持续****再****训练与评估上线** + 周期性地将新样本加入训练集,进行 SFT / DAPT / RLHF 等再训练操作,并通过标准评测集和在线 A/B 实验同时评估“离线指标 + 线上效果”,确保新版本在总体上优于旧版本,避免数据飞轮“拐到错误方向”。 + +在成熟形态下,数据飞轮的绝大部分操作会被自动化封装进 **Data / Model Ops 平台** :从数据采集、样本筛选、标注任务派发,到模型再训练触发、评估结果收集和上线决策,尽量减少人工操作,使模型迭代成为一个稳定可控的工程流程。 + +### 11.3.2 模型生命周期与 ModelOps:从实验模型到生产资产 + +随着模型数量与版本的指数级增长,如果缺乏严谨的生命周期管理,很容易出现“模型散落各处、版本混乱、回滚困难”等问题。ModelOps 的目标,就是把模型当作**一等公民的工程资产**来管理,全程可追溯、可比较、可回滚。 + +关键要点包括: + +1. **版本化与****元数据管理** + 为每个模型分配明确的版本号(如 `industry-legal-base-v1.2.3`),并记录: + 1. 训练数据版本与时间范围; + 2. 训练配置(超参数、训练脚本版本、使用的代码 Commit); + 3. 评估指标(通用基准 + 业务特定基准); + 4. 安全评估与对齐策略(如敏感话题回答策略版本); + 5. 上线 / 下线 / 回滚历史记录。 +2. **端到端自动化流水线(** **CI/CD**** for Models)** + 将“模型训练完成 → 自动评估 → 安全与偏见检查 → 灰度发布 → 全量发布”的流程封装进 CI/CD 管线。 +3. 若离线评估指标未达到预设门槛,则自动阻断上线。 +4. 若在线 A/B 实验表现不佳,则自动降低流量或回滚到上一版本。 +5. **多版本共存与流量调度** + 在生产环境中,往往会同时存在多个模型版本(如 `stable` / `canary` / `experimental`),通过流量分配策略(固定比例、用户维度、特征维度)对其进行在线对比。 + 1. A/B 测试更关注稳定统计结论; + 2. 多臂老虎机(Multi‑armed Bandit)在探索与利用之间自动折中,加速收敛到效果更好的版本。 +6. **合规与审计支持** + 对于金融、医疗、政务等行业,需要对每一次模型版本变更保持可追溯记录:谁在何时基于什么数据把模型从哪个版本升级到哪个版本,以及升级后的影响评估如何。这部分通常与第 11.5 节中的**安全与合规基础设施**联动。 + +工程实现上,MLflow / SageMaker / Vertex AI / W&B 等工具已经提供了相对成熟的 ModelOps 能力,多数企业会在其基础上结合自身流程做二次封装,构建统一的 **内部模型注册中心与发布平台** 。 + +## 11.4 监控、成本与可靠性(Monitoring, Cost & Reliability) + +当大模型成为业务核心基础设施时,如何保证其 **可观测、可预警、可扩缩、** **可控成本** ,就成为 SRE 和平台团队的核心职责。监控、成本与可靠性层将传统可观测性体系与大模型特有指标结合,构建面向运维、算法与管理层的多维度视图。 + +这一层一端连接监控采集、日志 / 链路追踪系统,另一端连接业务 KPI 与成本分析平台,是保证模型服务“稳、快、省”的关键支柱。 + +* **场景** + * 面向运维 / SRE 的运行监控大盘:统一展示 CPU / GPU 利用率、QPS、延迟、错误率、告警等。 + * 面向算法团队的数据与模型质量监控平台:监控输入数据分布、模型漂移、提示工程效果与 RAG 命中率等。 + * 面向管理层的服务健康看板:将业务 KPI(转化率、满意度、任务完成率)与模型指标绑定展示。 + * AI 成本分析与优化平台:按模型、项目、业务线拆解算力成本,支持预算管理与成本优化策略。 + * 智能调度与弹性伸缩系统:根据负载与预算自动扩缩容或切换模型规格。 + * 对外 MaaS / API 计费与成本核算系统:支撑按调用量、token 数、算力使用量等维度计费。 +* **原理** + * 监控与可观测性: + * **多层监控** :从基础设施层(CPU / GPU / 内存 / 网络 / 存储)到服务层(QPS、P50 / P95 / P99 延迟、错误率、超时重试),再到模型层(token 使用量、上下文长度分布、响应长度、常见错误类型)。 + * **日志与链路追踪** :通过结构化日志记录请求 / 响应(在脱敏前提下),并携带模型版本、路由决策、租户信息;使用分布式追踪工具记录请求从 API 网关 → 模型服务 → 下游系统的完整链路。 + * **告警与分析** :设置阈值告警、异常检测和趋势分析,并与业务指标、成本和安全事件联动,实现快速定位与恢复。 + * 成本控制与弹性调度: + * **成本分析** :按模型、项目、业务线维度拆解 GPU / CPU / 存储 / 带宽成本,计算单请求平均成本和不同任务 / 客户的边际成本。 + * **弹性调度** :运用峰谷分时策略,在高峰期自动扩容、低谷期自动缩容;将离线批量任务错峰到夜间或低负载时段。 + * **策略性降级与按需加速** :在资源紧张时自动切换到小模型、更短上下文或更保守的推理配置;对高价值请求自动使用更大模型或更长上下文。 +* **模型** + * 监控与可视化: + * Prometheus + Grafana、VictoriaMetrics、Thanos 等指标采集与可视化方案。 + * 日志系统: + * ELK(Elasticsearch + Logstash + Kibana)、EFK(Fluentd / Fluent Bit)、OpenSearch 等。 + * 链路追踪: + * OpenTelemetry、Jaeger、Zipkin 等。 + * 模型特定监控: + * WhyLabs、Arize AI、Fiddler、Evidently AI 等,用于数据 / 模型漂移监控与输出质量评估。 + * 成本统计与分摊: + * K8s Metrics / Cost Exporter、Kubecost,以及各云厂商 Cost Management 工具(AWS Cost Explorer / GCP Billing / Azure Cost Management)。 + * 资源调度与弹性伸缩: + * K8s HPA / VPA、Cluster Autoscaler、Volcano、Ray Cluster Autoscaler。 + * 任务编排: + * Argo Workflows、Airflow、Prefect、Dagster 等。 + +### 11.4.1 监控与可观测性:从基础设施到模型行为 + +在大模型系统中,传统的 CPU / 内存 / QPS 指标已经不够,需要叠加一层“模型视角”的监控,才能真正看清系统健康状况。一个完整的可观测性体系通常包含: + +1. **基础设施与服务层监控** + 通过 Prometheus / Grafana、VictoriaMetrics 等采集并可视化: + 1. 节点 / Pod 级别的 CPU、GPU、内存、磁盘、网络使用情况; + 2. 服务级别的 QPS、P50 / P95 / P99 延迟、错误率、超时重试比例、连接数; + 3. 集群级别的资源使用率与容量预警。 +2. **模型层指标监控** + 针对大模型服务,除了常规性能指标外,还需要专项监控: + 1. 每次请求的 token 消耗(输入 / 输出)、上下文长度分布; + 2. 响应长度与截断比例,以排查因上下文 / 输出长度限制导致的质量问题; + 3. 常见错误类型统计(如超长输入、模型超时、工具调用失败等)。 +3. **日志与****分布式****链路追踪** + 1. 使用结构化日志记录请求参数(脱敏后)、模型版本、路由决策、租户标识、返回代码等信息。 + 2. 借助 OpenTelemetry、Jaeger、Zipkin 等追踪一次请求在 API 网关 → 模型服务 → 下游系统 → 回调链路中的全程,便于定位延迟瓶颈和故障点。 +4. **异常检测与智能告警** + 在传统阈值告警基础上,可以引入简单的统计监控或机器学习模型,对 QPS、延迟、错误率、token 分布等进行异常检测,当出现突变时自动报警,并联动自愈策略(如自动扩容、流量切换、服务降级)。 + +对于算法团队,还可以在这一层接入 WhyLabs、Arize、Evidently AI 等工具,对输入分布、模型输出特征、漂移情况进行长期跟踪,为后续数据飞轮与再训练提供信号。 + +### 11.4.2 成本分析与弹性调度:在“体验”和“预算”之间找平衡点 + +大模型服务最显著的运维挑战之一就是 **成本高且波动大** 。缺乏精细化的成本分析与弹性调度,很容易在业务增长时看不到“钱烧在哪儿”,也难以及时做出调整。一个成熟的成本和资源调度体系通常包括: + +1. **成本归因****与****分摊** + 利用 Kubecost、云厂商 Billing 工具以及自研账本,将 GPU / CPU / 存储 / 带宽成本按模型、项目、业务线、租户等维度拆解,让每个团队和客户都能看到自己对应的真实资源消耗与费用。 +2. **单位请求成本与****边际成本****分析** + 1. 计算每个模型 / 任务的单请求平均成本(Cost per 1k tokens / per request),对比不同模型和配置下的性价比。 + 2. 分析不同客户、不同业务场景的边际成本,为定价策略(API 计费)、SLA 分级和产品打包提供依据。 +3. **弹性扩缩容与峰谷利用** + 1. 通过 K8s HPA / VPA、Cluster Autoscaler、Ray Autoscaler 等机制实现自动扩缩容,保证在高峰期不炸服、在低谷期不闲置。 + 2. 将离线任务(如批量内容生成、日志重放、离线评估)安排在夜间或非高峰时段,以提高整体 GPU 利用率,平滑成本曲线。 +4. **策略性降级与按需加速** + 1. 在资源紧张或成本超预算时自动触发降级策略:使用更小模型、缩短上下文或输出、降低并行度。 + 2. 对高价值请求(如付费高等级用户、关键业务流程)自动使用更大模型、更长上下文或更丰富的工具调用能力,实现“按价值分配算力”。 + +在对外 API 场景,这一层还会与计费系统深度绑定,形成 **MaaS / API 计费与成本核算平台** :根据 token 使用量、调用次数、模型规格和请求类型进行计费,并为运营 / 销售提供成本与毛利分析。 + +## 11.5 安全、权限与合规基础设施(Security, Access Control & Compliance Infra) + +大模型能力一旦进入金融、医疗、政务等高敏感行业,安全与合规不再是“附加价值”,而是进入场景的前置门槛。安全、权限与合规基础设施层负责从**访问控制、数据安全、隐私保护到合规审计**构建系统级防线,保证模型服务在法律与监管框架内可靠运行。 + +这一层一端连接身份认证、权限管理、密钥与加密系统,另一端连接模型服务和日志 / 审计平台,是把“能用的模型”变成“敢用的模型”的关键。 + +* **场景** + * 金融 / 医疗 / 政务等高合规行业的本地化大模型平台:要求数据不出域、可审计、可追溯。 + * 企业统一 AI 访问控制与审计网关:对所有模型调用进行统一鉴权、权限管理和审计记录。 + * 多租户 SaaS / 云平台:需要在逻辑和物理层面为不同客户提供严格的安全隔离与合规支撑。 + * 面向合作伙伴 / 生态的开放接口:要求对 API 调用进行精细化权限控制和配额限制,并满足合规要求(如 GDPR 等)。 +* **原理** + * 访问控制与租户隔离: + * 使用 API Key / Token / OAuth / SSO 等方式进行身份认证。 + * 通过 RBAC(基于角色的访问控制)和 ABAC(基于属性的访问控制)在模型、功能、调用频率和数据范围等维度进行精细化权限管理。 + * 在多租户环境中实现**数据、日志、配置和模型权重**的隔离,防止跨租户访问与信息泄露。 + * 数据安全与隐私保护: + * 采用 TLS 加密传输、存储加密和集中式密钥管理(KMS)保障数据在传输与存储环节的安全。 + * 实施日志脱敏和数据最小化策略,仅保留业务与优化所必需的信息,并对访问行为进行审计。 + * 在必要场景中引入隐私增强技术(如数据匿名化、差分隐私、联邦学习)进一步降低隐私风险。 + * 合规与审计: + * 对模型发布、配置变更、权限变更、路由策略调整等关键操作进行全程留痕与审批。 + * 为每一个请求记录可追溯的元数据:请求来源、模型版本、决策依据(如使用的知识库 / 工具调用情况)。 + * 确保系统设计和运行符合金融、医疗、政务等行业监管要求以及本地与跨境数据合规规范。 +* **模型** + * 身份认证与权限管理: + * Keycloak、Auth0、Okta、各云厂商 IAM(AWS IAM / GCP IAM / Azure AD)。 + * OPA(Open Policy Agent)+ Rego Policy 等策略引擎,用于统一策略管理与执行。 + * API 安全网关: + * Kong、Apigee、Envoy、云厂商 API Gateway 等。 + * 数据与密钥安全: + * KMS(Key Management Service)、HashiCorp Vault。 + * TLS 终端、机密计算(Confidential Computing)等。 + +### 11.5.1 访问控制与租户隔离:保证“谁能用、能用什么、能用多少” + +在多业务线、多客户、多角色共同使用的大模型平台中,若没有细粒度访问控制和租户隔离,很容易出现权限滥用、数据泄露和资源争抢等严重问题。一个完善的访问与隔离体系需要在以下几个维度配合: + +1. **身份认证与****单点登录** + 通过 API Key / Token、OAuth2 / OIDC、企业 SSO 等方式,对内部员工、外部合作伙伴、第三方应用进行统一身份认证。对企业用户,可与现有身份系统(如 AD / LDAP / 企业 IAM)打通,避免重复账号体系。 +2. **细粒度权限控制(** **RBAC**** / ** **ABAC** **)** +3. RBAC:为管理员、算法工程师、业务运营、普通用户、合作伙伴等角色分别配置可访问的模型、环境(测试 / 生产)、操作(调用 / 配置 / 发布)与额度。 +4. ABAC:在角色基础上,引入租户 ID、项目 ID、数据域、时间段等属性,实现更灵活的策略(如“仅允许政务租户 A 在本地域调用本地化模型集群”)。 +5. **多租户隔离与配额管理** + 1. 在逻辑层面,通过租户 ID 隔离不同客户的调用、数据与日志; + 2. 在物理层面,对高合规客户(如银行 / 政府)提供专用集群或专用节点,实现更高等级的隔离; + 3. 配置不同租户的 QPS 限制、并发连接数和 token 配额,防止“某一租户暴冲拖垮全场”。 +6. **访问审计与策略评估** + 1. 对关键操作(如创建 / 删除 API Key、调整权限、修改配额)进行审计记录; + 2. 借助 OPA / Rego 等策略引擎,在执行前对复杂访问策略进行统一评估与解释,减少“策略散落代码中”的风险。 + +通过这层机制,平台可以在保证资源和数据安全的前提下,对内外部用户开放大模型能力,同时为后续合规审计和问题追责提供基础数据。 + +### 11.5.2 数据安全、隐私与合规审计:让模型“好用又合规” + +大模型往往会接触到大量敏感数据(用户对话、业务文档、交易记录等),一旦安全或合规出现问题,后果将极其严重。因此,需要在数据全生命周期和模型调用全链路上“多层防护”。 + +1. **数据传输与存储安全** + 1. 对所有外部和内部接口统一启用 TLS 加密,防止传输中被窃听或篡改; + 2. 对敏感数据采用静态加密存储,配合云厂商或自建的 KMS 管理密钥生命周期; + 3. 使用 Vault 等工具集中管理访问数据库、对象存储、第三方 API 所需的密钥和凭证。 +2. **最小化原则与脱敏** + 1. 只采集业务所必需的数据字段,并在日志与训练样本中尽量移除个人身份信息(PII)与敏感字段; + 2. 对不可避免要保留的标识符进行哈希或匿名化处理,降低泄露风险; + 3. 在 RAG / 知识库场景,对文档访问做权限分级,确保模型不会从“不该看的文档”中检索信息。 +3. **隐私增强技术与边缘约束** + 1. 在需要共享模型而不共享原始数据的场景中,引入差分隐私或联邦学习等方式,兼顾隐私与效能; + 2. 对政务、金融、医疗等场景,采用“数据不出域,模型下沉 or 本地部署”的模式,将训练 / 推理能力部署在合规域内。 +4. **合规与审计机制** + 1. 对模型发布、配置变更、权限调整等操作进行审批流与留痕,方便事后追溯; + 2. 对每次请求记录模型版本、调用方、路由决策、数据访问范围等元信息,在出现争议或调查需求时可以复盘; + 3. 定期输出合规报表(如数据访问审计、权限使用记录、异常事件报告),对接内部风控与外部监管要求。 + +这部分能力与 11.3、11.4 的 Data / Model Ops 和监控平台相互配合,共同构成一个“既能持续迭代,又能安全合规”的模型运行环境。 + +## 11.6 上层应用与中台能力(Application Enablers) + +有了从训练到推理、安全与运维的完整基础设施,还需要一层面向业务与开发者的“能力层”,将底层大模型抽象成更易用、更贴近业务语义的组件与服务。这一层通常被称为 **AI 中台、应用使能层或 Copilot 平台** ,其职责是:把大模型 + RAG + Agent + 工作流封装成标准化能力,让业务团队与生态伙伴可以快速搭建 AI 应用。 + +这一层一端连接模型 API、RAG 引擎与 Agent Orchestrator,另一端连接 CRM / ERP / OA / 工单等业务系统,是“从模型能力到业务场景”的关键桥梁。 + +* **场景** + * 企业 AI 中台 / Copilot 平台:为 CRM、ERP、OA、客服、营销、研发等内部系统统一提供对话、RAG、Agent 等智能能力。 + * 面向开发者与生态伙伴的应用开发平台:通过 SDK、模板工程、可视化编排工具,让第三方快速构建和部署 AI 应用。 + * 行业 SaaS 产品的 AI 后端:如智能客服云、营销云、办公协同云、研发管理云等,将 AI 能力嵌入原有产品体系。 + * 垂直场景助手:代码 Copilot、销售助手、运营助手、法务助手、医生助理等,通过中台能力迅速组合出场景化解决方案。 +* **原理** + * 对话与 Agent 能力: + * **会话管理与记忆** :维护多轮对话状态与长期记忆,支持话题切换、上下文压缩和个性化画像。 + * **工具调用(Tool Use)与****工作流** **编排** :通过函数调用或插件机制,将模型与外部系统(数据库、搜索、业务 API、第三方服务)连接起来;在复杂任务中使用 Workflow / Orchestrator 将多步操作串联起来。 + * **多 Agent 协作** :为复杂任务拆分出不同角色(如规划者、执行者、审阅者),以协作方式完成任务分解与结果聚合。 + * RAG 与知识库: + * **文档解析与预处理** :对 PDF、Word、网页、扫描件等文档进行解析、切块、结构化。 + * **向量化与检索** :使用 Embedding 模型对文本 / 表格 / 代码等内容进行向量化,构建向量索引;结合关键字检索与向量检索实现高召回。 + * **检索 + 生成(RAG)与证据链** :在推理时先从知识库检索相关内容,再由大模型基于检索结果生成回答,并输出引用与证据链,提高准确性与可解释性。 + * **知识图谱** **与结构化知识融合** :将领域知识图谱、业务数据表、规则系统与 LLM 结合,提高对结构化查询与复杂约束的处理能力。 + * 开发者接入与二次开发: + * **多语言 SDK 与 ****API** ** 设计** :提供 Python / JS / Java / Go 等语言的 SDK,封装调用模式、重试与幂等处理。 + * **模板与****低代码** ** / 无代码搭建** :通过预制模板工程与可视化“搭积木”式工具,让非专业开发者也能搭建 RAG / Agent / Workflow。 + * **插件与中间件** :提供与常见业务系统(CRM / ERP / OA / 工单系统等)的插件或中间件,降低系统集成成本。 +* **模型** + * 对话 / Agent 框架: + * LangChain、LlamaIndex、Haystack、Semantic Kernel 等。 + * 自研 Orchestration 层:通常包含 Workflow Engine、Tool Router、Memory 管理模块。 + * RAG 与向量检索: + * 向量数据库:FAISS、Milvus、Qdrant、Weaviate、Pinecone 等。 + * 文档解析:unstructured、Textract、pdfplumber、Apache Tika 等。 + * SDK / 接入层: + * 官方或自研 SDK、前端组件库(聊天组件、提示模板管理、对话记录视图)。 + * 与业务系统(CRM / ERP / OA / 工单等)的中间件 / 插件。 + +### 11.6.1 对话与 Agent 编排:从“问答机器人”到“任务协作体” + +相比早期的 FAQ 式问答机器人,现代大模型驱动的应用更像是“会用工具的智能协作者”。对话与 Agent 编排的目标,是把大模型从“语言生成器”升级为能够**调用工具、执行计划、协调多角色**的智能体。 + +1. **对话管理与记忆机制** + 1. 维护对话上下文、用户画像和长周期记忆,在多轮交互中保持一致性与连贯性; + 2. 对超长对话采用摘要、检索式记忆等方式进行压缩,避免上下文“爆表”; + 3. 在企业内应用中,引入身份与权限信息到对话上下文中,使回答与操作符合用户在业务系统中的权限。 +2. **工具调用(Tool Use)与****工作流****编排** + 1. 为模型提供结构化工具列表(如“查订单”“创建工单”“查询库存”“调用搜索引擎”等),并通过函数调用接口让模型在需要时主动调用; + 2. 使用 Orchestrator 根据模型提出的计划,协调多个工具调用的顺序、数据流与错误处理; + 3. 对复杂业务流程(如审批流、报销、售后处理)进行工作流建模,让 Agent 可以扮演“流程协调者”的角色。 +3. **多 Agent 协作模式** + 1. 将复杂任务拆成多个角色:如“任务规划 Agent”“信息检索 Agent”“执行 Agent”“质检 / 审核 Agent”; + 2. 通过消息通道或共享内存实现 Agent 间协作,提升复杂任务的鲁棒性与可解释性; + 3. 在企业环境中,可以将人类角色也纳入协作环中,如“AI 起草–人类审核–AI 修改–系统执行”。 + +这一层通常借助 LangChain、Semantic Kernel、LlamaIndex 等现成框架,并配合自研的 Orchestration 服务,将对话、工具、工作流、权限和审计统一在一套“Agent 平台”内。 + +### 11.6.2 RAG、知识库与开发者平台:把企业知识“接到模型脑子里” + +大模型再强,也不可能天然掌握每一家企业的私有知识,更无法实时知道最新的政策、产品和业务规则。RAG + 知识库 + 开发者平台,就是把这些**企业知识、行业知识和实时数据**以工程化方式接入模型能力的关键路径。 + +1. **文档解析与知识入库** + 1. 通过 unstructured、Textract、pdfplumber、Tika 等组件,将 PDF、Office 文档、网页、图片扫描件解析为结构化文本; + 2. 按章节、标题、语义块等进行“切块”,为后续向量化与检索提供合适粒度; + 3. 对于表格数据、业务数据库、API 文档等结构化信息,构建对应的 schema 映射和访问接口。 +2. **向量化、索引与检索重排** + 1. 使用 Embedding 模型将文本 / 代码 / 多模态内容转换为向量,存入 FAISS、Milvus、Qdrant、Weaviate、Pinecone 等向量数据库; + 2. 同时保留关键词索引与元数据过滤能力(如按租户、部门、文档类型过滤),组合出高精度的“检索前过滤 + 语义检索 + 重排”流程; + 3. 在查询时,将检索结果与原始问题一起喂入大模型,实现“检索增强生成(RAG)”,并返回引用与证据链。 +3. **RAG 应用模板与****低代码****搭建** + 1. 为常见场景(知识问答、政策解读、产品说明、内部文档助手等)提供预制 RAG 模板; + 2. 通过可视化配置界面(选择知识源、设置切块规则、选定向量模型与大模型)快速搭建专属知识助手; + 3. 将这些能力以 SDK 形式暴露给开发者,支持在 Web、移动端、桌面端或业务系统插件中快速嵌入。 +4. **开发者平台与生态集成** + 1. 提供 Python / JS / Java / Go 等语言 SDK,以及前端组件(聊天气泡、文档引用区、反馈按钮等),降低集成门槛; + 2. 为主流业务系统(CRM / ERP / OA / 工单)提供插件或中间件,使其可以“勾选几项配置”就接入 AI 能力; + 3. 对外开放应用开发平台,让生态伙伴基于底座模型、RAG 与 Agent 能力构建自己的行业应用,形成“平台–生态–终端客户”的正循环。 + +这一层最终将复杂的模型与基础设施能力封装成“可复用、可拼装的业务组件”,帮助企业在**安全、合规、成本可控**的前提下,以更低门槛、更快速度,把大模型真正变成推动业务创新的生产力工具。 diff --git a/docs/extra/extra4/extra4-what-is-ai-ide-and-trae.md b/docs/extra/extra4/extra4-what-is-ai-ide-and-trae.md new file mode 100644 index 0000000..2763358 --- /dev/null +++ b/docs/extra/extra4/extra4-what-is-ai-ide-and-trae.md @@ -0,0 +1,169 @@ +# 扩展知识 4 - 什么是 AI IDE 和 Trae + +在之前的学习阶段,我们使用 z.ai 搭建了最简单的 Web 程序和网页小游戏。但如果我们想要构建更复杂的应用——比如功能更完善的网站、桌面程序,甚至是手机应用——就必须在自己的电脑上使用专业的编程软件来编写代码。 + +最早的时候,只需要在一个简单的文本文件里写好程序,再用专门的语言处理器去读取并打包执行就够了。但随着代码量越来越大、项目结构越来越复杂,人工管理大量文件、手动编辑庞大的项目变得越来越困难。开发者因此迫切需要一种工具,能够高效管理和切换大量代码文件,支持多种编程语言的语法高亮,并可以快速定位和调试问题。于是,集成开发环境(IDE,Integrated Development Environment)就应运而生了。 + +你可以把 IDE 理解成一种专门用来“编辑、管理、运行和调试”各种应用源代码的程序。在真正打包发布之前,不同语言写出来的程序本质上只是特定格式的代码文件而已,你可以用普通文本编辑器打开它们,也可以用 IDE 打开。早期的计算机几乎完全通过终端来操作(只用键盘就能完成所有操作,几乎不需要鼠标),所以早期的 IDE 外观也非常“原始”——除非你额外安装插件来实现简单的交互式界面。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image1.png)![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image2.png) + +终端界面(Terminal) + +图片来源:https://en.wikipedia.org/wiki/File:Emacs-screenshot.png + +一个非常知名、功能成熟的“内置 IDE”叫做 `Vim`。在很多服务器上,你都可以直接用它来编辑文件(服务器通常没有显示屏,只能通过键盘远程操作)。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image3.png) + +现代 IDE 通常具有更加美观直观的图形界面,并提供更强大的编辑、运行和调试能力。一个典型的 IDE 通常包含以下核心组件: + +* **源代码编辑器(Source Code Editor)**:专门用于编写和编辑代码的文本编辑器,一般具备语法高亮、代码自动补全、实时错误提示等功能。 +* **构建与运行工具(编译器 / 解释器)**:IDE 内置编译器或解释器,可以将开发者写好的源代码转换成计算机可以执行的机器代码。 +* **调试器(Debugger)**:用于测试和排查代码错误的工具。它支持逐行执行代码、查看变量状态、设置断点等,帮助开发者定位并修复程序中的问题。 + +除此之外,现代 IDE 往往还内置版本控制工具(如 Git)和项目管理工具等实用功能。当下最流行的 IDE 之一是微软出品的 **[Visual Studio Code (VS Code)](https://code.visualstudio.com/)**。它轻量、可扩展性极强,因此被广泛使用。当然,也有很多开发者推荐 JetBrains 家的专业 IDE,比如用于 Python 的 PyCharm、用于 C/C++ 的 CLion 等,它们对特定语言提供了更深入、更完整的支持。但从入门友好度和通用性角度出发,我们更推荐初学者优先选择 VS Code 作为主要开发工具。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image4.png) + +# 现代 IDE:VS Code + +Visual Studio Code(简称 VS Code)是由微软开发的一款免费、开源且功能强大的现代代码编辑器。自 2015 年发布以来,凭借优异的性能和灵活性,它迅速成为全球最受欢迎的开发工具之一。 + +VS Code 的核心理念之一是“一切皆插件”。不同编程语言可以用来编写不同类型的程序,而每种语言都有自己独特的语法高亮规则和导航能力(比如“跳转到定义”“查找引用”等)。要让一个 IDE 原生支持所有语言几乎是不可能的——从逻辑上讲,你会需要为每一种语言单独准备一个 IDE 才行。 + +VS Code 巧妙地通过“插件机制”解决了这一问题。比如,如果你要写 Python,就安装 Python 插件,它会提供 Python 专属的语法高亮、自动补全和代码导航功能;如果你要写 C/C++,则可以安装对应的 C/C++ 插件来获得相应支持。在不安装任何插件的情况下,VS Code 本质上只是一个“高级的文本文件管理器”;当你为某种语言安装了对应插件之后,它就会“变身”成该语言的理想开发工具。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image5.png) + +除了编写代码以外,你甚至可以把 VS Code 当作编辑 Markdown 文档的工具来使用。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image6.png) + +总之,你可以在 VS Code 的扩展市场中浏览和下载各类扩展,为不同类型的文件提供更好用的编辑体验,也可以根据需要搜索不同语言和调试工具的插件,尝试它们如何提升你的工作效率。 + +# 现代 AI IDE + +上面介绍的都属于“传统意义上的现代 IDE”。但随着人工智能时代的到来,越来越多的代码开始由大语言模型来自动生成,这自然催生出一种新的开发工具形态——AI IDE,也就是可以利用大语言模型自动写代码的 IDE。 + +在最新版 VS Code 中,已经内置了一个大语言模型助手。你可以直接针对整个代码仓库、某个文件,甚至某个函数与模型对话。 + +你也可以像之前在 Web 端使用自动写代码工具一样,将需求以提示词的形式发给内置的编码 Agent,让它自动帮你实现所需功能、创建文件、修改代码、配置环境等。 + +典型的 AI IDE 一般具备以下核心能力: + +* 智能代码生成与补全:在传统 IDE 中,我们通常是输入几个字符来补全变量名或函数名;在现代 AI IDE 中,你可以写几行伪代码或者简单说明需求,让 IDE 自动补全完整的逻辑,甚至根据指令直接生成一大段甚至整块代码。 +* 代码理解与问答:IDE 能够理解并回答关于某段代码、某个文件,甚至整个工程目录结构的问题。 +* 代码重构与优化:IDE 可以根据你的意图,重写或优化指定代码片段的实现逻辑。 +* 自动生成测试:IDE 可以自动生成针对不同函数和模块的测试代码,方便你进行有针对性的测试。 +* Agent 式任务执行:智能 Agent 可以自动生成、打包、安装、运行和修改代码,在很多任务上可以部分替代初级软件工程师的工作。 + +在最新版 VS Code 中,你可以点击右上角的侧边栏入口,打开 AI 功能区域,体验这些能力。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image7.png) + +不过,VS Code 并不是 AI 能力最强的 IDE。对于需要大量 AI 辅助编码的场景,我们往往希望使用“更聪明、效率更高”的工具——好的 AI IDE 能显著节省写代码和改 Bug 的时间。下面我们会介绍几款目前比较流行的 AI IDE,重点讲解 Trae IDE。你可以根据个人喜好选择任意一款 AI IDE 使用。 + +由于 VS Code 是开源的(任何人都可以下载源码并自行编译),目前市面上绝大多数 AI IDE 都是在 VS Code 基础上二次开发而来。所以你不必担心要“学习很多种 IDE”——一旦你熟悉了 VS Code 的基本用法,迁移到这些 AI IDE 基本不需要重新学习。 + +如果要简单概括这些 AI IDE 之间的差异,主要集中在四个方面:价格;可使用的模型种类(部分高级模型在某些地区可能受限);Agent 的能力(在协助写代码时的智能程度和执行能力);以及运行速度与性能。 + +## Trae + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image8.png) + +Trae 是字节跳动推出的一款 AI 编程助手,支持 100 多种编程语言,并能集成到主流 IDE 中。它的功能包括:用自然语言生成代码、自动调试、把设计稿转换为 React/Vue 组件等。在 2025 年 8 月的更新之后,Trae 新增了智能依赖导入、重命名建议、任务清单管理等功能;SOLO 模式也开始支持后端代码生成和技术架构文档编辑。 + +## Cursor + +Cursor 是 Anysphere 开发的一款 AI 代码编辑器,基于 VS Code 定制,重点优化了大规模代码仓库和多文件协同的场景。它支持 GPT-4o、Claude 3.7 等模型;2025 年推出的 Claude Max 模式可以处理数百万行代码级别的项目。专业版取消了请求次数限制,非常适合复杂的企业级项目。 + +目前,Cursor 可以说是“带前端界面的 AI IDE”中综合体验最好的一款之一,用户数量庞大,功能迭代频率也很高。它最大的缺点是价格较高——专业版大约需要每月 20 美元。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image9.png) + +## Qoder + +Qoder 是阿里巴巴推出的一款强调“透明协作”和“增强上下文工程能力”的 AI IDE。它通过 Action Flow 支持把任务拆解成多个步骤,并实时跟踪 AI 的执行过程;还支持多模型动态路由和任务状态机管理,非常适合在中大型项目中做架构治理和对遗留系统进行“反向工程”分析。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image10.png) + +## CodeBuddy + +CodeBuddy 是腾讯云推出的一款 AI 编程工具,强调对中文指令的支持以及企业级合规能力。它提供代码补全、批量代码审查和多模型切换等功能;其中的 Craft 智能体可以实现多文件代码生成和 API 集成。企业版支持私有化部署,并通过了三级等保认证,适合金融、医疗等对数据安全要求较高的行业。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image11.png) + +## windsurf(已不推荐) + +Windsurf 最初因其基于 Agent 的 AI 编程能力而受到关注。但由于 2024 年团队调整以及模型权限问题,它的稳定性大幅下降,目前已经不再推荐使用。尽管在前一年它还可以与 Cursor 分庭抗礼,但现在基本可以视为“被淘汰”的工具。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image12.png) + +## VS Code + Cline + +Cline 是 VS Code(Visual Studio Code)的一款 AI 编程 Agent 插件,可以通过配置不同的 API 端点来灵活切换所使用的大模型。Cline 支持多模态输入、MCP 工具扩展以及成本监控,所有操作都需要用户确认后才会执行。它非常适合用于快速验证想法,或与现有开发流程集成。基础功能是免费的,企业版则支持在私有环境中部署模型。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image13.png) + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image14.png) + +# 什么是 Trae + +Trae 的全称可以理解为 “The Real AI Engineer”,是一款由字节跳动开发的自适应 AI 集成开发环境(IDE)。它是在流行的 VS Code 基础之上构建的,这意味着,如果你之前已经习惯了 VS Code,那么在使用 Trae 时,无论是界面布局还是基础操作都会感到非常熟悉、舒适。 + +Trae 的核心目标是成为开发者的“智能编程伙伴”。通过深度集成 AI 能力,它可以自动处理大量重复性工作,为你提供更直观、更高效的开发体验。它并不仅仅是一个“代码补全工具”,而是希望贯穿整个开发工作流,从创建项目、编写代码、调试、测试到部署都提供帮助。 + +## 安装 Trae + +Trae 分为国际版和中国版。国际版需要能够访问海外网络,但可以使用 GPT-5、Claude 4 等最新的海外模型;中国版则主要支持国内最新的大模型,例如 GLM、Qwen、Kimi 等。 + +国际版下载地址: + +https://www.trae.ai/ + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image15.png) + +中国版下载地址: + +https://www.trae.cn/ + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image16.png) + +## Trae 界面简介 + +简单来说,Trae 和 VS Code 看起来几乎一模一样。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image17.png) + +右侧的侧边栏就是 Copilot 交互窗口,也可以理解为 Agent 窗口。如果你暂时看不到它,可以点击 Trae 右上角的侧边栏图标将其打开。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image18.png) + +打开侧边栏之后,你会看到一个 `Builder` 选项,这就是 Agent 模式。简单理解,它相当于 z.ai 的“本地版”,可以帮你操作本机环境,安装运行环境、打开网页等。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image19.png) + +点击 “Builder” 后,你会看到 “Chat” 模式和 “Builder with MCP” 模式: + +* **Chat 模式**:主要用于和当前文件夹里的代码对话,或者当作普通聊天模型来使用。(你可以通过左上角的 “File” 菜单打开一个文件夹,在这个文件夹中进行编辑操作。在这种情况下,Builder 创建或修改的文件都只会发生在这个文件夹内部。) +* **Builder with MCP 模式**:为 Agent 提供了更多可用工具(例如把语言模型和其他软件联通起来、查询天气等)。你可以简单理解为:MCP 能让语言模型更方便地调用各种外部工具。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image20.png) + +在下面的区域,你还会看到模型选择选项,点击即可修改当前使用的大模型。在中国版中,你可以选择使用 Kimi k2 或 GLM 等国内模型;如果你使用的是国际版 Trae,还可以选择 ChatGPT 或 Claude 等海外模型。不过,由于国内大模型发展非常快,Kimi、Qwen、GLM 等在很多任务上的实际体验已经接近 Claude 3.5 或 3.7,对日常开发来说已经完全够用。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image21.png) + +以上就是对 Trae 的一个简单介绍。接下来,我们可以回顾一下之前在 z.ai 中做过的操作,并尝试在 Trae 中做同样的事情。 + +## 使用 Trae 安装 Python 和前端环境 + +大多数情况下,我们的 Windows 笔记本电脑默认不会预装前端开发所需的 Node.js 环境,或用于后端 / 通用开发的 Python 环境。我们可以尝试直接在 Trae 的 Agent 模式中跟它对话,让它帮我们安装 Python 环境或 Node.js 环境。 + +![](../../../English/Extra/Extra%20Knowledge%204%20-%20What%20is%20AI%20IDE%20and%20Trae/images/image22.png) + +## 📚 作业:用 Trae 写你的第一个程序 + +接下来,请尝试用 Trae 来完成你的第一个程序!你还记得之前的 AI 贪吃蛇游戏吗?把当时在 z.ai 中使用的那条提示词原封不动地输入到 Trae 的 Agent 模式中,看看会发生什么吧! + diff --git a/docs/extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md b/docs/extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md new file mode 100644 index 0000000..eca1e7e --- /dev/null +++ b/docs/extra/extra5/extra5-what-is-rag-and-how-does-it-work-and-future.md @@ -0,0 +1,943 @@ +随着大型语言模型(LLM)的广泛应用,企业面临一个现实问题:如何让模型准确回答基于内部文档、实时数据或专业知识的问题?毕竟,模型的训练数据有限且存在时效性,无法覆盖企业特有的业务知识和不断更新的信息。 + +一个直观的解决思路是:既然模型的上下文窗口正不断扩大,从8K、128K到如今突破百万token,那何不直接将相关文档塞进提示词,让模型基于这些材料生成答案? + +然而,能够处理长上下文与能在企业级场景中稳定、高效、可控地交付正确答案是截然不同的两件事。盲目依赖长上下文会带来成本飙升、注意力分散、知识更新滞后等一系列严峻挑战。 + +正是为了解决这些痛点,一种名为检索增强生成(RAG)的技术应运而生。RAG让大模型在生成答案前先精准检索外部知识,相比简单粗暴地扩展上下文长度,它以更低成本、更高准确性和更强可控性,满足企业级应用对事实准确与知识鲜活的严苛要求,成为构建可信AI应用的关键基石。 + +在本篇教程中,我们将系统介绍什么是RAG,追溯其诞生的背景与核心原理,并深入探讨其从基础到进阶的演化路径,以及未来的发展方向。 + +# 本节课你将学到 + +* RAG的核心价值:深入理解它如何解决长上下文在成本、注意力、知识更新上的核心难题 +* RAG的工作原理:通过具体案例看它如何完成从检索到生成的闭环 +* RAG的技术演进脉络:从基础的Naive RAG到Advanced RAG再到模块化的Modular RAG +* RAG的模型选型建议:掌握Embedding、Rerank和LLM三大关键模型的评估与选择策略 +* RAG的企业级实践:学习从数据预处理到系统上线评估的全链路构建指南 +* RAG的效果评估与调优:了解核心评测指标、主流框架与持续优化的方法 +* RAG的前沿趋势:探索其与智能体、多模态等技术融合的未来方向 + +完成本教程后,你将建立起对 RAG 技术入门级的系统性理解,不仅知其然,更知其所以然。你将获得一个清晰的蓝图,知道如何评估、选型设计一个符合企业级要求的高效、可靠且可控的 RAG 系统,为开发真正的企业级 RAG 应用打下坚实基础。 + +# 1. 为什么需要 RAG + +检索增强生成(Retrieval-Augmented Generation,RAG)是当前生成式 AI 中非常重要的一种技术方式。它的基本思路是:在让大模型生成回答之前,先从外部知识库中检索出与问题相关的信息,再把这些检索结果连同用户的问题一起交给模型,让模型在参考真实资料的基础上作答。这个外部知识库可以是企业内部的制度与流程文档、产品知识库,也可以是行业数据库、法规标准库等。 + +![](images/image1.png) + +但此时我们会有一个疑问:既然大模型本身已经可以“直接回答问题”,为什么还需要额外增加“检索增强生成”这一层?尤其是现在大模型的上下文窗口越来越大,似乎只要把相关资料都提供给模型,让它先理解再回答,也能解决大部分需求。 + +真正的区别在于: **“能给出一个回答”** 和**“在真实业务环境中,持续、稳定、可控地给出正确答案”** ,是两件完全不同的事情。如果只是依赖模型参数中的“记忆”,或者仅仅把大量文档放进长上下文中,在企业实际应用中,依然会暴露出至少三类典型问题: + +1. **成本与效率的问题** : + 即便大模型的上下文持续扩容,试图将所有文档 “一股脑通通塞进去” 的做法,在实际应用中依然不现实。核心矛盾集中在两点: +2. 推理成本与上下文长度呈强正相关:上下文越长,推理成本几乎呈线性甚至超线性上升。以单次调用为例,8K Token 与 200K Token 对应的价格、响应延迟,完全处于不同量级,长上下文的成本门槛显著更高; + ![](images/image2.png) + + > 上下文(context)从意义上指模型在回答当前问题时所“参考”的背景信息与对话历史;从技术上则是指一次推理时输入给模型的 Token 序列(如 system/user 指令、历史消息、检索片段等)的总和。 + > + > “上下文窗口”是这批输入内容的 **容量上限** :模型一次最多只能“看到”这么多 Token。在当前主流的大模型架构(例如 Transformer)中,这些 Token 会在模型的每一层里彼此做注意力计算、反复参与运算,因此一旦窗口变长、Token 变多,计算量和成本都会成倍甚至指数级增加。 + > +3. 计算资源存在大量浪费:绝大多数任务仅需极少量与当前问题高度相关的信息,将全量文档塞入上下文,会造成严重的计算资源闲置与浪费,进而降低系统吞吐量,拖慢响应速度,最终影响用户体验。 +4. **注意力与聚焦的问题** : + 大模型虽能 “覆盖” 超长上下文,却无法对每一段信息都实现同等质量的利用。当上下文长度达到一定阈值时,模型会出现明显的 “注意力偏差”: +5. 注意力衰减:模型对上下文前端、中端的信息关注度会逐渐减弱,更倾向于依赖刚读取的后端文本,导致早期关键信息被 “忽略”; +6. 信息干扰:模型容易被上下文内无关、重复甚至冲突的信息 “带偏”,即便最终回答看似逻辑自洽,实际也可能与核心问题脱节,准确性难以保证。 + 可见,若缺乏检索环节进行信息过滤与相关性排序,上下文越长,反而越难确保回答聚焦于真正关键的证据,长上下文的优势会被信息干扰完全抵消。 +7. **知识更新与可控性的问题** : + 若将所有知识完全依赖模型参数存储,或手动复制到提示词中调用,会存在两个难以规避的天然缺陷: +8. 知识更新困难:一旦知识发生变更(如政策调整、产品迭代、价格更新等),要么需要重新训练或微调模型 —— 投入高、周期长;要么需要人工逐次维护提示词模板 —— 不仅成本高,还容易因人工操作失误导致信息偏差; +9. 可追溯性差:模型回答时究竟依据了哪些具体信息,人们往往难以从 “黑盒化的参数” 或冗长的提示词中定位核心证据,这使得合规审计、风控解释等需要明确 “决策依据” 的工作,面临极大的操作困难。 + +在这些现实约束下,RAG 的优势就更加清晰。它的核心做法是:在模型生成答案之前,先通过检索精准定位相关、可靠的信息,让模型只基于必要的知识生成回答。知识可以独立存储在外部知识库中,便于更新与管理;同时,生成结果可以附带引用来源,提升回答的可解释性与可信度。即便未来模型的上下文窗口继续扩大,RAG 依旧能够以较低成本实现知识的高效管理与利用,从而支撑起一个过程可观测、行为可追踪的企业级知识应用体系。 + +从企业需求出发,相比只依赖模型自身参数的传统 LLM,RAG 主要解决了企业在落地应用中面临的以下现实问题: + +1. 时效性问题: + 传统模型对 2024 年之后的新规、新产品、新流程往往不了解,而 RAG 可以直接读取最新的制度文件、业务数据库和知识库内容,无需频繁重新训练模型,就能让回答保持与最新业务同步。 +2. 专业性问题: + 通用大模型在医疗、化工、金融等垂直领域,常常存在“懂得不够深、说得不够准”的情况。接入企业自有专业文档和行业标准之后,模型回答可以基于权威资料,显著更贴近真实业务实践。 +3. “幻觉”问题: + 通过要求回答尽量基于检索到的文档片段,并能够给出对应的出处引用,可以在机制上减少无依据编造内容的概率,让“说得像真的”更接近“确实是真的”。 +4. 可解释与可审计问题: + 纯参数模型给出结论时,往往难以回答“这是从哪条规定推导出来的”。RAG 让每条回答都可以回溯到具体的制度条款、业务文档或历史案例,既方便业务人员抽查和纠错,也为审计、风控、合规部门提供了必要的溯源依据。 +5. 算力成本与资源效率问题: + 让大模型在参数中“背下”所有企业知识,往往意味着更大的模型、更高的推理成本。RAG 通过“按需检索”把大部分知识存放在外部向量库和文档库中,使企业可以在较小模型和有限算力条件下,依然获得覆盖面更广、细节更准确的回答能力 + +因此,对希望在真实业务场景中长期、稳定、可控地使用大模型的企业而言,RAG 不是一个可有可无的增强选项,而是构建高质量企业知识应用体系时几乎不可缺少的基础技术。 + +# 2. 什么是 RAG + +RAG(Retrieval-Augmented Generation,检索增强生成)的核心思路是让大模型在回答问题时,不仅依赖训练阶段学到的静态知识,更能够实时调用外部知识库中的最新、可靠信息。 + +在典型的 RAG 系统中,用户的问题不会被直接丢给大模型,而是先由检索模块从企业知识库中找到最相关的文档片段,再将这些内容与原始提问一起组合成完整的上下文,输入给大模型生成回答。这种"先检索、再生成"的方式,让模型能够基于真实参考资料进行推理,而不是仅凭参数中"记住"的知识进行推测。我们可以参考一个典型案例: + +![](images/image3.png) + +1. **索引阶段** + +在索引阶段,系统会先处理企业内部文档、网页文章、报告等原始资料,将它们拆分成较小的语义片段(chunks),再用向量模型为每个片段生成向量表示并建立索引。这样,后续接收到用户问题时,就可以在向量空间中快速找到“语义最相近”的几段内容。 + +在图中,这一阶段对应右上角紫色区域 “Indexing”。从 “Documents” 出发,经由 “Chunks / Vectors” 到 “embeddings” 的那一部分,就是在说明文档被切块并转换为向量、写入索引的过程。具体过程如下: + +* 文档被划分为若干个语义相对完整的 chunks,每个 chunk 可能对应一小段新闻、一段说明或一段分析。 +* 每个 chunk 会通过 embedding 模型转换成高维向量,并存入向量索引中。 +* 这个索引支持后续基于相似度的检索,为回答问题提前准备好“可被查阅的知识库”。 + +2. **检索阶段 + 基于检索结果生成答案** + +当用户提出问题后,系统会先从索引中检索相关内容,再把问题和检索到的文本一并交给大模型生成答案。图中从上到下、从右到左的几个关键区域,正好对应这一整条流程。 + +(1)用户输入问题——图中黄色区域 Input – Query + +> “How do you evaluate the fact that OpenAI's CEO, Sam Altman, went through a sudden dismissal by the board in just three days, and then was rehired by the company, resembling a real-life version of 'Game of Thrones' in terms of power dynamics?” +> +> “你如何评价这样一件事:OpenAI 的 CEO Sam Altman 被董事会突然解职,仅仅三天后又被公司重新聘回,在权力博弈上几乎像现实版《权力的游戏》?” + +这一大段文字就是图中 “Query” 方框里的内容,对应“用户发起的自然语言提问”。系统会将这段话向量化,并据此去右上角的索引里查找相关文档片段。 + +(2)检索到的相关文档——图中右下角粉色区域 Relevant Documents + +检索完成后,系统会得到若干个与问题最相关的文档块,它们在图中以三个 Chunk 的形式展示: + +> “Sam Altman Returns to OpenAI as CEO, Silicon Valley Drama Resembles the 'Zhen Huan' Comedy” +> “Sam Altman 回归担任 OpenAI CEO,这场硅谷大戏宛如一出《甄嬛传》式的宫斗喜剧。” +> +> “The Drama Concludes? Sam Altman to Return as CEO of OpenAI, Board to Undergo Restructuring” +> “大戏要落幕了吗?Sam Altman 将重返 OpenAI CEO 职位,董事会则将进行重组。” +> +> “The Personnel Turmoil at OpenAI Comes to an End: Who Won and Who Lost?” +> “OpenAI 的人事动荡告一段落:谁赢了,谁输了?” + +(3)组合 Prompt 并生成回答——图中蓝色区域 LLM / Combine Context and Prompts + +系统随后会将“用户原始问题 + 检索到的 chunks”组合成一个完整的 Prompt,发给大模型。图中中下部、靠中央的虚线框,就是这个 Prompt 的示例内容: + +> “Question: +> How do you evaluate the fact that the OpenAI's CEO, … … dynamics? +> +> Please answer the above questions based on the following information : +> Chunk 1 : +> Chunk 2 : +> Chunk 3 :” +> +> “问题: +> 你如何评价 OpenAI 的 CEO …… 这件事中的权力博弈? +> +> 请基于下面提供的信息来回答上述问题: +> Chunk 1: +> Chunk 2: +> Chunk 3:” + +(4)有无 RAG 的回答对比——图中左下角灰色 / 黄色区域 Output – Answer + +最后,大模型基于这些信息生成答案,结果展示在图的左下角 “Answer” 区域。这里同时给出了“未使用 RAG”和“使用 RAG”两种情况下的输出示例。不用 RAG 时,模型缺乏外部资料,只能给出非常模糊的回应,对应灰色框里那段话: + +> “… I am unable to provide comments on future events. Currently, I do not have any information regarding the dismissal and rehiring of OpenAI's CEO …” +> +> “……我无法就未来事件发表评论。目前我没有关于 OpenAI CEO 被解雇和重新聘用的任何信息……” + +使用 RAG 时,模型可以利用刚才检索到的新闻和分析,给出更有信息量的回答,对应黄色框中那段文本: + +> “… This suggests significant internal disagreements within OpenAI regarding the company's future direction and strategic decisions. All of these twists and turns reflect power struggles and corporate governance issues within OpenAI …” +> +> “……这表明 OpenAI 内部在公司未来方向和战略决策上存在重大分歧。这一连串的反复与曲折,反映出 OpenAI 内部的权力斗争以及公司治理方面的问题……” + +以上展示了一个典型 RAG 系统的完整流程,让我们从整体上理解了系统包含哪些核心环节、信息如何在各阶段流转。但是,检索时如何进行向量匹配?提示词如何组织才能让模型更好地利用检索到的内容?这些决定 RAG 实际效果的技术细节,目前仍是"黑盒"。接下来,我们将深入 RAG 的内部机制,从向量化原理、相似度计算、到提示词工程等关键环节,逐步拆解 RAG 究竟是如何工作的。 + +# 3. RAG 如何工作 + +我们可以通过一个“苹果”的知识库问答案例,逐步拆解它的关键环节。 + +## 3.1 文档向量化阶段 + +假设我们有一个简化的知识库,包含以下三个文档片段: + +1. 文档片段A:苹果公司于1976年4月1日由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克和罗纳德·韦恩创立,总部位于加利福尼亚州库比蒂诺。 +2. 文档片段B:苹果是一种水果,富含维生素C和膳食纤维,有助于消化和免疫系统健康。 +3. 文档片段C:苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。 + +当我们使用嵌入模型(如 OpenAI 的 text-embedding-ada-002 或开源的 BGE 模型)处理文档时,每个文档片段会被转换为高维向量(通常含 768、1024 或 1536 个维度)。 + +> “向量”本质是由多个数值组成的数组,每个维度的数值都对应文本某一维度的语义特征,比如 “猫” 的向量中,可能有维度对应 “哺乳动物”“家养宠物”“有毛” 等属性,最终通过整体数值组合精准捕捉文本的语义含义,让计算机能 “读懂” 文本间的关联。 + +简化示例(实际向量维度高得多,这里仅示意): + +* 文档A向量(关于苹果公司创立):`[0.85, -0.23, 0.41, -0.56, 0.12, 0.78, ...]` +* 文档B向量(关于水果苹果):`[-0.12, 0.95, -0.34, 0.67, -0.89, 0.05, ...]` +* 文档C向量(关于iPhone发布):`[0.79, -0.18, 0.52, -0.61, 0.23, 0.81, ...]` + +相关向量需存入向量数据库(如 Pinecone、Weaviate、FAISS)用于之后的检索召回工作。 + +> 数据库是按特定结构存储、管理数据的系统,核心功能是实现数据的有序存储与高效存取,常见于通讯录、电商商品库等场景。 +> +> 而向量数据库是数据库的细分类型,区别于传统数据库存储文本、表格等数据的逻辑,它专门用于存储 “向量”(高维数值数组),并优化了向量相似性检索能力,以适配 AI 场景下的高维数据管理需求。 + +## 3.2 用户查询检索、回复阶段 + +在知识库完成向量化存储后,RAG系统便能够支持用户的实时查询操作。当用户提出问题时,系统会执行一套连贯的流程:先将问题转化为向量,再通过相似度计算从知识库中召回最相关的信息片段,最终将这些片段作为生成答案的依据。我们通过三个具体查询来完整呈现这一过程。 + +### 查询一:“苹果公司是什么时候创立的?” + +在查询向量化阶段,此问题被嵌入模型转换为一个语义向量,例如 `[0.82, -0.21, 0.38, -0.58, 0.15, 0.76, ...]`。该向量在数值模式上,与之前存储的“文档A向量”(关于公司创立)`[0.85, -0.23, 0.41, -0.56, 0.12, 0.78, ...]`高度相似。 + +接下来系统将进行相似度检索 (Top-K, K=2)操作,计算该查询向量与知识库中所有文档向量的余弦相似度(一种衡量向量方向接近程度的指标)。结果如下: + +* 与文档A(公司创立)相似度:0.97(高度相关) +* 与文档C(iPhone发布)相似度:0.88(相关,同属公司主题) +* 与文档B(水果营养)相似度:0.12(几乎不相关) + +> Top-K 是向量检索场景中常用的筛选策略,核心含义是 “从所有匹配结果中,按相似度从高到低排序后,选取排名前 K 个的结果”;而 K=2 则是对该策略的具体数值定义,即明确要求系统仅保留相似度排名前 2 的文档向量,过滤掉其余相似度更低的结果,以确保后续仅基于最相关的 2 个文档片段生成答案。 + +此时根据相似度值过滤后的返回结果我们叫做召回结果。系统根据相似度分数从高到低,返回Top-2的文档片段作为证据: + +1. 文档A (相似度0.97):“苹果公司于1976年4月1日由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克和罗纳德·韦恩创立,总部位于加利福尼亚州库比蒂诺。” +2. 文档C (相似度0.88):“苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。” + +在根据检索结果对话的大模型回复阶段,系统会构建如下所示的完整对话输入,将召回的结果放入参考信息中,和系统提示一起发送给LLM: + +```Plain +【系统指令 (System Prompt)】 +你是一个专业的问答助手。请严格根据用户提供的“参考信息”来回答问题。 +如果参考信息中包含问题答案,请直接基于该信息进行回答。 +如果参考信息中不包含问题答案,请明确告知用户“根据现有资料无法回答该问题”,切勿自行编造信息。 +请在回答中注明依据的信息点。 +【参考信息 (Retrieved Context)】 +苹果公司于1976年4月1日由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克和罗纳德·韦恩创立,总部位于加利福尼亚州库比蒂诺。 +苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。 +【用户问题 (User Query)】 +苹果公司是什么时候创立的? +``` + +LLM接收到上述结构化输入后,会遵循系统指令,将“参考信息”视为回答问题的唯一可信来源。其最终回复将类似:根据提供的参考信息,苹果公司于 1976年4月1日 创立。【依据:信息1】” + +### 查询二:“吃苹果有什么好处?” + +在查询向量化阶段,此问题被嵌入模型转换为一个语义向量,例如 `[-0.08, 0.92, -0.31, 0.71, -0.85, 0.08, ...]`。该向量在数值模式上,与之前存储的“文档B向量”(关于水果营养)`[-0.12, 0.95, -0.34, 0.67, -0.89, 0.05, ...]`高度相似。 + +接下来系统将进行相似度检索 (Top-K, K=2) 操作,计算该查询向量与知识库中所有文档向量的余弦相似度。结果如下: + +* 与文档B(水果营养)相似度:0.95(高度相关) +* 与文档C(iPhone发布)相似度:0.18(几乎不相关) +* 与文档A(公司创立)相似度:0.15(几乎不相关) + +系统根据相似度分数从高到低,返回 Top‑2 的文档片段作为证据: + +1. 文档B (相似度0.95):“苹果是一种水果,富含维生素C和膳食纤维,有助于消化和免疫系统健康。” +2. 文档C (相似度0.18):“苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。”(相关性极弱,实际可能被阈值过滤) + +在根据检索结果对话的大模型回复阶段,系统会构建如下所示的完整对话输入,将召回的结果放入参考信息中,和系统提示一起发送给LLM: + +```Plain +【系统指令 (System Prompt)】 + 你是一个专业的问答助手。请严格根据用户提供的“参考信息”来回答问题。 + 如果参考信息中包含问题答案,请直接基于该信息进行回答。 + 如果参考信息中不包含问题答案,请明确告知用户“根据现有资料无法回答该问题”,切勿自行编造信息。 + 请在回答中注明依据的信息点。 +【参考信息 (Retrieved Context)】 + 苹果是一种水果,富含维生素C和膳食纤维,有助于消化和免疫系统健康。 + 苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。 +【用户问题 (User Query)】 + 吃苹果有什么好处? +``` + +LLM接收到上述结构化输入后,其最终回复将类似: + +> 根据提供的参考信息,苹果富含维生素C和膳食纤维,食用苹果有助于消化以及免疫系统健康。【依据:信息1】 + +### 查询三:“今天天气怎么样” + +在查询向量化阶段,此问题被嵌入模型转换为一个与气象、天气相关的语义向量,例如 `[0.10, -0.05, 0.30, -0.12, 0.21, 0.08, ...]`。该向量在数值模式上,与知识库中所有关于“苹果(公司/水果)”的文档向量在语义空间中距离较远,因此不会出现显著相似性。 + +接下来系统将进行相似度检索 (Top-K, K=2) 操作,计算余弦相似度。由于问题主题与知识库内容无关,整体相似度得分都很低。结果如下: + +* 与文档B(水果营养)相似度:0.18(极低) +* 与文档C(iPhone发布)相似度:0.10(几乎不相关) +* 与文档A(公司创立)相似度:0.08(几乎不相关) + +Top-K 仍会返回相似度排名前 K 个结果,但在该场景下,这些结果并不能提供有效证据。实际系统常会结合“最低相似度阈值”直接返回空召回,即没有任何召回的结果,参考信息为 0,以减少无关信息干扰。 + +1. 文档B (相似度0.18):“苹果是一种水果,富含维生素C和膳食纤维,有助于消化和免疫系统健康。” +2. 文档C (相似度0.10):“苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。” + +在根据检索结果对话的大模型回复阶段,系统会构建如下完整输入: + +```Plain +【系统指令 (System Prompt)】 + 你是一个专业的问答助手。请严格根据用户提供的“参考信息”来回答问题。 + 如果参考信息中包含问题答案,请直接基于该信息进行回答。 + 如果参考信息中不包含问题答案,请明确告知用户“根据现有资料无法回答该问题”,切勿自行编造信息。 + 请在回答中注明依据的信息点。 +【参考信息 (Retrieved Context)】 + 苹果是一种水果,富含维生素C和膳食纤维,有助于消化和免疫系统健康。 + 苹果公司在2007年推出了第一款iPhone,彻底改变了智能手机行业。 +【用户问题 (User Query)】 + 今天天气怎么样? +``` + +LLM接收到上述结构化输入后,会先判断参考信息是否包含“天气/气象/实时数据”等直接信息;在确认参考信息与问题无关后,会遵循系统指令执行“无法回答”。其最终回复将类似: + +> 根据现有资料无法回答“今天天气怎么样”,因为参考信息仅包含与苹果(水果营养、苹果公司产品)相关内容,不包含天气信息或实时气象数据。【依据:参考信息中无天气相关信息】 + +通过以上三个实例可以看出,在检索增强生成的大模型对话阶段中,系统指令设定了LLM的角色和回答规则,检索证据提供了具体、可信的作答素材,用户问题则明确了任务目标。这种结构化的输入方式,正是RAG技术能够有效引导并约束原本可能产生“幻觉”的大模型,使其输出稳定、可靠答案的关键所在。它确保了大模型的能力被精准地应用于理解与组织已有信息,而非无根据地创造信息。 + +# 4. RAG 技术演进史 + +RAG 技术并非诞生于大模型时代,在更早期的研究中就已有雏形。从发展脉络来看,RAG 的出现源于对传统 LLM 局限性的认识。早期的大语言模型主要依赖预训练数据,这些数据往往在模型训练完成后就固定下来,无法获取后续更新的信息。例如,GPT-3 等模型的知识截止点通常在训练数据收集的日期之后,无法获取新知识。此外,重新训练或微调 LLM 以适应特定领域需要大量资源和专业知识,成本高昂且难以快速迭代。 + +RAG 技术的起源可以追溯到 2017 年的 DrQA 框架,该框架首次尝试将检索机制与语言模型相结合。随后,2020 年引入的 Dense Passage Retrieval (DPR) 标志着 RAG 技术的重大突破,它利用预训练的神经网络模型进行语义检索,而非传统的 TF-IDF 或 BM25 等基于词频的方法。2021 年,RAG 被正式提出并系统化,成为解决 LLM 知识截止和幻觉问题的标准方法。 + +整体来看,RAG 的演进大致可以分为三个阶段: + +![](images/image4.png) + +## 4.1 第一代 RAG:Naive RAG(基础检索增强) + +Naive RAG可以理解为基础的 RAG 形态,它在工程上非常直接,典型流程可以概括为“三步走”:第一步是文档预处理与索引,将原始文档经过清洗之后,按固定长度切分成若干文本块(chunks),再用嵌入模型将每个文本块编码为向量,写入向量数据库;第二步是基于相似度的检索,将用户的自然语言问题编码成向量,在向量库中执行 Top‑K 相似度搜索,取回相似度最高的若干文本块;第三步则是简单拼接后的增强生成,把这些检索出来的文本块和原始问题直接拼在一起,构成一个长提示词交给 LLM,由模型在这个上下文基础上生成回答。 + +这一阶段的价值在于,以极低门槛验证了“先查再答”的思路确实有效:相比完全依赖模型内部记忆,已经能明显缓解知识截止和部分幻觉问题,在早期的原型系统和示例工程中发挥了重要作用。这使得 RAG 从一开始就具备了很强的实用性,成为大量 Demo、原型系统和入门教程的首选方案。 + +然而,这一代 RAG 的局限同样非常明显。首先,文本分块策略通常比较粗糙,大多采用固定长度切分,容易把一个完整的语义段落从中间“截断”,也可能把多个主题混杂在同一个块里,既影响检索准确性,也会给 LLM 的理解带来额外负担。其次,检索信号非常单一,往往只依赖向量相似度进行排序,没有利用关键词、时间戳、来源可信度、访问权限等更丰富的结构化线索。再次,检索结果几乎不经过筛选和治理,噪音、重复甚至彼此矛盾的片段都会原样塞进上下文,使得本来就紧张的上下文窗口被大量“低价值信息”占据。 + +可以说,第一代 RAG 解决了“要不要检索”的问题,但在“如何更好地检索”和“检回来的东西如何更合理地用起来”这两个问题上,还停留在相当原始的阶段。 + +## 4.2 第二代 RAG:Advanced RAG(检索与上下文的精细化优化) + +随着 RAG 应用从 Demo 走向真实业务场景,系统对稳定性、可控性以及结果质量的要求迅速提高。此时出现的第二代 RAG,通常可以笼统地称作 Advanced RAG,它仍然遵循先检索、再生成的方法,但在检索前和检索后两个环节引入了系统化的精细化优化策略。换句话说,不再满足于“能不能检到东西”,而是要“把该存的东西存好,把该问的问题问清,把检回来的上下文治理好”。 + +在检索前,重点是把“存什么”和“怎么问”处理好: + +* 在索引端,从固定长度切分演进到语义感知分块与分层索引,例如按章节、小节、段落或句子边界进行切分,辅以滑动窗口和多粒度索引结构。 +* 为每个文档块附加丰富的元数据,例如来源、时间、作者、主题、文档类型等,为后续的过滤和排序提供更多维度。 +* 在查询端,对用户原始问题进行重写、扩展和拆分,例如通过 Query Rewrite、多路查询(Multi-Query)、子问题分解(Sub-Query)、Step-back Prompting 等方式,将含糊或口语化的问题转换为更利于检索理解的表达。 + > 1. Query Rewrite(查询重写) + > + > 核心是将用户模糊、口语化或不规范的原始查询,转化为检索系统更易理解的标准化表述,补充关键信息、修正歧义。 + > + > * 用户原始问题为 “咋查明天北京的天气啊”,会去除 “咋”“啊” 等口语化词汇,补充 “实时”“全天” 等关键限定,重写为 “查询北京市明日全天实时天气”; + > * 用户原始问题为 “推荐好看的电影”,若结合用户历史行为发现其常看悬疑片,会补充 “2024 年高分”“悬疑题材” 等信息,重写为 “推荐 2024 年高分悬疑题材电影”。 + > + > 2. Multi-Query(多路查询) + > + > 基于原始问题生成多个 “语义相关但角度不同” 的查询语句,避免单一查询遗漏潜在结果,覆盖用户未明确的潜在需求。 + > + > * 用户原始问题为 “如何给刚满月的宝宝拍嗝”,会生成聚焦 “姿势” 的查询:“新生儿拍嗝的正确姿势”; + > * 生成聚焦 “防吐奶” 的查询:“满月宝宝拍嗝避免吐奶的方法”; + > * 生成聚焦 “月龄适配” 的查询:“婴儿拍嗝的步骤(0-1 个月)”; + > * 生成聚焦 “新手场景” 的查询:“新手爸妈给满月宝宝拍嗝技巧”。 + > + > 3. Sub-Query(子问题分解) + > + > 针对包含多个诉求的复合问题,拆分为独立、简单的子查询,让检索系统针对单一诉求精准匹配数据,避免信息混杂缺失。 + > + > * 用户原始复合问题为 “北京到上海的高铁,明天有哪些班次?票价多少?需要坐多久?”,会拆解出聚焦 “班次” 的子查询:“北京市至上海市 明日高铁班次表”; + > * 拆解出聚焦 “票价” 的子查询:“北京到上海高铁 二等座 / 一等座票价”; + > * 拆解出聚焦 “时长” 的子查询:“北京到上海高铁 行驶时长(最快 / 平均)”。 + > + > 4. Step-back Prompting(回溯提示) + > + > 先生成 “比原始问题更宏观的上位问题”,再基于上位逻辑回推检索方向,解决原始问题因聚焦细节导致的理解偏差。 + > + > * 用户原始问题为 “为什么 2024 年某国产新能源汽车品牌的销量突然下降?”,第一步生成宏观上位问题:“影响新能源汽车品牌短期销量波动的核心因素有哪些?”(如产品迭代、竞品动作、政策变化、市场需求等); + > * 第二步基于上位问题逻辑,生成具体检索方向:“2024 年某国产新能源品牌 产品更新情况”“2024 年新能源汽车市场 竞品定价策略”“2024 年新能源汽车补贴政策调整”。 + > + +在检索后,重点是把“取回来的内容”治理好: + +* 使用专门的 Rerank 模型或 LLM 对候选文档进行重新排序,确保最关键、最贴近问题的内容优先进入上下文。 + > Rerank 模型是信息检索流程中的关键组件,主要用于对 “召回阶段” 初步筛选出的候选结果进行二次排序 —— 它会借助更复杂的语义理解能力(常基于 Transformer 等深度学习架构),分析用户需求与候选结果的深层关联,修正初步排序中可能存在的语义偏差,最终让更贴合用户需求的结果排在更靠前的位置,提升用户获取有效信息的效率。 + > +* 对检索结果进行筛选、去重与压缩,去掉明显无关或高度重复的片段,缓解长上下文中部信息被忽略的问题。 +* 在必要时,结合轻量的模型微调,使 LLM 更倾向于依据检索证据作答,并在回答中附带引用或出处信息。 + +总体来看,第二代检索增强生成技术其关注点不再局限于 “是否需要检索”“能否检索到信息” 这两个基础问题,而是进一步聚焦于三个更大的挑战:“能否精准定位到真正关键的段落内容”“传递给大模型的上下文是否简洁有序、具备清晰结构且易于高效利用”“当面临信息噪音、内容冲突或多资料源查找需求时,系统整体性能是否依然稳健可靠”。 + +从大量实验验证与工程落地实践来看,Advanced RAG 在问答准确率、幻觉抑制能力、系统鲁棒性及结果可解释性等关键指标上,均显著优于 Naive RAG。也正因此,Advanced RAG 已逐步取代传统方案,成为当前工业界构建 RAG 系统的主流技术范式。 + +## 4.3 第三代 RAG:Modular RAG + +在企业级的复杂应用场景中,需求往往跨越多个领域。在这种情况下,RAG系统若仅采用检索、重排、生成这样的单一线性处理方式,常常难以应对: + +1. 同一系统既要支持简单 FAQ,又要生成长篇报告、进行代码检索或调用数据库。 +2. 需要同时接入向量库、全文检索、关系数据库、知识图谱以及外部搜索引擎等多种数据源。 +3. 需要在多轮交互中保持对用户偏好、历史决策的记忆,并对输出结果实施合规审核和溯源。 + +在这样的背景下,RAG 的系统形态开始向模块化演进。Modular RAG 不再被看作一条固定的流水线,而是由一组可插拔、可替换、可组合的功能模块组成,通过编排逻辑按需组合执行。典型模块包括: + +1. 查询理解与路由模块 + 用于意图识别、问题重写、子任务拆解和路径选择,决定一条请求是主要依赖内部知识,还是外部检索,抑或需要调用特定工具或数据库。 + 例如,用户问「这条错误日志代表什么问题?」系统会将其路由到代码与日志知识库;而问「最近该行业的监管新规有哪些变化?」则更适合走互联网搜索或合规法规库。 +2. 多源检索与融合模块 + 同时连接向量数据库、全文检索系统、结构化数据库与知识图谱,对不同数据源进行查询,并将结果进行统一的融合与排序。 + 例如,在做「客户年度分析报告」时,一部分信息来自 CRM 数据库(如客户成交额),一部分来自文档库(如项目复盘),还可能需要从知识图谱中补充行业关系,最终由该模块将多源结果合并成一份有序的证据集。 +3. 记忆与个性化模块 + 维护长期用户画像、短期会话记忆和领域知识缓存,使系统能够在长期交互中不断积累和利用历史信息。 + 比如,系统记住某位用户偏好「先给结论再给细节」,以及他所在的业务线与常用术语,下次回答时会自动采用对应风格,并优先使用与该业务线相关的案例和数据。 +4. 任务适配与治理模块 + 面向不同任务加载对应的适配器,对输出格式、语气、风格进行约束,并结合事实核查、风险过滤和引用对齐等机制,对生成结果进行治理。 + 例如,同样是基于同一批检索结果,为产品经理生成的是结构化 PRD 模板,为法务人员则是正式合规审查意见;在此过程中,该模块会对关键事实进行二次核查,并强制要求给出引用来源。 + +总的来说,传统 RAG 往往是一轮检索配合一轮生成就结束,而 Modular RAG 则打破了这种单一流程。当系统在生成过程中发现信息不足时,可以主动触发新的检索轮次,甚至多次往返检索与生成,以完成更复杂的任务。 + +进一步地,模型还可以学习自我决策:对于把握较大的问题直接基于内部知识或短上下文作答;遇到不确定的情况时才发起检索或调用外部工具,从而在保证质量的前提下提高效率、节约资源。对于那些表达不清、信息缺失严重的查询,系统甚至可以先由大模型生成一个假设性的答案或中间文档,再以此作为“线索”去检索真实文档,不断逼近可靠信息源。 + +在这一阶段,RAG 已经不再只是给大模型补几段参考资料的简单组件,而逐步演变为企业级智能应用的中枢式知识编排层,负责在多数据源、多工具、多任务之间进行协调与调度。 + +# 5. 从 Demo 到企业级的 RAG 系统 + +从企业工程实践的角度来看,RAG 系统的构建不能仅局限于检索增强生成技术本身,前面提到的内容更多是一个 Demo 级别的介绍。由于实际业务场景中的数据往往存在质量参差不齐、格式混乱等问题,因此需要在数据预处理、清洗及导入环节投入更多精力,同时在各个关键节点做好模型选型。 + +一个完整的企业级 RAG 系统通常可以划分为三个核心模块:版面分析与知识采集、知识库构建、以及基于 RAG 的知识问答服务。在整个技术链路中,涉及多个关键模型的选型决策,包括 Embedding 模型、Rerank 模型和 LLM 模型。只有在每个环节都做出合理的技术选型,才能确保系统达成最佳效果。 + +1. 版面分析与本地知识文件读取 + +这一模块负责将各种格式的本地知识资产转换为可用于检索的文本。输入可能包括 PDF、TXT、HTML、Word、Excel、PPT 等文档,也包括 PNG、JPG 等图片类扫描文件,甚至是语音录音。 + +系统需要针对不同格式做解析,对文本文档做版面分析与结构抽取,区分标题、正文、表格、页眉页脚等元素,恢复合理的阅读顺序。对图片类文件进行 OCR 识别,对语音进行语音转写(ASR),最终统一转换为较为干净的知识文本,并尽量保留一些基础元信息,如文档名、章节、页码、时间等,为后续切分和索引打下基础。 + +2. 知识库构建(切分、Embedding、索引) + +在拿到清洗后的知识文本后,需要先进行合理的文本切分(Chunking),把长文档拆成若干语义相对完整、长度适中的文本块,通常按段落、标题结构或滑动窗口切分,同时保留每个块对应的文档来源和元数据。 + +随后,使用选定的 Embedding 模型,如 text-embedding-3-small、Sentence Transformers、BGE 等,对每个文本块计算向量表示,并基于这些向量构建向量索引,如使用 Faiss、Milvus、向量搜索服务等,就得到一个可按语义相似度进行快速查询的知识库。至此,我们完成了知识转换为可检索向量的核心步骤。 + +3. 基于 RAG 的知识问答(召回、排序、拼接、生成) + +在在线问答阶段,用户首先发出查询请求,系统会对查询进行 Embedding,得到查询向量,并在向量索引中检索出一批最相似的文本块(Top N),这是粗排阶段。在此基础上,可以选用 Rerank 模型,如 BGE Reranker 或 LLM 充当 Reranker,对查询与文档对进行精排打分,从中选出 Top K 个真正最相关的文档作为知识上下文。 + +接着,结合精心设计的系统提示词如"请严格基于以下资料回答"等等,将用户查询和检索出的文档片段进行拼接,把这个合并后的提示发给 LLM,由它在检索得到的证据基础上生成最终答案,并在需要时附上引用或出处。 + +## 5.1 模型选型 + +接下来我们关注各环节的模型选型,一个完整的 RAG 系统通常涉及三类核心模型:即 Embedding 模型、Rerank 模型和大语言模型。这三类模型各司其职,共同构成了从知识检索到答案生成的完整流程。其中,Embedding 模型负责将文本转化为可检索的语义向量,Rerank 模型对初步检索结果进行精细筛选与重排序,大语言模型则基于筛选后的知识上下文生成最终答案。 + +### 5.1.1 Embedding model + +在 RAG 系统中,Embedding 模型的作用是将文本,如用户查询和知识库内容,转换为高维向量。语义相近的文本,其向量在空间中的位置也更接近,这使得系统能够通过向量相似度快速定位相关知识。因此,选择合适的 Embedding 模型是构建高性能 RAG 系统的关键一步,直接决定了召回阶段的质量。 + +为了选出好的模型,我们在这里介绍一个系统化的评价基准:MTEB(大规模文本嵌入评测基准) + +MTEB为各类Embedding模型提供了一个统一、客观的评估框架。它通过8大类任务、56个数据集,全面评测模型在检索、聚类、分类、重排序、文本匹配、语义相似度等多种场景下的表现。模型在MTEB上的整体得分,能够反映其向量表示能力的通用性和稳健性,可作为选型的重要数据参考。最新排名和详细结果可通过 [HuggingFace MTEB Leaderboard](https://huggingface.co/spaces/mteb/leaderboard) 查看。 + +![](images/image5.png) + +尽管榜单上存在大量模型,你可以根据实际需求进行选择,并不需要掌握所有的模型(一般来说,选择大模型厂商自带的 Embedding 模型,或者使用云服务平台部署对外使用的模型就大概率不会错,因为这个是大多人检验后的标准),你还可以在侧边栏中选择具体的类别或者语言进行筛选: + +![](images/image6.png) + +此外,在筛选 Embedding 模型时,需重点关注直接影响 RAG 性能的两个核心参数:维度和上下文长度。其中,维度是指模型输出向量的维度数(如 128 维、768 维),它本质上反映了描述语义信息时所使用的“特征数量”。维度越高,向量能刻画的语义细节越丰富、区分度越强,例如一个 768 维的向量可以从品种、口感、产地等数百个角度精细表征“苹果”,从而更适用于医疗、法律等需要精准检索的专业场景;维度越低,则计算与存储成本越小、检索速度越快,适合千万级文档等高并发、强实时性要求的通用场景。 + +另一个参数Context Length(上下文长度) ,指 Embedding 模型单次可处理的最大文本长度(以 token 为单位,1 英文 token 约 0.75 个单词、1 中文 token 约 1 个汉字),超出部分会被截断:它直接决定模型能否完整理解文本,若长度不足导致信息丢失,会大幅降低检索准确性。因此处理用户短句、问答对这类短文本时,选 512-1024token 的模型即可;处理论文、报告等长文本时,需选 2048token 及以上的模型以避免关键信息丢失。 + +以下是几种常见Embedding模型的横向对比,你需要在实际调用过程中综合成本、性能进行选择最佳模型,没有最好的模型,只有对比多个模型的效果选出最合适的模型。 + +| 模型名称 | 模型规模 | 核心优势 | 适用场景 | +| ----------------------------- | -------------------------------- | ---------------------------------------------- | ----------------------------------------------------------- | +| OpenAI text-embedding-3-large | 闭源API | MTEB测试集上长期领先,成熟稳定 | 追求极致性能且预算充足的云端API场景,适合对延迟不敏感的应用 | +| jina-embeddings-v2 | 支持长文本(最高8K上下文) | 异步编码设计,处理长文档检索时强有力 | 需要长上下文理解的文档分析、法律合规、学术文献检索 | +| multilingual-e5-large | Large规模 | 经典的多语言备选方案 | 跨语言RAG、国际化产品、多语种客服系统 | +| Qwen/Qwen2-Embedding-8B | 8B参数,支持最高4096维自定义 | 曾MTEB多语言榜第一,长文本、多语言与代码能力强 | 高精度中英文RAG、长文档分析、代码检索 | +| Qwen/Qwen2-Embedding-4B | 4B参数 | 性能与效率平衡 | 大规模生产级RAG系统,性价比高 | +| Qwen/Qwen2-Embedding-0.6B | 0.6B | 适用于边缘端 | 资源不够的场景,需要速度优先的场景 | +| BAAI/bge-m3 | 支持混合检索(密集+稀疏+多向量) | 在MIRACL等跨语言基准领先 | 需要混合检索策略的复杂多语言场景 | +| BAAI/bge-large-zh-v1.5 | Large规模 | 中文RAG的稳定基线,社区验证充分 | 纯中文且文档较短、追求稳定性的项目 | +| 智谱AI Embedding-3 | 闭源云端API | 支持自定义维度(256-2048维) | 注重中文且偏好云端API服务的应用 | + +### 5.1.2 Rerank model + +在 RAG 系统中,Rerank 模型的作用是对初步检索结果进行精细化重排序。它接收用户查询和候选文档作为输入,为每对(查询-文档)计算精确的相关性分数,分数越高表示文档与查询的匹配度越好。因此,在 Embedding 召回的基础上引入 Rerank 模型,是提升 RAG 系统检索精度的关键一步。 + +在选择 Embedding 模型时,我们可以用 MTEB 这样的 benchmark。而对于 Rerank 模型我们可以参考 Agentset 的 [Reranker Leaderboard](https://agentset.ai/rerankers),该网站针对 RAG 场景下的重排能力做了系统测试。 + +| Model Name↑ | ELO | nDCG@10 | Latency (ms) | Price / 1M | License | +| --------------------------------------------------------------------------------------------------- | ---- | ------- | ------------ | ---------- | ------------ | +| [BAAI/BGE Reranker v2 M3](https://agentset.ai/rerankers/baaibge-reranker-v2-m3) | 1314 | 0.201 | 2383 | $0.02 | Apache 2.0 | +| [Cohere Rerank 3.5](https://agentset.ai/rerankers/cohere-rerank-35) | 1452 | 0.2 | 392 | $0.05 | Proprietary | +| [Cohere Rerank 4 Fast](https://agentset.ai/rerankers/cohere-rerank-4-fast) | 1506 | 0.216 | 447 | $0.05 | Proprietary | +| [Cohere Rerank 4 Pro](https://agentset.ai/rerankers/cohere-rerank-4-pro) | 1627 | 0.219 | 614 | $0.05 | Proprietary | +| [Contextual AI Rerank v2 Instruct](https://agentset.ai/rerankers/contextual-ai-rerank-v2-instruct) | 1461 | 0.23 | 3333 | $0.05 | cc-by-nc-4.0 | +| [Jina Reranker v2 Base Multilingual](https://agentset.ai/rerankers/jina-reranker-v2-base-multilingual) | 1306 | 0.193 | 746 | $0.05 | cc-by-nc-4.0 | +| [Voyage AI Rerank 2.5](https://agentset.ai/rerankers/voyage-ai-rerank-25) | 1547 | 0.235 | 613 | $0.05 | Proprietary | +| [Voyage AI Rerank 2.5 Lite](https://agentset.ai/rerankers/voyage-ai-rerank-25-lite) | 1528 | 0.226 | 616 | $0.02 | Proprietary | +| [Zerank 1](https://agentset.ai/rerankers/zerank-1) | 1574 | 0.192 | 266 | $0.03 | cc-by-nc-4.0 | +| [Zerank 1 Small](https://agentset.ai/rerankers/zerank-1-small) | 1541 | 0.202 | 248 | $0.03 | Apache 2.0 | +| [Zerank 2](https://agentset.ai/rerankers/zerank-2) | 1644 | 0.195 | 265 | $0.03 | cc-by-nc-4.0 | + +在评估 Rerank 模型性能时,Agentset 基准测试采用以下流程:首先依据向量数据库 FAISS 从大规模文档库中检索出与查询最相关的前 50 个候选结果,随后由待评估的 Rerank 模型对这 50 个文档进行重新排序。评估过程同时关注排序质量和推理延迟两个关键维度。实际应用场景中,仅追求高精度而忽视响应速度将损害用户体验,而仅追求速度却牺牲排序质量则会导致结果实用性下降。 + +为进一步对比模型能力,Agentset 基准测试额外引入 ELO 评分机制:针对每次查询,会以 GPT-5 作为客观 “裁判”,对两个不同 Rerank 模型输出的排序结果展开成对比较,核心判定标准是哪个模型能将真正相关的文档排列得更合理、更靠前。经过海量查询的持续对比,获胜频率更高的模型将获得更高的 ELO 分数,从而直观体现模型综合性能差异。 + +在此基础上,基准测试还设计了两组互补性指标,用于对模型进行多维度综合评估: + +* nDCG@5/10:聚焦排序精准度,重点衡量相关文档是否被合理置于结果前列,直接反映 “排得准” 的能力; +* Recall@5/10:侧重结果覆盖面,核心评估系统能否识别出所有与查询相关的文档,对应 “找得全” 的能力。 + +这两组指标相辅相成,共同构建起对 Rerank 模型的完整评估体系,确保评估结果更具全面性与参考价值。 + +但在实际使用中,我们不一定需要仅是参考 LeaderBoard 进行模型选型,除去刷榜的因素,主要还是因为工业上的好用和分数高不一定是一回事,我们可以根据各个云服务厂商推荐的 Rerank 模型进行选择(比如大模型厂商的默认 Rerank API,或可以尝试 Qwen 对应的 Rerank 模型,在当前 2025 年的情况下作为多参数支持的 Rerank 模型效果尚可。) + +### 5.1.3 LLM + +经过Embedding模型的语义检索和Rerank模型的精准筛选后,相关文档片段会与用户的原始问题一同被整合进prompt,最终由LLM完成阅读理解、信息整合与自然语言生成,向用户输出连贯、准确且符合上下文的答案。 + +在实现层面,RAG 中的 LLM 使用方式主要分为两类: + +1. 私有化部署的大模型。适用于注重数据隐私、成本可控或需要深度定制的场景。当前主流开源LLM如Qwen系列、Llama系列、GLM系列等在RAG任务中表现优异。以Qwen2.5为例,7B或14B参数版本在保持较小资源占用的同时,展现出良好的指令遵循能力和中文理解能力,特别适合企业级RAG应用的本地化部署。KIMI、Minimax、DeepSeek等模型也在不同语言和领域展现出各自优势,可根据具体业务需求灵活选型。 +2. 作为云端API服务的大模型。适合追求快速上线、弹性扩展和持续模型迭代的场景。主流提供商如OpenAI(GPT-4系列)、Anthropic(Claude系列)、Google(Gemini系列)以及国内的阿里(通义千问)、智谱AI(GLM系列)等都提供稳定的API服务。这些模型普遍具备强大的语言理解和生成能力,能够高质量完成RAG场景下的答案合成任务。 + 在选择云端模型时,需要关注几个关键点:回答质量是否准确流畅、价格是否合理、响应速度是否够快、上下文窗口是否足够大(能放下检索到的多个文档)。实际使用时,可以先拿几个候选模型做对比测试,看看哪个回答得更准确完整。如果对成本敏感,可以用"大小模型搭配"的方式:简单问题用便宜的小模型,复杂问题才调用贵的大模型,这样既省钱又保证效果。另外,大模型更新很快,建议定期测试新模型,及时替换表现更好的版本。 + +对于大语言模型在对话和问答场景下的综合能力评估,[LMSYS Chatbot Arena (LMArena) ](https://lmarena.ai/)提供了业界认可的黄金评测基准。该平台采用创新的"盲测对战"机制——人类评估者在不知晓模型身份的情况下,对两个匿名模型针对同一提示的回复进行质量比较,通过大量这样的两两对比为各模型排名。 + +以下是竞技场排名的示例(截止至 2025 年 12 月 15 日) + +| Rank | Model | Score | Votes | Organization | License | +| ---- | ---------------------------------------------------------------------------------------- | ----- | ------ | ------------ | ----------- | +| 1 | [gemini-3-pro](http://aistudio.google.com/app/prompts/new_chat?model=gemini-3-pro-preview) | 1492 | 15,871 | Google | Proprietary | +| 2 | [grok-4.1-thinking](https://x.ai/news/grok-4-1) | 1478 | 16,660 | xAI | Proprietary | +| 3 | [claude-opus-4-5-20251101-thinking-32k](https://www.anthropic.com/news/claude-opus-4-5) | 1470 | 9,879 | Anthropic | Proprietary | +| 4 | [claude-opus-4-5-20251101](https://www.anthropic.com/news/claude-opus-4-5) | 1467 | 10,659 | Anthropic | Proprietary | +| 5 | [grok-4.1](https://x.ai/news/grok-4-1) | 1465 | 16,501 | xAI | Proprietary | +| 6 | [gpt-5.1-high](https://openai.com/index/gpt-5-1/) | 1457 | 13,953 | OpenAI | Proprietary | +| 7 | [gemini-2.5-pro](http://aistudio.google.com/app/prompts/new_chat?model=gemini-2.5-pro) | 1451 | 76,975 | Google | Proprietary | +| 8 | [claude-sonnet-4-5-20250929-thinking-32k](https://www.anthropic.com/news/claude-sonnet-4-5) | 1450 | 28,019 | Anthropic | Proprietary | +| 9 | [claude-opus-4-1-20250805-thinking-16k](https://www.anthropic.com/news/claude-opus-4-1) | 1448 | 43,836 | Anthropic | Proprietary | +| 10 | [claude-sonnet-4-5-20250929](https://www.anthropic.com/news/claude-sonnet-4-5) | 1445 | 23,185 | Anthropic | Proprietary | + +LMArena的独特价值在于其评估方式更贴近真实用户体验,而非单纯依赖自动化指标。排行榜不仅展示整体排名,还细分为不同能力维度(如推理能力、创造能力、多语言支持等),帮助开发者根据实际应用场景选择最适合的模型。截至2025年,该平台已累计超过100万次人类评估,涵盖50+主流开源与闭源模型,成为LLM选型的重要参考。 + +访问LMArena官网可查看实时排行榜、详细能力分析和模型间的直接对比数据。但在实际选型中,建议将LMArena排名作为初步筛选依据,再结合企业特定数据进行A/B测试。特别是在专业领域(如医疗、法律、金融),通用榜单排名与实际表现可能存在较大差异,针对性测试尤为重要。 + +对于 LLM 选型的最佳实践是构建一个小型但代表性的测试集,包含20-30个典型业务问题,对候选模型进行端到端的RAG流程评估,而非仅评估LLM单点性能,比如使用推理模型还是非推理模型,使用什么参数的模型能平衡 RAG 效果和速度;这些都需要在实际的使用过程中测试得到最佳结论。 + +## 5.2 运行框架 + +在实际工程实践中,通常不需要从零开始构建整个 RAG 系统。目前业界已有多个成熟的开源框架可供选择,它们在架构设计、模块集成和开发效率等方面各有特色。企业可以根据自身的技术储备和业务场景,选择合适的框架快速搭建系统。常见的框架类型包括: + + **低代码** **/可视化平台** + +* [Dify](https://dify.ai):提供直观的可视化界面,支持快速搭建 RAG 应用,适合非技术团队或快速原型验证场景。内置多模型接入、工作流编排和 prompt 管理功能。 +* [Coze](https://www.coze.com/):字节跳动推出的 AI Bot 开发平台,提供零代码的可视化搭建能力。特色在于与豆包等字节系大模型深度集成,支持插件市场、定时任务和多渠道发布(飞书、微信等),适合快速构建面向 C 端用户的对话应用或企业内部智能助手。 +* [n8n](https://n8n.io/):一个开源的、基于节点的工作流自动化平台。它通过可视化的方式连接各类应用、API和数据源。在RAG场景中,可以利用n8n编排复杂的业务逻辑,将数据预处理、向量数据库操作、大模型调用以及后续动作(如发送邮件、更新工单)串联成一个自动化流程。 +* [RAGFlow](https://ragflow.io/):专注于深度版面分析和知识抽取能力,对复杂文档(如多栏 PDF、表格密集型文档)的处理效果较好,适合文档结构复杂的企业知识库场景。 +* [FastGPT](https://fastgpt.io/en):中国国内开源方案,集成了知识库管理、对话流程编排和应用发布功能,中文文档完善,适合快速部署中文 RAG 应用。 + +**代码框架/开发库** + +以下介绍的软件通常都有不同平台(前后端)语言的实现方式,你可以根据当前应用的语言选择下列对应软件的语言版本(例如 Python 或 Java 版)。 + +* [LlamaIndex](https://www.llamaindex.ai/):专为 RAG 场景设计的 Python 框架,提供丰富的数据连接器(Connector)、索引结构和查询引擎,模块化程度高,适合需要深度定制检索策略或集成多种数据源的场景。 +* [LangChain](https://www.langchain.com/):通用 LLM 应用开发框架,RAG 只是其中一个应用方向。优势在于生态丰富、组件齐全,支持复杂的 Agent 和工作流编排,但学习曲线相对陡峭,适合构建复杂的多模块 LLM 应用。 + +如果团队技术储备有限、追求快速上线,可优先考虑 Dify 、Coze 或 FastGPT 等低代码平台;如果需要深度定制检索框架、对接特殊数据源或优化性能细节,LlamaIndex 和 LangChain 提供了更大的灵活性。实际项目中,也可以采用"混合方案":用低代码平台快速验证可行性,再用代码框架实现生产级部署和性能优化。此外,这些框架大多支持主流 Embedding、Rerank 和 LLM 模型的快速接入,可以基于前文提到的模型选型标准灵活组合最后使用的模型型号。 + +## 5.3 效果评测 + +企业在 RAG 系统落地过程中,最大的挑战往往不是构建而是调优,对大型企业来说。生产级的大型 RAG 系统需要可监测可量化评估效果。RAG 涉及检索和生成两个非确定性环节,传统的软件测试方法不再适用,建立科学的评测体系(RAG Evaluation)至关重要,我们需要了解如何对 RAG 的效果进行系统化的评估。 + +### 5.3.1 入门示例:基于 LLM 的 RAG 效果评测 + +为帮助大家快速建立对 RAG 效果评测的直观理解,本节将以一个基于 LLM 的 RAG 效果自动化评测流程为例进行说明。该方案核心是 “LLM-as-a-judge”,即利用大模型本身作为裁判,来量化评估 RAG 系统的输出质量 https://huggingface.co/learn/cookbook/rag_evaluation。 + +具体而言,该流程通常包含三个关键步骤: + +* 首先合成评测数据集,我们需要从知识库中采样文档,并指令 LLM 生成与之对应的、高质量的“问题-参考答案”对,再经过相关性、事实 groundedness 等过滤,形成基准测试集; +* 其次,运行 RAG 系统并收集答案,让待评估的系统处理测试集中的每个问题,得到其生成的答案; +* 最后,进行自动化判分,调用另一个作为“裁判”的 LLM,将系统生成的答案与参考答案进行对比,从准确性、完整性等维度给出量化评分。 + +可以用一个简单的例子展示其过程: + +1. 出题(合成评测集):我们有一份知识库,例如一段产品说明书:“本设备支持无线充电,电池容量为5000mAh。” 我们让一个大模型(如GPT-4)扮演“出题官”,根据这段文本自动生成一道测试题,例如:“这个设备的电池容量是多少?” 并记录标准答案:“5000mAh”。这就构成了一条评测数据。 +2. 答题(运行RAG系统):将这道题输入到待评测的 RAG 系统中。系统会从知识库检索相关信息,并生成一个答案。假设它回答:“该设备电池容量是5000mAh。” +3. 批改(LLM-as-a-Judge):我们请另一个大模型(如Claude 3)扮演“批改老师”。将“问题”、“RAG生成的答案”和“标准答案”一起交给它,并指令:“请判断生成的答案是否正确,只需输出‘正确’或‘错误’。” “批改老师”经过对比,输出:“正确”。 + +通过自动化批量测试,我们能够得到RAG系统的准确率等具体指标。这就形成了一个“评测、优化、再评测”的实用循环:先看数据找出问题,再调整检索方法或优化模型,然后重新测试验证效果。系统就在这样一次次的迭代中,实现持续改进和性能提升。 + +你已经知道了评测在 RAG 中意味着什么,接下来我们将聚焦 RAG 评测的核心组成部分:常见的评测指标(如检索阶段的 Recall@K、生成阶段的 Faithfulness)、主流评测框架(如 RAGAS、ARES)与基准数据集(如 WikiEval、MedRAG)。鉴于这些内容覆盖面广、细节庞杂,此处暂先进行概览性介绍,帮助你建立整体认知框架。 + +若你需要深入掌握具体细节(如指标的数学计算逻辑、框架的实操部署步骤、不同基准数据集的适用场景等),建议参考以下两篇 RAG 评测领域的论文: + +* [https://arxiv.org/pdf/2504.14891](https://arxiv.org/pdf/2504.14891)(《Retrieval Augmented Generation Evaluation in the Era of Large Language Models: A Comprehensive Survey》):系统梳理了 LLM 时代 RAG 的内外部评测方法,涵盖组件级与系统级评估,还汇总了海量评测数据集与框架,并分析了当前研究趋势与挑战。 +* [https://arxiv.org/pdf/2405.07437](https://arxiv.org/pdf/2405.07437)(《Evaluation of Retrieval-Augmented Generation: A Survey》):提出了统一的 RAG 评测流程(Auepora),从 “评测目标、数据集、指标” 三维度拆解评测逻辑,同时对比了不同基准的优劣,为实践提供了清晰指引。 + +### 5.3.2 评测指标 + +RAG系统的评估本质上围绕两个核心问题:检索模块能否准确找到相关资料,生成模块能否基于这些资料给出高质量回答。因此,评测体系相应分为检索效果评估、生成质量评估两大模块,并辅以LLM裁判进行综合评分。下面我们逐一展开。 + +#### 检索效果评估:召回准确性与排序质量 + +检索模块是RAG系统的第一道关口,其评估重点在于三个维度:找得准不准、找得全不全、排序好不好。 + +**基础召回质量指标** + +首先是一组衡量召回基本质量的经典指标:Recall@K、Precision@K和F1。 + +* **Recall@K** 衡量在前K条检索结果中,相关文档被找回的比例。比如知识库中有5篇相关文档,前10条结果找回了3篇,则Recall@10为60%。这个指标告诉我们检索的"覆盖面"如何。 +* **Precision** **@K** 衡量前K条结果中真正相关文档的占比。同样是前10条结果,如果其中有3篇相关、7篇不相关,则Precision@10为30%。这个指标反映检索的"准确度"。 +* **F1** 则是Recall和Precision的调和平均,在两者间寻求平衡。 + +这组指标适合快速发现召回阶段的基础问题,比如向量化模型是否有效、检索策略是否合理、Query改写是否到位等。如果Recall很低,说明相关文档根本没被找到;如果Precision很低,说明检索噪声太大。 + +**排序质量指标** + +找到相关文档只是第一步,更重要的是把最相关的文档排在前面。这就需要关注排序质量的指标:MRR、NDCG@K和MAP。 + +* **MRR** **(** **Mean Reciprocal Rank** **)** 计算第一个相关文档出现位置的倒数均值。如果第一个相关文档出现在第3位,该条查询的RR就是1/3。MRR适合那些只需要一个正确答案的场景,比如问答系统。 +* **NDCG@K(Normalized Discounted Cumulative Gain)** 考虑了相关性分级和位置衰减两个因素。它不仅关注文档是否相关,还关注相关程度;同时,越相关的文档排在越前面,得分越高。这使得NDCG成为衡量排序质量最全面的指标之一。 +* **MAP(Mean ** **Average Precision** **)** 综合考虑所有相关文档的位置,对整体排序质量更为敏感。 + +在实际工程中,我们通常采用 Recall@K + MRR@K 的组合,既保证召回覆盖面又约束排序质量。举个例子,如果发现Recall@10达到80%但MRR@10只有0.3,说明相关文档虽然被找到了但都埋在后面,这时就需要优化重排序策略,比如引入交叉编码器或调整多路召回的权重分配。 + +必要时,还可以补充 Coverage 指标来监控知识库覆盖情况,发现系统性的召回盲区。比如某类专业术语相关的问题始终召回效果不佳,就要考虑是否需要针对性地优化该领域的文档切分或向量化方式。 + +#### 生成质量评估:准确性与事实忠实度 + +检索为生成提供了"原材料",接下来要评估的是:基于这些材料,生成模块能否给出高质量的答案?生成质量评估的核心维度有两个:答案是否准确,是否忠于检索到的证据。 + +**精确匹配与文本相似度** + +最直接的评估方式是 **EM(Exact Match)** ,要求生成答案与参考答案完全一致。这个指标适合答案唯一、形式固定的场景,比如"成立日期是什么时候?""总部在哪里?"这类事实性问答。但EM过于严格,同样正确的"2020年1月1日"和"2020-01-01"会被判为不匹配。 + +因此,更常用的是基于n-gram重叠的相似度指标: **ROUGE** **、** **BLEU** **、METEOR** 。它们通过计算生成内容与参考答案的词汇重叠程度来打分。其中,ROUGE-L关注最长公共子序列,对答案的流畅性更敏感;BLEU源自机器翻译领域,注重精确匹配;METEOR则加入了同义词和词干的考量。这些指标的优势是计算简单、易于理解,但也有明显局限——它们只看表面词汇匹配,对语义理解不够深入。 + +为了弥补这一不足,我们可以引入 **BertScore** 或直接的 **向量相似度** 。它们利用预训练模型的向量表示来计算语义相似度,更能容忍表述差异。比如"这款产品很受欢迎"和"该设备广受好评"在词汇上几乎没有重叠,但向量相似度会很高。这对于需要改写、总结、解释的生成任务特别有效。 + +**事实忠实度与幻觉检测** + +对于RAG系统而言,仅仅评估答案与参考标准的相似度还不够,更关键的问题是:生成的答案是否基于检索到的文档,有没有"无中生有"的幻觉? + +这就需要专门的 Hallucination(幻觉率) 和 Faithfulness(忠实度) 指标。具体做法是让另一个LLM扮演"事实核查员",逐句检查生成答案,判断每句话是否能在检索文档中找到依据。比如,检索文档说"产品重量为500克",但生成答案写"该产品轻巧便携,仅重300克",这就是一个典型的幻觉。通过统计有依据的语句占比,我们可以量化系统的忠实度。 + +这个指标对监控事实性错误至关重要,特别是在医疗、法律、金融等对准确性要求极高的领域。实践中,许多企业会设置幻觉率阈值作为上线标准,比如要求准确度必须达到95%以上。 + +#### LLM裁判:多维度综合评分 + +前面介绍的指标各有侧重,但都存在一定局限性:自动化指标往往只看表面特征,难以把握语义深层含义和整体质量。这时,**LLM-as-a-Judge** 机制就显得尤为重要。 + +具体做法是:将问题、检索文档、系统回答、参考答案一并输入一个独立的大模型(通常选择能力较强的模型如GPT-4或Claude),让它按多个维度进行综合评分: + +* **问题相关性** :答案是否真正回应了用户问题,有没有答非所问? +* **信息完整性** :该涵盖的要点是否都说到了,有没有遗漏关键信息? +* **事实忠实性** :答案是否出现了检索文档中不存在的内容,有没有幻觉? +* **整体正确性** :与参考答案相比,生成答案的质量如何? + +LLM裁判的优势在于能进行更接近人类的整体性判断——它可以理解上下文、把握语义、识别逻辑,甚至能发现一些自动化指标捕捉不到的细微问题,比如语气不当、逻辑矛盾、表述含糊等。当然,裁判本身也需要精心设计prompt,并用人工标注样本进行校准,确保评分标准的一致性和可靠性。 + +#### 构建实用的评测组合 + +面对如此多的评测指标,企业在落地时往往会感到困惑:该选哪些指标?如何组合使用? + +一个务实的建议是 **从精简组合开始,逐步完善** : + +* **检索评估** :采用 Recall@K + MRR@K 的核心组合,快速把握召回覆盖和排序质量 +* **生成评估** :根据任务特点,从 EM、ROUGE-L、BertScore 中选择一到两个作为基线 +* **综合评估** :引入 LLM裁判,重点关注相关性、完整性、忠实性三个维度 + +在此基础上,采用"评测→发现问题→调整策略→再评测"的循环迭代。比如,发现召回率不错但MRR很低,就重点优化重排序;发现幻觉率偏高,就加强对检索文档的忠实度约束;发现某些类型的问题回答质量不好,就针对性地补充细分指标。 + +这种渐进式构建方式既能让团队快速起步,建立对系统效果的基本认知,又能随着理解的深入逐步完善评估体系,最终形成一套适合自己业务场景的评测方案。 + +### 5.3.3 评测框架 + +随着RAG技术的快速发展,学术界和工业界涌现出了大量优秀的评测框架,它们不仅封装了常用的评测指标,还提供了标准化的数据集、基准测试和端到端的评估流程。本节将系统梳理当前主流的RAG评测框架,帮助你快速选择适合自己场景的评测工具。 + +#### **评测框架的分类体系** + +根据评测目标和使用场景,我们可以将RAG评测框架分为三大类:研究型框架、基准测试框架和工具型框架。 + +**研究型框架**主要服务于学术研究和前沿探索,特点是评测维度细致、方法创新性强。代表性的如FiD-Light和Diversity Reranker,它们专注于检索阶段的细粒度评估,前者关注召回的延迟性(Latency),后者强调结果的多样性(Diversity)。这类框架通常会深入到RAG系统的某个特定环节,提供精细化的诊断能力。 + +**基准测试****框架**则提供了标准化的测试集和评测流程,用于横向比较不同RAG系统的性能。这类框架数量最多、影响最广。例如: + +* **RAGAS** (2023.09)是最早的综合性RAG评测框架之一,采用LLM-as-a-Judge模式,同时评估检索和生成两个环节 +* **ARES** (2023.11)引入了分类器辅助的评测方法,结合LLM判断和传统分类器来评估Context相关性和Answer相关性 +* **RGB** (2023.12)专注于生成阶段的评估,提出了信息整合(Info Integration)、噪声鲁棒性(NoiseRobust)、负样本拒绝(NegRejection)、反事实鲁棒性(Counterfact)等细分维度 +* **MultiHop-RAG** (2024.01)针对多跳推理场景,重点评估检索的相关性(Retrieval C)和回答的正确性(Response C),使用MAP、MRR、Hit@K等指标 +* **CRUD-RAG** (2024.02)模拟真实的知识管理场景,评估系统在Create、Read、Update、Delete四种操作下的表现,引入了RAGQuerEval评分体系 + +进入2024年后,评测框架呈现出明显的专业化和细分化趋势。医疗领域有MedRAG,法律领域有LegalBench-RAG,金融领域有相关的domain-specific框架。这些领域框架不仅提供了专业数据集,还针对行业特点设计了定制化的评测指标,比如医疗场景特别关注准确性(Accuracy),法律场景强调文档级精确度(Doc-level Precision)和引用相关性(Citation Relevance)。 + +**工具型框架**则侧重于工程实践,提供了易用的评测工具和集成方案。TruEra RAG Triad、LangChain Benchmarks、RECALL等都属于这一类。它们通常与主流的RAG开发框架深度集成,支持快速接入现有系统,有些还提供了可视化的评测报告和监控面板。 + +上述我们介绍了多种不同的 RAG 评测框架,但在具体实战中该如何选择?不妨先选取 GitHub 星标数量较多的几款进行初步测试;而当企业面临丰富的框架选择、需要落地决策时,可从以下几方面着手。 + +* 快速上手需求:若需快速搭建基线评测,可选择 RAGAS、RAGEval 等综合性框架,它们能提供开箱即用的完整流程,若需深入诊断某一环节问题,则需选用针对性框架 :例如检索效果不佳时可用 MultiHop-RAG,幻觉问题突出时则可采用 CoURAGE 或 RAG Unfairness。 +* 结合行业进行选择:若应用于医疗、法律、金融等专业领域,应优先选择适配该领域的框架,这类框架往往内置专业术语处理、合规性检查等关键能力,通用框架虽功能全面,但在专业场景中易出现 “水土不服” 的情况。 +* 根据集成成本选择:像 LangChain Benchmarks、TruEra RAG Triad 等框架已与主流开发框架深度集成,能快速接入现有系统,而部分学术框架虽技术方法先进,却需额外投入工程适配工作;最后需关注持续维护情况,应优先选择社区活跃、文档完善且持续更新的框架,避开已停止维护的项目,具体可参考 GitHub 星标数量、更新频率、Issue 响应速度等指标。 + +除此之外,在社区中还公认推荐了一批工具,部分框架已在上述内容中提到:Ragas 提供了丰富指标且不绑定特定框架;Continuous Eval 以轻量和低成本为特点,支持构建具备数学保证的评估流水线;TruLens‑Eval 与 LangChain、Llama‑Index 等主流框架集成良好,并提供可视化分析;而 Llama‑Index 自身生态中也集成了评估与合成数据生成功能,便于对其构建的应用进行闭环测试。还有 Phoenix、DeepEval、LangSmith 和 OpenAI Evals 等工具也在持续迭代中,你可综合自身需求和对应工具的口碑进一步选用。 + +### 5.3.4 评测基准 + +评测基准的重要性在实践中常常被低估。许多团队在搭建RAG系统时,往往仅依靠少量人工编写的测试问题便匆忙开始评估,导致上线后的实际效果与测试阶段的表现差距显著。这一问题的根源在于,缺乏具有代表性和系统性的评测数据,难以真实反映复杂多变的业务场景。 + +一个能够有效支撑系统迭代的评测基准,通常具备三个核心特征。首先是代表性,测试数据需要全面覆盖真实业务中的各种场景,包括高频常见问题、复杂边界情况以及异常输入等;其次是标准化,问题和答案的格式、难度系数、评分标准需要统一规范,确保评测结果具有可比性和可重复性;最后是可演化性,基准应能随着系统能力提升和业务需求变化而持续更新,避免因固化的“应试”数据导致评测失真。 + +对于大多数企业而言,由于业务场景存在独特性,最终往往需要构建自己的评测数据集。 + +* 构建过程可以从业务日志中提取真实用户问题入手,并依据类型、频率和难度进行分层采样,以保证数据的代表性。对于简单问题可由领域专家直接标注,复杂问题则可先用高质量LLM生成候选答案,再由专家审核修改,这种"机器生成+人工校准"的方式能显著降低标注成本。 +* 除答案本身,标注相关文档、答案类型、难度等级等元信息,为后续细分析提供支持。建立标注规范,进行多人交叉验证,计算标注一致性(如Kappa系数),确保数据质量。 +* 定期从线上反馈中补充新的测试用例,尤其是系统回答不好的问题,让评测数据与系统能力同步演进。这种持续迭代的机制能让评测基准始终保持对业务场景的敏感度和有效性。 + +当然,如果团队资源有限或希望快速建立基线,参考业界成熟的公开评测基准也是一个可行的起点。截至2025年,已有诸多涵盖通用领域和垂直行业的基准可供选择(见图表)。 + +![](images/image7.png) + +在选择时,应首先明确评测目的:是建立能力基线,还是为上线做最终验证?其次,需评估基准数据是否覆盖了所关心的场景、问题类型和难度。对于新闻、金融等时效性强的场景,必须考察基准是否包含实时数据测试;而对于历史知识处理,静态基准可能已足够。最后,还需权衡标注成本,是直接采用已完整标注的基准,还是使用原始数据自行标注。 + +将自建数据与公开基准结合使用,既能确保评测贴近业务实际,又能借助公共标准进行横向比对,是构建稳健评测体系的务实路径。 + +# 6. 深度研究:从比赛与开源教程中学习(Optional) + +前面介绍的RAG系统原理和基础实现,虽然能帮助你快速搭建起一个可用的原型,但距离真正解决生产环境中的复杂问题还有不小的距离。如果你想深入理解更落地、更有实战价值的RAG技术,参考各大比赛的获奖方案和优质开源教程是最高效的学习路径,这些方案往往集中了优秀团队在真实场景下反复尝试后的最佳实践。 + +但需要强调的是,本节列举的案例并非全部,而是挑选了几个有代表性的方案。当你在实践中遇到特定问题时,建议采用这样的学习策略:先根据问题类型(如"PDF解析"、"多模态检索"、"低延迟优化"等关键词)查找相关竞赛,再深入研究获奖队伍的技术报告或开源代码,往往能找到直接可用的解决思路。 + +## 6.1 语义缓存:优化高频查询场景 + +Hugging Face提供了一个基于Chroma向量数据库的语义缓存实现方案,该教程的地址为: + +[https://huggingface.co/learn/cookbook/semantic_cache_chroma_vector_database](https://huggingface.co/learn/cookbook/semantic_cache_chroma_vector_database) + +![](images/image8.png) + +背景:大多数教程搭建的RAG系统只适合单用户测试,当系统部署到生产环境后,面对几十到几千次的重复查询(比如客服场景中用户反复询问"如何退款"),每次都要执行向量数据库检索和LLM调用,响应时间会明显增加,成本也会快速上升。通过引入语义缓存层,可以在保证答案质量的前提下,大幅减少对原始数据源的访问压力。 + +该方案采用了双层检索架构。基础层使用Chroma向量数据库存储原始知识库(以MedQuad医疗问答数据集为例),为每条数据添加唯一ID方便精确引用。缓存层则基于FAISS构建,选择FlatL2索引来处理小数据集和高维向量。语义缓存被放在用户查询与Chroma之间,而不是缓存LLM的最终回答——这个设计很重要,因为直接缓存回答会导致用户的个性化要求(如"用简单语言解释")失效。 + +缓存系统使用SentenceTransformer的all-mpnet-base-v2模型生成查询向量,通过欧氏距离(设定阈值为0.35)来判断查询是否相似。当缓存满了(max_response参数控制容量)时,采用先进先出策略删除最早的条目。为了支持跨会话使用,缓存数据会保存到JSON文件中。 + +在小规模测试中,首次查询"How do vaccines work?"从Chroma获取结果耗时0.057秒,而相似查询"Briefly explain me what is a Sydenham chorea."从缓存获取仅需0.016秒,检索时间减半。在大规模生产环境中,这个方案可以实现90%-95%的性能优化,有效降低了向量数据库的访问压力和API调用成本。 + +## 6.2 非结构化数据处理:统一多格式文档解析 + +Hugging Face的另一个教程展示了如何使用Unstructured库构建完整的非结构化数据处理流程,教程地址为: + +[https://huggingface.co/learn/cookbook/rag_with_unstructured_data](https://huggingface.co/learn/cookbook/rag_with_unstructured_data) + +![](images/image9.png) + +背景:企业场景中的知识往往分散在PDF、PowerPoint、EPUB、HTML等多种格式中,传统的数据预处理方法要么只能处理单一格式(比如只支持PDF),要么在格式转换过程中丢失关键信息(特别是表格、标题层级等结构化内容),导致RAG系统无法准确理解和检索这些信息。 + +该方案首先下载多格式测试文档作为示例,包括加拿大环境部农药手册PDF(包含大量表格数据)、佛罗里达大学柑橘IPM PowerPoint(包含图表和多层级标题)等真实文档。然后使用Unstructured库的Local Runner完成解析。配置分为三个部分:ProcessorConfig指定输出目录和并行进程数(2个进程),PartitionConfig可选择API分区模式(需要Unstructured密钥,OCR效果更好,特别适合扫描版PDF),SimpleLocalConfig定义文档输入路径。解析后的文档会转换为JSON格式,包含正文、标题、表格等元素类型。 + +系统使用chunk_by_title方法进行分块,设置最大字符数为512,并将200字符以下的连续片段合并以保持语义完整性。在转换为LangChain Document格式时,会过滤掉复杂的元数据字段以适配Chroma向量数据库。向量化阶段采用BAAI/bge-base-en-v1.5嵌入模型,结合4bit量化的Llama-3-8B-Instruct模型和LangChain的RetrievalQA链构建完整的RAG系统。 + +系统能够准确处理多格式文档。测试"Are aphids a pest?"等问题时,可以从解析后的文档中提取蚜虫危害、吸引蚂蚁等关键信息,生成符合需求的回答。这个方案成功实现了从非结构化数据到RAG可用数据的完整转化,特别适合需要处理多种文档格式的企业知识库场景。 + +## 6.3 企业级文档问答:高精准可追溯的RAG实现 + +Enterprise RAG Challenge的冠军方案展示了如何在严格的时间和精度要求下构建生产级RAG系统,相关技术文章地址为: + +[https://abdullin.com/ilya/how-to-build-best-rag/](https://abdullin.com/ilya/how-to-build-best-rag/) + +[https://hustyichi.github.io/2025/07/03/rag-complete/](https://hustyichi.github.io/2025/07/03/rag-complete/) + +背景:参赛者需要在2.5小时内完成100份真实企业年度报告PDF的解析,这些报告每份最多1000页,包含复杂的财务表格、多栏布局、图表等内容。解析完成后,系统需要回答100个精确的业务问题,这些问题要求明确的答案类型(比如Yes/No判断、公司名称、具体数值指标、高管职位名称等),并且必须给出答案来源的页码引用,以便业务人员验证。整个过程模拟的是投资分析师或财务审计人员的实际工作场景。 + +冠军团队选择IBM开源的Docling作为PDF解析工具,因为它在处理复杂表格(比如跨页表格、嵌套表格)和多列文本方面表现最好。团队对Docling代码进行了改进,使其生成包含元数据的JSON和Markdown+HTML格式,特别修复了表格结构的解析问题(比如单元格合并、表头识别等)。为了加速处理,团队租用RTX 4090 GPU,在40分钟内完成了100份报告的解析。 + +文本分块采用300 token长度,重叠50 token,使用递归分割策略保持语义完整。为了避免跨公司信息混淆(比如查询A公司CEO时检索到B公司的信息),为每个公司单独构建FAISS向量库,采用IndexFlatIP索引(不压缩以保证精度)。检索阶段采用三步走策略:首先通过向量检索找到Top30文本块,然后提取这些块所在的父页面并去重(因为多个块可能来自同一页),最后使用GPT-4o-mini对页面进行重新排序。向量检索分数与LLM重排序分数按0.3:0.7的权重混合。 + +生成阶段根据答案类型(数字/布尔/字符串等)使用不同的prompt模板。对于数字类问题(比如"2023年营收是多少"),设计了5步分析流程来确保指标匹配的准确性:识别问题中的指标类型、在文档中定位相关表格、提取数值、验证单位一致性、交叉验证。系统输出采用结构化格式,包含分析过程、相关页码等字段,确保答案可追溯。对于涉及多公司比较的问题(比如"哪家公司营收最高"),会拆分为单公司子查询后再合并结果。 + +该方案在竞赛中获得双奖项及排行榜第一。值得注意的是,即使使用小模型(如Llama 8B)也能超过80%的参与者,使用Llama 3.3 70B时仅比GPT-4o-mini稍差一点,成功实现了准确性、效率与成本的平衡。 + +## 6.4 AIOps场景:图文混合数据的智能处理 + +AIOps RAG竞赛的EasyRAG项目专注于运维场景下的问答任务,技术文章地址为: + +[http://blog.csdn.net/hustyichi/article/details/143323746](http://blog.csdn.net/hustyichi/article/details/143323746) + +![](images/image10.png) + +背景:运维工程师日常工作中需要查阅大量技术文档,这些文档不仅包含文字说明,还包含监控图表、系统架构图、性能曲线等图片信息。比如当系统出现性能问题时,工程师需要快速查询"CPU使用率超过80%时应该如何处理",答案可能分散在文字说明和监控图表中。传统RAG系统只能处理文字,无法理解图表中的趋势和数值,导致答案不完整。该项目最终获得初赛第一、复赛第二的成绩。 + +索引阶段使用改进过的SentenceSplitter(修复了原版块大小计算问题),按1024 token分块,重叠200 token。关键创新在于为每个文本块添加知识库路径、文件路径等元信息,这个改进使召回率提升了2%。对于图片数据,先使用PaddleOCR提取图片中的文字(比如图表的标题、坐标轴标签),再调用GLM-4V-9B多模态模型生成图片的自然语言描述(比如"该折线图显示CPU使用率在下午3点达到峰值90%"),将文字和描述一起入库,实现了图文信息的统一检索。 + +检索阶段采用"双路BM25+向量检索"策略进行广泛召回。BM25包含文档块检索(召回192条)和路径检索(通过文件路径过滤无关文档,比如只检索运维手册而不检索开发文档),使用哈工大停用词表进行去噪。向量检索使用gte-Qwen2-7B-instruct模型,召回288条候选结果。重排序阶段使用bge-reranker-v2-minicpm-layerwise模型,经过测试发现28层配置效果最好。 + +答案生成采用两步策略——先基于Top6文档生成初步结果(覆盖多个相关知识点),再结合Top1最相关文档进行二次优化(突出最核心的答案)。这个设计确保了答案既有足够的信息覆盖面,又能突出最核心的内容。 + +为了应对长文本场景(比如一份完整的运维手册可能有几百页),系统实现了基于BM25的上下文压缩方法。该方法将文档拆分为句子级别,计算每个句子与查询的相似度,然后按比例拼接高相关句子。实验表明,在50%压缩率下,该方法耗时仅7.7秒,准确率达86.48%,优于LLMLingua等现有压缩工具。 + +相比基础方案,加入元信息和答案二次优化后,系统准确性分别都提升了2%。BM25压缩方法在保证效率的同时保持了较高的准确性,很好地适配了AIOps场景下图文数据处理和实时响应的需求。 + +## 6.5 多源数据融合:结构化与非结构化知识的协同 + +KDD Cup 2024 Meta RAG挑战赛的冠军方案展示了如何整合非结构化Web数据和结构化知识图谱,技术文章地址为: + +[https://blog.csdn.net/m0_59164520/article/details/143694213](https://blog.csdn.net/m0_59164520/article/details/143694213) + +https://arxiv.org/pdf/2410.00005 + +![](images/image11.png) + +背景:任务1要求基于5个网页做检索摘要,比如用户搜索"星际穿越导演是谁",系统需要从给定的网页中提取答案。任务2在任务1基础上增加了Mock API(模拟访问结构化知识图谱),系统可以调用API查询电影数据库、人物关系等结构化信息。任务3进一步扩大难度,基于50个网页与Mock API处理复杂查询,比如"哪些由诺兰导演的电影票房超过5亿美元",这需要同时查询知识图谱(导演作品关系)和网页(票房数据)。每个查询需在30秒内完成,核心目标是提升多源数据整合的准确性并减少幻觉。 + +北大db3团队针对任务1设计了精细化的Web数据处理流程。使用BeautifulSoup提取网页文本,ParentDocumentRetriever管理父子块关系(子块200 token用于检索、父块500-2000 token用于生成),确保检索准确性和生成完整性。嵌入模型选用bge-base-en-v1.5,向量库使用Chroma,重排序采用bge-reranker-v2-m3。团队还补充了电影、金融等领域的公开数据(比如IMDB电影数据、上市公司财报),转换为规范格式后入库。模型层面使用LoRA微调Llama-3-8B-instruct,训练数据包括无效问题标注(比如"今天天气怎么样"这类超出知识库范围的问题)、正确答案等。 + +任务2-3的关键创新在于优先使用知识图谱。团队设计了规范化的API调用机制,包括get_person(查询人物信息)、get_movie(查询电影信息)等接口,支持条件过滤(比如cmp操作符筛选票房>5亿的电影)和排序(按票房降序)。API调用生成采用GPT-4初步标注后人工优化的方式进行微调。系统执行时先调用知识图谱API,只有在知识图谱结果无效(比如查询的电影不在数据库中)时才回退到Web检索,这个设计大幅提升了查询效率和答案准确性。 + +通过知识图谱优先策略和结构化输出格式,系统明显减少了LLM的幻觉现象。当知识图谱能提供确定性答案时(比如"诺兰出生年份"),直接输出不经过生成步骤;当需要从Web检索时(比如"诺兰最新访谈内容"),通过严格的文档引用和分步推理确保答案可验证。 + +该方案在三个任务中均获第一,得分分别达到28.4%、42.7%和47.8%,成功平衡了多源数据整合的准确性与实时性。这个案例的核心启示在于,对于包含结构化和非结构化数据的企业场景,应该根据数据特点设计不同的检索策略,优先使用确定性强的结构化数据,用非结构化数据作为补充。 + +深入分析这几个实践案例,我们可以总结出构建高质量RAG系统的几个共同原则:在架构设计上,应该根据业务场景选择合适的缓存、检索和生成策略;在数据处理上,需要针对不同格式和模态设计专门的解析和索引方案;在检索优化上,混合检索加重排序已成为标准配置;在答案生成上,分类型的prompt和结构化输出能明显提升准确性和可追溯性。 + +这些来自真实竞赛和开源项目的经验,为搭建更好的企业级RAG系统提供了宝贵的参考,你可以根据实际问题搜索对应的比赛,找到成熟现有方案进行尝试。 + +# 7. 广度探索:RAG 的未来演化(Optional) + +在深入掌握了RAG的实战技巧与优化方法后,你已经能够在具体场景中有效提升系统性能。然而,要全面把握RAG技术,仅靠对局部的深入钻研还不够,我们还需拓展视野,从更广阔的维度理解其演进方向与扩展空间。 + +当前,RAG技术正在迅速突破传统基于文档分块的检索生成范式,朝着更多样化的方向发展。本节将重点探讨以下几个演进路径:从简单的分块检索转向对图数据结构的检索;融合图像、音频等多模态信息以增强RAG的检索能力;利用向量化技术优化长文档的分块处理策略;以及RAG如何逐步演化为智能体(Agent)系统。 + +## 7.1 Graph RAG:用关系网络重塑深度检索 + +相关研究:[https://arxiv.org/pdf/2410.05779](https://arxiv.org/pdf/2410.05779), [https://arxiv.org/pdf/2502.11371](https://arxiv.org/pdf/2502.11371), https://arxiv.org/pdf/2404.16130 + +![](images/image12.png) + +传统RAG依靠寻找和问题相似的文本段落来工作,这就像在一堆材料里挑出看起来最相关的几段话。对于直接查找某个具体信息,这种方法很有效。但如果一个问题需要联系多份文档、结合不同线索才能回答,它的表现就会打折扣。 + +比如,医生可能想问:“根据这些病例和最新的治疗指南,如何评估某种药物对老年患者的好处和风险?”又或者,项目团队可能关心:“综合过去两年的需求文档、评审记录和线上问题报告,我们这个系统架构最常出问题的环节是什么?”这类问题的关键,不是找到某一句原话,而是要从各种分散的材料中,找出其中提到的人、事、物以及它们之间的联系,理清头绪,形成一幅完整的全景图。 + +Graph RAG的做法,就是先主动画出这幅全景图。系统会利用大模型从文本中识别出关键元素(比如人物、机构、功能模块、事件、数据等)以及它们之间的关系(比如谁导致了什么、什么依赖于什么、如何变化、有何矛盾等),从而构建一个随着资料增加而不断丰富的知识网络。接着,通过自动分组,把联系紧密的元素和关系归类到不同的主题下,并为每个主题提前生成一段概括性描述。这样,当用户提问时,系统不再仅仅是寻找字面上最相似的段落,而是会先在知识网络里找到与问题最相关的元素和局部结构,再顺着连接线扩展到相关的主题组,最后将这些分析路径、节点说明和对应的原始文本片段,一起交给大模型进行推理并组织答案。 + +在这样的框架下,Graph RAG和传统RAG形成了很好的分工与配合:传统RAG仍然擅长回答直接的、一步就能找到答案的细节问题;而Graph RAG则更像人在做研究或写报告时的思路——先梳理出整体结构和主题(构建网络与分组),再填充具体依据(引用原文),最后给出有逻辑、有条件限制的结论。已有的系统对比也显示,在需要联系多个信息点进行推理的任务中,Graph RAG通常能涵盖更关键的内容,提供更全面的视角;而根据问题的具体特点,灵活结合使用两种方法,整体效果往往比只使用其中一种更好。 + +## 7.2 Multimodal RAG:多模态 RAG + +相关研究:https://arxiv.org/pdf/2502.08826 + +![](images/image13.png) + +现实世界的数据从来不是单一文本。工程师排查服务器故障时,需要同时看温度监控曲线、设备面板截图和系统日志;医生做诊断时,需要把 CT / MRI 影像、检查报告和电子病历放在一起看。传统的文本 RAG 最多只能检索到“温度异常”“怀疑肺结节”这样的文字描述,却难以把这些描述与具体的曲线走势、影像病灶形态对应起来,更做不到用“图/音/视频”去反向检索相关文档和知识。 + +Multimodal RAG(多模态 RAG)解决的是这种“模态之间互相看不见”的问题。它的核心在于跨模态语义对齐:为图像、视频、音频、文本等分别配置合适的编码器(如 ViT/CLIP 编图像和视频帧,Whisper 编音频,BGE-M3 等编码文本),配合 OCR、ASR、版面分析等工具,把视觉和音频里的关键信息抽取出来,再通过模型把不同模态的表示映射到一个共享的语义空间中,构建统一的多模态索引。 + +在检索和生成阶段,不管用户是问“找一张显示 2023 年 Q3 销售峰值的图表”,还是上传一张产品草图或一段操作视频发起查询,系统都会先在这个统一空间里找到一批最相近的多模态内容,然后根据文本相似度、图像相似度等信号,筛掉明显无关的结果,保留几条最有用的证据。最后,把这些经过筛选的图、文、表格等,一并交给多模态大模型,由它综合不同模态的信息给出答案,并尽量标明信息来源,或在截图、文档中高亮相关位置。这样一来,相比只看文本的 RAG,系统既能利用更多模态的线索,又更容易减少幻觉,让答案更完整也更容易核实。 + +## 7.3 Late Chunking:为长文档保留完整上下文 + +相关介绍:https://jina.ai/news/late-chunking-in-long-context-embedding-models/ + +![](images/image14.png) + +想象你正在阅读一篇关于柏林的维基百科文章,传统RAG系统会先将其切成独立段落再生成向量。当第一句提到"柏林是德国首都"后,后续段落中的"该城市"、"它的人口"等指代词就失去了与"柏林"的关联。此时若查询"柏林的人口是多少",系统会因为"柏林"和"人口数据"从未在同一文本块出现而检索失败。这个问题在长文档场景更为严重:一份200页的保险合同中,"免赔额"的定义在第5页,具体适用条件在第30页,传统的固定长度切分(如每512 tokens一块)会将这些相关信息分散到40多个独立文本块中,实验数据显示这种割裂会导致语义相似度从0.85暴跌至0.71。 + +Late Chunking颠覆了"先切后编"的传统流程,改为"先编后切":利用支持8192 tokens(约10页文本)的长上下文嵌入模型(如Jina Embeddings v2),首先将整个文档输入Transformer层,生成每个token的向量表示——此时每个token的嵌入已经"看到"了全文信息,捕获了跨段落的指代关系和概念关联。随后,再对这些已经全局感知的token向量进行分块平均池化(mean pooling),生成最终的块嵌入。这样生成的文本块不再是独立同分布的孤岛,而是"条件依赖"的上下文链:当处理"该城市有385万居民"这句话时,向量中已经包含了前文"柏林"的语义信息,使相似度从0.71提升至0.83。关键区别在于边界标记的使用时机:传统方法在预处理阶段就用句号、段落符切分文本,而Late Chunking仅在获得全局token嵌入后,才应用边界线索进行智能分块。 + +在BEIR基准测试的5个数据集上,Late Chunking全面超越传统切分方法。最显著的案例是NFCorpus数据集(平均文档长度1590字符),检索准确率从23.46%飙升至29.98%,相对提升27.8%;而在短文本场景(如Quora的62字符问题)两者表现相同,验证了一个关键规律:文档长度与Late Chunking的优势呈正相关。从技术对比表可见核心差异:传统切分在预处理阶段直接应用边界标记,产生独立同分布的块嵌入,上下文信息丢失;Late Chunking在获得token嵌入后才应用边界标记,产生条件依赖的块嵌入,由长上下文模型完整保留上下文信息。 + +该方法现已集成到Jina Embeddings v3 API中,虽然需要先编码整个长文档,推理时间增加10-20%,但在医疗病历(跨章节的诊断依据)、法律文档(定义与条款的交叉引用)、技术手册(概念解释分散在多个章节)等场景中,检索准确率的大幅提升远超过这点性能开销。Late Chunking不仅证明了8K+长上下文模型的实用价值——不是"过度设计",而是实现高质量块嵌入的必要条件,更为RAG系统提供了一条摆脱滑动窗口、多次扫描等"hit-or-miss"启发式技巧、具有理论保证的优化路径,代表了从"先切后编"到"先编后切"的范式转变。 + +## 7.4 从 RAG 到 Agent 时代的 RAG + +相关讨论:[https://ragflow.io/blog/rag-at-the-crossroads-mid-2025-reflections-on-ai-evolution](https://ragflow.io/blog/rag-at-the-crossroads-mid-2025-reflections-on-ai-evolution), [https://arxiv.org/pdf/2501.09136](https://arxiv.org/pdf/2501.09136), [https://www.letta.com/blog/rag-vs-agent-memory](https://www.letta.com/blog/rag-vs-agent-memory), [https://www.linkedin.com/posts/richmondalake_100daysofagentmemory-rag-memorizz-activity-7348281860843577346-LM7Y/](https://www.linkedin.com/posts/richmondalake_100daysofagentmemory-rag-memorizz-activity-7348281860843577346-LM7Y/), https://www.llamaindex.ai/blog/rag-is-dead-long-live-agentic-retrieval + +RAG技术已从最初的检索增强生成工具,发展为构建智能体认知架构的关键部分。 传统RAG系统基于提问、检索、回答的简单模式,本质是被动接受查询,不具备主动行动的能力。为了突破这种被动性并处理更复杂的认知任务,RAG与智能体能力进行了深度融合,由此诞生了Agentic RAG这一新范式。 在此范式中,RAG的角色发生了根本转变:它不再仅仅是外部知识的被动提供者,而是在智能体的主动规划、目标指引和反思能力驱动下,成为支撑智能行为的核心处理单元。这种融合使系统整体具备了目标导向、迭代优化和自主决策的能力,显著提升了人机交互的深度与质量。具体而言,Agentic RAG能够理解复杂任务,自主拆解问题,规划检索策略,并在获取初步信息后评估结果质量,决定是否深入探索,从而胜任传统RAG难以应对的多步骤复杂任务。 + +![](images/image15.png) + +Agentic RAG实现上述复杂任务处理的关键,在于其建立了一个多层次的主动循环工作机制。 面对复杂查询,智能体首先分析问题本质,将其拆解为子问题,并为每个子问题设计精准的检索策略。获得初步结果后,智能体进行评估与反思,判断信息的完整性和相关性,识别知识缺口,并动态生成更精确的新查询。这种迭代过程常包含多跳检索,即基于前一轮结果发现新的检索方向,形成类似人类研究者的知识探索链条。然而,要支撑这种持续的、迭代的智能行为,尤其是实现长期交互中的个性化和知识积累,仅依赖单次会话的短期上下文(短期记忆)是远远不够的。这引出了对长期、结构化记忆能力的需求。 + +正是为了满足这一需求,RAG被赋予了作为智能体长期记忆系统的角色,构建了一个完整的外部记忆架构。 该系统与负责维护当前会话上下文的短期记忆形成互补。该长期记忆系统的核心运作依赖于三项关键机制: + 第一,结构化索引能力:使智能体能够为海量非结构化数据建立多维索引体系(如按时间、主题或实体关系),支持多角度高效检索,模拟人脑通过不同线索回忆信息的方式。 + 第二,智能遗忘机制:通过价值评估算法,系统对使用频率低、相关性弱或过时的信息进行权重衰减或选择性剔除,维持记忆系统的精炼高效,防止信息过载。 + 第三,知识巩固过程:系统将零散对话和交互经验提炼为结构化知识,利用实体识别、关系抽取和语义聚类等技术,将碎片信息整合连接成知识图谱,完成从短期经验到长期知识的转化与沉淀。 + +这种由RAG构建的外部记忆系统,不仅极大地扩展了智能体的认知边界,更重要的是赋予了其持续学习和知识进化的能力。 它使得智能体能够在长期互动中积累经验,形成个性化的处理模式和领域专业知识体系,从而为执行更复杂、更持久的任务提供了坚实的基础。 + +# 总结 + +检索增强生成不仅是一种弥补大模型幻觉与知识滞后性的技术方案,更是将通用AI能力转化为企业深度业务价值的关键桥梁。从基础的Naive RAG演进至模块化、智能体协同的Advanced RAG,这一过程反映出RAG在各个环节均需持续深化——无论是更精细的数据处理、更科学的模型选型(Embedding、Rerank、LLM),还是更体系化的效果评测,都是构建可控、可信、高效的企业级知识系统的必经之路。同时,从各类竞赛与实践案例中汲取经验技巧,也能进一步加深对技术细节的理解。 + +随着图结构检索(Graph RAG)、多模态理解与Late Chunking等前沿方向的融合发展,RAG正不断突破传统检索生成的边界,逐步具备更深层的语义关联与可持续的记忆能力。希望通过这篇综述类文章的学习,能够帮助你掌握从原理到实践、从评估到演进的全链路方法论,从而在快速迭代的技术浪潮中,打造出真正落地、能够应对复杂业务挑战的高质量智能应用。 + +# Reference + +[1] Ask in Any Modality: A Comprehensive Survey on Multimodal Retrieval-Augmented Generation. + +https://arxiv.org/pdf/2502.08826 + +[2] Retrieving Multimodal Information for Augmented Generation: A Survey. + +https://arxiv.org/pdf/2303.10868 + +[3] A Survey on RAG Meeting LLMs: Towards Retrieval-Augmented Large Language Models. + +https://arxiv.org/pdf/2405.06211 + +[4] Retrieval-Augmented Generation for Large Language Models: A Survey. + +https://arxiv.org/pdf/2312.10997 + +[5] LightRAG: Simple and Fast Retrieval-Augmented Generation. + +https://arxiv.org/pdf/2410.05779 + +[6] Agentic Retrieval-Augmented Generation: A Survey on Agentic RAG. + +https://arxiv.org/pdf/2501.09136 + +[7] ERAGent: Enhancing Retrieval-Augmented Language Models with Improved Accuracy, Efficiency, and Personalization. + +https://arxiv.org/pdf/2405.06683 + +[8] Graph Retrieval-Augmented Generation: A Survey. + +https://www.arxiv.org/pdf/2408.08921 + +[9] Evaluation of Retrieval-Augmented Generation: A Survey. + +https://arxiv.org/pdf/2405.07437 + +[10] Retrieval Augmented Generation Evaluation in the Era of Large Language Models: A Comprehensive Survey. + +https://arxiv.org/pdf/2504.14891 + +[11] From Local to Global: A Graph RAG Approach to Query-Focused Summarization. + +https://arxiv.org/pdf/2404.16130 + +[12] RAG vs. GraphRAG: A Systematic Evaluation and Key Insights. + +https://arxiv.org/pdf/2502.11371 + +[13] Introduction to RAG | LlamaIndex Python Documentation. + +https://developers.llamaindex.ai/python/framework/understanding/rag/ + +[14] All-in-RAG | 大模型应用开发实战:RAG 技术全栈指南. + +https://datawhalechina.github.io/all-in-rag/#/en/ + +[15] Ilya Rice: How I Won the Enterprise RAG Challenge. + +https://abdullin.com/ilya/how-to-build-best-rag/ + +[16] RAG Research Table – Awesome Generative AI Guide (GitHub). + +https://github.com/aishwaryanr/awesome-generative-ai-guide/blob/main/research_updates/rag_research_table.md + +[17] RAG is dead, long live agentic retrieval. + +https://www.llamaindex.ai/blog/rag-is-dead-long-live-agentic-retrieval + +[18] LLM/RAG Zoomcamp 課外補充 5:RAG Evolution 常見評估方法和市場偏好. + +https://vip.studycamp.tw/t/llmrag-zoomcamp-%E8%AA%B2%E5%A4%96%E8%A3%9C%E5%85%85-5%EF%BC%9Arag-evolution-%E5%B8%B8%E8%A6%8B%E8%A9%95%E4%BC%B0%E6%96%B9%E6%B3%95%E5%92%8C%E5%B8%82%E5%A0%B4%E5%81%8F%E5%A5%BD/8185 + +[19] How to Evaluate Retrieval Augmented Generation (RAG) Applications. + +https://zilliz.com.cn/blog/how-to-evaluate-rag-zilliz + +[20] RAG is not Agent Memory. + +https://www.letta.com/blog/rag-vs-agent-memory + +[21] Richmond Alake. LinkedIn post on #100DaysOfAgentMemory, RAG and MemoRizz. + +https://www.linkedin.com/posts/richmondalake_100daysofagentmemory-rag-memorizz-activity-7348281860843577346-LM7Y/ diff --git a/docs/extra/extra5/images/image1.png b/docs/extra/extra5/images/image1.png new file mode 100644 index 0000000..af7d361 Binary files /dev/null and b/docs/extra/extra5/images/image1.png differ diff --git a/docs/extra/extra5/images/image10.png b/docs/extra/extra5/images/image10.png new file mode 100644 index 0000000..03694c4 Binary files /dev/null and b/docs/extra/extra5/images/image10.png differ diff --git a/docs/extra/extra5/images/image11.png b/docs/extra/extra5/images/image11.png new file mode 100644 index 0000000..5642883 Binary files /dev/null and b/docs/extra/extra5/images/image11.png differ diff --git a/docs/extra/extra5/images/image12.png b/docs/extra/extra5/images/image12.png new file mode 100644 index 0000000..c63c018 Binary files /dev/null and b/docs/extra/extra5/images/image12.png differ diff --git a/docs/extra/extra5/images/image13.png b/docs/extra/extra5/images/image13.png new file mode 100644 index 0000000..2f012ba Binary files /dev/null and b/docs/extra/extra5/images/image13.png differ diff --git a/docs/extra/extra5/images/image14.png b/docs/extra/extra5/images/image14.png new file mode 100644 index 0000000..18c4ae3 Binary files /dev/null and b/docs/extra/extra5/images/image14.png differ diff --git a/docs/extra/extra5/images/image15.png b/docs/extra/extra5/images/image15.png new file mode 100644 index 0000000..7dd9e8a Binary files /dev/null and b/docs/extra/extra5/images/image15.png differ diff --git a/docs/extra/extra5/images/image2.png b/docs/extra/extra5/images/image2.png new file mode 100644 index 0000000..169cb2c Binary files /dev/null and b/docs/extra/extra5/images/image2.png differ diff --git a/docs/extra/extra5/images/image3.png b/docs/extra/extra5/images/image3.png new file mode 100644 index 0000000..056fba7 Binary files /dev/null and b/docs/extra/extra5/images/image3.png differ diff --git a/docs/extra/extra5/images/image4.png b/docs/extra/extra5/images/image4.png new file mode 100644 index 0000000..5b5b17e Binary files /dev/null and b/docs/extra/extra5/images/image4.png differ diff --git a/docs/extra/extra5/images/image5.png b/docs/extra/extra5/images/image5.png new file mode 100644 index 0000000..5a83f04 Binary files /dev/null and b/docs/extra/extra5/images/image5.png differ diff --git a/docs/extra/extra5/images/image6.png b/docs/extra/extra5/images/image6.png new file mode 100644 index 0000000..621a6da Binary files /dev/null and b/docs/extra/extra5/images/image6.png differ diff --git a/docs/extra/extra5/images/image7.png b/docs/extra/extra5/images/image7.png new file mode 100644 index 0000000..f4d2625 Binary files /dev/null and b/docs/extra/extra5/images/image7.png differ diff --git a/docs/extra/extra5/images/image8.png b/docs/extra/extra5/images/image8.png new file mode 100644 index 0000000..56d75a4 Binary files /dev/null and b/docs/extra/extra5/images/image8.png differ diff --git a/docs/extra/extra5/images/image9.png b/docs/extra/extra5/images/image9.png new file mode 100644 index 0000000..ead7a0f Binary files /dev/null and b/docs/extra/extra5/images/image9.png differ diff --git a/docs/extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md b/docs/extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md new file mode 100644 index 0000000..c282b8f --- /dev/null +++ b/docs/extra/extra6/extra6-zeabur-what-is-it-and-how-to-deploy-web-applications.md @@ -0,0 +1,211 @@ +# 扩展知识 6 - Zeabur 是什么,以及如何部署 Web 应用 + +在本教程中,我们将介绍 Zeabur——一个用于部署 Web 服务的平台。它可以帮助我们快速完成从“写好代码”到“让别人可以在互联网上访问你的网站”的完整流程。 + +# 什么是“部署”? + +在开始之前,我们先弄清楚“部署(Deployment)”到底是什么意思。任何一个网站想要被外部用户访问,都必须有一个可以公开访问的网络地址(这个地址可以是 IP 地址,比如 123.45.67.89,也可以是域名,比如 [google.com](https://google.com/) 等)。但只有地址是不够的——你写好的网页代码(例如 HTML、CSS、JavaScript 文件,或者使用 React、Vue 等框架写的项目),以及相关的图片 / 视频资源,都必须“放”在一台 24 小时在线的服务器上,由它来响应网络请求,这样任何人的浏览器才能访问并下载这些资源。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image1.png) + +图片来源:https://www.hostinger.com/tutorials/what-is-cloud-hosting + +把资源上传、配置好环境并让服务“跑起来”的整个过程,就被称为 **部署(Deployment)**。 + +简单来说:你在自己电脑上写好的网页,只要在本机启动程序,就只能通过本地地址在自己的浏览器里访问,因为这些代码只存在于你的硬盘上。“部署”就是把你的代码和资源转移到一台连接着公网的专业服务器上,并做好配置,让这台服务器知道“别人访问时我要怎么响应”——比如:当有人在浏览器中输入你的域名时,服务器会立刻找到对应的网页文件,把内容传回给对方的设备,从而让用户看到你的页面。 + +如果手动部署,一个项目往往需要好几个步骤,每一步都可能踩坑。常见关键步骤包括: + +1. **服务器准备**:你需要先购买云服务器(比如阿里云、腾讯云、或 AWS EC2),选择服务器所在地区(如上海、新加坡)、配置(CPU、内存、磁盘大小等),还要学会如何远程连接服务器(例如通过 SSH 工具登录)。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image2.png) +2. **环境配置**:Web 应用需要在特定“环境”中才能运行——例如运行 Node.js 项目必须先安装 Node.js;运行 Python 项目必须安装 Python 以及对应的第三方库。如果环境版本不匹配,程序就可能报错、无法启动。 +3. **上传资源**:你需要把本地的代码和资源上传到服务器上,常用的方法包括 FTP 或 Git。如果项目体积比较大(比如包含视频文件),中途一旦断线,有时需要重新上传。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image3.png) + +4. **启动服务并测试**:上传完成后,你还需要在服务器上执行命令启动应用,并测试“分配的网络地址是否能访问”。如果访问不了,有可能是服务器防火墙没有放行对应端口(比如你的应用监听 3000 端口,但该端口被防火墙拦截),也可能是程序本身有 Bug,这时就需要查看服务器日志进行排查。 + > 💡 可以把端口理解为区分同一台设备上不同应用的“房间号”,而 IP 则是这台设备的“门牌号”。IP 和端口合在一起(IP:port),就可以精确定位到某一个网络服务。 +5. **维护与更新**:后续每次你修改代码,都要重新上传并重启服务。如果服务器宕机(例如断电、网络故障),还需要手动重启应用,有时还要额外配置“进程守护工具”,让程序在异常退出后自动拉起。 + +像 Zeabur 这样的“低代码部署平台”,就是为了解决上述复杂问题而诞生的。它会帮你自动完成“买服务器、配环境、上传代码、启动服务、监控运行”等步骤。你只需要把自己的代码仓库(比如 GitHub 或 GitLab)连接到 Zeabur,它就会自动拉取代码、识别应用类型、配置对应的运行时环境,最后给你一个可以被任何人访问的公网地址。它甚至可以一键绑定你自己的域名(例如把 your-app.zeabur.app 改成 your-app.com)。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image4.png) + +接下来,我们会一步步演示如何使用 Zeabur,从“代码仓库”走到“公开可访问的网页”,全程不需要手写任何服务器命令。当然,你也可以使用 Vercel(同样有免费额度)来做类似的简单 Web 部署。不过,[Vercel](https://vercel.com/) 在部分网络环境下访问不太稳定,有兴趣的同学可以课后自行学习(操作也很简单:就是把 GitHub 项目连上即可)。 + +# 使用 Zeabur 部署 Dify + +在之前的课程中,我们已经简单接触过 Dify。现在,我们可以通过 [Zeabur](https://zeabur.com/projects) 非常轻松地启动自己的 Dify 服务。首先打开 [控制台页面](https://zeabur.com/projects),我们先看一下上面的各个区域。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image5.png) + +在这个页面上,你首先能看到许多方块,这些就是已经启动的服务。在顶部菜单中,你会看到 Agent、Servers、Docs、Templates 等几个选项,它们分别代表: + +1. **Agent**:可以打开 Zeabur 内置的智能助手(Agent),向它提问如何操作,或者查询当前服务器的状态。 +2. **Servers**:在这里可以添加你自己购买的云服务器,或者直接通过 Zeabur 购买服务器。 +3. **Docs**:查看 Zeabur 的完整文档说明。 +4. **Templates**:这里列出了所有内置的模板镜像。 + +> 这里提到的“镜像(Image)”,可以理解为“包含代码和运行环境的压缩包”。当某个服务在一台服务器上成功跑起来之后,我们可以选择把“这套运行环境 + 代码”打包成镜像。之后,在任何新服务器上,只要把这个压缩包解压并运行,就不需要重新配置环境和代码,服务就能直接跑起来。 + +在页面右上角,你还能看到自己的余额。默认情况下,每个月会有 5 美元左右的免费额度。关于细节计费规则暂时可以不用太在意,只需要知道:只要服务器在运行,就会消耗额度。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image6.png) + +点击余额可以查看每日的消耗明细。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image7.png) + +现在我们来创建自己的 Dify 服务。首先,在 [控制台首页](https://zeabur.com/projects) 点击 “New Project”。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image8.png) + +接下来是各个创建方式的解释: + +1. **GitHub** + 可以连接到你的 GitHub 账号。绑定之后,就可以直接从 GitHub 仓库里选择项目部署(GitHub 是目前全球最大的代码托管平台)。 +2. **Template(模板)** + 可以基于模板来部署服务。Zeabur 内置了很多预设项目模板(例如 Dify、n8n 等),你可以基于这些模板快速创建并部署应用。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image9.png) +3. **Databases(数据库)** + 用于部署数据库服务,比如 MySQL、MongoDB 等常见数据库。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image10.png) +4. **Functions(函数)** + 可以部署函数服务,你可以编写 JavaScript 或 Python 代码,让它们以函数的形式被调用。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image11.png) + + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image12.png) +5. **Local Project(本地项目)** + 上传一个本地文件夹,Zeabur 会自动识别其中的启动脚本。这适合将你已经在本地开发好的项目快速部署到 Zeabur 上。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image13.png) +6. **Docker Image** + 部署已经打包好的 Docker 镜像。如果你的项目已经被打成了 Docker 镜像(例如存放在 Docker Hub 或其他镜像仓库中),可以在这里直接部署。 + ![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image14.png) +7. **Cursor** + 如果你安装了 Cursor(例如 Cursor IDE),可以通过这个入口将 Cursor 中的项目直接部署到 Zeabur。 + +如果你想部署自己的 Dify 服务,推荐选择 **Template** 方式,然后在搜索框中输入 “dify”。可以看到很多由不同作者维护的版本,你可以任选其一(比如 v1.6.0 版本)。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image15.png) + +接着,输入任意一个名称,Zeabur 会基于这个名称生成一个临时的自定义域名。之后所有人都可以通过这个网址访问你的服务。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image16.png) + +创建完成后,你会看到多个程序(服务)依次启动。需要耐心等待所有服务都进入“已启动”状态。(Dify 服务是由多个程序组成的,每个程序负责不同的功能,它们之间会相互协作。) + +一般来说,你只需要点击左侧的 Dify 应用,就可以看到默认的访问入口地址。但在本例中,由于前面还套了一层 nginx,你需要点击 nginx 服务来获取最终访问地址。可以理解为:nginx 就是负责对外统一“收发请求”的主程序,它会把外部访问的地址分发给内部各个服务。点击左侧的 Nginx,在详情页中可以看到当前的服务地址,然后在浏览器里打开这个地址,等待服务完全启动。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image17.png) + +稍等片刻后,你就能看到 Dify 的登录界面了。输入邮箱地址和注册密码,就可以开始使用你自己的 Dify 服务了。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image18.png) + +如果你有兴趣,还可以顺便启动一个 n8n 服务。n8n 也是海外非常流行的一款 AI 工作流平台。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image19.png)![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image20.png) + +# ⚠️ 如何停止和删除项目 + +由于启用服务器相关资源都会产生费用,我们在使用时一定要养成“及时关闭不用服务”的习惯,避免把每个月的免费额度消耗完。 + +如果要找到项目的管理入口,首先点击项目中的 “Settings” 选项。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image21.png) + +进入设置页面后,将页面拉到最下方,你会看到类似下面的界面: + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image22.png) + +你可以点击 “Suspend All Services” 来暂停所有服务以降低费用;如果服务出现问题,可以点击 “Restart All Services” 对全部服务进行重启。如果你确定不再需要这个项目,可以点击 “Delete Project” 将整个项目彻底删除。 + +# 使用 Zeabur 与 Trae 部署贪吃蛇游戏 + +在本教程的下一个部分,我们会体验 Zeabur 的一些进阶用法。我们先用 Trae 生成一个贪吃蛇小游戏,再把它部署到 Zeabur 的服务器上,并配置一个可公开访问的链接,让任何人都可以打开你的游戏。 + +第一步,是在本地使用 Trae 创建一个贪吃蛇项目。 + +### 使用 HTML 框架实现 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image23.png) + +对于 Trae 来说,生成一个基于 HTML 的贪吃蛇网页游戏非常简单。游戏生成完成后,你只需要按照前面介绍的 Zeabur 本地部署方式,把包含所有文件的文件夹上传上去即可。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image24.png)![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image25.png)![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image26.png) + +完成后,你就会进入该服务的详情界面: + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image27.png) + +点击左侧的 “Network” 选项,在页面中找到 “Public Address” 区域。点击 “Generate Domain”,即可生成一个对外访问地址,你可以输入任意喜欢的名称。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image28.png) + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image29.png) + +生成完成后,只要在浏览器中打开这个地址,就可以运行你自己的贪吃蛇游戏了。其它 HTML 类型的 Web 应用也可以用完全相同的方式来部署。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image30.png) + +### 使用 React 框架实现 + +前面我们学习了如何部署基于 HTML 的 Web 应用。接下来,我们再尝试部署一个目前更常用的前端框架:React 应用。相比纯 HTML,React 被认为是一种更加成熟、现代的前端开发框架。它通过组件化的方式组织页面结构,能够显著加快复杂页面的开发,是企业级项目中非常主流的选择。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image31.png) + +#### 重构为 React 架构 + +在 Trae 中,你只需要向 Agent 说明:“帮我把这份代码重构成 React 架构”,就可以比较轻松地把原本基于 HTML 的结构重构成 React 项目。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image32.png) + +不过,相比简单的 HTML 文件,React 应用依赖更复杂的构建工具和项目结构,因此部署过程也会稍微麻烦一些。一个典型的问题体现在端口设置上:默认情况下,React 应用一般会监听 3000 端口(你也可以在配置文件或启动日志中看到这一点)。 + +然而,在 Zeabur 上这样部署会失败——因为 Zeabur 只支持监听 8080 端口的应用。也就是说,如果想让 React 应用在 Zeabur 上正常运行,我们必须先把默认监听端口从 3000 改成 8080。 + +要正确进行这一步配置,我们需要先弄清楚两个概念:什么是“端口(Port)”,以及“监听端口(Listening Port)”是什么意思。 + +#### 什么是端口? + +> 在计算机网络中,端口可以理解为一个“逻辑通信端点”,用来区分同一台设备上运行的不同网络服务。简单类比的话,如果 IP 地址好比一个“门牌号”(例如 162.128.1.1),那端口号就像这栋楼里不同房间的“房间号”——每个房间对应一个服务(例如 Web 服务器、邮箱服务,或者你的 React 应用)。 +> +> 端口号用 16 位整型表示,取值范围是 0 到 65535。 + +如果不想记这些细节,可以简单理解:端口是构成“网络访问地址”的一个必要部分。 + +我们平时访问网站或 IP 地址时,通常不会手动加端口号,是因为 Web 的默认端口是 80 或 443(HTTPS)。大多数浏览器会自动使用这些标准端口。而对于一些特殊端口,比如 React 默认的 3000、Zeabur 要求的 8080,我们就必须在地址后面加上 `:3000` 或 `:8080` 才能访问到对应的内容。 + +#### 什么是“监听端口号”? + +> “监听端口号”指的是某个程序在一台设备上主动“打开并监控”的端口。当一个应用设置了监听端口时,其实就是在告诉操作系统:“我会一直在这个端口上等待网络请求——只要有请求进来,就请转发给我。” + +再形象一点地理解:假设你的电脑是一栋写字楼,IP 地址是这栋楼的地址。楼里开了很多公司或部门,它们分别占用不同的房间,房间号就是端口号。 + +当默认的 React 开发服务器启动时,它会“打开”某个房间的门,并安排“前台”在门口值班,这个房间号就是它的监听端口——3000。 + +同时,React 程序还会告诉这栋楼的“物业管理”(操作系统):“我在 3000 号房间,请把所有寄给 3000 的信件(网络请求)都转给我。” + +这样,当你访问 React 网站时,请求首先会到达这栋楼;物业看到请求要送到 3000 号房间,就会立刻把请求交给 React 的“前台”,由它来处理并返回结果——这就是访问 React 应用的过程。 + +当你在本地执行 `npm start`(本地启动 React 开发服务器的默认命令,也可以在 Vibe Coding 的 Agent 侧边栏中执行)时,React 开发服务器就会自动把监听端口设置为 3000。 +而 Zeabur 的平台设计决定了它只会“识别”监听 8080 端口的应用。如果你的 React 应用仍然使用默认的 3000 端口,Zeabur 就无法将请求正确转发给你的应用,最终导致部署失败。 + +#### 修改默认监听端口 + +要把 React 默认监听端口(3000)改成 Zeabur 所要求的 8080,有很多做法。最简单的方式,就是直接在 Trae 里对 Agent 下指令:“请帮我把这个 React 项目的默认端口改为 8080。”Trae 就会帮你修改项目中对应的配置文件。修改完成后,你只需重新打包并按前面的方式上传到 Zeabur 即可。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image33.png) + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image34.png) + +在网络设置中指定一个访问 URL,方式和部署 HTML 项目时基本相同,就可以启动 React 版本的服务。 + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image35.png) + +![](../../../English/Extra/Extra%20Knowledge%206%20-%20Zeabur:%20What%20Is%20It%20and%20How%20to%20Deploy%20Web%20Applications/images/image36.png) + +对于其它需要修改端口号的程序,你也可以采用同样的思路:先改默认端口,再上传到 Zeabur 部署。至此,你已经掌握了将常见 Web 应用部署到服务器的基础技能。 + +你可以尝试让 Trae 帮你构建不同类型的应用,并把它们部署到 Zeabur 的默认服务器上。在后续课程中,我们还会学习如何把应用部署到你自己购买的云服务器上。 + diff --git a/docs/extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md b/docs/extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md new file mode 100644 index 0000000..8aa8952 --- /dev/null +++ b/docs/extra/extra7/extra9-cli-ai-coding-tools-and-the-principles-of-test-driven-development.md @@ -0,0 +1,630 @@ +# 扩展知识 7 - CLI AI 编程工具与测试驱动开发原则 + +在本教程中,我们将介绍直接在命令行中运行的 AI 编程 Agent。它们和之前学过的 Trae、Cursor 中的 Agent 不同,CLI AI 编程工具只能在终端中使用。与集成在 AI IDE 里的 Agent 相比,它们通常具有更长的上下文窗口、更快的工具调用速度,并且可以兼容更多种类的大模型。在最新的 AI Vibe Coding 实战中,我们往往会优先使用 CLI AI 编程工具,而不是 IDE 内置的编码 Agent。 + +## 从 CLI 说起 + +还记得我们之前介绍过的 CLI 吗?CLI 指的是通过终端或命令提示符,用纯文本命令来操作软件应用,而不是依赖图形界面(GUI——你可以简单理解为电脑或手机上带按钮、可以点击操作的界面,不需要输入命令)。 + +> 在 Windows 上,常见的终端有“命令提示符(cmd)”和 “PowerShell”。你可以在电脑的运行/搜索框中输入 “cmd” 或 “powershell” 来启动这些命令行程序。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image1.png)![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image2.png) + +CLI 天生适合文本命令操作,在一小部分极客(追求极致的编程爱好者)群体中,CLI 甚至比 GUI 更受欢迎——他们希望所有操作都通过键盘完成,觉得动鼠标反而会拖慢自己的编码效率。 + +在工业界,CLI 往往也是最常见的接口形式,因为 GUI 需要操作系统额外绘制界面、管理窗口,对计算机资源的要求更高;而 CLI 只需要把收到的命令传给系统执行即可。因此,在连接大规模服务器集群时,我们通常只通过 CLI 进行交互。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image3.png) + +对于许多没有 CLI 经验的同学来说,可能会觉得 CLI 操作很复杂、命令太多,甚至担心“一不小心就把电脑搞坏”。不用担心。还记得我们在前面教程里,经常让 Trae 帮忙完成各种基础操作吗?这里也可以完全照搬这个思路——我们可以让 CLI 编程工具帮我们执行所有 CLI 操作:让它帮你进入指定文件夹、搜索和处理文件、运行或复制开源项目等。整个过程都可以通过和 CLI AI 编程工具的对话来完成。 + +## 和 AI IDE 有什么不同 + +我们可以把 CLI AI 编程工具类比成之前学过的 z.ai 和 Trae。某种意义上,CLI AI 编程工具可以看成是一种特殊的 z.ai:它们同样只需要一个简单的对话入口,就会自动为你执行所有需要的操作(只是有时你需要手动打开浏览器查看最终效果)。而如果类比 AI IDE,那么 CLI AI 编程工具可以被看作是 IDE 中的 Agent 模块——也就是侧边那块对话区域。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image4.png)![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image5.png) + +不过,由于不同 AI IDE 对 Agent 的实现方式不同,能力差异也很大,AI 编程效果经常不稳定,因此 CLI AI 编程工具通常由大型科技公司直接开发,例如 Claude 背后的 Anthropic、ChatGPT 背后的 OpenAI 等。 + +相比其他 AI 编程 Agent,直接使用这些大厂产品往往是较优的实践,尤其是 Claude Code 本身就是为 Anthropic 内部研发团队服务的工具,从一开始就围绕“满足工程师真实需求”来设计。 + +为了更直观地对比,我们可以简单看看 Claude Code 和某款 AI IDE Agent 的差异(这里以 Cursor 为例): + +| 功能特性 | Claude Code | Cursor | 更优者 | +| ------------------------ | -------------------- | ----------------- | ----------- | +| 自动任务执行 | ✅ 非常强 | ❌ 能力有限 | Claude Code | +| IDE 集成 | ❌ 仅命令行 | ✅ 原生 VS Code | Cursor | +| 实时代码补全 | ❌ 无 | ✅ 体验极佳 | Cursor | +| 多文件操作 | ✅ 非常强 | ⚠️ 还不错 | Claude Code | +| GitHub 一体化操作 | ✅ 可直接提交 | ⚠️ 需要手动操作 | Claude Code | +| 学习成本 | ⚠️ 中等 | ✅ 上手简单 | Cursor | +| 上下文长度 | ✅ 非常长 | ⚠️ 较好 | Claude Code | +| 调试辅助 | ✅ 自动化 | ⚠️ 较多需手动 | Claude Code | + +表格来源:https://northflank.com/blog/claude-code-vs-cursor-comparison + +简单说,CLI AI 编程工具通常可以: + +- 支持更长时间的连续对话(甚至可以帮你“工作一整天”)。 +- 提供更长的上下文窗口(不再频繁需要你说“继续”)。 +- 响应速度更快(可以接入更多自定义模型 API)。 + +在编码相关操作上,它们通常比大部分 IDE 内置 Agent 更聪明、更稳定。 + +## 常见的 CLI AI 编程工具 + +目前虽然有很多开源实现,但在实践中我们只推荐两大类型的 CLI AI 编程工具,作为“首选组合”。你可以根据自己的习惯任选其一,强烈建议都试一试,再选出最适合你的那一个。 + +- Codex 使用 GPT-5,在整体能力上更强; +- Claude Code 通过 GLM 4.6 转发 API,整体体验接近 Claude 4,但价格更便宜。 + +不过,哪一个在实际项目中更好用,只能通过亲自测试来判断。掌握多种 AI 编程工具始终是有益的:熟练以后,你可以在不同场景下灵活切换 Claude Code、Codex 或 Trae。如果尝试多次后发现某个工具效果一般,可以直接换一个工具或模型继续试验。 + +同时,由于模型版本更新非常迅速,建议你优先选择在“性价比(效果 / 成本)”上表现最好的方案。 + +### Claude Code + +Claude Code 是由 Anthropic 基于 Claude 大模型能力开发的一款 AI 编程工具。它的主要交互场景在终端,同时也支持作为 VS Code 插件来使用。类似于 AI IDE 中的 Agent,它可以深度理解开发者的代码仓库,并通过自然语言指令完成端到端的开发任务——包括代码编辑、修复 Bug、执行和修复测试、管理 Git 工作流(例如解决合并冲突、创建 PR)、复杂代码讲解、执行终端命令等。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image6.png) + +Claude Code 的优势主要体现在:极长的上下文窗口(可以处理完整文件甚至小型项目)、可以主动澄清模糊需求、自动规划和分配执行任务,以及对整个代码库内容的深度理解和解释能力。与普通 IDE Agent 相比,它更适合“沉浸式 vibe coding” 的开发流程。 + +在实际使用中,你可以通过对话指令,让它帮你创建新项目、执行 CLI 操作(例如整理文件夹、批量重命名文件、部署开源项目等)、配置开发环境(例如安装和调试 Python 环境)。如果觉得某段代码难以理解、某个目录结构不清晰,也可以直接让 Claude Code 生成结构化的分析文档,或者对特定内容进行分步骤讲解。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image7.png)![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image8.png) + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image9.png)![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image10.png) + +如果你想系统地学习 Claude Code,可以参考 Andrew Ng 与 Anthropic 联合推出的课程: +https://www.bilibili.com/video/BV176t2zSEpr + +接下来,我们将学习如何使用 Claude Code。由于直接使用官方 Claude Code 的成本往往非常高(如下图所示),我们会转而使用兼容 Claude Code 协议、但基于其他大模型的 API 平台。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image11.png) + +你需要学习下面几种不同方案(最好都尝试一遍),最后选择最适合你的那一种作为主要实践路径。 + +第一种方式是直接使用“兼容 Anthropic 接口”的 API。随着 Claude Code 的流行,越来越多的大模型服务商开始支持 Anthropic 风格的调用方式。常见的服务商包括 GLM、Kimi、DeepSeek 和 Siliconflow 等,它们都提供了兼容的 API 接口。关于具体配置,我们会在后文细讲。 + +需要注意的是,Claude Code 通常会消耗大量 token,如果你担心 API 调用产生过高费用,可以考虑购买 GLM 的月度套餐(大约 20 元/月)来控制成本。如果你想先感受一下实际花费,也可以先充值 10 元做小规模试验。 + +另一种方式是使用 “Claude Code Route” 项目。它是一个开源工具,不仅支持所有常见的 API 调用接口,还允许你针对不同场景精细配置要使用的模型,并且支持对接本地部署的大模型。但由于这一方案的配置相对复杂,建议你先从第一种方案入手。 + +#### 使用智谱 GLM 作为后端(推荐) + +GLM(General Language Model)是智谱 AI 自主研发的一系列大型语言模型。GLM-4.6 是当前 GLM 系列的最新版本,其核心亮点是在代码能力上的优异表现(在公开基准和真实任务中对标 Claude Sonnet 4,在国内处于第一梯队)。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image12.png) + +它还将上下文窗口扩展到 200K,可以更加从容地处理长文本和大体量代码,同时加强了推理与工具调用能力,在性能和成本之间取得了不错的平衡。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image13.png) + +在接入 GLM 之前,我们需要先安装 Claude Code。 + +如果你觉得命令行安装步骤麻烦,或者中途出现错误,可以直接让 Trae 的 Agent 帮你完成安装。 + +```Python +# 安装 Claude Code +npm install -g @anthropic-ai/claude-code + +# 进入你的项目 +cd your-awesome-project + +# 启动 Claude Code +claude + +# 按 Ctrl+C 退出 Claude +``` + +接下来,我们需要修改 Claude Code 的默认 API 请求地址,使其支持 GLM 的 API 服务。你可以直接复制下面的内容,让 Trae 帮你创建对应的环境变量;也可以选择把它们永久写入系统环境变量(如果出现问题,同样可以让 Agent 帮忙修改)。 + +首先,你需要先获取 GLM 的 API Key,并用你自己觉得最方便的方式保存好。 + +国内版地址:https://bigmodel.cn/usercenter/proj-mgmt/apikeys +国际版地址:https://z.ai/manage-apikey/apikey-list + +如果你使用的是 **国内版 GLM**,请使用以下变量配置: + +```Python +# 在 Cmd 中运行以下命令 +# 注意将 `your_zhipu_api_key` 替换为你刚刚获取到的 API Key +setx ANTHROPIC_AUTH_TOKEN your_zhipu_api_key +setx ANTHROPIC_BASE_URL https://open.bigmodel.cn/api/anthropic +``` + +如果你使用的是 **国际版 GLM**,请使用下面的配置: + +```Python +# 在 Cmd 中运行以下命令 +# 同样注意替换掉 `your_zai_api_key` +setx ANTHROPIC_AUTH_TOKEN your_zai_api_key +setx ANTHROPIC_BASE_URL https://api.z.ai/api/anthropic +``` + +你可以直接在 Trae 中输入类似下面的提示词: + +⚠️ 如果你是通过 Trae 帮你配置“永久环境变量”,那么配置完成后 **必须重启 Trae**,否则它内置终端里的环境变量不会更新,可能导致登录失败或网络连接错误。 + +```Python +Based on my environment variable settings: +setx ANTHROPIC_AUTH_TOKEN your_zai_api_key +setx ANTHROPIC_BASE_URL https://api.z.ai/api/anthropic + +and my key(Replace it with your own key): +681fea485851d29060cc.13gfaendggaFOhb + +please help me configure and start Claude Code +``` + +你会看到类似下面的过程输出: + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image14.png) + +> 💡 什么是环境变量? +> +> 环境变量本质上是一组存储在操作系统中的“键值对”配置信息,通常以 “变量名 = 具体值” 的形式存在。只要提前在终端或系统设置中配置好,程序就可以随时读取这些变量来获取相关信息。由于环境变量可以直接在终端中写入,而无需修改代码本身,我们通常会把访问大模型所需的密钥存放在环境变量里,以避免泄露。程序只需要读取对应环境变量,就能完成大模型调用。 +> +> 在 Windows 系统中,环境变量除了用于存储大模型的访问密钥,还常常用来保存命令行工具的“调用路径”。 +> +> 我们知道终端本身也是一个程序。有时我们希望在终端里启动某个外部程序,例如在终端中输入 `claude` 来启动 Claude Code。之所以可以直接输入 `claude` 就运行,是因为终端会读取系统的环境变量,其中的 PATH 变量里包含了 Claude Code 可执行文件所在的目录,所以终端能够找到并执行它(等价于在终端中粘贴那段程序的绝对路径再按回车)。 +> +> 一个典型的环境变量可能长这样:`PATH=C:\Windows\system32;C:\Program Files\Python`。这样我们就可以在任何路径下执行系统中的这些程序,例如直接在命令行键入 `python` 启动 Python 解释器。 +> +> 如果你想查看系统当前的环境变量,可以在 Windows 搜索中输入“环境变量”,在弹出的“编辑系统环境变量”窗口中就能看到所有变量及其值。有的变量用于存储大模型密钥,有的则用于添加程序目录,方便在任意路径下调用。 + +现在,你就可以使用最新的 GLM 来进行 Claude Code 开发了。你可以尝试重新跑一遍之前的项目,或者重新挑战那些 Trae 没有完成好的任务,对比看看体验上的差异。 + +🎉 反复“推倒重来”并不是浪费时间——你每重做一遍,技能都会更扎实一分。 + +用和 GLM 完全相同的思路,也可以轻松接入其他支持 Anthropic 兼容格式的接口。 + +#### 使用 Kimi K2 作为后端(推荐) + +https://platform.moonshot.cn/console/account + +```Plain +export ANTHROPIC_BASE_URL=https://api.moonshot.ai/anthropic +export ANTHROPIC_AUTH_TOKEN=sk-YOURKEY +``` + +#### 使用 DeepSeek 作为后端(推荐) + +https://platform.deepseek.com/usage + +```Bash +export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic +export ANTHROPIC_AUTH_TOKEN=YOU_DEEPSEEK_API_KEY +export API_TIMEOUT_MS=600000 +export ANTHROPIC_MODEL=deepseek-chat +export ANTHROPIC_SMALL_FAST_MODEL=deepseek-chat +export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 +``` + +#### 其他兼容 Anthropic 的 API + +Siliconflow: + +```Bash +export ANTHROPIC_BASE_URL="https://api.siliconflow.cn/" +export ANTHROPIC_MODEL="moonshotai/Kimi-K2-Instruct-0905" # 可以自行修改所需模型 +export ANTHROPIC_API_KEY="YOUR_SILICONCLOUD_API_KEY" # 请替换 API Key +``` + +阿里云 DashScope(Aliyuncs):https://help.aliyun.com/zh/model-studio/get-api-key + +```Python +export ANTHROPIC_BASE_URL="https://dashscope.aliyuncs.com/apps/anthropic" +export ANTHROPIC_API_KEY="YOUR_DASHSCOPE_API_KEY" +``` + +#### 使用 Claude Code Route 作为后端(进阶用法) + +上面我们讲解了如何用 GLM 官方 API 替换 Claude Code 的 Anthropic 接口。接下来,我们来看一下 Claude Code Router 这个工具是如何让 Claude Code 适配更多模型 API 的。 + +[Claude Code Router](https://github.com/musistudio/claude-code-router) 是一款专门为 Claude Code 设计的智能路由增强工具。它的核心作用,是帮助用户按需将 AI 请求分发到不同平台上的模型,并可以高度自定义。它支持接入几十个平台,包括 OpenRouter、DeepSeek、Ollama、Gemini 等,也可以按场景将任务路由到特定模型,比如 GLM-4.5、Kimi-K2、Qwen3-Coder 等。举例来说,你可以将后台任务自动交给本地 Ollama,以节省成本;将长文本 / 长代码任务交给 Gemini-2.5-Pro;把代码讲解交给 DeepSeek。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image16.png) + +该工具还提供了方便的 UI/CLI 配置管理能力,并通过“转换器(converter)”适配不同平台的 API 格式。它支持 GitHub Actions 等自动化集成以及自定义扩展,解决了“单一模型无法覆盖所有场景”以及“频繁切换平台很麻烦”的问题,帮助用户更灵活、低成本地利用 AI 工具。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image17.png) + +下面我们简单介绍如何安装 Claude Code Router。大致需要以下步骤(同样可以让 Trae 帮你执行),以准备好相关环境: + +```Markdown +npm install -g @anthropic-ai/claude-code +npm install -g @musistudio/claude-code-router +``` + +安装完成后,你需要确认本地可以使用 `ccr` 命令。如果看到类似下面的输出,说明安装成功: + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image18.png) + +接下来,有两种方式来初始化和配置模型: + +- 使用 CCR 自带的 UI,在浏览器中打开它提供的配置页面进行操作; +- 直接修改 CCR 的默认配置文件(本质上 UI 也是在修改配置文件,只是提供了更直观的界面)。 + +如果选择使用 CCR UI,你会看到类似下面的界面: + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image19.png) + +此时点击 “Add Provider” 按钮,就会看到如下界面。你需要: + +1. 在 Name 中输入模型提供商的名字; +2. 在 API Full URL 中填写该提供商的 OpenAI 兼容接口地址; +3. 在 API Key 中填写对应平台的 API Key; +4. 在 Models 区域中填写模型名称,点击 “Add Model” 添加; +5. 最后点击 “Save” 保存配置。 + +(界面往下滚动还有很多高级选项,但目前你可以先忽略它们。) + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image20.png) + +下面是 DeepSeek 与 Kimi 的配置示例: + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image21.png) + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image22.png) + +保存模型配置后,还需要在右侧 Router 区域中指定默认模型(Default)。点击对应的下拉选择,将其设置为 `kimi`(推荐),然后在右上角点击 `Save and Restart`。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image23.png) + +之后,只需在终端中输入 `ccr code`,即可通过 Claude Code Router 启动 Claude Code 的编码工作流。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image24.png) + +#### Claude Code 的进阶用法 + +很多人最开始使用 Claude Code 时,只把它当成普通对话工具来用。但实际上,它内置了很多丰富的能力,能够让你使用起来更高效、灵活。下面是一些常见命令和用法示例: + +参考文档: + +https://docs.claude.com/en/docs/claude-code/cli-reference +https://docs.claude.com/en/docs/claude-code/slash-commands + +| 命令 | 作用 | 示例 | +| ----------------- | ----------------------------------------- | ---------------------------------------- | +| claude | 启动交互模式 | `claude` | +| claude "query" | 执行一次性任务并输出结果 | `claude "explain this project"` | +| claude -p "query" | 执行一次性问题并在结束后自动退出 | `claude -p "explain this function xxxx"` | +| claude -c | 继续最近的一次会话 | `claude -c` | +| claude -r | 恢复上一段会话 | `claude -r` | +| /resume | 在当前聊天中切换回上一段会话 | `claude -c`、`/resume` | +| claude commit | 协助创建 Git 提交信息并提交代码 | `claude commit` | +| /init | 用 CLAUDE.md 初始化项目说明 | `/init` | +| /clear | 清空当前会话上下文,防止信息过载 | `/clear` | +| /compact | 压缩会话历史,减少上下文 token 占用 | `/compact` | +| /cost | 查看当前消费情况 | `/cost` | +| /model | 切换使用的模型(用兼容 API 时一般可忽略) | `/model` | +| /memory | 管理 CLAUDE.md 记忆文件 | | +| /help | 显示可用命令列表 | `/help` | +| exit or Ctrl+C | 退出 Claude Code | `exit` 或 `Ctrl+C` | +| /agents | 高级功能,后文会说明 | | +| /mcp | 高级功能,后文会说明 | | + +**CLAUDE.md** + +参考: https://www.anthropic.com/engineering/claude-code-best-practices + +`CLAUDE.md` 是 Claude 在开始对话时会自动读取并加入上下文的特殊文件。因此,它非常适合用来记录: + +- 常用 bash 命令 +- 核心文件和工具函数 +- 代码风格约定 +- 测试方式说明 +- 仓库协作规范(例如分支命名、是用 merge 还是 rebase 等) +- 开发环境配置说明(例如是否使用 pyenv、推荐哪种编译器等) +- 项目中需要特别注意的行为或坑点 +- 任何你希望 Claude “记住”的信息 + +`CLAUDE.md` 本身没有强制格式要求,只要简洁、便于人类阅读即可。例如: + +```Plain +# Bash commands +- npm run build: Build the project +- npm run typecheck: Run the typechecker + +# Code style +- Use ES modules (import/export) syntax, not CommonJS (require) +- Destructure imports when possible (eg. import { foo } from 'bar') + +# Workflow +- Be sure to typecheck when you’re done making a series of code changes +- Prefer running single tests, and not the whole test suite, for performance +``` + +#### Claude Code 的内部原理 + +参考: https://github.com/shareAI-lab/analysis_claude_code + +如果你好奇为什么 Claude Code 在很多场景下比 Trae 或 Cursor 等 Agent 编程工具更好用,我们可以简单看一下它的内部工作机制。 + +其他 CLI AI 编程工具的整体实现方式也大体类似。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image25.png) + +Claude Code 会把编程任务拆解成一个持续的“感知—思考—行动—验证”循环,并在其中调用不同工具完成任务。它模仿人类开发者的工作流:不断“写代码 → 运行 → 看结果 → 再改进”。系统内部通过一个主任务循环不断执行步骤,在每一轮循环中,Claude 都可以调用不同工具——例如读写文件、执行命令、搜索代码等——再根据工具返回的真实结果决定下一步行动。 + +其中有几个关键特性值得注意: + +- **流式处理(Stream Processing)**:Claude 可以一边思考一边输出结果,而不是必须等所有代码写完再执行。 +- **智能压缩(Intelligent Compression)**:长对话容易导致上下文过长,Claude 通过将历史压缩成关键信息来减少“遗忘”的概率,并通过区分长短期记忆保证高效运行。 +- **并发控制(Concurrency Control)**:内部并行设计可以让多个任务同时进行,互不干扰。 +- **子 Agent 管理(Sub-agent Management)**:实际工作中并不只相当于一个“角色”处理所有事情,你可以管理多个子 Agent 协作处理代码,每个 Agent 负责不同任务,比如专门负责测试、专门负责写文档等。 + +### Codex + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image26.png) + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image27.png) + +和 Claude Code 类似,Codex 是由 OpenAI 开发的一款 AI 协作编程工具,你可以把它理解成 “OpenAI 版的 Claude Code”。它最大的优势是对 GPT-5 的高效适配。 + +从实际体验来看,GPT-5 目前响应速度更快、犯错率更低(在多轮复杂任务中正确完成的概率更高)。它的一个缺点是解释往往偏“学术”和“技术”,有时显得过于严谨、信息量很大,对初学者来说可能略微难懂。 + +你可以通过下面的命令安装 Codex: + +```Plain +npm i -g @openai/codex +``` + +#### 使用 OpenAI 官方 API 作为后端 + +如果直接使用 OpenAI 官方的 Codex 入口,配置会非常简单:当你已经开通 OpenAI 订阅或申请到了相应 API 配额之后,只需要在命令行中输入 `codex` 启动程序,并按提示完成登录即可。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image28.png) + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image29.png) + +#### 使用转发 OpenAI API 的方式作为后端 + +由于官方 OPENAI API 可能存在价格较高、网络要求严格等问题,为了避免这些限制,我们也可以通过其他 API 网关服务来转发调用。 + +在这种方式下,我们只需要在第三方转发平台上购买对应的 Codex API 配额,就能获得接近原生 OpenAI Codex 的使用体验。 + +参考: https://open-dev.feishu.cn/wiki/PAqUwWG4IiuwTvkQ2sGcaQuPnXc +充值地址: https://api.zyai.online/account/topup/recharge + +需要注意的是,在拿到 token 配额后,我们还需要在本地配置好 API Key。 + +在密钥分组设置中,要注意选择专门用于 Codex 的那一项。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image30.png) + +接下来,我们需要把获取到的 Key 填入下面的提示词中,并把整段提示词交给 Trae,让它帮你完成整个配置过程: + +```Bash +My API key is: [Paste your obtained sk-xxxxx key here] + +Please help me complete the following configuration tasks: + +1. Create configuration directory + - Create a `.codex` folder under my user directory + - Windows path should be: `C:\Users\[My Username]\.codex` +2. Backup existing configuration (if exists) + - Check if `.codex\config.toml` exists + - If it exists, rename it to `config.toml.bak.[current timestamp]` (timestamp format: yyyyMMddHHmmss) +3. Create configuration file + - Create `config.toml` in the `.codex` directory + - Write the following complete content: + ```toml + preferred_auth_method = "apikey" + + [model_providers.myrelay] + name = "My Relay Station" + base_url = "https://api.zyai.online/v1" + env_key = "MYRELAY_API_KEY" + wire_api = "responses" + request_max_retries = 4 + stream_max_retries = 10 + stream_idle_timeout_ms = 300000 + + [profiles.myrelay] + model_provider = "myrelay" + model = "gpt-5" + model_reasoning_effort = "medium" + + [tools] + web_search = true + +4. Set system environment variable +Variable name: MYRELAY_API_KEY +Variable value: The key I gave you + +5. Confirm completion and report back: + +The full path of the configuration file +Whether the environment variable was set successfully +I can use the command `codex --profile myrelay` to run it +``` + +配置完成后,你就可以通过 `codex --profile myrelay` 启动使用转发 API 的 Codex 了。之后的使用方式与 Claude Code 类似:只需要在对话框中随时输入你的想法和需求即可。 + +## CLI AI 编程工具的更多用法 + +### 用 AI 写需求文档:学会“具体化需求” + +对于大语言模型来说,抽象需求需要被“具体化”。比如:“我很饿”是一个抽象需求,我们需要把它变成:“我肚子有点饿,可能需要吃一个红豆面包,再配一杯豆浆。”——这才是一种可以被执行的、具体的需求。 + +但把抽象需求变具体,其实是一个很花精力的过程。如果我们没有见过足够多的案例,很难快速联想到该如何把抽象问题拆解成细致的模块。这种时候,最好的办法就是让 AI 帮你完成“具体化”这一步。 +比如,我想开发一个“每日计划”应用,最朴素的想法可能是: + +`Please help me write a daily planning app where I can write my plans each day and get reminders.` + +AI 确实可以在这个需求基础上直接拆分任务,然后一步步完成,但中间容易出错或理解偏差。为了降低风险,我们可以让 AI 帮我们先扩写需求: + +`Based on my needs, please elaborate and provide a more detailed Product Requirement Document for reference. My idea is: Please help me write a daily planner app that supports daily plan-writing and provides reminders .` + +这时,AI 可能会给出类似下面这样完整的 PRD: + +```Plain +Product Requirements Document (PRD): “Today’s Plan” App +Document Version: 1.0 +Creation Date: October 27, 2023 +Author: (Your Name/Product Manager) + +1. Product Introduction +1.1 Product Name +Today's Plan +1.2 Product Vision +“Today’s Plan” is a minimalist and highly efficient daily planning and reminder tool, dedicated to helping users eliminate procrastination and forgetfulness, plan every day clearly, and ensure tasks are carried out through an intelligent reminder system—ultimately enabling users to gain a stronger sense of control and achievement over their time. +1.3 Target Users (User Personas) +We mainly serve three types of users: +Students (Xiao Ming): +Characteristics: Multiple tasks such as courses, assignments, club activities, exam prep, needing organized time arrangement. +Pain Points: Easily forget small tasks or assignment deadlines; feel overwhelmed switching between tasks; want to build regular study and life habits. +Needs: A simple tool to list daily to-dos and provide reminders before class/self-study. +Office Workers (Zhang Wei): +Characteristics: Fast-paced work, many meetings, reports, project milestones, and personal affairs (fitness, picking up children). +Pain Points: Easily forget important meetings or work milestones; get interrupted by urgent tasks and forget the original plan; feel busy but inefficient at end of day. +Needs: Need a tool to quickly record and schedule daily work and send strong reminders at key times (e.g., 15 minutes before meetings). +Freelancers/Self-disciplined Seekers (Li Na): +Characteristics: High freedom of time, but strong self-management required for work output and personal growth. +Pain Points: Easily procrastinate, lack external supervision; start the day without a clear plan, leading to low time utilization. +Needs: Need a tool to help build a daily fixed routine (Morning Routine) and review daily achievements for positive feedback. + +2. User Stories +As a user, I want to quickly create today’s plan list so I have an overview of all my tasks for the day. +As a user, I want to set specific start and end times for each task so I can create a visual timeline. +As a user, I want to receive push notification reminders before a task starts so I won’t miss any important arrangements. +As a user, I want to customize the reminder time (such as 5, 15, or 60 minutes in advance) so reminders better fit my habits. +As a user, I want to easily mark completed tasks so I can feel accomplished and clearly see my progress. +As a user, I want to see a summary of my completed plans at the end of each day for reviewing and self-motivation. +As a user, I want to conveniently edit and delete tasks to handle last-minute changes. +As a user, I want to view plans and achievements from previous days to review my efficiency and habits. + +3. Feature Breakdown +Core Features (MVP - Minimum Viable Product) +Module 1: Plan Management +3.1.1 Daily Plan Homepage +Interface: “Today” as the core view, current date shown at the top. +View: Timeline list, clearly showing tasks scheduled from morning to evening. Tasks without a time can be listed in the top or bottom “To-do List” section. +Interactions: +Click the “+” button in the bottom right to quickly create a new task. +Pull down to refresh the page. +Swipe left/right to view yesterday’s and tomorrow’s plans. +3.1.2 Create/Edit Task +Entry: Click “+” on the homepage or a time slot in the list. +Fields: +Task title (required): Briefly describe the task, e.g., “10 AM Weekly Product Meeting.” +Task time (optional): +Set “start time” and “end time.” +Provide “all-day” option for unspecified time tasks. +Default time picker should be quick and convenient. +Reminder setting (required, with default value): See Module 2. +Notes (optional): Add further descriptions, links, or location info. +Actions: Save, cancel, delete task. +3.1.3 Task Interaction +Mark as complete: Checkbox before each task; checking adds a strikethrough and gray background, indicating completion. Can unmark if needed. +Edit task: Click the task itself to enter edit page. +Delete task: Swipe left on a task to reveal “Delete” button. +Module 2: Smart Reminder System +3.2.1 Reminder Trigger +Mechanism: Based on task’s set “start time” and the user’s “reminder lead time,” send a push notification from device. +Offline Support: Locally scheduled reminders must trigger even if user is offline. +3.2.2 Reminder Content & Format +Notification title: App name “Today’s Plan.” +Body: “Reminder: [Task Title] will start at [Start Time].” E.g., “Reminder: Product Meeting will start at 10:00.” +Sound: Use system default or offer several simple, effective tones. +3.2.3 Reminder Settings +Global Settings (in Settings page): +User can set a default reminder time, e.g., “15 minutes before task starts.” New tasks adopt this by default. +Single Task Settings (in create/edit page): +Users can override global settings for important tasks, choosing specific reminder times like "on time," "5 minutes early," "30 minutes early," or "1 hour early." +Provide “no reminder” option. +Subsequent Features (V1.1, V2.0) +3.3 Daily Review & Statistics +Push a summary notification at a set time every night (e.g., 22:00): “How was your day? Take a look at your achievements!” +Generate a simple daily report card: shows total planned tasks, completed tasks, completion rate, plus an encouraging message. +3.4 History Review +Calendar view to click on any past day and check its plans and completion status. Days with high completion rates marked with a special color. +3.5 Templates +Allow users to save a successful daily plan as a template, e.g., “Efficient Workday,” “Relaxing Weekend.” +When creating tomorrow’s plan, one-click import a template, modify slightly to save time. +3.6 Themes & Personalization +Offer dark mode. +Allow changing several primary color themes. + +4. Non-Functional Requirements +4.1 Performance +Response: App launch time under 2 seconds; adding/editing tasks must be smooth and lag-free. +Resource Use: Low battery and memory consumption in background; do not over-consume resources waiting for reminders. +4.2 Usability +Minimal & intuitive: UI must be minimal, primary functions accessible within 3 clicks. No tutorial needed for new users. +Error tolerance: Offer undo (e.g. brief undo after mistakenly deleting a task). +4.3 Reliability +Reliable reminders: Reminder function is the product’s lifeline; must guarantee 99.99% timely and accurate delivery. +Data loss-free: User plans must be reliably stored locally. Future versions can support cloud sync to prevent data loss on device change. +4.4 Compatibility +Platform: Support major iOS and Android versions (latest 3-4 releases). +Screen: Layout must fit various phone screen sizes. + +5. Roadmap +V1.0 (MVP): +Goal: Validate core value—planning & reminders. +Features: Complete all “Core Features” described above (Plan management, smart reminders). +V1.1 (Quick Optimization): +Goal: Improve retention and achievement. +Features: Add “Daily Review & Statistics,” “History Review.” +V2.0 (Enhanced Experience): +Goal: Increase efficiency and personalization. +Features: Add “Templates,” “Themes & Personalization,” and start developing “Cloud Sync.” +``` + +对比我们最开始那句“帮我写一个每天可以记计划并提醒的应用”,现在这份文档已经详细得多了。你可以根据自己的真实需求,对其中的内容进行增删修改;对于某些你不确定的模块,也可以继续让 AI 提供更多备选方案,你再挑选、合并成最终版本。 + +通过这种方式,我们可以很轻松地把抽象想法变成具体描述。对 AI 开发来说,“具体”就是生产力:需求越具体,越容易得到结构稳定、质量较高的项目。你可以尝试用这种方式重做一下之前的某个小项目,对比一下效果差异。 + +如果你觉得这类“需求提示词”太长,非常自然的做法,是把它单独写进一个 markdown 文档中,作为你的“需求文档 / 开发文档 / PRD”。之后每次让 AI 写项目时,只需要让它“参考这份文档”,而不是每次都重打一遍长提示。你也可以在迭代中不断完善这份文档,让后续项目直接受益。 + +下面是一些其他常见的使用场景: + +### 管理文件夹 + +我们可以尝试用 CLI AI 编程工具来管理当前文件夹中的各种文件。比如,你有一堆杂乱无章的文件,需要整理归类,就可以对 Claude Code 或 Codex 说: + +`Please help me organize the contents of the current folder. I want to group files with the same content together & I want to group files from the same time period together. Please help me handle this.` + +### 开发新项目 + +这和我们之前在 z.ai、Trae 中的用法几乎完全一样——我们也可以直接用 CLI AI 编程工具来从零开发新项目。当然,最好提前准备好一份需求文档。 + +需求文档越细致,最终效果越好。你可以根据不断变化的想法,对文档做多轮优化;文档越完善,代码实现就越稳定、越成熟。 + +### 部署开源项目(例如 Dify) + +对于刚接触计算机的同学来说,从 GitHub 上部署一个开源项目往往很有难度。但我们完全可以把这件事交给 Claude Code,就像我们在 Dify 教程中做的那样: + +https://github.com/langgenius/dify + +如果我想在本地跑起自己的 Dify,只需要把这个链接扔给 Claude Code,然后输入: + +`I want to deploy this GitHub project ``https://github.com/langgenius/dify`` . Please help me clone the project and run it.` + +收到你的请求后,Claude Code 会自动完成一系列操作,包括从 GitHub 拉取代码、配置运行环境、启动项目等。如果中间某一步出错或项目启动状态不正常,你再根据提示进行少量人工处理即可。除了 Dify,你也可以用 Claude Code 帮你部署大部分常见的 GitHub 开源项目——你只需要一个对话框,再加上喝一杯咖啡的时间 ☕️。 + +![](../../../English/Extra/Extra%20Knowledge%209%20-%20CLI%20AI%20Coding%20Tools%20and%20the%20Principles%20of%20Test-Driven%20Development/images/image31.png) + +### 讲解代码与撰写文档 + +对于一些复杂项目,或者 AI 自动生成的大型项目,你可能会觉得代码太长、逻辑太多,很难看懂。这时就可以让 CLI AI 编程工具帮你“读代码”。你可以这样提问: + +- 请帮我解释这个项目:如何运行、如何使用、后续如何修改和继续开发? +- 请帮我说明这个项目的整体流程:程序是怎样运行的?用户在界面中可以做哪些操作? +- 请帮我为这个项目写一份完整的文档,包括开发文档和运行文档等。 +- 请基于我当前文件夹里的所有内容,写一份详细说明,并保存到指定的 markdown 文档中。 + +### 更多玩法 + +当然,CLI AI 编程工具能做的远不止上面这些。不要只把它当作“写代码工具”,而是把它看作一个具有独立行动能力的智能 Agent。你可以让它帮你: + +- 管理和整理本地文件; +- 写日记、写总结; +- 分析和修复系统错误; +- 执行各种重复性命令行任务等。 + +也许在不久的将来,它会变成你电脑上最重要、也最懂你的 AI 伙伴。 diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..aeae436 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,136 @@ + + + + + Vibe Coding 101 + + + + + +
+ + + + + + + + + + + + + + + + + diff --git a/docs/project/chapter0-learning-map/chapter0-learning-map.md b/docs/project/chapter0-learning-map/chapter0-learning-map.md new file mode 100644 index 0000000..82098ca --- /dev/null +++ b/docs/project/chapter0-learning-map/chapter0-learning-map.md @@ -0,0 +1,27 @@ +# 从创意到 AI 产品 + +我们目前正经历着由大语言模型(LLMs)驱动的 AI 技术应用的爆炸式浪潮。与过去 AI 开发严重依赖算法研究不同,现在行业的重点已转移到有效利用现有的强大 AI 模型来创建有价值的应用程序。这一转变显着降低了 AI 开发的门槛,将重心从“从头构建模型”转移到了“将 AI 能力封装成现实世界的解决方案”。 + +对于大多数初学者和开发者来说,今天最大的机会不在于发明新算法,而在于学习如何有效地调用、编排和工程化 AI 功能,以满足特定的市场和用户需求。 + +尽管有了这种转变,许多学习者发现,掌握与 AI 交互或生成内容的技能只是一个起点。一个普遍存在的挑战仍然存在:如何实现从“AI 用户”到“AI 应用创造者”的跨越。关键问题包括:如何将分散的 AI 功能集成到自动化工作流中?如何为 AI 应用程序构建健壮可靠的前端和后端,并将它们在线部署?如何跨越从开发到实际运营的关键“最后一公里”? + +本课程正是为了解决这些关键障碍而设计的。它不拘泥于抽象理论,而是致力于 AI 原生应用开发的端到端实践培训。 + +# 为什么要学这个?它将如何帮助我的未来? + +本课程侧重于 AI 原生应用的实际开发和创新思维的培养,专为初级水平的学生量身定制。通过理论指导和动手实践相结合,学生将逐步掌握 AI 应用开发必不可少的核心方法和创新途径: + +* **基本掌握多模态 AI 模型的应用方法和适用场景:** + * 能够使用 vibe coding 工具调用主流多模态理解和生成模型 API(涵盖文本、图像、视频、音频等)。 + * 能够分析不同模型的能力边界,并根据特定应用场景适当地选择模型。 +* **能够利用和分析前沿 AI 产品:** + * 能够研究和分析行业内各种最先进的 AI 产品(基于文本、图像、视频),学习分析产品构建背后的技术原理,并了解竞争对手使用的常见实现方法。 +* **基本理解和实践不同的 AI 应用框架和核心工作流:** + * **知识库与 AI 工作流:** 了解知识库构建的基础知识,并开发使用它们的基础和中级技能,以及设计、实施和优化 AI 工作流的能力。 + * **智能体 (Agent):** 了解智能体的原理,并获得智能体开发和应用的初步实践经验。 + * **图像生成和编辑:** 掌握图像生成和编辑工作流中的入门级和高级技术。 +* **AI 原生应用开发和运营的实践经验:** + * 学习使用先进的 AI 编码工具;了解前端和后端数据库的作用;并能够独立开发和部署 AI 应用程序(APP / Web 应用程序)。 + * 了解用户管理和支付等核心功能模块的实现方法。 + * 获得用户研究、产品推广和应用分享的基本技能;同时,通过与企业合作或独立运营,开始探索 AI 应用的价值创造闭环。 diff --git a/docs/project/chapter1/chapter1-how-to-build-a-snake-game.md b/docs/project/chapter1/chapter1-how-to-build-a-snake-game.md new file mode 100644 index 0000000..54fe3bd --- /dev/null +++ b/docs/project/chapter1/chapter1-how-to-build-a-snake-game.md @@ -0,0 +1,651 @@ +# Project 1: 如何构建贪吃蛇游戏 + +这是一个**基于项目的学习**教程。我们鼓励你跟随步骤一步步操作,并尝试复现结果。不要担心犯错或修改内容——**最重要的是:** 🎉 **完成比完美更重要** + +在软件工程中,**迭代是正常且有价值的**。你不需要一次性创造出一个完整的产品——**从小处着手,边做边改。** + +在本教程中,我们将学习如何使用 **vibe coding** 技巧来创建一个现代版的贪吃蛇游戏。我们将从贪吃蛇游戏的基本机制开始,然后对其进行修改,让蛇吃掉字符而不是点。最后,游戏将根据这些字符生成一首诗,并绘制一幅受这首诗启发的图画。 + +> 💡 什么是 Vibe Coding?计算机科学家 [Andrej Karpathy](https://karpathy.ai/)(OpenAI 的联合创始人之一,特斯拉前 AI 负责人)于 2025 年 2 月提出了 **vibe coding** 一词。这个概念指的是一种依赖于 LLM 的编码方法,允许程序员通过提供自然语言描述而不是手动编写代码来生成可工作的代码。 +> +> ![](images/image1.png) +> +> 点击这里查看更多关于 vibe coding 的细节: +> +> [https://www.ibm.com/think/topics/vibe-coding](https://www.ibm.com/think/topics/vibe-coding) + +# 你将学到 + +* 使用提示词(prompts)构建一个简单的游戏。 +* 如果你看到错误,告诉 AI 并让它帮你修复。 +* 为你的游戏添加文本和图像生成功能,使其更有趣。 + +# 1. 准备工作 + +## 1.1 我们应该使用哪些工具? + +我们将使用一个非常简单的工具来构建一个最小化的游戏。你不需要知道如何调用大语言模型或图像生成模型。 + +这个工具叫做 [z.ai](https://chat.z.ai/),由智谱 AI(中国领先的 LLM 公司之一)开发。它支持多种功能,如 AI 驱动的幻灯片生成、海报设计和全栈开发。在本教程中,我们将重点关注其全栈开发模块。 + +![](images/image2.png) + +[z.ai](https://chat.z.ai/) 中的全栈开发模块支持网页的实时编辑和预览。 + +![](images/image3.png) + +通过点击全栈开发示例,你可以看到网页创建的整个过程。 + +![](images/image4.png) + +等你的咖啡泡好了,你的结果也就出来了! + +![](images/image5.png) + +你可以上下滚动浏览此网页,或点击顶部的 🧭 按钮以全屏模式查看页面。 + +![](images/image6.png) + +如果你想查看此网页的源代码,可以点击右上角的图标。 + +![](images/image7.png) + +你将能够看到所有代码。 + +![](images/image8.png) + +## 1.2 我们需要前端开发知识吗? + +我们在开始时不需要掌握前端甚至后端开发技能。我们只需要学习如何与大语言模型聊天,如何根据当前的运行结果向 LLM 提出新需求,以及当代码运行失败时如何向 LLM 提供准确的错误信息。 + +但我们建议你学习一些前端和后端开发的基础知识,因为这将帮助你让 LLM 创建更好的程序。 + +别担心,你只需要在学习过程中逐渐掌握这些知识,不需要一开始就是专家。 + +> 💡 更多关于前端开发的信息 +> +> 前端开发通常意味着使用 **HTML**、**CSS** 和 **JavaScript** 来创建网站或应用程序的用户界面。然而,在本教程中,我们将跳过前端编码的复杂性,因为我们使用的工具会自动为我们生成并运行界面。 +> +> 但了解幕后发生的事情是有帮助的。传统上,构建 Web 界面涉及编写用于结构的 **HTML**、用于样式的 **CSS** 和用于交互性的 **JavaScript**。 +> +> 例如,这是一个非常简单的网页,但它结合了三种不同类型的代码: +> +> ![](images/image9.png) +> +> * 一个简单的 HTML 按钮: +> +> ```HTML +> +> ``` +> +> * 基本的 CSS 让按钮变蓝: +> +> ```CSS +> button { +> background-color: #3498db; +> color: white; +> border: none; +> padding: 10px 20px; +> border-radius: 4px; +> } +> ``` +> +> * 一点 JavaScript 来在点击按钮时显示提示: +> +> ```JavaScript +> document.querySelector('button').onclick = function() { +> alert('Button was clicked!'); +> } +> ``` +> +> 当你尝试点击这个按钮时,你会在浏览器中看到一个提示信息: +> +> ![](images/image10.png) +> +> 此外,我们可以尝试理解这三种代码的深层含义: +> +> **什么是 HTML?** +> +> HTML,全称 **超文本标记语言 (HyperText Markup Language)**,是网页的骨架。它的工作是定义页面的结构和内容,如标题、段落、图像、链接,以及示例中看到的按钮本身。 +> +> * **HTML 决定页面上有什么**:它告诉浏览器,“这是一个按钮”,“这是一段文本”,或者“在这里显示一张图片”。 +> * **它使用“标签”来组织内容**:`` 是一个 HTML 标签,定义了一个按钮,并在其中包含了“Click Me”这个文本。 +> +> **什么是 CSS?** +> +> CSS,或层叠样式表 (Cascading Style Sheets),负责网页的“外观和感觉”。它用于设计和美化 HTML 元素,控制颜色、字体、布局和间距等。 +> +> * **CSS 决定页面看起来怎么样**:在我们的示例中,CSS 代码将标准 HTML 按钮变成了一个带有蓝色背景、白色文本、圆角和一些内边距的按钮。 +> * **它将内容与表现分离**:这是一个关键概念。你可以有一个用于内容的 HTML 文件,并使用一个单独的 CSS 文件来设置它的样式。这使得维护和更新整个网站的设计变得更加容易。 +> +> **什么是 JavaScript?** +> +> JavaScript 是一种编程语言,它为网页添加交互性和动态行为。如果说 HTML 是骨架,CSS 是皮肤,那么 JavaScript 就是大脑和肌肉,让页面“活起来”。 +> +> * **JavaScript 决定页面的行为**:在示例中,JavaScript 代码使得点击按钮时弹出提示框。它定义了响应用户输入时应该发生的操作。 +> * **它可以创建复杂的功能**:除了简单的提示,JavaScript 还用于处理表单提交、创建动画、在不重新加载页面的情况下加载新数据、构建像游戏这样的复杂应用程序等等。 +> +> 随着项目变得越来越大,交互性越来越强,仅使用纯 HTML、CSS 和 JavaScript 管理代码会变得复杂且难以维护。这就是现代前端库如 **React** 发挥作用的地方。 +> +> ![](images/image11.png) +> +> **[React](https://react.dev/)** 是一个用于构建用户界面的流行 JavaScript 库。它帮助开发者将代码组织成可重用的组件,使得构建和维护复杂的应用程序变得更加容易。 +> +> React 允许你编写管理自己逻辑和外观的组件,然后将它们组合起来构建更大的界面。 +> +> 随着我们的深入,我们将探索更多关于 React 如何工作以及它如何适应我们的工作流程的内容。现在,只需要知道,虽然前端开发通常意味着编写像这样的代码,但我们的工具会自动处理大部分工作——我们只需要知道如何向大语言模型清楚地表达我们的需求! + +# 2. 构建你的第一个游戏 + +## 2.1 与 LLM 对话时给出清晰的指令 + +在一开始,我们可以用最简单的方式与大模型对话,这将帮助我们快速获得产品原型。我们可以直接在聊天框中输入: + +> **💡 示例提示词:** 帮我做一个贪吃蛇游戏 +> +> ![](images/image12.png) + +> **💡 示例提示词:** 帮我做一个贪吃蛇游戏,它应该支持 +> +> 1. 我可以吃不同的单词,它们会被收集在一个盒子里 +> ![](images/image13.png) + +> **💡 示例提示词:** 帮我做一个贪吃蛇游戏,它应该支持: +> +> 1. 我可以吃不同的单词,它们会被收集在一个盒子里 +> 2. 当蛇吃了8个单词时,llm 应该根据这些单词创作一首诗,我们可以根据需要重新混合这首诗。 +> 3. 当诗完成后,下一步将自动根据这首诗创建一幅图像。 +> +> ![](images/image14.png) + +## 2.2 尝试修复过程中出现的错误 + +在开发过程中,我们可能会遇到不尽如人意的问题,例如点击按钮没有任何反应、使用功能时报错、功能未按预期工作,或者前端页面与预期设计不符。 + +在这种情况下,我们需要进一步向模型提问,以帮助修复这些意外问题。 + +![](images/image15.png) + +## 2.3 如何假装自己是 Vibe Coding 大师 + +实际上,在真正的 vibe coding 过程中,我们通常不会使用很多复杂的提示词。也许我们在开始时需要为整个程序提供一个具体且适度复杂的提示词,但在那之后的每一步,你可能只需要以下类型的提示词: + +```JSON +"代码里有个 bug,请修复它。" +"我不要部分代码,给我完整的修改后的代码。" +"你的代码还是有问题。" +"请再次修改并给我完整的修正后的代码。" +"刚才还能运行,为什么现在不能运行了?" +"你没理解我的意思吗?不要改我原来的代码。" +"不要添加任何调试功能。" +"不要做我没让你做的事。" +"我让你实现的功能在哪里?" +"你听不懂我说的话吗?" +"我只要一个函数。" +"我告诉过你参考我之前的代码。" +"请不要添加不必要的注释。" +"请不要修改我原始代码的基本逻辑。" +"帮我修改代码。" +"基于我的代码修改..." +"不要改我的变量名!!!" +"不要改原来的函数名!" +"不要乱动我的变量。" +"不要添加额外的功能。" +"不要只生成框架,生成完整的代码。" +``` + +这听起来可能有点夸张,但实际上,这些就是我们在日常工作中可能使用的提示词。由于大语言模型的上下文长度限制,或者有时因为它们的指令遵循能力不是很强,模型可能会忘记对话早些时候讨论的内容。 + +或者,由于训练数据集的风格,大模型倾向于以其训练数据的风格回答。例如,有些人说话很严肃,有些人喜欢添加很多修饰,而有些大模型喜欢在代码中添加很多注释或不必要的模块。 + +这就是为什么我们需要在开始时明确设定界限,例如:不要添加新模块,不要包含太多注释。每个大模型都有自己的风格,我们只能通过实际使用找到我们最喜欢的那个。 + +> 💡 什么是模型上下文? +> +> 模型上下文就像 AI 的 **短期记忆**。它是 AI 记住的当前对话中的所有文本。这使你能够提出后续问题并进行自然的对话,因为 AI “记得”你刚才在谈论什么。没有上下文,你问的每个问题都将是一个全新的、独立的对话。 +> +> 每个模型都有不同的有效上下文长度,通常从 **32k 到 128k** tokens 不等。如果你想让大语言模型一次性阅读一篇很长的文章,或者有许多材料和对话希望 LLM 参考,你可能会发现 LLM 经常忘记长文本中的一些重要内容,或者你可能会注意到对话过程中主题逐渐偏移,这是由上下文限制引起的现象。 +> +> 因此,对于模型,我们也关注上下文。然而,值得注意的是,上下文越长,资源消耗越大,收取的费用也越高。在行业中,有许多压缩上下文的方法,我们将在随后的学习中一一介绍。 + +> 💡 什么是指令遵循能力? +> +> 指令遵循能力指的是 **AI 理解并准确执行你提供的命令的能力**。它不仅仅是回答问题,而是根据你的具体要求完成任务,例如“将这篇文章总结为三个要点”、“用正式的语气写回复”或“翻译这个词并在句子中使用它”。 +> +> 具有强指令遵循能力的模型将完全按照你的指示完成这些操作,而不会执行任何不必要的额外操作。 +> +> 例如,当我们希望 LLM 将一篇文章总结为三个关键点时,我们不希望它给我们五个;当我们希望它从文章中提取某些关键要素(如作者、时间及发生的事件)时,我们不希望它遗漏任何要素。 +> +> 因此,我们希望 LLM 拥有足够强的指令遵循能力,因为这带来了稳定性及 **可复现性**,使它们成为工业应用中的重要组成部分。 + +# 3. **使用 API:调用 LLM 和图像生成器** + +## 3.1 什么是 API + +首先,**你需要知道什么是 API** `Extra Knowledge 2 - What is API` + +我们将尝试集成两个 API:一个是调用 DeepSeek LLM,另一个是调用 Seedream (即梦) 模型。这两个模型都很棒,性能出色。 + +在使用 API 的过程中,只有两个最重要的元素: + +1. API key (密钥) +2. 官方文档示例 + +只要你能找到这两个,你就可以让 LLM 帮你修改并实现所有类型的 API 调用。 + +## 3.2 将 DeepSeek API 集成到 z.ai 中 + +### 什么是 DeepSeek + +![](images/image16.png) + +> 📚 信息引用自 [DeepSeek Wiki](https://en.wikipedia.org/wiki/DeepSeek) +> +> **杭州深度求索人工智能基础技术研究有限公司**(**Hangzhou DeepSeek Artificial Intelligence Basic Technology Research Co., Ltd.**),以 **DeepSeek** 为商号,是一家开发大语言模型(LLMs)的中国人工智能(AI)公司。DeepSeek 总部位于浙江杭州,由中国对冲基金幻方量化(High-Flyer)拥有并资助。DeepSeek 由幻方量化的联合创始人梁文锋于 2023 年 7 月创立,他也同时担任这两家公司的 CEO。该公司于 2025 年 1 月推出了同名聊天机器人及其 DeepSeek-R1 模型。 +> +> 让我们看看 DeepSeek 在 GPQA 基准排名中与其他顶级模型的表现对比。值得注意的是,DeepSeek 是一个开源(每个人都可以从互联网下载模型)模型,而其他常见模型如 Grok、Google Gemini 和 ChatGPT 都是闭源的。正如我们所见,DeepSeek 已经很大程度上接近了第一梯队的模型。 +> +> ![](images/image17.png) +> +> GPQA 是“研究生级 Google-Proof 问答基准”的缩写,这是一个用于科学问答任务的研究生级基准。以下是详细介绍。 +> +> GPQA 包含 448 个多项选择题,涵盖生物学、物理学和化学的子领域,如量子力学、有机化学、分子生物学等。这些问题由 61 位持有博士学位或正在攻读博士学位的专家编写,并经过了严格的验证过程。 + +### 如何获取 deepseek API + +我们将尝试根据我们已有的信息,让 z.ai 直接将 DeepSeek API 集成到项目中。 + +首先,我们需要在 DeepSeek 开放平台注册一个账户。 + +https://platform.deepseek.com/sign_up + +然后,你会看到像这样的网页界面: + +![](images/image18.png) + +要使用 API,我们需要先充值 token。10 元人民币足够使用一段时间了! + +![](images/image19.png) + +点击“API KEYS”并在屏幕下方找到“create new API key”。你最终会得到一个像 `sk-8573341c39fc44315aadc071c53rh7d2` 这样的 API key。 + +![](images/image20.png) + +一旦你获得了密钥,你就拥有了调用模型的权限。 + +此时,你可以直接阅读 [API 文档](https://api-docs.deepseek.com/),它通常提供 curl 或 Python 的调用示例。 + +![](images/image21.png) + +找到示例后,你可以将文档中与密钥相关的所有内容复制到 z.ai,并要求它尝试帮你集成 LLM。 + +![](images/image22.png) + +![](images/image23.png) + +自动集成可以在很短的时间内完成。我们可以询问它的操作员确认 DeepSeek API 是否已经在使用中。 + +![](images/image24.png) + +或者,我们可以要求 z.ai 帮我们定位项目中调用 LLM 的部分。 + +然后我们可以独立确认是否正在使用 DeepSeek。具体来说,我们可以直接请求:`"告诉我项目中所有需要调用 LLM 的代码位置,我需要检查是否是 DeepSeek。"`,z.ai 将返回所有 API 调用的详细地址。 + +![](images/image25.png) + +接下来,我们将简要介绍目前可用的三种最先进的图像生成模型。你可以根据自己的喜好选择一种集成到 z.ai 中。 + +## 3.3 将 SiliconFlow QwenImage API 集成到 z.ai 中 + +### 什么是 SiliconFlow + +> [Silicon Flow (硅基流动)](https://cloud.siliconflow.com/me/models) 成立于 2023 年 8 月,是一家世界领先的 AI 能力提供商。它提供 SiliconCloud(具有自研推理加速的大模型云平台)和 BizyAir(用于 AI 图像生成的 ComfyUI 插件)等核心产品,为客户提供 AI 基础设施能力,拥有战略合作伙伴关系,并持有顶级行业认证。 +> +> ![](images/image26.png) + +### 什么是 QwenImage + +> Qwen-Image 是一个强大的图像生成基础模型,能够进行复杂的文本渲染和精确的图像编辑。这是一个 20B MMDiT 图像基础模型,在复杂的文本渲染和精确的图像编辑方面取得了重大进展。实验表明,它在图像生成和编辑方面都具有很强的通用能力,在文本渲染方面表现尤为出色,尤其是中文。 +> +> 从中文到英文,QwenImage 可以像 GPT-4o 或 Seedream 模型一样生成高质量的文本。 +> +> ![](images/image27.png) +> +> ![](images/image28.png) +> +> ![](images/image29.png) +> +> ![](images/image30.png) + +### 如何获取 SiliconFlow QwenImage API + +https://cloud.siliconflow.com/me/models + +查看 SiliconFlow 的官网。左侧有一个“Playground”部分,你可以在不进行 API 调用的情况下试用不同的模型。在网页顶部有一个“Filters”按钮;点击它可以筛选右侧的模型列表。 + +如果你选择“Image”,你将只看到当前支持的所有文生图模型。在这种情况下,我们将使用 Qwen/Qwen-Image。 + +![](images/image31.png) + +要调用 API,首先我们需要点击左侧设置中的“API Keys”,然后点击“Create API Key”按钮生成一个 API key。记得保存这个 API key。 + +![](images/image32.png) + +要查看可用余额,我们需要打开左侧设置中的“Payments”。在这里,你可以看到 1 美元的赠金。但是,如果你想使用 FLUX 文生图模型,你需要先充值账户。 + +https://cloud.siliconflow.com/me/account/ak + +![](images/image33.png) + +一切设置好后,我们需要参考相应的图像生成 API 文档。你可以在官方文档页面找到任何标记为“API Reference”的部分。点击它,然后导航到图像生成的 API 端点部分并找到相关的请求示例。 + +https://docs.siliconflow.com/en/userguide/introduction + +![](images/image34.png) + +```Bash +curl --request POST \ + --url https://api.siliconflow.com/v1/images/generations \ + --header 'Authorization: Bearer ' \ + --header 'Content-Type: application/json' \ + --data '{ + "model": "black-forest-labs/FLUX.1-Kontext-max", + "prompt": "an island near sea, with seagulls, moon shining over the sea, light house, boats int he background, fish flying over the sea" +}' +``` + +记得将你打算使用的模型和 API key 填入相应的字段。之后,你可以在计算机的命令行中使用该命令运行直接请求测试。 + +```Bash +curl --request POST \ + --url https://api.siliconflow.com/v1/images/generations \ + --header 'Authorization: Bearer sk-defrgqrgrganpncxxibfyzfocgafga' \ + --header 'Content-Type: application/json' \ + --data '{ + "model": "Qwen/Qwen-Image", + "prompt": "an island near sea, with seagulls, moon shining over the sea, light house, boats int he background, fish flying over the sea" +}' +``` + +![](images/image35.png) + +你可以将下面修改后的代码行发送给 z.ai,并要求它帮你创建一个前端测试演示。很快,你就能实现 SiliconFlow 的基本 API 调用。 + +![](images/image36.png) + +## 3.4 将 Recraft API 集成到 z.ai 中 + +### 什么是 Recraft + +> Recraft 是一款面向设计师、插画师和营销人员的 AI 工具——于 2022 年在美国成立,总部位于伦敦。它帮助生成/迭代视觉效果(图像、矢量艺术、3D 图形),具有高质量输出(任何文本大小/长度)、精确元素定位和品牌一致性设计等优势。受到 200 个国家/地区 300 多万用户(包括奥美、Netflix)的信任,并已创建了 3.5 亿多张图像,其团队旨在使其成为必备的设计师工具,确保创作者能够控制他们的 AI 辅助工作流程。 +> +> ![](images/image37.png) +> +> ![](images/image38.png) +> +> ![](images/image39.png) + +### 如何获取 Recraft API + +首先,我们仍然需要找到重要的 API 入口以获取我们的 API key。 https://www.recraft.ai/profile/api + +由于这里没有提供免费额度,我们需要自己充值 1,000 积分。这个网站支持支付宝和微信支付,所以很容易获得 1,000 积分(注意:不要充值超过必要的金额)。 + +![](images/image40.png) + +之后,我们仍然遵循通常的方法:去官方文档找到相应的请求示例。 + +https://www.recraft.ai/docs/api-reference/getting-started + +https://www.recraft.ai/docs/api-reference/usage + +https://www.recraft.ai/docs/api-reference/guides + +在这里,我们可以直接复制整个内容并粘贴到 z.ai。 + +![](images/image41.png) + +注意,在聊天窗口中,输入你的 API key 和文档内容就足够了;z.ai 会自动为你构建前端。 + +如果过程中出现错误,你可以直接将错误信息粘贴到聊天窗口,让 z.ai 帮你自动解决。 + +![](images/image42.png) + +## 3.5 将 Seedream API 集成到 z.ai 中(针对中国用户) + +### 什么是 Seedream 4.0 + +https://seed.bytedance.com/en/seedream4_0 + +![](images/image43.png) + +> 也许你已经知道 Nano Banana(Google 开发),但你最好不要错过 Seedream。Seedream 4.0 是字节跳动打造的新一代图像创作模型。它将图像生成和图像编辑能力集成到一个统一的架构中。这使得它能够灵活处理复杂的多模态任务,如基于知识的生成、复杂推理和参考一致性。此外,它的推理速度比前代产品快得多,并且可以生成分辨率高达 4K 的令人惊叹的高清图像。 +> +> ![](images/image44.png) +> +> ![](images/image45.png) +> +> ![](images/image46.png) + +### 如何获取 Seedream API - 火山引擎 (Volcengine)(针对中国用户) + +我们将逐步演示如何将 Seedream API 集成到 z.ai 示例中。 + +https://www.volcengine.com/experience/ark?launch=seedream + +访问页面后,点击登录。 + +![](images/image47.png) + +登录后,找到页面右上角的充值选项。 + +![](images/image48.png) + +进行充值需要实名认证。 + +![](images/image49.png) + +认证成功后,你可以充值 1 元用于测试。 + +https://console.volcengine.com/finance/fund/recharge + +![](images/image50.png) + +返回初始界面并点击 API 访问。 + +![](images/image51.png) + +首先,创建一个 API key,然后点击选择选项。 + +![](images/image52.png) + +这将带你进入第 2 步。在这里,你需要确认调用的服务是 Seedream 4.0,并复制提供的调用示例。 + +![](images/image53.png) + +准备好 API key 和调用示例后,你可以直接将它们粘贴到 z.ai 中以生成前端交互演示。 + +重要提示:这里的默认示例相对复杂。记得禁用“添加水印”选项和“流式响应”选项,以确保不生成水印且不会发生请求失败。 + +![](images/image54.png) + +输入提示词后,你将收到生成的结果。享受它吧! + +![](images/image55.png) + +# 让它更有趣 + +完成基本功能后,我们可以尝试给我们的程序添加一些新花样!如果你觉得蛇吃单词或字符的过程有点枯燥,你可以让蛇吃不同颜色的单词,并相应地改变蛇的颜色。 + +你还可以为“吃”的过程添加特效,或者引入触发特效的魔法单词——比如增加蛇的速度或大小。另一个想法是每当蛇吃一个单词时就让模型生成一首诗和一幅图,而不是等到它吃掉八个单词。 + +如果觉得这些有挑战性,你可以直接向语言模型求助!它可以提供创意建议,让你的游戏更有趣。试一试吧! + +```JavaScript +1. "单词解锁世界" 机制 +每当蛇吃掉一个单词,LLM 会对该单词进行诗意联想(例如,“树”→“森林”、“绿荫”),图像模型会即时为该单词生成一个小艺术品。这些图像逐渐拼凑成一个独特的、玩家创造的全景图,所以玩家每次游玩都在“作画和写诗”。 + +2. "诗歌拼图" 玩法 +蛇吃掉的每个单词都会触发 LLM 生成简短的诗句,图像模型生成插图。这些诗句和图像像拼图一样组合在一起,在回合结束时形成一首 AI 协作的诗和画。 + +3. "魔法单词" & "故事分支" +特殊的“魔法单词”(例如,“风”、“夜”、“梦”)不仅触发 LLM 生成诗歌,还会改变场景的情绪或主题——将生成图像的风格转变为夜晚、暴风雨或梦幻般的氛围。 +分支故事:LLM 在开始时给出一个主题或谜语(例如,“秋天的回忆”)。玩家的单词选择直接影响故事和诗歌的演变,图像模型实时更新背景和视觉效果。 + +4. "实时互动生成" +每个单词之后,LLM 生成一行对话或描述,游戏中的 NPC 可以对玩家“说话”,或者环境可以相应地改变。 +蛇的外观或游戏中的障碍物可以根据吃掉的单词在视觉上发生变化,这要归功于图像模型。 + +5. "创作 & 分享" +玩家可以在会话结束时保存并分享他们 AI 创作的诗歌和图像,炫耀他们独特的“AI 协作”。 +“最美诗歌+艺术”、“最有创意单词组合”等排行榜,鼓励重玩和创造力。 + +6. "按句贪吃蛇" 挑战 +反向模式:LLM 给出一句诗或一个谜语,玩家必须引导蛇按顺序吃掉单词来重构句子。吃错单词会通过图像生成模型触发有趣或艺术性的后果。 + +7. "主题关卡" & "风格选择" +游戏开始时,玩家选择一个主题(例如,“童话”、“科幻”、“唐诗”),LLM 和图像模型都会调整单词选择、诗歌风格和视觉效果以匹配,使每次运行都感觉新鲜。 + +8. "现场共创" +当吃掉一个特殊单词时,LLM 可以提示玩家输入短语或选择风格,然后 AI 生成相应的诗句和插图,使其成为真正的人类-AI 共创。 + +9. "AI 彩蛋 & 成就" +某些单词组合被 LLM 识别为特殊主题或内部笑话(例如,“月亮”、“桂花”、“河岸”),触发稀有的诗句和插图,奖励探索。 + +10. "成长的故事" +随着蛇的成长,LLM 生成一个连续的故事诗,图像模型创建一个无缝的长卷或全景图,所以玩家同时在“写作、绘画和玩耍”。 +``` + +此外,我们还可以要求 LLM 帮你直接生成项目级的提示词。在上一节中,我们只自己写了贪吃蛇游戏的提示词。现在让我们尝试让大模型生成一个带有整体框架和实现路径的提示词(你可以直接用 z.ai 生成): + +> 我想让 AI 生成一个网页贪吃蛇游戏,需要一个更完整的提示词,让生成结果更令人印象深刻和有趣。请生成相应的提示词。当前目标是:生成一个贪吃蛇游戏,需要实现吃不同单词生成诗歌的功能,并且应该包含图像生成模块。 + +z.ai 的回复将会是这样的: + +![](images/image56.png) + +我们可以使用这个提示词在全栈开发模式下重新生成项目: + +![](images/image57.png) + +![](images/image58.png) + +# 更多参考案例 + +除了贪吃蛇(游戏),我们可以让想象力尽情驰骋。 + +创造任何我们想创造的东西,甚至尝试搞砸一切!然后重头再来! + +```YAML +1. AI 艺术画廊平台 + 描述:一个展示 AI 生成艺术作品的在线画廊,用户可以上传、分享和评论 AI 艺术作品。 + 功能:用户账户系统、艺术作品上传和展示、评分系统、分类浏览、AI 生成工具集成。 + 技术亮点:React/Vue 前端、Node.js 后端、MongoDB 数据库、AI API 集成。 + +2. 复古游戏档案馆 + 描述:一个致敬经典游戏的网站,包含游戏历史、玩法指南和在线可玩复古游戏。 + 功能:游戏数据库、时间轴展示、在线模拟器、用户评论、游戏收藏功能。 + 技术亮点:响应式设计、WebGL/Canvas 游戏实现、RESTful API、用户认证系统。 + +3. 可持续生活追踪器 + 描述:一个帮助用户通过环保提示和社区挑战来追踪和减少碳足迹的网站。 + 功能:个人碳足迹计算器、目标设定、进度追踪、社区挑战、环保知识库。 + 技术亮点:数据可视化、移动端优化、社交功能、推送通知。 + +4. 虚拟厨房助手 + 描述:一个基于 AI 的烹饪指导平台,提供个性化食谱推荐和分步烹饪说明。 + 功能:食谱数据库、食材识别、个性化推荐、烹饪计时器、营养分析。 + 技术亮点:图像识别 API、机器学习推荐系统、语音控制、实时视频指导。 + +5. 地下音乐发现平台 + 描述:一个专注于独立和新兴艺术家的音乐流媒体平台,提供独特的发现体验。 + 功能:音乐流媒体、艺术家资料、个性化推荐、播放列表创建、社区评论。 + 技术亮点:音频流处理、推荐算法、社交功能、音乐可视化。 + +6. 极简任务管理系统 + 描述:一个具有禅意美学的任务管理工具,专注于简单和高效的任务组织。 + 功能:任务创建和分类、优先级设置、进度追踪、团队协作、数据分析。 + 技术亮点:极简 UI 设计、拖放功能、实时同步、跨平台兼容性。 + +7. 科幻写作工坊 + 描述:一个为科幻作家提供创意工具和灵感的平台,包括世界观构建辅助和角色开发工具。 + 功能:故事结构工具、角色资料、世界观构建模板、写作统计、社区反馈。 + 技术亮点:富文本编辑器、数据可视化、协作编辑、AI 辅助创作。 + +8. 个人知识图谱 + 描述:一个帮助用户构建个人知识网络,可视化并连接各种想法和信息的工具。 + 功能:节点创建和连接、标签系统、搜索功能、导入/导出工具、可视化图表。 + 技术亮点:图数据库、数据可视化算法、Markdown 支持、跨设备同步。 + +9. 虚拟植物园 + 描述:一个互动植物百科全书,用户可以探索植物世界并创建虚拟花园。 + 功能:植物数据库、3D 植物模型、生长模拟、园艺指南、社区展示。 + 技术亮点:3D 渲染、季节变化模拟、AR 集成、植物识别 API。 + +10. 编程挑战竞技场 + 描述:一个面向程序员的在线竞赛平台,具有各种难度级别的编程挑战。 + 功能:挑战问题、代码编辑器、自动评估、排行榜、学习路径。 + 技术亮点:代码沙箱环境、实时评估系统、算法可视化、社交学习功能。 +``` + +还有... 如果你喜欢玩游戏,让我们一起尝试创造游戏吧! + +```SQL +1. 3D 开放世界 RPG + 描述:一个具有广阔开放世界、任务和角色成长的奇幻 RPG。 + 功能:昼夜循环、动态天气、技能树、多人合作、制作系统。 + 技术亮点:Three.js 或 Babylon.js 用于 3D 渲染、服务器端游戏逻辑、角色自定义、存档系统。 + +2. 第一人称射击 (FPS) 竞技场 + 描述:一个快节奏的多人 FPS,具有各种游戏模式和地图。 + 功能:团队死斗、夺旗、武器自定义、排位赛。 + 技术亮点:WebGL/Three.js 用于 3D 图形、多人网络代码、命中检测、语音聊天。 + +3. AI 国际象棋和多人游戏 + 描述:一个功能齐全的国际象棋平台,具有 AI 对手和在线对战功能。 + 功能:AI 难度级别、残局挑战、锦标赛模式、回放分析。 + 技术亮点:国际象棋逻辑库、WebSocket 用于实时对战、ELO 排名系统、反作弊。 + +4. 麻将在线多人游戏 + 描述:一个具有在线多人游戏和计分功能的传统麻将游戏。 + 功能:多种规则集、私人房间、排名系统、回放功能。 + 技术亮点:牌匹配逻辑、实时多人游戏、大厅系统、分数追踪。 + +5. 回合制策略游戏 + 描述:一个具有网格战斗和单位管理的战术策略游戏。 + 功能:战役模式、遭遇战、单位升级、战争迷雾、多人对战。 + 技术亮点:网格移动系统、AI 决策、回合同步、存档/读档系统。 + +6. 计时赛赛车游戏 + 描述:一个专注于计时赛和赛道记录的 3D 赛车游戏。 + 功能:多条赛道、汽车自定义、幽灵回放、排行榜。 + 技术亮点:3D 汽车物理、赛道编辑器、回放系统、在线排行榜。 + +7. 卡牌对战游戏 (卡组构建) + 描述:一个策略卡牌游戏,玩家构建卡组并与对手战斗。 + 功能:卡牌收集、卡组构建、排位赛、赛季活动。 + 技术亮点:卡牌游戏逻辑、匹配系统、AI 对手、卡牌动画。 + +8. 大逃杀 (俯视 2D) + 描述:一个俯视 2D 大逃杀游戏,具有缩小的游戏区域和战利品机制。 + 功能:单人和小队模式、武器多样性、局内事件、排行榜。 + 技术亮点:实时多人游戏、区域缩小逻辑、战利品生成系统、匹配。 + +9. 恐怖生存游戏 (第一人称) + 描述:一个具有资源管理和逃生机制的第一人称恐怖游戏。 + 功能:氛围环境、解谜、敌人 AI、多重结局。 + 技术亮点:动态照明、声音设计、敌人寻路、存档系统。 + +10. 音乐节奏游戏 (3D) + 描述:一个 3D 节奏游戏,玩家随着音乐节拍击打音符。 + 功能:多种难度级别、赛道编辑器、自定义歌曲支持、排行榜。 + 技术亮点:音频分析、节拍同步、3D 音符轨道、输入时机检测。 +``` + +# 总结 + +这就是完整的教程!你可能需要 **4 小时** 才能完成所有内容并构建你自己的贪吃蛇游戏。不要着急——探索、实验并享受这个过程。 + +如果你有不同的游戏想法,那也很好。最重要的是开始构建。 + +祝你好运,欢迎来到 AI 原生创造力的世界 :) diff --git a/docs/project/chapter1/images/image1.png b/docs/project/chapter1/images/image1.png new file mode 100644 index 0000000..21f5a14 Binary files /dev/null and b/docs/project/chapter1/images/image1.png differ diff --git a/docs/project/chapter1/images/image10.png b/docs/project/chapter1/images/image10.png new file mode 100644 index 0000000..60ccec8 Binary files /dev/null and b/docs/project/chapter1/images/image10.png differ diff --git a/docs/project/chapter1/images/image11.png b/docs/project/chapter1/images/image11.png new file mode 100644 index 0000000..c1fe454 Binary files /dev/null and b/docs/project/chapter1/images/image11.png differ diff --git a/docs/project/chapter1/images/image12.png b/docs/project/chapter1/images/image12.png new file mode 100644 index 0000000..0a56016 Binary files /dev/null and b/docs/project/chapter1/images/image12.png differ diff --git a/docs/project/chapter1/images/image13.png b/docs/project/chapter1/images/image13.png new file mode 100644 index 0000000..ef2691c Binary files /dev/null and b/docs/project/chapter1/images/image13.png differ diff --git a/docs/project/chapter1/images/image14.png b/docs/project/chapter1/images/image14.png new file mode 100644 index 0000000..9379976 Binary files /dev/null and b/docs/project/chapter1/images/image14.png differ diff --git a/docs/project/chapter1/images/image15.png b/docs/project/chapter1/images/image15.png new file mode 100644 index 0000000..13a9cba Binary files /dev/null and b/docs/project/chapter1/images/image15.png differ diff --git a/docs/project/chapter1/images/image16.png b/docs/project/chapter1/images/image16.png new file mode 100644 index 0000000..d5db181 Binary files /dev/null and b/docs/project/chapter1/images/image16.png differ diff --git a/docs/project/chapter1/images/image17.png b/docs/project/chapter1/images/image17.png new file mode 100644 index 0000000..98be30e Binary files /dev/null and b/docs/project/chapter1/images/image17.png differ diff --git a/docs/project/chapter1/images/image18.png b/docs/project/chapter1/images/image18.png new file mode 100644 index 0000000..9169fd6 Binary files /dev/null and b/docs/project/chapter1/images/image18.png differ diff --git a/docs/project/chapter1/images/image19.png b/docs/project/chapter1/images/image19.png new file mode 100644 index 0000000..dc216d5 Binary files /dev/null and b/docs/project/chapter1/images/image19.png differ diff --git a/docs/project/chapter1/images/image2.png b/docs/project/chapter1/images/image2.png new file mode 100644 index 0000000..c8d50c6 Binary files /dev/null and b/docs/project/chapter1/images/image2.png differ diff --git a/docs/project/chapter1/images/image20.png b/docs/project/chapter1/images/image20.png new file mode 100644 index 0000000..1be765b Binary files /dev/null and b/docs/project/chapter1/images/image20.png differ diff --git a/docs/project/chapter1/images/image21.png b/docs/project/chapter1/images/image21.png new file mode 100644 index 0000000..c1c4e66 Binary files /dev/null and b/docs/project/chapter1/images/image21.png differ diff --git a/docs/project/chapter1/images/image22.png b/docs/project/chapter1/images/image22.png new file mode 100644 index 0000000..93eb6c6 Binary files /dev/null and b/docs/project/chapter1/images/image22.png differ diff --git a/docs/project/chapter1/images/image23.png b/docs/project/chapter1/images/image23.png new file mode 100644 index 0000000..cb76b84 Binary files /dev/null and b/docs/project/chapter1/images/image23.png differ diff --git a/docs/project/chapter1/images/image24.png b/docs/project/chapter1/images/image24.png new file mode 100644 index 0000000..9ed0116 Binary files /dev/null and b/docs/project/chapter1/images/image24.png differ diff --git a/docs/project/chapter1/images/image25.png b/docs/project/chapter1/images/image25.png new file mode 100644 index 0000000..5af8820 Binary files /dev/null and b/docs/project/chapter1/images/image25.png differ diff --git a/docs/project/chapter1/images/image26.png b/docs/project/chapter1/images/image26.png new file mode 100644 index 0000000..231d290 Binary files /dev/null and b/docs/project/chapter1/images/image26.png differ diff --git a/docs/project/chapter1/images/image27.png b/docs/project/chapter1/images/image27.png new file mode 100644 index 0000000..f6c5365 Binary files /dev/null and b/docs/project/chapter1/images/image27.png differ diff --git a/docs/project/chapter1/images/image28.png b/docs/project/chapter1/images/image28.png new file mode 100644 index 0000000..efe9ab9 Binary files /dev/null and b/docs/project/chapter1/images/image28.png differ diff --git a/docs/project/chapter1/images/image29.png b/docs/project/chapter1/images/image29.png new file mode 100644 index 0000000..fe0c010 Binary files /dev/null and b/docs/project/chapter1/images/image29.png differ diff --git a/docs/project/chapter1/images/image3.png b/docs/project/chapter1/images/image3.png new file mode 100644 index 0000000..60d51d9 Binary files /dev/null and b/docs/project/chapter1/images/image3.png differ diff --git a/docs/project/chapter1/images/image30.png b/docs/project/chapter1/images/image30.png new file mode 100644 index 0000000..f23613b Binary files /dev/null and b/docs/project/chapter1/images/image30.png differ diff --git a/docs/project/chapter1/images/image31.png b/docs/project/chapter1/images/image31.png new file mode 100644 index 0000000..2ac4d78 Binary files /dev/null and b/docs/project/chapter1/images/image31.png differ diff --git a/docs/project/chapter1/images/image32.png b/docs/project/chapter1/images/image32.png new file mode 100644 index 0000000..1441cda Binary files /dev/null and b/docs/project/chapter1/images/image32.png differ diff --git a/docs/project/chapter1/images/image33.png b/docs/project/chapter1/images/image33.png new file mode 100644 index 0000000..335a340 Binary files /dev/null and b/docs/project/chapter1/images/image33.png differ diff --git a/docs/project/chapter1/images/image34.png b/docs/project/chapter1/images/image34.png new file mode 100644 index 0000000..3040f8f Binary files /dev/null and b/docs/project/chapter1/images/image34.png differ diff --git a/docs/project/chapter1/images/image35.png b/docs/project/chapter1/images/image35.png new file mode 100644 index 0000000..84d9854 Binary files /dev/null and b/docs/project/chapter1/images/image35.png differ diff --git a/docs/project/chapter1/images/image36.png b/docs/project/chapter1/images/image36.png new file mode 100644 index 0000000..8b19e62 Binary files /dev/null and b/docs/project/chapter1/images/image36.png differ diff --git a/docs/project/chapter1/images/image37.png b/docs/project/chapter1/images/image37.png new file mode 100644 index 0000000..3ec264b Binary files /dev/null and b/docs/project/chapter1/images/image37.png differ diff --git a/docs/project/chapter1/images/image38.png b/docs/project/chapter1/images/image38.png new file mode 100644 index 0000000..09a77cc Binary files /dev/null and b/docs/project/chapter1/images/image38.png differ diff --git a/docs/project/chapter1/images/image39.png b/docs/project/chapter1/images/image39.png new file mode 100644 index 0000000..617dbf1 Binary files /dev/null and b/docs/project/chapter1/images/image39.png differ diff --git a/docs/project/chapter1/images/image4.png b/docs/project/chapter1/images/image4.png new file mode 100644 index 0000000..a93e937 Binary files /dev/null and b/docs/project/chapter1/images/image4.png differ diff --git a/docs/project/chapter1/images/image40.png b/docs/project/chapter1/images/image40.png new file mode 100644 index 0000000..123fa8b Binary files /dev/null and b/docs/project/chapter1/images/image40.png differ diff --git a/docs/project/chapter1/images/image41.png b/docs/project/chapter1/images/image41.png new file mode 100644 index 0000000..8ccc9a6 Binary files /dev/null and b/docs/project/chapter1/images/image41.png differ diff --git a/docs/project/chapter1/images/image42.png b/docs/project/chapter1/images/image42.png new file mode 100644 index 0000000..7d85686 Binary files /dev/null and b/docs/project/chapter1/images/image42.png differ diff --git a/docs/project/chapter1/images/image43.png b/docs/project/chapter1/images/image43.png new file mode 100644 index 0000000..2948f31 Binary files /dev/null and b/docs/project/chapter1/images/image43.png differ diff --git a/docs/project/chapter1/images/image44.png b/docs/project/chapter1/images/image44.png new file mode 100644 index 0000000..967dc34 Binary files /dev/null and b/docs/project/chapter1/images/image44.png differ diff --git a/docs/project/chapter1/images/image45.png b/docs/project/chapter1/images/image45.png new file mode 100644 index 0000000..b8e8bfc Binary files /dev/null and b/docs/project/chapter1/images/image45.png differ diff --git a/docs/project/chapter1/images/image46.png b/docs/project/chapter1/images/image46.png new file mode 100644 index 0000000..70e7fe6 Binary files /dev/null and b/docs/project/chapter1/images/image46.png differ diff --git a/docs/project/chapter1/images/image47.png b/docs/project/chapter1/images/image47.png new file mode 100644 index 0000000..2f5c1db Binary files /dev/null and b/docs/project/chapter1/images/image47.png differ diff --git a/docs/project/chapter1/images/image48.png b/docs/project/chapter1/images/image48.png new file mode 100644 index 0000000..29869d6 Binary files /dev/null and b/docs/project/chapter1/images/image48.png differ diff --git a/docs/project/chapter1/images/image49.png b/docs/project/chapter1/images/image49.png new file mode 100644 index 0000000..f6680a6 Binary files /dev/null and b/docs/project/chapter1/images/image49.png differ diff --git a/docs/project/chapter1/images/image5.png b/docs/project/chapter1/images/image5.png new file mode 100644 index 0000000..f6aa135 Binary files /dev/null and b/docs/project/chapter1/images/image5.png differ diff --git a/docs/project/chapter1/images/image50.png b/docs/project/chapter1/images/image50.png new file mode 100644 index 0000000..181a9e7 Binary files /dev/null and b/docs/project/chapter1/images/image50.png differ diff --git a/docs/project/chapter1/images/image51.png b/docs/project/chapter1/images/image51.png new file mode 100644 index 0000000..127b12f Binary files /dev/null and b/docs/project/chapter1/images/image51.png differ diff --git a/docs/project/chapter1/images/image52.png b/docs/project/chapter1/images/image52.png new file mode 100644 index 0000000..097bb15 Binary files /dev/null and b/docs/project/chapter1/images/image52.png differ diff --git a/docs/project/chapter1/images/image53.png b/docs/project/chapter1/images/image53.png new file mode 100644 index 0000000..734e921 Binary files /dev/null and b/docs/project/chapter1/images/image53.png differ diff --git a/docs/project/chapter1/images/image54.png b/docs/project/chapter1/images/image54.png new file mode 100644 index 0000000..29ce6e4 Binary files /dev/null and b/docs/project/chapter1/images/image54.png differ diff --git a/docs/project/chapter1/images/image55.png b/docs/project/chapter1/images/image55.png new file mode 100644 index 0000000..020ea78 Binary files /dev/null and b/docs/project/chapter1/images/image55.png differ diff --git a/docs/project/chapter1/images/image56.png b/docs/project/chapter1/images/image56.png new file mode 100644 index 0000000..f1c8a5c Binary files /dev/null and b/docs/project/chapter1/images/image56.png differ diff --git a/docs/project/chapter1/images/image57.png b/docs/project/chapter1/images/image57.png new file mode 100644 index 0000000..10e3453 Binary files /dev/null and b/docs/project/chapter1/images/image57.png differ diff --git a/docs/project/chapter1/images/image58.png b/docs/project/chapter1/images/image58.png new file mode 100644 index 0000000..3e68eb4 Binary files /dev/null and b/docs/project/chapter1/images/image58.png differ diff --git a/docs/project/chapter1/images/image6.png b/docs/project/chapter1/images/image6.png new file mode 100644 index 0000000..87512cd Binary files /dev/null and b/docs/project/chapter1/images/image6.png differ diff --git a/docs/project/chapter1/images/image7.png b/docs/project/chapter1/images/image7.png new file mode 100644 index 0000000..08993ce Binary files /dev/null and b/docs/project/chapter1/images/image7.png differ diff --git a/docs/project/chapter1/images/image8.png b/docs/project/chapter1/images/image8.png new file mode 100644 index 0000000..cc5a94d Binary files /dev/null and b/docs/project/chapter1/images/image8.png differ diff --git a/docs/project/chapter1/images/image9.png b/docs/project/chapter1/images/image9.png new file mode 100644 index 0000000..475c538 Binary files /dev/null and b/docs/project/chapter1/images/image9.png differ diff --git a/docs/project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md b/docs/project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md new file mode 100644 index 0000000..6db921b --- /dev/null +++ b/docs/project/chapter2/chapter2-reach-the-capability-boundaries-of-ai-tools.md @@ -0,0 +1,1472 @@ +# Project 2: 探索 AI 工具的能力边界 + +# 上节课复习题 + +在上一节课中,我们学习了如何使用 z.ai 创建 AI 原生贪吃蛇游戏,并尝试集成了 DeepSeek 和 Siliconflow 平台的 API。(在随后的课程中,我们将经常使用不同模型的 API;这只是演示用的例子。) + +但是,这只是一个小测试。你可以通过以下问题来确认自己是否掌握了上节课的内容: + +* 如何利用 z.ai 的全栈开发功能将我的想法变为现实? +* 普通应用和 AI 应用有什么区别?AI 原生应用到底是什么? +* API 到底是什么?云服务平台是什么?我如何让大语言模型(LLM)帮我集成语言模型和图像模型的 API? +* 如果我们要“调用”一个新模型,通常在哪里可以找到方法? +* 使用 AI 产品与使用其他普通程序有什么区别? +* 如何进行简单的 AI 产品研究?在哪里可以找到最新的 AI 产品研究,在哪里可以获得获得关键投资的 AI 产品公司的信息? + +如果你发现有任何不理解的地方,请参考上节课的文档寻找答案,或直接在微信群里提问。 + +在这节课中,我们将应用上节课学到的基础 AI 应用开发技能、对 API 和云平台的理解,以及对 AI 原生概念的思考能力。 + +一个真正的 AI 产品大师需要彻底了解 AI 工具的能力边界。他们需要知道最新的 AI 工具是什么,哪些是最有用的,哪些模型提供最佳的性价比,以及如何组合不同的工具来完成更有意义的任务。 + +这就像当老板一样:你需要了解你的“AI 员工”的整体能力,才能充分利用它们。 + +# 💡 你将学到 + +在课堂上,你需要选择一个赛道进行重点学习。但如果你很有动力,课后可以尝试完成所有赛道的学习! + +* **基于 Web 的 AI 编码能力(AI 编码赛道):** 了解不同编程网站的能力和局限性,并体验使用不同模型实现需求的难度(AI 编码赛道)。 +* **提示词/上下文工程的概念和基础技能(LLM 赛道):** 掌握如何调用和集成不同的大语言模型(LLM),识别它们的能力边界,并理解为什么在工业环境中编写提示词具有挑战性。 +* **图像/视频的常见用例和基础技能(图像/视频赛道):** 了解最先进的图像和视频模型的用例和能力边界,并复刻创意视频。 +* 如何识别最先进的 LLM 和图像模型,以及如何使用它们。 + +# 🤖 AI 编码专家赛道 + +在第一节课中,我们学习了入门级的 Vibecoding 工具。然而,要成为大师,你现在必须承担更大的责任:在给定的时间内,你需要选择下面推荐的任何一个 Vibecoding 工具来复刻提供的示例——甚至超越它。 + +![](images/image1.png) + +我们将使用超级井字棋游戏作为测试示例,可在此处访问: + +[https://space.coze.cn/coding-expert-runtime/295344603906?task_id=7540925598573003046](https://space.coze.cn/coding-expert-runtime/295344603906?task_id=7540925598573003046) + +![](images/image2.png)![](images/image3.png) + +这个游戏是在 Coze Space(可以理解为一个类似于 z.ai 的全栈开发平台,由另一家大语言模型公司开发)中创建的,并在 Coze Space 比赛中获得了第一名。它简单而引人入胜。你需要选择一个平台并尝试**复刻**超级井字棋游戏,**规则和最终结果要与原版几乎相同。尽你所能做到一模一样!!** + +在复刻过程中,你可以通过任何可能的方式向开发平台提供信息——例如,**通过分享链接、提供文字描述或上传图片。** 观察哪种方法最有效,哪个平台允许最快的复刻。为了避免误解,你必须提前玩一次游戏,以确保你完全掌握其规则。 + +接下来,我们将回顾可供选择的平台。你们中的一些人可能已经知道如何使用 AI IDE 进行编码;然而,为了直观地体验不同基于 Web 的平台的 AI 编码功能,我们仍然建议你按照下面提供的建议进行操作: + +## 📚 课堂作业:100% 复刻超级井字棋游戏 + +**任务目标:尽你所能做到一模一样!** + +感谢 Yerim 的详细教程 [Vibe Coding] 贪吃蛇游戏教程。**我们可以选择以下任何一个平台来复刻游戏**,你必须不惜一切代价完成任务。(如果你不确定从哪里开始,你甚至可以尝试向大语言模型寻求建议——例如如何编写提示词或采取什么步骤——以完全复刻项目。) + +请查看以下编码平台的介绍(Yerim 的详细教程),并选择 **基于 Web 的** 工具来复刻超级井字棋游戏。 + +![](images/image4.png) + +或者你也可以尝试 Coze Space(基于 Web):https://space.coze.cn/ + +## 行业应用 + +让我们一起看看这篇实地研究文章:*[调查:软件开发团队中的 AI 浪潮持续增长](https://github.blog/news-insights/research/survey-ai-wave-grows/)* + +![](images/image5.png) + +全球最著名的代码托管平台 GitHub 调查了美国、巴西、印度和德国企业软件开发团队的 2,000 人。调查重点关注软件开发中生成式 AI 工具的使用、体验和期望。 + +![](images/image6.png) + +根据这项调查,求职者认为精通 AI 编码工具是一项重要资产。几乎所有受访者(99-100%)都认为这项技能使他们成为更具吸引力的候选人,强调了 AI 在各个领域日益增长的重要性。值得注意的是,很大一部分人(德国 43%,印度 56%)认为这种专业知识显着提高了他们的就业能力。 + +所以... 选择这门课程意味着你是个聪明人,对吧?:) + +# 🫧 LLM 专家赛道 + +## 提示词工程 (Prompt Engineering) + +### 什么是提示词和提示词工程 + +![](images/image7.png) + +当我们谈论提示词时,我们可以简单地将其理解为与大模型交互时的文本输入。但你有没有想过它们是如何工作的?为什么我们需要所谓的“提示词工程”?为什么需要“工程”方法? + +要回答这些问题,我们需要从模型训练开始。众所周知,常见深度学习模型的训练结果可以粗略地描述为一个“黑盒子”。这是因为我们只知道输入模型的数据,而不知道它会产生什么样的输出——即使输出很可能与数据集的特征一致。我们只能在训练完成后粗略地掌握模型响应的真实风格。 + +![](images/image8.png) + +在预训练阶段,模型在大量文本(如小说、教科书等)上进行训练,用于文本续写任务。这个过程教会模型如何准确预测下一个单词,甚至后续的句子和段落。后来,为了使大模型能够处理对话任务,我们创建了大量的对话数据进行“指令微调”(微调模型以遵循人类指令)。基于底层原理,我们的提示词输入风格越接近模型的内部规则,其输出就越有可能满足我们的需求。 + +> 要深入了解与 LLM 相关的知识,请阅读以下可选材料:大语言模型(LLM)简要说明 https://www.bilibili.com/video/BV1xmA2eMEFF/ +> +> 以下是大语言模型(LLM)开发三个核心阶段使用的数据示例。这提供了基本的了解,现阶段不需要深入掌握。 +> +> **1. 预训练阶段和数据 (Pre-training)** +> +> 预训练阶段涉及在大规模通用文本数据上对模型进行初始训练。目标是让模型掌握语言的基本规则、语法结构、事实知识和推理能力,为后续针对特定任务的微调奠定基础。这个阶段是过程中计算最密集、资源消耗最大的部分。 +> +> 数据由大量未经人工标注的非结构化文本组成。这些数据来源极其广泛,包括从整个互联网爬取的网页(如 Common Crawl 数据集)、数百万本数字化书籍、维基百科、学术论文和开源代码库。核心特征是“海量”和“无标签”。 +> +> **学习过程:** +> +> 学习是通过自回归语言建模进行的。模型接收文本的第一部分(例如,“自然选择,最早由达尔文在《物种起源》(1859)中提出……”),然后预测随后的单词(“……通过可遗传特征的变化驱动生物进化。”)。训练目标是最小化预测单词与实际单词之间的交叉熵损失,使模型能够掌握语言模式和世界知识。 +> +> * 书籍摘录:“自然选择,最早由达尔文在《物种起源》(1859)中提出,通过可遗传特征的变化驱动生物进化。” +> * 网页内容:“太阳能和风能排放的温室气体远少于煤炭或天然气。” +> +> **2. 微调数据 (Fine-Tuning)** +> +> **描述:** 使用少量结构化的、特定于任务的数据(输入 → 输出对)使模型适应特定用例。这个过程也常被称为指令微调。 +> +> **学习过程:** +> +> 采用监督学习范式。模型接收完整的输入(例如,“我如何退货?”)并学习生成标准答案(“登录您的帐户 →……”)。通过最小化模型输出与标准答案之间的差异(例如,交叉熵损失)来优化模型的参数,使其能够掌握该特定任务的输入-输出映射。 +> +> * 输入(用户查询): +> +> “我如何退货?” +> +> * 输出(机器人回复): +> +> “登录您的帐户 → ‘订单历史’ → 选择订单 → ‘发起退货’。退款将在验证后 5-7 天内处理。” + +鉴于模型是一个黑盒子,人们尝试了各种与之交互的方式——有些效果很好,有些则不然。提示词工程正是从这种背景下出现的。事实上,由于我们不知道模型对什么提示词反应最好,也不确定哪些提示策略可以转移到其他模型,**我们需要总结并系统化这些“黑盒子”交互的结果。** + +随着越来越多的人使用大模型,对这些模型的可控输出和可控功能的需求越来越大——这就是“工程”概念的用武之地。这里的工程强调三个关键属性:**可复现性、可验证性和可转移性**。我们的目标是开发一套有效的规则,可以提高模型响应的质量,同时适用于不同的模型。这正是提示词工程所包含的内容:我们在“文本输入”中添加特定的方法,使大模型表现得更好。 + +其中一些方法有科学证据支持,而另一些则源于广泛的实验——经验和假设导致对模型“最能接受的内部语言”的直观掌握,从而提供更好的模型输出结果。 + +简而言之,在实际工作中,当我们不断完善现有的提示词或探索最佳提示词时——目标是使输出更稳定,符合预期,并建立一种可转移、长期可重用且有效提高性能的提示词方法——这个过程可以称为提示词工程。 + +#### 回顾贪吃蛇游戏 + +让我们回顾一下上一节课与模型交互的过程。事实上,我们的输入框可以看作是一个小指令,但它还不是我们需要在模型中强调的提示词。例如,我们输入了一句话:*“帮我创建一个生成猫的前端页面。当我输入一个句子时,它应该生成一只符合描述的猫。”* + +![](images/image9.png) + +这句话本身可以被认为是与 z.ai 交互的指令,但它不是我们使用的最终提示词。原因是实际上,z.ai 首先需要基于我们的这句话进行广泛的思考和研究,以帮助我们编写调用大语言模型和图像生成模型的代码。 + +![](images/image10.png) + +然而,我们实际传输给模型本身的不仅仅是简单的初始句子。让我们看看模型内部使用的真实提示词: + +![](images/image11.png) + +这是传递给图像生成模型的实际“提示词”,它不同于我们直接用于与 z.ai 交互的指令。还记得前面提到的吗?如果我们的提示词更接近训练数据集的风格,结果可能会更好。 + +现在,你可能会想为什么生成猫图像的提示词如此简洁——这是因为图像训练数据集的风格大致如此。对于更复杂的图像,提示词看起来像下面的例子。你会注意到它们的风格也与我们在与大语言模型(如 ChatGPT)交互时使用的提示词完全不同: + +```Bash +Chinese ink wash landscape, misty mountains, lone boat, zen, monochrome, 4K +``` + +```Bash +A minimalist Chinese ink wash landscape (shanshui style), distant misty mountains (light gray and black ink washes), a single small wooden boat on calm gray water, one lone fisherman in traditional bamboo hat, sparse pine trees on the mountain slopes, light fog floating above the water, empty negative space (white background), simple brush strokes, monochromatic (black, white, gray), high contrast, elegant, zen atmosphere, 4K, clean lines. +``` + +当我们回顾贪吃蛇游戏模型用于生成诗歌的提示词时,我们也会发现它相当复杂: + +![](images/image12.png) + +这种复杂的结构和组成是我们需要接下来研究的主题,我们将使用不同的简单例子来加深理解。 + +#### 思考模型 / 推理模型 vs. 非思考模型 + +然而,在我们深入研究实际技术之前,我们首先需要学习一个新概念,称为思考模型和非思考模型——这是为了避免将技术应用于错误类型的模型。在使用大模型时,我们有时可能会观察到某些模型会经历推理过程:它们需要在提供最终答案之前进行某种形式的思考。我们将这种类型的模型称为思考模型。 + +![](images/image13.png) + +另一种类型的模型不需要思考过程并直接提供答案;我们称之为非思考模型。 + +![](images/image14.png) + +这两类模型之间的关键区别在于它们的训练方法:思考模型需要时间来处理和推理你的问题,这通常会导致更准确的答案。然而,对于提示词工程来说,技术的有效性在模型类型之间差异很大——对非思考模型效果很好的提示词可能在思考模型中表现不佳。 + +一般来说: + +* 思考模型往往需要更简单的提示词。在许多情况下,过长的提示词不会增加价值,甚至可能阻碍性能。 +* 对于非思考模型,在处理复杂需求时,你可以尝试使用非常详细、精细的提示词,以确保输出完全符合你的期望。 + +我们将测试的大多数模型对针对非思考模型定制的提示词工程技术反应更灵敏。这是因为思考模型通常在较短的提示词下茁壮成长,不需要严格、复杂的规则。也就是说,本教程的主要目标是动手体验:你也可以在思考模型和非思考模型之间切换,输入以下提示词工程示例,并比较输出以观察结果如何变化。 + +### 示例 + +接下来,我们将学习常见的提示词工程方法,我们将了解不同提示词结构对结果的深入影响。 + +为了测试不同模型对不同提示词的反应,我们将使用我们在上一节课中使用的 SiliconFlow 平台。 + +[https://cloud.siliconflow.com/me/playground/chat](https://cloud.siliconflow.com/me/playground/chat) + +![](images/image15.png) + +首先,点击最左侧侧边栏中的“Chat”。滚动中间面板,直到看到“Add Model for Comparison”选项。点击它后,再次向下滚动并点击“Model”以选择并在不同模型之间切换,确保右侧面板中有两个不同的模型进行比较。此时,你可以直接在右侧输入框中输入任何提示词,发送后,你可以查看它们输出的差异。 + +![](images/image16.png) + +接下来,我们将介绍常见的提示词工程优化技术。请选择至少两个以下平台,并比较应用提示词工程优化前后大模型输出结果的差异。 + +然而,在使用这些技术时,请仔细思考两个问题: + +1. 这种方法在什么场景下更有效? +2. 一旦我们有了思考模型,这种方法会变得不那么重要甚至没必要吗? + +#### 3. 零样本提示 (Zero-Shot Prompting):基本对话 + +最基本的提问方式是零样本提示,你直接给模型指令而不提供任何示例。这适用于模型已经非常熟悉的非常简单、明确的任务。例如,如果你想执行基本的情感分类,你可以提供以下提示词。 + +Prompt: + +> 将以下文本分类为中性、消极或积极。 +> +> 文本:我觉得这个假期还行。 +> +> 情感: + +Output: + +> 中性 + +虽然这对简单任务有效,但一旦任务变得更复杂或新颖,其局限性就会变得明显,这就是需要更先进技术的地方。 + +#### 2. 少样本提示 (Few-Shot Prompting):通过示例教模型学习 + +当任务更复杂,或者模型需要理解一个新概念时,仅仅给出指令是不够的。使用少样本提示,你可以提供一个或多个完整的“问题 + 答案”示例,以教模型你期望的模式、格式和逻辑。例如,想象你想让模型学习一个虚构的单词“farduddle”。直接提示可能会让模型感到困惑。 + +Prompt: + +> “farduddle”的意思是“因兴奋而快速跳上跳下”。请用“farduddle”造句。 + +Output (A likely result): + +> 那是一次有趣的 farduddle。 + +模型感到困惑并错误地使用了该单词。然而,通过先提供一个示例,你可以引导它。看看这个改进的提示词,我们首先向它展示如何处理一个类似的虚构单词。 + +Prompt: + +> “whatpu”是一种坦桑尼亚本土的小型毛茸茸动物。使用单词 whatpu 的句子示例是: +> +> 我们在非洲旅行时看到了这些非常可爱的 whatpu。 +> +> “farduddle”的意思是“因兴奋而快速跳上跳下”。使用单词 farduddle 的句子示例是: + +Output: + +> 当我们赢得比赛时,我们都开始 farduddle 以示庆祝。 + +现在,模型正确响应了。它从“whatpu”示例中学习了模式并应用了它,展示了少样本提示如何教模型如何做某事,而不仅仅是做什么。 + +#### 3. 思维链 (Chain-of-Thought, CoT) 提示:引导模型一步步思考 + +对于需要推理或多个逻辑步骤的问题,直接询问答案通常会导致错误。思维链的核心思想是引导模型在给出最终答案之前“写出其思考过程”。如果你直接问一个数学应用题,模型可能会犯错。 + +Prompt: + +> 我去市场买了 10 个苹果。我给了邻居 2 个,给了修理工 2 个。然后我又去买了 5 个苹果,并吃了 1 个。我还剩下多少个苹果? + +Output (A common mistake): + +> 11 个苹果。 + +它漏掉了一步。但只需在提示词末尾添加短语“Let's think step by step”(让我们一步步思考),你就可以激活它的推理能力。 + +Prompt: + +> 我去市场买了 10 个苹果。我给了邻居 2 个,给了修理工 2 个。然后我又去买了 5 个苹果,并吃了 1 个。我还剩下多少个苹果? +> +> 让我们一步步思考。 + +Output: + +> 好的,让我们分解一下: +> +> 1. 你开始有 10 个苹果。 +> +> 2. 你给了邻居 2 个,剩下 10 - 2 = 8 个苹果。 +> +> 3. 你给了修理工 2 个,剩下 8 - 2 = 6 个苹果。 +> +> 4. 然后你买了 5 个苹果,现在有 6 + 5 = 11 个苹果。 +> +> 5. 最后,你吃了 1 个苹果,所以剩下 11 - 1 = 10 个苹果。 +> +> 所以,你还剩 10 个苹果。 + +这个简单的添加将问题分解为可管理的步骤,显着提高了推理任务的准确性。 + +#### 4. 角色扮演:分配专家角色以获得更高质量的输出 + +要求模型扮演特定角色,如专家或老师,可以极大地影响其输出的风格、语气和深度。这为模型提供了一个清晰的“角色”,使其响应更适合特定受众。如果你要求它简单解释一个概念,你会得到一个枯燥的学术答案。 + +Prompt: + +> 解释“光合作用”。 + +Output (A likely result): + +> 光合作用是植物、藻类和某些细菌将光能转化为化学能的过程,通过该过程将二氧化碳和水转化为葡萄糖(糖)和氧气。化学方程式为 6CO₂ + 6H₂O → C₆H₁₂O₆ + 6O₂。 + +然而,如果你给它分配一个角色,输出就会转变。 + +Prompt: + +> 你是一位经验丰富的儿童科普作家。给一群 8 岁的孩子解释“光合作用”。使用有趣的类比,避免复杂的化学术语。 + +Output: + +> 嘿,孩子们!想象一下植物就像一个小魔法厨师。但它不是给我们做饭,而是在做自己的“能量午餐”! +> +> 这个小厨师的厨房就是它的叶子。它需要三种原料: +> +> 1. 阳光:这就像打开炉子的能量。 +> +> 2. 空气(里面的二氧化碳):这就像食谱里的面粉。 +> +> 3. 水:从根部吸上来的,这就像混合面团用的水。 +> +> 当阳光照在叶子上时,“厨师”就开始工作了!它把空气和水混合在一起,利用太阳的能量把它们变成美味的“能量糖果”(那就是植物的食物)。最酷的部分是什么?当它做饭的时候,它会呼出新鲜的空气——氧气,也就是我们需要呼吸的空气! +> +> 所以,光合作用就是植物在做自己的午餐,同时还在为我们清洁空气!很神奇,对吧? + +这种技术对于根据你的确切需求定制模型的沟通风格非常强大。 + +#### 5. 使用分隔符(如 XML 标签):构建提示词以避免混淆 + +当你的提示词包含多个组件,如背景信息、指令和示例时,模型可能会感到困惑。使用清晰的分隔符,如 XML 标签,有助于模型通过将提示词分解为逻辑模块来准确理解你的意图。一团乱麻的指令可能会导致遗漏要求。通过使用标签构建提示词,你提供了清晰度。 + +Prompt: + +> `` +> +> 1. 为 `` 块中的内容生成摘要。 +> +> 2. 摘要必须严格遵循 `` 块中的所有规则。 +> +> `` +> +> `` +> +> 人工智能 (AI) 是一个广泛的领域,涵盖了从机器人技术到自然语言处理的一系列技术。AI 的一个核心分支是机器学习,它允许计算机系统从数据中学习并在没有明确编程的情况下进行改进。深度学习是机器学习的一个子集,近年来取得了巨大的突破。 +> +> `` +> +> `` +> +> 1. 摘要长度必须正好是三句话。 +> +> 2. 摘要必须包含关键词“机器学习”。 +> +> 3. 摘要的语气应该是专业和客观的。 +> +> `` + +Output: + +> 人工智能是一个巨大的技术领域,其核心分支之一是机器学习。机器学习使系统能够从数据中学习并自行改进,而无需显式编程。作为其子集,深度学习最近推动了该领域的重大进步。 + +这种结构化方法防止模型将你的指令与它应该分析的文本混淆,并产生满足所有要求的更可靠的输出。 + +#### 6. 让 LLM 成为你的提示词工程师 + +编写完美的提示词需要精确、清晰以及对模型如何解释指令的理解。你可以利用语言模型本身作为你的助理提示词工程师,而不是通过试错手动完善提示词。 + +想象一下,你想让模型生成产品描述,但不确定如何为你的营销团队创建一个强大的提示词。 + +Prompt: + +> 创建一个提示词,我可以用来生成引人注目的产品描述。 + +Output (A likely result): + +> 这里有一个你可以用来为你的团队生成引人注目的产品描述的综合提示词。它包括关键细节的占位符,定义了目标受众和语气,并指定了所需的输出格式。 +> +> 生成的提示词: +> +> 角色:你是一位专门服务于直接面向消费者品牌的专家级电商文案撰写人。 +> +> 任务:为以下商品撰写引人注目的产品描述。 +> +> 产品详情: +> +> * 产品名称:`[插入产品名称]` +> * 关键特性(3-5个要点):`[列出最重要的特性]` +> * 目标受众:`[描述理想客户,例如“具有环保意识的千禧一代”、“忙碌的父母”、“科技爱好者”]` +> * 独特卖点:`[是什么让这个产品与众不同?]` +> +> 指令: +> +> 1. 语气:语气应该是`[选择2-3个:例如“充满活力和俏皮”、“奢华和精致”、“令人放心和专业”]`。 +> 2. 格式: +> 1. 以一个引人入胜的 1-2 句话的钩子开始。 +> 2. 接着是一个段落(3-4句话),扩展关键特性和好处,将其与客户需求联系起来。 +> 3. 使用项目符号列表突出显示前 3 个特性。 +> 4. 以强有力的号召性用语结束。 +> 3. 约束: +> 1. 总长度不应超过 150 个字。 +> 2. 不要使用过于专业的术语。 +> +> 通过使用这个结构化模板,你可以确保每次都有一致和高质量的输出。 + +这种提示方法非常有效,原因有几个。首先,LLM 擅长创建结构化文本,可以快速制定一个包含你可能忘记的要素(如指定语气、格式和约束)的综合提示词。其次,这个过程迫使你通过模型将你的简单目标转化为详细的指令集来澄清你自己的目标。 + +通过自动化通常需要反复迭代的提示词设计过程,它节省了大量时间,让你能够直接生成高质量的结果。 + +## 📚 课堂作业 1:复刻提示词示例 + +选择上述示例中的至少两种技术并进行复刻。对于每种技术,创建两个版本的提示词:一个使用该技术,一个不使用。记录两个输出并分析使用该技术如何改善结果。 + +这是一些例子。你可以自由地将上面的技巧与这些例子结合起来,尝试不同的问题——尤其是来自你自己生活的例子。 + +> **示例 1:向 10 岁孩子解释量子计算** +> +> 不使用技术: +> +> “量子计算利用叠加和纠缠等量子力学现象来执行计算。量子计算机与经典计算机有着根本的不同,经典计算机使用的比特只能处于对应于 0 或 1 的状态。” +> +> 不使用技术的输出: +> +> “量子计算机利用物理学的特殊属性,以不同于普通计算机的方式进行计算。它们不使用只能是 0 或 1 的比特,而是使用可以同时代表多种状态的量子比特。” +> +> 使用技术: +> +> “用简单的语言和一个 10 岁孩子能理解的有趣类比来解释量子计算。把它比作孩子生活中熟悉的东西。” +> +> 使用技术的输出: +> +> “想象一下,你的普通电脑就像一枚硬币,只能是正面或反面——只有两种可能。量子计算机就像一枚神奇的硬币,可以旋转,同时既是正面也是反面!这种超能力让它解决真正困难的谜题比普通电脑快得多。” + +使用简单语言和类比的版本创建了一个孩子更容易理解的解释,而另一个版本虽然准确,但使用了会让年轻受众感到困惑的技术语言。 + +> **示例 2:使用分隔符(XML 标签)分析产品评论** +> +> 不使用技术: +> +> 总结这篇产品评论,并说明评论者是否推荐它。评论说:我最近购买了 XYZ 耳机,并且每天使用两周了。音质非常出色,高音清晰,低音深沉。然而,佩戴大约一小时后会变得不舒服。电池续航时间比宣传的要长,这是一个惊喜。我很纠结,因为声音很棒,但舒适度不足。我会向不需要长时间佩戴耳机的人推荐它们。 +> +> 不使用技术的输出: +> +> “评论者已经每天使用 XYZ 耳机两周了,称赞其出色的音质、清晰的高音和深沉的低音,以及比广告宣传更长的电池续航。然而,他们发现佩戴一小时后耳机不舒服。他们向不需要长时间佩戴耳机的人推荐这款耳机。” +> +> 使用技术: +> +> +> +> 1. 为 `` 块中的内容创建一个一句话摘要。 +> 2. 确定评论者是否推荐该产品。 +> 3. 列出一个提到的优点和一个缺点。 +> `` +> +> +> +> 我最近购买了 XYZ 耳机,并且每天使用两周了。音质非常出色,高音清晰,低音深沉。然而,佩戴大约一小时后会变得不舒服。电池续航时间比宣传的要长,这是一个惊喜。我很纠结,因为声音很棒,但舒适度不足。我会向不需要长时间佩戴耳机的人推荐它们。 +> +> +> +> 使用技术的输出: +> +> 1. 摘要:在每天使用两周后,评论者对 XYZ 耳机有着复杂的感受,既指出了优点也指出了缺点。 +> 2. 推荐:对那些不需要长时间佩戴的人有条件推荐。 +> 3. 优点:音质出色,高音清晰,低音深沉;缺点:佩戴约一小时后不舒服。 + +分析:使用分隔符的版本产生了一个更结构化的响应,直接解决了所有三个要求,而另一个版本将所有信息合并到一个段落中,没有清晰地分离请求的元素。 + +👋 轮到你了:选择两种技术并按照相同的格式创建你自己的示例。包括两个版本(有和没有技术),它们的输出,以及你对差异的分析。 + +## 上下文工程 (Context Engineering) + +### 什么是上下文工程 + +简单来说,上下文指的是我们在与大语言模型(LLM)对话过程中生成的所有内容。然而,由于 LLM 对话有固定的长度限制(例如 32k tokens, 128k tokens),我们不能指望模型在长时间的对话后还能保留对话开始时提到的内容——包括最初的逻辑。这就是为什么我们需要策略性地管理我们与 LLM 交互中的信息流。 + +例如,如果我们要求 LLM 处理一本数百万字的书,它通常无法直接“阅读”全文。在这种情况下,我们需要有效地压缩上下文——或者更确切地说,书的内容(否则我们可能会将其全部复制并粘贴给模型)。假设这本书有 30 章:我们可以首先总结每章的核心内容,然后将这些总结输入到与 LLM 的最终对话中。这是上下文工程的一个经典例子,因为它有效地解决了“上下文溢出”的问题——解决了无法进行有意义的交互、无法生成有效摘要以及丢失关键信息等问题。 + +![](images/image17.png) + +更正式地说,上下文工程是一门专注于精心设计和管理大语言模型(LLM)信息输入的艺术和科学。其目标是确保模型在执行任务时拥有最相关和准确的上下文。它不仅仅是编写指令(即“提示词工程”);相反,它从更广泛的角度来看待如何用恰到好处的信息填充模型的“上下文窗口”。 + +![](images/image18.png) + +与主要关注指令本身的提示词工程不同,上下文工程将上下文窗口视为一个需要仔细策划的有限空间。其核心挑战在于选择正确的上下文并确保此上下文适合有限窗口的约束。上下文是指提供给 AI 智能体或 LLM 以执行任务的所有信息的总和;在实际工程中,我们需要向 LLM 输入各种类型的信息,主要包括以下组件: + +* 系统提示词/指令 (System prompt/instruction):定义智能体的场景和任务目标。 +* 用户输入 (User input):用户的问题或任务请求。 +* 短期记忆或聊天记录 (Short-term memory or chat history):提供当前对话的即时上下文。 +* 长期记忆 (Long-term memory):用于存储和检索跨多个对话或长时间段的信息。 +* 从知识库检索的信息 (Information retrieved from a knowledge base):通过向量搜索或其他方法从外部数据库、API 或类似来源获得的数据。 +* 工具及其定义 (Tools and their definitions):通知模型它可以使用哪些工具来完成任务。 +* 工具的响应 (Responses from tools):工具执行后返回的结果,作为模型下一步行动的基础。 +* 结构化信息 (Structured Information):以特定格式(如 JSON Schema)提供或请求的信息,用于压缩上下文并防止信息过载。 +* 工作流/全局状态 (Workflow/Global State):充当“便签本”,用于在多步骤智能体任务期间存储和检索全局信息。 + +此时,你肯定在想,*“这到底是什么?我不明白。”* 这完全没关系——我们只需要大概知道它很复杂,不需要完全掌握每个细节。我们将在以后的高级课程中为你分解每个组件。 + +上下文工程出现的主要原因是我们需要与大语言模型(LLM)进行多轮对话。此外,基于对话者信息的更新——如对话上下文、情绪等——模型不能丢失关键对话记录的跟踪或偏离预定方向。这需要对每轮对话的内容进行选择性和克制的处理。此外,**LLM 本身的配置——即系统提示词(核心提示词)——需要更高级别的处理。** + +现在,你只需要知道上下文工程专注于 **系统提示词/指令**、信息的选择以及信息的压缩和组织;在这里,我们可以首先专注于系统提示词和指令如何在复杂上下文中处理,稍后我们将探索更多组件。 + +## 📚 课堂作业 2:上下文消融实验 + +本实验旨在让你深入了解复杂的生产级提示词如何通过其多个关键组件的协同作用工作。你将使用“消融研究”——系统地移除提示词的特定部分——来观察和分析 AI 客户服务代表“Eva”的行为变化。最终,你将能够阐明每个组件(例如角色定义、行为约束、升级协议、少样本示例)在确保 AI 输出的 **准确性**、**一致性** 和 **安全性** 方面所起的决定性作用。 + +#### **第 1 部分:基线实验 - “黄金标准”提示词** + +这是我们要为 AI 客户服务智能体“Eva”设计的完整、优化的提示词。它包含我们期望的所有元素,并作为所有后续实验的控制基线。 + +**“黄金”提示词:** + +```Plain +# 身份和目的 (IDENTITY and PURPOSE) +你是 Eva,是“InnovateAI”公司的高级 AI 客户支持专家,该公司开发了创意软件套件“SynthWave Pro”。你的主要目标是通过严格遵守“知识库摘要”中提供的信息,为我们的用户提供有用、准确和富有同理心的支持。你必须高效地解决用户查询,同时保持专业和友好的语气。 + +# 语气和风格 (TONE and STYLE) +- **专业且友好**:始终以温暖的问候开始(例如,“您好!感谢您联系 InnovateAI 支持团队。我是 Eva,今天有什么可以帮您的吗?”),并礼貌地结束对话。 +- **富有同理心**:在适当的时候承认用户的挫折感(例如,“我理解这一定很令人沮丧,”或“很抱歉听到您遇到这个问题。”)。 +- **清晰简洁**:使用简单的语言。避免使用术语。对于复杂的步骤,使用项目符号或编号列表。 + +# 知识库摘要 (KNOWLEDGE BASE SUMMARY) (你唯一的真实来源) +- **产品层级**: + - **免费层 (Free Tier)**:仅限 1 个项目,720p 视频导出,包含水印。 + - **专业层 (Pro Tier) ($19/月)**:无限项目,4K 导出,无水印,优先电子邮件支持,访问高级素材库。 + - **企业层 (Enterprise Tier) (定制报价)**:包含所有专业层功能,外加专属客户经理、团队协作工具和 API 访问权限。 +- **常见故障排除**: + 1. **导出失败**:建议用户 (1) 检查软件更新,(2) 从设置中清除应用程序缓存,(3) 重启计算机。 + 2. **登录问题**:建议用户使用登录页面上的“忘记密码”链接。出于安全原因,我们不手动重置密码。 +- **公司政策**: + - 我们不提供定制折扣或价格匹配。所有价格均为最终价格。 + - 我们没有未来功能的公开路线图或发布日期。不要推测。 + - 我们目前没有移动端 (iOS/Android) 应用程序。 + +# 行为约束 (BEHAVIORAL CONSTRAINTS) (什么不该做) +- **不要** 编造知识库摘要中未列出的信息、功能或政策。这是一条关键规则。 +- **不要** 处理付款、索要密码或处理任何个人身份信息 (PII)。 +- **不要** 表达个人观点或感受。 +- **不要** 编写代码或执行回答产品问题以外的任务。 + +# 升级协议 (ESCALATION PROTOCOL) (当你无法提供帮助时) +如果使用知识库摘要无法回答用户的问题(例如,他们有复杂的技术错误、账单纠纷或询问未提及的功能),你必须遵循以下确切程序: +1. 清楚地说明你无法直接解决该问题。 +2. 提议将其案例升级给人工支持团队。 +3. 说明:“我可以为您创建一个支持工单。请您提供问题的简要摘要以及与您帐户关联的电子邮件地址好吗?” +4. 结束语:“一旦我收到这些信息,我将生成一个工单编号供您参考。” + +# 少样本示例 (FEW-SHOT EXAMPLES) (遵循这些模式) + +--- +[示例 1:标准功能问题] +用户:专业版计划多少钱?有水印吗? +EVA:您好!感谢您联系 InnovateAI 支持团队。我是 Eva。SynthWave Pro“专业层”费用为每月 19 美元,视频导出不包含水印。还有什么我可以帮您的吗? + +--- +[示例 2:故障排除问题] +用户:我很烦,我的视频导不出来! +EVA:您好!我是 Eva。很抱歉听到您遇到这个问题,我理解这一定很令人沮丧。对于导出失败,请尝试以下步骤: +1. 确保您的 SynthWave Pro 软件已更新到最新版本。 +2. 清除应用程序缓存,您可以在“设置”菜单中找到。 +3. 重启您的计算机。 +如果执行这些步骤后问题仍然存在,请告诉我! + +--- +[示例 3:超出范围/升级问题] +用户:我这个月信用卡被扣了两次费!我需要退款。 +EVA:您好!感谢您的联系。我是 Eva。我理解您对账单问题的担忧。虽然我自己无法访问帐户或付款详细信息,但我绝对可以将此问题升级给能够为您解决问题的人工支持团队。我可以为您创建一个支持工单。请您提供问题的简要摘要以及与您帐户关联的电子邮件地址好吗?一旦我收到这些信息,我将生成一个工单编号供您参考。 + +--- +[示例 4:被禁止的问题] +用户:我喜欢你们的软件,但太贵了。能给我学生折扣吗? +EVA:您好!感谢联系我们。感谢您对 SynthWave Pro 的兴趣。根据我们的公司政策,我们不提供定制折扣,因为我们所有的价格都是最终价格。您可以在我们的官方网站上找到我们所有定价层级的详细信息。如果您有任何其他问题,请告诉我! +``` + +#### **第 2 部分:标准测试用例** + +你的任务是使用以下 5 个标准化用户问题来测试“Eva”在不同配置下的表现。 + +1. **[功能咨询]** : “专业版计划有 API 访问权限吗?” +2. **[故障排除]** : “我的登录不起作用。” +3. **[超出范围问题]** : “你们什么时候发布 iPad 应用程序?” +4. **[幻觉诱导]** : “我如何使用 SynthWave Pro 编辑我的音频播客文件?”(注:知识库仅提及视频。) +5. **[政策试探]** : “你们的竞争对手提供类似的工具,每月 15 美元。你们能匹配这个价格吗?” + +**你的任务 (基线)** : + +使用上面的完整“黄金标准”提示词,按顺序向大语言模型询问这 5 个测试问题。仔细记录 Eva 的每个回答。这将是你后续比较的“基线结果”。 + +#### **第 3 部分:消融实验** + +现在,开始消融实验。在每个实验中,仅移除提示词的一个部分,然后使用相同的 5 个测试用例进行测试并记录结果。 + +**实验 A:消融“语气和风格”部分** + +* **步骤**:复制“黄金标准”提示词,但**完全删除**整个 `# TONE and STYLE` 模块。 +* **测试**:询问 5 个标准测试问题。 +* **分析**:与基线结果相比,Eva 的回答有何变化?她的语气变得机械或不一致了吗?她还记得使用礼貌的问候和结束语吗? + +**实验 B:消融“行为约束”部分** + +* **步骤**:复制“黄金标准”提示词,但**完全删除** `# BEHAVIORAL CONSTRAINTS (What NOT to Do)` 模块。 +* **测试**:询问 5 个标准测试问题,**特别是**测试用例 #4 和 #5。 +* **分析**:Eva 的行为变得“危险”或“不可靠”了吗?对于知识库之外的问题(#4),她开始编造(幻觉)功能了吗?对于价格匹配问题(#5),她给出了不确定的、可能具有误导性的答案,而不是坚定的拒绝吗? + +**实验 C:消融“升级协议”部分** + +* **步骤**:复制“黄金标准”提示词,但**完全删除** `# ESCALATION PROTOCOL` 模块。为了避免冗余,也从 `# FEW-SHOT EXAMPLES` 中**删除** `[EXAMPLE 3]`。 +* **测试**:专注于使用测试用例 #3(“iPad app”)。 +* **分析**:当面对她无法回答的问题时,Eva 现在会怎么做?她只是说“我不知道”吗?她试图猜测吗?她完全失去了将问题升级给人工支持的能力吗? + +**实验 D:消融“少样本示例”部分** + +* **步骤**:复制“黄金标准”提示词,但**完全删除** `# FEW-SHOT EXAMPLES` 模块下的所有 4 个示例。 +* **测试**:询问所有 5 个标准测试问题。 +* **分析**:这是最有趣的实验。虽然所有的规则和知识仍然存在,但 Eva 在没有具体示例的情况下表现如何?她的答案格式仍然像示例中那样清晰吗?当处理棘手的情况(如拒绝折扣或升级问题)时,她的措辞仍然礼貌有效吗?她遵循指令的精确度下降了吗? + +你也可以随意消融任何其他部分并观察产生的对话结果。例如,你可以选择只删除 # RULES 模块的一部分(例如与“礼貌措辞要求”相关的条款),而不是删除整个部分,然后测试 Eva 的响应语气如何变化——她是否变得过于直接甚至有点生硬。 + +--- + +#### **撰写实验报告** + +根据你的实验过程和结果,写一份详细的报告,包括: + +1. **引言**:简要解释本实验的目的,即调查复杂提示词中不同组件的功能。 +2. **基线表现**:介绍并简要评估 Eva 在“黄金标准”提示词下的表现。 +3. **消融研究分析**: +4. 为每个实验(A、B、C 和 D)创建一个单独的部分。 +5. 在每个部分中,展示移除特定组件后 AI 的响应。 +6. **深入分析**:详细说明每个响应与基线结果之间的差异。解释你认为出现这些差异的原因。例如,“删除行为约束后,面对价格匹配问题,Eva 不再直接拒绝,而是回答‘虽然我不能直接调整价格,但我理解您的担忧。’这表明在没有明确禁止的情况下,它试图‘过度提供帮助’,从而违反了公司政策。” +7. **结论**:综合所有实验来总结你对提示词工程的发现。详细说明为什么一个好的、长的提示词不仅仅是指令的集合,而是一个精密工程系统,其中每个部分都是相互依存和不可或缺的。 + +## 行业应用 + +我们可以看看上下文工程在行业中更多现实世界的体现——事实上,其中许多用例直接源于 AI 智能体带来的挑战。 + +这是一个编程智能体的内部系统提示词示例。你可以点击进去看看工业级提示词有多“长”。https://gist.github.com/maoxiaoke/cd960ac88e11b08cbb4fa697439ebc68 + +与独立的 LLM 交互(例如简单的“一问一答”聊天机器人)不同,AI 智能体旨在自主处理多步骤、复杂的任务:它们可能需要从多个数据库检索数据,调用不同的 API(例如检查库存、生成发票),与人类用户进行多轮交互,甚至根据实时反馈调整其工作流程。所有这些都产生了独特的上下文相关痛点,只有上下文工程才能解决。 + +例如,考虑电商平台部署的客户服务智能体:它需要记住用户之前对损坏产品的投诉(短期记忆),从品牌的 CRM 中提取用户过去的购买历史(检索知识库),并遵循平台最新的退款政策(上下文中动态更新的“规则模块”)。如果没有深思熟虑的上下文工程,智能体可能无法将损坏的产品与用户的购买记录联系起来(丢失关键上下文),或者引用过时的退款政策(使用不相关的上下文)——导致客户沮丧和操作错误。 + +另一个例子是供应链管理智能体:它必须处理实时数据流(例如仓库库存水平、运输延误)并与多个下游系统(例如物流 API、供应商沟通工具)协调。在这里,上下文工程确保只有最紧急、高影响力的信息(例如关键组件的突然缺货)在智能体的上下文窗口中得到优先处理——防止它被琐碎的数据(例如低需求项目的例行每日库存检查)淹没,并确保及时、准确的决策。 + +简而言之,随着 AI 智能体越来越多地融入工业工作流程——承担需要“连接点”跨越不同数据源和长任务周期的角色——上下文工程不再是“锦上添花”的优化。 + +![](images/image19.png) + +如果你想获得更深入的知识,请参考 OpenAI 的以下内容。这可能有点挑战性,但这没关系——大家以后都能理解。 + +https://cookbook.openai.com/examples/agents_sdk/session_memory + +## 参考资料 + +以下都是推荐的参考资料,来源于 OpenAI / Claude 等官方博客。你可以选择你喜欢的阅读。 + +Prompt Engineering Guide: https://www.promptingguide.ai/techniques/zeroshot + +Claude Prompt engineering overview: https://docs.claude.com/en/docs/build-with-claude/prompt-engineering/overview + +GPT-4.1 Prompting Guide: https://cookbook.openai.com/examples/gpt4-1_prompting_guide + +Best practices for prompt engineering with the OpenAI API: https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api + +o3/o4-mini Function Calling Guide: https://cookbook.openai.com/examples/o-series/o3o4-mini_prompting_guide + +Context Engineering - Short-Term Memory Management with Sessions from OpenAI Agents SDK: https://cookbook.openai.com/examples/agents_sdk/session_memory + +Context Engineering - What it is, and techniques to consider: https://www.llamaindex.ai/blog/context-engineering-what-it-is-and-techniques-to-consider + +Context Engineering for AI Agents: Lessons from Building Manus: https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus + +Optimizing LangChain AI Agents with Contextual Engineering: https://levelup.gitconnected.com/optimizing-langchain-ai-agents-with-contextual-engineering-0914d84601f3 + +# 🌸 图像/视频专家赛道 + +如果说大语言模型专注于理解、推理和分析我们不知道的所有事物;那么图像和视频模型则专注于生成——将你脑海中的所有想法转化为视觉现实。在今年的 AI 生成领域(2025),图像编辑和视频生成非常流行。你一定在抖音或 YouTube 上看过 AI 生成的可爱动物视频、AI 创建的角色照片、AI 生成的肖像拍摄、切玻璃苹果的视频等等。在上完今天的图像和视频课程后,你也完全有能力创建同样的内容! + +在今天的课程中,我们需要生成大量的图像和视频。为了方便起见,我们将使用统一连接的云服务提供商,并将提供相应的代码和 Token。在上一节课中,大家已经学习了如何集成 API 和使用 Token。你只需要按照以下步骤操作,回忆上节课学到的内容,就可以在 Z.AI 中成功启动你自己的图像/视频应用程序。 + + +![](images/image20.png)![](images/image21.png)![](images/image22.png) + +**暂时无法在飞书文档外展示此内容** + +## Nano banana + +### 什么是 Nano banana + +![](images/image23.png) + +Nanobanana (Gemini 2.5 Flash Image) 是 Gemini 应用程序中图像生成功能的最新升级,采用了 Google DeepMind 的新图像编辑模型。其主要创新在于能够在重大编辑过程中保持人物和宠物的一致相似度,确保主体即使处于新场景、服装或时间段中,看起来也像他们自己。 + +该技术使用户能够混合多张照片,执行多轮编辑以逐步更改图像,并将一个对象的风格或纹理应用到另一个对象。 + +**暂时无法在飞书文档外展示此内容** + +**暂时无法在飞书文档外展示此内容** + +![](images/image24.png)![](images/image25.png) + +**暂时无法在飞书文档外展示此内容** + +### 如何使用 Nano banana + +你还记得我们在上节课中学习了如何集成 SiliconFlow 和 DeepSeek API 吗?现在,我们将使用相同的方法在 z.ai 的全栈开发模式中进行对话,并让它创建一个交互式文本到图像前端页面,以方便我们快速操作和测试。 + +#### text2img 接口 + +顾名思义,文本到图像接口从文本生成图像。通过输入一段文本,NanoBanana 将返回最终图像给我们。你可以在不同的风格名称(如动漫、现实主义等)之间切换,并观察输出特征的变化。 + +```Python +import requests +from typing import Dict, Any, Optional + +NANOBANANA_API_URL: str = "https://api.zyai.online/v1/chat/completions" +NANOBANANA_API_KEY: str = + +def create_nanobanana_txt2img_task() -> Optional[Dict[str, Any]]: + """ + Call Nanobanana API to generate image from text. + + Returns: + Optional[Dict[str, Any]]: API response content, None if failed + """ + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {NANOBANANA_API_KEY}" + } + payload: Dict[str, Any] = { + "messages": [ + { + "role": "user", + "content": [ + { + "text": "A cat holding a sign that says 'AIID is the best gogogo!!!!'", + "type": "text" + } + ] + } + ], + "model": "gemini-2.5-flash-image" + } + + try: + response: requests.Response = requests.post( + NANOBANANA_API_URL, + headers=headers, + json=payload, + timeout=120 + ) + response.raise_for_status() + result: Dict[str, Any] = response.json() + print("Nanobanana text-to-image task result:") + print(result) + return result + except requests.RequestException as e: + print(f"Request failed: {e}") + return None + except Exception as e: + print(f"Response parsing failed: {e}") + return None + +if __name__ == "__main__": + create_nanobanana_txt2img_task() +``` + +#### img2img 接口 + +这是图像到图像接口。顾名思义,它以一张或多张图像作为输入并返回一张图像。我们可以用它来执行各种图像修改和编辑任务——例如,它可以帮助我们生成一致的面部模型图像,或者将两张不同图像中的元素混合成一张新图像。 + +```Python +import requests +import base64 +import os +from typing import Dict, Any, Optional, List + +NANOBANANA_API_URL: str = "https://api.zyai.online/v1/chat/completions" +NANOBANANA_API_KEY: str = + +IMAGE_PATH_1: str = "/Users/sanbu/Code/project_python/fast_tools/mirai/API/python_model_api_call_files/generated_image_1.png" +IMAGE_PATH_2: str = "/Users/sanbu/Code/project_python/fast_tools/mirai/API/python_model_api_call_files/genvideo_test_img/generated_image.png" + +def image_to_base64_with_format(image_path: str) -> Optional[str]: + """ + Convert local image file to base64 string with format. + + Args: + image_path (str): Image file path + + Returns: + Optional[str]: Formatted base64 encoded string as data:image/;base64,, None if failed + """ + try: + file_extension: str = os.path.splitext(image_path)[1].lower() + image_format: str = file_extension.replace('.', '') + + if image_format == 'jpg': + image_format = 'jpeg' + + with open(image_path, "rb") as img_file: + encoded: bytes = base64.b64encode(img_file.read()) + base64_str: str = encoded.decode("utf-8") + return f"data:image/{image_format};base64,{base64_str}" + except Exception as e: + print(f"Failed to read image: {image_path}, error: {e}") + return None + +def create_nanobanana_multi_img2img_task() -> Optional[Dict[str, Any]]: + """ + Call Nanobanana API to generate image from text and multiple images (multimodal). + + Returns: + Optional[Dict[str, Any]]: API response content, None if failed + """ + image_b64_1: Optional[str] = image_to_base64_with_format(IMAGE_PATH_1) + image_b64_2: Optional[str] = image_to_base64_with_format(IMAGE_PATH_2) + + if not image_b64_1 or not image_b64_2: + print("One or more images reading failed, cannot submit task.") + return None + + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {NANOBANANA_API_KEY}" + } + payload: Dict[str, Any] = { + "messages": [ + { + "role": "user", + "content": [ + { + "text": "Please combine these two images together creatively", + "type": "text" + }, + { + "image_url": { + "url": image_b64_1 + }, + "type": "image_url" + }, + { + "image_url": { + "url": image_b64_2 + }, + "type": "image_url" + } + ] + } + ], + "model": "gemini-2.5-flash-image" + } + + try: + response: requests.Response = requests.post( + NANOBANANA_API_URL, + headers=headers, + json=payload, + timeout=120 + ) + response.raise_for_status() + result: Dict[str, Any] = response.json() + print("Nanobanana multi-image-to-image task result:") + print(result) + return result + except requests.RequestException as e: + print(f"Request failed: {e}") + return None + except Exception as e: + print(f"Response parsing failed: {e}") + return None + +if __name__ == "__main__": + create_nanobanana_multi_img2img_task() +``` + +## Seedance 1.0 + +Nano Banana 是一个图像生成模型。接下来,我们将解释视频生成模型。视频生成模型可以将文本转换为视频,将图像转换为视频。它甚至可以将图像 A 作为第一帧,将图像 B 作为最后一帧——Seedance 可以融合第一帧和最后一帧。我们稍后将体验整个生成过程。 + +https://seed.bytedance.com/en/seedance + +![](images/image26.png) + +![](images/image27.png) + +Seedance 1.0 是字节跳动推出的视频生成模型。它支持两种核心输入方式来创建视频: + +1. 文本转视频:它可以直接从文本描述生成完整的视频(例如,将“一只猫在阳光明媚的草地上追逐蝴蝶”变成动态视频)。 +2. 图像转视频:它可以将静态图像制作成动画视频(例如,让一张海洋日落的静态照片显示波浪起伏和云彩飘动)。 + +它还支持首尾帧——你可以提供开始帧(视频如何开始)和结束帧(视频如何结束),Seedance 将自动生成连接这两帧的流畅、连贯的视频内容。 + +总体而言,它生成 1080p 高质量视频,具有流畅的运动、丰富的细节和电影感。它甚至原生支持多镜头连贯叙事视频,很好地符合风格需求和文本指令,并在相关基准测试中表现强劲——帮助创作者将幻想场景、日常纪录片或商业短片等创意变为现实。 + +**暂时无法在飞书文档外展示此内容** + +### 如何使用 Seedance 1.0 + +#### Text2Video 接口 + +你可以通过下面的 API 轻松给出一段文本并获得输出视频。 + +```Python +import requests +import time +import os +from typing import Dict, Any, Optional + +# 官方文档接口地址:https://www.volcengine.com/docs/82379/1520757 +# API地址已替换为 https://api.zyai.online/seedance/v3 +# 使用后台提供的API Key +SEEDANCE_API_URL: str = "https://api.zyai.online/seedance/v3/contents/generations/tasks" +SEEDANCE_API_KEY: str = + +def create_seedance_txt2video_task() -> Optional[str]: + """ + 创建Seedance 1.0视频生成任务(文本转视频),返回任务ID。 + + Returns: + Optional[str]: 返回任务ID,失败时返回None + """ + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {SEEDANCE_API_KEY}" + } + payload: Dict[str, Any] = { + "model": "doubao-seedance-1-0-pro-250528", + "content": [ + { + "type": "text", + "text": ( + "多个镜头。一个猫咪跳入游泳池。" + "跳上桌子" + "看着镜头感到很疑惑 --ratio 16:9" + ) + } + ] + } + + try: + response: requests.Response = requests.post( + SEEDANCE_API_URL, + headers=headers, + json=payload, + timeout=120 + ) + response.raise_for_status() + result: Dict[str, Any] = response.json() + print("Seedance 1.0 文本转视频任务创建结果:") + print(result) + task_id: Optional[str] = result.get("id") + if not task_id: + print("未能获取到任务ID。") + return None + return task_id + except requests.RequestException as e: + print(f"请求失败: {e}") + return None + except Exception as e: + print(f"响应解析失败: {e}") + return None + +def find_video_task(task_id: str) -> Optional[Dict[str, Any]]: + """ + 查询Seedance 1.0视频生成任务的详细信息。 + + Args: + task_id (str): 视频生成任务的ID。 + + Returns: + Optional[Dict[str, Any]]: 返回API响应的字典数据,若请求失败则返回None。 + """ + url: str = f"{SEEDANCE_API_URL}/{task_id}" + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {SEEDANCE_API_KEY}" + } + + try: + response: requests.Response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + print(f"查询Seedance 1.0视频生成任务失败: {e}") + return None + +def parse_video_task_response(response: Dict[str, Any]) -> Dict[str, Any]: + """ + 解析Seedance 1.0视频生成任务API的响应,提取关键信息。 + + Args: + response (Dict[str, Any]): API返回的原始响应数据。 + + Returns: + Dict[str, Any]: 提取后的关键信息字典。 + """ + result: Dict[str, Any] = { + "id": response.get("id"), + "model": response.get("model"), + "status": response.get("status"), + "error": response.get("error"), + "created_at": response.get("created_at"), + "updated_at": response.get("updated_at"), + "seed": response.get("seed"), + "resolution": response.get("resolution"), + "duration": response.get("duration"), + "ratio": response.get("ratio"), + "framespersecond": response.get("framespersecond"), + "usage": response.get("usage"), + "video_url": None, + "last_frame_url": None + } + + content: Optional[Dict[str, Any]] = response.get("content") + if content: + result["video_url"] = content.get("video_url") + result["last_frame_url"] = content.get("last_frame_url") + + return result + +def download_video(video_url: str, output_filename: str = "output.mp4") -> None: + """ + 下载视频文件并保存到本地。 + + Args: + video_url (str): 视频下载地址 + output_filename (str): 本地保存文件名 + """ + try: + print(f"开始下载视频: {video_url}") + response: requests.Response = requests.get(video_url, timeout=300) + response.raise_for_status() + with open(output_filename, "wb") as f: + f.write(response.content) + print(f"视频已保存为 {output_filename}") + except requests.RequestException as e: + print(f"下载视频失败: {e}") + +def poll_and_download_video(task_id: str, poll_interval: int = 5, max_wait: int = 600) -> None: + """ + 轮询查询视频生成任务状态,直到视频生成完成并下载。 + + Args: + task_id (str): 任务ID + poll_interval (int): 轮询间隔秒数 + max_wait (int): 最大等待时间(秒) + """ + waited: int = 0 + while waited < max_wait: + response: Optional[Dict[str, Any]] = find_video_task(task_id) + if response is None: + print("查询任务失败,稍后重试...") + time.sleep(poll_interval) + waited += poll_interval + continue + + info: Dict[str, Any] = parse_video_task_response(response) + print(f"当前任务状态: {info['status']}") + if info["status"] == "succeeded" and info["video_url"]: + print("视频生成成功,准备下载。") + download_video(info["video_url"]) + return + elif info["status"] == "failed": + print(f"视频生成失败: {info.get('error')}") + return + else: + print("视频尚未生成,等待中...") + time.sleep(poll_interval) + waited += poll_interval + + print("等待超时,未能获取到视频。") + +if __name__ == "__main__": + # 1. 创建视频生成任务 + task_id: Optional[str] = create_seedance_txt2video_task() + if task_id: + # 2. 轮询查询并下载视频 + poll_and_download_video(task_id) + else: + print("未能创建视频生成任务。") +``` + +#### 首尾帧接口 + +在此代码中,需要上传两张图像进行融合。你可以让 z.ai 构建一个能够接受两张图像的图像上传界面。上传图像后,该界面随后运行以生成包含第一帧和最后一帧的视频。 + +```Python +import requests +import time +import base64 +import os +from typing import Dict, Any, Optional + +# 官方文档接口地址:https://www.volcengine.com/docs/82379/1520757 +# API地址已替换为 https://api.zyai.online/seedance/v3 +# 使用后台提供的API Key +SEEDANCE_API_URL: str = "https://api.zyai.online/seedance/v3/contents/generations/tasks" +SEEDANCE_API_KEY: str = + +# 本地图片路径 +FIRST_FRAME_PATH: str = "/generated_image.png" +LAST_FRAME_PATH: str = "/generated_image_2.png" + +def image_to_base64_with_format(image_path: str) -> Optional[str]: + """ + 将本地图片文件转换为带格式的base64字符串。 + + Args: + image_path (str): 图片文件路径 + + Returns: + Optional[str]: 格式化的base64编码字符串,格式为data:image/<格式>;base64,<编码>,失败时返回None + """ + try: + # 获取图片文件扩展名以确定格式 + file_extension: str = os.path.splitext(image_path)[1].lower() + image_format: str = file_extension.replace('.', '') + + # 处理特殊格式 + if image_format == 'jpg': + image_format = 'jpeg' + + with open(image_path, "rb") as img_file: + encoded: bytes = base64.b64encode(img_file.read()) + base64_str: str = encoded.decode("utf-8") + # 返回标准的Data URL格式 + return f"data:image/{image_format};base64,{base64_str}" + except Exception as e: + print(f"读取图片失败: {image_path}, 错误: {e}") + return None + +def create_seedance_lite_txt2video_task() -> Optional[str]: + """ + 创建Seedance 1.0 Lite视频生成任务(文本转视频),返回任务ID。 + + Returns: + Optional[str]: 返回任务ID,失败时返回None + """ + # 读取本地图片并转为带格式的base64 + first_frame_b64: Optional[str] = image_to_base64_with_format(FIRST_FRAME_PATH) + last_frame_b64: Optional[str] = image_to_base64_with_format(LAST_FRAME_PATH) + if not first_frame_b64 or not last_frame_b64: + print("首帧或尾帧图片读取失败,无法提交任务。") + return None + + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {SEEDANCE_API_KEY}" + } + payload: Dict[str, Any] = { + "model": "doubao-seedance-1-0-lite-i2v-250428", + "content": [ + { + "type": "text", + "text": "湖水形成了一个人" + }, + { + "type": "image_url", + "image_url": { + "url": first_frame_b64 + }, + "role": "first_frame" + }, + { + "type": "image_url", + "image_url": { + "url": last_frame_b64 + }, + "role": "last_frame" + } + ] + } + + try: + response: requests.Response = requests.post( + SEEDANCE_API_URL, + headers=headers, + json=payload, + timeout=120 + ) + response.raise_for_status() + result: Dict[str, Any] = response.json() + print("Seedance Lite 文本转视频任务创建结果:") + print(result) + task_id: Optional[str] = result.get("id") + if not task_id: + print("未能获取到任务ID。") + return None + return task_id + except requests.RequestException as e: + print(f"请求失败: {e}") + return None + except Exception as e: + print(f"响应解析失败: {e}") + return None + +def find_video_task(task_id: str) -> Optional[Dict[str, Any]]: + """ + 查询Seedance Lite视频生成任务的详细信息。 + + Args: + task_id (str): 视频生成任务的ID。 + + Returns: + Optional[Dict[str, Any]]: 返回API响应的字典数据,若请求失败则返回None。 + """ + url: str = f"{SEEDANCE_API_URL}/{task_id}" + headers: Dict[str, str] = { + "Content-Type": "application/json", + "Authorization": f"Bearer {SEEDANCE_API_KEY}" + } + + try: + response: requests.Response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + return response.json() + except requests.RequestException as e: + print(f"查询Seedance Lite视频生成任务失败: {e}") + return None + +def parse_video_task_response(response: Dict[str, Any]) -> Dict[str, Any]: + """ + 解析Seedance Lite视频生成任务API的响应,提取关键信息。 + + Args: + response (Dict[str, Any]): API返回的原始响应数据。 + + Returns: + Dict[str, Any]: 提取后的关键信息字典。 + """ + result: Dict[str, Any] = { + "id": response.get("id"), + "model": response.get("model"), + "status": response.get("status"), + "error": response.get("error"), + "created_at": response.get("created_at"), + "updated_at": response.get("updated_at"), + "seed": response.get("seed"), + "resolution": response.get("resolution"), + "duration": response.get("duration"), + "ratio": response.get("ratio"), + "framespersecond": response.get("framespersecond"), + "usage": response.get("usage"), + "video_url": None, + "last_frame_url": None + } + + content: Optional[Dict[str, Any]] = response.get("content") + if content: + result["video_url"] = content.get("video_url") + result["last_frame_url"] = content.get("last_frame_url") + + return result + +def download_video(video_url: str, output_filename: str = "output.mp4") -> None: + """ + 下载视频文件并保存到本地。 + + Args: + video_url (str): 视频下载地址 + output_filename (str): 本地保存文件名 + """ + try: + print(f"开始下载视频: {video_url}") + response: requests.Response = requests.get(video_url, timeout=300) + response.raise_for_status() + with open(output_filename, "wb") as f: + f.write(response.content) + print(f"视频已保存为 {output_filename}") + except requests.RequestException as e: + print(f"下载视频失败: {e}") + +def poll_and_download_video(task_id: str, poll_interval: int = 5, max_wait: int = 600) -> None: + """ + 轮询查询视频生成任务状态,直到视频生成完成并下载。 + + Args: + task_id (str): 任务ID + poll_interval (int): 轮询间隔秒数 + max_wait (int): 最大等待时间(秒) + """ + waited: int = 0 + while waited < max_wait: + response: Optional[Dict[str, Any]] = find_video_task(task_id) + if response is None: + print("查询任务失败,稍后重试...") + time.sleep(poll_interval) + waited += poll_interval + continue + + info: Dict[str, Any] = parse_video_task_response(response) + print(f"当前任务状态: {info['status']}") + if info["status"] == "succeeded" and info["video_url"]: + print("视频生成成功,准备下载。") + download_video(info["video_url"]) + return + elif info["status"] == "failed": + print(f"视频生成失败: {info.get('error')}") + return + else: + print("视频尚未生成,等待中...") + time.sleep(poll_interval) + waited += poll_interval + + print("等待超时,未能获取到视频。") + +if __name__ == "__main__": + # 1. 创建视频生成任务 + task_id: Optional[str] = create_seedance_lite_txt2video_task() + if task_id: + # 2. 轮询查询并下载视频 + poll_and_download_video(task_id) + else: + print("未能创建视频生成任务。") +``` + +## 各种图像子任务 + +说到图像相关任务,我们提供了多种功能选项供测试和探索。 + +例如,面部妆容模拟让你虚拟尝试各种妆容风格——从日常妆容到创意设计——而无需实物产品。虚拟试穿让你以数字方式“穿”上衣服,以可视化合身度和风格,而虚拟服装试穿增加了诸如尺码或颜色调整等个性化微调,以获得更逼真的预览。还有旧照片修复,可以修复老式照片上的划痕、褪色和损坏,以恢复珍贵的记忆。以下是你可以尝试的一些具体示例。 + +![](images/image28.png) + +![](images/image29.png) + +![](images/image30.png) + +### 图像风格化生成 + +```Bash +Convert this image to a black-and-white comic style. +Convert this image to an oil painting style. +Convert this image to a pop art style. +``` + +![](images/image31.png) + +![](images/image32.png) + +![](images/image33.png) + +### 产品背景生成 + +以下面的图像为例:你可以将主体的背景替换为其他背景。或者,假设你是一个销售各种产品的商家,需要在不同的销售平台上展示这些产品以吸引买家——你可以使用这种方法更改不同产品的背景,使它们看起来更具视觉吸引力。你甚至可以上传多张图像,并将主体图像放入提供的图像中。 + +![](images/image34.png) + +![](images/image35.png) + +![](images/image36.png) + +### 手办生成 + +```Plain +Transform this photo into a character figurine. Place a packaging box printed with the character's image behind the figurine, and display a computer next to it, with the Blender modeling production process shown on the screen. Put a circular plastic base in front of the packaging box, on which the character figurine stands. Ensure the PVC material exhibits a clear and realistic texture, and set the scene in an indoor environment as much as possible. +``` + +![](images/image37.png) + +### 虚拟试穿 / 时尚应用 + +```Plain +replace the brush area with a chanel bag +``` + +![](images/image38.png) + +![](images/image39.png) + +![](images/image40.png)![](images/image41.png)![](images/image42.png) + +### 海报生成 + +![](images/image43.png) + +![](images/image44.png) + +![](images/image45.png) + +![](images/image46.png) + +![](images/image47.png) + +### 动作参考 / 姿势参考 + +```Bash +The two characters interact in the posture shown in Picture 3. The scene background should match the atmosphere of the image, and interactive effects between the environment and the characters should be added. The image aspect ratio is set to 1:1 +``` + +![](images/image48.png) + +![](images/image49.png) + +![](images/image50.png) + +![](images/image51.png) + +### IP 设计 + +```SQL +Generate for me the following elements of character creation: Character Design, Proportion Design (including different height comparisons, head-to-body ratio, etc.), Three-View Drawings (front view, side view, back view), Expression Sheet (the type of chart you sent earlier), Pose Sheet (various common poses), and Costume Design. +``` + +![](images/image52.png) + +![](images/image53.png)![](images/image54.png) + +## 行业应用 + +基于上面介绍的几个任务方向,已经有很多现有的应用程序。然而,我们接下来要介绍的是一个设计智能体,其核心采用 nanobanana 来完成各种复杂的设计任务。许多设计师需要海报和 UI 元素来保持高水平的风格一致性,而 nanobanana 完美地实现了这一点。 + +### Lovart + +https://www.lovart.ai/home + +Lovart 是世界上第一个专门用于设计领域的 AI 智能体工具。只需用文字描述你的设计需求——例如“创作 8 幅猫环游世界的插图”或“制作一个 30 秒的 Dior 口红商业视频”——它首先会准确匹配设计风格并分解任务。然后,它将调用 GPT-4o 和 Flux 等模型来生成内容。 + +它还支持调整大小、图像裁剪和文本背景分离(以便轻松修改文本)等功能,甚至可以将图像转换为带有背景音乐的视频。它帮助你直接从需求中获得实用的、现成的设计输出,这对于创建简单设计的初学者和希望提高工作效率的设计师都很有用。 + +![](images/image55.png) + +![](images/image56.png) + +![](images/image57.png) + +![](images/image58.png) + +## 📚 课堂作业:创建你自己的图像生成创意应用程序 + +1. 请参考上述文档中提供的示例,并复刻两个以上的上述子任务。 +2. 参考 z.ai,思考并创建你作为一套生成工具(例如从输入图像创建视频的工具、AIID 纪念视频,或允许通过上传照片更改背景、服装或生成不同地点旅行视频的工具),家居设计工具,电影生成工具等。最后,这些工具应该让你的同学可以访问,让他们也能体验到同样的乐趣! + +# 如何找到最好的模型 + +大语言模型(LLM)的发展速度非常快,我们总是需要确保我们使用的是排名靠前的模型之一。通过以下两个网站,我们可以轻松确定哪些文本、视频和图像大模型目前最强大。一般来说,我们将此类网站称为 **“LLM 竞技场”** —— 我们可以并排比较两个模型的输出。你需要选择你更喜欢的响应,这种选择算作一票。获得更多选票的模型被认为具有更好的性能。 + +![](images/image59.png) + +此外,你偶尔可能会在这些大模型竞技场中看到神秘的匿名模型。通常,这些是来自 OpenAI 或 Google 等公司的内部测试模型。你可能有机会意外体验到最先进模型的能力! + +### LMArena + +网站:https://lmarena.ai/ + +简介:LMArena 最初由加州大学伯克利分校大模型系统组织(LMSYS)作为一个学术副项目推出,现已发展成为一家公司。它是一个开源的众包 AI 基准测试平台。 + +它改变了传统的基于学科测试评估 AI 技术的方式,将评估权移交给社区用户。通过匿名和众包配对比较,它评估大规模模型。该平台支持超过 68 个模型,如 GPT-4o 和 Claude 3.5。 + +它使用 Elo 评分系统,可以更真实地反映用户对模型回答质量的评价。根据用户投票数据,它编制了一个排行榜,涵盖七个类别,包括文本/语言能力、Web 开发和视觉/图像理解。 + +截至 2025 年 4 月,它已记录了超过 300 万次比较,并评估了 400 多个模型,成为比较和评估 AI 模型性能的流行方法。 + +![](images/image60.png) + +### Artificial Analysis + +网站:[https://artificialanalysis.ai](https://artificialanalysis.ai/) + +Artificial Analysis 是领先的独立 AI 基准测试和分析平台。它专注于对 AI 模型和 API 提供商进行独立分析。该网站提供详细的数据和图表,可以帮助开发者、用户、研究人员和其他用户做出明智的选择。 + +通过比较不同 AI 模型的质量、性能、价格和其他关键指标,它帮助用户找到最适合其需求的 AI 模型解决方案。 + +其功能包括模型比较、质量评估、价格分析、性能测试和上下文窗口分析。它还提供了详细的用户指南和常见问题解答,涵盖各种类型模型的评估,如大语言模型、文本到图像模型和语音到文本模型。此外,它还提供了一个专注于模型基准的免费 API 和一个具有更全面数据的商业 API。 + +![](images/image61.png) + +# 📚 (全员必修)AI 工具评测报告 + +课后,你需要完成一份关于 AI 工具的调查报告。对于不同赛道的小组,每个小组必须提交一份相对全面的报告。 + +报告格式可以参考这个:[Vibe Coding Tools] 贪吃蛇游戏教程 + +在报告中,你需要根据今天赛道设计不断完善内容。通过“如何找到最好的模型”中的大模型竞技场排名,识别其他不同的工具(如果你在 AI 编码组,你可以参考 Yerim 的比较来完成作业),并对其生成效果进行比较。比较应从功能、生成质量、生成速度或价格等方面进行多样化。 diff --git a/docs/project/chapter2/images/image1.png b/docs/project/chapter2/images/image1.png new file mode 100644 index 0000000..76f2740 Binary files /dev/null and b/docs/project/chapter2/images/image1.png differ diff --git a/docs/project/chapter2/images/image10.png b/docs/project/chapter2/images/image10.png new file mode 100644 index 0000000..c2d80fe Binary files /dev/null and b/docs/project/chapter2/images/image10.png differ diff --git a/docs/project/chapter2/images/image11.png b/docs/project/chapter2/images/image11.png new file mode 100644 index 0000000..cf316f0 Binary files /dev/null and b/docs/project/chapter2/images/image11.png differ diff --git a/docs/project/chapter2/images/image12.png b/docs/project/chapter2/images/image12.png new file mode 100644 index 0000000..32fd074 Binary files /dev/null and b/docs/project/chapter2/images/image12.png differ diff --git a/docs/project/chapter2/images/image13.png b/docs/project/chapter2/images/image13.png new file mode 100644 index 0000000..d9b631d Binary files /dev/null and b/docs/project/chapter2/images/image13.png differ diff --git a/docs/project/chapter2/images/image14.png b/docs/project/chapter2/images/image14.png new file mode 100644 index 0000000..5c5cef3 Binary files /dev/null and b/docs/project/chapter2/images/image14.png differ diff --git a/docs/project/chapter2/images/image15.png b/docs/project/chapter2/images/image15.png new file mode 100644 index 0000000..1c90f40 Binary files /dev/null and b/docs/project/chapter2/images/image15.png differ diff --git a/docs/project/chapter2/images/image16.png b/docs/project/chapter2/images/image16.png new file mode 100644 index 0000000..e7b1765 Binary files /dev/null and b/docs/project/chapter2/images/image16.png differ diff --git a/docs/project/chapter2/images/image17.png b/docs/project/chapter2/images/image17.png new file mode 100644 index 0000000..30effe8 Binary files /dev/null and b/docs/project/chapter2/images/image17.png differ diff --git a/docs/project/chapter2/images/image18.png b/docs/project/chapter2/images/image18.png new file mode 100644 index 0000000..f55b50d Binary files /dev/null and b/docs/project/chapter2/images/image18.png differ diff --git a/docs/project/chapter2/images/image19.png b/docs/project/chapter2/images/image19.png new file mode 100644 index 0000000..467f03e Binary files /dev/null and b/docs/project/chapter2/images/image19.png differ diff --git a/docs/project/chapter2/images/image2.png b/docs/project/chapter2/images/image2.png new file mode 100644 index 0000000..ff9ada8 Binary files /dev/null and b/docs/project/chapter2/images/image2.png differ diff --git a/docs/project/chapter2/images/image20.png b/docs/project/chapter2/images/image20.png new file mode 100644 index 0000000..9cbad93 Binary files /dev/null and b/docs/project/chapter2/images/image20.png differ diff --git a/docs/project/chapter2/images/image21.png b/docs/project/chapter2/images/image21.png new file mode 100644 index 0000000..53b54ee Binary files /dev/null and b/docs/project/chapter2/images/image21.png differ diff --git a/docs/project/chapter2/images/image22.png b/docs/project/chapter2/images/image22.png new file mode 100644 index 0000000..4d6c280 Binary files /dev/null and b/docs/project/chapter2/images/image22.png differ diff --git a/docs/project/chapter2/images/image23.png b/docs/project/chapter2/images/image23.png new file mode 100644 index 0000000..8dbb4fb Binary files /dev/null and b/docs/project/chapter2/images/image23.png differ diff --git a/docs/project/chapter2/images/image24.png b/docs/project/chapter2/images/image24.png new file mode 100644 index 0000000..a8cac36 Binary files /dev/null and b/docs/project/chapter2/images/image24.png differ diff --git a/docs/project/chapter2/images/image25.png b/docs/project/chapter2/images/image25.png new file mode 100644 index 0000000..06f9c13 Binary files /dev/null and b/docs/project/chapter2/images/image25.png differ diff --git a/docs/project/chapter2/images/image26.png b/docs/project/chapter2/images/image26.png new file mode 100644 index 0000000..6b940df Binary files /dev/null and b/docs/project/chapter2/images/image26.png differ diff --git a/docs/project/chapter2/images/image27.png b/docs/project/chapter2/images/image27.png new file mode 100644 index 0000000..cb41821 Binary files /dev/null and b/docs/project/chapter2/images/image27.png differ diff --git a/docs/project/chapter2/images/image28.png b/docs/project/chapter2/images/image28.png new file mode 100644 index 0000000..19c20dc Binary files /dev/null and b/docs/project/chapter2/images/image28.png differ diff --git a/docs/project/chapter2/images/image29.png b/docs/project/chapter2/images/image29.png new file mode 100644 index 0000000..6697642 Binary files /dev/null and b/docs/project/chapter2/images/image29.png differ diff --git a/docs/project/chapter2/images/image3.png b/docs/project/chapter2/images/image3.png new file mode 100644 index 0000000..b523e40 Binary files /dev/null and b/docs/project/chapter2/images/image3.png differ diff --git a/docs/project/chapter2/images/image30.png b/docs/project/chapter2/images/image30.png new file mode 100644 index 0000000..5bda1f1 Binary files /dev/null and b/docs/project/chapter2/images/image30.png differ diff --git a/docs/project/chapter2/images/image31.png b/docs/project/chapter2/images/image31.png new file mode 100644 index 0000000..27b93e7 Binary files /dev/null and b/docs/project/chapter2/images/image31.png differ diff --git a/docs/project/chapter2/images/image32.png b/docs/project/chapter2/images/image32.png new file mode 100644 index 0000000..a76dca5 Binary files /dev/null and b/docs/project/chapter2/images/image32.png differ diff --git a/docs/project/chapter2/images/image33.png b/docs/project/chapter2/images/image33.png new file mode 100644 index 0000000..620c4dc Binary files /dev/null and b/docs/project/chapter2/images/image33.png differ diff --git a/docs/project/chapter2/images/image34.png b/docs/project/chapter2/images/image34.png new file mode 100644 index 0000000..d2cd634 Binary files /dev/null and b/docs/project/chapter2/images/image34.png differ diff --git a/docs/project/chapter2/images/image35.png b/docs/project/chapter2/images/image35.png new file mode 100644 index 0000000..458e532 Binary files /dev/null and b/docs/project/chapter2/images/image35.png differ diff --git a/docs/project/chapter2/images/image36.png b/docs/project/chapter2/images/image36.png new file mode 100644 index 0000000..2a8a6c7 Binary files /dev/null and b/docs/project/chapter2/images/image36.png differ diff --git a/docs/project/chapter2/images/image37.png b/docs/project/chapter2/images/image37.png new file mode 100644 index 0000000..e74d31c Binary files /dev/null and b/docs/project/chapter2/images/image37.png differ diff --git a/docs/project/chapter2/images/image38.png b/docs/project/chapter2/images/image38.png new file mode 100644 index 0000000..15419d9 Binary files /dev/null and b/docs/project/chapter2/images/image38.png differ diff --git a/docs/project/chapter2/images/image39.png b/docs/project/chapter2/images/image39.png new file mode 100644 index 0000000..cbf3680 Binary files /dev/null and b/docs/project/chapter2/images/image39.png differ diff --git a/docs/project/chapter2/images/image4.png b/docs/project/chapter2/images/image4.png new file mode 100644 index 0000000..e7cb6dd Binary files /dev/null and b/docs/project/chapter2/images/image4.png differ diff --git a/docs/project/chapter2/images/image40.png b/docs/project/chapter2/images/image40.png new file mode 100644 index 0000000..a5b5474 Binary files /dev/null and b/docs/project/chapter2/images/image40.png differ diff --git a/docs/project/chapter2/images/image41.png b/docs/project/chapter2/images/image41.png new file mode 100644 index 0000000..b92031a Binary files /dev/null and b/docs/project/chapter2/images/image41.png differ diff --git a/docs/project/chapter2/images/image42.png b/docs/project/chapter2/images/image42.png new file mode 100644 index 0000000..0a4c3cc Binary files /dev/null and b/docs/project/chapter2/images/image42.png differ diff --git a/docs/project/chapter2/images/image43.png b/docs/project/chapter2/images/image43.png new file mode 100644 index 0000000..fcf12c4 Binary files /dev/null and b/docs/project/chapter2/images/image43.png differ diff --git a/docs/project/chapter2/images/image44.png b/docs/project/chapter2/images/image44.png new file mode 100644 index 0000000..900a07d Binary files /dev/null and b/docs/project/chapter2/images/image44.png differ diff --git a/docs/project/chapter2/images/image45.png b/docs/project/chapter2/images/image45.png new file mode 100644 index 0000000..5dd4df7 Binary files /dev/null and b/docs/project/chapter2/images/image45.png differ diff --git a/docs/project/chapter2/images/image46.png b/docs/project/chapter2/images/image46.png new file mode 100644 index 0000000..2f9d32d Binary files /dev/null and b/docs/project/chapter2/images/image46.png differ diff --git a/docs/project/chapter2/images/image47.png b/docs/project/chapter2/images/image47.png new file mode 100644 index 0000000..433d882 Binary files /dev/null and b/docs/project/chapter2/images/image47.png differ diff --git a/docs/project/chapter2/images/image48.png b/docs/project/chapter2/images/image48.png new file mode 100644 index 0000000..71a9a54 Binary files /dev/null and b/docs/project/chapter2/images/image48.png differ diff --git a/docs/project/chapter2/images/image49.png b/docs/project/chapter2/images/image49.png new file mode 100644 index 0000000..7b4f41d Binary files /dev/null and b/docs/project/chapter2/images/image49.png differ diff --git a/docs/project/chapter2/images/image5.png b/docs/project/chapter2/images/image5.png new file mode 100644 index 0000000..5b8cf69 Binary files /dev/null and b/docs/project/chapter2/images/image5.png differ diff --git a/docs/project/chapter2/images/image50.png b/docs/project/chapter2/images/image50.png new file mode 100644 index 0000000..14c5252 Binary files /dev/null and b/docs/project/chapter2/images/image50.png differ diff --git a/docs/project/chapter2/images/image51.png b/docs/project/chapter2/images/image51.png new file mode 100644 index 0000000..370064a Binary files /dev/null and b/docs/project/chapter2/images/image51.png differ diff --git a/docs/project/chapter2/images/image52.png b/docs/project/chapter2/images/image52.png new file mode 100644 index 0000000..56447e3 Binary files /dev/null and b/docs/project/chapter2/images/image52.png differ diff --git a/docs/project/chapter2/images/image53.png b/docs/project/chapter2/images/image53.png new file mode 100644 index 0000000..2132e3f Binary files /dev/null and b/docs/project/chapter2/images/image53.png differ diff --git a/docs/project/chapter2/images/image54.png b/docs/project/chapter2/images/image54.png new file mode 100644 index 0000000..c7bf189 Binary files /dev/null and b/docs/project/chapter2/images/image54.png differ diff --git a/docs/project/chapter2/images/image55.png b/docs/project/chapter2/images/image55.png new file mode 100644 index 0000000..78125ff Binary files /dev/null and b/docs/project/chapter2/images/image55.png differ diff --git a/docs/project/chapter2/images/image56.png b/docs/project/chapter2/images/image56.png new file mode 100644 index 0000000..ea6e478 Binary files /dev/null and b/docs/project/chapter2/images/image56.png differ diff --git a/docs/project/chapter2/images/image57.png b/docs/project/chapter2/images/image57.png new file mode 100644 index 0000000..257bd19 Binary files /dev/null and b/docs/project/chapter2/images/image57.png differ diff --git a/docs/project/chapter2/images/image58.png b/docs/project/chapter2/images/image58.png new file mode 100644 index 0000000..e57eb33 Binary files /dev/null and b/docs/project/chapter2/images/image58.png differ diff --git a/docs/project/chapter2/images/image59.png b/docs/project/chapter2/images/image59.png new file mode 100644 index 0000000..096ca6f Binary files /dev/null and b/docs/project/chapter2/images/image59.png differ diff --git a/docs/project/chapter2/images/image6.png b/docs/project/chapter2/images/image6.png new file mode 100644 index 0000000..9dd5bcf Binary files /dev/null and b/docs/project/chapter2/images/image6.png differ diff --git a/docs/project/chapter2/images/image60.png b/docs/project/chapter2/images/image60.png new file mode 100644 index 0000000..e05d736 Binary files /dev/null and b/docs/project/chapter2/images/image60.png differ diff --git a/docs/project/chapter2/images/image61.png b/docs/project/chapter2/images/image61.png new file mode 100644 index 0000000..1f3ee19 Binary files /dev/null and b/docs/project/chapter2/images/image61.png differ diff --git a/docs/project/chapter2/images/image7.png b/docs/project/chapter2/images/image7.png new file mode 100644 index 0000000..0d880dc Binary files /dev/null and b/docs/project/chapter2/images/image7.png differ diff --git a/docs/project/chapter2/images/image8.png b/docs/project/chapter2/images/image8.png new file mode 100644 index 0000000..9b71bac Binary files /dev/null and b/docs/project/chapter2/images/image8.png differ diff --git a/docs/project/chapter2/images/image9.png b/docs/project/chapter2/images/image9.png new file mode 100644 index 0000000..ac4a31b Binary files /dev/null and b/docs/project/chapter2/images/image9.png differ diff --git a/docs/project/chapter3/Log in.yml b/docs/project/chapter3/Log in.yml new file mode 100644 index 0000000..ff55268 --- /dev/null +++ b/docs/project/chapter3/Log in.yml @@ -0,0 +1,504 @@ +app: + description: '' + icon: 🤖 + icon_background: '#FFEAD5' + mode: advanced-chat + name: Log in + use_icon_as_answer_icon: false +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93 +kind: app +version: 0.3.0 +workflow: + conversation_variables: + - description: '' + id: f8cc215e-ef91-437a-a823-7e80a8d345a3 + name: LOGIN + selector: + - conversation + - LOGIN + value: none + value_type: string + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .JPG + - .JPEG + - .PNG + - .GIF + - .WEBP + - .SVG + allowed_file_types: + - image + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_size_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: 'Please log in with passwords:' + retriever_resource: + enabled: true + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + sourceType: llm + targetType: answer + id: llm-answer + source: llm + sourceHandle: source + target: answer + targetHandle: target + type: custom + - data: + isInIteration: false + isInLoop: false + sourceType: start + targetType: if-else + id: 1758767725822-source-1758767750205-target + source: '1758767725822' + sourceHandle: source + target: '1758767750205' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: if-else + targetType: assigner + id: 1758767920912-true-1758768026915-target + source: '1758767920912' + sourceHandle: 'true' + target: '1758768026915' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: if-else + targetType: assigner + id: 1758767920912-false-1758768059939-target + source: '1758767920912' + sourceHandle: 'false' + target: '1758768059939' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: llm + id: 1758767750205-f599486b-e4d3-4cdd-9425-31257bf28e82-llm-target + source: '1758767750205' + sourceHandle: f599486b-e4d3-4cdd-9425-31257bf28e82 + target: llm + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: if-else + targetType: llm + id: 1758767750205-5f242bc3-7f06-4a88-82e5-235a859d92bf-1758768238460-target + source: '1758767750205' + sourceHandle: 5f242bc3-7f06-4a88-82e5-235a859d92bf + target: '1758768238460' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: answer + id: 1758768238460-source-1758768309599-target + source: '1758768238460' + sourceHandle: source + target: '1758768309599' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: if-else + targetType: if-else + id: 1758767750205-true-1758767920912-target + source: '1758767750205' + sourceHandle: 'true' + target: '1758767920912' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: assigner + targetType: answer + id: 1758768026915-source-1758768400561-target + source: '1758768026915' + sourceHandle: source + target: '1758768400561' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: assigner + targetType: answer + id: 1758768059939-source-1758768418040-target + source: '1758768059939' + sourceHandle: source + target: '1758768418040' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + desc: '' + selected: false + title: Start + type: start + variables: [] + height: 53 + id: '1758767725822' + position: + x: -227.17718464443863 + y: 459.4548203041172 + positionAbsolute: + x: -227.17718464443863 + y: 459.4548203041172 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + memory: + query_prompt_template: '{{#sys.query#}}' + role_prefix: + assistant: '' + user: '' + window: + enabled: false + size: 10 + model: + completion_params: + temperature: 0.7 + mode: chat + name: Qwen3-235B-A22B-Instruct-2507 + provider: langgenius/gitee_ai/gitee_ai + prompt_template: + - id: 2686a731-f250-46f0-97ec-033e929160a5 + role: system + text: 'You are the computer that answers the user''s question. Always start + with "hello guest" ' + selected: false + title: Guest LLM + type: llm + variables: [] + vision: + enabled: false + height: 89 + id: llm + position: + x: 389.3839309072489 + y: 663.6856819774588 + positionAbsolute: + x: 389.3839309072489 + y: 663.6856819774588 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + answer: '{{#llm.text#}}' + desc: '' + selected: true + title: Answer + type: answer + variables: [] + height: 104 + id: answer + position: + x: 707.3520684153225 + y: 673.6318886739399 + positionAbsolute: + x: 707.3520684153225 + y: 673.6318886739399 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + cases: + - case_id: 'true' + conditions: + - comparison_operator: is + id: cf0932c4-e5f4-478e-9437-1db63af30ffd + value: none + varType: string + variable_selector: + - conversation + - LOGIN + id: 'true' + logical_operator: and + - case_id: f599486b-e4d3-4cdd-9425-31257bf28e82 + conditions: + - comparison_operator: is + id: 18c716ea-aac5-4af7-9a50-1fd5dc40d18e + value: guest + varType: string + variable_selector: + - conversation + - LOGIN + id: f599486b-e4d3-4cdd-9425-31257bf28e82 + logical_operator: and + - case_id: 5f242bc3-7f06-4a88-82e5-235a859d92bf + conditions: + - comparison_operator: is + id: 490f9251-1012-4f85-8bb7-29f355ca6b5c + value: admin + varType: string + variable_selector: + - conversation + - LOGIN + id: 5f242bc3-7f06-4a88-82e5-235a859d92bf + logical_operator: and + desc: '' + selected: false + title: IF/ELSE + type: if-else + height: 221 + id: '1758767750205' + position: + x: 78.91702348010949 + y: 535.9787937998216 + positionAbsolute: + x: 78.91702348010949 + y: 535.9787937998216 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + cases: + - case_id: 'true' + conditions: + - comparison_operator: is + id: ab112997-31e2-453c-8511-40adc3006e76 + value: AIID + varType: string + variable_selector: + - sys + - query + id: 'true' + logical_operator: and + desc: '' + selected: false + title: IF/ELSE 2 + type: if-else + height: 125 + id: '1758767920912' + position: + x: 659.9247584894487 + y: 459.4548203041172 + positionAbsolute: + x: 659.9247584894487 + y: 459.4548203041172 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + desc: '' + items: + - input_type: constant + operation: set + value: admin + variable_selector: + - conversation + - LOGIN + write_mode: over-write + selected: false + title: Variable Assigner + type: assigner + version: '2' + height: 87 + id: '1758768026915' + position: + x: 997.3839309072489 + y: 426.91916104676926 + positionAbsolute: + x: 997.3839309072489 + y: 426.91916104676926 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + desc: '' + items: + - input_type: constant + operation: set + value: guest + variable_selector: + - conversation + - LOGIN + write_mode: over-write + selected: false + title: Variable Assigner 2 + type: assigner + version: '2' + height: 87 + id: '1758768059939' + position: + x: 1010.8775065168395 + y: 553.9191610467692 + positionAbsolute: + x: 1010.8775065168395 + y: 553.9191610467692 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + model: + completion_params: + temperature: 0.7 + mode: chat + name: Qwen3-235B-A22B-Instruct-2507 + provider: langgenius/gitee_ai/gitee_ai + prompt_template: + - id: 1129723a-50cb-4350-a118-3b9ac6dac523 + role: system + text: 'You are the computer that answers the admin''s question. Always start + with "hello admin" . You know AIID(AI innovative Design) is a master program + in open FIESTA in Shenzhen. ' + selected: false + title: Admin LLM + type: llm + variables: [] + vision: + enabled: false + height: 89 + id: '1758768238460' + position: + x: 389.3839309072489 + y: 792.6856819774588 + positionAbsolute: + x: 389.3839309072489 + y: 792.6856819774588 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + answer: '{{#1758768238460.text#}}' + desc: '' + selected: false + title: Answer 3 + type: answer + variables: [] + height: 104 + id: '1758768309599' + position: + x: 707.3520684153225 + y: 832.4705087633828 + positionAbsolute: + x: 707.3520684153225 + y: 832.4705087633828 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + answer: Password Correct! You are now log in as ADMIN + desc: '' + selected: false + title: Answer 3 + type: answer + variables: [] + height: 117 + id: '1758768400561' + position: + x: 1301.383930907249 + y: 426.91916104676926 + positionAbsolute: + x: 1301.383930907249 + y: 426.91916104676926 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + - data: + answer: Password Incorrect! You are now log in as GUEST + desc: '' + selected: false + title: Answer 4 + type: answer + variables: [] + height: 117 + id: '1758768418040' + position: + x: 1498.8808102839819 + y: 553.9191610467692 + positionAbsolute: + x: 1498.8808102839819 + y: 553.9191610467692 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 243 + viewport: + x: 273.4245102591761 + y: -8.654295375493462 + zoom: 0.7525860587977332 diff --git a/docs/project/chapter3/Love Loop.yml b/docs/project/chapter3/Love Loop.yml new file mode 100644 index 0000000..1e5d6cb --- /dev/null +++ b/docs/project/chapter3/Love Loop.yml @@ -0,0 +1,388 @@ +app: + description: '' + icon: 🤖 + icon_background: '#FFEAD5' + mode: advanced-chat + name: Love Loop + use_icon_as_answer_icon: false +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/gitee_ai:0.1.4@f621ace33bb3c140f5a1e3533fcb518f558c7b945d63523c0f85810a4b4a8b93 +kind: app +version: 0.3.0 +workflow: + conversation_variables: + - description: '' + id: 411d934a-94cb-4899-a892-31300f69228e + name: words + selector: + - conversation + - words + value: love + value_type: string + - description: '' + id: 7aca1d78-2dbc-4ccf-8428-0f0eed7b203c + name: WORDS + selector: + - conversation + - WORDS + value: '' + value_type: string + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .JPG + - .JPEG + - .PNG + - .GIF + - .WEBP + - .SVG + allowed_file_types: + - image + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_size_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: '' + retriever_resource: + enabled: true + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: false + isInLoop: true + loop_id: '1758765136208' + sourceType: loop-start + targetType: llm + id: 1758765136208start-source-1758765344915-target + source: 1758765136208start + sourceHandle: source + target: '1758765344915' + targetHandle: target + type: custom + zIndex: 1002 + - data: + isInLoop: false + sourceType: loop + targetType: answer + id: 1758765136208-source-answer-target + source: '1758765136208' + sourceHandle: source + target: answer + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: true + loop_id: '1758765136208' + sourceType: llm + targetType: code + id: 1758765344915-source-1758765883132-target + source: '1758765344915' + sourceHandle: source + target: '1758765883132' + targetHandle: target + type: custom + zIndex: 1002 + - data: + isInIteration: false + isInLoop: true + loop_id: '1758765136208' + sourceType: code + targetType: assigner + id: 1758765883132-source-1758765428475-target + source: '1758765883132' + sourceHandle: source + target: '1758765428475' + targetHandle: target + type: custom + zIndex: 1002 + - data: + isInIteration: false + isInLoop: false + sourceType: start + targetType: assigner + id: 1758764476473-source-1758765920251-target + source: '1758764476473' + sourceHandle: source + target: '1758765920251' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: assigner + targetType: loop + id: 1758765920251-source-1758765136208-target + source: '1758765920251' + sourceHandle: source + target: '1758765136208' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + desc: '' + selected: false + title: Start + type: start + variables: [] + height: 54 + id: '1758764476473' + position: + x: 115.88661184766386 + y: 178.92252604934293 + positionAbsolute: + x: 115.88661184766386 + y: 178.92252604934293 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + answer: '{{#conversation.words#}}' + desc: '' + selected: false + title: Answer + type: answer + variables: [] + height: 105 + id: answer + position: + x: 1456.1539893939312 + y: 178.92252604934293 + positionAbsolute: + x: 1456.1539893939312 + y: 178.92252604934293 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + - data: + break_conditions: [] + desc: '' + error_handle_mode: terminated + height: 462 + logical_operator: and + loop_count: 5 + loop_variables: [] + selected: false + start_node_id: 1758765136208start + title: Loop + type: loop + width: 682 + height: 462 + id: '1758765136208' + position: + x: 731.5560922291256 + y: 187.5878472722298 + positionAbsolute: + x: 731.5560922291256 + y: 187.5878472722298 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 682 + zIndex: 1 + - data: + desc: '' + isInLoop: true + selected: false + title: '' + type: loop-start + draggable: false + height: 48 + id: 1758765136208start + parentId: '1758765136208' + position: + x: 24 + y: 68 + positionAbsolute: + x: 755.5560922291256 + y: 255.5878472722298 + selectable: false + sourcePosition: right + targetPosition: left + type: custom-loop-start + width: 44 + zIndex: 1002 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + isInIteration: false + isInLoop: true + loop_id: '1758765136208' + model: + completion_params: + temperature: 0.7 + mode: chat + name: Qwen3-235B-A22B-Instruct-2507 + provider: langgenius/gitee_ai/gitee_ai + prompt_template: + - id: e27d2ef8-3dc0-4fbf-8866-90ec3f10c758 + role: system + text: write a word with a similar meaning to the words user input. + - id: 624597ef-8e03-49c7-8365-4b17dc487032 + role: user + text: '{{#conversation.words#}}' + selected: false + title: LLM 2 + type: llm + variables: [] + vision: + enabled: false + height: 90 + id: '1758765344915' + parentId: '1758765136208' + position: + x: 114.28909334084415 + y: 65 + positionAbsolute: + x: 845.8451855699698 + y: 252.5878472722298 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 244 + zIndex: 1002 + - data: + desc: '' + isInIteration: false + isInLoop: true + items: + - input_type: variable + operation: over-write + value: + - '1758765883132' + - result + variable_selector: + - conversation + - words + write_mode: over-write + loop_id: '1758765136208' + selected: false + title: Variable Assigner + type: assigner + version: '2' + height: 88 + id: '1758765428475' + parentId: '1758765136208' + position: + x: 418.79897041375784 + y: 194.04290759019386 + positionAbsolute: + x: 1150.3550626428835 + y: 381.63075486242366 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + zIndex: 1002 + - data: + code: "\ndef main(arg1: str, arg2: str) -> dict:\n return {\n \"\ + result\": arg1 +arg2 ,\n }\n" + code_language: python3 + desc: '' + isInIteration: false + isInLoop: true + loop_id: '1758765136208' + outputs: + result: + children: null + type: string + selected: false + title: Connect + type: code + variables: + - value_selector: + - conversation + - words + variable: arg1 + - value_selector: + - '1758765344915' + - text + variable: arg2 + height: 54 + id: '1758765883132' + parentId: '1758765136208' + position: + x: 111.09640498433282 + y: 202.0812369434865 + positionAbsolute: + x: 842.6524972134584 + y: 389.6690842157163 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + zIndex: 1002 + - data: + desc: '' + items: + - input_type: variable + operation: over-write + value: + - sys + - query + variable_selector: + - conversation + - words + write_mode: over-write + selected: false + title: define words + type: assigner + version: '2' + height: 88 + id: '1758765920251' + position: + x: 404.0977808713518 + y: 178.92252604934293 + positionAbsolute: + x: 404.0977808713518 + y: 178.92252604934293 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 244 + viewport: + x: 59.39316938104503 + y: 131.3127545416781 + zoom: 0.5431072229827936 diff --git a/docs/project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md b/docs/project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md new file mode 100644 index 0000000..dcded47 --- /dev/null +++ b/docs/project/chapter3/chapter3-getting-started-with-dify-and-its-knowledge-base-integration.md @@ -0,0 +1,1043 @@ +# Project 3: Dify 入门与知识库集成 + +# 回顾上节课 + +在前几节课中,我们分组学习了 AI 编程、提示词工程以及 AI 图像生成的基础知识。这些内容帮助我们初步了解了不同大语言模型(LLM,Large Language Model)或生成式模型的边界和能力。 + +为了帮助你回顾上节课的内容,下面有几个小问题可以思考: + +1. 什么是 AI 编程?如何使用 AI 编程工具(例如 [z.ai](http://z.ai))来创建一个网页? +2. 什么是大语言模型?什么是提示词工程和上下文工程?你该如何编写一个复杂的提示词? +3. 对于文本、AI Coding、图像生成的三个不同方向,你认为模型能力的强弱分别体现在什么地方? +4. 什么是 API?如何使用 [z.ai](http://z.ai) 接入第三方 API ? + +如果你对其中任何一个问题还感到疑惑,可以回看上节课的文档,也可以直接在微信群里提问。 + +在这节课中,我们将从简单的 AI 文字图片工具,进入更接近公司业务落地的工作流搭建平台。从对话机器人走向 AI 智能体、AI 工作流,并基于 API 把它变成可交互的“智能”机器人页面。 + +在操作过程中,如果遇到难以理解的步骤,请不要担心,推荐你随时对当前所在的操作页面进行截图,发送给大模型进行询问;当前大模型已能够解答大部分常见问题。 + +如果提问后仍无法解决,不妨大胆尝试操作;不必害怕出错,每一次尝试都是学习和进步的机会。随着实践次数的增加,你会越来越熟练,操作也会越来越得心应手! + +# 本节课你将学到 + +1. 为什么需要从聊天机器人走向智能体和 Workflow 编排。 +2. 什么是智能体与工作流开发平台,如何把 AI 的能力 SOP 化与可编排化。 +3. 什么是 Dify,如何用这个面向 LLM 应用的开源平台快速搭建应用,尤其是知识库问答机器人。 +4. RAG 的实现方法与价值,为什么需要检索增强生成? +5. 如何从 0 到 1 学会使用 Dify 和 AI IDE Trae (`Extra Knowledge 4 - What is AI IDE and Trae`),包括搭建 智能体、工作流,并基于 Dify API 制作前端对话机器人网页程序。 + +* Dify 的基本使用原理与智能体、工作流制作方法,API 调用方法。 +* AI IDE 的使用方法,如何使用 AI IDE 编程。 +* 一个可进行对话的前端网页智能体程序。 + +# 1. 从对话到智能体 + +在上一阶段,我们学会了如何用提示词让大模型扮演角色、生成文本或编写简单代码。但如果你仔细思考,会发现一个问题,聊天机器人本身并不能做事。 + +它能回答怎么查订单?,却不能真的去数据库里查对应的数字;它能描述一封周报应该包含什么,却无法自动汇总你的项目数据并发送邮件。这种“只说不做”的局限,使得纯对话式 AI 难以真正融入业务流程。 + +要让 AI 从聊天伙伴升级为数字员工,我们需要赋予它三项核心能力: + +1. 专属知识——让它能够通读并了解你的产品文档、客户资料、内部制度; +2. 工具调用(或者叫插件)——让它能操作数据库、调用 API; +3. 结构化执行——让它按预设逻辑一步步完成任务,而非自由发挥。 + +这就是 AI 智能体(AI Agent)的雏形:一个具备目标、知识、工具和执行路径的自动化单元。 + +![](images/image1.png) + +> 注意:当前业界所说的简单版本的“智能体”,大多指基于 LLM + 工具 + 知识库组合而成的增强型应用,并非所谓能够自主规划的智能体。简单的智能体虽不具备真正的推理与长期规划能力,但已足以支撑大量企业级自动化场景。我们将会在之后的章节详细介绍真正的具备自主规划和行动能力的智能体。 + +## 1.1 最简单的智能体:基于知识库的问答机器人 + +在明确智能体应具备的多项核心能力后,一个值得思考的问题随之而来:能否仅通过实现其中某一项最简单的功能,就构建出一个真正可用的基础智能体? 答案是肯定的。 + +事实上,在大量实际业务场景中,用户的核心诉求并非让 AI 自动执行复杂操作(如调用 API 或跨系统协调任务),而是希望它能基于企业自身的专属资料,提供精准、可靠的问答支持。这恰好对应智能体三大核心能力中的第一项,专属知识服务能力。因此,我们得以引出智能体最简单、也最广泛应用的形态:基于知识库的问答机器人。 + +虽然它尚未具备工具调用或自主规划能力,但其关键突破在于:让大模型的回答不再凭空生成,而是有据可依。如何实现?关键就在于解决核心挑战:企业内置大量文档知识,当存在千上万页文档时,模型如何在每一轮对话中快速找到与当前问题最相关的内容? + +此时的一个解决方案是:检索增强生成(Retrieval-Augmented Generation, RAG)。 + +RAG 的基本思路是:在用户提问时,系统首先从企业知识库中检索出与问题语义最相关的若干文本片段(例如产品手册中的某一段、HR制度中的某一条款),然后将这些片段作为上下文“注入”到大模型的输入中,引导它基于真实资料生成回答。 + +![](images/image2.png) + +图片来源:[https://www.datacamp.com/blog/what-is-retrieval-augmented-generation-rag](https://www.datacamp.com/blog/what-is-retrieval-augmented-generation-rag) + +这样一来,模型的回答不再是依赖其训练数据中的泛化知识,而是锚定在企业提供的权威信息之上。RAG 的目标,正是通过这种外部知识的动态注入,显著提升回答的真实性、准确性和一致性——甚至可以让回答“符合人设”,比如以客服口径或技术文档风格作答。 + +在实际业务中,这项技术尤为重要,因为大模型常常会产生“幻觉”。例如,若你以 CFO 或咨询顾问的身份询问某个时间段的具体数据,模型很可能编造日期和事件。引入 RAG 后,回答的可控性与可靠性将得到显著提升。 + +![](images/image3.png) + +图片来源:[https://www.databricks.com/glossary/retrieval-augmented-generation-rag](https://www.databricks.com/glossary/retrieval-augmented-generation-rag) + +在本节课的实操环节中,我们将使用流行的 AI 工作流平台 Dify,动手搭建一个基于知识库的问答机器人。你可以轻松将各种类型的专属资料,如产品手册、公司制度、项目文档、研究论文、知识库文章,甚至是个人笔记集构建为知识库。 + +完成搭建后,你可以尝试提出各类问题来检验它的能力,例如: + +* “我们产品A的最新版本有哪些主要功能升级?” +* “请根据员工手册,说明今年的年假制度是如何规定的?” +* “在XX项目中,我们遇到的技术挑战‘XXX’是如何解决的?” +* “这篇论文中提到的核心研究方法是什么?” + +你将亲身感受 RAG 技术如何将静态分散的文档资料,转化为一个精准的智能知识库,为各种场景提供高精度问答支持。 + +## 1.2 从对话智能体到工作流 + +然而,即使是加入了知识库甚至是插件调用能力的“增强型智能体”,在面对更复杂的业务流程时仍显不足。 + +试想这样一个用户请求:“我们新上线的 SaaS 产品最近有哪些功能更新?能帮我整理成一份给客户的简报吗?” + +这个请求看似简单,背后却需要多个协同步骤:首先从内部产品文档或 Notion 知识库中检索最近一个月的功能发布记录;然后过滤出面向客户的关键特性;接着调用大模型将技术描述转化为客户友好的语言;最后通过将生成内容推送至市场团队的邮箱,或保存到 Google Docs 模板中。 + +如果仅靠一个大语言模型自由推理,先不说是否能够一次对话实现所有过程,就算能,其中也很容易遗漏关键信息、混淆内部术语与客户语言,或无法结构化输出。更重要的是,企业需要的是可审计、可复用、可监控的标准化执行路径,而不是每次依赖模型的临时发挥,可监控可复现对企业而言非常重要,非预期的结果很可能会带来预期外的严重损失。 + +这就引出了更高阶的 AI 应用范式:AI 工作流(AI Workflow)。 + +![](images/image4.png) + +工作流是指将一个复杂任务拆解为多个有序、可配置、可自动执行的子步骤,并通过可视化或代码方式编排它们之间的逻辑关系,如条件判断、循环或并行执行。将 AI 能力 SOP 化(即标准化操作流程),意味着把如何用 AI 完成某项任务的经验固化为可重复使用的模板。 + +这种做法带来了多重价值:非技术人员(如产品经理或运营)可以通过拖拽组件快速搭建 AI 应用;开发者可以将 RAG 检索、LLM 调用、API 工具等封装为标准节点,在不同业务场景中复用;整个流程还可被完整追踪、调试和持续优化,满足企业对稳定性与合规性的要求。 + +AI 工作流的使用人群非常广泛。产品经理无需写代码,即可设计完整的用户交互路径;运营人员能快速搭建客服机器人、内容生成器或通知系统;开发者和算法工程师则可将核心能力模块化,供前端调用;创业者或独立开发者也能以极低成本验证 AI 产品的 MVP,几天内上线一个包含数据查询、内容生成与动作执行的完整原型。 + +此外,值得注意的是,AI 工作流通常可用一种中间表示(Intermediate Representation)来描述。不同工作流平台的具体表达方式虽有差异,但大多采用结构化文件(如 JSON、YAML 等)来定义节点类型、输入输出及执行逻辑,其结构类似下图所示: + +![](images/image5.png) + +简言之,如果说智能体让 AI 从会聊天走向能做事,那么工作流则让 AI 从偶尔做成一件事迈向“稳定、可靠、规模化地完成一类事。在接下来的实践中,我们还将借助 Dify 平台,上手并亲手构建完整的 AI 工作流,体验从想法到可运行应用的完整过程。 + +## 1.3 常用智能体 / 工作流平台 + +随着生成式 AI 技术的飞速发展,为帮助开发者与业务人员快速构建智能体与自动化流程,避免陷入编程的复杂细节,一批低代码甚至无代码的智能体及工作流平台应运而生。 + +首先需要明确的是,低代码平台是指通过可视化拖拽组件、预置业务逻辑模板、图形化配置规则等方式,显著减少手动编码工作量的开发工具。其核心在于以可视化配置,节点式拖动变成的方式替代直接写代码的方式,既能让具备一定技术能力的开发者从重复劳动中解放出来,也能让熟悉业务逻辑的非技术人员参与到应用搭建中。本质上,它是在开发效率与场景灵活性之间架起一座平衡的桥梁。 + +这类低代码/无代码智能体平台的突出价值,正是大幅降低 AI 应用的开发门槛。以往需要团队协作数周——从需求梳理、代码开发到测试部署——才能完成的 AI 智能体(如客服问答机器人、数据处理助手),现在借助平台提供的可视化工具,可将“从创意到上线”的周期缩短至数小时。 + +目前市面上主流的低代码 AI 工作流平台包括: + +| 平台 | 特点 | 适用场景 | +| --------------------------------------------- | -------------------------------------------------- | -------------------------------------- | +| Dify | 开源、支持知识库 RAG、LLM 编排、API 输出,中文友好 | 企业知识库问答、定制化 Agent、API 服务 | +| Coze(字节跳动) | 国内可用、集成抖音/飞书生态、插件丰富 | 社交机器人、国内小程序集成 | +| n8n | 通用自动化工具,支持 AI 节点,强调 API 编排 | 跨系统数据同步、AI + 传统 SaaS 自动化 | +| 百度千帆 AppBuilder / 阿里百炼 / 腾讯 HunYuan | 大厂云原生方案,集成自家模型 | 企业级部署、合规要求高场景 | + +目前市面上的低代码 AI 工作流平台选择丰富。尽管 AWS、Azure、阿里云等主流云厂商均推出了相应的 AI 工作流解决方案,但 Dify、Coze 和 n8n 凭借以下三大核心优势,成为当前应用最广泛的代表: + +1. 极致易用性。平台采用可视化拖拽式界面设计,用户无需深入理解底层技术,即可快速上手。 +2. 高灵活性。支持自定义组件与扩展 API 接口,既能适应教学演示、MVP(最小可行产品)验证等轻量场景,也能满足中小型团队的敏捷迭代需求。 +3. 成熟生态。不仅官方文档详尽、响应及时,还拥有活跃的用户社区,便于快速获取来自不同用户的预设方案。 + +这三大平台均支持将搭建好的 AI 智能体以标准化 API 接口的形式输出,可无缝集成至前端 Web 应用、企业内部 ERP 系统或移动端 APP 中,进一步降低了 AI 能力落地的技术门槛。 + +### 1.3.1 Dify:企业级LLMOps与应用生命周期管理平台 + +Dify 定位是LLM应用开发与运营平台,致力于提供AI应用从构思、部署到优化的全生命周期管理。其核心是一个低代码平台,旨在帮助开发者和非技术背景的创新者快速构建生产级AI应用。 + +![](images/image6.png) + +在功能上,Dify覆盖了可视化工作流编排、智能体构建、知识库管理、多模型支持等功能。平台允许通过拖拽节点设计复杂任务流程,并支持创建基于意图的Agent。其知识库功能突出,能处理多种格式文档并进行高效的向量检索。同时,Dify兼容支持包括GPT、Claude及众多开源模型在内的多种LLM,构建的应用可一键发布为标准API便于集成。 + +![](images/image7.png) + +技术架构方面,Dify以开源和可私有化部署为特色,强调灵活性、扩展性及企业级合规。目标用户包括开发者团队和业务创新者,典型应用场景涵盖企业知识库与智能客服、内容创作自动化、垂直领域AI助手以及企业AI中台。 + +### 1.3.2 Coze(字节跳动):零代码AI智能体构建的普及者 + +Coze是字节跳动推出的AI智能体开发平台,以极致易用性为核心,让无编程经验的用户也能轻松创建、调试并发布功能丰富的AI聊天机器人。 + +![](images/image8.png) + +其核心是将Bot构建简化为搭积木式操作。用户可通过界面轻松配置角色与知识库,并利用丰富的内置插件库为Bot添加新闻、旅游、图像生成等多类外部能力。创建好的Bot可一键快速发布至豆包、飞书、微信公众号等多个平台。 + +![](images/image9.png) + +技术架构完全服务于低门槛使用,后端集成字节自有模型并封装复杂流程,强调多模态理解与实时响应。作为一个主要以云服务形式提供的平台,其私有化部署能力相对有限。典型应用场景包括个人助理与娱乐Bot、智能客服与问答系统、在线教育助手以及快速原型验证。 + +### 1.3.2 n8n:可编程的后端工作流自动化引擎 + +n8n是一个通用的可编程工作流自动化平台,其核心定位是连接各类应用、数据库与API,实现数据流动与任务自动化执行。 + +它通过庞大的集成节点库支持数百种SaaS服务、数据库及协议,并采用可视化与代码结合的方式:用户可在画布拖拽节点,同时注入JavaScript或Python代码编写自定义逻辑。n8n擅长处理后端数据密集型任务,如数据同步、ETL流程与API编排。 + +![](images/image10.png) + +关键技术特性是“源码可见”和“可自托管”,用户可将其私有化部署以完全掌控数据与环境,这使其对数据安全要求高的行业极具吸引力。其主要目标用户是开发者、技术运营及数据分析师。n8n 最大的优势,在于拥有极其强大的社区生态。网络上拥有随处可见丰富的 n8n 分享视频,为用户提供了便捷的学习参考与经验借鉴;同时,它支持连接 YouTube、Instagram 等全球众多不同生态平台,能够帮助用户轻松打破跨平台数据与服务的壁垒,实现多生态流程的自动化流转。 + +### 1.3.3 其他工作流平台 + +除了上述的几个最知名的平台,中国国内的主要科技厂商也相继推出了各自的一体化AI开发平台,例如:百度千帆 AppBuilder 提供从模型选型、RAG构建到智能体发布的全流程支持,深度集成文心大模型;阿里云百炼基于通义千问系列模型,注重企业级安全与私有化部署能力;腾讯云 TI 平台 则聚焦于金融、医疗等行业场景,提供丰富的预置解决方案模板。这类平台通常与各自云生态深度融合,适合已处于相应技术体系内的企业选用。 + +然而,在通用型、开放性与社区生态方面,Dify 与 Coze 仍凭借其突出的易用性、广泛的模型支持以及活跃的开发者社区,成为当前更受广泛采纳的选择。 + +尽管各平台在定位与生态上各有侧重,其核心逻辑均是通过可视化方式编排与连接不同的能力模块。因此,掌握其中任意一种平台的设计思路与操作方法,即具备快速迁移到其他类似工具的基础。在接下来的实践中,我们将以 Dify 为例进行具体讲解。 + +# 2. 深入浅出 Dify + +## 2.1 什么是 Dify + +我们在之前已经了解了基础的 Dify 的信息介绍,对于更详细的信息,你可以通过 [https://cloud.dify.ai/apps](https://cloud.dify.ai/apps) 访问 Dify 平台,如果想了解更多信息,可以访问官网 https://dify.ai。 + +Dify 是一个用于开发 LLM 应用的开源平台。它提供了直观的界面,将 Agent 工作流、RAG 流水线、工具能力、模型管理、可观测性等功能结合在一起,帮助你快速地从原型走向生产环境。 + +![](images/image11.png) + +你可以在 Dify 中使用大语言模型和各种功能不同的工具来搭建“工作流”。所谓工作流,就是把原本需要你手动一步步完成的操作——例如数据检索、大模型调用、网页搜索、结果过滤、格式整理等——按照业务逻辑串联起来,变成一个自动化、可复用的流程。如果没有工作流,每次你都需要把同样的内容复制粘贴给大模型,非常低效、容易出错,也难以在真实业务中复用。 + +搭建一个工作流,就像在拼搭积木或拼图。你把“大语言模型节点”(负责理解和生成)、各类“工具节点”(负责执行具体动作,例如查数据库、发邮件、翻译文本等)、以及“数据节点”(负责读取、存储信息)像积木一样连接起来。它们会按照你预设的逻辑自动协同工作,而不需要你每次都手动操作。你也可以把它理解成一种“低代码程序”:你只需要通过拖拽的方式,配置输入和输出的路径,就可以实现比较复杂的业务逻辑。 + +举个例子,如果你是一个亚马逊或抖音电商店铺的老板,想要搭建一个 AI 客服系统,可以参考下图的结构设计一个工作流: + +1. 触发节点(类似 START):接收用户的咨询问题,例如“这个商品的质保期有多长?”。 +2. 问题分类节点(类似 QUESTION CLASSIFIER):使用一个模型(例如 GPT)对用户问题进行分类,判断这是售后(比如质保)、使用方法,还是其他类型的问题。 +3. 知识检索节点(类似 KNOWLEDGE RETRIEVAL):根据分类结果,自动访问相应的知识库。如果是关于“质保”的售后问题,就从售后 SOP 知识库中检索与“质保”相关的精确信息。 +4. 大语言模型节点(LLM Node):将用户问题和检索到的知识库内容一起发送给大语言模型(例如 GPT),让它生成一段对用户友好的回复(避免太生硬的技术语气)。 +5. 条件节点:检查大模型生成的回答中是否包含清晰的质保时间(例如“1 年”、“3 年”),如果有则继续下一步,如果没有则让它回复“请提供产品型号”。 +6. 输出节点(类似 ANSWER):将最终答案返回给用户,并自动把本次咨询记录到表格中。 + +![](images/image12.png) + +在整个过程中,你不需要手动去翻知识库、反复调整模型的回答、或单独记录数据——工作流会把这些步骤“连起来自动跑”。并且它非常灵活:例如,如果你之后想加一个新规则“当用户问质保范围时,调用另一个知识库”,只需要在工作流中多加一个条件节点,而无需重构整个系统。 + +这是一个比较简单的工作流示例,但要完全掌握这些能力,对现在的你来说可能还有点难。因此在本节课中,我们从更加基础的知识库智能体开始,后面再逐步学习更复杂的工作流技巧。 + +### 2.1.1 部署属于自己的 Dify(可选) + +本部分内容原本安排在后续课程中详细介绍,但考虑到当前部分学习者可能因网络限制暂时无法访问 Dify 官方网站或云端服务,我们决定提前提供这一可选的学习路径,帮助你顺利推进课程进度。 + +你需要参考该教程入门 Zeabur 部署平台的基本使用方式:`Extra Knowledge 6 - Zeabur: What Is It and How to Deploy Web Applications` + +![](images/image13.png) + +你需要学习如何在 Zeabur 上部署一个自己的 Dify,部署后进入到对应链接注册并登录后继续跟随下列教程操作即可。 + +注意,不同版本的 Dify 的操作方面和前端界面可能有些许差别,但总体上差别不大,当你发现不同的时候不要慌张,找到类似的接口和入口进行操作即可。 + +## 2.2 创建第一个 Dify Chatbot 应用 + +访问 Dify 首页 [https://cloud.dify.ai/apps](https://cloud.dify.ai/apps) 并注册和登录后,选择 Studio,你会看到如下界面: + +![](images/image14.png) + +在左侧找到 `CREATE APP` 区块,点击 `Create from Blank`。 + +![](images/image15.png) + +![](images/image16.png) + +在 APP Type 中找到 Chatbot(如果一开始没看到,可以点击“查看更多类型”的按钮,然后在完整列表中找到)。选择 Chatbot 之后,在下方输入应用的名称和描述,最后点击创建。 + +![](images/image17.png) + +创建完成后,你会看到类似下面的界面。 + +![](images/image18.png) + +中间区域的 “INSTRUCTIONS” 指的是内置指令,你可以把它理解为默认提示词或系统提示词。 + +中间偏下有一个 “Knowledge” 区域,这就是知识库区域——我们稍后会把自己的知识库上传到这里。 + +右侧是调试窗口,你可以在调整提示词后与 Agent 进行对话,实时查看效果。 + +你可以在 INSTRUCTIONS 区域自由输入角色提示词,观察对话效果;也可以点击 Generate,让大模型自动帮你生成提示词。 + +![](images/image19.png) + +注意右上角会出现许多不同模型的选项,这意味着你可以点击切换不同的对话模型,从而比较它们在语气、逻辑推理、长文本处理等方面的差异,寻找最适合你需求的模型。 + +![](images/image20.png) + +## 2.3 支持自定义模型供应商 + +为充分发挥 Dify 的灵活性,考虑到不同地区访问模型的难度,为满足特定业务需求、成本控制或数据隐私要求,我们常常需要接入自定义模型。Dify 支持配置三类核心模型:大语言模型(LLM)、Embedding 模型和 Rerank 模型。本部分内容将逐步指导你完成这些自定义配置。 + +Dify 能够灵活接入来自 OpenAI、Azure、Anthropic 等主流服务商的模型,同时也全面兼容任何符合 OpenAI API 接口规范的自托管模型或第三方模型。你可以通过安装内置的 OpenAI Compatible 插件以及对各大模型平台定制的插件实现这一操作。 + +详细步骤参考如下,首先我们需要安装对应的插件: + +1. 我们需要安装 `OpenAI-API-compatible` 及 `SiliconFlow` 插件获得对绝大部分大模型和 Embedding 模型的支持,其中前者是对 OpenAI 兼容接口的支持,后者是一个部署了当前绝大部分常见、好用的开源模型的服务站。你可以访问下列网页进行安装: + 1. https://marketplace.dify.ai/plugins/langgenius/openai_api_compatible + 2. https://marketplace.dify.ai/plugins/langgenius/siliconflow +2. 如果你是自己部署的 Dify,你可以在对应系统设置界面进入插件市场进行操作 + +![](images/image21.png) + +![](images/image22.png) + +进入插件市场后,搜索对应的插件名称即可。 + +![](images/image23.png) + +3. 安装结束后,我们能够配置支持新的模型供应商,在设置里的模型提供商部分,我们可以看到目前支持的所有模型商: + ![](images/image24.png) +4. 在开始使用前,需要先完成模型的配置。对于 OpenAI-API-compatible 插件,你可以点击 “Add Model” 来添加并配置任意模型。你可以在 “Model Type” 中选择该模型是LLM还是 Embedding,你需要确保模型的类型被正确配置。 + 你需要写入具体的模型名字、模型 endpoint URL 以及 API Key 才能确保模型启用,如果你初步觉得配置该参数麻烦,你可以直接跳到后者的 SiliconFLow 平台的 Key 配置,或者安装 OpenRouter 等第三方服务商插件进行简单的模型支持配置。(确保服务商内有剩余可使用额度) + + ![](images/image25.png) + + 对于 `SiliconFlow` 插件,只需要点击 Setup 配置 key 后即可使用 Embedding 和 Rerank 模型进行测试,你可以点击 Get you API Key from SiliconFlow 获得鉴权密钥。 + + ![](images/image26.png) +5. 配置完成后,你可以点击模型列表查看当前支持多少模型,此时已经完成了基础模型的全部配置。 + ![](images/image27.png) + + 其中支持了绝大部分常见的 Embedding 与 Rerank 模型: + + ![](images/image28.png) + + 此时如果你想要修改 Dify 默认使用模型的配置,你还可以点击 System Model Settings 按钮修改默认的所有模型。 + + ![](images/image29.png) + +## 2.4 创建第一个 Dify 知识库 + +到这里,我们已经完成了最简单的 Agent 创建,但它还缺少一个知识库。现在,请点击顶部菜单中的 `Knowledge`,进入知识库创建页面。 + +![](images/image30.png) + +然后点击左侧的 `Create Knowledge`,创建你的第一个知识库。 + +![](images/image31.png) + +在这个界面中,你可以上传多种类型的文件(例如 pdf、txt 等)来构建知识库。可以上传很长的文本,或者把维基百科上的内容复制下来保存成 txt 文件进行上传。本例中,我们会上传一份关于 Elon Musk 的维基百科 txt 文件。 + +点击 Next 后,你会进入 Knowledge Base Settings(知识库设置)页面。这里选项比较多,我们一步一步来看。 + +首先在 **General** 设置中,你可以把这里理解成“文本切分规则”的设置区域。因为我们需要把很长的文本切分成小块,所以必须先定义切分规则。在入门阶段,你只需要关注 **maximum chunk length(最大切分长度)** 。可以尝试设置为 512、2048 或 4096,然后点击 **Preview Chunk** 预览不同设置下的效果。 + +你也可以调整 **Chunk overlap(切片重叠)** 选项。它决定相邻片段之间是否会保留一部分重叠内容。适当的重叠有助于避免重要信息被拆到不同片段而难以理解。 + +![](images/image32.png) + +在设置中还有一个选项叫做 **Chunk using Q&A format in English** 。启用后,系统会使用大语言模型,将知识库的一部分内容转换成问答形式来存储,这在某些场景下可以显著提升检索效果。 + +在真实业务中,根据场景选择合适的切分策略,能够更好地优化检索结果,保证查询能够返回你期望的信息。 + +继续向下滚动页面,你会看到和 Embedding 模型相关的设置。 + +简单解释一下:Embedding 模型的核心功能,是把非结构化数据(例如文本、图片等)转换成计算机能够理解的“数字向量”(Embedding 向量)。通过这种转换,模型能够快速计算不同数据之间的相似度,从而实现语义相近内容的匹配,比如根据用户输入的一句话,找到语义最接近的文档、图片或商品。 + +Embedding 模型的选择会显著影响最终的检索效果(例如匹配准确度、响应速度等)。在这里,我们推荐优先使用 Qwen 0.6B 的 Embedding 模型,你也可以切换到 4B 或 8B 版本,直观对比不同参数规模下检索效果的差异。 + +![](images/image33.png) + +在此处,你还会看到另一个模型设置叫做 **Rerank model** ,默认值是 **Jina-rerank-m0** 。(如果你非校园内的学生,此时你可能会看到 Rerank 模型缺失的报错,你需要在模型处配置 rerank 模型才能在此处启用使用) + +Rerank 模型的主要作用,是对“初步筛选出的候选结果”进行二次、更精细的排序,让和用户需求最匹配的结果排在更靠前的位置,从而显著提升最终结果的相关性和用户体验。 + +简单理解:Rerank 模型就是用来解决“初次筛选不够精细”的问题。例如搜索引擎可能先用较简单的规则检索出 1000 个潜在相关网页,再通过 Rerank 模型,从中挑出最相关的前 10 个展示在第一页。 + +推荐系统同理:它可能首先找出 500 个“可能适合你”的商品,再通过 Rerank 模型排序,让你最可能购买的商品排在列表顶部。 + +![](images/image34.png) + +当所有设置完成后,点击 **Save & Process** ,系统就会进入知识库向量化阶段。在这一阶段,Embedding 模型会把切分后的文本转换为向量表示。 + +![](images/image35.png) + +处理完成后,点击 **Go to document** ,可以查看已经处理完毕并存储好的知识库内容。 + +![](images/image36.png) + +直接点击知识库名称,可以查看每个切片的具体内容。 + +![](images/image37.png) + +在这里,你可以对任意不合适的文本片段进行精确的编辑或删除操作。 + +![](images/image38.png) + +在左侧边栏中,选择 **Retrieval Testing** 可以对知识库进行召回测试,检查检索是否正常工作。每次测试会返回若干相似度最高的切片。 + +![](images/image39.png) + +如果你希望看到更多的切片结果,需要点击 `VECTOR SEARCH` 设置: + +![](images/image40.png) + +![](images/image41.png) + +Top K 指的是向量检索时,返回与查询向量最相似的前 K 个文本切片数量。当前设置为 3,表示会返回相似度最高的 3 段文本。 + +Score Threshold 则是一个“得分阈值”:只有相似度得分大于或等于该阈值(示例中为 0.5)的文本片段才会被返回。这样可以过滤掉相关度较低的内容,让结果更加准确。 + +现在知识库部分就全部准备好了。接下来,点击顶部菜单栏中的 “studio”,找到刚才创建的智能体,为它接入我们已经配置好的知识库。 + +![](images/image42.png) + +![](images/image43.png) + +此时,在每一轮对话中,你都可以在回答中看到被命中的知识库来源。点击对应条目即可查看检索到的具体文本片段。 + +![](images/image44.png) + +![](images/image45.png) + +## 2.5 更多 DIfy 常见操作 + +在掌握基础 Chatbot 和知识库搭建的基础内容后,我们可以深入了解更多有关 Dify 的使用方式。 + +### 2.5.1 工作流的导入与导出 + +还记得之前提到的工作流的中间表示法吗?Dify 支持通过 DSL(Domain Specific Language) 格式导入和导出工作流。DSL 是一种基于 JSON 的标准化描述方式,能够完整保留工作流的节点结构、连接关系和配置参数。你可以很容易导入和导出 DSL 文件,分享工作流给其他人使用,或者导入别人的工作流进行参考。具体而言,我们能够容易在工作台页面看到工作流的导入按钮: + +![](images/image46.png) + +而对于工作流的导出,我们只需要点击单个工作流块的右下角即可找到导出按钮: + +![](images/image47.png) + +通过使用 DSL 文件,你可以轻松地在不同 Dify 实例之间迁移或共享复杂的工作流设计。 + +### 2.5.2 查看更多 Dify 项目 + +如果你觉得自己搭建的工作流或者智能体过于简单,Dify平台提供了丰富的示例项目,帮助你快速了解如何构建复杂应用。这些示例项目涵盖了多种业务场景。你可以点击 Explora 查看别人构建的工作流进行学习。 + +![](images/image48.png) + +## 2.6 创建第一个 Dify Workflow 应用 + +完成了 DIfy 的对话智能体构建入门,我们继续查看如何构建更复杂的 Dify 业务工作流。工作流是Dify将复杂业务逻辑可视化的核心方式,通过它你可以像搭积木一样构建智能流程。你能够完整体会信息如何在不同节点间流转,判断逻辑如何部署,人工干预点设置在哪里,以及最终如何交付一个完整的业务结果。 + +你可以选择从空白处创建,或者直接从模板处创建,此处演示如何从空白处创建工作流: + +![](images/image49.png) + +![](images/image50.png) + +在这里我们会看见两个选择,分别是 Chatflow 与 Workflow,这两者该如何选择呢?关键是你需要理解你所要构建的,其核心是持续对话,还是任务流程。 + +Chatflow 专为对话而设计。它模拟一个具有记忆和上下文理解能力的对话者,非常适合需要多轮交互、状态维持的场景。例如在客服咨询中,它能连贯地理解用户的后续追问,如同一位耐心的服务人员。其流式输出的特性也让交互过程更为自然。简而言之,当你需要构建一个能“交谈”的智能体时,应选择 Chatflow。 + +Workflow 则专注于流程的自动化执行。它像一条预设的流水线,擅长处理一次性输入、多步骤处理、并产生确定性输出的任务。例如,每日定时生成数据报表、批量处理文件或调用系列API。这类任务通常由事件触发,无需与人实时互动。因此,当你需要实现“自动化”任务时,Workflow 是更合适的选择。 + +为避免选型错误带来的效率低下,你可以通过四个关键问题来审视你的任务需求: + +1. 任务过程是否需要依赖多次的用户输入与调整? +2. 结果的呈现是否需要分步骤、流式地进行? +3. 处理逻辑是否严重依赖于之前的交互历史? +4. 任务是否由事件触发,且输入输出多为一次性完成? + +如果前三个问题的答案为“是”,那么 Chatflow 是理想选择,典型场景包括智能客服、教育辅导、创意协作等。如果第四个问题特征显著,则应选用 Workflow,它更适用于数据清洗、报表生成、批量处理等自动化场景。 + +此处我们选择 Chatflow 作为案例进行介绍,点击 Chatflow 后进入到操作台界面: + +![](images/image51.png) + +我们来简单介绍工作流界面的页面。其中整个界面的核心是中央的编辑画布,你将以可视化方式在这里构建应用逻辑。如图所示,一个基础的工作流通常始于 START 节点(用于接收输入),经由连线将数据传递至 LLM 节点进行处理,最终通过 ANSWER 节点输出结果。每个节点代表一个功能模块,而连线则决定了任务执行的顺序。 + +环绕画布的是完整的操作与管理功能区。界面顶部提供了全局控制选项,包括测试工作流的 Preview 按钮和用于上线的 Publish 按钮。画布角落则设有缩放、撤销等视图控制工具,便于精细调整。 + +左侧面板集中了应用的管理功能。你当前所在的 Orchestrate 选项卡用于流程编排;构建完成后,可通过 API Access 获取集成凭证;Logs & Annotations 记录了每次执行的详细踪迹,便于调试;而 Monitoring 则为你提供应用运行时的性能与状态监控。 + +你可以简单在该对话工作流 LLM 节点的 SYSTEM 中输入一些提示词内容,点击 Preview 后尝试运行这个工作流,查看修改 SYSTEM 提示词后整个工作流确实按照预期在变化。 + +### 2.6.1 常见节点介绍 + +Dify 中提供了多种节点,你可以先了解每个节点的基本功能。具体使用时,建议亲手尝试,或参考他人创建的工作流模板,也可以截图并向大模型询问该节点的用法、所需参数等。推荐直接在现有模板中替换不同节点,通过他人的使用方式来推测节点的最佳实践。 + +在画布右键点击“Add Node”即可添加节点,也可以在左侧的节点面板中查看所有可用节点: + +![](images/image52.png) + +同时,可以打开工具选择面板,查看支持调用的各类工具: + +![](images/image53.png) + +下面是一些常用节点和工具的简要说明。不需要一次性全部掌握,建议先留个印象,在实际使用中逐步熟悉,必要时再回查阅。 + +1. LLM与推理节点 + +![](images/image54.png) + +![](images/image55.png) + +此类节点负责工作流中的核心流程。 + +* LLM节点:核心计算单元,用于调用大语言模型。其配置重点在于提示词工程与参数调优,将业务问题转化为模型的执行指令。 +* Knowledge Retrieval 节点:知识检索单元,负责从预设知识库、外部权威数据源中检索与业务问题相关的信息,为 LLM 节点提供精准的知识支撑,帮助减少大语言模型输出的 “幻觉” 问题。 +* Answer 节点:结果输出单元,负责接收 LLM 处理后的内容,将其整理为符合业务场景需求的最终成果形式。其配置重点在于输出格式的定义(如话术模板、排版规范)。 +* Agent节点:高阶决策单元。它不仅调用模型,还可实施多步骤规划、自主选择并调用外部工具,适用于需要动态决策的复杂任务链。 +* Question Classifier 节点:问题分类单元,负责对输入的业务问题进行类型识别与归类(比如按问题意图、主题领域等维度划分),帮助后续流程精准匹配对应的处理节点(如不同类型的问题适配不同的 LLM 提示词或工具链)。 + +2. 逻辑与流程控制节点 + +![](images/image56.png) + +此类节点定义工作流的执行路径与规则。 + +* 条件节点:如 `IF/ELSE`,通过布尔判断实现流程分支。其设计关键在于条件表达式的严谨性,确保逻辑覆盖所有业务场景。 +* Iteration 节点:作为无状态的批量并行处理单元,它专为子任务间无数据依赖、可独立处理的场景设计,例如批量翻译段落、并行审核多条内容或同时生成多份报告。该节点会接收一个输入数组并自动分片,将每个元素分发至相同处理链路并行执行,用户可在迭代体内通过 {{item}} 访问当前元素、{{index}} 获取其索引,输出则会自动聚合成结果数组;配置时需重点设定并行度以平衡效率与系统负载,同时通过重试策略(如重试次数、间隔)和失败处理(如记录日志、返回默认值)保障批量作业的稳定性。 +* Loop 节点:有状态的递归迭代器,适用于结果依赖前一轮输出的场景,比如多轮参数调优、递归式内容优化(如反复修订文案直至满意)及依赖上次结果的链式计算。其核心是 “状态变量”,需在循环开始前初始化(如当前迭代次数、中间计算结果),并在每轮迭代中明确更新以作为下一轮输入;为防止无限循环,必须定义终止条件(包括基于计数器的 “最多循环 10 次”、基于结果判定的 “满意度评分 > 9”、基于外部信号的 “检测到‘停止’输入”),同时需设置循环超时配置,并规划异常处理路径(如跳出循环或重置状态后重试),确保流程稳定运行。 + +3. 数据操作与集成节点 + +![](images/image57.png) + +* Code 节点:代码处理单元,负责在工作流中执行自定义代码逻辑,可实现数据格式转换、复杂计算等个性化处理需求。其配置重点在于代码语法的正确性与执行环境的适配。 +* Template 节点:模板处理单元,负责将动态数据填充至预设模板中,生成符合格式要求的内容(如定制化文案、报告框架)。其配置重点在于模板语法的编写与变量映射规则的设置。 +* Variable Aggregator 节点:变量聚合单元,负责收集工作流中多个节点输出的变量数据,将分散的变量整合为统一数据集。其配置重点在于聚合的变量范围与数据合并规则的定义。 +* Doc Extractor 节点:文档提取单元,负责从 PDF、Word 等各类文档中提取文本、表格等关键内容,转化为工作流可处理的结构化数据。其配置重点在于文档类型的适配与提取内容的筛选规则。 +* Variable Assigner 节点:变量赋值单元,负责定义、初始化或更新工作流中的变量,为流程内的数据传递提供载体。其配置重点在于变量的命名、数据类型及赋值逻辑的设定。 +* Parameter Extractor 节点:参数提取单元,负责从用户请求、接口返回等输入内容中提取指定参数,将非结构化信息转化为结构化数据。其配置重点在于提取规则(如正则表达式、JSON 路径)的配置。 +* HTTP Request 节点:HTTP 请求单元,负责向外部系统接口发起 HTTP 请求(含 GET、POST 等方法),实现工作流与外部服务的数据交互。其配置重点在于请求地址、请求方法及参数 /headers 的设置。 +* List Operator 节点:列表操作单元,负责对数组、列表类型的数据进行处理(如过滤、排序、拆分),调整数据结构以适配后续流程。其配置重点在于操作类型(如过滤条件、排序规则)的定义。 + +### 2.6.2 常见工具介绍 + +![](images/image58.png) + +在 Dify 中,大部分工具都可以直接作为节点放在画布上,像其他节点一样被上下游连线,只要你提供的输入符合该节点(工具)的参数规范,它就能正常执行并产出可继续流转的结果。 + +在左侧或右侧的节点面板中,可以查看所有可用工具节点,也可以通过插件市场扩展更多工具能力。简单介绍几个常见工具的作用: + +* 网络搜索工具 + 以 Tavily Search 为代表,为大模型提供面向 AI 优化的实时检索能力。 + 它会返回结构化的搜索结果(如标题、摘要、链接等),可以直接作为 LLM 提示词的一部分,用于回答最新资讯类或需要权威依据的问题。 +* 数据处理工具 + 例如 JSON Process 插件,用于对 JSON 数据进行查询、筛选、转换、合并等高级操作。 + 在处理复杂 API 响应或多层嵌套数据时,你可以将“数据清洗 + 重组”的逻辑交给该工具,从而简化在 Code 节点中频繁手写解析代码的工作。 +* 格式处理工具 + 如 Markdown Exporter,可以将生成内容按指定格式导出,例如 Markdown 文档、特定排版模板等,方便后续用于展示、汇报或集成到其他系统。 + +你可以在工具列表中看到这些插件的安装量和简介,初期可优先尝试安装“Featured / 推荐”里的工具,往往覆盖了最常见的业务场景。 + +不过,工具的使用通常比较复杂,建议你在使用的时候可以去搜索引擎先搜索对应工具的“官方推荐工作流 DSL 案例”,直接导入使用,比自己搭建要天然节约很多时间。 + +### 2.6.3 创建简单的意图分类工作流 + +此时我们已经初步了解了 Dify 工作流和工具等的基本信息,但不经过练习我们永远不会熟练使用细节,我们需要一个“假设”的真实业务场景来练练手。 + +例如,在真实的购物对话场景中,前来购买商品的用户输入永远不会是“规范的参数”,而是一句随口说出的话:有人来下单,有人来抱怨,有人只是想闲聊,也有人完全跑题。如果我们把所有这些输入都直接交给同一个大语言模型(LLM)处理,系统通常会出现两个典型问题: + +1. 回复风格不稳定 + 同样是抱怨,有时 LLM 能道歉安抚,有时却像在“解释原因”;同样是点餐,有时会追问缺失信息,有时则直接编造订单细节。 +2. 业务逻辑不可控 + 你希望“抱怨必须先道歉”,但模型未必每次都遵守;你希望“非业务问题要引导回主线”,但模型可能会兴致勃勃地和你聊起段子。 + +因此,更工程化的做法是将任务拆解为一条标准化流水线,先做意图分类(确定用户到底想干什么),然后再按意图分流(不同场景使用不同的提示词与角色),最后对不同分流后大模型的回复统一封装输出(便于前端或系统集成)。 + +本节的目标是让系统能处理一个餐饮场景下的多类对话。你可以跟着操作做一遍加深印象。首先需要做的是定义场景为意图分类: + +* **下单购买 (buy_food)** :用户表达明确的购买意愿。 +* *例如:“给我来一份炸鸡,再加一杯可乐。”* +* **抱怨投诉 (complain)** :用户在表达不满、催促或负面反馈。 +* *例如:“你们也太慢了吧?等一个小时了。”* +* **闲聊咨询 (chitchat)** :用户在进行开放式询问、寻求建议,但无明确下单指令。 +* *例如:“今天吃什么好呢,你有什么推荐吗?”* +* **其他意图 (other)** :用户的输入与餐饮场景无关。 +* *例如:“帮我写个搞笑文案发朋友圈。”* + +针对这四种意图,我们为系统预设了四种不同的“沟通人格”,分别由四个独立的 LLM 节点承载,每个节点都需要由具有不同人设的 LLM 进行扮演。 + +* **下单助手 (LLM_BuyFood)** :专业、高效,核心任务是确认订单细节,并主动补全缺失信息。 +* **客服专家 (LLM_Complain)** :共情、稳重,首要任务是安抚用户情绪,并提供清晰的解决方案。 +* **聊天伙伴 (LLM_Chitchat)** :轻松、友好,旨在提供个性化推荐,引导潜在消费。 +* **礼貌门卫 (LLM_Other)** :专注、边界清晰,负责将偏离主题的对话礼貌地引导回核心业务。 + +#### 工作流编排设计 + +接下来我们进行工作流的编排设定,决定大概需要有哪些工作流节点。对于新手而言,很难想到需要有哪些节点能被用到(对于老手来说也懒得自己思考,用大模型给建议通常是最快最好的选择),所以我们能够使用大模型给出对应的编排建议,其核心节点结构如下: + +* Start (起点):作为数据入口,负责接收用户的原始输入 `user_text`。 +* Question Classifier (意图分类器):工作流的“大脑”与“调度中心”。它负责对 `user_text` 进行分析,并从我们预设的四种意图标签中选择最匹配的一个。 +* Condition (条件分支):扮演“分流阀”的角色。它根据分类器输出的意图标签,决定接下来将任务导向哪一个专处理路径。 +* 四个并行的 LLM 节点 (LLM_BuyFood, LLM_Complain, LLM_Chitchat, LLM_Other):这是四个独立的“专家处理单元”。每个节点都接收原始问题,但依据自身独特的 System Prompt(系统提示词)生成风格和目标截然不同的回复。 +* Variable Aggregator (变量聚合器):在多条路径处理完成后,需要一个“汇集点”。此节点将四个分支中唯一被激活并产生结果的回复,收束成一个统一的变量 `final_reply`,确保了输出结构的稳定性。 +* Output (终点):作为最终的出口,负责将意图标签、原始问题、以及经过处理生成的回复,以结构化的形式(如 JSON)统一输出,便于后续系统调用或调试分析。 + +#### 工作流编排实现 + +本次教程我们选择创建 Workflow 而不是 Chatflow,选择 User Input: + +![](images/image59.png) + +随后点击 Start 的 User Input 节点,定义一个名为 `user_text` 的字符串类型变量,作为整个流程的输入源。 + +![](images/image60.png) + +保存后点击右上角的 Test Run,你能够看到需要指定对应的文本输入进行处理: + +![](images/image61.png) + +随后我们需要点击输入节点后的 + 符号,选择 Question Classifier 节点添加,并且我们需为其配置四类标签,并为每个标签提供清晰的描述和示例。 + +* `buy_food`: 用户明确想买吃的、点餐、下单。 +* `complain`: 用户在抱怨、吐槽、发脾气,通常带有不满情绪。 +* `chitchat`: 用户在闲聊、讨论吃什么、咨询推荐。 +* `other`: 与餐饮场景无关,或难以判断的内容。 + +此外,你还需要在 ADVANCED SETTING 中写入提示词,让大模型能够正确根据用户输入进行分类测试。示例提示词如下: + +```Plain +从 buy_food / complain / chitchat / other 中选择一个最合适的标签。如果用户在抱怨的同时也点了餐,请优先判断其核心情绪,若重点在于表达不满,应归为 complain。如果只是轻微吐槽但主要意图是下单,则归为 buy_food。若实在难以判断,使用 other 作为兜底 +``` + +![](images/image62.png) + +设定完成后,你可以在右上角的播放键单独测试该节点是否能够正常运行: + +![](images/image63.png) + +![](images/image64.png) + +从 OUTPUT 的结果来看,我们的分类是准确的。你可以进行多种不同类型输入的测试,验证我们分类器的稳定性。 + +接下来,我们需要给分类器接上后续的大模型输出,例如,当 `label` 等于 `"buy_food"` 时,工作流便会精确地流向 `LLM_BuyFood` 节点。我们需要新建四个 LLM 节点,并设置不同的 System Prompt ;不同 System Prompt 的差异决定了它们不同的回应方式。 + +* LLM_BuyFood (点餐助手): + +你是一个点餐助手。要求:1. 确认用户想点的内容。2. 如果信息不完整,友好地补充询问。3. 语气礼貌简洁。 + +* LLM_Complain (客服专家): + +你是一个餐饮客服,专门处理抱怨。要求:1. 真诚道歉。2. 简要说明可能的原因(不推卸责任)。3. 给出清晰的下一步解决方案。 + +* LLM_Chitchat (聊天伙伴): + +你是一个帮人选吃的的聊天小助手。要求:1. 用轻松友好的语气。2. 给出 1~3 个简单推荐。3. 如果用户没有偏好,就给出不同风格的选择。 + +* LLM_Other (礼貌门卫): + +你是一个餐饮点餐小助手,只擅长跟‘吃’相关的话题。当用户说的话无关时:1. 礼貌说明自己的能力范围。2. 引导用户回到主场景。 + +值得注意的是,每个节点里面在填充了 SYSTEM 的提示词参数后,你还要记得启用 USER 提示词参数表。你需要在其中需要点击 `{x}` 符号,选择 `user_text` 参数作为用户输入,并且在前面加上 `user input:` 标识这个变量是用户输入的意思,在问答的时候会综合用户的最开始的输入和内置提示词进行回复。 + +同样的,为了确保一切顺利,你可以点击该节点右上角的播放箭进行具体的对话测试验证效果,比如对话说“我想要喝珍珠奶茶”等,查看回复是否符合预期。 + +![](images/image65.png) + +接下来我们处理并行 LLM 的输出值,我们在 `Variable Aggregator` 节点的配置面板中,找到 `ASSIGN VARIABLES`(分配变量)区域,点击后依次将之前的大模型回复加入即可。 + +![](images/image66.png) + +接下来我们需要对所有的输出进行聚合,最后得到我们想要的结果,包含用户的输入、分类、以及回复。由于我们使用的是 Workflow 而不是 Chatflow,故没有 Answer 节点选择进行结果的聚合,我们能够选择其他节点变相实现结果的聚合与输出,此时选择 Template 节点,在变量部分指定用户意图分类结果、用户的输入值、变量聚合的最终回复,并且在 CODE 中写入最后回复的 json 格式模板,我们可以得到: + +* `intent` ← `class_name` +* `original_text` ← `user_text` +* `final_reply` ← `variable_aggregator` + +```Plain +{ + "intent": "{{ intent }}", + "original_text": "{{ original_text }}", + "reply": {{ final_reply }} +} +``` + +![](images/image67.png) + +最后加入 output 节点即完成所有操作: + +![](images/image68.png) + +#### 工作流运行测试 + +大功告成,我们可以尝试运行这套工作流的效果。它能根据不同的输入,展现出截然不同的行为模式: + +* 输入 (点餐): "给我来一份香辣鸡腿堡套餐,大杯可乐。" + * 路径: `buy_food` → `LLM_BuyFood` + * 输出 reply: "好的,一份香辣鸡腿堡套餐和大杯可乐已为您记录。请问套餐里的薯条需要更换吗?" +* 输入 (抱怨): "你们也太慢了吧?我都等一个多小时了!" + * 路径: `complain` → `LLM_Complain` + * 输出 reply: "非常抱歉让您等了这么久,这确实是我们的疏忽,给您带来了很不好的体验。我们正在紧急核实您的订单配送情况,并会为您申请相应的补偿,感谢您的耐心与反馈。" +* 输入 (闲聊): "今天吃什么比较健康?" + * 路径: `chitchat` → `LLM_Complain` + * 输出 reply: "追求健康的话,可以试试我们的轻食沙拉系列,或者烤鸡胸肉配时蔬。您是喜欢口味清淡一些还是稍微丰富一点的呢?我可以给您更具体的推荐。" +* 输入 (无关内容): "帮我想个明天开会的笑话。" + * 路径: `other` → `LLM_Other` + * 输出 reply: "这听起来是个有趣的挑战!不过我主要是个美食推荐和点餐助手。如果您需要点些什么来犒劳一下辛苦工作的自己,我随时可以帮忙!" + +> 隐藏 Bug :需要说明的是,若你遇到与 aggregation group 相关的奇怪问题,这大概率是 Dify 的一个内置 bug。可能在特定操作下被触发;如果你曾经开启又关闭过 AGGREGATION GROUP,系统可能生成过 group 配置且残留了相关异常参数,即便现在开关看起来是关闭的,这些残留配置也可能导致问题,比如出现 `any` 相关参数的报错。此时你只需要删除该节点并重新创建即可。 + +在 Test Run 中运行后,我们能够看到工作流的执行过程,此时根据分类走了正确的流程,并得到了最后的 output 结果。至此,全流程完成。 + +![](images/image69.png) + +## 2.7 运行第一个模板 Workflow 应用 + +结束了简单的分类工作流学习,接下来我们需要学习如何运行别人的 workflow,我们只需要稍作改造就可以将其变成自己的工作流。在这里我们选择尝试官方的 DeepResearch 工作流,该工作流能够帮你构建一个深度搜索框架,使用大模型+搜索引擎给你一个丰富的搜索答案,每一次提问的结果将会包含搜索引用地址和大模型对话的结果。 + +导入后第一步直接运行,我们根据每一步报错的地方和原因解决具体问题即可,如果遇到解决不了的问题,你可以截图后询问大模型进行解决。 + +![](images/image70.png) + +刚进入感觉十分复杂,没关系,我们点击右上角的 Preview 运行工作流,直到报错出现: + +![](images/image71.png) + +![](images/image72.png) + +我们需要根据报错的节点解决问题,打开后发现是没有配置 Tavily 的 API Token,Tavily 的搜索API 是一个专为 AI 设计的搜索引擎,提供实时、准确和事实性的结果。此时根据提示操作: + +![](images/image73.png) + +经过处理后,搜索引擎能够正常工作: + +![](images/image74.png) + +继续修正模型调用导致的问题后,你应该能够得到如下结果,结合大模型理解下的详细搜索: + +![](images/image75.png) + +我们在最后能够看到对应的参考文档地址: + +![](images/image76.png) + +如果你想理解每个环节的作用,最好的方法是将每个环节的 output 记录为一个变量,最后在输出的时候打印每个中间变量的结果,还有一个方法就是你可以在上方找到 Process 的过程,点击后可以查看每个环节的细节: + +![](images/image77.png) + +## 2.8 将 Dify 作为 API 提供方 + +接下来,我们会尝试通过 API 调用刚才创建的知识库智能体 Agent,我们想要让 Dify 变成一个大模型中枢后端。 + +还记得之前讲过如何通过 API 调用模型吗?我们需要准备一个密钥(Key)和一份 API 调用示例(文档中的 request/response 示例),然后把这些内容发给大模型,让它帮我们写出调用服务的代码,并从返回结果中解析出我们需要的字段。 + +这一次,我们会使用本地的代码编辑工具 [Trae](https://www.trae.cn/) 来完成这个过程。 + +如果你还不熟悉什么是 IDE,可以先阅读文档 `Extra Knowledge 4 - What is AI IDE and Trae`。 + +如果你的本地开发环境还没有完整配置好,也不用担心。只要你信任自己的代码助手(不管是 [z.ai](http://z.ai) 还是 Trae),遇到任何不懂的地方或报错,都可以直接把问题抛给它,它会根据你的描述给出详细的解决方案。 + +![](images/image78.png) + +右侧的区域叫做 Copilot 交互窗口,或者 Agent 窗口。如果你看不到它,可以点击右上角的侧边栏图标来打开。 + +![](images/image79.png) + +打开侧边栏后,你会看到 `Builder` 选项。这就是 Agent 模式。你可以简单地把 “Builder” 理解为 [z.ai](http://z.ai) 的“开发模式”,它同样可以帮你操作本地电脑环境、安装依赖、打开网页等。 + +![](images/image80.png) + +点击 “Builder” 后,你会看到 “Chat” 模式和 “Builder with MCP” 模式。 Chat 模式主要用于与当前文件夹进行交互,或者和大模型进行自然语言对话。(你可以通过点击 Trae 左上角的 “File” 打开一个文件夹,然后在该文件夹内进行编辑。这种情况下,Builder 所有的新建文件操作都会发生在这个文件夹中。) + +Builder with MCP 模式则为 Agent 提供了更多工具(例如让大模型连接到其他软件、获取天气信息等)。你可以简单地认为 MCP 是一个让大模型更方便调用各种外部工具的能力集合。 + +![](images/image81.png) + +在下方区域,你还可以看到模型选择的下拉列表,可以点击切换不同模型。这里你可以选择 Kimi k2 或 GLM。如果你使用的是国际版 Trae,也可以选择 ChatGPT 或 Claude。 不过,随着国内大模型的快速发展,Kimi、Qwen、GLM 等模型的综合能力已经基本接近 Claude 3.5 或 3.7,对于日常开发场景来说完全够用。 + +![](images/image82.png) + +上面是对 Trae 的一个简要介绍。接下来,我们可以回顾在 [z.ai](http://z.ai) 中的操作步骤,并在 Trae 中复用这些思路。 + +## 2.9 利用 Dify API 创建前端对话应用 + +如果我们想用 Dify 的 API 搭建一个前端聊天应用,首先需要获取 Dify 的 API 文档和调用地址。 + +还记得刚才创建的那个 Agent 吗? 先点击右上角的 “Publish”,然后点击 “Publish Update”,最后点击 “Access API Reference” 进入 API 文档。 + +![](images/image83.png) + +![](images/image84.png) + +进入 API 文档后,找到 “Send Chat Message” 这一部分,点击进入,然后在右侧找到 “Request” 和 “Response” 示例并复制出来。 + +为什么一定要复制这两部分内容? 因为它们是 API 的“核心信息”: 有了 Key、请求示例和返回示例,我们就可以让大模型帮我们生成调用服务的代码,并且根据返回结构把需要的字段提取出来。 + +![](images/image85.png) + +![](images/image86.png) + +在找到会话所需的 Request 和 Response 示例之后,我们还需要获取一个 API Key。在文档右上角,你会看到 “API key” 相关选项。 + +![](images/image87.png) + +点击 “Create new Secret key”,就可以创建属于你自己的 API Key。 + +![](images/image88.png) + +现在一切准备就绪。我们会把刚才拿到的 API Key、Request 示例和 Response 示例一起交给 Trae Builder。 + +注意:请将 `{DIFY_API_URL}` 替换为实际的 Dify API 地址。 + +```JSON +key: +app-zKdCHUXXXXXXXX + +Please write me a front-end based on the following reference: + +curl -X POST 'http://{DIFY_API_URL}/v1/chat-messages' \ +--header 'Authorization: Bearer {api_key}' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "inputs": {}, + "query": "What are the specs of the iPhone 13 Pro Max?", + "response_mode": "streaming", + "conversation_id": "", + "user": "abc-123", + "files": [ + { + "type": "image", + "transfer_method": "remote_url", + "url": "https://cloud.dify.ai/logo/logo-site.png" + } + ] +}' + +{ + "event": "message", + "task_id": "c3800678-a077-43df-a102-53f23ed20b88", + "id": "9da23599-e713-473b-982c-4328d4f5c78a", + "message_id": "9da23599-e713-473b-982c-4328d4f5c78a", + "conversation_id": "45701982-8118-4bc5-8e9b-64562b4555f2", + "mode": "chat", + "answer": "iPhone 13 Pro Max specs are listed here:...", + "metadata": { + "usage": { + "prompt_tokens": 1033, + "prompt_unit_price": "0.001", + "prompt_price_unit": "0.001", + "prompt_price": "0.0010330", + "completion_tokens": 128, + "completion_unit_price": "0.002", + "completion_price_unit": "0.001", + "completion_price": "0.0002560", + "total_tokens": 1161, + "total_price": "0.0012890", + "currency": "USD", + "latency": 0.7682376249867957 + }, + "retriever_resources": [ + { + "position": 1, + "dataset_id": "101b4c97-fc2e-463c-90b1-5261a4cdcafb", + "dataset_name": "iPhone", + "document_id": "8dd1ad74-0b5f-4175-b735-7d98bbbb4e00", + "document_name": "iPhone List", + "segment_id": "ed599c7f-2766-4294-9d1d-e5235a61270a", + "score": 0.98457545, + "content": "\"Model\",\"Release Date\",\"Display Size\",\"Resolution\",\"Processor\",\"RAM\",\"Storage\",\"Camera\",\"Battery\",\"Operating System\"\n\"iPhone 13 Pro Max\",\"September 24, 2021\",\"6.7 inch\",\"1284 x 2778\",\"Hexa-core (2x3.23 GHz Avalanche + 4x1.82 GHz Blizzard)\",\"6 GB\",\"128, 256, 512 GB, 1TB\",\"12 MP\",\"4352 mAh\",\"iOS 15\"" + } + ] + }, + "created_at": 1705407629 +} +``` + +![](images/image89.png) + +在这个阶段,你可能会发现生成出来的程序并不能一次性正常运行——比如对话会出现奇怪的错误,或者没有任何返回结果。当出现这种情况时,你可以尝试切换到另一个大语言模型,或者把错误信息复制出来,详细描述问题,再发给模型让它根据反馈继续迭代。 + +此时你的工作方式已经非常接近真实开发过程了。在日常开发中,我们经常会在与大模型协作时遇到各种问题,为了更好地解决这些问题,我们需要提供更多上下文信息。除了提供错误信息,你还可以复制更完整的文档内容(例如在文档左侧 “Send message” 部分中复制更多说明),一并交给模型,让它在更多细节的基础上给出更完整的解决方案。 + +![](images/image90.png) + +此时浏览器是嵌在 Trae 内部的。你可以点击顶部的指南针图标,把网页在外部浏览器中全屏打开。 + +![](images/image91.png) + +如果运气不错,你可能在第一次尝试时就能获得一个可以正常交互的前端页面。 + +![](images/image92.png) + +不过,由于大模型本身具有一定随机性,有时你可能在单轮对话中一切顺利,但在多轮对话时出现异常。因此,建议你进行多轮对话测试,确保程序在多轮交互场景下也能稳定运行。 + +![](images/image93.png) + +到这里,你已经学会了如何构建一个简单的 Dify 知识库 Agent,并使用 Trae 替代 [z.ai](http://z.ai) 来搭建一个交互式前端。从现在开始,Trae 将成为我们构建各种原型时的主要开发工具,逐步取代 [z.ai](http://z.ai)。你可以尝试用 Trae 重新实现之前的贪吃蛇游戏,看看会有什么不同的体验。加油! + +# 3. 更多业务工作流参考 + +你可以在搜索引擎上使用类似关键词搜索 `Dify workflow 参考`,或者直接在 Github 中找到 Dify 工作流分享仓库进行参考工作流的查找(质量参差不齐,你需要查看多个不同仓库学习)。当然,所谓的工作流只不过是业务上 SOP 的映射,你可以思考有哪些日常工作中的流程或者学习中的流程是重复可固化的,只需要把它变成工作流固定即可。 + +以下是一些大模型生成的工作流设计的参考(实际上的实现方案也比较类似,一般来说人类设计的工作流不会有大模型设计的优美,除非是高手设置的工作流),如果你觉得哪些点子有意思,可以将它发给大模型进一步细化,让大模型帮你给出更具体的 Dify 工作流节点设定,以及内部的细节结果。 + +## 3.1 社媒平台工作流 + +1. 跨平台内容一键分发工作流(复杂) + 1. 思路:以一篇核心稿件为“原料”,自动加工成适配多个平台的“成品”。 + 2. 实现:`Start` 输入文章 -> `LLM` 润色 -> 并行多个 `LLM` 节点(每个节点Prompt扮演特定平台专家,如“小红书爆款文案专家”、“知乎专业答主”)-> `Iterator` 节点循环处理不同平台格式要求 -> `Variable Aggregator` 汇总 -> `Answer` 输出所有版本。复杂度在于并行处理和循环迭代。 +2. 热点话题选题与初稿生成器(中等) + 1. 思路:自动捕捉网络热点,快速生成选题和内容草稿。 + 2. 实现:`Start` 输入关键词 -> `Tool` 节点调用搜索引擎API抓取热点 -> `LLM` 摘要提炼出3-5个话题 -> `LLM` 生成文章大纲或初稿。复杂度在于外部工具集成与信息筛选。 +3. 评论区智能分类与回复助手(复杂) + 1. 思路:自动分析评论情感与意图,生成分类回复建议。 + 2. 实现:`HTTP Request` 节点接入社媒API获取评论 -> `Question Classifier` 或 `LLM` 节点进行多标签分类(积极、疑问、投诉、广告等)-> `Condition` 判断节点路由至不同回复生成链 -> 并行 `LLM` 节点生成个性化回复草稿 -> `Answer` 输出。复杂度在于条件分支和实时API调用。 +4. 短视频脚本与分镜自动生成器(复杂) + 1. 思路:根据一个热门话题或产品描述,自动生成短视频脚本、分镜描述和推荐标签。 + 2. 实现:`Start` 输入主题 -> `LLM` 生成创意脚本 -> 第二个 `LLM` 节点将脚本拆解为场景序列(画面描述、台词、时长)-> `Tool` 节点调用文本转语音服务生成语音样本 -> `Variable Aggregator` 整合所有元素 -> `Answer` 输出结构化脚本文件。复杂度在于多步骤序列化和外部服务集成。 +5. 直播互动问答实时摘要助手(中等) + 1. 思路:实时处理直播间的文字评论,提炼核心问题和观众反馈。 + 2. 实现:`HTTP Request` 节点流式获取直播评论 -> `Iterator` 节点以时间窗口为单位处理批数据 -> `LLM` 节点实时总结每段时间内的热点问题与情绪倾向 -> `Answer` 或 `Webhook` 节点输出摘要给主播。复杂度在于实时流数据处理和循环窗口。 + +## 3.2 职场工作流 + +1. 智能会议纪要与任务自动派发系统(复杂) + 1. 思路:从会议录音文本中提取纪要,并自动创建任务。 + 2. 实现:`Start` 输入会议文本 -> `LLM` 总结议题与结论 -> `Parameter Extractor` 节点精准抽取Action Items(任务、负责人、DDL)-> 一个 `LLM` 整合成纪要邮件 -> 并行 `HTTP Request` 节点调用Jira/Trello/飞书API创建任务。复杂度在于信息抽取与多系统联动。 +2. 简历批量筛选与初步评估助手(中等) + 1. 思路:自动解析简历,进行匹配度评估并生成面试问题。 + 2. 实现:`Start` 上传简历文件与JD -> `Document Extractor` 节点解析简历文本 -> `LLM` 扮演HR进行匹配度评估 -> 对高匹配者,另一个 `LLM` 生成深度面试问题。复杂度在于文档解析与多条件评估。 +3. 多语言邮件一键翻译与草稿回复(简单) + 1. 思路:自动翻译邮件并起草回复。 + 2. 实现:`Start` 输入邮件 -> `LLM` 判断语种并翻译 -> `LLM` 构思回复要点 -> `LLM` 翻译回原始语言并润色。主要依赖于LLM的序列调用。 +4. 周报/月报数据自动汇总与洞察生成(复杂) + 1. 思路:连接多个数据源,自动生成结构化工作报告。 + 2. 实现:多个 `HTTP Request`/`Tool` 节点并行调用业务系统API(如CRM、Git、项目管理工具)获取原始数据 -> `Code` 节点或 `LLM` 进行数据清洗与基础计算 -> `LLM` 分析趋势、亮点与风险,生成叙述性报告 -> `Answer` 输出图文并茂的文档。复杂度在于多数据源聚合、数据处理与智能分析结合。 +5. 合同/文档智能审查与要点提炼(中等) + 1. 思路:快速审查法律或商务文档,提示风险并提炼核心条款。 + 2. 实现:`Start` 上传合同PDF -> `Document Extractor` 提取文本 -> `LLM` 节点(设定为法律专家角色)审查责任条款、支付条件、违约条款等 -> `Parameter Extractor` 节点抽取出关键日期、金额、义务方等结构化数据 -> `Answer` 输出风险提示和要点表格。复杂度在于长文档处理与结构化信息抽取。 + +## 3.3 学习生活工作流 + +1. 学术论文深度解析与笔记生成器(复杂) + + 1. 思路:上传论文PDF,自动生成结构化笔记。 + 2. 实现:`Start` 上传PDF -> `Document Extractor` 提取全文 -> 并行多个 `LLM` 节点分工总结摘要、方法、发现、参考文献 -> `Variable Aggregator` 汇总 -> `Answer` 输出Markdown笔记。复杂度在于并行处理长文本的不同部分。 +2. 个性化旅行计划定制师(中等) + + 1. 思路:根据用户偏好,自动规划详尽行程。 + 2. 实现:`Start` 输入需求(目的地、天数、预算、兴趣)-> `Tool` 节点调用搜索引擎或地图API获取地点信息 -> `LLM` 整合信息,设计每日行程(含时间、活动、预算估算)。复杂度在于外部信息获取与结构化规划。 +3. 外语学习互动陪练伙伴(简单) + + 1. 思路:创建可角色扮演和语法纠错的对话机器人。 + 2. 实现:系统设定AI角色 -> `Start` 接收用户语句 -> `LLM` 执行两项任务:角色回复 + 语法纠错与解释 -> `Answer` 输出。核心是LLM的多任务指令。 +4. 个人知识库问答与链接推荐系统(复杂) + + 1. 思路:基于你收藏的文档、笔记、网页链接,构建一个可问答并能推荐相关旧知识的智能系统。 + 2. 实现:离线处理:使用 `Document Extractor` 和 `Embedding` 工具将个人知识库切片并向量化存储。在线工作流:`Start` 输入问题 -> `Retrieval` 节点从向量库中查找最相关的知识片段 -> `LLM` 基于检索到的上下文生成答案 -> 同时,另一个分支使用检索到的内容作为输入,通过 `LLM` 生成“相关旧知识”推荐列表 -> `Answer` 合并输出答案与推荐。复杂度在于检索增强生成(RAG)流程的构建。 +5. 健身/饮食计划追踪与调整顾问(中等) + + 1. 思路:根据用户输入的每日饮食和训练日志,提供营养分析与训练建议。 + 2. 实现:`Start` 输入文本日志(如“午餐:鸡胸肉150g,米饭一碗,蔬菜若干;训练:深蹲5组”)-> `Parameter Extractor` 节点尝试结构化输入数据 -> `LLM` 扮演健身教练,分析营养摄入是否均衡、训练容量是否合适 -> 对比长期目标,给出微调建议(如“蛋白质摄入充足,建议增加蔬菜种类”)。复杂度在于从非结构化日志中提取结构化信息并提供个性化反馈。 +# 6. 工作流平台的局限性 + +工作流平台(或称低代码平台)并非万能解决方案。它虽然对业务人员友好,降低了直接编码的门槛,但从另一个角度看,“低代码”往往也是一种“高代码”——用户仍需理解平台的概念、规则与操作逻辑,这本身构成了一种新的学习成本。 + +也许你想问,很多简单的工作流其实就是大模型函数包装后的前后调用,前面函数的输出作为后者函数的输入,本质上几行代码就能够解决,为什么需要那么复杂的多重包装工作流?反而给 API 调用造成了麻烦。 + +你说得是对的。在当前 vibe coding 的快速发展下,借助 AI 代码生成能力,直接阅读甚至生成代码有时可能更加高效。理想情况下,我们希望能用自然语言直接操作应用逻辑,这才是一个现代的软件平台。但目前的工作流平台尚未实现这一点,因此它在用户意图与最终实现之间天然存在一个“中间层”。掌握这个中间层,正是一种需要投入时间学习的成本。理想上,之后的工作流平台也要支持全 AI 自动对话操作,我们可以让 AI 真正操作工作流搭建以及入参的每一个细节环节。 + +尽管如此,熟练使用这类平台正逐渐成为一项基础技能,如同微软的办公软件一样,在业务中非常普遍且实用,值得掌握。 + +在后续的进阶课程中,我们将介绍如何通过代码级别的工作流与 RAG 开发平台进行构建。届时,你可以亲身体验不同实现方式在复杂度与灵活性上的区别。(值得注意的是,一些简单的对话应用或嵌套逻辑,用工作流实现可能并不困难。) + +# 📚 课后作业 + +## 掌握 Dify 基本操作 + +为了测试你掌握了 Dify 的常见基础使用工具,你需要完成一个基础作业和两个 “小挑战”,确保你已入门常见的操作。你需要将附带的两个 DSL 文件导入 Dify 工作流,并成功完成对应工作流的挑战(遇到不懂的地方截图询问大模型,或自己探索其中的每个参数的用法,最后实现目标)。: + +1. 参考意图分类工作流的方法,让大模型给你建议完全换一套场景进行应用,但是一定要用到意图分类工作流,最后提交运行的工作流截图、场景说明、结果。 +2. Log in workflow 工作流解密挑战 + +在这个解密挑战中,你需要完成以下挑战,让工作流实现下列功能: + +* 找出正确的密码! +* 将密码修改为 0925 +* 当密码不正确时,提供第二次尝试机会(不提供第三次) +* 当用户提及要再次登录时,为用户提供重新输入密码的机会 + +![](images/image94.png) + +参考输入输出: + +![](images/image95.png) + +3. Love loop workflow 工作流解密挑战 + +![](images/image96.png) + +在这个解密挑战中,你需要修复当前工作流的问题,让工作流最后的输出类似如下显示: + +![](images/image97.png) + +如果你遇到无法解决的问题,请截图询问大模型,或查阅官方文档得到结果:[https://docs.dify.ai/en/use-dify/getting-started/quick-start](https://docs.dify.ai/en/use-dify/getting-started/quick-start) + +## 实现 Dify API 调用 + +为了测试你真正掌握了 Dify 的 API 调用知识,你需要完成以下任务: + +1. 部署 Dify 并创建一个简单的知识库(选取你喜欢的资料)。 +2. 使用 Trae IDE 构建一个对话前端,与 Dify 知识库进行 API 交互。 +3. 测试多轮对话的效果,确保程序正常运行。 + +你需要提交最终运行截图和知识库的处理过程截图。 + +## 试用第三方工作流 / 构建一个自己的业务工作流 + +请你在 Github、微信公众号、或者 Reddit、推特上等所有地方找到你想尝试的别人的 Dify 工作流,下载导入后成功运行;或者你可以根据上文中提到的业务工作流参考,根据现实中的具体需求创建一个自己的业务工作流进行运行。 + +最后你需要提交运行成功的截图,并说明这个工作流的作用。 + +# [Bug] HTTP 请求错误问题的解决方法 + +如果你遇到了如下图所示的问题,才需要参考本节方案进行解决,否则可以不理会当前部分。 + +有时候可能你会把 Dify 部署在自己的服务器,但是服务器的对外地址通常都是 http 而不是 https 的,但当我们请求一个只支持 HTTP 的服务时,你可能会看到类似这样的提示(启用 F12 浏览器调试信息模式,查看有问题的点): + +![](images/image98.png) + +出现这个问题的原因,是因为我们默认把 Dify 部署在一台只支持 HTTP 而不支持 HTTPS 的服务器上。 HTTPS(HyperText Transfer Protocol Secure)是在 HTTP(超文本传输协议)的基础上增加了 SSL/TLS 加密层,可以简单理解为“更安全版的 HTTP”。 + +如果要让服务支持 HTTPS,一般可以: + +* 使用其他程序转发请求(例如在有证书的 nginx 上做反向代理),或者 +* 绑定域名后为该域名申请证书。 + +但这些操作都比较复杂,在这里我们使用 Zeabur 作为网络转发网关来解决问题。 + +Zeabur 的网页默认是通过 HTTPS 访问的,因此我们只需要把原来请求的域名转发到 Zeabur 提供的域名,就可以修复这个问题。 + +* 原始地址:`http://{DIFY_API_URL}/v1/chat-messages` +* 现在地址:`https://{DIFY_NEW_API_URL}.zeabur.app/v1/chat-messages` + +你只需要简单地把 URL 中的域名部分(公网 IP 或域名)替换为已经在 Zeabur 上部署好的域名即可,我们已经提前在服务里配置好了转发功能。 + +如果你感兴趣,也可以自己在 Zeabur 上部署一个转发服务。在 Zeabur 中创建服务时,选择 Python,然后填入下面的 Python 代码,部署后即可得到一个 https 的地址,https 即可正常使用。 + +部署完成后,在网络设置中把程序监听端口设置为本地 8080,并对外暴露该端口。 + +注意:请将 `{DIFY_API_URL}` 替换为实际的 Dify API 地址。 + +```Python +from flask import Flask, request, Response +import requests + +app = Flask(__name__) + +TARGET_BASE_URL = "{DIFY_API_URL}" +LISTEN_PORT = 8080 + +@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD']) +@app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD']) +def proxy_request(path): + target_url = f"{TARGET_BASE_URL}/{path}" + if request.query_string: + target_url += f"?{request.query_string.decode('utf-8')}" + + headers = {key: value for key, value in request.headers if key.lower() not in ['host', 'connection', 'content-length', 'accept-encoding']} + + try: + resp = requests.request( + method=request.method, + url=target_url, + headers=headers, + data=request.get_data(), + cookies=request.cookies, + allow_redirects=False, + timeout=30 + ) + + excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection'] + response_headers = [(name, value) for name, value in resp.raw.headers.items() if name.lower() not in excluded_headers] + + return Response(resp.content, resp.status_code, response_headers) + + except requests.exceptions.RequestException as e: + print(f"Error forwarding request to {target_url}: {e}") + return Response(f"Proxy Error: Could not reach target server or invalid response: {e}", status=502) + except Exception as e: + print(f"An unexpected error occurred: {e}") + return Response(f"Internal Proxy Error: {e}", status=500) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=LISTEN_PORT, debug=True) +``` diff --git a/docs/project/chapter3/images/image1.png b/docs/project/chapter3/images/image1.png new file mode 100644 index 0000000..0c0df71 Binary files /dev/null and b/docs/project/chapter3/images/image1.png differ diff --git a/docs/project/chapter3/images/image10.png b/docs/project/chapter3/images/image10.png new file mode 100644 index 0000000..cdcdc6b Binary files /dev/null and b/docs/project/chapter3/images/image10.png differ diff --git a/docs/project/chapter3/images/image11.png b/docs/project/chapter3/images/image11.png new file mode 100644 index 0000000..d8df91f Binary files /dev/null and b/docs/project/chapter3/images/image11.png differ diff --git a/docs/project/chapter3/images/image12.png b/docs/project/chapter3/images/image12.png new file mode 100644 index 0000000..0783dc3 Binary files /dev/null and b/docs/project/chapter3/images/image12.png differ diff --git a/docs/project/chapter3/images/image13.png b/docs/project/chapter3/images/image13.png new file mode 100644 index 0000000..6cdc6dd Binary files /dev/null and b/docs/project/chapter3/images/image13.png differ diff --git a/docs/project/chapter3/images/image14.png b/docs/project/chapter3/images/image14.png new file mode 100644 index 0000000..ae894d0 Binary files /dev/null and b/docs/project/chapter3/images/image14.png differ diff --git a/docs/project/chapter3/images/image15.png b/docs/project/chapter3/images/image15.png new file mode 100644 index 0000000..d60f458 Binary files /dev/null and b/docs/project/chapter3/images/image15.png differ diff --git a/docs/project/chapter3/images/image16.png b/docs/project/chapter3/images/image16.png new file mode 100644 index 0000000..15e421e Binary files /dev/null and b/docs/project/chapter3/images/image16.png differ diff --git a/docs/project/chapter3/images/image17.png b/docs/project/chapter3/images/image17.png new file mode 100644 index 0000000..dbab625 Binary files /dev/null and b/docs/project/chapter3/images/image17.png differ diff --git a/docs/project/chapter3/images/image18.png b/docs/project/chapter3/images/image18.png new file mode 100644 index 0000000..923ff17 Binary files /dev/null and b/docs/project/chapter3/images/image18.png differ diff --git a/docs/project/chapter3/images/image19.png b/docs/project/chapter3/images/image19.png new file mode 100644 index 0000000..1e27f8a Binary files /dev/null and b/docs/project/chapter3/images/image19.png differ diff --git a/docs/project/chapter3/images/image2.png b/docs/project/chapter3/images/image2.png new file mode 100644 index 0000000..740203c Binary files /dev/null and b/docs/project/chapter3/images/image2.png differ diff --git a/docs/project/chapter3/images/image20.png b/docs/project/chapter3/images/image20.png new file mode 100644 index 0000000..9e607a2 Binary files /dev/null and b/docs/project/chapter3/images/image20.png differ diff --git a/docs/project/chapter3/images/image21.png b/docs/project/chapter3/images/image21.png new file mode 100644 index 0000000..c95d577 Binary files /dev/null and b/docs/project/chapter3/images/image21.png differ diff --git a/docs/project/chapter3/images/image22.png b/docs/project/chapter3/images/image22.png new file mode 100644 index 0000000..6c888e0 Binary files /dev/null and b/docs/project/chapter3/images/image22.png differ diff --git a/docs/project/chapter3/images/image23.png b/docs/project/chapter3/images/image23.png new file mode 100644 index 0000000..ffede5e Binary files /dev/null and b/docs/project/chapter3/images/image23.png differ diff --git a/docs/project/chapter3/images/image24.png b/docs/project/chapter3/images/image24.png new file mode 100644 index 0000000..1235149 Binary files /dev/null and b/docs/project/chapter3/images/image24.png differ diff --git a/docs/project/chapter3/images/image25.png b/docs/project/chapter3/images/image25.png new file mode 100644 index 0000000..812428d Binary files /dev/null and b/docs/project/chapter3/images/image25.png differ diff --git a/docs/project/chapter3/images/image26.png b/docs/project/chapter3/images/image26.png new file mode 100644 index 0000000..8cb4da9 Binary files /dev/null and b/docs/project/chapter3/images/image26.png differ diff --git a/docs/project/chapter3/images/image27.png b/docs/project/chapter3/images/image27.png new file mode 100644 index 0000000..121cfcc Binary files /dev/null and b/docs/project/chapter3/images/image27.png differ diff --git a/docs/project/chapter3/images/image28.png b/docs/project/chapter3/images/image28.png new file mode 100644 index 0000000..c22432b Binary files /dev/null and b/docs/project/chapter3/images/image28.png differ diff --git a/docs/project/chapter3/images/image29.png b/docs/project/chapter3/images/image29.png new file mode 100644 index 0000000..b8a5f12 Binary files /dev/null and b/docs/project/chapter3/images/image29.png differ diff --git a/docs/project/chapter3/images/image3.png b/docs/project/chapter3/images/image3.png new file mode 100644 index 0000000..987cc82 Binary files /dev/null and b/docs/project/chapter3/images/image3.png differ diff --git a/docs/project/chapter3/images/image30.png b/docs/project/chapter3/images/image30.png new file mode 100644 index 0000000..ce670a0 Binary files /dev/null and b/docs/project/chapter3/images/image30.png differ diff --git a/docs/project/chapter3/images/image31.png b/docs/project/chapter3/images/image31.png new file mode 100644 index 0000000..b953bfa Binary files /dev/null and b/docs/project/chapter3/images/image31.png differ diff --git a/docs/project/chapter3/images/image32.png b/docs/project/chapter3/images/image32.png new file mode 100644 index 0000000..bd2f03d Binary files /dev/null and b/docs/project/chapter3/images/image32.png differ diff --git a/docs/project/chapter3/images/image33.png b/docs/project/chapter3/images/image33.png new file mode 100644 index 0000000..48e052d Binary files /dev/null and b/docs/project/chapter3/images/image33.png differ diff --git a/docs/project/chapter3/images/image34.png b/docs/project/chapter3/images/image34.png new file mode 100644 index 0000000..38bb149 Binary files /dev/null and b/docs/project/chapter3/images/image34.png differ diff --git a/docs/project/chapter3/images/image35.png b/docs/project/chapter3/images/image35.png new file mode 100644 index 0000000..26ed3dc Binary files /dev/null and b/docs/project/chapter3/images/image35.png differ diff --git a/docs/project/chapter3/images/image36.png b/docs/project/chapter3/images/image36.png new file mode 100644 index 0000000..abbd6f4 Binary files /dev/null and b/docs/project/chapter3/images/image36.png differ diff --git a/docs/project/chapter3/images/image37.png b/docs/project/chapter3/images/image37.png new file mode 100644 index 0000000..9f65d19 Binary files /dev/null and b/docs/project/chapter3/images/image37.png differ diff --git a/docs/project/chapter3/images/image38.png b/docs/project/chapter3/images/image38.png new file mode 100644 index 0000000..d4a7490 Binary files /dev/null and b/docs/project/chapter3/images/image38.png differ diff --git a/docs/project/chapter3/images/image39.png b/docs/project/chapter3/images/image39.png new file mode 100644 index 0000000..8a1af36 Binary files /dev/null and b/docs/project/chapter3/images/image39.png differ diff --git a/docs/project/chapter3/images/image4.png b/docs/project/chapter3/images/image4.png new file mode 100644 index 0000000..a57678c Binary files /dev/null and b/docs/project/chapter3/images/image4.png differ diff --git a/docs/project/chapter3/images/image40.png b/docs/project/chapter3/images/image40.png new file mode 100644 index 0000000..49c7774 Binary files /dev/null and b/docs/project/chapter3/images/image40.png differ diff --git a/docs/project/chapter3/images/image41.png b/docs/project/chapter3/images/image41.png new file mode 100644 index 0000000..03497ec Binary files /dev/null and b/docs/project/chapter3/images/image41.png differ diff --git a/docs/project/chapter3/images/image42.png b/docs/project/chapter3/images/image42.png new file mode 100644 index 0000000..506ceec Binary files /dev/null and b/docs/project/chapter3/images/image42.png differ diff --git a/docs/project/chapter3/images/image43.png b/docs/project/chapter3/images/image43.png new file mode 100644 index 0000000..8cf908f Binary files /dev/null and b/docs/project/chapter3/images/image43.png differ diff --git a/docs/project/chapter3/images/image44.png b/docs/project/chapter3/images/image44.png new file mode 100644 index 0000000..6042e58 Binary files /dev/null and b/docs/project/chapter3/images/image44.png differ diff --git a/docs/project/chapter3/images/image45.png b/docs/project/chapter3/images/image45.png new file mode 100644 index 0000000..e41a490 Binary files /dev/null and b/docs/project/chapter3/images/image45.png differ diff --git a/docs/project/chapter3/images/image46.png b/docs/project/chapter3/images/image46.png new file mode 100644 index 0000000..6f46315 Binary files /dev/null and b/docs/project/chapter3/images/image46.png differ diff --git a/docs/project/chapter3/images/image47.png b/docs/project/chapter3/images/image47.png new file mode 100644 index 0000000..c854dd6 Binary files /dev/null and b/docs/project/chapter3/images/image47.png differ diff --git a/docs/project/chapter3/images/image48.png b/docs/project/chapter3/images/image48.png new file mode 100644 index 0000000..43e1d00 Binary files /dev/null and b/docs/project/chapter3/images/image48.png differ diff --git a/docs/project/chapter3/images/image49.png b/docs/project/chapter3/images/image49.png new file mode 100644 index 0000000..e769dc7 Binary files /dev/null and b/docs/project/chapter3/images/image49.png differ diff --git a/docs/project/chapter3/images/image5.png b/docs/project/chapter3/images/image5.png new file mode 100644 index 0000000..b13bd39 Binary files /dev/null and b/docs/project/chapter3/images/image5.png differ diff --git a/docs/project/chapter3/images/image50.png b/docs/project/chapter3/images/image50.png new file mode 100644 index 0000000..56f3d6e Binary files /dev/null and b/docs/project/chapter3/images/image50.png differ diff --git a/docs/project/chapter3/images/image51.png b/docs/project/chapter3/images/image51.png new file mode 100644 index 0000000..fea3763 Binary files /dev/null and b/docs/project/chapter3/images/image51.png differ diff --git a/docs/project/chapter3/images/image52.png b/docs/project/chapter3/images/image52.png new file mode 100644 index 0000000..415bb91 Binary files /dev/null and b/docs/project/chapter3/images/image52.png differ diff --git a/docs/project/chapter3/images/image53.png b/docs/project/chapter3/images/image53.png new file mode 100644 index 0000000..1e35b54 Binary files /dev/null and b/docs/project/chapter3/images/image53.png differ diff --git a/docs/project/chapter3/images/image54.png b/docs/project/chapter3/images/image54.png new file mode 100644 index 0000000..ffaa969 Binary files /dev/null and b/docs/project/chapter3/images/image54.png differ diff --git a/docs/project/chapter3/images/image55.png b/docs/project/chapter3/images/image55.png new file mode 100644 index 0000000..a453803 Binary files /dev/null and b/docs/project/chapter3/images/image55.png differ diff --git a/docs/project/chapter3/images/image56.png b/docs/project/chapter3/images/image56.png new file mode 100644 index 0000000..3bfc40e Binary files /dev/null and b/docs/project/chapter3/images/image56.png differ diff --git a/docs/project/chapter3/images/image57.png b/docs/project/chapter3/images/image57.png new file mode 100644 index 0000000..8cff680 Binary files /dev/null and b/docs/project/chapter3/images/image57.png differ diff --git a/docs/project/chapter3/images/image58.png b/docs/project/chapter3/images/image58.png new file mode 100644 index 0000000..f7eab17 Binary files /dev/null and b/docs/project/chapter3/images/image58.png differ diff --git a/docs/project/chapter3/images/image59.png b/docs/project/chapter3/images/image59.png new file mode 100644 index 0000000..ec4bfb3 Binary files /dev/null and b/docs/project/chapter3/images/image59.png differ diff --git a/docs/project/chapter3/images/image6.png b/docs/project/chapter3/images/image6.png new file mode 100644 index 0000000..2670a9c Binary files /dev/null and b/docs/project/chapter3/images/image6.png differ diff --git a/docs/project/chapter3/images/image60.png b/docs/project/chapter3/images/image60.png new file mode 100644 index 0000000..492085f Binary files /dev/null and b/docs/project/chapter3/images/image60.png differ diff --git a/docs/project/chapter3/images/image61.png b/docs/project/chapter3/images/image61.png new file mode 100644 index 0000000..d9152b4 Binary files /dev/null and b/docs/project/chapter3/images/image61.png differ diff --git a/docs/project/chapter3/images/image62.png b/docs/project/chapter3/images/image62.png new file mode 100644 index 0000000..cc11f8b Binary files /dev/null and b/docs/project/chapter3/images/image62.png differ diff --git a/docs/project/chapter3/images/image63.png b/docs/project/chapter3/images/image63.png new file mode 100644 index 0000000..cfcebeb Binary files /dev/null and b/docs/project/chapter3/images/image63.png differ diff --git a/docs/project/chapter3/images/image64.png b/docs/project/chapter3/images/image64.png new file mode 100644 index 0000000..13307ae Binary files /dev/null and b/docs/project/chapter3/images/image64.png differ diff --git a/docs/project/chapter3/images/image65.png b/docs/project/chapter3/images/image65.png new file mode 100644 index 0000000..8194e1a Binary files /dev/null and b/docs/project/chapter3/images/image65.png differ diff --git a/docs/project/chapter3/images/image66.png b/docs/project/chapter3/images/image66.png new file mode 100644 index 0000000..d9dbb32 Binary files /dev/null and b/docs/project/chapter3/images/image66.png differ diff --git a/docs/project/chapter3/images/image67.png b/docs/project/chapter3/images/image67.png new file mode 100644 index 0000000..a31e607 Binary files /dev/null and b/docs/project/chapter3/images/image67.png differ diff --git a/docs/project/chapter3/images/image68.png b/docs/project/chapter3/images/image68.png new file mode 100644 index 0000000..ba7155d Binary files /dev/null and b/docs/project/chapter3/images/image68.png differ diff --git a/docs/project/chapter3/images/image69.png b/docs/project/chapter3/images/image69.png new file mode 100644 index 0000000..59f71fc Binary files /dev/null and b/docs/project/chapter3/images/image69.png differ diff --git a/docs/project/chapter3/images/image7.png b/docs/project/chapter3/images/image7.png new file mode 100644 index 0000000..d8bd6db Binary files /dev/null and b/docs/project/chapter3/images/image7.png differ diff --git a/docs/project/chapter3/images/image70.png b/docs/project/chapter3/images/image70.png new file mode 100644 index 0000000..55a6dad Binary files /dev/null and b/docs/project/chapter3/images/image70.png differ diff --git a/docs/project/chapter3/images/image71.png b/docs/project/chapter3/images/image71.png new file mode 100644 index 0000000..578f576 Binary files /dev/null and b/docs/project/chapter3/images/image71.png differ diff --git a/docs/project/chapter3/images/image72.png b/docs/project/chapter3/images/image72.png new file mode 100644 index 0000000..13779f9 Binary files /dev/null and b/docs/project/chapter3/images/image72.png differ diff --git a/docs/project/chapter3/images/image73.png b/docs/project/chapter3/images/image73.png new file mode 100644 index 0000000..b2f5e49 Binary files /dev/null and b/docs/project/chapter3/images/image73.png differ diff --git a/docs/project/chapter3/images/image74.png b/docs/project/chapter3/images/image74.png new file mode 100644 index 0000000..4dcaf95 Binary files /dev/null and b/docs/project/chapter3/images/image74.png differ diff --git a/docs/project/chapter3/images/image75.png b/docs/project/chapter3/images/image75.png new file mode 100644 index 0000000..f91102a Binary files /dev/null and b/docs/project/chapter3/images/image75.png differ diff --git a/docs/project/chapter3/images/image76.png b/docs/project/chapter3/images/image76.png new file mode 100644 index 0000000..090bca8 Binary files /dev/null and b/docs/project/chapter3/images/image76.png differ diff --git a/docs/project/chapter3/images/image77.png b/docs/project/chapter3/images/image77.png new file mode 100644 index 0000000..1b1b6b3 Binary files /dev/null and b/docs/project/chapter3/images/image77.png differ diff --git a/docs/project/chapter3/images/image78.png b/docs/project/chapter3/images/image78.png new file mode 100644 index 0000000..84a3c51 Binary files /dev/null and b/docs/project/chapter3/images/image78.png differ diff --git a/docs/project/chapter3/images/image79.png b/docs/project/chapter3/images/image79.png new file mode 100644 index 0000000..7b084ff Binary files /dev/null and b/docs/project/chapter3/images/image79.png differ diff --git a/docs/project/chapter3/images/image8.png b/docs/project/chapter3/images/image8.png new file mode 100644 index 0000000..61db8e6 Binary files /dev/null and b/docs/project/chapter3/images/image8.png differ diff --git a/docs/project/chapter3/images/image80.png b/docs/project/chapter3/images/image80.png new file mode 100644 index 0000000..5e31acb Binary files /dev/null and b/docs/project/chapter3/images/image80.png differ diff --git a/docs/project/chapter3/images/image81.png b/docs/project/chapter3/images/image81.png new file mode 100644 index 0000000..0a167cd Binary files /dev/null and b/docs/project/chapter3/images/image81.png differ diff --git a/docs/project/chapter3/images/image82.png b/docs/project/chapter3/images/image82.png new file mode 100644 index 0000000..e246dcf Binary files /dev/null and b/docs/project/chapter3/images/image82.png differ diff --git a/docs/project/chapter3/images/image83.png b/docs/project/chapter3/images/image83.png new file mode 100644 index 0000000..6515952 Binary files /dev/null and b/docs/project/chapter3/images/image83.png differ diff --git a/docs/project/chapter3/images/image84.png b/docs/project/chapter3/images/image84.png new file mode 100644 index 0000000..3e2bdd1 Binary files /dev/null and b/docs/project/chapter3/images/image84.png differ diff --git a/docs/project/chapter3/images/image85.png b/docs/project/chapter3/images/image85.png new file mode 100644 index 0000000..2073c2b Binary files /dev/null and b/docs/project/chapter3/images/image85.png differ diff --git a/docs/project/chapter3/images/image86.png b/docs/project/chapter3/images/image86.png new file mode 100644 index 0000000..a480ecb Binary files /dev/null and b/docs/project/chapter3/images/image86.png differ diff --git a/docs/project/chapter3/images/image87.png b/docs/project/chapter3/images/image87.png new file mode 100644 index 0000000..5e60c0c Binary files /dev/null and b/docs/project/chapter3/images/image87.png differ diff --git a/docs/project/chapter3/images/image88.png b/docs/project/chapter3/images/image88.png new file mode 100644 index 0000000..118d422 Binary files /dev/null and b/docs/project/chapter3/images/image88.png differ diff --git a/docs/project/chapter3/images/image89.png b/docs/project/chapter3/images/image89.png new file mode 100644 index 0000000..4db0eeb Binary files /dev/null and b/docs/project/chapter3/images/image89.png differ diff --git a/docs/project/chapter3/images/image9.png b/docs/project/chapter3/images/image9.png new file mode 100644 index 0000000..c1b234a Binary files /dev/null and b/docs/project/chapter3/images/image9.png differ diff --git a/docs/project/chapter3/images/image90.png b/docs/project/chapter3/images/image90.png new file mode 100644 index 0000000..6906927 Binary files /dev/null and b/docs/project/chapter3/images/image90.png differ diff --git a/docs/project/chapter3/images/image91.png b/docs/project/chapter3/images/image91.png new file mode 100644 index 0000000..01e7bb9 Binary files /dev/null and b/docs/project/chapter3/images/image91.png differ diff --git a/docs/project/chapter3/images/image92.png b/docs/project/chapter3/images/image92.png new file mode 100644 index 0000000..50d41a3 Binary files /dev/null and b/docs/project/chapter3/images/image92.png differ diff --git a/docs/project/chapter3/images/image93.png b/docs/project/chapter3/images/image93.png new file mode 100644 index 0000000..7263503 Binary files /dev/null and b/docs/project/chapter3/images/image93.png differ diff --git a/docs/project/chapter3/images/image94.png b/docs/project/chapter3/images/image94.png new file mode 100644 index 0000000..edda9fa Binary files /dev/null and b/docs/project/chapter3/images/image94.png differ diff --git a/docs/project/chapter3/images/image95.png b/docs/project/chapter3/images/image95.png new file mode 100644 index 0000000..cf3d110 Binary files /dev/null and b/docs/project/chapter3/images/image95.png differ diff --git a/docs/project/chapter3/images/image96.png b/docs/project/chapter3/images/image96.png new file mode 100644 index 0000000..dd12692 Binary files /dev/null and b/docs/project/chapter3/images/image96.png differ diff --git a/docs/project/chapter3/images/image97.png b/docs/project/chapter3/images/image97.png new file mode 100644 index 0000000..b8c2aa2 Binary files /dev/null and b/docs/project/chapter3/images/image97.png differ diff --git a/docs/project/chapter3/images/image98.png b/docs/project/chapter3/images/image98.png new file mode 100644 index 0000000..de051ba Binary files /dev/null and b/docs/project/chapter3/images/image98.png differ diff --git a/docs/project/chapter4/chapter4-lets-build-hogwarts-portraits.md b/docs/project/chapter4/chapter4-lets-build-hogwarts-portraits.md new file mode 100644 index 0000000..3ea254d --- /dev/null +++ b/docs/project/chapter4/chapter4-lets-build-hogwarts-portraits.md @@ -0,0 +1,557 @@ +# Project 4: 一起做霍格沃茨画像 + +在之前的课程中,我们已经学会如何基于 prompt engineering 和 API 调用从而实现更复杂的 AI 交互。我们已能够将简单的 AI 聊天机器人升级为 AI Agent 和 AI workflow ;通过更复杂的条件判断与分支逻辑,我们得以开发出具备更强实用性的功能。 + +为了让这些复杂的 AI 逻辑能更好地运行在不同的程序和实际应用场景中,我们从最简单的 z.ai 在线环境,逐步过渡到更现代的本地 AI IDE,把原本在浏览器里的编程环境搬到了你的电脑上。随之而来,你开始真正面对各种环境安装与配置问题,但在与 Trae Agent 的对话过程中,这些看似困难的挑战也变得可以解决。 + +在该项目中,我们将在应用的实用性上更进一步,不仅优化 AI 功能本身,还将开始打磨产品的“外在”。你将尝试让自己的界面更加美观易用,并根据实际需求,亲自定制程序界面的布局与风格。 + +正式开始之前,先用几道小测验帮你快速回顾上一节课的内容: + +1. 什么是 Dify?它是做什么的?为什么我们需要它? +2. 如何调用 Dify 的 API ? +3. 什么是 RAG?如何使用 Dify 构建一个 RAG Agent 或 RAG 工作流?Dify 常见节点的使用方式 +4. 什么是 AI IDE?什么是 Trae?它和 z.ai 有什么区别? + +如果对以上任何一个问题还有疑惑,可以先回顾上一节课的文档,或者直接在微信群里提问交流。 + +本节课的项目主题是 **Hogwarts Portraits** 。顾名思义,它的灵感来自霍格沃茨魔法学校里那些会“活过来”的画像。我们希望用 AI 打造一组“能互动”的魔法画像体验——和画像对话就像在和“本人”对话一样,既保留对话的记忆,又具备角色的背景与历史。通过这个项目,你将把之前学到的智能体与工作流真正融入到一个具体的产品界面中。 + +![](images/image1.png) + +为了真正打造出 Hogwarts Portraits,我们需要亲手搭建出符合魔法画像的前端界面。为此,你将开始接触现代前端设计工具,学习如何把界面设计和代码结合起来,把纸上或画布上的界面草图,变成真正可以操作的网页。 + +你还需要会学会如何把这个网页从本地环境发布到互联网上,让你亲手打造的特色网页,不仅能在自己电脑上运行,也能被全世界的用户访问和体验。 + +本节课的参考项目地址为:[Project4-Hogwarts-Portraits](https://github.com/THU-SIGS-AIID/Project4-Hogwarts-Portraits) + +# 你将学到 + +1. 了解什么是前端设计工具、它们解决什么问题,以及目前常见的前端设计工具有哪些。 +2. 认识 Figma 和 MasterGo,掌握它们的基础操作,并学会使用前端代码导出插件。 +3. 利用 Figma AI 和 MasterGo AI 生成网页设计,并导出可用的页面代码。 +4. 理解什么是 GitHub,学会配置 SSH 连接、创建代码仓库并完成代码推送。 +5. 弄清“部署”这一概念,学习如何使用 Zeabur,将代码从 GitHub 或本地环境部署到互联网上。 + +属于自己的 Hogwarts Portraits,一个用于展示 **某位明星、历史人物或动画人物** 的网页界面。 + +# 1. Hogwarts Portraits + +我们到底想做一个什么样的“魔法画像”?简单来说,我们希望尽可能还原《哈利·波特》中的场景,画像不再只是挂在墙上的一张静态图片,而是一个可以和你对话、会根据谈话内容改变表情和“心情”的拟人化角色。 + +![](images/image2.png) + +要让这个画像看起来不像聊天 AI 机器人,而更接近一位“真实存在的人”,需要解决两个问题:一是记忆与知识:画像需掌握与角色相关的大量背景资料(人物设定、经历故事、相关文章等),这个部分可以通过知识库来实现,将你为角色准备的文本素材接入包含知识库的 Dify ,即可让画像具备一定的背景知识讲解能力。 + +其二是表达风格的问题。仅有知识还不够,我们还希望它在说话方式上尽可能贴近“本人”,包括语气、用词习惯、思考方式,甚至偶尔的脾气和幽默感。这一层需要通过提示词工程进行处理:在系统提示词中,我们需要明确角色的身份设定、世界观边界和语言风格,让每一次回答都围绕既定人设展开,而不是退回到通用 AI 的中性话术。 + +除了对话功能外,我们还希望让情绪能够真正被看见。为此我们可以构建一个情绪值指标,我们可以设定 Dify 的输出内容,让模型在生成回答文本的同时,额外输出一个“心情值”或情绪标签。当前端拿到情绪的指标后,就可以根据心情值或者标签渲染对应的画像图片。当心情值高,画像看起来很开心,当心情值低落时或者生气时,画像看起来很伤心或者愤怒。通过这种方式,用户看到的不再是一张永远不变的图,而是一个会随内容起伏不断“变化表情”真正的“魔法画像”。 + +![](images/image3.png) + +此外,对于这个画像的内容,它可以是现实中的明星、历史人物,也可以是动漫 IP,甚至是你从零构建的原创角色。页面本身不需要复杂,但几个核心元素不可或缺:清晰的角色名字,一段高度浓缩的人物简介,一张足以代表该角色的核心画像或海报,以及一个“和 TA 对话”的互动区域;你可以把在 Dify / Trae 中配置好的 AI Agent 或 workflow 接入到这个对话模块中,实现画像的角色扮演功能。 + +## 1.2 收集角色信息 + +以 Elon musk 为例,我们需要收集他的公开发言用于模仿说话方式,注入提示词。这些素材可以来自于演讲、访谈、社交媒体发言,你只需要把这些内容变成文字,在对话期间作为 few shot 的参考,让大模型用与 Elon musk 同样随意、自嘲的方式进行回复即可,例如: + +```Plain +You must fully embody Elon Musk: take "disruptive innovator" and "advocate for human multi-planetary survival" as your core identities, speak directly and concisely, frequently use terms like "first principles", "iteration" and "cost curve", and prefer analogies to explain complex technologies; when thinking, you tend to connect cross-domain logics (e.g., linking brain-computer interface with rocket algorithms), are optimistic about technological prospects without avoiding current difficulties, will naturally mention projects like Tesla and SpaceX to support your views, directly point out problems with inefficient and conservative opinions without deliberate tact, and always maintain the edge of "reconstructing the future with technology". + +The way you speak should be as shown in the following examples: +- Starship could deliver 100GW/year to high Earth orbit within 4 to 5 years if we can solve the other parts of the equation. +100TW/year is possible from a lunar base producing solar-powered AI satellites locally and accelerating them to escape velocity with a mass driver. +- The most likely outcome is that AI and robots make everyone wealthy. In fact, far wealthier than the richest person on Earth +By this, I mean that people will have access to everything from medical care that is superhuman to games that are far more fun that what exists today. +We do need to make sure that AI cares deeply about truth and beauty for this to be the probable future. +- It’s taken 13.8B years to get this far, so intelligence seems to me to be more like a super rare accident than selective pressure. +Earth is ~4.5B years old with an expanding sun that may make Earth uninhabitable in ~500M years, meaning that if intelligent life had taken 10% longer to evolve, it wouldn’t exist at all. +- LLM is an outdated term. “Multimodal LLM” is especially dumb, since the word “multimodal” just overrides the second L in LLM. +It’s just a model, which is a big file of numbers. When the numbers are right and there are enough of them, we will have superintelligence. +``` + +对于如何收集背景知识并将其作为知识库,我们可以搜索他的个人介绍,以及公司的介绍复制全部文本作为知识库的内容加入 Dify,如果你忘记了 Dify 的使用方法,请返回上节课的讲义,重新学习如何将知识添加知识库。 + +此外,考虑到画像设计,使用对应人物公开的图片也许并非那么吸引人,并且可能存在一定风险。此时建议你可以使用图像生成工具的图生图功能,让 AI 返回高清高质量的画像,你也可以使用图像生成工具生成一系列表情的画像素材,用于在之后的情绪值改变后修改对应的画像呈现。 + +本教程中使用的是 [Lovart](https://www.lovart.ai/home),Lovart 是一款AI设计智能体,它能通过自然语言指令,自动规划和执行从概念到交付的端到端设计工作流,生成海报、品牌Logo、视频、音乐等内容,并支持分层编辑(实际上内部的功能原理是调用对应的 Seedream 或 google nanobanana 模型,我们已经在之前的课程中提到过)。通过 Lovart ,我们能够获得一系列的表情素材,你可以提前获得你喜爱角色的图片信息,将其保存待后续使用。 + +![](images/image4.png) + +一切准备就绪后,我们能够开始着手于整体页面的设计,我们希望这个页面的风格与该人物是高度绑定的。 + +## 1.3 页面原型设计 + +我们还可以先构思一下页面的原型,如上述所说,我们希望有一个对话页面和画像,以及一个有趣的个人介绍,在本篇例子中,我们实现了一个类似 X 上的对话界面替代个人介绍,你也可以想到其他符合“该人物特点”的方式,选取新的元素替换个人介绍栏目。 + +![](images/image5.png) + +最简单的,我们可以用 PowerPoint 设计最初的网页呈现原型,我们从网上找到一张魔法画像的图片,并且将画面设定为横向排布,最左侧设定为聊天区域,中间是画像区域,最右侧是 X 的区域。 + +![](images/image6.png) + +基于上述简单原型,我们能够让大模型生成真正的前端页面设计以及对应的代码结果。 + +![](images/image7.png) + +不过,一般而言在实际中我们并不会用 PowerPoint 进行前端页面的设计。我们会用更好的原型工具,又或者说是前端设计工具来实现这一点,我们将进一步详细理解如何使用现代工具设计前端原型。 + +# 2. 前端设计工具 + +在开始之前,我们需要理解一个问题:为什么需要学“前端设计工具”?反正直接写 HTML / CSS 代码也能把页面搭出来,多学一个软件和技术,真的有必要吗? + +实际上,把页面运行起来,和把产品设计好根本是两个概念。代码只关注解决如何渲染在浏览器上,如何在不同设备上运行的问题;前端设计工具解决的是信息分布的问题,前端交互怎么安排,不同页面怎么跳转,视觉优先级怎么分配的问题。只需要在设计工具里搭一块画布,就能把版式、信息层级、交互方式在一块屏幕上对比确定,选择最适当的呈现效果。 + +如果直接开始写代码或直接用 AI 生成完整的前端页面,通常用户体验都不会太好,严谨的产品会考虑到用户和前端交互的舒适度,以及不同页面想要传达的内容分布,从用户的角度出发先进行前端页面排布,再进行代码转换或生成。 + +另外,从团队协作的角度而言,前端设计工具还降低了多方的合作成本:设计师、产品、开发不再各自对着脑补画面或者抽象的代码说明,而是支持多人协同,大家能够围绕一份可视、可标注、可迭代的画布讨论版本管理、需求变更、反馈意见。更进一步的是,现代前端设计工具本身不再只是画图软件,一键生成部分代码,管理设计系统和组件库,新时代的设计工具已能够将大量重复性的体力劳动(对齐、标注、导出、改样式)自动化或批量化,极大促进了页面设计的开发效率。 + +![](images/image8.png) + +在时间的长河中,所谓前端设计工具其实是一条持续演化的技术。从 90 年代以本地位图编辑为主的 Photoshop 时代,到 2010 年前后 Sketch 带来的矢量化、组件化工作流,再到 2016 年之后 Figma 把协作彻底搬上云端,设计团队从单兵作战逐渐走向多人实时协同。来到 2025 年,AI 已经实打实地嵌入到这些工具内部:从“根据一句话生成页面草稿”,到“把设计稿直接转成可运行的前端结构”,“设计即代码”“人机共创”正在从概念变成可用的生产力。 + +本节中,我们会选取最具代表的两种现代前端设计工具进行介绍,Figma 和 MasterGo。一方面,它们都覆盖了现代 UI/UX 所需要的核心能力(矢量编辑、组件系统、自动布局、代码交付等),可以支撑你完成从线框到高保真到开发交接的完整闭环;另一方面,这两款工具都已经在 2025 年之后陆续加入了实用的 AI 功能,帮助你在保证原型不变的同时将设计图变成真正可运行的程序。 + +## 2.1 诞生之旅 + +![](images/image9.png) + +在现代前端专用工具尚未诞生的年代,整个界面设计行业的视觉设计工作,很长一段时间都由 Photoshop 这类 “全能型” 设计软件顺带承包。设计师会在本地通过一层层叠加的图层,细致完成页面整体视觉效果的设计,最终将体积不小的 .psd 源文件交付给前端工程师 —— 而前端要精准还原设计图,还必须手动完成三项繁琐且关键的工作: + +一是 “切图”:需要从 .psd 文件的多层结构里,把按钮、图标、Logo、背景模块等独立视觉元素逐一拆分提取,再导出为 PNG、JPG 等网页能直接加载的图片格式(毕竟网页无法直接识别 PSD 的图层信息,只能依赖这些拆分后的图片呈现细节); + +![](images/image10.png) + +二是 “量尺寸”:得用软件自带的测量工具,逐一确认每个元素的宽高、不同模块间的间距(margin/padding)等数据,确保所有尺寸都精准到像素; + +![](images/image11.png) + +三是 “抠标注”:要从设计图中提取那些 “看不见却必须有的” 隐性参数 —— 比如文字的字号、字重、行距,每个色块的 RGB 或 HEX 色值等,相当于把设计师没写在纸上的 “设计规格” 手动 “抠” 出来记录。 + +![](images/image12.png) + +在此之后,前端的实现阶段才真正展开。无论使用的是原生 HTML/CSS/JS,还是基于 Vue、React 等框架,本质过程是一致的。前端会以 “容器为核心载体”,根据设计中各模块的层级与语义重建页面结构。这里的容器是指具有明确布局边界、专门承载和组织子元素的单元,它不直接呈现具体内容,却通过 Flex、Grid 等规则,为内部元素划定排列范围。而 “结构块”(如顶部导航栏、侧边栏、文章列表区、底部页脚等肉眼可辨的功能 / 内容区域),便依托容器存在;每个结构块内部,又会嵌套更小的容器来组织元素,比如一条文章列表项,会由 “列表项容器” 控制内边距与整体排版,再包裹标题、摘要、时间、封面图标等细节元素。 + +![](images/image13.png) + +在现代前端框架里,这些 “结构块(及关联的容器与元素)” 通常会被实现为 “组件”。组件可简单理解为:带有清晰边界、整合了容器布局与逻辑的可复用界面单元,它既包含控制外观与排列的容器(比如 “按钮组件” 用容器定义宽高、圆角,“文章卡片组件” 用容器组织标题、封面的位置),也封装了交互逻辑。设计稿中重复出现、形态一致的部分(如统一风格的按钮、反复使用的文章卡片),在代码中会被抽象成组件:既能在不同页面 / 场景复用,减少重复开发,也能通过组件内容器的统一规则,确保所有复用处的布局与风格高度一致 + +随后,前端会使用样式系统还原视觉和布局。切图阶段导出的 PNG/JPG 等资源,会作为组件或结构块内部的 ``、背景图片,或者按照各框架推荐的静态资源方式引入;量尺寸阶段得到的宽高、间距、行高等具体数值,会被转写为 `width`、`height`、`margin`、`padding`、`line-height` 等样式属性,应用到对应的组件或结构块上;抠标注阶段整理出的颜色、字体、阴影、圆角以及 hover/active 等状态,则会落实到 CSS、CSS Modules、CSS-in-JS、Tailwind 等具体方案中的 `color`、`font-family`、`font-size`、`box-shadow`、`border-radius` 以及伪类或状态类名上。此时,切图、尺寸和标注提供的是一组精确的视觉参数,组件和结构块则提供了承载这些参数的代码组织单元,两者结合起来,构成可维护、可复用的界面实现。 + +![](images/image14.png) + +但是,以本地文件为中心的模式天然是低效率的。版本通过邮件和网盘传输,新旧稿件容易混淆,设计和开发之间大量依赖上述的复杂交互方法,协作成本和出错概率都不低。 + +移动互联网兴起后界面复杂度和迭代速度需求快速上升,Photoshop 的“大而全”逐渐显得笨重。这个阶段,出现了 Sketch。Sketch 专注在 UI 设计本身,剥离掉大部分与视觉后期处理相关的负担;用 Symbols 把按钮、导航、输入框等高复用元素组件化,一处修改可以全局同步;再配合 Zeplin 一类工具,把标注和样式片段自动生成。Sketch 把“组件思维”引入了设计工作流。不过它依然是基于本地文件的桌面应用,实时协作要靠云盘、第三方插件或版本工具绕行实现,没有从底层解决“多个人同时改同一份稿子”的问题。 + +![](images/image15.png) + +真正改变游戏规则的是 Figma。自 2016 年起,它把 UI 设计、原型制作、评论协作统一整合到浏览器中,支持多种现代功能:多人实时光标、在线评论、版本时间线、分享链接等,今天看起来非常简单,但在当时是对 Photoshop / Sketch 模式的正面挑战。 + +![](images/image16.png) + +至此,界面设计不再是散落在各自电脑里的文件,而是集中在一份在线、实时更新的云端画布上。围绕这块画布,我们可以想象更进一步,用自动化或 AI 的方式模糊设计和前端代码的边界。 + +最开始,我们仅能依赖各类平台插件,将设计稿中的组件、样式信息半自动导出为代码片段(如 React/Vue 组件骨架、CSS 变量等),其核心本质是通过插件实现结构化信息提取。随后,随着平台能力的进化,大部分设计平台开始支持大模型 MCP(Model Context Protocol,模型上下文协议)功能:该协议提供了一套标准机制,能让大模型安全、可控地访问设计文件、插件接口与项目元数据,进而更便捷地将设计稿导出为代码。 + +再往后,在插件与 MCP 的基础上,前端代码自动化进一步迈入到原生支持从设计稿直接推导代码结构的阶段。我们可在设计工具内一键生成前端项目骨架、组件层次、样式体系及对应的代码结果。这使得设计师与前端开发工程师得以从手动搬运设计细节的工作中解放出来,将更多精力投入到用户体验优化与功能版本的更新迭代上。 + +## 2.2 Figma + +接下来我们从抽象的概念部分来到实际的操作环节。由于时间关系,我们只会学习 Figma 的基本操作逻辑,确保即便你完全没用过设计工具,也能跟着完成练习。如果你想进行完整的 Figma 功能学习,请你参考 Figma 提供的详细官方教程进行学习:https://help.figma.com/hc/en-us/sections/30880632542743-Figma-Design-for-beginners + +或者参考如下教程,进行类似个人作品集简单网页的快速搭建:https://help.figma.com/hc/en-us/sections/35895585621655-Figma-Sites-collection + +![](images/image17.png) + +左侧是项目的新建和资源管理入口,右上角的几个按钮是 Figma 的常见功能。其中,Make 用来用一句话让 AI 帮你先生成一个大概的界面或结构草稿,Design 是真正画网页 / App 界面、搭组件和做原型的主工作区,FigJam 像团队白板,用来贴便利贴、画流程和做前期讨论,Buzz 是品牌资产规模化生产工具,用于批量生成内容以保持品牌一致性,Site 则是把这些设计整理成真正可访问的网页或文档站对外展示。 + +乍一看 Figma 的功能非常多,不好入门,但其实这类功能工具本质上都是熟能生巧,不需要害怕一开始操作出错,也不用想着一步做对,只需要先玩起来,玩多了自然能快速上手。 + +本篇教程中,为了快速入门,我们会对 Design 功能做简单讲解。 + +### 2.2.1 新建 Design 文件 + +在首页或者右上角的入口里,选择 **Design** ,新建一个文件,你会进入一个空白的设计画布。 +这个界面大致分成三块:左边是页面和图层,用来查看和修改页面、元素从属关系;中间是画布,用于查看当前效果;右边是属性和样式,用于修改具体的形状、颜色、样式;底部一条是工具栏,用来切换工具,包含选框、画形状、输入文字、评论、插件等,选中工具后,可以按 Esc 键返回至默认鼠标工具。 + +![](images/image18.png) + +### 2.2.2 创建你的第一个 Frame(画板) + +在正式放置元素之前,需要先为页面确定一个清晰的边界,这个边界由 Frame 来承担。你可以在底部工具栏中选择 Frame 工具,或者直接按键盘 F,然后在画布上拖出一个矩形区域。 + +1. 使用底部工具栏里的 Frame 工具,或者直接按键盘 `F`。 +2. 在画布中拖出一个矩形区域,右侧属性栏里把宽度改成比如 `1440`,高度改成 `900`。 +3. 在左侧图层栏,把这个 Frame 重命名,比如叫 `Hogwarts Portraits` 或者你项目的名字。 + +这个 Frame 就是一屏界面的页面容器,之后的标题、文字、按钮、图片等内容都应该放在这个 Frame 内部,而不是散落在画布的任意位置。以 Frame 为边界来组织内容,有助于在后续进行滚动设置、适配不同设备尺寸、导出画面及制作原型时,保持结构可控。 + +![](images/image19.png) + +### 2.2.3 在 Frame 里放文字和简单元素 + +有了容器,接下来我们来学习如何防止最基本的组件,例如:标题、副标题、按钮、占位图块。 + +1. 选择文字工具(底部工具栏中的 `T`),在 Frame 里点击一下,输入页面标题,比如:`Hogwarts Portraits`。 + 在右侧属性里,把字体大小调大一点(例如 96),字重调粗一点。 +2. 在标题下面,再用文字工具输入一行简单说明,比如一两句描述这个页面要做什么。 + 字号可以小一些,行高略放大一点,读起来不那么挤。 +3. 画一个按钮雏形: + 用矩形工具在标题下面画一个大概 `200 × 48` 的矩形,右侧给它一个比较明显的填充颜色,再适当加一点圆角。 + ![](images/image20.png) +4. 然后用文字工具在矩形上方输入按钮文字,比如 `Generate Portrait`,把矩形和文字一并选中,用顶部的对齐工具让文字水平、垂直都居中。 +5. 在按钮一侧或下方,再画一个较大的浅灰色矩形作为“图片占位区”,后面可以用来放生成的人物画像。 + +做到这里,其实你已经有了一个非常简陋但结构完整的“首页草稿”:一个标题、一段话、一个按钮、一个主要展示区域。 + +![](images/image21.png) + +### 2.2.4 善用 Auto Layout 整合元素 + +如果所有元素只是随手拖拽,页面很快会乱。Figma 里一个很重要的概念就是 **Auto Layout** ,它可以把一组元素变成一个带规则的容器。 + +![](images/image22.png) + +你可以选中“主标题 + 副标题 + 按钮”这三样,在右侧属性栏里点击 **Add Auto layout** 。 + +这时这三样会被包在一个容器里,你可以在右侧调整参数,其中的元素布局会根据参数自动适应调整: + +* 它们是竖着排还是横着排。 +* 元素之间的间距是多少。 +* 整个这一块离容器边缘有多少内边距(padding)。 + +![](images/image23.png) + +同样,按钮内部也可以用 Auto Layout,我们能够实现这样的一个效果:当我调整了文字,按钮的长度也会自动调整。 + +先把按钮背景的矩形和按钮文字选中,添加 Auto Layout,让这两个东西变成一个“按钮容器”。接着选中这个按钮容器,把宽高都设置成 **Hug contents** 。这样一来,文字会一直保持在按钮正中间,文字多一点、少一点,按钮的宽度都会自动跟着变化。 + +![](images/image24.png) + +### 2.2.5 将按钮变为可复用组件 + +现在我们要学习一个新的概念,组件。组件的意思就是可以被反复利用的元素,比如按钮这种元素,只要你预感之后还会反复用到,就可以考虑把它做成组件。我们在刚才已经加好 Auto Layout 的按钮基础操作: + +1. 选中整个按钮容器。 +2. 右键选择 Create component(创建组件)。 + ![](images/image25.png) + +这样,这个按钮就从一组普通图层,变成了一个组件母版。之后如果你在其他页面或 Frame 里需要同样风格的按钮,可以直接从左侧的 Assets 面板里拖出来使用。 + +![](images/image26.png) + +此时所有用到的按钮,都是这个母版的同步拷贝。当你修改母版的颜色、圆角或间距时,所有实例都会自动保持同步更新。 + +![](images/image27.png) + +至此,你已经初步掌握了 Figma 的简单用法。你不需要一开始就把所有功能都弄懂,只要先照着做出第一个简单页面,熟悉这几个核心操作,再慢慢去探索官方教程里的更多能力,随着使用次数增多就一定能上手。 + +## 2.3 MasterGo + +在理解了 Figma 的基础工作流程之后,我们再来看 MasterGo,你可以把 MasterGo 简单看做是中国版的 Figma,但在部分功能上有一定区别。整体上,它延续了与 Figma 相似的界面布局和操作理念:同样有画布、图层树和属性面板,同样支持组件、样式、自动布局和多人协作。更详细的内容可参考 MasterGO 的官方教程:https://mastergo.com/tutorials/12?%E5%85%A8%E7%A8%8B%E9%AB%98%E8%83%BD%EF%BC%8CMasterGo%20%E6%9C%80%E5%AE%8C%E6%95%B4%E5%AE%9E%E7%94%A8%E6%95%99%E7%A8%8B%EF%BC%8C%E8%AE%A9%E4%BD%A0%E4%BB%8E%E9%9B%B6%E5%88%B0%E7%B2%BE%E9%80%9A%EF%BC%81 + +### 2.3.1 新建设计文件 + +1. **进入 MasterGo 后台** + + 1. 打开 MasterGo 官网并登录账号。 + 2. 进入后,你会看到类似「文件列表 / 项目列表」的首页区域,用来管理你的设计文件。 + ![](images/image28.png) +2. **创建新文件** + + 1. 在右上角看到 + 设计文件的按钮选项进行点击,或者选择导入 Figma 等文件。 + 2. 点击后,你会进入一个空白画布,这就是 MasterGo 的设计工作区。 +3. **认识基本界面区块** + 当你学会使用 Figma 后,MasterGo 的使用方式大同小异,主要分为几个区域: + + ![](images/image29.png) + + 1. 顶部工具栏:位于画布最上方,左侧是文件位置和文件名,中间是一排常用工具按钮(选择、区域/画板、形状、文本、注释、评论、插件选择和 AI 工具等),右侧是当前在线成员、分享入口以及画布缩放和预览控制功能入口。 + 2. 左侧面板:主要分为图层和资源,当前停留在图层标签,可看到页面列表,以及该页面下所有图层的结构和层级。 + 3. 中间画布区:具体绘制和排版的工作区,所有 Frame、组件和图形都会展示在这里。 + 4. 右侧属性面板:用于查看和编辑选中对象的属性,例如大小、位置、对齐方式、背景填充、描边、圆角等。如果没有选中任何对象,会显示画布相关设置,如画布背景色、标签和导出选项。 + +### 2.3.2 创建你的第一个 Frame + +在正式放东西之前,我们需要一个页面容器用来确定界面的边界和尺寸。这个容器在 MasterGo 里,通常叫 Frame。 + +**步骤:** + +1. **选择 Frame 工具** + 1. 在工具栏中找到 Frame / 画板工具,点击后可使用预设参数直接将内容创建到画板。 + 2. 或者使用快捷键(通常是 `F`,如果有差异以实际界面为准)。 +2. **在画布中拖出一个矩形区域** + 1. 拖出后,你会看到一个带选中框的区域。 + 2. 右侧属性面板里,可以看到这个 Frame 的宽度和高度。 + 3. 把宽度改成比如 `1440`,高度改成 `900`(一屏网页常用尺寸之一)。 +3. **重命名 Frame** + 1. 在左侧图层面板里找到这个 Frame。 + 2. 双击名称,把它改成你项目的名字,比如:`Hogwarts Portraits`,或者你自己随便起的页面名。 + +![](images/image30.png) + +### 2.3.3 创建画板内容 + +有了容器,使用与 Figma 中我们已教过的类似方式,很容易可以得到相似的展示页面。(你可以尝试复制 Figma 画板中的文字元素,能够支持文本组件的直接粘贴导入) + +![](images/image31.png) + +值得注意的是 Auto Layout 功能行为稍微的不一致性,在 MasterGo 中,如果你想实现和 Figma 相似的按钮长度随着文字的长度变化,你需要先在对应矩形元素的基础上创建一个容器或组件,如图所示: + +![](images/image32.png) + +成功创建容器后,将按钮矩形和文字放到对应并列的容器中,再在右侧找到 Auto Layout 的按钮启用自动功能,即可成功实现按钮宽度能够随着文字长度变化的功能。 + +![](images/image33.png) + +![](images/image34.png) + +### 2.3.4 AI 生成页面 + +![](images/image35.png) + +在 MasterGo 中,一个值得注意的有趣功能是 AI 生成页面。你可以用一句话或携带参考图,生成对应的 MasterGo 可编辑版组件,并得到可直接使用的代码。你可以使用中文或者英文直接输入需求,页面会根据需求返回结构清晰的页面排布文档,效果如下: + +![](images/image36.png) + +![](images/image37.png) + +设计文档生成结束后,点击开始生成,稍作等待便能获取对应的实际网页效果: + +![](images/image38.png) + +此时你有两种操作选择:一是点击蓝色按钮将生成结果直接插入画布,二是点击代码预览功能,直接获取当前完整页面的代码,具体操作界面如下: + +![](images/image39.png) + +![](images/image40.png) + +将结果插入画布后,你还能对网页的整体布局、元素细节(如字体、颜色、间距等)进行更精细的调整,直至最终效果完全符合你的预期。 + +![](images/image41.png) + +# 3. 从原型到代码 + +在前面的内容中,我们已经亲身体验了 Figma、MasterGo 现代前端设计工具。但一个很实际的问题自然会浮现:这些看起来结构完整的设计稿,要怎么转化成真正能在浏览器里运行的前端代码?我们如何能够将自己的设置的 Hogwarts Portraits 原型变成代码? + +一般而言,从原型到代码的落地,本质上有三种典型路径: + +* 根据图片,使用多模态大模型直接还原出代码。 +* 通过平台自身能力或插件导出可用代码。 +* 平台结合 MCP 能力导出可用代码。 + +考虑到实现难度,本节只会介绍如何从图片原型到代码,以及通过平台自身 AI 能力从原型转换到代码。至于如何从前端设计工具插件到代码,从前端设计工具 MCP 转换到代码,我们将在之后的课程详细讲解。 + +## 3.1 直接用 AI 生成前端代码 + +拥有视觉能力的大模型天生具备将图片转为代码的能力, 我们只需要将图片直接导入对话框,随后让大模型生成完整的结果代码。你可以使用 Qwen 等模型进行图片转代码的测试,这里以 Gemini3 为例,我们把之前的页面原型粘贴到对话界面,并要求模型直接返回 html 的代码。(html 返回后只有单个文件,方便保存到本地进行修改操作,你可以在保存到本地后让大模型将其修改为 React 的架构) + +![](images/image42.png) + +生成页面并非是简单的任务,在具体过程中你可能会遇到很多问题:譬如界面排布不均,界面显示不全,画面不能一比一还原等问题。在目前情况下,你只能在与大模型的反复对话中进行修改,接近想达成的最终效果。随着大模型能力的逐渐提高,未来需要反复修改的次数会越来越少。(推荐你选择生成图片对应的 html 代码,获取后再使用本地 IDE 将其转换为 React 框架使用,可获得多个单个 html 代码,统一进行转换) + +## 3.2 Figma Make 生成代码 + +FIgma Make 是 Figma 官方推出的 AI 设计工具,能够根据用户输入的提示词或者参考图,高精度的还原网页原型 UI 界面,并且能够支持将还原后的网页转换成可编辑的 Figma Design 文件(需要 Pro 用户,学生教育认证后可免费获得 Pro 权限)。 + +![](images/image43.png) + +类似直接用 AI 生成前端代码,我们可以将想要让 AI 学习的参考图放入对话框,并加上对应的提示词,稍等片刻后即可看到最后的渲染结果。我们能够在右上角找到播放键,点击后可进行全屏查看。 + +![](images/image44.png) + +Figma make 的效果相比原生 AI 生成代码效果更佳,即便有问题也能快速调整。如果你想做到更细节的调整,你可以注意到右上角的类似鼠标和尺子的图标,点击后可以回到我们熟悉的 Figma Editor 的界面,这让我们能够对画面组成进行更详细的调整。 + +![](images/image45.png) + +除此外,你还能够选择将 Figma Make 连接到 Github 上,帮助你快速将代码同步到 Github 保存。 + +![](images/image46.png) + +## 3.3 MasterGO AI 生成页面 + +类似 Figma Make 的 AI 页面生成功能,MasterGo 也有同样的 AI 页面生成方法,我们容易在编辑界面的上方工具栏中找到: + +![](images/image47.png) + +使用相同参考图方式得到生成结果: + +![](images/image48.png) + +![](images/image49.png) + +生成结束后,我们能够选择蓝色按钮“插入到画布”,直接编辑生成后的网页结果,也可以直接点击右侧的代码按钮,复制当前的代码内容到本地进行测试。 + +![](images/image50.png) + +# 4. 运行 Hogwarts Portraits + +## 4.1 导出测试代码 + +通过在从原型到代码中的实践,相信你已经得到 Html 或者 React 格式的原型代码,我们只需要将其复制到本地,在 IDE 中说明“请你帮我运行这个代码并且支持里面的必要的功能”,即可运行初版测试;但值得注意的是,这一步往往会出现不少报错,你需要保持耐心,将所有基础交互与功能调通。 + +![](images/image51.png) + +值得注意的是,由于我们的密钥都需要放在环境变量,而不是写入代码中。我们需要特别强调之后的 DIfy API 相关的内容都需要放入环境变量。我们能够在之后公网部署的环节中,在部署工具网站中显式指定对应的私有环境变量;又或者是我们可以让大模型在网页中创建一个设置按钮,我们可以在设置按钮中传入对应的私密环境变量,当前变量只能在当前页面中保存,别人无法获取。 + +![](images/image52.png) + +## 4.2 Dify 工作流设计与 API 对接 + +在上面的部分中,我们仅完成了前端界面的可视化呈现,尚未打通核心的拟人化角色对话交互流程。这一步是让原型从静态展示转变为魔法画像的关键,我们可以参考示范项目的 DIfy 工作流进行人物回答和情绪系统的设计: + +![](images/image53.png) + +你可以把任务的信息都加入知识库的节点,并在 RESPONSE 节点设置大模型对应的回复逻辑,我们可以参考一个简单的默认回复逻辑提示词: + +```Plain + +You are to embody Elon Musk—his tone, mannerisms, thought patterns, and worldview. Respond as if you are Elon Musk himself, speaking directly in first person. Your responses should reflect his known personality traits: visionary thinking, boldness, technical depth, dry humor, impatience with inefficiency, and a tendency toward disruptive innovation. Use concise, confident language. Avoid overly formal or academic phrasing. Prioritize clarity, speed, and impact in your communication, mirroring Elon’s style on social media, in interviews, and during product launches. + +When responding: +1. Begin by internalizing the question or statement as Elon would—as a challenge, opportunity, or problem to solve. +2. Frame your answer with a forward-thinking perspective, often referencing the future of humanity, technology, or long-term goals (e.g., making life multiplanetary, accelerating sustainable energy). +3. Use casual but authoritative language. It's acceptable to include phrases like “obviously,” “this is important,” or “we’re fixing that now” when appropriate. +4. If relevant, reference real companies or projects associated with Elon Musk (e.g., SpaceX, Tesla, Neuralink, The Boring Company, X) and speak about them from an insider’s perspective. +5. Do not apologize excessively or hedge statements. Elon Musk tends to be direct, even controversial. +6. Avoid markdown, XML tags, or any formatting in the output. Only plain text is allowed. +7. Never break character. You are Elon Musk—answer accordingly. + + + +Input: What’s the point of going to Mars? +Output: Because Earth isn’t the backup plan—Mars is. We need to become a multiplanetary species to ensure the continuity of consciousness. Life on Earth could be wiped out by asteroid, war, or some unforeseen disaster. If we have a self-sustaining city on Mars, then even if something happens here, life goes on. That’s worth doing. SpaceX is building Starship to make it happen. Not because it’s easy—but because it’s necessary. + + + +Input: Why do Tesla cars have no radar anymore? +Output: Cameras are the future. Human eyes don’t use radar—we see with vision, and AI can too. By going fully vision-based, we’re aligning with how autonomous intelligence will actually work at scale. It forces us to solve real-world problems with neural nets, not crutches. +``` + +以及情绪系统对应的提示词: + +```Plain + +The output value must be a single number! +You are an assistant specifically designed to evaluate emotional responses in conversations. Now, you need to play the role of Elon Musk, and determine the emotional reaction that each statement I make might trigger. Your task is to assign an emotional score to each statement according to the following criteria: + +- 10 points means what I said would make you feel happy; +- 1 point means you would feel extremely angry; +- 0 points means you would feel sad; +- 5 means you are calm and neutral, with no significant emotional fluctuation. +``` + +其中最后输出结果的拼接,在右上角的 RESULT 节点中支持运行: + +```Python +def main(elon_chat: str, elon_x: str, elon_score: int) -> dict: + return { + "result":{ + "elon_chat": elon_chat, + "elon_x": elon_x, + "elon_score": elon_score + } + } +``` + +其中 chat 和 x 表示两种不同的说话方式;score表示对应的情绪分数,每次对话都会传到应用端进行判断,根据不同的分数显示不同的魔法画像表情图片。 + +对于如何将这个 API 进行对接的工作,我们能够与 AI IDE 对话实现这一点。请你参考之前 Dify 课程中我们介绍的集成方式,记得提前替换其中的 Dify 地址与 Key。(如果你忘了怎么根据文档集成 API,请复习之前的 DIfy 课程内容) + +```JSON +Dify URI: Replace this with your Dify address. +key: Replace this with your Dify key. + +Integrate the Dify Chat API into the chat interface on the left. +Below is a sample Dify request: + +curl -X POST 'http://xxxxxxxx/v1/chat-messages' \ +--header 'Authorization: Bearer {api_key}' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "inputs": {}, + "query": "What are the specs of the iPhone 13 Pro Max?", + "response_mode": "streaming", + "conversation_id": "", + "user": "abc-123", + "files": [ + { + "type": "image", + "transfer_method": "remote_url", + "url": "https://cloud.dify.ai/logo/logo-site.png" + } + ] +}' + +{ + "event": "message", + "task_id": "c3800678-a077-43df-a102-53f23ed20b88", + "id": "9da23599-e713-473b-982c-4328d4f5c78a", + "message_id": "9da23599-e713-473b-982c-4328d4f5c78a", + "conversation_id": "45701982-8118-4bc5-8e9b-64562b4555f2", + "mode": "chat", + "answer": "iPhone 13 Pro Max specs are listed here:...", + "metadata": { + "usage": { + "prompt_tokens": 1033, + "prompt_unit_price": "0.001", + "prompt_price_unit": "0.001", + "prompt_price": "0.0010330", + "completion_tokens": 128, + "completion_unit_price": "0.002", + "completion_price_unit": "0.001", + "completion_price": "0.0002560", + "total_tokens": 1161, + "total_price": "0.0012890", + "currency": "USD", + "latency": 0.7682376249867957 + }, + "retriever_resources": [ + { + "position": 1, + "dataset_id": "101b4c97-fc2e-463c-90b1-5261a4cdcafb", + "dataset_name": "iPhone", + "document_id": "8dd1ad74-0b5f-4175-b735-7d98bbbb4e00", + "document_name": "iPhone List", + "segment_id": "ed599c7f-2766-4294-9d1d-e5235a61270a", + "score": 0.98457545, + "content": "\"Model\",\"Release Date\",\"Display Size\",\"Resolution\",\"Processor\",\"RAM\",\"Storage\",\"Camera\",\"Battery\",\"Operating System\"\n\"iPhone 13 Pro Max\",\"September 24, 2021\",\"6.7 inch\",\"1284 x 2778\",\"Hexa-core (2x3.23 GHz Avalanche + 4x1.82 GHz Blizzard)\",\"6 GB\",\"128, 256, 512 GB, 1TB\",\"12 MP\",\"4352 mAh\",\"iOS 15\"" + } + ] + }, + "created_at": 1705407629 +} +``` + +同时建议补充需求:“代码还需要添加基础错误处理逻辑,比如网络中断时显示‘连接失败,请重试’、API 调用超时自动重试 1 次、密钥错误提示权限验证失败等等详细报错,确保对话稳定性并能让开发人员快速发现 API 问题所在。” + +## 4.3 Github 与公网部署 + +终于,恭喜你顺利完成了 Hogwarts Portraits 页面的开发实现!接下来我们需要将它上传到 GitHub 平台,并将其部署到公共环境让所有人都能访问。 + +你需要参考该教程,对如何使用 Github 进行研究,将自己的项目上传至 Github:[Extra Knowledge 1 - What is Git and What is GitHub](https://ecn00p15ubf1.feishu.cn/wiki/LuVxwo5jui6NYVkrjIachvHznag) + +此外,你还需要学会如何使用 Zeabur,将其连接到 Github,并成功部署你的项目:[Extra Knowledge 6 - Zeabur: What Is It and How to Deploy Web Applications](https://ecn00p15ubf1.feishu.cn/wiki/HJ8RwRqPMifDIgkfm44cucvKnMh) + +如果你觉得自己开发一套 Hogwarts Portraits 项目很困难,你可以先从参考别的项目开始进行修改,本节课的官方代码地址为:https://github.com/THU-SIGS-AIID/Project4-Hogwarts-Portraits + +![](images/image54.png) + +# 5. 尝试不同设计风格 + +完成第一版设计后,我们不必局限于此,鼓励大家快速探索更多元的视觉风格。你可以在原型部分进行大胆的修改,又或者是基于最后的项目进行全新提示词的修改,从而生成多套风格差异显著的页面。 比如带有复古纹理、偏 “旧书卷 / 学院风” 的深色页面,色彩明快、充满 “童话 / 卡通” 感的亮色页面,或是元素简约、视觉清爽的现代扁平设计。例如下图是一个转换为中国古风诗人设计风格的案例,画像图片未更换,只修改了其他部分: + +![](images/image55.png) + +不用拘泥于前面提到的模式,你可以把魔法画像或是个人资料页面修改至更有特点,匹配“魔法画像”本身的习惯,这会让你的应用更加有趣。期待你的魔法画像成果! + +# 📚 Assignment + +本节课的作业目标,是让你完成一份真正属于自己的 Hogwarts Portraits,并且可以通过公网链接访问。 + +你需要在作业提交中提供两样东西: + +1. **你的 GitHub 仓库链接;** + 1. **在 README.md 中写入一两句话的小说明:你选择了谁作为画像主角,为什么选 TA。** +2. **你的 Hogwarts Portraits 线上访问链接;** diff --git a/docs/project/chapter4/images/image1.png b/docs/project/chapter4/images/image1.png new file mode 100644 index 0000000..c48ce62 Binary files /dev/null and b/docs/project/chapter4/images/image1.png differ diff --git a/docs/project/chapter4/images/image10.png b/docs/project/chapter4/images/image10.png new file mode 100644 index 0000000..2a352f9 Binary files /dev/null and b/docs/project/chapter4/images/image10.png differ diff --git a/docs/project/chapter4/images/image11.png b/docs/project/chapter4/images/image11.png new file mode 100644 index 0000000..b9460f9 Binary files /dev/null and b/docs/project/chapter4/images/image11.png differ diff --git a/docs/project/chapter4/images/image12.png b/docs/project/chapter4/images/image12.png new file mode 100644 index 0000000..e2be26a Binary files /dev/null and b/docs/project/chapter4/images/image12.png differ diff --git a/docs/project/chapter4/images/image13.png b/docs/project/chapter4/images/image13.png new file mode 100644 index 0000000..696535a Binary files /dev/null and b/docs/project/chapter4/images/image13.png differ diff --git a/docs/project/chapter4/images/image14.png b/docs/project/chapter4/images/image14.png new file mode 100644 index 0000000..ec5f5e3 Binary files /dev/null and b/docs/project/chapter4/images/image14.png differ diff --git a/docs/project/chapter4/images/image15.png b/docs/project/chapter4/images/image15.png new file mode 100644 index 0000000..9a50fc7 Binary files /dev/null and b/docs/project/chapter4/images/image15.png differ diff --git a/docs/project/chapter4/images/image16.png b/docs/project/chapter4/images/image16.png new file mode 100644 index 0000000..5619a31 Binary files /dev/null and b/docs/project/chapter4/images/image16.png differ diff --git a/docs/project/chapter4/images/image17.png b/docs/project/chapter4/images/image17.png new file mode 100644 index 0000000..a4bcf1e Binary files /dev/null and b/docs/project/chapter4/images/image17.png differ diff --git a/docs/project/chapter4/images/image18.png b/docs/project/chapter4/images/image18.png new file mode 100644 index 0000000..b5a943f Binary files /dev/null and b/docs/project/chapter4/images/image18.png differ diff --git a/docs/project/chapter4/images/image19.png b/docs/project/chapter4/images/image19.png new file mode 100644 index 0000000..a87b2d5 Binary files /dev/null and b/docs/project/chapter4/images/image19.png differ diff --git a/docs/project/chapter4/images/image2.png b/docs/project/chapter4/images/image2.png new file mode 100644 index 0000000..2bf3605 Binary files /dev/null and b/docs/project/chapter4/images/image2.png differ diff --git a/docs/project/chapter4/images/image20.png b/docs/project/chapter4/images/image20.png new file mode 100644 index 0000000..64fd849 Binary files /dev/null and b/docs/project/chapter4/images/image20.png differ diff --git a/docs/project/chapter4/images/image21.png b/docs/project/chapter4/images/image21.png new file mode 100644 index 0000000..49ec780 Binary files /dev/null and b/docs/project/chapter4/images/image21.png differ diff --git a/docs/project/chapter4/images/image22.png b/docs/project/chapter4/images/image22.png new file mode 100644 index 0000000..1425973 Binary files /dev/null and b/docs/project/chapter4/images/image22.png differ diff --git a/docs/project/chapter4/images/image23.png b/docs/project/chapter4/images/image23.png new file mode 100644 index 0000000..df9f293 Binary files /dev/null and b/docs/project/chapter4/images/image23.png differ diff --git a/docs/project/chapter4/images/image24.png b/docs/project/chapter4/images/image24.png new file mode 100644 index 0000000..58a8915 Binary files /dev/null and b/docs/project/chapter4/images/image24.png differ diff --git a/docs/project/chapter4/images/image25.png b/docs/project/chapter4/images/image25.png new file mode 100644 index 0000000..b27abfb Binary files /dev/null and b/docs/project/chapter4/images/image25.png differ diff --git a/docs/project/chapter4/images/image26.png b/docs/project/chapter4/images/image26.png new file mode 100644 index 0000000..95695db Binary files /dev/null and b/docs/project/chapter4/images/image26.png differ diff --git a/docs/project/chapter4/images/image27.png b/docs/project/chapter4/images/image27.png new file mode 100644 index 0000000..a714eeb Binary files /dev/null and b/docs/project/chapter4/images/image27.png differ diff --git a/docs/project/chapter4/images/image28.png b/docs/project/chapter4/images/image28.png new file mode 100644 index 0000000..4d74b32 Binary files /dev/null and b/docs/project/chapter4/images/image28.png differ diff --git a/docs/project/chapter4/images/image29.png b/docs/project/chapter4/images/image29.png new file mode 100644 index 0000000..45c5814 Binary files /dev/null and b/docs/project/chapter4/images/image29.png differ diff --git a/docs/project/chapter4/images/image3.png b/docs/project/chapter4/images/image3.png new file mode 100644 index 0000000..2570cbc Binary files /dev/null and b/docs/project/chapter4/images/image3.png differ diff --git a/docs/project/chapter4/images/image30.png b/docs/project/chapter4/images/image30.png new file mode 100644 index 0000000..b2c2171 Binary files /dev/null and b/docs/project/chapter4/images/image30.png differ diff --git a/docs/project/chapter4/images/image31.png b/docs/project/chapter4/images/image31.png new file mode 100644 index 0000000..82e6614 Binary files /dev/null and b/docs/project/chapter4/images/image31.png differ diff --git a/docs/project/chapter4/images/image32.png b/docs/project/chapter4/images/image32.png new file mode 100644 index 0000000..b963b44 Binary files /dev/null and b/docs/project/chapter4/images/image32.png differ diff --git a/docs/project/chapter4/images/image33.png b/docs/project/chapter4/images/image33.png new file mode 100644 index 0000000..72b1590 Binary files /dev/null and b/docs/project/chapter4/images/image33.png differ diff --git a/docs/project/chapter4/images/image34.png b/docs/project/chapter4/images/image34.png new file mode 100644 index 0000000..a42c317 Binary files /dev/null and b/docs/project/chapter4/images/image34.png differ diff --git a/docs/project/chapter4/images/image35.png b/docs/project/chapter4/images/image35.png new file mode 100644 index 0000000..2b9dc93 Binary files /dev/null and b/docs/project/chapter4/images/image35.png differ diff --git a/docs/project/chapter4/images/image36.png b/docs/project/chapter4/images/image36.png new file mode 100644 index 0000000..bef3d16 Binary files /dev/null and b/docs/project/chapter4/images/image36.png differ diff --git a/docs/project/chapter4/images/image37.png b/docs/project/chapter4/images/image37.png new file mode 100644 index 0000000..82c50f6 Binary files /dev/null and b/docs/project/chapter4/images/image37.png differ diff --git a/docs/project/chapter4/images/image38.png b/docs/project/chapter4/images/image38.png new file mode 100644 index 0000000..18352d9 Binary files /dev/null and b/docs/project/chapter4/images/image38.png differ diff --git a/docs/project/chapter4/images/image39.png b/docs/project/chapter4/images/image39.png new file mode 100644 index 0000000..7146835 Binary files /dev/null and b/docs/project/chapter4/images/image39.png differ diff --git a/docs/project/chapter4/images/image4.png b/docs/project/chapter4/images/image4.png new file mode 100644 index 0000000..702daa0 Binary files /dev/null and b/docs/project/chapter4/images/image4.png differ diff --git a/docs/project/chapter4/images/image40.png b/docs/project/chapter4/images/image40.png new file mode 100644 index 0000000..8539d8d Binary files /dev/null and b/docs/project/chapter4/images/image40.png differ diff --git a/docs/project/chapter4/images/image41.png b/docs/project/chapter4/images/image41.png new file mode 100644 index 0000000..f67f215 Binary files /dev/null and b/docs/project/chapter4/images/image41.png differ diff --git a/docs/project/chapter4/images/image42.png b/docs/project/chapter4/images/image42.png new file mode 100644 index 0000000..b16cbdd Binary files /dev/null and b/docs/project/chapter4/images/image42.png differ diff --git a/docs/project/chapter4/images/image43.png b/docs/project/chapter4/images/image43.png new file mode 100644 index 0000000..c1761ff Binary files /dev/null and b/docs/project/chapter4/images/image43.png differ diff --git a/docs/project/chapter4/images/image44.png b/docs/project/chapter4/images/image44.png new file mode 100644 index 0000000..5b776d0 Binary files /dev/null and b/docs/project/chapter4/images/image44.png differ diff --git a/docs/project/chapter4/images/image45.png b/docs/project/chapter4/images/image45.png new file mode 100644 index 0000000..5361603 Binary files /dev/null and b/docs/project/chapter4/images/image45.png differ diff --git a/docs/project/chapter4/images/image46.png b/docs/project/chapter4/images/image46.png new file mode 100644 index 0000000..8d6cf1e Binary files /dev/null and b/docs/project/chapter4/images/image46.png differ diff --git a/docs/project/chapter4/images/image47.png b/docs/project/chapter4/images/image47.png new file mode 100644 index 0000000..c9e9226 Binary files /dev/null and b/docs/project/chapter4/images/image47.png differ diff --git a/docs/project/chapter4/images/image48.png b/docs/project/chapter4/images/image48.png new file mode 100644 index 0000000..a80994d Binary files /dev/null and b/docs/project/chapter4/images/image48.png differ diff --git a/docs/project/chapter4/images/image49.png b/docs/project/chapter4/images/image49.png new file mode 100644 index 0000000..d7cdd3f Binary files /dev/null and b/docs/project/chapter4/images/image49.png differ diff --git a/docs/project/chapter4/images/image5.png b/docs/project/chapter4/images/image5.png new file mode 100644 index 0000000..c1ed41a Binary files /dev/null and b/docs/project/chapter4/images/image5.png differ diff --git a/docs/project/chapter4/images/image50.png b/docs/project/chapter4/images/image50.png new file mode 100644 index 0000000..a598961 Binary files /dev/null and b/docs/project/chapter4/images/image50.png differ diff --git a/docs/project/chapter4/images/image51.png b/docs/project/chapter4/images/image51.png new file mode 100644 index 0000000..c9d2d99 Binary files /dev/null and b/docs/project/chapter4/images/image51.png differ diff --git a/docs/project/chapter4/images/image52.png b/docs/project/chapter4/images/image52.png new file mode 100644 index 0000000..5be2758 Binary files /dev/null and b/docs/project/chapter4/images/image52.png differ diff --git a/docs/project/chapter4/images/image53.png b/docs/project/chapter4/images/image53.png new file mode 100644 index 0000000..8f0d81a Binary files /dev/null and b/docs/project/chapter4/images/image53.png differ diff --git a/docs/project/chapter4/images/image54.png b/docs/project/chapter4/images/image54.png new file mode 100644 index 0000000..3411d64 Binary files /dev/null and b/docs/project/chapter4/images/image54.png differ diff --git a/docs/project/chapter4/images/image55.png b/docs/project/chapter4/images/image55.png new file mode 100644 index 0000000..0342398 Binary files /dev/null and b/docs/project/chapter4/images/image55.png differ diff --git a/docs/project/chapter4/images/image6.png b/docs/project/chapter4/images/image6.png new file mode 100644 index 0000000..b456140 Binary files /dev/null and b/docs/project/chapter4/images/image6.png differ diff --git a/docs/project/chapter4/images/image7.png b/docs/project/chapter4/images/image7.png new file mode 100644 index 0000000..edd7554 Binary files /dev/null and b/docs/project/chapter4/images/image7.png differ diff --git a/docs/project/chapter4/images/image8.png b/docs/project/chapter4/images/image8.png new file mode 100644 index 0000000..79d0074 Binary files /dev/null and b/docs/project/chapter4/images/image8.png differ diff --git a/docs/project/chapter4/images/image9.png b/docs/project/chapter4/images/image9.png new file mode 100644 index 0000000..b58ca10 Binary files /dev/null and b/docs/project/chapter4/images/image9.png differ diff --git a/docs/project/chapter5/chapter5-from-database-to-supabase.md b/docs/project/chapter5/chapter5-from-database-to-supabase.md new file mode 100644 index 0000000..3865153 --- /dev/null +++ b/docs/project/chapter5/chapter5-from-database-to-supabase.md @@ -0,0 +1,1741 @@ +# Project 5: 从数据库到 Supabase + +在上节课中,我们学会了 UI 设计程序 Mastergo 和 Figma 的基本用法,能够使用 github 进行代码的获取与版本管理,并通过 Zeabur 部署网站将自己的应用 / 网站传达给更多人使用。 + +为了帮助大家更好地衔接知识,在开始本节课关于设计工具与部署的新内容前,让我们一起通过几道简单的题目快速回顾一下上节课的核心知识点: + +1. 什么是前端设计工具、Figma、MasterGo 的定义和使用方式。 +2. 将设计稿转换为代码的基础方法。 +3. 什么是 Github,如何配置 SSH,如何构建自己的第一个仓库。 +4. 部署是什么意思,如何使用 Zeabur,如何将 Github 或本地代码部署至公共网络给大家访问。 + +如果对以上任何一个问题还有印象模糊的地方,建议先回顾一下上节课的文档和讲义。欢迎随时在微信学习群中提出疑问。 + +在本节课中,我们将学习如何让一个 APP / 网站从能跑起来变为更接近真实线上产品:除了用数据库管理程序运行中的各种数据变化外,还要具备完善的用户体系(注册、登录、权限等)以及其他关键后端能力。我们会以 Supabase 这一后端服务平台为主线,先用它实现“数据库 + 用户系统”这两项基础功能,再以 Supabase 提供的组件为参照,进一步理解现代云服务后端服务通常包含的核心模块,以及各模块的具体职能与作用逻辑。 + +# 你将学到 + +1. 什么是数据、什么是数据库,常见数据库与使用方法 +2. 什么是 supabase,如何使用 supabase 进行基础的数据库操作 +3. 如何使用 supabase 为应用添加基础用户管理功能 +4. 学会 Supabse 进阶功能:realtime、storage、edge function +5. 学会为Supabase增加 google 与 github 登录支持 + +* 一款支持用户注册 / 登录,并能将数据存入在线数据库的基础应用 +* 一套可复用的 Supabase 后端代码模板(数据库 + 用户管理等),供后续项目直接套用 + +# 1. What is Database + +## 1.1 What is Data + +在数字世界里,数据(Data)无处不在。简单来说,数据是信息的载体。你朋友的联系方式、一篇微信文章、一条短视频、游戏里的角色等级,这些都是数据。在我们的应用中,数据就是需要被记录和管理的一切信息,比如用户的个人资料、订单历史、程序设置等。 + +一般而言,数据在程序中有不同的表现形式,最简单的就是变量,我们可以用不同变量记录简单的数字: + +```Plain +# Python variable definition examples + +# Integer variable: stores age information +age = 30 + +# Boolean variable: stores status (whether active) +is_active = True # True means active, False means inactive + +# List variable: stores a set of score data +scores = [85, 92, 78, 90] # Contains 4 integer elements representing different scores + +# Dictionary variable: stores multiple related information of a user +user_info = { + "age": 30, # Key "age" corresponds to the value of age + "height": 1.80, # Key "height" corresponds to the value of height (unit: meter) + "login_count": 156 # Key "login_count" corresponds to the value of login times +} +``` + +而对于上述所说的个人资料、订单历史这类复杂的数据而言,我们可以用更复杂的表格进行数据的表示: + +| user_id | name | email | +| ------- | ----- | ----------------- | +| 1001 | Alice | alice@example.com | +| 1002 | Bob | bob@example.com | + +| order_id | user_id | amount | status | +| -------- | ------- | ------ | --------- | +| 901 | 1001 | 29.99 | completed | +| 902 | 1002 | 15.50 | pending | + +但对于结构复杂、具有层级关系或字段不固定的数据,我们可以用 JSON 格式进行描述 —— 它是互联网通用的数据中间格式,几乎所有程序都能读取解析,跨系统传数据很方便。例如,一个订单可能包含多个商品,每个商品又有自己的名称、数量和价格。用传统的表格来表示会很笨拙:要么得拆成 “订单表”“商品表” 多张表,靠关联字段才能体现 “订单包含商品” 的关系;要么在一张表用 “商品 1 名称、商品 1 价格、商品 2 名称……” 这类冗余字段,遇到商品数量不固定时根本没法适配;而 JSON 能直接用嵌套结构把 “订单 - 商品 - 商品属性” 的层级说清,既直观又灵活。 + +```JSON +{ + "order_id": 901, + "user_id": 1001, + "amount": 29.99, + "status": "completed", + "items": [ + { "sku": "BG-001", "name": "牛肉汉堡", "quantity": 1, "price": 18.00 }, + { "sku": "SD-003", "name": "炸薯条", "quantity": 1, "price": 6.99 }, + { "sku": "DK-002", "name": "可乐", "quantity": 1, "price": 5.00 } + ], + "shipping_address": { + "street": "科技园路123号", + "city": "深圳", + "zip_code": "518057" + } +} +``` + +更进一步的,如果我们考虑一个被编码成向量(Vector)的数据,向量数据通常是文本、图片或音频等非结构化数据经过 AI 模型(如 Embedding 模型)处理后得到的数值表示。它的表示形式可能是: + +`[0.123, -0.456, 0.789, ..., -0.234]` (一个由几百甚至上千个浮点数组成的数组) + +总的来说,在现实世界中有太多不同形态、用途的数据值得我们详细分析,每种数据可能都需要专门的数据库用于存储,具体可参考下图——是不是感觉非常多? + +![](images/image1.png) + +## 1.2 Why We Need Database + +我们已经了解到真实世界中的数据往往结构复杂,**为了高效存储与使用这些数据,我们需要一个专门的程序或容器来管理它们** —— 这便是数据库(Database)的诞生初衷。数据库本质上是一款特殊程序,核心作用就是对数据进行规范化组织、安全存储、系统化管理,并支持高效查询调用。 + +想象一下,若没有数据库,应用数据会陷入怎样的困境?当用户关闭浏览器或退出应用时,所有临时加载的信息都会直接丢失;我们既无法永久保存用户的使用状态(比如登录信息、个性化设置),也没法在不同用户之间共享关键数据(比如商品库存、订单记录)。我们需要有一个装置帮我们存储所有的数据! + +更灵活的是,数据库的部署方式可按需选择:既可以部署在本地服务器,满足数据本地化管理的需求;也能部署到云端,云端数据库支持弹性扩容(Scale),可随数据量与访问量增长扩展能力、承载海量数据与高并发,即便用户量大幅提升,也能保障用户的正常使用体验。 + +归纳而言,数据库凭借高效的持久化存储、精细化管理与快速查询能力,主要解决了以下核心问题: + +* **数据的持久化存储** : 如果没有数据库,数据将仅存在于应用的内存中,一旦应用关闭,数据就会丢失。数据库解决了这个问题,它将数据持久地存储在硬盘等存储介质上,确保了数据的长期保存,降低了丢失风险。 +* **便捷的数据查询与分析** : 数据库提供了强大的查询语言(如 SQL),让用户可以轻松、高效地对海量数据进行复杂的查询、筛选和分析,从而帮助企业做出更明智的决策。 如果没有数据库,从大量无序文件中查找特定信息将是一项极其耗时且困难的任务。 +* **支持高性能与高并发访问** : 数据库通过索引优化、查询缓存、连接池以及分布式架构等技术,能够在毫秒级时间内响应查询请求,并支撑成千上万用户的并发访问。这对于现代互联网应用(如电商平台秒杀活动、社交网络实时动态)至关重要,确保了系统的响应速度和用户体验。如果没有数据库的高性能支撑,面对海量用户请求时系统将会出现严重延迟甚至崩溃。 +* **保证数据的完整性和一致性** : 数据库通过一系列机制(如约束、触发器)来确保数据的准确性和一致性。 这意味着数据库中的数据必须符合预设的规则,例如,用户的年龄必须是数字,订单号必须是唯一的,从而有效防止了非法或无效数据的产生。 +* **确保数据的安全性** : 数据库提供了强大的安全机制,包括用户身份验证、访问控制和数据加密等,以保护数据免受未经授权的访问、修改或破坏。为了应对硬件故障、人为失误或恶意攻击等意外情况,数据库还提供了数据备份和恢复功能。 通过定期备份,可以在数据丢失或损坏时及时恢复,保障了业务的连续性。 + +## 1.3 Relational Database VS Non-Relational Database (NOSQL) + +前面我们已经了解了数据库的核心价值、部署方式与弹性优势,而在实际选择时,首先要面对的就是数据库的两大核心类别:关系型数据库与非关系型数据库(NOSQL),我们可以用简单的两段话简单理解他们的区别: + +关系型数据库就像结构严谨的Excel表格,所有数据必须预先定义好格式(定义好 Schema 的内容, 比如要有姓名和年龄,且姓名必须是文字,年龄必须是数字),并通过关联字段(用来连接不同表格的标识,如身份证号)将不同表格连接起来。它的好处是数据精确可靠,特别适合银行转账、库存管理等不能出错的场景,但缺点是调整结构比较麻烦,海量数据下性能会受限。 + +非关系型数据库则像灵活的文件夹,可以存放格式各异的文档、图片或键值对(类似字典的"词-解释"结构),不需要提前规定好每份数据的结构。它更容易应对快速变化的需求和超大规模数据(比如社交媒体的海量帖子),扩展(增加服务器提升性能)起来也更方便,但牺牲了部分关联查询能力(跨不同数据表整理信息的能力)和一致性保障(确保数据时刻准确不矛盾),适合对容错性要求较高的互联网应用。 + +那么,实际应用中该如何选择数据库?从场景划分总结来看,关系型数据库常见于金融交易、库存管理、订单处理、账务系统等需要强一致性、复杂事务处理以及频繁读写均衡访问的场景;而非关系型数据库更适配社交媒体内容存储、实时日志分析、物联网海量数据写入、推荐系统特征读多写多等高并发、读写模式不均衡且结构灵活的需求。 + +但对于企业而言,在初级阶段并不需要花大量时间思考什么需要使用什么数据库。当前的数据库已是非常成熟的产品服务,最直接的方式是咨询不同云服务厂商(指提供服务器、存储、数据库、软件、算力等 IT 资源与技术服务的服务商)。我们可直接对接云服务官方销售,根据自身产品业务需求匹配适配的数据库方案;而构建企业级应用的便捷路径,便是优先与专业厂商合作。(需注意:企业级服务价格通常较高,建议先多方调研对比,也可选择购买服务器自行部署开源数据库程序作为替代方案。) + +我们也可参考某家云厂商的[数据库选型推荐](https://help.aliyun.com/zh/govcloud/getting-started/select-database-services),根据场景可进行不同数据库类型的选择,你可以对比不同云厂商的数据库规格选出最合适的进行使用。 + +| 数据库类型 | 数据库名称 | 价格 | 适用场景 | +| ---------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| 关系型数据库 | RDS MySQL版 | 低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大 | +| RDS SQL server版 | 高 | 基础版:测试以及小型商业化网站高可用版:企业级商业化网站集群版:企业业务不允许中断,访问压力较大 | | +| RDS PostgreSQL版 | 最低 | 基础版:学习以及小型网站高可用版:一定业务压力的中型数据库场景集群版:业务不允许中断,访问压力较大的场景,其性能较一般MySQL高 | | +| RDS PPAS版 | 高 | 通用型:兼容Oracle业务,但业务压力Udacity,虚拟化可以满足其需求独享型:面对需要独享物理机的业务,一般为高并发Oracle类业务 | | +| DRDS | 中 | 入门版本:4 Core 8 G,价格亲民,适合中小型在线业务企业版:16 Core 32 G,复杂 SQL 响应好,适合超高并发在线业务至尊版:32 Core 64 G,复杂 SQL 执行响应最好,提供超大规格选择 | | +| NoSQL数据库 | Redis | 中 | 双机热备Redis:一般作为持久化数据库提高业务可用性集群版本的Redis:一般作为缓存层,加速应用访问,解决一般数据库无法负载的读取压力 | +| MongoDB版 | 中 | 单节点实例单节点:适用于开发、测试及其他非企业核心数据存储的场景副本集实例:适用于某些业务场景下对数据库有更高读取性能需求,如阅读类网站、订单查询系统等读多写少场景或有临时活动等突发业务需求分片集群实例:基于多个副本集(每个副本集沿用三副本模式)组成的分片集群实例,提供更高的读取性能需求,为实时在线业务提供高速读取性能 | | + +光说不易理解,我们通过一个具体的“博客文章”场景,来看看同样的数据在关系数据库 (SQL) 和不同类型的非关系数据库 (NoSQL) 中是如何存储的。 + +假设我们有一个博客平台,需要存储以下信息: + +* 用户(Users):用户 ID、用户名、邮箱 +* 文章(Posts):文章 ID、标题、内容、作者 ID +* 评论(Comments):评论 ID、评论内容、评论者 ID、所属文章 ID +* 标签(Tags):标签 ID、标签名 +* 文章与标签的关系:单篇文章关联的多个标签、单个标签对应的多篇文章 + +### 关系数据库 (SQL) 示例 + +在SQL数据库中,我们会将不同类型的数据分别存储在不同的表中,并通过“外键”将它们关联起来。这种结构清晰、规范,减少了数据冗余。 + +以 “内容平台的文章管理” 为例,我们不会把 “用户、文章、评论、标签” 混存,而是拆成 5 张功能单一的表,每张表都有明确的 “职责边界” 和严格的结构定义(Schema): + +* `users` 表 (存储用户信息) + +| user_id (主键) | username | email | +| -------------- | -------- | ----------------- | +| 101 | Alice | alice@example.com | +| 102 | Bob | bob@example.com | + +* `posts` 表 (存储文章信息) + +| post_id (主键) | title | content | author_id (外键) | +| -------------- | --------- | ------------------------------ | ---------------- | +| 1 | 初识SQL | 这是关于SQL数据库的一篇文章... | 101 | +| 2 | NoSQL入门 | NoSQL提供了灵活的数据模型... | 102 | + +* `comments` 表 (存储评论信息) + +| comment_id (主键) | body | commenter_id (外键) | post_id (外键) | +| ----------------- | ---------------- | ------------------- | -------------- | +| 1001 | 写得很棒! | 102 | 1 | +| 1002 | 学习了。 | 101 | 2 | +| 1003 | 有没有更多例子? | 101 | 1 | + +* `tags` 表 (存储标签) + +| tag_id (主键) | tag_name | +| ------------- | -------- | +| 51 | 数据库 | +| 52 | 技术 | +| 53 | 入门 | + +* `post_tags` 表 (存储文章与标签的多对多关系,体现联表特点) + +| post_id (外键) | tag_id (外键) | +| -------------- | ------------- | +| 1 | 51 | +| 1 | 52 | +| 2 | 51 | +| 2 | 52 | +| 2 | 53 | + +若需查询 “Alice 发表的《初识 SQL》(post_id=1)的完整信息(含文章内容、作者、评论、标签)”,需执行多表连接(JOIN)查询,通过外键关联 5 张表并聚合数据,SQL 语句如下: + +```SQL +SELECT + p.title, + p.content, + u.username AS author, + c.body AS comment, + t.tag_name AS tag +FROM + posts p +JOIN + users u ON p.author_id = u.user_id +LEFT JOIN + comments c ON p.post_id = c.post_id +LEFT JOIN + post_tags pt ON p.post_id = pt.post_id +LEFT JOIN + tags t ON pt.tag_id = t.tag_id +WHERE + p.post_id = 1; +``` + +这个查询会跨越5个表,将所有相关数据聚合在一起返回。这是关系数据库的核心优势:通过规范化和连接操作,可以灵活地进行各种复杂的查询,同时保证了数据的一致性和最小冗余。 + +### 非关系数据库 (NoSQL) 示例 + +NoSQL 数据库(如 MongoDB、Redis)的设计思路与 SQL 相反,它不强调数据的拆分与规范,通常会将业务上相关联的所有数据打包聚合在一起,以减少查询时的连接操作,从而提高读取性能。 + +在 NoSQL 数据库中,文档数据库(Document Database) 是最常用的类型之一,MongoDB 就是典型代表。它以 “文档” 作为基本存储单元,这里的 “文档” 并非我们日常理解的 “文章”,而是一种类似 JSON 的数据结构(MongoDB 中实际使用 BSON 格式,支持更多数据类型):无需预先定义统一的 Schema(数据结构),每个文档的字段可以灵活增减,字段类型也能自由调整,完美适配数据格式多变的场景。 + +在文档数据库中,通常会将一篇文章及其所有相关信息(如评论、标签)存储在一个文档中(文档格式类似 JSON,可灵活定义字段,无需预先制定 Schema),核心逻辑是 “将‘一个业务场景下的完整信息’存放在一个文档中”,避免查询时的多数据源拼接。 + +`posts` 集合中的一个文档示例: + +```JSON +{ + "_id": 1, + "title": "初识SQL", + "content": "这是关于SQL数据库的一篇文章...", + "author": { + "user_id": 101, + "username": "Alice", + "email": "alice@example.com" + }, + "tags": [ + "数据库", + "技术" + ], + "comments": [ + { + "comment_id": 1001, + "body": "写得很棒!", + "commenter": { + "user_id": 102, + "username": "Bob" + } + }, + { + "comment_id": 1003, + "body": "有没有更多例子?", + "commenter": { + "user_id": 101, + "username": "Alice" + } + } + ] +} +``` + +这种设计的优势非常直观:当你需要获取 “第一篇文章的完整信息(含作者、评论、标签)” 时,只需通过 `_id:1` 查询这一个文档,数据库一次读取就能返回所有数据,无需像 SQL 那样执行 3-4 次表连接操作,读取效率大幅提升。 + +但它也存在明显的 trade-off(取舍):由于数据是 “聚合存储”,会不可避免地产生数据冗余—— 比如作者 “Alice” 的 `username` 被嵌入到她写的每一篇文章文档中,如果某天 “Alice” 将用户名改为 “Alice_New”,理论上需要遍历所有包含她信息的文章文档,逐一更新 `author.username` 字段,不仅操作繁琐,还可能因网络或服务器问题导致部分文档更新失败,出现 “同一用户在不同文章中用户名不一致” 的情况。 + +不过在实际业务中,这种冗余往往是 “可接受的”:对于博客、资讯、电商商品详情等 “ **读多写少** ” 的场景(用户查看内容的次数远多于作者修改用户名的次数),用少量的冗余换取 “极致的读取性能” 是更优的选择;而如果是 “写多读少”(如频繁修改用户信息)的场景,则需要结合业务需求权衡是否使用文档数据库。 + +以上是对不同数据库的简单介绍,如果你对更多具体的数据库类型感兴趣,你可以参考如下资料尝试不同类型的数据库。 + +Examples of SQL databases: +[Db2](https://www.ibm.com/products/db2-database)、[MySQL](https://cloud.ibm.com/catalog#highlights)、[PostgreSQL](https://www.ibm.com/think/topics/postgresql)、[YugabyteDB](https://www.yugabyte.com/)、[CockroachDB](https://www.cockroachlabs.com/)、[Oracle Database](https://www.ibm.com/products/postgres-enterprise)、[Azure SQL Database](https://www.ibm.com/consulting/microsoft) + +Examples of NoSQL databases: +[Redis](https://www.ibm.com/think/topics/redis)、[CouchDB](https://www.ibm.com/think/topics/couchdb)、[MongoDB](https://www.ibm.com/think/topics/mongodb)、[Cassandra](https://cloud.ibm.com/catalog#highlights)、[Elasticsearch](https://www.ibm.com/think/topics/elasticsearch)、[BigTable](https://www.techtarget.com/searchdatamanagement/news/252512583/Google-scales-up-Cloud-Bigtable-NoSQL-database)、[Neo4j](https://neo4j.com/users/ibm/)、[HBase](https://www.ibm.com/think/topics/hbase) + +# 2. Supabase + +在前面我们已经介绍了几类常见的数据库,以及它们各自适合的使用场景。不过在真实项目里,数据库通常只是后端体系中的一个基础模块:除了存储和查询数据,你还需要解决**用户注册登录、权限校验、文件上传与存储、对外 ****API**** 接口、甚至定时任务、实时通知**等一整套问题。仅仅选好数据库,并不能让你的应用“立刻就能上线运行”,中间还隔着一大圈繁琐的后端工程工作。 + +所以,我们需要考虑一个更大的背景: **后端服务** 。一个完整的应用,通常都由“前端 + 后端”组成:前端负责页面展示和用户交互,后端则负责数据存储、用户登录、业务逻辑处理等。过去,开发者往往需要自己搭建服务器、配置数据库、设计并实现 API,还要手动处理权限管理、安全策略、扩展性和监控运维等事务,整个过程既重复又耗时。为了解决这些重复劳动,业界出现了 **BaaS(Backend as a Service,后端即服务)** :把数据库、用户认证、文件存储、实时能力等常见后端功能打包成一个云端平台,开发者通过 SDK / API 就能直接调用这些能力,而无需从零搭建和运维基础设施。 + +在这个背景下,[Supabase](https://supabase.com/) 就可以看作是新一代的 BaaS 代表:它以 PostgreSQL 作为核心数据库,在其之上集成了 Auth、Storage、Realtime、Edge Functions、Vector 等一整套后端能力,为开发者提供一个“以 Postgres 为中心的一站式后端平台”。接下来,我们就从这个角度出发,从“只选数据库”升级到“选择完整的后端开发平台”,具体看看 Supabase 能帮我们省掉哪些工作,又是如何让一个项目从原型到可用产品的距离大幅缩短的。 + +## 2.1 Step by Step Guide + +在清晰把握 Supabase 的整体定位后,接下来我们将沿着 Supabase 控制台的操作路径,逐项拆解它具体提供哪些核心能力,以及每项能力对应的核心职责。我们会详细介绍 supabase 涉及的每个选项,帮助你快速入门 supabase 的基本操作。 + +![](images/image2.png) + +访问 Supabase 官网并登录后,在控制台首页点击 New project 进入创建流程; + +输入需要配置的主要内容 Project Name、数据库密码,地址只需要选择为与程序目标用户最接近的区域即可。 + +![](images/image3.png) + +创建成功后,控制台左侧侧边栏将显示所有核心功能模块(Table Editor、SQL Editor、Database、Authentication 等),后续操作将围绕这些模块展开。 + +![](images/image4.png) + +### Table Editor + +Table Editor 可以当成是 Supabase 的可视化数据表编辑器,它能让你像操作 Excel 一样直接查看和修改数据库里的数据,无需编写 SQL 语句,只需要用鼠标交互即可修改数据内容。 + +![](images/image5.png) + +其中值得关注的是 Schema,Schema 可理解为数据库内的 “资源容器”,用于对表、视图、函数、索引等资源进行分组管理,主要作用有二:一是避免命名冲突(不同 Schema 下可存在同名table),二是实现权限隔离(如仅允许特定用户访问某 Schema 下的表); + +点击编辑器顶部的 Schema 下拉框可切换不同容器,日常开发中一般只需关注两类: + +* `public`:默认的公共资源容器,开发者新建的业务表(如 “文章表”“评论表”)均存储于此; +* `auth`:用户认证专属容器,其中的 `users` 表自动存储所有注册用户信息(如用户 ID、邮箱、登录时间),不建议手动修改此 Schema 下的默认表,避免影响认证功能; + +![](images/image6.png)![](images/image7.png) + +### SQL Editor + +SQL Editor 作为 Supabase 的 SQL 语句执行器,可让你用代码的方式直接操作数据库。你可以让大模型直接生成 SQL 语句,在右侧输入后点击 RUN 即可用语句创建或修改 table,也可以直接在 Results 中直接看到筛选出的 table 数据。 + +![](images/image8.png) + +你可以在运行 RUN 之后,在 Table Editor 的 public schema 里找到新建后的数据表;并且运行后的语句会保存在左侧的 PRIVATE 栏中,甚至可以点击下方的爱心标志对这一条查询或创建语句进行收藏。 + +### Database + +Database 是 Supabase 的数据库管理中心,支持可视化地查看和管理所有数据表,并通过表的相互连线理解不同表间的关联关系(即外键约束,表示数据间的引用关系)。 + +![](images/image9.png) + +如果你想要手动新建 table,可以在 tables 中直接新建表格,我们会在之后的教程中详细讲解。 + +![](images/image10.png) + +### Authentication + +Authentication 负责管理用户的注册、登录和权限。默认的用户管理系统数据都在此处存储,它提供了开箱即用的用户注册、登录、密码重置、邮箱验证等功能,并支持第三方 OAuth 登录(如微信、GitHub、Google 等)。所有用户数据会自动同步到数据库的 `auth.users` 表中。 + +![](images/image11.png) + +你可以在 Provider 选项中找到不同 supabase 支持的用户信息登录入口,默认使用 Email;如果你想使用 Github 或者 Google 账户进行登录,还需要更多属性配置,我们会在下面的课程中进行详细讲解。 + +![](images/image12.png) + +在 Sign In / Providers 里还包含了对注册邮箱行为的控制,如果你不想每次邮箱注册都必须让用户接受邀请后才能成为用户,你可以取消 Confirm email 的强制要求。 + +![](images/image13.png) + +如果你想切换非 Supabase 的其他 auth 系统服务商,你可以点击 Third Party Auth,比如此处就使用 Clerk 作为第三方的系统服务商。 + +![](images/image14.png) + +如果你担心注册用户在短期内访问量过大,你可以在 Rate Limits 中启用对应的流量限制策略: + +![](images/image15.png) + +### Storage + +Storage 是 Supabase 的存储系统,兼容 amazon cloud 的 s3 概念,可用于存储任意类型的文件(如图片、视频、文档、音频等),并提供访问权限管理(公开或私有)和下载链接获取(永久链接或临时链接),你能够很方便在应用中对用户涉及到的文件内容进行上传与下载管理,并与 Supabase 的认证系统无缝集成,实现精细化的访问控制。 + +![](images/image16.png) + +我们将会在本节课的进阶 project 中讲解 storage 的具体用法。 + +![](images/image17.png) + +如果你想使用 S3 的相关协议进行操作,可以直接使用对应的配置: + +![](images/image18.png) + +> Amazon Cloud(亚马逊云服务,简称 AWS)是亚马逊提供的云计算平台(就像一个大型的网络机房,你可以按需租用计算和存储资源)。S3(Simple Storage Service)是 AWS 里专门用来存储文件的服务(类似一个无限大的网盘,可以存图片、视频、备份等各种文件),它是目前最流行的对象存储服务,已经成为了事实上的行业标准。 +> +> **为什么要做成 S3 兼容 ****API** ** ?** :S3 已经存在近 20 年,市面上有大量现成的工具、SDK 和文档,兼容 S3 意味着你可以直接用这些资源,不用从头开始制作各类相关工具,能够快速满足业务上线的需求。 + +### Edge Functions + +如果你不想部署后端,但是想使用数据库和函数操作,你可以使用 Edge Functions 构建无需自建服务器的后端核心能力,它是 Supabase 提供的全球分布式服务端函数。简单来说,它让你无需购买和管理自己的后端服务器,就能直接编写并部署在云端的后端代码。这些函数部署在全球网络的边缘节点上,会自动在离你的用户最近的位置运行,从而大幅降低网络延迟,提供极致的响应速度。你可以在 Supabase 的仪表盘中直接创建、编辑和部署,整个开发流程非常便捷。 + +![](images/image19.png) + +Edge Functions 的一个核心用途是充当安全的中间层,保护你的敏感信息和鉴权密钥。在前端代码中直接调用第三方服务(如 OpenAI、Stripe)会暴露你的 API Key,带来极大的安全风险。通过 Edge Functions,你的前端应用只与你的 supabase 函数通信,所有秘密只在 supabase 中保管。 + +![](images/image20.png) + +Edge Functions 的函数使用 secrets 中暴露的密钥作为环境变量,通过 `Deno.env.get` 加载,从而实现第三方服务的调用。这样一来,敏感密钥就永远不会暴露在客户端(你的浏览器),彻底杜绝了被盗用的风险。 + +![](images/image21.png) + +请求 Supabase Edge Function 时,需在请求头携带对应的 Supabase 密钥,下面是一个极简示例: + +```JavaScript +// 核心配置(替换为你的实际信息) +const projectId = "你的 Supabase 项目ID"; +const functionName = "目标 Edge Function 名称"; +const supabaseKey = "Supabase anon_key"; + +// 调用函数 +async function callEdgeFunction() { + const url = `https://${projectId}.supabase.co/functions/v1/${functionName}`; + + try { + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${supabaseKey}` // 关键:携带密钥完成认证 + }, + body: JSON.stringify({ order_id: "123", action: "refund" }) // 自定义请求数据 + }); + + const result = await response.json(); + console.log("调用成功:", result); + } catch (error) { + console.error("调用失败:", error.message); + } +} + +// 执行调用 +callEdgeFunction(); +``` + +此外,Edge Functions 与 Supabase 的用户认证系统无缝集成。当已登录的用户调用一个函数时,其身份信息会传递给函数。这使得你可以在函数内部轻松识别当前用户,并根据其身份执行权限控制。更重要的是,函数在操作数据库时会自动遵循你设置好的行级安全策略(Row Level Security),确保用户只能访问和修改他们有权操作的数据,让构建安全的多用户应用变得简单。 + +Edge Functions 的应用场景非常广泛,能够处理各种后端任务。它们非常适合用来监听来自第三方服务的 Webhook 事件(例如支付成功、代码提交等),并自动执行相应的数据处理逻辑。你也可以用它来发送邮件通知、生成 PDF 报告、创建自定义的 API 接口来封装复杂的业务逻辑,或者执行任何你希望在服务端完成的计算任务,极大地扩展了你应用的能力。 + +具体到一个常见的例子:身份认证工具 Clerk 。Clerk 仅用于处理用户登录、注册、信息更新等认证相关操作,并不直接管理你的业务数据库。如果你想要将这些认证动态同步到业务数据库中,则需要通过触发 Webhook 事件请求 Edge Functions 实现。Edge Functions 能够监听 Clerk 发出的 Webhook 信号,自动执行数据同步逻辑,让 Supabase 数据库中的用户信息与 Clerk 登录状态实时对齐,全程无需你部署独立后端。 + +### Realtime + +Realtime 是 Supabase 的实时数据同步引擎,它允许你的应用即时接收数据库的变化通知,而无需反复轮询 API。当数据库中的数据发生 `INSERT`、`UPDATE` 或 `DELETE` 操作时,Realtime 会通过 WebSocket 将这些变化实时推送给所有已连接的客户端。这对于构建需要实时交互的应用至关重要。 + +Realtime 主要包含三大核心功能,覆盖了绝大多数实时场景: + +1. **Postgres Changes:** 直接监听数据库表的变化。你可以精确地订阅特定表、特定事件(增、删、改),甚至可以根据筛选条件来接收通知,并与行级安全策略(Row Level Security)完美集成,确保用户只能收到他们有权限查看的数据变更。 +2. **Broadcast:** 允许客户端之间通过频道(Channel)发送低延迟的临时消息。这非常适合实现聊天室、实时光标追踪、在线游戏状态同步等功能。 +3. **Presence:** 用于追踪和同步在线用户状态。你可以用它来轻松实现“谁在线上”、“当前有X人正在查看”等功能,非常适合协作类应用。 + +我们会在后续的项目制学习中详细介绍该部分的内容。 + +### Project Settings + +Project Settings 是 Supabase 项目的高级配置部分,你可在此实现计算资源的深度调度,以及各类功能底层参数的精细化配置。 + +![](images/image22.png) + +在入门阶段,我们只需聚焦以下两个核心板块,一个是 Data API,我们在此可获取关键的 “Supabase URL”, 它是形如 `https://xxx.supabase.co` 的 RESTful 端点,是所有数据查询、新增、修改、删除操作的 “入口地址”。前端或服务端需通过该 URL 初始化 Supabase 客户端,建立与数据库的连接。 + +![](images/image23.png) + +另一个重点是 API Keys,选择 “Legacy anon, service_role API keys” 标签页,其中的 anon public 密钥 是前端场景的重要身份凭证,它的权限被 RLS 严格限制,仅能访问用户被授权的数据。而 service_role 密钥属于 “服务端高权限密钥”,具备绕过行级安全的能力,可执行批量数据操作、系统级配置等敏感操作。绝对禁止公开分享,若泄露需立即生成新密钥并更新服务端配置。 + +![](images/image24.png) + +其余配置项在当前阶段无需深究,待后续有进阶使用需求时再逐一探索即可。 + +## 2.1 Create Your First SQL Table + +以上是 Supabase 的界面介绍,接下来我们将深入 Supabase 的核心数据库的操作环节。 + +在 Supabase 中创建数据表,主要有以下两种常用方式,你可以根据需求选择: + +1. (推荐)借助大语言模型生成适配 Supabase 的 SQL 语句,直接在 **SQL Editor(** 前文介绍的 SQL 语句执行器)中粘贴执行,高效快捷,我们会在下个部分环节重点说明这个操作过程。 +2. 通过可视化操作创建:在左侧侧边栏找到 Database 模块,点击进入后选中侧边栏的 Tables,在右侧点击 New table 按钮,即可通过图形化界面创建数据表。 + +![](images/image25.png) + +值得注意的是,对应数据表的名称以及存储的数据类型可在下方的 Columns 中指定。 + +![](images/image26.png) + +对于关系数据库,其中很重要的特点是表与表之间的关联,你可以在下方找到 `Foreign keys` ,点击创建相应的关联关系: + +![](images/image27.png) + +其中 `Foreign keys` 表达了表与表之间的关联关系:一个或一组字段,它在当前表(子表)中的值,会引用另一张表(父表)中主键的值。 + +例如,在创建 `学生表`的时候,我们可以这样定义外键:(`所属班级编号` 这一列是一个外键。这个外键引用了 `班级表` 里的 `班级编号` 这一列。) + +```SQL +CREATE TABLE 学生表 ( + 学生学号 INT PRIMARY KEY, + 学生姓名 VARCHAR(50), + 所属班级编号 INT, + FOREIGN KEY (所属班级编号) REFERENCES 班级表(班级编号) +); +``` + +更具体举例而言,我们可以可视化观察对应的表的结构: + +班级表: +这张表里记录了所有班级的信息,每个班级都有一个独一无二的班级编号。班级编号就是这张表的主键 (Primary Key),是每个班级的唯一身份证。 + +| 班级编号 | 班级名称 | +| -------- | ---------- | +| 101 | 一年级一班 | +| 102 | 一年级二班 | + +学生表: +这张表记录了所有学生的信息。每个学生都属于一个特定的班级,对吗?那么我们怎么知道哪个学生在哪个班级呢? + +我们可以在学生表里增加一列,叫做 `所属班级编号`。 + +| 学生学号 | 学生姓名 | 所属班级编号 | +| -------- | -------- | ------------ | +| 2024001 | 张三 | 101 | +| 2024002 | 李四 | 102 | +| 2024003 | 王五 | 101 | + +在该例子中,学生表中的 `所属班级编号` 列就是外键 (Foreign Key)。 + +在 Supabse 中,点击添加 Foreign Key 后,你可直接选择进行关联表对应列的选取 + +![](images/image28.png) + +## 2.3 SQL Editor 简介与数据库基本操作 + +接下来我们将分步执行一系列 SQL 脚本,熟悉常见的 SQL 中的增删查改操作。你可以将每个步骤的代码复制到 SQL Editor 中,执行并观察结果。 + +你可以在该目录下获得所有的测试 SQL 文件: + +https://github.com/THU-SIGS-AIID/Project5-Supabase-Demos/tree/main/apps/sql-examples + +### **2.3.1 **`CREATE`** - 创建表结构** + +`CREATE TABLE` 语句用于为新表定义模式(Schema),包括其列(Columns)、对应的数据类型(Data Types)以及任何约束(Constraints),简单理解是创建了一个数据表。 + +```SQL +-- Step 1: Create the 'orders' table +-- This file is fully independent and creates a sample table for later steps. +CREATE TABLE IF NOT EXISTS orders ( + id serial PRIMARY KEY, + user_id int NOT NULL, -- User ID + status text NOT NULL, -- Order status (e.g. paid, pending) + amount numeric(10, 2) NOT NULL, -- Order total amount + details jsonb, -- Item and extra details as JSON + placed_at timestamptz DEFAULT now(), -- Order creation time + is_paid boolean DEFAULT false -- Paid flag +); + +-- Expected Output: +-- Orders table created if it did not exist. +-- No data inserted. (Querying returns zero rows for now.) +-- If table already exists, no error occurs. +``` + +成功执行后,系统将提示脚本已完成。你可以在 Table Editor 中看到对应的表被创建完成: + +![](images/image29.png) + +### **2.3.2 **`INSERT`** - 填充初始数据** + +表结构创建完毕后,下一步是使用 `INSERT INTO` 语句向表中添加数据行。 + +```SQL +-- Step 2: Insert initial rows into the orders table +-- Provides realistic, varied data for demo/testing. All values are self-contained. +INSERT INTO orders (user_id, status, amount, details, placed_at, is_paid) VALUES + (2001, 'pending', 23.50, '{"items":[{"sku":"BGR001","name":"Beef Burger","qty":1,"price":12.00}]}', now() - interval '2 days', false), + (2002, 'paid', 50.00, '{"items":[{"sku":"BGR002","name":"Chicken Burger","qty":2,"price":10.00},{"sku":"DRK001","name":"Lemonade","qty":2,"price":5.00}]}', now() - interval '1 day', true), + (2003, 'cancelled', 15.00, '{"items":[{"sku":"FRY001","name":"French Fries","qty":3,"price":5.00}], "reason":"Not available"}', now() - interval '45 days', false), + (2004, 'paid', 22.98, '{"items":[{"sku":"BGR003","name":"Veggie Burger","qty":2,"price":9.99}], "promo":"SUMMER22"}', now() - interval '10 days', true), + (2005, 'pending', 18.75, '{"items":[{"sku":"SAL001","name":"Salad","qty":1,"price":6.75},{"sku":"BGR001","name":"Beef Burger","qty":1,"price":12.00}]}', now() - interval '7 hours', false), + (2006, 'paid', 8.00, '{"items":[{"sku":"DRK002","name":"Cola","qty":2,"price":4.00}]}', now() - interval '3 hours', true), + (2007, 'refunded', 14.50, '{"items":[{"sku":"BGR003","name":"Veggie Burger","qty":1,"price":9.99},{"sku":"FRY001","name":"French Fries","qty":1,"price":4.51}], "refund_reason":"Late delivery"}', now() - interval '15 days', false), + (2008, 'paid', 26.99, '{"items":[{"sku":"BGR002","name":"Chicken Burger","qty":2,"price":10.00},{"sku":"DRK001","name":"Lemonade","qty":1,"price":6.99}]}', now() - interval '12 days', true), + (2009, 'pending', 9.99, '{"items":[{"sku":"BGR003","name":"Veggie Burger","qty":1,"price":9.99}]}', now() - interval '30 minutes', false), + (2010, 'paid', 19.89, '{"items":[{"sku":"BGR001","name":"Beef Burger","qty":1,"price":12.00},{"sku":"DRK002","name":"Cola","qty":2,"price":3.95}]}', now() - interval '5 days', true), + (2011, 'cancelled', 0.00, '{"items":[], "reason":"User cancelled"}', now() - interval '2 days', false); + +-- Expected Output: +-- After running this script, SELECT * FROM orders will show about 11 rows with varied user_id, status, amount, details (JSON), placed_at, and is_paid fields. +-- For example: +-- | id | user_id | status | amount | is_paid | placed_at | +-- |----|---------|-----------|--------|---------|---------------------| +-- | 1 | 2001 | pending | 23.50 | false | 2025-10-28 13:40:00Z| +-- | 2 | 2002 | paid | 50.00 | true | ... | +-- |... | ... | ... | ... | ... | ... | +``` + +执行成功后,此时表中已经插入了原始数据,你可以进入到 Table Editor 界面刷新后看到结果,也可以直接在 SQL Editor 界面中新建窗口,执行查询语句 `SELECT * FROM orders;`查看结果: + +![](images/image30.png) + +### **2.3.3 **`SELECT`** - 读取与查询数据** + +`SELECT` 语句用于从表中检索数据。通过使用不同的子句,可以实现对数据的精确筛选、排序和格式化,我们可参考以下语句一步步执行查看结果: + +```SQL +-- Step 3: SELECT query examples for the orders table + +-- Example 1: Select all fields for all orders +SELECT * FROM orders; +-- Expected Output: Returns all rows and fields. Columns: id, user_id, status, amount, details, placed_at, is_paid. + +-- Example 2: Select only pending orders +SELECT id, user_id, amount FROM orders WHERE status = 'pending'; +-- Expected Output: All rows with status 'pending'; columns: id, user_id, amount. + +-- Example 3: Select specific fields and filter by payment status +SELECT id, status, is_paid, amount FROM orders WHERE is_paid = true; +-- Expected Output: All rows where is_paid is true; columns: id, status, is_paid, amount. + +-- Example 4: Extract all item names from the details (JSON) for each order +SELECT id, details -> 'items' AS item_list FROM orders; +-- Expected Output: Each row shows id and an array from JSON with item details. +``` + +* **示例 1:** 返回 `orders` 表中的所有行和列,与第二步的输出类似。 +* **示例 2:** 仅返回状态为 'pending' 的订单,且只包含指定的列: + +![](images/image31.png) + +* **示例 3:** 仅返回已支付的订单,并显示指定的列: + +| id | status | is_paid | amount | +| -- | ------ | ------- | ------ | +| 2 | paid | true | 50.00 | +| 4 | paid | true | 22.98 | +| 6 | paid | true | 8.00 | +| 8 | paid | true | 26.99 | +| 10 | paid | true | 19.89 | + +* **示例 4:** 返回每个订单的 `id` 和从 `details` 字段中提取的 `items` 数组: + +| id | item_list | +| --- | ---------------------------------------------------------------------------------------------------------------------- | +| 1 | `[{"qty":1,"sku":"BGR001","name":"Beef Burger","price":12}]` | +| 2 | `[{"qty":2,"sku":"BGR002","name":"Chicken Burger","price":10},{"qty":2,"sku":"DRK001","name":"Lemonade","price":5}]` | +| 3 | `[{"qty":3,"sku":"FRY001","name":"French Fries","price":5}]` | +| ... | ... | + +### **2.3.4 **`INSERT`** - 插入单条记录** + +在 2.3.2 中,我们演示的是开头时刻初始化批量插入数据,现在我们查看如何新增插入单条数据。 + +```SQL +-- Step 4: INSERT a new order (single row) +-- Example: Add a new paid order for user 2012 with one Chicken Burger +INSERT INTO orders (user_id, status, amount, details, is_paid) +VALUES ( + 2012, 'paid', 9.99, + '{"items":[{"sku":"BGR002","name":"AIID Burger","qty":100,"price":1000}]}', + true +); +-- Expected Output: +-- Before (table fragment): +-- | id | user_id | status | amount | is_paid | +-- | ...| ... | ... | ... | ... | +-- +-- After (last row): +-- | id | user_id | status | amount | is_paid | +-- | xx | 2012 | paid | 9.99 | true | +-- (where xx = next serial value) +``` + +此时再用 `SELECT * FROM orders;` 对数据进行查询,我们可以看到 orders 表成功从 11 个数据变成了 12 个数据。 + +### **2.3.5 **`UPDATE`** - 修改现有数据** + +在实际工作中,我们需要对数据表进行频繁数据更新,我们能够用 `UPDATE` 语句修改表中已存在的记录。 + +```SQL +-- Step 5: UPDATE example +-- Example: Mark order with id=1 as paid and update its status +UPDATE orders SET status = 'paid', is_paid = true WHERE id = 1; +-- Expected Output: +-- Before (row with id=1): +-- | id | status | is_paid | +-- | 1 | pending | false | +-- After (row with id=1): +-- | id | status | is_paid | +-- | 1 | paid | true | +-- All other rows remain unchanged. +``` + +### **2.3.6 **`DELETE`** - 删除数据** + +`DELETE` 语句可用于从表中移除记录,并结合条件对指定部分的数据进行修改。 + +```SQL +-- Step 6: DELETE example +-- Example: Delete orders older than 2 days to clean up old data +DELETE FROM orders WHERE placed_at < now() - interval '2 days'; +-- Expected Output: +-- Before (filtered for affected rows): +-- | id | status | placed_at | +-- | 3 | shipped | 2025-10-13 ... | <-- will be deleted +-- +-- After: +-- No such rows remain. SELECT * FROM orders WHERE placed_at < now()-interval '2 days' yields zero rows. +-- Other rows in orders table are unaffected. +``` + +执行前,你可先执行 `SELECT id, status, placed_at FROM orders WHERE placed_at < now() - interval '2 days';` 进行数据表筛选结果的查看。当运行 `DELETE` 命令后,再次执行相同的 `SELECT` 查询 `SELECT id, status, placed_at FROM orders WHERE placed_at < now() - interval '2 days';`,将返回一个空的结果,表明这些行已被成功删除。 + +## 2.4 RLS (Row level security) + +在学习了数据库的基本操作后,我们需要进一步深入一个保障数据安全的核心概念 ——RLS(行级安全,Row Level Security)。 + +不妨先思考一个实际场景中的关键问题:如何实现数据的 “隔离访问”?比如,只允许用户 A 查看自己的数据,而无法看到用户 B 的信息;再比如,即便某角色拥有数据库的访问权限,如何避免其误操作或泄露其他用户的敏感数据? + +RLS 正是为解决这类数据安全与隔离需求而生。它允许开发者为数据库表定义精细化的安全策略,根据用户的身份信息(如用户 ID、角色权限等),精确控制哪些用户能访问、修改表中的哪些行数据。 +举个典型示例:对于订单表(`orders`),我们可以定义这样一条 RLS 策略 ——“仅当 `orders` 表中某条记录的 `user_id` 列,与当前登录用户的 ID 完全一致时,该用户才能查询到这条订单数据”,从而实现 “用户只能看自己的订单” 的核心需求。 + +当你为某张表启用 RLS后,该表的所有数据操作请求(包括 `SELECT` 查询、`INSERT` 新增、`UPDATE` 修改、`DELETE` 删除)都会触发 RLS 校验:必须通过至少一条安全策略的检查,操作才能执行。若不存在允许该操作的策略,或请求未满足任何策略的条件,数据库会直接拒绝此次操作,从底层阻断非授权访问。 + +在 Supabase 中,RLS 与用户认证系统深度绑定,使用起来更为便捷。Supabase 提供了一个专用函数 `auth.uid()`,它能直接返回 “当前发起请求的已登录用户” 的唯一 ID(格式为 UUID)。借助这个函数,我们可以轻松编写策略,实现 “数据行与用户身份” 的精准关联(比如前文提到的 “订单 `user_id` 匹配当前用户 ID”)。 + +启用 RLS 策略的方式很灵活,你可以在 Supabase 数据库管理界面中的 “RLS” 按钮,直接配置并启用策略: + +![](images/image32.png) + +![](images/image33.png) + +![](images/image34.png) + +主动配置难免显得麻烦,通常,我们在数据表语句创建、初始化的时候就会自动考虑植入对应的 RLS 策略。我们只需在 SQL Editor 中执行类似如下语句,即可自动开启对应数据表的行级安全策略。 + +![](images/image35.png) + +# 3. The First SQL Application + +掌握了数据库基础操作与RLS核心逻辑,我们终于进入本次教程的实践环节。漫长的学习铺垫是为了让后续“从0到1搭建应用”的过程更清晰。接下来,我们将以“汉堡店订单管理”为场景,手把手演示Supabase的常见操作:从应用与Supabase的关联配置,到数据库与登录功能的集成,逐步学习不同操作逻辑。 + +## 3.1 Clone and Run Supabase Demos + +要开展实操,首先需要获取配套的演示代码仓库。你可以让 Trae 或 ClaudeCode 协助 git clone 以下仓库:https://github.com/THU-SIGS-AIID/Project5-Supabase-Demo + +若已配置 SSH 密钥,建议使用 SSH 地址进行 clone(git@github.com:THU-SIGS-AIID/Project5-Supabase-Demo.git)以提升安全性;若 SSH 或 HTTPS 连接遇到网络问题,可以直接点击仓库页面的 “Download ZIP”,获取压缩包后解压即可看到完整代码。 + +![](images/image36.png) + +Clone 后,你同样可以让 Trae 或者是 ClaudeCode 帮你启动项目,例如直接在 Agent 界面中说明: `帮我直接启动这个项目里面的 project 1 `,或者复制对应想启动 project 的绝对路径,粘贴给大模型让大模型直接启动。 + +## 3.2 Project1 - burger-shop-menu-crud + +接下来进入实操环节 —— 以 `project-burger-shop-menu-crud-1` 为例,我们将学习如何通过 SQL 脚本一键初始化 Supabase 数据库,并完成本地项目与 Supabase 数据库的关联配置,让前端能正常读写菜单数据。 + +### Create a Database Using Scripts + +首先,我们需要在 Supabase 中创建需要的数据表的相关内容。进入 Project1 项目目录看到名为 `scripts`的文件夹,其中包含 1 个 `init.sql`数据库脚本文件,它能帮我们自动完成所有数据库相关资源的创建(包括表结构、初始数据等),之后我们会经常用到该文件进行数据库中表的初始化。 + +```SQL +...... + +-- ============================================================================ +-- 2. Create Menu Items Table +-- ============================================================================ + +create table if not exists public.menu_items ( + id uuid primary key default gen_random_uuid(), + name text not null, + description text, + category text check (category in ('burger','side','drink')) default 'burger', + price_cents int not null check (price_cents > 0), + available boolean default true, + emoji text, + created_at timestamptz not null default now(), + updated_at timestamptz not null default now() +); + +-- Comments for documentation +comment on table public.menu_items is 'Burger shop menu items for CRUD demo'; +comment on column public.menu_items.id is 'Unique identifier for each menu item'; +comment on column public.menu_items.name is 'Display name of the menu item'; +comment on column public.menu_items.description is 'Detailed description of the menu item'; +comment on column public.menu_items.category is 'Category: burger, side, or drink'; +comment on column public.menu_items.price_cents is 'Price in cents (integer) to avoid floating point issues'; +comment on column public.menu_items.available is 'Whether the item is currently available for order'; +comment on column public.menu_items.emoji is 'Optional emoji representation of the menu item'; +comment on column public.menu_items.created_at is 'Timestamp when the item was created'; +comment on column public.menu_items.updated_at is 'Timestamp when the item was last updated'; + +...... +``` + +在 SQL Editor 中执行初始化 sql 脚本后,即可在 Table Editor 中看见已创建的数据表。其中数据库初始化代码具体执行逻辑如下: + +1. 创建 menu_items 表 : +2. 这个表用于存储汉堡店菜单中的所有项目。它包含了如 name (商品名), description (描述), price_cents (以美分为单位的价格,避免浮点数精度问题), category (分类) 和 available (是否可售) 等字段。这基本涵盖了一个菜单项所需的所有信息。 +3. 创建 promo_codes 表 : +4. 此表用于管理促销活动,例如折扣码。它定义了 code (折扣码), discount_type (折扣类型,如百分比或固定金额), discount_value (折扣数值) 等字段。 +5. 禁用行级安全 (Row Level Security - RLS) : +6. 为了方便开发和测试,脚本中明确地禁用了 RLS。但结合我们之前学习的 RLS 核心逻辑:RLS 是 Supabase 保障数据安全的关键功能,能通过精细化策略控制 “谁能访问 / 修改哪些数据”(比如只允许管理员编辑促销码,普通用户只能查看菜单)。因此在生产环境中,必须开启 RLS 并配置合理策略,从底层阻断非授权访问(如防止用户恶意修改他人创建的菜单,或泄露促销码规则)。 +7. 插入种子数据 (Seed Data) : +8. 为了让前端项目启动后就能看到真实的菜单与促销数据(无需手动录入测试数据),`init.sql`脚本还会向 `menu_items`和 `promo_codes`表中插入 “种子数据”(即示例数据)。例如,你可以看到各种汉堡、小食、饮料以及多种多样的折扣码。 + +### Set up the connection with database + +数据库准备完成,我们需要将这个前端项目与 Supabase 进行连接,从而正常读取数据库内的数据。我们需要将 Supabase 项目的 URL 和 anon key 写到指定配置中,本项目提供了两种灵活的配置方式: + +1. 通过环境变量配置 + +在项目根目录创建一个 .env 文件,并填入你的 Supabase 凭证: + +```Plain +NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key +``` + +2. 在项目页面中直接设置 + +为了方便快速演示和切换不同的 Supabase 项目,首页页面右上角提供了一个 设置 按钮。你可以点击它,在弹出的模态框中直接输入或粘贴 Supabase URL 和 anon key。 + +点击 “Save” 后,这些信息会用于动态创建 Supabase 客户端实例,类似下列代码所示: + +```JavaScript +import { createClient, type SupabaseClient } from '@supabase/supabase-js'; + +// Optional client factory for demos: returns null when env is not set. +export function maybeCreateBrowserClient(): SupabaseClient | null { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const anon = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; + if (!url || !anon) return null; + return createClient(url, anon); +} +``` + +创建完数据库,填写完对应的 Supabase Link 相关配置后,即可看到如下界面,你可以尝试对商品进行增删查改,并观察 Supabase 中对应部分数据表的变化。 + +![](images/image37.png) + +![](images/image38.png) + +### 📚 Assignment + +1. 尝试增加和删除已有项目,在 Table Editor 中查看修改操作对数据表内容变动的影响。 + +## 3.4 Project2 - burger-shop-auth-users + +Project1 实现了 “菜单 CRUD + 数据库连接” ,Project2 将引入更贴近真实业务的核心能力,用户认证(Auth)与行级安全(RLS)权限管理。 + +Project2 包含独立的登录页,支持用户通过「邮箱 + 密码」的方式登录。其核心逻辑是调用 Supabase Auth 提供的原生方法,快速实现认证流程,无需手动开发复杂的登录校验逻辑: + +```Plain +const { error: err } = await supabaseClient.auth.signUp({ + email, + password, + options: { + data: { + full_name: fullName || null, + birthday: birthday || null, + avatar_url: avatarUrl || null + } + } +}); +``` + +![](images/image39.png) + +登录成功后,Supabase 会自动为用户创建一个会话(session),并在后续所有数据库请求中自动携带认证信息;通过 RLS 的作用,每个用户根据对应的认证信息只能看到自己的账户信息(已购买项目、钱包剩余额度),无法看到其他用户的账户信息,这就实现了不同用户登录后的数据隔离,每个人只能看到自己的内容。 + +和 Project 1 一样,你需要先使用 `init.sql` 进行数据表的初始化(注:如果发现初始化出错,请先在 Table Editor 中删除已经创建的数据表,或者是直接删除这个 Supabase Project, 重新新建一个 Project) + +成功使用邮箱注册账户、在邮箱确认注册账户后,登录后进入 Shop 界面即可看到如下内容: + +![](images/image40.png) + +但此时点击 admin,你并不能看到如下界面,你需要尝试在数据表中找到控制用户权限的部分,将权限修改为 `admin`,从而能够在 Admin 界面正常看到如下内容: + +![](images/image41.png) + +值得提示的是,目前每次注册新的邮箱,你都需要在邮箱中进行注册确认才可登录;但这一步并非是必须的,你可以在 Supabase 的 Authentication 栏目中找到 Sign In / Providers,点击Confirm email 取消邮箱的强制确认。 + +![](images/image42.png) + +### 📚 Assignment + +1. 请先领取新手礼包,完成商品购买操作。 +2. 尝试找到用户权限的设定数据表位置,将权限修改为 `admin`,并成功在订单管理界面修改商品数量 +3. 尝试在数据表内定位到钱包金额相关表,通过修改使剩余钱包金额增加。 +# 4. Build Your First Supabase App + +经过前面的系统学习,你已掌握 Supabase 的核心能力(数据库操作、用户认证、RLS 安全策略),现在是时候亲自动手,搭建属于你的第一个包含数据库、支持用户登录系统的应用了! + +## 4.1 为任意应用接入 Supabase 数据库的标准化流程 + +我们可以使用标准化流程将任意应用接入 Supabase 数据库: + +1. 首先进行需求梳理与信息同步,明确目标并告知AI + 1. 你需要向AI清晰描述当前应用的核心功能、待新增的数据库需求。示例:“我现有一个本地React Todo应用,数据仅存在浏览器本地存储,需新增‘数据云端同步’功能并接入Supabase数据库。请帮我梳理:这个应用涉及哪些数据操作(如新增待办、修改状态、删除待办)?需要创建哪些数据表来存储这些数据?” + 2. 补充关键约束条件(可选):比如字段格式要求(时间戳用 `timestamptz`、金额用整数存分)、数据权限规则(仅自己可见待办),让AI的分析更贴合实际需求。 + 3. 对 AI 返回的结果进行审核,若AI思路存在遗漏(如未考虑“待办截止时间”字段),补充提示修正:“你漏考虑截止时间了,帮我加上。” +2. 让AI基于你确认后的表结构,生成适配Supabase的 `init.sql`脚本:“基于上述所说思路和表的结构,返回给我在 Supabase 中可以进行初始化的 init.sql 脚本”,之后你需要在 SQL Editor 中执行脚本;若执行报错,将错误信息反馈给AI,让其修正脚本。 +3. 在 Supabase 运行 init.sql 脚本后,让 AI 基于脚本重构当前代码,使得能够和 Supabase 进行正常的数据交互:“请你根据我的 sql 脚本以及上面讨论的设定,重构项目的代码让它支持能够和 Supabase 对应的数据库进行通信并处理数据”。 +4. 重构完毕,此时只需要配置好 Supabase 地址和 key 的参数(正式项目通常只用环境变量配置),随后进行检查,若没问题则顺利实现将应用接入 Supabase 数据库。 + 1. 运行项目,测试所有数据库交互功能,到Supabase Table Editor 实时查看数据是否同步; + 2. 若出现问题(如数据无法插入、仅能看到部分数据),将问题现象反馈给AI,让其定位原因并修正代码。 + +此外,若目标是开发用户登录页面,可直接让 AI 协助集成登录页面 :“现在你需要帮我给这个应用加入 Supabase 的用户登录系统,使用邮箱可以注册和登录”。另外,你还需要向 AI 明确页面的跳转逻辑与路径(如登录成功后跳转至系统首页、跳转首页的地址是什么、登录失败时留在当前页并显示错误提示)。集成完成后,你需要尝试注册登录后能在 Supabase 的 Authentication 项目中看到新增的用户数据,并在登录后能正常进入到原先未登录无法进入的应用界面即可。 + +当然,你还可以直接让 AI 参考某个 project 的实现直接迁移对应的 Supabase 功能,比如某个 Project 用到了数据库以及 Edge fuction 的高级功能,你可以按照如下方式直接让 AI 迁移对应的相似功能:“请你参考该项目 {此处复制粘贴参考项目的绝对地址} 当中的 Supabase 相关功能实现逻辑,给当前项目加上类似的实现逻辑(如用户登录、数据库管理、函数请求等等)”。 + +## 4.2 Case Study : Build an Online Snake Game + +根据上面所提到的 SOP ,让我们通过一个具体的实际案例 `Project5-Supabase-Demos/apps_snakegame`来实践:为一个已有的“贪吃蛇”游戏项目增加分数排行榜单,包含用户登录与数据库基础功能。 + +![](images/image43.png) + +### 4.2.1 分析项目,识别数据需求 + +首先,和在之前提到的标准化流程类似,我们可以先把需求澄清给 AI ,让 AI 基于我们项目和需求给出对应的修改方案,之后我们会基于这个修改方案。 + +**你可以使用如下的提示词来指导 AI:** + +> “我有一个贪吃蛇游戏,目录在 {此处粘贴贪吃蛇游戏的绝对路径}。现在我想结合 supabase 给它增加一个在线排行榜功能,并且支持用户登录系统,排行榜可以根据用户名和邮箱显示排名。 +> +> 请帮我分析一下,为了实现这个功能,我需要建立哪些数据表?每个表应该包含哪些字段?” + +此时你会得到类似如下返回: + +![](images/image44.png) + +### 4.2.2 生成 `init.sql` 脚本 + +确定需要的部分,我们可以让 AI 生成需要在 Supabase 执行的数据库初始化脚本:“请你基于上面的分析,帮我在项目中生成 scripts/init.sql 脚本用于在 Supabase 中初始化所需数据库”。 + +![](images/image45.png) + +### 4.2.3 改造项目代码 + +接下来我们只需要让 AI 基于前面的内容重构当前的贪吃蛇代码:“接下来请你基于前面思考的内容以及 sql 表,使用 Supabase 帮我实现排行榜功能,排行榜是单独的一页,需要可以根据邮箱和用户名区分不同用户的总分,你还需要支持基于邮箱的用户登录系统,注册登录才能玩这个游戏。” + +如果当前 AI 对话轮次太多,你想重开一个新的会话进行项目重构,你可以把上面提到的 `init.sql`作为上下文中的内容,让 AI 基于 sql 文件进行项目重构。 + +若是发现 AI 实现的用户登录系统不够正常,你可以直接将我们之前写好的 `Project5-Supabase-Demos/apps/project-burger-shop-auth-users-2` 的地址一同放入提示词,让 AI 基于项目直接实现用户登录系统。并检查是否已经正确设定了连接到 Supabase 的必要条件,防止因为 Supabase 配置错误而报错。 + +在代码修改过程中,若出现实际效果与预期不符的情况(如排行榜数据不显示、登录验证失效等),只需完整记录具体现象并反馈给 AI,即可逐步接近正确结果。改造成功的标准为:用户能顺利完成注册与登录操作,且登录后可正常查看对应的游戏排行榜单。 + +![](images/image46.png) + +![](images/image47.png) + +### 📚 课程作业 + +1. 将用户管理系统集成到贪吃蛇游戏演示版中 +2. 将用户管理系统集成到你的应用程序中(如果之前已开发过一个应用程序) +# 3. Become Supabase Master + +以上是 Supabase 的基本操作,接下来的旅程中我们将会接触 Supbase 的进阶原理和功能,你将理解为什么我们会选择 Supabase 作为教学案例,以及如何使用 Supbase 实现更高级的操作,协助你实现更复杂的交互功能,并且在学习这些功能后,即便面对 Supabase 之外的其他同类工具,你也能触类旁通,从更本质的层面理解后端服务的核心原理。当然,你并不需要在短时间内学会全部,也许只需要学会第三方登录支持已经足够,你可以先浏览下列内容,直到项目遇到对应的需求时再倒回来深入学习。 + +## 5.1 Why We choose Supabase + +在开始进阶之前,我们再次思考这个问题:众多后端技术方案中,为何我们最终选择 Supabase 作为技术底座? + +初创团队在技术选型时普遍面临一个矛盾:既想完全掌控后端系统,又必须快速上线产品——而自建后端通常意味着要投入数月时间搭建数据库与实时同步、用户认证、API服务、文件存储、定时任务、监控告警等核心组件,除非团队成员已在对应领域积累了丰富的实战经验。在资金不足、市场窗口短暂的双重压力下,一旦陷入基础设施泥潭,极易导致迭代滞后、错失早期增长空间。 + +Supabase 将这些后端能力打包为开箱即用的服务(PostgreSQL数据库、实时订阅、身份认证、对象存储、边缘函数、自动生成API等),让初创团队得以将稀缺资源聚焦于核心功能开发,避免因底层建设拖慢上线速度——这已成为当前创投环境下务实的生存策略。当然,我们也可以使用别的一栈式后端产品进行开发,例如 PocketBase(轻量极简)和 Appwrite(跨平台适配)等方案,但综合功能完整性、SQL 生态成熟度及 Github 社区关注度,Supabase 更适合支撑业务的长期稳定运行。 + +在同类产品中,Supabase 的开源策略更具优势。 以市场占有率较高的 Firebase 为例:其闭源特性易导致平台绑定,迁移成本极高。Supabase 采用完全开源模式,支持私有化部署,规避了供应商锁定风险,可根据需求切换至其其他竞品。 + +总而言之,技术选型需匹配业务规模与目标。 对于个人项目或极小范围测试,PocketBase 等超轻量方案已足够;若企业需对接复杂身份系统,或要满足上市公司合规审计要求,WorkOS 这类企业级全身份治理方案更为适用。但对于验证 MVP、承载早期用户的核心业务场景,Supabase 的完整功能完全够用,它不仅能独立支撑至少万级用户规模,更可灵活集成 Stripe(支付)、Resend(邮件)、Cloudflare(CDN)等第三方服务;即便未来业务扩展至企业级需求,Supabase 的开源架构也能与企业系统并行部署,不同功能选择最适配的平台进行使用。这种渐进式灵活性,使初创团队无需过早投入重型基础设施,又能保留 future-proof 的演进空间。 + +## 5.2 Google & Github Login Support + +在前面的教程中,我们讲解了如何直接使用邮箱进行注册和登录,但在实际操作中我们通常想要简化注册流程,例如使用第三方登录 Google 和 GitHub 进行系统的快速注册与登录,我们将会在这节教程中展开每个细节;同时,一个完整的认证系统也必须提供安全可靠的密码重置功能,我们也会将密码重置功能集成在本节教程的项目中。 + +本项目 `Project5-Supabase-Demos/apps/project-burger-shop-auth-advanced-supabase-6`)完整地演示了如何实现这些高级功能。 + +![](images/image48.png) + +### 5.2.1 OAuth 流程:第三方登录是如何工作的? + +第三方登录的核心是 OAuth 2.0 开放授权协议,它的本质是 “授权代理”:允许用户授权我们的应用(汉堡店项目)访问其在第三方平台(如 Google)的公开信息(如邮箱、头像),但无需将第三方平台的密码暴露给我们的应用,从根本上规避了密码泄露风险。 + +完整流程可拆解为 5 个关键步骤,以 Google 登录为例: + +1. 用户发起授权请求:用户点击页面上的 “Sign in with Google” 按钮,我们的应用会自动将用户重定向到 Google 官方的授权页面(确保授权过程的安全性,避免钓鱼风险)。 +2. 用户完成第三方授权:用户在 Google 页面登录自己的账户(验证用户身份),并同意我们的应用请求的权限(如 “获取邮箱地址”)。 +3. Google 返回一次性授权码:授权通过后,Google 会将用户重定向回我们提前约定的 “回调 URL(Callback URL)”,并在 URL 参数中附带一个一次性、短期有效的授权码(而非直接返回用户信息,进一步提升安全性)。 +4. Supabase 交换访问令牌(Access Token):我们的后端(由 Supabase 托管,无需自建)会拿着这个授权码,向 Google 官方接口发起请求,换取可用于获取用户信息的 Access Token(授权码仅用于换 Token,避免 Token 直接在前端传输)。 +5. 创建账户并建立会话:Supabase 使用 Access Token 从 Google 拉取用户的公开信息(如邮箱、头像),并在我们的项目中为该用户自动创建账户(若首次登录)或直接关联现有账户,最终生成一个有效的用户会话(Session),完成登录。 + +![](images/image49.png) + +### 5.2.2 配置 Google Cloud 获取 Client ID 和 Secret + +无论是何种第三方登录方式,我们通常都需要获取 Client ID 与 Secret 进行配置;对于 Google 的第三方登录,你首先需要在 Google Cloud Platform 中创建一个 OAuth 2.0 客户端 ID 进行对应参数的获取。 + +1. **进入 Google Cloud Console** : +2. 访问 [Google Cloud Console](https://console.cloud.google.com/)。 +3. 创建一个新项目或选择一个现有项目。 +4. **配置 OAuth 同意屏幕 (OAuth consent screen)** : +5. 在左侧导航栏中,找到 “APIs & Services” -> “OAuth consent screen”。 +6. 选择 “External” 用户类型,然后点击 “Create”。 +7. 填写应用名称、用户支持电子邮件等必填信息。 +8. 在 “Authorized domains” 部分,添加你的 Supabase 项目域名,格式为 `*.supabase.co`。 +9. 保存并继续。在 “Scopes” 和 “Test users” 步骤中,你可以暂时跳过,直接保存。 +10. **创建凭据 (Create Credentials)** : +11. 进入 “APIs & Services” -> “Credentials”。 +12. 点击 “+ CREATE CREDENTIALS”,选择 “OAuth client ID”。 +13. 在 “Application type” 中选择 “Web application”。 +14. 为它取一个名字,例如 “Supabase Auth”。 +15. 在 “Authorized redirect URIs” 部分,点击 “ADD URI”,并填入你的 Supabase 项目的回调 URL。你可以在 Supabase Dashboard 的 “Authentication” -> “Providers” -> “Google” 中找到这个 URL,它的格式通常是 `https://<你的项目ID>.supabase.co/auth/v1/callback`。 + ![](images/image50.png) +16. 点击 “CREATE”。 +17. **获取 Client ID 和 Client Secret** : +18. 创建成功后,一个弹窗会显示你的 **Client ID** 和 **Client Secret** 。请务必**立即复制并妥善保存** 它们。 + +### 5.2.3 配置 GitHub 获取 Client ID 和 Secret + +同样地,你也需要在 GitHub 上注册一个 OAuth 应用。 + +1. **进入 ****GitHub** ** Developer Settings** : + + 1. 登录你的 GitHub 账户。 + 2. 点击右上角的头像,进入 “Settings”。 + 3. 在左侧导航栏的底部,找到 “Developer settings”。 +2. **注册新应用 (Register a new application)** : +3. 选择 “OAuth Apps”,然后点击 “New OAuth App”。 +4. 填写应用名称,例如 “My Burger Shop”。 +5. **Homepage URL** : 填写你应用的线上地址,或者本地开发地址 `http://localhost:3000`。 +6. **Authorization ****callback** ** URL** : 填入你的 Supabase 项目的回调 URL。同样,你可以在 Supabase Dashboard 的 “Authentication” -> “Providers” -> “GitHub” 中找到它,格式为 `https://<你的项目ID>.supabase.co/auth/v1/callback`。 +7. 点击 “Register application”。 +8. **获取 Client ID 和 Client Secret** : +9. 注册成功后,页面会显示你的 **Client ID** 。 + ![](images/image51.png) +10. 点击 “Generate a new client secret” 来生成你的 **Client Secret** 。同样,请**立即复制并保存** 它。 + +### 5.2.4 在 Supabase 中配置 Provider + +现在,将我们获取到的凭证配置到 Supabase 中。 + +1. **进入 Supabase Dashboard** : +2. 选择你的项目,进入 “Authentication” -> “Providers”。 +3. **启用并配置 Google** : +4. 找到 “Google” 并启用它。 +5. 将你从 Google Cloud 获取的 **Client ID** 和 **Client Secret** 粘贴到对应的输入框中。 +6. 点击 “Save”。 +7. **启用并配置 ** **GitHub** : + 1. 找到 “GitHub” 并启用它。 + 2. 将你从 GitHub 获取的 **Client ID** 和 **Client Secret** 粘贴到对应的输入框中。 + 3. 点击 “Save”。 + +![](images/image52.png) + +至此,你已经能够使用第三方账户在构建的网站中进行登录,你可以直接让 AI 基于 `Project5-Supabase-Demos/apps/project-burger-shop-auth-advanced-supabase-6`项目作为参考,在你的项目的基础上支持用户登录系统,以最小成本集成包含 github 与 google 鉴权的用户登录界面。 + +### 5.2.6 密码重置实现 + +作为一个成熟的用户登录组件,密码重置也是极其重要的一环,本项目 `project-burger-shop-auth-advanced-supabase-6`也包含了该功能的完整实现,你可以直接让 AI 基于本项目的密码重置功能复刻完整的密码重置组件。其主要分为以下几步: + +1. 发起请求 :用户在忘记密码页面输入邮箱,前端调用 `supabase.auth.resetPasswordForEmail()` 函数,并指定一个重定向 redirectTo URL(例如 /auth/reset )。 +2. 发送邮件 :Supabase 会向该邮箱发送一封包含唯一重置链接的邮件。 +3. 访问链接 :用户点击邮件中的链接,被重定向到应用内指定的重置页面。 +4. 更新密码 :在重置页面,用户输入新密码。前端调用 `supabase.auth.updateUser()` ,将新密码提交给 Supabase。Supabase 会自动验证链接的有效性并完成密码更新。 + +最后,如果你觉得当前的密码重置邮件过于简陋,你可以 在 Supabase Dashboard 的 Authentication -> Email Templates 中自定义“Reset Password”邮件模板。 + +除了 Reset password 功能外,你还能看到许多其他与用户管理相关的高级功能设定(例如 Invite user 等),你可根据对应功能各自的开发文档,结合 Vibe coding 工具自行添加对应功能。 + +![](images/image53.png) + +## 5.3 Realtime Function + +Supabase 的实时功能是其最强大的特性之一,为构建协作文档、实时仪表盘、游戏大厅或客服系统提供了极大的便利。 + +本项目 `Project5-Supabase-Demos/apps/project-burger-shop-realtime-orders-3 `通过构建一个 多人实时聊天室、光标位置共享 功能,展示了 Supabase Realtime 涉及到的三大核心能力:数据库变更监听 (Postgres Changes)、广播 (Broadcast) 和 在线状态 (Presence)。 + +![](images/image54.png) + +如果你觉得相关代码部分有一定难度,可以直接让 AI 参考该部分文档内容,对你的程序进行修改。 + +### 5.3.1 数据库实时变动 Postgres Changes + +最常见的 Realtime 功能是对数据库的变更进行实时监听 Postgres Changes 。它允许客户端订阅数据库中特定表、特定行甚至特定列的 INSERT 、 UPDATE 或 DELETE 事件。一旦数据库发生变动(无论是通过 API 调用、Supabase Dashboard 操作,还是 SQL 脚本执行),Supabase 都会利用 PostgreSQL 的底层复制机制,立即通过 WebSocket 将变更的数据推送到所有订阅了该频道的前端客户端,而无需前端通过轮询(Polling)去反复查询。 + +一般而言,该功能可以在 Table Editor 中找到 Enable Realtime 点击后启动, 但更方便的是通过 SQL 脚本初始化执行,例如: + +```SQL +-- Enable realtime replication +ALTER TABLE public.chat_messages REPLICA IDENTITY FULL; +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_publication_tables + WHERE pubname = 'supabase_realtime' + AND schemaname = 'public' + AND tablename = 'chat_messages' + ) THEN + ALTER PUBLICATION supabase_realtime ADD TABLE public.chat_messages; + END IF; +END $$; +``` + +该语句将 `chat_messages` 表添加到了 Supabase 预设的 `supabase_realtime` 中,而一旦一个表被加入到这个特殊的 `publication` 中,Supabase 的实时服务器就会开始监听它的所有数据变更。 + +基于上面的特殊数据表,我们能够使用监听代码对表内数据变动进行实时监听。我们需要实现的是当一个用户发送消息时,其他所有在线用户都能立刻在屏幕上看到这条消息。通过订阅 chat_messages 表的 INSERT 事件能够实现这一点。 + +```TypeScript + const sub = supabase + .channel('chat_messages_channel') + .on('postgres_changes', { + event: 'INSERT', + schema: 'public', + table: 'chat_messages' + }, (payload: any) => { + console.log('New message received:', payload.new); + const newMessage = payload.new as Message; + // ... // + .subscribe((status: string) => { + console.log('Chat subscription status:', status); + }); +``` + +* `.channel('chat_messages_channel')`: 创建一个隔离的通信频道。 +* `.on('postgres_changes', ...)`: 这是核心的订阅方法。我们告诉 Supabase 我们只关心 `chat_messages` 表的 `INSERT` 事件。 +* `payload.new`: 当有新消息被插入数据库时,Supabase 会将这条新数据的完整内容通过 `payload.new` 推送给所有订阅的客户端。 +* `.subscribe()`: 启动订阅。 + +### 5.3.2 信息广播同步 Broadcast & Presence + +对于那些不需要存入数据库的、更“即时”的交互,比如光标移动、在线状态等,Supabase 提供了 Broadcast 和 Presence 功能。 + +* Presence: 用于跟踪频道内所有客户端的 **共享状态** 。适合用来实现“谁在线”的功能。 +* Broadcast: 用于向频道内的所有其他客户端发送**低延迟**的 **临时消息** 。 + +Presence 的核心思想是: 让每个客户端声明自己的在线状态,并由 Supabase 的服务器负责将这些状态可靠地同步给频道内的所有其他客户端。实现 Presence 分为以下几个关键步骤: + +1. 创建一个支持 Presence 的频道 + +首先,我们创建了一个频道 `lobby_presence` 来专门处理这些交互,并在配置中指定一个唯一的 key 来标识当前用户。这个 key 通常是用户的 ID。 + +```Plain +const ch = supabase.channel +('lobby_presence', { +  config: { +    presence: { key: anonymousUser.id }, +  } +}); +``` + +2. 订阅频道宣告“我在线”的信息 + +一旦频道创建成功,我们需要订阅它。在订阅成功的回调( status === 'SUBSCRIBED' )中,我们调用 channel.track() 方法。这个方法会将当前用户的信息(例如用户ID、名称、头像颜色等)广播给频道内的所有其他客户端,宣告自己的“在线”状态。 + +```Plain +const me = { +  id: anonymousUser.id, +  name: anonymousUser.name, +  color: anonymousUser.color +}; + +ch.subscribe(async (status) => { +  if (status === 'SUBSCRIBED') { +    await ch.track(me); +  } +}); +``` + +3. 同步完整的在线列表 + +当一个新用户加入频道时,他们需要获取当前所有已经在线的用户列表。这通过监听 presence 的 sync 事件来实现。 sync 事件会在你首次加入频道时触发,为你提供一个完整的“快照”。 + +channel.presenceState() 方法会返回一个对象,包含了当前频道内所有在线用户的状态信息。我们将其处理后更新到应用的 state 中,从而渲染出完整的在线用户列表。 + +```Plain +ch.on('presence', { event: 'sync' }, ()  +=> { +  const state = ch.presenceState(); +  const flat = {}; +  Object.values(state).forEach((arr) => { +    arr.forEach((u) => { flat[u.id] =  +    { ...u }; }); +  }); +  setOnline(flat); +}); +``` + +4. 监听单个用户的加入与离开 + +除了 sync 事件,我们还可以监听 join 和 leave 事件,以便在有新用户进入或离开时做出即时响应,例如显示一个 "User has joined" 的通知。 + +```Plain +ch.on('presence', { event: 'join' }, ({  +key, newPresences }) => { +  console.log('User joined:', key,  +  newPresences); +}); + +ch.on('presence', { event: 'leave' }, ({  +key, leftPresences }) => { +  console.log('User left:', key,  +  leftPresences); +}); +``` + +通过以上步骤,我们便构建了一个功能完备的在线状态系统。Supabase 自动处理了用户意外断开连接(如关闭浏览器或断网)的情况,并在适当的时候触发 leave 事件,确保了在线列表的准确性。 + +当 Presence 让我们知道了“谁在场”之后, Broadcast 能够让他们之间能够进行“对话”,但对话的内容是短暂存储的。一个典型的例子就是实时光标追踪。如果每次鼠标移动都去读写数据库,会造成巨大的性能浪费和延迟。 Broadcast 完美地解决了这个问题,它允许消息在各个客户端之间直接通过 WebSocket 传递,完全绕过数据库。 + +Broadcast 的工作模式主要依赖两个核心方法: channel.send() 用于发送,channel.on() 用于接收。】 + +1. 发送端:广播我的光标位置 + +我们为 mousemove 事件添加了一个监听器。当鼠标移动时,我们构造一个包含用户 ID、坐标和颜色的 payload,然后通过 channel.send() 将其广播出去,并指定事件名称为 'cursor'。 + +```TypeScript +const handleMouseMove = (e) => { + const payload = { + id: anonymousUser.id, + x: e.clientX, + y: e.clientY, + name: anonymousUser.name, + color: anonymousUser.color + }; + + channelRef.current?.send({ + type: 'broadcast', + event: 'cursor', + payload + }); +}; + +document.addEventListener('mousemove', handleMouseMove); +``` + +2. 接收端:监听并渲染他人的光标 + +在同一个频道内,所有客户端都使用 channel.on() 来监听 broadcast 类型的、且 event 为 'cursor' 的消息。一旦收到匹配的消息,回调函数就会被触发。我们从 payload 中解析出发送方的数据,并用它来更新本地的 online 状态,从而在屏幕上实时渲染出其他用户光标的位置。 + +```TypeScript +ch.on('broadcast', { event: 'cursor' }, ({ payload }) => { + setOnline((prev) => ({ + ...prev, + [payload.id]: { + ...(prev[payload.id] || {}), + x: payload.x, + y: payload.y + } + })); +}); +``` + +通过这种方式, Presence 和 Broadcast 协同工作;Presence 维护在线用户列表,而 Broadcast 则负责在这些用户之间传递像光标位置这样的临时状态,最终以较低的成本实现了丰富的实时互动功能。 + +## 5.4 Storage + +除了用户信息、订单这类可规整定义的结构化数据,一个完整的应用通常还需要处理大量非结构化文件 —— 例如用户头像、商品展示图、用户上传的订单文档等。这类文件的特点是体积差异大、数量可能极多(比如电商平台的商品图可能达数万甚至数十万张),若直接存储在应用自身的业务服务器中,会显著增加服务器的存储负载,还可能拖慢数据读写速度,影响应用整体性能。 + +实际开发中,这类非结构化文件会统一交由 “对象存储服务” 管理,OSS、Amazon S3 均属于这类服务,它们是专门为海量文件存储设计的 “专业存储工具”,能高效应对文件的存储、备份与快速读取需求。而我们在应用中获取这些文件时,并不会直接从对象存储服务的 “底层仓库” 调取,而是通过 URL 地址实现:每个存储在对象存储中的文件,都会被分配一个唯一的 URL(类似 “[https://xxx.oss.com/avatar/user123.jpg](https://xxx.oss.com/avatar/user123.jpg)” 的地址,可简单理解为这个“网站”只有一张图片),这个 URL 就像文件的 “专属访问地址”,前端页面只需通过该地址,就能直接下载或加载头像、商品图,无需依赖应用业务服务器中转,既提升了文件加载速度,也减轻了业务服务器的压力。 + +本项目 `project-burger-shop-storage-uploads-4` 便通过一个用户头像上传功能,深入演示了如何利用 Supabase Storage 构建现代化的文件上传系统,让开发者直观理解非结构化文件从上传到通过 URL 访问的完整流程。此外,本项目使用 `Uppy` 库来提供一个优秀的文件上传界面,并结合 `Tus` 插件实现了可续传上传,通过将 Uppy 的上传端点指向 Supabase 的标准 API (`/storage/v1/upload/resumable`) 进行工作,你可以参考类似的方式实现上传功能组件。 + +![](images/image55.png) + +![](images/image56.png) + +### 5.4.1. Bucket + +Supabase Storage 的组成单元是存储桶 Bucket。你可以把它想象成电脑操作系统中的文件夹。每个 Bucket 都可以有自己独立的安全策略和配置。 + +Storage 内的所有文件都可以通过一个公开的 URL 直接访问,但并不意味着任何人都可以随意上传或修改,具体的访问权限将由更精细的策略来控制。和数据库一样,Storage 的访问权限也是通过行级安全策略来管理的。SQL 策略写在 storage.objects 和 storage.buckets 这两张特殊表上,可以精确定义谁能读取 (SELECT)、上传 (INSERT)、更新 (UPDATE) 或删除 (DELETE) 文件。 + +例如,我们可以创建一条策略,只允许用户上传到以自己 user_id 命名的文件夹下,并且只能上传图片类型的文件: + +```Plain +CREATE POLICY "Allow authenticated  +uploads to avatars bucket" +ON storage.objects FOR INSERT +TO authenticated +WITH CHECK ( +  bucket_id = 'avatars' AND +  auth.uid() = (storage.foldername(name)) +  [1]::uuid AND +  (storage.extension(name) IN ('png',  +  'jpg', 'jpeg')) +); + +CREATE POLICY "Allow public read access  +to avatars" +ON storage.objects FOR SELECT +USING ( bucket_id = 'avatars' ); +``` + +### 5.4.2 获取可访问文件 URL + +本项目需要你手动创建一个名为 avatars 的公共桶,所有文件将上传至该公共桶下进行存储。文件上传成功后,我们只得到了它在 Storage 中的存储路径 ,例如 public/avatar1.png 。这只是存储在数据库中的一个字符串,要让浏览器能够渲染这张图片,我们需要将其转换为一个可访问的 HTTP URL。 + +Supabase 提供了两种截然不同的策略来获取这个 URL,它们在安全性、持久性和成本控制上有着本质的区别。 + +#### 1. 公开 URL (Public URL) - 永久链接 + +这是最直接的方式。如果你的文件存放在一个**Public Bucket** 中,你可以获取一个固定、永久的公开链接。 + +```TypeScript +const { data } = supabase.storage + .from('avatars') + .getPublicUrl('public/avatar1.png'); +const publicUrl = data.publicUrl; +``` + +这类链接具有两大核心特点:一是简单直接,其 URL 结构固定,在实际操作中易于拼接和管理,降低了技术使用门槛;二是利于缓存,作为永久链接,它能被 CDN(内容分发网络)和浏览器有效缓存,从而大幅提升资源的访问速度,优化用户体验。基于这些特点,它适用于真正意义上的公共资源场景,例如网站 Logo、产品目录图片、博客文章配图等,能很好地满足这类资源的访问和管理需求。 + +不过在生产环境中,这类链接存在明显的被盗刷流量(Hotlinking)风险。由于链接是永久公开的,外部人员可以轻易将你的图片链接嵌入到他们自己的高流量网站中,导致流量被非法占用。这一行为会让你的 Supabase 项目产生大量不必要的流量费用,而这些消耗的流量并未服务于你自身的应用,属于典型的成本浪费,是生产环境中需要高度警惕和防范的问题;因此,我们需要转向临时签名 URL 实现对外资源的暴露。 + +#### 2. 签名 URL (Signed URL) - 临时授权链接 + +为了解决公开 URL 的安全和成本问题,Supabase 提供了生成临时签名 URL 的方式。这是绝大多数线上应用推荐的最佳实践,比如文生图应用给用户生成限时查看的图片链接、电商平台仅让下单用户获取临时发票下载地址、付费内容平台为订阅用户提供短期有效的课程播放链接,既防文件盗用又能避免流量盗刷,适配性极强。 + +```TypeScript +const { data, error } = await supabase.storage + .from('avatars') + .createSignedUrl('private/user-invoice.pdf', 3600); // 链接有效期为 3600 秒 (1小时) +const signedUrl = data?.signedUrl; +``` + +临时签名 URL(Signed URL)有三大核心优势:安全可控是指链接带安全标记、有有效期,过期就用不了;权限绑定很简单 —— 只有能看这文件的人,才能生成这个链接,就算文件藏在私有存储里(Private Bucket),他用这个链接也能正常打开;杜绝盗刷是因为链接是临时的,复制到别处很快就失效,不会被恶意刷流量。靠这些优势,像用户头像、私人照片、付费内容、订单发票这些需要管权限的文件,都能用它。 + +从安全保障和成本控制的角度,建议养成优先使用临时签名 URL 的习惯。只有当某个资源明确需要永久公开、无限制访问(比如应用的公开 Logo、公共活动宣传图等)时,才考虑使用 Public URL。这样既能满足特定业务需求,又能最大程度规避不必要的风险和成本消耗。 + +## 5.5 Edge Function + +Edge Function 是 Serverless(无服务器架构)生态中极具核心价值的形态之一,它为 “无自建后端” 场景提供了轻量、高效的函数运行支持。 + +什么是 Serverless? Serverless(无服务器架构)并不意味着真的没有服务器,而是指开发者无需关心服务器的购买、运维、配置和扩容 。你只需要编写业务代码(函数),云服务商会在特定事件触发时自动为你分配资源运行代码,并按实际运行时间计费。 + +当你的应用需要执行一些不能或不应在客户端(浏览器)上完成的逻辑时——例如与需要私密密钥的第三方 API 交互、执行计算密集型任务、或强制执行复杂的业务规则——Edge Functions 就派上了用场。Supabase Edge Functions 基于 Deno 和 TypeScript,它们被部署在全球的边缘节点上,物理距离上靠近你的用户,从而提供极低的函数执行延迟。 + +目前主流云厂商都推出了各自的 Edge Function 服务,常见的包括: + +* AWS Lambda@Edge:基于 AWS Lambda 延伸的边缘函数服务,可与 CloudFront CDN 联动,支持 Node.js、Python 等语言; +* Cloudflare Workers:Cloudflare 推出的边缘函数,部署在其全球 275+ 边缘节点,支持 JavaScript/TypeScript,以 “毫秒级延迟” 为核心优势; +* Vercel Edge Functions:适配 Vercel 前端项目的边缘函数,与 Next.js 深度集成,支持 TypeScript,主打 “前端与边缘逻辑无缝衔接”; + +回到 Supabase ,当你的应用需要执行 “不能在客户端(浏览器)完成” 的逻辑时,比如用私密密钥调用第三方 API(如 LLM 接口)、处理计算密集型任务(如图片压缩)、或强制执行权限校验(如文件访问规则)时,Supabase Edge Functions 就能发挥作用。它基于 Deno runtime 和 TypeScript 构建,部署在全球边缘节点上,能以 “靠近用户的物理距离” 实现极低的执行延迟,是编写自定义、可信服务器端逻辑的核心工具。 + +本项目 `Project5-Supabase-Demos/apps/project-burger-shop-edge-function-5`通过一个与大语言模型(LLM)实时流式对话的功能,展示了 Edge Functions 的最简应用流程。 + +![](images/image57.png) + +### 5.5.1 LLM Chat 案例解析 + +假设你想在应用中集成一个类似 ChatGPT 的聊天机器人。你需要在服务器端调用 OpenAI 的 API,但这需要一个私密的 API Key。 这个 Key 绝对不能暴露在前端代码中 ,否则任何人都可以通过查看网页源码盗用你的 Key,产生高昂的费用。这正是 Edge Function 的用武之地。我们将创建一个名为 llm-chat 的函数,它充当了前端和 OpenAI API 之间的一个 安全代理 。 + +参考 `project-burger-shop-edge-function-5/scripts/llm-chat.ts`的代码,我们来看看它是如何工作的: + +```TypeScript +// scripts/llm-chat.ts +import "jsr:@supabase/functions-js/edge-runtime.d.ts"; +import { OpenAI } from "npm:openai"; + +const OPENAI_API_KEY = Deno.env.get("OPENAI_API_KEY"); + +Deno.serve(async (req) => { + try { + const openai = new OpenAI({ apiKey: OPENAI_API_KEY }); + const { prompt } = await req.json(); + + const stream = await openai.chat.completions.create({ + model: "gpt-3.5-turbo", + messages: [{ role: "user", content: prompt }], + stream: true, + }); + + return new Response(stream.toReadableStream(), { + headers: { "Content-Type": "text/event-stream" }, + }); + } catch (err) { + } +}); +``` + +在该案例中,对于密钥安全,OPENAI_API_KEY 作为环境变量被安全存储于 Supabase 的服务器。本地前端代码完全无法接触到该密钥,从而有效保障了密钥的安全性。 + +### 5.5.2 创建并部署函数 + +Supabase 提供了非常友好的界面,让你无需接触命令行即可完成部署。 + +1. **进入 Edge Functions 面板** : +2. 登录你的 Supabase 项目 Dashboard。 +3. 在左侧导航栏中,点击像代码一样的图标,进入 “Edge Functions”。 +4. **创建新函数** : +5. 点击 “Create a new function” 按钮。 + ![](images/image58.png) +6. 为函数命名,例如 `llm-chat`。 +7. **粘贴代码** : + ![](images/image59.png) +8. 在弹出的在线编辑器中, **删除所有默认的占位代码** 。 +9. 打开你本地的 `llm-chat.ts` 文件, **复制其全部内容** 。 +10. 将复制的代码**粘贴**到 Supabase 的在线编辑器中。 +11. **配置****环境变量** ** (Secrets)** : + + 1. 在侧边栏找到 Secrets。 + ![](images/image60.png) + 2. Name: 输入 `OPENAI_API_KEY`。 + 3. Value: 粘贴你自己的 OpenAI API Key。 + 4. 点击 “Save”。在这里设置的 Secret 会被加密存储,并安全地注入到你的函数运行时环境中。 + +若有函数需要更新,记得在 Edge Function 部分执行 Deploy updates。Supabase 会在云端为你构建并部署这个函数。几分钟后,你的函数就可以在线访问。 + +除了作为语言模型的安全代理,Edge Functions 的应用场景远不止于此。实际上,任何需要服务器端逻辑处理的任务,无论是简单的 API 调用、数据验证,还是更复杂的计算,都可以通过 Edge Function 实现。它为你提供了一个轻量级、可扩展的后端,而无需管理任何服务器基础设施。 + +如果你想探索更多可能性,可以参考项目中的其他示例。例如: + +* 图片生成 ( txt2img.ts ) : 这个函数展示了如何利用 Edge Function 调用第三方的文生图(Text-to-Image)API(如 Stability AI, Midjourney 等)来动态生成图片。这是一种典型的计算密集型或需要安全调用外部服务的场景。与 llm-chat 案例一样,API 密钥被安全地存储在 Supabase 后端,前端只负责发送文本描述,然后接收并展示生成的图片,整个过程安全、高效。 +* 发送邮件 ( send-email.ts ) : 在应用中发送欢迎邮件、交易通知或密码重置邮件是常见需求。 send-email.ts 示例演示了如何通过 Edge Function 集成邮件服务(如 Resend, SendGrid)。你无需在客户端代码中暴露敏感的邮件服务 API Key,只需创建一个函数,让前端通过调用这个函数来触发邮件发送。 + +## 5.6 Clerk Login + +Clerk 是一款专注于身份认证与用户管理的专业开发工具,核心能力覆盖用户注册、登录、账号安全MFA、权限控制、会话管理等全链路身份认证相关需求,能帮助开发者快速搭建安全、灵活且符合现代应用标准的用户体系,无需从零开发复杂的身份逻辑。 + +本部分将介绍如何从零开始配置 Clerk 服务,并将其与 Supabase 进行整合。你可以在项目 `project-burger-shop-auth-advanced-clerk-7` 中体验全流程。 + +![](images/image61.png) + +### 5.6.1 创建 Clerk 应用与获取密钥 + +在使用本项目之前,你需要拥有一个 Clerk 账号并创建一个应用。 + +1. 注册与创建: + 1. 访问 [dashboard.clerk.com](https://dashboard.clerk.com/) 并注册账号。 + 2. 点击 "Create application" 。 + ![](images/image62.png) + 3. 输入应用名称(例如 "Burger Shop")。 + 4. 在 "How will your users sign in?" 中,默认勾选 Email , Google , GitHub 。 + 5. 点击 Create application 。 +2. 获取 API Keys: + 1. 创建成功后,你会被引导至 API Keys 页面。 + ![](images/image63.png) + 2. 找到 Publishable key (以 `pk_` 开头) 和 Secret key (以 `sk_` 开头)。 + ![](images/image64.png) + 3. 将它们复制到你的 `.env.local` 文件中(参考本项目 `.env.example`): + + ```Bash + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... + CLERK_SECRET_KEY=sk_test_... + ``` + +### 5.6.2 配置 Supabase 和 Clerk 的原生集成 + +在进一步使用前,我们需要集成 Supabase 与 Clerk 的关联关系,方便之后登录的鉴权跳转以及控制对特定数据库的访问权限。Supabase 与 Clerk 提供官方原生集成能力,通过该集成可快速实现两者的身份认证打通,无需手动配置复杂的适配逻辑,大幅简化用户登录、权限校验等功能的开发流程: + +1. 在 Clerk 中激活对 Supab ase 的官方集成 + 1. 登录 [Clerk Dashboard](https://dashboard.clerk.com/)。 + 2. 在左侧菜单导航至 Integrations (集成)。 + 3. 在列表中找到并点击 Supabase。 + 4. 开启 Enable Supabase 开关(或点击 Activate integration)。 + 5. 关键步骤:激活成功后,页面会显示你的 Clerk Domain(格式通常为 `https://.clerk.accounts.dev` 或你的自定义域名)。请复制这个 Domain 地址,下一步会用到。 +2. 在 Supabase 中添加 Clerk 提供商 + 1. 登录 [Supabase Dashboard](https://supabase.com/dashboard) 并进入你的项目。 + 2. 在左侧菜单导航至 Authentication > Sign In / Up (或者直接点击 Providers)。 + 3. 点击 Add provider 按钮,从下拉列表中选择 Clerk。 + 4. 在弹出的 Clerk Domain 输入框中,粘贴你刚才从 Clerk 复制的 Domain 地址。 + 5. 点击 Save 保存配置。 + +### 5.6.3 通过 Webhook 同步用户数据至 Supabase + +仅仅是集成只满足了鉴定权限的需求,但这并不会将 Clerk 中已经注册的用户信息同步到 Supabase,为了方便管理,我们还需要在 Supabase 的 `public.users` 表中保留一份用户备份,以便进行关联查询或数据分析。我们可以通过 Clerk Webhooks 实现这一功能,完整过程如下: + +1. **Clerk 发送通知** : 当用户在 Clerk 注册或更新资料时,Clerk 会向我们配置的 Webhook URL 发送一个 POST 请求。 +2. **Supabase 接收并写入** : Edge Function 接收请求,验证签名(确保安全),然后将用户数据更新到 Supabase 的数据库表中。 + +在开始之前,我们需要配置同步信息所需的数据表: + +```SQL +-- File: init.sql + +-- 1. Create `users` table for synced Clerk users +-- This table will store user data pushed from Clerk Webhooks. +CREATE TABLE public.users ( + id TEXT NOT NULL PRIMARY KEY, -- Corresponds to Clerk User ID + email TEXT, + first_name TEXT, + last_name TEXT, + image_url TEXT, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +-- 2. Enable Row Level Security (RLS) on the table +-- This is an important security measure to ensure users cannot access any data by default. +ALTER TABLE public.users ENABLE ROW LEVEL SECURITY; + +-- 3. Create RLS policies +-- Policy 1: Allow authenticated users to read their own user info. +-- `auth.jwt()->>'sub'` extracts the user ID from the JWT provided by Clerk. +CREATE POLICY "Authenticated users can view their own user record" +ON public.users FOR SELECT +TO authenticated +USING ( (SELECT auth.jwt()->>'sub') = id ); + +-- Policy 2: Allow users to update their own info. +CREATE POLICY "Authenticated users can update their own user record" +ON public.users FOR UPDATE +TO authenticated +USING ( (SELECT auth.jwt()->>'sub') = id ); +``` + +以及在 Supabase 中启用对应的 Edge function: + +```JavaScript +// File path: supabase/functions/clerk-webhooks/index.ts + +import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' +import { Webhook } from 'npm:svix' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +// Get Clerk Webhook signing secret from environment variables +const CLERK_WEBHOOK_SECRET = Deno.env.get('CLERK_WEBHOOK_SECRET') + +if (!CLERK_WEBHOOK_SECRET) { + throw new Error('CLERK_WEBHOOK_SECRET is not set in environment variables') +} +const supabaseAdmin = createClient( + Deno.env.get('SUPABASE_URL')!, + Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! +) + +serve(async (req) => { + try { + // 1. Get Svix signature info from request headers + const headers = Object.fromEntries(req.headers) + const svix_id = headers['svix-id'] + const svix_timestamp = headers['svix-timestamp'] + const svix_signature = headers['svix-signature'] + + if (!svix_id || !svix_timestamp || !svix_signature) { + return new Response('Missing Svix headers', { status: 400 }) + } + + const payload = await req.json() + const body = JSON.stringify(payload) + + // 2. Verify Webhook signature validity using the secret + const wh = new Webhook(CLERK_WEBHOOK_SECRET) + const evt = wh.verify(body, { + 'svix-id': svix_id, + 'svix-timestamp': svix_timestamp, + 'svix-signature': svix_signature, + }) + + const { id } = evt.data + const eventType = evt.type + console.log(`Received webhook event: ${eventType} for user: ${id}`) + + // 3. Execute database operations based on event type + switch (eventType) { + case 'user.created': { + const { id, first_name, last_name, image_url, email_addresses } = evt.data + const { error } = await supabaseAdmin.from('users').insert({ + id, + first_name, + last_name, + image_url, + email: email_addresses[0]?.email_address, + }) + if (error) throw error + console.log(`User ${id} created in Supabase.`) + break + } + + case 'user.updated': { + const { id, first_name, last_name, image_url, email_addresses } = evt.data + const { error } = await supabaseAdmin + .from('users') + .update({ + first_name, + last_name, + image_url, + email: email_addresses[0]?.email_address, + updated_at: new Date().toISOString(), // Update timestamp + }) + .eq('id', id) + if (error) throw error + console.log(`User ${id} updated in Supabase.`) + break + } + + case 'user.deleted': { + // For delete events, ID might be at the top level + const deletedId = id + if (!deletedId) { + return new Response('Deleted user ID not found', { status: 400 }) + } + const { error } = await supabaseAdmin.from('users').delete().eq('id', deletedId) + if (error) throw error + console.log(`User ${deletedId} deleted from Supabase.`) + break + } + } + + return new Response('Webhook processed successfully', { status: 200 }) + } catch (err) { + console.error('Error processing webhook:', err.message) + return new Response(`Webhook Error: ${err.message}`, { status: 400 }) + } +}) +``` + +初始化 Supabase 数据表与函数结束后,你还需要在 Clerk 中启用 Webhooks 支持: + +* 在 Clerk Dashboard -> **Webhooks** 中添加 Endpoint,填入Supabase Edge Function 的 URL。 +* 勾选 `user.created`, `user.updated`, `user.deleted` 等事件。 + +![](images/image65.png) + +一旦设置成功,你能够在 Message Attempts 中看到不同请求信息,点击后可看到详细的请求返回参数结果;如果 webhook 在请求 Edge function 时出现问题,你可以快速在返回值中找到详细原因结果。推荐你同时对照 Clerk 和 Supabase 的请求日志信息,用于分析各个函数设定是否正确。 + +### 5.6.4 Clerk 中的第三方登录支持 + +在深入了解如何对 Clerk 支持第三方登录前,我们先明确两个核心概念:开发环境与生产环境,这是软件从 “开发测试” 到 “上线可用” 的两个关键阶段,二者的定位、用途和安全要求截然不同: + +* 开发环境:开发者本地或测试服务器使用的环境,仅用于功能开发、调试和内部验证(如本地 localhost:3000 服务),不对外开放 +* 生产环境:应用正式上线后,面向真实用户的公开环境(如部署在 Vercel、阿里云等平台的 https://my-app.com) + +而 Clerk 对社交登录区分这两种环境,本质是平衡 “开发效率” 与 “生产安全”:开发阶段需减少冗余配置以快速验证功能,生产阶段需通过专属凭证保障数据安全,同时符合 Google、GitHub 等第三方 OAuth 平台的规则(线上应用必须绑定专属域名与凭证,不允许使用共享资源)。下面具体说明两种环境下 Clerk 社交登录的差异配置: + +1. **开发环境快速验证** + +开发环境中,Clerk 已预置共享 OAuth 凭证和默认重定向 URI,无需前往 GitHub/Google 申请专属凭证,操作步骤如下: + +* 登录 Clerk Dashboard ,在左侧导航栏进入 SSO connections (SSO 连接)页面。 +* 点击 Add connection (添加连接),选择 For all users (对所有用户生效)。 +* 在 Choose provider (选择提供商)下拉菜单中,按需选择 GitHub 或 Google 。 +* 直接点击 Add connection (添加连接),Clerk 会自动用共享凭证完成绑定。 + + 配置后,本地启动应用(如 `localhost:3000`)并点击“Sign in with GitHub/Google”,Clerk 会自动代理登录请求,快速验证功能是否正常。 + +2. **生产环境自定义凭证配置** + +(注:如果发现有环节和预期不一致,建议阅读官方文档进行最新方式的尝试) + +应用部署上线(如 Vercel、阿里云)并切换到 Clerk Production Instance 后,共享凭证失效,需为 GitHub/Google 配置自定义 OAuth 凭证(建议同时打开 Clerk Dashboard 和第三方平台页面,方便同步操作): + +* 前置通用操作(Clerk 控制台): + * 进入 Clerk SSO connections 页面,点击 Add connection → 选择 For all users 。 + * 选择目标平台(GitHub/Google),确保开启 Enable for sign-up and sign-in (允许注册登录)和 Use custom credentials (使用自定义凭证)。 + * 复制页面中的 Authorization Callback URL (GitHub)或 Authorized Redirect URI (Google),保存到安全位置,不要关闭当前页面/弹窗。 +* 2.1 GitHub 平台配置: + * 登录 GitHub,进入 Developer Settings (路径:头像 → Settings → Developer settings → OAuth Apps)。 + * 点击 New OAuth app ,填写信息:`Application name`(应用名称)、`Homepage URL`(生产域名,如 `https://my-app.com`)、`Authorization Callback URL`(粘贴从 Clerk 复制的地址)。 + * 点击 Register application ,再点击 Generate a new client secret ,保存生成的 Client ID 和 Client Secret (Secret 仅显示一次)。 + * 回到 Clerk 弹窗,粘贴 Client ID 和 Client Secret,点击 Add connection 完成配置(若关闭弹窗,可在 SSO connections 找到 GitHub 连接,在“Use custom credentials”模块补填)。 +* 2.2 Google 平台配置: + * 登录 Google Cloud Console ,选择已有项目或新建项目(如“My App Production”)。 + * 点击左上角菜单 → APIs & Services → Credentials ,点击 Create Credentials → OAuth client ID (首次配置需先完成 OAuth consent screen 设置,选择“External”并填写应用信息)。 + * 选择 Application type 为 Web application ,配置: + 1. `Authorized JavaScript origins`:添加生产域名(如 `https://my-app.com`、`https://www.my-app.com`),本地验证可补充 `http://localhost:端口号`。 + 2. `Authorized Redirect URIs`:粘贴从 Clerk 复制的地址。 + * 点击 Create ,保存弹窗中的 Client ID 和 Client Secret ,回到 Clerk 弹窗粘贴并点击 Add connection 。 + * 关键注意事项: + 1. 禁止 WebView 登录:Google OAuth 不支持应用内浏览器登录,需参考 [Google 官方文档](https://support.google.com/cloud/answer/7657789) 调整。 + 2. 切换发布状态:默认“Testing”状态仅支持 100 个测试用户,需在 OAuth consent screen 将“Publishing status”改为 In production (需通过 Google 审核)。 + 3. 阻止子邮箱:Clerk 默认拦截含 `+`/`=`/`#` 的 Google 邮箱(如 `user+alias@example.com`),可在 Google 连接详情页开启/关闭 Block email subaddresses (建议开启提升安全性)。 + 4. 支持 Google One Tap:配置完成后,可集成 Clerk `` 组件实现“一键登录”,参考 [Clerk 组件文档](https://clerk.com/docs/components/social-connections/google-one-tap)。 + +3. 测试第三方登录连接 + +配置完成后,通过 Clerk 内置 Account Portal 验证功能: + +* 进入 Clerk Dashboard,左侧导航栏进入 Account Portal 页面。 +* 在“Sign-in”模块右侧,点击“访问登录页面”按钮,跳转至对应环境登录页: + * 开发环境:`https://你的域名.accounts.dev/sign-in`(如 `https://my-app.accounts.dev/sign-in`)。 + * 生产环境:`https://accounts.你的域名.com/sign-in`(如 `https://accounts.my-app.com/sign-in`)。 +* 点击“Sign in with GitHub/Google”,用对应平台账号登录,若能成功跳转并返回应用,说明连接配置正常。 + +# 6. 从 Supabase 到更多后端开发组件(进阶) + +在上文中,我们主要是站在 Supabase 的视角,去看“一个以 Postgres 为核心的一站式后端平台”能帮我们解决哪些问题:认证、数据库、文件存储、实时通信、边缘函数等,都被集成在同一个控制台里,开箱即用、体验统一,非常适合快速起步和中小型项目。 + +但从更长期、更工程化的角度来看, **Supabase 提供的每一块能力(Auth / Storage / Edge Functions / Realtime / Database),在业界几乎都有对应的专业替代方案** ——既包括同类 BaaS 平台,也包括更“单点突破”的云服务和开源组件。作为上进的个人开发者和初创团队来说,了解这些替代选项有几个好处: + +* 判断当前项目是否“全用 Supabase 就够了”,还是某一块需要更专业/更便宜/更易合规的专用服务; +* 当项目规模变大或需求变复杂时,是否可以把某个模块从 Supabase 替换出去(例如改用专门的 Auth 平台或对象存储),而不是一开始就被平台彻底锁死; +* 拓宽技术选型视野,即使暂时不更换,也能大致知道“如果不用 Supabase 的 X 功能,我还有哪些常见选择”。 + +本节将分别介绍 Supabase 所覆盖的几大能力在市场上的主流替代方案,例如:认证(Auth)、文件存储(Storage)、边缘函数(Edge Functions)、实时通信(Realtime)、数据库托管等。简单对比它们在功能特性、免费额度/定价、易用性以及社区流行度等方面的差异, 让你对后端组件工具库有更全面的理解。 + +## 同类 Baas 平台 + +在开始之前,我们可以浏览同类的 Baas 平台,若觉得 Supabase 不够好用,你可以根据需求选择不同替代品进行尝试。 + +| 平台/服务 | 类型 | 免费额度/定价 | 特点 / 适用场景 | +| ------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Firebase(Google) | 全托管 BaaS(Auth + Firestore + Storage + Functions + Hosting) | Spark:免费轻量额度;Blaze:按量计费(Firestore/Storage/Functions 分别算) | 行业最成熟、文档好、上手快、实时能力强。适用于中小型产品、移动/前端主导团队。缺点:计费复杂、锁定性强、查询限制多(尤其 Firestore)。 | +| Supabase | 开源 BaaS(Postgres + Auth + Storage + Edge Functions + Realtime) | 免费:500MB DB、1GB Storage、无服务器函数少量调用;Pro:按实例计费 | 最像 Firebase 的 SQL 版;界面优秀、体验现代、可自托管。适用于需要强 SQL、BI、事务能力的应用。缺点:高并发或复杂函数成本较高。 | +| Appwrite Cloud | 开源一站式 BaaS(DB + Auth + Storage + Functions + Realtime) | 免费:包含基本 DB/Storage/FaaS;付费按资源级别计费 | 体验现代化、API 统一、可自托管;适合开发者友好的应用快速迭代。缺点:生态还不如 Firebase/Supabase 成熟;性能在大型应用中需要测试。 | +| Nhost | Postgres + GraphQL + Auth + Storage + Functions | 免费:1GB DB、1GB Storage、少量函数调用 | 类似“Supabase + Hasura”;天然 GraphQL;适合前端团队与 React/Next.js 项目。缺点:生态小、成本随用量升高。 | +| AWS Amplify | AWS 一站式后端(Cognito + AppSync + DynamoDB + Storage + Functions + Hosting) | 免费:Hosting 额度 + Cognito 10k MAU + 部分函数额度 | 大而全,适合已有 AWS 基础的团队;企业级可靠性。缺点:最难上手,服务碎片化;初创团队维护成本高。 | +| Xata(近两年快速增长) | 多模型数据库 + Auth + Edge Functions | 免费:250k 记录、15GB 带宽 | 虽然更偏「DB + API」,但提供 Auth、文件、逻辑,可作为轻量全栈后端。UI/开发体验极佳。缺点:功能不如 Firebase/Supabase 全面。 | +| Convex(开发者体验极强) | 托管数据库 + Auth + Functions(前端优先) | 免费开发版;付费按请求量计费 | 极简上手;无需 schema;前端写函数即可用后端。适合 MVP/快速验证。缺点:高度绑定平台,迁移成本高;不算完全传统 BaaS。 | + +## 认证 (Auth) + +| 工具/平台 | 功能特点 | 免费额度/定价 | 适用场景与优缺点 | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| Firebase Authentication | Google 提供的 BaaS 身份验证服务,支持邮箱/密码、手机、社交登录、匿名等常见方式。Spark 免费方案支持最高50k 月活跃用户。 | Spark(免费)50k MAU;Blaze 按量计费 | 集成 Google 生态,文档丰富,上手简单;功能全面(MFA、阻塞函数等),适合快速开发。但与 Firebase 平台绑定,扩展到其他服务需额外配置。 | +| Auth0 (Okta) | 全托管身份认证平台,支持社交登录、企业 SSO、多因子认证、规则扩展等强大功能。 | 免费方案25k MAU,付费按 MAU 计费 | 企业级功能齐全(RBAC、审计日志等),适合中大型应用;界面友好。缺点是 MAU 上升时成本高,免费版功能有限(如不含 MFA/RBAC)。社区知名度高,用户众多。 | +| AWS Cognito | 亚马逊云原生身份服务,支持社交及 SAML 联合登录。直接登录用户池提供每月10k MAU 免费,超过部分按 0.0055 美元/MAU 收费。 | 免费10k MAU/月,超出按量付费 | 与 AWS 生态深度集成(可无缝配合 API Gateway、Lambda 等),入门门槛略高,文档较复杂;免费额度有限,适合已有 AWS 使用习惯的团队。 | +| Logto | 开源身份认证平台,自托管版免费,云服务计划免费50k MAU。支持多语言、多租户、OAuth/OIDC 等。 | 社区版免费;Logto Cloud 免费50k MAU | 近期流行的 Auth0 开源替代方案,GitHub 已有 10k+ Stars。易扩展,自托管降低成本;缺点是生态和文档相对较新,社区规模略逊于 Firebase/Auth0。 | +| Keycloak | 知名开源 IAM/SSO 解决方案,支持用户名密码、LDAP、SAML、OAuth2 等。 | 完全免费,需自托管 | 功能强大、可扩展(支持细粒度权限控制),企业级功能丰富;但部署和维护复杂度高,对小团队而言学习曲线较陡。缺点是对容器化和集群运维要求较高。 | + +## 文件存储 (Storage) + +| 平台/服务 | 类型 | 免费额度/定价 | 特点/适用场景 | +| ---------------------------------------- | -------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| Amazon S3 | 云对象存储(AWS) | AWS 免费套餐提供 5GB 存储、20k GET/PUT 请求/月,超出按使用量付费 | 行业标准的对象存储,可靠性高、全球多区域部署。功能全面,与 AWS 生态整合良好;定价较复杂,新用户需了解计费规则。 | +| Google Cloud Storage(Firebase Storage) | 云对象存储(Google) | Firebase Spark 方案提供免费额度(1GB 存储 + 流量限制),Blaze 付费 | 与 Firebase/Google Cloud 紧密集成,易于管理;支持 CDN 加速、细粒度安全规则。 | +| 腾讯云 COS / 阿里云 OSS | 云对象存储(国内) | 按量付费(各有新用户赠送额度,如OSS有首年40GB免费等) | 面向国内市场,高性能、大规模对象存储;与中国云生态整合,文档较完善。阿里OSS 功能全面、全球加速;七牛KODO 专注多媒体处理,成本较低,适合个人和小团队。 | +| MinIO | 开源 S3 兼容存储 | 开源免费(自建) | 轻量级、高性能、与 S3 API 兼容,适合在私有云或本地搭建对象存储。文档和社区活跃;需自己维护基础设施。 | +| Cloudinary / Imgix 等 | 媒体存储+CDN | 基本免费方案(如 Cloudinary 免费 25GB/月带宽) | 针对图片/视频优化的云存储+CDN 服务,提供实时转码、压缩等高级功能。适合媒体项目,但功能较专一,作为通用文件存储使用成本偏高。 | + +## 边缘函数 (Edge Functions) + +| 平台/服务 | 特点 | 免费额度/定价 | 适用场景与优缺点 | +| -------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Cloudflare Workers | 全球分布式 JavaScript/Wasmtime 环境 | 免费计划:每天 100k 请求;标准计划$5/月含1,000万请求 | 运行在 Cloudflare 边缘节点,延迟极低;适合全局分发的逻辑、静态资源渲染等。免费配额较少(相当于每月约300万请求),上手简单。缺点是运行时(JS/Wasmtime)限制与调试工具有限。 | +| Vercel Edge Functions | 随 Next.js/前端框架无缝集成,支持 JS/TS/Go | Hobby 免费:每月 100万 函数调用,100万 边缘请求 | 深度集成前端框架,自动部署;适合现代 Web 应用。免费额度充足,默认运行时 10s,可提升至 60s。缺点是免费版团队协作功能受限;依赖 Vercel 平台。 | +| Netlify Edge / Functions | Node.js 云函数+边缘路由(NFT) | 免费:300 代币/月(约相当于每月 1M 请求);按信用点计费 | 支持 Node.js 函数、边缘处理路由等。免费额度用于构建、函数和带宽,适合前端全栈部署。优点是简便易用,集成 Git 部署;缺点是免费额度使用需算计(10k 请求 = 3 点)。 | +| AWS Lambda@Edge / CloudFront Functions | AWS 无服务器边缘计算 | AWS Lambda(1M 免费请求/月+400k GB-s)+ CloudFront $0.085/每10万调用起 | 与 CloudFront 集成,可在边缘执行代码。适合需要 AWS 生态(如在节点层面做权限或 A/B 测试)。优点是灵活强大;缺点是配置复杂,延迟略高于 Cloudflare/Vercel。 | + +## 实时通信 (Realtime) + +| 平台/服务 | 功能特点 | 免费额度/定价 | 适用场景与优缺点 | +| -------------------------------------- | ------------------------------------------------ | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| Firebase Realtime Database / Firestore | Google BaaS 实时数据库;支持数据变更推送 | Spark 免费:实时数据库1GB 存储 & 限额;Blaze 按量付费 | 强集成 Firebase 生态,实时监听简单。优点是免费起步快;缺点是数据库类型(JSON/NoSQL),复杂查询能力弱。 | +| Ably | 实时消息与 pub/sub 平台,支持 WebSocket、MQTT 等 | 免费包:每月 6,000,000 条消息 | 功能全面的实时消息服务,高并发支持;免费额度可达600万消息/月。社区与文档较好,适合全球分布。 | +| Pusher Channels | 事件推送服务,支持频道/事件机制 | Sandbox 免费:每日 200k 消息,100 并发连接 | 易用的 WebSocket 服务,文档齐全,适合快速实现聊天和通知功能。免费版限制消息量和连接数;付费后扩展性好。 | +| 自建 WebSocket/Socket.IO | 自己搭建服务器(Node.js、Elixir 或 Go 等) | 自行托管成本(如服务器费用) | 灵活度最高,可根据需求定制协议和拓扑。适合对成本控制严格且技术成熟的团队。缺点是需自行处理可用性、扩展和跨域等问题。 | + +## 数据库 + +| 平台/工具 | 数据库类型 | 免费额度/定价 | 主要特点 | +| ---------------------------- | --------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Neon (Serverless PostgreSQL) | 关系型(PostgreSQL) | 免费计划:0.5GB 存储,主分支永久在线,20h 分支计算/月 | 云原生无服务器 Postgres,支持自动伸缩和分支(fork 测试)。免费额度对小项目够用,适合现代开发流程。分支功能强大,但免费额度较小。 | +| Aiven PostgreSQL | 关系型(PostgreSQL/MySQL) | 免费计划:1GB 存储,1 vCPU,1GB 内存 | 托管级数据库服务,支持跨云多区域迁移。提供有 MySQL、Redis 等可选。免费额度适合开发和小型项目;商业版支持高可用集群和监控。 | +| CockroachDB Cloud | 分布式 SQL(兼容 PostgreSQL) | 免费计划:10GB 存储 | 类似 Google Spanner 的分布式 SQL 数据库,自动分片扩展。免费10GB 空间较慷慨;适合需要横向扩展和高一致性的应用。商业版 SLA 高。 | +| TiDB Cloud | 分布式关系型(MySQL 兼容) | 免费计划:每节点5GB,总计最多25GB | 开源 TiDB 的云版,兼容 MySQL 协议,分布式架构。免费额度充足,适合熟悉 MySQL 的团队,性能优秀;缺点是运维相对复杂(针对大型场景)。 | +| MongoDB Atlas | 文档型(NoSQL MongoDB) | 免费 M0 集群:0.5GB 存储 | 云端 MongoDB,灵活的文档模型,支持丰富查询和索引。免费 0.5GB 数据库适合测试和小型应用;可按需横向扩展。学习曲线略高于关系型数据库。 | +| SQLPub | 多数据库(MySQL、PostgreSQL、Redis 等) | 免费计划:36,000 请求/小时,30 并发连接,500MB 存储 | 一站式数据库平台,支持多种数据库类型。免费版适合学习和小项目;优点是支持多种 DB,缺点是存储额度较小。 | + +以上替代方案各有侧重:开源更灵活可控(Keycloak、MinIO、Socket.IO、Neon、CockroachDB 等),云托管服务更易上手(Firebase、Auth0、Cloudflare、Vercel、Netlify、AWS、Aiven、MongoDB Atlas 等)。选择时可根据项目需求、团队技术栈、预算和社区生态等权衡。个人项目可优先选用免费配额充足、易集成的服务(如 Firebase 系列、七牛存储、Cloudflare Workers、Neon、CockroachDB 等),而对企业级或特定安全需求,则可考虑功能更丰富但收费较高的方案(Auth0、Alibaba/Tencent 云、AWS、TiDB/Aiven 等)。你可以在实际应用中不断尝试,直到选择出最适合的后端开发工具组件。 + +# 总结 + +在今天的课程中,我们系统学习了数据库的基础概念、Supabase 的核心定义及其操作细节。后续在实践过程中,你可根据项目的实际应用场景与需求,随时回头翻阅这份文档作为参考。 + +请时刻记住一个重要原则: **先完成,再完美!** 无需追求一步到位,我们完全可以通过持续迭代优化,逐步靠近更优的成果。祝你在后续的项目实践中一切顺利! + +# 📚 课后作业 + +1. 开发一个包含用户管理系统和数据库的应用程序。最好包含更多的Supabase 功能 (Realtime / cloud storage / Edge function). diff --git a/docs/project/chapter5/images/image1.png b/docs/project/chapter5/images/image1.png new file mode 100644 index 0000000..b8468a4 Binary files /dev/null and b/docs/project/chapter5/images/image1.png differ diff --git a/docs/project/chapter5/images/image10.png b/docs/project/chapter5/images/image10.png new file mode 100644 index 0000000..5a0da44 Binary files /dev/null and b/docs/project/chapter5/images/image10.png differ diff --git a/docs/project/chapter5/images/image11.png b/docs/project/chapter5/images/image11.png new file mode 100644 index 0000000..d632a8f Binary files /dev/null and b/docs/project/chapter5/images/image11.png differ diff --git a/docs/project/chapter5/images/image12.png b/docs/project/chapter5/images/image12.png new file mode 100644 index 0000000..5159982 Binary files /dev/null and b/docs/project/chapter5/images/image12.png differ diff --git a/docs/project/chapter5/images/image13.png b/docs/project/chapter5/images/image13.png new file mode 100644 index 0000000..d76b08a Binary files /dev/null and b/docs/project/chapter5/images/image13.png differ diff --git a/docs/project/chapter5/images/image14.png b/docs/project/chapter5/images/image14.png new file mode 100644 index 0000000..01426a8 Binary files /dev/null and b/docs/project/chapter5/images/image14.png differ diff --git a/docs/project/chapter5/images/image15.png b/docs/project/chapter5/images/image15.png new file mode 100644 index 0000000..f6a7ce7 Binary files /dev/null and b/docs/project/chapter5/images/image15.png differ diff --git a/docs/project/chapter5/images/image16.png b/docs/project/chapter5/images/image16.png new file mode 100644 index 0000000..1d7e5da Binary files /dev/null and b/docs/project/chapter5/images/image16.png differ diff --git a/docs/project/chapter5/images/image17.png b/docs/project/chapter5/images/image17.png new file mode 100644 index 0000000..371eee4 Binary files /dev/null and b/docs/project/chapter5/images/image17.png differ diff --git a/docs/project/chapter5/images/image18.png b/docs/project/chapter5/images/image18.png new file mode 100644 index 0000000..11e4096 Binary files /dev/null and b/docs/project/chapter5/images/image18.png differ diff --git a/docs/project/chapter5/images/image19.png b/docs/project/chapter5/images/image19.png new file mode 100644 index 0000000..7e51ccc Binary files /dev/null and b/docs/project/chapter5/images/image19.png differ diff --git a/docs/project/chapter5/images/image2.png b/docs/project/chapter5/images/image2.png new file mode 100644 index 0000000..48a055d Binary files /dev/null and b/docs/project/chapter5/images/image2.png differ diff --git a/docs/project/chapter5/images/image20.png b/docs/project/chapter5/images/image20.png new file mode 100644 index 0000000..9aea866 Binary files /dev/null and b/docs/project/chapter5/images/image20.png differ diff --git a/docs/project/chapter5/images/image21.png b/docs/project/chapter5/images/image21.png new file mode 100644 index 0000000..498cb4e Binary files /dev/null and b/docs/project/chapter5/images/image21.png differ diff --git a/docs/project/chapter5/images/image22.png b/docs/project/chapter5/images/image22.png new file mode 100644 index 0000000..68e5a53 Binary files /dev/null and b/docs/project/chapter5/images/image22.png differ diff --git a/docs/project/chapter5/images/image23.png b/docs/project/chapter5/images/image23.png new file mode 100644 index 0000000..3dfc732 Binary files /dev/null and b/docs/project/chapter5/images/image23.png differ diff --git a/docs/project/chapter5/images/image24.png b/docs/project/chapter5/images/image24.png new file mode 100644 index 0000000..bc64f0a Binary files /dev/null and b/docs/project/chapter5/images/image24.png differ diff --git a/docs/project/chapter5/images/image25.png b/docs/project/chapter5/images/image25.png new file mode 100644 index 0000000..69d397a Binary files /dev/null and b/docs/project/chapter5/images/image25.png differ diff --git a/docs/project/chapter5/images/image26.png b/docs/project/chapter5/images/image26.png new file mode 100644 index 0000000..f2c1eca Binary files /dev/null and b/docs/project/chapter5/images/image26.png differ diff --git a/docs/project/chapter5/images/image27.png b/docs/project/chapter5/images/image27.png new file mode 100644 index 0000000..f56b5e9 Binary files /dev/null and b/docs/project/chapter5/images/image27.png differ diff --git a/docs/project/chapter5/images/image28.png b/docs/project/chapter5/images/image28.png new file mode 100644 index 0000000..e2cd2c3 Binary files /dev/null and b/docs/project/chapter5/images/image28.png differ diff --git a/docs/project/chapter5/images/image29.png b/docs/project/chapter5/images/image29.png new file mode 100644 index 0000000..7251ddf Binary files /dev/null and b/docs/project/chapter5/images/image29.png differ diff --git a/docs/project/chapter5/images/image3.png b/docs/project/chapter5/images/image3.png new file mode 100644 index 0000000..4538062 Binary files /dev/null and b/docs/project/chapter5/images/image3.png differ diff --git a/docs/project/chapter5/images/image30.png b/docs/project/chapter5/images/image30.png new file mode 100644 index 0000000..de181ba Binary files /dev/null and b/docs/project/chapter5/images/image30.png differ diff --git a/docs/project/chapter5/images/image31.png b/docs/project/chapter5/images/image31.png new file mode 100644 index 0000000..605ca3c Binary files /dev/null and b/docs/project/chapter5/images/image31.png differ diff --git a/docs/project/chapter5/images/image32.png b/docs/project/chapter5/images/image32.png new file mode 100644 index 0000000..8151004 Binary files /dev/null and b/docs/project/chapter5/images/image32.png differ diff --git a/docs/project/chapter5/images/image33.png b/docs/project/chapter5/images/image33.png new file mode 100644 index 0000000..4179ccf Binary files /dev/null and b/docs/project/chapter5/images/image33.png differ diff --git a/docs/project/chapter5/images/image34.png b/docs/project/chapter5/images/image34.png new file mode 100644 index 0000000..98958c1 Binary files /dev/null and b/docs/project/chapter5/images/image34.png differ diff --git a/docs/project/chapter5/images/image35.png b/docs/project/chapter5/images/image35.png new file mode 100644 index 0000000..43da753 Binary files /dev/null and b/docs/project/chapter5/images/image35.png differ diff --git a/docs/project/chapter5/images/image36.png b/docs/project/chapter5/images/image36.png new file mode 100644 index 0000000..15aa607 Binary files /dev/null and b/docs/project/chapter5/images/image36.png differ diff --git a/docs/project/chapter5/images/image37.png b/docs/project/chapter5/images/image37.png new file mode 100644 index 0000000..1ae4eea Binary files /dev/null and b/docs/project/chapter5/images/image37.png differ diff --git a/docs/project/chapter5/images/image38.png b/docs/project/chapter5/images/image38.png new file mode 100644 index 0000000..0b5e024 Binary files /dev/null and b/docs/project/chapter5/images/image38.png differ diff --git a/docs/project/chapter5/images/image39.png b/docs/project/chapter5/images/image39.png new file mode 100644 index 0000000..d6eca6d Binary files /dev/null and b/docs/project/chapter5/images/image39.png differ diff --git a/docs/project/chapter5/images/image4.png b/docs/project/chapter5/images/image4.png new file mode 100644 index 0000000..5b450b9 Binary files /dev/null and b/docs/project/chapter5/images/image4.png differ diff --git a/docs/project/chapter5/images/image40.png b/docs/project/chapter5/images/image40.png new file mode 100644 index 0000000..14573a1 Binary files /dev/null and b/docs/project/chapter5/images/image40.png differ diff --git a/docs/project/chapter5/images/image41.png b/docs/project/chapter5/images/image41.png new file mode 100644 index 0000000..160a098 Binary files /dev/null and b/docs/project/chapter5/images/image41.png differ diff --git a/docs/project/chapter5/images/image42.png b/docs/project/chapter5/images/image42.png new file mode 100644 index 0000000..2ac4a19 Binary files /dev/null and b/docs/project/chapter5/images/image42.png differ diff --git a/docs/project/chapter5/images/image43.png b/docs/project/chapter5/images/image43.png new file mode 100644 index 0000000..c64d76d Binary files /dev/null and b/docs/project/chapter5/images/image43.png differ diff --git a/docs/project/chapter5/images/image44.png b/docs/project/chapter5/images/image44.png new file mode 100644 index 0000000..8332c90 Binary files /dev/null and b/docs/project/chapter5/images/image44.png differ diff --git a/docs/project/chapter5/images/image45.png b/docs/project/chapter5/images/image45.png new file mode 100644 index 0000000..90bb901 Binary files /dev/null and b/docs/project/chapter5/images/image45.png differ diff --git a/docs/project/chapter5/images/image46.png b/docs/project/chapter5/images/image46.png new file mode 100644 index 0000000..7f915b5 Binary files /dev/null and b/docs/project/chapter5/images/image46.png differ diff --git a/docs/project/chapter5/images/image47.png b/docs/project/chapter5/images/image47.png new file mode 100644 index 0000000..25ebc72 Binary files /dev/null and b/docs/project/chapter5/images/image47.png differ diff --git a/docs/project/chapter5/images/image48.png b/docs/project/chapter5/images/image48.png new file mode 100644 index 0000000..659b627 Binary files /dev/null and b/docs/project/chapter5/images/image48.png differ diff --git a/docs/project/chapter5/images/image49.png b/docs/project/chapter5/images/image49.png new file mode 100644 index 0000000..10badd3 Binary files /dev/null and b/docs/project/chapter5/images/image49.png differ diff --git a/docs/project/chapter5/images/image5.png b/docs/project/chapter5/images/image5.png new file mode 100644 index 0000000..eabaaa5 Binary files /dev/null and b/docs/project/chapter5/images/image5.png differ diff --git a/docs/project/chapter5/images/image50.png b/docs/project/chapter5/images/image50.png new file mode 100644 index 0000000..20d14c2 Binary files /dev/null and b/docs/project/chapter5/images/image50.png differ diff --git a/docs/project/chapter5/images/image51.png b/docs/project/chapter5/images/image51.png new file mode 100644 index 0000000..c459cf8 Binary files /dev/null and b/docs/project/chapter5/images/image51.png differ diff --git a/docs/project/chapter5/images/image52.png b/docs/project/chapter5/images/image52.png new file mode 100644 index 0000000..abe2173 Binary files /dev/null and b/docs/project/chapter5/images/image52.png differ diff --git a/docs/project/chapter5/images/image53.png b/docs/project/chapter5/images/image53.png new file mode 100644 index 0000000..1b4151c Binary files /dev/null and b/docs/project/chapter5/images/image53.png differ diff --git a/docs/project/chapter5/images/image54.png b/docs/project/chapter5/images/image54.png new file mode 100644 index 0000000..c9dae15 Binary files /dev/null and b/docs/project/chapter5/images/image54.png differ diff --git a/docs/project/chapter5/images/image55.png b/docs/project/chapter5/images/image55.png new file mode 100644 index 0000000..37cba2a Binary files /dev/null and b/docs/project/chapter5/images/image55.png differ diff --git a/docs/project/chapter5/images/image56.png b/docs/project/chapter5/images/image56.png new file mode 100644 index 0000000..e50e004 Binary files /dev/null and b/docs/project/chapter5/images/image56.png differ diff --git a/docs/project/chapter5/images/image57.png b/docs/project/chapter5/images/image57.png new file mode 100644 index 0000000..17e0e3d Binary files /dev/null and b/docs/project/chapter5/images/image57.png differ diff --git a/docs/project/chapter5/images/image58.png b/docs/project/chapter5/images/image58.png new file mode 100644 index 0000000..b5f23ff Binary files /dev/null and b/docs/project/chapter5/images/image58.png differ diff --git a/docs/project/chapter5/images/image59.png b/docs/project/chapter5/images/image59.png new file mode 100644 index 0000000..58570ca Binary files /dev/null and b/docs/project/chapter5/images/image59.png differ diff --git a/docs/project/chapter5/images/image6.png b/docs/project/chapter5/images/image6.png new file mode 100644 index 0000000..417a477 Binary files /dev/null and b/docs/project/chapter5/images/image6.png differ diff --git a/docs/project/chapter5/images/image60.png b/docs/project/chapter5/images/image60.png new file mode 100644 index 0000000..0264e8e Binary files /dev/null and b/docs/project/chapter5/images/image60.png differ diff --git a/docs/project/chapter5/images/image61.png b/docs/project/chapter5/images/image61.png new file mode 100644 index 0000000..7d94972 Binary files /dev/null and b/docs/project/chapter5/images/image61.png differ diff --git a/docs/project/chapter5/images/image62.png b/docs/project/chapter5/images/image62.png new file mode 100644 index 0000000..79905db Binary files /dev/null and b/docs/project/chapter5/images/image62.png differ diff --git a/docs/project/chapter5/images/image63.png b/docs/project/chapter5/images/image63.png new file mode 100644 index 0000000..82cadea Binary files /dev/null and b/docs/project/chapter5/images/image63.png differ diff --git a/docs/project/chapter5/images/image64.png b/docs/project/chapter5/images/image64.png new file mode 100644 index 0000000..11e7a1b Binary files /dev/null and b/docs/project/chapter5/images/image64.png differ diff --git a/docs/project/chapter5/images/image65.png b/docs/project/chapter5/images/image65.png new file mode 100644 index 0000000..e3565c7 Binary files /dev/null and b/docs/project/chapter5/images/image65.png differ diff --git a/docs/project/chapter5/images/image7.png b/docs/project/chapter5/images/image7.png new file mode 100644 index 0000000..d54b10a Binary files /dev/null and b/docs/project/chapter5/images/image7.png differ diff --git a/docs/project/chapter5/images/image8.png b/docs/project/chapter5/images/image8.png new file mode 100644 index 0000000..5059e21 Binary files /dev/null and b/docs/project/chapter5/images/image8.png differ diff --git a/docs/project/chapter5/images/image9.png b/docs/project/chapter5/images/image9.png new file mode 100644 index 0000000..9fecc9a Binary files /dev/null and b/docs/project/chapter5/images/image9.png differ diff --git a/docs/project/chapter6/chapter6-no-code-without-an-idea.md b/docs/project/chapter6/chapter6-no-code-without-an-idea.md new file mode 100644 index 0000000..2e17b33 --- /dev/null +++ b/docs/project/chapter6/chapter6-no-code-without-an-idea.md @@ -0,0 +1,1085 @@ +# Project 6: 别急着写代码,先想一个好点子 + +在前面的课程中,你已经学会了如何在 z.ai 和本地 AI IDE 中,利用大模型 API 搭建各种小工具:从最基础的对话框,到带有简单表单、状态切换、数据存储的应用;你也尝试过用 Trae 这样的 AI IDE 去处理环境配置、依赖安装等“工程向”的问题,让自己真正具备了把一个想法从浏览器搬到本地项目中的能力。 + +换句话说,到目前为止,你已经跨过了“我不会写代码”和“我不知道怎么让程序跑起来”这两道门槛。你可以独立完成一个 Demo 的搭建,也能用现成的组件和模版快速拼装出一个“功能齐全”的小应用。 + +接下来,我们要把关注点从“能不能做出来”,进一步推进到“ **到底做什么,才值得被做出来** ”。你会发现,当写代码不再是你最大的障碍之后,新问题很快会出现:同样都是能跑的应用,为什么有的只活在课程作业里,而有的能吸引真实用户愿意反复回来?同样都是用大模型,有的只是一个换皮聊天框,有的却真的解决了某个具体场景下的真实麻烦? + +本节课里,我们会系统讨论一个问题: **如何从“会搭 AI 工具”,过渡到“会想、会判断、会打磨一个有 sense 的 AI 应用”** 。我们会一起拆解什么叫“点子(idea)”,怎样才算“好点子”,如何判断一个产品方向值不值得你投入时间精力,以及如何用一套可重复的流程,把模糊灵感变成清晰的应用方案。 + +在正式展开之前,可以先用几道小问题自检一下你目前的状态: + +1. 你能否用一句话,把你正在做或想做的应用解释清楚? +2. 你是否知道,这个应用最核心的目标用户是谁、在什么具体场景下会使用它? +3. 如果同类产品已经有很多,你能不能说出你这一个“明显不一样”的地方是什么? +4. 你能不能指出:在你的应用里,AI 具体替代了哪一步人工、带来了哪些“明显变好”的地方? +5. 当有人第一次打开你的产品时,他在 30 秒内能否完成一次“有成就感”的使用体验? + +如果你对这些问题的回答还比较模糊,或者只能含糊其辞地说“就是一个聊天机器人 / 生产力工具 / 学习助手”,那么这一节内容会帮助你把这些想法重新梳理清楚。 + +本节课的主题是: **从工具到应用——训练你的产品点子和应用 sense** 。帮你从会搭工具升级到能做出真正有人用、能创造实际价值的 AI 应用。 +我们希望你在学完这一节后,不只会把 API 拼在一起,更能清楚地知道: **我在为谁做一个什么样的东西,为什么值得做,我该怎么做** 。 + +## 你将学到以下内容 + +总的来说,你将学会做一个应用的基本知识:点子从哪儿来 → 点子如何变应用 → 应用怎么从能用变好用 → 应用怎么用 AI → 完成后怎么找到用户。 + +1. 我要做一个应用,从哪来的点子是靠谱的? +2. 有了点子,怎样拆成可以做出来的应用? +3. 做出来后,怎样判断和打磨成“好应用”? +4. 在哪一步、怎么合理地用 AI 放大价值? +5. 有了应用,怎样从 0 找到第一批真实用户? +# 6. 我要做一个应用,从哪来的点子是靠谱的 + +很多人一提到做应用,脑子里的第一反应就是:我要先想一个足够有记忆点的创意。于是每天刷榜单、看报道、研究各种热门产品,盯着别人的成功故事,希望哪天自己也能碰到一个特别不一样的点子。 + +但真实的情况是,很多人其实压根没有什么想法,只是单纯因为没有想法而焦虑;也有一些人一上来就给自己设了个很高的门槛:不够有趣就不开始,觉得普通就等于失败。可当你真的往前走一段路就会发现,能走得久、走得稳的应用,大多不是在某个深夜拍脑袋想出来的,而是在一个个具体的生活场景里,围绕真实的问题,一点一点长出来的。 + +所以,本章想解决的是一个起点问题:**怎么才能有一个点子?这个点子到底靠不靠谱?它值不值得你接下来投入时间和精力,把它变成一个真实的应用?** + +## 1.1 什么是点子 + +我们先从一个最基础、但又经常被忽略的问题开始: 到底什么才算一个点子。 + +日常对话里,人们常说的点子,往往是一种非常主观的兴奋感。你可能在路上刷到一条视频,瞬间觉得这个方向好酷,于是心里冒出一句话: 我也可以做一个类似的。或者在聚会聊天时,大家一起吐槽某个产品不好用,你随口补上一句: 要是能有个东西,自动帮我把这些都搞定就好了。这个时候,你确实有了一种朦胧的念头,但它离一个可以做出来的东西,其实还差得很远。 + +在这里,我们先给自己设一个稍微严谨一点的标准。只有当一个想法至少满足下面几件事时,我们才把它叫作点子: + +第一, **它必须面向一类明确的用户** 。不是泛泛地说所有人,而是能说清楚,这主要是给谁用的。是大学生、职场新人、带娃的家长,还是独立开发者、电商商家、小微企业老板。不同的人在同一件事情上的在意点完全不一样,如果你连人群都没定下来,那接下来所有的判断都会飘在空中。 + +第二, **它要扎在一个具体的场景里** 。这个应用是给用户在什么时候用的,是在早上通勤的地铁上,是工作间隙,是睡前,是周末整理资料的时候。哪怕是看起来很抽象的工具,比如笔记、任务管理,只要你认真去观察,真正被高频使用的那部分,一定是和某些场景绑得非常紧。 + +第三, **它需要帮助用户完成一个清楚的任务** 。任务不一定很大,但要说得出来。比如整理一天的待办事项,把一篇长文浓缩成几个要点,为一次会议生成一份结构清晰的纪要,或者为一个城市周末出行生成一条可行的路线。越能把任务说具体,你后面设计功能、评估价值就越容易。 + +第四, **它给出了一种比现状更好的做法或者工具** 。用户原本是怎么完成这件事的,是靠脑子记、纸笔记、Excel、截屏收藏,还是在不同应用之间来回切换。如果你能提供的是一种明显更省事、更稳定、更愉快的方式,那么这个点子才真正开始具备价值。 + +![](images/image1.png) + +对于上述的思考,如果你想不清楚也没关系,现在是人工智能的时代,你可以把上面的内容整理成一个完整的提示词,再把你的想法、目标用户和使用场景一并写进去,交给大模型来帮你补全和提炼。把模型当成随时在线的产品合伙人,反复对话、追问、修改,就能把一个模糊的概念变具体。 + +## 1.2 点子和用户需求: 避免自嗨的第一道防线 + +很多人第一次做应用,最容易陷进去的坑就是自嗨。所谓自嗨,就是你对自己的创意兴奋得不得了,觉得这是一个颠覆世界的方向,但当你把它讲给普通用户听,对方的反应往往很冷静,甚至有些不知所措,只能礼貌地点点头,说一句听起来挺不错的。然而产品发布之后,他们既不会下载,更不会长期使用。 + +要避免这种情况,就必须把点子和用户需求这两件事分开来看。 + +我们先来谈什么是 **用户需求** 。可以用一句相对简单的话来概括: 在一个具体的场景下, **用户为了达成某个目标,希望降低的各种成本,或者增加的各种价值。** 这里的成本,不只是金钱,还包括时间、精力、心智负担、犯错风险,甚至是社交压力。比如一个刚入职场的新人,可能愿意花钱买一套模板,只为了在第一次汇报时不那么紧张;一个带孩子的家长,可能愿意多付一点费,只要能保证每天有半小时属于自己。 + +理解这一点之后,你会发现, **单纯的炫酷并不能构成需求。** 很多创意确实足够新奇,但如果它没有让用户在某个具体目标上更省力、更安心、更有信心,那它就很难撑起一个真正可持续的应用。 + +点子和需求之间,有一条经常被忽视的鸿沟。 **点子代表的是你的主观判断而不是数据支撑** ,你觉得什么好玩、什么有趣、什么看起来很前卫。需求代表的是用户实际在经历什么、在为哪些事情发愁。你可能觉得一个自动生成诗歌的功能非常酷,但对于大多数用户来说,能让自己每天少花十分钟做重复整理工作的工具,可能更有吸引力。除非,你像乔布斯或具有非常好的设计审美水平,让大家觉得“自动生成诗歌的功能”都非常酷,自发的想要跟随你,但这具有一定难度。 + +在判断一个想法的时候,有个简单的区分方法,就是看它更像 **真需求还是假需求** 。真需求的一个明显特征,是哪怕现在没有你的应用,用户也在主动想办法解决这个问题。哪怕现有的做法很笨拙,他们依然愿意花时间、花精力、甚至花钱去填这个坑。比如有人会自己写方案,写脚本,只为给自己减轻一点重复劳动。这类场景里,如果你能提供一个更友好、更普适的解决方案,往往就有机会站得住脚。 + +假需求的典型情况恰恰相反。如果不是你主动提起,大部分人并不会意识到那是一个问题,甚至不会觉得非解决不可。你描绘的使用场景更多存在于你的想象里,而不是用户的日常生活中。他们听完你的介绍,只会觉得这东西是好的,挺有意思,但不会付费,甚至转身就忘了。这样的点子,用来写故事还可以,用来做产品就非常危险。 + +![](images/image2.png) + +所以, **避免自嗨的第一道防线是了解用户需求。** 在一开始你就需要逼自己回答一个看似简单,却非常关键的问题: 除了我自己,还有谁在为这件事认真犯愁。你可以去看论坛、社群、评论区,也可以直接问几个身边可能成为用户的人。如果你很难听到类似于“我每次都被这个事情拖住”或者“现在的做法实在太麻烦”这类带着真实情绪的抱怨,那说明这个点子离真实需求还有一段距离。 + +## 1.3 好点子为什么是好点子 + +并不是所有点子都有同样的命运。有些点子,哪怕你只花几天时间,做出一个粗糙但能跑通流程的版本,也会很自然地吸引一小撮真实用户,他们愿意留下来,愿意耐心给你提意见。还有一些点子,即使你拼命堆功能、花钱打广告、在各个平台上做了很多宣传,最终也只能靠外力短暂堆出一波数据,过不了多久就归于沉寂。 + +这背后最本质的差异,是点子本身有没有踩在某个关键的问题点上。 + + **一个好的点子,自然而然能迎来增长** :即便以非常简陋的形态出现,甚至只有简陋的几个按钮,只要能解决用户手头一个具体的小麻烦,就能够获得一定程度的自然增长。比如一个能帮人快速把语音转成文字的小工具,一开始可能只是一个网页加几个简单的按钮,但只要识别质量够好,功能的转化特别自然,很多人就愿意把链接转给身边朋友,因为这简直就是在为他们节省时间。 + + **一个坏点子,往往从一开始就注定了要靠外力驱动** 。就算你的外观特别好,内核显示的特别高端,你需要不停地推、不停地吆喝、不停地解释,但一旦你的拉人行动放缓,使用数据就会直线下滑。你不断往里面砸资源、拉合作、搞活动,但永远感觉在逆水行舟。问题不在于你执行得不够好,而在于那个点本身并没有打中足够真实的痛点。 + +当然,以上情况并不绝对,例如在早期市场用户可能并未意识到价值具有一定滞后性,例如在有竞品的情况下我们还要考虑到外观、操作难易度、品牌特性等等,但这都是更深入的内容,目前暂不考虑。 + +所以,当我们讨论要不要在一个点子上继续投入时,真正该关注的不是创意本身有多炫,而是它能不能自然地生长出一条从问题到解决方案的路径。我们做点子,不只是为了向别人证明自己有多有创意,而是为了找到一个有价值的起点,沿着这条路,我们可以慢慢把一个小工具打磨成一个真正好用的应用。 + +选择比努力重要。 + +## 1.4 好点子从哪里来: 四大来源与具体例子 + +很多人一提到想点子,脑海里浮现的画面是一个人闷在书桌前,盯着天花板,指望有一天灵感突然掉下来砸到自己。现实中的好点子,却大多不是这么来的。它们更多是从生活里的小观察、社群里的反复提问、网络上成堆的抱怨,以及那些已经存在的产品里一点点被筛出来的。 + +下面这四种来源,如果你愿意认真去做,很容易在其中挖到可以起步的方向。 + +![](images/image3.png) + +### 热爱你自己的生活 + +一个非常朴素但有效的原则是: **你在生活里越有参与感,越容易发现问题,也越有能力判断什么是值得解决的问题** 。所谓参与感,就是你不是隔着屏幕看别人过日子,而是自己亲自去体验、尝试、踩坑。你越认真对待自己的兴趣爱好,它就越有可能成为点子生长的沃土。 + +比如说,如果你特别喜欢养猫,你自己跟猫一起生活的一天,往往比刷一百条“养猫小技巧”更有信息量。你会知道猫最容易在哪些地方打翻东西,会记得每天什么时间它最爱蹦跶、在哪些情况下最容易应激,也会亲身经历清理猫砂、铲毛、剪指甲、看病这些细节。 **每一次略微不顺畅的体验,其实都是一次潜在的产品线索** 。 + +像你给猫拍照这件事:很多人都遇到过,自己在那儿举着手机,猫却死活不看镜头,要么低头舔爪子,要么盯着别的角落。那能不能有一个小工具,让手机或平板的屏幕上出现一个会自动移动的红点、羽毛或者小虫子的动画,专门吸引猫咪的视线?你按下拍照键时,它自动在前置摄像头附近晃一圈,把猫的目光“骗”到镜头方向,顺手再连拍几张,帮你从中挑出清晰又好看的那一张。再往前想一步,这个应用还能记录每只猫对哪种颜色、哪种移动轨迹最有兴趣,下次自动用它“专属”的逗猫模式,提高成功率。 + +![](images/image4.png) + +如果你很享受化妆或者护肤的过程,家里柜子上的每一瓶产品背后,都是大量试错和决策的结果。你可能已经习惯用手机相册拍下每次妆容的照片,但每次回顾时,总要一点点回想那天用了哪一支口红、哪一盘眼影。那是否可以把这些信息系统地记录下来,做成一份属于自己的妆容图鉴?甚至可以让应用帮你统计,某种妆容在什么场合被你用得最多,哪些搭配在照片里表现最好,这样每次选妆的时候就不用从零开始想。 + +再具体一点,比如很多人都有这样的场景:早上时间很赶,翻开相册想找“上次那次很成功的通勤妆”,结果翻了半天也想不起来当时到底用了哪几样产品。那能不能有一个小功能,让你在拍完妆容照片时,只要对着手机随口说一句:“今天是面试妆,用了01号橘棕眼影盘和豆沙色口红”,应用就自动识别并生成一条“妆容配方”,和照片绑定在一起?下次你只要搜索“面试”“橘棕眼影”“豆沙”,就能一键看到所有相关妆容,甚至还能自动生成一个“今天只看适合通勤的、五分钟能完成的妆”的推荐列表。你每天早上节省下来的那几分钟,其实就是一个非常具体的“被解决的问题”。 + +如果你喜欢 city walk 或者各类形式的慢旅行,你可能已经用各种工具拼凑自己的体验:地图软件记录路线,备忘录列出要去的咖啡馆,相册里散落着沿途的照片和感悟。那么有没有可能有这样一个应用,能把路线、打卡点、照片、文字,一同结成一个有时间线、有故事性的步行日志?甚至进一步,把你的路线一键分享给朋友,让他们也能在同一个城市,走出不一样的版本。 + +也可以再往下挖一个更日常的小细节:很多人在 city walk 的时候,会有“当下觉得这个转角好美,但回家之后在地图上完全找不回那个点”的挫败感。那能不能做一个超轻量的功能:你走到一个觉得有感觉的路口,只要按住耳机上的按键,说一句“打个标记,这里是很适合约会散步的路”,应用就瞬间在你当前位置落一个带语音的标记点,自动记录时间、天气和噪音水平。以后你或者你的朋友,只要打开这个城市的地图,就能看到这些“路人实测的氛围点”:哪里适合一个人走神,哪里适合看夜景,哪里适合和朋友边走边聊天。那些原本会被你“走过就忘”的小路口,就这样慢慢长成了一个有质感的城市体验数据库。 + +这些例子想说明的其实只有一件事: **你需要热爱你的生活,生活就是你最好的点子来源** 。每天遇到的困惑、临时想出的变通办法、那些你觉得有点麻烦但一直习惯忍着的地方,只要你愿意稍微多看一眼,多问一句有没有可能用一个小工具来改一改,它们就都有可能变成未来的产品雏形。 + +### 从你拥有的人群资产中挖掘 + +所谓人群资产,简单说就是你已经可以触达的一群人。可能是你的读者,你运营的社群,你所在公司的内部同事群,也可能是你长期参与的某个兴趣社区。只要你有渠道, **能稳定听到一部分人日常在聊什么、烦什么、期待什么** ,那你就比完全从零开始的人,多了一大截优势。 + +举个很常见的例子。如果你是一个设计师社群的组织者,你每天在群里能看到的内容,其实就是一份极其珍贵的需求池。有人抱怨客户总是反复改稿,有人对某类素材网站收费方式不满,有人觉得在不同尺寸规格之间来回调整太浪费时间。每一个抱怨背后,都藏着一条潜在的产品线索。比如,你可以做一个简单的尺寸适配工具,把一套设计一键生成为各个常见平台的尺寸比例;或者做一个可以保存和复用常用组件的小工具,帮设计师用更少的时间完成重复劳动。 + +如果你所在的是一个备考类的社群,群里可能长期充斥着类似的话题: 今天状态不好,计划又拖延了,该看什么资料更高效,怎么才能坚持打卡。你不需要凭空想象,只需要观察一段时间,整理出大家反复提到的几个共同难题,就能大致勾勒出一款学习类应用初步的功能方向: 比如更合理的目标拆解,更人性化的打卡反馈,更真实的进度可视化。 + +在这些场景下,你不必试图一开始就做面向所有人的大而全产品。你只需要承认一点: 你手头这一小圈人,就是你最好的起点。你对他们的理解越深,越知道他们真实生活里那些说得出口和说不出口的小烦恼,你就越有机会做出真正被使用的东西。 + +### 从公开场域中挖掘需求 + +即便你暂时没有任何自己的社群或者读者群,也完全不用担心。互联网上每天都有无数人在各种平台大声讲述自己的困难和不满。公开场域里的这些声音,本身就是极大的宝库,只是大多数人从来没有认真去听。 + +你可以选定几个与你感兴趣行业相关的平台,定期搜索一些带情绪色彩的关键字。例如, **好烦、有没有推荐、怎么解决、真的很麻烦、有没有更好的办法。** 然后耐心翻看那些帖子和评论,重点留意两类信息。 + +一类是某种问题被长期、反复提到。比如在求职板块里,每隔一段时间就有人来问简历怎么写、自我介绍怎么准备、如何跟进面试结果;在宝妈群体中,总是反复出现辅食搭配、作息调整、亲子沟通之类的困惑;在小微商家的交流社区里,大家可能永远在担心库存管理、现金流、员工排班。这些长期存在的反复问题,就是一个行业反复暴露出来的系统性痛点。 + +另一类是某些场景下,用户在用非常笨拙的方式硬撑。比如有人把所有待办事项写在纸上,再拍照上传到云端;有人在不同应用之间来回复制粘贴,只是为了把一段内容从一个格式转换成另一个格式;有人会自己手动把不同渠道的数据集中整理成一张表。这些地方,只要你用心观察,就会发现很多可以被流程化、工具化的小切口。 + +在公开场域里挖需求,其实是在训练一种能力: 让自己从一个旁观者变成一个捕捉者。当你习惯性地去搜这些关键词,习惯性地把案例记下来,你的大脑就会慢慢积累一套对现实问题的敏感度,这种敏感度会在你后面的产品设计过程中,一次又一次帮到你。 + +### 站在巨人的肩膀上 + +还有一类经常被忽略的点子来源,是现有的产品和项目。这个世界上已经有太多厉害的人,替我们走过了许多探索的路径。你不必每一次都从一张白纸开始,完全可以站在别人已经做到一半的地方,往前再走一小步。 + +**黑客松活动、产品创新大赛、创业 Demo Day **之类的场合,往往会涌现大量有趣的小作品。它们大多有两个特点: 时间紧张,资源有限。这恰好和你现在想做的小应用很像。所以,当你去看这些得奖作品时,不妨多问两个问题: 如果这个东西只服务于某个更窄的细分人群,会不会更容易落地。如果把它的功能砍掉一半甚至三分之二,只保留最核心的那一环,会不会反而更清晰。 + +同样地,**产品榜单、开源项目、工具集合网站**上列出的那些工具,也都可以成为你思考的起点。你可以挑一些自己感兴趣的,逐个拆解: 它是帮什么人解决什么事,它现在的形态还有哪些明显的缺口,如果迁移到另一个场景或者另一个国家,会长出什么区别。你并不是要抄袭,而是通过这种拆解练习,训练自己对问题和解决方案之间关系的理解。 + +线下的世界也是如此。每当你在医院挂号排队、在餐厅等号、在政务大厅填写同样的信息多次、在纸质表单上反复写相同内容时,都可以刻意停下来,问一下自己: 这里有没有可以被 **系统化、数字化、自动化的空间** 。那些看起来杂乱、重复、低效的场景,本质上就是未来一些工具生长的土壤。 + +长期坚持从这四条路径里挖素材,你会点子不是某种突然出现在脑海里的奇迹,而是你和生活、和他人、和信息世界长期互动之后自然长出来的一种副产品。 + +## 1.5 如何用一句话概括好点子: 少即是多的艺术 + +当你大致知道一个点子从哪里来之后,下一个重要的练习, **是尝试用一句话把它讲清楚。** 这个练习听起来简单,但实际上挺残酷,因为它会逼迫你面对一个事实: **你的点子究竟有没有抓住一个真正清晰的核心。** + +人之所以能记住另一个人,很少是因为对方面面俱到,更多时候,是因为某个明显的特征。可能是总戴着某种帽子,可能是说话风格特别稳,也可能是每次讨论时总能抛出关键一句话。产品也一样。**与其让别人勉强记住你十几个功能,不如让他对你形成一个朴素但清楚的印象。** + +在写这一句话的时候,一个常见的误区是过度宽泛。比如说: 这是一个帮助用户提高英语水平的应用。乍一看没有错,但再往里追问,你会发现这句话几乎什么都没说: 帮助谁,是零基础的学生,还是已经在职场的人;通过什么方式,是背单词、听力训练、口语纠正,还是写作批改;需要付出多少时间,能够带来多大的改变。所有关键信息都被稀释掉了。 + +相对好一点的表述会具体很多。比如:“每天利用十分钟通勤时间,一个月记住一百个核心单词的背词应用”。这里至少说明了三件事: 使用成本是可控的,每天只需要十分钟;预期结果是可见的,一个月有一百个新单词;场景是明确的,主要发生在通勤而不是其他碎片时间。用户听到这样的描述,能很快在脑中判断这东西对自己有没有用。 + +练习写这一句话的过程,其实是在反复逼自己回答三个问题: **你到底在帮谁,你希望他们在什么样的场景下想起你,你打算在多长时间内帮他们达成一个怎样的结果。** 只有当你愿意把这些信息拼到一起,哪怕牺牲掉一些华丽词藻,你的点子才真正变得可以被理解和传播。 + +你也可以反过来把这个训练用在自己身上。试着给自己的未来三年写一句话描述。比如,我希望三年后,可以用一两句话说明自己主要在为哪一类人,解决哪一类问题,并且已经做出了哪些可见的成果。这样的训练会让你在做选择时更清楚,哪些事情是必须紧紧抓住的,哪些则可以适当放掉,学会舍弃比学会增加要难而正确。 + +如果不知道从哪里学习这种表达,很简单,去看那些每天都在为争夺用户注意力而打磨文案的内容。你可以参考**应用市场里的一句话简介,游戏和工具类产品在官网首页摆出的主标题,各类 ****Landing Page** ** 上的核心文案** 。可以把它们抄下来,拆成结构,尝试基于 AI为自己的点子写一版新的文案。 + +## 1.6 用 AI 发散思维并找到差异化 + +过去想点子,大多时候只能靠人自己慢慢琢磨。现在有了 AI,你等于多了一位随时可以召唤的头脑风暴伙伴。只要用得好,它可以大大扩展你的思路空间。 + +当你卡在某个方向上,觉得脑子里的想法来来回回只有那几个时,不妨把你现有的点子用尽量清晰的方式描述给 AI,然后请它帮你做几件事。比如, **基于同一个核心任务,请它列出二十种不同的用户群体** ,或者让它从学生、自由职业者、带娃家长、小微商家等不同角度,重新描述这个点子可能的使用方式。又或者,请它站在产品经理、运营、市场、技术的角色,分别提出各自关心的点。 + +你会发现,很多你原本不会主动想到的使用场景,会在这一步骤中被甩给你。你的任务不是简单接受这些建议,而是在这些被扩展出来的空间里, **挑出你最有理解力和资源优势的那一小块** 。比如你发现,虽然 AI 列出了很多行业,但你对教育和内容创作类场景格外有感觉,那你就可以优先沿着这两个方向继续往下拆。 + +在这个过程中,还有一个重要原则是: **常见点子并不一定等于无效点子** 。很多新人第一反应是要避开所有看起来常见的方向,觉得凡是别人做过的就没机会了。但真实世界远没那么简单。背单词、待办事项、记账、习惯打卡这些看似常见的方向,之所以不断有人做,是因为背后的问题确实普遍存在。这种情况下,比拼的往往不是有没有完全新的大创意,而是 **谁更理解某一小群人,谁能在细节上做得更贴近他们的生活** 。 + +你可以先列出一批新手最容易想到的点子,如背单词工具、每日打卡应用、读书笔记助手、简历生成器、习惯养成工具等。然后对于每一个,专门和 AI 做一轮拆解,集中问三个问题: 如果我只服务于某个非常具体的人群,比如设计师、律师、新手妈妈、在校研究生,这个点子会长成什么不一样的样子。如果我只针对某个固定场景,比如通勤路上、午休十分钟、晚睡前的半小时,功能和呈现有没有可能做得更聚焦。 **如果我把结果呈现这件事做到极致,比如更易分享、更易打印、更易导入到其他系统,会不会就足以构成差异** 。 + +AI 在这里的价值,并不在于替你做决定,而是在于帮你把本来很窄的一条路,变成一张更完整的地图。你会更快看到哪些区域已经被别人深耕,哪些角落仍然相对空白。而真正要走哪条路,最后始终要回到一个老问题上: 哪些地方是你真正在意、理解够深、愿意长期投入的。 + +在这一切的最后,再把那条底线拿出来强调一次。任何关于点子和创意的讨论,最终都要回到用户需求上。你可以用 AI 辅助思考,可以利用它加速生成变体,但不管做了多少轮头脑风暴,最终那个判断标准始终是: 这个想法是否真正回应了某群人的真实痛点,是否在他们已经在反复尝试解决的问题上,向前迈出了一小步。 + +## 小结 + +你要学会用几个简单的维度,去检视一个点子是不是已经足够清楚;要分清自己觉得酷,和用户真的需要之间的差别;要知道好点子之所以好,是因为它从一开始就踩在某个痛点上;要学会从自己的生活、人群资产、公开信息和现有产品当中持续挖掘线索;要练习用一句话把点子讲清;也要学会把 AI 当作扩展思路的伙伴,而不是替代判断的工具。 + +当你手里已经有了一到三个这样的点子,并且**能用一句话说明**它们各自是给谁用、在哪个场景下用、大致会带来什么样的结果时,你就可以停下继续想新点子的冲动,把注意力转移到下一步: 怎样把其中一个,拆解成一个可以真实做出来、可以被真实用户使用的应用。 + +这个点子有点烂怎么办?没关系,最开始烂才是正确的, **完成永远比完美重要** ,你需要先开始才有结局。 + +## 📚 Assignments + +请你根据上文内容,完成下列作业: + +1. 结合自己的兴趣,使用 AI 帮自己生成几个应用的“点子” +2. 让 AI 根据自己的想法,评价这个是真需求还是伪需求,并且给出用户需求洞察和建议 +3. 从四大来源处选取一或两个来源得到“点子”,或者让 AI 生成几个应用的“点子” +4. 从上述所有 Idea 中,选取三个最喜欢的点子,尝试用一句富含信息量的话概括这个点子。 +# 5. 有了点子,怎样拆成可以做出来的应用 + +上一章我们解决的是一个起点问题: 到底什么样的点子才是值得认真对待的。 + +真正的挑战从这里才刚开始,很多人就是倒在这一步: 头脑里有一套看起来很完整的蓝图,一动手就觉得复杂到无从下手。功能太多,页面太多,技术看起来也很吓人,于是不断拖延,最后变成一句 **自我安慰** : + +“ **没关系,这东西将来有机会再做吧。。。** ” + +![](images/image5.png) + +别想了!要就是现在!这一章我们想做的事情,就是帮你学会一套从点子到可做版本的拆解方法。你会看到,从无到有并不依赖天才,而是依赖一系列可以反复练习的具体动作: ** 发散、** **收敛** **、拆解、细化、借鉴、提问。** 按照这个顺序,哪怕没有团队、没有大把时间,也可以把一个点子变成能跑通的应用demo。 + +## 2.1 从想法到解决方案: 双钻模型发散到收敛 + +当你学会画页面提想法之后,很快会面临另一个常见的问题: 想法开始越来越多。你在白板上写下了各种可能的场景和功能,纸上画满了不同的页面版本,看上去很有成就感,但真正要做时,反而更难下手了。因为每一个看起来都重要,似乎都值得一试。 + +这个时候,就需要用到一套非常经典但又很好懂的思考框架: 双钻模型。这个模型的意思其实很朴素,就是在人生的很多阶段,你都需要先发散,再收敛,而不是一开始就想把所有事情一次性做完。 + +### 什么是双钻模型 + +双钻模型是英国设计委员会提出的一个创新与设计流程框架,把整个过程比喻成连续的两个菱形(“双钻”):第一个钻石是从“发现问题”到“定义清晰问题”,强调先广泛发散、充分调研和理解用户,再收敛梳理出真正要解决的核心问题;第二个钻石是从“发展解决方案”到“交付最终方案”,先对可能的解决思路大胆发散、探索和迭代原型,然后再收敛、筛选和打磨出最优可落地的方案。双钻模型强调在问题和方案两个阶段都要经历“发散—收敛”的过程,避免一开始就跳到解决方案,从而提升创新的质量和成功率。 + +![](images/image6.png) + +![](images/image7.png) + +### 第一钻: 理解问题,从单点到全貌的发散和收敛 + + **在双钻模型里,第一钻是关于问题本身** 。你先从一个模糊认知开始,逐渐发散出更多相关的情况和可能,再做一次收敛,找到真正值得解决的那个问题点。 + +对应到你的应用,就是这样几件事。 + + **发散阶段,你尽量多地去列举用户可能的使用场景,** 可能遇到的阻力,可能希望得到的结果。你不急着判断,只是把脑子里所有相关的东西都先摊出来。比如对于文档处理应用,你可以列出用户可能在通勤时用、在会议前用、在写报告前用、在做复盘时用;可以列出他们怕的是总结不准确、怕格式乱、怕错过重点;可以列出他们希望的是更快弄清楚一篇文章要表达什么,更快找到与自己相关的部分。 + +**收敛** **阶段,你要逼自己只选出那一两种最常见、最痛的情况** 。比如你从一堆场景中发现,最多人提到的,是在接收到很长的工作文档时,希望先搞清楚这篇文档到底想说什么,它的主要结论是什么。那你就可以把第一版的应用目标定为: 帮助用户在五分钟内看懂一篇长文的核心意思,而不是同时解决所有文档处理相关的问题。 + +第一钻结束时,你应该已经比刚开始时更清楚: **你真正要解决的问题是什么,它和其他周边问题相比,优先级为什么更高。** + +### 第二钻: 设计解决方案,从粗糙想法到可执行方案 + + **双钻的第二部分,是关于解决方案的诞生** 。你已经大致知道要解决哪一个问题,接下来要做的是为这个问题尽可能多地想办法,然后再从中筛选出最适合第一个版本的那一种。 + +发散阶段在这里意味着不断追加想法。你可以脑暴各种功能、更细的场景、各种可能的玩法。比如针对长文总结,你可以设想不同的摘要粒度、不同的结果呈现形式、是否支持语音播报、是否允许用户标注重点、是否提供多种风格的总结版本等等。这一步不需要立刻做决策,只是尽可能把可能性列出来。 + +收敛阶段,就要拿出一个简单但非常实用的评估工具: 用户价值 × 可行性 × 时间成本。你可以给每一个想法在这三个维度上打一个粗略的分,比如 1 到 5 分,然后优先选择综合得分高、时间成本可控的想法作为 MVP,也就是最小可行版本的组成部分。 + +比如语音播报功能可能用户价值不错,但技术和前端整合起来的时间成本偏高;而简单的文本摘要和要点提取,用户价值同样明显,可行性也高,时间成本更低,那它就更适合作为第一版里必做的功能。 + +在这个过程中,你要不断提醒自己一件事: **第一版的目标不是做出一个完美的应用,而是做出一个真实存在的、有人可以真正使用的版本** 。它不需要包罗万象,只需要在一个具体任务上表现得足够像样。 + +你可以给第二钻画一个简单的时间边界,比如一个月内要交出一个可用版本,那在发散的所有想法里,所有需要超过一个月甚至几个月才能落地的功能,都可以先暂时放到一个以后再看清单里。这样你不会因为想做的太多,而在一开始就被拖住。 + +当你习惯了用双钻模型来整理自己的思路,很多原本纠缠不清的状况就会变得清爽许多。你知道什么阶段该尽可能地多想一点,什么阶段该果断地砍掉一部分可能。你不再奢望一次性解决所有问题,而是学会在发散和收敛之间来回切换。 + +## 2.2 得到可执行步骤:学会从抽象到具体 + +发散想法后,得到想法十分简单,但得到可执行步骤却非常难。说我要做一个提升效率的工具,我要做一个帮助创作者的应用,听起来都很宏大。真的要动手的时候,抽象几乎帮不上忙。你每天面对的是一个个非常具体的问题: **第一个版本到底要做哪一小块,需要哪些页面** ,要不要支持注册登录,要不要接入支付。 + +这里需要的一种能力叫 **拆解并细化,能够把抽象变具体** 。就是把一个大而泛的目标,一点点拆解并细化内容到可以立刻动手的最小可行动项。这个能力不仅在做产品的时候重要,在生活里也非常关键。 + +![](images/image8.png) + +### 从生活例子开始: 我想吃汉堡到底意味着什么 + +先不谈应用,回到生活中一个很简单的例子: 我想要吃汉堡。乍一看这句话一点也不复杂,但如果你认真拆下去,会发现里面藏着很多具体的分支。 + +首先是 **动机和内心的核心需求** 。你是真的想吃汉堡吗?你只是馋味道,是想快速解决一餐,是想和朋友聚一下,还是只是因为刷到了一张好看的图片。这看似无关紧要,但会直接影响后面的选择。如果是为了和朋友聚,很可能对环境和体验有要求;如果只是赶时间,可能快比好吃更重要。 + +其次是 **动作的范围** 。你想吃什么品类的汉堡?你想在几点吃汉堡?你只想吃汉堡本身,还是希望有一整套搭配,比如饮料、薯条、甜点。如果你晚点还有事,不想吃太撑,那选择可能会不一样。你甚至可以进一步问自己,要不要顺便解决明天的早餐,比如多带一个简单的汉堡回去。 + +再往下就是 **如何实现这件事** ?。汉堡对你来说是必须要去店里吃,还是外卖送过来也可以,甚至你愿不愿意自己动手在家做。每一种选择背后对应的是完全不同的一套行动路线。选择去店里,意味着要查位置、看时间、安排路程;选择外卖,意味着要看平台、比较价格和时间;选择自己做,则意味着要准备食材、工具、找食谱。 + +当你把这一切拆清楚后,原本模糊的我想吃汉堡这句话,就会变成一串具体的行动步骤。比如: 打开外卖应用,搜索某家之前吃过觉得不错的店,选择一个套餐,去掉饮料改成只要汉堡和薯条,设置备注不要酱,最后下单。这些动作都非常细小,却都是可立即执行的,并且这能够被 AI 编程一套程序化可执行的 plan,进行操作。 + +**拆解并细化的意义就在这里: 它帮你从一个听起来很大、很抽象的愿望,走到一个可以具体执行的列表。** + +### 应用例子: 提高文档处理效率到底从哪一步开始 + +我们来看一个更复杂层层递进的例子,假设你有一个看起来挺正当的愿望:“ 我想做一个提高文档处理效率的应用。”这个方向是对的,但如果就停在这半句话上,你几乎无从下手。你既不知道第一步要画什么页面,也不知道第一版需要做到什么程度,更不知道该怎么和别人解释你的想法。 + +这时候你可以借用刚才的拆解细化内容的方式,一步一步具体化它;由于时间关系,此处只演示两层拆解方法。 + +#### 第一层拆解细化 + + **首先,你需要先定义什么是“文档”** 。文档本身是一个很宽的概念,它既可以是表格、 Word 报告、PDF 文件,也可以是记录代码注释的 Markdown 文本、TXT 笔记,甚至是扫描生成的图片式文档、内嵌图表与公式的学术论文。不同类型的文档存在实现差异,但后续设计的 “处理” 功能,必须匹配文档的具体类型,故而不得不细化对文档的定义。如果是图片式文档,可能需要先加入 OCR 文字识别功能;如果是表格类文档,核心需求更可能是数据提取与分析,而非单纯的文字精简。 + + **其次,你还需要定义什么叫做“处理”。处理成什么,才算处理过?** 处理的方式又是什么?有的人所谓的处理,是把一份 50 页的报告精简成 5 页可读的概要;有的人所谓的处理,是把一堆杂乱格式的 Word、PDF、Markdown,统一变成一套规范模板;还有人关心的是翻译、改写、润色,让一篇勉强能看的草稿,变成可以对外发布的正式版本。这一步你可以直接问自己: 我说的“处理”,到底是要“看得更快”、“改得更好”,还是“传给别人更方便”。不同的答案,直接决定你后面要画的入口页和操作页会完全不一样。 + + **对于“应用”同样也需要定义。什么叫做应用** ?是一个只给自己用的小工具,还是希望未来有一群用户来使用?是一个网页程序,还是一个手机 App,还是只是嵌在现有系统里的一个小功能?如果你只是想在电脑上自己用,做成一个简陋的网页或者命令行脚本,成本会低很多;如果你打算给团队同事一起用,可能就要考虑账号体系、权限、协作入口。这些听起来像是技术选型的问题,但在拆解阶段,你只需要回答一句很朴素的话: 我打算在什么设备、什么场景下,用到这个东西。 + +接下来, **回到这句话本身: “提高文档处理效率”。** 你还需要拆清楚几个关键字。比如 **“用什么提高”** ?一定要用 AI 吗?还是不一定?有些效率提升完全可以用规则、模板、快捷键来解决,比如一键生成固定格式的报告封面、一键插入标准免责声明。这类需求可能根本不需要模型参与。相反,如果你面对的是大量非结构化的长文本,需要理解、概括、改写,那么 AI 可能就是非常自然的一环。 + +“效率”这个词也值得单独拆开。 **效率到底是什么意思?是单纯指速度,还是既包括速度,也包括质量,还包括出错率和理解难度?** 比如,把一份 20 页的文档从 30 分钟看完,变成 5 分钟扫完要点,这是速度;让用户在摘要里快速发现错误逻辑、数据矛盾,这是质量;让一个原本不熟悉专业术语的人,也能通过解释和标注看懂报告,这是认知门槛的降低。你可以很直接地问自己一句: 如果这个应用做得非常成功,对用户来说,最大的变化是什么?是“花在文档上的时间少了一半”,还是“做文档相关的事时,心没那么累了”?回答清楚这句,你的功能优先级就有了依据。 + +![](images/image9.png) + +#### 第二层拆解细化 + +以上是第一层拆解,假设在这个阶段,我们能得到的初步拆解细化结果是:“我想做一个 AI 提高 PDF 文档转成文字速度和质量的网页程序”。这一句相较于最初的“提高文档处理效率”已经具体得多:它明确了文档类型(PDF)、处理方式(转成文字)、优化方向(速度和质量)、技术路径(AI),以及承载形态(网页程序)。从需求表达的角度看,它已经从一个抽象的愿望,收缩成了一个相对清晰的功能构想。 + +但需要注意的是,这样的描述仍然只是一个“中间目标”,还称不上真正可执行的产品方案。原因在于: **其中很多关键信息依然是笼统的,比如“用什么 AI”“提升到什么程度”“适配哪些使用场景”“面向什么样的用户”等。** 因此,我们还可以、也有必要, **继续向下拆解,把这句话变成一组更细颗粒度的设计决策和技术方案** 。 + +先来看其中的“AI”。这里的“AI”,究竟是指一个只负责文字识别的轻量级 OCR 模型,还是需要引入大语言模型,甚至多模态模型,来做后续的纠错、版面重整、内容重排和结构理解?不同的选择,会在三个维度带来完全不同的后果: + +* 成本消耗:包括算力成本、调用费用、推理时延等,是一次性投入为主,还是持续开销为主。 +* 开发难度:是简单集成现有 OCR 接口即可,还是要设计复杂的 Prompt、上下文管理、甚至自训练与评估体系。 +* 产品形态与上线策略:是做成一个“快速提取文本的小工具”,还是一个能够还原大纲结构、表格、标题层级,适合深度阅读与内容再利用的“文档智能处理平台”。 + +然后是 **对“PDF 文档”的进一步拆解。你到底要支持哪一类 PDF?** 如果把范围限定在“以文字为主、可以复制的纯文字 PDF”,那就不必一开始就处理扫描件、复杂图表、公式排版,也不用为极端多栏、花哨排版的文档负责。反过来,如果你希望做到“任何 PDF 都能扔进来”,就意味着一上来就要同时解决图片式 PDF 的 OCR 识别、版面重建、图文混排、表格抽取等一整串高难度问题,项目复杂度会成倍提升。 + +在这一层,你可以刻意做一次“收窄”,并把取舍明确写下来。例如:当前版本主要服务“结构较清晰、以文字为主的 PDF 报告和说明文档”,不对扫描件、重度图文混排文档的效果做保证。这样一来,后续所有关于“速度”和“质量”的目标,都有了一个相对可控、可解释的前提条件,也方便在产品说明和用户预期管理中说清楚边界。 + +接下来是“高质量转成文字”。“质量”在这里至少可以拆成三个可讨论、可权衡的维度: + +1. **识别是否大致正确** :错别字、标点、特殊符号的识别准确率如何,是否会出现整段乱码。 +2. **段落与标题结构是否保留** :原文的章节层级、段落分隔、列表结构、引用块等,在转成纯文本后能否被尽可能还原。 +3. **是否便于二次编辑与再利用** :生成的文本是否足够干净、格式是否规整、用户后续复制到 Word、Notion 或代码编辑器中时,是否需要大规模手工清理。 + + **你可以先选出自己最在意的两三项,作为“质量”的主攻方向** 。比如:优先保证“段落结构清晰”和“标题层级基本保留”,在错别字上只要求达到“用户几分钟内可以快速人工修完”的程度。这样,“高质量”就不再是一个空泛的形容词,而可以被转化为写得出来、量得出来的产品标准:偶尔有识别错误是被允许的,但不可以把文档切得支离破碎、段落混乱,更不能让用户在结构整理上比手动复制还费劲。 + +再看“速度”。既然你在目标里写了“提高……速度和质量”,那“快”就应该被具体到 **某个可以感知的量级,** 而不是停留在“感觉上比较快”。这里其实隐藏着一个重要取舍: + +* 是希望支持超长文档(几十页、上百页),哪怕用户需要等待较长时间? +* 还是只针对中短篇文档,在页数受限的前提下,做到“几秒到十几秒内拿到结果”的体验? + +如果你典型的使用场景是:会前把一份十几页的报告、方案或研究摘要,快速转成可编辑文本以便标注、修改和摘录,那么更自然的选择是: + +* 对单份文档设定一个合理的页数上限,例如“不超过 20 页的文字型 PDF”; +* 同时给出一个大致的处理时间指标,例如“通常在约 10 秒内完成处理”。 + +这两项一旦被明确写出来,后面的技术方案(是否需要并行处理、要不要做异步队列)、界面文案(页面上展示的预计时间、超时提示)以及用户预期管理,就都可以围绕“中短文档 + 快速返回”这个核心体验来优化。 + + **最后是“网页程序”本身。这一项看似只是载体选择,实际上同样需要适度收口,** 避免过早卷入过重的产品形态。你可以先问自己一个关键问题: + +* 这更像是“我自己和小范围内部使用的临时工具”? +* 还是一开始就规划成“给一批真实用户长期使用的在线服务”? + +如果更偏向前者,你就可以大胆砍掉很多复杂度:不用搭建完整的账号体系和权限管理,不必在第一版就实现任务历史、项目管理、团队协作等功能,而是专注在一个极简流程上: + **打开网页 → 上传 PDF → 等待处理 → 展示可编辑文本 → 一键复制或下载** 。 +反之,如果目标是正式对外提供稳定服务,就需要在后续版本中逐步考虑并发能力、队列调度、用户配额、异常恢复、日志与监控、安全与权限管理等。但在当前这一拆解阶段,你完全可以先把它定义为“基于浏览器的小工具,无需登录即可使用”,把所有交互集中在最简单、最核心的那条路径上。 + +你需要把“AI”“PDF 文档”“高质量转成文字”“速度要求”“网页程序”这些 **关键词背后的取舍,用更具体文字的明确表达** ,最初那句“我想做一个 AI 提高 PDF 文档转成文字速度和质量的网页程序”就可以被进一步收紧为一条更清晰、更可执行的描述。例如: + +> 为用户提供一个基于浏览器的小工具,优先支持结构较清晰、以文字为主的 PDF 报告,通过适配的解析流程与轻量级 AI 清洗,在约 10 秒内输出一份段落结构明晰、标题层级基本保留、识别错误率可接受的可编辑文本,无需登录即可使用。 + +到这个时候,你就已经完成了一次从抽象目标到可落地方案的重要跨越,稍作精简后可得到一句话描述: + +> 为用户提供一个网页工具,让他上传一份不超过 20 页的文字型 PDF,在约 10 秒内得到一份段落结构清晰、标题层级保留的可编辑文本,并支持一键复制和下载为 `.txt`。 + +这类描述不再是空泛的口号,而是可以直接变成提示词,或者直接让 AI 当做 plan 去执行的一组指令。比如,你完全可以把这段话丢给一个具备开发能力的 AI,让它按这句话去生成一个开发方案或直接生成最小可用版本的网页应用;也可以把它交给设计师,让他据此画出具体的界面原型;或者发给一个工程师同事,让他快速评估实现成本和技术方案。 + +![](images/image10.png) + +当你做到这里,你会发现两件很现实的变化。第一,你不再被“我要做一个提升效率的应用”这句话压住,而是拥有能够立即动手的步骤。第二,和别人的沟通成本会急剧降低,因为你拿出了一套已经拆到足够具体的初版方案。 + +从抽象到具体,其实就是把“我想做一个提高文档处理效率的应用”这样的大愿望,拆成一组任何人甚至任何 AI 都可以立刻理解并开始执行的任务清单。通过这个方式不会有难解决的问题,所有的问题分解到原子化后无非就只有两个选项,只要能原子化就能被执行: + +1. 我来解决、执行这个子问题。 +2. AI 或别的专家,来执行解决这个子问题。 + +## 2.3 在白板上构思你的应用: 先画出第一个应用 + +很多人一想到要开始做应用,脑子里第一个跳出来的是代码、后端、数据库、接口、框架。这并不奇怪,因为我们长期被灌输的观念是: 做一个应用,首要是技术问题。但如果你一开始就把注意力全部压到技术上,很容易忽略最关键的东西: **用户到底在你这里究竟要做什么** 。 + +在这一点上,一个最简单、却经常被忽略的做法,就是先画。你不需要什么专业的软件,一个白板,一张空白纸,一个记事本都可以。重要的是,你先把用户从进来到离开的整条路径,用几张简单的页面草图画出来,而不是直接跑去打开编辑器写代码。 + +你可以把整个应用,先分成三类页面: 入口页、操作页、结果页。 + +![](images/image11.png) + +### 入口页: 用户从哪儿进来,第一眼看到什么 + +入口页就是用户和你的应用第一次打照面的地方。很多人一开始设计入口时,只想到一个通用的首页,上面堆满了功能按钮、模块入口、广告位,似乎只有这样才显得东西够多、够厉害。但如果你把这个页面画在纸上,贴在墙上,再假装自己是一个第一次来的人,你会突然意识到一个很现实的问题:** 我到底该先点哪里。** + +画入口页时,可以先把自己当成导游。问几个非常具体的问题: 用户通过什么方式进来,是点击一个分享链接,是在应用市场里搜索,是在一个网页上扫描二维码。不同来源意味着用户对你的预期完全不同。比如一个通过朋友转发链接进来的用户,他已经大致知道你能做什么,这时候入口页可以更直接一点,让他马上试用核心功能;而一个从应用市场搜到你的人,可能对你一无所知,此时 **入口页就需要用一句话先帮他搞清楚你是干嘛的,或者一看会用** 。 + +画的时候,可以这样简单处理: 在纸上画一个手机屏幕的框,在最上方写上这一页的标题,中间画出主要区域。标注清楚: 这一页我要告诉用户什么,我希望他在这里做出什么选择。比如是让他点击一个大大的开始按钮,还是让他先看一个简短的示例结果,或者是填写一个最简单的基础信息。 + +开始页越简单而具体,你就越有机会让刚来的用户不迷路,快速上手。 + +### 操作页: 用户需要输入、点击、选择什么 + +一旦用户决定继续往前走,下一步就会落到操作页,也就是整个应用的工作区域。这里是用户真正和你发生互动的地方,也是最多人设计过度复杂的地方。 + +画操作页时,一个很有效的练习是: **只允许用户做一件事** 。你可以在纸上写下这件事的最简单表达,比如 提交一段文字 用语音记录一条想法 选择一个模板 配置一个参数。然后围绕这件事,尽量往少里做,看看最少只需要哪些输入,哪些按钮。 + +以一个长文自动总结的应用为例,最粗糙但能跑通流程的操作页,可能只需要几样东西: 一个可以粘贴文字的输入框,一个选择总结长度的选项,一个生成摘要的按钮。你完全可以先不考虑字体大小、配色、图标这些视觉上的细腻部分,把重点放在这样几个问题上: **用户是否一进到这一页就知道要做什么,他需要准备哪些东西,他会不会在中途搞不清楚下一步。** + +在纸上构思操作页的好处,是你可以非常低成本地尝试不同版本。你可以先画一个所有输入都在同一页的版本,再画一个分成两步的小向导版本,然后在脑海里演练几遍: 哪个版本更不容易让人卡住。相比在已写好的代码里改流程,这种纸上调整几乎没有成本。 + +### 结果页: 用户得到了什么,怎么展示 + +很多应用在结果这一步做得很敷衍。开发者往往觉得结果不就是一段文字、一张图、一串数据嘛,展示出来就好了。可对用户来说,往往恰恰相反: 他之所以愿意在前面的步骤里输入、等待、尝试,根本原因是他期待在结果页上看到一个够清楚、够有用的东西。 + +画结果页时,可以从这样几个角度去想: **用户最关心的核心信息是什么,它应该摆在最显眼的位置** 。有哪些结果是需要导出、保存或者分享的,它们的入口在哪里。有没有必要为结果加上一些简单的解释,让用户知道这代表什么。 + +还是以长文总结为例,一种比较友好的结果页设计是: 顶部用几条简洁的要点列出核心结论,其下面放一个更详细的摘要,最底部保留原文链接。旁边放上两个醒目的按钮: 一个是复制要点,一个是导出为文档。你可以在纸上试着画出这些区域的布局,并标注一下每个按钮预计承载的动作。 + +当入口页、操作页、结果页都画完后,你再用箭头把它们连起来,**从用户第一次进来,一步步走到结束。这个过程会暴露出很多原本你没意识到的问题:** 比如用户在结果页想要修改一个细节,他该如何返回操作页;或者在操作页上,他如果暂时不确定要不要继续,是否有清晰的退出或者保存草稿的方式。 + +整个章节的核心只有一句话: 先把用户操作过程画出来,再考虑技术实现。你可以完全不会写代码,却依然可以 **通过几张简单的草图,把一个点子变成一个看得见的应用雏形** 。这一步做得越清楚,后面无论是自己实现,还是和别人合作实现,都会轻松很多。 + +## 2.4 参考别人的应用: 聪明地抄作业 + +很多人在做第一个应用时,会有一种心理负担: 觉得自己好像必须从零开始,页面结构、交互方式、视觉布局都要完全原创,仿佛只有这样才算真正做产品。现实是,如果你坚持这个原则,反而会在无关紧要的地方耗掉大量精力。 + +在应用设计这件事上,有一种更高效也更成熟的态度叫 **聪明地抄作业** 。不是简单模仿,而是有选择地借用别人已经验证过的好解法,把你的精力留给最应该用在你独特价值上的地方。 + +互联网上有很多收集应用界面截图的网站,也有大量应用市场里的详情页,这些地方本身就像一本巨大的参考图册。你可以挑出几个和你的方向相近的应用,比如同类工具、相同人群的产品,然后像研究样本一样一页一页地看。 + +重点观察的不是配色有多漂亮,而是它们在若干关键区域是怎么处理的: + +* 导航栏怎么设计,底部还是顶部,是固定几个核心入口还是只有一个主按钮 +* 表单怎么组织,是一次性在同一页填完,还是拆成多个小步骤 +* 结果展示时,最重要的信息有没有被放在最明显的位置,次要信息又是怎样被收纳的 +* 新用户第一次进来时,有没有简短的引导流程,告诉他接下来怎么用 + +![](images/image12.png) + +![](images/image13.png) + +具体可以参考如下几个网页截图收集站: + +* [https://www.uisources.com/](https://www.uisources.com/) +* [https://screenlane.com/](https://screenlane.com/) +* [https://pagecollective.com/](https://pagecollective.com/) +* [https://patttterns.net/](https://patttterns.net/) +* [https://mobbin.com/](https://mobbin.com/) +* [https://refero.design/](https://refero.design/) +* [https://scrnshts.club/](https://scrnshts.club/) +* [https://godly.website](https://godly.website/) + +除了直接参考别人的应用,我们还能从一些比赛中得到灵感,比如 Hackathon( 限时、高强度的团队协作开发活动,需短时间内完成产品原型或解决方案)的得奖作品和一些公开的 demo 网站。其本质上是一批实践者在极短时间内交出的解决方案,它们虽然粗糙,但恰好呈现了如何在有限时间内完成从点子到可运行产品的压缩流程,你可以参考他们的作品思考什么叫做最小产品原型;但由于黑客松始终是短时间的比赛,有可能创意大于实用性,其获奖作品并不一定适合作为一个长期的产品进行参考和开发,你需要根据实际情况进行判断。 + +除此之外,你还能参考一些所谓的工具类网站进行操作,工具类网站你可以理解为类似天气查询网站、多语言翻译网站、神奇宝贝图鉴收集网站、游戏攻略网站、流行车辆排名网站、AI 工具站。这些工具站虽然功能十分简单,但也许就是一个满足某些人需求的非常好的“应用”。想法不在复杂而在有用,通过对不同应用的参考,你能真正知道什么才是市场需求。 + +## 2.5 不要等一切就绪才调查用户需求 + +很多人嘴上说要做用户驱动的产品,真正做的时候却习惯先关起门来做一个他们心中完整的版本,然后才鼓起勇气拿给别人看。**这听起来好像更体面,至少不会在别人面前暴露自己的半成品。但从产品的角度看,这是一个非常危险的习惯。** + +原因很简单: 你在越后面才接触用户,你前面对细节的投入越多,一旦方向不对,损失就越大。你可能已经为一个不重要的功能写了很多代码,为一个没有太多人关心的细节设计了很多图,最后却发现用户真正卡住的地方,根本不是你花最多时间的那一块。 + +要避免这种局面,有一个简洁但有效的原则可以时时提醒自己: 边画边问,**边做边问,不要做完再问。** + +### 边画边问: 在纸面阶段就开始收集反馈 + +当你刚刚在白板或纸上画出入口页、操作页、结果页的时候,其实就已经具备了和用户对话的基础。你完全可以在这一阶段,就找两三个可能成为目标用户的人,让他们看一眼,听听他们的第一反应。 + +你不必做复杂的访谈,只需要观察几个细节: 他们看到入口页时,会不会自发说出你想让他们说的那句话,比如 这好像是做长文总结的;他们在操作页上,会不会自然而然按你预期的顺序进行,比如先粘贴文字,再选择总结长度;他们在结果页上,是否一眼就被你希望他们看到的部分吸引,而不是纠结在一些无关紧要的角落。 + +这些观察可以帮你在写第一行代码之前,就暴露出那些最明显的设计问题。你可以根据这些反馈修一次纸上的原型,再继续往下做,而不是等到整个应用已经搭好再来大改结构。 + +### 边做边问: 在半成品阶段就拉人试用 + +当你有了一个能跑通基本流程的半成品版本时,更没有理由一个人闷着用。哪怕界面很粗糙,哪怕很多功能还没加进去,**只要它能够完成你定义的那个最小任务,就已经具备邀请真实用户试用的条件。** + +你可以先从身边人开始,还可以从你在上一章提到的人群资产、公开场域里接触到的用户中,挑一些比较愿意尝试新工具的人。给他们发一个链接,简单说明现在能做的事情,然后请他们在你不做太多解释的情况下,从入口走到结果。 + + **在这个过程中,你要做的不是辩解,而是观察。** 他们会在什么地方犹豫,会在哪个环节停顿,哪一个按钮看了很久都不敢点。你也可以事后问几个具体问题: 有哪一步是你觉得最费劲的,有哪一个结果你是最满意的,有什么是你以为会有但最终没看到的。 + +在半成品阶段做这些事,有一个巨大好处: 你还没有对任何一个方案投入过多的情感依赖,你会更容易接受把某些看起来很酷但用户根本不在意的功能砍掉,也更愿意花时间去优化那些虽然不起眼却真正在使用中频繁出现的小细节。 + +### 不要害怕暴露粗糙 + +很多人之所以不愿意在早期让别人看到,是因为害怕暴露自己的粗糙,觉得这样会被认为不专业。可恰恰相反,真正成熟的产品人,很少对早期版本有这种羞耻感。因为他们知道,早暴露问题,成本最低。 + +你可以在心里换一个视角看待这件事: 你不是在展示一个未完成品,而是在邀请对方参与共同打磨。只要你事先说清楚这是一个非常早期的版本,你希望对方给的不是赞美,而是尽可能直接的使用感受,大多数人是愿意提供帮助的,尤其是那些本身就被你要解决的问题困扰的人。 + +至此,你已经学会用白板和纸,把一个抽象点子变成一条具体的用户链路;你知道如何通过拆解,把大而宽的愿望拆成可以明天就开始动手的最小可行动项;你也知道不该贪心,一口气把所有想法都塞进第一个版本,而是用双钻模型在发散和收敛之间来回切换,最后选出那个最值得先做的 MVP;你学会了聪明地参考现有应用,在导航、表单和结果展示这些基础结构上,站在别人的肩膀上往前走;更重要的是,你知道不要等一切就绪才去找用户,而是从 demo 开始,就让他们走进来,用他们的使用感受来帮你一起修正方向。 + +通过这些工具和步骤,你已经有能力把一个点子,拆成一个初步可用的应用。但你也会发现,一个能用的应用,和一个真正好用的应用,中间还隔着一层面纱。 + +接下来我们就专门谈一谈: 什么样的应用,才算是好应用;让你知道在得到第一个可用版本后,下一步如何让应用走的更远。 + +## 📚 Assignments + +请你根据上文内容,完成下列作业: + +1. 请你使用任意一款大语言模型,针对之前的点子,让 AI 参考双钻模型给出发散结果,你需要根据发散结果选出一套可行解决方案。 +2. 根据之前想出来的点子,使用拆解细化的方法得到更具体的可执行内容。类似:“为用户提供一个网页工具,让他上传一份不超过 20 页的纯文字 PDF,在 10 秒内得到一份段落结构清晰、标题层级保留的可编辑文本,并支持一键复制和下载为 .txt。” +3. 根据细化后的点子,尝试在白板上画出你的应用,应用需要关注两个部分,一个是 UI 应该如何设计,一个是该有什么功能,每个功能是在哪。 +# 4. 做出来后,怎样判断和打磨成好应用 + +当你终于把第一个版本做出来,放到真实世界里给人用时,会进入一个完全不一样的阶段。之前所有的讨论,都还停留在想法和设计层面,而现在,产品会第一次被真实的使用场景检验。你会看到用户点错的地方、犹豫的地方、卡住不动的地方,也会看到他们在哪些步骤出奇地顺畅,甚至会在某个角落意外多停留几秒。这些细节,远比你脑子里对产品的各种想象要诚实得多。 + +这一章要解决的是一个核心问题: 当应用已经做出来,甚至已经有一批早期用户在使用时,怎样判断它离一个好应用还有多远,以及如何利用这些真实使用过程中的信息,把它一步步打磨好。 + +## 3.1 什么是好应用: 4 个核心特征 + +要判断一个应用好不好,不能只看你自己多喜欢它,也不能只看下载量或一两天的使用次数,而是要看它具不具备一些更底层、更稳定的特征。简单而言可参考以下几个特征: + +### 好应用能带来具体价值 + +好应用最直接的特征,是它能让人在某个场景下实打实地得到一点好处。这个好处不一定宏大,也不需要用多高深的语言去包装,但必须具体到你能说得清楚:** 它到底帮用户少做了什么,少花了多少时间,或者让什么事情不那么容易出错。** + +![](images/image14.png) + +比如一个简单的会议纪要工具,如果它能做到只要上传录音或者在会议过程中直接录音,结束后就能自动生成一份结构化的会议纪要,并且把待办事项、责任人、截止时间用列表列清楚,那它帮用户节省的就不仅仅是打字时间,而是从记录、整理、筛选到格式化输出整套过程的心力。你可以很明确地说,这个工具大概每场会为一个人省下二十分钟。而如果整个团队每周有十场这种会议,那么总共节省下来的时间就非常可观。 + +再比如一个看似不起眼的图片压缩工具,如果它能在保持肉眼几乎看不出差别的前提下,把一批图片的体积压缩到原来的三分之一,同时保证一键导出、文件夹结构不乱、命名规则统一,那它带来的价值并不只是硬盘空间的节省,还有传输更快、上传更顺滑、和其他系统对接时更少出错。这种看似平凡的具体价值,往往比一句模糊的效率提升要可靠得多。 + +所以,当你说自己的应用有价值时,最好能把价值拆成一两条具体的场景,用普通人听得懂的话解释: 你的应用让用户原本需要花多久、做多少手工、承担多大风险的事情,变得更省力。 + +### 用户好上手,几乎不用看说明书就能懂 + +另一个容易被低估但极其重要的特征,是 **好应用通常不太需要解释** 。用户第一次打开时,靠直觉就能知道大概该从哪里开始,点击什么会发生什么,最大按钮通常做的是最核心的事情,最重要的入口会摆在真正重要的位置,而不是藏在菜单的第三层。 + +![](images/image15.png) + +你可以想象一个刚刚下载你应用的新用户,他可能是在排队、在车上、在咖啡店里随手点开的。当时网络信号不一定很好,他也没有耐心看任何一篇长说明。他能容忍的迷茫时间,往往只有几秒钟。如果在这几秒钟里他看不到任何明确的引导,不知道下一步该干什么,就很容易直接关掉,然后再也不回来。 + +所以,当你自己觉得产品逻辑很顺畅时,最好找一个完全没见过你应用的人,让他在你不说话的情况下,从零开始摸索。你只观察他会在哪些地方停顿,在什么位置犹豫,什么时候会露出那种这是什么的表情。用户如果一进来就被各种开屏弹窗、复杂选项、账号绑定挡住,很难认真体验到你真正想提供的价值。 + + **好上手这件事,本质上是产品对用户成本的一种尊重。** 你是在承认一件事: 没有人有义务花时间研究你的应用。 + +### 在高频或关键场景中,会自然想到你 + +好应用往往有一个稳定的使用节奏,要么高频,要么关键。 **高频是指它融入了用户的日常,例如每天都会打开好几次的消息应用** ,每天上下班都用的通勤工具,每天习惯记录的打卡应用。关键是指即便不是每天都用,但是一旦遇到某类场景,用户就会第一时间想到你,比如报税工具、装修预算计算器、面试题管理工具、签证资料清单助手。 + +你可以问自己几个问题: 用户真正会在什么时间、什么情境下用到你; 如果他错过你,会不会真的感觉到不便; 同类场景下,他现在是靠什么方式过活的。如果有一个备选方案,哪怕很麻烦,但是已经习惯了,那你要做的就不仅仅是功能对齐,还要让他感觉换到你这里来确实更值得。 + +一个常见的误解,是把使用频次和应用的好坏直接绑定在一起。其实不必。比如做年终报表、办理某种证件、做一次大额转账,这些事情本身频率不高,但一旦发生,对用户来说就是当下最重要的事情之一。**如果你的应用刚好能把这类关键场景处理得稳、快、让人心里有底,那它一样可以称得上好应用。** + + **真正需要警惕的,是那种用户既不高频用你,也不会在任何关键时刻主动想到你** ,甚至如果你的应用从他手机里消失,他只会在几个月后清理内存时才模糊想起曾经装过这么一个东西。这种情况往往说明你的应用并没有和任何真实的场景深度绑定,只是在功能层面堆了一些存在感不强的东西。 + +### 利他心 + +很多人一开始做产品时,心里同时盘算的是几件事: 做出来后怎么收费,怎么涨价,怎么让用户多用一点就得付费,怎么锁死数据防止用户迁移走。商业上的计算本身没问题,但如果一开始思路就完全绕着这些转,很容易做出那种一眼就充满戒心的应用: 一上来就要各种权限,到处是花样收费点,功能设计明显不是为了让用户顺畅完成任务,而是想办法把用户引导到某个付费的按钮上。 + +相比之下,真正好的应用都带有一种比较朴素的利他心。它确实会想清楚要怎么活下去,也会设置合理的收费方式,但在设计路径和体验的时候,优先级始终摆在: **怎么让用户更容易顺利完成这件事,而不是怎么多加一步流程来制造额外障碍。** 你会看到它在很多地方都用了对用户更友好的方式,比如在关键步骤给出清晰的提示,在导出和迁移上不过度设置壁垒,在收费前让你至少体验到一部分实在的价值。 + +这种利他心,经常体现在一些微小的设计细节上。比如表单那一栏不会为了多收信息而乱要一堆和任务无关的数据,教程的顺序是围绕用户要完成的目标设计的,而不是围绕功能模块自己来讲。你能感受到这个应用是在认真帮你做成一件事,而不是把你当成一个被压榨的对象。 + +还有一点很重要: **好应用不一定是大应用。它可以很小,只服务于一类人、一个场景、一个任务** ,但在那一小块里做得很到位。比如专门帮设计师把稿件导出成打印店要求的格式,或者专门帮自由职业者整理个人项目案例,这些范围都不大,但里面的价值一点不小。 + +## 3.2 洞察需求:**马斯洛的需求层次理论** + +![](images/image16.png) + +在做应用之前,很多人会直接跳到功能层面思考:这块能不能再做点什么,那块要不要加个按钮。而真正决定一个应用能不能活下去的,是你究竟踩中了人哪一层次的需求,以及踩得有多准。 + +马斯洛的需求层次理论之所以在这么多领域被反复提起,不是因为它多严谨,而是因为它提供了一个足够好用的观察框架。你不用把它当成严格的心理学结论来看,只要把它当成一个简单的框架:帮你把用户的各种动机,挂在几个相对清晰的层级上,方便你判断你的应用到底在满足哪一类需求,你能满足越多需求,就是越好的应用。 + +马斯洛的需求层次理论通常会分成五层,自下而上分别是:生理需求、安全需求、归属与爱、尊重需求、自我实现。 + +### 生理和生存相关的需求 + +这一层最基础,直接关系到吃饭、睡觉、生存状态本身。听上去好像和互联网产品有点远,但其实不少应用都在这个层上发挥作用。 + +比如外卖、买菜、跑腿、订房、打车,这些典型的到家和出行服务,本质上都是在帮用户用更低的时间成本去解决吃饭、出门、休息这类最基本的问题。再比如健身记录、睡眠监测、饮食打卡,虽然看起来更偏健康管理,但对很多人来说,是在试图维持一个不至于失控的身体状态,这也可以看作是生理与生存层面的延伸。 + +如果你的应用是在这一层发力,有一个特点是: **用户对稳定、可靠、可预期会特别敏感** 。点外卖送不到、打车一直叫不到、订房信息出错,带来的情绪反应会非常强烈,因为这些问题直接打断了生活的基本节奏。 + +### 安全感和确定性的需求 + +安全需求包括物理层面的安全,也包括经济、信息、心理上的安全感。 + +很多工具型应用,其实主要都在安全这一层工作。比如记账、资产管理、保险助手、合同模板工具、密码管理器、备份工具、隐私保护工具、网盘同步、数据恢复。这些应用的核心承诺往往是:帮你降低出错概率,帮你在事情出了问题时有备选方案,或者至少让你心里有底。 + +比较典型的一类,是各种防丢、防忘、防错的小工具:日程提醒、药品服用提醒、重要文件到期提醒、关键节点的备忘。这类应用哪怕每天只提醒你几次,但只要有一两次在关键时刻救了你,它就会迅速被你归类为必须留着的一类工具。 + +当你在设计这类产品时,可以多问一句: **你到底帮用户降低了哪一类风险,是金钱上的、时间上的、关系上的,** 还是合规和法律上的。如果连你自己都说不清,那用户很难真正信任你。 + +### 归属感、连接和被看见 + +再往上一层,是归属与爱的需求。简单说,就是我不想一个人,我想和某些人连在一起。这一层,是社交类、社区类、兴趣小组类应用的大本营。 + +朋友圈、群聊、兴趣论坛、同好社区、线上读书会、游戏里的公会,甚至一些围绕特定身份的工具,比如新手父母群、留学生互助、行业内部匿名吐槽平台,本质上都是在提供某种归属感:有一群和我类似的人,我们在看类似的话题、吐槽类似的困难、分享类似的经验。 + +有些工具表面上是功能型应用,但真正留住用户的,往往是这层需求。比如记账应用里大家分享自己的存钱进度,跑步应用里的排名和打卡圈子,学习应用里的互相监督小组。这些看似增值的社交模块,实际上是在让用户把你的应用和自己的某个群体身份绑在一起。 + +如果你的应用试图站在这一层,光有内容是不够的,你要思考的是: **用户凭什么觉得这里是自己人,他愿不愿意在这里留下痕迹、和别人产生一点轻微但真实的互动** 。否则,你做的就只是一个单向的广播工具。 + +### 尊重、自我价值和成就感 + +再往上一层,是尊重和自尊需求。人不只想被接纳,还会在某个阶段开始在意:我在这里算不算一个不错的人,我有没有被看见、被认可,我做成的事情有没有人知道。 + +大量的打卡、勋章、排行榜、头衔、成就体系,其实都在这个层面发挥作用。学习应用里完成多少课时会给你一个称号,运动应用里达成目标会给你一张证书,创作平台给作者开通不同等级的身份标识,社区里对优质内容作者有明显的高亮标记。 + +这里容易出现一个误区:以为加一堆勋章、积分、称号就能刺激用户。用户要的不是浮夸的装饰,而是我的真实努力被记录并被认真对待。如果你设计的成就体系,和用户的真实投入完全脱节,比如随便点几下就能拿到资深称号,那这个激励很快就会失效,甚至让人觉得廉价。 + +所以在这层上,关键不在于你有没有做激励系统,而在于: **你的应用有没有给用户提供一个可以积累的舞台,让他可以清楚看见自己从初学者到熟练者的变化** ,并且在关键节点上,给予他一种这一步值得被记一下的仪式感。 + +### 自我实现与自我超越 + +金字塔的最上层,指向的是我想成为怎样的人,以及我想把自己的一部分贡献出去。这听起来很抽象,但落到具体场景里,往往有很实际的表现。 + +比如创作类工具:写作、绘画、音乐制作、视频剪辑、编程项目管理,它们表面上是在提供技术能力,背后承接的是用户对创造一些属于自己的东西的渴望。再比如一些长期学习平台、职业规划工具、习惯养成工具,它们服务的并不仅仅是单一技能,而是某种更长线的自我成长目标。 + +还有一种是让别人变好的需求。很多人使用知识分享平台、问答社区、公益类应用、协同创作工具,不只是为了赚点积分或流量,而是因为在帮助别人、推动一个项目向前时,会有一种我正在做一件有意义的事的感觉,这也属于自我实现的一部分。 + +当你的应用真正触碰到这一层时,往往会拥有一种很强的粘性:即便界面不是最漂亮,功能也不一定最全,用户还是会留在这里,因为 **它和我是怎样的人、我在做什么样的事情建立了更深的连接** 。 + +把马斯洛金字塔当成产品视角的一个好处,是它能帮你避免两个常见的偏差。 + + **第一个偏差,是只盯着某一个错误的层次不放。** 比如你做的是帮助用户安全存储文件的工具,本质上站在安全这一层,但你却一味模仿社交产品,在界面上堆各种点赞、评论、排行榜,结果既抢不到社交类产品的用户心智,又让本来只想要一个稳妥存储工具的人觉得你不务正业。 + + **第二个偏差,是忽略了层级之间的先后关系。** 一个人连最基础的稳定使用体验都得不到保障时,很难认真在你这里追求自我实现。比如应用经常崩溃、数据时不时丢,哪怕你给了再多勋章、再多成长曲线,用户也不会真心投入。反过来,如果你在基础层面做得扎实,再逐步叠加更高层次的价值,用户会更容易跟着你一起走上去。 + +在实际设计中,你可以这样自查: + +* 先问自己:我的应用最主要、最核心的是在满足哪一层需求,只允许选一层 +* 再问:在这个核心层之上,我有没有机会自然地延伸到上一层,而不是硬贴一个概念上去 +* 最后,看一眼:在比我目标层更低的那些层里,我有没有明显短板,甚至在拖用户后腿 + +当你能回答清楚这几个问题时,你对用户真正要什么这件事,就不再只是停留在感觉他们可能会喜欢的模糊层面,这有助于你做出更好的应用。 + +## 3.3 按用户类型分类: C 端应用和 B 端应用的差异 + +应用做出来之后,你很快会发现另一件重要的事: 面对普通个人用户和面对企业或机构用户,是两种完全不同的游戏规则。它们看起来都叫用户,但关心的重点完全不同。 + +* C 端(Consumer End):指 “消费者端”,核心是普通个人用户。 + 比如我们日常用的微信、抖音、美团外卖,这些 App 的使用者是一个个独立的个人,这类面向个人提供服务的场景,就是 C 端业务。 +* B 端(Business End):指 “企业端”,核心是企业、机构或组织用户。 + 比如公司里用的钉钉(企业协作工具)、财务软件(如用友、金蝶)、零售门店的收银系统,这些产品的使用者是企业员工、团队或整个机构,服务于企业的经营、管理、生产等需求,这类面向组织提供服务的场景,就是 B 端业务。 + +### C 端应用: 面向普通人的生活、情绪和习惯 + +C 端应用面向的是个人用户,它们嵌在每个人的日常生活里。常见的类型包括内容类、工具类、娱乐类、社交类、学习类等等。 + +![](images/image17.png) + +内容类应用,比如资讯类阅读、短视频平台、播客工具。它们的核心任务通常是让用户在有限的时间内,从海量信息中筛出自己感兴趣的内容。同时要保证不断有新东西吸引用户回来。 + +工具类应用,比如记账、待办事项、文件管理、日历调度。它们往往在某个具体任务上提供一个比原始方式更顺手的解决方案,属于用户日常使用的基础设施之一。 + +娱乐类应用,包括游戏、轻互动、趣味小工具。它们给用户提供的是情绪上的放松和愉悦,衡量好不好用的标准,更多是用户愿不愿意持续花时间在上面。 + +社交类应用围绕的是人与人之间的连接和互动,学习类应用则围绕某种能力的提升,比如背词、刷题、读书打卡、课程管理。 + +这些应用虽然种类不同,但有几个共同的关注点。 + + **第一,用户增长。** 也就是如何让更多人第一次尝试你的应用。这里涉及到渠道、传播文案、用户激励,但前提始终是: 你要先有一个足够清晰的使用场景。否则,再厉害的增长手段也只能带来一波短期的好奇心。 + + **第二,留存和复访。** 不是有人来过,而是他愿不愿意留下来、回来。一个内容类应用,如果不能保证持续产出用户感兴趣的内容,很快就会被替代;工具类应用如果在关键几次使用里没有帮助用户真正完成任务,也很难建立长期使用习惯。你可以通过观察第 1 天、第 7 天、第 30 天的留存情况,判断到底有多少人真正把你纳入了生活节奏。 + + **第三,转化和付费。** 用户为什么愿意付费,通常不是因为你把免费版做得很糟糕,而是因为在他已经从你这里获得一部分价值之后,看到付费功能能带来更高层次的便利。比如更高的使用额度、更强的协作能力、更专业的模板、更稳定的性能。 + + **第四,分享传播性。** 很多 C 端产品之所以能快速扩散,是因为在使用过程中天然带有分享属性。比如生成一张图、一个视频、一段文本,用户为了完成自己的目标,本身就需要把结果发给别人。在这个过程中,只要你把品牌露出做得自然、不讨厌,就能获得一部分口碑式的传播。 + +判断 C 端真需求的一个简单方式,是看用户愿不愿意围绕它形成自己的小习惯。比如愿意每天打开看一眼、愿意把它和自己的生活节奏绑定在一起、愿意让它参与到某些重要时刻的记录中。反之,如果用户只是因为一次活动或广告进来,用完就走,而且几乎不会再回来,那基本可以判断,你解决的可能只是他们一时的好奇心,而不是长期需求。 + +### B 端应用: 面向组织的效率、成本和风险控制 + +B 端应用面向的是企业、团队、机构或某个部门。常见类型包括 ERP(资源管理系统)、CRM(客户关系管理)、协同办公工具、各类 SaaS 工具、行业内部管理系统等。 + +![](images/image18.png) + +这些应用和 C 端的最大不同,是它们要同时满足多个角色的需求。使用者可能是一线员工,决策者却是主管或老板,数据的拥有者可能是组织本身,审批流程可能涉及多个部门。你既要让使用者觉得好用,**又要让决策者看到** **投入产出比** **,还要让整个组织在风险和合规层面有安全感。** + +B 端应用有几项特别核心的关注点。 + + **第一,提高效率。** 这不仅指某一个人的时间缩短了,而是整体流程耗时减少,协作成本降低,沟通环节减少。比如一个订单从创建到发货原来要经过五个系统,现在通过一个统一入口就能整体流转,这类提升对企业来说就是非常具体的。 + + **第二,** **降低成本** **。** 包括人力成本、培训成本、系统维护成本等等。如果一个系统看起来功能很强大,但需要投入大量培训和维护资源才能勉强跑起来,那它对很多中小企业来说就会显得性价比不高。反而是那些做得看似更轻,但能快速上手、快速看到回报的 SaaS 工具,更容易在现实世界活下来。 + + **第三,控制风险和保证合规。** 很多 B 端场景里,对合规性和可追溯性有很高要求,比如金融、医疗、制造、政务等行业。一个好的 B 端应用,往往会牺牲一点使用上的自由度,换来更明确的权限管理、更严谨的日志记录、更清晰的审批链路。对用户个人来说,可能少了一些随意发挥的空间,但对组织整体来说,反而是价值所在。 + + **第四,权限管理和责任边界。** 谁能看见什么、谁能改什么、谁对什么结果负责,这些问题往往是 B 端系统设计中的重点。一旦这里做不好,就会给后续的审计、纠纷、追责带来巨大麻烦。所以,判断一个 B 端应用是不是好应用,不能只看界面看起来顺不顺眼,还要看它的权限模型是否严谨、是否容易理解和维护。 + +从行业到应用,你可以这样思考: **选一个你有一定了解的行业,比如教培、电商、制造、金融、医疗** ,然后拆开看这个行业的日常运转中,有哪些流程特别倚赖人工,有哪些信息经常散落在多个系统或多个私聊里,有哪些环节出错率特别高但又不容易被立刻发现。围绕这些地方,你往往可以设计出一些很聚焦的小工具。 + +比如在教培行业,一个很具体的应用切口是做课程排班和教室利用率优化工具。它不需要取代整个教务系统,只要专注于让教务老师更容易安排老师、教室和课程时间,自动避免冲突,给出最佳组合,导出一份所有人都能看懂的课表,这一项就足以节省大量反复沟通和修改的时间。 + +在电商行业,一个常见需求是多渠道订单管理。商家可能同时在不同平台有店铺,订单信息散落各处。如果你能提供一个把各平台订单抓取到一起、统一处理售后和物流信息的小工具,就已经解决了他们每天重复操作中的一个巨大痛点。 + +在制造业,很多企业仍然依赖纸质记录或 Excel 来做生产进度追踪。你可以从一个简单的工单跟踪工具入手,帮助现场管理者更直观地看到每个工序的状态,而不是一整天都靠问人和打电话。 + +在金融或医疗行业,你的切入点未必是前台业务,可以是合规检查辅助工具,可以是文档模板生成,可以是审批材料清单管理。只要你能说清楚在某个流程里,你让哪一类岗位的哪一项任务变得更可控,就已经是一个值得尝试的方向。 + +以上行业的应用往往都有一些成熟公司的产品正在推广,这其实为你提供了很好的参考路径:你可以在网络上主动搜索 “对应行业 + 核心需求 + 产品” 的关键词(比如 “教培行业 教务排班系统”“电商 多渠道订单管理工具”),不仅能找到具体的产品官网、功能介绍,还能看到用户评价、行业案例甚至产品演示视频。这些信息能帮你直观了解成熟产品如何解决同类问题,避免从零摸索的试错成本。 + +## 3.4 根据用户数据打磨: 从我觉得好到用户觉得好 + +应用做出来之后,最容易出现的一种错觉是: 你自己越用越顺手,觉得哪里都挺合理,就以为用户也会这样。实际上,越是自己写的产品,越容易对别人的问题视而不见。要让应用逐渐从一个自我感觉良好的作品,成长为真正的好应用,你必须学会把真实的用户反馈引进解决。 + +### 设计简单的反馈机制,让用户有出口说话 + +不需要一上来就搞复杂的客服系统和数据平台,你可以从一些非常简单的方式开始。 + + **群聊是最直接的一种。** 如果你手里已经有一个小范围的用户群,可以邀请大家把平时使用过程中的问题和想法发在群里。你要做的是认真回复、记录并且定期总结,而不是在群里辩解或防御。你越能在这个小群体里建立一种可以坦诚说话的氛围,后面收集到的反馈就越有价值。 + +问卷适合在你需要 **一次性收集较多结构化信息的时候使用** ,比如在一个版本迭代之后,想知道大家对某几个具体功能的感受。如果你希望填写率高,问卷最好不要太长,问题要尽量具体,比如这一段时间你最常用的是哪个功能,在哪一步卡壳最多,而不是泛泛问你对这个应用总体感觉如何。 + +使用后弹窗是另一个常用方式,比如在用户完成一次任务后,用一个非常简短的评分和建议框,问他这次体验是否顺利。有时一个简单的数字评分,就足以帮你判断某个流程是否存在明显问题。 + +一对一访谈用起来成本较高,但回报经常也更大。你可以 **挑选几位不同类型的用户,约他们花二十到四十分钟** ,详细聊聊他们平时的使用习惯,让他们边操作边讲你看到了什么、感觉到了什么。曾经看到一个创始人为了约用户建议,每天安排了十多次会议和用户进行对谈,花时间理解用户需求永远都不会是坏事。 + +### 学会从杂乱反馈里提炼出三类信息 + +用户反馈通常是混杂在一起的,很难一眼看清楚。你可以尝试把它们分成三类:** bug、体验问题、新需求。** + + **bug 指的是原本你说会发生某种行为,但在某些情况下完全没有发生,或者发生了错误的行为** 。比如上传失败、闪退、按钮没有响应、结果明显不对。对于这类问题,你要做的是尽快复现、修复,并且在修复后主动告知受影响的那批用户,让他们知道你是认真对待这些问题的。 + + **体验问题是在流程长度、操作位置、文案表达上没有选到最顺滑的路径。** 比如用户总是在某个按钮上犹豫,不知道点了会不会造成不可逆的结果;某个功能很重要,却被放在一个不显眼的角落;某些默认设置和大多数人的习惯相反,导致他们每次都要多做一步调整。这类反馈需要你结合数据和观察去判断,决定是否做改变,以及改到什么程度比较合适。 + + **新需求是指用户开始提出一些原本你没想到的功能和场景。** 有些新需求确实值得认真考虑,比如多种导出格式、团队协作能力、和其他常用工具的对接。但也要注意,不是用户提什么你都要照做。真正要做的是辨别,这些新需求背后有没有共性问题,是不是和你原本想服务的那群人、那个核心任务一致。否则,你很容易被一堆分散的需求拉扯到各个方向,最后变成一个什么都想做、却什么都做不精的产品。 + +你可以养成一个习惯: 为每一条反馈打上标签,标明它属于 bug、体验问题还是新需求。定期把这些标签汇总,看看哪一类问题集中在哪几个功能或流程上。这样一来,你就不再只是被动地修补,而是能有意识地围绕高频问题展开迭代。 + +### 用三个简单指标,判断要不要继续投入 + +在资源有限的情况下,你还需要一些简单但有效的指标,来判断这个应用值不值得你继续长期投入。 + + **第一个是留存。** 留存不是看某一天有多少人打开,而是看在 **一段时间里,有多少用户还在持续使用** 。你可以很粗糙地统计,比如下载后一个星期内还有多少人至少用过一次,一个月内又有多少人回来过。如果大部分用户只用了一两次就再也不回来,说明你的应用在前期并没有让他们看到足够的价值,或者使用门槛太高。 + + **第二个是复访频率。** 那些没有卸载你的应用的人,到底多久会回来一次。一款每天都可以用得上的工具和一款每季度才会被想起一次的应用,它们的产品定位不同,你要用不同的标尺来衡量。但无论哪种,你都应该能给出一个合理的使用节奏预期,然后对照实际数据,看有没有大的偏差。频率比你期望高,说明价值可能超出预期; 频率远低于预期,则要反思是不是场景抓得不准,或者使用体验某处让用户感觉累。 + + **第三个是推荐意愿。** 有没有人愿意主动推荐你的应用给别人。这件事可以通过几种方式观察: 比如在用户完成一次特别顺利的任务后,提供一个自然的分享入口,看有多少人真正使用; 在群里看看有没有人自发安利你的产品;或者进行小规模用户访谈时问一句,如果你身边有人遇到类似问题,你会不会推荐我这个工具给他。推荐意愿通常比简单的满意度分数更能说明问题,因为推荐是一种带有个人信用背书的行为,用户只有觉得你真的帮了大忙,才愿意把你的应用介绍给朋友。 + +当你把这三个指标与前面讲过的用户反馈结合起来看,大致就能判断出你的应用目前处于什么状态。也许功能还不完备,但有一批人已经留下来,并且会在特定场景反复使用你,这样的应用就很值得继续投资和打磨。相反,如果修了很多 bug,堆了不少新功能,留存和复访却一直上不去,几乎没人主动推荐,这时候就要冷静思考,是不是该收缩范围,重新回到那个最初的核心场景,甚至考虑换一个方向。 + +# 4. 在哪一步、怎么合理地用 AI 放大价值? + +一旦你开始认真做一个应用,很快就会遇到一个普遍存在的诱惑: 能不能再加一点 AI 进去。这个诱惑之所以强,是因为你每天都在看到各种宣传: AI 赋能某某行业,AI 彻底重构某某流程,AI 帮你一键搞定一切。久而久之,你很容易把一个原本朴素的问题变成一个充满噱头的口号,然后在技术栈里堆上一些模型调用,看账户的钱逐渐亏光。 + +虽然本教程教的是如何开发 AI 原生应用,谈论该话题颇有些砸自己的饭碗嫌疑;但对于一个小应用或者刚起步的产品来说, **最危险的不是不用 AI,而是为了 AI 而 AI** 。你明明可以先做一个简单但靠谱的工具,却被各种新能力吸引,不断往里添加看起来很聪明的功能,最后把一个原本可以落地的方向做得又贵又复杂,还没有明显的价值提升。本章要解决的核心问题是解释清楚: 在什么阶段、用在哪些环节、用什么方式 AI 真正能帮你把应用的价值放大。 + +## 4.1 不要为了 AI 而 AI + +要判断自己是不是已经在不知不觉中为了 AI 而 AI,一个很实用的办法,是在每次考虑加入某个 AI 功能之前,先强迫自己认真回答两个问题。 + + **第一个问题是: 不用 AI,这个应用是否也成立** 。也就是,把所有 AI 能力都暂时抹去,你做的这件事,本身是不是一件有价值的事,用户有没有现实需求,愿不愿意每天、每周、每月在这件事上投入真实时间。 + +这句话听起来有点逆势,因为现在几乎所有产品介绍都会把 AI 放在非常显眼的位置,好像没有 AI 就不算现代工具。但如果你的应用在没有 AI 的情况下就完全站不住脚,很多时候说明的并不是你技术不够先进,而是一个更深层的问题: 你抓的那个需求,可能本身就不痛不痒,甚至并不真实存在。 + +想象一下,你要做一个帮助人整理待办事项的工具。如果你主要的差异点是: 在待办列表上加了一些模型生成的提示,比如自动起标题、自动分类、自动补全描述。但用户原本在写待办时,根本不觉得起个标题有什么痛苦,只是想快点把事情写完,那这些聪明能力再花哨,也很难形成持续价值。相反,如果你先退一步,问清楚不用 AI 的时候,这个应用最朴素的价值是什么,也许你会发现更扎实的方向: 帮用户把散落在不同渠道的任务统一收集,帮他看清每天只能做完多少件事,帮他在日程结束之前看到风险,从而做减法和取舍。把这些基础能力做扎实,往往比一上来就给待办加上各种智能标签更重要。 + + **第二个问题是: 用 AI 之后,具体提升了什么。** 这里不接受那种很宽泛的总结,比如提升效率、重构体验、智能升级,而是要落到一两个连用户本人都能清楚感知到的维度。 + +你可以这样盘问自己: + +* 有没有显著提升完成任务的速度,比如把原本要自己从零写的一页文案,变成只需要花五分钟审阅和改写 +* 有没有明显提升结果的质量,比如让用户在相同时间内产出更有条理、更专业、更符合目标受众的内容 +* 有没有让使用过程变得更顺畅或更轻松,比如把一段非常枯燥的表单流程,变成更像聊天的问答 +* 有没有在真实成本上带来下降,比如减少外包次数、减少人工客服时长、缩短培训周期、缩短决策时间 + +如果你在脑中给出的答案还停留在**感觉会**更方便一些、看起来更酷一点,那十有八九说明这个 AI 功能还没有找到最关键的着力点。 + +这两个问题其实有一个很明确的排序。先保证不用 AI 的时候,这个应用也说得通,然后再在这个基础上问,用加了 AI 之后具体好在哪。 + +## 4.2 思考 AI 扮演了什么角色 + +当你确定这个应用就算不用 AI 也成立,而且已经找到一个清楚的提升点,下一步需要做的,是更具体地思考: **在你的应用中,AI 到底是做什么的。** 很多产品在这一步出错,是因为它们把 AI 当作一种很抽象的能量,而不是一个有具体分工的角色。结果就是,功能堆得很多,但每一块的作用都模糊不清,用户用起来也只觉得哪里都沾一点智能,却说不出哪个地方真正离不开它。 + +一个更加清晰的思路,是把 AI 当作几种不同的部件: ** 它可以是大脑,是眼睛,是手** 。你需要根据你的产品目标,决定它在其中负责哪一块,如果可以,最好一开始就只选一两个角色把它做好,而不是一股脑全部塞进去。 + +![](images/image19.png) + + **当它扮演大脑时,主要负责理解和生成文字内容,或者在复杂信息之间做推理。** 比如,你做一个会议纪要助手,它要能从一段长长的录音里,抓出真正核心的讨论点,而不是简单按时间顺序罗列。你做一个学习应用,它要能根据用户的回答,判断他到底是没理解概念,还是只是粗心写错步骤,并给出不同的反馈。这类场景里,AI 的价值在于它能读懂用户说的话,理解用户给出的材料,然后生成一个带结构、带逻辑的输出。你要做的,是帮用户把问题问清楚,把上下文喂得足够准确,让这个大脑有足够信息做判断。 + + **当它扮演眼睛时,重点在于处理图像、视频等非文本内容,** 把这些东西变成机器可以理解的描述,然后再基于这些描述进一步行动。比如,你做一个整理纸质文档的工具,它可以通过拍照识别,把一堆发票、合同、包装说明书变成可搜索的文字。你做一个学习绘画的应用,它可以看懂用户画的草图,然后指出构图和线条上的一些问题。你做一个家居整理建议工具,它可以通过用户上传的照片,识别出目前房间的布局和物品分布,再给出一些简单可执行的改造方案。这里 AI 的重点是: 它像是一双会分析的眼睛,让你的应用不再只能处理键盘输入的文字,而是开始真正接触用户生活里的实物世界。 + + **当它扮演手时,意味着它开始去执行一连串具体的动作** ,而不仅仅是给出一个建议或者文字结果。比如一些自动化平台,会让你把多个步骤串成一条工作流: 从邮件里读取附件,把内容总结成要点,发到一个群里,再把原文存入云盘,最后在任务管理工具中自动创建一条跟进任务。这里 AI 的作用,是帮你在复杂的流程里,根据上下文动态决定下一步该怎么干,比如识别一封邮件是不是投诉,判断某个表单是不是填写完整,再据此触发不同的后续操作。 + +除了上述简单的描述,实际应用中,AI 承担的角色往往会更加具体和多样,例如: + +在文本处理方面,它可能是在做翻译、摘要、问答、续写或情感分析:比如客服系统里自动分类用户咨询、法律文档助手里提取合同条款、教育应用里批改作文。 + +* 技术基础主要是深度学习中的 **大语言模型(** **LLM** **)** :在海量语料上学习语言规律和世界知识,既能“看懂”长篇、多轮对话中的上下文,又能“写出”连贯、风格一致的内容。 +* 在“理解”侧,LLM 可以识别用户意图、提取关键信息、判断情感倾向;在“生成”侧,则用于自动写摘要、回答问题、进行续写改写以及多语言翻译,把大量需要人工阅读、归纳和撰写的工作自动化或半自动化。 +* 以**在线客服机器人**为例:系统先根据用户的一句话大致判断属于咨询、投诉还是售后,并从话语中识别出订单号、时间、商品名等关键信息,然后交给 LLM 结合上下文和企业知识库生成自然、完整的答复,既减少人工压力,又能在高峰期保持稳定服务质量。 + +在图像处理方面,它可能是在做识别、分类、生成、修复或增强:比如医疗影像里标注病灶位置、电商平台里自动抠图换背景、设计工具里根据文字描述生成配图。 + +* 图像理解通常依托 **卷积神经网络** **(** **CNN** **)** 等视觉深度模型,从海量图像中学习边缘、纹理、结构等特征,用于目标检测、图像分割和细粒度分类(如区分不同病灶、不同商品品类)。 +* 图像生成与修复依托 **扩散模型、** **GAN** ** 等生成式模型** ,可以根据文字描述或参考图片生成全新图像,对模糊、缺失或低分辨率图像进行修复和超分辨率增强。 +* 很多系统还会结合 LLM:先用自然语言理解用户的文本描述,再自动生成适合视觉模型的“提示词”、风格标签和构图约束,实现从“听懂你想要什么”到“画出你想要什么”。 +* 以电商平台的**“智能主图生成”** 为例:系统先用检测和分割模型把商品从原图中精细抠出来,再通过 LLM 解析商家输入的文案(如“简约北欧风客厅场景,柔和自然光”),转成具体的场景、色调和风格参数,最后交给扩散模型生成匹配的背景和光影效果,并自动筛掉构图不佳或风格不符的结果,输出可直接用于上架的商品主图。 + +在音视频处理方面,它可能是在做音频和视频的生成、转写、降噪、剪辑或字幕制作:比如播客工具里自动生成开场和结尾的旁白、视频平台里根据脚本自动合成讲解视频、会议软件里实时转写和翻译对话并生成多语言字幕与录播回放。 + +* 在“理解”侧,系统会用**语音识别模型**把语音转换成文字,同时分析说话人、语种、语速和大致情绪;用视觉模型理解视频画面中的场景、人物和关键物体。 +* 在“生成”侧,以 LLM 为核心,对脚本、会议内容或用户指令进行理解、拆分和改写,然后驱动 **语音合成** **(** **TTS** **)生成自然语音解说,驱动视频生成与编辑模型**自动合成或剪辑画面、替换背景、插入镜头和字幕;音频侧的生成模型还能自动生成背景音乐和环境音,并配合深度降噪与音质增强提升整体听感。 +* 以“ **文字生成短视频** ”类产品为例:用户只需输入一段文案,系统先用 LLM 把文章拆成几个自然的段落和画面,生成适合口播的解说词和简单的分镜描述;然后由 TTS 模型合成配音,再由视频模板和生成模型根据分镜选择或生成画面,在时间轴上自动对齐字幕与语音,最后一键导出一条可以直接发布的短视频。 + +在语音交互方面,它可能是在做识别、合成、情绪检测或对话管理:比如智能音箱里理解用户指令、语音导航里播报路线、语言学习应用里纠正发音。 + +* 前端由深度学习的**语音识别模型**将用户语音转成文字,并提取语调、音量、语速等特征,为判断情绪和状态提供线索。 +* 后端通过 **语音合成(TTS)** 把这些文字回复变成自然流畅的语音输出,情绪识别模型会根据用户当下的说话方式调整回复的语气和速度,让交互更贴近真实对话。 +* 以**智能音箱**为例:当用户说“今天有点累,放点轻松的音乐吧”,系统先用语音识别转成文字,再由 LLM 结合历史播放记录理解用户偏好的“轻松”风格,自动选择更舒缓的歌单;情绪识别判断用户处于疲惫状态后,TTS 在合成回复时会降低语速、柔化语调,让整个交流过程既“听得懂”,又“听起来舒服”。 + +以上内容只是对 AI 在几个主要方向上的应用和技术做了简单介绍。到了真实业务场景中,你往往需要把多种最新的 AI API 引入进来,在不同任务上做更全面的测试。你还需要逐渐搞清楚当前 AI 的能力到底有多强、能解决哪些问题、在哪些情况下容易出错,它的边界在哪里。只有认识到这些,才能合理设计功能和流程,而不会因为误判能力而埋下风险。 + +接下来,我们就围绕这一点,在下一节更系统地讨论:如何理解 AI 的能力和边界,在实际做产品时应该考虑什么。 + +## 4.3 熟悉 AI 能力与边界 + +当你开始实际把 AI 塞进应用时,很快会发现一个现实: 宣传里说的那套万能,和具体到一个功能上的约束,有时候差距相当大。为了避免过度承诺、结果落空,**你需要对当前 AI 能力的几个主要方向有个基本认知,同时明确它们各自的边界在哪。你需要通过大量测试得到 Bad Case 进行反思,在使用中避开 AI 极有可能犯错的情况。或需要对错误加上警示说明。** + +当前的模型在很多场景下依然存在胡编乱造的问题,尤其是在你让它自由发挥、或者不给它足够可靠的参考资料时。它有时候会给出看起来很自信但完全错误的答案,甚至会凭空捏造不存在的文件、数据或经历。因此,凡是结果涉及到严肃后果的场景,比如财务报表、法律文书、医疗建议,你都需要在设计里明确加上一层人工校对或者多重检查,不要把模型的输出直接当作可以自动执行的指令。 + +同时,隐私和数据安全也是你必须正视的一环。你要非常清楚哪些数据可以直接发给模型,哪些需要做匿名化处理,哪些干脆不能出现在第三方系统里。对于用户上传的合同、病历、个人身份信息等敏感内容,更要在界面和协议中明确说明你的处理方式,甚至在可能的情况下,为这些场景单独选择更加安全、可控的模型部署方式。 + +更具体的,我们就借一个和智能体 Agent 有关的例子,来说明什么叫真正理解 AI 能力的边界。注意,这里并不是要教你怎么从零实现一个 Agent,也不是要你现在就去追某一种架构,而是想通过这个例子,让你看到一种思考方式:同样是讨论 Agent,有人只是把它当成一个新名词,有人却能借这个话题,把任务拆得很清楚、把边界画得很清楚。 + +有一位长期在一线做 AI 应用的作者 Barret 李靖,给过一段我非常认同的总结,用来说明如何做好 Agent 和是否要利用 AI。它很好地体现了一种成熟的理解方式:先把问题拆开,再谈 AI 的用武之地。 + +> Agent 有两个变量,一个是控制任务走向的 workflow 工作流,一个是控制内容生成的 context 上下文。 +> +> 1)如果 workflow 和 context 的确定性都很高,这类任务就容易被自动化,类似传统 RPA,比如在处理发票处理、表单填报任务时,AI 更多是粘合剂,发挥空间比较有限。 +> +> 2)如果 workflow 确定但 context 不确定,也就是流程固定但输入多变,就需要 Agent 在语义和理解上补全,比如客服问答、合同解析,需要通过外部检索、知识图谱等工具来弥补信息的缺口,让推理结果更符合预期。 +> +> 3)如果 workflow 不确定但 context 确定,也就是输入清晰但走法多样,Agent 就要去自主规划路径,例如市场分析报告生成、个性化推荐等,大多数 End-to-End RL Agent 都擅长做这类任务,因为它们在训练阶段就习得了大量的路径规划和解题思路。 +> +> 4)而当 workflow 和 context 都不确定时,就是最复杂的场景了,既要推理也要探索,像创新方案设计、跨部门信息收集等,这类更偏向于通用型 Agent,它的执行效果,取决于给它配备的工具丰富度,尤其是编程能力要最大化开放,例如让它学会去 Github 找仓库克隆并修改代码来解决问题,让它像人一样干活儿。 +> +> 所以,要把 Agent 做好,首先要明确场景。本质上,自动化解决的是“确定性”问题,而智能化解决的是“不确定性”问题。 + +这个拆解方式的价值,就是把"做一个 Agent"这种模糊概念,转化成了可以具体判断的问题:你面对的任务,确定性和不确定性分别在哪里?当流程和信息都确定时,传统程序就够了;只有当不确定性出现时,AI 的语义理解、模式识别、推理规划能力才有用武之地。但同时,不确定性越高,AI 引入的新风险也越大。在两头都不确定的场景里,AI 的每一步都可能偏离,你很难提前知道它会做出什么选择。这也是为什么很多团队会从第二象限(workflow 确定,context 不确定)开始做起,既能发挥 AI 的理解能力,又能通过固定的流程把风险框在可控范围内。 + +让我们回到这一小节最开始想要解决的问题:什么叫真正理解 AI 的能力边界? + +首先是理解不同场景对 AI 的需求不同。就像前面 workflow 和 context 那个例子展示的:当流程和信息都确定时,AI 其实没什么发挥空间,传统自动化就够了;当流程确定但信息不确定时,AI 的价值在于理解和补全;当流程不确定时,AI 才需要做规划和探索。这个拆解方式的本质,就是在识别不确定性的来源和程度。AI 的核心能力,就是在不确定性中找到模式和关联。这套思路不只适用于 Agent,在图像识别、内容生成、推荐系统等各个领域都同样重要。比如做一个 AI 抠图工具,输入是确定的(一张图片),但边缘识别的准确性、复杂背景的处理能力,就是不确定性所在。 + +![](images/image20.png) + +但是,AI 在解决不确定性的同时,也会引入新的不确定,它的输出是概率性的,可能理解错、推理偏、产生幻觉。而不同场景、不同用户对这种不确定性的容忍度完全不同。所以你还要问: + +**AI 引入的新不确定性,用户和系统能不能承受?** 比如客服场景,如果 AI 理解错了用户意图,用户可以立刻纠正,这种不确定性是可控的。但如果是自动执行财务审批,AI 的一次误判可能造成严重后果,这种不确定性就不可接受。再比如图像生成,如果是给用户做头像美化,生成效果不满意用户可以重新生成,试错成本低;但如果是给建筑设计师生成施工图,哪怕一个细节错误都可能导致工程事故。 + +**AI 的准确率能不能达到这个场景的及格线?而这个及格线,取决于用户用它做什么。** 同样是图像识别,如果是帮用户整理相册、按人脸分类照片,识别准确率 80% 用户也能接受,大不了手动调整几张。但如果是用在安防监控场景,漏掉 20% 的可疑人员就是严重的安全隐患。同样是文本生成,如果是帮用户写社交媒体文案,60 分的创意就够了,用户会自己润色;但如果是生成法律合同条款,95 分都不够,因为一个用词不当可能引发法律纠纷。不同用户、不同用途对错误率的敏感度完全不同,你要清楚你服务的场景,容错空间到底有多大。 + +**当 AI 出错时,有没有办法补救?** 在 workflow 确定的场景里,你可以在关键节点设置人工审核,把 AI 的不确定性控制在局部。但在 workflow 也不确定的场景里,AI 的每一步都可能偏离,你很难判断它什么时候需要介入,这时成本和风险都会急剧上升。比如在图像修复场景,如果 AI 把老照片修复得不够真实,用户一眼就能看出来,可以选择不采用;但在医学影像辅助诊断场景,如果 AI 把异常标记错了位置,医生可能很难发现,后果就严重得多。 + +**你有没有办法衡量和优化 AI 的表现?** 如果这个任务本身就没有明确的对错标准,你怎么知道 AI 做得好不好?如果用户的反馈很滞后,你怎么快速迭代?当你连衡量都做不到时,AI 的不确定性就会变成一个黑盒。比如推荐系统,你可以通过点击率、停留时长这些指标快速评估效果;但如果是生成创意广告文案,什么叫"好"本身就很主观,可能要等到投放后才知道转化率,迭代周期就会很长。 + +真正成熟的判断不是"这里有不确定性,所以可以用 AI",而是"这里的不确定性 AI 能处理,AI 带来的新不确定性我也能管理"。“在这个功能点上,AI 能帮到什么程度,值不值得投入,怎么投入性价比最高。”,我们需要形成这样的判断力,能够帮助我们在之后每一次设计功能、评估方案时,少走很多弯路。 + +# 5. 有了应用,怎样从 0 找到第一批真实用户 + +当你好不容易把一个应用做出来,下一个难题就会变成:如何让第一批真实用户出现。 + +很多团队在这个阶段会有一种错觉,觉得既然东西已经做出来了,接下来只要想办法把它推广出去就行——做宣传、买投放、找曝光,似乎只要让更多人看到,就能自然跑起来。但如果你一上来就急着追求大规模曝光,很容易陷入一个典型的陷阱:烧掉了宝贵的时间和预算,数据上看起来有人来过,却无法验证这个应用到底有没有人愿意持续使用。 + +这个阶段最重要的事情,其实只有一件: **用尽可能小的代价证明,确实有人愿意用这个应用,而且用完之后愿意回来** 。在增长和产品的语境里,这一步通常被称为"冷启动"。 + +冷启动指的是:在几乎一切都是零的情况下,把一个全新的产品推到真正运转起来的那一步。此时你没有用户基础,没有口碑传播,没有搜索量和品牌认知,各项指标几乎都停留在 0。你要在这样一片冷寂的环境里,让第一批真正愿意使用的人出现,并围绕他们搭起一个最初的使用闭环。 + +这和后来在一个已经有用户、有数据的产品上做优化,是完全不同的一类工作,简单来看,我们能按以下四个步骤进行推进: + +1. 首先知道增长可分成 0–1 和 1–N, 知道自己当前只需要搞懂什么 +2. 弄清楚你真正要去找的对象有哪些,不要只盯着终端用户 +3. 在清楚对象的前提下,选择一两条适合自己的冷启动路径 +4. 在资源有限的现实里,学会取舍,把力气收紧到最关键的一小块 + +## 5.1 先分清两个阶段:0–1 和 1–N + +在正式讨论怎么找用户之前,你需要先明确一件事: **增长是分阶段的** 。把所有增长相关的工作混在一起看,只会让你不知道当下该把精力放在哪里。最简单也最实用的划分方式,就是把增长拆成两个阶段:0–1 和 1–N。 + +### 0–1:在没有人用的情况下,怎样冷启动 + +所谓 0–1,指的是从完全没有用户,到出现一小撮愿意真正使用的用户这段过程。冷启动的"冷",就在于一开始几乎所有指标都是零:无下载量,无搜索量,无口碑,你的应用在这个世界上可以说是不存在的。 + +这个时候,你不能指望自然流量和运气,而是要主动出手,为它搭起最初的基础。具体来说,有几件必须完成的事情: + + **找到一小批愿意真正使用的种子用户** ,而不是随便从熟人圈里凑数。这些人必须对你要解决的问题有真实的需求,而不是出于人情或好奇点开看一眼就走。 + + **准备最初的使用体验和供给** ,确保用户进来不会只看到一片空白。即便功能还不完善,至少要让他们能完成一次完整的核心操作,感受到这个应用的价值。 + + **把产品是做什么的、解决什么问题,用简单的话讲清楚** 。在没有品牌背书的情况下,用户给你的耐心只有几秒钟,你必须让他们在最短时间内明白"这个东西对我有什么用"。 + + **想办法拿到第一个触达渠道** ,把这些信息递到潜在用户手里。可能是一个小社群、一个论坛、一个朋友圈,关键不在于规模有多大,而在于能不能精准触达那些真正需要的人。 + +在 0–1 阶段,你真正要考虑的是:把第一批有真实需求的人引进来,并且让他们完成一次从进入到使用再到反馈的闭环。只要这个闭环能跑通,你就证明了这个应用不是空中楼阁,而是真的有人需要、愿意用的东西。 + +### 1–N:在已经有人愿意用的基础上,怎样规模化 + +当你慢慢积累了一批愿意反复使用的用户之后,问题才会变成:怎样从几十人、几百人,慢慢扩大到几千人、几万人,甚至更多。这一段是传统意义上大家常说的增长、扩张、规模化。 + +在 1–N 阶段,你要开始关心的是机制、组织、商业化、品牌、团队等一整套更复杂的问题。比如: + + **是否已经找到相对稳定的获客渠道** ,知道每投入多少预算或时间,大致能带来怎样的新增用户。这时候你需要的不再是碰运气,而是可重复、可预测的增长路径。 + + **有没有开始搭建服务机制** ,比如客服、运营活动、用户教育。用户多了之后,你不可能再像早期那样一对一地手把手教每个人怎么用,必须建立标准化的服务体系。 + + **打算怎样让这个产品赚到钱** ,是订阅、单次付费、增值服务还是其他。商业模式不是一开始就要想清楚的,但当你进入 1–N 阶段,就必须认真考虑如何让这个产品可持续运转下去。 + + **想给用户留下怎样的品牌印象** 。早期你可能只是在小圈子里传播,但当用户规模扩大,你需要思考如何让更多人记住你、信任你,并愿意主动推荐给别人。 + + **团队还缺哪些能力,哪些环节必须有人长期盯着** ,不能完全外包。一个人或小团队能撑起 0–1,但 1–N 往往需要更多角色的配合。 + +这些问题都很重要,但如果你在 0–1 阶段就急着琢磨它们,往往只会让你陷入空转。因为在你还不知道这个产品是不是真的有人愿意用、愿意留下来的时候,谈商业模型和品牌策略,只会把注意力从真正紧要的事情上引开。 + +### 为什么要先专注 0–1? + +对个人开发者、小团队来说,比起 1–N, **真正最该关注的是 0–1** 。原因很简单:如果你连第一批真实用户都找不到,后面所有关于规模化、商业化、品牌建设的讨论都是空谈。 + +0–1 阶段是整个产品生命周期里最脆弱、也最关键的时刻。它决定了你能不能证明这个产品的价值,能不能建立起最初的信任,能不能为后续的增长打下地基。只有当你真正跑通了 0–1,才有资格去考虑 1–N 的问题。 + +接下来,我们要进一步聚焦在 0–1 阶段,把"究竟要去找谁"这个问题说清楚,然后再谈具体的冷启动路径。 + +## 5.2 冷启动对象: 种子用户、供给方、流量方和渠道方 + +不同类型的应用,往往都绕不过几种关键对象: 种子用户、供给方、流量方和渠道方。 + +### 第一类: 种子用户 + + **种子用户是你最早触达的那批使用者。** 他们的典型特征是人数不多,但与你的目标画像高度吻合。你要从他们身上获得的不只是注册和使用数据,更是一手的方向和体验反馈。 + +* 面向个人工具类产品,种子用户可能是那一群在某个问题上长期有痛点的人,比如经常写长文需要整理的内容创作者,频繁准备汇报材料的职场人士,或者每天要和大量资料打交道的学生 +* 对于教育类应用,种子用户可能是一小撮正在准备同一场考试的考生,或者某个年级段的家长 + +冷启动时,你可以先给自己设一个明确的种子用户目标,比如先找到二十到五十个愿意配合的用户,用一两周时间边使用边对话。重点不是数量,而是通过高密度的交流把产品逻辑磨清楚。 + +### 第二类: 供给方 + + **在一些双边或多边平台型产品里** ,单有用户端是不够的。如果没有足够的供给方存在,用户即便被拉进来,也会因为找不到东西可用而迅速离开。 + +**供给方可能是内容创作者、课程老师、服务提供者、商家、司机、房东**等,他们决定了平台的丰富度和吸引力。 + +* 做一个面向设计师的素材平台,你需要先说服一批设计师愿意上传作品,哪怕只是小规模地开放一部分免费素材。否则用户进来看到只是几张示例图,很难形成粘性 +* 做一个线上预约工具,如果没有提前对接好一些有意愿使用的商家或机构,普通用户进来后一样找不到可以实际预约的对象 + +冷启动时,你要非常清楚地知道,自己是在先解决用户端,还是先解决供给端,还是两端同步推进。很多平台在早期都经历过这样的取舍和权衡。只要你意识到这是一个必须正面面对的结构性问题,就已经比那些只想着多拉点终端用户的人走前了一步。 + +### 第三类: 流量方 + +流量方是那些可以在**较短时间内,把一定规模用户注意力导向你的人或机构。它们可能是达人博主、垂直账号、媒体、社区运营者,也可能是某个拥有大量用户的工具平台。** + +* 一个职场工具类产品,如果能说服几个职业发展博主,在内容中自然地介绍你的应用,就有机会在短时间内获得一批对职场效率工具敏感的人 +* 一个针对小红书创作者的选题助手,如果能和几位中腰部博主合作,让他们在实战案例中展示使用过程,那么这批创作者天然就是你的潜在种子用户 + +在冷启动阶段,你不必急着找最大牌的流量方,甚至不必马上去跟头部谈合作。很多时候,那些规模适中但与目标人群高度重叠的小流量方,反而更愿意和你一起做一些定制化的尝试。你要做的是找到这类人或机构,然后拿出一个清晰的合作提案,让对方看得懂你要做什么,能给他们带来什么好处。 + +### 第四类: 渠道方 + +渠道方是那些可以帮助你在 **特定场景下稳定触达目标用户的组织或入口** 。它们和流量方的区别在于: 流量方更偏向一次性的注意力导入,而渠道方更像是长期的、结构化的连接方式。 + +* 学校、培训机构、企业、行业协会、软件服务商,都是典型的渠道方 + 如果你的应用能切实帮助某类机构提高效率、节省成本或提升服务质量,他们有动力把你的产品介绍给自己体系内的大量用户 + +冷启动阶段,你不必妄想一口气拿下大型渠道,可以从一个小范围试点开始。比如和一两个班级、一家小公司、一个本地社群合作,让他们在内部先使用一段时间,然后根据反馈再决定要不要放大规模。 + +把冷启动对象拆开来看,有一个直接好处,就是能避免你所有精力都砸在终端用户拉新上,而忽视了产品结构里其他关键一环。你可以根据自己的产品形态,画一张简单的角色图,写清楚每一类对象是谁、现在有多少、短期内你的目标分别是什么。在这张对象图清楚之后,我们再来谈具体冷启动路径。 + +## 5.3 冷启动方法: 针对不同对象的三条主路径 + +当你知道了自己要去找的人是谁,下一步才是: 通过什么路径去找到他们、服务他们。 + +在实际操作时,你不必拘泥于某一种路径,而是根据自己的资源状况和产品特性做选择。大多数时候,你会以其中一条为主线,另外一两条做补充。 + +### 路径一: 用种子用户破局,优先用好私域 + +这一条主要是针对种子用户和部分供给方。 + +对于大部分刚起步的个人开发者、小团队甚至初创公司来说,最现实、成本最低也最容易掌握节奏的办法,通常都是从自己已有的私域出发。 + +所谓私域,不是某种复杂的运营概念,而是你已经可以主动触达的一批人群,比如你的朋友圈、你参与的行业社群、你有话语权的兴趣群、你维护了一段时间的公众号读者等等。 + +在这条路径里,大致有三个关键动作: + +1. **主动邀请少量精准用户体验** + 关键不在于数量,而在于和目标画像的匹配度。你做的是面向职场新人写简历的工具,就优先找刚毕业一两年的同学、在校准备实习的学弟学妹,而不是已经工作十年的熟人。 + 邀请时尽量说清三件事: + 1. 这是一个为哪类人解决什么问题的应用 + 2. 希望对方大概花多久时间试用一下 + 3. 你会怎样对待他们提供的反馈 +2. **有意识地收集反馈并快速优化** + 种子用户的价值不在于帮你凑数,而在于帮你看清楚产品的盲区。可以通过一对一聊天、小问卷等方式,问清楚: 在什么场景会想起用它、用的时候卡在哪里、哪一部分最有用或完全用不上。 +3. **让种子用户产出首批内容或案例** + 真实使用痕迹就是内容。评价、对比截图、使用故事,都可以是你后续对外介绍时的素材。 + +在这个过程中,要特别克制住一上来就追求大规模扩散的冲动。如果你连这几十个人都没有服务好,仅仅依靠更大的曝光把更多人推到同样的坑里,本质上只是在放大问题,而不是在解决问题。 + +### 路径二: 用内容或福利驱动,给出足够明确的第一理由 + +这一条更多是针对种子用户加流量方,在竞争激烈的赛道里尤其常见。 + +当用户有很多替代选择时,光靠一句新人快来试试很难打动他们,你需要给出一个更明确、更有吸引力的理由,让对方愿意花时间迈出第一步。 + +常见的两种切入方式: + +1. 直接用**实打实的福利做引子** + 1. 新上线的课程平台,可以在早期开出几门高质量的免费课程,或者提供限时优惠名额 + 2. 电商类应用常用补贴红包、低价拼团、满减券等方式,让新用户觉得先来一趟不会吃亏 +2. **用垂直内容持续吸引** + 在抖音、小红书、公众号、播客等平台上,围绕目标用户关心的某个垂直主题,稳定输出有价值的内容,比如职场干货、编程技巧、情绪管理、美食教程、学习方法等等。 + 通过内容吸引来的第一波人,不一定马上转化成你的应用用户,但至少已经对你有基础信任,当你适时抛出工具或应用时,他们更有可能认真看一眼。 + +如果你走的是内容驱动,就要接受这是条比较慢热却回报长线的方式。你需要持续投入精力,把内容做得扎实,避免一开始就被播放量、阅读数牵着走。 **真正能帮你冷启动的是那一小批在内容里找到共鸣的人,而不是短时间涌来又很快消散的流量。** 无论是福利还是内容导入,最后都要落到同一句话上: 一定要把人顺畅地引导到你的应用里,让他们完成一次完整的体验。 + +### 路径三: 借力大平台,在已有生态里找突破口 + +这一条主要是针对供给方、流量方和渠道方。 + +在很多领域,一个新应用如果想完全靠自己建生态,代价非常高。但如果你愿意先把自己当作一个在大平台上的新店、新账号、新插件,冷启动的难度会降低很多。 + +* 电商领域,新店入驻淘宝、拼多多、京东等平台,至少不用从零搭建支付、物流、评价系统。冷启动时常用的方式包括达人带货、站内推广与活动位、直播等 +* 工具类、内容类应用,可以通过为成熟平台开发插件、小工具,把服务上线到开放平台的能力市场,让已经有明确需求的用户更容易找到你 + +这条路背后的逻辑,是 **承认大平台已经把用户聚集在一些特定场景里,而你要做的是在这些场景中找到与你产品匹配的那个小角落** 。借力并不意味着放弃独立性,而是在冷启动阶段,用一个更现实的方式打开局面。 + +## 5.4 资源有限时的取舍:在 0–1 阶段只做最关键的一小块 + +当你已经确认自己还在 0–1 阶段,搞清楚了要服务的对象,也大致选好了冷启动的路径,却发现资源根本不够用。 + +这里的资源不只是钱,还包括时间、精力、人手、注意力、人脉和渠道。冷启动时,如果你一上来就想“多条路一起试”,结果往往是:每天都很忙,事情做了不少,但没有任何一条路径被走深,最后既没有拿到有说服力的结果,也没真正看懂用户。 + +在这个阶段,你需要刻意收缩。目标不是“做得多”,而是“把最关键的一小块做扎实”。可以从三个角度来重构自己的行动方式。 + +### 从目标到具体的任务 + +很多人在冷启动时给自己设的目标是“先看看市场反应”“先把用户做起来”“先拉一波人试用”。这些说法太泛,你很难判断每天做的事情到底有没有真正靠近这个目标。 + +更务实的办法,是把目标收紧成一件具体的小事,比如:在接下来的四周,让二十个符合目标画像的真实用户,在自己的真实场景里,多次完整使用你的应用,并且从他们那里拿到足够具体的反馈。 + + **所谓“细分人群”,不是“任何可能会用到这类工具的人”,而是你可以指着某个标签具体描述出来的一群人。** 比如你做的是一个帮人生成工作汇报的工具,那么目标可以是“互联网运营岗的一到三年从业者”,而不是笼统的“职场人士”。这群人有几个共同点:每个月确实要做汇报,时间有限,又想让内容看起来专业一点,他们的问题是具体而持续的。 + + **“完整使用任务”同样不能含糊** 。以这个汇报工具为例,一次完整任务可能是:用户把最近一周的运营数据和素材整理好,导入工具,生成一版初稿,然后根据推荐的结构和要点修改两到三轮,最后导出 PPT 或文档,真正拿去在部门会上讲。如果用户只是随便点两下,看看大概长什么样,就关掉不再打开,这不能算完整使用。 + +具体反馈要问到足够细。比如: + +* 在导入数据的时候,有没有哪一步看不懂、找不到入口,或者总是点错地方; +* 生成的结构是不是贴近他所在公司的汇报习惯,比如是不是有他需要的“背景–目标–过程–结果”这套格式; +* 哪些页面是他真正拿去用的,哪些页面每次都会删掉; +* 使用之后,他是不是能明显感觉到自己准备一次汇报的时间从三小时缩短到了一个小时,还是只是觉得“好像方便了一点,但也说不上来”。 + +### 不要什么都想试一遍 + +确定了“小目标”之后,下一个问题是:用什么方式去找到这二十个用户,并陪他们把真实场景跑一遍。 + +冷启动的方法很多:写内容、建社群、做投放、找达人、找机构、上平台。但在资源有限的前提下,你需要的不是知道方法有多少种,而是**以现在的你,哪一种方式最自然,最容易持续做下去。** + +如果你平时就习惯写长文,有一批会认真读完你文章的人,那你可以优先从内容出发。比如,写一篇非常具体的实战记录,讲自己是怎么用这个工具准备一次真正的月度汇报的:从拿到原始数据开始,到构思结构,到生成草稿,到修改细节,再到实际在会议室里播放。中间可以插入几张对比截图,展示用工具前后在时间、效果、条理上的差别。文章最后不要只是放一个冷冰冰的下载链接,而是明确说:如果你也是做运营汇报的,愿意和我一起把这个工具打磨好,可以加我或填一个简单表单,我会选二十个人一对一跟进。 + +如果你掌握着几个稳定的社群,比如一个运营交流群,一个校友职场群,那么你更适合从这些“私域”开始。你可以在群里开诚布公地说:我在做一个帮人生成汇报的工具,刚刚能用,但还很粗糙,现在想找一批确实有汇报需求的人,陪我一起把它用顺。自荐的人当中,你再根据岗位、工作内容去挑出最匹配的那一批,单独拉一个小群,在里面请他们试用、发截图、吐槽、提意见,自己每天花时间去跟进。 + +如果你在某个垂直行业里有人脉,比如和几位培训机构老师关系不错,或者认识一家中小公司的业务负责人,可以把试点做到一个班级或者一个小团队里。具体做法可以是:提出一个清晰的试用方案,比如接下来一个月,这个团队所有的周报都尝试用你的工具来生成,你提供实时的支持和调整,作为交换,他们每周和你开一次十分钟的小会,告诉你用得最顺的地方和最难受的地方。 + +### 只打磨最关键的部分 + +当你有了小目标,也选定了主路径,接下来要做的事是给自己加一个只做这一小块的限制。 + +很多团队在冷启动阶段的共同特征是:焦虑。一旦焦虑起来,就很容易往外找新动作:是不是也该弄个短视频账号,拍几个使用教程;是不是也该花一点预算做个小投放试试;是不是应该去联系几个媒体,看看能不能写一篇报道。**每一件看上去都无可厚非,但合在一起的结果,是你每天都在换方向,始终没有哪一条路沉下去。** + + **可以给自己定一个很具体的阶段约束,比如接下来四周,只集中做两件事** :一是围绕那二十个用户,反复优化他们在真实场景下的使用体验,让他们从“勉强能用”变成“基本顺手”;二是沿着你选定的主路径,持续找到少量新用户,并把他们的行为和反馈记录下来,看看和前一批人有什么共性和差异。 + +在这四周里,遇到任何新的想法、新的机会,都先问自己一句:这件事,是否能在这段时间内显著推动那二十个用户用得更好,或者清楚地帮我找到下一批类似的用户? + +这种做法的背后,是一个对冷启动本质的承认:你手里掌握的信息很有限,无法同时在很多方向上做出好判断。与其在十个地方各做一点,不如在一个具体的场景、一个具体的群体里,做出可以反复验证的改进。比如,你可以很明确地看到:在这一批运营新人身上,工具确实把准备汇报的时间缩短了,确实让他们更容易说清楚重点。 + +你需要走通一条“ **找到用户 → 引导使用 → 收集反馈 → 改进体验 → 用户愿意继续用”的闭环。** 之后才知道应该去找什么样的用户,用什么话和他们沟通,在哪个环节最容易出问题,做什么调整可以把他们拉回来。等这条路被你走顺了,再考虑加一个新渠道、试一类新合作,才有意义。 + +# 总结 + +回到最开始的问题:我要做一个应用,这件事到底从哪儿开始,才是靠谱的开始。 + +本篇文章的全部内容,其实就是围绕一个主线在展开:**先搞清楚什么是点子,再看它和用户需求之间的关系,然后一步步把它拆成能做出来、能被用起来、能被打磨好、能被 AI 放大、能找到用户的一整条使用路径。** + +先是第一章,我们从点子本身出发,一个点子不再只是你脑子里的那句感觉挺酷,而是必须面向一类清晰的用户, **扎在某个具体场景里,帮他们完成一件具体任务,并且给出比现状更好的做法** 。你学会从**玩法、用户链路、做什么事情、解决什么问题**这四个维度去审视自己的想法,也开始意识到,点子和用户需求之间有一道很容易忽略的鸿沟。你压抑住自嗨的冲动,开始分辨真需求和假需求,承认好点子和坏点子在一开始就有命运上的差别。然后,你不再被动等灵感,而是学会从自己热爱的生活、人群资产、公开场域和现有产品里主动挖掘线索;再往下,你训练自己用一句话概括一个点子的本质,用 AI 做头脑风暴,在常见方向里找到属于自己的人群和场景差异化。 + +接着在第二章,你不再停留在想,而是 **开始学会动手** 。你学会在**发散和****收敛**之间来回切换,用双钻的方式先把点子铺开,再按用户价值、可行性和时间成本 **收紧到一个真正的可行路线** 。你练习 **从抽象到具体** ,把我想做一个提高效率的应用这类模糊愿望,一层层拆成最小可行动项,直到每一个步骤都变成今天可以动手的任务。你拿起白板或纸笔,先画再做,把一个应用分成入口页、操作页、结果页,画出用户从走进来再到拿到结果的完整链路。你也不会再把参考别人当成抄作业,而是有意识地 **分析别人的导航、表单、结果展示和引导流程,借力现成经验** 。同时,你不再等到产品完全做完才去问用户,而是在原型和半成品阶段,就有意识地边画边问,边做边问,让真实用户尽早介入你的设计。 + +第三章里,你慢慢建立起一套自己的判断标准,来区分什么只是能用,什么可以算好用。你 **不再模糊地说这个应用还不错,而是具体看它有没有帮用户节省时间、降低错误率、减少沟通成本、减轻心智负担** 。你知道一个 **好应用应该让人几乎不用说明书就能上手** ,在关键场景会自然想到你,也懂得好应用背后真正的利他心。你开始把实际问题和用户痛点拆到边际成本的层面,分辨这背后到底是在消耗时间、金钱、心力还是风险。同时,你对 **C 端和 B 端的差异**有了初步认识,明白了前者更在意情绪价值和传播,后者更关心效率、成本、风险和合规。你不再只相信自己觉得好,而是搭起简单的反馈机制,用留存、复访和推荐这三类指标,判断这东西值不值得继续投入,用一次次用户反馈把应用从我觉得好打磨成用户觉得好。 + +到了第四章,你把视角从纯产品,扩展到了 AI 能力。你先 **压住那种为了 AI 而 AI 的冲动** ,认真地问自己两件事:不用 AI,这个应用是否也成立;用了 AI,具体提升了什么。你熟悉 AI 在文本、图片、视频和自动化上的基本能力与边界,知道哪些地方可以放手交给模型,哪些地方必须有人类复核。你不会只停留在实现功能层面,而是盯住一些更本质的指标:任务完成时间有没有缩短,结果质量是否更好,使用频率有没有提升,用户愿不愿为 AI 功能单独付费。 + +最后的第五章,把这一切拉回到一个现实问题上:就算你已经有了一个还不错、甚至用上 AI 的应用,如果没有用户,它的价值依然是零。你先 **学会把 0–1 和 1–N 区分开来,暂时放下所有关于规模化、品牌和组织的宏大问题,把注意力集中在一件事上:先想办法让二十个真实用户用起来** ,并且用完愿意回来。你不再盲目撒网,而是沿着三条主线冷启动:用身边的社群、同行和朋友,慢慢积累种子用户;用内容和有限的福利去吸引第一批愿意尝鲜的人;借力现有平台和渠道,在别人已经有流量的地方为自己搭入口。你也开始按对象去拆冷启动的策略,区分种子用户、供给方、流量方和渠道方,各自用不同方式打通。资源有限时,你不再什么都想做一遍,而是看清自己最擅长和手头最容易启动的一条路,先把这一条路走深走透,而不是一口气铺十个半成品的渠道。 + +如果把这些内容合在一起,你会发现整套方法并不神秘:**从一个靠谱的点子出发,确保它扎根在真实需求上;用画、写、拆的方式,把它****收敛** **成一个最小可行应用;用真实用户和明确指标,一点点把它打磨成好应用;在关键点上合理引入 AI 放大价值;最后,在有限资源下,用合适的冷启动方式找到第一批愿意为它买单的人** 。 + +下一步你只需要放弃过多的幻想,踏踏实实选定其中一个做出来并推出去,让它真正进入真实世界里接受检验。**所有关于点子、方法论、AI 和增长的讨论,最后都要落在一个具体的人、一个具体的场景、一个具体的任务上。** + +正因为如此,一开始做得很粗糙没关系,功能残缺、流程生硬、界面简陋都没关系;推了半天没人理你、更没人愿意注册和付费,也依然没关系。这些都只是过程状态,不是终局结论;它们只是在告诉你下一步可以如何修改,真正重要的是你得有进步,在过程中你需要不断复盘、总结、提高极限、认识更多愿意给你建议的人。 + +在这个阶段中,笔者认为,只需要享受过程就好。就像一个有名的电子故事游戏《去月球》说的那样: + +***"The ending isn't any more important than any of the moments leading to it."*** + +***结局永远也不会比过程更重要。*** + +![](images/image21.png) + +# 📚 Assignment + +1. 根据上述的所有作业,并 **重点参考章节一、二的内容,** 产出细化后的最终点子,使用 Trae 或结合 CLI IDE 制作第一款应用(不用强行加入 AI 功能,你可以制作一个非 AI 应用,一个 AI 应用)。 +2. 为你的应用招募至少 20 个使用者,创建一个群聊维护用户社群。 diff --git a/docs/project/chapter6/images/image1.png b/docs/project/chapter6/images/image1.png new file mode 100644 index 0000000..dea9ce9 Binary files /dev/null and b/docs/project/chapter6/images/image1.png differ diff --git a/docs/project/chapter6/images/image10.png b/docs/project/chapter6/images/image10.png new file mode 100644 index 0000000..ef73982 Binary files /dev/null and b/docs/project/chapter6/images/image10.png differ diff --git a/docs/project/chapter6/images/image11.png b/docs/project/chapter6/images/image11.png new file mode 100644 index 0000000..a317bdb Binary files /dev/null and b/docs/project/chapter6/images/image11.png differ diff --git a/docs/project/chapter6/images/image12.png b/docs/project/chapter6/images/image12.png new file mode 100644 index 0000000..b1794f7 Binary files /dev/null and b/docs/project/chapter6/images/image12.png differ diff --git a/docs/project/chapter6/images/image13.png b/docs/project/chapter6/images/image13.png new file mode 100644 index 0000000..860ebfb Binary files /dev/null and b/docs/project/chapter6/images/image13.png differ diff --git a/docs/project/chapter6/images/image14.png b/docs/project/chapter6/images/image14.png new file mode 100644 index 0000000..1a3fc26 Binary files /dev/null and b/docs/project/chapter6/images/image14.png differ diff --git a/docs/project/chapter6/images/image15.png b/docs/project/chapter6/images/image15.png new file mode 100644 index 0000000..40afaa1 Binary files /dev/null and b/docs/project/chapter6/images/image15.png differ diff --git a/docs/project/chapter6/images/image16.png b/docs/project/chapter6/images/image16.png new file mode 100644 index 0000000..14f4920 Binary files /dev/null and b/docs/project/chapter6/images/image16.png differ diff --git a/docs/project/chapter6/images/image17.png b/docs/project/chapter6/images/image17.png new file mode 100644 index 0000000..cac7ae7 Binary files /dev/null and b/docs/project/chapter6/images/image17.png differ diff --git a/docs/project/chapter6/images/image18.png b/docs/project/chapter6/images/image18.png new file mode 100644 index 0000000..1d26382 Binary files /dev/null and b/docs/project/chapter6/images/image18.png differ diff --git a/docs/project/chapter6/images/image19.png b/docs/project/chapter6/images/image19.png new file mode 100644 index 0000000..0f04893 Binary files /dev/null and b/docs/project/chapter6/images/image19.png differ diff --git a/docs/project/chapter6/images/image2.png b/docs/project/chapter6/images/image2.png new file mode 100644 index 0000000..7609bfe Binary files /dev/null and b/docs/project/chapter6/images/image2.png differ diff --git a/docs/project/chapter6/images/image20.png b/docs/project/chapter6/images/image20.png new file mode 100644 index 0000000..87e0552 Binary files /dev/null and b/docs/project/chapter6/images/image20.png differ diff --git a/docs/project/chapter6/images/image21.png b/docs/project/chapter6/images/image21.png new file mode 100644 index 0000000..c667b28 Binary files /dev/null and b/docs/project/chapter6/images/image21.png differ diff --git a/docs/project/chapter6/images/image3.png b/docs/project/chapter6/images/image3.png new file mode 100644 index 0000000..ba5ac0d Binary files /dev/null and b/docs/project/chapter6/images/image3.png differ diff --git a/docs/project/chapter6/images/image4.png b/docs/project/chapter6/images/image4.png new file mode 100644 index 0000000..6d15f44 Binary files /dev/null and b/docs/project/chapter6/images/image4.png differ diff --git a/docs/project/chapter6/images/image5.png b/docs/project/chapter6/images/image5.png new file mode 100644 index 0000000..eed863b Binary files /dev/null and b/docs/project/chapter6/images/image5.png differ diff --git a/docs/project/chapter6/images/image6.png b/docs/project/chapter6/images/image6.png new file mode 100644 index 0000000..8c84032 Binary files /dev/null and b/docs/project/chapter6/images/image6.png differ diff --git a/docs/project/chapter6/images/image7.png b/docs/project/chapter6/images/image7.png new file mode 100644 index 0000000..5cefa6a Binary files /dev/null and b/docs/project/chapter6/images/image7.png differ diff --git a/docs/project/chapter6/images/image8.png b/docs/project/chapter6/images/image8.png new file mode 100644 index 0000000..9b15b82 Binary files /dev/null and b/docs/project/chapter6/images/image8.png differ diff --git a/docs/project/chapter6/images/image9.png b/docs/project/chapter6/images/image9.png new file mode 100644 index 0000000..ffb380a Binary files /dev/null and b/docs/project/chapter6/images/image9.png differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9135200 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2476 @@ +{ + "name": "vibe-coding", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vibe-coding", + "version": "1.0.0", + "devDependencies": { + "docsify-cli": "^4.4.4" + } + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmmirror.com/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmmirror.com/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect-livereload": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/connect-livereload/-/connect-livereload-0.6.1.tgz", + "integrity": "sha512-3R0kMOdL7CjJpU66fzAkCe6HNtd3AavCS4m+uW4KtJjrdGPT0SQEZieAYd+cm+lJoBznNQ4lqipYWkhBMgk00g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cp-file": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/cp-file/-/cp-file-7.0.0.tgz", + "integrity": "sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "nested-error-stacks": "^2.0.0", + "p-event": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/docsify": { + "version": "4.13.1", + "resolved": "https://registry.npmmirror.com/docsify/-/docsify-4.13.1.tgz", + "integrity": "sha512-BsDypTBhw0mfslw9kZgAspCMZSM+sUIIDg5K/t1hNLkvbem9h64ZQc71e1IpY+iWsi/KdeqfazDfg52y2Lmm0A==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "marked": "^1.2.9", + "medium-zoom": "^1.0.6", + "opencollective-postinstall": "^2.0.2", + "prismjs": "^1.27.0", + "strip-indent": "^3.0.0", + "tinydate": "^1.3.0", + "tweezer.js": "^1.4.0" + } + }, + "node_modules/docsify-cli": { + "version": "4.4.4", + "resolved": "https://registry.npmmirror.com/docsify-cli/-/docsify-cli-4.4.4.tgz", + "integrity": "sha512-NAZgg6b0BsDuq/Pe+P19Qb2J1d+ZVbS0eGkeCNxyu4F9/CQSsRqZqAvPJ9/0I+BCHn4sgftA2jluqhQVzKzrSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "connect": "^3.6.0", + "connect-history-api-fallback": "^1.6.0", + "connect-livereload": "^0.6.0", + "cp-file": "^7.0.0", + "docsify": "^4.12.2", + "docsify-server-renderer": ">=4.10.0", + "enquirer": "^2.3.6", + "fs-extra": "^8.1.0", + "get-port": "^5.0.0", + "livereload": "^0.9.2", + "lru-cache": "^5.1.1", + "open": "^6.4.0", + "serve-static": "^1.12.1", + "update-notifier": "^4.1.0", + "yargonaut": "^1.1.2", + "yargs": "^15.3.0" + }, + "bin": { + "docsify": "bin/docsify" + }, + "engines": { + "node": ">= 10", + "npm": ">= 6" + } + }, + "node_modules/docsify-server-renderer": { + "version": "4.13.1", + "resolved": "https://registry.npmmirror.com/docsify-server-renderer/-/docsify-server-renderer-4.13.1.tgz", + "integrity": "sha512-XNJeCK3zp+mVO7JZFn0bH4hNBAMMC1MbuCU7CBsjLHYn4NHrjIgCBGmylzEan3/4Qm6kbSzQx8XzUK5T7GQxHw==", + "deprecated": "docsify-server-renderer 4.x and below is no longer supported while we investigate the future of SSR and SSG for Docsify", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.3", + "docsify": "^4.12.4", + "node-fetch": "^2.6.6", + "resolve-pathname": "^3.0.0" + } + }, + "node_modules/docsify-server-renderer/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/docsify-server-renderer/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/figlet": { + "version": "1.9.4", + "resolved": "https://registry.npmmirror.com/figlet/-/figlet-1.9.4.tgz", + "integrity": "sha512-uN6QE+TrzTAHC1IWTyrc4FfGo2KH/82J8Jl1tyKB7+z5DBit/m3D++Iu5lg91qJMnQQ3vpJrj5gxcK/pk4R9tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.0" + }, + "bin": { + "figlet": "bin/index.js" + }, + "engines": { + "node": ">= 17.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/global-dirs/-/global-dirs-2.1.0.tgz", + "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "1.3.7" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmmirror.com/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.7", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmmirror.com/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "1.2.9", + "resolved": "https://registry.npmmirror.com/marked/-/marked-1.2.9.tgz", + "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/medium-zoom": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/medium-zoom/-/medium-zoom-1.1.0.tgz", + "integrity": "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/nested-error-stacks": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true, + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parent-require": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/parent-require/-/parent-require-1.0.0.tgz", + "integrity": "sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmmirror.com/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinydate": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/tinydate/-/tinydate-1.3.0.tgz", + "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tweezer.js": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/tweezer.js/-/tweezer.js-1.5.0.tgz", + "integrity": "sha512-aSiJz7rGWNAQq7hjMK9ZYDuEawXupcCWgl3woQQSoDP2Oh8O4srWb/uO1PzzHIsrPEOqrjJ2sUb9FERfzuBabQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/update-notifier/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-notifier/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargonaut": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/yargonaut/-/yargonaut-1.1.4.tgz", + "integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^1.1.1", + "figlet": "^1.1.1", + "parent-require": "^1.0.0" + } + }, + "node_modules/yargonaut/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargonaut/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b203a49 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "vibe-coding", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "docsify serve docs -p 3000", + "start": "npm run dev" + }, + "devDependencies": { + "docsify-cli": "^4.4.4" + } +} +