자동 답장을 보내기 전에 그 요청을 처리한 턴을 찾아야 했다. 턴은 요청을 받아 답변을 마치는 한 차례의 처리다. 같은 채팅에서 다른 작업이 이어지거나 여러 요청이 한 턴에 모일 수 있어, 가장 최근 답변이라는 위치만으로 보낼 내용을 고를 수 없었다.
전달 본문에서 운영 안내를 빼 달라는 요청에 이어, 최종 답변을 메신저로 자동 회신하는 기능이 더해졌다. 집필 당시 Rust 소스와 가이드는 보이는 본문을 간단히 두면서 로컬 기록으로 요청과 답변을 연결하고 있었다. 이번 집필에서 메신저나 연결기를 실행하지는 않았다.
본문에서 뺀 운영 정보는 로컬 기록에 남겼다
메신저의 새 본문만 기존 AI 채팅에 넘기고, 요청 식별자와 접수 결과는 로컬 작업 기록에 저장한다. 사용자가 읽을 메시지와 운영자가 추적할 정보를 나눈 것이다. 본문에서 안내를 덜어내도 어느 요청을 전달했는지는 기록에 남는다.
전달 전에 대기 기록을 준비하고, 앱이 지정한 채팅으로 접수했다고 응답하면 접수 기록을 저장한다. 이때의 상태는 실제 수신 확인을 기다리는 단계다. 앱의 접수 응답만으로 AI가 그 요청을 읽었거나 작업을 마쳤다고 표시하지 않았다.
화면의 본문을 짧게 정리해도 운영 기록에는 원래 요청과 전달 시도, 나중에 연결할 턴이 남아야 했다. 로컬 기록을 읽을 권한과 공개할 정보의 범위도 따로 정해야 한다. 이 글에는 실제 방 이름·메시지·요청 ID를 싣지 않았다.
같은 문장보다 이번에 생긴 전달 항목을 찾았다
연결 코드는 지정 채팅에서 온 전달 항목인지, 기대한 형식인지, 잘린 출력이 아닌지부터 확인한다. 본문이 대기 중인 요청과 같아야 하며, 전달 전에 이미 있던 항목은 제외한다. 다른 요청에 연결한 항목도 다시 사용하지 않는다. 조건이 맞으면 요청에 턴 ID와 전달 항목 ID를 기록한다.
예를 들어 과거와 이번 요청의 문장이 같을 수 있다. 과거에 그 문장을 읽은 기록이 있다는 이유만으로 이번 요청의 수신을 확정하면, 새 질문이 처리되지 않았는데도 다음 단계로 넘어갈 수 있다. 여기서는 전달 전 항목 목록을 남겨 이전 것과 이번에 생긴 것을 구분했다. 이 예시는 설명용이며 실제 대화 내용이 아니다.
아직 접수를 확정하지 못한 준비 상태에서는 새 항목의 시각 조건도 검사한다. 조건에 맞는 연결을 관찰하면 별도 수신 기록을 남긴다. 다만 동일한 본문은 그 자체로 발신자의 신원이나 실행 권한을 증명하지 않는다. 이 구현은 지정한 방과 채팅을 전제로 한 로컬 연결이며, 모든 메시지 서비스에 통용되는 인증 방식은 아니다.
최종 문구와 턴의 완료를 함께 확인했다
회신 대상으로 고르는 것은 연결된 턴의 최종 답변이다. 진행 안내가 최종 답변과 함께 남아 있어도 진행 안내를 보내지 않는다. 읽은 코드는 턴이 완료 상태이고 오류가 없을 때 최종 답변 메시지 ID를 고른다. 저장된 대화 기록을 구성할 때도 최종 문구뿐 아니라 작업 완료 이벤트를 확인한다.
답변 본문은 같은 채팅·턴·최종 메시지 ID가 일치하는 로컬 기록에서 읽는다. 최근 몇 턴만 보여 주거나 긴 본문을 줄여 보여 주는 조회 결과로 전체 답변을 대신하지 않기 위한 선택이었다. 다른 요청의 완료 답변이 더 최근에 보이더라도 이 요청의 턴을 바꾸지 않는다.
회신을 기다리는 코드는 저장된 답변을 확인하며 답변을 새로 생성하는 모델을 호출하지 않는다. 읽기 범위를 지정 채팅으로 제한하는 구현과 실제 PC에서 연결이 계속 동작하는지는 다른 확인이다. 이번에는 원래 로컬 대화 파일이나 인증 파일을 직접 열지 않고 선택한 소스를 읽었다.
답변의 ID로 시도를 묶고 전송 전에 예약했다
여러 메신저 요청이 같은 턴에 들어오면 하나의 최종 답변을 공유할 수 있다. 소스는 채팅·턴·최종 메시지 ID를 함께 사용해 회신 식별자를 만든다. 원래 요청마다 별도 답장을 새로 만드는 대신 같은 최종 답변의 전송 기록을 함께 참조하도록 한 것이다.
회신 파일이 이미 있으면 그 기록을 반환한다. 새 시도에서는 입력창이 비었는지 먼저 확인하고, 새 파일만 만들 수 있는 create_new 옵션으로 예약 기록을 만든 뒤 저장을 동기화한다. 그다음 답변 본문을 보관하고 전송 단계로 들어간다. Rust 공식 문서는 create_new의 파일 생성을 원자적 동작으로 설명한다. 존재 확인과 생성 사이에 다른 작업이 파일을 만드는 경쟁을 줄이는 수단이다.
이 파일 예약이 메신저 전송까지 하나의 트랜잭션으로 묶지는 않는다. 예를 들어 예약을 저장한 뒤 실제 전송 전에 중단됐다면, 예약만으로 보내졌다고 결론 내릴 수 없다. 반대로 전송 여부가 불확실한 기록을 없애 다시 시도하면 중복 전송의 위험이 생긴다. 이 구현은 불확실한 시도를 자동 반복하지 않는 쪽을 택했다. 미전송과 중복을 모두 없앴다는 보장은 아니다.
출처: Rust 표준 라이브러리 문서, OpenOptions::create_new.
새 파일 생성의 의미와 경쟁 조건에 대한 공식 문서. 메시지 전송 보장은 별개다.
입력 대기와 전송 후 미확인을 다른 상태로 남겼다
사람이 메신저 입력창에서 글을 쓰고 있으면 회신은 기다린다. 아직 예약하지 않은 단계의 입력 대기는 나중에 다시 확인할 수 있다. 예약 뒤 각 답장 조각을 보내기 직전에도 입력창을 검사한다. 그 사이 사람이 글을 쓰기 시작했다면 해당 입력을 보존하고 중단한다. 이미 시도를 예약한 뒤의 중단과 전송 전 대기는 같은 재시도 상태가 아니다.
긴 답변은 결과 접두문을 붙인 조각으로 나눈다. 수신 경로는 그 접두문으로 시작하는 본문을 새 사용자 요청에서 제외해 회신이 다시 요청으로 돌아오는 것을 막는다. 이는 이 도구가 사용하는 분류 규칙이다. 접두문이 있는 텍스트를 누가 썼는지 인증하거나, 그 내용의 정확성을 검증하는 장치는 아니다.
전송 결과도 나누어 기록한다. 전송 호출이 성공으로 끝난 횟수와 대화에 같은 본문이 더 나타났는지 확인한 횟수를 따로 세고, 확인하지 못한 전송은 미확인으로 남긴다. 대화에 본문이 추가됐다는 관찰은 다른 사람이 받거나 읽었다는 증거가 아니다. 여러 조각 중 일부만 보내진 상황도 전체 답장 성공으로 합치지 않아야 한다.
검사한 코드와 실제 회신 확인을 구분했다
선택한 소스에는 새 전달 항목 연결, 완료 이벤트, 전체 답변의 채팅·턴·메시지 일치, 긴 텍스트 분할 등을 다루는 시험 정의가 있다. 이번에는 그 정의와 구현을 읽었고 원제품 시험을 실행하지 않았다. 과거 채팅이 시험 회신을 확인했다고 기록한 것과 이번에 실제 회신을 새로 검증한 것도 구별했다.
기존 최신 스냅샷 상태 모듈은 예약·확인 선언·미확정을 설명하는 독립 합성 예제다. 이번 연결기는 그 모듈을 설치한 결과가 아니며, 한 번의 현재 상태로 여러 요청을 합쳐 버리는 모델도 아니다. 회신의 소속을 실제 요청과 완료된 턴에 연결하는 문제가 추가돼 있으므로, 상태 용어만 같다고 같은 기능으로 취급하지 않았다.
비슷한 연결기를 인계한다면 아래 항목을 함께 남길 수 있다. 실제 수신·회신과 화면 조작은 그 연결기의 허용된 환경에서 따로 확인해야 한다. 이 글을 쓰며 원래 프로그램을 재시작하거나 모든 조건을 시험한 것은 아니다.
- 원래 요청과 이번 새 전달 항목을 연결한 근거.
- 대상 채팅·처리 턴·최종 메시지의 식별 관계와 완료 조건.
- 회신 예약, 조각별 전송 시도와 대화 내 관찰의 차이.
- 작성 중인 입력을 보존하는 조건과 불확실한 시도의 처리.
- 과거 기록, 이번 코드 검토와 아직 하지 않은 실화면 검증의 범위.