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
Expand all
Hide 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):
default
=
"Chưa rõ."
,
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
(
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"
...
...
backend/agent/system_prompt.txt
View file @
73b2d542
This diff is collapsed.
Click to expand it.
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
from
.data_retrieval_tool
import
data_retrieval_tool
from
.get_tools
import
get_all_tools
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
from
.data_retrieval_tool
import
data_retrieval_tool
from
.promotion_canifa_tool
import
canifa_get_promotions
from
.check_is_stock
import
check_is_stock
from
.take_order
import
create_customer_order
,
cancel_order
def
get_retrieval_tools
()
->
list
[
Tool
]:
...
...
@@ -19,7 +20,7 @@ def get_retrieval_tools() -> list[Tool]:
def
get_collection_tools
()
->
list
[
Tool
]:
"""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
]:
...
...
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