隧道(Tunnel)
云端的 Agent 想使用部署在企业内网里的资源服务(MCP Server、内部 API 等),但内网通常不允许外部直接访问。VeADK 的 veadk.tunnel 提供一条由企业侧主动向外建立的隧道:连接器(connector)从内网出站连到云端 Agent,把一个或多个本地资源服务注册上去;Agent 在运行时自动感知并以工具形式调用。因为连接是出站的,所以无需在企业侧开放入站端口,天然穿透 NAT / 防火墙。
隧道本身与具体资源类型解耦:每个注册的服务带一个 protocol 字段,云端据此选择对应的协议处理器,把它转换成 Agent 工具。MCP 是目前内置的第一种协议,未来可通过实现 BaseProtocol 扩展更多协议(见下文协议)。
工作原理
- Agent 用
Agent(enable_tunnel=True)开启隧道,自动获得一个TunnelToolset。 - 云端 app 通过
mount_tunnel(或mount_tunnel_if_enabled)挂载/tunnel/*路由。 - 连接器从内网出站连到
/tunnel/connect,把本地资源服务按 Agent 名字注册。 - 下一轮对话时,
TunnelToolset.get_tools()读取注册表,按每个服务的protocol生成工具 —— 无需重新部署。
协议(protocol)
隧道是通用的:它只负责把请求安全地转发到企业内网,“某种资源如何变成 Agent 工具”交给协议处理器。
- 每个注册的服务声明一个
protocol类型,云端用工厂get_protocol(type)选出对应的BaseProtocol子类。 - 目前内置
mcp(McpProtocol):把隧道当作传输通道跑一个 MCP 客户端,列出并调用 MCP Server 暴露的工具。 - 扩展新协议:实现一个
BaseProtocol子类(定义如何把该类资源变成工具),注册到PROTOCOLS即可,无需改动隧道主流程。
from veadk.tunnel.protocol import PROTOCOLS, get_protocol
# {"mcp": McpProtocol, ...}下面的示例以 mcp 协议为例。
云端:开启并挂载
Agent 侧只需打开开关:
from veadk import Agent
agent = Agent(name="ops_agent", enable_tunnel=True)
root_agent = agent云端服务(ADK 的 get_fast_api_app)挂载隧道路由:
from google.adk.cli.fast_api import get_fast_api_app
from veadk.tunnel import mount_tunnel_if_enabled
app = get_fast_api_app(agents_dir="agents", web=False)
# 只要有一个 Agent 开了 enable_tunnel 就挂载;token 是隧道层鉴权
mount_tunnel_if_enabled(app, agents=[root_agent], token="<tunnel-token>")注册时连接器必须指定目标 Agent 名字,且该 Agent 必须 enable_tunnel=True,否则注册被拒绝。注册表按 Agent 名字隔离:注册到 A 的服务只有 A 能看到。
企业侧:连接器
在内网运行连接器,把本地资源服务注册到云端 Agent(这里是一个 MCP Server):
import asyncio
from veadk.tunnel import LocalServer, TunnelConnector
async def main():
connector = TunnelConnector(
cloud_url="https://<agent-endpoint>",
agent="ops_agent",
token="<tunnel-token>",
servers=[
LocalServer(
name="ops",
protocol="mcp",
address="http://your-mcp-host:9000/mcp", # 内网服务地址
# 访问你自己服务的鉴权(留在企业侧,不出内网):
# headers={"Authorization": "Bearer <your-token>"},
# query={"api_key": "<your-key>"},
# tool_filter=["get_employee"], # 可只暴露部分工具
),
],
)
await connector.start() # 保持运行
asyncio.run(main())两层鉴权
| 层 | 谁向谁 | 凭证 | 存放位置 |
|---|---|---|---|
| 隧道层 | 连接器 → Agent | token(经 ?token= 或 Authorization 头) | 由管理员为 Agent 颁发 |
| 单服务层 | 连接器 → 你的资源服务 | LocalServer.headers / query | 仅留在连接器(企业侧),密钥不出内网 |
部署到 AgentKit
mount_tunnel 加的是普通路由,云端服务可像其它 Agent 一样用 veadk agentkit launch 部署(参见 部署)。注意两点:
- 隧道依赖经过网关的 WebSocket,已在 AgentKit(
key_auth)网关上验证可用。 - 网关的
key_auth会占用Authorization头。此时让连接器把网关凭证放在extra_headers,隧道token走?token=:
TunnelConnector(
cloud_url="https://<endpoint>",
agent="ops_agent",
token="<tunnel-token>", # 走 ?token=
extra_headers={"Authorization": "Bearer <gateway-key>"}, # 走网关鉴权
servers=[...],
)局限
- 注册表为进程内:连接器的 WebSocket 与 Agent 运行必须命中同一进程。多副本部署需 sticky 路由或共享注册表 / 消息总线。
- 云端请求取消时,连接器侧的长连流暂未联动关闭。
完整可运行示例见 examples/12_mcp-tunnel。