모든 후보를 고려하는 추천 Agent
AgentDR: Dynamic Recommendation with Implicit Item-Item Relations via LLM-based Agents, 2025, arXiv.
LLM을 추천에 쓰는 방법은 세 갈래로 나뉘어요. 텍스트를 임베딩으로 바꾸는 encoder, 추천을 언어 모델링 문제로 바꾼 recommender, 그리고 memory와 reflection을 갖춘 agent예요. AgentDR은 세 번째 갈래에 속하면서, 앞선 agent 연구들이 비켜 간 두 가지를 다뤄요. 하나는 전체 catalog를 정렬하는 full-ranking이고, 다른 하나는 데이터에 라벨이 없는 item-item 관계예요.
LLM은 전체 상품을 고려하지 못해요
LLM을 추천기로 직접 쓰면 두 가지가 걸려요. 첫째는 hallucination이에요. 모델이 catalog에 없는 상품 이름을 만들어내요. 둘째는 token limit이에요. 아이템이 수천 개인 catalog를 프롬프트에 넣고 전부 정렬시키는 건 비용과 길이 양쪽에서 불가능해요. 입출력이 길어질수록 hallucination 위험도 같이 커져요.
그래서 기존 LLM agent 연구는 평가 방식을 바꿔요. 유저가 실제로 상호작용한 정답 아이템 하나에 무작위 negative 몇 개를 섞은 작은 후보 집합을 만들고, 그 안에서 정답이 몇 등인지를 봐요. 이 방식은 구현이 쉽지만 실제 서비스와 어긋나요. 실제 추천기는 수천에서 수백만 개 아이템 중에서 골라야 하고, 후보를 좁혀 놓으면 성능이 부풀려져요.
AgentDR의 첫 번째 선택은 여기서 나와요. Full-ranking은 애초에 LLM이 할 일이 아니라고 보고, 전통적인 추천 모델에게 통째로 위임해요.
기존 agent는 이미 풀린 문제를 풀어요
두 번째 문제는 역할 분담이에요. 기존 LLM agent는 user-item 상호작용에서 유저 선호를 모델링하는 데 집중해요. 그런데 이건 LightGCN이나 SASRec 같은 embedding 기반 모델이 이미 잘하는 일이에요. LLM을 붙여서 얻는 이득이 크지 않아요.
반대로 전통 모델이 놓치는 영역이 있어요. 공동 등장 통계를 넘어서는 의미적 item-item 관계, 즉 substitute와 complement예요. DSLR 카메라를 산 유저에게 삼각대는 complement예요. 티라미수가 품절이면 치즈케이크가 substitute이지 햄버거는 아니에요. 이 관계는 추천 품질을 크게 올리지만, 공개 데이터셋에는 라벨이 거의 없어요.
기존 연구는 co-view, co-purchase 로그로 라벨을 만들었어요. 문제는 함께 본 아이템 쌍이 기능적으로 substitute나 complement가 아닌 경우가 많다는 거예요. 사람이 직접 붙이려면 도메인 지식과 비용이 들어요. LLM의 world knowledge가 들어갈 자리가 여기예요.
AgentDR은 full-ranking을 tool에게 맡겨요
문제 정의는 표준적인 sequential recommendation이에요. 유저 $u$의 행동 시퀀스 $\mathbf{s} = (i_1, i_2, \dots, i_n)$가 있고, 각 아이템은 텍스트 설명을 가져요. 아이템 시퀀스의 설명 목록을 $\text{desc}(\mathbf{s})$로 써요. 앞부분 $\mathbf{s}_{[:-k]}$를 보고 다음 아이템을 맞히되, 출력은 전체 아이템 집합 $\mathcal{I}$에 대한 정렬된 리스트예요.
AgentDR은 추천 모델을 tool로 취급해요. 요구 조건은 하나뿐이에요. 전체 아이템에 대한 점수 리스트를 내놓기만 하면 돼요. 논문은 세 가지 계열을 골랐어요.
- LightGCN, graph 기반 collaborative filtering, 출력 $\hat{\mathbf{r}}_G$
- SASRec, Transformer 기반 sequential model, 출력 $\hat{\mathbf{r}}_S$
- SimpleX, MF 기반 model, 출력 $\hat{\mathbf{r}}_M$
세 리스트 모두 $\mathbb{R}^{1 \times |\mathcal{I}|}$에 속해요. 이 tool들은 $\mathbf{s}_ {[:-k]}$로 미리 학습돼요. 즉 마지막 $k$개 아이템 $\mathbf{s}_ {[-k:]}$는 tool이 본 적 없는 구간이고, 이 구간이 agent를 최적화하는 정답 신호로 쓰여요. 실험에서 $k=1$이에요.
전체 흐름은 세 단계예요. 유저마다 substitute와 complement 후보를 생성하고, 두 종류의 memory를 최적화하고, tool 출력을 합친 뒤 상위 아이템을 다시 정렬해요.
User agent는 두 종류의 memory를 가져요
각 agent는 profile 하나와 memory 두 개를 가져요.
Profile은 정적이에요. 공개 데이터셋에는 유저 프로필이 없으니, 과거 아이템 설명을 LLM에게 요약시켜 만들어요.
\[y_{\text{prof}} = \text{LLM}_{\text{prof}}(\text{desc}(\mathbf{s}))\]프롬프트는 이래요. 논문 부록의 프롬프트는 모두 Electronics 데이터셋 기준으로 적혀 있고, 데이터셋 이름만 바꿔 재사용해요.
1
2
3
4
5
[Instruction]
Summarize the user's preference based on the historical items
this user purchased under Electronics on Amazon.
[Historical Items] {item_descriptions}
Memory는 동적이에요. RecTool memory $\mathbf{m}^{rec} \in \mathbb{R}^{1 \times |\mathcal{T}|}$는 각 tool이 이 유저에게 얼마나 맞는지를 담은 가중치예요. 세 tool에 대해 $m_G$, $m_S$, $m_M$으로 써요. Intent memory $\mathbf{m}^{int} \in \mathbb{R}^{1 \times 3}$은 유저가 substitute, complement, 일반 아이템 중 무엇을 향하는지를 담아요. 각각 $m_{sub}$, $m_{com}$, $m_{reg}$예요. 세 성분을 한꺼번에 가리킬 때는 인덱스 $int \in \{sub, com, reg\}$를 써서 $m_{int}$로 표기해요. RecTool memory 쪽 $m_t$의 $t \in \mathcal{T}$에 대응하는 자리예요.
두 memory 모두 학습 가능한 파라미터가 아니라, LLM의 판단으로 값이 누적되는 스칼라예요. 여기가 이 프레임워크의 특징이에요. Backpropagation 없이 유저 단위 적응이 일어나요.
Substitute와 complement는 유저 단위로 생성해요
관계 라벨을 아이템 쌍마다 붙이려면 LLM 호출이 $O(|\mathcal{I}|^2)$번 필요해요. 수백만 아이템 규모에서는 성립하지 않아요.
AgentDR은 방향을 바꿔요. 아이템 쌍이 아니라 유저의 최근 관심사를 기준으로 후보 목록을 생성해요. Tool이 볼 수 있었던 가장 최근 $c$개 아이템 $\mathbf{s}_{[-(k+c):-k]}$의 설명을 LLM에게 주고, substitute 목록과 complement 목록을 각각 받아요.
\[y_{\text{gen}}^{sub} = \text{LLM}_{\text{gen}}^{sub}(\text{desc}(\mathbf{s}_{[-(k+c):-k]}))\] \[y_{\text{gen}}^{com} = \text{LLM}_{\text{gen}}^{com}(\text{desc}(\mathbf{s}_{[-(k+c):-k]}))\]Substitute 생성 프롬프트는 이래요. Complement 쪽은 단어만 바뀌어요.
1
2
3
4
5
6
7
8
[Instruction]
According to the historical items purchased by a user, generate
20 substitutes of these items under Electronics on Amazon.
[Historical Items] {item_descriptions}
The output must be one list of item titles in length of 20,
separated by lines.
호출 횟수가 $O(|\mathcal{I}|^2)$에서 $O(|\mathcal{U}|)$로 줄어요. 여기서 생성되는 건 catalog의 실제 아이템 ID가 아니라 상품 제목 형태의 텍스트예요. 이 목록은 나중에 실제 후보 아이템을 재정렬하는 기준으로만 쓰이니, 존재하지 않는 이름이 나와도 최종 출력에 섞이지 않아요. 실험에서 $c=10$이에요.
어떤 tool이 이 유저에게 맞을까
유저마다 잘 맞는 추천 전략이 달라요. 시리즈물을 순서대로 읽는 유저에게는 sequential model이 강하고, 시리즈를 다 읽은 뒤 비슷한 취향의 다른 책으로 넘어가는 유저에게는 collaborative filtering이 강해요. AgentDR은 이 차이를 RecTool memory로 흡수해요. 갱신 경로는 두 개예요.
첫째는 LLM-based tool comparison이에요. 각 tool의 상위 $k_{\text{cpr}}$개 아이템 설명과, tool이 보지 못한 정답 구간 $\mathbf{s}_{[-k:]}$의 설명을 함께 주고, 어느 tool이 유저 선호에 가장 맞는지 고르게 해요.
\[\mathbf{z}_{\text{cpr}} = \text{LLM}_{\text{cpr}}(\{\text{desc}(f_{\text{top}}(\hat{\mathbf{r}}_t, k_{\text{cpr}})) \mid t \in \mathcal{T}\}, \text{desc}(\mathbf{s}_{[-k:]}))\]출력은 $|\mathcal{T}|$개의 binary indicator이고, 선택된 tool의 가중치만 올라가요.
\[m_t \leftarrow m_t + \alpha \cdot z_{\text{cpr}}^t\]프롬프트는 이래요. A, B, C 세 group에는 각 tool이 뽑은 상위 아이템이 하나씩 들어가고, target item 자리에는 정답 구간이 들어가요.
1
2
3
4
5
6
7
8
9
10
11
12
[Instruction]
Under Electronics on Amazon, according to the descriptions of items
in three groups A, B and C, evaluate which group the target item is
most relevant to.
[Group A] {item_descriptions}
[Group B] {item_descriptions}
[Group C] {item_descriptions}
[Target Item] {target_item_description}
The output must be one single character in {A, B, C} denoting the
most relevant group.
이 비교는 A/B/C 중 하나를 고르는 강제 선택 형태예요. LLM은 웬만한 추천 결과에 다 동의하는 경향이 있고, 단일 도메인 데이터셋에서는 모든 출력이 그럴듯해 보여요. 강제 선택은 이 두 성향을 눌러요.
둘째는 ranking comparison이에요. LLM 없이, 정답 아이템이 각 tool에서 몇 등이었는지를 직접 반영해요.
\[m_t \leftarrow m_t + \beta \sum_{\{r_t^j \mid i_j \in \mathbf{s}_{[-k:]}\}} \frac{(r_t^j)^{-1}}{\sum_{t' \in \mathcal{T}} (r_{t'}^j)^{-1}}\]$r_t^j$는 tool $t$에서 $j$번째 정답 아이템의 순위예요. 역수를 tool 간에 정규화하니, 정답을 앞쪽에 둔 tool이 더 큰 몫을 가져가요. 정성적 판단과 정량적 신호를 각각 하나씩 둔 구조예요.
갱신된 memory로 세 리스트를 가중합해 통합 순위를 만들어요.
\[\hat{\mathbf{r}} = \sum_{t \in \mathcal{T}} m_t \hat{\mathbf{r}}_t = m_G \hat{\mathbf{r}}_G + m_S \hat{\mathbf{r}}_S + m_M \hat{\mathbf{r}}_M\]Rank-level ensemble이라 학습 비용이 없어요. 논문은 이 자리를 학습 가능한 가중치 벡터, MLP, attention으로 갈아 끼우는 실험도 해요.
여기서 $\hat{\mathbf{r}}_G$, $\hat{\mathbf{r}}_S$, $\hat{\mathbf{r}}_M$은 각 tool의 output ranking score인데, 아무 scaling 없이 사용한 건 저도 잘 이해가 안되네요…
유저에 성향에 따라 추천 방향을 결정해요
다음에 살 아이템이 substitute인지, complement인지, 그냥 평소 취향인지를 구분하는 단계예요. ID 기반 추천기는 이 구분을 못 해요. 공동 등장 통계만으로는 기능적 관계를 알 수 없기 때문이에요.
앞서 만든 두 목록과 정답 구간을 함께 주고, 어느 쪽이 더 잘 맞는지 고르게 해요.
\[\mathbf{z}_{\text{dcm}} = \text{LLM}_{\text{dcm}}(y_{\text{gen}}^{sub}, y_{\text{gen}}^{com}, \text{desc}(\mathbf{s}_{[-k:]}))\]
1
2
3
4
5
6
7
8
9
10
[Instruction]
Given the two groups of items under Electronics on Amazon, evaluate
which group is more relevant to the target item.
[Group 1] {item_descriptions}
[Group 2] {item_descriptions}
[Target Item] {target_item_description}
The output must be one single number in {1, 2} denoting the more
relevant group.
Group 1과 Group 2가 각각 substitute 목록과 complement 목록이고, target item이 정답 구간이에요. 출력은 합이 1인 두 개의 binary indicator예요. 여기에 세 번째 축을 하나 더 둬요. 유저 이력 전체를 보고, substitute나 complement 패턴이 뚜렷하지 않은지를 판정해요.
\[z_{\text{dcm}}^{reg} = \text{LLM}_{\text{dcm}}^{reg}(\text{desc}(\mathbf{s}))\]
1
2
3
4
5
6
7
8
[Instruction]
According to the historical items purchased by a user under
Electronics on Amazon, evaluate if this user exhibits clear
substitute/complement patterns or not.
[Historical Items] {item_descriptions}
The output must be one single word in {Yes, No}.
이 축이 없으면 agent가 모든 유저에게 관계 추론을 억지로 적용해요. 관계가 약한 유저에게는 profile 기반 일반 선호로 돌아갈 통로가 필요해요. 그 통로가 general reranking이에요.
\[y_{\text{rank}}^{reg} = \text{LLM}_{\text{rank}}^{reg}(f_{\text{top}}(\hat{\mathbf{r}}, k'), \text{desc}(f_{\text{top}}(\hat{\mathbf{r}}, k')), y_{\text{prof}})\]세 판정 결과는 같은 형태로 intent memory에 누적돼요.
\[m_{int} \leftarrow m_{int} + \gamma \cdot z_{\text{dcm}}^{int}\]Dual S&C reranking이 최종 순위를 만들어요
통합 순위 $\hat{\mathbf{r}}$의 상위 $k’$개만 LLM에게 넘겨 다시 정렬해요. 기준은 두 개예요. 하나는 substitute 목록과의 의미적 유사도, 다른 하나는 complement 목록과의 유사도예요.
\[y_{\text{rank}}^{sub} = \text{LLM}_{\text{rank}}(f_{\text{top}}(\hat{\mathbf{r}}, k'), \text{desc}(f_{\text{top}}(\hat{\mathbf{r}}, k')), y_{\text{gen}}^{sub})\] \[y_{\text{rank}}^{com} = \text{LLM}_{\text{rank}}(f_{\text{top}}(\hat{\mathbf{r}}, k'), \text{desc}(f_{\text{top}}(\hat{\mathbf{r}}, k')), y_{\text{gen}}^{com})\]프롬프트는 이래요. Target item list에 $y_{\text{gen}}^{sub}$ 또는 $y_{\text{gen}}^{com}$이 들어가고, candidate item list에 통합 순위의 상위 $k’$개가 들어가요.
1
2
3
4
5
6
7
8
9
10
[Instruction]
Rank top-20 items from the candidate item list based on their
similarity to the target item list, from higher to lower similarity.
[Target Item List ordered by priority] {item_descriptions}
[Candidate Item List in format of (ID, description)]
{(item_IDs, item_descriptions)}
The output must be a list of candidate item IDs with length of 20,
with items separated by lines.
General reranking은 target item list 자리에 $y_{\text{prof}}$가 들어가는 형태예요. 두 출력은 같은 $k’$개 아이템을 순서만 바꿔 담은 리스트예요. 여기서 hallucination이 다시 등장할 수 있어요. LLM이 없는 ID를 쓰거나 엉뚱한 텍스트를 뱉을 수 있어요. AgentDR은 학습이 아니라 규칙으로 막아요. Catalog에 실제로 있는 ID만 남기고, 개수가 $k’$보다 적으면 $f_{\text{top}}(\hat{\mathbf{r}}, k’)$에 남아 있던 아이템을 원래 순서대로 뒤에 붙여요.
논문은 이 장치가 hallucination의 영향을 완전히 제거한다고 표현해요 (“This strategy completely eliminates the impact of hallucination”). 출력이 항상 유효한 ID $k’$개로 채워진다는 뜻에서는 맞아요. 다만 제 생각에 순위 품질까지는 해결하지 못한 것 같아요. LLM이 상위에 놓았어야 할 아이템을 누락하면 그 아이템은 뒤로 밀려나고, 그만큼 순위가 나빠져요.
두 리스트 $y_{\text{rank}}^{sub}$와 $y_{\text{rank}}^{com}$은 intent memory로 섞어요. 아이템 $i$의 점수는 두 리스트에서의 위치를 가중합한 값이에요.
\[\hat{r}_{i}^{dual} = m_{sub} \cdot \text{index}(i, y_{\text{rank}}^{sub}) + m_{com} \cdot \text{index}(i, y_{\text{rank}}^{com})\]여기에 general reranking 결과를 한 번 더 같은 방식으로 섞어 최종 리스트를 만들어요.
\[\hat{r}_{i}^{final} = 1 \cdot \text{index}(i, y_{\text{rank}}^{dual}) + m_{reg} \cdot \text{index}(i, y_{\text{rank}}^{reg})\]$y_{\text{rank}}^{dual}$은 $\hat{\mathbf{r}}^{dual}$을 정렬해 얻은 리스트예요.
세 리스트 $y_{\text{rank}}^{sub}$, $y_{\text{rank}}^{com}$, $y_{\text{rank}}^{reg}$는 모두 같은 $k’$개 아이템을 순서만 바꿔 담고 있어요. 그래서 아이템 하나가 리스트마다 위치를 하나씩 갖고, 그 위치들을 가중합해 다시 정렬하는 게 전부예요. $m_{reg}=0.5$이고 어떤 아이템이 dual에서 3등, general에서 15등이라면 점수는 $3 + 0.5 \times 15 = 10.5$예요. Index가 위치 값이라 작을수록 앞에 와요. 논문은 이 값을 score로 부르지만, 정의상 오름차순으로 읽어야 순서가 맞아요.
두 가중치 중 dual 쪽은 1로 고정이고 general 쪽만 $m_{reg}$로 움직여요. 논문은 이 단계를 profile 기반 순위를 regularization으로 넣는 것이라고 설명해요. 관계 신호가 약하다고 판정된 유저일수록 $m_{reg}$가 커지면서 그 순위의 비중이 올라가요. 위치만 더하는 rank fusion이라 이 단계에서 LLM 호출은 없어요.
VDCG는 의미가 유사한지도 평가해요
논문은 새 평가 지표를 하나 제안해요. 예를 들어, 검은 야구 헬멧을 찾는 유저에게 다른 색 야구 헬멧 리스트와 자전거 헬멧 리스트는 완전히 다른 맥락이지만, 정답을 못 맞혔다는 점에서 Recall과 NDCG는 둘 다 0이에요. 언어 기반 방법은 정답을 못 맞혀도 의미적으로 가까운 리스트를 내놓는데, 기존 지표는 이 차이를 못 봐요.
VDCG(Vicinity-DCG)는 추천 리스트의 각 아이템이 정답 아이템과 얼마나 관련 있는지를 LLM에게 0에서 9까지 점수로 매기게 해요. 정답을 맞혔는지 대신 얼마나 가까운지를 보는 셈이에요. 이 점수를 추천 리스트에서 순위가 높은 아이템일수록 크게 치는 방식으로 합산해요. 결과적으로 관련도 높은 아이템을 위에 올린 리스트가 높은 값을 받아요.
지표 자체를 LLM으로 만든다는 점은 주의해서 봐야 해요. LLM 기반 reranking을 쓰는 방법이 LLM 기반 지표에서 유리할 여지가 있어요.
성능은 tool 대비 두 배 가까이 올라요
Instacart, Electronics, Sports 세 데이터셋에서 실험했어요. LLM은 Phi-4를 vLLM으로 로컬 배포하고 temperature는 0으로 뒀어요.
Full-ranking Recall@10 기준으로 AgentDR은 세 데이터셋 모두에서 1위예요. DiffRec 같은 최신 baseline 대비 Recall 최대 33.5%, NDCG 최대 28.4% 올랐고, 자기 tool인 SASRec 대비로는 모든 데이터셋에서 최소 33.3% 올랐어요. 성능이 tool에 종속되는 구조라, 더 강한 tool을 붙이면 더 오를 여지가 있어요.
읽을 만한 대비가 두 개 있어요.
첫째, BM25와 LLMRank 같은 언어 전용 방법은 Recall에서 거의 모든 추천 baseline에 밀려요. Collaborative signal과 sequential pattern을 못 잡으니 대규모 후보 공간에서 무너져요. 그런데 VDCG에서는 경쟁력이 있어요. 정답은 못 맞혀도 의미적으로 가까운 리스트를 내놓는다는 뜻이에요.
둘째, RAG baseline과의 비교예요. 각 tool이 뽑은 50개 후보를 LLM이 20개로 재정렬하는 방식인데, AgentDR보다 후보 집합이 큰데도 성능이 일관되게 오르지 않아요. 논문은 두 가지를 원인으로 봐요. 단일 추천기를 retriever로 쓰면 후보 집합이 그 모델의 편향을 그대로 물려받아요. 그리고 원본 아이템 설명을 그대로 넣으면 약어와 기호 같은 노이즈가 섞여요. AgentDR은 여러 tool을 합친 더 작고 신뢰도 높은 후보를 쓰고, 원문 대신 요약된 profile과 추론된 intent로 판단해요.
Ablation에서 dual S&C reranking은 substitute나 complement 한쪽만 쓰는 변형보다 모든 설정에서 나았어요. Tool comparison은 대부분 도움이 되지만 Instacart에서는 성능을 떨어뜨리기도 해요. 식료품 리스트끼리는 의미 차이가 작아 LLM이 tool을 고를 근거가 약하기 때문이에요. Ensemble을 attention이 붙은 MLP로 바꾸면 NDCG가 최대 13.56% 더 올라요. 다만 backpropagation 비용이 붙어요.
한계점은 뚜렸해요
논문이 직접 인정하는 한계는 도메인이에요. Substitute와 complement 관계는 식료품과 전자제품에서는 선명하지만, 영화나 음악에서는 훨씬 약해요. 이 프레임워크의 핵심 축이 그대로 옮겨가지 않아요.
실험 설계도 함께 봐야 해요. Tool은 전체 학습 데이터로 학습하지만, agent 최적화와 평가는 데이터셋마다 무작위 추출한 160명으로 진행해요. Table 3의 수치가 $1/160$ 단위로 떨어지는 이유예요. Full-ranking으로 평가 범위를 넓힌 대신 유저 표본은 좁아요. 개선 폭을 읽을 때 감안할 부분이에요.
비용도 짚을 만해요. 유저 한 명당 profile 요약, substitute·complement 생성, tool 비교, intent 판정, 세 번의 reranking까지 LLM 호출이 여러 번 필요해요. 논문은 intent 판정 모듈 하나가 최적화 단계 호출의 3분의 1을 차지한다고 밝히고, 이를 끄고 $m_{reg}$를 전체 유저 공통 상수로 두는 선택지를 제시해요. Full-ranking 자체는 tool이 처리하니 catalog 크기에 대해서는 확장되지만, 유저 수에 대한 비용은 남아 있어요.
정리하면 AgentDR의 기여는 새 모델이 아니라 역할 분배예요. 순위 매기기는 전통 추천기에게, 어떤 추천기를 믿을지와 상위 후보를 어떤 관계로 다시 볼지는 LLM에게 맡겨요. Agent의 memory가 학습 파라미터가 아니라 LLM 판단이 쌓인 스칼라라는 점도 이 설계와 맞물려요. Backbone을 건드리지 않고 유저 단위 적응을 얻는 방식이에요.

