DeepSeek Harness가 실제로 하는 일, 멀티턴 호출과 상태 조정
다중 모델 호출과 도구 실행, 세션 상태 조정, 실패 후 재시도 지점, 컨텍스트 압축 시 보존 정보 등 DeepSeek Harness의 핵심 역할을 정리했다.
중국어 원문을 AI로 번역했습니다. 고유명사와 수치는 원문 표기를 우선하며, 중요한 판단에는 아래 출처 원문을 함께 확인하세요.
DeepSeek Harness가 해결하는 핵심 문제는 다중 모델 호출, 도구 실행, 세션 상태 사이의 조정이다. 입력이 언제 요청에 들어가는지, 도구 결과가 어떻게 히스토리에 다시 기록되는지, 요청이 실패하면 어디서부터 재시도하는지, 컨텍스트 압축 후 어떤 정보를 남기는지, 그리고 작업이 중단된 후 어느 상태까지 복구할 수 있는지가 그것이다.
이러한 문제들을 중심으로 프레임워크는 실행 과정을 플러그인 조립, 턴 구동, 세션 로그, 도구 파이프라인, 예외 처리로 나눈다. 그 아키텍처를 분석하는 관건은 각 부분의 상태 경계, 그리고 그 경계가 제공하는 보장과 제한에 있다.
본 글은 커밋 477b4f4205(2026년 9월 25일)에 대응하는 구현으로 범위를 한정한다.
1. 플러그인 조립
플러그인은 DeepSeek Harness의 핵심 역량이며, 플러그인 설정이 Agent의 실행 능력을 결정한다.
DeepSeek Harness는 Cordis 플러그인을 통해 실행 능력을 구성한다. Cordis는 Context, Service, Plugin, Event 등의 추상화를 제공하며, 플러그인은 의존성 주입과 이벤트 시스템으로 협력하고 프레임워크가 생명주기를 관리한다.
LLM 서비스, Session, Agent, AgentLoop, 도구 레지스트리 등 기본 컴포넌트는 bundle이 일괄로 마운트한다. 시작 시 dsh는 profile을 진입점으로 설정을 로드하고, id로 설정 행을 찾아 해당하는 config 전체 구간을 교체한다.
여기서 설정의 의미는 필드 단위 병합이 아니라 구간 전체 교체다. 어떤 컴포넌트의 설정을 덮어쓸 때 원래 설정에서 유지되지 않은 필드도 함께 사라질 수 있다.
따라서 플러그인 조립은 Agent의 실제 동작에 직접 영향을 준다. 모델 서비스가 사용 가능한지, 어떤 도구가 등록되는지, 어떤 이벤트 핸들러가 실행에 참여하는지는 모두 최종적으로 로드된 플러그인과 설정에 달려 있다. 저장소에 특정 능력이 포함되어 있다는 것이 현재 실행 인스턴스에서 그 능력이 이미 활성화되었다는 것과 같은 것은 아니다.
이 설계는 컴포넌트 교체와 능력 조합을 용이하게 하지만, 실행 로직이 여러 플러그인에 분산되도록 만든다. 한 번의 요청 동작을 분석하려면 핵심 루프, 플러그인 등록 관계, 최종적으로 유효한 설정을 동시에 살펴봐야 한다.
2. 핵심 루프
Agent 루프는 Agent의 핵심 로직이며, 주로 턴, 스텝, 요청 재시도를 구분한다.
실제 동작 로직상 agent-loop는 모델과 도구 사이의 루프를 구동한다. 주요 실행 경로는 다음과 같다.
- 모델 요청을 준비하고, 설정을 파싱하여 어댑터를 선택한다.
- 시스템 프롬프트를 투영하고, 이번에 히스토리에 들어가야 할 사용자 입력을 제출한다.
- 세션으로부터 요청을 구성하고, 스트리밍 응답을 수신하여 누적한다.
- assistant 메시지를 Session에 기록한다.
- 모델이 제안한 도구 호출을 실행하고, 결과를 후속 모델 요청에 넘긴다.
- 도구 호출이 없거나 종료 조건을 충족하면 이번 턴의 실행을 종료한다.
이 경로를 이해하려면 세 가지 층위로 나누어야 한다.
- turn: 사용자 입력에 의해 구동되는 턴.
- step: 턴 내부의 실행 경계.
- 요청 시도: 실제로 모델로 보내는 한 번의 호출이며, 실패하면 동일한 step 내에서 재시도할 수 있다.
따라서 step은 단순히 한 번의 네트워크 요청과 같다고 볼 수 없다. 재시도는 여러 번의 요청 시도를 만들어낼 수 있지만, 입력을 중복 소비하거나 사용자 메시지를 중복 제출해서는 안 된다.
Agent는 next-turn과 next-step 두 큐를 유지하며, 각각 다음 턴과 다음 스텝의 입력을 받는다. 큐 변경은 agent/inbox/spliced 이벤트로 Session에 기록되며, 대기 중인 입력은 로그 투영으로 복구할 수 있다.
이 설계는 「시스템이 이미 메시지를 받았다」와 「모델이 이미 메시지를 소비했다」를 구분한다.
실행 과정에서 도착한 추가 요구는 해당 경계에서 후속 요청에 들어가야 한다. 그것은 이미 발송된 모델 요청을 자동으로 바꾸지 못하며, 이미 발생한 도구 부작용을 되돌리지도 못한다. 따라서 입력 큐잉과 실행 취소는 서로 다른 메커니즘에 속한다.
큐 변경을 로그에 기록하면 복구 시 채팅 기록만 되찾고 아직 처리되지 않은 입력을 잃는 일도 피할 수 있다.
3. Session
Session 메커니즘은 실행 기록과 모델 히스토리의 분리를 구현한다.
Session은 추가 전용 이벤트 로그로 설계되었으며, 이벤트는 증가하는 시퀀스 번호로 순서를 정하고 type으로 유형을 구분하며 body로 내용을 저장한다.
로그는 사용자 및 assistant 메시지뿐 아니라 요청 실패, 입력 큐 변경, 도구 실행, 압축 과정 등의 실행 사실도 기록한다.
그러나 로그에 존재하는 이벤트가 반드시 모델 컨텍스트에 들어가는 것은 아니다.
실제로 모델에 보내는 messages는 Session의 surface가 deriveMessages()를 통해 재구성하고, 이를 다시 불변 요청으로 동결한다. surface에 들어간 메시지 이벤트만 히스토리 투영에 참여하며, assistant/attempt 등 실행 기록은 자동으로 모델 메시지가 되지 않는다.
이 분리는 두 가지 역할을 한다.
- 문제 해결 정보 보존: 요청 실패와 복구 과정을 완전히 기록하면서도 전부 컨텍스트 윈도우를 차지하게 하지 않는다.
- 가시적 히스토리 조정 허용: 모델 컨텍스트를 압축하거나 교체할 때도 원본 이벤트를 유지할 수 있다.
Session의 append는 또한 JSON 직렬화 가능성, 이벤트 규칙, surface 교체의 적법성을 검사하여, 영속화할 수 없거나 올바르게 투영할 수 없는 데이터가 로그에 들어가는 것을 줄인다.
로그 복구는 외부 동작 롤백과 같지 않다
추가 전용 로그는 감사와 상태 재구성의 기반을 제공하지만, 임의의 중단 지점에서 모두 무손실 복구할 수 있음을 단독으로 보장하지는 않는다.
예를 들어 도구가 이미 파일을 수정했지만 결과가 아직 Session에 기록되기 전에 프로세스가 종료되면, 외부 환경과 로그 사이에 상태 차이가 생길 수 있다. 로그를 복구하면 이미 커밋된 기록은 되살릴 수 있지만, 「결과가 없다」는 것만으로 도구가 실행되지 않았다고 판단할 수는 없다.
따라서 복구 능력은 다음을 구분해야 한다.
- 커밋된 이벤트와 그 투영의 복구.
- 외부 부작용의 확인과 처리.
후자는 도구의 멱등성, 상태 점검 능력 또는 추가적인 트랜잭션 메커니즘에 의존하며, Session의 추가 전용 설계만으로 도출할 수는 없다.
4. 도구 파이프라인
도구 파이프라인은 모델 의도에서 통제된 실행까지를 구현한다.
도구 레지스트리는 모델에 name, description, parameters 등 화이트리스트 필드만 노출한다. 실행 함수, 타임아웃 설정, 동시성 속성은 런타임에 유지된다.
이는 모델 측의 도구 설명과 런타임의 실행 제어를 분리한다. 모델은 호출을 제안하고, 프레임워크는 호출이 실행 가능한지 그리고 어떻게 실행할지를 판단한다.
한 번의 도구 호출은 다음을 차례로 거친다.
- 모델이 생성한 파라미터를 파싱한다.
- 현재 Agent에게 보이는 도구를 조회한다.
- tools/pre-execute를 통해 실행 전 검사를 수행하고, 호출을 허용하거나 거부한다.
- tools/execute 래핑 체인에 들어간다.
- tools/post-execute를 통해 결과를 처리한다.
- 결과를 Session에 기록하여 후속 모델 요청에 사용하게 한다.
따라서 권한 검사, 실행 래핑, 결과 처리는 각기 독립된 개입 지점을 가지며, 모두 도구 함수 안에 작성할 필요가 없다.
동시 실행과 순차 커밋의 분리
동일한 assistant 메시지가 여러 개의 툴 호출을 제기할 수 있다. 스케줄러는 툴의 isConcurrencySafe 속성을 통해 분류한다:
- 명시적으로 true를 반환하는 호출은 병렬 풀에 들어간다.
- 그 밖의 호출은 독점 배리어(barrier)로 처리된다.
동시성은 명시적으로 선언된 능력이며, 안전하다고 선언되지 않은 툴은 기본적으로 병렬 실행되지 않는다.
툴 본체는 동시 실행할 수 있지만, 준비, 후처리, tool/result 기록 및 추가 context 큐잉은 모델 호출 순서에 따라 커밋된다. 이렇게 하면 툴 완료 시점의 무작위성이 세션 내 결과 순서를 직접 바꾸는 것을 피할 수 있다.
대가는 대기가 발생할 수 있다는 점이다: 뒤에 있는 툴이 먼저 완료되더라도 앞선 호출이 커밋되기를 기다려야 한다. 동시성은 일부 실행 시간을 단축할 수 있지만, 결과가 즉시 히스토리에 들어갈 수 있음을 보장하지는 않는다. 먼저 완료된 결과는 임시 보관이 필요할 수도 있다.
또한 커밋 순서가 안정적이라는 것이 외부 부작용이 확정적이라는 뜻은 아니다. 동시 실행되는 툴이 실제로 안전한지는 여전히 공유 상태에 접근하는지, 실행 의존성이 존재하는지, 그리고 동시성 선언이 정확한지에 달려 있다.
5. 안전 격리
DSH는 구현상 승인과 실행 제한을 각각 적용되게 하여 안전 격리를 실현한다.
Harness의 샌드박스 모드는 다음과 같다:
type SandboxMode = | 'read-only' | 'workspace-write' | 'danger-full-access'
정책은 파일 경로 권한, 명령 실행 제한 및 권한 승격을 포함한다. 툴은 justification과 sandbox_permissions를 통해 권한 승격을 요청할 수 있고, 이후 approval 메커니즘이 승인을 처리한다.
기본 bundle은 기본적으로 workspace-write 파일 샌드박스와 ask 승인 정책을 마운트하며, sandbox-policy는 현재 파일 작업 모드를 실제 백엔드에 해석해 전달한다.
이 구조는 세 가지 서로 다른 계층을 포함한다:
- 툴 가시성: 모델이 어떤 호출을 제기할 수 있는지.
- 실행 승인: 특정 호출이 허가를 받았는지.
- 백엔드 격리: 승인되어 실행된 후 실제로 어떤 리소스에 접근할 수 있는지.
세 가지는 서로를 대체할 수 없다. 승인 통과는 동작이 허가를 받았을 뿐이며, 실행 환경이 무한한 권한을 가진다는 뜻이 아니다. 모델이 어떤 툴을 볼 수 없다는 것도 다른 툴이 동일한 리소스에 접근할 수 없다는 증거가 될 수 없다.
안전 경계는 최종적으로 정책 구성과 실제 백엔드의 공동 제약에 달려 있으며, 샌드박스 모드의 이름만으로 결정되지 않는다.
6. 예외 수렴
예외는 애플리케이션에서 매우 흔한 것이며, Harness가 중점적으로 처리하는 대상이기도 하다.
DSH는 재시도, 압축, 취소를 통해 각각 다른 문제를 처리한다.
6.1 요청 재시도
요청 재시도를 통해 실패 기록을 보존하고 입력의 중복 제출을 피한다.
모델 요청이 실패하면, 시스템은 먼저 assistant/attempt를 기록한 뒤 agent/request-error 워터폴 체인으로 넘겨 처리한다.
llm-retry는 어댑터가 제공하는 retry policy에 따라 오류 유형, 재시도 횟수 및 백오프 방식을 판단한다. 계획된 재시도와 실제 시작은 각각 llm/retry와 llm/retry-started로 기록된다.
재시도는 동일한 step에서 요청을 다시 준비하며, 최초 사용자 메시지를 중복 제출하지 않는다. 이러한 경계는 네트워크 계층의 재시도가 세션 계층의 중복 입력으로 이어지는 것을 피한다.
재시도 계획과 시작을 각각 기록함으로써, 로그는 "재시도를 결정함"과 "다음 시도를 이미 시작함"을 구분할 수 있다.
6.2 컨텍스트 압축
컨텍스트 압축은 보이는 히스토리를 대체하면서 원본 이벤트를 보존하는 것을 구현한다.
자동 압축은 agent/pre-step에서 컨텍스트 압력을 감지하고 다음과 같이 기록한다:
- compaction/start
- compaction/summary
- compaction/end
이후 시스템은 선택된 연속 히스토리 구간을 하나의 checkpoint 사용자 메시지로 대체한다.
여기서 일어나는 것은 surface replace다: 모델이 볼 수 있는 히스토리는 짧아지지만 원본 로그는 여전히 보존된다. 따라서 압축이 줄이는 것은 요청 컨텍스트이며, 영속 저장을 동기적으로 줄이는 것과 같지 않다.
압축은 정보 손실의 가능성도 도입한다. 원본 제약이 여전히 로그에 남아 있더라도, 요약이나 남은 surface에 들어가지 않는다면 이후 모델은 이를 직접 사용할 수 없다. 압축이 유효한지는 컨텍스트 길이 외에도 작업 목표, 제한 조건 및 미완료 사항이 보존되었는지에 달려 있다.
6.3 취소
취소를 통해 기존 결과를 되돌리는 것이 아니라 정지 신호를 전달한다.
AbortSignal은 턴, 요청 및 툴을 관통하여, 취소 신호가 실행 체인을 따라 전달될 수 있게 한다.
그러나 신호 전달이 모든 동작의 즉각 정지를 의미하지는 않는다. 툴이 제때 종료되는지는 취소를 확인하고 응답하는지에 달려 있으며, 이미 완료된 파일 쓰기나 원격 작업도 AbortSignal로 인해 자동으로 롤백되지 않는다.
취소 메커니즘이 제공하는 것은 계속 실행을 정지시키는 제어 경로이며, 외부 부작용의 철회는 여전히 독립적으로 구현해야 한다.
7. 소결
DSH의 Harness는 실제로 일종의 아키텍처적 트레이드오프이며, 그 트레이드오프는 추적 가능성과 실행 비용을 고려한 것이다.
다음의 메커니즘들이 함께 DeepSeek Harness의 주요 엔지니어링 트레이드오프를 형성한다:
메커니즘 | 제공하는 능력 | 비용 또는 경계 Cordis 플러그인 조립 | 컴포넌트 교체와 능력 조합 | 동작이 구성과 여러 핸들러에 분산됨 turn / step과 입력 큐 | 명확한 입력 귀속과 실행 경계 | 큐 상태와 이벤트 유지보수 증가 추가식 Session | 감사와 커밋된 상태 재구성 | 로그가 지속적으로 증가하며, 외부 동작의 정확히 한 번 실행을 보장하지 않음 surface 투영 | 실행 기록과 모델 컨텍스트 분리 | 투영 일관성과 대체 규칙 유지보수 필요 동시 실행, 순차 커밋 | 동시성을 활용하면서 히스토리 순서를 안정화 | 커밋 대기와 결과 임시 보관이 발생할 수 있음 샌드박스와 승인 | 동작 권한의 계층적 제어 | 실제 보장은 구성과 실행 백엔드에 의존 자동 압축 | 컨텍스트 윈도우 압력 감소 | 요약 비용이 증가하고, 작업 정보가 유실될 수 있음 AbortSignal | 취소 의도의 통일된 전달 | 툴의 협조에 의존하며, 부작용을 자동으로 롤백할 수 없음
DeepSeek Harness의 핵심 가치는 Agent 실행에서의 핵심 상태를 명시화하는 것이다: 입력에는 큐가 있고, 실행에는 경계가 있으며, 요청에는 보이는 히스토리가 있고, 툴에는 스케줄링과 승인이 있으며, 실패에는 독립적인 처리 경로가 있다.
이러한 메커니즘은 다중 턴 작업을 추적, 검사 및 복구할 수 있게 하는 동시에 프레임워크의 능력 경계도 명확히 한다: 로그 복구는 부작용 확인을 대체할 수 없고, 순차 커밋은 동시성 안전을 대체할 수 없으며, 권한 승인은 환경 격리를 대체할 수 없고, 컨텍스트 압축도 정보의 무손실을 보장할 수 없다.
이상이다.