feat(quality): show replay evidence details

This commit is contained in:
Jyong 2026-08-19 09:45:41 -04:00
parent 53df42c890
commit 0391141936
39 changed files with 925 additions and 26 deletions

View File

@ -174,6 +174,7 @@ from services.knowledge_fs.product_dto import (
KnowledgeFSPresignedUploadResponse,
KnowledgeFSProfileMigrationResponse,
KnowledgeFSQualityListQuery,
KnowledgeFSQualityReplayDetailQuery,
KnowledgeFSQualityReplayListQuery,
KnowledgeFSQualityReplayListResponse,
KnowledgeFSQualityReplayPayload,
@ -299,6 +300,7 @@ register_schema_models(
KnowledgeFSAdmittedQueryRequest,
KnowledgeFSQueryCreatePayload,
KnowledgeFSQualityListQuery,
KnowledgeFSQualityReplayDetailQuery,
KnowledgeFSQualityReplayListQuery,
KnowledgeFSQualityReplayPayload,
KnowledgeFSResearchTaskPartialsQuery,
@ -3376,6 +3378,7 @@ class KnowledgeFSSpaceQualityReplayApi(Resource):
@console_ns.route("/knowledge-fs/spaces/<string:control_space_id>/quality/replay-runs/<string:run_id>")
class KnowledgeFSSpaceQualityReplayDetailApi(Resource):
@console_ns.doc(params=query_params_from_model(KnowledgeFSQualityReplayDetailQuery))
@console_ns.response(
HTTPStatus.OK,
"KnowledgeFS quality replay run",
@ -3387,11 +3390,13 @@ class KnowledgeFSSpaceQualityReplayDetailApi(Resource):
@_knowledge_fs_errors
def get(self, control_space_id: str, run_id: str):
actor_id, tenant_id = _actor()
query = KnowledgeFSQualityReplayDetailQuery.model_validate(request.args.to_dict())
result = _console_services().facade.get_quality_replay(
tenant_id=tenant_id,
account_id=actor_id,
control_space_id=control_space_id,
run_id=run_id,
evidence_item_id=str(query.evidence_item_id) if query.evidence_item_id else None,
)
return dump_response(KnowledgeFSQualityReplayResponse, result)

View File

@ -1,7 +1,7 @@
{
"schemaVersion": 5,
"subtreeTree": "04565f7a5d363ef4ccb11547e3fa148f99b42ca0",
"openapiSha256": "c9de381284acb183e947fe4d6b019135223511c4fed35b91e966d808ea3a2bf4",
"subtreeTree": "05e402ad162663945d482ebfa7c5221e73347364",
"openapiSha256": "2cf348c68bbe65dd51bbde9a0a4f91398beeebd79e89e9288c9386b26ae09796",
"capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7",
"capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3",
"productOperationManifestSha256": "11f801a41805b250f17766935ee9cc7fc93c3ef6489503ba6297673b73362b7d",

View File

@ -2198,6 +2198,7 @@ class KnowledgeFSDataFacade:
account_id: str,
control_space_id: str,
run_id: str,
evidence_item_id: str | None = None,
) -> KnowledgeFSQualityReplayResponse:
raw = self._interactive_child(
tenant_id=tenant_id,
@ -2206,6 +2207,7 @@ class KnowledgeFSDataFacade:
operation_id="getQualityReplay",
resource_id=run_id,
path_parameters=(("runId", run_id),),
query=(("evidenceItemId", evidence_item_id),) if evidence_item_id else (),
)
return KnowledgeFSQualityReplayResponse.model_validate(raw)

View File

@ -3370,7 +3370,35 @@ class KnowledgeFSQualityReplayPayload(BaseModel):
return self
class KnowledgeFSQualityReplayDetailQuery(BaseModel):
evidence_item_id: UUID | None = None
class KnowledgeFSQualityReplayEvidenceItem(ResponseModel):
available: bool
document_name: str | None = Field(
default=None,
validation_alias=AliasChoices("document_name", "documentName"),
)
matched: bool
ordinal: int = Field(ge=1)
page_number: int | None = Field(
default=None,
ge=1,
validation_alias=AliasChoices("page_number", "pageNumber"),
)
section_path: list[str] = Field(
default_factory=list,
validation_alias=AliasChoices("section_path", "sectionPath"),
)
text: str | None = None
class KnowledgeFSQualityReplayEvidenceDiff(ResponseModel):
evidence_items: list[KnowledgeFSQualityReplayEvidenceItem] | None = Field(
default=None,
validation_alias=AliasChoices("evidence_items", "evidenceItems"),
)
expected_count: int = Field(ge=0, validation_alias=AliasChoices("expected_count", "expectedCount"))
matched_count: int = Field(ge=0, validation_alias=AliasChoices("matched_count", "matchedCount"))
missing_count: int = Field(ge=0, validation_alias=AliasChoices("missing_count", "missingCount"))

View File

@ -2041,7 +2041,7 @@ def test_logical_document_delete_preserves_initial_row_version() -> None:
"get_quality_replay",
"KnowledgeFSQualityReplayResponse",
"getQualityReplay",
{"run_id": "replay-run-1"},
{"run_id": "replay-run-1", "evidence_item_id": "replay-item-1"},
"replay-run-1",
),
(
@ -2285,6 +2285,7 @@ def test_facade_public_methods_preserve_the_registered_operation_and_child_bindi
)
if operation_id == "getQualityReplay":
assert delegated.call_args.kwargs["path_parameters"] == (("runId", "replay-run-1"),)
assert delegated.call_args.kwargs["query"] == (("evidenceItemId", "replay-item-1"),)
if operation_id == "getOverviewQueryOutcomes":
assert delegated.call_args.kwargs["query"] == (("window", "7d"),)
if operation_id == "cancelBackgroundTask":

View File

@ -34,6 +34,8 @@ from services.knowledge_fs.product_dto import (
KnowledgeFSOverviewWindowQuery,
KnowledgeFSPublicFailureResponse,
KnowledgeFSQualityListQuery,
KnowledgeFSQualityReplayDetailQuery,
KnowledgeFSQualityReplayEvidenceDiff,
KnowledgeFSQualityReplayPayload,
KnowledgeFSQueryCreatePayload,
KnowledgeFSRerankIntent,
@ -78,6 +80,40 @@ def test_quality_replay_payload_requires_exactly_one_selection_mode() -> None:
KnowledgeFSQualityReplayPayload(selection="all-active", golden_question_ids=["question-1"])
def test_quality_replay_detail_query_requires_a_uuid_evidence_item_id() -> None:
value = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c50"
assert str(KnowledgeFSQualityReplayDetailQuery(evidence_item_id=value).evidence_item_id) == value
with pytest.raises(ValidationError):
KnowledgeFSQualityReplayDetailQuery(evidence_item_id="not-a-uuid")
def test_quality_replay_evidence_diff_accepts_safe_detail_aliases() -> None:
diff = KnowledgeFSQualityReplayEvidenceDiff.model_validate(
{
"evidenceItems": [
{
"available": True,
"documentName": "Permissions.pdf",
"matched": False,
"ordinal": 1,
"pageNumber": 3,
"sectionPath": ["Permissions", "Limits"],
"text": "Editors cannot promote themselves.",
}
],
"expectedCount": 1,
"matchedCount": 0,
"missingCount": 1,
"retrievedCount": 10,
}
)
assert diff.evidence_items is not None
assert diff.evidence_items[0].document_name == "Permissions.pdf"
assert diff.evidence_items[0].section_path == ["Permissions", "Limits"]
def test_golden_question_evidence_match_requires_exactly_one_lookup_mode() -> None:
assert KnowledgeFSGoldenQuestionEvidenceMatchPayload(evidence=" permissions ").evidence == "permissions"
assert KnowledgeFSGoldenQuestionEvidenceMatchPayload(node_ids=[" node-1 ", "node-1", "node-2"]).node_ids == [

View File

@ -0,0 +1,47 @@
# Quality replay evidence details
## What changed
- Added an optional, item-scoped evidence-detail expansion to the existing quality replay detail
endpoint. The normal report response remains compact; evidence text is resolved only after the
user opens one question's hit count.
- Resolved immutable expected evidence nodes in their persisted order and classified each passage
as hit or missed from the replay's frozen evidence diff.
- Added document name, section path, page number, and passage text to the expanded response without
exposing internal node identifiers or unrelated retrieved identifiers.
- Made the report's evidence-hit count interactive and added a centered dialog that groups hit and
missed passages, with loading, retry, empty, inaccessible, and close states.
- Added localized UI copy for every supported dataset locale.
## Why
The report previously exposed only aggregate counts such as `1/2`. That was enough to determine
that an evaluation failed, but not enough to identify which expected passage was absent or inspect
the evidence that did match.
## Security and performance
- Evidence nodes and their backing document assets are rechecked against the current candidate
grants before text or source metadata is returned. Deleted or inaccessible historical evidence
is represented by a redacted placeholder.
- Node resolution is one bounded bulk lookup. Backing asset lookups are deduplicated and bounded by
the expected evidence set, and execute only for the single replay item explicitly requested.
- Replay list and ordinary replay-detail requests do not load evidence text, preventing report
payload growth across large evaluation runs.
## Verification
- `@knowledge/api` quality-control handler tests: 25 passed.
- `@knowledge/api` typecheck passed.
- Python KnowledgeFS DTO and facade tests: 155 passed.
- Generated Console contract typecheck passed.
- Quality evaluation panel tests: 4 passed.
- Targeted Biome, Ruff, Ruff format, frontend static checks, locale JSON checks, and
`git diff --check` passed.
## Known risks and follow-up
- Historical evidence may have been deleted or may no longer be visible under the current user's
grants; the dialog intentionally redacts it instead of reproducing the old content.
- A future report export feature should use a paginated evidence-detail endpoint rather than
expanding every replay item in one response.

View File

@ -26,6 +26,9 @@ const GOLDEN_QUESTION_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c44";
const TRACE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c46";
const BAD_CASE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c47";
const CAPABILITY_GRANT_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c48";
const MATCHED_EVIDENCE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c50";
const MISSING_EVIDENCE_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c51";
const DOCUMENT_ASSET_ID = "018f0d60-7a49-7cc2-9c1b-5b36f18f2c52";
const NOW = "2026-07-14T15:00:00.000Z";
describe("quality-control handlers", () => {
@ -184,6 +187,212 @@ describe("quality-control handlers", () => {
});
});
it("loads permission-safe matched and missing evidence details only when requested", async () => {
const base = replayRun();
const replayItem = {
...base.items[0],
expectedEvidenceIds: [MATCHED_EVIDENCE_ID, MISSING_EVIDENCE_ID],
result: {
evidenceDiff: {
matchPolicy: "all",
missingEvidenceIds: [MISSING_EVIDENCE_ID],
retrievedEvidenceIds: [MATCHED_EVIDENCE_ID, "retrieved-evidence-secret"],
},
metrics: { totalMs: 17 },
},
};
const getManyByIdsAcrossGenerations = vi.fn(async () => [
{
documentAssetId: DOCUMENT_ASSET_ID,
id: MATCHED_EVIDENCE_ID,
permissionScope: ["subject:editor-1"],
sourceLocation: { pageNumber: 2, sectionPath: ["Permissions", "Roles"] },
text: "Workspace owners can change member permissions.",
},
{
documentAssetId: DOCUMENT_ASSET_ID,
id: MISSING_EVIDENCE_ID,
permissionScope: ["subject:editor-1"],
sourceLocation: { pageNumber: 3, sectionPath: ["Permissions", "Limitations"] },
text: "Editors cannot promote themselves to owner.",
},
]);
const getAsset = vi.fn(async () => ({
filename: "Workspace permissions.pdf",
id: DOCUMENT_ASSET_ID,
metadata: { permissionScope: ["subject:editor-1"] },
}));
const app = qualityApp(
{
getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })),
} as unknown as QualityControlRepository,
{
assets: { get: getAsset } as never,
nodes: { getManyByIdsAcrossGenerations } as never,
},
);
const response = await app.request(
`/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`,
);
expect(response.status).toBe(200);
expect(getManyByIdsAcrossGenerations).toHaveBeenCalledWith({
ids: [MATCHED_EVIDENCE_ID, MISSING_EVIDENCE_ID],
knowledgeSpaceId: SPACE_ID,
});
const body = await response.json();
expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([
{
available: true,
documentName: "Workspace permissions.pdf",
matched: true,
ordinal: 1,
pageNumber: 2,
sectionPath: ["Permissions", "Roles"],
text: "Workspace owners can change member permissions.",
},
{
available: true,
documentName: "Workspace permissions.pdf",
matched: false,
ordinal: 2,
pageNumber: 3,
sectionPath: ["Permissions", "Limitations"],
text: "Editors cannot promote themselves to owner.",
},
]);
expect(JSON.stringify(body)).not.toContain("retrieved-evidence-secret");
});
it("redacts replay evidence text that is no longer visible to the current grants", async () => {
const base = replayRun();
const replayItem = {
...base.items[0],
expectedEvidenceIds: [MISSING_EVIDENCE_ID],
result: {
evidenceDiff: {
missingEvidenceIds: [MISSING_EVIDENCE_ID],
retrievedEvidenceIds: [],
},
},
};
const app = qualityApp(
{
getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })),
} as unknown as QualityControlRepository,
{
assets: { get: vi.fn() } as never,
nodes: {
getManyByIdsAcrossGenerations: vi.fn(async () => [
{
documentAssetId: DOCUMENT_ASSET_ID,
id: MISSING_EVIDENCE_ID,
permissionScope: ["scope:not-granted"],
sourceLocation: { sectionPath: ["Private"] },
text: "private-evidence-secret",
},
]),
} as never,
},
);
const response = await app.request(
`/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`,
);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([
{ available: false, matched: false, ordinal: 1, sectionPath: [] },
]);
expect(JSON.stringify(body)).not.toContain("private-evidence-secret");
});
it("redacts replay evidence when its document asset is no longer visible", async () => {
const base = replayRun();
const replayItem = {
...base.items[0],
expectedEvidenceIds: [MATCHED_EVIDENCE_ID],
result: {
evidenceDiff: {
missingEvidenceIds: [],
retrievedEvidenceIds: [MATCHED_EVIDENCE_ID],
},
},
};
const app = qualityApp(
{
getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })),
} as unknown as QualityControlRepository,
{
assets: {
get: vi.fn(async () => ({
filename: "private-document.pdf",
id: DOCUMENT_ASSET_ID,
metadata: { permissionScope: ["scope:not-granted"] },
})),
} as never,
nodes: {
getManyByIdsAcrossGenerations: vi.fn(async () => [
{
documentAssetId: DOCUMENT_ASSET_ID,
id: MATCHED_EVIDENCE_ID,
permissionScope: ["subject:editor-1"],
sourceLocation: { sectionPath: ["Private"] },
text: "asset-protected-evidence-secret",
},
]),
} as never,
},
);
const response = await app.request(
`/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`,
);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.items[0].result.evidenceDiff.evidenceItems).toEqual([
{ available: false, matched: true, ordinal: 1, sectionPath: [] },
]);
expect(JSON.stringify(body)).not.toContain("asset-protected-evidence-secret");
expect(JSON.stringify(body)).not.toContain("private-document.pdf");
});
it("rejects oversized replay evidence details before loading content", async () => {
const base = replayRun();
const replayItem = {
...base.items[0],
expectedEvidenceIds: Array.from(
{ length: 51 },
(_, index) => `018f0d60-7a49-7cc2-9c1b-${index.toString(16).padStart(12, "0")}`,
),
};
const getManyByIdsAcrossGenerations = vi.fn();
const getAsset = vi.fn();
const app = qualityApp(
{
getReplay: vi.fn(async () => ({ ...base, items: [replayItem] })),
} as unknown as QualityControlRepository,
{
assets: { get: getAsset } as never,
nodes: { getManyByIdsAcrossGenerations } as never,
},
);
const response = await app.request(
`/knowledge-spaces/${SPACE_ID}/quality/replay-runs/${RUN_ID}?evidenceItemId=${replayItem.id}`,
);
expect(response.status).toBe(400);
await expect(response.json()).resolves.toEqual({
error: "Replay evidence detail limit exceeded",
});
expect(getManyByIdsAcrossGenerations).not.toHaveBeenCalled();
expect(getAsset).not.toHaveBeenCalled();
});
it("serializes optional replay errors, results, embedding, and rerank provenance safely", async () => {
const base = replayRun();
const getReplay = vi

View File

@ -56,6 +56,7 @@ import { evidenceBundleFromAnswerTrace } from "./query-virtual-entries";
const MAX_QUALITY_REPLAY_QUESTIONS = 1_000;
const QUALITY_REPLAY_QUESTION_PAGE_SIZE = 100;
const MAX_QUALITY_REPLAY_EVIDENCE_DETAILS = 50;
class QualityReplaySelectionTooLargeError extends Error {}
@ -512,9 +513,22 @@ export function registerQualityControlHandlers({
subjectId: scope.subject.subjectId,
tenantId: scope.subject.tenantId,
});
return run
? context.json(publicReplay(run), 200)
: context.json({ error: "Replay run not found" }, 404);
if (!run) return context.json({ error: "Replay run not found" }, 404);
const evidenceItemId = context.req.valid("query").evidenceItemId;
if (!evidenceItemId) return context.json(publicReplay(run), 200);
const replayItem = run.items.find((item) => item.id === evidenceItemId);
if (!replayItem) return context.json({ error: "Replay item not found" }, 404);
if (replayItem.expectedEvidenceIds.length > MAX_QUALITY_REPLAY_EVIDENCE_DETAILS) {
return context.json({ error: "Replay evidence detail limit exceeded" }, 400);
}
const evidenceItems = await publicReplayEvidenceItems({
assets,
candidateGrants: scope.candidateGrants,
item: replayItem,
knowledgeSpaceId: scope.knowledgeSpaceId,
nodes,
});
return context.json(publicReplay(run, { evidenceItemId: replayItem.id, evidenceItems }), 200);
});
app.openapi(listQualityReplaysRoute, async (context) => {
@ -537,7 +551,7 @@ export function registerQualityControlHandlers({
});
return context.json(
{
items: result.items.map(publicReplay),
items: result.items.map((item) => publicReplay(item)),
...(result.nextCursor ? { nextCursor: encodeQualityCursor(result.nextCursor) } : {}),
},
200,
@ -1004,7 +1018,96 @@ function publicBadCase(value: ProductionBadCase) {
return { ...safe, tags: [...value.tags] };
}
function publicReplay(run: QualityReplayRun) {
interface PublicReplayEvidenceItem {
readonly available: boolean;
readonly documentName?: string | undefined;
readonly matched: boolean;
readonly ordinal: number;
readonly pageNumber?: number | undefined;
readonly sectionPath: readonly string[];
readonly text?: string | undefined;
}
async function publicReplayEvidenceItems(input: {
readonly assets: Pick<DocumentAssetRepository, "get">;
readonly candidateGrants: readonly string[];
readonly item: QualityReplayRun["items"][number];
readonly knowledgeSpaceId: string;
readonly nodes: Pick<KnowledgeNodeRepository, "getManyByIdsAcrossGenerations">;
}): Promise<readonly PublicReplayEvidenceItem[]> {
const resolvedNodes = await input.nodes.getManyByIdsAcrossGenerations({
ids: input.item.expectedEvidenceIds,
knowledgeSpaceId: input.knowledgeSpaceId,
});
const nodesById = new Map(
resolvedNodes
.filter((node) => candidatePermissionAllowsNode(node, input.candidateGrants))
.map((node) => [node.id, node]),
);
const documentAssetIds = [
...new Set([...nodesById.values()].map((node) => node.documentAssetId)),
];
const resolvedAssets = await Promise.all(
documentAssetIds.map((id) =>
input.assets.get({ id, knowledgeSpaceId: input.knowledgeSpaceId }),
),
);
const assetsById = new Map(
resolvedAssets
.filter((asset): asset is NonNullable<typeof asset> =>
Boolean(asset && candidatePermissionAllowsAsset(asset, input.candidateGrants)),
)
.map((asset) => [asset.id, asset]),
);
const diff =
input.item.result && isPlainRecord(input.item.result.evidenceDiff)
? input.item.result.evidenceDiff
: {};
const missingEvidenceIds = new Set(
Array.isArray(diff.missingEvidenceIds)
? diff.missingEvidenceIds.filter((id): id is string => typeof id === "string")
: [],
);
return input.item.expectedEvidenceIds.map((id, index) => {
const node = nodesById.get(id);
if (!node) {
return {
available: false,
matched: !missingEvidenceIds.has(id),
ordinal: index + 1,
sectionPath: [],
};
}
const asset = assetsById.get(node.documentAssetId);
if (!asset) {
return {
available: false,
matched: !missingEvidenceIds.has(id),
ordinal: index + 1,
sectionPath: [],
};
}
return {
available: true,
documentName: asset.filename,
matched: !missingEvidenceIds.has(id),
ordinal: index + 1,
...(node.sourceLocation.pageNumber === undefined
? {}
: { pageNumber: node.sourceLocation.pageNumber }),
sectionPath: [...node.sourceLocation.sectionPath],
text: node.text,
};
});
}
function publicReplay(
run: QualityReplayRun,
evidence?: {
readonly evidenceItemId: string;
readonly evidenceItems: readonly PublicReplayEvidenceItem[];
},
) {
const embedding = run.frozenSnapshot.embeddingProfile;
const retrieval = run.frozenSnapshot.retrievalProfile;
const passed = run.items.filter((item) => item.state === "passed").length;
@ -1027,6 +1130,7 @@ function publicReplay(run: QualityReplayRun) {
item.result,
item.expectedEvidenceIds.length,
item.matchPolicy,
evidence?.evidenceItemId === item.id ? evidence.evidenceItems : undefined,
),
}
: {}),
@ -1076,6 +1180,7 @@ function publicReplayResult(
value: Readonly<Record<string, unknown>>,
expectedCount: number,
matchPolicy: "all" | "any",
evidenceItems?: readonly PublicReplayEvidenceItem[],
) {
const diff = isPlainRecord(value.evidenceDiff) ? value.evidenceDiff : {};
const metrics = isPlainRecord(value.metrics) ? value.metrics : {};
@ -1105,6 +1210,7 @@ function publicReplayResult(
const matchedCount = Math.max(0, expectedCount - missingCount);
return {
evidenceDiff: {
...(evidenceItems ? { evidenceItems: evidenceItems.map(publicReplayEvidenceItem) } : {}),
expectedCount,
matchedCount,
missingCount,
@ -1115,6 +1221,18 @@ function publicReplayResult(
};
}
function publicReplayEvidenceItem(item: PublicReplayEvidenceItem) {
return {
available: item.available,
...(item.documentName ? { documentName: item.documentName } : {}),
matched: item.matched,
ordinal: item.ordinal,
...(item.pageNumber === undefined ? {} : { pageNumber: item.pageNumber }),
sectionPath: [...item.sectionPath],
...(item.text ? { text: item.text } : {}),
};
}
function isPlainRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}

View File

@ -10,6 +10,16 @@ const BadCaseParams = SpaceParams.extend({ badCaseId: z.string().uuid() });
const ReplayParams = SpaceParams.extend({ runId: z.string().uuid() });
const DateTime = z.string().datetime();
const ReplayEvidenceItemSchema = z.object({
available: z.boolean(),
documentName: z.string().optional(),
matched: z.boolean(),
ordinal: z.number().int().positive(),
pageNumber: z.number().int().positive().optional(),
sectionPath: z.array(z.string()),
text: z.string().optional(),
});
export const QualityTraceHistoryQuerySchema = z
.object({
cursor: z.string().max(1000).optional(),
@ -112,6 +122,7 @@ export const ReplayRunSchema = z
result: z
.object({
evidenceDiff: z.object({
evidenceItems: z.array(ReplayEvidenceItemSchema).optional(),
expectedCount: z.number().int().nonnegative(),
matchedCount: z.number().int().nonnegative(),
missingCount: z.number().int().nonnegative(),
@ -514,7 +525,10 @@ export const getQualityReplayRoute = createRoute({
operationId: "getQualityReplay",
path: "/knowledge-spaces/{id}/quality/replay-runs/{runId}",
"x-knowledge-fs-max-response-bytes": 4 * 1024 * 1024,
request: { params: ReplayParams },
request: {
params: ReplayParams,
query: z.object({ evidenceItemId: z.string().uuid().optional() }).strict(),
},
responses: {
200: {
content: { "application/json": { schema: ReplayRunSchema } },

View File

@ -121,6 +121,7 @@ import {
zGetKnowledgeFsSpacesByControlSpaceIdQualityBadCasesQuery,
zGetKnowledgeFsSpacesByControlSpaceIdQualityBadCasesResponse,
zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath,
zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery,
zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdResponse,
zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsPath,
zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsQuery,
@ -1617,7 +1618,12 @@ export const get33 = oc
path: '/knowledge-fs/spaces/{control_space_id}/quality/replay-runs/{run_id}',
tags: ['console'],
})
.input(z.object({ params: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath }))
.input(
z.object({
params: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath,
query: zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery.optional(),
}),
)
.output(zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdResponse)
export const byRunId = {

View File

@ -2229,6 +2229,7 @@ export type KnowledgeFsTraceStageResponse = {
}
export type KnowledgeFsQualityReplayEvidenceDiff = {
evidence_items?: Array<KnowledgeFsQualityReplayEvidenceItem> | null
expected_count: number
matched_count: number
missing_count: number
@ -2248,6 +2249,16 @@ export type KnowledgeFsQualityReplayMetrics = {
total_ms?: number | null
}
export type KnowledgeFsQualityReplayEvidenceItem = {
available: boolean
document_name?: string | null
matched: boolean
ordinal: number
page_number?: number | null
section_path?: Array<string>
text?: string | null
}
export type GetKnowledgeFsWellKnownJwksJsonData = {
body?: never
path?: never
@ -3547,7 +3558,9 @@ export type GetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdData = {
control_space_id: string
run_id: string
}
query?: never
query?: {
evidence_item_id?: string
}
url: '/knowledge-fs/spaces/{control_space_id}/quality/replay-runs/{run_id}'
}

View File

@ -2872,16 +2872,6 @@ export const zKnowledgeFsTraceListResponse = z.object({
next_cursor: z.string().nullish(),
})
/**
* KnowledgeFSQualityReplayEvidenceDiff
*/
export const zKnowledgeFsQualityReplayEvidenceDiff = z.object({
expected_count: z.int().gte(0),
matched_count: z.int().gte(0),
missing_count: z.int().gte(0),
retrieved_count: z.int().gte(0),
})
/**
* KnowledgeFSQualityReplayMetrics
*/
@ -2898,6 +2888,30 @@ export const zKnowledgeFsQualityReplayMetrics = z.object({
total_ms: z.number().gte(0).nullish(),
})
/**
* KnowledgeFSQualityReplayEvidenceItem
*/
export const zKnowledgeFsQualityReplayEvidenceItem = z.object({
available: z.boolean(),
document_name: z.string().nullish(),
matched: z.boolean(),
ordinal: z.int().gte(1),
page_number: z.int().gte(1).nullish(),
section_path: z.array(z.string()).optional(),
text: z.string().nullish(),
})
/**
* KnowledgeFSQualityReplayEvidenceDiff
*/
export const zKnowledgeFsQualityReplayEvidenceDiff = z.object({
evidence_items: z.array(zKnowledgeFsQualityReplayEvidenceItem).nullish(),
expected_count: z.int().gte(0),
matched_count: z.int().gte(0),
missing_count: z.int().gte(0),
retrieved_count: z.int().gte(0),
})
/**
* KnowledgeFSQualityReplayItemResult
*/
@ -3912,6 +3926,10 @@ export const zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdPath =
run_id: z.string(),
})
export const zGetKnowledgeFsSpacesByControlSpaceIdQualityReplayRunsByRunIdQuery = z.object({
evidence_item_id: z.uuid().optional(),
})
/**
* KnowledgeFS quality replay run
*/

View File

@ -1,6 +1,6 @@
import type { ReactNode } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { render } from '@/test/console/render'
import { QualityEvaluationPanel } from '../quality/quality-evaluation-panel'
@ -94,6 +94,41 @@ const completedRun = {
updated_at: '2026-08-19T12:00:02.000Z',
} as const
const completedRunWithEvidence = {
...completedRun,
items: [
{
...completedRun.items[0],
result: {
...completedRun.items[0].result,
evidence_diff: {
...completedRun.items[0].result.evidence_diff,
evidence_items: [
{
available: true,
document_name: 'Workspace permissions.pdf',
matched: true,
ordinal: 1,
page_number: 2,
section_path: ['Permissions', 'Roles'],
text: 'Workspace owners can change member permissions.',
},
{
available: true,
document_name: 'Workspace permissions.pdf',
matched: false,
ordinal: 2,
page_number: 3,
section_path: ['Permissions', 'Limitations'],
text: 'Editors cannot promote themselves to owner.',
},
],
},
},
},
],
} as const
function renderPanel() {
const queryClient = new QueryClient({
defaultOptions: {
@ -187,4 +222,56 @@ describe('QualityEvaluationPanel', () => {
params: { control_space_id: 'space-1', run_id: 'run-1' },
})
})
it('opens evidence hit details and identifies matched and missing passages', async () => {
const user = userEvent.setup()
serviceMock.listReplays.mockResolvedValue({ data: [completedRun], next_cursor: null })
serviceMock.getReplay.mockImplementation(async (input) =>
'query' in input && input.query.evidence_item_id ? completedRunWithEvidence : completedRun,
)
renderPanel()
await user.click(
await screen.findByRole('button', {
name: 'dataset.newKnowledge.qualityPage.evaluation.viewReport',
}),
)
await screen.findByText('Who can change workspace permissions?')
await user.click(
screen.getByRole('button', {
name: /^dataset\.newKnowledge\.qualityPage\.evaluation\.openEvidenceDetails/,
}),
)
expect(
await screen.findByRole('dialog', {
name: 'dataset.newKnowledge.qualityPage.evaluation.evidenceDetailsTitle',
}),
).toBeVisible()
const matchedSection = screen.getByRole('region', {
name: 'dataset.newKnowledge.qualityPage.evaluation.passed',
})
const missingSection = screen.getByRole('region', {
name: 'dataset.newKnowledge.qualityPage.evaluation.missed',
})
expect(
within(matchedSection).getByText('Workspace owners can change member permissions.'),
).toBeVisible()
expect(
within(missingSection).getByText('Editors cannot promote themselves to owner.'),
).toBeVisible()
expect(serviceMock.getReplay).toHaveBeenCalledWith({
params: { control_space_id: 'space-1', run_id: 'run-1' },
query: { evidence_item_id: 'item-1' },
})
await user.click(
screen.getByRole('button', { name: 'dataset.newKnowledge.qualityPage.closeDialog' }),
)
expect(
screen.queryByRole('dialog', {
name: 'dataset.newKnowledge.qualityPage.evaluation.evidenceDetailsTitle',
}),
).not.toBeInTheDocument()
})
})

View File

@ -1,6 +1,9 @@
'use client'
import type { KnowledgeFsQualityReplayResponse } from '@dify/contracts/api/console/knowledge-fs/types.gen'
import type {
KnowledgeFsQualityReplayEvidenceItem,
KnowledgeFsQualityReplayResponse,
} from '@dify/contracts/api/console/knowledge-fs/types.gen'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
@ -65,6 +68,7 @@ function EvaluationReport({
runId: string
}) {
const { t } = useTranslation('dataset')
const [selectedEvidenceItemId, setSelectedEvidenceItemId] = useState<string>()
const detailOptions =
consoleQuery.knowledgeFs.spaces.byControlSpaceId.quality.replayRuns.byRunId.get.queryOptions({
input: { params: { control_space_id: knowledgeSpaceId, run_id: runId } },
@ -74,6 +78,17 @@ function EvaluationReport({
refetchInterval: (query) =>
query.state.data && activeReplayStates.has(query.state.data.state) ? 1500 : false,
})
const evidenceDetailOptions =
consoleQuery.knowledgeFs.spaces.byControlSpaceId.quality.replayRuns.byRunId.get.queryOptions({
input: {
params: { control_space_id: knowledgeSpaceId, run_id: runId },
query: selectedEvidenceItemId ? { evidence_item_id: selectedEvidenceItemId } : {},
},
})
const evidenceDetailQuery = useQuery({
...evidenceDetailOptions,
enabled: Boolean(selectedEvidenceItemId),
})
if (detailQuery.isLoading)
return (
@ -102,6 +117,23 @@ function EvaluationReport({
const run = detailQuery.data
const progress = run.summary.total === 0 ? 0 : run.summary.completed / run.summary.total
const selectedReportItem = run.items.find((item) => item.id === selectedEvidenceItemId)
const selectedEvidenceItem = evidenceDetailQuery.data?.items.find(
(item) => item.id === selectedEvidenceItemId,
)
const evidenceItems = selectedEvidenceItem?.result?.evidence_diff.evidence_items ?? []
const evidenceGroups = [
{
items: evidenceItems.filter((item) => item.matched),
matched: true,
title: t(($) => $['newKnowledge.qualityPage.evaluation.passed']),
},
{
items: evidenceItems.filter((item) => !item.matched),
matched: false,
title: t(($) => $['newKnowledge.qualityPage.evaluation.missed']),
},
]
return (
<section className="mt-3 min-w-0">
<div className="flex flex-wrap items-start justify-between gap-3">
@ -200,9 +232,26 @@ function EvaluationReport({
<span className="system-xs-regular text-text-secondary">
{t(($) => $[`newKnowledge.qualityPage.matchPolicy.${item.match_policy}`])}
</span>
<span className="system-xs-medium text-text-secondary">
{diff ? `${diff.matched_count}/${diff.expected_count}` : '—'}
</span>
{diff ? (
<Button
variant="ghost"
className="-ml-2 h-8 w-fit gap-1 px-2 text-text-accent"
aria-label={t(
($) => $['newKnowledge.qualityPage.evaluation.openEvidenceDetails'],
{
expected: diff.expected_count,
matched: diff.matched_count,
question: item.question,
},
)}
onClick={() => setSelectedEvidenceItemId(item.id)}
>
{diff.matched_count}/{diff.expected_count}
<span aria-hidden className="i-ri-arrow-right-s-line size-4" />
</Button>
) : (
<span className="system-xs-medium text-text-secondary"></span>
)}
<span className="system-xs-regular text-text-secondary">
{formatDuration(item.result?.metrics.total_ms)}
</span>
@ -236,10 +285,108 @@ function EvaluationReport({
</span>
</div>
</div>
<Dialog
open={Boolean(selectedEvidenceItemId)}
onOpenChange={(open) => {
if (!open) setSelectedEvidenceItemId(undefined)
}}
>
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-180! max-w-[calc(100vw-2rem)]! flex-col overflow-hidden! p-0!">
<div className="relative border-b border-divider-subtle px-6 pt-6 pb-5">
<DialogTitle className="pr-10 title-xl-semi-bold text-text-primary">
{t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsTitle'])}
</DialogTitle>
<DialogDescription className="mt-1.5 pr-10 system-sm-regular text-text-tertiary">
{t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsDescription'])}
</DialogDescription>
{selectedReportItem && (
<p className="mt-3 rounded-lg bg-background-section-burn px-3 py-2 system-sm-medium text-text-primary">
{selectedReportItem.question}
</p>
)}
<DialogCloseButton aria-label={t(($) => $['newKnowledge.qualityPage.closeDialog'])} />
</div>
<div className="min-h-40 flex-1 overflow-y-auto px-6 py-5">
{evidenceDetailQuery.isLoading ? (
<div className="flex min-h-40 items-center justify-center" role="status">
<Loading />
</div>
) : evidenceDetailQuery.isError ? (
<div className="flex min-h-40 flex-col items-center justify-center gap-3 text-center">
<p role="alert" className="system-sm-medium text-text-primary">
{t(($) => $['newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError'])}
</p>
<Button onClick={() => void evidenceDetailQuery.refetch()}>
{t(($) => $['newKnowledge.qualityPage.evaluation.retryLoad'])}
</Button>
</div>
) : (
<div className="space-y-6">
{evidenceGroups.map((group) => (
<section key={String(group.matched)} aria-label={group.title}>
<div className="mb-2 flex items-center gap-2">
<h3 className="system-sm-semibold text-text-primary">{group.title}</h3>
<span
className={cn(
'inline-flex min-w-5 items-center justify-center rounded-md px-1.5 py-0.5 system-2xs-medium',
group.matched
? 'bg-state-success-hover text-text-success'
: 'bg-state-destructive-hover text-text-destructive',
)}
>
{group.items.length}
</span>
</div>
{group.items.length === 0 ? (
<p className="rounded-lg border border-divider-subtle px-4 py-3 system-xs-regular text-text-tertiary">
{t(($) => $['newKnowledge.qualityPage.evaluation.noEvidenceInGroup'])}
</p>
) : (
<div className="space-y-2">
{group.items.map((evidence) => (
<EvidenceDetailCard key={evidence.ordinal} evidence={evidence} />
))}
</div>
)}
</section>
))}
</div>
)}
</div>
</DialogContent>
</Dialog>
</section>
)
}
function EvidenceDetailCard({ evidence }: { evidence: KnowledgeFsQualityReplayEvidenceItem }) {
const { t } = useTranslation('dataset')
const source = [
evidence.document_name,
evidence.section_path?.join(' / '),
evidence.page_number
? t(($) => $['newKnowledge.qualityPage.evaluation.evidencePage'], {
page: evidence.page_number,
})
: undefined,
].filter(Boolean)
return (
<article className="rounded-xl border border-divider-subtle bg-background-default p-4">
{source.length > 0 && (
<p className="mb-2 system-xs-medium text-text-tertiary">{source.join(' · ')}</p>
)}
<p className="system-sm-regular break-words whitespace-pre-wrap text-text-secondary">
{evidence.available && evidence.text
? evidence.text
: t(($) => $['newKnowledge.qualityPage.evaluation.evidenceUnavailable'])}
</p>
</article>
)
}
export function QualityEvaluationPanel({ knowledgeSpaceId }: { knowledgeSpaceId: string }) {
const { t } = useTranslation('dataset')
const queryClient = useQueryClient()

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "أسئلة مرجعية لاختبار الاسترجاع مجددًا، وحالات تحتاج إلى تحسين واردة من بيئة الإنتاج.",
"newKnowledge.qualityPage.edit": "تحرير",
"newKnowledge.qualityPage.editTitle": "تعديل السؤال المرجعي",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "قارن كل مقطع دليل متوقع بالمقاطع التي استرجعها هذا التقييم.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "تعذر تحميل تفاصيل الأدلة. حاول مرة أخرى.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "تفاصيل مطابقة الأدلة",
"newKnowledge.qualityPage.evaluation.evidencePage": "الصفحة {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "لم يعد مقطع الدليل التاريخي هذا متاحًا، أو لم يعد لديك إذن لعرضه.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "لا توجد مقاطع أدلة في هذه المجموعة.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "عرض تفاصيل الأدلة للسؤال «{{question}}»: تمت مطابقة {{matched}} من {{expected}}",
"newKnowledge.qualityPage.evidence": "الدليل",
"newKnowledge.qualityPage.evidencePlaceholder": "الصق المقطع المتوقع أو صِف الدليل المطلوب العثور عليه",
"newKnowledge.qualityPage.evidenceSelected": "عدد مقاطع الدليل المحددة: {{count}}",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Referenzfragen zum erneuten Testen des Abrufs sowie Problemfälle aus dem Produktivbetrieb.",
"newKnowledge.qualityPage.edit": "Bearbeiten",
"newKnowledge.qualityPage.editTitle": "Referenzfrage bearbeiten",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Vergleichen Sie jeden erwarteten Belegabschnitt mit den von dieser Auswertung abgerufenen Abschnitten.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Die Belegdetails konnten nicht geladen werden. Versuchen Sie es erneut.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Details zu den Belegtreffern",
"newKnowledge.qualityPage.evaluation.evidencePage": "Seite {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Dieser historische Belegabschnitt ist nicht mehr verfügbar oder Sie sind nicht mehr berechtigt, ihn anzuzeigen.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Diese Gruppe enthält keine Belegabschnitte.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Belegdetails für „{{question}}“ anzeigen: {{matched}} von {{expected}} Treffern",
"newKnowledge.qualityPage.evidence": "Beleg",
"newKnowledge.qualityPage.evidencePlaceholder": "Erwarteten Textabschnitt einfügen oder den gesuchten Beleg beschreiben",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} Belegabschnitte ausgewählt",

View File

@ -503,7 +503,12 @@
"newKnowledge.qualityPage.evaluation.duration": "Duration",
"newKnowledge.qualityPage.evaluation.emptyDescription": "Run an evaluation to see the overall evidence hit rate and the result for every golden question.",
"newKnowledge.qualityPage.evaluation.emptyTitle": "No evaluation runs yet",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compare every expected evidence passage with the passages returned by this evaluation.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Evidence details could not be loaded. Try again.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Evidence hit details",
"newKnowledge.qualityPage.evaluation.evidenceHit": "Evidence hit",
"newKnowledge.qualityPage.evaluation.evidencePage": "Page {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "This historical evidence passage is no longer available or you no longer have permission to view it.",
"newKnowledge.qualityPage.evaluation.executionFailed": "The run could not be completed. Check the knowledge retrieval configuration and run it again.",
"newKnowledge.qualityPage.evaluation.hitRate": "Evidence hit rate",
"newKnowledge.qualityPage.evaluation.loadError": "Evaluation runs could not be loaded. Try again.",
@ -513,6 +518,8 @@
"newKnowledge.qualityPage.evaluation.mode.fast": "Fast",
"newKnowledge.qualityPage.evaluation.mode.research": "Research",
"newKnowledge.qualityPage.evaluation.modeLabel": "Retrieval mode",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "No evidence passages in this group.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "View evidence details for {{question}}: {{matched}} of {{expected}} hit",
"newKnowledge.qualityPage.evaluation.passed": "Hit",
"newKnowledge.qualityPage.evaluation.policy": "Match policy",
"newKnowledge.qualityPage.evaluation.profileRevision": "Retrieval profile",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Preguntas de referencia para volver a probar la recuperación y casos problemáticos notificados desde el entorno de producción.",
"newKnowledge.qualityPage.edit": "Editar",
"newKnowledge.qualityPage.editTitle": "Editar pregunta de referencia",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compara cada fragmento de evidencia esperado con los fragmentos recuperados por esta evaluación.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "No se pudieron cargar los detalles de la evidencia. Inténtalo de nuevo.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalles de coincidencia de evidencia",
"newKnowledge.qualityPage.evaluation.evidencePage": "Página {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Este fragmento de evidencia histórico ya no está disponible o ya no tienes permiso para verlo.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "No hay fragmentos de evidencia en este grupo.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Ver los detalles de evidencia de «{{question}}»: {{matched}} de {{expected}} coincidencias",
"newKnowledge.qualityPage.evidence": "Evidencia",
"newKnowledge.qualityPage.evidencePlaceholder": "Pega el fragmento esperado o describe la evidencia que buscas",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} fragmentos de evidencia seleccionados",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "پرسش‌های مرجع برای آزمایش دوبارهٔ بازیابی و موارد نیازمند بهبود گزارش‌شده از محیط عملیاتی.",
"newKnowledge.qualityPage.edit": "ویرایش",
"newKnowledge.qualityPage.editTitle": "ویرایش سوال مرجع",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "هر بخش شاهد مورد انتظار را با بخش‌های بازیابی‌شده در این ارزیابی مقایسه کنید.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "جزئیات شاهد بارگیری نشد. دوباره تلاش کنید.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "جزئیات تطبیق شاهد",
"newKnowledge.qualityPage.evaluation.evidencePage": "صفحه {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "این بخش شاهد تاریخی دیگر در دسترس نیست یا دیگر مجوز مشاهدهٔ آن را ندارید.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "در این گروه هیچ بخش شاهدی وجود ندارد.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "مشاهدهٔ جزئیات شاهد برای «{{question}}»: {{matched}} مورد از {{expected}} مورد تطبیق یافت",
"newKnowledge.qualityPage.evidence": "شاهد",
"newKnowledge.qualityPage.evidencePlaceholder": "بخش مورد انتظار را جای‌گذاری کنید یا شاهد مورد نظر را توضیح دهید",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} بخش شاهد انتخاب شد",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Questions de référence pour tester à nouveau la recherche et cas problématiques signalés en production.",
"newKnowledge.qualityPage.edit": "Modifier",
"newKnowledge.qualityPage.editTitle": "Modifier la question de référence",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Comparez chaque extrait attendu aux extraits retrouvés lors de cette évaluation.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Impossible de charger les détails des extraits. Réessayez.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Détails des extraits retrouvés",
"newKnowledge.qualityPage.evaluation.evidencePage": "Page {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Cet extrait historique nest plus disponible ou vous nêtes plus autorisé à le consulter.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Aucun extrait dans ce groupe.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Afficher les détails des extraits pour « {{question}} » : {{matched}} sur {{expected}} retrouvés",
"newKnowledge.qualityPage.evidence": "Extrait de référence",
"newKnowledge.qualityPage.evidencePlaceholder": "Collez lextrait attendu ou décrivez lextrait à rechercher",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} extraits de référence sélectionnés",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "रिट्रीवल को दोबारा जाँचने के लिए संदर्भ प्रश्न और प्रोडक्शन परिवेश से रिपोर्ट किए गए सुधार योग्य मामले।",
"newKnowledge.qualityPage.edit": "संपादित करें",
"newKnowledge.qualityPage.editTitle": "संदर्भ प्रश्न संपादित करें",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "हर अपेक्षित साक्ष्य अंश की तुलना इस मूल्यांकन में मिले अंशों से करें।",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "साक्ष्य विवरण लोड नहीं हो सका। फिर से कोशिश करें।",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "साक्ष्य मिलान का विवरण",
"newKnowledge.qualityPage.evaluation.evidencePage": "पृष्ठ {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "यह पुराना साक्ष्य अंश अब उपलब्ध नहीं है या अब आपको इसे देखने की अनुमति नहीं है।",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "इस समूह में कोई साक्ष्य अंश नहीं है।",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "“{{question}}” के साक्ष्य विवरण देखें: {{expected}} में से {{matched}} मिले",
"newKnowledge.qualityPage.evidence": "साक्ष्य",
"newKnowledge.qualityPage.evidencePlaceholder": "अपेक्षित अंश चिपकाएँ या खोजे जाने वाले साक्ष्य का वर्णन करें",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} साक्ष्य अंश चुने गए",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Pertanyaan referensi untuk menguji kembali pengambilan, serta kasus bermasalah yang dilaporkan dari lingkungan produksi.",
"newKnowledge.qualityPage.edit": "Sunting",
"newKnowledge.qualityPage.editTitle": "Edit pertanyaan referensi",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Bandingkan setiap bagian bukti yang diharapkan dengan bagian yang ditemukan oleh evaluasi ini.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Detail bukti tidak dapat dimuat. Coba lagi.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detail kecocokan bukti",
"newKnowledge.qualityPage.evaluation.evidencePage": "Halaman {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Bagian bukti historis ini sudah tidak tersedia atau Anda tidak lagi memiliki izin untuk melihatnya.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Tidak ada bagian bukti dalam grup ini.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Lihat detail bukti untuk “{{question}}”: {{matched}} dari {{expected}} cocok",
"newKnowledge.qualityPage.evidence": "Bukti",
"newKnowledge.qualityPage.evidencePlaceholder": "Tempel bagian yang diharapkan atau jelaskan bukti yang ingin dicari",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} bagian bukti dipilih",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Domande di riferimento per verificare nuovamente il recupero e casi problematici segnalati nellambiente di produzione.",
"newKnowledge.qualityPage.edit": "Modifica",
"newKnowledge.qualityPage.editTitle": "Modifica la domanda di riferimento",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Confronta ogni passaggio di riscontro previsto con i passaggi recuperati da questa valutazione.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Impossibile caricare i dettagli dei riscontri. Riprova.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Dettagli dei riscontri trovati",
"newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Questo passaggio storico non è più disponibile oppure non disponi più dellautorizzazione per visualizzarlo.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Nessun passaggio di riscontro in questo gruppo.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Visualizza i dettagli dei riscontri per “{{question}}”: {{matched}} su {{expected}} trovati",
"newKnowledge.qualityPage.evidence": "Riscontro",
"newKnowledge.qualityPage.evidencePlaceholder": "Incolla il passaggio previsto o descrivi il riscontro da trovare",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} passaggi di riscontro selezionati",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "検索を再テストするための参考質問と、本番環境から報告された問題ケースを管理します。",
"newKnowledge.qualityPage.edit": "編集",
"newKnowledge.qualityPage.editTitle": "参考質問を編集",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "期待される各根拠段落と、この評価で取得された段落を比較します。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "根拠の詳細を読み込めませんでした。もう一度お試しください。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "根拠のヒット詳細",
"newKnowledge.qualityPage.evaluation.evidencePage": "{{page}} ページ",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "この過去の根拠段落は利用できないか、現在は表示する権限がありません。",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "このグループに根拠段落はありません。",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "「{{question}}」の根拠詳細を表示:{{expected}} 件中 {{matched}} 件がヒット",
"newKnowledge.qualityPage.evidence": "根拠",
"newKnowledge.qualityPage.evidencePlaceholder": "期待する文章を貼り付けるか、検索する根拠を説明してください",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} 件の根拠となる文章を選択済み",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "검색을 다시 테스트하기 위한 참고 질문과 운영 환경에서 보고된 문제 사례를 관리합니다.",
"newKnowledge.qualityPage.edit": "편집",
"newKnowledge.qualityPage.editTitle": "참고 질문 수정",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "각 예상 근거 문단과 이번 평가에서 검색된 문단을 비교합니다.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "근거 세부 정보를 불러오지 못했습니다. 다시 시도해 주세요.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "근거 적중 세부 정보",
"newKnowledge.qualityPage.evaluation.evidencePage": "{{page}}페이지",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "이 과거 근거 문단은 더 이상 존재하지 않거나 현재 볼 수 있는 권한이 없습니다.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "이 그룹에는 근거 문단이 없습니다.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "{{question}}’의 근거 세부 정보 보기: {{expected}}개 중 {{matched}}개 적중",
"newKnowledge.qualityPage.evidence": "근거",
"newKnowledge.qualityPage.evidencePlaceholder": "예상 문단을 붙여 넣거나 찾을 근거를 설명하세요",
"newKnowledge.qualityPage.evidenceSelected": "근거 문단 {{count}}개 선택됨",

View File

@ -367,6 +367,13 @@
"newKnowledge.providerNotConfiguredDescription": "ເພີ່ມລະຫັດ API ຫຼື URL ຈຸດສິ້ນສຸດເພື່ອລວບລວມຂໍ້ມູນເວັບໄຊທ໌ດ້ວຍ {{provider}}.",
"newKnowledge.providerUnavailable": "ຜູ້ໃຫ້ບໍລິການນີ້ບໍ່ສາມາດໃຊ້ໄດ້.",
"newKnowledge.quality": "ຄຸນະພາບ",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "ປຽບທຽບທຸກວັກຫຼັກຖານທີ່ຄາດໄວ້ກັບວັກທີ່ການປະເມີນນີ້ຄົ້ນພົບ.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "ບໍ່ສາມາດໂຫຼດລາຍລະອຽດຫຼັກຖານໄດ້. ລອງໃໝ່.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "ລາຍລະອຽດການພົບຫຼັກຖານ",
"newKnowledge.qualityPage.evaluation.evidencePage": "ໜ້າ {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "ວັກຫຼັກຖານເກົ່ານີ້ບໍ່ມີແລ້ວ ຫຼື ທ່ານບໍ່ມີສິດເບິ່ງອີກຕໍ່ໄປ.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "ບໍ່ມີວັກຫຼັກຖານໃນກຸ່ມນີ້.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "ເບິ່ງລາຍລະອຽດຫຼັກຖານສຳລັບ “{{question}}”: ພົບ {{matched}} ຈາກ {{expected}}",
"newKnowledge.qualityPage.ignore": "ບໍ່ສົນໃຈ",
"newKnowledge.qualityPage.reasonValues.lowScore": "ຄະແນນຕໍ່າ",
"newKnowledge.reCrawl": "ກວາດຄືນ",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Referentievragen om het ophalen opnieuw te testen en probleemgevallen die vanuit de productieomgeving zijn gemeld.",
"newKnowledge.qualityPage.edit": "Bewerken",
"newKnowledge.qualityPage.editTitle": "Referentievraag bewerken",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Vergelijk elke verwachte bewijs passage met de passages die door deze evaluatie zijn opgehaald.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "De bewijsdetails konden niet worden geladen. Probeer het opnieuw.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Details van bewijsresultaten",
"newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Deze historische bewijs passage is niet meer beschikbaar of u hebt geen toestemming meer om deze te bekijken.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Deze groep bevat geen bewijs passages.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Bewijsdetails voor {{question}} bekijken: {{matched}} van {{expected}} gevonden",
"newKnowledge.qualityPage.evidence": "Bewijs",
"newKnowledge.qualityPage.evidencePlaceholder": "Plak de verwachte passage of beschrijf het te vinden bewijs",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} bewijsfragmenten geselecteerd",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Pytania referencyjne do ponownego testowania wyszukiwania oraz przypadki do poprawy zgłoszone w środowisku produkcyjnym.",
"newKnowledge.qualityPage.edit": "Edytuj",
"newKnowledge.qualityPage.editTitle": "Edytuj pytanie referencyjne",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Porównaj każdy oczekiwany fragment źródłowy z fragmentami pobranymi w tej ocenie.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Nie udało się wczytać szczegółów fragmentów. Spróbuj ponownie.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Szczegóły trafień fragmentów",
"newKnowledge.qualityPage.evaluation.evidencePage": "Strona {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Ten historyczny fragment nie jest już dostępny albo nie masz już uprawnień do jego wyświetlenia.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "W tej grupie nie ma fragmentów źródłowych.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Wyświetl szczegóły fragmentów dla „{{question}}”: trafiono {{matched}} z {{expected}}",
"newKnowledge.qualityPage.evidence": "Fragment źródłowy",
"newKnowledge.qualityPage.evidencePlaceholder": "Wklej oczekiwany fragment lub opisz fragment źródłowy do znalezienia",
"newKnowledge.qualityPage.evidenceSelected": "Liczba wybranych fragmentów źródłowych: {{count}}",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Perguntas de referência para testar novamente a recuperação e casos problemáticos relatados no ambiente de produção.",
"newKnowledge.qualityPage.edit": "Editar",
"newKnowledge.qualityPage.editTitle": "Editar pergunta de referência",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compare cada trecho de evidência esperado com os trechos recuperados nesta avaliação.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Não foi possível carregar os detalhes das evidências. Tente novamente.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalhes das evidências encontradas",
"newKnowledge.qualityPage.evaluation.evidencePage": "Página {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Este trecho de evidência histórico não está mais disponível ou você não tem mais permissão para visualizá-lo.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Não há trechos de evidência neste grupo.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Ver detalhes das evidências de “{{question}}”: {{matched}} de {{expected}} encontradas",
"newKnowledge.qualityPage.evidence": "Evidência",
"newKnowledge.qualityPage.evidencePlaceholder": "Cole o trecho esperado ou descreva a evidência que deseja encontrar",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} trechos de evidência selecionados",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Întrebări de referință pentru a retesta regăsirea și cazuri problematice raportate din mediul de producție.",
"newKnowledge.qualityPage.edit": "Editează",
"newKnowledge.qualityPage.editTitle": "Editați întrebarea de referință",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Compară fiecare fragment de dovadă așteptat cu fragmentele găsite de această evaluare.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Detaliile dovezilor nu au putut fi încărcate. Încearcă din nou.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Detalii despre dovezile găsite",
"newKnowledge.qualityPage.evaluation.evidencePage": "Pagina {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Acest fragment istoric nu mai este disponibil sau nu mai ai permisiunea de a-l vedea.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Nu există fragmente de dovadă în acest grup.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Vezi detaliile dovezilor pentru „{{question}}”: {{matched}} din {{expected}} găsite",
"newKnowledge.qualityPage.evidence": "Dovadă",
"newKnowledge.qualityPage.evidencePlaceholder": "Lipește fragmentul așteptat sau descrie dovada de găsit",
"newKnowledge.qualityPage.evidenceSelected": "Au fost selectate {{count}} fragmente de dovadă",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Эталонные вопросы для повторной проверки поиска и проблемные случаи из рабочей среды.",
"newKnowledge.qualityPage.edit": "Править",
"newKnowledge.qualityPage.editTitle": "Редактировать эталонный вопрос",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Сравните каждый ожидаемый подтверждающий фрагмент с фрагментами, найденными при этой оценке.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Не удалось загрузить сведения о фрагментах. Повторите попытку.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Сведения о найденных фрагментах",
"newKnowledge.qualityPage.evaluation.evidencePage": "Страница {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Этот исторический фрагмент больше недоступен или у вас больше нет разрешения на его просмотр.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "В этой группе нет подтверждающих фрагментов.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Показать сведения о фрагментах для «{{question}}»: найдено {{matched}} из {{expected}}",
"newKnowledge.qualityPage.evidence": "Подтверждающий фрагмент",
"newKnowledge.qualityPage.evidencePlaceholder": "Вставьте ожидаемый фрагмент или опишите, какой подтверждающий фрагмент нужно найти",
"newKnowledge.qualityPage.evidenceSelected": "Выбрано подтверждающих фрагментов: {{count}}",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Referenčna vprašanja za ponovno preverjanje pridobivanja in problematični primeri, prijavljeni v produkcijskem okolju.",
"newKnowledge.qualityPage.edit": "Uredi",
"newKnowledge.qualityPage.editTitle": "Uredi referenčno vprašanje",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Primerjajte vsak pričakovani referenčni odlomek z odlomki, ki jih je našlo to vrednotenje.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Podrobnosti odlomkov ni bilo mogoče naložiti. Poskusite znova.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Podrobnosti zadetkov odlomkov",
"newKnowledge.qualityPage.evaluation.evidencePage": "Stran {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Ta zgodovinski odlomek ni več na voljo ali pa nimate več dovoljenja za ogled.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "V tej skupini ni referenčnih odlomkov.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Prikaži podrobnosti odlomkov za »{{question}}«: {{matched}} od {{expected}} zadetkov",
"newKnowledge.qualityPage.evidence": "Referenčni odlomek",
"newKnowledge.qualityPage.evidencePlaceholder": "Prilepite pričakovani odlomek ali opišite iskani referenčni odlomek",
"newKnowledge.qualityPage.evidenceSelected": "Izbranih referenčnih odlomkov: {{count}}",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "คำถามอ้างอิงสำหรับทดสอบการค้นคืนอีกครั้ง และกรณีที่ต้องปรับปรุงซึ่งรายงานจากระบบที่ใช้งานจริง",
"newKnowledge.qualityPage.edit": "แก้ไข",
"newKnowledge.qualityPage.editTitle": "แก้ไขคำถามอ้างอิง",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "เปรียบเทียบข้อความหลักฐานที่คาดหวังแต่ละรายการกับข้อความที่การประเมินนี้ค้นคืนมา",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "ไม่สามารถโหลดรายละเอียดหลักฐานได้ โปรดลองอีกครั้ง",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "รายละเอียดการพบหลักฐาน",
"newKnowledge.qualityPage.evaluation.evidencePage": "หน้า {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "ข้อความหลักฐานย้อนหลังนี้ไม่มีอยู่แล้ว หรือคุณไม่มีสิทธิ์ดูอีกต่อไป",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "ไม่มีข้อความหลักฐานในกลุ่มนี้",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "ดูรายละเอียดหลักฐานสำหรับ “{{question}}”: พบ {{matched}} จาก {{expected}}",
"newKnowledge.qualityPage.evidence": "หลักฐาน",
"newKnowledge.qualityPage.evidencePlaceholder": "วางข้อความที่คาดหวังหรืออธิบายหลักฐานที่ต้องการค้นหา",
"newKnowledge.qualityPage.evidenceSelected": "เลือกข้อความหลักฐานแล้ว {{count}} รายการ",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Getirme işlemini yeniden test eden referans soruları ve canlı ortamdan bildirilen sorunlu durumlar.",
"newKnowledge.qualityPage.edit": "Düzenle",
"newKnowledge.qualityPage.editTitle": "Referans sorusunu düzenle",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Beklenen her kanıt bölümünü bu değerlendirmede getirilen bölümlerle karşılaştırın.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Kanıt ayrıntıları yüklenemedi. Tekrar deneyin.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Kanıt eşleşme ayrıntıları",
"newKnowledge.qualityPage.evaluation.evidencePage": "Sayfa {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Bu geçmiş kanıt bölümü artık kullanılamıyor veya artık görüntüleme izniniz yok.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Bu grupta kanıt bölümü yok.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "“{{question}}” için kanıt ayrıntılarını görüntüle: {{expected}} kanıttan {{matched}} tanesi eşleşti",
"newKnowledge.qualityPage.evidence": "Kanıt",
"newKnowledge.qualityPage.evidencePlaceholder": "Beklenen bölümü yapıştırın veya bulunacak kanıtııklayın",
"newKnowledge.qualityPage.evidenceSelected": "{{count}} kanıt bölümü seçildi",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Еталонні запитання для повторної перевірки пошуку та проблемні випадки з робочого середовища.",
"newKnowledge.qualityPage.edit": "Редагувати",
"newKnowledge.qualityPage.editTitle": "Редагувати еталонне запитання",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "Порівняйте кожен очікуваний підтверджувальний уривок з уривками, знайденими під час цього оцінювання.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Не вдалося завантажити відомості про уривки. Спробуйте ще раз.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Відомості про знайдені уривки",
"newKnowledge.qualityPage.evaluation.evidencePage": "Сторінка {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Цей історичний уривок більше недоступний або ви більше не маєте дозволу на його перегляд.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "У цій групі немає підтверджувальних уривків.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Показати відомості про уривки для «{{question}}»: знайдено {{matched}} з {{expected}}",
"newKnowledge.qualityPage.evidence": "Підтверджувальний уривок",
"newKnowledge.qualityPage.evidencePlaceholder": "Вставте очікуваний уривок або опишіть, який підтверджувальний уривок потрібно знайти",
"newKnowledge.qualityPage.evidenceSelected": "Вибрано підтверджувальних уривків: {{count}}",

View File

@ -491,6 +491,13 @@
"newKnowledge.qualityPage.description": "Các câu hỏi tham chiếu được chạy lại qua hệ thống truy xuất; các trường hợp cần cải thiện đến từ báo cáo trong môi trường thực tế.",
"newKnowledge.qualityPage.edit": "Chỉnh sửa",
"newKnowledge.qualityPage.editTitle": "Chỉnh sửa câu hỏi tham chiếu",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "So sánh từng đoạn bằng chứng mong đợi với các đoạn được truy xuất trong lần đánh giá này.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "Không thể tải chi tiết bằng chứng. Hãy thử lại.",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "Chi tiết bằng chứng được tìm thấy",
"newKnowledge.qualityPage.evaluation.evidencePage": "Trang {{page}}",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "Đoạn bằng chứng lịch sử này không còn tồn tại hoặc bạn không còn quyền xem.",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "Không có đoạn bằng chứng nào trong nhóm này.",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "Xem chi tiết bằng chứng cho “{{question}}”: tìm thấy {{matched}}/{{expected}}",
"newKnowledge.qualityPage.evidence": "Bằng chứng",
"newKnowledge.qualityPage.evidencePlaceholder": "Dán đoạn văn dự kiến hoặc mô tả bằng chứng cần tìm",
"newKnowledge.qualityPage.evidenceSelected": "Đã chọn {{count}} đoạn bằng chứng",

View File

@ -503,7 +503,12 @@
"newKnowledge.qualityPage.evaluation.duration": "耗时",
"newKnowledge.qualityPage.evaluation.emptyDescription": "运行一次评测后,可在这里查看整体命中率和每个问题的证据命中结果。",
"newKnowledge.qualityPage.evaluation.emptyTitle": "暂无评测任务",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "逐条对比预期证据段落与本次评测实际检索到的段落。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "无法加载证据明细,请重试。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "证据命中明细",
"newKnowledge.qualityPage.evaluation.evidenceHit": "证据命中",
"newKnowledge.qualityPage.evaluation.evidencePage": "第 {{page}} 页",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "该历史证据段落已不存在,或你当前没有权限查看。",
"newKnowledge.qualityPage.evaluation.executionFailed": "任务执行失败。请检查知识库检索配置后重新运行。",
"newKnowledge.qualityPage.evaluation.hitRate": "证据命中率",
"newKnowledge.qualityPage.evaluation.loadError": "无法加载评测任务,请重试。",
@ -513,6 +518,8 @@
"newKnowledge.qualityPage.evaluation.mode.fast": "快速检索",
"newKnowledge.qualityPage.evaluation.mode.research": "研究检索",
"newKnowledge.qualityPage.evaluation.modeLabel": "检索模式",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "该分组中没有证据段落。",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "查看问题「{{question}}」的证据明细:命中 {{matched}}/{{expected}}",
"newKnowledge.qualityPage.evaluation.passed": "已命中",
"newKnowledge.qualityPage.evaluation.policy": "匹配策略",
"newKnowledge.qualityPage.evaluation.profileRevision": "检索配置版本",

View File

@ -494,6 +494,13 @@
"newKnowledge.qualityPage.description": "用於重新測試檢索效果的參考問題,以及正式環境回報的問題案例。",
"newKnowledge.qualityPage.edit": "編輯",
"newKnowledge.qualityPage.editTitle": "編輯參考問題",
"newKnowledge.qualityPage.evaluation.evidenceDetailsDescription": "逐條比較預期依據段落與本次評測實際檢索到的段落。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsLoadError": "無法載入依據明細,請重試。",
"newKnowledge.qualityPage.evaluation.evidenceDetailsTitle": "依據命中明細",
"newKnowledge.qualityPage.evaluation.evidencePage": "第 {{page}} 頁",
"newKnowledge.qualityPage.evaluation.evidenceUnavailable": "該歷史依據段落已不存在,或你目前沒有權限檢視。",
"newKnowledge.qualityPage.evaluation.noEvidenceInGroup": "此群組中沒有依據段落。",
"newKnowledge.qualityPage.evaluation.openEvidenceDetails": "查看問題「{{question}}」的依據明細:命中 {{matched}}/{{expected}}",
"newKnowledge.qualityPage.evidence": "依據",
"newKnowledge.qualityPage.evidencePlaceholder": "貼上預期段落,或描述要尋找的依據",
"newKnowledge.qualityPage.evidenceSelected": "已選擇 {{count}} 個依據段落",