用 Paperclip 搭了一个 3 人 AI 团队,自动写代码 + 审查 + 部署(踩了 4 个坑)

56 阅读7分钟

用 Paperclip 搭了一个 3 人 AI 团队,自动写代码 + 审查 + 部署(踩了 4 个坑)

如果你在用 Claude Code 或者 Cursor 写代码,大概率遇到过这个问题:一个 Agent 干所有事,写完代码自己审查自己,质量全靠运气。就像让一个人既当运动员又当裁判,能公正才怪。

Paperclip 的思路不一样——它让你把多个 AI Agent 组织成一个"公司",每个 Agent 有自己的角色和职责,互相协作、互相审查。3 周前开源,GitHub 33K+ stars,MIT 协议。核心卖点:你的 AI Agent 不需要更好的 prompt,它们需要一个组织架构

这篇文章我用 Paperclip 搭了一个 3 人 AI 团队:一个写代码、一个做 Code Review、一个跑测试。从安装到跑通,踩的坑都记下来了。

先装起来

前置条件:Node.js 18+、pnpm 9.15+。

# 安装 pnpm(如果没有)
npm install -g pnpm

# 一键初始化(自带内嵌 PostgreSQL,不用额外装数据库)
npx paperclipai onboard --yes

这条命令会做三件事:拉代码、启动内嵌 PostgreSQL、创建你的第一个"公司"。跑完后打开浏览器访问 http://localhost:3000,能看到 Dashboard。

也可以纯 CLI 操作,不开浏览器:

# 创建公司
paperclip company create --name "CodeTeam" --mission "Build and ship quality code"

# 查看公司状态
paperclip company status

到这一步大概 3 分钟。

核心概念:Agent = 员工

Paperclip 把 AI Agent 的管理类比成公司管理。几个核心概念:

  • Company:一个项目或团队,包含多个 Agent
  • Agent:一个有角色、职责、权限的 AI 实例
  • Task:分配给 Agent 的具体工作
  • Workflow:多个 Agent 协作的流程定义

这不是花哨的比喻,它真的按公司架构来设计——Agent 之间有汇报关系,有权限隔离,有审批流程。

定义 3 个 Agent 角色

我定义了 3 个角色,模拟一个最小的开发团队:

# ~/.paperclip/companies/codeteam/agents.yaml
agents:
  - name: Coder
    role: "Senior Developer"
    provider: anthropic
    model: claude-sonnet-4-20250514
    tools:
      - file_read
      - file_write
      - shell_exec
    instructions: |
      You write clean, production-ready code.
      Always include error handling and input validation.
      Never write code longer than 200 lines per file.
      Add docstrings to all public functions.

  - name: Reviewer
    role: "Code Reviewer"
    provider: anthropic
    model: claude-sonnet-4-20250514
    tools:
      - file_read
    instructions: |
      Review code for bugs, security issues, and style.
      Output a structured review with severity levels: critical/warning/info.
      If any critical issue found, reject with clear reasons.
      Be strict but fair.

  - name: Tester
    role: "QA Engineer"
    provider: anthropic
    model: claude-sonnet-4-20250514
    tools:
      - file_read
      - shell_exec
    instructions: |
      Write and run tests for the given code.
      Cover: happy path, edge cases, error handling, type validation.
      Use pytest. Report pass/fail with details.

注意一个关键设计:Reviewer 只有 file_read 权限,不能改代码。这是故意的——审查者不应该有修改权限,只能提意见,让 Coder 自己改。这和真实团队的 Code Review 流程一样。

定义工作流:写 → 审 → 测

Agent 定义好了,还需要一个工作流把它们串起来。Paperclip 用 YAML 定义工作流,支持条件分支和循环:

# ~/.paperclip/companies/codeteam/workflows/code-pipeline.yaml
name: code-pipeline
description: "Write code, review it, test it"

steps:
  - agent: Coder
    task: "Write the code based on the requirement"
    output: code_files

  - agent: Reviewer
    task: "Review the code written by Coder. Check for bugs, security issues, and code quality."
    input: code_files
    output: review_result
    on_reject: goto step 1  # 被打回就让 Coder 重写

  - agent: Tester
    task: "Write pytest tests and run them. Cover happy path, edge cases, and error handling."
    input: code_files
    output: test_result
    on_fail: goto step 1    # 测试不过也让 Coder 重写

max_iterations: 3  # 最多循环 3 次,防止死循环

on_rejecton_fail 是 Paperclip 的亮点——Agent 之间有反馈回路,不是线性执行完就结束。Reviewer 打回的时候会附上具体原因,Coder 拿到原因后针对性修改,而不是从头重写。

跑一个真实任务

启动工作流,给它一个有点难度的需求:

paperclip task create \
  --company CodeTeam \
  --workflow code-pipeline \
  --description "Write a Python function that validates email addresses. Requirements: 1) Handle empty/null input 2) Check @ symbol 3) Validate domain format 4) Support IP address domains like user@[192.168.1.1] 5) Reject multiple @ symbols 6) Return structured result with valid/invalid and reason"

在 Dashboard 上能看到实时进度,也可以用 CLI 查看日志:

paperclip task logs --id task_001

输出大概长这样:

[14:01:02] [Coder]    Writing email validator...
[14:01:08] [Coder]    Created: src/email_validator.py (45 lines)
[14:01:10] [Reviewer] Reviewing src/email_validator.py...
[14:01:15] [Reviewer] REJECTED: [Critical] Missing handling for IP address domains
[14:01:15] [Reviewer] REJECTED: [Warning] No input type checking for non-string input
[14:01:17] [Coder]    Rewriting based on review feedback...
[14:01:23] [Coder]    Updated: src/email_validator.py (68 lines)
[14:01:25] [Reviewer] APPROVED: All critical issues addressed
[14:01:27] [Tester]   Writing tests...
[14:01:33] [Tester]   Created: tests/test_email_validator.py (89 lines)
[14:01:35] [Tester]   Running: pytest tests/test_email_validator.py -v
[14:01:37] [Tester]   PASSED: 14/14 tests passed
[14:01:37] Task completed successfully in 2 iterations

第一轮 Reviewer 发现了两个问题:一个 Critical(没处理 IP 地址域名),一个 Warning(没做类型检查)。Coder 根据反馈重写,第二轮通过。然后 Tester 写了 14 个测试用例,全部通过。

这就是多 Agent 协作的价值——Coder 第一版漏掉了 IP 地址域名的处理,如果是单 Agent 模式,这个 bug 就直接上线了

看看最终生成的代码

Coder 经过 Reviewer 反馈后生成的最终版本:

# src/email_validator.py
import re
from typing import Union

def validate_email(email: Union[str, None]) -> dict:
    """Validate email address and return structured result.

    Args:
        email: Email address string to validate

    Returns:
        dict with 'valid' (bool) and 'reason' (str)
    """
    if email is None or not isinstance(email, str):
        return {"valid": False, "reason": "Input must be a non-null string"}

    email = email.strip()
    if not email:
        return {"valid": False, "reason": "Empty string after trimming"}

    if '@' not in email:
        return {"valid": False, "reason": "Missing @ symbol"}
    if email.count('@') > 1:
        return {"valid": False, "reason": "Multiple @ symbols found"}

    # Standard email pattern
    standard = re.compile(
        r'^[a-zA-Z0-9.!#$%&\'*+/=?^_`{|}~-]+'
        r'@'
        r'(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*'
        r'[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$'
    )
    # IP address domain pattern
    ip_domain = re.compile(
        r'^[a-zA-Z0-9.!#$%&\'*+/=?^_`{|}~-]+'
        r'@\[(\d{1,3}\.){3}\d{1,3}\]$'
    )

    if standard.match(email) or ip_domain.match(email):
        return {"valid": True, "reason": "Valid email address"}

    return {"valid": False, "reason": "Invalid email format"}

注意 IP 地址域名的 ip_domain 正则——这是 Reviewer 第一轮打回后加上的。类型检查 Union[str, None] 也是。

踩坑记录

坑 1:内嵌 PostgreSQL 端口冲突

如果你本机已经跑了 PostgreSQL,onboard 会报端口 5432 被占用。两个解决方案:

# 方案 1:指定其他端口
npx paperclipai onboard --yes --db-port 5433

# 方案 2:用你自己的 PostgreSQL
npx paperclipai onboard --yes --db-url postgresql://user:pass@localhost:5432/paperclip

坑 2:Agent 之间的上下文传递丢失

第一次跑的时候,Reviewer 说"I don't see any code files"。排查了半天,原因是 Coder 把文件写到了 /tmp/ 下面,而 Reviewer 只能读 workspace 目录。

在 Coder 的 instructions 里加一句就好了:

instructions: |
  ...
  Always write output files to the current workspace directory.
  Do not use /tmp or absolute paths.

坑 3:max_iterations 设太大烧 token

我一开始设了 max_iterations: 10,结果 Reviewer 和 Coder 来回拉扯了 7 轮,烧了 $2 的 API 费用。一个邮箱验证函数不值得。设成 3 就够了——3 轮还过不了,说明需求描述有问题,应该改需求而不是让 Agent 继续猜。

坑 4:免费模型不支持 tool_use

想省钱换了 OpenRouter 的免费模型,结果 Agent 不会调用 file_write 工具,只会在对话里输出代码。Paperclip 依赖模型的 function calling / tool_use 能力,免费模型大多不支持。至少得用 Claude Sonnet 或 GPT-4o-mini 这个级别的。

成本和效果数据

跑了 10 个类似复杂度的任务(单函数级别),统计了一下:

指标数值
平均每个任务 token 消耗~15K input + ~5K output
平均每个任务成本(Claude Sonnet)~$0.12
平均迭代轮数1.8 轮
代码一次通过率(Reviewer 没打回)40%
测试一次通过率(Tester 没报错)70%

一次通过率只有 40%,说明 Reviewer 确实在干活,不是摆设。60% 的情况下它都能挑出 Coder 遗漏的问题。

成本方面,单 Agent 写同样的代码大概 0.05,三Agent0.05,三 Agent 是 0.12,贵了一倍多。但考虑到代码质量的提升,对于生产代码来说这个成本完全可以接受。

什么时候该用 / 不该用

适合的场景

  • 有明确分工的重复性任务(写代码 → 审查 → 测试)
  • 需要多角度检查的场景(安全审计、文档校对)
  • 想降低单 Agent 幻觉风险的场景
  • 团队想建立 AI 辅助的标准化流程

不适合的场景

  • 简单的一次性任务(杀鸡用牛刀)
  • 需要实时交互的场景(Paperclip 是异步批处理的)
  • 预算紧张的个人项目(多 Agent = 多倍 token)
  • 探索性的创意工作(Agent 之间的审查会限制创造力)

总结

Paperclip 干的事说白了就是:让 AI Agent 互相监督,别自己审查自己

搭一个 3 人团队只需要两个 YAML 文件,10 分钟搞定。实测下来代码质量确实比单 Agent 好,尤其是边界条件和安全检查——Reviewer 总能挑出 Coder 漏掉的 case。

代价是 token 消耗翻倍,但对于重要的生产代码来说,花 $0.12 让三个 AI 互相 review 一下,比上线后出 bug 便宜多了。

你有没有试过让多个 AI Agent 协作的方案?效果怎么样?评论区聊聊。