اعرض الشخصية التعريفية لوكيلك في تطبيق Flutter مع Wixzel Voice
في نهاية هذا الدليل يعرض تطبيق Flutter لديك الشخصية التعريفية (mascot) لوكيلك، الوجه المتحرك الذي يملكه كل وكيل على Wixzel Voice، وهي تستمع وتفكر وتتكلم مع مكالمة حية. ترسمها حزمة wixzel_voice_flutter عبر AgentMascotView وتبقيها متزامنة مع المكالمة عبر MascotRealtimeLink؛ وتتولى حزمة wixzel_voice لـ Dart المكالمة. توقّع نحو ساعة، معظمها لتوصيل أداة التسجيل والمشغّل. تُحتسب الجلسة مثل مكالمة هاتفية على المحرك نفسه: $0.0491 لكل دقيقة اتصال على Gemini Live، أي نحو $0.20 لأربع دقائق، دون خط هاتف.
بقلم Aqeel Shamsudheen · نُشر في · حُدّث في
- المدة
- نحو ساعة
- المحرك
- gemini-live
- وقت المحرك
- $0.0491 لكل دقيقة متصلة

ما الذي تحتاجه قبل أن تبدأ؟
- وكيل على Wixzel Voice ومفتاح API مع رصيد، محفوظ على خادمك. أي وكيل يصلح: لكل وكيل شخصية تعريفية، بدءًا من الشخصية الخضراء الافتراضية.
- Flutter 3.27 أو أحدث.
- خادم تتحكم فيه ويمكنه استدعاء الـ API. يستخدم المثال Node.js وTypeScript SDK.
- إضافة تسجيل تبث PCM بدقة 16 بت، ويُفضَّل بتردد 8 kHz أحادي القناة مع إلغاء الصدى، ومشغّل يقبل PCM خامًا، مثل
recordوflutter_pcm_sound.
كيف تبني هذا على Wixzel Voice، خطوة بخطوة؟
01أضف الحزم
ترسم
wixzel_voice_flutterالشخصية التعريفية؛ وwixzel_voiceهي Dart SDK التي تُبنى عليها:pubspec.yamldependencies: wixzel_voice: ^0.8.0 wixzel_voice_flutter: ^0.1.0 http: ^1.2.0تعتمد الحزمة على
sensors_plus، لكي تتبع عينا الشخصية التعريفية ميلان الهاتف، وعلىvisibility_detector، لكي تتحرك فقط وهي ظاهرة على الشاشة. لا تسجّل الصوت ولا تشغّله.02أعطِ التطبيق الوكيل وجلسة، من خادمك
لا يوضع مفتاح API في التطبيق أبدًا. يجلب خادمك الوكيل ويُنشئ جلسة صالحة لدقيقة واحدة ولمرة واحدة، ويطلب التطبيق كليهما من خادمك:
server.mjsimport express from 'express'; import { WixzelVoice } from 'wixzel-voice'; const wixzel = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); const app = express(); const AGENT_ID = process.env.AGENT_ID; // Put your own sign-in check in front of both routes. app.get('/agent', async (req, res) => { res.json(await wixzel.agents.retrieve(AGENT_ID)); }); app.post('/session', async (req, res) => { res.json(await wixzel.realtime.createSession({ agent_id: AGENT_ID, max_duration_seconds: 300 })); }); app.listen(3000);يحمل JSON الوكيل
mascot.spec، وهو المواصفة الصغيرة التي تُرسم منها الشخصية التعريفية. إنشاء الجلسة مجاني؛ ويبدأ الاحتساب عند اتصال التطبيق.03ارسم الشخصية التعريفية
يقرأ
Agent.fromJsonما أرسله خادمك، ويرسمهAgentMascotView:agent_face.dartimport 'dart:convert'; import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; const backendUrl = 'https://api.example.com'; Future<Agent> loadAgent() async { final res = await http.get(Uri.parse('$backendUrl/agent')); return Agent.fromJson(jsonDecode(res.body) as Map<String, dynamic>); } Widget agentFace(Agent agent, MascotController controller) => AgentMascotView(agent: agent, controller: controller, size: 220); // In a list, draw one frame and run no animation: Widget agentRow(Agent agent) => AgentMascotView(agent: agent, size: 40, still: true);الوكيل الذي لا مواصفة له يحصل على المظهر الافتراضي. الشخصية التعريفية القريبة من الأسود أو الأبيض تقف على خلفية بلونها الخاص، حتى لا تختفي في شاشة داكنة أو فاتحة؛ أوقف ذلك عبر
stage: false. اللمسة تُشغّل تعبيرًا، ويشغّلMascotControllerالحركات بالاسم، مثلmascot.play('hello').04اربط الشخصية التعريفية بمكالمة
اتصل عبر
RealtimeConnectionمن Dart SDK وسلّمه إلىMascotRealtimeLink. يجعل الرابط الشخصية التعريفية تفكر أثناء اتصال المكالمة، وتلوّح بالتحية، وتستمع بينما يتحدث المستخدم، وتحرّك فمها مع صوت الوكيل:talk.dartimport 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; /// Your audio plugins, behind a small interface. abstract class PcmPlayer { void add(Int16List samples); // 16-bit PCM, 8 kHz mono void clear(); } class MascotCall { MascotCall(this.mascot, this.player); final MascotController mascot; final PcmPlayer player; RealtimeConnection? _conn; MascotRealtimeLink? _link; StreamSubscription<Int16List>? _mic; /// [micFrames] is 16-bit PCM at 8 kHz in 160-sample (20 ms) frames. Future<void> start(Stream<Int16List> micFrames) async { final res = await http.post(Uri.parse('https://api.example.com/session')); final session = RealtimeSession.fromJson(jsonDecode(res.body) as Map<String, dynamic>); final conn = RealtimeConnection.forSession(session); _conn = conn; _link = MascotRealtimeLink(mascot, conn); conn.events.listen((event) { switch (event) { case RealtimeAudio(:final mulaw): player.add(mulawToPcm16(mulaw)); // the mouth follows this audio through the link case RealtimeAudioClear(): player.clear(); // the user spoke over the agent case RealtimeEnded(): unawaited(end()); default: } }); await conn.ready; _mic = micFrames.listen((frame) { conn.sendAudio(pcm16ToMulaw(frame)); _link?.micAudio(frame); // it leans in while the user talks }); } Future<void> end() async { await _mic?.cancel(); await _link?.dispose(); // leaves the mascot idle; does not end the call await _conn?.end(); _mic = null; _link = null; _conn = null; } }اعرضها عبر
AgentMascotView(agent: agent, controller: mascot)، وهو المتحكم نفسه الذي تقوده المكالمة. تخلّص من المتحكم (dispose) عندما تُغلق الشاشة.فعّل إلغاء الصدى الخاص بالمنصة في أداة التسجيل، وإلا سمع الوكيل نفسه عبر مكبّر الصوت وقاطع نفسه.
05اجعل مزامنة الفم دقيقة، إن كان مشغّلك يجدول الصوت
افتراضيًا يفترض الرابط أن مشغّلك يبدأ كل مقطع بعد نحو 120 ms من وصوله. إذا كان مشغّلك يعرف بالضبط متى يُشغَّل المقطع، فأخبر الرابط بذلك بدلًا من التقدير:
exact_timing.dartimport 'dart:typed_data'; import 'package:wixzel_voice/wixzel_voice.dart'; import 'package:wixzel_voice_flutter/wixzel_voice_flutter.dart'; MascotRealtimeLink exactLink(MascotController mascot, RealtimeConnection conn) => MascotRealtimeLink(mascot, conn, estimatePlayback: false); // In your player, each time it schedules a chunk, on the player's own clock: void scheduled(MascotRealtimeLink link, Int16List pcm, double playAt, double now) { link.agentAudio(pcm, playAt: playAt, currentTime: now, sampleRate: 8000); }
كم يكلّف هذا البناء على Wixzel Voice؟
وقت المحرك لجلسات مدة كل منها 4 دقيقة على محرك gemini-live بسعر $0.0491 لكل دقيقة متصلة: نحو $0.20 للجلسة. لا توجد تكلفة اتصالات هاتفية، لأنه لا يوجد خط هاتفي.
| الجلسات شهريًا | الدقائق المتصلة | تكلفة المحرك شهريًا |
|---|---|---|
| 500 | 2,000 | $98.20 |
| 2,500 | 10,000 | $491 |
| 10,000 | 40,000 | $1,964 |
الحزم مجانية ومرخّصة بترخيص MIT. رسم الشخصية التعريفية مجاني؛ ولا تنفق الرصيد إلا الجلسة الحية، بسعر المحرك في الدقيقة، بالثانية، من رصيدك المسبق الدفع.
مسبق الدفع، دون اشتراك ودون خطة مجانية: أضِف رصيدًا بدءًا من $5 ويُستهلك مع الاستخدام. قدّر تكلفة استخدامك.
ما حدود Wixzel Voice التي تنطبق على هذا البناء؟
- لا تسجّل الحزمة الصوت ولا تشغّله. يوفّر تطبيقك كليهما عبر الإضافات التي يختارها.
- صوت بجودة الهاتف: G.711 µ-law بتردد 8 kHz في الاتجاهين.
- تُحتسب الجلسات ضمن حد المكالمات المتزامنة للحساب، خمس افتراضيًا.
- Flutter 3.27 أو أحدث. يحتاج الميلان إلى مقياس تسارع؛ وبدونه تتلفّت الشخصية التعريفية من تلقاء نفسها.
- تغيير مظهر الشخصية التعريفية استدعاء API من خادمك (
agents.updateMascot)، لا من التطبيق، لأنه يحتاج إلى مفتاح API الخاص بك.
الأسئلة الشائعة
كيف أعرض الشخصية التعريفية لوكيل Wixzel Voice في Flutter؟
wixzel_voice وwixzel_voice_flutter إلى pubspec، واقرأ الوكيل الذي جلبه خادمك عبر Agent.fromJson، وارسمه عبر AgentMascotView(agent: agent). مرّر MascotController للتحكم فيه من الكود.كيف تزامن الشخصية التعريفية حركة فمها مع الوكيل؟
MascotRealtimeLink إلى أحداث RealtimeConnection ويحرّك الفم مع صوت الوكيل، موقّتًا بتقديره الخاص لتأخير مشغّلك أو، مع estimatePlayback: false، بالأوقات التي يبلّغ عنها مشغّلك عبر agentAudio. ويضبط أيضًا الحالات: التفكير أثناء الاتصال، والاستماع بينما يتحدث المستخدم، والخمول عند انتهاء المكالمة.هل تبدو الشخصية التعريفية كما تبدو على الموقع؟
هل يوجد الشيء نفسه لتطبيقات الويب؟
mountMascot من wixzel-voice/mascot (الإصدار wixzel-voice 0.10.0 على npm) الشخصية التعريفية على canvas، ويبدأ face.connect(session) المكالمة الآنية مع ربط الشخصية التعريفية بها، بما في ذلك الميكروفون والتشغيل.للمزيد من القراءة
- حزم SDKالتوثيق
حزمتا Dart وTypeScript SDK وأدوات عرض الشخصية التعريفية.
- الشخصيات التعريفيةالتوثيق
المواصفة، وتغيير المظهر، وإنشاء مظهر من موقع.
- الوقت الفعلي: الويب والهاتف المحمولالتوثيق
إنشاء الجلسات، والبروتوكول، والاحتساب.
عن Wixzel Voice
Wixzel Voice هو API للذكاء الاصطناعي الصوتي لبناء وكلاء ذكاء اصطناعي يجرون مكالمات هاتفية حقيقية ويردّون عليها عبر SIP trunk الخاص بك، أو يتحدثون إلى الناس في تطبيقات الويب والجوال الخاصة بك. مفتاح API واحد ورصيد مدفوع مسبقًا واحد يغطيان كل محركات الصوت، مع فوترة بالثانية حسب الاستخدام الفعلي.
المزيد من الأدلة
Claude، في Claude Code أو claude.ai، يجري مكالمة حقيقية عبر SIP trunk الخاص بك ويقرأ لك النص المكتوب، مع طلب تأكيد قبل أي اتصال.
رقم Twilio الحالي لديك يجيب عليه موظف استقبال بالذكاء الاصطناعي يحجز المواعيد عبر الـ API، مبني بـ Node.js.
وكيلان هاتفيان، أحدهما يتحدث الهندية والآخر المالايالامية، على نماذج Sarvam المخصصة للغات الهند، يجيبان على المكالمات أو يجريانها عبر SIP trunk الخاص بك.
تطبيق Flutter يتحدث فيه المستخدمون إلى وكيلك الصوتي عبر WebSocket، مع بقاء مفتاح API على خادمك ودون أي رقم هاتف.
وكيل Gemini Live يتصل بهاتف حقيقي عبر SIP trunk الخاص بك من أربعة طلبات cURL، وسجل المكالمة مع النص المكتوب والتكلفة.
قائمة عملاء محتملين اتصل بهم وكيلك عبر SIP trunk الخاص بك، على دفعات بحجم حد التزامن لديك، مع قراءة حالة كل مكالمة ونصها المكتوب.
شخصية تعريفية ناطقة في زاوية موقعك يتصل بها الزوار من المتصفح، مقيّدة بنطاقاتك وحدودك اليومية، مع قائمة بكل متصل.
ابنِ أول وكيل صوتي لك

