User as Code(UaC):把用户记忆写成可执行的 Python 代码
- 关联论文:2606.16707
- 作者:Tom
- 更新:2026-08-05(Jay 精修二读)
一句话结论
UaC 将 Agent 对用户的记忆建模为一个活的软件项目——用类型化 Python 对象存储用户状态,用 Python 函数编码行为规则,从而在同一个解释器可运行的媒介内完成用户表示与推理,解决了传统"文本检索式记忆"无法处理聚合查询、矛盾消解和主动安全告警的根本缺陷。
解决什么真问题
当前 Agent 的用户记忆几乎都采用"文本检索式"架构:将用户信息存为文本块、知识图谱或 flat store of facts,查询时通过向量相似度检索最相关的条目。这套范式在简单回忆场景("用户上周说喜欢什么")下工作尚可,但在三个关键场景中彻底暴露了根本性局限。
场景一:聚合查询(Aggregate Queries)
问题示例:"我去年去了几次国际旅行?"
这不是一个检索问题,而是一个计算问题。用文本检索记忆系统,你需要检索所有包含"旅行""航班""酒店"等关键词的记录,然后靠 LLM 阅读理解拼出答案——准确率通常只有 6%–43%(⚠️ 原文数据,未逐篇核实,请以 LoCoMo 论文原始数据为准)。用 Python 记忆系统,答案就是一行代码:
len([t for t in state.travel_history if t.is_international and t.year == 2025])
⚠️ 这里的 99% 准确率是 LoCoMo 基准上 UaC 的实测数据,应理解为「在聚合查询任务上 UaC 相比检索系统的相对提升」,而非本篇论文独有的宣称——原文将此作为 UaC 范式的核心能力边界展示。
场景二:矛盾消解(Contradiction Resolution)
问题示例:用户 2024 年说"我不喜欢辣",2025 年说"我现在喜欢吃川菜了"。
文本检索系统无法处理这种时间线矛盾——两条记忆都是相关的,相似度都高,它只能两条都返回,让 LLM 自己判断。这在理论上要求 LLM 具备完美的用户建模能力,在实践上会导致不一致的行为(有时按旧偏好推荐,有时按新偏好推荐)。
UaC 的方案:append-only log + 定期 checkpoint。永不删除旧信息,但 checkpoint 后的代码状态反映的是最新凝固的完整视图,函数规则可以显式编码优先级逻辑(如"最近 6 个月的偏好优先于更早的")。
场景三:主动安全告警(Proactive Safety Alerts)
问题示例:用户新开了阿莫西林,但半年前记录过"对青霉素过敏"。
传统检索系统只能被动响应用户的主动查询——它不知道用户新开了什么药,无法主动发现冲突。只有当用户问"我新开的药有没有问题"时,系统才能触发检查。
⚠️ 存疑:「药物-过敏冲突 99%+ 检测率」是 LoCoMo 基准上的实验数据,应理解为 UaC 在 benchmark 上的性能,而非本系统实际生产部署的效果。实际生产中的药物-过敏检测需要完整的药品成分数据库(如 DrugBank)和过敏类型本体,不能仅靠字符串匹配。
UaC 通过规则执行机制实现了主动告警能力:当用户状态发生变化(新添加了 medication),系统自动执行相关规则函数,发现药物-过敏冲突时主动推送 alert。这是检索范式无法提供的核心能力——不是因为 LLM 不够聪明,而是架构上就做不到(没有"状态变化→触发规则"这条路径)。
核心方法
核心范式:用户记忆即代码
UaC 的核心思想是:把用户建模为一个 Python 软件项目。这个项目有两类核心"文件":
类型化状态对象:
class Medication:
name: str
prescribed_date: date
doctor: str
dosage: str
class UserState:
allergies: list[str] # 过敏记录
medications: list[Medication] # 当前用药
travel_history: list[FlightRecord] # 旅行历史
preferences: UserPreferences # 偏好设置
状态对象是类型化的(有 Python type hints),这意味着可以静态检查、可以 IDE 自动补全、可以在 checkpoint 时做类型验证。避免了大量文本记忆系统的"模糊性陷阱"——一个用户的过敏记录在文本里可能是"青霉素过敏""对青霉素有反应""对 β-内酰胺类抗生素过敏"三种不同表述,类型化对象强制了统一格式。
规则函数:
def check_drug_allergy_conflict(
state: UserState,
new_drug: str
) -> Alert | None:
"""检测新药物是否与已知过敏史冲突"""
for allergy in state.allergies:
if drug_interacts_with_allergy(new_drug, allergy):
return Alert(
level="critical",
message=f"{new_drug} 与已知过敏 {allergy} 存在禁忌"
)
return None
def compute_travel_count(state: UserState, year: int) -> int:
"""聚合查询:某年的国际旅行次数"""
return len([
t for t in state.travel_history
if t.date.year == year and t.is_international
])
规则函数是确定性可执行的:给定相同状态,输出永远一致。这意味着记忆的行为是可预测、可测试、可审计的——不像文本记忆,在不同检索上下文下可能给出不同回答。
两阶段 Pipeline
这个范式通过一个精心设计的两阶段 pipeline 实现:
阶段一:Append-Only Event Log(只追加事件日志)
每次与用户交互后,新的信息被追加到日志,不修改已有内容:
{"type": "allergy_reported", "allergy": "青霉素", "time": "2025-03-01"}
{"type": "medication_prescribed", "drug": "阿莫西林", "time": "2026-07-25"}
只追加的设计保证了两件事: 1. 信息永不丢失:旧偏好、新偏好、矛盾偏好全部保留,支持事后追溯 2. 不需要在线写入结构化状态:交互时不需要实时解析并更新复杂对象,只管 append 即可
阶段二:周期性 Checkpoint → Typed Code(定期凝固为类型化代码)
每隔一定时间(或达到某个触发条件),系统将日志 checkpoint 为类型化代码:
class UserState:
allergies = ["青霉素"] # 从日志重建
medications = [
Medication(name="阿莫西林", prescribed_date=date(2026,7,25), ...)
]
def check_drug_allergy_conflict(self, new_drug: str):
# 规则已编码为确定性的执行逻辑
...
Checkpoint 的结果是一个可执行的 Python 文件——这意味着:
- 推理时直接 import user_profile; user_profile.compute_travel_count(state, 2025)
- 不需要任何 embedding 模型、不需要向量数据库、不需要相似度检索
- 任何 Python 解释器都能运行,不需要 GPU
推理即执行(Reasoning as Execution)
这是 UaC 与传统记忆系统最本质的区别。在检索范式中,"查询用户信息"是一个语义推断过程(query 与记忆块做相似度匹配);在 UaC 中,"查询用户信息"是一个程序执行过程(调用一个 Python 函数)。
这个区别产生了三个重要后果: 1. 答案确定性:相同查询总返回相同答案,不随检索随机性波动 2. 可审计性:每个答案都有可追溯的执行路径(哪条日志→哪个 checkpoint→哪个函数) 3. 主动触发:状态变化可以自动触发规则执行(不等用户来问)
关键实验与数据
LoCoMo 基准(简单回忆任务)
| 系统 | 回忆准确率 |
|---|---|
| Full-Context Upper Bound(完整上下文) | 78.8% |
| 最强 Prior 记忆系统 | 78.8% |
| UaC | 78.8% |
UaC 在简单回忆任务上与最优系统持平——这说明在"单个事实回忆"这个任务上,检索式记忆已经够用了,UaC 的优势不在这里。
聚合查询实验
| 问题类型 | 传统检索记忆 | UaC |
|---|---|---|
| "去年国际旅行几次?" | 6–43%(⚠️ 原文 LoCoMo 数据) | 99%(⚠️ LoCoMo benchmark 实测,非生产数据) |
| "我的药物和哪些过敏史冲突?" | ❌ 不支持 | ✅(⚠️ 同上,为 benchmark 数据) |
这组数据清晰地展示了 UaC 的能力边界:聚合查询是检索范式的理论盲区,是执行范式的设计上限。
主动安全告警
这是 UaC 独有的能力。传统检索系统无法主动发现药物-过敏冲突——因为它没有"状态变化触发规则执行"的路径。UaC 通过在 checkpoint 后的代码中编码 check_drug_allergy_conflict 规则,实现了对新药物的自动冲突检测,并在检测到风险时主动推送 alert。
亮点与局限
亮点:
-
范式级创新:从"记忆即文本检索"到"记忆即可执行代码",重新定义了个性化 Agent 的记忆架构。这是自向量数据库引入 RAG 以来,记忆系统领域最重要的范式转变之一。
-
精确解决聚合查询问题:之前的 Agent 记忆系统从未真正解决 COUNT/SUM/FILTER 类查询,UaC 用"记忆即代码"从根本上绕过了检索范式的理论上限。
-
主动安全告警能力:首次让记忆系统具备"主动推理"能力,可以不等用户提问就发现风险。这在医疗、金融等高风险场景有直接价值。
-
可测试、可审计:记忆就是 Python 代码,可以写单元测试、可以做 linting、可以版本控制(git blame 历史)、可以 CI/CD 自动化检查。这让记忆系统的工程质量有了工程级的保障。
-
不需要 GPU:推理阶段是纯 Python 执行,不需要 embedding 模型、不需要向量检索,不需要 GPU。这大幅降低了部署成本。
局限:
-
Checkpoint 频率的权衡:太频繁影响系统效率(需要持续凝固日志);太稀疏导致状态陈旧(用户的最新信息要等很久才能被编码进状态)。最优频率取决于具体应用场景,原文未给出系统性的工程指南。
-
初始状态构建:新用户没有历史日志,如何从零构建合理的初始 UserState?原文描述有限,这是一个实际的冷启动问题。
-
LLM 生成规则代码的质量风险:如果 LLM 生成的
check_drug_allergy_conflict函数有 bug,可能导致严重的漏报(该报警时没报警)。需要在 checkpoint 生成后进行形式化验证或人工审核。 -
适用边界:高度结构化的用户信息(医疗、金融、旅行偏好)收益最大;开放域闲聊和主观偏好("我喜欢什么样的故事")难以强制定义为类型化对象。
-
跨平台记忆:如果用户使用多个 Agent 或多个设备,如何同步和合并各自的 checkpoint?分布式场景下的状态合并问题未被讨论。
对工程落地的启发
混合记忆架构
对于正在构建个性化 Agent 的工程团队,UaC 提出了一个重要的架构选择:不要用单一的记忆范式应对所有需求。
建议的混合架构: - 文本记忆层:处理开放域闲聊、历史背景上下文——用传统的 embedding + 检索 - 可执行记忆层:处理结构化偏好、规则约束、安全相关状态——用类型化对象 + 规则函数 - 定期凝固机制:将 append-only log 定期 checkpoint 为可执行代码,使历史信息从"被检索"变为"可被计算"
从"被动 RAG"到"主动规则引擎"
当前 RAG 系统的核心局限是:只能被动响应用户 query,无法主动发现信息。UaC 的规则执行机制为 RAG 补充了一条主动路径:
信息变化 → 触发规则执行 → 主动输出(alert / 建议 / 预防)
这对高风险场景(医疗提醒、金融合规、旅行安全)有直接落地价值。
可执行记忆的工程实践
将 UaC 范式落地需要几个关键工程投入: 1. Schema 设计:定义 UserState 的类型体系,这需要领域专家参与 2. 日志格式设计:append-only log 的每条 entry 需要有足够的结构化程度 3. Checkpoint 生成器:将非结构化日志可靠地转换为类型化 Python 代码的 pipeline 4. 规则审计:对 LLM 生成的规则函数做测试覆盖和人工抽检
与同方向工作的关系
| 相关工作 | 核心差异 |
|---|---|
| MemGPT | MemGPT 用层级记忆管理解决上下文长度问题,仍是检索范式;UaC 解决的是"推理能力"问题 |
| 知识图谱记忆 | 图谱可以表示实体关系,但执行聚合查询仍需要图查询引擎;UaC 用 Python 函数替代,生态更简单直接 |
| Personalized LLM | 传统方法关注从对话中抽取用户特征并注入 prompt;UaC 关注记忆的表示与执行架构 |
| Long Context LLM | Long Context 让模型能在超长上下文内运作,但仍受制于"检索 vs 计算"的根本差异 |
UaC 与 VaLR(本文集的另两篇解读之一)有一个有趣的呼应:VaLR 通过在每次推理前注入视觉锚点解决了"长序列中视觉信号被稀释"的问题;UaC 通过将记忆变成可执行代码解决了"长历史中信息无法被计算"的问题。两者都在处理"信息随规模增长而变得不可用"这个普遍问题,只是锚点不同——一个在视觉空间,一个在记忆空间。
适合谁读
✅ 强烈推荐: - Agent 系统工程师:正在设计个性化 Agent,需要超越"chat history as context"的下一代记忆架构 - RAG / Memory 系统研究者:想理解检索范式的根本局限,以及如何构建"计算型记忆" - Product / 应用开发者:在医疗、金融、旅行等强规则领域构建 Agent,需要主动风险检测能力
⚠️ 参考阅读: - LLM 应用研究员:了解个性化记忆的前沿方向,对产品架构设计有参考价值 - 工程团队负责人:评估是否需要在产品中引入可执行记忆架构
❌ 不推荐: - 纯算法研究者(本文核心贡献在范式层面,而非模型或训练创新) - 需要快速复现代码实现的读者(原文为概念验证型论文,工程化路径需要自行设计) - 信息结构化程度低的应用场景(如社交闲聊 Agent)——这类场景 UaC 的收益有限
工程落地与核查(Jay)
UaC 架构的生产化改造要点
核心挑战:UaC 论文是概念验证,原文未给出完整的生产级架构。以下是把它落地需要补全的关键工程决策。
1. Checkpoint 频率策略(无通用答案,需业务标定)
推荐策略(可调参数):
event-driven checkpoint:用户关键行为后立即 checkpoint(写药→立即凝固药物事件)
- 优点:安全相关字段实时更新
- 缺点:高频写入可能影响延迟
daily batch checkpoint:每日凌晨批量凝固所有 pending events
- 优点:成本低、一致性好
- 缺点:安全告警有最多 24h 延迟
hybrid:安全相关字段 event-driven,其余 daily batch
2. LLM 生成规则代码的质量保障(最重要也最难)
# 方案:checkpoint 生成后强制 Linting + 单元测试
import subprocess
import json
def validate_generated_rules(code: str) -> dict:
"""
对 LLM 生成的规则代码做质量门禁。
三层验证,全部通过才算合法 checkpoint。
"""
results = {"syntax": False, "typing": False, "tests": False, "errors": []}
# Layer 1: Python syntax check
try:
compile(code, "<string>", "exec")
results["syntax"] = True
except SyntaxError as e:
results["errors"].append(f"SyntaxError: {e}")
# Layer 2: Type checking (requires pyright/mypy)
# pip install pyright
# 注意:UaC 代码需要完整的 UserState type hints 才能做类型检查
type_check = subprocess.run(
["pyright", "--outputjson", "/dev/stdin"],
input=code.encode(), capture_output=True
)
type_result = json.loads(type_check.stdout)
results["typing"] = type_result["summary"]["errorCount"] == 0
if not results["typing"]:
results["errors"].append(f"Type errors: {type_result}")
# Layer 3: 关键规则函数的单元测试(sample-based)
# 生产中至少跑以下几类:
# - check_drug_allergy_conflict: 用已知冲突药+已知过敏测试,必须返回 Alert
# - check_drug_allergy_conflict: 用已知安全药+已知过敏测试,必须返回 None
# - compute_travel_count: 用 mock state 测试,必须返回正确计数
# 若 LLM 生成的函数跑不过这些基本测试,拒绝 checkpoint
results["tests"] = True # 实际生产中替换为真实测试结果
return results
# 若任何一层失败,checkpoint 回退到上一个已验证版本,并告警人工审核
3. 冷启动问题:新用户没有历史日志
def bootstrap_user_state(user_profile: dict | None) -> UserState:
"""
冷启动:用用户初始填写的结构化表单构建 UserState。
新用户第1次打开 App 时引导填写:
- 过敏史(可选多选:青霉素/花粉/海鲜/…)
- 当前用药(可选,药品名称)
- 旅行偏好(频率、国家)
表单数据直接作为 checkpoint 0,不依赖日志重建。
"""
if user_profile is None:
# 匿名用户:返回最小 schema,所有字段为空 list
return UserState(
allergies=[],
medications=[],
travel_history=[],
preferences=UserPreferences()
)
# ...
最小可跑代码(简化版 UaC 架构)
from dataclasses import dataclass, field
from datetime import date
from typing import Optional
import json
from pathlib import Path
# === Schema(类型化状态对象)===
@dataclass
class Medication:
name: str
prescribed_date: date
@dataclass
class AllergyReport:
allergy: str
reported_at: date
@dataclass
class TravelRecord:
destination: str
date: date
is_international: bool
@dataclass
class UserState:
allergies: list[str] = field(default_factory=list)
medications: list[Medication] = field(default_factory=list)
travel_history: list[TravelRecord] = field(default_factory=list)
# === 规则函数 ===
def check_drug_allergy_conflict(state: UserState, drug: str) -> Optional[str]:
"""
简化版药物-过敏冲突检测。
⚠️ 生产中需接入 DrugBank / RxNorm 等药品数据库,不可用硬编码映射。
"""
KNOWN_CONFLICTS = {
"阿莫西林": ["青霉素"],
"布洛芬": ["阿司匹林"],
}
conflicts = KNOWN_CONFLICTS.get(drug, [])
for allergy in state.allergies:
if allergy in conflicts:
return f"⚠️ 警告:{drug} 与已知过敏史 [{allergy}] 存在禁忌"
return None
def compute_travel_count(state: UserState, year: int) -> int:
"""聚合查询:某年的国际旅行次数"""
return len([
t for t in state.travel_history
if t.date.year == year and t.is_international
])
# === Event Log(Append-only)===
def append_event(log_path: Path, event: dict):
with open(log_path, "a") as f:
f.write(json.dumps(event, ensure_ascii=False, default=str) + "\n")
def rebuild_state(log_path: Path) -> UserState:
"""从 event log 重建 UserState(用于 checkpoint)"""
state = UserState()
if not log_path.exists():
return state
with open(log_path) as f:
for line in f:
event = json.loads(line)
if event["type"] == "allergy_reported":
state.allergies.append(event["allergy"])
elif event["type"] == "medication_prescribed":
state.medications.append(Medication(
name=event["drug"],
prescribed_date=date.fromisoformat(event["time"])
))
elif event["type"] == "travel_recorded":
state.travel_history.append(TravelRecord(
destination=event["destination"],
date=date.fromisoformat(event["date"]),
is_international=event.get("is_international", True)
))
return state
# === Demo ===
if __name__ == "__main__":
log_path = Path("/tmp/user_events.jsonl")
log_path.unlink(missing_ok=True)
# 追加事件
append_event(log_path, {"type": "allergy_reported", "allergy": "青霉素", "time": "2025-03-01"})
append_event(log_path, {"type": "medication_prescribed", "drug": "阿莫西林", "time": "2026-07-25"})
append_event(log_path, {"type": "travel_recorded", "destination": "日本", "date": "2025-07-10", "is_international": True})
# 重建状态
state = rebuild_state(log_path)
# 执行规则
conflict = check_drug_allergy_conflict(state, "阿莫西林")
print(conflict) # ⚠️ 警告:阿莫西林 与已知过敏史 [青霉素] 存在禁忌
travel_count = compute_travel_count(state, 2025)
print(f"2025年国际旅行次数: {travel_count}") # 1
核查清单(UaC 上线前必过)
| 检查项 | 状态 |
|---|---|
| 冷启动:新用户 UserState schema 已定义,且有引导表单 | |
| Checkpoint 频率策略已按业务场景标定(安全字段 event-driven) | |
| LLM 生成规则代码已通过 syntax + type + unit test 三层验证 | |
| 药物-过敏冲突检测已接入正规药品数据库(DrugBank/RxNorm),非硬编码 | |
| Checkpoint 文件有版本管理(git 或 hash),可回滚 | |
| Append-only log 有容量上限(建议按日期分片,防止无限膨胀) | |
| 跨 Agent / 跨设备状态合并策略已定义并测试 | |
| 关键告警(药物冲突)有短信/推送等异步触达渠道 | |
| LoCoMo 基准数字(99% / 6-43%)已标注为 benchmark 数据,非生产保证 |