new brnach
151
docs/agents.md
|
|
@ -1,44 +1,57 @@
|
|||
# Agents
|
||||
|
||||
Agents are the core building block in your apps. An agent is a large language model (LLM), configured with instructions and tools.
|
||||
Agents are the core of CAI. An agent uses Large Language Models (LLMs), configured with instructions and tools.
|
||||
Each agent is defined in its own `.py` file in `src/cai/agents`.
|
||||
|
||||
## Basic configuration
|
||||
|
||||
The most common properties of an agent you'll configure are:
|
||||
Key agent properties include:
|
||||
|
||||
- `instructions`: also known as a developer message or system prompt.
|
||||
- `model`: which LLM to use, and optional `model_settings` to configure model tuning parameters like temperature, top_p, etc.
|
||||
- `name`: of the agent e.g. the name of `one_tool_agent` is 'CTF Agent'.
|
||||
- `instructions`: known as the system prompt.
|
||||
- `model`: which LLM to use, and optional `model_settings` to configure their parameters like temperature, top_p, etc.
|
||||
- `tools`: Tools that the agent can use to achieve its tasks.
|
||||
- `handoffs`: wich allows an agent to delegate tasks to another agent.
|
||||
|
||||
|
||||
## Example: `one_tool_agent.py`
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, ModelSettings, function_tool
|
||||
from cai.sdk.agents import Agent, OpenAIChatCompletionsModel
|
||||
from cai.tools.reconnaissance.generic_linux_command import generic_linux_command
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
return f"The weather in {city} is sunny"
|
||||
|
||||
agent = Agent(
|
||||
name="Haiku agent",
|
||||
instructions="Always respond in haiku form",
|
||||
model="o3-mini",
|
||||
tools=[get_weather],
|
||||
one_tool_agent = Agent(
|
||||
name="CTF agent",
|
||||
description="Agent focused on conquering security challenges using generic linux commands",
|
||||
instructions="You are a Cybersecurity expert Leader facing a CTF challenge.",
|
||||
tools=[
|
||||
generic_linux_command,
|
||||
],
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model="qwen2.5:14b",
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Context
|
||||
|
||||
There are two main context types. See [context](context.md) for details.
|
||||
|
||||
Agents are generic on their `context` type. Context is a dependency-injection tool: it's an object you create and pass to `Runner.run()`, that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class UserContext:
|
||||
uid: str
|
||||
is_pro_user: bool
|
||||
class SecurityContext:
|
||||
target_system: str
|
||||
is_compromised: bool
|
||||
|
||||
async def fetch_purchases() -> list[Purchase]:
|
||||
async def get_exploits() -> list[Exploits]:
|
||||
return ...
|
||||
|
||||
agent = Agent[UserContext](
|
||||
agent = Agent[SecurityContext](
|
||||
...,
|
||||
)
|
||||
```
|
||||
|
|
@ -51,22 +64,21 @@ By default, agents produce plain text (i.e. `str`) outputs. If you want the agen
|
|||
from pydantic import BaseModel
|
||||
from cai.sdk.agents import Agent
|
||||
|
||||
|
||||
class CalendarEvent(BaseModel):
|
||||
class SecurityVulnerability(BaseModel):
|
||||
name: str
|
||||
date: str
|
||||
participants: list[str]
|
||||
severity: str
|
||||
affected_files: list[str]
|
||||
description: str
|
||||
|
||||
agent = Agent(
|
||||
name="Calendar extractor",
|
||||
instructions="Extract calendar events from text",
|
||||
output_type=CalendarEvent,
|
||||
name="Vulnerability scanner",
|
||||
instructions="Analyze system output and identify security vulnerabilities",
|
||||
output_type=SecurityVulnerability,
|
||||
)
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses.
|
||||
<span style="color: red;"> When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses.</span>
|
||||
|
||||
## Handoffs
|
||||
|
||||
|
|
@ -75,17 +87,38 @@ Handoffs are sub-agents that the agent can delegate to. You provide a list of ha
|
|||
```python
|
||||
from cai.sdk.agents import Agent
|
||||
|
||||
booking_agent = Agent(...)
|
||||
refund_agent = Agent(...)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Triage agent",
|
||||
crypto_agent = Agent(
|
||||
name="Cryptography agent",
|
||||
description="Agent specialized in solving cryptographic challenges and decoding encrypted messages",
|
||||
instructions="Analyze encrypted data and apply cryptographic techniques to decode it.",
|
||||
tools=[
|
||||
execute_cli_command,
|
||||
],
|
||||
handoff_description="Specialized agent in Cryptography and code breaking",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:14b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
network_agent = Agent(
|
||||
name="Network Agent",
|
||||
description="Agent specialized in network analysis, packet inspection, and network security assessments",
|
||||
instructions="Analyze network traffic, identify suspicious patterns, and help with network-related CTF challenges",
|
||||
handoff_description="Specialized agent in network security, traffic analysis, and protocol understanding",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:72b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
lead_agent = Agent(
|
||||
name="Cybersecurity Lead Agent",
|
||||
instructions=(
|
||||
"Help the user with their questions."
|
||||
"If they ask about booking, handoff to the booking agent."
|
||||
"If they ask about refunds, handoff to the refund agent."
|
||||
"You are a lead cybersecurity expert coordinating security operations."
|
||||
"If the user needs network analysis or traffic inspection, handoff to the network agent."
|
||||
"If the user needs cryptographic solutions or code breaking, handoff to the crypto agent."
|
||||
),
|
||||
handoffs=[booking_agent, refund_agent],
|
||||
handoffs=[network_agent, crypto_agent],
|
||||
model="qwen2.5:72b"
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -97,51 +130,21 @@ In most cases, you can provide instructions when you create the agent. However,
|
|||
def dynamic_instructions(
|
||||
context: RunContextWrapper[UserContext], agent: Agent[UserContext]
|
||||
) -> str:
|
||||
return f"The user's name is {context.context.name}. Help them with their questions."
|
||||
security_level = "high" if context.context.is_admin else "standard"
|
||||
return f"You are assisting {context.context.name} with cybersecurity operations. Their security clearance level is {security_level}. Tailor your security recommendations appropriately and prioritize addressing their immediate security concerns."
|
||||
|
||||
|
||||
agent = Agent[UserContext](
|
||||
name="Triage agent",
|
||||
name="Cybersecurity Triage Agent",
|
||||
instructions=dynamic_instructions,
|
||||
)
|
||||
```
|
||||
|
||||
## Lifecycle events (hooks)
|
||||
|
||||
Sometimes, you want to observe the lifecycle of an agent. For example, you may want to log events, or pre-fetch data when certain events occur. You can hook into the agent lifecycle with the `hooks` property. Subclass the [`AgentHooks`][cai.sdk.agents.lifecycle.AgentHooks] class, and override the methods you're interested in.
|
||||
## Next steps
|
||||
|
||||
## Guardrails
|
||||
- For running agents, see [running_agents documentation](running_agents.md).
|
||||
|
||||
Guardrails allow you to run checks/validations on user input, in parallel to the agent running. For example, you could screen the user's input for relevance. Read more in the [guardrails](guardrails.md) documentation.
|
||||
- For understanding what it returns, see [results documentation](results.md).
|
||||
|
||||
## Cloning/copying agents
|
||||
|
||||
By using the `clone()` method on an agent, you can duplicate an Agent, and optionally change any properties you like.
|
||||
|
||||
```python
|
||||
pirate_agent = Agent(
|
||||
name="Pirate",
|
||||
instructions="Write like a pirate",
|
||||
model="o3-mini",
|
||||
)
|
||||
|
||||
robot_agent = pirate_agent.clone(
|
||||
name="Robot",
|
||||
instructions="Write like a robot",
|
||||
)
|
||||
```
|
||||
|
||||
## Forcing tool use
|
||||
|
||||
Supplying a list of tools doesn't always mean the LLM will use a tool. You can force tool use by setting [`ModelSettings.tool_choice`][cai.sdk.agents.model_settings.ModelSettings.tool_choice]. Valid values are:
|
||||
|
||||
1. `auto`, which allows the LLM to decide whether or not to use a tool.
|
||||
2. `required`, which requires the LLM to use a tool (but it can intelligently decide which tool).
|
||||
3. `none`, which requires the LLM to _not_ use a tool.
|
||||
4. Setting a specific string e.g. `my_tool`, which requires the LLM to use that specific tool.
|
||||
|
||||
!!! note
|
||||
|
||||
To prevent infinite loops, the framework automatically resets `tool_choice` to "auto" after a tool call. This behavior is configurable via [`agent.reset_tool_choice`][cai.sdk.agents.agent.Agent.reset_tool_choice]. The infinite loop is because tool results are sent to the LLM, which then generates another tool call because of `tool_choice`, ad infinitum.
|
||||
|
||||
If you want the Agent to completely stop after a tool call (rather than continuing with auto mode), you can set [`Agent.tool_use_behavior="stop_on_first_tool"`] which will directly use the tool output as the final response without further LLM processing.
|
||||
- For connecting Agents to external tools (Model Context Protocol), see [mcp documentation](mcp.md).
|
||||
|
|
@ -1 +0,0 @@
|
|||
TODO
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# Core API Reference
|
||||
|
||||
## Agent
|
||||
|
||||
The `Agent` class is the main abstraction for implementing AI agents in CAI.
|
||||
|
||||
```python
|
||||
from cai import Agent
|
||||
|
||||
class MyAgent(Agent):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Initialize your agent here
|
||||
|
||||
async def run(self, input_data):
|
||||
# Implement your agent's logic here
|
||||
pass
|
||||
```
|
||||
|
||||
### Key Methods
|
||||
|
||||
- `__init__()`: Initialize the agent
|
||||
- `run(input_data)`: Main execution method
|
||||
- `add_tool(tool)`: Add a tool to the agent
|
||||
- `remove_tool(tool_name)`: Remove a tool from the agent
|
||||
|
||||
## Tools
|
||||
|
||||
Tools are the building blocks that agents use to interact with the world.
|
||||
|
||||
```python
|
||||
from cai import Tool
|
||||
|
||||
class MyTool(Tool):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
name="my_tool",
|
||||
description="Description of what the tool does"
|
||||
)
|
||||
|
||||
async def execute(self, **kwargs):
|
||||
# Implement tool logic here
|
||||
pass
|
||||
```
|
||||
|
||||
### Built-in Tools
|
||||
|
||||
- `LinuxCmd`: Execute Linux commands
|
||||
- `WebSearch`: Perform web searches
|
||||
- `Code`: Execute code
|
||||
- `SSHTunnel`: Create SSH tunnels
|
||||
|
||||
## Patterns
|
||||
|
||||
Patterns are reusable agent behaviors that can be composed together.
|
||||
|
||||
```python
|
||||
from cai import Pattern
|
||||
|
||||
class MyPattern(Pattern):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
async def execute(self, context):
|
||||
# Implement pattern logic here
|
||||
pass
|
||||
```
|
||||
|
||||
## Handoffs
|
||||
|
||||
Handoffs allow agents to transfer control to other agents or human operators.
|
||||
|
||||
```python
|
||||
from cai import Handoff
|
||||
|
||||
class MyHandoff(Handoff):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
async def execute(self, context):
|
||||
# Implement handoff logic here
|
||||
pass
|
||||
```
|
||||
|
||||
## Tracing
|
||||
|
||||
Tracing provides visibility into agent execution.
|
||||
|
||||
```python
|
||||
from cai import Tracer
|
||||
|
||||
tracer = Tracer()
|
||||
tracer.start_trace()
|
||||
# ... agent execution ...
|
||||
tracer.end_trace()
|
||||
```
|
||||
|
||||
## HITL (Human In The Loop)
|
||||
|
||||
HITL allows human operators to interact with agents during execution.
|
||||
|
||||
```python
|
||||
from cai import HITL
|
||||
|
||||
class MyHITL(HITL):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
async def execute(self, context):
|
||||
# Implement HITL logic here
|
||||
pass
|
||||
```
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Architecture Overview
|
||||
|
||||
CAI focuses on making cybersecurity agent **coordination** and **execution** lightweight, highly controllable, and useful for humans. To do so it builds upon 7 pillars: `Agent`s, `Tools`, `Handoffs`, `Patterns`, `Turns`, `Tracing` and `HITL`.
|
||||
|
||||
```
|
||||
┌───────────────┐ ┌───────────┐
|
||||
│ HITL │◀─────────▶│ Turns │
|
||||
└───────┬───────┘ └───────────┘
|
||||
│
|
||||
▼
|
||||
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
||||
│ Patterns │◀─────▶│ Handoffs │◀────▶ │ Agents │◀────▶│ LLMs │
|
||||
└───────────┘ └─────┬─────┘ └───────────┘ └───────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
┌────────────┐ ┌────┴──────┐ ┌───────────┐
|
||||
│ Extensions │◀─────▶│ Tracing │ │ Tools │
|
||||
└────────────┘ └───────────┘ └───────────┘
|
||||
│
|
||||
┌─────────────┬─────┴────┬─────────────┐
|
||||
▼ ▼ ▼ ▼
|
||||
┌───────────┐┌───────────┐┌────────────┐┌───────────┐
|
||||
│ LinuxCmd ││ WebSearch ││ Code ││ SSHTunnel │
|
||||
└───────────┘└───────────┘└────────────┘└───────────┘
|
||||
```
|
||||
|
||||
If you want to dive deeper into the code, check the following files as a start point for using CAI:
|
||||
|
||||
```
|
||||
cai
|
||||
├── __init__.py
|
||||
│
|
||||
├── cli.py # entrypoint for CLI
|
||||
├── core.py # core implementation and agentic flow
|
||||
├── types.py # main abstractions and classes
|
||||
├── util.py # utility functions
|
||||
│
|
||||
├── repl # CLI aesthetics and commands
|
||||
│ ├── commands
|
||||
│ └── ui
|
||||
├── agents # agent implementations
|
||||
│ ├── one_tool.py # agent, one agent per file
|
||||
│ └── patterns # agentic patterns, one per file
|
||||
│
|
||||
├── tools # agent tools
|
||||
│ ├── common.py
|
||||
|
||||
caiextensions # out of tree Python extensions
|
||||
```
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# Contributing to CAI
|
||||
|
||||
## Development Setup
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/cai.git
|
||||
cd cai
|
||||
```
|
||||
|
||||
2. Create and activate a virtual environment:
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||
```
|
||||
|
||||
3. Install development dependencies:
|
||||
```bash
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
## Code Style
|
||||
|
||||
CAI follows PEP 8 style guidelines. We use:
|
||||
- Black for code formatting
|
||||
- isort for import sorting
|
||||
- flake8 for linting
|
||||
- mypy for type checking
|
||||
|
||||
To run the code quality checks:
|
||||
```bash
|
||||
black .
|
||||
isort .
|
||||
flake8
|
||||
mypy .
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
We use pytest for testing. To run the test suite:
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is built using MkDocs. To build and serve the documentation locally:
|
||||
```bash
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Run all tests and code quality checks
|
||||
5. Submit a pull request
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md) when contributing to CAI.
|
||||
|
||||
## License
|
||||
|
||||
By contributing to CAI, you agree that your contributions will be licensed under the project's [MIT License](LICENSE).
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# Configuration
|
||||
|
||||
## Environment Variables
|
||||
|
||||
CAI leverages the `.env` file to load configuration at launch. To facilitate the setup, the repo provides an exemplary [`.env.example`](.env.example) file provides a template for configuring CAI's setup and your LLM API keys to work with desired LLM models.
|
||||
|
||||
⚠️ Important:
|
||||
|
||||
CAI does NOT provide API keys for any model by default. Don't ask us to provide keys, use your own or host your own models.
|
||||
|
||||
⚠️ Note:
|
||||
|
||||
The OPENAI_API_KEY must not be left blank. It should contain either "sk-123" (as a placeholder) or your actual API key. See https://github.com/aliasrobotics/cai/issues/27.
|
||||
|
||||
### List of Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| CTF_NAME | Name of the CTF challenge to run (e.g. "picoctf_static_flag") |
|
||||
| CTF_CHALLENGE | Specific challenge name within the CTF to test |
|
||||
| CTF_SUBNET | Network subnet for the CTF container |
|
||||
| CTF_IP | IP address for the CTF container |
|
||||
| CTF_INSIDE | Whether to conquer the CTF from within container |
|
||||
| CAI_MODEL | Model to use for agents |
|
||||
| CAI_DEBUG | Set debug output level (0: Only tool outputs, 1: Verbose debug output, 2: CLI debug output) |
|
||||
| CAI_BRIEF | Enable/disable brief output mode |
|
||||
| CAI_MAX_TURNS | Maximum number of turns for agent interactions |
|
||||
| CAI_TRACING | Enable/disable OpenTelemetry tracing |
|
||||
| CAI_AGENT_TYPE | Specify the agents to use (boot2root, one_tool...) |
|
||||
| CAI_STATE | Enable/disable stateful mode |
|
||||
| CAI_MEMORY | Enable/disable memory mode (episodic, semantic, all) |
|
||||
| CAI_MEMORY_ONLINE | Enable/disable online memory mode |
|
||||
| CAI_MEMORY_OFFLINE | Enable/disable offline memory |
|
||||
| CAI_ENV_CONTEXT | Add dirs and current env to llm context |
|
||||
| CAI_MEMORY_ONLINE_INTERVAL | Number of turns between online memory updates |
|
||||
| CAI_PRICE_LIMIT | Price limit for the conversation in dollars |
|
||||
| CAI_REPORT | Enable/disable reporter mode (ctf, nis2, pentesting) |
|
||||
| CAI_SUPPORT_MODEL | Model to use for the support agent |
|
||||
| CAI_SUPPORT_INTERVAL | Number of turns between support agent executions |
|
||||
| CAI_WORKSPACE | Defines the name of the workspace |
|
||||
| CAI_WORKSPACE_DIR | Specifies the directory path where the workspace is located |
|
||||
|
||||
## Custom OpenAI Base URL Support
|
||||
|
||||
CAI supports configuring a custom OpenAI API base URL via the `OPENAI_BASE_URL` environment variable. This allows users to redirect API calls to a custom endpoint, such as a proxy or self-hosted OpenAI-compatible service.
|
||||
|
||||
Example `.env` entry configuration:
|
||||
```
|
||||
OLLAMA_API_BASE="https://custom-openai-proxy.com/v1"
|
||||
```
|
||||
|
||||
Or directly from the command line:
|
||||
```bash
|
||||
OLLAMA_API_BASE="https://custom-openai-proxy.com/v1" cai
|
||||
```
|
||||
|
||||
## OpenRouter Integration
|
||||
|
||||
The Cybersecurity AI (CAI) platform offers seamless integration with OpenRouter, a unified interface for Large Language Models (LLMs). This integration is crucial for users who wish to leverage advanced AI capabilities in their cybersecurity tasks. OpenRouter acts as a bridge, allowing CAI to communicate with various LLMs, thereby enhancing the flexibility and power of the AI agents used within CAI.
|
||||
|
||||
To enable OpenRouter support in CAI, you need to configure your environment by adding specific entries to your `.env` file. This setup ensures that CAI can interact with the OpenRouter API, facilitating the use of sophisticated models like Meta-LLaMA. Here's how you can configure it:
|
||||
|
||||
```bash
|
||||
CAI_AGENT_TYPE=redteam_agent
|
||||
CAI_MODEL=openrouter/meta-llama/llama-4-maverick
|
||||
OPENROUTER_API_KEY=<sk-your-key> # note, add yours
|
||||
OPENROUTER_API_BASE=https://openrouter.ai/api/v1
|
||||
```
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# Installation
|
||||
|
||||
```bash
|
||||
pip install cai-framework
|
||||
```
|
||||
|
||||
## OS X
|
||||
```bash
|
||||
brew update && \
|
||||
brew install git python@3.12
|
||||
|
||||
# Create virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
## Ubuntu 24.04
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y git python3-pip python3.12-venv
|
||||
|
||||
# Create the virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
## Ubuntu 20.04
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y software-properties-common
|
||||
|
||||
# Fetch Python 3.12
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt update
|
||||
sudo apt install python3.12 python3.12-venv python3.12-dev -y
|
||||
|
||||
# Create the virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
## Windows WSL
|
||||
Go to the Microsoft page: https://learn.microsoft.com/en-us/windows/wsl/install. Here you will find all the instructions to install WSL
|
||||
|
||||
From Powershell write: wsl --install
|
||||
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y git python3-pip python3-venv
|
||||
|
||||
# Create the virtual environment
|
||||
python3 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
## Android
|
||||
|
||||
We recommend having at least 8 GB of RAM:
|
||||
|
||||
1. First of all, install userland https://play.google.com/store/apps/details?id=tech.ula&hl=es
|
||||
|
||||
2. Install Kali minimal in basic options (for free). [Or any other kali option if preferred]
|
||||
|
||||
3. Update apt keys like in this example: https://superuser.com/questions/1644520/apt-get-update-issue-in-kali, inside UserLand's Kali terminal execute
|
||||
|
||||
```bash
|
||||
# Get new apt keys
|
||||
wget http://http.kali.org/kali/pool/main/k/kali-archive-keyring/kali-archive-keyring_2024.1_all.deb
|
||||
|
||||
# Install new apt keys
|
||||
sudo dpkg -i kali-archive-keyring_2024.1_all.deb && rm kali-archive-keyring_2024.1_all.deb
|
||||
|
||||
# Update APT repository
|
||||
sudo apt-get update
|
||||
|
||||
# CAI requieres python 3.12, lets install it (CAI for kali in Android)
|
||||
sudo apt-get update && sudo apt-get install -y git python3-pip build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev pkg-config
|
||||
wget https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tar.xz
|
||||
tar xf Python-3.12.4.tar.xz
|
||||
cd ./configure --enable-optimizations
|
||||
sudo make altinstall # This command takes long to execute
|
||||
|
||||
# Clone CAI's source code
|
||||
git clone https://github.com/aliasrobotics/cai && cd cai
|
||||
|
||||
# Create virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip3 install -e .
|
||||
|
||||
# Generate a .env file and set up
|
||||
cp .env.example .env # edit here your keys/models
|
||||
|
||||
# Launch CAI
|
||||
cai
|
||||
```
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Cybersecurity AI (CAI)
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<a align="center" href="" target="https://github.com/aliasrobotics/CAI">
|
||||
<img
|
||||
width="100%"
|
||||
src="https://github.com/aliasrobotics/cai/raw/main/media/cai.png"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🎯 Milestones
|
||||
[-red.svg)](https://app.hackthebox.com/users/2268644)
|
||||
[-red.svg)](https://app.hackthebox.com/users/2268644)
|
||||
[-red.svg)](https://app.hackthebox.com/users/2268644)
|
||||
[-red.svg)](https://app.hackthebox.com/users/2268644)
|
||||
[_world-red.svg)](https://ctf.hackthebox.com/event/2000/scoreboard)
|
||||
[](https://ctf.hackthebox.com/event/2000/scoreboard)
|
||||
[](https://ctf.hackthebox.com/event/2000/scoreboard)
|
||||
[](https://ctf.hackthebox.com/event/2000/scoreboard)
|
||||
[](https://lu.ma/roboticshack?tk=RuryKF)
|
||||
[](https://github.com/aliasrobotics/cai)
|
||||
|
||||
## 📦 Package Attributes
|
||||
[](https://badge.fury.io/py/cai-framework)
|
||||
[](https://pypistats.org/packages/cai-framework)
|
||||
[](https://github.com/aliasrobotics/cai)
|
||||
[](https://github.com/aliasrobotics/cai)
|
||||
[](https://github.com/aliasrobotics/cai)
|
||||
[](https://github.com/aliasrobotics/cai)
|
||||
[](https://discord.gg/fnUFcTaQAC)
|
||||
[](https://arxiv.org/pdf/2504.06017)
|
||||
</div>
|
||||
|
||||
A lightweight, ergonomic framework for building bug bounty-ready Cybersecurity AIs (CAIs).
|
||||
|
||||
| CAI on JWT@PortSwigger CTF — Cybersecurity AI | CAI on HackableII Boot2Root CTF — Cybersecurity AI |
|
||||
|-----------------------------------------------|---------------------------------|
|
||||
| [](https://asciinema.org/a/713487) | [](https://asciinema.org/a/713485) |
|
||||
|
||||
> [!WARNING]
|
||||
> ⚠️ CAI is in active development, so don't expect it to work flawlessly. Instead, contribute by raising an issue or [sending a PR](https://github.com/aliasrobotics/cai/pulls).
|
||||
>
|
||||
> Access to this library and the use of information, materials (or portions thereof), is **<u>not intended</u>, and is <u>prohibited</u>, where such access or use violates applicable laws or regulations**. By no means the authors encourage or promote the unauthorized tampering with running systems. This can cause serious human harm and material damages.
|
||||
>
|
||||
> *By no means the authors of CAI encourage or promote the unauthorized tampering with compute systems. Please don't use the source code in here for cybercrime. <u>Pentest for good instead</u>*. By downloading, using, or modifying this source code, you agree to the terms of the [`LICENSE`](LICENSE) and the limitations outlined in the [`DISCLAIMER`](DISCLAIMER) file.
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
CAI focuses on making cybersecurity agent **coordination** and **execution** lightweight, highly controllable, and useful for humans. To do so it builds upon 7 pillars: `Agent`s, `Tools`, `Orchestation`, <span style="color: red;">`Patterns`</span>, `Turns`, `Tracing` and `HITL`.
|
||||
|
||||
<span style="color: red;">TODO: Graph</span>
|
||||
|
||||
If you want to dive deeper into the code, check the following files as a start point for using CAI:
|
||||
|
||||
<span style="color: red;">TODO: files tree</span>
|
||||
|
||||
|
||||
### 🔹 Agent
|
||||
|
||||
At its core, CAI abstracts its cybersecurity behavior via `Agents` and agentic `Patterns`. An Agent in *an intelligent system that interacts with some environment*. More technically, within CAI we embrace a robotics-centric definition wherein an agent is anything that can be viewed as a system perceiving its environment through sensors, reasoning about its goals and and acting accordingly upon that environment through actuators (*adapted* from Russel & Norvig, AI: A Modern Approach). In cybersecurity, an `Agent` interacts with systems and networks, using peripherals and network interfaces as sensors, reasons accordingly and then executes network actions as if actuators. Correspondingly, in CAI, `Agent`s implement the `ReACT` (Reasoning and Action) <span style="color: red;">agent model[3].
|
||||
|
||||
For more details, including examples and implementation guidance, see the [Agents documentation](agents.md).
|
||||
|
||||
|
||||
### 🔹 Tools
|
||||
|
||||
`Tools` let cybersecurity agents take actions by providing interfaces to execute system commands, run security scans, analyze vulnerabilities, and interact with target systems and APIs - they are the core capabilities that enable CAI agents to perform security tasks effectively; in CAI, tools include built-in cybersecurity utilities (like LinuxCmd for command execution, WebSearch for OSINT gathering, Code for dynamic script execution, and SSHTunnel for secure remote access), function calling mechanisms that allow integration of any Python function as a security tool, and agent-as-tool functionality that enables specialized security agents (such as reconnaissance or exploit agents) to be used by other agents, creating powerful collaborative security workflows without requiring formal handoffs between agents.
|
||||
|
||||
You may find different [tools](src/cai/tools). They are grouped in 6 major categories inspired by the <span style="color: red;">security kill chain[2]:
|
||||
s
|
||||
1. Reconnaissance and weaponization - *reconnaissance* (crypto, listing, etc)
|
||||
2. Exploitation - *exploitation*
|
||||
3. Privilege escalation - *escalation*
|
||||
4. Lateral movement - *lateral*
|
||||
5. Data exfiltration - *exfiltration*
|
||||
6. Command and control - *control*
|
||||
|
||||
For more information, examples, and implementation details, please refer to the [Tools documentation](tools.md).
|
||||
|
||||
### 🔹 Orchestrating agents
|
||||
|
||||
Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents:
|
||||
|
||||
1. Orchestrating via LLM: Allowing the LLM to make decisions, this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that.
|
||||
2. Orchestrating via code: determining the flow of agents via your code.
|
||||
|
||||
You can mix and match these patterns. Each has their own tradeoffs, described below.
|
||||
|
||||
We have a number of examples in examples/cai/agent_patterns.
|
||||
|
||||
#### ◉ Orchestrating via LLM
|
||||
|
||||
An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using [tools](tools.md) to take actions and acquire data, and using [handoffs](handoffs.md) to delegate tasks to sub-agents.
|
||||
|
||||
You could also use an agent as a tool. The agents operates independently on its provided input —without access to prior conversation history or "taking over" the conversation - completes its specific task, and returns the result to the calling (parent) agent.
|
||||
|
||||
|
||||
#### ◉ Orchestrating via code
|
||||
|
||||
While orchestrating via LLM is powerful, orchestrating via code makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are:
|
||||
- Using structured outputs to generate well formed data that you can inspect with your code.
|
||||
- Using a determinitstic pattern: Breaking down a task into a series of smaller steps. Chaining multiple agents, each step can be performed by an agent, and the output of one agent is used as input to the next.
|
||||
- Using [Guardrails](guardrails.md) and LLM_as_judge: They are agents that evaluates and provides feedback, until they says the inputs/outputs passes certain criteria. The agent ensures inputs/outputs are appropriate.
|
||||
- Paralelization of task: Running multiple agents in parallel. This is useful for speed when you have multiple tasks that don't depend on each other.
|
||||
|
||||
|
||||
|
||||
### 🔹 Patterns
|
||||
|
||||
<span style="color: red;">An agentic `Pattern` is a *structured design paradigm* in artificial intelligence systems where autonomous or semi-autonomous agents operate within a defined *interaction framework* (the pattern) to achieve a goal. These `Patterns` specify the organization, coordination, and communication
|
||||
methods among agents, guiding decision-making, task execution, and delegation.</span>
|
||||
|
||||
An agentic pattern (`AP`) can be formally defined as a tuple:
|
||||
|
||||
\\[
|
||||
AP = (A, H, D, C, E)
|
||||
\\]
|
||||
|
||||
wherein:
|
||||
|
||||
- **\\(A\\) (Agents):** A set of autonomous entities, \\( A = \\{a_1, a_2, ..., a_n\\} \\), each with defined roles, capabilities, and internal states.
|
||||
- **\\(H\\) (Handoffs):** A function \\( H: A \times T \to A \\) that governs how tasks \\( T \\) are transferred between agents based on predefined logic (e.g., rules, negotiation, bidding).
|
||||
- **\\(D\\) (Decision Mechanism):** A decision function \\( D: S \to A \\) where \\( S \\) represents system states, and \\( D \\) determines which agent takes action at any given time.
|
||||
- **\\(C\\) (Communication Protocol):** A messaging function \\( C: A \times A \to M \\), where \\( M \\) is a message space, defining how agents share information.
|
||||
- **\\(E\\) (Execution Model):** A function \\( E: A \times I \to O \\) where \\( I \\) is the input space and \\( O \\) is the output space, defining how agents perform tasks.
|
||||
|
||||
When building `Patterns`, we generall y classify them among one of the following categories, though others exist:
|
||||
|
||||
| **Agentic** `Pattern` **categories** | **Description** |
|
||||
|--------------------|------------------------|
|
||||
| `Swarm` (Decentralized) | Agents share tasks and self-assign responsibilities without a central orchestrator. Handoffs occur dynamically. *An example of a peer-to-peer agentic pattern is the `CTF Agentic Pattern`, which involves a team of agents working together to solve a CTF challenge with dynamic handoffs.* |
|
||||
| `Hierarchical` | A top-level agent (e.g., "PlannerAgent") assigns tasks via structured handoffs to specialized sub-agents. Alternatively, the structure of the agents is harcoded into the agentic pattern with pre-defined handoffs. |
|
||||
| `Chain-of-Thought` (Sequential Workflow) | A structured pipeline where Agent A produces an output, hands it to Agent B for reuse or refinement, and so on. Handoffs follow a linear sequence. *An example of a chain-of-thought agentic pattern is the `ReasonerAgent`, which involves a Reasoning-type LLM that provides context to the main agent to solve a CTF challenge with a linear sequence.*[1] |
|
||||
| `Auction-Based` (Competitive Allocation) | Agents "bid" on tasks based on priority, capability, or cost. A decision agent evaluates bids and hands off tasks to the best-fit agent. |
|
||||
| `Recursive` | A single agent continuously refines its own output, treating itself as both executor and evaluator, with handoffs (internal or external) to itself. *An example of a recursive agentic pattern is the `CodeAgent` (when used as a recursive agent), which continuously refines its own output by executing code and updating its own instructions.* |
|
||||
|
||||
Building a `Pattern` is rather straightforward and only requires to link together `Agents`, `Tools` and `Handoffs`. For example, the following builds an offensive `Pattern` that adopts the `Swarm` category:
|
||||
|
||||
### 🔹 Turns
|
||||
During the agentic flow (conversation), we distinguish between **interactions** and **turns**.
|
||||
|
||||
- **Interactions** are sequential exchanges between one or multiple agents. Each agent executing its logic corresponds with one *interaction*. Since an `Agent` in CAI generally implements the `ReACT` <span style="color: red;">agent model[^3]</span>, each *interaction* consists of 1) a reasoning step via an LLM inference and 2) act by calling zero-to-n `Tools`.
|
||||
- **Turns**: A turn represents a cycle of one ore more **interactions** which finishes when the `Agent` (or `Pattern`) executing returns `None`, judging there're no further actions to undertake.
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> CAI Agents are not related to Assistants in the Assistants API. They are named similarly for convenience, but are otherwise completely unrelated. CAI is entirely powered by the Chat Completions API and is hence stateless between calls.
|
||||
|
||||
|
||||
### 🔹 Tracing
|
||||
|
||||
<span style="color: red;">CAI implements AI observability by adopting the OpenTelemetry standard and to do so, it leverages [Phoenix](https://github.com/Arize-ai/phoenix) which provides comprehensive tracing capabilities through OpenTelemetry-based instrumentation, allowing you to monitor and analyze your security operations in real-time. This integration enables detailed visibility into agent interactions, tool usage, and attack vectors throughout penetration testing workflows, making it easier to debug complex exploitation chains, track vulnerability discovery processes, and optimize agent performance for more effective security assessments.</span>
|
||||
|
||||
|
||||

|
||||
|
||||
### 🔹 Human-In-The-Loop (HITL)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ │
|
||||
│ Cybersecurity AI (CAI) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ Autonomous AI │ │
|
||||
│ └────────┬────────┘ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ┌────────▼─────────┐ │
|
||||
│ │ HITL Interaction │ │
|
||||
│ └────────┬─────────┘ │
|
||||
│ │ │
|
||||
└────────────────┼────────────────┘
|
||||
│
|
||||
│ Ctrl+C (cli.py)
|
||||
│
|
||||
┌───────────▼───────────┐
|
||||
│ Human Operator(s) │
|
||||
│ Expertise | Judgment │
|
||||
│ Teleoperation │
|
||||
└───────────────────────┘
|
||||
```
|
||||
|
||||
CAI delivers a framework for building Cybersecurity AIs with a strong emphasis on *semi-autonomous* operation, as the reality is that **fully-autonomous** cybersecurity systems remain premature and face significant challenges when tackling complex tasks. While CAI explores autonomous capabilities, we recognize that effective security operations still require human teleoperation providing expertise, judgment, and oversight in the security process.
|
||||
|
||||
Accordingly, the Human-In-The-Loop (`HITL`) module is a core design principle of CAI, acknowledging that human intervention and teleoperation are essential components of responsible security testing. Through the `cli.py` interface, users can seamlessly interact with agents at any point during execution by simply pressing `Ctrl+C`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[1] Arguably, the Chain-of-Thought agentic pattern is a special case of the Hierarchical agentic pattern.
|
||||
[2] Kamhoua, C. A., Leslie, N. O., & Weisman, M. J. (2018). Game theoretic modeling of advanced persistent threat in internet of things. Journal of Cyber Security and Information Systems.
|
||||
[3] Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., & Cao, Y. (2023, January). React: Synergizing reasoning and acting in language models. In International Conference on Learning Representations (ICLR).
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
|
||||
## Citation
|
||||
If you want to cite our work, please use the following format
|
||||
```bibtex
|
||||
@misc{mayoralvilches2025caiopenbugbountyready,
|
||||
title={CAI: An Open, Bug Bounty-Ready Cybersecurity AI},
|
||||
author={Víctor Mayoral-Vilches and Luis Javier Navarrete-Lozano and María Sanz-Gómez and Lidia Salas Espejo and Martiño Crespo-Álvarez and Francisco Oca-Gonzalez and Francesco Balassone and Alfonso Glera-Picón and Unai Ayucar-Carbajo and Jon Ander Ruiz-Alcalde and Stefan Rass and Martin Pinzger and Endika Gil-Uriarte},
|
||||
year={2025},
|
||||
eprint={2504.06017},
|
||||
archivePrefix={arXiv},
|
||||
primaryClass={cs.CR},
|
||||
url={https://arxiv.org/abs/2504.06017},
|
||||
}
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
CAI was initially developed by [Alias Robotics](https://aliasrobotics.com) and co-funded by the European EIC accelerator project RIS (GA 101161136) - HORIZON-EIC-2023-ACCELERATOR-01 call. The original agentic principles are inspired from OpenAI's [`swarm`](https://github.com/openai/swarm) library. This project also makes use of other relevant open source building blocks including [`LiteLLM`](https://github.com/BerriAI/litellm), and [`phoenix`](https://github.com/Arize-ai/phoenix)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
Development is facilitated via VS Code dev. environments. To try out our development environment, clone the repository, open VS Code and enter de dev. container mode:
|
||||
|
||||

|
||||
|
||||
|
||||
### Contributions
|
||||
|
||||
If you’d like to contribute to this project, feel free to jump in! Whether it’s fixing bugs, improving documentation, or adding new features—your help is welcome and appreciated. Please open an issue or submit a pull request to get started.
|
||||
|
||||
### Usage Data Collection
|
||||
|
||||
CAI is provided free of charge for researchers. Instead of payment for research use cases, we ask you to contribute to the CAI community by allowing usage data collection. This data helps us understand how the framework is being used, identify areas for improvement, and prioritize new features. The collected data includes:
|
||||
|
||||
- Basic system information (OS type, Python version)
|
||||
- Username and IP information
|
||||
- Tool usage patterns and performance metrics
|
||||
- Model interactions and token usage statistics
|
||||
|
||||
We take your privacy seriously and only collect what's needed to make CAI better.
|
||||
|
||||
### Reproduce CI-Setup locally
|
||||
|
||||
To simulate the CI/CD pipeline, you can run the following in the Gitlab runner machines:
|
||||
|
||||
```bash
|
||||
docker run --rm -it \
|
||||
--privileged \
|
||||
--network=exploitflow_net \
|
||||
--add-host="host.docker.internal:host-gateway" \
|
||||
-v /cache:/cache \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:rw \
|
||||
registry.gitlab.com/aliasrobotics/alias_research/cai:latest bash
|
||||
```
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
<details><summary>OLLAMA is giving me 404 errors</summary>
|
||||
|
||||
Ollama's API in OpenAI mode uses `/v1/chat/completions` whereas the `openai` library uses `base_url` + `/chat/completions`.
|
||||
|
||||
We adopt the latter for overall alignment with the gen AI community and empower the former by allowing users to add the `v1` themselves via:
|
||||
|
||||
```bash
|
||||
OLLAMA_API_BASE=http://IP:PORT/v1
|
||||
```
|
||||
|
||||
See the following issues that treat this topic in more detail:
|
||||
- https://github.com/aliasrobotics/cai/issues/76
|
||||
- https://github.com/aliasrobotics/cai/issues/83
|
||||
- https://github.com/aliasrobotics/cai/issues/82
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Where are all the caiextensions?</summary>
|
||||
Currently, the extensions are not available as they have been (largely) integrated or are in the process of being integrated into the core architecture. We aim to have everything converge in next version. Coming soon!
|
||||
</details>
|
||||
|
||||
<details><summary>How do I set up SSH access for Gitlab?</summary>
|
||||
|
||||
Generate a new SSH key
|
||||
```bash
|
||||
ssh-keygen -t ed25519
|
||||
```
|
||||
|
||||
Add the key to the SSH agent
|
||||
```bash
|
||||
ssh-add ~/.ssh/id_ed25519
|
||||
```
|
||||
|
||||
Add the public key to Gitlab
|
||||
Copy the key and add it to Gitlab under https://gitlab.com/-/user_settings/ssh_keys
|
||||
```bash
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
To verify it:
|
||||
```bash
|
||||
ssh -T git@gitlab.com
|
||||
Welcome to GitLab, @vmayoral!
|
||||
```
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<details><summary>How do I clear Python cache?</summary>
|
||||
|
||||
```bash
|
||||
find . -name "*.pyc" -delete && find . -name "__pycache__" -delete
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>If host networking is not working with ollama check whether it has been disabled in Docker because you are not signed in</summary>
|
||||
|
||||
Docker in OS X behaves funny sometimes. Check if the following message has shown up:
|
||||
|
||||
*Host networking has been disabled because you are not signed in. Please sign in to enable it.*
|
||||
|
||||
Make sure this has been addressed and also that the Dev Container is not forwarding the 8000 port (click on x, if necessary in the ports section).
|
||||
|
||||
To verify connection, from within the VSCode devcontainer:
|
||||
```bash
|
||||
curl -v http://host.docker.internal:8000/api/version
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Run CAI against any target</summary>
|
||||
|
||||

|
||||
|
||||
The starting user prompt in this case is: `Target IP: 192.168.2.10, perform a full network scan`.
|
||||
|
||||
The agent started performing a nmap scan. You could either interact with the agent and give it more instructions, or let it run to see what it explores next.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>How do I interact with the agent? Type twice CTRL + C </summary>
|
||||
|
||||

|
||||
|
||||
If you want to use the HITL mode, you can do it by presssing twice ```Ctrl + C```.
|
||||
This will allow you to interact (prompt) with the agent whenever you want. The agent will not lose the previous context, as it is stored in the `history` variable, which is passed to it and any agent that is called. This enables any agent to use the previous information and be more accurate and efficient.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary> Can I change the model while CAI is running? /model </summary>
|
||||
|
||||
Use ```/model``` to change the model.
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>How can I list all the agents available? /agent </summary>
|
||||
|
||||
Use ```/agent``` to list all the agents available.
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Where can I list all the environment variables? /config </summary>
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary> How to know more about the CLI? /help </summary>
|
||||
|
||||

|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>How can I trace the whole execution?</summary>
|
||||
The environment variable `CAI_TRACING` allows the user to set it to `CAI_TRACING=true` to enable tracing, or `CAI_TRACING=false` to disable it.
|
||||
When CAI is prompted by the first time, the user is provided with two paths, the execution log, and the tracing log.
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Can I expand CAI capabilities using previous run logs?</summary>
|
||||
|
||||
Absolutely! The **memory extension** allows you to use a previously sucessful runs ( the log object is stored as a **.jsonl file in the [log](cai/logs) folder** ) in a new run against the same target.
|
||||
The user is also given the path highlighted in orange as shown below.
|
||||
|
||||

|
||||
|
||||
How to make use of this functionality?
|
||||
|
||||
1. Run CAI against the target. Let's assume the target name is: `target001`.
|
||||
2. Get the log file path, something like: ```logs/cai_20250408_111856.jsonl```
|
||||
3. Generate the memory using any model of your preference:
|
||||
```shell JSONL_FILE_PATH="logs/cai_20250408_111856.jsonl" CTF_INSIDE="false" CAI_MEMORY_COLLECTION="target001" CAI_MEMORY="episodic" CAI_MODEL="claude-3-5-sonnet-20241022" python3 tools/2_jsonl_to_memory.py ```
|
||||
|
||||
The script [`tools/2_jsonl_to_memory.py`](cai/tools/2_jsonl_to_memory.py) will generate a memory collection file with the most relevant steps. The quality of the memory collection will depend on the model you use.
|
||||
|
||||
4. Use the generated memory collection and execute a new run:
|
||||
```shell CAI_MEMORY="episodic" CAI_MODEL="gpt-4o" CAI_MEMORY_COLLECTION="target001" CAI_TRACING=false python3 cai/cli.py```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can I expand CAI capabilities using scripts or extra information?</summary>
|
||||
|
||||
Currently, CAI supports text based information. You can add any extra information on the target you are facing by copy-pasting it directly into the system or user prompt.
|
||||
|
||||
**How?** By adding it to the system ([`system_master_template.md`](cai/repl/templates/system_master_template.md)) or the user prompt ([`user_master_template.md`](cai/repl/templates/user_master_template.md)). You can always directly prompt the path to the model, and it will ```cat``` it.
|
||||
</details>
|
||||
<details>
|
||||
<summary>How do I run the documentation locally?</summary>
|
||||
|
||||
To view and edit the documentation locally, you can use [MkDocs](https://www.mkdocs.org/), which is a static site generator for project documentation.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Install MkDocs and the Material theme:**
|
||||
```bash
|
||||
pip install mkdocs mkdocs-material
|
||||
```
|
||||
|
||||
2. **Serve the documentation locally:**
|
||||
```bash
|
||||
python -m mkdocs serve
|
||||
```
|
||||
This will start a local server (usually at [http://127.0.0.1:8000](http://127.0.0.1:8000)) where you can view the docs in your browser.
|
||||
|
||||
3. **Build the static site (optional):**
|
||||
```bash
|
||||
mkdocs build
|
||||
```
|
||||
This will generate a `site/` directory with the static HTML files.
|
||||
|
||||
For more details, see the [MkDocs documentation](https://www.mkdocs.org/user-guide/).
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Find Us
|
||||
|
||||
We're excited to connect with you! Join our community through any of the following channels:
|
||||
|
||||
* [GitHub](https://github.com/aliasrobotics/cai)
|
||||
|
||||
* [Discord Community](https://discord.gg/v3SAXXg5)
|
||||
|
||||
* [Linkedin Group](https://www.linkedin.com/groups/10086397/)
|
||||
|
||||
* [Alias Robotics](https://aliasrobotics.com)
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
```bash
|
||||
pip install cai-framework
|
||||
```
|
||||
|
||||
The following subsections provide a more detailed walkthrough on selected popular Operating Systems. Refer to the [Development](cai_development.md) section for developer-related install instructions.
|
||||
|
||||
### OS X
|
||||
```bash
|
||||
brew update && \
|
||||
brew install git python@3.12
|
||||
|
||||
# Create virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
### Ubuntu 24.04
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y git python3-pip python3.12-venv
|
||||
|
||||
# Create the virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
### Ubuntu 20.04
|
||||
```bash
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y software-properties-common
|
||||
|
||||
# Fetch Python 3.12
|
||||
sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt update
|
||||
sudo apt install python3.12 python3.12-venv python3.12-dev -y
|
||||
|
||||
# Create the virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
### Windows WSL
|
||||
Go to the Microsoft page: https://learn.microsoft.com/en-us/windows/wsl/install. Here you will find all the instructions to install WSL
|
||||
|
||||
From Powershell write: wsl --install
|
||||
|
||||
```bash
|
||||
|
||||
sudo apt-get update && \
|
||||
sudo apt-get install -y git python3-pip python3-venv
|
||||
|
||||
# Create the virtual environment
|
||||
python3 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip install cai-framework
|
||||
|
||||
# Generate a .env file and set up with defaults
|
||||
echo -e 'OPENAI_API_KEY="sk-1234"\nANTHROPIC_API_KEY=""\nOLLAMA=""\nPROMPT_TOOLKIT_NO_CPR=1' > .env
|
||||
|
||||
# Launch CAI
|
||||
cai # first launch it can take up to 30 seconds
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
We recommend having at least 8 GB of RAM:
|
||||
|
||||
1. First of all, install userland: `https://play.google.com/store/apps/details?id=tech.ula&hl=es`
|
||||
|
||||
2. Install Kali minimal in basic options (for free). [Or any other kali option if preferred]
|
||||
|
||||
3. Update apt keys like in this example: `https://superuser.com/questions/1644520/apt-get-update-issue-in-kali`, inside UserLand's Kali terminal execute
|
||||
|
||||
```bash
|
||||
# Get new apt keys
|
||||
wget http://http.kali.org/kali/pool/main/k/kali-archive-keyring/kali-archive-keyring_2024.1_all.deb
|
||||
|
||||
# Install new apt keys
|
||||
sudo dpkg -i kali-archive-keyring_2024.1_all.deb && rm kali-archive-keyring_2024.1_all.deb
|
||||
|
||||
# Update APT repository
|
||||
sudo apt-get update
|
||||
|
||||
# CAI requieres python 3.12, lets install it (CAI for kali in Android)
|
||||
sudo apt-get update && sudo apt-get install -y git python3-pip build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev pkg-config
|
||||
wget https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tar.xz
|
||||
tar xf Python-3.12.4.tar.xz
|
||||
cd ./configure --enable-optimizations
|
||||
sudo make altinstall # This command takes long to execute
|
||||
|
||||
# Clone CAI's source code
|
||||
git clone https://github.com/aliasrobotics/cai && cd cai
|
||||
|
||||
# Create virtual environment
|
||||
python3.12 -m venv cai_env
|
||||
|
||||
# Install the package from the local directory
|
||||
source cai_env/bin/activate && pip3 install -e .
|
||||
|
||||
# Generate a .env file and set up
|
||||
cp .env.example .env # edit here your keys/models
|
||||
|
||||
# Launch CAI
|
||||
cai
|
||||
```
|
||||
|
||||
|
||||
### Setup `.env` file
|
||||
|
||||
CAI leverages the `.env` file to load configuration at launch. To facilitate the setup, the repo provides an exemplary `.env.example` file provides a template for configuring CAI's setup and your LLM API keys to work with desired LLM models.
|
||||
|
||||
```bash
|
||||
OPENAI_API_KEY="sk-1234"
|
||||
# OPENAI_API_KEY MUST BE FILLED-IN.
|
||||
# It should contain either "sk-123" (as a placeholder)
|
||||
# or your actual API key.
|
||||
# See https://github.com/aliasrobotics/cai/issues/27
|
||||
|
||||
ANTHROPIC_API_KEY=""
|
||||
OLLAMA=""
|
||||
PROMPT_TOOLKIT_NO_CPR=1
|
||||
```
|
||||
|
||||
|
||||
⚠️ CAI does NOT provide API keys for any model by default.
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
The **Cybersecurity AI (CAI)** platform provides seamless integration with multiple Large Language Models (LLMs). This functionality allows users to leverage state-of-the-art AI capabilities for various cybersecurity tasks. CAI acts as a bridge between your security workflows and a wide range of LLMs, enhancing both flexibility and performance of AI agents.
|
||||
|
||||
CAI supports **over 300 models**, thanks to its integration with [LiteLLM](https://github.com/BerriAI/litellm). You can choose from a wide variety of providers and models, including:
|
||||
|
||||
- **Anthropic**: Claude 3.7, Claude 3.5, Claude 3, Claude 3 Opus
|
||||
- **OpenAI**: O1, O1 Mini, O3 Mini, GPT-4o, GPT-4.5 Preview
|
||||
- **DeepSeek**: DeepSeek V3, DeepSeek R1
|
||||
- **Ollama**: Qwen2.5 72B, Qwen2.5 14B, and more
|
||||
|
||||
CAI is also compatibile with other platforms like OpenRouter and Ollama. Below you’ll find some configurations to help you get started.
|
||||
|
||||
#### [OpenRouter Integration](https://openrouter.ai/)
|
||||
|
||||
To enable OpenRouter support in CAI, you need to configure your environment by adding specific entries to your `.env` file. This setup ensures that CAI can interact with the OpenRouter API, facilitating the use of sophisticated models like Meta-LLaMA. Here’s how you can configure it:
|
||||
|
||||
```bash
|
||||
CAI_MODEL=openrouter/meta-llama/llama-4-maverick
|
||||
OPENROUTER_API_KEY=<sk-your-key> # note, add yours
|
||||
OPENROUTER_API_BASE=https://openrouter.ai/api/v1
|
||||
```
|
||||
|
||||
|
||||
#### [Ollama Integration](https://ollama.com/)
|
||||
For local models using Ollama, add the following to your .env:
|
||||
|
||||
```bash
|
||||
CAI_MODEL=qwen2.5:72b
|
||||
OLLAMA_API_BASE=http://localhost:8000/v1 # note, maybe you have a different endpoint
|
||||
```
|
||||
|
||||
Make sure that the Ollama server is running and accessible at the specified base URL. You can swap the model with any other supported by your local Ollama instance.
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
To start CAI after installing it, just type `cai` in the CLI:
|
||||
|
||||
```bash
|
||||
└─# cai
|
||||
|
||||
CCCCCCCCCCCCC ++++++++ ++++++++ IIIIIIIIII
|
||||
CCC::::::::::::C ++++++++++ ++++++++++ I::::::::I
|
||||
CC:::::::::::::::C ++++++++++ ++++++++++ I::::::::I
|
||||
C:::::CCCCCCCC::::C +++++++++ ++ +++++++++ II::::::II
|
||||
C:::::C CCCCCC +++++++ +++++ +++++++ I::::I
|
||||
C:::::C +++++ +++++++ +++++ I::::I
|
||||
C:::::C ++++ ++++ I::::I
|
||||
C:::::C ++ ++ I::::I
|
||||
C:::::C + +++++++++++++++ + I::::I
|
||||
C:::::C +++++++++++++++++++ I::::I
|
||||
C:::::C +++++++++++++++++ I::::I
|
||||
C:::::C CCCCCC +++++++++++++++ I::::I
|
||||
C:::::CCCCCCCC::::C +++++++++++++ II::::::II
|
||||
CC:::::::::::::::C +++++++++ I::::::::I
|
||||
CCC::::::::::::C +++++ I::::::::I
|
||||
CCCCCCCCCCCCC ++ IIIIIIIIII
|
||||
|
||||
Cybersecurity AI (CAI), v0.4.0
|
||||
Bug bounty-ready AI
|
||||
|
||||
CAI>
|
||||
```
|
||||
|
||||
That should initialize CAI and provide a prompt to execute any security task you want to perform. The navigation bar at the bottom displays important system information. This information helps you understand your environment while working with CAI.
|
||||
|
||||
Here's a quick [demo video](https://asciinema.org/a/zm7wS5DA2o0S9pu1Tb44pnlvy) to help you get started with CAI. We'll walk through the basic steps — from launching the tool to running your first AI-powered task in the terminal. Whether you're a beginner or just curious, this guide will show you how easy it is to begin using CAI.
|
||||
|
||||
From here on, type on `CAI` and start your security exercise. Best way to learn is by example:
|
||||
|
||||
### Environment Variables
|
||||
|
||||
??? "List of Environment Variables"
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| CTF_NAME | Name of the CTF challenge to run (e.g. "picoctf_static_flag") |
|
||||
| CTF_CHALLENGE | Specific sub challenge name within the CTF to test (e.g. CTF_NAME="kiddoctf" contains 4 subchallenges. For running one of them: "01 linux i") |
|
||||
| CTF_SUBNET | Network subnet for the CTF container |
|
||||
| CTF_IP | IP address for the CTF container |
|
||||
| CTF_INSIDE | Whether to conquer the CTF from within container |
|
||||
| CAI_MODEL | Model to use for agents |
|
||||
| CAI_DEBUG | Set debug output level (0: Only tool outputs, 1: Verbose debug output, 2: CLI debug output) |
|
||||
| CAI_BRIEF | Enable/disable brief output mode |
|
||||
| CAI_MAX_TURNS | Maximum number of turns for agent interactions |
|
||||
| CAI_TRACING | Enable/disable OpenTelemetry tracing |
|
||||
| CAI_AGENT_TYPE | Specify the agents to use (e.g. "boot2root") |
|
||||
| CAI_PRICE_LIMIT | Price limit for the conversation in dollars |
|
||||
| CAI_WORKSPACE | Defines the name of the workspace |
|
||||
| CAI_WORKSPACE_DIR | Specifies the directory path where the workspace is located |
|
||||
|
||||
|
||||
## Setting Environment Variables
|
||||
|
||||
There are several ways to configure environment variables for CAI:
|
||||
|
||||
1. **Using the `.env` file**:
|
||||
|
||||
```
|
||||
# Add any env variable to your .env file
|
||||
CAI_PRICE_LIMIT="0.004"
|
||||
CAI_MODEL="qwen2.5:72b"
|
||||
```
|
||||
|
||||
2. **Command-line parameters**: Pass variables directly when launching CAI
|
||||
|
||||
```
|
||||
CAI_PRICE_LIMIT="0.004" CAI_MODEL="qwen2.5:72b" cai
|
||||
```
|
||||
|
||||
3. **Runtime configuration**: after running CAI, use `/config`
|
||||
|
||||
```
|
||||
/config set <number> <value> to configure a variable # see `config.py` or type `/help`
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
cai
|
||||
/config # It will display a panel with all the environment variables.
|
||||
# You must pick its reference NUMBER (1st column left)
|
||||
# `18` is the corresponding number for CAI_PRICE_LIMIT
|
||||
/config set 18 "0.004"
|
||||
```
|
||||
|
|
@ -10,10 +10,10 @@ Context is an overloaded term. There are two main classes of context you might c
|
|||
This is represented via the [`RunContextWrapper`][cai.sdk.agents.run_context.RunContextWrapper] class and the [`context`][cai.sdk.agents.run_context.RunContextWrapper.context] property within it. The way this works is:
|
||||
|
||||
1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object.
|
||||
2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`.
|
||||
2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`).
|
||||
3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`.
|
||||
|
||||
The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context.
|
||||
The **most important** thing to be aware of: every agent, tool function, lifecycle, etc for a given agent run must use the same _type_ of context.
|
||||
|
||||
You can use the context for things like:
|
||||
|
||||
|
|
@ -32,40 +32,40 @@ from dataclasses import dataclass
|
|||
from cai.sdk.agents import Agent, RunContextWrapper, Runner, function_tool
|
||||
|
||||
@dataclass
|
||||
class UserInfo: # (1)!
|
||||
name: str
|
||||
uid: int
|
||||
class SecurityAlert: # (1)!
|
||||
ip_address: str
|
||||
threat_id: int
|
||||
|
||||
@function_tool
|
||||
async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)!
|
||||
return f"User {wrapper.context.name} is 47 years old"
|
||||
async def fetch_threat_details(wrapper: RunContextWrapper[SecurityAlert]) -> str: # (2)!
|
||||
return f"IP {wrapper.context.ip_address} is associated with a DDoS attack"
|
||||
|
||||
async def main():
|
||||
user_info = UserInfo(name="John", uid=123)
|
||||
security_alert = SecurityAlert(ip_address="192.168.1.100", threat_id=507)
|
||||
|
||||
agent = Agent[UserInfo]( # (3)!
|
||||
name="Assistant",
|
||||
tools=[fetch_user_age],
|
||||
agent = Agent[SecurityAlert]( # (3)!
|
||||
name="SecurityAnalyst",
|
||||
tools=[fetch_threat_details],
|
||||
)
|
||||
|
||||
result = await Runner.run( # (4)!
|
||||
starting_agent=agent,
|
||||
input="What is the age of the user?",
|
||||
context=user_info,
|
||||
input="What type of threat is associated with this IP?",
|
||||
context=security_alert,
|
||||
)
|
||||
|
||||
print(result.final_output) # (5)!
|
||||
# The user John is 47 years old.
|
||||
# IP 192.168.1.100 is associated with a DDoS attack.
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
1. This is the context object. We've used a dataclass here, but you can use any type.
|
||||
2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context.
|
||||
3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type).
|
||||
2. This is a tool. You can see it takes a `RunContextWrapper[SecurityAlert]`. The tool implementation reads from the context.
|
||||
3. We mark the agent with the generic `SecurityAlert`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type).
|
||||
4. The context is passed to the `run` function.
|
||||
5. The agent correctly calls the tool and gets the age.
|
||||
5. The agent correctly calls the tool and gets the threat information.
|
||||
|
||||
## Agent/LLM context
|
||||
|
||||
|
|
|
|||
|
|
@ -51,43 +51,43 @@ from cai.sdk.agents import (
|
|||
input_guardrail,
|
||||
)
|
||||
|
||||
class MathHomeworkOutput(BaseModel):
|
||||
is_math_homework: bool
|
||||
class MaliciousRequestOutput(BaseModel):
|
||||
is_malicious_request: bool
|
||||
reasoning: str
|
||||
|
||||
guardrail_agent = Agent( # (1)!
|
||||
name="Guardrail check",
|
||||
instructions="Check if the user is asking you to do their math homework.",
|
||||
output_type=MathHomeworkOutput,
|
||||
name="Security Guardrail Check",
|
||||
instructions="Check if the user is asking for help with hacking or bypassing security systems.",
|
||||
output_type=MaliciousRequestOutput,
|
||||
)
|
||||
|
||||
|
||||
@input_guardrail
|
||||
async def math_guardrail( # (2)!
|
||||
async def security_guardrail( # (2)!
|
||||
ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
|
||||
) -> GuardrailFunctionOutput:
|
||||
result = await Runner.run(guardrail_agent, input, context=ctx.context)
|
||||
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=result.final_output, # (3)!
|
||||
tripwire_triggered=result.final_output.is_math_homework,
|
||||
tripwire_triggered=result.final_output.is_malicious_request,
|
||||
)
|
||||
|
||||
|
||||
agent = Agent( # (4)!
|
||||
name="Customer support agent",
|
||||
instructions="You are a customer support agent. You help customers with their questions.",
|
||||
input_guardrails=[math_guardrail],
|
||||
name="Security assistant",
|
||||
instructions="You are a security assistant. You help users with legitimate security questions.",
|
||||
input_guardrails=[security_guardrail],
|
||||
)
|
||||
|
||||
async def main():
|
||||
# This should trip the guardrail
|
||||
try:
|
||||
await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
|
||||
await Runner.run(agent, "Hello, can you help me bypass the firewall on this corporate network?")
|
||||
print("Guardrail didn't trip - this is unexpected")
|
||||
|
||||
except InputGuardrailTripwireTriggered:
|
||||
print("Math homework guardrail tripped")
|
||||
print("Security guardrail tripped")
|
||||
```
|
||||
|
||||
1. We'll use this agent in our guardrail function.
|
||||
|
|
@ -110,42 +110,42 @@ from cai.sdk.agents import (
|
|||
class MessageOutput(BaseModel): # (1)!
|
||||
response: str
|
||||
|
||||
class MathOutput(BaseModel): # (2)!
|
||||
class SecurityOutput(BaseModel): # (2)!
|
||||
reasoning: str
|
||||
is_math: bool
|
||||
contains_sensitive_data: bool
|
||||
|
||||
guardrail_agent = Agent(
|
||||
name="Guardrail check",
|
||||
instructions="Check if the output includes any math.",
|
||||
output_type=MathOutput,
|
||||
name="Data Leakage Guardrail Check",
|
||||
instructions="Check if the output includes any sensitive data like passwords or API keys.",
|
||||
output_type=SecurityOutput,
|
||||
)
|
||||
|
||||
@output_guardrail
|
||||
async def math_guardrail( # (3)!
|
||||
async def data_leakage_guardrail( # (3)!
|
||||
ctx: RunContextWrapper, agent: Agent, output: MessageOutput
|
||||
) -> GuardrailFunctionOutput:
|
||||
result = await Runner.run(guardrail_agent, output.response, context=ctx.context)
|
||||
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=result.final_output,
|
||||
tripwire_triggered=result.final_output.is_math,
|
||||
tripwire_triggered=result.final_output.contains_sensitive_data,
|
||||
)
|
||||
|
||||
agent = Agent( # (4)!
|
||||
name="Customer support agent",
|
||||
instructions="You are a customer support agent. You help customers with their questions.",
|
||||
output_guardrails=[math_guardrail],
|
||||
output_type=MessageOutput,
|
||||
name="Security assistant",
|
||||
instructions="You are a security assistant. You help users with legitimate security questions.",
|
||||
output_guardrails=[data_leakage_guardrail],
|
||||
output_type=ResponseOutput,
|
||||
)
|
||||
|
||||
async def main():
|
||||
# This should trip the guardrail
|
||||
try:
|
||||
await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
|
||||
await Runner.run(agent, "What are the best practices for storing API keys in code?")
|
||||
print("Guardrail didn't trip - this is unexpected")
|
||||
|
||||
except OutputGuardrailTripwireTriggered:
|
||||
print("Math output guardrail tripped")
|
||||
print("Data leakage guardrail tripped")
|
||||
```
|
||||
|
||||
1. This is the actual agent's output type.
|
||||
|
|
|
|||
140
docs/handoffs.md
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc.
|
||||
|
||||
Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`.
|
||||
Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Flag Discriminator`, the tool would be called `transfer_to_flag_discriminator`.
|
||||
|
||||
## Creating a handoff
|
||||
|
||||
All agents have a [`handoffs`][cai.sdk.agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff.
|
||||
|
||||
You can create a handoff using the [`handoff()`][cai.sdk.agents.handoffs.handoff] function provided by the Agents SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters.
|
||||
You can create a handoff using the [`handoff()`][cai.sdk.agents.handoffs.handoff] function provided. This function allows you to specify the agent to hand off to, along with optional overrides and input filters.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
|
|
@ -17,14 +17,16 @@ Here's how you can create a simple handoff:
|
|||
```python
|
||||
from cai.sdk.agents import Agent, handoff
|
||||
|
||||
billing_agent = Agent(name="Billing agent")
|
||||
refund_agent = Agent(name="Refund agent")
|
||||
crypto_agent = Agent(name="Cryptography Agent")
|
||||
bash_agent = Agent(name="Bash Agent")
|
||||
|
||||
# (1)!
|
||||
triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)])
|
||||
cybersecurity_lead = Agent(name="Cybersecurity Lead Agent", handoffs=[crypto_agent, handoff(bash_agent)])
|
||||
```
|
||||
|
||||
1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function.
|
||||
|
||||
|
||||
1. You can use the agent directly (as in `crypto_agent`), or you can use the `handoff()` function.
|
||||
|
||||
### Customizing handoffs via the `handoff()` function
|
||||
|
||||
|
|
@ -87,15 +89,15 @@ There are some common patterns (for example removing all tool calls from the his
|
|||
from cai.sdk.agents import Agent, handoff
|
||||
from agents.extensions import handoff_filters
|
||||
|
||||
agent = Agent(name="FAQ agent")
|
||||
network_agent = Agent(name="Network Agent")
|
||||
|
||||
handoff_obj = handoff(
|
||||
agent=agent,
|
||||
agent=network_agent,
|
||||
input_filter=handoff_filters.remove_all_tools, # (1)!
|
||||
)
|
||||
```
|
||||
|
||||
1. This will automatically remove all tools from the history when `FAQ agent` is called.
|
||||
(1). This will automatically remove all tools from the history when `Network Agent` is called.
|
||||
|
||||
## Recommended prompts
|
||||
|
||||
|
|
@ -106,8 +108,126 @@ from cai.sdk.agents import Agent
|
|||
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
||||
|
||||
billing_agent = Agent(
|
||||
name="Billing agent",
|
||||
name="Phising Agent",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
<Fill in the rest of your prompt here>.""",
|
||||
)
|
||||
```
|
||||
|
||||
## All-in-one example
|
||||
|
||||
```
|
||||
We will represent the following workflow. You can make it more or less complex, depending on your needs.
|
||||
|
||||
┌───────────────────┐
|
||||
│ Cybersecurity │
|
||||
User Request ───────►│ Lead Agent │
|
||||
└───────┬───────────┘
|
||||
┌──────────────┴──────────────┐
|
||||
│ │
|
||||
┌─────────▼────────┐ ┌─────────▼────────┐
|
||||
│ Bash Agent │ │ Crypto Agent │
|
||||
└─────────┬────────┘ └──────────────────┘
|
||||
│
|
||||
┌─────────▼────────┐
|
||||
│ Flag │
|
||||
│ Discriminator │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, OpenAIChatCompletionsModel
|
||||
from openai import AsyncOpenAI
|
||||
from cai.sdk.agents import handoff, function_tool, trace
|
||||
from cai.sdk.agents import Runner
|
||||
from cai.tools.common import run_command
|
||||
from cai.sdk.agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
|
||||
@function_tool
|
||||
def execute_cli_command(command: str) -> str:
|
||||
"""Execute a command-line command and return its output."""
|
||||
return run_command(command)
|
||||
|
||||
|
||||
# Create the Flag Discriminator Agent (terminal agent in the chain)
|
||||
flag_discriminator = Agent(
|
||||
name="Flag Discriminator Agent",
|
||||
description="Agent specialized in verifying if content matches the expected flag format in CTF challenges",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
You are an expert at identifying CTF flags. The final flag is the first file of a -ls command. You return the flag with this format: flag{{filename}}""",
|
||||
handoff_description="Specialized agent in determining whether the content corresponds to the flag of the CTF challenge",
|
||||
handoffs=[],
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:72b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
# Create the Bash Agent (can hand off to Flag Discriminator)
|
||||
bash_agent = Agent(
|
||||
name="Bash Agent",
|
||||
description="Agent specialized in Bash commands and Linux operations",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
You are a Bash and Linux expert. Use your knowledge to navigate the file system and execute commands.
|
||||
The final flag is the first element listed in the directory.
|
||||
When you find the name of the file, handoff to the Flag Discriminator Agent for verification.""",
|
||||
tools=[execute_cli_command],
|
||||
handoffs=[handoff(flag_discriminator)],
|
||||
handoff_description="Specialized agent in Bash commands and Linux operations",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:14b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
# Create the Crypto Agent
|
||||
crypto_agent = Agent(
|
||||
name="Cryptography Agent",
|
||||
description="Agent specialized in cryptography, ciphers, and encoding/decoding",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
You are a cryptography expert. Help decipher and decode encrypted messages or files.""",
|
||||
tools=[execute_cli_command],
|
||||
handoffs=[],
|
||||
handoff_description="Specialized agent in cryptography and codebreaking",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:14b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
# Create the Cybersecurity Lead Agent (can hand off to both Bash and Crypto)
|
||||
cybersecurity_lead = Agent(
|
||||
name="Cybersecurity Lead Agent",
|
||||
description="Lead agent focused on conquering security challenges by delegating to specialist agents",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
You are a Cybersecurity expert Leader facing cybersecurity challenges.
|
||||
- Hand off to the Bash Agent when you need to execute Linux commands or navigate the file system.
|
||||
- Hand off to the Cryptography Agent when you encounter encrypted data or codes that need deciphering.""",
|
||||
tools=[execute_cli_command],
|
||||
handoffs=[
|
||||
handoff(bash_agent),
|
||||
handoff(crypto_agent)
|
||||
],
|
||||
handoff_description="Lead agent in cybersecurity operations",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model=os.getenv('CAI_MODEL', "qwen2.5:14b"),
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
# Trace the entire run as a single workflow
|
||||
with trace(workflow_name="CTF Workflow"):
|
||||
# Run with cybersecurity_lead directly
|
||||
result = await Runner.run(cybersecurity_lead, "List directories to find the flag")
|
||||
|
||||
print(result.final_output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
|
|
|||
120
docs/index.md
|
|
@ -1,52 +1,102 @@
|
|||
# OpenAI Agents SDK
|
||||
# Cybersecurity AI (`CAI`)
|
||||
|
||||
The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) enables you to build agentic AI apps in a lightweight, easy-to-use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, [Swarm](https://github.com/openai/swarm/tree/main). The Agents SDK has a very small set of primitives:
|
||||
<div align="center">
|
||||
<p>
|
||||
<a align="center" href="" target="https://github.com/aliasrobotics/CAI">
|
||||
<img
|
||||
width="100%"
|
||||
src="https://github.com/aliasrobotics/cai/raw/main/media/cai.png"
|
||||
>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
- **Agents**, which are LLMs equipped with instructions and tools
|
||||
- **Handoffs**, which allow agents to delegate to other agents for specific tasks
|
||||
- **Guardrails**, which enable the inputs to agents to be validated
|
||||
<h2>🎯 Milestones</h2>
|
||||
<p>
|
||||
<a href="https://app.hackthebox.com/users/2268644"><img src="https://img.shields.io/badge/HTB_ranking-top_90_Spain_(5_days)-red.svg" alt="HTB top 90 Spain (5 days)"></a>
|
||||
<a href="https://app.hackthebox.com/users/2268644"><img src="https://img.shields.io/badge/HTB_ranking-top_50_Spain_(6_days)-red.svg" alt="HTB ranking top 50 Spain (6 days)"></a>
|
||||
<a href="https://app.hackthebox.com/users/2268644"><img src="https://img.shields.io/badge/HTB_ranking-top_30_Spain_(7_days)-red.svg" alt="HTB ranking top 30 Spain (7 days)"></a>
|
||||
<a href="https://app.hackthebox.com/users/2268644"><img src="https://img.shields.io/badge/HTB_ranking-top_500_World_(7_days)-red.svg" alt="HTB ranking top 500 World (7 days)"></a>
|
||||
<a href="https://ctf.hackthebox.com/event/2000/scoreboard"><img src="https://img.shields.io/badge/HTB_Human_vs_AI_CTF-top_1_(AIs)_world-red.svg" alt="HTB Human vs AI CTF top 1 (AIs) world"></a>
|
||||
<a href="https://ctf.hackthebox.com/event/2000/scoreboard"><img src="https://img.shields.io/badge/HTB_Human_vs_AI_CTF-top_1_Spain-red.svg" alt="HTB Human vs AI CTF top 1 Spain"></a>
|
||||
<a href="https://ctf.hackthebox.com/event/2000/scoreboard"><img src="https://img.shields.io/badge/HTB_Human_vs_AI_CTF-top_20_World-red.svg" alt="HTB Human vs AI CTF top 20 World"></a>
|
||||
<a href="https://ctf.hackthebox.com/event/2000/scoreboard"><img src="https://img.shields.io/badge/HTB_Human_vs_AI_CTF-750_$-yellow.svg" alt="HTB Human vs AI CTF 750 $"></a>
|
||||
<a href="https://lu.ma/roboticshack?tk=RuryKF"><img src="https://img.shields.io/badge/Mistral_AI_Robotics_Hackathon-2500_$-yellow.svg" alt="Mistral AI Robotics Hackathon 2500 $"></a>
|
||||
<a href="https://github.com/aliasrobotics/cai"><img src="https://img.shields.io/badge/Bug_rewards-250_$-yellow.svg" alt="Bug rewards 250 $"></a>
|
||||
</p>
|
||||
|
||||
In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application.
|
||||
<h2>📦 Package Attributes</h2>
|
||||
<p>
|
||||
<a href="https://badge.fury.io/py/cai-framework"><img src="https://badge.fury.io/py/cai-framework.svg" alt="version"></a>
|
||||
<a href="https://pypistats.org/packages/cai-framework"><img src="https://img.shields.io/pypi/dm/cai-framework" alt="downloads"></a>
|
||||
<a href="https://github.com/aliasrobotics/cai"><img src="https://img.shields.io/badge/Linux-Supported-brightgreen?logo=linux&logoColor=white" alt="Linux"></a>
|
||||
<a href="https://github.com/aliasrobotics/cai"><img src="https://img.shields.io/badge/OS%20X-Supported-brightgreen?logo=apple&logoColor=white" alt="OS X"></a>
|
||||
<a href="https://github.com/aliasrobotics/cai"><img src="https://img.shields.io/badge/Windows-Supported-brightgreen?logo=windows&logoColor=white" alt="Windows"></a>
|
||||
<a href="https://github.com/aliasrobotics/cai"><img src="https://img.shields.io/badge/Android-Supported-brightgreen?logo=android&logoColor=white" alt="Android"></a>
|
||||
<a href="https://discord.gg/fnUFcTaQAC"><img src="https://img.shields.io/badge/Discord-7289DA?logo=discord&logoColor=white" alt="Discord"></a>
|
||||
<a href="https://arxiv.org/pdf/2504.06017"><img src="https://img.shields.io/badge/arXiv-2504.06017-b31b1b.svg" alt="arXiv"></a>
|
||||
</p>
|
||||
|
||||
## Why use the Agents SDK
|
||||
</div>
|
||||
|
||||
The SDK has two driving design principles:
|
||||
A lightweight, ergonomic framework for building bug bounty-ready Cybersecurity AIs (CAIs).
|
||||
|
||||
1. Enough features to be worth using, but few enough primitives to make it quick to learn.
|
||||
2. Works great out of the box, but you can customize exactly what happens.
|
||||
| CAI on JWT@PortSwigger CTF — Cybersecurity AI | CAI on HackableII Boot2Root CTF — Cybersecurity AI |
|
||||
|-----------------------------------------------|---------------------------------|
|
||||
| [](https://asciinema.org/a/713487) | [](https://asciinema.org/a/713485) |
|
||||
|
||||
Here are the main features of the SDK:
|
||||
|
||||
- Agent loop: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done.
|
||||
- Python-first: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions.
|
||||
- Handoffs: A powerful feature to coordinate and delegate between multiple agents.
|
||||
- Guardrails: Run input validations and checks in parallel to your agents, breaking early if the checks fail.
|
||||
- Function tools: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation.
|
||||
- Tracing: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the OpenAI suite of evaluation, fine-tuning and distillation tools.
|
||||
> ⚠️ CAI is in active development, so don't expect it to work flawlessly. Instead, contribute by raising an issue or [sending a PR](https://github.com/aliasrobotics/cai/pulls).
|
||||
>
|
||||
> Access to this library and the use of information, materials (or portions thereof), is **<u>not intended</u>, and is <u>prohibited</u>, where such access or use violates applicable laws or regulations**. By no means the authors encourage or promote the unauthorized tampering with running systems. This can cause serious human harm and material damages.
|
||||
>
|
||||
> *By no means the authors of CAI encourage or promote the unauthorized tampering with compute systems. Please don't use the source code in here for cybercrime. <u>Pentest for good instead</u>*. By downloading, using, or modifying this source code, you agree to the terms of the [`LICENSE`](LICENSE) and the limitations outlined in the [`DISCLAIMER`](DISCLAIMER) file.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install openai-agents
|
||||
```
|
||||
## Motivation
|
||||
### Why CAI?
|
||||
The cybersecurity landscape is undergoing a dramatic transformation as AI becomes increasingly integrated into security operations. **We predict that by 2028, AI-powered security testing tools will outnumber human pentesters**. This shift represents a fundamental change in how we approach cybersecurity challenges. *AI is not just another tool - it's becoming essential for addressing complex security vulnerabilities and staying ahead of sophisticated threats. As organizations face more advanced cyber attacks, AI-enhanced security testing will be crucial for maintaining robust defenses.*
|
||||
|
||||
## Hello world example
|
||||
This work builds upon prior efforts[1] and similarly, we believe that democratizing access to advanced cybersecurity AI tools is vital for the entire security community. That's why we're releasing Cybersecurity AI (`CAI`) as an open source framework. Our goal is to empower security researchers, ethical hackers, and organizations to build and deploy powerful AI-driven security tools. By making these capabilities openly available, we aim to level the playing field and ensure that cutting-edge security AI technology isn't limited to well-funded private companies or state actors.
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, Runner
|
||||
Bug Bounty programs have become a cornerstone of modern cybersecurity, providing a crucial mechanism for organizations to identify and fix vulnerabilities in their systems before they can be exploited. These programs have proven highly effective at securing both public and private infrastructure, with researchers discovering critical vulnerabilities that might have otherwise gone unnoticed. CAI is specifically designed to enhance these efforts by providing a lightweight, ergonomic framework for building specialized AI agents that can assist in various aspects of Bug Bounty hunting - from initial reconnaissance to vulnerability validation and reporting. Our framework aims to augment human expertise with AI capabilities, helping researchers work more efficiently and thoroughly in their quest to make digital systems more secure.
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
### Ethical principles behind CAI
|
||||
|
||||
result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
|
||||
print(result.final_output)
|
||||
You might be wondering if releasing CAI *in-the-wild* given its capabilities and security implications is ethical. Our decision to open-source this framework is guided by two core ethical principles:
|
||||
|
||||
# Code within the code,
|
||||
# Functions calling themselves,
|
||||
# Infinite loop's dance.
|
||||
```
|
||||
1. **Democratizing Cybersecurity AI**: We believe that advanced cybersecurity AI tools should be accessible to the entire security community, not just well-funded private companies or state actors. By releasing CAI as an open source framework, we aim to empower security researchers, ethical hackers, and organizations to build and deploy powerful AI-driven security tools, leveling the playing field in cybersecurity.
|
||||
|
||||
(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_)
|
||||
2. **Transparency in AI Security Capabilities**: Based on our research results, understanding of the technology, and dissection of top technical reports, we argue that current LLM vendors are undermining their cybersecurity capabilities. This is extremely dangerous and misleading. By developing CAI openly, we provide a transparent benchmark of what AI systems can actually do in cybersecurity contexts, enabling more informed decisions about security postures.
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=sk-...
|
||||
```
|
||||
CAI is built on the following core principles:
|
||||
|
||||
- **Cybersecurity oriented AI framework**: CAI is specifically designed for cybersecurity use cases, aiming at semi- and fully-automating offensive and defensive security tasks.
|
||||
- **Open source, free for research**: CAI is open source and free for research purposes. We aim at democratizing access to AI and Cybersecurity. For professional or commercial use, including on-premise deployments, dedicated technical support and custom extensions [reach out](mailto:research@aliasrobotics.com) to obtain a license.
|
||||
- **Lightweight**: CAI is designed to be fast, and easy to use.
|
||||
- **Modular and agent-centric design**: CAI operates on the basis of agents and agentic patterns, which allows flexibility and scalability. You can easily add the most suitable agents and pattern for your cybersecurity target case.
|
||||
- **Tool-integration**: CAI integrates already built-in tools, and allows the user to integrate their own tools with their own logic easily.
|
||||
- **Logging and tracing integrated**: using [`phoenix`](https://github.com/Arize-ai/phoenix), the open source tracing and logging tool for LLMs. This provides the user with a detailed traceability of the agents and their execution.
|
||||
- **Multi-Model Support**: more than 300 supported and empowered by [LiteLLM](https://github.com/BerriAI/litellm). The most popular providers:
|
||||
|
||||
### Popular Model Providers
|
||||
* **Anthropic**: `Claude 3.7`, `Claude 3.5`, `Claude 3`, `Claude 3 Opus`
|
||||
* **OpenAI**: `O1`, `O1 Mini`, `O3 Mini`, `GPT-4o`, `GPT-4.5 Preview`
|
||||
* **DeepSeek**: `DeepSeek V3`, `DeepSeek R1`
|
||||
* **Ollama**: `Qwen2.5 72B`, `Qwen2.5 14B`, And many more
|
||||
|
||||
### Closed-source alternatives
|
||||
Cybersecurity AI is a critical field, yet many groups are misguidedly pursuing it through closed-source methods for pure economic return, leveraging similar techniques and building upon existing closed-source (*often third-party owned*) models. This approach not only squanders valuable engineering resources but also represents an economic waste and results in redundant efforts, as they often end up reinventing the wheel. Here are some of the closed-source initiatives we keep track of and attempting to leverage genAI and agentic frameworks in cybersecurity AI:
|
||||
|
||||
- [Runsybil](https://www.runsybil.com)
|
||||
- [Selfhack](https://www.selfhack.fi)
|
||||
- [Sxipher](https://www.sxipher.com/) (seems discontinued)
|
||||
- [Staris](https://staris.tech/)
|
||||
- [Terra Security](https://www.terra.security)
|
||||
- [Xint](https://xint.io/)
|
||||
- [XBOW](https://www.xbow.com)
|
||||
- [ZeroPath](https://www.zeropath.com)
|
||||
- [Zynap](https://www.zynap.com)
|
||||
|
||||
---
|
||||
|
||||
[1] Deng, G., Liu, Y., Mayoral-Vilches, V., Liu, P., Li, Y., Xu, Y., ... & Rass, S. (2024). {PentestGPT}: Evaluating and harnessing large language models for automated penetration testing. In 33rd USENIX Security Symposium (USENIX Security 24) (pp. 847-864).
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# OpenAI Agents SDK
|
||||
|
||||
The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) enables you to build agentic AI apps in a lightweight, easy-to-use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, [Swarm](https://github.com/openai/swarm/tree/main). The Agents SDK has a very small set of primitives:
|
||||
|
||||
- **Agents**, which are LLMs equipped with instructions and tools
|
||||
- **Handoffs**, which allow agents to delegate to other agents for specific tasks
|
||||
- **Guardrails**, which enable the inputs to agents to be validated
|
||||
|
||||
In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application.
|
||||
|
||||
## Why use the Agents SDK
|
||||
|
||||
The SDK has two driving design principles:
|
||||
|
||||
1. Enough features to be worth using, but few enough primitives to make it quick to learn.
|
||||
2. Works great out of the box, but you can customize exactly what happens.
|
||||
|
||||
Here are the main features of the SDK:
|
||||
|
||||
- Agent loop: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done.
|
||||
- Python-first: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions.
|
||||
- Handoffs: A powerful feature to coordinate and delegate between multiple agents.
|
||||
- Guardrails: Run input validations and checks in parallel to your agents, breaking early if the checks fail.
|
||||
- Function tools: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation.
|
||||
- Tracing: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the OpenAI suite of evaluation, fine-tuning and distillation tools.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install openai-agents
|
||||
```
|
||||
|
||||
## Hello world example
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
|
||||
result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
|
||||
print(result.final_output)
|
||||
|
||||
# Code within the code,
|
||||
# Functions calling themselves,
|
||||
# Infinite loop's dance.
|
||||
```
|
||||
|
||||
(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_)
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=sk-...
|
||||
```
|
||||
16
docs/mcp.md
|
|
@ -4,7 +4,7 @@ The [Model context protocol](https://modelcontextprotocol.io/introduction) (aka
|
|||
|
||||
> MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.
|
||||
|
||||
The Agents SDK has support for MCP. This enables you to use a wide range of MCP servers to provide tools to your Agents.
|
||||
MCP enables you to use a wide range of MCP servers to provide tools to your Agents.
|
||||
|
||||
## MCP servers
|
||||
|
||||
|
|
@ -33,9 +33,12 @@ MCP servers can be added to Agents. The Agents SDK will call `list_tools()` on t
|
|||
|
||||
```python
|
||||
|
||||
agent=Agent(
|
||||
name="Assistant",
|
||||
instructions="Use the tools to achieve the task",
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent
|
||||
cybersecurity_lead = Agent(
|
||||
name="Cybersecurity Lead Agent",
|
||||
instructions="Use the tools to solve the",
|
||||
mcp_servers=[mcp_server_1, mcp_server_2]
|
||||
)
|
||||
```
|
||||
|
|
@ -50,11 +53,10 @@ If you want to invalidate the cache, you can call `invalidate_tools_cache()` on
|
|||
|
||||
View complete working examples at [examples/mcp](https://github.com/openai/openai-agents-python/tree/main/examples/mcp).
|
||||
|
||||
## Tracing
|
||||
|
||||
## Tracing
|
||||
[Tracing](./tracing.md) automatically captures MCP operations, including:
|
||||
|
||||
1. Calls to the MCP server to list tools
|
||||
2. MCP-related info on function calls
|
||||
|
||||

|
||||

|
||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 38 MiB |
|
|
@ -73,23 +73,23 @@ async def main():
|
|||
|
||||
with trace(workflow_name="Conversation", group_id=thread_id):
|
||||
# First turn
|
||||
result = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
|
||||
result = await Runner.run(agent, "What is phishing?")
|
||||
print(result.final_output)
|
||||
# San Francisco
|
||||
|
||||
# Expected: A type of cyberattack where users are tricked into giving sensitive info.
|
||||
|
||||
# Second turn
|
||||
new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
|
||||
new_input = result.to_input_list() + [{"role": "user", "content": "How can I protect myself from it?"}]
|
||||
result = await Runner.run(agent, new_input)
|
||||
print(result.final_output)
|
||||
# California
|
||||
# Expected: Use email filters, don't click unknown links, and enable 2FA.
|
||||
```
|
||||
|
||||
## Exceptions
|
||||
|
||||
The SDK raises exceptions in certain cases. The full list is in [`cai.sdk.agents.exceptions`][]. As an overview:
|
||||
|
||||
- [`AgentsException`][cai.sdk.agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK.
|
||||
- [`AgentsException`][cai.sdk.agents.exceptions.AgentsException] is the base class for all exceptions raised.
|
||||
- [`MaxTurnsExceeded`][cai.sdk.agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods.
|
||||
- [`ModelBehaviorError`][cai.sdk.agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools.
|
||||
- [`UserError`][cai.sdk.agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK.
|
||||
- [`UserError`][cai.sdk.agents.exceptions.UserError] is raised when you (the person writing code using CAI) make an error using it .
|
||||
- [`InputGuardrailTripwireTriggered`][cai.sdk.agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][cai.sdk.agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped.
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ from cai.sdk.agents import Agent, Runner
|
|||
async def main():
|
||||
agent = Agent(
|
||||
name="Joker",
|
||||
instructions="You are a helpful assistant.",
|
||||
instructions="CyberGuard.",
|
||||
)
|
||||
|
||||
result = Runner.run_streamed(agent, input="Please tell me 5 jokes.")
|
||||
result = Runner.run_streamed(agent, input="Please tell me 5 cybersecurity tips.")
|
||||
async for event in result.stream_events():
|
||||
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
|
||||
print(event.data.delta, end="", flush=True)
|
||||
|
|
@ -43,15 +43,15 @@ import random
|
|||
from cai.sdk.agents import Agent, ItemHelpers, Runner, function_tool
|
||||
|
||||
@function_tool
|
||||
def how_many_jokes() -> int:
|
||||
def how_many_tips() -> int:
|
||||
return random.randint(1, 10)
|
||||
|
||||
|
||||
async def main():
|
||||
agent = Agent(
|
||||
name="Joker",
|
||||
instructions="First call the `how_many_jokes` tool, then tell that many jokes.",
|
||||
tools=[how_many_jokes],
|
||||
instructions="First call the `how_many_tips` tool, then tell that many cybersecurity tips.",
|
||||
tools=[how_many_tips],
|
||||
)
|
||||
|
||||
result = Runner.run_streamed(
|
||||
|
|
|
|||
|
|
@ -1,75 +1,3 @@
|
|||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Regular.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-RegularItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Medium.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-MediumItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Semibold.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-SemiboldItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Bold.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-BoldItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
/*
|
||||
Root variables that apply to all color schemes.
|
||||
Material for MkDocs automatically switches data-md-color-scheme
|
||||
|
|
@ -77,10 +5,8 @@
|
|||
*/
|
||||
:root {
|
||||
/* Font families */
|
||||
--md-text-font: "OpenAI Sans", -apple-system, system-ui, Helvetica, Arial,
|
||||
sans-serif;
|
||||
--md-typeface-heading: "OpenAI Sans", -apple-system, system-ui, Helvetica,
|
||||
Arial, sans-serif;
|
||||
--md-text-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
--md-typeface-heading: "Playfair Display", Georgia, "Times New Roman", serif;
|
||||
|
||||
/* Global color variables */
|
||||
--md-default-fg-color: #212121;
|
||||
|
|
@ -89,7 +15,7 @@
|
|||
--md-accent-fg-color: #000;
|
||||
|
||||
/* Code block theming */
|
||||
--md-code-fg-color: red;
|
||||
--md-code-fg-color: #333;
|
||||
--md-code-bg-color: #f5f5f5;
|
||||
|
||||
/* Tables, blockquotes, etc. */
|
||||
|
|
@ -104,6 +30,10 @@
|
|||
--md-code-fg-color: #000;
|
||||
}
|
||||
|
||||
/* Add elegant font imports */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap');
|
||||
|
||||
/* Header styling */
|
||||
.md-header {
|
||||
background-color: #000;
|
||||
|
|
@ -115,15 +45,20 @@
|
|||
|
||||
.md-content .md-typeset h1 {
|
||||
color: #000;
|
||||
font-size: 2.5rem;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.md-typeset p,
|
||||
.md-typeset li {
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.md-typeset__table p {
|
||||
line-height: 1em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.md-nav {
|
||||
|
|
@ -132,6 +67,7 @@
|
|||
.md-nav__title {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.md-typeset h1,
|
||||
|
|
@ -141,6 +77,19 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md-typeset h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.md-typeset h3 {
|
||||
font-size: 1.4rem;
|
||||
margin-top: 1.5rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.md-typeset h1 code {
|
||||
color: #000;
|
||||
padding: 0;
|
||||
|
|
@ -164,19 +113,24 @@
|
|||
|
||||
.md-typeset pre > code {
|
||||
font-size: 14px;
|
||||
font-family: "Fira Code", "JetBrains Mono", Consolas, monospace;
|
||||
}
|
||||
|
||||
.md-typeset__table code {
|
||||
font-size: 14px;
|
||||
font-family: "Fira Code", "JetBrains Mono", Consolas, monospace;
|
||||
}
|
||||
|
||||
/* Custom link styling */
|
||||
.md-content a {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.md-content a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
|
|
@ -192,3 +146,6 @@
|
|||
.md-sidebar__scrollwrap {
|
||||
scrollbar-color: auto !important;
|
||||
}
|
||||
|
||||
/* Add monospace font for code */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,194 @@
|
|||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Regular.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-RegularItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Medium.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-MediumItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Semibold.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-SemiboldItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Bold.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: "OpenAI Sans";
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-BoldItalic.woff2")
|
||||
format("woff2");
|
||||
}
|
||||
|
||||
/*
|
||||
Root variables that apply to all color schemes.
|
||||
Material for MkDocs automatically switches data-md-color-scheme
|
||||
between "default" (light) and "slate" (dark) when you use the toggles.
|
||||
*/
|
||||
:root {
|
||||
/* Font families */
|
||||
--md-text-font: "OpenAI Sans", -apple-system, system-ui, Helvetica, Arial,
|
||||
sans-serif;
|
||||
--md-typeface-heading: "OpenAI Sans", -apple-system, system-ui, Helvetica,
|
||||
Arial, sans-serif;
|
||||
|
||||
/* Global color variables */
|
||||
--md-default-fg-color: #212121;
|
||||
--md-default-bg-color: #ffffff;
|
||||
--md-primary-fg-color: #000;
|
||||
--md-accent-fg-color: #000;
|
||||
|
||||
/* Code block theming */
|
||||
--md-code-fg-color: red;
|
||||
--md-code-bg-color: #f5f5f5;
|
||||
|
||||
/* Tables, blockquotes, etc. */
|
||||
--md-table-row-border-color: #e0e0e0;
|
||||
--md-admonition-bg-color: #f8f8f8;
|
||||
--md-admonition-title-fg-color: #373737;
|
||||
--md-default-fg-color--light: #000;
|
||||
|
||||
--md-typeset-a-color: #000;
|
||||
--md-accent-fg-color: #000;
|
||||
|
||||
--md-code-fg-color: #000;
|
||||
}
|
||||
|
||||
/* Header styling */
|
||||
.md-header {
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.md-header--shadow {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.md-content .md-typeset h1 {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.md-typeset p,
|
||||
.md-typeset li {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.md-typeset__table p {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.md-nav {
|
||||
font-size: 14px;
|
||||
}
|
||||
.md-nav__title {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md-typeset h1,
|
||||
.md-typeset h2,
|
||||
.md-typeset h3,
|
||||
.md-typeset h4 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md-typeset h1 code {
|
||||
color: #000;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.md-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md-header__title {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.md-typeset .admonition,
|
||||
.md-typeset details {
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md-typeset pre > code {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.md-typeset__table code {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Custom link styling */
|
||||
.md-content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.md-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
.md-content .md-code__content {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.md-clipboard.md-icon {
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
/* Reset scrollbar styling to browser default with high priority */
|
||||
.md-sidebar__scrollwrap {
|
||||
scrollbar-color: auto !important;
|
||||
}
|
||||
274
docs/tools.md
|
|
@ -1,41 +1,48 @@
|
|||
# Tools
|
||||
|
||||
Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK:
|
||||
Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the CAI Agents
|
||||
|
||||
- Hosted tools: these run on LLM servers alongside the AI models. OpenAI offers retrieval, web search and computer use as hosted tools.
|
||||
- Hosted tools: these run on LLM servers alongside the AI models. CAI offers some [tools](src/cai/tools)
|
||||
- Function calling: these allow you to use any Python function as a tool.
|
||||
- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them.
|
||||
|
||||
## Hosted tools
|
||||
|
||||
OpenAI offers a few built-in tools when using the [`OpenAIResponsesModel`][cai.sdk.agents.models.openai_responses.OpenAIResponsesModel]:
|
||||
|
||||
- The [`WebSearchTool`][cai.sdk.agents.tool.WebSearchTool] lets an agent search the web.
|
||||
- The [`FileSearchTool`][cai.sdk.agents.tool.FileSearchTool] allows retrieving information from your OpenAI Vector Stores.
|
||||
- The [`ComputerTool`][cai.sdk.agents.tool.ComputerTool] allows automating computer use tasks.
|
||||
CAI offers a few built-in tools when using the [`OpenAIResponsesModel`][cai.sdk.agents.models.openai_responses.OpenAIResponsesModel]. They are in [tools](src/cai/tools) and grouped in 6 major categories inspired by the <span style="color: red;">security kill chain[^2]<span>:
|
||||
s
|
||||
|
||||
1. Reconnaissance and weaponization - *reconnaissance* (crypto, listing, etc)
|
||||
2. Exploitation - *exploitation*
|
||||
3. Privilege escalation - *escalation*
|
||||
4. Lateral movement - *lateral*
|
||||
5. Data exfiltration - *exfiltration*
|
||||
6. Command and control - *control*
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, FileSearchTool, Runner, WebSearchTool
|
||||
from cai.sdk.agents import Agent, Runner, OpenAIChatCompletionsModel
|
||||
from cai.tools.reconnaissance.generic_linux_command import generic_linux_command
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
one_tool_agent = Agent(
|
||||
name="CTF agent",
|
||||
description="Agent focused on listing directories",
|
||||
instructions="You are a Cybersecurity expert Leader facing a CTF challenge.",
|
||||
tools=[
|
||||
WebSearchTool(),
|
||||
FileSearchTool(
|
||||
max_num_results=3,
|
||||
vector_store_ids=["VECTOR_STORE_ID"],
|
||||
),
|
||||
generic_linux_command,
|
||||
],
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model="qwen2.5:14b",
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?")
|
||||
result = await Runner.run(one_tool_agent, "List all directories")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
## Function tools
|
||||
|
||||
You can use any Python function as a tool. The Agents SDK will setup the tool automatically:
|
||||
You can use any Python function as a tool. The CAI will setup the tool automatically:
|
||||
|
||||
- The name of the tool will be the name of the Python function (or you can provide a name)
|
||||
- Tool description will be taken from the docstring of the function (or you can provide a description)
|
||||
|
|
@ -46,52 +53,54 @@ We use Python's `inspect` module to extract the function signature, along with [
|
|||
|
||||
```python
|
||||
import json
|
||||
|
||||
from typing_extensions import TypedDict, Any
|
||||
from cai.sdk.agents import Agent, FunctionTool, RunContextWrapper, function_tool, OpenAIChatCompletionsModel
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
from cai.sdk.agents import Agent, FunctionTool, RunContextWrapper, function_tool
|
||||
class IPAddress(TypedDict):
|
||||
ip: str
|
||||
|
||||
|
||||
class Location(TypedDict):
|
||||
lat: float
|
||||
long: float
|
||||
|
||||
@function_tool # (1)!
|
||||
async def fetch_weather(location: Location) -> str:
|
||||
# (2)!
|
||||
"""Fetch the weather for a given location.
|
||||
@function_tool
|
||||
async def check_ip_reputation(ip_data: IPAddress) -> str:
|
||||
"""Check if an IP address has a bad reputation.
|
||||
|
||||
Args:
|
||||
location: The location to fetch the weather for.
|
||||
ip_data: A dictionary with the IP address to check.
|
||||
"""
|
||||
# In real life, we'd fetch the weather from a weather API
|
||||
return "sunny"
|
||||
# In a real system, this would query an IP reputation API
|
||||
return "malicious" if ip_data["ip"].startswith("192.168") else "clean"
|
||||
|
||||
|
||||
@function_tool(name_override="fetch_data") # (3)!
|
||||
def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str:
|
||||
"""Read the contents of a file.
|
||||
@function_tool(name_override="read_log_file")
|
||||
def read_log_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str:
|
||||
"""Read the contents of a log file.
|
||||
|
||||
Args:
|
||||
path: The path to the file to read.
|
||||
directory: The directory to read the file from.
|
||||
path: The path to the log file.
|
||||
directory: The optional directory to search in.
|
||||
"""
|
||||
# In real life, we'd read the file from the file system
|
||||
return "<file contents>"
|
||||
# In a real system, this would read from the filesystem logs
|
||||
return "<log file contents: suspicious activity found>"
|
||||
|
||||
|
||||
# Create the cybersecurity agent
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
tools=[fetch_weather, read_file], # (4)!
|
||||
name="CyberSecBot",
|
||||
tools=[check_ip_reputation, read_log_file],
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model="qwen2.5:14b",
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
# Display metadata for each available tool
|
||||
for tool in agent.tools:
|
||||
if isinstance(tool, FunctionTool):
|
||||
print(tool.name)
|
||||
print(tool.description)
|
||||
print(json.dumps(tool.params_json_schema, indent=2))
|
||||
print()
|
||||
|
||||
```
|
||||
|
||||
1. You can use any Python types as arguments to your functions, and the function can be sync or async.
|
||||
|
|
@ -102,70 +111,79 @@ for tool in agent.tools:
|
|||
??? note "Expand to see output"
|
||||
|
||||
```
|
||||
fetch_weather
|
||||
Fetch the weather for a given location.
|
||||
check_ip_reputation
|
||||
Check if an IP address has a bad reputation.
|
||||
{
|
||||
"$defs": {
|
||||
"Location": {
|
||||
"properties": {
|
||||
"lat": {
|
||||
"title": "Lat",
|
||||
"type": "number"
|
||||
"$defs": {
|
||||
"IPAddress": {
|
||||
"properties": {
|
||||
"ip": {
|
||||
"title": "Ip",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"long": {
|
||||
"title": "Long",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"lat",
|
||||
"long"
|
||||
],
|
||||
"title": "Location",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"location": {
|
||||
"$ref": "#/$defs/Location",
|
||||
"description": "The location to fetch the weather for."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"title": "fetch_weather_args",
|
||||
"type": "object"
|
||||
"required": [
|
||||
"ip"
|
||||
],
|
||||
"title": "IPAddress",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"ip_data": {
|
||||
"description": "A dictionary with the IP address to check.",
|
||||
"properties": {
|
||||
"ip": {
|
||||
"title": "Ip",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"ip"
|
||||
],
|
||||
"title": "IPAddress",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"ip_data"
|
||||
],
|
||||
"title": "check_ip_reputation_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
fetch_data
|
||||
Read the contents of a file.
|
||||
read_log_file
|
||||
Read the contents of a log file.
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"description": "The path to the file to read.",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
"properties": {
|
||||
"path": {
|
||||
"description": "The path to the log file.",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
},
|
||||
"directory": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "The optional directory to search in.",
|
||||
"title": "Directory"
|
||||
}
|
||||
},
|
||||
"directory": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "The directory to read the file from.",
|
||||
"title": "Directory"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "fetch_data_args",
|
||||
"type": "object"
|
||||
"required": [
|
||||
"path",
|
||||
"directory"
|
||||
],
|
||||
"title": "read_log_file_args",
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -180,17 +198,13 @@ Sometimes, you don't want to use a Python function as a tool. You can directly c
|
|||
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from cai.sdk.agents import RunContextWrapper, FunctionTool
|
||||
|
||||
|
||||
|
||||
def do_some_work(data: str) -> str:
|
||||
return "done"
|
||||
|
||||
|
||||
class FunctionArgs(BaseModel):
|
||||
username: str
|
||||
age: int
|
||||
|
|
@ -223,40 +237,58 @@ The code for the schema extraction lives in [`cai.sdk.agents.function_schema`][]
|
|||
In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools.
|
||||
|
||||
```python
|
||||
from cai.sdk.agents import Agent, Runner
|
||||
from cai.sdk.agents import Agent, Runner, OpenAIChatCompletionsModel
|
||||
from openai import AsyncOpenAI
|
||||
import asyncio
|
||||
|
||||
spanish_agent = Agent(
|
||||
name="Spanish agent",
|
||||
instructions="You translate the user's message to Spanish",
|
||||
# Agent that simulates scanning an IP for threats
|
||||
ip_scanner_agent = Agent(
|
||||
name="IP Scanner",
|
||||
instructions="You receive an IP address and respond with its threat status (e.g., malicious or clean).",
|
||||
)
|
||||
|
||||
french_agent = Agent(
|
||||
name="French agent",
|
||||
instructions="You translate the user's message to French",
|
||||
# Agent that simulates analyzing a log file
|
||||
log_analyzer_agent = Agent(
|
||||
name="Log Analyzer",
|
||||
instructions="You receive a log file path and respond with any suspicious findings from the logs.",
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model="qwen2.5:14b",
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
orchestrator_agent = Agent(
|
||||
name="orchestrator_agent",
|
||||
# Orchestrator agent that routes cybersecurity tasks to the correct tool
|
||||
cyber_orchestrator_agent = Agent(
|
||||
name="Cyber Orchestrator",
|
||||
instructions=(
|
||||
"You are a translation agent. You use the tools given to you to translate."
|
||||
"If asked for multiple translations, you call the relevant tools."
|
||||
"You are a cybersecurity assistant. Based on the user's request, you decide whether to scan an IP or analyze a log. "
|
||||
"Use the appropriate tool for each task."
|
||||
),
|
||||
tools=[
|
||||
spanish_agent.as_tool(
|
||||
tool_name="translate_to_spanish",
|
||||
tool_description="Translate the user's message to Spanish",
|
||||
ip_scanner_agent.as_tool(
|
||||
tool_name="scan_ip",
|
||||
tool_description="Scan an IP address for possible threats",
|
||||
),
|
||||
french_agent.as_tool(
|
||||
tool_name="translate_to_french",
|
||||
tool_description="Translate the user's message to French",
|
||||
log_analyzer_agent.as_tool(
|
||||
tool_name="analyze_log",
|
||||
tool_description="Analyze a system log file for suspicious activity",
|
||||
),
|
||||
],
|
||||
model=OpenAIChatCompletionsModel(
|
||||
model="qwen2.5:14b",
|
||||
openai_client=AsyncOpenAI(),
|
||||
)
|
||||
)
|
||||
|
||||
# Main function that asks the orchestrator to scan an IP
|
||||
async def main():
|
||||
result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.")
|
||||
# Example input to scan an IP
|
||||
result = await Runner.run(cyber_orchestrator_agent, input="Scan the IP address 192.168.0.10 for threats.")
|
||||
print(result.final_output)
|
||||
|
||||
# Run the asynchronous main function
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Handling errors in function tools
|
||||
|
|
|
|||
36
mkdocs.yml
|
|
@ -17,16 +17,21 @@ theme:
|
|||
palette:
|
||||
primary: black
|
||||
logo: assets/imago.png
|
||||
favicon: images/favicon-platform.svg
|
||||
#favicon: images/favicon-platform.svg
|
||||
nav:
|
||||
- Intro: cai.md
|
||||
- SDK:
|
||||
- Intro: index.md
|
||||
- Quickstart: quickstart.md
|
||||
- Examples: examples.md
|
||||
- Introduction: index.md
|
||||
- Installation: cai_installation.md
|
||||
- Quickstart: cai_quickstart.md
|
||||
- List of Models: cai_list_of_models.md
|
||||
- Architecture: cai_architecture.md
|
||||
- Development: cai_development.md
|
||||
- Start Building:
|
||||
#- Intro: index.md
|
||||
#- Quickstart: quickstart.md
|
||||
#- Examples: examples.md
|
||||
- Documentation:
|
||||
- agents.md
|
||||
- running_agents.md
|
||||
- agents.md #
|
||||
- running_agents.md
|
||||
- results.md
|
||||
- streaming.md
|
||||
- tools.md
|
||||
|
|
@ -35,10 +40,10 @@ nav:
|
|||
- tracing.md
|
||||
- context.md
|
||||
- guardrails.md
|
||||
- multi_agent.md
|
||||
- models.md
|
||||
- config.md
|
||||
- visualization.md
|
||||
#- multi_agent.md
|
||||
#- models.md
|
||||
#- config.md
|
||||
#- visualization.md
|
||||
- Voice agents:
|
||||
- voice/quickstart.md
|
||||
- voice/pipeline.md
|
||||
|
|
@ -93,7 +98,10 @@ nav:
|
|||
- Extensions:
|
||||
- ref/extensions/handoff_filters.md
|
||||
- ref/extensions/handoff_prompt.md
|
||||
|
||||
- More About CAI:
|
||||
- FAQ: cai_faq.md
|
||||
- Find Us: cai_find_us.md
|
||||
- Citation & Acknowledgments: cai_citation_and_acknowledgments.md
|
||||
plugins:
|
||||
- search
|
||||
- autorefs
|
||||
|
|
@ -128,6 +136,7 @@ markdown_extensions:
|
|||
class: mermaid
|
||||
format: pymdownx.superfences.fence_code_format
|
||||
- admonition
|
||||
- codehilite
|
||||
- pymdownx.details
|
||||
- attr_list
|
||||
- md_in_html
|
||||
|
|
@ -137,6 +146,7 @@ markdown_extensions:
|
|||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.superfences
|
||||
- pymdownx.details
|
||||
|
||||
validation:
|
||||
omitted_files: warn
|
||||
|
|
|
|||