feat: 新增 Agent CLI — Web 版 AI 运维 Agent(参考 opsxcli 移植)
后端引擎 backend/pkg/agentcli/(纯引擎库,零业务依赖,剥离 CLI/TUI 外壳): - llm: OpenAI 兼容客户端,复用 model.LLMConfig - tools: ssh_execute(复用 pkg/remoteexec)+ analyze_output + 风险评估黑名单 - safety: 三级安全模式 + 五级风险 + WebSocket 异步确认 - prompt: 五层 System Prompt - core: ReAct 16 轮循环 + 自适应检查点 + 循环检测 - evolver: 四层记忆 + 进化循环,data/agentcli/ 文件存储 集成层: - controller/agent_cli.go: WebSocket 控制器(事件流推流 + 高危确认 + 会话持久化) - model/agent_cli.go: agent_cli_sessions/messages 表 + AutoMigrate - 路由 /ws/agent-cli + /api/v1/aiops/agent-cli/sessions - seed 权限 aiops:agent-cli / aiops:llm-config 前端: - AgentCLI.tsx: 对话流 + 工具块 + 高危确认 + 四模块 tips 面板 - LLMConfig.tsx: 大模型设置独立页 - api/agentCli.ts + Sidebar/App 菜单路由 文档: - docs/agent/ 11 篇(opsxcli 同名文档适配改写 + 00-README 映射总览) 其他: - CLAUDE.md 补充 agent-cli 模块说明 - 修正 .gitignore 根级二进制规则(前导 /,避免误伤 frontend/src/components/aiops/) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
f2069fe3f0
commit
92a2247390
37 files changed
+6449
-6
No files matched your search
@@ -0,0 +1,70 @@
|
||||
// agent_cli_api.go - Agent CLI 会话历史 REST 接口 // Logan.Li
|
||||
// 供前端展示历史任务列表与回放某次任务的完整推理过程。
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"ezops/middleware"
|
||||
"ezops/model"
|
||||
)
|
||||
|
||||
// ListSessions 会话列表(按当前用户隔离) // Logan.Li
|
||||
// GET /api/v1/aiops/agent-cli/sessions?server_id=&page=&page_size=
|
||||
func (h *AgentCLIHandler) ListSessions(c *gin.Context) {
|
||||
uid := middleware.GetUserID(c)
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
q := h.db.Model(&model.AgentCLISession{}).Where("user_id = ?", uid)
|
||||
if sid := c.Query("server_id"); sid != "" {
|
||||
q = q.Where("server_id = ?", sid)
|
||||
}
|
||||
|
||||
var total int64
|
||||
q.Count(&total)
|
||||
var sessions []model.AgentCLISession
|
||||
if err := q.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&sessions).Error; err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 500, Message: "查询失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: gin.H{"list": sessions, "total": total}})
|
||||
}
|
||||
|
||||
// GetSession 会话详情(含完整推理消息,供回放) // Logan.Li
|
||||
// GET /api/v1/aiops/agent-cli/sessions/:id
|
||||
func (h *AgentCLIHandler) GetSession(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var sess model.AgentCLISession
|
||||
if err := h.db.First(&sess, id).Error; err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 404, Message: "会话不存在"})
|
||||
return
|
||||
}
|
||||
var msgs []model.AgentCLIMessage
|
||||
h.db.Where("session_id = ?", id).Order("id ASC").Find(&msgs)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: gin.H{"session": sess, "messages": msgs}})
|
||||
}
|
||||
|
||||
// DeleteSession 删除会话及其消息 // Logan.Li
|
||||
// DELETE /api/v1/aiops/agent-cli/sessions/:id
|
||||
func (h *AgentCLIHandler) DeleteSession(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.db.Where("session_id = ?", id).Delete(&model.AgentCLIMessage{}).Error; err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 500, Message: "删除消息失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
if err := h.db.Delete(&model.AgentCLISession{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{Code: 500, Message: "删除会话失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "已删除"})
|
||||
}
|
||||
Reference in new issue
Block a user