news 2026/10/18 1:11:31

通过AgentScope在EKS部署远程沙盒与代理应用:TaoToken统一Key接入实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
通过AgentScope在EKS部署远程沙盒与代理应用:TaoToken统一Key接入实践

1. AgentScope 在 EKS 上跑远程沙盒,为什么鉴权会先崩

AgentScope Runtime 是一套面向 AI Agent 的全栈运行时,核心解决两件事:把 Agent 应用高效部署成服务,以及把工具执行放进安全隔离的沙盒里。它适合谁?适合已经在用 AgentScope 写 ReAct、多智能体协作,准备把本地能跑的 demo 搬到 EKS 集群上做远程沙盒执行和代理应用部署的团队。你搜 agentscope EKS 沙盒部署,大概率就是卡在“本地跑通了,上集群就各种连不上”。

我先把问题场景说清楚。AgentScope 的沙盒工具(Sandbox)默认用 Docker 起容器,通过 Sandbox SDK 创建、连接、释放沙箱。到了 EKS 上,链路变成:客户端 → runtime-sandbox-server(运行时管理服务)→ 动态创建 Pod → 返回访问地址 → 客户端连进去执行工具。这条链路上有两个鉴权点特别容易分散:

第一,沙盒服务本身的身份令牌验证。runtime-sandbox-server 支持身份令牌验证,多用户多会话场景下还要靠 session_id 和 user_id 复用隔离资源,token 一旦散落在各个调用方,管理成本直接爆炸。

第二,沙盒里的代理应用要调用外部模型服务。你的 ReActAgent 里配的是 OpenAIChatModel,base_url 指向某个模型服务,api_key 写死在代码里。沙盒容器一多,每个容器都要一份 key,轮换、限额、审计全乱套。

这就是“多工具调用时鉴权分散”的真实痛点。我试过的做法是:把模型服务的访问收敛到一个统一 Key/API 通道,沙盒和代理应用都只认这一个入口。下面按可复现的步骤走一遍,从环境准备到 EKS 部署再到连通性验证。

先明确一个容易踩的坑:EKS 里 server 创建 Pod 时会自动建一个 NodePort 类型的 Service,名字类似runtime-sandbox-container-xxxx-service,然后 server 通过远程集群返回的 Node 的 ExternalIP 或 InternalIP 去访问,优先用 ExternalIP。EKS 节点的 ExternalIP 往往是公网 IP,安全组没放行就连不上,报错是Runtime service did not start within the specified timeout。这个后面排障章节细说。

2. TaoToken 统一 Key 前置准备:把模型入口收敛成一条通道

在动手部署之前,先把模型访问这一层理顺。AgentScope 的代理应用最终要调用外部模型服务,与其在每个沙盒里塞一份 api_key,不如统一走一个兼容 OpenAI 协议的入口。TaoToken 提供的就是这样一个统一 Key/API 通道,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。

你需要准备三样东西,我把它叫“三件套”,后面所有配置都围绕它:

  • Base URL:https://taotoken.net/api
  • API Key:在控制台创建,形如sk-开头的一串
  • Model ID:你要调用的模型标识,比如qwen3-vl这类

创建 Key 的入口在控制台,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。如果你只是想先验证模型能不能通,可以直接用模型对话页试一条: https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。

为什么要在 EKS 部署前做这一步?因为 AgentScope 的OpenAIChatModel接受client_kwargs={"base_url": ...},只要这个 base_url 指向统一通道,沙盒里的代理应用就不需要各自持有不同的 key。你可以在 EKS 里用 Secret 注入一个环境变量,所有 Pod 共享,轮换时只改一处。

这里给一个最小验证,确认通道本身是通的,再往集群里搬。用 curl 打一次 chat completions:

curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-vl", "messages": [{"role": "user", "content": "ping"}], "stream": false }'

返回里能看到choices数组就说明通道没问题。这一步别跳过,很多人上集群后排查半天,最后发现是 key 本身没生效。

关于 Coding Plan,如果你的场景是长期编码或 Agent 持续调用,可以看 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合高频、长周期的调用配额管理。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,配置细节以文档为准。

环境准备部分,测试环境用 Python 3.13,安装 agentscope-runtime:

uv add "agentscope-runtime[ext]"

如果你用 Claude Code 做辅助开发,Anthropic 兼容入口在 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite ,同样走统一 Key。

3. 可复制配置:EKS 环境变量注入与沙盒 server 配置片段

这一节是全文的核心,给你能直接抄的配置。分三块:EKS Secret 注入、runtime-sandbox-server 的 k8s 配置、代理应用的模型配置。

先做 EKS 侧的 Secret。把 TaoToken 的 Key 存成 Secret,Pod 通过环境变量引用,避免硬编码:

apiVersion: v1 kind: Secret metadata: name: taotoken-credentials namespace: agentscope-runtime type: Opaque stringData: TAOTOKEN_API_KEY: "sk-你的key" TAOTOKEN_BASE_URL: "https://taotoken.net/api"

应用时:

kubectl apply -f taotoken-secret.yaml

然后是 runtime-sandbox-server 的配置。它通过配置文件扩展功能,参考环境变量如下:

export RUNTIME_SANDBOX_REGISTRY="agentscope-registry.ap-southeast-1.cr.aliyuncs.com" runtime-sandbox-server --config k8s.env

k8s.env里配置在 K8s 上启动 sandbox 的关键参数:

POOL_SIZE=1 DEFAULT_SANDBOX_TYPE=base,browser CONTAINER_DEPLOYMENT=k8s K8S_NAMESPACE=default KUBECONFIG_PATH=/home/ec2-user/.kube/config CONTAINER_PREFIX_KEY=runtime-sandbox-container-

这里几个参数值得展开。POOL_SIZE=1是预热容器池大小,用户请求新沙箱时优先从预热池分配,比从零创建容器快很多。DEFAULT_SANDBOX_TYPE=base,browser指定默认沙盒类型,base 是基础镜像,browser 带浏览器自动化能力。CONTAINER_DEPLOYMENT=k8s指定容器运行时后端,支持 docker、k8s、agentrun、fc、gvisor 等。CONTAINER_PREFIX_KEY是创建的 Pod 名前缀,方便你按前缀筛选。

如果你要启用身份令牌验证和 Redis 缓存,在配置里加上对应项。Redis 用于沙箱状态和状态管理,优势是简单且延迟更低。持久存储目录默认是sessions_mount_dir,SDK 会自动把本地目录挂到容器的/workspace下,挂载记录类似:

"Binds": [ "/home/xxx/sessions_mount_dir/c48d9ix2r7zl2zy766xwh0dq5:/workspace:rw" ]

接下来是代理应用的模型配置。AgentScope 的OpenAIChatModel这样接统一通道:

from agentscope.model import OpenAIChatModel model = OpenAIChatModel( model_name="qwen3-vl", api_key=os.environ["TAOTOKEN_API_KEY"], client_kwargs={"base_url": os.environ["TAOTOKEN_BASE_URL"]}, stream=True, )

注意base_url用环境变量注入,不要写死。在 EKS 的 Deployment 里这样引用 Secret:

env: - name: TAOTOKEN_API_KEY valueFrom: secretKeyRef: name: taotoken-credentials key: TAOTOKEN_API_KEY - name: TAOTOKEN_BASE_URL valueFrom: secretKeyRef: name: taotoken-credentials key: TAOTOKEN_BASE_URL

如果你用 KubernetesDeployManager 部署 agent app,environment参数里可以直接传:

environment={ "PYTHONPATH": "/app", "TAOTOKEN_API_KEY": os.environ["TAOTOKEN_API_KEY"], "TAOTOKEN_BASE_URL": os.environ["TAOTOKEN_BASE_URL"], },

这样沙盒里的代理应用和外部模型服务之间,只通过一个统一 Key 通信,鉴权不再分散到每个容器。

4. 验证请求:从沙盒执行到代理应用调用模型

配置写完,必须验证。分两层:先验证沙盒本身能远程执行,再验证代理应用能通过统一通道调模型。

第一层,用 Sandbox SDK 连接远程 server 执行代码:

from agentscope_runtime.sandbox import BaseSandbox with BaseSandbox(base_url="http://127.0.0.1:8000") as box: print(box.run_ipython_cell(code="print('hi')"))

如果 server 部署在 EKS 上,把base_url换成集群内可达的地址。执行成功会打印hi,说明沙盒创建、连接、执行、释放这条链路通了。

第二层,验证代理应用。用 LocalDeployManager 先在本地跑通,再换 KubernetesDeployManager。核心的 AgentApp 代码结构如下:

import os from agentscope.agent import ReActAgent from agentscope.model import OpenAIChatModel from agentscope.formatter import OpenAIChatFormatter from agentscope.tool import Toolkit, execute_python_code from agentscope.pipeline import stream_printing_messages from agentscope.memory import InMemoryMemory from agentscope_runtime.engine import AgentApp from agentscope_runtime.engine.schemas.agent_schemas import AgentRequest from agentscope_runtime.engine.services.agent_state import InMemoryStateService from agentscope_runtime.engine.deployers.local_deployer import LocalDeployManager agent_app = AgentApp( app_name="Friday", app_description="A helpful assistant", ) @agent_app.init async def init_func(self): self.state_service = InMemoryStateService() await self.state_service.start() @agent_app.shutdown async def shutdown_func(self): await self.state_service.stop() @agent_app.query(framework="agentscope") async def query_func(self, msgs, request: AgentRequest = None, **kwargs): session_id = request.session_id user_id = request.user_id state = await self.state_service.export_state( session_id=session_id, user_id=user_id, ) toolkit = Toolkit() toolkit.register_tool_function(execute_python_code) agent = ReActAgent( name="Friday", model=OpenAIChatModel( model_name="qwen3-vl", api_key=os.environ["TAOTOKEN_API_KEY"], client_kwargs={"base_url": os.environ["TAOTOKEN_BASE_URL"]}, stream=True, ), sys_prompt="You're a helpful assistant named Friday.", toolkit=toolkit, memory=InMemoryMemory(), formatter=OpenAIChatFormatter(), ) agent.set_console_output_enabled(enabled=False) if state: agent.load_state_dict(state) async for msg, last in stream_printing_messages( agents=[agent], coroutine_task=agent(msgs), ): yield msg, last state = agent.state_dict() await self.state_service.save_state( user_id=user_id, session_id=session_id, state=state, ) async def main(): deploy_manager = LocalDeployManager(host="0.0.0.0", port=8091) await agent_app.deploy(deploy_manager, mode='standalone') if __name__ == "__main__": import asyncio asyncio.run(main())

部署后用 OpenAI 客户端测试:

from openai import OpenAI client = OpenAI( base_url="http://127.0.0.1:8654/compatible-mode/v1", api_key="sk-placeholder", ) response = client.responses.create( model="any_name", input="杭州天气如何?", ) print(response)

本地通了之后,换成 KubernetesDeployManager:

import asyncio from agentscope_runtime.engine.deployers.kubernetes_deployer import ( KubernetesDeployManager, RegistryConfig, K8sConfig, ) async def deploy_to_k8s(): deployer = KubernetesDeployManager( kube_config=K8sConfig( k8s_namespace="agentscope-runtime", kubeconfig_path=None, ), registry_config=RegistryConfig( registry_url="000000000.dkr.ecr.cn-north-1.amazonaws.com.cn", namespace="agentscope-runtime", ), use_deployment=True, ) result = await agent_app.deploy( deployer, port="8080", replicas=1, image_name="agent_app", image_tag="v1.0", requirements=["agentscope", "fastapi", "uvicorn"], base_image="python:3.10-slim-bookworm", environment={ "PYTHONPATH": "/app", "TAOTOKEN_API_KEY": os.environ["TAOTOKEN_API_KEY"], "TAOTOKEN_BASE_URL": os.environ["TAOTOKEN_BASE_URL"], }, runtime_config={ "resources": { "requests": {"cpu": "200m", "memory": "512Mi"}, "limits": {"cpu": "1000m", "memory": "2Gi"}, }, }, platform="linux/amd64", push_to_registry=True, ) print(f"部署成功:{result['url']}") return result, deployer if __name__ == "__main__": asyncio.run(deploy_to_k8s())

它会用本地 Docker 打包镜像,推送到指定仓库,然后在 EKS 创建 Pod。调用正常就说明整条链路通了。

5. 常见报错排查:401、local proxy failed、timeout 与 OAuth

部署过程中我踩过的坑集中在这几类,对照着查。

报错一:Runtime service did not start within the specified timeout

这是最典型的。现象是 server 报错,但 Pod 正常启动了。根因是 server 创建 Pod 时自动建了一个 NodePort 类型的 Service,名字类似runtime-sandbox-container-0ban2c523t0md7sx14w2c5g6v-service,server 通过远程集群返回的 Node 的 ExternalIP 或 InternalIP 访问,优先用 ExternalIP。EKS 节点的 ExternalIP 是公网 IP,安全组没放行就连不上。

排查步骤:先kubectl get svc -n default | grep runtime-sandbox确认 Service 建出来了;再kubectl get nodes -o wide看节点的 ExternalIP 和 InternalIP;然后检查安全组是否放行了 NodePort 范围。临时方案是让 InternalIP 优先返回,改源码后访问成功。长期方案是配好安全组规则,或者用集群内可达的地址。

报错二:401 Unauthorized

调用模型服务返回 401,先确认三件套是否齐全:Base URL 是不是https://taotoken.net/api,Key 是不是sk-开头且没多余空格,Model ID 是不是控制台里存在的。在 EKS 里还要确认 Secret 注入成功:

kubectl exec -it <pod-name> -n agentscope-runtime -- env | grep TAOTOKEN

如果环境变量为空,检查 Deployment 的envFrom或valueFrom配置。

报错三:local proxy failed

这类通常是沙盒容器和 server 之间的网络代理问题。检查CONTAINER_DEPLOYMENT=k8s是否配对,KUBECONFIG_PATH是否指向正确的 kubeconfig。如果用了 gvisor 或 fc 后端,确认对应运行时已安装。

报错四:reading choices解析失败

返回体里没有choices字段,多半是 base_url 拼错,比如漏了/v1或者多了斜杠。统一通道的 base_url 是https://taotoken.net/api,SDK 内部会拼/v1/chat/completions,不要自己重复加。

报错五:OAuth 相关

如果你用 Claude Code 或 Anthropic 兼容入口,OAuth 流程失败通常是回调地址或 token 过期。Anthropic 入口在 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite ,按文档重新走一遍授权。

排查时记住一个原则:先分层定位。沙盒层用BaseSandbox单独测,模型层用 curl 单独测,两层都通再测代理应用。别一上来就怀疑整个链路。

6. 把统一 Key 固化进部署流程

走到这里,你已经有一套可运行的方案:EKS 上跑 runtime-sandbox-server 管理沙盒,代理应用通过统一 Key 调模型,环境变量注入避免硬编码。最后说几个让这套方案更稳的实操点。

第一,把 Secret 的创建纳入 CI/CD。每次部署前用kubectl apply更新 Secret,Pod 滚动重启后自动拿到新 Key,不用手动进容器改。

第二,沙盒的 session_id 和 user_id 要规划好。多会话场景下靠这两个 ID 复用隔离资源,设计时想清楚粒度,避免一个用户占满沙盒池。

第三,预热池POOL_SIZE按并发量调。太小起不到加速效果,太大浪费资源。先设 1 跑通,再按压测结果加。

第四,模型调用统一走TAOTOKEN_BASE_URL,任何地方都不要写死具体地址。这样以后换通道只改一个环境变量。

如果你要长期跑 Agent 编码任务,Coding Plan 的配额管理比按次调用更划算,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入细节和最新参数以官方文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 为准,Key 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 管理。整套流程跑通后,你会发现鉴权分散的问题本质上不是技术难题,而是入口没收敛——收敛到一个 Key,剩下的就是配置和验证的体力活。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 1:11:16

muduo网络库--服务端视角核心流程与评价

1.核心流程 1.1.服务端视角 1.1.1.服务启动 流程 准备主EventLoop对象准备一个支持业务处理的BussinessServer&#xff0c;含TcpServer成员。TcpServer初始化。TcpServer启动 创建监听描述符注册可读监控设置新连接事件回调&#xff0c;设置收包回调TcpServer配置的线程数大于0…

作者头像 李华
网站建设 2026/10/18 1:10:12

逆向工程揭秘:Ampere GPU L2 Cache微架构探测与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 1:09:37

ESP32-C3物联网开发实战:从选型到量产的工程经验总结

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 1:09:22

嵌入式多MCU平台代码可移植性设计规范

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 1:08:35

低功耗SoC设计实战:从架构到量产的功耗优化与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 1:08:35

ROS2小车开发从仿真到实车部署:核心路径与实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华