Skip to main content

Overview

The FinAI chatbot is your personal financial assistant that answers questions about your money in natural, conversational language. Ask anything about your spending patterns, wallet balances, or recent transactions, and get instant, context-aware responses.

Powered by Google Gemini 2.5 Flash

The chatbot uses the same Google Gemini 2.5 Flash model as categorization, but with streaming support for real-time responses:
# From app/ai_service.py:101-104
response_stream = self.client.models.generate_content_stream(
    model=self.model_name,
    contents=prompt
)
The chatbot uses streaming mode, meaning responses appear word-by-word in real-time, creating a natural conversational experience.

How to Use the Chatbot

1

Open Chat Interface

Access the chatbot through the main navigation or dashboard
2

Ask a Question

Type your question in natural language:
  • “Tháng này tôi chi tiêu bao nhiêu?”
  • “Top 3 danh mục tôi chi nhiều nhất là gì?”
  • “Số dư ví hiện tại của tôi?”
  • “Giao dịch gần nhất của tôi là gì?”
3

Receive Streaming Response

The AI analyzes your financial data and responds in real-time, word by word
4

Continue Conversation

Ask follow-up questions or request clarifications

Context Building

Before answering your question, the system builds a comprehensive financial context from your data:

1. Current Wallet Balances

# From app/routes/ai.py:80-83
wallets = Wallet.query.filter_by(user_id=user_id).all()
context_text = "--- TỔNG QUAN TÀI CHÍNH ---\nSỐ DƯ CÁC VÍ:\n"
for w in wallets: 
    context_text += f"- {w.name}: {int(w.balance):,} VND\n"
Example context:
SỐ DƯ CÁC VÍ:
- Tiền mặt: 5,000,000 VND
- Techcombank: 25,000,000 VND
- Momo: 1,500,000 VND

2. 30-Day Spending Summary by Category

# From app/routes/ai.py:85-99
expense_summary = db.session.query(
    Category.name, func.sum(Transaction.amount)
).join(Transaction, Transaction.category_id == Category.id).filter(
    Transaction.user_id == user_id, 
    Transaction.date >= last_30_days,
    Transaction.type == 'chi'
).group_by(Category.name).all()
Example context:
THỐNG KÊ CHI TIÊU 30 NGÀY QUA:
- Ăn uống: 3,500,000đ
- Xăng xe: 1,200,000đ
- Giải trí: 800,000đ

3. Recent Transaction History

# From app/routes/ai.py:101-107
recent_transactions = Transaction.query.filter_by(user_id=user_id)
    .order_by(Transaction.date.desc()).limit(5).all()

for t in recent_transactions:
    cat_name = t.category.name if t.category else "Khác"
    type_label = "CHI" if t.type == 'chi' else "THU" if t.type == 'thu' else "CHUYỂN KHOẢN"
    context_text += f"- {t.date.strftime('%d/%m')}: {int(t.amount):,}đ ({type_label}) - {cat_name} ({t.description})\n"
Example context:
5 GIAO DỊCH GẦN NHẤT:
- 07/03: 85,000đ (CHI) - Ăn uống (Cafe sáng với team)
- 06/03: 350,000đ (CHI) - Xăng xe (Đổ xăng Petrolimex)
- 05/03: 2,000,000đ (CHUYỂN KHOẢN) - (Gửi tiết kiệm)
The context is optimized to include only the most relevant data, preventing token limit issues while providing comprehensive information.

The Chatbot Prompt

The AI receives a detailed prompt that defines its personality and response style:
# From app/ai_service.py:76-98
prompt = f"""
Bạn là FinAI – một trợ lý tài chính cá nhân thông minh, thân thiện, nói chuyện tự nhiên như một người bạn hiểu về tiền bạc.

Dữ liệu tài chính gần đây của người dùng:
{context_data}

Câu hỏi: "{user_question}"

Cách trả lời:
- Trả lời kết quả chính ngay câu đầu tiên (ngắn gọn, rõ ràng).
- Viết như đang nói chuyện thật, KHÔNG mang văn phong báo cáo.
- Không dùng các câu mở đầu kiểu: "Dựa trên dữ liệu...", "Theo thông tin tôi có..."
- Nếu có lời khuyên hoặc cảnh báo, hãy xuống dòng 1 lần trước khi viết.
- Dùng emoji nhẹ nhàng (1–2 cái là đủ).
- Giữ câu trả lời trong 3–6 câu, tránh lan man.

Nếu thiếu dữ liệu, hỏi lại một cách tự nhiên như đang trò chuyện.

QUY TẮC KẾ TOÁN (BẮT BUỘC):
- "CHI TIÊU" = tiền ra khỏi hệ thống.
- "CHUYỂN KHOẢN" = tiền chuyển giữa ví của bạn → KHÔNG phải chi tiêu.
- Khi tính tổng chi tiêu: TUYỆT ĐỐI không cộng chuyển khoản.
"""

Personality Traits

Friendly & Conversational

Speaks naturally like a knowledgeable friend, not a formal report

Direct & Concise

Answers the main question immediately, then provides context

Financially Accurate

Follows strict accounting rules, especially around transfers vs expenses

Helpful & Proactive

Offers advice when appropriate, warns about overspending

Streaming Response Implementation

The chatbot streams responses in real-time using Flask’s stream_with_context:
# From app/routes/ai.py:117-141
@stream_with_context
def generate():
    full_answer = ""
    try:
        for chunk in response_stream:
            if chunk.text:
                full_answer += chunk.text
                yield chunk.text  # Send immediately to frontend
        
        # Save to database only after complete response
        log_id = str(uuid.uuid4())[:8]
        db.session.add(ChatbotLog(
            id=log_id, 
            user_id=user_id, 
            question=user_question, 
            answer=full_answer, 
            created_at=datetime.now()
        ))
        db.session.commit()
    except Exception as e:
        print(f"Lỗi Stream/Database: {e}")
        db.session.rollback()

return Response(generate(), mimetype='text/plain')
The system saves the conversation to the database ONLY after the AI finishes responding, ensuring complete messages are stored.

Chat History

All conversations are logged to the ChatbotLog model:
# From app/models.py:152-158
class ChatbotLog(db.Model):
    __tablename__ = 'chatbot_lichsu'
    id = db.Column('MaHoiThoai', db.String(8), primary_key=True)
    user_id = db.Column('MaNguoiDung', db.String(8))
    question = db.Column('NoiDungHoi', db.Text)
    answer = db.Column('NoiDungTraLoi', db.Text)
    created_at = db.Column('NgayTao', db.DateTime, default=datetime.now)

Retrieving Chat History

GET /api/chat/history
Response (app/routes/ai.py:150-161):
[
  {
    "role": "user",
    "content": "Tháng này tôi chi tiêu bao nhiêu?"
  },
  {
    "role": "ai",
    "content": "Tháng này bạn chi tiêu tổng cộng 5,500,000đ. Phần lớn tiền đi vào Ăn uống (3.5 triệu) và Xăng xe (1.2 triệu). Có vẻ tháng này bạn ăn uống nhiều hơn bình thường đấy!"
  }
]
The API returns the 20 most recent messages, ordered chronologically (app/routes/ai.py:154).

Example Conversations

User: “Tháng này tôi chi tiêu bao nhiêu?”FinAI: “Tháng này bạn chi tiêu tổng cộng 5,500,000đ. Trong đó Ăn uống chiếm nhiều nhất với 3.5 triệu, tiếp theo là Xăng xe 1.2 triệu và Giải trí 800k.Lưu ý là tôi không tính chuyển khoản giữa các ví của bạn nhé, đó không phải chi tiêu thật.”
User: “Tôi còn bao nhiêu tiền trong ví Momo?”FinAI: “Ví Momo của bạn đang có 1,500,000đ.”
User: “Tôi nên tiết kiệm như thế nào?”FinAI: “Nhìn vào chi tiêu của bạn, tôi thấy Ăn uống chiếm tới 3.5 triệu/tháng. Bạn có thể thử nấu ăn tại nhà nhiều hơn, hoặc giảm số lần đi cafe xuống còn 3-4 lần/tuần.Ngoài ra, tháng này bạn chuyển 2 triệu vào tiết kiệm rồi, đó là dấu hiệu tốt! Cứ giữ thói quen này nhé.”
User: “Tôi có vượt ngân sách không?”FinAI: “Bạn chưa tạo ngân sách nào cả. Muốn tôi hướng dẫn cách tạo ngân sách không?”

Critical Accounting Rule

The AI is specifically trained to distinguish between expenses and transfers:
# From app/ai_service.py:94-97
QUY TẮC KẾ TOÁN (BẮT BUỘC):
- "CHI TIÊU" = tiền ra khỏi hệ thống.
- "CHUYỂN KHOẢN" = tiền chuyển giữa ví của bạn → KHÔNG phải chi tiêu.
- Khi tính tổng chi tiêu: TUYỆT ĐỐI không cộng chuyển khoản.
This prevents the AI from incorrectly inflating your spending totals by counting internal transfers between your own wallets.

API Reference

Send Chat Message

POST /api/chat
Content-Type: application/json
Authentication: Required (session-based)

{
  "message": "Tôi chi tiêu nhiều nhất vào danh mục nào?"
}
Response: Streaming text/plain The response streams word-by-word in plain text format. Error Response:
{
  "response": "Xin lỗi, hệ thống AI đang bận kết nối. Vui lòng thử lại sau vài giây nhé!"
}

Get Chat History

GET /api/chat/history
Authentication: Required (session-based)
Response: Array of message objects with role and content

Privacy & Security

User Isolation

Each user’s data is strictly isolated - the AI only sees YOUR financial data

Session-Based Auth

All chat endpoints require authentication via session cookies

No Training on Your Data

Your conversations are not used to train Gemini models

Temporary Context

Financial context is built per-request and not persisted in AI memory

Error Handling

If the AI service fails:
# From app/ai_service.py:107-109
except Exception as e:
    print(f"Chat Error: {e}")
    return "Xin lỗi, hệ thống AI đang bận kết nối. Vui lòng thử lại sau vài giây nhé!"
The system returns a friendly Vietnamese error message and logs the technical error for debugging.

Performance Optimization

The context builder uses optimized SQL queries:
# From app/routes/ai.py:86-92
# Uses GROUP BY to aggregate spending by category in one query
expense_summary = db.session.query(
    Category.name, func.sum(Transaction.amount)
).join(...).filter(...).group_by(Category.name).all()
This prevents N+1 query problems and reduces database load.

Disabling AI Suggestions

Users can disable AI features through settings:
# From app/routes/ai.py:171-177
user_setting = UserSetting.query.get(user_id)
if user_setting and user_setting.ai_suggestions == 0:
    return jsonify({
        'status': 'disabled',
        'message': 'Tính năng AI đang bị tắt.'
    })
This applies to the dashboard insights feature (see Reports).

Build docs developers (and LLMs) love