本文档将论文中的 LIGHT 系统整理成可落地的工程方案,重点说明三层记忆结构、读写链路、核心数据结构,以及在产品化实现时需要重点处理的问题。
LIGHT 不是单纯把上下文窗口做大,而是把长期记忆拆成三层:
episodic memory:负责从历史对话中检索证据working memory:负责保留最近上下文scratchpad:负责沉淀长期稳定的高价值事实回答时由统一的记忆编排器组合这三层记忆,再交给生成模型。
┌────────────────────────────────────────────────────┐
│ 用户对话流 │
└────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 回合后处理器 │
├────────────────────────────────────────────────────┤
│ 1. 保存原始 turn 到会话存储 │
│ 2. 提取 key-value 和摘要,写入情节记忆 │
│ 3. 提炼显著事实,增量更新 scratchpad │
└────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 用户新问题到达 │
└────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────┐
│ 记忆编排器 │
├────────────────────────────────────────────────────┤
│ 1. 检索 episodic top-k │
│ 2. 读取最近 N 轮 working memory │
│ 3. 对 scratchpad 分块并过滤相关内容 │
│ 4. 组装 prompt,交给回答模型 │
└────────────────────────────────────────────────────┘
episodic memory作用:
写入方式:
user-assistant turn pair 结束后执行一次key-value pairssummary建议字段:
{
"session_id": "session-001",
"turn_id": 1842,
"timestamp": "2026-03-19T18:00:00+08:00",
"entities": [
{"key": "居住城市", "value": "苏州"},
{"key": "宠物名字", "value": "团子"}
],
"summary": "用户提到已搬到苏州,并养了一只叫团子的猫。",
"raw_span": "原始对话片段",
"embedding_text": "居住城市=苏州;宠物名字=团子;用户提到已搬到苏州,并养了一只叫团子的猫。"
}
工程建议:
top-k 不要盲目调大,过多召回会引入噪声working memory作用:
实现方式:
N 轮token budget 截断,而不是写死固定轮数工程建议:
scratchpad作用:
写入方式:
推荐分槽位组织,而不是一整段自由文本:
{
"profile": [
"用户当前住在苏州"
],
"preferences": [
"默认使用简体中文",
"倾向简洁回答"
],
"instructions": [
"代码注释使用中文",
"避免未验证结论"
],
"timeline": [
"2025-12: 从杭州搬到苏州",
"2026-04: 计划去东京出差"
],
"open_loops": [
"简历修改尚未完成"
]
}
工程建议:
profile / preferences / instructions / timeline / open_loops 分开维护每轮对话结束后:
key-value + summary收到新问题后:
top-k建议至少拆成四类存储:
conversation_store:保存原始对话 turnepisodic_index:保存摘要、结构化提取、embedding、原始片段引用scratchpad_store:保存长期摘要与槽位化事实memory_metadata:保存版本号、状态、更新时间、冲突标记可选状态字段:
active:当前有效outdated:已过期contradicted:被冲突信息覆盖tentative:不确定信息,需后续确认记忆编排器是系统核心,职责包括:
一个简单的优先级顺序可以是:
最小版本只需要五个组件:
conversation_storeepisodic_indexerscratchpad_managermemory_orchestratorresponse_generator伪代码:
def on_turn_end(session_id, user_msg, assistant_msg):
save_raw_turn(session_id, user_msg, assistant_msg)
memory_item = extract_kv_and_summary(user_msg, assistant_msg)
vector_store.upsert(session_id, memory_item)
scratchpad = load_scratchpad(session_id)
scratchpad = update_scratchpad(scratchpad, user_msg, assistant_msg)
if token_len(scratchpad) > 30000:
scratchpad = compress_scratchpad(scratchpad, target_tokens=15000)
save_scratchpad(session_id, scratchpad)
def answer(session_id, question):
episodic_docs = retrieve_topk(session_id, question, k=15)
recent_turns = load_recent_turns(session_id, token_budget=8000)
scratchpad = load_scratchpad(session_id)
scratch_chunks = semantic_chunk(scratchpad)
filtered_scratch = filter_relevant_chunks(question, scratch_chunks)
prompt = build_prompt(
question=question,
episodic_memory=episodic_docs,
working_memory=recent_turns,
scratchpad=filtered_scratch,
)
return llm_generate(prompt)
普通 RAG 常见流程是:
LIGHT 的差异在于:
同一事实会变化,例如“住在杭州”后来变成“搬到苏州”。系统不能只追加,不做状态管理。
不同时间的记忆可能互相冲突。系统需要判断:
如果长期只追加不整理,scratchpad 会退化成另一份噪声上下文,导致检索和回答质量持续下降。
每轮写入依赖 LLM 提取。如果提取错误,错误会长期保留并反复影响后续回答。
如果从论文原型继续往前做,建议增加以下能力:
LIGHT 适合以下系统:
如果把 LIGHT 思路引入当前小说写作系统,可以这样映射:
这样可以把“长篇连载的一致性问题”从单次检索,升级成持续维护的记忆闭环。
LIGHT 的核心价值不在于更大的上下文窗口,而在于把长期记忆拆成:
工程上最值得复用的是这套分层记忆与统一编排思路,而不是某个固定模型或具体实现细节。