How I replaced LLM calls with coding agent calls and saved money A developer proposes replacing LLM API calls with coding agent calls to reduce costs, citing that coding assistants like Codex, Cursor Pro, and GitHub Copilot Pro cost significantly less per million tokens than traditional LLM APIs. The developer provides a Python wrapper to integrate Claude Code CLI as a drop-in replacement for LLM calls, enabling the use of cheaper agent-based inference for building AI agents. When building an AI agent, you need LLM calls. It can be done via a remote API or a local API, but either way you need to do it. Whether the agent is a simple conversational agent or a ReAct agent with a bunch of tools, whether it's using a complex graph or a simple RAG, it must be based on the concept of sending prompts to the language model. But, what if we replace the language model with... another agent? Let's say we already have a smart agent with a bunch of tools that can handle complex problems. Why not use it to build a new agent on top of it? This way we can focus on the specific custom functionality we want to achieve, while already having the common functionalities covered by the underlying agent. You might think this must be expensive. You get a better performance, so you have to pay for it, right? Well, not necessarily. The catch is that the coding assistants are actually surprisingly cheap when compared to API prices. They offer much more than raw LLM calls, they offer amazing agent functionality, but the cost is actually lower, and it's not a small difference. The cost of LLM calls per 1M tokens is usually between $2 and $7. For coding assistant subscriptions, it's a bit more tricky to calculate because you pay for monthly subscription, but it can be still converted to per 1M tokens cost, and from what LLM just told me it is around $0.08 to $2. That's a huge difference And the complex agents are cheaper than raw LLM's according to ChatGPT: | Service | Cost per 1M tokens | |---|---| | Codex / ChatGPT coding plan | ~$0.08 | | Cursor Pro | ~$0.08–0.25 | | GitHub Copilot Pro | ~$0.10–0.30 | | Claude Pro / Claude Code | ~$0.74 | | GPT-5 API | ~$2.1 | | Claude Sonnet API | ~$4.2 | | Claude Opus API | ~$7.0 | according to Claude: | Service | Cost per 1M tokens | |---|---| | Haiku 4.5 API | $1.80 | | Sonnet 5 API, intro thru Aug 31 '26 | $3.60 | | Sonnet 5 API, standard | $5.40 | | Opus 4.8 API | $9.00 | | Fable 5 API | $18.00 | | Claude Code — Pro | ~$1.10–$2.15 est. | | Claude Code — Max 5x | ~$2.10–$4.15 est. | | Claude Code — Max 20x | ~$2–$4 est. | So, why not use the coding agents to build new agents? The only problem is that they don't have API. But, this is a problem that can be solved by writing a simple wrapper to the agent that will communicate with it via CLI. We just have to install the agent on the machine from which we want to call it and use the wrapper. Simple and brilliant. Here's the code: """Claude Code CLI — complete integration module. Provides plain-text and structured LLM calls via the local claude binary, plus a LangChain-compatible adapter for use with make llm . Requires the claude binary on PATH. """ import json import subprocess from typing import Generic, TypeVar from langchain core.messages import HumanMessage, SystemMessage from pydantic import BaseModel T = TypeVar "T", bound=BaseModel TIMEOUT = 120 ── Core subprocess layer ───────────────────────────────────────────────────── def cli call system: str, user: str - str: """Run a plain-text prompt through the Claude CLI and return the response.""" return run system=system, user=user def cli call structured system: str, user: str, output type: type T - T: """Run a structured prompt through the Claude CLI and parse into output type.""" schema = json.dumps output type.model json schema raw = run system=system, user=user, json schema=schema return output type.model validate json raw def run system: str, user: str, json schema: str | None = None - str: cmd = "claude", "--print", "--no-session-persistence", "--tools", "", if system: cmd += "--system-prompt", system if json schema: cmd += "--output-format", "json", "--json-schema", json schema cmd.append user result = subprocess.run cmd, capture output=True, text=True, encoding="utf-8", timeout= TIMEOUT if result.returncode = 0: stderr = result.stderr or "" .strip raise RuntimeError f"claude CLI exited {result.returncode}: {stderr}" stdout = result.stdout or "" .strip if not stdout: raise RuntimeError "claude CLI returned empty output" if json schema: envelope = json.loads stdout payload = envelope.get "result", stdout return payload if isinstance payload, str else json.dumps payload return stdout ── LangChain-compatible adapter ────────────────────────────────────────────── class CliResponse: def init self, content: str - None: self.content = content class ClaudeCliStructured Generic T : def init self, output type: type T - None: self. type = output type def invoke self, messages: list - T: system, user = extract messages messages return cli call structured system=system, user=user, output type=self. type class ClaudeCli: """LangChain-compatible adapter that delegates to the claude CLI binary.""" def invoke self, messages: list - CliResponse: system, user = extract messages messages return CliResponse content=cli call system=system, user=user def with structured output self, output type: type T - ClaudeCliStructured T : return ClaudeCliStructured output type def extract messages messages: list - tuple str, str : system = "" user = "" for m in messages: if isinstance m, SystemMessage : system = m.content if isinstance m.content, str else "" elif isinstance m, HumanMessage : user = m.content if isinstance m.content, str else "" return system, user