> ## Documentation Index
> Fetch the complete documentation index at: https://www.sapientpriors.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Answering

> The three ways to ask an agent a question. Which one to use, what the response contains, and every error with what to do.

This page explains the three calls that answer a question. After you read it, you can choose the right call for your product, read its response, and handle every error.

Every answering call does two things, in this order:

1. It learns from the message you send, in the same way as [`observe`](/docs/concepts/learning).
2. It searches the agent's memory for the memories that relate to the message.

The three calls differ in what they do after the search. The table below compares them.

| Call | What it does after the search | Use it when |
| - | - | - |
| `observe-and-enrich` | Returns the related memories as text. | You have your own language model. Priors gives it the memory. |
| `observe-and-respond` | Writes an answer from the memories. | You want Priors to write the answer. |
| `chat` and `chat/stream` | Writes an answer, saves the message and the answer in the thread, and points to the files it used. | You build a chat screen. |

<Warning>
  Every answering call learns from the message you send. A question becomes a memory. Do not send a question that the agent must not remember.
</Warning>

## Answer length: `detail`

Every answering call takes `detail`. It tells the agent how much to write. On `observe-and-enrich` and `observe-and-respond` it sets the length of `response_context`. On `chat` and `chat/stream` it sets the length of the answer.

| `detail` | What the agent writes | Use it for |
| - | - | - |
| `brief` | Only the most important facts, in 2 to 4 sentences. On `chat`, a short answer that still includes any code or list the question needs. | A chat widget, a notification, an SMS, a quick lookup ("What plan is Acme on?"), or a model of your own that needs only the key facts. |
| `standard` | The normal length. This is the default. | Most questions. |
| `detailed` | Every relevant fact, date, name, number, and direct quote the memories hold, with a list or table when the question asks for one or there are many items. | Reports and audits, the full history of a customer or a project, "list every time..." questions, and a model of your own that writes a long answer. |

```json theme={null}
{
  "observations": [
    {"content": [{"type": "text", "text": "List every outage Acme had this year, with dates."}]}
  ],
  "detail": "detailed"
}
```

`detail` is an instruction to the agent, not a cut. Priors never trims the text to make it fit, so an answer never stops mid-sentence. A `brief` answer can still be a little longer than 4 sentences when the question needs it. If your own model has a hard limit on input size, use `max_context_chars` instead; see the next section.

## `observe-and-enrich`: memory for your own model

Use this call when your product already has a language model. Priors returns the memories that relate to the message. Your model writes the answer.

```bash theme={null}
curl -X POST https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../observe-and-enrich \
  -H "Authorization: Bearer $SP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "observations": [
      {"content": [{"type": "text", "text": "Why did their service go down last time?"}]}
    ]
  }'
```

The request body is the same as for `observe`. You can add:

* `detail` (`brief`, `standard`, or `detailed`) to set how much the agent writes. See [Answer length](#answer-length-detail).
* `max_context_chars` (100 to 100,000) to put a hard limit on the size of the returned text.
* `request_id` to match this call with your own logs, and to look up later whether the message was learned.

`max_context_chars` is a **hard cut, not an instruction about length.** The agent writes its whole answer, and Priors then trims the text to that many characters, so a limit shorter than the answer leaves the reader with a sentence that stops mid-word. Omitting it means no trimming. Set it only when your own model has a small context window. To get shorter text, send `detail: brief`, which changes what gets written.

Your own model writes the answer, so Priors has no answer to learn. To have the answer remembered, send it back with [`observe`](/docs/concepts/learning) and `speaker` set to your assistant's name.

### The success response

```json theme={null}
{
  "agent_id": "agent_01J...",
  "thread_id": "thread_01J...",
  "response_context": "The customer runs version 4.2 on Ubuntu 22.04. Their last outage was caused by a full disk.",
  "preference": "",
  "in_context_memory_ids": ["mem_01J...", "mem_01J..."],
  "used_memory_ids": ["mem_01J..."],
  "in_context_memory_turns": ["turn:1", "turn:2"],
  "used_memory_turns": ["turn:2"],
  "in_context_source_ids": [],
  "used_source_ids": []
}
```

| Field | Meaning |
| - | - |
| `response_context` | The memories that relate to the message, written as text for a model to read. Put it in your prompt. It is not an answer. |
| `preference` | Reserved for learned user preferences. Empty today. |
| `in_context_memory_ids` | Every memory that Priors considered. |
| `used_memory_ids` | The memories that Priors used to write `response_context`. |
| `in_context_memory_turns`, `used_memory_turns` | The messages those memories came from. |
| `in_context_source_ids`, `used_source_ids` | The uploaded files those memories came from. Use them to show the user which file the information came from. |
| `learning` | Earlier messages in this thread that were not learned. Usually empty. See [Messages that were not learned](#messages-that-were-not-learned). |

When the agent has no related memories, `response_context` is empty. This is a success, not an error. Your model answers from the message alone.

## `observe-and-respond`: an answer from Priors

Use this call when you want Priors to write the answer.

```bash theme={null}
curl -X POST https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../observe-and-respond \
  -H "Authorization: Bearer $SP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "observations": [
      {"content": [{"type": "text", "text": "Why did their service go down last time?"}]}
    ]
  }'
```

### The success response

The response has the same fields as `observe-and-enrich`. The difference is the meaning of `response_context`: here it **is the answer**. Show it to the user. Do not send it to another model.

```json theme={null}
{
  "agent_id": "agent_01J...",
  "thread_id": "thread_01J...",
  "response_context": "Their last outage was caused by a full disk.",
  "preference": null,
  "in_context_memory_ids": ["mem_01J...", "mem_01J..."],
  "used_memory_ids": ["mem_01J..."],
  "used_memory_turns": ["turn:2"],
  "in_context_source_ids": [],
  "used_source_ids": []
}
```

`max_context_chars` trims the answer. It is the same hard cut described above: the answer is written in full, then cut to that many characters. `detail` sets the length of the answer without a cut.

**The agent learns its own answer.** Like `chat`, `observe-and-respond` learns the answer it writes, so a later question can build on it. Send `"learn_reply": false` in two cases:

* You also send the answer back as an observation. Otherwise it is learned twice.
* Answers must not become memory, for example when they are drafts that a person reviews.

### Errors for `observe-and-enrich` and `observe-and-respond`

The table below lists every error. `401` and `503 service_unavailable` can happen on any call; see [Errors](/docs/errors).

| Status | `code` | Cause | What to do |
| - | - | - | - |
| `400` or `422` | `invalid_request` | `observations` is empty or has more than 100 items, a block has the wrong format, or `max_context_chars` is outside 100 to 100,000. | Correct the request body. |
| `402` | `payment_required` | Your organization has no credits. | Add credits. |
| `404` | `thread_not_found` | The thread or the agent does not exist, or you cannot open it. | Check the ids. |
| `409` | `agent_not_ready` | The agent is still `initializing`, or it `failed`. | Poll the agent until its status is `ready`. |
| `429` | `rate_limit_exceeded` | More than 20 calls in one minute for this subject, 2 answers already running for it, or this agent is already learning something. `details` names which control refused. | Wait the number of seconds in `Retry-After`, then send the request again. |
| `503` | `answer_model_unavailable` | The model that writes the answer did not respond. | Wait, then send the request again with the same `request_id`. |
| `503` | `service_unavailable` | The agent is restarting. | Wait a few seconds, then send the request again with the same `request_id`. |

## `chat`: one message in a chat

Use `chat` when you build a chat screen. It takes one message from the user, writes an answer, saves both in the thread, and returns links to the files the answer used.

```bash theme={null}
curl -X POST https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../chat \
  -H "Authorization: Bearer $SP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "turn_id": "c6a9f2e0-1d2b-4c3a-9e8f-7a6b5c4d3e2f",
    "message": "Why did their service go down last time?"
  }'
```

| Field | Required | Meaning |
| - | - | - |
| `turn_id` | yes | An id that you create, unique for each message, up to 200 characters. If you send the same `turn_id` again, Priors charges it only once. |
| `message` | yes | The user's message. 1 to 2,000 characters. |
| `timestamp` | no | When the user sent the message. |
| `image_source_ids` | no | Up to 10 images that the model must look at for this answer. Upload them first; see [Sources](/docs/concepts/sources). |
| `detail` | no | `brief`, `standard` (the default), or `detailed`. How long the answer is. See [Answer length](#answer-length-detail). |

### The success response

```json theme={null}
{
  "agent_id": "agent_01J...",
  "thread_id": "thread_01J...",
  "turn_id": "c6a9f2e0-1d2b-4c3a-9e8f-7a6b5c4d3e2f",
  "message": "Their last outage was caused by a full disk. The runbook recommends a disk alert at 80 percent.",
  "memory_context": "The customer runs version 4.2 on Ubuntu 22.04. Their last outage was caused by a full disk.",
  "model": "…",
  "duration_ms": 1840,
  "stages": [
    {"name": "memory_context", "duration_ms": 310},
    {"name": "final_generation", "duration_ms": 1420},
    {"name": "turn_learning", "duration_ms": 110}
  ],
  "citations": [
    {"source_id": "src_01J...", "label": "acme-runbook.pdf", "media_type": "application/pdf",
     "href": "/api/v1/agents/agent_01J.../threads/thread_01J.../sources/src_01J...", "verified": false}
  ]
}
```

| Field | Meaning |
| - | - |
| `message` | The answer. |
| `memory_context` | The memories the answer was written from. |
| `stages` | The three steps and how long each took: `memory_context` (the memory search), `final_generation` (writing the answer), `turn_learning` (learning from the message). |
| `citations` | The files the answer used. `href` is a path on the same host that downloads the file. `verified` is `true` only for an image attached to this message. It is `false` for a document learned earlier, which is the normal case. |
| `learning` | Earlier messages or answers in this thread that were not learned. Usually empty. See the next section. |

### `chat/stream`: the same message, answer sent piece by piece

`chat/stream` takes the same request body. It returns server-sent events, so your screen can show the answer while it is written. The table below lists the events.

| Event | Data | What to do |
| - | - | - |
| `stage` | `{"stage": "memory_context"}` | One of the three steps finished. Optional: show progress. |
| `delta` | `{"delta": "..."}` | The next piece of the answer. Add it to the text on the screen. |
| `complete` | The full `chat` response. | The answer is done. Replace the text on the screen with `message`. |
| `error` | `{"error": "...", "code": "...", "request_id": "..."}` | The answer failed. This is the last event. `code` is `cancelled` when your client closed the connection. |

A stream always ends with exactly one `complete` event or one `error` event.

### Errors for `chat` and `chat/stream`

The first table lists errors where your request is wrong. Correct the request before you send it again.

| Status | `code` | Cause | What to do |
| - | - | - | - |
| `400` or `422` | `invalid_request` | `turn_id` or `message` is missing, empty, or too long (200 and 2,000 characters). | Correct the request body. |
| `402` | `payment_required` | Your organization has no credits. | Add credits. |
| `404` | `thread_not_found` | The thread or the agent does not exist, or you cannot open it. | Check the ids. |
| `404` | `not_found` | An id in `image_source_ids` is not an image that this agent holds. | Upload the image first, then send its id. |
| `413` | `payload_too_large` | More than 10 images in the message, or more than 32 MB of images in total. | Remove an image and send the message again. |

The second table lists errors where your request is correct but Priors cannot answer now. Wait, then send the same request again with the same `turn_id`.

| Status | `code` | Cause | What to do |
| - | - | - | - |
| `409` | `agent_not_ready` | The agent is still `initializing`, or it `failed`. | Poll the agent until its status is `ready`. |
| `429` | `rate_limit_exceeded` | More than 20 messages in one minute for this subject, or 2 answers already running for it. | Wait the number of seconds in `Retry-After`. |
| `503` | `answer_model_unavailable` | The model that writes the answer did not respond. | Wait, then try again. |
| `503` | `service_unavailable` | Image storage or the agent did not respond. | Wait a few seconds, then try again. |

On `chat/stream`, an error that happens after the stream has started arrives as the `error` event, with the same `code` values.

## Messages that were not learned

An answering call returns its answer first and learns the message after. Rarely, writing the memory fails. A message that fails is never learned later, so Priors tells you about it.

Every answering response has a `learning` list. It holds the messages of this thread that were not learned and that no earlier response reported. It is almost always empty.

```json theme={null}
"learning": [
  {
    "request_id": "req-4711",
    "role": "user",
    "status": "not_learned",
    "reason": "generation_failed",
    "message": "This message was answered but couldn't be saved to memory. You weren't charged for saving it. Send it again to save it.",
    "refunded_credits": "0.12",
    "action": "resend"
  }
]
```

| Field | Meaning |
| - | - |
| `request_id` | The `request_id` you sent (or the `x-request-id` header of that response), or the `turn_id` on `chat`. |
| `role` | `user` for your message. `assistant` for an answer Priors wrote and was learning. |
| `reason` | Why, as a code: `generation_failed`, `model_unavailable`, `generation_timeout`, and others. Treat an unknown code like `generation_failed`. |
| `refunded_credits` | What was refunded for the input side of this message. Null when nothing was charged, and for an answer, which was delivered and stays charged. |
| `action` | Always `resend`: send the message again to have it learned. |

To check one request, call `GET .../threads/{thread_id}/learning?request_id=...`. It lists every turn of that request as `learned`, `pending`, or `not_learned`. Without `request_id`, it returns the turns of the thread that were not learned, newest first, 50 per page. Send `next_cursor` as `before` for the next page.

```bash theme={null}
curl "https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../learning?request_id=req-4711" \
  -H "Authorization: Bearer $SP_API_KEY"
```

Messages you send with `observe` or `observe-file` report on their observation status instead. See [Learning](/docs/concepts/learning).

## Summary: which call to use

* You have your own model and prompt: `observe-and-enrich`.
* You want an answer and nothing else: `observe-and-respond`.
* You want a chat with history, an answer sent piece by piece, and links to files: `chat/stream`.
* You want the agent to remember without answering: [`observe`](/docs/concepts/learning).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.