INTEGRITY Dokumentace

Mluvte se svou znalostní bází

Tento tutoriál sestaví hlasového agenta, se kterým si můžete povídat a který nahlas odpovídá na základě vaší AI Search znalostní báze. Používá Cloudflare Agents @cloudflare/voice balíček pro hlasový pipeline a AI Search jako znalostní bázi agenta, zpřístupněnou jako nástroj pro vyhledávání, který volá model agenta.

Co vytvoříte: Hlasový agent, který přepíše vaši řeč, zavolá AI Search, aby načetl relevantní obsah z vaší indexované znalostní báze, vygeneruje podloženou odpověď a přečte vám ji nahlas.

Jak to funguje

@cloudflare/voice balíček přidává do Cloudflare Agent kompletní hlasový pipeline: převod řeči na text (STT), handler pro "kolo", ve kterém vytváříte odpověď, a převod textu na řeč (TTS). Pipeline běží v jediném Workeru podpořeném Durable Object a prohlížeč se k němu připojuje přes WebSocket.

Jedinou metodou, kterou napíšete, je onTurn(), který přijímá přepis promluvy uživatele a vrací text, jenž se má přečíst nahlas. Právě zde přichází ke slovu AI Search: spustíte jazykový model a dáte mu AI Search k dispozici jako nástroj pro vyhledávání. Model sám rozhoduje, kdy prohledat vaši znalostní bázi, svou odpověď opře o získané výsledky a vrátí text odpovědi, který pipeline přečte nahlas.

Mikrofon: uživatel mluví
ProhlížečMikrofon
Převod řeči na text (Workers AI) Workers AIPřevod řeči na text
Cloudflare Agents: obslužná rutina onTurn spustí model, který může volat AI Search jako jeden ze svých nástrojů.
Cloudflare AgentsonTurn(přepis)
Převod textu na řeč (Workers AI) Workers AIPřevod textu na řeč
Reproduktor: odpověď se přehraje
ProhlížečReproduktor

Předpoklady

  1. Zaregistrujte si účet Cloudflare ↗.
  2. Nainstalujte Node.js ↗.

Správce verzí Node.js

Použijte správce verzí Node, jako je Volta ↗ nebo nvm ↗ abyste se vyhnuli problémům s oprávněními a mohli měnit verze Node.js. Wrangler, o kterém se dozvíte dále v této příručce, vyžaduje verzi Node 16.17.0 nebo novější.

Také potřebujete instanci AI Search, která již obsahuje zaindexovaný obsah. Jde o znalostní bázi, se kterou budete komunikovat. Vytvoření instance a přidání obsahu najdete v Začínáme.

1. Vytvořte hlasového agenta

Vytvořte projekt Cloudflare Agents pomocí startovací šablony pro hlas, která obsahuje propojení Durable Object a klienta React:

npm create cloudflare@latest voice-knowledge-base -- --template cloudflare/agents-starter
cd voice-knowledge-base

Nainstalujte hlasový balíček:

npm i @cloudflare/voice

@cloudflare/voice balíček poskytuje withVoice mixin a poskytovatele Workers AI (WorkersAIFluxSTT a WorkersAITTS). Kompletní postup pro samotného hlasového agenta, včetně klienta v prohlížeči, najdete v Příklad hlasového agenta.

2. Přidejte vazbu AI Search

Přidejte AI Search binding do vašeho Konfigurační soubor Wrangler, spolu s vazbou Workers AI a Durable Object agenta. Nahraďte my-instance s názvem vaší instance.

{
	"name": "voice-knowledge-base",
	"main": "src/server.ts",
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"compatibility_flags": ["nodejs_compat"],
	"ai": {
		"binding": "AI",
		"remote": true
	},
	"ai_search": [
		{
			"binding": "AI_SEARCH",
			"instance_name": "my-instance",
			"remote": true
		}
	],
	"durable_objects": {
		"bindings": [
			{
				"name": "TalkToDocs",
				"class_name": "TalkToDocs"
			}
		]
	},
	"migrations": [
		{
			"tag": "v1",
			"new_sqlite_classes": ["TalkToDocs"]
		}
	]
}
name = "voice-knowledge-base"
main = "src/server.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
compatibility_flags = [ "nodejs_compat" ]

[ai]
binding = "AI"
remote = true

[[ai_search]]
binding = "AI_SEARCH"
instance_name = "my-instance"
remote = true

[[durable_objects.bindings]]
name = "TalkToDocs"
class_name = "TalkToDocs"

[[migrations]]
tag = "v1"
new_sqlite_classes = [ "TalkToDocs" ]

Znovu vygenerujte typy vazeb, aby env.AI a env.AI_SEARCH mají typy:

npx wrangler types

3. Odpovídejte na základě znalostní báze

Aktualizujte src/server.ts. Agenta sestavte pomocí withVoice mixin, nastavte poskytovatele STT a TTS, a v onTurn() spustit model Workers AI, který volá AI Search jako nástroj pro vyhledávání.

Model obdrží searchKnowledgeBase nástroj, který volá AI Search search() pro vyhledávání (retrieval). Nástroj volá ve chvíli, kdy potřebuje fakta z vaší znalostní báze, svou odpověď opírá o vrácené chunky a vy vygenerovaný text vrátíte, aby ho pipeline mohl vyslovit. Agent automaticky ukládá historii konverzace, takže můžete předat context.messages pro navazující otázky.

src/server.js
import { Agent, routeAgentRequest } from "agents";
import { withVoice, WorkersAIFluxSTT, WorkersAITTS } from "@cloudflare/voice";
import { generateText, tool, stepCountIs } from "ai";
import { createWorkersAI } from "workers-ai-provider";
import { z } from "zod";

const VoiceAgent = withVoice(Agent);

export class TalkToDocs extends VoiceAgent {
	// Workers AI powers speech-to-text and text-to-speech (no API keys needed).
	transcriber = new WorkersAIFluxSTT(this.env.AI);
	tts = new WorkersAITTS(this.env.AI);

	// Called each time the user finishes speaking. The agent's model generates
	// the reply, calling AI Search as a retrieval tool when it needs facts from
	// your knowledge base.
	async onTurn(transcript, context) {
		const workersai = createWorkersAI({ binding: this.env.AI });

		const result = await generateText({
			// Use a Workers AI model that supports function calling.
			model: workersai("@cf/zai-org/glm-5.2"),
			system:
				"You are a helpful voice assistant that answers from a Cloudflare AI Search knowledge base. " +
				"For questions about the product, call the searchKnowledgeBase tool first and answer using the results. " +
				"Skip the tool for greetings and small talk. Keep replies short and conversational.",
			messages: [
				...context.messages.map((message) => ({
					role: message.role,
					content: message.content,
				})),
				{ role: "user", content: transcript },
			],
			tools: {
				searchKnowledgeBase: tool({
					description:
						"Search the knowledge base for information to answer the user's question.",
					inputSchema: z.object({
						query: z.string().describe("A focused search query"),
					}),
					execute: async ({ query }) => {
						// search() runs retrieval only and returns the matching chunks.
						const res = await this.env.AI_SEARCH.search({
							query,
							ai_search_options: { retrieval: { max_num_results: 5 } },
						});
						return res.chunks.map((chunk) => chunk.text).join("\n\n");
					},
				}),
			},
			// Let the model call the tool, then answer from the results.
			stopWhen: stepCountIs(4),
		});

		// Return the generated answer for the pipeline to speak.
		return result.text;
	}
}

export default {
	async fetch(request, env) {
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
src/server.ts
import { Agent, routeAgentRequest } from "agents";
import {
	withVoice,
	WorkersAIFluxSTT,
	WorkersAITTS,
	type VoiceTurnContext,
} from "@cloudflare/voice";
import { generateText, tool, stepCountIs } from "ai";
import { createWorkersAI } from "workers-ai-provider";
import { z } from "zod";

const VoiceAgent = withVoice(Agent);

export class TalkToDocs extends VoiceAgent<Env> {
	// Workers AI powers speech-to-text and text-to-speech (no API keys needed).
	transcriber = new WorkersAIFluxSTT(this.env.AI);
	tts = new WorkersAITTS(this.env.AI);

	// Called each time the user finishes speaking. The agent's model generates
	// the reply, calling AI Search as a retrieval tool when it needs facts from
	// your knowledge base.
	async onTurn(transcript: string, context: VoiceTurnContext) {
		const workersai = createWorkersAI({ binding: this.env.AI });

		const result = await generateText({
			// Use a Workers AI model that supports function calling.
			model: workersai("@cf/zai-org/glm-5.2"),
			system:
				"You are a helpful voice assistant that answers from a Cloudflare AI Search knowledge base. " +
				"For questions about the product, call the searchKnowledgeBase tool first and answer using the results. " +
				"Skip the tool for greetings and small talk. Keep replies short and conversational.",
			messages: [
				...context.messages.map((message) => ({
					role: message.role as "user" | "assistant",
					content: message.content,
				})),
				{ role: "user" as const, content: transcript },
			],
			tools: {
				searchKnowledgeBase: tool({
					description:
						"Search the knowledge base for information to answer the user's question.",
					inputSchema: z.object({
						query: z.string().describe("A focused search query"),
					}),
					execute: async ({ query }) => {
						// search() runs retrieval only and returns the matching chunks.
						const res = await this.env.AI_SEARCH.search({
							query,
							ai_search_options: { retrieval: { max_num_results: 5 } },
						});
						return res.chunks.map((chunk) => chunk.text).join("\n\n");
					},
				}),
			},
			// Let the model call the tool, then answer from the results.
			stopWhen: stepCountIs(4),
		});

		// Return the generated answer for the pipeline to speak.
		return result.text;
	}
}

export default {
	async fetch(request: Request, env: Env) {
		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

Každá část obsahu vrácená search() obsahuje svou zdrojovou položku a skóre relevance, takže můžete zobrazit citace nebo zaznamenat, co agent načetl.

4. Sestavte klienta

Nahraďte src/client.tsx s komponentou React, která používá useVoiceAgent hook. Tento hook spravuje mikrofon, WebSocket připojení k vašemu agentovi, přehrávání zvuku a detekci přerušení, takže komponenta musí pouze vykreslit ovládací prvky. Nastavte agent k názvu třídy vašeho agenta, TalkToDocs.

src/client.tsx
import { useVoiceAgent } from "@cloudflare/voice/react";

function App() {
	// useVoiceAgent connects to your agent over WebSocket, captures the
	// microphone, plays the spoken response, and exposes the live call state.
	// `agent` matches your Durable Object class name.
	const {
		// Pipeline state: "idle" | "listening" | "thinking" | "speaking".
		status,
		// Finalized conversation turns (your speech and the agent's replies).
		transcript,
		// Live partial transcription of what you are currently saying.
		interimTranscript,
		// Whether the WebSocket connection to the agent is open.
		connected,
		startCall,
		endCall,
		toggleMute,
		isMuted,
	} = useVoiceAgent({ agent: "TalkToDocs" });

	const inCall = status !== "idle";

	return (
		<div>
			<h1>Talk to your knowledge base</h1>

			{/* Shows "thinking" while the agent searches the KB and generates a reply. */}
			<p>Status: {status}</p>

			{/* Toggle the call. Disabled until the agent connection is open. */}
			<button
				onClick={inCall ? endCall : startCall}
				disabled={!connected && !inCall}
			>
				{!connected ? "Connecting…" : inCall ? "End call" : "Start call"}
			</button>

			{/* Mute only applies once a call is active. */}
			{inCall && (
				<button onClick={toggleMute}>{isMuted ? "Unmute" : "Mute"}</button>
			)}

			{/* Lightweight loading state while the agent works on a reply. */}
			{status === "thinking" && <p>Thinking…</p>}

			{/* Live partial transcript, updated as you speak. */}
			{interimTranscript && (
				<p>
					<em>{interimTranscript}</em>
				</p>
			)}

			{/* Finalized turns from both you and the agent. */}
			{transcript.map((message, index) => (
				<p key={index}>
					<strong>{message.role}:</strong> {message.text}
				</p>
			))}
		</div>
	);
}

export default App;

Hook se stará o mikrofon a přehrávání zvuku, takže není potřeba tlačítko push-to-talk. Model detekuje, kdy jste domluvili, a spustí onTurn(), a automaticky přehraje namluvenou odpověď.

5. Spusťte to lokálně

Spusťte místní vývojový server:

npm run dev

Otevřete aplikaci v prohlížeči a vyberte Zahájit hovor a povolte přístup k mikrofonu, poté položte otázku, na kterou váš obsah umí odpovědět. Vaše slova se v reálném čase přepisují do textu a agent odpoví hlasem na základě vaší znalostní báze. status hodnota prochází listening, thinking, a speaking tak, jak funguje.

6. Nasaďte

Nasaďte svého agenta, aby byl dostupný na internetu:

npx wrangler deploy

Mluvte se svou znalostní bází s více lidmi

Tento tutoriál sestaví hlasového agenta pro jednoho uživatele. Pokud místo toho potřebujete, aby se znalostní bází bavilo najednou více lidí ve společné místnosti, použijte RealtimeKit pro vrstvu vícestranného zvuku a videa a ponechte tohoto hlasového agenta jako komponentu, která odpovídá pomocí AI Search. RealtimeKit poskytuje místnost schůzky a přepis řeči, ale nehostuje systém pro generování odpovědí.

Další kroky

Referenční dokumentace API pro hlasové agenty

Pipeline @cloudflare/voice, poskytovatelé a kontrakt onTurn.

Příklad hlasového agenta

Kompletní průvodce hlasovým agentem a jeho klientem pro prohlížeč.

Vazba Workers pro Search

Úplná referenční dokumentace k chatCompletions() a search() z Workeru.

Vlastní generační model

Použijte model třetí strany ke generování, zatímco vyhledávání zajišťuje AI Search.