feat(lightrag): 添加LightRAG知识库的语言选择和LLM模型选择功能
- 支持在创建LightRAG知识库时选择语言和LLM模型 - 修复知识图谱加载按钮在没有文件时仍可点击的问题 - 更新README中的分支说明 - 简化chroma和milvus的集合命名方式 - 修复知识库创建失败的情况
This commit is contained in:
parent
9e4402aa60
commit
e6145657c5
@ -37,10 +37,10 @@ https://github.com/user-attachments/assets/15f7f315-003d-4e41-a260-739c2529f824
|
||||
|
||||
1. **克隆项目**
|
||||
```bash
|
||||
git clone -b stable https://github.com/xerrors/Yuxi-Know.git
|
||||
git clone -b 0.2.0.preview https://github.com/xerrors/Yuxi-Know.git
|
||||
cd Yuxi-Know
|
||||
```
|
||||
如果想要使用 v0.2 预览版,可以使用分支:`0.2.0.preview`
|
||||
如果想要使用之前的稳定版,可以使用分支:`stable` 分支,`main` 分支是最新的开发版本。
|
||||
|
||||
2. **配置 API 密钥**
|
||||
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
- [x] 切换知识库之后,检索结果没有刷新
|
||||
- [x] 文件上传模块 UI的边距、配色有问题
|
||||
- [x] 知识图谱页面的节点数量统计方法 #236
|
||||
- [ ] 当没有手动添加节点的时候,尝试检索,会出现为创建索引的情况 #236
|
||||
- [x] 当没有手动添加节点的时候,尝试检索,会出现为创建索引的情况 #236
|
||||
- [ ] 删除知识库的时候,没有正常将数据从 Neo4j / Milvus 数据库中删除(需要添加检测脚本)
|
||||
|
||||
# 💯 More:
|
||||
|
||||
|
||||
@ -33,10 +33,11 @@ async def create_database(
|
||||
embed_model_name: str = Body(...),
|
||||
kb_type: str = Body("lightrag"),
|
||||
additional_params: dict = Body({}),
|
||||
llm_info: dict = Body(None),
|
||||
current_user: User = Depends(get_admin_user)
|
||||
):
|
||||
"""创建知识库"""
|
||||
logger.debug(f"Create database {database_name} with kb_type {kb_type}, additional_params {additional_params}")
|
||||
logger.debug(f"Create database {database_name} with kb_type {kb_type}, additional_params {additional_params}, llm_info {llm_info}")
|
||||
try:
|
||||
embed_info = config.embed_model_names[embed_model_name]
|
||||
database_info = await knowledge_base.create_database(
|
||||
@ -44,6 +45,7 @@ async def create_database(
|
||||
description,
|
||||
kb_type=kb_type,
|
||||
embed_info=embed_info,
|
||||
llm_info=llm_info,
|
||||
**additional_params
|
||||
)
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ class ChromaKB(KnowledgeBase):
|
||||
embedding_function = self._get_embedding_function(embed_info)
|
||||
|
||||
# 创建或获取集合
|
||||
collection_name = f"kb_{db_id}"
|
||||
collection_name = db_id
|
||||
|
||||
try:
|
||||
# 尝试获取现有集合
|
||||
|
||||
@ -85,7 +85,7 @@ class KnowledgeBase(ABC):
|
||||
pass
|
||||
|
||||
def create_database(self, database_name: str, description: str,
|
||||
embed_info: dict | None = None, **kwargs) -> dict:
|
||||
embed_info: dict | None = None, llm_info: dict | None = None, **kwargs) -> dict:
|
||||
"""
|
||||
创建数据库
|
||||
|
||||
@ -108,6 +108,7 @@ class KnowledgeBase(ABC):
|
||||
"description": description,
|
||||
"kb_type": self.kb_type,
|
||||
"embed_info": embed_info,
|
||||
"llm_info": llm_info,
|
||||
"metadata": kwargs,
|
||||
"created_at": datetime.now().isoformat()
|
||||
}
|
||||
@ -187,7 +188,6 @@ class KnowledgeBase(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def export_data(self, db_id: str, format: str = 'zip', **kwargs) -> str:
|
||||
pass
|
||||
|
||||
|
||||
@ -55,6 +55,16 @@ class LightRagKB(KnowledgeBase):
|
||||
|
||||
llm_info = self.databases_meta[db_id].get("llm_info", {})
|
||||
embed_info = self.databases_meta[db_id].get("embed_info", {})
|
||||
# 读取在创建数据库时透传的附加参数(包括语言)
|
||||
metadata = self.databases_meta[db_id].get("metadata", {}) or {}
|
||||
addon_params = {}
|
||||
if isinstance(metadata.get("addon_params"), dict):
|
||||
addon_params.update(metadata.get("addon_params", {}))
|
||||
# 兼容直接放在 metadata 下的 language
|
||||
if isinstance(metadata.get("language"), str) and metadata.get("language"):
|
||||
addon_params.setdefault("language", metadata.get("language"))
|
||||
# 默认语言从环境变量读取,默认 English
|
||||
addon_params.setdefault("language", os.getenv("SUMMARY_LANGUAGE", "English"))
|
||||
|
||||
# 创建工作目录
|
||||
working_dir = os.path.join(self.work_dir, db_id)
|
||||
@ -71,6 +81,7 @@ class LightRagKB(KnowledgeBase):
|
||||
graph_storage="Neo4JStorage",
|
||||
doc_status_storage="JsonDocStatusStorage",
|
||||
log_file_path=os.path.join(working_dir, "lightrag.log"),
|
||||
addon_params=addon_params,
|
||||
)
|
||||
|
||||
return rag
|
||||
@ -107,7 +118,18 @@ class LightRagKB(KnowledgeBase):
|
||||
def _get_llm_func(self, llm_info: dict):
|
||||
"""获取 LLM 函数"""
|
||||
from src.models import select_model
|
||||
model = select_model(LIGHTRAG_LLM_PROVIDER, LIGHTRAG_LLM_NAME)
|
||||
|
||||
# 如果用户选择了LLM,使用用户选择的;否则使用环境变量默认值
|
||||
if llm_info and llm_info.get("provider") and llm_info.get("model_name"):
|
||||
provider = llm_info["provider"]
|
||||
model_name = llm_info["model_name"]
|
||||
logger.info(f"Using user-selected LLM: {provider}/{model_name}")
|
||||
else:
|
||||
provider = LIGHTRAG_LLM_PROVIDER
|
||||
model_name = LIGHTRAG_LLM_NAME
|
||||
logger.info(f"Using default LLM from environment: {provider}/{model_name}")
|
||||
|
||||
model = select_model(provider, model_name)
|
||||
|
||||
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
|
||||
return await openai_complete_if_cache(
|
||||
|
||||
@ -101,7 +101,7 @@ class MilvusKB(KnowledgeBase):
|
||||
raise ValueError(f"Database {db_id} not found")
|
||||
|
||||
embed_info = self.databases_meta[db_id].get("embed_info", {})
|
||||
collection_name = f"kb_{db_id}"
|
||||
collection_name = db_id
|
||||
|
||||
try:
|
||||
# 检查集合是否存在
|
||||
@ -256,7 +256,7 @@ class MilvusKB(KnowledgeBase):
|
||||
self._save_metadata()
|
||||
|
||||
file_record["file_id"] = file_id
|
||||
|
||||
|
||||
# 添加到处理队列
|
||||
self._add_to_processing_queue(file_id)
|
||||
|
||||
|
||||
1
test/data/lightrag_kb_test_tiny.txt
Normal file
1
test/data/lightrag_kb_test_tiny.txt
Normal file
@ -0,0 +1 @@
|
||||
《红楼梦》是中国古典四大名著之一,由曹雪芹创作
|
||||
@ -370,10 +370,7 @@ const printDatabaseInfo = async () => {
|
||||
if (!checkAdminPermission()) return;
|
||||
|
||||
try {
|
||||
console.log('=== 知识库信息 ===');
|
||||
|
||||
// 直接调用API获取最新的数据库信息
|
||||
await databaseStore.refreshDatabase();
|
||||
console.log('知识库信息', databaseStore.database);
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取知识库信息失败:', error);
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
size="small"
|
||||
@click="loadGraph"
|
||||
:disabled="!isGraphSupported"
|
||||
:icon='h(ReloadOutlined)'
|
||||
>
|
||||
加载图谱
|
||||
</a-button>
|
||||
@ -153,7 +154,6 @@
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useDatabaseStore } from '@/stores/database';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import { getKbTypeLabel } from '@/utils/kb_utils';
|
||||
import { ReloadOutlined, DeleteOutlined, ExpandOutlined, UpOutlined, DownOutlined, SettingOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import KnowledgeGraphViewer from '@/components/KnowledgeGraphViewer.vue';
|
||||
@ -208,6 +208,9 @@ const toggleVisible = () => {
|
||||
};
|
||||
|
||||
const loadGraph = () => {
|
||||
if (!(Object.keys(store.database?.files).length > 0)) {
|
||||
return;
|
||||
}
|
||||
if (graphViewerRef.value && typeof graphViewerRef.value.loadFullGraph === 'function') {
|
||||
graphViewerRef.value.loadFullGraph();
|
||||
}
|
||||
|
||||
@ -241,17 +241,17 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
try {
|
||||
const response = await queryApi.getKnowledgeBaseQueryParams(db_id);
|
||||
queryParams.value = response.params?.options || [];
|
||||
|
||||
|
||||
// Create a set of currently supported parameter keys
|
||||
const supportedParamKeys = new Set(queryParams.value.map(param => param.key));
|
||||
|
||||
|
||||
// Remove unsupported parameters from meta
|
||||
for (const key in meta) {
|
||||
if (key !== 'db_id' && !supportedParamKeys.has(key)) {
|
||||
delete meta[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add default values for supported parameters that are not in meta
|
||||
queryParams.value.forEach(param => {
|
||||
if (!(param.key in meta)) {
|
||||
@ -291,16 +291,16 @@ export const useDatabaseStore = defineStore('database', () => {
|
||||
stopAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function selectAllFailedFiles() {
|
||||
const files = Object.values(database.value.files || {});
|
||||
const failedFiles = files
|
||||
.filter(file => file.status === 'failed')
|
||||
.map(file => file.file_id);
|
||||
|
||||
|
||||
const newSelectedKeys = [...new Set([...selectedRowKeys.value, ...failedFiles])];
|
||||
selectedRowKeys.value = newSelectedKeys;
|
||||
|
||||
|
||||
if (failedFiles.length > 0) {
|
||||
message.success(`已选择 ${failedFiles.length} 个失败的文件`);
|
||||
} else {
|
||||
|
||||
@ -47,18 +47,26 @@
|
||||
<h3>嵌入模型</h3>
|
||||
<a-select v-model:value="newDatabase.embed_model_name" :options="embedModelOptions" style="width: 100%;" size="large" />
|
||||
|
||||
<!-- 根据类型显示不同配置 -->
|
||||
<!-- <div v-if="newDatabase.kb_type === 'chroma' || newDatabase.kb_type === 'milvus'" class="storage-config">
|
||||
<h3>存储配置</h3>
|
||||
<div class="param-row">
|
||||
<label>存储方式:</label>
|
||||
<a-select v-model:value="newDatabase.storage" style="width: 200px;">
|
||||
<a-select-option value="DemoA">DemoA</a-select-option>
|
||||
<a-select-option value="DemoB">DemoB</a-select-option>
|
||||
</a-select>
|
||||
<span class="param-hint">存储方式配置(功能预留)</span>
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- 仅对 LightRAG 提供语言选择和LLM选择 -->
|
||||
<div v-if="newDatabase.kb_type === 'lightrag'">
|
||||
<h3 style="margin-top: 20px;">语言</h3>
|
||||
<a-select
|
||||
v-model:value="newDatabase.language"
|
||||
:options="languageOptions"
|
||||
style="width: 100%;"
|
||||
size="large"
|
||||
:dropdown-match-select-width="false"
|
||||
/>
|
||||
|
||||
<h3 style="margin-top: 20px;">语言模型 (LLM)</h3>
|
||||
<p style="color: var(--gray-700); font-size: 14px;">可以在设置中配置语言模型</p>
|
||||
<ModelSelectorComponent
|
||||
:model_name="newDatabase.llm_info.model_name || '请选择模型'"
|
||||
:model_provider="newDatabase.llm_info.provider || ''"
|
||||
@select-model="handleLLMSelect"
|
||||
style="width: 100%; height: 60px;"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 20px;">知识库描述</h3>
|
||||
<p style="color: var(--gray-700); font-size: 14px;">在智能体流程中,这里的描述会作为工具的描述。智能体会根据知识库的标题和描述来选择合适的工具。所以这里描述的越详细,智能体越容易选择到合适的工具。</p>
|
||||
@ -72,13 +80,13 @@
|
||||
<a-button key="submit" type="primary" :loading="state.creating" @click="createDatabase">创建</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="state.loading" class="loading-container">
|
||||
<a-spin size="large" />
|
||||
<p>正在加载知识库...</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 数据库列表 -->
|
||||
<div v-else class="databases">
|
||||
<div class="new-database dbcard" @click="state.openNewDatabaseModel=true">
|
||||
@ -115,7 +123,7 @@
|
||||
<p class="description">{{ database.description || '暂无描述' }}</p>
|
||||
<div class="tags">
|
||||
<a-tag color="blue" v-if="database.embed_info?.name">{{ database.embed_info.name }}</a-tag>
|
||||
<a-tag color="green" v-if="database.embed_info?.dimension">{{ database.embed_info.dimension }}</a-tag>
|
||||
<!-- <a-tag color="green" v-if="database.embed_info?.dimension">{{ database.embed_info.dimension }}</a-tag> -->
|
||||
<a-tag
|
||||
:color="getKbTypeColor(database.kb_type || 'lightrag')"
|
||||
class="kb-type-tag"
|
||||
@ -139,6 +147,7 @@ import { message } from 'ant-design-vue'
|
||||
import { BookPlus, Database, Zap, FileDigit, Waypoints, Building2 } from 'lucide-vue-next';
|
||||
import { databaseApi, typeApi } from '@/apis/knowledge_api';
|
||||
import HeaderComponent from '@/components/HeaderComponent.vue';
|
||||
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@ -158,6 +167,21 @@ const embedModelOptions = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
// 语言选项(值使用英文,以保证后端/LightRAG 兼容;标签为中英文方便理解)
|
||||
const languageOptions = [
|
||||
{ label: '英语 English', value: 'English' },
|
||||
{ label: '中文 Chinese', value: 'Chinese' },
|
||||
{ label: '日语 Japanese', value: 'Japanese' },
|
||||
{ label: '韩语 Korean', value: 'Korean' },
|
||||
{ label: '德语 German', value: 'German' },
|
||||
{ label: '法语 French', value: 'French' },
|
||||
{ label: '西班牙语 Spanish', value: 'Spanish' },
|
||||
{ label: '葡萄牙语 Portuguese', value: 'Portuguese' },
|
||||
{ label: '俄语 Russian', value: 'Russian' },
|
||||
{ label: '阿拉伯语 Arabic', value: 'Arabic' },
|
||||
{ label: '印地语 Hindi', value: 'Hindi' },
|
||||
]
|
||||
|
||||
const emptyEmbedInfo = {
|
||||
name: '',
|
||||
description: '',
|
||||
@ -165,6 +189,12 @@ const emptyEmbedInfo = {
|
||||
kb_type: 'chroma', // 默认为 Milvus
|
||||
// Vector 知识库特有配置
|
||||
storage: '', // 存储方式配置
|
||||
// LightRAG 特有配置
|
||||
language: 'English',
|
||||
llm_info: {
|
||||
provider: '',
|
||||
model_name: ''
|
||||
},
|
||||
}
|
||||
|
||||
const newDatabase = reactive({
|
||||
@ -312,6 +342,13 @@ const handleKbTypeChange = (type) => {
|
||||
newDatabase.kb_type = type
|
||||
}
|
||||
|
||||
// 处理LLM选择
|
||||
const handleLLMSelect = (selection) => {
|
||||
console.log('LLM选择:', selection)
|
||||
newDatabase.llm_info.provider = selection.provider
|
||||
newDatabase.llm_info.model_name = selection.name
|
||||
}
|
||||
|
||||
const createDatabase = () => {
|
||||
if (!newDatabase.name?.trim()) {
|
||||
message.error('数据库名称不能为空')
|
||||
@ -338,6 +375,17 @@ const createDatabase = () => {
|
||||
requestData.additional_params.storage = newDatabase.storage || 'DemoA'
|
||||
}
|
||||
|
||||
if (newDatabase.kb_type === 'lightrag') {
|
||||
requestData.additional_params.language = newDatabase.language || 'English'
|
||||
// 添加LLM信息到请求数据
|
||||
if (newDatabase.llm_info.provider && newDatabase.llm_info.model_name) {
|
||||
requestData.llm_info = {
|
||||
provider: newDatabase.llm_info.provider,
|
||||
model_name: newDatabase.llm_info.model_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
databaseApi.createDatabase(requestData)
|
||||
.then(data => {
|
||||
console.log('创建成功:', data)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user