@aurite-ai/kai 0.2.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -0
- package/dist/kai-mcp.cjs +1652 -0
- package/dist/kai-mcp.cjs.map +7 -0
- package/dist/templates/copilot-configs/claude-code/.claude/CLAUDE.md +272 -0
- package/dist/templates/copilot-configs/claude-code/.claude/agents/architect.md +133 -0
- package/dist/templates/copilot-configs/claude-code/.claude/agents/implementer.md +88 -0
- package/dist/templates/copilot-configs/claude-code/.claude/settings.json +87 -0
- package/dist/templates/copilot-configs/claude-code/.claude/skills/documentation/SKILL.md +54 -0
- package/dist/templates/copilot-configs/claude-code/.claude/skills/verification/SKILL.md +125 -0
- package/dist/templates/frameworks/langgraph/README.md +118 -0
- package/dist/templates/frameworks/langgraph/main.py +97 -0
- package/dist/templates/frameworks/langgraph/pyproject.toml +29 -0
- package/dist/templates/frameworks/langgraph/src/agent/__init__.py +31 -0
- package/dist/templates/frameworks/langgraph/src/agent/graph.py +257 -0
- package/dist/templates/frameworks/langgraph/src/agent/state.py +65 -0
- package/dist/templates/frameworks/langgraph/src/agent/tools.py +30 -0
- package/dist/templates/frameworks/openai/README.md +131 -0
- package/dist/templates/frameworks/openai/main.py +80 -0
- package/dist/templates/frameworks/openai/pyproject.toml +28 -0
- package/dist/templates/frameworks/openai/src/agent/__init__.py +18 -0
- package/dist/templates/frameworks/openai/src/agent/agents.py +122 -0
- package/dist/templates/frameworks/openai/src/agent/tools.py +100 -0
- package/dist/templates/index.d.ts +85 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +270 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/knowledge-base/langgraph-best-practices.mdc +277 -0
- package/dist/templates/knowledge-base/openai-agents-overview.mdc +602 -0
- package/dist/templates/project-env +7 -0
- package/dist/templates/project-gitignore +26 -0
- package/package.json +66 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: knowledge
|
|
3
|
+
title: OpenAI Agents Overview
|
|
4
|
+
summary: >
|
|
5
|
+
Comprehensive reference for building with the OpenAI Agent SDK
|
|
6
|
+
created_at: "2026-03-06T00:00:00Z"
|
|
7
|
+
updated_at: "2026-03-06T00:00:00Z"
|
|
8
|
+
|
|
9
|
+
source:
|
|
10
|
+
file: openai-agents-overview.mdc
|
|
11
|
+
project: null
|
|
12
|
+
path: null
|
|
13
|
+
|
|
14
|
+
classification:
|
|
15
|
+
category: reference
|
|
16
|
+
confidence: 1.0
|
|
17
|
+
reasoning: >
|
|
18
|
+
Seed content providing technical reference documentation for the OpenAI
|
|
19
|
+
Agents SDK.
|
|
20
|
+
topics:
|
|
21
|
+
- OpenAI
|
|
22
|
+
- Agents
|
|
23
|
+
- Python AI Agents
|
|
24
|
+
|
|
25
|
+
status: active
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# OpenAI Agents SDK - Development Overview
|
|
29
|
+
|
|
30
|
+
A comprehensive guide to building AI agents with the OpenAI Agents SDK.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Setup
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Create project and virtual environment
|
|
40
|
+
mkdir my_project && cd my_project
|
|
41
|
+
python -m venv .venv
|
|
42
|
+
source .venv/bin/activate
|
|
43
|
+
|
|
44
|
+
# Install SDK
|
|
45
|
+
pip install openai-agents
|
|
46
|
+
|
|
47
|
+
# Set API key
|
|
48
|
+
export OPENAI_API_KEY=sk-...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Basic Agent
|
|
52
|
+
|
|
53
|
+
Agents are defined with a name, instructions, and optional configuration:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import asyncio
|
|
57
|
+
from agents import Agent, Runner
|
|
58
|
+
|
|
59
|
+
agent = Agent(
|
|
60
|
+
name="History Tutor",
|
|
61
|
+
instructions="You answer history questions clearly and concisely.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
async def main():
|
|
65
|
+
result = await Runner.run(agent, "When did the Roman Empire fall?")
|
|
66
|
+
print(result.final_output)
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Core Concepts
|
|
75
|
+
|
|
76
|
+
### The Agent Loop
|
|
77
|
+
|
|
78
|
+
When you call `Runner.run()`, the SDK executes a loop:
|
|
79
|
+
|
|
80
|
+
1. Call the LLM for the current agent with current input
|
|
81
|
+
2. Process the LLM output:
|
|
82
|
+
- **Final output** → Loop ends, return result
|
|
83
|
+
- **Handoff** → Update agent and input, continue loop
|
|
84
|
+
- **Tool calls** → Execute tools, append results, continue loop
|
|
85
|
+
3. If `max_turns` exceeded → Raise `MaxTurnsExceeded` exception
|
|
86
|
+
|
|
87
|
+
### Runner Methods
|
|
88
|
+
|
|
89
|
+
Three ways to run agents:
|
|
90
|
+
|
|
91
|
+
| Method | Type | Returns | Use Case |
|
|
92
|
+
|--------|------|---------|----------|
|
|
93
|
+
| `Runner.run()` | Async | `RunResult` | Standard async execution |
|
|
94
|
+
| `Runner.run_sync()` | Sync | `RunResult` | Synchronous wrapper |
|
|
95
|
+
| `Runner.run_streamed()` | Async | `RunResultStreaming` | Streaming LLM events |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Tools
|
|
100
|
+
|
|
101
|
+
### Function Tools
|
|
102
|
+
|
|
103
|
+
Use the `@function_tool` decorator to give agents capabilities:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from typing import Annotated
|
|
107
|
+
from pydantic import BaseModel, Field
|
|
108
|
+
from agents import Agent, Runner, function_tool
|
|
109
|
+
|
|
110
|
+
class Weather(BaseModel):
|
|
111
|
+
city: str = Field(description="The city name")
|
|
112
|
+
temperature_range: str = Field(description="The temperature range in Celsius")
|
|
113
|
+
conditions: str = Field(description="The weather conditions")
|
|
114
|
+
|
|
115
|
+
@function_tool
|
|
116
|
+
def get_weather(city: Annotated[str, "The city to get the weather for"]) -> Weather:
|
|
117
|
+
"""Get the current weather information for a specified city."""
|
|
118
|
+
return Weather(
|
|
119
|
+
city=city,
|
|
120
|
+
temperature_range="14-20C",
|
|
121
|
+
conditions="Sunny with wind."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
agent = Agent(
|
|
125
|
+
name="Weather Assistant",
|
|
126
|
+
instructions="You are a helpful agent.",
|
|
127
|
+
tools=[get_weather],
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async def main():
|
|
131
|
+
result = await Runner.run(agent, "What's the weather in Tokyo?")
|
|
132
|
+
print(result.final_output)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Key Points:**
|
|
136
|
+
- Use type hints and Pydantic models for structured outputs
|
|
137
|
+
- Docstrings become tool descriptions for the LLM
|
|
138
|
+
- Tools are automatically called when the agent needs them
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Multi-Agent Patterns
|
|
143
|
+
|
|
144
|
+
### Pattern 1: Handoffs (Specialist Takes Over)
|
|
145
|
+
|
|
146
|
+
Specialists take over the conversation for their domain:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from agents import Agent
|
|
150
|
+
|
|
151
|
+
# Define specialist agents
|
|
152
|
+
history_tutor = Agent(
|
|
153
|
+
name="History Tutor",
|
|
154
|
+
handoff_description="Specialist agent for historical questions",
|
|
155
|
+
instructions="You answer history questions clearly and concisely.",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
math_tutor = Agent(
|
|
159
|
+
name="Math Tutor",
|
|
160
|
+
handoff_description="Specialist agent for math questions",
|
|
161
|
+
instructions="You explain math step by step and include worked examples.",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Define routing agent
|
|
165
|
+
triage_agent = Agent(
|
|
166
|
+
name="Triage Agent",
|
|
167
|
+
instructions="Route each homework question to the right specialist.",
|
|
168
|
+
handoffs=[history_tutor, math_tutor],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async def main():
|
|
172
|
+
result = await Runner.run(
|
|
173
|
+
triage_agent,
|
|
174
|
+
"Who was the first president of the United States?",
|
|
175
|
+
)
|
|
176
|
+
print(result.final_output)
|
|
177
|
+
print(f"Answered by: {result.last_agent.name}")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**When to use:** The specialist should own the final answer for that part of the conversation.
|
|
181
|
+
|
|
182
|
+
### Pattern 2: Agents as Tools (Orchestrator Controls)
|
|
183
|
+
|
|
184
|
+
An orchestrator stays in control and calls specialists as tools. See the SDK documentation for details on this manager-style pattern.
|
|
185
|
+
|
|
186
|
+
**When to use:** You want centralized control with the orchestrator owning the final answer.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Conversation Management
|
|
191
|
+
|
|
192
|
+
### Strategy Comparison
|
|
193
|
+
|
|
194
|
+
| Strategy | State Location | Best For | Next Turn Input |
|
|
195
|
+
|----------|---------------|----------|-----------------|
|
|
196
|
+
| `result.to_input_list()` | Your app memory | Manual control, any provider | Previous list + new message |
|
|
197
|
+
| `session` | Your storage + SDK | Persistent chat, resumable runs | Same session instance |
|
|
198
|
+
| `conversation_id` | OpenAI server | Named server-side conversation | Same ID + new message only |
|
|
199
|
+
| `previous_response_id` | OpenAI server | Lightweight continuation | Last response ID + new message |
|
|
200
|
+
|
|
201
|
+
### Manual History Management
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
async def main():
|
|
205
|
+
agent = Agent(name="Assistant", instructions="Reply very concisely.")
|
|
206
|
+
|
|
207
|
+
# First turn
|
|
208
|
+
result = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
|
|
209
|
+
print(result.final_output) # San Francisco
|
|
210
|
+
|
|
211
|
+
# Second turn - manually manage history
|
|
212
|
+
new_input = result.to_input_list() + [
|
|
213
|
+
{"role": "user", "content": "What state is it in?"}
|
|
214
|
+
]
|
|
215
|
+
result = await Runner.run(agent, new_input)
|
|
216
|
+
print(result.final_output) # California
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Automatic Session Management
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from agents import Agent, Runner, SQLiteSession
|
|
223
|
+
|
|
224
|
+
async def main():
|
|
225
|
+
agent = Agent(name="Assistant", instructions="Reply very concisely.")
|
|
226
|
+
session = SQLiteSession("conversation_123")
|
|
227
|
+
|
|
228
|
+
# First turn
|
|
229
|
+
result = await Runner.run(
|
|
230
|
+
agent,
|
|
231
|
+
"What city is the Golden Gate Bridge in?",
|
|
232
|
+
session=session
|
|
233
|
+
)
|
|
234
|
+
print(result.final_output) # San Francisco
|
|
235
|
+
|
|
236
|
+
# Second turn - session automatically handles history
|
|
237
|
+
result = await Runner.run(
|
|
238
|
+
agent,
|
|
239
|
+
"What state is it in?",
|
|
240
|
+
session=session
|
|
241
|
+
)
|
|
242
|
+
print(result.final_output) # California
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Server-Managed Conversations
|
|
246
|
+
|
|
247
|
+
**Using conversation_id:**
|
|
248
|
+
```python
|
|
249
|
+
from agents import Agent, Runner
|
|
250
|
+
from openai import AsyncOpenAI
|
|
251
|
+
|
|
252
|
+
client = AsyncOpenAI()
|
|
253
|
+
|
|
254
|
+
async def main():
|
|
255
|
+
agent = Agent(name="Assistant", instructions="Reply very concisely.")
|
|
256
|
+
|
|
257
|
+
# Create server-managed conversation
|
|
258
|
+
conversation = await client.conversations.create()
|
|
259
|
+
conv_id = conversation.id
|
|
260
|
+
|
|
261
|
+
while True:
|
|
262
|
+
user_input = input("You: ")
|
|
263
|
+
result = await Runner.run(agent, user_input, conversation_id=conv_id)
|
|
264
|
+
print(f"Assistant: {result.final_output}")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Using previous_response_id:**
|
|
268
|
+
```python
|
|
269
|
+
async def main():
|
|
270
|
+
agent = Agent(name="Assistant", instructions="Reply very concisely.")
|
|
271
|
+
previous_response_id = None
|
|
272
|
+
|
|
273
|
+
while True:
|
|
274
|
+
user_input = input("You: ")
|
|
275
|
+
result = await Runner.run(
|
|
276
|
+
agent,
|
|
277
|
+
user_input,
|
|
278
|
+
previous_response_id=previous_response_id,
|
|
279
|
+
auto_previous_response_id=True,
|
|
280
|
+
)
|
|
281
|
+
previous_response_id = result.last_response_id
|
|
282
|
+
print(f"Assistant: {result.final_output}")
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Streaming
|
|
288
|
+
|
|
289
|
+
Stream LLM events as they arrive:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from openai.types.responses import ResponseTextDeltaEvent, ResponseContentPartDoneEvent
|
|
293
|
+
from agents import Agent, Runner, RawResponsesStreamEvent
|
|
294
|
+
|
|
295
|
+
async def main():
|
|
296
|
+
agent = Agent(name="Assistant", instructions="Be helpful.")
|
|
297
|
+
|
|
298
|
+
result = Runner.run_streamed(agent, "Tell me about Python.")
|
|
299
|
+
|
|
300
|
+
async for event in result.stream_events():
|
|
301
|
+
if not isinstance(event, RawResponsesStreamEvent):
|
|
302
|
+
continue
|
|
303
|
+
data = event.data
|
|
304
|
+
if isinstance(data, ResponseTextDeltaEvent):
|
|
305
|
+
print(data.delta, end="", flush=True)
|
|
306
|
+
elif isinstance(data, ResponseContentPartDoneEvent):
|
|
307
|
+
print("\n")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Configuration
|
|
313
|
+
|
|
314
|
+
### RunConfig
|
|
315
|
+
|
|
316
|
+
Configure global settings for a run:
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
from agents import Agent, Runner, RunConfig
|
|
320
|
+
|
|
321
|
+
agent = Agent(name="Assistant", instructions="Be concise.")
|
|
322
|
+
|
|
323
|
+
result = await Runner.run(
|
|
324
|
+
agent,
|
|
325
|
+
"Explain quantum computing",
|
|
326
|
+
run_config=RunConfig(
|
|
327
|
+
model="gpt-4", # Override model
|
|
328
|
+
max_turns=10, # Limit turns
|
|
329
|
+
tracing_disabled=False, # Enable tracing
|
|
330
|
+
workflow_name="QA Bot", # Name for traces
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Key Configuration Options:**
|
|
336
|
+
|
|
337
|
+
- **Model settings:** `model`, `model_provider`, `model_settings`
|
|
338
|
+
- **Guardrails:** `input_guardrails`, `output_guardrails`
|
|
339
|
+
- **Handoff control:** `handoff_input_filter`, `nest_handoff_history`
|
|
340
|
+
- **Tracing:** `tracing`, `workflow_name`, `trace_id`, `trace_metadata`
|
|
341
|
+
- **Hooks:** `call_model_input_filter`, `session_input_callback`
|
|
342
|
+
|
|
343
|
+
### Input Filtering
|
|
344
|
+
|
|
345
|
+
Edit model input before the LLM call:
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
from agents import RunConfig
|
|
349
|
+
from agents.run import CallModelData, ModelInputData
|
|
350
|
+
|
|
351
|
+
def drop_old_messages(data: CallModelData[None]) -> ModelInputData:
|
|
352
|
+
# Keep only last 5 items
|
|
353
|
+
trimmed = data.model_data.input[-5:]
|
|
354
|
+
return ModelInputData(
|
|
355
|
+
input=trimmed,
|
|
356
|
+
instructions=data.model_data.instructions
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
result = await Runner.run(
|
|
360
|
+
agent,
|
|
361
|
+
"Explain quines",
|
|
362
|
+
run_config=RunConfig(call_model_input_filter=drop_old_messages),
|
|
363
|
+
)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Error Handling
|
|
369
|
+
|
|
370
|
+
### Common Exceptions
|
|
371
|
+
|
|
372
|
+
- `AgentsException`: Base class for all SDK exceptions
|
|
373
|
+
- `MaxTurnsExceeded`: Agent exceeded turn limit
|
|
374
|
+
- `ModelBehaviorError`: LLM produced unexpected output (malformed JSON, unexpected tool failures)
|
|
375
|
+
- `ToolTimeoutError`: Tool call exceeded timeout
|
|
376
|
+
- `UserError`: Incorrect SDK usage
|
|
377
|
+
- `InputGuardrailTripwireTriggered`, `OutputGuardrailTripwireTriggered`: Guardrail conditions met
|
|
378
|
+
|
|
379
|
+
### Error Handlers
|
|
380
|
+
|
|
381
|
+
Handle errors gracefully instead of raising exceptions:
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
from agents import (
|
|
385
|
+
Agent,
|
|
386
|
+
RunErrorHandlerInput,
|
|
387
|
+
RunErrorHandlerResult,
|
|
388
|
+
Runner,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
def on_max_turns(_data: RunErrorHandlerInput[None]) -> RunErrorHandlerResult:
|
|
392
|
+
return RunErrorHandlerResult(
|
|
393
|
+
final_output="I couldn't finish within the turn limit. Please narrow the request.",
|
|
394
|
+
include_in_history=False,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
result = Runner.run_sync(
|
|
398
|
+
agent,
|
|
399
|
+
"Analyze this long transcript",
|
|
400
|
+
max_turns=3,
|
|
401
|
+
error_handlers={"max_turns": on_max_turns},
|
|
402
|
+
)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Advanced Features
|
|
408
|
+
|
|
409
|
+
### WebSocket Transport
|
|
410
|
+
|
|
411
|
+
Use WebSocket for better performance with the Responses API:
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
from agents import Agent, responses_websocket_session
|
|
415
|
+
|
|
416
|
+
async def main():
|
|
417
|
+
agent = Agent(name="Assistant", instructions="Be concise.")
|
|
418
|
+
|
|
419
|
+
async with responses_websocket_session() as ws:
|
|
420
|
+
first = ws.run_streamed(agent, "Say hello in one short sentence.")
|
|
421
|
+
async for _event in first.stream_events():
|
|
422
|
+
pass
|
|
423
|
+
|
|
424
|
+
second = ws.run_streamed(
|
|
425
|
+
agent,
|
|
426
|
+
"Now say goodbye.",
|
|
427
|
+
previous_response_id=first.last_response_id,
|
|
428
|
+
)
|
|
429
|
+
async for _event in second.stream_events():
|
|
430
|
+
pass
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Durable Execution
|
|
434
|
+
|
|
435
|
+
For long-running workflows with human-in-the-loop, the SDK integrates with:
|
|
436
|
+
|
|
437
|
+
- **Temporal**: Durable workflows with retries and restarts
|
|
438
|
+
- **Restate**: Lightweight durable agents with single-binary runtime (requires Restate runtime)
|
|
439
|
+
- **DBOS**: Reliable agents with SQLite/Postgres persistence (preserves progress across failures)
|
|
440
|
+
|
|
441
|
+
These integrations support tool approval pause/resume patterns, handoffs, and session management.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Complete Examples
|
|
446
|
+
|
|
447
|
+
### Hello World
|
|
448
|
+
```python
|
|
449
|
+
import asyncio
|
|
450
|
+
from agents import Agent, Runner
|
|
451
|
+
|
|
452
|
+
async def main():
|
|
453
|
+
agent = Agent(
|
|
454
|
+
name="Assistant",
|
|
455
|
+
instructions="You only respond in haikus.",
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
result = await Runner.run(agent, "Tell me about recursion in programming.")
|
|
459
|
+
print(result.final_output)
|
|
460
|
+
# Function calls itself,
|
|
461
|
+
# Looping in smaller pieces,
|
|
462
|
+
# Endless by design.
|
|
463
|
+
|
|
464
|
+
if __name__ == "__main__":
|
|
465
|
+
asyncio.run(main())
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Multi-Agent Routing with Streaming
|
|
469
|
+
```python
|
|
470
|
+
import asyncio
|
|
471
|
+
import uuid
|
|
472
|
+
from openai.types.responses import ResponseTextDeltaEvent, ResponseContentPartDoneEvent
|
|
473
|
+
from agents import Agent, RawResponsesStreamEvent, Runner, trace
|
|
474
|
+
|
|
475
|
+
# Define language-specific agents
|
|
476
|
+
french_agent = Agent(
|
|
477
|
+
name="french_agent",
|
|
478
|
+
instructions="You only speak French",
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
spanish_agent = Agent(
|
|
482
|
+
name="spanish_agent",
|
|
483
|
+
instructions="You only speak Spanish",
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
english_agent = Agent(
|
|
487
|
+
name="english_agent",
|
|
488
|
+
instructions="You only speak English",
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
# Define routing agent
|
|
492
|
+
triage_agent = Agent(
|
|
493
|
+
name="triage_agent",
|
|
494
|
+
instructions="Handoff to the appropriate agent based on the language of the request.",
|
|
495
|
+
handoffs=[french_agent, spanish_agent, english_agent],
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
async def main():
|
|
499
|
+
conversation_id = str(uuid.uuid4().hex[:16])
|
|
500
|
+
|
|
501
|
+
msg = "Hello, how do I say good evening in French?"
|
|
502
|
+
agent = triage_agent
|
|
503
|
+
inputs = [{"content": msg, "role": "user"}]
|
|
504
|
+
|
|
505
|
+
while True:
|
|
506
|
+
# Each turn is a single trace
|
|
507
|
+
with trace("Routing example", group_id=conversation_id):
|
|
508
|
+
result = Runner.run_streamed(agent, input=inputs)
|
|
509
|
+
|
|
510
|
+
async for event in result.stream_events():
|
|
511
|
+
if not isinstance(event, RawResponsesStreamEvent):
|
|
512
|
+
continue
|
|
513
|
+
data = event.data
|
|
514
|
+
if isinstance(data, ResponseTextDeltaEvent):
|
|
515
|
+
print(data.delta, end="", flush=True)
|
|
516
|
+
elif isinstance(data, ResponseContentPartDoneEvent):
|
|
517
|
+
print("\n")
|
|
518
|
+
|
|
519
|
+
inputs = result.to_input_list()
|
|
520
|
+
user_msg = input("Enter a message: ")
|
|
521
|
+
inputs.append({"content": user_msg, "role": "user"})
|
|
522
|
+
agent = result.current_agent
|
|
523
|
+
|
|
524
|
+
if __name__ == "__main__":
|
|
525
|
+
asyncio.run(main())
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Best Practices
|
|
531
|
+
|
|
532
|
+
1. **Start Simple**: Begin with a single agent before adding tools or handoffs
|
|
533
|
+
2. **Use Type Hints**: Leverage Pydantic models for structured tool outputs
|
|
534
|
+
3. **Choose One Memory Strategy**: Don't mix client-managed and server-managed state
|
|
535
|
+
4. **Enable Tracing**: Set `workflow_name` in RunConfig for observability
|
|
536
|
+
5. **Handle Errors**: Use error handlers for graceful degradation
|
|
537
|
+
6. **Stream When Possible**: Better UX with streaming for long responses
|
|
538
|
+
7. **Test Tool Calls**: Verify tools work independently before integrating
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Quick Reference
|
|
543
|
+
|
|
544
|
+
### Essential Imports
|
|
545
|
+
```python
|
|
546
|
+
from agents import (
|
|
547
|
+
Agent, # Define agents
|
|
548
|
+
Runner, # Execute agents
|
|
549
|
+
function_tool, # Create tools
|
|
550
|
+
RunConfig, # Configure runs
|
|
551
|
+
SQLiteSession, # Session management
|
|
552
|
+
trace, # Tracing context
|
|
553
|
+
)
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Common Patterns
|
|
557
|
+
```python
|
|
558
|
+
# Basic run
|
|
559
|
+
result = await Runner.run(agent, "user message")
|
|
560
|
+
|
|
561
|
+
# With session
|
|
562
|
+
result = await Runner.run(agent, "message", session=session)
|
|
563
|
+
|
|
564
|
+
# With streaming
|
|
565
|
+
result = Runner.run_streamed(agent, "message")
|
|
566
|
+
async for event in result.stream_events():
|
|
567
|
+
# Process events
|
|
568
|
+
pass
|
|
569
|
+
|
|
570
|
+
# Multi-turn
|
|
571
|
+
inputs = result.to_input_list() + [{"role": "user", "content": "next message"}]
|
|
572
|
+
result = await Runner.run(agent, inputs)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Additional Notes
|
|
578
|
+
|
|
579
|
+
### Session Persistence
|
|
580
|
+
|
|
581
|
+
Sessions automatically:
|
|
582
|
+
- Retrieve conversation history before each run
|
|
583
|
+
- Store new messages after each run
|
|
584
|
+
- Maintain separate conversations for different session IDs
|
|
585
|
+
|
|
586
|
+
**Important**: Session persistence cannot be combined with server-managed conversation settings (`conversation_id`, `previous_response_id`, or `auto_previous_response_id`) in the same run.
|
|
587
|
+
|
|
588
|
+
### Nested Handoffs
|
|
589
|
+
|
|
590
|
+
Nested handoffs are available as an opt-in beta. Enable collapsed-transcript behavior by passing `RunConfig(nest_handoff_history=True)` or set `handoff(..., nest_handoff_history=True)` for specific handoffs. By default, the raw transcript is passed through.
|
|
591
|
+
|
|
592
|
+
### Reasoning Item ID Policy
|
|
593
|
+
|
|
594
|
+
Control how reasoning items are converted into next-turn model input:
|
|
595
|
+
- `None` or `"preserve"` (default): Keep reasoning item IDs
|
|
596
|
+
- `"omit"`: Strip reasoning item IDs from generated next-turn input
|
|
597
|
+
|
|
598
|
+
Use `"omit"` as an opt-in mitigation for Responses API 400 errors where a reasoning item is sent with an ID but without the required following item.
|
|
599
|
+
|
|
600
|
+
### Conversation Locking
|
|
601
|
+
|
|
602
|
+
The SDK automatically retries `conversation_locked` errors with backoff. In server-managed conversation runs, it rewinds the internal conversation-tracker input before retrying. In local session-based runs, it performs best-effort rollback of recently persisted input items to reduce duplicate history entries.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
|
|
9
|
+
# TypeScript/Node
|
|
10
|
+
node_modules/
|
|
11
|
+
dist/
|
|
12
|
+
|
|
13
|
+
# Environment & Secrets
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
*.pem
|
|
17
|
+
*.key
|
|
18
|
+
|
|
19
|
+
# IDE (optional - uncomment if desired)
|
|
20
|
+
# .idea/
|
|
21
|
+
# .vscode/
|
|
22
|
+
|
|
23
|
+
# OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
.kai-test.json
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aurite-ai/kai",
|
|
3
|
+
"version": "0.2.0-dev.0",
|
|
4
|
+
"description": "Kai MCP server - context management tools for coding copilots",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Kai Team",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Aurite-ai/kai.git",
|
|
11
|
+
"directory": "apps/mcp"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Aurite-ai/kai#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/Aurite-ai/kai/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"ai",
|
|
24
|
+
"copilot",
|
|
25
|
+
"context-management",
|
|
26
|
+
"claude",
|
|
27
|
+
"anthropic"
|
|
28
|
+
],
|
|
29
|
+
"main": "./dist/kai-mcp.cjs",
|
|
30
|
+
"bin": {
|
|
31
|
+
"kai-mcp": "dist/kai-mcp.cjs"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/kai-mcp.cjs",
|
|
35
|
+
"dist/kai-mcp.cjs.map",
|
|
36
|
+
"dist/templates",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"bundle": "tsx scripts/bundle.ts",
|
|
45
|
+
"dev": "tsx watch src/index.ts",
|
|
46
|
+
"start": "node dist/index.js",
|
|
47
|
+
"start:bundle": "node dist/kai-mcp.cjs",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"prepublishOnly": "pnpm build && pnpm bundle"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@anthropic-ai/sdk": "^0.32.1",
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
56
|
+
"dotenv": "^16.4.0",
|
|
57
|
+
"yaml": "^2.4.0",
|
|
58
|
+
"zod": "^4.3.6"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^22.0.0",
|
|
62
|
+
"esbuild": "^0.27.3",
|
|
63
|
+
"tsx": "^4.19.0",
|
|
64
|
+
"vitest": "^3.1.0"
|
|
65
|
+
}
|
|
66
|
+
}
|