uni-agent LocalDeployment 里 published_port 和 runtime_port 的一个坑
uni-agent LocalDeployment 里 published_port 和 runtime_port 的一个坑
本文只讨论
uni-agentlocal deployment 中published_port和runtime_port的区别。Agent 的 tool calling、parser、训练流程暂时不展开,不影响理解本文主体问题。
铺垫
uni-agent 的 AgentEnv 可以先理解成一个 Agent 执行任务时使用的运行环境。它不是只负责保存几行配置,而是要准备一个可以执行命令、读写文件、安装依赖的 sandbox。
local deployment 下,这个 sandbox 通常由 Docker、Podman 或 Apptainer 启动。sandbox 里面会运行一个 swerex.server,外部的 RemoteRuntime 再通过 HTTP 请求调用它。也就是说,execute_bash、read_file 这些工具动作,最终不是直接在当前 Python 进程里执行,而是发给 sandbox 里的 runtime server。
大体流程如下:
1 | AgentEnv |
画成图大概是这样:
1 | +----------------------+ HTTP +-------------------------+ |
所以 local deployment 能否启动成功,不只取决于容器有没有起来,还取决于外部的 RemoteRuntime 能不能访问到 sandbox 里的 swerex.server。
这里就会碰到两个端口:
1 | runtime_port |
这两个字段如果只看名字,很容易混在一起。
Docker 的端口映射
先看一个最普通的 Docker 命令:
1 | docker run -p 4567:8000 xxx |
-p 4567:8000 表示:
1 | 宿主机 4567 -> 容器 8000 |
左边的 4567 是宿主机端口,右边的 8000 是容器内端口。容器里的服务监听 8000,但是宿主机上的程序访问它时,应该访问 4567。
也就是:
1 | curl http://127.0.0.1:4567 |
而不是:
1 | curl http://127.0.0.1:8000 |
后者是在访问宿主机自己的 8000 端口。如果宿主机上没有服务监听这个端口,请求自然不会通。
换成 Java 服务看
如果用 Spring Boot 举例,会更直观一点。
项目配置:
1 | server.port=8080 |
Docker 启动:
1 | docker run -p 18080:8080 my-spring-app |
这时候含义是:
- Spring Boot 在容器内监听
8080 - 宿主机暴露
18080 - 浏览器或其他外部 client 访问
18080 - Docker 把宿主机
18080转发到容器8080
流程如下:
1 | 浏览器 |
所以 8080 和 18080 都是正确端口,只是视角不同。服务端关心自己监听哪个端口,client 关心自己应该访问哪个端口。
回到 uni-agent
LocalDeployment 里的两个字段,可以先按这个方式理解:
| 字段 | 类比 Spring Boot | 含义 |
|---|---|---|
runtime_port |
容器内 server.port=8080 |
swerex.server 在 sandbox 内监听的端口 |
published_port |
docker run -p 18080:8080 左边的 18080 |
Docker/Podman 暴露给宿主机访问的端口 |
假设配置是:
1 | runtime_port = 8000 |
如果 RemoteRuntime 在宿主机上访问 sandbox,那么访问链路应该是:
1 | RemoteRuntime -> 127.0.0.1:4567 -> sandbox:8000 -> swerex.server |
这时 RemoteRuntimeConfig.port 应该是 4567。如果这里误用了 8000,就会变成访问:
1 | http://127.0.0.1:8000 |
这相当于让宿主机上的 client 直接访问容器内部端口,通常是不通的。
不能简单认为 Docker/Podman 永远用 published_port
上面的场景是 RemoteRuntime 在宿主机上访问 sandbox。但还有另一种情况:uni-agent 自己也在容器里运行,并且和 sandbox 容器处在同一个 Docker network。
这时候访问链路可能是:
1 | +-------------------------+ Docker network +-------------------------+ |
RemoteRuntime 可能直接访问 sandbox 容器 IP:
1 | http://172.18.0.9:8000 |
这个访问没有经过宿主机的 4567,而是在容器网络里直连 swerex.server。这种情况下,RemoteRuntimeConfig.port 应该使用 runtime_port,也就是 8000。
所以这里不能按 container runtime 类型判断端口。不是看到 Docker/Podman 就一律使用 published_port,还要看 RemoteRuntime 实际访问的 host 是什么。
判断方式
可以按访问地址来判断:
- 如果
RemoteRuntimeConfig.host是http://127.0.0.1、http://localhost,或者用户显式配置的 Docker host,通常应该使用published_port - 如果
RemoteRuntimeConfig.host是 sandbox 容器 IP,例如http://172.18.0.9,通常应该使用runtime_port
对应两种链路:
1 | 情况一:宿主机访问 |
关键不在 Docker 本身,而在 client 从哪里访问 sandbox。
测试里覆盖的两个场景
这里的测试不需要真正启动 Docker。要验证的是 LocalDeployment 生成的 RemoteRuntimeConfig 是否正确,而不是 Docker 的端口映射功能。
宿主机访问场景:
1 | runtime_port = 8000 |
期望结果:
1 | RemoteRuntimeConfig.host = http://127.0.0.1 |
如果 port 变成 8000,后续 health check 就会访问:
1 | http://127.0.0.1:8000 |
这就和 Docker 的 4567:8000 映射对不上。
容器网络访问场景:
1 | host = http://172.18.0.9 |
期望结果:
1 | RemoteRuntimeConfig.port = 8000 |
这两个例子放在一起看,边界就比较清楚了:published_port 和 runtime_port 没有谁天然更对,关键是 RemoteRuntime 实际从哪条路径访问 sandbox。宿主机入口走 published port,容器网络直连就走 runtime port。
和 Agent 的关系
如果只看表面,这只是 Docker 端口映射问题。但放在 AgentEnv 里,它影响的是工具执行链路。
一个能执行命令的 Agent,大体会经过这些步骤:
1 | 模型输出 tool call |
如果 runtime client 连接不到 sandbox,后面的 tool parser、observation、训练数据都不会进入正题。它表现出来可能像 Agent 工具不可用,但实际问题在 deployment/runtime 这一层。
这和业务系统里排接口问题很像。Controller、Service 写得没问题,不代表请求一定能到应用。Nginx、网关、端口映射、服务注册,其中任何一层配置错了,接口都会失败。
以后看到这种端口字段,先问三个问题
1 | 服务在哪监听? |
套到这次:
1 | 服务在哪监听? |
这样就能判断 RemoteRuntimeConfig.port 应该取 published_port 还是 runtime_port。
先记到这
runtime_port 更接近服务自身监听端口,published_port 更接近外部访问入口端口。哪个字段进入 RemoteRuntimeConfig.port,取决于 RemoteRuntimeConfig.host 是谁。
后面继续看 RemoteRuntime,它负责把 execute_bash 这类工具调用包装成 HTTP 请求。等这条链路看明白,再看 tool parser 和训练,会更容易接上。
春风十里扬州路,卷上珠帘总不如。

