Nhiệm vụ: Triển khai lõι hội thoại AI của riêng bạn với các kịch bản động

Triển khai lõι hội thoại AI của riêng bạn với các kịch bản động

Triển khai từ đầu một runtime hội thoại đa người dùng mà không cần n8n hay các giải pháp workflow tương tự, với các kịch bản phức tạp được xác định từ bên ngoài và trạng thái độc lập của từng cuộc hội thoại.

Triển khai lõι hội thoại AI của riêng bạn với các kịch bản động

Bối cảnh

Dự án "Trung tâm tác nhân AI" cần phải có phần hội thoại của riêng mình. Không được xây dựng nó trên nền tảng n8n, Make, các trình dựng chatbot/workflow có sẵn hoặc các hệ thống tương tự nơi logic hội thoại chính được thực thi bởi một công cụ bên ngoài.

Cần phải triển khai một lõi riêng, nhận mô tả kịch bản phức tạp từ bên ngoài, dẫn dắt người dùng theo kịch bản đó một cách động và trả về kết quả. Mỗi cuộc hội thoại của người dùng phải tồn tại và được tính toán như một thực thể độc lập riêng biệt với trạng thái, lịch sử và kết quả của chính nó.

Mục tiêu

Tạo ra một dịch vụ thực thi hội thoại AI vạn năng có thể được sử dụng bởi các trang web và tác nhân khác nhau mà không cần mã hóa cứng kịch bản cụ thể vào trong mã nguồn ứng dụng.

Dịch vụ phải có khả năng:

  • nhận kịch bản hội thoại từ bên ngoài;
  • tạo một phiên làm việc/hội thoại riêng biệt cho một người dùng cụ thể;
  • tại mỗi tin nhắn, xác định bước tiếp theo một cách động dựa trên kịch bản, trạng thái đã tích lũy và câu trả lời của người dùng;
  • gọi LLM ở những nơi được quy định bởi logic;
  • lưu trữ trạng thái và lịch sử hội thoại;
  • kết thúc hội thoại với một kết quả được chính thức hóa;
  • đồng thời phục vụ nhiều người dùng và nhiều cuộc hội thoại độc lập.

Nguyên tắc kiến trúc cốt lõi

Kịch bản là dữ liệu, không phải mã nguồn của một đoạn chat cụ thể.

Công cụ hội thoại phải mang tính vạn năng. Hệ thống bên ngoài truyền cho nó mô tả kịch bản mang tính khai báo, và runtime sẽ phiên dịch nó cũng như quản lý các chuyển đổi.

Không được yêu cầu lập trình một backend-flow mới cho mỗi kịch bản mới.

Những gì cần triển khai

1. Mô hình kịch bản

Thiết kế định dạng kịch bản có thể được truyền qua API và được quản lý phiên bản.

Tối thiểu phải bao gồm:

  • định danh và phiên bản kịch bản;
  • điểm bắt đầu;
  • các bước/nút (steps/nodes);
  • hướng dẫn hệ thống cho LLM;
  • dữ liệu người dùng mong đợi;
  • điều kiện và phân nhánh;
  • chuyển đổi giữa các bước;
  • biến/bối cảnh trung gian;
  • giá trị được tính toán;
  • hành động trước/sau bước;
  • điều kiện kết thúc;
  • lược đồ kết quả cuối cùng.

Kịch bản phải hỗ trợ không chỉ chuỗi câu hỏi tuyến tính mà còn cả các phân nhánh động.

2. Runtime hội thoại

Triển khai cỗ máy thực thi kịch bản của riêng mình.

Tại mỗi tin nhắn đến, runtime phải:

  1. tải trạng thái của cuộc hội thoại cụ thể;
  2. xác định nút kịch bản hiện tại;
  3. xử lý đầu vào của người dùng;
  4. gọi LLM nếu cần thiết;
  5. xác thực/chuẩn hóa dữ liệu nhận được;
  6. tính toán điều kiện chuyển đổi;
  7. cập nhật trạng thái;
  8. tạo câu trả lời tiếp theo;
  9. lưu trữ lịch sử và trạng thái mới một cách nguyên tử (atomically);
  10. trả về câu trả lời và trạng thái kỹ thuật thực thi ra bên ngoài.

Logic chuyển đổi phải được thực thi bên trong dịch vụ của chúng ta chứ không phải bên trong công cụ workflow bên ngoài.

3. Trạng thái hội thoại

Lưu trữ từng cuộc hội thoại một cách riêng biệt.

Cần phải dự trù tối thiểu:

  • dialogId;
  • tenant/site/agent/user identifiers;
  • scenarioId + scenarioVersion;
  • nút hiện tại;
  • accumulated state / variables;
  • lịch sử tin nhắn;
  • kết quả gọi LLM cần thiết cho việc tái tạo;
  • trạng thái: active / completed / failed / cancelled;
  • timestamps;
  • kết quả cấu trúc cuối cùng (result).

Một người dùng có thể có nhiều cuộc hội thoại độc lập.

4. Chế độ đa người dùng

Dịch vụ phải hoạt động chính xác trong điều kiện các cuộc hội thoại song song của nhiều người dùng và trang web khác nhau.

Bắt buộc phải dự trù:

  • cô lập trạng thái theo từng cuộc hội thoại;
  • không có trạng thái biến đổi toàn cục (global mutable state) giữa những người dùng;
  • bảo vệ chống lại việc xử lý đồng thời hai tin nhắn của cùng một cuộc hội thoại;
  • tính bất biến (idempotency) khi gửi lại tin nhắn;
  • khả năng mở rộng quy mô theo chiều ngang (horizontal scaling) của nhiều thể hiện runtime;
  • cô lập tenant/site ở cấp độ API và lưu trữ dữ liệu.

5. API bên ngoài

Cần có một hợp đồng lập trình rõ ràng cho các hệ thống bên ngoài.

Tập hợp thao tác tối thiểu:

  • đăng ký/cập nhật phiên bản kịch bản;
  • tạo cuộc hội thoại theo kịch bản;
  • gửi tin nhắn vào một cuộc hội thoại cụ thể;
  • lấy trạng thái hiện tại;
  • lấy lịch sử;
  • lấy kết quả cuối cùng;
  • hủy/đóng cuộc hội thoại.

Phản hồi cho sendMessage không chỉ chứa văn bản cho người dùng mà khi cần thiết còn phải chứa phần máy đọc được (machine-readable):

  • trạng thái;
  • bước hiện tại/tiếp theo;
  • giá trị được trích xuất (extracted values);
  • kết quả khi kết thúc;
  • siêu dữ liệu kỹ thuật thực thi.

6. Làm việc với LLM

LLM là một phần của runtime, nhưng bản thân nó không được là nguồn duy nhất kiểm soát luồng.

Cần phân tách:

  • logic chuyển đổi tất định (deterministic);
  • trích xuất/phân loại dữ liệu thông qua LLM;
  • tạo văn bản phản hồi;
  • xác thực các câu trả lời có cấu trúc của mô hình.

Đối với các thao tác có cấu trúc, hãy sử dụng đầu ra bị ràng buộc bởi lược đồ (schema-constrained output) và bắt buộc xác thực kết quả của mô hình.

Dự trù xử lý:

  • timeout;
  • đầu ra sai định dạng (malformed output);
  • thử lại (retry);
  • dự phòng (fallback);
  • giới hạn tốc độ (rate limits);
  • lỗi từ nhà cung cấp.

7. Phiên bản hóa và tính tái tạo

Cuộc hội thoại đã bắt đầu phải tiếp tục chạy trên phiên bản kịch bản mà nó được tạo ra, ngay cả khi sau đó một phiên bản mới được xuất bản.

Không được phép thay đổi ngữ nghĩa của một cuộc hội thoại đang diễn ra bằng cách chỉnh sửa kịch bản một cách âm thầm.

8. Khả năng quan sát (Observability)

Đối với mỗi bước, hãy lưu trữ một trace kỹ thuật:

  • đầu vào;
  • nút đã chọn;
  • quyết định đã đưa ra;
  • chuyển đổi;
  • các lần gọi mô hình;
  • độ trễ (latency);
  • lỗi;
  • mức sử dụng token/chi phí, nếu có;
  • kết quả của bước.

Điều này phải cho phép điều tra lý do tại sao một cuộc hội thoại cụ thể lại đi theo một nhánh nhất định.

9. Bảo mật thực thi kịch bản

Kịch bản bên ngoài không được phép cho phép thực thi mã tùy ý trên máy chủ.

Các điều kiện (conditions), biểu thức (expressions) và hành động phải được thực thi thông qua một DSL bị giới hạn / tập hợp các thao tác được phép.

Không được sử dụng eval hoặc JavaScript tùy ý từ kịch bản được gửi tới.

Những gì KHÔNG nằm trong nhiệm vụ này

  • trình dựng kịch bản trực quan (visual builder);
  • giao diện UI của tổng đài viên/quản lý;
  • tiện ích chat trên một trang web cụ thể;
  • tích hợp CRM như một tầng lớn riêng biệt;
  • bảng điều khiển phân tích BI;
  • xây dựng workflow trên n8n/Make và các sản phẩm tương tự.

Các phần này có thể sử dụng runtime hội thoại sau này, nhưng không được định nghĩa kiến trúc của nó.

Kịch bản kiểm tra tối thiểu

Để nghiệm thu, hãy triển khai ít nhất một kịch bản xác nhận hoạt động động của công cụ:

  • một vài câu hỏi;
  • ít nhất một phân nhánh có điều kiện;
  • trích xuất giá trị có cấu trúc thông qua LLM;
  • quay lại câu hỏi làm rõ khi dữ liệu không đủ;
  • kết thúc với kết quả máy đọc được.

Đồng thời chạy một số cuộc hội thoại theo cùng một kịch bản với các câu trả lời khác nhau và xác nhận rằng trạng thái cũng như kết quả không bị trộn lẫn.

Tiêu chí hoàn thành

  • Runtime hội thoại được viết bởi chính chúng ta và không phụ thuộc vào công cụ workflow bên ngoài.
  • Kịch bản mới có thể được truyền qua hợp đồng bên ngoài mà không cần thay đổi mã nguồn runtime.
  • Hỗ trợ các phân nhánh phi tuyến tính và chuyển đổi động.
  • Mỗi cuộc hội thoại có trạng thái bền vững (persistent) độc lập.
  • Một người dùng có thể có nhiều cuộc hội thoại, và dịch vụ có thể đồng thời phục vụ nhiều người dùng/trang web.
  • Có bảo vệ chống lại điều kiện tranh chấp (race conditions) và việc gửi lại tin nhắn.
  • Phiên bản kịch bản được cố định trong suốt vòng đời của cuộc hội thoại.
  • Kết quả cuối cùng có sẵn dưới dạng có cấu trúc.
  • Dựa vào trace, có thể hiểu được cách thức và lý do tại sao mỗi bước được thực thi.
  • Kịch bản kiểm tra vượt qua bài kiểm tra end-to-end và xác nhận việc cô lập các cuộc hội thoại song song.

Ворклоги

Nền tảng runtime: thực thi dạng stream và hủy bỏ

Đối với runtime hội thoại tùy chỉnh trong tương lai, cơ chế thực thi cấp độ thấp đã được triển khai:

  • ExecutionContext chung với định danh chạy, tín hiệu hủy (abort signal) và phân phối sự kiện;
  • Hợp đồng NDJSON dạng stream POST /api/chat;
  • Các trạng thái chính xác started/delta/done/error/cancelled;
  • Hủy bỏ thực thi rõ ràng và hủy bỏ khi ngắt kết nối/quá thời gian/tắt hệ thống (disconnect/timeout/shutdown);
  • Tính độc lập của các tiến trình chạy song song;
  • Giới hạn đầu vào và kích thước phản hồi.

Lớp HTTP đã được kiểm tra lại bằng 8/8 bài kiểm tra.

Điều này không có nghĩa là công cụ kịch bản đã sẵn sàng: mô hình kịch bản khai báo, các nút/nhánh, phiên bản kịch bản, trạng thái hội thoại bền vững (persistent), tính bất biến (idempotency) của tin nhắn và các chuyển đổi nguyên tử vẫn chưa được triển khai.

Mô hình sự kiện tối thiểu của agent và LLM

Đã triển khai lớp phản ứng cơ bản của agent, phù hợp làm khối xây dựng cho runtime trong tương lai:

  • lớp Agent và endpoint POST /api/agent;
  • chuỗi cơ bản message.received → Agent.react → message.publish;
  • liên kết hành động với sự kiện nguồn thông qua stimulusId và actionId riêng;
  • JSON/NDJSON và cơ chế hủy chung;
  • Agent.react hiện gọi runChat/LLM thực tế thay vì bản giả lập (mock);
  • đã vượt qua lại 2/2 bài kiểm tra agent về các đoạn/tính danh tính của action, việc hủy và lỗi nhà cung cấp.

Việc triển khai hiện tại xử lý một loại sự kiện đến và kênh chat; mô hình chỉ nhận tin nhắn hiện tại. Hiện tại chưa có bộ nhớ hội thoại, tích lũy nhiều sự kiện, khử trùng lặp (deduplication), hệ thống ra quyết định chung và máy chuyển đổi trạng thái theo kịch bản.