콘텐츠로 이동

런타임 API

이 페이지는 런타임의 외부 표면만 다룹니다. 에이전트 내부 구성은 에이전트 가이드, 전체 요청 경로는 아키텍처를 참고하세요.

목적표면
에이전트 호출POST /의 A2A JSON-RPC message/send·message/stream
기능·공개 URL 확인GET /.well-known/agent-card.json
상태·메모리·Registry 관리/api/v1/* REST API
프로세스 점검GET /healthz

create_server()에 evaluator를 연결하면 모든 completed 성공 출력이 A2A 응답으로 확정되기 전에 같은 평가를 정확히 한 번 거칩니다.

새 프로젝트에 커스텀 evaluator와 SDK 기본 evaluator 예제를 함께 만들 수 있습니다.

Terminal window
uv run llamon agent my-agent --template agent-general --example observability --yes
from llamon_agent import create_server
app = await create_server(
card=card,
agent=agent,
evaluators=[output_ready, quality],
evaluator_timeout_seconds=120.0,
)
인자기본값계약
evaluatorsNone최종 출력에 적용할 순서 있는 runtime evaluator 목록
evaluator_timeout_seconds90.0evaluator별 제한 시간. None이면 제한 없음

자동 평가는 input_required·failed 결과에는 적용되지 않습니다. 오류는 on_error="record"로 관측하므로 judge 장애가 완료 응답을 실패로 바꾸지 않으며, score=None이면 Numeric score를 만들지 않습니다. evaluator 작성법과 projection은 Runtime Evaluator Framework을 참고하세요.

같은 대화는 contextId를 유지하고 각 메시지는 새 messageId를 사용합니다.

Terminal window
curl -s -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"metadata": {"userId": "user-123"},
"message": {
"messageId": "msg-001",
"contextId": "support-42",
"role": "user",
"parts": [{"kind": "text", "text": "주문 상태를 알려줘"}]
}
}
}'
필드규칙
jsonrpc항상 "2.0"
idJSON-RPC 요청 ID
method단일 응답은 message/send, SSE는 message/stream
messageId메시지별 고유 ID
contextId대화·메모리의 thread_id; 후속 요청에서 유지
role사용자 요청은 "user"
parts하나 이상의 TextPart·DataPart·FilePart
params.metadata사용자·라우팅·관측 metadata의 기본 위치
kind용도핵심 모양
text자연어 질의·지시{"kind":"text","text":"..."}
data구조화 JSON 또는 외부 리소스 ID{"kind":"data","data":{"schema":"order.v1"}}
fileURI 또는 작은 base64 파일{"kind":"file","file":{"uri":"...","mimeType":"application/pdf"}}

FilePart.file에는 uribytes 중 하나만 넣습니다. 수신측이 다시 조회할 파일 ID는 FilePart가 아니라 DataPart에 둡니다. 서로 다른 kind는 한 요청에 함께 보낼 수 있습니다.

agentId·workflowId·chatbotId와 각 *Name은 선택적 관측 label입니다. trace를 묶어 보는 관례이며 SDK 실행 계층을 바꾸지는 않습니다.

외부 workflow host가 기존 대화를 보유한 채 새 contextId로 Agent를 호출할 때만 metadata.history를 사용합니다.

{
"metadata": {
"history": [
{"role": "user", "parts": [{"kind": "text", "text": "이전 질문"}]},
{"role": "agent", "parts": [{"kind": "text", "text": "이전 답변"}]}
]
}
}
  • params.metadata.historymessage.metadata.history를 모두 읽습니다.
  • checkpointer가 비어 있는 cold start에만 한 번 시드합니다. 이후에는 저장된 state가 기준입니다.
  • user·agent 역할의 TextPart만 변환하고 malformed 항목은 건너뜁니다.
  • 기본적으로 최근 50개 메시지만 유지합니다. 운영별 상한은 A2A_HISTORY_MAX_MESSAGES로 조절합니다.
  • host가 과거 assistant 발화를 만들 수 있으므로 신뢰 경계 안의 host에만 허용합니다.

일반 멀티턴은 history를 매번 보내지 말고 같은 contextId를 유지하세요. 저장 메시지와 실제 LLM 입력 window의 차이는 멀티턴 메모리에 설명되어 있습니다.

A2A envelope를 만들기 전의 입력 거부는 JSON-RPC error로, Agent 실행 중 실패는 result.status.state="failed"와 DataPart로 전달됩니다. 애플리케이션 코드는 보통 예외를 그대로 올리면 SDK가 후자 형식으로 변환합니다.

errorCode대표 상황기본 재시도
INVALID_INPUT입력·설정·schema 오류아니오
AUTH_REQUIRED키·권한·Registry 인증 오류아니오
NOT_FOUNDRegistry·Task·리소스 없음아니오
UPSTREAM_UNAVAILABLEMCP·DB·LLM 연결 또는 호출 실패
UPSTREAM_TIMEOUT외부 호출 시간 초과
RATE_LIMITEDupstream 429
GUARDRAIL_BLOCKED안전 정책 차단아니오
INTERNAL_ERROR분류되지 않은 내부 오류아니오

클라이언트 분기는 고정 enum인 errorCode로 합니다. errorReason은 로그·집계용 ASCII 식별자이고 errorMessage는 사용자 표시 문구입니다.

도메인 reason이나 재시도 시간을 직접 지정할 때만 raise_application_error()를 사용합니다.

from llamon_agent.core.errors import ErrorCode, raise_application_error
if not query:
raise_application_error(
ErrorCode.INVALID_INPUT,
"query_empty",
message="질의가 비어 있습니다.",
retriable=False,
)

일반 ComponentInvokeError, timeout, HTTP 401·403·429, GraphRecursionError는 자동 매핑됩니다. 프로젝트의 자체 오류 번호는 아래 도메인 오류처럼 표준 코드에 연결하세요.

Flow에서는 가능한 범위에서 완료 노드와 부분 artifact도 보존합니다.전체 화면에서 보기 ↗

상황상태
정상 결과, 빈 검색 결과, 업무상 부적합 판정completed + 구조화 DataPart
사용자·담당자의 추가 입력이 필요함input_required
요청을 정상 처리할 수 없음failed + errorCode

부분 스트림을 보낸 뒤 실패하면 SDK는 invoke로 재실행하지 않고 현재 버퍼를 정리해 failed로 마감합니다. input_required는 실패가 아닙니다. 같은 contextId로 이어서 호출하세요.

llamon_agent.response는 Markdown 표와 DataPart를 함께 내보낼 때 생기는 반복 코드를 줄입니다. 별도 서버나 원격 스키마 저장소를 사용하지 않는 앱 내부 유틸리티입니다.

app/nodes.py
from llamon_agent import MarkdownTableColumn, data_response, markdown_table
rows = [
{"name": "홍길동", "amount": 12000, "createdYmd": "20260624"},
{"name": "김철수", "amount": 34000, "createdYmd": "20260625"},
]
table = markdown_table(
rows,
columns=[
MarkdownTableColumn("name", "이름"),
{"key": "amount", "label": "금액", "format": "money", "align": "right"},
{"key": "createdYmd", "label": "생성일", "format": "date_ymd"},
],
)
return data_response(
text=f"### 조회 결과\n{table}",
data={"rows": rows},
schema="lookup_result",
artifact_name="lookup-result",
)

markdown_table()의 기본 format은 text, number, money, date_ymd, join입니다. 셀 안의 |는 이스케이프하고 줄바꿈은 <br>로 바꿉니다. data_response()RuntimeOutput을 반환하므로 Adapter와 graph node에서 모두 쓸 수 있습니다. 여러 DataPart가 필요하면 mapping 목록을 data=에 넘기세요. schema=는 각 DataPart에 schema 키를 넣되 이미 있는 값은 덮어쓰지 않습니다. 서버 수준 기본값은 ExtensionConfig.output_schema로 선언합니다(결과의 첫 DataPart에만 적용).

고정 artifact 이름은 ExtensionConfig에 두고 요청마다 달라질 때만 RuntimeOutput에서 지정합니다. 우선순위는 요청 결과값 → Adapter ClassVar → ExtensionConfig → SDK 기본 이름 순입니다.

이 유틸리티는 원격 사양 관리, DB 버전 관리, 임의 SQL 변경을 제공하지 않습니다. 실행 중 스키마를 바꿔야 한다면 별도 설정 서비스나 mounted config가 필요합니다.

DOC_005 같은 프로젝트 코드를 표준 ErrorCode와 함께 관리하려면 카탈로그에서 전용 오류 함수를 만듭니다.

app/internals/errors.py
from llamon_agent.core.errors import DomainErrorSpec, ErrorCode, make_domain_error_raiser
CATALOG: dict[str, DomainErrorSpec] = {
"CTX_001": {
"sdk_code": ErrorCode.INVALID_INPUT,
"reason": "context_invalid",
"title": "요청 컨텍스트 오류",
"retriable": False,
},
"DOC_005": {
"sdk_code": ErrorCode.NOT_FOUND,
"reason": "doc_name_unrecognized",
"title": "서류명 미식별",
"retriable": False,
},
}
raise_app_error = make_domain_error_raiser(CATALOG)

노드에서는 한 줄로 호출합니다.

from app.internals.errors import raise_app_error
raise_app_error("DOC_005", detail="documents 빈 배열")

SDK는 errorCode, errorReason, retriable, domainCode, domainTitle, detail을 trace 이벤트의 최상위 필드에 기록합니다. 추가 인자도 trace에 포함됩니다. 카탈로그에 없는 코드는 ValueError, 잘못된 sdk_code는 생성 함수를 만드는 시점에 TypeError로 잡힙니다.

코드가 한두 개뿐이면 raise_application_error()를 직접 호출해도 같은 trace 계약을 얻습니다. 코드가 3개 이상이거나 여러 노드가 공유한다면 생성 함수가 일관성을 지키기 쉽습니다.

오케스트레이터의 FinalResponse.on_failure()에서는 체크포인트를 먼저 남겨야 하므로 예외를 바로 던지지 말고 finalization_error_result()를 반환하세요. SDK가 저장을 마친 뒤 표준 실패 응답으로 바꿉니다. 자세한 실패 경계는 오케스트레이터 운영과 실패 복구를 참고하세요.

관리 API는 다음 envelope를 공통으로 사용합니다.

{"success": true, "data": {}, "error": null}

AGENT_ID는 공유 저장소에서 thread 소유권을 구분합니다. Registry hot reload endpoint는 INTERNAL_RUNTIME_CONTROL_ENABLED=true일 때만 등록됩니다. 메모리 endpoint는 이 설정과 무관합니다.

method·path역할
GET /api/v1/statusAgent ID와 MCP·A2A 연결 상태
GET /api/v1/card/url현재 공개 Agent Card URL
PATCH /api/v1/card/url공개 URL 변경; 응답의 persisted.env 반영 여부 확인
GET /api/v1/memory/config현재 memory 설정
PATCH /api/v1/memory/configsummarize, window_size, summarize_threshold 변경
GET /api/v1/memory/threads이 Agent가 소유한 thread 목록
GET /api/v1/memory/threads/{thread_id}/messagesthread 메시지 조회
DELETE /api/v1/memory/threads/{thread_id}thread checkpoint·장기 기억 삭제
DELETE /api/v1/memory/threads?all=true이 Agent 소유의 모든 thread 삭제
POST /api/v1/memory/migrate-agent-idAgent ID 변경에 맞춰 소유권 이전
GET /api/v1/registry/metadataRegistry 참조와 generation 조회
POST /api/v1/registry/reload변경된 Registry component hot reload

enabled, backend, postgres_dsn은 런타임에서 바꿀 수 없습니다. checkpointer나 pool을 다시 조립해야 하므로 설정을 바꾸고 재시작하세요.

관리 오류HTTP의미
INVALID_BODY·INVALID_PARAM·MISSING_THREAD_ID400요청 형식 오류
FORBIDDEN403다른 Agent 소유 thread
MEMORY_DISABLED·THREAD_NOT_FOUND404기능 또는 thread 없음
AGENT_ID_REQUIRED·RUNTIME_UNAVAILABLE409런타임 전제 미충족
IMMUTABLE_FIELD422재시작이 필요한 설정 변경

세션 보존·정리 정책은 멀티턴 메모리, Registry 연결 문제는 문제 해결을 참고하세요.