Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Pangea-Agent
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Gavin-Group
Pangea-Agent
Commits
ae753c87
Commit
ae753c87
authored
Dec 22, 2025
by
ligaowei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修复AgentController中getUserAgents方法缺少权限注解的问题
parent
405accc6
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
445 additions
and
29 deletions
+445
-29
TOOL_MANAGEMENT_SCHEME.md
TOOL_MANAGEMENT_SCHEME.md
+200
-0
TimerExecutionHistory.java
...main/java/pangea/hiagent/model/TimerExecutionHistory.java
+0
-1
AgentController.java
...n/java/pangea/hiagent/web/controller/AgentController.java
+1
-0
ToolController.java
...in/java/pangea/hiagent/web/controller/ToolController.java
+59
-2
Login.vue
frontend/src/pages/Login.vue
+9
-5
ToolManagement.vue
frontend/src/pages/ToolManagement.vue
+176
-21
No files found.
TOOL_MANAGEMENT_SCHEME.md
0 → 100644
View file @
ae753c87
# HiAgent 工具管理方案
## 1. 概述
本文档旨在详细说明 HiAgent 平台的工具管理机制,确保工具方法能够被 Spring AOP 正确代理,并支持手动扫描注册及 UI 配置功能。
通过对现有代码的分析,我们发现当前系统在工具管理方面还存在一些问题,主要包括:
1.
缺少手动触发工具扫描的前端界面
2.
工具无法被正确找到和调用的问题
3.
工具扫描API端点未暴露给前端使用
本文档将在分析这些问题的基础上,提出相应的改进建议.
## 2. Spring AOP 代理兼容性方案
### 2.1 工具类设计规范
为了确保工具方法能够被 Spring AOP 正确代理,所有工具类需要遵循以下规范:
1.
**注解使用**
:
-
工具类必须使用
`@Component`
或其派生注解(如
`@Service`
)进行标记
-
工具方法必须使用
`@org.springframework.ai.tool.annotation.Tool`
注解进行标记
2.
**访问修饰符**
:
-
工具方法必须是
`public`
方法
-
避免在同一个类中直接调用其他带有
`@Tool`
注解的方法
3.
**类设计**
:
-
工具类应该是无状态的,或者状态应该是线程安全的
-
避免使用
`final`
方法,因为这会影响 CGLIB 代理的创建
### 2.2 AOP 代理穿透机制
系统已经实现了 AOP 代理穿透机制,确保即使在使用 Spring AOP 代理的情况下也能正确获取工具信息:
1.
在
[
AgentToolManager.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/AgentToolManager.java
)
中提供了
`getTargetClass()`
方法来获取代理对象的原始类:
```
java
private
Class
<?>
getTargetClass
(
Object
bean
)
{
if
(
bean
==
null
)
{
return
null
;
}
return
AopUtils
.
getTargetClass
(
bean
);
}
```
2.
在工具匹配过程中,系统会穿透代理获取真实的类信息进行比较,确保匹配准确性。
### 2.3 工具执行日志切面
系统通过
[
ToolExecutionLoggerAspect.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/aspect/ToolExecutionLoggerAspect.java
)
实现了工具执行的日志记录和监控:
1.
使用
`@Around("@annotation(tool)")`
环绕通知拦截所有带有
`@Tool`
注解的方法
2.
自动记录工具执行的输入参数、输出结果、执行时间等信息
3.
将工具执行信息同步到 WorkPanel 进行可视化展示
## 3. 工具扫描与注册机制
### 3.1 自动扫描机制
系统通过
[
ToolBeanNameInitializer.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/ToolBeanNameInitializer.java
)
实现工具的自动扫描和注册:
1.
**扫描范围**
:
-
扫描所有 Spring 容器中的 Bean
-
识别带有
`@Tool`
注解方法的类作为工具类
-
过滤掉 Spring 框架自带的 Bean
2.
**工具识别规则**
:
-
类名包含 "Tool" 关键字
-
被
`@Component`
或
`@Service`
标注
-
类中包含带有
`@Tool`
注解的方法
3.
**工具名称推导**
:
-
从类名推导工具名称,去除 "Tool" 后缀
-
转换为小驼峰命名格式
### 3.2 手动触发扫描
系统支持通过管理界面手动触发工具扫描和注册:
1.
提供
`initializeToolBeanNamesManually()`
方法用于手动触发扫描
2.
扫描过程会与数据库中的工具记录进行同步:
-
如果数据库中已存在对应工具,则更新 beanName
-
如果数据库中不存在对应工具,则创建新的工具记录
-
如果数据库中有记录但 Spring 容器中不存在对应 Bean,则记录警告信息
目前系统已经实现了手动扫描功能的后端API端点,位于
`/api/v1/admin/system/initialize-tool-beans`
,通过 POST 请求触发。但在前端界面上还未提供相应的人机交互界面。
### 3.3 数据库同步策略
工具信息会被持久化存储在数据库中,确保系统重启后配置不会丢失:
1.
**工具实体**
:
-
工具名称(唯一标识)
-
Spring Bean 名称(用于查找对应的实例)
-
工具显示名称
-
工具描述
-
工具状态(active/inactive)
-
工具所有者等信息
2.
**同步机制**
:
-
系统启动时不自动执行扫描(避免影响启动速度)
-
通过管理界面手动触发扫描和同步
-
支持增量更新,只处理发生变化的工具
## 4. 当前存在的问题与改进建议
### 4.1 当前存在的主要问题
通过分析现有代码和功能实现,我们发现工具管理系统存在以下主要问题:
1.
**缺少手动扫描的前端界面**
:
-
后端已经实现了手动扫描工具的API端点(
`/api/v1/admin/system/initialize-tool-beans`
)
-
但前端尚未提供相应的用户界面来触发这一功能
2.
**工具无法正确找到和调用**
:
-
在
[
AgentToolManager.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/AgentToolManager.java
)
的
`getAvailableToolInstances`
方法中,当工具的 beanName 为空或查找失败时,仅记录日志而没有提供有效的错误反馈机制
-
工具调用失败时缺乏详细的错误信息和调试手段
3.
**工具管理页面功能不完善**
:
-
当前的
[
ToolManagement.vue
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/frontend/src/pages/ToolManagement.vue
)
页面仅支持基础的增删改查功能
-
缺少与后端扫描功能的集成
### 4.2 改进建议
针对上述问题,我们提出以下改进建议:
#### 4.2.1 完善前端工具管理界面
1.
**增加手动扫描按钮**
:
-
在工具管理页面添加"扫描工具"按钮
-
点击后调用后端API
`/api/v1/admin/system/initialize-tool-beans`
触发扫描
-
显示扫描进度和结果
-
提供扫描历史记录查看功能
2.
**增强工具详情展示**
:
-
在工具列表中增加显示工具的Bean名称、状态等详细信息
-
提供工具测试功能,允许用户直接测试工具调用
-
显示工具的最后更新时间和创建者信息
3.
**优化错误提示**
:
-
当工具无法找到或调用失败时,提供更明确的错误信息
-
增加工具诊断功能,帮助用户排查问题
-
提供常见问题解决方案链接和帮助文档
4.
**增加工具诊断界面**
:
-
提供单个工具的详细诊断信息查看
-
支持批量工具状态检查
-
显示工具依赖关系图谱#### 4.2.2 后端功能优化
1.
**完善工具调用错误处理**
:
-
在
[
AgentToolManager.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/AgentToolManager.java
)
中增强错误处理机制
-
提供更详细的错误信息,便于前端展示和用户排查问题
-
增加结构化的错误信息返回,包含具体的原因和解决方案建议
2.
**增加工具诊断API**
:
-
提供工具诊断端点,检查工具是否正确定义和注册
-
返回工具的详细信息和可能存在的问题
-
支持单个工具诊断和批量工具诊断功能
3.
**优化日志记录**
:
-
增强工具调用过程中的日志记录
-
提供更详细的调试信息,便于问题追踪
-
结构化日志信息,方便后续分析和问题定位## 5. 实施步骤
### 5.1 后端实施
1.
完善
[
ToolBeanNameInitializer.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/ToolBeanNameInitializer.java
)
的手动扫描接口
2.
优化
[
AgentToolManager.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/AgentToolManager.java
)
的工具获取逻辑,增强错误处理和日志记录
3.
增强
[
ToolExecutionLoggerAspect.java
](
file:///c:/Users/Gavin/Documents/PangeaFinal/HiAgent/backend/src/main/java/pangea/hiagent/tool/aspect/ToolExecutionLoggerAspect.java
)
的日志记录功能
4.
增加工具诊断API端点,提供工具状态检查功能
5.
实现工具依赖关系分析功能
6.
增加工具使用统计和性能监控
### 5.2 前端实施
1.
在工具管理页面增加手动扫描按钮,调用
`/api/v1/admin/system/initialize-tool-beans`
端点
2.
增强工具列表展示,显示更多工具详细信息如Bean名称、状态等
3.
增加工具测试功能,允许用户直接测试工具调用
4.
优化错误提示,提供更明确的错误信息帮助用户排查问题
5.
实现工具诊断界面,支持单个和批量工具诊断
6.
增加工具依赖关系可视化展示
### 5.3 测试验证
1.
验证工具方法的 AOP 代理兼容性
2.
测试手动扫描和自动注册功能
3.
验证 UI 配置功能的完整性和易用性
4.
测试工具执行的日志记录和监控功能
5.
验证错误处理机制的有效性
6.
测试工具诊断功能的准确性和完整性
7.
验证工具依赖关系分析的正确性
## 6. 总结
本方案通过规范工具类设计、实现 AOP 代理穿透、建立完善的扫描注册机制以及提供友好的 UI 配置界面,全面解决了工具管理的相关需求。该方案既保证了系统的稳定性和扩展性,又提升了用户的使用体验。
通过对现有代码的分析,我们确认系统已经具备了良好的基础架构,包括:
1.
完善的 AOP 代理支持
2.
工具扫描和注册的核心功能实现
3.
工具调用的日志记录机制
接下来的工作重点应该放在完善前端界面和增强错误处理上,使系统更加易于使用和维护。特别需要关注的是工具诊断功能的实现,这将大大提高系统运维和问题排查的效率。
\ No newline at end of file
backend/src/main/java/pangea/hiagent/model/TimerExecutionHistory.java
View file @
ae753c87
...
@@ -3,7 +3,6 @@ package pangea.hiagent.model;
...
@@ -3,7 +3,6 @@ package pangea.hiagent.model;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableLogic
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Data
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
lombok.EqualsAndHashCode
;
...
...
backend/src/main/java/pangea/hiagent/web/controller/AgentController.java
View file @
ae753c87
...
@@ -289,6 +289,7 @@ public class AgentController {
...
@@ -289,6 +289,7 @@ public class AgentController {
* 获取用户的Agent列表
* 获取用户的Agent列表
*/
*/
@GetMapping
@GetMapping
@PreAuthorize
(
"isAuthenticated()"
)
public
ApiResponse
<
java
.
util
.
List
<
Agent
>>
getUserAgents
()
{
public
ApiResponse
<
java
.
util
.
List
<
Agent
>>
getUserAgents
()
{
try
{
try
{
String
userId
=
UserUtils
.
getCurrentUserId
();
String
userId
=
UserUtils
.
getCurrentUserId
();
...
...
backend/src/main/java/pangea/hiagent/web/controller/ToolController.java
View file @
ae753c87
...
@@ -8,10 +8,13 @@ import pangea.hiagent.model.Tool;
...
@@ -8,10 +8,13 @@ import pangea.hiagent.model.Tool;
import
pangea.hiagent.web.dto.ApiResponse
;
import
pangea.hiagent.web.dto.ApiResponse
;
import
pangea.hiagent.web.service.ToolService
;
import
pangea.hiagent.web.service.ToolService
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.HashMap
;
/**
/**
* 工具API控制器
* 工具API控制器
...
@@ -24,9 +27,11 @@ import java.util.List;
...
@@ -24,9 +27,11 @@ import java.util.List;
public
class
ToolController
{
public
class
ToolController
{
private
final
ToolService
toolService
;
private
final
ToolService
toolService
;
private
final
ApplicationContext
applicationContext
;
public
ToolController
(
ToolService
toolService
)
{
public
ToolController
(
ToolService
toolService
,
ApplicationContext
applicationContext
)
{
this
.
toolService
=
toolService
;
this
.
toolService
=
toolService
;
this
.
applicationContext
=
applicationContext
;
}
}
/**
/**
...
@@ -141,4 +146,56 @@ public class ToolController {
...
@@ -141,4 +146,56 @@ public class ToolController {
return
ApiResponse
.
error
(
4001
,
"获取工具列表失败: "
+
e
.
getMessage
());
return
ApiResponse
.
error
(
4001
,
"获取工具列表失败: "
+
e
.
getMessage
());
}
}
}
}
/**
* 诊断工具
*/
@GetMapping
(
"/{id}/diagnose"
)
@Operation
(
summary
=
"诊断工具"
,
description
=
"诊断指定ID的工具是否存在和配置是否正确"
)
public
ApiResponse
<
Map
<
String
,
Object
>>
diagnoseTool
(
@PathVariable
String
id
)
{
try
{
String
userId
=
getCurrentUserId
();
if
(
userId
==
null
)
{
return
ApiResponse
.
error
(
4001
,
"用户未认证"
);
}
Tool
tool
=
toolService
.
getToolById
(
id
,
userId
);
if
(
tool
==
null
)
{
return
ApiResponse
.
error
(
4004
,
"工具不存在"
);
}
Map
<
String
,
Object
>
diagnosis
=
new
HashMap
<>();
diagnosis
.
put
(
"tool"
,
tool
);
// 检查Bean是否存在
if
(
tool
.
getBeanName
()
!=
null
&&
!
tool
.
getBeanName
().
isEmpty
())
{
try
{
Object
bean
=
applicationContext
.
getBean
(
tool
.
getBeanName
());
diagnosis
.
put
(
"beanExists"
,
true
);
diagnosis
.
put
(
"beanClass"
,
bean
.
getClass
().
getName
());
// 检查是否为代理类
if
(
org
.
springframework
.
aop
.
support
.
AopUtils
.
isAopProxy
(
bean
))
{
diagnosis
.
put
(
"isProxy"
,
true
);
Class
<?>
targetClass
=
org
.
springframework
.
aop
.
support
.
AopUtils
.
getTargetClass
(
bean
);
diagnosis
.
put
(
"targetClass"
,
targetClass
.
getName
());
}
else
{
diagnosis
.
put
(
"isProxy"
,
false
);
diagnosis
.
put
(
"targetClass"
,
bean
.
getClass
().
getName
());
}
}
catch
(
Exception
e
)
{
diagnosis
.
put
(
"beanExists"
,
false
);
diagnosis
.
put
(
"beanError"
,
e
.
getMessage
());
}
}
else
{
diagnosis
.
put
(
"beanExists"
,
false
);
diagnosis
.
put
(
"beanError"
,
"工具未配置Bean名称"
);
}
return
ApiResponse
.
success
(
diagnosis
,
"工具诊断完成"
);
}
catch
(
Exception
e
)
{
log
.
error
(
"诊断工具失败"
,
e
);
return
ApiResponse
.
error
(
4001
,
"诊断工具失败: "
+
e
.
getMessage
());
}
}
}
}
\ No newline at end of file
frontend/src/pages/Login.vue
View file @
ae753c87
...
@@ -112,10 +112,10 @@ onMounted(() => {
...
@@ -112,10 +112,10 @@ onMounted(() => {
ElMessage
.
error
(
`登录失败:
${
error
}
`
)
ElMessage
.
error
(
`登录失败:
${
error
}
`
)
}
else
if
(
token
&&
method
===
'oauth2'
)
{
}
else
if
(
token
&&
method
===
'oauth2'
)
{
// 有效的 OAuth2 回调,正常登录
// 有效的 OAuth2 回调,正常登录
localStorage
.
setItem
(
'token'
,
token
)
authStore
.
token
=
token
authStore
.
token
=
token
localStorage
.
setItem
(
'token'
,
token
)
ElMessage
.
success
(
'OAuth2 登录成功'
)
ElMessage
.
success
(
'OAuth2 登录成功'
)
router
.
push
(
'/
chat
'
)
router
.
push
(
'/
dashboard
'
)
}
}
})
})
...
@@ -126,9 +126,13 @@ const handleLogin = async () => {
...
@@ -126,9 +126,13 @@ const handleLogin = async () => {
await
formRef
.
value
.
validate
()
await
formRef
.
value
.
validate
()
loading
.
value
=
true
loading
.
value
=
true
await
authStore
.
login
(
form
.
username
,
form
.
password
)
const
response
=
await
authStore
.
login
(
form
.
username
,
form
.
password
)
ElMessage
.
success
(
'登录成功'
)
if
(
response
.
code
===
200
)
{
router
.
push
(
'/chat'
)
ElMessage
.
success
(
'登录成功'
)
router
.
push
(
'/dashboard'
)
}
else
{
ElMessage
.
error
(
response
.
message
||
'登录失败,请检查用户名和密码'
)
}
}
catch
(
error
:
any
)
{
}
catch
(
error
:
any
)
{
console
.
error
(
'登录失败:'
,
error
)
console
.
error
(
'登录失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'登录失败,请检查用户名和密码'
)
ElMessage
.
error
(
error
.
message
||
'登录失败,请检查用户名和密码'
)
...
...
frontend/src/pages/ToolManagement.vue
View file @
ae753c87
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment