openai-symphony-autonomous-agents by aradotso/trending-skills
npx skills add https://github.com/aradotso/trending-skills --skill openai-symphony-autonomous-agentsSkill by ara.so — Daily 2026 Skills 合集。
Symphony 将项目工作转化为独立的、自动化的实施运行,使团队能够管理工作,而不是监督编码代理。你无需观看代理编码,只需定义任务(例如在 Linear 中),Symphony 便会生成代理来完成这些任务,提供工作证明(CI 状态、PR 审查、演示视频),并自动合并 PR。
将此提示粘贴到 Claude Code、Cursor 或 Codex 中:
Implement Symphony according to the following spec:
https://github.com/openai/symphony/blob/main/SPEC.md
git clone https://github.com/openai/symphony.git
cd symphony/elixir
按照 elixir/README.md 操作,或询问代理:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
Set up Symphony for my repository based on
https://github.com/openai/symphony/blob/main/elixir/README.md
export OPENAI_API_KEY="sk-..." # OpenAI API key for Codex
export LINEAR_API_KEY="lin_api_..." # Linear integration
export GITHUB_TOKEN="ghp_..." # GitHub PR operations
export SYMPHONY_REPO_PATH="/path/to/repo" # Target repository
cd elixir
mix deps.get
elixir/config/config.exs)import Config
config :symphony,
openai_api_key: System.get_env("OPENAI_API_KEY"),
linear_api_key: System.get_env("LINEAR_API_KEY"),
github_token: System.get_env("GITHUB_TOKEN"),
repo_path: System.get_env("SYMPHONY_REPO_PATH", "./"),
poll_interval_ms: 30_000,
max_concurrent_agents: 3
mix symphony.start
# or in IEx for development
iex -S mix
每个任务都有其独立的运行:
在 PR 被接受之前,Symphony 会收集:
# In your application.ex or directly
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
Symphony.Supervisor
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
defmodule Symphony.Task do
@type t :: %__MODULE__{
id: String.t(),
title: String.t(),
description: String.t(),
source: :linear | :manual,
status: :pending | :running | :completed | :failed,
branch: String.t() | nil,
pr_url: String.t() | nil,
proof_of_work: map() | nil
}
defstruct [:id, :title, :description, :source,
status: :pending, branch: nil,
pr_url: nil, proof_of_work: nil]
end
defmodule Symphony.AgentRunner do
@doc """
Spawns an isolated agent run for a given task.
Each run gets its own branch and Codex session.
"""
def run(task) do
branch = "symphony/#{task.id}-#{slugify(task.title)}"
with :ok <- Git.create_branch(branch),
{:ok, result} <- Codex.implement(task, branch),
{:ok, pr_url} <- GitHub.open_pr(branch, task),
{:ok, proof} <- ProofOfWork.collect(pr_url) do
{:ok, %{task | status: :completed, pr_url: pr_url, proof_of_work: proof}}
else
{:error, reason} -> {:error, reason}
end
end
defp slugify(title) do
title
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/, "-")
|> String.trim("-")
end
end
defmodule Symphony.Linear.Poller do
use GenServer
@poll_interval Application.compile_env(:symphony, :poll_interval_ms, 30_000)
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
schedule_poll()
{:ok, %{processed_ids: MapSet.new()}}
end
def handle_info(:poll, state) do
case Symphony.Linear.Client.fetch_todo_tasks() do
{:ok, tasks} ->
new_tasks = Enum.reject(tasks, &MapSet.member?(state.processed_ids, &1.id))
Enum.each(new_tasks, &Symphony.AgentRunner.run/1)
new_ids = Enum.reduce(new_tasks, state.processed_ids, &MapSet.put(&2, &1.id))
schedule_poll()
{:noreply, %{state | processed_ids: new_ids}}
{:error, reason} ->
Logger.error("Linear poll failed: #{inspect(reason)}")
schedule_poll()
{:noreply, state}
end
end
defp schedule_poll do
Process.send_after(self(), :poll, @poll_interval)
end
end
defmodule Symphony.Linear.Client do
@linear_api "https://api.linear.app/graphql"
def fetch_todo_tasks do
query = """
query {
issues(filter: { state: { name: { eq: "Todo" } } }) {
nodes {
id
title
description
}
}
}
"""
case HTTPoison.post(@linear_api, Jason.encode!(%{query: query}), headers()) do
{:ok, %{status_code: 200, body: body}} ->
tasks =
body
|> Jason.decode!()
|> get_in(["data", "issues", "nodes"])
|> Enum.map(&to_task/1)
{:ok, tasks}
{:error, reason} ->
{:error, reason}
end
end
defp headers do
[
{"Authorization", System.get_env("LINEAR_API_KEY")},
{"Content-Type", "application/json"}
]
end
defp to_task(%{"id" => id, "title" => title, "description" => desc}) do
%Symphony.Task{id: id, title: title, description: desc, source: :linear}
end
end
defmodule Symphony.ProofOfWork do
@doc """
Collects proof of work for a PR before it can be merged.
Returns a map with CI status, review feedback, and complexity.
"""
def collect(pr_url) do
with {:ok, ci_status} <- wait_for_ci(pr_url),
{:ok, reviews} <- fetch_reviews(pr_url),
{:ok, complexity} <- analyze_complexity(pr_url) do
{:ok, %{
ci_status: ci_status,
reviews: reviews,
complexity: complexity,
collected_at: DateTime.utc_now()
}}
end
end
defp wait_for_ci(pr_url, retries \\ 30) do
case GitHub.get_pr_ci_status(pr_url) do
{:ok, :success} -> {:ok, :success}
{:ok, :pending} when retries > 0 ->
Process.sleep(60_000)
wait_for_ci(pr_url, retries - 1)
{:ok, status} -> {:ok, status}
{:error, reason} -> {:error, reason}
end
end
defp fetch_reviews(pr_url), do: GitHub.get_pr_reviews(pr_url)
defp analyze_complexity(pr_url), do: GitHub.get_pr_diff_complexity(pr_url)
end
当使用另一种语言构建 Symphony 时,规范定义了:
伪代码中的最小实现循环:
# Core symphony loop
def symphony_loop(state) do
tasks = fetch_new_tasks(state.source)
tasks
|> Enum.filter(&(&1.status == :todo))
|> Enum.each(fn task ->
Task.async(fn ->
branch = create_isolated_branch(task)
invoke_agent(task, branch) # Codex / Claude / etc.
proof = collect_proof_of_work(branch)
present_for_review(task, proof)
end)
end)
Process.sleep(state.poll_interval)
symphony_loop(state)
end
defmodule Symphony.AgentPool do
use GenServer
@max_concurrent 3
def start_link(_), do: GenServer.start_link(__MODULE__, %{running: 0, queue: []}, name: __MODULE__)
def submit(task) do
GenServer.cast(__MODULE__, {:submit, task})
end
def handle_cast({:submit, task}, %{running: n} = state) when n < @max_concurrent do
spawn_agent(task)
{:noreply, %{state | running: n + 1}}
end
def handle_cast({:submit, task}, %{queue: q} = state) do
{:noreply, %{state | queue: q ++ [task]}}
end
def handle_info({:agent_done, _result}, %{running: n, queue: [next | rest]} = state) do
spawn_agent(next)
{:noreply, %{state | running: n, queue: rest}}
end
def handle_info({:agent_done, _result}, %{running: n} = state) do
{:noreply, %{state | running: n - 1}}
end
defp spawn_agent(task) do
parent = self()
spawn(fn ->
result = Symphony.AgentRunner.run(task)
send(parent, {:agent_done, result})
end)
end
end
# In IEx or a Mix task
Symphony.AgentPool.submit(%Symphony.Task{
id: "manual-001",
title: "Add rate limiting to API",
description: "Implement token bucket rate limiting on /api/v1 endpoints",
source: :manual
})
| 问题 | 可能原因 | 修复方法 |
|---|---|---|
| 代理未生成 | 缺少 OPENAI_API_KEY | 检查环境变量是否已导出 |
| 未检测到 Linear 任务 | Linear 状态过滤器错误 | 更新查询过滤器以匹配你的看板状态名称 |
| 未创建 PR | 缺少 GITHUB_TOKEN 或仓库错误 | 验证令牌是否具有 repo 作用域 |
| CI 从未完成 | 超时时间太短 | 在 wait_for_ci/2 中增加 retries |
| 并发运行过多 | 默认池大小 | 在配置中设置 max_concurrent_agents |
| 分支冲突 | 代理重用分支名称 | 确保每次运行的任务 ID 唯一 |
# In config/dev.exs
config :symphony, log_level: :debug
# Or at runtime
Logger.put_module_level(Symphony.AgentRunner, :debug)
Logger.put_module_level(Symphony.Linear.Poller, :debug)
每周安装次数
362
仓库
GitHub Stars
10
首次出现
8 天前
安全审计
已安装于
gemini-cli359
github-copilot359
codex359
amp359
cline359
kimi-cli359
Skill by ara.so — Daily 2026 Skills collection.
Symphony turns project work into isolated, autonomous implementation runs, allowing teams to manage work instead of supervising coding agents. Instead of watching an agent code, you define tasks (e.g. in Linear), and Symphony spawns agents that complete them, provide proof of work (CI status, PR reviews, walkthrough videos), and land PRs autonomously.
Paste this prompt into Claude Code, Cursor, or Codex:
Implement Symphony according to the following spec:
https://github.com/openai/symphony/blob/main/SPEC.md
git clone https://github.com/openai/symphony.git
cd symphony/elixir
Follow elixir/README.md, or ask an agent:
Set up Symphony for my repository based on
https://github.com/openai/symphony/blob/main/elixir/README.md
export OPENAI_API_KEY="sk-..." # OpenAI API key for Codex
export LINEAR_API_KEY="lin_api_..." # Linear integration
export GITHUB_TOKEN="ghp_..." # GitHub PR operations
export SYMPHONY_REPO_PATH="/path/to/repo" # Target repository
cd elixir
mix deps.get
elixir/config/config.exs)import Config
config :symphony,
openai_api_key: System.get_env("OPENAI_API_KEY"),
linear_api_key: System.get_env("LINEAR_API_KEY"),
github_token: System.get_env("GITHUB_TOKEN"),
repo_path: System.get_env("SYMPHONY_REPO_PATH", "./"),
poll_interval_ms: 30_000,
max_concurrent_agents: 3
mix symphony.start
# or in IEx for development
iex -S mix
Each task gets its own isolated run:
Before a PR is accepted, Symphony collects:
# In your application.ex or directly
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
Symphony.Supervisor
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
defmodule Symphony.Task do
@type t :: %__MODULE__{
id: String.t(),
title: String.t(),
description: String.t(),
source: :linear | :manual,
status: :pending | :running | :completed | :failed,
branch: String.t() | nil,
pr_url: String.t() | nil,
proof_of_work: map() | nil
}
defstruct [:id, :title, :description, :source,
status: :pending, branch: nil,
pr_url: nil, proof_of_work: nil]
end
defmodule Symphony.AgentRunner do
@doc """
Spawns an isolated agent run for a given task.
Each run gets its own branch and Codex session.
"""
def run(task) do
branch = "symphony/#{task.id}-#{slugify(task.title)}"
with :ok <- Git.create_branch(branch),
{:ok, result} <- Codex.implement(task, branch),
{:ok, pr_url} <- GitHub.open_pr(branch, task),
{:ok, proof} <- ProofOfWork.collect(pr_url) do
{:ok, %{task | status: :completed, pr_url: pr_url, proof_of_work: proof}}
else
{:error, reason} -> {:error, reason}
end
end
defp slugify(title) do
title
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/, "-")
|> String.trim("-")
end
end
defmodule Symphony.Linear.Poller do
use GenServer
@poll_interval Application.compile_env(:symphony, :poll_interval_ms, 30_000)
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
schedule_poll()
{:ok, %{processed_ids: MapSet.new()}}
end
def handle_info(:poll, state) do
case Symphony.Linear.Client.fetch_todo_tasks() do
{:ok, tasks} ->
new_tasks = Enum.reject(tasks, &MapSet.member?(state.processed_ids, &1.id))
Enum.each(new_tasks, &Symphony.AgentRunner.run/1)
new_ids = Enum.reduce(new_tasks, state.processed_ids, &MapSet.put(&2, &1.id))
schedule_poll()
{:noreply, %{state | processed_ids: new_ids}}
{:error, reason} ->
Logger.error("Linear poll failed: #{inspect(reason)}")
schedule_poll()
{:noreply, state}
end
end
defp schedule_poll do
Process.send_after(self(), :poll, @poll_interval)
end
end
defmodule Symphony.Linear.Client do
@linear_api "https://api.linear.app/graphql"
def fetch_todo_tasks do
query = """
query {
issues(filter: { state: { name: { eq: "Todo" } } }) {
nodes {
id
title
description
}
}
}
"""
case HTTPoison.post(@linear_api, Jason.encode!(%{query: query}), headers()) do
{:ok, %{status_code: 200, body: body}} ->
tasks =
body
|> Jason.decode!()
|> get_in(["data", "issues", "nodes"])
|> Enum.map(&to_task/1)
{:ok, tasks}
{:error, reason} ->
{:error, reason}
end
end
defp headers do
[
{"Authorization", System.get_env("LINEAR_API_KEY")},
{"Content-Type", "application/json"}
]
end
defp to_task(%{"id" => id, "title" => title, "description" => desc}) do
%Symphony.Task{id: id, title: title, description: desc, source: :linear}
end
end
defmodule Symphony.ProofOfWork do
@doc """
Collects proof of work for a PR before it can be merged.
Returns a map with CI status, review feedback, and complexity.
"""
def collect(pr_url) do
with {:ok, ci_status} <- wait_for_ci(pr_url),
{:ok, reviews} <- fetch_reviews(pr_url),
{:ok, complexity} <- analyze_complexity(pr_url) do
{:ok, %{
ci_status: ci_status,
reviews: reviews,
complexity: complexity,
collected_at: DateTime.utc_now()
}}
end
end
defp wait_for_ci(pr_url, retries \\ 30) do
case GitHub.get_pr_ci_status(pr_url) do
{:ok, :success} -> {:ok, :success}
{:ok, :pending} when retries > 0 ->
Process.sleep(60_000)
wait_for_ci(pr_url, retries - 1)
{:ok, status} -> {:ok, status}
{:error, reason} -> {:error, reason}
end
end
defp fetch_reviews(pr_url), do: GitHub.get_pr_reviews(pr_url)
defp analyze_complexity(pr_url), do: GitHub.get_pr_diff_complexity(pr_url)
end
When building Symphony in another language, the spec defines:
Minimal implementation loop in pseudocode:
# Core symphony loop
def symphony_loop(state) do
tasks = fetch_new_tasks(state.source)
tasks
|> Enum.filter(&(&1.status == :todo))
|> Enum.each(fn task ->
Task.async(fn ->
branch = create_isolated_branch(task)
invoke_agent(task, branch) # Codex / Claude / etc.
proof = collect_proof_of_work(branch)
present_for_review(task, proof)
end)
end)
Process.sleep(state.poll_interval)
symphony_loop(state)
end
defmodule Symphony.AgentPool do
use GenServer
@max_concurrent 3
def start_link(_), do: GenServer.start_link(__MODULE__, %{running: 0, queue: []}, name: __MODULE__)
def submit(task) do
GenServer.cast(__MODULE__, {:submit, task})
end
def handle_cast({:submit, task}, %{running: n} = state) when n < @max_concurrent do
spawn_agent(task)
{:noreply, %{state | running: n + 1}}
end
def handle_cast({:submit, task}, %{queue: q} = state) do
{:noreply, %{state | queue: q ++ [task]}}
end
def handle_info({:agent_done, _result}, %{running: n, queue: [next | rest]} = state) do
spawn_agent(next)
{:noreply, %{state | running: n, queue: rest}}
end
def handle_info({:agent_done, _result}, %{running: n} = state) do
{:noreply, %{state | running: n - 1}}
end
defp spawn_agent(task) do
parent = self()
spawn(fn ->
result = Symphony.AgentRunner.run(task)
send(parent, {:agent_done, result})
end)
end
end
# In IEx or a Mix task
Symphony.AgentPool.submit(%Symphony.Task{
id: "manual-001",
title: "Add rate limiting to API",
description: "Implement token bucket rate limiting on /api/v1 endpoints",
source: :manual
})
| Problem | Likely Cause | Fix |
|---|---|---|
| Agents not spawning | Missing OPENAI_API_KEY | Check env var is exported |
| Linear tasks not detected | Wrong Linear state filter | Update query filter to match your board's state name |
| PRs not opening | Missing GITHUB_TOKEN or wrong repo | Verify token has repo scope |
| CI never completes | Timeout too short | Increase retries in wait_for_ci/2 |
| Too many concurrent runs |
# In config/dev.exs
config :symphony, log_level: :debug
# Or at runtime
Logger.put_module_level(Symphony.AgentRunner, :debug)
Logger.put_module_level(Symphony.Linear.Poller, :debug)
Weekly Installs
362
Repository
GitHub Stars
10
First Seen
8 days ago
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
gemini-cli359
github-copilot359
codex359
amp359
cline359
kimi-cli359
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
| Default pool size |
Set max_concurrent_agents in config |
| Branch conflicts | Agent reusing branch names | Ensure task IDs are unique per run |