Keep interactive retrieval evidence-only and isolate Research synthesis

This commit is contained in:
Jyong 2026-07-29 10:45:37 -04:00
parent 5c359b710a
commit b8a2f991c1
2 changed files with 56 additions and 15 deletions

View File

@ -113,6 +113,42 @@ describe("createApiAnswerGenerationOptions", () => {
expect(indexSource).not.toContain("model: answerGenerationOptions.model");
});
it("keeps interactive retrieval evidence-only and reserves LLM synthesis for Research", async () => {
const { readFile } = await import("node:fs/promises");
const indexSource = await readFile(new URL("./index.ts", import.meta.url), "utf8");
const citationOptionsStart = indexSource.indexOf("const multimodalCitationOptions =");
const researchMultimodalOptionsStart = indexSource.indexOf(
"const researchAnswerMultimodalOptions =",
);
const retrievalAssemblyStart = indexSource.indexOf(
"const retrievalEvidenceQueryGenerator = retriever",
);
const researchAnswerAssemblyStart = indexSource.indexOf(
"const profileLlmAnswerQueryGenerator = retriever",
);
const retrievalAssembly = indexSource.slice(
retrievalAssemblyStart,
researchAnswerAssemblyStart,
);
const citationOptionsAssembly = indexSource.slice(
citationOptionsStart,
researchMultimodalOptionsStart,
);
expect(citationOptionsStart).toBeGreaterThanOrEqual(0);
expect(researchMultimodalOptionsStart).toBeGreaterThan(citationOptionsStart);
expect(retrievalAssemblyStart).toBeGreaterThanOrEqual(0);
expect(researchAnswerAssemblyStart).toBeGreaterThan(retrievalAssemblyStart);
expect(citationOptionsAssembly).toContain("multimodalCandidateResolver");
expect(citationOptionsAssembly).not.toContain("multimodalAnswerOptions");
expect(retrievalAssembly).toContain("createHybridQueryGenerator");
expect(retrievalAssembly).toContain("...multimodalCitationOptions");
expect(retrievalAssembly).not.toContain("multimodalAnswerOptions");
expect(indexSource).toContain("generator: researchAnswerQueryGenerator");
expect(indexSource).toContain("{ queryGenerator: retrievalEvidenceQueryGenerator }");
expect(indexSource).not.toContain("{ queryGenerator: researchAnswerQueryGenerator }");
});
it("honors explicit output-token overrides", () => {
const options = createApiAnswerGenerationOptions({
KNOWLEDGE_ANSWER_MAX_OUTPUT_TOKENS: "2048",

View File

@ -558,8 +558,8 @@ const retrievalTestExecutor = retriever
retriever,
})
: undefined;
// Resolves multimodal citations (manifest item id, asset route, page/bbox) and is shared by both
// query generators. Available in database-repository mode, which is also when the retriever exists.
// Resolves multimodal citations (manifest item id, asset route, page/bbox) without invoking a
// model. Available in database-repository mode, which is also when the retriever exists.
const multimodalCandidateResolver =
repositoryOptions.documentAssets && repositoryOptions.parseArtifacts
? createDocumentMultimodalCandidateResolver({
@ -570,21 +570,26 @@ const multimodalCandidateResolver =
parseArtifacts: repositoryOptions.parseArtifacts,
})
: undefined;
const multimodalGeneratorOptions = {
...multimodalAnswerOptions,
const multimodalCitationOptions = {
...(multimodalCandidateResolver ? { multimodalCandidateResolver } : {}),
};
const extractiveQueryGenerator = retriever
const researchAnswerMultimodalOptions = {
...multimodalAnswerOptions,
...multimodalCitationOptions,
};
// Interactive query-stream is a retrieval surface: it returns bounded evidence and citations,
// never an LLM/VLM-synthesized answer. Answer synthesis remains a separate Research capability.
const retrievalEvidenceQueryGenerator = retriever
? createHybridQueryGenerator({
limit: 5,
maxAnswerChars: 2_000,
retriever,
topK: 10,
...embeddingGeneratorOptions,
...multimodalGeneratorOptions,
...multimodalCitationOptions,
})
: undefined;
const llmAnswerQueryGenerator = retriever
const profileLlmAnswerQueryGenerator = retriever
? createLlmAnswerQueryGenerator({
limit: 5,
maxAnswerChars: 2_000,
@ -594,14 +599,14 @@ const llmAnswerQueryGenerator = retriever
temperature: 0,
topK: 10,
...embeddingGeneratorOptions,
...multimodalGeneratorOptions,
...researchAnswerMultimodalOptions,
})
: undefined;
const queryGenerator =
extractiveQueryGenerator && llmAnswerQueryGenerator
const researchAnswerQueryGenerator =
retrievalEvidenceQueryGenerator && profileLlmAnswerQueryGenerator
? createProfileAwareQueryGenerator({
extractiveGenerator: extractiveQueryGenerator,
profileLlmGenerator: llmAnswerQueryGenerator,
extractiveGenerator: retrievalEvidenceQueryGenerator,
profileLlmGenerator: profileLlmAnswerQueryGenerator,
})
: undefined;
const researchProjectionSnapshotResolver = repositoryOptions.projectionSetPublications
@ -646,7 +651,7 @@ const researchTaskRuntime =
databaseRepositories.researchTaskPartialResults &&
databaseRepositories.researchTaskProgressEvents &&
repositoryOptions.knowledgeSpaceAccess &&
queryGenerator
researchAnswerQueryGenerator
? createApiResearchTaskRuntime({
access: repositoryOptions.knowledgeSpaceAccess,
adapter,
@ -654,7 +659,7 @@ const researchTaskRuntime =
? { capabilityGrants: databaseRepositories.capabilityGrantProvenance }
: {}),
...(deletionLifecycleFence ? { deletionFence: deletionLifecycleFence } : {}),
generator: queryGenerator,
generator: researchAnswerQueryGenerator,
manifests: knowledgeSpaceManifests,
metrics: operationalMetrics.durableTasks,
partials: databaseRepositories.researchTaskPartialResults,
@ -768,7 +773,7 @@ const app = createKnowledgeGateway({
},
}
: {}),
...(queryGenerator ? { queryGenerator } : {}),
...(retrievalEvidenceQueryGenerator ? { queryGenerator: retrievalEvidenceQueryGenerator } : {}),
...(retrievalTestExecutor ? { retrievalTestExecutor } : {}),
...(publishedGraph ? { publishedGraph } : {}),
...(researchTaskRuntime