Skip to content

Commit 94447af

Browse files
committed
Document nested handoff history defaults
1 parent e895a77 commit 94447af

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

docs/handoffs.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ handoff_obj = handoff(
8282

8383
When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`.
8484

85+
By default the runner now wraps the prior transcript inside a developer-role summary message (see [`RunConfig.nest_handoff_history`][agents.run.RunConfig.nest_handoff_history]). That default only applies when neither the handoff nor the run supplies an explicit `input_filter`, so existing code that already customizes the payload (including the examples in this repository) keeps its current behavior without changes.
86+
8587
There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][]
8688

8789
```python
@@ -98,6 +100,35 @@ handoff_obj = handoff(
98100

99101
1. This will automatically remove all tools from the history when `FAQ agent` is called.
100102

103+
### Inspecting handoff payloads
104+
105+
When you are debugging a workflow it is often useful to print the exact transcript that will be sent to the next agent. You can do this by inserting a lightweight filter that logs the `HandoffInputData` and then returns either the unmodified payload or the output from [`nest_handoff_history`](agents.extensions.handoff_filters.nest_handoff_history).
106+
107+
```python
108+
import json
109+
110+
from agents import Agent, HandoffInputData, handoff
111+
from agents.extensions.handoff_filters import nest_handoff_history
112+
113+
114+
def log_handoff_payload(data: HandoffInputData) -> HandoffInputData:
115+
nested = nest_handoff_history(data)
116+
history_items = nested.input_history if isinstance(nested.input_history, tuple) else ()
117+
for idx, item in enumerate(history_items, start=1):
118+
print(f"Turn {idx}: {json.dumps(item, indent=2, ensure_ascii=False)}")
119+
return nested
120+
121+
122+
math_agent = Agent(name="Math agent")
123+
124+
router = Agent(
125+
name="Router",
126+
handoffs=[handoff(math_agent, input_filter=log_handoff_payload)],
127+
)
128+
```
129+
130+
The new [examples/handoffs/log_handoff_history.py](https://github.com/openai/openai-agents-python/tree/main/examples/handoffs/log_handoff_history.py) script contains a complete runnable sample that prints the nested transcript every time a handoff occurs.
131+
101132
## Recommended prompts
102133

103134
To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts.

docs/running_agents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ The `run_config` parameter lets you configure some global settings for the agent
5151
- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`.
5252
- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs.
5353
- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details.
54-
- [`nest_handoff_history`][agents.run.RunConfig.nest_handoff_history]: When `True` (the default) the runner wraps the prior transcript in a developer-role summary message and keeps the latest user turn separate before invoking the next agent. Set this to `False` or provide a custom handoff filter if you prefer to pass through the raw transcript. You can also call [`nest_handoff_history`](agents.extensions.handoff_filters.nest_handoff_history) from your own filters to reuse the default behavior.
54+
- [`nest_handoff_history`][agents.run.RunConfig.nest_handoff_history]: When `True` (the default) the runner wraps the prior transcript in a developer-role summary message and keeps the latest user turn separate before invoking the next agent. Set this to `False` or provide a custom handoff filter if you prefer to pass through the raw transcript. You can also call [`nest_handoff_history`](agents.extensions.handoff_filters.nest_handoff_history) from your own filters to reuse the default behavior. All [`Runner` methods](agents.run.Runner) automatically create a `RunConfig` when you do not pass one, so the quickstarts and examples pick up this default automatically, and any explicit [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] callbacks continue to override it.
5555
- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run.
5656
- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs.
5757
- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The group ID is an optional field that lets you link traces across multiple runs.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
import json
5+
6+
from agents import Agent, HandoffInputData, Runner, handoff
7+
from agents.extensions.handoff_filters import nest_handoff_history
8+
from agents.items import ItemHelpers
9+
10+
11+
math_agent = Agent(
12+
name="Math Agent",
13+
instructions=(
14+
"You are a friendly math expert. Explain your reasoning and finish with a clear answer."
15+
),
16+
)
17+
18+
19+
def log_handoff_history(data: HandoffInputData) -> HandoffInputData:
20+
"""Print the transcript that will be forwarded to the next agent."""
21+
22+
nested = nest_handoff_history(data)
23+
history_items = (
24+
nested.input_history
25+
if isinstance(nested.input_history, tuple)
26+
else tuple(ItemHelpers.input_to_new_input_list(nested.input_history))
27+
)
28+
29+
print("\n--- Handoff transcript ---")
30+
for idx, item in enumerate(history_items, start=1):
31+
print(f"Turn {idx}: {json.dumps(item, indent=2, ensure_ascii=False)}")
32+
print("--- end of transcript ---\n")
33+
34+
return nested
35+
36+
37+
router_agent = Agent(
38+
name="Router",
39+
instructions=(
40+
"You greet the user and then call the math handoff tool whenever the user asks for"
41+
" calculation help so the specialist can respond."
42+
),
43+
handoffs=[handoff(math_agent, input_filter=log_handoff_history)],
44+
)
45+
46+
47+
async def main() -> None:
48+
result = await Runner.run(
49+
router_agent,
50+
"Hi there! Could you compute 784 + 219 and explain how you got the result?",
51+
)
52+
print("Final output:\n", result.final_output)
53+
54+
55+
if __name__ == "__main__":
56+
asyncio.run(main())

0 commit comments

Comments
 (0)