needhelp
← Back to blog

DeepSeek Harness: 말 그대로 모든 것이 플러그인인 오픈소스 에이전트 프레임워크

by needhelp
DeepSeek
AI Agents
Open Source
Cordis
Plugin Architecture

GitHub에 공개된 완전한 에이전트 스택

DeepSeek는 2026년 8월 13일 DeepSeek Harness(dsh) 개발자 프리뷰를 공개했다. 리포지토리는 deepseek-ai/deepseek-harness에 있다. 당일 말 기준 스타 1.6k개, 커밋 12,293개, 기여자 19명 — 주말 해킹 프로젝트가 아니라는 뜻의 수치다. 코드베이스는 TypeScript 97.1%, CSS 1.6%, Python 0.7%로 구성된다. 공개 당시 패키지 버전은 0.1.0-rc.5.

랜딩 페이지는 deepseek.com/harness, 개발자 문서는 deepseek-harness.github.io/deepseek-harness에 있다.

라이선스는 MIT.

한 줄 요약

README에 적힌 문구: “Agent = Model + Harness.”

모델은 영혼이다. 하네스는 에이전트가 환경을 이해하고 도구를 사용하며 실제 환경에서 계속 작동하게 만든다. DeepSeek의 접근 방식: 모델, 도구, 스킬, 세션, 샌드박스, 스토리지, 루프, 스케줄링, UI에 이르는 모든 기능이 교체 가능한 플러그인이다.

슬로건이 아니다. 아키텍처가 강제하는 규칙이다.

Cordis: 모든 것 아래에 있는 커널

DeepSeek Harness는 벤더링된 플러그인 프레임워크인 Cordis 위에 구축됐다. Cordis의 설계는 시공간 조합 가능성을 위한 프로그래밍 패러다임 논문에 설명돼 있다.

프레임워크 전체는 다섯 가지 아이디어로 귀결된다:

  1. 플러그인은 Service를 구현하는 객체다. injectapply(ctx)를 가진 함수거나 Service 서브클래스일 수 있다.
  2. 컨텍스트는 서비스의 저장소다. 플러그인은 ctx.tools, ctx.llm, ctx.sessions 같은 안정적인 키를 차지한다. 다른 플러그인은 구체적인 구현을 import하는 대신 키로 서비스를 찾는다.
  3. inject로 서비스 의존성을 선언한다. 필요한 서비스를 명시한 플러그인은 해당 서비스가 생길 때까지 기다린다. 수동 부트 순서가 필요 없다.
  4. 통신을 위한 타입화된 이벤트. 네 가지 디스패치 모드: emit(발사 후 망각), waterfall(next()를 쓰는 미들웨어 방식), parallel(모든 리스너가 동시 실행), serial(리스너가 순서대로 실행되고 반환값을 가짐).
  5. 등록은 되돌릴 수 있는 효과다. 프롬프트 섹션, 도구 스키마, 어댑터, 리스너 — 모든 것이 ctx.effect()를 통해 설치돼 리로드와 종료 시 예측 가능하게 해제된다.

Cordis waterfall 모델은 around 미들웨어다. 리스너는 (...args, next)를 받는다. next()를 호출하면 다음 서비스로 위임하고, next() 없이 반환하면 단락 회로된다. 단일 결정 이벤트에서 단락 회로는 의도된 설계 — 정책 리스너가 결정을 완전히 소유할 수 있다.

모든 것이 플러그인이다 (진심이다)

deepseek.com/harness의 플러그인 목록은 전체 기능 범위를 명확히 보여준다:

  • Models — LLM 백엔드 (DeepSeek, OpenAI, Anthropic, Bedrock, Vertex, Azure, Codex, 그리고 모든 OpenAI 호환 엔드포인트)
  • Tools — 모델이 호출할 수 있는 것
  • Skills — 재사용 가능한 프롬프트와 도구 묶음
  • Sessions — 대화 상태 관리
  • Sandboxes — 코드가 실행되는 곳 (PTY bash, danger-full-access 로컬 백엔드, landlock 기반 네이티브 샌드박스)
  • Storage — 세션 영속성
  • Loops — 에이전트 턴 로직
  • Scheduling — 서브에이전트와 태스크 디스패치
  • UI — 기본적으로 포트 3080에서 제공되는 웹 UI

개발자는 설정을 통해 이 중 어느 것이든 선택, 교체, 확장할 수 있다 — DeepSeek Harness 자체 소스 변경 없이. 플러그인 설정 카탈로그는 소스(scripts/gen-config-catalog.ts)에서 자동 생성되고 CI에서 최신 상태로 검증되므로, cordis.yml config: 블록의 모든 필드는 플러그인이 선언한 설정 타입과 정확히 일치한다.

네 가지 런타임 모드

문서에는 네 가지 프리셋이 제공된다. 각각 다른 사용 사례를 겨냥한다.

Standard Mode — 완전한 코딩 에이전트: 파일 편집, 셸, 파일 및 웹 검색, 스킬, 계획, 목표, 서브에이전트, 워크플로우. 기본 웹 UI 경험이다.

Code Mode — Standard의 모든 기능에 더해, Code Mode SDK를 통해 도구가 노출돼 모델이 여러 단계의 작업을 하나의 TypeScript 프로그램으로 합칠 수 있다. 한 번의 모델 생성 프로그램이 여러 차례의 도구 호출 라운드를 대체한다.

Minimal Mode — 단 두 가지 도구: 영속적인 bash 셸과 str_replace_editor. 간소화된 환경에서 모델을 벤치마킹할 때 쓴다. 스킬도, 계획도, 서브에이전트도 없다 — 실제 코드베이스에 대한 순수한 모델 능력이다.

Creator Mode — 커스텀 에이전트 프리셋을 작성하기 위해 만들어졌다. 모든 Standard 모드 기능에 더해 런타임 검사, 메모리 내 Cordis 플러그인 실험, 프리셋 작성 가이드를 포함한다. 다섯 번째, 여섯 번째, 일곱 번째 모드를 만드는 방식이다.

모든 실행은 추적 가능하다

Harness가 대부분의 에이전트 도구와 차별화되는 지점이다. 모델이 보는 모든 것이 추가 전용 세션 로그에 기록된다:

  • 시스템 프롬프트
  • 추론 출력
  • 도구 호출과 그 결과
  • 서브에이전트 스케줄링 결정
  • 모든 컨텍스트 인젝션

웹 UI에는 Trajectory 뷰가 있어 소스별로 필터링한 기록을 검사할 수 있다. 이어하기, 포크, 검색, 재생 — 네 가지 작업 모두 같은 이벤트 스트림에 대해 동작한다. 별도의 추적 데이터베이스도, 내보내기 단계도 없다. 세션 JSONL 자체가 진실의 원천이다.

Python SDK의 경우 세션 디렉토리에 조립된 모델 요청과 도구 호출을 담은 압축되지 않은 JSONL이 저장된다. examples/jsonrpc-agent/minimal.py의 예제가 이 과정을 처음부터 끝까지 보여준다.

모델 설정: DeepSeek + 모든 것

모델 설정 페이지(모델 구성)는 세 가지 계층으로 이뤄진다.

DeepSeek 공식. 설정 → 모델을 열고 DeepSeek API 키를 붙여넣은 뒤 저장한다. 키는 쓰기 전용이다. 저장 후 UI는 평문 대신 마스킹된 설명자를 받는다. 키는 \$DSH_HOME/.credentials.yaml에 저장되고, 설정 페이지는 자격 증명 참조만 유지한다.

카탈로그 제공자. “제공자 추가”를 클릭하고 Anthropic이나 OpenAI를 고른 뒤 키를 붙여넣는다. 네이티브 인증을 쓰는 제공자(AWS 자격 증명 + 리전을 통한 Bedrock, ADC 프로젝트를 통한 Vertex, api-version을 통한 Azure, OAuth를 통한 Codex)는 API 키 필드만으로는 동작하지 않는다 — 각각 고유한 인증 경로가 필요하다.

커스텀 제공자. 회사 게이트웨이, 자체 호스팅 서버, 카탈로그에 없는 모든 제공자를 위한 것이다. 소문자 Provider ID(영구적 — 요청, 저장된 세션, 모델 기본값, 자격 증명 참조가 모두 이를 사용), 기본 URL, API 프로토콜, 자격 증명, 그리고 최소 하나 이상의 모델을 설정한다.

비주얼 모델은 한 단계가 더 필요하다. 커스텀 엔드포인트는 지원하는 양식을 알릴 방법이 없으므로 폼이 비전 지원을 자동 감지할 수 없다. \$DSH_HOME/settings.yaml에서 모델에 input: [text, image]를 추가한다. 모든 커스텀 모델이 이미지를 받는다면 모델별 대신 제공자 수준에서 defaultInput: [text, image]를 한 번만 설정해도 된다. input 필드는 검사가 아니라 단언이다 — 모델이 비전을 지원한다고 주장했지만 엔드포인트가 실제로는 지원하지 않으면, Harness 대신 제공자가 요청을 거절한다.

문제 해결은 문서에 직접 적혀 있다:

  • MISSING_CREDENTIAL → 모델 페이지를 통해 제공자 키를 저장하거나 참조된 환경 변수를 export
  • UNKNOWN_MODEL → 설정된 모델을 고르거나, 커스텀 제공자에 누락된 모델을 추가
  • “사용 가능한 모델 가져오기가 401을 반환” → 키를 확인. 모델 검색은 OpenAI 호환 GET /models 엔드포인트를 호출한다. 서비스가 이를 노출하지 않으면 모델을 수동으로 입력
  • “전송 전 이미지가 거부됨” → 모델이 image 양식을 선언하지 않았다. input: [text, image]를 추가
  • “제공자가 이미지가 포함된 요청을 거절함” → 모델이 엔드포인트에 실제로 없는 비전 기능을 주장했다. 목록에서 image를 제거하고 새 세션을 시작한다(이전 이미지가 세션 로그에 남아 같은 요청을 계속 반복한다).

시작하기: 세 가지 경로

경로 1: npx @deepseek-ai/dsh web

Node.js를 설치하고 명령 하나만 실행한다. 웹 UI가 http://127.0.0.1:3080에서 시작된다. 끝이다. 클론도, 빌드도, pnpm도 필요 없다.

Terminal window
npx @deepseek-ai/dsh web

다음: 설정 → 모델 → DeepSeek API 키를 붙여넣는다. 워크스페이스를 고른다(dsh를 호출한 디렉토리로도 가능). 세션을 시작한다:

이 리포지토리를 요약하고 주요 패키지를 식별해줘.

에이전트는 워크스페이스 파일을 읽고 편집하며, 명령을 실행하고, 작업을 위임하며, 계획을 유지한다. 활성화된 권한 정책에서 승인이 필요한 모든 작업은 웹 UI에서 실행 전 대화 상자를 띄운다.

경로 2: 소스에서 클론하고 빌드

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

경로 3: Python SDK

요구 사항: Python 3.10+, Linux x64 / Linux arm64 / macOS 14+ arm64, DeepSeek 호환 엔드포인트.

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk

자격 증명을 설정한다:

8000/v1
export DEEPSEEK_API_KEY=sk-your-key-here
# 옵션:
# export DSH_MODEL=deepseek-v4-flash
# export DSH_SYSTEM_PROMPT='You are a helpful software engineer assistant.'

격리된 워크스페이스에서 태스크를 실행한다:

Terminal window
python examples/jsonrpc-agent/minimal.py \
--workspace /absolute/path/to/workspace \
--session-root /absolute/path/to/sessions \
--session-id example-001 \
"Inspect the repository and fix the failing tests."

자체 코드에서는 SDK 진입점으로 컨텍스트 매니저인 DeepSeekHarness를 쓴다:

from pathlib import Path
from deepseek_harness import DeepSeekHarness
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
session_root=str(sessions),
cordis=str(config),
) as harness:
result = harness.run(
"Inspect the repository and fix the failing tests.",
session_id="example-001",
)
print(result.final_response)

DeepSeekHarness는 번들된 런타임을 지연 시작하고 with 블록이 종료될 때까지 재사용한다. 같은 세션 ID를 재사용하면 Bash 프로세스(작업 디렉토리, 환경 변수, 셸 함수)를 보존할 수 있다. 독립적인 태스크에는 새 세션 ID를 쓴다.

jsonrpc-agent 미니멀 컴포지션은 의도적으로 간소화돼 있다: 모델이 접하는 도구로 영속적인 bashstr_replace_editor만 있다. Bash 타임아웃은 300초. 에디터 출력 제한은 16,000자. 컨텍스트 압축은 비활성. 파일시스템은 베어 로컬 백엔드를 쓴다 — 에디터 경로는 런타임 프로세스가 볼 수 있는 모든 곳을 지정할 수 있다. 문서에서는 명시적으로 경고한다: “일회용 체크아웃이나 컨테이너 내부에서만 실행하라.” 영속 PTY 백엔드는 POSIX 터미널 기반도 필요하다 — 이 컴포지션은 Windows를 지원하지 않는다.

첫 플러그인 작성하기

튜토리얼: 첫 플러그인. 플러그인은 apply 함수를 export하는 TypeScript 모듈이다:

import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}

cordis.yml 패치에 등록한다:

- insert:
- id: hello
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'

오버레이로 부팅한다:

Terminal window
pnpm dsh web --patch ./scratch-plugin/cordis.yml

자동 정리가 핵심 기능이다. ctx를 통해 등록된 모든 것 — 이벤트 리스너, 도구, 타이머 — 는 플러그인이 언로드될 때 정리된다. 수동 removeListenerclearInterval이 필요 없다. 명시적인 정리(네트워크 연결)가 필요하면 ctx.effect()에서 디스포저를 반환한다.

의존성은 inject로 선언한다:

export const name = 'my-tool-plugin'
export const inject = ['tools']
export function apply(ctx: Context) {
// ctx.tools is ready here
}

Cordis는 플러그인을 로드하기 전에 모든 필요한 서비스가 준비될 때까지 기다린다.

세 가지 플러그인 형태가 있다: 함수(위), apply를 가진 객체, Service를 확장한 클래스. 플러그인 자체가 다른 플러그인이 소비할 서비스를 제공할 때는 클래스 형태를 쓴다.

첫 도구 작성하기

튜토리얼: 도구 만들기. @deepseek-ai/dsh-tools에서 defineTool을 사용한다:

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: {
type: 'string',
required: true,
description: 'The name to greet',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}

defineToolparameters에서 args를 추론하고 검증한다. executeoutput.schema가 선언한 정식 값을 반환한다. output.render는 그 정식 값을 모델이 보는 콘텐츠로 변환한다. 패치를 적용하고 재시작한 뒤 웹 UI에 “greet 도구를 사용해 Ada에게 인사해줘”라고 물어보면, 모델은 greet를 호출하고 Hello, Ada!를 받는다.

튜토리얼의 다음 단계는 플러그인 설정, 도구 작성 참조(중첩 스키마, 정식 값, 백그라운드 작업, 정책 훅, Code Mode, UI 카드), 그리고 기능 계층화(서비스 정의 → 서비스 제공자 → 소비자 패키지 분할)다.

CLI 진입 모드

@deepseek-ai/dsh 명령은 제품 런처다. 네 가지 진입점:

명령 용도
dsh --profile <name> \$DSH_HOME/profiles/<name> 아래의 이름 지정된 프로필로 부팅
dsh --profile headless "job" 하나의 새 영속 세션을 실행하고 최종 답변을 출력한 뒤 종료
dsh web --profile web의 별칭
dsh plugin --profile <name> <pnpm args> pnpm에 전달해 프로필의 플러그인을 관리

호출한 디렉토리가 기본 워크스페이스 루트다. webheadless 프로필은 첫 사용 시 제공되는 템플릿에서 자동 초기화된다. 다른 프로필은 dsh plugin으로 생성해야 한다.

런처 플래그가 먼저 온다. 런처가 인식하지 못하는 첫 토큰이 앱의 인수가 된다. 예: dsh --profile web --port 8080--port 8080을 런처가 아닌 웹 앱에 전달한다.

프로필 디렉토리는 package.json(트리 외 플러그인 의존성 + 정렬된 bundles 목록을 가진 프로필 매니페스트 dsh.profile)과 cordis.patch.yml(사용자 자신의 패치 레이어)을 담는다. 빈 루트에 대한 컴포지션 순서는: dsh.profile.bundles 순서의 각 번들 패치 → 프로필의 cordis.patch.yml → 홈 수준 \$DSH_HOME/cordis.patch.yml--patch 오버레이. --dump-default-config--dump-config를 써서 부팅하지 않고 컴포즈된 트리를 검사할 수 있다.

커뮤니티 플러그인과 생태계

검색 가능성을 높이려면 GitHub에서 플러그인 리포지토리에 dsh-plugin 태그를 붙여라. 공식 사이트가 해당 토픽 페이지로 직접 링크한다. 토론을 위한 DeepSeek Harness Discord 커뮤니티도 있다.

프로젝트는 피드백과 버그 리포트에 GitHub Discussions를 쓴다. 문서는 개발 워크플로우를 위한 CONTRIBUTING.md, 시스템 설계를 위한 architecture.md, 에이전트별 코딩 규약을 위한 AGENTS.md로 링크한다.

개발자 프리뷰 — 깨질 것이다

README에 대문자로 적혀 있다: “호환성을 깨뜨리는 변경이 있을 것이다.”

코어 플러그인과 API는 여전히 진화 중이다. 랜딩 페이지에 직접 적힌 문구: “DeepSeek Harness는 개발자 프리뷰 상태이며, 에이전트 하네스를 구축하는 개발자들에 의해 여전히 테스트되고 있습니다.”

이 위에 구축한다면 커밋 해시를 고정하고, 플러그인을 설정 카탈로그에 대해 얇게 유지하며, 모든 rc 업데이트마다 재테스트할 각오를 해라. 플러그인 자동 정리와 의존성 주입 때문에 재테스트가 모놀리식 프레임워크보다는 덜 고통스럽지만, “개발자 프리뷰”는 말 그대로의 뜻이다.

무엇이 다른가

오늘날 대부분의 에이전트 프레임워크는 턴 루프로 시작하고 확장성을 나중에 덧붙인다. Harness는 이를 뒤집는다: 확장성 자체가 프레임워크이고, 턴 루프는 그저 또 다른 플러그인일 뿐이다. 눈에 보이는 결과는 한 패키지에서 보통 얻을 수 없는 세 가지다:

  • 포크 없이 무엇이든 교체. 내장 도구 시스템이 마음에 안 들면 바꿔라. 다른 LLM 라우팅 레이어가 필요하면 제공자를 교체해라. 모든 것이 Cordis 서비스 키를 통해 해결된다.
  • 기본적으로 추적 가능성. 추가 전용 로그는 관측 기능 애드온이 아니다. 세션이 동작하는 방식 그 자체다. 이어하기, 포크, 검색, 재생이 모두 같은 스트림을 쓴다.
  • 기능 플래그가 아닌 조합 가능한 프리셋. 네 가지 모드(Standard / Code / Minimal / Creator)는 그저 정렬된 플러그인-번들 패치 레이어일 뿐이다. 코드 대신 YAML로 자신만의 프리셋을 위에 얹을 수 있다.

이 접근 방식이 모놀리식 SDK를 이길지는 플러그인 생태계가 교체/재조합 이야기를 현실로 만들 만큼 충분한 서드파티 도구와 제공자를 생산하느냐에 달렸다. 출시 첫 날 스타 1.6k개와 커밋 12k개로 볼 때, 모멘텀은 분명히 있다.

참고

Share this page