에이전트 실행은 일반적인 HTTP 요청보다 오래 살 수 있다. 도구를 여러 번 호출하고, 사람의 승인을 기다리고, 서버 프로세스가 바뀐 뒤 다시 이어질 수도 있다. 실행 상태를 메모리에만 보관하면 새로고침, 재배포, 프로세스 장애가 곧 작업 유실로 이어진다.
enterprise-agent-reference는 대화와 실행을 thread ID로 묶고 PostgreSQL checkpoint를 사용한다. 중요한 점은 화면에 보여줄 대화 기록과 에이전트 내부 실행 상태를 같은 것으로 취급하지 않는다는 것이다.
사용자 기록과 실행 상태는 목적이 다르다
ConversationThread와 ConversationMessage는 제품 API를 위한 데이터다. 사용자가 보낸 질문, 사용자에게 공개된 최종 답변, 인용 정보를 안정적으로 조회할 수 있게 한다. UI는 이 테이블만으로 이전 대화를 다시 그릴 수 있다.
LangGraph checkpoint는 실행 엔진을 위한 데이터다. 어떤 메시지와 도구 호출까지 처리됐는지, 어느 interrupt에서 멈췄는지, 재개할 때 어떤 state가 필요한지를 보존한다. 이 구조는 라이브러리의 실행 의미에 가깝고 그대로 외부 API로 노출하기에는 변화 가능성이 크다.
둘을 분리하면 다음 이점이 있다.
- 사용자 API가 에이전트 라이브러리 내부 형식에 묶이지 않는다.
- 공개하면 안 되는 중간 reasoning을 대화 기록에 저장하지 않는다.
- 체크포인트 형식이 바뀌어도 UI 조회 계약을 유지할 수 있다.
- 실행 실패와 사용자 메시지 저장 실패를 별도로 다룰 수 있다.
thread ID가 재개의 기준점이다
새 대화를 시작하면 API가 thread를 만들고 tenant와 사용자를 연결한다. 에이전트 호출에도 같은 thread ID를 configurable state로 전달한다. interrupt 이후 승인 요청이 오면 API는 먼저 해당 thread가 현재 사용자와 tenant 소유인지 확인한 다음 같은 ID로 checkpoint를 불러온다.
config = {"configurable": {"thread_id": thread_id}}
await agent.ainvoke(Command(resume={"decisions": [decision]}), config=config)클라이언트가 임의의 checkpoint 위치나 내부 state를 제출하지 않는 것도 중요하다. 외부 요청은 공개된 approval ID와 decision만 보내고, 서버가 소유한 저장소에서 실제 실행 상태를 찾는다.
durable이라는 말의 범위
PostgreSQL에 저장한다고 모든 장애에 자동으로 안전한 것은 아니다. 스키마 migration, 보존 기간, 개인정보 삭제 정책, 백업과 복구, 동시에 들어온 승인 요청을 고려해야 한다. 레퍼런스 구현은 프로세스 재시작을 넘어 state를 유지하는 기본 경로를 보여주지만 production 운영 정책까지 완성하지는 않는다.
특히 다음 상황을 테스트해야 한다.
- 승인 직전에 API 프로세스가 종료된다.
- 승인 요청은 성공했지만 클라이언트가 응답을 받지 못해 다시 보낸다.
- 이미 거절된 approval을 다시 승인하려 한다.
- 다른 tenant가 thread ID를 추측해 접근한다.
- deployment 사이에 checkpoint 스키마가 달라진다.
재개와 재시도는 다르다
재개는 저장된 실행의 다음 단계로 이동하는 일이다. 재시도는 실패한 네트워크 호출을 다시 수행하는 일이다. 둘을 섞으면 이미 성공한 쓰기를 반복하거나, 사용자가 승인하지 않은 새 인자로 실행할 수 있다.
이 프로젝트는 transport 오류에만 제한된 retry를 적용하고, validation·authorization·not found는 즉시 실패시킨다. 쓰기 재개에는 별도의 idempotency key를 전달한다. checkpoint는 “어디서 이어갈지”를 해결하고, 멱등성은 “같은 부작용을 또 만들지 않을지”를 해결한다.
상태가 있는 에이전트를 설계할 때는 채팅 기록만 데이터베이스에 넣는 것으로 충분하지 않다. 사용자가 보는 역사, 승인 워크플로, 실행 엔진의 state가 각각 누구를 위한 데이터인지 먼저 구분해야 한다.