尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Genkit × Vercel AI SDK 集成指南:用 GenkitChatTransport 为 Agent 打造流式聊天界面

Genkit × Vercel AI SDK 集成指南:用 GenkitChatTransport 为 Agent 打造流式聊天界面 Genkit × Vercel AI SDK 集成指南用 GenkitChatTransport 为 Agent 打造流式聊天界面【免费下载链接】genkitOpen-source framework for building agentic apps in JavaScript, Go, Dart, and Python, built and used in production by Google项目地址: https://gitcode.com/GitHub_Trending/ge/genkit导读genkit-ai/vercel-ai是 Genkit 官方提供的桥接插件它为 Vercel AI SDK 实现了一个标准的ChatTransportGenkitChatTransport让你可以直接用 AI SDK 的useChathook 对接 Genkit Agent在 ReactNext.js、Vite 等应用中零成本获得文本流式输出、多轮会话、工具调用与基于中断interrupt的人工介入human-in-the-loop能力。读完本文你将掌握该插件的安装配置、服务端与客户端接线方式、UUID 会话机制、会话恢复与再生成逻辑以及中断响应/重启两种 HITL 模式的完整实现。本文以 js/plugins/vercel-ai/README.md 为核心结合 客户端传输实现、消息映射工具 及 端到端测试 展开说明。插件定位与核心能力Genkit 是 Google 开源的 Agent 应用框架支持 JavaScript、Go、Dart、Python而 Vercel AI SDK 是前端 AI 应用的事实标准生态之一。两者桥接的关键在于AI SDK v6 的useChat支持通过自定义ChatTransport定义消息的发送与流式读取协议GenkitChatTransport正是这样一个实现——它把 Genkit Agent 原生的事件流转换为 AI SDK 的UIMessageChunk事件流从而让useChat直接驱动一个 Genkit Agent。该插件开箱即用地支持以下能力Features 完整清单见文末文本流式输出逐 token 将文本增量推送到 UI多轮会话以useChat的id作为 GenkitsessionId由服务端自动维护会话连续性工具调用实时把工具输入与输出以tool-input-available/tool-output-available等事件呈现给 UI中断human-in-the-loop对defineInterrupt工具提供一等支持可暂停并携带用户输入恢复自定义请求头支持静态对象或动态函数两种鉴权方式浏览器安全/client入口不依赖任何 Node.js 运行时 API零配置客户端无需维护ChatSessionStore。安装与依赖npm install genkit-ai/vercel-ai版本依赖peer dependencies包名是否必需说明genkit✅1.0.0本插件依赖的 Agent APIdefineAgent、streamFlow位于genkit/beta子路径ai✅^6.0.0提供ChatTransport、UIMessage等核心类型ai-sdk/react仅客户端^3.0.0仅在浏览器端调用useChathook 时需要注意ai-sdk/react并未声明为插件自身的必需 peer dependency因为插件服务端入口不需要它。任何在项目里使用useChat的应用需要自行安装。从 package.json 可以看到实际声明的 peer 依赖与 engines 约束ai: ^6.0.0、ai-sdk/react: ^3.0.0标记为optional: true、genkit: workspace:^Node.js 要求20服务端。快速上手两步完成 Agent ↔ Chat UI 接线第一步在服务端定义一个 Genkit AgentNext.js App Router 示例app/api/chat/weather/route.tsimport { appRoute } from genkit-ai/next; import { genkit } from genkit/beta; import { googleAI } from genkit-ai/google-genai; const ai genkit({ plugins: [googleAI()] }); const weatherAgent ai.defineAgent({ name: weatherAgent, model: googleai/gemini-flash-latest, tools: [/* your tools here */], }); export const POST appRoute(weatherAgent);或者使用 Expressimport { expressHandler } from genkit-ai/express; import express from express; const app express(); app.post(/api/chat/weather, expressHandler(weatherAgent)); app.listen(3000);第二步在客户端用useChat连接use client; import { useChat } from ai-sdk/react; import { GenkitChatTransport } from genkit-ai/vercel-ai/client; import { useMemo } from react; export default function Chat() { const transport useMemo( () new GenkitChatTransport({ url: /api/chat/weather }), [] ); // useChat 的 id 会被当作 Agent 的 sessionId 发送它必须是 UUID。 const chatId useMemo(() crypto.randomUUID(), []); const { messages, input, handleInputChange, handleSubmit, status } useChat({ id: chatId, transport }); return ( div {messages.map((m) ( div key{m.id} strong{m.role}:/strong{ } {m.parts .filter((p) p.type text) .map((p) p.text) .join()} /div ))} form onSubmit{handleSubmit} input value{input} onChange{handleInputChange} / button typesubmit disabled{status ! ready} Send /button /form /div ); }仅此而已——文本流式输出、多轮历史、工具调用和中断恢复都开箱即用。深入源码GenkitChatTransport配置项GenkitChatTransport在 src/client.ts 中实现实现接口为 AI SDK 的ChatTransportUIMessage。其构造函数接收一个配置对象import { GenkitChatTransport } from genkit-ai/vercel-ai/client; const transport new GenkitChatTransport({ url: /api/chat/weather, headers: { Authorization: Bearer token }, // 静态请求头 // headers: () ({ Authorization: Bearer ${getToken()} }), // 或动态获取 });配置参数选项类型说明urlstring必填。Genkit Agent 端点的 URL。headersRecordstring, string \| () Recordstring, string可选。附加到每个请求上的 HTTP 请求头支持静态对象或返回动态值的函数例如鉴权 token。源码中resolveHeadersclient.ts会合并构造函数配置的基础请求头与每次sendMessages调用携带的额外请求头后者覆盖前者并兼容把标准Headers对象归一化为普通记录。端到端测试中专门有静态与动态两种请求头的用例见 client_test.ts 中 should support static and dynamic headers会断言服务端实际收到X-Custom/X-Dynamic请求头。关键约束useChat的id必须是裸 UUIDuseChat的id必须是裸 UUID。传输层会把聊天的id作为 Genkit 的sessionId发送给 Agent而 Agent 服务端要求会话 id 必须是裸 UUID。请用crypto.randomUUID()生成并传给useChat({ id })。如果省略idAI SDK 会生成一个非 UUID 的 id传输层将返回错误。import { useMemo } from react; const chatId useMemo(() crypto.randomUUID(), []); const { messages, sendMessage } useChat({ id: chatId, transport });这条约束不是约定俗成而是由源码强制校验的sendMessages一进来就用正则UUID_PATTERNclient.ts校验chatId不匹配时不会发起任何网络请求而是立即返回一个携带错误信息的errorStreamstart → error → finish标准封套。测试 should return an error chunk when chatId is not a UUID 验证了错误文本会包含 UUID 提示。服务端会话管理状态全部由服务端持有会话状态完全由服务端管理。传输层把聊天的id作为sessionId传给 AgentAgent 在自己配置的SessionStore中持久化按会话维度的状态。每一轮自动恢复该会话最新的快照——客户端不需要做任何快照记账或持久化即使页面刷新也不会丢失任何状态状态在服务端。要继续已有会话只需复用同一个 UUIDid。在 client.ts 中可以看到每次请求都会携带init: { sessionId: chatId }发送给 Agent。测试 should maintain conversation across turns via sessionId 验证了第二轮请求发送给模型的请求中会包含首轮的用户消息与模型回复turn2UserMsgs.length 2证明历史由服务端会话累计测试 should maintain independent sessions per chatId 则验证了不同chatId之间的会话相互隔离A 会话第二轮看到的历史来自 A 的第一轮而非 B。示例应用 js/testapps/vercel-ai-elements/lib/agents.ts 中使用new InMemorySessionStore()作为会话存储即开即用生产环境可替换为持久化实现。刷新后恢复会话messagesFromSnapshot因为状态在服务端恢复下一轮只需要复用同一个 UUIDid。客户端唯一需要重建的是渲染用的消息列表这些归useChat所有传输层不负责持久化。使用messagesFromSnapshot可以从 Genkit 的SessionSnapshot通过/stateflow 按同一sessionId加载重建可见消息import { useChat } from ai-sdk/react; import { GenkitChatTransport } from genkit-ai/vercel-ai/client; import { messagesFromSnapshot } from genkit-ai/vercel-ai; import { runFlow } from genkit/beta/client; import { useEffect, useMemo, useState } from react; export default function RestoredChat({ sessionId }: { sessionId: string }) { const transport useMemo(() new GenkitChatTransport({ url: /api/chat/weather }), []); const [initialMessages, setInitialMessages] useState([]); useEffect(() { (async () { // 加载该会话的最新快照返回 SessionSnapshot。 const snapshot await runFlow({ url: /api/chat/weather/state, input: sessionId }); // 从快照重建可见消息列表。 setInitialMessages(messagesFromSnapshot(snapshot.state.messages)); })(); }, [sessionId]); // 复用同一个 UUID id下一轮会恢复服务端会话。 const { messages, sendMessage } useChat({ id: sessionId, transport, messages: initialMessages }); // ...渲染消息、发送新回合——连续性得到保持。 }messagesFromSnapshot的实现细节见 mapping.ts值得说明Genkit 把工具的请求与响应存放在不同的消息里toolRequest在 model 消息、toolResponse在 tool 角色消息该函数先遍历全部消息按ref建立responsesByRef映射再把成对的请求/响应合并成单个AI SDK tool part使每个工具调用在 UI 上渲染为一个元素同时跳过独立的 tool 角色消息将reasoning与media类型的 part 分别映射为 AI SDK 的 reasoning 与 file part保留 part 的metadata双向保留详见映射工具一节尚未解决的工具请求例如待处理的中断以input-available状态输出UI 仍然可以继续解决它。提示加载快照所用的sessionId必须与传给useChat的id是同一个 UUID这样渲染出的消息与服务端会话才能保持同步。响应再生成Regenerate当 UI 触发再生成时AI SDK 发送trigger: regenerate-message传输层会把最后一条用户消息作为全新的一轮基于当前服务端会话状态重新执行为同一问题生成新答案。因为状态由sessionId在服务端管理不存在客户端快照指针可供回退——再生成的一轮就像普通轮次一样追加到会话中。从源码看client.ts再生成时resolvedTools会被强制置空isRegenerate ? [] : currentTurnResolvedTools(messages)即再生成绝不会被误判为中断恢复。测试 should regenerate the last turn as a fresh run 验证再生成会重新发出最后一条用户消息模型请求历史里最后一条用户消息仍是second测试 should regenerate the first turn from scratch 验证当会话尚无任何状态时再生成也能优雅地退化为一次全新运行。工作方式从streamFlow到UIMessageChunk的事件流传输层的核心执行路径client.ts可以概括为四步发送消息通过 Genkit 的streamFlow客户端——一个浏览器安全的 HTTP 客户端向 Agent 端点 POST 请求并读取 NDJSON 流式响应转换事件把 Genkit 的AgentStreamChunk事件转换为 Vercel AI SDK 的UIMessageChunk事件text-delta、tool-input-available、tool-output-available等携带会话 id通过init: { sessionId: chatId }传给 Agent服务端持久化按会话状态并在每轮恢复最新快照客户端无需任何快照追踪处理中断Agent 通过defineInterrupt暂停等待人工输入时传输层把挂起的工具调用呈现给 UI下一次sendMessages时从消息历史中直接检测已解决的工具结果自动发送resume载荷而不是新消息。流式转换的细节源码视角sendMessages返回一个ReadableStreamUIMessageChunk其start回调里是一个完整的流式状态机开始封套先发start含随机messageId结束前必定发finish测试断言types[0] start且types.at(-1) finishstep 管理start-step/finish-step围住一次模型输出过程文本块text-start携带随机块 id→ 若干text-delta→text-end同一个文本块的 id 保持一致测试对块 id 的一致性有专门断言推理块Genkit 的 reasoning part 映射为reasoning-start/reasoning-delta/reasoning-end且推理块会在可见文本开始前完整关闭reasoning-end必须先于text-start工具输入/输出tool-input-start→tool-input-available携带toolName、toolCallId、input工具执行完成后发tool-output-available携带outputAgent 自定义状态会话自定义状态每次变更时运行时自动下发 RFC 6902 JSON Patchchunk.customPatch传输层用applyPatch维护本地副本并把补丁后的完整状态以 transient 的data-customchunk 呈现React 应用可以据此渲染实时进度无 ref 的工具调用兜底当模型下发的工具调用缺少ref时传输层会按工具名维护 FIFO 队列分配确定性toolCallId每次请求分配全新 id避免同一工具多次无 ref 调用互相串线保证请求与响应的toolCallId一致中止处理监听abortSignal被中止时优雅地以abortchunk 收尾而不是抛出错误测试 should handle abort signal gracefully 验证了中止后流仍以finish结束且不产生errorchunk错误处理streamFlow抛出的Error其 message 已内嵌 HTTP 状态与 Genkit 错误信息errorTextFromUnknown会原样保留这些上下文非 Error 的抛出值会优先 JSON 序列化避免信息丢失。流总是以start → error → finish的封套返回对应测试 should return error chunk when no user message is found 断言恰好 3 个 chunk。此外reconnectToStream()始终返回null——Genkit 传输层暂不支持流重连Genkit 服务端已有 durable streaming 能力重连支持可能在未来版本加入。中断与人工介入human-in-the-loopGenkit Agent 可以通过defineInterrupt或调用interrupt()的工具在运行中途暂停以向人类提问。此时传输层把挂起的工具调用以input-available状态的 tool part 呈现给useChat。解决它有两种方式Respond响应——直接提供工具的输出人类决策就是结果。这正是defineInterrupt工具所期望的。Restart重启——让 Agent重新执行该工具可选附带 metadata。适用于先interrupt()等待确认、恢复后再计算真实结果的工具此时它们的resumed参数会被设置。两种方式都由 AI SDK 原生的 HITL 原语驱动——addToolResult配合sendAutomaticallyWhen——无需手动setMessages/flushSyncimport { useChat } from ai-sdk/react; import { GenkitChatTransport, restartInterrupt, } from genkit-ai/vercel-ai/client; import { lastAssistantMessageIsCompleteWithToolCalls } from ai; const { messages, addToolResult } useChat({ transport: new GenkitChatTransport({ url: /api/chat/banking }), // 每个挂起的工具调用都有结果后自动恢复。 sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls, }); // 1. Respond提供工具输出resume.respond。 addToolResult({ tool: userApproval, toolCallId, output: { approved: true, feedback: Looks good }, }); // 2. Restart重新执行工具resume.restart。metadata 会成为服务端 // 工具的 resumed 参数。 addToolResult({ tool: getExchangeRate, toolCallId, output: restartInterrupt({ confirmedAt: Date.now() }), });注意Genkit 的中断映射到 AI SDK 的addToolResult流程不是工具审批流程needsApproval/addToolApprovalResponse。Genkit 中断不会自行在服务端执行——你传给addToolResult的值就是解决方案。若希望 Agent 重新执行工具而不是接受给定输出请使用restartInterrupt()。中断恢复的源码机制中断恢复完全从消息历史推导mapping.ts 的currentTurnResolvedTools扫描最近一条 assistant 消息中state output-available的 tool part且其后没有非空 user 消息isEmptyUserMessage用于过滤sendMessage({ text: })产生的幽灵空消息即判定为一次恢复。此作用域刻意限制在最近一轮避免把早前已完成轮次的工具调用当作 resume 载荷重放。当检测到恢复时client.ts传输层把解决方案拆成两类载荷respond用户直接提供输出组装为resume.respond条目toolResponse: { name, ref, output }同时记录到respondedInterruptIds之后流式阶段会抑制重新输出这些已经由客户端持有的结果避免 UI SDK 困惑restart通过restartInterrupt()的字符串标记__genkitVercelAiRestart__用 Symbol 无法跨 JSON 序列化故选择字符串识别组装为resume.restart条目携带原始工具输入服务端要求与中断时完全一致并把 metadata 作为工具的resumed值传递。重启的工具会在服务端重新执行产生全新输出因此不会被抑制客户端必须收到。一次恢复可以同时携带 respond 与 restart 两类条目。测试 should handle interrupt and resume flow 与 should restart an interrupted tool via restartInterrupt() 分别覆盖了两种路径后者还断言restartInterrupt({ confirmedBy: user })的 metadata 原样出现在重启后工具输出中的resumedWith字段。消息映射工具函数根入口genkit-ai/vercel-ai导出了 Vercel AI SDKUIMessage类型与 GenkitMessageData之间的映射函数以及传输层用到的辅助函数函数方向说明mapUIMessageToGenkit(msg)UI → Genkit把 VercelUIMessage转换为 GenkitMessageDatamapUIPartToGenkit(part)UI → Genkit把单个UIMessagePart转换为 GenkitPart[]messagesFromSnapshot(msgs)Genkit → UI把SessionSnapshot的state.messages转换为UIMessage[]以重新水合useChat成对工具请求/响应、保留 metadatamapGenkitMessageToUI(msg)Genkit → UI把单个 GenkitMessageData转换为UIMessageextractResolvedToolResults(msgs)—从消息数组中提取已解决的工具调用结果按toolCallId去重最近的胜出findLastUserMessage(msgs)—查找UIMessage[]中最后一条用户消息工具/part 的 metadata 在两个方向都会被保留mapUIPartToGenkit会把 UI part 的metadata带到 Genkit part 上messagesFromSnapshot会把 Genkit part 的metadata带回 UI part 上——这保证了经 UI → Genkit → UI 往返后 metadata 不丢失源码中的extractPartMetadata/withMetadata封装。映射细节见 mapping.ts角色映射AI SDK 的assistant↔ Genkit 的modeluser↔usersystem↔system未知角色兜底为user防止内容被静默丢弃mapUIPartToGenkit支持 v6 的两种工具 part 形态——ToolUIParttype: tool-name名称编码在 type 里与DynamicToolUIParttype: dynamic-tool带显式toolNamestate output-available的工具 part 会产生toolRequesttoolResponse两个 Genkit part与 Genkit 的会话模型一致filepart 映射为mediapart无 Genkit 对应物的 part 类型source-*、step-start、data-*被静默跳过messagesFromSnapshot输出的每个 UI 消息 id 形如restored-index且会丢弃没有可渲染 part 的消息。此外UIMessage类型也作为便捷从ai包重新导出src/index.ts。入口划分与浏览器安全导入路径环境内容genkit-ai/vercel-ai服务端 / 共享映射工具mapUIMessageToGenkit、mapUIPartToGenkit等与重新导出的UIMessage类型genkit-ai/vercel-ai/client浏览器 / 客户端GenkitChatTransport类/client入口是浏览器安全的没有任何 Node.js 依赖。这得益于巧妙的类型设计在 src/client.ts 中AgentOutput、AgentStreamChunk等 Genkit 类型全部以import type方式引入编译期擦除不产生任何运行时 Node 代码运行时仅依赖浏览器原生的fetch、ReadableStream、crypto等标准能力。package.json 的exports字段package.json同时声明了.与./client两个入口及其 CJS/ESM/types 变体。与 AI Elements 兼容该传输层与 AI ElementsAI 原生应用的组件库与自定义 shadcn/ui registry完全兼容。由于GenkitChatTransport实现了标准的ChatTransport接口AI Elements 的所有组件Conversation、Message、Tool、PromptInput等都可以直接配合 Genkit Agent 工作use client; import { useChat } from ai-sdk/react; import { GenkitChatTransport } from genkit-ai/vercel-ai/client; import { useMemo } from react; import { Conversation, ConversationContent, ConversationScrollButton, } from /components/ai-elements/conversation; import { Message, MessageContent, MessageResponse } from /components/ai-elements/message; import { PromptInput, PromptInputTextarea, PromptInputFooter, PromptInputSubmit, } from /components/ai-elements/prompt-input; import { Tool, ToolHeader, ToolContent, ToolInput, ToolOutput } from /components/ai-elements/tool; export default function Chat() { const transport useMemo( () new GenkitChatTransport({ url: /api/chat/weather }), [] ); // useChat 的 id 会被当作 Agent 的 sessionId 发送它必须是 UUID。 const chatId useMemo(() crypto.randomUUID(), []); const { messages, status, sendMessage } useChat({ id: chatId, transport }); return ( Conversation ConversationContent {messages.map((message) ( Message key{message.id} from{message.role} MessageContent {message.parts.map((part, i) { if (part.type text part.text) { return MessageResponse key{i}{part.text}/MessageResponse; } if (part.type.startsWith(tool-)) { const toolPart part as any; return ( Tool key{i} ToolHeader type{toolPart.type} state{toolPart.state} / ToolContent ToolInput input{toolPart.input} / ToolOutput output{toolPart.output} / /ToolContent /Tool ); } return null; })} /MessageContent /Message ))} /ConversationContent ConversationScrollButton / PromptInput onSubmit{(msg) sendMessage({ text: msg.text })} PromptInputTextarea placeholderMessage... / PromptInputFooter div / PromptInputSubmit status{status} / /PromptInputFooter /PromptInput /Conversation ); }完整的可运行示例位于 js/testapps/vercel-ai-elements它演示了多 Agent 选择、工具调用渲染以及基于中断的人工审批流——全部由 AI Elements 驱动。完整示例应用vercel-ai-elementsjs/testapps/vercel-ai-elements/README.md 是插件的端到端演示覆盖传输层支持的全部特性文本流式输出——逐 token 响应多轮会话——基于快照的自动会话连续性工具调用——Weather Agent 调用getWeather工具并内联渲染输入/输出中断human-in-the-loop——Banking Agent 用userApproval中断在执行transferMoney前暂停请求确认多 Agent 选择——在 Weather 与 Banking 两个 Agent 间切换。其架构如下Browser (useChat GenkitChatTransport) │ POST (Genkit streamFlow 协议, NDJSON) ▼ Next.js Route Handler (appRoute from genkit-ai/next) │ ▼ Genkit Agent (defineAgent, tools, interrupts, InMemorySessionStore) │ ▼ Gemini (genkit-ai/google-genai)关键文件分工路径说明lib/genkit.ts配置了 Google GenAI 插件的 Genkit 实例lib/agents.tsweatherAgent工具调用与bankingAgent中断app/api/chat/weather/route.ts通过appRoute暴露weatherAgentapp/api/chat/banking/route.ts通过appRoute暴露bankingAgentapp/page.tsx聊天 UIAgent 选择器、消息渲染、中断审批components/ai-elements/*AI Elements 组件来自 shadcn registry在 lib/agents.ts 中可以同时看到三种工具形态的完整定义普通工具getWeatherdefineToolAgent 自动执行并返回真实结果中断工具userApprovaldefineInterrupt暂停等待人类提供{ approved, feedback }输出即 respond 路径可重启工具getExchangeRatedefineToolinterrupt()首次调用时中断让用户确认收款方若用户在 UI 中选择Restart传输层携带resumed重新执行该工具此时工具返回真实汇率而不是再次中断——这正是 restart 路径的典型场景。运行示例前需要 Node.js20与 Gemini API Key然后执行cd js pnpm install pnpm build在 testapp 目录cp .env.local.example .env.local配置GEMINI_API_KEY后pnpm dev打开 http://localhost:3000 即可体验。特性清单文本流式输出——将逐 token 的文本增量流式传输到 UI多轮会话——以useChat的idsessionId为键的服务端自动会话连续性工具调用——实时呈现工具输入与输出中断human-in-the-loop——对defineInterrupt工具的一等支持支持带用户输入的暂停与恢复自定义请求头——静态或动态请求头用于鉴权浏览器安全——/client入口无 Node.js 依赖零配置——客户端无需ChatSessionStore。环境要求Genkit1.0.0——本传输层使用的 Agent APIdefineAgent、streamFlow通过主1.x包的genkit/beta子路径暴露Vercel AI SDK6.0.0为useChat提供ChatTransportai-sdk/react3.0.0客户端供useChathook 使用Node.js20服务端。常见注意事项id必须是裸 UUID省略id或用非 UUID 值会导致传输层直接返回start → error → finish错误流且不发出任何请求中断 ≠ 工具审批不要试图用needsApproval/addToolApprovalResponse处理 Genkit 中断应使用addToolResult重启工具的服务端校验resume.restart必须携带与中断时完全一致的原始工具输入重连reconnectToStream()始终返回nullGenkit 侧的重连支持尚在演进中metadata 双向保留若你的 UI 依赖 part metadata如中间件附加的信息插件在两个方向的映射中都会完整保留。许可Apache-2.0见 LICENSE。本文依据仓库 js/plugins/vercel-ai 目录下的 README 与源码、测试编写涉及的具体实现以当前仓库版本插件版本 0.5.0为准。【免费下载链接】genkitOpen-source framework for building agentic apps in JavaScript, Go, Dart, and Python, built and used in production by Google项目地址: https://gitcode.com/GitHub_Trending/ge/genkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表