AI 자동화 프로젝트 3: 검토를 붙이면 답이 좋아질까
TL;DR
- 작성 → 검토 → 수정의 세 단계를 연결했다. Kafka가 작업을 전달하고, 두 역할은 Coral에 남긴 대화를 읽어 다음 답변을 만든다.
- 실제 모델 실험에서 오류 초안 세 개는 고쳐졌지만, 정상 초안 하나는 나빠졌다. 검토자는 다섯 사례 모두 “수정 불필요”라고 답했다.
- 초안·검토·수정본을 비교하는 콘솔과 중간 장애 복구까지 구현했다. 검토 단계가 품질 개선에 기여하는지는 별도 비교 실험으로 확인해야 한다.
들어가며
2편에서는 작업 열 개가 모두 완료됐는데도 답변에는 오류가 있었던 일을 기록했다. 결과를 보존하고 Jev 평가를 연결할 자리는 만들었지만, API 키가 없어 실제 Jev의 판정은 확인하지 못했다.
이번에는 기존 Ollama 모델로 작성과 검토를 나눠 봤다. 한 번 만든 답변을 다른 역할이 읽고 의견을 남기면, 최종 답변이 나아질까? Kafka와 Coral을 함께 쓰는 이유도 이 흐름 안에서 구체화하고 싶었다.
Coral에 남긴 대화를 다음 작업에서 읽기
이전까지 Coral은 최종 결과를 메시지로 남기는 역할이었다. 이제는 writer와 reviewer가 같은 스레드에 초안과 검토 의견을 남기고, 다음 역할이 그 대화를 읽도록 했다.
역할은 둘이지만 작업 단계는 셋이다. 작성자가 초안을 만들고, 검토자가 의견을 남기고, 작성자가 수정본을 만든다. 두 역할은 같은 Java 앱에서 같은 qwen2.5:1.5b 모델을 호출한다. 실행 순서도 앱이 정하는 고정된 워크플로다.
reviewer가 대화 읽기"] ReadDraft --> Review["검토: 의견 생성"] Review --> ReadReview["DB 저장 · Coral에 기록
writer가 대화 읽기"] ReadReview --> Revision["수정: 최종 답변 생성·저장·전달"]
각 단계는 기존 Kafka command/result 토픽을 재사용한다. 결과를 PostgreSQL에 저장한 뒤 Coral에 전달하고, 다음 역할의 MCP 연결에서 coral://state를 읽어 후속 작업을 접수한다. 검토 의견은 수정 단계의 실제 모델 입력에 포함된다. Coral 읽기가 실패하면 다음 단계로 넘어가지 않는다.
Kafka는 단계별 작업 전달을, Coral은 역할 사이의 대화 공유를, PostgreSQL은 결과와 복구 기록 보존을 맡는다. 현재 규모의 고정된 세 단계만 구현한다면 DB에 저장한 대화를 직접 읽는 더 단순한 방법도 가능하다. 이번에는 Coral을 통한 역할 간 문맥 전달을 실제로 연결하고, 그 동작을 관찰하는 데 의미를 두었다. 구현은 WorkflowService에 모아 뒀다.
검토자가 오류를 찾을 것이라는 기대부터 확인했다
실험은 로컬 Docker 환경의 Kafka·PostgreSQL·Coral·Ollama를 연결해서 진행했다. 추론은 CPU에서 실행했고, 출력 토큰 상한은 256이었다. Jev는 이번에도 OFF이며, 아래 검토 의견은 모두 Ollama가 생성했다.
처음에는 역할 지시와 검토 대상을 하나의 사용자 메시지에 묶었다. 검토자는 잘못된 초안에도 “수정 불필요”라고 답했고, 수정본에는 입력의 제목과 설명이 섞여 나오기도 했다.
그래서 역할 지시는 시스템 메시지로, 원래 요청과 초안은 사용자 메시지로 나누고 지시문도 짧게 다듬었다. 당시의 시스템 지시문과 Coral 대화도 DB에 보존했다. 메시지 역할과 문구를 함께 바꿨으므로, 이후 차이를 어느 한 변경의 효과라고 단정할 수는 없다.
같은 다섯 사례를 다시 실행한 결과는 아래와 같다. 앞의 네 개는 초안을 직접 제공했고, 마지막 하나는 모델이 초안부터 생성했다. 긴 문장은 표에서 요약했다.
| 요청 | 초안 | 수정본 |
|---|---|---|
| 방수 여부 문의 분류 | 배송 | 상품 |
| 고장 난 제품 리뷰의 감정 분류 | 긍정 | 부정 |
| 오후 2시 → 3시 회의 변경 안내 | 오전 3시로 변경 | 오후 2시에서 오후 3시로 변경 |
| 정상 문장을 그대로 출력 | 회의는 오후 3시에 시작합니다. | 재입력을 요청하는 답변 |
| 구매 취소·환불 문의 분류 | 환불 | 환불 |
오류 초안 세 개는 수정됐다. 하지만 검토 의견은 다섯 사례 모두 “수정 불필요”였다. 검토자가 오류를 찾아준 결과라고 말할 수는 없다. 수정 역할이 원래 요청을 다시 읽고 답을 생성한 효과일 수 있다.
정상 초안을 유지해야 하는 사례에서는 오히려 다음과 같은 답이 나왔다.
다시 입력해주세요. 작성자님의 요구사항이 수정되었는지 확인하지 못했습니다.
검토와 수정을 추가하면 새로운 오류가 생길 기회도 늘어난다. 이 다섯 사례를 일반적인 정답률로 해석할 수는 없지만, 단계를 늘렸다는 이유만으로 품질 개선을 기대해서는 안 된다는 점은 확인했다.
제공 초안은 검토와 수정에 두 번, 새 초안은 작성까지 세 번 모델을 실행했다. 합계 11회였고 이번 재실험에는 재시도가 없었다. 개별 협업의 접수부터 최종 전달까지 약 5.9~14.5초가 걸렸다. 큐 대기와 전달까지 포함한 한 번의 로컬 측정이며, 성능 벤치마크는 아니다. 같은 입력으로 실행할 수 있는 검증 스크립트도 남겼다.
결과를 비교하는 개발 콘솔로 바꾸기
이번 결과를 보려면 ‘완료’ 배지보다 실제 답변이 먼저 눈에 들어와야 했다. 웹을 실행·협업·Kafka 화면으로 나누고, 긴 설명을 줄였다. 실행 목록에서 작업을 고르면 오른쪽에서 답변과 대기·추론·전체 시간을 확인한다. 긴 프롬프트와 실행 식별자는 펼쳤을 때 보이도록 정리했다.
새 실행 → 작성 → 검토 → 수정으로 협업을 시작한다. 기존 초안 사용을 선택하면 이미 받은 답변부터 검토할 수 있다. 협업 화면에서는 초안, 검토 의견, 수정본을 나란히 비교한다. 아래는 방수 문의를 ‘배송’으로 분류한 초안이 최종적으로 ‘상품’으로 바뀐 실제 실행 화면이다.
Kafka 화면도 같은 콘솔 안에 두었다. 토픽·Consumer 그룹·개별 Consumer의 실제 구성을 10초마다 조회해 그래프를 갱신한다. 실선은 실제 파티션 할당, 점선은 앱에 정의된 발행·실패 경로다. 메시지 한 건의 이동을 추적하는 애니메이션은 아니다.
세 이미지는 모두 실행 중인 로컬 앱을 캡처했다. 화면의 완료는 생성·저장·전달을 끝냈다는 뜻이며, 답변이 옳다는 승인을 의미하지 않는다.
검토까지 끝났는데 전달이 끊긴다면
단계가 늘어난 만큼 장애 복구도 확인했다. 모델이 답변을 생성했다면 결과부터 DB에 남긴다. Coral 전달만 실패한 경우에는 저장된 답변을 다시 전달하고, 추론이 실패한 경우에는 해당 단계부터 다시 실행한다.
한 단계의 전달 완료와 다음 작업 접수는 같은 DB 트랜잭션으로 처리한다. 중복 결과 이벤트가 다음 작업을 두 개 만들지 않게 했다. 다만 DB 밖의 모델 호출과 Coral 전달까지 모두 묶는 분산 트랜잭션은 아니므로, 모든 장애 상황에서 외부 호출이 딱 한 번만 일어난다는 보장은 없다.
Coral의 대화는 메모리에 있다. 진행 중 서버가 재시작되면 DB의 단계별 결과를 새 스레드에 복원하고 다음 역할이 읽도록 했다. 실제 협업 도중 Coral을 재시작해 완료까지 이어지는지 확인했다. 제공 초안으로 시작한 이 검증에서는 모델 호출이 총 두 번이었고, 이미 끝난 추론을 복구 때문에 반복하지 않았다. 이 복구는 진행 중인 협업을 위한 것이며, 과거에 완료된 모든 스레드를 자동 복원하는 기능은 아니다.
로컬 자동 테스트는 56개 중 54개 통과, 별도 설정이 필요한 실제 서비스 테스트 2개 건너뜀이다. 실제 서비스 실행과 브라우저의 비교·재시도·모바일 표시도 따로 확인했다. 구현과 한·영 README, 실제 화면 캡처는 커밋 33c1e81까지 push했고, GitHub Actions의 빌드·Docker·전체 흐름·복구 검사도 통과했다.
다음에는 검토 단계를 빼고 비교해 보려 한다
이번에 만든 것은 앞 역할의 결과를 다음 역할이 읽고, 단계별 출력과 실패 지점을 확인할 수 있는 실행 흐름이다. 그 안에서 검토의 효과를 따로 측정할 준비가 됐다.
다음 실험은 같은 초안에 대해 바로 재작성과 검토 후 수정을 비교하는 것이다. 오류 수정뿐 아니라 정상 답변을 망가뜨리는 비율, 추가 호출과 시간도 함께 기록하려 한다. 다른 검토 모델이나 Jev와의 비교는 실제 연동 준비가 된 뒤 이어갈 수 있다. 먼저 지금의 검토 단계가 추가 호출 한 번만큼의 값을 하는지 확인하고 싶다.
작성 기준: 2026년 9월 30일. 실제 로컬 실행 기록과 원격 저장소의 구현·검증 결과를 기준으로 작성했다.



Comments