- 新增 Go 语言后端服务(server/),包含用户认证、Agent管理、数据库连接等API - 新增 Python Agent 服务(agent/),实现Agent核心逻辑和工具集 - 前端从原生HTML迁移到Vue.js框架(web/src/) - 添加 Docker Compose 支持(docker-compose.yml) - 添加项目架构文档(docs/ARCHITECTURE.md) - 添加环境变量示例(.env.example)和本地启动脚本(start-local.ps1) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
147 lines
3.3 KiB
Go
147 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"x-agents/server/internal/model"
|
|
"x-agents/server/internal/repository"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type ChatService struct {
|
|
pythonURL string
|
|
agentRepo *repository.AgentRepository
|
|
}
|
|
|
|
func NewChatService(pythonURL string, agentRepo *repository.AgentRepository) *ChatService {
|
|
return &ChatService{
|
|
pythonURL: pythonURL,
|
|
agentRepo: agentRepo,
|
|
}
|
|
}
|
|
|
|
type ChatRequest struct {
|
|
AgentID string `json:"agent_id"`
|
|
Message string `json:"message"`
|
|
SessionID string `json:"session_id"`
|
|
Context map[string]interface{} `json:"context"`
|
|
}
|
|
|
|
type ChatResponse struct {
|
|
Reply string `json:"reply"`
|
|
SessionID string `json:"session_id"`
|
|
ToolsUsed []string `json:"tools_used"`
|
|
Metadata map[string]interface{} `json:"metadata"`
|
|
}
|
|
|
|
// Chat 处理聊天请求
|
|
func (s *ChatService) Chat(ctx context.Context, userID string, req model.AgentRequest) (*model.AgentResponse, error) {
|
|
// 1. 检查 Agent 是否存在
|
|
agent, err := s.agentRepo.FindByID(req.AgentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("agent not found: %w", err)
|
|
}
|
|
|
|
// 2. 检查用户权限
|
|
if !agent.IsActive {
|
|
return nil, fmt.Errorf("agent is not active")
|
|
}
|
|
|
|
// 3. 生成会话ID
|
|
sessionID := req.SessionID
|
|
if sessionID == "" {
|
|
sessionID = uuid.New().String()
|
|
}
|
|
|
|
// 4. 调用 Python 服务
|
|
pythonReq := ChatRequest{
|
|
AgentID: req.AgentID,
|
|
Message: req.Message,
|
|
SessionID: sessionID,
|
|
Context: req.Context,
|
|
}
|
|
|
|
pythonResp, err := s.callPythonChat(ctx, pythonReq)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to call python service: %w", err)
|
|
}
|
|
|
|
return &model.AgentResponse{
|
|
Reply: pythonResp.Reply,
|
|
SessionID: pythonResp.SessionID,
|
|
ToolsUsed: pythonResp.ToolsUsed,
|
|
Metadata: pythonResp.Metadata,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ChatService) callPythonChat(ctx context.Context, req ChatRequest) (*ChatResponse, error) {
|
|
jsonData, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
httpReq, err := http.NewRequestWithContext(
|
|
ctx,
|
|
"POST",
|
|
s.pythonURL+"/agent/chat",
|
|
bytes.NewBuffer(jsonData),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{
|
|
Timeout: 120 * time.Second, // Agent 可能需要较长时间
|
|
}
|
|
|
|
resp, err := client.Do(httpReq)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("python service returned status: %d", resp.StatusCode)
|
|
}
|
|
|
|
var chatResp ChatResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&chatResp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &chatResp, nil
|
|
}
|
|
|
|
// ListAgents 获取用户可用的 Agent 列表
|
|
func (s *ChatService) ListAgents(userID string) ([]model.Agent, error) {
|
|
return s.agentRepo.FindByOwnerID(userID)
|
|
}
|
|
|
|
// CreateAgent 创建新的 Agent
|
|
func (s *ChatService) CreateAgent(userID string, name, description string) (*model.Agent, error) {
|
|
agent := &model.Agent{
|
|
ID: uuid.New().String(),
|
|
Name: name,
|
|
Description: description,
|
|
OwnerID: userID,
|
|
SecurityLevel: model.SecurityLevelSafe,
|
|
IsActive: true,
|
|
Timeout: 60,
|
|
MemoryLimit: 134217728, // 128MB
|
|
}
|
|
|
|
if err := s.agentRepo.Create(agent); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return agent, nil
|
|
}
|