diff --git a/cmd/gateway_managed.go b/cmd/gateway_managed.go index 3525583a..0d4274fe 100644 --- a/cmd/gateway_managed.go +++ b/cmd/gateway_managed.go @@ -242,13 +242,19 @@ func wireExtras( slog.Info("memory layering enabled (Postgres)") } - // Wire knowledge graph store on KG tool + // Wire knowledge graph store on KG tool + hint in memory_search results if stores.KnowledgeGraph != nil { if kgTool, ok := toolsReg.Get("knowledge_graph_search"); ok { if kgt, ok := kgTool.(*tools.KnowledgeGraphSearchTool); ok { kgt.SetKGStore(stores.KnowledgeGraph) } } + // Enable KG hint in memory_search results + if searchTool, ok := toolsReg.Get("memory_search"); ok { + if mst, ok := searchTool.(*tools.MemorySearchTool); ok { + mst.SetHasKG(true) + } + } slog.Info("knowledge graph tool wired (Postgres)") } diff --git a/internal/agent/systemprompt.go b/internal/agent/systemprompt.go index eea8a416..b912e3f9 100644 --- a/internal/agent/systemprompt.go +++ b/internal/agent/systemprompt.go @@ -82,7 +82,7 @@ var coreToolSummaries = map[string]string{ "read_document": "Analyze documents (PDF, DOCX, etc.) attached to the conversation. Call this when you see tags. If this tool fails, use a relevant skill instead (e.g. pdf skill with exec tool). The path attribute in is a directly accessible file in your workspace — use it directly, no need to copy", "create_image": "Generate images from text descriptions using AI", "create_audio": "Generate music or sound effects from text descriptions using AI", - "knowledge_graph_search": "Search entities and traverse relationships in the knowledge graph", + "knowledge_graph_search": "Find people, projects, and their connections — use for relationship questions (who works with whom, project dependencies) that memory_search may miss", "handoff": "Transfer conversation to another agent (ONLY when user explicitly asks to switch agents — NOT for task delegation)", "evaluate_loop": "Run a generate→evaluate→revise loop between two agents for quality-critical tasks", "delegate_search": "Search for agents by expertise to find the right delegation target", @@ -244,7 +244,7 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string { if !isMinimal && cfg.HasMemory { memReminder := "Reminder: Before answering questions about prior work, decisions, or preferences, always run memory_search first." if cfg.HasKnowledgeGraph { - memReminder += " When the question involves people, relationships, or how things connect, run knowledge_graph_search to find entities and multi-hop connections that memory_search alone may miss." + memReminder += " Also run knowledge_graph_search when the question involves people, teams, projects, or connections — it finds relationship paths that memory_search misses." } lines = append(lines, memReminder, "") } @@ -413,7 +413,7 @@ func buildMemoryRecallSection(hasKG bool) []string { } if hasKG { lines = append(lines, - "When the question involves people, relationships, or how things connect, also run `knowledge_graph_search` — it finds entities and multi-hop connections (e.g. \"who does X work with?\", \"what projects is Y involved in?\") that memory_search alone may miss.", + "Also run `knowledge_graph_search` alongside memory_search when the question involves people, teams, projects, or connections between things. KG search is faster for relationship questions (e.g. \"who works with Minh?\", \"what projects is team X involved in?\", \"what depends on GoClaw?\") — it finds multi-hop connections that memory_search alone may miss.", "", ) } diff --git a/internal/tools/knowledge_graph.go b/internal/tools/knowledge_graph.go index ce12469b..61c72991 100644 --- a/internal/tools/knowledge_graph.go +++ b/internal/tools/knowledge_graph.go @@ -28,7 +28,7 @@ func (t *KnowledgeGraphSearchTool) SetKGStore(ks store.KnowledgeGraphStore) { func (t *KnowledgeGraphSearchTool) Name() string { return "knowledge_graph_search" } func (t *KnowledgeGraphSearchTool) Description() string { - return "Search the knowledge graph for entities and their relationships. Use specific names or keywords (e.g. a person's name, project name) — not generic words like 'entities' or 'all'. Use query='*' to list all entities. Supports graph traversal via entity_id to discover multi-hop relationships." + return "Search the knowledge graph to find people, projects, organizations, and how they connect. Better than memory_search when you need: who works with whom, what projects someone is involved in, dependencies between tasks, or any multi-hop relationship question. Use specific names (e.g. 'Minh', 'GoClaw') — not generic words. Use query='*' to list all known entities. Use entity_id to traverse connections from a specific entity." } func (t *KnowledgeGraphSearchTool) Parameters() map[string]any { diff --git a/internal/tools/memory.go b/internal/tools/memory.go index c2dd43c5..bd97708c 100644 --- a/internal/tools/memory.go +++ b/internal/tools/memory.go @@ -14,6 +14,7 @@ import ( // MemorySearchTool implements the memory_search tool for hybrid semantic + FTS search. type MemorySearchTool struct { memStore store.MemoryStore // Postgres-backed + hasKG bool // knowledge_graph_search tool is available } func NewMemorySearchTool() *MemorySearchTool { @@ -25,6 +26,11 @@ func (t *MemorySearchTool) SetMemoryStore(ms store.MemoryStore) { t.memStore = ms } +// SetHasKG enables the KG hint in search results. +func (t *MemorySearchTool) SetHasKG(has bool) { + t.hasKG = has +} + func (t *MemorySearchTool) Name() string { return "memory_search" } func (t *MemorySearchTool) Description() string { @@ -100,10 +106,14 @@ func (t *MemorySearchTool) Execute(ctx context.Context, args map[string]any) *Re return NewResult("No memory results found for query: " + query) } - data, _ := json.MarshalIndent(map[string]any{ + output := map[string]any{ "results": results, "count": len(results), - }, "", " ") + } + if t.hasKG { + output["hint"] = "Also run knowledge_graph_search if the query involves people, teams, projects, or connections between entities." + } + data, _ := json.MarshalIndent(output, "", " ") return NewResult(string(data)) }