Python 코드 실행 (Monty)
ADK-Rust은 Pydantic Monty 인터프리터를 통해 모델이 작성한 Python을 프로세스 내에서 실행합니다 — 컨테이너도, 서브프로세스도 없으며 시작 시간이 마이크로초 단위입니다. 이 기능은 두 계층으로 제공됩니다.
adk-code(embedded-python기능) —MontyExecutorBuilder및 두 실행기 제품인MontyOneShotExecutor과MontyReplExecutor. 둘 다CodeExecutor을 구현합니다.adk-tool(code-embedded-python기능) — 이러한 실행기 위에서 에이전트가 사용하는 도구인MontyPythonCodeTool(monty_python_code).
이는 컨테이너를 기반으로 하는 PythonCodeTool (python_code)을 보완합니다. 해당 기능은 Docker에서 완전한 CPython을 실행합니다. 스크립트에 실제 Python 생태계(pip 패키지, C 확장, 완전한 표준 라이브러리)가 필요한 경우 이를 사용하세요. Monty는 프로세스 내 실행 속도, 직렬화 가능한 인터프리터 상태, 그리고 구조적으로 보장되는 네트워크 및 서브프로세스 미사용을 제공하는 대신 Python의 일부만 구현합니다.
[dependencies]
adk-tool = { version = "2.1.0", features = ["code-embedded-python"] }
또는 상위 crate를 통해 사용할 수 있습니다.
[dependencies]
adk-rust = { version = "2.1.0", features = ["minimal", "code-embedded-python"] }
일회성 실행과 REPL
하나의 builder가 두 제품을 모두 생성하며, 모드는 플래그가 아니라 타입에 인코딩됩니다.
| 모드 | 빌드 | 상태 | 동시성 |
|---|---|---|---|
| 일회성 | build_one_shot() | 호출마다 새 인터프리터 | 동시성 안전 |
| REPL | build_repl() | 변수, 함수 및 import가 호출 간에 유지됨 | 세션별 호출 직렬화 |
use adk_code::{MontyExecutorBuilder, PathAccess};
let builder = MontyExecutorBuilder::new()
.allow_path("/data", "/srv/agent/data", PathAccess::ReadOnly)
.allow_path("/out", "/srv/agent/out", PathAccess::ReadWrite)
.environ_var("PROJECT", "acme")
.system_clock();
let one_shot = builder.clone().build_one_shot()?;
let repl = builder.build_repl()?;
REPL 실행기는 호출 간에 직렬화된 인터프리터를 저장합니다. Monty는 Python 수준의 예외가 발생하더라도 세션을 유지하므로, 실패한 스니펫이 누적된 상태를 소실시키지 않습니다. CodeExecutor 수명 주기 메서드가 세션을 관리합니다. start()은 세션을 초기화하고, stop()는 세션을 삭제하며, restart()은 세션을 재설정하고, execute()는 start() 전에 세션을 지연 초기화합니다.
보안 모델
격리는 명시적 정책과 누락에 의한 적용을 결합합니다.
- 파일 시스템.
allow_path으로 권한이 부여된 디렉터리만 접근할 수 있으며, 각 디렉터리는pathlib.Path을 통해 가상 마운트 경로에 대해 읽기 전용 또는 읽기-쓰기로 제공됩니다. Monty의 마운트 테이블이 경계를 적용합니다(정규화 + 심볼릭 링크 탈출 감지). 그 외의 모든 경로는 포착 가능한OSError을 발생시키며(존재 여부 확인은False를 반환합니다). - 환경.
os.getenv/os.environ은 생성 시 허용된 명시적 맵만 읽습니다. 호스트 프로세스 환경은 절대 노출되지 않습니다. - 시계.
.system_clock()가 허용된 경우에만date.today()/datetime.now()이 작동하며, 그렇지 않으면OSError를 발생시킵니다. - 네트워크 및 하위 프로세스. Monty에는 어느 쪽도 사용할 수 있는 표면이 없으므로, 구성과 관계없이 불가능합니다.
- 시간 제한.
SandboxPolicy::timeout은 Monty의ResourceLimits::max_duration로 매핑됩니다(호출마다 실제 VM 선점). 메모리 상한(기본값 256 MiB)은 힙의 크기를 제한하며, REPL 모드에서는 누적 세션 힙의 크기를 제한합니다.
권한 부여와 요청 정책. 빌더의 권한 부여는 모든 스크립트가 가질 수 있는 최대 접근 권한입니다. 요청별 SandboxPolicy은 그 범위 내에서만 축소할 수 있으며 — 권한 부여를 초과하는 요청은 코드가 실행되기 전에 초과 항목을 명시하는 ExecutionError::UnsupportedPolicy와 함께 기본 거부 방식으로 거부됩니다.
권한 부여는 해당 디렉터리 하위 트리 전체에 적용됩니다. 권한이 부여된 마운트 또는 그 하위 디렉터리를 요청하면 성공하며, 유효한 마운트는 요청된 경로이고 이에 대응하는 호스트 하위 디렉터리가 백업으로 사용됩니다. 실행기가 제공하는 항목을 정확히 요청하려면 granted_policy()을 사용하세요.
REPL 세션의 유효 정책은 호출 간에 변경되지 않아야 합니다. 세션에 설정된 정책과 다른 정책을 사용하는 호출은 restart()에 대한 안내와 함께 거부됩니다.
호스트 함수
등록된 Rust 함수(동기 또는 비동기)는 호출 가능한 Python 함수가 되며, 스크립트에서 이름만으로 사용할 수 있습니다.
use adk_code::MontyExecutorBuilder;
use serde_json::json;
let executor = MontyExecutorBuilder::new()
.function_fn("row_count", "Count rows in the loaded dataset.", |args, _kwargs| async move {
Ok(json!(args.len()))
})
.build_one_shot()?;
전체 trait 형식을 사용하려면 HostFunction(name, description, LLM 프롬프트를 위한 선택적 signature, 그리고 JSON로 변환된 위치 인수 및 키워드 인수를 받는 비동기 call)을 구현하세요. 레지스트리 검증은 build_*()에서 수행됩니다. 이름은 유효한 Python 식별자여야 하고, 서로 중복되지 않아야 하며, Python 내장 함수와 충돌해서는 안 됩니다.
스크립트 내부에서는 호스트 함수를 동기적으로 호출하며 — 절대로 await과 함께 호출하지 않습니다. 반환된 Err는 해당 메시지를 담은 포착 가능한 Python 예외가 됩니다. 등록되지 않은 이름을 호출하면 등록된 이름을 나열하는 수정 안내 예외가 발생합니다. 호스트 함수 실행에는 자체 wall-clock 제한(host_function_timeout, 기본값 30초)이 있으므로, 멈춘 함수가 execute()을 방해할 수 없습니다.
참고: 호스트 함수는 호스트 코드로 실행됩니다. 호스트 함수는 Monty가 아닌 사용자의 자체 신뢰 경계입니다. 인터프리터 샌드박스는 호스트 함수의 부작용을 제한하지 않습니다.
자체 설명형 실행기
두 실행기는 모두 CodeExecutor::prompt_snippet()를 구현하여 빌드된 기능을 렌더링합니다. 여기에는 모드 의미 체계, 액세스 수준이 포함된 파일 시스템 루트, 환경 변수 이름(값은 렌더링되지 않음), 시계 사용 가능 여부, 네트워크 및 서브프로세스가 없다는 보장, 출력 계약, 등록된 호스트 함수에 대한 Python 스텁 블록이 포함됩니다. MontyPythonCodeTool는 이 스니펫을 LLM 대상 설명에 추가하므로, 프롬프트와 인터프리터 내부 동작은 동일한 구성에서 파생되며 서로 어긋날 수 없습니다.
MontyPythonCodeTool
에이전트 대상 도구(monty_python_code, 범위 code:execute)는 JavaScriptCodeTool을 그대로 따릅니다. 즉, 오류를 정보로 취급하는 JSON, camelCase 출력 키, 기능이 비활성화된 경우의 구조화된 "rejected" 대체 동작을 사용합니다.
use adk_code::PathAccess;
use adk_tool::MontyPythonCodeTool;
use serde_json::json;
use std::sync::Arc;
let tool = MontyPythonCodeTool::builder()
.allow_path("/out", "/srv/agent/out", PathAccess::ReadWrite)
.environ_var("PROJECT", "acme")
.system_clock()
.function_fn("get_weather", "Current weather for a city.", |args, _kwargs| async move {
Ok(json!({ "temp_c": 21 }))
})
.build_repl()?;
let agent = LlmAgentBuilder::new("data_agent")
.instruction("Use monty_python_code for calculations and data work.")
.model(model)
.tool(Arc::new(tool))
.build()?;
MontyPythonCodeTool::new()는 완전히 샌드박스된 일회성 도구를 구축하고, MontyPythonCodeTool::repl()는 완전히 샌드박스된 REPL 도구를 구축합니다.
세션 범위 지정
REPL 모드에서 인터프리터 세션은 전체 ADK 세션 식별자(앱 이름, 사용자 ID, 세션 ID)를 기준으로 지정되므로, 여러 사용자 간에 세션 ID 문자열이 반복되더라도 상태가 사용자 간에 유출되지 않습니다. 모든 세션은 동일한 권한 부여와 호스트 함수 레지스트리를 공유하며, 세션별로 유지되는 것은 인터프리터 상태뿐입니다. 세션 맵은 LRU 제한(max_sessions, 기본값 100)으로 크기가 제한됩니다. 0은 1로 처리되며, 제거된 세션의 다음 호출에서는 투명하게 새 인터프리터가 시작됩니다.
도구 인수
| 인수 | 유형 | 설명 |
|---|---|---|
code | 문자열(필수) | 실행할 Python 소스 |
input | 모든 유형 | input 변수에 바인딩할 선택적 JSON 값 |
timeout_secs | 정수 | 인터프리터 시간 예산(기본값 30, 1~300으로 제한됨) |
reset | 부울 | REPL 모드 전용: 실행하기 전에 영구 세션 삭제 |
출력 엔벨로프
{ "status": "success", "stdout": "", "stderr": "", "output": {"n": 42},
"stdoutTruncated": false, "stderrTruncated": false, "durationMs": 3 }
exitCode는 없습니다 — 실행은 프로세스 내부에서 이루어지며, 별도의 프로세스가 생성되지 않습니다.
status는 성공/실패 신호입니다. stdoutTruncated / stderrTruncated는 캡처된 출력이 샌드박스 정책의 바이트 제한(각각 기본값 1MB)에 도달하여 잘렸을 때 이를 보고합니다.
스크립트의 마지막 표현식 값은 output로 반환되며, print()
출력은 stdout로 캡처됩니다. 실패 상태: "failed" (Python
예외 — 호스트 함수에서 발생한 예외를 포함하여 트레이스백은 stderr에 있음), "timeout" (시간 예산 초과), "rejected" (잘못된 인수 또는 기능 비활성화). ToolError는 절대 발생하지 않습니다.
엔벨로프는 두 모드에서 동일하게 고정되므로, 도구는 이를
Tool::response_schema()를 통해 선언합니다 — 응답 스키마를 제공하는 프로바이더는 parameters와 함께 도구 선언에 이를 포함합니다.
CodeAct와의 관계
CodeActAgent + adk-codeact-monty 경로 역시 Monty를 통해 Python을 실행하지만,
스크립트 내부에서 ADK Tool 디스패치를 수행하고(call_tool(...)),
에이전트 턴 사이에서 일시 중지 및 재개됩니다. MontyPythonCodeTool는
두 가지 모두를 의도적으로 제외합니다 — 호스트 함수 레지스트리를 확장 지점으로 사용하는 독립적인 코드 실행 도구입니다. 자세한 내용은
코딩 에이전트를 참조하세요.
CodeAct.
예제
examples/monty_python_code_tool는 읽기-쓰기 마운트, 환경 변수 및 등록된 호스트 함수가 구성된 REPL 모드
LlmAgent를 실행하여, 여러 턴에 걸친 변수 지속성과 모델이 작성한 Python에서의 호스트 함수 호출을 보여 줍니다. MontyPythonCodeTool