返回首頁Dialogflow

Dialogflow 行動開發整合:Android 與 Flutter 完整教學

21 min 分鐘閱讀
#Dialogflow#Android#Flutter#行動開發#App 整合

Dialogflow 行動開發整合:Android 與 Flutter 完整教學

Dialogflow 行動開發整合:Android 與 Flutter 完整教學

想在你的 App 中加入 AI 對話功能嗎?

不論是客服助理、語音助手還是互動式導覽,Dialogflow 都可以幫你實現。這篇文章教你如何在 Android 和 Flutter 中整合 Dialogflow,打造具備自然語言理解能力的行動應用。

如果你還不熟悉 Dialogflow,建議先閱讀 Dialogflow 完整指南


行動 App 整合方式比較

在 App 中整合 Dialogflow 有兩種主要方式,各有優缺點。

直接 API 呼叫

App 直接呼叫 Dialogflow API:

App → Dialogflow API → 回應

優點

  • 架構簡單
  • 延遲較低
  • 不需要自建後端

缺點

  • API 金鑰暴露在 App 中(資安風險)
  • 無法加入額外商業邏輯
  • 難以記錄對話歷史

透過後端轉發

App 透過你的後端呼叫 Dialogflow:

App → 你的後端 → Dialogflow API → 你的後端 → App

優點

  • API 金鑰安全保存在後端
  • 可以加入商業邏輯(驗證、記錄、過濾)
  • 容易整合其他系統

缺點

  • 需要建置和維護後端
  • 延遲稍高
  • 架構較複雜

優缺點分析與選擇建議

方式適用場景
直接 APIPOC 驗證、學習用途、內部工具
後端轉發正式產品、需要資安、企業應用

建議:正式上線的 App 一律使用後端轉發方式。


Android Studio 整合

專案設定

Step 1:新增依賴套件

app/build.gradle 中加入:

dependencies {
    implementation 'com.google.cloud:google-cloud-dialogflow:4.0.0'
    implementation 'io.grpc:grpc-okhttp:1.56.1'
    implementation 'com.google.auth:google-auth-library-oauth2-http:1.19.0'
}

Step 2:設定網路權限

AndroidManifest.xml 中加入:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

Gradle 依賴套件

完整的 build.gradle 設定:

android {
    compileSdk 34

    defaultConfig {
        minSdk 24
        targetSdk 34
    }

    packagingOptions {
        exclude 'META-INF/INDEX.LIST'
        exclude 'META-INF/DEPENDENCIES'
    }
}

dependencies {
    implementation 'com.google.cloud:google-cloud-dialogflow:4.0.0'
    implementation 'io.grpc:grpc-okhttp:1.56.1'
    implementation 'io.grpc:grpc-stub:1.56.1'
}

Service Account 設定

Step 1:取得金鑰檔案

  1. 前往 Google Cloud Console > IAM > Service Accounts
  2. 建立或選擇服務帳戶
  3. 下載 JSON 金鑰檔案

Step 2:放置金鑰(開發用)

將金鑰檔案放到 app/src/main/res/raw/credentials.json

注意:這只適合開發測試。正式環境請使用後端轉發。

DetectIntent API 呼叫

建立 Dialogflow 客戶端類別:

class DialogflowClient(context: Context) {
    private val sessionsClient: SessionsClient
    private val session: SessionName
    private val projectId = "your-project-id"
    private val sessionId = UUID.randomUUID().toString()

    init {
        // 載入憑證
        val stream = context.resources.openRawResource(R.raw.credentials)
        val credentials = GoogleCredentials.fromStream(stream)
            .createScoped(listOf("https://www.googleapis.com/auth/cloud-platform"))

        val settings = SessionsSettings.newBuilder()
            .setCredentialsProvider { credentials }
            .build()

        sessionsClient = SessionsClient.create(settings)
        session = SessionName.of(projectId, sessionId)
    }

    suspend fun detectIntent(text: String): String {
        return withContext(Dispatchers.IO) {
            val textInput = TextInput.newBuilder()
                .setText(text)
                .setLanguageCode("zh-TW")
                .build()

            val queryInput = QueryInput.newBuilder()
                .setText(textInput)
                .build()

            val request = DetectIntentRequest.newBuilder()
                .setSession(session.toString())
                .setQueryInput(queryInput)
                .build()

            val response = sessionsClient.detectIntent(request)
            response.queryResult.fulfillmentText
        }
    }

    fun close() {
        sessionsClient.close()
    }
}

範例程式碼

完整的 Activity 範例:

class ChatActivity : AppCompatActivity() {
    private lateinit var dialogflowClient: DialogflowClient
    private lateinit var messageAdapter: MessageAdapter
    private val messages = mutableListOf<Message>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat)

        dialogflowClient = DialogflowClient(this)
        messageAdapter = MessageAdapter(messages)
        recyclerView.adapter = messageAdapter

        sendButton.setOnClickListener {
            val text = inputEditText.text.toString()
            if (text.isNotEmpty()) {
                sendMessage(text)
                inputEditText.text.clear()
            }
        }
    }

    private fun sendMessage(text: String) {
        // 顯示使用者訊息
        messages.add(Message(text, isUser = true))
        messageAdapter.notifyItemInserted(messages.size - 1)

        // 呼叫 Dialogflow
        lifecycleScope.launch {
            try {
                val response = dialogflowClient.detectIntent(text)
                messages.add(Message(response, isUser = false))
                messageAdapter.notifyItemInserted(messages.size - 1)
                recyclerView.scrollToPosition(messages.size - 1)
            } catch (e: Exception) {
                messages.add(Message("抱歉,發生錯誤", isUser = false))
                messageAdapter.notifyItemInserted(messages.size - 1)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        dialogflowClient.close()
    }
}

Flutter 整合

套件選擇

Flutter 有幾個 Dialogflow 相關套件:

套件說明維護狀態
dialogflow_grpc官方 gRPC 協定活躍
flutter_dialogflow社群套件較少更新
dialogflow_flutter簡化版本較少更新

建議:使用 dialogflow_grpc 或直接用 HTTP API。

跨平台實作

Step 1:新增依賴

pubspec.yaml 中:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0
  uuid: ^4.0.0

Step 2:建立 API 服務

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:uuid/uuid.dart';

class DialogflowService {
  final String projectId;
  final String accessToken; // 從後端取得
  final String sessionId = Uuid().v4();

  DialogflowService({required this.projectId, required this.accessToken});

  Future<String> detectIntent(String text) async {
    final url = Uri.parse(
      'https://dialogflow.googleapis.com/v2/projects/$projectId/agent/sessions/$sessionId:detectIntent'
    );

    final response = await http.post(
      url,
      headers: {
        'Authorization': 'Bearer $accessToken',
        'Content-Type': 'application/json',
      },
      body: jsonEncode({
        'queryInput': {
          'text': {
            'text': text,
            'languageCode': 'zh-TW',
          },
        },
      }),
    );

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      return data['queryResult']['fulfillmentText'];
    } else {
      throw Exception('Dialogflow API 錯誤: ${response.statusCode}');
    }
  }
}

範例 Widget

class ChatScreen extends StatefulWidget {
  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<ChatMessage> _messages = [];
  late DialogflowService _dialogflow;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _dialogflow = DialogflowService(
      projectId: 'your-project-id',
      accessToken: 'your-access-token', // 實際應從後端取得
    );
  }

  void _sendMessage() async {
    final text = _controller.text.trim();
    if (text.isEmpty) return;

    setState(() {
      _messages.add(ChatMessage(text: text, isUser: true));
      _isLoading = true;
    });
    _controller.clear();

    try {
      final response = await _dialogflow.detectIntent(text);
      setState(() {
        _messages.add(ChatMessage(text: response, isUser: false));
      });
    } catch (e) {
      setState(() {
        _messages.add(ChatMessage(text: '抱歉,發生錯誤', isUser: false));
      });
    } finally {
      setState(() => _isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AI 助理')),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: _messages.length,
              itemBuilder: (context, index) {
                final message = _messages[index];
                return ChatBubble(
                  text: message.text,
                  isUser: message.isUser,
                );
              },
            ),
          ),
          if (_isLoading) LinearProgressIndicator(),
          _buildInputArea(),
        ],
      ),
    );
  }

  Widget _buildInputArea() {
    return Container(
      padding: EdgeInsets.all(8),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _controller,
              decoration: InputDecoration(hintText: '輸入訊息...'),
              onSubmitted: (_) => _sendMessage(),
            ),
          ),
          IconButton(
            icon: Icon(Icons.send),
            onPressed: _sendMessage,
          ),
        ],
      ),
    );
  }
}

狀態管理整合

使用 Provider 管理對話狀態:

class ChatProvider extends ChangeNotifier {
  final List<ChatMessage> _messages = [];
  bool _isLoading = false;

  List<ChatMessage> get messages => _messages;
  bool get isLoading => _isLoading;

  Future<void> sendMessage(String text) async {
    _messages.add(ChatMessage(text: text, isUser: true));
    _isLoading = true;
    notifyListeners();

    try {
      final response = await _dialogflow.detectIntent(text);
      _messages.add(ChatMessage(text: response, isUser: false));
    } catch (e) {
      _messages.add(ChatMessage(text: '發生錯誤', isUser: false));
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}

語音助理功能

讓 App 支援語音輸入和輸出。

Speech-to-Text 整合

Android(使用 SpeechRecognizer)

class VoiceInputManager(private val context: Context) {
    private var speechRecognizer: SpeechRecognizer? = null
    private var onResult: ((String) -> Unit)? = null

    fun startListening(onResult: (String) -> Unit) {
        this.onResult = onResult

        speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context)
        speechRecognizer?.setRecognitionListener(object : RecognitionListener {
            override fun onResults(results: Bundle?) {
                val matches = results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
                matches?.firstOrNull()?.let { onResult(it) }
            }

            override fun onError(error: Int) {
                // 處理錯誤
            }

            // 其他必要的 override 方法...
        })

        val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
            putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
            putExtra(RecognizerIntent.EXTRA_LANGUAGE, "zh-TW")
        }

        speechRecognizer?.startListening(intent)
    }

    fun stopListening() {
        speechRecognizer?.stopListening()
        speechRecognizer?.destroy()
    }
}

Flutter(使用 speech_to_text)

import 'package:speech_to_text/speech_to_text.dart' as stt;

class VoiceInput {
  final stt.SpeechToText _speech = stt.SpeechToText();
  bool _isListening = false;

  Future<void> initialize() async {
    await _speech.initialize();
  }

  void startListening(Function(String) onResult) {
    _speech.listen(
      onResult: (result) {
        if (result.finalResult) {
          onResult(result.recognizedWords);
        }
      },
      localeId: 'zh_TW',
    );
    _isListening = true;
  }

  void stopListening() {
    _speech.stop();
    _isListening = false;
  }
}

Text-to-Speech 回放

import 'package:flutter_tts/flutter_tts.dart';

class VoiceOutput {
  final FlutterTts _tts = FlutterTts();

  Future<void> initialize() async {
    await _tts.setLanguage('zh-TW');
    await _tts.setSpeechRate(0.5);
  }

  Future<void> speak(String text) async {
    await _tts.speak(text);
  }

  Future<void> stop() async {
    await _tts.stop();
  }
}

持續對話模式

實現類似 Siri 的持續對話體驗:

class ContinuousDialogue {
  final VoiceInput _voiceInput;
  final VoiceOutput _voiceOutput;
  final DialogflowService _dialogflow;

  bool _isActive = false;

  void startConversation() async {
    _isActive = true;

    while (_isActive) {
      // 語音輸入
      final userText = await _listenForInput();
      if (userText == '結束對話') {
        _isActive = false;
        break;
      }

      // 呼叫 Dialogflow
      final response = await _dialogflow.detectIntent(userText);

      // 語音輸出
      await _voiceOutput.speak(response);

      // 短暫等待後繼續監聽
      await Future.delayed(Duration(milliseconds: 500));
    }
  }
}

安全性考量

行動 App 的安全性尤其重要,API 金鑰一旦外洩就無法收回。

API 金鑰保護策略

絕對不要做的事

// ❌ 把金鑰寫死在程式碼中
val apiKey = "AIzaSyXXXXXXXXXXXXXXX"

建議做法

1. 使用後端 Proxy(推薦)

App → 你的後端(驗證 + 呼叫 Dialogflow)→ App

後端保存 API 金鑰,App 只需要跟後端通訊。

2. 使用 Firebase Remote Config

val remoteConfig = Firebase.remoteConfig
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val apiKey = remoteConfig.getString("dialogflow_api_key")
    }
}

3. 使用 Android Keystore

val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
// 安全儲存和讀取金鑰

使用者認證整合

確保只有登入的使用者才能使用 AI 功能:

class SecureDialogflowClient(private val authService: AuthService) {

    suspend fun detectIntent(text: String): String {
        // 確認使用者已登入
        val user = authService.currentUser
            ?: throw UnauthorizedException("請先登入")

        // 取得存取權杖
        val token = authService.getIdToken()

        // 呼叫你的後端(不是直接呼叫 Dialogflow)
        return apiService.chat(
            token = token,
            text = text
        )
    }
}

敏感資料處理

不要記錄敏感對話

// ❌ 記錄完整對話
Log.d("Chat", "User said: $userInput")

// ✓ 只記錄必要資訊
Log.d("Chat", "User sent message, length: ${userInput.length}")

清除本地對話歷史

fun clearChatHistory() {
    // 清除記憶體
    messages.clear()

    // 清除本地儲存
    sharedPreferences.edit().remove("chat_history").apply()
}

擔心 API 金鑰安全?行動 App 的資安問題很容易被忽略,一旦出事就很麻煩。預約架構諮詢,讓我們幫你設計安全的整合架構。


發布注意事項

App Store / Play Store 審核

隱私政策要求

如果 App 會收集對話內容,需要在隱私政策中說明:

  • 收集什麼資料
  • 資料如何使用
  • 資料保存多久
  • 使用者如何刪除資料

權限說明

如果使用麥克風權限,需要說明為什麼需要:

  • 「用於語音輸入,讓您可以用說的與 AI 助理對話」

效能優化

減少啟動時間

// 懶載入 Dialogflow 客戶端
private val dialogflowClient by lazy {
    DialogflowClient(applicationContext)
}

背景處理

// 在背景執行緒處理
viewModelScope.launch(Dispatchers.IO) {
    val response = dialogflowClient.detectIntent(text)
    withContext(Dispatchers.Main) {
        updateUI(response)
    }
}

離線處理

當沒有網路時的處理:

suspend fun detectIntent(text: String): String {
    if (!isNetworkAvailable()) {
        return "目前沒有網路連線,請稍後再試。"
    }

    return try {
        dialogflowClient.detectIntent(text)
    } catch (e: IOException) {
        "網路連線不穩定,請稍後再試。"
    }
}

更多 API 整合細節,請參考 Dialogflow Fulfillment 與 API 整合教學。Intent 設計請參考 Dialogflow Intent 與 Context 教學。費用估算請參考 Dialogflow 費用完整解析

常見問題 FAQ

Q1: 行動 App 直接呼叫 Dialogflow API 會不會把 service account key 暴露?

會,這是最大的安全風險。行動 App 是 不可信環境——用戶可反編譯 APK/IPA 取得打包在 app 裡的 secret。錯誤做法(常見):(1) 把 service account JSON key 放在 APK 的 assets 裡;(2) 把 API key 寫在 Kotlin/Swift code 裡(hardcode);(3) 用 Firebase 存 service account key 讓 client 讀取。這些做法 100% 會洩漏,只是時間問題。正確做法:(1) Backend Proxy 架構——app → 你的後端 → Dialogflow API。後端持 service account key,app 只和你的後端通訊並用 user auth(Firebase Auth、JWT token 等);(2) 短期 Token 機制——後端幫 app 換取短期 token(5–15 分鐘 expire),app 用這個 token 直接呼叫 Dialogflow;(3) Workload Identity Federation(2025 年推薦)——如果是 GCP 生態,用 WIF 讓 app 用 Firebase Auth token 直接換 GCP credentials。實作重點(Backend Proxy,Node.js):後端建立受 authenticateUser middleware 保護的 POST /api/chat route,使用 dialogflow.SessionsClientdetectIntent({ session, queryInput: { text: { text, languageCode: 'zh-TW' }}}),把結果的 queryResult.fulfillmentText 回傳 app。額外保護:(1) Rate limiting(每 user 每分鐘 60 次);(2) input validation(防 prompt injection);(3) request 簽章(防中間人篡改);(4) 送 Dialogflow 前先 moderation(OpenAI Moderation API 或 Perspective API)。

Q2: iOS 和 Android 整合 Dialogflow 的 SDK 差別在哪?哪個比較好做?

實際上推薦都用 REST API 自己包,官方 SDK 不夠維護。(1) 官方 SDK 狀況——(A) Google 有 Android SDK(已淘汰,很久沒更新);(B) iOS 沒有官方 SDK;(C) 官方推薦走 REST API。(2) iOS 實作(Swift)——用 URLSession 或 Alamofire,pair with Auth0/Firebase Auth 做認證。範例:let url = URL(string: "https://your-backend.com/api/chat")!; URLSession.shared.dataTask(with: request) { ... }優點:iOS 的網路層處理簡單;難點:語音輸入整合(用 AVAudioEngine + Speech framework)。(3) Android 實作(Kotlin)——用 Retrofit + OkHttp,pair with Firebase Auth。範例:@POST("/api/chat") suspend fun chat(@Body request: ChatRequest): ChatResponse優點:Retrofit 生態完整;難點:語音需要 RecognitionListener + Android Speech 或 Google Cloud Speech-to-Text。(4) 跨平台選擇——(A) React Native——react-native-voice + axios;(B) Flutter——speech_to_text + http package;(C) 兩個 package 都用 Dart/JS 共用邏輯,比原生省時 50%+。實作優先順序:(1) MVP 階段——用跨平台框架(Flutter 或 RN)一次搞定 iOS + Android;(2) 成熟產品——原生 iOS Swift + Android Kotlin,效能最佳;(3) 企業內部 App——原生搭配 MDM(Mobile Device Management),資安要求高。

Q3: App 內整合語音對話要注意什麼?延遲體驗不好怎麼辦?

整體延遲應 < 2 秒才有好 UX。延遲拆解:(1) 語音轉文字(Speech-to-Text)——佔 40–60% 延遲,通常 800–1500ms。可優化:(A) 用 streaming STT(Google Cloud Streaming API)邊講邊轉;(B) 在地端用 Android Speech 或 iOS Speech framework(較快但品質較差)。(2) Dialogflow intent 辨識——佔 10–20% 延遲,通常 200–500ms。除了換成 Dialogflow CX(速度類似)沒太多優化空間。(3) Webhook 處理(如果有 fulfillment)——佔 10–30%,看你 webhook 複雜度。優化:(A) 用 Cloud Run with min instances(避免 cold start);(B) 加 cache;(C) async 處理非關鍵邏輯。(4) 文字轉語音(TTS)——佔 10–30%,通常 300–800ms。優化:(A) 用 streaming TTS 邊生成邊播;(B) 常見回覆 pre-render 存成 MP3 用。(5) 網路——佔 5–15%,視使用者網路狀況。優化技巧總結:(A) Streaming 是關鍵——讓使用者看到「正在轉文字中...」視覺回饋;(B) 減少 round trips——不要一來一回呼叫多個 API;(C) 預載入——打開 app 時預先 warm up connection;(D) Fallback UX——超過 3 秒顯示「稍等一下」字樣。實戰數字:Google Assistant 平均延遲 1.2 秒,這是目標。

Q4: App Store / Google Play 審核會因為 AI 對話功能被退件嗎?要注意哪些合規?

會,而且審核越來越嚴。常見退件原因:(1) 內容審核——(A) Apple 要求 app 生成內容有 moderation(不能用 AI 產出成人、暴力、hateful 內容);(B) Google Play 類似要求;(C) 解法:輸出前過 moderation API(OpenAI Moderation、Perspective API)。(2) 使用者資料保護——(A) Apple 要求 app 在 App Privacy 詳細申報收集的資料;(B) GDPR / CCPA 要求 opt-in、可刪除;(C) 解法:清楚的 privacy policy、in-app 可查看/刪除資料功能。(3) 年齡限制——(A) 如果 app 可能被未成年人用(年齡 12+ 以下),AI 對話有嚴格限制;(B) 需要家長同意機制;(C) 解法:提供「限制模式」或設 13+ 等級。(4) 功能描述符合——(A) 如果宣稱「AI 客服」但實際功能很陽春也可能被退;(B) 實際 feature 必須和 App Store description 相符。(5) Medical/Financial/Legal advice——(A) AI 給醫療、財務、法律建議有嚴格限制;(B) 必要:加免責聲明、明顯標示「非專業建議」、嚴重情況導向真人專業。實戰建議:(1) 第一次送審前先讀 App Store Guidelines 4.8(Login Services)和 5.1(Privacy);(2) 確保 TestFlight beta 有真實使用者測試;(3) 準備好 moderation 報告(被問時拿得出);(4) 對話 UI 必須讓使用者知道「這是 AI」,不假裝是真人。

Q5: 我的 App 已經用了 Firebase,可以直接整合 Dialogflow 嗎?Cost 會不會爆?

可以,而且 Firebase 是最流暢的選擇之一。Firebase + Dialogflow 整合方式:(1) Firebase Extensions——Google 提供「Dialogflow Integration」official extension,一鍵安裝;對 Firestore 訊息自動送到 Dialogflow 處理回覆。(2) Cloud Functions for Firebase——寫 Cloud Function 當 webhook / proxy:Firebase Auth 驗證 user → call Dialogflow → store conversation 到 Firestore。(3) Firebase Auth + Dialogflow——app 用 Firebase Auth 登入,拿到 token 送給你的 Cloud Function,function 用該 token 識別 user。成本結構(500 DAU app):(A) Firebase:Authentication 免費;Firestore 約 $5–20/月(1M reads、200K writes、100K deletes);Cloud Functions 免費額度 2M invocations/月通常夠;(B) Dialogflow ES:$270/月(如前面算過);(C) Cloud Storage(如果存語音檔):$5–10/月;(D) Cloud Run(webhook,如果不用 Functions):$20–50/月;(E) 總月費:$300–400/月。省錢技巧:(1) 用 Firestore 而非 Realtime Database(Firestore 更省);(2) Cloud Functions min instances = 0(有 cold start 但省錢);(3) Dialogflow 免費額度 15K/月要用盡;(4) 只存必要資料,conversation 設 90 天 TTL 自動刪除。scale 時要注意:10K DAU 後 Dialogflow 費用會占大部分(可能 $5,000+/月),這時要評估自架開源方案(Rasa)或用更便宜的 LLM API(Gemini Flash)替代。


下一步

完成行動整合後,你可以:

  1. 優化對話設計Dialogflow Intent 與 Context 完整教學
  2. 開發後端整合Dialogflow Fulfillment 與 API 整合教學
  3. 控制成本Dialogflow 費用完整解析

想在 App 中加入 AI 對話功能?

在 App 中整合 AI 對話涉及很多技術細節:API 整合、安全性、效能優化、審核合規...

如果你需要:

  • 在現有 App 中加入 AI 客服功能
  • 開發具備語音助理的新 App
  • 設計安全可靠的整合架構
  • 跨平台(iOS + Android)解決方案

預約 AI 導入諮詢,讓有經驗的團隊幫你規劃和實作。

我們已協助多個 App 成功整合 AI 對話功能,諮詢完全免費。


需要專業的雲端建議?

無論您正在評估雲平台、優化現有架構,或尋找節費方案,我們都能提供協助

預約免費諮詢

相關文章