본문으로 건너뛰기

일반적인 Contexta 워크플로

이 문서는 워크스페이스에 표준 데이터가 저장된 뒤, 사용자가 자주 수행하는 조회, 비교, 리포트 생성, 진단, 리니지 추적, 추세 분석 흐름을 설명합니다.

기본 원칙은 간단합니다.

  • Contexta에서 시작하세요.
  • 하나의 로컬 워크스페이스에 연결하세요.
  • 더 세밀한 제어가 필요할 때만 스토어 또는 복구 API를 직접 사용하세요.

아직 실행 가능한 워크스페이스를 만들지 않았다면 시작하기를 먼저 완료하세요.


워크스페이스 열기


대부분의 워크플로는 하나의 워크스페이스를 여는 것으로 시작합니다.

from pathlib import Path

from contexta import Contexta
from contexta.config import UnifiedConfig, WorkspaceConfig

ctx = Contexta(
config=UnifiedConfig(
project_name="guide-proj",
workspace=WorkspaceConfig(root_path=Path(".contexta")),
)
)

프로젝트나 실험 단위마다 하나의 워크스페이스를 사용하는 것이 좋습니다. 이렇게 하면 실행 참조, 리포트, 복구 작업 등을 더 쉽게 추적할 수 있습니다.

위 코드는 Contexta 퍼사드가 .contexta/에 저장된 표준 데이터 스토어를 조회할 준비를 하는 코드입니다.

아래의 조회, 비교, 리포트, 진단, 리니지, 추세 API는 해당 워크스페이스에 이미 실행과 기록이 저장되어 있다는 전제에서 동작합니다.

문서 뒤쪽의 실제 작업의 런타임 수집 예제는 이와 반대로, 실행 중 새 관측 기록을 생성하는 흐름을 보여 줍니다.


하나의 실행 조사하기


가장 빠른 읽기 경로는 실행 스냅샷입니다.

snapshot = ctx.get_run_snapshot("run:guide-proj.demo-run")

print(snapshot.run.run_id)
print(snapshot.run.status)
print(len(snapshot.stages))
print(len(snapshot.records))
print(len(snapshot.artifacts))

ctx.get_run_snapshot(run_ref)는 하나의 실행을 기준으로 그 실행에 연결된 아래 사항들에 대한 메모를 모아 RunSnapshot으로 반환합니다.

  • 단계
  • 관측 기록
  • 아티팩트
  • 배치
  • 샘플
  • 배포
  • 관계
  • 완전성

즉, 개별 파일을 하나씩 찾는 대신 스냅샷을 통해 한 객체 안에서 이 실행 주변에 어떤 기록이 남아 있는지를 읽을 수 있습니다.

예를 들어 demo-runtrain, evaluate, package 단계와 평가 메트릭 네 개, 저장된 모델 아티팩트 하나가 있다면 출력은 다음과 같습니다.

run:guide-proj.demo-run
completed
3
4
1

이 결과는 실행이 완료되었고, 조사할 단계가 세 개 있으며, 연결된 관측 기록이 네 개이고, 결과물 하나가 저장되어 있음을 알려 줍니다.


숫자가 0이라고 해서 반드시 실패를 의미하지는 않습니다.

다만 완료된 평가 실행에 records가 하나도 없다면 성능을 검토할 근거가 부족할 수 있습니다.

이런 경우에는 diagnose_run()으로 누락 여부를 더 확인합니다.

이 흐름은 다음 질문에 답할 때 유용합니다.

  • 이 실행에서 어떤 일이 일어났나요?
  • 어떤 단계가 포함되었나요?
  • 워크스페이스에 어떤 기록이 저장되어 있나요?
  • 연결된 아티팩트는 몇 개인가요?

두 실행 비교하기


두 개 이상의 실행 결과를 조사해야 할 때는 비교 API를 사용할 수 있습니다.

comparison = ctx.compare_runs(
"run:guide-proj.demo-run",
"run:guide-proj.demo-run-v2",
)

print(comparison.summary)
print(len(comparison.stage_comparisons))

ctx.compare_runs(...)는 두 실행의 스냅샷을 읽고 단계별 메트릭 변화와 아티팩트 변화를 RunComparison에 정리합니다.

기본 비교 정책에서는 같은 메트릭에 대해 나중 실행의 값에서 이전 실행의 값을 뺀 delta를 계산하며, 0인 경우는 차이 목록에서 생략됩니다.


두 실행 모두 trainevaluate 단계를 가지고 있고 evaluate/accuracy 값만 달라졌다면 요약 출력은 다음과 같습니다.

Compared demo-run and demo-run-v2: 2 stage rows, 1 metric deltas, 0 artifact changes.
2

stage rows는 비교 가능한 단계의 수이고, metric deltas는 실제로 변화가 보고된 메트릭의 수입니다.

따라서 이 출력은 두 실행이 같은 두 단계 구조로 비교되었고, 적어도 하나의 측정값이 바뀌었다는 뜻입니다.


다만 이 값만으로 개선인지 악화인지 판단할 수는 없습니다.

메트릭의 방향을 알고, comparison.stage_comparisons[*].metric_deltas에서 이전 값, 이후 값, delta를 함께 읽어야 합니다.

여러 후보 실행 중 특정 메트릭 기준으로 가장 나은 실행을 선택하고 싶다면 다음 API를 사용할 수 있습니다.

best = ctx.select_best_run(
[
"run:guide-proj.demo-run",
"run:guide-proj.demo-run-v2",
],
metric_key="accuracy",
stage_name="evaluate",
higher_is_better=True,
)

print(best)

ctx.select_best_run(...)은 후보 실행들에서 요청한 메트릭을 찾고, higher_is_better=True이면 최대값을 가진 실행 참조를 반환합니다.


이 코드의 accuracyevaluate 단계에 연결된 메트릭이므로 stage_name="evaluate"로 비교 대상을 지정합니다.

예를 들어 평가 정확도가 0.88에서 0.91로 올라갔다면 출력은 다음과 같습니다.

run:guide-proj.demo-run-v2

이 반환값은 “정확도 하나를 기준으로 선택된 실행”을 의미합니다. 단, 새 실행이 모든 면에서 더 낫다는 뜻은 아닙니다.

아티팩트 변화, 수집 누락, 지연 시간처럼 선택 기준에 포함되지 않은 정보는 compare_runs()diagnose_run()으로 함께 확인해야 합니다.

요청한 메트릭이 후보 실행에 저장되어 있지 않으면 반환값은 None입니다.


비교 기능은 다음 내용을 확인할 때 사용합니다.

  • 메트릭 변화
  • 단계 수준의 차이
  • 리포트 수준의 차이
  • 특정 메트릭 기준의 최적 실행

리포트 만들기


데이터가 표준 형식으로 저장되어 있으면 리포트도 같은 워크스페이스에서 만들 수 있습니다.

snapshot_report = ctx.build_snapshot_report("run:guide-proj.demo-run")
compare_report = ctx.build_run_report(
"run:guide-proj.demo-run",
"run:guide-proj.demo-run-v2",
)
project_report = ctx.build_project_summary_report("guide-proj")

생성된 리포트는 후속 작업에 맞는 형식으로 변환할 수 있습니다.

markdown_text = snapshot_report.to_markdown()
html_text = snapshot_report.to_html()
json_payload = snapshot_report.to_json()

세 API는 각각 하나의 실행 스냅샷, 두 실행의 비교, 프로젝트의 실행 목록을 사람이 검토하기 쉬운 ReportDocument로 변환합니다. build_snapshot_report()는 내부에서 스냅샷과 진단 결과를 함께 읽어 실행 요약, 단계, 아티팩트, 진단, 완전성 메모 등의 섹션을 만듭니다.


리포트는 원본 관측 기록을 대체하는 저장 형식이 아닙니다. 이미 저장된 기록을 사람이 읽기 쉽게 배열한 파생 결과물입니다.

예를 들어 snapshot_report.to_markdown()의 앞부분은 다음과 같은 모양이 됩니다.

# Run Snapshot Report: run:guide-proj.demo-run

## Run Summary
run_id: run:guide-proj.demo-run
name: demo-run
status: completed
project: guide-proj

## Stages
train: status=completed, stage_id=stage:guide-proj.demo-run.train
evaluate: status=completed, stage_id=stage:guide-proj.demo-run.evaluate

리포트 API의 각 메소드는 다음 상황에서 사용 가능합니다.

  • to_markdown()은 리뷰나 문서 공유에 적합합니다.
  • to_html()은 웹 화면 렌더링에 적합합니다.
  • to_json()은 다른 프로그램에서 섹션과 기록 링크를 후처리할 때 적합합니다.

리포트에 Diagnostics 또는 Completeness Notes 섹션이 있다면 성능 숫자만 전달하기 전에 기록이 충분한지도 함께 검토할 수 있습니다.


리포트는 다음 용도로 사용할 수 있습니다.

  • 검토
  • 공유
  • 아카이브
  • HTML 렌더링
  • 다른 워크플로와의 연결

실행 진단하기


부분적으로만 수집되었거나, 기능 저하 상태가 있거나, 기록 구성이 의심스러워 보이는 실행을 확인할 때는 진단 기능을 사용합니다.

diagnostics = ctx.diagnose_run("run:guide-proj.demo-run")

for issue in diagnostics.issues:
print(issue.severity, issue.code, issue.summary)

ctx.diagnose_run(run_ref)는 저장된 데이터를 읽어 완료된 단계에 대한 여러 조사 신호를 DiagnosticsResult로 반환합니다.

issue는 중요도인 severity, 문제 유형인 code, 사람이 읽는 설명인 summary, 그리고 문제가 연결된 subject_ref를 가집니다.


예를 들어 package 단계가 완료로 저장되었지만 해당 단계에 메트릭이 없다면 다음과 같은 진단이 나올 수 있습니다.

info missing_stage_metrics completed stage package has no metric records

이 출력은 모델 점수가 낮다는 뜻이 아닙니다. package 단계에 대해 기대할 만한 측정 기록이 남아 있지 않다는 뜻입니다.

issues가 비어 있다면 현재 진단 규칙으로 발견된 문제가 없다는 의미이지, 모델 품질이나 운영 안정성이 자동으로 검증되었다는 뜻은 아닙니다.


진단 기능은 다음 질문에 답할 때 사용합니다.

  • 어떤 부분이 누락되었나요?
  • 어떤 부분이 기능 저하 상태로 보이나요?
  • 어떤 기록이 서로 일관되지 않나요?
  • 어떤 이슈를 먼저 조사해야 하나요?

리니지 추적하기


리니지는 단일 실행을 넘어 엔티티 사이의 관계를 파악해야 할 때 사용합니다.

traversal = ctx.traverse_lineage(
"artifact:guide-proj.demo-run.model",
direction="outbound",
max_depth=3,
)

print(len(traversal.edges))
print(len(traversal.visited_refs))

ctx.traverse_lineage(subject_ref, direction, max_depth)는 저장된 관계 레코드를 따라가며 LineageTraversal을 만듭니다.

이 예제는 모델 아티팩트를 시작점으로 삼고, 그 아티팩트가 가리키는 하위 대상만 최대 세 단계까지 탐색합니다.

edges에는 관계 유형과 시작/도착 참조가 들어가고, visited_refs에는 탐색 중 방문한 실행, 아티팩트, 리포트, 배포 등의 참조가 들어갑니다.


모델 아티팩트가 배포 엔트리와 이후 리포트로 이어져 있다면 개수 출력은 다음처럼 나타날 수 있습니다.

2
3

여기서는 루트 아티팩트에서 출발해 관계 두 개를 따라 세 개의 대상을 확인했다는 뜻입니다.

단, direction="outbound"은 저장된 관계의 방향을 따른 결과이며, 생성 원인을 찾을 때는 inbound 또는 both가 더 적절할 수 있습니다.

관계가 저장되지 않았다면 edges는 비어 있습니다. 이는 아티팩트가 없다는 뜻이 아니라, 연결 정보가 기록되어 있지 않다는 뜻입니다.


리니지는 다음 질문에 답할 때 사용합니다.

  • 이 아티팩트는 어디에서 왔나요?
  • 이 결과에 의존하는 대상은 무엇인가요?
  • 이 대상의 상위(upstream) 또는 하위(downstream)에는 무엇이 있나요?

메트릭 추세 분석하기


단일 실행 비교가 아니라 여러 실행에 걸친 변화를 보고 싶다면 메트릭 추세를 조회할 수 있습니다.

from contexta.interpretation import RunListQuery

trend = ctx.get_metric_trend(
"accuracy",
project_name="guide-proj",
stage_name="evaluate",
query=RunListQuery(sort_by="started_at"),
)

print("Accuracy trend:")
for point in trend.points:
print(f"- {point.run_name}: {point.value:.3f}")

if len(trend.points) >= 2:
first = trend.points[0]
latest = trend.points[-1]
peak = max(trend.points, key=lambda point: point.value)
delta = latest.value - first.value

print(f"Net change: {delta:+.3f} ({first.value:.3f} -> {latest.value:.3f})")
print(f"Best observed run: {peak.run_name} ({peak.value:.3f})")

if latest.run_id != peak.run_id:
print("Latest run is below the observed peak.")

ctx.get_metric_trend(...)는 프로젝트의 실행들을 읽고, 각 실행에서 지정한 메트릭의 대표값을 하나씩 골라 MetricTrend로 반환합니다.

여기서는 stage_name="evaluate"를 지정했기 때문에 각 실행의 평가 단계에 저장된 accuracy만 분석 대상으로 삼습니다.

또한 RunListQuery(sort_by="started_at")를 전달해 trend.points가 실행 시작 시각 순서로 놓이도록 합니다.

따라서 첫 점은 기준 실행, 마지막 점은 가장 최근 실행으로 해석할 수 있습니다.


기본 설정에서는 각 실행에서 가장 최근에 기록된 해당 메트릭 값이 점 하나가 됩니다. 메트릭이 없는 실행은 점으로 만들지 않고 완전성 메모에 남깁니다.

예를 들어 세 번의 평가 실행이 0.881, 0.906, 0.897의 정확도를 남겼다면 출력은 다음처럼 보입니다.

Accuracy trend:
- demo-run: 0.881
- demo-run-v2: 0.906
- demo-run-v3: 0.897
Net change: +0.016 (0.881 -> 0.897)
Best observed run: demo-run-v2 (0.906)
Latest run is below the observed peak.

이 출력에서는 최초 실행보다 최신 실행의 정확도가 0.016 높으므로 전체 기간 기준으로는 성능이 개선되었습니다.

하지만 최고 관측값은 중간 실행인 demo-run-v2에서 나왔고, 최신 실행은 그 최고점보다 낮습니다.

즉, '처음보다 좋아졌다'와 '현재가 가장 좋다'는 서로 다른 결론이며, 간단한 추세 분석만으로도 이 차이를 발견할 수 있습니다.


또한, trend.points에는 실행 참조, 측정값, 기록 시각이 함께 들어 있습니다.

그러므로 하락이 시작된 실행을 찾은 뒤 compare_runs()diagnose_run()으로 그 실행의 변경 사항과 기록 상태를 더 조사할 수 있습니다.


만약 점의 개수가 프로젝트의 실행 수보다 적다면 일부 실행에는 해당 메트릭이 없었을 수 있습니다.

값이 상승하거나 하락했다는 결론을 내리기 전에 trend.completeness_notes도 확인하세요.


추세 분석은 다음 상황에 유용합니다.

  • 여러 실행에 걸친 메트릭 드리프트 확인
  • 기준 실행 대비 최신 실행의 순변화 확인
  • 최고 성능 이후 회귀가 발생했는지 확인
  • 더 깊은 비교나 진단이 필요한 실행 구간 찾기

실제 작업의 런타임 수집


런타임 수집은 관측 대상 작업에서 신호가 실제로 생성될 때 가장 의미가 있습니다.

"""Train a real regression model and capture its measured evidence."""

import pickle
from pathlib import Path

from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split

from contexta import Contexta
from contexta.capture import LocalJsonlSink


features, targets = load_diabetes(return_X_y=True)
train_x, test_x, train_y, test_y = train_test_split(
features, targets, test_size=0.2, random_state=42
)

workspace = Path(".contexta")
ctx = Contexta(workspace=str(workspace), config={"project_name": "diabetes-regression"})
local_sink = next(sink for sink in ctx.sinks if isinstance(sink, LocalJsonlSink))
model = LinearRegression()

with ctx.run("linear-regression", dataset_ref="dataset:sklearn.diabetes") as run:
run.event(
"dataset.loaded",
message="Loaded the scikit-learn diabetes dataset",
attributes={"rows": len(features), "features": features.shape[1]},
)
with run.stage("train"):
model.fit(train_x, train_y)

with run.stage("evaluate") as stage:
predictions = model.predict(test_x)
r2 = r2_score(test_y, predictions)
mae = mean_absolute_error(test_y, predictions)
stage.metric("r2", r2, unit="ratio")
stage.metric("mae", mae)

model_path = workspace / "models" / "linear-regression.pkl"
model_path.parent.mkdir(parents=True, exist_ok=True)
model_path.write_bytes(pickle.dumps(model))
run.register_artifact("model", str(model_path), attributes={"format": "pickle"})

records_path = local_sink.file_path_for("RECORD").relative_to(Path.cwd())

print(f"Captured run: {run.ref}")
print(f"Measured r2: {r2:.3f}; mae: {mae:.3f}")
print(f"Records: {records_path.as_posix()}")
print(f"Model artifact: {model_path.as_posix()}")

이 예제들은 조회 예제와 달리, 실제 모델 학습이나 API 형태의 요청을 수행하면서 Contexta 런타임 수집 API로 새 관측 기록을 남깁니다.

예제관측하는 실제 작업사용하는 핵심 API결과를 읽는 방법
머신러닝선형회귀 모델
학습/평가
ctx.run(...), 단계 스코프, 메트릭 기록계산된 지표가 어떤 실행과 평가 단계에서
나왔는지 확인
딥러닝이미지 분류 모델
학습/평가
실행/단계 스코프, 손실 및 정확도 기록,
체크포인트 아티팩트 등록
학습 결과와 체크포인트가 같은 실행 기록으로
연결되는지 확인
LLMAPI 호출 형태의
로컬 평가
실행/단계 스코프, latency/usage/평가 기록응답 품질뿐 아니라 토큰 사용량과 지연 시간도
함께 조사

각 예제를 실행한 후 터미널에는 수집한 실행 참조, 측정값, .contexta/cache/capture/record.jsonl 경로가 표시됩니다.

또한, 모델 또는 checkpoint를 만드는 예제는 저장된 결과물 경로도 함께 표시합니다.


record.jsonl에는 단순한 결과 숫자 뿐만 아니라 그 숫자가 어느 실행과 어느 단계에서 생성되었는지 연결하는 envelope 정보도 함께 저장됩니다.

따라서 이후에는 “정확도가 얼마였나”뿐 아니라 “어떤 학습/평가 실행에서 그 정확도가 생겼나”까지 확인할 수 있습니다.


런타임 수집은 다음 상황에 사용합니다.

  • 애플리케이션 코드 안에서 실시간으로 관측 정보를 수집할 때
  • 실행 스코프를 인식하는 이벤트와 메트릭을 남길 때
  • 실행 생명주기와 수집 동작을 하나의 제품 인터페이스에서 다룰 때

관련 문서