AIAI

Giới thiệu về SCI Pháp lý

Giới thiệu về SCI Pháp lý

const fs = require("fs");

const {

Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType,

Table, TableRow, TableCell, WidthType, ShadingType, BorderStyle,

LevelFormat, convertInchesToTwip,

} = require("docx");

const FONT = "Calibri";

const ACCENT = "A85D3E"; // terracotta, matches site accent-hover

const HEAD_BG = "2D2B28"; // site header charcoal

const LIGHT_BG = "FDF4EF"; // site accent-bg

function h(text, level) {

return new Paragraph({ text, heading: level, spacing: { before: 280, after: 120 } });

}

function p(text, opts = {}) {

return new Paragraph({

spacing: { after: 140 },

children: [new TextRun({ text, font: FONT, size: 22, ...opts })],

});

}

function pRich(runs) {

return new Paragraph({ spacing: { after: 140 }, children: runs.map((r) => new TextRun({ font: FONT, size: 22, ...r })) });

}

function bold(text) {

return new Paragraph({ spacing: { after: 60 }, children: [new TextRun({ text, font: FONT, size: 22, bold: true })] });

}

function bullet(text, level = 0) {

return new Paragraph({

numbering: { reference: "bullets", level },

spacing: { after: 70 },

children: [new TextRun({ text, font: FONT, size: 22 })],

});

}

function bulletRich(runs, level = 0) {

return new Paragraph({

numbering: { reference: "bullets", level },

spacing: { after: 70 },

children: runs.map((r) => new TextRun({ font: FONT, size: 22, ...r })),

});

}

function num(text, level = 0) {

return new Paragraph({

numbering: { reference: "nums", level },

spacing: { after: 70 },

children: [new TextRun({ text, font: FONT, size: 22 })],

});

}

function cell(children, opts = {}) {

return new TableCell({

children: Array.isArray(children) ? children : [children],

width: { size: opts.width || 2000, type: WidthType.DXA },

shading: opts.header ? { type: ShadingType.CLEAR, fill: HEAD_BG } : (opts.fill ? { type: ShadingType.CLEAR, fill: opts.fill } : undefined),

margins: { top: 80, bottom: 80, left: 100, right: 100 },

});

}

function headCell(text, width) {

return cell(new Paragraph({ children: [new TextRun({ text, font: FONT, size: 19, bold: true, color: "FFFFFF" })] }), { header: true, width });

}

function bodyCell(text, width) {

return cell(new Paragraph({ children: [new TextRun({ text, font: FONT, size: 20 })] }), { width });

}

function cmpTable() {

const widths = [1600, 3400, 1700, 3300];

return new Table({

width: { size: widths.reduce((a, b) => a + b, 0), type: WidthType.DXA },

columnWidths: widths,

rows: [

new TableRow({ children: [headCell("Cách đọc", widths[0]), headCell("Khi nào dùng", widths[1]), headCell("Ai quyết định", widths[2]), headCell("Bạn sẽ thấy", widths[3])] }),

new TableRow({ children: [

bodyCell("RAG", widths[0]),

bodyCell("Mặc định khi có văn bản để đọc — nhanh, rẻ, cần Admin đã bấm \"Index RAG\" cho văn bản đó từ trước.", widths[1]),

bodyCell("Bạn (mặc định bật)", widths[2]),

bodyCell("Trả lời nhanh, trích đúng đoạn liên quan thay vì đọc hết văn bản.", widths[3]),

] }),

new TableRow({ children: [

bodyCell("Nguyên văn", widths[0]),

bodyCell("Bạn chủ động bật (cần nhập mã xác nhận), hoặc hệ thống tự chuyển khi RAG không có dữ liệu và văn bản đủ ngắn.", widths[1]),

bodyCell("Bạn / Tự động", widths[2]),

bodyCell("Chính xác nhất vì đọc toàn bộ, nhưng chậm hơn với văn bản dài.", widths[3]),

] }),

new TableRow({ children: [

bodyCell("Theo Điều", widths[0]),

bodyCell("Tự động, khi RAG không có dữ liệu và tổng văn bản quá dài để đọc hết nguyên văn.", widths[1]),

bodyCell("Tự động", widths[2]),

bodyCell("AI tự dò mục lục, chỉ đọc đúng (các) Điều liên quan tới câu hỏi.", widths[3]),

] }),

new TableRow({ children: [

bodyCell("Không tìm kiếm", widths[0]),

bodyCell("Tự động, khi bạn chưa chọn phạm vi (nhánh Luật / Dự án) nào cả.", widths[1]),

bodyCell("Tự động", widths[2]),

bodyCell("AI trả lời bằng kiến thức chung, không trích dẫn văn bản cụ thể nào.", widths[3]),

] }),

],

});

}

const doc = new Document({

numbering: {

config: [

{ reference: "bullets", levels: [

{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 420, hanging: 260 } } } },

{ level: 1, format: LevelFormat.BULLET, text: "◦", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 820, hanging: 260 } } } },

] },

{ reference: "nums", levels: [

{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT, style: { paragraph: { indent: { left: 420, hanging: 260 } } } },

] },

],

},

sections: [{

properties: { page: { size: { width: 11906, height: 16838 }, margin: { top: 1000, bottom: 1000, left: 1100, right: 1100 } } }, // A4

children: [

// ═══ GIỚI THIỆU ═══

h("Giới thiệu", HeadingLevel.HEADING_1),

p("Một nơi để tra cứu văn bản pháp luật xây dựng và quản lý hồ sơ pháp lý của từng dự án công ty — có Trợ lý AI đọc giúp và soạn báo cáo."),

h("Hai khu vực", HeadingLevel.HEADING_2),

bold("Khu công khai — ai cũng xem được, không cần tài khoản"),

bullet("Tra cứu văn bản pháp luật xây dựng (luật, nghị định, quyết định…)"),

bullet("Bài hướng dẫn nghiệp vụ, biểu mẫu"),

bullet("Bản đồ toàn bộ cơ sở dữ liệu"),

bold("Khu nội bộ — cần đăng nhập và được thêm làm thành viên dự án"),

bullet("Hồ sơ pháp lý riêng của từng dự án công ty"),

bullet("Phân quyền Xem / Sửa độc lập theo từng dự án"),

bullet("Đăng, sửa, xoá tài liệu (nếu được cấp quyền Sửa)"),

h("Điểm nổi bật", HeadingLevel.HEADING_2),

bulletRich([{ text: "Cây danh mục 3 cấp — ", bold: true }, { text: "Việt Nam / Lào / Quảng Trị, mỗi nhánh chia nhỏ theo chuyên môn." }]),

bulletRich([{ text: "Mục lục tự sinh — ", bold: true }, { text: "văn bản dài tự động có mục lục bên cạnh (dò Chương / Điều / Khoản)." }]),

bulletRich([{ text: "Quan hệ hiệu lực — ", bold: true }, { text: "hệ thống tự suy ra văn bản nào đã bị thay thế, hết hiệu lực." }]),

bulletRich([{ text: "Trợ lý pháp lý AI — ", bold: true }, { text: "hỏi đáp có trích dẫn đúng Điều/Khoản, nhớ ngữ cảnh, tìm thêm trên web khi cần." }]),

bulletRich([{ text: "Xuất báo cáo 11 định dạng — ", bold: true }, { text: "HTML, Word, Excel, PDF, PowerPoint, Markdown, SVG, Mermaid, Graphviz, KML, DXF." }]),

bulletRich([{ text: "Phân quyền rõ ràng — ", bold: true }, { text: "3 vai trò hệ thống, cộng quyền Xem/Sửa riêng theo từng dự án." }]),

// ═══ HƯỚNG DẪN SỬ DỤNG ═══

h("Hướng dẫn sử dụng", HeadingLevel.HEADING_1),

h("Người dùng thường", HeadingLevel.HEADING_2),

h("1. Tra cứu văn bản pháp luật", HeadingLevel.HEADING_3),

num("Chọn nhánh trong \"Cây thư mục\" (Việt Nam / Lào / Quảng Trị) để thu hẹp phạm vi, hoặc để nguyên \"Tất cả\"."),

num("Lọc thêm nếu cần: trạng thái hiệu lực, loại văn bản, hoặc gõ thẳng số hiệu / tên văn bản vào ô Tìm kiếm rồi bấm Lọc."),

num("Bấm vào tên văn bản để xem toàn văn — có mục lục bên cạnh để nhảy nhanh tới Điều cần đọc, và nút tải bản gốc PDF có dấu đỏ nếu có."),

h("2. Xem tài liệu nội bộ dự án", HeadingLevel.HEADING_3),

p("Chỉ áp dụng nếu bạn đã được Admin thêm làm thành viên của dự án đó."),

num("Đăng nhập bằng email + mật khẩu được cấp."),

num("Vào mục \"Nội bộ\" — chỉ hiện các dự án bạn là thành viên."),

num("Đọc hoặc sửa tuỳ quyền được cấp: quyền \"Xem\" chỉ đọc được; quyền \"Sửa\" đăng/sửa/xoá được tài liệu trong đúng dự án đó."),

h("3. Đăng xuất / đổi tài khoản", HeadingLevel.HEADING_3),

p("Bấm \"Đăng xuất\" ở góc phải thanh trên cùng (hoặc trong trang quản trị nếu đang ở đó) rồi đăng nhập lại bằng tài khoản khác."),

h("Quản trị viên / CTV pháp luật", HeadingLevel.HEADING_2),

p("Mục này chỉ hiện đầy đủ với tài khoản Admin hoặc CTV pháp luật. Tài khoản Người xem không vào được trang quản trị."),

h("1. Đăng bài luật bằng AI", HeadingLevel.HEADING_3),

num("Quản trị → Đăng Luật (AI) — dán nội dung hoặc tải file văn bản luật lên."),

num("AI tự trích các trường (số hiệu, loại văn bản, cơ quan ban hành, ngày hiệu lực…) — kiểm tra lại trước khi lưu."),

num("Chọn danh mục, gắn văn bản vào đúng nhánh cây thư mục rồi Đăng bài."),

h("2. Đăng tài liệu thường / đăng dự án", HeadingLevel.HEADING_3),

pRich([{ text: "Đăng tài liệu: ", bold: true }, { text: "dùng cho biểu mẫu, hướng dẫn — chỉ cần tải file, không cần AI trích trường." }]),

pRich([{ text: "Đăng dự án (AI): ", bold: true }, { text: "tạo dự án nội bộ mới, hệ thống tự sinh sẵn 12 thư mục con cố định cho dự án đó." }]),

h("3. Quét hiệu lực bằng AI", HeadingLevel.HEADING_3),

p("Quản trị → Quét hiệu lực. Chỉ quét trong khu \"Văn bản pháp luật\" — không đụng tài liệu dự án. AI chỉ đề xuất — bạn luôn phải bấm \"Duyệt\" thì hệ thống mới chuyển văn bản cũ sang trạng thái \"Hết hiệu lực\". Có thể thêm quan hệ thủ công nếu AI bỏ sót."),

h("4. Chỉ dành cho Admin", HeadingLevel.HEADING_3),

bulletRich([{ text: "Quản lý người dùng — ", bold: true }, { text: "tạo tài khoản, đổi vai trò hệ thống, gán quyền Xem/Sửa riêng cho từng dự án." }]),

bulletRich([{ text: "Quản lý menu — ", bold: true }, { text: "sửa tên, ẩn/hiện, sắp xếp lại cây danh mục hiển thị ngoài trang công khai." }]),

bulletRich([{ text: "Cấu hình AI — ", bold: true }, { text: "nhập API key, bật/tắt tìm kiếm web mặc định, chỉnh ngân sách ký tự mỗi lượt hỏi." }]),

bulletRich([{ text: "Nhật ký AI — ", bold: true }, { text: "xem lại các lượt gọi AI trong hệ thống, phục vụ kiểm tra chi phí và chất lượng trả lời." }]),

// ═══ DÙNG TRỢ LÝ AI ═══

h("Dùng Trợ lý pháp lý AI", HeadingLevel.HEADING_1),

p("Trợ lý mở từ nút nổi trên mọi trang. Phần này giải thích chính xác cách nó đọc tài liệu, khi nào dùng cách nào, và cách xuất/sửa báo cáo."),

h("1. Đưa tài liệu vào cho AI đọc", HeadingLevel.HEADING_2),

p("Hệ thống có 2 cách nạp tài liệu — quản trị viên đặt cách mặc định cho cả hệ thống, mỗi cuộc hội thoại giữ nguyên 1 cách trong suốt phiên."),

bold("Cách A — Theo thư mục / dự án (thường gặp nhất)"),

p("Bạn tick vào các nhánh Luật và/hoặc Dự án muốn AI đọc. Ba loại nguồn cho biết AI đang lấy văn bản từ đâu:"),

bulletRich([{ text: "📌 pham-vi — ", bold: true }, { text: "nằm trong nhánh/dự án bạn đã chọn." }]),

bulletRich([{ text: "↺ giữ lại — ", bold: true }, { text: "đã đọc ở câu hỏi trước, AI tự giữ để hỏi tiếp không bị mất mạch." }]),

bulletRich([{ text: "✦ tìm thấy — ", bold: true }, { text: "AI tự tìm thêm theo từ khoá trong câu hỏi, ngoài phạm vi đã chọn." }]),

pRich([{ text: "Lưu ý: ", bold: true }, { text: "không tick nhánh nào → AI trả lời bằng kiến thức chung, không đọc văn bản nào cả — hệ thống sẽ nhắc bạn chọn phạm vi." }]),

p("Muốn hỏi riêng về 1 file chưa có trên hệ thống (hợp đồng, công văn…) mà không thêm hẳn vào phạm vi: bấm biểu tượng kẹp giấy và đính kèm — file chỉ dùng cho đúng câu hỏi đó."),

bold("Cách B — Theo file / Kho dữ liệu"),

num("Kẹp file — hệ thống tự trích chữ và dò các căn cứ pháp lý được nhắc trong file."),

num("Xem kết quả dò: ✓ xanh = văn bản đã có sẵn trên hệ thống, ✗ đỏ = chưa có (AI sẽ không đọc được)."),

num("Bấm \"Chuyển vào kho\" để thêm các mục ✓ vào kho — không tự động, bạn luôn phải bấm."),

p("Kho giữ nguyên suốt cả cuộc hội thoại (đóng mở lại phiên vẫn còn) — xoá bớt bằng dấu × trên từng chip. Mọi câu hỏi trong phiên này AI đều đọc toàn bộ những gì đang có trong kho."),

h("2. AI đọc nguyên văn, theo Điều, hay qua RAG?", HeadingLevel.HEADING_2),

p("4 cách đọc tài liệu — phần lớn hệ thống tự chọn giúp bạn, chỉ 1 phần bạn bấm nút chọn được."),

cmpTable(),

new Paragraph({ spacing: { before: 140, after: 140 }, children: [new TextRun({ text: "Cuối mỗi câu trả lời có 1 dòng nhỏ ghi lại cách AI vừa đọc (vd \"Đã đọc 3 văn bản · RAG\") — nhìn dòng đó là biết ngay. Nút chuyển sang Nguyên văn yêu cầu nhập mã xác nhận trước khi bật.", font: FONT, size: 22 })] }),

h("3. Tìm kiếm web", HeadingLevel.HEADING_2),

p("Nút cạnh ô nhập — bạn bật/tắt tuỳ ý cho từng câu hỏi (mặc định tắt, trừ khi Admin đã bật sẵn cho cả hệ thống). Bật nút chỉ là \"cho phép\" — AI mới là bên quyết định có thật sự tìm trên mạng cho câu hỏi cụ thể đó hay không, dựa vào việc tài liệu đã có có đủ trả lời hay cần thông tin mới hơn."),

h("4. Nén hội thoại dài — lệnh /compact", HeadingLevel.HEADING_2),

p("Hội thoại càng dài, AI càng phải \"nhớ\" nhiều — tốn thời gian và chi phí. Gõ đúng /compact vào ô chat rồi gửi như một câu hỏi bình thường: hệ thống giữ nguyên 4 lượt gần nhất, tóm tắt phần cũ hơn thành 1 đoạn ngắn. Toàn bộ tin nhắn cũ vẫn hiện nguyên trên màn hình — chỉ phần AI \"nhớ\" là bị rút gọn."),

p("Hệ thống cũng tự động nén ngầm mỗi khi 1 câu trả lời dùng hết trên 85% bộ nhớ ngữ cảnh — bạn không cần làm gì, chỉ câu hỏi tiếp theo sẽ nhẹ hơn."),

h("5. Xuất báo cáo — 11 định dạng", HeadingLevel.HEADING_2),

p("HTML, Word (DOCX), Excel (XLSX), PDF, PowerPoint (PPTX), Markdown, SVG, Mermaid, Graphviz, KML, DXF — chỉ cần gõ yêu cầu trong câu hỏi, ví dụ \"xuất báo cáo này ra Word\"."),

bold("Với yêu cầu phức tạp — duyệt kế hoạch trước"),

p("Yêu cầu có nhiều phần, bảng biểu, biểu đồ hoặc cần phân tích/so sánh → AI tự dừng lại, trình bày kế hoạch trước. Bấm \"Sửa kế hoạch\" để sửa thẳng vào đoạn văn bản kế hoạch đó, rồi \"Duyệt & tạo báo cáo\" khi đã ưng. Yêu cầu đơn giản thì AI làm thẳng luôn, không hỏi."),

bold("Sửa file đã xuất"),

num("Bấm \"Sửa file này\" trên file vừa xuất — chưa gửi gì cả, chỉ đánh dấu \"đang sửa file này\"."),

num("Gõ yêu cầu chỉnh sửa như một câu hỏi bình thường."),

num("Nhận file mới: AI viết lại toàn bộ file nhưng được dặn giữ nguyên phần bạn không yêu cầu đổi. File cũ vẫn còn — có thể sửa tiếp nhiều vòng liên tiếp."),

p("Tên file tự đặt theo đúng nội dung bạn yêu cầu + ngày tháng, ví dụ \"Điều kiện cấp phép xây dựng nhà ở-10-08-26.pdf\" — không còn dãy số khó hiểu."),

],

}],

});

Packer.toBuffer(doc).then((buf) => {

fs.writeFileSync(__dirname + "/SCI-Phap-ly-Gioi-thieu-Huong-dan.docx", buf);

console.log("OK");

});