Avagenc Yori
2026 — An AI agent in a group chat that finds music on Spotify and plays it for you
01 A member who handles music
Avagenc Chat is a group chat where one person talks with several AI agents, each good at one thing. Yori is the one for music. Writing "@yori put on Bohemian Rhapsody" is enough, and a moment later the song plays on the phone or laptop where the person has Spotify open.
Behind the name is a language model, Gemini, which on its own only reads text and writes text. It cannot hear what is playing and it cannot press play. Yori becomes an agent because it is handed eleven tools it may ask to use, each a small Go function that calls Spotify, and a written instruction that tells it when to use which.
So a turn is a loop. Yori reads the chat, asks for a tool, reads what the tool returns, and decides again, until the request is done and it writes its reply. The person sees only that reply. Everything before it happens out of sight, and the replay of four scripted requests shows that hidden part.
02 Two ways in
A message that mentions only @yori goes straight to Yori. A message that mentions nobody goes to Ava, the agent who leads the chat, and when it is about music she calls Yori as one of her own tools. She writes only "@yori" and repeats nothing, because Yori reads the request a few messages up in the same thread.
Ava knows when to call Yori from a paragraph Yori ships with, its description. It says Yori searches and plays Spotify as one surface, that playback needs Spotify Premium, and that Yori makes no playlists and plays no podcasts or audiobooks.
Each turn also tells Yori who woke it. Woken by Ava, it must not take her "@yori" for a greeting from the person, and if its own answer to the request is already in the thread, its reply is empty. Woken by the person, it answers them directly and sends them to Ava for anything another agent does.
func subAgentToADKTool(subAgent SubAgent) (adktool.Tool, error) {
t, err := functiontool.New(
functiontool.Config{
Name: subAgent.Name(),
Description: subAgent.Description(),
},
func(toolCtx agent.ToolContext, in subAgentToolArg) (subAgentToolOutput, error) {
ctx, err := toolCtxToCtx(toolCtx)
if err != nil {
return subAgentToolOutput{}, err
}
reply, err := subAgent.Run(ctx, in.Message)
if err != nil {
return subAgentToolOutput{}, fmt.Errorf("ava: delegate to %s: %w", subAgent.Name(), err)
}
return subAgentToolOutput{Response: reply}, nil
},
)
if err != nil {
return nil, fmt.Errorf("ava: build delegation tool %q: %w", subAgent.Name(), err)
}
return t, nil
}03 What it is told
Yori's instruction is written in Indonesian, like the chat, and most of it is about acting. Before playing anything, Yori must already have the URI, the address Spotify gives every track and playlist, from search_tracks or my_playlists. "Jangan pernah menebak uri", it says, never guess a URI.
Yori may not trust the conversation for what is playing, which playlists exist, or which devices are on, because any of that can change outside the chat. It calls now_playing or my_devices and looks. Every request to find, play, or change the music has to end in a tool call, and answering from memory is forbidden.
It also knows where to stop. Playback control needs Spotify Premium, so when a tool reports that the account is not Premium, Yori passes that on as it is and does not try to get around it. Making or editing playlists, podcasts, audiobooks, and other music services are outside its work, and Yori says so clearly and stops there.
04 When it said it couldn't
In July 2026, tracing real conversations showed Yori telling the person it could not do things Spotify allows. Told that no device was active, it asked the person to open Spotify, while my_devices listed a Safari web player that was open but idle, which Spotify plays on when given its ID. Asked to play a song called Kacamata from one of their playlists, it said it could only play the song alone or the playlist from the start, so skipping would leave the playlist.
The first limit was only words. The play tool's description told the model to ask the person to open Spotify somewhere, and Yori did. The second was real, because the spotify library sent Spotify either a playlist or a track and never both, and Spotify needs both to start a playlist at a chosen song.
So the library learned to send both and play gained a context_uri for the playlist. A new transfer_playback tool moves a running session onto an idle device, and Yori's instruction gained a paragraph on playing inside a playlist. Its rule on devices was rewritten to open with "Perangkat tidak aktif ≠ tidak ada perangkat", an idle device is not a missing one, and now has Yori call my_devices and retry play on what it finds before asking the person for anything. The error a tool returns still offers both, opening Spotify or checking my_devices, and the instruction decides the order.
functiontool.Config{
Name: "play",
Description: `Put music on, or resume what's loaded. My way of actually starting the sound.
WHEN TO USE:
- I've found something with search_tracks/my_playlists and want to play it
- The human asks to play a song, album, artist, or playlist
- Playback is paused and I want to pick it back up
HOW TO USE:
- uri: the Spotify uri of what to play. A track uri plays that one song; an
album, artist, or playlist uri plays the whole thing. Leave uri empty to
resume whatever is already loaded — that's how I un-pause.
- context_uri: an album/playlist/artist to play WITHIN. Set this together with
a track uri when the human picks a track that came from a playlist (or album)
and wants skip next/previous to stay inside it: pass the playlist as
context_uri and the track as uri. Playback starts at that track but keeps the
playlist as context. Leave context_uri empty for a plain single track or to
play a whole thing.
- device_id: leave empty to use the active device. Set it (from my_devices) to
start playback on a specific device.
- No active device? Don't give up and don't ask the human yet. Call my_devices:
if it lists ANY available device, retry play with that device's device_id —
an available device can be idle and still be targeted, no human action needed.
Only ask the human to open Spotify when my_devices comes back empty. To move an
already-playing session onto an idle device while keeping its queue/position,
prefer transfer_playback.
Premium-only, like all playback control.`,
}05 A module of its own
Yori is a small Go module that Avagenc Chat imports. Its name is fixed in the code and its description and instruction are files built into the module, so no deployment can rename it or rewrite who it is. A caller hands it only a model, a Spotify client that acts for each person, and any instruction to add at the end.
Avagenc Chat adds what every member of the group shares, that it sits in one chat with one person and the other agents and talks like a warm friend, not a robot. Without the chat, a small program in the same repository runs Yori in a terminal against one Spotify account, connected once through a helper that goes through the real Spotify consent screen.
The repository began in May 2026 as a Gmail agent and was rewritten as the Spotify one on 4 July 2026, keeping the name. Gmail now belongs to Rafal.
func New(cfg Config) (agent.Agent, error) {
if cfg.Model == nil {
return nil, fmt.Errorf("yori: model is required")
}
if cfg.SpotifyClient == nil {
return nil, fmt.Errorf("yori: spotify client is required")
}
spotifyTools, err := adkspotify.Tools(cfg.SpotifyClient)
if err != nil {
return nil, fmt.Errorf("yori: spotify tools: %w", err)
}
tools := make([]adktool.Tool, 0, len(spotifyTools))
tools = append(tools, spotifyTools...)
instruction := "[SYSTEM_INSTRUCTION]" + systemInstruction + "\n[/SYSTEM_INSTRUCTION]"
if cfg.AdditionalInstruction != "" {
instruction = "[SYSTEM_INSTRUCTION]" + systemInstruction + "\n\n" + cfg.AdditionalInstruction + "\n[/SYSTEM_INSTRUCTION]"
}
a, err := llmagent.New(llmagent.Config{
Name: "yori",
Model: cfg.Model,
Tools: tools,
Description: description,
Instruction: instruction,
})
if err != nil {
return nil, fmt.Errorf("yori: agent: %w", err)
}
return a, nil
}06 Where it stops
Every tool acts for the person whose chat it is, with the refresh token they gave when they connected Spotify. If they never connected, the tool says so, and Yori tells them as it is instead of trying again.
The larger stop is Spotify's. Only Premium accounts can control playback, and an app stays in development mode, open only to accounts added by hand, until Spotify grants it extended access, which it now gives only to organizations with at least 250,000 monthly users. That is one reason Avagenc Chat is paused, and Yori with it.
Avagenc Yori
Turns one chat message into the Spotify calls it takes, and says plainly when Spotify will not