콘텐츠로 이동

v0.3.0

릴리스이전 버전

호환성 깨짐 없음 — 모두 추가 변경뿐입니다. 기존 agent/flow 프로젝트는 코드·요청/응답·CLI 출력이 그대로이며, SDK 버전만 >=0.3.0으로 올리면 됩니다. orchestrator는 직접 만들어 쓰기 전까지 어떤 기존 경로에도 로드되지 않습니다.

신규 — Workflow Orchestrator · 세 번째 프로젝트 타입(agent/flow 옆). 여러 자식 에이전트를 하나의 멀티턴 워크플로우로 묶고 턴을 넘어 결과를 누적합니다.

  • 코어에 LLM 없음 — 분류·검색·응답 작성은 모두 자식 에이전트가 맡습니다. orchestrator는 흐름 제어 + 턴 간 상태 누적만 책임집니다.
  • run_turn(ctx) 코드퍼스트 — 순서/분기/병렬/루프를 순수 Python으로 작성.
  • state_backend 2종in_memory(개발·데모) · postgres(운영 — 영속·멀티 워커 안전).
  • 멀티턴 맥락 헬퍼 — 직전 대화의 텍스트(fold_prior)·파일(fold_files)·결과(prior_result)를 자식 입력에 한 줄로 합칩니다.

공통 개선 · agent·flow·orchestrator 모두 적용

  • typed reasoning + provider_extra 직접 전달reasoning=False(on/off) · reasoning="low"(portable effort) · provider_flavor(OpenAI-compatible discriminator) · verbosity(OpenAI native 출력 분량). SDK가 provider별로 자동 매핑하며, 기본값이 None/auto라 미지정 시 모델 기본 동작을 유지합니다.
  • LLM 호출 타임아웃LLM_TIMEOUT(env, 기본 600초)로 단발 호출 상한 → 무한 대기 차단. message/send·stream·skill·judge 공통, 0=무제한.
  • Langfuse 세션·trace 정합화 — 클라이언트 sessionId 등 관측 키가 멀티 에이전트 체인 전체에 자동 전파. 같은 세션이 호출별로 쪼개져 보이던 문제 수정.
  • 운영 튜닝 env — HTTP 풀·동시성·타임아웃 상수를 env로 조정(미설정 시 기존 값 그대로).
  • 가드레일 검사 파트 선별GuardrailConfig(inspect_parts={...})text/data/file 중 검사 대상 한정(기본은 전부 검사라 미지정 시 동일).

개발 편의

  • 타입 지정 HITL 페이로드(HITLQuestion/HITLOption) · MockOrchestratorContext — 네트워크 없는 단위 테스트.
  • Studio — 미저장 변경 보호 · 노드 삭제 실행 취소 · 예시 코드 삽입 · Host Run Auto-reload.

신규 프로젝트 타입 — orchestrator

섹션 제목: “신규 프로젝트 타입 — orchestrator”

agent·flow에 이어 세 번째 프로젝트 타입입니다. 흐름은 app/orchestrator.pyrun_turn(ctx)(순수 Python)로 작성하고, 자식 에이전트 연결은 orchestrator.toml [agents]에 선언합니다.

Terminal window
llamon orchestrator intake-review --template generic --yes

자식 에이전트의 대화 맥락은 자식별({conversationId}:{alias})로 격리되어 서로 섞이지 않습니다.

from app.reducers import merge_documents # 직접 작성하는 reducer (작성법은 오케스트레이터 가이드 참고)
async def run_turn(ctx):
ctx.record_request() # 이번 입력(text·files·data)을 user 턴으로 기록 (emit의 짝)
# 동적 라우팅 = 자식 분류기(LLM). 결정형 분기는 그 출력으로.
intent = await ctx.call("intent", text=ctx.user_text)
if intent.data.get("intentType") == "unclassified":
return ctx.emit(await ctx.call("unclassified", text=ctx.user_text))
# 병렬 (cap=16) — gather 안에서는 상태 변경 금지
if ctx.files:
res = await ctx.gather({"review": ctx.call("document_review", files=ctx.files)})
ctx.reduce("documents", res["review"].data_parts, merge_documents) # 턴 간 누적
documents = ctx.get("documents", []) # 이전 턴 누적분까지
final = await ctx.call("response", data={"documents": documents})
return ctx.emit(final) # = append_message("assistant", …) + return

flow+memory로는 처리하기 어려운 턴 간 상태 누적(ctx.remember/ctx.reduce/ctx.get)을 기본으로 제공합니다. 분기마다 반복되는 응답 기록 + returnctx.emit(result) 한 줄로 줄일 수 있습니다.

멀티턴 맥락 헬퍼 — 텍스트·파일·결과 세 축

섹션 제목: “멀티턴 맥락 헬퍼 — 텍스트·파일·결과 세 축”

“그거 검증해줘” 같은 모호한 발화를 보완하거나 여러 턴에 걸쳐 문서를 함께 처리할 때, messages를 직접 잘라 쓰지 말고 헬퍼를 쓰세요. 직전 대화 스냅샷은 턴 시작 시점에 고정되어 항상 직전 턴만 가리킵니다.

ctx.record_request() # 이번 입력을 user 턴으로 기록 (emit의 짝)
work = await ctx.call("comparison", # 직전+현재 파일을 한 번에
text=ctx.fold_prior(3), files=ctx.fold_files(3))
prior = ctx.prior_result() # 가장 최근 'data 있는' 결과
  • 텍스트ctx.fold_prior(n) 직전 대화를 합친 문자열(text=에 바로) · ctx.prior_turns(n) 직전 메시지 목록
  • 파일(신규) — ctx.fold_files(n) 직전+현재 파일 병합(files=에 바로) · ctx.prior_files(n) 직전 파일만
  • 결과 data(신규) — ctx.prior_result() 가장 최근 data 있는 결과 1건 · ctx.prior_data(n) 범위 내 전부
  • 기록(신규) — ctx.record_request() 이번 입력을 user 턴으로 기록. 이게 있어야 다음 턴 헬퍼들이 직전 입력을 봅니다.
  • 윈도우 범위 밖의 영속 누적은 ctx.accumulate(key, items).

직전 결과를 자식 data=자동으로 넣어주지는 않습니다 — 자식 스키마 계약에 맞춰 앱이 직접 감싸 전달하세요([{"priorWork": prior}, ...]).

대화 기록(messages)은 개수(max_messages)와 바이트(max_state_bytes) 두 축으로 오래된 것부터 자동 정리됩니다. 대용량 파일은 바이트로 직접 싣기보다 URI로 넘기는 방식을 권장합니다.

버그 수정 — 컨텍스트 격리 · Langfuse (기존 배포 영향 없음)

섹션 제목: “버그 수정 — 컨텍스트 격리 · Langfuse (기존 배포 영향 없음)”
  • 익명 턴 격리contextId 없이 들어온 단발 호출들이 상태를 공유할 수 있던 가능성을 차단하고 턴마다 격리합니다. 멀티턴이 필요하면 기존처럼 contextId를 보내면 됩니다.
  • emit의 멀티 DataPart 보존ctx.emit(result)가 결과의 모든 data_parts를 대화기록에 남깁니다(이전엔 첫 파트만).
  • Langfuse 세션 분절 수정 — 같은 sessionId로 보낸 요청인데 Langfuse 세션이 호출(에이전트)별로 쪼개져 보이던 문제를 일괄 수정했습니다. 이제 모든 trace 레이어가 동일 규칙(클라이언트 metadata.sessionId 우선, 없으면 contextId)을 따르고, orchestrator도 수신 요청의 관측 키를 자식 호출에 자동 전달합니다. sessionId를 보내지 않는 배포는 종전과 동일합니다.
  • 스트리밍 trace output 보강message/stream 요청의 Langfuse trace output에 text만 남고 응답 DataPart가 누락되던 동작을 invoke 경로와 맞췄습니다. 파일 본문(base64)은 자리표시자로 치환되어 trace에 노출되지 않습니다.
  • 빌드타임 스냅샷 response_format 누락 수정LLMConfig(response_format=...)이 런타임 resolve에선 적용됐지만 빌드타임 스냅샷(resolved.json, 에어갭) 경로에선 누락되어 조용히 드롭되던 문제를 수정했습니다. 이제 provider_extra·reasoning과 동일하게 스냅샷에 함께 구워져 양 경로 동작이 일치합니다(registry-structured 템플릿의 JSON 강제 권장이 에어갭 배포에서도 적용됨). response_format 미설정 배포는 종전과 동일합니다.

언제 쓰나 (agent / flow / orchestrator)

섹션 제목: “언제 쓰나 (agent / flow / orchestrator)”
무엇상태
agent단일 에이전트 (LLM + tools/MCP/guardrail)에이전트별 멀티턴(thread memory)
flow정적 LangGraph 그래프 (순서/분기/병렬) — 한 번의 호출 내대화 메모리(checkpointer)
orchestrator여러 자식을 묶는 상태 유지 워크플로우턴 간 상태 누적(reduce/remember)

→ “여러 에이전트를 묶어 자연스러운 통합 응답”을 만들면서 턴을 넘어 결과를 누적해야 하면 orchestrator를 쓰세요. 순서/분기만 필요하면 flow로 충분합니다.

from llamon_agent.orchestrator.server import build_orchestrator_app
app = await build_orchestrator_app(card=card, run_turn=run_turn, config_path="orchestrator.toml")

기존 create_server/executor를 어댑터로 그대로 구동하므로 기존 코드 수정이 없습니다. 현재는 스트리밍을 지원하지 않고 단일 artifact로 응답합니다.

백엔드영속동시성용도
in_memory (기본)없음 — restart 시 유실단일 프로세스 내 잠금개발·데모·단일 워커
postgres있음DB 행 잠금 — 멀티 워커에서도 같은 대화는 순서대로 처리운영·영속·멀티 워커

postgres는 별도 schema(llamon_orch)를 쓰므로 메모리 backend와 같은 DB를 공유해도 안전하고, 스키마/테이블은 첫 turn에 자동 생성됩니다. DSN은 POSTGRES_ORCH_DSN(미설정 시 POSTGRES_MEMORY_DSN)에서 읽습니다.

app = await build_orchestrator_app(
card=card, run_turn=run_turn,
input_guardrail=my_input_guardrail, # 선택
output_guardrail=my_output_guardrail, # 선택
)

자식 에이전트가 각자 가드레일을 갖고 있어도, orchestrator 자신의 경계에서 합성 응답 전체(text + DataPart)를 한 번 더 검증할 수 있습니다. 둘 다 생략하면 기존 동작 그대로입니다.

결정 유틸 — build_decider + 프리셋 5종 (선택)

섹션 제목: “결정 유틸 — build_decider + 프리셋 5종 (선택)”
from llamon_agent import build_classifier
scope = build_classifier("ollama/qwen3-7b") # 상태 없음 — 턴 간 재사용 안전
async def run_turn(ctx):
res = await scope.decide(ctx.user_text, labels=["current_only", "accumulated"])

run_turn 안에서 필요한 작은 LLM 판단(스코프 분류·중복 판정·모호 해소·불리언/필드 추출)을 한 줄로 만듭니다 — build_classifier / build_duplicate_detector / build_disambiguator / build_boolean_extractor / build_field_extractor. decider는 개발자가 써넣은 결정 지점에서만 돌고 응답을 생성하지 않으므로, 코어 조율은 여전히 LLM 없이 돌아갑니다.

registry id로 선언한 decider LLM은 빌드타임에 resolve되어(resolve --deciders) 런타임에 registry 네트워크 호출이 없습니다(폐쇄망 안전). resolve CLI의 인증도 선택사항이 되어 AUTH_HOST 미설정 시 무인증 registry를 그대로 조회합니다. → 가이드

자식 에이전트 호출에 세션·상관관계 식별자를 잇는 방법이 정리됐습니다. 해당 값을 쓰지 않으면 기존 요청 형식이 그대로 유지됩니다.

  • ctx.call(metadata=) / OrchestratorContext(base_metadata=)request_id·traceparent 같은 식별자를 자식 호출 metadata로 전달합니다. base_metadata는 매 호출에 자동 병합되고, 호출별 metadata=의 같은 키가 우선합니다.
  • flow call_agent_autoforward_session(기본 ON) — 클라이언트 metadata 중 관측 키(sessionId·userId·workflowId·workflowName·agentId·agentName·chatbotId·chatbotName)만 자식 호출에 선별 전파합니다. 멀티 에이전트 체인 전체의 Langfuse trace가 클라이언트 세션 한 줄로 묶이고 같은 워크플로우 이름으로 그룹핑됩니다. 자식에 이 식별자를 노출하면 안 되면 forward_session=False. → 헬퍼 레퍼런스 · 클라이언트가 넣는 법은 관측 메타데이터
  • orchestrator도 동일 키 집합 자동 전파 — 수신 요청 metadata의 관측 키가 모든 ctx.call 자식 호출에 병합됩니다(명시 metadata= 우선).
  • call_agent에 keyword-only context_id 추가 — 미지정 시 기존과 동일하게 동작합니다.

타입 지정 HITL 페이로드 — HITLQuestion / HITLOption

섹션 제목: “타입 지정 HITL 페이로드 — HITLQuestion / HITLOption”

노드 그래프(agent/flow)의 HITL 질문을 pydantic으로 선언합니다. 생성되는 페이로드는 기존 {"question", "options"} 형식을 포함하는 상위 호환 형식이라 기존 클라이언트가 그대로 동작하고, 재개 시 사용자의 답변을 옵션 value로 정규화합니다(LLM 불필요). → HITL 가이드

코드 상수로만 박혀 있던 풀/타임아웃/동시성 값을 env로 조정할 수 있습니다. 미설정·오타·범위 위반 시 경고 후 코드 기본값으로 동작하므로 env 실수로 부팅이 죽지 않습니다. 명시 코드 인자가 있으면 env보다 우선합니다.

그룹env (default)
HTTP 공유 풀HTTP_MAX_CONNECTIONS(500) · HTTP_MAX_KEEPALIVE_CONNECTIONS(100) · HTTP_STREAMING_MAX_CONNECTIONS(200) · HTTP_TIMEOUT_CONNECT(10s) · HTTP_TIMEOUT_READ(30s)
외부 호출REGISTRY_HTTP_TIMEOUT(10s) · A2A_CARD_TIMEOUT(30s) · A2A_SEND_TIMEOUT(120s) · A2A_CONNECT_TIMEOUT(10s) · GUARDRAIL_HTTP_TIMEOUT(5s) · LLM_MAX_RETRIES(3) · LLM_TIMEOUT(600s, 미설정 시에도 적용·0=무제한)
동시성FANOUT_MAX_CONCURRENCY(16)
메모리·PostgreSQLMEMORY_PG_COMMAND_TIMEOUT(10s) · MEMORY_PG_STATEMENT_CACHE_SIZE(100) · MEMORY_INMEMORY_MAX_THREADS(10000) · ORCH_PG_POOL_MAX_SIZE(5) · ORCH_PG_STATEMENT_TIMEOUT_MS(5000)

CLI scaffold의 .env.example에 전 키가 주석 처리 상태(미설정 = 기본값)로 생성되며, 템플릿 종류에 맞는 그룹만 노출됩니다.

reasoning·verbosity 제어 + provider_extra 직접 전달

섹션 제목: “reasoning·verbosity 제어 + provider_extra 직접 전달”

Thinking·Reasoning 설정 방식은 모델과 제공자마다 다릅니다. SDK는 이를 추상화하기 위해 reasoning을 공통 인터페이스로 제공하며, 각 제공자에 맞는 설정 형식으로 자동 변환합니다. 제공자 전용 옵션이 필요한 경우에만 provider_extra를 사용하세요.

LLMConfig(id="<MODEL_ID>", reasoning=False) # thinking/reasoning off
LLMConfig(id="<MODEL_ID>", reasoning="low") # portable effort
LLMConfig(id="<MODEL_ID>", provider_flavor="vllm") # OpenAI-compatible discriminator
LLMConfig(id="<MODEL_ID>", verbosity="low") # OpenAI native 출력 분량
설정대상provider 매핑미지원 시
reasoningon/off + effort + budget intentOpenAI native=reasoning_effort/reasoning · vLLM=chat_template_kwargs.enable_thinking · Ollama=reasoning · Anthropic=thinking/output_configvLLM/비-gpt-oss Ollama는 effort depth를 on/off로 degrade
provider_flavorOpenAI-compatible endpoint 구분Registry 경로는 provider_type.code 우선, code-first는 base URL로 추정대부분 auto면 충분. 프록시/Azure는 명시 권장
verbosityOpenAI native 응답 분량 조절OpenAI native=verbosity미지원 OpenAI 모델=서버 400 가능, SDK가 fail-soft 재시도
provider_extravendor raw 파라미터OpenAI/vLLM/Anthropic=extra_body · Ollama=options내용은 provider-specific
  • reasoningverbosity는 서로 독립입니다. 하나는 추론/생각 방식, 하나는 출력 분량입니다.
  • **기본값 None**이면 모델/서버 기본 동작을 그대로 따르므로, 미지정 시 결과가 바이트 단위까지 동일해 기존 배포를 그대로 둬도 됩니다.
  • registry 런타임 · 빌드타임 스냅샷(에어갭) · code-first 세 경로 모두에서 동작합니다.
  • 직접 우회 통로 LLMConfig.provider_extra하나의 필드를 SDK가 provider raw 채널로 자동 매핑합니다(OpenAI/vLLM/Anthropic→extra_body, Ollama→options). portable knob으로 다룰 수 없는 provider 고유 파라미터를 직접 넘길 때 쓰며, 내용은 provider마다 다릅니다.
  • 주의: vLLM 경로는 게이트웨이·프록시가 chat_template_kwargs를 업스트림 서빙 엔진으로 전달해야 실제로 적용됩니다.

config.py 설정 예시는 에이전트 합성 — LLM 튜닝 파라미터를, 증상별 원인·처방은 트러블슈팅을 참고하세요.

llamon run — 캐시 빌드 기본 복원 (빠른 반복)

섹션 제목: “llamon run — 캐시 빌드 기본 복원 (빠른 반복)”

기본값이 --no-cache 전체 빌드에서 --cache(docker compose up --build, 레이어 캐시 사용)로 바뀌어 반복 실행이 수 초로 단축됩니다. 낡은 wheel이 끼어드는 근본 원인이던 Dockerfile 템플릿의 bind-mount RUN cp /build/llamon_agent-*.whl 방식을 콘텐츠 체크섬 기반 COPY로 교체해, 같은 파일명 wheel의 내용 변경도 캐시 빌드에 정확히 반영됩니다.

# 변경 전 (낡은 산출물 위험 + 캐시 무력화)
RUN --mount=type=bind,source=.,target=/build \
cp /build/llamon_agent-*.whl /app/ 2>/dev/null || true && \
cp /build/llamon_agent-*.whl.sha256 /app/ 2>/dev/null || true
# 변경 후 (콘텐츠 체크섬 기반 — 정확하고 빠름)
COPY llamon_agent-*.whl* /app/

기존 생성 프로젝트는 Dockerfile/Dockerfile.local의 해당 RUN 블록을 위 COPY 한 줄로 교체하세요(미교체 시 llamon run . --no-cache로 종전 전체 빌드 동작). 전체 빌드가 필요하면 --no-cache를 명시해 그대로 쓸 수 있습니다.

orchestrator.toml이 있으면 llamon doctor가 설정 파일과 app/orchestrator.py 존재를 검증합니다(DOC130DOC133). 비-orchestrator 프로젝트에는 영향 없습니다.

Studio — 편집 안전성·노드 예시코드

섹션 제목: “Studio — 편집 안전성·노드 예시코드”

flow/agent 비주얼 에디터의 데이터 유실 방지와 노드 코드 작성 경험을 다듬었습니다(orchestrator는 코드퍼스트라 해당 없음).

  • 미저장 변경 보호.env·에이전트 카드 패널을 미저장으로 닫거나 백업 복원 시 확인 단계를 거칩니다.
  • 노드 삭제 실행 취소 — 휴지통·Delete로 지운 노드·코드·라우팅을 토스트의 ‘실행 취소’로 한 번에 복원합니다.
  • 예시 코드 삽입 — 노드/라우팅 함수 에디터에서 백엔드 기본 코드를 언제든 불러와 편집·복사할 수 있습니다.
  • Host Run Auto-reload (항상 on) — Host Run으로 실행 중이면 프로젝트 파일(*.py·.env·pyproject.toml·orchestrator.toml) 저장 시 호스트 프로세스를 자동 재시작합니다(uvicorn --reload와 같은 방식 — 재시작 동안 진행 중 요청은 끊김). 헤더 상태 배지에 auto-reload 표시. 수동 Stop·크래시 시에는 자동 재시작하지 않으며, .venv·__pycache__·숨김 디렉터리(백업 포함)는 감시에서 제외됩니다.
  • 저장 피드백·접근성(aria-live)·라이트 테마 대비 등 다수 개선.

import pytest
from llamon_agent.orchestrator.testing import MockOrchestratorContext
from app.orchestrator import run_turn
@pytest.mark.asyncio
async def test_unclassified_branch():
ctx = MockOrchestratorContext(conversation_id="t-1", user_text="???")
ctx.mock_call("intent", data={"intentType": "unclassified"})
ctx.mock_call("unclassified", text="다시 말씀해주세요")
result = await run_turn(ctx)
assert ctx.calls_made == ["intent", "unclassified"] # 호출 순서/분기
assert result.text == "다시 말씀해주세요"

시나리오작업
기존 agent/flow 프로젝트수정 불필요 — 전부 추가 변경뿐. orchestrator를 쓰지 않으면 아무것도 로드·변경되지 않습니다.
SDK 버전 지정pyproject.tomlllamon-agent>=0.2.x>=0.3.0.
orchestrator 신규 생성llamon orchestrator <name> --agent alias=target (generic 템플릿).
postgres stateorchestrator.tomlstate_backend = "postgres" + POSTGRES_ORCH_DSN(미설정 시 POSTGRES_MEMORY_DSN). 새 PyPI 의존성 없음.
Studioorchestrator는 코드퍼스트 — 비주얼 편집 미지원. app/orchestrator.py/orchestrator.toml을 직접 편집(CLI가 안내).

현재 제약: 스트리밍 미지원(단일 artifact) · Studio 비주얼 편집 미지원(코드퍼스트). 워크플로우 상태에는 PII/민감정보가 담길 수 있으므로 운영 배포 전 가드레일 적용과 상태 보존/암호화/접근통제를 검토하세요.