feat: 增加 Agent Excel 批量测试
This commit is contained in:
@@ -759,6 +759,7 @@ async function clearFeishuCache() {
|
||||
<template v-if="activeMenu === 'prompt'">
|
||||
<AgentManagementView
|
||||
:preview-knowledge-id="previewKnowledgeId"
|
||||
:can-batch="can('prompt.batch')"
|
||||
@consumed-preview="previewKnowledgeId = null"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
|
||||
|
||||
import { api } from "../services/api";
|
||||
import type { AgentBatchItem, AgentBatchJob, AgentBatchJobDetail } from "../types/api";
|
||||
import AdminPagination from "./AdminPagination.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
config: Record<string, unknown>;
|
||||
modelName: string;
|
||||
knowledgeSummary: string;
|
||||
}>();
|
||||
|
||||
const fileInput = ref<HTMLInputElement | null>(null);
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const taskName = ref("");
|
||||
const creating = ref(false);
|
||||
const loading = ref(false);
|
||||
const exportingId = ref<number | null>(null);
|
||||
const jobs = ref<AgentBatchJob[]>([]);
|
||||
const pager = reactive({ page: 1, pageSize: 10, total: 0 });
|
||||
const detailOpen = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const selectedJob = ref<AgentBatchJobDetail | null>(null);
|
||||
const detailPager = reactive({ page: 1, pageSize: 20, total: 0 });
|
||||
let pollTimer: number | null = null;
|
||||
|
||||
const hasRunningJob = computed(() => jobs.value.some((job) => ["pending", "running"].includes(job.status)));
|
||||
|
||||
onMounted(() => void loadJobs());
|
||||
onBeforeUnmount(stopPolling);
|
||||
|
||||
async function loadJobs(page = pager.page, pageSize = pager.pageSize, quiet = false) {
|
||||
if (!quiet) loading.value = true;
|
||||
try {
|
||||
const result = await api.agentBatchJobs({ page, pageSize });
|
||||
jobs.value = result.items;
|
||||
Object.assign(pager, { page: result.page, pageSize: result.pageSize, total: result.total });
|
||||
schedulePolling();
|
||||
if (selectedJob.value && detailOpen.value) await loadDetail(selectedJob.value.id, detailPager.page, detailPager.pageSize, true);
|
||||
} catch (error) {
|
||||
if (!quiet) ElMessage.error(errorMessage(error, "批量测试任务加载失败"));
|
||||
} finally {
|
||||
if (!quiet) loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function schedulePolling() {
|
||||
stopPolling();
|
||||
if (!hasRunningJob.value) return;
|
||||
pollTimer = window.setTimeout(() => void loadJobs(pager.page, pager.pageSize, true), 2500);
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (pollTimer !== null) window.clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
|
||||
function selectFile(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
selectedFile.value = input.files?.[0] || null;
|
||||
if (selectedFile.value && !taskName.value.trim()) taskName.value = selectedFile.value.name.replace(/\.xlsx$/i, "");
|
||||
}
|
||||
|
||||
function clearFile() {
|
||||
selectedFile.value = null;
|
||||
if (fileInput.value) fileInput.value.value = "";
|
||||
}
|
||||
|
||||
async function downloadTemplate() {
|
||||
try {
|
||||
await api.downloadAgentBatchTemplate();
|
||||
} catch (error) {
|
||||
ElMessage.error(errorMessage(error, "模板下载失败"));
|
||||
}
|
||||
}
|
||||
|
||||
async function createJob() {
|
||||
if (!selectedFile.value) return ElMessage.warning("请先选择填写好的 Excel 文件");
|
||||
if (!String(props.config.modelId || "")) return ElMessage.warning("请先在调试配置中选择模型");
|
||||
creating.value = true;
|
||||
try {
|
||||
const created = await api.createAgentBatch(selectedFile.value, { ...props.config, name: taskName.value.trim() });
|
||||
ElMessage.success(`已创建批量测试任务,共 ${created.totalCount} 个问题`);
|
||||
clearFile();
|
||||
taskName.value = "";
|
||||
await loadJobs(1, pager.pageSize);
|
||||
await openDetail(created);
|
||||
} catch (error) {
|
||||
ElMessage.error(errorMessage(error, "批量测试任务创建失败"));
|
||||
} finally {
|
||||
creating.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function openDetail(job: AgentBatchJob) {
|
||||
detailOpen.value = true;
|
||||
detailPager.page = 1;
|
||||
await loadDetail(job.id, 1, detailPager.pageSize);
|
||||
}
|
||||
|
||||
async function loadDetail(jobId: number, page = detailPager.page, pageSize = detailPager.pageSize, quiet = false) {
|
||||
if (!quiet) detailLoading.value = true;
|
||||
try {
|
||||
const result = await api.agentBatchJob(jobId, { page, pageSize });
|
||||
selectedJob.value = result;
|
||||
Object.assign(detailPager, { page: result.items.page, pageSize: result.items.pageSize, total: result.items.total });
|
||||
} catch (error) {
|
||||
if (!quiet) ElMessage.error(errorMessage(error, "任务详情加载失败"));
|
||||
} finally {
|
||||
if (!quiet) detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function exportJob(job: AgentBatchJob) {
|
||||
exportingId.value = job.id;
|
||||
try {
|
||||
await api.exportAgentBatch(job.id);
|
||||
ElMessage.success("测试结果已导出");
|
||||
} catch (error) {
|
||||
ElMessage.error(errorMessage(error, "结果导出失败"));
|
||||
} finally {
|
||||
exportingId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelJob(job: AgentBatchJob) {
|
||||
try {
|
||||
await ElMessageBox.confirm("取消后尚未开始的问题不会继续生成,已完成结果仍可导出。", "取消批量测试", {
|
||||
type: "warning", confirmButtonText: "确认取消", cancelButtonText: "继续执行",
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api.cancelAgentBatch(job.id);
|
||||
ElMessage.success("批量测试已取消");
|
||||
await loadJobs(pager.page, pager.pageSize);
|
||||
} catch (error) {
|
||||
ElMessage.error(errorMessage(error, "取消失败"));
|
||||
}
|
||||
}
|
||||
|
||||
function statusLabel(status: string) {
|
||||
return ({ pending: "等待执行", running: "生成中", completed: "已完成", completed_with_errors: "部分失败", cancelled: "已取消", success: "成功", failed: "失败" } as Record<string, string>)[status] || status;
|
||||
}
|
||||
|
||||
function statusType(status: string) {
|
||||
if (["completed", "success"].includes(status)) return "success";
|
||||
if (["completed_with_errors", "failed"].includes(status)) return "danger";
|
||||
if (status === "running") return "primary";
|
||||
if (status === "cancelled") return "info";
|
||||
return "warning";
|
||||
}
|
||||
|
||||
function formatDate(value?: string | null) {
|
||||
return value ? new Date(value).toLocaleString("zh-CN", { hour12: false }) : "—";
|
||||
}
|
||||
|
||||
function itemDuration(item: AgentBatchItem) {
|
||||
return item.durationMs == null ? "—" : `${(item.durationMs / 1000).toFixed(1)} 秒`;
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown, fallback: string) {
|
||||
return error instanceof Error ? error.message : fallback;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="agent-batch-panel">
|
||||
<div class="agent-section-intro">
|
||||
<div><h3>Excel 批量测试</h3><p>使用当前编辑框中的提示词、调试模型、知识库和生成参数创建配置快照,后台逐条生成答案。</p></div>
|
||||
<el-button @click="downloadTemplate">下载导入模板</el-button>
|
||||
</div>
|
||||
|
||||
<section class="agent-batch-create-card">
|
||||
<div class="agent-batch-config-summary">
|
||||
<span><small>模型</small><strong>{{ modelName }}</strong></span>
|
||||
<span><small>知识库</small><strong>{{ knowledgeSummary }}</strong></span>
|
||||
<span><small>单次上限</small><strong>200 个问题</strong></span>
|
||||
</div>
|
||||
<div class="agent-batch-upload-row">
|
||||
<label class="agent-batch-name"><span>任务名称</span><input v-model="taskName" maxlength="160" placeholder="例如:8月主提示词回归测试" /></label>
|
||||
<label class="agent-batch-file" :class="{ selected: selectedFile }">
|
||||
<input ref="fileInput" type="file" accept=".xlsx" @change="selectFile" />
|
||||
<span>{{ selectedFile?.name || "选择填写好的 Excel 文件" }}</span>
|
||||
<small>{{ selectedFile ? `${(selectedFile.size / 1024).toFixed(1)} KB` : "仅支持 .xlsx,最大 5MB" }}</small>
|
||||
</label>
|
||||
<el-button v-if="selectedFile" @click="clearFile">移除</el-button>
|
||||
<el-button type="primary" :loading="creating" :disabled="!selectedFile" @click="createJob">开始批量测试</el-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="agent-batch-list-head"><div><h4>测试记录</h4><p>任务在后台执行,离开页面不会中断。</p></div><el-button :loading="loading" @click="loadJobs()">刷新</el-button></div>
|
||||
<el-table v-loading="loading" :data="jobs" stripe>
|
||||
<el-table-column label="任务" min-width="190"><template #default="{ row }"><strong>{{ row.name }}</strong><small class="agent-batch-cell-note">#{{ row.id }} · {{ formatDate(row.createdAt) }}</small></template></el-table-column>
|
||||
<el-table-column label="配置快照" min-width="180"><template #default="{ row }"><span>{{ row.modelName }}</span><small class="agent-batch-cell-note">{{ row.knowledgeNames.join('、') }}</small></template></el-table-column>
|
||||
<el-table-column label="进度" min-width="190"><template #default="{ row }"><el-progress :percentage="row.progress" :status="row.status === 'completed' ? 'success' : undefined" /><small class="agent-batch-cell-note">{{ row.processedCount }}/{{ row.totalCount }} · 成功 {{ row.successCount }} · 失败 {{ row.failedCount }}</small></template></el-table-column>
|
||||
<el-table-column label="状态" width="108"><template #default="{ row }"><el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag></template></el-table-column>
|
||||
<el-table-column label="操作" width="190" fixed="right"><template #default="{ row }"><el-button link type="primary" @click="openDetail(row)">查看</el-button><el-button link type="primary" :loading="exportingId === row.id" @click="exportJob(row)">导出</el-button><el-button v-if="['pending','running'].includes(row.status)" link type="danger" @click="cancelJob(row)">取消</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
<AdminPagination :page="pager.page" :page-size="pager.pageSize" :total="pager.total" @change="loadJobs" />
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="detailOpen" width="min(1100px, 94vw)" title="批量测试详情" destroy-on-close>
|
||||
<div v-loading="detailLoading" class="agent-batch-detail">
|
||||
<template v-if="selectedJob">
|
||||
<div class="agent-batch-detail-head">
|
||||
<div><strong>{{ selectedJob.name }}</strong><span>{{ selectedJob.modelName }} · {{ selectedJob.knowledgeNames.join('、') }}</span></div>
|
||||
<el-tag :type="statusType(selectedJob.status)">{{ statusLabel(selectedJob.status) }}</el-tag>
|
||||
</div>
|
||||
<el-table :data="selectedJob.items.items" stripe max-height="520">
|
||||
<el-table-column prop="externalNo" label="序号" width="76" />
|
||||
<el-table-column prop="question" label="问题" min-width="230" show-overflow-tooltip />
|
||||
<el-table-column label="答案" min-width="360"><template #default="{ row }"><div class="agent-batch-answer">{{ row.answer || row.errorMessage || statusLabel(row.status) }}</div></template></el-table-column>
|
||||
<el-table-column label="状态" width="90"><template #default="{ row }"><el-tag size="small" :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag></template></el-table-column>
|
||||
<el-table-column label="耗时" width="92"><template #default="{ row }">{{ itemDuration(row) }}</template></el-table-column>
|
||||
</el-table>
|
||||
<AdminPagination :page="detailPager.page" :page-size="detailPager.pageSize" :total="detailPager.total" @change="(page, pageSize) => loadDetail(selectedJob!.id, page, pageSize)" />
|
||||
</template>
|
||||
</div>
|
||||
<template #footer><el-button @click="detailOpen = false">关闭</el-button><el-button v-if="selectedJob" type="primary" :loading="exportingId === selectedJob.id" @click="exportJob(selectedJob)">导出 Excel</el-button></template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
@@ -5,11 +5,12 @@ import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
|
||||
import { api, streamDebugAgent } from "../services/api";
|
||||
import type { AdminUser, AgentRuntimeConfig, KnowledgeItem, ModelItem, PromptDetail, PromptHistoryItem, TopicSessionRecord } from "../types/api";
|
||||
import AgentGenerationParameters from "./AgentGenerationParameters.vue";
|
||||
import AgentBatchTestPanel from "./AgentBatchTestPanel.vue";
|
||||
import AgentResponseDepthControl from "./AgentResponseDepthControl.vue";
|
||||
import AdminPagination from "./AdminPagination.vue";
|
||||
import StreamingMarkdownMessage from "./StreamingMarkdownMessage.vue";
|
||||
|
||||
const props = defineProps<{ previewKnowledgeId?: number | null }>();
|
||||
const props = withDefaults(defineProps<{ previewKnowledgeId?: number | null; canBatch?: boolean }>(), { canBatch: false });
|
||||
const emit = defineEmits<{ consumedPreview: [] }>();
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -97,6 +98,19 @@ const selectedDebugTopicLabel = computed(() => {
|
||||
const topic = debugTopics.value.find((item) => item.id === agentForm.topicSessionId);
|
||||
return topic ? topic.title : `主题 #${agentForm.topicSessionId}`;
|
||||
});
|
||||
const batchConfig = computed(() => ({
|
||||
promptContent: promptContent.value,
|
||||
modelId: agentForm.modelId,
|
||||
knowledgeIds: [...agentForm.knowledgeIds],
|
||||
temperature: agentForm.temperature,
|
||||
topP: agentForm.topP,
|
||||
topK: agentForm.topK,
|
||||
presencePenalty: agentForm.presencePenalty,
|
||||
frequencyPenalty: agentForm.frequencyPenalty,
|
||||
maxToken: agentForm.maxToken,
|
||||
streamEnabled: agentForm.streamEnabled,
|
||||
responseDepth: agentForm.responseDepth,
|
||||
}));
|
||||
|
||||
onMounted(load);
|
||||
|
||||
@@ -481,7 +495,7 @@ function errorMessage(error: unknown, fallback: string) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section v-loading="loading" class="agent-workbench agent-workbench-v2">
|
||||
<section v-loading="loading" class="agent-workbench agent-workbench-v2" :class="{ 'agent-batch-mode': activeConfigTab === 'batch' }">
|
||||
<div class="agent-config-panel agent-control-panel">
|
||||
<el-tabs v-model="activeConfigTab" class="agent-config-tabs">
|
||||
<el-tab-pane label="主提示词" name="prompt">
|
||||
@@ -651,6 +665,14 @@ function errorMessage(error: unknown, fallback: string) {
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="props.canBatch" label="批量测试" name="batch" lazy>
|
||||
<AgentBatchTestPanel
|
||||
:config="batchConfig"
|
||||
:model-name="selectedModelName"
|
||||
:knowledge-summary="selectedKnowledgeSummary"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="历史版本" name="history">
|
||||
<div class="agent-section-intro"><div><h3>主提示词历史</h3><p>查看完整内容或恢复任意版本。恢复操作会作为一个新版本记录。</p></div></div>
|
||||
<div v-loading="historyLoading" class="prompt-history-list">
|
||||
|
||||
@@ -8,6 +8,8 @@ import type {
|
||||
AgentDebugStreamComplete,
|
||||
AgentGenerationConfig,
|
||||
AgentRuntimeConfig,
|
||||
AgentBatchJob,
|
||||
AgentBatchJobDetail,
|
||||
AiLogRecord,
|
||||
ApiResponse,
|
||||
ChatDetail,
|
||||
@@ -259,6 +261,19 @@ export const api = {
|
||||
agentRuntimeConfig: () => request<AgentRuntimeConfig>("/admin/agent/runtime-config"),
|
||||
saveAgentRuntimeConfig: (payload: AgentGenerationConfig) =>
|
||||
request<AgentRuntimeConfig>("/admin/agent/runtime-config", { method: "PUT", body: JSON.stringify(payload) }),
|
||||
downloadAgentBatchTemplate: () => download("/admin/agent/batch/template", "Agent批量测试导入模板.xlsx"),
|
||||
createAgentBatch: (file: File, config: Record<string, unknown>) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("configJson", JSON.stringify(config));
|
||||
return upload<AgentBatchJob>("/admin/agent/batch/jobs", formData);
|
||||
},
|
||||
agentBatchJobs: (query: { page?: number; pageSize?: number } = {}) =>
|
||||
request<PageResult<AgentBatchJob>>(`/admin/agent/batch/jobs${queryString(query)}`),
|
||||
agentBatchJob: (id: number, query: { page?: number; pageSize?: number } = {}) =>
|
||||
request<AgentBatchJobDetail>(`/admin/agent/batch/jobs/${id}${queryString(query)}`),
|
||||
exportAgentBatch: (id: number) => download(`/admin/agent/batch/jobs/${id}/export`, `Agent批量测试结果_${id}.xlsx`),
|
||||
cancelAgentBatch: (id: number) => request<AgentBatchJob>(`/admin/agent/batch/jobs/${id}/cancel`, { method: "POST", body: "{}" }),
|
||||
models: () => request<ModelItem[]>("/admin/model/list"),
|
||||
createModel: (payload: Record<string, unknown>) =>
|
||||
request<ModelItem>("/admin/model", { method: "POST", body: JSON.stringify(payload) }),
|
||||
|
||||
@@ -975,6 +975,10 @@ textarea {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.agent-workbench.agent-batch-mode { grid-template-columns: minmax(0, 1fr); }
|
||||
.agent-workbench.agent-batch-mode > .agent-preview-panel,
|
||||
.agent-workbench.agent-batch-mode > .agent-trace-panel { display: none; }
|
||||
|
||||
.agent-page-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -1049,6 +1053,33 @@ textarea {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.agent-batch-panel { display: grid; gap: 16px; }
|
||||
.agent-batch-create-card { display: grid; gap: 16px; padding: 16px; border: 1px solid #dfe8e5; border-radius: 10px; background: #f8fbfa; }
|
||||
.agent-batch-config-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
|
||||
.agent-batch-config-summary span { min-width: 0; padding: 11px 12px; border: 1px solid #e1e9e6; border-radius: 8px; background: #fff; }
|
||||
.agent-batch-config-summary small, .agent-batch-config-summary strong { display: block; }
|
||||
.agent-batch-config-summary small { margin-bottom: 4px; color: #7a8c85; font-size: 11px; }
|
||||
.agent-batch-config-summary strong { overflow: hidden; color: #2b4038; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.agent-batch-upload-row { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
|
||||
.agent-batch-name { display: grid; gap: 6px; width: 220px; color: #536860; font-size: 12px; }
|
||||
.agent-batch-name input { height: 40px; padding: 0 12px; border: 1px solid #d8e2df; border-radius: 6px; outline: none; background: #fff; color: #263832; }
|
||||
.agent-batch-name input:focus { border-color: #2f9479; box-shadow: 0 0 0 2px rgba(47, 148, 121, .12); }
|
||||
.agent-batch-file { position: relative; display: grid; min-width: 260px; flex: 1; gap: 3px; min-height: 40px; padding: 7px 12px; border: 1px dashed #b9cac4; border-radius: 7px; background: #fff; cursor: pointer; }
|
||||
.agent-batch-file.selected { border-style: solid; border-color: #62aa95; background: #f3faf7; }
|
||||
.agent-batch-file input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
||||
.agent-batch-file span { overflow: hidden; color: #315148; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.agent-batch-file small { color: #82918c; font-size: 11px; }
|
||||
.agent-batch-list-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.agent-batch-list-head h4, .agent-batch-list-head p { margin: 0; }
|
||||
.agent-batch-list-head h4 { margin-bottom: 4px; color: #263832; font-size: 15px; }
|
||||
.agent-batch-list-head p { color: #71827c; font-size: 12px; }
|
||||
.agent-batch-cell-note { display: block; margin-top: 4px; overflow: hidden; color: #7b8c86; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.agent-batch-detail { min-height: 260px; }
|
||||
.agent-batch-detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 14px; padding: 13px 14px; border-radius: 8px; background: #f5f8f7; }
|
||||
.agent-batch-detail-head div { display: grid; gap: 4px; min-width: 0; }
|
||||
.agent-batch-detail-head span { overflow: hidden; color: #71827c; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.agent-batch-answer { max-height: 104px; overflow: auto; white-space: pre-wrap; word-break: break-word; line-height: 1.6; }
|
||||
|
||||
.agent-advanced-settings {
|
||||
margin-top: 16px;
|
||||
border: 1px solid #e2eae7;
|
||||
@@ -2315,6 +2346,10 @@ textarea {
|
||||
.agent-debug-context-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.agent-batch-config-summary { grid-template-columns: 1fr; }
|
||||
.agent-batch-name, .agent-batch-file { width: 100%; min-width: 0; }
|
||||
.agent-batch-upload-row > .el-button { flex: 1; }
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
|
||||
@@ -392,6 +392,44 @@ export interface AgentDebugStreamComplete {
|
||||
questionType?: string;
|
||||
}
|
||||
|
||||
export type AgentBatchStatus = "pending" | "running" | "completed" | "completed_with_errors" | "cancelled";
|
||||
|
||||
export interface AgentBatchJob {
|
||||
id: number;
|
||||
name: string;
|
||||
originalFilename: string;
|
||||
status: AgentBatchStatus;
|
||||
totalCount: number;
|
||||
processedCount: number;
|
||||
successCount: number;
|
||||
failedCount: number;
|
||||
progress: number;
|
||||
modelId: number;
|
||||
modelName: string;
|
||||
knowledgeIds: number[];
|
||||
knowledgeNames: string[];
|
||||
createdAt: string;
|
||||
startedAt?: string | null;
|
||||
finishedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface AgentBatchItem {
|
||||
id: number;
|
||||
rowNumber: number;
|
||||
externalNo?: string | null;
|
||||
question: string;
|
||||
answer?: string | null;
|
||||
status: "pending" | "running" | "success" | "failed" | "cancelled";
|
||||
errorMessage?: string | null;
|
||||
modelName?: string | null;
|
||||
retrieveCount?: number | null;
|
||||
durationMs?: number | null;
|
||||
}
|
||||
|
||||
export interface AgentBatchJobDetail extends AgentBatchJob {
|
||||
items: PageResult<AgentBatchItem>;
|
||||
}
|
||||
|
||||
export interface KnowledgeVersion {
|
||||
id: number;
|
||||
knowledgeId: number;
|
||||
|
||||
Reference in New Issue
Block a user