Skip to content

Commit 5a10bee

Browse files
committed
Add sandbox agent tool routing and runner
1 parent fa1ddc9 commit 5a10bee

10 files changed

Lines changed: 663 additions & 529 deletions

File tree

config.yaml.full

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ volcengine:
3737
access_key:
3838
secret_key:
3939

40+
agentkit:
41+
# [optional] default AgentKit tool id fallback for all sandbox tools
42+
tool_id:
43+
# [optional] dedicated tool id for `run_code`
44+
tool_id_script:
45+
# [optional] dedicated tool id for `execute_skills`
46+
tool_id_skills:
47+
# [optional] dedicated tool id for `coding`
48+
tool_id_opencode:
49+
# [optional] AgentKit endpoint configs
50+
tool_host:
51+
tool_service_code: agentkit
52+
tool_region: cn-beijing
53+
tool_scheme: https
54+
top_scheme: https
55+
4056
tool:
4157
# [optional] https://console.volcengine.com/ask-echo/my-agent
4258
vesearch:

docs/docs/tools/builtin.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ VeADK 集成了以下火山引擎工具:
6767
| `image_edit` | [编辑图片](https://www.volcengine.com/docs/82379/1541523)(图生图)。 | `from veadk.tools.builtin_tools.image_edit import image_edit` |
6868
| `video_generate` | 根据文本描述[生成视频](https.www.volcengine.com/docs/82379/1520757)| `from veadk.tools.builtin_tools.video_generate import video_generate` |
6969
| `run_code` |[AgentKit 沙箱](https://console.volcengine.com/agentkit-ppe/region:agentkit-ppe+cn-beijing/builtintools)中执行代码。 | `from veadk.tools.builtin_tools.run_code import run_code` |
70+
| `execute_skills` | 在预制技能沙箱中远程执行 `agent.py` 工作流。 | `from veadk.tools.builtin_tools.execute_skills import execute_skills` |
71+
| `coding` | 在预制 OpenCode 沙箱中执行代码生成工作流。 | `from veadk.tools.builtin_tools.coding import coding` |
72+
| `run_sandbox_agent` | 指定任意 `tool_id` 在远端 AgentKit 沙箱中执行 `agent.py`| `from veadk.tools.builtin_tools.run_sandbox_agent import run_sandbox_agent` |
7073
| `lark` | 集成[飞书开放能力](https://open.larkoffice.com/document/uAjLw4CM/ukTMukTMukTM/mcp_integration/mcp_installation),实现文档处理、会话管理等。 | `from veadk.tools.builtin_tools.lark import lark` |
7174
| `las` | 基于[火山引擎 AI 多模态数据湖服务 LAS](https://www.volcengine.com/mcp-marketplace) 进行数据管理。 | `from veadk.tools.builtin_tools.las import las` |
7275
| `mobile_run` | 手机指令执行 | `from veadk.tools.builtin_tools.mobile_run import create_mobile_use_tool` |
@@ -183,7 +186,10 @@ VeADK 集成了以下火山引擎工具:
183186

184187
以下是必须在环境变量里面的配置项:
185188

186-
- `AGENTKIT_TOOL_ID`:用于调用火山引擎AgentKit Tools的沙箱环境Id
189+
- `AGENTKIT_TOOL_ID`:默认的 AgentKit 沙箱环境 Id,会作为所有沙箱工具的兜底配置
190+
- `AGENTKIT_TOOL_ID_SCRIPT`:`run_code` 专用沙箱环境 Id,未配置时回退到 `AGENTKIT_TOOL_ID`
191+
- `AGENTKIT_TOOL_ID_SKILLS`:`execute_skills` 专用沙箱环境 Id,未配置时回退到 `AGENTKIT_TOOL_ID`
192+
- `AGENTKIT_TOOL_ID_OPENCODE`:`coding` 专用沙箱环境 Id,未配置时回退到 `AGENTKIT_TOOL_ID`
187193
- `AGENTKIT_TOOL_HOST`:用于调用火山引擎AgentKit Tools的EndPoint
188194
- `AGENTKIT_TOOL_SERVICE_CODE`:用于调用AgentKit Tools的ServiceCode
189195
- `AGENTKIT_TOOL_SCHEME`:用于切换调用 AgentKit Tools 的协议,允许 `http`/`https`,默认 `https`
@@ -204,6 +210,11 @@ VeADK 集成了以下火山引擎工具:
204210
name: doubao-seed-1-6-250615
205211
api_base: https://ark.cn-beijing.volces.com/api/v3/
206212
api_key: your-api-key-here
213+
agentkit:
214+
tool_id: your-default-tool-id
215+
tool_id_script: your-script-tool-id
216+
tool_id_skills: your-skills-tool-id
217+
tool_id_opencode: your-opencode-tool-id
207218
volcengine:
208219
# [optional] for Viking DB and `web_search` tool
209220
access_key: you-access-key-here
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import importlib.util
16+
import os
17+
import sys
18+
import types
19+
import unittest
20+
from pathlib import Path
21+
from unittest.mock import patch
22+
23+
24+
def _load_agentkit_module():
25+
module_path = (
26+
Path(__file__).resolve().parents[3]
27+
/ "veadk"
28+
/ "tools"
29+
/ "builtin_tools"
30+
/ "_agentkit.py"
31+
)
32+
33+
fake_veadk = types.ModuleType("veadk")
34+
fake_veadk.__path__ = [] # type: ignore[attr-defined]
35+
fake_auth = types.ModuleType("veadk.auth")
36+
fake_auth.__path__ = [] # type: ignore[attr-defined]
37+
fake_veauth = types.ModuleType("veadk.auth.veauth")
38+
fake_veauth.__path__ = [] # type: ignore[attr-defined]
39+
fake_veauth_utils = types.ModuleType("veadk.auth.veauth.utils")
40+
fake_config = types.ModuleType("veadk.config")
41+
fake_utils = types.ModuleType("veadk.utils")
42+
fake_utils.__path__ = [] # type: ignore[attr-defined]
43+
fake_logger = types.ModuleType("veadk.utils.logger")
44+
fake_sign = types.ModuleType("veadk.utils.volcengine_sign")
45+
46+
def fake_getenv(env_name, default_value="", allow_false_values=False):
47+
value = os.getenv(env_name, default_value)
48+
if allow_false_values:
49+
return value
50+
if value:
51+
return value
52+
raise ValueError(
53+
f"The environment variable `{env_name}` not exists. Please set this in your environment variable or config.yaml."
54+
)
55+
56+
class _FakeCredential:
57+
access_key_id = "ak"
58+
secret_access_key = "sk"
59+
session_token = "token"
60+
61+
class _FakeLogger:
62+
def debug(self, *_args, **_kwargs):
63+
return None
64+
65+
def warning(self, *_args, **_kwargs):
66+
return None
67+
68+
def error(self, *_args, **_kwargs):
69+
return None
70+
71+
fake_veauth_utils.get_credential_from_vefaas_iam = lambda: _FakeCredential()
72+
fake_config.getenv = fake_getenv
73+
fake_logger.get_logger = lambda _name: _FakeLogger()
74+
fake_sign.ve_request = lambda **_kwargs: {"Result": {"AccountId": "test-account"}}
75+
76+
stub_modules = {
77+
"veadk": fake_veadk,
78+
"veadk.auth": fake_auth,
79+
"veadk.auth.veauth": fake_veauth,
80+
"veadk.auth.veauth.utils": fake_veauth_utils,
81+
"veadk.config": fake_config,
82+
"veadk.utils": fake_utils,
83+
"veadk.utils.logger": fake_logger,
84+
"veadk.utils.volcengine_sign": fake_sign,
85+
}
86+
87+
with patch.dict(sys.modules, stub_modules):
88+
spec = importlib.util.spec_from_file_location(
89+
"test_agentkit_module", module_path
90+
)
91+
module = importlib.util.module_from_spec(spec)
92+
assert spec is not None
93+
assert spec.loader is not None
94+
spec.loader.exec_module(module)
95+
return module
96+
97+
98+
class TestResolveAgentkitToolId(unittest.TestCase):
99+
@classmethod
100+
def setUpClass(cls):
101+
cls.agentkit_module = _load_agentkit_module()
102+
103+
def setUp(self):
104+
self.env_patcher = patch.dict(
105+
os.environ,
106+
{},
107+
clear=False,
108+
)
109+
self.env_patcher.start()
110+
for env_name in [
111+
"AGENTKIT_TOOL_ID",
112+
"AGENTKIT_TOOL_ID_SCRIPT",
113+
"AGENTKIT_TOOL_ID_SKILLS",
114+
"AGENTKIT_TOOL_ID_OPENCODE",
115+
]:
116+
os.environ.pop(env_name, None)
117+
118+
def tearDown(self):
119+
self.env_patcher.stop()
120+
121+
def test_resolve_prefers_script_tool_id(self):
122+
os.environ["AGENTKIT_TOOL_ID_SCRIPT"] = "script-tool"
123+
os.environ["AGENTKIT_TOOL_ID"] = "default-tool"
124+
125+
tool_id = self.agentkit_module.resolve_agentkit_tool_id(
126+
"AGENTKIT_TOOL_ID_SCRIPT"
127+
)
128+
129+
self.assertEqual(tool_id, "script-tool")
130+
131+
def test_resolve_prefers_skills_tool_id(self):
132+
os.environ["AGENTKIT_TOOL_ID_SKILLS"] = "skills-tool"
133+
os.environ["AGENTKIT_TOOL_ID"] = "default-tool"
134+
135+
tool_id = self.agentkit_module.resolve_agentkit_tool_id(
136+
"AGENTKIT_TOOL_ID_SKILLS"
137+
)
138+
139+
self.assertEqual(tool_id, "skills-tool")
140+
141+
def test_resolve_prefers_opencode_tool_id(self):
142+
os.environ["AGENTKIT_TOOL_ID_OPENCODE"] = "opencode-tool"
143+
os.environ["AGENTKIT_TOOL_ID"] = "default-tool"
144+
145+
tool_id = self.agentkit_module.resolve_agentkit_tool_id(
146+
"AGENTKIT_TOOL_ID_OPENCODE"
147+
)
148+
149+
self.assertEqual(tool_id, "opencode-tool")
150+
151+
def test_resolve_falls_back_to_default_tool_id(self):
152+
os.environ["AGENTKIT_TOOL_ID"] = "default-tool"
153+
154+
tool_id = self.agentkit_module.resolve_agentkit_tool_id()
155+
156+
self.assertEqual(tool_id, "default-tool")
157+
158+
def test_resolve_raises_when_all_tool_ids_missing(self):
159+
with self.assertRaisesRegex(ValueError, "AGENTKIT_TOOL_ID"):
160+
self.agentkit_module.resolve_agentkit_tool_id("AGENTKIT_TOOL_ID_SCRIPT")
161+
162+
163+
if __name__ == "__main__":
164+
unittest.main()

0 commit comments

Comments
 (0)