I built this to prove a thesis I keep coming back to: the best way to make an agentic product actually useful is to bring all of the relevant context to the agent (the data, the documents, the tools, the permissions) instead of making the user go find it. HR was a good place to test that idea because the pain is so concrete: the answer to almost any employee’s question already exists inside the company’s systems; it’s just buried behind a portal nobody wants to log into.
The immediate reason it exists, though, is a demo I built for an enterprise client. Their internal HR portal was miserable to use. It was the kind where employees would rather email HR than click through five screens to check their own leave balance, so they wanted to see what a natural-language interface on top of the same underlying HRIS would feel like. HR Agent is that demo, generalized.
What it actually is
A conversational HR assistant that connects directly into the client’s HR management system, on top of a lightweight HR dashboard for the admin side. Employees ask questions in plain language, such as “how many vacation days do I have left,” “what medical plan am I on,” or “file a case about my missing bonus,” and get the answer sourced from live HRIS data, not a stale export. The agent can request things on their behalf too: file a case, submit a leave request, ask a follow-up question in the same thread.
Managers get the same chat surface with a different set of tools underneath: team schedules, coverage analysis, pending approvals they can act on straight from the conversation. HR staff use it as a directory-and-cases console: search employees, pull org context, work through cases fast because the agent already has access to the data and can act, not just look. Managers can also upload the company’s HR policy documents, benefits guides, and handbooks so the agent can search over them when it needs to.
Permissions live in the agent, not around it
The harder problem here wasn’t the chat interface. It was making sure the assistant never answers with information or takes an action the person asking isn’t allowed to see or do. Role-based access control (employee, manager, HR staff) is enforced at the tool layer, so the agent literally doesn’t have a “look up any employee’s salary” tool available to it unless the caller’s role permits it. You can’t jailbreak your way past a tool that isn’t in the toolbox.
RAG that’s actually fast enough to use
The knowledge base search is the piece I spent the most time on. RAG is easy to build badly: an employee asks a policy question, the agent takes six seconds to think, then produces something that sounds like the handbook but subtly isn’t. For an HR product that’s disqualifying: if the answer is slower than opening the PDF, nobody uses it, and if it’s wrong they trust it even less.
So retrieval is heavily optimized for speed and citation. Documents get chunked and indexed into an OpenAI vector store on upload, semantic search runs against that, and the model is forced to answer with citations back to the exact source passage. The employee (or the HR admin reviewing behavior) can click through to the sentence in the handbook the answer came from. Wrong-but-confident is worse than “I couldn’t find it,” so the retrieval path is built to fail visibly rather than hallucinate over a miss.
The admin side: centralized control and observability
The other half of this product, and honestly the part that took the most careful backend work, is what admins get. RBAC and audit trails aren’t just about restricting what users see; they’re what makes it safe to hand HR staff a chat interface at all. Every conversation, every tool call, every case update, every document lookup is logged against a user identity, so an admin can go back and answer “who asked what, when, and what did the agent do about it” for any interaction on the system.
On top of that, admins get a dashboard that pulls all of it behind centralized control: full conversation histories across the org, per-user usage and cost accounting, document-upload management, role assignments, and credit/budget allocations. Because usage is metered per tool call with reservation-based accounting, an admin can see exactly what each user is spending, cap it, or filter out expensive tools when someone’s over budget, a real problem in any AI product where a chatty user can quietly rack up a serious bill.
Observability was a first-class concern the whole way through, not something bolted on at the end. LLM traces go to Langfuse so I (and the admin) can see the full model input/output, tool sequence, and latency for any conversation. That’s what you actually need when someone reports “the agent gave a weird answer” and you have to reconstruct why.
Cases with real workflow behind them
HR questions often turn into HR cases, something with an owner, an SLA, and an audit trail. The agent can open a case straight from a conversation and from there it’s a structured object: automatic categorization, SLA targets, hours-remaining tracking, escalation paths, full history preserved. Every tool call, case update, and lookup gets logged with user identity and timestamp because it has to. This is the kind of product where “who saw what, when” is the whole point of having governance.
Generative UI, before it was called that
The part of building this I liked the most was the generative UI. Instead of the agent writing prose back at you and stopping there, it emits JSON in a schema the frontend already knows about, and that JSON gets rendered straight into a live React component the user can actually interact with on the page: a leave-request approval card, a coverage view for a manager’s team, a case detail form. The model isn’t writing code, isn’t generating markup; it just produces a structured object in the right shape because it knows the output schema. The UI ends up feeling like it was assembled on the fly for the specific question you asked, which is exactly the effect I wanted.
This was pretty novel at the time I built it. Now the same idea shows up everywhere as “artifacts” or “generative UI” in every chat product, but the shape of it is identical: constrain the model to a schema, let the frontend render the schema, and you get bespoke interactive UI per turn without the model ever leaving the chat loop.
Stack
Fully web, fully full-stack TypeScript. Next.js 15 deployed on Vercel with a Postgres database. Vercel AI SDK for the agent loop, which is the piece I keep coming back to across projects. The best model changes every few weeks, and the AI SDK lets me swap providers (Anthropic, OpenAI, Google, xAI, open-source through the same gateway) behind one API without touching app code. That’s not a minor convenience; being able to move to whatever’s currently best without a rewrite is how the product stays current.
The rest of the stack: Better Auth for authentication and sessions, Drizzle ORM over Postgres, tRPC for the type-safe API surface, Redis for resumable streaming so a dropped connection doesn’t lose the response mid-flight. Retrieval runs on OpenAI vector stores; the model layer goes through Vercel’s AI Gateway. Observability through Langfuse. Documents live in Vercel Blob.
The result is a product an HR team can actually operate: upload the handbook, invite the org, and start answering questions. It’s also proof of the thesis I started with: agents get useful the moment you stop making the user carry the context to them.
