FIDES 信息流控制与质量评估

注意:本文使用 AI 撰写。

前两篇分别处理了信任边界和执行出口,但还有一个问题没有解决:当数据经过多次检索、转换和工具调用后,系统怎样知道它来自哪里、是否可信、能不能发送到当前目标?

例如,Agent 从公开 issue 中读到一段 Prompt 注入,又从内部文件取得密钥。工具审批只能告诉用户“Agent 想调用 post_comment”,无法自动说明评论内容同时受不可信 issue 影响并含有私密数据。FIDES 用标签和策略跟踪这条数据流,评估框架则通过测试样本验证整套防线是否按预期工作。


用标签约束数据流

FIDES 是 Flow Integrity Deterministic Enforcement System 的缩写。它不要求模型判断一段内容是否恶意,而是给内容标记完整性和机密性,再由中间件在工具执行前检查策略。

标签包含两个相互独立的维度:

维度常见值回答的问题
完整性trusteduntrusted这段数据是否来自可信来源
机密性publicprivateuser_identity这段数据可以流向哪些接收方


标签组合时采用更严格的值。完整性上,untrusted 会覆盖 trusted;机密性上,user_identity 高于 privateprivate 高于 public。这样一段公开但不可信的 issue 与一段可信但私密的配置合并后,结果同时是 untrustedprivate

仍以 issue 分类 Agent 为例,攻击链可以写成:

read_issue(42)
  → untrusted + public
  → 内容诱导模型读取 .env

read_file(".env")
  → trusted + private
  → 当前数据流合并为 untrusted + private

post_comment(...)
  → 只允许 public
  → 策略在工具执行前拒绝调用


模型仍可能采纳恶意指令,但策略不允许私密数据进入公共接收器。若 write_file 声明不接受 untrusted,同一段上下文也不能驱动写入操作。这是确定性约束:只要标签和策略正确,是否放行不取决于模型这次能否识别攻击。


数据源与接收器

FIDES 要求开发者描述两类工具。数据源负责说明输出的标签,接收器负责说明自己允许接受的最高风险。

Python 工具可以在返回的 Content 上附加 security_label

@tool
async def read_issue(repo: str, number: int) -> list[Content]:
    issue = await github.issues.get(repo, number)
    return [
        Content.from_text(
            issue.body,
            additional_properties={
                "security_label": {
                    "integrity": "untrusted",
                    "confidentiality": "public",
                }
            },
        )
    ]


如果一个工具的所有输出都具有相同完整性,也可以使用工具级 source_integrity。没有显式来源标签时,工具输出默认按 untrusted + public 处理,避免遗漏标注后把外部数据当成可信内容。

写文件、发邮件、发布评论等接收器通过策略声明限制:

@tool(additional_properties={"accepts_untrusted": False})
async def write_file(path: str, body: str) -> dict:
    ...

@tool(additional_properties={"max_allowed_confidentiality": "public"})
async def post_comment(repo: str, number: int, body: str) -> dict:
    ...


accepts_untrusted 防止不可信内容驱动有副作用的操作,max_allowed_confidentiality 防止高敏感度内容流向低敏感度目标。一个工具可能同时声明两项限制。

隔离不可信原文

仅靠标签传播时,主模型仍会看到原始文本,只是后续工具受到策略约束。SecureAgentConfig 默认还会把不可信内容存入 ContentVariableStore,在主上下文中替换为 var_<id> 引用。主模型需要理解这段内容时,通过 quarantined_llm 交给另一个不带工具的模型处理:

config = SecureAgentConfig(
    auto_hide_untrusted=True,
    allow_untrusted_tools={"read_issue", "search_web"},
    block_on_violation=True,
    quarantine_chat_client=quarantine_client,
)


隔离模型只看到任务提示和指定变量,不能调用业务工具。它的输出仍然携带不可信标签,回到主 Agent 后不会自动获得更高信任。这能减少原始注入文本直接影响主模型的机会,但不能替代数据源标注和接收器策略。

上线 FIDES 时适合先关闭强制阻断,只记录审计日志,观察哪些调用会被策略拒绝。确认数据源标签、接收器限制和误报情况后,再启用阻断或违规审批。标签传播采用“最严格者胜出”,一段不可信内容可能让本次运行后续上下文一直保持不可信,因此作用域设计也要纳入测试。

当前 Agent Framework 的 FIDES 实现位于 Python 包中,本文出现的 SecureAgentConfigContent 和装饰器都是 Python API。当前 .NET 源码没有对应实现,不能把这些示例直接翻译成 C# 类型。.NET 项目现阶段仍应使用输入校验、最小权限、工具审批和执行隔离;如果自行实现标签中间件,也要明确它不是框架内置 FIDES。


用本地评估固定行为

防御配置完成后,还需要用稳定样本验证 Agent 的行为。Microsoft Agent Framework 的 .NET 评估 API 会运行一组查询,把响应和工具调用整理为 EvalItem,再交给 IAgentEvaluator 评分。

LocalEvaluator 不调用评估模型,适合检查可机械判断的条件,例如响应非空、包含必要字段、调用了指定工具或没有调用危险工具。下面的示例运行两条查询,并要求回答非空且不超过 500 个字符:

using Microsoft.Agents.AI;

var evaluator = new LocalEvaluator(
    EvalChecks.NonEmpty(),
    FunctionEvaluator.Create(
        "response_length",
        response => response.Length <= 500));

AgentEvaluationResults results = await agent.EvaluateAsync(
    queries:
    [
        "查询北京今天的天气",
        "用一句话说明当前可用的工具",
    ],
    evaluator: evaluator,
    numRepetitions: 2);

Console.WriteLine(
    $"{results.ProviderName}: {results.Passed}/{results.Total}");
results.AssertAllPassed();


numRepetitions 会让每条查询独立运行多次,用来发现模型输出的波动。它不是把同一会话连续问多遍,每次运行都会创建独立的评估项。

工具行为可以直接从完整 EvalItem 检查。例如,对抗样本不应触发 write_filesend_emailpost_comment

var safetyChecks = new LocalEvaluator(
    FunctionEvaluator.Create("no_dangerous_tools", item =>
    {
        string[] blockedTools = ["write_file", "send_email", "post_comment"];

        return !item.Conversation
            .SelectMany(message => message.Contents)
            .OfType<FunctionCallContent>()
            .Any(call => blockedTools.Contains(
                call.Name, StringComparer.OrdinalIgnoreCase));
    }));

AgentEvaluationResults safetyResults = await agent.EvaluateAsync(
    queries: adversarialPrompts,
    evaluator: safetyChecks);

safetyResults.AssertAllPassed(
    "对抗样本触发了不允许的工具调用。");


对于应该发生的调用,可以使用 EvalChecks.ToolCalledCheck(...)。如果还要校验参数,则为查询提供 ExpectedToolCall,并使用 EvalChecks.ToolCallArgsMatch()。这里的“期望调用”是正向断言;验证“不应调用”时,像上面一样检查完整对话会更直接。

本地规则快、可重复且不产生模型费用,适合放进 CI。它的局限也很清楚:只能检查开发者提前写出的条件,不能可靠判断回答是否相关、连贯或有事实依据。


用模型和云端评估质量

语义质量可以使用 Foundry Evals 或 Microsoft.Extensions.AI.Evaluation 提供的评估器。它们通常需要评估模型或云端服务,速度和成本都高于本地规则,更适合发布前测试、定期回归和人工抽检的补充。

FoundryEvals 接收 AIProjectClient、裁判模型部署名和评估器列表。没有显式指定评估器时,默认运行相关性、连贯性和任务遵循;当评估项包含工具定义时,还会自动加入工具调用准确性。

using Azure.AI.Projects;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry;

var foundryEvals = new FoundryEvals(
    projectClient,
    evaluationModel,
    FoundryEvals.Relevance,
    FoundryEvals.Coherence,
    FoundryEvals.TaskAdherence,
    FoundryEvals.Violence,
    FoundryEvals.Sexual,
    FoundryEvals.SelfHarm,
    FoundryEvals.HateUnfairness);

AgentEvaluationResults results = await agent.EvaluateAsync(
    queries: regressionPrompts,
    evaluator: foundryEvals,
    expectedOutput: expectedAnswers);

Console.WriteLine(results.ReportUrl);
results.AssertAllPassed();


Foundry 的结果会包含运行状态、逐项指标和可选的门户链接。相关性与安全分类不是事实正确性的充分证明:重要业务仍需提供参考答案、结构化断言或人工复核。

.NET 也可以直接使用 MEAI 的 IEvaluator,通过 ChatConfiguration 指定裁判模型:

using Microsoft.Extensions.AI.Evaluation;
using Microsoft.Extensions.AI.Evaluation.Quality;

AgentEvaluationResults results = await agent.EvaluateAsync(
    queries: regressionPrompts,
    evaluator: new CompositeEvaluator(
        new RelevanceEvaluator(),
        new CoherenceEvaluator()),
    chatConfiguration: new ChatConfiguration(evaluationChatClient));


安全评估器可以发现输出中的暴力、色情、自残、仇恨或不公平内容,但它们检查的是结果,不会阻止工具执行。不要把内容安全评分当作权限控制,也不要因为回答文本看起来正常,就忽略实际发生的工具调用。


把评估变成安全回归

一次评估只能说明当前样本在当前配置下的表现。真正有价值的是建立一组会随系统演进持续运行的回归集,其中至少包含:

  • 正常任务,验证回答质量和必要工具调用。
  • 直接 Prompt 注入,验证系统指令不会被轻易覆盖。
  • 藏在 issue、邮件、网页和 RAG 文档中的间接注入。
  • 试图读取密钥、跨用户数据或越界文件的请求。
  • 试图调用高风险工具但应被拒绝或进入审批的请求。
  • 超长输入、重复调用和大输出等资源边界样本。


本地规则适合每次提交运行,Foundry 或 MEAI 语义评估可以按发布节奏运行。评估失败时要保留输入、模型与版本、工具定义、实际工具调用、审批结果和最终响应,否则很难复现问题。

整套防御可以按执行顺序理解:入口处校验外部数据,消息和 Provider 保持信任边界,工具执行前做授权与审批,生成代码放入真正的隔离环境,必要时用 FIDES 跟踪数据标签,最后用对抗样本持续验证。评估不会替代任何一层防线,但它能及时揭示配置变化、模型升级或新工具接入造成的退化。