戰(zhàn):在 Next.js 中構(gòu)建帶 Agent 的生成式 UI 與多通道交付)
CopilotKit × Mastra 集成實(shí)戰(zhàn)在 Next.js 中構(gòu)建帶 Agent 的生成式 UI 與多通道交付【免費(fèi)下載鏈接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol項(xiàng)目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit本指南圍繞倉庫中 examples/integrations/mastra 這一官方 Starter 模板展開系統(tǒng)講解如何將 Mastra Agent 接入 CopilotKit 運(yùn)行時在同一個 Next.js 應(yīng)用中實(shí)現(xiàn)生成式 UI、前端工具Frontend Tools、Human-in-the-Loop、Agent 共享狀態(tài)與持久化線程記憶并通過 Intelligence Channel 把同一個 Agent 掛載到 Slack、Teams 等會話平臺上。讀完本文你將掌握該模板的完整啟動流程、腳本體系、Intelligence 本地棧的搭建方法以及 Channel 宿主進(jìn)程的底層工作機(jī)制。模板概覽一次把 Agent、前端與消息通道串起來CopilotKit Mastra Starter 是一個面向?qū)崙?zhàn)的起步模板用 Mastra 定義 Agent含工具與記憶用 CopilotKit 提供前端運(yùn)行時Runtime、UI 組件與協(xié)議層再疊一層 Next.js 應(yīng)用作為界面載體。它通過ag-ui/mastra這個適配包把 Mastra 的 Agent 轉(zhuǎn)成 CopilotKit 運(yùn)行時能直接調(diào)度的通用 Agent 抽象并依賴 AG-UI 協(xié)議打通 Web 與各消息渠道。模板的核心代碼結(jié)構(gòu)如下src/agent.ts將本地 Mastra Agent 批量暴露為 CopilotKit 可調(diào)度的 Agent 對象src/mastraMastra 側(cè)的真實(shí)定義——index.ts創(chuàng)建Mastra實(shí)例agents/index.ts定義 weatherAgenttools/index.ts實(shí)現(xiàn)天氣查詢工具src/app/api/copilotkit/[[...slug]]/route.tsCopilotKit 運(yùn)行時掛在 Next.js Route Handler 上的 HTTP 入口Web 端的所有對話請求都走這里channel-host.mts 與 channels.mtsChannel 宿主進(jìn)程與通道聲明文件用于把同一個 Agent 交付到 Slack、Teams 等 Intelligence Channelsrc/app/page.tsx演示生成式 UI、前端工具、Human-in-the-Loop 與共享狀態(tài)的前端頁面。與許多HTTP 轉(zhuǎn)發(fā)式Starter 不同這里沒有獨(dú)立的 Agent 服務(wù)進(jìn)程Mastra Agent 直接運(yùn)行在 Next.js 進(jìn)程內(nèi)createLocalAgents的注釋明確寫到 Unlike the HTTP-backed starters there is no agent server here: the agents run in this process.即本地 Agent 全部在當(dāng)前進(jìn)程內(nèi)執(zhí)行。環(huán)境準(zhǔn)備Node 版本與包管理器模板對運(yùn)行環(huán)境的要求不高官方要求如下Node.js 18倉庫內(nèi)package.json使用 Next.js 16 與 React 19建議使用較新的 Node LTS 版本以下任一包管理器npm默認(rèn)、pnpm、yarnclassic、bun。核心依賴見 package.json包括包版本區(qū)間作用mastra/mastra/core1.1.0-alpha.3 / 1.41.0Mastra Agent 框架本體ag-ui/client/ag-ui/mastra0.0.58 / 1.1.2AG-UI 協(xié)議客戶端與 Mastra 適配層把 Mastra Agent 橋接進(jìn) CopilotKitcopilotkit/react-core/copilotkit/runtime1.70.0CopilotKit 前端核心與運(yùn)行時含 v2 入口copilotkit/channels0.9.0Channel 聲明與創(chuàng)建 APInext/react16.1.2 / ^19.2.1UI 框架mastra/libsql/mastra/memory1.1.0-alpha.2 / 1.0.1-alpha.1Mastra 存儲與記憶能力hono^4運(yùn)行時 HTTP 處理層由hono/vercel適配到 Route Handler快速啟動三步入軌1. 寫入 OpenAI API KeyMastra 的模型調(diào)用走ai-sdk/openai在.env中寫入密鑰即可你完全可以用 Mastra 支持的任何模型替換模板默認(rèn)使用gpt-4o# you can use whatever model Mastra supports echo OPENAI_API_KEYyour-key-here .env模型在 src/mastra/agents/index.ts 中通過model: openai(gpt-4o)指定按需更換模型名即可。2. 安裝依賴# Using npm (default) npm install # Using pnpm pnpm install # Using yarn yarn install # Using bun bun install3. 啟動開發(fā)服務(wù)器# Using npm (default) npm run dev # Using pnpm pnpm dev # Using yarn yarn dev # Using bun bun run devnpm run dev通過concurrently同時拉起 UI 與 Agent 兩個進(jìn)程package.json中--names ui,agent --kill-others所以日志會以雙進(jìn)程并行的形式輸出。注意這里所謂Agent 服務(wù)其實(shí)仍是同一個 Next.js 進(jìn)程內(nèi)的 Mastra Agentdev:agent執(zhí)行的是mastra dev用于提供 Mastra 的開發(fā)輔助能力。模板腳本體系詳解package.json 定義了以下腳本均可配合你偏好的包管理器使用dev— 同時以開發(fā)模式啟動 UI 與 Agent 服務(wù)器concurrently npm run dev:ui npm run dev:agentdev:ui— 僅啟動 Next.js UI 服務(wù)器next dev --turbopackdev:agent— 僅啟動 Mastra Agent 開發(fā)服務(wù)mastra devdev:debug— 以LOG_LEVELdebug啟動開發(fā)服務(wù)器方便排查運(yùn)行時的調(diào)試日志對應(yīng)src/mastra/index.ts中從process.env.LOG_LEVEL讀取日志級別、默認(rèn)info的實(shí)現(xiàn)build— 構(gòu)建生產(chǎn)版本next buildstart— 啟動生產(chǎn)服務(wù)器next startchannel— 保持一個 Intelligence Channel 在線tsx --tsconfig tsconfig.channel.json channel-host.mts。其中channel腳本使用獨(dú)立的 tsconfig.channel.json 做模塊解析因?yàn)閏hannel-host.mts會經(jīng)src/agent.ts傳遞引入src/mastra/**而該子樹帶有一個與 Channel 宿主無關(guān)的既有類型錯誤見下文已知限制。tsconfig.channel.json額外配置了/*路徑別名baseUrl: .paths: { /*: [./src/*] }否則tsx會以Cannot find module /mastra/tools崩潰。Agent 定義Mastra 側(cè)的完整實(shí)現(xiàn)Mastra 實(shí)例與天氣 Agentsrc/mastra/index.ts 創(chuàng)建 Mastra 實(shí)例注冊defaultAgent并使用內(nèi)存版 LibSQLStore 作為存儲、ConsoleLogger輸出日志const LOG_LEVEL (process.env.LOG_LEVEL as LogLevel) || info; export const mastra new Mastra({ agents: { default: weatherAgent }, storage: new LibSQLStore({ id: mastra-storage, url: :memory: }), logger: new ConsoleLogger({ level: LOG_LEVEL }), });天氣 Agent 定義在 src/mastra/agents/index.ts它注冊了weatherTool、使用gpt-4o模型并掛載了 Mastra Memory。值得關(guān)注的是記憶作用域的選擇memory: new Memory({ storage: new LibSQLStore({ id: weather-agent-memory, url: file::memory: }), options: { workingMemory: { enabled: true, schema: AgentState, scope: resource, }, }, }),代碼注釋解釋了為什么必須用scope: resource資源作用域而不是thread線程作用域CopilotKit 橋接層會在每次對話回合開始前把 UI 的共享狀態(tài)寫進(jìn) working memory而該寫入只在資源存儲中做 upsert線程作用域的 working memory 存放在線程元數(shù)據(jù)里要求線程行必須已存在——但會話首回合線程還不存在會導(dǎo)致Thread id not found而讓聊天永遠(yuǎn)無法應(yīng)答。這里的 resource id 由線程 id 推導(dǎo)而來因此狀態(tài)按會話隔離。這是一個非常典型的橋接集成才暴露的坑遷移到其他框架時同樣適用。Agent 的共享狀態(tài)由AgentStatezod 對象聲明{ proverbs: z.array(z.string()).default([]) }對應(yīng)前端 ProverbsCard 展示的格言列表。天氣工具兩段式 API 調(diào)用src/mastra/tools/index.ts 實(shí)現(xiàn)了weatherTool采用 Open-Meteo 的免費(fèi) API地理編碼先調(diào)用geocoding-api.open-meteo.com/v1/search把城市名解析為經(jīng)緯度查不到會拋出Location name not found天氣查詢再用經(jīng)緯度調(diào)用api.open-meteo.com/v1/forecast拉取溫度、體感溫度、濕度、風(fēng)速、陣風(fēng)與天氣碼最后經(jīng)getWeatherCondition把 WMO 天氣碼映射為可讀描述。工具通過createTool聲明inputSchemalocation: z.string()與outputSchemaWeatherToolResultSchema為前端生成式 UI 渲染提供了結(jié)構(gòu)化輸出依據(jù)。前端能力全景一個頁面演示五種 CopilotKit 特性src/app/page.tsx 用極少的代碼同時演示了 CopilotKit v2 的五種核心能力前端工具Frontend ToolsuseFrontendTool({ name: setThemeColor, ... })讓 Agent 可以通過調(diào)用前端函數(shù)直接改變頁面主題色建議SuggestionsuseConfigureSuggestions配置了Generative UI / Frontend Tools / Human In the Loop / Write Agent State / Update Agent State / Read Agent State六個引導(dǎo)提示詞幫助用戶快速體驗(yàn)各能力生成式 UIGenerative UI第二個useFrontendTool以available: false聲明weatherTool的render函數(shù)返回WeatherCard——即 Agent 調(diào)用工具時由前端組件渲染結(jié)果卡片Human-in-the-LoopuseHumanInTheLoop聲明go_to_moon工具并渲染MoonCard把需要用戶確認(rèn)的回合交還給界面共享狀態(tài)Shared StateuseAgent({ agentId: default })讀取agent.state并通過agent.setState寫回對應(yīng) Mastra working memory 中的AgentState實(shí)現(xiàn)前端狀態(tài) ? Agent 記憶雙向同步。頁面整體被一個非受控uncontrolled的CopilotChatConfigurationProvider包裹CopilotThreadsDrawer與CopilotSidebar共享同一個 active thread——注釋特別提醒受控 provider 會阻塞 New重置新線程因此必須保持非受控。運(yùn)行時裝配Route Handler 如何調(diào)度 AgentWeb 端所有請求都匯聚到 src/app/api/copilotkit/[[...slug]]/route.tsconst runtime new CopilotRuntime({ agents: createLocalAgents(), ...(process.env.CPK_INTELLIGENCE_API_KEY ? { intelligence: new CopilotKitIntelligence({ apiKey: ..., apiUrl?, wsUrl? }), identifyUser: () ({ id: demo-user, name: Demo User }), } : { runner: new InMemoryAgentRunner() }), }); const app createCopilotEndpoint({ runtime, basePath: /api/copilotkit }); export const GET handle(app); export const POST handle(app); export const PATCH handle(app); export const DELETE handle(app);這里有兩套執(zhí)行路徑由環(huán)境變量CPK_INTELLIGENCE_API_KEY決定未設(shè)置時使用InMemoryAgentRunnerAgent 直接在當(dāng)前進(jìn)程內(nèi)執(zhí)行適合本地快速開發(fā)設(shè)置后啟用CopilotKitIntelligence線程歷史與記憶由 Intelligence 服務(wù)接管并通過identifyUser關(guān)聯(lián)用戶身份。注意代碼注釋的警告這個identifyUser是演示樁返回固定的demo-user任何多用戶部署前都必須替換為從真實(shí)鑒權(quán)推導(dǎo)的用戶身份否則所有用戶會共享同一份線程歷史且未知的用戶 id 可能讓線程操作失敗。createLocalAgents()src/agent.ts返回MastraAgent.getLocalAgents({ mastra })的結(jié)果即把 Mastra 實(shí)例中所有注冊的 Agent 一次性暴露給運(yùn)行時調(diào)度。Running a Channel把同一個 Agent 送上 Slack / Teams概念與前置條件channel-host.mts把 Web 路由服務(wù)的同一個 Agent掛載為 Intelligence ChannelSlack、Teams 等。它需要CPK_INTELLIGENCE_API_KEY以及一個聲明在.copilotkit/channels.json中的 Channel——這兩項(xiàng)都可以用 CLI 完成copilotkit init # 或 copilotkit channels add name這兩個命令會寫入channels.json文件并把.env需要的憑據(jù)補(bǔ)全然后運(yùn)行npm run channel宿主進(jìn)程的設(shè)計哲學(xué)channel-host.mts的注釋把職責(zé)邊界說得很清楚宿主持有零個 provider 憑據(jù)、暴露零個 provider 端點(diǎn)——provider 邊緣邊緣連接由 Intelligence 擁有交付通過其實(shí)時傳輸完成因此同一個宿主文件對每個 provider 都字節(jié)級一致Channel 本體在channels.mts里聲明——添加 commands、reactions 或onMentionhandler 的地方是channels.mts而不是宿主宿主只負(fù)責(zé)進(jìn)程生命周期——它沒有 HTTP 服務(wù)器gateway 連接是出向的保持連接打開就是讓進(jìn)程存活的方式生產(chǎn)部署通常還要加一個上報channels.status()的健康端點(diǎn)。宿主啟動的關(guān)鍵細(xì)節(jié)channel-host.mts 的啟動邏輯值得逐段拆解必填環(huán)境變量required()檢查CPK_INTELLIGENCE_API_KEY缺失時打印[channel] missing required env var: name并以退出碼 1 退出優(yōu)雅關(guān)閉在激活前先注冊SIGINT/SIGTERM處理調(diào)用handler.channels.stop()做拆線——這樣即使 Ctrl-C 發(fā)生在連接窗口期也不會孤兒化 gateway 會話有界等待await handler.channels.ready({ timeoutMs: 30_000 })用 30 秒上限避免連接卡死導(dǎo)致啟動無限掛起失敗則以非零碼退出如實(shí)上報狀態(tài)ready()解析只代表每個 Channel 進(jìn)入終態(tài)含setup_required因此宿主隨后用channels.status()的真實(shí)結(jié)果決定日志文案。啟動完成后日志會按 Channel 逐條給出真實(shí)狀態(tài)Channel name is online.—— 會話已建立、可以收發(fā)消息Channel name is declared but no provider is attached yet.—— 這是正常的等待狀態(tài)不是失敗運(yùn)行copilotkit channels status查看還差哪些配置。README 特意提醒這兩種日志都不代表 provider 應(yīng)用已安裝、可觸達(dá)或有人能發(fā)消息——要確認(rèn) Channel 真正可用需要單獨(dú)驗(yàn)證把 bot 拉進(jìn)會話、實(shí)際發(fā)一條消息。Channel 名稱解析與通道聲明channels.mts 的resolveChannelName()決定宿主應(yīng)保持哪個 Channel 在線優(yōu)先級為環(huán)境變量INTELLIGENCE_CHANNEL_NAME一個項(xiàng)目聲明了多個 Channel 時必須顯式指定否則視為歧義錯誤并打印候選名稱退出.copilotkit/channels.json中聲明的唯一 Channel 名文件缺失或解析失敗都會給出不同的修復(fù)提示分別建議copilotkit channels add name或檢查 JSON 拼寫——注釋強(qiáng)調(diào)文件不存在與JSON 損壞是兩個不同的問題刻意分兩個 try 塊處理。createDefaultChannel()則用createChannel構(gòu)建通道并注冊onMessage而非onMentionhandler——這樣 1:1 平臺與多人群組平臺都能工作。handler 把當(dāng)前消息顯式傳給thread.runAgent({ prompt })因?yàn)镃hannel 歷史不包含進(jìn)行中的回合不傳的話 Agent 會以零條消息運(yùn)行出錯時會給用戶回一句抱歉并繼續(xù)存活。已知限制為何沒有typecheck:channel與其他 Starter 不同這個模板沒有typecheck:channel腳本。原因記錄在 tsconfig.channel.json宿主的導(dǎo)入鏈會到達(dá)src/mastra/**而該子樹帶有一個與 Channel 宿主無關(guān)的既有類型錯誤——src/mastra/agents/index.ts第 18 行的Type Memory is not assignable to type MastraMemory。由于 TypeScript 無論include如何都會跟隨 import 鏈tsc -p tsconfig.channel.json --noEmit永遠(yuǎn)無法通過。該配置仍被channel腳本經(jīng)tsx --tsconfig使用只是用于模塊解析而非干凈的類型檢查??蛇x進(jìn)階CopilotKit Intelligence 線程與記憶CopilotKit Intelligence 為 Agent 增加持久化線程歷史與跨會話記憶。它需要三樣?xùn)|西COPILOTKIT_LICENSE_TOKEN、CPK_INTELLIGENCE_API_KEY以及本地運(yùn)行的 Intelligence 棧Docker Desktop 本地 Intelligence 倉庫檢出。前置條件清單運(yùn)行中的Docker Desktop從 CopilotKit Cloud 獲取的COPILOTKIT_LICENSE_TOKEN對應(yīng) Intelligence 項(xiàng)目的CPK_INTELLIGENCE_API_KEY本地克隆的 Intelligence 倉庫。docker-compose.intelligence.yml默認(rèn)從本 Starter 往上三級找同級目錄../../../Intelligence若檢出在其他位置用環(huán)境變量INTELLIGENCE_REPO覆蓋。啟動 Intelligence 棧在 Starter 目錄內(nèi)執(zhí)行docker compose -f docker-compose.intelligence.yml up -d --wait首次運(yùn)行會從源碼構(gòu)建 intelligence 鏡像可能需要幾分鐘。從 docker-compose.intelligence.yml 可以看到棧內(nèi)三個服務(wù)及其端口服務(wù)鏡像宿主機(jī)端口健康檢查postgrespgvector/pgvector:0.8.2-pg165485POSTGRES_HOST_PORT可覆蓋pg_isready -U intelligence -d intelligence_appredisredis:7-alpine6392REDIS_HOST_PORT可覆蓋redis-cli pingintelligencecpki/intelligence-composite:local從 Intelligence 倉庫構(gòu)建4204 API / 4404 GatewayAPP_API_HOST_PORT/GATEWAY_HOST_PORT可覆蓋curlAPI 健康端點(diǎn) nc探測 gatewaypostgres服務(wù)掛載了 docker/postgres-init/01-create-databases.sql首次啟動即創(chuàng)建intelligence_app與intelligence_app_shadow兩個庫intelligence 服務(wù)以self_hosted模式運(yùn)行且為最小化開發(fā)環(huán)境默認(rèn)關(guān)閉了 memory 與 structured-loggingMEMORY_ENABLED: false、SL_ENABLED: false并內(nèi)置了本地開發(fā)用的AUTH_SECRET、RUNNER_AUTH_SECRET、SECRET_KEY_BASE等密鑰。驗(yàn)證棧健康docker compose -f docker-compose.intelligence.yml ps三個服務(wù)postgres、redis、intelligence都應(yīng)顯示healthy。配置環(huán)境變量在.env中添加COPILOTKIT_LICENSE_TOKENyour-license-token-here CPK_INTELLIGENCE_API_KEYyour-project-api-key-here INTELLIGENCE_API_URLhttp://localhost:4204 INTELLIGENCE_GATEWAY_WS_URLws://localhost:4404然后照常npm run dev啟動即可。只要設(shè)置了CPK_INTELLIGENCE_API_KEY線程歷史與記憶功能就會自動激活這正是前文 route.ts 中條件裝配CopilotKitIntelligence的觸發(fā)開關(guān)。停止與重置# Stop without removing data: docker compose -f docker-compose.intelligence.yml down # Full reset (removes postgres redis volumes): docker compose -f docker-compose.intelligence.yml down -vdown -v會連帶刪除postgres-data與redis-data兩個命名卷如需徹底清空線程歷史與記憶數(shù)據(jù)可選用。調(diào)試與排障要點(diǎn)查看運(yùn)行時調(diào)試日志用npm run dev:debug即LOG_LEVELdebug npm run dev日志級別會透傳到src/mastra/index.ts的ConsoleLoggerChannel 起不來先確認(rèn).env中CPK_INTELLIGENCE_API_KEY已配置、.copilotkit/channels.json存在且可解析copilotkit channels status可查看 setup 缺口多 Channel 歧義聲明了多個 Channel 時務(wù)必設(shè)置INTELLIGENCE_CHANNEL_NAME宿主會拒絕猜測Agent 不響應(yīng)檢查是否誤把 working memory 作用域配成了thread首回合會報Thread id not found應(yīng)保持resource作用域線程歷史共享route.ts 中的identifyUser是演示樁多用戶部署前必須替換為真實(shí)鑒權(quán)身份。小結(jié)這條集成鏈路教會了我們什么從 README 到源碼這個模板演示了一條完整的多端交付鏈路Mastra 定義能力工具 記憶→ AG-UI 適配層轉(zhuǎn)成通用 Agent → CopilotKit 運(yùn)行時在 Next.js 中調(diào)度 → 生成式 UI / 前端工具 / Human-in-the-Loop 渲染到界面 → 同一 Agent 又經(jīng) Intelligence Channel 掛到 Slack / Teams。其中Channel 宿主只持有進(jìn)程生命周期、provider 邊緣歸 Intelligence 所有的設(shè)計與working memory 必須用 resource 作用域的橋接細(xì)節(jié)都是復(fù)用該模板時最值得保留的心得。需要更深入的 API 說明時可繼續(xù)查閱倉庫中的 CopilotKit 文檔源碼如 docs 目錄下的相關(guān)頁面與copilotkit/*各包源碼packages/runtime、packages/channels?!久赓M(fèi)下載鏈接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol項(xiàng)目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考