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
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 asmain.py:
DECIMAL_API_KEY, it sends nothing to DecimalAI, and says so once on stderr:
instructions=support.instructionsis a function the Agents SDK calls before the model request. It returns yourfallback=(later, the saved prompt), then the skill list and one sentence telling the model to load a skill before using it.*support.toolsadds one tool,load_skill. The model calls it with a skill’s name and gets the skill’s full text back.name=support.nameis the agent’s DecimalAI name. Runs are filed under it, whatever you name theAgent.- 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 fromDECIMAL_API_KEYby itself.
Your own tools
Async
await Runner.run(agent, question) works the same way as Runner.run_sync.
Add a registry skill
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:
.agents/skills/<name>/SKILL.md:
.agents/skills/refund-tone/SKILL.md
[authored] in .decimal/skills.toml:
decimalai skills remove <name> takes a skill off the list.
Add a key
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.tomlgives 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 raisesDecimalConfigError, 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.
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:
{{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: passresult.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 whenModelSettings.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).statebeside the chat’s history, and pass it back withsupport.conversation(thread_id, state=saved)before the chat’s next run. It isNonebefore 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 agroup_id. - LiteLLM turns an effort into a thinking budget (
budget_tokens), which Claude 5.x models refuse with a 400. Theextra_argsline sends adaptive thinking instead.
Its own skill loader
The Agents SDK’s sandboxSkills 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 plainSkills(...). 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.instructionsandsupport.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.
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.
Tracing only
To trace without DecimalAI’s prompt or skills, use the framework flag: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 thatAgent’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)orskill_dirs=[...]reads localSKILL.mdfiles and syncs the ones you wrote. Without them, orinit(skill_authority="harness"), it reads none.
Troubleshooting
DecimalAI turns its skills off for one agent in these cases, with one warning startingDecimalAI 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. UseStopAtTools(stop_at_tool_names=["lookup_order"]), with your own tools’ names.- The sandbox
Skillscapability on the same agent. Use one or the other. - Another tool named
load_skill. Rename yours. support.instructionswithout*support.tools. The agent gets its prompt with no skill list. Add*support.toolstotools=.
load_skill is on this agent, but its instructions are a fixed string. Passinstructions=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 fillRunContextWrapper.turn_input, so skills are listed in a fixed order instead of being ranked on each message.MaxTurnsExceededafter adding skills. Eachload_skillcall is one turn of the run. Raisemax_turns=onRunner.run.- No trace, though skills work. With
RunConfig(tracing_disabled=True),set_tracing_disabled(True)orOPENAI_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.tomllists nothing. With a key, check that the agent gets the skill. - No
load_skilltool on the agent. WithDECIMALAI_LOAD_SKILL_TOOL=0ordecimalai.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.