mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-30 03:32:26 +08:00
feat(llm): add LLM guidance system for failed tool executions and validations
This commit is contained in:
parent
f04e56137b
commit
910c5039f4
@ -23,6 +23,7 @@ interface ToolValidationResult {
|
|||||||
valid: boolean;
|
valid: boolean;
|
||||||
tool: ToolInterface | null;
|
tool: ToolInterface | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
guidance?: string; // Guidance to help the LLM select better tools/parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,11 +142,14 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
|
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
log.error(`Tool not found in registry: ${toolCall.function.name}`);
|
log.error(`Tool not found in registry: ${toolCall.function.name}`);
|
||||||
|
// Generate guidance for the LLM when a tool is not found
|
||||||
|
const guidance = this.generateToolGuidance(toolCall.function.name, `Tool not found: ${toolCall.function.name}`);
|
||||||
return {
|
return {
|
||||||
toolCall,
|
toolCall,
|
||||||
valid: false,
|
valid: false,
|
||||||
tool: null,
|
tool: null,
|
||||||
error: `Tool not found: ${toolCall.function.name}`
|
error: `Tool not found: ${toolCall.function.name}`,
|
||||||
|
guidance // Add guidance for the LLM
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +191,15 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
: JSON.stringify(toolCall.function.arguments);
|
: JSON.stringify(toolCall.function.arguments);
|
||||||
log.info(`Tool parameters: ${argsStr}`);
|
log.info(`Tool parameters: ${argsStr}`);
|
||||||
|
|
||||||
// If validation failed, throw the error
|
// If validation failed, generate guidance and throw the error
|
||||||
if (!valid || !tool) {
|
if (!valid || !tool) {
|
||||||
throw new Error(error || `Unknown validation error for tool '${toolCall.function.name}'`);
|
// If we already have guidance from validation, use it, otherwise generate it
|
||||||
|
const toolGuidance = validation.guidance ||
|
||||||
|
this.generateToolGuidance(toolCall.function.name,
|
||||||
|
error || `Unknown validation error for tool '${toolCall.function.name}'`);
|
||||||
|
|
||||||
|
// Include the guidance in the error message
|
||||||
|
throw new Error(`${error || `Unknown validation error for tool '${toolCall.function.name}'`}\n${toolGuidance}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Tool validated successfully: ${toolCall.function.name}`);
|
log.info(`Tool validated successfully: ${toolCall.function.name}`);
|
||||||
@ -315,6 +325,12 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
const errorMessage = execError instanceof Error ? execError.message : String(execError);
|
const errorMessage = execError instanceof Error ? execError.message : String(execError);
|
||||||
log.error(`================ TOOL EXECUTION FAILED in ${executionTime}ms: ${errorMessage} ================`);
|
log.error(`================ TOOL EXECUTION FAILED in ${executionTime}ms: ${errorMessage} ================`);
|
||||||
|
|
||||||
|
// Generate guidance for the failed tool execution
|
||||||
|
const toolGuidance = this.generateToolGuidance(toolCall.function.name, errorMessage);
|
||||||
|
|
||||||
|
// Add the guidance to the error message for the LLM
|
||||||
|
const enhancedErrorMessage = `${errorMessage}\n${toolGuidance}`;
|
||||||
|
|
||||||
// Record this failed tool execution if there's a sessionId available
|
// Record this failed tool execution if there's a sessionId available
|
||||||
if (input.options?.sessionId) {
|
if (input.options?.sessionId) {
|
||||||
try {
|
try {
|
||||||
@ -324,7 +340,7 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
toolCall.id || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
|
toolCall.id || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
|
||||||
args,
|
args,
|
||||||
"", // No result for failed execution
|
"", // No result for failed execution
|
||||||
errorMessage
|
enhancedErrorMessage // Use enhanced error message with guidance
|
||||||
);
|
);
|
||||||
} catch (storageError) {
|
} catch (storageError) {
|
||||||
log.error(`Failed to record tool execution error in chat storage: ${storageError}`);
|
log.error(`Failed to record tool execution error in chat storage: ${storageError}`);
|
||||||
@ -339,7 +355,7 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
name: toolCall.function.name,
|
name: toolCall.function.name,
|
||||||
arguments: {} as Record<string, unknown>
|
arguments: {} as Record<string, unknown>
|
||||||
},
|
},
|
||||||
error: errorMessage,
|
error: enhancedErrorMessage, // Include guidance in the error message
|
||||||
type: 'error' as const
|
type: 'error' as const
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -354,6 +370,10 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modify the error to include our guidance
|
||||||
|
if (execError instanceof Error) {
|
||||||
|
execError.message = enhancedErrorMessage;
|
||||||
|
}
|
||||||
throw execError;
|
throw execError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,8 +564,9 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
// Force initialization to ensure it runs even if previously marked as initialized
|
// Force initialization to ensure it runs even if previously marked as initialized
|
||||||
await agentTools.initialize(true);
|
await agentTools.initialize(true);
|
||||||
log.info('Agent tools initialized successfully');
|
log.info('Agent tools initialized successfully');
|
||||||
} catch (initError: any) {
|
} catch (initError: unknown) {
|
||||||
log.error(`Failed to initialize agent tools: ${initError.message}`);
|
const errorMessage = initError instanceof Error ? initError.message : String(initError);
|
||||||
|
log.error(`Failed to initialize agent tools: ${errorMessage}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -629,6 +650,7 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the vectorSearchTool has the required methods
|
||||||
if (!vectorSearchTool.searchNotes || typeof vectorSearchTool.searchNotes !== 'function') {
|
if (!vectorSearchTool.searchNotes || typeof vectorSearchTool.searchNotes !== 'function') {
|
||||||
log.error(`Tool '${toolName}' dependency vectorSearchTool is missing searchNotes method`);
|
log.error(`Tool '${toolName}' dependency vectorSearchTool is missing searchNotes method`);
|
||||||
return false;
|
return false;
|
||||||
@ -641,7 +663,6 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add additional tool-specific validations here
|
// Add additional tool-specific validations here
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
@ -650,6 +671,63 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate guidance for the LLM when a tool fails or is not found
|
||||||
|
* @param toolName The name of the tool that failed
|
||||||
|
* @param errorMessage The error message from the failed tool
|
||||||
|
* @returns A guidance message for the LLM with suggestions of what to try next
|
||||||
|
*/
|
||||||
|
private generateToolGuidance(toolName: string, errorMessage: string): string {
|
||||||
|
// Get all available tool names for recommendations
|
||||||
|
const availableTools = toolRegistry.getAllTools();
|
||||||
|
const availableToolNames = availableTools
|
||||||
|
.map(t => {
|
||||||
|
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
|
* Determines if a tool result is effectively empty or unhelpful
|
||||||
* @param result The result from the tool execution
|
* @param result The result from the tool execution
|
||||||
|
Loading…
x
Reference in New Issue
Block a user