사용자가 대화로 워크플로우를 생성하도록 하세요 (ByteChef Embedded, 파트 9)
요약
본 글은 자동화 워크플로우 생성 과정에 채팅 인터페이스를 도입하는 방법을 다룹니다. 사용자가 복잡한 캔버스 대신 자연어로 원하는 바를 설명하면, 에이전트가 이를 이해하고 자동으로 워크플로우를 구축해주는 방식입니다. 개발자는 React 컴포넌트와 콜백만으로 이 기능을 구현할 수 있습니다.
핵심 포인트
- 사용자에게는 채팅 인터페이스가 가장 직관적이고 관대합니다.
- 에이전트는 사용자의 자연어 설명을 기반으로 워크플로우를 구축합니다.
- 구현은 React 컴포넌트, 콜백, 버튼 등 최소한의 개발 노력으로 가능합니다.
사용자들은 자동화를 처음부터 아는 경우가 거의 없습니다. 그들은 설명하는 과정에서 발견합니다. 예를 들어, "딜이 성사되면... 실제로 엔터프라이즈 딜만 해당하고... 계정 관리자를 초대하고... 주말은 건너뛰어."와 같이 말입니다.
템플릿으로는 이런 것을 처리할 수 없습니다. 드래그 앤 드롭(drag-and-drop) 캔버스는 절반의 사용자들을 겁먹게 만듭니다. 그래서 ByteChef Embedded 시리즈의 9번째 파트에서는 그들에게 채팅을 제공합니다. 사용자가 원하는 것을 설명하면, ByteChef의 워크플로우 에이전트가 사용자가 지켜보는 동안 계정 내에서 이를 구축해 줍니다.
개발자 입장에서는 하나의 React 컴포넌트, 하나의 콜백(callback), 그리고 버튼 하나면 충분합니다.
요약 (TL;DR)
EmbeddedWorkflowChat은 연결된 사용자의 JWT, 시작 제안(starter suggestions), 선택적 시스템 프롬프트(system prompt), 그리고onWorkflowReady콜백을 받습니다.- 마운트 시 빈 초안 워크플로우를 생성합니다. 에이전트는 이 초안을 턴(turn)마다 직접 편집합니다.
- 모든 턴은
POST /automation/workflows/{uuid}/copilot/chat에서 AG-UI를 통해 스트리밍됩니다. - 에이전트가 무언가가 필요할 때, 스레드 내에 카드(card)를 렌더링합니다. 이 카드는 질문, 옵션 선택기(option picker), 연결 선택기(connection picker), 또는 연결(Connect) 버튼일 수 있습니다.
- 첫 번째 턴 이후에는
onWorkflowReady(uuid)가 발생하며, 사용자를 에디터로 보낼 수 있습니다.
채팅이 적합한 경우 (Where chat fits)
임베디드 샘플 앱에서 **자동화(Automations)**를 열어보세요. 분할 버튼은 워크플로우를 시작하는 모든 방법을 나열합니다:
| 옵션 | 사용자는... | 다음 경우에 가장 좋음 |
|---|---|---|
| 템플릿에서 새로 만들기 (part 8) | 개발자가 만든 워크플로우를 선택합니다 | 일반적인 자동화가 무엇인지 알고 있을 때 |
| ... | ||
채팅은 가장 관대한 옵션입니다. 사용자들은 Slack의 액션(action) 이름이 _Send Channel Message_라는 것을 알 필요도 없고, 스케줄 트리거(schedule trigger)에 dayOfWeek 속성(property)이 있다는 것도 알 필요가 없습니다. 에이전트가 알고 있습니다. 사용자가 무슨 뜻인지 확실하지 않을 때 질문합니다. |
페이지 (The page)
**채팅에서 새로 만들기(New from Chat)**를 클릭하면 /automations/chat으로 이동합니다:
세 부분으로 구성되어 있습니다:
- 통합 개발자(integrator)가 자체 규칙을 추가하는 접힌 시스템 프롬프트 (System prompt, 선택 사항) 필드.
- 비어 있는 상태로 중앙에 배치된 채팅창.
- 제안 칩(Suggestion chips). 이 중 하나를 클릭하면 첫 메시지로 전송되므로, 빈 채팅창이 부담스럽지 않습니다.
대화가 시작되면 컴포저(composer)는 하단에 고정됩니다. 첫 번째 턴 이후에는 오른쪽 상단에 워크플로우 열기 (Open workflow) 버튼이 나타납니다.
컴포넌트 구현하기 (Mounting the component)
다음은 app/automations/chat/page.tsx를 간소화한 코드입니다:
export default function GenerateFromChatPage() {
const [jwtToken, setJwtToken] = useState<string | null>(null);
const [systemPrompt, setSystemPrompt] = useState('');
...
getToken()은 샘플의 백엔드에 연결된 사용자 JWT를 서명하도록 요청하며, 이는 파트 1과 동일합니다. 채팅창은 이 사용자를 대신하여 모든 작업을 수행하기 때문에 토큰을 기다립니다.
Props (속성)
| Prop | Type (유형) | What it does (기능) |
|---|---|---|
jwtToken | string (필수) | 연결된 사용자의 JWT를 Authorization: Bearer ...로 전송합니다. |
| ... |
이 컴포넌트는 부모의 높이를 채우므로, 부모에게 높이를 지정해야 합니다 (샘플에서는 h-screen 컬럼 내부에 min-h-0 flex-1을 사용합니다).
패키지가 아닌 소스 코드입니다
useConnectDialog나 EmbeddedWorkflowBuilder와 달리, 이 채팅창은 npm으로 배포되는 패키지가 아닙니다. front-end/src/components/embedded-workflow-chat/ 경로의 샘플 앱에 존재하며, 해당 폴더를 복사해야 합니다:
embedded-workflow-chat/
├── index.ts
├── EmbeddedWorkflowChat.tsx # 공개 컴포넌트
...
이는 의도된 것입니다. 채팅창은 제품에서 가장 눈에 띄는 부분이므로, 우리 것(개발사)처럼 보이지 않고 사용자의 제품처럼 보여야 합니다. Tailwind를 사용한 assistant-ui의 기본 요소 위에 구축되었기 때문에, 스타일을 변경하는 것은 일반적인 React 작업입니다.
의존성(Dependencies): ConnectDialog를 위해 @ag-ui/client, @assistant-ui/react, @assistant-ui/react-markdown, zustand, lucide-react, tailwind-merge, 그리고 @bytechef/embedded가 필요합니다.
마운트 시: 스켈레톤 워크플로우
에이전트는 워크플로우를 생성하고 대화 마지막에 전달하는 것이 아닙니다. 대신, 대화 과정 중에 실제 존재하는 워크플로우를 제자리에서 편집합니다. 따라서 사용자가 아무것도 입력하기 전에도 컴포넌트는 이를 생성합니다:
export const SKELETON_WORKFLOW_DEFINITION = JSON.stringify({
description: '',
inputs: [],
...
이 초안은 연결된 사용자(connected user)의 해당 환경 개인 프로젝트에 저장됩니다. 요청이 실행되는 동안, 컴포넌트는 '시작 중...'을 표시합니다. 만약 실패할 경우 (JWT 만료, 잘못된 baseUrl 등), 오류는 채팅창 대신 빨간색으로 표시됩니다.
이 UUID가 전체 대화를 고정(anchor)합니다. ref latch를 사용하면 React StrictMode 하에서도 단 하나의 스켈레톤만 생성되도록 보장합니다.
계획해야 할 한 가지: 사용자가 아무것도 입력하지 않고 떠나더라도, 방문할 때마다 초안이 생성됩니다. 이에 대해서는 'Hardening'에서 더 자세히 다룹니다.
각 턴(turn)마다: AG-UI 스트리밍
사용자가 메시지를 보내면, 컴포넌트는 AG-UI HttpAgent와 함께 대화를 게시합니다:
const agent = new HttpAgent({
agentId: 'workflow_editor',
url: `${baseUrl}/api/embedded/v1/automation/workflows/${workflowUuid}/copilot/chat`,
...
이 상태에서 서버는 시스템 프롬프트만 사용합니다. 나머지 모든 것은 자체적으로 재구성합니다 (에이전트가 할 수 있는 것과 없는 것(What the agent can and can't do) 참조).
| AG-UI 이벤트 | 사용자가 보는 것 |
|---|---|
TEXT_MESSAGE_START, _CONTENT, _END | 어시스턴트 메시지, 토큰 단위로 스트리밍됨 |
| ... |
에이전트가 도구를 호출할 때마다 서버는 열려 있는 텍스트 메시지를 닫기 때문에, 한 턴은 발생한 순서대로 읽힙니다:
| 원인 | 사용자가 읽는 내용 |
|---|---|
| 환경에 AI 제공업체가 없음 | 이 환경에는 AI 제공업체가 설정되어 있지 않아 채팅이 답변할 수 없습니다. |
| ... | |
| 어떤 것이든 _Copilot 실행 실패(The copilot run failed). 참조: ..._라고 읽습니다. 텍스트는 예외 메시지가 아니라 고정된 것이기 때문에, 예외는 테이블 이름, 내부 호스트 또는 키를 언급할 수 있습니다. 전체 원인은 동일한 실행 ID로 서버 로그에 기록됩니다. 사용자가 실패를 보고하면, 참조 번호를 요청하고 grep을 사용하여 검색하세요. |
사용자가 기대하는 컨트롤 기능
- 중지(Stop): 실행을 중단합니다.
- 수정(Edit): 사용자 메시지에 대해 되돌아가서 거기부터 다시 실행합니다.
- 새로고침(Refresh): 마지막 어시스턴트 메시지를 재생성합니다.
- 복사(Copy) 및 마크다운으로 내보내기(Export as Markdown): 어시스턴트 메시지 아래에 있습니다.
수정(Edit)과 새로고침(Refresh)은 화면뿐만 아니라 에이전트를 되돌립니다. 에이전트의 메모리는 서버에 저장되며, 사용자, 워크플로우 및 대화와 연결된 키를 가집니다. 매 실행 전에 ByteChef는 그 메모리를 양측이 동의한 마지막 사용자 메시지까지만 잘라냅니다. 서버는 이런 방식으로만 메시지를 제거합니다. 절대로 클라이언트가 말한 것을 그대로 받아들이지는 않습니다.
대화형 카드: 에이전트가 사용자를 필요로 할 때
텍스트 전용 에이전트는
kind | 카드 | 클릭 시 동작 |
|---|---|---|
ask-user-question | 객관식 질문, 한 번에 하나 (질문 1/3, 이전) | 답변은 "사용자가 선택: ..."으로 돌아갑니다. |
| ... | ||
| 세 가지 중 세 가지는 서버에서 질문을 브라우저로 전달하는 것 외에는 아무것도 하지 않습니다. 속성 선택기(property picker)는 조금 더 많은 작업을 수행합니다. 사용자가 사용할 수 있는 연결을 이용해 먼저 해당 속성의 실제 옵션들을 가져옵니다. |
에이전트가 카드를 표시하면, 그 에이전트의 차례는 끝납니다. 추측하거나 계속 진행하지 않습니다. 기다립니다. 사용자의 선택은 일반 메시지로 돌아가며, 이는 에이전트가 정확히 요청한 내용으로 다음 차례를 시작합니다. 답변된 카드들은 짧은 요약과 녹색 체크 표시(Picked: #standup)로 축소되어 스레드가 읽기 쉬운 상태를 유지하게 합니다.
대화 내에서 연결하기 (Connect, right in the conversation)
이 부분이 전체 과정이 하나로 합쳐지는 곳입니다. 사용자가 Slack 알림을 요청하지만 아직 Slack과 연결하지 않은 경우, 에이전트는 createConnection을 호출하고 채팅창에 Slack 연결이 표시됩니다. 이를 클릭하면:
- 사용자의 JWT를 사용하여
GET /api/embedded/v1/integrations를 호출하고,componentName이slack인 통합(integration)을 찾습니다. useConnectDialog를 통해 첫 번째 부분의 ConnectDialog를 엽니다.- 대화 상자의 포털(portal)을 관찰하며, 대화 상자가 닫힐 때 카드가 연결되었음을 표시합니다.
사용자는 채팅을 벗어나지 않고 통합(Integrations) 페이지에서와 동일한 OAuth 또는 API 키 흐름(API-key flow)을 거칩니다. 그런 다음 에이전트는 select-connection으로 새로운 연결을 선택합니다.
createConnection은 신호만 보냅니다. 에이전트는 자격 증명(credentials)을 절대 보고 저장하지 않으며, 도구 호출은 서버에 아무것도 생성하지 않습니다. 만약 앱이 사용자의 통합 목록에 없다면, 카드는 _Slack에 사용할 수 있는 통합이 없습니다_라고 표시합니다.
사용자 정의 규칙: 시스템 프롬프트 (system prompt)
사용자가 워크플로우가 무엇을 할지 결정합니다. 개발자가 어떻게 구축할지를 결정합니다.
좋은 시스템 프롬프트는 사용자가 결정할 필요가 없는 결정을 인코딩합니다:
- Slack 팀에 항상 알리고, 이메일로는 절대 하지 마세요.
- 워크플로우를 5단계 이하로 유지하세요.
- 사용자가 다른 시간대를 지정하지 않는 한 모든 일정에는 Europe/Berlin 시간대를 사용하세요.
- CRM 기록은 절대 삭제하지 말고, 상태 필드를 업데이트하세요.
ByteChef가 이를 처리하는 방식:
- 모든 턴(turn)을 읽습니다. 대화 중간에 변경해도 다음 턴이 이를 파악합니다.
- 서버에서 4,000자로 줄이고 제한(Trimmed and capped) 합니다.
- 핵심 규칙을 재정의할 수 없습니다. 이는 통합 애플리케이션의 추가 지침임을 표시하는 제목 아래에 추가되며, 빌드 규칙, 워크플로우 정의 계약 또는 안전 제약 조건과 충돌하지 않는 경우에만 적용됩니다. 사용자의 규칙이 에이전트를 안내합니다. 그것을 다른 것으로 바꿀 수는 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기


