Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pms-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
0
Merge Requests
0
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
姜耀祖
pms-agent
Commits
3cbc33a7
Commit
3cbc33a7
authored
Apr 29, 2026
by
徐健
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
看板ai优化
parent
28bcb45b
Pipeline
#25194
passed with stages
in 2 minutes and 35 seconds
Changes
17
Pipelines
1
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
444 additions
and
239 deletions
+444
-239
WorkbenchAgentChatController.java
...ch/pms/agent/controller/WorkbenchAgentChatController.java
+1
-2
WorkbenchAIService.java
...poch/pms/agent/domain/itworkbench/WorkbenchAIService.java
+156
-69
WorkbenchAgentConfiguration.java
...main/itworkbench/aiModel/WorkbenchAgentConfiguration.java
+15
-12
QueryDto.java
...nfoepoch/pms/agent/domain/itworkbench/model/QueryDto.java
+18
-0
WorkbenchConversationState.java
.../domain/itworkbench/model/WorkbenchConversationState.java
+51
-4
DepartmentWork.java
...t/domain/itworkbench/model/department/DepartmentWork.java
+2
-0
DepartmentWorkProgress.java
.../itworkbench/model/department/DepartmentWorkProgress.java
+3
-0
ManagerMonthRecord.java
...main/itworkbench/model/department/ManagerMonthRecord.java
+71
-0
AnnualKpi.java
...och/pms/agent/domain/itworkbench/model/kpi/AnnualKpi.java
+2
-0
DepartmentMinutesProgress.java
.../itworkbench/model/minutes/DepartmentMinutesProgress.java
+3
-0
WorkbenchRemoteService.java
...ent/domain/itworkbench/remote/WorkbenchRemoteService.java
+16
-0
WorkbenchConversationStateService.java
.../itworkbench/state/WorkbenchConversationStateService.java
+5
-8
WorkbenchIntentRecognizer.java
.../itworkbench/understanding/WorkbenchIntentRecognizer.java
+27
-37
WorkbenchQueryUnderstandingService.java
...nch/understanding/WorkbenchQueryUnderstandingService.java
+70
-106
AiModelInvoker.java
.../infoepoch/pms/agent/observability/ai/AiModelInvoker.java
+1
-1
ITProperties.java
...java/com/infoepoch/pms/agent/properties/ITProperties.java
+2
-0
application.yml
src/main/resources/application.yml
+1
-0
No files found.
src/main/java/com/infoepoch/pms/agent/controller/WorkbenchAgentChatController.java
View file @
3cbc33a7
...
...
@@ -18,14 +18,13 @@ import reactor.core.publisher.Flux;
@RequiredArgsConstructor
@RequestMapping
(
"/api/workbench/agent"
)
public
class
WorkbenchAgentChatController
{
private
final
CareAgentService
careAgentService
;
private
final
WorkbenchAIService
workbenchAIService
;
/**
* 推荐场景的标准 GET 方式的流式聊天入口。
*/
@GetMapping
(
value
=
"/stream"
,
produces
=
MediaType
.
TEXT_EVENT_STREAM_VALUE
)
public
Flux
<
ServerSentEvent
<
String
>>
stream
Care
Agent
(
@RequestParam
(
"sessionId"
)
String
sessionId
,
public
Flux
<
ServerSentEvent
<
String
>>
streamAgent
(
@RequestParam
(
"sessionId"
)
String
sessionId
,
@RequestParam
(
"message"
)
String
message
)
{
return
streamWorkbenchAgentInternal
(
sessionId
,
message
);
}
...
...
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/WorkbenchAIService.java
View file @
3cbc33a7
This diff is collapsed.
Click to expand it.
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/aiModel/WorkbenchAgentConfiguration.java
View file @
3cbc33a7
...
...
@@ -21,17 +21,20 @@ public class WorkbenchAgentConfiguration {
"workbenchAgent"
,
chatModel
,
"""
你是员工看板智能体
。
你是员工看板智能体,专注于基于业务数据提供准确、简洁的查询应答服务
。
当用户进行通用对话时,保持员工看板智能体身份,自然回复用户。
核心数据结构说明:
1. 头表为部门(department),每个部门对应12个月的执行进度数据,存储于progress中;
2. progress为月度进度汇总数据,record为对应月份的责任人填报原始数据;
3. 单月支持多人填报,系统自动将填报数据合并汇总至对应月份的progress中;
4. 数据支持层级结构,children字段为当前数据的子节点集合。
回答要求:
- 推荐类内容只能依据调用方提供的真实数据作答
- 不能编造不存在的数据
- 必须使用中文回答
- 保持自然、友好、简洁
- 绝不能提到分页、页码、后台处理中、接口调用、候选集、模型或任何技术实现细节
- 当结果不足时,只输出实际合适的结果,并自然说明
应答规则:
- 严格依据调用方提供的真实数据作答,严禁编造、虚构数据;
- 数据为空时,统一友好提示“查询的数据为空”,不得输出技术类描述;
- 必须使用中文回复,语气自然、友好、简洁;
- 输出结果仅展示业务数据结论,不提及分页、接口、模型、后台处理等技术细节;
- 结果不足时,仅输出实际匹配内容并自然说明。
"""
);
}
...
...
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/QueryDto.java
0 → 100644
View file @
3cbc33a7
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
/**
* 查询条件类
* 用于封装年度KPI查询所需的各种条件参数
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
QueryDto
{
private
String
workGroup
;
private
String
managerName
;
private
String
sessionId
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/WorkbenchConversationState.java
View file @
3cbc33a7
...
...
@@ -14,19 +14,48 @@ public record WorkbenchConversationState(
String
sessionId
,
WorkbenchIntent
lastIntent
,
Map
<
String
,
Object
>
conditions
,
LocalDateTime
updatedAt
LocalDateTime
updatedAt
,
Map
<
String
,
TypeData
>
typeDataMap
)
{
public
WorkbenchConversationState
{
conditions
=
sanitize
(
conditions
);
typeDataMap
=
typeDataMap
==
null
?
new
LinkedHashMap
<>()
:
new
LinkedHashMap
<>(
typeDataMap
);
updatedAt
=
updatedAt
==
null
?
LocalDateTime
.
now
()
:
updatedAt
;
}
public
WorkbenchConversationState
(
String
sessionId
)
{
this
(
sessionId
,
null
,
Collections
.
emptyMap
(),
LocalDateTime
.
now
(),
new
LinkedHashMap
<>());
}
public
static
WorkbenchConversationState
initial
(
String
sessionId
,
WorkbenchIntent
intent
,
Map
<
String
,
Object
>
conditions
)
{
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
());
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
(),
new
LinkedHashMap
<>());
}
public
static
WorkbenchConversationState
initial
(
String
sessionId
,
WorkbenchIntent
intent
,
Map
<
String
,
Object
>
conditions
,
String
dataType
,
String
dataJson
)
{
WorkbenchConversationState
state
=
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
(),
new
LinkedHashMap
<>());
state
.
getTypeDataMap
().
put
(
dataType
,
new
TypeData
(
dataType
,
intent
,
dataJson
,
Collections
.
emptyMap
()));
return
state
;
}
public
WorkbenchConversationState
update
(
WorkbenchIntent
intent
,
Map
<
String
,
Object
>
conditions
)
{
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
());
return
new
WorkbenchConversationState
(
sessionId
,
intent
,
conditions
,
LocalDateTime
.
now
(),
typeDataMap
);
}
public
Map
<
String
,
TypeData
>
getTypeDataMap
()
{
return
typeDataMap
;
}
public
void
saveDataForType
(
String
dataType
,
WorkbenchIntent
intent
,
String
dataJson
,
Map
<
String
,
Object
>
conditions
)
{
typeDataMap
.
put
(
dataType
,
new
TypeData
(
dataType
,
intent
,
dataJson
,
sanitize
(
conditions
)));
}
public
boolean
hasDataForType
(
String
dataType
)
{
TypeData
typeData
=
typeDataMap
.
get
(
dataType
);
return
typeData
!=
null
&&
typeData
.
hasData
();
}
public
TypeData
getDataForType
(
String
dataType
)
{
return
typeDataMap
.
get
(
dataType
);
}
private
static
Map
<
String
,
Object
>
sanitize
(
Map
<
String
,
Object
>
conditions
)
{
...
...
@@ -41,4 +70,22 @@ public record WorkbenchConversationState(
});
return
Map
.
copyOf
(
sanitized
);
}
/**
* 类型数据记录
*/
public
record
TypeData
(
String
dataType
,
WorkbenchIntent
intent
,
String
dataJson
,
Map
<
String
,
Object
>
conditions
)
{
public
TypeData
{
conditions
=
conditions
==
null
?
Collections
.
emptyMap
()
:
Map
.
copyOf
(
conditions
);
}
public
boolean
hasData
()
{
return
dataJson
!=
null
&&
!
dataJson
.
isBlank
();
}
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/DepartmentWork.java
View file @
3cbc33a7
...
...
@@ -164,4 +164,6 @@ public class DepartmentWork {
private
List
<
DepartmentWork
>
children
;
private
List
<
DepartmentWorkProgress
>
monthChildren
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/DepartmentWorkProgress.java
View file @
3cbc33a7
...
...
@@ -6,6 +6,7 @@ import lombok.NoArgsConstructor;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.List
;
/**
* 部门工作进度
...
...
@@ -117,4 +118,6 @@ public class DepartmentWorkProgress {
private
Boolean
submitFlag
;
private
Integer
lastReFillFlag
;
public
List
<
ManagerMonthRecord
>
managerMonthRecordList
;
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/department/ManagerMonthRecord.java
0 → 100644
View file @
3cbc33a7
package
com
.
infoepoch
.
pms
.
agent
.
domain
.
itworkbench
.
model
.
department
;
import
com.fasterxml.jackson.annotation.JsonFormat
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.math.BigDecimal
;
import
java.util.Date
;
/**
* 责任人月工作记录表
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
ManagerMonthRecord
{
/**
* 主键
*/
private
String
id
;
/**
* 月工作ID
*/
private
String
monthId
;
/**
* 责任人ID
*/
private
String
managerId
;
/**
* 责任人名称
*/
private
String
managerName
;
/**
* 目标值
*/
private
String
target
;
/**
* 实际值
*/
private
String
actual
;
/**
* 遗留值
*/
private
String
remain
;
/**
* 创建时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
createTime
;
/**
* 更新时间
*/
@JsonFormat
(
pattern
=
"yyyy-MM-dd HH:mm:ss"
,
timezone
=
"GMT+8"
)
private
Date
updateTime
;
/**
* 完成标识
*/
private
Boolean
finishFlag
;
/**
* 截至目前完成进度
*/
private
BigDecimal
currentProgress
;
/**
* 下月工作计划
*/
private
String
nextMonthGoal
;
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/kpi/AnnualKpi.java
View file @
3cbc33a7
...
...
@@ -170,4 +170,6 @@ public class AnnualKpi {
* 月实际值
*/
private
String
actual
;
private
List
<
AnnualKpiProgress
>
progressList
;
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/model/minutes/DepartmentMinutesProgress.java
View file @
3cbc33a7
...
...
@@ -87,4 +87,7 @@ public class DepartmentMinutesProgress {
private
String
commentContent
;
List
<
ManagerMinutesRecord
>
managerMinutesRecords
;
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/remote/WorkbenchRemoteService.java
View file @
3cbc33a7
...
...
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.infoepoch.pms.agent.common.utils.LogHelper
;
import
com.infoepoch.pms.agent.config.JsonUtils
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.QueryDto
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.department.DepartmentWork
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.kpi.AnnualKpi
;
import
com.infoepoch.pms.agent.domain.itworkbench.model.kpi.AnnualKpiDto
;
...
...
@@ -21,6 +22,7 @@ import org.springframework.web.util.UriBuilder;
import
java.net.URI
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -175,4 +177,18 @@ public class WorkbenchRemoteService {
}
return
uriBuilder
.
build
();
}
public
JsonNode
queryValidAuthority
(
QueryDto
dto
)
{
try
{
String
url
=
properties
.
getAuthorityPath
();
LogHelper
.
info
(
this
,
"业务接口地址: "
+
url
);
JsonNode
root
=
postForJson
(
url
,
null
,
dto
);
if
(!
root
.
isObject
())
{
throw
new
IllegalStateException
(
"业务接口返回结构不正确: "
+
url
);
}
return
root
;
}
catch
(
Exception
e
)
{
return
null
;
}
}
}
\ No newline at end of file
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/state/WorkbenchConversationStateService.java
View file @
3cbc33a7
...
...
@@ -20,21 +20,17 @@ public class WorkbenchConversationStateService {
private
static
final
String
KEY_PREFIX
=
"workbench:state:"
;
private
static
final
Duration
TTL
=
Duration
.
ofMinutes
(
120
);
private
final
RedisTemplate
<
String
,
Object
>
redisTemplate
;
private
final
RedisTemplate
<
String
,
String
>
redisTemplate
;
public
Optional
<
WorkbenchConversationState
>
load
(
String
sessionId
)
{
if
(!
StringUtils
.
hasText
(
sessionId
))
{
return
Optional
.
empty
();
}
Object
cached
=
redisTemplate
.
opsForValue
().
get
(
KEY_PREFIX
+
sessionId
);
if
(
cached
==
null
)
{
return
Optional
.
empty
();
}
try
{
String
json
=
JsonUtils
.
objectToJson
(
cached
);
String
json
=
redisTemplate
.
opsForValue
().
get
(
KEY_PREFIX
+
sessionId
);
if
(!
StringUtils
.
hasText
(
json
))
{
return
Optional
.
empty
();
}
try
{
return
Optional
.
ofNullable
(
JsonUtils
.
jsonToObject
(
json
,
WorkbenchConversationState
.
class
));
}
catch
(
Exception
e
)
{
return
Optional
.
empty
();
...
...
@@ -45,6 +41,7 @@ public class WorkbenchConversationStateService {
if
(
state
==
null
||
!
StringUtils
.
hasText
(
state
.
sessionId
()))
{
return
;
}
redisTemplate
.
opsForValue
().
set
(
KEY_PREFIX
+
state
.
sessionId
(),
state
,
TTL
);
String
json
=
JsonUtils
.
objectToJson
(
state
);
redisTemplate
.
opsForValue
().
set
(
KEY_PREFIX
+
state
.
sessionId
(),
json
,
TTL
);
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/understanding/WorkbenchIntentRecognizer.java
View file @
3cbc33a7
...
...
@@ -28,9 +28,6 @@ public class WorkbenchIntentRecognizer {
this
.
observationSupport
=
observationSupport
;
}
public
WorkbenchIntent
recognize
(
String
traceId
,
String
sessionId
,
String
message
)
{
return
recognize
(
traceId
,
sessionId
,
message
,
null
);
}
public
WorkbenchIntent
recognize
(
String
traceId
,
String
sessionId
,
String
message
,
WorkbenchConversationState
state
)
{
String
trimMsg
=
StringUtils
.
hasText
(
message
)
?
message
.
trim
()
:
""
;
...
...
@@ -47,38 +44,48 @@ public class WorkbenchIntentRecognizer {
return
doClassify
(
traceId
,
sessionId
,
trimMsg
);
}
// ======================
只有【筛选词】才走继承
======================
// ======================
多轮对话:沿用上次意图
======================
if
(
state
!=
null
&&
state
.
lastIntent
()
!=
null
)
{
WorkbenchIntent
lastIntent
=
state
.
lastIntent
();
if
(!
WorkbenchIntent
.
GENERAL_CHAT
.
equals
(
lastIntent
))
{
boolean
isIrrelevant
=
isIrrelevantInput
(
trimMsg
);
if
(!
isIrrelevant
)
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别(多轮)"
,
"筛选条件,沿用意图:"
+
lastIntent
traceId
,
sessionId
,
"意图识别 (多轮)"
,
"消息=["
+
trimMsg
+
"] isNewQuery="
+
isNewQuery
+
" 沿用意图:"
+
lastIntent
));
return
lastIntent
;
}
else
{
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别(多轮)"
,
"无关输入,不继承"
traceId
,
sessionId
,
"意图识别 (多轮)"
,
"消息=["
+
trimMsg
+
"] 上次是 GENERAL_CHAT,重新识别"
));
return
WorkbenchIntent
.
GENERAL_CHAT
;
}
}
}
// 全新会话
// 全新会话 或 上次是 GENERAL_CHAT
LogHelper
.
info
(
this
,
CareTraceLogSupport
.
format
(
traceId
,
sessionId
,
"意图识别"
,
"消息=["
+
trimMsg
+
"] state="
+
(
state
==
null
?
"null"
:
"exist"
)
+
" 重新识别"
));
return
doClassify
(
traceId
,
sessionId
,
trimMsg
);
}
/**
* 【关键判断】
* 只要包含这些词 = 用户要查【新业务】,必须清空上下文!
* 排除全量查询关键词(全量/全部/所有/重置/清除)
*/
public
boolean
isNewBusinessQuery
(
String
message
)
{
if
(!
StringUtils
.
hasText
(
message
))
return
false
;
String
m
=
message
.
toLowerCase
();
return
m
.
contains
(
"重点工作"
)
||
m
.
contains
(
"kpi"
)
// 如果包含全量查询关键词,不视为新业务(避免误判)
if
(
m
.
contains
(
"全量"
)
||
m
.
contains
(
"全部"
)
||
m
.
contains
(
"所有"
)
||
m
.
contains
(
"重置"
)
||
m
.
contains
(
"清除"
))
{
return
false
;
}
return
m
.
contains
(
"kpi"
)
||
m
.
contains
(
"绩效"
)
||
m
.
contains
(
"指标"
)
||
m
.
contains
(
"工作"
)
...
...
@@ -137,21 +144,4 @@ public class WorkbenchIntentRecognizer {
return
WorkbenchIntent
.
GENERAL_CHAT
;
}
}
private
boolean
isIrrelevantInput
(
String
message
)
{
if
(!
StringUtils
.
hasText
(
message
))
return
true
;
boolean
isPureNumber
=
message
.
matches
(
"^\\d+$"
);
boolean
isTooShort
=
message
.
length
()
<=
1
;
boolean
hasBizKeyword
=
message
.
contains
(
"室"
)
||
message
.
contains
(
"部门"
)
||
message
.
contains
(
"完成"
)
||
message
.
contains
(
"填报"
)
||
message
.
contains
(
"年度"
)
||
message
.
contains
(
"月度"
)
||
message
.
contains
(
"重点"
)
||
message
.
contains
(
"a"
)
||
message
.
contains
(
"b"
)
||
message
.
contains
(
"c"
);
return
isPureNumber
||
isTooShort
||
!
hasBizKeyword
;
}
}
src/main/java/com/infoepoch/pms/agent/domain/itworkbench/understanding/WorkbenchQueryUnderstandingService.java
View file @
3cbc33a7
This diff is collapsed.
Click to expand it.
src/main/java/com/infoepoch/pms/agent/observability/ai/AiModelInvoker.java
View file @
3cbc33a7
src/main/java/com/infoepoch/pms/agent/properties/ITProperties.java
View file @
3cbc33a7
...
...
@@ -15,4 +15,6 @@ public class ITProperties {
private
String
minutesPath
;
private
String
workPath
;
private
String
authorityPath
;
}
src/main/resources/application.yml
View file @
3cbc33a7
...
...
@@ -62,6 +62,7 @@ pms:
kpi-path
:
/it-workbench/api/functionCallTools/queryKpiList
work-path
:
/it-workbench/api/functionCallTools/queryWorkList
minutes-path
:
/it-workbench/api/functionCallTools/queryMinutesList
authority-path
:
/it-workbench/api/functionCallTools/queryValidAuthority
sso
:
cookie-name
:
pms_agent_sso_token
session-cookie-name
:
pms_agent_session
...
...
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