- name
- llm-grounding-additions-can-evict-existing-grounding
- description
- 프롬프트에 "당연히 관련 있는" 근거 파일을 더하면 능력이 늘 것 같지만, 이미 잘 되던 것을 밀어내 능력이 줄 수 있다. 근거 추가는 기능 추가가 아니라 예산 재배분이다 — 더하기 전에 재라.
- category
- trap
# LLM 근거를 더하면 있던 근거가 밀려난다
## Problem
LLM 에게 "레포가 선언한 것에 근거하라"고 시키는 파이프라인(게이트 도출 · RAG ·
코드 에이전트의 컨텍스트 수집기)에서, 프롬프트가 놓치고 있는 **명백히 관련 있는
선언 파일**을 발견한다. 당연히 넣으면 좋아질 것 같다.
- **증상**: 근거를 더했는데 목표 항목은 조금 오르고, **전혀 다른 항목이 무너진다.**
- **근본 원인**: 프롬프트는 **고정 예산**이다. 근거 추가는 기능 추가가 아니라
**예산 재배분**이고, 새 텍스트가 길면 기존 근거를 창 밖으로 밀어낸다.
주의(attention)도 마찬가지로 희석된다.
- **흔한 오해**: *"관련 있는 정보를 더 주는 게 나쁠 리 없다"* · *"노이즈만 안 넣으면
된다"*. 밀어내는 것은 노이즈가 아니라 **진짜 관련 있는 내용**이었다.
## 실측 (BSVibe, 2026-08-26)
검증 게이트 deriver 는 레포 매니페스트를 근거로 검사 명령을 도출한다. 조사해 보니
`pyproject.toml` 에 `[tool.ruff]` / `[tool.mypy]` 가 **없었다** — 설정이 전부
`ruff.toml`(7,587B) · `mypy.ini` 에 있었고 deriver 는 그걸 본 적이 없었다.
즉 `ruff format` 은 **선언 자체가 안 보이는** 상태였다. 가설: 보여주면 살아난다.
과거 실입력 2건 · 같은 모델 · 변수는 "툴 설정을 매니페스트에 더하느냐" 하나,
각 n=5(총 20셀, 오류 0):
| arm | `ruff check` | `format --check` | `mypy` | `lint-imports` |
|---|---|---|---|---|
| 기존 (pyproject 만) | 10/10 | **0/10** | 10/10 | **10/10** |
| **+ 툴 설정** | 10/10 | 3/10 | 10/10 | **3/10** |
`format` 은 0→3 으로 조금 올랐고, **`lint-imports` 는 10/10 → 3/10 으로 무너졌다.**
순효과 음수. `ruff.toml` 의 대부분이 `[lint.per-file-ignores]` 경로 나열이라,
pyproject 안쪽(바이트 3566)의 `[tool.importlinter]` 근거를 밀어냈다.
같은 날, 같은 코드베이스에서 **독립적으로** 확인된 사실: CI 근거 수집의 바이트
예산이 문자로 차감돼 예산의 1.67배를 쓰고 있었고, 그 상한의 존재 이유가 코드에
*"a repo with 40 workflows would otherwise crowd the manifests out of the prompt
entirely"* 라고 적혀 있었다. **같은 밀어내기가 두 층에서 일어나고 있었다.**
## Solution
1. **근거를 더하기 전에 A/B 로 재라.** 같은 실입력 · 같은 모델 · 변수는 "그 파일을
넣느냐" 하나. n≥5, 입력 2건 이상.
2. **목표 항목만 보지 마라.** 이전에 잘 되던 항목 전부를 같은 표에 넣어라.
회귀는 목표 항목 옆 칸에서 일어난다.
3. **길이를 먼저 봐라.** 넣으려는 파일이 기존 근거보다 크면 그 자체가 경고다.
설정 파일은 대개 *선언 몇 줄 + 예외 목록 수백 줄*이다 — 신호 대비 부피가 나쁘다.
4. **채널을 나눠라.** 이미 잘 되고 있는 근거와 새 근거를 **별도 블록**으로 두고
각자 예산을 갖게 하라. 한 덩어리에 합치면 큰 쪽이 이긴다.
5. **예산은 자기 단위로 세라.** 바이트 예산이면 바이트로 차감하라
(→ `ascii-fixture-cannot-catch-byte-vs-char-confusion`).
```python
# 나쁨 — 한 dict 에 합치면 큰 파일이 작은 파일을 창 밖으로 민다
manifests = {**manifests, **tool_configs}
# 나음 — 별도 블록 + 자기 예산. 없으면 블록 자체를 생략한다
messages = build(manifests=manifests, ci_declarations=ci or None)
```
## Key Insights
- **프롬프트 근거는 단조 증가하지 않는다.** 코드에서 함수를 하나 더하는 것과
다르다 — 근거는 서로 **경쟁**한다.
- **"안 보여준 것"을 찾는 것과 "보여줘야 하는 것"은 다른 질문이다.** 안 보이는
선언을 발견한 것 자체는 옳았다. 그렇다고 그것을 넣는 게 답은 아니었다.
- **가설이 기각될 때 방향까지 볼 것.** 나는 "효과 없음"을 예상하고 기각을
준비했는데, 실제로는 **반대 방향**이었다. 그 차이가 결정을 바꿨다.
- 다음에 먼저 확인할 것: **넣으려는 파일의 바이트 수 / 기존 근거의 바이트 수.**
## Red Flags
- 프롬프트 수집기에 파일을 "당연히 관련 있으니" 추가하려 할 때
- 추가하려는 설정 파일이 기존 근거보다 크거나, 대부분이 예외·무시 목록일 때
- 프롬프트 조립부에 truncation/cap 상수가 있는데 그 근처 주석이
*"crowd out"* · *"otherwise the model sees only …"* 같은 말을 할 때
- A/B 결과를 **목표 항목 한 칸만** 보고 판단하려 할 때
- 근거를 늘렸는데 게이트/출력이 **짧아졌을** 때 (밀어내기의 직접 신호)
Auf GitHub ansehen