mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 20:12:48 +05:30
refactor(intent): Improve intent decoder userContext structure
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { WorldState } from "@omnia/core";
|
||||
import { WorldState, resolveAlias } from "@omnia/core";
|
||||
import { ILLMProvider } from "@omnia/llm";
|
||||
import { IntentSequence, LLMIntentSequenceSchema } from "./intent.js";
|
||||
import { Intent, IntentSequence, LLMIntentSequenceSchema } from "./intent.js";
|
||||
|
||||
export class IntentDecoder {
|
||||
constructor(private llmProvider: ILLMProvider) {}
|
||||
@@ -18,20 +18,47 @@ export class IntentDecoder {
|
||||
worldState: WorldState,
|
||||
actorId: string,
|
||||
narrativeProse: string,
|
||||
recentIntents: Intent[] = [],
|
||||
): Promise<IntentSequence> {
|
||||
const entityIds = Array.from(worldState.entities.keys());
|
||||
const actor = worldState.getEntity(actorId);
|
||||
|
||||
const aliasEntries = actor ? Array.from(actor.aliases.entries()) : [];
|
||||
const aliasContext =
|
||||
aliasEntries.length > 0
|
||||
? aliasEntries
|
||||
.map(
|
||||
([targetId, alias]) =>
|
||||
`- "${alias}" refers to entity ID: "${targetId}"`,
|
||||
)
|
||||
.join("\n")
|
||||
: "(No known aliases)";
|
||||
// 1. Get other entities co-located in the same context
|
||||
const otherEntitiesLines: string[] = [];
|
||||
for (const otherEntity of worldState.entities.values()) {
|
||||
if (
|
||||
otherEntity.id !== actorId &&
|
||||
otherEntity.locationId === actor?.locationId
|
||||
) {
|
||||
const alias = actor
|
||||
? resolveAlias(actor, otherEntity.id)
|
||||
: otherEntity.id;
|
||||
otherEntitiesLines.push(` - Alias="${alias}" ID=${otherEntity.id}`);
|
||||
}
|
||||
}
|
||||
const otherEntitiesContext =
|
||||
otherEntitiesLines.length > 0
|
||||
? otherEntitiesLines.join("\n")
|
||||
: " (No other entities in context)";
|
||||
|
||||
// 2. Format historical context (2-3 recent intents received by the actor)
|
||||
const historicalLines: string[] = [];
|
||||
for (const prior of recentIntents) {
|
||||
const targetIds =
|
||||
prior.actorId !== actorId ? [prior.actorId] : prior.targetIds;
|
||||
const targetsStr = targetIds
|
||||
.map((tid) => {
|
||||
const alias = actor ? resolveAlias(actor, tid) : tid;
|
||||
return `(Alias="${alias}", ID="${tid}")`;
|
||||
})
|
||||
.join(", ");
|
||||
historicalLines.push(
|
||||
` - Content: "${prior.originalText}", Type: ${prior.type}, Target Entities: ${targetsStr || "None"}`,
|
||||
);
|
||||
}
|
||||
const historicalContext =
|
||||
historicalLines.length > 0
|
||||
? historicalLines.join("\n")
|
||||
: " (No prior intents in context)";
|
||||
|
||||
const systemPrompt = `
|
||||
You are the Intent Decoder for a narrative simulation engine.
|
||||
@@ -47,23 +74,16 @@ For each intent you must:
|
||||
- "description": No subject or name — a bare third-person verb phrase only (e.g. "clears their throat", "shakes their head slowly")
|
||||
- "selfDescription": The same event from the actor's own perspective, second person, complete sentence starting with "You" (e.g. "You clear your throat.", "You shake your head slowly."). This is shown directly in the actor's own memory — it must never say "the actor" or refer to them in the third person.
|
||||
- In case of a dialogue, the description and self Description only stores the exact words said by the entity. (e.g. "I will do that later", "Are you serious right now?")
|
||||
4. Identify targetIds — the entity IDs of the receiving parties. Use the "KNOWN ENTITY IDS" mapping to resolve any subjective names,or aliases used in the prose to their correct system entity IDs. If no specific target, use an empty array.
|
||||
4. Identify targetIds — the entity IDs of the receiving parties. Use the "Other entities in context" list to resolve any subjective names, aliases, or descriptions used in the prose to their correct entity IDs. If no specific target, use an empty array.
|
||||
5. Identify modifiers — a list of strings representing additional qualities or modifiers extracted from the narrative prose. This includes emotions, tone of voice, speed, manner of action, or statement type (e.g., "question", "anxious", "whispering", "slowly", "quietly", "forcefully"). If no modifiers are present, use an empty array.
|
||||
`.trim();
|
||||
|
||||
const userContext = `
|
||||
=== KNOWN ENTITY IDS ===
|
||||
${entityIds.length > 0 ? entityIds.join(", ") : "(No entities)"}
|
||||
|
||||
=== ACTOR ALIASES ===
|
||||
The actor refers to other entities using these subjective names/aliases:
|
||||
${aliasContext}
|
||||
|
||||
=== WORLD STATE ===
|
||||
${serializeSimplifiedWorldState(worldState)}
|
||||
|
||||
=== ACTOR ===
|
||||
Actor ID: ${actorId}
|
||||
Intent Source: ${actorId}
|
||||
Other entities in context:
|
||||
${otherEntitiesContext}
|
||||
Historical Context:
|
||||
${historicalContext}
|
||||
|
||||
=== NARRATIVE PROSE ===
|
||||
${narrativeProse}
|
||||
@@ -91,32 +111,3 @@ ${narrativeProse}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function serializeSimplifiedWorldState(worldState: WorldState): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push("Locations:");
|
||||
if (worldState.locations.size > 0) {
|
||||
for (const loc of worldState.locations.values()) {
|
||||
const parentId = (loc as { parentId?: string | null }).parentId;
|
||||
const parentStr = parentId ? ` (Parent: ${parentId})` : "";
|
||||
lines.push(` - Location [ID: ${loc.id}]${parentStr}`);
|
||||
}
|
||||
} else {
|
||||
lines.push(" (No locations)");
|
||||
}
|
||||
|
||||
lines.push("Entities:");
|
||||
if (worldState.entities.size > 0) {
|
||||
for (const entity of worldState.entities.values()) {
|
||||
const locStr = entity.locationId
|
||||
? ` (Location: ${entity.locationId})`
|
||||
: "";
|
||||
lines.push(` - Entity [ID: ${entity.id}]${locStr}`);
|
||||
}
|
||||
} else {
|
||||
lines.push(" (No entities)");
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user