Building a Minimalist AI Agent Framework: Theory, Architecture, and Code Walkthrough
This article explains the fundamentals of AI agents, compares major frameworks, introduces the ReAct, Plan‑and‑Execute, and Reflection paradigms, and provides a step‑by‑step Python implementation of a lightweight agent loop with LLM calls, tool execution, and context engineering, complete with usage examples and references.
AI agents are software systems that use large language models (LLMs) to achieve goals on behalf of users, combining reasoning, planning, and autonomous execution. The most influential paradigms are ReAct (reasoning + acting), Plan‑and‑Execute (plan first, then act), and Reflection (verbal self‑feedback).
1. Theory of AI Agent Frameworks
1.1 AI Agent Basics
According to Google Cloud, an AI agent performs reasoning, planning, and memory tasks with a degree of autonomy, allowing it to learn, adapt, and make decisions.
1.2 ReAct Mode
Proposed by Yao et al. (2022) in "ReAct: Synergizing Reasoning and Acting in Language Models," ReAct integrates Chain‑of‑Thought reasoning with tool interaction, addressing the lack of external feedback in pure CoT.
Reasoning : LLM analyzes the current task and generates internal thoughts (CoT).
Acting : Executes actions such as function calls, shell commands, or code execution.
Observation : Observes results and feeds them back into the next reasoning step.
1.3 Plan‑and‑Execute Mode
Based on the "Plan‑and‑Solve Prompting" paper (2023) and the BabyAGI project, this mode first creates a complete multi‑step plan and then executes it step by step, which is suitable for long‑running, well‑structured tasks but less dynamic.
1.4 Reflection Mode
Key papers include "Reflexion: Language Agents with Verbal Reinforcement Learning" and "Self‑Refine: Iterative Refinement with Self‑Feedback," which introduce verbal self‑feedback loops and iterative improvement without weight updates. These ideas extend ReAct with richer self‑correction mechanisms.
1.5 Comparison of Popular Frameworks
LangChain – mature, extensive toolchain, multi‑LLM support.
LlamaIndex – focuses on data indexing and retrieval (RAG).
AutoGPT/AutoGen – multi‑agent collaboration.
CrewAI – role‑based team simulation.
LangGraph – state‑graph workflow control.
Semantic Kernel – lightweight, Azure‑integrated.
Selection advice: use LangChain for quick prototypes, LlamaIndex for RAG, AutoGen or CrewAI for multi‑agent collaboration, LangGraph for complex workflows, and Semantic Kernel for .NET environments.
2. Practical Implementation
2.1 Core Architecture
The agent consists of three essential parts:
LLM Call : Standardized API wrapper (e.g., OpenAI SDK) that handles model invocation.
Tools Call : Executes external actions (shell, file I/O, Python code) via OpenAI‑style function calling.
Context Engineering : Manages system prompts, user messages, and tool results within a message list that serves as the agent’s state.
In practice, the DeepSeek deepseek-chat model is used because it supports tool calls and is compatible with the OpenAI SDK.
2.2 Agent Loop
# ============================================================
# Agent Loop — core
# ============================================================
MAX_TURNS = 20
def agent_loop(user_message: str, messages: list, client: OpenAI) -> str:
"""Agent Loop: while‑loop driving LLM reasoning and tool calls.
Steps:
1. Append user message to messages.
2. Call LLM with current messages and tool schemas.
3. If tool_calls are present, execute each tool, append results, and continue.
4. If no tool_calls, return the assistant's final response.
5. Stop after MAX_TURNS iterations.
"""
messages.append({"role": "user", "content": user_message})
tool_schemas = [t["schema"] for t in TOOLS.values()]
for turn in range(1, MAX_TURNS + 1):
# --- LLM Call ---
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
tools=tool_schemas,
)
choice = response.choices[0]
assistant_msg = choice.message
messages.append(assistant_msg.model_dump())
# Termination condition: no tool_calls
if not assistant_msg.tool_calls:
return assistant_msg.content or ""
# Execute each tool_call
for tool_call in assistant_msg.tool_calls:
name = tool_call.function.name
raw_args = tool_call.function.arguments
try:
args = json.loads(raw_args)
except json.JSONDecodeError:
args = {}
tool_entry = TOOLS.get(name)
if tool_entry is None:
result = f"[error] unknown tool: {name}"
else:
result = tool_entry["function"](**args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
return "[agent] reached maximum turns, stopping."2.3 Tools Implementation and Registration
# ============================================================
# Tools implementation — 4 functions
# ============================================================
def shell_exec(command: str) -> str:
"""Execute a shell command and return stdout + stderr."""
try:
result = subprocess.run(
command, shell=True, capture_output=True, text=True, timeout=30
)
output = result.stdout
if result.stderr:
output += "
[stderr]
" + result.stderr
if result.returncode != 0:
output += f"
[exit code: {result.returncode}]"
return output.strip() or "(no output)"
except subprocess.TimeoutExpired:
return "[error] command timed out after 30s"
except Exception as e:
return f"[error] {e}"
def file_read(path: str) -> str:
"""Read file content."""
try:
with open(path, "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
return f"[error] {e}"
def file_write(path: str, content: str) -> str:
"""Write content to a file, creating parent directories if needed."""
try:
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return f"OK — wrote {len(content)} chars to {path}"
except Exception as e:
return f"[error] {e}"
def python_exec(code: str) -> str:
"""Execute Python code in a subprocess and return its output."""
try:
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False, encoding="utf-8") as tmp:
tmp.write(code)
tmp_path = tmp.name
result = subprocess.run([sys.executable, tmp_path], capture_output=True, text=True, timeout=30)
output = result.stdout
if result.stderr:
output += "
[stderr]
" + result.stderr
return output.strip() or "(no output)"
except subprocess.TimeoutExpired:
return "[error] execution timed out after 30s"
except Exception as e:
return f"[error] {e}"
finally:
try:
os.unlink(tmp_path)
except OSError:
pass
# ============================================================
# Tools registration — name → (function, OpenAI schema)
# ============================================================
TOOLS = {
"shell_exec": {
"function": shell_exec,
"schema": {
"type": "function",
"function": {
"name": "shell_exec",
"description": "Execute a shell command and return its output.",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string", "description": "The shell command to execute."}},
"required": ["command"]
}
}
}
},
"file_read": {
"function": file_read,
"schema": {
"type": "function",
"function": {
"name": "file_read",
"description": "Read the contents of a file at the given path.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string", "description": "Absolute or relative file path."}},
"required": ["path"]
}
}
}
},
"file_write": {
"function": file_write,
"schema": {
"type": "function",
"function": {
"name": "file_write",
"description": "Write content to a file (creates parent directories if needed).",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Absolute or relative file path."},
"content": {"type": "string", "description": "Content to write."}
},
"required": ["path", "content"]
}
}
}
},
"python_exec": {
"function": python_exec,
"schema": {
"type": "function",
"function": {
"name": "python_exec",
"description": "Execute Python code in a subprocess and return its output.",
"parameters": {
"type": "object",
"properties": {"code": {"type": "string", "description": "Python source code to execute."}},
"required": ["code"]
}
}
}
}
}2.4 System Prompt
SYSTEM_PROMPT = """You are a helpful AI assistant with access to the following tools:
1. shell_exec — run shell commands
2. file_read — read file contents
3. file_write — write content to a file
4. python_exec — execute Python code
Think step by step. Use tools when needed. When the task is complete, respond directly without calling any tool."""2.5 CLI Interface
def main():
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
print("Error: please set DEEPSEEK_API_KEY environment variable.")
sys.exit(1)
client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
print("Agent ready. Type your message (or 'exit' to quit, 'clear' to reset).
")
while True:
try:
user_input = input("You> ").strip()
except (EOFError, KeyboardInterrupt):
print("
Bye.")
break
if not user_input:
continue
if user_input.lower() == "exit":
print("Bye.")
break
if user_input.lower() == "clear":
messages.clear()
messages.append({"role": "system", "content": SYSTEM_PROMPT})
print("(context cleared)
")
continue
reply = agent_loop(user_input, messages, client)
print(f"
Agent> {reply}
")2.6 Demonstration
The demo shows the agent listing directory contents, counting lines of code, and performing simple file operations, illustrating how the loop continuously calls tools, updates context, and produces final answers.
3. Conclusions and Outlook
The minimalist framework demonstrates that the essential components of an AI agent are LLM calls, tool calls, and context engineering. While the implementation lacks advanced features such as streaming, robust error handling, and sophisticated tool registries, it provides a clear, educational baseline for understanding and extending AI agents.
Future work should focus on improving robustness, adding security sandboxes for tool execution, supporting asynchronous tool calls, and integrating richer context management (long‑term memory, retrieval‑augmented generation, and domain‑specific skills).
References
Google Cloud definition: https://cloud.google.com/discover/what-are-ai-agents
ReAct paper: https://arxiv.org/abs/2210.03629
Plan‑and‑Solve Prompting: https://arxiv.org/abs/2305.04091
BabyAGI project: https://github.com/yoheinakajima/babyagi
Plan‑and‑Execute blog: https://blog.langchain.com/plan-and-execute-agents/
Reflexion paper: https://arxiv.org/abs/2303.11366
Self‑Refine paper: https://arxiv.org/abs/2303.17651
CRITIC paper: https://arxiv.org/abs/2305.11738
CodeBuddy Agent SDK: https://www.codebuddy.cn/docs/cli/sdk
WorkBuddy application: https://www.codebuddy.cn/work/
Context Engineering for AI Agents (Manus blog): https://manus.im/zh-cn/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus
Executable Code Actions paper: https://arxiv.org/abs/2402.01030
Anthropic MCP code execution article: https://www.anthropic.com/engineering/code-execution-with-mcp
OpenClaw documentation: https://docs.openclaw.ai/reference/AGENTS.default
Shunyu Yao article on Context difficulty: https://mp.weixin.qq.com/s?__biz=MzkwODU2OTQyNQ==∣=2247497329&idx=1&sn=59de1b66fa0e9da3bc9a2e371eeab4ae
Pi Agent core description: https://lucumr.pocoo.org/2026/1/31/pi/
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
