OnDuty

Bộ mã Connector cho web PHP

Updated 2026-09-11

This documentation is written in Vietnamese. The rest of the site follows the language you picked. If you need this page in English, tell GoEcomaZ on Zalo and we will look at it.

Trang OnDuty Connector mô tả chuẩn. Trang này dành cho web viết bằng PHP và dùng MySQL hoặc MariaDB (PHP thuần, CodeIgniter, Laravel, web tự viết, kể cả hosting đời cũ chạy PHP 5.6): thay vì tự viết endpoint, bạn dùng bộ mã OnDuty làm sẵn và chỉ khai báo dữ liệu nào trợ lý được tra.

Web WordPress có WooCommerce thì mẫu WordPress ở trang Connector gọn hơn.

Bộ mã gồm gì

File Để làm gì
onduty-connector.php Endpoint. Đặt ở gốc web, không sửa
onduty-config.sample.php Cấu hình mẫu. Chép thành onduty-config.php, đặt ngoài gốc web
connector-probe.mjs Công cụ kiểm tra, chạy bằng Node trên máy bạn
htaccess.txt Khoá thư mục, dùng cho cách A bên dưới. Lưu thành .htaccess

Tải về xong, bỏ đuôi .txt (riêng htaccess.txt lưu thành .htaccess). OnDuty để đuôi .txt để trình duyệt tải về chứ không chạy.

Mọi thứ riêng của tiệm nằm trong file cấu hình, nên nâng cấp bộ mã chỉ là thay một file onduty-connector.php. Bản hiện tại là php/1.0.6, và địa chỉ connector của bạn tự báo bản đang chạy trong trường kit.

Năm bước

1 và 2. Chọn chỗ đặt. File cấu hình có mã bí mật và cách vào database, nên trình duyệt không được đọc nó. Có hai cách, bộ mã tự tìm cấu hình ở cả hai chỗ:

  • Cách A, gom một thư mục (gọn nhất, gỡ ra chỉ cần xoá thư mục): tạo public_html/onduty/, bỏ vào đó onduty-connector.php, onduty-config.php (chép từ file mẫu) và .htaccess (từ htaccess.txt). Địa chỉ connector là https://web-cua-ban.vn/onduty/onduty-connector.php. Hai lớp khoá: file cấu hình tự trả 404 khi bị gọi thẳng, và .htaccess chặn nó thêm lần nữa, kèm tắt liệt kê thư mục. Mở thử https://web-cua-ban.vn/onduty/onduty-config.php phải thấy 403 hoặc 404, không một chữ nào.
  • Cách B, cấu hình ngoài gốc web: onduty-connector.php ở gốc web, onduty-config.phpthư mục chứa gốc web, ví dụ public_html/ nằm trong /home/tiem/domains/tiem.vn/ thì file cấu hình là /home/tiem/domains/tiem.vn/onduty-config.php. Dùng cách này khi hosting không đọc .htaccess (Nginx).

Cả hai cách: quyền 640 cho file cấu hình, 644 cho onduty-connector.php. Muốn tách mã bí mật ra file riêng thì dùng onduty-secret.php cạnh file cấu hình, cũng mở đầu bằng đoạn kiểm ONDUTY_KIT_LOADED như file mẫu.

3. Điền phần chung.

  • secret: vào trang quản trị OnDuty, tab Trợ lý, mục Nối dữ liệu sống, bấm Tạo mã bí mật.
  • tenant: mã tiệm của bạn trên OnDuty. Có dòng này thì yêu cầu ký cho tiệm khác bị từ chối.
  • site: địa chỉ web, bắt đầu bằng https://. Đường dẫn trong câu trả lời được ghép từ đây.
  • db: một hàm trả về kết nối mysqli. Web đã có file cấu hình database thì đọc lại từ đó, đừng chép mật khẩu ra chỗ thứ hai.

4. Khai báo dữ liệu được tra (sources). Mỗi nguồn là một bảng mà mọi dòng đã công khai trên web: sản phẩm, dịch vụ, bài viết, trang chính sách. Ví dụ bảng sản phẩm:

array(
    'id' => 'products',
    'table' => 'products',
    'search' => array('name' => 4, 'code' => 4),
    'deep' => array('details' => 1),
    'select' => array('slug', 'price', 'old_price', 'in_stock'),
    'where' => array('is_active' => 1),
    'price_column' => 'price',
    'title' => '{name}',
    'body' => function ($r, $terms) {
        $gia = onduty_money($r['price']);
        return ($gia !== '' ? 'giá ' . $gia : 'giá liên hệ') . ', '
            . ($r['in_stock'] ? 'còn hàng' : 'hết hàng') . '. '
            . onduty_snippet($r['details'], $terms, 400);
    },
    'url' => 'san-pham/{slug}-{id}.html',
),
  • search: cột ngắn được tìm ở mọi câu hỏi (tên, mã, mô tả ngắn), số là trọng số. Chữ khớp trong tên đáng gấp bốn lần chữ khớp trong mô tả.
  • deep: cột dài (thông số, nội dung bài viết), chỉ được tìm khi cột ngắn chưa đủ 8 câu trả lời. Đừng bỏ cột dài vào search: quét vài KB chữ mỗi dòng cho mọi câu hỏi làm một web thật trả lời mất gần một giây thay vì vài chục mili giây, quá sát giới hạn 2 giây của OnDuty.
  • select: cột đọc thêm để viết câu trả lời.
  • where: điều kiện cố định, thường là "chỉ dòng đang hiện trên web".
  • price_column: có cột này thì câu như "dưới 10 triệu", "từ 5 đến 8 triệu", "rẻ nhất" được lọc theo giá thật.
  • id_column: khoá chính không tên id (ví dụ news_id) thì ghi vào đây.
  • title, body: mẫu kiểu '{name}', hoặc một hàm nhận dòng dữ liệu và các từ khách hỏi. onduty_snippet() trích đúng đoạn của một bài dài có chứa câu trả lời, thay vì 600 ký tự đầu.
  • url: đường dẫn của một dòng, có {id} thì bộ mã nhận ra được khách đang xem trang nào. Trang tĩnh không theo một mẫu thì dùng một hàm trả về đường dẫn.

Thêm facts cho những điều luôn đúng (hotline, địa chỉ, giờ mở cửa), mỗi dòng kèm từ khoá, và stopwords cho những chữ có ở khắp nơi trên web của bạn, thường là tên tiệm.

Một nguồn có thể khai intent: những chữ cho biết khách đang hỏi đúng loại thông tin đó, dù không nêu tên món nào.

'intent' => array('keywords' => array('cách', 'hướng dẫn', 'thế nào'), 'boost' => 2.5),          // bài viết
'intent' => array('keywords' => array('công trình', 'đã làm', 'đơn vị nào'), 'browse' => 3),     // công trình
  • boost: câu hỏi có chữ đó thì nguồn này được tìm cả cột dài ngay lượt đầu và xếp cao hơn. "Treo máy chiếu lên trần thì lật hình thế nào" ra bài hướng dẫn, không ra giá treo máy chiếu.
  • browse: câu hỏi có chữ đó mà không khớp dòng nào ("Sao Mai từng làm sự kiện cho đơn vị nào") thì trả vài dòng mới nhất của nguồn này.

5. Kiểm tra, rồi bật. Mở địa chỉ connector trong trình duyệt, phải thấy một dòng JSON có tên tiệm. Rồi chạy công cụ kiểm tra trên máy bạn (cần Node 18 trở lên):

node connector-probe.mjs https://web-cua-ban.vn/onduty/onduty-connector.php <mã bí mật> --tenant <mã tiệm> --question "máy chiếu giá bao nhiêu"

Chín dòng PASS là xong. Dòng latency fail thì thêm --debug: connector trả kèm thời gian từng nguồn, thấy ngay bảng nào đang chậm. Vào trang quản trị, dán địa chỉ, tick Bật kết nối, bấm Lưu rồi Kiểm tra kết nối.

Bộ mã tự lo những gì

  • Không trả dữ liệu trước khi kiểm chữ ký và giờ gửi. Sai chữ ký, sai mã tiệm, hay yêu cầu cũ hơn 5 phút đều bị từ chối, kèm lý do trong trường error để trang quản trị hiện ra. Mở bằng trình duyệt thì chỉ thấy tên tiệm và phiên bản, cấu hình hỏng cũng chỉ thấy "config error", không bao giờ thấy chi tiết.
  • Không đụng tới dữ liệu riêng. Chỉ tra bảng và cột có tên trong cấu hình. Tên bảng hay cột trông như dữ liệu riêng (member, user, order, customer, admin, password...) làm bộ mã từ chối chạy, và nói rõ tên nào. Thật sự công khai thì ghi vào allow_names.
  • Mọi giá trị đi qua prepared statement, tên bảng và cột không bao giờ lấy từ yêu cầu.
  • Phản hồi luôn là JSON sạch. Notice hay Warning của PHP không bao giờ in ra trước JSON.
  • Đúng giới hạn của OnDuty: tối đa 8 mục, mỗi mục 600 ký tự, cả phản hồi dưới 16 KB.

Chỉ mục tìm kiếm, tự làm

Cột deep không được quét bằng SQL ở mỗi câu hỏi. Lần đầu cần tới, bộ mã rút chữ sạch của các cột đó ra một lần và lưu thành file onduty-cache-<nguồn>.txt cạnh onduty-connector.php, rồi những câu sau tìm trong file đó. Trên web thật đầu tiên, câu cần tới cột dài từ gần 1 giây xuống vài chục mili giây.

  • File tự làm mới sau mỗi giờ (cache_ttl, tính bằng giây), và việc làm mới chạy sau khi đã trả lời khách.
  • Chỉ chứa chữ của những cột bạn đã khai trong sources, tức những gì vốn đã công khai trên web. .htaccess của bộ mã vẫn chặn trình duyệt đọc nó, như mọi file .txt khác trong thư mục.
  • Thư mục phải cho PHP ghi. Không ghi được thì bộ mã vẫn chạy, chỉ chậm hơn, và ghi lý do vào error log.
  • Muốn để file ở chỗ khác thì đặt 'cache_dir' => '/đường/dẫn'; muốn tắt hẳn thì 'cache' => false.

Tìm kiếm hiểu tiếng Việt tới đâu

  • Bỏ chữ hỏi ("có", "không", "bao nhiêu", "giá"...), gõ có dấu hay không dấu đều tìm được, "den led" ra "Đèn LED".
  • Web cũ lưu chữ có dấu dạng mã HTML (h&agrave;nh thay cho "hành") vẫn tìm được.
  • Hai chữ gõ liền nhau là một từ: "xe máy" không khớp một trang có "gửi xe" ở đoạn này và "máy chiếu" ở đoạn kia. Câu dài phải khớp phần lớn số chữ.
  • Mã model khớp một phần: "vw355" ra "PT-VW355NZ".
  • Khách đang ở trang sản phẩm hỏi "cái này giá bao nhiêu" hay "máy này còn hàng không" thì chỉ trả đúng sản phẩm đó. Câu hỏi nối tiếp như "còn loại nào rẻ hơn không" mượn chữ của câu trước.
  • Khách hỏi tiếng Anh ("rent a projector for one day") thì những chữ thông dụng được đổi sang tiếng Việt trước khi tìm (thuê, máy chiếu, ngày...). Thêm chữ riêng của tiệm bằng 'synonyms_en' => array('beamer' => 'máy chiếu'). Chỉ áp dụng khi khách dùng tiếng Anh, vì tiếng Việt gõ không dấu dễ trùng ("day" là "dây").

Những chỗ hay vướng

Gặp gì Sửa
Mở địa chỉ connector thấy config error, see the PHP error log Cấu hình có chỗ sai. Lý do cụ thể nằm trong error log của PHP, và hiện trong trang quản trị khi bấm Kiểm tra kết nối (phần sau "máy chủ tiệm báo:"): thiếu mã bí mật, sai site, hay một tên bảng bị chặn. Địa chỉ mở bằng trình duyệt không bao giờ nói lý do, để người lạ không đọc được cấu hình của bạn
onduty-config.php not found File cấu hình không nằm cạnh onduty-connector.php (cách A) hay ở thư mục chứa gốc web (cách B)
Mở onduty-config.php bằng trình duyệt thấy chữ Dừng lại: file cấu hình thiếu đoạn kiểm ONDUTY_KIT_LOADED ở đầu, hoặc .htaccess chưa được đặt. Sửa rồi đổi mã bí mật mới trong trang quản trị
Kiểm tra kết nối báo quá cũ Đồng hồ máy chủ lệch, bật đồng bộ giờ (NTP)
database error, see the PHP error log Hàm db không kết nối được, hoặc sai tên cột. Lỗi chi tiết nằm trong error log của PHP, không bao giờ trong phản hồi
Không thấy file onduty-cache-*.txt sau vài câu hỏi Thư mục chưa cho PHP ghi. Cho user chạy PHP quyền ghi vào thư mục đó, hoặc đặt cache_dir sang chỗ ghi được
Hỏi gì cũng không ra Kiểm where (cột và giá trị có đúng dòng đang hiện không), id_column, và thử lại với tên sản phẩm có thật
Ra quá nhiều thứ không liên quan Thêm tên tiệm vào stopwords, hạ trọng số cột mô tả, bỏ bớt cột dài khỏi search