XGEN Harness — 5 트랙 확장: Stage 실동작 스모크 + custom_tools Auto 토글 + Ingress preserve_order + browser_sessions 리소스 타입 + Credential Vault/secret_ref/allowed_domains
Project description
한 줄 요약
워크플로우를 "짜는 것" 이 아니라 "말로 설명하는 것" 으로 바꾼 에이전트 실행기. 사용자는 "뭘 하고 싶다" 만 선언, 하네스는 LLM 이 직접 어떤 Stage 를 쓸지 · 어떤 Strategy 로 · 몇 번 반복할지 · 어떤 오케스트레이션 패턴으로 돌릴지까지 전부 스스로 결정.
현재 (
v0.16.9) — 11 Stage +s00_harnessAuto 통제탑 · 재귀적 자율주행 4레벨 · 자가증식 도구 루프 · 드롭-인 Stage 플러그인 · entry_points 12 그룹 · DAG 오케스트레이터 · 컴파일러(→ pip wheel).
30 초 데모
from xgen_harness import Pipeline, PipelineState, HarnessConfig, EventEmitter
from xgen_harness.core.execution_context import set_execution_context
set_execution_context(api_key="sk-...", provider="openai", model="gpt-4o-mini")
# "Auto" 모드 — 무엇을 할지만 선언. 하네스가 LLM 한테 파이프라인을 조립하게 시킴
config = HarnessConfig(
provider="openai", model="gpt-4o-mini",
harness_mode="autonomous", # ← 이 한 줄
capabilities=["retrieval.web_search"],
)
pipeline = Pipeline.from_config(config, EventEmitter())
state = PipelineState(user_input="오늘 한국 날씨 요약해줘")
await pipeline.run(state)
print(state.final_output)
실제로 벌어지는 일 — s00_harness 가 LLM 한테 HarnessPlan 을 만들게 시키고, Plan 에 따라 필요한 Stage 만 실행, 반복 횟수도 LLM 이 결정, 매 iter 재계획(replan). 첫 답에 만족하면 done=true 로 즉시 종료.
[요청] "오늘 한국 날씨 요약해줘"
↓
[s00_harness Auto] LLM Planner — catalog 를 보고 Plan 결정
Plan(chosen=[s01, s03, s04, s06, s07], max_iter=2, orchestrator_hint="iterative", done=false)
↓
[선택된 Stage 만 실행] s01 → s03 → s04 → s06 → [main_call] → s07 → ...
↓
[s00 재호출 iter#2] previous_results 보고 다음 Plan 결정
done=true → 종료
🆕 크게 바뀐 점 (v0.11.x → v0.16.x)
| 구분 | 이전 (v0.11.x) | 지금 (v0.16.x) |
|---|---|---|
| 파이프라인 | 12 Stage 고정 파이프라인 | 11 Stage + s00_harness Auto 통제탑 (Planner) |
| 실행 조립 | 사용자가 stage_params / active_strategies 다 설정 | LLM 이 catalog 보고 자율 조립 (Stage / Strategy / params / max_iter / orchestrator) |
| 본문 LLM 호출 | s07_llm Stage 가 담당 |
s00_harness 가 단일 Provider 로 통제, s07_act 직전에 main_call 주입 (TransportStrategy 패턴) |
| 반복 | HarnessConfig.max_iterations=10 고정 |
Plan.max_iterations 을 LLM 이 1~50 범위에서 결정 |
| 오케스트레이션 | 단일 agentic loop | Plan.orchestrator_hint: linear / iterative / plan_execute / react / dag — 런타임에 동적 분기 (v0.16.8 OrchestratorSpec flag 기반) |
| 도구 | 사용자가 custom_tools 로 나열 |
Tool Synthesis — LLM 이 도구 생성 → Sandbox 테스트 통과 → 자동 레지스트리 합류 → 다음 Plan 이 재사용 |
| 외부 확장 | entry_points 9 그룹 | 12 그룹 (+ orchestrators / phases / node_plugins / tool_sources) + 파일시스템 자동 스캔 + NOM 단일 IR |
| Stage 이름 의존 | Pipeline 이 s07_llm / s08_judge 리터럴을 코드에 박아둠 |
Stage.role 역할 선언 (orchestrator_planner / main_actor / scorer) — Pipeline 리터럴 0, 리네이밍 무서워할 필요 없음 |
핵심 릴리즈 요약
| 버전 | 날짜 | 핵심 |
|---|---|---|
| v0.12.0 | 04-22 | 🎯 REAL HARNESS Phase 1 — s00_harness Planner + self-describing catalog + 13 Stage 디렉토리화 (파일 하나 → 디렉토리 하나) |
| v0.13.0 | 04-22 | Iterative Planning — 단일 Provider 1 인스턴스 재활용 + 매 iter s00 재호출(replan) + Plan.done 조기 종료 + previous_results 자동 주입 |
| v0.14.0 | 04-22 | 🎯 s07_llm 삭제 + 번호 시프트 — s00_harness 통제탑 승격, TransportStrategy (streaming/batch), harness_mode 3-way (autonomous/selected/off) |
| v0.15.0 | 04-22 | 🎯 재귀적 자율주행 완성 — Plan.max_iterations + Plan.orchestrator_hint + OrchestratorRegistry 자동 연동 + display_name="Auto" |
| v0.15.1~3 | 04-22 | 🎯 자동 연동성 9축 감사 — Phase / Transport / Capability / Provider / Tool entry_points 갭 제거 + 파일시스템 자동 스캔 + catalog source_file 노출 |
| v0.16.0 | 04-22 | 🚀 자가증식 4축 전면 실증 — Sandbox / NOMGraph / NodePlugin / ToolSynthesis PASS, LLM 생성 도구가 Sandbox 통과 후 자동 합류 |
| v0.16.1 | 04-22 | Sandbox rlimit 하드닝 (CPU/메모리/fd/파일크기/코어덤프) + LocalManifest 통합 스키마 + synthesis 하드코딩 제거 |
| v0.16.3~4 | 04-22 | XGEN_HARNESS_PRELOAD_MANIFEST env 로 LocalManifest 자동 주입 + s04_tool 브릿지 (전역 tool_sources → LLM tool_definitions) |
| v0.16.6 | 04-22 | 🎯 Pipeline Role 체계 — Stage 이름 리터럴 12 → 0. role="orchestrator_planner" 선언으로 Pipeline 수정 0 |
| v0.16.7~8 | 04-23 | hot-fix — off 모드 본문 호출 복원 + OrchestratorSpec flag 기반 분기 (리터럴 0 주장 보증) |
벤치 근거 (v0.11.x 누적 n=3 replay): assort 키워드 +332%, latency −37% · krra 문서 인용 2.10/turn · cascade std −½ — 상세는 bench/INFINITE-LOG.md.
실행 모드 3종 (harness_mode)
┌──────────────────────┬────────────────────────────────────────────────────────────┐
│ autonomous (기본) │ LLM Auto — s00_harness 가 매 iter Plan 수립, 재계획 │
│ │ · max_iterations / orchestrator 도 LLM 이 선택 │
│ │ · previous_results 주입, done=true 시 조기 종료 │
├──────────────────────┼────────────────────────────────────────────────────────────┤
│ selected │ 사용자가 UI 에서 핀한 Stage / Strategy / params 그대로 │
│ │ · Plan 생성 skip, 사용자 설정 = 진실의 소스 │
├──────────────────────┼────────────────────────────────────────────────────────────┤
│ off │ s00_harness bypass — 11 Stage 전부 순차 (레거시 호환) │
│ │ · 본문 LLM 호출은 여전히 s00 내부 main_call 로 수행 │
└──────────────────────┴────────────────────────────────────────────────────────────┘
HarnessConfig(harness_mode="autonomous") # 기본
HarnessConfig(harness_mode="selected") # UI 핀 그대로
HarnessConfig(harness_mode="off") # 레거시 fallback
재귀적 자율주행 — 4 Layer
사용자가 묻는 1 문장 만 있으면 LLM 이 아래 4 층을 전부 스스로 내려간다.
L1 — 구조 파악 catalog["stages"] / ["strategies"] / ["orchestrators"] /
["providers"] / ["phases"] / ["capabilities"] / ["tools"]
전부 런타임 레지스트리에서 생성 (리터럴 0)
↓
L2 — Stage 선택 Plan.chosen : list[stage_id]
"이 요청엔 s03(Prompt) + s06(RAG) + s07(Act) 만 필요"
↓
L3 — Strategy 선택 Plan.strategies : {stage_id: strategy_name}
"s06 은 cascade 압축, s07 의 transport 는 streaming"
↓
L4 — 파라미터 / 도구 Plan.params : {stage_id: {k: v}}
"s04_tool 의 force_tool_use=True, s06_context.cascade_l3=80"
↓
+ 오케스트레이션 Plan.orchestrator_hint : linear / iterative / plan_execute /
react / dag / <외부 등록명>
+ 반복 횟수 Plan.max_iterations : 1 ~ 50
+ 조기 종료 Plan.done : 만족하면 True
Plan 은 매 iter 재생성(replan) — Planner 가 previous_results (직전 iter 의 answer 미리보기 / tool_calls / validation score / token 사용량) 를 보고 다음 조각을 재조립.
11 Stage + s00_harness Auto 통제탑
Phase A: 준비 (ingress, order ≤4)
s00_harness ─ Auto ← LLM Planner (role=orchestrator_planner)
s01_input ─ Input · Provider / model / API 키
s02_history ─ History · 이전 대화 로드
s03_prompt ─ Prompt · 시스템 프롬프트 조립 + RAG + Citation
s04_tool ─ Tool · MCP / Gallery / RAG 도구 수집
Phase B: 에이전트 루프 (loop, order ≤9) ← 매 iter 시작에 s00 재호출 (replan)
s05_strategy ─ Strategy · CoT / ReAct / none / auto
s06_context ─ Context · RAG + 5-Level Cascade Compression
[main_call] ← s00 이 s07_act 직전에 주입 (TransportStrategy)
s07_act ─ Act (role=main_actor) · LLM tool_use 디스패치
s08_judge ─ Judge (role=scorer) · 품질 평가 (LLM Judge / rule / none)
s09_decide ─ Decide · Guard 체인 + 루프 판단 (DecideStrategy)
↓
계속 → s00 재계획 → s05 로 루프
완료 → Phase C
Phase C: 마무리 (egress, order ≥10)
s10_save ─ Save · DB 저장
s11_finalize ─ Finalize · 메트릭스 + 포맷팅
v0.14.0 — 과거
s07_llmStage 는 삭제되고 본문 LLM 호출은s00_harness.main_call()로 이관. 전체 파이프라인에서 LLM 인스턴스는 1개 (state.provider 재활용).s08_execute→s07_act로 번호 시프트.v0.16.6 Role 체계 — Pipeline 은 Stage 이름을 몰라도 된다. 외부 기여자가
role="orchestrator_planner"를 선언한 자기 Planner 로 s00_harness 를 교체하면 Pipeline 코드 수정 0.
Stage별 기능
| # | Stage ID | 표시 | 하는 일 | 대표 Strategy |
|---|---|---|---|---|
| 0 | s00_harness |
Auto | Planner — catalog → HarnessPlan LLM 호출 → 매 iter replan. 본문 main_call 호스팅 |
transport: streaming / batch |
| 1 | s01_input |
Input | Provider 생성, API 키 해석 | default |
| 2 | s02_history |
History | 이전 대화 이력 로드 | default / embedding_search |
| 3 | s03_prompt |
Prompt | 섹션 기반 프롬프트 + RAG + Citation | section_priority / simple |
| 4 | s04_tool |
Tool | MCP / Gallery / RAG / 전역 tool_sources 수집 + force_tool_use |
progressive_3level / eager_load |
| 5 | s05_strategy |
Strategy | 실행 계획 (CoT / ReAct / none) | auto |
| 6 | s06_context |
Context | RAG 검색 + 5-Level Cascade Compression | 6종: token_budget · sliding_window · microcompact · context_collapse_overlay · autocompact_llm · cascade |
| 7 | s07_act |
Act (main_actor) | LLM tool_use 디스패치 | sequential / parallel |
| 8 | s08_judge |
Judge (scorer) | LLM Judge / Rule-based / None | llm_judge / rule_based / none |
| 9 | s09_decide |
Decide | Guard 체인 + 루프 판단 (DecideStrategy.decide()) |
threshold / always_pass |
| 10 | s10_save |
Save | 실행 이력 DB 저장 | default / noop |
| 11 | s11_finalize |
Finalize | 메트릭스 + 포맷팅 | default / format_json |
구 ID 호환 —
STAGE_ID_ALIASES가 저장된 워크플로우의s07_llm/s02_memory등 구 id 를 자동 정규화. 이식측 수정 없이 계속 동작.
자가증식 4 축 (v0.16.0+)
하네스가 런타임에 자기 자신을 확장 하는 4 축. 각 축이 단일 진실 스키마(LocalManifest) 로 연결.
┌──────────────────┐ Subprocess 격리 실행. -I isolated + rlimit 5종
│ 1. Sandbox │ CPU sec · address_space_mb · max_open_files · max_file_size · no_core_dump
│ │ POSIX preexec_fn 강제 (parent 영향 0)
└──────────────────┘
↓ 테스트 통과한 도구만 등록
┌──────────────────┐ Stage / Strategy / Tool / MCP / legacy Node 를
│ 2. NOM │ 단일 IR (NOMNode/NOMGraph) 로 통일.
│ (Node Object │ snapshot_current_registry_as_nom() → 54 노드 그래프
│ Model) │ 갤러리 · 샌드박스 · 컴파일러 공통 입력
└──────────────────┘
↑ 매니페스트 주입
┌──────────────────┐ 외부 pip 패키지가 자기 노드를 `NodePluginManifest` 로 선언.
│ 3. Node Plugin │ 3 경로: Python dict / YAML·JSON 파일 / entry_points
│ │ (xgen_harness.node_plugins). 엔진 무침범
└──────────────────┘
↑ LLM 이 생성한 도구
┌──────────────────┐ `SynthesizedTool` + `ToolTestCase` + `SynthesizedToolSource`
│ 4. Tool │ test_synthesized_tool() 이 Sandbox 로 전수 검증 →
│ Synthesis │ synthesize_and_register() 가 레지스트리 합류 →
│ │ 다음 Plan 이 재사용 (자가 증식 루프)
└──────────────────┘
Tool Synthesis 예시
from xgen_harness.tools.synthesis import (
SynthesizedTool, ToolTestCase, synthesize_and_register,
)
tool = SynthesizedTool(
name="slugify", description="한글/영문 문자열을 URL slug 로",
code='def tool(args): return {"slug": args["text"].replace(" ", "-").lower()}',
test_cases=[
ToolTestCase(input={"text": "Harness Auto Weave"},
expected={"slug": "harness-auto-weave"}),
],
)
# Sandbox 에서 test_cases 전수 통과 → 자동 register_tool_source → 다음 Plan 이 발견
await synthesize_and_register(tool)
LocalManifest 자동 주입 (v0.16.3)
# 다른 프로세스에서 synthesize 한 결과를 env 한 줄로 주입
export XGEN_HARNESS_PRELOAD_MANIFEST=/path/to/manifest.json
python -m xgen_harness.cli # get_tool_sources() 첫 조회 시 idempotent 로드
compile.local_manifest 가 synthesis ↔ NodePlugin ↔ gallery 3 곳을 동일 포맷으로 묶어 drift 원천 차단.
확장 통로 — 12 entry_points 그룹
외부 패키지가 자기 pyproject.toml 한 줄 추가하면 하네스 부팅 시 자동 합류. 라이브러리 본체 수정 0.
[project.entry-points."xgen_harness.stages"] # ① Stage 추가 / swap
[project.entry-points."xgen_harness.strategies"] # ② Stage 내부 알고리즘
[project.entry-points."xgen_harness.orchestrators"] # ③ 실행 패턴 (v0.15.0)
[project.entry-points."xgen_harness.phases"] # ④ Phase 경계 (v0.15.1)
[project.entry-points."xgen_harness.providers"] # ⑤ LLM 프로바이더
[project.entry-points."xgen_harness.capabilities"] # ⑥ 선언형 도구 바인딩
[project.entry-points."xgen_harness.tool_sources"] # ⑦ 외부 도구 디스패치 (v0.15.2)
[project.entry-points."xgen_harness.node_adapters"] # ⑧ 노드 카테고리 → tool_def
[project.entry-points."xgen_harness.option_sources"] # ⑨ UI 셀렉터 데이터
[project.entry-points."xgen_harness.fan_out_strategies"] # ⑩ 멀티에이전트 분기
[project.entry-points."xgen_harness.evaluation_criteria"] # ⑪ s08 평가 기준
[project.entry-points."xgen_harness.node_plugins"] # ⑫ Node Plugin (v0.16.0)
파일시스템 자동 스캔 (v0.15.2)
stages/sNN_xxx/ 디렉토리만 떨어뜨리면 fs_scanner 가 자동 import + register. entry_points 도 필요 없음.
my_package/
└─ stages/
└─ s04_tool_lotte/ ← 디렉토리 이름이 자동 발견 키
├─ __init__.py ← Stage 서브클래스 export
└─ strategies/
└─ progressive/
└─ custom.py ← register_strategy(s04_tool, progressive, custom, …) 자동
catalog["stages"][i]["source_file"] / strategies[j]["source_file"] 로 LLM 이 "이 Stage 는 어디 파일에 있는지" 직접 읽어서 판단 가능 (재귀적 자율주행 L1).
레지스트리 API — 코드로 붙이기
from xgen_harness import (
register_stage, register_provider, register_service, register_tool_source,
register_xgen_node_resolver,
)
from xgen_harness.core.strategy_resolver import register_strategy
from xgen_harness.core.orchestrator_registry import register_orchestrator
from xgen_harness.core.phase_registry import register_phase
from xgen_harness.capabilities import register_capability
register_stage("s99_custom", "default", MyStage)
register_strategy("s08_judge", "evaluation", "strict", StrictJudge)
register_orchestrator("my_pattern", description="...", terminates_after_first=True)
register_phase("post_egress", upper_order=9999)
register_provider("my_llm", MyLLMProvider)
register_service("documents", "http://my-rag:8000")
register_tool_source(my_tool_source)
Stage 역할 교체 (v0.16.6)
Pipeline 은 역할 이름 만 안다. 이름 리네이밍에 독립.
from xgen_harness import Stage
class MyPlanner(Stage):
@property
def stage_id(self) -> str: return "my_planner"
@property
def role(self) -> str: return "orchestrator_planner" # ← s00_harness 자리에 꽂힘
@property
def order(self) -> int: return 0
...
register_stage("my_planner", "default", MyPlanner)
# Pipeline 수정 0 — role 기반 검색이 새 Planner 를 자동 발견
3 가지 도구 바인딩 경로 (Capability 시스템)
① 선언 바인딩 ② 발견 바인딩 ③ 자동 발행
(capabilities 필드) (s05 natural intent) (Adapter → Registry)
↓ ↓ ↓
config.capabilities = "뉴스 찾아서 요약" 워크플로우 노드
["retrieval.web_search"] ↓ → MCP/API/DB/RAG
↓ Matcher 매칭 → publish_capabilities()
s04_tool 바인딩 s04 바인딩 → ①② 사용 가능
실행 중 LLM 이 빠뜨린 필수 파라미터는 ParameterResolver 가
provided → context → llm_infer → default 순으로 자동 채움.
빠른 시작
1. 독립 실행 (어댑터 없이)
from xgen_harness import Pipeline, PipelineState, HarnessConfig, EventEmitter
from xgen_harness.core.execution_context import set_execution_context
set_execution_context(api_key="sk-...", provider="openai", model="gpt-4o-mini")
config = HarnessConfig(
provider="openai", model="gpt-4o-mini",
harness_mode="autonomous",
capabilities=["retrieval.web_search"],
)
pipeline = Pipeline.from_config(config, EventEmitter())
state = PipelineState(user_input="오늘 한국 날씨 알려줘")
await pipeline.run(state)
print(state.final_output)
2. xgen-workflow 연동
from xgen_harness.adapters.xgen import XgenAdapter
adapter = XgenAdapter(db_manager=db_manager)
async for event in adapter.execute(
workflow_data, input_data, user_id=user_id,
user_is_admin=is_admin, user_is_superuser=is_superuser, # v0.11.26+
):
yield event # xgen SSE 포맷 (그대로 프론트에 전달 가능)
3. 워크플로우 → wheel 컴파일 (v0.10.0+)
import xgen_harness as xh
result = xh.compile_workflow(
harness_config=config,
workflow_data={"nodes": [...], "edges": [...]},
gallery_name="team_q_and_a",
gallery_version="0.1.0",
out_dir="./dist",
)
# → dist/xgen_gallery_team_q_and_a-0.1.0-py3-none-any.whl
# 받는 쪽은 한 줄로 설치 + 실행
# $ pip install xgen-gallery-team-q-and-a
# >>> from xgen_gallery_team_q_and_a import arun
# >>> await arun("안녕?")
${OPENAI_API_KEY} 같은 참조는 자동 스캔 → env.example 생성. 외부 SDK 의존성은 register_dependency_rule() 로 자동 wheel 주입. 폐쇄망(pip install --no-index --find-links wheelhouse/) 시나리오 검증됨.
4. Strategy Variant — 디폴트 건들지 않고 "복사해서 v2" (v0.10.4)
config = HarnessConfig(
provider="openai",
strategy_variants={
"s06_context": [
{
"name": "my_compactor_v2",
"base": "token_budget", # 디폴트 impl 을 복사
"params": {"budget_ratio": 0.5}, # 파라미터만 교체
"label": "엄격 압축",
}
]
},
active_strategies={"s06_context": "my_compactor_v2"},
)
5. 구성 저장 / 로드
builder = (PipelineBuilder()
.with_provider("openai", model="gpt-4o-mini")
.with_rag("docs", top_k=5)
.with_artifact("s04_tool", "lotte")
.disable("s05_strategy"))
builder.save("./harness.json")
loaded = PipelineBuilder.load("./harness.json")
pipeline = loaded.with_api_key("sk-...").build()
config = HarnessConfig(provider="openai", capabilities=["retrieval.web_search"])
config.save("./config.json")
config = HarnessConfig.load("./config.json")
dataclasses.fields() 자동 순회 → 새 필드 추가해도 직렬화 코드 수정 불필요. api_key 등 민감/런타임 객체는 자동 제외.
3 층 아키텍처 — Stage · Strategy · Resource
┌──────────────────────────────────┐
┌──────────────────▶│ Resource │
│ │ MCP · RAG · DB · API · Gallery │
│ │ · Capability · Tool Synthesis │
│ └──────────────────────────────────┘
│ ▲
│ │ register_service()
│ │ register_tool_source()
│ ┌────────────────────────────┴──────────────────┐
│ │ Strategy │
│ │ Transport(streaming/batch) · ExpBackoff · │
│ │ AnthropicCache · LLMJudge · RuleBased · │
│ │ Progressive3Level · Threshold · AlwaysPass · │
│ │ ContentGuard · Cascade · Microcompact · … │
│ │ 40+ 구현체, StrategyResolver 런타임 교체 │
│ └───────────────────────────┬───────────────────┘
│ │
│ ┌─────────────────────────┴────────────────────┐
└───────┤ Stage │
│ s00 Auto (Planner + main_call) │
│ s01 Input · s02 History · s03 Prompt │
│ s04 Tool · s05 Strategy · s06 Context │
│ s07 Act · s08 Judge · s09 Decide │
│ s10 Save · s11 Finalize │
│ 12 슬롯 (Artifact 로 구현 swap) │
└──────────────────────────────────────────────┘
핵심 원칙
- 라이브러리 ≠ 인프라: 라이브러리는 URL·API 키·프로바이더 이름을 모른다 → 어댑터가 주입
- Graceful skip: 미등록 자원은 에러가 아니라 자동으로 건너뜀
- 무침범: 새 Stage/Strategy/Tool 추가 시 기존 코드 1줄도 수정 안 함
- 하드코딩 0:
if provider == "..."/if stage_id == "..."/if orch_hint == "linear"같은 분기 전부 제거 (v0.16.8OrchestratorSpecflag 기반 전환으로 마지막 2건까지 해소) - 단일 진실 소스: UI 필드 · 옵션 소스 · tokenizer · orchestrator · phase 경계 전부 엔진이 결정, 이식/프론트는 받아서 렌더만. drift 감지 시 경고 로그.
확장성 · 안정성 현황 (v0.16.9)
| 확장 지점 | 방식 | 등급 |
|---|---|---|
| Stage 추가 | register_stage() + entry_points + fs_scanner (v0.15.2) |
A |
| Strategy 교체 | StrategyResolver + stage-local 디렉토리 스캔 (v0.15.3) |
A |
| Strategy Variant | 디폴트 복사 → 파라미터만 교체 (v0.10.4) | A |
| Orchestrator | register_orchestrator() + flag 기반 분기 (v0.15.0 / v0.16.8) |
A |
| Phase | register_phase() + xgen_harness.phases (v0.15.1) |
A |
| LLM 프로바이더 | PROVIDER_REGISTRY + count_tokens() 확장점 (v0.11.22) |
A |
| Capability | 타입 무관 CapabilityRegistry + entry_points (v0.15.1) |
A |
| Tool 소스 | register_tool_source() + entry_points + s04 브릿지 (v0.16.4) |
A |
| Tool Synthesis | LLM 생성 도구 → Sandbox 검증 → 자동 합류 (v0.16.0) | A |
| Node Plugin | NodePluginManifest + LocalManifest 통합 (v0.16.1) |
A |
| Sandbox 격리 | subprocess + rlimit 5종 하드닝 (v0.16.1) | A |
| Role 체계 | Stage 가 자기 역할 선언, Pipeline 이름 리터럴 0 (v0.16.6) | A |
| UI 옵션 소스 | 엔진 options_source 단일 선언 + 이식 역매핑 (v0.11.23) |
A |
| Decide 분기 | DecideStrategy.decide() 로 Stage 내부 if/else 0 줄 (v0.11.1) |
A |
| 컴파일러 | xgen.compile(wf) + register_dependency_rule() + 폐쇄망 (v0.10.0) |
A |
| DAG 오케스트레이터 | fan-out Strategy 레지스트리 + DAGCycleError 명시 실패 (v0.11.27) |
A |
| Context Compression | 5-Level Cascade + AdvancedContextCompactor ABC 외부 교체 (v0.11.21) |
A |
| Tool Choice 제어 | OpenAI/Anthropic/LangChain 통합 + circuit breaker (v0.11.20) | A |
| 전체 plug-and-play 성숙도 | ~99% |
모든 확장 지점이 레지스트리 + 팩토리 + ABC/Protocol 기반. 라이브러리 본체에 하드코딩된 프로바이더/모델/판정/오케스트레이션 분기 리터럴 0 건.
5-Level Context Compression Cascade (Claude Code 이식, v0.11.14~16)
대화가 길어져 context_window 에 근접하면 단일 압축으론 부족. Claude Code 5-Level 을 이식, 압력 구간별 단계 적용.
| Level | 전략 | 동작 | stage_params.s06_context |
|---|---|---|---|
| L1 | Tool Result Budget (내재) | 50 KB 이상 tool_result → preview + pd_stores["tool_result"] 보존 |
(항상) |
| L2 | History Snip | token_budget first + last N | strategy="token_budget" |
| L3 | Microcompact | 오래된 tool_result 블록만 placeholder 교체 (비파괴) |
strategy="microcompact" |
| L4 | Context Collapse Overlay | 중간 메시지 → overlay 마커 + 원본 pd_stores["history"] 보존 |
strategy="context_collapse_overlay" |
| L5 | Autocompact | child LLM 이 9-section 요약 생성 → [first, summary, last_N] 로 축소 | strategy="autocompact_llm" |
Cascade — 압력 자동 선택
stage_params = {
"s06_context": {
"strategy": "cascade",
"cascade_l3_threshold": 80, # baseline 동기 (조기 발동 방지)
"cascade_l4_threshold": 90,
"cascade_l5_threshold": 97,
}
}
한 턴당 L3 + (L4 또는 L5) 최대 2 단계. 실행 결과는 results["cascade_applied"] = ["L3", "L5"].
RAG 주입 모드
| 값 | 설명 | 용도 |
|---|---|---|
system_prompt (기본) |
chunk → 시스템 프롬프트 주입 | 빠른 응답 |
tool_only |
시스템 prompt skip, rag_search 도구만 사용 |
L3 Microcompact 실전 조건 |
both |
둘 다 | 양쪽 장점 결합 |
s04_tool.rag_tool_mode="tool" 이면 자동 tool_only 로 전환 (사용자 의도 존중).
외부 교체 (v0.11.21)
from xgen_harness.stages.strategies.compactor_pd import AdvancedContextCompactor
from xgen_harness.core.strategy_resolver import register_strategy
class MyCompactor(AdvancedContextCompactor):
async def apply(self, state, stage, budget_used, results):
...
register_strategy("s06_context", "compactor", "my_compactor", MyCompactor)
Tool Choice 제어 (v0.11.19~20)
stage_params = {
"s04_tool": {
"rag_tool_mode": "tool",
"force_tool_use": True, # 첫 iter tool_choice="required"
}
}
- OpenAI:
body["tool_choice"] = "required" - Anthropic:
body["tool_choice"] = {"type": "any"} - LangChain:
llm.bind_tools(tools, tool_choice="required")forward none: Anthropic 미지원 →tools자체 drop 으로 semantics 맞춤
Circuit Breaker — loop_iteration >= 1 부터 auto 로 자동 격하 (무한 루프 방지).
RAG 연동
# 1. Pre-search (s06)
HarnessConfig(preset="rag",
stage_params={"s06_context": {"rag_collections": ["my_collection"]}})
# 2. Tool mode (에이전트 호출)
HarnessConfig(stage_params={"s04_tool": {
"rag_collections": ["my_collection"],
"rag_tool_mode": "tool", # presearch / tool / both
}})
# 3. Citation
HarnessConfig(stage_params={"s03_prompt": {"citation_enabled": True}})
# → LLM 이 [DOC_1], [DOC_2] 형식으로 문서 인용
API 키 해석 (동시성 안전)
os.environ 쓰기 0개. contextvars 기반으로 동시 실행 시 키가 섞이지 않음.
1. ExecutionContext (contextvars) ← 최우선
2. ServiceProvider.config.get_api_key() ← xgen-core persistent_configs
3. os.environ (읽기 전용 폴백)
from xgen_harness.core.execution_context import set_execution_context
set_execution_context(api_key="sk-...", provider="openai", model="gpt-4o-mini")
Preset
| Preset | 용도 | 특징 |
|---|---|---|
minimal |
단순 질의응답 | 도구/RAG/판정 없이 바로 대화 |
chat |
멀티턴 | 이전 대화 이력 유지 |
agent |
에이전트 | 도구 + RAG + 전략 + 판정 + 루프 |
evaluator |
품질 평가 | LLM Judge 엄격 |
rag |
문서 검색 | 문서 기반 답변, 도구 없음 |
프로바이더
5종 빌트인 + LangChain 래핑 + 커스텀 등록.
from xgen_harness.providers import register_provider, create_provider, wrap_langchain
# 빌트인: anthropic / openai / google / bedrock / vllm
provider = create_provider("openai", api_key, "gpt-4o-mini")
# LangChain 호환
from langchain_anthropic import ChatAnthropic
provider = wrap_langchain(ChatAnthropic(model="claude-sonnet-4-6"))
# 커스텀
register_provider("my_llm", MyProvider)
Provider tokenizer 확장점 (v0.11.22): LLMProvider.count_tokens(text) -> (tokens, source) 공식 확장. OpenAI 는 tiktoken 자동 감지. s00 usage 실패 시 자동 보정 + state.metadata["output_tokens_sources"] 출처 기록.
Stage 별 상세 — 설정, 연동, 확장
s00_harness (Auto) — v0.14.0+ 통제탑
역할 (role="orchestrator_planner"): Planner LLM 이 HarnessPlan 생성 + 본문 main_call 호스팅.
설정:
HarnessConfig(
harness_mode="autonomous", # autonomous / selected / off
planner_model="gpt-4o-mini", # Plan 생성용 모델 (기본 = provider default)
max_iterations=10, # Plan.max_iterations 이 이 값을 override 가능 (1~50)
)
Strategy (transport): streaming (기본) / batch — 외부에서 websocket / caching_proxy 등 추가 가능.
확장:
# 외부 Transport 추가
class MyTransport(TransportStrategy):
async def call(self, state) -> str: ...
register_strategy("s00_harness", "transport", "websocket", MyTransport)
s01 Input
Provider 생성, API 키 해석.
stage_params = {"s01_input": {
"provider": "openai", # anthropic / openai / google / bedrock / vllm
"model": "gpt-4o-mini",
"temperature": 0.7,
}}
s02 History
이전 대화 이력 로드. interaction_id 있을 때만 동작. embedding_search 전략 시 DocumentService 미주입이면 graceful skip (v0.11.25).
s03 Prompt
섹션 기반 조립: Identity → Rules → Tools → RAG → History → Citation.
stage_params = {"s03_prompt": {
"system_prompt": "당신은 한국어 도우미입니다.",
"prompt_id": "...", # v0.11.23 options_source="prompt-store"
"citation_enabled": False, # [DOC_1] 인용 형식
}}
Strategy (cache): anthropic_cache (기본) / no_cache.
s04 Tool
MCP / Gallery / RAG / 전역 tool_sources 도구 수집 (v0.16.4 브릿지).
stage_params = {"s04_tool": {
"mcp_sessions": ["session-abc"],
"rag_collections": ["my_docs"],
"rag_tool_mode": "both", # presearch / tool / both
"force_tool_use": False, # 첫 iter tool_choice="required"
"custom_tools": [...], # options_source="tools" (v0.11.23)
"capabilities": [...], # options_source="capabilities"
}}
Strategy (discovery): progressive_3level / eager_load.
s05 Strategy
실행 계획 (CoT / ReAct / none / auto). 첫 번째 루프에서만 실행.
s06 Context
RAG 검색 + 5-Level Cascade Compression. 위 Cascade 섹션 참조.
s07 Act (main_actor)
역할 (role="main_actor"): LLM 이 반환한 tool_use 를 실제로 실행. s00 의 main_call 이 이 Stage 직전 에 주입되어 응답 스트리밍 + tool 파싱.
stage_params = {"s07_act": {
"timeout": 60,
"result_budget": 50000,
# main_call 설정 (s00 에서 내려옴)
"max_tokens": 8192,
"max_retries": 3,
"context_limit": 500000,
"thinking_enabled": False,
"thinking_budget": 10000,
}}
Strategy (executor): sequential / parallel (읽기 병렬, 쓰기 순차).
Strategy (router): composite / mcp / builtin.
s08 Judge (scorer)
역할 (role="scorer"): StageExit 이벤트 score 에 validation_score 노출.
stage_params = {"s08_judge": {
"criteria": ["relevance", "completeness", "accuracy", "clarity"],
"threshold": 0.7,
}}
Strategy: llm_judge (기본, 가중평균) / rule_based (길이/에러/키워드) / none.
s09 Decide
Stage 내부 분기 0 줄 — DecideStrategy.decide() 전적 위임 (v0.11.1).
stage_params = {"s09_decide": {
"max_iterations": 10,
"guards": ["iteration", "cost_budget", "token_budget", "content"],
"cost_budget_usd": 5.0,
"token_budget": 500000,
"content_blocked_patterns": ["password\\s*:\\s*\\S+"],
"content_detect_pii": True,
}}
Guard 체인: Iteration · CostBudget · TokenBudget(80% 경고 / 95% 차단) · Content(정규식 + 이메일/휴대폰/주민번호/카드 PII 실구현).
s10 Save
실행 결과 DB 저장. save_enabled=False 면 건너뜀.
s11 Finalize
메트릭스 집계 + 출력 포맷팅 (text / json / markdown).
서비스 연동
라이브러리는 범용 이름(documents, mcp, config)으로 조회 → 어댑터가 실제 URL 등록. 미등록 시 graceful skip.
register_service("config", "http://xgen-core:8000")
register_service("documents", "http://xgen-documents:8000")
register_service("mcp", "http://xgen-mcp-station:8000")
| 서비스 | Stage | 용도 |
|---|---|---|
config |
s01 | API 키 조회 (persistent_configs) |
documents |
s02 / s03 / s06 | RAG 문서 검색 · embedding_search |
mcp |
s04 / s07 | MCP 도구 디스커버리 + 실행 |
| (DB) | s02 / s10 | 대화 이력 + 실행 로그 (ServiceProvider 주입) |
엔진 독립성 (v0.11.24~25) — 엔진은 xgen-documents API 스키마(/api/retrieval/...) 를 더 이상 알지 않음. 호스트 노드 참조는 register_xgen_node_resolver() 공식 확장 지점으로 주입.
워크플로우 컴파일러 (v0.10.0+)
HarnessConfig + workflow_data
↓
xgen.compile(wf)
↓
snapshot.json + env.example + cli.py + __init__.py
↓
build_wheel (python -m build)
↓
xgen_gallery_<name>-<ver>-py3-none-any.whl
↓
pip install xgen-gallery-<name>
↓
from xgen_gallery_<name> import arun
await arun("입력")
external_inputs— 선언 +${VAR}자동 스캔.PROVIDER_API_KEY_MAP경유로 secret 타입 확정.DependencyResolver+register_dependency_rule()— 외부 SDK 의존성 자동 wheel 주입.- drift-free (v0.10.2) — 엔진이 확정한
dist_name/package_name을 결과에 담아 이식/프론트 재조합 제거. - 3 채널 배포 — 공개 PyPI / 사내 인덱스 / 로컬 wheel 모두 동일 산출물.
- 폐쇄망 —
pip download후pip install --no-index --find-links wheelhouse/검증. - MCP 서브커맨드 —
pip install xgen-gallery-<name>[mcp]후serve-mcpCLI.
상세: docs/harness/2026-04-20-workflow-compiler.md.
DAG 오케스트레이터
멀티에이전트 플로우를 DAG 로 표현 + fan-out / join 자동 관리. 서브 파이프라인마다 독립 HarnessConfig + ExecutionContext (API 키 격리). SSE 이벤트를 부모 스트림으로 자동 포워딩.
from xgen_harness.orchestrator import DagOrchestrator, DagNode
orchestrator = DagOrchestrator()
orchestrator.add_node(DagNode(id="search", config=search_config))
orchestrator.add_node(DagNode(id="summarize", config=summarize_config, depends_on=["search"]))
async for event in orchestrator.run(initial_input="오늘 뉴스 요약"):
yield event
Fan-out Strategy: broadcast / round_robin / capability_match + register_fan_out_strategy() 외부 등록.
사이클 감지 (v0.11.27): Kahn 알고리즘 미처리 시 DAGCycleError raise (silent drop 방지).
디렉토리 구조
xgen_harness/
├── core/ # 핵심 엔진
│ ├── pipeline.py # 3-Phase 실행 + role 기반 분기 (v0.16.6)
│ ├── stage.py # Stage ABC + role 체계
│ ├── planner.py # HarnessPlanner + HarnessPlan + PLAN_TOOL_SCHEMA
│ ├── catalog.py # 런타임 카탈로그 (stages/orchestrators/providers/phases/…)
│ ├── llm_call.py # 본문 LLM 호출 공용 헬퍼 (v0.14.0 s07_llm 에서 이관)
│ ├── orchestrator_registry.py # OrchestratorSpec + 5 defaults + entry_points (v0.15.0/16.8)
│ ├── phase_registry.py # Phase 경계 레지스트리 (v0.15.1)
│ ├── fs_scanner.py # 파일시스템 Stage/Strategy 자동 스캔 (v0.15.2/3)
│ ├── sandbox.py # subprocess + rlimit 격리 (v0.16.0/1)
│ ├── nom.py # NOMNode / NOMGraph 단일 IR (v0.16.0)
│ ├── node_plugin.py # NodePluginManifest (v0.16.0)
│ ├── state.py # PipelineState (ToolGroup/ValidationGroup v0.11.22)
│ ├── config.py # HarnessConfig + harness_mode (v0.14.0)
│ ├── service_registry.py # 서비스 URL 레지스트리
│ ├── execution_context.py # contextvars API 키 격리
│ ├── strategy_resolver.py # Strategy 레지스트리 (40+)
│ ├── registry.py # Stage 레지스트리 (fs_scanner 위임)
│ ├── presets.py
│ ├── stage_config.py # STAGE_ID_ALIASES + options_source (v0.11.23)
│ └── artifact.py
│
├── stages/ # 12 Stage 디렉토리화 (v0.12.0)
│ ├── s00_harness/ # Planner + main_call (role=orchestrator_planner)
│ ├── s01_input/ ~ s11_finalize/ # 각 디렉토리에 stage.py + artifacts/ + strategies/
│ ├── interfaces.py # Strategy ABC
│ └── strategies/ # 공용 Strategy
│ ├── transport.py # StreamingTransport / BatchTransport (v0.14.0)
│ ├── retry.py · parser.py · thinking.py · token_tracker.py
│ ├── tool_router.py · tool_executor.py · discovery.py
│ ├── evaluation.py · compactor.py · compactor_pd.py (AdvancedCompactor ABC)
│ ├── guard.py · _decide.py · cache.py
│
├── providers/ # LLM 프로바이더 + count_tokens() 확장점
│ ├── anthropic.py · openai.py · google.py · bedrock.py · vllm.py
│ └── langchain_adapter.py
│
├── compile/ # 워크플로우 → wheel
│ ├── external_inputs.py · snapshot.py · deps.py · wheel.py
│ └── local_manifest.py # 통합 스키마 (v0.16.1)
│
├── tools/ # 도구 시스템
│ ├── synthesis.py # Tool Synthesis 루프 (v0.16.0/1)
│ ├── rag_tool.py · mcp_client.py · gallery.py
│
├── orchestrator/ # DAG 멀티에이전트
├── capabilities/ # 선언형 도구 바인딩
├── adapters/ # XgenAdapter 등
├── integrations/ # xgen 연동
├── events/ # SSE 스트리밍
├── errors/ # ErrorCategory + DAGCycleError
└── api/ # FastAPI 라우터
외부 기여 / 확장 매뉴얼
라이브러리 소스 수정 0 — 외부 패키지 + entry_points + register_*() API 만으로 12 지점 확장.
- 📌 docs/harness/INDEX.md — 모든 문서 단일 진입점
- docs/harness/STAGE_CONTRACT.md — Stage 1 페이지 계약서
- docs/harness/EXTENSION_POINTS.md — 확장 지점 전수 매뉴얼
- docs/harness/NODE_PLUGIN_SPEC.md — Node Plugin 매니페스트 규약 (v0.16.0)
- docs/harness/ANTHROPIC_OPENAI_PATTERNS.md — Anthropic/OpenAI 패턴 ↔ 우리 구현 매핑
- docs/harness/TOOL_GUIDE.md — 도구 패키지 작성 가이드
- docs/harness/2026-04-22-autonomous-driving.md — 재귀적 자율주행 4 Layer 설계
xgen-harness-stage-sample/— 외부 Stage 샘플 패키지 (pip install → entry_points → swap end-to-end)
버전 이력 요약
| 버전 | 주요 변경 |
|---|---|
| 0.16.9 | __version__ auto-sync — importlib.metadata 로 dist 버전 동적 조회 (상수 박제 제거) |
| 0.16.8 | 감사 hot-fix — Pipeline.describe 들여쓰기 복원, OrchestratorSpec flag 기반 분기 (리터럴 0), providers 무로깅 except 제거 |
| 0.16.7 | hot-fix — off 모드 본문 LLM 호출 복원 (Planner Stage 항상 주입) |
| 0.16.6 | 🎯 Pipeline Role 체계 — Stage.role 선언 (orchestrator_planner/main_actor/scorer). Pipeline Stage 이름 리터럴 12 → 0 |
| 0.16.5 | Tool result content string 정규화 — Anthropic 400 + slice TypeError 동시 수정 |
| 0.16.4 | s04_tool 브릿지 — 전역 tool_sources → LLM tool_definitions 자동 전파 |
| 0.16.3 | XGEN_HARNESS_PRELOAD_MANIFEST env 로 LocalManifest 자동 주입 |
| 0.16.1 | Sandbox rlimit 5종 하드닝 + LocalManifest 통합 스키마 + synthesis 하드코딩 제거 |
| 0.16.0 | 🚀 자가증식 4축 전면 실증 — Sandbox / NOM / NodePlugin / ToolSynthesis PASS |
| 0.15.3 | Pipeline B orchestrator_hint 실 분기 (linear/iterative/plan_execute) + Stage-local Strategy 자동 스캔 |
| 0.15.2 | 파일시스템 자동 스캔 (fs_scanner) + Tool entry_points + catalog source_file 노출 |
| 0.15.1 | 9 축 자동 연동성 감사 — Phase / Transport / Capability / Provider entry_points 갭 3 개 제거 |
| 0.15.0 | 🎯 재귀적 자율주행 완성 — Plan.max_iterations + Plan.orchestrator_hint + OrchestratorRegistry + display_name="Auto" |
| 0.14.0 | 🎯 s07_llm 삭제 + 번호 시프트 — s00_harness 통제탑 승격, TransportStrategy (streaming/batch), harness_mode 3-way |
| 0.13.0 | Iterative Planning — 단일 Provider 1 인스턴스, 매 iter replan, Plan.done 조기 종료, previous_results 자동 주입 |
| 0.12.0~3 | REAL HARNESS Phase 1 — s00_harness Planner + self-describing catalog + 13 Stage 디렉토리화 + PlanningEvent SSE |
| 0.11.24~27 | 감사 C+ 9건 해소 — 엔진 독립성 · API 권한 · ErrorEvent trace 차단 · s09_judge provider 주입 · DAG 사이클 명시 실패 · 집계 0 drop 버그 |
| 0.11.22~23 | count_tokens() 확장점 + PipelineState 도메인 분해 + options_source 11 필드 단일 진실 소스 |
| 0.11.21 | 연결선 3 갭 해소 — HarnessConfig top-level forwarding + s07 output_tokens 집계 + AdvancedContextCompactor ABC |
| 0.11.14~20 | Claude Code 5-Level Cascade 이식 + tool_choice API (OpenAI/Anthropic/LangChain 통합) + rag_ingestion_mode + circuit breaker |
| 0.11.0~1 | Stage ID 리네이밍 + alias 하위호환 + DecideStrategy 실 구현 + ContentGuard PII 실구현 |
| 0.10.0~4 | 워크플로우 컴파일러 + Strategy Variants + drift-free 연동 + PROVIDER_CONTEXT_LIMITS 레지스트리 |
| 0.9.x | Stage 책임 재정의 (s01 축소, anthropic 하드코딩 제거) |
| 0.8.x | Capability 시스템 + NodeAdapter + Progressive Disclosure + DocumentService 확장 |
| 0.5.x | ServiceRegistry + ExecutionContext + Plugin System |
| 0.1.0 | 12 Stage 파이프라인 초기 구현 |
상세 변경 내역: CHANGELOG.md.
Acknowledgement
설계·UI/UX 영감 및 하네스 구성의 reference — 🎁 geny-executor by CocoRoF.
본 프로젝트의 Stage 고정 + Artifact 교체 패턴, DAG 오케스트레이터 사고, Progressive Disclosure 환경 설계는 geny-executor 에서 많은 영감을 얻었습니다.
v0.16.x 의 자가증식 Sandbox 설계는 xgen-sandbox 아이디어를 차용 (코드 카피 없음 — 스펙만 통합).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xgen_harness-0.17.0.tar.gz.
File metadata
- Download URL: xgen_harness-0.17.0.tar.gz
- Upload date:
- Size: 305.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
532249bae84b0ae32f98e134764c3091201c1367851a772ff63ac4454184d6b6
|
|
| MD5 |
31c55d1e312bd715baf2857d1465bd3b
|
|
| BLAKE2b-256 |
b6451ebc2fd3f596ac509f29dbb2bcf855588910a400b6cc91e98c7a906d21c9
|
File details
Details for the file xgen_harness-0.17.0-py3-none-any.whl.
File metadata
- Download URL: xgen_harness-0.17.0-py3-none-any.whl
- Upload date:
- Size: 341.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acfdd58794ed9fdfc4b81a89ef7165a2416dff0bd446048efad2006e330ef0d3
|
|
| MD5 |
c79b96ea4e9c67c2843b015f26767033
|
|
| BLAKE2b-256 |
ca6893d80ab057d353264f16f857d1d900eda65adc1ab8ceb4dc7c5e1744d6cf
|