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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions agentex/docs/docs/agent_types/agent_type_migration_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Migration Guide: Converting Between ACP Types

This guide provides comprehensive, step-by-step instructions for migrating agents between complexity levels as your requirements evolve. Each migration path includes real examples, common challenges, and testing strategies.

## Migration Paths Overview

| From | To | When to Migrate | Complexity Increase |
|------|----|-----------------|--------------------|
| **Sync** | **Agentic (Base)** | Need to change from a synchronous blocking agent to a session-based asynchronous agent. | Small |
| **Agentic (Base)** | **Agentic (Temporal)** | Need to build a long-running agent<br>• Handle complex multi-step / transactional tools<br>• Easier state management with just class variables<br>• Better race condition handling<br>• Better batch processing | Medium |

## Part 1: Sync ACP → Agentic (Base) ACP

Use `agentex init` to create a new project directory (select "Agentic (Base)" when prompted for ACP type), then migrate your code from the single `@acp.on_message_send` handler to the three Agentic (Base) handlers:

- `@acp.on_task_create` - Initialize state or send welcome messages
- `@acp.on_task_event_send` - Your main message processing logic (migrated from `on_message_send`)
- `@acp.on_task_cancel` - Cleanup when tasks end

**Key change:** You must manually create messages using `adk.messages.create()` instead of returning them.

### Before (Sync)

```python
@acp.on_message_send
async def handle_message_send(params: SendMessageParams):
# Your logic here
response = process_user_input(params.content.content)
return TextContent(author=MessageAuthor.AGENT, content=response)
```

### After (Agentic Base)

```python
@acp.on_task_create
async def handle_task_create(params: CreateTaskParams):
# Optional: initialize state, send welcome message
pass

@acp.on_task_event_send
async def handle_event_send(params: SendEventParams):
# Your logic here (migrated from on_message_send)
response = process_user_input(params.event.content.content)

# Manually create message (new requirement)
await adk.messages.create(
task_id=params.task.id,
content=TextContent(author=MessageAuthor.AGENT, content=response)
)

@acp.on_task_cancel
async def handle_task_cancel(params: CancelTaskParams):
# Optional: cleanup resources
pass
```

## Part 2: Agentic (Base) ACP → Agentic (Temporal) ACP

Use `agentex init` to create a new project directory (select "Agentic (Temporal)" when prompted for ACP type), then migrate your three handlers into a Temporal workflow:

- `@acp.on_task_create` → `@workflow.run` (workflow initialization)
- `@acp.on_task_event_send` → `@workflow.signal` (event processing)
- `@acp.on_task_cancel` → Handled automatically by Temporal

**Key changes:**
- State becomes class variables on the workflow
- Wrap API calls and side effects in Temporal activities for automatic retries
- Workflow state persists across server restarts

### Before (Agentic Base)

```python
@acp.on_task_create
async def handle_task_create(params: CreateTaskParams):
await adk.state.create(...)

@acp.on_task_event_send
async def handle_event_send(params: SendEventParams):
# Direct API calls
result = await external_api.call(params.event.content)
await adk.messages.create(task_id=params.task.id, content=result)

@acp.on_task_cancel
async def handle_task_cancel(params: CancelTaskParams):
# Cleanup
pass
```

### After (Agentic Temporal)

```python
@workflow.defn
class MyAgentWorkflow(BaseWorkflow):
def __init__(self):
super().__init__()
self.state = {} # State as class variables

@workflow.run
async def on_task_create(self, params: CreateTaskParams):
# Initialization logic
self.state["initialized"] = True

# Wait for events
await workflow.wait_condition(lambda: self._complete_task)

@workflow.signal(name=SignalName.RECEIVE_EVENT)
async def on_task_event_send(self, params: SendEventParams):
# Wrap API calls in activities (automatic retries)
result = await workflow.execute_activity(
external_api_activity,
params.event.content,
start_to_close_timeout=timedelta(minutes=5)
)

await workflow.execute_activity(
create_message_activity,
CreateMessageArgs(task_id=params.task.id, content=result),
start_to_close_timeout=timedelta(seconds=30)
)

# Define activities for side effects
@activity.defn
async def external_api_activity(content):
return await external_api.call(content)

@activity.defn
async def create_message_activity(args):
await adk.messages.create(task_id=args.task_id, content=args.content)
```

---

## Summary

Migration between ACP types follows clear patterns. Test your migrated agent with the same inputs to ensure equivalent behavior before deploying to production.
106 changes: 106 additions & 0 deletions agentex/docs/docs/agent_types/agentic/base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Base Agentic ACP

**Base Agentic ACP** is the foundational asynchronous model for Agentex. It gives you full control over the task lifecycle with three handlers while Agentex takes care of transport, streaming, and message delivery.

## Core Characteristics

- **Three handler methods**
- `@acp.on_task_create` – initialize state or send a welcome message
- `@acp.on_task_event_send` – process each incoming event/message
- `@acp.on_task_cancel` – cleanup when a task is cancelled
- **Explicit message creation** – create all messages via `adk.messages.create()`
- **Asynchronous and concurrent** – multiple requests can be in-flight; your code should be async-safe
- **State management available** – use `adk.state` when you need persistence across events
- **No durability guarantees** – crashes and retries are your responsibility (see Temporal for durability)

## Message Flow

```mermaid
sequenceDiagram
participant Client
participant Agentex
participant Agent

Client->>Agentex: Create Task
Agentex->>Agent: on_task_create(params)
Agent->>Agentex: adk.messages.create(...)

Client->>Agentex: Send Event
Agentex->>Agent: on_task_event_send(params)
Agent->>Agent: Process logic / update state
Agent->>Agentex: adk.messages.create(...)

Client->>Agentex: Cancel Task
Agentex->>Agent: on_task_cancel(params)
```

## Basic Implementation

```python
from agentex.lib.sdk.fastacp.fastacp import FastACP
from agentex.lib.types.fastacp import AgenticACPConfig
from agentex.lib.types.acp import CreateTaskParams, SendEventParams, CancelTaskParams
from agentex.lib import adk

# Create Base Agentic ACP server
acp = FastACP.create(
acp_type="agentic",
config=AgenticACPConfig(type="base")
)

@acp.on_task_create
async def handle_task_create(params: CreateTaskParams) -> None:
# Optionally initialize state, then send a message
await adk.messages.create(
task_id=params.task.id,
agent_id=params.agent.id,
content="Welcome!"
)

@acp.on_task_event_send
async def handle_task_event_send(params: SendEventParams) -> None:
# Process the incoming event and respond
await adk.messages.create(
task_id=params.task.id,
agent_id=params.agent.id,
content=f"You said: {params.event.content}"
)

@acp.on_task_cancel
async def handle_task_cancel(params: CancelTaskParams) -> None:
# Cleanup or finalization logic
pass
```

## Handler Parameters

### CreateTaskParams

Used in `@acp.on_task_create` for task initialization:

::: agentex.lib.types.acp.CreateTaskParams
options:
heading_level: 4
show_root_heading: false
show_source: false

### SendEventParams

Used in `@acp.on_task_event_send` for processing events:

::: agentex.lib.types.acp.SendEventParams
options:
heading_level: 4
show_root_heading: false
show_source: false

### CancelTaskParams

Used in `@acp.on_task_cancel` for cleanup:

::: agentex.lib.types.acp.CancelTaskParams
options:
heading_level: 4
show_root_heading: false
show_source: false

90 changes: 90 additions & 0 deletions agentex/docs/docs/agent_types/agentic/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Agentic ACP

**Agentic ACP** is the powerful, event-driven approach for complex agent interactions. It provides complete control over task lifecycle, state management, and workflows through three distinct handlers: [`on_task_create`](https://github.com/scaleapi/scale-agentex-python/blob/main/src/agentex/lib/sdk/fastacp/base/base_acp_server.py#L314), [`on_task_event_send`](https://github.com/scaleapi/scale-agentex-python/blob/main/src/agentex/lib/sdk/fastacp/base/base_acp_server.py#L321), [`on_task_cancel`](https://github.com/scaleapi/scale-agentex-python/blob/main/src/agentex/lib/sdk/fastacp/base/base_acp_server.py#L339).

## Core Architecture

Both Base and Temporal Agentic ACP share the same three-handler pattern:

```python
@acp.on_task_create
async def handle_task_create(params: CreateTaskParams):
"""Initialize new tasks - setup state, send welcome messages"""
pass

@acp.on_task_event_send
async def handle_event_send(params: SendEventParams):
"""Process events during task lifetime - core business logic"""
pass

@acp.on_task_cancel
async def handle_task_cancel(params: CancelTaskParams):
"""Clean up when tasks end - archive data, release resources"""
pass
```

## Task Lifecycle

```mermaid
graph LR
subgraph CA["Client Actions"]
direction TB
A[Client Creates Task]
E[Client Subscribes to Task]
K[Client Cancels Task]
F[Client Sends Event]
end

subgraph AH["Agent Handlers"]
direction TB
B[on_task_create]
G[on_task_event_send]
L[on_task_cancel]
end

subgraph AA["Agent Activity"]
direction TB
C[Initialize State]
D[Task Active]
H[Process Event]
I[Send Response Messages]
M[Cleanup Resources]
N[Task Complete]
end

%% lock column spacing
CA ~~~ AH
AH ~~~ AA

A --> B
B --> C
C --> D

F --> G
G --> H
H --> I
I --> E

K --> L
L --> M
M --> N

%% force a curved back-edge from J to E and keep columns
linkStyle 9 interpolate basis,stroke-dasharray:4 4
```

## Asynchronous Event Processing

Think of Agentic ACP like a **postal system for agents** - each agent has its own mailbox where events are delivered asynchronously, and agents decide when and how to process their mail.

### Every Agent Has a Mailbox

Each agent has a **mailbox** where events are delivered and queued:

- Events arrive from other agents, external clients, scheduled tasks, or webhooks
- Events pile up whether the agent is ready or not
- Agents decide when to process accumulated events

### Handling Concurrent Events

Unlike Sync ACP, Agentic ACP doesn't lock or block past requests. If you expect to receive many concurrent requests, you'll need to handle async events yourself. You can use the Agent Task Tracker with Base ACP or asyncio Queues with Temporal to manage event processing. For more information, see [Events vs Messages](../../concepts/callouts/events_vs_messages.md).
Loading