본문으로 건너뛰기

Cordis 브리지

Cordis 브리지는 DeepSeek Harness(DSH) 엔진을 Libre WebUI 백엔드에 내장합니다. DSH는 Libre WebUI가 호스팅하는 Cordis 런타임의 플러그인 트리로 실행되므로, 직접 가져오는 모듈이 아닌 Cordis 서비스로 기능을 제공합니다.

브리지는 기본적으로 꺼져 있습니다. 운영자가 활성화하기 전에는 이 문서의 기능이 실행되지 않습니다. Cordis 설정을 참조하세요.

직접 통합하지 않고 브리지를 사용하는 이유

Libre WebUI 서비스에서 DSH 패키지를 직접 가져오면 코드는 짧아지지만 엔진이 컴파일 시점의 의존성이 됩니다. 모델 어댑터나 에이전트 루프를 교체하거나 엔진을 제거할 때마다 Libre WebUI를 수정하고 다시 배포해야 합니다.

브리지는 이 관계를 뒤집습니다. Libre WebUI는 하나의 추상 인터페이스에 의존하고, Cordis 구성 문서가 이를 제공할 구현을 선택합니다.

  • 다시 빌드하지 않고 대상 변경. 구성은 YAML 파일이므로 다른 제공자로 전환할 때는 설정만 변경하면 됩니다.
  • 기능 구성. 각 기능은 Loader의 한 행입니다. 운영자가 관리하는 구성을 변경하면 다음 호스트 시작 때 적용됩니다.
  • 완전한 제거. 엔진이 설치한 모든 서비스, 리스너, 효과는 루트 파이버에 속합니다. 해당 파이버를 해제하면 모두 되돌려지므로 Libre WebUI를 다시 시작하지 않고 엔진을 중지할 수 있습니다.

계층

DSH의 구체적인 의존성은 backend/src/cordis/dsh/ 안에만 둡니다. 경로와 애플리케이션 서비스는 브리지 인터페이스를 사용합니다. Work 드라이버는 별도의 메모리 내 구성을 사용하며 호스트 파일 시스템 플러그인을 마운트하지 않습니다.

인터페이스

인터페이스는 backend/src/cordis/contracts.ts에 있습니다. 엔진 용어에 의존하지 않고 Libre WebUI API에 필요한 데이터만 표현하도록 범위를 좁혔습니다.

인터페이스용도
DshEngine.status()엔진 서비스별 수명 주기 상태(pending / ready / failed)
DshEngine.modelConfiguration()실행 중인 구성의 모델과 제공자 기본값
DshEngine.listSessions()최신순 세션 요약
DshEngine.getSession(id)세션과 투영된 메시지 조회
DshEngine.createSession(opts)세션 ID와 작업 디렉터리 예약
DshEngine.updateSessionSettings(id, settings)유휴 상태에서 실제 모델 선택과 네이티브 파일 시스템 권한 모드 저장
DshEngine.decideApproval(id, approvalId, decision)해당 세션의 보류 중인 네이티브 도구 승인 한 건 처리
DshEngine.deleteSession(id)세션 종료 및 에이전트 해제
DshEngine.listAgents()실행 중인 에이전트를 루트 또는 자식으로 구분하여 조회
DshEngine.listTools()엔진이 모델용으로 등록한 도구
DshEngine.sendMessage(id, txt)턴을 시작하고 스트림 핸들 반환
DshEngine.cancel(id)세션에서 진행 중인 턴 취소

인터페이스는 Cordis 서비스 libreDshEngine으로 제공됩니다. 사용자는 ctx.get('libreDshEngine')으로 읽으며 브리지 모듈을 직접 가져오지 않습니다.

EngineStreamChunktext, reasoning, tool-call, tool-result, approval-request, approval-decision, error, done을 전달합니다. 실시간 프레임은 소유 에이전트와 세션별로 라우팅하며, 해당하는 영구 저장 어시스턴트 메시지를 다시 보내지 않습니다. sendMessage가 반환하는 핸들의 subscribe는 이미 보낸 내용을 재생하므로, 엔진이 턴을 시작한 뒤 HTTP 처리기가 리스너를 연결하기 전에 첫 토큰이 생성되어도 잃지 않습니다.

순서: 채팅 한 턴

턴은 요청 이후 서버에서 클라이언트로 흐르는 단일 시퀀스이므로 NDJSON을 사용합니다. POST 안에서 처리하면 두 번째 핸드셰이크, 티켓, 재연결 프로토콜이 필요 없으며 전체 턴이 하나의 인증된 요청에 속합니다.

DONE과 PENDING

Cordis는 플러그인이 선언한 서비스가 준비되면 플러그인을 활성화합니다. 따라서 아직 실행되지 않는 행 상태가 존재합니다. 아래 두 개념을 혼동하면 엔진이 아무 응답도 하지 않는 원인이 됩니다.

Loader 항목 상태. Loader는 각 행을 PENDING → LOADING → ACTIVE 또는 FAILED로 추적합니다. 선언된 서비스가 부족한 행은 실패하지 않고 무기한 대기합니다. 불완전한 구성에서 엔진이 시작되지만 아무 기능도 제공하지 않는 이유입니다.

서비스 가용성. 호스트는 예상하는 각 서비스를 다음 상태로 보고합니다.

상태의미원인
pending컨텍스트에 등록되지 않음제공하는 행이 아직 활성화되지 않았거나 비활성화됨
ready등록되어 사용 가능제공하는 행이 활성화됨
failed선언되었으나 사용 불가detail 문자열로 원인 보고

host.status()는 예상하는 모든 서비스의 가용성과 누락된 필수 서비스 이름을 나열합니다. GET /api/cordis/health도 같은 정보를 제공합니다. 필수 서비스가 없는 구성은 빈 목록을 응답하는 엔진을 제공하지 않고 시작 시 예외를 발생시킵니다.

다음 두 의존성 연결을 특히 주의하세요.

  • dsh-toolssystemPrompt 없이 시작할 수 없습니다.
  • dsh-agent-loopagents, sessions, llm, tools, systemPrompt, sessionProjections가 모두 존재해야 시작할 수 있습니다.

하나라도 없으면 세션 저장소는 작동해도 엔진은 메시지에 응답하지 않습니다.

제공자 설정

기본 제공 libre-webui-llm-adapter 행은 Libre WebUI에 설정된 모델 제공자를 지원합니다. 엔진 페이지의 모델 선택기는 해당 행을 교체하지 않고 세션에 사용할 제공자 모델을 선택합니다.

구성의 플러그인 행을 변경하면 다음 호스트 시작 때 적용됩니다. 백엔드를 다시 시작하거나 관리자 스위치가 잠겨 있지 않으면 Cordis를 껐다 켜세요. 저장된 세션은 설정한 저장소에 남으며 현재 구성으로 재개됩니다.

신뢰할 수 있는 통합 코드는 Cordis Loader 수명 주기 API를 직접 사용할 수 있습니다. 브리지는 어댑터 교체 엔드포인트를 제공하지 않으며, 교체에 실패해도 이전 어댑터를 자동 복원하지 않습니다.

롤백

호스트의 루트 파이버를 해제하면 엔진이 설치한 모든 것이 제거됩니다. 다음과 같은 단일 소유 관계가 이를 보장합니다.

  • 서비스는 플러그인이 등록하므로 파이버와 함께 철회됩니다.
  • session/event 구독은 브리지 자신의 생성자 안에서 등록하며 브리지 행의 파이버에 속합니다.
  • 브리지는 에이전트 핸들을 추적하고 정리 효과에서 해제합니다.
  • 호스트는 모든 행을 소유하는 루트 컨텍스트를 해제합니다.

stopCordisHost()는 멱등이며 백엔드 종료 절차에 연결됩니다. 엔진의 타이머와 파일 핸들은 프로세스 종료에 맡기지 않고 명시적으로 해제됩니다.

세션 식별과 영구 저장

엔진 페이지는 생성 시 불투명한 세션 ID를 예약합니다. 영구 저장이 켜져 있으면 헤더를 즉시 저장하므로 빈 세션도 재시작 후 유지됩니다. 브리지는 저장된 세션과 실행 중인 세션을 모두 나열하고, DSH의 검증된 영구 저장 API로 로그를 읽으며, 후속 대화에서 같은 ID로 에이전트를 재개합니다. 새 사용자 메시지는 DSH의 식별자 포함 메시지 생성자를 사용합니다.

세션 삭제 시 먼저 에이전트를 취소하고 해제한 뒤 저장 파일을 제거합니다. 로컬 JSONL 삭제 어댑터는 저장소와 세션 경로를 검증하며 심볼릭 링크를 거부합니다. 삭제를 지원하지 않는 사용자 지정 영구 저장 백엔드는 데이터가 제거되었다고 주장하지 않고 오류를 반환합니다.

취소는 네이티브 에이전트, 모델 요청, 도구 작업에 전달됩니다. 클라이언트 연결이 끊기면 해당 턴이 취소되지만 완료된 메시지는 계속 읽을 수 있습니다. 스트림 재생 버퍼에는 한도가 있으며 읽는 쪽이 연결하기 전에 빠르게 도착한 응답을 보존합니다.

호스트 엔진은 단일 복제본 solo 기능입니다. 팀 배포는 이 로컬 JSONL 런타임을 마운트할 수 없습니다. 샌드박스 Work는 기존 SQL 기반 작업, 실행, 메시지, 승인, 이벤트 저장소를 사용합니다.

HTTP 인터페이스

메서드경로용도
GET/api/cordis/health브리지 상태, 인증 불필요
GET/api/cordis/sessions세션 목록
POST/api/cordis/sessions세션 생성
GET/api/cordis/sessions/:id세션과 메시지 조회
DELETE/api/cordis/sessions/:id세션 종료
POST/api/cordis/sessions/:id/messages메시지 전송 및 NDJSON 스트리밍
POST/api/cordis/sessions/:id/cancel진행 중인 턴 취소
GET/api/cordis/agents실행 중인 에이전트 목록
GET/api/cordis/tools등록된 도구 목록

/health를 제외한 모든 경로에는 인증된 관리자 세션이 필요합니다. 브리지가 요청을 처리할 수 없으면 503을 반환하며 codeCORDIS_DISABLED, CORDIS_STARTING, CORDIS_UNAVAILABLE 중 하나입니다.

세션 목록, 등록된 엔진 도구, 스트리밍 채팅 기록을 보여주는 Libre WebUI Cordis 엔진 페이지.

페이지 구현은 frontend/src/pages/CordisPage.tsx이며 사이드바에서 /cordis로 이동합니다. 세션과 등록 도구를 나열하고 세션을 생성하며 턴을 기록에 스트리밍합니다. 브리지가 꺼져 있거나 시작할 수 없으면 빈 목록 대신 이유를 표시하여, 세션이 없는 상태와 엔진이 없는 상태를 구분합니다.

브라우저 클라이언트는 frontend/src/utils/api/cordisApi.ts입니다. 이 인터페이스만 사용하며 백엔드 타입이나 @deepseek-ai/* 패키지를 가져오지 않으므로 프런트엔드를 변경하지 않고 엔진을 교체할 수 있습니다. sendMessage(sessionId, text, { onChunk })로 턴을 수신하고 줄바꿈으로 구분된 JSON을 직접 파싱하여 네트워크 읽기 경계에서 나뉜 청크도 처리합니다.

엔진 채팅 제어

엔진 페이지는 Markdown, 표, 구문 강조 코드 블록을 렌더링하고 응답과 코드를 복사하는 기능을 제공합니다. 시스템 프롬프트와 주입된 런타임 컨텍스트는 접힌 세션 컨텍스트 영역에 모으며, 사용자가 작성한 메시지로 표시하지 않습니다. 공개된 추론과 도구 활동은 별도로 펼쳐 볼 수 있고, 다시 로드해도 도구 결과가 올바른 작업과 연결됩니다.

작성기에서 실제 제공자 모델을 선택하세요. 선택기는 로그인한 관리자가 사용할 수 있는 로컬 모델과 플러그인 모델을 제공자 정보와 함께 표시합니다. Chat의 페르소나나 에이전트 선택은 모델 ID가 아니며 그 지침을 엔진 대화에 주입하지 않습니다. 과거 실패한 페르소나 모델 헤더는 기본 모델 힌트로 사용하지 않으며 저장된 로그도 변경하지 않습니다.

각 세션에는 읽기 전용 또는 작업 공간 쓰기 설정이 있으며, DSH 파일 시스템 정책과 브리지의 정규화된 작업 공간 경계가 이를 강제합니다. 작성기는 작업 공간 범위를 표시합니다. 설정은 네이티브 세션 이벤트로 저장되어 재시작 후에도 유지되며 턴 실행 중에는 변경을 거부합니다.

네이티브 권한 승격 요청은 해당 작업에 연결된 한 번만 허용 / 거부 카드로 표시됩니다. 승인은 그 요청에만 적용되고 기존 권한 모드는 유지됩니다. 오래되거나 취소된 요청은 승인할 수 없으며, UI가 없는 Chat 호출은 표시할 수 없는 질문을 거부합니다. 브리지는 무제한 호스트 접근을 제공하지 않습니다.

추가 관리자 엔드포인트는 다음과 같습니다.

메서드경로용도
GET/api/cordis/models사용 가능한 제공자 모델과 현재 실제 모델 기본값
PATCH/api/cordis/sessions/:id/settings세션 모델 및/또는 권한 모드 설정
POST/api/cordis/sessions/:id/approvals/:approvalId대기 중인 요청 한 건을 allowed-once 또는 rejected로 처리

Chat에서 엔진 사용

접근 및 정책 → 에이전트 CLI 모델Cordis 엔진을 모두 활성화하면 관리자는 Chat에서 DeepSeek Harness를 선택할 수 있습니다. 각 요청은 해당 Chat 요청이 제공한 대화 기록만 포함하는 새로운 임시 엔진 세션을 받습니다. 일반 Chat 데이터베이스가 계속 권위 있는 원본이므로 서로 다른 대화, 분기, 재시도가 보이지 않는 엔진 기록을 공유하지 않습니다. 임시 엔진 로그는 완료 또는 취소 후 제거되며 엔진 페이지에 나타나지 않습니다.

표준 제공자 구성은 에이전트 그룹에 DeepSeek Harness · 모델(제공자) 선택지도 표시합니다. 저장된 ID는 엔진 페이지와 같은 한정된 제공자 경로를 감싼 dsh:lwui:ollama:<model> 또는 dsh:lwui:plugin:<plugin>:<model>이며 각 제공자 구성 요소는 퍼센트 인코딩됩니다. 선택적인 로컬 네이티브 DSH 연결은 연결된 인스턴스의 실시간 카탈로그에서 dsh:native:<provider>:<model>을 추가하고 원래 제공자의 설정과 인증 정보를 재사용합니다. libre-webui/dsh-native-provider의 Apache-2.0 독립 패키지를 설치하거나 Libre WebUI 배포본에서 번들을 준비하세요. 둘 다 @libre-webui/dsh-native-provider라는 패키지 이름을 사용하고 제공자 키는 네이티브 DSH에 보관합니다. 같은 Unix 호스트와 OS 계정의 비공개 Unix 소켓을 사용하므로 같은 계정을 공유하는 애플리케이션을 서로 격리할 수는 없습니다. 모델 추론만 제공하며 네이티브 에이전트 세션이나 도구 실행은 제공하지 않습니다. 설치, 프로필 재시작, 업그레이드, 제거는 설정 가이드를 참조하세요. 연결 또는 선택한 모델이 없으면 제공자를 바꾸지 않고 실패합니다. 네이티브 호출은 프로바이더 사용량에도 나타나며 선택 모델, 보고된 토큰, 지연 시간, 결과 상태를 확인할 수 있습니다. 기본 dsh 프로필은 실행 중인 구성의 기본 모델을 유지합니다. 사용자 지정 어댑터 구성은 기본 프로필만 제공하며 지원하지 않는 Libre WebUI 제공자 재정의를 선택지로 내놓지 않습니다.

제목과 사고 요약은 선택한 DSH 모델의 기반 제공자를 찾아 도구나 에이전트 세션 없이 직접 텍스트를 요청합니다. 기본 프로필 요청은 현재 카탈로그에서 추측하지 않고 브리지 행의 재정의를 포함한 실행 중인 엔진 기본값을 읽습니다. 사용자 지정 어댑터는 이런 기능을 위해 Ollama 또는 플러그인 작업 모델을 명시적으로 설정해야 합니다. 선택한 제공자가 없으면 정상적인 실패나 로컬 제목 미리보기를 반환하며 다른 제공자에 요청하지 않습니다.

요청에는 인증된 관리자의 제공자 설정과 인증 정보를 사용합니다. 다른 관리자의 인증 정보를 암묵적으로 선택하지 않습니다. 기본 작업 공간은 설정된 Cordis 작업 공간이며 Chat이 서버 사용자의 홈 디렉터리로 대체하지 않습니다.

샌드박스 Work

Cordis를 활성화하면 Work에 Libre WebUIDeepSeek Harness를 선택하는 별도의 엔진 제어가 나타납니다. 모델 선택기는 기존 모델 이름과 제공자 식별 정보를 유지합니다. LWUI 기반 제공자의 DSH 선택은 내부적으로 dsh:<model>로 저장합니다. 네이티브 DSH 선택은 providerType: dsh, 정확한 네이티브 제공자 ID, 원래 모델 ID로 저장합니다. 일반 도구 기능 및 접근 검사는 그대로 적용되고, 네이티브 인증 정보 사용에는 활성 관리자 조건이 추가됩니다.

실행마다 격리된 메모리 내 DSH 에이전트 루프를 만듭니다. 모델 어댑터는 현재 Work 대화 기록, 제공자 메타데이터, 이미지, 도구 스키마를 받습니다. 도구 본체는 Work가 반환한 결과를 기다릴 뿐이며 호스트 파일을 읽거나 호스트 프로세스를 시작할 수 없습니다.

인수 검증, 승인 요청, 작업 공간 런타임에서 도구 실행, 결과와 제공자 재생 상태의 SQL 기록, 예산 제한, 이벤트 발행은 계속 Work가 담당합니다. 거부된 도구는 일반적인 거부 결과를 생성합니다. 취소하면 DSH를 해제하고 Work의 기존 컨테이너 정리 절차를 따릅니다. 워커 복구 후 새로운 DSH 드라이버는 복원된 Work 컨텍스트를 받으며 완료된 도구의 부수 효과를 다시 실행하지 않습니다.

Work 통합에는 호스트 엔진 구성이나 JSONL 세션 저장소가 필요하지 않습니다. 팀 모드의 공유 영구 저장 요구 사항을 포함한 Work의 기존 Docker/Kubernetes 런타임 및 배포 규칙을 따릅니다.

보안 경계

엔진 페이지와 호스트 측 Chat 에이전트는 관리자 전용입니다. 시스템 프롬프트를 포함한 엔진 세션은 사용자별 작업 공간이 아닌 공유 관리자 콘솔입니다. 일반 계정은 API를 통해 이를 읽거나 생성, 수정, 취소할 수 없습니다.

기본 제공 호스트 파일 시스템 도구는 심볼릭 링크 해석을 포함한 정규화된 파일 시스템 대상을 사용하여 읽기와 쓰기를 설정된 작업 공간으로 제한합니다. 세션의 작업 디렉터리를 재정의해도 그 작업 공간 안에 있어야 합니다. 네이티브 DSH 변경 정책과 엔진의 일회성 승인은 계속 적용됩니다. 운영자가 설치한 구성 플러그인은 신뢰된 서버 코드로 추가 기능을 부여할 수 있습니다. 엔진 승인은 Work의 승인 및 컨테이너 실행 절차와 별개입니다.

Work의 DSH 드라이버는 별개입니다. 호스트 파일 시스템, 셸, 영구 저장 플러그인을 마운트하지 않으며 기존 Work의 권한 검사와 샌드박스를 통해서만 실행합니다. 원격 모델 제공자는 계속 명시적으로 활성화해야 하며 선택한 계정에 설정된 제공자 경로를 사용합니다.