Tool-Calling CLI Assistant
Build a CLI assistant from scratch — raw SDK only, no frameworks — that answers questions using three tools: calculator, get_current_time, and read_file. This is the atom every later lab is built from. You start from an empty directory; the skeleton below is the map.
pip install anthropic (or openai), and your API key exported. Add pip install pytest for the test criterion. No other dependencies. Budget roughly an afternoon for the core criteria.What you're building
A terminal REPL: the user types a question, your loop calls the model with three tool schemas, executes whatever the model requests, feeds results back, and prints the final answer — plus a running token/cost report. It must survive multi-step questions like "what's 3 more than the number in numbers.txt?" (read_file → calculator → answer).
Suggested structure
# tools.py — implementations + schemas
def calculator(expression: str) -> str:
# SAFELY evaluate arithmetic. No eval() on raw input — and note that
# ast.literal_eval can't do math (literals only): walk an ast.parse()
# tree allowing only number/operator nodes, or write a tiny parser.
...
def get_current_time(timezone: str = "UTC") -> str: ...
def read_file(path: str) -> str:
# constrain to the working directory; return a clear error string
# (not an exception) when the file doesn't exist
...
# agent.py — the loop
def run_turn(messages, budget):
while True:
resp = call_with_retries(lambda: client.messages.create(
model=MODEL, max_tokens=1024, tools=SCHEMAS, messages=messages))
budget.add(resp.usage) # track every call
if resp.stop_reason != "tool_use":
return resp
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": execute_all(resp.content)})is_error: true) so the model can recover; the calculator must not eval() arbitrary input; the loop needs a max-iteration guard so a confused model can't spin forever.Ship it to your portfolio
This lab is the first artifact in your portfolio, and hiring managers routinely look at a candidate's GitHub before the résumé — 2–3 deep, evaluated projects beat a pile of shallow demos. Package it so a reviewer gets the signal in one minute:
- README with a 60-second demo: a GIF or asciinema recording of the multi-step question (
read_file→calculator→ answer) plus copy-paste run instructions. Reviewers rarely clone the repo — the demo IS the first impression. - Reported numbers: pass count from the
test_agent.pysuite you wrote, tokens + estimated cost for a typical session, and observed retry behavior under a simulated 429. Real measured numbers signal you ran and verified it, not just wrote it. - An honest "Limitations" section: e.g. what expressions the calculator can't parse, the single-directory file sandbox, no conversation persistence across runs. Stated limitations read as engineering judgment, not weakness — and their absence is what reviewers notice.
- A trace or screenshot of it working: one full multi-tool transcript (assistant tool call → tool result → final answer, with the token/cost report) so a reviewer can verify the loop without running anything.
- ◇Stream the final answer token-by-token while still handling tool-use turns
- ◇Add prompt caching with a cache breakpoint after the system prompt + tools, and log cache-read savings
- ◇Add a --think flag that enables adaptive thinking (thinking={'type': 'adaptive'}) and prints the model's reasoning summary before the final answer
- ◇Practical test: re-implement the minimal one-tool loop from memory in under 30 minutes
Be honest — the gates only mean something if the criteria really pass.
Repo URL, demo link, notes to your future self — saved locally with your progress, and handy when you package the portfolio.