# Custom agents

Source: /docs/reference/python/agents.md

An `Agent` is a custom assistant users build for an app: a prompt, optional model selection, tabular context, backend API tools, and optional analysis/chart capabilities. Define one exported `Agent(...)` declaration in Python. This is separate from the coding agent implementing the project and the React conversation manager displaying it.

## Declaration and defaults [#declaration-and-defaults]

```text
Agent(
    provider: "openai" | "anthropic" | None = None,
    model: str | None = None,
    prompt: str = "",
    context: list[Table | TransformOutput] = [],
    tools: list[ApiRunner] = [],
    capabilities: AgentCapabilities = AgentCapabilities(),
)
AgentCapabilities(
    sql_analysis: bool = False,
    chart_generation: bool = False,
)
```

The SDK creates fresh context/tool lists and capabilities per instance. Provider
and model may be omitted for platform defaults. Set an explicit model only when that provider/model is available in your deployment. Unknown Agent fields
are rejected. Agent has no `name` property; identity comes from the registered
resource name or ID used in CLI and React calls.

Write a concrete prompt describing purpose, available data/tools, decision rules,
expected response structure and how to handle missing evidence or execution
failures. Describe the configured tools and data so the assistant knows what it can use.
Keep secrets out of prompts, source and user-visible results.

## Context and API tools [#context-and-api-tools]

- `Table("connector.sync")` declares synced tabular context.
- `Table("_upload.customers")` declares an uploaded table named `customers`. `_upload` is reserved and cannot name a connector.
- `TransformOutput("transform")` declares a single published transform output.
- `TransformOutput("transform.output")` selects one named published output.
- Only Table and TransformOutput are supported in Python Agent.context.
  DatabaseTable, Database, Input, Secret and File are not Agent context types.
- Uploaded documentation is platform-managed context outside the Python declaration.
  Attach documents through the managed upload workflow below.
- `ApiRunner("api_resource_name")` declares an API tool. It is a resource reference,
  not a direct HTTP client, a Python function or an arbitrary callable wrapper.

Discover actual resources and API input/response contracts before planning tools.
Declaring context does not create a missing table or run an upstream transform.
The platform binds tools to runtime resources; constructing a runner locally does
not bind it. A bound `runner.run(inputs: dict | None = None)` invokes the configured
API with scalar runtime inputs and returns its response. SDK-level api_id/client
binding arguments are runtime plumbing; customer declarations should use names,
not private clients or embedded UUID mappings. Tools resolve through the platform's
execution mode: preview selects committed API versions and preview data, while deployed runs select deployed API versions and production data. The runtime pins selected API tool versions for the turn.

```python
from vortexify import Agent, AgentCapabilities, ApiRunner, Table, TransformOutput

assistant = Agent(
    prompt=(
        "Help operators understand customer orders. Use available tables and tools "
        "to substantiate answers. Ask for a customer ID when it is needed. "
        "If a tool fails or data is missing, explain the limitation without inventing results."
    ),
    context=[Table("shopify.orders"), TransformOutput("customer_totals")],
    tools=[ApiRunner("customer_orders")],
    capabilities=AgentCapabilities(sql_analysis=True),
)
```

Resource names are illustrative; use existing resources and the project plan. The parser reads declarative constructors rather than executing
arbitrary Python to discover the definition; keep declarations statically readable.
The platform parses this declaration into resource metadata when source is saved; the Python file remains the versioned source of the definition.

## Capabilities [#capabilities]

Both capabilities are off by default. Enable SQL analysis when the assistant needs to answer questions from its declared data, and chart generation when it should propose visualizations of those answers.

`sql_analysis` exposes context-scoped table inspection and
analyze_table_data, using isolated DuckDB jobs. Poll with job_id alone; query_id is
the resulting analysis job ID. Start with query and tables=[{"alias": "orders",
"table_id": "id-from-list_tables"}]; SQL reads the provided aliases. Only the saved
agent version's declared context tables can be analyzed. chart_generation exposes generate_chart(query_id,
chart_name, chart_config). It references a completed analysis without rerunning SQL.
Chart config uses chartType (Bar, StackedBar, GroupedBar, Line, MultiLine, Scatter,
Pie), xKey and yKeys. Charts require a complete result with at most 1000 rows.
Apps read the chart tool result and fetch stored JSON rows with `client.getAnalysisData(query_id)`. See [analysis charts](/docs/reference/react/analysis-charts) for rendering.
Chart references must belong to the same saved agent version. Enable sql_analysis
as well when the agent needs to create the analyses it will chart.
Enable only capabilities required by the task and supported by the runtime.
Inspect the resulting analysis or tool execution when verifying the interaction.

## React integration and verification [#react-integration-and-verification]

An app uses `useVortexify()` and
`agentManager.consume(client.runAgent("order_assistant", {message, data, actions}))`.
See [React conversations and actions](/docs/reference/react/conversations-and-actions) for selection, bounded data snapshots, stream state, and frontend actions. Frontend execute functions stay in the browser; only
action names/descriptions/schemas and timeoutMs reach the backend. The SDK submits
AgentToolResult ({success, message?, data?}; message required on failure), and the
agent waits for that actual result. Errors and timeouts are failures; a timeout
means browser outcome is unknown. When get_current_view_context is available,
request it for fresh page context after navigation or changes. Initial
application data.current_view is a bounded snapshot, not a continuously live view.

Verify the assistant by trying a question that needs one of its declared tables or API tools, then inspect the saved conversation and tool results. In the platform, **Conversations** provides this history, **Files** lists attached documents, and **Code** exposes the saved declaration. Transform/API `vxfy_test` decorators do not define Agent tests.

## Attach searchable documentation [#attach-searchable-documentation]

Uploaded PDF, TXT, and Markdown documents are managed attachments rather than Python `Agent.context` entries. Create and implement the custom agent first, then upload and commit:

```sh
vortexify upload file ./operations.md --resource-type agent --resource order_assistant --wait --json
vortexify files agent order_assistant --json
vortexify commit --type agent --resource order_assistant
```

The saved agent version's attachments supply document-search and bounded-preview tools at runtime. No extra SDK context type or capability flag is required. An attachment to a connector is not automatically an attachment to a custom agent. CLI `files` commands show bounded beginning-text previews; full-document search is not a CLI command today. See [CLI attachments](/docs/reference/cli/resources#source-documentation-and-agent-attachments).

See [add an assistant](/docs/building/add-an-assistant) for the app workflow.

Python SDK 0.1.1 removes the `name` property from `Agent`. Identity comes from the
registered resource name or ID used by the CLI and React SDK. Remove `name=` from
existing declarations and update `requirements.in` to `vortexify==0.1.1` when upgrading.
