Skip to content

Commit d79a507

Browse files
feat: Implement an offline data store with predefined chatbot content, user data structures, and career group blueprints.
1 parent 27000ab commit d79a507

4 files changed

Lines changed: 189 additions & 32 deletions

File tree

assets/index-PJc7HnBD.js

Lines changed: 121 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/dist/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
}(window.location));
1717
</script>
1818
<script src="./runtime-config.js"></script>
19-
<script type="module" crossorigin src="./assets/index-D9JdCNWa.js"></script>
19+
<script type="module" crossorigin src="./assets/index-PJc7HnBD.js"></script>
2020
<link rel="stylesheet" crossorigin href="./assets/index-D83N1gQE.css">
2121
</head>
2222
<body>

frontend/src/offlineStore.js

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,19 +2704,17 @@ async function pickNextQuestion(state) {
27042704
const pastBotMessages = history.filter(m => m.sender === 'bot').map(m => m.message);
27052705
const conversationText = history.map(m => `${m.sender}: ${m.message}`).join('\n');
27062706

2707-
// Try LLM dynamic question if enabled and we have enough history
2708-
if (state.answers.length >= 1) {
2709-
const llmQ = await generateNextQuestionWithLLM(conversationText, profile, pastBotMessages);
2710-
if (llmQ) {
2711-
console.log("LLM Generated Question:", llmQ);
2712-
state.lastQuestionTags = llmQ.intended_tags || [];
2713-
state.lastQuestionText = llmQ.question;
2714-
return {
2715-
id: `llm_${Date.now()}`,
2716-
text: llmQ.question,
2717-
tags: llmQ.intended_tags || []
2718-
};
2719-
}
2707+
// Attempt LLM dynamic question ALWAYS
2708+
const llmQ = await generateNextQuestionWithLLM(conversationText, profile, pastBotMessages);
2709+
if (llmQ) {
2710+
console.log("Autonomous AI Question:", llmQ);
2711+
state.lastQuestionTags = llmQ.intended_tags || [];
2712+
state.lastQuestionText = llmQ.question;
2713+
return {
2714+
id: `ai_${Date.now()}`,
2715+
text: llmQ.question,
2716+
tags: llmQ.intended_tags || []
2717+
};
27202718
}
27212719

27222720
// Fallback to randomized static logic
@@ -2925,23 +2923,26 @@ async function generateNextQuestionWithLLM(conversationText, profile, pastBotMes
29252923

29262924
try {
29272925
const prompt = `
2928-
Bạn là một chatbot tư vấn nghề nghiệp thông minh.
2926+
Bạn là một CHUYÊN GIA TƯ VẤN NGHỀ NGHIỆP tâm lý và sâu sắc.
29292927
Hồ sơ người dùng: ${JSON.stringify(profile)}
2930-
Lịch sử hội thoại gần đây:
2931-
${conversationText.slice(-2000)}
2928+
Lịch sử hội thoại:
2929+
${conversationText || "Chưa có (đây là câu hỏi đầu tiên)"}
29322930
2933-
Nhiệm vụ: Hãy đặt một câu hỏi tiếp theo để tìm hiểu sâu hơn về sở thích, năng lực hoặc định hướng nghề nghiệp của người dùng.
2934-
2935-
YÊU CẦU QUAN TRỌNG:
2936-
1. TUYỆT ĐỐI KHÔNG lặp lại các câu hỏi đã hỏi sau đây: ${JSON.stringify(pastBotMessages.slice(-10))}
2937-
2. Câu hỏi phải tự nhiên, dựa trên những gì người dùng vừa trả lời.
2938-
3. Nếu người dùng muốn "hỏi thêm", hãy khai thác một khía cạnh mới hoặc đào sâu vào một ngành mà họ có vẻ quan tâm.
2939-
4. Ngôn ngữ thân thiện, gần gũi (dùng "bạn", "mình").
2931+
NHIỆM VỤ:
2932+
- Hãy dẫn dắt buổi tư vấn một cách tự nhiên.
2933+
- Nếu là câu đầu, hãy bắt đầu bằng một lời chào thân thiện và hỏi về đam mê, ước mơ hoặc một khó khăn họ đang gặp phải khi chọn nghề.
2934+
- Nếu đã trò chuyện, hãy phân tích câu trả lời trước đó để hỏi sâu hơn.
2935+
- Đừng chỉ hỏi về môn học, hãy hỏi về giá trị sống, môi trường làm việc mơ ước (trong nhà/ngoài trời, tự do/ổn định).
2936+
2937+
YÊU CẦU:
2938+
1. KHÔNG lặp lại các câu hỏi đã hỏi: ${JSON.stringify(pastBotMessages.slice(-10))}
2939+
2. Ngôn ngữ ấm áp, khích lệ (dùng "mình", "bạn").
2940+
3. Câu hỏi ngắn gọn nhưng gợi mở.
29402941
29412942
TRẢ VỀ DUY NHẤT JSON:
29422943
{
2943-
"question": "nội dung câu hỏi mới",
2944-
"intended_tags": ["tag_lien_quan1", "tag_lien_quan2"]
2944+
"question": "nội dung câu hỏi",
2945+
"intended_tags": ["tag1", "tag2"]
29452946
}
29462947
`;
29472948

@@ -2960,10 +2961,7 @@ async function generateNextQuestionWithLLM(conversationText, profile, pastBotMes
29602961
const jsonMatch = text.match(/\{[\s\S]*\}/);
29612962
if (jsonMatch) {
29622963
const result = JSON.parse(jsonMatch[0]);
2963-
// Double check uniqueness locally
2964-
if (pastBotMessages.includes(result.question)) {
2965-
return null; // Force fallback to randomized static
2966-
}
2964+
if (pastBotMessages.includes(result.question)) return null;
29672965
return result;
29682966
}
29692967
} catch (e) {
@@ -2972,6 +2970,41 @@ async function generateNextQuestionWithLLM(conversationText, profile, pastBotMes
29722970
return null;
29732971
}
29742972

2973+
async function getFinalAIEvaluation(conversationText, profile, recommendations) {
2974+
const apiKey = localStorage.getItem('GEMINI_API_KEY') || 'AIzaSyBufWY4GjPYSXH9jkOD6pjDcdMAgSgA2gM';
2975+
if (!apiKey) return "Cảm ơn bạn đã tham gia tư vấn. Dưới đây là kết quả dựa trên số liệu phân tích.";
2976+
2977+
try {
2978+
const prompt = `
2979+
Bạn là chuyên gia tư vấn nghề nghiệp. Dựa vào:
2980+
Hồ sơ: ${JSON.stringify(profile)}
2981+
Hội thoại: ${conversationText}
2982+
Gợi ý của hệ thống: ${JSON.stringify(recommendations.slice(0, 3))}
2983+
2984+
HÃY VIẾT:
2985+
1. Một đoạn tóm tắt về thế mạnh và định hướng của người dùng qua cuộc trò chuyện.
2986+
2. Giải thích tại sao 3 nghề nghiệp top đầu lại phù hợp với họ.
2987+
3. Một lời khuyên thực tế để họ bắt đầu (lộ trình học tập hoặc kỹ năng cần luyện).
2988+
2989+
YÊU CẦU: Ngôn ngữ chuyên nghiệp nhưng truyền cảm hứng. Tối đa 250 từ. Trả về text thuần.
2990+
`;
2991+
2992+
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`, {
2993+
method: 'POST',
2994+
headers: { 'Content-Type': 'application/json' },
2995+
body: JSON.stringify({
2996+
contents: [{ parts: [{ text: prompt }] }]
2997+
})
2998+
});
2999+
3000+
const data = await response.json();
3001+
return data.candidates?.[0]?.content?.parts?.[0]?.text || "Gợi ý của chúng mình dựa trên các tiêu chí bạn đã chia sẻ.";
3002+
} catch (e) {
3003+
return "Chúng mình đánh giá cao sự chia sẻ của bạn. Hãy xem qua danh sách gợi ý bên dưới nhé.";
3004+
}
3005+
}
3006+
3007+
29753008
export const offlineApi = {
29763009
getMe(token) {
29773010
if (!token || !token.startsWith('offline:')) return { success: false };
@@ -3071,12 +3104,15 @@ export const offlineApi = {
30713104
const enoughInfo = state.answers.length >= 6;
30723105
if (enoughInfo && !request_more) {
30733106
const recs = scoreCareers(state);
3107+
const conversationText = messages.map(m => `${m.sender}: ${m.message}`).join('\n');
3108+
const aiSummary = await getFinalAIEvaluation(conversationText, state.userProfile, recs);
3109+
30743110
saveRecommendations(convId, recs);
30753111
saveState(convId, state);
30763112
return {
30773113
success: true,
30783114
data: {
3079-
bot_reply: 'Đây là gợi ý nghề nghiệp phù hợp:',
3115+
bot_reply: aiSummary,
30803116
recommendations: recs,
30813117
next_node: null,
30823118
completed: true,

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
}(window.location));
1717
</script>
1818
<script src="./runtime-config.js"></script>
19-
<script type="module" crossorigin src="./assets/index-D9JdCNWa.js"></script>
19+
<script type="module" crossorigin src="./assets/index-PJc7HnBD.js"></script>
2020
<link rel="stylesheet" crossorigin href="./assets/index-D83N1gQE.css">
2121
</head>
2222
<body>

0 commit comments

Comments
 (0)