story-summary: facts migration + recall enhancements
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
// LLM Service
|
||||
|
||||
const PROVIDER_MAP = {
|
||||
// ...
|
||||
openai: "openai",
|
||||
google: "gemini",
|
||||
gemini: "gemini",
|
||||
@@ -39,43 +38,37 @@ Incremental_Summary_Requirements:
|
||||
- Causal_Chain: 为每个新事件标注直接前因事件ID(causedBy)。仅在因果关系明确(直接导致/明确动机/承接后果)时填写;不明确时填[]完全正常。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 选 1:status / 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,
|
||||
|
||||
Reference in New Issue
Block a user