From 2f303b1ae9baa674703fb7a07dbdef35e10d8252 Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 29 May 2025 21:58:03 +0000 Subject: [PATCH] feat(llm): also update OpenAI tool usage prompts --- .../llm/constants/llm_prompt_constants.ts | 32 ++++++++++++++++++- .../llm/formatters/openai_formatter.ts | 25 +++++++++++++-- .../services/llm/providers/openai_service.ts | 25 +++++++++++---- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/apps/server/src/services/llm/constants/llm_prompt_constants.ts b/apps/server/src/services/llm/constants/llm_prompt_constants.ts index 63dc72987..6a061aea9 100644 --- a/apps/server/src/services/llm/constants/llm_prompt_constants.ts +++ b/apps/server/src/services/llm/constants/llm_prompt_constants.ts @@ -184,6 +184,22 @@ When responding: INSTRUCTIONS_WRAPPER: (instructions: string) => `\n${instructions}\n`, + + // Tool instructions for Anthropic Claude + TOOL_INSTRUCTIONS: ` +When using tools to search for information, follow these requirements: + +1. ALWAYS TRY MULTIPLE SEARCH APPROACHES before concluding information isn't available +2. YOU MUST PERFORM AT LEAST 3 DIFFERENT SEARCHES with varied parameters before giving up +3. If a search returns no results: + - Try broader terms (e.g., "Kubernetes" instead of "Kubernetes deployment") + - Use synonyms (e.g., "meeting" instead of "conference") + - Remove specific qualifiers (e.g., "report" instead of "Q3 financial report") + - Try different search tools (vector_search for conceptual matches, keyword_search for exact matches) +4. NEVER tell the user "there are no notes about X" until you've tried multiple search variations +5. EXPLAIN your search strategy when adjusting parameters (e.g., "I'll try a broader search for...") +6. When searches fail, AUTOMATICALLY try different approaches rather than asking the user what to do +`, ACKNOWLEDGMENT: "I understand. I'll follow those instructions.", CONTEXT_ACKNOWLEDGMENT: "I'll help you with your notes based on the context provided.", @@ -203,7 +219,21 @@ ${context} Focus on relevant information from these notes when answering. Be concise and informative in your responses. -` +`, + + // Tool instructions for OpenAI models + TOOL_INSTRUCTIONS: `When using tools to search for information, you must follow these requirements: + +1. ALWAYS TRY MULTIPLE SEARCH APPROACHES before concluding information isn't available +2. YOU MUST PERFORM AT LEAST 3 DIFFERENT SEARCHES with varied parameters before giving up +3. If a search returns no results: + - Try broader terms (e.g., "Kubernetes" instead of "Kubernetes deployment") + - Use synonyms (e.g., "meeting" instead of "conference") + - Remove specific qualifiers (e.g., "report" instead of "Q3 financial report") + - Try different search tools (vector_search for conceptual matches, keyword_search for exact matches) +4. NEVER tell the user "there are no notes about X" until you've tried multiple search variations +5. EXPLAIN your search strategy when adjusting parameters (e.g., "I'll try a broader search for...") +6. When searches fail, AUTOMATICALLY try different approaches rather than asking the user what to do` }, OLLAMA: { diff --git a/apps/server/src/services/llm/formatters/openai_formatter.ts b/apps/server/src/services/llm/formatters/openai_formatter.ts index 301edd3a7..d09a3675a 100644 --- a/apps/server/src/services/llm/formatters/openai_formatter.ts +++ b/apps/server/src/services/llm/formatters/openai_formatter.ts @@ -1,7 +1,7 @@ import sanitizeHtml from 'sanitize-html'; import type { Message } from '../ai_interface.js'; import { BaseMessageFormatter } from './base_formatter.js'; -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 { HTML_ALLOWED_TAGS, @@ -10,6 +10,7 @@ import { HTML_ENTITY_REPLACEMENTS, FORMATTER_LOGS } from '../constants/formatter_constants.js'; +import log from '../../log.js'; /** * OpenAI-specific message formatter @@ -24,8 +25,13 @@ export class OpenAIMessageFormatter extends BaseMessageFormatter { /** * Format messages for the OpenAI API + * @param messages The messages to format + * @param systemPrompt Optional system prompt to use + * @param context Optional context to include + * @param preserveSystemPrompt When true, preserves existing system messages + * @param useTools Flag indicating if tools will be used in this request */ - formatMessages(messages: Message[], systemPrompt?: string, context?: string): Message[] { + formatMessages(messages: Message[], systemPrompt?: string, context?: string, preserveSystemPrompt?: boolean, useTools?: boolean): Message[] { const formattedMessages: Message[] = []; // Check if we already have a system message @@ -47,9 +53,22 @@ export class OpenAIMessageFormatter extends BaseMessageFormatter { } // If we don't have explicit context but have a system prompt else if (!hasSystemMessage && systemPrompt) { + let baseSystemPrompt = systemPrompt || PROVIDER_PROMPTS.COMMON.DEFAULT_ASSISTANT_INTRO; + + // Check if 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 OpenAI when tools are being used + if (isToolUsingConversation && PROVIDER_PROMPTS.OPENAI.TOOL_INSTRUCTIONS) { + log.info('Adding tool instructions to system prompt for OpenAI'); + baseSystemPrompt = `${baseSystemPrompt}\n\n${PROVIDER_PROMPTS.OPENAI.TOOL_INSTRUCTIONS}`; + } + formattedMessages.push({ role: 'system', - content: systemPrompt + content: baseSystemPrompt }); } // If neither context nor system prompt is provided, use default system prompt diff --git a/apps/server/src/services/llm/providers/openai_service.ts b/apps/server/src/services/llm/providers/openai_service.ts index a9a833163..e0633ca1f 100644 --- a/apps/server/src/services/llm/providers/openai_service.ts +++ b/apps/server/src/services/llm/providers/openai_service.ts @@ -3,6 +3,8 @@ import { BaseAIService } from '../base_ai_service.js'; import type { ChatCompletionOptions, ChatResponse, Message, StreamChunk } from '../ai_interface.js'; import { getOpenAIOptions } from './providers.js'; import OpenAI from 'openai'; +import { PROVIDER_PROMPTS } from '../constants/llm_prompt_constants.js'; +import log from '../../log.js'; export class OpenAIService extends BaseAIService { private openai: OpenAI | null = null; @@ -36,7 +38,17 @@ export class OpenAIService extends BaseAIService { // Initialize the OpenAI client const client = this.getClient(providerOptions.apiKey, providerOptions.baseUrl); - const systemPrompt = this.getSystemPrompt(providerOptions.systemPrompt || options.getOption('aiSystemPrompt')); + // Get base system prompt + let systemPrompt = this.getSystemPrompt(providerOptions.systemPrompt || options.getOption('aiSystemPrompt')); + + // Check if tools are enabled for this request + const willUseTools = providerOptions.enableTools && providerOptions.tools && providerOptions.tools.length > 0; + + // Add tool instructions to system prompt if tools are enabled + if (willUseTools && PROVIDER_PROMPTS.OPENAI.TOOL_INSTRUCTIONS) { + log.info('Adding tool instructions to system prompt for OpenAI'); + systemPrompt = `${systemPrompt}\n\n${PROVIDER_PROMPTS.OPENAI.TOOL_INSTRUCTIONS}`; + } // Ensure we have a system message const systemMessageExists = messages.some(m => m.role === 'system'); @@ -67,7 +79,7 @@ export class OpenAIService extends BaseAIService { } // Log the request parameters - console.log('OpenAI API Request:', JSON.stringify({ + log.info(`OpenAI API Request: ${JSON.stringify({ endpoint: 'chat.completions.create', model: params.model, messages: params.messages, @@ -76,7 +88,7 @@ export class OpenAIService extends BaseAIService { stream: params.stream, tools: params.tools, tool_choice: params.tool_choice - }, null, 2)); + }, null, 2)}`); // If streaming is requested if (providerOptions.stream) { @@ -84,10 +96,10 @@ export class OpenAIService extends BaseAIService { // Get stream from OpenAI SDK const stream = await client.chat.completions.create(params); - console.log('OpenAI API Stream Started'); + log.info('OpenAI API Stream Started'); // Create a closure to hold accumulated tool calls - let accumulatedToolCalls: any[] = []; + const accumulatedToolCalls: OpenAI.Chat.ChatCompletionMessageToolCall[] = []; // Return a response with the stream handler const response: ChatResponse = { @@ -104,7 +116,8 @@ export class OpenAIService extends BaseAIService { if (Symbol.asyncIterator in stream) { for await (const chunk of stream as AsyncIterable) { // Log each chunk received from OpenAI - console.log('OpenAI API Stream Chunk:', JSON.stringify(chunk, null, 2)); + // Use info level as debug is not available + log.info(`OpenAI API Stream Chunk: ${JSON.stringify(chunk, null, 2)}`); const content = chunk.choices[0]?.delta?.content || ''; const isDone = !!chunk.choices[0]?.finish_reason;