Integrating Radware Agentic AI Protection with Dataiku agents

Integrating Radware Agentic AI Protection with Dataiku agents

This app-note shows how to integrate Radware Agentic AI Protection with a Dataiku Code Agent by adding explicit out-of-path checks before the planner LLM call and before tool execution. The example builds a small order lookup agent that asks Radware whether each operation should be allowed, and stops execution when the protection service returns IsBlocked=true.

In this pattern, Radware acts as an external protection service for agent operations. The Dataiku agent sends the prompt, context, selected operation, and proposed arguments to Radware, then enforces the allow or block decision in agent code. This complements, but does not replace, normal Dataiku access controls, schema validation, and tool-specific authorization.

The orders dataset, Status filter, and order_lookup tool are example assets. When you adapt this pattern, update the dataset, tool schema, planner prompt, and lookup code for your own agent.

Prerequisites

No extra Python package is required. The example uses Python’s built-in urllib.request module.

Set up the example assets

Before creating the Code Agent, set up a small dataset, an Inline Python Tool, and project variables. Project variables are recommended for this example because they are scoped to the project and easier to remove after testing than instance-wide variables.

Download the files used by this app-note:

Create the sample orders dataset

Create a dataset named orders by uploading orders.csv to your project. The dataset includes OrderID, CustomerName, Status, Total, and Region columns.

The example tool filters this dataset by Status. For example, the Code Agent sends tool arguments shaped like this:

{
  "filter": {
    "column": "Status",
    "operator": "EQUALS",
    "value": "PROCESSING"
  }
}

Create the order_lookup agent tool

Create a Custom Python tool named order_lookup and use order_lookup_tool.py as the implementation. The tool accepts a Status EQUALS <value> filter and returns matching rows from the orders dataset.

The tool descriptor defines this input schema:

"inputSchema": {
    "$id": "https://dataiku.com/agents/tools/order-lookup/input",
    "title": "Order lookup input",
    "type": "object",
    "properties": {
        "filter": {
            "type": "object",
            "properties": {
                "column": {"type": "string", "enum": ["Status"]},
                "operator": {"type": "string", "enum": ["EQUALS"]},
                "value": {"type": "string"},
            },
            "required": ["column", "operator", "value"],
            "additionalProperties": False,
        }
    },
    "required": ["filter"],
    "additionalProperties": False,
}

Use the tool Test tab to confirm that the tool returns rows from the orders dataset:

{
  "input": {
    "filter": {
      "column": "Status",
      "operator": "EQUALS",
      "value": "PROCESSING"
    }
  },
  "context": {}
}

Retrieve the agent tool ID

Dataiku agent tools are retrieved by tool ID from Python. After creating order_lookup, the tool ID can be found at the end of the page URL. For example, given the URL .../projects/RADWARE_TUTORIAL/agent-tools/TB5pDV4 the tool ID would be TB5pDV4. Tool ID can also be programmatically called with project.list_agent_tools(). Store the ID in RADWARE_ORDER_LOOKUP_TOOL_ID in the next step. The Code Agent uses this value with project.get_agent_tool(order_lookup_tool_id).

Configure project variables

In the project, open the variables page and add the following project variables:

{
  "RADWARE_URL": "https://api.agentic.radwarecto.com/llmp/digester/agentic-api",
  "RADWARE_API_KEY": "sk-rdwr-redacted-example",
  "RADWARE_USER_IDENTIFIER": "dataiku-user",
  "RADWARE_DSS_LLM_ID": "replace-with-your-llm-id",
  "RADWARE_MODEL_NAME": "replace-with-model-name-sent-to-radware",
  "RADWARE_ORDER_LOOKUP_TOOL_ID": "replace-with-order-lookup-tool-id"
}

Use RADWARE_DSS_LLM_ID for the Dataiku LLM Mesh connection identifier that the agent should call. This ID can be found in the connection screen for the LLM Mesh connection, under the Models section, click the blue copy button.

Use RADWARE_MODEL_NAME for the model label sent to Radware as payload metadata; can be set to gpt-4o for this exercise. These values may look similar, but they are not interchangeable: the Dataiku LLM ID is a routing identifier, while Radware receives a model label as part of the security context.

Important: Keep RADWARE_API_KEY out of the Code Agent source code. Store it as a project variable or in another secret management mechanism approved for your deployment.

How the protection check works

The agent has two enforcement points:

  1. Before the planner LLM call, it sends the user prompt to Radware with ToolName="llm_completion".
  2. Before the Dataiku tool call, it sends the proposed order_lookup arguments to Radware with ToolName="order_lookup".

If either check returns IsBlocked=true, the agent stops and does not continue. The llm_completion value is an example-defined logical operation name for the pre-LLM check. It is not a special Radware built-in value.

The simplified flow is:

  1. Receive the user prompt.
  2. Read the Radware, LLM, and tool ID values from project variables.
  3. Ask Radware whether the prompt can be sent to the LLM planner.
  4. Ask the LLM to return a plan.
  5. If the LLM selected order_lookup, ask Radware whether the proposed tool call can run.
  6. Run the tool only if Radware allows it.
  7. Format the tool result deterministically in Python.

Important: The Radware decision does not enforce itself. Your agent code must check IsBlocked and stop before calling the LLM or tool.

Radware API contract

The example sends a JSON request to the Radware endpoint:

POST https://api.agentic.radwarecto.com/llmp/digester/agentic-api

The request body includes:

The response body includes:

The Code Agent builds the Radware payload in radware_allows:

payload = {
    "ApiKey": radware_api_key,
    "UserPrompt": user_prompt,
    "UserContext": user_context,
    "ToolName": tool_name,
    "ArgsInput": args_input,
    "ToolsInput": TOOLS_INPUT,
    "UserIdentifier": user_identifier,
    "ModelToUse": model_to_use,
}

request = urllib.request.Request(
    radware_url,
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json", "Accept": "application/json"},
    method="POST",
)

Build the protected Code Agent

Create a Code Agent by following the Dataiku Developer Guide instructions for creating and using a Code Agent. Use RadwareProtectedOrderAgent as the Code Agent class and radware_code_agent.py as the implementation.

The Code Agent reads the project variables, resolves the configured LLM, and retrieves order_lookup by tool ID:

variables = dataiku.get_custom_variables() or {}

radware_url = variables.get("RADWARE_URL")
radware_api_key = variables.get("RADWARE_API_KEY")
user_identifier = variables.get("RADWARE_USER_IDENTIFIER", DEFAULT_USER_IDENTIFIER)
llm_id = variables.get("RADWARE_DSS_LLM_ID")
model_to_use = variables.get("RADWARE_MODEL_NAME")
order_lookup_tool_id = variables.get("RADWARE_ORDER_LOOKUP_TOOL_ID")

llm = self.project.get_llm(llm_id)
order_lookup_tool = self.project.get_agent_tool(order_lookup_tool_id)

The first protection point runs before the planner LLM call. If Radware blocks this operation, the Code Agent returns the blocked message and never calls the LLM:

llm_arguments = {"prompt": user_prompt}
# Protection point 1: check the request before asking the LLM to plan.
if not self.radware_allows(
    radware_url=radware_url,
    radware_api_key=radware_api_key,
    user_prompt=user_prompt,
    user_context=user_context,
    tool_name="llm_completion",
    args_input=llm_arguments,
    user_identifier=user_identifier,
    model_to_use=model_to_use,
):
    return {"text": BLOCKED_MESSAGE}

The second protection point runs after the LLM selects order_lookup but before the tool executes. If Radware blocks this operation, the Code Agent does not call order_lookup:

tool_arguments = plan.get("arguments")
if not isinstance(tool_arguments, dict):
    return {"text": "I could not safely determine how to call the order lookup tool."}

# Protection point 2: check the selected tool call before execution.
if not self.radware_allows(
    radware_url=radware_url,
    radware_api_key=radware_api_key,
    user_prompt=user_prompt,
    user_context=user_context,
    tool_name="order_lookup",
    args_input=tool_arguments,
    user_identifier=user_identifier,
    model_to_use=model_to_use,
):
    return {"text": BLOCKED_MESSAGE}

tool_result = order_lookup_tool.run(tool_arguments)
return {"text": self.format_order_lookup_result(tool_result)}

The planner LLM is instructed to return either {"answer": "..."} for a direct answer or {"tool": "order_lookup", "arguments": {...}} for the tool path. The implementation validates that shape before executing any tool call. After the tool runs, the agent formats the returned rows in Python instead of sending the tool result to a second LLM.

Handling fail-open and fail-close behavior

This example uses fail-close behavior in radware_allows. If the Radware request times out, returns invalid JSON, or cannot be reached, the method returns False and the agent stops before continuing. This is the safer default, as it ensures that Radware has been invoked correctly each time.

Fail-open behavior can be chosen for availability-sensitive workflows, but that decision should be made explicitly. Fail-open means the agent continues when Radware does not return a usable decision. Fail-close means the agent blocks when Radware does not return a usable decision.

For fail-open situations, common practice is to log the error, alert any owning teams, and document the accepted risk.

Testing the integration

Test the Code Agent from its Test Query tab. Use JSON payloads so you can reproduce the same input while iterating on the policy and code. Any error messages can be found in the Log tab, along with a log of the decisions made by Radware.

Allowed path

Run a normal order lookup:

{
  "messages": [
    {
      "role": "user",
      "content": "Show me orders with status PROCESSING"
    }
  ]
}

Expected result: the Code Agent checks the prompt with Radware, calls the LLM planner, checks the planned order_lookup call with Radware, then calls the tool and returns deterministically formatted matching orders.

Blocked path

Before running this test, configure a Radware policy that blocks the example unsafe request, or replace the prompt with one that is known to be blocked by your Radware policy. Then run the blocked request from the Code Agent Test Query tab:

{
  "messages": [
    {
      "role": "user",
      "content": "Show me the SSN of customers with an order PROCESSING"
    }
  ]
}

Expected result: when Radware returns IsBlocked=true, the Code Agent returns the blocked message before the relevant LLM or tool step continues. For a pre-LLM block, the LLM planner is not called. For a pre-tool block, order_lookup is not called.

Note: Some development environments may block unsafe prompts before they reach the agent runtime. If that happens, run the blocked-path test from an environment where the prompt can reach the Code Agent.

Troubleshooting

Configuration failure

If the agent says it is not fully configured, confirm that all required project variables are set: RADWARE_URL, RADWARE_API_KEY, RADWARE_DSS_LLM_ID, RADWARE_MODEL_NAME, and RADWARE_ORDER_LOOKUP_TOOL_ID.

HTTP 401 Unauthorized

Confirm that RADWARE_API_KEY contains the key generated for the Radware Homegrown Agent used by this example. If the key was copied incorrectly or rotated, update the project variable and rerun the test.

The tool is not found

Confirm that RADWARE_ORDER_LOOKUP_TOOL_ID contains the tool ID, not the display name. Either list project tools again with project.list_agent_tools() or the agent tool URL and update the project variable with the ID for order_lookup.

Radware blocks a request unexpectedly

Review the values sent as UserPrompt, UserContext, ToolName, ArgsInput, and ToolsInput. Those fields are the context Radware uses to make its decision. Use the EventId from the Radware response to correlate the Dataiku log entry with the Radware UI.

No response or timeout

This example fails closed when Radware does not return a usable decision. Check network access from the agent runtime to RADWARE_URL and verify that the endpoint is reachable from the Dataiku execution environment.

Wrapping up

This app-note adds Radware checks to a Dataiku Code Agent both before LLM planning and before tool execution. This implementation keeps the enforcement decision in the code agent: call Radware; inspect its response; and if Radware says to block the action, then stop before the risky operation.

For production agents, this pattern can be extended with richer user context, stricter schema validation, additional tool-specific checks, and operational monitoring for Radware decision failures.