};
};
}
const TOOL_REGISTRY: ToolDefinition[] = [
{
type: 'function',
function: {
name: 'fetchMarketMetrics',
description: 'Retrieves real-time pricing and volume data for a specified asset ticker.',
parameters: {
type: 'object',
properties: {
ticker: { type: 'string', description: 'Stock or crypto ticker symbol (e.g., AAPL, BTC)' },
timeframe: { type: 'string', description: 'Data window: 1h, 1d, or 1w' }
},
required: ['ticker', 'timeframe']
}
}
},
{
type: 'function',
function: {
name: 'generateSummaryReport',
description: 'Compiles raw metrics into a structured executive summary.',
parameters: {
type: 'object',
properties: {
rawData: { type: 'string', description: 'JSON string containing market metrics' },
tone: { type: 'string', description: 'Output style: concise, detailed, or risk-focused' }
},
required: ['rawData']
}
}
}
];
### Step 2: Implement State Tracking and Loop Control
The agent must maintain execution context across iterations. A dedicated state object tracks the objective, completed actions, and iteration count. This decouples execution metadata from conversation history, preventing context window pollution.
```typescript
import OpenAI from 'openai';
const openai = new OpenAI();
interface AgentState {
objective: string;
iteration: number;
maxIterations: number;
executionLog: Array<{ tool: string; status: 'success' | 'error'; payload: string }>;
}
async function executeAgenticWorkflow(
userPrompt: string,
initialState: AgentState
): Promise<string> {
let state = { ...initialState };
let conversationHistory = [
{ role: 'user' as const, content: userPrompt }
];
while (state.iteration < state.maxIterations) {
state.iteration++;
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: conversationHistory,
tools: TOOL_REGISTRY,
tool_choice: 'auto'
});
const message = response.choices[0].message;
// Termination condition: model returns direct text without tool calls
if (!message.tool_calls || message.tool_calls.length === 0) {
return message.content || 'Task completed without explicit output.';
}
// Process tool invocations
for (const call of message.tool_calls) {
const toolName = call.function.name;
const args = JSON.parse(call.function.arguments);
let observation: string;
try {
if (toolName === 'fetchMarketMetrics') {
observation = JSON.stringify(await fetchExternalData(args.ticker, args.timeframe));
} else if (toolName === 'generateSummaryReport') {
observation = await compileReport(args.rawData, args.tone);
} else {
observation = 'Unknown tool invoked.';
}
state.executionLog.push({ tool: toolName, status: 'success', payload: observation });
} catch (err) {
observation = `Execution failed: ${(err as Error).message}`;
state.executionLog.push({ tool: toolName, status: 'error', payload: observation });
}
conversationHistory.push(
{ role: 'assistant' as const, content: null, tool_calls: [call] },
{ role: 'tool' as const, tool_call_id: call.id, content: observation }
);
}
}
return 'Maximum iteration limit reached. Partial results available in execution log.';
}
Architecture Decisions and Rationale
- Explicit State Object: Decouples execution metadata from conversation history. This prevents context window pollution and enables deterministic logging for debugging.
- Hard Iteration Cap:
maxIterations acts as a circuit breaker. It prevents infinite loops caused by ambiguous prompts or failing tools. Production systems typically cap between 3–5 iterations.
- Tool Choice Auto: Allows the model to decide whether to call tools or terminate. Forcing tool usage on every turn degrades performance and increases latency unnecessarily.
- Observation Injection: Results are appended as
tool role messages. This maintains API compatibility while preserving the reasoning chain for subsequent iterations. The model treats these as ground-truth feedback rather than speculative text.
- Idempotent Tool Design: Tools should be safe to call multiple times. If the model retries a query due to a transient network error, the backend must return consistent results without side effects.
Pitfall Guide
1. Unbounded Execution Loops
Explanation: The model repeatedly calls the same tool or cycles through tools without reaching a conclusion, often due to vague success criteria or ambiguous feedback.
Fix: Implement a strict maxIterations counter. Add a termination prompt that explicitly defines what constitutes task completion. Log iteration counts to identify loops that consistently hit the cap.
2. Context Window Bleed
Explanation: Retaining full conversation history across iterations causes token costs to scale exponentially and eventually hits model limits, truncating critical instructions.
Fix: Prune intermediate tool responses after successful execution. Use a sliding window or summarize completed steps before appending to the next iteration. Keep only the most recent 2–3 tool exchanges in active memory.
Explanation: Vague parameter descriptions lead to hallucinated arguments or type mismatches, causing tool execution failures and wasted API calls.
Fix: Enforce strict JSON schema validation. Include concrete examples in descriptions, constrain enums where applicable, and validate inputs against TypeScript interfaces before execution.
4. Synchronous UI Blocking
Explanation: Agentic workflows take 10–30 seconds. Blocking the frontend until completion degrades user experience and triggers timeout errors in load balancers or API gateways.
Fix: Stream intermediate states via WebSockets or Server-Sent Events. Render incremental updates (e.g., "Querying database...", "Analyzing results...") to maintain perceived responsiveness and prevent client-side timeouts.
5. Ignoring Cost Scaling
Explanation: Each loop iteration retransmits system prompts, tool definitions, and history. Costs can easily exceed 5x a standard call, especially when using premium models.
Fix: Implement token budgeting. Route simple queries to cheaper models (e.g., gpt-4o-mini) and reserve expensive models for complex orchestration. Cache repeated tool outputs and implement session-level cost caps.
6. State Desynchronization
Explanation: The model’s internal reasoning diverges from the actual system state, leading to contradictory actions or repeated queries. This occurs when tools mutate data without explicit feedback.
Fix: Design idempotent tools. Inject explicit state snapshots into the prompt when critical variables change. Validate tool outputs against expected schemas before feeding them back into the loop.
Explanation: The model generates syntactically valid but semantically incorrect arguments (e.g., passing a date string where a timestamp is expected, or using invalid enum values).
Fix: Add a pre-execution validation layer. If validation fails, inject a corrective observation back into the conversation loop with explicit error guidance. Retry once with corrected parameters before escalating.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Simple Q&A or single-step data retrieval | Static Prompt Pipeline | Deterministic, low latency, minimal token usage | Baseline (1x) |
| Multi-step data aggregation with conditional branching | Agentic Loop (3-4 iterations) | Model dynamically selects tools based on intermediate results | Moderate (3-5x) |
| Complex workflow automation with external API orchestration | Framework-Driven Agent (LangGraph/CrewAI) | Built-in state machines, retry policies, and parallel execution | High (5-10x) |
| Real-time user-facing interface with strict SLA | Hybrid Pipeline (Static + Fallback Agent) | Guarantees response time while allowing async background processing | Variable (cached vs live) |
Configuration Template
// agent.config.ts
export const AGENT_CONFIG = {
model: 'gpt-4o',
maxIterations: 4,
contextWindowLimit: 128000,
pruningStrategy: 'summarize_completed_steps',
streaming: true,
timeoutMs: 25000,
costBudgetPerSession: 0.50, // USD
tools: {
fetchMarketMetrics: {
endpoint: '/api/v1/metrics',
retryAttempts: 2,
timeoutMs: 5000
},
generateSummaryReport: {
endpoint: '/api/v1/reports',
retryAttempts: 1,
timeoutMs: 8000
}
}
};
// Type-safe environment validation
export function validateConfig(config: typeof AGENT_CONFIG) {
if (config.maxIterations < 1 || config.maxIterations > 10) {
throw new Error('maxIterations must be between 1 and 10');
}
if (config.costBudgetPerSession <= 0) {
throw new Error('costBudgetPerSession must be positive');
}
return config;
}
Quick Start Guide
- Initialize the SDK: Install
openai and configure your API key. Set up the AGENT_CONFIG object with iteration limits, timeout thresholds, and tool endpoints. Validate configuration on startup.
- Register Tools: Define your tool registry using strict JSON schemas. Implement the actual backend functions that handle external calls, database queries, or file operations. Ensure all tools are idempotent.
- Deploy the Loop Controller: Use the
executeAgenticWorkflow function as your core orchestrator. Pass the user prompt and initial state. Manage conversation history per session to prevent cross-user state leakage.
- Stream Results: Connect the orchestrator to a WebSocket or SSE endpoint. Push iteration updates to the client so users see progress instead of a blank loading screen. Handle partial results gracefully if the iteration cap is reached.
- Monitor & Iterate: Track token usage, iteration counts, and termination reasons. Adjust
maxIterations and pruning strategies based on production telemetry. Implement circuit breakers for downstream API failures.