تخطَّ إلى المحتوى
دليل

اعرض الشخصية التعريفية لوكيلك في تطبيق Flutter مع Wixzel Voice

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

بقلم · نُشر في · حُدّث في

المدة
نحو ساعة
المحرك
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، خطوة بخطوة؟

  1. 01أضف الحزم

    ترسم wixzel_voice_flutter الشخصية التعريفية؛ وwixzel_voice هي Dart SDK التي تُبنى عليها:

    pubspec.yaml
    dependencies:
      wixzel_voice: ^0.8.0
      wixzel_voice_flutter: ^0.1.0
      http: ^1.2.0

    تعتمد الحزمة على sensors_plus، لكي تتبع عينا الشخصية التعريفية ميلان الهاتف، وعلى visibility_detector، لكي تتحرك فقط وهي ظاهرة على الشاشة. لا تسجّل الصوت ولا تشغّله.

  2. 02أعطِ التطبيق الوكيل وجلسة، من خادمك

    لا يوضع مفتاح API في التطبيق أبدًا. يجلب خادمك الوكيل ويُنشئ جلسة صالحة لدقيقة واحدة ولمرة واحدة، ويطلب التطبيق كليهما من خادمك:

    server.mjs
    import 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، وهو المواصفة الصغيرة التي تُرسم منها الشخصية التعريفية. إنشاء الجلسة مجاني؛ ويبدأ الاحتساب عند اتصال التطبيق.

  3. 03ارسم الشخصية التعريفية

    يقرأ Agent.fromJson ما أرسله خادمك، ويرسمه AgentMascotView:

    agent_face.dart
    import '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').

  4. 04اربط الشخصية التعريفية بمكالمة

    اتصل عبر RealtimeConnection من Dart SDK وسلّمه إلى MascotRealtimeLink. يجعل الرابط الشخصية التعريفية تفكر أثناء اتصال المكالمة، وتلوّح بالتحية، وتستمع بينما يتحدث المستخدم، وتحرّك فمها مع صوت الوكيل:

    talk.dart
    import '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) عندما تُغلق الشاشة.

    فعّل إلغاء الصدى الخاص بالمنصة في أداة التسجيل، وإلا سمع الوكيل نفسه عبر مكبّر الصوت وقاطع نفسه.

  5. 05اجعل مزامنة الفم دقيقة، إن كان مشغّلك يجدول الصوت

    افتراضيًا يفترض الرابط أن مشغّلك يبدأ كل مقطع بعد نحو 120 ms من وصوله. إذا كان مشغّلك يعرف بالضبط متى يُشغَّل المقطع، فأخبر الرابط بذلك بدلًا من التقدير:

    exact_timing.dart
    import '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 للجلسة. لا توجد تكلفة اتصالات هاتفية، لأنه لا يوجد خط هاتفي.

تكلفة المحرك الشهرية لما يبنيه هذا الدليل
الجلسات شهريًاالدقائق المتصلةتكلفة المحرك شهريًا
5002,000$98.20
2,50010,000$491
10,00040,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. ويضبط أيضًا الحالات: التفكير أثناء الاتصال، والاستماع بينما يتحدث المستخدم، والخمول عند انتهاء المكالمة.

هل تبدو الشخصية التعريفية كما تبدو على الموقع؟

نعم. بيئة تشغيل Flutter منقولة عن بيئة الويب، وتُلزم الاختبارات الاثنتين بالأرقام نفسها، لذا تبدو المواصفة وتتحرك بالطريقة نفسها في تطبيقك، وفي تطبيق Wixzel Voice، وعلى رابط المشاركة، وفي widget الموقع.

هل يوجد الشيء نفسه لتطبيقات الويب؟

نعم. ترسم mountMascot من wixzel-voice/mascot (الإصدار wixzel-voice 0.10.0 على npm) الشخصية التعريفية على canvas، ويبدأ face.connect(session) المكالمة الآنية مع ربط الشخصية التعريفية بها، بما في ذلك الميكروفون والتشغيل.

للمزيد من القراءة

واجهات API للاتصالات الهاتفية القائمة على الوكلاء

عن Wixzel Voice

Wixzel Voice هو API للذكاء الاصطناعي الصوتي لبناء وكلاء ذكاء اصطناعي يجرون مكالمات هاتفية حقيقية ويردّون عليها عبر SIP trunk الخاص بك، أو يتحدثون إلى الناس في تطبيقات الويب والجوال الخاصة بك. مفتاح API واحد ورصيد مدفوع مسبقًا واحد يغطيان كل محركات الصوت، مع فوترة بالثانية حسب الاستخدام الفعلي.

أعطِ Claude خط هاتف حقيقيًا عبر خادم MCP من Wixzel Voice

Claude، في Claude Code أو claude.ai، يجري مكالمة حقيقية عبر SIP trunk الخاص بك ويقرأ لك النص المكتوب، مع طلب تأكيد قبل أي اتصال.

بناء موظف استقبال بالذكاء الاصطناعي على Twilio في Node.js مع Wixzel Voice

رقم Twilio الحالي لديك يجيب عليه موظف استقبال بالذكاء الاصطناعي يحجز المواعيد عبر الـ API، مبني بـ Node.js.

وكيل هاتفي بالهندية والمالايالامية على محرك sarvam من Wixzel Voice

وكيلان هاتفيان، أحدهما يتحدث الهندية والآخر المالايالامية، على نماذج Sarvam المخصصة للغات الهند، يجيبان على المكالمات أو يجريانها عبر SIP trunk الخاص بك.

أضف وكيلًا صوتيًا إلى Flutter عبر WebSocket مع Wixzel Voice

تطبيق Flutter يتحدث فيه المستخدمون إلى وكيلك الصوتي عبر WebSocket، مع بقاء مفتاح API على خادمك ودون أي رقم هاتف.

Gemini Live في مكالمة هاتفية حقيقية عبر Wixzel Voice API

وكيل Gemini Live يتصل بهاتف حقيقي عبر SIP trunk الخاص بك من أربعة طلبات cURL، وسجل المكالمة مع النص المكتوب والتكلفة.

تشغيل حملة لتأهيل العملاء المحتملين بالذكاء الاصطناعي مع Wixzel Voice

قائمة عملاء محتملين اتصل بهم وكيلك عبر SIP trunk الخاص بك، على دفعات بحجم حد التزامن لديك، مع قراءة حالة كل مكالمة ونصها المكتوب.

أضف شخصية تعريفية ناطقة بالذكاء الاصطناعي إلى موقعك مع Wixzel Voice

شخصية تعريفية ناطقة في زاوية موقعك يتصل بها الزوار من المتصفح، مقيّدة بنطاقاتك وحدودك اليومية، مع قائمة بكل متصل.