feat: establish v0.70 platform milestone
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-08-29 21:56:51 +08:00
parent d97bbf7cf5
commit e652516147
133 changed files with 12907 additions and 801 deletions
+1
View File
@@ -20,6 +20,7 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w \
-X data-terminal-cloud/internal/buildinfo.Commit=${COMMIT} \
-X data-terminal-cloud/internal/buildinfo.BuildTime=${BUILD_TIME}" -o /out/worker ./cmd/worker && \
CGO_ENABLED=0 go build -trimpath -o /out/sim-device ./cmd/sim-device && \
CGO_ENABLED=0 go build -trimpath -o /out/device-credential ./cmd/device-credential && \
CGO_ENABLED=0 go build -trimpath -o /out/firmware-conformance ./cmd/firmware-conformance && \
CGO_ENABLED=0 go build -trimpath -o /out/alert-receiver-conformance ./cmd/alert-receiver-conformance && \
CGO_ENABLED=0 go build -trimpath -o /out/kms-rotation-conformance ./cmd/kms-rotation-conformance && \
+20
View File
@@ -79,6 +79,26 @@
"emailVerified": true,
"enabled": true,
"credentials": [{"type": "password", "value": "LocalOnly123!", "temporary": false}]
},
{
"id": "00000000-0000-0000-0000-000000000104",
"username": "operator",
"firstName": "平台",
"lastName": "运维管理员",
"email": "operator@example.invalid",
"emailVerified": true,
"enabled": true,
"credentials": [{"type": "password", "value": "OpsLocalOnly123!", "temporary": false}]
},
{
"id": "00000000-0000-0000-0000-000000000105",
"username": "operator-approver",
"firstName": "平台",
"lastName": "运维审批员",
"email": "operator-approver@example.invalid",
"emailVerified": true,
"enabled": true,
"credentials": [{"type": "password", "value": "OpsLocalOnly123!", "temporary": false}]
}
]
}
+5
View File
@@ -60,6 +60,11 @@ data:
ARK_CONTENT_MODEL: replace-with-ark-content-endpoint-id
ARK_EMBEDDING_MODEL: replace-with-ark-embedding-endpoint-id
ARK_REQUEST_TIMEOUT: 30s
MEMORY_TOOL_TOKEN_TTL: 4h
MEMORY_TOOL_RATE_RPS: "2"
MEMORY_TOOL_RATE_BURST: "5"
PROVIDER_MEMORY_MODE: disabled
PROVIDER_MEMORY_TIMEOUT: 5s
WORKER_POLL_INTERVAL: 2s
ALERT_WEBHOOK_TIMEOUT: 5s
---
+6 -8
View File
@@ -55,14 +55,16 @@ spec:
value: /var/run/secrets/voice-platform/common/database-url
- name: REDIS_PASSWORD_FILE
value: /var/run/secrets/voice-platform/common/redis-password
- name: DEVICE_SECRET_DIRECTORY
value: /var/run/secrets/voice-platform/devices
- name: DEVICE_CREDENTIAL_MASTER_KEY_FILE
value: /var/run/secrets/voice-platform/api/device-credential-master-key
- name: RTC_APP_KEY_FILE
value: /var/run/secrets/voice-platform/api/rtc-app-key
- name: CURSOR_SIGNING_SECRET_FILE
value: /var/run/secrets/voice-platform/api/cursor-signing-secret
- name: SHARE_TOKEN_SIGNING_SECRET_FILE
value: /var/run/secrets/voice-platform/api/share-token-signing-secret
- name: MEMORY_TOOL_SIGNING_SECRET_FILE
value: /var/run/secrets/voice-platform/model-storage/memory-tool-signing-secret
- name: OBJECT_STORE_ACCESS_KEY_FILE
value: /var/run/secrets/voice-platform/model-storage/object-store-access-key
- name: OBJECT_STORE_SECRET_KEY_FILE
@@ -114,9 +116,6 @@ spec:
- name: model-storage-secrets
mountPath: /var/run/secrets/voice-platform/model-storage
readOnly: true
- name: device-secrets
mountPath: /var/run/secrets/voice-platform/devices
readOnly: true
volumes:
- name: common-secrets
secret:
@@ -127,9 +126,6 @@ spec:
- name: model-storage-secrets
secret:
secretName: voice-platform-model-storage-secrets
- name: device-secrets
secret:
secretName: voice-platform-device-secrets
---
apiVersion: apps/v1
kind: Deployment
@@ -311,6 +307,8 @@ spec:
value: /var/run/secrets/voice-platform/model-storage/ark-api-key
- name: CONTENT_SAFETY_API_KEY_FILE
value: /var/run/secrets/voice-platform/model-storage/content-safety-api-key
- name: MEMORY_TOOL_SIGNING_SECRET_FILE
value: /var/run/secrets/voice-platform/model-storage/memory-tool-signing-secret
ports:
- name: http
containerPort: 8082
@@ -19,7 +19,7 @@ metadata:
spec:
provider: replace-with-installed-provider
parameters:
objects: replace-with-provider-specific-rtc-app-key-cursor-and-share-secrets
objects: replace-with-provider-specific-rtc-app-key-cursor-share-and-device-credential-master-key
---
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
@@ -49,14 +49,4 @@ metadata:
spec:
provider: replace-with-installed-provider
parameters:
objects: replace-with-provider-specific-object-store-ark-and-content-safety-credentials
---
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: voice-platform-devices
namespace: voice-platform
spec:
provider: replace-with-installed-provider
parameters:
objects: replace-with-provider-specific-per-device-secrets
objects: replace-with-provider-specific-object-store-ark-content-safety-and-memory-tool-credentials
@@ -26,12 +26,6 @@ spec:
readOnly: true
volumeAttributes:
secretProviderClass: voice-platform-model-storage
- name: device-secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: voice-platform-devices
---
apiVersion: apps/v1
kind: Deployment
+26 -3
View File
@@ -10,6 +10,21 @@
.\deploy\local-rtc\configure.ps1
```
如果还要同时验证方舟大模型总结,可改用:
```powershell
.\deploy\local-rtc\configure.ps1 -EnableArkSummary
```
该模式会额外隐藏读取方舟API Key和内容模型Endpoint ID,并通过只读密钥文件传给Worker。
如果 RTC 配置已经存在,只需要补充大模型总结,不必使用`-Force`重录全部密钥。先在[方舟 API Key 控制台](https://console.volcengine.com/ark/region:ark+cn-beijing/apikey)创建密钥,再运行:
```powershell
.\deploy\local-rtc\configure-ark-summary.ps1
```
该脚本只更新`ark-api-key`、`CONTENT_GENERATOR`和`ARK_CONTENT_MODEL`,不会修改已有RTC、语音或设备凭据。当前Chat API既支持模型Model ID,也支持Endpoint ID;脚本默认使用`doubao-seed-2-0-lite-260215`,如果账号未开通该模型,可在提示中填写已开通的Model ID或Endpoint ID。
脚本会以隐藏输入方式依次读取:
- RTC AppKey;
@@ -34,8 +49,16 @@
1. 保持默认本地平台地址、设备 ID 和本地测试设备密钥;
2. 点击“检测平台和音频设备”;
3. 选择麦克风和扬声器;
4. 点击“开始语音聊天”;
5. 看到“豆包语音任务已激活”后直接说话;
6. 点击“结束通话”验证`StopVoiceChat`和RTC离房。
4. 确认同一个RTC AppID已在RTC控制台的“实时字幕”功能中启用流式语音识别;
5. 点击“开始语音聊天”;
6. 看到“RTC 实时字幕已启动”和“豆包语音任务已激活”后直接说话;
7. 确认“实时字幕与上传”区域同时出现长者和豆包话轮;
8. 点击“结束通话”验证字幕补交、`StopVoiceChat`和RTC离房。
实时字幕要求先在豆包语音控制台创建服务类型为“流式语音识别”的应用,再在RTC控制台的实时字幕配置中填写该应用的APP ID、Access Token和Cluster ID。未开通或配置不匹配时SDK返回`ServiceAccessDenied`;这与“实时对话式AI > 回调设置”无关,不需要为字幕上传填写回调URL。
通话结束后,Worker会根据最终话轮生成通话摘要、关键词、开放话题和待审核故事。只有人工确认故事后才会进入长期记忆,这是既有授权与审核边界,模拟器不会绕过审核自动写入长期记忆。本地默认`CONTENT_GENERATOR=local`仅用于确定性闭环验证;要验证真实大模型总结,必须另外为Worker配置`CONTENT_GENERATOR=ark`、`ARK_API_KEY_FILE`和`ARK_CONTENT_MODEL`。
当新通话冻结快照中的`story_extraction`和`personalized_memory`授权均有效时,平台会把最近3次已完成通话的就绪摘要作为短期连续上下文注入豆包;S2S-O模式使用`dialog.system_role`,无需开启供应商侧`dialog_id`历史留存。长者档案中的“避免主动提及”话题也会从下一次开房起一并生效。
RTC AppKey、火山 AK/SK 和豆包 Access Token 均不进入模拟器安装包,也不会通过渲染页面读取。
+5
View File
@@ -21,7 +21,12 @@ services:
# Quote date-shaped API versions so YAML does not coerce them into timestamps.
VOLC_RTC_API_VERSION: "2024-12-01"
VOLC_VOICE_CHAT_TEMPLATE_FILE: /run/secrets/local-rtc/voice-chat-template.json
CONTENT_GENERATOR: ${CONTENT_GENERATOR:-local}
ARK_API_KEY: ""
ARK_API_KEY_FILE: /run/secrets/local-rtc/ark-api-key
ARK_CONTENT_MODEL: ${ARK_CONTENT_MODEL:-}
volumes:
- ./deploy/local-rtc/runtime/volc-access-key-id:/run/secrets/local-rtc/volc-access-key-id:ro
- ./deploy/local-rtc/runtime/volc-secret-access-key:/run/secrets/local-rtc/volc-secret-access-key:ro
- ./deploy/local-rtc/runtime/voice-chat-template.json:/run/secrets/local-rtc/voice-chat-template.json:ro
- ./deploy/local-rtc/runtime/ark-api-key:/run/secrets/local-rtc/ark-api-key:ro
@@ -0,0 +1,52 @@
[CmdletBinding()]
param(
[string]$Model = "doubao-seed-2-0-lite-260215"
)
$ErrorActionPreference = "Stop"
$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path
$runtimeDirectory = Join-Path $scriptDirectory "runtime"
$environmentFile = Join-Path $runtimeDirectory "local-rtc.env"
$apiKeyFile = Join-Path $runtimeDirectory "ark-api-key"
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
if (-not (Test-Path -LiteralPath $environmentFile -PathType Leaf)) {
throw "Run .\deploy\local-rtc\configure.ps1 before configuring Ark summaries."
}
$secureKey = Read-Host "Ark API Key (input is hidden)" -AsSecureString
$pointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureKey)
try {
$apiKey = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($pointer)
}
finally {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($pointer)
}
if ([string]::IsNullOrWhiteSpace($apiKey)) {
throw "Ark API Key is required."
}
$selectedModel = Read-Host "Ark content Model ID or Endpoint ID [$Model]"
if ([string]::IsNullOrWhiteSpace($selectedModel)) {
$selectedModel = $Model
}
$selectedModel = $selectedModel.Trim()
if ([string]::IsNullOrWhiteSpace($selectedModel)) {
throw "Ark content model is required."
}
$settings = [ordered]@{}
foreach ($line in Get-Content -LiteralPath $environmentFile) {
if ($line -match '^([^#=][^=]*)=(.*)$') {
$settings[$matches[1].Trim()] = $matches[2]
}
}
$settings["CONTENT_GENERATOR"] = "ark"
$settings["ARK_CONTENT_MODEL"] = $selectedModel
$environmentContent = ($settings.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "`n"
[IO.File]::WriteAllText($apiKeyFile, $apiKey.Trim(), $utf8NoBom)
[IO.File]::WriteAllText($environmentFile, $environmentContent + "`n", $utf8NoBom)
Write-Host "Ark summary configuration updated. Existing RTC and voice credentials were not changed." -ForegroundColor Green
Write-Host "Next: .\deploy\local-rtc\start.ps1" -ForegroundColor Green
+9 -1
View File
@@ -1,6 +1,8 @@
[CmdletBinding()]
param(
[string]$RtcAppId = "6a8f92ca43726c017d34cce1",
[switch]$EnableArkSummary,
[string]$ArkContentModel = "",
[switch]$Force
)
@@ -57,6 +59,11 @@ $volcSecretAccessKey = Read-RequiredSecret "火山引擎 Secret AccessKey"
$doubaoAppId = Read-RequiredText "豆包端到端语音 APP ID"
$doubaoAccessToken = Read-RequiredSecret "豆包端到端语音 Access Token"
$speaker = Read-RequiredText "豆包音色 ID" "zh_female_vv_jupiter_bigtts"
$arkApiKey = ""
if ($EnableArkSummary) {
$arkApiKey = Read-RequiredSecret "方舟 API Key"
$ArkContentModel = Read-RequiredText "方舟内容模型 Endpoint ID" $ArkContentModel
}
$template = [ordered]@{
schema_version = "voice-chat-template-bundle-v1"
@@ -103,6 +110,7 @@ $template = [ordered]@{
Write-SecretFile "rtc-app-key" $rtcAppKey
Write-SecretFile "volc-access-key-id" $volcAccessKeyId
Write-SecretFile "volc-secret-access-key" $volcSecretAccessKey
Write-SecretFile "ark-api-key" $arkApiKey
[IO.File]::WriteAllText(
(Join-Path $runtimeDirectory "voice-chat-template.json"),
($template | ConvertTo-Json -Depth 20),
@@ -110,7 +118,7 @@ Write-SecretFile "volc-secret-access-key" $volcSecretAccessKey
)
[IO.File]::WriteAllText(
(Join-Path $runtimeDirectory "local-rtc.env"),
"RTC_APP_ID=$RtcAppId`n",
"RTC_APP_ID=$RtcAppId`nCONTENT_GENERATOR=$(if ($EnableArkSummary) { 'ark' } else { 'local' })`nARK_CONTENT_MODEL=$ArkContentModel`n",
$utf8NoBom
)
+7 -6
View File
@@ -10,13 +10,14 @@ $requiredFiles = @(
"rtc-app-key",
"volc-access-key-id",
"volc-secret-access-key",
"ark-api-key",
"voice-chat-template.json",
"local-rtc.env"
)
foreach ($name in $requiredFiles) {
if (-not (Test-Path -LiteralPath (Join-Path $runtimeDirectory $name) -PathType Leaf)) {
throw "缺少 $name,请先运行 .\deploy\local-rtc\configure.ps1"
throw "Missing $name. Run .\deploy\local-rtc\configure.ps1 first."
}
}
@@ -35,9 +36,9 @@ try {
"callbacks", "mock-alert-receiver", "mock-provider", "api", "worker"
)
& docker @arguments
if ($LASTEXITCODE -ne 0) { throw "Docker Compose 启动失败" }
if ($LASTEXITCODE -ne 0) { throw "Docker Compose startup failed." }
Write-Host "`n等待本地 API 就绪..." -ForegroundColor Cyan
Write-Host "`nWaiting for local API readiness..." -ForegroundColor Cyan
$deadline = [DateTime]::UtcNow.AddMinutes(3)
do {
Start-Sleep -Seconds 2
@@ -48,9 +49,9 @@ try {
catch { }
} while ([DateTime]::UtcNow -lt $deadline)
if (-not $health) { throw "本地 API 未在三分钟内就绪,请检查 docker compose logs api worker" }
Write-Host "本地真实 RTC 后端已就绪:http://localhost:58080" -ForegroundColor Green
Write-Host "现在可以启动 Windows 语音终端模拟器。" -ForegroundColor Green
if (-not $health) { throw "Local API did not become ready within three minutes. Check docker compose logs api worker." }
Write-Host "Local real RTC backend is ready: http://localhost:58080" -ForegroundColor Green
Write-Host "You can now start the Windows voice device simulator." -ForegroundColor Green
}
finally {
Pop-Location
+11
View File
@@ -15,14 +15,25 @@
载入同一候选版本的`data-terminal-cloud-backend`和`data-terminal-cloud-web`镜像后执行:
```sh
chmod 0755 deploy/server/*.sh
docker compose -f compose.yaml -f deploy/server/compose.override.yaml \
--env-file deploy/server/.env up -d --no-build \
postgres redis minio keycloak migrate seed mock-alert-receiver callbacks \
mock-provider api worker web gateway
docker compose -f compose.yaml -f deploy/server/compose.override.yaml \
--env-file deploy/server/.env up -d --no-build --force-recreate --no-deps gateway
```
从Windows生成的压缩包解开后必须恢复脚本执行位。若`current`是切换到新发布目录的符号链接,还必须强制重建Gateway;否则Docker会继续使用旧发布目录已解析的Nginx绑定挂载,新增路由不会生效。
安全组和主机防火墙只保留TCP 80、443以及受限来源的SSH 22。HTTP 80仅用于跳转到标准HTTPS 443。正式环境仍应配置自有域名、生产OIDC和真实外部服务,并切换到Kubernetes生产覆盖层。
需要在演示机接入真实RTC、豆包语音和方舟摘要时,把凭据文件以`0600`权限放入`/etc/data-terminal-cloud/voice-runtime`,并在`deploy/server/.env`设置`RTC_TOKEN_ISSUER=volcengine`、`VOICE_PROVIDER=volcengine`、`RTC_APP_ID`、`CONTENT_GENERATOR`和`ARK_CONTENT_MODEL`。Compose只读挂载该目录,敏感值不进入源码、镜像或环境文件。
设备HMAC密钥由`device_credential`表以AES-256-GCM密文保存。`deploy/server/.env`中的`DEVICE_CREDENTIAL_MASTER_KEY`只负责数据库密文的认证解密,不得写入数据库或设备;既有`DEVICE_LOCAL_SECRET`只用于首次部署升级时导入两台演示设备,确认数据库凭据记录完整后可移除。新增、轮换和吊销设备密钥使用后端镜像内的`/app/device-credential`维护命令,生成的设备明文密钥只显示一次。
通话内记忆工具只通过网关精确开放`/internal/v1/tools/memory-search`,请求必须携带平台为当前通话签发的短期任务令牌。`MEMORY_TOOL_SIGNING_SECRET`必须是服务器独立随机密钥,不得与游标签名、分享签名或RTC密钥复用;网关不会开放其他`/internal`路径。
## 自动备份与恢复校验
首次安装定时任务:
+2
View File
@@ -33,9 +33,11 @@ umask 077
printf 'KEYCLOAK_ADMIN_USERNAME=platform-admin\n'
printf 'KEYCLOAK_ADMIN_PASSWORD=%s\n' "$(openssl rand -hex 24)"
printf 'DEVICE_LOCAL_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'DEVICE_CREDENTIAL_MASTER_KEY=%s\n' "$(openssl rand -hex 32)"
printf 'RTC_TOKEN_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'CURSOR_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'SHARE_TOKEN_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'MEMORY_TOOL_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'PROVIDER_CALLBACK_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'PLATFORM_HEALTH_CALLBACK_SECRET=%s\n' "$(openssl rand -hex 32)"
printf 'MOCK_PROVIDER_API_KEY=%s\n' "$(openssl rand -hex 24)"
+22 -2
View File
@@ -19,6 +19,8 @@ x-server-backend-environment: &server-backend-environment
OBJECT_STORE_PUBLIC_SECURE: "true"
CURSOR_SIGNING_SECRET: ${CURSOR_SIGNING_SECRET}
SHARE_TOKEN_SIGNING_SECRET: ${SHARE_TOKEN_SIGNING_SECRET}
MEMORY_TOOL_BASE_URL: ${PUBLIC_ORIGIN}/internal/v1/tools/memory-search
MEMORY_TOOL_SIGNING_SECRET: ${MEMORY_TOOL_SIGNING_SECRET}
EMBEDDING_PROVIDER: local
VOICE_PROVIDER: ${VOICE_PROVIDER:-mock}
@@ -92,10 +94,15 @@ services:
environment:
<<: *server-backend-environment
HTTP_ADDR: 0.0.0.0:8080
DEVICE_LOCAL_SECRET: ${DEVICE_LOCAL_SECRET}
DEVICE_LOCAL_SECRET: ${DEVICE_LOCAL_SECRET:-}
DEVICE_CREDENTIAL_MASTER_KEY: ${DEVICE_CREDENTIAL_MASTER_KEY}
RTC_TOKEN_SIGNING_SECRET: ${RTC_TOKEN_SIGNING_SECRET}
RTC_TOKEN_ISSUER: ${RTC_TOKEN_ISSUER:-local}
RTC_APP_ID: ${RTC_APP_ID:-local-public-rtc-app}
RTC_APP_KEY: ""
RTC_APP_KEY_FILE: ${RTC_APP_KEY_FILE:-/run/secrets/voice-runtime/rtc-app-key}
volumes:
- /etc/data-terminal-cloud/voice-runtime:/run/secrets/voice-runtime:ro
restart: unless-stopped
callbacks:
@@ -122,8 +129,21 @@ services:
MOCK_PROVIDER_API_KEY: ${MOCK_PROVIDER_API_KEY}
ALERT_WEBHOOK_URL: http://mock-alert-receiver:8084/webhooks/alerts
ALERT_WEBHOOK_SECRET: ${ALERT_WEBHOOK_SECRET}
CONTENT_GENERATOR: local
RTC_APP_ID: ${RTC_APP_ID:-local-public-rtc-app}
VOLC_ACCESS_KEY_ID: ""
VOLC_ACCESS_KEY_ID_FILE: ${VOLC_ACCESS_KEY_ID_FILE:-/run/secrets/voice-runtime/volc-access-key-id}
VOLC_SECRET_ACCESS_KEY: ""
VOLC_SECRET_ACCESS_KEY_FILE: ${VOLC_SECRET_ACCESS_KEY_FILE:-/run/secrets/voice-runtime/volc-secret-access-key}
VOLC_RTC_REGION: ${VOLC_RTC_REGION:-cn-north-1}
VOLC_RTC_API_VERSION: ${VOLC_RTC_API_VERSION:-2024-12-01}
VOLC_VOICE_CHAT_TEMPLATE_FILE: ${VOLC_VOICE_CHAT_TEMPLATE_FILE:-/run/secrets/voice-runtime/voice-chat-template.json}
CONTENT_GENERATOR: ${CONTENT_GENERATOR:-local}
ARK_API_KEY: ""
ARK_API_KEY_FILE: ${ARK_API_KEY_FILE:-/run/secrets/voice-runtime/ark-api-key}
ARK_CONTENT_MODEL: ${ARK_CONTENT_MODEL:-}
CONTENT_SAFETY_PROVIDER: local
volumes:
- /etc/data-terminal-cloud/voice-runtime:/run/secrets/voice-runtime:ro
restart: unless-stopped
mock-provider:
+8
View File
@@ -44,6 +44,14 @@ server {
proxy_buffering off;
}
# The voice provider can call only this signed, conversation-scoped tool.
# Do not expose the rest of the /internal namespace.
location = /internal/v1/tools/memory-search {
proxy_pass $api_backend;
proxy_buffering off;
proxy_request_buffering off;
}
location /callbacks/ {
proxy_pass $callbacks_backend;
proxy_buffering off;