ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

A2UI FileUpload 组件架构设计:基于 IoC 委托与内联回退的双策略文件上传方案

A2UI FileUpload 组件架构设计:基于 IoC 委托与内联回退的双策略文件上传方案 A2UI FileUpload 组件架构设计基于 IoC 委托与内联回退的双策略文件上传方案【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui本篇技术指南以仓库中的架构设计提案 fileupload_component_design.md 为核心骨架完整讲解 A2UI 如何在不污染 Agent 上下文的前提下原生支持文件上传通过单一组件、双策略的混合架构——宿主委托IoC 回调为主、内联 data URI 回退为辅并配套隐式/显式两种指针解析模型。文章同时结合仓库中的 Angular 宿主示例 与 ADK 文件摘要 Agent 示例 的源码与测试给出可复制、可运行的完整落地方案。读完本文你将掌握 FileUpload 组件的目录 Schema 定义、多态上传逻辑、宿主注册方式以及 Agent 后端 FileResolver 适配器的完整实现思路。1. 为什么 A2UI 需要原生文件上传问题背景随着 Agent 扩展到多模态任务Agent-to-UIA2UI协议必须在原生层面支持文档、图片等文件上传。在标准 A2UI 工作流中用户动作事件的中继会把客户端数据直接嵌入 JSON action 载荷如果直接复用这条中继通道来传输文件而不加约束原始二进制数据会被直接嵌入载荷。由于这些载荷经常被追加进 Agent 的对话历史对于大文件而言这种做法会导致上下文拥塞Context congestion将大型二进制文件直接编码进 LLM Prompt 或 A2UI JSON-RPC 载荷会显著增加延迟、Token 消耗和内存占用。多端面Multi-surface要求用户与 A2UI Agent 之间的会话状态必须能在桌面端、Web、移动端和可穿戴设备之间平滑迁移且不能丢失文件附件。这一要求直接否决了仅存本地的文件存储方案。框架互操作性无论是 Agent 开发 SDK如 LangChain、LlamaIndex、ADK还是基础模型 API如 Claude、Gemini都应当开箱即用地解析文件引用而无需编写自定义中间件去解码私有协议。2. 行业先例MCP、ChatGPT、Claude 的抽象指针模式在设计方案之前先看业界成熟平台如何处理文件上传抽象指针模式Abstract pointer pattern商业平台将上传管线与消息管线分离。用户上传文件时文件直接进入云端对象存储聊天状态里只记录一个抽象标识符例如file-12345或一个预签名 URL。即时JIT解析后端编排层在模型推理的那一刻才把这些标识符解析为原始二进制或抽取出的文本让传输层始终保持轻量。Model Context ProtocolMCPMCP 通过向服务器传递标准 URI 和文件路径来带外out of band处理文件引用从而避免载荷膨胀。这三条先例共同指向一个结论上传管线与消息管线必须解耦。3. 统一混合架构单一组件、双策略模型与其在 Web 组件里内置复杂的 HTTP 上传器或强制宿主开发者使用某一种网络协议A2UI 采用由环境感知的 Web 组件编排的双策略模型来处理文件上传。3.1 核心设计原则单一组件双策略Agent 开发者只需声明一个FileUpload组件无需关心用户当前是在 Web 浏览器、原生 iOS App 还是企业沙箱中。组件内部自行决定文件如何传输策略适用场景机制宿主委托模式IoC 主策略生产环境 Web、企业应用、原生移动 App宿主应用在目录注册时以编程方式把上传回调传入组件构造函数或工厂。组件拦截文件把二进制对象在内存中直接交给宿主回调宿主通过自己的安全通道完成上传并返回一个抽象引用 ID如fileId: host-file-ref-789。内联上传模式回退策略快速原型、轻量 Web 场景若未配置宿主回调组件退化为把小文件编码为内联 data URIfileId: data:image/png;base64,...或带内会话附件fileId: inline://...。无需预置云存储桶即可开箱即用。预签名 URL未来扩展预留由客户端主导、基于预签名 URL 的直接 HTTP 上传被保留为未来扩展让核心组件不背负网络传输复杂度。无论走哪条路径最终发给 Agent 后端的载荷都统一为一个引用字符串fileId从而保持 WebSocket 与 JSON-RPC 层的干净整洁。整个决策流程如下3.2 指针解析隐式与显式两种模型文件上传完成、产生抽象引用 IDfileId之后Agent 必须在模型推理期间把这个指针解析为物理字节或抽取出的文本。依据 Agent 与宿主之间的信任边界与耦合程度架构支持两种解析模型隐式解析共享 Schema当 Agent 与宿主处于同一组织边界内或共享一套约定的 URI Schema如gdrive://id、s3://bucket/key、enterprise-vault://id时解析是隐式的。宿主把抽象 URI 字符串放进 action 载荷Agent 的后端适配器通过识别 URI 前缀使用自己预配置的后端凭据或 SDK 带外拉取物理文件。显式解析非共享 Schema当 Agent 与宿主跨组织边界协作、且没有预共享存储 Schema如第三方 Agent 或多厂商生态时解析是显式的。Agent 无法假定如何解释私有指针或如何对私有存储桶鉴权因此宿主必须提供显式的解析指令方式有三种发送一个临时的、带鉴权的 HTTPS 下载 URL如https://host.domain.com/api/files/download/token在事件载荷中传递自描述self-describing的解析器元数据暴露一个标准化的资源读取工具类似 Model Context Protocol 的做法由 Agent 调用以获取文件内容。4. 职责分工谁负责什么功能 / 层责任方描述与机制UX 与 UI 状态管理A2UI 核心库在 Web 组件内部原生实现拖放区域、文件队列、进度指示器、暂停/恢复控件和错误状态。宿主委托传输IoC宿主开发者主策略在目录注册组件时以编程方式传入上传回调onUploadFile将上传路由到原生 OS 守护进程或内部 VPC 端点。内联上传传输A2UI 核心库回退在没有宿主回调时自动把小文件编码为内联 base64 data URI或作为带内会话附件传递实现开箱即用的快速原型。存储基础设施与安全宿主开发者预置云存储桶S3 或 GCS、配置 CORS 与 CSP 头、强制恶意软件扫描、为孤儿文件设置生命周期规则。指针解析与推理Agent 后端开发者将fileId载荷即时解析为原始字节——内部 Agent 用隐式 Schema 约定如gdrive://、s3://外部 Agent 用显式解析机制如临时 HTTPS URL 或资源工具。5. 技术实现从 Schema 到解析器FileUpload被打包为可选的扩展模块a2ui/plugin-fileupload。通过移除内部 HTTP 分片与网络引擎组件保持轻量专注于 UI 状态与策略委托。5.1 A组件目录 Schema 定义Agent 的接口要把FileUpload集成进 A2UI 生态首先要在目录 Schema 中定义它。这份契约规定了 Agent 可配置的属性和将收到的事件。注意预签名 URL 相关属性被有意排除在 Schema 之外留给未来扩展。import {ComponentDefinition} from a2ui/core; export const FileUploadDefinition: ComponentDefinition { type: FileUpload, // 1. Properties: Configured by the agent during rendering properties: { accept: { type: string, optional: true, description: Allowed MIME types (e.g., image/jpeg, application/pdf), }, maxSize: { type: number, optional: true, description: Maximum file size in bytes, }, multiple: {type: boolean, optional: true, default: false}, label: { type: string, optional: true, default: Drag and drop files or click to upload, }, }, // 2. Events: Dispatched from the component back to the agent via JSON-RPC events: { upload_complete: { description: Fired once a file (or batch of files) is successfully uploaded., payloadSchema: { surfaceId: { type: string, description: The ID of the surface where the file upload occurred., }, files: { type: array, description: Array of resolved abstract file pointers, inline data URIs, or in-band session pointers., items: { type: object, properties: { fileId: {type: string}, metadata: { type: object, properties: { fileName: {type: string}, fileSize: {type: number}, mimeType: {type: string}, }, }, }, }, }, }, }, }, };仓库中的真实宿主示例把这份契约落到了 zod Schema 上。在 catalog.ts 中FileUploadSchema使用DynamicStringSchema、DynamicNumberSchema与z.boolean()描述label、accept、maxSize、multiple四个属性并通过DEMO_CATALOG将FileUpload组件注册进以https://a2ui.org/samples/community/agent/adk/file_upload_summarizer/catalogs/0.9/file_upload_catalog.json为 ID 的目录中。这说明Schema 只暴露 UI 属性不暴露任何上传函数——这正是零提示与 Schema 污染设计目标的直接体现。5.2 B多态组件逻辑Web 组件负责拖放区域、进度条等视觉状态同时检查构造函数配置决定执行宿主委托还是内联 data URI 编码export interface FileUploadConfig { /** Primary strategy: Host-delegated upload callback for production web, mobile, or enterprise VPCs */ onUploadFile?: (file: File, onProgress: (percent: number) void) Promisestring; /** Callback fired when a file is removed from the UI queue */ onRemoveFile?: (pointerUri: string) void; /** Maximum file size allowed for fallback inline data URI encoding (in bytes, default 500KB) */ maxInlineSize?: number; } export class FileUploadComponent extends HTMLElement { private config: FileUploadConfig; constructor(config: FileUploadConfig {}) { super(); this.config { maxInlineSize: 500_000, // 500KB default limit for inline fallback ...config, }; } async handleFileSelect(file: File) { this.updateUIState(uploading, 0); try { let fileId: string; // Strategy 1: Host-delegated IoC via programmatic constructor configuration if (this.config.onUploadFile) { fileId await this.config.onUploadFile(file, percent this.updateProgress(percent)); } // Strategy 2: Fallback inline upload for small files else if (file.size (this.config.maxInlineSize || 500_000)) { fileId await this.encodeAsDataUri(file); } else { throw new Error( File size (${file.size} bytes) exceeds inline upload limit. A host onUploadFile callback must be configured for large file uploads., ); } // Final dispatch to agent contains only the resolved fileId pointer this.dispatchUserAction(upload_complete, {fileId}); this.updateUIState(success); } catch (err) { this.updateUIState(error, (err as Error).message); } } private updateUIState(state: string, detail?: number | string) { // DOM rendering logic for progress bars and badges } private updateProgress(percent: number) { this.updateUIState(uploading, percent); } private encodeAsDataUri(file: File): Promisestring { return new Promise((resolve, reject) { const reader new FileReader(); reader.onload () resolve(reader.result as string); reader.onerror () reject(new Error(Failed to encode file as data URI)); reader.readAsDataURL(file); }); } }Angular 宿主中的真实实现 file-upload.ts 完整复刻了这套逻辑FILE_UPLOAD_CONFIG通过 Angular 的InjectionToken注入配置DEFAULT_MAX_INLINE_SIZE为500_000字节INLINE_URI_PREFIX为inline://并通过isIocModegetter 暴露当前是否处于 IoC 模式。在回退路径中内联文件还会用crypto.randomUUID()生成一个inline://uuid指针把 data URI 本体单独存放在inlineData字段里确保 JSON-RPC 事件载荷中只有轻量的指针。该组件在 file-upload.spec.ts 中有完整的单元测试覆盖验证了默认属性值、maxSize超限报错、内联模式成功分发、multiplefalse时强制单选、移除文件、拖放处理以及 IoC 回调路径等关键行为。5.3 C批处理与错误处理如果multiple为 true 且用户拖入多个文件组件会并发处理它们。为避免 LLM 竞态条件组件把成功上传的文件批量合并进单个upload_complete事件如果个别文件失败成功文件立即分发失败文件留在 UI 队列中并提供重试选项。export class FileUploadComponent extends HTMLElement { // ... (methods from above) ... async handleMultipleFiles(files: File[]) { const successfulFiles: Array{fileId: string; metadata: FileMetadata} []; await Promise.all( files.map(async file { this.updateUIStateForFile(file.name, uploading, 0); try { const fileId await this.executeUploadStrategy(file); const metadata this.getFileMetadata(file); successfulFiles.push({fileId, metadata}); this.updateUIStateForFile(file.name, success); } catch (err) { this.updateUIStateForFile(file.name, error_retry, (err as Error).message); } }), ); if (successfulFiles.length 0) { this.dispatchUserAction(upload_complete, {files: successfulFiles}); } } }实际 Angular 实现还在此基础上增加了数据模型清理上传开始前会通过rendererService.surfaceGroup.getSurface(this.surfaceId())获取 A2UI Surface并调用surface.dataModel.set(/uploaded_files, undefined)清掉上一次遗留的文件指针避免陈旧引用污染会话。5.4 D宿主应用组件注册编程式配置要让组件在生产或企业环境运行宿主开发者必须在把FileUpload注册进目录时以编程方式传入上传回调而快速原型场景下不传回调注册即可自动启用内联上传。import {Catalog} from a2ui/web_core/v0_9; import {createFileUploadComponent} from a2ui/plugin-fileupload; const catalog new Catalog(); // 1. Production host (enterprise web, desktop, or native mobile IoC) // The host passes a direct upload callback when registering the component: catalog.addComponent( FileUpload, createFileUploadComponent({ onUploadFile: async (file: File, onProgress: (percent: number) void): Promisestring { // Bridges directly to host network layer or native OS daemons const response await hostUploadService.upload(file, onProgress); return response.fileId; // Returns abstract pointer (e.g., s3://bucket/key) }, }), ); // 2. Rapid prototyping or lightweight demo host (inline upload fallback) // Registering without onUploadFile uses inline data URIs for files up to 1MB: catalog.addComponent( FileUpload, createFileUploadComponent({ maxInlineSize: 1_000_000, }), );在仓库示例中宿主的 IoC 回调由 mock-drive-upload-service.ts 提供它把文件以FormDataPOST 到/api/upload端点并把响应中的 Drive 文件 ID 包装成mockdrive://id形式的抽象指针返回同时通过 telemetry-logger-service.ts 记录 IoC 上传事件用于验证 JSON-RPC 载荷中 0 字节的 Base64 内联膨胀。示例还提供了 file-upload-settings-service.ts 用enableIoc、enableMultiFile两个信号在 UI 上实时切换两种策略与多文件模式方便直观对比。5.5 EAgent 后端解析FileResolver 适配器后端 SDK 依赖存储适配器模式FileResolver。在模型推理期间解析器检查传入的fileId并应用隐式或显式解析策略隐式解析共享 Schema对共享已知 Schema如s3://或gdrive://的内部 Agent适配器匹配 URI 前缀用后端服务凭据带外下载对象。显式解析非共享 Schema对外部 Agent 或非共享 Schema适配器对临时 HTTPS 下载 URL 执行标准 HTTP GET或调用环境提供的资源读取工具。import base64 import boto3 import httpx from a2ui_sdk.server import A2UIOrchestrator, SessionData from adk.core import Agent, LLMProvider s3_client boto3.client(s3, region_nameus-east-1) # 1. Define resolution adapter (handling inline URIs, implicit schemas, and explicit HTTPS URLs) async def resolve_file_adapter(file_id: str, session: SessionData) - bytes: # Strategy 1: Inline ADK session history resolving if file_id.startswith(inline://): if not session: raise ValueError(fCannot resolve {file_id}: No session provided.) for event in getattr(session, events, []): if hasattr(event, message) and event.message and getattr(event.message, parts, None): for part in event.message.parts: if getattr(part, inline_data, None): part_meta getattr(part, part_metadata, None) or {} part_file_id part_meta.get(fileId) if isinstance(part_meta, dict) else getattr(part_meta, fileId, None) if part_file_id file_id: return part.inline_data.data raise ValueError(fInline data pointer {file_id} not found in session history.) # Strategy 2: Inline data URI fallback if file_id.startswith(data:): header, base64_data file_id.split(,, 1) return base64.b64decode(base64_data) # Strategy 3: Implicit resolution via shared schema (e.g., s3:// or gdrive://) if file_id.startswith(s3://): bucket, key file_id.replace(s3://, ).split(/, 1) response s3_client.get_object(Bucketbucket, Keykey) return response[Body].read() # Strategy 4: Explicit resolution via unshared schema (e.g., ephemeral HTTPS download URL) if file_id.startswith(https://): async with httpx.AsyncClient() as client: response await client.get(file_id, follow_redirectsTrue) response.raise_for_status() return response.content raise ValueError(fUnsupported file pointer schema: {file_id}) # 2. Initialize the A2UI Backend SDK with the resolution adapter orchestrator A2UIOrchestrator(file_resolverresolve_file_adapter) agent Agent(llm_providerLLMProvider.GEMINI) # 3. Just-in-time (JIT) resolution during model inference orchestrator.on_user_action(upload_complete) async def handle_upload_complete(payload: dict, session: SessionData): file_id payload.get(fileId) # 4. The SDK executes the registered adapter to retrieve physical bytes file_bytes await orchestrator.resolve_file(file_id, session) # 5. Pass bytes directly into the multimodal context window response await agent.generate_content( promptPlease analyze this uploaded document., attachments[{ data: file_bytes, mime_type: application/pdf }] ) return response仓库中配套的 ADK Agent 示例把上述适配器模式落地为真实的FileResolver。在 file_resolution.py 中注册了自定义mockdrive://Scheme 的处理器_mock_drive_handler它会向{base_url}/api/mock-drive/v3/files/{id}?altmedia发起带follow_redirects的 HTTP GET返回原始字节配置了ALLOWED_MIME_TYPEStext/*、application/pdf、application/json、application/javascript、application/xml、image/*作为解析层安全白名单通过resolver.as_tool_decorator(arg_namefiles, inject_namegenai_parts, ...)把解析能力以装饰器形式注入工具自动下载并把文件内容直接注入工具调用安全校验失败时返回FileResolverSecurityError处理结果。整个链路验证了设计文档的核心承诺WebSocket 上传输的只有fileId指针二进制字节在推理时刻带外解析。6. 端到端演示仓库中的可运行示例设计文档在仓库中有完整的端到端落地演示由 Angular 宿主与 ADK Agent 两部分组成Angular 宿主应用samples/community/client/angular/projects/file_upload自包含的 FileUpload Web 组件实现拖放上传与视觉进度状态IoC 上传回调把文件上传到后端 Mock Drive 服务并返回mockdrive://...抽象指针右侧固定的 Protocol Inspector 抽屉记录 IoC 上传事件、展示原始 JSON-RPC 消息验证 0 字节 Base64 内联膨胀以及 Agent 解析轨迹。ADK 摘要 Agentsamples/community/agent/adk/file_upload_summarizer内置 Mock Drive v3 REST APIPOST /api/mock-drive/v3/files与GET /api/mock-drive/v3/files/{id}用于在本地无 Google Cloud OAuth 凭据的情况下测试上传与指针解析通过带外 FileResolver 按mockdrive://指针 ID 下载文件并使用多模态模型生成上传文档的摘要。按以下步骤即可本地运行整个链路启动 Agent端口 10008cd samples/community/agent/adk/file_upload_summarizer export GEMINI_API_KEYyour-api-key # 或配置 Vertex AI uv run . --port 10008构建共享依赖并启动 Angular 宿主yarn workspace a2ui/web_core build yarn workspace angular-a2ui ng serve file_upload --port 4200浏览器打开http://localhost:4200/切换 IoC / 内联模式上传文件观察 Protocol Inspector 中载荷始终只有轻量指针。7. 安全与运营收益双策略架构带来的关键收益核心组件复杂度下降由于把预签名 URL 支持推迟到未来扩展FileUploadWeb 组件无需内置 HTTP 客户端、分片上传逻辑或 CORS 排障。声明式纯粹性与优雅降级Agent 开发者只写一个通用的FileUpload标签系统自动适配宿主环境能力不向 Agent 泄漏实现逻辑。原生移动端韧性通过宿主委托iOS 和 Android 实现可绕过 WebView 线程挂起限制把大文件上传交给原生后台守护进程。跨信任边界的灵活指针解析同时支持隐式解析共享 URI Schema内部 Agent 零开销部署与显式解析临时 HTTPS URL 或资源工具第三方 Agent 不暴露内部云存储拓扑。零 Prompt 与 Schema 污染上传回调以编程方式配置在组件构造函数上不会出现在目录 JSON Schema 中消除了 LLM 幻觉式调用任意组件上传函数的风险。即时原型能力开发者做演示时可立即使用内联上传无需预置云存储桶或后端上传端点。编排器数据主权统一使用抽象标识符编排器可实施多 Agent 隔离类似A2uiSubagentMap的工具可以从子 Agent 载荷中剥离无主文件指针防止跨 Agent 数据泄漏。零信任文件校验accept和maxSize等 Schema 属性只是客户端 UI 便利。由于物理文件传输绕过了 JSON-RPC 层宿主存储桶策略和后端FileResolver适配器必须在字节进入 LLM 上下文窗口之前强制执行文件大小限制、校验 MIME 类型并执行恶意软件扫描。8. 分批上线策略与战术建议为推进开发、部署到生产环境并及时与安全评审团队对接设计文档建议采用以下分阶段策略立即提交双架构IoC 内联上传通过宿主委托覆盖生产企业 Web 与原生移动环境通过内联 data URI 支持快速原型。保留预签名 URL 传输uploadUrl作为未来插件扩展让初始代码库保持精简聚焦核心 UI 状态管理与宿主集成。这一路线图与仓库现状一致当前版本的FileUpload组件只含两种策略预签名 URL 属性在 catalog.ts 的 Schema 中完全缺席正是为未来扩展预留空间的刻意设计。团队可在此基础上按图索骥将文件上传能力以最小侵入方式引入任何 A2UI 宿主。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表