Files
aiagent/test_tool_calling_visualization.py
2026-03-06 22:31:41 +08:00

369 lines
12 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
测试工具调用可视化功能
创建一个简单的Agent使用工具调用然后查看执行详情
"""
import requests
import json
import time
import sys
BASE_URL = "http://localhost:8037"
def print_section(title):
print("\n" + "=" * 80)
print(f" {title}")
print("=" * 80)
def print_info(message):
print(f" {message}")
def print_success(message):
print(f"{message}")
def print_error(message):
print(f"{message}")
def login():
"""用户登录"""
print_section("1. 用户登录")
login_data = {"username": "admin", "password": "123456"}
try:
response = requests.post(f"{BASE_URL}/api/v1/auth/login", data=login_data)
if response.status_code != 200:
print_error(f"登录失败: {response.status_code}")
return None
token = response.json().get("access_token")
if not token:
print_error("登录失败: 未获取到token")
return None
print_success(f"登录成功")
return {"Authorization": f"Bearer {token}"}
except Exception as e:
print_error(f"登录异常: {str(e)}")
return None
def create_test_workflow(headers):
"""创建测试工作流(使用工具调用)"""
print_section("2. 创建测试工作流")
workflow_data = {
"name": "工具调用可视化测试工作流",
"description": "用于测试工具调用可视化功能",
"nodes": [
{
"id": "start",
"type": "start",
"position": {"x": 100, "y": 200},
"data": {"label": "开始"}
},
{
"id": "llm-with-tools",
"type": "llm",
"position": {"x": 400, "y": 200},
"data": {
"label": "工具调用测试",
"provider": "deepseek",
"model": "deepseek-chat",
"temperature": 0.7,
"max_tokens": 1000,
"enable_tools": True,
"selected_tools": ["http_request", "datetime", "math_calculate"],
"prompt": """用户请求:{{input.query}}
请根据用户需求,选择合适的工具执行任务。可以使用以下工具:
- http_request: 发送HTTP请求
- datetime: 获取当前时间
- math_calculate: 执行数学计算
请分析用户需求,调用合适的工具,然后基于工具返回的结果生成回复。"""
}
},
{
"id": "end",
"type": "end",
"position": {"x": 700, "y": 200},
"data": {"label": "结束"}
}
],
"edges": [
{
"id": "e1",
"source": "start",
"target": "llm-with-tools",
"sourceHandle": "right",
"targetHandle": "left"
},
{
"id": "e2",
"source": "llm-with-tools",
"target": "end",
"sourceHandle": "right",
"targetHandle": "left"
}
]
}
try:
response = requests.post(
f"{BASE_URL}/api/v1/workflows",
headers=headers,
json=workflow_data
)
if response.status_code not in [200, 201]:
print_error(f"创建工作流失败: {response.status_code}")
print(f"响应: {response.text}")
return None
workflow = response.json()
print_success(f"工作流创建成功: {workflow.get('id')}")
return workflow
except Exception as e:
print_error(f"创建工作流异常: {str(e)}")
return None
def execute_workflow(headers, workflow_id, input_data):
"""执行工作流"""
print_section("3. 执行工作流")
execution_data = {
"workflow_id": workflow_id,
"input_data": input_data
}
try:
response = requests.post(
f"{BASE_URL}/api/v1/executions",
headers=headers,
json=execution_data
)
if response.status_code not in [200, 201]:
print_error(f"执行工作流失败: {response.status_code}")
print(f"响应: {response.text}")
return None
execution = response.json()
execution_id = execution.get("id")
print_success(f"执行已创建: {execution_id}")
return execution_id
except Exception as e:
print_error(f"执行工作流异常: {str(e)}")
return None
def wait_for_completion(headers, execution_id, timeout=60):
"""等待执行完成"""
print_section("4. 等待执行完成")
start_time = time.time()
while time.time() - start_time < timeout:
try:
response = requests.get(
f"{BASE_URL}/api/v1/executions/{execution_id}",
headers=headers
)
if response.status_code != 200:
print_error(f"获取执行状态失败: {response.status_code}")
return None
execution = response.json()
status = execution.get("status")
print_info(f"执行状态: {status}")
if status in ["completed", "failed"]:
print_success(f"执行完成,状态: {status}")
return execution
time.sleep(2)
except Exception as e:
print_error(f"获取执行状态异常: {str(e)}")
return None
print_error("执行超时")
return None
def get_execution_logs(headers, execution_id):
"""获取执行日志"""
print_section("5. 获取执行日志(包含工具调用信息)")
try:
response = requests.get(
f"{BASE_URL}/api/v1/execution-logs/executions/{execution_id}",
headers=headers,
params={"limit": 100}
)
if response.status_code != 200:
print_error(f"获取执行日志失败: {response.status_code}")
print(f"响应: {response.text}")
return None
logs = response.json()
print_success(f"获取到 {len(logs)} 条日志")
# 查找工具调用相关的日志
tool_call_logs = []
for log in logs:
if not log:
continue
data = log.get("data") or {}
if isinstance(data, str):
try:
data = json.loads(data)
except:
data = {}
if data.get("tool_name") or "工具" in log.get("message", ""):
tool_call_logs.append(log)
if tool_call_logs:
print_success(f"找到 {len(tool_call_logs)} 条工具调用日志")
print("\n工具调用日志详情:")
for i, log in enumerate(tool_call_logs, 1):
print(f"\n{i}. {log.get('message')}")
print(f" 时间: {log.get('timestamp')}")
print(f" 节点: {log.get('node_id')}")
data = log.get("data", {})
if data.get("tool_name"):
print(f" 工具名称: {data.get('tool_name')}")
print(f" 状态: {data.get('status')}")
if data.get("tool_args"):
print(f" 参数: {json.dumps(data.get('tool_args'), ensure_ascii=False, indent=2)}")
if data.get("duration"):
print(f" 耗时: {data.get('duration')}ms")
else:
print_info("未找到工具调用日志")
return logs
except Exception as e:
print_error(f"获取执行日志异常: {str(e)}")
return None
def get_node_execution_data(headers, execution_id, node_id):
"""获取节点执行数据"""
print_section(f"6. 获取节点执行数据 (节点: {node_id})")
try:
response = requests.get(
f"{BASE_URL}/api/v1/execution-logs/executions/{execution_id}",
headers=headers,
params={"node_id": node_id, "limit": 100}
)
if response.status_code != 200:
print_error(f"获取节点执行数据失败: {response.status_code}")
return None
logs = response.json()
print_success(f"获取到 {len(logs)} 条节点日志")
# 显示工具调用信息
tool_calls = []
for log in logs:
if not log:
continue
data = log.get("data") or {}
if isinstance(data, str):
try:
data = json.loads(data)
except:
data = {}
if data.get("tool_name"):
tool_calls.append({
"tool_name": data.get("tool_name"),
"status": data.get("status"),
"args": data.get("tool_args"),
"result": data.get("tool_result"),
"duration": data.get("duration"),
"timestamp": log.get("timestamp")
})
if tool_calls:
print_success(f"找到 {len(tool_calls)} 个工具调用")
for i, call in enumerate(tool_calls, 1):
print(f"\n工具调用 {i}:")
print(f" 工具: {call['tool_name']}")
print(f" 状态: {call['status']}")
print(f" 参数: {json.dumps(call['args'], ensure_ascii=False, indent=2) if call['args'] else ''}")
if call.get('result'):
result_preview = call['result'][:200] if len(call['result']) > 200 else call['result']
print(f" 结果预览: {result_preview}...")
print(f" 耗时: {call.get('duration', 'N/A')}ms")
print(f" 时间: {call['timestamp']}")
else:
print_info("该节点没有工具调用")
return logs
except Exception as e:
print_error(f"获取节点执行数据异常: {str(e)}")
return None
def main():
"""主函数"""
print_section("工具调用可视化功能测试")
# 1. 登录
headers = login()
if not headers:
return
# 2. 创建测试工作流
workflow = create_test_workflow(headers)
if not workflow:
return
workflow_id = workflow.get("id")
# 3. 执行工作流(使用不同的测试用例)
test_cases = [
{
"name": "测试HTTP请求工具",
"input": {"query": "请查询 https://api.github.com/users/octocat 的信息"}
},
{
"name": "测试时间工具",
"input": {"query": "现在是什么时间?"}
},
{
"name": "测试数学计算工具",
"input": {"query": "计算 123 * 456 的结果"}
}
]
for i, test_case in enumerate(test_cases, 1):
print_section(f"测试用例 {i}: {test_case['name']}")
# 执行工作流
execution_id = execute_workflow(headers, workflow_id, test_case["input"])
if not execution_id:
continue
# 等待完成
execution = wait_for_completion(headers, execution_id)
if not execution:
continue
# 获取执行日志
logs = get_execution_logs(headers, execution_id)
# 获取节点执行数据
node_logs = get_node_execution_data(headers, execution_id, "llm-with-tools")
print_success(f"测试用例 {i} 完成")
print("\n" + "-" * 80)
print_section("测试完成")
print_success("所有测试用例执行完成!")
print_info("请在前端查看执行详情,验证工具调用可视化功能:")
print_info(f"1. 打开执行详情页面: http://localhost:8038/executions/{execution_id}")
print_info("2. 点击节点查看节点执行详情")
print_info("3. 检查工具调用可视化是否正确显示")
if __name__ == "__main__":
main()