---
title: "前端"
---

VeADK 提供两种前端：**VeADK Web** 用于本地快速调试，**VeADK Frontend** 是可定制的生产级 React 应用。

## VeADK Web

`veadk web` 启动一个继承自 Google ADK Web 的可视化调试界面，零前端代码即可在浏览器中与智能体对话、查看执行过程，适合本地开发与排查。用法见[快速开始 · 本地可视化调试](/cn/docs/framework/quickstart)。

## VeADK Frontend

VeADK 自带一个 React 前端，用于渲染智能体经由 Google ADK API Server 流式返回的 [A2UI](/cn/docs/framework/a2ui)（智能体驱动 UI）。它与 `veadk frontend` 启动的是同一个服务进程，因此**无需单独部署后端**。

### 功能一览

- **对话**：与智能体多轮对话，渲染流式返回的 A2UI 卡片，并展示思考过程、工具调用、Token 与用时。
- **多模态消息**：上传图片、TXT/Markdown、PDF 与视频；用户附件和模型返回的媒体均支持预览与历史回放。
  对话中的图片默认以紧凑尺寸展示，点击后可全屏缩放预览。
- **技能与子 Agent 调用**：在输入框输入 `/` 选择挂载技能，输入 `@` 将本轮交给可选的子 Agent。
- **新会话模式**：保留 Agent 对话模式；临时会话会在 AgentKit Sandbox 中启动 Codex Agent；Skill 创建模式会在两个独立的 AgentKit CodeEnv Session 中使用固定模型做真实 A/B 生成，完成后可对比、下载 ZIP 或添加到 AgentKit。
- **Agent 选择器**：左上角切换 agent；长列表在视口内独立滚动，悬停可查看该 agent 的模型与挂载的工具。
- **历史会话**：自动保存、按时间排序，可重新打开或删除。
- **临时会话**：从新会话输入框启动独立的 AgentKit 沙箱临时会话。
  Studio 会复用专属 CodeEnv Tool、为当前用户创建全新的 Sandbox Session，并通过
  Codex 进行多轮对话，实时展示公开思考摘要、命令、文件修改、MCP 调用和最终回复。
  退出后云端 Session 会被删除，内容不会写入普通历史会话；
  页面刷新后可直接新建，遗留 Session 由 AgentKit 在 TTL 到期后自动回收。
- **智能搜索**：「会话」源在当前 agent 的历史消息中做全文检索；「网页」源调用该 agent 挂载的联网搜索工具实时检索（使用服务端环境变量里的凭据）。
- **添加 AgentKit 智能体**：填入访问地址 + API Key，按 ADK 协议接入远程 agent，接入后出现在选择器中。
- **自定义 Agent 工作台**：系统提示词支持所见即所得的 Markdown
  编辑与标题、列表等快捷输入；本地技能可直接拖入文件夹或 ZIP，格式会自动识别，
  也可按地域和项目浏览账号下的 AgentKit Skill Space 及其中的技能。
  完成配置后可在同页调试。调试初始化、
  会话或对话失败时会展示经过凭据脱敏的具体错误和 Runner 日志，
  并支持展开与复制。部署页左侧展示 Agent 拓扑，悬停可核对节点信息与配置，
  也可导出配置、下载源码或在弹窗中浏览和编辑代码；右侧集中设置发布区域、
  消息渠道、网络、环境变量后部署到 AgentKit。全局标题栏提供跨页面部署任务列表，
  展示 AgentKit 实际生成的 Runtime 名称、地域与进度；运行中的任务可取消并清理资源，
  失败任务可重试。远程 Agent 的拓扑和 Trace 会通过所选 Runtime 的数据面获取。
  自定义创建中的“远程 Agent”仅可作为子 Agent，通过 AgentKit 智能体中心 ID、
  召回数量、地域和 OpenAPI 地址配置。名称、描述和能力由中心返回的 Agent Card
  提供；生成的内部代理会在每轮请求中动态发现和挂载匹配的远程 Agent。
- **Tracing 观测**：查看本次会话的调用火焰图。
- **登录**：支持 SSO（VeIdentity / GitHub / Google / 任意 OIDC）或本地用户名。

知识库、记忆库、工具和 Tracing 等组件只要求填写无法自动获取的参数。Studio
服务端已有的火山引擎凭证会自动传给本地调试进程和部署后的 AgentKit Runtime，
VeADK 会据此获取 Ark、Embedding、图像、视频、语音、VeSearch 和 APMPlus 的
API Key，挂载组件时无需重复填写。

“灵光一现”需要 Studio 服务端配置 `VOLCENGINE_ACCESS_KEY`、
`VOLCENGINE_SECRET_KEY`、`MODEL_AGENT_API_KEY` 和 `MODEL_AGENT_NAME`。
火山引擎凭证、模型凭证以及带授权信息的 Sandbox Endpoint 始终保留在服务端，
不会返回浏览器。
临时沙箱状态保存在 Studio 进程内；部署时应使用单个服务进程，或配置会话亲和，
确保同一浏览器的创建、对话和退出请求到达同一个实例。

在部署页打开或关闭飞书渠道时，Studio 会重新生成项目，确保 `app.py`、
`extensions` 依赖和运行时环境变量在部署前保持一致。

### 运行

先构建前端，再用一条命令同时提供 UI 与智能体 API：

```bash
# 1. 构建前端
cd frontend && npm install && npm run build

# 2. 在仓库根目录，单进程提供 UI + 智能体 API
veadk frontend --agents-dir examples
# 打开 http://127.0.0.1:8000
```

#### 开发模式（热更新）

开发模式下只提供 API（并为 Vite 开发服务器开启 CORS），前端由 Vite 单独以热更新方式运行：

```bash
veadk frontend --dev --agents-dir examples   # 仅 API，为 vite 放行 CORS
cd frontend && npm run dev                    # http://localhost:5173（代理 API）
```

### `veadk frontend` 命令

| 选项 | 默认值 | 说明 |
| :-- | :-- | :-- |
| `--agents-dir` | — | 智能体应用目录，每个子目录暴露一个 `root_agent`。 |
| `--frontend-dir` | `./frontend` 的构建产物 | 已构建 React UI（`npm run build` 输出）所在目录。 |
| `--host` | `127.0.0.1` | 监听地址。 |
| `--port` | `8000` | 监听端口。 |
| `--dev` | 关闭 | 开发模式：仅提供 API，并放行来自 Vite 开发服务器（`http://localhost:5173`）的 CORS。 |
| `--oauth2-user-pool` / `--oauth2-user-pool-client` | — | VeIdentity 用户池 / 客户端**名称**。设置后启用 SSO。 |
| `--oauth2-user-pool-uid` / `--oauth2-user-pool-client-uid` | 环境变量 `OAUTH2_USER_POOL_ID` / `OAUTH2_USER_POOL_CLIENT_ID` | 用 **UID** 代替名称指定用户池 / 客户端。 |
| `--oauth2-provider` / `--oauth2-provider-label` | `veidentity` | 登录按钮的 provider 标识与显示文案。 |
| `--oauth2-redirect-uri` | `http://{host}:{port}/oauth2/callback` | OAuth2 回调地址（环境变量 `OAUTH2_REDIRECT_URI`）。部署到公网 / runtime 时需设置。 |
| 第三方 / 自定义 provider | 见下文 | 通过环境变量接入 GitHub / Google / 任意 OIDC，详见「认证」一节。 |

<Callout type="info">
  非开发模式下，若未找到已构建的前端目录，命令会报错提示先执行 `npm run build`（或改用 `--dev` 配合 Vite 开发服务器）。
</Callout>

### 多模态附件与存储

输入框支持 PNG、JPEG、WebP、GIF、TXT、Markdown、PDF、MP4、WebM 和 QuickTime。默认单文件上限为 20 MB。浏览器使用二进制表单上传，不会把附件转成 base64 写入会话。

媒体本体与 ADK Session 分开保存：

- 本地模式默认写入 `/tmp/veadk-media/apps/.../sessions/.../media/<media-id>/`，其中 `content` 是文件本体，`metadata.json` 是文件名、MIME、大小和哈希等元数据。
- TOS 模式默认写入 `veadk-media/users/<encoded-username>/apps/<app>/sessions/<session>/media/<media-id>/` 下的同名两个对象。用户名位于最外层租户 prefix，不同用户的数据相互隔离；username、app 和 session 路径段都会进行 URL 编码。
- Session Event 只保存 Google GenAI `FileData` 稳定引用，例如 `veadk-media://apps/.../media/<media-id>`；重新打开历史会话时再通过媒体 API 读取本体。

调用模型前，TXT/Markdown 会解码为 `Part.text`，图片和视频会从当前存储读取并转换为
`Part.inline_data`，PDF 则自动渲染为逐页 PNG 图片。PDF 渲染运行时包含在 VeADK
默认依赖中，不需要额外安装或配置 Agent callback。模型返回的 `inline_data` 会先落到
媒体存储，再在 Event 持久化和 SSE 输出前改写为稳定引用。TOS 的 15 分钟签名 URL
只用于浏览器下载/预览，不作为模型的 `FileData` URI。

选择云端 Agent Runtime 时，上传、预览和删除仍由 Studio 自身的 `/web/media`
接口处理，不会转发到 Runtime。Studio 仅在代理 `/run_sse` 时读取稳定引用，将内容转换为
远端模型可直接消费的 Part；原始 `veadkMedia` 元数据会保留，因此历史消息仍按原文件类型
和名称加载。这个流程同时支持默认 `/tmp` 与 TOS，无需远端 Runtime 挂载媒体 HTTP 路由。

| 环境变量 | 默认值 | 说明 |
| :-- | :-- | :-- |
| `VEADK_MEDIA_STORAGE` | `local` | 选择 `local` 或 `tos`。 |
| `VEADK_MEDIA_LOCAL_DIR` | `/tmp/veadk-media` | 本地媒体根目录。 |
| `VEADK_MEDIA_MAX_FILE_BYTES` | `20971520` | 单个上传文件或模型输出的字节上限。 |
| `VEADK_MEDIA_TOS_PREFIX` | `veadk-media` | TOS 对象 Key 前缀。 |
| `DATABASE_TOS_BUCKET` | — | TOS Bucket。 |
| `DATABASE_TOS_REGION` / `DATABASE_TOS_ENDPOINT` | 按云环境推导 | TOS Region 与 Endpoint。 |
| `VOLCENGINE_ACCESS_KEY` / `VOLCENGINE_SECRET_KEY` | — | TOS 访问凭据。 |
| `VOLCENGINE_SESSION_TOKEN` | — | 可选的临时凭据 Token。 |

移除尚未发送的附件会立即删除对应对象；删除 Session 会清理该 Session 下的全部媒体。本地模式无需额外配置，但 `/tmp` 可能随进程或宿主机回收而被清理；需要持久保存附件时应使用 TOS。启用 TOS 的最小配置如下：

```bash
export VEADK_MEDIA_STORAGE=tos
export DATABASE_TOS_BUCKET=<bucket>
export DATABASE_TOS_REGION=cn-beijing
export VOLCENGINE_ACCESS_KEY=<access-key>
export VOLCENGINE_SECRET_KEY=<secret-key>
veadk frontend --agents-dir examples
```

### 使用技能与子 Agent

在输入框中输入 `/` 可搜索当前 Agent 挂载的技能，输入 `@` 可搜索其 Agent 树中允许转移的后代节点。使用上下方向键移动，按 Enter 或 Tab 选择，按 Escape 关闭菜单。选中项会变成可移除的 chip，不会作为普通文本留在消息中。

选中子 Agent 后，`/` 菜单会改为展示目标 Agent 自己挂载的技能。切换或移除目标时会清空原有技能，避免把技能发送给没有挂载它的 Agent。`task` 和 `single_turn` 工作流节点仍会显示在拓扑中，但不能通过 `@` 选择。

前端不会从消息字符串中猜测调用意图，而是发送结构化的 `veadkInvocation` metadata。后端插件据此要求 ADK 调用技能工具，或沿 Agent 树逐级调用 `transfer_to_agent`，直到到达目标节点。同一份 metadata 也会写入首个 Google GenAI `Part`，因此重新加载历史会话后仍能恢复 `/skill` 与 `@agent` chip。

### Skill 创建模式

新会话输入框中的「Skill 创建」会固定使用
`doubao-seed-2-0-pro-260215` 和 `deepseek-v4-flash-260425`，通过 Ark
Responses API 并行生成两个候选方案。每个候选使用独立的 AgentKit CodeEnv
Session，生成结果会校验目录名、`SKILL.md` frontmatter、文件数量、大小和路径安全，
再打包为 ZIP。真实 Ark API Key 由 AgentKit 凭据托管保存，Studio 和 Sandbox
只使用可撤销的网关票据；每个任务的 Session 环境变量不会覆盖托管票据，且服务端
只接受 Volcengine HTTPS 凭据中继地址。浏览器不会收到模型凭据。Skill 创建接口仅允许
Studio developer 和 admin 使用。

提交需求后会立即显示两个候选对话流，各自持续渲染生成器返回的公开思考摘要、工具
调用和助手回复。私有思维链和凭据不会进入页面。候选完成后仍可预览、下载 ZIP 或
添加到 AgentKit。前端拿到真实任务 ID 后会直接从 Sandbox 状态恢复轮询；创建流中断
或 AgentKit 控制面短暂未发现新 Session 时，不会把中间状态误判为任务过期。

本地启动前需要分别配置临时会话和 Skill 创建使用的两个 `Ready` CodeEnv Tool：

```bash
export SANDBOX_CHAT_CODEX=<chat-code-env-tool-id>
export SANDBOX_SKILL_CREATOR=<skill-code-env-tool-id>
veadk frontend --agents-dir examples
```

本地运行前使用 AgentKit 的 `credential-hosting` 命令，并将结果分别写入上述 CodeEnv
Tool。添加到 AgentKit 时，服务端会先将
ZIP 上传到 TOS，再调用 Skills API 创建或更新 Skill；只有用户填写 SkillSpace ID
时才发布到对应空间。以下变量均为可选覆盖项：

| 环境变量 | 默认值 | 说明 |
| :-- | :-- | :-- |
| `SANDBOX_CHAT_CODEX` | — | 临时会话专用 AgentKit CodeEnv Tool ID。 |
| `SANDBOX_SKILL_CREATOR` | — | Skill 创建专用 AgentKit CodeEnv Tool ID。 |
| `VEADK_SKILL_CREATOR_TOS_BUCKET` | AgentKit 账户默认 Bucket | 发布产物使用的 TOS Bucket。 |
| `VEADK_SKILL_CREATOR_TOS_PREFIX` | `agentkit/skills` | TOS 对象 Key 前缀。 |
| `VEADK_SKILL_CREATOR_PROJECT_NAME` | — | 创建 Skill 时使用的项目名称。 |

候选 Session 的 TTL 为 30 分钟，用户重新创建或离开任务时会立即清理。任务状态保存
在 Sandbox 而非 Frontend 进程内存，因此 FaaS 请求命中不同实例时仍可继续轮询和下载。

云上首次部署时，若没有指定 Tool ID，部署命令会使用部署 AK/SK 自动创建两个独立
CodeEnv Tool。也可以分别使用 `--sandbox-chat-codex-tool-id` 和
`--sandbox-skill-creator-tool-id` 指定已有 Tool。部署命令会通过 AgentKit 凭据托管
将 Ark 凭据保存到 KMS，并把代理地址和可撤销票据绑定到两个 Tool；VeFaaS Function
只接收 Tool ID，不接收模型凭据：

```bash
veadk studio deploy \
  --user-pool-id <pool-id> \
  --allowed-client-id <client-id> \
  --vefaas-app-name <app-name>
```

部署凭据支持通过 `--volcengine-access-key` / `--volcengine-secret-key`
显式传入，也支持当前进程的 `VOLCENGINE_ACCESS_KEY` /
`VOLCENGINE_SECRET_KEY` 环境变量。两者均未提供时，会读取
`~/.volc/credentials` 中的 `[default]` 配置。

### 自定义 Studio 品牌

使用 `--site-title` 设置不超过 6 个字符的系统名称，使用
`--site-logo` 指定本地图片或 HTTP(S) 图片地址。Logo 会同时用于
侧边栏、登录页和浏览器 favicon，系统名称也会作为浏览器页面标题。
不传 `--site-title` 时保留默认名称 `VeADK Studio`；6 字符限制只适用于自定义名称。

```bash
veadk studio --site-title 火山助手 --site-logo ./logo.png
veadk studio --site-title 火山助手 --site-logo https://example.com/logo.webp
```

支持 PNG、JPEG、GIF、WebP、AVIF 和 ICO，图片最大 5 MB；也可以通过
`VEADK_SITE_TITLE` 与 `VEADK_SITE_LOGO` 环境变量配置。云上部署使用相同
参数，网络图片会在部署时下载，并与本地图片一样打包进 VeFaaS，
不依赖原始图片地址：

```bash
veadk studio deploy \
  --user-pool-id <pool-id> \
  --allowed-client-id <client-id> \
  --vefaas-app-name <app-name> \
  --region cn-beijing \
  --project default \
  --site-title 火山助手 \
  --site-logo ./logo.png
```

`--region` 指定 Studio 部署地域，默认为 `cn-beijing`，也支持
`cn-shanghai`。部署时会先查询部署地域，再跨地域查询北京、上海的 Identity
用户池；跨地域命中时会显示 warning 并继续。`--project` 指定 VeFaaS 函数
所属项目，默认为 `default`。

更新已部署的 Studio 时，在 VeADK 源码目录执行：

```bash
veadk studio update --vefaas-app-name <app-name>
```

该命令重新构建本地前端并更新已有 VeFaaS Function 的代码，然后发布原
Application。未指定 `--region` 和 `--project` 时，会在北京、上海以及所有可见
项目中查找；若同名 Application 不唯一，命令会列出候选项并要求缩小范围。
更新不会改变 Application/Function ID、访问 URL、SSO、IAM、网关或已有环境
变量。`--site-title` 和 `--site-logo` 仅在显式传入时覆盖，省略时保留云上品牌
设置。可用 `--path` 指定源码目录，默认为当前目录；本地需安装 Node.js 与 npm。
`--sandbox-chat-codex-tool-id` 和 `--sandbox-skill-creator-tool-id` 也只在显式传入时
更新对应环境变量；未传的 Tool ID 保留云上已有值，不会被清空。

部署注册回调地址时会保留 VeIdentity 登录页，并为该客户端开启跳过授权确认，
避免用户登录后再次确认授权。

### Studio 角色与 Runtime 权限

`--admin` 和 `--developer` 分别接收逗号分隔的用户名或 OAuth 邮箱。空格会被
忽略，身份匹配不区分大小写；同一身份同时出现在两个名单时，`admin` 优先。
本地启动示例：

```bash
veadk studio \
  --admin "admin,admin@example.com" \
  --developer "alice,alice@example.com,bob"
```

也可以分别通过 `VEADK_STUDIO_ADMINS` 和 `VEADK_STUDIO_DEVELOPERS` 环境变量
设置这两个名单。

部署 Studio 时使用相同参数：

```bash
veadk studio deploy \
  --user-pool-id <pool-id> \
  --allowed-client-id <client-id> \
  --vefaas-app-name <app-name> \
  --admin "admin@example.com" \
  --developer "alice@example.com,bob@example.com"
```

两项都不传时，所有登录用户都按管理员（`admin`）处理。只要传入任一名单就会启用
角色权限；未命中名单的身份是普通用户。

| 功能 | admin | developer | 普通用户 |
| :-- | :-- | :-- | :-- |
| 添加、调试和部署 Agent | 允许 | 允许 | 不允许；侧边栏隐藏添加/管理入口 |
| 查看和连接 Runtime | 全部 Runtime | 仅自己创建的 Runtime | 仅自己创建的 Runtime |
| 管理或删除 Runtime | 全部 Studio 管理的 Runtime | 仅自己创建的 Runtime | 不允许 |

侧边栏底部的账号区域会在用户名下显示 OAuth 邮箱，并在用户名右侧用紫色、黄色、
绿色徽标分别标识管理员、开发者和普通用户。
管理 Agent 页面默认展示北京区域的 Runtime，也可以切换到上海。
新会话发送首条消息后会立即进入对话页面；服务端会话创建期间，会话 ID 显示为
“初始化中”。

Runtime 归属由服务端在创建时记录，不使用浏览器提交的作者字段做授权。升级前创建
的 Runtime 会兼容 `veadk:author` 归属标签；同时缺少 `veadk:owner` 和
`veadk:author` 归属标签的历史 Runtime 仅 admin 可见。

<Callout type="warning">
  本地用户名保存在浏览器中，可以被用户修改或冒充，只适合本地开发和功能验证。
  生产环境必须使用 OAuth 或 gateway 认证，让服务端从已验证的身份中判断角色和
  Runtime 归属。
</Callout>

### `veadk studio deploy` 的 IAM 权限

部署命令会先检查 `ServerlessApplicationRole`。如果该 Role 不存在，终端会显示
黄色提示并自动创建 Role，同时创建或复用自定义策略 `vefaas_full_access`，然后
绑定以下策略（作用域均为 Global）：

- `CloudMonitorReadOnlyAccess`
- `vefaas_full_access`
- `VPCFullAccess`
- `TLSFullAccess`
- `APIGFullAccess`
- `ECSFullAccess`
- `VeFaaSFullAccess`
- `STSAssumeRoleAccess`

该检查不受 `--iam-role` 影响；部署使用的 AK/SK 需要具备查询、创建 IAM
Role/策略以及绑定策略的权限。

未传入 `--iam-role` 时，部署命令会创建或复用 `VeADKFrontendServiceRole`，并确保该 Role 绑定以下火山引擎系统策略：

- `ArkReadOnlyAccess`
- `TLSReadOnlyAccess`
- `APMPlusServerReadOnlyAccess`
- `VikingdbReadOnlyAccess`
- `ESCloudReadOnlyAccess`
- `LLMShieldProtectSdkAccess`
- `TorchlightApiFullAccess`
- `Mem0ReadOnlyAccess`
- `IDReadOnlyAccess`

Role 同时保留 `VeADKFrontendPolicy` 自定义策略中的 AgentKit 权限。传入 `--iam-role` 时，命令直接使用指定 Role，不修改其策略。

### 认证（SSO 与本地用户名）

会话与记忆按 ADK 的 `user_id` 隔离，该 `user_id` 来自登录用户。

**SSO（VeIdentity OAuth2）** —— 传入用户池 / 客户端后启用。前端会展示登录页并跳转到 VeIdentity，登录后用 `/oauth2/userinfo` 返回的 `sub` 作为 `user_id`：

```bash
veadk frontend --agents-dir examples \
  --oauth2-user-pool <name> --oauth2-user-pool-client <name>
# 或用 UID（读环境变量 OAUTH2_USER_POOL_ID / OAUTH2_USER_POOL_CLIENT_ID）：
# --oauth2-user-pool-uid <id> --oauth2-user-pool-client-uid <id>
```

需要进程能拿到火山引擎凭证（AK/SK）。中间件保护 API、放行 SPA 外壳与 `GET /web/auth-config`，因此应用能加载并展示自己的登录页（而不是被直接重定向到 IdP）。登录按钮的文案/图标由配置驱动（`--oauth2-provider` / `--oauth2-provider-label`）。

连接使用 `custom_jwt` 鉴权的 AgentKit Runtime 时，服务端代理会转发当前会话中已经验证的 OAuth access token。前端所用 token 的 issuer 必须匹配 Runtime 的 discovery URL，并且 client ID 必须包含在 Runtime 的 `allowed_clients` 中。

**第三方 / 自定义 OAuth2（环境变量）** —— 不依赖 VeIdentity 用户池时，只要设置 `OAUTH2_CLIENT_ID`（及密钥），即可接入 GitHub、Google 或任意 OAuth2/OIDC 登录。端点来源按以下顺序确定：内置预设（`OAUTH2_PROVIDER=github` / `google`）→ OIDC 自动发现（设置 `OAUTH2_ISSUER`）→ 显式端点（`OAUTH2_AUTHORIZE_URL` 等）。

| 环境变量 | 说明 |
| :-- | :-- |
| `OAUTH2_PROVIDER` | provider 标识：`github`、`google` 或自定义名。决定登录按钮文案与内置预设。 |
| `OAUTH2_CLIENT_ID` / `OAUTH2_CLIENT_SECRET` | OAuth2 客户端凭据。**设置 `OAUTH2_CLIENT_ID` 即启用通用 provider。** |
| `OAUTH2_ISSUER` | OIDC issuer 基址，端点自动发现（如 `https://accounts.google.com`）。 |
| `OAUTH2_AUTHORIZE_URL` / `OAUTH2_TOKEN_URL` / `OAUTH2_USERINFO_URL` | 显式端点（用于非 OIDC provider）。 |
| `OAUTH2_SCOPE` | 覆盖请求的 scope。 |
| `OAUTH2_PROVIDER_LABEL` | 覆盖登录按钮文案。 |
| `OAUTH2_REDIRECT_URI` | **回调地址**。部署到公网 / runtime 时设为公网回调，并在 OAuth 应用中登记同一地址；本地默认 `http://{host}:{port}/oauth2/callback`。 |

GitHub（预设，仅需 id/secret）：

```bash
export OAUTH2_PROVIDER=github
export OAUTH2_CLIENT_ID=<github-oauth-client-id>
export OAUTH2_CLIENT_SECRET=<github-oauth-client-secret>
export OAUTH2_REDIRECT_URI=http://127.0.0.1:8000/oauth2/callback
veadk frontend --agents-dir examples
```

Google 同理，把 `OAUTH2_PROVIDER` 换成 `google`；Keycloak / Auth0 / Okta 等任意 OIDC 则设 `OAUTH2_ISSUER` + 客户端凭据即可。完整示例见仓库 `examples/front_with_sso/`。

<Callout type="info">
  部署到 runtime / 公网时，OAuth 回调必须指向外部可访问的地址：把 `OAUTH2_REDIRECT_URI` 设为公网回调 URL，并在 OAuth 应用里登记同一地址。Cookie 的 `Secure` 标志会根据该地址是否为 HTTPS 自动开启。
</Callout>

SSO 引导语固定为“登录以继续使用”，不追加系统名称；登录页底部使用
“火山引擎 AgentKit 提供企业级 Agent 解决方案”品牌文案。

**无 SSO（本地用户名）** —— 不传上述参数时，登录页会让用户输入一个用户名（字母 + 数字，≤16 位），保存在本地并作为 `user_id`。

<Callout type="info">
  登录态会被缓存：SSO 走 `veadk_session` Cookie，本地模式走 `localStorage`。会话本身**在发送第一条消息或上传第一个附件时才创建**（而非打开页面时）。退出登录为本地登出（清除会话并回到登录页）。
</Callout>

### Agent 命名规则

Studio 会按照 Google ADK 的规则校验根 Agent 与所有嵌套 Agent 的名称：名称须以
英文字母或下划线开头，后续只能包含英文字母、数字和下划线；不能使用保留名
`user`，并且在整个 Agent 结构中必须唯一。

### 工作原理

<Steps>

<Step>
`adk/client.ts` 调用 `/list-apps`，创建会话，并通过 `/run_sse` 进行流式接收。
</Step>

<Step>
`veadk.multimodal` 校验上传，统一管理本地/TOS 存储，在模型调用前解析媒体引用，并在历史持久化前保存模型返回的媒体。
</Step>

<Step>
`veadk.cli.frontend_invocation` 暴露 Agent 挂载的技能，并把结构化的 `/skill`、`@agent` 选择转换为 ADK 技能工具与 Agent 转移指令。
</Step>

<Step>
`a2ui/extract.ts` 从 `send_a2ui_json_to_client` 工具的响应（`validated_a2ui_json`）中提取 A2UI 消息。
</Step>

<Step>
`a2ui/Surface.tsx` 将 `createSurface` / `updateComponents` / `updateDataModel` 应用到 surface 状态中，并渲染根组件。
</Step>

<Step>
`a2ui/registry.ts` 将每个组件名映射到一个 React 渲染器；每个渲染器位于 `a2ui/components/<Name>/` 目录下并自行注册。
</Step>

</Steps>

### 添加企业自定义组件

一个自定义组件由两个“对半”组成，它们共享同一个 **catalog id**。后端部分见 [A2UI](/cn/docs/framework/a2ui#注册企业自定义组件)；前端部分如下。

**前端部分** —— 新建一个目录即可自动注册（无需修改任何中心文件）：

<Files>
  <Folder name="src/a2ui/components/RevenueChart" defaultOpen>
    <File name="RevenueChart.tsx" />
    <File name="index.ts" />
  </Folder>
</Files>

```ts title="src/a2ui/components/RevenueChart/index.ts"
import { register } from "../../registry";
import { RevenueChart } from "./RevenueChart";
register("RevenueChart", RevenueChart);
```

```tsx title="src/a2ui/components/RevenueChart/RevenueChart.tsx"
import type { ComponentRendererProps } from "../../registry";

export function RevenueChart({ node, ctx }: ComponentRendererProps) {
  const series = ctx.resolve(node.series as any);
  return <div className="corp-chart">{/* 渲染 series */}</div>;
}
```

`components/index.ts` 通过 `import.meta.glob` 导入每个 `*/index.ts`，因此新目录会被自动拾取。

<Callout type="info">
  未注册渲染器的未知组件会回退到可折叠的 JSON 视图，因此目录与渲染器不匹配也不会导致界面崩溃。
</Callout>
