han-dong.link — spatial portfolio cockpit, grounded Q&A, and Role Fit agent
- TypeScript
- Astro
- React
- Three.js
- Spatial UI
- Provider-agnostic LLM
- LLM Tool-Calling
- Generative UI
- Guardrails
- Progressive Enhancement
Role Fit turns pasted or uploaded JDs into source-backed fit briefs for recruiters; the same han-dong.link system also pairs a live Q&A assistant for source-backed answers with a Three.js cockpit for profile, work, research, and CV.
At a glance
Role Sole builder — interactive cockpit UI, Three.js navigation, Q&A and Role Fit agent loops, source-backed answer cards, file/JD guardrails, deployment
Problem
A public portfolio has to serve recruiters, technical peers, and research collaborators without making them guess which evidence supports each claim. Static pages can list work, but they rarely answer role-specific questions or show how the site itself organizes evidence.
Solution
I built a spatial cockpit plus canonical read-mode pages, then connected both to the same Astro content collections that power Q&A and Role Fit. The portfolio can be browsed visually, read linearly, or queried through evidence-backed cards.
Impact
Live on han-dong.link · homepage cockpit entry with live portfolio counts · answer cards cite source pages · visible answer trace · Role Fit turns JD text or files into strengths, adjacent evidence, gaps, and questions
My contribution
- Spatial portfolio cockpit: a Three.js experience around the assistant, with four planets for profile, work, research, and CV, plus read-mode pages for reduced-motion, mobile, and no-WebGL contexts
- Five typed tools (getProjects / getPublications / getStack / getTimeline / getContact) the LLM chooses between per question, with a prose fallback
- Role Fit agent: a dedicated `/role-fit` page accepts pasted JD text or PDF/PNG/JPEG/WebP uploads, then returns an advocacy brief with strong matches, adjacent evidence, honest gaps, and recruiter questions — no percentages or hire/no-hire recommendations
- Source-linked generative UI: every tool result renders a React card (project cards, publication list, stack chips, timeline, contact) that links back to its canonical page
- Visible answer trace: the assistant surfaces the source-backed path from retrieved content to answer cards, so a visitor can see why a response was produced
- Content collections are the single source of truth — a new project flows into the agent with no retraining and no embeddings
- Honest persuasion: leads with strengths, acknowledges gaps in one brief clause, and never fabricates facts, titles, or numbers
Technical evidence
- Progressively enhanced Three.js shell: WebGL/reduced-motion/mobile checks, boot sequence, keyboard and wheel navigation, and a cockpit dashboard that shows live content counts from the same Astro content collections
- Agent loop: validate input → build a grounded system prompt from collections → LLM tool decision → dedup tool calls → resolve typed tools → render cards + prose
- Observable agent traces keep the loop inspectable: retrieved collection records, selected tool cards, and final prose remain visibly connected rather than disappearing into a chat-only response
- Guardrails: input validation (length cap, prompt-injection regex, refuse off-topic and prompt-reveal attempts) plus per-IP rate limiting
- Role Fit safeguards: JD-specific validation, third-party job-link refusal instead of crawling, PDF/image extraction, evidence allowlisting, numeric/credential claim guards, and site-evidence brief generation when full analysis is delayed
- Provider-agnostic LLM integration with a deterministic mock fallback, so dev / no-key runs work fully offline
- Server-rendered on Astro + Vercel; trilingual (EN / 繁中 / 简中); answers render through markdown
The agent loop
Each question runs: validate input → assemble a grounded system prompt from the content collections → ask the LLM which tools to call → dedup the tool calls → resolve the typed tools → render cards plus prose. The LLM decides what to say and which tools to use; the code never hard-codes question-to-answer routing.
The latest agent pass makes that path visible as an answer trace: the retrieved source records, chosen cards, and final prose stay connected, which is why the assistant can be inspected instead of merely trusted.
Role Fit — evidence-backed JD matching
Role Fit is a separate task workflow rather than another chat prompt. A recruiter can paste a job description or upload a PDF/screenshot image; the site extracts the JD text, checks it against public content collections, and returns a brief organized into strong matches, adjacent evidence, gaps, and questions to confirm.
The boundary is explicit: the site does not read third-party job-board URLs, does not store the JD in chat history, does not fabricate private details, and does not make percentage scores or hire/no-hire recommendations. The trace also marks when a result is assembled from site evidence only, so the boundary stays inspectable.
Grounded, not RAG
The content collections are the single source of truth. There are no embeddings and no vector store — a new project added to the site flows into the agent with zero retraining. The system prompt is explicit: use only the information provided, never invent facts, titles, or numbers; if a detail isn't there, say so and point to the contact options.
Guardrails and honest persuasion
Input is validated for length and screened against prompt-injection attempts; off-topic, role-play, and prompt-reveal requests are refused; requests are rate-limited per IP. Role Fit adds JD-specific validation, uploaded-file extraction limits, third-party URL refusal, evidence allowlisting, and numeric/credential guardrails. The prompt leads with strengths and names gaps honestly — persuasive, but it never fabricates.
Cockpit shell — spatial portfolio
The cockpit is the site's interactive front end, built as a full-window Three.js cockpit with four navigable planets: profile, work, research, and CV. Scroll wheel, keyboard, or click moves between the four planets; each planet opens cards drawn from the same Astro content collections that feed the assistant, so the 3D scene, dossier views, and answer cards stay aligned.
The dashboard surfaces live counts from those same collections. Reduced-motion, mobile, and no-WebGL contexts keep the canonical read-mode pages, so the spatial layer adds interaction while the underlying portfolio remains accessible and source-linked.
Screenshots