Files
X-Agents/server/internal/handler/agent_handler.go
DESKTOP-72TV0V4\caoxiaozhu 3a4876ab00 fix: 修复Python模块导入错误并优化Chat功能
- 修复 core/agents/api 模块导入问题
- 优化 ChatInput 组件交互体验
- 增强 agent_handler 和 agent_service 功能
- 调整 Chat 页面样式和布局

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 10:27:07 +08:00

441 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"log"
"net/http"
"strconv"
"strings"
"x-agents/server/internal/model"
"x-agents/server/internal/repository"
"x-agents/server/internal/service"
"x-agents/server/internal/utils"
"github.com/gin-gonic/gin"
)
// AgentHandler Agent 处理器
type AgentHandler struct {
agentService *service.AgentService
agentRepo *repository.AgentRepository
}
// NewAgentHandler 创建 Agent 处理器
func NewAgentHandler(agentService *service.AgentService, agentRepo *repository.AgentRepository) *AgentHandler {
return &AgentHandler{
agentService: agentService,
agentRepo: agentRepo,
}
}
// ChatRequest 对话请求
type ChatRequest struct {
AgentID string `json:"agent_id"` // 字符串类型,支持 UUID可为空当使用 mentioned_agent_ids 时)
Message string `json:"message" binding:"required"`
SessionID string `json:"session_id"`
ModelID string `json:"model_id"`
UseXBot bool `json:"use_xbot"`
MentionedAgentIDs []string `json:"mentioned_agent_ids"` // @ 提及的智能体 ID 列表
}
// ChatResponse 对话响应
type ChatResponse struct {
AgentID string `json:"agent_id"` // 支持 UUID 字符串
Reply string `json:"reply"`
ToolsUsed []string `json:"tools_used"`
SessionID string `json:"session_id"`
TokensUsed int `json:"tokens_used"`
DurationMs int `json:"duration_ms"`
Metadata interface{} `json:"metadata"`
}
// CreateAgentResponse 创建智能体响应
type CreateAgentResponse struct {
AgentID int `json:"agent_id"`
Name string `json:"name"`
Message string `json:"message"`
}
// ListAgentsResponse 获取智能体列表响应
type ListAgentsResponse struct {
Agents []interface{} `json:"agents"`
}
// Chat 单智能体对话
// @Summary 单智能体对话
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param request body ChatRequest true "对话请求"
// @Success 200 {object} ChatResponse
// @Router /api/agent/chat [post]
func (h *AgentHandler) Chat(c *gin.Context) {
var req ChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 获取用户 ID从认证中间件获取
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
// 直接使用字符串类型的 agent_id支持 UUID
pythonReq := service.AgentChatRequest{
AgentID: req.AgentID,
Message: req.Message,
UserID: userID,
SessionID: req.SessionID,
ModelID: req.ModelID,
UseXBot: req.UseXBot,
}
result, err := h.agentService.Chat(pythonReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 转换工具调用
toolsUsed := make([]string, 0)
for _, tool := range result.ToolCalls {
if toolMap, ok := tool.(map[string]interface{}); ok {
if skillID, ok := toolMap["skill_id"].(string); ok {
toolsUsed = append(toolsUsed, skillID)
}
}
}
c.JSON(http.StatusOK, ChatResponse{
AgentID: result.AgentID,
Reply: result.Response,
ToolsUsed: toolsUsed,
SessionID: result.SessionID,
TokensUsed: result.TokensUsed,
DurationMs: result.DurationMs,
Metadata: nil,
})
}
// ChatStream 单智能体对话(流式输出)
// @Summary 单智能体对话(流式输出)
// @Tags 智能体管理
// @Accept json
// @Produce text/event-stream
// @Param request body ChatRequest true "对话请求"
// @Router /api/agent/chat/stream [post]
func (h *AgentHandler) ChatStream(c *gin.Context) {
var req ChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 获取用户 ID
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
// 直接使用字符串类型的 agent_id支持 UUID
agentID := req.AgentID
// 优先使用前端传递的 mentioned_agent_ids
if len(req.MentionedAgentIDs) > 0 {
// 如果有多个 @ 提及,使用第一个
mentionedAgentID := req.MentionedAgentIDs[0]
log.Printf("[ChatStream] Using mentioned_agent_ids: %v", req.MentionedAgentIDs)
agentID = mentionedAgentID
// 清理消息,移除 @ 提及
mentionParser := utils.NewMentionParser()
req.Message = mentionParser.RemoveMentions(req.Message)
} else if agentID == "" {
// 兼容:解析消息中的 @ 提及(备用方案)
mentionParser := utils.NewMentionParser()
mentions := mentionParser.ParseMentions(req.Message)
if len(mentions) > 0 {
mentionedAgent := h.findAgentByName(mentions[0])
if mentionedAgent != nil {
log.Printf("[ChatStream] Detected @mention: %s, routing to agent: %s", mentions[0], mentionedAgent.ID)
agentID = mentionedAgent.ID
}
req.Message = mentionParser.RemoveMentions(req.Message)
}
}
// 构建 SSE 流
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
c.Header("Access-Control-Allow-Origin", "*")
// 调用 Python 服务的流式端点
err := h.agentService.ChatStream(c, agentID, req.Message, req.SessionID, req.ModelID, userID)
if err != nil && !c.IsAborted() {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
}
// findAgentByName 根据用户名查找智能体
func (h *AgentHandler) findAgentByName(name string) *model.Agent {
log.Printf("[findAgentByName] Searching for agent: %s, agentRepo: %v", name, h.agentRepo)
if h.agentRepo == nil {
log.Printf("[findAgentByName] ERROR: agentRepo is nil!")
return nil
}
// 先尝试精确匹配
agents, err := h.agentRepo.FindAll()
if err != nil {
return nil
}
for _, agent := range agents {
if agent.Name == name {
return &agent
}
}
// 再尝试模糊匹配(忽略大小写)
for _, agent := range agents {
if strings.Contains(strings.ToLower(agent.Name), strings.ToLower(name)) {
return &agent
}
}
return nil
}
// TeamChatRequest 多智能体群聊请求
type TeamChatRequest struct {
SupervisorAgentID int `json:"supervisor_agent_id" binding:"required"`
MemberAgentIDs []int `json:"member_agent_ids" binding:"required"`
Message string `json:"message" binding:"required"`
SessionID string `json:"session_id"`
Strategy string `json:"strategy"`
}
// TeamChatResponse 多智能体群聊响应
type TeamChatResponse struct {
SupervisorAgentID int `json:"supervisor_agent_id"`
Reply string `json:"reply"`
SubtaskResults interface{} `json:"subtask_results"`
Strategy string `json:"strategy"`
SessionID string `json:"session_id"`
DurationMs int `json:"duration_ms"`
Metadata interface{} `json:"metadata"`
}
// TeamChat 多智能体群聊
// @Summary 多智能体群聊
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param request body TeamChatRequest true "群聊请求"
// @Success 200 {object} TeamChatResponse
// @Router /api/agent/team/chat [post]
func (h *AgentHandler) TeamChat(c *gin.Context) {
var req TeamChatRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 获取用户 ID
userIDStr := "1" // TODO: 从 c.Get("user_id") 获取
userID, _ := strconv.Atoi(userIDStr)
pythonReq := service.TeamChatRequest{
SupervisorAgentID: req.SupervisorAgentID,
MemberAgentIDs: req.MemberAgentIDs,
Message: req.Message,
UserID: userID,
SessionID: req.SessionID,
Strategy: req.Strategy,
}
result, err := h.agentService.TeamChat(pythonReq)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, TeamChatResponse{
SupervisorAgentID: result.SupervisorAgentID,
Reply: result.Response,
SubtaskResults: result.SubtaskResults,
Strategy: result.Strategy,
SessionID: result.SessionID,
DurationMs: result.DurationMs,
Metadata: nil,
})
}
// CreateAgent 创建智能体
// @Summary 创建智能体
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param request body CreateAgentRequest true "创建智能体请求"
// @Success 200 {object} CreateAgentResponse
// @Router /api/agent/create [post]
func (h *AgentHandler) CreateAgent(c *gin.Context) {
var req service.CreateAgentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 获取用户 ID
userID := 1 // TODO: 从 c.Get("user_id") 获取
result, err := h.agentService.CreateAgent(req, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, result)
}
// InitTeamMembersResponse 初始化团队成员响应
type InitTeamMembersResponse struct {
Message string `json:"message"`
Count int `json:"count"`
}
// InitTeamMembers 初始化团队成员智能体
// @Summary 初始化团队成员智能体
// @Tags 智能体管理
// @Produce json
// @Success 200 {object} InitTeamMembersResponse
// @Router /api/agent/init-team [post]
func (h *AgentHandler) InitTeamMembers(c *gin.Context) {
if err := h.agentService.InitTeamMembers(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, InitTeamMembersResponse{
Message: "Team members initialized successfully",
Count: 1, // 小荣
})
}
// ListAgents 获取智能体列表
// @Summary 获取智能体列表
// @Tags 智能体管理
// @Produce json
// @Success 200 {object} ListAgentsResponse
// @Router /api/agent/list [get]
func (h *AgentHandler) ListAgents(c *gin.Context) {
result, err := h.agentService.ListAgents()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, result)
}
// UpdateAgentStatusRequest 更新状态请求
type UpdateAgentStatusRequest struct {
IsActive bool `json:"is_active"`
}
// UpdateAgentStatus 更新智能体状态
// @Summary 更新智能体状态
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param id path string true "Agent ID"
// @Param request body UpdateAgentStatusRequest true "状态请求"
// @Success 200 {object} gin.H
// @Router /api/agent/{id}/status [put]
func (h *AgentHandler) UpdateAgentStatus(c *gin.Context) {
agentID := c.Param("id")
if agentID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
return
}
var req UpdateAgentStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := h.agentService.UpdateAgentStatus(agentID, req.IsActive)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "status updated successfully"})
}
// DeleteAgent 删除智能体
// @Summary 删除智能体
// @Tags 智能体管理
// @Produce json
// @Param id path string true "Agent ID"
// @Success 200 {object} gin.H
// @Router /api/agent/{id} [delete]
func (h *AgentHandler) DeleteAgent(c *gin.Context) {
agentID := c.Param("id")
if agentID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
return
}
err := h.agentService.DeleteAgent(agentID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "agent deleted successfully"})
}
// UpdateAgentRequest 更新智能体请求
type UpdateAgentRequest struct {
Name string `json:"name"`
Description string `json:"description"`
Avatar string `json:"avatar"`
Skills []string `json:"skills"`
RoleDescription string `json:"role_description"`
ModelProvider string `json:"model_provider"`
ModelName string `json:"model_name"`
}
// UpdateAgent 更新智能体
// @Summary 更新智能体
// @Tags 智能体管理
// @Accept json
// @Produce json
// @Param id path string true "Agent ID"
// @Param request body UpdateAgentRequest true "更新请求"
// @Success 200 {object} gin.H
// @Router /api/agent/{id} [put]
func (h *AgentHandler) UpdateAgent(c *gin.Context) {
agentID := c.Param("id")
if agentID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "agent id is required"})
return
}
var req UpdateAgentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
err := h.agentService.UpdateAgent(agentID, req.Name, req.Description, req.Avatar, req.Skills, req.RoleDescription, req.ModelProvider, req.ModelName)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "agent updated successfully"})
}