From Job Description to Colleague: Building Your First AI Agent in 100 Lines
Writing Your First AI Agent: When a Skill Gains Memory, Role, and Initiative
Frontend AI Skill System · Part 6 (Agent Chapter)
In the previous chapter, we spent 15 minutes writing a daily report generator Skill. 30 lines of Markdown, and the AI no longer suffered amnesia every day.
But after using it for a week, I discovered a problem: The Skill wouldn't take initiative.
If I didn't say "write the daily report," it stayed silent. If I didn't say "review the code," it played dead. It was like an intern who only follows orders—you push, it moves.
What I wanted was a colleague: someone who remembers context on their own, decides the next step themselves, and proactively tells me the result when they're done.
That is an Agent. Today, we build one from scratch.
1. First, Clarify: What's the Difference Between an Agent and a Skill?
After the last chapter was published, the most common question in the comments was: "Aren't Skill and Agent just different names for the same thing?"
No. The difference is fundamental.
The One-Sentence Version
A Skill is a job description. An Agent is a living person.
The job description is posted at the workstation; whoever comes follows it. It doesn't think proactively, doesn't remember what happened yesterday, and certainly won't go coordinate with the neighboring department when it discovers a problem.
An Agent will.
Expanded
| Dimension | Skill | Agent |
|---|---|---|
| Essence | A Markdown instruction | A runnable program |
| Trigger | Passive—acts only when you speak | Proactive—decides when to act on its own |
| Memory | None. Each conversation starts from zero | Has it. Short-term working memory + long-term persistence |
| Tools | Describes "what should be done," AI decides freely | Code-level binding, deterministic execution |
| Decision-making | Linear process, no backtracking | Observe → Think → Act → Observe again, looping |
| Collaboration | Independent, unaware of each other | Handoff to other Agents, teamwork |
| Deployment | Just put it in the project directory | Requires Python/Node runtime |
| Cost | Zero (plain text) | API call fees / local compute |
When to Use Which?
My current rule of thumb is simple:
If the task requires "do one step, check the result, then decide the next step"—use an Agent. If the task is "input → fixed process → output"—use a Skill.
A few real examples:
| Scenario | Choice | Why |
|---|---|---|
| Unify code style | Skill | Rules are fixed, no decisions needed |
| Generate daily report | Skill | Fixed process, one execution |
| Auto-fix bugs | Agent | Must observe error → locate cause → fix → verify → if not fixed, try again |
| Multi-file refactoring | Agent | Must plan order, coordinate across files, check one after changing another |
| Customer service Q&A | Agent | Must remember context, search knowledge base, decide if escalation is needed |
| Code review | Skill + Agent | Skill defines review rules, Agent executes multi-round review |
They are not substitutes; they are complementary. Skills define "how to do it," Agents decide "when to do it, who to find, and what to do after it's done."
In my current system, 31 Skills define the standards, and 3 Agents handle orchestration and execution. Skills are the legal code; Agents are the judge.
2. Project Directory Structure
Simple Agent: 5 Files Get It Done
my-first-agent/
├── agent.py # Agent definition (role + instructions + tool bindings)
├── tools.py # Tool functions (the Agent's hands and feet)
├── main.py # Runtime entry point
├── requirements.txt # Dependency: openai-agents
└── .env # API Key (don't commit to Git)
That's it. Don't start by creating 20 directories.
Complex Agent: 5-Layer Architecture
When your Agents grow from 1 to 3 or 5, the directory needs layering:
my-agent-system/
│
├── agents/ # 🧠 Role layer: who does what
│ ├── orchestrator.py # Orchestrator—decides task allocation
│ ├── researcher.py # Researcher—searches and organizes information
│ ├── coder.py # Developer—writes code, fixes bugs
│ └── reviewer.py # Reviewer—quality gate
│
├── tools/ # 🔧 Capability layer: what can be done
│ ├── web_search.py # Web search
│ ├── file_ops.py # File read/write
│ ├── code_exec.py # Code execution (sandbox)
│ └── git_ops.py # Git operations
│
├── memory/ # 💾 Memory layer: what is remembered
│ ├── short_term.py # Current conversation context
│ ├── long_term.py # Cross-session persistence (SQLite/Redis)
│ └── vector_store.py # Semantic retrieval (vector database)
│
├── config/ # ⚙️ Configuration layer: which model, what parameters
│ ├── models.py # Model config (switchable OpenAI/Claude/Ollama)
│ ├── prompts.py # System Prompt templates
│ └── settings.py # Global settings (max_iterations, timeout)
│
├── workflows/ # 🔄 Orchestration layer: how to collaborate
│ ├── code_review.py # Code review workflow
│ └── feature_dev.py # Feature development workflow (with rollback retry)
│
├── main.py # Entry point
├── requirements.txt
├── .env.example
└── README.md
One Sentence Per Layer
| Layer | One Sentence | Analogy |
|---|---|---|
agents/ |
Defines "who"—role, goal, boundaries | Company org chart |
tools/ |
Defines "what can be done"—specific capabilities | Employee's toolbox |
memory/ |
Defines "what is remembered"—context and knowledge | Employee's brain |
config/ |
Defines "which model to use" | Company policy |
workflows/ |
Defines "how to collaborate" | Project process |
Principle: First get the simple 5-file version running, then split into layers as needed. Don't build a 5-layer architecture right away—that's only needed after the 3rd Agent appears.
3. Five Pitfalls I Fell Into
I fell into all of these. Each one wasted at least half a day.
Pitfall 1: Thinking a Long Prompt = an Agent
My initial "Agent" was just a 3000-character System Prompt that said "You must first search, then analyze, then output."
It wasn't an Agent. It was a Chatbot with a long instruction manual.
The core of an Agent isn't prompt length; it's the loop:
# ❌ This is not an Agent; this is a function call
response = llm.call("Help me research xxx")
# ✅ The Agent's core is this loop
while not done:
observation = agent.observe(environment) # What did it see
thought = agent.think(observation) # What did it think
action = agent.decide(thought) # What did it decide to do
result = agent.act(action) # Execute
done = agent.evaluate(result) # Is it enough? If not, go again
This loop is called ReAct (Reasoning + Acting). Without it, it's not an Agent.
Pitfall 2: Giving the Agent 30 Tools
"More tools means more powerful, right!"—that's what I thought at first.
The result: The Agent started having "choice paralysis." Asked to search for something, it first read a file. Asked to write code, it first searched a webpage.
Measured experience:
| Number of Tools | Task Accuracy (perceived) |
|---|---|
| 3-5 | ~90% |
| 10-15 | ~70% |
| 30+ | Starts calling tools randomly, ~50% |
Principle: Each Agent gets at most 5-7 tools. Need more capabilities? Split into multiple Agents, each managing only its own set of knives.
Pitfall 3: Building an "Omnipotent Agent"
# ❌ What I initially wrote
agent = Agent(
name="Everything",
instructions="You can search, write code, review code, deploy, write docs, make PPTs..."
)
It did everything, and did nothing well.
Later I split it into 3: Researcher only searches, Coder only writes, Reviewer only reviews. Each Agent's instructions were under 200 characters.
Immediate effect: Single Agent output quality went from "barely usable" to "basically no changes needed."
Pitfall 4: Not Setting a Maximum Loop Count
One evening I let an Agent research a technical solution and went to dinner.
Came back to find: It was still running. Searched 47 web pages, each time thinking "information is insufficient, search again." Burned $3.2 in tokens.
# ✅ Must set an upper limit
result = await Runner.run(
agent,
"Research xxx",
max_turns=10, # Max 10 rounds of tool calls; stop if exceeded
)
Lesson: An Agent has no concept of "enough." You must set it for them.
Pitfall 5: Not Implementing Human-in-the-loop
I asked an Agent to "clean up temporary files in the project."
It diligently executed rm -rf node_modules .git dist.
.git was gone.
# ✅ Dangerous operations must pause and wait for human confirmation
from agents import Agent, function_tool
@function_tool(needs_approval=True) # Key: pause before execution, wait for human confirmation
def delete_file(path: str) -> str:
"""Delete specified file"""
os.remove(path)
return f"Deleted {path}"
It's 2026, and Agents still need human oversight. Especially: delete operations, money-related actions, permission changes, externally-facing outputs.
Individual vs. Enterprise: Different Directions
| Dimension | Individual Developer (me) | Enterprise Team |
|---|---|---|
| Starting point | OpenAI Agents SDK, single file | LangGraph / CrewAI + orchestration platform |
| Model | OpenAI / Claude API | Private deployment + API hybrid |
| Memory | Local SQLite | Redis + vector database |
| Deployment | Run locally / simple script | K8s + monitoring + alerting |
| Security | .env manages keys |
Vault + RBAC + audit logs |
| Monthly cost | $10-50 | Budgeted, requires cost monitoring |
| Observability | print + Tracing panel | LangSmith / professional Tracing |
If you're an individual, don't do the enterprise thing. One .py file + one .env, that's enough.
4. 15 Minutes: Build a Research Agent That Can Search and Write Files
Framework choice: OpenAI Agents SDK. Reason: Runs in 5 lines of code, built-in Tracing, supports 100+ models, most active community in 2026. The complex version later also uses it; no framework switching.
Step 1: Environment Setup (2 minutes)
Prerequisite: Python 3.10+ (check with
python3 --version). For frontend folks who haven't installed Python: macOS usebrew install [email protected], Windows go to python.org to download and install.
mkdir my-first-agent && cd my-first-agent
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install openai-agents python-dotenv
# Environment variables (using Alibaba Bailian/Tongyi Qianwen here, direct connection in China, no VPN needed)
# Go to https://bailian.console.aliyun.com to activate; free quota is enough for dozens of runs
cat > .env << EOF
OPENAI_API_KEY=sk-xxx # Bailian API Key
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
TAVILY_API_KEY=tvly-xxx # For search tool, free registration at https://tavily.com
EOF
echo ".env" >> .gitignore
Why Bailian instead of OpenAI? Three reasons: direct connection in China without proxy, free quota enough for learning, qwen-plus has strong Chinese capability. Chapter 6 later explains how to switch back to OpenAI or local Ollama.
Step 2: Write Tools (5 minutes)
Tools are the Agent's hands and feet. An Agent without tools is just a talking mouth.
# tools.py
import subprocess
from pathlib import Path
from agents import function_tool
@function_tool
def web_search(query: str) -> str:
"""Search the web to get the latest information. Use when needing real-time data, technical docs, or news.
Args:
query: Search keywords, be as specific as possible. E.g., "OpenAI Agents SDK 2026 new features" is better than "AI framework".
"""
import json, os
api_key = os.getenv("TAVILY_API_KEY", "")
if not api_key:
return "Error: TAVILY_API_KEY environment variable not set. Please register for free at https://tavily.com."
try:
result = subprocess.run(
["curl", "-s", "-X", "POST", "https://api.tavily.com/search",
"-H", "Content-Type: application/json",
"-d", json.dumps({"api_key": api_key, "query": query, "max_results": 3})],
capture_output=True, text=True, timeout=15
)
data = json.loads(result.stdout)
summaries = [f"- {r.get('title', '')}: {r.get('content', '')[:300]}" for r in data.get("results", [])]
return "\n".join(summaries)[:2000] if summaries else "No relevant results found"
except (subprocess.TimeoutExpired, json.JSONDecodeError) as e:
return f"Search failed: {e}, please try a different keyword"
@function_tool
def read_file(file_path: str) -> str:
"""Read local file content. Use when needing to view code, docs, or config files.
Args:
file_path: Relative or absolute path to the file
"""
path = Path(file_path)
if not path.exists():
return f"File does not exist: {file_path}"
if path.stat().st_size > 50_000:
return f"File too large ({path.stat().st_size} bytes), please specify line number range"
return path.read_text(encoding="utf-8")[:5000]
@function_tool
def write_file(file_path: str, content: str) -> str:
"""Write content to a file. Use when needing to save research results or generate reports.
Args:
file_path: Target file path
content: Complete content to write
"""
path = Path(file_path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
return f"✅ Written to {file_path} ({len(content)} characters)"
3 details, all added after falling into pitfalls:
- Write "when to use" in the docstring—the Agent relies on this text to decide whether to call the tool
- Return values must be truncated (
[:2000])—without truncation, one search eats 50% of the context - Errors must return strings, not throw exceptions—the Agent sees the error message and will retry on its own
Step 3: Define the Agent (3 minutes)
# agent.py
from agents import Agent
from tools import web_search, read_file, write_file
research_agent = Agent(
name="Research Assistant",
model="qwen-plus", # Bailian model name. For OpenAI, change to "gpt-4o"
instructions="""You are a research assistant.
## Workflow
1. Understand the user's research question, break it into 2-3 sub-questions
2. Use web_search for each sub-question
3. Evaluate whether the information is sufficient—if not, change keywords and search again
4. Organize into a structured report, save to the reports/ directory using write_file
## Rules
- After each search, ask yourself: "Is the information enough to answer the question?" If not, continue
- The report must cite information source URLs
- Mark uncertain information with [To be verified]
- Search at most 5 times. If 5 times is still not enough, honestly state "Information is limited"
- Report in Chinese, keep technical terms in English""",
tools=[web_search, read_file, write_file],
)
Note the instructions writing style:
- Has a clear workflow (not vague instructions like "help me research something")
- Has an exit condition ("at most 5 times")—prevents Pitfall 4's infinite loop
- Has output standards ("cite sources", "use Chinese")
modelfield specifies the model—Bailian usesqwen-plus, OpenAI usesgpt-4o, local usesllama3.1:8b
Step 4: Run (2 minutes)
# main.py
from dotenv import load_dotenv
load_dotenv()
import os
os.environ["OPENAI_AGENTS_DISABLE_TRACING"] = "true"
import asyncio
from agents import Runner, set_default_openai_api
from agent import research_agent
# Bailian doesn't support Responses API, force Chat Completions API
set_default_openai_api("chat_completions")
async def main():
user_input = input("Enter your research question (press Enter to use default):\n> ").strip()
if not user_input:
user_input = "Research the pros and cons comparison of mainstream AI Agent frameworks in 2026 (OpenAI Agents SDK, LangGraph, CrewAI)"
result = await Runner.run(
research_agent,
input=user_input,
max_turns=10,
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())
python main.py
Two lines of "ugly code," but missing them causes errors directly:
| Code | Why needed | When using OpenAI |
|---|---|---|
OPENAI_AGENTS_DISABLE_TRACING=true |
Bailian doesn't support OpenAI's Tracing reporting | Delete it, enjoy built-in Tracing |
set_default_openai_api("chat_completions") |
Bailian only supports Chat Completions API | Delete it, SDK default is fine |
Step 5: See How It Thinks (3 minutes)
After running, you'll see the Agent's complete decision chain in the terminal:
Turn 1: Agent thinks → breaks down into 3 sub-questions
Turn 2: Calls web_search("OpenAI Agents SDK features 2026")
Turn 3: Calls web_search("LangGraph vs CrewAI comparison")
Turn 4: Calls web_search("AI agent framework benchmark 2026")
Turn 5: Agent thinks → information sufficient, starts writing report
Turn 6: Calls write_file("reports/agent-frameworks-2026.md", ...)
Turn 7: Outputs final summary
If you're using the OpenAI official API (without disabling Tracing), you can also open platform.openai.com/traces to see the visualization panel.
This is the difference between an Agent and a Chatbot: A Chatbot gives you a blob of text; an Agent gives you an auditable decision chain.
Final File List
my-first-agent/
├── agent.py # 22 lines: Agent definition
├── tools.py # 50 lines: 3 tools
├── main.py # 25 lines: runtime entry
├── requirements.txt # 2 lines: openai-agents + python-dotenv
└── .env # 3 lines: API Key + Base URL + Tavily Key
~100 lines of code. An Agent that can search, read/write files, and has a decision loop. Direct connection in China, no proxy needed.
5. 45 Minutes: Build a 3-Person Collaborative Dev Team
Still OpenAI Agents SDK. No framework switching. The core change from 1 Agent to 3 is: Handoff (task transfer).
Goal
Input one requirement: "Implement a React useDebounce Hook."
3 Agents collaborate automatically:
- PM Agent analyzes the requirement → outputs a technical plan
- Coder Agent writes code based on the plan → outputs to
src/ - Reviewer Agent reviews the code → pass or reject
If the review fails, automatically send back to Coder for rewrite, max 2 retries.
Environment Setup
Same as Chapter 4, requires Python 3.10+ environment. If you already ran Chapter 4, just reuse that virtual environment—dependencies are identical.
# If not installed yet:
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
This chapter reuses
tools.pyandrequirements.txtfrom Chapter 4; no additional dependencies needed.
Step 1: Define 3 Agents
# agents.py
from agents import Agent, function_tool
from tools import read_file, write_file
# ---------- PM Agent ----------
pm_agent = Agent(
name="PM",
model="qwen-plus",
instructions="""You are a technical product manager.
## Responsibility
Transform user requirements into executable technical plans.
## Output Format (strictly follow)
### Feature Description
One sentence explaining what to do.
### Interface Definition
- Input: parameter names, types, default values
- Output: return value type
- Usage example: ```tsx code block
### Boundary Conditions (at least 3)
1. ...
2. ...
3. ...
### Acceptance Criteria (testable checklist)
- [ ] ...
- [ ] ...
## Rules
- First ask "what does the user really want," don't design directly
- If the requirement is vague, list your assumptions
- Don't write code, only write the plan""",
tools=[read_file],
)
# ---------- Coder Agent ----------
coder_agent = Agent(
name="Coder",
model="qwen-plus",
instructions="""You are a senior frontend engineer.
## Responsibility
Write code according to the technical plan.
## Rules
- Write code to the src/ directory (use write_file tool)
- Main file: src/index.ts
- Test file: src/index.test.ts
- All functions must have JSDoc comments
- Error handling must not be omitted
- If the plan is ambiguous, mark in code comments // TODO: plan ambiguity - xxx
- Use TypeScript strict mode""",
tools=[read_file, write_file],
)
# ---------- Reviewer Agent ----------
reviewer_agent = Agent(
name="Reviewer",
model="qwen-plus",
instructions="""You are a code review expert.
## Review Dimensions (by priority)
1. Logical correctness—check against the technical plan's acceptance criteria item by item
2. Boundary conditions—are all boundary conditions listed in the plan handled
3. Error handling—are any exceptions swallowed
4. Type safety—is there any 'any'
5. Performance—are there unnecessary re-renders/repeated calculations
## Output Format
If passed:
"LGTM ✅" + one-sentence summary of highlights
If not passed:
"NEEDS_REVISION ❌" + specific problem list (precise to line number + fix suggestion)
## Rules
- The goal is to help, not to nitpick
- Style issues (semicolons, indentation) don't mention
- At most 5 problems, pick the most serious""",
tools=[read_file],
)
Step 2: Define Handoff and Orchestration
# workflow.py
from agents import Agent, Runner, handoff
from agents.stream_events import RunItemStreamEvent, AgentUpdatedStreamEvent
from agent import pm_agent, coder_agent, reviewer_agent
# Add Handoff to PM: after analysis, hand over to Coder
pm_agent.handoffs = [handoff(coder_agent)]
# Add Handoff to Coder: after writing, hand over to Reviewer
coder_agent.handoffs = [handoff(reviewer_agent)]
# Reviewer's Handoff: send back to Coder (conditional trigger)
reviewer_agent.handoffs = [handoff(coder_agent)]
def _format_event(event):
"""Format stream events into readable output"""
if isinstance(event, AgentUpdatedStreamEvent):
print(f"\n🤖 [{event.new_agent.name}] Taking over...")
elif isinstance(event, RunItemStreamEvent):
name = event.name
item = event.item
if name == "message_output_created":
text = getattr(item, 'raw_item', None)
if text:
content = getattr(text, 'content', '')
if isinstance(content, list):
for c in content:
if hasattr(c, 'text') and c.text:
print(f" 💬 {c.text[:200]}")
elif isinstance(content, str) and content.strip():
print(f" 💬 {content[:200]}")
elif name == "tool_called":
tool_name = getattr(item, 'name', 'unknown')
print(f" 🔧 Calling tool: {tool_name}")
elif name == "tool_output":
print(f" ✅ Tool execution complete")
elif name == "handoff_requested":
print(f" ➡️ Requesting handoff...")
elif name == "handoff_occured":
print(f" 🔀 Handoff complete")
async def run_dev_team(requirement: str) -> str:
"""Run the dev team, displaying each Agent's execution process in real-time"""
result = Runner.run_streamed(
pm_agent,
input=f"Requirement: {requirement}\n\nPlease analyze the requirement and output a technical plan, then hand over to Coder for implementation.",
max_turns=20,
)
async for event in result.stream_events():
_format_event(event)
return result.final_output
Here
Runner.run_streamedis used instead ofRunner.run—the difference is you can see in real-time what each Agent is doing: who took over, what tool was called, what was output. When debugging multi-Agent collaboration, this is far better than waiting for the final result.
Step 3: Add Retry Logic
# main.py
from dotenv import load_dotenv
load_dotenv()
import os
os.environ["OPENAI_AGENTS_DISABLE_TRACING"] = "true"
import asyncio
from agents import set_default_openai_api
from workflow import run_dev_team
# Bailian/Tongyi Qianwen doesn't support Responses API, force Chat Completions API
set_default_openai_api("chat_completions")
async def main():
default_req = "Implement a React Hook: useDebounce, supporting custom delay (default 300ms), cancel, and immediate execution (flush)"
user_input = input(f"Enter development requirement (press Enter to use default):\n> ").strip()
requirement = user_input if user_input else default_req
max_retries = 2
for attempt in range(max_retries + 1):
print(f"\n{'='*50}")
print(f"🚀 Round {attempt + 1}")
print(f"{'='*50}")
result = await run_dev_team(requirement)
if "LGTM" in result:
print("\n✅ Review passed! Code has been written to src/ directory")
break
elif attempt < max_retries:
print(f"\n❌ Review not passed, sending back for rewrite ({max_retries - attempt} chances remaining)")
requirement += f"\n\nPrevious round review comments:\n{result}"
else:
print("\n⚠️ Retry limit exhausted, manual intervention required")
print(result)
if __name__ == "__main__":
asyncio.run(main())
Same as Chapter 4, the trio is essential:
load_dotenv()loads.env, disable Tracing, force Chat Completions API. Forgetting any line will cause errors.
Step 4: Run
python main.py
You'll see:
==================================================
🚀 Round 1
==================================================
[PM] Analyzing requirement...
→ Output technical plan: interface definition, boundary conditions, acceptance criteria
→ Handoff to Coder
[Coder] Writing code...
→ write_file("src/index.ts", ...)
→ write_file("src/index.test.ts", ...)
→ Handoff to Reviewer
[Reviewer] Reviewing code...
→ NEEDS_REVISION ❌
→ Problem 1: src/index.ts:23 - timer not cleared after flush
→ Problem 2: src/index.test.ts - missing cancel test case
❌ Review not passed, sending back for rewrite (2 chances remaining)
==================================================
🚀 Round 2
==================================================
[Coder] Modifying based on review comments...
→ Fixed flush logic
→ Added cancel test
→ Handoff to Reviewer
[Reviewer] Reviewing again...
→ LGTM ✅ Logic complete, all boundary conditions covered
✅ Review passed! Code has been written to src/ directory
Architecture Diagram
┌──────────────────────────────────────────────────────┐
│ Dev Team Workflow │
│ │
│ ┌────────┐ Handoff ┌────────┐ Handoff ┌────────┐│
│ │ PM │─────────▶│ Coder │─────────▶│Reviewer││
│ │Req Analysis│ │Code Impl│ │Code Review││
│ └────────┘ └────────┘ └───┬────┘│
│ ▲ │ │
│ │ NEEDS_REVISION │ │
│ └──────────────────┘ │
│ │
│ Outer loop: main.py controls max 2 retries │
│ Exceeding 2 → manual intervention │
└──────────────────────────────────────────────────────┘
Differences from the simple version:
| Dimension | Simple (1 Agent) | Complex (3 Agents) |
|---|---|---|
| Agent count | 1 | 3 |
| Tool allocation | 1 Agent gets all tools | Each Agent only gets what it needs |
| Collaboration | None | Handoff transfer |
| Quality control | Self-check | Independent Reviewer |
| Failure handling | None | Send back for rewrite + retry limit |
6. Switching Models: From OpenAI to Local Ollama
Everything above used cloud APIs (Bailian). But some scenarios require switching:
- Privacy: Code can't leave the company
- Cost: High-frequency simple tasks, API fees too expensive
- Offline: Must run without internet
Solution Comparison (One Table)
| Solution | Suitable for | Monthly Cost | Speed | Capability |
|---|---|---|---|---|
| OpenAI API | Rapid prototyping, complex reasoning | $10-50 | 200+ tok/s | ⭐⭐⭐⭐⭐ |
| Claude API | Long text, code review | $10-50 | 150+ tok/s | ⭐⭐⭐⭐⭐ |
| Ollama Local | Privacy, offline, high-frequency simple tasks | Hardware one-time | 20-40 tok/s | ⭐⭐⭐ |
| vLLM Self-deployed | Enterprise privatization, high concurrency | Server cost | 100+ tok/s | Depends on model |
| LiteLLM Proxy | Unified entry for multiple models | Pass-through | Pass-through | Pass-through |
Switch to Ollama: 3 Steps Done
# 1. Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# 2. Pull models
ollama pull llama3.1:8b
ollama pull qwen2.5-coder:7b
# 3. Change 2 lines of code
from openai import AsyncOpenAI
from agents import Agent, Runner, set_default_openai_client
local_client = AsyncOpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
)
set_default_openai_client(local_client, use_for_tracing=False)
agent = Agent(
name="Local Agent",
instructions="You are a locally running assistant",
model="llama3.1:8b",
)
import asyncio
result = asyncio.run(Runner.run(agent, "Write a binary search in Python"))
print(result.final_output)
That's it. Tool definitions don't change, Agent logic doesn't change, only the model connection changes.
Switch to Claude: Via LiteLLM Bridge
pip install 'openai-agents[litellm]'
agent = Agent(
name="Claude Agent",
instructions="...",
model="litellm/anthropic/claude-sonnet-4-20250514",
)
Enterprise: vLLM Deployment
vllm serve meta-llama/Llama-3.1-8B-Instruct \
--host 0.0.0.0 --port 8000 \
--max-model-len 8192
Connection method is the same as Ollama, just change base_url to your server address.
Multi-Model Unified Entry: LiteLLM Proxy
# litellm_config.yaml
model_list:
- model_name: fast
litellm_params:
model: openai/gpt-4o-mini
- model_name: smart
litellm_params:
model: anthropic/claude-sonnet-4-20250514
- model_name: private
litellm_params:
model: ollama/llama3.1:8b
api_base: http://localhost:11434
litellm --config litellm_config.yaml --port 4000
Selection Decision Tree
Is your data sensitive?
├── Yes → Local deployment
│ ├── Individual use → Ollama (one command done)
│ └── Enterprise use → vLLM (high concurrency + multi-GPU)
└── Not sensitive → Is the task complex?
├── Complex reasoning/code → OpenAI / Claude API
└── Simple/high-frequency → Local Ollama to save money
My actual setup: Daily use gpt-4o-mini (cheap), complex reasoning switch to gpt-4o, company code goes through local Ollama. About $15/month.
7. Q&A: The 7 Most Frequently Asked Questions
Q1: I already have 31 Skills, do I still need Agents?
Look at your pain points:
| Pain Point | Solution |
|---|---|
| AI doesn't follow Skill rules | Optimize the Skill itself (not an Agent problem) |
| Must manually trigger every time | Agent (proactivity) |
| Can't remember context across sessions | Agent + Memory |
| Complex tasks need multi-step coordination | Multi-Agent |
| Want to automate the entire dev workflow | Agent is mandatory |
Skills and Agents are not substitutes. My current system: 31 Skills set standards, 3 Agents do orchestration. Skills are the law, Agents are the judge.
Q2: Which framework is suitable for beginners?
| Framework | Learning Curve | One Sentence |
|---|---|---|
| OpenAI Agents SDK | ⭐ Lowest | Runs in 5 lines of code |
| CrewAI | ⭐⭐ | Give an Agent a "job title" |
| LangGraph | ⭐⭐⭐ | Kubernetes for Agents |
| AutoGen | ⭐⭐⭐⭐ | Most academic flavor |
Beginner path: OpenAI Agents SDK (understand concepts) → CrewAI (multi-Agent role division) → LangGraph (production-grade state machine)
Q3: How to control Agent token costs?
My measured consumption for a single Research Agent task:
| Component | Tokens | Percentage |
|---|---|---|
| System Prompt | ~500 | 5% |
| Tool descriptions | ~300 | 3% |
| Tool return results | ~5000 | 50% |
| Agent reasoning | ~3000 | 30% |
| Final output | ~1000 | 10% |
The bulk is tool return values. Control strategies:
- Tool returns must be truncated (
[:2000]) - Simple judgments use
gpt-4o-mini, complex reasoning usegpt-4o - Set
max_turnsto prevent infinite loops - Cache repeated queries
Q4: Can local models run Agents?
Yes, but depends on model size:
| Model | Memory Requirement | Agent Capability | Suitable Scenarios |
|---|---|---|---|
| 7B | 8GB+ | Single tool, simple ReAct | File operations, format conversion |
| 13B | 16GB+ | Multi-tool, simple planning | Code generation, Q&A |
| 70B | 48GB+ | Close to cloud | Complex reasoning |
Measured experience: 7B model running a 3-tool Agent, success rate about 60%. Same task with GPT-4o is 95%+.
Q5: How do multiple Agents communicate?
Three mainstream patterns:
Pattern 1: Handoff transfer (used in this article)
PM → Coder → Reviewer
Pattern 2: Central orchestration
Orchestrator
/ | \
Agent A Agent B Agent C
Pattern 3: Group chat negotiation (AutoGen style)
Agent A ←→ Agent B ←→ Agent C
Beginners start with Pattern 1. Patterns 2 and 3 are for when you have 5+ Agents.
Q6: What if an Agent "runs wild"?
7 lines of defense, ordered by importance:
max_turns: Max N rounds of tool calls, force stop if exceeded- Tool whitelist: Don't give
rm -rfcapability needs_approval=True: Dangerous operations pause for human confirmation- Guardrails: Input/output validation (built into SDK)
- Sandbox execution: Code runs in isolated environment
- Tracing: Every step logged, can trace back when problems occur
- Cost circuit breaker: Auto-terminate when token threshold exceeded
# Guardrails example: intercept dangerous input
from agents import Agent, input_guardrail, GuardrailFunctionOutput, RunContextWrapper
@input_guardrail
async def block_dangerous(
ctx: RunContextWrapper, agent: Agent, input: str | list
) -> GuardrailFunctionOutput:
text = input if isinstance(input, str) else str(input)
dangerous = ["delete all", "rm -rf", "drop table", "format c:"]
is_safe = not any(word in text.lower() for word in dangerous)
return GuardrailFunctionOutput(
output_info={"safe": is_safe},
tripwire_triggered=not is_safe,
)
agent = Agent(
name="Safe Agent",
instructions="You are a safe assistant",
input_guardrails=[block_dangerous],
)
Q7: Do custom GPTs count as Agents?
They count as a "trial version." They have System Prompt (≈ Skill) + knowledge base (≈ RAG) + a few tools, but:
- ❌ No autonomous loop
- ❌ No persistent memory
- ❌ No multi-Agent collaboration
- ❌ No code-level control
A real Agent requires code. Custom GPTs let you experience the feeling of an Agent, but can't do what an Agent does.
Conclusion
In the previous chapter, we wrote a job description for the AI. In this chapter, we hired a person.
Skill: "You tell me how to do it, and I'll do it that way."
Agent: "You tell me the goal, and I'll figure out how myself."
82 lines of code, a Research Agent that can search, write files, and judge for itself whether "it's enough."
Another 100 lines, and it gained two colleagues: one writes code, one reviews code. If the review fails, it sends back for rewrite on its own.
This isn't science fiction. This is what I did on a Tuesday afternoon in 2026, sitting at my desk.
OpenAI made the Agents SDK. 100,000 developers are already using it.
Now it's your turn.
Start with that task you manually coordinate across 3 steps every day. Hand it to an Agent. Then watch it run on its own.
That feeling—it's like you finally hired a colleague who doesn't need to be prodded.
Series Review:
- Part 1: "Frontend Team AI Efficiency Practice Based on Four-Layer Skill System"
- *Part 2: "Stop Tuning Prompts by Feel: 8 Demos to Manage Prompts Like Code"
- Part 3: "One AGENTS.md, AI Code Standard Compliance Rate Soars from 60% to 95%"
- Part 4: "More AI Skills Make It Dumber? A Survival Guide for Token Context Explosion"
- Part 5: "Writing Your First AI Skill Step by Step"
Reference Sources
| Source | Content |
|---|---|
| OpenAI Agents SDK | Official docs + GitHub README (Agent / Handoff / Guardrails / Tracing) |
| CrewAI | GitHub README (Crews + Flows dual mode) |
| LangGraph | GitHub README (State machine / Persistent execution / Human-in-the-loop) |
| AutoGen | Microsoft GitHub (Actor model / Event-driven) |
| Ollama | GitHub README (Local deployment / REST API / OpenAI-compatible interface) |
| Anthropic | Agentic Systems official docs |
| LiteLLM | Multi-model unified proxy |