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。

亮点与局限

亮点:

  1. 范式级创新:从"记忆即文本检索"到"记忆即可执行代码",重新定义了个性化 Agent 的记忆架构。这是自向量数据库引入 RAG 以来,记忆系统领域最重要的范式转变之一。

  2. 精确解决聚合查询问题:之前的 Agent 记忆系统从未真正解决 COUNT/SUM/FILTER 类查询,UaC 用"记忆即代码"从根本上绕过了检索范式的理论上限。

  3. 主动安全告警能力:首次让记忆系统具备"主动推理"能力,可以不等用户提问就发现风险。这在医疗、金融等高风险场景有直接价值。

  4. 可测试、可审计:记忆就是 Python 代码,可以写单元测试、可以做 linting、可以版本控制(git blame 历史)、可以 CI/CD 自动化检查。这让记忆系统的工程质量有了工程级的保障。

  5. 不需要 GPU:推理阶段是纯 Python 执行,不需要 embedding 模型、不需要向量检索,不需要 GPU。这大幅降低了部署成本。

局限:

  1. Checkpoint 频率的权衡:太频繁影响系统效率(需要持续凝固日志);太稀疏导致状态陈旧(用户的最新信息要等很久才能被编码进状态)。最优频率取决于具体应用场景,原文未给出系统性的工程指南。

  2. 初始状态构建:新用户没有历史日志,如何从零构建合理的初始 UserState?原文描述有限,这是一个实际的冷启动问题。

  3. LLM 生成规则代码的质量风险:如果 LLM 生成的 check_drug_allergy_conflict 函数有 bug,可能导致严重的漏报(该报警时没报警)。需要在 checkpoint 生成后进行形式化验证或人工审核。

  4. 适用边界:高度结构化的用户信息(医疗、金融、旅行偏好)收益最大;开放域闲聊和主观偏好("我喜欢什么样的故事")难以强制定义为类型化对象。

  5. 跨平台记忆:如果用户使用多个 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 数据,非生产保证