android应用

This commit is contained in:
rjb
2026-01-20 11:03:55 +08:00
parent f6568f252a
commit d59f015362
16 changed files with 1754 additions and 17 deletions

283
Agent使用说明.md Normal file
View File

@@ -0,0 +1,283 @@
# Agent使用说明
## 📋 概述
Agent部署后状态变为"已发布"published或"运行中"running就可以被使用了。本文档介绍如何使用已发布的Agent。
## 🎯 使用方式
### 方式1通过前端界面使用推荐
#### 步骤1进入Agent设计器
1. 在Agent管理页面找到已发布的Agent
2. 点击 **"使用"** 按钮只有已发布或运行中的Agent才显示此按钮
3. 系统会跳转到Agent设计器页面
#### 步骤2在聊天界面使用
- Agent设计器页面右侧有一个聊天预览面板AgentChatPreview
- 在输入框中输入您的问题或需求
- 点击发送按钮或按 Enter 键发送
- Agent会执行工作流并返回结果
#### 功能特性:
- ✅ 实时对话:支持多轮对话
- ✅ 执行动画:左侧工作流会显示节点执行动画
- ✅ 自动滚动:新消息自动滚动到底部
- ✅ 清空对话:可以随时清空对话历史
### 方式2通过API调用
#### API端点
```
POST /api/v1/executions
```
#### 请求格式
```json
{
"agent_id": "agent-id",
"input_data": {
"query": "用户输入的问题",
"USER_INPUT": "用户输入的问题"
}
}
```
#### 响应格式
```json
{
"id": "execution-id",
"agent_id": "agent-id",
"status": "pending",
"input_data": {...},
"created_at": "2024-01-19T12:00:00"
}
```
#### 获取执行结果
```
GET /api/v1/executions/{execution_id}
```
#### 轮询执行状态
```
GET /api/v1/executions/{execution_id}/status
```
### 方式3使用测试工具
使用我们提供的测试工具 `test_workflow_tool.py`
```bash
# 通过Agent名称和用户输入测试
python3 test_workflow_tool.py -a "Agent名称" -i "用户输入内容"
# 交互式输入
python3 test_workflow_tool.py
```
## 📝 使用示例
### 示例1前端界面使用
1. **打开Agent管理页面**
- 访问 `/agents` 路径
- 找到状态为"已发布"的Agent
2. **点击"使用"按钮**
- 系统跳转到Agent设计器页面
- 右侧显示聊天界面
3. **发送消息**
- 在输入框输入:"生成一个导出androidlog的脚本"
- 点击发送或按Enter键
4. **查看结果**
- 左侧工作流显示执行动画
- 右侧聊天界面显示Agent的回复
### 示例2API调用
```bash
# 1. 登录获取token
curl -X POST http://localhost:8037/api/v1/auth/login \
-d "username=admin&password=123456"
# 2. 执行Agent
curl -X POST http://localhost:8037/api/v1/executions \
-H "Authorization: Bearer <your_token>" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent-id",
"input_data": {
"query": "生成一个导出androidlog的脚本",
"USER_INPUT": "生成一个导出androidlog的脚本"
}
}'
# 3. 获取执行结果使用返回的execution_id
curl -X GET http://localhost:8037/api/v1/executions/{execution_id} \
-H "Authorization: Bearer <your_token>"
```
### 示例3Python脚本调用
```python
import requests
# 登录
response = requests.post(
"http://localhost:8037/api/v1/auth/login",
data={"username": "admin", "password": "123456"}
)
token = response.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
# 执行Agent
response = requests.post(
"http://localhost:8037/api/v1/executions",
headers=headers,
json={
"agent_id": "agent-id",
"input_data": {
"query": "用户问题",
"USER_INPUT": "用户问题"
}
}
)
execution_id = response.json()["id"]
# 轮询获取结果
import time
while True:
response = requests.get(
f"http://localhost:8037/api/v1/executions/{execution_id}",
headers=headers
)
execution = response.json()
if execution["status"] == "completed":
print("结果:", execution["output_data"])
break
elif execution["status"] == "failed":
print("执行失败:", execution.get("error_message"))
break
time.sleep(2)
```
## 🔑 权限说明
### 已发布Agent的使用权限
1. **所有者**可以随时使用自己的Agent
2. **其他用户**只有已发布published或运行中running状态的Agent才能被其他用户使用
3. **草稿状态**:只有所有者可以测试,其他用户无法使用
### API权限检查
从代码中可以看到:
```python
# 只有已发布的Agent可以执行或者所有者可以测试
if agent.status not in ["published", "running"] and agent.user_id != current_user.id:
raise HTTPException(status_code=403, detail="Agent未发布或无权执行")
```
## 🎨 前端界面说明
### Agent设计器页面布局
```
┌─────────────────────────────────────────────────┐
│ Agent编排设计器 [测试运行] [发布] [返回] │
├──────────────────────┬──────────────────────────┤
│ │ │
│ 工作流编辑器 │ Agent聊天预览 │
│ (左侧) │ (右侧) │
│ │ │
│ - 节点可视化 │ - 对话界面 │
│ - 执行动画 │ - 消息历史 │
│ - 节点配置 │ - 输入框 │
│ │ - 实时响应 │
│ │ │
└──────────────────────┴──────────────────────────┘
```
### 聊天界面功能
1. **消息显示**
- 用户消息:右侧显示,蓝色背景
- Agent回复左侧显示白色背景
- 时间戳:每条消息显示发送时间
2. **输入功能**
- 文本输入:支持多行文本
- Enter发送按Enter键发送消息
- Shift+Enter换行
- 发送按钮:点击发送
3. **其他功能**
- 清空对话:清除所有消息历史
- 加载状态:显示"正在思考..."
- 执行动画:左侧工作流实时显示节点执行状态
## 🔧 常见问题
### Q1: 为什么看不到"使用"按钮?
**A:** "使用"按钮只对状态为"已发布"published或"运行中"running的Agent显示。如果Agent是"草稿"draft状态需要先点击"部署"按钮发布。
### Q2: 如何知道Agent是否可用
**A:** 查看Agent列表中的"状态"列:
-**已发布**published可以使用
-**运行中**running可以使用
- ⚠️ **草稿**draft只有所有者可以测试
- ⚠️ **已停止**stopped需要重新部署
### Q3: Agent执行失败怎么办
**A:**
1. 检查Agent的工作流配置是否正确
2. 查看执行记录中的错误信息
3. 检查LLM节点的API密钥是否配置
4. 查看后端日志获取详细错误信息
### Q4: 如何查看Agent的执行历史
**A:**
1. 访问"执行管理"页面(`/executions`
2. 筛选Agent相关的执行记录
3. 点击执行记录查看详细信息
### Q5: 可以同时使用多个Agent吗
**A:** 可以。每个Agent都是独立的可以同时打开多个Agent设计器页面使用不同的Agent。
## 📚 相关文档
- [Agent管理功能使用说明](./Agent管理功能使用说明.md)
- [工作流调用测试总结](./工作流调用测试总结.txt)
- [API文档](./backend/API_DOCUMENTATION.md)
## 🎯 快速开始
1. **确保Agent已发布**
- 在Agent列表中找到要使用的Agent
- 确认状态为"已发布"或"运行中"
2. **点击"使用"按钮**
- 跳转到Agent设计器页面
3. **开始对话**
- 在右侧聊天界面输入问题
- 点击发送或按Enter键
- 等待Agent执行并返回结果
4. **查看结果**
- 在聊天界面查看Agent的回复
- 在左侧工作流查看执行过程
---
**最后更新**2026-01-19
**适用版本**v1.0+

180
androidExample/README.md Normal file
View File

@@ -0,0 +1,180 @@
# Android Agent调用示例
这是一个Android示例项目演示如何调用情感分析Agent。
## 📋 项目结构
```
androidExample/
├── app/
│ ├── src/
│ │ └── main/
│ │ ├── java/com/example/agentclient/
│ │ │ ├── MainActivity.kt
│ │ │ ├── AgentService.kt
│ │ │ ├── models/
│ │ │ │ ├── AgentRequest.kt
│ │ │ │ ├── AgentResponse.kt
│ │ │ │ └── ExecutionResponse.kt
│ │ │ └── utils/
│ │ │ └── ApiClient.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ └── values/
│ │ └── strings.xml
│ └── build.gradle.kts
├── build.gradle.kts
├── settings.gradle.kts
└── README.md
```
## 🚀 快速开始
### 1. 配置API地址
`app/src/main/java/com/example/agentclient/utils/ApiClient.kt` 中修改:
```kotlin
private const val BASE_URL = "http://your-server-ip:8037"
```
### 2. 配置Agent ID
`MainActivity.kt` 中修改:
```kotlin
private val AGENT_ID = "your-agent-id" // 情感分析Agent的ID
```
### 3. 运行项目
1. 使用Android Studio打开项目
2. 同步Gradle依赖
3. 运行到Android设备或模拟器
## 📱 功能特性
- ✅ 用户登录
- ✅ 调用Agent API
- ✅ 实时显示执行状态
- ✅ 显示Agent回复
- ✅ 错误处理
## 🔧 依赖库
- Retrofit2网络请求
- OkHttpHTTP客户端
- GsonJSON解析
- Coroutines异步处理
## 📝 使用说明
1. **登录**
- 应用启动后会自动登录(使用配置的用户名和密码)
- 登录成功后可以开始使用Agent
2. **发送消息**
- 在输入框中输入文本
- 点击"发送"按钮
- 等待Agent处理并返回结果
3. **查看结果**
- Agent的回复会显示在消息列表中
- 执行状态会实时更新
## 🔑 API说明
### 登录API
```
POST /api/v1/auth/login
Content-Type: application/x-www-form-urlencoded
username=admin&password=123456
```
### 执行Agent API
```
POST /api/v1/executions
Authorization: Bearer <token>
Content-Type: application/json
{
"agent_id": "agent-id",
"input_data": {
"query": "用户输入",
"USER_INPUT": "用户输入"
}
}
```
### 获取执行状态API
```
GET /api/v1/executions/{execution_id}/status
Authorization: Bearer <token>
```
### 获取执行结果API
```
GET /api/v1/executions/{execution_id}
Authorization: Bearer <token>
```
## 🎯 示例调用情感分析Agent
```kotlin
// 1. 登录获取token
val token = agentService.login("admin", "123456")
// 2. 执行Agent
val execution = agentService.executeAgent(
agentId = "sentiment-analysis-agent-id",
userInput = "这个产品真的很棒!"
)
// 3. 轮询获取结果
while (true) {
val status = agentService.getExecutionStatus(execution.id)
if (status.status == "completed") {
val result = agentService.getExecutionResult(execution.id)
println("情感分析结果: ${result.output_data}")
break
}
delay(1000) // 等待1秒
}
```
## 📦 构建要求
- Android Studio Hedgehog | 2023.1.1 或更高版本
- JDK 17 或更高版本
- Android SDK API 24 或更高版本
- Gradle 8.0 或更高版本
## 🔒 安全注意事项
1. **不要硬编码密码**:生产环境应该使用安全的认证方式
2. **使用HTTPS**生产环境必须使用HTTPS
3. **Token管理**妥善保管和刷新Token
4. **网络安全配置**Android 9+需要配置网络安全策略
## 🐛 故障排除
### 问题1网络连接失败
- 检查API地址是否正确
- 检查设备网络连接
- 检查服务器是否运行
### 问题2登录失败
- 检查用户名和密码是否正确
- 检查服务器认证服务是否正常
### 问题3Agent执行失败
- 检查Agent ID是否正确
- 检查Agent是否已发布
- 查看服务器日志获取详细错误信息
## 📚 相关文档
- [Agent使用说明](../Agent使用说明.md)
- [API文档](../backend/API_DOCUMENTATION.md)

View File

@@ -0,0 +1,70 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.example.agentclient"
compileSdk = 34
defaultConfig {
applicationId = "com.example.agentclient"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
// Android Core
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.activity:activity-ktx:1.8.1")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// Retrofit & OkHttp
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
// Gson
implementation("com.google.code.gson:gson:2.10.1")
// Testing
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View File

@@ -0,0 +1,14 @@
package com.example.agentclient.models
import com.google.gson.annotations.SerializedName
/**
* Agent执行请求模型
*/
data class AgentExecutionRequest(
@SerializedName("agent_id")
val agentId: String,
@SerializedName("input_data")
val inputData: Map<String, Any>
)

View File

@@ -0,0 +1,27 @@
package com.example.agentclient.models
import com.google.gson.annotations.SerializedName
/**
* 执行记录响应模型
*/
data class ExecutionResponse(
val id: String,
@SerializedName("agent_id")
val agentId: String?,
@SerializedName("workflow_id")
val workflowId: String?,
val status: String,
@SerializedName("input_data")
val inputData: Map<String, Any>?,
@SerializedName("output_data")
val outputData: Any?,
@SerializedName("created_at")
val createdAt: String
)

View File

@@ -0,0 +1,14 @@
package com.example.agentclient.models
import com.google.gson.annotations.SerializedName
/**
* Token响应模型
*/
data class TokenResponse(
@SerializedName("access_token")
val accessToken: String,
@SerializedName("token_type")
val tokenType: String = "bearer"
)

View File

@@ -0,0 +1,99 @@
package com.example.agentclient.utils
import com.example.agentclient.models.*
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.*
import java.util.concurrent.TimeUnit
/**
* API客户端配置
*
* 使用说明:
* 1. 修改 BASE_URL 为你的服务器地址
* 2. 确保服务器允许跨域请求CORS
*/
object ApiClient {
// TODO: 修改为你的服务器地址
private const val BASE_URL = "http://101.43.95.130:8037"
// 创建OkHttp客户端
private val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
// 创建Retrofit实例
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
// 创建API服务
val agentService: AgentService = retrofit.create(AgentService::class.java)
}
/**
* Agent API接口
*/
interface AgentService {
/**
* 用户登录
*
* @param username 用户名
* @param password 密码
* @return Token响应
*/
@FormUrlEncoded
@POST("/api/v1/auth/login")
suspend fun login(
@Field("username") username: String,
@Field("password") password: String
): TokenResponse
/**
* 执行Agent
*
* @param request Agent执行请求
* @param token 认证Token
* @return 执行记录
*/
@POST("/api/v1/executions")
suspend fun executeAgent(
@Body request: AgentExecutionRequest,
@Header("Authorization") token: String
): ExecutionResponse
/**
* 获取执行状态
*
* @param executionId 执行ID
* @param token 认证Token
* @return 执行状态
*/
@GET("/api/v1/executions/{execution_id}/status")
suspend fun getExecutionStatus(
@Path("execution_id") executionId: String,
@Header("Authorization") token: String
): ExecutionStatusResponse
/**
* 获取执行结果
*
* @param executionId 执行ID
* @param token 认证Token
* @return 执行详情
*/
@GET("/api/v1/executions/{execution_id}")
suspend fun getExecutionResult(
@Path("execution_id") executionId: String,
@Header("Authorization") token: String
): ExecutionDetailResponse
}

View File

@@ -0,0 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}

View File

@@ -0,0 +1,18 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "AgentClient"
include(":app")

View File

@@ -397,3 +397,112 @@ async def duplicate_agent(
logger.info(f"用户 {current_user.username} 复制了Agent: {original_agent.name} ({agent_id}) -> {new_agent.name} ({new_agent.id})") logger.info(f"用户 {current_user.username} 复制了Agent: {original_agent.name} ({agent_id}) -> {new_agent.name} ({new_agent.id})")
return new_agent return new_agent
@router.get("/{agent_id}/export", status_code=status.HTTP_200_OK)
async def export_agent(
agent_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""导出AgentJSON格式"""
try:
agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not agent:
raise NotFoundError("Agent", agent_id)
# 检查权限read权限
if not check_agent_permission(db, current_user, agent, "read"):
raise HTTPException(status_code=403, detail="无权导出此Agent")
# 验证工作流配置
workflow_config = agent.workflow_config
if not workflow_config:
raise ValidationError("Agent工作流配置为空无法导出")
export_data = {
"id": str(agent.id),
"name": agent.name,
"description": agent.description,
"workflow_config": workflow_config,
"version": agent.version,
"status": agent.status,
"exported_at": datetime.utcnow().isoformat()
}
logger.info(f"用户 {current_user.username} 导出Agent: {agent.name} ({agent_id})")
return export_data
except Exception as e:
logger.error(f"导出Agent失败: {str(e)}", exc_info=True)
raise
@router.post("/import", response_model=AgentResponse, status_code=status.HTTP_201_CREATED)
async def import_agent(
agent_data: Dict[str, Any],
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""导入AgentJSON格式"""
# 提取Agent数据
name = agent_data.get("name", "导入的Agent")
description = agent_data.get("description")
workflow_config = agent_data.get("workflow_config", {})
# 验证工作流配置
if not workflow_config:
raise ValidationError("Agent工作流配置不能为空")
nodes = workflow_config.get("nodes", [])
edges = workflow_config.get("edges", [])
if not nodes or not edges:
raise ValidationError("Agent工作流配置无效缺少节点或边")
# 验证工作流
validation_result = validate_workflow(nodes, edges)
if not validation_result["valid"]:
raise ValidationError(f"导入的Agent工作流验证失败: {', '.join(validation_result['errors'])}")
# 重新生成节点ID避免ID冲突
node_id_mapping = {}
for node in nodes:
old_id = node["id"]
new_id = f"node_{len(node_id_mapping)}_{old_id}"
node_id_mapping[old_id] = new_id
node["id"] = new_id
# 更新边的源节点和目标节点ID
for edge in edges:
if edge.get("source") in node_id_mapping:
edge["source"] = node_id_mapping[edge["source"]]
if edge.get("target") in node_id_mapping:
edge["target"] = node_id_mapping[edge["target"]]
# 检查名称是否已存在
base_name = name
counter = 1
while db.query(Agent).filter(
Agent.name == name,
Agent.user_id == current_user.id
).first():
name = f"{base_name} (导入 {counter})"
counter += 1
# 创建Agent
agent = Agent(
name=name,
description=description,
workflow_config={
"nodes": nodes,
"edges": edges
},
user_id=current_user.id,
status="draft", # 导入的Agent默认为草稿状态
version=1
)
db.add(agent)
db.commit()
db.refresh(agent)
return agent

View File

@@ -1936,11 +1936,29 @@ class WorkflowEngine:
# 如果是条件节点,根据分支结果过滤边 # 如果是条件节点,根据分支结果过滤边
if node.get('type') == 'condition': if node.get('type') == 'condition':
branch = result.get('branch', 'false') branch = result.get('branch', 'false')
logger.info(f"[rjb] 条件节点分支过滤: node_id={next_node_id}, branch={branch}")
# 移除不符合条件的边 # 移除不符合条件的边
active_edges = [ # 只保留1) 不是从条件节点出发的边,或 2) 从条件节点出发且sourceHandle匹配分支的边
edge for edge in active_edges edges_to_remove = []
if not (edge['source'] == next_node_id and edge.get('sourceHandle') != branch) edges_to_keep = []
] for edge in active_edges:
if edge['source'] == next_node_id:
# 这是从条件节点出发的边
edge_handle = edge.get('sourceHandle')
if edge_handle == branch:
# sourceHandle匹配分支保留
edges_to_keep.append(edge)
logger.info(f"[rjb] 保留边: {edge.get('id')} (sourceHandle={edge_handle} == branch={branch})")
else:
# sourceHandle不匹配或为None移除
edges_to_remove.append(edge)
logger.info(f"[rjb] 移除边: {edge.get('id')} (sourceHandle={edge_handle} != branch={branch})")
else:
# 不是从条件节点出发的边,保留
edges_to_keep.append(edge)
active_edges = edges_to_keep
logger.info(f"[rjb] 条件节点过滤后: 保留{len(active_edges)}条边,移除{len(edges_to_remove)}条边")
# 如果是循环节点,跳过循环体的节点(循环体已在节点内部执行) # 如果是循环节点,跳过循环体的节点(循环体已在节点内部执行)
if node.get('type') in ['loop', 'foreach']: if node.get('type') in ['loop', 'foreach']:

View File

@@ -92,7 +92,7 @@
:node-types="customNodeTypes" :node-types="customNodeTypes"
:connection-line-style="{ stroke: '#409eff', strokeWidth: 2.5, strokeDasharray: '5,5' }" :connection-line-style="{ stroke: '#409eff', strokeWidth: 2.5, strokeDasharray: '5,5' }"
:default-edge-options="{ :default-edge-options="{
type: 'smoothstep', type: 'bezier',
animated: true, animated: true,
selectable: true, selectable: true,
deletable: true, deletable: true,
@@ -100,7 +100,7 @@
style: { stroke: '#409eff', strokeWidth: 2.5 }, style: { stroke: '#409eff', strokeWidth: 2.5 },
markerEnd: { type: 'arrowclosed', color: '#409eff', width: 20, height: 20 } markerEnd: { type: 'arrowclosed', color: '#409eff', width: 20, height: 20 }
}" }"
:connection-line-type="'smoothstep'" :connection-line-type="'bezier'"
:edges-focusable="true" :edges-focusable="true"
:nodes-focusable="true" :nodes-focusable="true"
:delete-key-code="'Delete'" :delete-key-code="'Delete'"
@@ -1902,7 +1902,7 @@ const onConnect = (connection: Connection) => {
target: connection.target, target: connection.target,
sourceHandle: connection.sourceHandle || undefined, sourceHandle: connection.sourceHandle || undefined,
targetHandle: connection.targetHandle || undefined, targetHandle: connection.targetHandle || undefined,
type: 'smoothstep', // 使用平滑步进曲线(类似 Dify type: 'bezier', // 使用贝塞尔曲线(平滑曲线
animated: true, animated: true,
selectable: true, selectable: true,
deletable: true, deletable: true,
@@ -2700,7 +2700,7 @@ watch(
selectable: true, selectable: true,
deletable: true, deletable: true,
focusable: true, focusable: true,
type: edge.type || 'smoothstep', type: edge.type || 'bezier',
animated: true, animated: true,
style: { style: {
stroke: '#409eff', stroke: '#409eff',
@@ -2955,7 +2955,7 @@ onMounted(async () => {
selectable: true, selectable: true,
deletable: true, deletable: true,
focusable: true, focusable: true,
type: edge.type || 'smoothstep', type: edge.type || 'bezier',
animated: true, animated: true,
style: { style: {
stroke: '#409eff', stroke: '#409eff',
@@ -3006,7 +3006,7 @@ onMounted(async () => {
target: edge.target, target: edge.target,
sourceHandle: edge.sourceHandle, sourceHandle: edge.sourceHandle,
targetHandle: edge.targetHandle, targetHandle: edge.targetHandle,
type: edge.type || 'smoothstep', type: edge.type || 'bezier',
animated: true, animated: true,
selectable: true, selectable: true,
deletable: true, deletable: true,

View File

@@ -166,6 +166,61 @@ export const useAgentStore = defineStore('agent', () => {
currentAgent.value = agent currentAgent.value = agent
} }
// 导出Agent
const exportAgent = async (agentId: string) => {
try {
const response = await api.get(`/api/v1/agents/${agentId}/export`)
if (!response.data) {
throw new Error('导出数据为空')
}
// 创建下载链接
const dataStr = JSON.stringify(response.data, null, 2)
const dataBlob = new Blob([dataStr], { type: 'application/json;charset=utf-8' })
const url = URL.createObjectURL(dataBlob)
const link = document.createElement('a')
link.href = url
// 处理文件名:移除特殊字符,避免下载失败
const agentName = (response.data.name || 'agent')
.replace(/[<>:"/\\|?*]/g, '_') // 移除Windows不允许的字符
.replace(/\s+/g, '_') // 空格替换为下划线
.substring(0, 50) // 限制长度
link.download = `${agentName}_${Date.now()}.json`
link.style.display = 'none'
document.body.appendChild(link)
// 触发下载
link.click()
// 清理
setTimeout(() => {
document.body.removeChild(link)
URL.revokeObjectURL(url)
}, 100)
return response.data
} catch (error: any) {
console.error('导出Agent失败', error)
const errorMessage = error.response?.data?.detail || error.message || '导出失败'
throw new Error(errorMessage)
}
}
// 导入Agent
const importAgent = async (agentData: any) => {
loading.value = true
try {
const response = await api.post('/api/v1/agents/import', agentData)
agents.value.unshift(response.data) // 添加到列表开头
return response.data
} finally {
loading.value = false
}
}
return { return {
agents, agents,
currentAgent, currentAgent,
@@ -178,6 +233,8 @@ export const useAgentStore = defineStore('agent', () => {
deployAgent, deployAgent,
stopAgent, stopAgent,
duplicateAgent, duplicateAgent,
setCurrentAgent setCurrentAgent,
exportAgent,
importAgent
} }
}) })

View File

@@ -5,10 +5,16 @@
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<h2>Agent管理</h2> <h2>Agent管理</h2>
<el-button type="primary" @click="handleCreate"> <div>
<el-icon><Plus /></el-icon> <el-button @click="handleImport">
创建Agent <el-icon><Upload /></el-icon>
</el-button> 导入Agent
</el-button>
<el-button type="primary" @click="handleCreate" style="margin-left: 10px">
<el-icon><Plus /></el-icon>
创建Agent
</el-button>
</div>
</div> </div>
</template> </template>
@@ -70,12 +76,21 @@
{{ formatDate(row.created_at) }} {{ formatDate(row.created_at) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="350" fixed="right"> <el-table-column label="操作" width="480" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" @click="handleEdit(row)"> <el-button link type="primary" @click="handleEdit(row)">
<el-icon><Edit /></el-icon> <el-icon><Edit /></el-icon>
编辑 编辑
</el-button> </el-button>
<el-button
v-if="row.status === 'published' || row.status === 'running'"
link
type="primary"
@click="handleUse(row)"
>
<el-icon><ChatDotRound /></el-icon>
使用
</el-button>
<el-button link type="primary" @click="handleDesign(row)"> <el-button link type="primary" @click="handleDesign(row)">
<el-icon><Setting /></el-icon> <el-icon><Setting /></el-icon>
设计 设计
@@ -84,6 +99,10 @@
<el-icon><CopyDocument /></el-icon> <el-icon><CopyDocument /></el-icon>
复制 复制
</el-button> </el-button>
<el-button link type="success" @click="handleExport(row)">
<el-icon><Download /></el-icon>
导出
</el-button>
<el-button <el-button
v-if="row.status === 'draft' || row.status === 'stopped'" v-if="row.status === 'draft' || row.status === 'stopped'"
link link
@@ -164,6 +183,63 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 导入Agent对话框 -->
<el-dialog
v-model="importDialogVisible"
title="导入Agent"
width="600px"
@close="handleImportDialogClose"
>
<el-upload
ref="uploadRef"
:auto-upload="false"
:on-change="handleFileChange"
:file-list="fileList"
accept=".json"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
将JSON文件拖到此处<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
只能上传JSON格式的Agent配置文件
</div>
</template>
</el-upload>
<div v-if="importFileContent" style="margin-top: 20px;">
<el-alert
type="info"
:closable="false"
show-icon
>
<template #title>
<div>
<div><strong>Agent名称:</strong> {{ importFileContent.name || '未命名' }}</div>
<div v-if="importFileContent.description" style="margin-top: 5px;">
<strong>描述:</strong> {{ importFileContent.description }}
</div>
<div style="margin-top: 5px;">
<strong>节点数:</strong> {{ importFileContent.workflow_config?.nodes?.length || 0 }}
<span style="margin-left: 20px;">
<strong>连接数:</strong> {{ importFileContent.workflow_config?.edges?.length || 0 }}
</span>
</div>
</div>
</template>
</el-alert>
</div>
<template #footer>
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirmImport" :loading="importing" :disabled="!importFileContent">
确认导入
</el-button>
</template>
</el-dialog>
</div> </div>
</MainLayout> </MainLayout>
</template> </template>
@@ -182,7 +258,11 @@ import {
Setting, Setting,
VideoPlay, VideoPlay,
VideoPause, VideoPause,
CopyDocument CopyDocument,
Upload,
Download,
UploadFilled,
ChatDotRound
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import { useAgentStore } from '@/stores/agent' import { useAgentStore } from '@/stores/agent'
import type { Agent } from '@/stores/agent' import type { Agent } from '@/stores/agent'
@@ -225,6 +305,13 @@ const form = ref({
}) })
const currentAgentId = ref<string | null>(null) const currentAgentId = ref<string | null>(null)
// 导入相关
const importDialogVisible = ref(false)
const fileList = ref<any[]>([])
const importFileContent = ref<any>(null)
const importing = ref(false)
const uploadRef = ref()
// 表单验证规则 // 表单验证规则
const rules = { const rules = {
name: [ name: [
@@ -331,6 +418,14 @@ const handleEdit = (agent: Agent) => {
dialogVisible.value = true dialogVisible.value = true
} }
// 使用Agent跳转到设计器那里有聊天界面
const handleUse = (agent: Agent) => {
router.push({
name: 'AgentDesigner',
params: { id: agent.id }
})
}
// 设计 // 设计
const handleDesign = (agent: Agent) => { const handleDesign = (agent: Agent) => {
router.push({ router.push({
@@ -427,6 +522,75 @@ const handleDelete = async (agent: Agent) => {
} }
} }
// 导出Agent
const handleExport = async (agent: Agent) => {
try {
await agentStore.exportAgent(agent.id)
ElMessage.success('Agent导出成功')
} catch (error: any) {
console.error('导出失败详情:', error)
const errorMessage = error.message || error.response?.data?.detail || '导出失败,请查看控制台获取详细信息'
ElMessage.error(errorMessage)
}
}
// 显示导入对话框
const handleImport = () => {
importDialogVisible.value = true
fileList.value = []
importFileContent.value = null
}
// 文件选择
const handleFileChange = (file: any) => {
const reader = new FileReader()
reader.onload = (e) => {
try {
const content = JSON.parse(e.target?.result as string)
importFileContent.value = content
} catch (error) {
ElMessage.error('文件格式错误请上传有效的JSON文件')
fileList.value = []
importFileContent.value = null
}
}
reader.readAsText(file.raw)
}
// 关闭导入对话框
const handleImportDialogClose = () => {
fileList.value = []
importFileContent.value = null
if (uploadRef.value) {
uploadRef.value.clearFiles()
}
}
// 确认导入
const handleConfirmImport = async () => {
if (!importFileContent.value) {
ElMessage.warning('请先选择要导入的文件')
return
}
importing.value = true
try {
const agent = await agentStore.importAgent(importFileContent.value)
importDialogVisible.value = false
ElMessage.success('Agent导入成功')
await loadAgents()
// 跳转到Agent设计器
router.push({
name: 'AgentDesigner',
params: { id: agent.id }
})
} catch (error: any) {
ElMessage.error(error.response?.data?.detail || '导入Agent失败')
} finally {
importing.value = false
}
}
// 提交表单 // 提交表单
const handleSubmit = async () => { const handleSubmit = async () => {
if (!formRef.value) return if (!formRef.value) return

231
test_agent_execution.py Executable file
View File

@@ -0,0 +1,231 @@
#!/usr/bin/env python3
"""
Agent工作流执行测试脚本
用于测试Agent工作流的正常执行
"""
import requests
import json
import time
import sys
# API基础URL
BASE_URL = "http://localhost:8037"
def print_section(title):
"""打印分隔线"""
print("\n" + "=" * 80)
print(f" {title}")
print("=" * 80)
def test_agent_execution(agent_id: str = None, user_input: str = "生成一个导出androidlog的脚本"):
"""
测试Agent执行
Args:
agent_id: Agent ID如果为None则自动查找第一个已发布的Agent
user_input: 用户输入内容
"""
print_section("Agent工作流执行测试")
# 1. 登录获取token
print_section("1. 用户登录")
login_data = {
"username": "admin",
"password": "123456"
}
try:
# OAuth2PasswordRequestForm需要form-data格式
response = requests.post(f"{BASE_URL}/api/v1/auth/login", data=login_data)
if response.status_code != 200:
print(f"❌ 登录失败: {response.status_code}")
print(f"响应: {response.text}")
return
token = response.json().get("access_token")
if not token:
print("❌ 登录失败: 未获取到token")
return
print("✅ 登录成功")
headers = {"Authorization": f"Bearer {token}"}
except Exception as e:
print(f"❌ 登录异常: {str(e)}")
return
# 2. 获取Agent列表如果没有指定agent_id
if not agent_id:
print_section("2. 查找可用的Agent")
try:
response = requests.get(
f"{BASE_URL}/api/v1/agents",
headers=headers,
params={"status": "published", "limit": 10}
)
if response.status_code == 200:
agents = response.json()
if agents:
# 优先选择已发布的Agent
published_agents = [a for a in agents if a.get("status") == "published"]
if published_agents:
agent_id = published_agents[0]["id"]
agent_name = published_agents[0]["name"]
print(f"✅ 找到已发布的Agent: {agent_name} (ID: {agent_id})")
else:
# 如果没有已发布的,使用第一个
agent_id = agents[0]["id"]
agent_name = agents[0]["name"]
print(f"⚠️ 使用Agent: {agent_name} (ID: {agent_id}) - 状态: {agents[0].get('status')}")
else:
print("❌ 未找到可用的Agent")
print("请先创建一个Agent并发布或者指定agent_id参数")
return
else:
print(f"❌ 获取Agent列表失败: {response.status_code}")
print(f"响应: {response.text}")
return
except Exception as e:
print(f"❌ 获取Agent列表异常: {str(e)}")
return
# 3. 执行Agent
print_section("3. 执行Agent工作流")
print(f"用户输入: {user_input}")
input_data = {
"query": user_input,
"USER_INPUT": user_input
}
execution_data = {
"agent_id": agent_id,
"input_data": input_data
}
try:
response = requests.post(
f"{BASE_URL}/api/v1/executions",
headers=headers,
json=execution_data
)
if response.status_code != 201:
print(f"❌ 创建执行任务失败: {response.status_code}")
print(f"响应: {response.text}")
return
execution = response.json()
execution_id = execution["id"]
print(f"✅ 执行任务已创建: {execution_id}")
print(f"状态: {execution.get('status')}")
except Exception as e:
print(f"❌ 创建执行任务异常: {str(e)}")
return
# 4. 轮询执行状态
print_section("4. 等待执行完成")
max_wait_time = 300 # 最大等待5分钟
start_time = time.time()
poll_interval = 2 # 每2秒轮询一次
while True:
elapsed_time = time.time() - start_time
if elapsed_time > max_wait_time:
print(f"❌ 执行超时(超过{max_wait_time}秒)")
break
try:
# 获取执行状态
status_response = requests.get(
f"{BASE_URL}/api/v1/executions/{execution_id}/status",
headers=headers
)
if status_response.status_code == 200:
status = status_response.json()
current_status = status.get("status")
# 显示进度
progress = status.get("progress", 0)
print(f"⏳ 执行中... 状态: {current_status}, 进度: {progress}%", end="\r")
if current_status == "completed":
print("\n✅ 执行完成!")
break
elif current_status == "failed":
print(f"\n❌ 执行失败")
error = status.get("error", "未知错误")
print(f"错误信息: {error}")
break
# 显示当前执行的节点
current_node = status.get("current_node")
if current_node:
print(f"\n 当前节点: {current_node.get('node_id')} - {current_node.get('node_name')}")
time.sleep(poll_interval)
except Exception as e:
print(f"\n❌ 获取执行状态异常: {str(e)}")
time.sleep(poll_interval)
# 5. 获取执行结果
print_section("5. 获取执行结果")
try:
response = requests.get(
f"{BASE_URL}/api/v1/executions/{execution_id}",
headers=headers
)
if response.status_code == 200:
execution = response.json()
status = execution.get("status")
output_data = execution.get("output_data")
execution_time = execution.get("execution_time")
print(f"执行状态: {status}")
if execution_time:
print(f"执行时间: {execution_time}ms")
print("\n输出结果:")
print("-" * 80)
if output_data:
if isinstance(output_data, dict):
# 尝试提取文本输出
text_output = (
output_data.get("output") or
output_data.get("text") or
output_data.get("content") or
output_data.get("result") or
json.dumps(output_data, ensure_ascii=False, indent=2)
)
print(text_output)
else:
print(output_data)
else:
print("(无输出数据)")
print("-" * 80)
# 显示执行日志(如果有)
if execution.get("logs"):
print("\n执行日志:")
for log in execution.get("logs", []):
print(f" [{log.get('timestamp')}] {log.get('message')}")
else:
print(f"❌ 获取执行结果失败: {response.status_code}")
print(f"响应: {response.text}")
except Exception as e:
print(f"❌ 获取执行结果异常: {str(e)}")
print_section("测试完成")
if __name__ == "__main__":
# 从命令行参数获取agent_id和user_input
agent_id = None
user_input = "生成一个导出androidlog的脚本"
if len(sys.argv) > 1:
agent_id = sys.argv[1]
if len(sys.argv) > 2:
user_input = sys.argv[2]
test_agent_execution(agent_id=agent_id, user_input=user_input)

444
test_workflow_tool.py Executable file
View File

@@ -0,0 +1,444 @@
#!/usr/bin/env python3
"""
工作流测试工具
支持通过Agent名称和用户输入来测试工作流执行
"""
import requests
import json
import time
import sys
import argparse
# API基础URL
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 print_warning(message):
"""打印警告信息"""
print(f"⚠️ {message}")
def login(username="admin", password="123456"):
"""
用户登录
Returns:
tuple: (success: bool, token: str or None, headers: dict or None)
"""
print_section("1. 用户登录")
login_data = {
"username": username,
"password": password
}
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}")
print(f"响应: {response.text}")
return False, None, None
token = response.json().get("access_token")
if not token:
print_error("登录失败: 未获取到token")
return False, None, None
print_success(f"登录成功 (用户: {username})")
headers = {"Authorization": f"Bearer {token}"}
return True, token, headers
except requests.exceptions.ConnectionError:
print_error("无法连接到后端服务,请确保后端服务正在运行")
print_info(f"后端服务地址: {BASE_URL}")
return False, None, None
except Exception as e:
print_error(f"登录异常: {str(e)}")
return False, None, None
def find_agent_by_name(agent_name, headers):
"""
通过名称查找Agent
Args:
agent_name: Agent名称
headers: 请求头包含token
Returns:
tuple: (success: bool, agent: dict or None)
"""
print_section("2. 查找Agent")
print_info(f"搜索Agent: {agent_name}")
try:
# 搜索Agent
response = requests.get(
f"{BASE_URL}/api/v1/agents",
headers=headers,
params={"search": agent_name, "limit": 100}
)
if response.status_code != 200:
print_error(f"获取Agent列表失败: {response.status_code}")
print(f"响应: {response.text}")
return False, None
agents = response.json()
# 精确匹配名称
exact_match = None
for agent in agents:
if agent.get("name") == agent_name:
exact_match = agent
break
if exact_match:
agent_id = exact_match["id"]
agent_status = exact_match.get("status", "unknown")
print_success(f"找到Agent: {agent_name} (ID: {agent_id}, 状态: {agent_status})")
# 检查状态
if agent_status not in ["published", "running"]:
print_warning(f"Agent状态为 '{agent_status}',可能无法执行")
print_info("只有 'published''running' 状态的Agent可以执行")
return True, exact_match
# 如果没有精确匹配,显示相似的结果
if agents:
print_warning(f"未找到名称为 '{agent_name}' 的Agent")
print_info("找到以下相似的Agent:")
for agent in agents[:5]: # 只显示前5个
print(f" - {agent.get('name')} (ID: {agent.get('id')}, 状态: {agent.get('status')})")
else:
print_error(f"未找到任何Agent")
print_info("请检查Agent名称是否正确或先创建一个Agent")
return False, None
except Exception as e:
print_error(f"查找Agent异常: {str(e)}")
return False, None
def execute_agent(agent_id, user_input, headers):
"""
执行Agent工作流
Args:
agent_id: Agent ID
user_input: 用户输入内容
headers: 请求头
Returns:
tuple: (success: bool, execution_id: str or None)
"""
print_section("3. 执行Agent工作流")
print_info(f"用户输入: {user_input}")
input_data = {
"query": user_input,
"USER_INPUT": user_input
}
execution_data = {
"agent_id": agent_id,
"input_data": input_data
}
try:
response = requests.post(
f"{BASE_URL}/api/v1/executions",
headers=headers,
json=execution_data
)
if response.status_code != 201:
print_error(f"创建执行任务失败: {response.status_code}")
print(f"响应: {response.text}")
return False, None
execution = response.json()
execution_id = execution["id"]
status = execution.get("status")
print_success(f"执行任务已创建")
print_info(f"执行ID: {execution_id}")
print_info(f"状态: {status}")
return True, execution_id
except Exception as e:
print_error(f"创建执行任务异常: {str(e)}")
return False, None
def wait_for_completion(execution_id, headers, max_wait_time=300, poll_interval=2):
"""
等待执行完成
Args:
execution_id: 执行ID
headers: 请求头
max_wait_time: 最大等待时间(秒)
poll_interval: 轮询间隔(秒)
Returns:
tuple: (success: bool, status: str or None)
"""
print_section("4. 等待执行完成")
print_info(f"最大等待时间: {max_wait_time}")
print_info(f"轮询间隔: {poll_interval}")
start_time = time.time()
last_node = None
while True:
elapsed_time = time.time() - start_time
if elapsed_time > max_wait_time:
print_error(f"执行超时(超过{max_wait_time}秒)")
return False, "timeout"
try:
# 获取执行状态
status_response = requests.get(
f"{BASE_URL}/api/v1/executions/{execution_id}/status",
headers=headers
)
if status_response.status_code == 200:
status = status_response.json()
current_status = status.get("status")
progress = status.get("progress", 0)
current_node = status.get("current_node")
# 显示当前执行的节点
if current_node:
node_id = current_node.get("node_id", "unknown")
node_name = current_node.get("node_name", "unknown")
if node_id != last_node:
print_info(f"当前节点: {node_id} ({node_name})")
last_node = node_id
# 显示进度
elapsed_str = f"{int(elapsed_time)}"
print(f"⏳ 执行中... 状态: {current_status}, 进度: {progress}%, 耗时: {elapsed_str}", end="\r")
if current_status == "completed":
print() # 换行
print_success("执行完成!")
return True, "completed"
elif current_status == "failed":
print() # 换行
print_error("执行失败")
error = status.get("error", "未知错误")
print_error(f"错误信息: {error}")
return False, "failed"
time.sleep(poll_interval)
except KeyboardInterrupt:
print() # 换行
print_warning("用户中断执行")
return False, "interrupted"
except Exception as e:
print_error(f"获取执行状态异常: {str(e)}")
time.sleep(poll_interval)
def get_execution_result(execution_id, headers):
"""
获取执行结果
Args:
execution_id: 执行ID
headers: 请求头
Returns:
tuple: (success: bool, result: dict or None)
"""
print_section("5. 获取执行结果")
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}")
print(f"响应: {response.text}")
return False, None
execution = response.json()
status = execution.get("status")
output_data = execution.get("output_data")
execution_time = execution.get("execution_time")
print_info(f"执行状态: {status}")
if execution_time:
print_info(f"执行时间: {execution_time}ms ({execution_time/1000:.2f}秒)")
print()
print("=" * 80)
print("输出结果:")
print("=" * 80)
if output_data:
if isinstance(output_data, dict):
# 尝试提取文本输出
text_output = (
output_data.get("output") or
output_data.get("text") or
output_data.get("content") or
output_data.get("result") or
json.dumps(output_data, ensure_ascii=False, indent=2)
)
print(text_output)
else:
print(output_data)
else:
print("(无输出数据)")
print("=" * 80)
return True, execution
except Exception as e:
print_error(f"获取执行结果异常: {str(e)}")
return False, None
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description="工作流测试工具 - 通过Agent名称和用户输入测试工作流执行",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 使用默认参数(交互式输入)
python3 test_workflow_tool.py
# 指定Agent名称和用户输入
python3 test_workflow_tool.py -a "智能需求分析与解决方案生成器" -i "生成一个导出androidlog的脚本"
# 指定用户名和密码
python3 test_workflow_tool.py -u admin -p 123456 -a "Agent名称" -i "用户输入"
"""
)
parser.add_argument(
"-a", "--agent-name",
type=str,
help="Agent名称如果不指定将交互式输入"
)
parser.add_argument(
"-i", "--input",
type=str,
help="用户输入内容(如果不指定,将交互式输入)"
)
parser.add_argument(
"-u", "--username",
type=str,
default="admin",
help="登录用户名(默认: admin"
)
parser.add_argument(
"-p", "--password",
type=str,
default="123456",
help="登录密码(默认: 123456"
)
parser.add_argument(
"--max-wait",
type=int,
default=300,
help="最大等待时间(秒,默认: 300"
)
parser.add_argument(
"--poll-interval",
type=float,
default=2.0,
help="轮询间隔(秒,默认: 2.0"
)
args = parser.parse_args()
# 打印标题
print("=" * 80)
print(" 工作流测试工具")
print("=" * 80)
# 1. 登录
success, token, headers = login(args.username, args.password)
if not success:
sys.exit(1)
# 2. 获取Agent名称
agent_name = args.agent_name
if not agent_name:
agent_name = input("\n请输入Agent名称: ").strip()
if not agent_name:
print_error("Agent名称不能为空")
sys.exit(1)
# 3. 查找Agent
success, agent = find_agent_by_name(agent_name, headers)
if not success or not agent:
sys.exit(1)
agent_id = agent["id"]
# 4. 获取用户输入
user_input = args.input
if not user_input:
user_input = input("\n请输入用户输入内容: ").strip()
if not user_input:
print_error("用户输入不能为空")
sys.exit(1)
# 5. 执行Agent
success, execution_id = execute_agent(agent_id, user_input, headers)
if not success:
sys.exit(1)
# 6. 等待执行完成
success, status = wait_for_completion(
execution_id,
headers,
max_wait_time=args.max_wait,
poll_interval=args.poll_interval
)
if not success:
if status == "timeout":
print_warning("执行超时,但可能仍在后台运行")
print_info(f"执行ID: {execution_id}")
print_info("可以通过API查询执行状态")
sys.exit(1)
# 7. 获取执行结果
success, result = get_execution_result(execution_id, headers)
if not success:
sys.exit(1)
# 完成
print_section("测试完成")
print_success("工作流测试成功完成!")
print_info(f"执行ID: {execution_id}")
print_info(f"Agent: {agent_name}")
if __name__ == "__main__":
main()