Skip to content
Learn Agentic AI14 min read0 views

Building an HR FAQ Agent: Policy Questions, Benefits Inquiries, and PTO Management

Create an AI agent that answers HR policy questions, looks up benefits details, checks PTO balances, and submits time-off requests — reducing the burden on HR teams while giving employees instant answers.

Why HR Teams Need an FAQ Agent

HR departments spend a disproportionate amount of time answering the same questions: "How many PTO days do I have left?", "When is open enrollment?", "What is the parental leave policy?" These questions have definitive answers that do not require human judgment — making them ideal for an agentic solution. By offloading repetitive inquiries, HR professionals can focus on strategic work like culture initiatives, conflict resolution, and organizational development.

The critical design decision is separating read-only queries (policy lookups, balance checks) from write operations (PTO requests, benefits changes) with appropriate authorization checks.

Policy Knowledge Base

Rather than embedding policy text directly into the agent's instructions, we store policies in a structured database that can be updated independently. This ensures the agent always references the current version.

from dataclasses import dataclass
from datetime import date, timedelta
from typing import Optional
from agents import Agent, Runner, function_tool
import json

@dataclass
class PolicyDocument:
    policy_id: str
    title: str
    category: str
    content: str
    effective_date: date
    last_updated: date

POLICY_DATABASE: dict[str, PolicyDocument] = {
    "pto-001": PolicyDocument(
        policy_id="pto-001",
        title="Paid Time Off Policy",
        category="time_off",
        content="""Employees accrue PTO based on tenure:
- 0-2 years: 15 days/year (1.25 days/month)
- 3-5 years: 20 days/year (1.67 days/month)
- 6+ years: 25 days/year (2.08 days/month)
PTO requests must be submitted at least 5 business days in advance.
Manager approval is required for requests exceeding 3 consecutive days.
Unused PTO carries over up to 5 days into the next calendar year.""",
        effective_date=date(2026, 1, 1),
        last_updated=date(2026, 1, 15),
    ),
    "benefits-001": PolicyDocument(
        policy_id="benefits-001",
        title="Health Benefits Overview",
        category="benefits",
        content="""Three plan tiers available: Bronze, Silver, Gold.
Open enrollment runs November 1-30 each year.
New hires can enroll within 30 days of start date.
Life changes (marriage, birth) trigger a special enrollment window.""",
        effective_date=date(2026, 1, 1),
        last_updated=date(2026, 2, 1),
    ),
}

@function_tool
def search_policies(query: str, category: str = "") -> str:
    """Search HR policies by keyword and optional category."""
    results = []
    query_lower = query.lower()
    for policy in POLICY_DATABASE.values():
        if category and policy.category != category:
            continue
        if (query_lower in policy.title.lower()
                or query_lower in policy.content.lower()):
            results.append({
                "policy_id": policy.policy_id,
                "title": policy.title,
                "category": policy.category,
                "content": policy.content,
                "last_updated": str(policy.last_updated),
            })
    if not results:
        return json.dumps({"message": "No matching policies found. "
                           "Please contact HR for assistance."})
    return json.dumps(results)

PTO Balance and Request Tools

The PTO system integrates with employee records to show accrued, used, and available balances. The request tool validates dates and submits for approval.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

@dataclass
class PTORecord:
    employee_id: str
    accrued: float
    used: float
    pending: float
    carry_over: float

    @property
    def available(self) -> float:
        return self.accrued + self.carry_over - self.used - self.pending

PTO_RECORDS: dict[str, PTORecord] = {}

@function_tool
def get_pto_balance(employee_id: str) -> str:
    """Get current PTO balance for an employee."""
    record = PTO_RECORDS.get(employee_id)
    if not record:
        return json.dumps({"error": "Employee PTO record not found"})

    return json.dumps({
        "accrued_this_year": record.accrued,
        "carried_over": record.carry_over,
        "used": record.used,
        "pending_approval": record.pending,
        "available": record.available,
    })

@function_tool
def submit_pto_request(
    employee_id: str,
    start_date: str,
    end_date: str,
    reason: str = "",
) -> str:
    """Submit a PTO request for approval."""
    record = PTO_RECORDS.get(employee_id)
    if not record:
        return json.dumps({"error": "Employee not found"})

    start = date.fromisoformat(start_date)
    end = date.fromisoformat(end_date)
    days_requested = (end - start).days + 1

    # Validate advance notice
    if (start - date.today()).days < 5:
        return json.dumps({
            "status": "rejected",
            "reason": "PTO requests require 5 business days advance notice.",
        })

    # Validate sufficient balance
    if days_requested > record.available:
        return json.dumps({
            "status": "rejected",
            "reason": f"Insufficient balance. Requested {days_requested} days "
                      f"but only {record.available} available.",
        })

    record.pending += days_requested
    needs_manager = days_requested > 3
    return json.dumps({
        "status": "submitted",
        "days": days_requested,
        "requires_manager_approval": needs_manager,
        "estimated_response": "1-2 business days",
    })

Benefits Lookup Tool

@dataclass
class BenefitsEnrollment:
    employee_id: str
    plan_tier: str
    dependents: int
    monthly_premium: float
    hsa_balance: float
    next_open_enrollment: date

BENEFITS_DB: dict[str, BenefitsEnrollment] = {}

@function_tool
def get_benefits_summary(employee_id: str) -> str:
    """Retrieve current benefits enrollment summary."""
    enrollment = BENEFITS_DB.get(employee_id)
    if not enrollment:
        return json.dumps({"error": "No benefits enrollment found"})

    return json.dumps({
        "plan": enrollment.plan_tier,
        "dependents_covered": enrollment.dependents,
        "monthly_premium": f"${enrollment.monthly_premium:.2f}",
        "hsa_balance": f"${enrollment.hsa_balance:.2f}",
        "next_open_enrollment": str(enrollment.next_open_enrollment),
    })

Assembling the HR FAQ Agent

hr_faq_agent = Agent(
    name="HRBot",
    instructions="""You are HRBot, an HR self-service assistant.
Answer employee questions about policies, benefits, and PTO.
Always cite the specific policy when answering policy questions.
For PTO requests, confirm the dates and check the balance before submitting.
Never share one employee's information with another employee.
If a question requires human judgment, direct the employee to their HR Business Partner.""",
    tools=[search_policies, get_pto_balance, submit_pto_request, get_benefits_summary],
)

FAQ

How do you ensure the agent gives accurate policy answers?

The agent retrieves policy text from a versioned database rather than relying on its training data. Each policy document includes an effective date and last-updated timestamp. When policies change, you update the database — the agent immediately reflects the new information without retraining.

What if an employee asks something the agent cannot answer?

The agent is instructed to recognize its boundaries. If no matching policy is found or the question involves subjective judgment (workplace conflicts, accommodation requests), it escalates to the appropriate HR representative with context about what the employee was asking.

How do you handle PTO requests that span holidays?

Add a company holiday calendar to the data layer. The PTO calculation tool subtracts company holidays from the requested range before computing the days charged, ensuring employees are not double-penalized for days the office is already closed.


#HRFAQ #PTOManagement #Benefits #EmployeeSelfService #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.