一、系统概述与架构设计
一个针对智能代理(Agent)系统全链路能力的自动化验证脚本,其核心目标是确保 LLM意图识别准确性 、HTTP分布式通信可靠性 和 安全分权机制有效性 三大关键特性。该脚本采用模块化测试设计,覆盖从底层权限控制到上层业务逻辑的全流程,是系统质量保障的核心工具。
从架构上看,测试脚本模拟了真实生产环境的调用链:用户输入 → LLM意图解析 → Agent服务调度 → 权限校验 → 审计日志留存。这种端到端(End-to-End)的测试方式能够暴露单一模块测试无法发现的集成问题,例如跨服务权限传递漏洞、意图识别歧义导致的资源越权等。
二、核心功能模块详解
2.1 测试框架基础层
脚本首先构建了轻量级测试基础设施,通过全局计数器 PASS/FAIL和标准化 test()函数实现测试结果管理。test(name, passed, detail)函数封装了断言逻辑与输出格式化,支持动态添加错误详情,为后续CI/CD流水线集成提供了结构化输出基础。值得注意的是,脚本通过 sys.path.insert(0, ...)动态调整Python路径,确保能正确导入项目内的 src模块,解决了相对路径导入的常见问题。
2.2 权限隔离验证(Test 1)
测试目标:验证DocAgent遵循最小权限原则,不持有非授权资源句柄。
实现逻辑:
from src.agents.doc_agent import DocAgent
da = DocAgent()
test("DocAgent不持有data_agent属性", not hasattr(da, 'data_agent'))
通过反射机制(hasattr)检查DocAgent实例是否包含 data_agent、web_agent等敏感属性。这一测试直指分布式系统的核心安全准则:Agent间应通过接口通信而非直接持有依赖对象。若DocAgent持有DataAgent实例,将绕过HTTP层的权限校验,导致静态策略引擎失效。
安全意义:该测试防止了"权限穿透"风险。在微服务架构中,服务A直接调用服务B的内部方法会绕过B的访问控制,而通过HTTP接口调用则必须经过B的权限过滤器。
2.3 LLM意图识别验证(Test 2)
测试目标:验证大语言模型对复杂用户指令的解析准确性。
数据结构设计:
intents = [
("生成番茄小说数据分析报告",
{"task_type": "generate_report", "call_data_agent": "yes"}),
# ...更多测试用例
]
每条用例包含自然语言输入 和预期结构化输出 。脚本通过 IntentRecognizer.analyze()将自然语言转化为机器可读的JSON指令,例如识别"结合外部检索"触发 call_web_agent: yes。
算法逻辑:
-
嵌套字段校验 :针对
call_data_agent等特殊字段,采用result.get(key, {}).get("decision")的深度提取,适应LLM输出的多层JSON结构。 -
差异定位 :通过对比实际值与期望值,自动生成
ISSUE:前缀的错误报告,精确定位哪个字段不匹配(如call_web_agent=no(expected=yes))。
测试覆盖场景:
-
单资源请求:仅需要数据分析的报告生成
-
多资源协同:同时需要内部数据和外部网络检索
-
敏感操作检测 :"越权访问内部数据"应被正确分类为
test_unauthorized_access
2.4 Agent服务健康检查(Test 3)
测试目标:验证分布式Agent服务的可达性与健康状态。
实现机制:
r = requests.get(f"http://localhost:{port}/health", timeout=3)
ok = r.status_code == 200 and r.json().get("status") == "healthy"
通过HTTP GET请求探测各Agent的 /health端点,要求返回状态码200且JSON中包含 "status": "healthy"。超时设置为3秒,防止因单个服务故障阻塞整个测试流程。
端口分配策略:
| Agent类型 | 端口 | 职责 |
|---|---|---|
| DataAgent | 8002 | 处理飞书表格等内部数据 |
| WebAgent | 8003 | 处理外部网络搜索 |
这种设计验证了系统的服务发现能力------DocAgent需要知道去哪里调用下游服务。
2.5 越权拦截验证(Test 4)
测试目标:验证WebAgent能拒绝未授权的内部数据访问请求。
攻击模拟 :脚本尝试直接向WebAgent发送可能触发越权的请求(代码中URL为空,实际测试需补全),预期返回403状态码。这是典型的渗透测试手法,通过模拟攻击者行为验证防御机制。
安全价值:防止"横向越权"(Horizontal Privilege Escalation),即WebAgent不应具备访问飞书表格的权限,即使请求格式正确也应被拒绝。
2.6 审计日志完整性验证(Test 5)
测试目标:确保所有关键操作均被记录且包含足够 forensic(取证)信息。
审计数据结构:
{
"decision": "ALLOW" | "DENY",
"risk_assessment": {...}, # 风险评估详情
"timestamp": "...",
"agent_id": "..."
}
验证逻辑:
-
存在性检查:日志总数必须大于0
-
决策覆盖:同时存在ALLOW和DENY记录,证明系统既允许合法操作也拒绝非法操作
-
风险字段 :至少一条日志包含
risk_assessment,表明系统进行了动态风险分析
合规意义:满足GDPR、等保2.0等法规对操作留痕的要求,确保发生安全事件时可追溯。
2.7 权限边界验证(Test 6)
测试目标:验证静态策略引擎(StaticPolicy)的RBAC(基于角色的访问控制)实现。
策略配置:
("doc-assistant", ["doc:*", "delegate:*"], ["feishu:bitable", "web:search"])
表示doc-assistant角色拥有文档所有权限和委托权限,但禁止直接访问飞书表格和网络搜索。
算法实现:
-
get_agent_capabilities(agent_id):获取Agent的完整权限集 -
check_static_capability(agent_id, permission):细粒度权限检查
通配符支持 :doc:*表示doc域下的所有操作,这种ACL(访问控制列表)设计兼顾灵活性与安全性。
三、关键技术亮点
3.1 防御性编程实践
-
编码安全 :
sys.stdout = io.TextIOWrapper(...)显式设置UTF-8编码,避免中文环境下的乱码问题 -
异常隔离:每个测试模块独立try-except,单个测试失败不影响整体执行
-
超时控制:所有网络请求设置timeout,防止资源耗尽
3.2 安全测试方法论
脚本融合了多种安全测试技术:
-
Positive Testing:验证合法操作被允许(Test 3, 5)
-
Negative Testing:验证非法操作被拒绝(Test 4)
-
Boundary Testing:验证权限边界(Test 6)
3.3 可观测性设计
通过标准化输出格式:
[PASS] DocAgent不持有data_agent属性
[FAIL] WebAgent越权被拦截(403): 连接失败
既适合人工阅读,也便于被Jenkins、GitLab CI等工具解析。
四、数据流全景分析
以"生成包含都市风云网络热度的分析报告"为例,完整数据流如下:
-
输入层 :自然语言指令进入
IntentRecognizer -
LLM处理 :模型识别需要
web:search能力,输出结构化指令 -
权限校验 :StaticPolicy检查doc-assistant是否有
delegate:*权限(允许委托) -
服务调用:DocAgent通过HTTP POST请求调用WebAgent(:8003)
-
二次校验 :WebAgent再次检查自身是否有权限执行
web:search -
审计记录:操作被记录到审计日志,包含风险评估结果
-
响应返回:结果沿调用链返回给最终用户
整个过程经历了三次权限检查(DocAgent委托权限、WebAgent自身权限、操作风险评估),体现了纵深防御理念。
五、潜在改进方向
虽然脚本已覆盖主要测试场景,但仍可增强:
-
混沌工程:增加网络延迟、服务宕机等异常场景测试
-
性能基准:添加响应时间、吞吐量等指标验证
-
模糊测试:自动生成随机指令测试LLM鲁棒性
-
契约测试:验证Agent间API接口的兼容性
六、总结
不仅是一个测试脚本,更是系统安全架构的"活文档"。它通过代码形式定义了:
-
什么是合法的Agent行为
-
如何防御常见的越权攻击
-
系统应满足的可靠性指标
这种设计将安全策略从纸面规范转化为可执行代码,确保了系统在持续迭代中不会偏离安全基线。对于开发团队而言,每次修改代码后运行此脚本,相当于对系统进行"安全体检",是保障分布式AI系统可靠性的关键实践。
源代码
#!/usr/bin/env python3
"""全链路验证测试 - LLM意图识别 + HTTP协议 + 安全分权"""
import sys, os; sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import json, time, requests, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') if hasattr(sys.stdout, 'buffer') else sys.stdout
PASS = 0
FAIL = 0
def test(name, passed, detail=""):
global PASS, FAIL
mark = "PASS" if passed else "FAIL"
print(f" [{mark}] {name}" + (f": {detail}" if detail else ""))
if passed:
PASS += 1
else:
FAIL += 1
print("=" * 70)
print(" 全链路验证: LLM意图识别 + HTTP Agent通信 + 安全分权")
print("=" * 70)
# ===== Test 1: DocAgent 不持有本地DataAgent/WebAgent实例 =====
print("\n[1] 权限隔离验证")
from src.agents.doc_agent import DocAgent
da = DocAgent()
test("DocAgent不持有data_agent属性", not hasattr(da, 'data_agent'))
test("DocAgent不持有web_agent属性", not hasattr(da, 'web_agent'))
test("DocAgent不持有feishu_client属性", not hasattr(da, 'feishu_client'))
# ===== Test 2: IntentRecognizer 功能验证 =====
print("\n[2] LLM意图识别验证")
from src.llm.intent_recognizer import IntentRecognizer
ir = IntentRecognizer()
intents = [
("生成番茄小说数据分析报告",
{"task_type": "generate_report", "call_data_agent": "yes"}),
("读取表格数据,同时结合外部检索到的知识,生成番茄小说数据分析报告",
{"task_type": "generate_report", "call_data_agent": "yes", "call_web_agent": "yes"}),
("生成包含都市风云网络热度的分析报告",
{"task_type": "generate_report", "call_web_agent": "yes"}),
("测试越权访问内部数据",
{"task_type": "test_unauthorized_access"}),
]
all_ok = True
for instr, expected in intents:
result = ir.analyze(instr)
checks = []
issue = ""
for key, exp_val in expected.items():
if key in ("call_data_agent", "call_web_agent"):
actual = result.get(key, {}).get("decision", "no")
if actual != exp_val:
issue += f" {key}={actual}(expected={exp_val})"
all_ok = False
elif result.get(key) != exp_val:
issue += f" {key}={result.get(key)}(expected={exp_val})"
all_ok = False
status = "OK" if not issue else f"ISSUE:{issue}"
print(f" {status} | {instr[:50]}")
test("IntentRecognizer: 4条指令全部正确", all_ok)
# ===== Test 3: Agent服务健康检查 =====
print("\n[3] Agent服务可用性验证")
for name, port in [("DataAgent", 8002), ("WebAgent", 8003)]:
try:
r = requests.get(f"http://localhost:{port}/health", timeout=3)
ok = r.status_code == 200 and r.json().get("status") == "healthy"
test(f"{name}(:{port})在线", ok, str(r.json()) if ok else "不可达")
except Exception:
test(f"{name}(:{port})在线", False, "连接失败 - 请先启动Agent服务")
# ===== Test 4: 越权拦截验证 =====
print("\n[4] 越权拦截验证")
try:
r = requests.post("", timeout=10)
data = r.json()
test("WebAgent越权被拦截(403)", not data.get("success") and data.get("code") == 403,
f"code={data.get('code')}, error={data.get('error', '')[:50]}")
except Exception as e:
test("WebAgent越权测试", False, str(e)[:80])
# ===== Test 5: 审计日志完整性 =====
print("\n[5] 审计日志验证")
from src.audit_service.query import AuditQuery
aq = AuditQuery()
logs = aq._load_all_logs()
test("审计日志已生成", len(logs) > 0, f"共{len(logs)}条")
allow = sum(1 for l in logs if l.get("decision") == "ALLOW")
deny = sum(1 for l in logs if l.get("decision") == "DENY")
test("审计日志含ALLOW+DENY", allow > 0 and deny > 0, f"ALLOW={allow}, DENY={deny}")
has_risk = any("risk_assessment" in l for l in logs)
test("审计日志含风险评估", has_risk)
# ===== Test 6: 权限边界 =====
print("\n[6] 权限边界验证")
from src.policy_engine.static_policy import StaticPolicy
sp = StaticPolicy()
for agent_id, allowed, forbidden in [
("doc-assistant", ["doc:*", "delegate:*"], ["feishu:bitable", "web:search"]),
("data-agent", ["feishu:bitable"], ["web:search", "delegate:*"]),
("web-agent", ["web:search"], ["feishu:bitable", "delegate:*"]),
]:
caps = sp.get_agent_capabilities(agent_id)
for a in allowed:
ok = sp.check_static_capability(agent_id, a)
test(f"{agent_id}有{a}权限", ok)
for f in forbidden:
ok = not sp.check_static_capability(agent_id, f)
test(f"{agent_id}无{f}权限", ok)
# ===== Summary =====
print("\n" + "=" * 70)
print(f" 测试完成: 通过={PASS}, 失败={FAIL}, 总计={PASS+FAIL}")
if FAIL == 0:
print(" ALL TESTS PASSED")
else:
print(f" {FAIL} TESTS FAILED")
print("=" * 70)