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:
attackerandClaude committed 2026-06-27 09:55:17 +08:00
1 parent f2069fe3f0
commit 92a2247390
37 files changed
+6449 -6

No files matched your search

+169
View File
@@ -0,0 +1,169 @@
// Package tools 提供 Agent CLI 引擎的工具注册中心与工具接口。 // Logan.Li
// 设计参考 opsxcli/internal/agent/tools,剥离 CLI 专属工具(local_bash 本地执行、scp)。
// Web 版聚焦远程运维:ssh_execute(目标主机执行)+ analyze_output(输出分析)。
// 风险评估逻辑(黑名单 + AssessBashRisk)置于本包,避免与 safety 包形成循环依赖。
package tools
import (
"context"
"fmt"
"sync"
"ezops/pkg/agentcli/llm"
)
// RiskLevel 工具风险等级 // Logan.Li
type RiskLevel int
const (
RiskSafe RiskLevel = iota // 只读操作(cat/grep/df/ps)
RiskLow // 低风险(mkdir/touch)
RiskMedium // 中风险(chmod/文件修改)
RiskHigh // 高风险(远程服务控制/reboot)
RiskCritical // 危险操作(rm -rf //mkfs/dd)
)
// String 返回风险等级字符串 // Logan.Li
func (r RiskLevel) String() string {
switch r {
case RiskSafe:
return "safe"
case RiskLow:
return "low"
case RiskMedium:
return "medium"
case RiskHigh:
return "high"
case RiskCritical:
return "critical"
default:
return "unknown"
}
}
// Display 返回风险等级中文显示 // Logan.Li
func (r RiskLevel) Display() string {
switch r {
case RiskSafe:
return "安全"
case RiskLow:
return "低风险"
case RiskMedium:
return "中风险"
case RiskHigh:
return "高风险"
case RiskCritical:
return "危险"
default:
return "未知"
}
}
// Result 工具执行结果 // Logan.Li
type Result struct {
Success bool `json:"success"`
Output string `json:"output"`
Error string `json:"error,omitempty"`
}
// Tool 核心工具接口(5 方法) // Logan.Li
type Tool interface {
Name() string // 工具名
Description() string // 给 LLM 看的描述
Parameters() map[string]interface{} // JSON Schema 参数定义
RiskLevel() RiskLevel // 静态风险等级
Execute(ctx context.Context, args map[string]interface{}) (*Result, error)
}
// DynamicRiskTool 动态风险评估接口 // Logan.Li
// 工具可额外实现此接口,依据运行时参数动态计算风险(如 ssh_execute 看具体命令)。
// 安全控制器 Check 时优先调用此接口。
type DynamicRiskTool interface {
Tool
RiskLevelForArgs(args map[string]interface{}) RiskLevel
}
// Registry 工具注册中心(线程安全) // Logan.Li
type Registry struct {
tools map[string]Tool
mu sync.RWMutex
}
// NewRegistry 创建空注册中心 // Logan.Li
func NewRegistry() *Registry {
return &Registry{tools: make(map[string]Tool)}
}
// Register 注册工具 // Logan.Li
func (r *Registry) Register(tool Tool) {
r.mu.Lock()
defer r.mu.Unlock()
r.tools[tool.Name()] = tool
}
// Get 按名获取工具 // Logan.Li
func (r *Registry) Get(name string) (Tool, error) {
r.mu.RLock()
defer r.mu.RUnlock()
t, ok := r.tools[name]
if !ok {
return nil, fmt.Errorf("工具未找到: %s", name)
}
return t, nil
}
// List 列出所有工具 // Logan.Li
func (r *Registry) List() []Tool {
r.mu.RLock()
defer r.mu.RUnlock()
list := make([]Tool, 0, len(r.tools))
for _, t := range r.tools {
list = append(list, t)
}
return list
}
// ToLLMTools 转换为 LLM function calling 格式 // Logan.Li
func (r *Registry) ToLLMTools() []llm.Tool {
r.mu.RLock()
defer r.mu.RUnlock()
out := make([]llm.Tool, 0, len(r.tools))
for _, t := range r.tools {
out = append(out, llm.Tool{
Type: "function",
Function: llm.ToolFunction{
Name: t.Name(),
Description: t.Description(),
Parameters: t.Parameters(),
},
})
}
return out
}
// ---- 参数解析辅助 ---- // Logan.Li
// StringParam 取字符串参数 // Logan.Li
func StringParam(args map[string]interface{}, key string) string {
if v, ok := args[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// IntParam 取整型参数(兼容 int/float64/int64) // Logan.Li
func IntParam(args map[string]interface{}, key string) int {
if v, ok := args[key]; ok {
switch val := v.(type) {
case int:
return val
case float64:
return int(val)
case int64:
return int(val)
}
}
return 0
}