ark-audio-agent
Autonomous text → music. Give it a sentence; it reasons about the feeling, composes a structured piece, and renders it to MIDI + audio — with no human in the loop.
The interesting part is the harness: composition is driven by a team of specialist agents running a ReAct loop (reason → act → observe) over a shared musical blackboard. It runs on the Claude API when a key is available, and on a fully-offline deterministic music-theory brain otherwise, so it always produces a result.
ark-audio "a rainy night in a neon city, lonely but hopeful"
# → renders/a-rainy-night-in-a-neon-city-lonely-but-hopeful/song.mp3 (+ .wav, .mid, report.md, trace.jsonl)
Why it's built this way
Rather than ask one model to emit raw notes (which drifts out of key and is hard to steer), the system separates musical intent from musical realization:
- Agents decide intent — key, mode, tempo, song form, chord progressions, a melody's contour/register/density, instrument roles, drum and bass styles.
- Deterministic engines realize it — pure music-theory functions turn that
intent into in-key notes, which a
midowriter turns into MIDI and a from-scratchnumpysynthesizer turns into audio.
Because every decision an agent can make is a tool, both brains (Claude and the offline planner) drive the exact same action surface and produce the exact same trace format.
Architecture
text prompt
│
┌──────────▼───────────────────────────────────────────────┐
│ Orchestrator (the harness) │
│ │
│ Interpreter → Composer → Melodist → Arranger │ each agent runs a
│ │ │ ReAct loop:
│ ▼ │ reason (thinking)
│ Critic ──reject──┐ │ → act (tool call)
│ │approve │ one targeted │ → observe (result)
│ │ revision (bounded) │
└───────────────────────────────┼────────────┘ │
│ │
┌───────────────────────────▼───────────────────────┐ │
│ Tools (the ReAct action space) │ │
│ set_brief · get_theory · set_global · │ │
│ set_structure · set_progression · design_melody · │ │
│ set_instruments · set_bass · set_drums · │ │
│ analyze_score · finish / approve / request_revision│ │
└───────────────────────────┬───────────────────────┘ │
▼ │
Score blackboard (shared state) │
└──────────────────────────────┬─────────────────────────────┘
▼
performance.py → { melody, harmony, bass, drums } notes
│
┌───────────────────┴────────────────────┐
▼ ▼
midi_render.py (mido) synth.py (numpy software synth)
│ │
song.mid song.wav ──(ffmpeg)──► song.mp3
The agents
| Agent | Decides | Tools |
|---|---|---|
| Interpreter | mood, imagery, genre, key/mode/tempo, emotional arc | set_brief, set_title |
| Composer | time signature, section form + dynamic arc, chord progressions | get_theory, set_global, set_structure, set_progression |
| Melodist | per-section melody contour, register, density, syncopation | design_melody, get_theory |
| Arranger | instruments, per-section bass & drum styles | set_instruments, set_bass, set_drums |
| Critic | judges the whole piece; approves or bounces back one revision | analyze_score, approve, request_revision |
Every agent also has analyze_score (a sensing action) and a terminal finish.
Install
Uses uv.
uv sync --extra llm # installs the optional anthropic SDK for the Claude brain
# or, without the Claude brain (offline only):
uv sync
ffmpeg is optional — if present, an MP3 is exported alongside the WAV.
Usage
CLI
ark-audio "triumphant sunrise over the mountains"
ark-audio "a quiet dawn over still water" --offline --seed 7
ark-audio "lonely synthwave drive at 2am" --out renders/drive --no-mp3
echo "a storm breaking over the sea" | ark-audio # reads stdin
Flags: --offline (force the deterministic brain), --seed, --out,
--model, --effort low|medium|high|xhigh|max, --max-revisions, --reverb,
--no-mp3.
Library
from ark_audio_agent import compose
result = compose("a melancholy rainy afternoon", out_dir="renders/rain", offline=True)
print(result.outputs["wav"], "-", result.score.title)
Outputs
Each run writes, into the output directory:
| File | What |
|---|---|
song.mid |
Standard MIDI file (open in any DAW / soundfont player) |
song.wav |
44.1 kHz stereo audio from the built-in synth |
song.mp3 |
192 kbps MP3 (if ffmpeg is installed) |
score.json |
the full declarative Score (reload / tweak / re-render) |
brief.json |
compact human/LLM summary of the piece |
trace.jsonl |
every agent step: thought · action · observation |
report.md |
a readable write-up of the brief, structure, and agent trace |
The two brains
Claude multi-agent (default when an API key is set). Each agent runs
the canonical Messages-API tool-use loop with adaptive thinking; the model
reasons, calls a tool, reads the observation, and continues until it finishes.
Defaults to claude-opus-5-5. The API call degrades gracefully across
SDK/account differences (server-side refusal fallback → plain streaming → drop
effort → drop thinking), so the run never hard-fails on a capability mismatch.
export ANTHROPIC_API_KEY=sk-ant-...
ark-audio "a cathedral of light at the edge of the world" --effort high
Claude on Microsoft Foundry (Azure). Point the brain at a Foundry resource
instead and it uses the SDK's AnthropicFoundry client with API-key auth. A
Foundry endpoint takes precedence over ANTHROPIC_API_KEY:
export ANTHROPIC_FOUNDRY_API_KEY=...
export ANTHROPIC_FOUNDRY_RESOURCE=my-resource # or ANTHROPIC_FOUNDRY_BASE_URL=https://my-resource.services.ai.azure.com/anthropic/
export ARK_MODEL=claude-opus-5-5 # your Foundry deployment name
ark-audio "a cathedral of light at the edge of the world"
Server-side refusal fallback is a first-party API feature, so it is skipped on Foundry. Adaptive thinking and effort still apply.
Deterministic (offline, always available). A rule-based planner maps the
text to a feeling via a lexicon, then applies music-theory heuristics to make the
same tool calls an LLM would. Reproducible given a --seed. This is what runs in
CI and what powers --offline.
If no key is found (and
--offlineisn't set), the CLI automatically uses the deterministic brain and says so.
How the music is realized
- Theory (
theory.py): scales/modes, diatonic chords, Roman-numeral resolution, scale-degree → MIDI, equal-temperament frequencies. - Blackboard (
blackboard.py): the declarativeScorethe agents edit. - Performance (
performance.py): realizes the Score into notes — a contour-driven melody that snaps to chord tones on strong beats, chord comping or sustained pads, several bass styles (roots, root-fifth, pulse, walking, arp), and synthesized drum patterns (four-on-floor, backbeat, breakbeat, …), all seeded and reproducible. - Synth (
synth.py): additive (band-limited) oscillator voices with ADSR, per-instrument patches, synthesized percussion, an FFT-convolution reverb, and a soft-clipping master bus.
Development
uv run pytest # theory + pipeline tests (offline, no key needed)
uv run ark-audio "..." --offline
License
MIT.
Metadata
Release files for ark-audio-agent 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ark_audio_agent-0.1.3.tar.gz | 40.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ark_audio_agent-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 87.3 kB
Release files / ark_audio_agent-0.1.3.tar.gz
| Download URL | ark_audio_agent-0.1.3.tar.gz |
|---|---|
| Size | 40.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e482c2af624be3ce70d1741587a6776e47eed9e5779a0efbad1603bbd00ddc3
|
|
BLAKE2b-256 checksum How to use checksums |
bc0bc8d146327edfa98ee210f0e4c447f0686cac625ea5a72ea6a7f5a528ca95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / ark_audio_agent-0.1.3-py3-none-any.whl
| Download URL | ark_audio_agent-0.1.3-py3-none-any.whl |
|---|---|
| Size | 46.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7c0876f2c18e1ce269186603b799d74f16a35db6bfd4ed99f7054be1119451b
|
|
BLAKE2b-256 checksum How to use checksums |
4f0971b0ade4a42870cce9e133f630b6de2691a16793a7a64ed9bf4f8a4ddb8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|