Documentation
¶
Overview ¶
Package answer turns a question into a grounded answer, or into an honest refusal to answer.
Declining is a first-class outcome rather than an error path. Calibration against the curated corpus showed that a retrieval score cannot separate a question the corpus answers from one whose subject it merely covers: the bands overlap, and excluding every unanswerable question would cost 40% of the answerable ones. BM25 measures term overlap, not whether a passage answers anything.
So the judgement lives here, and it is made against the retrieved passages. A component that can only produce answers produces one for every question, including the questions whose correct answer is that we do not know — and those answers arrive carrying a real citation to a page that does not say it, which is worse than silence.
Index ¶
Constants ¶
const DefaultLimit = 8
DefaultLimit is how many passages are retrieved for one question.
const PassageDelimiter = "-----BEGIN PASSAGE-----"
PassageDelimiter fences retrieved content in the prompt.
Passage text is untrusted: it comes from public repositories anyone may open a merge request against, and in later phases the question arrives from a public Discord channel. Delimiting is what keeps content from reading as instruction — it does not make injection impossible, but it makes the boundary explicit and it is the part a prompt can actually enforce.
Variables ¶
var ( // ErrUngrounded means the composer returned prose citing nothing. Whatever // it says is not supported by anything retrieved. ErrUngrounded = errors.New("answer: no passage was cited") // ErrFabricatedCitation means a citation pointed outside the retrieved set. ErrFabricatedCitation = errors.New("answer: cited a passage that was not retrieved") )
Reasons an answer was refused after the composer produced one.
Functions ¶
func ResponseSchema ¶
func ResponseSchema() *jsonschema.Schema
ResponseSchema is the structured-output schema the composer expects.
Reflected from composeReply through chat.GenerateSchema rather than written by hand. The hand-written map worked against claude-local, which marshals whatever it is given, and was silently mangled by the Anthropic provider — that provider reads a *jsonschema.Schema for its properties and required fields, and treats any other value as the properties map itself. Reflecting the struct keeps the schema and the type it decodes into from drifting apart as well.
Returns the concrete *jsonschema.Schema rather than any. chat.Config.Bind types the field concretely from v0.9.0, so the interface return now needs a type assertion at every call site — and the whole reason this exists is that handing that field the wrong shape fails silently rather than loudly.
func SystemPrompt ¶
func SystemPrompt() string
SystemPrompt is the instruction the composer relies on, exported so a caller constructing the client can set it.
Types ¶
type Answer ¶
type Answer struct {
// Text is the prose, empty when declining.
Text string
// Cites indexes the passages the answer is grounded in, as positions in the
// slice the composer was given.
Cites []int
// Declined marks a judgement that the passages do not answer the question.
Declined bool
// DeclineReason says why, in terms a person can read.
DeclineReason string
}
Answer is what a Composer produces.
type ChatComposer ¶
type ChatComposer struct {
// contains filtered or unexported fields
}
ChatComposer composes answers through a chat provider.
func NewComposer ¶
func NewComposer(client chat.ChatClient) *ChatComposer
NewComposer builds a composer over a chat client.
type Citation ¶
type Citation struct {
URL string
HeadingPath string
SourcePath string
RepoPath string
CommitSHA string
}
Citation is a resolved reference to the passage supporting an answer.
type Composer ¶
type Composer interface {
Compose(ctx context.Context, question string, results []index.Result) (Answer, error)
}
Composer turns a question and its retrieved passages into an answer, or declines.
The contract is deliberately able to say no. Sufficiency is judged against the passages rather than against the question or the model's own knowledge: an answer that cannot be grounded in what was retrieved must not be returned however plausible it is.
type Option ¶
type Option func(*Service)
Option configures a Service.
func WithScoreFloor ¶
WithScoreFloor discards passages scoring below f before composing.
A floor discards noise. It is deliberately not the gate: calibration showed no score separates answerable questions from unanswerable ones, so a floor set high enough to exclude the latter takes 40% of the former with it.
type Response ¶
type Response struct {
Text string
Citations []Citation
// Declined is true when the corpus did not answer. It is an outcome, not a
// failure: a declined question is a knowledge gap, which is the product's
// highest-value output.
Declined bool
DeclineReason string
// Cause carries why an answer was refused after being produced, when it was
// refused here rather than declined by the composer.
Cause error
// Retrieved is what the question found, for reporting and for measuring
// retrieval independently of composition.
Retrieved []index.Result
}
Response is the outcome of asking a question.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service answers questions from the index.
func (*Service) Ask ¶
Ask answers a question, or declines.
An error means something broke — the index was unreachable, the provider failed. It never means "we do not know", which is a Response with Declined set. Conflating them would lose the distinction between a knowledge gap and an incident, and the gap backlog depends on it.