PolicyKit

PolicyKit product preview

FastAPI, PostgreSQL, Next.js, OpenAI
A policy-driven review agent for consistent, compliant job postings

Overview

PolicyKit is a pre-publication review agent for recruiting teams. It checks each job description against shared company standards and local hiring requirements before it goes live.

Those policies can cover legal obligations, required content, terminology, and structure so every team and role present a clear, consistent picture of your company to candidates.

PolicyKit can identify problems, explains the relevant policy, and proposes edits. The recruiter can accept, reject, or replace each suggestion, and run another review until the posting is ready to publish.

The review flow

A recruiter starts with a job description and some additional details such as hiring locations and employment type. PolicyKit uses this information to select applicable local requirements, then reviews the posting against both those requirements and company-wide publishing standards.

If the agent finds a concern it:

  • Highlights the text connected to each finding
  • Explains the policy and why the posting does not meet it
  • Proposes an edit when it has enough information, or asks the recruiter for more details
  • Lets the recruiter accept, reject, or replace the proposed edit
  • Checks the revised posting again

The recruiter can publish a posting once PolicyKit clears it. If there are unresolved findings, they can override the agent and publish the posting with a justification (which we store in our database for potential future reinforcement learning)!

Here's what the lifecycle of a review session looks like:

PolicyKit session lifecycle

The Admin flow

Administrators can manage company-wide publishing standards and local requirements as policies here

1. Define the rule

An administrator creates a policy with rules and may provide examples of compliant postings and violations. They can also choose to either apply the policy to every posting or limit it by jurisdiction and employment type.

Here we are adding a company-wide policy that ensures all job-postings mention the company's core values:

Creating a company-wide policy rule in PolicyKit

Adding scope, rationale, remediation, and examples to a PolicyKit policy

2. Test it against a posting

Before publishing a policy, the administrator can test it against a sample job posting.

Here we are testing a New York compensation policy that ensures all job postings for roles in New York have a clear compensation range stated:

Testing a New York compensation policy against a sample job posting

3. Publish a version

Published policies become part of the policy set used in future reviews.

System architecture

There's three main parts to the system:

  1. The Next.js web app is the frontend for the recruiter review flow and policy administration
  2. The FastAPI service is our server which handles requests and interacts with our database (PostgreSQL).
  3. The Python agent worker(s) claim queued sessions and run the reviews

When a recruiter submits a posting, our frontend makes a request to our server which creates a new session marked as queued in the database. Workers poll the database every second and when they find a queued session, they mark it as investigating and start the agent runtime.

There are two model roles:

  1. The orchestrator model sees the current state of the session and available tools. It chooses one action at a time.
  2. The classifier LLM is invoked by the run_compliance_check tool call. For every batch of applicable policies (4 by default), the system makes a classifier LLM call with those policies supplied, and asked to return an assessment per policy.

Importantly, the orchestrator agent is driven by the available tools we provide it. (The classifiers have no access to tools)

Agent tools

The orchestrator receives a subset of these tools, depending on the current state:

ToolPurpose
ask_recruiterAsks the recruiter for more information
set_hiring_locationsSaves a location supplied by the recruiter
run_compliance_checkSpawns classifier agents (one per batch of 4 policies by default) to check every applicable policy
read_policyLoads a specific policy from the database
propose_revisionDeclares an edit to the draft
finish_with_findingsEnds the review with findings for the recruiter
complete_sessionAvailable when no violations remain; Python then runs validate_publishable

The runtime rejects multiple tool calls in one turn, overlapping edits, edits tied to the wrong finding, and changes outside the declared edit set.

PolicyKit system architecture

Deployment and scaling

PolicyKit can run on one machine as separate processes:

  1. the Next.js app
  2. the FastAPI service
  3. the Python worker(s)
  4. the PostgreSQL Database

For a small deployment, the worker can also run inside the API process.

We currently handle concurrency with workers polling and claiming queued sessions with a row lock. This prevents two workers from taking the same enqueued session.

However, as this scales, and we support many worker processes, it will make sense to use a dedicated message queue instead!