Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit 87fd6af

Browse files
committed
feat(llm): try to improve tool and tool calling, part 2
1 parent dccd647 commit 87fd6af

File tree

7 files changed

+29
-241
lines changed

7 files changed

+29
-241
lines changed

apps/server/src/services/llm/pipeline/stages/tool_calling_stage.ts

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -490,43 +490,9 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
490490
let directiveMessage = '';
491491

492492
if (hasEmptyResults) {
493-
// Empty results - be very directive about trying alternatives
494-
const emptyToolNames = toolResultMessages
495-
.filter(msg => this.isEmptyToolResult(msg.content, msg.name || ''))
496-
.map(msg => msg.name);
497-
498-
directiveMessage = `CRITICAL INSTRUCTION: YOU MUST NOT STOP AFTER EMPTY RESULTS!\n\n`;
499-
directiveMessage += `REQUIRED ACTIONS:\n`;
500-
501-
if (emptyToolNames.includes('search_notes')) {
502-
directiveMessage += `1. IMMEDIATELY use keyword_search_notes with specific terms\n`;
503-
directiveMessage += `2. Try attribute_search if content might be tagged/categorized\n`;
504-
directiveMessage += `3. Use discover_tools to find alternative approaches\n`;
505-
}
506-
507-
if (emptyToolNames.includes('keyword_search_notes')) {
508-
directiveMessage += `1. IMMEDIATELY use search_notes for semantic matching\n`;
509-
directiveMessage += `2. Try broader or alternative keyword terms\n`;
510-
directiveMessage += `3. Use workflow_helper for guidance on next steps\n`;
511-
}
512-
513-
if (emptyToolNames.includes('attribute_search')) {
514-
directiveMessage += `1. Use search_notes to find content about the attribute topic\n`;
515-
directiveMessage += `2. Try different attribute names or types\n`;
516-
directiveMessage += `3. Use search_suggestion to see available attributes\n`;
517-
}
518-
519-
directiveMessage += `\nFORBIDDEN: Do NOT ask user for clarification or offer general information!\n`;
520-
directiveMessage += `REQUIRED: CONTINUE with alternative tools and approaches immediately!`;
493+
directiveMessage = `No results found. Try alternative search approaches: use different search tools, broader terms, or alternative keywords. Continue searching - don't ask the user for guidance.`;
521494
} else {
522-
// Has results - encourage follow-up actions
523-
directiveMessage = `EXCELLENT! You found results. Now CONTINUE the workflow:\n\n`;
524-
directiveMessage += `NEXT REQUIRED ACTIONS:\n`;
525-
directiveMessage += `1. Use read_note to examine the most relevant results\n`;
526-
directiveMessage += `2. Use workflow_helper to plan next steps based on your findings\n`;
527-
directiveMessage += `3. Consider using related tools for deeper analysis\n\n`;
528-
directiveMessage += `GOAL: Provide comprehensive information by using multiple tools in sequence.\n`;
529-
directiveMessage += `CONTINUE with tool usage - don't stop at just search results!`;
495+
directiveMessage = `You found results! Use read_note with the noteId values to get full content and continue your analysis.`;
530496
}
531497

532498
updatedMessages.push({
@@ -638,14 +604,8 @@ export class ToolCallingStage extends BasePipelineStage<ToolExecutionInput, { re
638604
guidance += "RECOMMENDATION: If specific searches fail, try the 'search_notes' tool which performs semantic searches.\n";
639605
}
640606

641-
// Always suggest helper tools for guidance
642-
guidance += "HELPER TOOLS AVAILABLE:\n";
643-
guidance += "• Use 'discover_tools' to find the right tool for your task\n";
644-
guidance += "• Use 'workflow_helper' to get guidance on next steps\n";
645-
guidance += "• Use 'search_suggestion' for search syntax help\n";
646-
647607
// Encourage continued tool usage
648-
guidance += "\nIMPORTANT: Don't stop after one failed tool - try alternatives immediately!";
608+
guidance += "\nTry alternative tools immediately. Use discover_tools if unsure which tool to use next.";
649609

650610
return guidance;
651611
}

apps/server/src/services/llm/tools/attribute_search_tool.ts

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,26 @@ export const attributeSearchToolDefinition: Tool = {
1919
type: 'function',
2020
function: {
2121
name: 'attribute_search',
22-
description: `ATTRIBUTE-BASED search for notes. Find notes by their labels or relations (metadata/tags).
23-
24-
BEST FOR: Finding notes by categories, tags, status, relationships, or other metadata
25-
USE WHEN: You need notes with specific labels, relations, or organizational attributes
26-
DIFFERENT FROM: search_notes (content) and keyword_search_notes (text)
27-
28-
CRITICAL: attributeType MUST be exactly "label" or "relation" (lowercase only!)
29-
30-
COMMON ATTRIBUTES:
31-
• Labels: #important, #todo, #project, #status, #priority
32-
• Relations: ~relatedTo, ~childOf, ~contains, ~references
33-
34-
NEXT STEPS: Use read_note with returned noteId values for full content`,
22+
description: 'Search notes by attributes (labels/relations). attributeType must be exactly "label" or "relation" (lowercase).',
3523
parameters: {
3624
type: 'object',
3725
properties: {
3826
attributeType: {
3927
type: 'string',
40-
description: `MUST be exactly "label" or "relation" (lowercase only!)
41-
42-
CORRECT: "label", "relation"
43-
WRONG: "Label", "LABEL", "labels", "relations"
44-
45-
• "label" = tags/categories like #important, #todo
46-
• "relation" = connections like ~relatedTo, ~childOf`,
28+
description: 'Must be exactly "label" or "relation" (lowercase only).',
4729
enum: ['label', 'relation']
4830
},
4931
attributeName: {
5032
type: 'string',
51-
description: `Name of the attribute to search for.
52-
53-
LABEL EXAMPLES:
54-
- "important" (finds notes with #important)
55-
- "status" (finds notes with #status label)
56-
- "project" (finds notes tagged #project)
57-
58-
RELATION EXAMPLES:
59-
- "relatedTo" (finds notes with ~relatedTo relation)
60-
- "childOf" (finds notes with ~childOf relation)
61-
- "contains" (finds notes with ~contains relation)`
33+
description: 'Name of the attribute (e.g., "important", "todo", "relatedTo").'
6234
},
6335
attributeValue: {
6436
type: 'string',
65-
description: `OPTIONAL: Specific value of the attribute.
66-
67-
• Leave empty to find ALL notes with this attribute
68-
• Specify value to find notes where attribute = specific value
69-
70-
EXAMPLES:
71-
- attributeName: "status", attributeValue: "completed"
72-
- attributeName: "priority", attributeValue: "high"`
37+
description: 'Optional value to match. Leave empty to find all notes with this attribute name.'
7338
},
7439
maxResults: {
7540
type: 'number',
76-
description: 'Number of results (1-50, default: 20). Use higher values for comprehensive searches.'
41+
description: 'Maximum number of results (default: 20).'
7742
}
7843
},
7944
required: ['attributeType', 'attributeName']
@@ -112,15 +77,7 @@ export class AttributeSearchTool implements ToolHandler {
11277
suggestions.push('CORRECT: Use "relation" for connections and relationships');
11378
}
11479

115-
const errorMessage = `Invalid attributeType: "${attributeType}"
116-
117-
REQUIRED: Must be exactly "label" or "relation" (lowercase only!)
118-
119-
${suggestions.length > 0 ? suggestions.join('\n') : ''}
120-
121-
EXAMPLES:
122-
• Find notes with #important tag: { "attributeType": "label", "attributeName": "important" }
123-
• Find notes with ~relatedTo relation: { "attributeType": "relation", "attributeName": "relatedTo" }`;
80+
const errorMessage = `Invalid attributeType: "${attributeType}". Must be exactly "label" or "relation" (lowercase). Example: {"attributeType": "label", "attributeName": "important"}`;
12481

12582
return errorMessage;
12683
}

apps/server/src/services/llm/tools/keyword_search_tool.ts

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,49 +17,21 @@ export const keywordSearchToolDefinition: Tool = {
1717
type: 'function',
1818
function: {
1919
name: 'keyword_search_notes',
20-
description: `EXACT KEYWORD search for notes. Finds notes containing specific words, phrases, or attribute filters.
21-
22-
BEST FOR: Finding notes with specific words/phrases you know exist
23-
USE WHEN: You need exact text matches, specific terms, or attribute-based filtering
24-
DIFFERENT FROM: search_notes (which finds conceptual/semantic matches)
25-
26-
SEARCH TYPES:
27-
• Simple: "machine learning" (finds notes containing both words)
28-
• Phrase: "\"exact phrase\"" (finds this exact phrase)
29-
• Attributes: "#label" or "~relation" (notes with specific labels/relations)
30-
• Complex: "AI #project ~relatedTo" (combines keywords with attributes)
31-
32-
NEXT STEPS: Use read_note with returned noteId values for full content`,
20+
description: 'Keyword search for exact text matches. Supports phrases in quotes, #labels, ~relations, and search operators like OR.',
3321
parameters: {
3422
type: 'object',
3523
properties: {
3624
query: {
3725
type: 'string',
38-
description: `Keyword search query using Trilium search syntax.
39-
40-
SIMPLE EXAMPLES:
41-
- "machine learning" (both words anywhere)
42-
- "\"project management\"" (exact phrase)
43-
- "python OR javascript" (either word)
44-
45-
ATTRIBUTE EXAMPLES:
46-
- "#important" (notes with 'important' label)
47-
- "~project" (notes with 'project' relation)
48-
- "#status = completed" (specific label value)
49-
50-
COMBINED EXAMPLES:
51-
- "AI #project #status = active" (AI content with project label and active status)
52-
- "note.title *= \"weekly\"" (titles containing 'weekly')
53-
54-
AVOID: Conceptual queries better suited for search_notes`
26+
description: 'Search query. Examples: "machine learning", "#important", "python OR javascript", "note.title *= weekly"'
5527
},
5628
maxResults: {
5729
type: 'number',
5830
description: 'Number of results (1-50, default: 10). Use higher values for comprehensive searches.'
5931
},
6032
includeArchived: {
6133
type: 'boolean',
62-
description: 'INCLUDE ARCHIVED: Search archived notes too (default: false). Use true for complete historical search.'
34+
description: 'Include archived notes in search (default: false).'
6335
}
6436
},
6537
required: ['query']
@@ -130,21 +102,7 @@ export class KeywordSearchTool implements ToolHandler {
130102
count: 0,
131103
results: [],
132104
query: query,
133-
searchType: 'keyword',
134-
message: 'No exact keyword matches found.',
135-
nextSteps: {
136-
immediate: [
137-
`Try search_notes for semantic/conceptual search: "${this.convertToSemanticQuery(query)}"`,
138-
`Use attribute_search if looking for specific labels or relations`,
139-
`Try simpler keywords or check spelling`
140-
],
141-
queryHelp: [
142-
'Remove quotes for broader matching',
143-
'Try individual words instead of phrases',
144-
'Use OR operator: "word1 OR word2"',
145-
'Check if content might be in archived notes (set includeArchived: true)'
146-
]
147-
}
105+
message: `No keyword matches. Try: search_notes with "${this.convertToSemanticQuery(query)}" or check spelling/try simpler terms.`
148106
};
149107
}
150108

@@ -153,12 +111,7 @@ export class KeywordSearchTool implements ToolHandler {
153111
totalFound: searchResults.length,
154112
query: query,
155113
searchType: 'keyword',
156-
message: 'Found exact keyword matches. Use noteId values with other tools.',
157-
nextSteps: {
158-
examine: `Use read_note with any noteId (e.g., "${limitedResults[0].noteId}") to get full content`,
159-
refine: limitedResults.length < searchResults.length ? `Found ${searchResults.length} total matches (showing ${limitedResults.length}). Increase maxResults for more.` : null,
160-
related: 'Use search_notes for conceptually related content beyond exact keywords'
161-
},
114+
message: `Found ${limitedResults.length} keyword matches. Use read_note with noteId for full content.`,
162115
results: limitedResults.map(note => {
163116
// Get a preview of the note content with highlighted search terms
164117
let contentPreview = '';

apps/server/src/services/llm/tools/read_note_tool.ts

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,37 +34,17 @@ export const readNoteToolDefinition: Tool = {
3434
type: 'function',
3535
function: {
3636
name: 'read_note',
37-
description: `READ FULL CONTENT of a specific note by its ID. Get complete note content and metadata.
38-
39-
BEST FOR: Getting complete content after finding notes through search tools
40-
USE WHEN: You have a noteId from search results and need the full content
41-
IMPORTANT: Must use noteId (like "abc123def456") from search results - NOT note titles
42-
43-
TIP: This is typically used after search_notes, keyword_search_notes, or attribute_search
44-
45-
NEXT STEPS: Use note_update or attribute_manager tools to modify the note if needed`,
37+
description: 'Read the full content of a note by its ID. Use noteId from search results, not note titles.',
4638
parameters: {
4739
type: 'object',
4840
properties: {
4941
noteId: {
5042
type: 'string',
51-
description: `SYSTEM ID of the note to read.
52-
53-
CRITICAL: Must be a noteId (like "abc123def456") - NOT a note title!
54-
55-
CORRECT: "abc123def456" (from search results)
56-
WRONG: "My Note Title" (this will fail)
57-
58-
WHERE TO GET: From noteId field in search tool results`
43+
description: 'The noteId of the note to read (e.g., "abc123def456"). Get this from search results, not note titles.'
5944
},
6045
includeAttributes: {
6146
type: 'boolean',
62-
description: `INCLUDE METADATA: Get note attributes (labels, relations) in response.
63-
64-
• true = Get full note with all attributes/metadata
65-
• false = Get just note content (default)
66-
67-
Use true when you need to see tags, labels, relations, or other metadata`
47+
description: 'Include note attributes/metadata in response (default: false).'
6848
}
6949
},
7050
required: ['noteId']

0 commit comments

Comments
 (0)