opencode这名字最近在AI编程圈子里刷屏的频率肉眼可见地变高了。第一次看到那会儿,我还以为是某家大厂出的新IDE,直到在GitHub翻到仓库才反应过来,这其实是SST团队开源的一个终端AI编程代理——简单说,你给它一个任务,它能自己做代码检索、文件改动、命令执行、测试运行,整个过程像一个坐在你旁边听你指挥的工程师。opencode本身免费开源,不带套餐,钱只花在模型API上,所以社区讨论度一直很高,连带着"opencode安装""opencode配置""opencode免费模型"这些搜索词也跟着火了起来。
这篇文章是我实际用了一个多月之后写下的完整记录。从最基础的安装、解决Windows下的环境变量报错,到模型接入、Skill开发、在VSCode和IDEA里使用,再到配合Playwright调前端Bug,最后是所有我踩过的坑和排查思路。我尽量把每一步都讲到能直接照着做,让你看完不需要再去零散翻官方文档。
1. 认识opencode:它不是又一款IDE,而是一个会自己动手的AI代理
1.1 一句话说清楚它是干什么的
你可以把opencode理解成一个跑在终端里的AI结对编程搭子。传统上我们用AI补全代码,比如Copilot、通义灵码,都是在光标位置给建议,真正动手的还是你。而opencode这类工具的工作方式完全不同:你用一个自然语言句子描述需求,比如"帮我给用户模块加一个导出CSV的功能",它会自己去翻阅项目代码,搞清楚现有结构,然后直接修改文件、安装依赖、跑测试,最后把改动结果汇报给你。
这里的关键词是"自主执行"。它在背后做的事包括:读取项目目录树、搜索相关文件、分析依赖关系、生成补丁、执行shell命令、观察命令输出,然后根据报错继续调整。也就是一个完整的"读代码—出方案—改代码—验证"的闭环。
这套东西解决的是什么呢?是始终在IDE里面对单文件上下文、没法理解整个项目的痛点。以前接一个陌生项目,光读懂目录结构就要大半天,现在你只要把任务说明白,它比你先摸清代码脉络。对于老手来说,它像多了个不用休息的初级工程师;对于刚转行的新手,它也能帮你理解"一个需求在真实项目里到底要动哪几个文件"。
1.2 和Claude Code、Codex这些同类比起来,差异化在哪
现在市面上这类终端AI代理其实不少,除了opencode,还有Anthropic官方的Claude Code、OpenAI官方的Codex CLI,以及最近社区里讨论度越来越高的pi等开源agent。我全都试过一遍,说下自己的真实感受。
| 工具 | 开源 | 模型绑定 | 主要形态 | 我的评价 |
|---|---|---|---|---|
| opencode | 是(MIT协议) | 不绑定,Anthropic/OpenAI/Gemini/DeepSeek/Ollama都能接 | CLI + VSCode插件 + JetBrains插件 + 桌面版 | 模型中立,灵活度高,社区Skill生态丰富 |
| Claude Code | 否(至少最初不开源) | 主要绑定Claude模型,第三方模型接入麻烦 | CLI | 体验成熟,但对模型选择不自由 |
| Codex CLI | 否 | 主要绑定OpenAI系模型 | CLI | 官方打磨不错,生态封闭 |
| pi(开源agent) | 是 | 不绑定 | CLI | 轻量,但功能和Skill生态还在早期 |
这里面我最看重的是"模型中立"这一点。opencode接模型的方式非常开放,任何OpenAI-compatible接口、Anthropic接口、Gemini接口,甚至本地Ollama都能接。这意味着你不必为了用一个工具被迫在某个模型供应商上吊死,哪个模型的代码能力强、性价比高、当前适合跑什么任务,完全可以自由切换。
第二个差异化是它的Skill机制。这个类似给AI装"专业技能包",后面我会单独用一整章来讲。就冲这一点,opencode在社区的可玩性和扩展性比同类工具高出一大截,这也是为什么热搜词里会出现"opencode skills""opencode superpowers""opencode oh-my-claudecode"这类长尾词。
2. 安装opencode:从零到能在终端里跑起来
2.1 三种安装方式怎么选
opencode的安装方式不算复杂,但不同平台确实有讲究。我梳理了一下,最常见的有三种。
第一种是npm安装,全局装opencode的命令行包。包名是opencode-ai,命令是opencode,注意别搞混。
npm install -g opencode-ai这种方式的优点是版本更新方便,直接再执行一遍同样的命令就能升级。如果你平时用Node,我建议优先选这种方式。装完验证一下:
opencode --version能输出版本号就说明基本环境OK。
第二种是官方的一键安装脚本,适合不想碰npm的场景。macOS和Linux上直接在终端执行官方文档里的脚本命令,它会自动下载可执行文件到你的用户目录,然后把路径配好。这种方式对Windows的支持没有npm那么顺,Windows用户我更推荐走npm或者看下面3.3节里的包管理器方案。
第三种是brew等包管理器安装。macOS用户可以试试brew install sst/tap/opencode,这会从官方tap里拉取安装包。好处是依赖管理干净,bad upgrade和uninstall都很方便。
装完之后,直接在当前项目目录下运行opencode,第一次启动会进入一个TUI界面——也就是终端里的全屏交互面板,界面左边是对话历史,底部是输入框。到这里不算安装完,后面还要配置模型,但至少工具本身已经跑起来了。
注意:我第一次装的时候,只是在终端敲了
opencode,结果命令行卡在"Downloading runtime..."半天没反应。后来才搞明白,opencode启动时会下载自己的运行时依赖(Bun runtime组件),国内网络环境下这一步容易超时。如果遇到这种情况,多等一会儿,或者检查网络连通性,不要一上来就Ctrl+C。
2.2 Windows下最典型的安装报错:"无法将opencode项识别为cmdlet"
这条报错的热度在搜索词里居高不下,原文是这样的:opencode : 无法将“opencode”项识别为 cmdlet、函数、脚本文件或可运行程序的名。我帮好几个朋友远程排查过,也自己换过机器踩过一遍,原因其实就一个:npm全局包的安装目录不在Windows的PATH环境变量里。
排查步骤非常固定:
先用npm看全局包安装到了哪个目录:
npm config get prefix在Windows上通常输出类似C:\Users\你的用户名\AppData\Roaming\npm。然后执行:
echo %PATH%看看这个路径在不在里面。如果不在,有两种修复思路。
第一种是临时修复,不推荐但能应急:用npx opencode-ai代替opencode。npx会临时去node_modules里找包并执行,所以能跑起来,但每次都要敲一串,体验很差。
第二种是一劳永逸的修复:把npm全局目录加到Path环境变量。打开"系统属性→环境变量",在"用户变量"或"系统变量"里找到Path,点编辑,新增一条,填你上面npm config get prefix得到的路径,然后保存。改完记得重开终端,旧的终端窗口不会刷新环境变量。
还有一种更彻底的思路:如果你本来就装了nvm-windows管理多个Node版本,建议直接把默认Node版本对应的全局目录配置好。很多时候反复出现"cmdlet不识别",是因为装了新版Node以后PATH被重新覆盖过,旧路径失效了。
附加一个容易忽略的小坑:如果在Git Bash或WSL里面运行opencode,但报错还是"command not found",那多半是npm全局包和当前shell不是同一套环境。WSL里要用WSL内部的Node重新安装一遍,不能直接用Windows侧的npm。
2.3 初始化与配置文件结构
装完以后,第一次运行opencode会引导你登录或选择provider。如果界面里有auth login之类的引导,可以按提示走;不想走交互式登录的话,可以直接编辑配置文件。
opencode的配置目录在macOS/Linux下是~/.config/opencode/,Windows下同样在用户目录的.config/opencode/,对应所以你不用单独记。主配置文件是opencode.json,如果你之前用过Claude Code或者Cursor这类工具,对JSON形式的环境变量配置应该不陌生。
在项目层面,opencode默认读取当前工作目录下的AGENTS.md作为项目说明文件。这个文件有点像项目给AI的"入职手册",你希望它在改代码前先了解什么约束、技术栈、目录约定,都可以在这里面写。用opencode /init命令可以让它根据当前项目自动生成一份初始的AGENTS.md。
我建议刚上手时,先别盯着配置项抠细节,先把最基础的模型通道配好,能对话,再逐步加东西。
3. 模型接入与免费方案:opencode本身不要钱,花的是模型的钱
3.1 opencode.json里到底配什么
opencode本身完全免费,不需要买任何套餐,这也是它对比某些商业IDE插件最大的优势之一。你的支出仅限于模型API调用费,所以"opencode套餐"这个搜索词其实是个误解——它没有套餐,模型侧才有计费档位。
模型配置都写在opencode.json里。核心结构分两步:先定义provider,再指定默认模型。给你看一个最基础的示例,这里我以DeepSeek官方API为例:
{ "$schema": "https://opencode.ai/config.json", "provider": { "deepseek": { "npm": "@ai-sdk/deepseek", "name": "DeepSeek", "options": { "baseURL": "https://api.deepseek.com/v1", "apiKey": "你的APIKey" }, "models": { "deepseek-chat": { "name": "DeepSeek V3" }, "deepseek-reasoner": { "name": "DeepSeek R1" } } } }, "model": "deepseek/deepseek-chat" }注意这里的npm字段,它对应的是opencode用来和模型供应商通信的SDK包。接OpenAI生态的模型大多走@ai-sdk/openai,接Anthropic走@ai-sdk/anthropic。配置文件中填了npm字段后,opencode会在首次使用该provider时自动安装对应SDK,你不用手动npm install。
如果你接的是OpenAI官方API,配置会更简单,甚至官方自带的情况下可以不写baseURL,直接配APIKey就行。
3.2 免费模型怎么接
免费模型是很多人的第一诉求,我实测过几条能跑通的方案。
最省事的是Google Gemini的免费额度。Gemini的免费档虽然有限速,但对日常小任务、学习用途完全够用。在opencode.json里配置一个gemini provider,填上你在Google AI Studio申请的APIKey,然后把model设为gemini/gemini-2.5-pro或者gemini-2.5-flash这类具体型号就行。我实际用下来,Gemini在代码补全和结构化任务上表现不错,缺点是长上下文场景响应速度偶尔偏慢。
第二个方案是DeepSeek。DeepSeek的API价格在同类模型里算很便宜,而且注册时会送一点体验额度,新用户拿来跑通流程完全够。代码能力我也专门测过:中等规模项目的重构、单元测试生成、报错定位,都能给出像样的结果。这也是我目前最常用来跑opencode的模型之一。
第三个方案是OpenRouter,它上面聚合了很多模型,其中有不少:free后缀的免费模型可以用。配置方式就是在opencode.json里加一个openrouter provider,然后把模型名写成类似openrouter/xxx:free的格式。OpenRouter免费模型的缺点是排队时间长、限流严重,只适合尝鲜,不适合正经跑长任务。
第四个方案是本地模型,通过Ollama跑qwen2.5-coder或者deepseek-coder之类的小模型。好处是零API费用、数据不出本机,但代码生成质量和速度跟商业模型有差距,更适合隐私敏感场景。
我个人的建议:如果想认真用opencode干活,别把免费模型当主力。免费额度适合验证流程、测试Skill、跑demo;真正接手项目时,用便宜但质量稳定的付费API更省时间,因为AI返工浪费的是你的精力。
3.3 用cc-switch这类工具管理多套provider配置
很多人手上有好几套API渠道,比如A供应商的价格低,B供应商的模型质量高,今天用这家明天用那家。如果每次都手动改opencode.json,既烦又容易改错。
这时候可以用社区里常见的配置文件管理工具,比如搜索词里反复出现的cc-switch。cc-switch这类工具的定位是提供一套可视化界面,帮你集中维护多个API渠道的配置,一键切换到目标provider,并同步生成或更新opencode、Claude Code等工具的配置文件。
我自己的做法是:在cc-switch里把常用的几个渠道都维护好,每个渠道填上对应的baseURL和APIKey,然后按项目分profile。比如接私活的项目用性价比方案,公司内部项目用稳定的大模型渠道。切换时只需要在cc-switch里点一下,就不用再翻json文件了。
这里要提醒一句:如果你的APIKey是团队共享的,注意别把配置json误提交到Git仓库里。opencode也支持在系统环境变量里设置APIKey,比如
DEEPSEEK_API_KEY,官方推荐用环境变量而不是明文写在json里。我后来就把所有key都改成环境变量了,安全性和可维护性都高很多。
4. 从会用到好用:Skills、Memory与项目指令
4.1 让AI"越用越懂你"的AGENTS.md和Memory机制
这个章节要解决的核心问题是:怎么让AI不只停留在"你问一句它答一句"的被动状态,而是越用越贴合你的项目和工作习惯。
先说说AGENTS.md。这个文件相当于项目的长期记忆,每次opencode启动新会话时都会自动读它。你可以往里面写这些内容:
- 项目技术栈和目录结构说明
- 代码风格约束(比如"前端状态统一用zustand,不要用redux")
- 常见的构建命令和测试命令
- 需要特别注意的坑(比如"改了这个文件必须同步更新mock数据")
我接手一个新项目的第一件事,就是先让opencode读一遍代码,然后基于它生成一份AGENTS.md,我再手动修正几处不准的地方。这样后续每一次对话,它都自带"项目背景知识",不会说出明显外行的话。
另一个是Memory机制。opencode有自己的一套记忆管理方式,把历史对话里的关键信息沉淀到项目记忆文件中。实际使用中,我喜欢用小技巧:在Skill或者指令里加一条规则,要求AI在完成一次较大的功能改动后,把决策理由和注意事项追加到项目的AGENTS.md或者docs/DECISIONS.md里。这样项目越做,AI对新需求的判断就越准确,相当于给整个项目建立了活的经验库。
4.2 Skill开发:一个最简单的示例
Skill是opencode最有扩展性的机制。一个Skill本质上就是一个文件夹,里面有一个SKILL.md作为入口,描述这个技能在什么场景下被触发、具体怎么操作。opencode的Skill目录分为全局和项目两级:全局目录放你自己积累的通用技能,项目目录放当前仓库专用的技能。
我先给你一个最小可用的Skill示例。假设我经常需要让AI做代码审查,那就建一个code_review技能:
~/.config/opencode/skill/code-review/SKILL.md--- name: code-review description: 对指定改动或文件进行代码审查,输出问题清单和修复建议。 --- ## 触发场景 用户要求"review代码""检查一下这段改动""帮我审一下PR"时,自动应用该技能。 ## 执行步骤 1. 先定位本次改动涉及的文件列表。 2. 读取相关文件,重点关注: - 错误处理是否完备 - 是否引入安全隐患(如SQL注入、敏感信息日志) - 命名是否清晰,逻辑是否容易被误解 - 是否缺少必要的注释 3. 输出一份按严重程度排序的问题清单,每个问题标注文件与行号。 4. 对P0和P1级别问题,直接给出修复建议代码块。写完保存后,在opencode对话中输入"帮我审查一下刚才改的登录模块",它就会按照code-review这个Skill定义的步骤来执行,而不是漫无目的地发挥。
Skill的开发门槛很低,但价值上限很高。你可以把团队的一切规范性流程沉淀成Skill,比如"接口联调checklist""发布前审查流程""数据库迁移操作规范"。这些一旦沉淀成Skill,新成员也能借助AI执行出老手的水准。
4.3 现成的Skill资源:superpowers、oh-my-claudecode怎么接
自己写Skill当然是基本功,但社区里已经有很好的现成资源了。两个最常被提到的就是superpowers和oh-my-claudecode。
superpowers是一套开源的Skill集合,最初是给Claude Code设计的,社区后来适配了opencode。里面包含浏览器自动化、写作风格分析、深度代码审查、项目规划等十几二十个技能,安装以后相当于给opencode装上了一个"工具库"。我印象最深的是它的代码审查Skill,输出的问题清单分类非常细,直接可以贴在PR描述里用。
oh-my-claudecode也是类似思路,是一套面向中文用户定制的Skill集合,针对中文项目场景做了不少优化。它同样走Agent Skills规范,所以放到opencode的Skill目录下也能被识别。
安装方法很简单:把对应的仓库clone下来,然后把里面的skill文件夹拷贝到opencode的全局Skill目录,或者直接在项目级的.opencode/skill目录里引用。装完以后,在opencode TUI里输入查看技能列表的命令,能看到这些技能被自动加载,就说明成功了。
有一点要注意:这些第三方Skill集合更新频率不一定跟得上opencode的版本,装多了也可能出现互相干扰的情况。我建议先装一个最小的superpowers,用熟了再逐步加别的。
5. 脱离终端:在VSCode、IDEA里用opencode
5.1 VSCode插件与桌面版
看到"opencode vscode插件"这个搜索词的时候,我就知道很多人和我一样,对纯终端界面还是有点畏惧。这很正常。虽然TUI效率高,但在IDE里能同时看到代码树、报错面板和AI输出,对不习惯终端的开发者来说更友好。
opencode在VSCode里的使用方式很简单:直接在扩展市场搜索"opencode",安装之后一般会在侧边栏多出一个面板。这个面板本质上还是和同一个opencode服务对话,但在IDE集成下,你选中某段代码、右键发送给opencode,或者让它在当前文件上下文里直接改代码,体验会比终端更丝滑。
个人体会是:VSCode插件适合"看得见的操作",比如改某个文件、解释某段代码、生成单测;终端TUI适合"全局操作",比如跨多个文件的重构、跑一遍完整测试流程。两者我都在用,切换也不麻烦。
另外还有个桌面版(搜索词里的"opencode desktop")。它更像是给不想碰命令行的重度IDE用户提供的图形入口,能做基础的对话和文件预览。我实测感觉桌面版目前还偏尝鲜,核心能力还是在CLI和插件里。
5.2 JetBrains系插件
JetBrains全家桶(IDEA、PyCharm、WebStorm等等)也有opencode插件,安装方式同样是到插件市场里搜"opencode"。装上之后,它在JetBrains里的交互逻辑跟VSCode插件类似,但有几个细节体验更好:
一是能直接感知当前打开的文件和编辑器的选中内容,你问"这段代码怎么优化",它知道你说的是哪一段,不太需要粘代码进对话框。二是运行命令时,那行"执行哪个命令、在哪个目录"的确认弹窗比VSCode插件的终端反馈更清晰,误操作概率更低。
不过JetBrains插件目前的版本迭代速度不如VSCode插件勤,偶尔会遇到AI生成的代码改动没有自动格式化、光标跳回原点这类小问题。如果遇到,检查一下插件的设置里有没有"自动格式化"开关,把它打开基本能解决。
6. 实战:用opencode接手一个旧项目
6.1 第一次对话前,先做这些准备
很多人拿到opencode以后,第一句话就是"帮我处理一下这个项目",结果AI一脸懵。这里有个使用习惯问题:它再强,也需要你对项目做几个简单的"初始化动作"。
我的标准流程是这样:
先在项目根目录跑一次opencode /init,让它自己扫描一遍项目,生成一份初始的AGENTS.md。然后我手动补充几项关键信息:启动命令、测试命令、依赖安装方式、不要乱动的目录。接着我会先把项目在本地跑起来,确保没有环境问题。最后才开始提需求。
这一步的价值在于:AI改完代码后需要自己跑命令验证,它知道启动命令和测试命令,才能形成"改代码—验证—修复"的闭环。如果你跳过这些,它只能盲改,你就得手动反复验证,效率大打折扣。
6.2 让它快速定位并修复一个前端Bug
用一个我实际遇到过的场景演示:接手一个老后台管理系统,用户反馈"列表页搜索框输入关键字后,点击搜索无反应,控制台也没有报错"。
我当时的做法是直接在opencode里输入:
项目列表页的搜索功能失效了。 请先定位相关前端代码,找到搜索按钮的事件绑定逻辑, 分析为什么点击后没有触发请求,然后给出修复方案。 如果确认是代码问题,直接修改。opencode的处理路径很典型:先搜索包含"搜索"或"search"关键字的文件,定位到列表页组件,发现按钮点击绑定的handler里调用了一个已经被删除的API方法,而且这个方法在旧代码里是异步的,异常被吞掉了,所以控制台没有报错。它告诉我根因,并改成了调用新的API方法,同时补了一个简单的try/catch,避免异常被静默吞掉。
整个定位过程大概两分钟。如果换成我自己上手翻代码,至少得十分钟起步,还要看编译、看网络请求日志。典型的"AI接手旧项目"场景里,opencode这种先搜索全局、再读上下文、再动手改的能力,确实能省下大量时间。
6.3 把Playwright用起来:自动复现前端问题
前端Bug最麻烦的不是改,而是复现。opencode对这个问题有一个很实用的配合方案:Playwright集成。Playwright是一个浏览器自动化测试工具,opencode可以调用它打开页面、点击按钮、填写表单、截图,然后把结果反馈给AI。
我让opencode处理前端Bug的进阶流程是这样:
先跟它说清楚能复现的步骤,比如"登录后进入系统管理-用户列表,点击新增按钮,填写表单并保存,前端卡住了"。它如果判断需要浏览器验证,会先确认项目里有没有装Playwright依赖,没有就装,然后写一段测试脚本,启动本地开发服务,自动打开浏览器执行上面的步骤。
如果页面出现异常,它能看到控制台报错、截图和网络请求数据,然后回到代码层做定位。修完之后,它还能用同一个Playwright脚本再复现一遍,确认修复生效。整个"复现—定位—修复—回归"的链路,在理想情况下十几分钟就能走完。
这个功能对复杂交互场景特别值:涉及到登录态、多步骤表单、第三方嵌入页面的Bug,纯靠人肉点浏览器复现,经常半小时起步。用Playwright先从自动化角度把问题固定下来,后续修起来就安心很多。
7. 高频报错排查实录
7.1 启动报 unexpected server error,到底怎么回事
搜索词里有一条很典型:c:\windows\system32>opencode error: unexpected server error. check server lo...。这个报错我遇到过一次,当时一度以为是opencode崩了,后来才发现问题出在它内置的本地服务上。
opencode的架构里,CLI和模型请求之间有一个本地server,TUI、IDE插件都是通过这个server和模型交互的。如果这个server没起来,或者起来了但端口被占用、运行时组件损坏,就会抛unexpected server error。
排查顺序我建议这样来:
第一步,看日志。opencode会在配置目录或临时目录里写日志文件,具体路径可以通过TUI里的日志命令查看。Windows下也可以直接看命令行窗口是否有更早的警告信息。
第二步,杀掉残留进程。我在Windows上遇到过几次:程序没正常退出,后台留了一个老的opencode server进程,新开终端再运行就起冲突了。用任务管理器或者命令查找node/bun相关进程,结束掉再重开。
第三步,清理运行时缓存。把配置目录下的缓存文件删掉,重新启动,让opencode重新初始化。注意删之前确认你配置文件里的APIKey还有备份,别把配置一起删了。
绝大多数unexpected server error都能靠这三级排查解决。如果还不行,去GitHub仓库的Issues里搜一下同样的报错关键词,多半能找到临时workaround。
7.2 模型调用返回401/超时
这个属于接入模型时的经典问题。返回401,说明opencode已经配好能正常发起请求,但模型服务商不认你的key。先核对key有没有多余的引号或空格,再看配置文件里apiKey字段加载的是环境变量还是硬编码,最后确认你配置的baseURL跟所填的key是不是同一家服务商的。
超时问题则要看模型侧负载。免费模型的限流最明显,字一多、对话轮次一长就容易挂。建议长任务拆短,或者改用付费模型。另外也可以看看opencode配置里有没有超时时间相关的参数,适当调大能减少"半路断连"的情况。
7.3 还有几个容易忽略的小坑
第一个坑是中文路径。Windows下如果项目目录带中文或特殊字符,opencode在跑自动脚本时偶尔会解析出问题。我建议开发环境里尽量用英文路径。
第二个坑是Skill装多了不生效。很多人把superpowers、oh-my-claudecode一股脑全装完,结果发现有些技能在对话中触发不了。原因经常是Skill描述里的触发关键词和你实际问法对不上,或者多个Skill的description太相似,AI判断错了。遇到这种情况,可以在对话里明确指名要用哪个技能,比如"用code-review技能审查这段代码"。
第三个坑是版本更新后配置结构变了。opencode迭代速度极快,两三个月就能有较大的配置体系变化,网上教程很多已经过时。遇到配置项不识别、字段命名不同这类问题,先看官方文档的changelog,别急着怀疑自己写错了。
至于搜索词里那段"opencode mvn配置",我猜测多半是"mcp配置"的输入法手滑。MCP(Model Context Protocol)是给AI挂外部工具和知识源的标准协议,opencode支持MCP接入,但那是进阶用法,初学阶段不用着急。等你能熟练用Skill了,再研究MCP也不迟。
最后分享一个小习惯。我在实际使用中,始终把AGENTS.md当项目资产来维护,每次完成新功能会让opencode把决策记录追加进去。时间一长,它对这个项目的理解和判断会越来越接近"真正干过这个项目的老员工"的水平。这可能是整个工具里,投入产出比最高的一件事。