feat: AI 辅助知识库描述生成功能
This commit is contained in:
parent
751bd3c791
commit
6ebbb46c9f
@ -1295,3 +1295,66 @@ async def get_all_embedding_models_status(current_user: User = Depends(get_admin
|
||||
except Exception as e:
|
||||
logger.error(f"获取所有embedding模型状态失败: {e}, {traceback.format_exc()}")
|
||||
return {"message": f"获取所有embedding模型状态失败: {e}", "status": {"models": {}, "total": 0, "available": 0}}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# === AI 辅助功能分组 ===
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@knowledge.post("/generate-description")
|
||||
async def generate_description(
|
||||
name: str = Body(..., description="知识库名称"),
|
||||
current_description: str = Body("", description="当前描述(可选,用于优化)"),
|
||||
current_user: User = Depends(get_admin_user),
|
||||
):
|
||||
"""使用 LLM 生成或优化知识库描述
|
||||
|
||||
根据知识库名称和现有描述,使用 LLM 生成适合作为智能体工具描述的内容。
|
||||
"""
|
||||
from src.models import select_model
|
||||
|
||||
logger.debug(f"Generating description for knowledge base: {name}")
|
||||
|
||||
# 构建提示词
|
||||
if current_description.strip():
|
||||
prompt = textwrap.dedent(f"""
|
||||
请帮我优化以下知识库的描述。
|
||||
|
||||
知识库名称: {name}
|
||||
当前描述: {current_description}
|
||||
|
||||
要求:
|
||||
1. 这个描述将作为智能体工具的描述使用
|
||||
2. 智能体会根据知识库的标题和描述来选择合适的工具
|
||||
3. 所以描述需要清晰、具体,说明该知识库包含什么内容、适合解答什么类型的问题
|
||||
4. 描述应该简洁有力,通常 2-4 句话即可
|
||||
5. 不要使用 Markdown 格式
|
||||
|
||||
请直接输出优化后的描述,不要有任何前缀说明。
|
||||
""").strip()
|
||||
else:
|
||||
prompt = textwrap.dedent(f"""
|
||||
请为以下知识库生成一个描述。
|
||||
|
||||
知识库名称: {name}
|
||||
|
||||
要求:
|
||||
1. 这个描述将作为智能体工具的描述使用
|
||||
2. 智能体会根据知识库的标题和描述来选择合适的工具
|
||||
3. 所以描述需要清晰、具体,说明该知识库可能包含什么内容、适合解答什么类型的问题
|
||||
4. 描述应该简洁有力,通常 2-4 句话即可
|
||||
5. 不要使用 Markdown 格式
|
||||
|
||||
请直接输出描述,不要有任何前缀说明。
|
||||
""").strip()
|
||||
|
||||
try:
|
||||
model = select_model()
|
||||
response = await asyncio.to_thread(model.call, prompt)
|
||||
description = response.content.strip()
|
||||
logger.debug(f"Generated description: {description}")
|
||||
return {"description": description, "status": "success"}
|
||||
except Exception as e:
|
||||
logger.error(f"生成描述失败: {e}, {traceback.format_exc()}")
|
||||
raise HTTPException(status_code=500, detail=f"生成描述失败: {e}")
|
||||
|
||||
@ -53,6 +53,19 @@ export const databaseApi = {
|
||||
*/
|
||||
deleteDatabase: async (dbId) => {
|
||||
return apiAdminDelete(`/api/knowledge/databases/${dbId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 使用 AI 生成或优化知识库描述
|
||||
* @param {string} name - 知识库名称
|
||||
* @param {string} currentDescription - 当前描述(可选)
|
||||
* @returns {Promise} - 生成结果
|
||||
*/
|
||||
generateDescription: async (name, currentDescription = '') => {
|
||||
return apiAdminPost('/api/knowledge/generate-description', {
|
||||
name,
|
||||
current_description: currentDescription
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
120
web/src/components/AiTextarea.vue
Normal file
120
web/src/components/AiTextarea.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="ai-textarea-wrapper">
|
||||
<a-textarea
|
||||
:value="modelValue"
|
||||
@update:value="$emit('update:modelValue', $event)"
|
||||
:placeholder="placeholder"
|
||||
:rows="rows"
|
||||
:auto-size="autoSize"
|
||||
/>
|
||||
<a-tooltip v-if="name" title="使用 AI 生成或优化描述">
|
||||
<a-button
|
||||
class="ai-btn"
|
||||
type="text"
|
||||
size="small"
|
||||
:loading="loading"
|
||||
@click="generateDescription"
|
||||
>
|
||||
<template #icon>
|
||||
<svg v-if="!loading" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
||||
</svg>
|
||||
</template>
|
||||
<span v-if="!loading" class="ai-text">AI</span>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { databaseApi } from '@/apis/knowledge_api'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
autoSize: {
|
||||
type: [Boolean, Object],
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const generateDescription = async () => {
|
||||
if (!props.name?.trim()) {
|
||||
message.warning('请先输入知识库名称')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await databaseApi.generateDescription(props.name, props.modelValue)
|
||||
if (result.status === 'success' && result.description) {
|
||||
emit('update:modelValue', result.description)
|
||||
message.success('描述生成成功')
|
||||
} else {
|
||||
message.error(result.message || '生成失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成描述失败:', error)
|
||||
message.error(error.message || '生成描述失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ai-textarea-wrapper {
|
||||
position: relative;
|
||||
|
||||
.ai-btn {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 2px 6px;
|
||||
height: 24px;
|
||||
color: var(--main-color);
|
||||
background: var(--gray-50);
|
||||
border: 1px solid var(--gray-200);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--main-10);
|
||||
border-color: var(--main-color);
|
||||
}
|
||||
|
||||
.ai-text {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-input) {
|
||||
padding-right: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -45,7 +45,12 @@
|
||||
<a-input v-model:value="editForm.name" placeholder="请输入知识库名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="知识库描述" name="description">
|
||||
<a-textarea v-model:value="editForm.description" placeholder="请输入知识库描述" :rows="4" />
|
||||
<AiTextarea
|
||||
v-model="editForm.description"
|
||||
:name="editForm.name"
|
||||
placeholder="请输入知识库描述"
|
||||
:rows="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- 仅对 LightRAG 类型显示 LLM 配置 -->
|
||||
<a-form-item v-if="database.kb_type === 'lightrag'" label="语言模型 (LLM)" name="llm_info">
|
||||
@ -72,6 +77,7 @@ import {
|
||||
} from '@ant-design/icons-vue';
|
||||
import HeaderComponent from '@/components/HeaderComponent.vue';
|
||||
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
||||
import AiTextarea from '@/components/AiTextarea.vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -65,7 +65,12 @@
|
||||
<a-input v-model:value="editForm.name" placeholder="请输入知识库名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="知识库描述" name="description">
|
||||
<a-textarea v-model:value="editForm.description" placeholder="请输入知识库描述" :rows="4" />
|
||||
<AiTextarea
|
||||
v-model="editForm.description"
|
||||
:name="editForm.name"
|
||||
placeholder="请输入知识库描述"
|
||||
:rows="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="自动生成问题" name="auto_generate_questions">
|
||||
@ -99,6 +104,7 @@ import {
|
||||
CopyOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
||||
import AiTextarea from '@/components/AiTextarea.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const store = useDatabaseStore();
|
||||
|
||||
@ -81,8 +81,9 @@
|
||||
|
||||
<h3 style="margin-top: 20px;">知识库描述</h3>
|
||||
<p style="color: var(--gray-700); font-size: 14px;">在智能体流程中,这里的描述会作为工具的描述。智能体会根据知识库的标题和描述来选择合适的工具。所以这里描述的越详细,智能体越容易选择到合适的工具。</p>
|
||||
<a-textarea
|
||||
v-model:value="newDatabase.description"
|
||||
<AiTextarea
|
||||
v-model="newDatabase.description"
|
||||
:name="newDatabase.name"
|
||||
placeholder="新建知识库描述"
|
||||
:auto-size="{ minRows: 3, maxRows: 10 }"
|
||||
/>
|
||||
@ -244,6 +245,7 @@ import HeaderComponent from '@/components/HeaderComponent.vue';
|
||||
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
||||
import EmbeddingModelSelector from '@/components/EmbeddingModelSelector.vue';
|
||||
import dayjs, { parseToShanghai } from '@/utils/time';
|
||||
import AiTextarea from '@/components/AiTextarea.vue';
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user