GreenTemis Agent là nền tảng phỏng vấn ứng viên có hỗ trợ AI. Hệ thống cho phép HR nhập CV, job description và yêu cầu bổ sung; backend trích xuất thông tin, gọi các AI agent để sinh kế hoạch phỏng vấn và bài đánh giá, tạo phòng LiveKit cho ứng viên, điều phối buổi phỏng vấn giọng nói, ghi nhận transcript, bài làm, tín hiệu proctoring, bản ghi hình và cuối cùng sinh báo cáo đánh giá.
Dự án được thiết kế theo hướng nhiều service chạy bằng Docker Compose: frontend React cho HR/ứng viên, backend FastAPI làm source of truth, cụm ai-services chứa các agent và MCP tools, cùng các hạ tầng Postgres, LiveKit, MinIO và ClickHouse.
GreenTemis Agent tập trung vào bài toán AI screening CV và phỏng vấn sàng lọc ban đầu. Thay vì để HR đọc thủ công từng CV rồi mới chuyển ứng viên qua nhiều vòng hỏi lặp lại, hệ thống dùng AI để đọc CV + JD, khai thác thêm ứng viên qua một buổi phỏng vấn ngắn, và tạo báo cáo hồ sơ có cấu trúc cho các vòng sau.
Ứng viên nộp CV
-> HR đọc và lọc CV thủ công
-> HR screening qua điện thoại / meeting ngắn
-> chuyển sang phỏng vấn chuyên môn
-> interviewer phải đọc lại CV, hỏi lại bối cảnh, tự tìm điểm cần đào sâu
-> tổng hợp nhận xét rời rạc sau nhiều vòng
Các vấn đề thường gặp:
- Tốn nhiều thời gian ở bước lọc CV, đặc biệt khi số lượng ứng viên lớn.
- Dễ bỏ sót ứng viên có tín hiệu tốt nhưng CV trình bày chưa nổi bật hoặc không khớp keyword trực tiếp.
- HR screening thường chỉ kiểm tra thông tin cơ bản, chưa khai thác đủ năng lực thật sự của ứng viên.
- Vòng chuyên môn sau đó phải hỏi lại nhiều thông tin nền, làm mất thời gian của interviewer và ứng viên.
- Nhận xét giữa các vòng thiếu cấu trúc chung, khó so sánh ứng viên một cách nhất quán.
Ứng viên nộp CV + JD/vị trí tuyển dụng
-> AI Screening CV: phân tích CV, đối chiếu JD, phát hiện điểm mạnh / điểm thiếu / tín hiệu cần kiểm chứng
-> AI Interview Agent phỏng vấn sàng lọc để khai thác thêm các chủ đề CV chưa thể hiện rõ
-> Assignment Agent tạo bài coding/cognitive phù hợp với vị trí
-> Inspector Agent tổng hợp transcript, bài làm và tín hiệu proctoring
-> HR/interviewer nhận báo cáo hồ sơ có cấu trúc để dùng cho các vòng sau
Giá trị chính:
- Giảm thời gian lọc CV thủ công cho HR.
- Tránh bỏ sót ứng viên tiềm năng nhờ phân tích cả CV, JD và phần trả lời trong buổi screening.
- Khai thác ứng viên sâu hơn ở vòng đầu: AI hỏi theo kinh nghiệm thật, skill gap, dự án, mức độ sở hữu và khả năng giải thích.
- Chuẩn hóa hồ sơ ứng viên thành report gồm transcript, điểm năng lực, bài làm, tín hiệu rủi ro và khuyến nghị.
- Các vòng phỏng vấn sau không cần bắt đầu lại từ CV thô; interviewer chỉ cần dựa trên báo cáo để đào sâu đúng chủ đề quan trọng.
| Lớp | Công nghệ chính |
|---|---|
| Frontend | React 19, Vite 8, LiveKit Client, Monaco Editor, Sandpack, MediaPipe Tasks Vision, ESLint |
| Backend API | Python 3.12, FastAPI, Uvicorn, Pydantic, SQLAlchemy async, asyncpg |
| AI services | FastAPI, FastMCP/MCP SSE, LiveKit Agents, Microsoft Agent Framework, OpenAI-compatible LLM/STT/TTS, pydantic-settings |
| Data | PostgreSQL, MinIO/S3-compatible storage, ClickHouse |
| Voice/Realtime | LiveKit room, LiveKit agent dispatch, remote STT, remote LLM, remote Kokoro-compatible TTS |
| Report/Document | Markdown, fpdf2, matplotlib, PDF upload/download |
| Infra | Docker Compose, YAML service configs, .env secrets |
| Test | Pytest, Vitest-style frontend lint/build scripts, Locust load test config |
Luồng tạo và chạy phỏng vấn:
- HR gửi CV/JD lên
POST /api/v1/interviews/generate-link. - Backend trích xuất CV, gọi Planning Agent và Assignment Agent song song, tạo LiveKit room, lưu bản ghi
Interview. - Candidate mở
/interview/:id; frontend lấy LiveKit join token từ backend. - Backend dispatch
interview-agentvào room nếu agent chưa có mặt. - Interview Agent đọc metadata
interview_id, fetch context qua MCP, nói lời chào, phỏng vấn và ghi transcript. - Agent hoặc frontend gọi
/end; backend chuyển trạng thái sangevaluating, chạy Inspector Agent nền và phát SSEreport_readykhi report xong.
Phỏng vấn từ xa mở ra rủi ro gian lận mà phỏng vấn trực tiếp không có: tra phao trên màn hình phụ, có người nhắc bài ngoài khung hình, rời cửa sổ để tìm đáp án. GreenTemis xử lý bài toán này bằng một lớp giám thị ảo (proctoring) chống gian lận chạy realtime xuyên suốt buổi phỏng vấn, hoạt động end-to-end qua cả ba tầng của hệ thống: phát hiện ngay trên trình duyệt ứng viên → backend ghi nhận và điều phối → Interview Agent phản ứng tức thời bằng giọng nói → Inspector Agent đưa yếu tố liêm chính (integrity) vào báo cáo cuối.
Tín hiệu (kind) |
Cách phát hiện | Hành vi cảnh báo |
|---|---|---|
secondary_monitor |
Window Management API / screen.isExtended (Chromium qua HTTPS) |
Cắm thêm màn hình phụ để mở tài liệu/đáp án |
tab_switch |
Sự kiện visibilitychange + blur của trình duyệt |
Rời tab/cửa sổ phỏng vấn để tra cứu nơi khác |
gaze_away |
MediaPipe FaceLandmarker — blendshape hướng mắt + góc nghiêng đầu | Liên tục nhìn ra khỏi màn hình (đọc phao, có người nhắc bài) |
multiple_faces |
Cùng face model, lọc theo kích thước khuôn mặt | Xuất hiện người thứ hai trong khung hình (trợ giúp) |
phone_detected |
MediaPipe ObjectDetector (EfficientDet-Lite0, lớp COCO cell phone) | Điện thoại lọt vào khung hình camera |
detection_unsupported |
Báo một lần khi trình duyệt thiếu API tương ứng | Để Inspector biết "không kiểm tra được", thay vì ngầm cho qua |
- Toàn bộ thị giác máy tính chạy ngay trên trình duyệt ứng viên bằng MediaPipe Tasks Vision (WASM + GPU delegate). Luồng video không được gửi về server để phân tích — chỉ các tín hiệu sự kiện rất nhẹ (
kind,severity,detail,ts) được truyền đi. - Chống dương tính giả nhiều lớp: cửa sổ ân hạn (grace window) bỏ qua nhiễu focus thoáng qua (popup hệ thống, chuyển thiết bị âm thanh); ngưỡng thời gian giữ liên tục trước khi tính vi phạm; lọc khuôn mặt nhỏ ở nền (poster/ảnh/phản chiếu); chỉ tính
phonekhi đó là nhãn trội của vật thể; ngưỡng gaze nới lỏng để việc đọc nội dung trên chính màn hình không bị nhầm thành nhìn ra ngoài. - Fire-and-forget: lỗi khi gửi tín hiệu được nuốt im lặng — proctoring không bao giờ làm gián đoạn buổi phỏng vấn.
- Tự lặp lại khi vi phạm còn tiếp diễn: trình duyệt báo lại định kỳ nếu hành vi chưa được sửa, để agent tiếp tục nhắc cho đến khi ứng viên khắc phục.
Backend nhận tín hiệu tại POST /interviews/{id}/proctor-event, ghi vào activity log (kèm IP + user-agent để truy vết), rồi phát vào phòng LiveKit dưới dạng bản tin proctor:violation. Với tín hiệu mức high, Interview Agent:
- Tạm tắt mic ứng viên và ngắt câu đang nói để tránh chồng tiếng.
- Phát cảnh báo soạn sẵn nguyên văn qua TTS — không qua LLM, nên cảnh báo bật ra tức thì, không có độ trễ sinh câu.
- Leo thang theo ba mức: nhắc nhở chính thức → cảnh báo lần hai → cảnh báo cuối; song ngữ Anh/Việt theo ngôn ngữ của phiên.
- Khôi phục mic và để câu trả lời kế tiếp của ứng viên điều khiển luồng hội thoại bình thường.
- Sau số vi phạm tối đa (
proctoring_max_violations, mặc định 5), agent đọc thông báo kết thúc và tự rời phòng, chấm dứt buổi phỏng vấn.
Cơ chế single-flight cùng thời gian chờ giữa hai lần nhắc (proctoring_cooldown_seconds) đảm bảo agent không bao giờ nói chồng hai cảnh báo lên nhau.
- Mọi vi phạm được lưu bền vào activity log như một audit trail đầy đủ cho HR.
- Khi kết thúc (
/end), toàn bộproctor_eventsđược nạp và truyền cho Inspector Agent, để yếu tố liêm chính được phản ánh trong báo cáo đánh giá cuối cùng — chứ không chỉ là cảnh báo thoáng qua trong lúc phỏng vấn.
Ngưỡng thời gian, độ nhạy, URL model và việc bật/tắt từng tín hiệu đều nằm trong config YAML, tinh chỉnh được mà không cần sửa code: nhóm proctoring trong configs/frontend-services.yml (phía phát hiện) và các khóa proctoring_* trong configs/ai-services.yml (phía interview-agent).
Sơ đồ dưới đây là phương án vận dụng tài nguyên GreenNode cho môi trường production: một VPC riêng, bốn AgentBase Runtime cho các service ứng dụng, hai VM chuyên trách realtime/logging và model inference, một VDB cho dữ liệu nghiệp vụ, và một VStorage cho file/bản ghi/report.
Phân bổ tài nguyên đề xuất:
| Tài nguyên GreenNode | Số lượng | Vai trò |
|---|---|---|
| VPC | 1 | Mạng riêng cho backend, agent runtimes, database, object storage, LiveKit và model APIs |
| AgentBase Runtime | 4 | frontend, backend, ai-services API, interview-agent worker |
| VM realtime/logging | 1 | Chạy LiveKit server và ClickHouse |
| VM GPU model API | 1 | Chạy các OpenAI-compatible APIs cho LLM, STT, TTS |
| VDB | 1 | PostgreSQL managed database cho user, interview, transcript, assignment và report metadata |
| VStorage | 1 | Object storage cho CV, recording, report PDF và artifacts |
GreenTemis được đóng gói để chạy native trên GreenNode AgentBase: toàn bộ lớp ứng dụng — web, API và 4 AI agent — là các Custom Agent Runtime độc lập. Mỗi runtime là một Docker image tự chứa, tuân đúng "hợp đồng runtime" của AgentBase (nghe cổng
8080+ cóGET /healthtrả200), push lên Container Registryvcr.vngcloud.vn. Phần realtime/stateful nằm trên VM, còn inference và lưu trữ tận dụng các dịch vụ managed cloud của GreenNode (MaaS + vStorage).
| Thành phần | Nền tảng | Hình thức | Vì sao đặt ở đây |
|---|---|---|---|
frontend (React/Vite) |
🟢 AgentBase Runtime — PUBLIC | 1 image riêng, nginx serve build | HTTP thuần, khớp runtime contract |
ai-services — 4 agent (Planning, Interview, Assignment, Inspector) |
🟢 AgentBase Runtime — PUBLIC | 1 image dùng chung, REST + MCP SSE | Stateless, scale theo tải |
backend (FastAPI) |
🟢 AgentBase Runtime — VPC | 1 image riêng | Cần gọi DB/LiveKit private an toàn trong VPC |
| LiveKit server | 🖥️ GreenNode vServer (VM) | Tự host | Cần UDP/WebRTC — không hợp runtime HTTP-only |
| PostgreSQL, ClickHouse | 🖥️ GreenNode vServer (VM) | Tự host | Có state, không bỏ vào runtime ephemeral |
interview-agent worker |
🖥️ GreenNode vServer (VM) | Dispatch vào LiveKit room | Worker non-HTTP, không hợp runtime contract |
| LLM / STT / TTS | ☁️ GreenNode MaaS (Cloud) | OpenAI-compatible endpoints | Model inference managed, container không cần GPU |
| Object storage (CV, recording, report) | ☁️ GreenNode vStorage (Cloud S3) | S3-compatible | Lưu trữ bền, tách khỏi runtime ephemeral |
- Mỗi service = 1 runtime, deploy độc lập — quy trình chuẩn
build → push → create runtime → chờ ACTIVE → lấy endpoint → test /health, không cầndocker-composeở production. - Hợp đồng runtime tối giản: container nghe cổng
8080và cóGET /healthtrả200— mọi service của GreenTemis đều tuân theo, nền tảng lo phần còn lại (TLS, scale, endpoint). - PUBLIC và VPC song song: web + agent chạy PUBLIC để truy cập internet;
backendchạy VPC để vào DB/LiveKit private mà không expose ra ngoài. - Bảo mật bằng IAM: mọi API gọi qua IAM Bearer token của service account, gắn policy least-privilege (
AgentBaseFullAccess,vcrFullAccess,AiPlatformFullAccess). - Hybrid đúng chỗ: phần stateless/HTTP lên AgentBase, phần realtime/stateful để VM, phần nặng inference & lưu trữ giao cho cloud managed (MaaS + vStorage) — giảm vận hành, dễ scale.
📘 Chi tiết quy trình deploy, lỗi đã gặp và bài học: docs/agentbase-deploy/
greentemis-agent/
├── frontend/ # React/Vite UI cho HR và candidate interview room
├── backend/ # FastAPI API, DB models, LiveKit dispatch, storage, report lifecycle
├── ai-services/ # REST AI agents, MCP tools, LiveKit voice interview worker
├── configs/ # Cấu hình YAML không chứa secret cho từng service
├── data/ # Runtime storage cục bộ khi dev
├── docs/ # Tài liệu thiết kế, JD/CV mẫu, hướng dẫn nghiệp vụ
├── scripts/ # Script tiện ích như reset DB
├── tests/ # Integration/load tests cấp repo
├── docker-compose.yml # Stack dev đầy đủ
└── .env.example # Template biến môi trường, không commit secret thật
README chi tiết cho từng service:
Sao chép template rồi điền secret thật:
cp .env.example .envCác giá trị không phải secret nằm trong configs/*.yml, gồm URL service, model name, port, giọng TTS, timeout và cấu hình proctoring. API key, database password, LiveKit secret, JWT secret và internal service key phải nằm trong .env, .env.dev, .env.prod hoặc biến môi trường của runtime.
Các nhóm biến quan trọng:
| Nhóm | Biến tiêu biểu |
|---|---|
| PostgreSQL | POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL |
| Auth | JWT_SECRET, INTERNAL_SERVICE_KEY |
| LiveKit | LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_PUBLIC_URL |
| Object storage | MINIO_ACCESS_KEY, MINIO_SECRET_KEY, MINIO_PUBLIC_ENDPOINT |
| ClickHouse | CLICKHOUSE_USER, CLICKHOUSE_PASSWORD, CLICKHOUSE_DB |
| AI providers | OPENAI_API_KEY, STT_API_KEY, KOKORO_API_KEY, STT_VI_API_KEY, TTS_VI_API_KEY |
Không đưa secret thật vào README, commit, issue hoặc log chia sẻ.
docker compose up -d --buildKiểm tra nhanh:
docker compose ps
curl http://localhost:8000/health
curl http://localhost:8000/api/v1/health
curl http://localhost:8001/healthMở ứng dụng tại:
- Frontend: http://localhost:5173
- Backend Swagger: http://localhost:8000/docs
- AI services health: http://localhost:8001/health
Tắt stack:
docker compose down| Service | Port mặc định | Ghi chú |
|---|---|---|
| Frontend | 5173 |
Nginx container expose app build; Vite dev cũng dùng port này |
| Backend | 8000 |
API prefix /api/v1, Swagger /docs |
| AI Services | 8001 |
REST agent APIs và MCP SSE |
| PostgreSQL | 5433 |
Host port map tới container 5432 |
| LiveKit WebSocket | 7880 |
Browser và worker join room |
| LiveKit HTTP API | 7881 |
Backend gọi LiveKit API |
| LiveKit UDP | 7882/udp |
WebRTC media |
| MinIO | 9000 |
S3-compatible API |
| MinIO Console | 9001 |
Console dev |
| ClickHouse HTTP | 8123 |
clickhouse-connect |
| ClickHouse TCP | 9009 |
Host map tới container 9000 |
Backend:
cd backend
python -m pytestAI services:
cd ai-services
python -m pytest test app/testFrontend:
npm --prefix frontend run lint
npm --prefix frontend run buildIntegration tests cấp repo:
python -m pytest testsdocker-compose.ymlchạyai-servicesvàinterview-agenttừ cùng Dockerfile;interview-agentdùng command riêngpython -m app.agents.interview.agent start.- STT, LLM và TTS được cấu hình như OpenAI-compatible remote APIs trong
configs/ai-services.yml; container local không cần GPU. - Backend vẫn boot nếu MinIO hoặc ClickHouse chưa sẵn sàng, nhưng các chức năng upload recording/report hoặc logging tương ứng sẽ suy giảm.
- Frontend dùng
/apiproxy trong dev/build container, nên browser không cần biết trực tiếp địa chỉ backend nội bộ Docker. - Khi thay đổi contract giữa backend và ai-services, cập nhật schema ở cả hai bên vì backend không import trực tiếp model từ
ai-services.