全链路验证测试系统:一个针对智能代理(Agent)系统全链路能力的自动化验证脚本

一、系统概述与架构设计

一个针对智能代理(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_agentweb_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

算法逻辑

  1. 嵌套字段校验 :针对 call_data_agent等特殊字段,采用 result.get(key, {}).get("decision")的深度提取,适应LLM输出的多层JSON结构。

  2. 差异定位 :通过对比实际值与期望值,自动生成 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": "..."
}

验证逻辑

  1. 存在性检查:日志总数必须大于0

  2. 决策覆盖:同时存在ALLOW和DENY记录,证明系统既允许合法操作也拒绝非法操作

  3. 风险字段 :至少一条日志包含 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 安全测试方法论

脚本融合了多种安全测试技术:

  1. Positive Testing:验证合法操作被允许(Test 3, 5)

  2. Negative Testing:验证非法操作被拒绝(Test 4)

  3. Boundary Testing:验证权限边界(Test 6)

3.3 可观测性设计

通过标准化输出格式:

复制代码
[PASS] DocAgent不持有data_agent属性
  [FAIL] WebAgent越权被拦截(403): 连接失败

既适合人工阅读,也便于被Jenkins、GitLab CI等工具解析。


四、数据流全景分析

以"生成包含都市风云网络热度的分析报告"为例,完整数据流如下:

  1. 输入层 :自然语言指令进入 IntentRecognizer

  2. LLM处理 :模型识别需要 web:search能力,输出结构化指令

  3. 权限校验 :StaticPolicy检查doc-assistant是否有 delegate:*权限(允许委托)

  4. 服务调用:DocAgent通过HTTP POST请求调用WebAgent(:8003)

  5. 二次校验 :WebAgent再次检查自身是否有权限执行 web:search

  6. 审计记录:操作被记录到审计日志,包含风险评估结果

  7. 响应返回:结果沿调用链返回给最终用户

整个过程经历了三次权限检查(DocAgent委托权限、WebAgent自身权限、操作风险评估),体现了纵深防御理念。


五、潜在改进方向

虽然脚本已覆盖主要测试场景,但仍可增强:

  1. 混沌工程:增加网络延迟、服务宕机等异常场景测试

  2. 性能基准:添加响应时间、吞吐量等指标验证

  3. 模糊测试:自动生成随机指令测试LLM鲁棒性

  4. 契约测试:验证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)
相关推荐
有味道的男人1 小时前
电商效率翻倍:京东全量商品信息抓取
python
原来是猿2 小时前
博客系统自动化测试实战总结
python
YueJoy.AI2 小时前
创业团队如何进行绩效管理
人工智能·ai·语言模型
小江的记录本2 小时前
【JVM虚拟机】JVM调优:常用JVM参数、调优核心指标、OOM排查、GC日志分析、Arthas工具使用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
大数据魔法师2 小时前
Streamlit(十三)- API 参考文档(六)- 媒体展示组件
python·web
沉默王二2 小时前
腾讯面试官问CLAUDE.md维护,我只说了两个词,他当场愣住了!!
agent·ai编程·claude
爱写代码的倒霉蛋2 小时前
Hello-Agents的第一个练习-5分钟实现一个智能体(实现详解)
python
John_ToDebug2 小时前
CLAUDE.md 与 Skills 的区别:一张表彻底分清
人工智能·经验分享·ai
金銀銅鐵2 小时前
[Java] 用图形化界面演示 iadd, isub, iconst_<i> 指令的效果
java·后端·python