Một agent chỉ biết trò chuyện thì vô dụng. Giá trị thật đến khi nó hành động: tra cứu đơn hàng, đặt lịch, tạo ticket. Bài này dạy cách gắn tool vào voice agent mà không phá vỡ trải nghiệm thời gian thực.
Vòng đời một lượt có tool
- Khách nói yêu cầu → ASR → LLM.
- LLM quyết định gọi tool (ví dụ
get_order_status(order_id)). - Agent phát filler audio trong lúc chờ tool ("Dạ em kiểm tra giúp mình chút nhé").
- Tool trả kết quả → LLM sinh câu trả lời cuối → TTS.
async def respond_with_tools(history):
resp = await llm.chat(history, tools=TOOLS)
if resp.tool_calls:
await tts_say("Dạ em kiểm tra giúp mình chút nhé.") # filler
for call in resp.tool_calls:
result = await dispatch(call.name, call.args) # gọi API thật
history.append(tool_result(call, result))
resp = await llm.chat(history, tools=TOOLS) # vòng 2
await stream_tts(resp.text)
Filler: vũ khí chống độ trễ tool
Tool gọi API backend có thể mất 1-3 giây. Trong voice, im lặng 2 giây là "chết lâm sàng". Giải pháp: phát ngay một câu đệm khi phát hiện có tool_call, trong lúc tool chạy nền. Đây là mẹo tâm lý quan trọng nhất khi có nghiệp vụ chậm.
Các bước triển khai filler:
- Chuẩn bị vài câu filler cache sẵn theo ngữ cảnh (kiểm tra / đặt lịch / thanh toán).
- Ngay khi LLM báo tool_call, phát filler tương ứng.
- Chạy tool bất đồng bộ, khi có kết quả thì tiếp tục câu trả lời.
Xác nhận trước hành động không thể hoàn tác
Voice dễ nghe nhầm. Với hành động ghi/thanh toán/hủy, luôn xác nhận lại:
> Agent: "Em xác nhận đặt lịch cắt tóc lúc 3 giờ chiều mai cho mình đúng không ạ?"
> Khách: "Đúng rồi."
> Agent: [gọi book()]
Đọc lại thông tin quan trọng (số tiền, ngày giờ, tên) bằng SSML say-as để tránh nhầm.
Thiết kế tool cho voice
- Tool phải nhanh hoặc bất đồng bộ. Nếu tool chậm, trả về ngay "đang xử lý" rồi thông báo sau.
- Trả dữ liệu gọn. LLM sẽ đọc kết quả thành lời; đừng trả JSON khổng lồ.
- Xử lý lỗi tường minh. Tool thất bại → trả mã lỗi có nghĩa để LLM xin lỗi và đề xuất phương án.
- Idempotent khi có thể. Barge-in/nghe lại có thể gọi tool hai lần; dùng khóa idempotency để tránh đặt lịch trùng.
Khung tư duy: RAIL cho tool trong voice
- Reassure — trấn an bằng filler khi chờ.
- Act — gọi tool, có idempotency.
- Inform — báo kết quả ngắn gọn.
- Log — ghi lại call, args, kết quả để audit.
Khi LLM cần nhiều lượt tool
Đôi khi một yêu cầu cần chuỗi tool (tra khách → tra đơn → tính phí đổi lịch). Đừng để agent im lặng suốt chuỗi:
- Phát filler đầu chuỗi, và một câu "cập nhật" ngắn nếu chuỗi kéo dài ("Dạ em đang đối chiếu thêm chút nhé").
- Chạy các tool độc lập song song để giảm tổng thời gian.
- Đặt giới hạn số vòng tool để tránh lặp vô hạn; quá ngưỡng thì xin lỗi và escalate.
Xử lý khi khách đổi ý giữa chừng
Trong voice, khách hay đổi ý: "À thôi, đặt 4 giờ đi" ngay sau khi bạn vừa xác nhận 3 giờ. Agent phải:
- Ưu tiên thông tin mới nhất khách chủ động nêu.
- Nếu đã lỡ gọi tool với giá trị cũ, dùng idempotency + hành động sửa (update/hủy-tạo-lại) thay vì tạo trùng.
- Xác nhận lại giá trị mới trước khi chốt.
Bảo mật và quyền khi gọi tool
Tool chạm dữ liệu thật nên cần kiểm soát:
- Xác thực khách (OTP/PIN) trước khi cho phép tool nhạy cảm (số dư, chuyển tiền).
- Giới hạn phạm vi mỗi tool đúng nhu cầu (nguyên tắc quyền tối thiểu).
- Không đưa bí mật/khóa vào prompt; tool tự nạp từ môi trường.
- Log có che thông tin nhạy cảm (mask số thẻ) khi audit.
Sai lầm thường gặp
- Im lặng khi tool chạy. Khách tưởng rớt máy. Luôn có filler.
- Không xác nhận hành động ghi. Nghe nhầm "ba giờ" thành "bốn giờ" mà vẫn đặt luôn.
- Trả JSON thô cho LLM đọc. Kết quả tool phải được tóm gọn thành thông tin cần nói.
- Tool không idempotent. Barge-in giữa lúc gọi khiến đặt hai lần, trừ tiền hai lần.
- Chặn luồng chờ tool đồng bộ. Gọi tool phải async, nếu không cả pipeline đứng hình.