Login: dewitt                            Name: DeWitt Clinton
Directory: /home/dewitt                  Shell: /bin/zsh
On since Sat Nov 22 14:00 (PST) on ttys000, idle 
No unread mail.
Plan:

Agent Harness 102

I love this. Yesterday I worked through a short literate programming exercise, coding an miniature but fully functional agentic harness from scratch in about 100 or so lines of pure python.

Feel free to read that post and view the code first for context.

Today I had the idea to ask Gemini Flash 3.7 to simplify that code further, and here’s what it came up, split out into literate sections:

First, the imports and config:

import json, os, urllib.request

API_URL = "https://api.anthropic.com/v1/messages"
JSON_TYPE = {str: "string", int: "integer", float: "number", bool: "boolean"}

Next, I like the even more elegant way to define tools using only python type metadata, no import inspect required:

def tool_def(fn):
    params = {k: {"type": JSON_TYPE.get(v, "string")} 
              for k, v in fn.__annotations__.items() if k != "return"}
    return {
        "name": fn.__name__,
        "description": fn.__doc__ or "",
        "input_schema": {"type": "object", "properties": params, "required": list(params)}
    }

The post method is basically the same:

def post(payload: dict) -> dict:
    req = urllib.request.Request(
        API_URL, json.dumps(payload).encode(),
        {"content-type": "application/json",
         "anthropic-version": "2023-06-01",
         "x-api-key": os.environ["ANTHROPIC_API_KEY"]}
    )
    with urllib.request.urlopen(req) as resp:
        return json.load(resp)

And the run method is effectively the exact same idea, but there are several things I like better about this implementation. Such as:

  1. How it hoists the system message into the top-level in a more natural place (at the cost of replayability).
  2. Takes advantage of Anthropic API’s willingness to accept str blocks without "type": "text" wrappers.
  3. Inlines the tool use calls while generating the payload. (No error handling, but my version didn’t have any, either.)

The only awkward bit is the dual return paths, but I’ll allow it.

def run(prompt: str, tools: list = [], system: str = "", limit: int = 20, model: str = "claude-opus-5") -> str:
    registry = {fn.__name__: fn for fn in tools}
    tool_defs = [tool_def(fn) for fn in tools]
    messages = [{"role": "user", "content": prompt}]

    for _ in range(limit):
        payload = {"model": model, "max_tokens": 4096, "messages": messages}
        if system: payload["system"] = system
        if tool_defs: payload["tools"] = tool_defs

        res = post(payload)
        messages.append({"role": "assistant", "content": res["content"]})

        uses = [b for b in res["content"] if b["type"] == "tool_use"]
        if not uses:
            return "".join(b["text"] for b in res["content"] if b["type"] == "text")

        results = [{"type": "tool_result", "tool_use_id": u["id"],
                    "content": str(registry[u["name"]](**u["input"]))} for u in uses]
        messages.append({"role": "user", "content": results})

    return "".join(b["text"] for b in messages[-1]["content"] if b["type"] == "text")

And that’s the whole agentic harness.

Here’s the same example agent running against that harness, also basically identical to before:

import math

def calculate(expression: str) -> float:
    """Evaluate a Python arithmetic expression. The `math` module is in scope."""
    return eval(expression, {"__builtins__": {}, "math": math})

answer = run(
    prompt="What is the square root of 12345, times pi?",
    system="You are terse. Use tools rather than arithmetic.",
    tools=[calculate]
)
print(answer)

Save as harness.py and run it with python3 harness.py and you have even smaller, fully functioning agentic harness in under 50 lines of code.

(Footnote: Gemini 3.7 Flash also returned instantly with this. Imperceptable latency, like the response was already cached. Uncanny.)

_