코드 실행을 통한 증강
숫자 퍼즐 코드를 API로 생성하고, exec와 Monty 샌드박스에서 실행하는 방법 및 외부 샌드박스 서비스를 비교한다.
숫자 퍼즐의 모든 해 찾기
1부터 9까지의 수에서 서로 다른 세 수를 골라, 합이 15가 되는 모든 조합을 찾아라.
- 순서만 다른 조합은 하나로 취급. 예:
[1, 5, 9]와[9, 1, 5]는 같은 조합. - 몇 가지 답을 떠올리는 데서 끝내지 않고, 가능한 경우를 빠짐없이 검사하는 것이 목표.
- 모델이 탐색 코드를 작성하고, 파이썬이 실제로 실행.
Mermaid 다이어그램 원문
sequenceDiagram
autonumber
participant L as LLM
participant A as 애플리케이션
participant E as 실행기
A->>L: 작업 요청과 코드 작성 지시
L-->>A: 생성한 코드
A->>E: 코드와 입력 자료
E-->>A: 실행 결과
A->>L: 실행 결과
L-->>A: 최종 답변대화형 다이어그램을 불러오기 전에는 원문으로 표시됩니다.
- LLM의 코드 생성 → 실행기의 코드 실행 → 실행 결과를 바탕으로 한 LLM의 최종 답변 순서.
- 아래 실습은 코드 생성·실행·결과 검증까지 진행. 그림의 마지막 두 단계는 실행 결과를 모델에 전달해 답변으로 정리하는 확장 흐름.
- 실행기는
exec()또는 Monty.exec()는 실행 후 변수를 읽고, Monty는 마지막 표현식의 값을 반환.
API로 탐색 코드 작성하기
- 함수 호출 실습에서 준비한
client,model,json,Monty를 같은 노트북에서 이어 사용. 별도 자료 파일 불필요.
target = 15 # 세 수의 합으로 만들 목표값
- 목표값은
target변수로 전달. 답을 미리 적는 대신 가능한 조합을 검사하도록 요청. - 결과 형식: 조합마다 세 수를 오름차순으로 정렬한 리스트. 전체 결과는
result에 저장.
task = f"""
1부터 9까지 서로 다른 세 수를 골라 합이 {target}인 모든 조합을 찾는 파이썬 코드를 작성해라.
- 실행 환경에 target 변수가 주어진다. 코드에서 target을 덮어쓰지 말고 사용해라.
- 가능한 조합을 직접 검사하고 순서만 다른 조합은 중복해서 넣지 마라.
- 각 조합은 오름차순인 세 정수의 리스트, 전체 result는 조합들을 오름차순 정렬한 리스트로 저장해라.
- 답을 미리 적지 말고, 파이썬 기본 기능만 사용해라.
- import, 파일·네트워크 접근, 명령어 실행, print는 쓰지 마라.
- code 키에 코드 문자열을 넣은 JSON 객체로만 응답해라. 코드 펜스는 넣지 마라.
"""
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "숫자 퍼즐을 푸는 파이썬 코드를 JSON으로 반환하세요."},
{"role": "user", "content": task},
],
response_format={"type": "json_object"},
)
code = json.loads(response.choices[0].message.content)["code"]
print(code) # 생성된 여러 줄 코드를 읽기 위해 출력
json.loads(): JSON 응답에서 코드 문자열을 꺼내기 위한 변환.- 확인: 서로 다른 세 수를 검사하는가? 합을
target과 비교하는가? 중복 조합을 제외하는가? - API가 코드를 반환한 시점에는 아직 퍼즐을 탐색하지 않은 상태.
방법 1: exec로 실행하기
| 함수 | 용도 |
|---|---|
eval() |
1 + 2 같은 단일 표현식을 평가해 값 반환 |
exec() |
대입·반복문·조건문을 포함하는 여러 줄 프로그램 실행 |
- 이번에는 반복해서 조합을 검사하는 프로그램이므로
exec()사용. execution: 코드에 전달할 변수와 실행 후 만들어진 변수를 보관하는 사전.
execution = {"target": target}
exec(code, execution)
result = execution["result"]
result
exec()자체의 반환값은None. 생성 코드가 만든result를 실행 공간에서 꺼내야 함.- 모델이 글로 제시한 답 대신 실제 탐색 결과를 확인하는 단계.
방법 2: Monty 샌드박스에서 실행하기
- 샌드박스(sandbox): 실행 코드가 접근할 기능과 사용할 자원을 제한한 환경.
- Monty: 제한된 파이썬을 실행하는 별도 실행기. 파일 마운트·호스트 함수를 제공하지 않으면 호스트의 파일·환경 변수·네트워크에 접근할 수 없음.
- OS 전체를 격리하는 가상머신과는 구분. 외부 패키지를 그대로 실행하는 환경은 아니지만 이번 퍼즐의 반복문·리스트는 사용 가능.
- 설치·불러오기는 함수 호출 실습의 공통 준비 코드에 포함. Monty 실행 자체에는 별도 서비스 API 키 불필요.
limits = {
"max_feed_duration_secs": 1, # 코드 실행 시간 제한, 초
"max_memory": 10_000_000, # 할당 메모리 제한, 바이트
}
같은 생성 코드에 같은 목표값을 전달해 실행.
with Monty(request_timeout=2) as sandbox: # 워커 응답 대기 제한, 초
with sandbox.checkout(limits=limits) as session:
monty_result = session.feed_run(
code + "\nresult", # 마지막 표현식의 값을 반환
inputs={"target": target},
)
monty_result
inputs: 실행 코드에 전달할 변수. 앞선execution사전에 넣은 목표값과 같은 역할.feed_run(): 코드를 실행하고 마지막 표현식의 값을 반환.result를 덧붙여 생성 코드가 만든 결과를 꺼냄.exec()와의 차이: 현재 파이썬의 권한으로 직접 실행하는 대신, Monty의 제한된 실행기에서 설정한 시간·메모리 범위 안에 실행.- 메모리 설정은 실행기의 할당량 제한이며 프로세스 전체 메모리 사용량의 상한은 아님. 코드의 계산 정확성은 별도로 확인.
출처: Monty 시작 안내, 보안 모델, 지원 범위.
결과 확인하기
- 아래는 비교를 위해 작성한 기준 코드. API가 생성한 코드의 실행 결과와 구분.
a < b < c가 되도록 검사하면 같은 수의 반복과 순서만 다른 조합을 제외 가능.
reference_code = """
result = []
for a in range(1, 10):
for b in range(a + 1, 10):
for c in range(b + 1, 10):
if a + b + c == target:
result.append([a, b, c])
"""
reference_execution = {"target": target}
exec(reference_code, reference_execution)
reference_result = reference_execution["result"]
reference_result
실행 결과
[[1, 5, 9],
[1, 6, 8],
[2, 4, 9],
[2, 5, 8],
[2, 6, 7],
[3, 4, 8],
[3, 5, 7],
[4, 5, 6]]
range(1, 10): 1부터 9까지 검사. 끝값 10은 제외.- 다음 수를 앞선 수보다 크게 고르므로 중복 없이 탐색.
append(): 조건을 만족한 조합을 결과 리스트에 추가.
len(reference_result) # 조건을 만족하는 조합 수
실행 결과
8
result == monty_result == reference_result # 두 실행 방식과 기준 결과 비교
API를 호출하지 않고도, 같은 기준 코드를 Monty에서 실행해 결과 비교 가능.
with Monty(request_timeout=2) as sandbox:
with sandbox.checkout(limits=limits) as session:
monty_reference = session.feed_run(
reference_code + "\nresult",
inputs={"target": target},
)
monty_reference == reference_result
실행 결과
True
- 조합의 개수뿐 아니라 내용도 비교. 같은 개수여도 누락된 해와 잘못된 해가 섞일 수 있음.
- 이번 문제에서 결과가 같다고 모든 숫자 퍼즐을 올바르게 푼다는 뜻은 아님.
목표값 바꾸기
- 목표값을 18로 바꾼 뒤 같은 생성 코드를 다시 실행. 새로운 코드를 작성하지 않고도 입력에 따라 다른 조합을 탐색.
target = 18
execution = {"target": target}
exec(code, execution)
execution["result"]
- 확인: 답을 코드에 미리 적었다면 목표값을 바꿔도 결과가 그대로일 수 있음.
- 역할 분담: 모델은 탐색 절차 작성, 파이썬은 가능한 경우 검사, 사람은 문제 조건과 결과 확인.
LLM용 샌드박스 서비스
- 코드를 작성하는 모델과 코드를 실행하는 서비스는 별개. 생성한 코드 문자열을 외부 실행 환경에 보내고 출력·파일·오류를 돌려받는 구조.
- NumPy·pandas 같은 외부 패키지나 파일 작업, 터미널이 필요한 경우에는 이런 실행 환경을 검토.
| 서비스 | 제공하는 실행 환경 | 활용 예 |
|---|---|---|
| E2B | Python SDK로 코드 실행과 파일 작업을 요청하는 샌드박스 | 모델이 만든 분석 코드를 실행하고 결과 회수 |
| Daytona | 코드·프로세스 실행, 파일·터미널 등을 제공하는 샌드박스 | 코딩 에이전트가 파일을 수정하고 프로그램 테스트 |
| Vercel Sandbox | Firecracker microVM으로 격리한 실행 환경 | 생성한 코드나 앱을 실행·검사 |
- 공통 흐름: 인증 → 실행 환경 생성 → 코드·자료 전달 → 실행 결과 회수 → 환경 정리.
- 서비스별 계정·인증·사용량 비용과 네트워크·파일 접근 정책 확인. 격리 환경이라는 이유만으로 인터넷 연결까지 차단됐다고 가정하지 않기.
- 이번 실습은
exec()와 Monty만 실행. 외부 서비스는 실행 환경을 선택하는 예시로 소개.
출처: Python exec·eval, OpenRouter 응답 형식.
퀴즈
이번 퍼즐에서 코드를 실행하는 목적은?
퀴즈를 풀려면 대화형 기능을 불러와야 합니다.