先说结论:在终端里用AI写代码这件事,大部分人只停留在“装个CLI、配个模型、开始聊天”的阶段,真正让AI从“会聊天”变成“能干活”,靠的是外围这一整套约定、技能和配置。OpenCode本身只是引擎,引擎再猛,没有规范、没有技能库、没有一套趁手的配置体系,跑起来也是裸奔。这篇文章把OpenSpec、Superpowers、Oh-My-OpenCode这三样东西逐个拆开讲清楚,再把它们组合进OpenCode的完整流程走一遍。适合已经装过OpenCode但觉得“效果就那样”的人,也适合正准备从零搭一套终端AI开发环境的朋友。
1. 先把这几个项目的关系理顺
1.1 OpenCode是底座,但它默认状态确实“太素了”
OpenCode是一个用Go写的开源终端AI编程助手,界面是TUI风格,启动后在终端里直接开一个交互会话。它能读文件、改文件、跑命令、看Git状态,也能通过opencode run "任务描述"这种非交互方式一口气执行任务。跟Web端对话的最大区别在于:模型拿到了真实的项目上下文,而不是靠你把代码一段段粘进去。
很多人的第一反应是“那它本身就够用了,为什么还要装OpenSpec和Superpowers?”答案很简单:OpenCode默认只给了你一个空的会话框架,模型虽然有上百K甚至上M的上下文窗口,但它不知道你的项目规范是什么、不知道你们团队的开发流程是什么、也不知道“改完代码要跑哪些验证”才算完成。你就等于雇了一个能力很强但完全没培训过的新人,所有工作习惯都要现场教。
这就是为什么要做二次配置。OpenCode提供了几个扩展点:项目级AGENTS.md、全局配置文件~/.config/opencode/opencode.json、以及后来引入的skills(技能)机制。OpenSpec解决“按什么标准干活”的问题,Superpowers解决“有哪些成熟工作方法可以直接调用”的问题,Oh-My-OpenCode解决“这一大堆配置怎么管理、怎么一键复用”的问题。
1.2 OpenSpec:把“需求”变成“规格”,让AI不再瞎猜
OpenSpec是一套“规范驱动开发”的工具和约定。它的核心想法非常朴素:大多数AI编码翻车,不是模型能力不行,而是需求描述太模糊。模型不是不想做对,是根本不知道“对”的定义是什么。OpenSpec的做法是在项目里维护一个openspec/目录,用结构化Markdown文件把需求、方案、变更内容、验收条件全部沉淀下来。AI在动手之前先读规范,写完代码之后按规范的验收点逐项核验,而不是凭聊天记录里的几句话自由发挥。
这个思路最打动我的地方在于:它把“需求分析”这个本该由人完成的环节重新拉回了开发流程。以前用AI写代码,经常是我说一句“帮我加个导出功能”,模型就直接开干,干出来的东西跟我要的差很远。而OpenSpec强制我先写一份“变更提案”,把现状、目标、涉及模块、风险点、验收标准写清楚,然后模型才动手。听起来多了一步,实际上省了后面好几轮返工。
1.3 Superpowers:把AI从“通用助手”变成“专业选手”
Superpowers最初是给Claude Code设计的一套技能集,作者是Jesse Vincent(GitHub上的obra)。它针对编码场景把大量方法论固化成了技能文件,每个技能就像一份“岗位培训手册”,告诉AI遇到什么场景该用什么流程。例如“brainstorming”技能,要求AI先展开多轮发散讨论而不是直接给方案;“spec-driven-development”技能,强制先写规格再写代码;“test-driven-development”技能,则要求先写失败测试再实现。
OpenCode从v2开始支持原生Skills机制,.claude/skills里的技能文件经过少量适配就能被OpenCode读取。这意味着Superpowers里的那几十个成熟技能,可以直接搬进OpenCode用。换句话说,Superpowers补的是OpenCode的“方法论短板”,让模型不止是知道怎么调用工具,还知道什么时候该用哪种工作方式。
1.4 Oh-My-OpenCode:配置界的“收纳师”
Oh-My-OpenCode是一个面向OpenCode的配置集项目,命名致敬了Oh-My-Zsh。它把社区沉淀下来的主题、快捷键、模型参数、AGENTS.md规则、常用提示词片段打包成一套可切换的“配置档”。以前你要手动往配置文件里塞一堆JSON,现在直接引入Oh-My-OpenCode,再挑需要的模块启用就行。它解决的是“配置本身变得复杂之后怎么维护”的问题,属于配置之上的配置框架。
1.5 组合之后,开发范式变成了什么样
把这套组合装齐以后,一次完整的任务通常会走这样的路径:我先写一份OpenSpec变更提案,明确需求和验收标准,然后让OpenCode读取提案,按Superpowers里的技能库选择适合的工作方法,比如先做技术方案、再逐文件实现、再跑测试验证。整个过程里,模型的行为是有章法的,输出是可预期的,配置是统一管理的。下面从安装开始一步步来。
2. 环境准备:装好OpenCode并完成初始配置
2.1 安装方式与版本选择
OpenCode官方推荐一行命令安装,macOS和Linux都可以用:
curl -fsSL https://opencode.ai/install | bash如果你用的是Homebrew,也可以走tap方式:
brew install sst/tap/opencodenpm同样有对应的包,早期包名是opencode-ai,装完以后终端命令为opencode:
npm install -g opencode-ai我的建议是优先用官方脚本或Homebrew。npm方式虽然也能用,但OpenCode更新节奏非常快,npm包的发布偶尔会比官网渠道慢半拍。安装完成后先确认版本,OpenCode v2之后功能变化很大,如果你的版本太老,后面讲到的skills配置可能对不上:
opencode --version2.2 配置文件目录:先弄清楚每一层放什么
OpenCode的配置目录默认在~/.config/opencode/,项目级配置则在当前项目的opencode.json。全局配置存放跟个人偏好相关的内容,比如默认模型、主题、全局指令;项目级配置存放跟仓库绑定的约定,比如启用哪些技能、项目的验收流程、禁止修改哪些文件。
还有一个容易被忽略但极其重要的文件:AGENTS.md。这个文件会被OpenCode自动注入到每次会话的上下文里,作用相当于“给AI立规矩”。全局的AGENTS.md放在~/.config/opencode/AGENTS.md,项目级的放在仓库根目录。实际项目中,我在项目级AGENTS.md里写的是:
- 项目技术栈和目录结构说明
- 代码风格约定(缩进、命名、测试方式)
- “禁止直接修改哪些文件”等硬性规则
- 完成一项任务前必须执行的验证命令
这样每次会话开始,AI不需要我重新解释项目背景,读一遍AGENTS.md就掌握了基本盘。
2.3 接入模型Provider
OpenCode支持多种模型来源。最省事的方式是用官方Console,通过opencode auth login登录后可以直接使用内置额度;也可以配置自己的API Key,用环境变量或配置文件指定。以Anthropic模型为例,设置环境变量:
export ANTHROPIC_API_KEY=sk-ant-xxxx再通过配置文件指定默认模型:
{ "$schema": "https://opencode.ai/config.json", "provider": { "anthropic": { "models": { "claude-sonnet-4-5": { "name": "claude-sonnet-4-5" } } } }, "model": "claude-sonnet-4-5" }如果你主要使用本地模型,比如通过Ollama跑Qwen、Llama,也可以配置本地Provider。对于日常编码,我实测下来长上下文模型优势明显,因为OpenCode会持续往上下文里堆项目信息,上下文窗口不够的话,会话后半段模型基本就“失忆”了。
2.4 验证基本链路通不通
配置完成后,先在项目目录里跑一个最简单的任务测试链路:
opencode run "读取当前目录结构,用三句话总结这个项目是做什么的"如果模型能正常回答,说明OpenCode、Provider、目录读取都正常。这时候先别急着继续装别的,把这个最基本的环境稳定下来。我见过不少朋友一口气装了一堆东西,最后出问题都不知道是模型没配对还是技能路径配错,排查起来特别痛苦。
3. 接入OpenSpec:把规范驱动开发落到项目里
3.1 初始化OpenSpec目录结构
OpenSpec通过一套约定来组织规范文件,官方文档在openspec.dev。安装CLI后(可以全局安装也可以项目内安装),首先在项目根目录执行初始化:
openspec init这个命令会在项目里生成openspec/目录,典型结构大致如下:
openspec/ ├── specs/ │ ├── capabilities.md │ └── ... ├── projects/ │ └── ... └── changes/ └── ...specs/保存领域能力和现有行为的规格说明;changes/保存待实施的变更提案,也就是每次具体需求的“施工图”;projects/则把多个变更聚合成一个项目级的交付计划。上手阶段,重点用changes/就够了。
3.2 创建第一份变更提案
OpenSpec提供了CLI命令来创建变更模板,比如:
openspec new change "add-user-export"生成的文件里会包含几个固定章节:现状(Current State)、目标(Desired State)、实现方案(Implementation Plan)、受影响模块(Impacted Areas)、验收标准(Acceptance Criteria)。这些章节不是装饰,它们是后续让AI按规范执行的关键。
很多人第一次写变更提案时容易犯一个错误:把实现方案写得特别细,细到“第几行代码改成什么”。这完全走偏了。变更提案的重点是描述“要什么结果”和“怎么验证结果”,而不是替AI决定每一行代码怎么写。实现细节应该允许模型在框架内自己发挥,验收标准才是必须钉死的。我一般是这么写的:
- 现状:当前用户列表只能查看,无导出功能
- 目标:支持按当前筛选条件导出CSV文件,文件包含用户ID、姓名、邮箱、注册时间
- 实现方案:在用户列表页增加“导出”按钮,前端调用后端新增的
/api/users/export接口,后端从数据库读取数据并生成CSV流式返回 - 验收标准:筛选条件为“近30天注册”时,导出文件只包含近30天的用户数据;导出文件前两行为表头和分隔线;字段与需求完全一致;导出过程中界面不阻塞
这份提案写完后,直接丢给OpenCode,再补充一句“按照这份变更提案实施”。模型会读取规范内容,按照方案和验收标准来干活,而不是凭感觉写个导出功能完事。
3.3 在OpenCode中执行规范工作流
OpenSpec官方还提供了一个配套技能包,用来让AI代理直接操作规范文件。原理不难理解:通过技能文件告诉模型“你的项目里有一套规格文件,动手前先读一下openspec/目录,变更前先确认有没有对应的变更提案,没有就先写提案,写完提案再开始写代码”。
实际操作时,我在OpenCode里通常这样调用:
opencode run "先读取 openspec/changes/add-user-export.md,然后按提案完成实现,最后逐条核对验收标准并在回复中给出核对结果"对比一下:没有OpenSpec时,同样的任务我会写一大段提示词描述需求,然后模型自由发挥;有了OpenSpec之后,我需要写的提示词大幅缩短,因为需求的权威描述已经沉淀在规范文件里了。而且验收标准是结构化的,模型在执行结束后可以自己去检查,比如运行测试、检查导出文件的列头,形成闭环。
3.4 什么时候需要更新specs
当一次变更已经完成并合入主干,应该把对应的变更提案状态更新掉,并把新的能力同步到specs/里。这一步骤常常被人忽略,导致规范文件很快就过期,最后没人再信任它。我的习惯是:每个功能合入前,先确保OpenSpec文档和代码一起提交。代码改了而规范没更新,这个PR不应该被合入。很多团队把这个写进AGENTS.md里,让模型在收尾阶段自动检查。实测下来,这个习惯养成了,AI写代码的“稳定性”会有质的提升。
4. 引入Superpowers:让AI拥有可复用的专业技能
4.1 先理解Superpowers的技能机制
Superpowers的核心单元是“技能”(skill)。一个技能本质上是一个包含SKILL.md文件的目录,里面用Markdown写清楚:这个技能解决什么问题、前置条件是什么、执行流程分几步、每一步要产出什么结果。当OpenCode加载了这个技能目录后,模型在应对匹配场景时,会自动把对应的流程当作行动指南。
例如brainstorming技能会强制模型:先收集背景信息、再提出多个备选方案、逐个评估优缺点、最后才确定方案。默认情况下,模型总是倾向于直接给出一个“看起来合理”的答案,但这个技能会逼它慢下来。对于复杂功能设计,慢反而是快。
4.2 获取Superpowers技能库并接入OpenCode
Superpowers项目托管在GitHub上,作者是obra。把它克隆到本地:
git clone https://github.com/obra/superpowers.git ~/.superpowers克隆完成后,核心内容在skills/目录下,里面是按技能名组织的子目录。接下来把它接入OpenCode。OpenCode的skills目录可以是全局的,也可以是项目级的。我推荐放全局,这样所有项目都能共享:
mkdir -p ~/.config/opencode/skills cp -r ~/.superpowers/skills/* ~/.config/opencode/skills/如果你的OpenCode版本比较新,直接在配置文件的skills相关字段里指一下路径,或者在AGENTS.md里声明技能目录位置即可。不同版本的配置键名略有差异,但核心机制是通用的:把技能目录放进OpenCode的加载范围,让模型在会话中能读到这些SKILL.md文件。
Superpowers里有一批技能是直接从Claude Code生态平移过来的,OpenCode读取时基本不区分来源,只要你确保目录结构里有SKILL.md就行。
4.3 常用技能逐个体检
挑几个我日常用得最多的技能说一说:
- brainstorming:设计新功能时启用,别名类似“先发散再收敛”。它适合需求还不明确、需要探索方案的场景。
- writing-plans:把任务拆解成有序步骤,并在动手前写出完整计划。适合改动范围较大的任务。
- executing-plans:配合上面的计划使用,按计划逐步执行,而不是跳着乱改。
- spec-driven-development:和OpenSpec配合的专用技能,要求先读规格文档、再写测试、再实现。
- test-driven-development:强制先写失败测试再实现代码,对要求高测试覆盖率的项目很实用。
- request-review:代码写完后让AI以审查者身份重新审视改动,寻找边界问题和遗漏。
这些技能不是每个项目都要全量启用。比如我维护一个内部工具项目,没有严格要求覆盖率,那test-driven-development就关掉,否则每次改动都会额外生成一大轮测试文件,反而增加负担。技能是工具,不是教条,按需启用才合理。
4.4 通过Slash命令直接触发技能
OpenCode支持在交互会话里用斜杠命令唤起特定技能。例如输入/superpowers:brainstorming,或者直接写“使用brainstorming技能讨论一下这个需求”。在非交互模式下,则通过自然语言在任务描述里显式点名技能:
opencode run "使用brainstorming技能,围绕登录模块改造产出一份3个方案的技术对比"核心技巧是:不要指望模型主动想起来该用哪个技能。虽然技能描述会进入上下文,但模型在长任务中很容易忽略,尤其是在上下文压缩后。显式指定技能,是保证行为可控的最有效手段。
4.5 按团队情况微调技能内容
Superpowers的技能文件默认是为通用场景设计的,直接搬进团队项目时,我建议按项目特点做微调。比如writing-plans技能默认的输出粒度是“一个步骤一个代码块”,但我们团队习惯的是“一个步骤对应一个commit”,于是我直接在SKILL.md里加了一条:每个步骤结束后都必须运行测试并提交一次commit。这种微调不需要改代码,只需编辑Markdown里的描述。
有一点要提醒:技能文件本身也是会被模型读取的“提示词”,修改时注意保持结构清晰,不要堆砌无意义的口水话。模型对结构化的流程描述响应最好,对抒情式文字基本免疫。
5. 用Oh-My-OpenCode统一配置与主题
5.1 Oh-My-OpenCode提供的核心内容
Oh-My-OpenCode是一个把OpenCode配置变成“模块化”的项目。打开它的仓库会发现,它把很多零散的配置项做成了可开关的“模块”:
- 多套主题,包括Light、Dark、Dracula、Monokai等
- 模型供应商的预设配置
- 一组开箱即用的AGENTS.md规则(例如“不要修改锁定文件”“提交信息使用英文”等)
- 常用slash命令的预制片段
- 编码风格偏好模板
它的价值不在于配置内容多神奇,而在于“整理好了”。我见过很多人的OpenCode配置文件越加越乱,最后自己都忘了哪些配置是干什么的。Oh-My-OpenCode强制你用模块化结构来组织配置,后续加新功能时,只在对应的模块目录里加文件,不影响主配置文件。
5.2 安装与启用
安装方式很直接,克隆仓库后,把配置目录软链接到OpenCode的全局配置目录:
git clone https://github.com/xxx/oh-my-opencode.git ~/.oh-my-opencode ln -s ~/.oh-my-opencode/opencode.json ~/.config/opencode/opencode.json ln -s ~/.oh-my-opencode/AGENTS.md ~/.config/opencode/AGENTS.md具体路径以项目文档为准,不同版本略有差异。启用后,重启opencode就能看到新主题和新增的全局规则。
5.3 主题切换与模块管理
Oh-My-OpenCode通常会在配置文件里支持通过一个字段切换主题,比如:
{ "theme": "dracula" }模块的启用方式一般是增删目录或修改配置文件里的开关项。我的习惯是:把项目无关的个性化配置全交给Oh-My-OpenCode管理,把项目相关的规则放在项目级AGENTS.md里。前者负责“我个人的偏好”,后者负责“这个项目的硬性约束”,两者不交叉。
5.4 自己的配置如何覆盖默认值
OpenCode的配置加载是有优先级的,理解这个才能避免“改了没生效”。我的经验里,大致优先级从高到低是:
| 配置层级 | 说明 |
|---|---|
项目级opencode.json | 当前仓库生效,覆盖全局设置 |
项目级AGENTS.md | 当前仓库的AI行为约束 |
全局AGENTS.md | 所有项目的底线规则 |
全局opencode.json(含Oh-My-OpenCode) | 作为默认值兜底 |
也就说说,如果某个行为在两个层级里冲突了,项目级的会赢。比如全局AGENTS.md里写着“所有提交信息用英文”,项目里写“项目内提交用中文”,模型会优先遵守项目级的规则。这个优先级模型想清楚后,配置冲突的问题一下就明朗了。
6. 三件套联动:从需求到提交的完整实战流程
6.1 一次完整任务的分步记录
假设我要给项目加一个“从后台导出操作日志”的功能。在没搭这套体系时,流程通常是:打开opencode,说一句“帮我加导出功能”,然后焦虑地看它自由发挥。搭了OpenSpec+Superpowers之后,流程变成了:
第一步,在OpenCode会话里让模型先把需求变成OpenSpec变更提案:
opencode run "使用spec-driven-development技能,为'导出操作日志'功能创建一份变更提案,写到openspec/changes/add-log-export.md,需要包含现状、目标、影响范围、验收标准"第二步,人工审一遍提案,确认验收标准符合预期。这一遍非常关键,因为后续AI的全部行为都会围绕这份提案展开,提案逻辑有漏洞,代码一定跑偏。
第三步,让模型实施:
opencode run "读取openspec/changes/add-log-export.md,按提案实施。每个文件改完后运行相关测试,最后逐条核对验收标准"第四步,模型汇报核对结果,我抽查关键改动,确认无误后提交代码。整个过程,AI的行为一直有据可依,而我不需要在一轮轮的对话里反复纠正。
6.2 与Git工作流结合的几个操作习惯
OpenCode本身支持读写Git状态,配合OpenSpec后,我有了几个固定习惯:
- 变更提案先提交一次,作为任务的分界点
- 实现完成后,用
opencode run "查看git diff,检查是否偏离了变更提案"做一次自检 - 让模型生成提交信息时,强制它引用变更提案的ID
这些习惯最终都是为了让“从需求到代码”的链路可追溯。OpenSpec的规范文件就是这条链路上的锚点,代码改动和需求一一对应,团队review的时候也不用再猜“这个人为什么写这段代码”。
6.3 哪些环节仍然必须人来把关
把话说回来,三件套再好,有几个环节我绝不会完全交给AI:
- 变更提案里“目标”的定义,必须人工确认,AI容易把目标扩大或缩小
- 实现层面的“取舍判断”,比如“这个方案性能差但代码简单,当前阶段能不能接受”,这类权衡必须人来定
- 涉及敏感操作、数据迁移、权限修改的任务,AI只能出草案,不能自动执行
AI是执行者,不是决策者。这套配置的本质,是把执行环节的质量往上抬,而不是把决策环节外包出去。想清楚这一点,工具才不会反过来制造麻烦。
7. 避坑手册与常见问题
7.1 装完Superpowers后技能不生效
最常见的原因有三个:技能目录放错了位置、SKILL.md格式不对、配置里没有声明技能目录。排查顺序如下:
- 确认技能目录在OpenCode的加载范围内:全局技能放
~/.config/opencode/skills,项目技能放项目根目录的.opencode/skills - 确认每个技能目录下都有
SKILL.md文件,且文件名大小写正确 - 在交互会话里用“查看你加载了哪些技能”的方式让模型自述,能列出说明加载成功,列不出就是路径或配置问题
还有一个容易被忽略的坑:技能数量过多时,OpenCode不会把全部技能的详细内容塞进上下文,只会按描述做一次粗筛选。所以技能里的描述字段一定要写清楚适用场景,描述模糊的技能等于不存在。
7.2 OpenSpec提案写得过细或过粗
写提案时我见过两种极端。一种是把实现细节写到函数级别,模型没有发挥空间,遇到意外情况不会灵活处理;另一种是验收标准写得太虚,比如“功能正常”,这种标准等于没写。我的参考标准是:验收标准必须能被一个工程师通过跑命令或检查文件来客观验证。比如“点击导出按钮后,浏览器下载一个名为log_20250101.csv的文件”就是合格标准;“用户体验要好”就是不合格标准。
7.3 配置优先级不清导致“改了没用”
如果你改了配置文件但行为没变,先确认是哪一个层级的配置在生效。特别是Oh-My-OpenCode接管了全局配置之后,你可能会在项目里新写一个opencode.json,但发现某些全局规则还是被带进来了。这恰恰是优先级设计的一部分:项目配置覆盖全局配置,但覆盖是字段级的,不是文件级的。意思是,项目配置里没写到的字段,仍然会从全局配置继承。理解了这一点,就不会误以为“我写了项目配置,全局配置就全部失效了”。
7.4 上下文爆炸与费用控制
同时开启OpenSpec和Superpowers后,上下文占用会比裸OpenCode明显增加,因为规范文件和技能文件都会占用token。我的做法是:
- 技能只启用团队真正在用的五到六个,不搞全家桶
- 大任务拆成多个阶段,每个阶段单独启动会话,避免上下文越积越长
- 费用敏感时使用更便宜的模型跑“读规范+写测试”这类辅助环节,但“最终实现”仍然用强模型
还有一点,不要把整个openspec/目录所有文件都丢给模型读,它只需要读当前变更提案和相关的规格文件。在AGENTS.md里写明“只读取openspec/changes目录下最新的变更文件”,能省下大量token。
7.5 升级OpenCode v2之后的兼容性问题
OpenCode v2是最近一次大版本升级,配置格式和skills机制都有调整。如果你是从更早版本升上来的,升级后要做三件事:确认配置文件格式是否符合新schema、重新声明skills目录、检查Provider配置。最稳妥的做法是升级后先跑一遍opencode --version确认版本,再新建一个临时目录测一次基础对话和技能加载,没问题再切回正式项目。
我在升级时踩过一次坑:全局配置里用的旧字段被新版本忽略了,但OpenCode没有报错,只是静默用默认值跑,导致我一度以为模型变笨了。后续排查才发现是配置没迁移。自那以后,每次大版本升级,我都要把配置目录备份一次,然后逐个验证核心功能。
7.6 常见问题速查表
| 现象 | 可能原因 | 排查/解决 |
|---|---|---|
| 技能列表为空 | skills目录路径不对 | 检查全局和项目级skills目录是否存在 |
| 模型无视变更提案 | 提案文件内容模糊 | 检查验收标准是否客观可验证 |
| 配置改了没生效 | 配置优先级覆盖 | 确认项目级配置是否挡住了全局配置 |
| 上下文过长 | 规范文件或技能读太多 | 在AGENTS.md中限定读取范围 |
| 升级后行为异常 | 配置文件用了旧字段 | 对比新版本schema并迁移配置 |
| 模型输出不稳定 | 没有显式指定技能 | 在任务描述里点名技能名称 |
这篇文章写到这里,核心配置链路已经全部走完了。最后再分享一个实际体会:OpenCode、OpenSpec、Superpowers、Oh-My-OpenCode这四样东西,单拎出来每一个都不复杂,难的是把它们组合起来,并坚持“规范先行、技能驱动、配置收敛”这三个原则。我在团队里推行这套体系时,最大的阻力不是技术问题,而是人习惯性地想让AI赶紧出代码。但坚持了两周后,大家普遍反馈返工次数明显下降,代码review也轻松了很多。如果你正准备搭建自己的终端AI开发环境,我建议从OpenCode加OpenSpec这一步开始,跑顺一个需求闭环之后,再加入Superpowers的技能库,最后用Oh-My-OpenCode把配置整理好。一步步来,比一次性配齐更容易沉淀出真正适合自己的工作流。