콘텐츠로 이동

React에서 AG-UI Agent로

Nx Plugin for AWS는 AG-UI 프로토콜을 노출하는 Agent에 React 웹사이트를 연결하는 생성기를 제공합니다. 이는 웹사이트에서 @ag-ui/client HttpAgent와 함께 CopilotKit을 연결하며, AWS IAM 및 Cognito 인증을 지원합니다.

이 생성기를 사용하기 전에 다음이 필요합니다:

  1. React 웹사이트 (ts#website 생성기를 사용하여 생성)
  2. protocol=ag-ui를 사용하는 TypeScript 또는 Python Agent (ts#agent 또는 py#agent 생성기를 사용하여 생성)
  3. 배포된 에이전트의 경우, ts#website#auth 생성기를 통해 추가된 Cognito Auth

이 제너레이터 실행@aws/nx-plugin:connection

pnpm nx g @aws/nx-plugin:connection
명령 구성하기5

필수

필수

소스 프로젝트로 React 웹사이트를, 타겟 프로젝트로 AG-UI Agent가 포함된 프로젝트를 선택하라는 메시지가 표시됩니다. 타겟 프로젝트에 여러 컴포넌트(여러 에이전트 또는 다른 컴포넌트 유형)가 포함된 경우, 명확히 구분하기 위해 targetComponent를 지정하라는 메시지가 표시됩니다.

targetComponent는 에이전트 생성기에 제공한 --name을 모든 대소문자 형식(StoryAgent, story-agent)으로, 타겟 프로젝트 루트에 상대적인 컴포넌트의 경로, 또는 생성기 id로 받습니다.

제너레이터 옵션5 옵션
sourceProject필수string

소스 프로젝트

targetProject필수string

연결할 대상 프로젝트

sourceComponentstring

연결할 소스 컴포넌트 (컴포넌트 이름, 소스 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 소스로 명시적으로 선택하려면 '.'을 사용하세요.

targetComponentstring

연결할 대상 컴포넌트 (컴포넌트 이름, 대상 프로젝트 루트 기준 상대 경로, 또는 generator id). 프로젝트를 대상으로 명시적으로 선택하려면 '.'을 사용하세요.

preferInstallDependenciesboolean기본값: true

생성기 실행 후 의존성 설치를 선호할지 여부입니다. 여러 생성기를 일괄 처리할 때 설치를 연기하려면 false로 설정하세요 (후속 생성기가 Nx 프로젝트 그래프를 계산할 수 있도록 필요한 경우 설치는 여전히 실행됩니다); 마지막에 한 번만 설치합니다.

생성기는 단일 공유 AguiProvider 컴포넌트, 연결된 에이전트당 하나의 훅, 그리고 CopilotKit 채팅 컴포넌트를 위한 테마 래퍼를 생성합니다:

  • 디렉터리src
    • 디렉터리components
      • AguiProvider.tsx 모든 AG-UI 에이전트를 위한 단일 CopilotKitProvider. 첫 번째 connection 실행 시 생성되고 후속 실행 시 각 새 에이전트를 등록하도록 업데이트됩니다.
      • <agent-name>-chat.tsx 이 에이전트의 id에 바인딩된 테마가 적용된 <AgentName>Chat. connection 실행당 하나의 파일.
      • 디렉터리copilot
        • index.tsx 웹사이트의 ux(Cloudscape, Shadcn 또는 테마 없음)와 일치하는 슬롯 기본값으로 CopilotChat, CopilotSidebarCopilotPopup을 재내보냅니다.
        • ThemeComponents .tsx 슬롯별 테마 컴포넌트(예: CloudscapeAssistantMessage.tsx, ShadcnChatInput.tsx). uxcloudscape 또는 shadcn일 때만 제공됩니다.
    • 디렉터리hooks
      • useAgui<AgentName>.tsx 하나의 AG-UI 에이전트를 등록하고 해당 id를 <AGENT_NAME>_ID로 내보냅니다. connection 실행당 하나의 파일.
      • useSigV4.tsx SigV4 서명 (IAM 전용)

다른 에이전트에 대해 두 번째로 connection을 실행하면 새로운 useAgui<AgentName>.tsx 훅과 <agent-name>-chat.tsx 컴포넌트가 추가되고 AguiProvider.tsx가 두 훅을 모두 등록하도록 업데이트됩니다 — 프로바이더에 대한 사용자 정의 편집 내용은 보존됩니다. main.tsx는 단일 <AguiProvider> 래퍼를 유지하므로 중첩된 프로바이더가 생기지 않습니다.

다음 종속성이 루트 package.json에 추가됩니다:

  • @copilotkit/react-coreCopilotKitProvider 및 채팅 컴포넌트(CopilotChat, CopilotSidebar, CopilotPopup)를 제공합니다
  • @ag-ui/client — 생성된 훅에서 사용하는 HttpAgent
  • aws4fetch, oidc-client-ts, react-oidc-context, @aws-sdk/credential-providers — IAM 인증 전용
  • react-oidc-context — Cognito 인증

useAgui<AgentName> 훅은 런타임 구성에서 에이전트의 런타임 값을 읽고 @ag-ui/client HttpAgent를 인스턴스화합니다:

  • 배포됨: 런타임 값은 Bedrock AgentCore Runtime ARN이며, 이는 AgentCore HTTPS 엔드포인트로 변환됩니다: https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<encoded-arn>/invocations?qualifier=DEFAULT
  • 로컬 개발: dev가 값을 에이전트의 로컬 URL(예: http://localhost:8081)로 재정의합니다

공유 AguiProvider는 생성된 모든 훅을 호출하고 각각을 단일 CopilotKitProviderselfManagedAgents에 펼쳐서 CopilotKit 컴포넌트에 모두 노출합니다.

CopilotKit은 AG-UI 프로토콜을 위한 1st-party 참조 React 클라이언트이며 즉시 사용 가능한 채팅 컴포넌트를 제공합니다:

  • <CopilotChat /> — 전체 채팅 인터페이스
  • <CopilotSidebar /> — 고정 사이드 패널 채팅
  • <CopilotPopup /> — 플로팅 채팅 팝업

이들 중 하나를 <AguiProvider> 래퍼 내부 어디에나 배치할 수 있습니다(이미 main.tsx에 연결되어 있습니다).

생성된 코드는 에이전트의 구성에 따라 인증을 처리합니다:

  • IAM (기본값): AWS SigV4 서명된 HTTP 요청을 사용합니다. 자격 증명은 웹사이트의 인증과 함께 구성된 Cognito Identity Pool에서 가져옵니다.
  • Cognito: JWT 액세스 토큰을 Bearer 토큰으로 Authorization 헤더에 포함합니다.

AG-UI와 AgentCore Runtime은 각각 대화를 다르게 식별하며, 생성된 훅은 이들을 함께 연결합니다:

  • threadId — AG-UI 대화 식별자로, 요청 본문에 전송됩니다. CopilotKit은 명시적인 threadId를 전달하지 않는 한 채팅당 무작위 UUID를 생성합니다.
  • Session ID — AgentCore Runtime 세션으로, X-Amzn-Bedrock-AgentCore-Runtime-Session-Id 헤더에 전송됩니다. 요청을 처리하는 microVM을 선택하며, 에이전트의 session.ts / session.py가 대화 상태를 키로 사용하는 것입니다.

훅은 스레드 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#agent 또는 ts#agent 가이드를 참조하세요.

에이전트가 IAM 인증을 사용하는 경우, Cognito Identity Pool의 인증된 역할에 에이전트를 호출할 수 있는 권한이 부여되어야 합니다.

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는 에이전트의 런타임 ARN에 대한 모든 AgentCore 호출 작업(InvokeAgentRuntime, InvokeAgentRuntimeWithWebSocketStream)을 연결합니다.

에이전트가 Cognito 인증을 사용하는 경우, 웹사이트를 에이전트에 연결하기 위해 추가 인프라를 정의할 필요가 없습니다.

생성기는 연결된 에이전트당 <AgentName>Chat 컴포넌트를 제공하며, 이미 해당 에이전트의 id에 바인딩되어 있고 웹사이트의 ux와 일치하도록 테마가 적용되어 있습니다. <AguiProvider> 래퍼 내부 어디에나 배치할 수 있습니다:

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

이는 agentId를 제외한 모든 CopilotChat prop을 전달하므로 <CopilotChat />에 전달할 수 있는 모든 것이 여기서도 작동합니다.

에이전트당 한 번씩 connection 생성기를 실행합니다. 각 실행은 해당 에이전트의 자체 채팅 컴포넌트를 제공하므로 특정 에이전트로 채팅을 라우팅하는 것은 어떤 컴포넌트를 렌더링하느냐의 문제입니다:

import { StoryAgentChat } from './components/story-agent-chat';
import { ResearchAgentChat } from './components/research-agent-chat';
<StoryAgentChat /> {/* talks to StoryAgent */}
<ResearchAgentChat /> {/* talks to ResearchAgent */}

원시 id가 필요한 경우 — 예를 들어 CopilotKit의 자체 훅을 호출하려면 — 각 생성된 훅이 이를 내보냅니다:

import { STORY_AGENT_ID } from './hooks/useAguiStoryAgent';

<CopilotChat /> (및 <CopilotSidebar />, <CopilotPopup />)는 재귀적 슬롯 시스템을 사용합니다 — Tailwind 클래스 문자열, prop 객체 또는 사용자 정의 React 컴포넌트로 모든 하위 컴포넌트를 재정의할 수 있습니다. 전체 슬롯 트리는 CopilotKit 슬롯 가이드를 참조하세요.

생성기는 React 웹사이트 프로젝트에서 metadata.ux를 읽고 src/components/copilot/index.tsx에 테마 래퍼 모듈을 제공하여 추가 구성 없이 채팅 컴포넌트가 나머지 UI와 일치하도록 합니다:

uxCopilotChat / CopilotSidebar / CopilotPopup에 적용되는 스타일
cloudscape메시지는 gen-AI Avatar가 있는 Cloudscape ChatBubble 내부에 렌더링됩니다(Cloudscape 생성형 AI 채팅 패턴과 일치); 타이핑 표시기는 LoadingBar가 되고 입력은 PromptInput입니다. @cloudscape-design/components@cloudscape-design/chat-components로 구축됩니다.
shadcn어시스턴트 메시지는 Sparkles 아바타가 있는 bg-muted 버블에 렌더링됩니다; 사용자 메시지는 User 아바타가 있는 bg-primary 버블에 오른쪽 정렬로 렌더링됩니다. 입력은 둥근 Textarea + 알약 모양의 전송/중지 Button입니다(Enter는 제출, Shift+Enter는 줄바꿈). 공유 common-shadcn 패키지의 shadcn 프리미티브를 사용합니다.
none (또는 기타)테마 없음 — 모듈은 기본 CopilotKit 컴포넌트를 재내보냅니다.

제공된 <AgentName>Chat 컴포넌트는 이미 테마가 적용되어 있습니다. 직접 연결하는 채팅의 경우, 테마가 자동으로 적용되도록 로컬 테마 모듈에서 가져옵니다(@copilotkit/react-core/v2에서 직접 가져오지 않음):

import { CopilotChat } from './components/copilot';
import { STORY_AGENT_ID } from './hooks/useAguiStoryAgent';
<CopilotChat agentId={STORY_AGENT_ID} />

테마는 슬롯 기본값으로 적용되므로 명시적으로 전달하는 슬롯은 여전히 우선합니다 — 일회성 재정의가 필요할 때마다 완전한 제어를 유지합니다.

생성된 테마는 전적으로 프로젝트 내부에 있습니다:

  • src/components/copilot/index.tsx — 테마가 적용된 CopilotChat / CopilotSidebar / CopilotPopupcloudscapeCopilotTheme / shadcnCopilotTheme 객체를 내보냅니다. 이 파일을 편집하여 앱의 모든 채팅에 대한 기본 슬롯 연결을 변경합니다.
  • src/components/copilot/<ThemeComponent>.tsx — 슬롯별 테마 컴포넌트(예: CloudscapeAssistantMessage, ShadcnChatInput). 테마를 다시 연결하지 않고 단일 슬롯의 모양을 조정하려면 이를 편집합니다.

예를 들어, 테마의 나머지 부분을 유지하면서 자체 사용자 메시지 렌더러를 삽입하려면 src/components/copilot/의 관련 파일을 편집하고 index.tsx에서 재내보냅니다.

채팅별 재정의는 테마와 함께 작동합니다 — 슬롯 prop으로 전달하는 모든 것은 테마 기본값을 재정의합니다:

<StoryAgentChat
// 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',
}}
/>

사용자 정의 컴포넌트로 슬롯 교체

섹션 제목: “사용자 정의 컴포넌트로 슬롯 교체”

모든 슬롯은 className 대신 React 컴포넌트를 사용할 수 있으므로 기본값을 완전히 교체할 수 있습니다. 슬롯이 선언하는 props에 대해 컴포넌트를 타입 지정하세요 — sendButton<button>을 렌더링하므로 ButtonHTMLAttributes를 받습니다:

import { StoryAgentChat } from './components/story-agent-chat';
const MySendButton: React.FC<React.ButtonHTMLAttributes<HTMLButtonElement>> = ({
onClick,
}) => (
<button onClick={onClick} className="my-send-btn">
Send
</button>
);
<StoryAgentChat input={{ sendButton: MySendButton }} />;

더 깊은 재정의는 동일한 형태를 따릅니다 — 예를 들어 어시스턴트 메시지의 복사 버튼만 교체:

<StoryAgentChat
messageView={{
assistantMessage: {
copyButton: ({ onClick }) => <button onClick={onClick}>Copy</button>,
},
}}
/>

연결 생성기는 자동으로 dev 통합을 구성합니다:

  1. nx dev <website>를 실행하면 에이전트의 로컬 서버도 시작됩니다
  2. 런타임 구성이 로컬 AG-UI URL(예: http://localhost:8081)을 가리키도록 재정의됩니다
  3. 웹사이트와 에이전트가 함께 핫 리로드됩니다
Terminal window
pnpm nx dev <WebsiteProject>