Brief Introduction to the Occam SDK

Occam is currently in invite only access as we scale our system. To start using Occam for developing automation applications, request an invite, or get in touch at info@occam.ai.

Details of our architecture and product experience can be found at our website and our blog post.

Overview

Occam offers a comprehensive SDK for generating, running, modifying and supervising automations.

Our automation engine relies on the following key components

  • Resources catalogue: This is the set of APIs, models, tools and datasets that Occam natively supports or that you make available to the Occam automation engine.

  • Preferences: Guardrails, rules and limits the developer sets that the Occam engine will operate within (time limits, token limits etc)

  • Reasoning Engine: The component of our system responsible for coming up with automation plans to fulfil a specified goal. This component leverages a multi-agent planning architecture, that combines the resource catalogue, preferences and a specified business objective, to generate automation proposals that the you can modify, request modifications for or publish.

  • Run Manager: A distributed orchestration system used to run and monitor automation runs. It handles horizontal scalability, fault tolerance, and status updates. It also interfaces with our reasoning engine to enable runtime modifications to automation workflows.

  • Data Access and Manipulation layer: For accessing, querying and combining datasets. This supports a variety of dataset formats and sources, including APIs, filesystems, databases and automation run results, that we represent as datasets in their own right. The system was designed to enable writing single SQL queries that simultaneously speaks to these different formats. For example, you can write a query that reasons over data from an API in combination with one from a database.

  • Automation run results: Each step executed as part of a running automation plan is saved as a dataset (if you have designated that) and can be consumed by other steps in the run. Our system also produces run reports that allow you to inspect the outputs of intermediate steps, allows you to run your own analysis on the data or export it, and possibly guide run-time interventions.

The following sections describe the SDK capabilities Occam offers for developers to prototype, modify, run and supervise automations.

Create goal

Creating an automation goal is fairly simple. All you need to do is specify the name of the goal and a list of messages describing the purpose of your automation. The goal is what will be used to drive the automation engine.

from occam_sdk import OccamClient

occam_client = OccamClient(api_key='sk-occam-xxx')

goal_payload = {
     "name": "Search and Scrape",
     "chat_messages": [
          {"content": "Search for and categorize cashflow management products by service industry.", "role": "user"}
     ],
}

goal = occam_client.automations.create_goal(goal_payload)

Create automation

Once the goal is created, you can task our automation engine with creating a distributed automation workflow that can achieve your goal. The automation is a Direct Acyclic Graph that draws on all resources in your catalogue, whether datasets or tools, to sequence a set of steps to solve a problem.

Your resource catalogue includes Occam specialised tools, any files or databases or product integrations that you've connected to from the list we support (currently we support all the major enterprise product integrations covering finance, operations, sales, marketing and product work needs)

To create an automation, simply run

creation_status = occam_client.automations.create_automation(goal_id=goal.id)

This triggers an automation generation process that you can poll the status for while it's running.

automation_id = creation_status.id
status = occam_client.automations.get_status(automation_id)

Which returns an enum for the different generation statuses of an automation

class PlanGenerationStatus(Enum):

    # transient states
    GENERATING = "Generating"
    VALIDATING = "Validating"
    MODIFYING = "Modifying"
    TESTING = "Testing"

    # stable states
    READY = "Ready"
    FAILING = "Failing"
    WARNING = "Warning"
    UNTESTED = "Untested"

Once creation is completed, you can fetch the full automation payload using the automation graph endpoint.

automation_graph = occam_client.automations.get_graph(automation_id)

automation_graph will be a directed acyclic graph composed of nodes and field mappings.

  • Nodes: These can be runnable tools or datasets. The detail endpoint returns a number of node properties, including its node name, run parameters, parameter variable types, warnings and errors.

  • Field mappings: These represent field level edges between nodes. Our engine takes care of ensuring that connected fields are compatible or convertible to one another based on tightly specified input/output schemas, as well as field adaptation logic.

nodes = automation_graph.nodes()
field_mappings = automation_graph.field_mappings()

Below is an example of what automation_graph looks like is shown in the simple example below, that searches the web for some terms, scrapes URLs produced by the search tool, and then counts the number of times a set of search phrases appear in the web-browsed content.


{
    "nodes": [
        {
            "type": PlanElementType.DATASET,
            "node_id": 0,
            "name": "search_terms_dataset",
            "role_description": "Search terms dataset",
            "node_status": {"overall": "Ready"},
            "plan_id": "plan_57f4c1dcfb4a475197877b6d653f8c80"
        },
        {
            "type": PlanElementType.TOOL,
            "node_id": 1,
            "name": "SearchAgent",
            "role_description": "Search tool to find URLs given search terms",
            "node_status": {"overall": "Ready"},
            "plan_id": "plan_57f4c1dcfb4a475197877b6d653f8c80"
        },
        {
            "type": PlanElementType.TOOL,
            "node_id": 2,
            "name": "ScraperAgent",
            "role_description": "Scraper tool to scrape URLs found by search",
            "node_status": {"overall": "Ready"},
            "plan_id": "plan_57f4c1dcfb4a475197877b6d653f8c80"
        },
        {
            "type": PlanElementType.TOOL,
            "node_id": 3,
            "name": "DynamicQuerierAgent",
            "role_description": "Grouping and aggregating products by category.",
            "node_status": {"overall": "Ready"},
            "plan_id": "plan_57f4c1dcfb4a475197877b6d653f8c80"
        },
    ],
    "field_mappings": [
        {"src_type": PlanElementType.DATASET, "src": 0, "target": 1, "src_output_field": "query", "target_input_field": "query", "plan_id": plan_id},
        {"src_type": PlanElementType.TOOL, "src": 1, "target": 2, "src_output_field": "urls", "target_input_field": "urls", "plan_id": plan_id},
        {"src_type": PlanElementType.TOOL, "src": 2, "target": 3, "src_output_field": "pages", "target_input_field": "pages", "plan_id": plan_id},
    ],
    "plan_status": PlanGenerationStatus.READY,
    "modifier_type": PlanModifierType.AGENT,
}

Modifying automations

Automation plans can be modified in one of two ways. You can send your own modifications or you can ask the agents to perform modifications to an automation proposal.

Submitting modifications

You can submit modifications to automation proposals. These will get validated and applied to the automation. If a modification of yours maps outputs from one tool to an incompatible input of another tool, the reasoning engine will try to add an adapter that will transform that output. If it fails, it will still save your modifications but flag them as one with issues.

new_node_id = automation.nodes.get_max_node_id() + 1
validate_and_modify_payload = {
    "modifications_batch": [
        {
            "type": "AddNode",
            "name": "HumanInTheLoop",
            "params": {"email": "sdk@occam.ai"},
            "node_id": new_node_id
        },
        {
            "type": "AddFieldMapping",
            "src": 1,
            "src_output_field": "text",
            "target": new_node_id,
            "target_input_field": "query"
        },
        {
            "type": "AddFieldMapping",
            "src": new_node_id,
            "src_output_field": "response",
            "target": 2,
            "target_input_field": "choice"
        }
    ]
}

validation_and_modification_status = occam_client.automations.validate_and_modify(
    automation.id,
    validate_and_modify_payload
)

Here you inserted a human-in-the-loop between nodes 1 and 2, the human input will be provided to node 2, along with any other inputs node 2 was receiving.

Note: you can perform multiple modification request iterations, as needed.

Requesting agent modifications

Modification requests are expected to be a text message and can optionally also include a list of node ids that you would like to refer to when requesting the modifications.

modification_request = {
     "user_message": "I added a human in the loop to review scraping results. please review and modify if needed.",
    "highlights": [1, 2] # highlighted nodes
}

occam_client.automations.request_modifications(
    automation_id=automation.id,
    modification_request=modification_request
)

We also support combining both, sending your own modifications to the plan for saving and validation, and requesting the agents to then perform further modifications, just as you would while working in a team, you make your own additions and ask for others' feedback.

new_node_id = automation.nodes.get_max_node_id() + 1
modification_request = {
    "user_message": "I added a human in the loop to review scraping results. please review and modify if needed."
    "highlights": [1, 2] # highlighted nodes
    "modifications_batch": [
        {
            "type": "AddNode",
            "name": "HumanInTheLoop",
            "params": {"email": "sdk@occam.ai"},
            "node_id": new_node_id
        },
        {
            "type": "AddFieldMapping",
            "src": 1,
            "src_output_field": "text",
            "target": new_node_id,
            "target_input_field": "query"
        },
        {
            "type": "AddFieldMapping",
            "src": new_node_id,
            "src_output_field": "response",
            "target": 2,
            "target_input_field": "choice"
        }
    ]
}

Publishing automations

Once you're happy with the automation proposal, you can publish it to your catalog, so you can request automation runs when you so choose.

publication_status = occam_client.automations.publish(automation.id)

Running automations

Once your automation is ready, you can trigger a run. Automation runs are async. You can poll for status while they're executing, pause automation runs, request modifications while pausing, resume and abort automations.

To trigger an automation run:

run = occam_client.automations.run(automation.id)

Polling for run status

run_status = occam_client.automations.run_status(run.id)

which is an enum with the following values

class PlanRunStatus(str, Enum):
    STARTING = "Starting"
    RUNNING = "Running"
    PAUSING = "Pausing"
    PAUSED = "Paused"
    MODIFYING = "Modifying"

    # stable end states
    TERMINATED = "Terminated"
    SUCCEEDED = "Succeeded"
    FAILED = "Failed"

You can also get granular details about the run progress, status of individual nodes etc

run_detail = occam_client.automations.run_detail(run.id)

which follows the same structure as automation detail, but with extra information like run_status and node_runs on each node object.

Pause, resume and modify running automations

You can also pause running automations, this is an important feature in long running automations, where you might want to intervene with the run due to some unforeseen event, or to adjust the automation half-way through.

occam_client.automations.pause_run(run.id)

The status shown above can be polled to see if the plan is Pausing or Paused. While pausing or paused, we support runtime automation modification requests that you can ask our reasoning agents to make. At present, we don't support users submitting their own modifications to a running plan, only ask agents to perform modifications.

modification_request = {
    "user_message": "...",
    "highlights": [],
}

occam_client.automations.request_run_modifications(
    run_id=run.id,
    modification_request=modification_request
)

The automation can be resumed with or without a modification request.

occam_client.automations.resume_run(run.id)

Last updated