Build a CLI that connects to an LLM and answers questions. This is the foundation for the agent you will build in the next tasks.
A Python CLI program (agent.py) that takes a question, sends it to an LLM, and returns a structured JSON answer. No tools or agentic loop yet — just the basic plumbing: parse input, call the LLM, format output. You will add tools and the agentic loop in Tasks 2–3.
User question → agent.py → LLM API → JSON answer
Input — a question as the first command-line argument:
uv run agent.py "What does REST stand for?"Output — a single JSON line to stdout:
{"answer": "Representational State Transfer.", "tool_calls": []}Rules:
answerandtool_callsfields are required in the output.tool_callsis an empty array for this task (you will populate it in Task 2).- Only valid JSON goes to stdout. All debug/progress output goes to stderr.
- The agent must respond within 60 seconds.
- Exit code 0 on success.
Your agent needs an LLM that supports the OpenAI-compatible chat completions API. You are free to use any provider.
Recommended: Set up the Qwen Code API on your VM
Qwen Code provides 1000 free requests per day, works from Russia, and requires no credit card.
Follow the setup instructions to deploy it on your VM.
| Model | Tool calling | Notes |
|---|---|---|
qwen3-coder-plus |
Strong | Recommended, default in .env.agent.example |
coder-model |
Strong | Qwen 3.5 Plus |
Alternative: OpenRouter (click to open)
OpenRouter offers free models with no credit card required.
| Model | Tool calling | Notes |
|---|---|---|
meta-llama/llama-3.3-70b-instruct:free |
Strong | Good alternative |
qwen/qwen3-coder:free |
Good | Alternative |
[!WARNING] OpenRouter free-tier limitations:
- Free models have a 50 requests per day limit per account.
- Free models can be temporarily unavailable due to upstream provider load (
429errors).- The autochecker runs 10 questions against your agent — free-tier rate limits may cause failures.
- If you use OpenRouter, plan your testing carefully: use
run_eval.py --index Nto test one question at a time.
Create the agent environment file:
cp .env.agent.example .env.agent.secretEdit .env.agent.secret and fill in LLM_API_KEY, LLM_API_BASE, and LLM_MODEL. Your agent reads from this file.
Note: This is not the same as
LMS_API_KEYin.env.docker.secret. That one protects your backend LMS endpoints.LLM_API_KEYauthenticates with your LLM provider.
Before writing code, create plans/task-1.md. Describe which LLM provider and model you will use, and how you will structure the agent.
Create agent.py in the project root. The system prompt can be minimal for now — you will expand it in later tasks when you add tools and domain knowledge.
Create AGENT.md in the project root documenting how the agent works, which LLM provider you chose, and how to run it.
Create 1 regression test that runs agent.py as a subprocess, parses the stdout JSON, and checks that answer and tool_calls are present.
-
plans/task-1.mdexists with the implementation plan (committed before code). -
agent.pyexists in the project root. -
uv run agent.py "..."outputs valid JSON withanswerandtool_calls. - The API key is stored in
.env.agent.secret(not hardcoded). -
AGENT.mddocuments the solution architecture. - 1 regression test exists and passes.
- Git workflow: issue
[Task] Call an LLM from Code, branch, PR withCloses #..., partner approval, merge.