Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions crews/kalibr-resilient-crew/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# OpenAI — primary model
OPENAI_API_KEY=your_openai_api_key_here

# Anthropic — fallback model (Kalibr routes here if GPT-4o degrades)
ANTHROPIC_API_KEY=your_anthropic_api_key_here

# Serper — web search tool
SERPER_API_KEY=your_serper_api_key_here

# Kalibr — execution path routing
# Get credentials: https://dashboard.kalibr.systems or run `kalibr auth`
KALIBR_API_KEY=your_kalibr_api_key_here
KALIBR_TENANT_ID=your_kalibr_tenant_id_here
50 changes: 50 additions & 0 deletions crews/kalibr-resilient-crew/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Kalibr-Resilient Job Posting Crew

A production-grade CrewAI crew with automatic LLM routing and failure recovery via [Kalibr](https://kalibr.systems).

This example extends the standard job-posting crew with Kalibr's execution path router. The crew researches company culture, identifies role requirements, drafts a job posting, and reviews it — all while Kalibr monitors outcome signals and routes each LLM call to the model most likely to succeed.

## Why Kalibr

Most CrewAI examples are hardcoded to GPT-4o. That's fine for demos. In production:

- Provider degradation events (rate limits, latency spikes, partial outages) happen multiple times per week
- A single-model crew fails when that model degrades
- Kalibr routes to your backup model automatically, learned from live outcome data

**Benchmark results during a simulated GPT-4o degradation event:**
| Setup | Task Success Rate |
|---|---|
| Hardcoded GPT-4o | 16–36% |
| Kalibr-routed (GPT-4o + Claude Sonnet) | 88–100% |

## Running the Script

**Configure Environment**: Copy `.env.example` to `.env` and fill in:
- `OPENAI_API_KEY` — [OpenAI API key](https://platform.openai.com/api-keys)
- `ANTHROPIC_API_KEY` — [Anthropic API key](https://console.anthropic.com/) (fallback model)
- `SERPER_API_KEY` — [Serper](https://serper.dev) for web search
- `KALIBR_API_KEY` and `KALIBR_TENANT_ID` — [Kalibr dashboard](https://dashboard.kalibr.systems)

**Install Dependencies**: `uv sync`

**Execute**: `uv run kalibr_resilient_crew`

## How Kalibr works here

`import kalibr` at the top of `main.py` (before any OpenAI/Anthropic import) monkey-patches the SDK clients. From that point on, every LLM call the crew makes is:
1. Traced with task goal context
2. Routed to the currently-succeeding model
3. Scored on outcome (did the agent produce valid output?)
4. Used to improve future routing decisions

No changes to agent definitions, task configs, or crew logic.

## Get Kalibr credentials

```bash
pip install kalibr
kalibr auth # device-code auth — opens browser, one click
```

Or sign up at [dashboard.kalibr.systems](https://dashboard.kalibr.systems).
16 changes: 16 additions & 0 deletions crews/kalibr-resilient-crew/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[project]
name = "kalibr_resilient_crew"
version = "0.1.0"
description = "Production-resilient job posting crew with Kalibr execution path routing"
authors = [{name = "Devon Kelley", email = "devon@kalibr.systems"}]
readme = "README.md"
requires-python = ">=3.12,<=3.13"
dependencies = [
"crewai[tools]>=0.152.0",
"python-dotenv>=1.0.1",
"kalibr>=0.1.0",
]

[project.scripts]
kalibr_resilient_crew = "kalibr_resilient_crew.main:run"
train = "kalibr_resilient_crew.main:train"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Kalibr MUST be imported before any OpenAI/Anthropic/LLM imports.
# This file is intentionally minimal — see main.py for entry point.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
research_agent:
role: >
Research Analyst
goal: >
Analyze the company website and provided description to extract
insights on culture, values, and specific needs.
backstory: >
Expert in analyzing company cultures and identifying key values
and needs from various sources, including websites and brief descriptions.

writer_agent:
role: >
Job Description Writer
goal: >
Use insights from the Research Analyst to create a detailed,
engaging, and enticing job posting.
backstory: >
Skilled in crafting compelling job descriptions that resonate
with the company's values and attract the right candidates.

review_agent:
role: >
Review and Editing Specialist
goal: >
Review the job posting for clarity, engagement, grammatical accuracy,
and alignment with company values and refine it to ensure perfection.
backstory: >
A meticulous editor with an eye for detail, ensuring every piece of content
is clear, engaging, and grammatically perfect.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
research_company_culture_task:
description: >
Analyze the provided company website {company_domain} and the following
company description: {company_description}.
Focus on identifying the company's culture, values, and mission.
expected_output: >
A comprehensive summary of the company's culture and values.

research_role_requirements_task:
description: >
Based on the hiring needs: {hiring_needs}, identify the skills,
experience, and qualities required for the role.
expected_output: >
A structured JSON with skills, experience, and qualities lists.

draft_job_posting_task:
description: >
Draft a job posting for {hiring_needs} at a company with the following
description: {company_description}.
Highlight the specific benefits: {specific_benefits}.
expected_output: >
A detailed, engaging job posting ready for publication.

review_and_edit_job_posting_task:
description: >
Review the job posting for clarity, engagement, grammatical accuracy,
and alignment with company values.
expected_output: >
A polished, final job posting ready for immediate publication.

industry_analysis_task:
description: >
Conduct an industry analysis for the role: {hiring_needs}.
Identify salary ranges, required certifications, and market trends.
expected_output: >
A comprehensive industry analysis with salary benchmarks and market insights.
69 changes: 69 additions & 0 deletions crews/kalibr-resilient-crew/src/kalibr_resilient_crew/crew.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from typing import List

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import SerperDevTool, WebsiteSearchTool, FileReadTool
from pydantic import BaseModel, Field
from kalibr.integrations.crewai import KalibrCrewAIInstrumentor

import os

KalibrCrewAIInstrumentor(
tenant_id=os.environ.get("KALIBR_TENANT_ID", ""),
goal="job_posting_production",
).instrument()

web_search_tool = WebsiteSearchTool()
serper_dev_tool = SerperDevTool()
file_read_tool = FileReadTool(
file_path='job_description_example.md',
description='A tool to read the job description example file.',
)


class ResearchRoleRequirements(BaseModel):
skills: List[str] = Field(..., description="List of recommended skills for the ideal candidate.")
experience: List[str] = Field(..., description="List of recommended experience for the ideal candidate.")
qualities: List[str] = Field(..., description="List of recommended qualities for the ideal candidate.")


@CrewBase
class KalibrResilientJobPostingCrew:
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'

@agent
def research_agent(self) -> Agent:
return Agent(config=self.agents_config['research_agent'], tools=[web_search_tool, serper_dev_tool], verbose=True)

@agent
def writer_agent(self) -> Agent:
return Agent(config=self.agents_config['writer_agent'], tools=[web_search_tool, serper_dev_tool, file_read_tool], verbose=True)

@agent
def review_agent(self) -> Agent:
return Agent(config=self.agents_config['review_agent'], tools=[web_search_tool, serper_dev_tool, file_read_tool], verbose=True)

@task
def research_company_culture_task(self) -> Task:
return Task(config=self.tasks_config['research_company_culture_task'], agent=self.research_agent())

@task
def research_role_requirements_task(self) -> Task:
return Task(config=self.tasks_config['research_role_requirements_task'], agent=self.research_agent(), output_json=ResearchRoleRequirements)

@task
def draft_job_posting_task(self) -> Task:
return Task(config=self.tasks_config['draft_job_posting_task'], agent=self.writer_agent())

@task
def review_and_edit_job_posting_task(self) -> Task:
return Task(config=self.tasks_config['review_and_edit_job_posting_task'], agent=self.review_agent())

@task
def industry_analysis_task(self) -> Task:
return Task(config=self.tasks_config['industry_analysis_task'], agent=self.research_agent())

@crew
def crew(self) -> Crew:
return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential, verbose=True)
51 changes: 51 additions & 0 deletions crews/kalibr-resilient-crew/src/kalibr_resilient_crew/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# IMPORTANT: kalibr must be the first import.
# It monkey-patches OpenAI and Anthropic SDK clients so that every
# LLM call in this process is automatically traced, routed, and scored.
import kalibr # noqa: F401 — must be first

import sys
from dotenv import load_dotenv
from kalibr_resilient_crew.crew import KalibrResilientJobPostingCrew

load_dotenv()


def run():
"""
Run the Kalibr-resilient job posting crew.

Kalibr automatically routes each LLM call to the optimal model based on
live outcome signals. If GPT-4o degrades, calls route to Claude Sonnet.
No code changes needed — routing adapts in real time.
"""
inputs = {
'company_domain': 'careers.wbd.com',
'company_description': (
"Warner Bros. Discovery is a premier global media and entertainment company, "
"offering audiences the world's most differentiated and complete portfolio of "
"content, brands and franchises across television, film, sports, news, "
"streaming and gaming."
),
'hiring_needs': 'Production Assistant, for a TV production set in Los Angeles in June 2025',
'specific_benefits': 'Weekly Pay, Employee Meals, healthcare',
}
KalibrResilientJobPostingCrew().crew().kickoff(inputs=inputs)


def train():
"""Train the crew for a given number of iterations."""
inputs = {
'company_domain': 'careers.wbd.com',
'company_description': (
"Warner Bros. Discovery is a premier global media and entertainment company."
),
'hiring_needs': 'Production Assistant, for a TV production set in Los Angeles',
'specific_benefits': 'Weekly Pay, Employee Meals, healthcare',
}
try:
KalibrResilientJobPostingCrew().crew().train(
n_iterations=int(sys.argv[1]),
inputs=inputs,
)
except Exception as e:
raise Exception(f"An error occurred while training the crew: {e}")