Calling a model can be as simple as sending an HTTP request. We'll examine one closely, read the JSON response, and then build up a conversation across several calls. We'll also compare the local request with a hosted API and identify the sampling controls, output limit, and response fields that the harness uses.
Before we send anything, a word about the mechanism. An HTTP request is a message from a client to a server, addressed to a URL. It carries a method, such as GET to fetch something or POST to send something, a few headers that describe the message, and usually a body. The body is where the content goes. For almost every web API it is JSON, a plain-text format of named fields inside braces, which is easy for programs to write and to read.
The server answers with a response. It has a status code, two hundred when all went well, and a body of its own, again JSON. One request and its response make one API call.
Exposing a program this way, as a set of URLs that accept and return JSON over HTTP, is called a REST API. It is the standard way services are made available over the internet, and a model server is one more service of that kind. We are only showing the general shape of these messages here. Whenever you need to construct one yourself, you can of course consult your agent as needed. The idea is to take a deeper look at what the harness and the model actually exchange.
Curl sends HTTP requests from the terminal and prints the response. It is available on Linux, macOS, and Windows. Here we use it to send the contents of a JSON file to Ollama. The request is a POST to the local server's chat endpoint. Its body names the model and provides a messages array, with a role and content for each message. This first request contains one user question. Stream is false, so the response comes back as one JSON object.
The response contains an assistant message whose content is Paris. It also includes the model name and fields describing completion of the generation, which we'll return to near the end.
Our first request asks for the capital of France in one word. The response is Paris, as expected. Remember that both the question and the reply are available to the program that made this call.
The second request sends only "And of Italy?". The saved response talks about Italian wine. The earlier question about capitals is absent from this request, so the model must interpret the new text without it.
The server does not hold the conversation state. It keeps nothing from the first call, so the second call arrives with no earlier messages. The harness keeps the conversation instead. With every call, it sends the entire history again in the request.
Let's construct that history explicitly. The first messages array has one entry, the user question. The server returns the assistant's answer, Paris.
The second call sends three messages: the original question, the assistant's answer, and the new question about Italy. The previous answer is now part of the input. With that context, this run replies Rome.
The third call sends five messages, including both earlier question-and-answer pairs and the new question about which city is further north. The model answers Paris. Each call includes the accumulated transcript, so the request grows as the conversation continues.
The same basic structure appears in hosted APIs. On the left is our local Ollama request, with a model name and a list of messages containing roles and content.
On the right is an illustrative Anthropic Messages request. It has a hosted URL, an API-key header, a version header, and additional body fields. The output limit here is a thousand and twenty-four tokens, and the example also includes system instructions and tool definitions. The request is not byte-for-byte interchangeable with Ollama's. The useful similarity is that it supplies instructions and conversation messages for the model to continue.
This is the request-building part of the harness. It collects the system instructions, relevant conversation history, and tool definitions, then sends them in the provider's format. Other harness code handles the reply, permissions, and tool execution before the next call.
Several controls can affect generation. Temperature changes the sampling distribution. Top-p and top-k restrict which tokens are considered. The maximum-token setting limits output length. A seed controls the random-number sequence where supported, helping repeat an experiment when the rest of the setup stays fixed.
Availability, field names, and defaults depend on the API and model. Ollama exposes these controls through its options, while a hosted model may support only some of them. Even with a seed, do not assume identical results across different hardware or serving versions.
The response also tells the calling program what happened during generation. Two useful kinds of information are the reason generation stopped and the token counts. A stop reason can indicate completion, a request for tool execution, or an output limit being reached. Input and output token counts help measure usage and, for a billed API, understand the charge. Local inference still uses compute, but it does not incur that provider's per-token API bill.
The harness uses the stop reason together with the response content to decide how to continue. If the model requests a tool, the harness checks the requested operation, runs it when permitted, and includes the result in the next call.
You can now assemble a call to the model yourself, and explain why the follow-up needs its history. In these examples, the conversation is the list of messages we send, rather than a conversation automatically recovered by the server.
The local and hosted examples use the same idea, with provider-specific endpoints, authentication, and body fields. Reading those fields makes the request understandable without treating the API as a hidden conversation service. The harness automates this work throughout the agent loop. Examining the request structure gives you a way to recognize what it sends and interpret what comes back.
See you in the next video.