or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-agents.mdguardrails.mdhandoffs.mdindex.mditems-streaming.mdlifecycle.mdmcp.mdmemory-sessions.mdmodel-providers.mdrealtime.mdresults-exceptions.mdtools.mdtracing.mdvoice-pipeline.md

handoffs.mddocs/

0

# Handoffs

1

2

Handoffs enable agent-to-agent delegation in multi-agent workflows. They are implemented as specialized tool calls that transfer control from one agent to another, with support for input filtering, history management, and custom handoff logic.

3

4

## Capabilities

5

6

### Handoff Class

7

8

Configuration for agent handoffs with customizable behavior.

9

10

```python { .api }

11

class Handoff[TContext, TAgent]:

12

"""

13

Agent handoff configuration.

14

15

Type Parameters:

16

- TContext: Type of context object

17

- TAgent: Type of target agent

18

19

Attributes:

20

- tool_name: str - Tool name for handoff

21

- tool_description: str - Tool description for LLM

22

- input_json_schema: dict[str, Any] - Input schema

23

- on_invoke_handoff: Callable - Invocation function

24

- agent_name: str - Target agent name

25

- input_filter: HandoffInputFilter | None - Input filter function

26

- nest_handoff_history: bool | None - History nesting override

27

- strict_json_schema: bool - Use strict JSON schema mode

28

- is_enabled: bool | Callable - Enabled state or function

29

"""

30

31

def get_transfer_message(agent: Agent) -> str:

32

"""

33

Get transfer message for handoff.

34

35

Parameters:

36

- agent: Target agent

37

38

Returns:

39

- str: Transfer message

40

"""

41

42

@classmethod

43

def default_tool_name(agent: Agent) -> str:

44

"""

45

Get default tool name for agent.

46

47

Parameters:

48

- agent: Agent to generate name for

49

50

Returns:

51

- str: Default tool name

52

"""

53

54

@classmethod

55

def default_tool_description(agent: Agent) -> str:

56

"""

57

Get default description for agent.

58

59

Parameters:

60

- agent: Agent to generate description for

61

62

Returns:

63

- str: Default description

64

"""

65

```

66

67

### Handoff Function

68

69

Helper function to create handoffs from agents.

70

71

```python { .api }

72

def handoff(

73

agent: Agent,

74

*,

75

tool_name_override: str | None = None,

76

tool_description_override: str | None = None,

77

on_handoff: Callable | None = None,

78

input_type: type | None = None,

79

input_filter: HandoffInputFilter | None = None,

80

nest_handoff_history: bool | None = None,

81

is_enabled: bool | Callable = True

82

) -> Handoff[TContext, Agent[TContext]]:

83

"""

84

Create handoff from agent.

85

86

Parameters:

87

- agent: Target agent for handoff

88

- tool_name_override: Custom tool name (default: "transfer_to_{agent_name}")

89

- tool_description_override: Custom description

90

- on_handoff: Callback when handoff occurs

91

- input_type: Type for handoff input parameters

92

- input_filter: Function to filter/transform handoff input

93

- nest_handoff_history: Whether to nest history in single message

94

- is_enabled: Whether handoff is enabled or function to determine

95

96

Returns:

97

- Handoff: Configured handoff object

98

"""

99

```

100

101

Usage example:

102

103

```python

104

from agents import Agent, handoff

105

106

support_agent = Agent(

107

name="Support Agent",

108

instructions="Handle customer support requests."

109

)

110

111

sales_agent = Agent(

112

name="Sales Agent",

113

instructions="Handle sales inquiries."

114

)

115

116

# Simple handoff

117

triage_agent = Agent(

118

name="Triage Agent",

119

instructions="Route to appropriate agent.",

120

handoffs=[support_agent, sales_agent]

121

)

122

123

# Custom handoff with callback

124

def on_transfer_to_sales(ctx, agent, input_data):

125

print(f"Transferring to sales with: {input_data}")

126

127

custom_handoff = handoff(

128

sales_agent,

129

tool_name_override="escalate_to_sales",

130

tool_description_override="Escalate to sales team for complex inquiries",

131

on_handoff=on_transfer_to_sales

132

)

133

134

triage_agent = Agent(

135

name="Triage Agent",

136

handoffs=[support_agent, custom_handoff]

137

)

138

```

139

140

### Handoff Input Data

141

142

Data structure containing handoff context and history.

143

144

```python { .api }

145

class HandoffInputData:

146

"""

147

Input data for handoffs.

148

149

Attributes:

150

- input_history: str | tuple[TResponseInputItem, ...] - Input history

151

- pre_handoff_items: tuple[RunItem, ...] - Items before handoff

152

- new_items: tuple[RunItem, ...] - New items including handoff

153

- run_context: RunContextWrapper | None - Run context

154

"""

155

156

def clone(**kwargs) -> HandoffInputData:

157

"""

158

Create modified copy.

159

160

Parameters:

161

- **kwargs: Fields to override

162

163

Returns:

164

- HandoffInputData: New instance with changes

165

"""

166

```

167

168

### Handoff Input Filter

169

170

Function type for filtering handoff inputs.

171

172

```python { .api }

173

HandoffInputFilter = Callable[

174

[HandoffInputData],

175

MaybeAwaitable[HandoffInputData]

176

]

177

```

178

179

Usage example:

180

181

```python

182

from agents import Agent, handoff, HandoffInputData

183

184

async def filter_sensitive_data(data: HandoffInputData) -> HandoffInputData:

185

"""Remove sensitive information before handoff."""

186

# Filter history

187

filtered_history = [

188

item for item in data.input_history

189

if not contains_sensitive_info(item)

190

]

191

return data.clone(input_history=tuple(filtered_history))

192

193

specialist_agent = Agent(

194

name="Specialist",

195

instructions="Handle complex cases."

196

)

197

198

filtered_handoff = handoff(

199

specialist_agent,

200

input_filter=filter_sensitive_data

201

)

202

203

main_agent = Agent(

204

name="Main Agent",

205

handoffs=[filtered_handoff]

206

)

207

```

208

209

### History Management

210

211

Functions for managing conversation history during handoffs.

212

213

```python { .api }

214

def nest_handoff_history() -> list[TResponseInputItem]:

215

"""

216

Nest handoff history into single message.

217

218

Wraps the entire conversation history in a single message

219

to reduce token usage and improve context management.

220

221

Returns:

222

- list[TResponseInputItem]: Nested history

223

"""

224

225

def default_handoff_history_mapper() -> list[TResponseInputItem]:

226

"""

227

Default history mapper for handoffs.

228

229

Returns:

230

- list[TResponseInputItem]: Mapped history

231

"""

232

```

233

234

Type alias:

235

236

```python { .api }

237

HandoffHistoryMapper = Callable[

238

[list[TResponseInputItem]],

239

list[TResponseInputItem]

240

]

241

```

242

243

Usage example:

244

245

```python

246

from agents import Agent, RunConfig, nest_handoff_history

247

248

# Global configuration

249

config = RunConfig(

250

nest_handoff_history=True,

251

handoff_history_mapper=nest_handoff_history

252

)

253

254

# Run with history management

255

result = Runner.run_sync(

256

triage_agent,

257

"I need help",

258

run_config=config

259

)

260

261

# Per-handoff configuration

262

specialist_handoff = handoff(

263

specialist_agent,

264

nest_handoff_history=True

265

)

266

```

267

268

### Conversation History Wrappers

269

270

Global functions for managing conversation history presentation.

271

272

```python { .api }

273

def get_conversation_history_wrappers() -> tuple[str, str]:

274

"""

275

Get conversation history wrappers.

276

277

Returns:

278

- tuple[str, str]: Opening and closing wrapper strings

279

"""

280

281

def set_conversation_history_wrappers(opening: str, closing: str) -> None:

282

"""

283

Set conversation history wrappers.

284

285

Parameters:

286

- opening: Opening wrapper string

287

- closing: Closing wrapper string

288

"""

289

290

def reset_conversation_history_wrappers() -> None:

291

"""Reset conversation history wrappers to defaults."""

292

```

293

294

Usage example:

295

296

```python

297

from agents import set_conversation_history_wrappers, reset_conversation_history_wrappers

298

299

# Customize history presentation

300

set_conversation_history_wrappers(

301

opening="<conversation_history>",

302

closing="</conversation_history>"

303

)

304

305

# Run agents with custom wrappers

306

result = Runner.run_sync(agent, "Hello")

307

308

# Reset to defaults

309

reset_conversation_history_wrappers()

310

```

311

312

## Advanced Handoff Patterns

313

314

### Conditional Handoffs

315

316

Enable or disable handoffs based on context.

317

318

```python

319

from agents import Agent, handoff

320

321

def should_enable_expert(ctx):

322

"""Enable expert handoff only for premium users."""

323

return ctx.context.user.tier == "premium"

324

325

expert_agent = Agent(

326

name="Expert Agent",

327

instructions="Provide expert assistance."

328

)

329

330

expert_handoff = handoff(

331

expert_agent,

332

is_enabled=should_enable_expert

333

)

334

335

agent = Agent(

336

name="Assistant",

337

handoffs=[expert_handoff]

338

)

339

```

340

341

### Typed Handoff Inputs

342

343

Use Pydantic models for structured handoff parameters.

344

345

```python

346

from agents import Agent, handoff

347

from pydantic import BaseModel

348

349

class EscalationInput(BaseModel):

350

reason: str

351

priority: str

352

user_id: str

353

354

escalation_agent = Agent(

355

name="Escalation Handler",

356

instructions="Handle escalated issues."

357

)

358

359

typed_handoff = handoff(

360

escalation_agent,

361

input_type=EscalationInput,

362

tool_description_override="Escalate issue with structured details"

363

)

364

365

agent = Agent(

366

name="Support Agent",

367

handoffs=[typed_handoff]

368

)

369

```

370

371

### Multi-Stage Workflows

372

373

Chain multiple agents with handoffs.

374

375

```python

376

from agents import Agent

377

378

# Define workflow stages

379

intake_agent = Agent(

380

name="Intake",

381

instructions="Gather initial information."

382

)

383

384

analysis_agent = Agent(

385

name="Analysis",

386

instructions="Analyze the gathered information.",

387

handoffs=[intake_agent] # Can go back if needed

388

)

389

390

resolution_agent = Agent(

391

name="Resolution",

392

instructions="Provide final resolution.",

393

handoffs=[analysis_agent] # Can request more analysis

394

)

395

396

# Start with analysis stage

397

analysis_agent.handoffs.append(resolution_agent)

398

399

# Entry point

400

entry_agent = Agent(

401

name="Entry",

402

instructions="Start the workflow.",

403

handoffs=[intake_agent]

404

)

405

406

result = Runner.run_sync(entry_agent, "I need help with...")

407

```

408

409

### Handoff with History Filtering

410

411

Filter conversation history based on content.

412

413

```python

414

from agents import handoff, HandoffInputData

415

416

async def filter_history(data: HandoffInputData) -> HandoffInputData:

417

"""Keep only relevant messages."""

418

# Filter based on content, age, or other criteria

419

recent_items = data.input_history[-10:] # Last 10 messages

420

return data.clone(input_history=tuple(recent_items))

421

422

specialist = Agent(

423

name="Specialist",

424

instructions="Handle specialized requests."

425

)

426

427

filtered_handoff = handoff(

428

specialist,

429

input_filter=filter_history

430

)

431

432

agent = Agent(

433

name="General Agent",

434

handoffs=[filtered_handoff]

435

)

436

```

437

438

### Handoff Callbacks

439

440

Execute logic when handoffs occur.

441

442

```python

443

from agents import handoff

444

import logging

445

446

logger = logging.getLogger(__name__)

447

448

async def log_handoff(ctx, agent, input_data):

449

"""Log handoff events for analytics."""

450

logger.info(f"Handoff to {agent.name}", extra={

451

"from_agent": ctx.agent.name,

452

"to_agent": agent.name,

453

"user_id": ctx.context.user_id

454

})

455

456

expert = Agent(name="Expert", instructions="Expert assistance")

457

458

monitored_handoff = handoff(

459

expert,

460

on_handoff=log_handoff

461

)

462

```

463

464

## Handoff Best Practices

465

466

1. **Clear Descriptions**: Provide clear handoff tool descriptions to help the LLM understand when to use each handoff

467

2. **Avoid Cycles**: Be careful with bidirectional handoffs to prevent infinite loops

468

3. **Filter History**: Use input filters to reduce token usage and improve context relevance

469

4. **Use Nesting**: Enable `nest_handoff_history` for long conversations to manage context size

470

5. **Conditional Handoffs**: Use `is_enabled` to dynamically control handoff availability

471

6. **Monitor Handoffs**: Use `on_handoff` callbacks for logging and analytics

472