Vite (JavaScript)

ElevenLabs AI 에이전트와 음성 대화를 지원하는 웹 애플리케이션을 만드는 방법을 알아보세요

이 튜토리얼에서는 ElevenLabs 에이전트와 상호작용할 수 있는 웹 클라이언트를 만드는 방법을 안내합니다. 실시간 음성 대화를 구현하여 사용자가 듣고, 이해하고, 음성 합성으로 자연스럽게 응답하는 AI 에이전트와 대화하는 방법을 배웁니다.

React/Next.js로 개발하고 싶으신가요? Next.js 가이드를 확인하세요.

필요한 항목

  1. 이 가이드에 따라 생성한 ElevenLabs 에이전트
  2. 로컬 시스템에 설치된 npm
  3. JavaScript 기초 지식

프로젝트 설정

1

프로젝트 디렉터리 생성

터미널을 열고 프로젝트용 새 디렉터리를 만듭니다.

mkdir elevenlabs-conversational-ai
cd elevenlabs-conversational-ai
2

npm 초기화 및 종속성 설치

새 npm 프로젝트를 초기화하고 필요한 패키지를 설치합니다.

npm init -y
npm install vite @elevenlabs/client
3

기본 프로젝트 구조 설정

package.json에 다음을 추가합니다.

package.json
{
"scripts": {
...
"dev:frontend": "vite"
}
}

다음 파일 구조를 만듭니다.

elevenlabs-conversational-ai/
├── index.html
├── script.js
├── package-lock.json
├── package.json
└── node_modules

음성 채팅 인터페이스 구현

1

HTML 인터페이스 생성

index.html에서 간단한 사용자 인터페이스를 설정합니다.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ElevenLabs Agents</title>
</head>
<body style="font-family: Arial, sans-serif; text-align: center; padding: 50px;">
<h1>ElevenLabs Agents</h1>
<div style="margin-bottom: 20px;">
<button id="startButton" style="padding: 10px 20px; margin: 5px;">Start Conversation</button>
<button id="stopButton" style="padding: 10px 20px; margin: 5px;" disabled>Stop Conversation</button>
</div>
<div style="font-size: 18px;">
<p>Status: <span id="connectionStatus">Disconnected</span></p>
<p>Agent is <span id="agentStatus">listening</span></p>
</div>
<script type="module" src="../images/script.js"></script>
</body>
</html>
2

대화 로직 구현

script.js에서 기능을 구현합니다.

script.js
import { Conversation } from '@elevenlabs/client';
const startButton = document.getElementById('startButton');
const stopButton = document.getElementById('stopButton');
const connectionStatus = document.getElementById('connectionStatus');
const agentStatus = document.getElementById('agentStatus');
let conversation;
async function startConversation() {
try {
// Request microphone permission
await navigator.mediaDevices.getUserMedia({ audio: true });
// Start the conversation
conversation = await Conversation.startSession({
agentId: 'YOUR_AGENT_ID', // Replace with your agent ID
onConnect: () => {
connectionStatus.textContent = 'Connected';
startButton.disabled = true;
stopButton.disabled = false;
},
onDisconnect: () => {
connectionStatus.textContent = 'Disconnected';
startButton.disabled = false;
stopButton.disabled = true;
},
onError: (error) => {
console.error('Error:', error);
},
onModeChange: (mode) => {
agentStatus.textContent = mode.mode === 'speaking' ? 'speaking' : 'listening';
},
});
} catch (error) {
console.error('Failed to start conversation:', error);
}
}
async function stopConversation() {
if (conversation) {
await conversation.endSession();
conversation = null;
}
}
startButton.addEventListener('click', startConversation);
stopButton.addEventListener('click', stopConversation);
3

프런트엔드 서버 시작

npm run dev:frontend
'YOUR_AGENT_ID'를 ElevenLabs의 실제 에이전트 ID로 바꿔야 합니다.

이 인증 단계는 비공개 에이전트에만 필요합니다. 공개 에이전트를 사용하는 경우 이 섹션을 건너뛰고 startSession 호출에서 agentId를 직접 사용할 수 있습니다.

1

환경 변수 생성

프로젝트 루트에 .env 파일을 생성합니다.

.env
ELEVENLABS_API_KEY=your-api-key-here
AGENT_ID=your-agent-id-here

민감한 자격 증명을 실수로 커밋하지 않도록 .env를 .gitignore 파일에 추가하세요.

2

백엔드 설정

  1. 추가 종속성을 설치합니다.
npm install express cors dotenv
  1. backend라는 새 폴더를 만듭니다.
elevenlabs-conversational-ai/
├── backend
...
3

서버 생성

backend/server.js
require("dotenv").config();
const express = require("express");
const cors = require("cors");
const app = express();
app.use(cors());
app.use(express.json());
const PORT = process.env.PORT || 3001;
app.get("/api/get-signed-url", async (req, res) => {
try {
const response = await fetch(
`https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=${process.env.AGENT_ID}`,
{
headers: {
"xi-api-key": process.env.ELEVENLABS_API_KEY,
},
}
);
if (!response.ok) {
throw new Error("Failed to get signed URL");
}
const data = await response.json();
res.json({ signedUrl: data.signed_url });
} catch (error) {
console.error("Error:", error);
res.status(500).json({ error: "Failed to generate signed URL" });
}
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
4

클라이언트 코드 업데이트

서명된 URL을 가져와 사용하도록 script.js를 수정합니다.

script.js
// ... existing imports and variables ...
async function getSignedUrl() {
const response = await fetch('http://localhost:3001/api/get-signed-url');
if (!response.ok) {
throw new Error(`Failed to get signed url: ${response.statusText}`);
}
const { signedUrl } = await response.json();
return signedUrl;
}
async function startConversation() {
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
const signedUrl = await getSignedUrl();
conversation = await Conversation.startSession({
signedUrl,
// agentId has been removed...
onConnect: () => {
connectionStatus.textContent = 'Connected';
startButton.disabled = true;
stopButton.disabled = false;
},
onDisconnect: () => {
connectionStatus.textContent = 'Disconnected';
startButton.disabled = false;
stopButton.disabled = true;
},
onError: (error) => {
console.error('Error:', error);
},
onModeChange: (mode) => {
agentStatus.textContent = mode.mode === 'speaking' ? 'speaking' : 'listening';
},
});
} catch (error) {
console.error('Failed to start conversation:', error);
}
}
// ... rest of the code ...

서명된 URL은 짧은 시간 후 만료됩니다. 하지만 만료 전에 시작된 모든 대화는 중단 없이 계속됩니다. 프로덕션 환경에서는 새 대화를 시작하기 위한 적절한 오류 처리 및 URL 새로고침 로직을 구현하세요.

5

package.json 업데이트

package.json
{
"scripts": {
...
"dev:backend": "node backend/server.js",
"dev": "npm run dev:frontend & npm run dev:backend"
}
}
6

애플리케이션 실행

다음 명령으로 애플리케이션을 시작합니다.

npm run dev

다음 단계

기본 구현을 완료했으므로 다음을 할 수 있습니다.

  1. 음성 활동에 대한 시각적 피드백 추가
  2. 오류 처리 및 재시도 로직 구현
  3. 채팅 기록 표시 추가
  4. 브랜드에 맞게 UI 맞춤 설정

고급 기능 및 맞춤 설정 옵션은 @elevenlabs/client 패키지를 확인하세요.