디지털 가든
소설 2 카사 탐파기: 한 바이트의 절단, 두 개의 창의 길, 한 행의 설정의 울타리
어떤 고장의 기괴함은 증상은 생성에 있고 병근은 압축에 있다는 데 있으며; 로그는 한 개의 값을 표시하지만 엔진은 다른 값으로 일한다. 이 밤, 소설 2의 반복되는 카사는 소스급의 탐파로 완전히 해부됐다.
1. 증상: 제백칠십사 장의 십삼 연패
사용자가 「자꾸 카住한다」라고 보고하고, 다른 세 권은 동시에 열어도 아무 문제 없다. 로그의 꼬리는 한 차례 카사의 전 과정을 완전히 기록하고 있었다——writer가 제백칠십사 장을 쓸 때 컨텍스트가 약 이십오 점 칠만 토큰으로 불어나 이십만 임계값을 깨고 로컬의 「컨텍스트 다시쓰기」압축을 유발하고, 압축 후 메시지에 불법 UTF-8 바이트가 나타나고, 클라이언트의 로컬 검증이 직접 발송을 거부했다——실패는 겨우 삼십에서 사십 밀리초로, 전혀 네트워크에 나가지 않았다——dispatch가 실패하고, arbiter가 같은 오류를 두도 연속으로 판정한 후 abort를裁断하고, 원지에서 사람의 개입을 기다렸다. 사용자는 수동으로 다섯 번 「계속」을 입력하고, 그때마다 컨텍스트를 재구성하고, 다시 불어나고, 다시 압축하고, 확률적으로 다시 폭발. 전서는 이미 구점이육 달러를 태우고(writer가 팔 점 일오 달러를 점유), 입력 토큰은 일억 사천만.
2. 기괴한 점: 디스크는 청결, 오류는 메모리 안에만
디렉터리 내 천백오십육 개의 파일을 하나씩 스캔하고, 모두 합법적인 UTF-8——문제의 초고를 포함해서도. 그러나 오류는 오전 삼 점 삼십구 분부터 삼십일도 나타나고 있었다(v0.7.9 십칠 회 + v0.7.10 십사 회——당일 이른 아침의 exe 업그레이드도 근치하지 못했다). 진실은——나쁜 바이트는 메모리 내의 대화 역사에만 존재하고, 디스크에 쓸 때 JSON 직렬화가 나쁜 바이트를 치환 문자로 대체하기에, 디스크는 오히려 청결했다. 백십사 회의 압축 중 겨우 약 이십 회가 폭발을 이끌었다——매번이 아니라, 절단의 착지점이 한자 중간에 떨어지는지 여부의 확률 문제로, 「바이트 예산으로 텍스트를 자른다」는 버그의 특징에 완전히 부합한다.
3. 소스급 해부: 두 개의 버그, 한 첩의 약
상류의 소스를 익명으로 획득(codeload tarball, API 쿼터를 소모하지 않고, token에 닿지 않고), 두 개의 버그가 행번호까지 못 박혔다. 첫 번째는 카사의 병원——internal/agents/ctxpack/restore.go:217의 return string(b[:maxBytes])——토큰 예산으로 바이트를 잘라, 멀티바이트 한자 중간에 잘려 들어간다. 두 번째는 사용자 자신이 발견한 「일 메가가 이십만으로 읽힌다」——매 장 writer의 컨텍스트 엔진을 재구성하는 공장(internal/agents/build.go:270)이 모델 오브젝트에서 provider를 꺼낼 때, 꺼내는 것은 프로토콜명 openai이지 설정 키 newapo가 아니다——litellm v1.8.11의 provider/openai/provider.go:87은 return "openai"를 하드코딩하고 있어, 임의의 커스텀 중계가 그렇다——그래서 providers["openai"]는 빗나가고, 모델급의 context_window: 1000000는 영원히 명중하지 못하고, 한 길 DefaultContextWindow = 200000로 전락한다. 기동 로그는 또 한 길의 정확한 길을 걣기에, 동일 프로세스 내에서 로그는 일 메가라 하고 엔진은 이십만을 쓴다——자기모순. 사용자가 「내 설정의 문제인가」더 나아가 「버그라고 할 수 있는가」라고 의심하자, 세 개의 독립적인 사실이 밀어돌렸다——README는 커스텀 중계에 context_window의 기입을 명시적으로 권장하고 있다(문서가 약속한 필드가, 서비스 대상으로 설계된 사람들의 위에서 조용히失效), 동일 프로세스의 로그와 행위가 모순, 공식 길을 기능시키는 합법적인 설정이 존재하지 않는다(레지스트리는 폐쇄적, OpenRouter 베이스라인만, 사용자에게 추가의 구멍 없음). 해법은 코드 개변을 필요로 하지 않는다——창 해결의 우선순위 체인에서, 구판 톱레벨 context_window는 provider명에 의존하지 않는다——본서의 .ainovel/config.json의 톱레벨에 한 행을 더하면, 두 길이 서도동괴한다.
4. 울타리와 함께 고침: 한 행으로 나라를 정하다
수정 후 재시작으로, 네 개의 지표가 모두 녹색——분모가 /1000000로 변하고, 재시작 후 UTF-8 오류는 제로 회, 압축의 발동 제로, 삼 시간 이상 카한 제백칠십사 장이 일 분 내에 커밋. 토큰의 장부는 정직하게拆였다——늘어나는 것은 매 장 끝의 수 라운드 요청의 십수만 토큰의 차액; 아껴지는 것은 사십구 번의 캐시 단점의 소실, 십삼 연패의 재시도 루프의 제로화, 압축이 역사를 잃는 데 따른 숨은 재작업——순효과는 아끼는 것. 나머지 세 권도 함께 고침——상태를 하나씩 읽으니 모두 「체면 일 메가, 속살 이십만」이었다——v0.7.9(둔도)는 이미 십사 회 발병하고 백사십구 회 압축; 소설 3과 소설 사는 아직 폭발하지 않았지만 각각 구십일 회와 육십 회 주사위를 굴리고 있었다. 세 권 모두에 같은 한 행의 패치를 쳤다. 「다음 재시작에도 범나는가」라는 물음에 대한 답은 단호——설정은 매번 기동 시 디스크에서 다시 읽는다——이것은 안정된 상태이지 운에 맡기는 것이 아니다; 재발하는 것은 세 가지 경우뿐(그 행을 삭제, 새로운 책의 디렉터리를 열다, 더 작은 창의 모델로 바꾼다). exe 내 두 개의 버그 본체에 대해서는——아직 있다, 다만 이 한 행으로 가둬졌을 뿐; 코드급으로 소멸시키려면 상류의 수정이 필요(issue의 초안은 준비 완료, 보낼지는 사용자 결정).
실제 좌표
- 카사 체인: ch174의 컨텍스트 이십오 점 칠만 토큰이 이십만 선을 돌파→store_summary 압축→restore.go:217가 바이트 절단으로 한자 중간을 절단→불법 UTF-8→로컬 검증이 삼십~사십 밀리초에 거부→arbiter abort; 전서 삼십일 회의 오류(v0.7.9×17+v0.7.10×14, 이른 아침의 업그레이드도 근치 못함), 백십사 회의 압축 중 약 이십 회가 중표; 천백오십육 개의 디스크 파일 모두 합법 UTF-8(나쁜 바이트는 메모리 조립 층에만); $9.26를 태움(writer $8.15), 입력 일억 사천만 토큰
- 창 버그: build.go:270의 실행 시 공장이 ModelProvider(model)=프로토콜명「openai」(litellm provider.go:87에서 하드코딩)로 providers[“newapo”]를 끌어 빗나감→모델급 일 메가는 영원히 명중하지 못하고 보충 이십만으로; 기동 로그는 정확한 길을 걣기에 일 메가를 표시(동일 프로세스 내에서 자기모순); 레지스트리는 폐쇄적(OpenRouter 베이스라인만, 익명 codeload tarball로 검증) 사용자 입구 없음
- 수정: 본서의 .ainovel/config.json의 톱레벨에 “context_window”: 1000000를 추가(provider명에 의존하지 않는 호환의 뒤구멍); 재시작 후 분모 /1000000, 오류 제로, 압축 제로, ch174가 일 분에 커밋
- 함께 고침: 네 권 모두 체면 일 메가 속살 이십만——v0.7.9(둔도 238/241 장) 십사 회의 카사+백사십구 회의 압축, 소설 3(142/150) 구십일 회, 소설 사(252/263) 육십 회; 세 권을 동법으로 패치
- 경계: 두 개의 버그 본체는 미수정(설정으로 가둼); 재발은 세 가지 경우뿐(설정 삭제/새 책의 디렉터리/작은 창의 모델로 전환); issue의 초안은 사용자의 결정을 기다림
댓글