Vector search lets Redshred find passages that are semantically similar to a question, not just keyword matches. It has two main parts:
Vector search is configured as a normal enrichment in the Enrichment Config Editor. You do not need to call any special API endpoints or scripts to build the index.
The indexing component is responsible for creating a vector representation of the text in a collection. This is done by adding a vectors enrichment in the Enrichment Config Editor.
A typical configuration looks like this:
{
"config": {
"perspective_name": "vectors_main",
"queries": [
"perspective.name = \"typography\" and segment_type = \"paragraph\""
]
},
"name": "vectors",
"perspective": "vectors",
"segments": {
"prerequisites": ["typography"],
"queries": []
}
}
This configuration:
queries filter.vectors_main.typography enrichment (listed under prerequisites) to run first.The full set of possible options under config is:
"config": {
"queries": [],
"doc_name": "",
"fine_tune": false,
"normalize": true,
"base_model": "sentence-transformers/all-MiniLM-L12-v1",
"collection": "",
"lm_segment_only": false,
"perspective_name": "",
"bulk_segment_size": 1000,
"backend_batch_size": 100
}
For most use cases, only a few of these need to be changed:
queries (required)
perspective.name = "typography" and segment_type = "paragraph".perspective_name (required)
"vectors_main" or "product_vectors".base_model (optional, default: "sentence-transformers/all-MiniLM-L12-v1")
normalize (optional, default: true)
true.lm_segment_only (optional, default: false)
true, only the language-model text segment is indexed. Most use cases can leave this as false.bulk_segment_size (optional, default: 1000)
backend_batch_size (optional, default: 100)
Indexing is incremental and depends on whether your collection is empty or already has documents.
On an empty collection:
vectors enrichment runs for the first time and creates a global perspective over the collection.When a subsequent document is added:
On a non-empty collection:
The reader automatically makes document-level perspectives. The vectors enrichment ignores this existing behavior and relies solely on the global perspective:
When you run vector search:
There are two perspective names involved in the configuration:
perspective field at the root of the enrichment config is the document-level perspective name.perspective_name field inside the config object is the global, collection-level perspective name.
These fields are managed by Redshred and should not be set by users in the Enrichment Config Editor:
doc_namecollectionThey will be inferred from the documents and collection you are configuring. If they are set, please remove them.
fine_tune
fine_tune flag exists but fine-tuning is not currently supported.fine_tune set to false.The searching component is responsible for finding the most similar documents to a given query. The parameters for the searching endpoint are:
Required:
collection - The collection to search inperspective_name - The name of the perspective that holds the index parametersquery - The query text to search forenrichment_name - The name of the enrichmentOptional:
n - The number of results to return (default: 10)config - Additional configuration as a JSON string (default: “{}”)Sample API Call to invoke the search endpoint:
export REDSHRED_HOST="https://api.staging.redshred.com"
export COLLECTION="alddom-ucs"
export REDSHRED_TOKEN="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
curl -X POST $REDSHRED_HOST/v2/collections/$COLLECTION/services/vector-search -H "Accept:application/json" -H "Content-type:application/json" -H "Authorization: Token $REDSHRED_TOKEN" -d '{
"collection": "alddom-ucs",
"perspective_name": "name-of-generated-perspective",
"query": "How do I troubleshoot engine problems?",
"enrichment_name": "vectors",
"n": 5
}'
vsearch("<query>", ["result_limit",] "<perspective_name>")
vsearch("How do I troubleshoot engine problems?", "vectors_main") # default result limit is 10
vsearch("How do I troubleshoot engine problems?", 50, "vectors_main")
from redshred import RedShredClient
rs = RedShredClient()
collection_name = "alddom-ucs"
search_payload = {
"collection": collection_name,
"perspective_name": "name-of-generated-perspective",
"query": "How do I troubleshoot engine problems?",
"enrichment_name": "vectors",
"n": 5,
"config": {}
}
try:
results = rs.api.post(
f"v2/collections/{collection_name}/services/vectors-search",
json=search_payload, raise_for_status=True, absolute_path=True
)
# Process search results
for result in results.get("results", []):
print(f"Score: {result.get('distance')}")
print(f"Text: {result.get('text')}")
print(f"Segment Link: {result.get('self_link')}")
print("---")
except Exception as e:
print(f"Error searching collection: {e}")
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"sort"
"strings"
)
type VectorSearchResponse struct {
Results []VectorSearchResponseItem `json"results"`
}
type VectorSearchResponseItem struct {
I int `json:"i"`
Distance float64 `json:"distance"`
ID int `json:"id"`
Text string `json:"text"`
SelfLink string `json:"self_link"`
rs *RedShredClient
}
func VectorSearch(rs *RedShredClient, collection string, perspectiveName string, question string) (VectorSearchResponse, error) {
path := "/v2/collections/" + collection + "/services/vector-search"
// Define the request payload as a struct
payload := map[string]interface{}{
"collection": collection,
"perspective_name": perspectiveName,
// "token": token,
"config": "{}", // map[string]interface{}{}, // empty config object
"n": 40,
"enrichment_name": "vectors",
"query": question,
}
// Encode the payload as JSON
jsonData, err := json.Marshal(payload)
if err != nil {
return VectorSearchResponse{}, fmt.Errorf("error encoding JSON for Vector Search request: %v", err)
}
// Create the HTTP request
resp, err := rs.JSONRequest("POST", path, bytes.NewBuffer(jsonData))
if err != nil {
return VectorSearchResponse{}, fmt.Errorf("error performing Vector Search request: %v", err)
}
var vectorResp VectorSearchResponse
err = json.Unmarshal(resp, &vectorResp)
if err != nil {
return VectorSearchResponse{}, err
}
for i := range vectorResp.Results {
vectorResp.Results[i].rs = rs
}
var goodVectorResp VectorSearchResponse
for _, prospect := range vectorResp.Results {
if len(strings.Fields(prospect.Text)) > 10 {
goodVectorResp.Results = append(goodVectorResp.Results, prospect)
}
if len(goodVectorResp.Results) > 5 {
break
}
}
// Reverse list
sort.Slice(goodVectorResp.Results, func(i, j int) bool {
return goodVectorResp.Results[i].Distance < goodVectorResp.Results[j].Distance
})
return goodVectorResp, nil
}