Docker-Java 完全指南
AI Agent 的关键基础设施工具
AI Agent 需要一双"手"来操作真实世界——运行代码、启动服务、隔离环境。Docker-Java 就是那双手。它让你的 Java Agent 能通过代码创建容器、执行命令、管理环境,像操控乐高积木一样编排基础设施。
← 第一篇:LangChain4j | 第二篇:LangGraph4j一、开篇概览
Docker-Java 是最成熟的 Java Docker 客户端库(GitHub 3.2k+ Stars),它把 Docker Engine 的所有 REST API 封装成了流畅的 Java API。通过它,你的 Java 程序可以像在命令行敲 docker 命令一样,完全控制 Docker——拉取镜像、创建容器、执行命令、管理网络和卷。
docker 做的事,都能用 Java 代码做。
AI Agent 为什么需要 Docker?
在前两篇教程中,我们学了如何用 LangChain4j 调用 LLM,用 LangGraph4j 编排复杂工作流。但 Agent 的真正价值在于执行动作——而最强大、最安全的执行方式就是在 Docker 容器中运行。
Agent 决策 → Docker 执行 → 安全隔离
Agent + Docker 的典型场景
- 代码沙箱:Agent 生成代码后在容器中安全执行
- 环境创建:Agent 按需启动数据库、缓存等服务
- 工具隔离:每个 Tool 运行在独立容器,互不干扰
- 批量任务:Agent 并行启动多个容器处理数据
- 自愈系统:Agent 监控容器健康,自动重建故障服务
不用 Docker 的风险
- Agent 执行的代码直接跑在宿主机,安全隐患大
- 环境依赖混乱,不同任务互相冲突
- 没有资源限制,恶意代码可能耗尽系统资源
- 难以清理,残留进程和文件污染系统
✅ 本节小结
- Docker-Java 让 Java 程序完全控制 Docker Engine
- AI Agent + Docker = 安全、隔离、可控的代码执行环境
- 这是构建生产级 Agent 的关键基础设施能力
二、环境搭建
docker info 可以正常执行)JDK 11+(推荐 17+)
Maven 或 Gradle
Docker-Java 采用模块化设计。核心包 + 传输层实现可以灵活组合:
Maven 依赖(推荐组合)
<properties>
<docker-java.version>3.4.1</docker-java.version>
</properties>
<dependencies>
<!-- 核心 API -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-core</artifactId>
<version>${docker-java.version}</version>
</dependency>
<!-- 传输层:Apache HttpClient 5(推荐) -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>${docker-java.version}</version>
</dependency>
</dependencies>
或者用一键全包引入(包含所有传输层):
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>${docker-java.version}</version>
</dependency>
Gradle 依赖
dependencies {
implementation 'com.github.docker-java:docker-java-core:3.4.1'
implementation 'com.github.docker-java:docker-java-transport-httpclient5:3.4.1'
}
验证安装——30 秒看到结果:
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Info;
import com.github.dockerjava.core.DockerClientBuilder;
public class HelloDocker {
public static void main(String[] args) {
// 自动检测本地 Docker(Unix Socket 或 TCP)
DockerClient docker = DockerClientBuilder.getInstance().build();
// 获取 Docker 信息(相当于 docker info)
Info info = docker.infoCmd().exec();
System.out.println("Docker 版本: " + info.getServerVersion());
System.out.println("容器数量: " + info.getContainers());
System.out.println("镜像数量: " + info.getImages());
}
}
✅ 本节小结
- 核心依赖
docker-java-core+ 传输层实现 DockerClientBuilder.getInstance().build()自动连接本地 Docker- docker-java 的 API 与 Docker CLI 命令一一对应
三、核心概念
DockerClient — 遥控器
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
// 方式 1:最简单——自动检测配置
DockerClient docker = DockerClientBuilder.getInstance().build();
// 方式 2:显式配置(连接远程 Docker)
var config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost("tcp://192.168.1.100:2375") // 远程地址
.withDockerTlsVerify(true) // TLS 验证
.withDockerCertPath("/home/user/.docker/certs") // 证书路径
.build();
var httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.build();
DockerClient docker = DockerClientImpl.getInstance(config, httpClient);
命令模式 API
Docker-Java 的所有操作都遵循同一个模式:docker.xxxCmd().withYyy(value).exec()
| Docker CLI | Docker-Java API |
|---|---|
docker pull nginx | docker.pullImageCmd("nginx").exec(...) |
docker create --name web nginx | docker.createContainerCmd("nginx").withName("web").exec() |
docker start web | docker.startContainerCmd("web").exec() |
docker exec web ls / | docker.execCreateCmd("id").withCmd("ls","/").exec() |
docker stop web | docker.stopContainerCmd("web").exec() |
docker rm web | docker.removeContainerCmd("web").exec() |
✅ 本节小结
DockerClient是所有操作的入口- 所有 API 遵循
xxxCmd().withXxx().exec()流畅模式 - 支持本地 Unix Socket 和远程 TCP 连接
四、镜像管理
场景:Agent 需要准备执行环境——先确保需要的镜像存在,不存在就拉取。
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.model.Image;
// 拉取镜像(阻塞等待完成)
docker.pullImageCmd("python")
.withTag("3.12-slim")
.exec(new PullImageResultCallback())
.awaitCompletion(); // 等待拉取完成
System.out.println("Python 镜像拉取完成!");
// 列出本地镜像
List<Image> images = docker.listImagesCmd().exec();
for (Image img : images) {
System.out.println(Arrays.toString(img.getRepoTags())
+ " - " + img.getSize() / 1024 / 1024 + "MB");
}
// 搜索 Docker Hub 上的镜像
var results = docker.searchImagesCmd("ubuntu").exec();
results.forEach(r -> System.out.println(r.getName() + " ⭐" + r.getStarCount()));
// 删除镜像
docker.removeImageCmd("python:3.12-slim").exec();
带进度回调的镜像拉取
import com.github.dockerjava.api.model.PullResponseItem;
import com.github.dockerjava.api.async.ResultCallback;
docker.pullImageCmd("node").withTag("20-alpine")
.exec(new ResultCallback.Adapter<PullResponseItem>() {
@Override
public void onNext(PullResponseItem item) {
if (item.getStatus() != null) {
System.out.println(item.getStatus()
+ (item.getProgress() != null ? " " + item.getProgress() : ""));
}
}
})
.awaitCompletion();
✅ 本节小结
pullImageCmd()拉取镜像,awaitCompletion()等待完成listImagesCmd()列出本地镜像- 支持进度回调,可以展示下载进度
五、容器生命周期
场景:Agent 决定运行一段 Python 代码——需要创建容器、启动、等待执行完毕、获取结果、最后清理。
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.*;
// 1. 创建容器
CreateContainerResponse container = docker.createContainerCmd("python:3.12-slim")
.withName("agent-sandbox-001")
.withCmd("python", "-c", "print('Hello from Agent sandbox!')")
// 资源限制(Agent 安全必备!)
.withHostConfig(HostConfig.newHostConfig()
.withMemory(256 * 1024 * 1024L) // 内存上限 256MB
.withCpuCount(1L) // 最多 1 个 CPU
.withNetworkMode("none") // 禁止网络访问
.withAutoRemove(true)) // 执行完自动删除
.exec();
String containerId = container.getId();
System.out.println("容器已创建: " + containerId.substring(0, 12));
// 2. 启动容器
docker.startContainerCmd(containerId).exec();
// 3. 等待容器执行完毕
int exitCode = docker.waitContainerCmd(containerId)
.exec(new WaitContainerResultCallback())
.awaitStatusCode();
System.out.println("退出码: " + exitCode); // 0 表示成功
容器完整生命周期管理
// 查看运行中的容器
List<Container> running = docker.listContainersCmd()
.withShowAll(false) // 仅运行中的
.exec();
// 查看所有容器(包括已停止的)
List<Container> all = docker.listContainersCmd()
.withShowAll(true)
.exec();
// 暂停 / 恢复容器
docker.pauseContainerCmd(containerId).exec();
docker.unpauseContainerCmd(containerId).exec();
// 停止容器(发送 SIGTERM,10 秒后 SIGKILL)
docker.stopContainerCmd(containerId).withTimeout(10).exec();
// 强制杀死容器
docker.killContainerCmd(containerId).exec();
// 删除容器
docker.removeContainerCmd(containerId)
.withForce(true) // 强制删除运行中的容器
.withRemoveVolumes(true) // 同时删除关联的卷
.exec();
// 检查容器详情
var info = docker.inspectContainerCmd(containerId).exec();
System.out.println("状态: " + info.getState().getStatus());
System.out.println("IP: " + info.getNetworkSettings()
.getNetworks().values().iterator().next().getIpAddress());
一定要设置超时:
withStopTimeout() 防止容器永久运行。推荐
withAutoRemove(true):容器停止后自动清理,避免残留。推荐
withNetworkMode("none"):除非必要,否则禁止容器访问网络。
✅ 本节小结
- 容器生命周期:create → start → (exec) → stop → remove
HostConfig设置资源限制、网络、自动清理waitContainerCmd()阻塞等待容器结束并获取退出码
六、容器内执行命令(Exec)
场景:Agent 让一个长期运行的容器(如一个开发环境沙箱)执行各种命令——安装依赖、运行脚本、检查结果。这是 Agent 最核心的能力。
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.core.command.ExecStartResultCallback;
// 假设容器已在运行
String containerId = "abc123...";
// 1. 创建 exec 实例
ExecCreateCmdResponse exec = docker.execCreateCmd(containerId)
.withCmd("sh", "-c", "echo 'Hello' && ls /usr && whoami")
.withAttachStdout(true) // 捕获标准输出
.withAttachStderr(true) // 捕获错误输出
.exec();
// 2. 执行并收集输出
StringBuilder stdout = new StringBuilder();
StringBuilder stderr = new StringBuilder();
docker.execStartCmd(exec.getId())
.exec(new ResultCallback.Adapter<Frame>() {
@Override
public void onNext(Frame frame) {
String text = new String(frame.getPayload());
switch (frame.getStreamType()) {
case STDOUT -> stdout.append(text);
case STDERR -> stderr.append(text);
}
}
})
.awaitCompletion();
System.out.println("输出:\n" + stdout);
if (!stderr.isEmpty()) {
System.err.println("错误:\n" + stderr);
}
// 3. 检查执行结果
var execInfo = docker.inspectExecCmd(exec.getId()).exec();
System.out.println("退出码: " + execInfo.getExitCodeLong());
封装为实用工具方法
在 Agent 中你会频繁执行命令,封装一个工具类很有必要:
public class DockerExecUtil {
public record ExecResult(String stdout, String stderr, Long exitCode) {
public boolean isSuccess() { return exitCode != null && exitCode == 0; }
}
public static ExecResult exec(DockerClient docker, String containerId,
String... command) throws InterruptedException {
var execCreate = docker.execCreateCmd(containerId)
.withCmd(command)
.withAttachStdout(true)
.withAttachStderr(true)
.exec();
var stdout = new StringBuilder();
var stderr = new StringBuilder();
docker.execStartCmd(execCreate.getId())
.exec(new ResultCallback.Adapter<Frame>() {
@Override
public void onNext(Frame frame) {
String text = new String(frame.getPayload());
switch (frame.getStreamType()) {
case STDOUT -> stdout.append(text);
case STDERR -> stderr.append(text);
}
}
})
.awaitCompletion();
var info = docker.inspectExecCmd(execCreate.getId()).exec();
return new ExecResult(stdout.toString(), stderr.toString(),
info.getExitCodeLong());
}
}
// 使用
var result = DockerExecUtil.exec(docker, containerId,
"python", "-c", "print(2 + 2)");
System.out.println(result.stdout()); // "4\n"
System.out.println(result.isSuccess()); // true
带超时的命令执行
import java.util.concurrent.TimeUnit;
// Agent 执行用户代码必须设置超时
docker.execStartCmd(exec.getId())
.exec(new ResultCallback.Adapter<Frame>() { /* ... */ })
.awaitCompletion(30, TimeUnit.SECONDS); // 最多等 30 秒
// 如果超时返回 false
boolean finished = docker.execStartCmd(exec.getId())
.exec(callback)
.awaitCompletion(10, TimeUnit.SECONDS);
if (!finished) {
System.err.println("命令执行超时!");
// 可以考虑 kill 容器
docker.killContainerCmd(containerId).exec();
}
✅ 本节小结
- Exec = 两步操作:
execCreateCmd()+execStartCmd() - 通过
Frame回调分别捕获 stdout 和 stderr - 务必使用
awaitCompletion(timeout)防止 Agent 卡死
七、日志与监控
场景:Agent 启动一个长运行服务后,需要监控其日志来判断是否正常工作。
import com.github.dockerjava.api.async.ResultCallback;
// 获取容器日志(一次性获取)
StringBuilder logs = new StringBuilder();
docker.logContainerCmd(containerId)
.withStdOut(true)
.withStdErr(true)
.withTail(100) // 最后 100 行
.withTimestamps(true) // 包含时间戳
.exec(new ResultCallback.Adapter<Frame>() {
@Override
public void onNext(Frame frame) {
logs.append(new String(frame.getPayload()));
}
})
.awaitCompletion();
System.out.println(logs);
// 实时流式日志(Follow 模式)
docker.logContainerCmd(containerId)
.withStdOut(true)
.withStdErr(true)
.withFollowStream(true) // 持续跟踪
.withSince(0)
.exec(new ResultCallback.Adapter<Frame>() {
@Override
public void onNext(Frame frame) {
System.out.print("[实时] " + new String(frame.getPayload()));
}
});
// 注意:Follow 模式不会自动结束,需要自行 close
监控容器资源使用(Stats)
import com.github.dockerjava.api.model.Statistics;
// 获取容器实时资源统计
docker.statsCmd(containerId)
.exec(new ResultCallback.Adapter<Statistics>() {
@Override
public void onNext(Statistics stats) {
long memUsage = stats.getMemoryStats().getUsage();
long memLimit = stats.getMemoryStats().getLimit();
double memPercent = (double) memUsage / memLimit * 100;
System.out.printf("内存: %.1f MB / %.1f MB (%.1f%%)%n",
memUsage / 1e6, memLimit / 1e6, memPercent);
}
});
✅ 本节小结
logContainerCmd()获取容器日志,支持 tail 和实时 followstatsCmd()实时监控容器的 CPU 和内存使用- Agent 可以通过分析日志来判断任务状态
八、网络与卷
卷(Volume)——数据持久化
// 创建卷
var volume = docker.createVolumeCmd()
.withName("agent-workspace")
.exec();
// 创建容器时挂载卷
docker.createContainerCmd("python:3.12-slim")
.withHostConfig(HostConfig.newHostConfig()
.withBinds(new Bind(
"/host/path/data", // 宿主机路径
new Volume("/workspace"), // 容器内路径
AccessMode.rw)) // 读写权限
)
.exec();
// 使用命名卷(推荐)
docker.createContainerCmd("python:3.12-slim")
.withHostConfig(HostConfig.newHostConfig()
.withBinds(Bind.parse("agent-workspace:/workspace:rw"))
)
.exec();
// 删除卷
docker.removeVolumeCmd("agent-workspace").exec();
网络(Network)——容器互通
// 创建自定义网络
var network = docker.createNetworkCmd()
.withName("agent-network")
.withDriver("bridge")
.exec();
// 将容器连接到网络
docker.connectToNetworkCmd()
.withNetworkId("agent-network")
.withContainerId(containerId)
.exec();
// 同一网络中的容器可以通过容器名互访
// 例如:容器 A 可以用 http://container-b:8080 访问容器 B
✅ 本节小结
- Volume 用于数据持久化和宿主机-容器文件共享
- Network 让多个容器组成互联的微服务网络
- Agent 场景中,Volume 常用于共享工作区
九、文件传输
场景:Agent 生成代码后需要把代码文件传入容器执行,执行完再把结果文件拿出来。
import org.apache.commons.compress.archivers.tar.*;
import java.io.*;
// ===== 文件写入容器 =====
// Docker API 要求以 tar 归档格式传输文件
public static void copyToContainer(DockerClient docker, String containerId,
String content, String containerPath,
String fileName) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (TarArchiveOutputStream tar = new TarArchiveOutputStream(baos)) {
byte[] bytes = content.getBytes();
TarArchiveEntry entry = new TarArchiveEntry(fileName);
entry.setSize(bytes.length);
tar.putArchiveEntry(entry);
tar.write(bytes);
tar.closeArchiveEntry();
}
docker.copyArchiveToContainerCmd(containerId)
.withTarInputStream(new ByteArrayInputStream(baos.toByteArray()))
.withRemotePath(containerPath)
.exec();
}
// 使用:把 Agent 生成的 Python 代码传入容器
copyToContainer(docker, containerId,
"import math\nprint(math.pi)",
"/workspace",
"script.py");
// ===== 从容器读取文件 =====
InputStream stream = docker.copyArchiveFromContainerCmd(
containerId, "/workspace/output.txt")
.exec();
// 解析 tar 归档
try (TarArchiveInputStream tar = new TarArchiveInputStream(stream)) {
TarArchiveEntry entry;
while ((entry = tar.getNextEntry()) != null) {
if (!entry.isDirectory()) {
String content = new String(tar.readAllBytes());
System.out.println("文件内容: " + content);
}
}
}
copyArchiveToContainerCmd 传入 → execCreateCmd 执行 → copyArchiveFromContainerCmd 取回结果。这四步构成了完整的「沙箱代码执行」闭环。
✅ 本节小结
- Docker API 要求以 tar 格式传输文件
copyArchiveToContainerCmd()写入文件到容器copyArchiveFromContainerCmd()从容器读取文件
十、构建自定义镜像
场景:Agent 需要一个预装特定依赖的执行环境——直接从 Dockerfile 构建一个定制镜像。
import com.github.dockerjava.api.command.BuildImageResultCallback;
import java.io.File;
import java.util.Set;
// 假设 Dockerfile 在 ./sandbox-image/ 目录
String imageId = docker.buildImageCmd(new File("./sandbox-image/"))
.withTags(Set.of("agent-sandbox:latest"))
.withNoCache(true)
.exec(new BuildImageResultCallback() {
@Override
public void onNext(BuildResponseItem item) {
if (item.getStream() != null) {
System.out.print(item.getStream()); // 输出构建日志
}
super.onNext(item);
}
})
.awaitImageId();
System.out.println("镜像构建完成: " + imageId);
配套的 Dockerfile 示例——Agent 沙箱环境:
FROM python:3.12-slim
# 安装常用数据科学库
RUN pip install --no-cache-dir numpy pandas matplotlib requests
# 创建工作目录
WORKDIR /workspace
# 非 root 用户运行(安全)
RUN useradd -m sandbox
USER sandbox
CMD ["sleep", "infinity"]
✅ 本节小结
buildImageCmd()从 Dockerfile 构建镜像- Agent 可以预构建包含特定依赖的沙箱镜像
- 使用非 root 用户和最小权限原则提升安全性
十一、安全沙箱设计
场景:这是构建生产级 Agent 最关键的一章。当 Agent 执行 LLM 生成的代码时,你无法保证代码是安全的。必须从多个维度构建防护。
public class SecureSandbox {
private final DockerClient docker;
private final String sandboxImage;
public SecureSandbox(DockerClient docker, String sandboxImage) {
this.docker = docker;
this.sandboxImage = sandboxImage;
}
/**
* 创建一个安全的沙箱容器
*/
public String createSandbox() {
var container = docker.createContainerCmd(sandboxImage)
.withHostConfig(HostConfig.newHostConfig()
// 1. 内存限制(256MB,防止内存炸弹)
.withMemory(256 * 1024 * 1024L)
.withMemorySwap(256 * 1024 * 1024L) // 禁止 swap
// 2. CPU 限制(1 核心)
.withCpuCount(1L)
.withCpuPeriod(100000L)
.withCpuQuota(50000L) // 50% CPU
// 3. 禁止网络访问
.withNetworkMode("none")
// 4. 只读根文件系统
.withReadonlyRootfs(true)
// 但允许写入 /tmp 和 /workspace
.withTmpFs(Map.of(
"/tmp", "size=64m",
"/workspace", "size=128m"))
// 5. 禁止特权提升
.withPrivileged(false)
.withCapDrop(Capability.ALL) // 移除所有 Linux 能力
// 6. 限制进程数量(防止 fork 炸弹)
.withPidsLimit(50L)
// 7. 自动清理
.withAutoRemove(true)
)
.withWorkingDir("/workspace")
.exec();
docker.startContainerCmd(container.getId()).exec();
return container.getId();
}
/**
* 在沙箱中安全执行代码
*/
public ExecResult executeCode(String containerId, String code,
String language, int timeoutSeconds)
throws Exception {
// 写入代码文件
String fileName = switch (language) {
case "python" -> "script.py";
case "javascript" -> "script.js";
case "bash" -> "script.sh";
default -> "script.txt";
};
copyToContainer(docker, containerId, code, "/workspace", fileName);
// 构建执行命令
String[] cmd = switch (language) {
case "python" -> new String[]{"python", "/workspace/script.py"};
case "javascript" -> new String[]{"node", "/workspace/script.js"};
case "bash" -> new String[]{"sh", "/workspace/script.sh"};
default -> throw new IllegalArgumentException("不支持的语言: " + language);
};
// 带超时执行
return DockerExecUtil.execWithTimeout(docker, containerId, cmd,
timeoutSeconds);
}
/**
* 销毁沙箱
*/
public void destroy(String containerId) {
try {
docker.killContainerCmd(containerId).exec();
} catch (Exception ignored) {}
try {
docker.removeContainerCmd(containerId).withForce(true).exec();
} catch (Exception ignored) {}
}
}
CPU 限制:防止挖矿或死循环耗尽资源
网络隔离:
networkMode("none") 除非明确需要只读文件系统:
readonlyRootfs(true) + tmpfs 白名单非 root 用户:在 Dockerfile 中切换用户
进程数限制:
pidsLimit(50) 防 fork 炸弹执行超时:所有命令必须有超时
自动清理:
autoRemove(true) 避免资源泄露
✅ 本节小结
- 安全沙箱是生产级 Agent 的核心基础设施
- 必须从内存、CPU、网络、文件系统、进程数多维度限制
- 封装为
SecureSandbox类,供 Agent Tool 调用
十二、综合实战:Code Execution Agent Tool
让我们把 Docker-Java 与 LangChain4j + LangGraph4j 集成起来,构建一个完整的 Agent 代码执行工具——AI 生成代码、Docker 安全执行、返回结果。
定义 LangChain4j Tool——让 Agent 可以执行代码
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.P;
public class CodeExecutionTool {
private final DockerClient docker;
private final String sandboxImage = "agent-sandbox:latest";
public CodeExecutionTool(DockerClient docker) {
this.docker = docker;
}
@Tool("在安全沙箱中执行 Python 代码并返回结果。用于数学计算、数据处理等任务。")
public String executePython(
@P("要执行的 Python 代码") String code) {
var sandbox = new SecureSandbox(docker, sandboxImage);
String containerId = null;
try {
// 创建沙箱
containerId = sandbox.createSandbox();
// 执行代码(30 秒超时)
var result = sandbox.executeCode(
containerId, code, "python", 30);
if (result.isSuccess()) {
return "执行成功:\n" + result.stdout();
} else {
return "执行失败(退出码 " + result.exitCode() + "):\n"
+ result.stderr();
}
} catch (Exception e) {
return "执行异常: " + e.getMessage();
} finally {
if (containerId != null) {
sandbox.destroy(containerId);
}
}
}
@Tool("在安全沙箱中执行 Bash 命令并返回结果")
public String executeBash(
@P("要执行的 Bash 命令") String command) {
var sandbox = new SecureSandbox(docker, sandboxImage);
String containerId = null;
try {
containerId = sandbox.createSandbox();
var result = sandbox.executeCode(
containerId, command, "bash", 15);
return result.isSuccess()
? result.stdout()
: "错误: " + result.stderr();
} catch (Exception e) {
return "执行异常: " + e.getMessage();
} finally {
if (containerId != null) sandbox.destroy(containerId);
}
}
}
组装 Agent——用 LangChain4j AI Services 或 LangGraph4j AgentExecutor
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
// 方式 A:用 LangChain4j AI Services
interface CodeAssistant {
@SystemMessage("""
你是一个编程助手。当用户需要计算、数据处理或验证代码时,
使用 executePython 工具在沙箱中运行代码。
始终先编写代码,然后执行,最后解释结果。
""")
String chat(String message);
}
var model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
var docker = DockerClientBuilder.getInstance().build();
CodeAssistant assistant = AiServices.builder(CodeAssistant.class)
.chatLanguageModel(model)
.chatMemory(MessageWindowChatMemory.withMaxMessages(20))
.tools(new CodeExecutionTool(docker))
.build();
// 测试
System.out.println(assistant.chat("计算前 100 个质数的和"));
// Agent 会:
// 1. 生成 Python 代码(筛选质数并求和)
// 2. 调用 executePython Tool → Docker 沙箱执行
// 3. 获取结果 → 组织自然语言回复
System.out.println(assistant.chat("用 matplotlib 画一个 sin 函数的图"));
方式 B:用 LangGraph4j AgentExecutor(ReACT 循环)
import org.bsc.langgraph4j.agentexecutor.AgentExecutor;
// 如果代码执行失败,ReACT Agent 会自动分析错误、修改代码、重试
var agent = AgentExecutor.builder()
.chatModel(model)
.toolsFromObject(new CodeExecutionTool(docker))
.build()
.compile();
for (var step : agent.stream(Map.of("messages",
"读取 /etc/os-release 文件内容,告诉我沙箱运行的是什么操作系统"))) {
System.out.println(step);
}
// Agent 会多次尝试:先用 Python,如果不行就用 Bash
// ReACT 循环让它能从错误中学习
LangChain4j(第一篇):AI Services、@Tool 注解、Chat Memory
LangGraph4j(第二篇):AgentExecutor、ReACT 循环、状态管理
Docker-Java(第三篇):安全沙箱、代码执行、文件传输
它们共同构成了一个能安全执行代码的 AI Agent。
✅ 本节小结
- 用
@Tool注解把 Docker 沙箱封装为 Agent 的执行能力 - AI Services 或 AgentExecutor 均可驱动代码执行
- ReACT 模式让 Agent 能从执行错误中自动修正
十三、速查表 / Cheat Sheet
连接与信息
| API | 用途 |
|---|---|
DockerClientBuilder.getInstance().build() | 自动连接本地 Docker |
docker.infoCmd().exec() | 获取 Docker 系统信息 |
docker.pingCmd().exec() | 检测 Docker 是否可用 |
docker.versionCmd().exec() | 获取 Docker 版本 |
镜像操作
| API | 对应 CLI |
|---|---|
docker.pullImageCmd("img").withTag("tag").exec(cb) | docker pull img:tag |
docker.listImagesCmd().exec() | docker images |
docker.removeImageCmd("img").exec() | docker rmi img |
docker.buildImageCmd(dir).withTags(set).exec(cb) | docker build -t tag . |
docker.searchImagesCmd("q").exec() | docker search q |
容器操作
| API | 对应 CLI |
|---|---|
docker.createContainerCmd("img").withName("n").exec() | docker create --name n img |
docker.startContainerCmd(id).exec() | docker start id |
docker.stopContainerCmd(id).exec() | docker stop id |
docker.killContainerCmd(id).exec() | docker kill id |
docker.removeContainerCmd(id).withForce(true).exec() | docker rm -f id |
docker.listContainersCmd().withShowAll(true).exec() | docker ps -a |
docker.inspectContainerCmd(id).exec() | docker inspect id |
docker.waitContainerCmd(id).exec(cb) | docker wait id |
Exec 与日志
| API | 对应 CLI |
|---|---|
docker.execCreateCmd(id).withCmd(...).exec() | docker exec id ...(创建) |
docker.execStartCmd(execId).exec(cb) | docker exec(执行) |
docker.logContainerCmd(id).withStdOut(true).exec(cb) | docker logs id |
docker.statsCmd(id).exec(cb) | docker stats id |
文件、网络、卷
| API | 对应 CLI |
|---|---|
docker.copyArchiveToContainerCmd(id).exec() | docker cp file id:/path |
docker.copyArchiveFromContainerCmd(id, path).exec() | docker cp id:/path . |
docker.createNetworkCmd().withName("n").exec() | docker network create n |
docker.createVolumeCmd().withName("v").exec() | docker volume create v |
十四、进阶路线图
Docker Compose 集成:编排多容器服务(数据库 + 后端 + 前端),Agent 可以管理完整应用栈
Testcontainers:Docker-Java 的兄弟项目,专为集成测试设计,Agent 可以用它来验证代码
容器健康检查:配置 Health Check,Agent 自动监控和修复故障服务
镜像缓存策略:预热常用镜像、多阶段构建优化,减少 Agent 冷启动时间
Kubernetes 集成:从 Docker 扩展到 K8s,Agent 管理集群级别的基础设施
安全审计:记录所有 Agent 的 Docker 操作,用于合规和事后分析
推荐资源
| 资源 | 链接 | 说明 |
|---|---|---|
| Docker-Java GitHub | github.com/docker-java | 源码与示例 |
| Docker Engine API | docs.docker.com/engine/api | 底层 REST API 文档 |
| Testcontainers | testcontainers.com | 基于 Docker 的集成测试框架 |
| Baeldung 教程 | Docker Guide for Java | 详细的 API 参考 |
🎓 系列教程第三篇完成!
三篇教程共同构成了 Java AI Agent 的完整技术栈:
LangChain4j(LLM 调用)+
LangGraph4j(工作流编排)+
Docker-Java(安全执行)
现在去构建你的 AI Agent 吧!