KNN few-shot: choose demonstrations by similarity at run time
Source:R/teleprompter-knn.R
KNNFewShot.RdKNNFewShot() gives every input its own demonstrations: the k training
rows whose embeddings are most similar to it. Compiling embeds the training
set once, with no model calls; each run then embeds the input and picks its
nearest rows. LabeledFewShot(), by contrast, attaches one fixed set.
Usage
KNNFewShot(
metric = NULL,
metric_threshold = NULL,
max_errors = 5L,
k = 3L,
vectorizer = function() NULL,
input_text = NULL,
cache_embeddings = TRUE,
merge_demos = FALSE
)Arguments
- metric
Optional. It is not used for scoring, but when it has a
field, that column supplies the demonstrations' outputs.- metric_threshold, max_errors
Accepted for consistency with the other optimizers (see
Teleprompter());KNNFewShot()does not use them.- k
Integer number of neighbors used as demonstrations (default
3L).- vectorizer
Required. A function that takes a character vector and returns a numeric matrix of embeddings, one row per string.
- input_text
Optional function that turns a one-row data frame of inputs into the text to embed. The default pastes the signature's input columns together, separated by spaces.
- cache_embeddings
Currently has no effect: the training set is always embedded once, at compile time.
- merge_demos
If
TRUE, keep the module's existing demonstrations and add the selected ones after them. IfFALSE(the default), the selected rows replace them.
Value
A KNNFewShot object to pass to compile().
Details
vectorizer turns a character vector into a numeric matrix with one row
per string. For real embeddings, wrap a provider, for example
function(x) ragnar::embed_openai(x, model = "text-embedding-3-small").
Similarity is cosine similarity.
compile() returns a wrapper module. Each run records the chosen rows and
their similarity scores in compiled$state$demo_selections. Demonstration
outputs come from the metric's field when there is one, and otherwise from
an automatically detected label column, as in LabeledFewShot().
Examples
# A toy vectorizer that counts letters. Use real embeddings in practice.
letter_counts <- function(texts) {
t(vapply(
tolower(texts),
function(x) tabulate(utf8ToInt(x) - 96L, nbins = 26L),
integer(26)
))
}
tp <- KNNFewShot(k = 2L, vectorizer = letter_counts)
tp
#> <dsprrr::KNNFewShot>
#> @ metric : NULL
#> @ metric_threshold: NULL
#> @ max_errors : int 5
#> @ k : int 2
#> @ vectorizer : function (texts)
#> @ input_text : NULL
#> @ cache_embeddings: logi TRUE
#> @ merge_demos : logi FALSE
qa <- module(signature("question -> answer"))
trainset <- data.frame(
question = c(
"What is 2 + 2?", "Capital of France?",
"What is 10 / 5?", "Capital of Peru?"
),
answer = c("4", "Paris", "2", "Lima")
)
# Compiling embeds the training rows; no model is called
compiled <- compile(qa, tp, trainset)
#> Computing embeddings for 4 training examples...
if (FALSE) { # \dontrun{
run(
compiled,
question = "Capital of Chile?",
.llm = ellmer::chat_openai(model = "gpt-6-luna")
)
compiled$state$demo_selections
} # }