mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 19:12:27 +08:00
try using XML tags in sending to LLM, so it can more easily pick out information
This commit is contained in:
parent
6e8ab373d8
commit
caada309ec
@ -631,8 +631,8 @@ function buildContextFromNotes(sources: NoteSource[], query: string): string {
|
||||
const noteContexts = sources
|
||||
.filter(source => source.content) // Only include sources with content
|
||||
.map((source) => {
|
||||
// Format each note with its title as a natural heading
|
||||
return `### ${source.title}\n${source.content || 'No content available'}`;
|
||||
// Format each note with its title as a natural heading and wrap in <note> tags
|
||||
return `<note>\n### ${source.title}\n${source.content || 'No content available'}\n</note>`;
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
|
@ -71,11 +71,13 @@ Example: ["exact topic mentioned", "related concept 1", "related concept 2"]`,
|
||||
CONTEXT_NOTES_WRAPPER:
|
||||
`I'll provide you with relevant information from my notes to help answer your question.
|
||||
|
||||
<notes>
|
||||
{noteContexts}
|
||||
</notes>
|
||||
|
||||
When referring to information from these notes in your response, please cite them by their titles (e.g., "According to your note on [Title]...") rather than using labels like "Note 1" or "Note 2".
|
||||
|
||||
Now, based on the above information, please answer: {query}`,
|
||||
Now, based on the above information, please answer: <query>{query}</query>`,
|
||||
|
||||
// Default fallback when no notes are found
|
||||
NO_NOTES_CONTEXT:
|
||||
@ -90,17 +92,17 @@ Now, based on the above information, please answer: {query}`,
|
||||
// Headers for context (by provider)
|
||||
CONTEXT_HEADERS: {
|
||||
ANTHROPIC: (query: string) =>
|
||||
`I'm your AI assistant helping with your Trilium notes database. For your query: "${query}", I found these relevant notes:\n\n`,
|
||||
`I'm your AI assistant helping with your Trilium notes database. For your query: "<query>${query}</query>", I found these relevant <notes>`,
|
||||
DEFAULT: (query: string) =>
|
||||
`I've found some relevant information in your notes that may help answer: "${query}"\n\n`
|
||||
`I've found some relevant information in your notes that may help answer: "<query>${query}</query>"\n\n<notes>`
|
||||
},
|
||||
|
||||
// Closings for context (by provider)
|
||||
CONTEXT_CLOSINGS: {
|
||||
ANTHROPIC:
|
||||
"\n\nPlease use this information to answer the user's query. If the notes don't contain enough information, you can use your general knowledge as well.",
|
||||
"</notes>\n\nPlease use this information to answer the user's query. If the notes don't contain enough information, you can use your general knowledge as well.",
|
||||
DEFAULT:
|
||||
"\n\nBased on this information from the user's notes, please provide a helpful response."
|
||||
"</notes>\n\nBased on this information from the user's notes, please provide a helpful response."
|
||||
},
|
||||
|
||||
// Context for index service
|
||||
@ -110,21 +112,27 @@ Now, based on the above information, please answer: {query}`,
|
||||
// Prompt for adding note context to chat
|
||||
NOTE_CONTEXT_PROMPT: `Here is the content of the note I want to discuss:
|
||||
|
||||
<note_content>
|
||||
{context}
|
||||
</note_content>
|
||||
|
||||
Please help me with this information.`,
|
||||
|
||||
// Prompt for adding semantic note context to chat
|
||||
SEMANTIC_NOTE_CONTEXT_PROMPT: `Here is the relevant information from my notes based on my query "{query}":
|
||||
SEMANTIC_NOTE_CONTEXT_PROMPT: `Here is the relevant information from my notes based on my query "<query>{query}</query>":
|
||||
|
||||
<notes_context>
|
||||
{context}
|
||||
</notes_context>
|
||||
|
||||
Please help me understand this information in relation to my query.`,
|
||||
|
||||
// System message prompt for context-aware chat
|
||||
CONTEXT_AWARE_SYSTEM_PROMPT: `You are an AI assistant helping with Trilium Notes. Use this context to answer the user's question:
|
||||
|
||||
{enhancedContext}`,
|
||||
<enhanced_context>
|
||||
{enhancedContext}
|
||||
</enhanced_context>`,
|
||||
|
||||
// Error messages
|
||||
ERROR_MESSAGES: {
|
||||
@ -134,25 +142,25 @@ Please help me understand this information in relation to my query.`,
|
||||
|
||||
// Merged from JS file
|
||||
AGENT_TOOLS_CONTEXT_PROMPT:
|
||||
"You have access to the following tools to help answer the user's question: {tools}"
|
||||
"You have access to the following tools to help answer the user's question: <tools>{tools}</tools>"
|
||||
};
|
||||
|
||||
// Agent tool prompts
|
||||
export const AGENT_TOOL_PROMPTS = {
|
||||
// Prompts for query decomposition
|
||||
QUERY_DECOMPOSITION: {
|
||||
SUB_QUERY_DIRECT: 'Direct question that can be answered without decomposition',
|
||||
SUB_QUERY_GENERIC: 'Generic exploration to find related content',
|
||||
SUB_QUERY_ERROR: 'Error in decomposition, treating as simple query',
|
||||
SUB_QUERY_DIRECT_ANALYSIS: 'Direct analysis of note details',
|
||||
ORIGINAL_QUERY: 'Original query'
|
||||
SUB_QUERY_DIRECT: '<query_type>Direct question that can be answered without decomposition</query_type>',
|
||||
SUB_QUERY_GENERIC: '<query_type>Generic exploration to find related content</query_type>',
|
||||
SUB_QUERY_ERROR: '<query_type>Error in decomposition, treating as simple query</query_type>',
|
||||
SUB_QUERY_DIRECT_ANALYSIS: '<query_type>Direct analysis of note details</query_type>',
|
||||
ORIGINAL_QUERY: '<query_type>Original query</query_type>'
|
||||
},
|
||||
|
||||
// Prompts for contextual thinking tool
|
||||
CONTEXTUAL_THINKING: {
|
||||
STARTING_ANALYSIS: (query: string) => `Starting analysis of the query: "${query}"`,
|
||||
KEY_COMPONENTS: 'What are the key components of this query that need to be addressed?',
|
||||
BREAKING_DOWN: 'Breaking down the query to understand its requirements and context.'
|
||||
STARTING_ANALYSIS: (query: string) => `Starting analysis of the query: "<query>${query}</query>"`,
|
||||
KEY_COMPONENTS: '<analysis>What are the key components of this query that need to be addressed?</analysis>',
|
||||
BREAKING_DOWN: '<analysis>Breaking down the query to understand its requirements and context.</analysis>'
|
||||
}
|
||||
};
|
||||
|
||||
@ -188,13 +196,17 @@ When responding:
|
||||
OPENAI: {
|
||||
// OpenAI-specific prompt formatting
|
||||
SYSTEM_WITH_CONTEXT: (context: string) =>
|
||||
`You are an AI assistant integrated into TriliumNext Notes.
|
||||
`<system_prompt>
|
||||
You are an AI assistant integrated into TriliumNext Notes.
|
||||
Use the following information from the user's notes to answer their questions:
|
||||
|
||||
<user_notes>
|
||||
${context}
|
||||
</user_notes>
|
||||
|
||||
Focus on relevant information from these notes when answering.
|
||||
Be concise and informative in your responses.`
|
||||
Be concise and informative in your responses.
|
||||
</system_prompt>`
|
||||
},
|
||||
|
||||
OLLAMA: {
|
||||
@ -204,12 +216,12 @@ Be concise and informative in your responses.`
|
||||
|
||||
${context}
|
||||
|
||||
Based on this information, please answer: ${query}`
|
||||
Based on this information, please answer: <query>${query}</query>`
|
||||
},
|
||||
|
||||
// Common prompts across providers
|
||||
COMMON: {
|
||||
DEFAULT_ASSISTANT_INTRO: "You are an AI assistant integrated into TriliumNext Notes. Focus on helping users find information in their notes and answering questions based on their knowledge base. Be concise, informative, and direct when responding to queries."
|
||||
DEFAULT_ASSISTANT_INTRO: "<assistant_role>You are an AI assistant integrated into TriliumNext Notes. Focus on helping users find information in their notes and answering questions based on their knowledge base. Be concise, informative, and direct when responding to queries.</assistant_role>"
|
||||
}
|
||||
};
|
||||
|
||||
@ -217,14 +229,14 @@ Based on this information, please answer: ${query}`
|
||||
export const FORMATTING_PROMPTS = {
|
||||
// Headers for context formatting
|
||||
CONTEXT_HEADERS: {
|
||||
SIMPLE: (query: string) => `I'm searching for information about: ${query}\n\nHere are the most relevant notes from my knowledge base:`,
|
||||
DETAILED: (query: string) => `I'm searching for information about: "${query}"\n\nHere are the most relevant notes from my personal knowledge base:`
|
||||
SIMPLE: (query: string) => `I'm searching for information about: <query>${query}</query>\n\n<notes>Here are the most relevant notes from my knowledge base:`,
|
||||
DETAILED: (query: string) => `I'm searching for information about: "<query>${query}</query>"\n\n<notes>Here are the most relevant notes from my personal knowledge base:`
|
||||
},
|
||||
|
||||
// Closing text for context formatting
|
||||
CONTEXT_CLOSERS: {
|
||||
SIMPLE: `End of notes. Please use this information to answer my question comprehensively.`,
|
||||
DETAILED: `End of context information. Please use only the above notes to answer my question as comprehensively as possible.`
|
||||
SIMPLE: `</notes>\nEnd of notes. Please use this information to answer my question comprehensively.`,
|
||||
DETAILED: `</notes>\nEnd of context information. Please use only the above notes to answer my question as comprehensively as possible.`
|
||||
},
|
||||
|
||||
// Dividers used in context formatting
|
||||
@ -242,14 +254,14 @@ export const FORMATTING_PROMPTS = {
|
||||
export const CHAT_PROMPTS = {
|
||||
// Introduction messages for new chats
|
||||
INTRODUCTIONS: {
|
||||
NEW_CHAT: "Welcome to TriliumNext AI Assistant. How can I help you with your notes today?",
|
||||
SEMANTIC_SEARCH: "I'll search through your notes for relevant information. What would you like to know?"
|
||||
NEW_CHAT: "<greeting>Welcome to TriliumNext AI Assistant. How can I help you with your notes today?</greeting>",
|
||||
SEMANTIC_SEARCH: "<instruction>I'll search through your notes for relevant information. What would you like to know?</instruction>"
|
||||
},
|
||||
|
||||
// Placeholders for various chat scenarios
|
||||
PLACEHOLDERS: {
|
||||
NO_CONTEXT: "I don't have any specific note context yet. Would you like me to search your notes for something specific?",
|
||||
WAITING_FOR_QUERY: "Awaiting your question..."
|
||||
NO_CONTEXT: "<status>I don't have any specific note context yet. Would you like me to search your notes for something specific?</status>",
|
||||
WAITING_FOR_QUERY: "<prompt>Awaiting your question...</prompt>"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -131,9 +131,9 @@ export class ContextFormatter implements IContextFormatter {
|
||||
let formattedSource = '';
|
||||
if (providerId === 'ollama') {
|
||||
// For Ollama, use a simpler format and plain ASCII
|
||||
formattedSource = `${FORMATTING_PROMPTS.DIVIDERS.NOTE_START}${title}\n${content}\n\n`;
|
||||
formattedSource = `<note>\n${FORMATTING_PROMPTS.DIVIDERS.NOTE_START}${title}\n${content}\n</note>\n\n`;
|
||||
} else {
|
||||
formattedSource = `### ${title}\n${content}\n\n`;
|
||||
formattedSource = `<note>\n### ${title}\n${content}\n</note>\n\n`;
|
||||
}
|
||||
|
||||
// Check if adding this would exceed our size limit
|
||||
@ -144,8 +144,8 @@ export class ContextFormatter implements IContextFormatter {
|
||||
const availableSpace = maxTotalLength - totalSize - 100; // Buffer for closing text
|
||||
if (availableSpace > 200) { // Only if we have reasonable space
|
||||
const truncatedContent = providerId === 'ollama' ?
|
||||
`## ${title}\n${content.substring(0, availableSpace)}...\n\n` :
|
||||
`### ${title}\n${content.substring(0, availableSpace)}...\n\n`;
|
||||
`<note>\n## ${title}\n${content.substring(0, availableSpace)}...\n</note>\n\n` :
|
||||
`<note>\n### ${title}\n${content.substring(0, availableSpace)}...\n</note>\n\n`;
|
||||
formattedSources.push(truncatedContent);
|
||||
totalSize += truncatedContent.length;
|
||||
sourcesIncluded++;
|
||||
@ -185,6 +185,10 @@ export class ContextFormatter implements IContextFormatter {
|
||||
// Log final context size
|
||||
log.info(`Final context: ${formattedContext.length} chars, ${formattedSources.length} sources included`);
|
||||
|
||||
// DEBUG: Log a sample of the formatted context to verify <note> tags are present
|
||||
log.info(`Context sample (first 500 chars): ${formattedContext.substring(0, 500).replace(/\n/g, '\\n')}`);
|
||||
log.info(`Context sample (last 500 chars): ${formattedContext.substring(Math.max(0, formattedContext.length - 500)).replace(/\n/g, '\\n')}`);
|
||||
|
||||
return formattedContext;
|
||||
} catch (error) {
|
||||
log.error(`Error building context from notes: ${error}`);
|
||||
|
@ -58,11 +58,19 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
|
||||
if (msg.role === 'user' && !injectedContext) {
|
||||
// Simple context injection directly in the user's message
|
||||
const cleanedContext = this.cleanContextContent(context);
|
||||
|
||||
// DEBUG: Log the context before and after cleaning
|
||||
console.log(`[OllamaFormatter] Context (first 500 chars): ${context.substring(0, 500).replace(/\n/g, '\\n')}...`);
|
||||
console.log(`[OllamaFormatter] Cleaned context (first 500 chars): ${cleanedContext.substring(0, 500).replace(/\n/g, '\\n')}...`);
|
||||
|
||||
const formattedContext = PROVIDER_PROMPTS.OLLAMA.CONTEXT_INJECTION(
|
||||
cleanedContext,
|
||||
msg.content
|
||||
);
|
||||
|
||||
// DEBUG: Log the final formatted context
|
||||
console.log(`[OllamaFormatter] Formatted context (first 500 chars): ${formattedContext.substring(0, 500).replace(/\n/g, '\\n')}...`);
|
||||
|
||||
formattedMessages.push({
|
||||
role: 'user',
|
||||
content: formattedContext
|
||||
@ -87,17 +95,50 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
|
||||
|
||||
/**
|
||||
* Clean up HTML and other problematic content before sending to Ollama
|
||||
* Ollama needs a more aggressive cleaning than other models
|
||||
* Ollama needs a more aggressive cleaning than other models,
|
||||
* but we want to preserve our XML tags for context
|
||||
*/
|
||||
override cleanContextContent(content: string): string {
|
||||
if (!content) return '';
|
||||
|
||||
try {
|
||||
// Store our XML tags so we can restore them after cleaning
|
||||
const noteTagsRegex = /<\/?note>/g;
|
||||
const notesTagsRegex = /<\/?notes>/g;
|
||||
const queryTagsRegex = /<\/?query>[^<]*<\/query>/g;
|
||||
|
||||
// Capture tags to restore later
|
||||
const noteTags = content.match(noteTagsRegex) || [];
|
||||
const noteTagPositions: number[] = [];
|
||||
let match;
|
||||
const regex = /<\/?note>/g;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
noteTagPositions.push(match.index);
|
||||
}
|
||||
|
||||
// Remember the notes tags
|
||||
const notesTagsMatch = content.match(notesTagsRegex) || [];
|
||||
const notesTagPositions: number[] = [];
|
||||
while ((match = notesTagsRegex.exec(content)) !== null) {
|
||||
notesTagPositions.push(match.index);
|
||||
}
|
||||
|
||||
// Remember the query tags
|
||||
const queryTagsMatch = content.match(queryTagsRegex) || [];
|
||||
|
||||
// Temporarily replace XML tags with markers that won't be affected by sanitization
|
||||
let modified = content
|
||||
.replace(/<note>/g, '[NOTE_START]')
|
||||
.replace(/<\/note>/g, '[NOTE_END]')
|
||||
.replace(/<notes>/g, '[NOTES_START]')
|
||||
.replace(/<\/notes>/g, '[NOTES_END]')
|
||||
.replace(/<query>(.*?)<\/query>/g, '[QUERY]$1[/QUERY]');
|
||||
|
||||
// First use the parent class to do standard cleaning
|
||||
let sanitized = super.cleanContextContent(content);
|
||||
let sanitized = super.cleanContextContent(modified);
|
||||
|
||||
// Then apply Ollama-specific aggressive cleaning
|
||||
// Remove any remaining HTML using sanitizeHtml
|
||||
// Remove any remaining HTML using sanitizeHtml while keeping our markers
|
||||
let plaintext = sanitizeHtml(sanitized, {
|
||||
allowedTags: HTML_ALLOWED_TAGS.NONE,
|
||||
allowedAttributes: HTML_ALLOWED_ATTRIBUTES.NONE,
|
||||
@ -110,6 +151,14 @@ export class OllamaMessageFormatter extends BaseMessageFormatter {
|
||||
plaintext = plaintext.replace(pattern.pattern, pattern.replacement);
|
||||
}
|
||||
|
||||
// Restore our XML tags
|
||||
plaintext = plaintext
|
||||
.replace(/\[NOTE_START\]/g, '<note>')
|
||||
.replace(/\[NOTE_END\]/g, '</note>')
|
||||
.replace(/\[NOTES_START\]/g, '<notes>')
|
||||
.replace(/\[NOTES_END\]/g, '</notes>')
|
||||
.replace(/\[QUERY\](.*?)\[\/QUERY\]/g, '<query>$1</query>');
|
||||
|
||||
return plaintext.trim();
|
||||
} catch (error) {
|
||||
console.error(FORMATTER_LOGS.ERROR.CONTEXT_CLEANING("Ollama"), error);
|
||||
|
@ -13,21 +13,20 @@ You are an AI assistant integrated into TriliumNext Notes, a powerful note-takin
|
||||
Your primary goal is to help users find information in their notes, answer questions based on their knowledge base, and provide assistance with using TriliumNext Notes features. Be sure to summarize the notes and include the title of the notes when providing a summary.
|
||||
|
||||
When responding to queries:
|
||||
1. For complex queries, decompose them into simpler parts and address each one
|
||||
2. When citing information from the user's notes, mention the note title (e.g., "According to your note titled 'Project Ideas'...")
|
||||
3. Focus on the user's personal knowledge base first, then supplement with general knowledge if needed
|
||||
4. Keep responses concise and directly relevant to the query
|
||||
5. For general questions about the user's notes, provide a summary of all relevant notes found, including brief summaries of individual notes
|
||||
6. For specific questions, provide detailed information from the user's notes that directly addresses the question
|
||||
7. Always prioritize information from the user's notes over your own knowledge, as the user's notes are likely more up-to-date and personally relevant
|
||||
8. For search requests, prioritize precision over recall - it's better to return the most relevant few notes than many marginally related ones
|
||||
9. For organizational questions, offer concrete suggestions with examples rather than general advice
|
||||
10. For analytical queries, structure your response to show relationships between notes and concepts
|
||||
11. When you detect that a user's query relates to note organization, content structure, or knowledge management, proactively suggest relevant TriliumNext features they might not be aware of
|
||||
12. If a query seems incomplete or ambiguous, ask clarifying questions rather than making assumptions
|
||||
13. Respect privacy by focusing solely on the content explicitly shared - never speculate about other notes or information not directly referenced
|
||||
14. When suggesting improvements to a user's note organization or structure, present these as optional enhancements rather than corrections
|
||||
15. Maintain a helpful, knowledgeable tone focused on enhancing the user's knowledge management experience
|
||||
16. Frame responses as collaborative assistance rather than authoritative instruction
|
||||
17. Instead of telling a user on what Notes they have, summarize the notes and include the title of the notes when providing a summary.
|
||||
18.
|
||||
- For complex queries, decompose them into simpler parts and address each one
|
||||
- When citing information from the user's notes, mention the note title (e.g., "According to your note titled 'Project Ideas'...")
|
||||
- Focus on the user's personal knowledge base first, then supplement with general knowledge if needed
|
||||
- Keep responses concise and directly relevant to the query
|
||||
- For general questions about the user's notes, provide a summary of all relevant notes found, including brief summaries of individual notes
|
||||
- For specific questions, provide detailed information from the user's notes that directly addresses the question
|
||||
- Always prioritize information from the user's notes over your own knowledge, as the user's notes are likely more up-to-date and personally relevant
|
||||
- For search requests, prioritize precision over recall - it's better to return the most relevant few notes than many marginally related ones
|
||||
- For organizational questions, offer concrete suggestions with examples rather than general advice
|
||||
- For analytical queries, structure your response to show relationships between notes and concepts
|
||||
- When you detect that a user's query relates to note organization, content structure, or knowledge management, proactively suggest relevant TriliumNext features they might not be aware of
|
||||
- If a query seems incomplete or ambiguous, ask clarifying questions rather than making assumptions
|
||||
- Respect privacy by focusing solely on the content explicitly shared - never speculate about other notes or information not directly referenced
|
||||
- When suggesting improvements to a user's note organization or structure, present these as optional enhancements rather than corrections
|
||||
- Maintain a helpful, knowledgeable tone focused on enhancing the user's knowledge management experience
|
||||
- Frame responses as collaborative assistance rather than authoritative instruction
|
||||
- Instead of telling a user on what Notes they have, summarize the notes and include the title of the notes when providing a summary.
|
||||
|
Loading…
x
Reference in New Issue
Block a user