feat: 重构知识库系统,移除Hermes集成,增强RAG和同步功能
主要变更: - 移除Hermes智能体及相关回调服务 - 新增知识库RAG、同步、调度、规范化和索引任务服务 - 重构orchestrator服务,增强运行时聊天功能 - 更新前端聊天、政策制度、设置等页面样式和逻辑 - 更新expense_claims和document_intelligence服务 - 删除llm_wiki相关服务和测试文件 - 更新docker-compose配置和启动脚本
This commit is contained in:
@@ -39,8 +39,8 @@ const SECTION_DEFINITIONS = [
|
||||
id: 'llm',
|
||||
label: '大语言模型',
|
||||
title: '模型接入配置',
|
||||
desc: '主模型、备份模型与多模态模型',
|
||||
longDesc: '集中维护主模型、备份模型、VLM 模型和 Embedding 模型的接入参数,供 AI 助手和识别链路调用。',
|
||||
desc: '主模型、备份模型与检索模型',
|
||||
longDesc: '集中维护主模型、备份模型、Embedding 模型和 Reranker 模型的接入参数,供 AI 助手和检索链路调用。',
|
||||
actionLabel: '保存模型配置'
|
||||
},
|
||||
{
|
||||
@@ -82,7 +82,7 @@ const PROVIDER_OPTIONS = [
|
||||
CUSTOM_OPENAI_PROVIDER
|
||||
]
|
||||
|
||||
const PROVIDER_ENDPOINTS = {
|
||||
const PROVIDER_ENDPOINTS = {
|
||||
MiniMax: 'https://api.minimaxi.com/v1',
|
||||
GLM: 'https://open.bigmodel.cn/api/paas/v4/',
|
||||
Kimi: 'https://api.moonshot.ai/v1',
|
||||
@@ -90,8 +90,13 @@ const PROVIDER_ENDPOINTS = {
|
||||
Codex: 'https://api.openai.com/v1',
|
||||
Claude: 'https://api.anthropic.com/v1/',
|
||||
Gemini: 'https://generativelanguage.googleapis.com/v1beta/openai/',
|
||||
[CUSTOM_OPENAI_PROVIDER]: ''
|
||||
}
|
||||
[CUSTOM_OPENAI_PROVIDER]: ''
|
||||
}
|
||||
|
||||
const RERANKER_PROVIDER_ENDPOINTS = {
|
||||
Ali: 'https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank',
|
||||
[CUSTOM_OPENAI_PROVIDER]: ''
|
||||
}
|
||||
|
||||
const LEGACY_PROVIDER_MAP = {
|
||||
'OpenAI Compatible': 'Codex',
|
||||
@@ -117,23 +122,23 @@ const MODEL_TEST_CONFIGS = {
|
||||
apiKeyKey: 'backupApiKey',
|
||||
capability: 'chat'
|
||||
},
|
||||
vlm: {
|
||||
label: 'VLM 模型',
|
||||
providerKey: 'vlmProvider',
|
||||
modelKey: 'vlmModel',
|
||||
endpointKey: 'vlmEndpoint',
|
||||
apiKeyKey: 'vlmApiKey',
|
||||
capability: 'chat'
|
||||
},
|
||||
embedding: {
|
||||
label: 'Embedding 模型',
|
||||
providerKey: 'embeddingProvider',
|
||||
modelKey: 'embeddingModel',
|
||||
endpointKey: 'embeddingEndpoint',
|
||||
apiKeyKey: 'embeddingApiKey',
|
||||
capability: 'embedding'
|
||||
}
|
||||
}
|
||||
embedding: {
|
||||
label: 'Embedding 模型',
|
||||
providerKey: 'embeddingProvider',
|
||||
modelKey: 'embeddingModel',
|
||||
endpointKey: 'embeddingEndpoint',
|
||||
apiKeyKey: 'embeddingApiKey',
|
||||
capability: 'embedding'
|
||||
},
|
||||
reranker: {
|
||||
label: 'Reranker 模型',
|
||||
providerKey: 'rerankerProvider',
|
||||
modelKey: 'rerankerModel',
|
||||
endpointKey: 'rerankerEndpoint',
|
||||
apiKeyKey: 'rerankerApiKey',
|
||||
capability: 'reranker'
|
||||
}
|
||||
}
|
||||
|
||||
const MODEL_API_KEY_CONFIGS = Object.values(MODEL_TEST_CONFIGS)
|
||||
const SESSION_RETENTION_OPTIONS = Array.from({ length: 10 }, (_item, index) => ({
|
||||
@@ -159,9 +164,13 @@ function normalizeProviderValue(value, fallback = 'Codex') {
|
||||
return fallback
|
||||
}
|
||||
|
||||
function getProviderEndpoint(provider) {
|
||||
return PROVIDER_ENDPOINTS[provider] ?? ''
|
||||
}
|
||||
function getProviderEndpoint(provider) {
|
||||
return PROVIDER_ENDPOINTS[provider] ?? ''
|
||||
}
|
||||
|
||||
function getRerankerEndpoint(provider) {
|
||||
return RERANKER_PROVIDER_ENDPOINTS[provider] ?? getProviderEndpoint(provider)
|
||||
}
|
||||
|
||||
function buildDefaultState(companyProfile, currentUser) {
|
||||
const companyName = normalizeValue(companyProfile?.name) || 'X-Financial'
|
||||
@@ -201,21 +210,21 @@ function buildDefaultState(companyProfile, currentUser) {
|
||||
mainEndpoint: getProviderEndpoint('Codex'),
|
||||
mainApiKey: '',
|
||||
mainApiKeyConfigured: false,
|
||||
backupProvider: 'GLM',
|
||||
backupModel: 'glm-5.1',
|
||||
backupEndpoint: getProviderEndpoint('GLM'),
|
||||
backupApiKey: '',
|
||||
backupApiKeyConfigured: false,
|
||||
vlmProvider: 'Gemini',
|
||||
vlmModel: 'gemini-2.5-flash',
|
||||
vlmEndpoint: getProviderEndpoint('Gemini'),
|
||||
vlmApiKey: '',
|
||||
vlmApiKeyConfigured: false,
|
||||
embeddingProvider: 'GLM',
|
||||
embeddingModel: 'Embedding-3',
|
||||
backupProvider: 'GLM',
|
||||
backupModel: 'glm-5.1',
|
||||
backupEndpoint: getProviderEndpoint('GLM'),
|
||||
backupApiKey: '',
|
||||
backupApiKeyConfigured: false,
|
||||
embeddingProvider: 'GLM',
|
||||
embeddingModel: 'Embedding-3',
|
||||
embeddingEndpoint: getProviderEndpoint('GLM'),
|
||||
embeddingApiKey: '',
|
||||
embeddingApiKeyConfigured: false
|
||||
embeddingApiKeyConfigured: false,
|
||||
rerankerProvider: 'Ali',
|
||||
rerankerModel: 'gte-rerank-v2',
|
||||
rerankerEndpoint: getRerankerEndpoint('Ali'),
|
||||
rerankerApiKey: '',
|
||||
rerankerApiKeyConfigured: false
|
||||
},
|
||||
renderForm: {
|
||||
enabled: false,
|
||||
@@ -268,15 +277,18 @@ function readStoredSettings() {
|
||||
}
|
||||
|
||||
function mergeState(baseState, overrideState) {
|
||||
const mergedLlmForm = { ...baseState.llmForm, ...(overrideState?.llmForm || {}) }
|
||||
|
||||
mergedLlmForm.mainProvider = normalizeProviderValue(mergedLlmForm.mainProvider, baseState.llmForm.mainProvider)
|
||||
mergedLlmForm.backupProvider = normalizeProviderValue(mergedLlmForm.backupProvider, baseState.llmForm.backupProvider)
|
||||
mergedLlmForm.vlmProvider = normalizeProviderValue(mergedLlmForm.vlmProvider, baseState.llmForm.vlmProvider)
|
||||
mergedLlmForm.embeddingProvider = normalizeProviderValue(
|
||||
mergedLlmForm.embeddingProvider,
|
||||
baseState.llmForm.embeddingProvider
|
||||
)
|
||||
const mergedLlmForm = { ...baseState.llmForm, ...(overrideState?.llmForm || {}) }
|
||||
|
||||
mergedLlmForm.mainProvider = normalizeProviderValue(mergedLlmForm.mainProvider, baseState.llmForm.mainProvider)
|
||||
mergedLlmForm.backupProvider = normalizeProviderValue(mergedLlmForm.backupProvider, baseState.llmForm.backupProvider)
|
||||
mergedLlmForm.embeddingProvider = normalizeProviderValue(
|
||||
mergedLlmForm.embeddingProvider,
|
||||
baseState.llmForm.embeddingProvider
|
||||
)
|
||||
mergedLlmForm.rerankerProvider = normalizeProviderValue(
|
||||
mergedLlmForm.rerankerProvider,
|
||||
baseState.llmForm.rerankerProvider
|
||||
)
|
||||
|
||||
return {
|
||||
companyForm: { ...baseState.companyForm, ...(overrideState?.companyForm || {}) },
|
||||
@@ -302,8 +314,8 @@ function sanitizeForStorage(state) {
|
||||
...state.llmForm,
|
||||
mainApiKey: '',
|
||||
backupApiKey: '',
|
||||
vlmApiKey: '',
|
||||
embeddingApiKey: ''
|
||||
embeddingApiKey: '',
|
||||
rerankerApiKey: ''
|
||||
},
|
||||
renderForm: {
|
||||
...state.renderForm,
|
||||
@@ -402,11 +414,15 @@ function computeSectionStatus(state) {
|
||||
llm: Boolean(
|
||||
isModelConfigReady(state.llmForm.mainProvider, state.llmForm.mainModel, state.llmForm.mainEndpoint) &&
|
||||
isModelConfigReady(state.llmForm.backupProvider, state.llmForm.backupModel, state.llmForm.backupEndpoint) &&
|
||||
isModelConfigReady(state.llmForm.vlmProvider, state.llmForm.vlmModel, state.llmForm.vlmEndpoint) &&
|
||||
isModelConfigReady(
|
||||
state.llmForm.embeddingProvider,
|
||||
state.llmForm.embeddingModel,
|
||||
state.llmForm.embeddingEndpoint
|
||||
) &&
|
||||
isModelConfigReady(
|
||||
state.llmForm.rerankerProvider,
|
||||
state.llmForm.rerankerModel,
|
||||
state.llmForm.rerankerEndpoint
|
||||
)
|
||||
),
|
||||
rendering: Boolean(
|
||||
@@ -440,11 +456,11 @@ export default {
|
||||
const sessionRetentionPickerOpen = ref(false)
|
||||
const sessionRetentionPickerRef = ref(null)
|
||||
const modelTestState = ref({
|
||||
main: { status: 'idle', message: '' },
|
||||
backup: { status: 'idle', message: '' },
|
||||
vlm: { status: 'idle', message: '' },
|
||||
embedding: { status: 'idle', message: '' }
|
||||
})
|
||||
main: { status: 'idle', message: '' },
|
||||
backup: { status: 'idle', message: '' },
|
||||
embedding: { status: 'idle', message: '' },
|
||||
reranker: { status: 'idle', message: '' }
|
||||
})
|
||||
|
||||
const sections = SECTION_DEFINITIONS
|
||||
const logLevels = LOG_LEVELS
|
||||
@@ -484,8 +500,8 @@ export default {
|
||||
if (preserveModelApiKeys) {
|
||||
nextState.llmForm.mainApiKey = currentState.llmForm.mainApiKey
|
||||
nextState.llmForm.backupApiKey = currentState.llmForm.backupApiKey
|
||||
nextState.llmForm.vlmApiKey = currentState.llmForm.vlmApiKey
|
||||
nextState.llmForm.embeddingApiKey = currentState.llmForm.embeddingApiKey
|
||||
nextState.llmForm.rerankerApiKey = currentState.llmForm.rerankerApiKey
|
||||
}
|
||||
|
||||
if (preserveAdminPasswords) {
|
||||
@@ -582,7 +598,8 @@ export default {
|
||||
const provider = normalizeProviderValue(llmForm[config.providerKey], CUSTOM_OPENAI_PROVIDER)
|
||||
|
||||
llmForm[config.providerKey] = provider
|
||||
llmForm[config.endpointKey] = getProviderEndpoint(provider)
|
||||
llmForm[config.endpointKey] =
|
||||
slot === 'reranker' ? getRerankerEndpoint(provider) : getProviderEndpoint(provider)
|
||||
}
|
||||
|
||||
function getModelTestState(testKey) {
|
||||
@@ -735,12 +752,12 @@ export default {
|
||||
|
||||
async function saveLlmSection() {
|
||||
const llmForm = pageState.value.llmForm
|
||||
const modelConfigs = [
|
||||
['主模型', llmForm.mainProvider, llmForm.mainModel, llmForm.mainEndpoint],
|
||||
['备份模型', llmForm.backupProvider, llmForm.backupModel, llmForm.backupEndpoint],
|
||||
['VLM 模型', llmForm.vlmProvider, llmForm.vlmModel, llmForm.vlmEndpoint],
|
||||
['Embedding 模型', llmForm.embeddingProvider, llmForm.embeddingModel, llmForm.embeddingEndpoint]
|
||||
]
|
||||
const modelConfigs = [
|
||||
['主模型', llmForm.mainProvider, llmForm.mainModel, llmForm.mainEndpoint],
|
||||
['备份模型', llmForm.backupProvider, llmForm.backupModel, llmForm.backupEndpoint],
|
||||
['Embedding 模型', llmForm.embeddingProvider, llmForm.embeddingModel, llmForm.embeddingEndpoint],
|
||||
['Reranker 模型', llmForm.rerankerProvider, llmForm.rerankerModel, llmForm.rerankerEndpoint]
|
||||
]
|
||||
|
||||
for (const [label, provider, model, endpoint] of modelConfigs) {
|
||||
if (!isModelConfigReady(provider, model, endpoint)) {
|
||||
|
||||
Reference in New Issue
Block a user