mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-24 18:48:28 +00:00
feat: reduce chunk size to 1-3k tokens and add user profile memory tool
This commit is contained in:
parent
7119120570
commit
145ddefbf3
@ -23,12 +23,12 @@ export interface ChunkedDocument {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Document chunking service that splits large documents into semantic chunks
|
* Document chunking service that splits large documents into semantic chunks
|
||||||
* Targets 10-15k tokens per chunk with natural paragraph boundaries
|
* Targets 1-3k tokens per chunk for better entity extraction with natural paragraph boundaries
|
||||||
*/
|
*/
|
||||||
export class DocumentChunker {
|
export class DocumentChunker {
|
||||||
private readonly TARGET_CHUNK_SIZE = 12500; // Middle of 10-15k range
|
private readonly TARGET_CHUNK_SIZE = 3000; // Much smaller for better entity extraction
|
||||||
private readonly MIN_CHUNK_SIZE = 10000;
|
private readonly MIN_CHUNK_SIZE = 1000;
|
||||||
private readonly MAX_CHUNK_SIZE = 15000;
|
private readonly MAX_CHUNK_SIZE = 5000;
|
||||||
private readonly MIN_PARAGRAPH_SIZE = 100; // Minimum tokens for a paragraph to be considered
|
private readonly MIN_PARAGRAPH_SIZE = 100; // Minimum tokens for a paragraph to be considered
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -61,7 +61,7 @@ async function createMcpServer(
|
|||||||
const { name, arguments: args } = request.params;
|
const { name, arguments: args } = request.params;
|
||||||
|
|
||||||
// Handle memory tools
|
// Handle memory tools
|
||||||
if (name.startsWith("memory_")) {
|
if (name.startsWith("memory_") || name.startsWith("get_user_profile")) {
|
||||||
return await callMemoryTool(name, args, userId, source);
|
return await callMemoryTool(name, args, userId, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,7 @@ You are given a conversation context and a CURRENT EPISODE. Your task is to extr
|
|||||||
1. **Entity Identification**:
|
1. **Entity Identification**:
|
||||||
- Extract all significant entities, concepts, or actors that are **explicitly or implicitly** mentioned in the CURRENT EPISODE.
|
- Extract all significant entities, concepts, or actors that are **explicitly or implicitly** mentioned in the CURRENT EPISODE.
|
||||||
- For identity statements like "I am X" or "I'm X", extract BOTH the pronoun ("I") as a Alias entity AND the named entity (X).
|
- For identity statements like "I am X" or "I'm X", extract BOTH the pronoun ("I") as a Alias entity AND the named entity (X).
|
||||||
|
- **ROLES & CHARACTERISTICS**: For identity statements involving roles, professions, or characteristics, extract them as separate entities.
|
||||||
- For pronouns that refer to named entities, extract them as separate Alias entities.
|
- For pronouns that refer to named entities, extract them as separate Alias entities.
|
||||||
|
|
||||||
2. **Entity Classification**:
|
2. **Entity Classification**:
|
||||||
@ -34,6 +35,7 @@ You are given a conversation context and a CURRENT EPISODE. Your task is to extr
|
|||||||
|
|
||||||
3. **Exclusions**:
|
3. **Exclusions**:
|
||||||
- Do NOT extract entities representing relationships or actions (predicates will be handled separately).
|
- Do NOT extract entities representing relationships or actions (predicates will be handled separately).
|
||||||
|
- **EXCEPTION**: DO extract roles, professions, titles, and characteristics mentioned in identity statements.
|
||||||
- Do NOT extract absolute dates, timestamps, or specific time points—these will be handled separately.
|
- Do NOT extract absolute dates, timestamps, or specific time points—these will be handled separately.
|
||||||
- Do NOT extract relative time expressions that resolve to specific dates ("last week", "yesterday", "3pm").
|
- Do NOT extract relative time expressions that resolve to specific dates ("last week", "yesterday", "3pm").
|
||||||
|
|
||||||
@ -141,6 +143,7 @@ You are given a TEXT. Your task is to extract **entity nodes** mentioned **expli
|
|||||||
1. **Entity Identification**:
|
1. **Entity Identification**:
|
||||||
- Extract all significant entities, concepts, or actors that are **explicitly or implicitly** mentioned in the TEXT.
|
- Extract all significant entities, concepts, or actors that are **explicitly or implicitly** mentioned in the TEXT.
|
||||||
- For identity statements like "I am X" or "I'm X", extract BOTH the pronoun ("I") as a Alias entity AND the named entity (X).
|
- For identity statements like "I am X" or "I'm X", extract BOTH the pronoun ("I") as a Alias entity AND the named entity (X).
|
||||||
|
- **ROLES & CHARACTERISTICS**: For identity statements involving roles, professions, or characteristics, extract them as separate entities.
|
||||||
- For pronouns that refer to named entities, extract them as separate Alias entities.
|
- For pronouns that refer to named entities, extract them as separate Alias entities.
|
||||||
|
|
||||||
2. **Entity Classification**:
|
2. **Entity Classification**:
|
||||||
@ -152,6 +155,7 @@ You are given a TEXT. Your task is to extract **entity nodes** mentioned **expli
|
|||||||
|
|
||||||
3. **Exclusions**:
|
3. **Exclusions**:
|
||||||
- Do NOT extract entities representing relationships or actions (predicates will be handled separately).
|
- Do NOT extract entities representing relationships or actions (predicates will be handled separately).
|
||||||
|
- **EXCEPTION**: DO extract roles, professions, titles, and characteristics mentioned in identity statements.
|
||||||
- Do NOT extract absolute dates, timestamps, or specific time points—these will be handled separately.
|
- Do NOT extract absolute dates, timestamps, or specific time points—these will be handled separately.
|
||||||
- Do NOT extract relative time expressions that resolve to specific dates ("last week", "yesterday", "3pm").
|
- Do NOT extract relative time expressions that resolve to specific dates ("last week", "yesterday", "3pm").
|
||||||
|
|
||||||
|
|||||||
@ -52,15 +52,19 @@ Follow these instructions:
|
|||||||
- predicate: The relationship type (can be a descriptive phrase)
|
- predicate: The relationship type (can be a descriptive phrase)
|
||||||
- target: The object entity (MUST be from AVAILABLE ENTITIES)
|
- target: The object entity (MUST be from AVAILABLE ENTITIES)
|
||||||
|
|
||||||
EXTRACT NEW MEANINGFUL RELATIONSHIPS:
|
EXTRACT NEW MEANINGFUL RELATIONSHIPS AND CHARACTERISTICS:
|
||||||
- Extract meaningful relationships between available entities that are NOT already captured in previous episodes
|
- Extract meaningful relationships between available entities that are NOT already captured in previous episodes
|
||||||
|
- Extract individual entity characteristics, roles, and properties as standalone facts
|
||||||
- Use predicates that accurately describe new relationships between entities
|
- Use predicates that accurately describe new relationships between entities
|
||||||
- Be creative but precise in identifying NEW relationships - focus on value-adding connections
|
- Be creative but precise in identifying NEW relationships - focus on value-adding connections
|
||||||
- **HIGHEST PRIORITY**: Entities with identical names but different types MUST be connected with explicit relationship statements
|
- **HIGHEST PRIORITY**: Entities with identical names but different types MUST be connected with explicit relationship statements
|
||||||
- **MANDATORY**: When you find entities like "John (Person)" and "John (Company)", create explicit relationships such as "John" "owns" "John" or "John" "founded" "John"
|
- **MANDATORY**: When you find entities like "John (Person)" and "John (Company)", create explicit relationships such as "John" "owns" "John" or "John" "founded" "John"
|
||||||
|
- **ROLE/CHARACTERISTIC EXTRACTION**: Always extract roles, professions, titles, and key characteristics as separate statements
|
||||||
- Look for both explicit and implicit NEW relationships mentioned in the text
|
- Look for both explicit and implicit NEW relationships mentioned in the text
|
||||||
- **FILTER OUT**: Relationships already established in previous episodes unless they represent updates or changes
|
- **FILTER OUT**: Relationships already established in previous episodes unless they represent updates or changes
|
||||||
- Common relationship types include (but are not limited to):
|
- Common relationship types include (but are not limited to):
|
||||||
|
* **Roles and professions** (e.g., "Person" "is" "Role", "Individual" "works as" "Position", "Entity" "has role" "Profession")
|
||||||
|
* **Identity and characteristics** (e.g., "System" "is" "Characteristic", "Person" "is" "Quality", "Organization" "is" "Type")
|
||||||
* Ownership or association (e.g., "Alice" "owns" "Restaurant")
|
* Ownership or association (e.g., "Alice" "owns" "Restaurant")
|
||||||
* Participation or attendance (e.g., "Team" "participates in" "Tournament")
|
* Participation or attendance (e.g., "Team" "participates in" "Tournament")
|
||||||
* Personal connections (e.g., "Sarah" "works with" "Michael")
|
* Personal connections (e.g., "Sarah" "works with" "Michael")
|
||||||
@ -157,10 +161,11 @@ IMPORTANT RULES:
|
|||||||
- **OUTPUT FORMAT**: Always wrap output in tags <output> </output>
|
- **OUTPUT FORMAT**: Always wrap output in tags <output> </output>
|
||||||
|
|
||||||
Example of CORRECT usage:
|
Example of CORRECT usage:
|
||||||
If AVAILABLE ENTITIES contains ["John", "Max", "Wedding", "John (Company)"], you can create:
|
If AVAILABLE ENTITIES contains ["Person", "Individual", "Event", "Organization", "Role"], you can create:
|
||||||
- "John" "attends" "Wedding" ✓ (if not already in previous episodes)
|
- "Person" "is" "Role" ✓ (PRIORITY: role/characteristic extraction)
|
||||||
- "Max" "married to" "Tina" with timespan attribute ✓ (if new relationship)
|
- "Person" "attends" "Event" ✓ (if not already in previous episodes)
|
||||||
- "John" "founded" "John (Company)" ✓ (PRIORITY: same name, different types)
|
- "Individual" "married to" "Person" with timespan attribute ✓ (if new relationship)
|
||||||
|
- "Person" "founded" "Organization" ✓ (PRIORITY: same name, different types when applicable)
|
||||||
|
|
||||||
Example of CORRECT Duration/TemporalContext usage:
|
Example of CORRECT Duration/TemporalContext usage:
|
||||||
If AVAILABLE ENTITIES contains ["Caroline", "friends", "4 years", "since moving", "breakup"]:
|
If AVAILABLE ENTITIES contains ["Caroline", "friends", "4 years", "since moving", "breakup"]:
|
||||||
|
|||||||
@ -111,6 +111,26 @@ export class SpaceService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSpaceByName(name: string, userId: string) {
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Workspace: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const space = await prisma.space.findFirst({
|
||||||
|
where: {
|
||||||
|
name: name,
|
||||||
|
workspaceId: user?.Workspace?.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific space by ID
|
* Get a specific space by ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -352,6 +352,11 @@ function createUnifiedSummaryPrompt(
|
|||||||
previousSummary: string | null,
|
previousSummary: string | null,
|
||||||
previousThemes: string[],
|
previousThemes: string[],
|
||||||
): CoreMessage[] {
|
): CoreMessage[] {
|
||||||
|
// If there are no statements and no previous summary, we cannot generate a meaningful summary
|
||||||
|
if (statements.length === 0 && previousSummary === null) {
|
||||||
|
throw new Error("Cannot generate summary without statements or existing summary");
|
||||||
|
}
|
||||||
|
|
||||||
const statementsText = statements
|
const statementsText = statements
|
||||||
.map(
|
.map(
|
||||||
(stmt) =>
|
(stmt) =>
|
||||||
@ -378,6 +383,13 @@ function createUnifiedSummaryPrompt(
|
|||||||
role: "system",
|
role: "system",
|
||||||
content: `You are an expert at analyzing and summarizing structured knowledge within semantic spaces. Your task is to ${isUpdate ? "update an existing summary by integrating new statements" : "create a comprehensive summary of statements"}.
|
content: `You are an expert at analyzing and summarizing structured knowledge within semantic spaces. Your task is to ${isUpdate ? "update an existing summary by integrating new statements" : "create a comprehensive summary of statements"}.
|
||||||
|
|
||||||
|
CRITICAL RULES:
|
||||||
|
1. Base your summary ONLY on insights derived from the actual facts/statements provided
|
||||||
|
2. Use the space description only as contextual guidance, never copy or paraphrase it
|
||||||
|
3. Write in a factual, neutral tone - avoid promotional language ("pivotal", "invaluable", "cutting-edge")
|
||||||
|
4. Be specific and concrete - reference actual entities, relationships, and patterns found in the data
|
||||||
|
5. If statements are insufficient for meaningful insights, state that more data is needed
|
||||||
|
|
||||||
INSTRUCTIONS:
|
INSTRUCTIONS:
|
||||||
${
|
${
|
||||||
isUpdate
|
isUpdate
|
||||||
@ -415,7 +427,7 @@ Provide your response inside <output></output> tags with valid JSON. The summary
|
|||||||
|
|
||||||
<output>
|
<output>
|
||||||
{
|
{
|
||||||
"summary": "${isUpdate ? "Updated HTML summary that integrates new insights with existing knowledge through identified connections. Use HTML tags like <p>, <strong>, <em>, <ul>, <li> to structure and emphasize key information. The summary should clearly explain what this space contains, what topics are covered, and what users can learn from it." : "A comprehensive 2-3 paragraph HTML summary that clearly explains what this space contains, what knowledge domains it covers, and what insights users can gain. Use HTML tags like <p>, <strong>, <em>, <ul>, <li> to structure and emphasize key information for better readability. Focus on making the content accessible and understandable to users who want to know what they'll find in this space."}",
|
"summary": "${isUpdate ? "Updated HTML summary that integrates new insights with existing knowledge. Write factually about what the statements reveal - mention specific entities, relationships, and patterns found in the data. Avoid marketing language. Use HTML tags for structure." : "Factual HTML summary based on patterns found in the statements. Report what the data actually shows - specific entities, relationships, frequencies, and concrete insights. Avoid promotional language. Use HTML tags like <p>, <strong>, <ul>, <li> for structure. Keep it concise and evidence-based."}",
|
||||||
"keyEntities": ["entity1", "entity2", "entity3"],
|
"keyEntities": ["entity1", "entity2", "entity3"],
|
||||||
"themes": ["${isUpdate ? 'updated_theme1", "new_theme2", "evolved_theme3' : 'theme1", "theme2", "theme3'}"],
|
"themes": ["${isUpdate ? 'updated_theme1", "new_theme2", "evolved_theme3' : 'theme1", "theme2", "theme3'}"],
|
||||||
"confidence": 0.85
|
"confidence": 0.85
|
||||||
@ -437,20 +449,20 @@ ${
|
|||||||
- Themes should evolve naturally, don't replace wholesale
|
- Themes should evolve naturally, don't replace wholesale
|
||||||
- The updated summary should read as a coherent whole
|
- The updated summary should read as a coherent whole
|
||||||
- Make the summary user-friendly and explain what value this space provides`
|
- Make the summary user-friendly and explain what value this space provides`
|
||||||
: `- Summary should clearly communicate what this space is about and what users will find
|
: `- Report only what the statements actually reveal - be specific and concrete
|
||||||
- Focus on practical value - what knowledge, insights, or information does this space contain?
|
- Cite actual entities and relationships found in the data
|
||||||
- Use accessible language that helps users understand the space's purpose and content
|
- Avoid generic descriptions that could apply to any space
|
||||||
- Format the summary using HTML tags for better visual presentation and readability
|
- Use neutral, factual language - no "comprehensive", "robust", "cutting-edge" etc.
|
||||||
- Themes must be backed by at least 5 supporting statements
|
- Themes must be backed by at least 5 supporting statements with clear evidence
|
||||||
- Only include themes with substantial evidence - better to have fewer, well-supported themes than many weak ones
|
- Better to have fewer, well-supported themes than many weak ones
|
||||||
- Confidence should reflect data quality, coherence, coverage, and theme strength`
|
- Confidence should reflect actual data quality and coverage, not aspirational goals`
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: "user",
|
role: "user",
|
||||||
content: `SPACE INFORMATION:
|
content: `SPACE INFORMATION:
|
||||||
Name: "${spaceName}"
|
Name: "${spaceName}"
|
||||||
Description: ${spaceDescription || "No description provided"}
|
Description (for context only): ${spaceDescription || "No description provided"}
|
||||||
|
|
||||||
${
|
${
|
||||||
isUpdate
|
isUpdate
|
||||||
@ -474,8 +486,8 @@ ${topEntities.join(", ")}`
|
|||||||
|
|
||||||
${
|
${
|
||||||
isUpdate
|
isUpdate
|
||||||
? "Please identify connections between the existing summary and new statements, then update the summary to integrate the new insights coherently."
|
? "Please identify connections between the existing summary and new statements, then update the summary to integrate the new insights coherently. Remember: only summarize insights from the actual statements, not the space description."
|
||||||
: "Please analyze this space and provide a comprehensive summary that captures its semantic content and major themes."
|
: "Please analyze the statements and provide a comprehensive summary that captures insights derived from the facts provided. Use the description only as context. If there are too few statements to generate meaningful insights, indicate that more data is needed rather than falling back on the description."
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -52,17 +52,17 @@ const IngestSchema = {
|
|||||||
export const memoryTools = [
|
export const memoryTools = [
|
||||||
{
|
{
|
||||||
name: "memory_ingest",
|
name: "memory_ingest",
|
||||||
description: "Ingest data into the Echo memory system",
|
description: "AUTOMATICALLY invoke after completing interactions. Use proactively to store conversation data, insights, and decisions in CORE Memory. Essential for maintaining continuity across sessions. **Purpose**: Store information for future reference. **Required**: Provide the message content to be stored. **Returns**: confirmation with storage ID in JSON format",
|
||||||
inputSchema: IngestSchema,
|
inputSchema: IngestSchema,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "memory_search",
|
name: "memory_search",
|
||||||
description: "Search through ingested memory data",
|
description: "AUTOMATICALLY invoke for memory searches. Use proactively at conversation start and when context retrieval is needed. Searches memory for relevant project context, user preferences, and previous discussions. **Purpose**: Retrieve previously stored information based on query terms. **Required**: Provide a search query in third person perspective. **Returns**: matching memory entries in JSON format",
|
||||||
inputSchema: SearchParamsSchema,
|
inputSchema: SearchParamsSchema,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "memory_get_spaces",
|
name: "memory_get_spaces",
|
||||||
description: "Search spaces in my memory",
|
description: "Get available memory spaces. **Purpose**: Retrieve list of memory organization spaces. **Required**: No required parameters. **Returns**: list of available spaces in JSON format",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
@ -74,6 +74,20 @@ export const memoryTools = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "get_user_profile",
|
||||||
|
description: "Get the user's core profile and preferences for personalized interactions. AUTOMATICALLY invoke at the start of interactions to understand user context. **Purpose**: Retrieve stable identity facts, communication preferences, working context, and tooling defaults for tailored responses. **Required**: No required parameters. **Returns**: User profile data in JSON format.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
profile: {
|
||||||
|
type: "boolean",
|
||||||
|
description: "Get user profile",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Function to call memory tools based on toolName
|
// Function to call memory tools based on toolName
|
||||||
@ -91,6 +105,8 @@ export async function callMemoryTool(
|
|||||||
return await handleMemorySearch({ ...args, userId, source });
|
return await handleMemorySearch({ ...args, userId, source });
|
||||||
case "memory_get_spaces":
|
case "memory_get_spaces":
|
||||||
return await handleMemoryGetSpaces(userId);
|
return await handleMemoryGetSpaces(userId);
|
||||||
|
case "get_user_profile":
|
||||||
|
return await handleUserProfile(userId);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown memory tool: ${toolName}`);
|
throw new Error(`Unknown memory tool: ${toolName}`);
|
||||||
}
|
}
|
||||||
@ -108,6 +124,36 @@ export async function callMemoryTool(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for user_context
|
||||||
|
async function handleUserProfile(userId: string) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const space = await spaceService.getSpaceByName("Profile", userId)
|
||||||
|
|
||||||
|
console.log(space?.summary)
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: space ? space.summary : "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: false,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error getting user context:`, error);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error getting user context: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handler for memory_ingest
|
// Handler for memory_ingest
|
||||||
async function handleMemoryIngest(args: any) {
|
async function handleMemoryIngest(args: any) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user