Skip to main content
You have GitHub’s Terms of Service and a plain-language question about it. You need the lines that answer the question and a way to detect when the document has no answer. You end up with find(), which returns the exists probability and one relevance score per line. A query scans a document and reveals an answer attached to the matching
line The search has three parts:
  1. Tag each line with an ID so TypeSafe can point to it.
  2. Use a Choice question to rank those line IDs by how well they answer the query.
  3. In the same request, use a Noul question to check whether the document contains an answer at all.

Setup

Get a TypeSafe API key

Create a key in the TypeSafe console and export it:

Install the dependencies

JsonCache replays the API responses in json_cache.json, so the steps below run without an API key or any spend. Download it from typesafe-public-examples into the folder you run the script from. To make the requests live instead, set TYPESAFE_API_KEY and leave it out.

Create the script

Start semantic_search.py with the imports and the client:

Step 1: tag every line with an ID

The test document is GitHub’s Terms of Service, split into 218 clauses, so every search result points to one quotable line.
The cache prevents repeated downloads. Now prefix each line with a short ID and join the lines back into one document. The model uses these IDs to point to its answer.
DOCUMENT now looks like this:

Step 2: ask where the answer is

A Choice question returns a probability for every option. Use the line IDs as the options.
The option descriptions are None because the document already contains the text for each ID. The query goes in instructions; the state stays unchanged between searches.
A Choice question accepts up to 255 options, so this recipe searches documents of up to 255 lines in one request. Past that, search in two passes: one Choice question picks a window of lines, and a second ranks the lines inside it.

Step 3: check whether an answer exists

Choice probabilities always add up to 1, so some line ranks first even when the document doesn’t answer the question.
Unlike the Choice probabilities, the Noul probability doesn’t depend on the other options, so it can fall near zero when the document has no answer.

Step 4: send both questions in one request

Both questions go in one system_one call, so the document is sent once.
The relevance list keeps one score per line, in document order.

Step 5: read the result

The exists probability becomes one of three verdicts, the middle one for partial answers. The ranking prints as a bar chart.
Tune these thresholds on your own documents before using them in production. Ask two questions that have direct answers, one that has no answer, and one that has a partial answer.

What the scores mean

The first two queries return direct answers and the source lines needed to verify them.
  • Arbitration: The ranking gives the closest line a score of 0.86, but exists is only 0.14. The answer is not in the document.
  • Parental permission: The age rule ranks first, but it doesn’t answer whether parental permission changes the rule. The result is partially addressed.
The ranking tells you where to look; the exists score tells you whether the result answers the question.

Try it on your own document

Open the tagged contract in the TypeSafe playground to edit the questions against the same text. To search your own, swap DOCUMENT_URL; every other line of the script works off LINES.