Lần gần nhất mình rà một website dịch vụ nhỏ, cùng một doanh nghiệp mà trang chủ có ba phiên bản: JSON-LD ghi số hotline cũ, footer ghi số mới, còn llms.txt vẫn dùng tên trước khi đổi thương hiệu. Người xem lướt qua không để ý, nhưng máy đọc từng dòng thì thấy ba câu trả lời khác nhau cho câu hỏi "doanh nghiệp này là ai".
Bài này là cách mình kiểm nhanh chuyện đó trước khi làm bất cứ việc SEO AI nào khác: một script đọc JSON-LD trên trang chủ, đọc llms.txt, rồi so từng trường với một bộ thông tin gốc do chủ doanh nghiệp duyệt. Thay example.com bằng domain của bạn.
Bước 1: viết bộ thông tin gốc thành file
Mọi so sánh cần một mốc. Đừng lấy mốc từ website, vì website có thể đang sai. Viết tay một file entity.json và nhờ chủ doanh nghiệp xác nhận từng dòng:
{ "name": "Example Studio", "url": "https://example.com/", "telephone": "+84 912 345 678", "sameAs": [ "https://www.facebook.com/examplestudio", "https://www.linkedin.com/company/examplestudio" ] } Bước 2: lấy JSON-LD từ HTML ban đầu
Nhiều bot AI chỉ đọc HTML trả về lần đầu, không chạy JavaScript. Vì vậy script dùng requests thuần, không dùng trình duyệt headless. Nếu schema của bạn chỉ xuất hiện sau khi JS chạy, script sẽ báo không thấy, và đó chính là lỗi cần sửa.
import json, re, requests UA = {"User-Agent": "entity-check/1.0"} def jsonld_blocks(url): html = requests.get(url, headers=UA, timeout=15).text blocks = re.findall( r'<script[^>]+application/ld\+json[^>]*>(.*?)</script>', html, re.S) out = [] for b in blocks: try: data = json.loads(b) except json.JSONDecodeError: print("JSON-LD lỗi cú pháp:", b[:80]) continue items = data.get("@graph", [data]) if isinstance(data, dict) else data out.extend(i for i in items if isinstance(i, dict)) return out def find_org(items): for i in items: t = i.get("@type") types = t if isinstance(t, list) else [t] if {"Organization", "LocalBusiness", "ProfessionalService"} & set(types): return i return None Bước 3: đọc llms.txt
llms.txt là một đề xuất của cộng đồng (llmstxt.org), chưa phải chuẩn chính thức, và Google không nhắc tới nó trong hướng dẫn về tính năng AI trên Search. Mình vẫn kiểm vì nếu đã đặt file thì nó không được nói khác website. Theo đề xuất, dòng # đầu tiên là tên dự án, dòng > ngay sau là phần tóm tắt.
def read_llms(base): r = requests.get(base.rstrip("/") + "/llms.txt", headers=UA, timeout=15) if r.status_code != 200: return None lines = r.text.splitlines() name = next((l[2:].strip() for l in lines if l.startswith("# ")), None) summary = next((l[2:].strip() for l in lines if l.startswith("> ")), "") phones = re.findall(r"\+?\d[\d .]{8,}\d", r.text) return {"name": name, "summary": summary, "phones": phones} Bước 4: so với bộ thông tin gốc
def digits(s): return re.sub(r"\D", "", s or "").removeprefix("84").lstrip("0") def check(base, truth): org = find_org(jsonld_blocks(base)) if not org: print("[LỖI] Không thấy Organization trong HTML ban đầu") return if org.get("name") != truth["name"]: print("[LỆCH] name:", org.get("name"), "≠", truth["name"]) if org.get("url", "").rstrip("/") != truth["url"].rstrip("/"): print("[LỆCH] url:", org.get("url")) if digits(org.get("telephone")) != digits(truth["telephone"]): print("[LỆCH] telephone:", org.get("telephone")) missing = set(truth["sameAs"]) - set(org.get("sameAs", [])) for m in missing: print("[THIẾU] sameAs:", m) for s in org.get("sameAs", []): code = requests.head(s, headers=UA, timeout=15, allow_redirects=True).status_code if code >= 400: print("[CHẾT] sameAs", code, s) llms = read_llms(base) if llms is None: print("[INFO] Không có llms.txt, bỏ qua") else: if llms["name"] != truth["name"]: print("[LỆCH] llms.txt name:", llms["name"]) if llms["phones"] and digits(truth["telephone"]) not in map(digits, llms["phones"]): print("[LỆCH] llms.txt có số điện thoại khác:", llms["phones"]) if __name__ == "__main__": truth = json.load(open("entity.json", encoding="utf-8")) check(truth["url"], truth) Chạy python check_entity.py. Không in gì ngoài dòng INFO là tốt.
Ba lỗi mình gặp nhiều nhất khi chạy script này
- Schema chỉ có sau khi JS chạy. Plugin hoặc tag manager chèn JSON-LD phía client. Google vẫn render được, nhưng nhiều crawler AI thì không. Cách sửa là xuất JSON-LD ngay từ server.
- sameAs trỏ vào trang đã chết. Fanpage cũ bị gỡ, hồ sơ danh bạ hết hạn. Một link chết trong sameAs không phá gì lớn, nhưng nó là một nguồn không còn xác nhận được bạn.
- Số điện thoại viết ba kiểu.
0912...,+84 912...,84912...trên ba trang khác nhau. Con người hiểu là một, còn so khớp chuỗi thì không. Chọn một định dạng và dùng ở mọi nơi.
Script này không làm được gì
Nó chỉ kiểm phần bạn tự kiểm soát trên website. Nó không cho biết AI đang nói gì về bạn, và không chứng minh sửa xong thì AI sẽ nhắc tên. Phần đó cần hỏi lại một bộ câu hỏi mẫu trên từng nền tảng theo định kỳ và ghi lại câu trả lời.
Nếu muốn xem việc kiểm thực thể này nằm ở đâu trong cả quy trình (thống nhất thông tin, viết đoạn trả lời dễ trích, mở đường cho bot AI, đo lượt vào từ AI trong GA4), mình có mô tả 6 phần việc ở trang dịch vụ SEO AI của Mạnh Digital.
Đọc thêm trong loạt bài này: