> For the complete documentation index, see [llms.txt](https://docs.edisonscientific.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edisonscientific.com/edison-client/agent-configurations.md).

# Agent Configurations

{% hint style="info" %}
NOTE: These settings and methods are only available for enterprise organizations among users with admin/write permissions for agents.&#x20;
{% endhint %}

### Overview

A profile is the definition of an agent: which capability packages it has, which tools it can call, which prompt files compose its system prompt, how much CPU and memory it runs on, which sub-agents it may delegate to, and its timeouts.

You author a `Profile` and pass it to a deployment. The deployed job's `name` is what you then pass to `run_tasks_until_done` and `send_chat_message` — identical in use to the built-in `JobNames` entries.

```python
from edison_client import EdisonClient, JobDeploymentConfig
from edison_client.models.profile import BaseImageTier, Profile, Timeouts

client = EdisonClient()
```

The Edison platform supports three different means of modifying agents:

| Layer                     | Object                                          | Fixed at           | Changed by                          |
| ------------------------- | ----------------------------------------------- | ------------------ | ----------------------------------- |
| **1. Profile**            | `Profile` on `JobDeploymentConfig`              | Deploy             | Editing the profile and redeploying |
| **2. Per-call overrides** | `RuntimeConfig`, chat `ttl_seconds` / `timeout` | Every call         | The call itself                     |
| **3. Directives**         | `ResolvedDirective`                             | Next session start | A directive binding — no redeploy   |

You should modify the lowest layer that does the job. Directives are fastest and don't need a new deploy, profile changes with custom code often need a new build.

### Deploying an agent

Deployment is the existing `create_job` path with one new field. `profile` takes the profile object, so nothing about the agent needs to pre-exist inside the image:

```python
acme_profile = Profile(
    base_image_tier=BaseImageTier.CHEM,
    packages=["e14c-core", "e14c-client", "e14c-data-storage", "e14c-chemistry"],
    auto_skills=["glyph-structure-recognition"],
    memory="32Gi",
    timeouts=Timeouts(job=2 * 60 * 60, code_cell=20 * 60),
)

config = JobDeploymentConfig(
    name="acme-assay-analysis",
    profile=acme_profile,                # the object, not a name
    environment_variables=ENV_VARS,
    force=True,
)
client.create_job(config)
```

Then drive it exactly like a built-in job:

```python
client.run_tasks_until_done({
    "name": "acme-assay-analysis",
    "query": "Transcribe every Markush structure in the attached figures.",
})

client.send_chat_message(project_id, "Which figures have R-group variability?",
                        job_name="acme-assay-analysis")
```

`container_config` still wins over `Profile.cpu` / `Profile.memory` where the two overlap — it is infrastructure configuration rather than agent definition.

Everything else about deployment is unchanged: `name` is the job identity, `force` overwrites an existing deployment of the same name without prompting, and `create_job` returns the build metadata.

### The `Profile` schema

| Field                      | Type                   | Default                                                                                   | Consumed at | Meaning                                                                                        |
| -------------------------- | ---------------------- | ----------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------- |
| `base_image_tier`          | `BaseImageTier`        | `5-r`                                                                                     | **build**   | Container tier (see below)                                                                     |
| `packages`                 | `list[str]`            | Core packages                                                                             | **build**   | Capability packages installed into the code-execution environment. Will be parsed and built.   |
| `cpu`                      | `float \| None`        | `5`                                                                                       | **deploy**  | CPU request                                                                                    |
| `memory`                   | `str \| None`          | `"16Gi"`                                                                                  | **deploy**  | Memory request                                                                                 |
| `timeouts`                 | `Timeouts`             | see below                                                                                 | **deploy**  | Job / cell / LLM budgets                                                                       |
| `subagent_jobs`            | `SubagentJobs \| None` | `None`                                                                                    | **deploy**  | Which sub-agents this agent may delegate to                                                    |
| `subordinate_job`          | `str \| None`          | `None`                                                                                    | **deploy**  | Job this agent may spawn subordinate chat sessions as                                          |
| `model_override`           | `str \| None`          | `None`                                                                                    | **deploy**  | LLM for this profile; `None` uses the platform default. Expands to a fallback chain at runtime |
| `tools`                    | `list[str]`            | `run_cell`, `reset_kernel`, `submit_answer`, `read`, `explore`, `view_docs`, `load_skill` | session     | Tools registered for the agent. Must include `run_cell` and `reset_kernel`                     |
| `prompt_files`             | `list[str]`            | `BASE_GENERIC.md`, `SUBAGENT_PRINCIPLES.md`, + common                                     | session     | Markdown files composed into the system prompt, in order                                       |
| `workspace_prompt_files`   | `list[str]`            | `["MEMORY.md", "USER.md"]`                                                                | session     | Prompt files sourced from the workspace rather than the image                                  |
| `chat_prompt_files`        | `list[str]`            | `[]`                                                                                      | session     | Extra prompt files applied only in chat mode                                                   |
| `auto_skills`              | `list[str]`            | `[]`                                                                                      | session     | Skills loaded without the agent asking for them                                                |
| `system_reminder`          | `str \| None`          | `None`                                                                                    | session     | Markdown file re-injected periodically                                                         |
| `system_reminder_interval` | `int`                  | `20`                                                                                      | session     | Steps between reminder injections                                                              |
| `bootstrap_data_entry`     | `str \| None`          | `None`                                                                                    | session     | Data storage entry seeded into the workspace at start                                          |
| `workspace_callbacks`      | `list[str]`            | `[]`                                                                                      | session     | Named workspace callbacks to run                                                               |
| `orchestrator`             | `bool`                 | `False`                                                                                   | both        | Delegates rather than executing directly                                                       |
| `chat`                     | `bool`                 | `False`                                                                                   | both        | Multi-turn chat agent rather than one-shot                                                     |
| `chat_reasoning_effort`    | `str \| None`          | `None`                                                                                    | session     | Required when `chat` is `True`                                                                 |
| `unsafe_explore`           | `bool`                 | `False`                                                                                   | session     | Allows reads and shell outside the workspace. Leave `False` unless the deployment is isolated  |

Changing `base_image_tier` or `packages` means the redeploy rebuilds an image, which can take up to an hour. Changing anything else reuses the cached image, so a prompt or timeout edit redeploys quickly. Either way it is a redeploy — see directives for changing a live agent's guidance without one.

#### `BaseImageTier`

Tiers are cumulative — each includes everything below it. Pick the lowest tier that covers your dependencies, since tier drives image size and cold-start time.

| Tier            | Contains                                   |
| --------------- | ------------------------------------------ |
| `0-kernel-base` | Python, system packages, base pip packages |
| `1-data`        | Core scientific Python stack               |
| `2-bio`         | Bioinformatics tools                       |
| `3-chem`        | Chemistry tooling (RDKit et al.)           |
| `4-mamba`       | Conda/Mamba environment packages           |
| `5-r`           | R language and bioconda tools              |

A package needing native libraries from a higher tier will not import. This is the most common cause of a profile that deploys cleanly and then fails on its first cell, so check your packages against the tier before deploying.

#### `Timeouts`

| Field       | Type            | Default     | Meaning                                                  |
| ----------- | --------------- | ----------- | -------------------------------------------------------- |
| `job`       | `float`         | `3600` (1h) | Whole-job budget in seconds                              |
| `code_cell` | `float`         | `900` (15m) | Per-cell execution budget                                |
| `llm_call`  | `float \| None` | `None`      | Per-request LLM timeout; `None` leaves provider defaults |

Chat agents want a larger `job` — 4 hours is the platform default for chat profiles — because the budget covers a whole turn including tool work. The agent receives a "wrap up" warning at a fraction of `job`, so raising the job budget widens that window automatically.

#### `SubagentJobs`

Which specialists the agent may delegate to. `None` for an entry means **no access** — the standard way to prevent an agent from recursing into itself.

| Field                 | Serialized alias      | Delegates                         |
| --------------------- | --------------------- | --------------------------------- |
| `analysis`            | `analysis`            | Data analysis                     |
| `analysis_gpu`        | `analysis-gpu`        | GPU data analysis                 |
| `molecules`           | `molecules`           | Chemistry / small-molecule design |
| `data_retrieval`      | `data-retrieval`      | Dataset retrieval                 |
| `literature`          | `literature`          | Literature review                 |
| `artifact_generation` | `artifact-generation` | Long-form document authoring      |

Both spellings are accepted on input; the kebab-case aliases are the serialized format. Every value must be a job that is actually deployed and that you can reach — your own deployments are eligible, so an orchestrator can delegate to a worker you built.

### Authoring profiles

#### Validation

Two things are checked when you construct a `Profile`, so you find out at author time rather than at first run:

1. **`tools` contains `run_cell` and `reset_kernel`.**
2. **Unknown fields are rejected** — no silently-ignored typos.

#### Variants

A profile is a plain pydantic model, so a variant is `model_copy`. This is how you get an A/B pair: same profile, one field changed, two job names.

```python
cheap_profile = acme_profile.model_copy(update={"model_override": "openai/gpt-5-mini"})

client.create_job(JobDeploymentConfig(
    name="acme-assay-analysis-mini",
    profile=cheap_profile,
    environment_variables=ENV_VARS,
    force=True,
))
```

`model_copy(update=...)` **replaces** list fields rather than merging them. To add one package, pass the full list:

```python
acme_profile.model_copy(update={"packages": [*acme_profile.packages, "e14c-proteins"]})
```

Profiles also round-trip as JSON (`Profile.model_validate(blob)`), which is the form to use if you want to keep them under version control on your side or review them as config rather than code.

#### A worker profile, serialized

```json
{
  "base_image_tier": "5-r",
  "orchestrator": false,
  "chat": false,
  "chat_reasoning_effort": null,
  "cpu": 5.0,
  "memory": "16Gi",
  "packages": [
    "e14c-core", "e14c-client", "e14c-web", "e14c-llm", "e14c-data-retrieval",
    "e14c-data-storage", "e14c-reports", "e14c-chemistry", "e14c-subagents"
  ],
  "tools": [
    "run_cell", "reset_kernel", "submit_answer",
    "read", "explore", "view_docs", "load_skill"
  ],
  "prompt_files": [
    "BASE_DATA_ANALYSIS.md", "SUBAGENT_PRINCIPLES.md", "OUTPUT.md",
    "R_GUIDELINES.md", "WORKSPACE_WORKER.md", "VERIFICATION.md",
    "NOTEBOOK.md", "PROMPTS_GUIDANCE.md", "MARKDOWN_RENDERING.md"
  ],
  "workspace_prompt_files": ["MEMORY.md", "USER.md"],
  "chat_prompt_files": [],
  "auto_skills": ["subagents"],
  "system_reminder": null,
  "system_reminder_interval": 20,
  "bootstrap_data_entry": null,
  "subagent_jobs": {
    "analysis": null,
    "analysis_gpu": null,
    "molecules": null,
    "data_retrieval": "job-futurehouse-data-analysis-heron-data",
    "literature": null,
    "artifact_generation": null
  },
  "subordinate_job": null,
  "workspace_callbacks": [],
  "unsafe_explore": false,
  "model_override": "openai/gpt-5.6-sol",
  "timeouts": { "job": 3600.0, "code_cell": 900.0, "llm_call": null }
}
```

### Layer 2 — per-call overrides

Redeploying is not the only way to change behavior. For a one-off, override at call time instead.

#### `RuntimeConfig` (tasks)

Passed as `runtime_config` on a `TaskRequest`. `extra="forbid"`; must be JSON serializable.

| Field                | Type                     | Meaning                                            |
| -------------------- | ------------------------ | -------------------------------------------------- |
| `timeout`            | `int \| None`            | Maximum execution time in seconds                  |
| `environment_config` | `dict[str, Any] \| None` | Kwargs for the environment constructor — see below |
| `continued_job_id`   | `UUID \| None`           | Continue from a previous task                      |
| `world_model_id`     | `UUID \| str \| None`    | World model to attach                              |
| `agent`              | `AgentConfig \| None`    | Agent config override                              |
| `max_steps`          | `int \| None`            | **Deprecated** — use `timeout`                     |

#### `environment_config` keys

Validated with `extra="forbid"`, so a typo raises rather than being ignored.

| Key                      | Type                | Effect                                                                                                                    |
| ------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `data_storage_uris`      | `list[str]`         | Data entries to download into the workspace (`data_entry:<uuid>`)                                                         |
| `tool_names`             | `list[str] \| None` | **Replaces** the profile's `tools`. Must still contain `run_cell` and `reset_kernel`                                      |
| `prompting_config`       | `object`            | `system_prompt` here bypasses `prompt_files` entirely; also `additional_system_prompt_guidelines`, `output_format_prompt` |
| `llm_config`             | `object`            | Overrides the model and fallback chain from `model_override`                                                              |
| `compaction_config`      | `object`            | Context-compaction behavior                                                                                               |
| `agent_name`             | `str \| None`       | Display-name override                                                                                                     |
| `text_only`              | `bool`              | Strip images from tool responses, for text-only LLM backends                                                              |
| `guidance_dag_overrides` | `dict`              | Register or replace guidance corpora                                                                                      |
| `evaluate_config`        | `dict \| None`      | Evaluation harness settings                                                                                               |

```python
client.run_tasks_until_done({
    "name": "acme-assay-analysis",
    "query": "Cluster these expression profiles and characterize each cluster.",
    "runtime_config": {
        "timeout": 7200,
        "environment_config": {
            "data_storage_uris": [f"data_entry:{entry_id}"],
            "tool_names": ["run_cell", "reset_kernel", "submit_answer", "read"],
        },
    },
})
```

#### Chat session overrides

Chat has no `runtime_config`. Its two per-session knobs are arguments on `send_chat_message`, and they override the profile's timeouts for that session only:

| Argument      | Bounds    | Overrides                                                 |
| ------------- | --------- | --------------------------------------------------------- |
| `ttl_seconds` | 60–86400  | How long the sandbox survives idle                        |
| `timeout`     | 30–172800 | How long one turn may run — the `Timeouts.job` equivalent |

### Layer 3 — directives

Directives change a deployed agent's behavior without any redeploy at all. A directive is guidance — a system-prompt fragment or a skill — bound to a profile and resolved on every session start.

```python
from edison_client import EdisonClient

client = EdisonClient()

# 1. Register once
directive = client.create_directive(
    key="assay-units-policy",
    injection="system_prompt",
    organization_id=42,
)

# 2. Publish content — inert until bound
v1 = client.publish_directive_version(
    directive.directive_id,
    name="Assay unit conventions",
    body="Always report IC50 in nM. Convert and state the original unit.",
    change_note="Initial policy from the chem team",
)

# 3. Activate for one profile + one org
client.bind_directive(
    directive.directive_id,
    version=v1.version,
    profile_key="data-analysis",
    organization_id=42,
)
```

Publishing will not activate the directive. A version exists and is reviewable before it can affect a single session, which is what makes it safe to draft policy in a shared account. Nothing takes effect until step 3.

#### 1. Register the directive

`create_directive` establishes identity and never carries content.

| Parameter         | Type                         | Meaning                                               |
| ----------------- | ---------------------------- | ----------------------------------------------------- |
| `key`             | `str`                        | Stable identifier for the directive                   |
| `injection`       | `"system_prompt" \| "skill"` | How the content reaches the agent — fixed at creation |
| `organization_id` | `int \| None`                | Owning organization. `None` is tenant-wide            |

`injection` is fixed here because it determines what a version may carry:

| `injection`     | Version carries      | Reaches the agent as               |
| --------------- | -------------------- | ---------------------------------- |
| `system_prompt` | `body: str`          | Text appended to the system prompt |
| `skill`         | `dss_entry_id: UUID` | A skill loaded from data storage   |

#### 2. Publish a version

| Parameter            | Type                | Meaning                                               |
| -------------------- | ------------------- | ----------------------------------------------------- |
| `directive_id`       | `UUID`              | Directive to publish under                            |
| `name`               | `str \| None`       | Human label                                           |
| `description`        | `str \| None`       | Longer human label                                    |
| `body`               | `str \| None`       | The prompt fragment — `system_prompt` directives only |
| `dss_entry_id`       | `UUID \| None`      | The skill entry — `skill` directives only             |
| `change_note`        | `str \| None`       | Why this version exists; shown in version history     |
| `directive_metadata` | `JsonValue \| None` | Free-form metadata                                    |

Versions are **immutable and monotonic**. Editing guidance means publishing a new version, so `change_note` is the audit trail for why an agent's behavior changed:

```python
v2 = client.publish_directive_version(
    directive.directive_id,
    name="Assay unit conventions",
    body=(
        "Always report IC50 in nM. Convert and state the original unit. "
        "For Ki, report in nM as well and note the assay type."
    ),
    change_note="Extend to Ki after the Q3 review",
)
```

A skill directive publishes an entry id rather than text:

```python
skill = client.store_file_content("Glyph OCSR skill", "SKILL.md", project_id=project_id)
client.publish_directive_version(
    skill_directive.directive_id,
    name="Glyph structure recognition",
    dss_entry_id=skill.data_storage.id,
)
```

#### 3. Bind it

`bind_directive` is what activates a version, and it pins that version explicitly.

| Parameter         | Type          | Meaning                                                   |
| ----------------- | ------------- | --------------------------------------------------------- |
| `directive_id`    | `UUID`        | Directive to activate                                     |
| `version`         | `int`         | The version to pin                                        |
| `profile_key`     | `str \| None` | Profile to apply to. `None` is a wildcard — every profile |
| `organization_id` | `int \| None` | Organization to apply to. `None` is a wildcard            |
| `user_id`         | `str \| None` | Single user. `None` is a wildcard                         |
| `order`           | `int \| None` | Application order among directives on the same session    |

Bindings resolve across `(profile_key, organization_id, user_id)`, where a null column is a wildcard and the **most specific binding wins**. That is how a tenant default and a per-org exception coexist:

```python
# Tenant-wide floor
client.bind_directive(directive.directive_id, version=v1.version, profile_key="data-analysis")

# One org gets the stricter revision instead
client.bind_directive(
    directive.directive_id, version=v2.version,
    profile_key="data-analysis", organization_id=42,
)
```

Because a binding pins a version, publishing never changes a live agent. Rolling forward is a second `bind_directive` against the new version — the same explicitness as redeploying a profile, without the redeploy. Rolling back is a bind against the old version. `unbind_directive(directive_id, ...)` with the same scope arguments deactivates it and leaves the versions intact.

#### Verifying what an agent will see

`resolve_directives` answers with exactly what the resolver hands a session for a given profile — the check to run after binding, and the fastest way to debug a directive that appears to be doing nothing.

```python
resolved = client.resolve_directives(profile_key="data-analysis")

for directive in resolved.directives:
    print(directive.injection, directive.key, directive.version)
```

Each entry is a `ResolvedDirective` (`edison_client.models.directive`):

| Field                    | Type                | Meaning                          |
| ------------------------ | ------------------- | -------------------------------- |
| `directive_id`           | `UUID`              | Stable directive identity        |
| `key`                    | `str`               | Directive key                    |
| `order`                  | `int`               | Application order                |
| `version_id` / `version` | `UUID` / `int`      | The pinned version that resolved |
| `name` / `description`   | `str \| None`       | Human labels                     |
| `directive_metadata`     | `JsonValue \| None` | Free-form metadata               |
| `version_created_at`     | `datetime`          | When this version was created    |

`injection` is the discriminator: `body` is set on `system_prompt` directives and `dss_entry_id` on `skill` directives, never both.

Two behaviors to expect. A directive missing from this list is almost always a binding-scope problem rather than a content problem — check `profile_key` against the profile you actually deployed. And resolution is deliberately soft-failing on the agent side: if the resolver is unreachable the session proceeds *without* directives rather than aborting, so a directive is guidance an agent normally has, not a guarantee it always has. Anything that must hold every run belongs in the profile's `prompt_files`. Errors surface as `DirectiveResolveError` when you call the resolver yourself.

#### Choosing between a directive and a profile edit

| You want to                                       | Use                                                                           |
| ------------------------------------------------- | ----------------------------------------------------------------------------- |
| Add a skill or prompt fragment for one org        | **Directive** — no redeploy, effective next session                           |
| Draft guidance now, activate it later             | **Directive** — publish the version, bind when approved                       |
| Roll a guidance change back                       | **Directive** — bind the previous version                                     |
| Guarantee guidance on every run                   | **Profile** `prompt_files` — directives soft-fail, prompt files do not        |
| Change guidance for every user of the agent       | **Profile** `prompt_files` / `auto_skills` — a redeploy, but no image rebuild |
| Add a capability package or change the image tier | **Profile** — a redeploy with an image rebuild                                |
| Try something once                                | **`environment_config`** on the call                                          |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.edisonscientific.com/edison-client/agent-configurations.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
