diff --git a/apps/server/src/services/llm/pipeline/stages/tool_calling_stage.ts b/apps/server/src/services/llm/pipeline/stages/tool_calling_stage.ts index e71edbdfb..b788179fe 100644 --- a/apps/server/src/services/llm/pipeline/stages/tool_calling_stage.ts +++ b/apps/server/src/services/llm/pipeline/stages/tool_calling_stage.ts @@ -23,6 +23,7 @@ interface ToolValidationResult { valid: boolean; tool: ToolInterface | null; error: string | null; + guidance?: string; // Guidance to help the LLM select better tools/parameters } /** @@ -70,7 +71,7 @@ export class ToolCallingStage extends BasePipelineStage { // Create a proper ToolInterface from the ToolHandler @@ -88,11 +89,11 @@ export class ToolCallingStage extends BasePipelineStage 0) { const availableToolNames = availableTools.map(t => { // Safely access the name property using type narrowing - if (t && typeof t === 'object' && 'definition' in t && - t.definition && typeof t.definition === 'object' && - 'function' in t.definition && t.definition.function && - typeof t.definition.function === 'object' && - 'name' in t.definition.function && + if (t && typeof t === 'object' && 'definition' in t && + t.definition && typeof t.definition === 'object' && + 'function' in t.definition && t.definition.function && + typeof t.definition.function === 'object' && + 'name' in t.definition.function && typeof t.definition.function.name === 'string') { return t.definition.function.name; } @@ -141,11 +142,14 @@ export class ToolCallingStage extends BasePipelineStage }, - error: errorMessage, + error: enhancedErrorMessage, // Include guidance in the error message type: 'error' as const }; @@ -354,6 +370,10 @@ export class ToolCallingStage extends BasePipelineStage this.isEmptyToolResult(msg.content, msg.name || '')) .map(msg => msg.name); - + let directiveMessage = `YOU MUST NOT GIVE UP AFTER A SINGLE EMPTY SEARCH RESULT. `; - + if (emptyToolNames.includes('search_notes') || emptyToolNames.includes('vector_search')) { directiveMessage += `IMMEDIATELY RUN ANOTHER SEARCH TOOL with broader search terms, alternative keywords, or related concepts. `; directiveMessage += `Try synonyms, more general terms, or related topics. `; } - + if (emptyToolNames.includes('keyword_search')) { directiveMessage += `IMMEDIATELY TRY VECTOR_SEARCH INSTEAD as it might find semantic matches where keyword search failed. `; } - + directiveMessage += `DO NOT ask the user what to do next or if they want general information. CONTINUE SEARCHING with different parameters.`; - + updatedMessages.push({ role: 'system', content: directiveMessage }); } - + log.info(`Total messages to return to pipeline: ${updatedMessages.length}`); log.info(`Last 3 messages in conversation:`); const lastMessages = updatedMessages.slice(-3); @@ -544,8 +564,9 @@ export class ToolCallingStage extends BasePipelineStage { + if (t && typeof t === 'object' && 'definition' in t && + t.definition && typeof t.definition === 'object' && + 'function' in t.definition && t.definition.function && + typeof t.definition.function === 'object' && + 'name' in t.definition.function && + typeof t.definition.function.name === 'string') { + return t.definition.function.name; + } + return ''; + }) + .filter(name => name !== ''); + + // Create specific guidance based on the error and tool + let guidance = `TOOL GUIDANCE: The tool '${toolName}' failed with error: ${errorMessage}.\n`; + + // Add suggestions based on the specific tool and error + if (toolName === 'attribute_search' && errorMessage.includes('Invalid attribute type')) { + guidance += "CORRECT USAGE: The 'attribute_search' tool requires a valid 'attributeType' parameter that must be either 'label' or 'relation'.\n"; + guidance += "EXAMPLE: { \"attributeType\": \"label\", \"attributeValue\": \"important\" }\n"; + } + else if (errorMessage.includes('Tool not found')) { + // Provide guidance on available search tools if a tool wasn't found + const searchTools = availableToolNames.filter(name => name.includes('search')); + guidance += `AVAILABLE SEARCH TOOLS: ${searchTools.join(', ')}\n`; + guidance += "TRY VECTOR SEARCH: For conceptual matches, use 'vector_search' with a query parameter.\n"; + guidance += "EXAMPLE: { \"query\": \"your search terms here\" }\n"; + } + else if (errorMessage.includes('missing required parameter')) { + // Provide parameter guidance based on the tool name + if (toolName === 'vector_search') { + guidance += "REQUIRED PARAMETERS: The 'vector_search' tool requires a 'query' parameter.\n"; + guidance += "EXAMPLE: { \"query\": \"your search terms here\" }\n"; + } else if (toolName === 'keyword_search') { + guidance += "REQUIRED PARAMETERS: The 'keyword_search' tool requires a 'query' parameter.\n"; + guidance += "EXAMPLE: { \"query\": \"your search terms here\" }\n"; + } + } + + // Add a general suggestion to try vector_search as a fallback + if (!toolName.includes('vector_search')) { + guidance += "RECOMMENDATION: If specific searches fail, try the 'vector_search' tool which performs semantic searches.\n"; + } + + return guidance; + } + /** * Determines if a tool result is effectively empty or unhelpful * @param result The result from the tool execution @@ -663,61 +741,61 @@ export class ToolCallingStage extends BasePipelineStage; - - if (toolName === 'search_notes' && - 'results' in resultObj && - Array.isArray(resultObj.results) && + + if (toolName === 'search_notes' && + 'results' in resultObj && + Array.isArray(resultObj.results) && resultObj.results.length === 0) { return true; } - - if (toolName === 'vector_search' && - 'matches' in resultObj && - Array.isArray(resultObj.matches) && + + if (toolName === 'vector_search' && + 'matches' in resultObj && + Array.isArray(resultObj.matches) && resultObj.matches.length === 0) { return true; } } } - + return false; }