Symphony 與自管 Agent 的技術拆解

Symphony 與自管 Agent 的技術拆解

📌 本文重點

  • 讓 Agent 主動拉工單並自排程,減少工程師 babysit
  • 採用混合 Multi-Agent 模式與明確權限邊界
  • 透過 Task Queue + Worker + 審批閘門串起從工單到 PR 的全流程

人類注意力已經成為工程團隊採用 AI 助手的主要瓶頸:Agent 能寫 code,但你要一直盯著它。Symphony 類的自管 Agent 系統,直接改變的是這件事:

從「工程師 babysit 多個 Agent」→「Agent 自己從 Linear / Jira 拉工單、自排程、跑完整個 CI/CD pipeline,只在關鍵節點請你按一次 Approve」。

下面從實作角度拆解:如何設計任務拉取、Multi-Agent workflow、與 CI/CD 權限邊界;最後給一個「自動修 bug → 開 PR → 回寫 Linear 狀態」樣板。


重點說明

1. 工單拉取與任務自排程

核心是讓 Agent 變成一個長壽命 worker,定期從任務池拉工單,而不是被動等待 API 呼叫。

工單來源

  • Linear: /issues, /webhooks, /comments
  • Jira: /rest/api/3/search, /rest/api/3/webhook

Polling vs Webhook

  • Polling(簡單好 Debug)
  • 優點:
    • 實作簡單,只要定時 cron + API token
    • 不怕 webhook misconfig / 防火牆問題
  • 缺點:

    • 有延遲(30s–5min)
    • 需要自己做去重 / 任務狀態同步
  • Webhook(推薦長期方案)

  • 優點:
    • 事件即時觸發,適合高優先 bug / incident
    • 可根據事件類型直接分路由(bug vs feature)
  • 缺點:
    • 需要公開 endpoint + 驗簽
    • 部署與權限設定更複雜

實務上常用 混合策略

  • Webhook 處理新建 / 更新事件
  • Polling 每隔 5–10 分鐘做 reconcile,修正漏觸發 / 失敗同步

💡 關鍵: 用「Webhook 即時 + 每 5–10 分鐘 Polling 校正」的混合策略,可以在保持即時性的同時降低漏事件風險。

任務分派與併發控制

  • 任務表核心欄位建議:
  • id, source(issue_id), priority, status(queued/running/failed/done), agent_type, lock_owner, lock_expires_at
  • 分派策略可以簡化成:
  • 優先級隊列:依 Linear priority / label 映射成數值
  • 技能匹配:根據 label → agent_type(例如 frontend, backend, infra

併發與重試控制的關鍵:樂觀鎖 + visibility timeout

-- 簡化的任務鎖定 SQL
UPDATE tasks
SET status = 'running', lock_owner = :agent_id, lock_expires_at = NOW() + interval '15 minutes'
WHERE id = (
  SELECT id FROM tasks
  WHERE status IN ('queued', 'failed')
    AND (lock_expires_at IS NULL OR lock_expires_at < NOW())
  ORDER BY priority DESC, created_at ASC
  LIMIT 1
  FOR UPDATE SKIP LOCKED
)
RETURNING *;

好處

  • 避免多個 Agent 搶同一張工單
  • Agent 崩潰 / timeout 時,lock 過期後可被其他 Agent 接手(類似 SQS visibility timeout)

2. Multi-Agent:中心協調 vs 任務接力

現代多 Agent 系統基本都落在兩種模式上(參考 Agents as Tools vs Handoffs)。

模式 A:中心協調(Agents as Tools)

  • 一個「指揮官」Agent + 多個「工具」Agent
  • 主 Agent 保留全局 context 與決策權,子 Agent 像 function call

  • 示意(虛擬 code):

const orchestrator = new OrchestratorAgent({
  tools: {
    codeAgent: callCodeAgent,
    testAgent: callTestAgent,
    infraAgent: callInfraAgent,
  }
});

await orchestrator.run({
  goal: "Fix bug #123 in service A and deploy to staging",
  constraints: { require_approval_for_deploy: true }
});

適合

  • 需求不明確,需要動態拆解子任務
  • 需要統一治理(quota、安全策略、審計)

模式 B:任務接力(Handoffs)

  • 任務隨流程在 Agent 之間流動
  • 每個 Agent 處理完就寫結果 + 下一步指派
// task.payload 示例(存在 DB / Task Queue)
{
  "status": "code_fixed",
  "next_agent": "test_agent",
  "artifacts": {
    "branch": "fix/BUG-123-null-pointer",
    "diff_summary": "..."
  }
}

適合

  • Pipeline 已穩定(bugfix → test → PR → notify)
  • 易於水平擴展,每個 Agent 是一組 worker

實務建議:多數專案採用 混合

  • 一個 中心協調 Agent,但遇到標準化步驟(跑測試、開 PR、通知 Slack)時,交給 固定 handoff stage 的 worker;類似「主流程由 LLM 控制,heavy lifting 由 deterministic step 執行」。

💡 關鍵: 把「決策」交給中心協調 Agent,把「重複且標準化的步驟」交給固定 worker,可以在保持靈活度的同時確保穩定性與成本可控。


3. 與 CI/CD、code review、事故流程整合

自管 Agent 的威力,取決於你如何設計 權限邊界 + 審批閘門

權限邊界設計

  • Repo 層級:
  • 建立專用 GitHub App / GitLab Token,只開放:
    • repo:contents:write(但限制特定 org / repo)
    • pull_request:write
  • 禁止直接 push main / production branch
  • 環境層級:
  • Agent 只允許:
    • Deploy 到 staging / preview env
    • 觸發 read-only incident tooling(查 log、查 metrics),不要一開始就給 rollback / scale 權限

審批閘門(approval gate)

  • 在 CI pipeline 加一個手動 stage,例如 GitHub Actions:
jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

  deploy_staging:
    needs: tests
    if: github.actor == 'agent-bot'
    environment:
      name: staging
      # GitHub Environments 的 Reviewers 即是 approval gate
    steps:
      - run: ./deploy-staging.sh

審計 log

  • 每個關鍵行為都應落地:
  • 取得工單(issue_id, agent_id, reason, time
  • 對 repo 的修改(branch, commit_sha, diff_summary, tests_run
  • 任何 CI/CD trigger(workflow_id, inputs, result

  • 建議統一經過一個 AuditService.log(event)

await AuditService.log({
  actor: "agent-bot",
  action: "CREATE_PR",
  metadata: {
    issue_id: "ENG-1234",
    repo: "org/service-a",
    branch: "fix/ENG-1234-null-pointer",
    pr_url: "https://github.com/..."
  }
});

💡 關鍵: 把權限鎖在「staging + PR 層級」並配合審批閘門與審計 log,可以在不影響生產安全的前提下,讓 Agent 最大化自動化範圍。


實作範例:從 Linear 抓 bug → 開分支 → 修 code → 開 PR → 回寫狀態

以下是一個縮小版 blueprint,你可以直接改成自家 stack。

1. 任務入口:Linear Webhook + 任務表

Linear Webhook 指向你的 /linear/webhook

// Express 風格
app.post('/linear/webhook', async (req, res) => {
  const event = req.body;

  if (event.type === 'IssueCreated' || event.type === 'IssueUpdated') {
    const issue = event.data;

    // 僅處理 bug + 特定 team
    if (issue.team.key === 'ENG' && issue.labelNames.includes('bug')) {
      await TaskRepo.enqueue({
        source: 'linear',
        source_issue_id: issue.id,
        priority: mapLinearPriority(issue.priority),
        agent_type: 'bugfix',
        status: 'queued'
      });
    }
  }

  res.sendStatus(200);
});

2. Bugfix Agent Worker(核心 loop)

async function bugfixWorkerLoop() {
  while (true) {
    const task = await TaskRepo.acquireNext('bugfix', process.env.AGENT_ID);
    if (!task) {
      await sleep(5000);
      continue;
    }

    try {
      const issue = await LinearApi.getIssue(task.source_issue_id);
      const repo = mapIssueToRepo(issue);
      const branch = `fix/${issue.identifier}-${slug(issue.title)}`;

      await GitService.createBranch({ repo, from: 'main', branch });

      const diff = await CodeAgent.fixBug({
        repo,
        branch,
        issue_description: issue.title + '\n\n' + issue.description,
        files_hint: inferRelatedFiles(issue)
      });

      await GitService.commitAndPush({ repo, branch, message: `fix: ${issue.identifier}` });

      const pr = await GitService.createPR({
        repo,
        branch,
        base: 'main',
        title: `[Agent] Fix ${issue.identifier}: ${issue.title}`,
        body: renderPRBody(issue, diff)
      });

      await LinearApi.updateIssue(task.source_issue_id, {
        state: 'In Review',
        descriptionAppend: `\n\nLinked PR: ${pr.url}`
      });

      await TaskRepo.markDone(task.id);
    } catch (err) {
      await TaskRepo.markFailed(task.id, { error: String(err) });
    }
  }
}

關鍵點

  • CodeAgent.fixBug 本身可以是一個 Symphony / Claude Managed Agent:
  • 有自己的工具:get_file, apply_diff, run_tests
  • 有自己的「Outcomes」條件(例如:測試必須綠燈、diff 不能超過 500 行)
  • Worker loop 要能容錯:task failure 不要直接 crash process

3. 錯誤恢復與常見坑

(1) stale context / 版本衝突

  • 現象:Agent 基於舊 commit 生成 patch,push 時發現 remote 已有新 commit
  • 對策:
  • createBranch 前先 git fetch + 檢查 main 是否有新 commit
  • 若有衝突,改用 rebase + 再跑一次 CodeAgent,或直接加標籤請人工處理

(2) 任務飢餓(某些工單一直排不到)

  • 常見原因:
  • 單純用 FIFO,長工時任務卡住隊列
  • 高 priority 任務一直插隊
  • 對策:
  • 採用 優先級 + aging:等待時間越久,自動提高 effective priority
  • 給長任務單獨的 queue 或 agent_type

(3) 被動等待人工決策,Agent 資源被佔住

  • 例如:Agent 開 PR 後要等 Reviewer,期間 worker 就 idle with lock
  • 對策:
  • 把「等待人工」拆出成另一個狀態:
    • 任務設為 status = waiting_human
    • PR merge / Linear 狀態變更時再由 webhook 建下一個 task(例如 deploy)

(4) AI 決策不穩(修了錯問題)

  • 這是現在 Agent 最大痛點之一(參考「AI is getting better at doing things, but still bad at deciding what to do」)。
  • 對策:
  • CodeAgent 設定明確 Outcome 定義
    • 測試要準備好一組 reproduction test
    • 用獨立 Evaluator Agent 根據 log / diff 給出 pass / needs-clarification
  • 讓 Agent 更常問問題:若重現步驟不完整,直接在 Linear 開 comment 要求補充,而不是盲修。

建議與注意事項

  1. 從「觀察型 Agent」開始,不要一開始就給寫入權限

  2. 先只允許:讀工單 → 產生修復方案 / diff 草稿 → 貼回 Linear。穩定後再打開 PR 寫入、最後才接 CI/CD。

  3. 集中化審計與開關

  4. 所有 Agent 行為走一個 Agent Gateway / Orchestrator,集中:

    • 配額控制
    • 風險開關(feature flag 一鍵關掉所有 auto-deploy)
    • log / metrics / alert
  5. 明確定義「哪一段流程可以 0 人工」

  6. 常見安全配置:

    • bugfix PR 可以由 Agent 全自動產生,但 merge 必須人工
    • staging deploy 可自動,production deploy 必須經 Slack / PagerDuty approve
  7. 將 Agent 視為「非穩定服務」而非傳統微服務

  8. 接受它偶爾會做奇怪決策,因此整個系統必須:

    • 有清楚的 rollback 路徑
    • 任務永遠由 queue 控制,不綁死在單一 process
    • 重要資源(code、infra)永遠有 versioning + 審批

如果你已經有 Linear / Jira + GitHub + CI/CD 的基本骨架,其實不用重建世界:

只要加上一個 Task Queue + Agent Worker + 守門的 Orchestrator/Approval Gate,你就能讓 Symphony 類的自管 Agent 為團隊接手一條完整的「從工單到 PR」流水線,真正從盯著 Agent 寫 code,變成只盯少數關鍵決策點。

🚀 你現在可以做的事

  • 在現有 Linear / Jira 加一個 Webhook,寫入自建的 tasks 資料表作為任務池
  • 實作一個最小版 bugfixWorkerLoop,先只產生修復方案與 diff 草稿貼回工單
  • 在 CI/CD 中加入只對 agent-bot 生效的 staging deploy job,並配置 GitHub Environments 審批閘門

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *