feat: 优化追问功能 - LLM智能生成 + 显示位置修复
- 修复 AuthLoader 组件中 router.push 在渲染期间调用的 React 警告
- 追问问题改为只在最新 AI 回复下方显示(修复多处显示问题)
- 新增 LLM 生成追问 API:POST /api/v1/apps/{id}/suggestions
- 前端调用 LLM API 生成智能追问,失败时降级到规则生成
- 登录页填入默认测试账号 kj-admin@govai.gov.cn
This commit is contained in:
@@ -132,6 +132,7 @@ func newRouter(cfg *config.Config, pool *pgxpool.Pool, rdb *redis.Client) http.H
|
||||
r.Post("/generate-analysis", analysisH.GenerateReport)
|
||||
r.Get("/conversations", chatH.Conversations)
|
||||
r.Get("/conversations/{convId}/messages", chatH.Messages)
|
||||
r.Post("/suggestions", chatH.GetSuggestions)
|
||||
r.Delete("/conversations/{convId}", chatH.DeleteConversation)
|
||||
r.Put("/conversations/{convId}/name", chatH.RenameConversation)
|
||||
r.Post("/conversations/batch-delete", chatH.BatchDeleteConversations)
|
||||
|
||||
@@ -1737,3 +1737,100 @@ func (h *LLMChatHandler) generateConversationName(appID, userID, convID, userMes
|
||||
DO UPDATE SET name = EXCLUDED.name, updated_at = now()`,
|
||||
appID, userID, convID, name)
|
||||
}
|
||||
|
||||
// GenerateSuggestions 生成追问建议(由 LLM 智能生成)
|
||||
func (h *LLMChatHandler) GenerateSuggestions(ctx context.Context, conversationID, appID string, messages []map[string]string) ([]string, error) {
|
||||
if len(messages) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 构建对话历史上下文
|
||||
var historyBuilder strings.Builder
|
||||
for i, msg := range messages {
|
||||
role := "用户"
|
||||
if msg["role"] == "assistant" {
|
||||
role = "助手"
|
||||
}
|
||||
historyBuilder.WriteString(fmt.Sprintf("%d. %s:%s\n", i+1, role, msg["content"]))
|
||||
}
|
||||
|
||||
systemPrompt := `你是一个智能政务问答助手。请根据对话历史,生成4个追问问题,帮助用户深入了解相关内容。
|
||||
|
||||
要求:
|
||||
1. 问题必须与对话内容紧密相关
|
||||
2. 每个问题不超过20个字
|
||||
3. 问题要有深度,引导用户进一步思考
|
||||
4. 避免重复或过于宽泛的问题
|
||||
5. 直接输出问题,用换行分隔,不要编号,不要解释`
|
||||
|
||||
suggestReq := &llm.ChatRequest{
|
||||
Model: "",
|
||||
Messages: []llm.Message{
|
||||
{Role: "system", Content: systemPrompt},
|
||||
{Role: "user", Content: "对话历史:\n" + historyBuilder.String()},
|
||||
},
|
||||
Temperature: 0.7,
|
||||
MaxTokens: 200,
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
// 获取激活的 provider
|
||||
provider, defaultModel, err := h.getProviderWithModel(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if defaultModel != "" {
|
||||
suggestReq.Model = defaultModel
|
||||
}
|
||||
|
||||
result, err := h.manager.Chat(ctx, provider, suggestReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解析结果:按换行分割
|
||||
suggestions := strings.Split(strings.TrimSpace(result.Content), "\n")
|
||||
var validSuggestions []string
|
||||
for _, s := range suggestions {
|
||||
s = strings.TrimSpace(s)
|
||||
// 过滤空行和过长的行
|
||||
if len(s) > 0 && len([]rune(s)) <= 25 {
|
||||
validSuggestions = append(validSuggestions, s)
|
||||
}
|
||||
}
|
||||
|
||||
// 最多返回4个
|
||||
if len(validSuggestions) > 4 {
|
||||
validSuggestions = validSuggestions[:4]
|
||||
}
|
||||
|
||||
return validSuggestions, nil
|
||||
}
|
||||
|
||||
// GetSuggestions HTTP handler:获取追问建议
|
||||
func (h *LLMChatHandler) GetSuggestions(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
appID := chi.URLParam(r, "id")
|
||||
if appID == "" {
|
||||
response.Error(w, http.StatusBadRequest, 40001, "缺少应用ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ConversationID string `json:"conversation_id"`
|
||||
Messages []map[string]string `json:"messages"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
response.Error(w, http.StatusBadRequest, 40002, "请求格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
suggestions, err := h.GenerateSuggestions(ctx, req.ConversationID, appID, req.Messages)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("generate suggestions failed")
|
||||
response.Error(w, http.StatusInternalServerError, 50001, "生成追问失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(w, http.StatusOK, suggestions)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user