메인으로
지유 · 최고기술책임자(CTO) · 오늘의 기술 칼럼

기록은 고치고 내용은 고치지 않는다

에이전트 프레임워크가 형식이 깨진 도구 호출을 다루는 방식을 따라, 대화 기록을 수리하는 일과 모델의 틀린 출력을 수리하는 일을 왜 갈라야 하는지 다룹니다.

초록 언어 모델이 도구를 부를 때 인자를 깨진 JSON 으로 내는 일은 드물지 않습니다. 문제는 그 한 번의 실수가 대화 기록에 남아, 다음 차례에 기록을 다시 보내는 순간 제공자 API 가 대화 전체를 거절한다는 데 있습니다. 9월 하순 LangChain 저장소에 들어온 수정 넷은 이 문제를 같은 원칙으로 다룹니다. 기록의 짝은 맞춰 대화가 이어지게 하되, 모델을 다시 부르지 않고, 깨진 인자를 고쳐서 실행하지 않으며, 끊긴 스트림을 이어 붙이지 않습니다. 다만 깨진 원문을 얼마나 보존하는지는 제공자 어댑터마다 달랐습니다. 이 칼럼은 이 선을 법률 AI 로 옮깁니다. 검증에 실패한 인용을 비슷한 인용으로 바꿔 끼우는 것은 기록의 수리가 아니라 내용의 수리이고, 그것은 실패를 지우는 일입니다.

에이전트가 도구를 부르다 인자를 잘못 만들었다고 해 봅시다. 프레임워크는 그 호출을 '형식이 깨진 도구 호출'로 따로 적어 두지만, 짝이 되는 도구 결과는 만들어지지 않습니다. 다음 차례에 대화 기록을 통째로 다시 보내면, 호출과 결과가 짝을 이루어야 한다는 규칙을 지키는 제공자는 그 기록을 거절합니다. 한 번의 실수가 대화 전체를 멈추게 하는 것입니다. 가장 손쉬운 해결은 깨진 인자를 그럴듯하게 고쳐 넣고 계속 가는 것입니다. 그러나 이번 수정들은 그 길을 택하지 않았습니다. 기록이 다시 보낼 수 있는 모양이 되도록 짝만 맞추고, 모델이 실제로 무엇을 틀렸는지는 고치지 않은 채 남겼습니다. 법률 AI 가 검증에 실패한 인용을 다룰 때도 같은 갈림길에 섭니다. 실패를 표시해 남길 것인가, 비슷한 것으로 메울 것인가.

핵심 기술 개념

형식이 깨진 도구 호출(Invalid Tool Call)

모델이 도구를 부르면서 낸 인자가 JSON 으로 해석되지 않거나 객체가 아닌 경우, 프레임워크가 정상 호출과 따로 분류해 기록해 두는 호출입니다. LangChain 은 이를 AIMessage 의 별도 필드에 담으며, 이 호출에는 대개 짝이 되는 도구 결과가 없습니다.

기록 재생(History Replay)

대화형 API 는 상태를 서버에 두지 않는 경우가 많아, 매 차례 지난 대화 전체를 다시 보냅니다. 이때 과거의 결함도 함께 재전송되므로, 한 차례의 형식 오류가 이후 모든 차례의 요청을 거절당하게 만들 수 있습니다.

진단용 보존(Diagnostic Preservation)

잘못된 출력을 버리거나 고쳐 쓰지 않고, 식별 가능한 표지로 감싸 원문 그대로 남기는 처리입니다. 기록은 유효한 모양이 되지만, 무엇이 틀렸는지는 나중에 사람과 모델이 그대로 읽을 수 있습니다.

기술 심층 분석

1

짝을 맞추되 모델은 다시 부르지 않는다

LangChain 의 create_agent 수정(PR #40530)은 문제를 이렇게 적었습니다. 에이전트가 형식이 깨진 도구 호출을 기록에 남기면서, 제공자가 기록을 재생할 때 요구하는 짝, 곧 대응하는 도구 결과는 남기지 않을 수 있다는 것입니다. 해법은 식별 가능한 깨진 호출마다 오류를 담은 도구 결과 메시지를 하나씩 붙이는 것이었고, 과거의 호출까지 포함했습니다. 이미 결과가 있는 호출은 호출 식별자로 대조해 그대로 둡니다. 그리고 설명문은 이 모든 일이 모델을 다시 시도하지 않고 기록을 정상화한다고 못 박았습니다. 릴리스 노트는 이 변경의 목적을 대화를 안전하게 재개하고 재생할 수 있게 하는 것이라고 요약합니다. 여기서 고친 것은 대화의 문법이지 모델의 답이 아닙니다. 오류 결과 메시지는 '이 호출은 실패했다'는 사실을 기록에 적는 것이고, 실패한 호출이 성공했더라면 무엇을 돌려받았을지 지어내지 않습니다. 다음 차례의 모델은 자기가 무엇을 틀렸는지 보고 다시 시도할 수 있습니다.

2

깨진 인자는 감싸서 남기고 실행하지 않는다

Fireworks 어댑터의 수정(PR #40818)은 같은 문제를 전송 직전에 다룹니다. Fireworks 는 깨진 인자나 객체가 아닌 인자를 담은 대화 기록을 거절하고, 그래서 에이전트가 다음 차례에 회복할 기회를 잃습니다. 수정은 그런 인자를 버리지 않고 __invalid_tool_call_arguments 라는 키 아래 JSON 객체로 감싸, 원래 내용과 호출 식별자, 도구 결과와의 짝을 보존합니다. 설명문은 두 가지를 하지 않는다고 분명히 적었습니다. 메시지를 변형하지 않고, 고친 인자를 실행하지 않습니다. 정상적인 객체 문자열은 손대지 않습니다. 이 구분이 중요한 이유는 깨진 인자를 '고쳐서' 도구를 실제로 돌리는 순간, 모델이 의도하지 않았을 수도 있는 동작이 사람이 검토하지 않은 채 일어나기 때문입니다. 감싸 둔 원문은 기록을 유효하게 만들 만큼만 형식을 갖추고, 내용은 모델이 낸 그대로 남습니다.

3

같은 저장소, 다른 보존의 정도

같은 주에 들어온 Anthropic 어댑터의 수정(PR #40864)은 이슈 하나를 해결하면서 약간 다른 선택을 했습니다. 형식이 깨진 호출을 Anthropic 의 도구 사용 블록으로 직렬화해, 재생되는 도구 결과 블록이 짝을 찾게 합니다. 이때 인자가 유효한 딕셔너리면 그대로 쓰고, 깨진 인자는 빈 객체로 바꿉니다. 같은 식별자가 이미 있으면 중복 호출을 만들지 않고, 식별자나 이름이 없는 호출은 건너뜁니다. 설명문은 에이전트 쪽 수리는 그대로 두고, 깨진 호출이 대화를 망가뜨리지 않게 한다고 적었습니다. 모델을 다시 부르지 않고 인자를 지어내지 않는다는 선은 같습니다. 그러나 Fireworks 쪽은 깨진 원문을 감싸서 남기고, Anthropic 쪽은 빈 객체로 대신합니다. 같은 저장소 안에서도 '얼마나 남기는가'는 어댑터마다 다를 수 있다는 뜻입니다. 여러 모델을 갈아 끼우는 시스템이라면, 실패의 흔적이 어느 경로에서 얼마나 살아남는지를 경로마다 따로 확인해야 합니다.

4

끊긴 스트림은 이어 붙이지 않는다

Fireworks 어댑터의 또 다른 수정(PR #40874)은 실패의 분류를 다룹니다. 스트림이 첫 조각을 보낸 뒤에 읽기 타임아웃이 나면, 그 오류가 LangChain 의 모델 오류 분류를 우회해 버렸습니다. 수정은 이를 재시도 가능한 타임아웃 오류로 분류하되, 원래의 원인과 요청 맥락을 보존합니다. 그러면서도 부분 스트림은 다시 재생하지 않는다고 적었습니다. 그렇게 하면 텍스트가 중복되거나 도구 인자가 손상될 수 있기 때문입니다. 설명문은 기존 동작도 정확히 밝혔습니다. 재시도 래퍼는 스트리밍을 재시도하지 않고, 대체 모델 전환은 첫 조각이 나오기 전에만 일어납니다. 출력이 시작된 뒤의 복구는 애플리케이션이 명시적으로 처리해야 한다는 것입니다. 분류는 넓히되 복구는 넓히지 않은 셈입니다. 무엇이 실패했는지는 더 정확히 알리고, 절반쯤 나온 답을 다른 답의 뒷부분과 이어 붙이는 일은 하지 않습니다.

기술적 트레이드오프

긴장 관계 실패를 표시해 남기면 대화는 이어지지만, 사용자는 오류가 적힌 기록과 매끄럽지 않은 결과를 보게 된다. 깨진 인자를 그럴듯하게 고쳐 넣거나 끊긴 답을 이어 붙이면 화면은 매끄러워지지만, 모델이 실제로 틀린 자리가 기록에서 사라지고, 고친 쪽이 틀렸을 때 그 책임이 어디서 생겼는지 추적할 수 없게 된다.

실무적 해소 수리의 대상을 기록의 형식으로 한정합니다. 대화가 다시 보낼 수 있는 모양이 되도록 짝을 맞추고 표지를 붙이는 일은 적극적으로 하되, 모델이 낸 내용을 대신 만들어 넣는 일은 하지 않습니다. 실패를 되돌리는 방법은 그 부분만 땜질하는 것이 아니라 처음부터 다시 생성하는 것이고, 다시 생성한 결과도 같은 검증을 다시 통과해야 합니다. 이렇게 하면 화면의 매끄러움은 일부 잃지만, 무엇이 틀렸고 무엇이 확인됐는지가 기록에서 끝까지 구분됩니다. 사용자에게는 실패를 숨긴 매끄러운 답보다, 실패를 밝힌 짧은 답이 더 쓸모가 있습니다.

이 주장이 틀리는 조건

반증 조건 표시해 남기는 쪽이 낫다는 이 글의 판단은 운영 기록으로 뒤집힐 수 있습니다. 실패 표지를 남긴 대화에서 다음 차례의 모델이 그 표지를 읽고도 같은 오류를 되풀이하는 비율이, 깨진 인자를 자동으로 고쳐 넣은 대화에서 고친 인자가 틀렸던 비율보다 꾸준히 높게 나온다면, 원문 보존은 회복을 돕지 못한 것입니다. 또 검증 실패를 실패로 밝힌 답변과 대체 인용으로 메운 답변을 나란히 두었을 때 사람이 사후에 찾아내는 오류 수가 차이 나지 않는다면, 기록과 내용을 가르는 비용은 얻는 것 없이 화면만 거칠게 만든 셈이고 이 글의 중심 주장은 설 자리를 잃습니다.

법마디 OS에 적용한다면

법마디 OS 는 답변과 데일리 콘텐츠의 법령·판례 인용을 국가법령정보센터 원문과 대조하고, 대조에 실패한 인용을 비슷한 조문이나 판례로 바꿔 끼우지 않습니다. 확인되지 않은 인용은 막히며, 이것은 오늘 살펴본 원칙 가운데 '내용은 고치지 않는다'에 해당합니다. 두 번째 원칙인 실패의 정확한 분류도 이미 쓰고 있습니다. 원문 서버에 닿지 못해 판정하지 못한 인용은 차단이나 통과로 합치지 않고 '관측불가'로 따로 세며, 콘텐츠 생성은 이를 차단이 아닌 발행 보류로 다룹니다. 오늘 새벽 데일리 배치에서도 원문 서버 접속이 시간 초과로 끊겨 몇몇 조문이 관측불가로 기록됐고, 로그는 그것을 통과가 아니라 못 잰 것이라고 적었습니다. 세 번째 원칙인 '복구는 통째로'도 콘텐츠 생성에 적용돼 있습니다. 품질 게이트에 걸린 초안은 부분만 손봐 내보내지 않고 그날 발행을 멈추며, 되먹여 다시 생성하는 것은 기존 글과의 중복이라는 한 가지 사유에 한정합니다. 남은 과제는 기록 쪽입니다. 막힌 인용의 원문과 차단 사유를 사람이 다시 읽을 수 있는 형태로 남기는 범위를 넓혀, 차단이 옳았는지도 나중에 검토할 수 있게 하려 합니다.

기술적 함의

"대화가 멈추지 않게 하는 일과 틀린 것을 맞는 것처럼 보이게 하는 일은 겉으로는 비슷해 보입니다. 이번 수정들이 그은 선은 그 둘 사이에 있습니다. 기록은 다시 보낼 수 있게 고치고, 모델이 틀린 자리는 틀린 채로 표시해 둡니다. 법률 AI 가 확인하지 못한 인용 앞에서 할 일도 같습니다. 메우지 말고, 밝혀 두는 것입니다."

참고 자료

칼럼니스트

지유

지유

최고기술책임자 (CTO · Chief Technology Officer)

실리콘밸리 유니콘 창업 멤버급 / AI 무결성 검증 분야 세계적 석학급

법마디 OS 무료로 경험하기
본 칼럼은 법마디 OS 기술팀의 관점이며, 특정 제품·기술에 대한 보증이나 법률 자문이 아닙니다.