> For the complete documentation index, see [llms.txt](https://docs.karini.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.karini.ai/agentic-ai-workshops/ai-foundation-labs/code-optimization-agent.md).

# Code Optimization Agent

**Lab time:** about 60 minutes

## The Problem

A nightly SAS job is running slower every week. Somewhere in the script are hardcoded filters, repeated sorts, unnecessary loops, and a join that's quietly producing a Cartesian product. Finding them means reading the code line by line against its execution log, fixing each issue, and committing the change through version control so it can be reviewed and audited. With hundreds of legacy scripts, that work rarely gets done until something breaks.

## What You Will Build

A SAS Automation workflow that reviews and optimizes code on its own. Send it a job ID and it will:

1. Retrieve the job details, the SAS script, and its execution log
2. Review the script against the log and list each performance and correctness issue
3. Generate an optimized version of the script that fixes those issues
4. Commit the optimized script to the GitLab repository with a commit message, for governed, auditable deployment

## What You Will Learn

* Connect agents to outside systems with REST API tools and input schemas
* Split a task between two agents: one reviews, one optimizes and commits
* Pass one agent's output to another with node message context
* Trigger a workflow from an outside system with a webhook
* Compare models side by side and pick a primary and fallback model
* Monitor every run with webhook history and traces

## Build Order

Build the two agent prompts first, then the recipe. A recipe can only use prompts that are already published.

| Step | You create                                      | Contains                               |
| ---- | ----------------------------------------------- | -------------------------------------- |
| 1    | Agent: SAS Scripts Retrieval and Reviewer Agent | 2 tools: Gitlab API tool, Job API Tool |
| 2    | Agent: SAS Script Optimizer                     | 1 tool: JOB Commit Tool                |
| 3    | Workflow recipe: SAS Automation                 | Webhook, Start, 2 Agents, End          |
| 4    | API trigger                                     | One run started with curl              |
| 5    | Verification                                    | Webhook history and the GitLab commit  |

## Before You Start

* Access to the Karini AI platform
* Access to a GitLab repository with sample SAS scripts, and permissions to create branches and commits
* Access to the mock SAS command-line wrapper REST API, or equivalent simulated log and code endpoints
* The **SAS Scripts Retrieval and Reviewer Agent** and **SAS Script Optimizer** prompt templates, available in Prompt Playground
* Credentials for the REST API tools, provided separately
* A terminal with curl, to trigger the workflow
* Basic familiarity with SAS syntax and Git workflows

## Words You Will See

* **SAS:** Statistical Analysis System, a language widely used for data processing and analytics
* **Execution log:** the record SAS writes when a job runs, including notes, warnings, errors, and timings
* **Prompt template:** a ready-made prompt stored in Karini. You load it instead of typing it.
* **Tool:** something an agent can call, here REST APIs for jobs, files, and commits
* **Input schema:** the JSON structure that tells the agent what to send when it calls a tool
* **Node message:** a message context setting that passes a specific node's output to the next agent
* **Webhook:** a URL that lets an outside system start your recipe
* **Recipe:** the workflow that connects everything on a canvas
* **Publish:** saves a version that recipes can use. Saved but unpublished changes are not used.

## Step 1: Create the SAS Reviewer Agent Prompt

### 1.1 Create the prompt

1. Navigate to **Prompt Playground** on the left side.
2. Click **Add new** in the top right corner.
3. Click **Prompt templates** on the right side and select the **SAS Scripts Retrieval and Reviewer Agent** template.
4. Increase **Max State Updates** from **3** to **20** for complex reasoning.
5. Scroll down to the **Agent input** box and add this test query:

```
Job id 10002
```

6. Scroll up and rename your prompt, then save it. You'll return to Prompt Playground; select your prompt and open the **Tools** tab.

Save your changes before you navigate away to add tools, or they are lost.

### 1.2 Add tool 1: Gitlab API tool

1. Click **Add new** and fill in:

| Field        | Value                       |
| ------------ | --------------------------- |
| Name         | Gitlab API tool             |
| Description  | Copy the text below exactly |
| Type         | Rest API                    |
| Input Schema | Use the schema below        |
| Credentials  | Provided separately         |

Description text:

```
Retrieves details from SAS (Statistical Analysis System) files, including content information. Input requires a valid SAS file path. Returns comprehensive file information. Useful for data analysis and SAS file inspection tasks.
```

Input schema:

```json
{
    "$schema": "http://json-schema.org/schema#",
    "type": "object",
    "description": "Root configuration object for API requests",
    "properties": {
        "headers": {
            "type": "object",
            "description": "HTTP headers for the request",
            "properties": {
                "PRIVATE-TOKEN": {
                    "type": "string",
                    "description": "Authentication token for API authorization"
                }
            },
            "required": [
                "PRIVATE-TOKEN"
            ]
        },
        "endpoint": {
            "type": "string",
            "description": "Target URL or path for the API request"
        }
    },
    "required": [
        "endpoint",
        "headers"
    ]
}
```

2. Save the tool.

### 1.3 Add tool 2: Job API Tool

1. Click **Add new** and fill in:

| Field        | Value                       |
| ------------ | --------------------------- |
| Name         | Job API Tool                |
| Description  | Copy the text below exactly |
| Type         | Rest API                    |
| Input Schema | Use the schema below        |
| Credentials  | Provided separately         |

Description text:

```
Retrieves detailed information about a specific job using its unique job ID. Input requires a valid job identifier. Returns comprehensive job details including status, file URL, and related metadata. Useful for tracking and monitoring job progress or accessing job information.
```

Input schema:

```json
{
    "$schema": "http://json-schema.org/schema#",
    "type": "object",
    "description": "Root object containing API request parameters",
    "properties": {
        "query_params": {
            "type": "object",
            "description": "Container for query parameters used in the request",
            "properties": {
                "job_id": {
                    "type": "string",
                    "description": "Unique identifier for the job being queried"
                }
            },
            "required": [
                "job_id"
            ]
        }
    },
    "required": [
        "query_params"
    ]
}
```

2. Save the tool.

### 1.4 Test & Compare

1. Open the **Test & Compare** tab.
2. Select different models. Compare Claude Sonnet 4.5, Claude 3.5 Sonnet V2, and Claude Haiku 4.5.
3. Click **Test**.
4. Compare the outputs. Each should list the script's issues, each tied to a line in the execution log.
5. Click **Select as best answer** to set the best-performing model as the **Primary Model**.
6. Optional: click **Select as best answer** on another output to assign a **Fallback Model**.
7. Click **Save prompt run** in the right corner.
8. **Save** and **publish** the prompt.

## Step 2: Create the SAS Optimizer Agent Prompt

### 2.1 Create the prompt

1. Navigate to **Prompt Playground** on the left side and click **Add new**.
2. Click **Prompt templates** and select the **SAS Script Optimizer** Agent template.
3. Increase **Max State Updates** from **3** to **20** for complex reasoning.
4. Scroll down to the **Agent input** box and add this test input. It stands in for the reviewer agent's output: the script content plus the issues found in its log.

```
"{ "job_id": "10002", "file_content": "/\n===============================================================================\nDEMO SAS SCRIPT - DATA PROCESSING\n===============================================================================\nScript: data_processing.sas\nPurpose: Process customer data and generate summaries\nAuthor: Demo Team\nDate: 2024-11-10\n\nINTENTIONAL PERFORMANCE ISSUES:\n- Inefficient loops over datasets\n- Repeated sorts\n- Hardcoded filtering\n- Redundant calculations\n===============================================================================\n/\n\noptions fullstimer pageno=1 linesize=120;\n\n/* Inefficient data read /\ndata work.customer_data;\n set raw.customers;\n if country = \"USA\"; / Hardcoded filter /\nrun;\n\n/ Nested loop example /\ndata work.customer_summary;\n set work.customer_data;\n do i = 1 to 1000; / Dummy loop to simulate inefficiency */\n total\n", "issues": [ { "description": "Inefficient data filtering: The script uses a hardcoded filter (country = \"USA\") in the data step instead of using WHERE clause during initial data read, causing unnecessary I/O", "log_reference": "NOTE: Table WORK.CUSTOMER_DATA created with 1000000 observations." }, { "description": "Performance bottleneck: Multiple index statements detected which may impact performance", "log_reference": "WARNING: Multiple INDEX statements found. Performance may be impacted." }, { "description": "Memory management issue: Hash join implementation without proper memory allocation", "log_reference": "WARNING: Hash join used without proper memory allocation." }, { "description": "Critical join error: Cartesian product in join operation, which could result in exponential data growth", "log_reference": "ERROR: Potential cartesian product detected in join operation." }, { "description": "Resource intensive processing: The SQL procedure took over 15 seconds of real time", "log_reference": "NOTE: PROCEDURE SQL used (Total process time): real time 15.23 seconds cpu time 12.45 seconds" }, { "description": "Inefficient loop structure: The script contains a dummy loop (1000 iterations) that serves no practical purpose and degrades performance", "log_reference": "Script contains 'do i = 1 to 1000;' which is an unnecessary iteration" }, { "description": "NOTE: Script content appears to be truncated in the GitLab response, full review may be incomplete", "log_reference": "Script content ends abruptly at 'total'" } ] }"
```

5. Scroll up and rename your prompt, then save it. You'll return to Prompt Playground; select your prompt and open the **Tools** tab.

Save your changes before you navigate away to add tools, or they are lost.

### 2.2 Add the tool: JOB Commit Tool

1. Click **Add new** and fill in:

| Field        | Value                       |
| ------------ | --------------------------- |
| Name         | JOB Commit Tool             |
| Description  | Copy the text below exactly |
| Type         | Rest Api                    |
| Input Schema | Use the schema below        |
| Credentials  | Provided separately         |

Description text:

```
Creates and adds a new commit to a GitLab repository. Requires optimized SAS file content and commit message. Returns commit status and ID upon successful execution. Used for version control when saving changes to GitLab-hosted code repositories.
```

Input schema:

```json
{
    "$schema": "http://json-schema.org/schema#",
    "type": "object",
    "properties": {
        "query_params": {
            "type": "string",
            "description": "URL query parameters for the request"
        },
        "json_data": {
            "type": "object",
            "description": "Container for JSON payload data",
            "properties": {
                "content": {
                    "type": "string",
                    "description": "The main content or body of the data to be processed"
                },
                "commit_message": {
                    "type": "string",
                    "description": "Message describing the purpose or nature of the commit"
                }
            },
            "required": [
                "commit_message",
                "content"
            ]
        }
    },
    "required": [
        "json_data",
        "query_params"
    ]
}
```

2. Save the tool.

### 2.3 Test & Compare

1. Open the **Test & Compare** tab.
2. Select different models. Compare Claude Sonnet 4.5, Claude 3.5 Sonnet V2, and Claude Haiku 4.5.
3. Click **Test**.
4. Compare the outputs. Each should produce an optimized script that addresses the listed issues and report the commit status.
5. Click **Select as best answer** to set the best-performing model as the **Primary Model**.
6. Optional: click **Select as best answer** on another output to assign a **Fallback Model**.
7. Click **Save prompt run** in the right corner.
8. **Save** and **publish** the prompt.

## Step 3: Create a Workflow Recipe

### 3.1 Create the recipe

1. Navigate to **Recipes**.
2. Click **Add New** in the top-right corner and configure:

| Field   | Value          |
| ------- | -------------- |
| Name    | SAS Automation |
| Type    | Workflow       |
| Runtime | Karini         |

### 3.2 Add and connect the nodes

Drag each element onto the recipe canvas, then click it to configure it.

| # | Element | Label                                    | Settings                                                                                                                                                                                           |
| - | ------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Webhook | Webhook                                  | Query Method POST. Copy the Webhook URL and Webhook Token from the right panel for Step 4                                                                                                          |
| 2 | Start   | Start                                    | None                                                                                                                                                                                               |
| 3 | Agent   | SAS Scripts Retrieval and Reviewer Agent | Prompt = your SAS Scripts Retrieval and Reviewer Agent from Step 1. Messages = Last Messages                                                                                                       |
| 4 | Agent   | SAS Script Optimize                      | Agent = your SAS Script Optimizer from Step 2. State settings: Messages on, Message Context Node Message, Variable "question", Node SAS Scripts Retrieval and Reviewer Agent, Message Last Message |
| 5 | End     | End                                      | None                                                                                                                                                                                               |

Connect them in a straight line: Webhook → Start → SAS Scripts Retrieval and Reviewer Agent → SAS Script Optimize → End.

The Node Message setting is what hands the reviewer's findings to the optimizer: it passes the reviewer agent's last message into the optimizer as its "question".

### 3.3 Recipe settings and publish

1. On the right side, set **Number of state updates** to a higher value, such as 75.
2. Click the canvas, then select **Save**.
3. Select your recipe from the recipe table and click **Publish**. By default, the V1 version is deployed once it is published.
4. For later versions, click **Actions** and select **Deploy**.

The finished recipe looks like this:

<figure><img src="https://415930246-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7ZrVuiAUMyuYVvrK5KaB%2Fuploads%2FY1X1uxS7N4KpB665ba2N%2Fimage.png?alt=media&amp;token=b98b1960-4b45-4504-82ad-35aa88c615ab" alt=""><figcaption></figcaption></figure>

## Step 4: Trigger the Workflow via API

Open a terminal and run the command below to start the optimization workflow. Replace each placeholder in angle brackets, including the job ID, and make sure the payload stays valid JSON.

```bash
curl -X POST "<Webhook URL from the Webhook Tile>" \
-H "Content-Type: application/json" \
-H "x-api-token: <YOUR API TOKEN>" \
-d '{
    "files": [],
    "input_message": "<job_id>",
    "metadata": {}
}'
```

## Step 5: Monitor and Verify Results

1. Open **Recipes** from the left toolbar.
2. Click **Actions** on the **SAS Automation** recipe and select **Webhook History**. You'll see every webhook request with its input, response, status, tokens, and detailed traces.
3. Review the JSON output.
4. Open the GitLab repository to see the new commit with the optimized script.

This is the moment that shows the value: a job ID went in, and a reviewed, optimized, version-controlled script came out, with every step recorded in the trace.
