긴 대화가 흐트러질 때: 컨텍스트 윈도우와 AI 지원 코딩의 숨겨진 오류
Source: Dev.to
Context windows are not stable
나는 모델을 마치 시작 단계에서 설정한 모든 제약을 기억하는 상태를 유지하는 엔지니어처럼 계속 다루었다. 그것은 실수였다. 10시간에 걸친 디버깅 스레드에서 모델은 점점 원래 제약을 포기했다: 프로젝트는 Python 3.8과 동기식 코드를 요구했지만, 열두 차례 정도가 지나자 모델은 async‑native 스니펫과 이후 버전에서만 유효한 타입 힌트를 반환하기 시작했다. 이 행동은 무작위가 아니다. 어텐션은 최근 토큰을 선호한다. 초기 제약도 여전히 컨텍스트에 남아 있지만, 최신 설명, 코드 샘플, 오류 덤프에 비해 신호가 약해진다.
실제로는 내가 await을 도입한 제안을 병합했는데, 우리 호출 지점 중 어느 곳에서도 async를 사용하지 않았다. 테스트가 CI에서 실패했다. 모델을 비난하기 쉽지만, 실제 문제는 내 워크플로우였다: 나는 긴 스레드가 세부 정보를 축적하도록 두고, 정확성에 중요한 강제 제약을 다시 명시하지 않았다.
작은 드리프트가 큰 버그로 이어지다
하나의 풀 리퀘스트는 몇 개의 무해한 변경으로 시작되었습니다. 모델은 먼저 짧은 SQL 쿼리를, 그 다음 스키마 수정을, 그리고 쿼리에 맞추기 위해 필드 이름 변경을 제안했습니다. 각각의 제안은 개별적으로는 그럴듯해 보였습니다. 저는 그 중 세 가지를 수락했습니다. 클라이언트가 안정적인 컬럼 이름에 의존하고 있었는데 새로운 쿼리가 다른 정렬 의미를 가지고 있었기 때문에 프로덕션에서 모든 것이 깨졌습니다. 작은 편집들의 연속이 깨지기 쉬운 가정의 사슬을 만들었습니다.
드리프트는 종종 로컬 일관성을 유지하기 때문에 미묘합니다. 모델은 현재 컨텍스트를 중심으로 코드를 재작성합니다. 새로운 로컬 로직을 다루는 단위 테스트는 여전히 통과할 수 있습니다. 통합 테스트와 하위 소비자들은 나중에 실제 손상을 감지합니다. 제 경우에는 야간 작업이 일주일 동안 조용히 잘못된 집계값을 생성했으며, 아무도 눈치채지 못했습니다.
Hidden assumptions live between the lines
대부분의 실패 모드는 모델이 조용히 사용한 암시적 기본값에서 비롯되었습니다. 시간대, SQL 방언, 페이지네이션 동작, API 버전 관리 등이 우리를 계속 걸려 넘어뜨렸습니다. 예를 들어, 모델이 ORDER BY 없이 SQL LIMIT을 내보냈고 복제 클러스터에서 반환된 행들은 비결정적이었습니다. 이는 우리 집계 코드의 논리 오류처럼 보였지만 근본 원인은 모델이 당연하다고 가정한 ORDER BY가 없었기 때문이었습니다.
툴 통합은 이를 더욱 악화시킵니다. 툴 호출이 실패하면 부분적이거나 빈 데이터를 반환하고, 모델은 그 빈틈을 그럴듯한 내용으로 메우며 그 내용이 코드가 됩니다. 저는 타임아웃으로 인해 쿼리 출력이 잘려서 모델이 스키마를 만들어 계속 진행하는 것을 본 적이 있습니다. 외부에서는 이것이 환각처럼 보이지만, 내부적으로는 툴 응답에 대한 방어 장치가 없었기 때문이었습니다.
Practical ways I catch drift: logging, snapshots, and checks
After enough quiet failures I started treating every model turn as auditable. I snapshot prompts and responses alongside a short context hash. I log the last few system and user messages that influenced a suggestion. That lets me answer the question: which tokens were recent enough to sway the output? When a suggestion causes a test failure I replay the exact prompt and confirm the drift.
I also require machine‑checks before human review. Generated JSON or schema‑shaped outputs get validated with strict parsers. Generated SQL runs against a read‑only sandbox. If the model calls external tools I assert response shapes and non‑empty payloads before accepting downstream changes. For broader verification I keep experiments in a shared chat workspace so I can compare answers across models and sessions, and when I need a focused chain of sourcing I push notes into a research flow for cross‑checking with a focused sourcing tool.
놀라움을 막은 운영 가드레일
구체적인 조치가 모호한 규칙보다 효과적이었다. 나는 대화를 논리적 경계에서 재설정하기 시작했으며, 단순히 기분에 따라 재설정하지 않았다. 단일 기능에 대해 협상할 수 없는 제약 조건을 나열한 간결한 프롬프트를 유지하고 매 턴의 상단에 포함한다. 모델 출력에 스키마 검증자를 적용하고, 생성된 패치가 공개 식별자를 변경하면 즉시 실패하도록 한다. 이는 두 번의 우발적인 API 계약 변경을 방지했다.
또한 마이그레이션이나 다운스트림 계약에 영향을 주는 변경에 대해 저비용 인간‑인‑루프 게이트를 추가했다. CI가 집중된 통합 검사를 실행하기 전까지는 모델 제안이 병합되지 않는다. 나는 공유 환경을 사용해 대안 완성을 비교한다. 왜냐하면 의견 차이는 단일 답변보다 더 많은 정보를 제공하기 때문이다. 도구 호출이 불안정해 보이면 실패를 기록하고, 도구 오류가 해결될 때까지 모델 출력을 신뢰하지 않는다.