diff --git a/docs/index.md b/docs/index.md index 935c4be5b..b7ed85349 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,6 +6,8 @@ The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) enables - **Handoffs**, which allow agents to delegate to other agents for specific tasks - **Guardrails**, which enable the inputs to agents to be validated - **Sessions**, which automatically maintains conversation history across agent runs +- **Tracing**, which lets you visualize and debug the flow of an agent's actions + 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. diff --git a/docs/running_agents.md b/docs/running_agents.md index 5581823b0..b86888784 100644 --- a/docs/running_agents.md +++ b/docs/running_agents.md @@ -128,8 +128,10 @@ You can use the Agents SDK [Temporal](https://temporal.io/) integration to run d The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview: -- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK. -- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods. -- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools. -- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK. -- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped. +- [`AgentsException`][agents.exceptions.AgentsException]: This is the base class for all exceptions raised within the SDK. It serves as a generic type from which all other specific exceptions are derived. +- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded]: This exception is raised when the agent's run exceeds the `max_turns` limit passed to the `Runner.run`, `Runner.run_sync`, or `Runner.run_streamed` methods. It indicates that the agent could not complete its task within the specified number of interaction turns. +- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError]: This exception occurs when the underlying model (LLM) produces unexpected or invalid outputs. This can include: + - Malformed JSON: When the model provides a malformed JSON structure for tool calls or in its direct output, especially if a specific `output_type` is defined. + - Unexpected tool-related failures: When the model fails to use tools in an expected manner +- [`UserError`][agents.exceptions.UserError]: This exception is raised when you (the person writing code using the SDK) make an error while using the SDK. This typically results from incorrect code implementation, invalid configuration, or misuse of the SDK's API. +- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered]: This exception is raised when the conditions of an input guardrail or output guardrail are met, respectively. Input guardrails check incoming messages before processing, while output guardrails check the agent's final response before delivery. \ No newline at end of file diff --git a/src/agents/agent.py b/src/agents/agent.py index c6f25b08f..bd1b13c2a 100644 --- a/src/agents/agent.py +++ b/src/agents/agent.py @@ -222,10 +222,17 @@ class Agent(AgentBase, Generic[TContext]): to True. This ensures that the agent doesn't enter an infinite loop of tool usage.""" def clone(self, **kwargs: Any) -> Agent[TContext]: - """Make a copy of the agent, with the given arguments changed. For example, you could do: - ``` - new_agent = agent.clone(instructions="New instructions") - ``` + """Make a copy of the agent, with the given arguments changed. + Notes: + - Uses `dataclasses.replace`, which performs a **shallow copy**. + - Mutable attributes like `tools` and `handoffs` are shallow-copied: + new list objects are created only if overridden, but their contents + (tool functions and handoff objects) are shared with the original. + - To modify these independently, pass new lists when calling `clone()`. + Example: + ```python + new_agent = agent.clone(instructions="New instructions") + ``` """ return dataclasses.replace(self, **kwargs) diff --git a/tests/test_agent_clone_shallow_copy.py b/tests/test_agent_clone_shallow_copy.py new file mode 100644 index 000000000..16291911c --- /dev/null +++ b/tests/test_agent_clone_shallow_copy.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from agents import Agent, function_tool, handoff + + +@function_tool +def greet(name: str) -> str: + return f"Hello, {name}!" + +def test_agent_clone_shallow_copy(): + """Test that clone creates shallow copy with tools.copy() workaround""" + target_agent = Agent(name="Target") + original = Agent( + name="Original", + instructions="Testing clone shallow copy", + tools=[greet], + handoffs=[handoff(target_agent)], + ) + + cloned = original.clone( + name="Cloned", + tools=original.tools.copy(), + handoffs=original.handoffs.copy() + ) + + # Basic assertions + assert cloned is not original + assert cloned.name == "Cloned" + assert cloned.instructions == original.instructions + + # Shallow copy assertions + assert cloned.tools is not original.tools, "Tools should be different list" + assert cloned.tools[0] is original.tools[0], "Tool objects should be same instance" + assert cloned.handoffs is not original.handoffs, "Handoffs should be different list" + assert cloned.handoffs[0] is original.handoffs[0], "Handoff objects should be same instance"