Skip to main content
Glama

vanth

에이전트를 위한 이벤트 기반 백그라운드 작업.

Vanth는 Model Context Protocol (MCP) 인터페이스를 갖춘 로컬호스트 백그라운드 작업 데몬입니다. 분리된 비대화형 셸 명령을 실행하고, 출력을 지속적으로 캡처하며, 선택적 AGENT_EVENT 구조화 이벤트를 진행률 표시줄, 메트릭 시리즈, 체크포인트로 파싱하고, 작업이 주의를 필요로 할 때 Codex 또는 OpenCode 세션을 깨울 수 있습니다. 하나의 신뢰할 수 있는 사용자와 하나의 머신을 위해 설계되었습니다.

  • 모든 명령: 다운로드, 이미지/오디오 처리, ETL, ML 학습 — 셸에서 실행된다면 Vanth는 분리하여 실행하고 추적할 수 있습니다.

  • 내구성: 작업과 이벤트는 SQLite (WAL, busy-timeout)에 저장되며 데몬, MCP, 머신 재시작에도 유지됩니다.

  • 이벤트 우선: 에이전트는 로그를 폴링하는 대신 의미 있는 이벤트를 job_wait합니다.

  • 주의 시 깨우기: 내구성 있는 최소 한 번 전달로 작업이 사람이나 에이전트를 필요로 할 때 Codex 스레드 또는 OpenCode 세션을 재개합니다.

  • 터미널 대시보드: 네이티브 Go monitor가 작업, 메트릭, 플롯의 실시간 W&B-LEET 스타일 대시보드를 렌더링합니다.

v1 범위 밖: 원격 네트워크 액세스, TLS, 다중 사용자 테넌시/RBAC, 할당량, 대화형 stdin, 웹 UI.

에이전트를 위한: job_start로 작업을 시작하고, 폴링 대신 progress/checkpoint/completed 이벤트를 job_wait하세요. 작업이 AGENT_EVENT 줄(아래 참조)을 방출하도록 하여 진행률, 메트릭, 체크포인트가 vanth-monitor 대시보드에 실시간으로 나타나게 하고, 긴 작업은 당신이 확인하는 대신 깨우기 대상을 통해 당신을 재개하도록 하세요.


Quick start

설치 (uv 필요; Python 3.11+에서 실행):

uv tool install vanth

이것은 vanth MCP 서버, vanthd 데몬, vanth-monitor, 그리고 ops CLI를 독립형 도구로 설치합니다 (휠은 네이티브 Go 모니터를 번들로 포함하므로 Go 툴체인이 필요하지 않습니다).

소스 체크아웃에서 (개발):

git clone https://github.com/abhim-dv/vanth.git && cd vanth
uv sync

데몬 시작 (이 터미널을 열어 두세요):

uv run vanthd

두 번째 터미널에서 MCP 서버를 통해 추적되는 작업을 시작하세요:

uv run vanth

또는 MCP 클라이언트에서 직접 도구를 사용하세요 (MCP 통합 참조).

모든 것이 정상인지 확인하세요:

job_doctor()

End-to-end: run a tracked job

MCP 클라이언트가 연결되면, 전체 루프는 다음과 같습니다:

job_start(
  command="uv run python examples\\long_job.py",
  name="demo run",
  notify_on=["checkpoint", "failed", "completed"],
)
# -> job_<id>

job_wait(job_id="job_<id>", filters=["checkpoint"], timeout_seconds=120)
# -> returns the first checkpoint event + current status

job_wait(job_id="job_<id>", filters=["completed", "failed"], timeout_seconds=300)
# -> returns the terminal event + exit code

그리고 세 번째 터미널에서 실시간으로 확인하세요:

uv run vanth-monitor

Command-line entry points

Command

Purpose

uv run vanth

MCP stdio 서버 (데몬에 대한 브리지); 또한 status / doctor / restart 하위 명령

uv run vanthd

백그라운드 HTTP 데몬

uv run vanth-monitor

실시간 터미널 대시보드 (Go 바이너리, 휠에 번들됨)

uv run vanth-codex-notify

전달 어댑터: stdin에서 깨우기 페이로드를 읽어 Codex로 전송

Operations CLI

uv run vanth status              # is the daemon up? pid, schema, running jobs, deliveries
uv run vanth status --json       # machine-readable version
uv run vanth doctor              # full health report (same as job_doctor, human-readable)
uv run vanth restart             # gracefully stop + start the daemon (jobs survive)
uv run vanth setup               # register the MCP server in your clients' configs
uv run vanth setup --remove      # unregister it

vanth restart는 코드/버전 업데이트를 적용하는 신뢰할 수 있는 방법입니다: 데몬에 루프백을 통해 정상 종료를 보내고, 이전 프로세스가 홈 잠금을 완전히 해제할 때까지 기다린 후, 새 데몬을 시작합니다. 진행 중인 작업은 분리된 러너가 소유하므로 재시작 중에도 계속됩니다.


Related MCP server: Background Process MCP

How it works

MCP client / HTTP client
        |
        v
   vanthd (localhost HTTP daemon, bearer-token auth)
        |                 |                    |
        |                 |                    +---> wake adapters
        |                 |                          (local_command / codex_thread / opencode_thread)
        |                 |
        |                 +----> jobs.sqlite (durable source of truth)
        |
        +----> vanth.runner (detached worker process)
                    |
                    +----> your command (own process group)
                              |
                              +----> stdout/stderr -> logs/ + AGENT_EVENT parsing

소유권 규칙:

  • 러너는 실제 명령, 타임아웃, 스트림 드레이닝을 소유합니다;

  • 데몬은 유지보수, 전달 디스패치, API 요청, 복구를 소유합니다;

  • SQLite는 프로세스 재시작 전반에 걸쳐 진실의 원천입니다;

  • MCP 및 HTTP 클라이언트는 작업이 계속되기 위해 살아 있을 필요가 없습니다.

작업은 두 출력 스트림이 모두 EOF에 도달하고 모든 구조화된 이벤트가 영구 저장될 때까지 종료된 것으로 간주되지 않습니다.

Job lifecycle

작업은 소수의 상태를 거칩니다. 종료 상태는 영구적입니다.

State

Meaning

running

워크로드 실행됨; 러너가 출력을 스트리밍하고 하트비트를 보내는 중

completed

명령이 0으로 종료됨, 스트림 드레이닝 완료, 이벤트 영구 저장됨

failed

명령이 0이 아닌 값으로 종료됨

timeout

명령이 timeout_seconds를 초과함; 러너가 종료시킴

cancelled

job_stop이 발행되었고 프로세스 트리가 실제로 종료됨

orphaned

러너가 예기치 않게 사망함 (크래시); 절대 조용히 삭제되지 않음

러너는 데몬 재시작에도 timeout_seconds를 적용합니다. 복구 시, 러너가 사라진 running 작업은 (중지가 요청된 경우) cancelled로 표시되거나 (그렇지 않은 경우) orphaned로 표시됩니다 — 절대 좀비 running 행으로 남지 않습니다.


Installing the MCP server

vanth는 MCP stdio 서버입니다. 데몬과 통신하며, 아직 실행 중이 아닌 경우 첫 사용 시 자동으로 시작합니다.

One-shot setup

도구를 설치한 후, 한 단계로 머신의 MCP 클라이언트에 연결하세요:

uv tool install vanth
vanth setup

vanth setup은 설치된 클라이언트(opencode, Codex, 그리고 Claude Code / Cursor와 같은 일반 mcpServers 스타일 클라이언트)를 감지하고, 찾은 내용을 표시하며, 각 구성을 건드리기 전에 백업하고 (.vanth-setup-<ts>.bak), Vanth MCP 항목을 업서트(upsert)합니다 — 다른 모든 설정과 주석은 그대로 둡니다.

vanth setup                  # detect + configure everything found (prompts)
vanth setup --yes            # apply without prompting (scripts/CI)
vanth setup opencode codex   # only specific clients
vanth setup --json           # machine-readable result
vanth setup --remove         # remove the Vanth MCP entries instead

관리하는 구성:

클라이언트

파일

섹션

opencode

~/.config/opencode/opencode.json

mcp.vanth

Codex

~/.codex/config.toml

[mcp_servers.vanth]

Claude Code / Cursor

~/.claude.json

mcpServers.vanth

수동으로 동일한 항목은 다음과 같습니다:

opencode

~/.config/opencode/opencode.json에 추가:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "vanth": {
      "type": "local",
      "command": ["vanth"],
      "enabled": true,
      "timeout": 15000
    }
  }
}

소스 체크아웃에서는 vanth 대신 uv를 직접 사용하세요:

{
  "mcp": {
    "vanth": {
      "type": "local",
      "command": ["uv", "run", "--directory", "/path/to/vanth", "vanth"],
      "enabled": true,
      "timeout": 15000
    }
  }
}

연결 및 도구 확인:

opencode mcp list

Claude-style MCP clients (mcpServers)

게시된 휠:

{
  "mcpServers": {
    "vanth": { "command": "vanth", "env": { "VANTH_HOME": "C:/Users/you/.vanth" } }
  }
}

소스 체크아웃에서:

{
  "mcpServers": {
    "vanth": {
      "command": "uv",
      "args": ["--directory", "/path/to/vanth", "run", "vanth"],
      "env": { "VANTH_HOME": "C:/Users/you/.vanth" }
    }
  }
}

Configuring the daemon home

MCP 서버와 데몬 모두 VANTH_HOME에서 동일한 상태 루트를 확인합니다 (Windows에서는 기본값 %USERPROFILE%\.vanth, Unix에서는 ~/.vanth; AGENT_BG_HOME이 별칭으로 허용됩니다). 둘 다 설정된 경우 동일한 디렉터리로 확인되어야 합니다.


Instrumenting jobs with agent_event

모든 Python 스크립트는 Vanth가 파싱하고 모니터가 차트로 표시하는 구조화된 이벤트를 stdout(또는 stderr)으로 방출할 수 있습니다. 이는 선택 사항입니다 — 일반 스크립트도 여전히 실행되고 로그를 남깁니다 — 하지만 이것이 작업을 일급 추적 객체로 만드는 것입니다.

from vanth.agent_events import agent_event, progress

# A checkpoint: something meaningful happened.
agent_event("checkpoint", "epoch complete", epoch=10, val_loss=0.42)

# A progress update: drives the progress bar and progress.* plots.
progress(10, 100, unit="epoch", stage="train", message="10/100 epochs")

# Arbitrary scalar metrics: become their own line plots.
agent_event("metric", _step=10, loss=0.42, acc=0.88, mbps=12.4)

참고:

  • 헬퍼는 flush=True로 AGENT_EVENT {json}을 출력합니다 (flush가 중요합니다);

  • progress(current, total, unit=..., stage=...)가 percent를 계산해 줍니다;

  • metric 페이로드: 숫자 필드는 시리즈가 됩니다; _step (존재하고 숫자인 경우)이 x축이 되고, 그렇지 않으면 이벤트 시퀀스 번호가 사용됩니다; _step 이외에 _로 시작하는 키는 무시됩니다; 부울은 메트릭이 아닙니다; NaN/Infinity/null 값은 건너뛰고 모니터의 경고 배지에 집계됩니다;

  • 다른 모든 필드 (예: file, stage, phase)는 보존되며 정확한 이벤트 테이블에서 볼 수 있습니다.

Example: a tracked downloader

# downloader.py
import os
from vanth.agent_events import agent_event, progress

files = ["a.bin", "b.bin", "c.bin"]
total = sum(os.path.getsize(f) for f in files)
done = 0

for f in files:
    agent_event("checkpoint", f"starting {f}", file=f)
    # ... download f ...
    done += os.path.getsize(f)
    progress(done, total, unit="bytes", stage="download",
             message=f"{done}/{total} bytes")

Example: an image-processing batch

from vanth.agent_events import agent_event, progress

images = list(find_images("input/"))
for i, img in enumerate(images, 1):
    out = process(img)                    # resize, denoise, ...
    agent_event("metric", _step=i, sharpness=out.sharpness, size_mb=out.size_mb)
    progress(i, len(images), unit="images", stage="process", message=img.name)

Timestamped, leveled logging with loguru

Vanth는 모든 레코드를 구조화된 AGENT_EVENT 로그 줄로 라우팅하는 loguru 래퍼를 제공하므로, 로그가 일반 텍스트 대신 이벤트 테이블에 타임스탬프가 있고 레벨을 인식하는 이벤트(레벨 배지와 정확한 타임스탬프 포함)로 나타납니다:

from vanth.agent_logger import logger, log_with_context

logger.info("training started", lr=8e-5, batch_size=8)     # event type "log", level info
logger.warning("low disk", free_gb=2.5)
log_with_context("error", "failed to load checkpoint", path="best.pt")

각 호출은 AGENT_EVENT {"type":"log","level":"info","message":"...","data":{...}}를 방출하며, 데몬은 이를 내구성 있는 이벤트로 영구 저장합니다. data는 추가 컨텍스트를 전달합니다. 모니터는 이를 metric/progress 이벤트와 함께 정확한 이벤트 테이블에 표시합니다.


Tool reference (all 20 MCP tools)

Tool

Purpose

job_start

명령을 분리된 작업으로 실행

job_rerun

원래 명령/env/cwd/대상으로 작업 재실행

job_wait

일치하는 이벤트(또는 타임아웃)까지 차단 — 작업을 기다리는 권장 방법

job_status

하나의 작업 상태, 명령, env, 진행률, 마지막 이벤트, 연결, 태그

job_list

최근 작업, status / thread_id / name / tags로 필터 가능

job_view

에이전트 대상 요약, 주의 우선순위로 정렬

job_events

작업의 구조화된 이벤트 (since_event_id로 앞으로, 또는 reverse로 최신순)

job_tail

바이트 오프셋이 있는 제한된 stdout/stderr 로그 꼬리

job_metrics_query

저장된 스칼라 메트릭 시리즈 읽기 (loss, acc, progress.percent, ...)

job_metric_compare

작업 간 하나의 메트릭 비교 (최신/평균/최소/최대/합계/개수)

job_run_summary

한 번 호출로 '작동했나요?' — 상태, 실행 시간, 진행률, 메트릭, 아티팩트

job_artifact_add

아티팩트 (체크포인트, CSV, 출력)를 작업에 첨부

job_artifacts

작업에 첨부된 아티팩트 목록

job_dashboard

모든 렌더러를 위한 다운샘플링된 차트 데이터 보기

job_deliveries

작업의 깨우기 전달, status로 필터 가능

job_mark_delivery

전달 상태를 수동으로 설정

job_retry_delivery

실패한 전달을 디스패치를 위해 다시 대기열에 넣기

job_delivery_attempts

하나의 전달에 대한 시도/임대 기록

job_stop

실행 중인 작업 중지 (프로세스 트리 종료)

job_doctor

데몬 상태, 스키마, 테이블, 바이너리 가용성

job_cleanup

오래된 종료 작업의 시험 실행 또는 실제 제거

job_start

job_start(
  command="uv run python examples\\long_job.py",
  name="training run",
  cwd="F:\\git\\project",            # optional
  env={"CUDA_VISIBLE_DEVICES": "0"}, # optional
  timeout_seconds=3600,              # optional; None = no timeout
  notify_on=["progress","checkpoint","failed","completed"],
  origin_thread_id="019f...",        # the agent thread that launched it
  tags=["training","gpu"],           # optional
  wake_targets=[...]                 # optional, see below
)

job_id, status, worker_pid, 로그/이벤트 경로를 반환합니다.

job_status — see what a job is running

job_status(job_id="job_...")

상태, 명령, cwd, env, timeout_seconds, notes, run (작성자, 호스트명, OS, Python 버전, CPU/GPU, git 저장소/브랜치/커밋), runtime_seconds, 진행률, 마지막 이벤트, 스레드 연결, 태그, 종료 코드를 반환합니다. 이것은 에이전트가 "이 작업이 무엇을 하고 있나요?"에 답하는 가장 빠른 방법이며, W&B에서 실행에 대해 볼 수 있는 실행 개요를 반영합니다.

job_start에 notes="..."를 전달하여 실행에 주석을 달 수 있습니다 ("이 실행을 특별하게 만드는 것은 무엇인가요?"), 이는 job_rerun에서 보존되고 모니터에 표시됩니다.

job_rerun — relaunch a failed job

job_rerun(job_id="job_...")

원래 명령, cwd, env, 타임아웃, 이름, 태그, 원본 스레드, 깨우기 대상으로 작업을 재실행합니다 — 새 job_id가 반환됩니다. 요청을 재구성하지 않고 실패한 다운로드, 불안정한 처리 배치, 또는 일시적 실패를 재시도하는 데 사용하세요.

job_list — filter by name or tag

job_list(status=["running"], name="train", tags=["gpu"], limit=20)

필터: status (목록), thread_id, name (부분 문자열), tags (나열된 모든 태그를 포함해야 함).

job_events — forward or latest-first

job_events(job_id="job_...", since_event_id="evt_...", limit=20)      # events after the cursor
job_events(job_id="job_...", reverse=true, limit=20)                   # the 20 newest events, newest first

reverse: true는 가장 최근 이벤트를 반환합니다 (최신순) — "최근에 무슨 일이 있었나요?"에 이상적 — since_event_id와 결합하여 뒤로 페이지를 넘길 수 있습니다.

job_wait — 에이전트 사용의 핵심

job_wait(job_id="job_...", filters=["checkpoint","failed","completed"], timeout_seconds=3600)
  • 필터와 일치하는 첫 번째 이벤트를 기다렸다가 현재 상태와 함께 반환합니다.

  • since_event_id를 전달하면 이미 본 이벤트보다 최신 이벤트만 기다립니다.

  • 타임아웃 시 result: "timeout"을 반환하고, 데몬 종료 시 result: "shutdown"을 반환합니다.

job_view — 사용자에게 보여줄 내용

job_view(thread_id="019f...", limit=20)

주의 우선순위에 따라 정렬된 간결한 요약을 반환합니다: 실행 중인 작업과 실패한 작업이 먼저, 그다음으로 보류 중/실패한 전달이 있는 작업, 그 외 나머지 순서입니다. 각 항목에는 상태, 진행률, 최신 이벤트, 스레드 연결, 태그 및 전달 횟수가 포함됩니다.

job_stop — 실행 중인 작업 중지

job_stop(job_id="job_...", signal="terminate", kill_after_seconds=10)

작업의 프로세스 트리를 종료합니다. 먼저 정상 종료 signal(기본값 terminate)이 전송됩니다. kill_after_seconds 내에 작업이 종료되지 않으면 강제 종료됩니다. 작업은 워크로드 트리가 실제로 종료된 후에만 cancelled 상태가 됩니다. 그렇지 않으면 running 상태로 유지되며 중지를 재시도할 수 있습니다.

job_mark_delivery / job_retry_delivery — 수동 전달 제어

job_mark_delivery(delivery_id="del_...", status="delivered", error="optional reason")
job_retry_delivery(delivery_id="del_...")   # requeue a failed delivery

job_mark_delivery는 전달 상태를 수동으로 설정합니다(예: 어댑터 문제 해결 후). job_retry_delivery는 실패한 전달을 다음 디스패치 패스를 위해 다시 대기열에 넣습니다. job_delivery_attempts는 클레임/리스 기록을 보여줍니다.

job_cleanup — 오래된 종료 작업 제거

job_cleanup(older_than_seconds=86400, dry_run=true)   # preview
job_cleanup(older_than_seconds=86400, dry_run=false)  # delete

기준 시간보다 오래된 종료 작업을 제거합니다: 로그, 이벤트 미러, 스펙, 전달, 시도, 웨이크 대상, 이벤트, 그다음 작업 행 순서입니다. 실행 중인 작업은 절대 선택되지 않습니다. 드라이런은 완전히 읽기 전용입니다. 정리는 반복해도 안전합니다.

job_metrics_query — 저장된 스칼라 시계열 읽기

job_metrics_query(job_id="job_...", metric="loss", from_ms=..., to_ms=..., limit=1000)

하나의 작업에 대해 저장된 시계열을 메트릭 이름별로 그룹화하여 반환합니다. metric은 단일 시계열로 필터링합니다(예: loss, acc, progress.percent). from_ms/to_ms는 이벤트 타임스탬프(에포크 밀리초)로 필터링합니다. 포인트는 이벤트 순서대로 정렬됩니다. 이는 터미널 모니터 데이터의 읽기 측면입니다.

job_metric_compare — 실행 간 메트릭 비교

job_metric_compare(job_ids=["job_a", "job_b"], metric="val_loss", aggregation="min")

작업 간에 하나의 메트릭을 비교합니다(예: 시드 또는 구성 간 val_loss). aggregation은 latest, mean, min, max, sum 또는 count입니다. 결과에는 작업별 값과 첫 번째/마지막 포인트가 포함됩니다. 이는 W&B 스타일의 "어떤 실행이 이겼는가?" 프리미티브입니다.

job_run_summary — 작동했는가?

job_run_summary(job_id="job_...")

한 번의 호출로 상태, 이름, 실행 시간, 종료 코드, 최신 진행률, 메모, 메트릭별 개요(최신/첫 번째/최소/최대/개수) 및 첨부된 아티팩트를 반환합니다. 에이전트가 완료된 작업을 보고하는 가장 빠른 방법입니다.

job_artifact_add / job_artifacts — 출력물 첨부

job_artifact_add(job_id="job_...", name="best.pt", uri="file:///...", kind="checkpoint",
                 size_bytes=..., sha256="...", meta={"epoch": 5})
job_artifacts(job_id="job_...")

아티팩트(체크포인트, CSV, 렌더링된 출력)를 작업에 첨부하여 job_run_summary에 나열되고 나중에 검색할 수 있도록 합니다. meta는 자유 형식 JSON입니다.

job_dashboard — 모든 렌더러를 위한 차트 데이터

job_dashboard(job_ids=["job_..."], limit=5000)

작업 목록과 모든 저장된 메트릭 시계열을 시계열당 limit 포인트로 다운샘플링하여 반환합니다. Go 터미널 모니터가 차트로 표시하는 동일한 데이터로, HTTP/MCP를 통해 노출되어 모든 클라이언트(향후 웹/클라우드 대시보드)가 렌더링할 수 있습니다.


웨이크 대상 (작업이 주의를 필요로 할 때 에이전트 깨우기)

작업이 일치하는 이벤트를 내보내면 데몬은 지속적인 전달을 생성하고 어댑터를 통해 디스패치합니다. 전달은 최소 한 번(at-least-once) 입니다. 모든 페이로드는 중복 제거를 위해 delivery_id를 전달합니다.

local_command

임의의 명령을 실행하고 전달 페이로드를 JSON으로 stdin에 전달합니다:

{
  "type": "local_command",
  "events": ["checkpoint", "failed", "completed"],
  "command": ["python", "deliver.py"]
}

종료 코드 0은 전달을 delivered로 표시하고, 다른 종료 코드는 failed로 표시합니다.

codex_thread

로컬 앱 서버를 통해 Codex 스레드를 재개합니다:

{
  "type": "codex_thread",
  "thread_id": "019f...",
  "events": ["checkpoint", "failed", "completed"],
  "codex_command": ["C:\\codex\\codex.exe"]
}

프로토콜: initialize -> thread/resume -> turn/start.

opencode_thread

OpenCode 세션을 재개합니다:

{
  "type": "opencode_thread",
  "thread_id": "ses_...",
  "events": ["checkpoint", "failed", "completed"],
  "cwd": "F:\\git\\project",
  "opencode_command": ["opencode"],     # override the binary
  "attach": "http://127.0.0.1:4096",    # submit via an opencode serve instance
  "timeout_seconds": 120
}

기본 OpenCode 턴 타임아웃은 30초입니다. 긴 턴의 경우 늘리십시오.

공유 전달 옵션

{
  "type": "codex_thread",
  "thread_id": "019f...",
  "events": ["checkpoint"],
  "auto_dispatch": false,      // leave the delivery pending for manual inspection
  "max_attempts": 3,           // default 1
  "retry_delay_seconds": 5,    // default 5
  "timeout_seconds": 30        // adapter timeout; also sizes the delivery lease
}

auto_dispatch: false인 경우, 전달은 에이전트가 수동으로 디스패치하거나 대상을 변경할 때까지 pending 상태로 유지됩니다.

전달 작업

job_deliveries(job_id="job_...")
job_delivery_attempts(delivery_id="del_...")
job_retry_delivery(delivery_id="del_...")     # requeue a failed delivery
job_mark_delivery(delivery_id="del_...", status="delivered")

시도 기록에는 클레임 토큰, 시작/종료 시간, 상태 및 리스 만료 후 시도가 회수되었는지 여부가 기록됩니다. 어댑터가 웨이크를 수락했지만 Vanth가 성공을 기록하기 전에 데몬이 충돌하면 전달이 회수되어 재시도됩니다. 이는 정확히 한 번 전달로 클레임되는 것이 아니라 reclaimed 시도로 표시됩니다.


데몬 실행

포그라운드 (개발 또는 진단용):

uv run vanthd

로그인 시 시작 옵션:

  • Windows: 데몬은 사용자 시작 폴더(startup_commands.bat)에서 다른 시작 명령과 함께 시작됩니다. Task Scheduler 작업 템플릿도 deploy/vanthd.cmd에 있습니다.

  • Unix: deploy/vanthd.service는 systemd 사용자 서비스입니다.

VANTH_HOME당 하나의 데몬만 활성화하십시오. 동일한 홈에 대한 두 번째 데몬은 즉시 종료됩니다(OS 수준 잠금). 데몬은 루프백(127.0.0.1 / ::1 / localhost)에만 바인딩됩니다. 루프백이 아닌 VANTH_DAEMON_HOST는 거부됩니다.

보안

  • 모든 데이터 경로에는 Authorization: Bearer <token>이 필요합니다. 토큰은 홈별로 생성되며 절대 기록되지 않습니다. GET /health는 유일한 인증되지 않은 경로입니다(감독자를 위한 저렴한 활성 상태 프로브).

  • 데몬 시작 시 상태 디렉터리는 소유자로 다시 강화됩니다: Unix chmod 0700/0600, Windows는 ACL 상속을 비활성화하고 icacls를 통해 소유자, SYSTEM 및 Administrators에게만 권한을 부여합니다. 이는 다른 계정(예: 사용자 프로필에서 읽기 권한을 상속받는 샌드박스/CI 사용자)이 토큰이나 작업별 환경/스펙 데이터를 읽는 것을 차단합니다.

  • Windows에서는 소켓 SO_REUSEADDR이 비활성화되어 두 번째 데몬이 동일한 포트에서 팬텀 리스너가 될 수 없습니다. 바인딩 실패 시 홈 잠금이 해제되고 깔끔하게 종료됩니다.

Go 터미널 모니터

네이티브 Go 대시보드는 동일한 홈을 읽기 전용으로 읽고 실시간 플롯, 진행률 표시줄, 정확한 이벤트 테이블 및 로그 테일을 렌더링합니다:

uv run vanth-monitor

빌드된 휠에서 vanth-monitor는 번들된 네이티브 바이너리를 실행합니다(Go 도구 체인 불필요). 소스 체크아웃에서는 첫 사용 시 모니터를 빌드하고 ~/.cache/vanth/ 아래에 캐시합니다(PATH에 go 필요):

go build -o bin\vanth.exe ./cmd\vanth
bin\vanth.exe monitor

키: up/down 또는 j/k로 작업 선택 · enter로 작업 시계열 고정 · e 이벤트 테이블 · l 로그 테일 · +/- 차트 확대/축소 · [/] 이동 · t 라이브 테일로 돌아가기 · ? 도움말 · q 또는 Ctrl+C 종료.


구성 참조

환경 변수 (기본값은 src/vanth/server.py, src/vanth/daemon.py, src/vanth/migrations.py에 있음):

변수

기본값

목적

VANTH_HOME

~/.vanth

상태 루트 (별칭: AGENT_BG_HOME)

VANTH_DAEMON_URL

http://127.0.0.1:8765

클라이언트가 데몬에 도달하는 주소

VANTH_DAEMON_HOST

127.0.0.1

바인드 주소 (루프백만)

VANTH_DAEMON_PORT

8765

바인드 포트

VANTH_MAX_REQUEST_BYTES

1 MiB

HTTP 요청 본문 제한

VANTH_MAX_RESPONSE_BYTES

4 MiB

HTTP 응답 제한

VANTH_MAX_EVENT_BYTES

64 KiB

단일 이벤트 페이로드 제한

VANTH_MAX_EVENT_LINE_BYTES

1 MiB

AGENT_EVENT 라인 제한

VANTH_MAX_LOG_BYTES

10 MiB

스트림당 로그 제한 (드레인 계속)

VANTH_MAX_EVENTS_PER_JOB

100000

작업당 구조화된 이벤트 제한

VANTH_DELIVERY_POLL_INTERVAL

0.2s

유지 관리 루프 주기

VANTH_DELIVERY_LEASE_MARGIN

5s

어댑터 타임아웃을 초과하는 추가 리스 시간

VANTH_RUNNER_HEARTBEAT_INTERVAL

1s

러너 활성 상태 하트비트

VANTH_RUNNER_HEARTBEAT_STALE_AFTER

10s

하트비트 부실 임계값

VANTH_CODEX_BIN

codex / C:\codex\codex.exe

Codex 바이너리

VANTH_OPENCODE_BIN

opencode (shutil.which 통해)

OpenCode 바이너리

VANTH_LOG_LEVEL

INFO

데몬 로그 수준

VANTH_LOG_MAX_BYTES

5 MiB

순환 데몬 로그 크기

VANTH_LOG_BACKUP_COUNT

3

데몬 로그 순환 횟수

VANTH_BUSY_TIMEOUT_MS

30000

SQLite 쓰기 잠금 대기


운영

상태 레이아웃

~/.vanth/
  jobs.sqlite      durable jobs (incl. env, notes, run-overview) / events / deliveries / targets / attempts / tombstones
  token            bearer token (owner-only permissions)
  daemon.lock      single-daemon OS lock
  daemon.json      discovery metadata (url, pid, started_at, schema) — written atomically, removed on graceful shutdown
  logs/            daemon.log + per-job runner/stdout/stderr logs
  events/          per-job JSONL event mirrors (monitor fallback source)
  specs/           per-job launch specs (removed once the runner starts)
  backups/         pre-migration SQLite backups

상태, 준비 상태 및 진단

job_doctor()

상태 디렉터리, 데이터베이스 테이블, 상태별 전달 횟수, 스키마 버전, PRAGMA quick_check, 부실 전달 리스, 여유 디스크, 토큰 경로 및 Codex/OpenCode 바이너리 확인 가능 여부를 보고합니다. 토큰은 절대 공개하지 않습니다.

HTTP 데몬은 또한 다음을 노출합니다:

  • GET /health — 저렴하고 인증되지 않은 감독자용 활성 상태 프로브;

  • GET /ready — 인증된 준비 상태 (의사 보고서, 준비되지 않음 시 503).

업그레이드 및 백업

스키마 변경은 순서가 지정된 SQLite 마이그레이션입니다. 기존 데이터베이스의 첫 번째 마이그레이션 전에 SQLite 백업 API를 통해 backups/ 아래에 타임스탬프가 있는 백업이 기록됩니다(WAL이 활성화된 상태에서 원시 파일 복사는 절대 안 됨). 수동으로 업그레이드하려면 먼저 최신 backups/*.sqlite를 복사하십시오. 향후 데이터베이스 스키마는 파일을 건드리지 않고 거부됩니다.


HTTP API (MCP 도구와 동등)

Authorization: Bearer <token>으로 인증됩니다.

메서드

경로

목적

GET

/jobs

작업 목록 조회 (status, limit, thread_id, name, tags)

POST

/jobs

작업 시작

POST

/jobs/{id}/rerun

원래 설정으로 작업 재실행

GET

/jobs/{id}/status

작업 상태 (명령어/env/cwd 포함)

GET

/jobs/{id}/events

이벤트 (since_event_id, types, limit, reverse)

GET

/jobs/{id}/metrics

메트릭 시리즈 (metric, from_ms, to_ms, limit)

GET

/jobs/{id}/summary

실행 요약 (상태, 실행 시간, 메트릭, 아티팩트)

GET

/jobs/{id}/artifacts

아티팩트 (limit)

POST

/jobs/{id}/artifacts

아티팩트 추가

GET

/metrics/compare

작업 간 메트릭 비교 (job_ids, metric, aggregation)

GET

/dashboard

차트 데이터 (job_ids, limit)

GET

/jobs/{id}/tail

로그 꼬리 보기 (stream, max_bytes, offset)

POST

/jobs/{id}/wait

이벤트 대기

POST

/jobs/{id}/stop

작업 중지

GET

/view

에이전트 뷰 (thread_id, limit)

GET

/deliveries

전달 (job_id, status, limit)

GET

/deliveries/{id}/attempts

시도 이력

POST

/deliveries/{id}/mark

전달 표시

POST

/deliveries/{id}/retry

전달 재시도

POST

/cleanup

정리 (older_than_seconds, dry_run)

GET

/doctor

상태 보고서

GET

/health

인증 없는 활성 상태 확인


에이전트 사용 팁

  1. 대기(polling 대신). job_status를 반복 호출하는 대신 job_wait(job_id, filters=[...], timeout_seconds=...)를 사용하세요. 데몬은 일치하는 이벤트가 저장되는 즉시 대기를 깨웁니다.

  2. since_event_id 전달. 이벤트를 처리한 후 다음 job_wait에 since_event_id를 전달하여 이전 이벤트를 다시 처리하지 않도록 하세요.

  3. 작업에 태그와 스레드를 지정하세요. origin_thread_id(작업을 시작한 에이전트 스레드)와 tags를 설정하고, job_view(thread_id=...)를 사용하여 요약하세요.

  4. 사용자에게 상황을 제시할 때는 job_view를 job_status보다 선호하세요. 이미 주의 우선순위별로 정렬되어 있습니다.

  5. 작업을 자체 설명 가능하게 만드세요. AGENT_EVENT progress / checkpoint / metric 라인을 내보내세요(위 참조). 침묵하는 작업도 작동하지만, 추적된 작업이 훨씬 분석하기 쉽습니다.

  6. 장기 작업에는 웨이크 대상을 사용하세요. 훈련 실행이나 긴 다운로드가 체크포인트에서 결정을 필요로 하는 경우, events: ["checkpoint", "failed", "completed"]와 함께 codex_thread 또는 opencode_thread 대상을 추가하여 에이전트가 폴링 대신 재개되도록 하세요.

  7. 전달 실패를 검사하세요. job_delivery_attempts는 임대/클레임 내역을 보여줍니다. job_retry_delivery는 원인을 수정한 후 실패한 전달을 다시 대기열에 넣습니다.

  8. job_start에 적절한 timeout_seconds를 설정하세요. 중단된 명령어가 영원히 실행되는 대신 timeout(종료) 상태가 됩니다. 러너는 데몬 재시작에도 이 제한을 적용합니다.

  9. job_cleanup(older_than_seconds=..., dry_run=false)로 오래된 상태를 정리하세요. SQLite 저장소와 로그 파일의 크기를 제한할 수 있습니다.

  10. 실패한 작업을 재구축하지 말고 재실행하세요. job_rerun(job_id=...)는 원래 명령어, env, cwd 및 웨이크 대상으로 다시 실행합니다. 일시적으로 실패한 다운로드나 배치를 재시도하는 데 이상적입니다.

  11. "이 작업이 무엇인가요?"라고 묻는다면 job_status를 사용하세요. 이제 명령어, cwd, env 및 타임아웃을 반환하므로 로그를 읽지 않고도 사용자에게 작업을 설명할 수 있습니다.

  12. 이름/태그로 목록을 필터링하세요. job_list(name="train", tags=["gpu"])는 모든 것을 페이지로 탐색하지 않고도 증가하는 작업 목록을 좁힙니다.

  13. "최근에 무슨 일이 있었나요?"에는 reverse=true를 사용하세요. job_events(job_id, reverse=true, limit=20)는 가장 최근 이벤트를 먼저 반환하며, 본 가장 오래된 ID로 since_event_id를 설정하여 더 뒤로 페이지를 탐색할 수 있습니다.

  14. 작업은 데몬이 재시작되어도 유지됩니다. 러너는 분리되어 있습니다. 작업은 데몬/MCP 재시작에도 계속됩니다. 복구 시 러너가 없으면 작업은 orphaned(고아)로 표시됩니다(조용히 삭제되지 않음).


예제

uv run python examples\long_job.py    # emits progress + checkpoints

examples/long_job.py는 vanth.agent_events를 사용하는 작은 참조 작업입니다. job_start를 통해 시작하고 vanth monitor에서 지켜보세요.


문제 해결

  • Unauthorized (401): ~/.vanth/token의 베어러 토큰이 데몬이 기대하는 토큰과 일치하는지 확인하세요. 데몬과 클라이언트가 동일한 VANTH_HOME을 사용하는지 확인하세요.

  • 두 번째 데몬이 시작되지 않음: another vanthd already owns this VANTH_HOME. 설계상 홈 디렉토리당 하나의 데몬만 실행됩니다.

  • 작업이 running 상태에서 orphaned로 멈춤: 러너 프로세스가 종료되었습니다. logs/<job_id>.runner.log와 하트비트 임계값을 확인하세요.

  • 모니터에 차트가 없음: 작업이 AGENT_EVENT metric 또는 progress 라인을 내보내지 않고 있습니다. 추가하세요(선택 사항).

  • OpenCode 웨이크 타임아웃: 웨이크 대상의 timeout_seconds를 예상 처리 시간보다 더 길게 늘리세요.

  • 모니터에 아무것도 표시되지 않음/빈 상태: VANTH_HOME이 데몬의 홈 디렉토리를 가리키고 있는지, 그리고 그 안에 jobs.sqlite가 존재하는지 확인하세요.


개발

uv run pytest -q                 # Python suite (112 passed, 1 Linux-only skip)
uv run python -m compileall -q src tests examples
uv build                         # sdist + wheel; wheel bundles the Go monitor
go vet ./... && go test ./...    # Go: config, state, monitor

휠 빌드는 hatchling 빌드 훅(build-hooks/bundle_monitor.py)을 실행하여 호스트 플랫폼용 Go 모니터를 컴파일하고 vanth/monitor-bin/ 아래에 번들링합니다. 따라서 vanth-monitor는 런타임에 Go 툴체인이 필요하지 않습니다. 휠을 빌드할 때는 go가 PATH에 있어야 하지만, 설치하거나 실행할 때는 필요하지 않습니다. 휠은 네이티브 바이너리를 포함하므로 플랫폼 태그(py3-none-<platform>)가 지정됩니다.

릴리스 게이트 자동화는 scripts/에 있습니다:

  • scripts/chaos_matrix.py — 대규모 합성 워크로드 및 종료/재시작 매트릭스;

  • scripts/real_adapter_smoke.py — 옵트인 실시간 Codex/OpenCode 웨이크 스모크 테스트(VANTH_SMOKE_CODEX_THREAD / VANTH_SMOKE_OPENCODE_SESSION 설정);

  • scripts/generate_go_fixture.py — testdata/의 결정론적 스키마 v5 적합성 픽스처를 재생성;

  • scripts/demo_jobs.py — 모니터용 데모 작업(훈련 실행, 빠른 작업, 실패하는 작업) 시작.

제한 사항 (v1)

  • 대화형 stdin 및 job_send는 구현되지 않았습니다. 작업은 stdin이 닫힌 상태로 실행됩니다(명령어에 비대화형 플래그 사용).

  • 전달은 최소 한 번(at-least-once)입니다. 어댑터가 웨이크를 수락한 후 Vanth가 성공을 기록하기 전에 충돌이 발생하면 문서화된 명백한 모호성이 발생합니다.

  • 원격 액세스, TLS, 다중 사용자 정책, 할당량, 분산 워커 및 사용자 정의 서비스 관리자는 범위를 벗어납니다.

Related MCP Connectors

Related MCP Servers