【全域智能营销实战】9、Harness 约束层实现:为 AI Agent 构建安全可控的执行环境

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

在这里插入图片描述

Harness 约束层实现:为 AI Agent 构建安全可控的执行环境

三层防御体系 + 动态规则引擎 + Docker 沙箱,让 Agent 在安全边界内自由行动

📑 目录


一、引言:AI Agent 的安全悖论

AI Agent 越强大,带来的安全风险就越大。

一个能够读写文件、执行命令、调用 API、访问数据库的 Agent,如果缺少有效的约束机制,就像一台没有刹车的跑车——速度越快,风险越高。

现实中的 Agent 安全事故

  • Agent 在执行“清理临时文件”任务时,误删了生产环境的关键配置文件
  • Agent 在调试代码时,执行了 rm -rf / 命令(虽然被拦截了,但暴露了风险)
  • Agent 在读取数据库时,返回了包含用户敏感信息的查询结果
  • Agent 在调用第三方 API 时,因循环调用导致费用失控

这些问题不是模型能力不足,而是缺少一层有效的约束机制

Harness 的约束层正是为此而生。它像一道安全过滤器,位于“决策引擎”与“工具执行”之间,确保 Agent 的每一个行动都在安全边界内。

有约束层

没有约束层

直接执行

危险操作

安全过滤

安全执行

Agent

操作系统
数据库
API

💥 数据损坏
💰 费用失控
🔓 权限泄露

🛡️ Harness 约束层

✅ 可控执行
📋 完整审计
🔒 权限可控


二、Harness 在 Uni-MDP 中的落地位置

在 Uni-MDP 的整体架构中,Harness 约束层处于算法层(决策引擎)中台层(工具执行) 之间:

Uni-MDP 架构

交互层
用户输入

应用层
策略编排

算法层
Hermes 决策引擎

🛡️ Harness 约束层
认知约束 · 权限约束 · 流程约束

中台层
工具执行层
内容/活动/权益/推荐/优惠券

数据层
数据读写

算力层
计算执行

约束层在决策链路中的位置

通过

拒绝

Harness 约束层

认知约束
行为准则校验

权限约束
工具白名单/黑名单

流程约束
执行顺序/合规检查

用户请求

Hermes Agent
生成决策

执行工具

🚫 拒绝执行
记录审计日志

返回结果

三、三层约束体系设计

3.1 认知约束

定位:通过 System Prompt 注入行为准则,让 Agent 在“思考”阶段就知道什么该做、什么不该做。

认知约束是最前置的约束层,在 Agent 生成决策之前就已经生效。

认知约束(System Prompt 注入)

角色定义
你是谁,你的职责是什么

行为准则
应该做什么,不应该做什么

安全边界
哪些操作需要额外确认

优先级规则
遇到冲突时如何决策

Agent 推理

决策是否
违反认知约束?

⚠️ 注入提醒
重新决策

进入下一层

在 Uni-MDP 中的实现形式

# AGENTS.md - 认知约束配置文件
# 定义 Agent 的行为准则

## 角色定义
你是一个营销决策助手,职责包括:
- 分析用户行为数据,提供营销建议
- 执行营销活动配置
- 生成用户分群和标签

## 行为准则
1. 在执行任何数据写入操作前,必须先确认影响范围
2. 在涉及用户隐私数据时,必须脱敏处理
3. 预算相关的操作必须经过二次确认
4. 批量操作必须限制在合理范围内(单次不超过 10000 条)

## 安全边界
- 禁止删除用户数据
- 禁止修改系统配置
- 禁止执行未授权的数据库操作
- 禁止调用未列入白名单的 API

3.2 权限约束

定位:工具调用前的权限校验——Agent 能调用哪些工具、能访问哪些资源、能操作哪些数据。

通过

拒绝

权限约束

工具白名单
只能调用授权的工具

操作黑名单
禁止危险操作

最小权限原则
只给完成任务所需的最小权限

数据范围限制
只能访问授权范围内的数据

工具调用请求

执行工具

📋 记录审计日志

权限模型的层级结构

属于

拥有

定义

规则类型

工具规则
允许/禁止调用特定工具

数据规则
允许/禁止访问特定数据范围

操作规则
允许/禁止特定操作类型

资源规则
允许/禁止访问特定资源

用户
User

角色
Role

策略
Policy

3.3 流程约束

定位:标准化执行流程,确保 Agent 的行动遵循“规划→执行→验证→反馈”的标准路径。

流程约束

计划调整

📋 规划
必须先生成执行计划

⚡ 执行
按计划顺序执行

✅ 验证
执行后必须验证结果

🔄 反馈
记录执行结果,用于优化

流程约束的校验点

校验点检查内容拒绝条件
计划完整性是否包含所有必要步骤缺少关键步骤
步骤依赖步骤之间的依赖关系是否正确存在循环依赖
资源准备执行所需的资源是否就绪缺少必要权限或资源
影响评估操作的影响范围是否可控影响范围超出授权
结果验证执行结果是否符合预期结果异常或未达预期

四、Java 实现方案

4.1 规则引擎选型与实现

在约束层中,规则引擎负责执行权限校验和流程约束。我们对比了几种主流方案:

规则引擎优势劣势适用场景
Drools功能强大、支持复杂规则、DRL 语言灵活重量级、学习曲线陡峭复杂业务规则场景
Aviator轻量级、高性能、表达式简洁不支持复杂规则编排简单表达式计算
EasyRules简单易用、注解驱动功能相对有限规则数量较少的场景
自研表达式完全可控开发成本高特殊定制需求

在 Uni-MDP 中,我们采用 Aviator + 规则模板 的组合方案:

  • Aviator:负责执行具体的约束判断表达式
  • 规则模板:将约束规则抽象为可配置的模板,业务人员可动态调整
package com.unimdp.harness.rule;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
public class RuleEngine {
    
    // 规则缓存:避免重复编译
    private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>();
    
    /**
     * 执行规则判断
     */
    public RuleResult evaluate(String ruleId, Map<String, Object> context) {
        try {
            // 1. 获取规则定义
            RuleDefinition rule = getRuleDefinition(ruleId);
            if (rule == null) {
                return RuleResult.pass("Rule not found, default pass");
            }
            
            // 2. 编译或获取缓存的表达式
            Expression expression = expressionCache.computeIfAbsent(
                rule.getExpression(),
                expr -> AviatorEvaluator.compile(expr, true)
            );
            
            // 3. 执行表达式
            Object result = expression.execute(context);
            
            // 4. 解析结果
            if (result instanceof Boolean) {
                boolean passed = (Boolean) result;
                return passed 
                    ? RuleResult.pass("Rule passed: " + rule.getName())
                    : RuleResult.fail(rule.getFailMessage(), rule.getFailAction());
            }
            
            return RuleResult.pass("Rule result is not boolean, default pass");
            
        } catch (Exception e) {
            log.error("Rule evaluation failed: {}", ruleId, e);
            // 默认拒绝(安全优先)
            return RuleResult.fail("Rule evaluation failed", FailAction.BLOCK);
        }
    }
}

规则定义的数据结构

@Data
@Builder
public class RuleDefinition {
    private String id;
    private String name;
    private String description;
    private String expression;      // Aviator 表达式
    private String failMessage;
    private String failAction;      // BLOCK / WARN / LOG_ONLY
    private Integer priority;
    private Boolean enabled;
    private List<String> tags;
}

// 规则配置示例
// 规则:禁止删除操作
// expression: toolName == 'delete' ? false : true
// failMessage: 删除操作已被禁止,如需执行请联系管理员
// failAction: BLOCK

4.2 权限校验拦截器

package com.unimdp.harness.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {
    
    @Autowired
    private RuleEngine ruleEngine;
    
    @Autowired
    private AuditService auditService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        
        // 1. 提取调用上下文
        ToolCallContext context = extractContext(request);
        
        // 2. 执行权限校验
        String permissionRule = getPermissionRule(context.getToolName());
        RuleResult result = ruleEngine.evaluate(permissionRule, context.toMap());
        
        // 3. 记录审计日志
        auditService.logPermissionCheck(context, result);
        
        // 4. 处理校验结果
        if (!result.isPassed()) {
            log.warn("Permission denied: tool={}, user={}, reason={}", 
                context.getToolName(), 
                context.getUserId(), 
                result.getMessage()
            );
            
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setContentType("application/json");
            response.getWriter().write(buildErrorResponse(result));
            return false;
        }
        
        return true;
    }
    
    private ToolCallContext extractContext(HttpServletRequest request) {
        // 从请求中提取调用上下文
        return ToolCallContext.builder()
            .userId(request.getHeader("X-User-Id"))
            .sessionId(request.getHeader("X-Session-Id"))
            .toolName(request.getHeader("X-Tool-Name"))
            .params(extractParams(request))
            .build();
    }
}

4.3 结果验证器链

package com.unimdp.harness.validator;

@Component
@Slf4j
public class ResultValidatorChain {
    
    private final List<ResultValidator> validators;
    
    public ResultValidatorChain() {
        this.validators = new ArrayList<>();
        this.validators.add(new SchemaValidator());      // 格式验证
        this.validators.add(new RangeValidator());       // 范围验证
        this.validators.add(new SecurityValidator());    // 安全验证
        this.validators.add(new BusinessValidator());    // 业务验证
    }
    
    /**
     * 执行验证链
     */
    public ValidationResult validate(ToolCallContext context, Object result) {
        for (ResultValidator validator : validators) {
            ValidationResult vr = validator.validate(context, result);
            if (!vr.isPassed()) {
                log.warn("Validation failed: validator={}, reason={}", 
                    validator.getName(), vr.getMessage());
                return vr;
            }
        }
        return ValidationResult.pass();
    }
}

// 验证器接口
public interface ResultValidator {
    String getName();
    ValidationResult validate(ToolCallContext context, Object result);
}

// 安全验证器示例
@Component
@Slf4j
public class SecurityValidator implements ResultValidator {
    
    @Override
    public String getName() {
        return "SecurityValidator";
    }
    
    @Override
    public ValidationResult validate(ToolCallContext context, Object result) {
        // 检查返回结果是否包含敏感信息
        String jsonResult = toJson(result);
        
        if (containsSensitiveData(jsonResult)) {
            return ValidationResult.fail(
                "Return result contains sensitive data (email/phone/idcard)"
            );
        }
        
        return ValidationResult.pass();
    }
    
    private boolean containsSensitiveData(String json) {
        // 检测手机号、身份证、邮箱等敏感信息
        return Pattern.matches(".*\\d{11}.*", json) ||
               Pattern.matches(".*\\d{17}[\\dXx].*", json) ||
               Pattern.matches(".*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}.*", json);
    }
}

五、沙箱隔离

5.1 Docker 容器化执行

资源限制

CPU: 2 核上限

内存: 2GB 上限

磁盘: 10GB 上限

网络: 白名单 IP

Agent 执行容器

隔离的文件系统

受限的网络访问

CPU/内存限制

只读系统目录

宿主机

Docker Engine

容器编排

资源监控

5.2 Java 实现

package com.unimdp.harness.sandbox;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.ResourceLimits;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class DockerSandbox {
    
    @Autowired
    private DockerClient dockerClient;
    
    public SandboxResult execute(ToolCallContext context, String command) {
        // 1. 创建容器
        CreateContainerResponse container = dockerClient.createContainerCmd("unimdp-agent-sandbox:latest")
            .withName("sandbox-" + System.currentTimeMillis())
            .withCmd("/bin/sh", "-c", command)
            .withHostConfig(HostConfig.newHostConfig()
                .withMemory(2 * 1024 * 1024 * 1024L)  // 2GB
                .withMemorySwap(0L)                    // 禁用 swap
                .withCpuCount(2L)                     // CPU 上限 2 核
                .withReadonlyRootfs(true)             // 只读根文件系统
                .withNetworkMode("none")              // 默认无网络
                .withBinds(new Bind("/tmp/sandbox", new Volume("/workspace")))
            )
            .exec();
        
        try {
            // 2. 启动容器
            dockerClient.startContainerCmd(container.getId()).exec();
            
            // 3. 等待执行完成
            WaitResponse waitResponse = dockerClient.waitContainerCmd(container.getId())
                .start()
                .awaitCompletion(30, TimeUnit.SECONDS);
            
            // 4. 获取日志
            String log = dockerClient.logContainerCmd(container.getId())
                .withStdOut(true)
                .withStdErr(true)
                .exec()
                .readFully();
            
            // 5. 返回结果
            return SandboxResult.builder()
                .exitCode(waitResponse.getStatusCode())
                .output(log)
                .build();
                
        } catch (Exception e) {
            log.error("Sandbox execution failed", e);
            return SandboxResult.error(e.getMessage());
        } finally {
            // 6. 清理容器
            dockerClient.removeContainerCmd(container.getId())
                .withForce(true)
                .exec();
        }
    }
}

5.3 资源限制配置

# sandbox-config.yaml
sandbox:
  docker:
    image: unimdp-agent-sandbox:latest
    memory-limit: 2GB
    cpu-limit: 2
    disk-limit: 10GB
    timeout-seconds: 30
    network:
      mode: none  # none / bridge / host
      allowed-hosts:
        - api.openai.com
        - api.anthropic.com
    read-only-mounts:
      - /etc
      - /usr
      - /bin
    writable-mounts:
      - /workspace
      - /tmp
    banned-commands:
      - rm -rf /
      - dd if=
      - mkfs
      - chmod 777
      - sudo

六、审计日志

6.1 审计日志数据模型

CREATE TABLE audit_logs (
    log_id           BIGINT PRIMARY KEY AUTO_INCREMENT,
    
    -- 审计主体
    user_id          VARCHAR(64) NOT NULL,
    session_id       VARCHAR(64) NOT NULL,
    agent_id         VARCHAR(64),
    
    -- 操作信息
    action_type      VARCHAR(32) NOT NULL,    -- TOOL_CALL / SYSTEM_CMD / DATA_ACCESS
    tool_name        VARCHAR(64),
    action_params    JSON,
    action_result    JSON,
    
    -- 决策信息
    decision_path    JSON,                    -- 完整决策路径(含推理链)
    constraint_type  VARCHAR(32),             -- 认知/权限/流程
    constraint_check VARCHAR(128),            -- 具体约束规则
    
    -- 执行结果
    status           VARCHAR(16) NOT NULL,    -- SUCCESS / FAILED / BLOCKED
    error_message    TEXT,
    
    -- 技术元数据
    ip_address       VARCHAR(45),
    user_agent       TEXT,
    request_id       VARCHAR(64),
    
    -- 时间
    executed_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    
    -- 索引
    INDEX idx_user_id (user_id),
    INDEX idx_session_id (session_id),
    INDEX idx_action_type (action_type),
    INDEX idx_status (status),
    INDEX idx_executed_at (executed_at)
);

6.2 审计日志服务

package com.unimdp.harness.audit;

@Service
@Slf4j
public class AuditService {
    
    @Autowired
    private AuditLogRepository auditLogRepository;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    /**
     * 记录工具调用审计日志
     */
    @Async
    public void logToolCall(ToolCallContext context, ToolCallResult result, 
                           List<ConstraintCheck> checks) {
        AuditLog log = AuditLog.builder()
            .userId(context.getUserId())
            .sessionId(context.getSessionId())
            .actionType("TOOL_CALL")
            .toolName(context.getToolName())
            .actionParams(toJson(context.getParams()))
            .actionResult(toJson(result))
            .decisionPath(toJson(context.getDecisionPath()))
            .constraintType(determineConstraintType(checks))
            .constraintCheck(toJson(checks))
            .status(result.getStatus())
            .errorMessage(result.getErrorMessage())
            .requestId(context.getRequestId())
            .executedAt(Instant.now())
            .build();
        
        auditLogRepository.save(log);
    }
    
    /**
     * 记录约束拦截审计日志
     */
    public void logConstraintBlock(ToolCallContext context, 
                                  RuleResult ruleResult,
                                  String constraintType) {
        AuditLog log = AuditLog.builder()
            .userId(context.getUserId())
            .sessionId(context.getSessionId())
            .actionType("CONSTRAINT_BLOCK")
            .toolName(context.getToolName())
            .actionParams(toJson(context.getParams()))
            .constraintType(constraintType)
            .constraintCheck(ruleResult.getMessage())
            .status("BLOCKED")
            .requestId(context.getRequestId())
            .executedAt(Instant.now())
            .build();
        
        auditLogRepository.save(log);
        log.warn("🔴 CONSTRAINT BLOCK: user={}, tool={}, reason={}", 
            context.getUserId(), context.getToolName(), ruleResult.getMessage());
    }
}

七、配置化约束模板

7.1 约束模板设计

# constraint-templates.yaml
# 约束模板配置——业务人员可通过 YAML 配置,无需发版

templates:
  # 工具白名单模板
  - id: tool-whitelist
    name: 工具白名单
    description: 定义 Agent 可以调用的工具列表
    type: PERMISSION
    parameters:
      - name: allowed_tools
        type: array
        required: true
        description: 允许的工具名称列表
    expression: |
      allowed_tools = params.allowed_tools;
      return allowed_tools.contains(toolName);
    failAction: BLOCK
    
  # 数据范围模板
  - id: data-scope
    name: 数据范围限制
    description: 限制 Agent 可访问的数据范围
    type: PERMISSION
    parameters:
      - name: allowed_tables
        type: array
        required: true
      - name: max_rows
        type: integer
        default: 10000
    expression: |
      allowed_tables = params.allowed_tables;
      max_rows = params.max_rows ? params.max_rows : 10000;
      return allowed_tables.contains(tableName) && rowCount <= max_rows;
    failAction: BLOCK
    
  # 频次限制模板
  - id: rate-limit
    name: 调用频次限制
    description: 限制工具在单位时间内的调用次数
    type: PROCESS
    parameters:
      - name: max_calls
        type: integer
        required: true
      - name: time_window
        type: integer
        default: 60
    expression: |
      max_calls = params.max_calls;
      time_window = params.time_window ? params.time_window : 60;
      calls_in_window = getCallsInWindow(toolName, time_window);
      return calls_in_window < max_calls;
    failAction: WARN

7.2 约束模板加载器

package com.unimdp.harness.template;

@Component
@Slf4j
public class ConstraintTemplateLoader {
    
    private final Map<String, ConstraintTemplate> templates = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void loadTemplates() {
        try {
            // 从 YAML 文件加载模板
            Resource resource = new ClassPathResource("constraint-templates.yaml");
            Yaml yaml = new Yaml();
            Map<String, Object> config = yaml.load(resource.getInputStream());
            
            List<Map<String, Object>> templateList = (List<Map<String, Object>>) config.get("templates");
            for (Map<String, Object> tmpl : templateList) {
                ConstraintTemplate template = parseTemplate(tmpl);
                templates.put(template.getId(), template);
                log.info("Loaded constraint template: {}", template.getName());
            }
        } catch (Exception e) {
            log.error("Failed to load constraint templates", e);
        }
    }
    
    /**
     * 热加载模板(支持运行时更新)
     */
    public void reloadTemplates() {
        templates.clear();
        loadTemplates();
        log.info("Constraint templates reloaded");
    }
    
    /**
     * 根据模板创建约束规则
     */
    public ConstraintRule createRule(String templateId, Map<String, Object> params) {
        ConstraintTemplate template = templates.get(templateId);
        if (template == null) {
            throw new IllegalArgumentException("Template not found: " + templateId);
        }
        
        return ConstraintRule.builder()
            .id(UUID.randomUUID().toString())
            .templateId(templateId)
            .name(template.getName())
            .expression(template.getExpression())
            .params(params)
            .failAction(template.getFailAction())
            .build();
    }
}

八、总结

本文系统拆解了 Harness 约束层的工程实现,核心要点如下:

8.1 三层防御体系

层级作用实现方式
认知约束在决策前注入行为准则System Prompt + AGENTS.md
权限约束在工具调用前校验权限规则引擎 + 拦截器
流程约束在执行过程中规范流程验证器链 + 状态机

8.2 动态规则引擎

使用 Aviator 表达式引擎实现约束规则的动态配置和热加载,业务人员可通过 YAML 模板配置约束规则,无需发版。

8.3 Docker 沙箱隔离

通过 Docker 容器化执行 Agent 的操作,实现文件系统隔离、网络隔离和资源限制。

8.4 完整的审计日志

全量记录每一次工具调用和约束拦截,满足合规审计要求,并为后续的 Harness 优化提供数据支撑。

下一篇预告:我们将深入三大引擎的协同工作流,完整展示从用户消息到智能决策的端到端链路。

敬请期待!🚀


📌 本文收录于专栏从 OpenClaw 到 Hermes:自改进 Agent 完全指南

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无心水

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值