用 Issue Tracker 当控制面:OpenAI 开源 Symphony,让多编码 Agent 自主协作

2026-05-18 38 预计阅读时间:1 分钟
来源:infoq.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:12 分钟

OpenAI 刚开源了 Symphony——不是又一个模型,不是又一个框架,而是一份 SPEC.md。它定义了一套用项目管理工具(Issue Tracker)做控制面的多 Agent 编排方案:开发者不再逐个盯交互式编码会话,而是把每个 Issue 交给一个专属 Agent,Agent 自主跑完任务,人只做最终 Review。思路很朴素,但把"谁来管 Agent"这个问题从人推给了流程。

从"人盯 Agent"到"流程管 Agent"

当前主流的编码 Agent 工作方式是交互式:你在终端或 IDE 里开一个会话,Agent 写代码,你审查、追问、修正,循环往复。一个人能盯的会话数量有限,Agent 的并行能力被卡死在人的注意力带宽上。

Symphony 的核心翻转是——把 Issue Tracker 当成控制面。Issue 本身就是任务的原子单位:有描述、有状态、有依赖关系。Symphony 读 Issue 列表,为每个 Open Issue 派一个 Agent,Agent 拿到 Issue 描述后自主编码、跑测试、提交 PR,最后把 Issue 标记为 Done。人只在 PR Review 阶段介入。

这意味着: - Agent 的并行度不再受人的注意力限制,受的是 Issue 数量和 CI 资源。 - 人的角色从"实时驾驶员"变成"批量质检员"。 - 项目管理工具天然就是状态机,不需要额外造一套编排 DSL。

SPEC.md 里定义了什么

Symphony 目前开源的是 SPEC.md,不是可运行的代码。这份规格文档描述了编排的约定和流程,核心要素包括:

要素 作用
Issue 作为 Task 单元 每个 Issue 对应一个 Agent 的完整任务生命周期
Agent 的自主边界 Agent 在 Issue 描述范围内自主决策,不回问人
状态流转 Open → In Progress → Done,由 Agent 和 Tracker 共同驱动
Human Review Gate Agent 完成后产出 PR,人做 Review,不通过则打回重开 Issue

关键设计决策:Agent 不做交互式确认。它拿到 Issue 描述后一路跑到终点,中间不暂停、不请求许可。这和 Codex / Copilot 的"对话式"模式截然不同。代价是 Issue 描述必须足够精确——模糊的 Issue 会产出模糊的 PR。

实际跑起来:一个最小编排原型

Symphony 的 SPEC 是规范,不是实现。但我们可以用它的思路搭一个最小原型——用 GitHub Issue 做 Tracker,用 Python 脚本做 Orchestrator,用 OpenAI API 做 Agent 核心。下面是一个可以直接改造运行的示例。

1. 定义 Issue 模板

在你的 repo 里创建 .github/ISSUE_TEMPLATE/agent_task.yml

name: Agent Task
description: 一个可由编码 Agent 自主完成的任务
labels: ["agent-task"]
body:
  - type: textarea
    id: description
    attributes:
      label: 任务描述
      description: 请写清楚要做什么、改哪个文件、期望的行为是什么。Agent 不会回问。
      placeholder: "例如:在 src/api/users.py 中添加 DELETE /users/{id} 端点,返回 204,需覆盖测试"
    validations:
      required: true
  - type: textarea
    id: constraints
    attributes:
      label: 约束与边界
      description: Agent 必须遵守的规则
      placeholder: "例如:不要改动数据库 schema;只修改 users.py  tests/test_users.py"

2. Orchestrator 脚本

这个脚本轮询带 agent-task 标签的 Open Issue,为每个 Issue 启动一个 Agent 运行:

"""
symphony_minimal.py — 最小 Symphony 编排原型
依赖: pip install openai PyGithub
环境变量: GITHUB_TOKEN, OPENAI_API_KEY, REPO_NAME (格式: owner/repo)
"""

import os
import json
import subprocess
from github import Github
from openai import OpenAI

REPO_NAME = os.environ["REPO_NAME"]          # e.g. "myorg/myrepo"
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]

gh = Github(GITHUB_TOKEN)
repo = gh.get_repo(REPO_NAME)
client = OpenAI(api_key=OPENAI_API_KEY)

AGENT_LABEL = "agent-task"
STATUS_LABELS = {"in-progress": "agent-in-progress", "done": "agent-done"}

def fetch_open_tasks():
    """获取所有标记为 agent-task 且未关闭的 Issue"""
    issues = repo.get_issues(state="open", labels=[AGENT_LABEL])
    return [i for i in issues if not any(
        l.name == STATUS_LABELS["in-progress"] or l.name == STATUS_LABELS["done"]
        for l in i.labels
    )]

def mark_status(issue, status):
    """给 Issue 打上状态标签"""
    label = repo.get_label(STATUS_LABELS[status])
    issue.add_to_labels(label)

def agent_run(issue):
    """让 Agent 自主完成一个 Issue"""
    prompt = f"""你是一个自主编码 Agent。以下是你的任务 Issue:

标题: {issue.title}
描述: {issue.body}

规则:
1. 只修改与任务直接相关的文件。
2. 写完代码后生成对应的测试。
3. 输出格式为 JSON,包含两个字段:
   - "files": 一个 dict,key 是文件路径,value 是文件完整内容
   - "summary": 一段简短的变更说明

请直接输出 JSON,不要解释过程。"""

    response = client.chat.completions.create(
        model="gpt-4.1",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )

    result = json.loads(response.choices[0].message.content)
    return result

def apply_changes(result, issue):
    """把 Agent 产出的文件写入本地,提交并推 PR"""
    branch_name = f"agent-task-{issue.number}"
    base_branch = repo.default_branch

    # 从 base branch 创建新分支
    ref = repo.get_git_ref(f"heads/{base_branch}")
    repo.create_git_ref(f"refs/heads/{branch_name}", ref.object.sha)

    for path, content in result["files"].items():
        # 尝试获取已有文件(如果存在则更新,否则创建)
        try:
            existing = repo.get_contents(path, ref=branch_name)
            repo.update_file(
                path=path,
                message=f"agent update: {path}",
                content=content,
                sha=existing.sha,
                branch=branch_name,
            )
        except Exception:
            repo.create_file(
                path=path,
                message=f"agent create: {path}",
                content=content,
                branch=branch_name,
            )

    # 创建 Pull Request
    pr = repo.create_pull(
        title=f"[Agent] {issue.title}",
        body=result["summary"] + f"\n\nCloses #{issue.number}",
        head=branch_name,
        base=base_branch,
    )

    # 在 Issue 上评论 PR 链接
    issue.create_comment(f"Agent 已完成,PR: #{pr.number}")
    return pr

def main():
    tasks = fetch_open_tasks()
    print(f"发现 {len(tasks)} 个待处理 Agent Task")

    for issue in tasks:
        print(f"→ 处理 Issue #{issue.number}: {issue.title}")
        mark_status(issue, "in-progress")

        result = agent_run(issue)
        pr = apply_changes(result, issue)

        mark_status(issue, "done")
        print(f"  ✓ PR #{pr.number} 已创建,等待人类 Review")

if __name__ == "__main__":
    main()

3. 运行方式

# 安装依赖
pip install openai PyGithub

# 设置环境变量
export GITHUB_TOKEN="ghp_xxxx"
export OPENAI_API_KEY="sk-xxxx"
export REPO_NAME="myorg/myrepo"

# 执行编排
python symphony_minimal.py

这个原型做了 Symphony SPEC 里描述的核心流程:读 Issue → 派 Agent → Agent 自主产出 → 提 PR → 等 Review。你可以在此基础上加 CI 检查、依赖排序、失败重试。

人的角色变了,没消失

Symphony 把人从"实时驾驶员"挪到了"批量质检员",但这不意味着人可以撒手不管。几个实际要注意的点:

Issue 质量就是 Agent 质量。 Agent 不回问,所以 Issue 描述必须精确到文件路径、函数名、期望行为。模糊的 Issue 会产出无用的 PR,Review 成本反而更高。建议团队先花时间打磨 Issue 模板和写作规范,再放开 Agent 自动跑。

Review 是最后的闸门,不是可选步骤。 Agent 产出的 PR 必须经过完整 Review——代码逻辑、测试覆盖、边界情况。如果团队习惯快速 merge 小 PR,Agent 产出的 PR 量可能压垮现有 Review 流程。需要考虑自动化 lint + CI gate 作为前置过滤,人只 Review 通过 CI 的 PR。

依赖顺序不能忽略。 如果 Issue A 依赖 Issue B 的产出,并行派 Agent 会冲突。SPEC.md 里暗示用 Issue 的依赖关系做排序,但 GitHub Issue 本身没有原生依赖字段。你可以用标签(如 depends-on:#123)或项目管理工具(如 Linear、Jira)来表达。

失败处理要提前设计。 Agent 跑到一半 CI 爆了、文件冲突了、API 超时了——这些都要有明确的状态回退路径。最简单的做法:失败时把 Issue 标回 Open,评论里附上错误日志,让人决定是重派 Agent 还是手动修。

什么时候值得用

Symphony 的模式适合以下场景:

  • Issue 量大、单个复杂度低——比如批量修 lint warning、批量加类型标注、批量补测试。这些任务描述容易标准化,Agent 成功率高。
  • 团队已经有成熟的 Issue + PR 流程——Symphony 是在现有流程上加自动化层,不是替代流程。如果团队连 Issue 都不写,先解决流程问题。
  • CI 基础设施可靠——Agent 产出的代码必须过 CI 才值得人 Review。CI 不稳,Agent 的产出就是噪音。

不适合的场景:

  • 探索性任务——"调研一下这个新框架"这类 Issue,Agent 没有明确的完成标准。
  • 跨多模块的大重构——依赖关系复杂,并行 Agent 容易互相踩脚。
  • 对正确性要求极高的变更——比如支付逻辑、安全补丁,Agent 自主跑完再 Review 的风险太高。

Symphony 的 SPEC.md 是一份设计文档,不是产品。它的价值在于给出了一个清晰的编排约定:用 Issue Tracker 做状态机,用 Agent 做执行器,用 PR Review 做安全网。你可以用任何语言、任何模型、任何 Tracker 来实现这个约定——上面那个 Python 原型就是起点。


相关推荐