create-windy 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +392 -160
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +1 -0
  6. package/template/apps/agent-server/Dockerfile +20 -0
  7. package/template/apps/agent-server/README.md +46 -0
  8. package/template/apps/agent-server/pyproject.toml +44 -0
  9. package/template/apps/agent-server/src/southwind_agent_server/__init__.py +5 -0
  10. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +39 -0
  11. package/template/apps/agent-server/src/southwind_agent_server/app.py +263 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/config.py +141 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +99 -0
  14. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +52 -0
  15. package/template/apps/agent-server/src/southwind_agent_server/engine.py +85 -0
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +197 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +246 -0
  18. package/template/apps/agent-server/src/southwind_agent_server/service.py +180 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +317 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/sse.py +41 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/store.py +144 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +80 -0
  23. package/template/apps/agent-server/tests/test_api.py +207 -0
  24. package/template/apps/agent-server/tests/test_config.py +56 -0
  25. package/template/apps/agent-server/tests/test_openai_provider.py +224 -0
  26. package/template/apps/agent-server/tests/test_sqlite_store.py +93 -0
  27. package/template/apps/agent-server/uv.lock +925 -0
  28. package/template/apps/server/src/agent/execution-grants.ts +89 -0
  29. package/template/apps/server/src/agent/remote-runtime.ts +128 -0
  30. package/template/apps/server/src/agent/run-contracts.ts +43 -0
  31. package/template/apps/server/src/agent/run-facade.test.ts +257 -0
  32. package/template/apps/server/src/agent/run-facade.ts +190 -0
  33. package/template/apps/server/src/agent/run-routes.ts +222 -0
  34. package/template/apps/server/src/agent/runtime-bootstrap.ts +53 -0
  35. package/template/apps/server/src/agent/tool-host.test.ts +242 -0
  36. package/template/apps/server/src/agent/tool-host.ts +153 -0
  37. package/template/apps/server/src/application-services.ts +2 -2
  38. package/template/apps/server/src/audit/search-summary.ts +15 -8
  39. package/template/apps/server/src/example-modules.ts +58 -70
  40. package/template/apps/server/src/foundation.ts +8 -4
  41. package/template/apps/server/src/guards.test.ts +13 -0
  42. package/template/apps/server/src/guards.ts +12 -2
  43. package/template/apps/server/src/index.ts +48 -95
  44. package/template/apps/server/src/installed-business-modules.ts +23 -4
  45. package/template/apps/server/src/module-bootstrap.ts +83 -0
  46. package/template/apps/server/src/module-composition.test.ts +54 -1
  47. package/template/apps/server/src/module-composition.ts +58 -0
  48. package/template/apps/server/src/module-host/initialize-contracts.ts +67 -0
  49. package/template/apps/server/src/module-host/initialize.test.ts +192 -0
  50. package/template/apps/server/src/module-host/initialize.ts +328 -0
  51. package/template/apps/server/src/module-host/request-context.test.ts +66 -0
  52. package/template/apps/server/src/module-host/request-context.ts +156 -0
  53. package/template/apps/server/src/module-host/role-presets.test.ts +94 -0
  54. package/template/apps/server/src/module-host/role-presets.ts +76 -0
  55. package/template/apps/server/src/module-host/route-installer.test.ts +317 -0
  56. package/template/apps/server/src/module-host/route-installer.ts +97 -0
  57. package/template/apps/server/src/module-host/route-registration.test.ts +160 -0
  58. package/template/apps/server/src/module-host/search-providers.ts +31 -0
  59. package/template/apps/server/src/module-host/storage-port.test.ts +165 -0
  60. package/template/apps/server/src/module-host/storage-port.ts +59 -0
  61. package/template/apps/server/src/module-host/task-definitions.ts +41 -0
  62. package/template/apps/server/src/module-host/test-fixtures.ts +26 -0
  63. package/template/apps/server/src/module-host/tool-handlers.ts +25 -0
  64. package/template/apps/server/src/module-host.test.ts +202 -58
  65. package/template/apps/server/src/module-host.ts +35 -113
  66. package/template/apps/server/src/module-runtime-validation.ts +40 -0
  67. package/template/apps/server/src/route-guards.test.ts +112 -1
  68. package/template/apps/server/src/runtime-feature.ts +66 -31
  69. package/template/apps/server/src/runtime.test.ts +1 -0
  70. package/template/apps/server/src/runtime.ts +3 -1
  71. package/template/apps/server/src/system/built-in-roles.ts +1 -1
  72. package/template/apps/server/src/work-order/foundation-modules.test.ts +30 -10
  73. package/template/apps/server/src/work-order/routes.test.ts +30 -18
  74. package/template/apps/server/src/work-order/routes.ts +104 -135
  75. package/template/apps/server/src/work-order/search-api.test.ts +14 -3
  76. package/template/apps/server/src/work-order/search-provider.test.ts +23 -15
  77. package/template/apps/server/src/work-order/search-provider.ts +17 -12
  78. package/template/apps/server/src/work-order/task.test.ts +16 -11
  79. package/template/apps/web/Dockerfile +4 -1
  80. package/template/apps/web/src/layout/GlobalWatermark.layering.webtest.ts +4 -1
  81. package/template/apps/web/src/layout/GlobalWatermark.vue +2 -0
  82. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +4 -1
  83. package/template/docker-compose.yml +25 -0
  84. package/template/docs/architecture/ai-runtime.md +744 -0
  85. package/template/docs/architecture/object-storage.md +12 -0
  86. package/template/docs/platform/agent-runtime.md +128 -0
  87. package/template/docs/platform/agent-tools.md +8 -1
  88. package/template/package.json +1 -0
  89. package/template/packages/config/index.test.ts +100 -0
  90. package/template/packages/config/index.ts +1 -0
  91. package/template/packages/config/package.json +2 -1
  92. package/template/packages/config/src/ai-openai-compatible.ts +131 -0
  93. package/template/packages/config/src/platform.ts +28 -1
  94. package/template/packages/database/package.json +1 -1
  95. package/template/packages/database/src/runner.ts +7 -9
  96. package/template/packages/example-work-order/index.ts +1 -0
  97. package/template/packages/example-work-order/package.json +2 -0
  98. package/template/packages/example-work-order/src/server-module.ts +61 -0
  99. package/template/packages/jobs/package.json +1 -1
  100. package/template/packages/jobs/src/types.ts +7 -1
  101. package/template/packages/modules/package.json +1 -1
  102. package/template/packages/modules/src/catalog-validation.test.ts +212 -0
  103. package/template/packages/modules/src/catalog-validation.ts +19 -2
  104. package/template/packages/modules/src/migration-bundle-validation.test.ts +191 -0
  105. package/template/packages/server-sdk/index.ts +53 -0
  106. package/template/packages/server-sdk/package.json +18 -3
  107. package/template/packages/server-sdk/src/actor.ts +15 -0
  108. package/template/packages/server-sdk/src/ai-operation-registration.ts +31 -0
  109. package/template/packages/server-sdk/src/audit-port.ts +22 -0
  110. package/template/packages/server-sdk/src/jobs-port.ts +24 -0
  111. package/template/packages/server-sdk/src/module-host.ts +41 -1
  112. package/template/packages/server-sdk/src/namespace.test.ts +26 -0
  113. package/template/packages/server-sdk/src/namespace.ts +13 -0
  114. package/template/packages/server-sdk/src/request-context.ts +21 -0
  115. package/template/packages/server-sdk/src/role-preset.ts +18 -0
  116. package/template/packages/server-sdk/src/route-definition.ts +26 -0
  117. package/template/packages/server-sdk/src/search-provider-port.ts +48 -0
  118. package/template/packages/server-sdk/src/storage-port.ts +25 -0
  119. package/template/packages/server-sdk/src/task-registration.ts +18 -0
  120. package/template/packages/server-sdk/src/tool-host-port.ts +22 -0
  121. package/template/packages/server-sdk/src/tool-registration.ts +29 -0
  122. package/template/packages/shared/index.ts +1 -0
  123. package/template/packages/shared/package.json +1 -1
  124. package/template/packages/shared/src/license-catalog.test.ts +73 -1
  125. package/template/packages/shared/src/license-catalog.ts +95 -0
  126. package/template/packages/{database/src/bundle-validation.ts → shared/src/migration-bundle-validation.ts} +2 -1
  127. package/template/apps/server/src/work-order/task.ts +0 -30
@@ -0,0 +1,744 @@
1
+ # Southwind AI Runtime 架构
2
+
3
+ 更新时间:2026-07-25。
4
+
5
+ 状态:架构已确认,首个可消费的文本 Agent 切片已实现。当前包含 Module AI Operation
6
+ 注册、Guarded Run facade、短时 Execution Grant、受限 Tool Host、OpenAI-compatible
7
+ Provider、canonical HTTP/SSE、取消、cursor 重放和单副本有界 SQLite Store。未配置
8
+ Provider 时仍安全拒绝。ADK Workflow、LiteLLM 多 Provider 路由、PostgreSQL 多副本
9
+ Store、Document AI 和 Speech 运行进程尚未实现,不能据此宣称完整 AI Runtime 已交付。
10
+
11
+ ## 目标
12
+
13
+ Southwind AI 为生成项目提供可选的 AI Runtime。它统一治理模型 Deployment、业务 Operation、Feature Flag、RBAC Permission、License、Audit、Secret、用量和离线部署,但不自行重做通用 Agent Framework、LLM Router、OCR 引擎或语音模型。
14
+
15
+ 首期目标:
16
+
17
+ - 使用 Google ADK 承载 Agent、Workflow、Session、Eval 和开发调试;
18
+ - 使用自托管 LiteLLM Proxy 管理多个 LLM/VLM Provider;
19
+ - 支持 DeepSeek、GLM、Kimi、Qwen 及本地或内网 OpenAI-compatible 模型;
20
+ - 支持文本 LLM/VLM 的 canonical SSE 流式传输;
21
+ - 支持本地或云端 OCR、文档解析、ASR、TTS 和 AI Dictation;
22
+ - 支持完全离线、内网私有化和受控公网三种部署环境;
23
+ - 业务 Module 只依赖稳定 Operation,不依赖 ADK、LiteLLM、模型名或 Provider SDK。
24
+
25
+ 首期不包含腾讯混元。未列出的 Provider 通过 Deployment Registry 和 Adapter 扩展,不扩充业务 Interface。
26
+
27
+ ## 核心决策
28
+
29
+ 1. Agent Runtime 是 Southwind AI 的官方可选底座能力,不是每个业务 Module 自行实现的功能。
30
+ 2. Agent Server 独立部署,首期使用 Python ADK;ADK 只属于内部 Implementation。
31
+ 3. 首个文本切片由 Agent Server 内置 OpenAI-compatible Adapter;需要多 Provider
32
+ 路由、fallback 与集中限流时,再引入独立自托管 LiteLLM Proxy。
33
+ 4. OCR 和文档解析由独立 Document AI Server 执行,不放入 Agent Server 或 LiteLLM。
34
+ 5. ASR、TTS 和 AI Dictation 由独立 Speech Server 执行;实时 Agent 会话由 Agent Server 编排。
35
+ 6. 文本 LLM、VLM 和 Agent 输出使用 SSE;实时音频/视频需要双向传输,使用 WebSocket。
36
+ 7. Southwind AI Server 是身份、Feature、Permission、License、数据范围、Tool 授权和 Audit 的事实源。
37
+ 8. 所有业务文件通过 `ArtifactRef` 传递,不在跨服务 DTO 中携带大型 Base64 文件或本地绝对路径。
38
+
39
+ ## 总体拓扑
40
+
41
+ ```text
42
+ 业务 Module / Web
43
+
44
+ │ AgentOperation / DocumentOperation / SpeechOperation
45
+
46
+ Southwind AI Server
47
+ ├── Feature / RBAC / License / Audit / Data Scope
48
+ ├── AI Deployment Registry
49
+ ├── Agent Tool Host
50
+ └── 短时 Execution Grant
51
+
52
+ ├── Agent Server(Python + ADK)
53
+ │ ├── Prompt / Workflow / Session / Eval
54
+ │ └── canonical SSE / Live WebSocket
55
+ │ │
56
+ │ ▼
57
+ │ LiteLLM Proxy
58
+ │ ├── DeepSeek / GLM / Kimi / Qwen
59
+ │ └── vLLM / Ollama / OpenAI-compatible
60
+
61
+ ├── Document AI Server
62
+ │ ├── PaddleOCR
63
+ │ ├── Unlimited OCR
64
+ │ └── 云端或内网 OCR Adapter
65
+
66
+ └── Speech Server
67
+ ├── ASR / TTS
68
+ ├── AI Dictation
69
+ └── 本地、内网或云端 Speech Adapter
70
+ ```
71
+
72
+ 这些运行进程可以由同一套离线介质和 Compose profile 交付,但保持独立故障域和资源生命周期。未安装某个可选运行进程时,只将对应 Operation 标记为 unavailable,不阻止普通非 AI 功能启动。
73
+
74
+ ## Module 与部署职责
75
+
76
+ ### Southwind AI Server
77
+
78
+ 拥有:
79
+
80
+ - Module Manifest 中的 AI Operation 和 Tool 声明;
81
+ - Feature、Permission、License 和数据范围 Guard;
82
+ - Execution Grant 签发;
83
+ - Agent Tool 的 schema 校验、二次授权、人工审批和幂等执行;
84
+ - AI Deployment、Route Policy 和 Secret Reference 的 Admin 控制面;
85
+ - 面向业务的稳定错误和审计语义;
86
+ - Run 与业务资源、Job、Artifact、Audit 的关联;
87
+ - 各运行进程的健康状态和 capability digest 校验。
88
+
89
+ 不拥有:
90
+
91
+ - ADK Session 或 Workflow 实现;
92
+ - Provider API Key 明文;
93
+ - LiteLLM 路由内部状态;
94
+ - OCR、ASR、TTS 模型进程;
95
+ - Agent Server、Document AI Server 或 Speech Server 的 Repository。
96
+
97
+ ### Agent Server
98
+
99
+ 拥有:
100
+
101
+ - ADK Runner、Workflow、Session、Checkpoint、Eval;
102
+ - Agent、Workflow 和 Prompt Release;
103
+ - Run 生命周期、取消、恢复和 canonical event;
104
+ - ADK Event 到 Southwind AI Event 的转换;
105
+ - Agent Step、Prompt Version、Tool Call 和模型 attempt 的关联;
106
+ - 开发环境 ADK Web;
107
+ - 实时 Agent 会话编排。
108
+
109
+ Agent Server 不直接访问业务 Repository。模型产生 Tool Call 只表示执行意图;每次真实 Tool 执行都必须回到 Southwind AI Tool Host,重新检查当前 Feature、License、Permission 和数据范围。
110
+
111
+ ### LiteLLM Proxy
112
+
113
+ 拥有:
114
+
115
+ - Provider、Deployment 和模型凭据;
116
+ - route alias 到实际模型的映射;
117
+ - health、负载均衡、retry、cooldown、fallback 和限流;
118
+ - Provider 原始 token、cost 和错误;
119
+ - 公网、本地和内网模型 Endpoint。
120
+
121
+ Agent Server 只使用稳定 route alias,例如:
122
+
123
+ ```text
124
+ agent.fast
125
+ agent.reasoning
126
+ agent.long-context
127
+ agent.structured
128
+ agent.vision
129
+ speech.dictation.polish
130
+ ```
131
+
132
+ ADK 和 LiteLLM 不同时维护两套路由或 fallback。ADK 决定运行哪个 Agent 或 Step,LiteLLM 决定该 Step 落到哪个模型 Deployment。
133
+
134
+ ### Document AI Server
135
+
136
+ 拥有:
137
+
138
+ - OCR、版面分析、表格识别和长文档解析 Adapter;
139
+ - 文档处理 Run、页级进度和结果 Artifact;
140
+ - PaddleOCR、Unlimited OCR、本地/内网/云端 OCR 的能力归一化;
141
+ - 页数、图像、像素、处理时长和 GPU 时间等用量事实。
142
+
143
+ 即使底层 Unlimited OCR 或其他 OCR 使用视觉语言模型,业务 Interface 仍是 Document Operation,不暴露 Chat Completion。
144
+
145
+ ### Speech Server
146
+
147
+ 拥有:
148
+
149
+ - 批量和实时 ASR;
150
+ - 流式和非流式 TTS;
151
+ - VAD、音频格式转换、稳定片段判定;
152
+ - AI Dictation 的 ASR 与 LLM 润色编排;
153
+ - 音频秒数、字符数、实时连接和 GPU 时间等用量事实。
154
+
155
+ 纯转写和 AI Dictation 不依赖 Agent Server。只有包含 Agent Session、Tool Calling 或多轮任务执行的实时对话才进入 Agent Server。
156
+
157
+ ## 对业务公开的深 Module
158
+
159
+ 首期只提供一个公开 npm Module:
160
+
161
+ ```text
162
+ @southwind-ai/ai-client
163
+ ├─ @southwind-ai/ai-client/agent
164
+ └─ @southwind-ai/ai-client/contracts
165
+ ```
166
+
167
+ 普通业务从根入口调用 Agent;需要精确依赖边界的基础设施可使用子路径。Document 与
168
+ Speech 的 Interface 等出现真实调用能力时,再在同一 package 内增加 `./document` 与
169
+ `./speech` 子路径,不提前发布空壳。
170
+
171
+ npm Module 数量与部署进程数量是两个不同维度。Southwind Server、Agent Server、
172
+ Document AI Server 和 Speech Server 仍独立部署:它们具有不同的协议、资源和故障域;
173
+ 业务侧则只学习一个稳定入口。`ai-client` 内部通过 Agent、Document、Speech Adapter
174
+ 隔离远端 Implementation,不能把 ADK、LiteLLM、PaddleOCR 或 Provider SDK 类型暴露给业务。
175
+
176
+ ### Agent Interface
177
+
178
+ ```ts
179
+ interface AgentCaller {
180
+ start<Input, Output>(
181
+ operation: AgentOperation<Input, Output>,
182
+ input: Input,
183
+ options: {
184
+ idempotencyKey: string;
185
+ signal?: AbortSignal;
186
+ },
187
+ ): Promise<AgentRun<Output>>;
188
+ }
189
+
190
+ interface AgentRun<Output> {
191
+ readonly id: string;
192
+ readonly events: AsyncIterable<AgentEvent>;
193
+ result(): Promise<Output>;
194
+ cancel(reason?: string): Promise<void>;
195
+ }
196
+ ```
197
+
198
+ 业务只知道 Operation Key、类型化输入输出、幂等键、canonical event 和终态结果,不知道 Provider、模型名、Prompt 明文、ADK Agent 或 LiteLLM 参数。
199
+
200
+ ### Document Interface
201
+
202
+ ```ts
203
+ interface DocumentAiClient {
204
+ submit(
205
+ operation: DocumentOperation,
206
+ input: {
207
+ source: ArtifactRef;
208
+ options?: DocumentProcessingOptions;
209
+ },
210
+ context: {
211
+ idempotencyKey: string;
212
+ signal?: AbortSignal;
213
+ },
214
+ ): Promise<DocumentRun>;
215
+ }
216
+ ```
217
+
218
+ 输出可以引用纯文本、Markdown、搜索型 PDF、页级文本、坐标、表格、版面树或结构化 JSON Artifact。
219
+
220
+ ### Speech Interface
221
+
222
+ ```ts
223
+ interface SpeechClient {
224
+ transcribe(
225
+ source: ArtifactRef,
226
+ options?: TranscriptionOptions,
227
+ ): Promise<TranscriptionRun>;
228
+
229
+ openTranscriptionStream(
230
+ options: TranscriptionStreamOptions,
231
+ ): Promise<TranscriptionStream>;
232
+
233
+ openDictation(options: DictationOptions): Promise<DictationStream>;
234
+
235
+ synthesize(input: SpeechSynthesisInput): Promise<SpeechSynthesisRun>;
236
+ }
237
+ ```
238
+
239
+ ## Module Manifest
240
+
241
+ 每个面向业务的 AI Operation 必须在所属 Module Manifest 中声明:
242
+
243
+ - 稳定 Operation Key;
244
+ - Feature Key;
245
+ - Permission Key;
246
+ - Audit Action;
247
+ - 输入和输出 schema;
248
+ - 允许使用的 Tool Key;
249
+ - 数据分类;
250
+ - 所需模型 capability;
251
+ - 交互方式:interactive、streaming、background 或 live;
252
+ - 最大运行时间、输入大小和输出大小;
253
+ - 是否允许向公网 Provider 发送数据。
254
+
255
+ 示例:
256
+
257
+ ```ts
258
+ interface ModuleAiOperationDefinition {
259
+ key: string;
260
+ kind: "agent" | "document" | "speech";
261
+ featureKey: string;
262
+ permissionKey: string;
263
+ auditAction: string;
264
+ inputSchemaKey: string;
265
+ outputSchemaKey: string;
266
+ requiredCapabilities: readonly string[];
267
+ allowedToolKeys: readonly string[];
268
+ dataClassification: "public" | "internal" | "sensitive";
269
+ execution: "interactive" | "streaming" | "background" | "live";
270
+ }
271
+ ```
272
+
273
+ 真实 Operation Handler 与 Manifest 声明必须一一对应。重复、缺失、未知引用、命名空间不匹配和 capability digest 漂移都应在监听前 fail-fast。
274
+
275
+ ## AI Deployment Registry
276
+
277
+ Deployment 描述一个可调用的模型部署,不把能力写死为封闭 Provider 枚举:
278
+
279
+ ```ts
280
+ interface AiDeploymentDefinition {
281
+ key: string;
282
+ protocol:
283
+ | "openai-chat"
284
+ | "openai-realtime"
285
+ | "gemini-live"
286
+ | "whisper-compatible"
287
+ | "ollama"
288
+ | "paddle-ocr"
289
+ | "custom-http";
290
+ endpoint: string;
291
+ credentialRef?: string;
292
+ upstreamModel?: string;
293
+ inputModalities: readonly (
294
+ "text" | "image" | "audio" | "video" | "document"
295
+ )[];
296
+ outputModalities: readonly ("text" | "audio" | "embedding" | "document")[];
297
+ capabilities: readonly string[];
298
+ networkScope: "public" | "intranet" | "localhost";
299
+ }
300
+ ```
301
+
302
+ 本地和内网 Endpoint 必须支持:
303
+
304
+ - HTTP 或 HTTPS;
305
+ - IPv4、内网 DNS 和容器服务名;
306
+ - 自定义端口和 Base Path;
307
+ - 可选 API Key;
308
+ - 私有 CA;
309
+ - connect timeout、request timeout 和并发上限。
310
+
311
+ Admin 控制面可以分开输入协议、主机、端口和 Base Path,但持久化前归一化为合法 `endpoint`。业务调用不能临时覆盖 Endpoint。
312
+
313
+ Endpoint 配置属于高风险管理动作,必须具备独立 Permission 和关键审计。出站请求应支持域名或 CIDR allowlist,并禁止访问云 metadata 等保留地址,避免形成 SSRF 通道。
314
+
315
+ ## LLM、VLM 与 Agent SSE 协议
316
+
317
+ ### 适用范围
318
+
319
+ 以下文本或结构化输出统一使用 SSE:
320
+
321
+ - 普通 LLM Generation;
322
+ - Agent Run;
323
+ - VLM 图片/视频输入后的文本输出;
324
+ - Tool 状态、审批状态、usage 和终态事件;
325
+ - 需要断线恢复的交互式文本 Run。
326
+
327
+ SSE 是单向 Server-to-Client 协议。创建、取消、审批、恢复和 Tool continuation 使用独立 HTTP command,不在 SSE 连接中反向发送。
328
+
329
+ Southwind AI Server 与 Agent Server 之间使用本节定义的 canonical SSE;Agent Server 调用 LiteLLM 的文本 LLM/VLM 时使用 OpenAI-compatible SSE streaming。Provider 专有流协议在 LiteLLM Adapter 内终止,不能越过该 Seam。VLM 的图片或视频作为请求 Artifact 发送,模型的文本与结构化响应仍通过 SSE 返回。
330
+
331
+ 实时语音和视频需要客户端与服务端同时发送媒体帧,因此使用 WebSocket,不属于本节 SSE 约束。
332
+
333
+ ### 两阶段调用
334
+
335
+ 先创建可持久化 Run:
336
+
337
+ ```http
338
+ POST /api/agent/runs
339
+ Content-Type: application/json
340
+ Idempotency-Key: request-123
341
+ ```
342
+
343
+ 响应:
344
+
345
+ ```json
346
+ {
347
+ "runId": "run_123",
348
+ "status": "accepted",
349
+ "eventsUrl": "/api/agent/runs/run_123/events"
350
+ }
351
+ ```
352
+
353
+ 再建立 SSE:
354
+
355
+ ```http
356
+ GET /api/agent/runs/run_123/events
357
+ Accept: text/event-stream
358
+ Last-Event-ID: 42
359
+ ```
360
+
361
+ 响应至少包含:
362
+
363
+ ```http
364
+ Content-Type: text/event-stream; charset=utf-8
365
+ Cache-Control: no-cache, no-transform
366
+ X-Accel-Buffering: no
367
+ ```
368
+
369
+ 取消使用独立 command:
370
+
371
+ ```http
372
+ POST /api/agent/runs/run_123/cancel
373
+ Content-Type: application/json
374
+ ```
375
+
376
+ 收到 `run.completed` 后,客户端读取并按 Operation 的输出 parser 校验最终结果:
377
+
378
+ ```http
379
+ GET /api/agent/runs/run_123/result
380
+ Accept: application/json
381
+ ```
382
+
383
+ ```json
384
+ {
385
+ "runId": "run_123",
386
+ "status": "completed",
387
+ "output": {}
388
+ }
389
+ ```
390
+
391
+ 两阶段调用允许页面刷新、网络重连和后台 Run 继续执行,不要求重新发起模型调用。
392
+
393
+ ### Event 格式
394
+
395
+ 每条事件包含 SSE `id`、稳定 `event` 名称和单行 JSON `data`:
396
+
397
+ ```text
398
+ id: 43
399
+ event: output.delta
400
+ data: {"runId":"run_123","sequence":43,"text":"你好"}
401
+
402
+ ```
403
+
404
+ canonical event:
405
+
406
+ ```ts
407
+ type AgentEvent =
408
+ | { type: "run.started"; runId: string; sequence: number }
409
+ | { type: "output.delta"; runId: string; sequence: number; text: string }
410
+ | {
411
+ type: "tool.requested";
412
+ runId: string;
413
+ sequence: number;
414
+ toolCallId: string;
415
+ toolKey: string;
416
+ }
417
+ | {
418
+ type: "approval.required";
419
+ runId: string;
420
+ sequence: number;
421
+ workItemId: string;
422
+ }
423
+ | {
424
+ type: "tool.completed";
425
+ runId: string;
426
+ sequence: number;
427
+ toolCallId: string;
428
+ }
429
+ | {
430
+ type: "usage.reported";
431
+ runId: string;
432
+ sequence: number;
433
+ usage: AgentUsage;
434
+ }
435
+ | { type: "run.completed"; runId: string; sequence: number }
436
+ | {
437
+ type: "run.failed";
438
+ runId: string;
439
+ sequence: number;
440
+ error: AgentError;
441
+ }
442
+ | { type: "run.cancelled"; runId: string; sequence: number };
443
+ ```
444
+
445
+ ADK Event、LiteLLM chunk、OpenAI response event、Provider reasoning delta 和 partial JSON 不直接向业务透传。
446
+
447
+ ### SSE 不变量
448
+
449
+ - `sequence` 在单个 Run 内严格单调递增;
450
+ - SSE `id` 使用可恢复的 event cursor,不使用 token 文本或 Provider event ID;
451
+ - `Last-Event-ID` 和显式 `after` cursor 都能恢复,服务端采用 at-least-once 投递,客户端按 event ID 去重;
452
+ - 每个 Run 恰好一个 terminal event:completed、failed 或 cancelled;
453
+ - terminal event 之后不再发送业务事件;
454
+ - 首个用户可见 `output.delta` 或 Tool 请求之后,不允许静默切换模型并拼接新答案;
455
+ - fallback 只能发生在当前模型 Step 尚未产生用户可见输出和副作用之前;
456
+ - Tool 参数完整聚合并通过 schema、Guard 和审批后才执行;
457
+ - 断开 SSE 不等于取消 Run,取消必须调用 command;
458
+ - Server 使用有界 buffer;慢消费者通过事件存储重放,不能无限占用内存;
459
+ - 代理层必须关闭响应缓冲,并周期发送 SSE comment heartbeat;
460
+ - Provider 原始错误、Prompt、Secret、Endpoint 和响应正文不能进入普通错误事件。
461
+
462
+ ### 稳定错误
463
+
464
+ 业务可见错误至少包括:
465
+
466
+ ```text
467
+ OPERATION_NOT_FOUND
468
+ OPERATION_DISABLED
469
+ INVALID_INPUT
470
+ FORBIDDEN
471
+ LICENSE_NOT_ALLOWED
472
+ RUNTIME_UNAVAILABLE
473
+ MODEL_ROUTE_UNAVAILABLE
474
+ PROVIDER_UNAVAILABLE
475
+ RATE_LIMITED
476
+ BUDGET_EXCEEDED
477
+ TOOL_DENIED
478
+ APPROVAL_REQUIRED
479
+ OUTPUT_VALIDATION_FAILED
480
+ RUN_CONFLICT
481
+ RUN_TIMEOUT
482
+ RUN_CANCELLED
483
+ STREAM_INTERRUPTED
484
+ ```
485
+
486
+ 错误携带 `requestId`、稳定 code、可重试标记和安全中文说明,不透传 Provider HTTP body。
487
+
488
+ ## VLM 与多模态
489
+
490
+ 文本加图片或视频输入、文本或结构化输出的 VLM 继续通过 LiteLLM 和 Agent Server:
491
+
492
+ ```text
493
+ agent.vision
494
+ → private.qwen-vl
495
+ → approved.cloud-vlm
496
+ ```
497
+
498
+ 输入使用有权限的 `ArtifactRef`。业务不能提交任意公网媒体 URL;Adapter 应从 Object Storage 读取或生成受限、短时的内部访问地址。
499
+
500
+ 同一个底层 VLM Endpoint 可以服务不同 Operation:
501
+
502
+ ```text
503
+ agent.vision.inspect
504
+ document.parse.multimodal
505
+ ```
506
+
507
+ 前者遵循 LLM/VLM SSE Interface,后者遵循 Document Run Interface。底层模型类型不能替代业务能力语义。
508
+
509
+ ## Document AI
510
+
511
+ 首期支持:
512
+
513
+ - PaddleOCR;
514
+ - Unlimited OCR;
515
+ - 可配置地址、端口和凭据的本地或内网 OCR Server;
516
+ - 经数据策略允许的云 OCR Adapter。
517
+
518
+ 建议 Operation:
519
+
520
+ ```text
521
+ document.ocr.fast
522
+ document.ocr.accurate
523
+ document.parse.long
524
+ document.parse.layout
525
+ document.extract.table
526
+ document.extract.handwriting
527
+ ```
528
+
529
+ 长文档采用异步 Run。输入和结果通过 `ArtifactRef` 关联,页级进度可以使用状态查询或独立 SSE;文档内容本身不以 token delta 形式传输。
530
+
531
+ 路由考虑文档类型、页数、文件大小、语言、表格/版面需求、GPU、数据分类、成本和时限。敏感文档默认只允许 localhost 或 intranet Deployment,除非 Operation 明确允许出网。
532
+
533
+ ## Speech 与 AI Dictation
534
+
535
+ ### 原始能力
536
+
537
+ Speech Server 提供:
538
+
539
+ ```text
540
+ speech.transcribe.batch
541
+ speech.transcribe.realtime
542
+ speech.synthesize
543
+ speech.dictation
544
+ ```
545
+
546
+ 批量音频使用 `ArtifactRef`,实时音频使用 WebSocket。
547
+
548
+ ### AI Dictation
549
+
550
+ AI Dictation 不是传统直转 ASR,而是以下复合流程:
551
+
552
+ ```text
553
+ 麦克风音频
554
+ → 降噪 / VAD / 分帧
555
+ → Streaming ASR
556
+ → partial 文本
557
+ → 稳定片段判定
558
+ → LiteLLM route: speech.dictation.polish
559
+ → 去水词、去重复、自我纠正、标点和轻量书面化
560
+ → revision-aware 文本 Patch
561
+ → 输入框
562
+ ```
563
+
564
+ 只把 ASR final、停顿切分或多次不再变化的稳定片段交给 LLM。不能对每个不稳定 partial 反复调用 LLM。
565
+
566
+ 建议使用两阶段润色:
567
+
568
+ 1. 自然停顿后进行低延迟片段润色,只处理水词、重复、标点和明显自我纠正;
569
+ 2. 用户结束输入后整理最近窗口或完整 utterance,合并跨片段语义并统一格式。
570
+
571
+ LLM 必须低温运行,禁止补充原文不存在的事实;数字、金额、人名和日期默认保守处理。低置信度片段应保留或标记,不自行猜测。服务保存可撤销的原始转写引用,旧 revision 不能覆盖用户后续手工编辑。
572
+
573
+ Dictation WebSocket event:
574
+
575
+ ```ts
576
+ type DictationEvent =
577
+ | { type: "asr.partial"; segmentId: string; text: string }
578
+ | {
579
+ type: "asr.final";
580
+ segmentId: string;
581
+ text: string;
582
+ confidence?: number;
583
+ }
584
+ | {
585
+ type: "rewrite.patch";
586
+ revision: number;
587
+ range: { start: number; end: number };
588
+ text: string;
589
+ sourceSegmentId: string;
590
+ }
591
+ | { type: "dictation.completed"; text: string }
592
+ | {
593
+ type: "dictation.warning";
594
+ code: "LOW_CONFIDENCE" | "CONTEXT_CONFLICT";
595
+ };
596
+ ```
597
+
598
+ 输入目的、有限 surrounding text 和业务词表可以作为受控上下文,例如邮件、工单、搜索或会议记录;不能把整个页面和无关敏感数据默认发送给模型。
599
+
600
+ ### 实时 Agent 对话
601
+
602
+ 实时对话支持两种 Adapter:
603
+
604
+ - 级联:ASR → ADK Agent/LLM → TTS;
605
+ - 原生双向模型:音频/视频 ⇄ Realtime Model。
606
+
607
+ 客户端先向 Southwind AI Server 创建 Live Session 并通过 Guard,再使用短时、绑定 Session 和 audience 的凭证连接 Agent Server WebSocket。Tool Call 仍回到 Southwind AI Tool Host 二次授权。
608
+
609
+ 首期不新增独立 Realtime Server。连接规模、电话线路、WebRTC 或媒体转码成为真实需求后,再把媒体数据面拆出。
610
+
611
+ ## Prompt、Debug 与 Trace
612
+
613
+ ADK Web 只在 developer profile 启动,绑定 localhost 或受控开发网络。它不是生产 Prompt 管理或生产 Debug 控制面。
614
+
615
+ Prompt 和 Workflow 使用:
616
+
617
+ ```text
618
+ draft → published → archived
619
+ ```
620
+
621
+ published version 不可原地修改。每次 Run 固定:
622
+
623
+ - Operation Version;
624
+ - Workflow Version;
625
+ - Prompt Version;
626
+ - Route Policy Version;
627
+ - Tool Schema Version。
628
+
629
+ 首期优先使用代码或 YAML、Git、Eval 和不可变 Release。只有出现非开发人员在线编辑的真实需求后,再扩展 diff、审批、回滚、fixture、route 对比和发布管理。
630
+
631
+ 生产 Trace 优先输出到客户本地 OTel Collector 与 Jaeger/Tempo。Southwind AI 控制面提供脱敏 Run 摘要和受权限保护的 Trace 入口,不复制 ADK Web。
632
+
633
+ 生产默认不持久化完整 Prompt、响应、音频、图片或 Tool 参数。明文诊断需要专门 Feature、Permission、加密、脱敏、TTL 和访问审计。
634
+
635
+ ## Usage 与成本
636
+
637
+ 用量按 attempt 追加,不覆盖 Run 行上的旧值:
638
+
639
+ | 能力 | 主要计量 |
640
+ | ---- | ----------------------------------------------------- |
641
+ | LLM | input/output/cache/reasoning token、首 token 延迟 |
642
+ | VLM | 文本 token、图片数量或图片 token、视频时长 |
643
+ | OCR | 页数、图片数、像素、处理时长、GPU 时间 |
644
+ | ASR | 输入音频秒数、实时连接时长、GPU 时间 |
645
+ | TTS | 输入字符数、输出音频秒数、GPU 时间 |
646
+ | Live | Session 时长、输入/输出音频秒数、LLM token、Tool 次数 |
647
+
648
+ 每条事实标记 `provider-reported`、`estimated` 或 `unknown`;缺失不能记为零。成本绑定调用时的价格快照。LiteLLM 保存 Provider/Deployment 原始事实,Agent Server 保存 Run/Step 归属,Document AI 和 Speech Server 保存各自模态事实,Southwind AI 通过共享 invocation ID 汇总展示。
649
+
650
+ ## 安全与授权
651
+
652
+ - 浏览器和普通业务前端不能直接使用 LiteLLM master key;
653
+ - Southwind AI Server 在发起 Run 前执行 License → Feature → Permission Guard;
654
+ - Remote Runtime 只接受短时、限 audience、限 Operation、绑定 actor、run 和 expiry 的 Execution Grant;
655
+ - Grant 不能携带长期有效的 Permission 列表作为授权事实;
656
+ - Tool 执行、人工批准后的继续和后台重试都重新读取当前授权;
657
+ - Agent Server 不接受浏览器提交的“已批准”布尔值;
658
+ - 写 Tool 使用 `toolCallId` 作为幂等键,并继续执行业务 expected-version 或幂等检查;
659
+ - Secret 只通过 `SecretRef`/`SecretResolver` 在 Adapter 最后时刻解析;
660
+ - Prompt、音频、图片、文档正文、Authorization、Cookie 和 Provider body 不进入普通日志;
661
+ - reasoning 原文和 chain-of-thought 不向业务流出。
662
+
663
+ ## 离线与私有化部署
664
+
665
+ ### 受控公网
666
+
667
+ 客户部署允许通过 egress allowlist 访问 DeepSeek、GLM、Kimi、Qwen 等公网 Provider。
668
+
669
+ ### 内网私有化
670
+
671
+ LiteLLM、Document AI 和 Speech Server 连接客户内网模型:
672
+
673
+ ```text
674
+ http://10.10.20.15:8000/v1
675
+ http://qwen.internal:9000/v1
676
+ https://speech.internal:8443/api
677
+ ```
678
+
679
+ ### 完全隔离
680
+
681
+ 只注册本地或内网 Deployment,例如 vLLM、Ollama、PaddleOCR、Unlimited OCR、本地 ASR/TTS。启动和正常运行不访问公网。
682
+
683
+ 所有模式共同要求:
684
+
685
+ - 容器镜像固定 digest;
686
+ - Python wheel、模型文件和静态资源随离线介质交付;
687
+ - 禁止启动时下载依赖、模型、Prompt 或价格表;
688
+ - 保留 SBOM、镜像签名验证和版本清单;
689
+ - 各运行进程拥有独立数据库或 schema,禁止跨库直接查询;
690
+ - 数据库迁移有升级、备份和回滚步骤;
691
+ - 完全断网环境执行 deny-egress 验收;
692
+ - Provider 未配置或不可达不能阻止无关业务功能启动。
693
+
694
+ LiteLLM 版本必须固定并通过供应链审查,客户现场禁止动态安装未验证版本。
695
+
696
+ ## 首期交付切片
697
+
698
+ 当前已完成第 1、2、4 项和第 5 项的受限 Tool Host 基础闭环,并交付可直接连接
699
+ OpenAI-compatible Provider 的文本 Agent Adapter。SQLite Store 提供单副本有界持久化,
700
+ 重启后保留终态和 cursor,但不替代后续 PostgreSQL 多副本 Store。ADK unavailable
701
+ Adapter 保留为未配置 Provider 时的 fail-closed fallback。
702
+
703
+ 1. 定义 Agent、Document、Speech Operation 与 Module Manifest 组合校验;
704
+ 2. 建立 Python Agent Server、ADK Adapter 和 deterministic test Adapter;
705
+ 3. 部署 LiteLLM Proxy,接入 DeepSeek、GLM、Kimi、Qwen 和一个本地 OpenAI-compatible Endpoint;
706
+ 4. 完成 Agent Run 创建、canonical SSE、断线恢复、取消和稳定错误;
707
+ 5. 接入一个真实 Agent Tool,验证 Feature、Permission、License、数据范围和 Audit;
708
+ 6. 建立 Prompt/Workflow immutable Release、PostgreSQL Session、usage 和 OTel trace;
709
+ 7. 建立 Document AI Server,先接 PaddleOCR 和一个本地/内网 Adapter;
710
+ 8. 建立 Speech Server,先完成 Streaming ASR 和 AI Dictation;
711
+ 9. 增加 VLM Artifact 输入和 `agent.vision` route;
712
+ 10. 验证受控公网、内网私有化和完全断网三种 profile。
713
+
714
+ 首期不同时实现 LangGraph Adapter、完整在线 Visual Builder、生产 ADK Web、腾讯混元或通用 GPU 调度平台。
715
+
716
+ ## 验收标准
717
+
718
+ - 业务代码不导入 ADK、LiteLLM 或 Provider SDK;
719
+ - 业务不传 Provider、模型名、Endpoint 或 API Key;
720
+ - 文本 LLM、VLM 和 Agent 输出统一通过 canonical SSE;
721
+ - SSE 断线后使用 cursor 恢复,不重新执行模型调用;
722
+ - Feature、Permission、License 任一拒绝都会 fail-closed;
723
+ - 模型 Tool Call 不能绕过 Southwind AI Tool Host;
724
+ - 同一 Run 能关联 Audit、ADK Trace、LiteLLM attempt 和 Usage;
725
+ - 本地/内网模型可配置协议、主机、端口和 Base Path;
726
+ - 完全断网环境仍可运行已配置的本地 LLM、OCR 和 Speech Operation;
727
+ - ADK Web 不出现在生产 profile;
728
+ - 非 AI 业务在任一可选 AI Runtime 不可用时仍可运行。
729
+
730
+ ## 相关文档与资料
731
+
732
+ - [平台基础设施](./foundation-infrastructure.md)
733
+ - [Module Manifest 组合契约](./module-manifest-composition.md)
734
+ - [模块初始化 Host](./module-host.md)
735
+ - [Agent Tool 注册与执行](../platform/agent-tools.md)
736
+ - [Agent Runtime 接入与运行](../platform/agent-runtime.md)
737
+ - [通用文件与对象存储](./object-storage.md)
738
+ - [运行时可观测性](../operations/observability.md)
739
+ - [ADK Web](https://adk.dev/runtime/web-interface/)
740
+ - [ADK Streaming](https://adk.dev/streaming/)
741
+ - [ADK Audio、Image 与 Video](https://adk.dev/streaming/dev-guide/part5/)
742
+ - [LiteLLM](https://docs.litellm.ai/)
743
+ - [PaddleOCR Serving](https://www.paddleocr.ai/main/en/version3.x/inference_deployment/serving/serving.html)
744
+ - [Unlimited OCR](https://github.com/baidu/Unlimited-OCR)