Nhật ký công việc cho nhiệm vụ "Xem xét lại cấu trúc trang web, cải tiến nó và xây dựng lại nội dung"
Tôi sẽ không trích dẫn toàn bộ đoạn chat với ChatGPT, nhưng tôi đã suy nghĩ lại hoàn toàn bản chất của các khái niệm và sự thật, và tất cả quy về một điểm là không có lý do gì để biến các sự thật thành các thực thể riêng biệt trong cách triển khai của chúng ta. Bản thân các khái niệm của chúng ta cũng có thể là sự thật.
Dưới đây là tóm tắt ngắn gọn về cách tôi nhìn nhận vấn đề:
Chúng ta không phải là một hệ thống ngân hàng để mà xây dựng các mối quan hệ chi tiết. Vòng đời thông tin của chúng ta diễn ra như sau:
1. Một khái niệm chưa từng tồn tại trước đó được tạo ra. Ví dụ: Some Homestay.
2. Mọi thứ bạn muốn truyền tải sẽ được viết bên trong nó dưới dạng văn bản thuần túy. Đây là tất cả những gì người dùng sẽ đọc sau này. Trong dự án của chúng ta, về bản chất thì điều này tự nó đã là một sự thật, bởi vì toàn bộ nội dung đều do chúng ta tự tạo ra. Và chúng ta chỉ định ngay các thuộc tính chất lượng cái nào ra cái nào. Theo thời gian, khi thông tin này trở nên kém chất lượng, chúng ta chỉ cần hạ thấp thuộc tính chất lượng của nó xuống. Tương đối mà nói, hiện tại chúng ta chỉ cần đúng một chỉ số duy nhất từ 0 đến 1 dạng float. Chất lượng càng cao, khả năng hiển thị càng cao. Ví dụ, trang web của tôi đầy ắp những bài viết cũ không còn cần thiết nữa, cần phải vứt bỏ, nhưng tôi không thể vứt bỏ vì lỗi 404 thì không ngầu chút nào. Tôi chỉ cần đẩy chúng xuống sâu hơn.
3. Liên kết với các khái niệm khác. Tức là tôi chỉ cần liên kết chéo (cross-linking). Giống như các trang web cổ điển, tôi có thể đơn giản thêm các liên kết đến các khái niệm khác. Việc này để người dùng có thể đọc và chuyển đến các thẻ liên quan. Nhưng đây là liên kết ngoài. Trong khi tôi cũng cần cả liên kết ngược. Do đó, ở đây chúng ta không chỉ cần các liên kết gắn thẻ neo (anchor link) mà còn cần cả thông tin kỹ thuật đi kèm của liên kết (lý do tại sao tôi liên kết đến đây và một lần nữa, là một thuộc tính chất lượng nào đó).
4. Khi lưu một khái niệm như vậy, tất cả các liên kết của nó sẽ được tính toán lại. Những liên kết bị xóa khỏi nội dung cũng sẽ bị xóa khỏi danh sách liên kết, và các liên kết được thêm vào sẽ xuất hiện.
Đến đây chính là lúc chúng ta cần một bảng liên kết nhiều-nhiều (many-to-many) phẳng giữa các khái niệm. Lúc đó chúng ta có thể xem xét ngữ nghĩa tổng thể của toàn bộ cơ sở tri thức.
Và đây là điều tôi đang suy nghĩ, nếu phát triển ý tưởng này: Chúng ta có thể tạo ra ngữ nghĩa hoàn chỉnh ở cấp độ thẻ HTML. Chúng ta chỉ cần chèn các liên kết vào trong văn bản. Đồng thời chúng có innerText, title, rel, các thuộc tính data-, v.v. Tất cả những thứ này có thể được phản chiếu với bảng chỉ mục liên kết của chúng ta. Trong khi đó, parent và root vẫn giữ nguyên là các liên kết cứng.
Dưới đây là phần tóm tắt của ChatGPT:
Vâng, điều này trông đã rất mạch lạc rồi.
Khi đó, nội dung HTML/Markdown trở thành đồ thị ngữ nghĩa chính, còn bảng liên kết đóng vai trò là chỉ mục được hiện thực hóa (materialized index) của nó để truy vấn nhanh và tạo liên kết ngược.
Đồng thời:
parentId— hệ thống cấp bậc trực tiếp cứng;rootId— thuộc về cùng một cây hoặc một chuyên mục;- Các liên kết HTML — liên kết ngữ nghĩa mềm nhiều-nhiều;
- Bảng quan hệ — bản sao phản chiếu các liên kết từ nội dung.
Liên kết như một mối quan hệ ngữ nghĩa đầy đủ
Ví dụ, trong trình soạn thảo:
<a
href="/places/som-homestay"
data-concept-id="som-homestay-id"
data-relation="overnight"
data-context="Chỗ nghỉ qua đêm thích hợp trước khi ghé thăm hang động từ sớm"
data-quality="0.9"
title="Som Homestay — chỗ nghỉ qua đêm gần hang động"
>
ngủ qua đêm tại Som Homestay
</a>
Phần này đã chứa hầu như tất cả các ngữ nghĩa cần thiết:
href— địa chỉ công khai;data-concept-id— định danh kỹ thuật ổn định;innerText— thẻ neo tự nhiên;title— giải thích bổ sung cho người dùng;data-relation— loại quan hệ ngữ nghĩa;data-context— lý do liên kết xuất hiện;data-quality— độ mạnh hoặc tính hữu ích của liên kết.
Từ đó, một bản ghi chỉ mục được tạo ra:
sourceId: cave-id
targetId: som-homestay-id
relation: overnight
anchor: ngủ qua đêm tại Som Homestay
title: Som Homestay — chỗ nghỉ qua đêm gần hang động
context: Chỗ nghỉ qua đêm thích hợp trước khi ghé thăm hang động từ sớm
quality: 0.9
Lưu gì trong rel
Tôi sẽ cẩn thận khi sử dụng thuộc tính HTML chuẩn rel.
Nó đã có sẵn ngữ nghĩa về trình duyệt và SEO:
nofollow;ugc;sponsored;noopener;noreferrer;alternate;author;tag.
Tốt hơn hết là nên lưu trữ ngữ nghĩa miền (domain semantics) riêng của chúng ta tách biệt:
data-relation="overnight-before"
Và sử dụng rel chuẩn đúng với mục đích của nó:
rel="ugc nofollow"
Nếu không, sau này bạn có thể gặp xung đột giữa mô hình riêng của bạn và chuẩn HTML.
Định dạng liên kết tối thiểu
Đối với công việc thủ công trong trình soạn thảo, quá nhiều thuộc tính sẽ gây vướng víu. Do đó, tôi chỉ coi các mục sau là bắt buộc:
<a
data-concept-id="..."
data-relation="..."
>
văn bản liên kết
</a>
Phần còn lại có thể được xác định tự động:
href— lấy thông quaSiteRoute;anchor— lấy từinnerText;title— tạo từ khái niệm được liên kết;quality— sử dụng giá trị mặc định;context— trích xuất từ câu xung quanh;position— tính toán bằng trình phân tích cú pháp (parser).
Chế độ nâng cao chỉ cần thiết khi tác giả muốn ghi đè ngữ nghĩa một cách rõ ràng:
<a
data-concept-id="..."
data-relation="overnight"
data-quality="0.95"
data-context="Chỗ nghỉ tốt nhất trước khi xuất phát sớm"
>
Som Homestay
</a>
Văn bản xung quanh cũng mang ngữ nghĩa
Giả sử viết rằng:
Trước khi ghé thăm hang động từ sớm, tốt hơn là nên
<a data-concept-id="som-id">ngủ qua đêm tại Som Homestay</a>,
vì đường đi vào buổi sáng chỉ mất khoảng 30 phút.
Ngay cả khi không có data-relation rõ ràng, một agent hoặc parser vẫn có thể suy ra:
relation: overnight_before
context: chỗ nghỉ qua đêm trước khi ghé thăm hang động từ sớm
Nhưng tôi sẽ phân biệt giữa:
- ngữ nghĩa rõ ràng của tác giả;
- ngữ nghĩa được xác định tự động.
Ví dụ:
origin: explicit
hoặc:
origin: inferred
confidence: 0.72
Không nhất thiết phải hiển thị điều này cho biên tập viên. Đây là thông tin kỹ thuật của bảng chỉ mục.
Sơ đồ bảng có thể có
enum KBConceptRelationOrigin {
explicit
inferred
generated
}
model KBConceptRelation {
id String @id @default(cuid()) @db.VarChar(36)
createdAt DateTime @default(now()) @db.Timestamp(3)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamp(3)
sourceId String
Source KBConcept @relation(
"ConceptRelationSource",
fields: [sourceId],
references: [id],
onDelete: Cascade
)
targetId String
Target KBConcept @relation(
"ConceptRelationTarget",
fields: [targetId],
references: [id],
onDelete: Cascade
)
relation String? @db.VarChar(100)
anchor String? @db.Text
title String? @db.Text
context String? @db.Text
quality Float @default(0.5)
confidence Float?
origin KBConceptRelationOrigin @default(explicit)
// Định danh ổn định của một liên kết cụ thể trong trình soạn thảo
nodeId String? @db.VarChar(100)
// Vị trí trong nội dung
position Int?
// Các thuộc tính HTML gốc hoặc bổ sung
data Json?
@@unique([sourceId, nodeId])
@@index([sourceId])
@@index([targetId])
@@index([sourceId, relation])
@@index([targetId, relation])
@@index([quality])
}
quality và confidence ở đây mang ý nghĩa khác nhau:
quality— liên kết hữu ích cho người dùng đến mức nào;confidence— ngữ nghĩa của nó được xác định chắc chắn đến mức nào.
Đối với một liên kết được chỉ định rõ ràng:
origin = explicit
confidence = 1
Đối với liên kết được nhận diện tự động:
origin = inferred
confidence = 0.74
Ở giai đoạn đầu, có thể hoàn toàn chưa cần thêm confidence.
Cần có định danh ổn định cho nút HTML
Nếu chỉ dùng sourceId + targetId, vấn đề sẽ phát sinh: một văn bản có thể liên kết đến một khái niệm nhiều lần vì những lý do khác nhau.
Ví dụ:
<a
data-node-id="link-a17"
data-concept-id="som-id"
data-relation="overnight-before"
>
ngủ qua đêm trước khi tham quan
</a>
<a
data-node-id="link-b24"
data-concept-id="som-id"
data-relation="cold-weather-warning"
>
mùa đông ở đây có thể rất lạnh
</a>
Đây là hai mối quan hệ độc lập trỏ đến cùng một đích.
data-node-id phải do trình soạn thảo tự động tạo ra và giữ nguyên khi liên kết được di chuyển bên trong tài liệu.
Khi đó việc đồng bộ hóa khi lưu sẽ rất đơn giản:
- liên kết có cùng
nodeId— được cập nhật; nodeIdmới — được thêm vào;- thiếu
nodeId— bị xóa.
HTML và chỉ mục phải có một nguồn chân lý duy nhất (Single Source of Truth)
Tốt nhất là nên tránh việc chỉnh sửa hai chiều ở đây.
Tôi sẽ chốt lại rằng:
Nội dung là nguồn chân lý cho các liên kết biên tập đi ra (outgoing).
Bảng chỉ là chỉ mục.
Điều đó có nghĩa là:
- người dùng chỉnh sửa liên kết trong văn bản;
- sau khi lưu, chỉ mục được tính toán lại;
- việc chỉnh sửa trực tiếp các dòng trong bảng chỉ mục bị cấm hoặc bị hạn chế;
- các khối liên kết ngược (backlinks) được hình thành từ bảng chỉ mục;
- việc thay đổi mối quan hệ đi vào được thực hiện thông qua khái niệm nguồn.
Nếu không sẽ xảy ra tình trạng mất đồng bộ: bảng nói thế này, HTML nói thế khác.
Các liên kết tự động có thể được lưu trữ tách biệt với liên kết nội dung và không phản chiếu vào HTML cho đến khi được chấp nhận rõ ràng.
Liên kết ngược (Backlink)
Nếu trong thẻ hang động có:
<a
data-concept-id="som-id"
data-relation="overnight-after"
>
Som Homestay
</a>
Thì trên trang của Som Homestay, bạn có thể tự động hiển thị:
Thích hợp để nghỉ qua đêm sau khi tham quan
Hang động X — tác giả đề xuất homestay này làm nơi nghỉ qua đêm sau chuyến tham quan.
Hoặc nhúng một khối động vào văn bản:
<concept-backlinks
data-relation="overnight-after"
data-limit="5"
></concept-backlinks>
Hơn nữa, phần hiển thị ngược không bắt buộc phải lặp lại thẻ neo ban đầu. Nó được xây dựng từ:
- loại quan hệ;
- ngữ cảnh;
- khái niệm nguồn;
- chất lượng liên kết.
parent và root thực sự rất đáng được giữ lại
Chúng giải quyết một bài toán khác.
parentId
Sự trực thuộc cứng hoặc vùng chứa chính:
Thái Nguyên
└── Som Homestay
hoặc cấu trúc nội dung:
Chỗ ở
└── Homestay
└── Som Homestay
Chỉ cần chọn một ý nghĩa duy nhất cho parentId và không trộn lẫn nhiều hệ thống phân cấp khác nhau.
Ví dụ:
parentIdluôn có nghĩa là sự trực thuộc mang tính biên tập vào chuyên mục chính.
rootId
Giúp tăng tốc:
- lấy toàn bộ cây;
- tìm kiếm trong chuyên mục;
- tính duy nhất của mã;
- quản lý một nhánh lớn;
- xóa hoặc di chuyển chuyên mục.
Liên kết mềm
Mọi thứ còn lại:
- chỗ ở được đề xuất;
- một phần của khu phức hợp;
- phương án thay thế;
- điểm dừng tiếp theo trong lộ trình;
- liên quan đến rủi ro thời tiết;
- được nhắc đến trong hành trình;
đều đi qua chỉ mục liên kết.
Ngữ nghĩa đầy đủ không nhất thiết đòi hỏi các thẻ phức tạp
Bạn có thể bắt đầu với một liên kết thông thường:
<a data-concept-id="som-id">Som Homestay</a>
Và loại quan hệ được xác định qua ba cấp độ:
data-relationrõ ràng.- Ngữ cảnh của khối cha.
- Phân tích văn bản xung quanh bởi agent.
Ví dụ:
<section data-semantic-context="recommended-overnight">
<p>
Trước chuyến đi, tốt nhất nên dừng chân tại
<a data-concept-id="som-id">Som Homestay</a>.
</p>
</section>
Ở đây, mỗi liên kết bên trong section sẽ kế thừa:
relation = recommended_overnight
Điều này có thể đơn giản hóa rất nhiều việc gắn thẻ thủ công.
Tương tự trong các thành phần Markdown:
:::semantic{relation="overnight-before"}
Trước khi ghé thăm sớm, tốt nhất nên dừng lại ở
[Som Homestay](concept:som-id).
:::
Ngữ nghĩa có thể tồn tại ở các cấp độ DOM khác nhau
Trên liên kết
Dành cho mối quan hệ cá thể chính xác:
<a data-relation="alternative">
Trên đoạn văn
Dành cho ngữ cảnh chung:
<p data-semantic-context="winter-warning">
Trên section
Dành cho một nhóm liên kết:
<section data-semantic-context="where-to-stay">
Trên custom component
Dành cho việc truy vấn động:
<concept-list
data-relation="overnight-before"
data-source="current"
></concept-list>
Parser có thể kế thừa ngữ nghĩa từ trên xuống dưới:
link.relation
?? paragraph.semanticContext
?? section.semanticContext
?? inferredFromText
?? "reference"
Kết quả thu được cuối cùng
Bạn sẽ có bốn tầng đồng bộ với nhau:
Cây cứng:
parentId / rootId
Ý nghĩa của tác giả:
Nội dung HTML và các liên kết
Đồ thị nhanh:
KBConceptRelation
Biểu diễn động:
liên kết ngược, danh sách và các khối agent
Đây là một kiến trúc rất thực tế. Nó không đòi hỏi phải mô tả lại toàn bộ thế giới trong các bảng cơ sở dữ liệu một cách rời rạc: ngữ nghĩa xuất hiện ngay trong quá trình viết văn bản bình thường của con người, và cơ sở dữ liệu chỉ đơn thuần hiện thực hóa nó để phục vụ tìm kiếm, phân tích và tái sử dụng.