In my previous article, Using Claude Code and Natural Language to Create SAP Datasphere Artifacts, I used Claude Code’s natural language capabilities to automatically generate SAP Datasphere models — in that scenario, Claude Code itself was the Agent, understanding my intent, calling CLI scripts, and completing the modeling. But afterward, I kept thinking: what if I wanted an Agent of my own? One with a web interface that teammates could use, with freely extensible tools? So I decided: use Vibe Coding to build my own Agent.
1. From “Using an Agent” to “Building an Agent”
Let me clarify the difference between these two things.
In the previous article, the workflow was: I told Claude Code in the terminal “create a sales data table,” and Claude Code understood my intent, assembled the parameters, called JS scripts, and built the table. The Agent here was Claude Code — its Agent Loop, tool calling, and context management were all built by Anthropic, ready to use out of the box.
But this had several limitations: only I could use it in the terminal, with no way to share it with colleagues unfamiliar with the command line; the tool extension and orchestration logic was locked inside Claude Code, beyond my customization; and if I wanted to switch LLM backends or add RAG retrieval capabilities, there was no way in.
So this time, my goal was to implement every component of the Agent myself — LLM calls, tool registration, Agent Loop, streaming communication, Web UI — and plug in the previously written JS skill scripts as tools. The development approach? Still Vibe Coding, still conversing with Claude Code, except this time its role shifted from “Agent” to “development partner helping me build an Agent.”
2. What Is an AI Agent? Let’s Talk Design Patterns
Before getting hands-on, it’s worth understanding the major design patterns for AI Agents. These aren’t academic concepts — they’re choices you must make when building an Agent, and they determine how your Agent reasons and orchestrates tool calls.
2.1 ReAct: Reason-Act-Observe
ReAct (Reasoning + Acting) is currently the most widely used Agent pattern. Its workflow is intuitive:
Reason (Which tool should I call?) → Act (Call the tool) → Observe (See the result) → Reason again (Is that enough? What’s next?)
This loops until the task is complete. This is exactly the core pattern my Agent uses. The LLM doesn’t give a one-shot answer — it continuously reasons, executes, and reasons again in a loop. For example, when a user says “create a sales data table with ID, name, and amount,” the Agent’s inner monologue might be:
Reason: The user wants a table, I should call the create_local_table toolAct: Assemble parameters, call the toolObserve: Success returned, table createdReply to user: Table created with ID, NAME, and AMOUNT columns
2.2 Function Calling / Tool Use: Giving LLMs Hands and Feet
LLMs can only generate text on their own. But through the Tool Use mechanism, you can provide the LLM with descriptions of available tools (name, parameters, purpose), and the LLM can output not just text but structured tool-calling instructions — “I want to call create_local_table with parameters {name: ‘SALES’, columns: ‘…’}.” An external program executes the call and feeds the result back to the LLM.
This is OpenAI’s function calling protocol and the foundational capability of most Agent frameworks. My Agent is built on this mechanism: existing scripts are registered as tools, and the LLM decides when to call which one.
2.3 Reflection: Self-Review and Error Correction
The Reflection pattern lets an Agent look back at its own output to judge “was that right?” and correct course if not. It typically doesn’t require extra code logic — it can be implemented through System Prompt instructions. For example, you can tell the Agent “if the generated SQL has syntax errors, fix it yourself and re-execute,” and the Agent will autonomously adjust its strategy upon observing an error, rather than throwing it directly to the user.
2.4 Other Patterns: Planning and Multi-Agent
Planning (plan-then-execute) has the Agent formulate a multi-step plan before executing step by step. Suitable for complex tasks like “build me a data warehouse with three related tables and an analytic model.”
Multi-Agent has multiple Agents collaborate, each with its own role. For example, one understands requirements, one writes SQL, one reviews quality. Suitable for complex workflows requiring specialization.
For this project, I chose the ReAct + Tool Use + Reflection combination — sufficient for current needs, simple enough architecturally, and evolvable toward Planning or Multi-Agent later.
3. From Requirements to Plan: Claude Code’s Plan Mode
With design patterns decided, it was time to execute. I opened Claude Code and described the requirements in natural language: six existing JS CLI scripts need to be upgraded into an AI Agent framework with a Web UI, using FastAPI for the backend, Next.js for the frontend, calling LLMs through an OpenAI-compatible interface, and keeping existing scripts unchanged by calling them via Python subprocess.
Claude Code didn’t start writing code immediately. It first entered Plan Mode — generating a complete architecture plan for my review and approval. This process is important: it ensures the AI and I are aligned on “what to build,” rather than going off in different directions.
Here are the key parts of the plan Claude Code generated:
Tech Stack
Frontend: Next.js + Tailwind CSS + TypeScriptBackend: FastAPI + PythonLLM: HyperSpace LLM Proxy (OpenAI-compatible API)JS Skills: Keep existing code, call via subprocess
Project Structure
dsp-agent/
├── frontend/ # Next.js frontend
│ └── src/
│ ├── app/ # Pages
│ ├── components/# Chat components
│ ├── lib/ # API client, SSE stream parser
│ └── types/ # TypeScript types
├── backend/ # FastAPI backend
│ └── app/
│ ├── agent/ # Agent Loop, LLM client, System Prompt
│ ├── api/ # REST routes (chat, tools, health)
│ ├── tools/ # Tool base class, registry, Datasphere tools
│ └── models/ # Data models
└── skills/ # Existing JS Skills (untouched)
Phased Implementation
Phase 1: Backend Agent core (LLM client, Tool system, Agent Loop, API routes)Phase 2: Frontend chat interface (SSE streaming, message rendering, tool call visualization)Phase 3: Enhancements (session management, plugin system, vector DB interface reserved)
I reviewed the plan, adjusted a few details, and only after confirmation did it begin implementing module by module.
4. Vibe Coding in Practice: An Afternoon of Conversation
With the architecture blueprint in hand, it was time to implement. The entire process was done through Vibe Coding — describing requirements in natural language in the Claude Code CLI, advancing module by module.
4.1 Solving a UX Problem First: SSE Streaming
Before building specific modules, I thought through a UX problem: a user sends one message, but the Agent might need several rounds of tool calls, each taking tens of seconds. If we wait for everything to finish before returning one big result, users stare at a blank page — terrible experience.
The solution was SSE (Server-Sent Events). Simply put, the backend doesn’t batch up a big result to return — it pushes an event stream in real time, like a live broadcast. I defined five event types: AI starts outputting text (text_delta), starts calling a tool (tool_call_start), tool returns results (tool_call_result), error (error), and done (done). The frontend renders each event immediately as it arrives — users can see the AI “thinking and typing” in real time, with loading cards during tool execution that turn green on completion.
This decision permeated both backend and frontend design: every step of the Agent Loop must produce SSE events, and every frontend component must respond to SSE events. Getting this “communication pipeline” right first made everything downstream smoother.
4.2 Agent Loop: The Heart of the Agent
The Agent Loop is the system’s core — the concrete implementation of the ReAct pattern described above. The principle is simple: maintain a message list, keep feeding it to the LLM. The LLM either returns text (task complete) or returns tool call instructions (execute the tool, append the result to the message list, continue the loop). Every step pushes SSE events to the frontend, with a max iteration count to prevent infinite loops.
Here’s what the core code looks like (simplified):
async def run(self, user_message, history=None):
messages = [system_prompt] + (history or []) + [user_message]
tools = get_openai_tools()
for iteration in range(self._max_iterations):
# Call LLM with full message history and tool list
assistant_msg = await self._llm.chat(messages, tools=tools)
content = assistant_msg.get(“content”)
tool_calls = assistant_msg.get(“tool_calls”)
# LLM returned text → push to frontend
if content:
yield SSEEvent(event=”text_delta”, data={“content”: content})
# No tool calls → task complete, exit loop
if not tool_calls:
break
# Has tool calls → execute each, append results to message list
for tc in tool_calls:
result = await execute_tool(tc)
yield SSEEvent(event=”tool_call_result”, data=result)
messages.append(tool_result_message(tc, result))
# Back to top of loop, let LLM see tool results and decide next step
The Agent’s “intelligence” isn’t in this code — it’s just an orchestration framework. The real decisions (which tool to call, what parameters to pass, whether to retry) are all delegated to the LLM.
4.3 Tool System: Old Wine in New Bottles
The existing JS skill scripts already worked standalone — I didn’t want to rewrite them. The only question was: how to register them as Agent-callable tools in the Python backend?
The answer is a simple wrapper layer. Each tool class only needs to declare four things: name, description, parameter JSON Schema, and the script path. At execution time, Python converts the LLM’s parameters into CLI flags, calls the Node.js script via subprocess, and returns stdout as the result to the LLM.
The benefit: zero modifications to existing code — the Python layer only translates and bridges. To add a new tool in the future — whether another script, a Python function, or an HTTP API — just implement the same interface and register it.
4.4 Frontend: Chat Interface
The frontend was built with Next.js + Tailwind, with just three core components: message bubbles, tool call cards, and an input bar. Since the SSE event protocol was already defined, the frontend just needed a loop to parse the event stream and update UI state in real time. The entire frontend was also generated through Vibe Coding — describe the requirements clearly, and Claude delivered it in one pass.
Once running, I typed in the browser: “Create a customer dimension table CUSTOMER_DIM with columns CUSTOMER_ID, CUSTOMER_NAME, and CITY.” The Agent understood the intent and called the tool — the first two attempts returned 409 errors because objects with the same name already existed in Datasphere. The Agent automatically appended suffixes and retried, ultimately creating the table successfully as CUSTOMER_DIM_V3. The entire process required no human intervention.
So far I’ve only fully tested this one tool. The other five haven’t been individually verified yet, and the Agent is still a work in progress — but they share the same wrapper mechanism. The core works; what remains is adapting and polishing each one.
5. Final Thoughts
The purpose of this article is to experience the process of creating an Agent — to feel how the complete pipeline from natural language to tool calling comes together. What comes out of an afternoon of Vibe Coding is a prototype, not a production system. Building a production-grade AI Agent application requires serious attention to error handling, session management, security boundaries, and observability — following established design patterns and engineering practices, not just Vibe Coding your way through.
But the idea I want to share is this: every developer can build their own AI Agent.
You don’t need LangChain. You don’t need complex frameworks. A ReAct loop, a tool registration and orchestration mechanism, an SSE streaming channel — that’s the core, just three things. LLM capabilities come through standard OpenAI-compatible interfaces. Tools can be any scripts, APIs, or functions you already have. The Agent’s “intelligence” isn’t in the code — it’s in the System Prompt and tool design.
And Vibe Coding makes all of this remarkably fast. You don’t need to memorize every framework’s API or hand-write boilerplate. You just need to know clearly what you want, then describe it in natural language. AI handles the implementation; you handle verification and decisions.
If you have a set of scripts, an API, or any capability that can be toolified — try wrapping them into an Agent. It might only take an afternoon.
In my previous article, Using Claude Code and Natural Language to Create SAP Datasphere Artifacts, I used Claude Code’s natural language capabilities to automatically generate SAP Datasphere models — in that scenario, Claude Code itself was the Agent, understanding my intent, calling CLI scripts, and completing the modeling. But afterward, I kept thinking: what if I wanted an Agent of my own? One with a web interface that teammates could use, with freely extensible tools? So I decided: use Vibe Coding to build my own Agent.1. From “Using an Agent” to “Building an Agent”Let me clarify the difference between these two things.In the previous article, the workflow was: I told Claude Code in the terminal “create a sales data table,” and Claude Code understood my intent, assembled the parameters, called JS scripts, and built the table. The Agent here was Claude Code — its Agent Loop, tool calling, and context management were all built by Anthropic, ready to use out of the box.But this had several limitations: only I could use it in the terminal, with no way to share it with colleagues unfamiliar with the command line; the tool extension and orchestration logic was locked inside Claude Code, beyond my customization; and if I wanted to switch LLM backends or add RAG retrieval capabilities, there was no way in.So this time, my goal was to implement every component of the Agent myself — LLM calls, tool registration, Agent Loop, streaming communication, Web UI — and plug in the previously written JS skill scripts as tools. The development approach? Still Vibe Coding, still conversing with Claude Code, except this time its role shifted from “Agent” to “development partner helping me build an Agent.”2. What Is an AI Agent? Let’s Talk Design PatternsBefore getting hands-on, it’s worth understanding the major design patterns for AI Agents. These aren’t academic concepts — they’re choices you must make when building an Agent, and they determine how your Agent reasons and orchestrates tool calls.2.1 ReAct: Reason-Act-ObserveReAct (Reasoning + Acting) is currently the most widely used Agent pattern. Its workflow is intuitive:Reason (Which tool should I call?) → Act (Call the tool) → Observe (See the result) → Reason again (Is that enough? What’s next?)This loops until the task is complete. This is exactly the core pattern my Agent uses. The LLM doesn’t give a one-shot answer — it continuously reasons, executes, and reasons again in a loop. For example, when a user says “create a sales data table with ID, name, and amount,” the Agent’s inner monologue might be:Reason: The user wants a table, I should call the create_local_table toolAct: Assemble parameters, call the toolObserve: Success returned, table createdReply to user: Table created with ID, NAME, and AMOUNT columns2.2 Function Calling / Tool Use: Giving LLMs Hands and FeetLLMs can only generate text on their own. But through the Tool Use mechanism, you can provide the LLM with descriptions of available tools (name, parameters, purpose), and the LLM can output not just text but structured tool-calling instructions — “I want to call create_local_table with parameters {name: ‘SALES’, columns: ‘…’}.” An external program executes the call and feeds the result back to the LLM.This is OpenAI’s function calling protocol and the foundational capability of most Agent frameworks. My Agent is built on this mechanism: existing scripts are registered as tools, and the LLM decides when to call which one.2.3 Reflection: Self-Review and Error CorrectionThe Reflection pattern lets an Agent look back at its own output to judge “was that right?” and correct course if not. It typically doesn’t require extra code logic — it can be implemented through System Prompt instructions. For example, you can tell the Agent “if the generated SQL has syntax errors, fix it yourself and re-execute,” and the Agent will autonomously adjust its strategy upon observing an error, rather than throwing it directly to the user.2.4 Other Patterns: Planning and Multi-AgentPlanning (plan-then-execute) has the Agent formulate a multi-step plan before executing step by step. Suitable for complex tasks like “build me a data warehouse with three related tables and an analytic model.”Multi-Agent has multiple Agents collaborate, each with its own role. For example, one understands requirements, one writes SQL, one reviews quality. Suitable for complex workflows requiring specialization.For this project, I chose the ReAct + Tool Use + Reflection combination — sufficient for current needs, simple enough architecturally, and evolvable toward Planning or Multi-Agent later.3. From Requirements to Plan: Claude Code’s Plan ModeWith design patterns decided, it was time to execute. I opened Claude Code and described the requirements in natural language: six existing JS CLI scripts need to be upgraded into an AI Agent framework with a Web UI, using FastAPI for the backend, Next.js for the frontend, calling LLMs through an OpenAI-compatible interface, and keeping existing scripts unchanged by calling them via Python subprocess.Claude Code didn’t start writing code immediately. It first entered Plan Mode — generating a complete architecture plan for my review and approval. This process is important: it ensures the AI and I are aligned on “what to build,” rather than going off in different directions.Here are the key parts of the plan Claude Code generated:Tech StackFrontend: Next.js + Tailwind CSS + TypeScriptBackend: FastAPI + PythonLLM: HyperSpace LLM Proxy (OpenAI-compatible API)JS Skills: Keep existing code, call via subprocessProject Structuredsp-agent/
├── frontend/ # Next.js frontend
│ └── src/
│ ├── app/ # Pages
│ ├── components/# Chat components
│ ├── lib/ # API client, SSE stream parser
│ └── types/ # TypeScript types
├── backend/ # FastAPI backend
│ └── app/
│ ├── agent/ # Agent Loop, LLM client, System Prompt
│ ├── api/ # REST routes (chat, tools, health)
│ ├── tools/ # Tool base class, registry, Datasphere tools
│ └── models/ # Data models
└── skills/ # Existing JS Skills (untouched)
Phased ImplementationPhase 1: Backend Agent core (LLM client, Tool system, Agent Loop, API routes)Phase 2: Frontend chat interface (SSE streaming, message rendering, tool call visualization)Phase 3: Enhancements (session management, plugin system, vector DB interface reserved)I reviewed the plan, adjusted a few details, and only after confirmation did it begin implementing module by module.4. Vibe Coding in Practice: An Afternoon of ConversationWith the architecture blueprint in hand, it was time to implement. The entire process was done through Vibe Coding — describing requirements in natural language in the Claude Code CLI, advancing module by module.4.1 Solving a UX Problem First: SSE StreamingBefore building specific modules, I thought through a UX problem: a user sends one message, but the Agent might need several rounds of tool calls, each taking tens of seconds. If we wait for everything to finish before returning one big result, users stare at a blank page — terrible experience.The solution was SSE (Server-Sent Events). Simply put, the backend doesn’t batch up a big result to return — it pushes an event stream in real time, like a live broadcast. I defined five event types: AI starts outputting text (text_delta), starts calling a tool (tool_call_start), tool returns results (tool_call_result), error (error), and done (done). The frontend renders each event immediately as it arrives — users can see the AI “thinking and typing” in real time, with loading cards during tool execution that turn green on completion.This decision permeated both backend and frontend design: every step of the Agent Loop must produce SSE events, and every frontend component must respond to SSE events. Getting this “communication pipeline” right first made everything downstream smoother.4.2 Agent Loop: The Heart of the AgentThe Agent Loop is the system’s core — the concrete implementation of the ReAct pattern described above. The principle is simple: maintain a message list, keep feeding it to the LLM. The LLM either returns text (task complete) or returns tool call instructions (execute the tool, append the result to the message list, continue the loop). Every step pushes SSE events to the frontend, with a max iteration count to prevent infinite loops.Here’s what the core code looks like (simplified): async def run(self, user_message, history=None):
messages = [system_prompt] + (history or []) + [user_message]
tools = get_openai_tools()
for iteration in range(self._max_iterations):
# Call LLM with full message history and tool list
assistant_msg = await self._llm.chat(messages, tools=tools)
content = assistant_msg.get(“content”)
tool_calls = assistant_msg.get(“tool_calls”)
# LLM returned text → push to frontend
if content:
yield SSEEvent(event=”text_delta”, data={“content”: content})
# No tool calls → task complete, exit loop
if not tool_calls:
break
# Has tool calls → execute each, append results to message list
for tc in tool_calls:
result = await execute_tool(tc)
yield SSEEvent(event=”tool_call_result”, data=result)
messages.append(tool_result_message(tc, result))
# Back to top of loop, let LLM see tool results and decide next stepThe Agent’s “intelligence” isn’t in this code — it’s just an orchestration framework. The real decisions (which tool to call, what parameters to pass, whether to retry) are all delegated to the LLM.4.3 Tool System: Old Wine in New BottlesThe existing JS skill scripts already worked standalone — I didn’t want to rewrite them. The only question was: how to register them as Agent-callable tools in the Python backend?The answer is a simple wrapper layer. Each tool class only needs to declare four things: name, description, parameter JSON Schema, and the script path. At execution time, Python converts the LLM’s parameters into CLI flags, calls the Node.js script via subprocess, and returns stdout as the result to the LLM.The benefit: zero modifications to existing code — the Python layer only translates and bridges. To add a new tool in the future — whether another script, a Python function, or an HTTP API — just implement the same interface and register it.4.4 Frontend: Chat InterfaceThe frontend was built with Next.js + Tailwind, with just three core components: message bubbles, tool call cards, and an input bar. Since the SSE event protocol was already defined, the frontend just needed a loop to parse the event stream and update UI state in real time. The entire frontend was also generated through Vibe Coding — describe the requirements clearly, and Claude delivered it in one pass.Once running, I typed in the browser: “Create a customer dimension table CUSTOMER_DIM with columns CUSTOMER_ID, CUSTOMER_NAME, and CITY.” The Agent understood the intent and called the tool — the first two attempts returned 409 errors because objects with the same name already existed in Datasphere. The Agent automatically appended suffixes and retried, ultimately creating the table successfully as CUSTOMER_DIM_V3. The entire process required no human intervention.So far I’ve only fully tested this one tool. The other five haven’t been individually verified yet, and the Agent is still a work in progress — but they share the same wrapper mechanism. The core works; what remains is adapting and polishing each one.5. Final ThoughtsThe purpose of this article is to experience the process of creating an Agent — to feel how the complete pipeline from natural language to tool calling comes together. What comes out of an afternoon of Vibe Coding is a prototype, not a production system. Building a production-grade AI Agent application requires serious attention to error handling, session management, security boundaries, and observability — following established design patterns and engineering practices, not just Vibe Coding your way through.But the idea I want to share is this: every developer can build their own AI Agent.You don’t need LangChain. You don’t need complex frameworks. A ReAct loop, a tool registration and orchestration mechanism, an SSE streaming channel — that’s the core, just three things. LLM capabilities come through standard OpenAI-compatible interfaces. Tools can be any scripts, APIs, or functions you already have. The Agent’s “intelligence” isn’t in the code — it’s in the System Prompt and tool design.And Vibe Coding makes all of this remarkably fast. You don’t need to memorize every framework’s API or hand-write boilerplate. You just need to know clearly what you want, then describe it in natural language. AI handles the implementation; you handle verification and decisions.If you have a set of scripts, an API, or any capability that can be toolified — try wrapping them into an Agent. It might only take an afternoon. Read More Technology Blog Posts by SAP articles
#SAP
#SAPTechnologyblog