4.7 KiB
The Agentic Workflow Loop
Gadget Code is an Agentic Integrated Development Environment (AIDE) built around a bespoke Agentic Workflow Loop (AWL). The AWL resides and executes within the Gadget Drone.
The User can edit project files by hand. The User can also ask Gadget (the Gadget Code AI Programming Agent) to do work for them in the project. The User sends a natural language prompt to the Gadget programming agent for processing. The prompt is packaged as a Work Order and sent to an available Gadget Drone for processing. This is what starts the AWL.
The loop starts when the User submits a prompt for processing, and ends when an AI API call responds with no further tool function calls. If a response contains tool calls, the loop calls the tools, inserts tool role response messages in the context (messages array), and remains in the loop to submit the context back to the model for additional thinking, response(s), and tool call response(s).
Chat Session Turns and the AWL
From when a prompt work order is received until the final AI API response received is referred to as a Turn in the Chat Session, and represents one execution of the AWL.
Any amount of work can occur within a single execution of the AWL. There are no imposed limits on loop counts. The agent can keep working for as long as necessary in one Turn.
Prompt Work Orders
Each gadget-drone registered by the User maintains a Socket.IO connection to gadget-code's web services. The User can send a prompt to the Gadget Drone by sending a Work Order to the drone's Socket.IO connection.
The Work Order is a JSON object that contains information about the project, AI service provider and model, and also the User's prompt.
/*
* Chat session context is made of chat messages with a timestamp and role. The
* role is "system", "user", "assistant", or "tool". The assistant can have
* reasoning or "thinking" content. When thinking content is being included, we
* merge the thinking + response by enclosing the thinking content in a
* <thinking> element in the content.
*
* ```
* <thinking>I need to research the project...</thinking>
* I found the bug!
* ```
*/
interface IChatMessage {
createdAt: Date;
role: string;
content: string;
}
interface IWorkOrder {
project: {
_id: string;
name: string;
slug: string;
gitUrl: string;
};
session: {
name: string;
mode: string;
};
provider: {
baseUrl: string;
apiKey: string;
modelId: string;
params: {
temperature: number;
topP: number;
topK: number;
};
};
context: IChatMessage[];
prompt: string;
}
The Workflow Loop
When a Work Order job is received a Gadget Drone, it performs a series of steps in preparation to work on the prompt contained in the work order.
- Check if the workspace directory currently has the project cloned. If not, clone the project from git into the workspace directory.
- Instantiate the correct AI API client object configured with the credentials provided in the work order.
/*
* This is just pseudocode that shows the intent. It does not account for every
* aspect of the loop.
*/
async function workflowLoop(
session: IChatSession,
provider: IAiProvider,
llm: string,
userPrompt: string,
): Promise<void> {
const NOW = new Date();
/*
* Create the ChatTurn in the database at the *start* of the turn, and update
* it with information as the turn executes. This helps ensure we don't lose
* work conducted in a turn if an error happens. A turn should be able to be
* resumed whenever possible.
*/
const turn = new ChatTurn();
turn.createdAt = NOW;
turn.status = ChatTurnStatus.Processing;
turn.user = session.user;
turn.provider = provider;
turn.llm = llm;
turn.mode = session.mode;
turn.prompt = userPrompt;
turn.stats = {
toolCallCount: 0,
inputTokens: 0,
thinkintTokenCount: 0,
responseTokens: 0,
durationMs: 0,
durationLabel: "pending",
};
await turn.save();
/* emit Socket.IO message with turn
// this turn's context (system, history, prompt, work)
const messages = [];
const systemPrompt = buildSystemPrompt(session);
messages.push({ role: "system", content: systemPrompt });
// recall full session history into messages array
buildSessionContext(session, messages);
// push the User's latest prompt to the context
messages.push({ role: "user", content: userPrompt });
let keepProcessing = true;
do {
const response = await aiApiCall(messages);
keeProcessing = response.tool_calls.length > 0;
for (const tool_call of response.tool_calls) {
const response = await callTool(tool_call.name, tool_call.args);
messages.push({ role: "tool", content: response });
}
} while (keepProcessing);
}