Skip to content
Draft
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
Binary file added public/images/authors/jetchiang.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/app/blog/authors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export const authors: Author[] = [
photo: '/images/authors/linsun.jpg',
bio: 'Lin is the Head of Open Source at Solo.io, and a CNCF TOC member and ambassador. She has worked on the Istio service mesh since the beginning of the project in 2017 and serves on the Istio Technical Oversight Committee. Previously, she was a Senior Technical Staff Member and Master Inventor at IBM for 15+ years. She is the author of the book "Sidecar-less Istio Explained" and has more than 200 patents to her name.',
},
{
id: "jetchiang",
name: "Jet Chiang",
title: "Linux Foundation Mentee",
photo: "/images/authors/jetchiang.png",
bio: "Jet is an aspiring machine learning engineer + researcher studying at the University of Toronto. He is the Linux Foundation Mentee at Kagent for 2025.",
},
];

export const getAuthorById = (id: string): Author | undefined => {
Expand Down
8 changes: 8 additions & 0 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ function shortDate(date: string) {
}

const posts = [
{
slug: "crewai-byo-agent",
publishDate: "2025-10-14",
title: "From CrewAI to KAgent: Deploying Your Custom AI Agents",
description:
"Learn how to take your existing CrewAI agents and deploy them as Bring-Your-Own (BYO) agents in KAgent.",
authorId: "jetchiang",
},
{
slug: 'reactive-agents-khook',
publishDate: '2025-09-09',
Expand Down
277 changes: 277 additions & 0 deletions src/blogContent/crewai-byo-agent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
export const metadata = {
title: "From CrewAI to KAgent: Deploying Your Custom AI Agents",
publishDate: "2025-10-14T00:00:00Z",
description:
"Learn how to take your existing CrewAI agents and deploy them as Bring-Your-Own (BYO) agents in KAgent.",
author: "Jet Chiang",
authorIds: ["jetchiang"],
};

# From CrewAI to KAgent: Deploying Your Custom AI Agents

If you've been working with [CrewAI](https://www.crewai.com/) and love how it lets you orchestrate multiple AI agents for complex tasks, you might be wondering how to take those agents from your local development environment to a production-ready deployment.
That's where KAgent's Bring-Your-Own (BYO) agent feature comes in. With our latest support for CrewAI BYO agents, this process is now seamless and straightforward.

In this post, we'll walk through transforming your existing CrewAI crew into a KAgent BYO agent. We'll assume you already have a working CrewAI agent and focus on deployment steps.
If you don't have a CrewAI agent yet, consider following one of their [getting started guides](https://docs.crewai.com/getting-started) or cloning one of the [examples](https://github.com/crewAIInc/crewAI-examples).

## Why Deploy to KAgent?

Before we dive into the how, let's talk about the why. KAgent provides several advantages over running CrewAI agents locally:

- **Enterprise-grade deployment** with Kubernetes
- **Session-aware memory** that persists across conversations
- **Built-in tracing** with OpenTelemetry
- **A2A protocol compatibility** for agent-to-agent communication
- **Web dashboard** for easy management and testing
- **Production monitoring** and scaling capabilities

Furthermore, your agent will be able to interact with other agents and MCP servers already deployed in your KAgent environment, enabling more complex workflows and integrations.

## Prerequisites

Make sure you have:

- A working CrewAI agent
- KAgent installed in your Kubernetes cluster ([quick start guide](/docs/kagent/getting-started/quickstart))
- Docker for building container images
- kubectl for Kubernetes operations
- Tracing configured if desired, see the [tracing guide](/docs/kagent/getting-started/tracing)

## Step 0: Test Your Agent Locally

Ensure that you have a CrewAI agent ready and tested locally, for example using `crewai run, crewai flow kickoff`. Check what input your agent requires since that will be needed to kickoff the agent from Kagent.

If you want to follow the example below but don't have a CrewAI agent, a simple one to start is the `self_evaluation_loop_flow` agent from the CrewAI examples repository.
This example shows a flow with 2 agents that generates X posts in Shakespearean style and then evaluates them to iteratively improve the quality. It doesn't require any tool API keys.

```bash
git clone https://github.com/crewAIInc/crewAI-examples.git
cd crewAI-examples/self_evaluation_loop_flow
# setup your OpenAI API key in .env or export OPENAI_API_KEY=...
crewai flow kickoff
```

We will use this example to illustrate some steps below, but you can apply the same principles to your own CrewAI agents.

## Step 1: Prepare Your Agent for A2A

Most often when running CrewAI locally, you would directly pass user inputs to `kickoff()` by hard coding some values required by your task or agent definition. This is a great way to test locally as CrewAI tutorials suggest, but in production you will want to accept user inputs dynamically through A2A protocol.

User inputs come from an A2A task and will be passed in under the `input` key when invoking the crew or flow with `kickoff()`. For flows, the best practice is to manage the input by storing it in the flow state and then using it to kickoff the crew or functions. Here's our example:

```python
class ShakespeareXPostFlowState(BaseModel):
# the user provided inputs from A2A task will be used to initialize this "input" field in the state
input: str = "Flying cars"
... # other state fields such as feedback, iteration count, etc.


class ShakespeareXPostFlow(Flow[ShakespeareXPostFlowState]):
@start("retry")
def generate_shakespeare_x_post(self):
print("Generating Shakespearean X post")
result = (
ShakespeareanXPostCrew()
.crew()
.kickoff(
# use the input from the flow state
inputs={"topic": self.state.input, "feedback": self.state.feedback}
)
)

... # Other flow functions such as evaluate_posts(), routing logics, etc.
```

For crews, you can directly add the `{input}` template variable to the task definition, for example:

```yaml
write_x_post:
description: >
Given the topic '{input}', compose a humorous hot take in the style of Shakespeare. ...
expected_output: >
A witty, Shakespearean hot take between 200 and 280 characters ...
agent: shakespearean_bard
```

Then, write the A2A agent card which is a JSON file that describes your agent's capabilities, inputs, and outputs. This is exposed at the `/.well-known/agent.json` endpoint and used by KAgent to understand how to interact with your agent. Here's our example:

```json
{
"name": "self-evaluation-loop-flow",
"description": "A CrewAI flow that generates and iteratively improves Shakespearean-style posts.",
"version": "1.0.0",
"capabilities": {
"streaming": true
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text"],
"skills": [
{
"id": "generate_post",
"name": "Generate a post",
"description": "Can generate a post by providing a topic as input",
"tags": ["text generation", "Shakespearean"]
}
],
"url": "http://localhost:8080"
}
```

## Step 2: Setting Up the KAgent A2A Server

Now you can wrap your crew with KAgent's integration layer, usually in `main.py` created by CrewAI.

```python
# main.py
from kagent.crewai import KAgentApp
from your_crew import YourCrew # Import your existing crew, similarly for flow

# Your flow / crew definition if any
# Other CrewAI functions such as kickoff() and plot()

def main():
# Either write the agent card inline or load from a JSON file
with open("agent-card.json", "r") as f:
agent_card = json.load(f)

app = KAgentApp(crew=YourCrew(), agent_card=agent_card)
# app = KAgentApp(flow=YourFlow(), agent_card=agent_card) # if using a flow instead

server = app.build()

port = int(os.getenv("PORT", "8080"))
host = os.getenv("HOST", "0.0.0.0")
logger.info(f"Starting server on {host}:{port}")

uvicorn.run(
server,
host=host,
port=port,
log_level="info",
)

if __name__ == "__main__":
main()
```

## Step 3: Containerizing Your Agent

Create a `Dockerfile` in your project root:

```dockerfile
### 1. Choose a base image, here we use python 3.13 with uv
ARG DOCKER_REGISTRY=ghcr.io
ARG VERSION=latest
FROM ghcr.io/astral-sh/uv:python3.13-trixie-slim

WORKDIR /app

# Install system dependencies, git is required for installing from git
RUN apt-get update && apt-get install -y \
build-essential \
git \
&& rm -rf /var/lib/apt/lists/*

COPY ./ .

# Install dependencies
RUN uv venv && uv sync --locked --no-dev
# install kagent-crewai package from kagent-dev GitHub repository
RUN uv pip install git+https://github.com/kagent-dev/kagent.git#subdirectory=python/packages/kagent-crewai

# Set environment variables
ENV PORT=8080
ENV VIRTUAL_ENV=/app/.venv
ENV PATH="/app/.venv/bin:$PATH"

# Expose port
EXPOSE 8080

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1

# Run the application
CMD ["python", "path/to/your/main.py"]
```

If needed create a `.dockerignore` file to exclude unnecessary files from the Docker build context. Then build and push your image:

```bash
docker build -t your-registry.com/your-crew:latest . --push
```

## Step 4: Setting Up Secrets

Your CrewAI agent likely needs API keys. For our example, we only need OpenAI API key.

```bash
# Set your API keys
export OPENAI_API_KEY="your-openai-key"

# Create secrets
kubectl create secret generic kagent-openai \
--from-literal=OPENAI_API_KEY=$OPENAI_API_KEY \
-n kagent
```

Here is where you can add other secrets or credentials for tools and external integrations.

## Step 5: Creating the BYO Agent Resource

Now create the KAgent BYO agent resource:

```yaml
apiVersion: kagent.dev/v1alpha2
kind: Agent
metadata:
name: my-crew-name
namespace: kagent
spec:
description: Some description
type: BYO
byo:
deployment:
image: your-registry.com/your-crew:latest
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: kagent-openai
key: OPENAI_API_KEY
```

Apply it:

```bash
kubectl apply -f agent.yaml
```

## Step 6: Testing Your Deployed Agent

Let's test that everything works:

```bash
# Port forward to access the agent
kubectl port-forward svc/kagent-controller 8083:8083 -n kagent

# Check the agent card
curl localhost:8083/api/a2a/kagent/my-crew-name/.well-known/agent.json
```

You should see your agent's metadata. Now try invoking it through the KAgent CLI:

```bash
kagent invoke --agent my-crew-name --task "Research the latest developments in quantum computing"
```

You can also access the agent through the dashboard UI or the CLI (reference the quickstart tutorial if needed).

## Note on Session-aware Memory

If your CrewAI agent needs to maintain state or memory across sessions, you can leverage KAgent's session-aware memory feature. **No additional change is needed at all in your CrewAI code!** This means if you have already enabled `memory=True` or `@persist` on your Crew or Flow, they will automatically use the Kagent backend! You don't need to define any storage interface or connect to any database, `KAgentApp` handles that!

## Conclusion

Congratulations! You've successfully transformed your CrewAI agent into a production-ready KAgent BYO agent. You can now take advantage of KAgent's robust features to manage, scale, and monitor your AI agents in a Kubernetes environment.
10 changes: 9 additions & 1 deletion src/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,15 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
{children}
</em>
),
code: ({ children, ...props }) => <CodeBlock {...props}>{children}</CodeBlock>,
code: ({ children, className = "", ...props }) => {
// Extract language from className if present
const language = className?.replace("language-", "") || "";
return (
<CodeBlock language={language} className={className} {...props}>
{children}
</CodeBlock>
);
},
// Block elements
blockquote: ({ children, ...props }) => <blockquote {...props}>{children}</blockquote>,
hr: (props) => <hr className="my-8 border-muted" {...props} />,
Expand Down