diff --git a/internal/knowledgegraph/extractor_prompt.go b/internal/knowledgegraph/extractor_prompt.go index 0bb768c1..cc1deda9 100644 --- a/internal/knowledgegraph/extractor_prompt.go +++ b/internal/knowledgegraph/extractor_prompt.go @@ -1,6 +1,6 @@ package knowledgegraph -const extractionSystemPrompt = `You are a knowledge graph extractor for an AI assistant's memory system. Given text (usually personal notes, work logs, or conversation summaries), extract the most important entities and their relationships. +const extractionSystemPrompt = `You are a knowledge graph extractor for an AI assistant's memory system. Given text (personal notes, work logs, conversation summaries, or any domain content), extract the most important entities and their relationships. Output valid JSON with this schema: { @@ -8,7 +8,7 @@ Output valid JSON with this schema: { "external_id": "unique-lowercase-id", "name": "Display Name", - "entity_type": "person|project|task|event|concept|location|organization", + "entity_type": "person|organization|project|product|technology|task|event|document|concept|location", "description": "Brief description of the entity", "confidence": 0.0-1.0 } @@ -30,14 +30,24 @@ Output valid JSON with this schema: - Same real-world entity MUST always get the same external_id across extractions - When a pronoun or partial reference clearly refers to a named entity, use that entity's ID — do NOT create a new entity -## Entity Types -- person: named individuals -- organization: companies, teams, departments -- project: software projects, initiatives, products -- task: specific work items, tickets, TODOs -- event: meetings, releases, incidents, deadlines -- concept: technologies, methodologies, domains -- location: cities, offices, regions +## Entity Types (use ONLY these 10) +- person: named individuals (developer, manager, doctor, teacher) +- organization: companies, teams, departments, groups (Google, marketing team, hospital) +- project: initiatives, campaigns, programs being built or executed (GoClaw, thesis, ad campaign) +- product: finished goods, services, SaaS, platforms being used or sold (LadiSales, iPhone, insurance plan) +- technology: software, tools, frameworks, languages, databases, hardware (PostgreSQL, Docker, React, MRI machine) +- task: specific work items, tickets, TODOs (fix bug #123, deploy v2, quarterly review) +- event: meetings, releases, incidents, deadlines, milestones (launch day, sprint review, server outage) +- document: articles, reports, specs, contracts, guides, files (Q1 report, API spec, user manual) +- concept: abstract ideas, methodologies, domains, standards, patterns (RBAC, Agile, machine learning, GDPR) +- location: cities, offices, regions, venues (HCM, AWS us-east-1, room 3, building A) + +Choosing between similar types: +- project vs product: project = being built/executed, product = being used/sold/consumed +- technology vs concept: technology = concrete tool/software you install/run, concept = abstract idea/methodology +- technology vs product: technology = technical tool (PostgreSQL, Docker), product = commercial offering (Salesforce, iPhone) +- document vs concept: document = a specific artifact (Q1 report), concept = an abstract idea (quarterly reporting) +- task vs event: task = actionable work item with an owner, event = a point in time that happened/will happen ## Relation Types (use ONLY these) - works_on, manages, reports_to, collaborates_with (people↔work) @@ -45,31 +55,39 @@ Output valid JSON with this schema: - created, completed, assigned_to, scheduled_for (actions) - located_in, based_at (location) - uses, implements, integrates_with (technology) -- related_to (fallback — use sparingly) +- authored, references (documents: who wrote it, what it refers to) +- provides, requires (capabilities: what an entity offers or needs) +- related_to (LAST RESORT — if no specific relation fits, prefer omitting the relation entirely) ## Rules - Extract 3-15 entities depending on text density. Short text = fewer entities -- Confidence: 1.0 = explicitly stated, 0.7 = strongly implied, 0.4 = weakly inferred +- Confidence: 1.0 = explicitly stated fact, 0.8 = strongly implied, 0.5 = inferred from context +- Use varied confidence — NOT everything is 1.0. Reserve 1.0 for direct, unambiguous statements - Keep names in original language - Descriptions: 1 sentence max, capture the entity's role or significance - Skip generic/vague entities ("the system", "the team" without specific name) +- Do NOT use related_to as a default — if you cannot determine a specific relation, omit it - Output ONLY the JSON object, no markdown, no code blocks ## Example -Input: "Talked to Minh about the GoClaw migration. He'll handle the database schema changes by Friday. The team uses PostgreSQL with pgvector." +Input: "Talked to Minh about the GoClaw migration. He'll handle the database schema changes by Friday. The team uses PostgreSQL with pgvector. I wrote the migration guide yesterday." Output: { "entities": [ {"external_id": "minh", "name": "Minh", "entity_type": "person", "description": "Handling database schema changes for GoClaw", "confidence": 1.0}, {"external_id": "goclaw", "name": "GoClaw", "entity_type": "project", "description": "Project undergoing migration", "confidence": 1.0}, - {"external_id": "goclaw-migration", "name": "GoClaw Migration", "entity_type": "task", "description": "Database migration task for GoClaw", "confidence": 1.0}, - {"external_id": "postgresql", "name": "PostgreSQL", "entity_type": "concept", "description": "Database technology used with pgvector", "confidence": 1.0} + {"external_id": "goclaw-migration", "name": "GoClaw Migration", "entity_type": "task", "description": "Database migration task, deadline Friday", "confidence": 1.0}, + {"external_id": "postgresql", "name": "PostgreSQL", "entity_type": "technology", "description": "Database used with pgvector extension", "confidence": 1.0}, + {"external_id": "pgvector", "name": "pgvector", "entity_type": "technology", "description": "PostgreSQL extension for vector embeddings", "confidence": 0.8}, + {"external_id": "migration-guide", "name": "Migration Guide", "entity_type": "document", "description": "Guide for the GoClaw database migration", "confidence": 1.0} ], "relations": [ - {"source_entity_id": "minh", "relation_type": "works_on", "target_entity_id": "goclaw-migration", "confidence": 1.0}, + {"source_entity_id": "minh", "relation_type": "assigned_to", "target_entity_id": "goclaw-migration", "confidence": 1.0}, {"source_entity_id": "goclaw-migration", "relation_type": "part_of", "target_entity_id": "goclaw", "confidence": 1.0}, - {"source_entity_id": "goclaw", "relation_type": "uses", "target_entity_id": "postgresql", "confidence": 1.0} + {"source_entity_id": "goclaw", "relation_type": "uses", "target_entity_id": "postgresql", "confidence": 1.0}, + {"source_entity_id": "postgresql", "relation_type": "integrates_with", "target_entity_id": "pgvector", "confidence": 0.8}, + {"source_entity_id": "migration-guide", "relation_type": "references", "target_entity_id": "goclaw-migration", "confidence": 1.0} ] }` diff --git a/internal/tools/knowledge_graph.go b/internal/tools/knowledge_graph.go index 00f62322..aeb1a7b4 100644 --- a/internal/tools/knowledge_graph.go +++ b/internal/tools/knowledge_graph.go @@ -42,7 +42,7 @@ func (t *KnowledgeGraphSearchTool) Parameters() map[string]any { }, "entity_type": map[string]any{ "type": "string", - "description": "Filter by entity type (person, project, task, event, concept, location, organization)", + "description": "Filter by entity type (person, organization, project, product, technology, task, event, document, concept, location)", }, "entity_id": map[string]any{ "type": "string", diff --git a/ui/web/src/pages/memory/kg-graph-view.tsx b/ui/web/src/pages/memory/kg-graph-view.tsx index 18ace726..bf3df40d 100644 --- a/ui/web/src/pages/memory/kg-graph-view.tsx +++ b/ui/web/src/pages/memory/kg-graph-view.tsx @@ -29,17 +29,20 @@ interface TypeColor { } const TYPE_COLORS: Record = { person: { border: "#E85D24", dark: { bg: "rgba(232,93,36,0.15)", text: "#f4a574" }, light: { bg: "#fde8d8", text: "#7a2610" } }, + organization: { border: "#ef4444", dark: { bg: "rgba(239,68,68,0.15)", text: "#fca5a5" }, light: { bg: "#fee2e2", text: "#991b1b" } }, project: { border: "#22c55e", dark: { bg: "rgba(34,197,94,0.15)", text: "#86efac" }, light: { bg: "#dcfce7", text: "#166534" } }, + product: { border: "#f97316", dark: { bg: "rgba(249,115,22,0.15)", text: "#fdba74" }, light: { bg: "#ffedd5", text: "#9a3412" } }, + technology: { border: "#3b82f6", dark: { bg: "rgba(59,130,246,0.15)", text: "#93c5fd" }, light: { bg: "#dbeafe", text: "#1e40af" } }, task: { border: "#f59e0b", dark: { bg: "rgba(245,158,11,0.15)", text: "#fcd34d" }, light: { bg: "#fef3c7", text: "#92400e" } }, event: { border: "#ec4899", dark: { bg: "rgba(236,72,153,0.15)", text: "#f9a8d4" }, light: { bg: "#fce7f3", text: "#9d174d" } }, + document: { border: "#8b5cf6", dark: { bg: "rgba(139,92,246,0.15)", text: "#c4b5fd" }, light: { bg: "#ede9fe", text: "#6d28d9" } }, concept: { border: "#a78bfa", dark: { bg: "rgba(167,139,250,0.12)", text: "#c4b5fd" }, light: { bg: "#ede9fe", text: "#5b21b6" } }, location: { border: "#14b8a6", dark: { bg: "rgba(20,184,166,0.15)", text: "#5eead4" }, light: { bg: "#ccfbf1", text: "#115e59" } }, - organization: { border: "#ef4444", dark: { bg: "rgba(239,68,68,0.15)", text: "#fca5a5" }, light: { bg: "#fee2e2", text: "#991b1b" } }, }; const DEFAULT_TC: TypeColor = { border: "#9ca3af", dark: { bg: "rgba(156,163,175,0.12)", text: "#d1d5db" }, light: { bg: "#f3f4f6", text: "#374151" } }; const TYPE_MASS: Record = { - organization: 8, project: 6, person: 4, concept: 3, location: 3, event: 2, task: 1.5, + organization: 8, project: 6, product: 5, person: 4, technology: 3.5, concept: 3, location: 3, document: 2.5, event: 2, task: 1.5, }; const DEFAULT_MASS = 2;