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 金鑰安全保存在後端
- 可以加入商業邏輯(驗證、記錄、過濾)
- 容易整合其他系統
缺點:
- 需要建置和維護後端
- 延遲稍高
- 架構較複雜
優缺點分析與選擇建議
| 方式 | 適用場景 |
|---|---|
| 直接 API | POC 驗證、學習用途、內部工具 |
| 後端轉發 | 正式產品、需要資安、企業應用 |
建議:正式上線的 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:取得金鑰檔案
- 前往 Google Cloud Console > IAM > Service Accounts
- 建立或選擇服務帳戶
- 下載 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.SessionsClient 的 detectIntent({ 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)替代。
下一步
完成行動整合後,你可以:
- 優化對話設計:Dialogflow Intent 與 Context 完整教學
- 開發後端整合:Dialogflow Fulfillment 與 API 整合教學
- 控制成本:Dialogflow 費用完整解析
想在 App 中加入 AI 對話功能?
在 App 中整合 AI 對話涉及很多技術細節:API 整合、安全性、效能優化、審核合規...
如果你需要:
- 在現有 App 中加入 AI 客服功能
- 開發具備語音助理的新 App
- 設計安全可靠的整合架構
- 跨平台(iOS + Android)解決方案
預約 AI 導入諮詢,讓有經驗的團隊幫你規劃和實作。
我們已協助多個 App 成功整合 AI 對話功能,諮詢完全免費。
相關文章
Dialogflow CX vs ES 完整比較:2026 版本選擇指南
Dialogflow CX 和 ES 到底差在哪?本文詳細比較功能、費用、適用場景,附決策流程圖,幫你選對版本不踩雷、不花冤枉錢。
DialogflowDialogflow Fulfillment 與 API 整合完整教學
Dialogflow Webhook 開發完整教學:Cloud Functions 部署、DetectIntent API 呼叫、第三方 API 整合。含 Node.js、Python 範例程式碼與 GitHub 專案連結。
DialogflowDialogflow 完整指南 2026:從入門到實戰的 AI 對話機器人開發
完整解析 Google Dialogflow CX 與 ES 版本差異、Generative AI Agents、Vertex AI 整合、費用計算、LINE Bot 整合教學。從零開始打造企業級 AI 客服機器人,含 2026 最新生成式 AI 功能與實戰範例。