프로세스가 정상 종료됐다고 해서, 그 안에서 나온 내용까지 정상이라는 뜻은 아니었습니다.
Hermes 실행 계층에서 이 문제가 생길 수 있었습니다. 프로세스는 종료 코드 0으로 끝났는데, stdout에는 모델이 생성한 결과가 아니라 provider 오류 문구가 들어오는 경우였습니다. 겉으로 보면 실행은 성공한 것처럼 보입니다. 그런데 그 출력을 그대로 모델 응답으로 넘기면, 다음 단계에서 전혀 다른 문제가 시작됩니다.
원래 실행 흐름은 프로세스의 종료 상태와 stdout을 바탕으로 결과를 해석하는 구조였습니다. 일반적인 경우라면 stdout에 JSON 형태의 생성 결과가 들어오고, 그것을 파싱한 뒤 필요한 후속 처리를 진행하면 됩니다. 문제는 provider 오류도 같은 stdout으로 들어올 수 있다는 점이었습니다.
예를 들어 API call failed after 로 시작하는 문자열이 들어왔는데도 이것을 일반 응답으로 취급하면, 실행 계층은 일단 모델 결과라고 보고 JSON 파싱을 시도하게 됩니다. 파싱이 실패하면 후보를 고치거나 다시 해석하는 repair 단계로 넘어갈 수도 있습니다. 실제로는 생성 결과가 한 번도 나오지 않았는데, 결과 형식이 잘못된 것처럼 처리되는 셈입니다.

이 둘은 비슷해 보이지만 구분해야 합니다.
하나는 모델이 무언가를 생성했는데 형식이 기대와 다른 경우입니다. 이때는 후보 repair 같은 후속 처리가 의미가 있을 수 있습니다. 다른 하나는 provider 단계에서 호출 자체가 실패했다는 메시지입니다. 이 경우에는 고칠 후보가 없습니다. 모델 응답이 아니기 때문입니다.

그래서 hermes_client.py의 실행 결과 판별 단계에서 특정 prefix를 먼저 확인하도록 수정했습니다. stdout이 API call failed after 로 시작하면 일반 모델 응답으로 넘기지 않고 provider failure로 변환합니다. 그 뒤에는 JSON 파싱이나 후보 repair를 정상 응답처럼 이어가지 않도록 흐름을 끊습니다.
작은 문자열 판별처럼 보이지만, 경계가 분명해졌습니다.
실행이 끝났는가와 생성이 성공했는가는 서로 다른 상태입니다. 종료 코드만 보고 성공 여부를 판단하면 provider 오류를 모델 출력으로 오인할 수 있고, 그 뒤에 붙은 복구 로직까지 잘못 작동할 수 있습니다. 반대로 오류의 종류를 실행 계층에서 먼저 나누면, 생성 결과에만 적용해야 하는 처리를 실패 응답에 적용하지 않게 됩니다.
관련 테스트도 이 구분을 확인하는 방향으로 추가했습니다. provider failure 출력이 모델 응답으로 취급되지 않는지, 그리고 그 상황에서 후보 repair가 건너뛰어지는지를 검증합니다. 모든 Hermes 오류를 해결한 것은 아닙니다. 이번에 분리한 범위는 특정 provider failure prefix를 가진 stdout뿐입니다.
결국 이번 수정은 더 많은 복구를 붙인 작업이라기보다, 복구하면 안 되는 응답을 먼저 골라내는 작업에 가까웠습니다. 오류를 억지로 결과처럼 다루지 않는 것. 여기서부터 실행 상태와 생성 결과를 구분하는 경계가 분명해졌습니다.
다음에는 출력의 의미를 구분하는 것과는 별개로, Hermes가 실행되는 장소의 문제를 보게 됩니다. 로그와 상태 파일을 기록할 수 없는 환경에서 에이전트를 어떻게 실행할지, 그 경계가 이어집니다.
