Dynamic tool selection for Ollama based on user intent
Instead of sending all 12 tools every request, match the user's message against keyword groups (status, workload, assign, create, advance, revision) and only send relevant tools. search_entities always included for name resolution. Falls back to basic query tools if no keywords match. This cuts the tool definitions from ~12 to ~2-6 per request, significantly reducing context size for gemma4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e99391b824
commit
697b015675
1 changed files with 61 additions and 22 deletions
|
|
@ -273,34 +273,65 @@ async function checkOllamaHealth(): Promise<boolean> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Tools to send to Ollama — a trimmed subset to keep context small.
|
||||
* Smaller models struggle with 17 tool definitions, so we send only
|
||||
* the most commonly used ones. Bulk tools are excluded (handled by
|
||||
* the RBAC layer + confirmation flow anyway).
|
||||
* Dynamic tool selection for Ollama — pick only the tools relevant to
|
||||
* the user's request so smaller models aren't overwhelmed by 20 definitions.
|
||||
* `search_entities` is always included (needed for name→ID resolution).
|
||||
*/
|
||||
const OLLAMA_TOOL_ALLOWLIST = new Set([
|
||||
"search_entities",
|
||||
"list_projects",
|
||||
"get_project",
|
||||
"list_deliverables",
|
||||
"list_users",
|
||||
"get_blocked_stages",
|
||||
"list_overdue",
|
||||
"get_workload",
|
||||
"assign_artist",
|
||||
"advance_stage",
|
||||
"create_project",
|
||||
"create_deliverable",
|
||||
]);
|
||||
const TOOL_GROUPS: Record<string, { keywords: RegExp; tools: string[] }> = {
|
||||
status: {
|
||||
keywords: /status|overview|how.?s|progress|update me|what.?s going|summary|blocked|overdue|late|behind|bottleneck/i,
|
||||
tools: ["list_projects", "get_project", "list_deliverables", "get_blocked_stages", "list_overdue"],
|
||||
},
|
||||
workload: {
|
||||
keywords: /workload|capacity|busy|bandwidth|availab|who.?s free|how many|assigned/i,
|
||||
tools: ["get_workload", "list_users", "get_available_artists", "get_suggested_artists"],
|
||||
},
|
||||
assign: {
|
||||
keywords: /assign|reassign|move .* to|put .* on|give .* to|allocat/i,
|
||||
tools: ["assign_artist", "remove_assignment", "get_available_artists", "get_suggested_artists", "list_users"],
|
||||
},
|
||||
create: {
|
||||
keywords: /create|new project|new deliverable|add.*project|add.*deliverable|set up|setup/i,
|
||||
tools: ["create_project", "create_deliverable"],
|
||||
},
|
||||
advance: {
|
||||
keywords: /advance|approve|move.*stage|next stage|complete.*stage|mark.*done|progress.*stage|skip/i,
|
||||
tools: ["advance_stage"],
|
||||
},
|
||||
revision: {
|
||||
keywords: /revision|review|feedback|note|comment|round/i,
|
||||
tools: ["create_revision", "list_revisions"],
|
||||
},
|
||||
};
|
||||
|
||||
function getOllamaTools(userMessage: string) {
|
||||
// Always include search_entities for name resolution
|
||||
const selected = new Set(["search_entities"]);
|
||||
|
||||
// Match user message against keyword groups
|
||||
let matched = false;
|
||||
for (const group of Object.values(TOOL_GROUPS)) {
|
||||
if (group.keywords.test(userMessage)) {
|
||||
matched = true;
|
||||
for (const tool of group.tools) selected.add(tool);
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing matched (generic question), include basic query tools
|
||||
if (!matched) {
|
||||
for (const t of ["list_projects", "get_project", "list_deliverables", "list_users"]) {
|
||||
selected.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[Ollama] Selected ${selected.size} tools for: "${userMessage.slice(0, 60)}…"`);
|
||||
|
||||
function getOllamaTools() {
|
||||
return TOOL_DEFINITIONS
|
||||
.filter((t) => OLLAMA_TOOL_ALLOWLIST.has(t.name))
|
||||
.filter((t) => selected.has(t.name))
|
||||
.map((t) => ({
|
||||
type: "function" as const,
|
||||
function: {
|
||||
name: t.name,
|
||||
// Shorten descriptions for smaller context
|
||||
description: t.description.split(".")[0] + ".",
|
||||
parameters: t.input_schema,
|
||||
},
|
||||
|
|
@ -388,10 +419,18 @@ async function chatWithOllama(
|
|||
ollamaMessages[0].content = sp.trim() + "\n\nUse bullet points, not tables. Be concise.";
|
||||
}
|
||||
|
||||
// Extract the last user message for dynamic tool selection
|
||||
const lastUserContent = [...messages].reverse().find((m) => m.role === "user");
|
||||
const userText = typeof lastUserContent?.content === "string"
|
||||
? lastUserContent.content
|
||||
: Array.isArray(lastUserContent?.content)
|
||||
? lastUserContent.content.map((b: any) => b.text || b.content || "").join(" ")
|
||||
: "";
|
||||
|
||||
const requestBody = JSON.stringify({
|
||||
model: getOllamaChatModel(),
|
||||
messages: ollamaMessages,
|
||||
tools: getOllamaTools(),
|
||||
tools: getOllamaTools(userText),
|
||||
stream: false,
|
||||
options: {
|
||||
temperature: 0.3,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue