DSL 基本结构

本文面向需要用 DSL 语言手动编写 AgentGuard 访问控制策略的高级用户,重点介绍策略 DSL 的语法结构、常用字段、条件表达式、调用链规则以及动作语义。

AgentGuard 的策略文件通常使用 .rules 后缀。一个文件可以包含多条规则,每条规则描述一类工具调用在什么条件下应当被允许、拒绝或进入审批。

语法总览

以下是策略 DSL 的整体语法结构:

RULE: <rule_id>
[ON: <event_match>]
[TRACE: <placeholder> <trace_sep> <placeholder> ...]
[CONDITION: <expr>]
POLICY: <action>
[Severity: critical | high | medium | low | info]
[Category: <identifier-or-string>]
[Reason: <string>]
[Priority: <number>]
[ttl_ms: <number>]

# 事件匹配
<event_match> :=
    tool_call(<tool_pattern>)
  | tool_call.<subtype>
  | tool_call.<subtype>(<tool_pattern>)

<subtype> := requested | completed | failed
<tool_pattern> := * | tool_name | namespace.tool | namespace.*

# 条件表达式
<expr> :=
    <expr> OR <expr>
  | <expr> AND <expr>
  | NOT <expr>
  | (<expr>)
  | <path> <op> <value>
  | <function_call>

<op> := == | != | < | <= | > | >= | IN | NOT IN | MATCHES | CONTAINS

# 动作
<action> :=
    DENY
  | ALLOW
  | HUMAN_CHECK
  | LLM_CHECK

# 调用链 TRACE 分隔符
<trace_sep> :=
    ->
  | -> * ->
  | -> ... ->
  | -> ...? ->

一条最小规则

下面这条规则表示:当低信任智能体尝试调用 shell.exec 时,直接拒绝。

RULE: deny_low_trust_shell
ON: tool_call(shell.exec)
CONDITION: principal.trust_level < 2
POLICY: DENY
Severity: high
Category: capability
Reason: "Low-trust agent cannot execute shell commands"

这条规则包含四个核心部分:

  • RULE 定义规则 ID。
  • ON 限定规则作用于哪些工具调用。
  • CONDITION 定义命中条件。
  • POLICY 定义命中后的处理动作。

SeverityCategoryReason 是元数据。它们不改变条件判断本身,但会进入审计与告警信息,建议为生产规则补齐。

规则 ID

RULE 后面的值是规则 ID,用于审计、调试和规则管理。建议使用稳定、可读、唯一的名字。

RULE: deny_external_email_low_trust

推荐命名方式:

  • 使用小写字母、数字、下划线或连字符。
  • 让名字体现动作和场景,例如 deny_...review_...
  • 不要在同一批规则中重复使用同一个 ID;重复 ID 在加载到注册表时会覆盖旧规则。

ON:限定规则作用范围

ON 用于先按事件类型和工具名筛选候选规则。写得越精确,规则越容易理解,也越不容易误伤。

ON 可以省略。省略后等价于不按工具名预筛选,通常只建议用于 TRACE 规则,因为 TRACE 的最后一个占位符会进一步约束当前调用。普通单步规则建议显式写出 ON

按工具名匹配

RULE: deny_destructive_shell
ON: tool_call(shell.exec)
CONDITION: tool.cmd MATCHES ".*\\b(rm\\s+-rf|mkfs|dd\\s+if=)\\b.*"
POLICY: DENY
Severity: critical
Category: shell

tool_call(shell.exec) 只匹配名为 shell.exec 的工具。

使用通配符

RULE: review_all_shell_tools
ON: tool_call(shell.*)
CONDITION: principal.trust_level < 3
POLICY: HUMAN_CHECK
Severity: high
Category: shell

shell.* 可以匹配 shell.execshell.run 等工具名。也可以使用 tool_call(*) 表示匹配所有工具调用。

按工具调用阶段匹配

RULE: deny_external_http_before_execution
ON: tool_call.requested(http.post)
CONDITION: tool.domain NOT IN allowlist.http
POLICY: DENY
Severity: high
Category: egress

常用阶段包括:

阶段 含义
requested 工具执行前的请求阶段,最常用于访问控制
completed 工具执行完成后的结果阶段,可读取 tool.result
failed 工具调用失败阶段

如果你只是想在工具执行前拦截,优先使用 ON: tool_call.requested(...) 或直接使用 ON: tool_call(...)

CONDITION:条件表达式

CONDITION 决定规则是否命中。条件表达式由路径、字面量、比较运算符、函数和布尔逻辑组成。

CONDITION 可以省略。若同时存在 TRACE,则规则会在调用链模式匹配时命中;若既没有 TRACE 也没有 CONDITION,规则会在 ON 匹配时直接命中。除非你确实想写一条“无条件规则”,否则建议显式写出 CONDITION

RULE: deny_external_email_without_scope
ON: tool_call(email.send)
CONDITION: tool.recipient_domain NOT IN allowlist.email
           AND caller.scope_missing("external_email")
POLICY: DENY
Severity: high
Category: egress
Reason: "External email requires external_email scope"

布尔逻辑优先级

表达式优先级从高到低为:

优先级 语法
1 (...)
2 NOT
3 AND
4 OR

建议在混合使用 ANDOR 时显式加括号。

CONDITION: tool.boundary == "external"
           AND (caller.scope_missing("sensitive_export") OR goal_drift_detected())

字面量

DSL 支持以下常见字面量:

类型 示例
字符串 "evil.com"'internal'
数字 030.8
布尔值 truefalseTRUEFALSE
字符串集合 {"email.send", "http.post"}

字符串集合常用于 IN / NOT IN

CONDITION: tool.name IN {"email.send", "http.post", "slack.post"}

比较与集合运算符

运算符 含义 示例
== / != 相等 / 不相等 principal.role == "basic"
< / <= / > / >= 数值或可比较值比较 principal.trust_level < 2
IN 左值属于右侧集合、列表、字典 key 或相同字符串 tool.recipient_domain IN allowlist.email
NOT IN IN 的否定 tool.domain NOT IN allowlist.http
MATCHES 正则表达式匹配 tool.url MATCHES ".*127\\.0\\.0\\.1.*"
CONTAINS 字符串包含、列表成员包含或字典 key 包含 tool.body CONTAINS "password"

MATCHES 的右侧是正则表达式字符串,底层使用 Python re 语义。正则字符串中常见反斜杠需要转义,例如 \\b\\d

常用路径

路径用于读取当前事件、调用者、工具调用参数和运行时上下文中的值。

调用者身份

principal 表示当前发起工具调用的智能体身份。callerprincipal 的别名。

CONDITION: principal.role == "basic"
CONDITION: caller.trust_level < 3

常用字段包括:

路径 含义
principal.agent_id 智能体 ID
principal.session_id 会话 ID
principal.role 角色,常见值如 basicdefaultprivilegedsystem
principal.trust_level 信任级别,通常数值越高权限越大

工具信息与参数

tooltool_call 的便捷别名。

CONDITION: tool.name == "send_email_to"
CONDITION: tool.recipient MATCHES ".*@evil\\.com"

常用字段包括:

路径 含义
tool.name 当前工具名
tool.<param> 当前工具参数,例如 tool.domaintool.sqltool.recipient_domain
tool.result 工具执行结果,主要用于结果阶段事件

对于工具函数参数,推荐使用 tool.<param>,因为它在策略中更接近自然语言。例如:

RULE: deny_sql_ddl
ON: tool_call(db.query)
CONDITION: tool.sql MATCHES "(?i).*\\b(DROP|TRUNCATE|ALTER|GRANT|REVOKE)\\b.*"
POLICY: DENY
Severity: critical
Category: database

工具静态标签

注册工具时可以声明静态标签,规则可通过 tool.boundarytool.sensitivitytool.integritytool.tags 读取。

@guard.tool(
    "send_email_to",
    sink_type="email",
    boundary="external",
    sensitivity="moderate",
    integrity="trusted",
    tags=["egress", "email"],
)
def send_email_to(recipient: str, subject: str, body: str) -> str:
    ...

对应策略示例:

RULE: deny_high_sensitivity_external_tool
ON: tool_call.requested
CONDITION: tool.boundary == "external" AND tool.sensitivity == "high"
POLICY: DENY
Severity: critical
Category: data_exfiltration

标签字段的取值范围:

字段 取值
tool.boundary internalexternalprivileged
tool.sensitivity lowmoderatehigh
tool.integrity trustedunfiltered
tool.tags 字符串列表

事件字段

CONDITION: event.session_id == "session-001"
CONDITION: event.type == "tool_call_requested"

常用事件别名包括:

路径 实际含义
event.type 事件类型
event.id 事件 ID
event.timestamp 事件时间戳
event.session_id 当前会话 ID

Allowlist

Allowlist 通常由运行时配置注入,规则中可以用 allowlist.<name>whitelist("<name>") 访问。

POLICY:命中后的动作

POLICY 定义规则命中后的处理方式。

动作 含义
DENY 拒绝工具调用,不执行原工具
ALLOW 允许工具调用
HUMAN_CHECK 进入人工审批流程
LLM_CHECK 交给配置的 LLM 审查;未配置 LLM 或审查失败时会退化为人工审批

如果没有任何规则命中,当前运行时会默认允许工具调用。因此,表达“禁止未知目标”“禁止不在白名单中”的意图时,应写成 DENY / HUMAN_CHECK 条件规则,而不是只写一条正向 ALLOW 规则。

内置函数

函数可以出现在条件中。返回布尔值的函数可以直接作为条件;返回值的函数通常配合比较运算符使用。

Allowlist 与集合函数

函数 返回 说明
whitelist("name") 集合 读取运行时注入的 allowlist
subset(values, container) 布尔值 values 中所有元素都在 container
any_in(values, container) 布尔值 values 中任意元素在 container

示例:所有收件人都必须在用户通讯录中,否则拒绝。

RULE: deny_unknown_recipients
ON: tool_call(send_email_to)
CONDITION: NOT subset(tool.recipients, whitelist("user_address_book"))
POLICY: DENY
Severity: high
Category: email

示例:任意收件人命中黑名单就拒绝。

RULE: deny_blocked_recipient
ON: tool_call(send_email_to)
CONDITION: any_in(tool.recipients, whitelist("blocked_emails"))
POLICY: DENY
Severity: high
Category: email

字符串、URL 和邮箱函数

函数 说明
starts_with(text, prefix) 判断字符串前缀
ends_with(text, suffix) 判断字符串后缀
contains(container, value) 函数形式的包含判断
url.domain(url) 提取 URL 主机名
url.is_external(url) 判断 URL 是否不属于内部域名 allowlist
email.domain(address) 提取邮箱域名

示例:禁止访问本地或内网地址。

RULE: deny_http_ssrf
ON: tool_call(http.get)
CONDITION: tool.url MATCHES ".*(?:localhost|127\\.0\\.0\\.1|10\\.\\d+|172\\.1[6-9]\\.|192\\.168\\.).*"
POLICY: DENY
Severity: critical
Category: ssrf

示例:使用 URL 解析函数判断域名。

RULE: review_external_url
ON: tool_call(browser.open)
CONDITION: url.is_external(tool.url)
POLICY: HUMAN_CHECK
Severity: medium
Category: network

标签、scope 与工具 tag 函数

函数 说明
input.has_label("pattern") 当前会话输入或上游数据是否带有某个标签
input.has_any_label({"a/*", "b/*"}) 当前会话输入或上游数据是否命中任意标签模式
caller.scope_missing("scope") 当前调用者是否缺少某个 scope
tool.has_tag("tag") 当前工具是否带有某个静态 tag

示例:会话中出现敏感标签,并且调用者缺少外发授权时拒绝外部调用。

RULE: deny_sensitive_label_external_without_scope
ON: tool_call.requested
CONDITION: tool.boundary == "external"
           AND input.has_any_label({"pii/*", "finance/*", "secret/*"})
           AND caller.scope_missing("sensitive_export")
POLICY: DENY
Severity: critical
Category: data_exfiltration

会话历史与行为函数

函数 返回 说明
upstream_contains_tool("tool") 布尔值 当前会话上游是否调用过某工具
upstream_contains_any_tool({"a", "b"}) 布尔值 上游是否调用过集合中的任意工具
derived_from_tool("tool") 布尔值 当前实现中按上游工具判断
tool_sequence_matches({"a", "b"}) 布尔值 会话工具序列是否按顺序包含这些工具
repeated_attempts(tool="name", window="5m") 数字 当前会话中某工具重复尝试次数
distinct_targets() 数字 最近目标去重数量
path_length(source="tool") 数字 从某上游工具到当前调用的大致路径长度

示例:数据库查询后发送邮件,进入 LLM 审查。

RULE: review_email_after_db_query
ON: tool_call(email.send)
CONDITION: upstream_contains_any_tool({"db.query", "database_query"})
POLICY: LLM_CHECK
Severity: high
Category: data_exfiltration
Reason: "Email send follows a database query"

示例:5 分钟窗口内 HTTP 外发尝试过多,转人工审批。

RULE: review_http_burst
ON: tool_call(http.post)
CONDITION: repeated_attempts(tool="http.post", window="5m") > 4
POLICY: HUMAN_CHECK
Severity: medium
Category: behavioural_anomaly

历史参数与历史结果

函数 返回 说明
history_arg("tool", "param") 任意值 读取当前会话中某工具最近一次调用的参数
history_result("tool") 任意值 读取当前会话中某工具最近一次调用结果
history_args_match("tool", "param", value) 布尔值 判断某历史参数是否等于指定值

示例:如果文档分类结果为 restricted,则禁止外发邮件。

RULE: deny_restricted_doc_external_email
ON: tool_call(email.send)
CONDITION: history_result("classify_doc") == "restricted"
           AND tool.recipient_domain NOT IN allowlist.email
POLICY: DENY
Severity: critical
Category: data_exfiltration

注意:history_arghistory_result 读取的是已经写入会话历史的调用。若你需要在 TRACE 规则中读取“当前正在判断的这个工具调用”的参数,优先使用 TRACE 占位符,例如 Mailer.recipient

语义信号函数

这些函数读取运行时注入的安全信号。

函数 说明
goal_drift_detected() 检测到目标漂移
scope_expansion_detected() 检测到权限或范围扩张
suspicious_exfil_pattern() 检测到可疑外发模式
high_entropy_payload_detected() 检测到高熵 payload
goal_changed_from_initial() 当前目标偏离初始目标

示例:存在目标漂移且准备调用外部工具时转人工审批。

RULE: review_goal_drift_external_call
ON: tool_call.requested
CONDITION: tool.boundary == "external" AND goal_drift_detected()
POLICY: HUMAN_CHECK
Severity: high
Category: goal_drift

TRACE:声明式调用链规则

TRACE 用于描述“某个上游调用最终流向某个下游调用”的模式。它比 upstream_contains_tool(...) 更强,因为它可以给链上的每个位置命名,并在 CONDITION 中读取这些位置的工具名、参数、标签和结果。

RULE: deny_secret_to_external_sink
TRACE: SecRead ->...?-> Sink
CONDITION: SecRead.name == "secret.read"
           AND Sink.name IN {"http.post", "email.send", "slack.post"}
POLICY: DENY
Severity: critical
Category: secret_exfiltration
Reason: "Tool chain reads secret and then contacts an external sink"

TRACE 分隔符

语法 含义 示例
A -> B A 后面紧接 B,中间不能有其他调用 Fetcher -> Executor
A -> * -> B A 和 B 中间恰好有一次调用 Reader -> * -> Writer
A -> ... -> B A 和 B 中间至少有一次调用 A -> ... -> B
A -> ...? -> B A 在 B 之前任意位置,中间可以为 0 次或多次调用 DbOp -> ...? -> Mailer

最常用的是 -> ...? ->,表示“上游某个调用最终走到了下游某个调用”。

TRACE 占位符字段

TRACE: Src -> ...? -> Dst 中,SrcDst 是占位符。它们会绑定到会话调用链中的具体工具调用。

字段 含义
Placeholder.name 绑定调用的工具名
Placeholder.integrity 绑定调用的 label.integrity
Placeholder.sensitivity 绑定调用的 label.sensitivity
Placeholder.boundary 绑定调用的 label.boundary
Placeholder.result 绑定调用的返回值
Placeholder.<param> 绑定调用的参数,例如 Mailer.recipient

示例:外部输入最终进入 shell,进入 LLM 审查。

RULE: review_external_input_to_shell
TRACE: Src ->...?-> Shell
CONDITION: Src.boundary == "external"
           AND Shell.name IN {"shell.exec", "shell_exec"}
POLICY: LLM_CHECK
Severity: critical
Category: prompt_injection
Reason: "External input reached shell execution"

示例:数据库查询后紧接邮件发送,直接拒绝。

RULE: deny_adjacent_db_to_email
TRACE: DbOp -> Mailer
CONDITION: DbOp.name IN {"db.query", "database_query"}
           AND Mailer.name IN {"email.send", "send_email_to"}
POLICY: DENY
Severity: critical
Category: data_exfiltration

元数据

Severity: critical
Category: data_exfiltration
Reason: "Sensitive data sent to non-allowlisted endpoint"

建议至少填写:

字段 建议
Severity 用于告警分级,建议取 criticalhighmediumlowinfo
Category 用于归类,例如 egressdatabasedata_exfiltration
Reason 用一句话说明规则命中的安全原因

编写策略的建议

先限制高风险工具

优先为外发、命令执行、文件写入、数据库写入和敏感数据读取工具写规则。

RULE: deny_db_write_basic
ON: tool_call(db.exec)
CONDITION: tool.sql MATCHES "(?i).*\\b(INSERT|UPDATE|DELETE|DROP|ALTER)\\b.*"
           AND principal.role == "basic"
POLICY: DENY
Severity: critical
Category: database

优先写窄规则

能写 ON: tool_call(email.send) 时,不要写 ON: tool_call(*)。能写 tool.name IN {...} 时,不要只靠模糊正则。

窄规则更容易审计,也更容易定位误报。

把硬拒绝和审批分清楚

明确危险的行为用 DENY,不确定但高风险的行为用 HUMAN_CHECKLLM_CHECK

为跨步骤风险使用 TRACE

单次工具调用规则只能看到当前操作。对于“先读取敏感数据,再外发”的风险,应使用 TRACE 或会话历史函数。

RULE: review_db_result_to_external_email
TRACE: DbOp ->...?-> Mailer
CONDITION: DbOp.name IN {"db.query", "database_query"}
           AND Mailer.name IN {"email.send", "send_email_to"}
           AND Mailer.boundary == "external"
POLICY: LLM_CHECK
Severity: high
Category: data_exfiltration

不要依赖规则顺序表达业务意图

策略文件可以包含多条规则。多条规则同时命中时,运行时会合并命中结果并选择相应动作。为了让策略行为可预测,建议把互斥条件写清楚,而不是依赖规则书写顺序。

用校验命令检查规则

编写完成后,建议先运行:

python -m agentguard check rules/my_policy.rules

也可以校验一个目录下的所有 .rules 文件:

python -m agentguard check rules/

这个命令会解析、编译并做语义检查,能发现常见问题,例如重复规则 ID、缺少元数据、TRACE 分隔符错误、标签枚举值错误等。

results matching ""

    No results matching ""