Uvnitř fLMCP: jak nástrojovými agenty spolehlivě řídit živý DAW.
fLMCP neřeší jen to, zda agent umí poslat hudební příkaz. Řeší, zda jej živá desktopová aplikace provede na správném vlákně, v odpovídajícím běhovém kontextu, ve správném pořadí a s výsledkem, kterému se dá věřit.
fLMCP začalo méně efektní otázkou než „umí agent udělat beat?“ Dokáže software změnit živý projekt, aniž by poškodil relaci, zablokoval rozhraní nebo ztratil přehled o vlastních změnách? Odpověď musela respektovat skutečné vlastnosti DAW: hostitelské API není bezpečné pro souběžná vlákna, schopnosti jsou rozdělené mezi několik běhových prostředí Pythonu, veřejné API nepokrývá vše a hudební operace jsou citlivé na čas i aktuální výběr v rozhraní.
Dnešní veřejná vrstva nabízí 159 MCP nástrojů a 7 zdrojů pouze pro čtení. Samotný počet ale není hlavní výsledek. Důležitý je způsob provádění: před změnou načíst stav, na každé hranici ověřit vstup, hostitelské API volat pouze z jeho vlastního vlákna, vyšší hudební záměr překládat deterministickým kódem a tam, kde veřejné API operaci neumí, vrátit pravdivou chybu.
Nejtěžší je zachovat pravidla běhu samotného DAW.
Desktopový DAW není bezstavové API s přilepeným grafickým rozhraním. Je to interaktivní proces s měnícím se výběrem, transportem, pluginy, časově citlivou prací a událostní smyčkou vlastněnou hostitelem. I zdánlivě jednoduchý příkaz mívá skryté předpoklady. Nastavení kroku v jiném patternu vyžaduje dočasnou změnu kontextu a jeho obnovu. Některé operace mute a solo pouze přepínají stav, místo aby jej přímo nastavily. Objekty not z piano rollu nejsou dostupné obecnému řídicímu prostředí.
Proto fLMCP nepředstírá, že všechny operace mají stejnou spolehlivost. U každé schopnosti rozlišuje, zda je čitelná, měnitelná, které běhové prostředí ji vlastní a jak se musí projevit selhání.
| Kontrakt | Úloha | Hranice |
|---|---|---|
| Zdroje | Popisují aktuální stav bez jeho změny. | Nejde o atomický snímek celé relace. |
| Nástroje | Provádějí omezenou, typovanou změnu. | Parametry, výsledek a omezení jsou explicitní. |
| Řídicí prostředí | Vlastní běžná volání DAW. | Síťové vlákno smí práci zařadit, ne provést. |
| Prostředí piano rollu | Vlastní objekty not a jejich editaci. | Potřebuje samostatné předání dat a kontext rozhraní. |
| Zvukové prostředí | Provádí volitelnou analýzu a transkripci. | Náročné závislosti se načítají až při konkrétním volání nástroje. |
Tři běhové kontexty, jedna cesta požadavku.
Externí proces v Pythonu komunikuje přes MCP na standardním vstupu a výstupu. Uvnitř FL Studia řídicí skript vlastní lokální TCP koncový bod a běžné hostitelské API. Samostatný skript piano rollu vlastní operace nad notami. Rozdělení není jen pěkný diagram. Brání externímu serveru v chybném předpokladu, že všechny schopnosti FL lze importovat a volat na jednom místě.
Drátový protokol je záměrně nudný. Čtyřbajtová délka v pořadí big-endian udělá z proudu TCP jednoznačně dělitelnou posloupnost rámců. Pevný limit 16 MiB omezuje alokaci. Identifikátor požadavku odliší odpověď od rámce události a jeden klientský zámek zabrání souběžným volajícím promíchat bajty na trvalém spojení.
# Obálka mezi MCP procesem a FL; veřejný kontrakt.
request = {
"id": 42,
"action": "transport.setTempo",
"params": {"bpm": 174.0}
}
# frame = uint32_be(len(payload)) + utf8_json(payload)
# response = {"id": 42, "ok": True, "result": {...}}
FL dnes vysílá události transportu a obnovení, ale MCP klient je při čekání na odpověď pouze odčerpá. Nadřazenému klientovi je zatím neposílá. To je skutečná hranice aktuální verze, ne funkce, kterou si připíšeme předčasně.
Existuje i jemnější kompromis. Řídicí skript po provedení hostitelské operace zapisuje odpověď ze svého OnIdle(). Na lokálním spojení a s omezenými zprávami jde obvykle o krátkou práci, pořád je to ale zápis do socketu na vlákně DAW. Budoucí bridge může hotovou odpověď předat I/O vláknu, aniž by porušil hlavní pravidlo: pouze vlákno hostitele se dotýká FL API.
Spolehlivost vzniká respektováním hranic, ne přidáváním nástrojů.
Síťové vlákno nikdy nevlastní FL API.
Požadavek končí ve frontě FIFO. Hostitelský OnIdle() z ní v jednom cyklu odebere nejvýše 32 položek a provede je na vlákně, které k API skutečně patří. Limit chrání odezvu aplikace před nekonečně rostoucí frontou.
Automatizace se plánuje; nespí se u ní.
Časované kroky používají monotónní časové termíny a stavový plánovač. Blokující čekání by zastavilo rozhraní a vytvořilo časový posun. I tak zůstává známé omezení: plán si při zadání uloží aktuální tempo, takže změna BPM uprostřed běhu může posunout pozdější hudební události.
Noty se provádějí tam, kde existuje API pro jejich editaci.
Řídicí skript může připravit pattern a kontext kanálu, ale objekty not smí upravovat pouze skript piano rollu. Systém přesouvá přes hranici data, ne samotné API.
Nepodporované znamená nepodporované.
Pokud veřejné API neumí přesné umístění klipu, přímé vyrenderování projektu nebo konkrétní operaci v aranži, nástroj vrátí strukturované omezení. Falešný úspěch je horší než explicitní mezera, protože znehodnotí každý další krok agenta.
Přemostění piano rollu je pragmatická transakce mezi dvěma kontexty.
Editace not je místo, kde se obecný příběh automatizace rozpadá. FL zpřístupňuje score.addNote, score.deleteNote a objekty not pouze skriptům piano rollu. fLMCP proto zachází s tímto skriptem jako s malou prováděcí službou: vybere cílový pattern a kanál, připraví dávku JSON, zaměří piano roll, spustí aktivní skript a opakovaně čte exportovaný stav.
piano_roll_add_notes(
channel=4,
pattern=2,
clear_first=True,
notes=[
{"midi": 48, "time_bars": 0.0, "duration_bars": 1.0, "velocity": 0.78},
{"midi": 55, "time_bars": 0.0, "duration_bars": 1.0, "velocity": 0.78},
{"midi": 60, "time_bars": 0.0, "duration_bars": 1.0, "velocity": 0.78},
],
)
# select context -> stage batch -> trigger script -> read score state
Předání je schválně viditelné na disku a dobře se ladí. Zároveň jde o nejméně transakční část architektury. Aktuální implementace vyčistí starý stav, přidá operace, odešle klávesovou zkratku do popředí a po 50 ms kontroluje výsledek, nejvýše po dobu tří sekund. Chybné zaměření okna nebo jiný aktivní skript může běh zablokovat. Korelační identifikátor a atomická výměna souboru by lépe odmítly zastaralý stav; odstranění závislosti na klávesové zkratce by vyžadovalo hostitelské API, které dnes není k dispozici.
Počet nástrojů je inventář; doménové hranice jsou rozhraní.
Sto padesát devět registrací zní dobře, ale neříká nic o korektnosti. Užitečné je, že katalog kopíruje doménový model DAW. Nástroj transportu neschovává změnu mixu. Generátor vrací kontrolovatelná data not. Projektové nástroje zachovávají krok zpět tam, kde jej hostitel nabízí. Omezení zůstávají viditelná.
Sedm zdrojů s prefixem fl:// je druhá polovina kontraktu. Jde o dotazy pouze pro čtení, ne synchronizovanou databázi. Agent se díky nim může před změnou zorientovat a po ní ověřit vybraný stav. Smyčka „přečíst, změnit, znovu přečíst“ je podstatně bezpečnější než dlouhá série slepých příkazů postavená na starém zadání.
# Orchestrace na straně agenta.
before = read_resource("fl://channels")
target = find_channel(before, name_contains="Sub")
if target is not None:
channel_route_to_mixer(index=target.index, mixer_track=12)
mixer_set_eq_band(track=12, band=1, gain=-0.2, frequency=180)
after = read_resource("fl://channels")
assert route_for(after, target.index) == 12
Vyšší záměr se kompiluje; za API se neimprovizuje.
Generátorová vrstva je deterministický Python, nikoli druhý jazykový model schovaný uvnitř bridge. Název progrese se přeloží na stupně a kvalitu akordů. Styl basové linky rozvine kořeny do časovaných MIDI událostí. Styl bicích mapuje symbolické hlasy na krokovou mřížku. Agent volí hudební záměr, běžný kód jej rozvine do opakovatelného a testovatelného výsledku.
pattern_create(name="Verse - 174 DnB sketch")
transport_set_tempo(bpm=174)
gen_emit_drum_pattern_step_seq(
channel_map={"kick": 0, "snare": 1, "clhat": 2, "ophat": 3},
style="drum_and_bass",
repeats=2,
)
gen_emit_bassline(
channel=4,
root="C2",
scale="minor",
progression="i-VII-VI-V",
pattern_style="octaves",
)
Audio je užitečné, až když se změní v editovatelný stav.
Zvuková vrstva je záměrně volitelná. Transport, mix, kanály, pluginy a generátory naběhnou bez NumPy, knihoven pro analýzu zvuku, ovladačů mikrofonu i transkripčního prostředí. Náročné moduly se importují až při konkrétním volání, takže chybějící zvukový doplněk nerozbije celý MCP server.
Pro broukání a izolované melodické linky používá fLMCP odhad základní frekvence pYIN. Stabilní úseky rozdělí na noty, odstraní krátké nebo málo jisté kandidáty, volitelně je přitáhne ke stupnici, kvantizuje začátky a podle BPM převede sekundy na pozice v taktech. Polyfonní režim posílá překrývající se noty přes Basic Pitch na ONNX. Jde o dva různé odhady s různými chybami; rozhraní proto vrací, který z nich výsledek vytvořil. Skóre přesnosti nepublikujeme, protože zatím nemáme reprezentativní evaluační korpus.
Pět myšlenek, které se hodí i mimo hudbu.
1. Modelujte schopnosti, ne kliknutí.
Typovaná operace zachytí vstupní podmínky a chyby. Zaznamenané gesto zachytí jen jednu šťastnou polohu rozhraní.
2. Oddělte pozorování od mutace.
Zdroje pouze pro čtení dovolují plánování a kontrolu výsledného stavu, aniž by každé čtení samo něco měnilo.
3. Přes vlákna přenášejte data ve frontě.
Když API patří událostní smyčce hostitele, pošlete k ní popis práce a proveďte jej tam. Nepřesouvejte samotné API na cizí vlákno.
4. Vyšší záměr překládejte deterministicky.
Model může vybrat progresi nebo cíl směrování. Otestovaný kód má rozhodnout o přesných nízkoúrovňových operacích.
5. Omezení patří do protokolu.
Explicitní „hostitel tuto operaci nevystavuje“ je použitelný výsledek. Tiché nicnedělání nebo vymyšlený úspěch ničí důvěru v celý řetězec.
Lokální hranice je úzká, ne automaticky bezpečná.
Bridge poslouchá na 127.0.0.1, ale místní procesy neověřuje. Loopback omezuje dosah; neprokazuje identitu volajícího. Proxy nebo vystavení portu by změnily model hrozeb a nejsou součástí návrhu. Jeden trvalý klient a zámek současně drží nejvýše jeden rozpracovaný požadavek. Architektura tak upřednostňuje předvídatelný stav hostitele před propustností pro více klientů.
Nejzajímavější bugy byly v adaptérech, ne v hudbě.
Hostitelské API s pozičními přepínači režimu se volá chybně překvapivě snadno. V jedné třídě chyb skončila logická hodnota pro globální index kanálu v dřívějším parametru pickupMode. Jinde se přepínací funkce používala jako setter nebo adaptér barev předpokládal opačné pořadí bajtů. Každý příkaz něco vrátil; několik z nich přesto znamenalo něco jiného, než volající čekal.
# Stejný Python typ, odlišná host semantika.
volume = channels.getChannelVolume(index, True) # True je mode, ne global index
volume = channels.getChannelVolume(index, False, True) # mode=False, useGlobalIndex=True
# Toggle-only API: nejprve číst, měnit pouze při rozdílu.
if bool(channels.isChannelSolo(index)) != requested_solo:
channels.soloChannel(index)
Řešením je testovat hranici hostitele. Modul pro FL lze importovat s náhradami hostitelských modulů, podvržené veřejné API zachytí chybějící názvy a změnu signatur, falešné protistrany TCP prověří rámcování a obnovení spojení a čisté testy pokrývají generátory, převod zvuku, odložené importy i registraci serveru. Nenahradí to koncový test v DAW, ale velkou třídu regresí přesune z ručního klikání do automatizace.
Dalším krokem jsou silnější důkazy, ne delší katalog.
Nejbližší práce je konkrétní: zpřístupnit události bridge klientům MCP místo jejich zahazování, přidat korelační identifikátor a atomickou výměnu při předání piano rollu, přesunout I/O odpovědi z vlákna DAW, doplnit kontroly výsledného stavu k destruktivním pracovním postupům a vložit body návratu kolem vícekrokových editací. Zvuková transkripce potřebuje malou reprezentativní evaluační sadu dřív, než lze prahy jistoty vydávat za měřenou kvalitu.
Širší lekce fLMCP je přímočará. Profesionální desktopová aplikace se stane věrohodným agentním prostředím tehdy, když integrace respektuje vlastnictví vláken, hranice schopností, ověření stavu a semantiku chyb. Model je jen jeden účastník. Spolehlivost tvoří obyčejné inženýrství kolem něj.