LocalStack
LocalStack
LocalStack은 에이전트에게 에뮬레이트된 AWS 환경 접근을 줘서, 실제 계정을 건드리지 않고 AWS 서비스를 프로비저닝·시험할 수 있게 해요. 실행 중인 LocalStack 인스턴스에 AWS CLI를 연결해 — 엔드포인트·리전·자격 증명을 주입하고 — 선택적으로 각 실행을 위해 LocalStack Docker 컨테이너를 시작·중지할 수 있어요.
Pydantic AI Harness가 0.x 릴리스인 동안 API는 마이너 릴리스 사이에 바뀔 수 있어요. 바뀔 때는 폐기 경고와 릴리스 노트 마이그레이션 안내가 정확히 어떻게 업그레이드할지 알려줘요. 버전 정책 참고.
출처: 문서
본문
문제 (The problem)
클라우드 인프라를 만들거나 테스트하는 에이전트는 버킷·테이블·큐·함수를 만들 곳이 필요해요. 실제 AWS를 가리키면 느리고, 비용이 들고, 자격 증명이 새나갈 위험이 있고, 실행 사이에 리셋하기 어려워요. LocalStack이 AWS API를 로컬에서 에뮬레이트하지만, 에이전트를 연결하는 것은 같은 보일러플레이트를 반복하는 것을 뜻해요. 엔드포인트 URL 주입, 더미 자격 증명 제공, AWS CLI를 셸로 호출, 어떤 서비스가 떠 있는지 확인까지요.
사용법 (Usage)
LocalStack은 실행 중인 LocalStack 인스턴스에 연결된 AWS 툴링을 노출해요. 에이전트는 평범한 AWS CLI 명령을 발행하고, capability가 엔드포인트·리전·자격 증명을 주입하며 에뮬레이트된 서비스에 대한 헬스 체크를 추가해요.
from pydantic_ai import Agent
from pydantic_ai_harness import LocalStack
agent = Agent(
'anthropic:claude-sonnet-4-6',
capabilities=[LocalStack()],
)
result = agent.run_sync('Create an S3 bucket called reports and list all buckets.')
print(result.output)
기본적으로 에이전트는 별도로 시작한 LocalStack 인스턴스에 연결해요 — 예를 들어 localstack CLI(localstack start)로. 기본값은 LocalStack 관례와 일치해요. 에지 엔드포인트 http://localhost.localstack.cloud:4566(127.0.0.1로 해석됨)과 test/test 자격 증명. manage_container=True를 설정하면 실행마다 새 Docker 컨테이너를 시작·중지하게 해요.
도구 (Tools)
| 도구 | 용도 |
|---|---|
aws_cli |
LocalStack에 대해 AWS CLI 명령 실행. 명령을 선두 aws 없이 그리고 --endpoint-url 없이 넘기세요. 둘 다 주입돼요. 라벨이 붙은 stdout/stderr와 실패 시 종료 코드 반환 |
localstack_health |
LocalStack 헬스 엔드포인트를 조회하고 어떤 서비스(s3, dynamodb, sqs 등)가 사용 가능한지 JSON 반환 |
명령은 인자 벡터로 실행되고(셸 없음), 명령 문자열의 셸 연산자와 리다이렉션은 효과가 없어요. 출력은 [stdout]/[stderr] 마커와 0이 아닌 종료에 [exit code: N] 줄로 라벨돼요. max_output_chars를 초과하면 꼬리가 유지되고(머리는 버려짐) 오류가 절단을 견딥니다.
AWS CLI는 --body, file://, fileb://, s3 cp 같은 인자를 통해 로컬 파일을 읽고 쓸 수 있어요. 이 capability를 AWS-에뮬레이터 접근이자 프로세스 파일시스템에 대한 AWS CLI 접근으로 취급하세요.
서비스 제어 (Service controls)
| 필드 | 효과 |
|---|---|
allowed_services |
비어 있지 않으면 이 AWS 서비스들만 사용 가능(허용 목록), 예: ['s3', 'dynamodb'] |
denied_services |
이 AWS 서비스들은 항상 거부(차단 목록) |
allowed_services와 denied_services는 상호 배타적이에요 — 하나만 설정하세요. 서비스는 명령의 첫 비대상 토큰이에요(s3 ls의 s3).
Best-effort, 보안 경계 아님
이 검사들은 에이전트가 발행하는 명령을 게이트하지, 그것이 닿을 수 있는 것을 게이트하지 않아요. 하드 보장을 원하면 LocalStack 자체를 실행이 필요로 하는 가장 좁은 서비스·IAM 동작으로 구성하고, OS 수준 격리 아래 에이전트를 실행하세요.
컨테이너 관리 (Managing the container)
manage_container=True를 설정하면 capability가 각 실행을 위해 LocalStack Docker 컨테이너를 시작하고 실행이 끝나면 중지해요. 에이전트가 항상 새롭고 격리된 환경을 얻도록요. Docker가 설치·실행 중이어야 해요.
from pydantic_ai_harness import LocalStack
LocalStack(
manage_container=True,
image='localstack/localstack',
container_env={'DEBUG': '1', 'PERSISTENCE': '1'},
startup_timeout=120.0,
)
컨테이너의 에지 포트(4566)는 endpoint_url의 호스트 포트에 출판되고, capability는 실행이 시작되기 전에 헬스 엔드포인트를 기다리며, 실행이 끝나면 컨테이너를 중지해요(실행이 발생시켜도). 각 실행이 자체 컨테이너를 받으므로, 한 에이전트의 동시 실행은 구별되는 호스트 포트나 외부 관리 인스턴스(manage_container=False)가 필요해요.
LocalStack 2026.03.0 이후 기본 localstack/localstack 이미지는 시작에 인증 토큰이 필요한 단일 이미지예요(무료 Hobby/OSS 토큰이 커뮤니티 사용을 다룸). 현재 프로세스에 LOCALSTACK_AUTH_TOKEN이 설정되면 자동으로 컨테이너에 전달되고, 인증 토큰이 없을 때는 레거시 LOCALSTACK_API_KEY 값이 전달돼요. 인증 값은 docker run 인자에 임베드되는 대신 Docker CLI 환경을 통해 전달돼요. 기본 localstack/localstack 이미지는 시작에 토큰이 필요하므로, 관리형 실행에는 토큰이 필요해요. 토큰 없이 실행하려면 image를 계정 요구 이전의 태그(예: localstack/localstack:4.x 릴리스)로 설정하세요.
Lambda 같은 Docker 백업 서비스는 Docker 소켓 마운트가 필요하고, 일부 서비스는 게이트웨이 밖의 포트를 노출해요(LocalStack이 4510-4559 예약). 테스트하는 서비스가 요구할 때 명시적으로 활성화하세요:
from pydantic_ai_harness import LocalStack
LocalStack(manage_container=True, service_port_range='4510-4559', mount_docker_socket=True)
Docker 소켓 마운트는 컨테이너에 호스트 수준 Docker 제어를 줘요. 에뮬레이트된 서비스가 요구하고 실행 환경이 이미 신뢰된 경우가 아니면 mount_docker_socket=False를 유지하세요.
같은 라이프사이클을 독립형 비동기 컨텍스트 매니저로도 쓸 수 있어요:
import asyncio
from pydantic_ai_harness.localstack import LocalStackContainer
async def main() -> None:
async with LocalStackContainer(environment={'DEBUG': '1'}) as localstack:
... # talk to localstack.endpoint_url
asyncio.run(main())
구성 (Configuration)
from pydantic_ai_harness import LocalStack
LocalStack(
endpoint_url='http://localhost.localstack.cloud:4566', # edge endpoint (host port reused when managed)
region='us-east-1', # region for the CLI and environment
access_key_id='test', # LocalStack accepts any value
secret_access_key='test', # LocalStack accepts any value
allowed_services=[], # allowlist (mutually exclusive with denied)
denied_services=[], # denylist
default_timeout=60.0, # seconds, per command and health check
max_output_chars=50_000, # output cap returned to the model
aws_cli_path='aws', # CLI executable (e.g. 'aws' or 'awslocal')
manage_container=False, # start/stop a Docker container per run
image='localstack/localstack', # image used when managing the container
host_address='127.0.0.1', # host address for Docker port publishing
service_port_range=None, # e.g. '4510-4559' for non-gateway service ports
mount_docker_socket=False, # required by Docker-backed services such as Lambda
container_name=None, # optional name for the managed container
container_env={}, # env vars for the managed container
docker_path='docker', # Docker executable
startup_timeout=120.0, # seconds to wait for the container to be ready
include_instructions=True, # add usage instructions to the prompt
)
AWS CLI가 설치되고 PATH에 있어야 해요(aws_cli_path로 가리키거나). 바이너리가 없으면 aws_cli는 실행을 중단하는 대신 명확한 오류를 반환해요. 자신의 안내를 공급할 때는 include_instructions=False를 설정해 capability의 프롬프트 텍스트를 생략할 수 있어요.
에이전트 스펙 (Agent spec, YAML/JSON)
LocalStack은 Pydantic AI의 agent spec과 동작해요:
# agent.yaml
model: anthropic:claude-sonnet-4-6
capabilities:
- LocalStack:
endpoint_url: http://localhost.localstack.cloud:4566
allowed_services: ['s3', 'dynamodb', 'sqs']
from pydantic_ai import Agent
from pydantic_ai_harness import LocalStack
agent = Agent.from_file('agent.yaml', custom_capability_types=[LocalStack])
스펙 로더가 LocalStack을 어떻게 인스턴스화할지 알도록 custom_capability_types를 넘기세요.
더 읽기 (Further reading)
API 참고 (API reference)
LocalStack
Bases: AbstractCapability[AgentDepsT]
LocalStack을 통한 에뮬레이트된 AWS 환경 접근.
에이전트에게 실행 중인 LocalStack 인스턴스에 연결된 AWS CLI 툴링을 줘서, 실제 AWS를 건드리지 않고 AWS 서비스(S3, DynamoDB, SQS, Lambda, ...)를 프로비저닝·상호작용하게 해요. 에이전트를 실행하기 전에 LocalStack을 별도로 시작하세요(localstack start 또는 Docker 이미지).
from pydantic_ai import Agent
from pydantic_ai_harness.localstack import LocalStack
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[LocalStack()])
result = agent.run_sync('Create an S3 bucket called reports and list all buckets.')
print(result.output)
속성 (Attributes)
endpoint_url
실행 중인 LocalStack 인스턴스의 베이스 URL.
서브도메인 스타일 호스트가 필요한 AWS SDK와의 호환성을 위해 LocalStack의 localhost.localstack.cloud 도메인(127.0.0.1로 해석)으로 기본 설정.
타입: str 기본: 'http://localhost.localstack.cloud:4566'
region
CLI에 전달되고 환경으로 내보내지는 AWS 리전.
타입: str 기본: 'us-east-1'
access_key_id
AWS access key id. LocalStack은 아무 값이나 받아요. test 관례가 기본.
타입: str 기본: 'test'
secret_access_key
AWS secret access key. LocalStack은 아무 값이나 받아요. test 관례가 기본.
타입: str 기본: 'test'
allowed_services
비어 있지 않으면 이 AWS 서비스들만 사용 가능(허용 목록), 예: ['s3', 'dynamodb'].
타입: Sequence[str] 기본: field(default_factory=(list[str]))
denied_services
이 AWS 서비스들은 항상 거부(차단 목록).
타입: Sequence[str] 기본: field(default_factory=(list[str]))
default_timeout
AWS CLI 명령과 헬스 체크의 기본 타임아웃(초).
타입: float 기본: 60.0
max_output_chars
모델에 반환되는 최대 출력 문자 수. 양수여야 함.
타입: int 기본: 50000
aws_cli_path
AWS CLI 실행 파일의 경로 또는 이름(예: aws 또는 awslocal).
타입: str 기본: 'aws'
manage_container
True면 각 실행을 위해 LocalStack Docker 컨테이너를 시작하고 실행이 끝나면 중지.
Docker 필요. False(기본)일 때 에이전트는 endpoint_url에서 별도로 시작한 LocalStack 인스턴스에 연결.
타입: bool 기본: False
image
manage_container가 True일 때 실행할 Docker 이미지.
타입: str 기본: 'localstack/localstack'
host_address
Docker가 LocalStack 에지 포트를 출판하는 호스트 주소.
타입: str 기본: '127.0.0.1'
service_port_range
자체 포트를 노출하는 서비스용 선택적 호스트/컨테이너 포트 범위, 예: 4510-4559.
mount_docker_socket
True면 Lambda 같은 Docker 백업 서비스용으로 /var/run/docker.sock을 관리 컨테이너에 마운트.
타입: bool 기본: False
container_name
관리 컨테이너의 선택적 이름. Docker가 할당하게 하려면 None으로 두세요.
container_env
관리 컨테이너에 전달되는 환경 변수, 예: {'DEBUG': '1'}.
타입: Mapping[str, str] 기본: field(default_factory=(dict[str, str]))
docker_path
컨테이너 관리에 쓰는 Docker 실행 파일의 경로 또는 이름.
타입: str 기본: 'docker'
startup_timeout
관리 컨테이너가 준비될 때까지 기다릴 초. 실패하기 전.
타입: float 기본: 120.0
include_instructions
True면 에뮬레이트된 환경을 어떻게 쓰는지 모델에게 알려주는 지시문 추가.
타입: bool 기본: True
메서드 (Methods)
get_instructions
def get_instructions() -> str | None
비활성화되지 않았다면 에뮬레이트된 환경을 모델에게 설명.
반환
get_toolset
def get_toolset() -> AgentToolset[AgentDepsT]
LocalStack toolset 구축·반환.
반환
AgentToolset[AgentDepsT]
LocalStackToolset
Bases: FunctionToolset[AgentDepsT]
에이전트에게 에뮬레이트된 AWS 환경을 구동할 능력을 줘요.
AWS CLI를 감싸요. aws_cli는 엔드포인트·리전·자격 증명이 주입된 실행 중 LocalStack 인스턴스에 대해 명령을 실행하고, localstack_health는 어떤 에뮬레이트된 서비스가 가능한지 보고해요.
명령은 인자 벡터로 실행되고(셸 없음), 명령 문자열의 셸 연산자와 리다이렉션은 효과가 없어요.
메서드 (Methods)
for_run
@async
def for_run(ctx: RunContext[AgentDepsT]) -> AbstractToolset[AgentDepsT]
관리 컨테이너가 격리되고 종료되도록 실행별 새 인스턴스를 반환.
get_toolset은 에이전트 구성 시 하나의 공유 인스턴스를 만든다. 이 toolset이 Docker 컨테이너를 관리할 때 실행별 라이프사이클 상태를 보유하므로, 각 실행은 (__aexit__가 중지할 수 있는) 자체 인스턴스·자체 컨테이너를 얻는다.
반환
AbstractToolset[AgentDepsT]
call_tool
@async
def call_tool(
name: str,
tool_args: dict[str, Any],
ctx: RunContext[AgentDepsT],
tool: ToolsetTool[AgentDepsT],
) -> Any
도구 디스패치 지점에서 모델이 보는 출력 상한을 강제.
str 결과만 상한이 붙고, 미래에 풍부한 콘텐츠(예: ToolReturn)를 반환하는 도구는 이 지점을 확장해야 함.
반환
aenter
@async
def __aenter__() -> Self
구성돼 있으면 도구가 실행되기 전에 관리 LocalStack 컨테이너 시작.
반환
aexit
@async
def __aexit__(*args: object) -> None
시작된 컨테이너가 있으면 관리 LocalStack 컨테이너 중지.
반환
aws_cli
@async
def aws_cli(command: str, *, timeout_seconds: float | None = None) -> str
에뮬레이트된 AWS 환경에 대해 AWS CLI 명령 실행.
선두 aws 없이 그리고 --endpoint-url 없이 명령을 넘기세요. 엔드포인트·리전·자격 증명이 자동으로 주입돼요. 예: s3 mb s3://my-bucket, s3 ls, dynamodb list-tables.
반환
str — 라벨이 붙은 stdout/stderr 출력, 0이 아닌 종료에 종료 코드.
파라미터
command : str
실행할 AWS CLI 명령(예: s3 ls).
timeout_seconds : float | None 기본: None
최대 대기 초(기본: 구성된 타임아웃).
localstack_health
@async
def localstack_health() -> str
에뮬레이트된 AWS 서비스의 헬스·가용성 보고.
LocalStack의 헬스 엔드포인트를 조회하고 각 서비스(s3, dynamodb, sqs, ...)를 상태(available, running, ...)에 매핑하는 원시 JSON을 반환.
반환
str — 헬스 JSON, 또는 LocalStack에 닿을 수 없으면 오류 메시지.
LocalStackContainer
LocalStack Docker 컨테이너를 시작·중지하는 비동기 컨텍스트 매니저.
docker CLI를 구동하므로 Docker가 설치·실행 중이어야 해요. 진입 시 컨테이너를 실행하고, LocalStack이 준비될 때까지 헬스 엔드포인트를 폴링하며, endpoint_url을 노출해요. 종료 시 컨테이너를 중지해요. --rm으로 시작되므로 중지가 또한 제거해요.
async with LocalStackContainer() as localstack:
... # talk to localstack.endpoint_url
속성 (Attributes)
endpoint_url
컨테이너 에지 엔드포인트의 URL.
기본 루프백 출판 주소(127.0.0.1과 bind-all 0.0.0.0, 둘 다 127.0.0.1에서 닿음)에 대해 일부 AWS SDK가 필요로 하는 서브도메인 스타일 호스트를 지원하는 LocalStack localhost.localstack.cloud 도메인(127.0.0.1로 해석) 사용. 다른 host_address에 대해서는 그 주소를 그대로 사용. localhost.localstack.cloud 거기서 해석되지 않으니까.
타입: str
container_id
실행 중인 컨테이너의 id, 또는 실행 중이 아니면 None.
메서드 (Methods)
aenter
@async
def __aenter__() -> Self
컨테이너 시작하고 준비될 때까지 기다림.
반환
aexit
@async
def __aexit__(*args: object) -> None
컨테이너 중지·제거.
반환
더 알아보기 (Learn more)
- LocalStack documentation — 에뮬레이터.
- Pydantic AI Harness — 패키지 전반.