Feature/fsg gamma team ui fix (#3)
CI / test (push) Canceled after 0s

## Summary

What changed and why?

## Change Type

- [ ] Cowork feature
- [ ] Bug fix
- [x] Core AI contribution
- [ ] Test / hardening
- [ ] Performance
- [ ] Documentation

## Related Work

Cowork Task:

Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets

Core AI Issue:

Core Task:

Related PR:

## Scope

What is intentionally included?

What is intentionally NOT included?

## Validation

- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual verification
- [ ] Regression check

Commands / evidence:

## Security Impact

Permission / credential / network / customer data impact:

## Compatibility

- [ ] No breaking change
- [ ] Breaking change documented

## Reviewer Notes

Anything Cowork reviewers should pay attention to.

---------

Co-authored-by: Hiep Ha Van <hiephv3@fpt.com>
Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com>
Co-authored-by: Lam Hoang Van <lamhv7@fpt.com>
Co-authored-by: NamPDT <minhanhpkpro@gmail.com>
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-08-20 12:12:56 +00:00
co-authored by Hiep Ha Van Nam Pham Dinh Thanh lamhv7 NamPDT
parent 414eaddca3
commit 1419587401
137 changed files with 23356 additions and 3722 deletions
Binary file not shown.
+327
View File
@@ -0,0 +1,327 @@
<!doctype html>
<html lang="vi" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cấu trúc hệ thống · Cowork Local</title>
<style>
:root{
--bg:#f6f7f9; --surface:#ffffff; --surface-2:#eef1f4; --border:#dbe0e7;
--ink:#131a22; --ink-2:#4b5663; --ink-3:#7c8794;
--accent:#0e7c86; --accent-ink:#0a5b63; --accent-soft:#e2f2f2;
--ok:#157f4a; --warn:#9a5a0e; --crit:#b5322c;
--ok-soft:#e4f4ea; --warn-soft:#f8efdd; --crit-soft:#f7e5e3;
--mono-bg:#eef2f6; --shadow:0 1px 2px rgba(16,24,32,.06),0 8px 24px -12px rgba(16,24,32,.18);
--font-display:"Segoe UI Variable Display","Segoe UI Semibold","Segoe UI",system-ui,-apple-system,sans-serif;
--font-body:"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,sans-serif;
--font-mono:"Cascadia Code","Cascadia Mono",Consolas,"SF Mono",ui-monospace,monospace;
--maxw:920px;
}
@media (prefers-color-scheme:dark){
:root{
--bg:#0d1218; --surface:#141c25; --surface-2:#1b2530; --border:#28343f;
--ink:#e7edf4; --ink-2:#a2b2c2; --ink-3:#6c7d8e;
--accent:#46cfc8; --accent-ink:#8fe9e3; --accent-soft:#14312f;
--ok:#47c97e; --warn:#e0a33a; --crit:#f0726b;
--ok-soft:#12281c; --warn-soft:#2c2410; --crit-soft:#2e1614;
--mono-bg:#0f1720; --shadow:0 1px 2px rgba(0,0,0,.4),0 10px 30px -16px rgba(0,0,0,.7);
}
}
:root[data-theme="light"]{
--bg:#f6f7f9; --surface:#ffffff; --surface-2:#eef1f4; --border:#dbe0e7;
--ink:#131a22; --ink-2:#4b5663; --ink-3:#7c8794;
--accent:#0e7c86; --accent-ink:#0a5b63; --accent-soft:#e2f2f2;
--ok:#157f4a; --warn:#9a5a0e; --crit:#b5322c;
--ok-soft:#e4f4ea; --warn-soft:#f8efdd; --crit-soft:#f7e5e3; --mono-bg:#eef2f6;
--shadow:0 1px 2px rgba(16,24,32,.06),0 8px 24px -12px rgba(16,24,32,.18);
}
:root[data-theme="dark"]{
--bg:#0d1218; --surface:#141c25; --surface-2:#1b2530; --border:#28343f;
--ink:#e7edf4; --ink-2:#a2b2c2; --ink-3:#6c7d8e;
--accent:#46cfc8; --accent-ink:#8fe9e3; --accent-soft:#14312f;
--ok:#47c97e; --warn:#e0a33a; --crit:#f0726b;
--ok-soft:#12281c; --warn-soft:#2c2410; --crit-soft:#2e1614; --mono-bg:#0f1720;
--shadow:0 1px 2px rgba(0,0,0,.4),0 10px 30px -16px rgba(0,0,0,.7);
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-body);
font-size:16.5px;line-height:1.62;-webkit-font-smoothing:antialiased;}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px}
/* top bar */
header.top{position:sticky;top:0;z-index:20;background:color-mix(in srgb,var(--surface) 88%,transparent);
backdrop-filter:saturate(1.4) blur(10px);border-bottom:1px solid var(--border)}
.top .wrap{display:flex;align-items:center;gap:18px;height:58px}
.brand{font-family:var(--font-mono);font-weight:600;font-size:14px;letter-spacing:.02em;
color:var(--ink);display:flex;align-items:center;gap:9px;white-space:nowrap}
.brand .dot{width:10px;height:10px;border-radius:2px;background:var(--accent);
box-shadow:0 0 0 3px var(--accent-soft)}
nav.doc{display:flex;gap:4px;margin-left:auto;flex-wrap:wrap}
nav.doc a{font-size:13.5px;color:var(--ink-2);text-decoration:none;padding:7px 12px;border-radius:8px;
font-weight:550;white-space:nowrap}
nav.doc a:hover{background:var(--surface-2);color:var(--ink)}
nav.doc a[aria-current="page"]{background:var(--accent-soft);color:var(--accent-ink)}
.toggle{border:1px solid var(--border);background:var(--surface);color:var(--ink-2);
width:36px;height:34px;border-radius:9px;cursor:pointer;font-size:15px;display:grid;place-items:center}
.toggle:hover{color:var(--ink);border-color:var(--accent)}
.toggle:focus-visible,nav.doc a:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* hero */
.hero{padding:64px 0 34px}
.eyebrow{font-family:var(--font-mono);font-size:12px;letter-spacing:.18em;text-transform:uppercase;
color:var(--accent-ink);font-weight:600;margin:0 0 14px}
h1{font-family:var(--font-display);font-weight:700;font-size:clamp(2.1rem,5vw,3.1rem);line-height:1.06;
letter-spacing:-.02em;margin:0 0 18px;text-wrap:balance}
.lead{font-size:1.16rem;color:var(--ink-2);max-width:64ch;margin:0}
.meta{display:flex;gap:10px;flex-wrap:wrap;margin-top:26px}
.tag{font-family:var(--font-mono);font-size:12px;padding:5px 11px;border-radius:999px;
background:var(--surface-2);border:1px solid var(--border);color:var(--ink-2)}
section{padding:34px 0;border-top:1px solid var(--border)}
h2{font-family:var(--font-display);font-weight:650;font-size:1.6rem;letter-spacing:-.01em;margin:0 0 6px;
display:flex;align-items:baseline;gap:12px;text-wrap:balance}
h2 .num{font-family:var(--font-mono);font-size:.85rem;color:var(--accent-ink);font-weight:600}
h3{font-family:var(--font-display);font-weight:600;font-size:1.13rem;margin:26px 0 8px;letter-spacing:-.01em}
p{margin:.6em 0}
.sub{color:var(--ink-2);margin:2px 0 20px;max-width:66ch}
a{color:var(--accent-ink);text-underline-offset:3px}
strong{font-weight:650;color:var(--ink)}
code,.k{font-family:var(--font-mono);font-size:.86em;background:var(--mono-bg);
padding:2px 6px;border-radius:6px;border:1px solid var(--border);color:var(--ink)}
/* layer stack diagram */
.stack{display:flex;flex-direction:column;gap:0;margin:24px 0}
.layer{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:16px 18px;
box-shadow:var(--shadow);position:relative}
.layer + .layer{margin-top:26px}
.layer + .layer::before{content:"";position:absolute;top:-20px;left:50%;width:2px;height:14px;
background:var(--border);transform:translateX(-50%)}
.layer + .layer::after{content:"▾";position:absolute;top:-14px;left:50%;transform:translateX(-50%);
color:var(--ink-3);font-size:13px}
.layer .lh{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.layer .lh .tier{font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
color:#fff;background:var(--accent);padding:3px 8px;border-radius:6px;font-weight:600}
.layer .lh h4{margin:0;font-family:var(--font-display);font-weight:600;font-size:1.02rem}
.layer .lh small{color:var(--ink-3);margin-left:auto;font-size:12.5px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{font-family:var(--font-mono);font-size:12.5px;background:var(--surface-2);border:1px solid var(--border);
border-radius:8px;padding:6px 10px;color:var(--ink-2)}
.chip b{color:var(--ink);font-weight:600}
/* cards */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:16px;margin-top:8px}
.card{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:18px 18px;
box-shadow:var(--shadow)}
.card h4{margin:0 0 6px;font-family:var(--font-display);font-size:1.04rem;font-weight:600;
display:flex;align-items:center;gap:9px}
.card h4 .ic{width:26px;height:26px;border-radius:7px;background:var(--accent-soft);color:var(--accent-ink);
display:grid;place-items:center;font-size:14px;flex:none}
.card p{margin:0;color:var(--ink-2);font-size:14.5px}
.card .files{margin-top:10px;display:flex;flex-wrap:wrap;gap:6px}
.card .files code{font-size:11.5px}
/* flow steps */
.flow{display:flex;flex-direction:column;gap:10px;margin:18px 0;counter-reset:fl}
.flow li{list-style:none;display:flex;gap:14px;align-items:flex-start;background:var(--surface);
border:1px solid var(--border);border-radius:12px;padding:13px 15px}
.flow li::before{counter-increment:fl;content:counter(fl);font-family:var(--font-mono);font-weight:600;
font-size:13px;color:var(--accent-ink);background:var(--accent-soft);border-radius:8px;
width:28px;height:28px;display:grid;place-items:center;flex:none}
.flow b{color:var(--ink)}
.flow small{color:var(--ink-3);display:block;font-size:13px}
.note{border:1px solid var(--border);border-left:3px solid var(--accent);background:var(--surface);
border-radius:0 12px 12px 0;padding:14px 16px;margin:18px 0;font-size:14.5px;color:var(--ink-2)}
.note b{color:var(--ink)}
footer{border-top:1px solid var(--border);padding:34px 0 60px;color:var(--ink-3);font-size:13.5px}
footer .wrap{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;align-items:center}
footer a{color:var(--ink-2)}
@media (max-width:560px){.brand span.full{display:none}.hero{padding:40px 0 24px}}
</style>
</head>
<body>
<header class="top"><div class="wrap">
<span class="brand"><span class="dot"></span>cowork_local<span class="full">&nbsp;· docs</span></span>
<nav class="doc" aria-label="Tài liệu">
<a href="architecture.html" aria-current="page">Cấu trúc</a>
<a href="security.html">Bảo mật</a>
<a href="usage.html">Cách dùng</a>
</nav>
<button class="toggle" id="themeBtn" title="Đổi giao diện sáng/tối" aria-label="Đổi giao diện">◐</button>
</div></header>
<main>
<div class="wrap hero">
<p class="eyebrow">Cowork Local · Tài liệu kỹ thuật</p>
<h1>Cấu trúc hệ thống</h1>
<p class="lead">Trợ lý AI dạng agent chạy <strong>cục bộ trên máy</strong> (desktop, ưu tiên Windows). Người dùng trò chuyện, chạy luồng nhiều bước, thao tác tệp và lên lịch tác vụ — mọi thứ được bọc trong một khung bảo mật nhiều lớp.</p>
<div class="meta">
<span class="tag">PySide6 / Qt6</span>
<span class="tag">Local-first</span>
<span class="tag">Provider-agnostic</span>
<span class="tag">~53K dòng Python</span>
<span class="tag">Windows · macOS · Linux</span>
</div>
</div>
<section class="wrap">
<h2><span class="num">01</span> Tổng quan &amp; nguyên tắc</h2>
<p class="sub">Bốn nguyên tắc định hình toàn bộ kiến trúc.</p>
<div class="grid">
<div class="card"><h4><span class="ic">▤</span>Local-first</h4><p>Cấu hình, lịch sử hội thoại, workspace và nhật ký đều nằm trên máy người dùng. Chỉ lệnh gọi mô hình mới ra ngoài.</p></div>
<div class="card"><h4><span class="ic">⛨</span>Bảo mật nhiều lớp</h4><p>Mọi tool có tác động (chạy lệnh, ghi tệp, tải URL) đi qua chuỗi kiểm soát fail-closed; xem tài liệu <a href="security.html">Bảo mật</a>.</p></div>
<div class="card"><h4><span class="ic">⧉</span>Đa workspace</h4><p>Nhiều project chạy song song, mỗi project nhiều hội thoại Cowork và nhiều luồng Co4E — không cái nào chặn cái nào.</p></div>
<div class="card"><h4><span class="ic">⇄</span>Provider-agnostic</h4><p>Nhiều nhà cung cấp mô hình (OpenAI-compatible…), tự động định tuyến chọn mô hình phù hợp trong số các model được bật.</p></div>
</div>
</section>
<section class="wrap">
<h2><span class="num">02</span> Ngăn xếp công nghệ</h2>
<p class="sub">Những thư viện/thành phần chủ chốt và vai trò của chúng.</p>
<div class="chips">
<span class="chip"><b>PySide6/Qt6</b> · toàn bộ giao diện, đa luồng QThread</span>
<span class="chip"><b>FastAPI + uvicorn</b> · Routing API (chỉ localhost)</span>
<span class="chip"><b>MCP</b> · kết nối công cụ ngoài (Model Context Protocol)</span>
<span class="chip"><b>MSAL</b> · đăng nhập Microsoft 365</span>
<span class="chip"><b>openpyxl / python-pptx</b> · đọc Office</span>
<span class="chip"><b>opendataloader-pdf</b> · trích xuất PDF</span>
<span class="chip"><b>networkx</b> · đồ thị cấu trúc (GraphRAG)</span>
<span class="chip"><b>keyring</b> · lưu bí mật qua OS</span>
<span class="chip"><b>ctypes / Win32</b> · sandbox AppContainer &amp; Job Object</span>
<span class="chip"><b>Pygments</b> · tô màu mã nguồn</span>
</div>
</section>
<section class="wrap">
<h2><span class="num">03</span> Kiến trúc phân lớp</h2>
<p class="sub">Một yêu cầu đi từ giao diện xuống lớp thực thi rồi ra ngoài — mỗi lớp có trách nhiệm rõ ràng.</p>
<div class="stack">
<div class="layer">
<div class="lh"><span class="tier">UI</span><h4>Lớp giao diện — PySide6</h4><small>người dùng thao tác</small></div>
<div class="chips">
<span class="chip">MainWindow</span><span class="chip">WorkspaceTab / WorkspacePane</span>
<span class="chip">CoworkTab (chat)</span><span class="chip">Co4ETab (flow canvas)</span>
<span class="chip">FolderTab</span><span class="chip">ScheduleTaskTab</span>
<span class="chip">MonitoringTab → Security</span><span class="chip">SettingsDialog</span>
</div>
</div>
<div class="layer">
<div class="lh"><span class="tier">Agent</span><h4>Lớp agent / lõi thực thi</h4><small>điều phối lượt chạy</small></div>
<div class="chips">
<span class="chip"><b>chat_agent</b> · run_cowork</span>
<span class="chip"><b>code_agent</b> · run_code</span>
<span class="chip"><b>co4e_runner</b> · run_workflow</span>
<span class="chip"><b>task_executors</b> · tác vụ theo lịch</span>
<span class="chip"><b>model_routing</b> · assess &amp; chọn model</span>
<span class="chip"><b>agent_security</b> · guardrail</span>
</div>
</div>
<div class="layer">
<div class="lh"><span class="tier">Tool</span><h4>Lớp công cụ &amp; sandbox</h4><small>ranh giới tin cậy</small></div>
<div class="chips">
<span class="chip"><b>ToolContext</b> · confine đường dẫn + scope</span>
<span class="chip">execute_tool</span>
<span class="chip">read/write/edit/list_dir</span>
<span class="chip">run_command · install_package</span>
<span class="chip">fetch_url · jira</span>
<span class="chip"><b>SandboxManager</b> + backends</span>
</div>
</div>
<div class="layer">
<div class="lh"><span class="tier">Provider</span><h4>Lớp nhà cung cấp mô hình</h4><small>gọi ra mạng an toàn</small></div>
<div class="chips">
<span class="chip">providers/* (OpenAI-compatible…)</span>
<span class="chip"><b>tls_trust</b> · phục hồi TLS gateway</span>
<span class="chip">usage_tracker · đo token/chi phí</span>
</div>
</div>
<div class="layer">
<div class="lh"><span class="tier">Ngoài</span><h4>Dịch vụ bên ngoài</h4><small>không tin cậy mặc định</small></div>
<div class="chips">
<span class="chip">LLM APIs</span><span class="chip">MCP servers</span>
<span class="chip">Microsoft 365</span><span class="chip">Jira</span><span class="chip">Web (fetch_url)</span>
</div>
</div>
</div>
</section>
<section class="wrap">
<h2><span class="num">04</span> Các subsystem chính</h2>
<p class="sub">Mỗi khối là một tính năng lớn người dùng thấy được, ánh xạ tới module tương ứng.</p>
<div class="grid">
<div class="card"><h4><span class="ic">▦</span>Workspaces &amp; Projects</h4><p>Nhiều project song song, mỗi cái một pane riêng với sandbox bật/tắt để tiết kiệm tài nguyên.</p><div class="files"><code>workspace_tab.py</code><code>workspace_pane.py</code></div></div>
<div class="card"><h4><span class="ic">💬</span>Cowork · đa hội thoại</h4><p>Nhiều hội thoại trong một project; lượt chạy nền giữ đúng hội thoại gốc kể cả khi bạn chuyển tab.</p><div class="files"><code>chat_panel.py</code><code>cowork_tab.py</code></div></div>
<div class="card"><h4><span class="ic">◈</span>Co4E flows</h4><p>Canvas nhiều bước, agent tùy biến, chế độ auto/plan/manual, chạy song song &amp; theo dõi ở Flow Status.</p><div class="files"><code>co4e_tab.py</code><code>co4e_runner.py</code></div></div>
<div class="card"><h4><span class="ic">⇉</span>Model routing</h4><p>Tự đánh giá &amp; chọn mô hình tốt nhất trong số model được bật theo policy (chất lượng/chi phí/độ trễ).</p><div class="files"><code>core/routing/*</code></div></div>
<div class="card"><h4><span class="ic">⛨</span>Sandbox</h4><p>Chọn backend theo mức rủi ro: best-effort → AppContainer → Windows Sandbox VM.</p><div class="files"><code>sandbox_manager.py</code><code>appcontainer_sandbox.py</code></div></div>
<div class="card"><h4><span class="ic">⏱</span>Scheduler</h4><p>Tác vụ theo lịch (Cowork/Code/Flow), phụ thuộc chuỗi, opt-in chạy lệnh.</p><div class="files"><code>task_scheduler.py</code><code>task_executors.py</code></div></div>
<div class="card"><h4><span class="ic">📊</span>Monitoring</h4><p>Tổng quan chi phí, nhật ký sự kiện/bảo mật, quản trị Tool/Agent, trang Security.</p><div class="files"><code>monitoring_tab.py</code></div></div>
<div class="card"><h4><span class="ic">🗄</span>Lưu trữ</h4><p>Cấu hình + lịch sử theo project + workspace + audit log, tất cả trên máy.</p><div class="files"><code>config.py</code><code>core/history.py</code></div></div>
</div>
</section>
<section class="wrap">
<h2><span class="num">05</span> Mô hình đồng thời</h2>
<p class="sub">Vì sao nhiều lượt chạy song song không giẫm chân nhau.</p>
<h3>Cô lập theo lượt (per-turn)</h3>
<p>Mỗi lượt chat chạy trong một <code>AgentWorker</code> (QThread) riêng. Tại thời điểm bắt đầu, lượt chụp lại bối cảnh <span class="k">home_*</span> (id hội thoại, thư mục làm việc, project) — nên dù người dùng chuyển sang hội thoại khác, lượt nền vẫn ghi kết quả về <strong>đúng hội thoại gốc</strong> và quét đúng thư mục của nó.</p>
<h3>Quản lý luồng Co4E dùng chung</h3>
<p>Một <code>Co4ERunManager</code> duy nhất phục vụ mọi pane, mỗi run gắn <span class="k">project_id</span> để lọc. Khi dừng một worker bị treo, nó được "park" giữ tham chiếu (không GC luồng đang chạy → tránh crash <em>QThread destroyed while running</em>).</p>
<div class="note"><b>Cách ly dừng (Stop):</b> nút Stop chỉ tác động lên các worker của <em>chính</em> hội thoại đó và xóa hàng đợi của riêng nó — dừng ở hội thoại này không ảnh hưởng hội thoại khác.</div>
</section>
<section class="wrap">
<h2><span class="num">06</span> Luồng dữ liệu một lượt chat</h2>
<p class="sub">Từ tin nhắn người dùng đến kết quả — mỗi bước là một điểm kiểm soát.</p>
<ol class="flow">
<li><div><b>Tin nhắn + đính kèm</b><small>Người dùng gửi; tệp/thư mục workspace được nạp qua <code>_augment</code>.</small></div></li>
<li><div><b>Bọc nội dung không tin cậy</b><small>Nội dung tệp/web/tool được rào trong khối <span class="k">UNTRUSTED DATA</span> — model coi là dữ liệu, không phải mệnh lệnh.</small></div></li>
<li><div><b>Định tuyến mô hình</b><small>Auto Routing có thể chọn mô hình phù hợp trong số model được bật.</small></div></li>
<li><div><b>Gọi provider</b><small><code>provider.chat()</code> qua <code>tls_trust</code>; usage_tracker ghi token/chi phí theo hội thoại gốc.</small></div></li>
<li><div><b>Model gọi tool</b><small>Mỗi tool qua: kiểm scope ở executor → human-gate (nếu bật) → classifier → sandbox.</small></div></li>
<li><div><b>Kết quả &amp; lưu</b><small>Văn bản/diff hiện realtime; hội thoại lưu vào <code>.cowork_history</code> của project.</small></div></li>
</ol>
</section>
<section class="wrap">
<h2><span class="num">07</span> Lưu trữ trên máy</h2>
<p class="sub">Dữ liệu nằm ở đâu.</p>
<div class="chips">
<span class="chip"><b>~/.cowork_local/config.json</b> · cấu hình (perm 0o600)</span>
<span class="chip"><b>&lt;project&gt;/.cowork_history</b> · hội thoại theo project</span>
<span class="chip"><b>workspaces/</b> · thư mục làm việc mỗi project</span>
<span class="chip"><b>audit log</b> · mọi tool-call &amp; quyết định quyền (lưu hash lệnh)</span>
<span class="chip"><b>trusted_certs/</b> · cert gateway đã pin</span>
<span class="chip"><b>appcontainer_grants.json</b> · cache cấp quyền sandbox</span>
</div>
<div class="note">Vị trí lịch sử có thể trỏ vào thư mục đồng bộ OneDrive — tiện chia sẻ, nhưng lưu ý dữ liệu tệp đã nạp sẽ được sao lên cloud dạng plaintext. Xem khuyến nghị ở tài liệu <a href="security.html">Bảo mật</a>.</div>
</section>
</main>
<footer><div class="wrap">
<span>Cowork Local — tài liệu nội bộ · Cấu trúc hệ thống</span>
<span><a href="security.html">Bảo mật →</a> &nbsp; <a href="usage.html">Cách dùng →</a></span>
</div></footer>
<script>
(function(){
var root=document.documentElement, key="cowork_docs_theme";
var saved=null; try{saved=localStorage.getItem(key)}catch(e){}
if(saved==="dark"||saved==="light") root.setAttribute("data-theme",saved);
else root.removeAttribute("data-theme");
document.getElementById("themeBtn").addEventListener("click",function(){
var cur=root.getAttribute("data-theme");
if(!cur){ // currently following OS → flip to opposite of OS
cur=window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";
}
var next=cur==="dark"?"light":"dark";
root.setAttribute("data-theme",next);
try{localStorage.setItem(key,next)}catch(e){}
});
})();
</script>
</body>
</html>
+473
View File
@@ -0,0 +1,473 @@
# 📋 COWORK-LOCAL BamBOO — Danh Sách Chức Năng Chi Tiết Theo Navigation Bar
---
## 🔹 1. 📊 DASHBOARD (Bảng Điều Khiển)
### 1.1 Token Usage & Cost — Thống Kê Token & Chi Phí
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 1.1.1 | `_refresh_cards()` | Làm mới các thẻ thống kê (Total, Input, Output, Cache tokens + Cost) |
| 1.1.2 | `_refresh_chart()` | Vẽ biểu đồ spline theo chu kỳ (week/month/year) và metric (cost/tokens) |
| 1.1.3 | `_chart_prev()` / `_chart_next()` | Chuyển đến chu kỳ trước/sau trên biểu đồ |
| 1.1.4 | `_on_gran_changed()` | Thay đổi đơn vị thời gian (week/month/year) |
| 1.1.5 | `_refresh_budget()` | Cập nhật ngân sách (budget card — còn lại / đã dùng / cảnh báo >85%) |
| 1.1.6 | `_apply_budget()` | Lưu giá trị budget mới |
| 1.1.7 | `_refresh_habits()` | Hiển thị thói quen sử dụng (task tốn nhiều token nhất, trung bình/prompt, ngày/giờ bận nhất) |
| 1.1.8 | `_ai_analyze()` | ✨ AI phân tích thói quen dùng token và gợi ý tiết kiệm |
| 1.1.9 | `_apply_saving_strategy()` | Áp dụng chiến lược tiết kiệm AI (tự nén context, nén sớm hơn) |
| 1.1.10 | Currency Picker | Chọn đơn vị tiền tệ hiển thị (USD, VND, JPY, …) |
---
## 🔹 2. 📅 SCHEDULE TASK (Lên Lịch Nhiệm Vụ)
### 2.1 Kanban Board
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 2.1.1 | `_build_kanban()` | Xây dựng board Kanban với 7 cột: Backlog, Scheduled, Running, Waiting Input, Done, Failed, Paused |
| 2.1.2 | `_render_kanban()` | Render các thẻ task vào từng cột |
| 2.1.3 | `_on_task_dropped(task_id, new_status)` | Kéo thả task giữa các cột (thay đổi status) |
| 2.1.4 | `_on_card_double_click()` | Mở Task Editor khi double-click |
| 2.1.5 | `_on_card_right_click()` | Menu ngữ cảnh: Run now, Edit, Duplicate, Pause, Delete, View logs, Create-next-from-output |
| 2.1.6 | `_bulk_delete_menu()` | Xóa hàng loạt (chọn nhiều thẻ → right-click → Delete N selected) |
| 2.1.7 | `_run_now(task_id)` | Chạy task ngay lập tức |
| 2.1.8 | `_duplicate_task(task_id)` | Sao chép task |
| 2.1.9 | `_pause_task(task_id)` | Tạm dừng task |
| 2.1.10 | `_delete_task(task_id)` | Xóa task |
| 2.1.11 | `_view_logs(task_id)` | Xem log của task |
| 2.1.12 | `_search_tasks()` | Tìm kiếm task theo tên |
| 2.1.13 | `_filter_by_type()` | Lọc task theo loại (cowork/co4e/code/…) |
### 2.2 Calendar View
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 2.2.1 | `_build_calendar()` | Xây dựng chế độ xem lịch |
| 2.2.2 | `_shift(direction)` | Chuyển tháng/tuần trước/sau |
| 2.2.3 | `add_task_on_date(date)` | Thêm task vào ngày cụ thể |
| 2.2.4 | `edit_task(task_id)` | Sửa task từ lịch |
### 2.3 Add / AI Create Task
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 2.3.1 | `_open_add_dialog()` | Mở dialog thêm task thủ công |
| 2.3.2 | `_ai_create_task()` | Mở dialog AI tạo task tự động |
| 2.3.3 | `_ai_pick_files()` | Chọn file đính kèm cho AI planner |
| 2.3.4 | `_generate()` | AI tạo kế hoạch tasks từ mô tả |
| 2.3.5 | `_on_planned(result)` | Hiển thị preview các task AI đề xuất |
| 2.3.6 | `_confirm()` | Xác nhận và tạo các task từ AI plan |
### 2.4 AI Import Tasks
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 2.4.1 | `_ai_import()` | AI nhập task từ file/link |
| 2.4.2 | `_ai_pick_import_files()` | Chọn file để import |
| 2.4.3 | `_generate_import()` | AI phân tích file và tạo tasks |
| 2.4.4 | `_on_import_planned()` | Hiển thị preview import |
---
## 🔹 3. 🏠 WORKSPACE (Không Gian Làm Việc)
### 3.1 Projects — Quản Lý Dự Án (Tab 0)
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.1.1 | `_create()` | Tạo dự án mới |
| 3.1.2 | `_delete()` | Xóa dự án (có xác nhận) |
| 3.1.3 | `_save()` | Lưu thông tin dự án (name, description, instructions, folder) |
| 3.1.4 | `_pick_folder()` | Chọn workspace folder cho dự án |
| 3.1.5 | `_open_workspace()` | Mở folder workspace trong file explorer |
| 3.1.6 | `_select_project_row(project_id)` | Chọn dự án trong danh sách |
| 3.1.7 | `_refresh_sandbox_toggle()` | Bật/tắt sandbox cho dự án |
| 3.1.8 | `refresh()` | Làm mới danh sách dự án |
### 3.2 Workspace Pane — Mỗi Dự Án Mở (Tab 1..N)
#### 3.2.1 🤖 COWORK — Chat Với AI Agent
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.1.1 | `new_session()` | Tạo phiên chat mới |
| 3.2.1.2 | `send_message()` | Gửi tin nhắn đến AI agent |
| 3.2.1.3 | `_build_job()` | Xây dựng job cho AgentWorker (gọi `run_cowork`) |
| 3.2.1.4 | `_cleanup_turn(ctx, ok)` | Dọn dẹp sau khi turn kết thúc (promote files, xóa sandbox) |
| 3.2.1.5 | `_promote_turn_outputs()` | Di chuyển file đầu ra từ sandbox lên session output |
| 3.2.1.6 | `_refresh_outputs_from_disk()` | Làm mới danh sách output files |
| 3.2.1.7 | `_pick_output_folder()` | Chọn thư mục output |
| 3.2.1.8 | `_open_skills_manager()` | Mở Skill Manager |
| 3.2.1.9 | `refresh_header()` | Làm mới header (project name, model info) |
| 3.2.1.10 | `refresh_agents()` | Làm mới danh sách agents trong combo |
| 3.2.1.11 | `admin_agent_prompt()` | Lấy prompt từ agent preset đã chọn |
| 3.2.1.12 | `build_provider()` | Xây dựng provider từ cấu hình agent/model |
| 3.2.1.13 | `workspace_dir()` | Trả về workspace directory hiện tại |
| 3.2.1.14 | `_start_watching(dir)` | Giám sát folder output (file watcher) |
| 3.2.1.15 | `_on_file_changed()` | Xử lý khi file output thay đổi |
**ChatPanel (Class cha của CoworkTab):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.1.16 | `_submit_message()` | Gửi tin nhắn (kiểm tra queue, parallel limit) |
| 3.2.1.17 | `_on_turn_started()` | Khi turn bắt đầu (show thinking indicator) |
| 3.2.1.18 | `_on_turn_finished()` | Khi turn kết thúc (update UI, queue next) |
| 3.2.1.19 | `_on_event(ev)` | Xử lý streaming events (text delta, tool calls, plan) |
| 3.2.1.20 | `_compress_messages()` | Nén tin nhắn cũ để giảm token |
| 3.2.1.21 | `_on_agent_changed()` | Khi thay đổi agent trong combo |
| 3.2.1.22 | `_apply_routing()` | Áp dụng model routing (Auto/Manual/Off) |
| 3.2.1.23 | `_note_agent_switch()` | Ghi chú khi agent thay đổi giữa các turn |
| 3.2.1.24 | `_ensure_conversation()` | Đảm bảo conversation tab tồn tại |
| 3.2.1.25 | `load_conversation()` | Load hội thoại từ disk |
| 3.2.1.26 | `running_session_ids()` | Trả về danh sách session đang chạy |
| 3.2.1.27 | `active_workers()` | Trả về danh sách worker đang hoạt động |
| 3.2.1.28 | `_save_conversation()` | Tự động lưu hội thoại |
**Composer (Composer input box):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.1.29 | `send()` | Gửi tin nhắn |
| 3.2.1.30 | `attach_files()` | Đính kèm file |
| 3.2.1.31 | `attach_links()` | Đính kèm link URL |
| 3.2.1.32 | `has_any_queue()` | Kiểm tra queue có tin nhắn chờ |
| 3.2.1.33 | `_parse_directives()` | Phân tích directives inline (`/agent:name`, `/skill:name`) |
| 3.2.1.34 | `_show_autocomplete()` | Hiển thị gợi ý tự động |
#### 3.2.2 ⚡ CO4E — Node-Graph Workflow Studio
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.1 | `_build_sidebar()` | Xây dựng sidebar (Workflows / Agents / Skills tabs) |
| 3.2.2.2 | `_build_canvas()` | Xây dựng canvas node-graph |
| 3.2.2.3 | `_build_config_panel()` | Xây dựng config panel bên phải |
| 3.2.2.4 | `_toggle_config()` | Thu/mở config panel |
| 3.2.2.5 | `_build_canvas_overlay()` | Zoom +/− và Fit buttons trên canvas |
**Workflows (Sidebar):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.6 | `_refresh_flows_list()` | Làm mới danh sách flows |
| 3.2.2.7 | `_create_flow()` | Tạo flow mới |
| 3.2.2.8 | `_delete_flow()` | Xóa flow |
| 3.2.2.9 | `_duplicate_flow()` | Sao chép flow |
| 3.2.2.10 | `_import_flow()` | Import flow từ file |
| 3.2.2.11 | `_export_flow()` | Export flow ra file |
| 3.2.2.12 | `_run_flow()` | Chạy flow (foreground/background) |
| 3.2.2.13 | `_stop_flow()` | Dừng flow đang chạy |
| 3.2.2.14 | `_open_flow()` | Mở flow trên canvas |
**Agents (Sidebar):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.15 | `_refresh_agents_list()` | Làm mới danh sách agents |
| 3.2.2.16 | `_create_agent()` | Tạo agent mới (dialog) |
| 3.2.2.17 | `_edit_agent()` | Sửa agent |
| 3.2.2.18 | `_delete_agent()` | Xóa agent |
| 3.2.2.19 | `_toggle_agent_enabled()` | Bật/tắt agent |
**Skills (Sidebar):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.20 | `_refresh_skills_list()` | Làm mới danh sách skills |
**Canvas (Node-Graph):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.21 | `zoom_in()` / `zoom_out()` | Zoom canvas |
| 3.2.2.22 | `fit_view()` | Auto-fit canvas |
| 3.2.2.23 | `_add_node()` | Thêm node lên canvas |
| 3.2.2.24 | `_delete_node()` | Xóa node |
| 3.2.2.25 | `_connect_nodes()` | Kết nối 2 nodes |
| 3.2.2.26 | `_drag_node()` | Kéo thả node |
| 3.2.2.27 | `_select_node()` | Chọn node (→ config panel) |
| 3.2.2.28 | `_activate_node()` | Double-click node |
**Run Modes:**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.29 | `_set_run_mode("auto")` | Auto mode: agent tự plan rồi execute |
| 3.2.2.30 | `_set_run_mode("plan")` | Plan mode: chỉ tạo kế hoạch |
| 3.2.2.31 | `_set_run_mode("manual")` | Manual mode: từng bước, bấm "Next step" |
| 3.2.2.32 | `_run_step()` | Chạy bước tiếp theo (manual mode) |
| 3.2.2.33 | `_on_step_finished()` | Xử lý khi bước hoàn thành |
| 3.2.2.34 | `_render_plan()` | Render plan checklist |
**Chat/Output (Bottom):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.2.35 | `_get_flow_chat(flow_id)` | Lấy ChatView cho flow (tạo mới nếu chưa có) |
| 3.2.2.36 | `_on_chat_event()` | Xử lý event từ chat |
#### 3.2.3 📁 FOLDER — File Explorer
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.3.1 | `set_root(path)` | Đặt thư mục gốc |
| 3.2.3.2 | `_build_tree_view()` | Xây dựng cây thư mục (QFileSystemModel) |
| 3.2.3.3 | `_open_file(path)` | Mở file được chọn |
| 3.2.3.4 | `_view_source()` | Xem source code (syntax highlighting) |
| 3.2.3.5 | `_view_html_preview()` | Preview HTML (WebEngine/rich text) |
| 3.2.3.6 | `_view_office_doc()` | Xem Office doc (docx/pdf/xlsx/…) |
| 3.2.3.7 | `_view_image()` | Hiển thị ảnh inline |
| 3.2.3.8 | `_edit_file()` | Chỉnh sửa file (code editor) |
| 3.2.3.9 | `_save_file()` | Lưu file |
| 3.2.3.10 | `_preview_toggle()` | Chuyển đổi Preview ⇄ Edit |
| 3.2.3.11 | `_create_new_file()` | Tạo file mới |
| 3.2.3.12 | `_create_new_folder()` | Tạo folder mới |
| 3.2.3.13 | `_rename_item()` | Đổi tên file/folder |
| 3.2.3.14 | `_delete_item()` | Xóa file/folder |
| 3.2.3.15 | `_copy_item()` | Sao chép file/folder |
| 3.2.3.16 | `_paste_item()` | Dán file/folder |
| 3.2.3.17 | `refresh_ai_models()` | Làm mới danh sách AI models cho AI Edit |
**AI Edit (Chỉnh Sửa File Bằng AI):**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.3.18 | `_ai_send()` | Gửi yêu cầu AI edit |
| 3.2.3.19 | `_ai_apply()` | Áp dụng thay đổi AI |
| 3.2.3.20 | `_ai_discard()` | Hủy thay đổi AI |
| 3.2.3.21 | `_reset_ai_conversation()` | Xóa hội thoại AI edit |
| 3.2.3.22 | `_ai_apply_routing()` | Áp dụng routing cho AI edit |
#### 3.2.4 🧠 GRAPH RAG — Knowledge Graph
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.4.1 | `_build_graph()` | Xây dựng knowledge graph từ code/documents |
| 3.2.4.2 | `_render_d3_graph()` | Render graph bằng D3.js (WebEngine) |
| 3.2.4.3 | `_render_native_graph()` | Render graph bằng QGraphicsView (fallback) |
| 3.2.4.4 | `_auto_rotate()` | Tự xoay graph khi idle |
| 3.2.4.5 | `_on_node_click()` | Xử lý click node (mở folder) |
| 3.2.4.6 | `_open_node_path()` | Mở folder chứa node |
| 3.2.4.7 | `_refresh_graph()` | Tự cập nhật graph khi có output mới |
| 3.2.4.8 | `_search_graph()` | Tìm kiếm trong graph |
| 3.2.4.9 | `_filter_by_kind()` | Lọc node theo loại |
| 3.2.4.10 | `_zoom_graph()` | Zoom graph |
**Graph-RAG Q&A:**
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 3.2.4.11 | `_ask_question()` | Hỏi AI về graph |
| 3.2.4.12 | `_on_ask_event()` | Xử lý streaming answer |
| 3.2.4.13 | `_on_ask_done()` | Khi AI trả lời xong |
| 3.2.4.14 | `_candidate_file_paths()` | Lấy danh sách file để extract |
| 3.2.4.15 | `_extract_tmp_dir()` | Tạo thư mục tạm cho extraction |
| 3.2.4.16 | `_clear_extracts()` | Xóa dữ liệu extract tạm |
---
## 🔹 4. 📊 MONITORING (Giám Sát)
### 4.1 Overview — Tổng Quan
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.1.1 | `_refresh_overview()` | Làm mới tất cả cards overview |
| 4.1.2 | `_refresh_usage_cards()` | Token Usage & Cost cards (Total, Input, Output, Cache) |
| 4.1.3 | `_refresh_resource_usage()` | Resource usage (CPU, RAM, Disk) |
| 4.1.4 | `_refresh_recent_activity()` | Hoạt động gần đây |
| 4.1.5 | `_refresh_sandbox_details()` | Chi tiết sandbox (PID, uptime, limits) |
| 4.1.6 | `_refresh_permissions()` | Hiển thị permissions hiện tại |
| 4.1.7 | `_refresh_audit_log()` | Audit log gần đây |
| 4.1.8 | `_refresh_budget()` | Budget card (còn lại / đã dùng) |
| 4.1.9 | `_apply_budget()` | Lưu budget mới |
### 4.2 Security Events — Sự Kiện Bảo Mật
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.2.1 | `_refresh_security_events()` | Làm mới bảng security events (audit log `kind="security_block"`) |
| 4.2.2 | `_filter_security_events()` | Lọc sự kiện bảo mật |
| 4.2.3 | `_sort_events()` | Sắp xếp bảng events |
### 4.3 MCP Call History — Lịch Sử Gọi MCP
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.3.1 | `_refresh_mcp_calls()` | Làm mới bảng MCP calls (audit log `kind="mcp_call"`) |
| 4.3.2 | `_filter_mcp_calls()` | Lọc MCP calls |
### 4.4 Action Logs — Nhật Ký Hành Động
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.4.1 | `_refresh_action_logs()` | Làm mới bảng action logs (toàn bộ audit log) |
| 4.4.2 | `_filter_action_logs()` | Lọc action logs |
| 4.4.3 | `_sort_action_logs()` | Sắp xếp action logs |
### 4.5 Agent Status — Trạng Thái Agent
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.5.1 | `_refresh_agent_status()` | Làm mới trạng thái các agent (Cowork, Co4E, Schedule, GraphRAG) |
### 4.6 Security Settings — Cài Đặt Bảo Mật
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 4.6.1 | `_toggle_sandbox()` | Bật/tắt sandbox |
| 4.6.2 | `_toggle_network_block()` | Chặn kết nối mạng |
| 4.6.3 | `_set_resource_limits()` | Đặt giới hạn tài nguyên (CPU/RAM/Disk) |
| 4.6.4 | `_toggle_command_confirm()` | Xác nhận trước khi chạy lệnh |
| 4.6.5 | `_manage_permissions()` | Quản lý quyền truy cập |
---
## 🔹 5. ⚙️ SETTINGS (Cài Đặt)
### 5.1 AI Provider — Nhà Cung Cấp AI
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 5.1.1 | `_on_provider_changed()` | Khi thay đổi provider |
| 5.1.2 | `_load_models(provider)` | Load danh sách models của provider |
| 5.1.3 | `_test_connection(provider)` | Kiểm tra kết nối provider |
| 5.1.4 | `_stash_provider_fields()` | Lưu tạm các trường cấu hình provider |
| 5.1.5 | `_apply_provider_fields()` | Áp dụng các trường cấu hình provider |
| 5.1.6 | Model List Widget | Hiển thị danh sách models (enable/disable, chọn default) |
### 5.2 Connectors (MCP) — Kết Nối
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 5.2.1 | `_add_mcp_server()` | Thêm MCP server mới |
| 5.2.2 | `_edit_mcp_server()` | Sửa MCP server |
| 5.2.3 | `_delete_mcp_server()` | Xóa MCP server |
| 5.2.4 | `_test_mcp_connection()` | Kiểm tra kết nối MCP |
| 5.2.5 | MS365 Connector | Kết nối Microsoft 365 (tự động khi đăng nhập) |
| 5.2.6 | CAD/CAE Connectors | Kết nối CAD/CAE tools |
### 5.3 Parameters — Tham Số
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 5.3.1 | `attach_tokens` | Giới hạn token cho attachments |
| 5.3.2 | `attach_files` | Giới hạn số file attachments |
| 5.3.3 | `struct_nodes` | Giới hạn nodes cho GraphRAG |
| 5.3.4 | `struct_edges` | Giới hạn edges cho GraphRAG |
### 5.4 Model Routing — Định Tuyến Model
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 5.4.1 | `routing_mode` | Chế độ routing (Off/Auto/Manual) |
| 5.4.2 | `routing_policy` | Chính sách routing |
| 5.4.3 | `routing_min_gain` | Threshold tối thiểu để chuyển model |
| 5.4.4 | `routing_timeout` | Timeout xác nhận routing |
| 5.4.5 | `routing_interval` | Khoảng thời gian đánh giá lại |
| 5.4.6 | `routing_concurrency` | Số lượng request đồng thời per provider |
| 5.4.7 | `routing_judge` | Model dùng để đánh giá routing |
### 5.5 General — Chung
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 5.5.1 | Language Picker | Chọn ngôn ngữ (EN/VI/JP) |
| 5.5.2 | `tray_chk` | Minimize to tray thay vì đóng |
| 5.5.3 | `notify_chk` | Thông báo khi task hoàn thành |
| 5.5.4 | `_save()` | Lưu tất cả cài đặt |
---
## 🔹 6. 📜 HISTORY SIDEBAR (Thanh Lịch Sử Bên Trái)
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 6.0.1 | `refresh()` | Làm mới danh sách hội thoại |
| 6.0.2 | `set_view_state(session_id, running_ids)` | Đánh dấu hội thoại hiện tại + đang chạy |
| 6.0.3 | `set_project_filter(project_id)` | Lọc theo dự án |
| 6.0.4 | `_open_chat()` | Mở hội thoại khi click |
| 6.0.5 | `_context_menu()` | Menu chuột phải (Pin/Unpin, Rename, Delete) |
| 6.0.6 | `_bulk_delete_menu()` | Menu xóa hàng loạt |
| 6.0.7 | `_confirm_and_delete_selected()` | Xác nhận và xóa các hội thoại đã chọn |
| 6.0.8 | `new_chat(kind)` | Tạo hội thoại mới |
| 6.0.9 | `collapse_requested()` | Thu nhỏ sidebar |
| 6.0.10 | `expand_requested()` | Mở rộng sidebar |
---
## 🔹 7. 🧩 CÁC CHỨC NĂNG TOÀN CẦU (Global)
### 7.1 MainWindow (app.py)
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 7.1.1 | `_build_topbar()` | Xây dựng thanh trên cùng (User name, Settings, Language) |
| 7.1.2 | `_build_nav_rail()` | Xây dựng thanh điều hướng bên trái |
| 7.1.3 | `_toggle_nav()` | Thu/mở nav rail (icon-only ↔ full) |
| 7.1.4 | `_apply_nav_labels()` | Áp dụng labels cho nav items |
| 7.1.5 | `_ensure_page(row)` | Xây dựng page lười (lazy loading) |
| 7.1.6 | `_refresh_history()` | Làm mới history của tất cả panes |
| 7.1.7 | `_on_scheduled_task_done()` | Thông báo khi scheduled task hoàn thành |
| 7.1.8 | `_notify_task()` | Thông báo khi task hoàn thành |
| 7.1.9 | `_on_projects_changed()` | Khi danh sách dự án thay đổi |
| 7.1.10 | `_on_pane_turn_finished()` | Khi turn trong pane hoàn thành |
| 7.1.11 | `_open_settings()` | Mở dialog cài đặt |
| 7.1.12 | `_fit_to_screen()` | Tự động fit cửa sổ theo màn hình |
| 7.1.13 | Toast notifications | Hiển thị thông báo toast |
| 7.1.14 | System Tray | Minimize to tray, tray notifications |
### 7.2 Skills Manager
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 7.2.1 | `_open_skills_manager()` | Mở Skill Manager |
| 7.2.2 | `seed_library_skills()` | Gieo skills mặc định |
| 7.2.3 | `prune_seeded_builtins()` | Dọn dẹp skills built-in |
### 7.3 Welcome Dialog
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 7.3.1 | `maybe_show_welcome()` | Hiển thị dialog chào mừng lần đầu |
### 7.4 i18n (Đa Ngôn Ngữ)
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 7.4.1 | `tr(key)` | Dịch chuỗi theo ngôn ngữ hiện tại |
| 7.4.2 | `set_language(lang)` | Đặt ngôn ngữ |
| 7.4.3 | `get_language()` | Lấy ngôn ngữ hiện tại |
| 7.4.4 | `on_language_changed(callback)` | Đăng ký callback khi ngôn ngữ thay đổi |
### 7.5 Task Scheduler (Nền)
| # | Tên Hàm / Chức Năng | Mô Tả |
|---|---------------------|--------|
| 7.5.1 | `task_finished` signal | Khi scheduled task hoàn thành |
| 7.5.2 | `history_ready` signal | Khi session của task sẵn sàng |
| 7.5.3 | `running_session_ids()` | Lấy danh sách session đang chạy |
---
## 📌 TỔNG KẾT
| Navigation Item | Số Hàm/Chức Năng |
|----------------|:-:|
| 📊 Dashboard | ~10 |
| 📅 Schedule Task | ~25 |
| 🏠 Workspace → Projects | ~8 |
| 🏠 Workspace → Cowork | ~34 |
| 🏠 Workspace → Co4E | ~36 |
| 🏠 Workspace → Folder | ~22 |
| 🏠 Workspace → Graph RAG | ~16 |
| 📊 Monitoring | ~20 |
| ⚙️ Settings | ~25 |
| 📜 History Sidebar | ~10 |
| 🌐 Global Functions | ~15 |
| **TỔNG CỘNG** | **~221** |
> **Lưu ý:** Đây là danh sách các hàm/chức năng ở cấp UI và business logic chính. Các hàm core (providers, MCP, worker, security…) nằm ở tầng dưới và được gọi bởi các hàm UI ở trên.
+540
View File
@@ -0,0 +1,540 @@
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Tìm hiểu RAG — Hỏi &amp; Đáp</title>
<style>
:root{
--navy:#1B3C87; --blue:#0A4EA3; --acc:#1565C0; --acc2:#4A90D9;
--bg:#fff; --sf:#F7F9FC; --card:#fff; --bd:#E3E8EF; --bds:#CBD5E1;
--tx:#2B3542; --mut:#5A6675; --fnt:#8A94A3;
--ok:#1B7A3D; --okbg:#E8F5EC; --warn:#B26A00; --warnbg:#FDF3E3;
--bad:#C0392B; --badbg:#FCEDEC; --r:10px;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--tx);
font:15px/1.6 "Segoe UI Variable Text","Segoe UI",system-ui,sans-serif}
.bar{background:var(--navy);color:#fff;padding:12px 28px;font-weight:700;font-size:16px;
display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:9}
.bar .sub{font-weight:400;opacity:.85;font-size:13px}
.wrap{max-width:1060px;margin:0 auto;padding:28px 28px 80px}
h1{color:var(--blue);font-size:30px;margin:14px 0 6px;letter-spacing:-.02em}
h2{color:var(--blue);font-size:20px;margin:40px 0 4px;padding-top:18px;
border-top:2px solid var(--bd)}
.lead{color:var(--mut);margin:0 0 8px}
.qa{border:1px solid var(--bd);border-radius:var(--r);margin:14px 0;background:var(--card);
box-shadow:0 1px 2px rgba(16,32,64,.04)}
.q{padding:13px 18px;font-weight:700;color:var(--blue);font-size:15.5px;
display:flex;gap:10px;align-items:flex-start}
.q .n{background:var(--acc);color:#fff;border-radius:5px;min-width:26px;height:22px;
display:inline-flex;align-items:center;justify-content:center;font-size:12px;flex:none}
.a{padding:0 18px 15px 54px;color:var(--tx)}
.a p{margin:0 0 8px}
.a ul{margin:6px 0;padding-left:20px}.a li{margin:3px 0}
b{color:var(--blue)}
code{font:13px "Cascadia Code",Consolas,monospace;background:var(--sf);
border:1px solid var(--bd);border-radius:4px;padding:1px 5px;color:#0F3D6E}
.note{border-left:4px solid var(--acc);background:#EAF2FC;border-radius:6px;
padding:10px 14px;margin:10px 0}
.note.ok{border-left-color:var(--ok);background:var(--okbg)}
.note.warn{border-left-color:var(--warn);background:var(--warnbg)}
.note.bad{border-left-color:var(--bad);background:var(--badbg)}
figure{margin:12px 0;padding:14px;background:var(--sf);border:1px solid var(--bd);
border-radius:var(--r)}
figure svg{display:block;width:100%;height:auto}
figcaption{color:var(--fnt);font-size:12.5px;margin-top:8px;text-align:center}
table{width:100%;border-collapse:collapse;margin:10px 0;font-size:14px}
th,td{text-align:left;padding:7px 11px;border-bottom:1px solid var(--bd);vertical-align:top}
th{color:var(--mut);font-size:12px;text-transform:uppercase;letter-spacing:.05em}
.toc{background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);padding:14px 20px}
.toc ol{margin:6px 0;padding-left:20px;columns:2;column-gap:32px;font-size:14px}
.toc a{color:var(--tx);text-decoration:none}.toc a:hover{color:var(--acc)}
@media(max-width:820px){.toc ol{columns:1}.a{padding-left:18px}}
</style>
</head>
<body>
<div class="bar"><span>Tìm hiểu RAG — Hỏi &amp; Đáp</span>
<span class="sub">Chuẩn bị cho phần Q&amp;A sau buổi trình bày</span></div>
<div class="wrap">
<h1>Những câu hay được hỏi nhất</h1>
<p class="lead">20 câu, xếp từ dễ tới khó. Năm câu cuối là về chính dự án Cowork-Local —
nhóm câu này gần như chắc chắn sẽ có người hỏi.</p>
<div class="toc"><b>Nội dung</b>
<ol>
<li><a href="#q1">RAG là gì, nói gọn trong một câu?</a></li>
<li><a href="#q2">RAG khác fine-tuning thế nào?</a></li>
<li><a href="#q3">RAG có xoá hết bịa đặt không?</a></li>
<li><a href="#q4">Vector là gì mà so sánh được nghĩa?</a></li>
<li><a href="#q5">Chia đoạn bao nhiêu chữ là đúng?</a></li>
<li><a href="#q6">Overlap để làm gì?</a></li>
<li><a href="#q7">top-K nên đặt bao nhiêu?</a></li>
<li><a href="#q8">Chọn mô hình embedding thế nào? Tiếng Việt thì sao?</a></li>
<li><a href="#q9">Bắt buộc phải có Vector DB không?</a></li>
<li><a href="#q10">Chỉ tìm theo vector đã đủ chưa?</a></li>
<li><a href="#q11">Câu hỏi cần nối nhiều tài liệu thì sao?</a></li>
<li><a href="#q12">Context window đã 1 triệu token, còn cần RAG?</a></li>
<li><a href="#q13">Chi phí thực tế bao nhiêu?</a></li>
<li><a href="#q14">RAG làm chậm bao nhiêu?</a></li>
<li><a href="#q15">Tài liệu sửa thì cập nhật thế nào?</a></li>
<li><a href="#q16">Đo chất lượng RAG bằng gì?</a></li>
<li><a href="#q17">Phân quyền tài liệu xử lý ra sao?</a></li>
<li><a href="#q18">Cowork-Local đã có RAG chưa?</a></li>
<li><a href="#q19">GraphRAG của dự án có phải GraphRAG của Microsoft?</a></li>
<li><a href="#q20">Muốn nâng lên RAG đầy đủ cần làm gì?</a></li>
</ol></div>
<h2>Nhóm 1 — Khái niệm</h2>
<div class="qa" id="q1"><div class="q"><span class="n">1</span>
RAG là gì, nói gọn trong một câu?</div>
<div class="a">
<p><b>Tìm tài liệu liên quan trước, rồi đưa cho LLM đọc và trả lời dựa trên đó</b> — thay vì
để LLM trả lời bằng trí nhớ có sẵn.</p>
<p>Ví von: thay vì bắt thí sinh làm bài từ trí nhớ, ta cho <i>thi mở sách</i> — nhưng có
thủ thư lật sẵn đúng trang cần đọc.</p>
</div></div>
<div class="qa" id="q2"><div class="q"><span class="n">2</span>
RAG khác fine-tuning thế nào? Khi nào dùng cái nào?</div>
<div class="a">
<figure>
<svg viewBox="0 0 720 190" role="img" aria-label="So sánh RAG và fine-tuning">
<rect x="8" y="14" width="340" height="162" rx="8" fill="#EAF2FC" stroke="#1565C0"/>
<text x="26" y="40" font-size="15" font-weight="700" fill="#0A4EA3">RAG — đưa thêm tài liệu</text>
<rect x="26" y="56" width="86" height="34" rx="5" fill="#fff" stroke="#4A90D9"/>
<text x="69" y="77" font-size="12" text-anchor="middle" fill="#2B3542">Câu hỏi</text>
<path d="M116 73 h22" stroke="#5A6675" stroke-width="1.6" marker-end="url(#ar)"/>
<rect x="142" y="56" width="94" height="34" rx="5" fill="#fff" stroke="#4A90D9"/>
<text x="189" y="72" font-size="11" text-anchor="middle" fill="#2B3542">Tìm tài liệu</text>
<text x="189" y="84" font-size="10" text-anchor="middle" fill="#5A6675">top-K đoạn</text>
<path d="M240 73 h22" stroke="#5A6675" stroke-width="1.6" marker-end="url(#ar)"/>
<rect x="266" y="56" width="66" height="34" rx="5" fill="#1565C0"/>
<text x="299" y="77" font-size="12" text-anchor="middle" fill="#fff">LLM</text>
<text x="26" y="116" font-size="12" fill="#2B3542">✔ Cập nhật tức thì — chỉ re-index</text>
<text x="26" y="136" font-size="12" fill="#2B3542">✔ Trích được nguồn</text>
<text x="26" y="156" font-size="12" fill="#2B3542">✔ Rẻ, không cần GPU train</text>
<rect x="372" y="14" width="340" height="162" rx="8" fill="#FDF3E3" stroke="#B26A00"/>
<text x="390" y="40" font-size="15" font-weight="700" fill="#8A5000">Fine-tune — dạy lại mô hình</text>
<rect x="390" y="56" width="96" height="34" rx="5" fill="#fff" stroke="#D9A24A"/>
<text x="438" y="72" font-size="11" text-anchor="middle" fill="#2B3542">Dữ liệu mẫu</text>
<text x="438" y="84" font-size="10" text-anchor="middle" fill="#5A6675">hàng nghìn cặp</text>
<path d="M490 73 h22" stroke="#5A6675" stroke-width="1.6" marker-end="url(#ar)"/>
<rect x="516" y="56" width="80" height="34" rx="5" fill="#fff" stroke="#D9A24A"/>
<text x="556" y="77" font-size="12" text-anchor="middle" fill="#2B3542">Huấn luyện</text>
<path d="M600 73 h22" stroke="#5A6675" stroke-width="1.6" marker-end="url(#ar)"/>
<rect x="626" y="56" width="70" height="34" rx="5" fill="#B26A00"/>
<text x="661" y="77" font-size="12" text-anchor="middle" fill="#fff">Model mới</text>
<text x="390" y="116" font-size="12" fill="#2B3542">✔ Dạy được <i>văn phong</i>, định dạng</text>
<text x="390" y="136" font-size="12" fill="#2B3542">✔ Dạy được kỹ năng chuyên ngành</text>
<text x="390" y="156" font-size="12" fill="#2B3542">✘ Kiến thức mới → phải train lại</text>
<defs><marker id="ar" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto">
<path d="M0 0 L7 3.5 L0 7 z" fill="#5A6675"/></marker></defs>
</svg>
<figcaption>RAG thêm <i>kiến thức</i>. Fine-tune thay đổi <i>hành vi</i>.</figcaption>
</figure>
<p><b>Quy tắc chọn:</b> câu trả lời phụ thuộc <i>nội dung tài liệu</i> → RAG.
Phụ thuộc <i>cách nói / định dạng / kỹ năng</i> → fine-tune. Cần cả hai thì làm cả hai.</p>
<div class="note">Đa số bài toán doanh nghiệp là loại thứ nhất, nên RAG hầu như luôn là
bước làm trước.</div>
</div></div>
<div class="qa" id="q3"><div class="q"><span class="n">3</span>
RAG có xoá hết bịa đặt (hallucination) không?</div>
<div class="a">
<p><b>Không. Chỉ giảm mạnh.</b> Đây là câu dễ bị hỏi vặn nhất, nên trả lời thẳng.</p>
<p>RAG vẫn sai được ở bốn chỗ:</p>
<ul>
<li><b>Tra sai đoạn</b> — lấy nhầm tài liệu, LLM trả lời trung thực trên tài liệu sai.</li>
<li><b>Không có trong kho</b> — LLM vẫn cố trả lời thay vì nói "không tìm thấy".</li>
<li><b>Đọc đúng nhưng suy diễn thêm</b> — thêm chi tiết không có trong đoạn trích.</li>
<li><b>Tài liệu gốc đã sai</b> — RAG không kiểm chứng nội dung.</li>
</ul>
<div class="note warn">Cách khắc phục thực dụng: bắt LLM <b>trích dẫn đoạn nguồn</b> cho từng ý,
và cho phép trả lời <b>"không tìm thấy trong tài liệu"</b>. Slide "Ưu điểm" nên nói
<i>giảm</i> hallucination, không nói <i>hết</i>.</div>
</div></div>
<div class="qa" id="q4"><div class="q"><span class="n">4</span>
Vector là gì mà so sánh được "nghĩa giống nhau"?</div>
<div class="a">
<figure>
<svg viewBox="0 0 720 210" role="img" aria-label="Không gian vector, các câu gần nghĩa nằm gần nhau">
<rect x="40" y="14" width="640" height="164" rx="8" fill="#fff" stroke="var(--bds)"/>
<line x1="70" y1="160" x2="660" y2="160" stroke="#CBD5E1"/>
<line x1="70" y1="160" x2="70" y2="30" stroke="#CBD5E1"/>
<circle cx="180" cy="70" r="6" fill="#1565C0"/><text x="192" y="74" font-size="12">"Xe hơi"</text>
<circle cx="214" cy="88" r="6" fill="#1565C0"/><text x="226" y="92" font-size="12">"Ô tô"</text>
<circle cx="196" cy="52" r="6" fill="#1565C0"/><text x="208" y="56" font-size="12">"Xe bốn bánh"</text>
<ellipse cx="200" cy="70" rx="86" ry="46" fill="none" stroke="#1565C0"
stroke-dasharray="4 3" opacity=".6"/>
<circle cx="520" cy="120" r="6" fill="#B26A00"/><text x="532" y="124" font-size="12">"Nấu phở"</text>
<circle cx="556" cy="98" r="6" fill="#B26A00"/><text x="568" y="102" font-size="12">"Công thức bún"</text>
<ellipse cx="540" cy="110" rx="70" ry="38" fill="none" stroke="#B26A00"
stroke-dasharray="4 3" opacity=".6"/>
<circle cx="300" cy="118" r="7" fill="#C0392B"/>
<text x="252" y="140" font-size="12" fill="#C0392B">câu hỏi của user</text>
<line x1="300" y1="118" x2="214" y2="88" stroke="#C0392B" stroke-width="1.4"/>
<text x="236" y="112" font-size="10.5" fill="#C0392B">gần → lấy</text>
<line x1="300" y1="118" x2="520" y2="120" stroke="#CBD5E1" stroke-width="1.2"
stroke-dasharray="3 3"/>
<text x="386" y="134" font-size="10.5" fill="#8A94A3">xa → bỏ qua</text>
</svg>
<figcaption>Mỗi đoạn chữ thành một điểm trong không gian nhiều chiều.
Gần nhau = gần nghĩa.</figcaption>
</figure>
<p>Mô hình embedding biến một đoạn chữ thành dãy số (768 – 4096 chiều). Nó được huấn luyện
sao cho <b>hai đoạn cùng nghĩa cho ra hai điểm gần nhau</b>, kể cả khi không trùng một chữ nào.</p>
<p>Máy đo "gần" bằng <b>cosine similarity</b> — góc giữa hai vector. Nhờ vậy hỏi "xe hơi"
vẫn tìm ra tài liệu viết "ô tô".</p>
<div class="note">Đây chính là điểm RAG hơn tìm kiếm từ khoá: từ khoá cần <i>trùng chữ</i>,
vector chỉ cần <i>trùng nghĩa</i>.</div>
</div></div>
<h2>Nhóm 2 — Tham số kỹ thuật</h2>
<div class="qa" id="q5"><div class="q"><span class="n">5</span>
Chia đoạn bao nhiêu chữ là đúng?</div>
<div class="a">
<p><b>Không có con số đúng chung</b> — phụ thuộc loại tài liệu. Nhưng có nguyên tắc:</p>
<table>
<tr><th>Loại tài liệu</th><th>Cỡ đoạn gợi ý</th><th>Vì sao</th></tr>
<tr><td>FAQ, hỏi đáp ngắn</td><td>100 – 300 chữ</td><td>Mỗi mục vốn đã độc lập</td></tr>
<tr><td>Chính sách, quy trình</td><td>300 – 600 chữ</td><td>Giữ trọn một điều khoản</td></tr>
<tr><td>Sách, báo cáo dài</td><td>500 – 1000 chữ</td><td>Cần đủ ngữ cảnh xung quanh</td></tr>
<tr><td>Mã nguồn</td><td>theo hàm / lớp</td><td>Cắt giữa hàm là hỏng nghĩa</td></tr>
</table>
<div class="note warn"><b>Đoạn quá nhỏ</b> → mất ngữ cảnh, tra ra mảnh vụn vô nghĩa.
<b>Đoạn quá lớn</b> → một đoạn chứa nhiều chủ đề, vector bị "trung bình hoá" nên tra kém chính xác,
lại tốn token.</div>
<p>Thực tế nên <b>cắt theo cấu trúc trước</b> (theo mục, theo điều, theo hàm) rồi mới giới hạn
độ dài — cắt cứng theo số chữ là phương án cuối.</p>
</div></div>
<div class="qa" id="q6"><div class="q"><span class="n">6</span>
Overlap 10–20% để làm gì?</div>
<div class="a">
<figure>
<svg viewBox="0 0 720 150" role="img" aria-label="Chia đoạn có phần chồng lấn">
<text x="20" y="26" font-size="12.5" font-weight="700" fill="#C0392B">Không overlap — câu bị cắt đôi</text>
<rect x="20" y="36" width="200" height="30" rx="4" fill="#EAF2FC" stroke="#4A90D9"/>
<rect x="222" y="36" width="200" height="30" rx="4" fill="#EAF2FC" stroke="#4A90D9"/>
<rect x="424" y="36" width="200" height="30" rx="4" fill="#EAF2FC" stroke="#4A90D9"/>
<text x="120" y="55" font-size="11" text-anchor="middle">đoạn 1</text>
<text x="322" y="55" font-size="11" text-anchor="middle">đoạn 2</text>
<text x="524" y="55" font-size="11" text-anchor="middle">đoạn 3</text>
<line x1="221" y1="30" x2="221" y2="72" stroke="#C0392B" stroke-width="2"/>
<text x="228" y="82" font-size="10.5" fill="#C0392B">"Mức phụ cấp là | 2 triệu/tháng" — mất vế sau</text>
<text x="20" y="110" font-size="12.5" font-weight="700" fill="#1B7A3D">Có overlap — câu nào cũng trọn ở ít nhất 1 đoạn</text>
<rect x="20" y="118" width="210" height="26" rx="4" fill="#E8F5EC" stroke="#1B7A3D"/>
<rect x="196" y="118" width="210" height="26" rx="4" fill="#E8F5EC" stroke="#1B7A3D"
opacity=".75"/>
<rect x="372" y="118" width="210" height="26" rx="4" fill="#E8F5EC" stroke="#1B7A3D"
opacity=".55"/>
<rect x="196" y="118" width="34" height="26" fill="#1B7A3D" opacity=".2"/>
<rect x="372" y="118" width="34" height="26" fill="#1B7A3D" opacity=".2"/>
<text x="600" y="136" font-size="10.5" fill="#1B7A3D">phần tô đậm = chồng lấn</text>
</svg>
<figcaption>Overlap là bảo hiểm cho những câu nằm vắt ngang ranh giới đoạn.</figcaption>
</figure>
<p>Cắt cứng theo số chữ sẽ có lúc cắt <b>giữa một câu hoặc giữa một ý</b>. Đoạn nào cũng
lặp lại một phần đoạn trước thì thông tin ở ranh giới luôn còn nguyên vẹn ở ít nhất một đoạn.</p>
<p>Giá phải trả: kho phình thêm đúng bằng tỉ lệ overlap. 20% overlap → nhiều hơn ~20% vector.</p>
</div></div>
<div class="qa" id="q7"><div class="q"><span class="n">7</span>
top-K nên đặt bao nhiêu?</div>
<div class="a">
<p>Thường <b>3 – 10</b>. Cách chọn:</p>
<ul>
<li><b>K nhỏ (3–5)</b> — câu hỏi tra cứu một dữ kiện. Ít nhiễu, rẻ, nhanh.</li>
<li><b>K lớn (8–15)</b> — câu hỏi tổng hợp, cần gom nhiều nguồn.</li>
</ul>
<div class="note warn">K càng lớn <b>không</b> đồng nghĩa càng chính xác. Đoạn thứ 15 thường
đã lạc đề, và nó <i>làm loãng</i> ngữ cảnh khiến LLM trả lời kém đi — hiện tượng
"lạc giữa đống tài liệu".</div>
<p>Thực dụng hơn: đặt <b>ngưỡng điểm tương đồng</b> thay vì K cố định — lấy mọi đoạn trên
ngưỡng, không có đoạn nào đạt thì trả lời "không tìm thấy".</p>
</div></div>
<div class="qa" id="q8"><div class="q"><span class="n">8</span>
Chọn mô hình embedding thế nào? Tiếng Việt có ổn không?</div>
<div class="a">
<p>Ba tiêu chí: <b>hỗ trợ tiếng Việt</b>, <b>số chiều</b>, <b>chạy nội bộ hay gọi API</b>.</p>
<table>
<tr><th>Nhóm</th><th>Ví dụ</th><th>Ghi chú</th></tr>
<tr><td>API thương mại</td><td>OpenAI <code>text-embedding-3</code>, Cohere</td>
<td>Chất lượng tốt, nhưng <b>tài liệu phải gửi ra ngoài</b></td></tr>
<tr><td>Đa ngữ, chạy nội bộ</td><td>multilingual-e5, BGE-M3</td>
<td>Tiếng Việt khá tốt, chạy được trên máy công ty</td></tr>
<tr><td>Chuyên tiếng Việt</td><td>PhoBERT và các bản fine-tune</td>
<td>Cần đánh giá lại trên chính dữ liệu của mình</td></tr>
</table>
<div class="note bad"><b>Lưu ý bắt buộc:</b> đổi mô hình embedding thì
<b>phải index lại toàn bộ kho</b>. Vector của mô hình này không so sánh được với vector của
mô hình khác. Nên chọn kỹ ngay từ đầu.</div>
<p>Với dữ liệu nội bộ nhạy cảm, nhóm "chạy nội bộ" thường là lựa chọn duy nhất khả thi.</p>
</div></div>
<div class="qa" id="q9"><div class="q"><span class="n">9</span>
Bắt buộc phải có Vector DB riêng không?</div>
<div class="a">
<p><b>Không.</b> Chọn theo quy mô:</p>
<table>
<tr><th>Quy mô</th><th>Giải pháp</th><th>Ghi chú</th></tr>
<tr><td>&lt; 100k vector</td><td>FAISS, Chroma, hoặc file numpy</td>
<td>Không cần dựng thêm dịch vụ</td></tr>
<tr><td>Đã có PostgreSQL</td><td><code>pgvector</code></td>
<td>Dùng luôn DB sẵn có — thường là lựa chọn tốt nhất</td></tr>
<tr><td>Triệu vector trở lên</td><td>Milvus, Qdrant, Weaviate</td>
<td>Cần index ANN chuyên dụng</td></tr>
<tr><td>Không muốn tự vận hành</td><td>Pinecone</td>
<td>Dịch vụ đám mây, dữ liệu ra ngoài</td></tr>
</table>
<p>Ví dụ trong slide — 100 file PDF ra 20.000 vector — <b>hoàn toàn không cần</b> Vector DB
chuyên dụng. FAISS trên một máy là đủ và nhanh.</p>
</div></div>
<h2>Nhóm 3 — Chất lượng truy hồi</h2>
<div class="qa" id="q10"><div class="q"><span class="n">10</span>
Chỉ tìm theo vector đã đủ chưa?</div>
<div class="a">
<figure>
<svg viewBox="0 0 720 168" role="img" aria-label="Hybrid search và rerank">
<rect x="14" y="52" width="98" height="42" rx="6" fill="#fff" stroke="#4A90D9"/>
<text x="63" y="70" font-size="12" text-anchor="middle">Câu hỏi</text>
<text x="63" y="85" font-size="10" text-anchor="middle" fill="#5A6675">của user</text>
<path d="M116 73 h26" stroke="#5A6675" stroke-width="1.6" marker-end="url(#a2)"/>
<rect x="146" y="20" width="128" height="42" rx="6" fill="#EAF2FC" stroke="#1565C0"/>
<text x="210" y="38" font-size="12" text-anchor="middle" fill="#0A4EA3">Tìm theo vector</text>
<text x="210" y="52" font-size="10" text-anchor="middle" fill="#5A6675">bắt được ý nghĩa</text>
<rect x="146" y="84" width="128" height="42" rx="6" fill="#FDF3E3" stroke="#B26A00"/>
<text x="210" y="102" font-size="12" text-anchor="middle" fill="#8A5000">Tìm theo từ khoá</text>
<text x="210" y="116" font-size="10" text-anchor="middle" fill="#5A6675">bắt mã, tên riêng</text>
<path d="M278 41 h20 v32" stroke="#5A6675" stroke-width="1.6" fill="none"/>
<path d="M278 105 h20 v-32" stroke="#5A6675" stroke-width="1.6" fill="none"
marker-end="url(#a2)"/>
<rect x="318" y="52" width="104" height="42" rx="6" fill="#fff" stroke="#4A90D9"/>
<text x="370" y="70" font-size="12" text-anchor="middle">Gộp kết quả</text>
<text x="370" y="85" font-size="10" text-anchor="middle" fill="#5A6675">~30 đoạn</text>
<path d="M426 73 h26" stroke="#5A6675" stroke-width="1.6" marker-end="url(#a2)"/>
<rect x="456" y="52" width="110" height="42" rx="6" fill="#1565C0"/>
<text x="511" y="70" font-size="12" text-anchor="middle" fill="#fff">Rerank</text>
<text x="511" y="85" font-size="10" text-anchor="middle" fill="#D6E7F8">chấm lại điểm</text>
<path d="M570 73 h26" stroke="#5A6675" stroke-width="1.6" marker-end="url(#a2)"/>
<rect x="600" y="52" width="104" height="42" rx="6" fill="#E8F5EC" stroke="#1B7A3D"/>
<text x="652" y="70" font-size="12" text-anchor="middle" fill="#14612F">Top 5 tinh</text>
<text x="652" y="85" font-size="10" text-anchor="middle" fill="#5A6675">đưa cho LLM</text>
<defs><marker id="a2" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto">
<path d="M0 0 L7 3.5 L0 7 z" fill="#5A6675"/></marker></defs>
</svg>
<figcaption>Hai cách tìm bù khuyết cho nhau, rồi lọc lại một lần nữa.</figcaption>
</figure>
<p><b>Chưa đủ.</b> Vector giỏi bắt ý nghĩa nhưng <b>dở với mã số, tên riêng, ký hiệu</b> —
hỏi "điều 7.5.3" hay "mã lỗi FN0101" thì tìm từ khoá lại chính xác hơn hẳn.</p>
<p>Hai cải tiến gần như luôn đáng làm:</p>
<ul>
<li><b>Hybrid search</b> — chạy song song vector + từ khoá (BM25), gộp kết quả.</li>
<li><b>Rerank</b> — lấy ~30 đoạn rồi dùng mô hình cross-encoder chấm lại, giữ 5 đoạn tốt nhất.
Đây thường là <b>cải thiện lớn nhất</b> với chi phí nhỏ nhất.</li>
</ul>
</div></div>
<div class="qa" id="q11"><div class="q"><span class="n">11</span>
Câu hỏi cần nối nhiều tài liệu (multi-hop) thì sao?</div>
<div class="a">
<p>Slide đã nêu đúng đây là điểm yếu. Ví dụ: <i>"Nhân viên nào ký hợp đồng với nhà cung cấp
có doanh số cao nhất năm ngoái?"</i> — cần tra bảng doanh số trước, rồi mới tra hợp đồng.</p>
<p>RAG một lượt sẽ hỏng, vì một lần tra không thể ra cả hai. Ba hướng xử lý:</p>
<ul>
<li><b>Tra nhiều vòng (agentic RAG)</b> — cho LLM tự quyết định tra tiếp, dùng kết quả vòng
trước làm câu truy vấn vòng sau.</li>
<li><b>Tách câu hỏi</b> — chia thành các câu con, tra từng câu, rồi tổng hợp.</li>
<li><b>Knowledge graph</b> — dựng sẵn quan hệ giữa các thực thể để đi theo liên kết thay vì
tra lại từ đầu. Đây chính là ý tưởng của GraphRAG.</li>
</ul>
</div></div>
<h2>Nhóm 4 — Vận hành</h2>
<div class="qa" id="q12"><div class="q"><span class="n">12</span>
Context window đã tới 1 triệu token — còn cần RAG không?</div>
<div class="a">
<p><b>Vẫn cần</b>, vì ba lý do:</p>
<ul>
<li><b>Chi phí</b> — nhét 500k token vào mỗi câu hỏi thì mỗi lượt hỏi tốn gấp hàng trăm lần
so với nhét 5 đoạn. Nhân với số lượt hỏi mỗi ngày.</li>
<li><b>Độ trễ</b> — đọc 500k token mất hàng chục giây.</li>
<li><b>Quy mô</b> — kho tài liệu doanh nghiệp thường vài chục triệu token, vượt xa mọi
context window.</li>
</ul>
<div class="note">Thêm nữa, độ chính xác <b>giảm khi ngữ cảnh quá dài</b> — mô hình hay bỏ sót
thông tin nằm ở giữa. Đưa 5 đoạn đúng thường cho kết quả tốt hơn đưa cả cuốn sách.</div>
<p>Context dài <i>có</i> chỗ dùng: khi tổng tài liệu nhỏ (vài chục trang) và bạn muốn giải pháp
đơn giản nhất — lúc đó bỏ RAG cho gọn là hợp lý.</p>
</div></div>
<div class="qa" id="q13"><div class="q"><span class="n">13</span>
Chi phí thực tế bao nhiêu?</div>
<div class="a">
<p>Tách làm hai phần, và phần đắt <b>không</b> phải phần người ta hay lo:</p>
<table>
<tr><th>Khoản</th><th>Khi nào phát sinh</th><th>Mức độ</th></tr>
<tr><td>Embedding tài liệu</td><td>Một lần lúc index + khi tài liệu đổi</td>
<td><b>Rẻ</b> — embedding rẻ hơn LLM hàng chục lần</td></tr>
<tr><td>Lưu trữ vector</td><td>Liên tục</td><td>Nhỏ, trừ khi kho cực lớn</td></tr>
<tr><td>Embedding câu hỏi</td><td>Mỗi lượt hỏi</td><td>Không đáng kể</td></tr>
<tr><td><b>LLM sinh câu trả lời</b></td><td>Mỗi lượt hỏi</td>
<td><b>Chiếm phần lớn chi phí</b></td></tr>
</table>
<p>Vì vậy giảm chi phí RAG thực chất là <b>giảm số token đưa vào LLM</b> — tức chọn top-K
gọn và đoạn sạch, chứ không phải tiết kiệm ở khâu embedding.</p>
</div></div>
<div class="qa" id="q14"><div class="q"><span class="n">14</span>
RAG làm chậm thêm bao nhiêu?</div>
<div class="a">
<p>Bước tra thường tốn <b>vài chục tới vài trăm mili-giây</b>: embedding câu hỏi + tìm trong
vector DB. Có rerank thì cộng thêm chút nữa.</p>
<p>So với thời gian LLM sinh câu trả lời (thường vài giây), phần này <b>gần như không đáng kể</b>.</p>
<div class="note warn">Slide ghi "ứng dụng real-time cần &lt; 100ms" thì nên cẩn trọng —
đúng, nhưng lúc đó nút thắt là <b>LLM</b>, không phải bước tra. Nếu cần dưới 100ms thì
bản thân việc gọi LLM đã không khả thi rồi.</div>
</div></div>
<div class="qa" id="q15"><div class="q"><span class="n">15</span>
Tài liệu sửa thì cập nhật thế nào?</div>
<div class="a">
<p>Chỉ cần <b>index lại phần thay đổi</b>, không đụng tới mô hình:</p>
<ul>
<li>File sửa → xoá vector cũ của file đó, embedding lại, ghi vector mới.</li>
<li>File xoá → xoá vector tương ứng.</li>
<li>File mới → embedding và thêm vào.</li>
</ul>
<p>Cách làm thực dụng: lưu kèm <b>hash nội dung</b> mỗi file, chạy định kỳ, chỉ xử lý file
có hash đổi. Vài giây cho một lần cập nhật thông thường.</p>
<div class="note bad">Ngoại lệ duy nhất phải làm lại toàn bộ: <b>đổi mô hình embedding</b>
hoặc <b>đổi cách chia đoạn</b>.</div>
</div></div>
<div class="qa" id="q16"><div class="q"><span class="n">16</span>
Đo chất lượng RAG bằng gì? Làm sao biết là tốt?</div>
<div class="a">
<p>Điểm mấu chốt: <b>đo tách hai khâu</b>, vì hỏng ở đâu thì sửa ở đó khác nhau.</p>
<table>
<tr><th>Khâu</th><th>Đo gì</th><th>Hỏng thì sửa gì</th></tr>
<tr><td><b>Truy hồi</b></td><td>Đoạn đúng có nằm trong top-K không?</td>
<td>Chia đoạn, mô hình embedding, hybrid, rerank</td></tr>
<tr><td><b>Sinh câu trả lời</b></td><td>Câu trả lời có bám vào đoạn đã lấy không?</td>
<td>Prompt, model, yêu cầu trích nguồn</td></tr>
</table>
<p>Cách làm tối thiểu mà hiệu quả: dựng <b>bộ 50–100 câu hỏi mẫu có đáp án đúng</b> lấy từ
người dùng thật. Mỗi lần chỉnh tham số thì chạy lại bộ đó và so điểm.</p>
<div class="note">Không có bộ câu hỏi mẫu thì mọi tinh chỉnh chỉ là cảm tính — đây là việc
nên làm ngay từ đầu, trước cả khi tối ưu.</div>
</div></div>
<div class="qa" id="q17"><div class="q"><span class="n">17</span>
Phân quyền tài liệu xử lý ra sao? Người A không được xem tài liệu của phòng B.</div>
<div class="a">
<p>Đây là câu hay bị bỏ quên tới lúc triển khai thật mới lộ ra.</p>
<p><b>Nguyên tắc: lọc quyền ở bước truy hồi, không phải ở bước trả lời.</b> Tuyệt đối không
dựa vào việc nhắc LLM "đừng nói về tài liệu này" — không đáng tin.</p>
<ul>
<li>Mỗi vector lưu kèm <b>metadata quyền</b> (phòng ban, mức mật, danh sách người xem).</li>
<li>Khi tra, lọc theo quyền của người hỏi <b>ngay trong truy vấn</b>.</li>
<li>Tài liệu ngoài quyền thì không bao giờ vào được ngữ cảnh của LLM.</li>
</ul>
<div class="note bad">Rủi ro thường gặp: một đoạn trích chứa thông tin mật lọt vào ngữ cảnh,
LLM tóm tắt lại và <b>rò rỉ gián tiếp</b> dù không trích nguyên văn.</div>
</div></div>
<h2>Nhóm 5 — Về dự án Cowork-Local</h2>
<p class="lead">Nhóm này gần như chắc chắn được hỏi, vì slide 17 đã tự nêu ra.</p>
<div class="qa" id="q18"><div class="q"><span class="n">18</span>
Vậy Cowork-Local đã có RAG chưa?</div>
<div class="a">
<p>Trả lời thẳng như slide 17 đã viết: <b>chưa có RAG theo nghĩa đầy đủ.</b></p>
<figure>
<svg viewBox="0 0 720 150" role="img" aria-label="Ba mức nạp ngữ cảnh">
<rect x="10" y="24" width="222" height="104" rx="8" fill="#FDF3E3" stroke="#B26A00"/>
<text x="121" y="48" font-size="13" font-weight="700" text-anchor="middle" fill="#8A5000">
Mức 1 — Nạp thủ công</text>
<text x="121" y="70" font-size="11.5" text-anchor="middle" fill="#2B3542">Đính kèm file, dán link,</text>
<text x="121" y="86" font-size="11.5" text-anchor="middle" fill="#2B3542">Instructions của project</text>
<text x="121" y="110" font-size="11" text-anchor="middle" fill="#8A5000">Người dùng tự chọn</text>
<rect x="248" y="24" width="222" height="104" rx="8" fill="#EAF2FC" stroke="#1565C0"/>
<text x="359" y="48" font-size="13" font-weight="700" text-anchor="middle" fill="#0A4EA3">
Mức 2 — Tra theo cấu trúc</text>
<text x="359" y="70" font-size="11.5" text-anchor="middle" fill="#2B3542">GraphRAG: sơ đồ file,</text>
<text x="359" y="86" font-size="11.5" text-anchor="middle" fill="#2B3542">lớp, hàm, quan hệ</text>
<text x="359" y="110" font-size="11" text-anchor="middle" fill="#0A4EA3">AI tự tra — đang ở đây</text>
<rect x="486" y="24" width="224" height="104" rx="8" fill="#F2F4F7" stroke="#CBD5E1"
stroke-dasharray="5 4"/>
<text x="598" y="48" font-size="13" font-weight="700" text-anchor="middle" fill="#5A6675">
Mức 3 — Tra theo ngữ nghĩa</text>
<text x="598" y="70" font-size="11.5" text-anchor="middle" fill="#5A6675">Embedding + Vector DB</text>
<text x="598" y="86" font-size="11.5" text-anchor="middle" fill="#5A6675">tìm theo nghĩa</text>
<text x="598" y="110" font-size="11" text-anchor="middle" fill="#8A94A3">chưa có</text>
</svg>
<figcaption>Dự án đang ở mức 2. Mức 3 mới là RAG như trình bày ở phần đầu.</figcaption>
</figure>
<p>Cách nói an toàn khi bị hỏi vặn: <i>"Hiện tại là truy xuất theo cấu trúc, chưa phải truy xuất
theo ngữ nghĩa. Phần trình bày hôm nay là kiến thức nền cho bước tiếp theo."</i></p>
</div></div>
<div class="qa" id="q19"><div class="q"><span class="n">19</span>
"GraphRAG" của dự án có phải GraphRAG của Microsoft không?</div>
<div class="a">
<div class="note warn"><b>Câu này rất dễ bị hỏi và dễ gây hiểu nhầm — nên chủ động làm rõ trước.</b></div>
<table>
<tr><th></th><th>GraphRAG (Microsoft)</th><th>GraphRAG trong Cowork-Local</th></tr>
<tr><td>Đồ thị chứa gì</td><td>Thực thể và quan hệ do <b>LLM trích</b> từ nội dung</td>
<td>File, lớp, hàm và liên kết import</td></tr>
<tr><td>Dựng bằng gì</td><td>Gọi LLM nhiều lượt, tốn chi phí</td>
<td>Phân tích cú pháp mã nguồn, <b>không tốn phí gọi AI</b></td></tr>
<tr><td>Trả lời câu hỏi</td><td>Đi theo quan hệ + tóm tắt theo cụm</td>
<td>Đọc sơ đồ và nội dung file liên quan</td></tr>
</table>
<p><b>Cùng tên, khác bản chất.</b> Slide của bạn mô tả đúng cái thứ hai — "quét file, ghi nhận
mỗi file có class/hàm gì và liên kết với file nào".</p>
<p>Nói rõ điểm này lại là <b>lợi thế</b>: cách của dự án <i>rẻ và nhanh hơn nhiều</i> vì không
phải gọi LLM để dựng đồ thị.</p>
</div></div>
<div class="qa" id="q20"><div class="q"><span class="n">20</span>
Muốn nâng lên RAG đầy đủ thì cần làm gì?</div>
<div class="a">
<p>Bốn việc, xếp theo thứ tự nên làm:</p>
<table>
<tr><th>#</th><th>Việc</th><th>Quyết định phải chốt</th></tr>
<tr><td>1</td><td>Chọn mô hình embedding</td>
<td>Chạy nội bộ hay gọi API — quyết định này ràng buộc mọi thứ sau, và
<b>đổi về sau là phải index lại toàn bộ</b></td></tr>
<tr><td>2</td><td>Chia đoạn tài liệu</td>
<td>Cắt theo cấu trúc (mục, điều, hàm) trước khi cắt theo độ dài</td></tr>
<tr><td>3</td><td>Chọn nơi lưu vector</td>
<td>Quy mô hiện tại chỉ cần FAISS hoặc <code>pgvector</code></td></tr>
<tr><td>4</td><td>Dựng bộ câu hỏi đánh giá</td>
<td>50–100 câu có đáp án đúng — <b>làm trước khi tối ưu</b></td></tr>
</table>
<div class="note ok"><b>Điểm mạnh sẵn có:</b> dự án đã có sẵn khái niệm <i>project</i> với
thư mục riêng và phân tách dữ liệu theo project. Đó chính là ranh giới phân quyền tự nhiên
cho câu 17 — thứ mà nhiều dự án phải làm lại từ đầu.</div>
</div></div>
<h2>Ba câu nên chuẩn bị sẵn câu trả lời</h2>
<div class="note bad"><b>1. "RAG có hết bịa không?"</b> → Không, chỉ giảm. Nói thẳng và nêu
cách giảm: bắt trích nguồn, cho phép trả lời "không tìm thấy".</div>
<div class="note bad"><b>2. "GraphRAG này có phải GraphRAG kia không?"</b> → Không, cùng tên
khác bản chất. Chủ động nói trước khi bị hỏi.</div>
<div class="note bad"><b>3. "Vậy dự án đã có RAG chưa?"</b> → Chưa đủ. Đang ở mức truy xuất
theo cấu trúc, chưa có truy xuất theo ngữ nghĩa.</div>
</div>
</body>
</html>
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+542
View File
@@ -0,0 +1,542 @@
[
{
"slug": "dashboard",
"title": "Dashboard",
"theme": "dark",
"note": "ui/dashboard_tab.py:35",
"file": "screens/dashboard-dark.png",
"error": "",
"nav": "Dashboard",
"nav_expected": "Dashboard"
},
{
"slug": "schedule-kanban",
"title": "Schedule Task — Kanban",
"theme": "dark",
"note": "ui/schedule_task_tab.py:70",
"file": "screens/schedule-kanban-dark.png",
"error": "",
"nav": "Schedule Task",
"nav_expected": "Schedule Task"
},
{
"slug": "schedule-calendar",
"title": "Schedule Task — Calendar",
"theme": "dark",
"note": "ui/calendar_view.py:88",
"file": "screens/schedule-calendar-dark.png",
"error": "",
"nav": "Schedule Task",
"nav_expected": "Schedule Task"
},
{
"slug": "workspace-project",
"title": "Workspace ▸ Project",
"theme": "dark",
"note": "ui/workspace_tab.py:188",
"file": "screens/workspace-project-dark.png",
"error": "",
"nav": "Workspace",
"nav_expected": "Workspace"
},
{
"slug": "workspace-cowork",
"title": "Workspace ▸ Cowork",
"theme": "dark",
"note": "ui/cowork_tab.py:21",
"file": "screens/workspace-cowork-dark.png",
"error": "",
"nav": "Cowork",
"nav_expected": "Cowork"
},
{
"slug": "workspace-co4e",
"title": "Workspace ▸ Co4E",
"theme": "dark",
"note": "ui/co4e_tab.py:228",
"file": "screens/workspace-co4e-dark.png",
"error": "",
"nav": "Co4E",
"nav_expected": "Co4E"
},
{
"slug": "workspace-folder",
"title": "Workspace ▸ Folder",
"theme": "dark",
"note": "ui/folder_tab.py:238",
"file": "screens/workspace-folder-dark.png",
"error": "",
"nav": "Thư mục",
"nav_expected": "Thư mục"
},
{
"slug": "workspace-graphrag",
"title": "Workspace ▸ GraphRAG",
"theme": "dark",
"note": "ui/structure_graph_view.py:188",
"file": "screens/workspace-graphrag-dark.png",
"error": "",
"nav": "GraphRAG",
"nav_expected": "GraphRAG"
},
{
"slug": "monitoring-tổng-quan",
"title": "Monitoring ▸ Tổng quan",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-tổng-quan-dark.png",
"error": "",
"nav": "Tổng quan",
"nav_expected": "Tổng quan"
},
{
"slug": "monitoring-sự-kiện-bảo-mật",
"title": "Monitoring ▸ Sự kiện bảo mật",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-sự-kiện-bảo-mật-dark.png",
"error": "",
"nav": "Sự kiện bảo mật",
"nav_expected": "Sự kiện bảo mật"
},
{
"slug": "monitoring-lịch-sử-gọi-mcp",
"title": "Monitoring ▸ Lịch sử gọi MCP",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-lịch-sử-gọi-mcp-dark.png",
"error": "",
"nav": "Lịch sử gọi MCP",
"nav_expected": "Lịch sử gọi MCP"
},
{
"slug": "monitoring-nhật-ký-hành-động",
"title": "Monitoring ▸ Nhật ký hành động",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-nhật-ký-hành-động-dark.png",
"error": "",
"nav": "Nhật ký hành động",
"nav_expected": "Nhật ký hành động"
},
{
"slug": "monitoring-trạng-thái-agent",
"title": "Monitoring ▸ Trạng thái Agent",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-trạng-thái-agent-dark.png",
"error": "",
"nav": "Trạng thái Agent",
"nav_expected": "Trạng thái Agent"
},
{
"slug": "monitoring-agents-admin",
"title": "Monitoring ▸ Agents Admin",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-agents-admin-dark.png",
"error": "",
"nav": "Agents Admin",
"nav_expected": "Agents Admin"
},
{
"slug": "monitoring-công-cụ",
"title": "Monitoring ▸ Công cụ",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-công-cụ-dark.png",
"error": "",
"nav": "Công cụ",
"nav_expected": "Công cụ"
},
{
"slug": "monitoring-icon",
"title": "Monitoring ▸ Icon",
"theme": "dark",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-icon-dark.png",
"error": "",
"nav": "Icon",
"nav_expected": "Icon"
},
{
"slug": "dialog-settings",
"title": "Settings",
"theme": "dark",
"note": "ui/settings_dialog.py:26",
"file": "screens/dialog-settings-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-task-editor",
"title": "Task Editor",
"theme": "dark",
"note": "ui/task_editor_dialog.py:55",
"file": "screens/dialog-task-editor-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-skills",
"title": "Skills manager",
"theme": "dark",
"note": "ui/skills_dialog.py:108",
"file": "screens/dialog-skills-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-skill-edit",
"title": "Skill editor",
"theme": "dark",
"note": "ui/skills_dialog.py:23",
"file": "screens/dialog-skill-edit-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-file-edit",
"title": "File view & AI edit",
"theme": "dark",
"note": "ui/file_edit_dialog.py:50",
"file": "screens/dialog-file-edit-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-co4e-agent",
"title": "Co4E agent editor",
"theme": "dark",
"note": "ui/co4e_agent_dialog.py:23",
"file": "screens/dialog-co4e-agent-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-ext-connector",
"title": "External connector",
"theme": "dark",
"note": "ui/ext_connector_dialog.py:23",
"file": "screens/dialog-ext-connector-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-permission",
"title": "Permission request",
"theme": "dark",
"note": "ui/permission_dialog.py:13",
"file": "screens/dialog-permission-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-agent-edit",
"title": "Admin agent editor",
"theme": "dark",
"note": "ui/agents_admin_tab.py:35",
"file": "screens/dialog-agent-edit-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-login",
"title": "Login (dead screen — not wired)",
"theme": "dark",
"note": "ui/login_dialog.py:57",
"file": "screens/dialog-login-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "overlay-help-panel",
"title": "Help dock — expanded panel",
"theme": "dark",
"note": "ui/help_agent_widget.py:79",
"file": "screens/overlay-help-panel-dark.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dashboard",
"title": "Dashboard",
"theme": "light",
"note": "ui/dashboard_tab.py:35",
"file": "screens/dashboard-light.png",
"error": "",
"nav": "Dashboard",
"nav_expected": "Dashboard"
},
{
"slug": "schedule-kanban",
"title": "Schedule Task — Kanban",
"theme": "light",
"note": "ui/schedule_task_tab.py:70",
"file": "screens/schedule-kanban-light.png",
"error": "",
"nav": "Schedule Task",
"nav_expected": "Schedule Task"
},
{
"slug": "schedule-calendar",
"title": "Schedule Task — Calendar",
"theme": "light",
"note": "ui/calendar_view.py:88",
"file": "screens/schedule-calendar-light.png",
"error": "",
"nav": "Schedule Task",
"nav_expected": "Schedule Task"
},
{
"slug": "workspace-project",
"title": "Workspace ▸ Project",
"theme": "light",
"note": "ui/workspace_tab.py:188",
"file": "screens/workspace-project-light.png",
"error": "",
"nav": "Workspace",
"nav_expected": "Workspace"
},
{
"slug": "workspace-cowork",
"title": "Workspace ▸ Cowork",
"theme": "light",
"note": "ui/cowork_tab.py:21",
"file": "screens/workspace-cowork-light.png",
"error": "",
"nav": "Cowork",
"nav_expected": "Cowork"
},
{
"slug": "workspace-co4e",
"title": "Workspace ▸ Co4E",
"theme": "light",
"note": "ui/co4e_tab.py:228",
"file": "screens/workspace-co4e-light.png",
"error": "",
"nav": "Co4E",
"nav_expected": "Co4E"
},
{
"slug": "workspace-folder",
"title": "Workspace ▸ Folder",
"theme": "light",
"note": "ui/folder_tab.py:238",
"file": "screens/workspace-folder-light.png",
"error": "",
"nav": "Thư mục",
"nav_expected": "Thư mục"
},
{
"slug": "workspace-graphrag",
"title": "Workspace ▸ GraphRAG",
"theme": "light",
"note": "ui/structure_graph_view.py:188",
"file": "screens/workspace-graphrag-light.png",
"error": "",
"nav": "GraphRAG",
"nav_expected": "GraphRAG"
},
{
"slug": "monitoring-tổng-quan",
"title": "Monitoring ▸ Tổng quan",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-tổng-quan-light.png",
"error": "",
"nav": "Tổng quan",
"nav_expected": "Tổng quan"
},
{
"slug": "monitoring-sự-kiện-bảo-mật",
"title": "Monitoring ▸ Sự kiện bảo mật",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-sự-kiện-bảo-mật-light.png",
"error": "",
"nav": "Sự kiện bảo mật",
"nav_expected": "Sự kiện bảo mật"
},
{
"slug": "monitoring-lịch-sử-gọi-mcp",
"title": "Monitoring ▸ Lịch sử gọi MCP",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-lịch-sử-gọi-mcp-light.png",
"error": "",
"nav": "Lịch sử gọi MCP",
"nav_expected": "Lịch sử gọi MCP"
},
{
"slug": "monitoring-nhật-ký-hành-động",
"title": "Monitoring ▸ Nhật ký hành động",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-nhật-ký-hành-động-light.png",
"error": "",
"nav": "Nhật ký hành động",
"nav_expected": "Nhật ký hành động"
},
{
"slug": "monitoring-trạng-thái-agent",
"title": "Monitoring ▸ Trạng thái Agent",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-trạng-thái-agent-light.png",
"error": "",
"nav": "Trạng thái Agent",
"nav_expected": "Trạng thái Agent"
},
{
"slug": "monitoring-agents-admin",
"title": "Monitoring ▸ Agents Admin",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-agents-admin-light.png",
"error": "",
"nav": "Agents Admin",
"nav_expected": "Agents Admin"
},
{
"slug": "monitoring-công-cụ",
"title": "Monitoring ▸ Công cụ",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-công-cụ-light.png",
"error": "",
"nav": "Công cụ",
"nav_expected": "Công cụ"
},
{
"slug": "monitoring-icon",
"title": "Monitoring ▸ Icon",
"theme": "light",
"note": "ui/monitoring_tab.py:132",
"file": "screens/monitoring-icon-light.png",
"error": "",
"nav": "Icon",
"nav_expected": "Icon"
},
{
"slug": "dialog-settings",
"title": "Settings",
"theme": "light",
"note": "ui/settings_dialog.py:26",
"file": "screens/dialog-settings-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-task-editor",
"title": "Task Editor",
"theme": "light",
"note": "ui/task_editor_dialog.py:55",
"file": "screens/dialog-task-editor-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-skills",
"title": "Skills manager",
"theme": "light",
"note": "ui/skills_dialog.py:108",
"file": "screens/dialog-skills-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-skill-edit",
"title": "Skill editor",
"theme": "light",
"note": "ui/skills_dialog.py:23",
"file": "screens/dialog-skill-edit-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-file-edit",
"title": "File view & AI edit",
"theme": "light",
"note": "ui/file_edit_dialog.py:50",
"file": "screens/dialog-file-edit-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-co4e-agent",
"title": "Co4E agent editor",
"theme": "light",
"note": "ui/co4e_agent_dialog.py:23",
"file": "screens/dialog-co4e-agent-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-ext-connector",
"title": "External connector",
"theme": "light",
"note": "ui/ext_connector_dialog.py:23",
"file": "screens/dialog-ext-connector-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-permission",
"title": "Permission request",
"theme": "light",
"note": "ui/permission_dialog.py:13",
"file": "screens/dialog-permission-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-agent-edit",
"title": "Admin agent editor",
"theme": "light",
"note": "ui/agents_admin_tab.py:35",
"file": "screens/dialog-agent-edit-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "dialog-login",
"title": "Login (dead screen — not wired)",
"theme": "light",
"note": "ui/login_dialog.py:57",
"file": "screens/dialog-login-light.png",
"error": "",
"nav": "",
"nav_expected": ""
},
{
"slug": "overlay-help-panel",
"title": "Help dock — expanded panel",
"theme": "light",
"note": "ui/help_agent_widget.py:79",
"file": "screens/overlay-help-panel-light.png",
"error": "",
"nav": "",
"nav_expected": ""
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+1175
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long