{"id":169,"date":"2026-09-17T08:00:55","date_gmt":"2026-09-17T08:00:55","guid":{"rendered":"https:\/\/www.dobryakov.net\/blog\/169\/"},"modified":"2026-09-17T08:00:55","modified_gmt":"2026-09-17T08:00:55","slug":"llm-interaction-layers","status":"publish","type":"post","link":"https:\/\/www.dobryakov.net\/blog\/169\/","title":{"rendered":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer"},"content":{"rendered":"<p>The same prompt can be sent to a model in four ways: typed into a web chat, handed to a console agent, called from code via an SDK, or assembled as a raw HTTP request. The response will look similar. The system built around that response will differ wildly in latency, cost, predictability, and security \u2014 and that difference lives not in the model, but in the layer you use to reach it.<\/p>\n<p><!--more--><\/p>\n<p>Do not choose this by convenience. &quot;I prefer the chat interface&quot; and &quot;this service needs an SDK&quot; are statements from different planes. The first is about how you want to work tonight. The second is about whether your production pipeline will be reproducible, cheap, and observable six months from now. When engineers confuse these two questions, they get a system that works in a demo and leaks under load: unpredictable response formats, tokens paid for that could have been cached, and no way to tell which step of generation went wrong.<\/p>\n<p>Here is a breakdown of all four layers: how each works internally, how the ecosystems (Anthropic\/Claude, OpenAI, Google\/Gemini) differ, and which layer actually fits which engineering task. I walk this staircase every day: my own content pipeline calls models over HTTP with a routing rule \u2014 simple goes to the cheap model, complex goes to the expensive one. I look at &quot;chat vs. API&quot; from operations, not from documentation.<\/p>\n<h2>Web Chat Is a Product, Not a Model<\/h2>\n<p>Start with what everyone has seen. A web chat is not &quot;access to a neural network.&quot; It is a finished product application sitting on top of the model. Between your text and the model stands an orchestrator that handles a whole load of tasks you never think about:<\/p>\n<ul>\n<li><strong>Indexing and RAG.<\/strong> Upload a document, and the interface automatically splits it into chunks, runs embeddings, and stores them in a temporary vector store for contextual retrieval.<\/li>\n<li><strong>Context window management.<\/strong> As the dialogue grows, the orchestrator summarizes or truncates old messages on the fly to avoid hitting the model&#x27;s context limit. You do not see this.<\/li>\n<li><strong>Specialized rendering.<\/strong> The text stream is separated from structured entities. In Claude AI, these are Artifacts \u2014 separate rendering processes for code, SVG, Mermaid diagrams, or entire HTML applications. In ChatGPT, the Canvas editor plays the same role.<\/li>\n<li><strong>Preset system instructions.<\/strong> Modes like Projects (Claude) or Custom GPTs (OpenAI) are not &quot;a different model.&quot; They are a mechanism that injects your system prompt into every outgoing HTTP request. Attached files are either placed into context wholesale or pulled via retrieval (RAG) depending on their size.<\/li>\n<\/ul>\n<p>Chat is a convenient wrapper that takes over all the life support around the model. That is exactly what makes it good for some tasks and useless for others.<\/p>\n<p>Where chat fits well: ad-hoc exploration, prototyping, working with unstructured documentation, one-off scripts. From there, the ecosystems diverge:<\/p>\n<ul>\n<li><strong>Claude AI (Claude.ai)<\/strong> \u2014 long texts, analyzing large codebases (through the deep integration of Projects and Artifacts), complex technical documentation.<\/li>\n<li><strong>ChatGPT<\/strong> \u2014 when you need out-of-the-box multimodality (image generation, voice) or fast web search for current data.<\/li>\n<li><strong>Gemini<\/strong> \u2014 analyzing ultra-long media (hours of video, long audio) and tight integration with Google Drive.<\/li>\n<\/ul>\n<p>The key property of chat to carry forward: context management here is automatic and hidden, and determinism is low. You do not control what exactly went into the model or what was cut from history. For exploration, that is a plus. For a system that must be reproducible, it is a disqualifier.<\/p>\n<h2>CLI: The Model Leaves the Chat and Enters the OS<\/h2>\n<p>The next layer changes the very role of the model. In chat, it is an interlocutor in a sandbox. In CLI, it becomes an autonomous agent living in your OS: reading and writing files, running shell commands, accessing external services.<\/p>\n<pre><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502                      CLI Agent                          \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502\n\u2502  \u2502 Read\/Write Files \u2502  \u2502 Execute Shell Commands      \u2502  \u2502\n\u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n             \u2502                          \u2502\n             \u25bc                          \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502              Model Context Protocol (MCP)               \u2502\n\u2502        (Database, Git, Internal Services, Tools)        \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n<p>Internally, everything rests on the Agent Loop:<\/p>\n<ol>\n<li>The model receives a task, looks at the current context and local environment.<\/li>\n<li>It returns a response with a function call (<code>tool_use<\/code>) \u2014 for example, &quot;read file <code>src\/main.py<\/code>&quot; or &quot;run <code>npm test<\/code>.&quot;<\/li>\n<li>The CLI client intercepts this response, executes the command locally in the OS, and sends the result (<code>tool_result<\/code>) back to the model.<\/li>\n<li>The cycle repeats until the task is closed.<\/li>\n<\/ol>\n<p>The difference between systems here is the degree of protocol integration. Claude Code and the Anthropic ecosystem rely on the <strong>Model Context Protocol (MCP)<\/strong> \u2014 an open standard that lets a CLI agent uniformly connect to local and remote sources: Git repositories, databases, bug trackers. Additionally, Claude models in CLI emit thinking structures (<strong>thinking tokens<\/strong>) \u2014 showing intermediate logic steps before modifying code. For an engineer, this is the ability to intercept a wrong intent before it turns into a file edit.<\/p>\n<p>Outside the Claude ecosystem, the picture is different: engineers typically use third-party provider-agnostic agents (Aider, OpenHands \u2014 which also work with Claude) or a local runtime for open-source models (Ollama).<\/p>\n<p>Where CLI fits the task: automated refactoring and bug hunting across an entire repository, generating and running database migrations, automating CI\/CD, and writing integration tests. Context management here is hybrid: partly automatic, partly yours. Determinism is medium. It is the workhorse of local development, but not yet what you should build a backend on.<\/p>\n<h2>SDK: Where Production Begins<\/h2>\n<p>Here is the boundary between &quot;playing with a model&quot; and &quot;embedding a model into a service.&quot; An SDK is an abstraction over the network protocol, providing strictly typed interfaces for Python, TypeScript, Go, or Java. Context management is now fully manual: what you put in the request is what goes out \u2014 no hidden orchestrator. And it is at this level that providers diverge on three crucial mechanics.<\/p>\n<p><strong>Prompt Caching.<\/strong> In the Anthropic SDK, the developer manually marks static blocks \u2014 a massive system instruction, a specification, a chunk of a codebase \u2014 with the <code>cache_control: {&quot;type&quot;: &quot;ephemeral&quot;}<\/code> structure. On repeated calls, this cuts token cost by up to 90% and latency by up to 85% on long prompts. In the OpenAI SDK, a similar mechanism works mostly automatically on the server side (implicit caching), without explicit markup in the code.<\/p>\n<pre><code class=\"language-python\"># Example of explicit Prompt Caching markup in the Anthropic SDK\nimport anthropic\n\nclient = anthropic.Anthropic()\n\nresponse = client.messages.create(\n    model=&quot;claude-3-7-sonnet-20250219&quot;,\n    max_tokens=1024,\n    system=[\n        {\n            &quot;type&quot;: &quot;text&quot;,\n            &quot;text&quot;: &quot;Large system context or API specification...&quot;,\n            &quot;cache_control&quot;: {&quot;type&quot;: &quot;ephemeral&quot;} # Caching instruction\n        }\n    ],\n    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Generate a controller based on the specification&quot;}]\n)<\/code><\/pre>\n<p><strong>Reasoning \/ Extended Thinking.<\/strong> On reasoning-class models, the SDK provides parameters to control how much the model thinks before answering. It is telling how quickly this knob evolves: on the Claude 3.7 Sonnet generation, the thinking budget was set strictly and explicitly \u2014 <code>thinking: {&quot;type&quot;: &quot;enabled&quot;, &quot;budget_tokens&quot;: 2048}<\/code>. On current Anthropic generations, this form is already displaced: <code>budget_tokens<\/code> is rejected, and depth is set by adaptive thinking (<code>thinking: {&quot;type&quot;: &quot;adaptive&quot;}<\/code>) paired with an effort level (<code>effort<\/code>) \u2014 the model decides when and how much to think. The intent remains the same \u2014 balancing response time against quality \u2014 but the engineer now controls it declaratively rather than with a fixed token count.<\/p>\n<p><strong>Structured Outputs.<\/strong> The OpenAI SDK integrates natively with Pydantic and guarantees JSON schema compliance at the token decoding level (Guaranteed Structured Outputs). Anthropic today has a native output schema at the API level (<code>output_config.format<\/code> and\/or strict <code>strict: true<\/code> on a tool); historically, the same result was achieved indirectly \u2014 through strict tool use (<code>tool_use<\/code>) and response parsing. Both providers arrived at guaranteed schemas, but by different paths.<\/p>\n<p>Put this together, and it is clear why the SDK becomes the primary tool for production systems: microservices, backend logic, specialized RAG, autonomous business agents. Manual context, explicit cache, managed reasoning, and a predictable response format \u2014 this is exactly the set that chat does not give you by design. When I say &quot;for production \u2014 SDK or HTTP, not chat,&quot; it is not a matter of taste: determinism, cache, and explicit routing between models either exist at this level or they do not exist anywhere above it.<\/p>\n<h2>Raw HTTP: Removing the Last Dependency<\/h2>\n<p>The lowest layer is direct calls over HTTP\/HTTPS without any libraries. Here you speak to the provider&#x27;s API in the language of headers and JSON bodies, and format differences surface into plain view.<\/p>\n<pre><code>[ HTTP REST API ]\n  \u251c\u2500\u2500 Anthropic API: POST \/v1\/messages\n  \u2502    \u251c\u2500\u2500 Header: anthropic-version: 2023-06-01\n  \u2502    \u251c\u2500\u2500 Header: anthropic-beta: prompt-caching-2024-07-25\n  \u2502    \u2514\u2500\u2500 JSON Body: { &quot;system&quot;: &quot;...&quot;, &quot;messages&quot;: [...] }\n  \u2502\n  \u2514\u2500\u2500 OpenAI API Standard: POST \/v1\/chat\/completions\n       \u2514\u2500\u2500 JSON Body: { &quot;messages&quot;: [ {&quot;role&quot;: &quot;system&quot;, ...}, {&quot;role&quot;: &quot;user&quot;, ...} ] }<\/code><\/pre>\n<p><strong>JSON payload structure.<\/strong> In the Anthropic Messages API (<code>\/v1\/messages<\/code>), the system prompt is extracted into a separate top-level <code>system<\/code> parameter, and the <code>messages<\/code> array contains only <code>user<\/code> and <code>assistant<\/code> roles. In OpenAI (<code>\/v1\/chat\/completions<\/code>), the system instruction is a regular element of the <code>messages<\/code> array with the <code>system<\/code> role (<code>{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;...&quot;}<\/code>). The OpenAI format has become the de facto industry standard: local servers (vLLM, Ollama) and most open-source model providers (Groq, Together AI, DeepSeek) replicate it. In practice, this means the &quot;OpenAI format + custom router&quot; combination lets you keep different vendors behind a single interface \u2014 making this scheme work for cost balancing.<\/p>\n<p><strong>Versioning and experimental features.<\/strong> Anthropic uses a hard <code>anthropic-version<\/code> header and an <code>anthropic-beta<\/code> header to enable capabilities while they are in beta. The example below shows the beta header format on a historical feature: <code>anthropic-beta: prompt-caching-2024-07-25<\/code>. An important caveat for today: prompt caching has long been in GA, and this beta header is no longer needed for it (just use <code>cache_control<\/code> in the body). <code>anthropic-beta<\/code> remains the mechanism for features that are not yet in GA:<\/p>\n<pre><code class=\"language-bash\">curl https:\/\/api.anthropic.com\/v1\/messages \\\n     --header &quot;x-api-key: $ANTHROPIC_API_KEY&quot; \\\n     --header &quot;anthropic-version: 2023-06-01&quot; \\\n     --header &quot;anthropic-beta: prompt-caching-2024-07-25&quot; \\\n     --header &quot;content-type: application\/json&quot; \\\n     --data &#x27;{\n       &quot;model&quot;: &quot;claude-3-7-sonnet-20250219&quot;,\n       &quot;max_tokens&quot;: 1024,\n       &quot;system&quot;: &quot;You are a professional system architect.&quot;,\n       &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Analyze system design.&quot;}]\n     }&#x27;<\/code><\/pre>\n<p><strong>Server-Sent Events.<\/strong> With <code>stream: true<\/code>, Anthropic returns a strictly decomposed stream of SSE events:<\/p>\n<ul>\n<li><code>message_start<\/code> \u2014 initializes the response structure and counts input tokens.<\/li>\n<li><code>content_block_start<\/code> \u2014 begins a text block or a thinking block.<\/li>\n<li><code>content_block_delta<\/code> \u2014 incremental chunks (<code>text_delta<\/code> or <code>thinking_delta<\/code>).<\/li>\n<li><code>content_block_stop<\/code> \u2014 closes the current block.<\/li>\n<li><code>message_delta<\/code> \u2014 final message metadata: <code>stop_reason<\/code> and the <code>usage<\/code> aggregate (including output tokens).<\/li>\n<li><code>message_stop<\/code> \u2014 end-of-stream marker.<\/li>\n<\/ul>\n<p>This granularity lets you see exactly what stage generation is at: reasoning, forming the answer, or calling an external tool. For an API gateway that logs and proxies third-party requests, this is not decoration; it is necessary observability.<\/p>\n<p>Where HTTP fits the task: development in languages without official SDKs (Rust, C++, Elixir, Swift), building high-performance API gateways where you need to strip the overhead of external dependencies, and fine-tuning load balancing, proxying, and network request logging. Context management is fully manual, and control over the request is absolute: not a single hidden decision stands between you and the model. The engineering caveat is honest: &quot;full control&quot; refers to the request and pipeline reproducibility, not the model&#x27;s output. The LLM response itself is nondeterministic even at <code>temperature=0<\/code> (batching and hardware cause drift), and at the payload level, the SDK and raw HTTP send identical requests \u2014 the difference between them is dependencies and overhead, not &quot;predictability&quot; of generation. This is both the price and the meaning of the layer: you are responsible for everything yourself.<\/p>\n<h2>The Difference Is the Layer, Not the Model<\/h2>\n<p>Let us assemble the four levels into one table \u2014 this is the map for choosing:<\/p>\n<table>\n<thead>\n<tr>\n<th>Criterion<\/th>\n<th>Web Chat (UI)<\/th>\n<th>Console (CLI)<\/th>\n<th>SDK<\/th>\n<th>Raw HTTP API<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Abstraction level<\/td>\n<td>Highest (finished product)<\/td>\n<td>High (agent environment)<\/td>\n<td>Medium (programmatic)<\/td>\n<td>Low (network protocol)<\/td>\n<\/tr>\n<tr>\n<td>Context management<\/td>\n<td>Automatic (hidden)<\/td>\n<td>Automatic\/manual<\/td>\n<td>Fully manual<\/td>\n<td>Fully manual<\/td>\n<\/tr>\n<tr>\n<td>Request control \/ pipeline reproducibility<\/td>\n<td>Low<\/td>\n<td>Medium<\/td>\n<td>Full<\/td>\n<td>Full<\/td>\n<\/tr>\n<tr>\n<td>Primary use case<\/td>\n<td>Exploration, ad-hoc tasks<\/td>\n<td>Local dev, CI\/CD<\/td>\n<td>Production backend services<\/td>\n<td>API gateways, rare stacks<\/td>\n<\/tr>\n<tr>\n<td>Key Claude focus<\/td>\n<td>Artifacts, Projects<\/td>\n<td>Claude Code, MCP, Thinking<\/td>\n<td>Manual Prompt Caching<\/td>\n<td>Headers (anthropic-beta), \/v1\/messages<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Read the table along one axis: left to right, control over the request increases, and magic decreases. Chat takes over context, cache, and rendering \u2014 you get speed of entry at the cost of control. HTTP takes over nothing \u2014 you get full control at the cost of all life support now being your problem. The SDK is the sensible middle for most production tasks: manual context and explicit cache without the need to manually assemble headers and parse SSE.<\/p>\n<p>The choice is:, and it is about matching the layer to the task:<\/p>\n<ul>\n<li><strong>Exploration, prototype, one-off document analysis<\/strong> \u2014 chat. Do not build an SDK for what a browser tab solves.<\/li>\n<li><strong>Local development, repository refactoring, CI\/CD<\/strong> \u2014 CLI with an agent loop and MCP.<\/li>\n<li><strong>Backend service, RAG, autonomous agent under load<\/strong> \u2014 SDK: manual context, prompt caching, managed reasoning, predictable format.<\/li>\n<li><strong>Gateway, exotic stack, maximum control over network and cost<\/strong> \u2014 raw HTTP.<\/li>\n<\/ul>\n<p>When a pipeline operates at volume, the model stops being an &quot;interlocutor&quot; and becomes a conveyor component called from code: mass runs go through a cheap, fast model; quality-critical steps go through a stronger, more expensive one. This &quot;cheap \/ expensive&quot; routing and balancing between vendors lives only on the SDK and HTTP layers \u2014 where context, cache, and model selection are in your hands, not hidden in a chat orchestrator. Choose the access layer first.<\/p>\n<p>https:\/\/www.dobryakov.net\/lead-magnets\/llm-interaction-layers.html?utm_source=None&#038;utm_medium=None&#038;utm_campaign=llm-interaction-layers<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The same LLM behaves differently behind four interfaces. The difference is not the model \u2014 it&#8217;s the orchestration around it.<\/p>\n","protected":false},"author":0,"featured_media":168,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-169","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.dobryakov.net\/blog\/169\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Grigoriy Dobryakov - IT+AI Blog - Grigoriy Dobryakov&#039;s blog: management, development and testing\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer\" \/>\n\t\t<meta property=\"og:description\" content=\"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.dobryakov.net\/blog\/169\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-09-17T08:00:55+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-09-17T08:00:55+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer\" \/>\n\t\t<meta name=\"twitter:description\" content=\"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#blogposting\",\"name\":\"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer\",\"headline\":\"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\",\"author\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/author\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/llm-interaction-layers.jpg\",\"width\":1200,\"height\":630,\"caption\":\"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\"},\"datePublished\":\"2026-09-17T08:00:55+00:00\",\"dateModified\":\"2026-09-17T08:00:55+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#webpage\"},\"articleSection\":\"Uncategorized\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/category\\\/uncategorized\\\/#listItem\",\"name\":\"Uncategorized\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/category\\\/uncategorized\\\/#listItem\",\"position\":2,\"name\":\"Uncategorized\",\"item\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/category\\\/uncategorized\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#listItem\",\"name\":\"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#listItem\",\"position\":3,\"name\":\"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/category\\\/uncategorized\\\/#listItem\",\"name\":\"Uncategorized\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/#organization\",\"name\":\"Grigoriy Dobryakov - IT+AI Blog\",\"description\":\"Grigoriy Dobryakov's blog: management, development and testing\",\"url\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#webpage\",\"url\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/\",\"name\":\"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer\",\"description\":\"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \\u2014 context, caching, routing. An engineering breakdown.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/author\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/author\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/llm-interaction-layers.jpg\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/169\\\/#mainImage\"},\"datePublished\":\"2026-09-17T08:00:55+00:00\",\"dateModified\":\"2026-09-17T08:00:55+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/\",\"name\":\"Grigoriy Dobryakov - IT+AI Blog\",\"description\":\"Grigoriy Dobryakov's blog: management, development and testing\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.dobryakov.net\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","canonical_url":"https:\/\/www.dobryakov.net\/blog\/169\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#blogposting","name":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","headline":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer","author":{"@id":"https:\/\/www.dobryakov.net\/blog\/author\/#author"},"publisher":{"@id":"https:\/\/www.dobryakov.net\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/www.dobryakov.net\/blog\/wp-content\/uploads\/2026\/09\/llm-interaction-layers.jpg","width":1200,"height":630,"caption":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer"},"datePublished":"2026-09-17T08:00:55+00:00","dateModified":"2026-09-17T08:00:55+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.dobryakov.net\/blog\/169\/#webpage"},"isPartOf":{"@id":"https:\/\/www.dobryakov.net\/blog\/169\/#webpage"},"articleSection":"Uncategorized"},{"@type":"BreadcrumbList","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.dobryakov.net\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/#listItem","name":"Uncategorized"}},{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/#listItem","position":2,"name":"Uncategorized","item":"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#listItem","name":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#listItem","position":3,"name":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer","previousItem":{"@type":"ListItem","@id":"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/#listItem","name":"Uncategorized"}}]},{"@type":"Organization","@id":"https:\/\/www.dobryakov.net\/blog\/#organization","name":"Grigoriy Dobryakov - IT+AI Blog","description":"Grigoriy Dobryakov's blog: management, development and testing","url":"https:\/\/www.dobryakov.net\/blog\/"},{"@type":"WebPage","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#webpage","url":"https:\/\/www.dobryakov.net\/blog\/169\/","name":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.dobryakov.net\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.dobryakov.net\/blog\/169\/#breadcrumblist"},"author":{"@id":"https:\/\/www.dobryakov.net\/blog\/author\/#author"},"creator":{"@id":"https:\/\/www.dobryakov.net\/blog\/author\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/www.dobryakov.net\/blog\/wp-content\/uploads\/2026\/09\/llm-interaction-layers.jpg","@id":"https:\/\/www.dobryakov.net\/blog\/169\/#mainImage","width":1200,"height":630,"caption":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer"},"primaryImageOfPage":{"@id":"https:\/\/www.dobryakov.net\/blog\/169\/#mainImage"},"datePublished":"2026-09-17T08:00:55+00:00","dateModified":"2026-09-17T08:00:55+00:00"},{"@type":"WebSite","@id":"https:\/\/www.dobryakov.net\/blog\/#website","url":"https:\/\/www.dobryakov.net\/blog\/","name":"Grigoriy Dobryakov - IT+AI Blog","description":"Grigoriy Dobryakov's blog: management, development and testing","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.dobryakov.net\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Grigoriy Dobryakov - IT+AI Blog - Grigoriy Dobryakov's blog: management, development and testing","og:type":"article","og:title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","og:description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","og:url":"https:\/\/www.dobryakov.net\/blog\/169\/","article:published_time":"2026-09-17T08:00:55+00:00","article:modified_time":"2026-09-17T08:00:55+00:00","twitter:card":"summary_large_image","twitter:title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","twitter:description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown."},"aioseo_meta_data":{"post_id":"169","title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","og_description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":"Chat, CLI, SDK, or Raw HTTP: Choosing Your LLM Access Layer","twitter_description":"The same LLM behaves differently behind chat, CLI, SDK, and HTTP. The difference is orchestration \u2014 context, caching, routing. An engineering breakdown.","schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-09-17 08:01:00","updated":"2026-09-17 08:01:00"},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.dobryakov.net\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/\" title=\"Uncategorized\">Uncategorized<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tChat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.dobryakov.net\/blog"},{"label":"Uncategorized","link":"https:\/\/www.dobryakov.net\/blog\/category\/uncategorized\/"},{"label":"Chat, CLI, SDK, or Raw HTTP: Choosing the Right LLM Access Layer","link":"https:\/\/www.dobryakov.net\/blog\/169\/"}],"_links":{"self":[{"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/posts\/169","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/comments?post=169"}],"version-history":[{"count":0,"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/posts\/169\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/media\/168"}],"wp:attachment":[{"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/media?parent=169"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/categories?post=169"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dobryakov.net\/blog\/wp-json\/wp\/v2\/tags?post=169"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}