Thực hành với LiveKit: voice agent biết nhường lời và nhường máy cho người
Agent biết dừng khi bạn chen ngang trên laptop vẫn chưa chứng minh được gì, vì cách xử lý ngắt lời tùy nơi nó chạy, còn việc chuyển máy tùy SIP trunk của khách.
Tóm tắt nhanh
- Adaptive interruption chỉ bật mặc định khi agent chạy trên LiveKit Cloud hoặc ở dev mode có credentials Cloud, và phiên có mô hình VAD.
- Với câu bắt buộc như lời miễn trừ, hãy dùng allow_interruptions=False cho riêng câu đó thay vì tắt ngắt lời cả phiên.
- Warm transfer cần outbound SIP trunk; cold transfer cần trunk cho phép chuyển cuộc gọi và sẽ kết thúc phiên LiveKit của người gọi.
Bạn chen ngang agent trên laptop và nó dừng đúng lúc. Rồi agent lên môi trường của khách, và hành vi ngắt lời có thể không còn như bạn nghĩ, vì adaptive interruption của LiveKit chỉ bật mặc định trong một số điều kiện cụ thể.
Bản demo và hệ thống nhận cuộc gọi thật khác nhau ở những chi tiết nhỏ như vậy. Hướng dẫn này không dừng ở câu trả lời của LLM mà đi vào những tình huống của một cuộc gọi thật: khách ho giữa câu, khách cắt lời, khách đòi gặp nhân viên.
Nếu làm FDE, đây là phần bạn sẽ phải giải thích với đội vận hành của khách.
Bạn sẽ dựng gì, và cần chuẩn bị gì?
Thử hình dung một hotline bảo hiểm giả định. Agent đọc một câu thông báo pháp lý không được cắt, trả lời câu hỏi, cho phép khách chen ngang, và chuyển máy cho nhân viên khi khách yêu cầu.
Tài liệu của LiveKit yêu cầu Python 3.10 trở lên, và quickstart quản lý dependency bằng uv. Hãy kiểm tra trước khi làm gì khác:
python3 --version
uv --version
Kiểm tra: Python phải từ 3.10 trở lên. Các đoạn code bên dưới được giản lược để làm rõ ý, nên phần khung dự án, cài đặt và tên tham số đầy đủ hãy lấy từ tài liệu LiveKit.
Bước 1: Nối ba nhà cung cấp thành một phiên
Theo quickstart, agent nối ba nhà cung cấp riêng biệt là STT, LLM và TTS qua LiveKit Inference, gói trong một AgentSession. Ba mắt xích này là nơi đầu tiên bạn nhìn vào khi khách kêu agent nghe sai hoặc nói chậm.
from livekit.agents import AgentSession
# Giản lược: chưa chạy được nếu chưa điền đủ theo quickstart
session = AgentSession(
stt="<mô hình STT>",
llm="<mô hình LLM>",
tts="<mô hình TTS>",
# thêm mô hình VAD ở đây, cách khai báo xem quickstart
)
Đoạn trên chưa phải một agent chạy được. Bạn cần lấy từ quickstart bốn thứ: lệnh tạo dự án và cài dependency bằng uv, tên các mô hình STT, LLM, TTS để thay phần trong ngoặc, code khởi động agent và phiên, cùng cách khai báo mô hình VAD.
Kiểm tra: sau khi điền đủ bốn phần, file chạy không lỗi import. Đừng bỏ mô hình VAD, vì đó là một điều kiện để adaptive interruption bật mặc định, và bài đo ở bước 3 phụ thuộc vào nó.
Bước 2: Nghe thử ở console, nhưng đừng tin nó quá sớm
Quickstart có console mode để chạy agent ngay trong terminal, tách biệt với dev mode. Console là cách nhanh nhất để nghe prompt có ổn không.
# Giản lược: tên file theo dự án, cú pháp đầy đủ xem quickstart
uv run agent.py console
uv run agent.py dev
Adaptive interruption chỉ bật mặc định khi agent đã triển khai lên LiveKit Cloud hoặc chạy dev mode với credentials Cloud, đồng thời phiên có mô hình VAD. Console không nằm trong hai trường hợp được liệt kê, nên đừng dùng nó để kết luận về hành vi ngắt lời.
Kiểm tra: chen ngang agent ở cả hai chế độ và ghi lại khác biệt nếu có. Việc kiểm thử ngắt lời nên diễn ra ở dev mode với credentials Cloud, hoặc trên đúng môi trường sẽ chạy thật.
Bước 3: Chọn cách ngắt lời bằng phép đo
Có hai câu hỏi dễ bị gộp làm một: khách đã nói xong chưa, và khách có thật sự muốn cắt lời agent không. Với câu đầu, VAD-only chỉ dựa vào khoảng lặng và có thể phản ứng với tiếng ho hay nhiễu đường truyền.
LiveKit khuyến nghị turn detector model cho câu hỏi này, vì nó xét trực tiếp giọng nói, kết hợp lời với các tín hiệu âm học như ngữ điệu và nhịp. Với câu thứ hai, adaptive interruption được thiết kế để agent phản ứng tự nhiên khi khách nói chen giữa câu trả lời.
Nếu không dùng adaptive, bạn còn hai lựa chọn trong tùy chọn turn handling. Đoạn dưới chỉ minh họa các giá trị cần đặt, không phải cú pháp chính xác:
# Minh họa: tên lớp và cách truyền vào phiên xem tài liệu LiveKit
turn_handling = {"interruption": {"mode": "vad"}} # chỉ dùng VAD
turn_handling = {"interruption": {"enabled": False}} # tắt hẳn ngắt lời
Ngoài khuyến nghị về turn detector, các gợi ý trong bảng dưới dành cho hotline giả định, không phải quy tắc của LiveKit:
| Lựa chọn | Dùng cho việc gì | Hợp khi | Điều cần để ý |
|---|---|---|---|
| Turn detector model | Đoán khách đã nói xong chưa | Muốn agent lắng nghe tự nhiên | Vẫn phải đo trên âm thanh của chính khách |
| Adaptive | Xử lý khi khách nói chen | Đủ điều kiện Cloud và VAD, khách hay nói chen | Đổi nơi chạy thì phải kiểm tra lại, vì có thể không còn bật sẵn |
mode: "vad" |
Xử lý khi khách nói chen | Cần một cơ chế đơn giản, dễ giải thích | Agent có thể dừng nhầm vì tiếng ho hoặc nhiễu đường truyền |
enabled: false |
Tắt hẳn ngắt lời | Luồng chỉ phát thông báo một chiều | Khách không thể cắt lời agent |
Đừng chọn bằng tai, nhưng cũng đừng đo vội. Trước tiên hãy xác nhận agent đang chạy dev mode với credentials Cloud hoặc đã triển khai lên Cloud, và phiên có mô hình VAD. Thiếu một điều kiện, cấu hình “mặc định” có thể không phải adaptive, và bảng so sánh sẽ dẫn bạn đến kết luận sai.
Sau đó, thu sẵn 10 đoạn có tiếng ho và 10 đoạn khách thật sự cắt lời, lưu thành file để mọi lượt thử dùng đúng một bộ âm thanh.
Cách làm giản lược, không cần thêm API: cho agent đọc một câu dài cố định, rồi phát từng file từ điện thoại vào micro laptop khi agent đang đọc. Giữ nguyên âm lượng và khoảng cách, ghi lại agent có dừng không. Sau đó đổi đúng một tham số cấu hình và lặp lại y hệt.
Turn detector trả lời một câu hỏi khác nên cần một bài đo riêng. Hãy thu những câu khách ngừng giữa chừng để suy nghĩ, rồi đếm số lần agent trả lời khi khách chưa nói hết.
Với con số minh họa: cấu hình A dừng 6 trên 10 tiếng ho và 10 trên 10 lần ngắt thật; cấu hình B dừng 1 và 8. A làm khách bực vì agent hay khựng, còn B khiến 2 trong 10 lần khách phải nói lại. Bảng đếm ấy đưa cho khách một lựa chọn có cái giá rõ ràng, thay vì một lời hứa “nghe tự nhiên”.
Kiểm tra: bạn có một bảng đếm, mỗi cấu hình một dòng, ghi nơi agent chạy, phiên có VAD hay không, và số lần agent dừng trên 10 tiếng ho và trên 10 lần ngắt thật.
Bước 4: Tắt ngắt lời cho đúng một câu, không phải cả cuộc gọi
Câu thông báo pháp lý là trường hợp ngắt lời không được phép xảy ra. Trong Python, tài liệu LiveKit cho phép tắt ngắt lời cho riêng một câu, bằng cách truyền allow_interruptions=False vào say() hoặc generate_reply().
await session.say(
"Cuộc gọi này được ghi âm để bảo đảm chất lượng dịch vụ.",
allow_interruptions=False,
)
Kiểm tra: nói chen vào giữa câu này, agent phải đọc hết. Sau đó nói chen vào câu trả lời kế tiếp, agent phải phản ứng. Nếu cả hai lần đều không dừng, có thể bạn đang tắt ngắt lời ở cấp phiên.
Bước 5: Warm hay cold khi khách đòi gặp người?
Hai kiểu chuyển máy khác nhau ở chỗ ai giữ ngữ cảnh. Cold transfer chuyển người gọi sang một số điện thoại hoặc SIP endpoint khác, và phiên LiveKit của người gọi kết thúc sau đó. Vì thế, mọi thứ bạn cần lưu như transcript hay lý do chuyển nên được ghi lại trước khi chuyển.
Warm transfer thì để agent gọi nhân viên, tóm tắt ngữ cảnh rồi nối hai bên lại. Cách dựng sẵn là WarmTransferTask, và trong Python nó vẫn nằm dưới beta.workflows. Task này cần outbound SIP trunk, khai báo trực tiếp hoặc qua trunk ID đã lưu.
# Giản lược: đường dẫn import và tham số còn lại xem tài liệu WarmTransferTask
from livekit.agents.beta.workflows import WarmTransferTask
task = WarmTransferTask(
sip_trunk_id="<id outbound trunk>", # hoặc sip_connection=...
# số đích, ngữ cảnh tóm tắt: theo tài liệu
)
Ở hotline giả định, khách khiếu nại về bồi thường nên đi warm, vì không ai muốn kể lại câu chuyện lần thứ hai. Khách chỉ cần gặp bộ phận khác cho một việc đơn giản có thể đi cold.
Kiểm tra: gọi thử qua trunk thật. Khi warm, nhân viên phải nghe được tóm tắt trước khi nối máy; khi cold, hãy xác nhận cuộc gọi đến đúng đích và dữ liệu đã được lưu trước lúc phiên kết thúc.
Ba lỗi nên tránh từ đầu
Lỗi dễ mắc nhất là tắt ngắt lời toàn phiên chỉ để hết chuyện agent khựng vì tiếng ho. Triệu chứng biến mất, nhưng khách mất luôn quyền cắt lời một cái máy đang nói sai.
Lỗi tiếp theo nằm ở nhà cung cấp, không nằm trong code. Cold transfer sẽ thất bại nếu trunk của nhà cung cấp không cho phép chuyển cuộc gọi, chẳng hạn SIP REFER trên Twilio, nên hãy kiểm tra cấu hình này từ tuần đầu.
Cuối cùng là coi API beta như API ổn định. Vì WarmTransferTask trong Python vẫn ở nhánh beta, hãy ghim phiên bản thư viện và chạy lại bài thử chuyển máy mỗi lần nâng cấp.
Tại site khách hàng, câu hỏi đầu tiên không phải về mô hình
Hãy bắt đầu bằng ba câu hỏi: agent sẽ chạy ở đâu, trunk SIP do ai quản lý và có cho phép chuyển cuộc gọi không, và ai nghe máy khi agent chuyển. Câu đầu quyết định adaptive có bật sẵn hay không, câu thứ hai thường phải qua đội viễn thông của khách, còn câu cuối quyết định ai cần được đọc bản tóm tắt.
Khi đọc mô tả việc làm FDE ở mảng voice, hãy để ý các từ như telephony, SIP hay contact center. Trên CV, một dòng cụ thể sẽ thuyết phục hơn “có kinh nghiệm voice AI”: nêu bộ âm thanh bạn đo, tỷ lệ agent dừng sai trước và sau khi đổi cấu hình, cùng một lần warm transfer chạy qua trunk thật.
Giọng đọc hay chưa nói lên nhiều về một voice agent. Hãy nhìn nó xử lý ra sao ở giây khách cắt lời, và ở giây nó phải nhường máy cho người.
Bài này có hữu ích không?
Cảm ơn bạn đã góp ý!