|
| 1 | +/** |
| 2 | + * @fileoverview Memory-specific HyDE (Hypothetical Document Embedding) retriever. |
| 3 | + * |
| 4 | + * Improves memory recall for vague or abstract queries by generating a |
| 5 | + * hypothetical memory trace BEFORE embedding. The hypothesis is closer |
| 6 | + * in embedding space to actual stored traces than the raw query. |
| 7 | + * |
| 8 | + * Cognitive science grounding: this mirrors the "generation effect" — |
| 9 | + * generating information about a topic activates related neural pathways |
| 10 | + * more strongly than passive recognition. By generating what a memory |
| 11 | + * WOULD look like, we activate the right retrieval pathways. |
| 12 | + * |
| 13 | + * Effective for: |
| 14 | + * - Abstract queries ("that deployment discussion") |
| 15 | + * - Emotional recall ("when they were upset") |
| 16 | + * - Temporal queries ("something from last week") |
| 17 | + * - Vague references ("the thing about cats") |
| 18 | + * |
| 19 | + * Auto-attached by CognitiveMemoryManager when any LLM invoker is available. |
| 20 | + * Remains opt-in per query via `options.hyde: true` on `retrieve()`. |
| 21 | + * |
| 22 | + * @module agentos/memory/retrieval/hyde/MemoryHydeRetriever |
| 23 | + * @see {@link CognitiveMemoryManager.retrieve} — consumes the hypothesis |
| 24 | + */ |
| 25 | + |
| 26 | +/** LLM invoker function signature matching AgentOS observer/reflector convention. */ |
| 27 | +type LlmInvoker = (systemPrompt: string, userPrompt: string) => Promise<string>; |
| 28 | + |
| 29 | +/** |
| 30 | + * System prompt for hypothesis generation. |
| 31 | + * |
| 32 | + * Instructs the LLM to output what a STORED memory trace would look like, |
| 33 | + * not to answer the query. This produces embeddings that are semantically |
| 34 | + * closer to actual stored traces than raw recall queries. |
| 35 | + */ |
| 36 | +const HYDE_SYSTEM_PROMPT = `You are a memory system. Given a recall query, generate what a stored memory trace about this topic would look like. Write it as a first-person observation note, 1-2 sentences, as if you recorded it when it happened. |
| 37 | +
|
| 38 | +Do NOT answer the query. Generate what the STORED MEMORY would say. |
| 39 | +
|
| 40 | +Examples: |
| 41 | +Query: "what does the user do for work?" |
| 42 | +Hypothesis: "User mentioned they are a software engineer working on backend systems at a startup." |
| 43 | +
|
| 44 | +Query: "when were they upset?" |
| 45 | +Hypothesis: "User expressed frustration and stress about a missed deadline. Emotional tone was tense." |
| 46 | +
|
| 47 | +Query: "that thing about cats" |
| 48 | +Hypothesis: "User talked about having two cats named Luna and Mochi. They seem important to the user."`; |
| 49 | + |
| 50 | +/** |
| 51 | + * Memory-specific HyDE retriever that generates hypothetical memory traces. |
| 52 | + * |
| 53 | + * Implements the same `generateHypothesis()` interface expected by |
| 54 | + * CognitiveMemoryManager so it can be assigned via `setHydeRetriever()`. |
| 55 | + * |
| 56 | + * Lightweight: uses `maxTokens: 150` with no chain-of-thought. Target |
| 57 | + * latency is under 500ms with a fast model. |
| 58 | + * |
| 59 | + * @example |
| 60 | + * ```ts |
| 61 | + * const retriever = new MemoryHydeRetriever(llmInvoker); |
| 62 | + * const result = await retriever.generateHypothesis('what does the user like?'); |
| 63 | + * // result.hypothesis = "User mentioned they enjoy hiking and cooking..." |
| 64 | + * ``` |
| 65 | + */ |
| 66 | +export class MemoryHydeRetriever { |
| 67 | + private readonly llmInvoker: LlmInvoker; |
| 68 | + |
| 69 | + /** |
| 70 | + * @param llmInvoker - Function that calls an LLM with (systemPrompt, userPrompt). |
| 71 | + * Typically reused from the observer, reflector, or feature detection config. |
| 72 | + */ |
| 73 | + constructor(llmInvoker: LlmInvoker) { |
| 74 | + this.llmInvoker = llmInvoker; |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * Generate a hypothetical memory trace for a recall query. |
| 79 | + * |
| 80 | + * The generated hypothesis is used as the embedding input for vector |
| 81 | + * search, producing results that are more semantically aligned with |
| 82 | + * actual stored traces. |
| 83 | + * |
| 84 | + * Returns the same shape as `HydeRetriever.generateHypothesis()` so |
| 85 | + * CognitiveMemoryManager can use it interchangeably. |
| 86 | + * |
| 87 | + * @param query - The recall query (e.g., "what does the user do for work?") |
| 88 | + * @returns Object with `hypothesis` text and `latencyMs` timing |
| 89 | + */ |
| 90 | + async generateHypothesis(query: string): Promise<{ hypothesis: string; latencyMs: number }> { |
| 91 | + const start = Date.now(); |
| 92 | + try { |
| 93 | + const hypothesis = await this.llmInvoker(HYDE_SYSTEM_PROMPT, `Query: "${query}"`); |
| 94 | + return { |
| 95 | + hypothesis: hypothesis.trim(), |
| 96 | + latencyMs: Date.now() - start, |
| 97 | + }; |
| 98 | + } catch { |
| 99 | + // HyDE is non-critical — return empty hypothesis to fall through to raw query |
| 100 | + return { hypothesis: '', latencyMs: Date.now() - start }; |
| 101 | + } |
| 102 | + } |
| 103 | +} |
0 commit comments