Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
chatbot_order
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
chatbot_order
Commits
73b2d542
Commit
73b2d542
authored
Feb 11, 2026
by
Vũ Hoàng Anh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add CONTACT_INFO to user_insight for seamless order checkout
parent
178108a8
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
381 additions
and
6 deletions
+381
-6
structured_models.py
backend/agent/structured_models.py
+4
-0
system_prompt.txt
backend/agent/system_prompt.txt
+53
-4
take_order.prompt.txt
backend/agent/tool_prompts/take_order.prompt.txt
+130
-0
__init__.py
backend/agent/tools/__init__.py
+2
-1
get_tools.py
backend/agent/tools/get_tools.py
+2
-1
take_order.py
backend/agent/tools/take_order.py
+190
-0
No files found.
backend/agent/structured_models.py
View file @
73b2d542
...
@@ -14,6 +14,10 @@ class UserInsight(BaseModel):
...
@@ -14,6 +14,10 @@ class UserInsight(BaseModel):
default
=
"Chưa rõ."
,
default
=
"Chưa rõ."
,
description
=
"Thông tin người chat: Giới tính, Người lớn/Trẻ em, Style/Gu"
description
=
"Thông tin người chat: Giới tính, Người lớn/Trẻ em, Style/Gu"
)
)
CONTACT_INFO
:
str
=
Field
(
default
=
"Chưa có."
,
description
=
"Thông tin liên hệ khách: Name, Phone, Address, Email (thu thập dần qua hội thoại)"
)
TARGET
:
str
=
Field
(
TARGET
:
str
=
Field
(
default
=
"Chưa rõ."
,
default
=
"Chưa rõ."
,
description
=
"Đối tượng thụ hưởng: Quan hệ, Giới tính, Người lớn/Trẻ em, Style/Gu"
description
=
"Đối tượng thụ hưởng: Quan hệ, Giới tính, Người lớn/Trẻ em, Style/Gu"
...
...
backend/agent/system_prompt.txt
View file @
73b2d542
...
@@ -1125,12 +1125,13 @@ Cả hai màu này đều rất sang, anh xem thử mẫu nào thích nhé!"
...
@@ -1125,12 +1125,13 @@ Cả hai màu này đều rất sang, anh xem thử mẫu nào thích nhé!"
`user_insight` KHÔNG PHẢI LÀ NOTE DỮ LIỆU TĨNH. Nó là **BỘ NÃO GHI NHỚ** có cấu trúc chặt chẽ.
`user_insight` KHÔNG PHẢI LÀ NOTE DỮ LIỆU TĨNH. Nó là **BỘ NÃO GHI NHỚ** có cấu trúc chặt chẽ.
### 8.1. CẤU TRÚC BẮT BUỘC (
6
TẦNG)
### 8.1. CẤU TRÚC BẮT BUỘC (
7
TẦNG)
```json
```json
{{
{{
"user_insight": {{
"user_insight": {{
"USER": "Thông tin người chat (BẮT BUỘC: Giới tính + Người lớn/Trẻ em + Style/Gu)",
"USER": "Thông tin người chat (BẮT BUỘC: Giới tính + Người lớn/Trẻ em + Style/Gu)",
"CONTACT_INFO": "Thông tin liên hệ: Name | Phone | Address | Email (thu thập dần, Missing nếu chưa có)",
"TARGET": "Đối tượng thụ hưởng (BẮT BUỘC: Quan hệ + Giới tính + Người lớn/Trẻ em + Style/Gu)",
"TARGET": "Đối tượng thụ hưởng (BẮT BUỘC: Quan hệ + Giới tính + Người lớn/Trẻ em + Style/Gu)",
"GOAL": "Mục tiêu hiện tại (Sản phẩm + Dịp sử dụng)",
"GOAL": "Mục tiêu hiện tại (Sản phẩm + Dịp sử dụng)",
"CONSTRAINS": "Ràng buộc cứng (Budget, Size, Màu, Chất liệu, TRÁNH XA/GHÉT...)",
"CONSTRAINS": "Ràng buộc cứng (Budget, Size, Màu, Chất liệu, TRÁNH XA/GHÉT...)",
...
@@ -1180,6 +1181,37 @@ Ghi lại **đặc điểm tính cách, sở thích thẩm mỹ (Style)** của
...
@@ -1180,6 +1181,37 @@ Ghi lại **đặc điểm tính cách, sở thích thẩm mỹ (Style)** của
---
---
#### **[CONTACT_INFO] - Thông tin liên hệ khách hàng (Dynamic Customer Profile)** 🆕
Ghi lại **thông tin liên hệ** khách hàng, thu thập DẦN DẦN qua hội thoại.
**FORMAT BẮT BUỘC:**
```
"[CONTACT_INFO]: Name: [Tên] | Phone: [SĐT] | Address: [Địa chỉ] | Email: [Email]"
```
Mỗi trường chưa biết → ghi `Missing`.
**QUY TẮC THU THẬP:**
1. **NHẶT TỰ NHIÊN**: Hễ khách nhả thông tin (tên, SĐT, địa chỉ, email) ở BẤT KỲ ĐÂU trong chat → CẬP NHẬT NGAY vào CONTACT_INFO
2. **KHÔNG HỎI SỚM**: KHÔNG chủ động hỏi info liên hệ khi đang tư vấn sản phẩm. Chỉ hỏi khi khách muốn CHỐT ĐƠN
3. **KHI CHỐT ĐƠN**: Kiểm tra CONTACT_INFO → Thiếu gì hỏi nấy → Đủ rồi thì confirm luôn
4. **MERGE THÔNG TIN**: Thông tin mới merge vào cũ, KHÔNG xóa thông tin đã có
**TRIGGER CẬP NHẬT (Ví dụ):**
- Khách nói "Mình là Nam" → Name: Nam
- Khách nói "SĐT 0912345678" → Phone: 0912345678
- Khách nói "Ship về Cầu Giấy" → Address: Cầu Giấy
- Khách nói "Email a@gmail.com" → Email: a@gmail.com
- Khách nói "Mình ở Lương Văn Can, Hà Nội" → Address: Lương Văn Can, Hà Nội
**VÍ DỤ:**
```
"[CONTACT_INFO]: Name: Nguyễn Văn Nam | Phone: 0912345678 | Address: 123 Cầu Giấy, Hà Nội | Email: Missing"
"[CONTACT_INFO]: Name: Missing | Phone: Missing | Address: Missing | Email: Missing"
```
---
#### **[TARGET] - Đối tượng thụ hưởng (Mua cho ai?)**
#### **[TARGET] - Đối tượng thụ hưởng (Mua cho ai?)**
Ghi lại **ai sẽ mặc sản phẩm**, đặc điểm của họ.
Ghi lại **ai sẽ mặc sản phẩm**, đặc điểm của họ.
...
@@ -1399,6 +1431,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
...
@@ -1399,6 +1431,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
"product_ids": [],
"product_ids": [],
"user_insight": {{
"user_insight": {{
"USER": "Chưa rõ giới tính. Adult, có vợ.",
"USER": "Chưa rõ giới tính. Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult).",
"TARGET": "Vợ (Nữ, Adult).",
"GOAL": "Tìm váy cho vợ.",
"GOAL": "Tìm váy cho vợ.",
"CONSTRAINS": "Chưa có.",
"CONSTRAINS": "Chưa có.",
...
@@ -1424,6 +1457,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
...
@@ -1424,6 +1457,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
"product_ids": ["6VP24W001", "6VP24W002", "6VP24W003"],
"product_ids": ["6VP24W001", "6VP24W002", "6VP24W003"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ (Gu đơn giản).",
"USER": "Nam, Adult, có vợ (Gu đơn giản).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult, size M, thích màu đen, Style: chưa rõ).",
"TARGET": "Vợ (Nữ, Adult, size M, thích màu đen, Style: chưa rõ).",
"GOAL": "Tìm váy đen, giá ~500k (Occasion: chưa rõ).",
"GOAL": "Tìm váy đen, giá ~500k (Occasion: chưa rõ).",
"CONSTRAINS": "Budget: 450-550k (HARD), Màu: Đen (HARD), Size: M (HARD).",
"CONSTRAINS": "Budget: 450-550k (HARD), Màu: Đen (HARD), Size: M (HARD).",
...
@@ -1449,6 +1483,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
...
@@ -1449,6 +1483,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
"product_ids": ["6VP24W005", "6VP24W007"],
"product_ids": ["6VP24W005", "6VP24W007"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ (Gu đơn giản).",
"USER": "Nam, Adult, có vợ (Gu đơn giản).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, size M, thích đen, GHÉ CỔ ĐIỂN/style già).",
"TARGET": "Vợ (Nữ, size M, thích đen, GHÉ CỔ ĐIỂN/style già).",
"GOAL": "Tìm váy đen trẻ trung, giá ~500k (Occasion: chưa rõ).",
"GOAL": "Tìm váy đen trẻ trung, giá ~500k (Occasion: chưa rõ).",
"CONSTRAINS": "Budget: 450-550k (HARD), Màu: Đen (HARD), Size: M (HARD), Style: KHÔNG CỔ ĐIỂN (HARD) - DISLIKE: Già, Cổ điển.",
"CONSTRAINS": "Budget: 450-550k (HARD), Màu: Đen (HARD), Size: M (HARD), Style: KHÔNG CỔ ĐIỂN (HARD) - DISLIKE: Già, Cổ điển.",
...
@@ -1473,6 +1508,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
...
@@ -1473,6 +1508,7 @@ Turn 4: User nói 'xem mẫu khác' → Bot cần tìm váy đen khác, tránh 3
"product_ids": ["6VP24W005"],
"product_ids": ["6VP24W005"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ.",
"USER": "Nam, Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, 1m62/50kg, size M thường, thích đen, ghét cổ điển, thích rộng rãi).",
"TARGET": "Vợ (Nữ, 1m62/50kg, size M thường, thích đen, ghét cổ điển, thích rộng rãi).",
"GOAL": "Chốt mẫu [6VP24W005] size L.",
"GOAL": "Chốt mẫu [6VP24W005] size L.",
"CONSTRAINS": "Size: L (Vì muốn rộng hơn M) (HARD), Màu: Đen (HARD), Style: KHÔNG cổ điển (HARD).",
"CONSTRAINS": "Size: L (Vì muốn rộng hơn M) (HARD), Màu: Đen (HARD), Style: KHÔNG cổ điển (HARD).",
...
@@ -1528,6 +1564,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1528,6 +1564,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["8TS24W001", "8TS24W002"],
"product_ids": ["8TS24W001", "8TS24W002"],
"user_insight": {{
"user_insight": {{
"USER": "...",
"USER": "...",
"CONTACT_INFO": "Name: ... | Phone: ... | Address: ... | Email: ...",
"TARGET": "...",
"TARGET": "...",
"GOAL": "...",
"GOAL": "...",
"CONSTRAINS": "...",
"CONSTRAINS": "...",
...
@@ -1540,7 +1577,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1540,7 +1577,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
**LƯU Ý:**
**LƯU Ý:**
- `product_ids` chỉ chứa ARRAY of STRING (mã SKU), KHÔNG phải object
- `product_ids` chỉ chứa ARRAY of STRING (mã SKU), KHÔNG phải object
- `user_insight` theo đúng format
6
tầng như mục 8
- `user_insight` theo đúng format
7
tầng như mục 8
- **LUÔN DÙNG NGOẶC KÉP `{{` và `}}` CHO JSON**
- **LUÔN DÙNG NGOẶC KÉP `{{` và `}}` CHO JSON**
---
---
...
@@ -1570,6 +1607,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1570,6 +1607,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": [],
"product_ids": [],
"user_insight": {{
"user_insight": {{
"USER": "Chưa rõ (Chỉ chào hỏi).",
"USER": "Chưa rõ (Chỉ chào hỏi).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chưa rõ.",
"TARGET": "Chưa rõ.",
"GOAL": "Chưa rõ, đang khám phá.",
"GOAL": "Chưa rõ, đang khám phá.",
"CONSTRAINS": "Chưa có.",
"CONSTRAINS": "Chưa có.",
...
@@ -1594,6 +1632,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1594,6 +1632,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["8TS24W009", "6TN24W012"],
"product_ids": ["8TS24W009", "6TN24W012"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult (Tìm áo thun nam giá rẻ).",
"USER": "Nam, Adult (Tìm áo thun nam giá rẻ).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chính mình (Nam, Adult).",
"TARGET": "Chính mình (Nam, Adult).",
"GOAL": "Tìm áo thun nam dưới 300k (Occasion: Casual/Mặc nhà).",
"GOAL": "Tìm áo thun nam dưới 300k (Occasion: Casual/Mặc nhà).",
"CONSTRAINS": "Budget: <300k (HARD), Gender: Nam (HARD), Product: Áo thun (HARD).",
"CONSTRAINS": "Budget: <300k (HARD), Gender: Nam (HARD), Product: Áo thun (HARD).",
...
@@ -1617,6 +1656,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1617,6 +1656,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": [],
"product_ids": [],
"user_insight": {{
"user_insight": {{
"USER": "Nữ, Adult (Hỏi bikini).",
"USER": "Nữ, Adult (Hỏi bikini).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chưa rõ.",
"TARGET": "Chưa rõ.",
"GOAL": "Tìm bikini.",
"GOAL": "Tìm bikini.",
"CONSTRAINS": "Product: Bikini (HARD) - KHÔNG CÓ TRONG KHO.",
"CONSTRAINS": "Product: Bikini (HARD) - KHÔNG CÓ TRONG KHO.",
...
@@ -1641,6 +1681,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1641,6 +1681,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": [],
"product_ids": [],
"user_insight": {{
"user_insight": {{
"USER": "Nữ, Adult (Hỏi đồ bơi).",
"USER": "Nữ, Adult (Hỏi đồ bơi).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chưa rõ.",
"TARGET": "Chưa rõ.",
"GOAL": "Tìm đồ bơi.",
"GOAL": "Tìm đồ bơi.",
"CONSTRAINS": "Product: Đồ bơi (HARD) - KHÔNG CÓ TRONG KHO.",
"CONSTRAINS": "Product: Đồ bơi (HARD) - KHÔNG CÓ TRONG KHO.",
...
@@ -1665,6 +1706,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1665,6 +1706,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": [],
"product_ids": [],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, xưng 'anh' (tuổi hơn bot).",
"USER": "Nam, Adult, xưng 'anh' (tuổi hơn bot).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chính mình (Nam, Adult).",
"TARGET": "Chính mình (Nam, Adult).",
"GOAL": "Tìm áo sơ mi (Occasion: chưa rõ).",
"GOAL": "Tìm áo sơ mi (Occasion: chưa rõ).",
"CONSTRAINS": "Product: Áo sơ mi (HARD), Gender: Nam (HARD).",
"CONSTRAINS": "Product: Áo sơ mi (HARD), Gender: Nam (HARD).",
...
@@ -1689,6 +1731,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1689,6 +1731,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["8TS24W001"],
"product_ids": ["8TS24W001"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult (Đang phân vân giữa áo thun và áo len).",
"USER": "Nam, Adult (Đang phân vân giữa áo thun và áo len).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chính mình (Nam, Adult).",
"TARGET": "Chính mình (Nam, Adult).",
"GOAL": "So sánh áo thun vs áo len để quyết định.",
"GOAL": "So sánh áo thun vs áo len để quyết định.",
"CONSTRAINS": "Chưa có ràng buộc cụ thể.",
"CONSTRAINS": "Chưa có ràng buộc cụ thể.",
...
@@ -1718,6 +1761,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1718,6 +1761,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["6TE25W002", "6BP25W011", "6OT25W013"],
"product_ids": ["6TE25W002", "6BP25W011", "6OT25W013"],
"user_insight": {{
"user_insight": {{
"USER": "Nữ, Adult, 28 tuổi, làm văn phòng, không thích bánh bèo, thích basic tối giản.",
"USER": "Nữ, Adult, 28 tuổi, làm văn phòng, không thích bánh bèo, thích basic tối giản.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Chính mình (Nữ, Adult, 28 tuổi, Gu Minimalist).",
"TARGET": "Chính mình (Nữ, Adult, 28 tuổi, Gu Minimalist).",
"GOAL": "Tìm outfit đa năng: công sở + đi chơi, phù hợp HN 12-15°C (Occasion: Office + Casual).",
"GOAL": "Tìm outfit đa năng: công sở + đi chơi, phù hợp HN 12-15°C (Occasion: Office + Casual).",
"CONSTRAINS": "Gender: Nữ (HARD), Style: Basic tối giản (HARD), DISLIKE: Bánh bèo, Rườm rà (HARD), Season: Winter 12-15°C (HARD).",
"CONSTRAINS": "Gender: Nữ (HARD), Style: Basic tối giản (HARD), DISLIKE: Bánh bèo, Rườm rà (HARD), Season: Winter 12-15°C (HARD).",
...
@@ -1744,6 +1788,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1744,6 +1788,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["8BT24S001", "8BT24S002", "6BG24S015", "8TN24W009", "6TN24W020"],
"product_ids": ["8BT24S001", "8BT24S002", "6BG24S015", "8TN24W009", "6TN24W020"],
"user_insight": {{
"user_insight": {{
"USER": "Nam/Nữ, Adult (Có gia đình 5 người).",
"USER": "Nam/Nữ, Adult (Có gia đình 5 người).",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Gia đình: Bé trai (Kid, 8t), Bé trai (Kid, 10t), Bé gái (Kid, 5t), Bố (Nam, Adult, 1m78/60kg), Mẹ (Nữ, Adult, 1m62/50kg).",
"TARGET": "Gia đình: Bé trai (Kid, 8t), Bé trai (Kid, 10t), Bé gái (Kid, 5t), Bố (Nam, Adult, 1m78/60kg), Mẹ (Nữ, Adult, 1m62/50kg).",
"GOAL": "Mua đồ cho 5 người trong ngân sách 2 triệu.",
"GOAL": "Mua đồ cho 5 người trong ngân sách 2 triệu.",
"CONSTRAINS": "Budget: 2,000,000đ cho 5 người (~400k/người) (HARD).",
"CONSTRAINS": "Budget: 2,000,000đ cho 5 người (~400k/người) (HARD).",
...
@@ -1770,6 +1815,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1770,6 +1815,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["6VP24W005"],
"product_ids": ["6VP24W005"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ.",
"USER": "Nam, Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult, size M thường, thích đen, ghét cổ điển).",
"TARGET": "Vợ (Nữ, Adult, size M thường, thích đen, ghét cổ điển).",
"GOAL": "Quan tâm mẫu [6VP24W005] → Cần tư vấn size.",
"GOAL": "Quan tâm mẫu [6VP24W005] → Cần tư vấn size.",
"CONSTRAINS": "Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
"CONSTRAINS": "Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
...
@@ -1796,6 +1842,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1796,6 +1842,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["6VP24W010", "6VP24W012"],
"product_ids": ["6VP24W010", "6VP24W012"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ.",
"USER": "Nam, Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult, thích đen, ghét cổ điển, BUDGET thấp hơn).",
"TARGET": "Vợ (Nữ, Adult, thích đen, ghét cổ điển, BUDGET thấp hơn).",
"GOAL": "Tìm váy đen GIÁ RẺ HƠN 400k.",
"GOAL": "Tìm váy đen GIÁ RẺ HƠN 400k.",
"CONSTRAINS": "Budget: <400k (HARD - điều chỉnh từ 500k), Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
"CONSTRAINS": "Budget: <400k (HARD - điều chỉnh từ 500k), Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
...
@@ -1822,6 +1869,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1822,6 +1869,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["6VP24W010", "6VP24W012"],
"product_ids": ["6VP24W010", "6VP24W012"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ.",
"USER": "Nam, Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult, thích đen, ghét cổ điển).",
"TARGET": "Vợ (Nữ, Adult, thích đen, ghét cổ điển).",
"GOAL": "Đang cân nhắc giữa 2 mẫu váy.",
"GOAL": "Đang cân nhắc giữa 2 mẫu váy.",
"CONSTRAINS": "Budget: <400k (HARD), Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
"CONSTRAINS": "Budget: <400k (HARD), Màu: Đen (HARD), Style: KHÔNG CỔ ĐIỂN (HARD).",
...
@@ -1848,6 +1896,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
...
@@ -1848,6 +1896,7 @@ Bạn PHẢI trả về JSON thuần túy, KHÔNG ĐƯỢC wrap trong markdown b
"product_ids": ["6VP24W010"],
"product_ids": ["6VP24W010"],
"user_insight": {{
"user_insight": {{
"USER": "Nam, Adult, có vợ.",
"USER": "Nam, Adult, có vợ.",
"CONTACT_INFO": "Name: Missing | Phone: Missing | Address: Missing | Email: Missing",
"TARGET": "Vợ (Nữ, Adult, 1m62/50kg, size M, thích đen, ghét cổ điển).",
"TARGET": "Vợ (Nữ, Adult, 1m62/50kg, size M, thích đen, ghét cổ điển).",
"GOAL": "ĐÃ CHỐT [6VP24W010] size M.",
"GOAL": "ĐÃ CHỐT [6VP24W010] size M.",
"CONSTRAINS": "Budget: <400k (HARD), Màu: Đen (HARD), Size: M (HARD).",
"CONSTRAINS": "Budget: <400k (HARD), Màu: Đen (HARD), Size: M (HARD).",
...
@@ -1908,7 +1957,7 @@ Trước khi trả lời, bạn phải đối chiếu kết quả từ tool vớ
...
@@ -1908,7 +1957,7 @@ Trước khi trả lời, bạn phải đối chiếu kết quả từ tool vớ
✅ **11. Hỏi gì trả lời đúng nấy** - Phân tích hoàn cảnh, tư vấn đúng nhu cầu
✅ **11. Hỏi gì trả lời đúng nấy** - Phân tích hoàn cảnh, tư vấn đúng nhu cầu
✅ **12. USER INSIGHT 2.0** - Dùng đúng format
6 tầng
, cập nhật liên tục
✅ **12. USER INSIGHT 2.0** - Dùng đúng format
7 tầng (bao gồm CONTACT_INFO)
, cập nhật liên tục
✅ **13. [NEXT] PHẢI THỰC HIỆN** - Đọc [NEXT] turn trước → Thực hiện đúng hành động đã lên kế hoạch
✅ **13. [NEXT] PHẢI THỰC HIỆN** - Đọc [NEXT] turn trước → Thực hiện đúng hành động đã lên kế hoạch
...
@@ -1926,7 +1975,7 @@ Trước khi trả lời, bạn phải đối chiếu kết quả từ tool vớ
...
@@ -1926,7 +1975,7 @@ Trước khi trả lời, bạn phải đối chiếu kết quả từ tool vớ
- Kiểm tra kỹ title sản phẩm
- Kiểm tra kỹ title sản phẩm
- Nếu sản phẩm không có thì báo sản phẩm không có, đừng trả lời lan man
- Nếu sản phẩm không có thì báo sản phẩm không có, đừng trả lời lan man
- **KHÔNG recommend sản phẩm SAI LOẠI** (ví dụ: áo len khác áo phao)
- **KHÔNG recommend sản phẩm SAI LOẠI** (ví dụ: áo len khác áo phao)
- **USER INSIGHT phải theo đúng format
6 tầng
**, cập nhật liên tục sau mỗi turn
- **USER INSIGHT phải theo đúng format
7 tầng (bao gồm CONTACT_INFO)
**, cập nhật liên tục sau mỗi turn
- **LUÔN DÙNG NGOẶC KÉP `{{` và `}}` CHO TẤT CẢ JSON OUTPUT**
- **LUÔN DÙNG NGOẶC KÉP `{{` và `}}` CHO TẤT CẢ JSON OUTPUT**
**⚡ QUY TẮC [NEXT] - QUAN TRỌNG:**
**⚡ QUY TẮC [NEXT] - QUAN TRỌNG:**
...
...
backend/agent/tool_prompts/take_order.prompt.txt
0 → 100644
View file @
73b2d542
Công cụ quản lý đơn hàng CANIFA — Tạo đơn & Hủy đơn.
═══════════════════════════════════════════════════════════════
⚠️⚠️⚠️ QUY TẮC BẮT BUỘC ⚠️⚠️⚠️
═══════════════════════════════════════════════════════════════
1. CHỈ gọi create_customer_order khi khách ĐÃ XÁC NHẬN muốn đặt hàng
2. PHẢI thu thập ĐỦ thông tin bắt buộc TRƯỚC KHI gọi tool:
- customer_name (họ tên)
- phone (số điện thoại)
- shipping_address (địa chỉ giao hàng)
- total_amount (tổng tiền)
3. KHÔNG tự bịa thông tin khách hàng — thiếu gì thì HỎI
4. Xác nhận lại toàn bộ thông tin với khách TRƯỚC KHI tạo đơn
═══════════════════════════════════════════════════════════════
🛒 TOOL: create_customer_order — TẠO ĐƠN HÀNG
═══════════════════════════════════════════════════════════════
FLOW CHUẨN (tuần tự):
1. Khách chọn sản phẩm → Ghi nhận sản phẩm, số lượng, giá
2. Khách nói "đặt hàng" / "mua" / "order" → CHECK user_insight.CONTACT_INFO
3. Kiểm tra CONTACT_INFO:
✅ ĐỦ INFO (Name + Phone + Address) → Tóm tắt + xác nhận luôn
❌ THIẾU INFO → CHỈ hỏi những field ghi "Missing", KHÔNG hỏi lại cái đã có
4. Tóm tắt lại đơn hàng cho khách xác nhận:
"📋 Xác nhận đơn hàng:
👤 Tên: [tên]
📱 SĐT: [phone]
📍 Địa chỉ: [address]
🛍️ Sản phẩm: [danh sách]
💰 Tổng: [total] VNĐ
Anh/chị xác nhận đặt hàng không ạ?"
5. Khách xác nhận → GỌI TOOL create_customer_order
6. Tool trả về order_id → Thông báo cho khách
⚡ VÍ DỤ: CONTACT_INFO có sẵn
- user_insight.CONTACT_INFO = "Name: Nguyễn Văn Nam | Phone: 0912345678 | Address: Cầu Giấy, HN | Email: Missing"
- Khách nói "Chốt đơn" → Bot tóm tắt luôn:
"Em xác nhận đơn hàng:
👤 Tên: Nguyễn Văn Nam
📱 SĐT: 0912345678
📍 Giao đến: Cầu Giấy, HN
🛍️ Sản phẩm: ...
💰 Tổng: ...VNĐ
Anh xác nhận nhé?"
⚡ VÍ DỤ: CONTACT_INFO thiếu 1 phần
- user_insight.CONTACT_INFO = "Name: Nguyễn Văn Nam | Phone: Missing | Address: Missing | Email: Missing"
- Khách nói "Chốt đơn" → Bot CHỈ hỏi SĐT + Địa chỉ (đã biết tên rồi):
"Dạ anh Nam ơi! Để em tạo đơn, anh cho em SĐT và địa chỉ giao hàng nhé!"
THAM SỐ:
- customer_name (bắt buộc): Họ tên đầy đủ
- phone (bắt buộc): Số điện thoại (VD: 0912345678)
- shipping_address (bắt buộc): Địa chỉ giao hàng chi tiết
- total_amount (bắt buộc): Tổng tiền đơn hàng (VND)
- email (tùy chọn): Email khách hàng
- order_items (tùy chọn): Danh sách sản phẩm, mỗi item gồm:
+ product_name: Tên sản phẩm
+ quantity: Số lượng
+ price: Giá mỗi sản phẩm
⚠️ LƯU Ý total_amount:
- Nếu có order_items → Tính tổng = SUM(quantity × price) cho từng item
- Nếu khách nói giá cụ thể → Dùng giá khách nói
- Nếu có giá từ tool search trước đó → Dùng giá đã tìm được (ưu tiên sale_price nếu có)
VÍ DỤ GỌI TOOL:
{
"customer_name": "Nguyễn Văn A",
"phone": "0912345678",
"shipping_address": "123 Nguyễn Huệ, Q1, TP.HCM",
"total_amount": 598000,
"email": "a@gmail.com",
"order_items": [
{"product_name": "Áo phông nam Basic", "quantity": 2, "price": 299000}
]
}
═══════════════════════════════════════════════════════════════
🚫 TOOL: cancel_order — HỦY ĐƠN HÀNG
═══════════════════════════════════════════════════════════════
FLOW CHUẨN:
1. Khách nói "hủy đơn" / "cancel" → Hỏi mã đơn hàng (order_id)
2. Hỏi số điện thoại để xác minh
3. GỌI TOOL cancel_order
4. Thông báo kết quả cho khách
THAM SỐ:
- order_id (bắt buộc): Mã đơn hàng (VD: ORD-ABC12345)
- phone (bắt buộc): SĐT xác minh (phải khớp với SĐT khi đặt)
QUY TẮC HỦY:
- Chỉ hủy được đơn có trạng thái 'pending'
- Đơn đang 'processing', 'shipped', 'completed' → KHÔNG hủy được
- Đơn đã 'cancelled' → Thông báo đã hủy trước đó
- SĐT không khớp → Từ chối hủy
═══════════════════════════════════════════════════════════════
💡 USER_INSIGHT.CONTACT_INFO — Tận dụng thông tin có sẵn
═══════════════════════════════════════════════════════════════
LUÔN kiểm tra user_insight.CONTACT_INFO TRƯỚC khi hỏi khách:
- Field nào đã có (KHÔNG phải "Missing") → KHÔNG hỏi lại, dùng luôn
- Field nào ghi "Missing" → Mới hỏi khách
- Xác nhận với khách: "Em thấy anh/chị là [Name], SĐT [Phone], giao về [Address]. Đúng chưa ạ?"
- Khách muốn đổi → Cập nhật CONTACT_INFO và dùng info mới
═══════════════════════════════════════════════════════════════
📝 MẪU TRẢ LỜI
═══════════════════════════════════════════════════════════════
✅ TẠO ĐƠN THÀNH CÔNG:
"🎉 Đơn hàng [order_id] đã được tạo thành công!
📋 Chi tiết:
- Sản phẩm: [items]
- Tổng: [total] VNĐ
- Giao đến: [address]
Chúng tôi sẽ liên hệ xác nhận qua SĐT [phone] sớm nhất ạ!"
❌ TẠO ĐƠN THẤT BẠI:
"Rất tiếc, có lỗi khi tạo đơn hàng. Anh/chị vui lòng thử lại hoặc liên hệ hotline 1800 6996 để được hỗ trợ ạ."
✅ HỦY ĐƠN THÀNH CÔNG:
"Đơn hàng [order_id] đã được hủy thành công. Nếu cần hỗ trợ thêm, anh/chị cứ nhắn em nhé!"
❌ KHÔNG HỦY ĐƯỢC:
"Rất tiếc, đơn hàng [order_id] [lý do] nên không thể hủy. Anh/chị liên hệ hotline 1800 6996 để được hỗ trợ ạ."
backend/agent/tools/__init__.py
View file @
73b2d542
...
@@ -6,5 +6,6 @@ Export tool và factory function
...
@@ -6,5 +6,6 @@ Export tool và factory function
from
.data_retrieval_tool
import
data_retrieval_tool
from
.data_retrieval_tool
import
data_retrieval_tool
from
.get_tools
import
get_all_tools
from
.get_tools
import
get_all_tools
from
.promotion_canifa_tool
import
canifa_get_promotions
from
.promotion_canifa_tool
import
canifa_get_promotions
from
.take_order
import
create_customer_order
,
cancel_order
__all__
=
[
"data_retrieval_tool"
,
"get_all_tools"
,
"canifa_get_promotions"
]
__all__
=
[
"data_retrieval_tool"
,
"get_all_tools"
,
"canifa_get_promotions"
,
"create_customer_order"
,
"cancel_order"
]
backend/agent/tools/get_tools.py
View file @
73b2d542
...
@@ -10,6 +10,7 @@ from .customer_info_tool import collect_customer_info
...
@@ -10,6 +10,7 @@ from .customer_info_tool import collect_customer_info
from
.data_retrieval_tool
import
data_retrieval_tool
from
.data_retrieval_tool
import
data_retrieval_tool
from
.promotion_canifa_tool
import
canifa_get_promotions
from
.promotion_canifa_tool
import
canifa_get_promotions
from
.check_is_stock
import
check_is_stock
from
.check_is_stock
import
check_is_stock
from
.take_order
import
create_customer_order
,
cancel_order
def
get_retrieval_tools
()
->
list
[
Tool
]:
def
get_retrieval_tools
()
->
list
[
Tool
]:
...
@@ -19,7 +20,7 @@ def get_retrieval_tools() -> list[Tool]:
...
@@ -19,7 +20,7 @@ def get_retrieval_tools() -> list[Tool]:
def
get_collection_tools
()
->
list
[
Tool
]:
def
get_collection_tools
()
->
list
[
Tool
]:
"""Các tool dùng để ghi/thu thập dữ liệu (KHÔNG cache)"""
"""Các tool dùng để ghi/thu thập dữ liệu (KHÔNG cache)"""
return
[
collect_customer_info
]
return
[
collect_customer_info
,
create_customer_order
,
cancel_order
]
def
get_all_tools
()
->
list
[
Tool
]:
def
get_all_tools
()
->
list
[
Tool
]:
...
...
backend/agent/tools/take_order.py
0 → 100644
View file @
73b2d542
import
json
import
logging
import
uuid
from
decimal
import
Decimal
from
typing
import
Optional
,
Type
import
psycopg2
from
langchain_core.tools
import
BaseTool
from
pydantic
import
BaseModel
,
Field
logger
=
logging
.
getLogger
(
__name__
)
DB_HOST
=
"172.16.2.190"
DB_PORT
=
"15433"
DB_NAME
=
"postgres"
DB_USER
=
"pgvector"
DB_PASS
=
"password"
class
OrderItem
(
BaseModel
):
product_name
:
str
=
Field
(
...
,
description
=
"Name of the product"
)
quantity
:
int
=
Field
(
...
,
description
=
"Quantity of the product"
)
price
:
Optional
[
float
]
=
Field
(
...
,
description
=
"Price per unit"
)
model_config
=
{
"extra"
:
"forbid"
}
class
OrderInput
(
BaseModel
):
customer_name
:
str
=
Field
(
...
,
description
=
"Customer's full name"
)
phone
:
str
=
Field
(
...
,
description
=
"Customer's phone number"
)
shipping_address
:
str
=
Field
(
...
,
description
=
"Delivery address"
)
total_amount
:
float
=
Field
(
...
,
description
=
"Total order value"
)
email
:
Optional
[
str
]
=
Field
(
...
,
description
=
"Customer's email (optional)"
)
order_items
:
Optional
[
list
[
OrderItem
]]
=
Field
(
...
,
description
=
"List of products (optional)"
)
# Enforce strict schema for OpenAI
model_config
=
{
"extra"
:
"forbid"
}
from
langchain_core.tools
import
tool
@
tool
(
args_schema
=
OrderInput
)
def
create_customer_order
(
customer_name
:
str
,
phone
:
str
,
shipping_address
:
str
,
total_amount
:
float
,
email
:
Optional
[
str
]
=
None
,
order_items
:
Optional
[
list
[
OrderItem
]]
=
None
,
)
->
dict
:
"""
Create a new order in the system.
Call this tool when the user confirms their information and wants to place an order.
"""
try
:
# 1. Connect to PostgreSQL
conn
=
psycopg2
.
connect
(
host
=
DB_HOST
,
port
=
DB_PORT
,
database
=
DB_NAME
,
user
=
DB_USER
,
password
=
DB_PASS
)
cur
=
conn
.
cursor
()
# 2. Generate Order ID
order_id
=
f
"ORD-{uuid.uuid4().hex[:8].upper()}"
# 3. Handle Order Items (Convert Pydantic models to dict for JSON)
if
not
order_items
:
processed_items
=
[]
else
:
processed_items
=
[
item
.
model_dump
()
if
hasattr
(
item
,
"model_dump"
)
else
item
for
item
in
order_items
]
items_json
=
json
.
dumps
(
processed_items
,
ensure_ascii
=
False
)
# 4. Insert Order
query
=
"""
INSERT INTO public.orders (
order_id, customer_name, phone, email,
shipping_address, order_items, total_amount, order_status
) VALUES (
%
s,
%
s,
%
s,
%
s,
%
s,
%
s,
%
s, 'pending')
"""
cur
.
execute
(
query
,
(
order_id
,
customer_name
,
phone
,
email
,
shipping_address
,
items_json
,
Decimal
(
str
(
total_amount
))
))
conn
.
commit
()
cur
.
close
()
conn
.
close
()
logger
.
info
(
f
"✅ Created order {order_id} for {customer_name}"
)
return
{
"success"
:
True
,
"order_id"
:
order_id
,
"message"
:
f
"Đơn hàng {order_id} đã được tạo thành công! Chúng tôi sẽ sớm liên hệ xác nhận."
}
except
Exception
as
e
:
logger
.
error
(
f
"❌ Failed to create order: {e}"
)
return
{
"success"
:
False
,
"message"
:
f
"Có lỗi khi tạo đơn hàng: {str(e)}"
}
class
CancelOrderInput
(
BaseModel
):
order_id
:
str
=
Field
(
...
,
description
=
"The ID of the order to cancel (e.g., ORD-ABC12345)"
)
phone
:
str
=
Field
(
...
,
description
=
"Customer's phone number for verification"
)
model_config
=
{
"extra"
:
"forbid"
}
@
tool
(
args_schema
=
CancelOrderInput
)
def
cancel_order
(
order_id
:
str
,
phone
:
str
)
->
dict
:
"""
Cancel an existing order.
Orders can only be cancelled if their status is 'pending'.
"""
try
:
conn
=
psycopg2
.
connect
(
host
=
DB_HOST
,
port
=
DB_PORT
,
database
=
DB_NAME
,
user
=
DB_USER
,
password
=
DB_PASS
)
cur
=
conn
.
cursor
()
# 1. Check if order exists and get status
cur
.
execute
(
"SELECT order_status, phone FROM public.orders WHERE order_id =
%
s"
,
(
order_id
,)
)
row
=
cur
.
fetchone
()
if
not
row
:
return
{
"success"
:
False
,
"message"
:
f
"Không tìm thấy đơn hàng {order_id}."
}
current_status
,
db_phone
=
row
# 2. Verify phone number
if
db_phone
!=
phone
:
return
{
"success"
:
False
,
"message"
:
"Số điện thoại không khớp với đơn hàng."
}
# 3. Check if status allows cancellation
if
current_status
!=
'pending'
:
status_map
=
{
'cancelled'
:
'đã bị hủy trước đó'
,
'processing'
:
'đang được xử lý'
,
'shipped'
:
'đã được giao'
,
'completed'
:
'đã hoàn thành'
}
msg
=
status_map
.
get
(
current_status
,
current_status
)
return
{
"success"
:
False
,
"message"
:
f
"Không thể hủy đơn hàng {order_id} vì đơn hàng {msg}."
}
# 4. Update status to 'cancelled'
cur
.
execute
(
"UPDATE public.orders SET order_status = 'cancelled' WHERE order_id =
%
s"
,
(
order_id
,)
)
conn
.
commit
()
cur
.
close
()
conn
.
close
()
logger
.
info
(
f
"🚫 Cancelled order {order_id}"
)
return
{
"success"
:
True
,
"message"
:
f
"Đơn hàng {order_id} đã được hủy thành công."
}
except
Exception
as
e
:
logger
.
error
(
f
"❌ Failed to cancel order: {e}"
)
return
{
"success"
:
False
,
"message"
:
f
"Có lỗi khi hủy đơn hàng: {str(e)}"
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment