论文复现之AgentCF:用户/物品双代理的落地经验与关键注意事项

📅 2026/7/6 17:19:31 👁️ 阅读次数 📝 编程学习
论文复现之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 方法框架

  1. 从真实时间序列采样成对的正负样本,负样本刻意选热门且排在前面制造强干扰。
  2. LLM 读取用户短期记忆与两个物品记忆完成二选一,并输出简短理由。
  3. 与真值对照,选对就记一条证据;选错就触发协作反思,只编辑用户短期记忆和正样本物品记忆,负样本不改以免影响它在其他用户上的可迁移性,最后把旧的短期记忆归档进长期记忆,方便后续检索。
  4. 如此循环若干轮直到与真实一致或达到步数上限,用户–物品–用户的链路上会自然形成“偏好传播”,后续推断就更贴近真实个性。
原文说法

记忆设计很朴素但关键:用户有一个短期记忆(近期偏好,易于快速改写)和一个长期记忆库(历史片段,推断时面向候选做相关检索);物品有一个统一记忆(用标题、类目等身份信息初始化,随后持续注入“谁喜欢我”的受众画像)。把 embedding 换成“可读可改的语言记忆”,训练过程就变成了“可解释、可定位、可修复”的记忆治理。

1.3 推断阶段的三种提示策略

AgentCF_B(基础):只用用户短期记忆和候选物品记忆直接排序,通用且成本最低。
AgentCF_B+R(检索专长):在长期记忆里按候选做检索,把“与候选相关的个性化专长片段”并入上下文,长期信息很丰富时更有效。
AgentCF_B+H(序列增强):在稀疏场景把历史交互过的物品记忆也并入上下文,让大模型临时承担轻量的序列建模角色。

我复现这套方法看中的价值主要有四点:

  1. 双侧建模把“谁喜欢谁”的关系学得更扎实,也更容易解释。
  2. 不微调 LLM,只编辑记忆,成本友好且可控。
  3. 稀疏、冷启动场景下能靠 B+H、B+R 补上下文,鲁棒性不错。
  4. 每一步都有理由文本与可追溯的记忆改写,排错和 AB 调优非常直观。

二、原文结果与贡献

2.1 实验与评测设置

  1. 数据:Amazon Reviews 的CDs & VinylOffice Products两个子域,各自再划分“稠密/稀疏”两个 100 用户子集用于代理训练与评测。
  2. 评测:留一法(最后一条交互为真值),主指标NDCG@1/5/10,每个测试实例重复三次取均值。
  3. 基线:BPRSASRec(同时报告全量训练与采样训练版本)、PopBM25、以及把 ChatGPT 当零样本排序器的LLMRank
  4. AgentCF 推断配置:AgentCF_B(只用用户短期记忆Ms_u与候选物品记忆Mi)、AgentCF_B+R(从用户长期记忆Ml_u检索与候选相关的片段M_u^r加入上下文)、AgentCF_B+H(在稀疏场景把历史交互过的物品记忆{M_{i_k}}也纳入上下文用于轻量序列增强)。

2.2 主要排名结果

  1. 整体上,AgentCF 的三个变体在四个数据集的大多数指标上优于传统 CF/序列模型与零样本 LLM 排序,其中基础版B已能稳定越过强基线。
  2. 当用户长期记忆较为丰富时,B+RNDCG@5/10上进一步领先,因为能把“与候选强相关的历史专长片段”检索进上下文,证据更充分。
  3. 在交互稀疏的场景(尤其 Office),B+H优势更明显,它让模型在推断时临时承担一小步序列建模,用历史物品记忆补足上下文,从而显著缓解冷场景下的排名退化。

三种配置不是此消彼长,而是面向不同数据形态的“即插即用”增强,先跑B,看长期/序列信息是否充足,再决定是否打开RH

2.3 设计选择与消融观察

  1. 热门优先的负采样能稳定地区分“流行度驱动”与“个性化驱动”,迫使代理依赖个性化记忆而不是走流行度捷径。
  2. 只编辑用户短期记忆与正样本物品记忆,不编辑负样本物品记忆,更利于稳定泛化;一旦把负样本物品“写黑”,它在其他用户上的潜在吸引力会被不必要地削弱。
  3. 在小样本与高 API 成本约束下,采样训练的 BPR/SASRec相比其全量版本明显退化,而AgentCF不依赖大批量梯度更新,反而能凭“可编辑的语言记忆”保持竞争力。

这些设计共同把“学习重心”从参数梯度迁移到“记忆治理”,以文本方式记录、对齐与纠错,更可审计也更利于排错。

2.4 行为模拟结果

  1. 阅读评论的态度转移:用户代理在决策前若先读其他用户评论,正评显著提高选择概率,负评明显降低选择概率,而且更信任“与自己画像更接近的评论者”,与真实平台行为一致。
  2. 新物品的增温:新物品代理在进入排序前先与热门物品代理交互一次进行“增温”,其后续排序名次会提升,说明物品之间发生了“受众画像”的迁移,为冷启动提供了无需额外预训练的自然路径。
  3. 偏好传播可视案例:沿“用户—物品—用户”的链路,多轮交互后出现风格与情感的逐跳扩散,形成可解释的协同信号。

2.5 作者自述贡献

  1. 双边代理建模:首次同时把用户与物品建成自治语言代理,在同一语言空间里协同刻画双边关系。
  2. 无微调的学习闭环:提出“对比选择 → 与真实对照 → 协作反思”的闭环,不改 LLM 权重,只对可读可改的语言记忆进行定点编辑,从而在语言空间内复现协同过滤。
  3. 实证与行为证据:在真实数据上展示了个性化排名收益,并呈现阅读评论的态度转移、物品间增温、偏好随交互传播等类人行为现象,支撑智能体在推荐中的可用性与可解释性。

贡献的关键词是“记忆反思双边”,这是把协同过滤的统计本质翻译成一套可解释、可调、可复核的语言流程。

三、 获取作者代码数据集并配置环境

3.1 获取代码与数据集

首先在GitHub上获取源代码并下载测试数据集

之后将相应数据集(dense 版本)放于此目录或在 YAML 中指定绝对路径:

data_path=dataset/CDs-100-user-dense

3.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
Recall0.100.360.490.690.98
NDCG0.100.24960.3030.3720.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_baseapi_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、致谢

感谢原作者开源代码与配置文件;感谢师兄在我复现工作中对我的耐心指点,希望这篇复现博客能帮助到更多需要的人。