Objective
When a user says "I can't log in," the agent has to pick the right tool —
reset_password, not cancel_subscription. Hard-coded keyword routers break the moment
someone phrases it differently, and full LLM function-calling is slow and costly for every
turn. Here you'll route by meaning: store each tool's description as an embedding, match the
user's request to the nearest tool with one query, and confirm with a confidence gate. No
framework, no rules engine. The same router works from any framework or a voice agent — see
Use it from your agent at the end.
Step 1: Create the tool registry
Each row is a tool the agent can call, described in natural language and embedded for matching.
CREATE TABLE IF NOT EXISTS recipe_agent_tools (
tool_id INTEGER PRIMARY KEY,
tool_name TEXT, -- the function the agent would invoke
description TEXT, -- what the tool does, in plain language
embedding VECTOR(384)
);
Step 2: Register the agent's tools
A realistic toolbox for a SaaS support agent.
INSERT INTO recipe_agent_tools (tool_id, tool_name, description) VALUES
(1,'reset_password','Reset or recover a user account password; help when a user cannot log in or is locked out.'),
(2,'cancel_subscription','Cancel a paid subscription or stop recurring billing for the account.'),
(3,'check_order_status','Look up the shipping status and delivery estimate of an existing order.'),
(4,'update_billing','Update the credit card or billing address on file for the account.'),
(5,'create_support_ticket','Open a support ticket for a problem that needs a human agent.'),
(6,'check_usage','Report the account''s current API usage and remaining quota for the month.');
Step 3: Embed the tool descriptions
The embedding model runs in-database; this is the index the router searches.
UPDATE recipe_agent_tools SET embedding = EMBED(description);
Step 4: Route a request to the best tool
Match the user's message to the nearest tool by meaning — no shared keywords required.
SELECT tool_name, description,
COSINE_SIMILARITY(embedding, EMBED('I keep getting locked out and can''t sign in')) AS match_score
FROM recipe_agent_tools
ORDER BY match_score DESC
LIMIT 3;
Step 5: Pick the single tool to call, with a confidence gate
Return the top tool only when it clears a confidence threshold — otherwise the agent should ask a clarifying question.
SELECT tool_name, match_score,
CASE WHEN match_score >= 0.35 THEN 'CALL' ELSE 'ASK_CLARIFY' END AS decision
FROM (
SELECT tool_name,
COSINE_SIMILARITY(embedding, EMBED('I want to stop being charged every month')) AS match_score
FROM recipe_agent_tools
ORDER BY match_score DESC
LIMIT 1
);
Step 6: Route an ambiguous request and let the gate catch it
A vague message scores low against every tool, so the gate routes to clarification instead of guessing.
SELECT tool_name, match_score,
CASE WHEN match_score >= 0.35 THEN 'CALL' ELSE 'ASK_CLARIFY' END AS decision
FROM (
SELECT tool_name,
COSINE_SIMILARITY(embedding, EMBED('hey, quick question about my thing')) AS match_score
FROM recipe_agent_tools
ORDER BY match_score DESC
LIMIT 1
);
Cleanup (Optional)
DROP TABLE IF EXISTS recipe_agent_tools;
Expected Outcomes
- Step 4 ranks
reset_passwordfirst for "locked out / can't sign in" — by meaning, with no keyword overlap. - Step 5 returns
cancel_subscriptionwith aCALLdecision for "stop being charged." - Step 6 returns
ASK_CLARIFYfor the vague message, because no tool clears the confidence gate.
You now have a semantic function-router: requests map to the right tool by meaning, with a built-in "ask first when unsure" guardrail — no framework and no keyword rules.
Use it from your agent (framework-agnostic — this is the whole point)
The router is just a tool table + one match query, so any agent uses it with no framework lock-in:
- REST / SDK —
POST /v1/query/execute(any language), or@synapcores/sdkclient.executeQuery(...). Before each action, your agent runs the Step-5 query, readstool_name+decision, and either invokes that function or asks a clarifying question. - MCP (native, on by default) — point any MCP client (Claude Code, Cursor, a custom loop, a voice runtime) at
ws://<your-instance>/mcp?token=<jwt>(JWT from onePOST /v1/auth/login→access_token). Thequerytool returns the routed tool name; your agent maps that to its real function call — semantic routing without a routing framework. - Any framework — OpenClaw, LangChain's tool/agent executors, a custom loop, or a voice agent all use the same registry to decide which tool to fire. The database is the brain; the framework is swappable.
Key Concepts Learned
- Embedding tool descriptions turns "which function?" into a nearest-neighbor search — robust to phrasing.
- A confidence threshold on the top match gives a free "ask before acting" guardrail.
- This is function-calling without a framework: the registry lives in SQL and any agent queries it.
- Because it's plain data ops (SQL / REST / MCP), semantic routing works for any agent — the agent-agnostic backend pattern this cluster builds on.