[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
---
|
||||
id: ai-code-interpreter-sandbox
|
||||
title: Code Interpreter — Sandbox / E2B / Daytona
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [ai, code-execution, sandbox, e2b, vibe-coding]
|
||||
tech_stack: { language: "TS / Python / Sandbox", applicable_to: ["Backend"] }
|
||||
applied_in: []
|
||||
aliases: [code interpreter, E2B, Daytona, sandbox, jupyter, python execution]
|
||||
---
|
||||
|
||||
# Code Interpreter
|
||||
|
||||
> LLM 이 작성한 코드를 안전 실행. **격리된 sandbox** 가 핵심. E2B / Daytona / Modal / Cloudflare Containers / 자체 K8s. OpenAI Code Interpreter, Anthropic Bash tool 도 같은 패턴.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- Sandbox: 격리 + 자원 제한 + 시간 제한.
|
||||
- Stateful: 한 세션 안 변수 유지 (Jupyter kernel).
|
||||
- File I/O: upload + 결과 download.
|
||||
- Streaming output.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### E2B Code Interpreter
|
||||
```ts
|
||||
import { Sandbox } from '@e2b/code-interpreter';
|
||||
|
||||
const sb = await Sandbox.create({ apiKey: process.env.E2B_API_KEY });
|
||||
|
||||
const exec = await sb.runCode(`
|
||||
import pandas as pd
|
||||
df = pd.read_csv('/data/sales.csv')
|
||||
df.groupby('region').sum()
|
||||
`);
|
||||
|
||||
console.log(exec.text); // stdout
|
||||
console.log(exec.results); // 차트, table 등 rich result
|
||||
console.log(exec.error);
|
||||
|
||||
await sb.kill();
|
||||
```
|
||||
|
||||
### File upload / download
|
||||
```ts
|
||||
await sb.files.write('/data/sales.csv', csvBuffer);
|
||||
|
||||
const out = await sb.runCode(`
|
||||
import matplotlib.pyplot as plt
|
||||
df.plot()
|
||||
plt.savefig('/out/chart.png')
|
||||
`);
|
||||
|
||||
const png = await sb.files.read('/out/chart.png', 'binary');
|
||||
```
|
||||
|
||||
### Streaming
|
||||
```ts
|
||||
const exec = await sb.runCode(longTask, {
|
||||
onStdout: (line) => stream.write(line),
|
||||
onStderr: (line) => stream.write(line),
|
||||
});
|
||||
```
|
||||
|
||||
### LLM tool 로 wrap
|
||||
```ts
|
||||
const tools = [{
|
||||
name: 'execute_python',
|
||||
description: 'Execute Python code in a sandbox. Files in /data/. Save outputs to /out/. State persists between calls in the same conversation.',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: { code: { type: 'string' } },
|
||||
required: ['code'],
|
||||
},
|
||||
}];
|
||||
|
||||
async function executeTool(name: string, input: { code: string }) {
|
||||
if (name === 'execute_python') {
|
||||
const r = await sb.runCode(input.code);
|
||||
return JSON.stringify({
|
||||
stdout: r.text.slice(-2000),
|
||||
error: r.error?.value,
|
||||
results: r.results.map(x => ({ type: x.type, ...x })),
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 자원 제한
|
||||
```ts
|
||||
const sb = await Sandbox.create({
|
||||
template: 'code-interpreter-v1',
|
||||
timeoutMs: 60_000, // 세션 최대 1분
|
||||
cpuCount: 1,
|
||||
memoryMB: 512,
|
||||
});
|
||||
|
||||
const exec = await sb.runCode(code, { timeoutMs: 30_000 }); // 1 cell 30초
|
||||
```
|
||||
|
||||
### Persistent state (Jupyter)
|
||||
```ts
|
||||
await sb.runCode('x = 42');
|
||||
const r = await sb.runCode('print(x * 2)'); // 84
|
||||
```
|
||||
|
||||
### 자체 sandbox (Docker)
|
||||
```dockerfile
|
||||
FROM python:3.12-slim
|
||||
RUN useradd -m sandbox
|
||||
USER sandbox
|
||||
WORKDIR /home/sandbox
|
||||
RUN pip install pandas numpy matplotlib
|
||||
```
|
||||
|
||||
```ts
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
async function runInDocker(code: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const p = spawn('docker', [
|
||||
'run', '--rm',
|
||||
'--network', 'none',
|
||||
'--memory', '512m',
|
||||
'--cpus', '0.5',
|
||||
'-i', 'python:3.12-slim',
|
||||
'python', '-c', code,
|
||||
]);
|
||||
let out = ''; let err = '';
|
||||
p.stdout.on('data', (d) => { out += d.toString(); });
|
||||
p.stderr.on('data', (d) => { err += d.toString(); });
|
||||
p.on('close', (c) => c === 0 ? resolve(out) : reject(new Error(err)));
|
||||
setTimeout(() => p.kill(), 30_000);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
⚠️ Docker 자체로 sandbox 부족 — gVisor / firecracker / E2B 권장.
|
||||
|
||||
### 출력 안전 처리
|
||||
```ts
|
||||
function sanitizeOutput(s: string): string {
|
||||
return s
|
||||
.replace(/[\x00-\x08\x0b-\x1f]/g, '') // control chars
|
||||
.slice(0, 10_000); // 길이 제한
|
||||
}
|
||||
```
|
||||
|
||||
### 결과 caching (같은 코드)
|
||||
```ts
|
||||
const key = sha256(code + ':' + dataHash);
|
||||
const cached = await cache.get(key);
|
||||
if (cached) return cached;
|
||||
const r = await sb.runCode(code);
|
||||
await cache.set(key, r);
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 사용 | 추천 |
|
||||
|---|---|
|
||||
| ChatGPT-style data analysis | E2B Code Interpreter |
|
||||
| Long-running compute | Modal / Daytona |
|
||||
| Cloudflare 환경 | Cloudflare Containers |
|
||||
| Self-hosted K8s | Pod per session + gVisor |
|
||||
| 단순 calc | Python eval 위험 — math.js / sandbox js |
|
||||
| Untrusted user code (CTF, 강의) | gVisor / Firecracker / E2B |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`eval()` 직접**: RCE. sandbox 필수.
|
||||
- **Docker `--privileged`**: escape 가능.
|
||||
- **Network 허용 + 무제한**: SSRF / 외부 호출.
|
||||
- **Memory / CPU 무제한**: fork bomb / OOM.
|
||||
- **Output 무제한 → LLM 으로**: 다음 turn 비싸짐.
|
||||
- **Sandbox 재사용 cross-user**: state leak.
|
||||
- **Filesystem 무제한 write**: 디스크 채움.
|
||||
- **Timeout 없음**: 영원 hang.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- E2B / Daytona 가 가장 modern.
|
||||
- Sandbox = network none + memory limit + cpu limit + timeout.
|
||||
- Output truncate 후 LLM 으로.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[AI_Function_Calling_Deep]]
|
||||
- [[AI_Agentic_Patterns]]
|
||||
- [[Security_OWASP_Top_10_Practical]]
|
||||
Reference in New Issue
Block a user