Skip to main content

Building an Agent

Summary

An agent lets the model decide which action to take and then actually execute it, instead of only returning text. ADITO does not yet ship a dedicated agent framework — built-in agent logic is on the Roadmap — but a basic agent loop can already be built today in JDito by combining function calling with JDito's own process execution.

Prerequisites

How the loop works

An agent loop repeats three steps until the model has enough information to answer without calling another tool:

  1. Send the conversation, along with a tools definition, to the chat completions endpoint.
  2. If the response contains tool_calls, execute the corresponding JDito process for each call and collect its result.
  3. Append the tool result to the conversation and send the request again. Repeat until the response no longer contains tool_calls.
note

This loop follows the standard OpenAI tool-calling convention: an assistant message carrying tool_calls, followed by one tool message per call, each identified by tool_call_id. It is not demonstrated elsewhere in this documentation — the building blocks below show how to assemble it in JDito.

Building block 1: call the chat completions endpoint from JDito

Calling the ADITO-LLM endpoint from JDito uses the same net.createConfigForRestWebserviceCall() pattern as any other outgoing REST call, for example the one used to call the embedding model:

import { auth, net, util } from "@aditosoftware/jdito-types";

function callChatCompletions(pMessages, pTools)
{
let body = {
model: "adito-llm",
messages: pMessages,
tools: pTools,
tool_choice: "auto",
temperature: 0.15
};

let restConfig = net.createConfigForRestWebserviceCall()
.url("https://ai.adito.cloud/chat/completions")
.dataTypeAccept("application/json")
.dataTypeSend("application/json")
.dataTypeJDitoAccept(util.DATA_TEXT)
.dataTypeJDitoSend(util.DATA_TEXT)
.actionType(net.POST)
.requestEntity(JSON.stringify(body));

// Load the token from an approved secret source (alias or environment configuration),
// never hard-code it in the process.
let authConfig = auth.createConfigForOAuth2().token("myApiToken");

return net.callRestWebservice(restConfig, authConfig);
}

Building block 2: execute a tool as a JDito process

Each tool the model can call maps to one JDito process. process.execute() runs the process by name, passes the model's arguments in as local variables, and returns whatever the process returns via result.string() or result.object():

import { process } from "@aditosoftware/jdito-types";

function executeTool(pName, pArgs)
{
switch (pName)
{
case "get_contact_activities":
return JSON.parse(process.execute("GetContactActivities_process", pArgs));
default:
throw new Error("Unknown tool: " + pName);
}
}

Inside GetContactActivities_process, read the arguments with vars.get("$local.contact_name") and return the result with result.string(...) or result.object(...) — the same pattern used to build vector inputs in the vector search guide.

Building block 3: the loop

With both building blocks in place, the loop calls the model, executes any requested tools, feeds the results back, and repeats:

import { result, vars } from "@aditosoftware/jdito-types";

function runAgentLoop(pMessages, pTools)
{
for (let i = 0; i < 5; i++) // hard cap so a misbehaving loop cannot run forever
{
let response = JSON.parse(callChatCompletions(pMessages, pTools));
let choice = response.choices[0].message;

if (!choice.tool_calls || choice.tool_calls.length === 0)
return choice.content;

pMessages.push({ role: "assistant", tool_calls: choice.tool_calls, content: choice.content });

for (let toolCall of choice.tool_calls)
{
let toolResult = executeTool(toolCall.function.name, JSON.parse(toolCall.function.arguments));
pMessages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(toolResult) });
}
}

throw new Error("Agent loop did not resolve within the iteration limit.");
}

// Entry point: an executable process that starts the loop.
var messages = [
{ role: "system", content: "You are a CRM assistant. Use the available tools to look up data." },
{ role: "user", content: vars.get("$local.userQuestion") }
];

result.string(runAgentLoop(messages, tools));

tools is the same array structure used for function calling and tool use — one entry per JDito process the agent is allowed to call.

Guardrails

warning

Treat model output as a suggestion or draft, not an authorization to act. Per AI Compliance, record changes and other side-effecting actions require explicit input from a user or a configured, auditable process — do not let a tool call write or change data without that in place.


See also: Function calling and tool use | AI Compliance | Roadmap | JDito