anhkhoiphan commited on
Commit
f74347a
·
1 Parent(s): 01c4497

Thêm memory theo user id

Browse files
Files changed (2) hide show
  1. app.py +282 -165
  2. chainlit.md +72 -9
app.py CHANGED
@@ -1,199 +1,316 @@
 
 
 
 
1
  import chainlit as cl
2
- import aiohttp
 
 
3
  import asyncio
4
- from typing import Optional, Dict, Any
5
 
6
  # API Configuration
7
  API_BASE_URL = "https://kumiko-v2.onrender.com"
8
- API_TIMEOUT = 360
9
-
10
- class APIClient:
11
- """Simple async API client for Kumiko Agent"""
12
-
13
- def __init__(self, base_url: str = API_BASE_URL):
14
- self.base_url = base_url
15
- self.session = None
16
-
17
- async def _get_session(self):
18
- """Get or create aiohttp session"""
19
- if self.session is None or self.session.closed:
20
- timeout = aiohttp.ClientTimeout(total=API_TIMEOUT)
21
- self.session = aiohttp.ClientSession(timeout=timeout)
22
- return self.session
23
-
24
- async def _make_request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
25
- """Make HTTP request to API"""
26
- session = await self._get_session()
27
- url = f"{self.base_url}{endpoint}"
28
-
29
- try:
30
- if method.upper() == "GET":
31
- async with session.get(url) as response:
32
- return await self._handle_response(response)
33
- elif method.upper() == "POST":
34
- async with session.post(url, json=data) as response:
35
- return await self._handle_response(response)
36
- elif method.upper() == "DELETE":
37
- async with session.delete(url) as response:
38
- return await self._handle_response(response)
39
- except aiohttp.ClientError as e:
40
- raise Exception(f"Không thể kết nối API: {str(e)}")
41
-
42
- async def _handle_response(self, response: aiohttp.ClientResponse) -> Dict[str, Any]:
43
- """Handle API response"""
44
- if response.status == 200:
45
- return await response.json()
46
  else:
47
- error_text = await response.text()
48
- raise Exception(f"API error {response.status}: {error_text}")
49
-
50
- async def health_check(self) -> bool:
51
- """Check if API is healthy"""
52
- try:
53
- result = await self._make_request("GET", "/health")
54
- return result.get("status") == "healthy"
55
- except:
56
- return False
57
-
58
- async def chat(self, query: str, max_token_limit: int = 2000) -> Dict[str, Any]:
59
- """Send chat message to /chat endpoint"""
60
- data = {
61
- "query": query,
62
- "max_token_limit": max_token_limit
63
- }
64
- return await self._make_request("POST", "/chat", data)
65
-
66
- async def clear_all(self) -> Dict[str, Any]:
67
- """Clear all memory and caches via /clear-all endpoint"""
68
- return await self._make_request("DELETE", "/clear-all")
69
-
70
- async def close(self):
71
- """Close the session"""
72
- if self.session and not self.session.closed:
73
- await self.session.close()
74
-
75
- # Global API client
76
- api_client = APIClient()
77
 
78
  @cl.on_chat_start
79
  async def start():
80
  """Initialize chat session"""
81
- # Check API health
82
- is_healthy = await api_client.health_check()
83
 
84
- if not is_healthy:
85
- await cl.Message(
86
- content="❌ **Không thể kết nối đến Kumiko API!**\n\n"
87
- "Vui lòng kiểm tra:\n"
88
- "- API server đã được khởi động tại http://127.0.0.1:7861\n"
89
- "- Server hoạt động bình thường",
90
- author="Kumiko"
91
- ).send()
92
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- # Welcome message
95
- welcome_msg = """🤖 **Chào mừng bạn đến với Kumiko v2!**
 
 
 
 
 
96
 
97
- Xin chào, mình Kumiko, trợ lý ảo cute hột me!
98
 
99
- **Mình có thể giúp bạn những việc sau:**
100
- - 💬 Trò chuyện và tâm sự với bạn như một người bạn!
101
- - 🔍 Tìm kiếm và truy xuất thông tin từ Wikipedia và Internet.
102
 
103
- Hãy hỏi mình bất kỳ điều bạn muốn biết! 😊"""
 
 
 
 
 
 
104
 
105
- await cl.Message(
106
- content=welcome_msg,
107
- author="Kumiko"
108
- ).send()
109
 
110
  @cl.on_message
111
  async def main(message: cl.Message):
112
- """Handle incoming messages - only use /chat endpoint"""
113
- user_query = message.content.strip()
 
114
 
115
- # Validate input
116
- if not user_query:
 
 
 
 
 
 
 
117
  await cl.Message(
118
- content=" Vui lòng nhập câu hỏi của bạn.",
119
  author="Kumiko"
120
  ).send()
121
  return
122
 
123
- # Show processing indicator
124
- async with cl.Step(name="processing", type="run") as step:
125
- step.output = "Đang xử lý câu hỏi của bạn..."
126
-
127
- try:
128
- # Send query to /chat endpoint only
129
- response = await api_client.chat(user_query)
130
-
131
- if response.get("success"):
132
- agent_response = response.get("response", "Không có phản hồi từ agent")
133
- message_count = response.get("message_count", 0)
134
-
135
- # Update step status
136
- step.output = f"✅ Đã xử lý xong (tin nhắn #{message_count})"
137
-
138
- # Send agent response
139
- await cl.Message(
140
- content=agent_response,
141
- author="Kumiko"
142
- ).send()
143
-
144
- else:
145
- step.output = "❌ Lỗi xử lý"
146
- await cl.Message(
147
- content="❌ Xin lỗi, tôi không thể trả lời câu hỏi này. Vui lòng thử lại với câu hỏi khác.",
148
- author="Kumiko"
149
- ).send()
150
-
151
- except Exception as e:
152
- step.output = f"❌ Lỗi: {str(e)}"
153
- error_msg = f"❌ **Lỗi khi xử lý:** {str(e)}\n\nVui lòng thử lại hoặc kiểm tra kết nối API."
154
- await cl.Message(
155
- content=error_msg,
156
- author="Kumiko"
157
- ).send()
158
 
159
- @cl.on_chat_end
160
- async def end():
161
- """Handle chat end - automatically call /clear-all when user clicks 'New Chat'"""
162
- try:
163
- print("🧹 Chat ended - calling /clear-all endpoint...")
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
- # Call /clear-all endpoint to reset everything
166
- result = await api_client.clear_all()
 
 
167
 
168
- if result.get("success"):
169
- print("✅ Successfully cleared all memory and cache")
170
- # Optional: Send confirmation message (user might not see it due to chat ending)
171
  await cl.Message(
172
- content="🧹 **Đã dọn dẹp session hoàn tất** - Sẵn sàng cho cuộc trò chuyện mới!",
173
  author="Kumiko"
174
  ).send()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  else:
176
- print("⚠️ Warning: clear-all returned unsuccessful status")
177
-
178
- except Exception as e:
179
- print(f"❌ Error during chat end cleanup: {e}")
180
- # Still try to send error message
181
- await cl.Message(
182
- content=f"⚠️ **Cảnh báo:** lỗi khi dọn dẹp session: {str(e)}",
183
- author="Kumiko"
184
- ).send()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
- @cl.on_stop
187
- async def stop():
188
- """Handle application stop - cleanup resources"""
189
- try:
190
- await api_client.close()
191
- print("🔌 API client connection closed")
192
- except Exception as e:
193
- print(f"Error closing API client: {e}")
194
 
195
- if __name__ == "__main__":
196
- print("🚀 Starting Chainlit UI for RAG Agent...")
197
- print("📡 Connecting to API at:", API_BASE_URL)
198
- print("🌐 UI will be available at: http://localhost:8000")
199
- cl.run()
 
1
+ """
2
+ Chainlit UI for AI Agent with Login/Registration
3
+ """
4
+
5
  import chainlit as cl
6
+ import requests
7
+ from typing import Optional, Dict
8
+ import json
9
  import asyncio
 
10
 
11
  # API Configuration
12
  API_BASE_URL = "https://kumiko-v2.onrender.com"
13
+
14
+ # ============================================
15
+ # API HELPER FUNCTIONS
16
+ # ============================================
17
+
18
+ def check_user_exists(user_id: str) -> bool:
19
+ """Check if user exists in the system"""
20
+ try:
21
+ response = requests.get(f"{API_BASE_URL}/user-exists/{user_id}")
22
+ if response.status_code == 200:
23
+ return response.json().get("exists", False)
24
+ return False
25
+ except Exception as e:
26
+ print(f"Error checking user: {e}")
27
+ return False
28
+
29
+
30
+ def register_user(user_id: str) -> Dict:
31
+ """Register a new user"""
32
+ try:
33
+ response = requests.post(
34
+ f"{API_BASE_URL}/register",
35
+ json={"user_id": user_id}
36
+ )
37
+ if response.status_code == 201:
38
+ return {"success": True, "message": response.json().get("message", "Đăng ký thành công!")}
 
 
 
 
 
 
 
 
 
 
 
 
39
  else:
40
+ error_msg = response.json().get("detail", "Đăng ký thất bại")
41
+ return {"success": False, "message": error_msg}
42
+ except Exception as e:
43
+ return {"success": False, "message": f"Lỗi kết nối: {str(e)}"}
44
+
45
+
46
+ def send_chat_message(query: str, user_id: str) -> Dict:
47
+ """Send chat message to API"""
48
+ try:
49
+ response = requests.post(
50
+ f"{API_BASE_URL}/chat",
51
+ json={"query": query, "user_id": user_id}
52
+ )
53
+ if response.status_code == 200:
54
+ data = response.json()
55
+ return {
56
+ "success": True,
57
+ "answer": data.get("answer", ""),
58
+ "references": data.get("references", "Không có tài liệu tham khảo")
59
+ }
60
+ else:
61
+ error_msg = response.json().get("detail", "Lỗi xử lý câu hỏi")
62
+ return {"success": False, "message": error_msg}
63
+ except Exception as e:
64
+ return {"success": False, "message": f"Lỗi kết nối API: {str(e)}"}
65
+
66
+
67
+ # ============================================
68
+ # CHAINLIT EVENT HANDLERS
69
+ # ============================================
70
 
71
  @cl.on_chat_start
72
  async def start():
73
  """Initialize chat session"""
74
+ # Check if user is already logged in
75
+ user_id = cl.user_session.get("user_id")
76
 
77
+ if not user_id:
78
+ await show_login_screen()
79
+ else:
80
+ await show_chat_interface(user_id)
81
+
82
+
83
+ async def show_login_screen():
84
+ """Display login/registration screen"""
85
+ welcome_msg = """
86
+ ### 🎼 Chào mừng đến với Kumiko v2! 📯
87
+
88
+ **Đăng nhập** hoặc **đăng ký** để bắt đầu trò chuyện với mình nhé!
89
+
90
+ ---
91
+
92
+ #### 📝 Hướng dẫn:
93
+ - **Đăng nhập**: Gõ `/login <user_id>` (ví dụ: `/login john`)
94
+ - **Đăng ký**: Gõ `/register <user_id>` (ví dụ: `/register john`)
95
+ - User ID phải có ít nhất 3 ký tự
96
+
97
+ ---
98
+ **Ví dụ:**
99
+ - `/login alice`
100
+ - `/register bob123`
101
+ """
102
 
103
+ await cl.Message(content=welcome_msg, author="Kumiko").send()
104
+ cl.user_session.set("awaiting_auth", True)
105
+
106
+
107
+ async def show_chat_interface(user_id: str):
108
+ """Display chat interface after successful login"""
109
+ welcome_msg = f"""#### ✅ Yayy, bạn đã đăng nhập thành công!
110
 
111
+ **User ID của bạn là:** `{user_id}`
112
 
113
+ ---
 
 
114
 
115
+ Bạn có thể bắt đầu đặt câu hỏi. Mình sẽ trả lời cung cấp tài liệu tham khảo nếu có!
116
+
117
+ 💡 **Mẹo:** Sau mỗi câu trả lời, bạn có thể nhấn nút "𝄢 Tài liệu tham khảo" để check lại thông tin quan trọng!
118
+
119
+ ---
120
+ Để đăng xuất, gõ `/logout`
121
+ """
122
 
123
+ await cl.Message(content=welcome_msg, author="Kumiko").send()
124
+ cl.user_session.set("user_id", user_id)
125
+ cl.user_session.set("awaiting_auth", False)
126
+
127
 
128
  @cl.on_message
129
  async def main(message: cl.Message):
130
+ """Handle incoming messages"""
131
+ user_id = cl.user_session.get("user_id")
132
+ awaiting_auth = cl.user_session.get("awaiting_auth", True)
133
 
134
+ content = message.content.strip()
135
+
136
+ # Handle authentication commands
137
+ if content.startswith("/"):
138
+ await handle_command(content)
139
+ return
140
+
141
+ # Check if user is logged in
142
+ if not user_id or awaiting_auth:
143
  await cl.Message(
144
+ content="⚠️ Vui lòng đăng nhập trước khi chat.\n\nGõ `/login <user_id>` hoặc `/register <user_id>`",
145
  author="Kumiko"
146
  ).send()
147
  return
148
 
149
+ # Process chat message
150
+ await process_chat_message(content, user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+
153
+ async def handle_command(command: str):
154
+ """Handle special commands"""
155
+ parts = command.split(maxsplit=1)
156
+ cmd = parts[0].lower()
157
+
158
+ # Login command
159
+ if cmd == "/login":
160
+ if len(parts) < 2:
161
+ await cl.Message(content="❌ Sử dụng: `/login <user_id>`", author="Kumiko").send()
162
+ return
163
+
164
+ user_id = parts[1].strip()
165
+
166
+ if len(user_id) < 3:
167
+ await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
168
+ return
169
 
170
+ # Show loading with spinner
171
+ async with cl.Step(name="🔍 Đang kiểm tra tài khoản...", type="tool"):
172
+ # Check if user exists
173
+ exists = await asyncio.to_thread(check_user_exists, user_id)
174
 
175
+ if exists:
176
+ await show_chat_interface(user_id)
177
+ else:
178
  await cl.Message(
179
+ content=f" User ID `{user_id}` không tồn tại!\n\nVui lòng đăng bằng: `/register {user_id}`",
180
  author="Kumiko"
181
  ).send()
182
+
183
+ # Register command
184
+ elif cmd == "/register":
185
+ if len(parts) < 2:
186
+ await cl.Message(content="❌ Sử dụng: `/register <user_id>`", author="Kumiko").send()
187
+ return
188
+
189
+ user_id = parts[1].strip()
190
+
191
+ if len(user_id) < 3:
192
+ await cl.Message(content="❌ User ID phải có ít nhất 3 ký tự!", author="Kumiko").send()
193
+ return
194
+
195
+ # Show loading with spinner
196
+ async with cl.Step(name="📝 Đang đăng ký tài khoản...", type="tool"):
197
+ # Register user
198
+ result = await asyncio.to_thread(register_user, user_id)
199
+
200
+ if result["success"]:
201
+ await cl.Message(content=f"✅ {result['message']}", author="Kumiko").send()
202
+ await show_chat_interface(user_id)
203
  else:
204
+ await cl.Message(content=f" {result['message']}", author="Kumiko").send()
205
+
206
+ # Logout command
207
+ elif cmd == "/logout":
208
+ cl.user_session.set("user_id", None)
209
+ cl.user_session.set("awaiting_auth", True)
210
+ await cl.Message(content="👋 Đã đăng xuất thành công!", author="Kumiko").send()
211
+ await show_login_screen()
212
+
213
+ # Help command
214
+ elif cmd == "/help":
215
+ help_msg = """
216
+ #### 📖 Hướng dẫn sử dụng
217
+
218
+ #### Lệnh đăng nhập/đăng ký:
219
+ - `/login <user_id>` - Đăng nhập với user_id có sẵn
220
+ - `/register <user_id>` - Đăng ký user_id mới
221
+ - `/logout` - Đăng xuất
222
+
223
+ #### Lệnh khác:
224
+ - `/help` - Hiển thị hướng dẫn này
225
+
226
+ #### Chat:
227
+ Sau khi đăng nhập, bạn có thể chat bình thường. Hệ thống sẽ trả lời và cung cấp tài liệu tham khảo.
228
+ """
229
+ await cl.Message(content=help_msg, author="Kumiko").send()
230
+
231
+ else:
232
+ await cl.Message(content=f"❌ Lệnh không hợp lệ: `{cmd}`\n\nGõ `/help` để xem danh sách lệnh.", author="Kumiko").send()
233
+
234
+
235
+ async def process_chat_message(query: str, user_id: str):
236
+ """Process chat message and display response"""
237
+ # Show thinking message with spinner
238
+ async with cl.Step(name="🤔 Đang suy nghĩ...", type="llm"):
239
+ # Send request to API
240
+ result = await asyncio.to_thread(send_chat_message, query, user_id)
241
+
242
+ if not result["success"]:
243
+ await cl.Message(content=f"❌ Lỗi: {result['message']}", author="Kumiko").send()
244
+ return
245
+
246
+ answer = result["answer"]
247
+ references = result["references"]
248
+
249
+ # Store references in session for the button
250
+ cl.user_session.set("last_references", references)
251
+
252
+ # Create message with action button
253
+ actions = [
254
+ cl.Action(
255
+ name="show_references",
256
+ value="show",
257
+ label="𝄢 Tài liệu tham khảo",
258
+ description="Xem các nguồn tham khảo",
259
+ payload={"references": references}
260
+ )
261
+ ]
262
+
263
+ # Send answer with reference button
264
+ await cl.Message(
265
+ content=answer,
266
+ actions=actions,
267
+ author="Kumiko"
268
+ ).send()
269
+
270
+
271
+ @cl.action_callback("show_references")
272
+ async def on_show_references(action: cl.Action):
273
+ """Handle reference button click"""
274
+ # Get references from action payload
275
+ references = action.payload.get("references", "Không có tài liệu tham khảo")
276
+
277
+ # Send references as a new message
278
+ await cl.Message(
279
+ content=references,
280
+ author="Kumiko"
281
+ ).send()
282
+
283
+ # Optional: Remove the action button after clicking
284
+ await action.remove()
285
+
286
+
287
+ @cl.on_chat_end
288
+ async def end():
289
+ """Handle chat end"""
290
+ print("Chat session ended")
291
+
292
+ # ============================================
293
+ # CONFIGURATION
294
+ # ============================================
295
+
296
+ @cl.set_starters
297
+ async def set_starters():
298
+ """Set starter prompts for quick access"""
299
+ return [
300
+ cl.Starter(
301
+ label="Đăng nhập",
302
+ message="/login your_user_id",
303
+ icon="🔑",
304
+ ),
305
+ cl.Starter(
306
+ label="Đăng ký",
307
+ message="/register your_user_id",
308
+ icon="✍🏻",
309
+ ),
310
+ cl.Starter(
311
+ label="Trợ giúp",
312
+ message="/help",
313
+ ),
314
+ ]
315
 
 
 
 
 
 
 
 
 
316
 
 
 
 
 
 
chainlit.md CHANGED
@@ -1,13 +1,76 @@
1
- # Chào mừng đến với trợ lý ảo Kumiko v2.0 📯🎺🎷
2
- Tìm hiểu thêm về thiết lập nhân vật của Kumiko: https://hibike-euphonium.fandom.com/wiki/Kumiko_Oumae
 
 
 
 
 
 
 
 
 
 
 
3
 
4
  Kumiko hiện cho phép bạn:
5
- - Tìm kiếm nhân vật, sự vật, sự kiện, khái niệm trên Wikipedia.
6
- - Tra cứu thông tin, tin tức, thời tiết,... trên Internet.
7
 
8
- Sử dụng nút New Chat góc trên bên trái để reset phiên trò chuyện.
9
- thể chuyển giữa Light Mode/Dark Mode bằng button góc trên bên phải.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- Lưu ý, hiện Kumiko chưa tính năng ghi nhớ memory theo session ID.
12
- Điều này có nghĩa là thông tin cuộc trò chuyện có thể bị rò rỉ cho bên thứ ba.
13
- Vui lòng không chia sẻ thông tin nhạy cảm. Hãy ứng xử lịch sự.
 
1
+ ## 🎼 Chào mừng đến với trợ lý ảo Kumiko v2.0 📯🎺🎷
2
+
3
+ Xin chào! Mình là **Kumiko**, trợ lý ảo được lấy cảm hứng từ nhân vật Kumiko Oumae trong series Hibike! Euphonium.
4
+
5
+ ---
6
+
7
+ ### 🎭 Về Kumiko
8
+
9
+ Tìm hiểu thêm về thiết lập nhân vật của Kumiko: **[Kumiko Oumae - Hibike! Euphonium Wiki](https://hibike-euphonium.fandom.com/wiki/Kumiko_Oumae)**
10
+
11
+ ---
12
+
13
+ ### ✨ Khả năng của Kumiko
14
 
15
  Kumiko hiện cho phép bạn:
 
 
16
 
17
+ * 🔍 **Tìm kiếm nhân vật, sự vật, sự kiện, khái niệm** trên Wikipedia
18
+ * 🌐 **Tra cứu thông tin, tin tức, thời tiết,...** trên Internet
19
+ * 💬 **Trò chuyện tự nhiên** và trả lời các câu hỏi của bạn
20
+ * 📚 **Cung cấp tài liệu tham khảo** cho mỗi câu trả lời
21
+
22
+ ---
23
+
24
+ ### 📝 Hướng dẫn sử dụng
25
+
26
+ #### 1️⃣ Đăng nhập/Đăng ký
27
+
28
+ Trước khi bắt đầu trò chuyện, bạn cần đăng nhập hoặc đăng ký:
29
+
30
+ - **Đăng nhập**: `/login <user_id>` (ví dụ: `/login alice`)
31
+ - **Đăng ký**: `/register <user_id>` (ví dụ: `/register bob123`)
32
+ - **User ID** phải có ít nhất 3 ký tự
33
+
34
+ #### 2️⃣ Bắt đầu trò chuyện
35
+
36
+ Sau khi đăng nhập thành công, bạn có thể:
37
+
38
+ - Đặt bất kỳ câu hỏi nào cho Kumiko
39
+ - Nhấn nút **"𝄢 Tài liệu tham khảo"** sau mỗi câu trả lời để xem nguồn thông tin
40
+ - Sử dụng các lệnh đặc biệt bên dưới
41
+
42
+ #### 3️⃣ Các lệnh hữu ích
43
+
44
+ - `/help` - Hiển thị hướng dẫn
45
+ - `/logout` - Đăng xuất khỏi phiên hiện tại
46
+
47
+ ---
48
+
49
+ ### 🎯 Mẹo sử dụng
50
+
51
+ - 🔄 **New Chat**: Sử dụng nút **"New Chat"** ở góc trên bên trái để reset phiên trò chuyện
52
+ - 🌓 **Theme**: Có thể chuyển giữa **Light Mode/Dark Mode** bằng button góc trên bên phải
53
+ - 📚 **References**: Luôn kiểm tra tài liệu tham khảo để xác minh thông tin
54
+ - 💡 **Câu hỏi cụ thể**: Đặt câu hỏi càng rõ ràng, câu trả lời càng chính xác
55
+
56
+ ---
57
+
58
+ ### ⚠️ Lưu ý quan trọng
59
+
60
+ - Mỗi phiên trò chuyện được liên kết với **User ID** của bạn
61
+ - Thông tin chat được lưu trữ để cải thiện trải nghiệm sử dụng
62
+ - Hãy luôn kiểm tra lại thông tin quan trọng từ nguồn
63
+
64
+ ---
65
+
66
+ ### 🚀 Sẵn sàng bắt đầu?
67
+
68
+ Hãy đăng nhập hoặc đăng ký để bắt đầu trò chuyện với Kumiko nhé! 🎺
69
+
70
+ **Ví dụ:**
71
+ - `/login alice`
72
+ - `/register john_doe`
73
+
74
+ ---
75
 
76
+ 💙 *Chúc bạntrải nghiệm tuyệt vời cùng Kumiko!*