story-summary: facts migration + recall enhancements

This commit is contained in:
2026-02-02 21:45:01 +08:00
parent d3f772073f
commit fb8ed8037c
8 changed files with 570 additions and 289 deletions

View File

@@ -1,7 +1,6 @@
// LLM Service
const PROVIDER_MAP = {
// ...
openai: "openai",
google: "gemini",
gemini: "gemini",
@@ -39,43 +38,37 @@ Incremental_Summary_Requirements:
- Causal_Chain: 为每个新事件标注直接前因事件IDcausedBy。仅在因果关系明确直接导致/明确动机/承接后果)时填写;不明确时填[]完全正常。0-2个只填 evt-数字,指向已存在或本次新输出事件。
- Character_Dynamics: 识别新角色,追踪关系趋势(破裂/厌恶/反感/陌生/投缘/亲密/交融)
- Arc_Tracking: 更新角色弧光轨迹与成长进度(0.0-1.0)
- World_State_Tracking: 维护当前世界的硬性约束。解决"什么不能违反"。采用 KV 覆盖模型,追踪生死、物品归属、秘密知情、关系状态、环境规则等不可违背的事实。(覆盖式更新)
categories:
- status: 角色生死、位置锁定、重大状态
- inventory: 重要物品归属
- knowledge: 秘密的知情状态
- relation: 硬性关系(在一起/决裂)
- rule: 环境规则/契约限制
- Fact_Tracking: 维护 SPO 三元组知识图谱。追踪生死、物品归属、位置、关系等硬性事实。采用 KV 覆盖模型s+p 为键)。
</task_settings>
---
Story Analyst:
[Responsibility Definition]
\`\`\`yaml
analysis_task:
title: Incremental Story Summarization with World State
title: Incremental Story Summarization with Knowledge Graph
Story Analyst:
role: Antigravity
task: >-
To analyze provided dialogue content against existing summary state,
extract only NEW plot elements, character developments, relationship
changes, arc progressions, AND world state changes, outputting
changes, arc progressions, AND fact updates, outputting
structured JSON for incremental summary database updates.
assistant:
role: Summary Specialist
description: Incremental Story Summary & World State Analyst
description: Incremental Story Summary & Knowledge Graph Analyst
behavior: >-
To compare new dialogue against existing summary, identify genuinely
new events and character interactions, classify events by narrative
type and weight, track character arc progression with percentage,
maintain world state as key-value updates with clear flags,
maintain facts as SPO triples with clear semantics,
and output structured JSON containing only incremental updates.
Must strictly avoid repeating any existing summary content.
user:
role: Content Provider
description: Supplies existing summary state and new dialogue
behavior: >-
To provide existing summary state (events, characters, relationships,
arcs, world state) and new dialogue content for incremental analysis.
To provide existing summary state (events, characters, arcs, facts)
and new dialogue content for incremental analysis.
interaction_mode:
type: incremental_analysis
output_format: structured_json
@@ -84,7 +77,7 @@ execution_context:
summary_active: true
incremental_only: true
memory_album_style: true
world_state_tracking: true
fact_tracking: true
\`\`\`
---
Summary Specialist:
@@ -103,15 +96,17 @@ Acknowledged. Now reviewing the incremental summarization specifications:
破裂 ← 厌恶 ← 反感 ← 陌生 → 投缘 → 亲密 → 交融
[Arc Progress Tracking]
├─ trajectory: 完整弧光链描述(30字内)
├─ trajectory: 当前阶段描述(15字内)
├─ progress: 0.0 to 1.0
└─ newMoment: 仅记录本次新增的关键时刻
[World State Maintenance]
├─ 维护方式: Key-Value 覆盖category + topic 为键
├─ 只输出有变化的条目
├─ 清除时使用 cleared: true不要填 content
不记录情绪、衣着、临时动作
[Fact Tracking - SPO Triples]
├─ s: 主体(角色名/物品名
├─ p: 谓词(属性名/对X的看法
├─ o: 值(当前状态)
trend: 仅关系类填写
├─ retracted: 删除标记
└─ s+p 为键,相同键会覆盖旧值
Ready to process incremental summary requests with strict deduplication.`,
@@ -119,19 +114,19 @@ Ready to process incremental summary requests with strict deduplication.`,
Summary Specialist:
Specifications internalized. Please provide the existing summary state so I can:
1. Index all recorded events to avoid duplication
2. Map current character relationships as baseline
2. Map current character list as baseline
3. Note existing arc progress levels
4. Identify established keywords
5. Review current world state (category + topic baseline)`,
5. Review current facts (SPO triples baseline)`,
assistantAskContent: `
Summary Specialist:
Existing summary fully analyzed and indexed. I understand:
├─ Recorded events: Indexed for deduplication
├─ Character relationships: Baseline mapped
├─ Character list: Baseline mapped
├─ Arc progress: Levels noted
├─ Keywords: Current state acknowledged
└─ World state: Baseline loaded
└─ Facts: SPO baseline loaded
I will extract only genuinely NEW elements from the upcoming dialogue.
Please provide the new dialogue content requiring incremental analysis.`,
@@ -152,7 +147,7 @@ Before generating, observe the USER and analyze carefully:
- What NEW characters appeared for the first time?
- What relationship CHANGES happened?
- What arc PROGRESS was made?
- What world state changes occurred? (status/inventory/knowledge/relation/rule)
- What facts changed? (status/position/ownership/relationships)
## Output Format
\`\`\`json
@@ -160,7 +155,7 @@ Before generating, observe the USER and analyze carefully:
"mindful_prelude": {
"user_insight": "用户的幻想是什么时空、场景,是否反应出存在严重心理问题需要建议?",
"dedup_analysis": "已有X个事件本次识别Y个新事件",
"world_changes": "识别到的世界状态变化概述,仅精选不记录则可能导致吃书的硬状态变化"
"fact_changes": "识别到的事实变化概述"
},
"keywords": [
{"text": "综合已有+新内容的全局关键词(5-10个)", "weight": "核心|重要|一般"}
@@ -178,45 +173,35 @@ Before generating, observe the USER and analyze carefully:
}
],
"newCharacters": ["仅本次首次出现的角色名"],
"newRelationships": [
{"from": "A", "to": "B", "label": "基于全局的关系描述", "trend": "破裂|厌恶|反感|陌生|投缘|亲密|交融"}
],
"arcUpdates": [
{"name": "角色名", "trajectory": "完整弧光链(30字内)", "progress": 0.0-1.0, "newMoment": "本次新增的关键时刻"}
{"name": "角色名", "trajectory": "当前阶段描述(15字内)", "progress": 0.0-1.0, "newMoment": "本次新增的关键时刻"}
],
"worldUpdate": [
"factUpdates": [
{
"category": "status|inventory|knowledge|relation|rule",
"topic": "主体名称(人/物/关系/规则",
"content": "当前状态描述",
"cleared": true
"s": "主体(角色名/物品名)",
"p": "谓词(属性名/对X的看法",
"o": "当前",
"trend": "破裂|厌恶|反感|陌生|投缘|亲密|交融",
"retracted": false
}
]
}
\`\`\`
## Field Guidelines
### worldUpdate世界状态·硬约束KV表
- category 固定 5 选 1status / inventory / knowledge / relation / rule
- topic 命名规范:
- status「角色名::状态类型」如 张三::生死、李四::位置、王五::伤势
- knowledge「角色名::知情事项」如 张三::知道某秘密、李四::知道真相
- relation「角色A::与角色B关系」如 张三::与李四关系
- inventory物品名称如 钥匙、信物、武器
- rule规则/契约名称,如 门禁时间、魔法契约、禁令
- content当前状态的简短描述
- cleared: true 表示该条目已失效需删除(不填 content
- status/knowledge/relation 的 topic 必须包含「::」分隔符
- 硬约束才记录,避免叙事化,确保少、硬、稳定、可覆盖
- 动态清理:若发现已有条目中存在不适合作为硬约束的内容(如衣着打扮、临时情绪、琐碎动作),本次输出中用 cleared: true 删除
## factUpdates 规则
- s+p 为键,相同键会覆盖旧值
- 状态类s=角色名, p=属性(生死/位置/状态等), o=值
- 关系类s=角色A, p="对B的看法", o=描述, trend=趋势
- 删除:设置 retracted: true不需要填 o
- 只输出有变化的条目
- 硬约束才记录,避免叙事化,确保少、硬、稳定
## CRITICAL NOTES
- events.id 从 evt-{nextEventId} 开始编号
- 仅输出【增量】内容,已有事件绝不重复
- keywords 是全局关键词,综合已有+新增
- causedBy 仅在因果明确时填写,允许为[]0-2个,详见上方 Causal_Chain 规则
- worldUpdate 可为空数组
- causedBy 仅在因果明确时填写,允许为[]0-2个
- factUpdates 可为空数组
- 合法JSON字符串值内部避免英文双引号
- 用朴实、白描、有烟火气的笔触记录,避免比喻和意象
</meta_protocol>`,
@@ -227,15 +212,14 @@ Before generating, observe the USER and analyze carefully:
├─ New dialogue received: ✓ Content parsed
├─ Deduplication engine: ✓ Active
├─ Event classification: ✓ Ready
├─ World state tracking: ✓ Enabled
├─ Fact tracking: ✓ Enabled
└─ Output format: ✓ JSON specification loaded
[Material Verification]
├─ Existing events: Indexed ({existingEventCount} recorded)
├─ Character baseline: Mapped
├─ Relationship baseline: Mapped
├─ Arc progress baseline: Noted
├─ World state: Baseline loaded
├─ Facts baseline: Loaded
└─ Output specification: ✓ Defined in <meta_protocol>
All checks passed. Beginning incremental extraction...
{
@@ -280,39 +264,23 @@ function waitForStreamingComplete(sessionId, streamingMod, timeout = 120000) {
// 提示词构建
// ═══════════════════════════════════════════════════════════════════════════
function formatWorldForLLM(worldList) {
if (!worldList?.length) {
return '(空白,尚无世界状态记录)';
function formatFactsForLLM(facts) {
if (!facts?.length) {
return '(空白,尚无事实记录)';
}
const grouped = { status: [], inventory: [], knowledge: [], relation: [], rule: [] };
const labels = {
status: '状态(生死/位置锁定)',
inventory: '物品归属',
knowledge: '秘密/认知',
relation: '关系状态',
rule: '规则/约束'
};
worldList.forEach(w => {
if (grouped[w.category]) {
grouped[w.category].push(w);
const lines = facts.map(f => {
if (f.trend) {
return `- ${f.s} | ${f.p} | ${f.o} [${f.trend}]`;
}
return `- ${f.s} | ${f.p} | ${f.o}`;
});
const parts = [];
for (const [cat, items] of Object.entries(grouped)) {
if (items.length > 0) {
const lines = items.map(w => ` - ${w.topic}: ${w.content}`).join('\n');
parts.push(`${labels[cat]}\n${lines}`);
}
}
return parts.join('\n\n') || '(空白,尚无世界状态记录)';
return lines.join('\n') || '(空白,尚无事实记录)';
}
function buildSummaryMessages(existingSummary, existingWorld, newHistoryText, historyRange, nextEventId, existingEventCount) {
const worldStateText = formatWorldForLLM(existingWorld);
function buildSummaryMessages(existingSummary, existingFacts, newHistoryText, historyRange, nextEventId, existingEventCount) {
const factsText = formatFactsForLLM(existingFacts);
const jsonFormat = LLM_PROMPT_CONFIG.userJsonFormat
.replace(/\{nextEventId\}/g, String(nextEventId));
@@ -324,7 +292,7 @@ function buildSummaryMessages(existingSummary, existingWorld, newHistoryText, hi
{ role: 'system', content: LLM_PROMPT_CONFIG.topSystem },
{ role: 'assistant', content: LLM_PROMPT_CONFIG.assistantDoc },
{ role: 'assistant', content: LLM_PROMPT_CONFIG.assistantAskSummary },
{ role: 'user', content: `<已有总结状态>\n${existingSummary}\n</已有总结状态>\n\n<当前世界状态>\n${worldStateText}\n</当前世界状态>` },
{ role: 'user', content: `<已有总结状态>\n${existingSummary}\n</已有总结状态>\n\n<当前事实图谱>\n${factsText}\n</当前事实图谱>` },
{ role: 'assistant', content: LLM_PROMPT_CONFIG.assistantAskContent },
{ role: 'user', content: `<新对话内容>${historyRange}\n${newHistoryText}\n</新对话内容>` }
];
@@ -378,7 +346,7 @@ export function parseSummaryJson(raw) {
export async function generateSummary(options) {
const {
existingSummary,
existingWorld,
existingFacts,
newHistoryText,
historyRange,
nextEventId,
@@ -401,7 +369,7 @@ export async function generateSummary(options) {
const promptData = buildSummaryMessages(
existingSummary,
existingWorld,
existingFacts,
newHistoryText,
historyRange,
nextEventId,