답변이 미완료여도 사용한 토큰은 남을 수 있다
AI Chat의 사용량 처리에는 답변 완성 여부, 공급자가 반환한 실제 usage, 사용자 차감 정책이라는 서로 다른 기준이 있다. 출력 한도에 도달한 응답은 답변을 완성하지 못했어도 이미 토큰을 사용했다. 스트림에서 response.completed만 정산하면, usage가 함께 온 response.incomplete를 지나치며 사용량 기록을 빠뜨릴 수 있다.
AI Chat의 스트림 처리는 두 종료 이벤트를 공통 경로로 보내되 정산 상태를 각각 settled와 incomplete로 남기도록 바뀌었다. 이벤트에 실제 usage가 있어 처리한 경우, 요청 내부의 settledResponseId를 해당 응답 ID로 갱신한다. 이후 연결 중단의 사용량 회수 경로는 이 ID가 같으면 다시 조회·정산하지 않는다.
미완료 이벤트에 usage가 없으면 금액을 추정하지 않는다. 뒤이어 실제 중단이 발생한 경우에는 별도 회수 경로가 응답 상세를 조회할 수 있다. 회수한 usage가 있으면 aborted_partial로 처리하고, 없거나 조회가 실패하면 없는 사용량을 만들어 채우지 않는다. 미완료라는 상태만으로 토큰 수를 알 수는 없다.
도구 선택용 모델은 사용량을 남기고 사용자 차감은 0으로 둔다
도구를 고르는 보조 모델 호출은 최종 답변과 다른 정책을 갖는다. 라우팅 정산도 제목 생성처럼 사용자 금액을 0µ로 기록한다. 호출 자체를 기록에서 없애면 실제 사용 토큰과 호출 모델이 사라지고, 답변 모델의 요율을 적용하면 보조 호출을 무차감으로 처리하는 정책과 어긋난다.
| 조건 | 미완료 답변 | 보조 라우팅 |
|---|---|---|
| 기록할 상태 | incomplete | kind: routing, status: settled |
| 토큰 | 응답에 실제 usage가 있을 때 기록 | 실제 라우팅 모델의 usage 기록 |
| 사용자 금액 | 실제 사용량에 설정된 정산 정책 적용 | 명시적인 무차감 정책으로 0µ |
| 사용량 한도 | 일반 답변 정산 경로 | 월·계약 차감 카운터를 올리지 않음 |
라우팅 컨텍스트는 실제 모델명과 등급을 사용하되 모델 요율 스냅샷을 비운다. 라우팅 모델이 등록돼 있으면 그 모델의 등급을, 미등록이면 현재 대화 모델의 등급을 사용한다. 정산은 도구 횟수 없이 수행해 모델·도구·합계 금액을 모두 0으로 둔다. 통계 조회의 대상 종류에는 routing을 포함하므로 무차감 토큰도 조회할 수 있다.
0µ는 서비스의 사용자 가격이다. 공급자에게 비용이 발생하지 않았다는 뜻도, 공급자 청구서를 대조해 확인한 비용도 아니다. 내부 호출의 자원 사용과 사용자에게 적용하는 금액을 분리하면, 무차감 정책을 유지하면서 어느 모델이 토큰을 사용했는지는 남길 수 있다.
응답 ID와 정산 키가 모든 카운터를 보호하지는 않는다
settledResponseId는 한 요청 안에서 종료 이벤트 처리와 중단 회수가 같은 응답을 중복 처리하는 경로를 막는다. DB 정산은 별도로 고정된 중복 방지 키를 사용하며 라우팅 키는 request_id + ':routing'이다. 요청의 메모리 변수와 DB의 유일 키는 적용 범위가 다르므로 여러 프로세스의 모든 처리가 정확히 한 번이라고 묶어 설명할 수 없다.
현재 updateRoutingUsage()는 대화 누적 토큰을 먼저 증가시키고, credit context가 있을 때 정산을 호출한다. 답변 메시지의 토큰에는 라우팅 토큰을 넣지 않아 답변 모델과 문맥 크기의 통계를 구분한다. 두 쓰기는 하나의 트랜잭션이 아니며 정산 실패는 로그로 남겨 스트림을 계속 진행한다. 안정적인 정산 키도 이미 실행한 대화 카운터 증가까지 멱등적으로 만들지는 않는다. 일반 답변의 정산 기록과 한도 카운터도 함께 원자적으로 묶인 구조는 아니다.
스트림 테스트는 usage가 있는 미완료 응답의 통계·정산이 한 번 수행되고 후속 중단에서 다시 조회하지 않는지 확인한다. usage 없는 미완료 뒤 중단에서는 상세 조회 한 번으로 부분 정산하는 사례를 다룬다. 라우팅 정산 테스트는 요율 미조회, 실제 모델과 등급, 0µ 기록, 월·계약 카운터 무변경을 모델 모킹으로 확인한다. 외부 청구 정확성이나 동시 프로세스의 전체 카운터 일치는 이 검증 범위 밖이다.
사용량 집계의 시간 경계가 언제 저장된 usage를 읽을지 정한다면, 이 변경은 어떤 종료와 어떤 호출을 기록할지 정한다. AI Chat / Tutor에서는 usage가 있는 미완료 응답도 정산 기록에 남기고, 보조 호출은 토큰을 기록하면서 사용자 금액을 0으로 뒀다. 답변의 완성 여부가 사용량 기록을 결정하거나, 사용량 기록 자체가 사용자 차감을 뜻하지 않도록 나눈 것이다.