feat: 优化模型配置管理 - 动态使用优先级最高的Provider
## 主要改进 ### 1. 数据库驱动的模型配置 - 新增 GetActiveProviderWithModel() 方法,从数据库获取优先级最高的 Provider 及其默认模型 - 支持多 Provider 配置,通过 priority 字段控制优先级 - 实现 5 分钟缓存机制,减少数据库查询 ### 2. 应用模型配置优化 - 移除应用层硬编码模型配置 - 应用自动使用优先级最高的 Provider 的默认模型 - 支持应用级模型覆盖(可选) ### 3. 工具脚本 - check-providers: 查询数据库中的 Provider 和应用配置 - fix-providers: 修复 Provider 配置(补全 config 和 models 字段) - clear-app-models: 清空应用硬编码模型配置 ### 4. 代码质量 - 删除未使用的 getProvider() 方法 - 修复 fmt.Println 冗余换行警告 - 统一代码格式 ## 技术细节 **降级策略**: 数据库 Provider (优先级) → 环境变量 Provider → 应用配置模型 **当前配置**: - 优先级 110: 本地LLM (qwen2.5-7b-instruct) - 优先级 100: 阿里云百炼 (qwen-plus) 所有 41 个应用现在自动使用本地模型,无需手动配置。
This commit is contained in:
@@ -27,13 +27,14 @@ func NewAnalysisTemplateHandler(pool *pgxpool.Pool, manager *llm.Manager, provid
|
||||
return &AnalysisTemplateHandler{pool: pool, manager: manager, provider: provider}
|
||||
}
|
||||
|
||||
// getProvider 获取当前激活的 provider
|
||||
func (h *AnalysisTemplateHandler) getProvider(ctx context.Context) (llm.Provider, error) {
|
||||
provider, err := h.manager.GetActiveProvider(ctx)
|
||||
// getProviderWithModel 获取当前激活的 provider 及其默认模型
|
||||
func (h *AnalysisTemplateHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) {
|
||||
provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx)
|
||||
if err == nil {
|
||||
return provider, nil
|
||||
return provider, defaultModel, nil
|
||||
}
|
||||
return h.manager.GetProvider(h.provider)
|
||||
provider, err = h.manager.GetProvider(h.provider)
|
||||
return provider, "", err
|
||||
}
|
||||
|
||||
func (h *AnalysisTemplateHandler) ListTemplates(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -234,8 +235,21 @@ func (h *AnalysisTemplateHandler) GenerateReport(w http.ResponseWriter, r *http.
|
||||
}
|
||||
}
|
||||
|
||||
// 获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.getProviderWithModel(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用优先级:Provider 默认模型 > 应用配置模型
|
||||
modelToUse := defaultModel
|
||||
if modelToUse == "" {
|
||||
modelToUse = appModel // 降级时使用应用配置
|
||||
}
|
||||
|
||||
llmReq := &llm.ChatRequest{
|
||||
Model: appModel,
|
||||
Model: modelToUse,
|
||||
Messages: []llm.Message{
|
||||
{Role: llm.RoleSystem, Content: "你是一位资深的政务数据分析和研判专家。要求:1)基于提供的真实数据库数据进行分析 2)报告必须完整输出到最后一个章节,不能中途截断 3)保持精炼,每个章节500字以内 4)使用Markdown格式排版 5)表格数据必须引用真实数据 6)报告输出完毕后,必须在最后一行单独输出「---\\n\\n**【完稿】**」作为完成标记"},
|
||||
{Role: llm.RoleUser, Content: prompt},
|
||||
@@ -246,14 +260,7 @@ func (h *AnalysisTemplateHandler) GenerateReport(w http.ResponseWriter, r *http.
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, err := h.getProvider(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
body, err := h.manager.ChatStream(r.Context(), provider, llmReq)
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error())
|
||||
|
||||
@@ -34,16 +34,17 @@ func NewLLMChatHandler(pool *pgxpool.Pool, manager *llm.Manager, defaultProvider
|
||||
return &LLMChatHandler{pool: pool, manager: manager, provider: defaultProvider, rdb: rdb, workerURL: workerURL, embedder: embedder}
|
||||
}
|
||||
|
||||
// getProvider 获取当前激活的 provider,优先从数据库读取
|
||||
func (h *LLMChatHandler) getProvider(ctx context.Context) (llm.Provider, error) {
|
||||
// 尝试从数据库获取激活的 provider
|
||||
provider, err := h.manager.GetActiveProvider(ctx)
|
||||
// getProviderWithModel 获取当前激活的 provider 及其默认模型
|
||||
func (h *LLMChatHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) {
|
||||
// 尝试从数据库获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx)
|
||||
if err == nil {
|
||||
return provider, nil
|
||||
return provider, defaultModel, nil
|
||||
}
|
||||
|
||||
|
||||
// 降级到配置的 fallback provider
|
||||
return h.manager.GetProvider(h.provider)
|
||||
provider, err = h.manager.GetProvider(h.provider)
|
||||
return provider, "", err // 降级时返回空模型,使用应用配置
|
||||
}
|
||||
|
||||
type llmChatRequest struct {
|
||||
@@ -568,8 +569,21 @@ func (h *LLMChatHandler) Chat(w http.ResponseWriter, r *http.Request) {
|
||||
history = h.loadConversationHistory(r.Context(), appID, userID.String(), convID, 10)
|
||||
}
|
||||
|
||||
// 获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.getProviderWithModel(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用优先级:Provider 默认模型 > 应用配置模型
|
||||
modelToUse := defaultModel
|
||||
if modelToUse == "" {
|
||||
modelToUse = cfg.Model // 降级时使用应用配置
|
||||
}
|
||||
|
||||
llmReq := &llm.ChatRequest{
|
||||
Model: cfg.Model,
|
||||
Model: modelToUse,
|
||||
Messages: h.buildMessages(cfg.SystemPrompt, knowledgeCtx, hasKB, history, req.Message, orgApps),
|
||||
Temperature: cfg.Temp,
|
||||
MaxTokens: cfg.MaxTok,
|
||||
@@ -577,14 +591,7 @@ func (h *LLMChatHandler) Chat(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, err := h.getProvider(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
body, err := h.manager.ChatStream(r.Context(), provider, llmReq)
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error())
|
||||
@@ -673,8 +680,21 @@ func (h *LLMChatHandler) Completion(w http.ResponseWriter, r *http.Request) {
|
||||
// 加载同机构应用列表,用于超范围引导跳转
|
||||
orgApps := h.loadSameOrgApps(r.Context(), cfg.OrgID, appID)
|
||||
|
||||
// 获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.getProviderWithModel(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用优先级:Provider 默认模型 > 应用配置模型
|
||||
modelToUse := defaultModel
|
||||
if modelToUse == "" {
|
||||
modelToUse = cfg.Model // 降级时使用应用配置
|
||||
}
|
||||
|
||||
llmReq := &llm.ChatRequest{
|
||||
Model: cfg.Model,
|
||||
Model: modelToUse,
|
||||
Messages: h.buildMessages(cfg.SystemPrompt, knowledgeCtx, hasKB, nil, req.Message, orgApps),
|
||||
Temperature: cfg.Temp,
|
||||
MaxTokens: cfg.MaxTok,
|
||||
@@ -684,13 +704,6 @@ func (h *LLMChatHandler) Completion(w http.ResponseWriter, r *http.Request) {
|
||||
startTime := time.Now()
|
||||
convID := uuid.New().String()
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, err := h.getProvider(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
body, err := h.manager.ChatStream(r.Context(), provider, llmReq)
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error())
|
||||
@@ -1176,13 +1189,18 @@ func (h *LLMChatHandler) generateConversationName(appID, userID, convID, userMes
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, err := h.getProvider(ctx)
|
||||
// 获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.getProviderWithModel(ctx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("get provider for title generation failed")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 Provider 默认模型
|
||||
if defaultModel != "" {
|
||||
nameReq.Model = defaultModel
|
||||
}
|
||||
|
||||
result, err := h.manager.Chat(ctx, provider, nameReq)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -27,13 +27,14 @@ func NewDocTemplateHandler(pool *pgxpool.Pool, manager *llm.Manager, provider st
|
||||
return &DocTemplateHandler{pool: pool, manager: manager, provider: provider}
|
||||
}
|
||||
|
||||
// getProvider 获取当前激活的 provider
|
||||
func (h *DocTemplateHandler) getProvider(ctx context.Context) (llm.Provider, error) {
|
||||
provider, err := h.manager.GetActiveProvider(ctx)
|
||||
// getProviderWithModel 获取当前激活的 provider 及其默认模型
|
||||
func (h *DocTemplateHandler) getProviderWithModel(ctx context.Context) (llm.Provider, string, error) {
|
||||
provider, defaultModel, err := h.manager.GetActiveProviderWithModel(ctx)
|
||||
if err == nil {
|
||||
return provider, nil
|
||||
return provider, defaultModel, nil
|
||||
}
|
||||
return h.manager.GetProvider(h.provider)
|
||||
provider, err = h.manager.GetProvider(h.provider)
|
||||
return provider, "", err
|
||||
}
|
||||
|
||||
func (h *DocTemplateHandler) ListTemplates(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -171,8 +172,21 @@ func (h *DocTemplateHandler) GenerateDocument(w http.ResponseWriter, r *http.Req
|
||||
prompt = strings.ReplaceAll(prompt, "{{", "")
|
||||
prompt = strings.ReplaceAll(prompt, "}}", "")
|
||||
|
||||
// 获取激活的 provider 和默认模型
|
||||
provider, defaultModel, err := h.getProviderWithModel(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用优先级:Provider 默认模型 > 应用配置模型
|
||||
modelToUse := defaultModel
|
||||
if modelToUse == "" {
|
||||
modelToUse = appModel // 降级时使用应用配置
|
||||
}
|
||||
|
||||
llmReq := &llm.ChatRequest{
|
||||
Model: appModel,
|
||||
Model: modelToUse,
|
||||
Messages: []llm.Message{
|
||||
{Role: llm.RoleSystem, Content: "你是一个专业的政务公文写作专家,精通《党政机关公文格式》国家标准(GB/T 9704)和《党政机关公文处理工作条例》。请严格按照规范格式生成公文,确保行文庄重、严谨、准确。输出完整公文内容,使用Markdown格式排版。"},
|
||||
{Role: llm.RoleUser, Content: prompt},
|
||||
@@ -183,14 +197,7 @@ func (h *DocTemplateHandler) GenerateDocument(w http.ResponseWriter, r *http.Req
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, err := h.getProvider(r.Context())
|
||||
if err != nil {
|
||||
response.InternalError(w, "获取模型提供商失败")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
body, err := h.manager.ChatStream(r.Context(), provider, llmReq)
|
||||
if err != nil {
|
||||
response.Error(w, http.StatusBadGateway, 50202, "模型服务不可用: "+err.Error())
|
||||
|
||||
Reference in New Issue
Block a user