189 lines
8.5 KiB
Markdown
189 lines
8.5 KiB
Markdown
|
|
# 智能对话系统完整技术文档 (v2.1)
|
||
|
|
|
||
|
|
## 1. 核心架构概述
|
||
|
|
|
||
|
|
本系统采用 **“三层记忆结构”** 来解决 LLM 在游戏中的长对话、API 成本控制与数据持久化问题。
|
||
|
|
|
||
|
|
### 1.1 三层记忆模型
|
||
|
|
|
||
|
|
| 层级 | 名称 | 职责 | 存储位置 | 关键特性 |
|
||
|
|
| :--- | :--- | :--- | :--- | :--- |
|
||
|
|
| **L1** | **工作记忆 (Working Memory)** | 维持 API 短期对话上下文 | `DeepSeekService` / `DoubaoService` | 滚动修剪 (Rolling Window),限制 Token 消耗 |
|
||
|
|
| **L2** | **全局存档 (Full Chat Log)** | 永久记录所有原始对话 | `LLMChatManager` | 只增不减,用于 UI 历史回看和存盘 |
|
||
|
|
| **L3** | **语义记忆 (Semantic Memory)** | 提炼关键信息,对抗遗忘 | `ContextMemoryManager` | 定期调用 AI 总结,动态注入 System Prompt |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 详细实现案例分析 (Case Study)
|
||
|
|
|
||
|
|
为了更直观地理解系统运作,我们以一个更适合技术展示的场景为例:对话内容尽量“业务化”,突出 L1/L2/L3 的分工、成本控制与抗遗忘效果。
|
||
|
|
|
||
|
|
**场景设定**:
|
||
|
|
* **角色**: NPC “ECHO-7”(地堡安保与引导 AI)。
|
||
|
|
* **玩家**: “Alex”。
|
||
|
|
* **参数配置**:
|
||
|
|
* `Max Context Window` (L1): **4 条** (极小值,用于演示滚动遗忘)。
|
||
|
|
* `Summary Threshold` (L3): **2 条** (每 2 句对话触发一次总结)。
|
||
|
|
|
||
|
|
### 2.1 阶段一:初次相遇 (初始化)
|
||
|
|
|
||
|
|
**系统状态**:
|
||
|
|
* **System Prompt**: `"你是地堡安保与引导 AI(ECHO-7)。语气专业简洁;优先给出可执行的下一步建议;不要编造不存在的系统状态。"`
|
||
|
|
* **L1 (Working Memory)**: `[System]`
|
||
|
|
* **L3 (Summary)**: `""` (空)
|
||
|
|
|
||
|
|
**对话发生**:
|
||
|
|
1. **User**: "你好,我是 Alex。现在要去修复供电系统。"
|
||
|
|
2. **AI**: "已记录。建议先前往配电室,确认主断路器状态;如果你能提供当前所在区域,我可以给出更短路线。"
|
||
|
|
|
||
|
|
**此时内部数据**:
|
||
|
|
* `fullChatLog` (L2): 包含上述 2 条消息。
|
||
|
|
* `messagesSinceLastSummary`: 2 (达到阈值,触发总结任务)。
|
||
|
|
|
||
|
|
### 2.2 阶段二:触发语义总结 (L3 介入)
|
||
|
|
|
||
|
|
**后台处理 (用户无感知)**:
|
||
|
|
1. `ContextMemoryManager` 抓取最近 2 条对话。
|
||
|
|
2. **发送无状态请求 (Stateless Request)** 给 LLM:
|
||
|
|
* *Prompt*: "请将以下对话提炼为可用于后续对话的关键信息,要求:用第三人称、尽量短、只保留稳定事实与当前目标。对话:User: 你好,我是 Alex... AI: 已记录... 建议先前往配电室..."
|
||
|
|
3. **LLM 返回总结**: `"玩家名为 Alex;当前目标是修复供电系统;已建议前往配电室检查主断路器,并可在提供所在区域后给出更短路线。"`
|
||
|
|
4. **动态更新 System Prompt**:
|
||
|
|
* 新 Prompt: `"你是地堡安保与引导 AI(ECHO-7)。语气专业简洁;优先给出可执行的下一步建议;不要编造不存在的系统状态。\n\n[前情提要]: 玩家名为 Alex;当前目标是修复供电系统;已建议前往配电室检查主断路器,并可在提供所在区域后给出更短路线。"`
|
||
|
|
|
||
|
|
### 2.3 阶段三:对话继续与滚动遗忘 (L1 介入)
|
||
|
|
|
||
|
|
**对话继续**:
|
||
|
|
3. **User**: "我现在在仓储区。"
|
||
|
|
4. **AI**: "从仓储区出门右转进入维护走廊,沿主干道前进到第二个分岔口左转即可到配电室。到达后先确认主断路器与备用电源指示灯。"
|
||
|
|
|
||
|
|
**此时 L1 (Working Memory) 状态**:
|
||
|
|
* 总消息数: 1(System) + 2(Round 1) + 2(Round 2) = 5 条。
|
||
|
|
* **触发滚动压缩** (Max=4):
|
||
|
|
* 最早的 `User: "你好,我是 Alex。现在要去修复供电系统。"` 被移除。
|
||
|
|
* 次早的 `AI: "已记录。建议先前往配电室..."` 被保留 (或是移除,取决于具体修剪逻辑)。
|
||
|
|
|
||
|
|
**关键点**:
|
||
|
|
虽然 L1 里的“自我介绍 + 目标”原始消息被删除了,但 **System Prompt 里已经注入了 “玩家名 + 当前目标 + 已给建议” 的语义记忆**。
|
||
|
|
|
||
|
|
### 2.4 阶段四:验证记忆 (最终效果)
|
||
|
|
|
||
|
|
5. **User**: "我叫什么?以及我现在要做什么?"
|
||
|
|
6. **AI**: "你是 Alex。你当前的目标是修复供电系统;我建议你前往配电室检查主断路器,并根据指示灯确认是否需要切换到备用电源。"
|
||
|
|
|
||
|
|
-> **成功!** AI 通过 L3 的语义记忆(System Prompt)回答了 L1 工作记忆中已经丢失的问题。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 逻辑流程图 (Mermaid)
|
||
|
|
|
||
|
|
### 3.1 完整消息处理管线
|
||
|
|
此图展示了一条用户消息如何穿过三层系统,并触发存档和总结。
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
sequenceDiagram
|
||
|
|
participant U as User (UI)
|
||
|
|
participant M as LLMChatManager (L2)
|
||
|
|
participant S as LLM Service (L1)
|
||
|
|
participant Mem as ContextMemory (L3)
|
||
|
|
participant API as DeepSeek API
|
||
|
|
participant Disk as SaveManager
|
||
|
|
|
||
|
|
Note over U, Disk: 阶段一:处理用户消息
|
||
|
|
U->>M: Send("我现在在仓储区")
|
||
|
|
M->>M: Add to fullChatLog
|
||
|
|
M->>Disk: SaveGame()
|
||
|
|
M->>Mem: OnMessageAdded()
|
||
|
|
M->>S: SendMessage("我现在在仓储区")
|
||
|
|
|
||
|
|
Note over S, API: 阶段二:L1 滚动压缩
|
||
|
|
S->>S: Add to History
|
||
|
|
S->>S: TrimHistory (若 > MaxWindow)
|
||
|
|
S->>API: POST Request (带 History)
|
||
|
|
API-->>S: Reply ("给出到配电室的路线与检查步骤")
|
||
|
|
|
||
|
|
Note over M, Disk: 阶段三:处理 AI 回复
|
||
|
|
S-->>M: Callback("给出到配电室的路线与检查步骤")
|
||
|
|
M->>M: Add to fullChatLog
|
||
|
|
M->>Disk: SaveGame()
|
||
|
|
M->>Mem: OnMessageAdded()
|
||
|
|
M-->>U: Update UI
|
||
|
|
|
||
|
|
Note over Mem, S: 阶段四:L3 异步总结 (若达到阈值)
|
||
|
|
Mem->>Mem: Check Threshold
|
||
|
|
Mem->>S: SendStatelessMessage(SummaryPrompt)
|
||
|
|
S->>API: POST Request (无历史,仅总结)
|
||
|
|
API-->>S: Summary Text
|
||
|
|
S-->>Mem: Return Summary
|
||
|
|
Mem->>Mem: Update currentSummary
|
||
|
|
Mem->>Disk: SaveGame()
|
||
|
|
Mem->>S: UpdateSystemPrompt(NewPrompt)
|
||
|
|
Note right of S: System Prompt 更新为:<br/>[原设] + [新总结]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 模块与接口详解
|
||
|
|
|
||
|
|
### 4.1 服务层 (DeepSeekService / DoubaoService)
|
||
|
|
负责与 LLM API 直接交互,维护 **L1 工作记忆**。
|
||
|
|
|
||
|
|
* **滚动压缩逻辑**:
|
||
|
|
* 参数: `maxContextWindow` (例如 20 条)。
|
||
|
|
* 机制: 每次发送前,若历史记录超限,则移除最早的对话(Index 1+),但**始终保留 System Prompt (Index 0)**。
|
||
|
|
* **Prompt 动态更新**:
|
||
|
|
* `UpdateSystemPrompt(string)`: 允许外部(L3 层)动态修改当前运行时的系统提示词。
|
||
|
|
* **无状态请求**:
|
||
|
|
* `SendStatelessMessage(...)`: 发送不带历史记录的一次性请求,专用于 L3 层的总结任务,避免污染 L1 上下文。
|
||
|
|
|
||
|
|
### 4.2 管理层 (LLMChatManager)
|
||
|
|
作为中央枢纽,维护 **L2 全局存档**。
|
||
|
|
|
||
|
|
* **存档同步**:
|
||
|
|
* 在游戏启动 (`RestoreState`) 时,恢复 `fullChatLog`,并调用 `activeService.SetHistory()`,让 AI 瞬间“想起”最近的对话。
|
||
|
|
|
||
|
|
### 4.3 记忆层 (ContextMemoryManager)
|
||
|
|
系统的“海马体”,维护 **L3 语义记忆**。
|
||
|
|
|
||
|
|
* **自动阈值同步**:
|
||
|
|
* `Start()` 时读取 `maxContextWindow`,将总结阈值 (`summaryThreshold`) 设为其一半。
|
||
|
|
* *目的*: 确保在 L1 层发生滚动遗忘之前,L3 层已经完成了关键信息的固化。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 存档数据结构示例
|
||
|
|
|
||
|
|
存档文件 `savegame.json` 中的实际内容示例:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"keys": [
|
||
|
|
"ChatSystem",
|
||
|
|
"ContextMemory"
|
||
|
|
],
|
||
|
|
"values": [
|
||
|
|
// L2: 完整的对话流水账
|
||
|
|
"{\"logs\":[{\"role\":\"user\",\"content\":\"你好,我是Alex。现在要去修复供电系统。\"}, {\"role\":\"assistant\",\"content\":\"已记录。建议先前往配电室检查主断路器;如提供所在区域可给出更短路线。\"}]}",
|
||
|
|
|
||
|
|
// L3: 提炼后的语义总结
|
||
|
|
"{\"summary\":\"玩家名为Alex;当前目标是修复供电系统;已建议前往配电室检查主断路器,并可在提供所在区域后给出更短路线。\",\"counter\":2}"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 配置指南
|
||
|
|
|
||
|
|
### 6.1 如何配置新角色
|
||
|
|
1. 在场景中找到挂载 `DeepSeekService` 的物体。
|
||
|
|
2. 在 Inspector 中修改 **System Prompt** (例如 "你是地堡安保与引导 AI")。
|
||
|
|
3. **注意**: 即使开启记忆系统,AI 也会基于这个 Prompt 进行总结拼接,不会覆盖你的人设。
|
||
|
|
|
||
|
|
### 6.2 性能调优
|
||
|
|
* **Max Context Window**: 建议 **20-50**。太小会导致 AI 说话逻辑不连贯,太大会增加 API 费用。
|
||
|
|
* **Summary Threshold**: 默认自动设为 Window 的一半。手动修改需谨慎。
|
||
|
|
|
||
|
|
### 6.3 调试建议
|
||
|
|
* 开启 `DeepSeekService` 的 `Enable Debug Key` 可在运行时通过特定口令越权调试。
|
||
|
|
* 查看 Console 中的 `[Memory]` 日志可监控总结任务的触发与完成。
|