论文复现之AgentCF:用户/物品双代理的落地经验与关键注意事项
这是一份面向可复制的 AgentCF 复现笔记,记录了代码/配置/数据/评测的完整落地过程,给出从论文到跑通的最短路径,并逐条记录我踩过的坑与规避办法。
目录
- 一、论文背景与方法
- 1.1 背景动机
- 1.2 方法框架
- 1.3 推断阶段的三种提示策略
- 二、原文结果与贡献
- 2.1 实验与评测设置
- 2.2 主要排名结果
- 2.3 设计选择与消融观察
- 2.4 行为模拟结果
- 2.5 作者自述贡献
- 三、 获取作者代码数据集并配置环境
- 3.1 获取代码与数据集
- 3.2 配置新环境
- 四、实验设置
- 4.1 实验基本设置
- 4.2 优化 CD 数据集上的用户代理和项目代理
- 4.3 评估(即交互推理)
- 4.4 直接在CD数据集上加载预训练的用户代理和项目代理
- 五、结果复现
- 5.1 指标
- 5.2 简要解读
- 5.3 注意事项
- 6. 复盘与思考
- 6.1 我遇到的问题(精简版)
- 6.2 我学到的
- 6.3 我的选择清单
- 7、致谢
原文地址:官方论文地址
代码地址:官方代码地址
一、论文背景与方法
1.1 背景动机
传统推荐(MF/NCF/SASRec 等)依赖数值交互(点击/评分),对复杂偏好与语境表达不足,交互与解释能力弱。且现有 “LLM for Rec” 多把 LLM 当打分器/文本特征器;但现实里语言相似 ≠ 行为相似(如“尿布—啤酒”共现但语义无关)。因此需要一种能学行为共现、又保留解释与自我修正的 LLM 推荐范式。
1.2 方法框架
- 从真实时间序列采样成对的正负样本,负样本刻意选热门且排在前面制造强干扰。
- LLM 读取用户短期记忆与两个物品记忆完成二选一,并输出简短理由。
- 与真值对照,选对就记一条证据;选错就触发协作反思,只编辑用户短期记忆和正样本物品记忆,负样本不改以免影响它在其他用户上的可迁移性,最后把旧的短期记忆归档进长期记忆,方便后续检索。
- 如此循环若干轮直到与真实一致或达到步数上限,用户–物品–用户的链路上会自然形成“偏好传播”,后续推断就更贴近真实个性。
记忆设计很朴素但关键:用户有一个短期记忆(近期偏好,易于快速改写)和一个长期记忆库(历史片段,推断时面向候选做相关检索);物品有一个统一记忆(用标题、类目等身份信息初始化,随后持续注入“谁喜欢我”的受众画像)。把 embedding 换成“可读可改的语言记忆”,训练过程就变成了“可解释、可定位、可修复”的记忆治理。
1.3 推断阶段的三种提示策略
AgentCF_B(基础):只用用户短期记忆和候选物品记忆直接排序,通用且成本最低。
AgentCF_B+R(检索专长):在长期记忆里按候选做检索,把“与候选相关的个性化专长片段”并入上下文,长期信息很丰富时更有效。
AgentCF_B+H(序列增强):在稀疏场景把历史交互过的物品记忆也并入上下文,让大模型临时承担轻量的序列建模角色。
我复现这套方法看中的价值主要有四点:
- 双侧建模把“谁喜欢谁”的关系学得更扎实,也更容易解释。
- 不微调 LLM,只编辑记忆,成本友好且可控。
- 稀疏、冷启动场景下能靠 B+H、B+R 补上下文,鲁棒性不错。
- 每一步都有理由文本与可追溯的记忆改写,排错和 AB 调优非常直观。
二、原文结果与贡献
2.1 实验与评测设置
- 数据:Amazon Reviews 的CDs & Vinyl与Office Products两个子域,各自再划分“稠密/稀疏”两个 100 用户子集用于代理训练与评测。
- 评测:留一法(最后一条交互为真值),主指标NDCG@1/5/10,每个测试实例重复三次取均值。
- 基线:BPR、SASRec(同时报告全量训练与采样训练版本)、Pop、BM25、以及把 ChatGPT 当零样本排序器的LLMRank。
- AgentCF 推断配置:AgentCF_B(只用用户短期记忆
Ms_u与候选物品记忆Mi)、AgentCF_B+R(从用户长期记忆Ml_u检索与候选相关的片段M_u^r加入上下文)、AgentCF_B+H(在稀疏场景把历史交互过的物品记忆{M_{i_k}}也纳入上下文用于轻量序列增强)。
2.2 主要排名结果
- 整体上,AgentCF 的三个变体在四个数据集的大多数指标上优于传统 CF/序列模型与零样本 LLM 排序,其中基础版B已能稳定越过强基线。
- 当用户长期记忆较为丰富时,B+R在NDCG@5/10上进一步领先,因为能把“与候选强相关的历史专长片段”检索进上下文,证据更充分。
- 在交互稀疏的场景(尤其 Office),B+H优势更明显,它让模型在推断时临时承担一小步序列建模,用历史物品记忆补足上下文,从而显著缓解冷场景下的排名退化。
三种配置不是此消彼长,而是面向不同数据形态的“即插即用”增强,先跑B,看长期/序列信息是否充足,再决定是否打开R或H。
2.3 设计选择与消融观察
- 热门优先的负采样能稳定地区分“流行度驱动”与“个性化驱动”,迫使代理依赖个性化记忆而不是走流行度捷径。
- 只编辑用户短期记忆与正样本物品记忆,不编辑负样本物品记忆,更利于稳定泛化;一旦把负样本物品“写黑”,它在其他用户上的潜在吸引力会被不必要地削弱。
- 在小样本与高 API 成本约束下,采样训练的 BPR/SASRec相比其全量版本明显退化,而AgentCF不依赖大批量梯度更新,反而能凭“可编辑的语言记忆”保持竞争力。
这些设计共同把“学习重心”从参数梯度迁移到“记忆治理”,以文本方式记录、对齐与纠错,更可审计也更利于排错。
2.4 行为模拟结果
- 阅读评论的态度转移:用户代理在决策前若先读其他用户评论,正评显著提高选择概率,负评明显降低选择概率,而且更信任“与自己画像更接近的评论者”,与真实平台行为一致。
- 新物品的增温:新物品代理在进入排序前先与热门物品代理交互一次进行“增温”,其后续排序名次会提升,说明物品之间发生了“受众画像”的迁移,为冷启动提供了无需额外预训练的自然路径。
- 偏好传播可视案例:沿“用户—物品—用户”的链路,多轮交互后出现风格与情感的逐跳扩散,形成可解释的协同信号。
2.5 作者自述贡献
- 双边代理建模:首次同时把用户与物品建成自治语言代理,在同一语言空间里协同刻画双边关系。
- 无微调的学习闭环:提出“对比选择 → 与真实对照 → 协作反思”的闭环,不改 LLM 权重,只对可读可改的语言记忆进行定点编辑,从而在语言空间内复现协同过滤。
- 实证与行为证据:在真实数据上展示了个性化排名收益,并呈现阅读评论的态度转移、物品间增温、偏好随交互传播等类人行为现象,支撑智能体在推荐中的可用性与可解释性。
贡献的关键词是“记忆、反思、双边”,这是把协同过滤的统计本质翻译成一套可解释、可调、可复核的语言流程。
三、 获取作者代码数据集并配置环境
3.1 获取代码与数据集
首先在GitHub上获取源代码并下载测试数据集
之后将相应数据集(dense 版本)放于此目录或在 YAML 中指定绝对路径:
data_path=dataset/CDs-100-user-dense3.2 配置新环境
我的做法是建立一个Conda 独立环境名字就叫agentcf。毕竟以后经常切换多个实验;这样做一条命令激活就能复现
创建环境并安装依赖:
conda create-n agentcf python=3.11-y conda activate agentcf pip install--upgrade pip pip install-r requirements.txt pip install"openai==0.28.1"pyyaml pandas numpy tqdm faiss-cpu由于原文需要调用OpenAI的API,需要读者在代码中放入自己的api令牌密钥(若怕泄露可以用环境变量读取)
"""Dispatches requests to OpenAI API asynchronously. Args: messages_list: List of messages to be sent to OpenAI ChatCompletion API. model: OpenAI model to use. temperature: Temperature to use for the model. max_tokens: Maximum number of tokens to generate. top_p: Top p to use for the model. Returns: List of responses from OpenAI API. """四、实验设置
4.1 实验基本设置
在 .agentcf/props/AgentCF.yaml 文件中有关于基础实验设置,其中:
embedding_size:64# 推荐侧嵌入维度(如与RecBole/模型侧对齐)llm_model:gpt-3.5-turbo# 对话式模型,用于各类Agent对话/推理llm_temperature:0.2# 训练/生成阶段温度(略带随机性,利于探索)llm_temperature_test:0# 测试/评测阶段温度=0,保证可复现sample_num:200# 每轮采样的候选成对样本量(用户-物品二选一对数)api_batch:20# LLM请求的并发/批量大小(后端会做节流/聚合)chat_api_batch:10# Chat接口批量大小(通常低于通用接口)evaluation:basic# 评测模式:basic / rag / sequential 三选一接下来是用户和物品的agent提示词,以及前向反向和评测阶段的prompt,这里就不展示了,原作者已经给出,重点是要将自己的api密钥写入:
api_key_list:['sk-***']4.2 优化 CD 数据集上的用户代理和项目代理
cd agentcf/export api_base="Your openai.api_base"python run.py-m AgentCF-d CDs-100-user-dense--train_batch_size=20--eval_batch_size=200--max_his_len=20--MAX_ITEM_LIST_LENGTH=20--epochs=1--shuffle=False--api_batch=20--test_only=False关键含义:
–d CDs-100-user-dense:选择数据配置/别名为 CDs-100-user-dense(稠密版)。通常会映射到 props/CDs-100-user-dense.yaml 和 dataset/CDs-100-user-dense/
–test_only=False:先训练再评测。如果设为 True,则跳过训练、直接用已存在的权重做评测
本命令明确是 dense 版本。换成 sparse,请同步改 -d/YAML 与 data_path,并在文中写清用户数/物品数/交互数差异,避免评测协议不一致导致“假偏差”
4.3 评估(即交互推理)
cd agentcf/export api_base="Your openai.api_base"python run.py-m AgentCF-d CDs-100-user-dense--train_batch_size=20--eval_batch_size=200--max_his_len=20--MAX_ITEM_LIST_LENGTH=20--epochs=1--shuffle=False--api_batch=20--test_only=True关键含义:
–max_his_len=20:用户历史序列截断到最近 20 条,作为代理/模型的输入窗口
–test_only=True:跳过训练,直接做评测/交互推理
4.4 直接在CD数据集上加载预训练的用户代理和项目代理
cd agentcf/export api_base="Your openai.api_base"python run.py-m AgentCF-d CDs-100-user-dense--train_batch_size=20--eval_batch_size=200--max_his_len=20--MAX_ITEM_LIST_LENGTH=20--epochs=1--shuffle=False--api_batch=20--test_only=True--loaded=True--saved=False--saved_idx=1000关键含义:
–test_only=True:只做评估/交互推理,不进入训练环节
–loaded=True:从磁盘加载预训练好的“用户代理/物品代理状态”
–saved=False:本次不保存新的代理状态或权重(纯评估)
–saved_idx=1000:指定要加载的快照编号/版本号(例如第 1000 次保存的代理状态);需要和磁盘上已有文件对得上
五、结果复现
我训练用户代理/物品代理的前期执行过程为:
接下来就是进行训练与评测:
5.1 指标
| Metric | @1 | @3 | @5 | @7 | @10 |
|---|---|---|---|---|---|
| Recall | 0.10 | 0.36 | 0.49 | 0.69 | 0.98 |
| NDCG | 0.10 | 0.2496 | 0.303 | 0.372 | 0.4595 |
5.2 简要解读
- Recall@10 ≈ 0.98:前 10 名基本覆盖目标,排序前列稳定。
- NDCG 随 K 上升:从 0.10(@1)到 0.4595(@10),相关项更集中在 Top-10,而非绝对第 1。
- 时间成本:约 1h38m 完成一次完整评估,符合“用户代理 × 物品代理”的交互式推理特征。
5.3 注意事项
- 候选池协议:全排序 vs 采样会显著影响 Recall/NDCG
- 切分方式:时间切 vs 随机切差异明显;在“实验设置”中固定并公开。
- 多 seed:当前为单次评估,建议 ≥5 个 seed 报均值±方差。
- 预训练代理对齐:若加载同域(CDs-dense)且同模板的预训练代理,结果更稳;跨域/改模板会引入漂移。
6. 复盘与思考
这次复现主要是把“工程—协议—评测”三件事对齐。下面只留核心结论,便于读者复用。
6.1 我遇到的问题(精简版)
①CLI 告警与生效:日志提示will not be used in RecBole是 RecBole 不解析-m/-d的常规行为;实际由run.py用它们选择 YAML 与数据。通过“已加载的 YAML + data_path”日志自证即可。
②dense / sparse 的理解与取舍:dense=过滤后更稠密,冷启动少、结果稳;sparse=更贴近长尾,波动大但更接近实战。复现论文用 dense;做鲁棒性再跑 sparse。训练在哪个版本,评估就用哪个版本,避免 ID 映射不一致。
③评测协议对齐:候选池(全排序/采样)、切分方式(时间/随机)、负采样与指标实现需写死在 YAML,并在文中公开,否则容易出现“假偏差”。
④评估与训练的开关:--test_only=True只评估;False训练后评估。epochs=1仅用于冒烟,不用于对照。
⑤预训练代理加载:--loaded=True --saved_idx=…需满足同数据域、同模板、同截断长度;否则会维度不匹配或效果漂移。
⑥API 端点与并发:设置api_base;api_batch受限流影响,需要折中。交互式评估耗时在可接受的 1h+ 区间。
⑦版本与随机性:固定仓库 commit、Torch/RecBole/CUDA 版本并保存pip freeze;多 seed 报均值±方差。
6.2 我学到的
协议先于数值:候选池、切分、负采样、指标定义不一致,任何数值对比都失真。
先冒烟再放量:用最小配置验证管线通,再上完整训练与多 seed。
YAML 是“单一事实源”:把关键超参与评测项都写进 YAML,并在日志中打印,方便自证与复刻。
同域加载更稳:同数据、同模板、同长度的预训练代理能显著节省时间并减少波动。
可复制性是第一性:版本锁定、日志完整、结果可重跑,比单次最好值更有研究价值。
6.3 我的选择清单
① 复现论文表格:使用CDs-100-user-dense,评测协议写死,跑 ≥5 个 seed,保存日志与环境。
② 验证鲁棒性:在CDs-100-user-sparse复跑同配置,关注长尾场景的跌幅。
③ 省时评估:同域加载预训练代理(--loaded --saved_idx),评估前核对 ID 映射与模板。
小结:这次复现把工程脉络与评测协议理顺了;对本科阶段的研究实践而言,可复制性与自证能力比追求单点分数更重要
7、致谢
感谢原作者开源代码与配置文件;感谢师兄在我复现工作中对我的耐心指点,希望这篇复现博客能帮助到更多需要的人。