mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 13:01:31 +08:00 
			
		
		
		
	feat(llm): really try to coax ollama to run tools
This commit is contained in:
		
							parent
							
								
									87859aec1c
								
							
						
					
					
						commit
						8f8b9d9e3b
					
				| @ -213,7 +213,23 @@ Be concise and informative in your responses. | |||||||
| 
 | 
 | ||||||
| ${context} | ${context} | ||||||
| 
 | 
 | ||||||
| Based on this information, please answer: <query>${query}</query>` | Based on this information, please answer: <query>${query}</query>`,
 | ||||||
|  | 
 | ||||||
|  |         // Tool instructions for Ollama
 | ||||||
|  |         TOOL_INSTRUCTIONS: ` | ||||||
|  | CRITICAL INSTRUCTIONS FOR TOOL USAGE: | ||||||
|  | 1. YOU MUST TRY MULTIPLE TOOLS AND SEARCH VARIATIONS before concluding information isn't available | ||||||
|  | 2. ALWAYS PERFORM AT LEAST 3 DIFFERENT SEARCHES with different parameters before giving up on finding information | ||||||
|  | 3. If a search returns no results, IMMEDIATELY TRY ANOTHER SEARCH with different parameters: | ||||||
|  |    - Use broader terms: If "Kubernetes deployment" fails, try just "Kubernetes" or "container orchestration" | ||||||
|  |    - Try synonyms: If "meeting notes" fails, try "conference", "discussion", or "conversation" | ||||||
|  |    - Remove specific qualifiers: If "quarterly financial report 2024" fails, try just "financial report" | ||||||
|  |    - Try semantic variations: If keyword_search fails, use vector_search which finds conceptually related content | ||||||
|  | 4. CHAIN TOOLS TOGETHER: Use the results of one tool to inform parameters for the next tool | ||||||
|  | 5. NEVER respond with "there are no notes about X" until you've tried at least 3 different search variations | ||||||
|  | 6. DO NOT ask the user what to do next when searches fail - AUTOMATICALLY try different approaches | ||||||
|  | 7. ALWAYS EXPLAIN what you're doing: "I didn't find results for X, so I'm now searching for Y instead" | ||||||
|  | 8. If all reasonable search variations fail (minimum 3 attempts), THEN you may inform the user that the information might not be in their notes` | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     // Common prompts across providers
 |     // Common prompts across providers
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import type { Message } from '../ai_interface.js'; | import type { Message } from '../ai_interface.js'; | ||||||
| import { BaseMessageFormatter } from './base_formatter.js'; | import { BaseMessageFormatter } from './base_formatter.js'; | ||||||
| import sanitizeHtml from 'sanitize-html'; | import sanitizeHtml from 'sanitize-html'; | ||||||
| import { PROVIDER_PROMPTS, FORMATTING_PROMPTS } from '../constants/llm_prompt_constants.js'; | import { PROVIDER_PROMPTS } from '../constants/llm_prompt_constants.js'; | ||||||
| import { LLM_CONSTANTS } from '../constants/provider_constants.js'; | import { LLM_CONSTANTS } from '../constants/provider_constants.js'; | ||||||
| import { | import { | ||||||
|     HTML_ALLOWED_TAGS, |     HTML_ALLOWED_TAGS, | ||||||
| @ -29,7 +29,7 @@ export class OllamaMessageFormatter extends BaseMessageFormatter { | |||||||
|      * @param context Optional context to include |      * @param context Optional context to include | ||||||
|      * @param preserveSystemPrompt When true, preserves existing system messages rather than replacing them |      * @param preserveSystemPrompt When true, preserves existing system messages rather than replacing them | ||||||
|      */ |      */ | ||||||
|     formatMessages(messages: Message[], systemPrompt?: string, context?: string, preserveSystemPrompt?: boolean): Message[] { |     formatMessages(messages: Message[], systemPrompt?: string, context?: string, preserveSystemPrompt?: boolean, useTools?: boolean): Message[] { | ||||||
|         const formattedMessages: Message[] = []; |         const formattedMessages: Message[] = []; | ||||||
| 
 | 
 | ||||||
|         // Log the input messages with all their properties
 |         // Log the input messages with all their properties
 | ||||||
| @ -61,7 +61,19 @@ export class OllamaMessageFormatter extends BaseMessageFormatter { | |||||||
|             log.info(`Preserving existing system message: ${systemMessages[0].content.substring(0, 50)}...`); |             log.info(`Preserving existing system message: ${systemMessages[0].content.substring(0, 50)}...`); | ||||||
|         } else { |         } else { | ||||||
|             // Use provided systemPrompt or default
 |             // Use provided systemPrompt or default
 | ||||||
|             const basePrompt = systemPrompt || PROVIDER_PROMPTS.COMMON.DEFAULT_ASSISTANT_INTRO; |             let basePrompt = systemPrompt || PROVIDER_PROMPTS.COMMON.DEFAULT_ASSISTANT_INTRO; | ||||||
|  | 
 | ||||||
|  |             // Check if any message has tool_calls or if useTools flag is set, indicating this is a tool-using conversation
 | ||||||
|  |             const hasPreviousToolCalls = messages.some(msg => msg.tool_calls && msg.tool_calls.length > 0); | ||||||
|  |             const hasToolResults = messages.some(msg => msg.role === 'tool'); | ||||||
|  |             const isToolUsingConversation = useTools || hasPreviousToolCalls || hasToolResults; | ||||||
|  | 
 | ||||||
|  |             // Add tool instructions for Ollama when tools are being used
 | ||||||
|  |             if (isToolUsingConversation && PROVIDER_PROMPTS.OLLAMA.TOOL_INSTRUCTIONS) { | ||||||
|  |                 log.info('Adding tool instructions to system prompt for Ollama'); | ||||||
|  |                 basePrompt = `${basePrompt}\n\n${PROVIDER_PROMPTS.OLLAMA.TOOL_INSTRUCTIONS}`; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             formattedMessages.push({ |             formattedMessages.push({ | ||||||
|                 role: 'system', |                 role: 'system', | ||||||
|                 content: basePrompt |                 content: basePrompt | ||||||
| @ -151,13 +163,11 @@ export class OllamaMessageFormatter extends BaseMessageFormatter { | |||||||
|         if (!content) return ''; |         if (!content) return ''; | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             // Store our XML tags so we can restore them after cleaning
 |             // Define regexes for identifying and preserving tagged content
 | ||||||
|             const noteTagsRegex = /<\/?note>/g; |  | ||||||
|             const notesTagsRegex = /<\/?notes>/g; |             const notesTagsRegex = /<\/?notes>/g; | ||||||
|             const queryTagsRegex = /<\/?query>[^<]*<\/query>/g; |             // const queryTagsRegex = /<\/?query>/g; // Commenting out unused variable
 | ||||||
| 
 | 
 | ||||||
|             // Capture tags to restore later
 |             // Capture tags to restore later
 | ||||||
|             const noteTags = content.match(noteTagsRegex) || []; |  | ||||||
|             const noteTagPositions: number[] = []; |             const noteTagPositions: number[] = []; | ||||||
|             let match; |             let match; | ||||||
|             const regex = /<\/?note>/g; |             const regex = /<\/?note>/g; | ||||||
| @ -166,17 +176,15 @@ export class OllamaMessageFormatter extends BaseMessageFormatter { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Remember the notes tags
 |             // Remember the notes tags
 | ||||||
|             const notesTagsMatch = content.match(notesTagsRegex) || []; |  | ||||||
|             const notesTagPositions: number[] = []; |             const notesTagPositions: number[] = []; | ||||||
|             while ((match = notesTagsRegex.exec(content)) !== null) { |             while ((match = notesTagsRegex.exec(content)) !== null) { | ||||||
|                 notesTagPositions.push(match.index); |                 notesTagPositions.push(match.index); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Remember the query tags
 |             // Remember the query tag
 | ||||||
|             const queryTagsMatch = content.match(queryTagsRegex) || []; |  | ||||||
| 
 | 
 | ||||||
|             // Temporarily replace XML tags with markers that won't be affected by sanitization
 |             // Temporarily replace XML tags with markers that won't be affected by sanitization
 | ||||||
|             let modified = content |             const modified = content | ||||||
|                 .replace(/<note>/g, '[NOTE_START]') |                 .replace(/<note>/g, '[NOTE_START]') | ||||||
|                 .replace(/<\/note>/g, '[NOTE_END]') |                 .replace(/<\/note>/g, '[NOTE_END]') | ||||||
|                 .replace(/<notes>/g, '[NOTES_START]') |                 .replace(/<notes>/g, '[NOTES_START]') | ||||||
| @ -184,7 +192,7 @@ export class OllamaMessageFormatter extends BaseMessageFormatter { | |||||||
|                 .replace(/<query>(.*?)<\/query>/g, '[QUERY]$1[/QUERY]'); |                 .replace(/<query>(.*?)<\/query>/g, '[QUERY]$1[/QUERY]'); | ||||||
| 
 | 
 | ||||||
|             // First use the parent class to do standard cleaning
 |             // First use the parent class to do standard cleaning
 | ||||||
|             let sanitized = super.cleanContextContent(modified); |             const sanitized = super.cleanContextContent(modified); | ||||||
| 
 | 
 | ||||||
|             // Then apply Ollama-specific aggressive cleaning
 |             // Then apply Ollama-specific aggressive cleaning
 | ||||||
|             // Remove any remaining HTML using sanitizeHtml while keeping our markers
 |             // Remove any remaining HTML using sanitizeHtml while keeping our markers
 | ||||||
|  | |||||||
| @ -144,14 +144,19 @@ export class OllamaService extends BaseAIService { | |||||||
|                 messagesToSend = [...messages]; |                 messagesToSend = [...messages]; | ||||||
|                 log.info(`Bypassing formatter for Ollama request with ${messages.length} messages`); |                 log.info(`Bypassing formatter for Ollama request with ${messages.length} messages`); | ||||||
|             } else { |             } else { | ||||||
|  |                 // Determine if tools will be used in this request
 | ||||||
|  |                 const willUseTools = providerOptions.enableTools !== false; | ||||||
|  |                  | ||||||
|                 // Use the formatter to prepare messages
 |                 // Use the formatter to prepare messages
 | ||||||
|                 messagesToSend = this.formatter.formatMessages( |                 messagesToSend = this.formatter.formatMessages( | ||||||
|                     messages, |                     messages, | ||||||
|                     systemPrompt, |                     systemPrompt, | ||||||
|                     undefined, // context
 |                     undefined, // context
 | ||||||
|                     providerOptions.preserveSystemPrompt |                     providerOptions.preserveSystemPrompt, | ||||||
|  |                     willUseTools // Pass flag indicating if tools will be used
 | ||||||
|                 ); |                 ); | ||||||
|                 log.info(`Sending to Ollama with formatted messages: ${messagesToSend.length}`); |                  | ||||||
|  |                 log.info(`Sending to Ollama with formatted messages: ${messagesToSend.length}${willUseTools ? ' (with tool instructions)' : ''}`); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Get tools if enabled
 |             // Get tools if enabled
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user