The declarative agent builder for LLMs — build any specialist (engineering, fintech, support, product, legal, clinical) from persona, frameworks, probes, red flags, and citations.
Project description
personakit
The declarative agent builder. Encode any specialist's expertise — persona, frameworks, probes, red flags, recommendation themes — as data. Get a production-grade LLM agent with structured output, citations, and safety triggers. Works for any domain: engineering, fintech, customer support, product, legal, clinical, education — one library, one pattern.
Created by Majidul Islam.
pip install personakit
Why personakit
LangChain is for wiring LLM calls. CrewAI is for orchestrating agent teams. LangGraph is for branching control flow. They're all engineer-facing composition frameworks.
personakit is for encoding specialist expertise — declaratively. A code
reviewer, a fintech compliance officer, a customer-support triage agent, a
scrum master — each is a single Specialist(...) object. No chain wiring. No
graph building. Domain experts can author specialists in YAML and hand them to
engineers.
The distinctive primitives:
| Concept | What it gives you |
|---|---|
| Specialist | Frozen dataclass — the entire agent definition, authorable in YAML |
| Framework | Body of knowledge with a citation key, cited in output |
| Probe | Diagnostic question; becomes a typed field in the structured response |
| RedFlag | Trigger → severity → action → citation, matched deterministically AND semantically |
| Theme | User-selectable recommendation category |
| Priority | Always-on checks reported as met / unmet / unknown |
| Tool (optional) | @tool decorator — opt-in for external memory, DB, APIs |
Core has just two runtime deps: pydantic and httpx.
Quickstart — any domain in 20 lines
import asyncio
from personakit import Agent, Specialist, Framework, Probe, RedFlag, Severity, Theme
product_manager = Specialist(
name="product_manager",
display_name="Senior Product Manager",
persona="You are a senior B2B SaaS PM. You sharpen feature specs and find edge cases.",
frameworks=[Framework(name="Jobs-to-be-Done"), Framework(name="RICE scoring")],
probes=[
Probe(question="What is the user's job-to-be-done?"),
Probe(question="What is the current workaround cost?", value_type="string"),
Probe(question="Is there a measurable success metric proposed?",
value_type="boolean", weight="high"),
],
red_flags=[
RedFlag(
trigger="No success metric defined",
severity=Severity.HIGH,
action="Block PRD review. Require a quantitative success metric before scoping.",
),
],
themes=[Theme(name="Refinements"), Theme(name="Edge cases"), Theme(name="Open questions")],
)
agent = Agent(specialist=product_manager, model="gpt-4o-mini")
async def main():
result = await agent.analyze(
"PRD: Add a 'dark mode' toggle to settings. Shipping next quarter."
)
print(result.pretty())
asyncio.run(main())
Bundled specialists across domains
from personakit.examples import (
CODE_REVIEWER, # engineering — PRs, OWASP, SOLID
CONTRACT_REVIEWER, # legal — M&A redlines, GDPR
CUSTOMER_SUPPORT_TRIAGE, # support — SaaS B2C, refund policy, escalation
FALLS_PREVENTION_NURSE, # clinical — NICE guidelines, post-fall protocol
FINTECH_TRANSACTION_REVIEWER, # finance — AML/fraud, OFAC, FATF typologies
MATH_TUTOR, # education — Socratic, minimal shape
SCRUM_MASTER, # delivery — sprint health, blockers, WIP limits
)
Each one is a template. Copy, edit, ship your own.
Real agent types you can build in one file
| You want an agent that... | Define these concepts |
|---|---|
| Reviews pull requests for security and correctness | Framework(OWASP), RedFlag(sql_injection, hardcoded_secret), Theme(Security, Performance) |
| Screens fintech transactions for AML / sanctions | Framework(BSA/AML, OFAC), RedFlag(sanctioned_counterparty, structuring), typology Themes |
| Triages customer support messages | Probe(order_id, sentiment), RedFlag(chargeback_language), Theme(Resolution, Escalation) |
| Coaches a sprint team as a scrum master | Framework(Scrum Guide, DORA), RedFlag(scope_creep, external_blocker), Theme(At-risk stories, Retro candidates) |
| Reviews M&A contracts for legal risk | Framework(English common law, UCC), RedFlag(unlimited_liability), Theme(Liability, IP) |
| Runs a post-fall clinical assessment | Framework(NICE NG161, NICE CG176), RedFlag(LOC, head_contact_on_anticoagulant), clinical probes |
| Writes product specs against JTBD | Framework(Jobs-to-be-Done, RICE), RedFlag(no_success_metric), Theme(Edge cases, Open questions) |
| Tutors a student without giving away answers | Theme(Concept check, Next hint, Common pitfall), Constraint(no direct answer first) |
| Does equity research on a public SaaS name | Framework(DCF, Rule of 40), RedFlag(NDR_below_100, negative_fcf_plus_decel), Theme(Thesis, Risks) |
YAML authoring — hand off to a domain expert
name: code_reviewer
persona: Senior staff engineer reviewing PRs. Correctness, security, perf, in that order.
frameworks: [OWASP Top 10, SOLID, 12-Factor App]
probes:
- question: Does the change include tests?
key: has_tests
value_type: boolean
weight: high
red_flags:
- trigger: Hard-coded secret or API key
severity: critical
action: BLOCK merge. Rotate the secret. Move to a secret manager.
match: both
patterns: ['sk-[A-Za-z0-9]{20,}', 'AKIA[0-9A-Z]{16}']
themes: [Correctness, Security, Performance, Maintainability, Tests]
from personakit import Specialist, Agent
spec = Specialist.from_yaml("code_reviewer.yaml")
agent = Agent(specialist=spec, model="claude-sonnet-4-6")
Red flags — the feature no-one else has
Every RedFlag is a trigger → severity → action → citation contract, matched in two phases:
- Deterministic pre-match — regex / keywords on raw input. Fast, offline, quotable.
- Semantic post-match — the LLM evaluates whether the trigger applies in context. Catches paraphrase, synonyms, implicit meaning.
Results merge, with deterministic evidence winning on ties:
RedFlag(
trigger="Hard-coded secret, token, or credential",
severity=Severity.CRITICAL,
action="BLOCK merge. Rotate secret. Move to secret manager.",
citation="OWASP A02:2021",
match=MatchMode.BOTH,
patterns=[r"sk-[A-Za-z0-9]{20,}", r"AKIA[0-9A-Z]{16}"],
)
Structured output — derived from the Specialist
You never write a JSON schema by hand. The probes, red flags, and themes are the schema:
result = await agent.analyze(input_text)
result.summary # narrative summary
result.probes_answered # {probe_key: typed_value_or_null}
result.probes_unanswered # list[Probe] — next-round questions
result.red_flags_triggered # list[TriggeredRedFlag] with evidence + source
result.recommendations # themed list with citations
result.citations_used # framework citation keys referenced
result.priorities_status # per-priority met / unmet / unknown
result.has_urgent # convenience boolean
Tools — opt-in, for external memory & APIs
Core has zero coupling to tool calling. When you want tools, decorate functions and attach:
from personakit.tools import tool
@tool
def lookup_order(order_id: str) -> dict:
"""Fetch an order from the order database."""
return order_db.get(order_id)
@tool
def search_knowledge_base(query: str, top_k: int = 5) -> list[str]:
"""Semantic search against the internal KB — your vector store."""
return kb.search(query, k=top_k)
support_agent = Agent(specialist=CUSTOMER_SUPPORT_TRIAGE, model="gpt-4o-mini")
support_agent = support_agent.with_tools([lookup_order, search_knowledge_base])
OpenAI and Anthropic tool-calling happen through the same interface. Schemas are auto-built from your function signatures and docstrings.
Registry — one app, many specialists
from personakit import SpecialistRegistry
registry = SpecialistRegistry.from_directory("personas/")
# Route by domain
engineering_agents = registry.by_domain("engineering")
finance_agents = registry.by_domain("finance")
# Route by name
support = registry.get("support_triage")
Providers
| Extra | Install | Default model |
|---|---|---|
personakit[openai] |
openai>=1.0 |
gpt-4o-mini |
personakit[anthropic] |
anthropic>=0.20 |
claude-sonnet-4-6 |
personakit[yaml] |
pyyaml>=6.0 |
— |
personakit[all] |
all of the above | — |
MockProvider ships in the core for offline testing — no API key needed:
from personakit.testing import MockProvider
provider = MockProvider(responses={"summary": "...", "recommendations": [...]})
Testing helpers
from personakit.testing import assert_triggered, assert_cited
result = await agent.analyze("Customer: 'I want to chargeback this transaction.'")
assert_triggered(result, "legal_or_chargeback_language_attorney_lawsuit_chargeback_small_claims_bbb")
Design principles
- Specialist is pure data. No behaviour, no side effects, serializable.
- Schema is derived. Probes, red flags, themes are the output contract.
- Deterministic where possible, semantic where needed. Red flags run both.
- Tools are opt-in. Core has zero coupling to tool calling.
- Minimal dependencies.
pydantic+httpx. Everything else is an extra. - Domain-neutral. Engineering, support, fintech, legal, clinical, education, delivery, product — one library.
- Provider-agnostic. Same Specialist, any model.
Status
Alpha — API may evolve. See CHANGELOG.md.
Author
Majidul Islam — @Majidul17068
personakit is an independent open-source project. Contributions welcome.
License
MIT © 2026 Majidul Islam.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file personakit-0.1.1.tar.gz.
File metadata
- Download URL: personakit-0.1.1.tar.gz
- Upload date:
- Size: 40.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0fd6b6adbd4b1eca50c8656e5883dbba0e8703d70882980a18d1904ec71cf37
|
|
| MD5 |
196dcee7dfbdb1fd5a28154a36fa6b8f
|
|
| BLAKE2b-256 |
77e71e0befef269c9bb4ee11246da770fad60f0004582ed8afd3abbff23d755c
|
File details
Details for the file personakit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: personakit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 47.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec21bd407679b2988e64d98d334dbbac1af3c3a8ad16ed76a73b195b87034f71
|
|
| MD5 |
2c4904de51b07c7b588ef16b4c7b6de1
|
|
| BLAKE2b-256 |
c62380a6e26fddefba368ea783b016ce96eec5eaaa1c550ff59a0c1335bb6729
|