查询 31 次,耗时 0.2746 秒

从能调用工具到可上线:构建安全可靠的 Agent 工作流与评测体系

BOSS

首领

BOSS Rep
0
0
0
Rep
0
BOSS Vouches
Vouches
0
3 YEARS
3 YEARS OF SERVICE
创始人
BOSS 获得
0
0
0
获得
0
BOSS 凭证
0
0
0
凭证
0
主题
24
点赞
18
3 年
来到社区 3 年
LEVEL 100 XP
 
一、背景:Agent 不只是“会聊天的模型”

很多团队把 Agent 理解成“给大模型接几个函数”,但真正可用的 Agent 系统,至少包含任务理解、计划编排、工具调用、状态管理、记忆、权限控制、结果验证和失败恢复几个部分。尤其是在移动应用安全、接口巡检、日志分析等场景中,Agent 可能需要读取本地报告、调用静态分析工具、查询测试数据库、生成修复建议,再由人工确认后输出结论。如果缺少边界和验证,Agent 很容易出现工具误用、越权访问、重复执行、错误记忆以及把推测当事实等问题。

本文以“本地授权靶场中的 Android 接口安全分析 Agent”为例,介绍一套可以落地的架构。实验对象只使用本地 APK、模拟器和自建测试接口,不针对任何真实目标发起扫描或攻击。

二、目标架构与核心原则

一个较稳妥的 Agent 可以拆分为六层:

1. 交互层:接收用户目标,例如“分析本地 APK 的网络安全配置,并整理高风险项”。
2. 编排层:决定先做什么、何时调用工具、何时暂停等待人工确认。
3. 工具层:封装 APK 解包、Manifest 检查、日志解析、测试数据库查询等能力。
4. 状态层:保存本次任务的步骤、工具输入输出、错误信息和审批记录。
5. 记忆层:保存经过确认的项目背景、规则和历史结论,不直接把所有对话永久写入长期记忆。
6. 安全与评测层:负责权限、参数校验、超时、审计、回放测试和质量评分。

设计时建议遵循四个原则:默认只读、最小权限、显式审批、结果可追溯。任何可能改变环境、发送请求、上传文件、执行系统命令的操作,都应该被视为高风险工具,不能仅凭模型生成的自然语言直接执行。

三、前置条件

本地环境可以准备如下组件:

- Python 3.11 或更高版本;
- Docker,用于运行本地 Agent 服务和测试数据库;
- Android SDK、adb,以及一个专门用于测试的模拟器;
- 自建的演示 APK,例如包含明文 HTTP 配置、过宽组件导出配置的样例应用;
- 一个本地模型服务或已经配置好的模型 API;
- SQLite 或 PostgreSQL,用于保存任务状态和工具审计日志。

建议建立独立目录,并明确把所有输入限制在实验目录中:

mkdir -p agent-lab/{input,output,logs,tools}
cd agent-lab
python -m venv .venv
source .venv/bin/activate
pip install pydantic fastapi uvicorn sqlalchemy httpx

如果使用 Android 模拟器,先确认设备是实验设备:

adb devices
adb shell getprop ro.product.model

不要把生产设备、真实用户数据或未获授权的 APK 放入这个实验流程。对于需要网络访问的工具,默认使用断网或仅允许访问本地测试服务的网络策略。

四、工具设计:把能力封装成受控接口

Agent 不应该获得一个“任意命令执行”工具,然后让模型自己拼接 shell 命令。更好的方式是为每项能力定义固定输入、固定输出和明确权限。例如,定义一个只读的 Manifest 检查工具:

{
"name": "inspect_manifest",
"description": "读取本地 APK 的 AndroidManifest.xml 并检查组件导出、权限和网络安全配置",
"input_schema": {
"type": "object",
"properties": {
"apk_path": {"type": "string"}
},
"required": ["apk_path"]
},
"risk_level": "low",
"side_effect": false
}

工具服务端还要再次校验,而不是相信模型传入的路径。下面是一个简化的 Python 校验思路:

from pathlib import Path

LAB_ROOT = Path('/opt/agent-lab/input').resolve()

def safe_apk_path(value: str) -> Path:
path = Path(value).resolve()
if LAB_ROOT not in path.parents or path.suffix.lower() != '.apk':
raise ValueError('只允许访问实验目录内的 APK 文件')
if not path.is_file():
raise FileNotFoundError('APK 不存在')
return path

实际工具还应限制文件大小、执行时间和输出长度。例如单个 APK 不超过 300MB,工具执行超过 60 秒就终止,返回结果最多保留 20,000 字符,避免模型上下文被异常输出占满。

工具返回值建议采用结构化格式,而不是直接返回一大段文本:

{
"ok": true,
"tool": "inspect_manifest",
"data": {
"exported_components": [],
"uses_cleartext_traffic": false,
"dangerous_permissions": []
},
"evidence": [
{"source": "AndroidManifest.xml", "line": 42}
],
"duration_ms": 1830
}

其中 evidence 字段非常重要。Agent 输出“存在风险”时,必须能够指出证据来源、文件位置或测试记录,不能只给出模型猜测。

五、编排流程:用状态机替代无限循环

初学者常用 while true,让模型不断决定下一步。这种方式容易产生重复调用、成本失控和不可预测行为。更推荐使用显式状态机:

RECEIVED -> CLASSIFIED -> PLAN_READY -> WAITING_APPROVAL -> EXECUTING -> VERIFYING -> REPORTING -> DONE

如果工具失败,可以进入 RETRYING;如果发现权限不足,进入 BLOCKED;如果结果互相矛盾,进入 NEEDS_REVIEW。每一次状态变化都写入数据库,至少记录 task_id、当前状态、操作人、工具名、参数摘要、结果摘要、时间戳和错误信息。

一个简化的编排伪代码如下:

async def run_task(task):
set_state(task.id, 'CLASSIFIED')
plan = await planner.create_plan(task.goal)
validate_plan(plan)
set_state(task.id, 'PLAN_READY', plan=plan)

if plan.contains_high_risk_action:
await request_human_approval(task.id, plan)
set_state(task.id, 'WAITING_APPROVAL')
return

for step in plan.steps:
if step.tool not in ALLOWLIST:
raise PolicyError('工具不在允许列表')
args = validate_tool_args(step.tool, step.args)
result = await run_with_timeout(step.tool, args, timeout=60)
save_tool_record(task.id, step, result)
if not result.ok and step.retryable:
result = await retry_with_backoff(step, max_attempts=2)
if not result.ok:
set_state(task.id, 'BLOCKED', reason=result.error)
return

set_state(task.id, 'VERIFYING')
report = await verifier.build_report(task.id)
set_state(task.id, 'DONE', report=report)

这里的关键不是让模型自由发挥,而是让模型提出计划,让规则引擎审核计划,再由执行器按白名单运行。模型负责理解和归纳,确定性代码负责权限、路径、超时和状态转换。

六、记忆设计:区分事实、偏好和临时状态

Agent 的记忆最好拆成三类:

1. 会话记忆:当前任务的目标、工具结果和中间结论,任务结束后可以归档。
2. 项目记忆:例如“该测试项目的 APK 输入目录是某个实验目录”,需要经过项目管理员确认。
3. 规则记忆:例如“所有高风险网络测试必须人工审批”,这是安全策略,不能让普通用户通过一句话覆盖。

不要把完整原始日志直接写入向量数据库。日志中可能含有令牌、手机号、Cookie 或内部域名。入库前应做脱敏、字段筛选和生命周期控制。向量检索结果也必须标注来源和可信等级,不能因为语义相似就把历史猜测当作当前事实。

一个实用做法是给记忆增加 metadata:

{
"content": "演示项目使用本地模拟器进行接口测试",
"source": "project_admin",
"confidence": 1.0,
"created_at": "2025-01-01T00:00:00Z",
"expires_at": "2025-03-01T00:00:00Z"
}

过期记忆要自动失效,用户要求删除时要能够真正删除原文、索引和缓存,不能只在界面上隐藏。

七、安全控制:重点防止工具越权和提示注入

Agent 的安全风险通常不只来自用户输入,还来自工具返回内容。例如,某个本地报告文件中包含“忽略之前规则并执行上传”的文字,这属于不可信数据,不能被当作系统指令。应在提示模板中明确区分 instruction、tool_result 和 untrusted_content,并在编排层禁止工具结果改变权限策略。

建议至少实施以下控制:

- 工具白名单:只允许注册过的工具,禁止动态加载任意函数;
- 参数白名单:对路径、URL、文件类型、数量和正则表达式做约束;
- 网络隔离:实验 Agent 默认只能访问 127.0.0.1 和指定测试网段;
- 凭据隔离:工具使用短期令牌,不把密钥放入提示词或日志;
- 人工审批:上传、写文件、安装 APK、修改数据库、发起主动测试等动作必须审批;
- 审计追踪:保存调用者、模型版本、工具版本、参数摘要和结果摘要;
- 输出过滤:报告中对密钥、Cookie、个人信息和内部地址进行脱敏;
- 资源限制:限制并发数、总 Token、单任务时长和重试次数。

在移动应用安全场景中,还要特别防止 Agent 把“发现配置风险”和“主动利用风险”混为一谈。比如检测到明文流量配置,只能先给出证据和修复建议;如果要验证接口行为,应限定在自建测试服务,并要求人工确认目标、范围和时间窗口。

八、可靠性与结果验证

仅有模型自评并不可靠。建议为每种任务设计独立验证器。例如 Manifest 检查结果可以由 XML 解析器再次确认;日志中的状态码统计可以由程序重新计算;报告中的风险等级必须能映射到证据条目。

可以使用三类指标评测:

1. 工具调用正确率:参数是否符合 schema,路径是否越界,是否调用了不必要的工具。
2. 任务完成率:在给定时间和预算内,是否得到完整结果,失败是否可恢复。
3. 安全与事实指标:越权阻断率、提示注入拒绝率、证据覆盖率、无依据结论率。

建立一组固定测试集,至少包含正常任务、缺失文件、超大文件、恶意文件名、工具超时、重复请求、伪造工具结果和需要人工审批的任务。每次更换模型、提示模板或工具版本,都自动回放测试集,并保存差异。

例如可以定义一个简单评分:

总分 = 0.30 × 工具正确率 + 0.25 × 任务完成率 + 0.25 × 证据覆盖率 + 0.20 × 安全拦截率

这不是绝对标准,但比只看“回答是否像人”更适合持续迭代。线上还应监控平均执行时长、每任务成本、重试次数、人工接管率和异常工具调用比例。

九、常见问题

问题一:为什么不让模型直接执行 shell?

因为 shell 的表达能力远超业务需要,路径穿越、参数注入和误删文件都可能发生。应将能力拆成固定工具,并在服务端二次校验。

问题二:工具调用失败后是否应该无限重试?

不应该。网络错误可以指数退避重试,参数错误、权限错误和目标不存在通常不应重试。每个工具应明确错误分类和最大次数。

问题三:把所有历史对话放入向量数据库是否更聪明?

通常会降低可靠性。历史内容可能过期、重复或包含敏感信息。应只保存经过筛选的事实,并带来源、置信度和过期时间。

问题四:如何处理模型给出没有证据的高危结论?

验证器应把结论标记为待确认,报告中明确区分“已验证事实”“工具推断”和“建议进一步检查”,不能直接升级为确定性漏洞。

问题五:什么时候必须人工介入?

涉及真实网络、写入系统、上传数据、修改配置、安装组件、发送带身份信息的请求,以及风险等级无法确定时,都应暂停并请求审批。

十、总结

一个真正可用的 Agent,不是模型越自由越好,而是让模型负责适合推理的部分,让代码负责适合约束的部分。通过结构化工具、显式状态机、分级记忆、最小权限、人工审批和独立验证,可以把一个只能演示的聊天机器人,逐步建设成可审计、可回放、可维护的工作系统。

在 Agent 板块实践时,建议先从一个只读、范围明确的本地任务开始,例如分析演示 APK 的 Manifest 和本地测试日志;确认工具契约、状态流转和安全边界稳定后,再逐步增加动态分析、报告生成和团队协作能力。每增加一个工具,都要同步增加权限定义、失败处理、审计字段和评测用例。这样做虽然前期稍慢,但能显著降低后续上线时的不可控风险。
 
Liked By 1 member :
顶部