diff --git a/pipeline/preprocessors/link_map.py b/pipeline/preprocessors/link_map.py index 55b2f6ae42..05ac31a567 100644 --- a/pipeline/preprocessors/link_map.py +++ b/pipeline/preprocessors/link_map.py @@ -35,6 +35,7 @@ class LinkMap(TypedDict): # Deep Agents "create_deep_agent": "deepagents/graph/create_deep_agent", "DeepAgentState": "deepagents/graph/DeepAgentState", + "SystemPromptConfig": "deepagents/graph/SystemPromptConfig", "SubAgent": "deepagents/middleware/subagents/SubAgent", "CompiledSubAgent": "deepagents/middleware/subagents/CompiledSubAgent", "SubAgentMiddleware": "deepagents/middleware/subagents/SubAgentMiddleware", diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index 5c343c33d6..e3a3ad07ca 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -31,6 +31,7 @@ import CustomizationToolsPy from '/snippets/code-samples/customization-tools-py. import CustomizationToolsJs from '/snippets/code-samples/customization-tools-js.mdx'; import CustomizationSystemPromptPy from '/snippets/code-samples/customization-system-prompt-py.mdx'; import CustomizationSystemPromptJs from '/snippets/code-samples/customization-system-prompt-js.mdx'; + import CustomizationMiddlewarePy from '/snippets/code-samples/customization-middleware-py.mdx'; import CustomizationMiddlewareJs from '/snippets/code-samples/customization-middleware-js.mdx'; import CustomizationMiddlewareDoPy from '/snippets/code-samples/customization-middleware-do-py.mdx'; @@ -52,7 +53,6 @@ import CustomizationOverviewPy from '/snippets/code-samples/customization-overvi import CustomizationOverviewJs from '/snippets/code-samples/customization-overview-js.mdx'; import CustomizationMcpPy from '/snippets/code-samples/customization-mcp-py.mdx'; import CustomizationMcpJs from '/snippets/code-samples/customization-mcp-js.mdx'; -import CustomizationPromptAssemblyPy from '/snippets/code-samples/customization-prompt-assembly-py.mdx'; import CustomizationGpSubagentProfilePy from '/snippets/code-samples/customization-gp-subagent-profile-py.mdx'; Build the harness around your goal. `create_deep_agent` gives you a production-ready foundation: connect it to your data, shape its behavior, and add the capabilities your use case needs. @@ -177,11 +177,7 @@ For detailed configuration options including stdio servers, OAuth authentication ## System prompt -Deep Agents come with a built-in system prompt. A deep agent's value comes from the orchestration layer the SDK provides on top of the model—planning, virtual-filesystem tools, and subagents—and the model needs to know those exist and when to reach for them. The built-in prompt teaches the agent how to use that scaffolding so you don't have to re-derive it for every project; tweak it through a [profile](/oss/deepagents/profiles#harness-profiles) or your own `system_prompt=` rather than copying it verbatim. - -When middleware add special tools, like the filesystem tools, it appends them to the system prompt. - -Each deep agent should also include a custom system prompt specific to its specific use case: +Deep Agents ship with a built-in base system prompt that teaches the agent how to use the harness scaffolding (planning, filesystem tools, subagents). Pass `system_prompt=` to prepend your own instructions before that base prompt: :::python @@ -191,64 +187,41 @@ Each deep agent should also include a custom system prompt specific to its speci ::: -### Prompt assembly - -Deep Agents builds the system prompt from up to four named parts so that caller-supplied instructions, the SDK's built-in agent guidance, and any model-specific [profile](/oss/deepagents/profiles) overrides can coexist with predictable precedence. Without this layering, a profile suffix tuned for Claude (for example) could overwrite or be overwritten by your `system_prompt=` argument depending on call order; the named slots make the ordering explicit and stable. - -In practice, most callers only encounter two slots: `USER` (your `system_prompt=`) and `BASE` (the SDK default). Selecting a model with a built-in profile—Anthropic or OpenAI today—adds a `SUFFIX`. The full four-part assembly is mainly relevant when you author a custom `HarnessProfile` or debug why a profile's text appears where it does. +When middleware adds special tools, like the filesystem tools, it appends its own guidance to the system prompt at runtime. -The four named parts (each may be absent): - -| Name | Source | Notes | -| -------- | ------------------------------------------------- | ------------------------------------------------- | -| `USER` | `system_prompt=` argument to `create_deep_agent` | `str` or `SystemMessage`; omitted when unset. | -| `BASE` | The SDK default (`BASE_AGENT_PROMPT`) | Always present unless replaced by a profile's `CUSTOM`. | -| `CUSTOM` | [`HarnessProfile.base_system_prompt`](/oss/deepagents/profiles#harness-profiles) | Replaces `BASE` outright when a matching profile sets it. | -| `SUFFIX` | [`HarnessProfile.system_prompt_suffix`](/oss/deepagents/profiles#harness-profiles) | Appended last when a matching profile sets it. | +:::python + +`SystemPromptConfig` requires `deepagents>=0.7.0a6`. + -The order is always **`USER` -> (`BASE` or `CUSTOM`) -> `SUFFIX`**, joined by blank lines (`\n\n`). Two invariants follow: +For full control over prompt assembly, pass a @[`SystemPromptConfig`] dict with `prefix`, `base`, and `suffix` keys: -1. **`USER` is always at the front.** The caller's text precedes any SDK or profile content, so persona/instructions take precedence regardless of which model is selected. -2. **`SUFFIX` is always at the end.** Profile suffixes sit closest to the conversation history, where model-tuning guidance lands most reliably. +- **`prefix`**: text placed before the base prompt (same as passing a bare string). +- **`base`**: replaces the built-in base prompt. Omit the key to keep the built-in base, or set it to `None` to drop the base entirely. +- **`suffix`**: text placed after the base prompt. -Assembled shapes (✓ = field is set, - = field is unset): +Parts are assembled in order: `prefix` -> `base` -> `suffix` -> any model-specific [profile](/oss/deepagents/profiles) suffix. Each part accepts a `str` or a `SystemMessage` (to preserve `cache_control` markers for Anthropic prompt caching). -| `system_prompt=` | profile `base_system_prompt` (`CUSTOM`) | profile `system_prompt_suffix` (`SUFFIX`) | Final assembled system prompt | -| ---------------- | :-------------------------------------: | :---------------------------------------: | ----------------------------- | -| `None` | - | - | `BASE` | -| `None` | - | ✓ | `BASE` + `SUFFIX` | -| `None` | ✓ | - | `CUSTOM` | -| `None` | ✓ | ✓ | `CUSTOM` + `SUFFIX` | -| `str` | - | - | `USER` + `BASE` | -| `str` | - | ✓ | `USER` + `BASE` + `SUFFIX` | -| `str` | ✓ | - | `USER` + `CUSTOM` | -| `str` | ✓ | ✓ | `USER` + `CUSTOM` + `SUFFIX` | +```python +# Overwrite the default base prompt: +create_deep_agent(..., system_prompt={"base": "..."}) -Worked example—built-in profiles (Anthropic, OpenAI) ship only a `system_prompt_suffix`, so a typical call lands in the `str` + `-` + `✓` row: +# No system prompt except from middleware: +create_deep_agent(..., system_prompt={"base": None}) - +# Sandwich the default base prompt: +create_deep_agent(..., system_prompt={"prefix": "...", "suffix": "..."}) +``` - - Passing a `SystemMessage` (rather than a string) triggers a different concatenation path: the right-hand assembly (`BASE`-or-`CUSTOM` plus any `SUFFIX`) is appended as an additional text content block onto the message's existing `content_blocks`. The same logical ordering applies (caller blocks first), and any `cache_control` markers on the caller's blocks are preserved—useful for placing explicit Anthropic prompt-cache breakpoints. - +For model-specific system prompt customization, such as replacing the base prompt or appending a suffix for a particular provider, use a [harness profile](/oss/deepagents/profiles#harness-profiles). +::: - The [prompt assembly](#prompt-assembly) overlay rules also apply to declarative [subagents](/oss/deepagents/subagents): each subagent re-runs profile resolution against **its own model**, then applies the resolved profile's `base_system_prompt` / `system_prompt_suffix` to its authored `system_prompt`. The subagent's `system_prompt` plays the `BASE` role; `CUSTOM` and `SUFFIX` come from the profile that matches the subagent's model (which may differ from the main agent's profile). - - | `spec["system_prompt"]` | profile `base_system_prompt` (`CUSTOM`) | profile `system_prompt_suffix` (`SUFFIX`) | Final subagent system prompt | - | ----------------------- | :-------------------------------------: | :---------------------------------------: | ---------------------------- | - | authored | - | - | authored | - | authored | - | ✓ | authored + `SUFFIX` | - | authored | ✓ | - | `CUSTOM` | - | authored | ✓ | ✓ | `CUSTOM` + `SUFFIX` | - - There is no `USER` segment for subagents. The spec's authored `system_prompt` is the closest analog and stays in the `BASE` slot. A profile that ships only a `system_prompt_suffix` (the common case for built-in Anthropic / OpenAI profiles) just appends to whatever the subagent author wrote. A profile that sets `base_system_prompt` will *replace* the authored prompt outright. + Declarative [subagents](/oss/deepagents/subagents) resolve profile overlays against their own model, then apply the resolved profile's `base_system_prompt` / `system_prompt_suffix` to the subagent's authored `system_prompt`. A profile that ships only a `system_prompt_suffix` (the common case for built-in Anthropic / OpenAI profiles) appends to the authored prompt. A profile that sets `base_system_prompt` replaces it outright. - The auto-added [general-purpose subagent](/oss/deepagents/subagents#the-general-purpose-subagent) follows the [prompt assembly](#prompt-assembly) overlay rules with one extra layer: the GP base prompt is resolved as **`general_purpose_subagent.system_prompt` (if set) -> `HarnessProfile.base_system_prompt` (if set) -> SDK general-purpose default**. The profile suffix layers on top either way. - - The two override fields can both carry a base-prompt replacement, but they are not interchangeable. `general_purpose_subagent.system_prompt` is general-purpose-specific configuration; `base_system_prompt` is a global override that primarily targets the main agent. When both are set, the **general-purpose-specific intent wins for the general-purpose subagent** so a user tuning both fields never sees their GP override silently dropped: + The auto-added [general-purpose subagent](/oss/deepagents/subagents#the-general-purpose-subagent) resolves its base prompt as **`general_purpose_subagent.system_prompt` (if set) -> `HarnessProfile.base_system_prompt` (if set) -> SDK general-purpose default**, with the profile suffix layered on top. When both override fields are set, the general-purpose-specific one wins so a caller tuning both fields never sees their GP override silently dropped: @@ -256,8 +229,6 @@ Worked example—built-in profiles (Anthropic, OpenAI) ship only a `system_promp | ----- | ------------------- | | Main agent | `"You are ACME's support orchestrator." + SUFFIX` | | GP subagent | `"You are a research subagent. Cite sources." + SUFFIX` | - - If `general_purpose_subagent.system_prompt` is unset, the GP subagent falls back to `base_system_prompt` (when set) and finally to the SDK general-purpose default. diff --git a/src/oss/deepagents/profiles.mdx b/src/oss/deepagents/profiles.mdx index fcbefeb73b..2204e79e35 100644 --- a/src/oss/deepagents/profiles.mdx +++ b/src/oss/deepagents/profiles.mdx @@ -42,11 +42,11 @@ A harness profile describes prompt-assembly, tool-visibility, middleware, and de :::python - Replace the base Deep Agents system prompt (`CUSTOM` in [Prompt assembly](/oss/deepagents/customization#prompt-assembly)). + Replace the base Deep Agents system prompt (the `base` key in [System prompt](/oss/deepagents/customization#system-prompt)). - Append text to the assembled base prompt (`SUFFIX` in [Prompt assembly](/oss/deepagents/customization#prompt-assembly)); applied to the main agent, declarative subagents, and the auto-added general-purpose subagent. + Append text after the caller's `suffix`, placed last in the assembled system prompt. Applied to the main agent, declarative subagents, and the auto-added general-purpose subagent. @@ -72,11 +72,11 @@ A harness profile describes prompt-assembly, tool-visibility, middleware, and de :::js - Replace the base Deep Agents system prompt (`CUSTOM` in [Prompt assembly](/oss/deepagents/customization#prompt-assembly)). + Replace the base Deep Agents system prompt (the `base` key in [System prompt](/oss/deepagents/customization#system-prompt)). - Append text to the assembled base prompt (`SUFFIX` in [Prompt assembly](/oss/deepagents/customization#prompt-assembly)); applied to the main agent, declarative subagents, and the auto-added general-purpose subagent. + Append text after the caller's `suffix`, placed last in the assembled system prompt. Applied to the main agent, declarative subagents, and the auto-added general-purpose subagent. @@ -102,13 +102,13 @@ A harness profile describes prompt-assembly, tool-visibility, middleware, and de :::python - Caller-supplied `system_prompt=` always sits at the front of the assembled prompt, and `system_prompt_suffix` always sits at the end—regardless of which model is selected. The same overlay rules apply to subagents: each subagent re-runs profile resolution against its own model. See [Prompt assembly](/oss/deepagents/customization#prompt-assembly) for the full per-case breakdown (main agent, subagents, and the general-purpose subagent). + Caller-supplied `system_prompt=` always sits at the front of the assembled prompt, and `system_prompt_suffix` always sits at the end—regardless of which model is selected. The same overlay rules apply to subagents: each subagent re-runs profile resolution against its own model. See [System prompt](/oss/deepagents/customization#system-prompt) for the full per-case breakdown (main agent, subagents, and the general-purpose subagent). ::: :::js - Caller-supplied `systemPrompt` always sits at the front of the assembled prompt, and `systemPromptSuffix` always sits at the end—regardless of which model is selected. The same overlay rules apply to subagents: each subagent re-runs profile resolution against its own model. See [Prompt assembly](/oss/deepagents/customization#prompt-assembly) for the full per-case breakdown (main agent, subagents, and the general-purpose subagent). + Caller-supplied `systemPrompt` always sits at the front of the assembled prompt, and `systemPromptSuffix` always sits at the end—regardless of which model is selected. The same overlay rules apply to subagents: each subagent re-runs profile resolution against its own model. See [System prompt](/oss/deepagents/customization#system-prompt) for the full per-case breakdown (main agent, subagents, and the general-purpose subagent). ::: diff --git a/src/oss/deepagents/subagents.mdx b/src/oss/deepagents/subagents.mdx index 649b72a667..ba405b7808 100644 --- a/src/oss/deepagents/subagents.mdx +++ b/src/oss/deepagents/subagents.mdx @@ -414,7 +414,7 @@ For full details on schema types and strategies (tool calling vs. provider-nativ In addition to any user-defined subagents, every deep agent has access to a `general-purpose` subagent at all times. This subagent: -- Uses its own [default system prompt with profile overlays applied](/oss/deepagents/customization#prompt-assembly) +- Uses its own [default system prompt with profile overlays applied](/oss/deepagents/customization#system-prompt) - Has access to all the same tools - Uses the same model (unless overridden) - Inherits skills from the main agent (when skills are configured) diff --git a/src/oss/python/releases/changelog.mdx b/src/oss/python/releases/changelog.mdx index 2348d4a3f8..3cc50b55ac 100644 --- a/src/oss/python/releases/changelog.mdx +++ b/src/oss/python/releases/changelog.mdx @@ -17,6 +17,7 @@ rss: true - **`write_file` now overwrites existing files**: `write_file` used to error if the target file already existed. It now overwrites it — use `edit_file` for targeted changes to an existing file. - **[Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance)**: A `middleware=` (or subagent `middleware`) instance whose `.name` matches a default now replaces that default in place, instead of erroring on duplicate middleware. - **[Restrict filesystem tools](/oss/deepagents/overview#virtual-filesystem-access)**: `FilesystemMiddleware` now accepts a `tools` allowlist to expose only a subset of the built-in filesystem tools to the model, building on the middleware-override behavior above. + - **[Structured system prompt configuration](/oss/deepagents/customization#system-prompt)**: The `system_prompt` parameter now accepts a `SystemPromptConfig` dict with `prefix`, `base`, and `suffix` keys, enabling callers to replace or remove the built-in base prompt and add text before or after it.