在前面的幾天裡,我們透過 Google AI Studio 打造了具備蘇格拉底式引導靈魂的 System Instruction,也使用 Google Stitch 快速刻畫出了聊天室前端介面。然而,一個空有大腦(Gemini API)卻無法與外表(HTML 介面)溝通的系統,終究只能停留在測試階段,所以今天的核心任務,就是將我們的 Express 後端與前端介面完美串接,讓學生在網頁上打字提問時,能真正呼叫 Google AI 並即時獲得引導式回覆!
將前端介面與 Node.js 後端 API 進行全面串接。我們成功讓使用者在網頁上輸入問題時,後端能夠透過最新的模型進行蘇格拉底式引導運算,並以乾淨的 JSON 結構回傳給前端,讓 AI 助教正式上線。
要讓前後端順暢溝通,我們需要完成以下三個步驟的介接設定:
index.html,並開啟 CORS(跨來源資源共享),確保前端網頁在本地開發或部署時,不會被瀏覽器的安全機制擋下。POST 請求到我們後端的 /api/chat 路由。reply(助教回覆)與 suggested_questions(引導快捷按鈕),並自動動態插入對話泡泡與互動按鈕到聊天室畫面中。
import express from 'express';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
import { GoogleGenAI, Type } from '@google/genai';
dotenv.config();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
app.use(express.json());
app.use(express.static(__dirname));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
// 具備自動重試功能的包裝函式
async function generateWithRetry(ai, payload, retries = 3, delay = 1000){
for (let i = 0; i < retries; i++) {
try {
return await ai.models.generateContent(payload);
} catch (error) {
if (error.status === 503 && i < retries - 1) {
console.warn(`⚠️ 遇到 503 伺服器忙碌,正在進行第 ${i + 1} 次重試...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
app.post('/api/chat', async (req, res) => {
try {
const { message, history = [] } = req.body;
console.log(`📥 收到前端訊息: ${message}`);
const response = await generateWithRetry(ai, {
model: 'gemini-3.6-flash',
contents: [...history, { role: 'user', parts: [{ text: message }] }],
config: {
systemInstruction: '你是一位親切且具備蘇格拉底引導式的 AI 助教 Professor Spark。請不要直接給答案,而是透過提問引導學生思考。',
responseMimeType: 'application/json',
responseSchema: {
type: Type.OBJECT,
properties: {
reply: { type: Type.STRING, description: '助教對學生的白話引導對話' },
scaffold_type: { type: Type.STRING, enum: ['analogy', 'breakdown', 'quiz'] },
suggested_questions: { type: Type.ARRAY, items: { type: Type.STRING } }
},
required: ['reply', 'scaffold_type', 'suggested_questions'],
},
},
});
const jsonResponse = JSON.parse(response.text || '{}');
console.log('✅ 成功輸出結構化資料');
return res.json({ success: true, ...jsonResponse });
} catch (error) {
console.error('❌ API 處理發生錯誤:', error);
return res.status(500).json({ error: '伺服器處理失敗', details: error.message });
}
});
app.listen(3000, () => {
console.log('🚀 伺服器已成功啟動:http://localhost:3000');
});
將理論與介面化為真實可用的系統從來不是一路順風。在今天的實戰過程中,我們經歷了真實開發者都會遇到的幾波試錯與重構。以下是我們逐步打通前後端的完整操作步驟與避坑指南:
**統一架構與路由對齊:**起初我們擁有 index.js 與 server.js 兩個檔案,導致進入點混淆與路由不匹配(前端呼叫 /api/chat 而後端寫成 /api/ask-tutor)。我們果斷進行重構,刪除冗餘的 index.js,將所有靜態檔案託管與 API 邏輯集中於 server.js 單一檔案中,完美解決 Port 衝突。
**克服 ES 模組與路徑錯誤:**由於專案在 package.json 中設定了 "type": "module",直接使用 __dirname 會引發 ReferenceError。我們透過引入 path 與 url 模組手動宣告路徑:JavaScript
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
app.use(express.static(__dirname));
同時設定根路由 app.get('/', ...),解決了瀏覽器訪問時常見的 Cannot GET / 與 403 Forbidden 跨域阻擋問題。
503 雲端防禦 :我在測試中遇到雲端流量尖峰造成的 503 Service Unavailable 錯誤,所以為此在後端加入了自動重試機制(Retry Mechanism),當偵測到伺服器暫時忙碌時自動延遲 1 秒重試,大幅提升系統的強韌度。
為什麼前後端串接的穩定性對「未來教育」如此重要?在真實的教學情境中,學生的學習情緒與心流是非常脆弱的。如果當學生滿懷好奇心詢問時,系統卻因為 CORS 錯誤、模型過期或 503 忙碌而直接崩潰或卡死在「思考中...」,這會瞬間打斷學生的學習節奏,甚至澆熄他們主動探索的熱情。透過在後端加入嚴謹的錯誤攔截與自動重試機制,我們確保了 AI 助教能夠在背後默默承擔網路波動與模型更迭的風險,呈現在學生面前是流暢、穩定且充滿啟發性的對話體驗。
明天我們將進入 Day 10 - 前 10 天 MVP 成果展示與 AI Studio 運用心得整理,為這十天的原型開發做一次系統性的總結與復盤!