mirror of
https://github.com/langgenius/dify.git
synced 2026-09-05 08:48:10 +08:00
feat(quality): show replay evidence details
This commit is contained in:
parent
53df42c890
commit
0391141936
@ -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)
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 5,
|
||||
"subtreeTree": "04565f7a5d363ef4ccb11547e3fa148f99b42ca0",
|
||||
"openapiSha256": "c9de381284acb183e947fe4d6b019135223511c4fed35b91e966d808ea3a2bf4",
|
||||
"subtreeTree": "05e402ad162663945d482ebfa7c5221e73347364",
|
||||
"openapiSha256": "2cf348c68bbe65dd51bbde9a0a4f91398beeebd79e89e9288c9386b26ae09796",
|
||||
"capabilityV2AuthManifestSha256": "fc0a47e23cce12544882f0298522b4933002e892b84ce1815df7e81d36a7a0c7",
|
||||
"capabilityV2AuthTestVectorSha256": "ae0de37b1ff05c40f905cf17a7b410d8971acacf64db07d5ee3d6fecfa559ce3",
|
||||
"productOperationManifestSha256": "11f801a41805b250f17766935ee9cc7fc93c3ef6489503ba6297673b73362b7d",
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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":
|
||||
|
||||
@ -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 == [
|
||||
|
||||
@ -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.
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 } },
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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}'
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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}}",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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}} بخش شاهد انتخاب شد",
|
||||
|
||||
@ -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 n’est 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 l’extrait attendu ou décrivez l’extrait à rechercher",
|
||||
"newKnowledge.qualityPage.evidenceSelected": "{{count}} extraits de référence sélectionnés",
|
||||
|
||||
@ -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}} साक्ष्य अंश चुने गए",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -491,6 +491,13 @@
|
||||
"newKnowledge.qualityPage.description": "Domande di riferimento per verificare nuovamente il recupero e casi problematici segnalati nell’ambiente 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ù dell’autorizzazione 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",
|
||||
|
||||
@ -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}} 件の根拠となる文章を選択済み",
|
||||
|
||||
@ -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}}개 선택됨",
|
||||
|
||||
@ -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": "ກວາດຄືນ",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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}}",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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ă",
|
||||
|
||||
@ -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}}",
|
||||
|
||||
@ -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}}",
|
||||
|
||||
@ -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}} รายการ",
|
||||
|
||||
@ -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ı açıklayın",
|
||||
"newKnowledge.qualityPage.evidenceSelected": "{{count}} kanıt bölümü seçildi",
|
||||
|
||||
@ -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}}",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "检索配置版本",
|
||||
|
||||
@ -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}} 個依據段落",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user