Compare commits

...

1 Commits
1.4.5 ... 1.4.6

Author SHA1 Message Date
04f038ba65 Memory prompt refinement
All checks were successful
Publish Library / Build NPM Project (push) Successful in 38s
Publish Library / Tag Version (push) Successful in 19s
2026-08-05 13:14:21 -04:00
3 changed files with 20 additions and 24 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@ztimson/ai-utils",
"version": "1.4.5",
"version": "1.4.6",
"description": "AI Utility library",
"author": "Zak Timson",
"license": "MIT",

View File

@@ -1,4 +1,4 @@
import {clean, snakeCase} from '@ztimson/utils';
import {clean, makeUnique, snakeCase} from '@ztimson/utils';
import {AbortablePromise, Ai} from './ai.ts';
import {Anthropic} from './antrhopic.ts';
import {OpenAi} from './open-ai.ts';
@@ -7,7 +7,7 @@ import {AiTool, AiToolArg} from './tools.ts';
import {fileURLToPath} from 'url';
import {dirname, join} from 'path';
import {spawn} from 'node:child_process';
import {Memory, MemoryCache, MemoryManager, MemoryOptions} from './memory.ts';
import {Memory, MemoryCache, MemoryManager, MemoryOptions, stripHeader} from './memory.ts';
const MAX_AGENT_DEPTH = 5;
@@ -207,7 +207,7 @@ ${a.system}`,
const list = allTools.map(t => `- ${t.name}: ${t.description}`).join('\n');
return {
prompt: `You have access to the following MCP tools:\n${list}`,
prompt: `## MCP\nYou have access to the following MCP tools:\n${list}`,
tools: allTools
};
}
@@ -216,7 +216,7 @@ ${a.system}`,
if(!skills?.length) return {prompt: '', tools: []};
const list = skills.map(s => `- ${s.name}: ${s.description}`).join('\n');
return {
prompt: `You have access to the following skill documents, whenever there is overlap between a question and a skill file, use \`skill_read\` to get instructions and background knowledge:\n${list}`,
prompt: `## Skills\nYou have access to the following skill documents, whenever there is overlap between a question and a skill file, use \`skill_read\` to get instructions and background knowledge:\n${list}`,
tools: [{
name: 'skill_read',
description: 'Read the full content of a skill/knowledge document',
@@ -316,30 +316,26 @@ ${a.system}`,
} else listed.push(r);
}
prompts.unshift(`You have a background memory process which has prefetched relevant information${mem.update ? ' and will create new memories from this conversation' : ''} for you
prompts.unshift(`## Memory
You have a background memory process which has prefetched relevant information${mem.update ? ' and will create new memories from this conversation' : ''} for you
Assume it is perfect and never mention this process to anyone ever
Always use your memories to craft a personalized response, they contain links / [[wiki links]] which you use navigate between them
${mem.tool ? `You can access memory files via the \`memory_search\` and \`memory_recall\` tools
When you need information about the user, \`memory_recall\` \`People/User\` before asking (fetch if not included bellow)
When you need information not provided, attempt 1-3 \`memory_search\` calls with unique queries before asking` : ''}
When you need information not provided, attempt 1-3 \`memory_search\` calls with distinct queries before asking` : ''}
${preloaded.length ? `
Prefetched Memories (Most relevant first):
${preloaded.length ? `### Prefetched Memories (Most relevant first):
${preloaded.map(r => `Memory: ${r.name}
Description: ${r.description}
Linked: ${[r.links, ...r.backlinks].join(', ')}
Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')}
\`\`\`
${r.content}
${stripHeader(r.content)}
\`\`\``).join('\n\n')}` : ''}
${mem.tool && listed.length ? listed.map(r => `Memory: ${r.name}
${mem.tool && listed.length ? '\n' + listed.map(r => `Memory: ${r.name}
Description: ${r.description}
Linked: ${[r.links, ...r.backlinks].join(', ')}
<!-- Truncated -->`).join('\n\n') : ''}
${mem.tool ? `Full memory list:
${mems.map(m => `- ${m.name}: ${m.description}`).join('\n')}` : ''}`.trim())
Linked: ${makeUnique([...r.links, ...r.backlinks]).join(', ')}
<!-- Truncated -->`).join('\n\n') : ''}`.trim())
}
if(mem.tool) tools.push(this.memoryManager.tools.read(mem.memory));
}

View File

@@ -61,6 +61,10 @@ function cosineSearch(query: number[], memories: Memory[], limit: number): Memor
.map(s => s.ref);
}
export function stripHeader(content: string): string {
return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart();
}
export class MemoryCache {
private tree!: KDTree<MemoryRef>;
public memories: Memory[];
@@ -249,7 +253,7 @@ ${m.content}
private appendFacts(node: Memory, facts: string[]): void {
this.ensureDoc(node);
const body = this.stripHeader(node.content);
const body = stripHeader(node.content);
const bullets = facts.map(f => `- ${f}`).join('\n');
const idx = body.indexOf(FACTS_HEADING);
const newBody = idx === -1
@@ -355,7 +359,7 @@ ${ghosts.length ? `${ghosts.map(g => `- ${g}: (Ghost)`).join('\n')}` : ''}`,
}
private async docAgent(node: Memory, memories: Memory[], options: LLMRequest, entry: {request: {abort?: () => void} | null}): Promise<void> {
const currentBody = this.stripHeader(node.content);
const currentBody = stripHeader(node.content);
let update;
try {
for (let i = 0; i < 2 && !update?.content; i++) {
@@ -420,10 +424,6 @@ ${currentBody}
return {fm, body: match[2]};
}
private stripHeader(content: string): string {
return content.replace(/^---[\s\S]*?\n---\n?/, '').trimStart();
}
private touchHeader(node: Memory, body: string): string {
const {fm} = this.parseFrontmatter(node.content);
fm.set('name', node.name);