OpenAI Responses Executor
Executor for Ark agents backed by the OpenAI Responses API . Supports built-in tools (web search, code interpreter, file search), CFG/Lark grammar-constrained output, structured JSON output, MCP function tools, and stateless multi-turn threading via previous_response_id.
Overview
- Built-in Tools —
web_search_preview,file_search,code_interpreter,computer_useconfigured via annotations - CFG/Grammar Output — Lark grammar constraints enforced at token level (not by prompt) via
customtool type - Structured Output — JSON schema enforcement via
text.formatannotation; response is a valid JSON object - Multi-turn Threading — Conversations thread via
previous_response_id— no full history resent each turn - MCP Tools — Custom function tools from
spec.toolswired through Ark’s tool infrastructure - GPT-5 Support — Reasoning parameter (
effort) forgpt-5models; temperature disabled automatically - OTEL Tracing — Optional observability via
openinference-instrumentation-openai - A2A Protocol — Compliant with the Agent-to-Agent protocol for seamless Ark integration
Conversation Threading
Each request carries an A2A context_id → mapped to conversationId in the executor → used as a key to look up the last response_id on disk (/data/sessions/<conversationId>/response_id). Subsequent turns pass previous_response_id to the API instead of resending history — keeping payloads small and preserving server-side context.
Query CR A2A layer Executor OpenAI API
───────────────────── ────────────────── ──────────────────── ──────────────────
conversationId: "abc" → context_id: "abc" → lookup session file → previous_response_id: "resp_xyz"
save response.id ← response.id: "resp_xyz2"Install
ark install marketplace/executors/executor-openai-responsesOr with DevSpace:
cd executors/openai-responses
devspace deployOr with Helm:
helm install executor-openai-responses ./chart -n default --create-namespacePrerequisites
Model CRD (Required)
apiVersion: ark.mckinsey.com/v1alpha1
kind: Model
metadata:
name: openai-gpt-4o
spec:
provider: openai
type: completions
model:
value: gpt-4o
config:
openai:
apiKey:
valueFrom:
secretKeyRef:
name: openai-credentials
key: api-keyFor GPT-5 models, include baseUrl:
baseUrl:
valueFrom:
secretKeyRef:
name: openai-credentials
key: base-urlOpenAI Credentials Secret
kubectl create secret generic openai-credentials \
--from-literal=api-key=sk-... \
--from-literal=base-url=https://your-endpoint # optionalAnnotations
All configuration uses annotations with cascade: ExecutionEngine → Agent → Query (highest priority wins, merged by type key).
Built-in Tools
annotations:
executor-openai-responses.ark.mckinsey.com/tools: |
[
{
"type": "web_search_preview",
"user_location": {"type": "approximate", "country": "GB", "city": "London", "region": "London"}
}
]Available types: web_search_preview, file_search, code_interpreter, computer_use.
MCP Tools
The executor runs an MCP client for tools declared on the Agent via spec.tools (type: mcp). The ARK SDK resolves those to request.mcpServers; the executor connects (streamable-HTTP or SSE), lists the allow-listed tools, and exposes each to the model as an OpenAI function tool. When the model calls one, the executor dispatches tools/call to the MCP server and feeds the result back into the tool loop.
spec:
tools:
- type: mcp
name: tavily-search # Tool CR → MCPServer + toolNameThe synthesized function name is <mcpServer>__<toolName>. Tool-name matching is hyphen/underscore-insensitive; if none of an agent’s declared tools match what the server exposes, the executor logs the available names.
Deterministic prefetch (MCP chain)
For latency-sensitive agents, mcp-prefetch runs a chain of MCP tool calls before the model, injects the results into the prompt, and lets the model answer in a single no-tool turn — removing the model’s decide-to-call round-trip. Each step may bind its result so later steps template it in with {<bind>.<field>}; {input} is the cleaned user input.
annotations:
executor-openai-responses.ark.mckinsey.com/mcp-prefetch: |
[
{"tool": "companies-house__resolve_uk_company",
"args": {"company_name": "{input}"},
"bind": "ch", "label": "Companies House"},
{"tool": "tavily-search-mcp__tavily_search",
"args": {"query": "{ch.company_name} {ch.locality} official website UK"},
"label": "Web search results"}
]Each step: tool (a discovered <server>__<toolName>), args (string values are templated), optional bind (name for {bind.field} references), label (heading for the injected block), inject (default true). A single step object is also accepted. Domain logic lives in the agent config + the MCP servers — the executor stays generic. This is how a UK-company website-discovery agent hits ~2–4s while staying accurate: step 1 anchors the registered entity, step 2 searches biased by its town.
Reasoning (GPT-5 only)
annotations:
executor-openai-responses.ark.mckinsey.com/reasoning: '{"effort": "low"}'Effort values: "low", "medium", "high". Omitting the annotation defaults to "medium".
Use "low" for focused single-task agents (e.g. find one URL). Use "medium" or higher for agents that must gather multiple pieces of information (e.g. structured lookup with web search across several fields) — lower effort may not perform enough searches to find all required data.
File Attachments
Attach OpenAI file IDs to a query; the executor passes each as an input_file content part. File IDs come from the executor’s /v1/files API (below) or direct upload to the OpenAI Files API with the same credentials the agent’s Model uses.
annotations:
executor-openai-responses.ark.mckinsey.com/file-ids: '["file-abc123"]'Files attach on the turn they are new to the conversation: already-attached IDs are tracked per conversation and not re-sent (the threaded response state retains them).
Structured Output
Constrains the response to a JSON object matching the schema — enforced at token level:
annotations:
executor-openai-responses.ark.mckinsey.com/output-schema: |
{
"type": "object",
"properties": {
"company_name": {"type": "string"},
"website_url": {"type": "string"}
},
"required": ["company_name", "website_url"],
"additionalProperties": false
}Files API and UI
The executor serves an OpenAI-compatible Files API and a chat + upload UI at GET / on the pod:
POST /v1/files— multipart upload (file,purpose); proxied to the OpenAI Files APIGET /v1/files— list;DELETE /v1/files/{id}— deletePOST /chat— SSE chat with optionalfile_idsselection (UI shortcut, bypasses the Ark control plane)
All endpoints accept ?agent=<namespace>/<name>: credentials resolve from that agent’s Model CRD, so uploads land in the OpenAI project the agent’s Responses calls use, and listings are scoped to that agent’s uploads (UPLOADED_FILES_ONLY, see the README ).
Note: conversation threading uses
previous_response_id, which OpenAI does not support for Zero Data Retention organizations. On ZDR orgs each turn runs fresh — the executor detects the provider error, resets the stored conversation state, and returns a clear message telling the caller to retry.
Examples
See examples/ for ready-to-use YAML manifests and demo scripts.
Running the demo
Against a live cluster:
# Apply all CRDs and run each example, printing prompt, input and response
examples/demo.shLocally without Kubernetes:
export OPENAI_API_KEY=sk-...
export OPENAI_BASE_URL=https://your-endpoint # optional
export MODEL_NAME=gpt-5.2-2025-12-11 # optional
python3 examples/demo_local.pyExample manifests
| Example | What it shows |
|---|---|
website-search-agent.yaml | web_search_preview with UK location context |
company-lookup-agent.yaml | Web search + structured JSON output (company data) |
sql-generator-agent.yaml | CFG/Lark grammar-constrained SQL generation |
dsl-generator-agent.yaml | CFG/Lark grammar for a functional pipeline DSL |
companies-house-agent.yaml | MCP function tools via spec.tools |
Configuration
| Env Var | Default | Description |
|---|---|---|
SESSIONS_DIR | /data/sessions | Directory for persisting response_id per conversation |
MAX_TOOL_ITERATIONS | 10 | Max function-call loop iterations before returning |
OTEL_INSTRUMENTATION_ENABLED | false | Enable OpenAI OTEL instrumentation |
PORT | 8000 | HTTP server port |
Data Flow and Encryption
This section covers data surfaces specific to the OpenAI Responses executor. For platform-level surfaces (etcd, Kubernetes Secrets, broker, OTel collector, pod logs), see the Data Flow and Encryption operations guide.
Data at rest
| Surface | What lands there | Encrypted by default? | How to protect |
|---|---|---|---|
Session response IDs (/data/sessions/<conversationId>/response_id) | A single OpenAI response UUID per conversation. No message content — just the ID used for previous_response_id threading. | No. Plaintext file on the PVC. | Use an encrypted PersistentVolume. Note: response IDs alone do not contain message content, but they can be used to retrieve conversation history from the OpenAI API. |
| OpenAI server-side state | Full conversation history, tool outputs, file search indexes, code interpreter state. Retained server-side by OpenAI and linked by response_id. | Managed by OpenAI. | Review OpenAI’s data retention and encryption policies. The executor does not control server-side storage. |
Data in transit
| Hop | Protocol | TLS by default? | Notes |
|---|---|---|---|
| Executor → OpenAI API | HTTPS | Yes. The OpenAI Python SDK enforces HTTPS by default. | Custom baseUrl from the Model CRD can override — ensure any proxy or Azure endpoint uses HTTPS. |
| Executor → OpenAI built-in tools | HTTPS | Yes. Tool execution (web search, code interpreter, file search) happens server-side within OpenAI’s infrastructure. | User location data (country, city, region) is sent to OpenAI for web_search_preview context. |
| Controller → executor (A2A) | HTTP | No. | Deploy a service mesh for mTLS between pods. |
Logging
- The executor logs agent name, model name, conversation ID, and tool types at info level.
- Function tool arguments are logged unmasked at info level when tool calls are executed. If tools receive sensitive inputs, configure your log aggregator to filter these entries.
- API keys are retrieved at runtime from Model CRDs and passed directly to the SDK client — they are not logged except in unhandled error tracebacks.
Data retention and disposal
The executor does not manage data lifecycle — retention and cleanup are deployment configuration.
- Session response IDs: Files in
/data/sessions/accumulate indefinitely on the PVC. Schedule a CronJob to prune entries older than your retention window. The PVC surviveshelm uninstall— delete it explicitly when decommissioning. - OpenAI server-side state: Conversation history linked by
response_idis retained by OpenAI according to their data retention policies. The executor has no mechanism to request deletion. Deleting the localresponse_idfile breaks the threading link but does not remove data from OpenAI’s servers. Review OpenAI’s data retention terms for your usage tier.