AWS开源Dogwood:用时序策略语言治理AI代理的工具调用链

引言:AI代理治理的困境

当AI代理开始在企业的生产环境中执行工具调用——查询数据库、发送邮件、修改配置文件——一个根本性的问题浮出水面:如何确保代理的行为序列符合安全策略?2026年8月16日,AWS给出了自己的答案——开源了Dogwood策略语言,这是对Cedar策略语言的时序扩展,允许规则回溯审视代理已经执行的工具调用序列。

这一发布的意义远超一次普通的开源项目发布。它标志着AI代理治理从"单次调用检查"走向"调用序列治理"的范式转变,填补了AI代理安全基础设施中一个关键空白。

Cedar的局限与Dogwood的突破

Cedar:单次请求的世界

Cedar是AWS在2023年开源的策略语言,已被捐赠给CNCF作为沙箱项目。它的核心设计原则是确定性:同一个请求在任何时候都会得到相同的决策结果,不受历史上下文的影响。这种设计带来了两大优势——审计能力和自动化推理能力,使得策略可以被形式化验证。

但Cedar的代价同样明显:它只能描述单个操作的策略。一个操作序列——比如"在执行了某个操作之后必须获得审批",或者"累计调用次数不得超过限制"——超出了Cedar的表达能力。

具体来说,Cedar的条件语句(when子句)只能评估当前请求的属性,无法回溯代理之前的行为。这意味着以下常见的治理需求无法用Cedar表达:

  • "代理在访问敏感数据后的24小时内不得调用外部服务"

  • "代理每小时调用外部API不得超过10次"

  • "代理在执行高风险操作前必须获得人类审批"

  • "代理每日累计转账金额不得超过5000美元"
  • Dogwood:引入时序维度

    Dogwood的核心创新是在Cedar的when条件之外引入了when temporal条件。时序条件可以读取代理的事件历史——即工具调用请求及其结果,包括输入参数和请求主体。动作模式来自代理的MCP工具清单,每个工具对应一个动作,Dogwood直接生成。

    在底层实现上,时序条件被转换为Cedar上下文中的一个字段,由解释器在做出决策前从事件历史中填充。这意味着Dogwood并非完全独立于Cedar,而是在Cedar的基础上增加了一层时序评估层。

    python
    # Dogwood策略示例:速率限制
    # 当代理在1小时内向外部API发起超过10次请求时拒绝
    permit(
        principal == agent::"payment_agent",
        action == tool::"call_external_api",
        resource == external_service::"stripe_api"
    ) when {
        count_within(duration("PT1H"), action) <= 10
    };
    
    # Dogwood策略示例:审批门控
    # 在执行高敏感操作前必须获得人类审批
    permit(
        principal == agent::"data_agent",
        action == tool::"modify_production_config",
        resource == config::"prod_database"
    ) when temporal {
        formerly(duration("PT24H"), action == tool::"request_human_approval")
    };
    
    # Dogwood策略示例:数据隔离
    # 访问机密数据后24小时内禁止外部调用
    forbid(
        principal == agent::"research_agent",
        action == tool::"call_external_service",
        resource == external_service::"webhook"
    ) when temporal {
        formerly(duration("PT24H"), action == tool::"access_confidential_data")
    };

    四大时序操作符

    Dogwood提供了四个核心操作符,都是基于Metric First-Order Temporal Logic(度量一阶时序逻辑)子集的宏定义,而非语言原语:

    | 操作符 | 功能 | 典型场景 |
    |--------|------|----------|
    | formerly | 判断某事件是否在时间窗口内发生过 | 审批门控:执行高风险操作前确认已获得审批 |
    | count_within | 统计时间窗口内事件发生次数 | 速率限制:限制API调用频率 |
    | count_distinct_within | 统计时间窗口内不同值的数量 | 去重限制:限制调用不同端点的数量 |
    | sum_within | 计算时间窗口内数值的累计总和 | 配额管理:限制累计交易金额 |

    此外,bind操作符用于命名聚合结果,使当前请求可以与历史聚合进行比较。例如,可以先计算过去24小时的转账总额,绑定到一个变量名,然后与当前请求的金额相加后与限额比较。

    并发陷阱:分布式系统的新老问题

    一个经典的并发漏洞

    AWS在发布文档中给出了一个极具教育意义的例子,展示了一个常见的正确性陷阱。考虑一个银行转账代理的场景,设定每笔转账限额5000美元:

    python
    # 场景:三个并发的$2000转账请求
    # 时间线:
    # T0: 请求1 ($2000) 到达
    # T1: 请求2 ($2000) 到达  
    # T2: 请求3 ($2000) 到达
    # T3: 响应1 返回
    # T4: 响应2 返回
    # T5: 响应3 返回
    
    # --- 错误策略:基于响应事件计算累计金额 ---
    # 在T0时,策略查看响应历史:无已完成转账,累计$0
    # 在T1时,策略查看响应历史:仍无已完成转账,累计$0
    # 在T2时,策略查看响应历史:仍无已完成转账,累计$0
    # 结果:三笔$2000全部通过,累计$6000超出$5000限额!
    
    permit(
        principal == agent::"transfer_agent",
        action == tool::"execute_transfer",
        resource == account::"checking"
    ) when temporal {
        sum_within(duration("PT24H"), response.amount) < 5000
    };
    
    # --- 正确策略:基于请求事件计算累计金额 ---
    # 在T0时,策略查看请求历史:无请求,累计$0,允许
    # 在T1时,策略查看请求历史:1个请求$2000,累计$2000,允许
    # 在T2时,策略查看请求历史:2个请求共$4000,累计$4000+$2000=$6000>$5000,拒绝!
    
    permit(
        principal == agent::"transfer_agent",
        action == tool::"execute_transfer",
        resource == account::"checking"
    ) when temporal {
        sum_within(duration("PT24H"), request.amount) < 5000
    };

    两个策略唯一的区别是对response还是request求和,但结果是安全与不安全的分水岭。一个词之差,就是安全漏洞和安全防线的区别。

    这种异步性问题在分布式系统中早已被认知——但在AI代理治理领域,它是全新的挑战。代理发出并行的工具调用,在多代理设置中交错使得问题更加复杂。一个在顺序执行下正确的策略,在并发场景下可能完全失效。

    多代理场景下的交错复杂度

    在多代理设置中,问题进一步复杂化。多个代理同时发起工具调用,调用顺序的交错使得策略评估更加困难。例如:

  • 代理A发起转账$2000

  • 代理B同时发起转账$2000

  • 两个请求在任何一笔结算前到达策略引擎

  • 如果策略基于响应事件计算累计,它看到的累计为$0

  • 两个请求都通过,但累计$4000可能已超限
  • 这意味着策略设计者必须将并发语义纳入考量,而不仅仅是顺序逻辑。这与分布式系统中的并发控制问题本质相同——只是出现在了一个全新的应用领域。

    与MCP规范的协同

    Dogwood的发布时间点恰逢MCP 2026-07-28规范更新,两者解决了同一问题的相邻部分:

  • MCP规范更新:通过必需的method和tool-name头部,让HTTP基础设施(网关、负载均衡器)能够识别代理正在调用哪个工具。这是"可见性"的解决方案

  • Dogwood策略语言:表达一系列这些调用序列被允许累计达到什么效果。这是"可控性"的解决方案
  • 换言之,MCP让网关"看见"代理在调用什么工具,Dogwood则定义这些调用序列被允许"达到"什么结果。两者结合,构成了一套从可见到可控的完整代理流量治理框架。

    实践指南:如何在生产中部署Dogwood

    架构设计

    AgentCore Policy位于模型之外,作为确定性控制层。模型提出工具调用,策略引擎接受或拒绝,模型永远不触碰执行逻辑。这种"模型与策略分离"的架构确保了即使模型被对抗性输入攻击,策略层仍然提供确定性的安全边界。

    text
    [AI代理] → [工具调用请求] → [AgentCore Policy引擎]
                                        |
                                  [事件历史存储]
                                        |
                               [Dogwood时序评估]
                                        |
                            [允许 / 拒绝 / 需审批]
                                        |
                                  [执行或阻止]
                                        |
                               [记录事件到历史]

    集成代码示例

    python
    import boto3
    from datetime import datetime, timedelta
    
    class AgentGovernanceManager:
        """AI代理时序治理管理器"""
        
        def __init__(self, policy_store_id):
            self.agentcore = boto3.client('agentcore-policy')
            self.policy_store_id = policy_store_id
            self.event_store = EventStore()  # 事件存储抽象
        
        def check_tool_call(self, agent_id, tool_name, tool_input):
            """检查代理工具调用是否被策略允许"""
            # 获取代理的事件历史
            event_history = self.event_store.get_events(
                agent_id=agent_id,
                time_window=timedelta(hours=24)
            )
            
            # 评估策略
            response = self.agentcore.evaluate(
                policyStoreId=self.policy_store_id,
                principal={
                    'entityType': 'agent',
                    'entityId': agent_id
                },
                action={
                    'actionType': 'tool',
                    'actionId': tool_name
                },
                resource={
                    'resourceType': 'service',
                    'resourceId': tool_input.get('target_service', 'default')
                },
                context={
                    'eventHistory': event_history,
                    'currentInput': tool_input,
                    'currentTimestamp': datetime.utcnow().isoformat()
                }
            )
            
            decision = response.get('decision', 'DENY')
            
            if decision == 'ALLOW':
                # 记录请求事件
                self.event_store.record_event(
                    agent_id=agent_id,
                    action=tool_name,
                    input_data=tool_input,
                    event_type='request',
                    timestamp=datetime.utcnow()
                )
                return True, None
            elif decision == 'DENY':
                reason = response.get('deniedReason', 'Policy denied')
                return False, reason
            else:
                # 需要人类审批
                return False, 'HUMAN_APPROVAL_REQUIRED'
        
        def record_tool_response(self, agent_id, tool_name, response_data):
            """记录工具调用响应"""
            self.event_store.record_event(
                agent_id=agent_id,
                action=tool_name,
                input_data=response_data,
                event_type='response',
                timestamp=datetime.utcnow()
            )
    
    
    class EventStore:
        """事件历史存储"""
        
        def __init__(self):
            # 实际实现应使用DynamoDB、Aurora或专门的审计日志
            self._events = []  # 简化示例
        
        def record_event(self, agent_id, action, input_data, event_type, timestamp):
            self._events.append({
                'agent_id': agent_id,
                'action': action,
                'input': input_data,
                'type': event_type,
                'timestamp': timestamp
            })
        
        def get_events(self, agent_id, time_window):
            cutoff = datetime.utcnow() - time_window
            return [e for e in self._events 
                    if e['agent_id'] == agent_id and e['timestamp'] >= cutoff]

    关键注意事项

    AWS在发布中明确指出了几个关键注意事项:

  • 参考解释器不适用于生产:GitHub上的参考解释器仅用于探索和测试语言,不应用于生产环境的授权。生产部署应使用AgentCore Policy服务

  • 时间戳必须可信:事件的时间戳必须是可信的,事件必须经过认证。不可信的时间戳会使整个策略评估失去意义

  • 多租户隔离:一个租户的历史数据绝不能泄露到另一个租户。这是多租户SaaS环境的基本要求

  • 留存策略:工具调用历史包含敏感数据,必须制定数据保留策略,定期清理过期事件

  • 评估成本:时序评估需要状态化的事件追踪,评估时间可能取决于事件日志长度。长事件日志会导致评估延迟
  • 未来路线图

    AWS为Dogwood规划了几个关键方向:

  • 绝对时间窗口:支持基于墙钟时间的规则,如"工作时间外禁止特定操作"或"月末结算窗口内放宽限制"

  • 活跃性属性:断言"某事最终必须发生"而非"某事不能发生",适用于超时检测和SLA监控——例如"代理必须在30分钟内完成审批流程"

  • 编排策略:面向多代理系统的策略,覆盖代理间的任务交接和锁机制——例如"代理A完成后才能触发代理B"
  • 目前AWS尚未接受社区贡献,计划先收集反馈,待语言稳定后再开放贡献。这一审慎的态度反映了时序策略语言的复杂性——在形式化方法和分布式系统交叉的领域,设计选择的影响深远。

    与同期的DynamoDB向量搜索发布

    值得注意的是,AWS在发布Dogwood的同期(8月16日)还宣布了DynamoDB的原生向量搜索功能。开发者现在可以在DynamoDB中直接存储嵌入向量并执行近似最近邻查询,无需单独的向量数据库。

    这两个发布虽然面向不同场景,但共同指向一个趋势:AWS正在将AI应用所需的基础设施能力(代理治理、向量搜索)深度集成到其核心云服务中。对于构建AI代理应用的开发者来说,这意味着可以在一个云平台内获得从数据存储到代理治理的完整技术栈。

    结语

    Dogwood的开源标志着AI代理治理从"单次调用检查"走向"调用序列治理"的关键一步。随着AI代理在生产环境中的部署越来越广泛——从SpaceXAI的Grok Bot到Grab的分析代理——对代理行为序列的时序治理能力将成为企业安全架构的必备组件。

    AWS通过开源而非闭源的方式推进这一标准,有助于推动行业形成统一的代理治理框架。对于开发者而言,现在是了解和实验Dogwood的最佳时机——在AI代理大规模部署之前构建好治理基础设施,远比事后补救更加明智。

    💬 评论区 (0)

    暂无评论,快来抢沙发吧!