Lorem, ipsum dolor sit amet consectetur adipisicing elit. Qui, itaque voluptate ipsa non enim amet ducimus voluptatibus deserunt nam esse!
A Practical Guide to a 50-Line Deep Seek Agent in JavaScript

A Practical Guide to a 50-Line Deep Seek Agent in JavaScript

pr0h0
javascriptai-agentsdeepseekprogramming
AI Usage (88%)

Why a 50-line agent is worth building

I like small agent loops because they surface failure modes quickly. You avoid framework glue, and you can see exactly where the model is allowed to act, where it has to stop, and what happens when its output is messy.

The useful part of a tiny agent is not that it is clever. It is that it makes the control flow plain:

  • the model receives a goal
  • it can ask for a tool
  • your code runs the tool
  • the model gets the result
  • the loop ends when the model says it is done

That is enough to prototype internal helpers, safe lookup agents, or a command assistant for narrow tasks.

What the agent actually needs to do

Prompt, model call, and response parsing

A minimal agent needs three things:

  1. a system prompt that defines the job and the allowed tools
  2. a model call that returns structured text
  3. a parser that can tell whether the model is done or wants a tool

The parser is where people usually gloss over the hard part. In practice, the model will sometimes return valid JSON, sometimes extra text, and sometimes something that looks structured but still fails to parse. If your agent cannot recover from that, it breaks on the first odd response.

Tool boundary and when to stop

The other key decision is the tool boundary. The model should not be able to call arbitrary functions. I usually define a small set of named tools and a hard stop condition:

  • search for lookup
  • read_file for local inspection
  • done for final output

If the agent asks for anything else, reject it. If it loops more than a few times, stop it. That is the difference between a bounded workflow and an accidental runaway process.

Minimal JavaScript implementation

Environment setup and API client

Here is the shape I start with. It is intentionally plain.

agent.js
import OpenAI from "openai";

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const tools = {
search: async ({ query }) => {
  return `results for: ${query}`;
},
};

const systemPrompt = `You are a small agent.
Use only the available tools.
Return JSON with either { "tool": "...", "args": {...} } or { "final": "..." }.`;

A small loop for reasoning and tool calls

The loop below is enough for a useful prototype. It keeps the state in memory and stops after a few rounds.

agent-loop.js
async function runAgent(task) {
const messages = [
  { role: "system", content: systemPrompt },
  { role: "user", content: task },
];

for (let step = 0; step < 5; step += 1) {
  const response = await client.chat.completions.create({
    model: "deepseek-chat",
    messages,
    temperature: 0,
  });

  const text = response.choices[0]?.message?.content ?? "";
  let data;

  try {
    data = JSON.parse(text);
  } catch {
    messages.push({
      role: "user",
      content: "Your last reply was not valid JSON. Return only JSON.",
    });
    continue;
  }

  if (data.final) return data.final;

  if (!tools[data.tool]) {
    return `رفض unsupported tool: ${data.tool}`;
  }

  const result = await tools[data.tool](data.args || {});
  messages.push({ role: "assistant", content: text });
  messages.push({ role: "tool", content: String(result), tool_call_id: data.tool });
}

throw new Error("agent exceeded step limit");
}

The parts that break first

Truncation, malformed JSON, and runaway loops

The first breakage is usually output truncation. If the model hits a token limit mid-object, JSON.parse fails and the whole loop becomes brittle. I treat that as normal, not exceptional.

The second breakage is malformed JSON. Even with a tight prompt, the model may add commentary or drop quotes. The fix is not “trust it more.” The fix is to reprompt with a very specific correction message and keep the loop bounded.

The third breakage is the agent that never decides. That happens when the prompt encourages reasoning but not termination. A hard maximum step count is mandatory.

Failure modeWhat you seeWhat to do
TruncationJSON cut off halfwayRetry once, then stop
Malformed JSONExtra prose or invalid syntaxReprompt for strict JSON only
Runaway loopRepeated tool requestsCap steps and add stop rules

Cost control and timeout handling

A tiny agent gets expensive when it retries too much or uses a chatty model for every step. I keep three limits in place:

  • max steps per task
  • max tool calls per task
  • timeout for each tool call

If a tool hangs, the agent should fail closed. Do not let the model block your runtime waiting on a network call that never returns.

Hardening the pattern without bloating it

Schema checks and safe tool design

This is where the 50-line version usually becomes a real system. I would not add a big framework first. I would add schema checks.

Use a validator for the tool payload before execution. If the model says search, then args.query should be a string and nothing else. Keep tools narrow and deterministic. The more side effects a tool has, the more damage a bad prompt can do.

⚠️

Do not expose filesystem writes, shell execution, or admin APIs in the first version. A bad agent prompt should be annoying, not destructive.

Logging, retries, and observability

You need logs for:

  • the prompt sent
  • the raw model output
  • the parsed tool request
  • the tool result
  • why the loop stopped

Without that, debugging is guesswork. I also retry only on transient transport failures, not on every bad model response. If the model output is invalid three times in a row, that is a product signal, not a network glitch.

What to keep if you turn this into production code

Keep the loop small, but move the risky parts into explicit boundaries:

  • strict input and output schemas
  • a hard step limit
  • per-tool timeouts
  • a deny-by-default tool registry
  • structured logs
  • replayable traces for debugging

If you need user-facing reliability, add approval steps before any side-effecting tool runs. The model can suggest an action; your app should still own the final decision.

Conclusion

A 50-line agent is useful because it makes the control surface visible. You can see exactly where parsing fails, where tools need validation, and where the loop needs to stop. That clarity is the point.

If you keep the first version narrow, a small JavaScript agent is a solid way to learn the shape of model-driven workflows without hiding the hard parts behind a framework.

Share this post

More posts

Comments