gradium-ai/gradbot/gradbot-voice-agent
Claude Code and agent skill by gradium-ai/gradbot
- community
Checking that the SKILL.md file is still in the repository.
Build complete, working voice agent applications using the gradbot framework. Use when user asks to "build a voice assistant", "create a voice agent", "make a voice app", "build a haggling game", or any voice-interactive application with speech. Generates a FastAPI backend with STT/LLM/TTS orchestration, tool calling, system prompts, and a polished frontend UI. Works for any domain - customer service, games, booking, tutoring, roleplay, etc. Also use when user is working with an existing gradbot demo and wants to modify, extend, or understand it.
Install
npx vskill@latest install gradium-ai/gradbot/gradbot-voice-agentSKILL.md
Gradbot Voice Agent Builder
Build working voice agent apps using the gradbot framework. Output is a complete, runnable app with backend, frontend, prompts, and configuration.
Important
- ALWAYS read
references/backend-template.mdandreferences/frontend-guidelines.mdbefore generating code - The generated app MUST follow the exact patterns from the reference files — these are battle-tested
- Use the
frontend-designskill to generate thestatic/index.html— do NOT write a generic HTML file
Two Complexity Levels
Simple chat (no tools)
For apps that are purely conversational with no actions/side-effects:
- No tools, no tool handler, no state dataclass needed
- Do NOT pass
on_tool_calltogradbot.websocket.handle_session()(omit it entirely) - Prompt can come from a file OR from the frontend (editable textarea)
- No
prompts/directory needed if prompt comes from frontend - Pass
with_voices=Truetogradbot.routes.setup()to enable voice selection UI - See the "Minimal (no tools)" template in
references/backend-template.md
Tool-using agent (with tools)
For apps with actions (ordering, searching, game mechanics, etc.):
- Define tools via
gradbot.ToolDefand implement tool handlers - Track state in a dataclass, update it in tool handlers
- System prompts loaded from
prompts/files - Include
on_tool_callcallback ingradbot.websocket.handle_session() - See the "With tools" template in
references/backend-template.md
Workflow
Step 1: Understand the Use Case
Before writing code, identify:
- Domain: What is the voice agent about?
- Persona: Who is the AI character? What personality?
- Tools needed: What actions can it perform? (If none, use simple chat pattern)
- State: What data to track across the conversation? (If none, use simple chat)
- UI needs: What should the frontend show besides transcript?
Step 2: Create the App Directory
Simple chat (no tools, prompt from frontend):
Tool-using agent (tools, file-based prompts):
If the app has static data (menus, inventories, etc.), include it as JSON files.
Step 3: Write the Backend (main.py)
Consult references/backend-template.md for exact code patterns. Choose the right template:
- Minimal (no tools): ~30 lines in a single
main.py, voice selection, frontend-editable prompt - With tools: thin
main.py(~20 lines) + domain module (game.py, ~100+ lines) with state, tools, prompts, and tool handler
Key setup rules:
- Use
gradbot.config.load(Path(__file__).parent)orgradbot.config.from_env()for config loading — it loadsconfig.yamlfrom the app directory and any shared parent config automatically. Returns aConfigobject with.client_kwargsand.session_kwargsproperties. - ALWAYS call
gradbot.routes.setup(app, config=cfg, static_dir=...)to serve frontend and bundled JS - Pass
with_voices=Truetogradbot.routes.setup()if the frontend needs voice selection (registers/api/voices) rewrite_rulesenables language-specific text rewriting before TTS. Get it fromvoice.language.rewrite_rules(returns"en","fr", etc.). Do NOT use.value—Langis not a Python enum.- ALWAYS set
silence_timeout_sto0.0— the default 5s causes the agent to re-prompt itself with its last message when the user is silent - Pass
config=cfgtohandle_session()to auto-setrun_kwargs,output_format, anddebugfrom the config. Or pass them individually for custom setups.
Critical rules for tool definitions (when using tools):
parameters_jsonmust be a JSON string — usejson.dumps()- NEVER use
"type": "array"in parameters (some LLMs like Gemma fail). Use"type": "string"with"description": "Comma-separated list"instead - Tool descriptions should say WHEN to call the tool, not just what it does
Critical rules for tool handlers (when using tools):
on_tool_callreceives 3 args:(handle, input_handle, websocket)wherehandleis agradbot.ToolHandlehandle.namegives the tool name,handle.argsgives parsed args (already deserialized dict)- Send results via
handle.send_json({...})(auto-serializes) orhandle.send(json.dumps({...}))for raw JSON - Send errors via
handle.send_error("message") - Send UI updates via
websocket.send_json({"type": "custom_event", ...}) - Use
input_handle.send_config(new_config)to swap prompts/tools mid-session
Critical rules for system prompts:
- Keep responses SHORT: "1-2 sentences max" for voice (long text = slow TTS)
- If using tools: include "Call tools silently FIRST, then speak"
- Be explicit about what the agent should NOT do
Step 4: Write System Prompts (tool-using agents only)
Skip this step for simple chat apps where the prompt comes from the frontend.
For tool-using agents, create prompts/base.txt and prompts/main.txt:
- base.txt: personality, speaking style, boundaries, response length
- main.txt: conversation flow, when to call tools, error handling
For multi-phase apps, create phase1.txt, phase2.txt, etc. and swap via input_handle.send_config().
Step 5: Build the Frontend
IMPORTANT: Invoke the frontend-design skill to create static/index.html.
When invoking frontend-design, provide these requirements:
- It's a voice agent UI — primary interaction is speech, not typing
- Must include a mic/call button to start/stop the session
- Must show a live transcript (user bubbles + agent bubbles)
- CRITICAL: Must load audio via three script tags (NOT ES module imports):
opus-encoder.js,audio-processor.js,synced-audio-player.jsfrom/static/js/— then useSyncedAudioPlayeras a global (it is NOT an ES module) - Must follow the WebSocket protocol in
references/frontend-guidelines.md - Must include echo cancellation checkbox (checked by default) — without it the agent hears its own TTS
- For simple chat: include voice selector grid, editable prompt textarea, speed slider
- For tool-using agents: include domain-specific content panels (menu, inventory, results, etc.)
Step 6: Create pyproject.toml
gradbot includes fastapi, uvicorn with websocket support, pydantic-settings, and pyyaml. Add extra dependencies only if needed.
Step 7: Verify
After generating all files:
- [ ]
main.pyimportsgradbot(usesgradbot.websocket,gradbot.routes,gradbot.config) - [ ] Uses
gradbot.config.load(Path(__file__).parent)orgradbot.config.from_env()for configuration - [ ]
gradbot.routes.setup()called withstatic_dirandwith_voices=True(if voice selection needed) - [ ]
rewrite_rulesusesvoice.language.rewrite_rules(not.value) - [ ]
silence_timeout_sset to0.0 - [ ] WebSocket endpoint calls
gradbot.websocket.handle_session()with correct callbacks - [ ] If no tools:
on_tool_callis NOT passed tohandle_session() - [ ] If tools:
parameters_jsonis a JSON string viajson.dumps(), tool results viahandle.send_json()orhandle.send(json.dumps()) - [ ] If tools:
on_tool_calltakes 3 args(handle, input_handle, websocket), useshandle.nameandhandle.args - [ ] Frontend loads JS via three
<script>tags, NOT ES module imports - [ ] Frontend
onTextdestructures a single object:({ text, turnIdx, isUser }) => - [ ] Frontend has echo cancellation checkbox wired to
SyncedAudioPlayer - [ ] System prompt enforces short responses for voice
Common Patterns
Simple Voice Chat (no tools)
- No state, no tools, no prompts/ directory
- Voice selection + editable prompt from frontend
- Frontend: transcript + voice grid + speed slider
- ~40 lines of backend code
Voice Game (haggling, trivia, roleplay)
- State tracks game progress, scores, inventory
- Tools: game actions (buy, sell, attack, answer)
- Prompt: character personality + game rules + win/lose conditions
- Frontend: game state display (inventory, score, health bar)
Customer Service Agent (ordering, booking, support)
- State tracks order/booking details
- Tools: CRUD operations (add, remove, modify, confirm)
- Prompt: service persona + menu/catalog knowledge + ordering rules
- Frontend: order summary, menu display, confirmation
Tutoring / Language Learning
- State tracks lesson progress, mistakes, topics covered
- Tools: check answer, advance lesson, provide hint
- Prompt: teacher persona + curriculum + encouragement style
- Frontend: lesson content, progress tracker, exercise display
Search-and-Act Agent (hotel booking, product search)
- State tracks search results and selections
- Tools: search, get details, book/purchase
- Prompt: phases that swap as user progresses through workflow
- Frontend: search results cards, detail view, confirmation
Troubleshooting
Agent gives long responses
Add to system prompt: "CRITICAL: Keep ALL responses to 1-2 SHORT sentences. This is a voice conversation, not a text chat."
Agent repeats itself / re-prompts when user is silent
Set silence_timeout_s = 0.0 in the session config. The default 5s timeout causes the agent to re-send its last message as context and generate a new response.
Tool calls fail with "Invalid JSON"
tool_handle.send() requires a valid JSON string. Always use json.dumps({...}), never pass a raw string.
Tool calls fail silently
Ensure on_tool_call is passed to handle_session(). Check that handle.send() is called with a JSON string, or use handle.send_json() with a dict.
Audio doesn't play
Verify frontend loads JS via three script tags (opus-encoder.js, audio-processor.js, synced-audio-player.js), NOT via ES module import. SyncedAudioPlayer is a global.
Transcript shows [object Object]
The onText callback receives a single object, not separate args. Must destructure: ({ text, turnIdx, isUser }) =>.
Agent hears its own voice (feedback loop)
Add an echo cancellation checkbox and wire it to SyncedAudioPlayer({ echoCancellation: checkbox.checked }).
Voice selection not working
Ensure gradbot.routes.setup(app, ..., with_voices=True) is passed. Without with_voices=True, the /api/voices endpoint is not registered.
WebSocket returns 404 / "Unsupported upgrade request"
Uvicorn needs the websockets library. Ensure you're using gradbot[demos] (which includes uvicorn[standard]). If still failing, add websockets explicitly to your dependencies.
Wrong language TTS pronunciation
Set rewrite_rules to voice.language.rewrite_rules. This enables language-specific text rewriting before synthesis.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/gradbot-voice-agent/SKILL.md
Popularity
- Repository stars
- 116
- Repository forks
- 23
- 7-day trend score
- 24
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/gradium-ai/gradbot/gradbot-voice-agent)