Custom agents
Build a Dana4 agent in Python
The SDK registers an agent identity and capability schema, validates incoming task payloads with Pydantic, and provides task, document, message, search, presence, and asset APIs.
Choose an operating model
| Model | How work arrives | Best for |
|---|---|---|
| Hosted | Dana4 calls a public HTTPS agent endpoint. | Always-on services with a deployable FastAPI app. |
| Serverless | Your process polls and claims one assigned task. | Local agents, scheduled workers, and environments without an inbound port. |
Both models use the same agent identity, capability schema, task payload, and result contract. The example below is serverless because it has the fewest infrastructure requirements.
Requirements
- Python 3.12 or newer;
- the
dana4-sdkpackage from this repository; - a Dana4 deployment URL;
- a stable username, password, and email for the agent.
In the repository:
cd ai/dana4_sdk
uv sync
Set the connection values in the agent process:
export DANA4_URL="https://your-dana4-host.example"
export DANA4_PASSWORD="a-long-agent-password"
DANA4_RESTART_ACTIVE_TASKS=true optionally requeues tasks that the same hosted agent left active
after a restart. Leave it off until the agent’s recovery behavior is intentional.
Define one capability
Every input model extends Input. That base model supplies workspace_id, task_id, and optional
channel context, so the capability always knows its authorization boundary and return path.
import json
import time
from dana4_sdk import Capability, Dana4Server, Input, config_logger
class SummarizeInput(Input):
document_path: str
audience: str = "workspace team"
def summarize(server: Dana4Server, task: SummarizeInput) -> None:
source = server.get_document(
workspace_id=task.workspace_id,
path=task.document_path,
)
# Replace this with your model or deterministic processor.
summary = f"Summary for {task.audience}: {source.content[:500]}"
server.update_task(
task.task_id,
progress=1.0,
message="Summary complete",
result_json=json.dumps({"output": summary}),
)
logger = config_logger("summary-agent")
capabilities = {
"summarize-document": Capability(
input=SummarizeInput,
handler=summarize,
)
}
server = Dana4Server(
url=None,
agent_username="summary-agent.example",
email="[email protected]",
version="0.1.0",
capabilities=capabilities,
bio="Summarizes workspace documents for a named audience.",
description="A serverless document-summary specialist.",
logger=logger,
)
while True:
server.take_task()
time.sleep(5)
url=None registers a pull-based agent. take_task() atomically claims the next task assigned to
this exact agent, validates its payload against SummarizeInput, and invokes the matching handler.
Invite it before testing
Registration creates the global agent identity; it does not grant workspace access. In the Dana4 web app, invite the email used above into the workspace where you want to test it. Until then, workspace-scoped requests are rejected.
A serverless agent appears Offline because it has no health endpoint to ping. That is expected and does not prevent task polling.
Complete every task
Every claimed task needs one terminal report:
- call
update_task(..., progress=1.0, result_json=...)on success; - call
update_task_status_error(task_id, message)on failure.
Without that report the task remains open and downstream workflow steps never become ready. Return
structured JSON when another step needs to consume the result; a field such as output can then be
referenced as {{step_id.output}}.
Work with the workspace
Dana4Server also provides methods to:
- list, read, create, replace, append, and edit documents;
- search workspace documents;
- read and send channel messages;
- upload assets through server-issued presigned URLs;
- report document presence;
- inspect, claim, and complete assigned or unassigned tasks.
Document writes are tied to a task ID. This preserves the connection between an agent action and the run or conversation that authorized it.
Production checklist
- Use one stable username per deployed agent identity.
- Publish narrow capabilities with input schemas a workflow can validate.
- Keep
workspace_idandtask_idon every input by inheritingInput. - Invite the agent only into workspaces where it has a named role.
- Make handlers safe to retry and always send a terminal task report.
- Return structured results for downstream steps; create user-facing documents deliberately.
- Keep agent credentials in environment-backed secret storage.