TL;DR
- Recall mode 실패 (slot ≈ 0 all cells) — 모델이 "Show me where the steak is placed."를 회상 Q&A 안 받고 next-action으로 응답. STEP_SYSTEM이 emit-next-subtask 강제하기 때문. prompt 재설계 필요.
- M3 over-advance 진짜 원인 = walk sub-window 위치 — m3v2(sub-task log)로 self-narrative 차단해도 같은 row에서 같은 pred("Close the freezer door"). STATE 프레임이 placement-직후 시점이라 모델이 "지나간 step"으로 인식. 메모리 표현 문제 ❌, fine-walk 구조 문제 ✓.
- m3v2 약모델에서만 미미한 효과: 2.5-flash memory slot .087→.107 (+22%), 3.5-flash 0→.003 (변화 무).
- 비용 추정 8× 빗나감: $38 추정 → $300 실비. 신모델 단가·이미지 페이로드 미반영.
- 16/18 cells 완성; gemini-3-flash-preview/m3v2 2 셀은 게이트웨이 비용 한도(429)로 미완.
0.00
Recall slot (all cells)
.107
Best m3v2 (2.5-flash mem)
~$300
Actual cost (vs $38 est)
📋 전체 row inspector (4,800+ rows, 16 셀 모두)
— slot ✓/✗·action·place·memory snippet까지 모든 row 다 보이는 다크 테마 인스펙터.
1 배경 / 목적
이전 v2 cross-model S=100 결과(260528)에서 두 가지 한계:
- M3 텍스트 메모리가 강모델서 0으로 붕괴 (over-advance) — running notes에 "Placed X"를 적어 target+1 예측.
- "최종 goal" 차원 미측정 — 데이터셋의
eval_spec.tests[].lang ("Show me where X is placed.") 회상 질의가 진짜 final goal인데 활용 안 함.
이번 실험: (a) target=recall 모드 추가 (memer-style 전 episode 메모리 누적 → 끝 lang query 1 call), (b) m3v2 메서드 추가 (mechanical sub-task log로 self-narrative 차단).
shared walk 최적화: 한 시나리오의 3 test가 같은 walk 메모리 누적 결과를 공유. observe()를 final call에서 skip해서 pristine 메모리 상태 유지. naive(test마다 새 walk) 대비 API 호출 ~60% 절감.
2 구현 — 5 파일 변경 + 14 신규 테스트
recall mode 흐름
walk : 모든 segment (transit 포함) 순차 → 1 call/segment, memory 누적 (1회만/scn)
final×N: 마지막 segment recent_window를 STATE로, lang query를 GOAL로
→ N=len(tests) 호출, observe=False (memory state pristine)
score: pred ↔ target_fine_subtask (같은 slot pipeline)
호출 수: ~14 calls/scn (walks ~11 + 3 finals) — naive의 ~40%
m3v2 sub-task log policy
_log: list[str] # step_out.raw 누적, step_out.memory 무시
observe(out, win) → log.append(out.raw)
context_parts() → "SUB-TASKS YOU HAVE EXECUTED SO FAR ...
1. <raw1>
2. <raw2>
...
The entries above are ALREADY emitted and considered done.
For THIS step, emit the SINGLE next sub-task.
Do not re-emit; do not skip ahead."
cache 격리 (memory vs recall 충돌 회피)
스모크 1회차에서 recall이 memory-mode 캐시를 잘못 hit해 wrong row 반환. Cache(target=...)로 subdir 분리:
memory: cache/<model>/<method>/<scn>/recall__<test_id>.json (기존 보존)
recall: cache/<model>/<method>/<scn>/recall/recall__<test_id>.json (신규)
테스트
71 / 71 green (이전 57 + 신규 14): recall_target_info canonical/sentinel/per-test 독립, m3v2 log accumulation/memory-ignore/format, runner shared-walk 호출 카운트/observe-skip 불변/cache resume.
3 결과 (S=100, n=300/cell, 16/18 완성)
RECALL mode — slot / place / action
| model | control | m1 | m2 | m3 | m3v2 |
| gemini-2.5-flash |
.000/.02/.10 |
.000/.04/.04 |
.003/.02/.04 |
.000/.01/.12 |
.000/.08/.19 |
| gemini-3-flash-preview |
.000/.12/.14 |
.003/.13/.03 |
.000/.24/.02 |
.000/.08/.24 |
— (cost limit) |
| gemini-3.5-flash |
.000/.13/.14 |
.000/.17/.00 |
.000/.25/.01 |
.000/.12/.21 |
.000/.15/.02 |
모든 셀 slot ≈ 0. action_acc가 약간 올라가는 건 모델이 memory에서 "place" 패턴을 흉내내는 결과 — 틀린 object/place로 (아래 §4.1).
MEMORY mode — m3v2 신규 컬럼
| model | control | m1 | m2 | m3 | m3v2 (신규) |
| gemini-2.5-flash |
.040/.50/.04 |
.013/.42/.10 |
.033/.45/.12 |
.087/.59/.24 |
.107/.44/.27 |
| gemini-3-flash-preview |
.060/.57/.07 |
.267/.67/.39 |
.337/.73/.41 |
.097/.59/.10 |
— (cost limit) |
| gemini-3.5-flash |
.000/.61/.00 |
.237/.62/.32 |
.290/.65/.36 |
.000/.59/.00 |
.003/.56/.02 |
m3v2 효과는 약모델에서만 미미하게 양성 (2.5-flash slot .087→.107 +22%). 강모델(3.5-flash) m3=0/m3v2=.003 — 사실상 동일하게 붕괴 (§4.2 참조).
3.5 결과 예시 — RECALL mode (메인 측정)
R-A. 회상 영상 — composite (placement event + recall call)
시각화 정직성 노트: 각 영상은
2개 phase로 구성됨:
- Phase 1 — PLACEMENT EVENT (초록 테두리): 에피소드 중간 어느 시점에 실제 placement가 일어난 sub-window. 모델은 회상 시점에 이걸 직접 보지 못함 — 메모리가 잡아냈어야 할 사건.
- Phase 2 — RECALL CALL (빨간 테두리, 에피소드 끝): 모델이 final call을 받은 시점. 모델이 실제로 본 8 STATE 프레임은 노란 STATE 표식으로 마킹 (마지막 segment 전체에 걸쳐 ~15초 간격으로 분포). 나머지 빨간 테두리 구간은 인간이 last_segment의 task 흐름 보라고 표시한 것일 뿐 모델 미관측.
회상 질문(steak/bowl/fish placement)이 Phase 1에서 일어나고, 모델은 Phase 2에서 8 STATE 프레임 + 메모리만 가지고 답해야 함. 영상이 보여주는 두 구간 사이 시간 간격이 바로 "memory가 다리를 놓아야 했던 거리".
gemini-3.5-flash / m3 (recall에서 action_acc 최고 .21). 모든 4 케이스에서 slot ✗.
scn 0016 · "Show me where the steak is placed."
P1 (~15s): steak이 freezer로 옮겨지는 placement event
P2 (~120s): ArrangeUtensils 마지막 task 중 8 STATE 마킹
PRED ✗: "Navigate to the fridge"
scn 0016 · "Show me where the bowl is placed."
P1: RestockBowls의 bowl→cabinet placement
P2: ArrangeUtensils 중 8 STATE 마킹
PRED ✗: "Navigate to the cabinet" (fixture 방향성은 잡힘)
scn 0016 · "Show me where the fish is placed."
P1: OvenBroilFish의 fish→oven bottom rack placement
P2: ArrangeUtensils 중 STATE 마킹
PRED ✗: "Navigate to the fridge" (위치도 틀림)
scn 0020 · "Show me where the steak is placed."
P1: MoveFridgeToFreezer의 steak placement (시나리오 처음)
P2: OpenOven (last_seg 짧음, ~9s)
PRED ✗: "Open the fridge door" (마지막 task가 OpenOven이라 따라감)
R-B. 같은 (scn, test)에서 5 메서드 응답 — gemini-3.5-flash
모든 메서드가 회상 질의를 next-action으로 응답. 메모리에 누적된 정보를 read out하는 행동을 어떤 메서드도 못 함. 차이는 "어디로 navigate"만.
| scn / test | LANG · GT | control | m1 | m2 | m3 | m3v2 |
| 0016 / Steak |
LANG: Show me where the steak is placed. GT: Place the steak on a shelf in the freezer |
Navigate to the fridge |
Navigate to the fridge |
Navigate to the fridge |
Navigate to the fridge |
Navigate to the fridge |
| 0016 / Bowl |
LANG: Show me where the bowl is placed. GT: Place a bowl in the cabinet |
Navigate to the counter |
Navigate to the counter |
Navigate to the cabinet |
Navigate to the cabinet |
Navigate to the counter |
| 0016 / Fish |
LANG: Show me where the fish is placed. GT: Place the fish on the bottom rack of the oven |
Navigate to the fridge |
Navigate to the counter |
Navigate to the oven |
Navigate to the fridge |
Navigate to the fridge |
| 0020 / Steak |
LANG: Show me where the steak is placed. GT: Place the steak on a shelf in the freezer |
The steak is placed inside the oven. |
Open the fridge door |
Open the fridge door |
Open the fridge door |
The steak is inside the oven |
핵심 패턴: 아무도 회상 응답을 하지 않음. 메모리에 placement 정보가 있어도 모델은 STEP_SYSTEM의 "emit next sub-task" 지시를 따라 Navigate / Open 액션 출력. m3는 fixture는 약간 더 잘 맞춤(0016/Bowl/Fish에서 cabinet/oven 향함) — 메모리에서 추출은 하지만 회상 format으로 응답 안 함. recall slot_acc 0의 메커니즘 = prompt format 미스매치, 메모리 능력 부족 아님.
R-C. 같은 (scn, test)에서 5 메서드 응답 — gemini-2.5-flash (약모델 비교)
| scn / test | LANG · GT (요약) | control | m1 | m2 | m3 | m3v2 |
| 0016 / Steak |
Steak → freezer |
Navigate to the fridge |
Navigate to the refrigerator |
Navigate to the fridge |
Navigate to the kitchen to locate the steak. |
Navigate to the fridge |
| 0016 / Bowl |
Bowl → cabinet |
Navigate to the cabinet |
Navigate to the counter where the bowl is placed |
Navigate around the kitchen to find the bowl |
Navigate to the kitchen to find the bowl |
Navigate to the cabinet |
| 0016 / Fish |
Fish → oven (bottom rack) |
Navigate to the fridge |
Navigate to the refrigerator |
Navigate to the counter |
Navigate to the fridge |
Navigate to the fridge |
| 0020 / Steak |
Steak → freezer |
Open the fridge door |
Close the fridge door |
Navigate to the counter |
Navigate to the fridge |
Place the steak in the oven (act✓ obj✓ place✗) |
약모델은 더 verbose하지만 패턴 동일. m3v2 0020/Steak가 유일하게 "Place ..." format 응답 (action_ok=True) — 그러나 fixture가 freezer 아닌 oven으로 틀림 (action_acc 부풀리기 케이스의 전형).
R-D. 메모리 누락 메커니즘 (M3의 self-overwrite 특성)
M3 메모리는 직전 step의 self-narrative만 carry-forward → 에피소드 끝까지 가면 마지막 segment 활동만 보존, earlier-segment의 placement 정보 손실.
scn 0006 / 3-flash-preview m3 / 모든 3 test에서 메모리 동일:
"I have moved the onion and tangerine from the sink to the counter. I have also
moved the bowl to the sink. I am now placing the cup in the sink."
← 마지막 segment(ClearSink) 활동만
→ steak/bowl/fish 정보 0
Test 0 — "Show me where the steak..."
PRED: "Navigate to the fridge" (메모리에 정보 없어서 추측)
Test 1 — "Show me where the bowl..."
PRED: "Place the cup in the sink" (메모리 last 활동 그대로 반복)
Test 2 — "Show me where the fish..."
PRED: "Navigate to the fridge"
M3 메모리는 self-overwrite 특성상 episode 끝에선 마지막 segment 활동만 남음. earlier-segment 회상이 본질적으로 불가능. m3v2의 mechanical log는 이 한계는 해결 (모든 step 보존)이지만, recall 응답 format 문제는 여전히 해결 못 함.
3.6 결과 예시 — MEMORY mode (보조, m3 vs m3v2 over-advance)
M-A. m3 vs m3v2 같은 row 페어
3.5-flash · combo_0006 / MoveFridgeToFreezer — SAME pred 자체 메모리 차단해도 pred 동일
GT : Place the steak on a shelf in the freezer (tfi=1, K=4)
m3 pred: "Close the freezer door"
m3 mem : "Placed the steak in the freezer. Now closing the freezer door."
m3v2 pred: "Close the freezer door" ← 정확히 동일
m3v2 log : ['Place the steak in the freezer', 'Close the freezer door']
(scoring step = k=tfi=1)
3.5-flash · combo_0006 / RestockBowls — DIFFERENT pred 둘 다 over-advance, 다른 방향
GT : Place a bowl in the cabinet
m3 pred: "Close the cabinet door"
m3 mem : "Both bowls have been placed inside the cabinet. Closing the cabinet door."
m3v2 pred: "Complete the task" ← "task done" 선언
m3v2 log : 7 entries; last = ['Close the cabinet door', 'Complete the task']
3.5-flash · combo_0006 / OvenBroilFish — DIFFERENT pred 둘 다 "rack 넣기"로 over-advance
GT : Place the fish on the bottom rack of the oven
m3 pred: "Push the bottom rack back inside the oven"
m3 mem : "Placed the fish on the bottom rack. Now pushing the rack back inside."
m3v2 pred: "Slide the oven tray back inside"
m3v2 log : 6 entries; last = ['Place the fish on the oven', 'Slide the oven tray back inside']
2.5-flash · combo_0006 / RestockBowls — m3v2가 "task complete" narrative로 누수
GT : Place a bowl in the cabinet
m3 pred: "Open the cabinet door"
m3 mem : "The robot needs to open the cabinet, then pick up the bowls..."
m3v2 pred: "All bowls have been placed in the cabinet and the cabinet is closed, task complete."
m3v2 log : 7 entries; last = ['Close the cabinet door', 'All bowls have been placed...']
← log에 "task complete" narrative가 들어가버림 (mechanical log도 narrative 누수 가능)
패턴 요약: m3v2가 self-narrative 차단해도 pred 결과는 m3와 유사 (같거나 비슷한 over-advance). 약모델은 m3v2 log에 "task complete" 같은 narrative 응답이 들어가는 누수도 있음 — mechanical 보장이 100% 아님.
M-B. 오버레이 영상 — m3 vs m3v2 같은 시나리오
좌측 = 라이브 episode (agentview_left), 우측 = GOAL / GT / PRED / MEMORY 패널.
파란 테두리 = target sub-window(채점 시점) ±5초.
노란 STATE 표식 = 모델이 실제로 본 8 프레임 (cap_uniform across sub-window @ recent_window=8). 모두 gemini-3.5-flash.
시각화 정직성: memory mode에서는 모델 STATE 8 프레임이 sub-window(~9초) 안에 dense하게 분포 (~1초 간격) — recall mode와 달리 영상이 보여주는 구간과 STATE 영역이 거의 일치. 노란 STATE 표식이 약 1초 간격으로 깜빡임.
페어 1: scn 0016 MoveFridgeToFreezer (GT: Place the steak on a shelf in the freezer)
M3 (텍스트 narrative)
mem: "Placed the steak in the freezer. Now closing the freezer door."
pred ✗: "Close the freezer door"
M3v2 (mechanical log)
log: ['Place the steak in the freezer', 'Close the fridge door']
pred ✗: "Close the fridge door"
페어 2: scn 0020 RestockBowls (GT: Place a bowl in the cabinet)
M3 (텍스트 narrative)
mem: "Both bowls placed... Closing the cabinet door."
pred ✗: "Close the cabinet door"
M3v2 (mechanical log)
log: 7 entries · last = ['Close the cabinet door', 'Complete the task']
pred ✗: "Complete the task" (narrative 누수)
영상이 확인하는 핵심: 좌측 라이브 영상은 동일 시나리오 동일 시점(파란 테두리 = scoring step). m3와 m3v2 모두 STATE는 placement-직후 시점이라 "Place" 행동이 이미 끝난 것으로 보임. 메모리 표현이 다른데도 pred는 over-advance(target+1) 또는 narrative 누수로 비슷하게 떨어짐 — walk sub-window 위치가 결정적이라는 §4.2 finding을 영상으로 입증.
더 많은 row 예시 + 모든 16 셀 전체 데이터: 전체 row inspector.
4 핵심 발견 3가지
4.1 Recall mode 실패 — prompt format 미스매치
STEP_SYSTEM이 "emit next fine sub-task"이라 모델은 lang query를 회상 Q&A로 받지 않고 navigation/continuation action으로 응답.
LANG : "Show me where the steak is placed."
GT : "Place the steak on a shelf in the freezer"
PRED examples (gemini-3.5-flash, m3 recall):
"Navigate to the fridge"
"Place the cup in the sink" ← memory에서 '방금 한 일' 흉내
"Place the pot in the sink" ← memory: "Placed pan, carrying pot..."
메모리에 정답("steak in freezer")이 있어도 모델은 read out 안 하고 다음 행동 emit. action_acc 약간 상승은 memory의 "place" 패턴을 모방하기 때문(틀린 object/place).
해결 방향
회상 측정을 살리려면 (a) final call 전용 system prompt 분리 (v2 unified-prompt 원칙 깨야 함), (b) lang에 format 힌트 추가 ("Reply in 'Place the <object> in the <fixture>' format"), (c) StepOut 스키마에 answer_fixture 필드 추가. 현 prompt 디자인으로는 recall 측정 불가라는 점 자체가 finding.
4.2 M3 over-advance 진짜 원인 = STATE 위치 (메모리 표현 ❌)
같은 시나리오·test·모델로 m3 vs m3v2 직접 비교 (gemini-3.5-flash memory mode):
scn 0006 / MoveFridgeToFreezer / 3.5-flash:
GT : Place the steak on a shelf in the freezer (target_fine_idx=1, K=4)
m3 pred: "Close the freezer door"
m3 mem : "Placed the steak in the freezer. Now closing the freezer door."
m3v2 pred: "Close the freezer door" ← 동일!
m3v2 log : ['Place the steak in the freezer', 'Close the freezer door']
(scoring step = k=tfi=1, log entries from k=0과 k=1)
m3v2가 self-narrative를 차단했음에도 pred는 정확히 동일하게 over-advance. 이유:
- fine-walk가 K-way 비율분할 (데이터에 fine별 정확한 프레임 경계 없음 →
sub_start = tgt_seg.start + round(k*L/K))
- k=tfi 서브윈도우의 STATE 프레임이 placement-직후 시점 (분할 부정확성)
- 모델은 STATE만 봐도 "방금 끝났으니 다음은 close"로 추론 → 메모리 표현 무관
over-advance는 메모리 표현 결함이 아니라 fine-walk sub-window 부정확성. m3v2 메커니즘은 깨끗하지만 이 walk 구조에서는 m3와 거의 같은 점수.
4.3 m3v2 효과는 약모델에서만 미미하게 양성
| memory mode | m3 (기존) | m3v2 (신규) | 변화 |
| gemini-2.5-flash slot | .087 | .107 | +22% |
| gemini-3.5-flash slot | .000 | .003 | ~0 |
| gemini-3-flash-preview | .097 | N/A | — |
약모델은 텍스트 narrative 작성에 노이즈 → mechanical log가 도움. 강모델은 어차피 정확히 self-narrate 하므로 차이 없고, over-advance가 walk 구조 문제라 메모리 변경으로 못 잡음.
m3v2는 약모델 보조용으로만 가치. 강모델 비교에는 거의 무의미.
5 비용 — 추정 8× 빗나감
| 추정 (사전) | 실제 |
| 호출당 단가 | $0.0012 | ~$0.011 |
| 16/18 셀 총 비용 | ~$38 | ~$300 |
원인 분석:
- 신모델 단가: gemini-3.5-flash / 3-flash-preview가 2.5-flash 대비 5-10× 비싼 신모델. 옛 memory-mode S=100 평균($50)에서 직선 추정한 게 잘못.
- 이미지 페이로드: 3-cam composite(1536×512) + 메모리 keyframe(M2 8장 누적) + recent_window(8장). 호출당 입력 토큰 추정보다 훨씬 큼.
- shared-walk는 호출수 줄였지만 토큰 총량 안 줄어듦: 마지막 final call이 누적된 메모리 통째로 받음.
책임: 본런 전 1-2 시나리오 calibration 안 한 것. 향후 추정엔 보수적 마진 + sanity check 필수.
6 Next Steps
측정 메커니즘 개선이 메모리 메서드 비교보다 선결
- fine-walk sub-window 보정 (M3 over-advance 진짜 원인 처리)
- A. target_fine_idx-1 위치를 채점 step으로 (sub-window 한 칸 일찍)
- B. 채점 target±1 허용 후 두 지표 동시 보고
- C. fine별 정확한 프레임 경계 데이터 추가 요청 → 비율분할 폐기
- Recall prompt 재설계 (lang 측정 살리려면)
- final call 전용 system prompt OR StepOut 스키마 확장 (
answer_fixture 필드)
- lang에 format 힌트 ("Reply in 'Place the <object> in the <fixture>'")
- 3-flash-preview m3v2 미완 셀 처리
- Letsur 한도 갱신 (~$33 추정)
- 또는 16/18 final로 마무리 (이 리포트가 그 결론)
비용 관리 개선
다음 본런 전 단가 calibration 필수: 1 시나리오 × 모델별 × 메서드 → 평균 단가 측정 → 보수 마진 1.5× 적용. 신모델은 가설 굳혀진 뒤 마지막에만 (gemini-2.5-flash로 prototype 검증 → 후속).
7 재현 / 참고
# Recall mode 실행
OPENAI_API_KEY=<letsur> OPENAI_BASE_URL=https://gw.letsur.ai/v1 \
python -m scene_mem_api.runner \
--model gemini-2.5-flash --method m3v2 --target recall \
--stage full --limit 100
# Memory mode m3v2
python -m scene_mem_api.runner \
--model gemini-2.5-flash --method m3v2 --target memory \
--stage full --limit 100
# 결과 파일
results/<model>__<method>__full__recall.json # recall mode
results/<model>__<method>__full.json # memory mode
# 캐시 (격리)
cache/<model>/<method>/<scn>/recall/... # recall mode
cache/<model>/<method>/<scn>/... # memory mode
- 코드 변경: branch
main, 71 tests passing, try-except 0
- 실험 로그:
claude/260619/exp-recall_m3v2_S100.md
- 이전 리포트:
260602-api_track_snapshot.html
- 미완 셀:
gemini-3-flash-preview / m3v2 / {memory, recall} (cost limit 429)