Skip to main content

What you get

agent_config() gives an OpenAI Agents SDK agent its prompt and skills, and you pass them into your own Agent(...). It runs before you have a DecimalAI account, with the skills you give it in your project: ones from the public registry, or ones you wrote. Add a key later and the same code connects: the agent appears in your workspace with those skills, every run is traced, and you change its prompt and skills in the dashboard without redeploying. Nothing in the Agents SDK is patched, so other agents in the process are left alone. This page is for the Agents SDK (from agents import Agent). For your own loop on the openai package, see OpenAI SDK.

Install

The extra installs openai-agents. Work in a git repository (git init): the next steps add files to commit, and adding a key writes one.

Run with no account

Save this as main.py:
Run it. With no DECIMAL_API_KEY, it sends nothing to DecimalAI, and says so once on stderr:
The project has no skills yet, so the model guesses the command.
  • instructions=support.instructions is a function the Agents SDK calls before the model request. It returns your fallback= (later, the saved prompt), then the skill list and one sentence telling the model to load a skill before using it.
  • *support.tools adds one tool, load_skill. The model calls it with a skill’s name and gets the skill’s full text back.
  • name=support.name is the agent’s DecimalAI name. Runs are filed under it, whatever you name the Agent.
  • No decimalai.init(). init() raises without a key, so a file that calls it can’t run keyless. With a key, agent_config() sets the SDK up from DECIMAL_API_KEY by itself.
To see where the list comes from, read Without a DecimalAI key.

Your own tools

Async

await Runner.run(agent, question) works the same way as Runner.run_sync.

Add a registry skill

Run main.py again. The model loads the skill and gives the real command, playwright-cli attach --cdp=http://localhost:9222. The command wrote the skill’s files to .agents/skills/, listed it in .decimal/skills.toml, and recorded its exact bytes in .decimal/skills.lock. Commit both directories:
To find others, see Browse the registry. A skill you wrote goes in the same folder, .agents/skills/<name>/SKILL.md:
.agents/skills/refund-tone/SKILL.md
The same command lists it, under [authored] in .decimal/skills.toml:
decimalai skills remove <name> takes a skill off the list.

Add a key

Get one from Settings → API keys, and run main.py again. The code stays the same. Its first run connects the project:
  • The agent is created in your workspace, with no saved prompt, so the fallback= in your code keeps running. DecimalAI never saves the prompt in your code.
  • The project’s skills are copied onto it, once. A registry skill keeps the version .decimal/skills.toml gives it. A skill you wrote is uploaded to your organization, not published. A registry skill whose files you edited is named, because the agent gets the registry’s text: to keep an edit, fork the skill on its page.
  • From now on its skills live on DecimalAI. Change them on the agent’s page, or with decimalai skills add <name>, which now adds to the agent instead of the file. Each run’s list is picked from them.
  • Commit .decimal/link.toml. In this project, agent_config() with no key now raises DecimalConfigError, so a deploy that lost its key fails at startup rather than running without its saved prompt and skills.
  • Every run is traced, filed under support.name. See Tracing. Runs made before the key are not uploaded.
The project is connected once per agent, and only in a git repository. The detail is in Without a DecimalAI key. With a key, decimalai init <agent> --framework openai-agents writes a program like this for an agent that already exists.

Change the prompt without redeploying

Write a prompt on the agent’s Prompt & skills page and save it. agent_config() keeps a local copy of the prompt and re-reads it in the background at most once a minute, so the next run gets the saved prompt within a minute, in place of fallback=. support.prompt_source tells you which is running: "fallback (no key)", then "fallback (none saved)", then "decimalai". With a key, fallback= is what runs when the agent has no saved prompt or DecimalAI can’t be reached at startup. With a key and no fallback=, an unreachable DecimalAI raises rather than start an agent with no prompt. Each run’s trace records which prompt it used. A saved prompt can hold variables, written {{name}}. The Agents SDK fills them from the context you pass to Runner.run, as attributes or dict keys:
A {{name}} in your fallback= is filled the same way. A variable with no value stops the run before the model call with decimalai.MissingPromptVariable, which names it. DecimalAI fills only the prompt, never a skill’s text, and the trace keeps the placeholder names, not their values.

Conversations

A chat’s history is yours: pass result.to_input_list(), plus the next message, to the next run. On any model but Claude, every run gets the newest prompt and skill list, so a prompt saved mid-chat reaches the chat’s next message. On Claude, see below.

Claude through LiteLLM

The Agents SDK sends earlier turns’ thinking back when ModelSettings.reasoning has an effort, and Claude then rejects a turn whose instructions or tools changed. So a chat on Claude keeps its first run’s instructions and load_skill definition: give the chat’s runs one RunConfig(group_id=...), the Agents SDK’s id for a chat thread. LitellmModel needs pip install "openai-agents[litellm]".
  • A newer saved prompt or skill list reaches the next chat, not an open one. Each message is still ranked, for its trace, unless the list was cut to fit: that chat keeps the ranking its list came from, so the skills it shows can be loaded.
  • Another process continues a chat from its state: store support.conversation(thread_id).state beside the chat’s history, and pass it back with support.conversation(thread_id, state=saved) before the chat’s next run. It is None before the chat’s first run in the process.
  • Variables keep the values of the chat’s first run, since they are part of its instructions.
  • A run with tracing disabled shows no group_id, so its instructions follow the newest prompt. One warning says so when a Claude chat sends thinking back without a group_id.
  • LiteLLM turns an effort into a thinking budget (budget_tokens), which Claude 5.x models refuse with a 400. The extra_args line sends adaptive thinking instead.

Its own skill loader

The Agents SDK’s sandbox Skills capability reads skill folders itself. decimalai.openai_agents.sandbox_skills() is that capability over the skills decimalai skills add installed:
  • Each load counts as Loaded, with the version of the copy the sandbox made for the model. The skills the sandbox lists count as Offered. A new session serves the new version after decimalai skills update, and a session you keep across runs keeps serving its copy.
  • Use sandbox_skills(), not a plain Skills(...). Only the helper’s lists and loads are recorded. It also warns when the sandbox would list nothing, and answers the model when it asks for a name that doesn’t exist, where the plain capability aborts the run.
  • No DecimalAI list or tool on this agent. Don’t also give it support.instructions and support.tools: DecimalAI switches its skills off for the agent.
  • Run from inside the project. sandbox_skills() raises when the project’s root is not under the working directory, because the sandbox would list no skills from there.
  • Scripts lose their exec bit. Run them through their interpreter (python3 .agents/my-skill/scripts/run.py), not as programs.
See Native skill loaders for how the version is decided and how the other frameworks compare.

Tracing

With a key, agent_config() registers DecimalAI as a trace processor beside OpenAI’s own, and changes no Agents SDK class. Each run is one trace, filed under support.name. With no key, nothing is traced. A trace holds:
  • Every model call, with its model, tokens and latency.
  • Tool calls with their inputs and outputs, handoffs between agents, and guardrail checks and results.
  • The agent’s manifest (the record of its tools, model, instructions and sub-agents), detected from the agent.
  • With agent_config(): the prompt version, the variable names it filled, any skill outage, and the skills offered, delivered and loaded, each delivery with the exact version the model received.
Multi-agent shows how handoffs appear in the trace.

Tracing only

To trace without DecimalAI’s prompt or skills, use the framework flag:
It traces every run of every Agent in the process and changes no agent. decimalai.openai_agents.instrument() does the same, and takes more. Using either with agent_config() is safe: there is still one trace processor, and each run is sent once.
  • instrument(agent=agent) also records that Agent’s manifest, with full tool schemas, before the first run.
  • instrument(exclusive=True) replaces the trace processors already registered, including OpenAI’s own, instead of running beside them.
  • instrument(disk_sync=True) or skill_dirs=[...] reads local SKILL.md files and syncs the ones you wrote. Without them, or init(skill_authority="harness"), it reads none.

Troubleshooting

DecimalAI turns its skills off for one agent in these cases, with one warning starting DecimalAI skills are off for this agent. The prompt still runs.
  • tool_use_behavior="stop_on_first_tool". A skill’s text would become the answer. Use StopAtTools(stop_at_tool_names=["lookup_order"]), with your own tools’ names.
  • The sandbox Skills capability on the same agent. Use one or the other.
  • Another tool named load_skill. Rename yours.
  • support.instructions without *support.tools. The agent gets its prompt with no skill list. Add *support.tools to tools=.
Other things to check:
  • load_skill is on this agent, but its instructions are a fixed string. Pass instructions=support.instructions, or the model gets no skill list.
  • this agent's tool_use_behavior function also receives load_skill results. Ignore them in your function, or a skill’s text could become the answer.
  • does not expose the run's input. This Agents SDK doesn’t fill RunContextWrapper.turn_input, so skills are listed in a fixed order instead of being ranked on each message.
  • MaxTurnsExceeded after adding skills. Each load_skill call is one turn of the run. Raise max_turns= on Runner.run.
  • No trace, though skills work. With RunConfig(tracing_disabled=True), set_tracing_disabled(True) or OPENAI_AGENTS_DISABLE_TRACING, each run still gets its skills, but the Agents SDK calls no trace processor.
  • HTTP 400, “bound to a different conversation”. A Claude chat’s runs need one group_id. See Claude through LiteLLM.
  • The model never loads a skill. After a run, print(support) names the prompt version and the skills on its list. With no key, an empty list means .decimal/skills.toml lists nothing. With a key, check that the agent gets the skill.
  • No load_skill tool on the agent. With DECIMALAI_LOAD_SKILL_TOOL=0 or decimalai.init(load_skill_tool=False), the text of the skills picked for each run goes into the instructions instead. The other switches are in Turn it off.