技术选型系列文章(四):从任务清单到接口规格

前言:代码不是写出来的,是"规"出来的

前三篇文章,我写了项目背景、技术选型、架构设计。文章发出去后,不少朋友问我:"你这些文档写得这么细,那代码到底怎么写?是不是还是要自己手写?"

我的答案是:代码不是手写的,也不是 AI 瞎写的,是"规"出来的。

在把任务清单交给 AI 之前,我花了整整一周,做了一件看起来"最枯燥"的事:把每一个任务,升级成"接口规格文档"

这篇文章,我就讲这个过程------怎么把"打开 B 站"这个需求,变成 AI 能直接生成代码的"函数签名 + 参数说明 + 验收标准"。

一、从"任务清单"到"接口规格":为什么不能跳步

在第三篇文章结束时,我的项目已经有了三份任务清单(边缘端、云端、安卓端),每份清单都拆到了"原子任务"粒度。

任务清单长这样 问题
E7.2: 实现 open_app 命令 AI 不知道函数名、参数类型、返回值、错误处理
C6.1: 定义 open_app 工具 AI 不知道工具描述、参数 schema、返回格式
A4.1: 实现 AudioRecorder 类 AI 不知道有哪些方法、参数、回调

如果我直接把这份清单丢给 AI,它会怎么做?

  • 第一次生成:函数名叫 OpenApp

  • 第二次生成:函数名叫 LaunchApp

  • 第三次生成:参数用 pkg,第四次用 packageName

每次都不一样。不是 AI 不行,是我没给它"标准答案"。

所以,在让 AI 写代码之前,我必须先把"标准答案"写好------这就是接口规格文档。

二、接口规格文档长什么样

我把每个任务,都升级成包含以下内容的规格:

要素 说明
文件路径 代码放哪
函数/类/接口签名 完整的代码定义
参数说明 类型、含义、示例
返回值说明 成功/失败的结构
实现逻辑 关键步骤(不是代码,是步骤)
验收标准 可执行的命令或断言

边缘端 E7.2 的接口规格(节选)

markdown

复制代码
### E7.2: 实现 open_app 命令

**文件**: `internal/adb/adb.go`

**函数签名**:
```go
func (c *ADBController) OpenApp(ctx context.Context, packageName string) error

参数:

  • ctx: 超时控制,默认 5 秒

  • packageName: 应用包名,如 tv.danmaku.bili/.ui.splash.SplashActivity

返回值:

  • nil: 成功

  • error: 失败时返回,包含错误码 E1002

实现逻辑:

  1. 先调用 ensureConnected(ctx) 确保 ADB 已连接

  2. 执行 adb shell am start -n {packageName}

  3. 执行成功返回 nil,失败返回错误

验收标准:

  • 单元测试:TestOpenApp 验证命令拼接正确

  • 手动测试:OpenApp(ctx, "tv.danmaku.bili/...") 后,电视打开 B 站

text

复制代码
### 云端 C6.1 的接口规格

```markdown
### C6.1: 定义 open_app 工具

**文件**: `src/tools/openApp.ts`

**工具定义**:
```typescript
export const openAppTool = tool(async ({ packageName }) => {
  return JSON.stringify({ action: "open_app", params: { package: packageName } });
}, {
  name: "open_app",
  description: "打开电视上的应用",
  schema: z.object({
    packageName: z.string().describe("应用包名,如 tv.danmaku.bili/.ui.splash.SplashActivity")
  })
});

验收标准:

  • 调用 openAppTool.invoke({ packageName: "tv.danmaku.bili/..." }) 返回 {"action":"open_app","params":{"package":"..."}}

text

复制代码
### 安卓端 A4.1 的接口规格

```markdown
### A4.1: 实现 AudioRecorder 类

**文件**: `app/src/main/java/com/homesense/AudioRecorder.kt`

**接口定义**:
```kotlin
class AudioRecorder(private val onResult: (File) -> Unit) {
    fun start(durationMs: Long = 3000)
    fun stop()
    fun release()
}

实现细节:

  • 使用 MediaRecorder

  • 输出格式:OutputFormat.MPEG_4

  • 音频编码:AudioEncoder.AAC

  • 采样率:16000,声道:单声道

  • 文件路径:context.cacheDir/audio_${timestamp}.m4a

验收标准:

  • 调用 start(3000),3 秒后 onResult 收到文件,文件大小 < 20KB

text

复制代码
## 三、我花了多久,值得吗

三份任务清单,Phase 1 一共 30 多个核心任务。我花了 **3 天**,把每个任务升级成接口规格。

这 3 天值不值?

| 对比 | 没接口规格 | 有接口规格 |
|------|-----------|-----------|
| AI 生成代码 | 每次猜,可能 3 轮才能对 | 1 轮通过 |
| 代码一致性 | 同一功能不同命名 | 全项目统一 |
| 验收 | 手动看代码对不对 | 跑验收命令,过就是过 |
| 返工率 | 约 40% | < 5% |

**3 天的投入,换来后面 2 周零返工。这笔账,太值了。**


## 四、接口规格文档的“铁律”

在写接口规格时,我给自己定了三条铁律:

### 铁律 1:签名必须完整

```go
// 不完整
func OpenApp(pkg string) error

// 完整
func (c *ADBController) OpenApp(ctx context.Context, packageName string) error

铁律 2:参数必须有示例

typescript

复制代码
// 不完整
packageName: z.string()

// 完整
packageName: z.string().describe("应用包名,如 tv.danmaku.bili/.ui.splash.SplashActivity")

铁律 3:验收必须可执行

markdown

复制代码
// 不可执行
验收标准:功能正常

// 可执行
验收标准:
- 单元测试:`go test -run TestOpenApp` 通过
- 手动测试:`OpenApp(ctx, "tv.danmaku.bili/...")` 后,电视打开 B 站

五、拿到接口规格后,AI 的反应

我把接口规格文档发给 AI 后,它的表现让我吃惊:

以前

我:实现 open_app 命令

AI:好的,我创建了一个 LaunchApp 函数...

我:不对,函数名应该是 OpenApp

AI:好的,我改成 OpenApp...

我:参数名应该是 packageName

AI:...

现在

我:请按 E7.2 接口规格实现 open_app 命令

AI:已实现 internal/adb/adb.go 中的 OpenApp 方法,参数为 packageName,包含超时控制和错误码 E1002。单元测试已通过。

一次过。

六、这个过程教会我什么

  1. AI 不是读心术,你需要给它"标准答案"

    接口规格就是标准答案。写得越细,AI 猜得越少。

  2. 3 天的投入,换 2 周的零返工

    前期懒,后期苦。接口规格是最值得花时间的地方。

  3. 接口规格不是"写文档",是"定契约"

    你写的不是给 AI 看的说明书,是你和 AI 之间的合同。合同明确,执行就稳。

  4. 验收标准必须是"机器能跑的"

    "功能正常"是废话。"go test 通过"才是验收。

七、下一篇预告

接口规格写完了,任务清单变成了"可执行合同"。下一篇文章,我会写:

《第五篇:逐行验收------我是怎么让 AI 按规格生成代码,并一行行验证的》

内容包括:

  • 怎么把接口规格发给 AI,一次只发一个任务

  • 怎么验收 AI 生成的代码(不是看,是跑)

  • 怎么用 Makefile 把验收自动化

  • 遇到 AI 输出不对时,怎么让它在原文件上修正

如果你也在用 AI 写代码,欢迎关注。这条路,我们一起趟。

附:接口规格文档的完整示例(边缘端)

我把边缘端 E7 的接口规格完整版贴在下面,供参考:

markdown

复制代码
### E7: ADBController 实现

**文件**: `internal/adb/adb.go`

**结构体定义**:
```go
type ADBController struct {
    mu      sync.Mutex
    device  gadb.Device
    tvIP    string
    tvPort  int
    connected bool
}
E7.1: 连接管理

函数签名:

go

复制代码
func (c *ADBController) ensureConnected(ctx context.Context) error
  • 指数退避重试(1s,2s,4s),最多 3 次

  • 使用 gadb.Connect 建立连接

  • 连接失败返回 E1001 错误

E7.2: open_app 命令

函数签名:

go

复制代码
func (c *ADBController) OpenApp(ctx context.Context, packageName string) error
  • 先调用 ensureConnected

  • 执行 adb shell am start -n {packageName}

  • 成功返回 nil,失败返回 E1002

E7.3: tap 命令

函数签名:

go

复制代码
func (c *ADBController) Tap(ctx context.Context, x, y int) error
  • 执行 adb shell input tap x y

...(其余命令类似)

text

复制代码
**有了这份规格,AI 写的代码和我想的完全一样。**
相关推荐
我叫张小白。2 小时前
Dify系列(三):提示词工程的Prompt 结构优化与变量设计
ai·大模型·prompt·dify·智能体
Bear Coding2 小时前
使用qoder从零搭建项目
ai·qoder
Thomas.Sir3 小时前
第八章:Python3 之 异常与文件处理【从基础入门到底层原理+项目实战】
python·ai·文件处理·异常
张较瘦_3 小时前
[论文阅读] AI + 软件工程 | 从1对1到规模化,Lacy用AI+专家代码漫游重构软件入职指导
人工智能·重构·软件工程
启航挨踢12 小时前
2026年教师资格证面试资料攻略
软件工程
路小雨~12 小时前
Transformer架构学习笔记:从数学推导到工程实现与主流变体
笔记·ai·transformer
大势智慧12 小时前
使用低空大师进行无人机巡查一段时间后,如何确定重点整治区域?
人工智能·ai·无人机·实景三维·低空经济·事件预警
唐骁虎14 小时前
Claude Code 全景架构指南——三大核心支柱及四大关键扩展组件
ai·架构·ai编程·claude code
VIP_CQCRE14 小时前
Claude Code + Seedance MCP:命令行 AI 舞蹈视频生成
ai