도구 응답 · 실제 캡처
당신의 에이전트가 받는 응답.
아래 네 개는 examples/contract_sklearn의 실제
실행에서 캡처한 MCP 도구 응답입니다. 환경 종속 필드
(timestamps, git SHA, env hash, 절대 경로)만
"..."로 축약했고, 구조와 값은 변경하지
않았습니다.
JSON contracts →
호출
pcq describe-run output --json
# MCP: mcp__pcq__describe_run({"path": "output"})
응답
{
"schema_version": 1,
"run_id": "run_20260510_001107_c87d7d",
"name": "sklearn-iris-contract",
"status": "completed",
"output_dir": "...",
"cmd": "uv run python train.py",
"target_metric": "eval_acc",
"mode": "max",
"best": { "epoch": 0, "value": 1.0, "metrics": {"eval_acc": 1.0}, "checkpoint": "best.ckpt" },
"best_value": 1.0,
"best_epoch": 0,
"last": { "epoch": 0, "value": 1.0, "metrics": {"eval_acc": 1.0}, "checkpoint": "last.ckpt" },
"epochs_completed": 1,
"partial": false,
"last_updated_at": "...",
"git_sha": "...",
"dirty": true,
"python": "3.12.10",
"platform": "Darwin-arm64",
"metrics_declared": [{"name": "epoch"}, {"name": "eval_acc"}],
"artifacts": [
{"path": "config.json", "kind": "config", "sha256": "...", "size_bytes": 250, "created_at": "..."},
{"path": "metrics.json", "kind": "metrics", "sha256": "...", "size_bytes": 74, "created_at": "..."},
{"path": "model.pkl", "kind": "model", "sha256": "...", "size_bytes": 186929, "created_at": "..."},
{"path": "run_summary.json", "kind": "summary", "sha256": "...", "size_bytes": 586, "created_at": "..."}
],
"artifacts_summary": {"config": 1, "metrics": 1, "model": 1, "summary": 1},
"validation_status": "pass",
"decision_facts": {
"run_completed": true,
"validation_passed": true,
"has_target_metric": true,
"has_best": true,
"has_parent": false,
"artifact_count": 4,
"metric_count": 2,
"dirty_source": true,
"has_lockfile": true
}
}
호출
pcq compare-runs output_a output_b --json
# MCP: mcp__pcq__compare_runs({"a": "output_a", "b": "output_b"})
응답
{
"schema_version": 1,
"a_run_id": "run_20260510_001106_26fbda",
"b_run_id": "run_20260510_001107_c87d7d",
"target_metric": "eval_acc",
"mode": "min",
"best": { "a": 1.0, "b": 1.0, "delta": 0.0, "direction": "tied", "epoch_a": 0, "epoch_b": 0 },
"metric_delta": 0.0,
"metric_direction": "tied",
"last": { "a": 1.0, "b": 1.0, "delta": 0.0, "direction": "tied", "epoch_a": 0, "epoch_b": 0 },
"validation": { "a": "fail", "b": "pass", "same": false },
"artifacts": { "a_count": 6, "b_count": 4 },
"source": { "same_git_sha": true, "same_cq_yaml_sha256": true, "dirty_changed": false },
"a_status": "completed",
"b_status": "completed",
"a_is_ancestor_of_b": false,
"notes": [
"both runs picked epoch 0 as best — likely same initial weights (seed) and no improvement during training. agent should consider this a 'no learning' signal."
],
"decision_facts": {
"comparable": true,
"best_improved": false,
"best_tied": true,
"candidate_completed": true,
"candidate_validated": true,
"config_changed": false,
"has_lineage_relation": false
}
}
호출
pcq validate-run output --strictness 3 --json
# MCP: mcp__pcq__validate_run({"path": "output", "strictness": 3})
응답
{
"schema_version": 1,
"status": "pass",
"strictness": 3,
"strictness_name": "reproducible",
"checks": [
{ "id": "manifest_evidence", "status": "pass", "detail": "schema v2, 4 entries verified" },
{ "id": "metrics_well_formed", "status": "pass", "detail": "1 epoch(s) recorded" },
{ "id": "summary_metrics_consistent", "status": "pass", "detail": "run_summary best/last align with metrics history" },
{ "id": "run_record_complete", "status": "pass", "detail": "run_record schema v1, all required keys present" },
{ "id": "run_finalized", "status": "pass", "detail": "run finalized with status='completed'" },
{ "id": "source_reproducibility", "status": "pass", "detail": "git_sha=..., dirty=True" },
{ "id": "environment_reproducibility", "status": "pass", "detail": "python/platform environment evidence recorded" },
{ "id": "lockfile_evidence", "status": "pass", "detail": "lockfile recorded: uv.lock" },
{ "id": "seed_evidence", "status": "pass", "detail": "seed recorded: 42" },
{ "id": "metrics_schema_evidence", "status": "pass", "detail": "2 metric declaration(s) recorded" }
],
"blocking_count": 0,
"warning_count": 0
}
호출
pcq lineage output --json
# MCP: mcp__pcq__lineage_chain({"path": "output"})
응답
{
"schema_version": 1,
"chain": [
{
"run_id": "run_20260510_001107_c87d7d",
"output_dir": "...",
"depth": 0,
"name": "sklearn-iris-contract",
"status": "completed",
"target_metric": "eval_acc",
"best_value": 1.0
}
],
"truncated": false,
"notes": []
}
로드맵
pcq가 향하는 곳 (날짜가 아닌 방향).
명제는 그대로입니다: pcq는 학습의 수단과 경쟁하지
않는다. 앞으로의 작업은 framework 중립적인 evidence와
control 계층을 강화합니다 — 더 넓은 실제 contract 커버리지,
더 깊은 validation/lineage 사실, 에이전트 runtime을 위한 더
많은 기계 가독 표면, 그리고 CQ managed consumer와의 더 긴밀한
통합. 내장 모델, 손실, 데이터셋, framework별 어댑터 매트릭스는
의도적으로 범위 밖에 둡니다.
v4 Direction →
·
Completion Roadmap →
·
spec/ (SoT) →
·
Releases →
최근 (2026-05-12): pcq가 이제
Glama에서 검증됨 —
stdio MCP server가 빌드·시작되고 14개 tool 전체가 catalog에
노출됨.
awesome-mcp-servers
등재 PR 진행 중.
2026-05-10: spec/ foundation 완료 —
contract spec을 docs/에서 spec/로
이동, versioning + conformance 정책과 자동 export되는 JSON
Schema 추가. 1·2·3 (spec 분리 · schema versioning ·
conformance) arc의 첫 벽돌.
예제
같은 contract, 어떤 framework든.
학습 코드는 그대로 둡니다. pcq는 세 개의 호출만 필요합니다:
pcq.config(), pcq.log(...),
pcq.save_all(...). 최소 cq.yaml,
NumPy, post-run 명령 목록까지 전체는
examples/
에 있습니다.
sklearn — Iris RandomForest
어댑터 없음, Trainer 서브클래스 없음. pcq 호출 세 줄.
# train.py
import pcq
import joblib
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
cfg = pcq.config()
out = pcq.output_dir()
pcq.seed_everything(cfg.get("seed", 42))
X, y = load_iris(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2,
random_state=cfg["seed"])
model = RandomForestClassifier(n_estimators=cfg.get("n_estimators", 100))
model.fit(X_tr, y_tr)
acc = float(model.score(X_te, y_te))
pcq.log(epoch=0, eval_acc=acc)
joblib.dump(model, out / "model.pkl")
pcq.save_all(history=[{"epoch": 0, "eval_acc": acc}],
artifacts={"model": "model.pkl"})
PyTorch — 학습 루프
모델, optimizer, dataloader는 사용자 코드. pcq는 경계에 자리합니다.
# train.py
import pcq, torch
from torch import nn
cfg = pcq.config()
out = pcq.output_dir()
pcq.seed_everything(cfg.get("seed", 42))
model = nn.Linear(cfg["in_dim"], cfg["out_dim"])
opt = torch.optim.Adam(model.parameters(), lr=cfg["lr"])
history = []
for epoch in range(cfg["epochs"]):
train_loss = train_one_epoch(model, opt) # 사용자 코드
val_acc = evaluate(model) # 사용자 코드
pcq.log(epoch=epoch, train_loss=train_loss, val_acc=val_acc)
history.append({"epoch": epoch,
"train_loss": train_loss,
"val_acc": val_acc})
torch.save(model.state_dict(), out / "model.pt")
pcq.save_all(history=history, artifacts={"model": "model.pt"})