普通视图
千亿诉讼临近 OpenAI指控马斯克搞“突袭”
连载05-Claude Skill 不是抄模板:真正管用的 Skill,都是从实战里提炼出来的
别再直接 Fork 别人的 Claude Skill:真正有用的 Skill,都是从项目里长出来的
AI Coding 系列第 05 篇 · 核心工具
我第一次批量导入公开 Skill 模板的时候,是真的以为自己走了捷径。
GitHub 上一堆 star 很高的仓库,code review、需求分析、文档编写、调研、拆任务,看起来什么都有。我当时的想法很直接:既然别人已经把常见工作流整理好了,我直接 fork 一份,全量导入,不就能让 Claude 立刻更稳、更懂项目吗?
结果用了几天,我反而越来越不放心。
不是因为它“明显做错了什么”,而是因为它总在看起来没问题的地方出问题。格式完整,措辞专业,检查项也不少,可真正让我在项目里反复吃亏的那几件事,它一次都没替我盯住。异步链里是不是又漏了 await,这次 migration 有没有回滚方案,新同学是不是又顺手写了 throw new Error(),数据库 schema 改了之后 Prisma 类型是不是也一起更新了。
它会提醒一堆“大家普遍都应该注意”的东西,却不知道“我们团队到底最怕什么”。
后来我才明白,问题不是 Skill 机制不好,而是我导入的根本不是“自己的 Skill”,只是别人整理好的经验。
这些经验当然有价值,但它们解决的是共性问题,不会天然长成你项目里的“肌肉记忆”。
真正有用的 Skill,恰恰应该做一件事:
把那些你本来总要重复提醒、总会漏掉、总会在项目里反复踩坑的动作,固化成默认动作。
也就是一句话:
Skill 的本质,不是收藏经验,而是固化默认动作。
这篇文章会从最基本的边界讲起,一路走到 SKILL.md、源码机制、任务类型和可执行能力。内容不少,但我尽量只保留真正有助于你在项目里把 Skill 用起来的部分。
![]()
先说结论
如果你只记住这几条,这篇文章就已经值回时间:
- 对所有任务都生效的规则,写进
CLAUDE.md;只对某类重复任务生效的,做成Skill;只对这一次有效的,写进Prompt - 只有“输入相对稳定、输出有模式、而且容易漏步骤”的任务,才值得沉淀为 Skill
- 通用 Skill 模板只能当原材料,项目级 Skill 必须自己裁剪、自己维护
-
description不是装饰字段,它承担了触发场景的职责,最好把Use when...直接写进去,关键词前置、长度克制 - Claude 启动时主要只看 frontmatter,Skill 正文在真正触发时才按需载入
-
allowed-tools是权限边界,不是行为建议;paths是条件激活,不是说明文字 - 第一个 Skill 不要挑最关键的任务,先拿中等风险任务练手
一、公开 Skill 模板为什么一开始很香,后来却越用越别扭
我现在反而会对“看起来很全”的公开 Skill 模板保持一点警惕。
不是因为它们没用,而是因为它们太容易制造一种错觉:好像什么都覆盖到了,但真正最重要的东西其实没进去。
公开模板最常见的问题,不是方向错,而是下面这三种。
1. 太宽泛
它什么都管一点,但什么都不够深。
它会告诉你“注意异常处理”“注意性能”“注意安全”,这些当然没错。但这些话本身不构成你项目里的工作流。它不知道你们统一用的是 AppError,不知道你们数据库变更必须检查回滚,也不知道你们哪几个目录历史包袱最重。
2. 太嘈杂
50 行模板里,真正有价值的可能只有 5 行。
剩下的 45 行不是完全没用,而是在和那 5 行争夺 Claude 的注意力。对于 agent 来说,规则不是越多越强。很多时候,8 行写透项目约束的 Skill,比 50 行“样样都提一点”的模板更有用。
3. 太不像你的项目
这点最致命。
公开模板知道“大家普遍应该注意什么”,但不知道“你们团队反复死在哪些地方”。而真正有价值的 Skill,恰恰应该把那些项目特有、团队高频踩坑的东西固化下来。
说得更直白一点:你把一个新同事扔进团队,给他一份行业通用培训材料,当然比什么都不给强;但如果你不告诉他“我们团队最容易出错的是哪三件事”,他依然干不好你最在意的活。
所以正确姿势不是“找一个最全的模板直接用”,而是:
先借鉴,再裁剪,最后只留下真正属于你项目的那几条。
![]()
二、先把 Prompt、CLAUDE.md、Skill 这三件事彻底分清楚
很多人不是不会写 Skill,而是一开始就把这三件事混在一起了。
判断方法其实很简单,只问一个问题:
这个要求的作用范围到底有多大?
- 这个要求对所有任务都成立吗?如果是,放
CLAUDE.md - 这个要求只对某一类任务成立吗?如果是,做成
Skill - 这个要求只对这一次成立吗?如果是,写进
Prompt
举几个特别典型的例子:
“所有 throw 必须是 AppError”
这是全局规则。不管你是在写新功能、修 bug,还是做重构,都要遵守。它应该进 CLAUDE.md。
“代码审查时按固定顺序检查数据库、异步和错误处理”
这只在 code review 这种任务里才触发,它不是全局规则,而是任务模板,所以应该做成 Skill。
“这次先只分析原因,不要动代码”
这只对当前这次任务有效,应该写进 Prompt。
最容易搞混的是 CLAUDE.md 和 Skill。它们都能约束 Claude 的行为,但本质完全不同:
-
CLAUDE.md是永远生效的规则 -
Skill是遇到对应任务才触发的模板
如果要打个比方:
-
CLAUDE.md是交通规则 -
Skill是导航路线 -
Prompt是你这次上车前临时交代的一句话
这三层一旦分清楚,后面 80% 的混乱都会自动消失。
![]()
一个常见误判:很多问题根本不需要写 Skill
我后来发现,很多人想写 Skill,并不是因为真的存在一个稳定、重复、值得沉淀的任务,而是因为这一次和 Claude 协作得不顺。
比如目标没说清,边界没收紧,上下文没给够,或者你真正缺的是一条全局规则,却误以为自己需要一份任务模板。这个时候你如果急着把它沉淀成 Skill,本质上只是把一次性的混乱模板化。
几个很常见的误判场景是:
- 这次需求本身还在摇摆,连你自己都没想清楚要什么
- 这个问题只发生过一次,下次未必还会以同样的形状出现
- 你真正缺的是全局约定,比如错误处理、目录规范、命名规则
- 你只是想表达“这次先别改代码”“这次先只分析原因”这种一次性约束
写 Skill 之前,先问自己一句话:
这个问题下次还会以差不多的形状再来一次吗?如果不会,先别急着写 Skill。
三、什么时候一个任务真的值得被沉淀成 Skill
不是所有重复任务都值得沉淀。
我现在给自己的标准其实很克制,就一句话:
同一类任务做了三次以上,而且每次都要重新给 Claude 解释背景。
反过来说,如果某个任务每次背景和目的都完全不同,就不值得沉淀。比如“写文档”这个动作本身很常见,但公司文档、API 文档、用户手册的写法完全不同,它们应该是三个不同的 Skill,而不是一个叫“写文档”的通用模板。
在真正开始写之前,我会先做三个检查。
1. 输入是否稳定
“根据 Figma 设计稿生成 React 组件”这种任务,输入格式相对稳定,比较适合沉淀。
“根据 SQL 查询结果生成图表”这种任务,每次数据格式和图表类型都可能差很多,Skill 会很难写得稳。
2. 输出是否有共同模式
“写 Pull Request 描述”很适合,因为它天然就有固定框架:改了什么、为什么改、怎么测试。
但“和 AI 讨论技术方案”这种任务,每次深度、重点、结论都不同,就不太适合硬沉淀成一个模板。
3. 有没有容易漏掉的关键步骤
最值得沉淀成 Skill 的任务,通常不是“最复杂”的任务,而是那些不特别提醒就容易漏一步的任务。
Skill 最有价值的地方,不是让 Claude 变得更聪明,而是把你每次最容易忘的检查项,固化成默认动作。
所以一个任务如果同时满足下面三点:
- 输入相对稳定
- 输出有共同模式
- 总有一两步容易漏
它就很值得沉淀成 Skill。
![]()
四、从一个真实痛点开始,走完 Skill 的提炼过程
光讲判断标准还是有点抽象,不如走一遍完整例子。
代码审查,几乎每个后端工程师每周都在做,也是最容易进入“重复解释”困境的任务。用它来走一遍完整的 Skill 提炼过程会很清楚。
你反复踩的坑
假设你们团队每周都做代码审查,而且总在重复盯这几件事:
- 有人改一个功能,顺手动了三个不相关模块
- 新同学不知道项目里统一用
AppError,直接throw new Error() - Promise 链里漏了
await - 数据库查询没有索引,或者潜在 N+1 没被看出来
这就是非常典型的“该沉淀 Skill 的信号”。
先设计内容,再去想格式
一个好 Skill,先别急着写文件。先把内容层想清楚,只要回答四个问题:
1. 什么时候用
不是写“代码审查”四个字,而是写清楚触发场景。
❌ 代码审查
✅ 当我提交 PR 前,检查我的 TypeScript 后端代码是否符合项目约定
差别在于:模糊的描述会让 Claude 在不该用的时候乱触发,而具体的场景描述更容易精准命中。
2. 按什么顺序做
步骤尽量不要超过五步。
你从公开模板里借灵感,但通用模板有 50 行,而你真正关心的可能只有四件事:改动范围、错误处理、异步操作、数据库查询。
1. 读完整个改动的 diff,确认改动是否只涉及这个 PR 的范围
2. 检查错误处理:所有 throw 都必须是 throw new AppError()
3. 检查异步操作:Promise 链是否有遗漏的 await
4. 检查数据库查询:是否有 N+1 问题,关键查询是否 explain 过
3. 输出长什么样
不要写“请清晰输出”。这种话几乎没有约束力。直接给格式。
🔴 Critical: ...
🟡 Warning: ...
✅ Suggestion: ...
Summary: X critical issues to fix before merge.
4. 什么时候不适用
写清楚边界比写清楚功能更重要。
比如:
- 不审查 UI 层代码
- 不关注代码风格
- 改动超过 500 行先拆 PR
这些“我不做什么”的声明,往往比“我会做什么”更能防止 Claude 越界。
到这里,你脑子里其实已经有一个能用的 Skill 了。下一步只是把它放进 Claude Code 认识的格式里。
五、真正落到 SKILL.md 文件层,哪些字段值得你认真写
一个完整的 SKILL.md,通常会长这样:
---
name: code-review
description: Review TypeScript backend code before merging. Use when asked to review code, check a PR, or verify implementation before committing.
allowed-tools:
- Read
- Grep
- Glob
- Bash(git diff *)
argument-hint: "[PR 分支名或文件路径]"
arguments:
- target
---
# Code Review
## 步骤
1. 读取 ${target} 的改动 diff
2. 检查错误处理:所有 throw 必须是 throw new AppError()
3. 检查异步操作:Promise 链是否有遗漏的 await
4. 检查数据库查询:是否有 N+1 问题
## 输出格式
🔴 Critical: ...
🟡 Warning: ...
✅ 通过: ...
这里最值得你认真写的,其实是下面几个字段。
name
名字别太抽象。要让人一眼知道它是做什么的。像 helper、utils、tools 这种名字几乎没有路由价值,远不如 code-review、pr-summary、api-conventions 这种具体命名。
description
这是现在最关键的字段。它不只是“简介”,还承担了“触发场景”的职责。你最好直接把 Use when... 写进去,而不是写一句空话。更重要的是,别把它写成长段说明文。关键词尽量前置,长度最好控制在 250 字符左右,太长往往只会稀释命中信号。官方还特别提醒,description 最好用第三人称去写,像 “Analyzes pull requests...” 这种句式,比 “I can help...” 或 “You can use this...” 更稳。
allowed-tools
它决定这个 Skill 具备哪些能力。这个字段后面我会在源码部分展开讲,因为它比很多人想象的更“硬”。
arguments
让 Skill 接受参数,比如目标文件、目录、分支名。${target} 会在正文里被替换成你传进去的实际值;如果你喜欢按位置拿参数,也可以用 $0、$1 这类方式。
还有几个很好用,但不是每次都要上的字段。
argument-hint
告诉调用者这个 Skill 期待什么参数。
model: haiku
简单任务可以指定更轻量的模型,直接省成本。像格式化、重命名、简单改写这类工作,很多时候没必要上更重的模型。
paths
让 Skill 只在某些路径下激活。适合模块边界明确的项目。
context: fork
高风险操作放进独立上下文,避免污染主会话。
disable-model-invocation: true
禁止 Claude 自动触发,只允许你手动 /skill-name 调用。部署、发版、发邮件这类有副作用的 Skill,应该优先考虑加上。
大多数 Skill 根本不需要把字段填满。真正实用的思路不是“功能全”,而是“正好够用”。
如果一个 Skill 只是做常规代码审查,name、description、allowed-tools、arguments 往往就够了。只有当你真的遇到参数化、模块隔离、上下文隔离这些需求时,再往上加。
SKILL.md 不是整个 Skill,它只是入口
很多人以为一个 Skill 就是一份 SKILL.md。其实不是。
更实用的做法通常是:把 SKILL.md 控制在足够短、足够清楚的范围里,让它承担“入口”和“调度”职责;真正长的规范、示例、脚本都拆出去。
一个 Skill 目录完全可以长这样:
my-skill/
├── SKILL.md
├── reference.md
├── examples/
│ └── sample.md
└── scripts/
└── helper.py
这里的关键点不是“可以放很多文件”,而是:这些文件不会自动加载,必须在 SKILL.md 里显式引用。
比如:
## 参考资料
- 完整的 API 规范见 [reference.md](reference.md),需要查接口细节时读它
- 期望的输出格式见 [examples/sample.md](examples/sample.md)
这个设计和前面说的懒加载是同一套思路:不是 Skill 触发时把所有材料都灌进上下文,而是只在真正需要的时候再去读。
所以:
-
reference.md适合放项目特有知识,比如内部 API 规范、禁用库、架构约定 -
examples/适合放期望输出样例,帮助 Claude 对齐格式 -
scripts/适合放真正可执行的辅助脚本,让 Skill 不只是“描述怎么做”,还能“先把上下文准备好”
这点很重要,因为它决定了 Skill 的上限不是“几行 prompt”,而是“一个有入口、有知识、有执行能力的局部工作流”。
Skill 放在哪,决定它是谁的能力
这点很容易被忽略,但工程上很重要。
同样是一个 Skill,放在不同位置,意义完全不一样:
-
~/.claude/skills:你个人所有项目都能用,适合个人长期习惯 -
.claude/skills:只在当前项目生效,适合团队项目约定 -
<plugin>/skills:跟着插件走,适合做模块化分发
如果不同层级里恰好有同名 Skill,优先级也不是平均的。官方规则更接近:企业级配置优先于个人级,个人级优先于项目级;插件 Skill 因为带命名空间,通常不会和前面这些直接撞名。
官方文档里甚至把这件事讲得很直接:Skill 存放的位置,本身就是它的作用域设计。
这背后的工程含义非常大。
如果你把一个强项目耦合的 Skill 放进个人目录,它就会带着这个项目的假设跑到别的仓库里;反过来,如果你把一个本该跨项目复用的通用 Skill 只塞在项目目录里,它的复用价值又被锁死了。
在 monorepo 里,这件事更有意思。Claude Code 会自动发现子目录下的 .claude/skills/。也就是说,你完全可以让 packages/frontend/.claude/skills/ 只服务前端包,让 packages/backend/.claude/skills/ 只服务后端包,而不是把所有知识都堆在仓库根目录。
这时 Skill 就不只是“提示词文件”,而是团队知识的分发机制:
- 个人层的 Skill,固化的是你的工作习惯
- 项目层的 Skill,固化的是团队约定
- 包级 Skill,固化的是模块边界里的局部知识
如果你能把这层想清楚,很多“这个规则到底该放哪”的问题,答案会比只看内容本身更清楚。
六、如果只停在经验层,这篇还差半口气:我后来去翻了源码
前面这些判断,靠经验其实也能总结出来。
但我后来还是不太满足。因为有几个问题如果不看实现,心里总会悬着:
-
description到底是不是自动触发的关键? -
allowed-tools到底只是提示,还是硬限制? -
paths到底是真过滤,还是只是写给人看的说明?
我后来去翻了一遍源码,结论是:这些字段比我一开始以为的更“硬”。
1. 为什么触发逻辑主要看 frontmatter,而不是正文
loadSkillsDir.ts 里有一个函数 estimateSkillFrontmatterTokens,注释写得非常直接:
/**
* Estimates token count for a skill based on frontmatter only
* (name, description, whenToUse) since full content is only loaded on invocation.
*/
export function estimateSkillFrontmatterTokens(skill: Command): number {
const frontmatterText = [skill.name, skill.description, skill.whenToUse]
.filter(Boolean)
.join(' ')
return roughTokenCountEstimation(frontmatterText)
}
这段代码背后的意思非常重要。
Claude Code 启动时,主要只把每个 Skill 的 frontmatter 信息算进上下文。Skill 正文不是一开始就全量塞进去,而是在你真正触发它的时候才加载。
这直接解释了两件事。
第一,Claude 不是先把你整篇 Skill 读完再判断要不要触发,它先看的就是前面这几行。换句话说,触发效果主要取决于 frontmatter,不取决于正文写得多漂亮。
第二,Skill 多不等于上下文立刻爆炸,因为启动时压进去的不是全文,而是 frontmatter。
源码里保留了 whenToUse 这个概念,但从现在的文档实践看,推荐做法已经更偏向把触发描述直接写进 description。所以对大多数人来说,最稳的策略不是纠结“要不要额外写一个触发字段”,而是把 description 写得具体、可命中、带触发场景。
比如:
description: Review TypeScript backend code before merging. Use when asked to review code, check a PR, or verify implementation before committing.
比“代码审查 Skill”这种描述强太多了。
源码注释里其实还暗含了一个很实用的提醒:触发描述不是越长越稳。冗长的 whenToUse 或 description 不会线性提高命中率,很多时候只是在白白消耗首轮缓存和注意力。所以对这个字段最好的优化,不是“多写一点”,而是“把真正会命中的词放到前面”。
2. 为什么 allowed-tools 不是建议,而是权限边界
这一点是我看源码之后感受最强的一处。
Skill 执行时,getPromptForCommand 会在返回内容之前把 allowedTools 写进工具权限上下文:
getAppState() {
const appState = toolUseContext.getAppState()
return {
...appState,
toolPermissionContext: {
...appState.toolPermissionContext,
alwaysAllowRules: {
...appState.toolPermissionContext.alwaysAllowRules,
command: allowedTools,
},
},
}
}
这说明 allowed-tools 不是“提醒 Claude 尽量这样做”,而是权限层的强制限制。
比如一个 code review Skill 只开放 Read、Grep、Glob 和 Bash(git diff *),那它就不是“理论上不该写文件”,而是从架构上根本没有写文件的能力。Bash(git diff *) 这种写法也不是装饰,它真的只允许 git diff 开头的命令,其他 Bash 调用会被挡住。
这让我对 allowed-tools 的理解完全变了。它不是“不信任模型”,而是最小权限设计。就像你给数据库只读账号只开 SELECT 权限,不是因为你怀疑这账号会作恶,而是因为这个任务本来就不该拥有写权限。
3. 为什么 paths 不是说明文字,而是条件激活机制
源码里,带 paths 的 Skill 在加载时会被单独分流到一个 conditionalSkills Map:
// Separate conditional skills (with paths frontmatter) from unconditional ones
for (const skill of deduplicatedSkills) {
if (skill.type === 'prompt' && skill.paths && skill.paths.length > 0
&& !activatedConditionalSkillNames.has(skill.name)) {
newConditionalSkills.push(skill)
} else {
unconditionalSkills.push(skill)
}
}
// Store conditional skills for later activation when matching files are touched
for (const skill of newConditionalSkills) {
conditionalSkills.set(skill.name, skill)
}
// 最后只返回无条件的 Skill
return unconditionalSkills
这段逻辑的含义是:带 paths 的 Skill,根本不会像普通 Skill 一样直接进入启动时上下文。它会先待在一个“条件激活区”里,只有当你在会话里碰到了匹配路径的文件,它才会被真正激活。
这点对复杂项目非常有价值。
比如你给支付模块写一个 paths: src/payment/** 的 Skill,在你处理用户系统、文章系统、管理后台时,这个 Skill 对 Claude 几乎是隐身的。只有当你真的进入 src/payment/ 相关文件,它才“出现”。
这也是我现在很认同的一种团队实践:不要在根目录堆一个什么都想管的大 Skill 集合,而是让复杂模块在自己的目录附近维护自己的 Skill。
4. 为什么大型项目不该只在根目录维护一套总 Skill
还有一个很容易被忽略,但工程上非常实用的机制:Claude Code 会从当前文件所在目录一路向上寻找 .claude/skills。
源码大概是这样:
// Walk up to cwd but NOT including cwd itself
while (currentDir.startsWith(resolvedCwd + pathSep)) {
const skillDir = join(currentDir, '.claude', 'skills')
// ...check if exists, then load
currentDir = dirname(currentDir)
}
// Sort by path depth (deepest first) so skills closer to the file take precedence
return newDirs.sort((a, b) => b.split(pathSep).length - a.split(pathSep).length)
这里最关键的是最后一行:deepest first。也就是说,越靠近当前文件的 Skill,优先级越高。
这意味着你放在 src/auth/.claude/skills/ 里的 Skill,可以自然覆盖根目录下更通用的同名 Skill。对 monorepo 或大仓库来说,这个机制非常好用:
-
packages/api/.claude/skills/可以放 API 专属 Skill -
packages/web/.claude/skills/可以放前端专属 Skill - 根目录只保留真正的全局规则
如果把上面四点放在一起看,设计 Skill 的顺序其实会变得很清楚:
- 先把 frontmatter 写准,再去打磨正文步骤
- 先按最小权限收紧
allowed-tools,再考虑要不要给更多能力 - 只有模块边界明确时再上
paths,不要为了“高级”硬加 - 多目录项目优先做“离代码更近”的局部 Skill,而不是维护一个大而全的总模板
5. 为什么长对话里,Skill 不会轻易“失忆”
还有一个很多人会担心的问题:会话一长、上下文一压缩,前面调过的 Skill 会不会就悄悄失效了?
从实现思路看,Claude Code 不是简单把它们扔掉,而是会把最近调用过的 Skill 重新注入压缩后的上下文。工程上你可以把它理解成:Skill 不是“一次触发完就全靠模型自己记住”,而是一个可以被系统再次带回来的工作单元。
当然,这也不是说你可以无限制地把 Skill 写成超长文档。实现上会有保留预算,比如最近调用的 Skill 只会保留前一段核心内容,而不是把所有正文永久塞在上下文里。所以前面那条原则依然成立:把 frontmatter 写准,把正文写短,把真正长的材料拆到 reference 或脚本里。
![]()
七、不是所有 Skill 都应该让 Claude 自动触发
到这里,其实已经够你写出一个基础可用的 Skill 了。
但如果你真的准备在项目里长期用,接下来有一个问题迟早会遇到:
这个 Skill 到底应该让 Claude 自动触发,还是只能我手动触发?
这背后其实对应两种完全不同的 Skill。
1. 参考型 Skill:给 Claude 补充背景知识
这类 Skill 的作用不是“执行一个任务”,而是“把某个项目知识注入到当前工作里”。
比如 API 设计规范、错误处理约定、数据库命名规则。这些东西你希望 Claude 在写代码、改代码、review 代码时,只要场景合适就自动想起来。
这类 Skill 的特点是:
- 倾向自动触发
- 内容会留在主对话上下文里
- 更像“局部规范”而不是“独立任务”
比如:
---
name: api-conventions
description: API design patterns and conventions for this codebase. Use when writing or reviewing API endpoints.
---
响应格式统一用 { success, data, timestamp }
禁止在 controller 层直接写 SQL,通过 service 层操作
所有异步函数必须有 try-catch,错误统一 throw new AppError()
2. 任务型 Skill:给 Claude 一个要完成的动作
这类 Skill 有明确边界,通常还可能带副作用。
比如 /deploy、/send-release-email、/prepare-release-notes、/migrate-db。这类 Skill 更像一段可执行流程,而不是知识注入。
这类 Skill 的特点是:
- 通常应该手动触发
- 有副作用时最好加
disable-model-invocation: true - 有风险时再配
context: fork
比如:
---
name: deploy
description: Deploy the application to production. Manual trigger only.
context: fork
disable-model-invocation: true
allowed-tools:
- Bash(npm run test)
- Bash(npm run build)
- Bash(git push *)
---
1. 跑完整测试:npm run test
2. 确认测试全绿后构建:npm run build
3. 推送到部署分支
4. 等待 CI 完成并检查健康状态
这里的关键不是字段多了,而是触发权变了。
你真正要想清楚的问题是:
这件事我愿不愿意让 Claude 自己判断“现在该触发了”?
如果答案是“不愿意”,那就不要让它自动触发。
3. disable-model-invocation: true 和 user-invocable: false 不是一回事
这是一个很容易混淆,但又非常关键的区别。
默认情况下,你和 Claude 都可以调用一个 Skill。你可以手动 /skill-name,Claude 也可以在觉得合适时自动加载它。
但很多人会把下面两个字段混为一谈:
disable-model-invocation: trueuser-invocable: false
它们看起来都像“限制调用”,其实限制的是两件完全不同的事。
disable-model-invocation: true 的意思是:Claude 不能自己触发,只有你能手动触发。 这类 Skill 适合部署、发版、发邮件、推送消息这类你必须自己掌握时机的动作。更重要的是,它还会把这个 Skill 的描述从 Claude 的常驻上下文里拿掉,平时的上下文成本直接归零,只有你手动调用时才完整加载。
user-invocable: false 的意思则是:你不能从 / 菜单里把它当命令来点,但 Claude 仍然可以在合适时自动用它。 这类 Skill 更适合背景知识,比如老系统架构、内部缩写、遗留约定。这些东西你希望 Claude 在相关任务里自动想起来,但你并不需要一个显眼的 /legacy-context 命令天天挂在菜单里。
所以更准确的判断应该是:
- 有副作用、要你亲自控制时机:
disable-model-invocation: true - 只是背景知识、不适合被人手动当命令点:
user-invocable: false - 想限制 Claude 到底能不能调用某些 Skill:去配权限规则,而不是只盯菜单显示
这组区别值得写进脑子里,因为它直接决定了 Skill 的“触发权”到底属于谁。
![]()
八、Skill 的天花板:从静态模板到可执行能力
前面几节讲的,主要还是“怎么把经验写成一个好模板”。但如果 Skill 只能放静态文字,它的上限其实并不高。
真实项目里,很多任务依赖的是实时信息:当前 PR 的 diff、评论区讨论、今天的测试结果、最新 schema、CI 状态。你当然可以在 Skill 里写“先去看这些东西”,但这样一来,最关键的一步又变回 Claude 自己先兜一圈去找。
Claude Code 里真正更有意思的地方是:Skill 不只是 prompt 模板,它还可以在触发瞬间先准备上下文。
1. 动态注入:先拿真实数据,再交给 Claude
比如你要做一个 PR 总结 Skill,不一定要让 Claude 先自己去猜该看哪些信息,你可以直接让 Skill 触发时先把它们准备好:
---
name: pr-summary
description: Summarize the current pull request. Use when asked to review or summarize a PR.
context: fork
allowed-tools:
- Bash(gh *)
---
## 当前 PR 信息
- diff:!`gh pr diff`
- 评论区讨论:!`gh pr view --comments`
- 涉及文件:!`gh pr diff --name-only`
## 你的任务
基于以上真实数据,总结这个 PR 做了什么、为什么改、有哪些潜在风险。
这个 Skill 真正触发时,前面的命令会先执行,输出直接注入到 prompt 里。Claude 拿到的不是“请你去看看 PR”这种模糊要求,而是已经准备好的真实上下文。
这也是为什么前面说 scripts/ 不是装饰。如果一个 Skill 需要先查状态、先取数、先做一轮预处理,那它就不再只是“告诉 Claude 怎么做”,而是在执行前把材料也一起备好了。更复杂一点时,你完全可以把逻辑放进 scripts/,再通过 CLAUDE_SKILL_DIR 去调用目录里的脚本,让 Skill 触发时先跑一轮取数或整理。
2. 这件事为什么重要:它决定了 Skill 的上限
一旦你理解了动态注入这层能力,就会发现 Skill 的上限根本不只是“几行 prompt”。
它可以同时承担三件事:
- 定义触发条件
- 限制工具权限
- 在执行前准备实时上下文
换句话说,Skill 不是只能做静态模板,它完全可以长成一个带入口、带约束、还能主动取数的局部能力单元。
不要只把 Skill 当成“写给模型的一段话”,而要把它当成“一个局部工作流的入口”。
3. 从架构位置看,Skill 不是 Tool,也不是 Agent
如果再往上抽一层,我现在对 Skill 的理解是:
-
Tool是原子能力 -
Skill是任务知识和操作规约 -
Agent是执行与编排单元
这个分层不一定是官方唯一表述,但作为工程心智模型非常有用。因为一旦把 Skill 放到 Tool 和 Agent 中间去理解,很多判断都会顺下来:
- 该写成脚本的,别硬写进 Skill 正文
- 该放进
CLAUDE.md的全局规则,别误沉淀成任务 Skill - 该拆给 SubAgent 的复杂协作,别硬让一个 Skill 扛完
Skill 真正做的事,不是替代 Tool,也不是替代 Agent,而是把“什么场景下、按什么步骤、调用哪些能力”组织成可复用的任务单元。
九、真正让 Skill 变靠谱的,不是写出来,而是验证出来
这是我觉得官方 best practices 里最容易被忽略、但最能拉开水平差距的一点。
很多人写 Skill 的方式是:先凭感觉写一版,再上项目里试试。这样当然也能跑,但它很容易陷进一种错觉里: 你以为自己在“优化 Skill”,其实只是一直在补想象中的问题。
官方更推荐的思路其实更工程化:
先准备评测样例,再写 Skill。
最轻量、也最实用的做法,是先找出三个真实场景:
- 一个 Claude 原本就能做得不错的
- 一个 Claude 容易漏步骤的
- 一个 Claude 容易理解偏、触发错或者输出不稳的
先不用 Skill 跑一遍,记录基线。看它具体错在哪:
- 是没想到要用这个 Skill
- 是触发了,但没读对参考资料
- 是步骤顺序不稳
- 是输出格式飘了
- 是该脚本处理的确定性工作,被它自己“猜”过去了
然后再写最小版本的 Skill,只补刚才暴露出来的那几个缺口,而不是一上来把所有可能性都写满。
把这个过程压缩成一句话,其实就是:
- 先找失败样例
- 再写最小 Skill
- 再看它是不是真的修掉了失败样例
如果三轮下来都没有明显提升,那大概率不是 Skill 写得不够多,而是这个问题根本不该沉淀成 Skill。
不要只看结果,还要看 Claude 是怎么“导航”这个 Skill 的
只看最终结果对不对还不够,更重要的是看 Claude 在过程中到底是怎么用这个 Skill 的。
官方专门建议观察 Claude 实际怎么使用 Skill,而不是只看最终结果对不对。比如:
- 它是不是总在错误顺序里读文件
- 它是不是老是忽略某个引用文件
- 它是不是每次都反复读同一个 reference,那这部分也许该往
SKILL.md主体里提 - 某个 examples 文件是不是从来没被读过,那它可能根本没价值,或者信号太弱
这些观察特别重要,因为它能直接反推出信息架构是不是合理。
我现在会把一个 Skill 是否成熟,简单看成四个问题:
- 会不会在该触发的时候触发
- 触发后会不会按预期去读材料
- 执行过程会不会漏掉关键步骤
- 输出结果能不能稳定复现
如果你团队里会混用不同模型,官方还建议至少跨你计划使用的模型测一遍。不是因为所有模型都得兼容,而是因为有些 Skill 对提示强度和结构依赖更高,换模型后会暴露出你原来没看到的问题。
说到底,Skill 的成熟度,不是靠“我觉得写得挺全”来判断,而是靠一组真实任务能不能稳定跑通来判断。
十、写 Skill 时,最常见的四种设计模式,以及最容易踩的反模式
这里先说清楚:下面这四种名字,不是官方文档逐字给出的固定术语,而是我结合 Anthropic 官方 best practices 做的工程化归纳。
但它们确实能覆盖大多数项目里真正会遇到的 Skill 设计问题。
1. 模板驱动模式:解决“输出不稳定”
这类 Skill 的核心不是让 Claude 更会想,而是让它更稳定地按结构输出。
适合:
- 周报
- PR 描述
- 事故复盘
- 评审报告
它的关键不是“给一个模板”,而是把模板当成输出接口。模板负责格式,SKILL.md 负责路由和规则。模板里不要塞判断逻辑,也不要把一个模板写成一套小程序。
如果一个模板已经长到一百多行,而且开始出现大量条件分支,通常不是你模板写得认真,而是职责已经混了。
2. 脚本增强模式:解决“结果不稳定”
这类 Skill 的核心是:确定性的事交给脚本,不要交给模型猜。
适合:
- 指标统计
- CSV 解析
- 正则匹配
- Git / PR / CI 状态抓取
- 需要预处理的上下文准备
官方对这件事的说法很直白:solve, don't punt。能脚本算出来的,就别只写一句“请 Claude 自行分析”。
这类 Skill 真正提高的,不是文风,而是可靠性。你把概率型推理替换成确定性执行,整个 Skill 的下限会明显抬高。
3. 知识分层模式:解决“上下文过载”
这是官方反复强调的 progressive disclosure 思路。
也就是:SKILL.md 只做入口和导航,把大块知识拆进 reference/、examples/、forms/ 这类文件里,按需读取,而不是一次灌满。
这类模式适合:
- 领域知识很多的 Skill
- 不同子领域差异很大的 Skill
- 需要高级用法、边界情况、案例库的 Skill
它的关键不是“拆文件”,而是“拆得有层次”。官方明确不建议深层嵌套引用。最稳的做法是所有重要材料都从 SKILL.md 一层直达,别让 Claude 从 advanced.md 再跳 details.md 才看到真正关键信息。
4. 工具隔离模式:解决“能力边界失控”
这类模式最容易被低估。
很多人写 Skill 时只关注“让它能做事”,但真正稳定的 Skill 往往同样重视“让它不能乱做事”。
适合:
- 部署
- 发版
- 写数据库迁移
- 调外部系统
- 会改文件、发消息、推远端的任务
这一类 Skill 的核心组合通常是:
-
allowed-tools收到最小 - 必要时配
context: fork - 不希望自动触发时加
disable-model-invocation: true
它不是在限制 Claude 的创造力,而是在设计这个能力包的安全边界。
最常见的反模式
如果把前面四种模式反过来看,最常见的坑基本也就集中在下面这些地方:
- 一个 Skill 管太多事,什么都想覆盖,最后什么都不够准
-
description写得很空,只写“处理文档”“帮助分析”这种谁都能套上的话 - 一上来给一堆方案,不给默认路径,让 Claude 自己从五六种做法里摇摆
- 模板里写判断逻辑,或者把本该脚本做的事丢给模型推理
- reference 层级太深,真正关键信息藏在第二跳、第三跳文件里
- 把会过期的信息硬写进 Skill,比如时间敏感规则、旧接口切换说明
- 默认假设工具和依赖都已经装好,结果一跑就断
- 在路径里写 Windows 反斜杠,跨环境直接出问题
你会发现,所谓反模式,本质上就是一句话:
该约束的地方没约束,该拆开的地方没拆开,该交给脚本的地方还在让模型猜。
十一、完整案例:把一个通用 code review 模板,提炼成你项目真正需要的 Skill
上面说了这么多抽象原则,不如走一遍完整例子。
假设你们团队每周都做后端代码审查,而且总在重复盯这几件事:
- 有人改一个功能,顺手动了三个不相关模块
- 新同学不知道项目里统一用
AppError,直接throw new Error() - Promise 链里漏了
await - 数据库查询没有索引,或者潜在 N+1 没被看出来
这就是非常典型的“该沉淀 Skill 的信号”。
第一步:先确认痛点到底是什么
这一步别着急写模板,先把“你们到底在反复出什么问题”说清楚。
很多团队的问题不是“没有 code review”,而是每次 review 的注意力都被分散了。真正高频出错的点,永远是那几类项目特有的约束。
所以要沉淀的不是“代码审查”这四个字,而是你们团队在代码审查里最容易漏掉的那几类检查。
第二步:从公开模板里提取真正有用的部分
这时候公开模板就有用了,但它的用途不是直接上生产,而是当素材库。
假设你找到一个 50 行的通用 code review 模板。你真正该提取的,可能只有下面这几类东西:
- 逻辑正确性,尤其是异步操作
- 项目约定的遵守,比如
AppError、错误处理模式 - 数据库相关的风险,比如 N+1、索引、查询范围
- 改动范围是否聚焦,不要顺手改不相干文件
剩下那些跟你们项目关系不大的部分,就应该果断删掉。
第三步:把它压缩成一个真正能用的 Skill
最后落地出来的 Skill,应该更像这样:
---
name: code-review
description: Review TypeScript backend code before merging. Use when asked to review code, check a PR, or verify implementation before committing.
allowed-tools:
- Read
- Grep
- Glob
- Bash(git diff *)
argument-hint: "[PR 分支名或文件路径]"
---
# Code Review Skill
## Steps
1. 读 $ARGUMENTS 的改动 diff,确认改动是否只涉及这个 PR 的范围(不要顺手改无关文件)
2. 检查错误处理:所有 throw 都必须是 throw new AppError(),不能 throw new Error()
3. 检查异步操作:Promise 链是否有遗漏的 await,错误是否被正确 catch
4. 检查数据库查询:是否有 SELECT * 的懒惰写法,是否明显的 N+1 查询,关键查询是否 explain 过
## Output Format
Issues found (Critical → Warning → Info):
🔴 **Line 45**: Missing `.select()` in Prisma query - this will fetch unnecessary columns
🟡 **Line 67**: Potential N+1: loop inside `posts.map()` should use `Promise.all()`
✅ **No AppError violations** — all errors properly handled
Summary: 1 critical issue to fix before merge.
## Caveats
- 不审查 UI 层代码(只关心后端逻辑)
- 不关注代码风格(那是 prettier 的事)
- 如果一个改动涉及多个不相干功能,分别提交 PR 再 review
你会发现,到这一步之后,Skill 就不再是“通用模板的中文版”了,而是你们项目真正有用的一个局部工作流。
50 行公开模板,最后可能只剩下 4 个真正属于你项目的核心关注点。但恰恰是这 4 个点,才决定它到底值不值得用。
第四步:在真实使用里继续迭代
Skill 从来不是一次写完的。
比如你用了两周之后,又发现一个常见问题:改了数据库 schema,但忘记更新 Prisma 类型。那就把它加进去:
4.5. 检查 Prisma 类型:如果改了数据库,Prisma schema 和生成的 types 是否都已更新
这时候你会发现,Skill 真正的价值不是“第一次写出来”,而是在真实工作里被持续打磨。
十二、Skill 的维护节奏,比第一次写出来更重要
Skill 不是写好就扔。
如果你写完之后三个月不看,它很快就会从“项目经验”重新退化成“历史遗留文档”。
我更推荐一个更贴近实际的轻量节奏:
前一两周
高频使用,快速迭代。每次用完就问自己三个问题:步骤是不是太复杂?输出是不是太啰嗦?有没有漏掉今天刚踩到的新坑?
稳定之后每周一次
回顾一次。看看最近有没有经常被遗漏的步骤,有没有新的痛点需要加入。对 Skill 这种高频小迭代的东西来说,按周看通常比按月看更合适。
每个月做一次清理
把已经不再是问题的注意事项删掉,把那些已经变成全局共识的规则移进 CLAUDE.md。这一步的重点不是加内容,而是防止 Skill 越写越胖。
Skill 应该越用越精炼,而不是越写越臃肿。
十三、一个特别反直觉,但很重要的经验:第一个 Skill,故意别写最重要的任务
这条我非常想单独拿出来讲。
因为很多人第一次沉淀 Skill,会本能地想挑一个最关键的任务,比如“生产环境发布前检查”“数据库迁移前审查”“支付流程改动 review”。
但工程上更稳的做法,其实正好相反。
大多数人写的第一个 Skill,质量都不会太高。触发条件偏模糊,步骤偏啰嗦,输出格式也不够稳定。这很正常,因为你第一次做这件事时,对“什么是好的 Skill”还没有直觉。
如果你一开始就把它用在最关键的任务上,一旦写得不够好,伤害会非常直接:要么关键场合出问题,要么你从此对这套机制失去信心。
更好的策略是:先拿一个重要程度中等、容错率比较高的任务练手。
比如:
- 写周报
- 生成 PR 描述
- 做一次常规 code review
先跑两周,迭代两三次,等你对 Skill 的节奏有感觉了,再去沉淀真正关键的流程。
第一个 Skill 的目的,不是直接解决最大的问题,而是让你学会怎么写 Skill。
十四、如果你今天就想开始,可以直接做这三个动作
别先想着搭一整套系统,直接从最小动作开始:
任务一: 列出你最近一个月里重复做过三次以上的任务。用第一节的判断法(对所有任务成立?→ CLAUDE.md;只对某类任务成立?→ Skill;只对这次成立?→ Prompt),确认你要处理的是 Skill 还是 CLAUDE.md 的事。
任务二: 为这个任务写一个 Skill。先想清楚四个问题(什么时候用、按什么顺序、输出长什么样、什么时候不适用),然后包装成 SKILL.md。目标是精炼——大多数好 Skill 的有效指令不超过 10 行。记住:第一次的目的是练手,不是写出完美的 Skill。
任务三: 选三个你最近真的遇到过的场景,先不用 Skill 跑一遍,再用 Skill 跑一遍,对比它到底有没有少漏步骤、少返工、少补充解释。然后在 SKILL.md 里补一行今天发现的问题(哪个步骤漏了,或者哪个注意事项需要加)。这就是 Skill 的维护节奏,也是最轻量的评测方法。
真正好的 Skill,几乎都不是第一次就写对的,而是在实际使用里慢慢长出来的。
下篇预告
第 06 篇:Sub-agents 实战——什么时候应该拆任务,怎么设计子任务边界
单个 Claude 实例有上下文上限,复杂任务拆成多个子任务让 Sub-agents 并行处理,理论上能大幅提速。但什么时候值得拆?拆错了会有什么代价?下一篇会拆开 Sub-agents 的真实适用场景,以及最常见的过度设计陷阱。
写在最后
公开 Skill 模板当然有用,但它的价值更像脚手架,而不是成品。
你不需要一个很复杂的 Skill 系统。你需要的,往往只是把团队最容易反复犯错的那几件事提前写下来,让 Claude 每次都替你盯住。
如果你现在想动手,就直接做这三步:列出最近一个月里重复做过三次以上的任务;用"三问法"确认它该放 CLAUDE.md、Skill 还是 Prompt;先写一个只有 5 到 10 行的版本,实际用一次,再立刻改第一轮。不要追求第一版完美——Skill 的价值,从来不是"写出来的那一刻",而是"它开始帮你减少重复错误的那一天"。
评论区想聊一个问题:你现在最卡的,到底是"写不出规则",还是"没分清哪些该放 CLAUDE.md、哪些该做成 Skill"? 这两个问题看起来很像,但解法完全不同。
觉得这篇有帮助,点个赞让更多工程师看到 👍
AI Coding 系列持续更新。用别人的 Skill 模板是起点,不是终点。真正管用的 Skill,只有你自己的项目才能提炼出来。
3月北京二手房成交近2万套,创近15个月新高
硅基同事埋的坑,我用2小时才填平:Nuxt 4 路由踩坑:可选参数 [[id]] 与 [id] 的区别
在开发博客系统时,遇到了一个路由不生效的问题:/section 可以访问,但 /section/id 却始终无法匹配。折腾了一番后发现是 Nuxt 文件路由的可选参数语法理解有误。
问题背景
需求很简单:
-
/blog→ 显示博客列表,默认选中第一篇文章 -
/blog/kubernetes-1-32-release→ 显示博客列表,选中指定文章
最初创建了两个路由文件:
pages/
├── [section].vue # 匹配 /blog
└── [section]/
└── [id].vue # 匹配 /blog/xxx
结果:/blog 正常,/blog/kubernetes-1-32-release 却始终匹配不上。
问题原因
最初采用了两个独立文件的方式:
pages/
├── [section].vue # 匹配 /blog
└── [section]/
└── [id].vue # 匹配 /blog/xxx
这种结构看似合理,实则存在多个问题:
- 代码重复:两个文件 95% 代码相同,维护成本高
- 状态同步:需要额外处理跨页面状态共享
-
路由匹配:某些情况下 Nuxt 无法正确区分两个路由,导致
/blog/xxx匹配失败
解决方案
Nuxt 提供了可选路由参数语法 —— 双括号 [[param]],用一个文件同时处理两种情况:
pages/
└── [section]/
└── [[id]].vue # 同时匹配 /blog 和 /blog/xxx
核心区别
| 语法 | 含义 | 匹配示例 |
|---|---|---|
[id] |
必需参数 |
/blog/abc ✅ / /blog ❌ |
[[id]] |
可选参数 |
/blog/abc ✅ / /blog ✅ |
[[id]] 是 Nuxt/Vue Router 的特殊语法,表示该参数可以存在也可以不存在。
实现方案
合并后的 [[id]].vue:
<script setup>
const route = useRoute()
const section = route.params.section
const articleId = route.params.id // 可能为 undefined
// 有 id 用 id,没有则用 firstArticleId
const activeArticleId = ref(articleId || firstArticleId)
// 监听路由变化(SPA 导航时更新)
watch(() => route.params.id, (newId) => {
if (newId) activeArticleId.value = newId
})
// 点击文章时更新 URL
const handleSelectArticle = (id) => {
activeArticleId.value = id
navigateTo(`/${section}/${id}`, { replace: true })
}
</script>
关键点
-
route.params.id可能为undefined:需要提供默认值 - 添加路由监听:SPA 内导航时 URL 变化不会重新执行 setup,需要 watch
-
navigateTo更新 URL:选中文章时同步 URL,支持分享和书签
调试技巧
在排查过程中,发现 Nuxt 4 的 console.log 在 SSR 阶段可能被过滤。一个实用的做法是在 composable 中添加显眼前缀:
export function useContentArticles(section: string) {
console.log('>>> [useContentArticles] section:', section)
console.log('>>> [useContentArticles] cache keys:', Object.keys(sectionDataCache))
// ...
}
终端输出:
>>> [useContentArticles] section: blog
>>> [useContentArticles] cache keys: [ 'blog', 'interview', 'nuxt4' ]
总结
| 场景 | 推荐方案 |
|---|---|
| 单一页面 + 可选子路径 | [[id]].vue |
| 完全不同的两个页面 | 分开两个文件 |
| 参数必需 | [id].vue |
可选参数 [[param]] 是 Nuxt 文件路由的利器,用好了可以大幅减少代码重复。但要注意处理 undefined 的情况和路由变化的监听。
延伸阅读
nuxt4完整系列,持续更新中...
内容有帮助?点赞、收藏、关注三连!评论区等你 💪
拿下豪华种子轮,一家明星AI公司宣布倒闭
作者/吴琼
报道/投资界PEdaily
唏嘘一幕。
近日,知名AI创业公司Yupp宣布:将停止服务,于4月15日正式关闭。要知道,这距离Yupp产品上线才不到一年时间。
曾经,Yupp的故事颇具前景:瞄准AI模型评测赛道——通过免费模型服务吸引用户评测,再将测评数据卖给模型厂商。犹记得2024年,公司拿下3300万美元(约合2.2亿元)的豪华种子轮,身后聚集a16z合伙人、Google首席科学家、Twitter联合创始人等超45位天使投资人。
只是,Yupp却以极为荒诞的方式失败:投资人的钱还没花完,AI技术的演进就让其市场不复存在。而这一切,就发生在短短几个月之间。
拿下豪华种子轮,一家明星AI公司宣布倒闭
从成立到关门,Yupp只存活了22个月。
故事始于一支精英团队;创始人兼CEO Pankaj Gupta职业生涯遍布Twitter、Google,Coinbase等硅谷最顶尖的科技公司,且此前就有成功创业经验;联合创始人兼AI负责人Gilad Mishne曾是GoogleX的机器学习负责人;首席科学家Jimmy Lin是麻省理工学院博士、滑铁卢大学教授。
早在2010年,三人于Twitter相识,共同构建和优化大规模推荐与搜索系统,成为此后共同创业的契机。
直至2024年6月,他们共同成立Yupp。创业的理由很简单:彼时AI竞争激烈,全球涌现的大模型层出不穷,Yupp最初的设想是帮助用户比较和选择最佳模型,以应对AI可能带来的幻觉问题。
而另一边,大模型表现不仅取决于算力与算法表现,同样依赖人类反馈,AI企业往往基于这些反馈数据来优化、改进模型。他们正是从这两边需求中找到机会,创建一家AI模型评估平台。2025年6月,Yupp正式上线。
说起来,Yupp的商业逻辑很简单:用户在平台输入问题后,平台将从数百个AI模型池中抽取两个,并展示两份答案,用户可以反馈哪些模型更好并给出理由;完成反馈后,平台会随机给予用户一定数量的积分,相应积分可用于继续调用AI模型,还可以兑换成现金。
这里聚集了市面上最炙手可热的大模型,从ChatGPT、Claude,到Gemini、DeepSeek,还有Grok、Llama……超500种模型,任意选择,全部免费。
对用户来说,只需要在一个平台上就能体验多种模型,不仅免费还能收获一些零花钱,于是Yupp迅速走红。官方显示,目前Yupp已经吸引超过130万用户注册。
精英团队加上风头正劲的大模型赛道,投资人迅速集结。成立不久,Yupp就完成由a16z合伙人Chris Dixon领投的种子轮融资,金额高达3300万美元(约合人民币2亿元)。即便放眼硅谷,这样的大额种子轮也并不多见。
更为豪华的还有跟投阵营——谷歌首席科学家Jeff Dean、Twitter联合创始人Biz Stone、Pinterest联合创始人Evan Sharp、Perplexity首席执行官Aravind Srinivas、Cred首席执行官Kunal Shah、斯坦福大学的四位教授(Dan Boneh、Chris Re、Nick McKeown、Balaji Prabhakar)……超45位天使投资人,几乎聚集了AI圈最权威的面孔。
“Yupp的设计将人类的判断转化为一种可再生经济资源。随着新的交互不断涌现,数据会‘过期’,从而形成一个良性循环:更多的使用带来更及时的评估;更及时的评估催生出更优秀的模型;更优秀的模型吸引更多用户。”对于这笔投资,彼时a16z言语之中充满��待。
但这一切在几个月后戛然而止:Pankaj Gupta和Gilad Mishne共同在社交平台宣布:停止Yupp的服务。如今再点开Yupp官网,基于文本、图像、编码等多个分类,赫然躺着多个大模型排行榜。只是,它们再也不会更新。
谁杀死了它?
“我们的产品与市场契合度不够高”,Yupp告别博客中写到。
很难想象,一家AI创业公司最后却因AI发展太快而倒闭。在Pankaj Gupta的描述中,“仅在过去一年时间内,AI模型的能力格局发生了巨大变化,而且还将继续快速变化。未来不仅仅是模型,而是Agent系统。”
2025年Yupp上线时,AI行业的主流交互方式还是Chatbot——用户输入提示词,模型返回文本。这一模式下,“哪个模型回答得更好”确实有其市场。但AI的进化速度显然超过他们的预期。
短短几个月,行业风向发生巨大变化,Agent成为行业主题。正如年初爆火的“龙虾”OpenClaw,用户不再只是获取信息,而是需要事情被完成。相对应的,底层模型需要连接数百种工具和子系统,最终协同完成任务。
还没到达顶峰,Yupp就落入用户、客户双双流失的境地。
一方面,Yupp吸引用户的方式简单粗暴——免费且有利可图。但当头部模型的回答质量差距缩小,用户通过Yupp进行多模型比较的需求自然下降。
另一方面,虽然人类反馈数据对AI模型的后训练至关重要,但是Yupp提供的仅仅是普通消费者在免费使用时随手点击的偏好。而模型厂商的主流选择是,与Scale AI、Mercor等头部玩家合作,用PhD级别的专家提供高质量的强化学习反馈。Yupp与对手们提供的数据质量,显然不在一个量级。
尤其当下,对AI的评判标准变为“哪个Agent能帮我更好地完成任务”,相反,对于Chatbot层面的模型评估就显得没那么重要了。但对于Agent的评估,并非过去简单对比两段文字就能完成,而是需要在真实工作场景中验证,已经不再是普通用户所能实现的。
如此,Yupp终究难以生存下去。
那场豪华种子轮,意外成了Yupp最后的高光时刻。唏嘘的是,Yupp甚至没来得及用完融资的钱。“剩余资金将返还给我们的投资人”,Pankaj Gupta写到。一段创业故事就此结束。
AI洗牌潮,99%创业项目将消亡
“人间一年,AI一天”,大家如此形容当下AI演进速度。
自ChatGPT横空出世以来,全球掀起一场AI创业浪潮。AI为许多行业带来全新的想象空间,也带来过往难以想象的创业机会。中国信通院数据显示,截至2025年9月,全球人工智能企业达到37664家。未来几年,这一数据仍将以指数级上升。
这也带来了AI时代最魔幻一幕。
一边,AI正以前所未有的速度,催生新一批高估值公司。2025年全年诞生的新晋独角兽中,有六成是AI独角兽。许多公司从成立到成为百亿估值独角兽,不过短短一两年时间,这在过往互联网时代是难以想象的。
但另一边,AI技术爆发式迭代,从文本生成到图像、视频创作,从被动问答到主动执行任务的智能体系统,行业每隔数月就迎来一次范式革新。高压之下,许多AI初创公司从高光登场到黯然落幕的周期也被极度压缩。
稍不注意,就被AI快车抛下了。
最近轰动的当属Sora,这是OpenAI推出的文生视频模型Sora。2024年初,Sora以一段长视频引爆全网,就连马斯克也发出一句意味深长的感慨:“人类愿赌服输”。但两年过去,等来的却是一纸关停信号。
仅仅存活25个月,Sora“猝死”的理由很残酷。据美国《福布斯》杂志估算,Sora项目每年的运行成本高达50多亿美元,单月算力成本突破千万美元级别;但自上线以来,Sora应用程序内的总收入仅约210万美元,高昂的运维成本与微薄的收入之间形成鲜明对比。
一边是种子选手黯然退场,一边是新技术仍在以近乎疯狂的速度迭代突破。新旧交替之间,只留下无尽唏嘘。AI赛道的残酷正在于此:竞争白热化到甚至来不及为倒下的玩家驻足默哀,下一轮技术浪潮便已汹涌而至。
第一批AI项目开始倒闭了。早些时候,曾被看作是欧洲AI行业希望之星的Robin AI,被挂上了破产网站。究其原因,虽其瞄准的“AI+法律”行业颇具前景,但Robin AI里的功能,完全可以通过Claude等完成;缓慢的技术迭代速度更是成为原罪,使其逐渐与竞争对手拉开差距。最后,Robin AI被资本集体抛弃了。
想起几个月前,硅谷一篇名为《99%的AI初创公司将在2026年消亡》的文章刷屏。作者Srinivas Rao直言:“当下的AI繁荣,不过是互联网泡沫的翻版。”
他以2000年互联网浪潮为例,当时互联网被认为会改变世界,后来证明确实如此。电子商务、搜索引擎、社交媒体、在线服务,都在之后二十多年里迅速发展。当时同样涌现互联网创业潮,但很多企业在技术真正成熟之前,就因为资金、商业模式、竞争等种种问题被淘汰。
这样一幕也会在AI重演。“企业的发展轨迹与技术的发展轨迹有着天壤之别”,桥水基金创始人瑞·达利欧此前判断,“这类周期的常态是,技术会一直向前,但初期绝大多数企业都会倒闭,只有极少数能够存活。”
保持敬畏,且行且珍惜。
本文来自微信公众号“投资界”,作者:吴琼,36氪经授权发布。
浅谈人工智能时代的用户体验:Agent Experience 导论
凯文沃什获特朗普提名出任下一任美联储主席,提名确认程序恐遭延误
CSS 技巧:CSS 单位使用指南
![]()
写完《现代 CSS 中的相对单位》这节课之后,我原本以为大家会带着一种“原来如此”的轻松感继续往下写代码。但很快,私下就有不同的同学问:为什么这里不用 px ?什么时候该用 rem ?vw 和 0% 到底有什么区别?移动端布局什么该用 rem 还是 vw ?这些问题不仅在一个地方出现,而是在不同平台、不同时间被反复提起。
于是,我决定单独用一节课,把这些零散的问题集中起来,好好聊一聊。我们不再孤立地看某一个单位,而是把 px 、em 、rem 、% 、视窗单位(如 vw 、vh)、容器查询单位(如 cqw 、cqh)以及 ex 、ch 和 lh 放在同一个语境下,重新理解它们的作用,以及更重要的——如何做出选择。
CSS 单位选择的本质:不是用什么,而是跟随谁变化
在实际开发中,很多人一开始都会本能地选择 px。它足够简单、直接,几乎不需要额外思考——设计稿量多少就写多少,浏览器呈现出来的结果也高度一致。这种“所见即所得”的确定性,在项目初期确实非常有吸引力。
但随着项目逐渐复杂,这种看似稳妥的选择,往往会开始暴露出问题。页面在不同设备上看起来似乎“差不多”,却总差那么一点协调感;字体系统变得难以统一,局部调整容易牵一发动全身;为了适配各种尺寸,不得不引入越来越多的媒体查询;而组件一旦脱离原有上下文,复用时也常常出现不可预期的表现。
很多人会因此怀疑,是不是一开始就不应该使用 px 。但从结果来看,问题并不在于某一个具体单位,而在于我们没有在一开始建立一套清晰的尺寸关系。
这正是这节课真正要解决的核心问题。CSS 单位表面上是在描述“长度”,但它们更本质的作用,其实是在定义一种关系——元素的尺寸,应该跟随变化。
当你写下一个值时,本质上是在做一次“绑定”。使用 px ,意味着它几乎不跟随外部环境变化;使用 % ,它会依赖父元素;使用 em ,它会响应当前元素的字体大小(font-size);使用 rem ,则是跟随根元素(html);视窗单位(如 vw 、vh)绑定的是浏览器的视窗,而 cqw 、cqh 这样的容器查询单位,则让元素直接跟随其所在的父容器。
也就是说,你并不是在选择“用哪种单位更好”,而是在决定这个元素应该“听谁的”。是跟随屏幕变化,还是跟随父级容器?是依赖全局排版系统,还是保持自身的稳定?一旦换一个角度去看,很多原本让人纠结的问题,其实会自然消失。
你会开始意识到,px 并不是错误,它只是选择了不响应变化;rem 也不是万能方案,它只是把变化集中在一个全局入口;vw 看起来很灵活,但有时会让组件失去边界感;而容器查询单位,则是在回答一个更现代的问题——组件是否可以只关心自己所在的空间,而不是整个页面。
接下来,我们不会从定义出发逐个讲解每个单位,而是先建立一套“尺寸关系模型”,再去分析每种单位适合绑定的对象,最后总结出一套可以直接应用在项目中的选择策略。目标不是让你记住所有单位的细节,而是让你在任何场景下,都能快速判断——这里的尺寸,究竟应该跟随谁变化。
CSS 单位的尺寸关系模型
如果把前面所有零散的讨论收拢在一起,其实可以得到一个非常关键的结论:
CSS 单位并不是一堆彼此独立的“语法选项”,它们本质上构成了一整套尺寸依赖关系系统。
一旦从这个角度去理解,就可以把所有单位放进一个统一的框架中来看待,也就是所谓的“CSS 单位的尺寸关系模型”。
这个模型的核心可以用一句话概括:每一个尺寸,本质上都是依附在某个参照物上的结果。 你写下的从来不是一个孤立的数值,而是一种“绑定关系”。也就是说,当你在写 CSS 时,你真正做的事情并不是定义一个长度,而是在决定这个长度应该“跟谁产生关系”。
基于这个视角,我们就可以把所有常见单位重新整理一遍。不再按语法分类,而是按照它们“依附谁”来划分。这样整理之后,会得到一个非常清晰的结构——一个由不同“关系层级”组成的尺寸模型。
绝对关系:不跟随任何对象
在所有尺寸关系中,“绝对关系”是容易理解的一类,它几乎不依附于任何外部对象。典型代表就是 px 。当你使用 px 时,本质上传达的是一种明确的意图:这个尺寸应当保持稳定,不随着环境变化而改变。
这种特性让 px 在很多场景下非常有价值。比如边框、阴影、精细对齐等细节控制,都需要高度确定性和可预测性,这正是 px 的优势所在。它提供了一种接近“所见即所得”的体验,让你可以精确地掌握视觉结果。
但问题在于,一旦把这种“固定性”扩展到布局层面,比如用在容器尺寸、间距系统或整体排版上,就会开始削弱页面的弹性。页面难以自然适配不同设备,响应能力变差,后期往往需要额外的补丁来弥补。
所以,与其把 px 简单理解为“绝对单位”,不如换一个更本质的视角来看——它其实是一种主动拒绝建立关系的选择。
全局关系:跟随根元素
在“在局关系”这一类中,所有尺寸都会统一依附于同一个源头——根元素(html)。最典型的代表就是 rem 。当你使用 rem 时,本质上传达的是一种明确的选择:这个尺寸不由局部环境决定,而是完全跟随全局系统。
这种机制非常适合用于构建设计系统。无论是字体层级、间距体系,还是组件的基础尺寸,都可以基于 rem 来建立。一旦调整根元素的字号(font-size),整个页面就会按照既定比例整体缩放,从而实现一致且可控的响应效果。
不过,这种“全局绑定”也有一个前提:你必须建立清晰且合理的全局规则。如果没有这一步,rem 并不会减少复杂度,只是把原本分散在各处的问题,集中转移到了全局层面。换句话说,rem 并不是简化问题的工具,而是放大设计系统价值的工具。
局部关系:跟随当前上下文
“局部关系”这一类单位,强调的是尺寸与当前上下文之间的依赖关系,而不是与整个页面或全局系统的绑定。典型代表是 em 和 % 。其中,em 相对于当前元素的字体大小(font-size),而 % 通常相对于父元素的尺寸计算。需要知道的是,% 是一个较为复杂的单位,它应用在不同属性值时,依附的参照物是不同的。
当你使用这类单位时,其实是在表达这样一种意图:这个元素的大小,不由全局决定,而是由它所处的环境来决定。换句话说,它就是“就地适应”的。
这种特性在组件内部尤其有用。例如按钮、卡片这类 UI 元素,其内边距、间距、子元素尺寸等,都可以随着组件自身的变化而自然缩放,从而形成更具弹性的结构。
不过,这种“局部依赖”也带来一个隐患:它是可以层层嵌套的。一旦组件结构变得复杂,多层 em 或 % 叠加之后,最终的计算结果往往不再直观,甚至难以追踪。因此,在使用这类单位时,需要对层级关系保持足够的控制,避免无意中引入过深依赖链。
环境关系:跟随视口
“环境关系”这一类单位,直接把尺寸绑定到浏览器视口,也就是屏幕本身。典型代表是 vw 、vh ,以及 vmin 、vmax 等。使用这些单位时,元素的尺寸应当随着屏幕大小按比例变化——屏幕多大,它就多大。
这种能力在很多场景中非常有用。比如全屏布局(如首屏视觉区域)、流式字号,以及一些强依赖视觉比例的设计,都可以通过视口单位获得非常自然的响应效果。
不过,一旦把这类单位用在组件内部,就容易引发问题。因为此时组件的尺寸不再由自身或其容器决定,而是直接被整个页面环境“接管”。结果就是,组件失去了应有的独立性,在不同布局或上下文中变得难以控制和复用。
容器关系:跟随父容器
“容器关系”代表的是一种更现代的尺寸绑定方式——元素不再依赖整个视口,而是直接依附于自身所在的父容器。典型单位是 cqw 、cqh 等容器查询单位。
当你使用这类单位时,这个元素的尺寸,不再由屏幕决定,而是由它当前所处的空间来决定。也就是说,它只关心“自己有多少可用空间”,而不是整个页面有多大。
这正好解决了传统响应式设计中一个核心限制——媒体查询只能基于视口进行判断,但在实际开发中,组件更关心的往往是自身容器的尺寸,而不是屏幕尺寸本身。
容器单位的出现,让响应式能力从“页面级”下沉到了“组件级”。组件不再依赖外部布局或全局断点,就可以根据自身环境自动调整,从而真正具备独立、自适应的能力。这也是现代 CSS 组件化设计中非常关键的一步。
内容关系:跟随文本与排版度量
“内容关系”这一类单位,关注的已不再是布局结构,而是文本本身的度量。典型代表包括 ch 、ex 、lh 和 rlh 。与前几类不同,它们不依赖视口、容器或父元素,而是直接绑定到字体和排版系统之上。
当你使用这些单位时,其实是在表达一种更偏向内容驱动的设计思路。尺寸应当由文本本身来决定,而不是由外部布局强加。比如,ch 基于字符宽度,常用于控制理想的阅读行长;ex 反映字体的 x-height ;lh 和 rlh 则直接与行高(line-height)相关,用来建立稳定的垂直节奏。
这种方式的价值在于,它把“排版经验”转化成了“系统规则”。例如,用 60ch 控制段落宽度,可以自然得到更舒适的阅读长度;用 1lh 控制段落或模块之间的间距,可以让整体节奏始终保持 一致,而不需要反复微调具体数值。
从更高层来看,这类单位所代表的,是一种设计理念的转变:不是让内容去适配布局,而是让布局围绕内容生长。
比例关系:无单位
这一类并不是在描述“跟随谁变化”,而是在定义变化本身。与前面所有单位不同,它不关心依附对象,而是直接规定变化的比例,因此,可以把它理解为一种“比例关系”。典型的例子包括:
line-height: 1.5;
flex: 1;
opacity: 0.8;
这些值有一个共同特点:它们表达的不是一个具体长度,而是一个倍数关系。换句话说,它们关注的不是“多大”,而是“多少倍”。以最常见的 line-height: 1.5 为例:
p {
font-size: 16px;
line-height: 1.5;
}
这里的 1.5 实际含义是:行高等于当前字体大小的 1.5 倍。如果字体大小从 16px 变成 20px,行高会自动变成 30px。
关键在于,这种关系是“内嵌”的,而不是“引用”的。它不像 em 那样依赖字体作为单位,也不像 rem 依赖根元素,而是直接把比例规则写进属性本身。这种表达方式更直接,也更稳定。
正因为如此,在很多场景中,无单位数值反而是更推荐的写法,尤其是在排版中。相比固定的 24px,或者可能受嵌套影响的 1.5em,line-height: 1.5 不依赖具体单位,不会产生层级累积问题,还能随着字体变化自动调整。同时,它的语义也更清晰——表达的是“阅读密度”,而不是一个具体尺寸。
这种“比例关系”其实在 CSS 中无处不在,并不仅仅局限于排版。在 Flex 布局中:
.item {
flex: 1;
}
表达的是空间分配的比例,而不是具体宽度。
在 Grid 中,虽然 fr 看起来像单位,但本质上同样是一种比例系统:
grid-template-columns: 1fr 2fr;
表示的是 1:2 的空间分配关系。再比如透明度、变换等属性:
opacity: 0.5;
scale: 1.2;
同样是在描述相对变化,而不是绝对数值。
从这个角度来看,无单位数值其实承担的是另一种角色:它不负责“绑定关系”,而是负责在既定关系中,定义变化的幅度。
在理解了前面的所有关系之后,其实还可以再往前走一步,把整个模型进一步抽象。换一个更简洁的视角来看,CSS 中的所有尺寸,本质上都在回答两个问题:它是否需要变化?如果需要变化,它应该跟随谁变化。
第一个问题是在区分“固定”和“响应”。有些尺寸需要保持稳定,比如边框或精细对齐,这时选择的是“不变化”;而有些尺寸则需要随着环境调整,这时就进入“响应”的范畴。第二个问题则是在确定依附对象——是跟随全局系统、局部上下文、视口环境、容器空间,还是内容本身。
一旦用这种方式思考,单位选择就不再是一个依赖经验或记忆的过程,而是一个可以被推导的决策过程。你不再需要记住“某种场景用某种单位”,而是先明确关系,再自然得出答案。
这也正是“尺寸关系模型”真正重要的地方。很多开发中的问题,并不是单纯因为“单位选错了”,而是关系选错了:在应该使用全局关系的地方使用了局部关系,在应该依赖容器的地方却绑定了视口,在应该围绕内容排版的地方却用了固定值。结果就是,系统逐渐失去一致性,适配变得困难,维护成本不断增加。
而这个模型的价值,在于提供了一个统一的判断框架:先决定关系,再选择单位。一旦顺序颠倒——先选单位,再试图去适配各种场景——问题往往就会不断出现。
最佳使用场景
从这里开始,CSS 单位就不再是一组零散的工具,而是一整套可以被设计、被推理、也可以被复用的系统。你不再是在零碎地选择某一个单位,而是在基于一套清晰的关系模型,去构建整个页面的尺寸逻辑。
接下来,更关键的一步,是把这套模型真正落到实际项目中。也就是说,在具体场景里,你到底该使用哪一类单位。与其去记忆“某种场景对应某种单位”,不如换一种更稳定的思考方式——从“关系”出发,反推出“选择”。一旦你明确了这个元素应该跟随谁变化,单位本身几乎是自然浮现的。
因此,接下来的内容,我们会基于这套“尺寸关系模型”,按顺序逐一拆解每一类单位在真实项目中的最佳使用场景。重点不在于记住规则,而在于理解背后的逻辑,并能够在不同情境中灵活应用。
当你理解到这一层时,CSS 单位这件事就不再是一个依赖经验的选择题,而是一个可以建模、可以推导,甚至可以系统化设计的能力。这一步,基本已经进入“设计系统思维”的范畴了。
px :用于“需在稳定、不参与响应”的细节
px 最适合的使用场景,其实并不在布局层面,而是在那些需要稳定、不参与响应变化的细节上。换句话说,当一个尺寸的变化不会带来收益,甚至可能破坏视觉一致性时,px 往往是最合理的选择。
典型的例子包括细边框(如 1px 或 0.5px)、图标对齐的微调、阴影与描边、分割线,以及一些小范围的视觉校正。这些细节的共同点在于:它们依赖精确的像素控制,一旦随着环境变化而缩放,反而容易产生模糊、偏移或不协调的视觉效果。
从这个角度来看,在这些场景中使用 px,反而是一种更“响应式”的选择——因为你在有意识地避免不必要的变化,让关键细节保持稳定。
但需要注意的是,这种优势并不适用于布局层面。如果把 px 用作主要的布局单位,比如容器宽度、模块间距或字体尺寸,一旦页面进入多端适配阶段,这些“固定值”就会迅速变成负担,限制整体的弹性和可扩展性。
rem:用于“全局一致”的系统级尺寸
rem 最适合的使用场景,是构建设计系统中的“全局一致”尺度。它的核心价值在于,把所有尺寸统一绑定到根元素,从而让整个页面在同一个规则下运作。
在实际项目中,rem 常用于定义全局字体体系、统一的间距系统,以及组件的基础尺寸(例如 padding、margin、gap)。这些场景有一个共同特点:它们需要在整个页面中保持一致,并且能够随着整体策略一起缩放。
例如,你可以基于 rem 定义一套间距系统:
:root {
--space-1: 0.5rem;
--space-2: 1rem;
--space-3: 2rem;
}
当根元素的字号发生变化时,这些间距会自动按比例调整,而不需要逐个修改组件。这种方式可以极大地降低维护成本,同时保证视觉上的一致性。
不过需要注意的是,rem 更适合用于“系统层”,而不是“局部例外”。如果某个组件需要根据自身环境动态变化,比如根据容器或上下文自适应,那么继续使用 rem 反而会让它变得僵硬。换句话说,rem 擅长统一规则,但并不擅长处理局部差异。
em 或 % :用于“组件内部的自适应关系”
em 和 % 这一类单位,最适合用于构建组件内部的自适应关系。它们的特点是依赖当前上下文,而不是全局系统,因此非常适合用来描述组件内部各个元素之间的比例关系。
例如,一个按钮组件可以这样定义:
.button {
font-size: 1rem;
padding: 0.5em 1em;
}
这里的 em 实际上传达的是一种关系:按钮的内边距应该随着字体大小变化。也就是说,组件内部的空间是“绑定”在自身排版之上的,而不是写死为某个固定值。
类似的使用场景还有很多,比如图标与文字之间的比例关系(使用 em),子元素宽度占父元素的比例(使用 %),以及卡片内部结构中常见的 100% 宽度元素等。这些都属于在组件内部建立相对关系的典型用法。
这类单位的核心价值在于,让组件具备“自我缩放”的能力。当组件被放入不同的上下文中,比如更大的容器、不同的字号环境,或者不同的布局结构时,它可以自然适配,而不需要额外调整具体数值。
不过需要注意的是,这种依赖关系是可以层层叠加的。一旦嵌套层级过深,em 的计算路径就会变得复杂,最终结果也不再直观。因此,在复杂结构中使用时,需要有意识地控制层级,避免关系链失控。
视窗单位:用于“与屏幕强绑定”的布局或视觉
vw 和 vh 这类视窗单位,最适合用于那些与屏幕强绑定的布局或视觉设计。它们直接以浏览器视口为参照,因此表达的是一种非常明确的关系:尺寸应该随着屏幕变化,而不是由容器或内容决定。
在实际项目中,这类单位常见于全屏布局、流式字号,以及一些依赖屏幕比例的视觉效果。例如:
.hero {
height: 100vh;
}
或者:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
这些用法的共同点在于:它们让元素的尺寸直接响应视口变化,从而在不同设备上保持一种整体的视觉比例。
不过需要注意的是,这种“直接绑定屏幕”的能力,并不适合用在组件内部。一旦在组件中使用 vw 或 vh,组件的尺寸就会脱离自身上下文,被整个页面环境所控制。这会导致组件难以复用,也更难在不同布局中保持稳定表现。因此,视口单位更适合作为页面级或视觉级的工具,而不是组件级的默认选择。
窗器单位:用于“真正可复用的响应式组件”
cqw、cqh 等容器查询单位,最核心的使用场景是构建真正可复用的响应式组件。与视口单位不同,它们并不依赖整个页面,而是直接绑定到组件所在的父容器。
例如一个卡片组件:
.card {
width: 100%;
padding: 5cqw;
}
这里表达的是一种非常清晰的关系:组件的尺寸不由屏幕决定,而是由它当前所处的容器空间决定。也就是说,组件只关心“自己有多少可用空间”,而不关心整个页面的大小。
这种能力在很多场景中尤为重要。比如同一个组件需要出现在不同布局中(侧边栏、主内容区或网格中),或者组件需要根据可用空间自动调整结构,又或者希望减少对媒体查询的依赖。这些问题,用传统的视口响应方式往往很难优雅解决,而容器单位则可以直接应对。
从更高层来看,容器查询单位的意义在于,让组件具备一种“环境感知能力”,但这个环境是局部的,而不是全局的。组件不再依赖页面断点,而是根据自身所处的上下文自我调整。这也可以说是现代 CSS 从页面级响应式迈向组件级响应式的关键一步。
ch 或 lh 等:用于“排版与阅读体验驱动”的设计
ch、lh 等这类单位,最适合用于那些以排版和阅读体验为核心的内容区域。与前面侧重布局的单位不同,它们直接绑定到文本本身的度量,因此更适合解决“读起来是否舒服”的问题。
典型的应用场景包括:控制文章正文的宽度(例如 max-width: 60ch)、设置段落之间的间距(如 margin-bottom: 1lh),以及建立整体的垂直节奏。这些场景有一个共同点:尺寸不应该由布局决定,而应该由内容本身来驱动。
例如:
.article {
max-width: 65ch;
line-height: 1.6;
}
通过 ch 控制行长,可以直接得到更理想的阅读宽度,而不需要反复尝试具体数值。再比如:
p {
margin-bottom: 1lh;
}
使用 lh 来定义间距,可以让段落之间始终与当前的文本节奏保持一致。
这类单位的真正价值,在于它们把原本依赖经验的排版决策,转化为可以复用的系统规则。你不再需要不断“微调数值”,而是可以通过单位本身,让设计自然成立。
无单位值
无单位数值最适合用于那些本质上是在表达“比例关系”,而不是具体尺寸的场景。与其他单位不同,它不依附于某个参考系,而是直接定义一种倍数规则,因此特别适合用来描述变化幅度、分配关系或节奏控制。
在实际开发中,这类写法广泛存在于排版、布局和视觉效果中。例如在排版中:
p {
line-height: 1.6;
}
这里的 1.6 表达的是行高与字体大小之间的比例关系,而不是一个固定高度。这样做的好处是,无论字体如何变化,行高都会自动按比例调整,从而保持稳定的阅读节奏。这类用法的共同点在于:它们关注的不是尺寸本身,而是尺寸之间的关系。
无单位数值的核心价值在于,它把设计中的“比例”和“节奏”直接表达出来,而不会受到单位或上下文嵌套的干扰。相比使用固定值或依赖单位的写法,它通常更加稳定、可预测,也更贴近设计本意。
从更高层来看,这类写法实际上是在把“经验规则”转化为“数学关系”。你不再是在不断调整一个个具体数值,而是在定义一套可以自动生效的比例系统。这也是它在现代 CSS 中越来越重要的原因。
如果把前面所有使用场景再压缩成一条清晰的决策路径,其实可以用一个非常简单的思考顺序来判断:首先,这个尺寸是否需要变化?如果答案是不需要,那么直接使用 px,让它保持稳定即可。
如果这个尺寸需要变化,接下来要问的就是——它应该跟随谁变化。是跟随全局系统(rem),还是组件内部(em 、 %),是跟随屏幕(vw 、 vh),还是跟随容器(cqw 、 cqh),又或者是跟随内容本身(ch 、 lh)。当这个问题有了明确答案,单位的选择几乎是自然得出的结果。
当你用这种方式做决策时,会慢慢意识到一件事:你不再是在“选单位”,而是在设计一套尺寸关系。一旦关系是清晰的,代码本身就会变得更加稳定、可预测,同时也更容易维护。这才是 CSS 单位真正的使用方式。
不过,到这里还只是第一步。如果你已经接受了“尺寸关系模型”的视角,那么接下来一个更贴近真实开发的问题就会浮现出来:在实际项目中,我们几乎不会只使用一种单位——那不同类型的单位应该如何组合使用?
这一步,才是真正体现 CSS 能力的地方。因为一旦进入“混合使用”,你所做的就不再只是选择某一种关系,而是在有意识地组合多种关系,让它们在同一个系统中协同工作。
混合使用的本质:叠加多个“跟随逻辑”
在讨论“混合使用”之前,需要先把一件事说清楚:混合单位并不是随意拼凑,而是在同一个元素上,有意识地叠加不同层级的“依附关系” 。每一种单位,都在负责一部分逻辑,而不是彼此冲突。
举个最简单的例子:
.card {
padding: 1rem 2em;
}
这段代码里,其实同时存在两种关系。1rem 绑定的是全局系统,用来保证整体的一致性;而 2em 则跟随组件内部的字体大小,用来维持局部的协调性。
从结果来看,这并不是混乱,而是一种分工:全局负责统一规则,局部负责自适应细节。正是这种“关系的叠加”,让组件既能融入整体系统,又能在自身内部保持灵活性。
最常见的组合:全局 + 局部(rem + em)
这是最推荐、也最稳定的一种组合方式:让不同层级的关系各司其职,而不是互相干扰。一个常见且有效的模式是——用 rem 定义基础尺寸,用 em 来做组件内部的比例调整。
例如一个按钮组件:
.button {
font-size: 1rem; /* 全局控制 */
padding: 0.5em 1.2em; /* 局部跟随 */
border-radius: 0.5rem; /* 系统统一 */
}
这里的结构其实非常清晰。按钮的整体尺寸(如字体大小、圆角)是绑定在全局系统上的,因此可以保持一致性;而内部的间距(padding)则依赖自身字体,从而能够随着组件大小自然缩放。
这种组合方式的核心在于分层:全局负责统一规则,局部负责内部协调。最终的效果是,组件既能融入整体设计系统,又能在自身范围内保持灵活性——既统一,又不僵硬。
布局 + 内容:% 或 fr + ch 或 lh
在内容型页面(比如文章或文档)中,一种非常高效且稳定的组合方式是:用布局单位控制结构,用内容单位控制阅读体验。也就是说,先解决“内容放在哪里”,再解决“内容读起来是否舒服”。
例如:
.layout {
display: grid;
grid-template-columns: 1fr min(65ch, 100%);
}
在这个结构中,1fr 负责的是整体布局的空间分配,让页面具备弹性;而 65ch 则用来限制文本的最大行长,从而保证阅读的舒适度。两者关注的层面完全不同,却可以自然协同。
再比如段落间距的处理:
.article p {
margin-bottom: 1lh;
}
这里使用 lh 来控制间距,使其直接跟随文本节奏变化,而不是依赖固定数值。
从整体来看,这种组合的核心在于分工明确:布局单位决定“块在哪里、占多少空间”,而内容单位决定“读起来是否舒适” 。当两者各自负责自己的问题时,页面既能保持结构上的灵活性,又能在阅读体验上更加稳定自然。
响应式组合:rem + vw
这是现代响应式设计中非常常见、也非常实用的一种组合方式。它的核心思路可以概括为一句话:在“稳定”和“流动”之间找到平衡。
例如:
h1 {
font-size: clamp(2rem, 5vw, 4rem);
}
这段代码实际上融合了多层关系。2rem 作为最小值,提供一个基于全局系统的稳定下限;5vw 作为中间值,让字体能够随着视口变化而动态调整;而 4rem 则作为最大值,防止尺寸在大屏上无限增长而失控。
从本质上来看,这种写法并不是简单地混用单位,而是在构建一套“有边界的响应规则”:允许尺寸根据环境变化,但同时为这种变化设定清晰的上下限。这样既能获得流动性的优势,又能避免不可控带来的问题。
这种模式的价值在于,它让响应式不再是“要么固定,要么完全流动”的二选一,而是变成一种可控、可设计的连续变化过程。
组件级响应:容器单位 + rem
当你开始使用容器查询单位时,一种非常自然、也非常实用的组合方式就会出现:让容器决定结构,用 rem 维持系统一致性。
例如:
.card {
padding: 2rem;
gap: 2cqw;
}
这里其实是两种关系在协同工作。rem 用来定义基础间距,确保组件始终遵循整体设计系统;而 cqw 则根据容器宽度进行微调,让组件在不同空间下都能做出合适的调整。
这种组合的关键在于分工清晰:全局系统负责“统一语言”,容器关系负责“适应环境” 。最终的结果是,组件既能够在不同容器中灵活变化,又不会偏离整体设计风格,从而同时具备一致性和适应性。
固定 + 弹性:px + 其他单位
在实际开发中,“完全响应式”并不总是目标。有些细节本身就需要保持稳定,比如 1px 的边框、精细的阴影、或者分隔线。这些元素一旦随着环境变化,反而容易破坏视觉的清晰度和一致性。
因此,一种非常常见且合理的做法是,把不同类型的尺寸分开处理。例如:
.card {
padding: 1rem;
border: 1px solid #ddd;
}
这里的 rem 用来控制整体结构,让组件能够随着全局系统调整;而 px 则用来锁定细节,确保边框始终保持清晰和稳定。
这种组合方式的核心,其实是一种取舍:让该变化的部分具备弹性,让不该变化的细节保持稳定。当这两者各自发挥作用时,页面既有响应能力,又不会失去精致度,这是一种非常健康且实用的平衡。
calc():显式组合关系
当你需要更精细地控制多种尺寸关系时,可以借助 calc() 把这些关系“写出来”。它的作用,不只是做简单计算,而是把原本隐含在设计中的逻辑,转化为清晰可读的表达。
例如:
.container {
padding: calc(1rem + 2vw);
}
这里实际上是在组合两种关系:1rem 提供一个稳定的基础间距,而 2vw 让间距可以随着屏幕略微放大。也就是说,这个间距既有全局一致性,又具备一定的响应能力。
再比如:
.box {
height: calc(100vh - 4rem);
}
这里表达的是另一种常见逻辑:用视口高度作为整体空间,再减去一个固定的导航高度,从而得到实际可用区域。
从本质上看,calc() 的价值在于,它让不同关系之间的组合变得明确且可控。你不再需要在脑中“估算”这些关系,而是可以直接用数学形式把它们表达出来,从而让代码本身就成为设计逻辑的一部分。
当你逐渐熟悉这些单位的组合方式之后,会开始看到一个更高层的模式——不同单位,其实在解决不同“层级”的问题。换句话说,它们并不是随意选择的工具,而是在各自的层面上承担不同职责。
例如,px 更偏向视觉细节层,用来处理那些不应该变化的精细部分;rem 属于设计系统层,负责全局的一致性;em 和 % 则工作在组件内部,用于建立局部的自适应关系;vw、vh 面向页面环境,与屏幕尺寸直接相关;cqw 这样的容器单位属于组件环境层,让组件根据自身空间变化;而 ch、lh 则作用于内容层,用来优化排版和阅读体验。
当这些层级被理清之后,一个非常重要的原则也会变得清晰:在混合使用单位时,最容易出问题的情况,是多个单位在“争夺同一个控制权”。比如,一个尺寸既想跟随视口(vw),又想跟随容器(cqw);或者字体既由 rem 控制,又在嵌套中被 em 不断放大。这种情况下,结果往往会变得不可预测,也更难维护。
为了避免这种问题,可以用一个非常简单但实用的判断方式:在写下任何一个尺寸之前,先问自己一句——这个值,主要由谁来决定? 如果答案是唯一的,那么直接选择对应的单位即可;如果答案涉及多个因素,那么就需要通过组合(例如 clamp()、calc(),或者分层设计)来明确各自的作用边界。
当你开始有意识地“混合关系”,而不是简单地“混合单位”时,你会发现一个明显的变化:CSS 不再是一个依赖不断试错的过程,而是变成了一套可以被设计、被推导的系统。这一步,其实就是从“会写 CSS”,走向“会设计 CSS”的关键转变。
小结
当你把这些内容全部走完,再回头看最开始的问题——“到底该用 px、rem,还是 vw?”——其实已经不那么重要了。
因为你已经不再依赖某一个“正确答案”,而是拥有了一套可以自己推导答案的思考方式。
你会开始从“关系”出发,而不是从“单位”出发;先判断这个尺寸应该跟随谁,再去选择合适的表达方式。很多过去需要反复试错的问题,也会在这个过程中自然消失。
从更长远来看,这种思维方式的价值,并不仅仅局限在 CSS 单位上。它本质上是在训练你用一种更结构化的方式去理解界面:把页面拆成不同层级,把尺寸拆成不同关系,再通过组合让它们协同工作。
当你做到这一点时,CSS 就不再只是“写样式”,而是在构建一套有逻辑的系统。
而这,也正是从“能实现设计”,走向“能设计系统”的关键一步。
崔东树:3月的A00级电动车剧烈萎缩带来均价的上升,2026年销量增长压力较大
Node.js 如何判断入口文件:从 require.main 到 ES Module 实现
目标:判断当前文件是否被直接执行(而不是被 import)
一、CommonJS(对照)
if (require.main === module) {
main();
}
二、ES Module 写法(核心)
import { fileURLToPath } from 'url';
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}
三、关键点(只记这 3 个)
-
import.meta.url→ 当前文件(URL) -
fileURLToPath()→ 转为本地路径 -
process.argv[1]→ 启动入口文件路径
👉 判断本质:
入口路径 === 当前文件路径
四、推荐封装
import { fileURLToPath } from 'url';
export const isMain = (meta) => process.argv[1] === fileURLToPath(meta.url);
// 使用:
if (isMain(import.meta)) {
main();
}
五、关键技术点拆解(用于深入理解)
1️⃣ import.meta.url
返回当前模块的 URL,例如:
file:///Users/demo/index.js
2️⃣ fileURLToPath
将 file:// URL 转换为本地路径:
/Users/demo/index.js
3️⃣ process.argv[1]
Node 启动时的入口文件路径:
node index.js
# 得到:/Users/demo/index.js
中信证券:流动性冲击扰动,互联网板块关注业绩韧性与AI主线
怎么在VS Code 调试vue2 源码
总结一下怎么在VS Code 调试vue2 源码
- 克隆vue2源码到本地
- 在源码根目录安装依赖 把项目跑起来 生成/dist/vue.js文件
- 在/examples/ 下随便找个文件(引入的文件要是我们生成的vue.js) 打上断点
- 安装Live Server插件 把我们打上断点的文件在浏览器打开
- 在.vscode文件夹下配置launch.json
- 点击VS Code的Run and Debug图标 就可以进入了
我们开始吧~
克隆vue2源码到本地
去Github克隆源码,克隆后我们用VS Code打开。
git clone https://github.com/vuejs/vue
![]()
在源码根目录安装依赖 把项目跑起来
pnpm i
![]()
![]()
把项目跑起来
npm/pnpm run dev
![]()
bundles /Users/gongzemin/Documents/GitHub/vue/src/platforms/web/entry-runtime-with-compiler.ts → dist/vue.js...
从 entry-runtime-with-compiler.ts 这个入口文件打包生成 dist/vue.js 这个最终可用的 Vue 文件
生成了dist文件夹 里面有vue.js
![]()
在examples/ 下随便找个文件 打上断点
我们找examples/classic/commits/app.js 在如图位置打上断点
![]()
commits/index.html 这个文件引入了vue.min.js, 我们刚才构建出来的是vue.js文件,我们把引入的文件改成vue.js
<script src="../../../dist/vue.js"></script>
安装Live Server插件 把我们打上断点的文件在浏览器打开
![]()
安装好插件后,打开文件的上下文菜单 可以看到Open with Live Server
![]()
这样我们就可以打开我们的examples/classic/commits/index.html 文件了 是用服务器打开的
![]()
在.vscode文件夹下配置launch.json
注意这里的URL是我们的要调试URL路径
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Run parser",
"runtimeExecutable": "parser",
"cwd": "${workspaceFolder}/packages/reactivity-transform/node_modules/@vue/compiler-core",
"args": []
},
{
"type": "chrome",
"request": "launch",
"name": "调试Vue源码",
"url": "http://localhost:5501/examples/classic/commits/index.html",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"webpack:///./*": "${webRoot}/*",
"webpack:///packages/*": "${webRoot}/packages/*",
"*": "${webRoot}/*"
},
"skipFiles": ["<node_internals>/**"]
}
]
}
点击VS Code的Run and Debug图标
点击Run and Debug图标, 选择调试Vue源码(就是我们配置launch.json里面配置的name)
看到app.js 进入我们打的断点了
![]()
我们点击Step Into
![]()
就进入Vue()构造函数了
![]()
调试vue3源码方法也一样 参考这篇笔记
奥特曼家被炸了!
![]()
奥特曼家被炸了
【导读】当地时间周五清晨,奥特曼家被炸了。奥特曼发出家人和孩子的照片,并且发出长文表示,AGI如今已经如同魔戒一般,让人做出疯狂的举动。
奥特曼家被炸了!
就在当地时间周五清晨,一名20岁男子向奥特曼旧金山中投掷了燃烧弹,引起火灾。
这栋豪宅价值2700万美元。
凌晨4点12分,警方接到报警,调查后发现无人受伤,火势已被控制。但嫌疑人已经徒步逃跑,体貌特征已被通报。
凌晨5点07分左右,警方又接到报警称,OpenAI办公楼外,一名身份不明的男子扬言要烧毁大楼。
警员很快发现,该男子与向奥特曼家中投掷燃烧弹的嫌疑人特征相符,应为同一人,警方立即将其拘留。
现在,旧金山警察局已经在X上发布通报。目前调查仍在进行中,尚未正式提出指控。
![]()
旧金山警察局已经在X上发布通报
面对这起事件,奥特曼愤怒了!他罕见地公开全家福,并且深夜发表了一篇长文。
![]()
奥特曼发长文
![]()
奥特曼发长文
奥特曼表示,「我爱我的家人胜过一切」
在这篇文章中,奥特曼首次深刻地回应了公众对AI的极度焦虑。他坦诚这种恐惧是合理的,呼吁通过全社会的政策响应和AI民主化来应对,坚决反对少数实验室独占未来控制权。
同时,他也进行了深度的个人反思,为自己因「逃避冲突」导致董事会风波而致歉。
他犀利指出,当前AI行业频发的内部斗争,就是源于AGI犹如「魔戒」般的权力诱惑。而唯一的解法,就是不让任何人独占控制权。
最后,他呼吁各方保持理性,停止极端的言语与物理攻击。
美国民众,开始仇视AI
近几个月来,美国民众对AI的看法急剧恶化。
就在Anthropic拒绝国防部合同要求的几小时后,OpenAI宣布与五角大楼达成协议,此举招致了大量对OpenAI的恶评。
奥特曼也明白,AI在美国大众的心目中并不受欢迎,很多人认为电价上涨、大量裁员的罪魁祸首就是AI。
![]()
山姆·奥特曼:AI在美国大众的心目中并不受欢迎
奥特曼的遭遇并不是首例,很多科技领袖都受过类似威胁,各大公司也都加大了对高管保护的投入。
仅在2019年,扎克伯格的安保费就超过了2000万美元。
而特斯拉提交给美国证券交易委员会的文件也透露了马斯克的安保成本:该公司「在2023年为此类安保服务支付了约240万美元的费用,并在2024年2月之前支付了约50万美元的费用,这仅仅占总成本的一部分。」
此前,美国各地就曾爆发反AI的大规模游行,愤怒的民众聚集在OpenAI和Anthropic总部外面,街道上写满标语。
3月份,就曾爆发过一场美国史上最大规模的AI全球抗议活动,200人横跨Anthropic、OpenAI、xAI高喊「停止AI竞赛」!
![]()
美国史上最大规模的AI全球抗议活动
总诉求只有一个:若主要AI实验室彼此达成一致,应承诺暂停更强大模型的训练与发布。
参与联盟包括MIRI、PauseAI、StopAI、QuitGPT等组织成员,以及部分学者与前实验室员工。
如今,AI安全派和技术加速派之间关系已经极度紧张。
![]()
美国史上最大规模的AI全球抗议活动
![]()
美国史上最大规模的AI全球抗议活动
奥特曼博客:AGI已成魔戒
我希望图片是有力量的。通常我们尽量保持低调私密,但在这种情况下,我决定分享一张照片,希望它能打消下一个人向我们家扔燃烧瓶的念头,不管他们对我有什么看法。
上一个人是在昨晚,凌晨3点45分干的。谢天谢地,燃烧瓶被房子的外墙弹开了,没有人受伤。
文字也是有力量的。几天前,有一篇关于我的文章,充满了火药味。昨天有人跟我说,在大家对AI感到极度焦虑的当下出这种文章,会让我的处境变得更加危险。我当时没当回事。
现在,我大半夜醒来,火很大,并且意识到我之前低估了文字和舆论导向的力量。现在看来,正是把一些事情说清楚的好时候。
首先,我的信念。
*为每个人争取繁荣、赋能全人类,以及推动科技进步,对我来说是义不容辞的道德责任。
*AI将成为人类见过的、扩展自身能力和潜力最强大的工具。人们对这个工具的需求基本上是没有上限的,大家会用它做出不可思议的事情。这个世界理应拥有海量的AI,我们必须想办法实现这一点。
*但一切不会总是一帆风顺。对AI的恐惧和焦虑是完全合理的;我们正在见证社会发生长久以来、甚至是史无前例的巨变。我们必须搞定安全问题,这不仅仅是对齐某个模型那么简单——我们迫切需要全社会的共同响应,以抵御新的威胁。这包括制定新政策,帮助我们度过艰难的经济转型期,从而走向一个美好的多的未来。
*AI必须民主化;权力绝对不能过度集中。未来的控制权属于全人类及其社会机构。AI需要在个体层面赋能每个人,同时我们需要共同为我们的未来和新规则做决定。我认为由少数几家AI实验室来决定我们未来社会形态这种最重大的事情,是不对的。
*适应性至关重要。我们都在极其快速地学习新事物;我们的一些信念会被证明是对的,有些则会是错的。有时随着技术发展和社会演变,我们需要迅速转变观念。现在还没有人真正了解超级智能的影响,但这种影响必将是极其巨大的。
其次,一些个人的反思。
回首我在OpenAI头十年的工作,有很多让我感到骄傲的事情,但也犯了一大堆错误。
我刚刚想起了我们即将与马斯克对簿公堂的事,想起了我当初是如何死守底线,拒绝同意他想要对OpenAI拥有单方面控制权的要求。我为此感到骄傲,也为我们当时在夹缝中求生,保住了OpenAI并取得随后的所有成就而感到自豪。
我不为自己害怕冲突的性格感到骄傲,这给我和OpenAI都带来了巨大的痛苦。我不为自己在与前任董事会的冲突中表现糟糕而感到骄傲,那给公司造成了巨大的烂摊子。在OpenAI疯狂的发展轨迹中,我还犯过许多其他错误;我是一个有缺陷的人,身处在一个极其复杂的局面中心,试图每年都取得一点点进步,始终为着公司的使命而工作。我们在入局之前就知道AI的赌注有多大,也知道我所关心的、心怀善意的人们之间的个人分歧会被无限放大。但亲身经历这些痛苦的冲突,并且还要经常出面调停,完全是另一回事,我们付出了惨痛的代价。我向我伤害过的人道歉,希望我当初能学得更多、更快一些。
我也非常清楚,OpenAI现在是一个大平台了,不再是当初那个草台班子般的初创公司,我们现在必须以一种更可预测、更稳定的方式运营。过去这几年,强度极大、极其混乱且压力山大。
但抛开这些,最让我自豪的是,我们正在兑现我们的使命,这在我们刚起步时看起来简直是天方夜谭。排除了万难,我们弄清楚了如何构建极其强大的AI,弄清楚了如何筹集足够的资金来建立基础设施以交付它,弄清楚了如何打造一家产品公司和商业模式,弄清楚了如何在大规模上提供相对安全和稳健的服务,还有很多很多。
很多公司都把「改变世界」挂在嘴边;但我们是真的做到了。
第三,关于这个行业的一些想法。
过去几年我个人的体会,以及我对为什么我们这个领域的公司之间会上演这么多莎士比亚戏剧般抓马事件的看法,归结起来就是一句话:「一旦你见识过通用人工智能(AGI),你就再也忘不掉了。」它带有一种真实的「魔戒」效应,能让人做出极其疯狂的事情。我不是说AGI本身就是那枚魔戒,而是那种「要成为控制AGI的那个人」的极权主义思想。
我能想到的唯一出路,就是坚定地与大众分享这项技术,不让任何人独占这枚「魔戒」。实现这一点的两个最明显的方法就是:赋能个人,以及确保民主制度始终掌握控制权。
关键在于,民主进程必须始终拥有比公司更大的权力。法律和规范肯定会改变,但我们必须在民主的框架内行事,哪怕这个过程会很混乱,而且比我们希望的要慢。我们希望成为一种发声渠道和一个利益相关者,但绝不希望拥有所有的权力。
对我们行业的许多批评,实际上源于对这项技术极高风险的真诚担忧。这是非常合理的,我们欢迎善意的批评和辩论。我对那些反技术的情绪感同身受,显然技术并不总是对每个人都有利。但总的来说,我相信技术进步能为你我的家人创造一个无比美好的未来。
在我们进行这些辩论的同时,我们应该降降温,少用点过激的言辞和手段,不管是在比喻意义上还是字面意义上,都少搞点爆炸,少炸点房子吧。
参考资料:
https://www.theverge.com/ai-artificial-intelligence/910393/openai-sam-altman-house-molotov-cocktail
https://blog.samaltman.com/2279512
https://www.businessinsider.com/sam-altman-home-attack-molotov-cocktail-police-arrest-2026-4
本文来自微信公众号“新智元”,作者:新智元,36氪经授权发布。