An AI companion that walks citizens through every administrative procedure. Filed right the first time.
Vietnam AI Innovation Challenge, track AI-guided public service procedures.
Govi plugs straight into the real National Public Service Portal. A citizen opens the procedure page they already know, and Govi shows the filing form right there, checks it before they submit, reads their document photos for them and cross-checks the values. No portal clone, no app to install, no leaving the page.
Read this first: ONE-PAGER (5 min) · Storyboard of the real UI · Storyboard of the real Portal UI · Why we put the form inside the portal ← the question everyone asks · Architecture and system diagram · AI Safety and Grounding · Demo script · Integration roadmap
8 steps: (I) one complete filing pass, from injecting the button, opening the form,
two-layer validation and OCR cross-check, through to ready to submit; (II) the four
remaining features that have a UI of their own: proactive suggestions, legal citation
explanations, Govi Radar, smart search. The HTML/CSS is taken word for word from today's
ui.js / insights.js / legal-explain.js / radar.js / search.js. Only the sample
data and the portal frame around it are staged.
Interactive version (open it in any browser, nothing to install): docs/storyboard-ui.html
Real screenshots from the running portal, not a rebuild: login, overview, form health, suggestion review, citizen submissions, policies.
| Component | What it is | Address |
|---|---|---|
govi-widget/apps/extension/ |
The main product, an MV3 content script that puts the form into the portal, buildless | Load unpacked |
govi-backend/ |
FastAPI: OCR, AI validation, smart search, telemetry, policy radar | https://api.vietnamaichallenge-govi.team |
govi-portal/ |
Next.js dashboard for civil servants: Form Health, suggestion review, policy review, submission review | https://portal.vietnamaichallenge-govi.team |
govi-infra/ |
Terraform and Ansible: GCP, Docker Swarm, nginx/TLS, Keycloak, SigNoz | (no public address) |
Supporting infrastructure: auth.vietnamaichallenge-govi.team (Keycloak),
obs.vietnamaichallenge-govi.team (SigNoz, for traces/logs/metrics).
Portal demo account: admin / admin.
Pushing to main deploys: GitHub Actions → Workload Identity Federation (no
service-account keys) → Cloud Build → ssh over IAP → Docker Swarm rolling update.
All inference runs through FPT AI Marketplace (OpenAI-compatible), not a foreign model provider. This is a deliberate choice: the administrative data of Vietnamese citizens is processed on infrastructure located in Vietnam and run by a Vietnamese company. That is very nearly a hard requirement for real integration into public services.
| Task | Model | How the output is constrained |
|---|---|---|
| Semantic validation of a filing | gpt-oss-120b |
forced tool call + closed enum of 15 error codes |
| Reading document images (OCR/VLM) | Qwen2.5-VL-7B-Instruct |
prompt-JSON + a deterministic guardrail behind it |
| Two-VLM consensus | Qwen2.5-VL-7B + gemma-3-27b-it |
pure-Python comparison; if they disagree, keep the primary and flag it |
| Search ranking | self-hosted BM25 (runs locally) | (not a model) |
| Search refusal gate | bge-reranker-v2-m3 |
only decides "is this relevant", it does not re-rank |
| Pulling conditions out of legal texts | gpt-oss-120b |
forced tool + closed attribute enum, fail-closed |
Without an API key the backend degrades instead of dying: validation falls back to the deterministic rule layer, search falls back to plain BM25.
This section says exactly what runs for real, what is simulated and what is not switched on, so nobody has to guess.
Real, with real data
- Form injection and deterministic validation across 135 procedures (
apps/extension/forms.js, about 2,660 declared fields). - OCR, semantic validation and document cross-checking through the backend.
- Smart search over the 25 essential public services of Project 06 (108 knowledge chunks, 39 sources, 41 chunks eligible for citation).
- Portal: Form Health, suggestion review, policy CRUD and review, submission review.
- Real Keycloak login (ROPC), real SigNoz observability, real CI/CD.
- 397 backend tests and 49 extension assertions (4 jsdom suites), all green.
Simulated on purpose, a demo of the idea rather than unfinished work
profile_service.py (7 hardcoded personas) is simulated data on purpose. It exists to
prove something words cannot: once Govi is connected to citizen data, the interface is
already able to receive it. The searchProfile contract runs for real, plugs straight
into /ai/smart-search, and has tests locking its shape.
What is missing is access to the data, not the source code.
Govi aims to be integrated with the National Data Center. That is when this feature changes level: instead of citizens describing their own situation, Govi already understands their life context (a birth certificate just issued at home, an ID card about to expire, a child turning six) and offers the right procedure before they think to ask. That is an institutional condition, it needs a ministry-level agreement, not a technical one. See PILOT.md §3.
Until the data is real, smart search deliberately returns profileApplied: false: it
accepts a correctly shaped profile but does not use it for ranking. Reporting true on
simulated data would be an overclaim.
This personalization does not contradict the Radar module's principle of "Govi delivers the question, never receives the answer". They are two different data flows, told apart in AI-SAFETY.md §6.
Built, not switched on
- Dense retrieval (
smart_search/embed.py) is fully implemented but not wired into the production engine. The reason is a measurement, not an oversight: across 68 in-scope cases, letting the cross-encoder re-rank dropped Recall@1 from 0.933 to 0.844. BM25 plus the refusal gate does better at a 25-procedure scale. The code stays, together with its numbers, ready to switch on when the corpus grows.
Coverage: two overlapping sets, not nested ones
| Count | |
|---|---|
| Procedures with a filing form and validation | 135 |
| Procedures with a smart lookup card (Project 06) | 25 |
Of those, at detailed level, with full legal citations |
6 |
| Procedures with both (fileable and searchable) | 6 |
The two layers differ on purpose. Filing and validation only needs a form definition, so it scales fast. Smart lookup cites the legal basis, and once you quote the law for a citizen to read, the underlying data has to be verified first. So it only opens for the part where that work is done.
Not citing law for unverified content is a principle, not a technical limit. The engine is complete and data-driven: widening coverage means loading more verified data, without touching a single line of code.
We measured the gap between "the team grading itself" and "a stranger typing"
The original eval set was written by the team, so its questions and aliases share one
source, which makes it optimistic. The team wrote a second, adversarial eval set
(govi-backend/evals/, 53 cases) under the opposite rule: questions have to be phrased the
way citizens actually speak, and reusing corpus aliases is forbidden.
| Metric | Original set | Adversarial set |
|---|---|---|
| Correct refusal (out of scope) | 1.000 | 1.000 |
| Recall@1 | 0.933 | 0.684 |
| Recall@5 | 1.000 | 0.763 |
A drop of about 0.24 on both recall metrics. The right-hand column is the number to trust.
This set caught a real trade-off on its very first run: the scope_exclusions fix took
correct refusal from 0.800 to 1.000 but pulled recall down by 0.053, and the two cases
it lost were genuine ones (đăng ký kết hôn cần giấy tờ gì is now refused, even though
đăng ký kết hôn still works). Details and the plan: AI-SAFETY §9.1.
tests/test_smart_search_eval.py locks it in as a regression net.
Not done
- An eval set built from the portal's real search logs. The adversarial set is still team-written.
- Legal-data verification for the 19 of 25 cards not yet at
detailedlevel.
# chrome://extensions → turn on Developer mode → Load unpacked → select:
govi-widget/apps/extensionThen open a real procedure page:
- Legacy portal: https://vpcp.dichvucong.gov.vn/p/home/dvc-chi-tiet-thu-tuc-nganh-doc.html?ma_thu_tuc=1.004194
- New portal: https://dichvucong.gov.vn/thu-tuc-hanh-chinh/019d2bf8-42e3-7208-be06-b276fc806cda
The extension already points at the production backend, so nothing needs to run locally. Click-by-click script: docs/DEMO.md.
cd govi-widget && pnpm install && pnpm test # 4 jsdom suitescd govi-backend
cp .env.example .env # set FPT_MARKETPLACE_API_KEY to turn on the AI layer
python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest -q
docker compose up -d postgres && .venv/bin/alembic upgrade head
.venv/bin/uvicorn app.main:app --reload # Swagger at /docsCLAUDE.md.
cd govi-portal && pnpm install
cp .env.example .env.local # point GOVI_API_URL at the backend
pnpm dev| File | Contents |
|---|---|
| docs/ONE-PAGER.md | Problem, solution, target users, rollout plan |
| docs/storyboard-ui.html | 8-step storyboard of the real extension UI. Download it and open in a browser, no install needed |
| Portal storyboard | The 8 govi-portal screens |
| docs/WHY-EMBED.md | Why we put the form inside the real portal instead of building a separate app, and why that is the shape of the future portal |
| docs/ARCHITECTURE.md | System diagram, data flow, the reason behind each choice |
| docs/AI-SAFETY.md | Anti-hallucination principles, grounding, human-in-the-loop, known limits |
| docs/DEMO.md | Step-by-step demo script for judges |
| docs/PILOT.md | Roadmap for integration into public services |
| docs/error-codes.md | Error-code taxonomy, the source of truth |
| docs/telemetry-spec.md | Form event collection (no PII) |
| docs/insight-spec.md | The telemetry to suggestion loop that helps the next citizen |
| docs/policy-radar-spec.md | "The law finds the person": new policies reaching the right beneficiaries |
| CLAUDE.md | Engineering conventions, traps we hit, open work |
| govi-infra/README.md | Infrastructure, deployment, operations |
Trợ lý AI đồng hành cùng người dân trong mọi thủ tục hành chính. Nộp đúng ngay lần đầu.
Vietnam AI Innovation Challenge, đề AI-guided public service procedures.
Govi chèn thẳng vào Cổng Dịch vụ công thật. Người dân mở trang thủ tục quen thuộc, Govi hiện form kê khai ngay tại chỗ, soát lỗi trước khi nộp, đọc giúp ảnh giấy tờ và đối chiếu chéo. Không clone cổng, không cài app, không rời trang.
Lấy gì đọc trước: ONE-PAGER (5 phút) · Storyboard UI thật · Storyboard Portal UI thật · Vì sao chèn form vào cổng ← câu hỏi ai cũng hỏi · Kiến trúc và sơ đồ hệ thống · AI Safety và Grounding · Kịch bản demo · Lộ trình tích hợp
8 bước: (I) một lượt kê khai đầy đủ, từ chèn nút, mở form, soát lỗi 2 tầng và đối
chiếu OCR, cho tới lúc sẵn sàng nộp; (II) 4 tính năng còn lại có giao diện riêng: gợi ý
chủ động, giải thích trích dẫn pháp lý, Govi Radar, tìm kiếm thông minh. HTML/CSS lấy
nguyên văn từ ui.js / insights.js / legal-explain.js / radar.js / search.js
hiện tại. Chỉ dữ liệu mẫu và khung cổng xung quanh là dàn dựng.
Bản tương tác (mở bằng trình duyệt, không cần cài gì): docs/storyboard-ui.html
Ảnh chụp thật từ portal đang chạy, không phải dựng lại: đăng nhập, tổng quan, sức khoẻ biểu mẫu, duyệt gợi ý, hồ sơ người dân nộp về, chính sách.
| Thành phần | Là gì | Địa chỉ |
|---|---|---|
govi-widget/apps/extension/ |
Sản phẩm chính, content script MV3 chèn form vào cổng, buildless | cài Load unpacked |
govi-backend/ |
FastAPI: OCR, soát lỗi AI, smart search, telemetry, radar chính sách | https://api.vietnamaichallenge-govi.team |
govi-portal/ |
Bảng điều khiển Next.js cho cán bộ: Form Health, duyệt gợi ý, duyệt chính sách, duyệt hồ sơ | https://portal.vietnamaichallenge-govi.team |
govi-infra/ |
Terraform và Ansible: GCP, Docker Swarm, nginx/TLS, Keycloak, SigNoz | (không có địa chỉ công khai) |
Hạ tầng phụ: auth.vietnamaichallenge-govi.team (Keycloak),
obs.vietnamaichallenge-govi.team (SigNoz, lo trace/log/metric).
Tài khoản demo portal: admin / admin.
Push main là deploy: GitHub Actions → Workload Identity Federation (không có
service-account key) → Cloud Build → ssh qua IAP → Docker Swarm rolling update.
Toàn bộ suy luận chạy qua FPT AI Marketplace (OpenAI-compatible), không phải model nước ngoài. Đây là lựa chọn có chủ ý: dữ liệu hành chính của công dân Việt Nam được xử lý bởi hạ tầng đặt tại Việt Nam, do doanh nghiệp Việt Nam vận hành. Đó gần như là điều kiện bắt buộc nếu muốn tích hợp thật vào hệ thống dịch vụ công.
| Việc | Model | Cách ràng buộc đầu ra |
|---|---|---|
| Soát lỗi ngữ nghĩa hồ sơ | gpt-oss-120b |
forced tool call + enum đóng 15 mã lỗi |
| Đọc ảnh giấy tờ (OCR/VLM) | Qwen2.5-VL-7B-Instruct |
prompt-JSON + hàng rào kiểm tất định phía sau |
| Đồng thuận hai VLM | Qwen2.5-VL-7B + gemma-3-27b-it |
so khớp thuần Python; lệch thì giữ bản chính và ghi chú |
| Xếp hạng tìm kiếm | BM25 tự cài (chạy tại chỗ) | (không phải model) |
| Cổng từ chối tìm kiếm | bge-reranker-v2-m3 |
chỉ phán "có liên quan không", không xếp lại |
| Trích điều kiện từ văn bản luật | gpt-oss-120b |
forced tool + enum thuộc tính đóng, fail-closed |
Thiếu khoá API thì backend giảm cấp chứ không chết: soát lỗi rơi về tầng luật tất định, tìm kiếm rơi về BM25 thuần.
Phần này liệt kê đúng cái gì đã chạy thật, cái gì là mô phỏng, cái gì chưa bật, để không ai phải đoán.
Chạy thật, có dữ liệu thật
- Chèn form và soát lỗi tầng thuần trên 135 thủ tục (
apps/extension/forms.js, khoảng 2.660 ô khai báo). - OCR, soát ngữ nghĩa và đối chiếu chéo giấy tờ qua backend.
- Smart search trên 25 dịch vụ công thiết yếu Đề án 06 (108 knowledge chunk, 39 nguồn, 41 chunk đủ điều kiện trích dẫn).
- Portal: Form Health, duyệt gợi ý, CRUD và duyệt chính sách, duyệt hồ sơ nộp về.
- Đăng nhập Keycloak thật (ROPC), quan trắc SigNoz thật, CI/CD thật.
- 397 test backend và 49 assertion extension (4 bộ jsdom), tất cả xanh.
Mô phỏng có chủ đích, là demo tư tưởng chứ không phải chỗ làm dở
profile_service.py (7 persona hardcode) cố ý là dữ liệu mô phỏng. Nó tồn tại để
chứng minh một điều không chứng minh được bằng lời: khi Govi được nối vào dữ liệu công
dân, giao diện đã sẵn sàng nhận. Hợp đồng searchProfile đã chạy thật, cắm thẳng
vào /ai/smart-search được, có test khoá shape.
Thứ còn thiếu là quyền truy cập dữ liệu, không phải mã nguồn.
Govi hướng tới được tích hợp với Trung tâm Dữ liệu Quốc gia. Đó là lúc tính năng này đổi bậc: thay vì người dân phải tự mô tả hoàn cảnh, Govi hiểu sẵn bối cảnh cuộc sống của họ (nhà vừa có giấy chứng sinh, căn cước sắp hết hạn, con đến tuổi vào lớp 1) và đưa đúng thủ tục lên trước khi họ kịp hỏi. Việc này thuộc nhóm điều kiện thể chế, cần thoả thuận cấp bộ, chứ không phải điều kiện kỹ thuật. Xem PILOT.md §3.
Chừng nào chưa có dữ liệu thật, smart search cố ý trả profileApplied: false: nhận
hồ sơ đúng shape nhưng không dùng để xếp hạng. Báo true trên dữ liệu mô phỏng sẽ là
nói quá.
Cá nhân hoá này không mâu thuẫn với nguyên tắc "Govi giao câu hỏi, không nhận câu trả lời" của module Radar. Đó là hai luồng dữ liệu khác nhau, phân biệt rõ ở AI-SAFETY.md §6.
Đã cài, chưa bật
- Dense retrieval (
smart_search/embed.py) đã cài đặt đầy đủ nhưng không được nối vào engine production. Lý do là số đo, không phải quên: trên 68 ca in-scope, để cross-encoder xếp lại làm Recall@1 tụt từ 0.933 xuống 0.844. BM25 cộng cổng từ chối cho kết quả tốt hơn ở quy mô 25 thủ tục. Code giữ lại kèm số đo, sẵn sàng bật khi corpus lớn hơn.
Độ phủ: hai tập giao nhau, không lồng nhau
| Số lượng | |
|---|---|
| Thủ tục có form kê khai và soát lỗi | 135 |
| Thủ tục có thẻ tra cứu thông minh (Đề án 06) | 25 |
Trong đó đạt mức detailed, có trích dẫn pháp lý đầy đủ |
6 |
| Thủ tục có cả hai (vừa kê khai được vừa tra cứu được) | 6 |
Hai tầng cố ý khác nhau. Kê khai và soát lỗi chỉ cần định nghĩa form, nên phủ rộng được nhanh. Tra cứu thông minh thì trích dẫn căn cứ pháp lý, mà đã trích luật cho người dân đọc thì dữ liệu phải được thẩm định trước. Nên nó chỉ mở cho phần đã làm xong việc đó.
Không trích dẫn luật cho thứ chưa thẩm định là nguyên tắc, không phải giới hạn kỹ thuật. Engine đã hoàn chỉnh và data-driven: mở rộng phạm vi là nạp thêm dữ liệu đã thẩm định, không đụng một dòng code nào.
Đã tự đo khoảng cách giữa "đội tự chấm" và "người lạ gõ"
Bộ eval gốc do chính đội soạn nên câu hỏi và alias cùng một nguồn, vì thế nó lạc quan.
Đội soạn thêm một bộ eval đối kháng (govi-backend/evals/, 53 ca) theo luật ngược
lại: câu hỏi phải diễn đạt như người dân nói, cấm tái dùng alias trong corpus.
| Chỉ số | Bộ gốc | Bộ đối kháng |
|---|---|---|
| Từ chối đúng (ngoài phạm vi) | 1.000 | 1.000 |
| Recall@1 | 0.933 | 0.684 |
| Recall@5 | 1.000 | 0.763 |
Chênh khoảng 0.24 ở hai chỉ số recall. Con số bên phải là con số đáng tin hơn.
Bộ này đã bắt được một đánh đổi thật ngay ngày đầu chạy: bản sửa scope_exclusions đưa
từ chối đúng từ 0.800 lên 1.000 nhưng kéo recall xuống 0.053, và hai ca mất đi là ca
thật (đăng ký kết hôn cần giấy tờ gì giờ bị từ chối, dù đăng ký kết hôn vẫn chạy).
Chi tiết và hướng xử lý: AI-SAFETY §9.1.
tests/test_smart_search_eval.py khoá lại làm lưới chặn hồi quy.
Chưa làm
- Bộ eval từ log tra cứu thật của cổng. Bộ đối kháng vẫn do đội soạn.
- Thẩm định dữ liệu pháp lý cho 19 trong 25 thẻ chưa đạt mức
detailed.
# chrome://extensions → bật Developer mode → Load unpacked → chọn:
govi-widget/apps/extensionRồi mở một trang thủ tục thật:
- Cổng cũ: https://vpcp.dichvucong.gov.vn/p/home/dvc-chi-tiet-thu-tuc-nganh-doc.html?ma_thu_tuc=1.004194
- Cổng mới: https://dichvucong.gov.vn/thu-tuc-hanh-chinh/019d2bf8-42e3-7208-be06-b276fc806cda
Extension đã trỏ sẵn về backend production, nên không cần chạy gì ở máy. Kịch bản bấm từng bước: docs/DEMO.md.
cd govi-widget && pnpm install && pnpm test # 4 bộ test jsdomcd govi-backend
cp .env.example .env # đặt FPT_MARKETPLACE_API_KEY nếu muốn bật tầng AI
python3.12 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest -q
docker compose up -d postgres && .venv/bin/alembic upgrade head
.venv/bin/uvicorn app.main:app --reload # /docs có SwaggerCLAUDE.md.
cd govi-portal && pnpm install
cp .env.example .env.local # trỏ GOVI_API_URL về backend
pnpm dev| File | Nội dung |
|---|---|
| docs/ONE-PAGER.md | Vấn đề, giải pháp, người dùng mục tiêu, lộ trình triển khai |
| docs/storyboard-ui.html | Storyboard 8 bước UI thật (extension). Tải về mở bằng trình duyệt, không cần cài extension |
| Storyboard Portal | 8 màn hình govi-portal |
| docs/WHY-EMBED.md | Vì sao chèn form vào cổng thật thay vì dựng app riêng, và vì sao đó là hình dạng của cổng trong tương lai |
| docs/ARCHITECTURE.md | Sơ đồ hệ thống, luồng dữ liệu, lý do từng lựa chọn |
| docs/AI-SAFETY.md | Nguyên tắc chống bịa, grounding, human-in-the-loop, giới hạn đã biết |
| docs/DEMO.md | Kịch bản demo từng bước cho người chấm |
| docs/PILOT.md | Lộ trình tích hợp vào hệ thống dịch vụ công |
| docs/error-codes.md | Taxonomy mã lỗi, nguồn sự thật |
| docs/telemetry-spec.md | Thu thập sự kiện form (không PII) |
| docs/insight-spec.md | Vòng lặp telemetry thành gợi ý, cứu người dân kế tiếp |
| docs/policy-radar-spec.md | "Luật đi tìm người": chính sách mới tìm đúng người thụ hưởng |
| CLAUDE.md | Quy ước kỹ thuật, bẫy đã gặp, việc đang mở |
| govi-infra/README.md | Hạ tầng, deploy, vận hành |

