- 읽은 자료 4건
- 정본 반영 2개 파일(services/x.md, radar.md)
- 레이더 2건
오늘 함께 본 다른 변화는 다음과 같다.
- 같은 X 커밋에서 VM Ranker(후보 묶음을 다시 정렬하는 원격 서비스) 경로가 로컬 다양성 계산을 버리고, 서버가 돌려준 점수를 그대로 쓰게 됐다.
- X의 Following 피드는 인용하거나 리트윗한 원 저자가 뷰어를 블록한 포스트도 걸러 낸다.
- Meta와 Netflix는 신규 자료가 없었다. arXiv에서는 WeChat의 Tlow와 익명 라이브 광고 플랫폼의 TAGR이 레이더를 통과했다.
1스코어러가 계정 조치를 직접 요청하는 경로가 생겼다 — X
자료 xai-org/x-algorithm@0d3cdd8 — xai-org 공개 저장소, 2026-08-25T23:20:04Z 커밋(파일 47, +5,850/−586), 공개 소스코드. 이 노트들은 4세대 공개 뒤의 X 커밋에 차례로 번호를 붙여 부르며, 이 커밋은 4세대+8이다. 커밋 메시지는 세대 시리즈 관행대로 "Open-source X Recommendation Algorithm" 한 줄뿐이어서, 커밋 내용은 파일 diff로만 판독했다.
배경 X For You 타임라인의 4세대 오픈소스 릴리스는 2026-08-13 첫 공개(47c1bcd, +363,246/−11,640) 이후 하루에서 사흘 간격으로 후속 커밋이 붙고 있다. 이 커밋은 08-25 23:20 UTC에 올라왔는데, 어제(08-26) 배치가 놓쳐서 오늘 27일 배치에서 따라잡았다. 직전 커밋인 4세대+7(d011592)이 안전 스택을 세로로 심화했다면, 이번 4세대+8은 여섯 곳에 나란히 손을 댔다.
- 4세대+7이 안전 스택을 심화한 내용은 세 가지다.
- VF(visibility filtering, 노출 가능 여부를 판정하는 안전 계층) 검증인 reference_compare
- 브랜드 안전 tier 확장인 HighRisk
- PTOS(정책 기반 위반 카테고리 판정) 심판 3카테고리
- 4세대+8이 손댄 여섯 곳은 enforcement, 랭커, Following, Brazil 컴플라이언스, Phoenix rankall, Phoenix 학습 인프라다.
- Phoenix는 X For You에서 후보마다 참여 확률을 예측하는 Grok 기반 트랜스포머다.
- 규모(5,850줄)로 보면 4세대+4(+3,303) 이후 가장 크다. 다만 그중 3,379줄은 Brazil 2026 election 필터의 계정 데이터 갱신이 차지한다.
- 코드가 새롭다기보다, 6개 서브시스템에 나란히 새 연결이 들어가서 폭이 넓어진 공개다.
이번 커밋(0d3cdd8, 2026-08-25, +5,850/−586, 파일 47)은 스코어러가 계정 조치를 직접 요청하는 경로를 열고, 랭커와 Following, Brazil 필터의 계정 명단, Phoenix 인덱스와 학습 인프라에도 함께 손을 댔다.
README의 표제도 바뀌었다.
## Latest Updates가## Notable Updates로 바뀌었다.- 그 아래 August 14th 섹션의 Brazil 항목에는
*(Account list updated August 25, 2026.)*문구가 붙었다. - 이 표제 변경은 해당 섹션을 항상 최신 상태로 두지 않고, 특기할 만한 변경 위주로 유지하겠다는 의미로 해석한다 추정. 후속 커밋이 매일 붙는 상황에서 README까지 매일 갱신하지는 않겠다고 선언한 것이다.
1-1. 스코어러가 계정 조치를 직접 요청하는 일반 경로가 생겼다
AES(abuse-enforcement-service)는 이제 정적 YAML 규칙 말고도, 스코어러가 직접 붙여 보낸 조치 요청을 받아 실행한다. 요청은 코드에 고정된 종류와 동적 allowlist를 통과한 것만 실행되고, 거부된 요청은 이유와 함께 기록된다.
배경 abuse-enforcement-service/ (AES)는 4세대 첫 공개 때 전부 드러난 서브레포다. AES는 모델 스코어를 받아 실제 계정 조치로 매핑하는 규칙 엔진이다.
- 받는 모델 스코어의 예는 BDSM(계정 행동 시퀀스 부정 계정 탐지) 봇 헤드, LLM slop 라벨, PandaReports 등이다.
- 계정 조치는 label / bounce_captcha / bounce_arkose / spam_liveness_check / suspend다.
종전의 AES 규칙은 abuse-enforcement-service/service-lib/rules/enforcement_user.yaml과 enforcement_post.yaml에 정적으로 인코딩되어 있었다. 어떤 라벨이 붙으면 어떤 액션을 취하라는 매핑을 CEL 표현식으로 명시하는 방식이다. 예를 들면 다음과 같다.
- id: anchor_campaign_suspend_cse
when: '"anchor_campaign_suspend_cse" in score.labels'
then: { kind: act_suspend_user, perm: true, policy: "Cse" }
이 방식에는 새 스코어러를 추가할 때마다 AES YAML을 편집해야 한다는 한계가 있었다. 4세대+8이 이 결합을 끊었다.
스코어러가 요청 목록을 직접 붙여서 보내면, AES는 정해진 allowlist를 통과한 것만 실행한다.
- 이 일반 패스스루는
generic_actions.rs(663줄 신규)와 여기에 대응하는 YAML 규칙 두 개(act_requested_actions와platform_row_without_requested_actions)가 연결한다. - 새 파일의 전체 경로는
abuse-enforcement-service/service-lib/src/generic_actions.rs이고, 663줄 전부가 신규다. - 스코어러가 붙이는 요청 목록은
requested_actions리스트다.
- 이 일반 패스스루는
스코어러가 요청할 수 있는 액션 종류는 코드 상수로 고정되어 있다.
user entity에는 5종, post entity에는 2종만 요청할 수 있다. 클라이언트가 임의의 kind 문자열을 만들어 낼 수는 없다.
메시지 하나당 요청 가능한 액션은 16개로 제한된다.
pub const USER_KINDS: &[&str] = &[ "suspend", "label", "bounce_captcha", "bounce_arkose", "spam_liveness_check", ]; pub const POST_KINDS: &[&str] = &["post_label", "suspend_author"]; pub const MAX_REQUESTED_ACTIONS_PER_MESSAGE: usize = 16;
요청은 이중 fail-closed(조건을 통과하지 못하면 거부하는 방식) 검증을 거친다. 검증은
action_to_spec(entity_type, action, allowlist)에서 다음 다섯 단계로 이루어진다.hardcoded_kinds를 통과해야 한다. 통과하지 못하면"unknown_kind"이고, 다른 엔티티 타입에 속한 kind라면"entity_type_mismatch"로 구분한다.- 동적
GenericActionAllowlist.kinds에 해당 kind가 들어 있어야 한다. 없으면"kind_not_allowlisted"다. suspend나suspend_authorkind라면allowlist.suspend_policies에 policy가 들어 있어야 한다. 없으면"policy_not_allowlisted"다.label이나post_labelkind라면 세 조건을 모두 만족해야 한다.action.labels가 비어 있지 않아야 한다("no_labels").- 모든 label이
allowlist.labels에 포함되어야 한다("label_not_allowlisted"). ttl_msec >= 0을 만족해야 한다("invalid_ttl").
- 중복을 제거한다. 같은 spec이 이미 리스트에 있으면
"duplicate_action"이다.
거부된 액션은 그냥 버려지지 않고 관측된다.
- 거부된 액션은
SkippedRequestedAction { kind, head, reason, count }구조체로 수집되어outcome.info["requested_actions_skipped"]에 JSON으로 붙는다. - 별도 메트릭인
GENERIC_ACTION_TOTAL{entity_type, kind, outcome=resolved|skipped, reason}으로도 집계된다. - 즉 요청이 왜 통과하지 못했는지가 그대로 로그에 남는다. 스코어러 팀은 자기 요청이 거부된 이유를 바로 확인할 수 있다.
- 거부된 액션은
YAML 규칙은 enforcement_user.yaml에 두 개가 짝으로 들어갔다.
- id: platform_row_without_requested_actions
when: '"abuse_platform_requested_actions" in score.labels && size(score.requested_actions) == 0'
then:
kind: skip
reason: platform_row_without_requested_actions
- id: act_requested_actions
when: "size(score.requested_actions) > 0"
then:
kind: act_requested_actions
- 스코어러가 자기 라벨(
abuse_platform_requested_actions)로 일반 액션 파이프라인임을 선언해 놓고도 실제 액션 리스트가 비어 있으면, 그 요청은 건너뛴다. - 액션이 하나라도 있으면
act_requested_actionsdecision을 통해 위의 검증 파이프라인으로 라우팅한다. enforcement_post.yaml에는 대칭되는 규칙 하나만 들어갔고, post 쪽에는 platform_row 건너뛰기 규칙이 없다.
AES의 decision에도 요청 목록을 처리하는 새 variant가 생겼다.
abuse-enforcement-service/service-lib/src/lib.rs(+276/−19)에Decision::ActRequestedActionsvariant가 새로 생겼다.run_enforcement_inner는 이 variant를 만나면 다음과 같이 처리한다.let (decision, mut requested_actions_skipped) = match decision { Decision::ActRequestedActions => expand_requested_actions_decision( facts.entity_type, &facts.score, &ctx.dynamic_config.generic_action_allowlist(facts.entity_type), ), Decision::Skip(reason) => (ExpandedDecision::Skip(reason), None), Decision::Act(specs) => (ExpandedDecision::Act(specs), None), };allowlist를 통과한 액션이 하나도 없으면
Skip(REQUESTED_ACTIONS_DENIED)가 되고, 하나라도 있으면Act(specs)가 된다.즉 요청이 전부 거부되더라도 outcome이 skip으로 명시되기 때문에, 무엇이 요청됐고 왜 거부됐는지가 outcome.info의 JSON에 남는다.
dynamic_config에서 generic_action_allowlist로 이어지는 경로도 생겼다.
growthbook.rs(+126)에 새 메서드가 추가됐다.- GrowthBook은 X의 dynamic config이자 feature-flag 시스템으로 보인다 추정.
- 그렇다면 allowlist, 즉 스코어러가 어떤 kind를 요청할 수 있고 어떤 policy와 label을 쓸 수 있는지는 런타임에 A/B로 게이팅할 수 있다.
- 새 스코어러를 붙일 때 코드를 배포하지 않고도 GrowthBook 콘솔에서 allowlist 항목을 켜고 끌 수 있다.
이 구조의 첫 클라이언트로 panda_reports_embedding_v10_rough_spam이 함께 유입됐다. enforcement_user.yaml에 규칙이 새로 추가됐다.
- id: panda_reports_embedding_v10_rough_spam
when: '"panda_reports_embedding_v10_rough_spam" in score.labels'
then: { kind: act_suspend_user, perm: false, policy: "PlatformManipulation" }
- 이 규칙 자체는 예전 방식 그대로의 정적 매핑이다. 다만 라벨 이름 자체가 새로운 정보를 담고 있다.
panda는 X의 내부 spam 검출 시스템 이름으로 보인다 추정.reports_embedding_v10은 리포트 embedding을 기반으로 하는 검출기의 10번째 버전이다.rough_spam은 확신도는 낮지만 스팸일 가능성이 높다는 판정이다.
- 조치는 임시(perm: false) suspend에 PlatformManipulation policy를 붙이는 것이다.
- 즉 이 검출기는 embedding을 기반으로 하고, 버전이 v10까지 올라왔다. 계속 재학습되는 파이프라인이라고 볼 수 있다.
정본 반영 services/x.md에서 세 군데를 고쳤다.
- 4세대 표에 4세대+8 행을 추가했다.
- 한 줄 요약 문단을 확장해 4세대+8 커밋을 서술했다.
- BDSM 절 뒤에 "4세대+8 (2026-08-25) — AES
generic_actions패스스루 배선" 소절을 추가했다.
의미 이 변경의 의미는 1-7 끝에서 4세대+8 공개 전체의 세 축과 함께 정리한다.
1-2. VM Ranker가 로컬 계산을 걷어 내고 서버가 돌려준 점수를 그대로 쓴다
VM Ranker 경로에서 home-mixer(For You와 Following 등 X 홈 타임라인 피드 요청을 서빙하는 서비스)가 하던 로컬 계산 층이 빠졌다. 이제 home-mixer는 rank server가 돌려준 점수를 그대로 쓰고, 다양성은 서버 쪽 DPP(다양성 재정렬 기법)가 처리한다.
배경 VMRanker는 home-mixer에서 slate reranker 역할을 하는 원격 gRPC 서비스다. Phoenix가 뽑은 후보 점수 위에서 DPP(Determinantal Point Process) 기반 다양성 재정렬을 수행하고, 그 파라미터는 θ=0.65, K=150이다. 지금까지 home-mixer/scorers/vm_ranker.rs는 value model 두 개를 지원했다.
author_diversity는 MPN 폴드백을 기반으로 저자 다양성을 감쇠시키는 값을 home-mixer가 로컬에서 계산해 rank server에 전달했다.- default는 rank server가 반환한 점수를 그대로 사용했다.
- 4세대+3(2026-08-18)의 SID 필드 7개 연결이 이 vm_ranker에 붙은 것이다. SID(semantic ID)는 아이템을 discrete token ID 시퀀스로 나타낸 것이다.
4세대+8이 이 경로를 크게 단순화했다. ranking_scorer::effective_head_weights 88줄과 MPN 폴드백 128줄을 제거했다.
VM Ranker 스코어러에서 저자 다양성 값 모델과 MPN 폴드백 로직이 삭제됐다(
home-mixer/scorers/vm_ranker.rs, +11/−128).삭제된 코드는 다음과 같다.
// 삭제됨: const AUTHOR_DIVERSITY_VALUE_MODEL_ID: &str = "author_diversity"; const MPN_FOLD_MULTIPLIER_MAX: f64 = 10.0; pub struct VMRanker { pub client: Arc<dyn VMRankerClient>, pub xds_client: Option<Arc<dyn VMRankerClient>>, pub author_cold_start: AuthorColdStart, // 삭제 } // score() 안의 MPN fold-back 로직 전체 삭제: let fold_weights = (query.params.get(EnableMpnScoring) && query.params.get(VMRankerValueModelId) == AUTHOR_DIVERSITY_VALUE_MODEL_ID) .then(|| ScoringWeights::from_params(&query.params)); // ...MpnParts.pos/neg × (returned/sent).clamp(0, MPN_FOLD_MULTIPLIER_MAX) 계산 // ...AuthorColdStart.apply로 후처리
새로 추가된 상수는
const DPP_VALUE_MODEL_ID: &str = "dpp";하나다. 즉 값 모델 ID 하나(author_diversity)를 삭제하고 다른 하나(dpp)로 교체했다.새 score()는 VM Ranker가 반환한 점수를 그대로 통과시키고, 로컬에서 다시 계산하지 않는다. MPN 폴드백과 AuthorColdStart 후처리가 모두 사라졌다.
candidates.iter().map(|c| Ok(PostCandidate { score: score_map.get(&c.tweet_id).copied().or(c.score), ..Default::default() })).collect()후보마다 가중치를 조합해 VMRanker로 보내던 메서드 88줄도 삭제됐다(
home-mixer/scorers/ranking_scorer.rs, +28/−201).- 삭제된 메서드는
ScoringWeights::effective_head_weights다. - 이 메서드는 candidate마다
xai_vm_ranker_proto::HeadWeights프로토버프를 만들어 VMRanker에 전송하는 로직이었다. - post_unexplored 승수 계산, click_dwell_time의 low-fav-rate 페널티, quoted_vqv duration 체크, reply weight의 후보별 조정처럼 후보마다 가중치를 조합하는 처리였는데, 지금은 전부 사라졌다.
- 삭제된 메서드는
이 단순화가 뜻하는 바는 네 가지다.
- 다양성 계산이 클라이언트인 home-mixer에서 서버인 VMRanker로 옮겨 갔다.
- 이전에는 home-mixer가 다양성 요구사항을 담은 head weights를 후보마다 만들어 rank server에 전송했다.
- 이제는 rank server가 자체 DPP 로직으로 다양성을 처리하고, home-mixer는 반환된 점수만 소비한다.
author_diversity에서dpp로 바뀐 명칭의 실체는 아직 분명하지 않다.dpp가 정말로 기존 재순위 절의 DPP(θ=0.65, K=150)를 가리키는지, 아니면 rank server 안에서만 쓰는 다른 DPP 인스턴스인지는 이 커밋만으로 확정할 수 없다 추정.- VMRankerValueModelId 파라미터도 함께 정리된 흔적이
home-mixer/params/param.rs에 남아 있다(+10/−29로 순 감소).
AuthorColdStart는 사라진 것이 아니라 자리를 옮겼다.home-mixer/scorers/author_cold_start.rs파일은 여전히 존재하고 +12/−9로 수정됐다.- VMRanker의 후처리에서만 빠졌으므로, RankingScorer나 Phoenix 파이프라인 같은 다른 지점에서는 여전히 적용될 가능성이 있다.
- 삭제된
effective_head_weights가 계산하던 값은 이제 서버가 자체 로직으로 계산하거나, head weights라는 개념 자체가 다른 방식으로 대체된 것으로 보인다.- 이 메서드가 계산하던 head weights는 favorite·reply·retweet·share·dwell·quoted_vqv 등이다.
- VMRanker가 원래의 phoenix 점수를 그대로 소비하고 자체 DPP만 적용한다고 해석하는 편이 자연스럽다 추정.
이 단순화는 4세대의 큰 방향, 즉 home-mixer에서 로컬 계산과 캐시를 걷어내고 서빙 산출을 우선 사용하는 흐름의 연장이다. 4세대+5의 UseServedSlateContext 게이트가 slate context 계산을 로컬에서 Phoenix 서빙으로 옮겼던 것과 짝을 이룬다.
"AUTHOR_DIVERSITY_VALUE_MODEL_ID: &str = "author_diversity"" ← 삭제 "DPP_VALUE_MODEL_ID: &str = "dpp"" ← 신규 —
home-mixer/scorers/vm_ranker.rs
정본 반영 services/x.md에서 세 군데를 고쳤다.
- 랭킹 절의 VMRanker 서술에 값 모델 명칭 변경과 effective_head_weights 삭제를 반영했다.
- 다양성 절에 DPP가 VMRanker의 유일한 값 모델이 됐다는 사실을 반영했다.
- 파라미터 변경 관행에 파라미터 -29줄(MPN·author_diversity 관련 상수 정리)을 적었다.
1-3. Following 피드가 인용·리트윗 원 저자의 블록까지 따른다
Following 피드가 블록 관계를 인용 저자와 리트윗 저자까지 넓혀 확인한다. 원 저자가 뷰어를 블록했다면, 뷰어가 팔로우하는 사람이 그 포스트를 인용하거나 리트윗해도 뷰어에게는 보이지 않는다.
배경 Following 지면은 인네트워크 최신 포스트만 노출하는 reverse-chron 파이프라인이다. 4세대+3에서 Following 전용 VF hydrator가 분기된 이후 이 파이프라인은 계속 정교해지고 있다. 이번에는 블록 관계 처리를 인용 저자와 리트윗 저자까지 확장하는 작업이 추가됐다.
새 hydrator(후보마다 필드를 채워 붙이는 단계)인
FollowingBlockedByHydrator는 인용 저자와 리트윗 저자 가운데 누가 뷰어를 블록하고 있는지 SocialGraphClient에 묻는다.파일은
home-mixer/candidate_hydrators/following_blocked_by_hydrator.rs(65줄 신규)다.candidate마다 quoted 저자와 retweeted 저자의 user_id 리스트를 모아 한 번에 묻는다.
let user_ids: Vec<u64> = candidates .iter() .flat_map(|c| c.quoted_user_id.into_iter().chain(c.retweeted_user_id)) .collect(); let blocked_by_user_ids = self.socialgraph_client .check_blocked_by(query.user_id, &user_ids) .await?;그 결과는 후보 필드 두 개에 설정된다.
author_blocks_viewer는 retweeted 저자가 뷰어를 블록하면 true다.quoted_author_blocks_viewer는 quoted 저자가 뷰어를 블록하면 true다.
4세대에 이미 있던 필터
AuthorSocialgraphFilter에 새 조건이 연결됐고, 이제 6개 조건 중 하나라도 참이면 candidate를 제거한다.이전에는 뷰어에서 저자로 향하는 block과 mute만 확인했다. 이제는 저자에서 뷰어로 향하는 방향과 quote·retweet 체인의 원 저자까지 여섯 갈래로 확장됐다.
if muted // 뷰어가 저자를 mute || blocked // 뷰어가 저자를 block || author_blocks_viewer // 저자(=retweeted 저자)가 뷰어를 block || quoted_author_blocks_viewer // quoted 저자가 뷰어를 block || viewer_blocks_quoted_author // 뷰어가 quoted 저자를 block || viewer_blocks_retweeted_user // 뷰어가 retweet된 저자를 block { removed.push(candidate); } else { kept.push(candidate); }
두 단계는 reverse-chron 파이프라인에 연결됐고, 필터는 VF 필터 앞에 놓였다(
home-mixer/candidate_pipeline/reverse_chron_posts_pipeline.rs, +27/−2).let post_selection_hydrators = vec![ Box::new(FollowingBlockedByHydrator::new(socialgraph_client).await), // 신규 Box::new(VFFollowingCandidateHydrator::new(...)), // ... ]; let post_selection_filters = vec![ Box::new(AuthorSocialgraphFilter), // 신규 Box::new(VFFilter), Box::new(AncillaryVFFilter), ];AuthorSocialgraphFilter는 VF 필터 앞에 위치한다. 즉 블록이나 뮤트로 걸러진 후보는 VF 판정 조회 자체를 건너뛰기 때문에 성능도 함께 개선된다.SocialGraphClient는 XAI candidate_pipeline component_library에 속한 클라이언트다. TLS S2S로 flock에 접속하며, 서비스 자체는 공개되지 않았다.
이 변경이 뜻하는 바는 세 가지다.
- 블록의 의미가 전이적으로 넓어졌다.
- 이전에는 "당신이 A를 팔로우하지 않으면 A의 포스트가 보이지 않는다"였는데, 이제는 "당신이 A를 팔로우하더라도 A가 원 저자 B를 리트윗했고 B가 당신을 블록하고 있으면 보이지 않는다"까지 확장됐다.
- B가 당신을 블록한 사실이 팔로잉 관계와 무관하게 존중된다.
- 적용 범위가 넓은 정책 결정이며, X가 quote와 retweet을 통한 우회 노출을 명시적으로 막기 시작했다는 신호로 해석한다 추정.
- 성능 면에서는 요청마다 SocialGraphClient에 원격 조회가 하나 추가된다.
- 다만 모든 candidate의 quoted·retweeted user_id를 한꺼번에
check_blocked_by로 묻는 batch 조회이므로, 고정 비용이 아니라 후보 수 대비 sub-linear로 늘어난다.
- 다만 모든 candidate의 quoted·retweeted user_id를 한꺼번에
- 이 hydrator는 Following에만 연결됐다.
- For You(algorithmic) 파이프라인의 hydrator 목록에는 이 hydrator가 아직 없다.
- 연결이 대칭을 이룬다면 다음 커밋에서 For You에도 붙을 가능성이 있다 추정.
정본 반영 services/x.md에서 두 군데를 고쳤다.
- Following 지면 서술에 블록 확장 소절을 추가했다.
- 필터 21개 표의
AuthorSocialgraphFilter상세를 갱신했다. 기존에도 있던 필터지만 의미가 확장됐음을 반영했다.
1-4. 브라질 선거 필터의 계정 명단이 TSE 원본으로 크게 늘었다
브라질 2026 선거 필터의 계정 명단에 브라질 선거관리위원회 공개 데이터를 바탕으로 +3,379줄이 추가됐다. 필터의 동작 자체는 바뀌지 않았다.
- 필터
Brazil2026ElectionFilter의 파일home-mixer/filters/brazil_2026_election_filter.rs에 계정 리스트가 대폭 추가됐다(+3,379/−5, 파일 전체 4,947줄). - README 알림란에도 이 갱신이 명시됐다: "Account list updated August 25, 2026."
- 데이터 원본은 dadosabertos.tse.jus.br/dataset/candidatos-2026이다. 브라질 선거관리위원회(TSE)가 공개한 2026 선거 후보자 데이터셋이다.
필터 로직은 4세대+1에서 신설된 그대로이며, 세 단계로 동작한다.
dadosabertos.tse.jus.br의 신고 계정 명단을 하드코딩한다.- For You 파이프라인에서 이들 계정의 포스트를 제거한다.
- 뷰어가 명시적으로 팔로우한 계정은 예외로 둔다.
이는 Art. 28 § 1º-A of Electoral Resolution 23.610을 준수하기 위한 조치다.
명단을 싣는 방식에서는 투명성 관행이 보인다.
// User ids below are obfuscated; usernames are included for transparency.
// @OmarAzizSenador deleted his account at the time this code was written.
// @_ANDREDOPRADO no live account was found.
// @_EDUARDOMANTOAN no live account was found.
// @ADALBERTO_1111 no live account was found.
...
- user_id는 obfuscated 처리해 내부 X ID가 노출되지 않게 하면서도, @username은 그대로 코드에 남긴다.
- 각 계정 옆에는 "deleted his account"나 "no live account was found" 같은 상태 주석까지 붙어 있다.
- 공개 소스와 사용자 프라이버시(내부 user_id 유출 방지) 사이의 절충으로, 검증 가능성은 유지하면서 X의 내부 user_id 매핑은 감추는 방식이다.
- 규모는 4세대+1의 665개보다 훨씬 늘어난 것으로 보인다 추정.
- 정확한 계정 수는 diff 라인을 세어 따로 계산해야 한다. 4947줄 파일에 계정당 2~4줄이 들어간다면 대략 1200~2000개 규모다 추정.
정본 반영 services/x.md의 Brazil2026ElectionFilter 항목을 갱신해, 계정 리스트를 정기적으로 갱신하는 관행이라는 사실을 명시했다.
1-5. Phoenix 인덱스가 최소 경과 시간을 채우기 전의 데이터도 함께 불러온다
Phoenix의 offline 인덱싱 파이프라인이 게시 후 최소 경과 시간 조건이 있는 window마다, 아직 그 조건을 채우지 못한 데이터를 별도 스냅샷으로 함께 불러온다. 이 변경은 Phoenix rankall 파이프라인의 base 저장소에 들어갔다.
배경 phoenix-rankall/는 Phoenix의 offline 인덱싱·집계 파이프라인이다. 후보 pool을 여러 window로 나눠 유지하며, 예를 들어 1fav_uec_1day, video_14day, evergreen_video_1825day가 있다. 4세대+8은 아래 두 변경의 흐름을 이어받아 phoenix-rankall의 base 저장소 자체를 확장했다.
- 4세대+1에서
video_14daySID window가 추가됐다. - 4세대+6에서
EVERGREENretrieval dataset이evergreen_video_1825day→video_4to14day로 축소됐다.
변경은 phoenix-rankall/src/store/base.rs (+202/−61)의 새 상수와 로딩 로직이다.
const PREFLOOR_SLACK_SECS: f64 = 6.0 * 3600.0;
// window config에 min_age가 있으면:
if wc.min_age.is_some() {
let pf_name = format!("prefloor_{window_name}");
let pf_link = self.config.output_dir.join(format!("{pf_name}.parquet"));
let pf_path = if pf_link.exists() {
Some(pf_link)
} else {
find_latest_versioned_file(&self.config.output_dir, &pf_name)
};
// pf_path에서 배치 로딩...
}
- 특정 window가
min_age조건을 가지면, 그 조건을 아직 통과하지 못한 min_age 이전 데이터를 별도의prefloor_<window>.parquet파일로 유지해 함께 로드한다.min_age조건의 예는 "이 pool에는 게시 후 최소 N시간이 지난 것만 포함한다" 같은 것이다.
PREFLOOR_SLACK_SECS = 6시간은 prefloor 데이터가 min_age 문턱을 넘기 전까지 유지되는 여유 시간이다.
정본 반영 services/x.md의 Phoenix 학습·인덱스 절에 소절 "4세대+8 (2026-08-25) — rankall prefloor 스냅샷 배선"을 추가했다.
의미 이 변경은 4세대+2에서 phoenix-rankall-strato의 favoriteCount>=1 게이트가 제거됐던 흐름의 연장이다. 이전에는 인덱스에 들어가려면 조건을 통과해야 했지만, 이제 rankall은 아직 조건을 통과하지 못한 후보도 별도 pool에 유지해서 통과하는 시점에 즉시 프로덕션에 반영할 수 있게 한다. 콜드스타트와 freshness 관점의 개선이며, 신규 포스트의 인덱스 지연을 줄이는 방향이다 추정.
1-6. Phoenix 다운로드 클라이언트가 동시 실행 상한을 실제로 지키게 됐다
Phoenix 서빙이 임베딩 테이블과 체크포인트를 내려받을 때, 실제 동시 다운로드 수가 설정한 상한을 넘을 수 있던 문제를 고쳤다. 회귀 테스트도 2개 새로 추가됐다.
copy_port는 Phoenix 서빙에서 오브젝트 스토리지(S3/xai-o2)로부터 임베딩 테이블과 체크포인트를 다운로드하는 클라이언트다.이전 코드는
join_all(futures).await로 여러 다운로드를 병렬로 진행했다.- 그런데 rate limit 배치 안에서
join_all이 futures를 순차적으로 poll하면, 실제 in-flight concurrency가max_concurrent를 넘을 수 있다는 문제가 있었다.
- 그런데 rate limit 배치 안에서
수정 후에는 각 future를
tokio::task::spawn으로 별도 태스크에 감싼다. 이tokio::spawn하드닝은 rate-limit이 병렬 폴링 없이도 in-flight cap을 지키도록 고친 것이다.// Before: let batch_results = join_all(batch).await; // After: let batch_results = join_all(batch.into_iter().map(tokio::task::spawn)) .await .into_iter() .collect::<Result<Vec<_>, _>>() .map_err(|e| CopyPortError::Other(format!("copy_port download task join: {e}")))?;- 이렇게 감싸면 런타임 스케줄러가 실제로 병렬 실행한다.
- rate limit 로직(
join_rate_limited)은 다음 배치를 issue할 시점을 in-flight 카운트에 근거해 정확히 결정한다.
새로 추가된 회귀 테스트 두 개는 각각 다음을 확인한다.
rate_limit_caps_in_flight_despite_spawn은 6개 다운로드에 concurrency=2 상한을 걸었을 때 peak in-flight이 정확히 2인지 본다.rate_limit_paces_between_spawned_batches는 배치 사이의 페이싱이 유지되는지 본다.
의미 이번 변경은 서빙 시점의 병렬 다운로드 스로틀링 정확성을 잡는 하드닝으로, 4세대+7에서 Phoenix 체크포인트 로더 다중 노드 하드닝이 restore path의 OOM을 잡은 것과 짝을 이룬다. 두 작업은 4세대+6에서 Muon·GB300 학습이 export에 정식 포함된 이후 이어지는 학습·서빙 양쪽 인프라 성숙 사이클의 연장이다. 이런 stability 픽스가 매일 커밋에 붙는다는 사실 자체가, X가 4세대 오픈소스 릴리스를 검증 가능하고 프로덕션에서 신뢰할 수 있는 상태로 유지하는 데 조직 자원을 계속 투입하고 있다는 증거다.
1-7. grox, engagement counts, URT의 소소한 변경 4건과 그 밖의 정비
나머지는 규모가 작은 정리와 확장이다.
새로 알게 된 것그 밖의 변경
- grox(Grok 기반 nearline 주석 파이프라인)의 reply_spam 파이프라인을 소소하게 정리했다.
- 바뀐 파일은
grox/flows/reply_spam/task_rate_limit.py−10줄,plan_reply_ranking.py−6,plan_spam_comment.py−6,task_filter.py+2/−2다. task_rate_limit에서 −10줄이라 rate limit 로직 일부가 사라진 것으로 보인다. 다만 순수 삭제 규모만 드러나므로, 정확한 변화는 별도로 diff를 확인해야 한다.task_filter.py는 임계값을 바꾸지 않고 실행 흐름만 미세하게 조정했다. 11일 동안 15k에서 100k로 이어진 follower 임계 완화 흐름은 이번 커밋에서도 유지된다.
- 바뀐 파일은
- grox의
upa/(grox의 프로필·유저 분석 파이프라인으로 보인다 추정)에 새 상수 2개와 새 generator 하나가 추가됐다.- 바뀐 파일은
grox/flows/upa/constants.py+2,generators.py+11이다. - 이 서브플로우가 무엇을 하는지는 4세대에서 공개된 이후로도 정본에 아직 명시하지 못했으므로, 다음 커밋을 계속 지켜봐야 한다.
- 바뀐 파일은
- engagement count(favorite/reply/retweet 카운트)를 후보에 붙이는 hydrator를 소소하게 확장했다.
- 바뀐 파일은
home-mixer/candidate_hydrators/engagement_counts_hydrator.rs+16/−1이다. - 새로 추가된 로직은 더 확인해야 하는데, 필드 하나를 추가한 것으로 보인다.
- 바뀐 파일은
- URT(Universal Response Tree, X의 응답 렌더링 스키마) 마셜링 층을 소소하게 확장했다.
- 대상은 광고와 WTF(Who To Follow), client event 마셜링 로직이다.
- 바뀐 파일은 다섯 개다.
home-mixer/util/urt/mod.rs(+13/−8)ad_marshaller.rs(+18/−8)client_event.rs(+27/−7)wtf_marshaller.rs(+14/−3)reverse_chron_following/mod.rs(+12/−5)
- 개별 변화보다는 이번 공개가 URT 층 전반에 골고루 손을 댔다는 점이 특징이다.
- 4세대+7 restore 하드닝의 후속으로 Phoenix 학습·인덱싱 인프라를 소소하게 손질했다.
- 바뀐 파일은 여섯 개다.
- AuthorColdStart 스코어러는 VMRanker에서 소비가 사라진 것과 별개로 남아서 정비됐다.
- 바뀐 파일은
home-mixer/scorers/author_cold_start.rs+12/−9다. - 새 소비자가 어디인지는 더 확인해야 한다.
- 바뀐 파일은
- Night Owl(밤 시간대 fatigue 조정) 소스를 소소하게 확장했다.
- 바뀐 파일은
home-mixer/sources/following_night_owl_source.rs+5다.
- 바뀐 파일은
의미 4세대+8 공개 전체로 보면 축은 셋이다.
- enforcement 관행이 유연해졌다.
- AES가 하드코딩된 YAML 규칙에 더해, 스코어러가 직접 요청하는 일반 액션 파이프라인도 함께 지원한다. 새 스코어러를 붙이는 배포 사이클이 짧아졌다.
panda_reports_embedding_v10_rough_spam처럼 embedding을 기반으로 하는 spam 검출기가 계속 재학습되며 붙는 인프라가 자리를 잡았다.
- 랭커 경로가 크게 단순해졌다.
- VMRanker의 로컬 계산 층을 걷어내고 서버 측 DPP에 위임했다.
- 4세대+5의 slate context 이관과 짝을 이루는 아키텍처 리팩터링이며, home-mixer는 얇은 어그리게이터 역할만 맡고 계산은 gRPC 서버들이 담당한다.
- Following 지면의 정책이 넓어졌다.
- 인용과 리트윗 체인에 있는 저자의 블록 관계까지 존중한다. quote를 통한 블록 우회를 명시적으로 막았다.
4세대 전체의 흐름은 여전히 코드 공개, 검증 인프라 연결, experiment 트래킹, 아키텍처 정리, 정책 확장으로 이어지는 성숙 곡선을 그리고 있다. 길어야 사흘 간격의 커밋이 4세대+8(08-25)까지 12일 동안 계속 이어졌다는 사실 자체가, X가 이 오픈소스 릴리스를 마케팅 이벤트가 아니라 지속되는 코드 미러 관계로 유지하고 있다는 증거다.
2Tlow가 flow 기반 tokenizer로 프로덕션 CTR을 올렸다 — WeChat
자료 Tlow: Flow-based Item Tokenizer for Recommendation — arXiv 2608.24176, 2026-08-25 v1, 논문. 저자는 6명이다(Nian Li, Chonggang Song, Jingtao Ding, Lingling Yi, Yong Li, Qingmin Liao). HTML 판 abstract에 China's largest social media platform WeChat의 프로덕션 A/B라고 명시되어 있어 소속을 확인했다.
배경 Semantic ID(SID) 라인의 핵심 컴포넌트인 item tokenizer는 각 아이템의 의미 임베딩을 discrete token ID 시퀀스로 변환하는 모듈이다. 사실상의 표준은 RQ-VAE(Residual-Quantized VAE)다. Google의 2023 TIGER 논문 이후 Netflix Foundation Model, Kuaishou PushDualGen 등 여러 프로덕션 시스템이 채택했다. 하지만 RQ-VAE에는 알려진 문제가 세 가지 있다.
- codebook collapse가 일어나서 대부분의 code slot이 죽고 소수만 활용된다.
- quantization 오류가 residual로 축적되어, deep quantization 층에서 오차가 커진다.
- 불연속성 때문에 유사한 아이템이 완전히 다른 token 시퀀스로 매핑되어 학습이 어렵다.
Flow-based model은 invertible neural network로 임베딩 공간을 학습 가능한 분포로 변환하고 역변환한다. 이번 논문(Tlow)은 위 문제들을 normalizing flow로 접근하고, 저자들은 flow 기반 tokenization이 codebook 활용률과 semantic locality 두 축에서 RQ-VAE보다 낫다고 주장한다.
새로 알게 된 것A. 프로덕션 A/B에서 신규 아이템의 CTR 개선 폭이 더 컸다
프로덕션 A/B에서 글로벌 CTR이 +10.32%, 신규 아이템 CTR이 +11.64% 올랐다 확인.
"The retrieval model based on token IDs improves user CTR by 10.32% globally and by 11.64% for new items." — Tlow abstract, 2026-08-25
- 신규 아이템에서 개선 폭이 더 크다는 것은 콜드스타트에 특히 강하다는 시사다 확인 — 같은 abstract.
- semantic tokenization 라인의 핵심 이점, 즉 cold item에 대해서도 유의미한 표현을 확보한다는 점이 프로덕션 지표로 확인됐다 확인 — 같은 abstract.
새로 알게 된 것B. 평가 지면은 WeChat retrieval model이고, 세부 지면은 공개되지 않았다
- 평가 지면은 WeChat retrieval model이다 확인.
- 광고인지 유기 콘텐츠인지, 그리고 Look/Video Channels/Moments 가운데 어느 세부 지면인지는 abstract에 공개되지 않았다 확인.
새로 알게 된 것C. 저자 소속은 Tencent WeChat rec 그룹으로 확정했다
- Tencent 관련 저자 이름이 있고 WeChat이 China's largest social platform으로 abstract에 명시되어 있으므로, Tencent WeChat rec 그룹으로 확정했다.
- Tencent 관련 저자 이름은
Chonggang Song이다.
- Tencent 관련 저자 이름은
정본 반영 radar.md "이번 달 신규"에 ### Tlow (WeChat/Tencent) — flow-based item tokenizer가 RQ-VAE 대안으로 프로덕션 CTR +10.32% 절을 추가했다. 위치는 HEGM보다 위이고, SID 스레드에 링크했다.
의미
- SID/generative retrieval 스레드의 새 축으로, tokenizer 아키텍처 자체가 프로덕션에서 갈리기 시작했다.
- 지금까지 SID 라인은 대체로 RQ-VAE와 downstream 모델을 조합하는 방식이었다.
- Tlow는 tokenizer 층을 flow 기반으로 대체해도 프로덕션에서 유효한 지표 개선이 나온다고 밝힌 첫 진술이다.
- RQ-VAE의 codebook collapse가 실제 서빙에서 문제였다는 방증이며 추정, Tencent가 이 문제를 겪고 대안을 내놓은 것으로 해석한다.
- Tencent WeChat이 오랜만에 내놓은 프로덕션 진술이다.
- WeChat rec은 최근 몇 년 조용한 편이었다.
- Tlow가 나오면서 유기 콘텐츠 지면의 실제 아키텍처 진술이 재개되는 신호로 보인다.
- Weixin Video Channels(WeChat의 짧은 비디오 지면)의 rec 스택 논문이 이어질지 관찰할 가치가 있다.
- 앞으로 볼 지점은 두 가지다.
- code와 상세한 block-wise flow structure가 후속 자료에서 나오는지 본다.
- Kuaishou나 ByteDance, Xiaohongshu 같은 두 번째 회사가 유사한 tokenizer 대안을 진술하면
topics/승격 후보가 된다.
3TAGR은 라이브 커머스 광고에서 revenue를 +16.1% 올렸다 — 익명 플랫폼
자료 TAGR: Temporally Adaptive Generative Recommendation for Industrial Live-Streaming Advertising — arXiv 2608.24034, 2026-08-25 v1, 논문. 저자는 10명이다(Wencai Ye, Guangyi Liu, Chaoyi Wang, Wenbin Luo, Shengyu Wang 외). 저자 소속과 플랫폼 이름은 abstract에 명시되지 않았다. abstract에서는 "a large-scale e-commerce live-stream advertising platform"이라고만 언급한다.
배경 라이브 스트리밍 광고는 콘텐츠와 상품, 유저 피드백이 실시간으로 바뀌기 때문에 recommender에 강한 freshness 요구를 부과한다. 지금까지 generative recommender(SID 기반) 라인은 대부분 static 카탈로그를 가정했다. 라이브는 스트림이 열리고 닫히는 시간 척도가 분 단위라서 static token vocab의 유효 기간이 짧다. 이번 논문(TAGR)은 빠르게 바뀌는 라이브 콘텐츠와 상품, 유저 피드백에 맞춘 generative recommendation을 내놓았다.
새로 알게 된 것A. 프로덕션 A/B에서 진입률, 장바구니 클릭률, revenue가 모두 올랐다
- 프로덕션 A/B 결과는 다음과 같다 확인.
- live-room entry rate +8.5%
- shopping-cart click rate +7.4%
- revenue +16.1% over production baseline
새로 알게 된 것B. 시간축에 적응하는 구체적 메커니즘은 abstract에 나오지 않는다
- 아키텍처는 Temporally Adaptive Generative Recommendation이다.
- abstract에서는 "designed for rapidly changing live content, promoted products, and user feedback"라고 표현한다.
- tokenizer 재학습 주기나 online update처럼 구체적인 시간축 adaptation 메커니즘은 abstract에 나오지 않는다. 본문을 확인해야 하지만 이번 배치에서는 넘어갔다.
새로 알게 된 것C. 플랫폼은 저자 이름만으로 확정할 수 없다
- 중국 e-commerce live-stream의 대표 후보로는 Kuaishou(Kwai), Alibaba(Taobao Live), Douyin/ByteDance(Douyin E-commerce), Xiaohongshu가 있는데, 저자 이름은 이 가운데 어느 회사와도 직접 매칭되지 않는다 추정.
정본 반영 radar.md "이번 달 신규"에 ### TAGR — 익명 대형 e-commerce live-stream 광고 플랫폼, revenue +16.1% 절을 추가했다.
의미
- 라이브 스트리밍 랭킹 스레드에 세 번째 회사가 붙었다.
- 기존의 Twitch(Amazon) Multi-Objective와 Netflix의 라이브 콜드스타트에 이어, TAGR은 광고 지면이자 중국 e-commerce 라이브라는 세 번째 각도를 더한다.
- generative recommender가 광고와 라이브처럼 빠르게 변하는 도메인에서도 프로덕션 진술을 쌓고 있다.
- 이전까지는 static short-video나 e-commerce catalog를 기준으로 한 진술이 많았다. 이번에는 시간축 adaptation이 필요한 도메인에서 나온 진술이다.
- 플랫폼을 확인하지 못한 것이 가장 큰 약점이다.
- v2 이후 소속이 공개되거나 저자 이메일 도메인을 확인할 수 있으면 정본 승격을 다시 검토한다.
검증에서 뒤집힌 것
없다. 이번 배치의 신규 자료(X 4세대+8, Tlow, TAGR)는 기존 정본을 뒤집지 않는다.
- VM Ranker 단순화(4세대+8)는 4세대의 아키텍처 정리 흐름과 맞아떨어진다.
- Following 블록 확장은 정책 방향을 넓힌 것이고, Brazil 계정 갱신은 기존 컴플라이언스 관행을 유지한 것이다.
- Tlow와 TAGR은 각각의 관찰 스레드(SID/generative retrieval, live-stream)에 새 데이터 포인트로 붙는다.
레이더
- 신규 2건:
- Tlow(Tencent WeChat, arXiv 2608.24176): flow-based item tokenizer, 프로덕션 CTR +10.32%
- TAGR(익명 e-commerce live-stream, arXiv 2608.24034): temporally adaptive generative recommendation, revenue +16.1%
arXiv cs.IR triage 30건 가운데 2건이 통과했다. 통과하지 못한 자료는 대체로 offline 벤치마크 위주였고 industry provenance가 없었다. 그중 주목한 미통과 후보는 4건이다.
- Native Multimodal for CTR: Alibaba로 추정되나 프로덕션 진술은 확인하지 못했다.
- RecGPT-Mobile-V2: Alibaba 온디바이스 query prediction이다. 이전 V1을 정본에서 추적하지 않았다.
- WeMM-Embedding: Tencent WeChat multimodal embedding technical report다. retrieval 프로덕션 진술이 없다.
- RetrievalFormer: dual-encoder로 search와 rec을 통합한 index다. 프로덕션 규모가 명확하지 않다.
다음에 볼 것
- X 4세대+9(다음 커밋) — 4세대 원본이 나온 08-13부터 08-25까지 13일 가운데 9일에 커밋이 올라왔고, 가장 긴 공백은 사흘이다. 확인할 지점은 여섯 가지다.
- AES
generic_actionsallowlist가 GrowthBook을 통해 어떤 스코어러들에게 열리는지 본다. - VMRanker의
dpp값 모델이 유일한 선택지로 정착하는지, 아니면 다른 값 모델이 추가되는지 본다. AuthorSocialgraphFilter가 For You 파이프라인에도 연결되는지 본다.panda_reports_embedding_v10다음으로 v11이 언제 나오는지 본다. embedding 검출기의 재학습 주기를 관측하는 지점이다.prefloor스냅샷이 어떤 window에 처음 활성화되는지 본다.- 4세대+7의
SafetyPtosPolicyCrossValidator가 스팸과 혐오 발화 카테고리로 확장되는지 본다.
- AES
- Tlow 후속 — code 공개 여부와, Tencent의 다른 WeChat 지면(Video Channels·Moments·Look)에 대한 진술을 추적한다.
- TAGR 플랫폼 확인 — v2 이후 저자 소속이 공개되면 정본 승격을 결정한다. 저자 이름으로 Kuaishou / Xiaohongshu / Douyin 매칭을 다시 확인한다.
- 월요일(2026-08-31) 산업 블로그 스윕 — Pinterest engineering, research.atspotify.com, Google Research blog, LinkedIn engineering을 본다.