Files
ai-gateway-go/internal/workbench/agentpolicy.go
T
LLMGuardX Dev d534865b33 0.11.4: 旗舰版第五轮完善(智能体节点任务下发/个人智能体安全策略)
- 节点任务:管理端向节点池下发(Prompt/HTTP/MCP/Skill/数字员工/自定义),
  指定节点或池路由,认领 SKIP LOCKED + 15 分钟租约,认领令牌防重放上报,
  失败 30s×次数退避重入队,达上限 failed,支持取消/重试,完成与失败站内信。
- 个人智能体安全策略:auto_approve_tools 跳过个人调用审批门;
  rate_limit_multiplier 按 (tool,user) 独立窗口放宽个人限流(全局额度不受影响)。
- 修复存量缺陷:/v1/agent/nodes/ 未挂 publicMux,节点心跳/认领端点在部署
  拓扑下不可达。
- 迁移 000046;任务全链路集成测试连真实库通过,HTTP 端到端验证
  (下发→认领→伪造令牌拒绝→上报→succeeded,列表不泄露认领令牌);
  25 包测试通过,前后端构建通过。
2026-08-13 14:30:37 +08:00

123 lines
4.0 KiB
Go

package workbench
import (
"context"
"encoding/json"
"errors"
"net/http"
"strings"
"aigateway.local/core/internal/identity"
"aigateway.local/core/internal/platform/apiresponse"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
// AgentPolicy 是个人智能体安全策略。
type AgentPolicy struct {
AutoApproveTools bool `json:"auto_approve_tools"`
RateLimitMultiplier int `json:"rate_limit_multiplier"`
}
// AgentPolicyService 管理个人智能体安全策略。
type AgentPolicyService struct {
pool *pgxpool.Pool
}
func NewAgentPolicyService(pool *pgxpool.Pool) *AgentPolicyService {
return &AgentPolicyService{pool: pool}
}
// Get 返回用户的策略(未配置时返回默认值)。
func (s *AgentPolicyService) Get(ctx context.Context, portalUserID string) (AgentPolicy, error) {
var policy AgentPolicy
if s == nil || s.pool == nil {
return policy, errors.New("安全策略服务不可用")
}
err := s.pool.QueryRow(ctx, `SELECT auto_approve_tools,rate_limit_multiplier FROM gateway.portal_agent_policies WHERE portal_user_id=$1`, portalUserID).Scan(&policy.AutoApproveTools, &policy.RateLimitMultiplier)
if errors.Is(err, pgx.ErrNoRows) {
policy.RateLimitMultiplier = 1
return policy, nil
}
if err != nil {
return policy, err
}
return policy, nil
}
// Set 更新用户的策略。
func (s *AgentPolicyService) Set(ctx context.Context, portalUserID string, policy AgentPolicy) error {
if s == nil || s.pool == nil {
return errors.New("安全策略服务不可用")
}
if policy.RateLimitMultiplier < 1 || policy.RateLimitMultiplier > 100 {
return errors.New("限流倍数必须在 1-100 之间")
}
_, err := s.pool.Exec(ctx, `INSERT INTO gateway.portal_agent_policies(portal_user_id,auto_approve_tools,rate_limit_multiplier) VALUES($1,$2,$3)
ON CONFLICT(portal_user_id) DO UPDATE SET auto_approve_tools=$2,rate_limit_multiplier=$3,updated_at=clock_timestamp()`,
portalUserID, policy.AutoApproveTools, policy.RateLimitMultiplier)
return err
}
// AgentPolicyHTTPHandler 门户个人智能体安全策略。
type AgentPolicyHTTPHandler struct {
service *AgentPolicyService
identity *identity.Service
mux *http.ServeMux
}
func NewAgentPolicyHTTPHandler(service *AgentPolicyService, identityService *identity.Service) *AgentPolicyHTTPHandler {
h := &AgentPolicyHTTPHandler{service: service, identity: identityService, mux: http.NewServeMux()}
h.mux.HandleFunc("GET /api/v1/portal/agent-policy", h.get)
h.mux.HandleFunc("PUT /api/v1/portal/agent-policy", h.put)
return h
}
func (h *AgentPolicyHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mux.ServeHTTP(w, r)
}
func (h *AgentPolicyHTTPHandler) account(w http.ResponseWriter, r *http.Request) (identity.Account, bool) {
account, err := h.identity.Authenticate(r.Context(), identity.KindPortal, r.Header.Get("Authorization"))
if err != nil {
apiresponse.Error(w, http.StatusUnauthorized, "登录状态无效或已过期")
return identity.Account{}, false
}
return account, true
}
func (h *AgentPolicyHTTPHandler) get(w http.ResponseWriter, r *http.Request) {
a, ok := h.account(w, r)
if !ok {
return
}
policy, err := h.service.Get(r.Context(), a.ID)
if err != nil {
apiresponse.Error(w, http.StatusServiceUnavailable, "安全策略查询失败")
return
}
apiresponse.OK(w, policy)
}
func (h *AgentPolicyHTTPHandler) put(w http.ResponseWriter, r *http.Request) {
a, ok := h.account(w, r)
if !ok {
return
}
var input struct {
AutoApproveTools bool `json:"auto_approve_tools"`
RateLimitMultiplier int `json:"rate_limit_multiplier"`
}
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20))
decoder.DisallowUnknownFields()
if decoder.Decode(&input) != nil {
apiresponse.Error(w, http.StatusBadRequest, "请求格式无效")
return
}
if err := h.service.Set(r.Context(), a.ID, AgentPolicy{AutoApproveTools: input.AutoApproveTools, RateLimitMultiplier: input.RateLimitMultiplier}); err != nil {
apiresponse.Error(w, http.StatusBadRequest, strings.TrimSpace(err.Error()))
return
}
apiresponse.OK(w, map[string]bool{"saved": true})
}