운영 설치

모델 연결과 프로필 구성

세션이 사용할 연결과 프로필을 설정한다

무엇을 하게 되나

워크스페이스의 모델 연결과 프로필을 구성하고 세션 한 turn으로 실제 호출을 확인한다. 모델 자격 증명은 사용자 API key와 다르다.

준비물

owner 로그인, 사용할 모델의 key 또는 사설 모델 endpoint, 세션에서 접근할 저장소가 필요하다. 비용 상한과 설정 계약을 확인한다.

단계

1. 콘솔 연결과 프로필

세션이 모델을 부르려면 모델 키와, 그 키를 쓰는 프로필이 있어야 한다. 둘 다 웹 콘솔에서 만든다. owner로 로그인한 상태에서 다음 순서로 한다.

  1. 설정 > 에이전트 연결 > 모델 연결에서 연결 추가를 누른다. 종류는 anthropic, 주소는 기본값(https://api.anthropic.com)을 두고, Anthropic key를 붙여 넣어 저장한다. 키는 DB에 봉인돼 저장되고, 화면에는 끝 네 글자만 보인다.
  2. 프로필 > 프로필 만들기에서 방금 만든 연결을 고르고 모델·도구·실행 방식을 정해 저장한다. 에이전트를 만들 때 이 프로필을 고른다.

에이전트가 쓸 저장소는 프로필 생성 단계에서 고르지 않고, 해당 에이전트의 GitHub 탭에서 워크스페이스가 허용한 범위 안에서 고른다.

키를 넣기 전에 이 프로필로 세션을 시작하면 모델 연결에 쓸 수 있는 키가 없다는 취지의 오류가 나고, 오류가 1번 화면 경로를 알려 준다. 키를 바꿀 때도 같은 화면의 값 바꾸기를 쓴다.

2. 환경 변수 대안

키를 DB가 아니라 서버의 env 파일에 두고 싶다면 /etc/werft/test-ops.env의 ANTHROPIC_API_KEY를 채우고, 단일 서버 2단계의 설정 문서에 연결과 프로필을 환경 변수 이름으로 적는다. 문서에는 값이 아니라 이름만 적으므로 비밀이 없다. connections와 profiles를 아래처럼 채우고 저장소 항목에 "profiles": ["claude-coding"]을 더한 뒤 단일 서버 3단계에서 settings digest와 release 입력을 다시 생성한다. 이미 설치했다면 Compose 업그레이드로 적용한다.

{
  "connections": {
    "anthropic": {
      "kind": "anthropic",
      "endpoint": "https://api.anthropic.com",
      "auth": { "kind": "api_key", "value_env": "ANTHROPIC_API_KEY" }
    }
  },
  "profiles": {
    "claude-coding": {
      "runtime_kind": "claude_agent_sdk",
      "runtime_version": "0.3.319",
      "model": "claude-sonnet-5",
      "tools": ["Read", "Edit", "Write", "Glob", "Grep", "Bash"],
      "permission_mode": "default",
      "provider": {
        "kind": "anthropic",
        "endpoint": "https://api.anthropic.com",
        "auth": { "kind": "api_key", "value_env": "ANTHROPIC_API_KEY" }
      },
      "project_settings": { "claude_md": true }
    }
  }
}

이렇게 만든 연결은 나중에 화면으로 옮길 수 있다. 모델 연결의 해당 행에서 화면에서 키 넣기를 눌러 키를 다시 붙여 넣으면 같은 연결과 그 위의 프로필이 그대로 저장된 키를 쓴다. 서버가 env 값을 읽어 옮기지는 않는다. 키를 사람이 다시 넣는다. 옮긴 뒤에도 설정 문서에 같은 연결이 남아 있어도 가져오기는 DB의 행을 그대로 둔다.

Kubernetes에서 Secret의 모델 key를 쓰지 않으면 secrets.catalogEnv에서 그 이름을 뺀다. 고정 bootstrap 토큰 등 필요한 이름은 남긴다. 목록은 전체를 대체하므로 Secret 키와 맞춰야 한다.

3. 설정 문서 가져오기·내보내기

API_CONTAINER='<api-container-id-from-this-installation>'
docker exec "$API_CONTAINER" bun run apps/control-host/src/api/settings.ts export > settings.json
docker exec -i "$API_CONTAINER" bun run apps/control-host/src/api/settings.ts import --document - --dry-run < settings.json

API_CONTAINER는 이 설치의 status와 project label로 확인한다. Kubernetes에서 아래 package script는 설치 버전의 저장소 checkout과 호스트 Bun·kubectl을 필요로 한다. bun run settings:k8s export와 bun run settings:k8s import --document - --dry-run으로 바꾼다. dry-run의 추가 항목과 오류를 검토한 뒤 같은 명령에서 --dry-run을 빼고 가져온다. 기존 DB 행을 파일 내용으로 무조건 덮어쓰는 도구가 아니다. 자격 증명은 문서에 값 대신 환경 변수 이름을 적는다.

4. 과금 없는 사설 모델

과금 없는 profile (billing: none)

자체 호스팅 모델(Ollama, 사설망의 LiteLLM 등)은 토큰당 청구가 없다. 가격표에도 없어서 그대로 두면 fallback 최고 단가로 계산되고, SESSION_COST_LIMIT_USD와 SDK maxBudgetUsd에 걸려 turn이 끊긴다. 이런 profile은 provider.billing: none으로 선언한다.

provider:
  kind: litellm
  endpoint: http://litellm:4000
  billing: none
  auth:
    kind: bearer
    value_env: LITELLM_MASTER_KEY
  • 생략하면 per_token이다. 생략한 profile과 per_token이라고 적은 profile은 fingerprint가 같고, 둘 다 가격표·fallback으로 계산한다.
  • none profile로 만든 세션의 provider 호출은 응답이 어떤 model 이름을 대든 비용 0, ledger priced_by=unmetered로 적힌다. 토큰은 그대로 적힌다. 세션 비용이 늘지 않으므로 비용 상한 판정 네 곳(egress 인가, nextInput, 메시지 접수, 엔진)에 걸리지 않는다.
  • claim의 remaining_budget_usd는 null이고 worker는 SDK에 maxBudgetUsd를 넘기지 않는다. SDK는 모르는 model을 자체 추정 단가로 계산해 예산에서 turn을 끊기 때문이다. 묶을 예산이 없으므로 /clear 등으로 SDK의 비용 집계가 다시 시작돼도 worker는 drain하지 않는다.
  • GET /v1/sessions/{id}/usage는 cost.kind: "unmetered"로 답한다. amount_usd는 "0.000000"이다. 이 세션에는 비용 상한이 적용되지 않으므로 cost_limit_usd는 null이다. 상한 도달 안내(attention.reason)에도 달러 상한은 나오지 않는다. budget_exceeded는 토큰 상한에 닿았을 때만 true가 된다.
  • 과금 여부는 세션을 만들 때 세션 행(sessions.unmetered)에 고정된다. 가격·claim 예산·usage 표시는 이 값을 읽고, 보고를 받은 API의 카탈로그는 보지 않는다. 그래서 rolling update 중 그 profile을 모르는 replica가 보고를 받아도 비용이 붙지 않는다. billing을 바꾸면 fingerprint가 바뀌므로 새 id로 추가한다(같은 id를 고치면 기존 세션은 CATALOG_MISMATCH가 된다).
  • none profile로 세션을 만드는 것은 모든 API replica와 모든 scheduler의 WORKER_IMAGE를 이 버전으로 올린 뒤에 한다. 옛 API는 unmetered 세션의 보고도 가격표로 계산하고, 옛 worker는 claim의 remaining_budget_usd: null을 계약 위반으로 거절해 엔진을 띄우지 못한다. 같은 이유로 unmetered 세션이 남아 있는 동안에는 API·worker를 옛 버전으로 되돌리지 않는다.
  • 토큰 상한(SESSION_TOKEN_LIMIT)과 시간·동시성 상한(MAX_TURN_SECONDS, EXECUTION_SLOT_LIMIT, QUEUED_INPUT_LIMIT_PER_SESSION)은 그대로 걸린다. unmetered 행의 토큰도 sessions.provider_tokens에 더해진다.
  • API는 endpoint host가 사설 주소(10/8, 172.16/12, 192.168/16, 100.64/10, 127/8, 169.254/16, ::1, fc00::/7, fe80::/10)이거나 공개 DNS에 없는 이름(점 없는 compose 서비스 이름, localhost, .internal·.local·.localhost·.lan·.home.arpa)일 때만 none을 받는다. 그 밖이면 기동하지 않는다. egress proxy 쪽에서는 그 host를 EGRESS_CREDENTIAL_PRIVATE_ALLOWLIST에 둔다.
  • 사설 주소는 필요조건일 뿐이다. 사설망의 LiteLLM도 유료 upstream을 대리할 수 있다. none 선언이 맞는지는 운영자가 책임진다. 잘못 선언하면 유료 호출이 비용 상한 없이 나간다. SESSION_TOKEN_LIMIT을 설정해 두면 그때도 세션당 토큰은 묶인다. Datadog werft.provider.unmetered_tokens_1h(최근 1시간 unmetered 행 토큰 합)와 provider 콘솔의 사용량을 같이 본다.

활성화 전에 업그레이드 게이트를 충족해야 한다.

확인

프로필을 쓰는 에이전트에서 세션을 만들고 turn이 completed인지 확인한다. ready 200은 모델 key나 호출 성공을 검사하지 않는다. 환경 변수 경로를 콘솔로 옮긴 뒤에는 새 호출과 key 교체 동작을 확인한다.

자주 막히는 곳

모델 key 없음 오류는 연결 화면의 경로를 확인한다. 사설 endpoint라는 이유만으로 billing:none이 안전해지는 것은 아니다. 유료 upstream을 대리하는 모델을 none으로 선언하면 비용 상한 없이 호출된다.

되돌리기

사용 중인 프로필 id를 직접 수정해 fingerprint를 바꾸지 않는다. 새 연결·프로필 버전을 만들고 새 세션으로 전환한다. 콘솔에 저장한 key를 폐기할 때는 provider 쪽 권한도 함께 정리한다.

다음에 읽을 곳

용량과 비용 계획, 백업과 복원.

이 페이지