|$ curl https://forge-ai.dev/api/markdown?path=docs/ai/agents
$cat docs/ai-engineering-—-tool-use-&-agents.md
updated Recently·40 min read·published

AI Engineering — Tool Use & Agents

AI EngineeringAgentsIntermediate to Advanced
Introduction

An AI agent is a system that uses a language model to reason about a goal, plan actions, execute tools, observe results, and iterate until completion. Unlike simple chat completions, agents maintain state, use external tools, and make decisions across multiple steps.

Modern agent architectures combine LLMs with function calling, memory systems, planning algorithms, and orchestration frameworks to tackle complex, multi-step tasks autonomously.

Agent Architecture

Every agent system shares a core architecture: a reasoning loop that repeatedly decides what to do, executes tools, and incorporates results. The loop continues until a termination condition is met.

The Agent Loop

agent-loop.ts
TypeScript
1// Core agent reasoning loop
2class Agent {
3 private messages: Message[] = [];
4 private maxSteps = 25;
5 private step = 0;
6
7 constructor(
8 private model: string,
9 private tools: Tool[],
10 private systemPrompt: string
11 ) {
12 this.messages.push({
13 role: "system",
14 content: systemPrompt
15 });
16 }
17
18 async run(userInput: string): Promise<string> {
19 this.messages.push({ role: "user", content: userInput });
20
21 while (this.step < this.maxSteps) {
22 const response = await this.think();
23 const action = this.parseAction(response);
24
25 if (action.type === "final") {
26 return action.content;
27 }
28
29 const result = await this.act(action);
30 this.messages.push({
31 role: "tool",
32 tool_call_id: action.id,
33 content: JSON.stringify(result)
34 });
35
36 this.step++;
37 }
38
39 return "Max steps reached without completion.";
40 }
41
42 private async think() {
43 return await openai.chat.completions.create({
44 model: this.model,
45 messages: this.messages,
46 tools: this.tools,
47 tool_choice: "auto"
48 });
49 }
50
51 private parseAction(response: any) {
52 if (!response.choices[0].message.tool_calls) {
53 return { type: "final", content: response.choices[0].message.content };
54 }
55 return { type: "tool", ...response.choices[0].message.tool_calls[0] };
56 }
57
58 private async act(action: any) {
59 const args = JSON.parse(action.function.arguments);
60 return await executeTool(action.function.name, args);
61 }
62}

best practice

Always set a maximum step limit in your agent loop to prevent infinite loops and runaway costs. A typical limit is 10-25 steps. Monitor step count and log each iteration for debugging and observability.
Memory Systems

Memory in agents operates at multiple timescales. Short-term memory is the conversation context window. Long-term memory uses external storage (vector databases, key-value stores) for information that persists across sessions.

Memory TypeStorageRetentionUse Case
Short-termContext windowSingle sessionConversation flow
WorkingScratchpadCurrent taskIntermediate state
Long-termVector DBCross-sessionUser preferences, facts
EpisodicLog / DBAudit trailPast actions, outcomes
ProceduralPrompts / CodePermanentHow-to knowledge
memory-agent.ts
TypeScript
1// Long-term memory with vector store
2interface MemoryStore {
3 add(text: string, metadata?: Record<string, any>): Promise<void>;
4 search(query: string, topK?: number): Promise<MemoryItem[]>;
5 forget(ids: string[]): Promise<void>;
6}
7
8class VectorMemory implements MemoryStore {
9 constructor(
10 private embedder: (text: string) => Promise<number[]>,
11 private storage: VectorDatabase
12 ) {}
13
14 async add(text: string, metadata?: Record<string, any>) {
15 const embedding = await this.embedder(text);
16 await this.storage.upsert({
17 id: crypto.randomUUID(),
18 values: embedding,
19 metadata: { text, ...metadata, timestamp: Date.now() }
20 });
21 }
22
23 async search(query: string, topK = 5) {
24 const embedding = await this.embedder(query);
25 const results = await this.storage.query({
26 topK,
27 vector: embedding,
28 includeMetadata: true
29 });
30 return results.map(r => ({
31 text: r.metadata!.text,
32 score: r.score,
33 id: r.id
34 }));
35 }
36
37 async forget(ids: string[]) {
38 await this.storage.deleteMany(ids);
39 }
40}
41
42// Memory-augmented agent
43class MemoryAgent extends Agent {
44 constructor(
45 model: string,
46 tools: Tool[],
47 private memory: MemoryStore
48 ) {
49 super(model, tools, buildMemoryPrompt());
50 }
51
52 async run(input: string): Promise<string> {
53 const relevant = await this.memory.search(input);
54 if (relevant.length > 0) {
55 this.messages.push({
56 role: "system",
57 content: `Relevant memories: ${JSON.stringify(relevant)}`
58 });
59 }
60 const result = await super.run(input);
61 await this.memory.add(input, { type: "user_query" });
62 await this.memory.add(result, { type: "agent_response" });
63 return result;
64 }
65}
Planning

Advanced agents decompose complex tasks into subtasks before execution. Planning strategies include ReAct (Reasoning + Acting), Plan-and-Solve, Tree of Thoughts, and Reflexion — each with different trade-offs between depth, cost, and reliability.

planning.ts
TypeScript
1// ReAct: Reasoning + Acting loop
2// The model outputs "Thought:" then "Action:" then "Observation:"
3// This structured thinking improves complex reasoning
4
5const reactPrompt = `You are a reasoning agent. For each step:
61. Thought: Reason about what to do next
72. Action: Select a tool and specify arguments
83. After receiving the observation, repeat
9
10When you have enough information, output:
11Final Answer: <your answer>
12
13Available tools:
14${tools.map(t => t.function.name).join(", ")}
15
16Always show your reasoning in Thought steps.`;
17
18// Plan-and-Solve decomposition
19async function planAndSolve(task: string, agent: Agent) {
20 // Phase 1: Decompose
21 const plan = await agent.ask(
22 `Decompose this task into 3-5 subtasks: "${task}"`
23 );
24
25 // Phase 2: Execute each subtask
26 const results = [];
27 for (const subtask of plan.subtasks) {
28 const result = await agent.run(subtask);
29 results.push(result);
30 }
31
32 // Phase 3: Synthesize
33 const final = await agent.ask(
34 `Synthesize these results into a final answer: ${results}`
35 );
36
37 return final;
38}

info

ReAct prompting dramatically improves agent reasoning on complex tasks. The structured "Thought → Action → Observation" pattern helps the model avoid hallucination by explicitly reasoning before each action. Add this to your system prompt for better results.
Agent Types

Different tasks call for different agent architectures. Simple tasks work with a single agent loop, while complex workflows benefit from specialized patterns.

TypeArchitectureBest For
Simple AgentSingle LLM + tools loopQ&A, simple tasks
ReAct AgentThought → Action → ObservationComplex reasoning
Plan-and-ExecutePlanner → Executor → SynthesizerMulti-step projects
Reflection AgentGenerate → Critique → RefineContent creation, code
Multi-AgentMultiple agents with rolesComplex workflows
Human-in-the-LoopAgent + human approval stepSensitive operations
🔥

pro tip

Start with the simplest agent architecture that solves your problem. Over-engineering with complex frameworks early adds maintenance burden and debugging difficulty. Graduate to advanced patterns only when you hit concrete limitations.
$Blueprint — Engineering Documentation·Section ID: AI-AG-01·Revision: 1.0