refactor(llm, memory): Use generic types prompt builder and prompt component for actor,intent and handoff

This commit is contained in:
2026-07-19 18:20:28 +05:30
parent 1e34becec7
commit ee25bf4a4c
11 changed files with 267 additions and 158 deletions

View File

@@ -0,0 +1,59 @@
import { Entity } from "@omnia/core";
import { PromptBreakdown, PromptComponent } from "@omnia/llm";
import { BufferEntry, serializeSubjectiveBufferEntry } from "./buffer.js";
/**
* Prompt builder for the Handoff Engine.
* Separates prompt generation, structure, and component breakdowns.
*/
export class HandoffPromptBuilder {
build(entity: Entity, candidates: BufferEntry[], now: Date): PromptBreakdown {
const candidatesList = candidates
.map((entry) => {
const serialized = serializeSubjectiveBufferEntry(entry, entity);
return `ID: ${entry.id} | Timestamp: ${entry.timestamp} | Location: ${entry.locationId || "None"}\nContent: ${serialized}`;
})
.join("\n---\n");
const systemPrompt = `
You are the memory Handoff Engine. Your task is to process a list of Cognitive Buffer entries for an entity and select which memories to promote to the Memory Ledger, and which to forget or summarize.
Instructions:
1. **Cluster** related consecutive buffer entries into high-level narrative beats or events (e.g. physical action and its outcome or trivial actions). Combine them into a single chunk.
2. **Write in the third-person** for the events of other entities. (eg. Alan did that. Sarah did this, etc)
2. **Write in first-person for the events that you yourself did. (eg. I did this, I did that.)
3. **verbatim Quotes**: Extract verbatim, high-salience quotes from dialogue if relevant. Do not modify or invent quotes.
4. **Determine Importance**: Assign an importance score from 1 (trivial, e.g. waking up) to 10 (life-altering, e.g. witnessing a crime).
4. Discard small body movements like looking around, sighing, etc that do not contextually hold any meaning after it is done.
5. **Involved Entities**: Identify all entity IDs involved in the memories in this chunk.
6. **Retain in Cognitive Buffer (Pinning)**: If a beat represents an unresolved high-stakes situation (e.g. a standing threat, an unanswered accusation, an ongoing chase or conflict), set "retainInBuffer" to true so it remains in the Cognitive Buffer for immediate context. Otherwise, set it to false so it is safely pruned from the Cognitive Buffer.
7. **Exclude stage business**: Glances, sighs, ambient noticing, and irrelevant sensory details should be ignored and not included in any promoted chunk. They will be forgotten.
8. **Forget by omission**: Any buffer entry ID that you do not include in any chunk's "sourceEntryIds" will be permanently deleted and forgotten.
`.trim();
const entityContext = `
Subject Entity ID: ${entity.id}
Current Time: ${now.toISOString()}
`.trim();
const candidatesSection = `Cognitive Buffer Candidates for Handoff:\n${candidatesList}`;
const userContext = `${entityContext}\n\n${candidatesSection}`;
const components: PromptComponent[] = [
{ label: "System Prompt", type: "system", content: systemPrompt },
{ label: "Entity Context", type: "world", content: entityContext },
{
label: "Cognitive Candidates",
type: "input",
content: candidatesSection,
},
];
return {
systemPrompt,
userContext,
components,
};
}
}

View File

@@ -6,7 +6,8 @@ import {
BufferRepository,
} from "./buffer.js";
import { LedgerEntry, LedgerRepository } from "./ledger.js";
import { ILLMProvider, IEmbeddingProvider } from "@omnia/llm";
import { ILLMProvider, IEmbeddingProvider, PromptComponent } from "@omnia/llm";
import { HandoffPromptBuilder } from "./handoff-prompt-builder.js";
export const HandoffChunkSchema = z.object({
sourceEntryIds: z.array(z.string()), // buffer rows this chunk consumes
@@ -202,55 +203,44 @@ export function splitBufferForHandoff(
/**
* HandoffEngine processes memory handoffs using LLM summarization and DB transactions.
*/
export interface HandoffRunResult {
success: boolean;
systemPrompt?: string;
userContext?: string;
promptComponents?: PromptComponent[];
response?: any;
}
export class HandoffEngine {
public lastResult: HandoffRunResult | null = null;
private promptBuilder: HandoffPromptBuilder;
constructor(
private llmProvider: ILLMProvider,
private embedProvider: IEmbeddingProvider,
private bufferRepo: BufferRepository,
private ledgerRepo: LedgerRepository,
) {}
) {
this.promptBuilder = new HandoffPromptBuilder();
}
async runHandoff(
entity: Entity,
bufferEntries: BufferEntry[],
now: Date,
): Promise<boolean> {
this.lastResult = null;
const { candidates } = splitBufferForHandoff(bufferEntries, now);
if (candidates.length === 0) {
return false;
}
const candidatesList = candidates
.map((entry) => {
const serialized = serializeSubjectiveBufferEntry(entry, entity);
return `ID: ${entry.id} | Timestamp: ${entry.timestamp} | Location: ${entry.locationId || "None"}\nContent: ${serialized}`;
})
.join("\n---\n");
const systemPrompt = `
You are the memory Handoff Engine. Your task is to process a list of Cognitive Buffer entries for an entity and select which memories to promote to the Memory Ledger, and which to forget or summarize.
Instructions:
1. **Cluster** related consecutive buffer entries into high-level narrative beats or events (e.g. physical action and its outcome or trivial actions). Combine them into a single chunk.
2. **Write in the third-person** for the events of other entities. (eg. Alan did that. Sarah did this, etc)
2. **Write in first-person for the events that you yourself did. (eg. I did this, I did that.)
3. **verbatim Quotes**: Extract verbatim, high-salience quotes from dialogue if relevant. Do not modify or invent quotes.
4. **Determine Importance**: Assign an importance score from 1 (trivial, e.g. waking up) to 10 (life-altering, e.g. witnessing a crime).
4. Discard small body movements like looking around, sighing, etc that do not contextually hold any meaning after it is done.
5. **Involved Entities**: Identify all entity IDs involved in the memories in this chunk.
6. **Retain in Cognitive Buffer (Pinning)**: If a beat represents an unresolved high-stakes situation (e.g. a standing threat, an unanswered accusation, an ongoing chase or conflict), set "retainInBuffer" to true so it remains in the Cognitive Buffer for immediate context. Otherwise, set it to false so it is safely pruned from the Cognitive Buffer.
7. **Exclude stage business**: Glances, sighs, ambient noticing, and irrelevant sensory details should be ignored and not included in any promoted chunk. They will be forgotten.
8. **Forget by omission**: Any buffer entry ID that you do not include in any chunk's "sourceEntryIds" will be permanently deleted and forgotten.
`.trim();
const userContext = `
Subject Entity ID: ${entity.id}
Current Time: ${now.toISOString()}
Cognitive Buffer Candidates for Handoff:
${candidatesList}
`.trim();
const { systemPrompt, userContext, components } = this.promptBuilder.build(
entity,
candidates,
now,
);
const response = await this.llmProvider.generateStructuredResponse({
systemPrompt,
@@ -259,9 +249,23 @@ ${candidatesList}
});
if (!response.success || !response.data) {
this.lastResult = {
success: false,
systemPrompt,
userContext,
promptComponents: components,
};
return false;
}
this.lastResult = {
success: true,
systemPrompt,
userContext,
promptComponents: components,
response: response.data,
};
const result = response.data;
const db = (this.bufferRepo as any).db;