런타임 API
이 페이지는 런타임의 외부 표면만 다룹니다. 에이전트 내부 구성은 에이전트 가이드, 전체 요청 경로는 아키텍처를 참고하세요.
표면 선택
섹션 제목: “표면 선택”| 목적 | 표면 |
|---|---|
| 에이전트 호출 | POST /의 A2A JSON-RPC message/send·message/stream |
| 기능·공개 URL 확인 | GET /.well-known/agent-card.json |
| 상태·메모리·Registry 관리 | /api/v1/* REST API |
| 프로세스 점검 | GET /healthz |
Agent 서버의 최종 출력 평가
섹션 제목: “Agent 서버의 최종 출력 평가”create_server()에 evaluator를 연결하면 모든 completed 성공 출력이 A2A 응답으로
확정되기 전에 같은 평가를 정확히 한 번 거칩니다.
새 프로젝트에 커스텀 evaluator와 SDK 기본 evaluator 예제를 함께 만들 수 있습니다.
uv run llamon agent my-agent --template agent-general --example observability --yesfrom llamon_agent import create_server
app = await create_server( card=card, agent=agent, evaluators=[output_ready, quality], evaluator_timeout_seconds=120.0,)| 인자 | 기본값 | 계약 |
|---|---|---|
evaluators | None | 최종 출력에 적용할 순서 있는 runtime evaluator 목록 |
evaluator_timeout_seconds | 90.0 | evaluator별 제한 시간. None이면 제한 없음 |
자동 평가는 input_required·failed 결과에는 적용되지 않습니다. 오류는
on_error="record"로 관측하므로 judge 장애가 완료 응답을 실패로 바꾸지 않으며,
score=None이면 Numeric score를 만들지 않습니다. evaluator 작성법과 projection은
Runtime Evaluator Framework을 참고하세요.
A2A 요청
섹션 제목: “A2A 요청”같은 대화는 contextId를 유지하고 각 메시지는 새 messageId를 사용합니다.
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" |
id | JSON-RPC 요청 ID |
method | 단일 응답은 message/send, SSE는 message/stream |
messageId | 메시지별 고유 ID |
contextId | 대화·메모리의 thread_id; 후속 요청에서 유지 |
role | 사용자 요청은 "user" |
parts | 하나 이상의 TextPart·DataPart·FilePart |
params.metadata | 사용자·라우팅·관측 metadata의 기본 위치 |
Part
섹션 제목: “Part”| kind | 용도 | 핵심 모양 |
|---|---|---|
text | 자연어 질의·지시 | {"kind":"text","text":"..."} |
data | 구조화 JSON 또는 외부 리소스 ID | {"kind":"data","data":{"schema":"order.v1"}} |
file | URI 또는 작은 base64 파일 | {"kind":"file","file":{"uri":"...","mimeType":"application/pdf"}} |
FilePart.file에는 uri와 bytes 중 하나만 넣습니다. 수신측이 다시 조회할 파일 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.history와message.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_FOUND | Registry·Task·리소스 없음 | 아니오 |
UPSTREAM_UNAVAILABLE | MCP·DB·LLM 연결 또는 호출 실패 | 예 |
UPSTREAM_TIMEOUT | 외부 호출 시간 초과 | 예 |
RATE_LIMITED | upstream 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도 보존합니다.전체 화면에서 보기 ↗
Task 상태 선택
섹션 제목: “Task 상태 선택”| 상황 | 상태 |
|---|---|
| 정상 결과, 빈 검색 결과, 업무상 부적합 판정 | completed + 구조화 DataPart |
| 사용자·담당자의 추가 입력이 필요함 | input_required |
| 요청을 정상 처리할 수 없음 | failed + errorCode |
부분 스트림을 보낸 뒤 실패하면 SDK는 invoke로 재실행하지 않고 현재 버퍼를 정리해 failed로 마감합니다. input_required는 실패가 아닙니다. 같은 contextId로 이어서 호출하세요.
응답 조립
섹션 제목: “응답 조립”llamon_agent.response는 Markdown 표와 DataPart를 함께 내보낼 때 생기는 반복 코드를 줄입니다. 별도 서버나 원격 스키마 저장소를 사용하지 않는 앱 내부 유틸리티입니다.
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와 함께 관리하려면 카탈로그에서 전용 오류 함수를 만듭니다.
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
섹션 제목: “관리 API”관리 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/status | Agent 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/config | summarize, window_size, summarize_threshold 변경 |
GET /api/v1/memory/threads | 이 Agent가 소유한 thread 목록 |
GET /api/v1/memory/threads/{thread_id}/messages | thread 메시지 조회 |
DELETE /api/v1/memory/threads/{thread_id} | thread checkpoint·장기 기억 삭제 |
DELETE /api/v1/memory/threads?all=true | 이 Agent 소유의 모든 thread 삭제 |
POST /api/v1/memory/migrate-agent-id | Agent ID 변경에 맞춰 소유권 이전 |
GET /api/v1/registry/metadata | Registry 참조와 generation 조회 |
POST /api/v1/registry/reload | 변경된 Registry component hot reload |
enabled, backend, postgres_dsn은 런타임에서 바꿀 수 없습니다. checkpointer나 pool을 다시 조립해야 하므로 설정을 바꾸고 재시작하세요.
| 관리 오류 | HTTP | 의미 |
|---|---|---|
INVALID_BODY·INVALID_PARAM·MISSING_THREAD_ID | 400 | 요청 형식 오류 |
FORBIDDEN | 403 | 다른 Agent 소유 thread |
MEMORY_DISABLED·THREAD_NOT_FOUND | 404 | 기능 또는 thread 없음 |
AGENT_ID_REQUIRED·RUNTIME_UNAVAILABLE | 409 | 런타임 전제 미충족 |
IMMUTABLE_FIELD | 422 | 재시작이 필요한 설정 변경 |