feat(知识库): 完善知识库检索结果展示逻辑和样式
refactor(工具): 更新知识库工具元数据结构 style(UI): 优化配置侧边栏和消息组件样式 docs: 更新开发计划文档 perf(调试): 增强智能体配置调试信息输出
This commit is contained in:
parent
2e4b8a2363
commit
b8ba85654f
@ -1,6 +1,7 @@
|
||||
目前已有的开发计划包括:
|
||||
|
||||
💭 **Features Todo**
|
||||
- [ ] LangGraph 升级到 0.6+ 版本,并适配新特性,如 context 等。
|
||||
- [ ] 使用其他的聊天记录管理方法,解决两个问题,一个是上下文长度过长,一个是上下文的类型变得更加丰富,比如多模态等等。(现在是基于 LangGraph 的 Memory 实现的,v0.2.3 版本实现),暂定使用 [mem0](github.com/mem0ai/mem0) 来实现。但是目前了解下来,还不是我想要的那种方案。可能会基于这个实现一个 ThreadConvManager 这个类。
|
||||
- [ ] 添加对于上传文件的支持:这里的复杂的地方就在于如何和历史记录结合在一起(v0.2.3 版本实现,放在记忆管理后面)
|
||||
|
||||
@ -13,6 +14,8 @@
|
||||
|
||||
- [ ] 支持数据库查询功能
|
||||
- [ ] 消息内容支持图片显示
|
||||
- [ ] 添加 SQL 读取工具
|
||||
- [ ] 添加绘图工具(这里的绘图是指绘制固定格式的图和表等,暂时没想好如何支持自定义绘图)
|
||||
- [ ] 添加测试脚本,覆盖最常见的功能
|
||||
- [ ] 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
|
||||
- [ ] 优化对文档信息的检索展示(检索结果页、详情页)
|
||||
|
||||
@ -84,8 +84,8 @@ def get_kb_based_tools() -> dict[str, Any]:
|
||||
name=tool_id,
|
||||
description=description,
|
||||
args_schema=KnowledgeRetrieverModel,
|
||||
metadata=retrieve_info | {
|
||||
"tag": "knowledgebase"
|
||||
metadata=retrieve_info["metadata"] | {
|
||||
"tag": ["knowledgebase"]
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -439,6 +439,7 @@ class KnowledgeBase(ABC):
|
||||
"name": meta["name"],
|
||||
"description": meta["description"],
|
||||
"retriever": make_retriever(db_id),
|
||||
"metadata": meta
|
||||
}
|
||||
return retrievers
|
||||
|
||||
|
||||
@ -205,7 +205,7 @@
|
||||
<!-- 固定在底部的操作按钮 -->
|
||||
<div class="sidebar-footer" v-if="!isEmptyConfig">
|
||||
<div class="form-actions">
|
||||
<a-button type="primary" @click="saveConfig" class="save-btn">
|
||||
<a-button @click="saveConfig" class="save-btn" :class="{'changed': agentStore.hasConfigChanges}">
|
||||
保存配置
|
||||
</a-button>
|
||||
<!-- TODO:BUG 目前有 bug 暂时不展示 -->
|
||||
@ -631,14 +631,18 @@ watch(() => props.isOpen, (newVal) => {
|
||||
|
||||
.save-btn {
|
||||
flex: 1;
|
||||
background: var(--main-color);
|
||||
background-color: var(--gray-100);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
|
||||
&.changed {
|
||||
background-color: var(--main-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--main-color);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ const editorRef = ref()
|
||||
const emit = defineEmits(['retry', 'retryStoppedMessage', 'openRefs']);
|
||||
|
||||
// 推理面板展开状态
|
||||
const reasoningActiveKey = ref(['show']);
|
||||
const reasoningActiveKey = ref(['hide']);
|
||||
const expandedToolCalls = ref(new Set()); // 展开的工具调用集合
|
||||
|
||||
// 引入智能体 store
|
||||
@ -235,7 +235,7 @@ const toggleToolCall = (toolCallId) => {
|
||||
.reasoning-box {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--gray-200);
|
||||
background-color: var(--gray-25);
|
||||
overflow: hidden;
|
||||
@ -258,7 +258,7 @@ const toggleToolCall = (toolCallId) => {
|
||||
// background-color: var(--gray-100);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-800);
|
||||
color: var(--gray-700);
|
||||
// border-bottom: 1px solid var(--gray-200);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
@ -267,7 +267,7 @@ const toggleToolCall = (toolCallId) => {
|
||||
}
|
||||
|
||||
.ant-collapse-expand-icon {
|
||||
color: var(--main-color);
|
||||
color: var(--gray-400);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -94,10 +94,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onActivated, onUnmounted, nextTick, reactive, computed, h } from 'vue';
|
||||
import { ref, onMounted, onActivated, onUnmounted, nextTick, reactive, computed, h, toRaw } from 'vue';
|
||||
import { useConfigStore } from '@/stores/config';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { useDatabaseStore } from '@/stores/database';
|
||||
import { useAgentStore } from '@/stores/agent';
|
||||
import { useThrottleFn } from '@vueuse/core';
|
||||
import { message } from 'ant-design-vue';
|
||||
import {
|
||||
@ -121,6 +122,7 @@ import { checkAdminPermission } from '@/stores/user';
|
||||
const configStore = useConfigStore()
|
||||
const userStore = useUserStore();
|
||||
const databaseStore = useDatabaseStore();
|
||||
const agentStore = useAgentStore();
|
||||
const config = configStore.config;
|
||||
|
||||
|
||||
@ -144,7 +146,6 @@ const state = reactive({
|
||||
isFullscreen: false,
|
||||
});
|
||||
|
||||
const logs = ref('');
|
||||
const error = ref('');
|
||||
const logContainer = ref(null);
|
||||
let autoRefreshInterval = null;
|
||||
@ -231,11 +232,6 @@ const onSearch = () => {
|
||||
// 搜索会通过computed自动触发
|
||||
};
|
||||
|
||||
// 过滤日志
|
||||
const filterLogs = () => {
|
||||
// 过滤会通过computed自动触发
|
||||
};
|
||||
|
||||
// 日志级别选择相关方法
|
||||
const isLogLevelSelected = (level) => {
|
||||
return state.selectedLevels.includes(level);
|
||||
@ -376,24 +372,67 @@ const printAgentConfig = async () => {
|
||||
if (!checkAdminPermission()) return;
|
||||
|
||||
try {
|
||||
console.log('=== 智能体配置 ===');
|
||||
console.log('=== 智能体配置信息 ===');
|
||||
|
||||
// 获取智能体列表
|
||||
const agentsData = await agentApi.getAgents();
|
||||
console.log('智能体列表:', JSON.stringify(agentsData.agents, null, 2));
|
||||
// 从store 获取状态信息
|
||||
console.log('Store 状态:', {
|
||||
isInitialized: agentStore.isInitialized,
|
||||
selectedAgentId: agentStore.selectedAgentId,
|
||||
defaultAgentId: agentStore.defaultAgentId,
|
||||
currentThreadId: agentStore.currentThreadId,
|
||||
isStreaming: agentStore.isStreaming,
|
||||
loadingStates: {
|
||||
isLoadingAgents: agentStore.isLoadingAgents,
|
||||
isLoadingConfig: agentStore.isLoadingConfig,
|
||||
isLoadingTools: agentStore.isLoadingTools,
|
||||
isLoadingThreads: agentStore.isLoadingThreads,
|
||||
isLoadingMessages: agentStore.isLoadingMessages
|
||||
},
|
||||
error: agentStore.error
|
||||
});
|
||||
|
||||
// 获取默认智能体
|
||||
const defaultAgent = await agentApi.getDefaultAgent();
|
||||
console.log('默认智能体:', JSON.stringify(defaultAgent, null, 2));
|
||||
// 智能体列表信息
|
||||
console.log('智能体列表 (从store):', {
|
||||
count: agentStore.agentsList.length,
|
||||
agents: toRaw(agentStore.agentsList)
|
||||
});
|
||||
|
||||
// 获取每个智能体的配置
|
||||
for (const agent of agentsData.agents) {
|
||||
try {
|
||||
const agentConfig = await agentApi.getAgentConfig(agent.id);
|
||||
console.log(`智能体 "${agent.name}" 配置:`, JSON.stringify(agentConfig, null, 2));
|
||||
} catch (err) {
|
||||
console.log(`智能体 "${agent.name}" 配置获取失败:`, err.message);
|
||||
}
|
||||
// 当前选中智能体信息
|
||||
if (agentStore.selectedAgent) {
|
||||
console.log('当前选中智能体:', {
|
||||
agent: toRaw(agentStore.selectedAgent),
|
||||
isDefault: agentStore.isDefaultAgent,
|
||||
configSchema: toRaw(agentStore.configSchema),
|
||||
configurableItems: Object.keys(agentStore.configurableItems).length
|
||||
});
|
||||
|
||||
// 当前智能体配置
|
||||
console.log('当前智能体配置:', {
|
||||
current: toRaw(agentStore.agentConfig),
|
||||
original: toRaw(agentStore.originalAgentConfig),
|
||||
hasChanges: agentStore.hasConfigChanges
|
||||
});
|
||||
}
|
||||
|
||||
// 工具信息
|
||||
console.log('可用工具:', toRaw(agentStore.availableTools));
|
||||
|
||||
// 线程信息
|
||||
console.log('线程信息:', {
|
||||
currentAgentThreads: agentStore.currentAgentThreads.length,
|
||||
currentThread: agentStore.currentThread ? toRaw(agentStore.currentThread) : null,
|
||||
currentThreadMessages: agentStore.currentThreadMessages.length
|
||||
});
|
||||
|
||||
// 对话状态
|
||||
if (agentStore.isStreaming || Object.keys(agentStore.onGoingConv.msgChunks).length > 0) {
|
||||
console.log('当前对话状态:', {
|
||||
isStreaming: agentStore.isStreaming,
|
||||
ongoingChunks: Object.keys(agentStore.onGoingConv.msgChunks).length,
|
||||
ongoingMessages: agentStore.onGoingConvMessages.length,
|
||||
conversations: agentStore.conversations.length,
|
||||
detail: toRaw(agentStore.conversations)
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@ -462,7 +501,7 @@ const printAgentConfig = async () => {
|
||||
}
|
||||
|
||||
.auto-refresh-button {
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,16 +38,16 @@
|
||||
v-for="(chunk, index) in fileGroup.chunks"
|
||||
:key="chunk.id"
|
||||
class="chunk-item"
|
||||
:class="{ 'high-relevance': chunk.rerank_score > 0.5 }"
|
||||
:class="{ 'high-relevance': chunk.score > 0.5 }"
|
||||
@click="showChunkDetail(chunk, index + 1)"
|
||||
>
|
||||
<div class="chunk-summary">
|
||||
<span class="chunk-index">#{{ index + 1 }}</span>
|
||||
<div class="chunk-scores">
|
||||
<span class="score-item">相似度 {{ (chunk.distance * 100).toFixed(0) }}%</span>
|
||||
<span class="score-item">相似度 {{ (chunk.score * 100).toFixed(0) }}%</span>
|
||||
<span v-if="chunk.rerank_score" class="score-item">重排序 {{ (chunk.rerank_score * 100).toFixed(0) }}%</span>
|
||||
</div>
|
||||
<span class="chunk-preview">{{ getPreviewText(chunk.entity.text) }}</span>
|
||||
<span class="chunk-preview">{{ getPreviewText(chunk.content) }}</span>
|
||||
<EyeOutlined class="view-icon" />
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@
|
||||
<!-- 弹窗展示chunk详细信息 -->
|
||||
<a-modal
|
||||
v-model:open="modalVisible"
|
||||
:title="`文档片段 #${selectedChunk?.index} - ${selectedChunk?.data?.file?.filename}`"
|
||||
:title="`文档片段 #${selectedChunk?.index} - ${selectedChunk?.data?.metadata?.source}`"
|
||||
width="800px"
|
||||
:footer="null"
|
||||
class="chunk-detail-modal"
|
||||
@ -72,10 +72,10 @@
|
||||
<div class="detail-scores">
|
||||
<div class="score-card">
|
||||
<div class="score-label">相似度分数</div>
|
||||
<div class="score-value-large">{{ (selectedChunk.data.distance * 100).toFixed(1) }}%</div>
|
||||
<div class="score-value-large">{{ (selectedChunk.data.score * 100).toFixed(1) }}%</div>
|
||||
<a-progress
|
||||
:percent="getPercent(selectedChunk.data.distance)"
|
||||
:stroke-color="getScoreColor(selectedChunk.data.distance)"
|
||||
:percent="getPercent(selectedChunk.data.score)"
|
||||
:stroke-color="getScoreColor(selectedChunk.data.score)"
|
||||
:show-info="false"
|
||||
stroke-width="6"
|
||||
/>
|
||||
@ -92,13 +92,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-meta">
|
||||
<span class="meta-item"><DatabaseOutlined /> ID: {{ selectedChunk.data.id }}</span>
|
||||
<span class="meta-item"><DatabaseOutlined /> ID: {{ selectedChunk.data.metadata.chunk_id || selectedChunk.data.metadata.file_id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-content">
|
||||
<h5>文档内容</h5>
|
||||
<div class="content-text">{{ selectedChunk.data.entity.text }}</div>
|
||||
<div class="content-text">{{ selectedChunk.data.content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
@ -128,7 +128,7 @@ const fileGroups = computed(() => {
|
||||
const groups = new Map()
|
||||
|
||||
props.data.forEach(item => {
|
||||
const filename = item.file.filename
|
||||
const filename = item.metadata.source
|
||||
if (!groups.has(filename)) {
|
||||
groups.set(filename, {
|
||||
filename,
|
||||
@ -187,66 +187,71 @@ const getScoreColor = (score) => {
|
||||
border: 1px solid var(--gray-200);
|
||||
|
||||
.kb-header {
|
||||
padding: 10px 14px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
background: var(--gray-50);
|
||||
background: var(--gray-25);
|
||||
|
||||
h4 {
|
||||
margin: 0 0 2px 0;
|
||||
color: var(--main-color);
|
||||
margin: 0 0 4px 0;
|
||||
color: var(--gray-800);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.anticon {
|
||||
color: var(--main-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.result-summary {
|
||||
font-size: 11px;
|
||||
color: var(--gray-600);
|
||||
font-size: 12px;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
}
|
||||
|
||||
.kb-results {
|
||||
padding: 6px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.file-group {
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gray-150);
|
||||
border-radius: 8px;
|
||||
background: var(--gray-0);
|
||||
overflow: hidden;
|
||||
|
||||
.file-header {
|
||||
padding: 10px 12px;
|
||||
padding: 12px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
background: var(--gray-25);
|
||||
transition: all 0.2s ease;
|
||||
background: var(--gray-10);
|
||||
|
||||
&:hover {
|
||||
background: var(--gray-50);
|
||||
background: var(--gray-25);
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
background: var(--main-10);
|
||||
border-bottom: 1px solid var(--gray-200);
|
||||
background: var(--gray-50);
|
||||
border-bottom: 1px solid var(--gray-150);
|
||||
}
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.anticon {
|
||||
color: var(--main-color);
|
||||
color: var(--gray-500);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -263,17 +268,18 @@ const getScoreColor = (score) => {
|
||||
|
||||
.chunk-count {
|
||||
font-size: 11px;
|
||||
color: var(--gray-500);
|
||||
background: var(--gray-100);
|
||||
padding: 1px 5px;
|
||||
border-radius: 10px;
|
||||
color: var(--gray-400);
|
||||
background: var(--gray-50);
|
||||
padding: 2px 6px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--gray-150);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
color: var(--gray-500);
|
||||
transition: transform 0.15s ease;
|
||||
color: var(--gray-400);
|
||||
transition: transform 0.2s ease;
|
||||
font-size: 12px;
|
||||
|
||||
.rotated {
|
||||
@ -283,25 +289,25 @@ const getScoreColor = (score) => {
|
||||
}
|
||||
|
||||
.chunks-container {
|
||||
background: var(--gray-10);
|
||||
background: var(--gray-0);
|
||||
}
|
||||
|
||||
.chunk-item {
|
||||
padding: 8px 12px;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--gray-100);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&.high-relevance {
|
||||
background: var(--main-10);
|
||||
background: var(--main-5);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--main-30);
|
||||
background: var(--gray-25);
|
||||
|
||||
.view-icon {
|
||||
opacity: 1;
|
||||
@ -311,34 +317,38 @@ const getScoreColor = (score) => {
|
||||
.chunk-summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 10px;
|
||||
|
||||
.chunk-index {
|
||||
color: var(--main-color);
|
||||
font-size: 10px;
|
||||
color: var(--gray-500);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
min-width: 18px;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
background: var(--gray-25);
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chunk-scores {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
gap: 6px;
|
||||
|
||||
.score-item {
|
||||
font-size: 11px;
|
||||
color: var(--gray-700);
|
||||
// background: var(--main-10);
|
||||
padding: 1px 4px;
|
||||
color: var(--gray-600);
|
||||
background: var(--gray-25);
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--gray-100);
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.chunk-preview {
|
||||
flex: 1;
|
||||
font-size: 11px;
|
||||
color: var(--gray-700);
|
||||
font-size: 12px;
|
||||
color: var(--gray-600);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -346,10 +356,10 @@ const getScoreColor = (score) => {
|
||||
}
|
||||
|
||||
.view-icon {
|
||||
color: var(--main-color);
|
||||
color: var(--gray-400);
|
||||
font-size: 12px;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.15s ease;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -386,21 +396,23 @@ const getScoreColor = (score) => {
|
||||
|
||||
.score-card {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
background: var(--gray-50);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gray-200);
|
||||
padding: 12px 14px;
|
||||
background: var(--gray-25);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--gray-150);
|
||||
|
||||
.score-label {
|
||||
font-size: 14px;
|
||||
color: var(--gray-600);
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--gray-500);
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.score-value-large {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--main-color);
|
||||
color: var(--gray-800);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -411,13 +423,13 @@ const getScoreColor = (score) => {
|
||||
|
||||
.meta-item {
|
||||
font-size: 11px;
|
||||
color: var(--gray-500);
|
||||
color: var(--gray-400);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.anticon {
|
||||
color: var(--main-color);
|
||||
color: var(--gray-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,10 +449,10 @@ const getScoreColor = (score) => {
|
||||
color: var(--gray-700);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
background: var(--gray-50);
|
||||
background: var(--gray-25);
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--gray-150);
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@ -44,6 +44,9 @@ import WebSearchResult from './WebSearchResult.vue'
|
||||
import KnowledgeBaseResult from './KnowledgeBaseResult.vue'
|
||||
import KnowledgeGraphResult from './KnowledgeGraphResult.vue'
|
||||
import CalculatorResult from './CalculatorResult.vue'
|
||||
import { useAgentStore } from '@/stores/agent';
|
||||
|
||||
const agentStore = useAgentStore()
|
||||
|
||||
const props = defineProps({
|
||||
toolName: {
|
||||
@ -56,6 +59,11 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const tool = computed(() => {
|
||||
return agentStore?.availableTools?.[props.toolName] || null
|
||||
})
|
||||
|
||||
|
||||
// 解析数据
|
||||
const parsedData = computed(() => {
|
||||
if (typeof props.resultContent === 'string') {
|
||||
@ -87,23 +95,28 @@ const isWebSearchResult = computed(() => {
|
||||
|
||||
// 判断是否为知识库检索结果
|
||||
const isKnowledgeBaseResult = computed(() => {
|
||||
const toolNameLower = props.toolName.toLowerCase()
|
||||
const isKnowledgeBaseTool = toolNameLower.includes('retrieve') ||
|
||||
toolNameLower.includes('knowledge')
|
||||
// 首先检查工具的 metadata
|
||||
const currentTool = tool.value
|
||||
if (currentTool && currentTool.metadata) {
|
||||
const metadata = currentTool.metadata
|
||||
const hasKnowledgebaseTag = metadata.tag && metadata.tag.includes('knowledgebase')
|
||||
const isNotLightrag = metadata.kb_type !== 'lightrag'
|
||||
|
||||
if (!isKnowledgeBaseTool) return false
|
||||
if (hasKnowledgebaseTag && isNotLightrag) {
|
||||
const data = parsedData.value
|
||||
return Array.isArray(data) &&
|
||||
data.length > 0 &&
|
||||
data.every(item =>
|
||||
item &&
|
||||
typeof item === 'object' &&
|
||||
'content' in item &&
|
||||
'score' in item &&
|
||||
'metadata' in item
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const data = parsedData.value
|
||||
return Array.isArray(data) &&
|
||||
data.length > 0 &&
|
||||
data.every(item =>
|
||||
item &&
|
||||
typeof item === 'object' &&
|
||||
'id' in item &&
|
||||
'distance' in item &&
|
||||
'entity' in item &&
|
||||
'file' in item
|
||||
)
|
||||
return false
|
||||
})
|
||||
|
||||
// 判断是否为知识图谱查询结果
|
||||
|
||||
Loading…
Reference in New Issue
Block a user