跳转到内容

React 连接到 AG-UI Agent

Nx Plugin for AWS 提供了一个生成器,用于将 React 网站连接到公开 AG-UI 协议的 Agent。它在您的网站上使用 @ag-ui/client HttpAgent 连接 CopilotKit,并支持 AWS IAM 和 Cognito 身份验证。

在使用此生成器之前,请确保您具备:

  1. 一个 React 网站(使用 ts#website 生成器生成)
  2. 一个带有 protocol=ag-ui 的 TypeScript 或 Python Agent(使用 ts#agentpy#agent 生成器生成)
  3. 对于已部署的 agent,需要通过 ts#website#auth 生成器添加 Cognito Auth
Terminal window
pnpm nx g @aws/nx-plugin:connection
您还可以执行试运行以查看哪些文件会被更改
Terminal window
pnpm nx g @aws/nx-plugin:connection --dry-run

系统将提示您选择 React 网站作为源项目,并选择包含 AG-UI Agent 的项目作为目标项目。如果您的目标项目包含多个组件(例如多个 agent 或其他组件类型),系统将提示您指定 targetComponent 以消除歧义。

参数类型默认值描述
sourceProject 必需string-源项目
targetProject 必需string-要连接到的目标项目
sourceComponent string-要从其连接的源组件(组件名称、相对于源项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为源。
targetComponent string-要连接到的目标组件(组件名称、相对于目标项目根目录的路径或生成器 ID)。使用 '.' 显式选择项目作为目标。
preferInstallDependencies booleantrue是否在生成器运行后优先安装依赖项。设置为 false 可在批量运行多个生成器时延迟安装(如果后续生成器需要计算 Nx 项目图,仍会运行安装);在最后统一安装一次。

生成器创建一个单一共享的 AguiProvider 组件、每个连接的 agent 一个 hook,以及一个用于 CopilotKit 聊天组件的主题包装器:

  • 文件夹src
    • 文件夹components
      • AguiProvider.tsx 为每个 AG-UI agent 提供单一的 CopilotKitProvider。在第一次运行 connection 时创建,在后续运行时更新以注册每个新 agent。
      • 文件夹copilot
        • index.tsx 重新导出 CopilotChatCopilotSidebarCopilotPopup,并使用与您网站的 ux(Cloudscape、Shadcn 或无主题)匹配的插槽默认值。
        • ThemeComponents .tsx 每个插槽的主题组件(例如 CloudscapeAssistantMessage.tsxShadcnChatInput.tsx)。仅在 uxcloudscapeshadcn 时提供。
    • 文件夹hooks
      • useAgui<AgentName>.tsx 注册一个 AG-UI agent。每次运行 connection 生成一个文件。
      • useSigV4.tsx SigV4 签名(仅限 IAM)

第二次为不同的 agent 运行 connection添加一个新的 useAgui<AgentName>.tsx hook 并更新 AguiProvider.tsx 以注册两个 hook——您对 provider 所做的任何自定义编辑都会被保留。main.tsx 保持其单一的 <AguiProvider> 包装器——您永远不会得到嵌套的 provider。

以下依赖项将添加到根 package.json

  • @copilotkit/react-core — 提供 CopilotKitProvider 和聊天组件(CopilotChatCopilotSidebarCopilotPopup
  • @ag-ui/client — 生成的 hook 使用的 HttpAgent
  • aws4fetchoidc-client-tsreact-oidc-context@aws-sdk/credential-providers — 仅限 IAM 身份验证
  • react-oidc-context — Cognito 身份验证

每个 useAgui<AgentName> hook 从运行时配置读取其 agent 的运行时值,并实例化一个 @ag-ui/client HttpAgent

  • 已部署:运行时值是 Bedrock AgentCore Runtime ARN,它会被转换为 AgentCore HTTPS 端点:https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<encoded-arn>/invocations?qualifier=DEFAULT
  • 本地开发dev 将值覆盖为 agent 的本地 URL(例如 http://localhost:8081

共享的 AguiProvider 调用每个生成的 hook,并将每个 hook 展开到单个 CopilotKitProviderselfManagedAgents 中,从而将它们全部公开给 CopilotKit 组件。

CopilotKit 是 AG-UI 协议的第一方参考 React 客户端,并提供现成的聊天组件:

  • <CopilotChat /> — 完整的聊天界面
  • <CopilotSidebar /> — 固定侧面板聊天
  • <CopilotPopup /> — 浮动聊天弹出窗口

将这些组件中的任何一个放置在 <AguiProvider> 包装器内的任何位置(已为您连接到 main.tsx 中)。

生成的代码根据您的 agent 配置处理身份验证:

  • IAM(默认):使用 AWS SigV4 签名的 HTTP 请求。凭证从配置了您网站身份验证的 Cognito Identity Pool 获取。
  • Cognito:将 JWT 访问令牌作为 Bearer 令牌嵌入到 Authorization 标头中。

AG-UI 和 AgentCore Runtime 各自以不同的方式识别对话,生成的 hook 将它们联系在一起:

  • threadId — AG-UI 对话标识符,在请求正文中发送。除非您传递显式的 threadId,否则 CopilotKit 会为每个聊天生成一个随机 UUID。
  • Session ID — AgentCore Runtime 会话,在 X-Amzn-Bedrock-AgentCore-Runtime-Session-Id 标头中发送。它选择为请求提供服务的 microVM,并且是您的 agent 的 session.ts / session.py 用于键入对话状态的内容。

hook 从线程 ID 派生会话 ID,将其右填充到 AgentCore Runtime 所需的 33 个字符:

function agentCoreSessionId(input: RunAgentInput): string {
return (input.threadId ?? '').padEnd(33, '0');
}

不设置 threadId 是最简单的——CopilotKit 生成的 UUID 已经是 36 个字符。如果您显式传递一个,请确保它至少有 33 个字符,因为填充会将仅在尾随字符上不同的线程 ID 映射到同一个会话。

Session ID 和 Thread ID 都由浏览器提供。要将每个用户限制为他们自己的对话,请参阅 py#agentts#agent 指南。

如果您的 agent 使用 IAM 身份验证,则必须授予 Cognito Identity Pool 的已认证角色调用该 agent 的权限。

packages/infra/src/stacks/application-stack.ts
const identity = new UserIdentity(this, 'Identity');
const myAgent = new MyAgent(this, 'MyAgent');
// Grant the authenticated Cognito role permission to invoke the agent
myAgent.grantInvokeAccess(identity.identityPool.authenticatedRole);

grantInvokeAccess 会在 agent 的运行时 ARN 上连接所有 AgentCore 调用操作(InvokeAgentRuntimeInvokeAgentRuntimeWithWebSocketStream)。

如果您的 agent 使用 Cognito 身份验证,则无需定义任何额外的基础设施即可将您的网站连接到您的 agent。

使用 agentId 实例化 CopilotKit 组件以选择要使用的 agent。该 id 是 agent 的名称——与您运行 ts#agentpy#agent 生成器时选择的名称相同——您也可以在生成的 hook 文件中找到它(例如从 packages/web/src/hooks/useAgui<AgentName>.tsx 返回的键)。

从生成的 ./components/copilot 模块导入聊天组件,以便自动应用与您网站的 ux 匹配的主题:

import { CopilotChat } from './components/copilot';
function ChatPage() {
return (
<CopilotChat
agentId="agent"
labels={{
welcomeMessageText: 'How can I help you today?',
chatInputPlaceholder: 'Ask me anything...',
}}
/>
);
}

每个 agent 运行一次 connection 生成器。通过共享的 AguiProvider 注册的每个 agent 在应用程序的任何位置都可见——使用不同的 agentId 实例化 CopilotKit 组件,以将每个聊天路由到您想要的 agent:

import { CopilotChat } from './components/copilot';
<CopilotChat agentId="story" /> {/* talks to StoryAgent */}
<CopilotChat agentId="research" /> {/* talks to ResearchAgent */}

<CopilotChat />(以及 <CopilotSidebar /><CopilotPopup />)使用递归插槽系统——您可以使用 Tailwind 类字符串、prop 对象或自定义 React 组件覆盖任何子组件。有关完整的插槽树,请参阅 CopilotKit 插槽指南

生成器从您的 React 网站项目读取 metadata.ux,并在 src/components/copilot/index.tsx 提供一个主题包装器模块,使聊天组件与您的 UI 的其余部分匹配,无需任何额外配置:

ux应用于 CopilotChat / CopilotSidebar / CopilotPopup 的样式
cloudscape消息在 Cloudscape ChatBubble 中渲染,带有生成式 AI Avatar(匹配 Cloudscape 生成式 AI 聊天模式);输入指示器变为 LoadingBar,输入为 PromptInput。由 @cloudscape-design/components@cloudscape-design/chat-components 构建。
shadcn助手消息在 bg-muted 气泡中渲染,带有 Sparkles 头像;用户消息在 bg-primary 气泡中右对齐渲染,带有 User 头像。输入是圆角 Textarea + 药丸形状的发送/停止 Button(Enter 提交,Shift+Enter 换行)。使用来自共享 common-shadcn 包的 shadcn 原语。
none(或其他任何值)无主题——模块只是重新导出默认的 CopilotKit 组件。

本地主题模块导入主题组件(而不是直接从 @copilotkit/react-core/v2 导入),以便自动应用主题:

import { CopilotChat } from './components/copilot';
<CopilotChat agentId="agent" />

主题作为插槽默认值应用,因此您显式传递的任何插槽仍然优先——每当您需要一次性覆盖时,您都可以保持完全控制。

生成的主题完全位于您的项目内:

  • src/components/copilot/index.tsx — 导出主题化的 CopilotChat / CopilotSidebar / CopilotPopup 以及 cloudscapeCopilotTheme / shadcnCopilotTheme 对象。编辑此文件以更改应用程序中每个聊天的默认插槽连接。
  • src/components/copilot/<ThemeComponent>.tsx — 每个插槽的主题组件(例如 CloudscapeAssistantMessageShadcnChatInput)。编辑这些以调整单个插槽的外观,而无需重新连接主题。

例如,要在保持主题其余部分的同时放入您自己的用户消息渲染器,请编辑 src/components/copilot/ 中的相关文件并从 index.tsx 重新导出它。

每个聊天的覆盖仍然可以与主题一起工作——您作为插槽 prop 传递的任何内容都会覆盖主题默认值:

<CopilotChat
agentId="agent"
// style the input and its children
input={{
textArea: 'text-blue-600',
sendButton: 'bg-blue-600 hover:bg-blue-700',
}}
// style nested message slots
messageView={{
assistantMessage: 'bg-blue-50 rounded-xl p-2',
userMessage: 'bg-blue-100 rounded-xl',
}}
/>

任何插槽都可以接受 React 组件而不是 className,因此您可以完全替换默认值:

import { CopilotChat } from './components/copilot';
const MySendButton: React.FC<{ onClick: () => void }> = ({ onClick }) => (
<button onClick={onClick} className="my-send-btn">
Send
</button>
);
<CopilotChat
agentId="agent"
input={{ sendButton: MySendButton }}
/>;

更深层次的覆盖遵循相同的形状——例如,仅替换助手消息上的复制按钮:

<CopilotChat
agentId="agent"
messageView={{
assistantMessage: {
copyButton: ({ onClick }) => <button onClick={onClick}>Copy</button>,
},
}}
/>

连接生成器会自动配置 dev 集成:

  1. 运行 nx dev <website> 也会启动 agent 的本地服务器
  2. 运行时配置被覆盖以指向本地 AG-UI URL(例如 http://localhost:8081
  3. 网站和 agent 一起热重载
Terminal window
pnpm nx dev <WebsiteProject>