目录
[项目管理 AI Agent案例DEMO](#项目管理 AI Agent案例DEMO)
[1. Maven 依赖配置 (pom.xml)](#1. Maven 依赖配置 (pom.xml))
[2. 数据模型定义](#2. 数据模型定义)
[3. AI Agent 工具定义](#3. AI Agent 工具定义)
[4. AI Agent 核心](#4. AI Agent 核心)
[5. Spring Boot 服务层](#5. Spring Boot 服务层)
[6. REST API 控制器](#6. REST API 控制器)
[7. 应用主类和配置](#7. 应用主类和配置)
[8. 数据仓库接口](#8. 数据仓库接口)
[9. 前端演示页面 (resources/static/index.html)](#9. 前端演示页面 (resources/static/index.html))
在复杂、动态的环境中,Agent 常常面临大量潜在行动、相互冲突的目标以及有限的资源。如果没有明确的 流程来确定后续行动,Agent 可能会遇到效率低下、操作延迟或无法实现关键目标的问题。优先级排序模式 通过使 Agent 能够根据重要性、紧迫性、依赖关系和既定标准来评估和排序任务、目标或行动,从而解决这 一挑战。这确保了 Agent 将精力集中在最关键的任务上,从而提高有效性和目标一致性。
优先级排序模式概述
Agent 使用优先级排序来有效管理任务、目标和子目标,指导后续行动。这个过程有助于在处理多个需求时 做出明智决策,优先处理重要或紧急的活动,而不是次要的活动。这在资源受限、时间有限以及目标可能冲 突的实际场景中尤为重要。
Agent 优先级排序的基本方面通常涉及几个要素。首先,标准定义建立用于任务评估的规则或指标。这些可 能包括紧急性(任务的时间敏感性)、重要性(对主要目标的影响)、依赖关系(该任务是否是其他任务的前 提条件)、资源可用性(必要工具或信息的就绪状态)、成本/收益分析(努力与预期结果的对比),以及个性 化 Agent 的用户偏好。其次,任务评估涉及根据这些定义的标准评估每个潜在任务,利用从简单规则到 LLM 复杂评分或推理的各种方法。第三,调度或选择逻辑是指基于评估结果选择最优下一步行动或任务序列的算 法,可能利用队列或高级规划组件。最后,动态重新优先级排序允许 Agent 随着情况变化修改优先级,例如 新关键事件的出现或截止日期的临近,确保 Agent 的适应性和响应能力。
优先级排序可以在各个层面进行:选择总体目标(高层次目标优先级排序)、在计划内排序步骤(子任务优 先级排序)或从可用选项中选择下一个即时行动(行动选择)。有效的优先级排序使 Agent 能够展现更智能、 更高效、更稳健的行为,特别是在复杂的多目标环境中。这反映了人类团队的组织方式,其中管理者通过考 虑所有成员的输入来优先处理任务。
实际应用和用例
AI Agent 在实际场景中通过复杂的优先级排序机制,实现高效、及时的决策与响应。以下是其在不同领域的典型运用:
-
智能客服系统
Agent 能够识别并优先响应紧急问题,如服务中断或系统故障,而将常规咨询(如密码找回)置于次要位置。同时,还可根据客户价值动态调整处理顺序。
-
云资源调度
在云计算环境中,AI 根据实时负载与业务关键性,优先为高需求应用分配计算资源,并将非紧急任务(如批量数据处理)安排至低负荷时段,从而实现成本与性能的平衡。
-
自动驾驶决策
车辆系统持续评估环境风险,并依据安全 > 通行效率 > 能耗优化的层级进行实时决策。例如,紧急避障会立即中断当前行驶策略,确保行车安全。
-
量化交易执行
交易机器人综合考量市场波动、风险指标、收益预期及实时资讯,对交易指令进行智能排序与即时执行,确保高优先级交易得以迅速落地。
-
敏捷项目管理
AI 助手根据任务截止时间、依赖关系、团队负载及项目战略价值,动态调整任务优先级,帮助团队聚焦关键工作,推进项目进度。
-
智能安全防御
网络安全 Agent 持续分析威胁情报,从危害程度、潜在影响范围、资产重要性等维度评估告警事件,确保对高危攻击做出第一时间响应。
-
个性化日常助理
个人助理 AI 根据用户设定的事项重要性、时间紧迫度及当前场景,自动安排日程、提醒与通知,帮助用户高效管理时间与事务。
这些示例共同说明了优先级排序能力对于增强 AI Agent 在各种情况下的性能和决策能力是多么基础。
概览
**定义(What):**在复杂环境中运行的 AI Agent 面临大量潜在行动、相互冲突的目标和有限的资源。如果没 有明确的方法来确定下一步行动,这些 Agent 将面临效率低下和效果不佳的风险。这可能导致严重的操作延 迟或完全无法完成主要目标。核心挑战是管理这一压倒性数量的选择,以确保 Agent 有目的性和逻辑性地行 动。
**原因(Why):**优先级排序模式通过使 Agent 能够对任务和目标进行排序,为这个问题提供了标准化的解决 方案。这是通过建立明确的标准(如紧急性、重要性、依赖关系和资源成本)来实现的。然后 Agent 根据这些标准评估每个潜在行动,以确定最关键和最及时的行动方案。这种 Agentic 能力允许系统动态适应不断变 化的环境并有效管理受限资源。通过专注于最高优先级的项目,Agent 的行为变得更加智能、稳健,并与其 战略目标保持一致。
**经验法则(Rule of thumb):**当 Agentic 系统必须在资源约束下自主管理多个(通常是相互冲突的)任务或 目标,以在动态环境中有效运行时,使用优先级排序模式。

图 1:优先级排序设计模式
关键要点
・ 优先级排序使AIAgent能够在复杂的多方面环境中有效运作。
・ Agent 利用既定标准(如紧急性、重要性和依赖关系)来评估和排序任务。
・ 动态重新优先级排序允许Agent根据实时变化调整其操作焦点。
・ 优先级排序发生在各个层面,包括总体战略目标和即时战术决策。
・ 有效的优先级排序可提高AIAgent的效率和操作稳健性。
项目管理 AI Agent案例DEMO
以下演示了使用 LangChain4J 开发项目管理 AI Agent。该 Agent 促进任务的创建、优先级排序和分配给团队成员,说明了 LLM 与定制工具在自动化项目管理中的应用。
我们假设我们要构建一个项目管理AI Agent,它能够:
-
1、创建任务
-
2、为任务设置优先级(基于截止日期、依赖关系、战略重要性等)
-
3、将任务分配给团队成员
我们将使用LangChain4j来构建这个Agent。我们将创建一个Spring Boot应用,并定义一些工具(Tools)来执行项目管理操作。
步骤:
-
1、定义工具:创建任务、设置优先级、分配任务等。
-
2、将这些工具装配给AI Agent。
-
3、使用自然语言与Agent交互,让Agent调用适当的工具。
注意:为了简化,我们不会使用真实的项目管理软件(如Jira),而是使用内存中的数据结构(如列表)来模拟。
我们将创建以下工具:
-
createTaskTool: 创建新任务
-
listTasksTool: 列出所有任务
-
assignTaskTool: 将任务分配给团队成员
-
setTaskPriorityTool: 设置任务优先级
优先级排序逻辑:我们将根据任务的截止日期、依赖关系和战略重要性来计算优先级。这里我们假设优先级分为:高、中、低。
我们还将创建一个AI Service,它将使用这些工具,并根据用户输入决定调用哪个工具。
项目结构
XML
project-management-agent/
├── src/
│ ├── main/java/com/example/pmagent/
│ │ ├── ProjectManagementAgentApplication.java
│ │ ├── agent/
│ │ │ ├── ProjectManagementAgent.java
│ │ │ ├── tools/
│ │ │ │ ├── TaskManagerTool.java
│ │ │ │ ├── PriorityCalculatorTool.java
│ │ │ │ └── TeamAssignmentTool.java
│ │ │ └── memory/
│ │ │ └── ProjectMemory.java
│ │ ├── model/
│ │ │ ├── Task.java
│ │ │ ├── Project.java
│ │ │ ├── TeamMember.java
│ │ │ └── PriorityFactors.java
│ │ ├── service/
│ │ │ └── ProjectService.java
│ │ └── web/
│ │ ├── ProjectController.java
│ │ └── dto/
│ │ ├── TaskRequest.java
│ │ └── ProjectResponse.java
│ └── resources/
│ └── application.yml
├── pom.xml
└── README.md
1. Maven 依赖配置 (pom.xml)
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
</parent>
<groupId>com.example</groupId>
<artifactId>project-management-agent</artifactId>
<version>1.0.0</version>
<name>Project Management AI Agent</name>
<properties>
<java.version>17</java.version>
<langchain4j.version>0.27.1</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- LangChain4j -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- 数据库支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 数据模型定义
2.1 任务实体 (Task.java)
java
package com.example.pmagent.model;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
@Entity
@Table(name = "tasks")
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(length = 2000)
private String description;
@Enumerated(EnumType.STRING)
private TaskStatus status = TaskStatus.BACKLOG;
@Enumerated(EnumType.STRING)
private PriorityLevel priority = PriorityLevel.MEDIUM;
private Double priorityScore; // 计算出的优先级分数
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "project_id")
private Project project;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "assignee_id")
private TeamMember assignee;
private LocalDate dueDate;
private LocalDate startDate;
private Integer estimatedHours;
private Integer actualHours;
@ManyToMany
@JoinTable(
name = "task_dependencies",
joinColumns = @JoinColumn(name = "task_id"),
inverseJoinColumns = @JoinColumn(name = "depends_on_id")
)
private Set<Task> dependencies = new HashSet<>();
@ManyToMany
@JoinTable(
name = "task_blockers",
joinColumns = @JoinColumn(name = "blocked_task_id"),
inverseJoinColumns = @JoinColumn(name = "blocking_task_id")
)
private Set<Task> blockingTasks = new HashSet<>();
private String category;
private String tags;
private Integer businessValue; // 1-10分
@Column(name = "risk_level")
private Integer riskLevel; // 1-5分
@ElementCollection
@CollectionTable(name = "task_requirements", joinColumns = @JoinColumn(name = "task_id"))
@Column(name = "requirement")
private List<String> requirements = new ArrayList<>();
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 枚举定义
public enum TaskStatus {
BACKLOG, TODO, IN_PROGRESS, REVIEW, DONE, BLOCKED, CANCELLED
}
public enum PriorityLevel {
CRITICAL(5), HIGH(4), MEDIUM(3), LOW(2), NONE(1);
private final int weight;
PriorityLevel(int weight) {
this.weight = weight;
}
public int getWeight() {
return weight;
}
}
// 构造器
public Task() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
public Task(String title, String description) {
this();
this.title = title;
this.description = description;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public TaskStatus getStatus() { return status; }
public void setStatus(TaskStatus status) {
this.status = status;
this.updatedAt = LocalDateTime.now();
}
public PriorityLevel getPriority() { return priority; }
public void setPriority(PriorityLevel priority) {
this.priority = priority;
this.updatedAt = LocalDateTime.now();
}
public Double getPriorityScore() { return priorityScore; }
public void setPriorityScore(Double priorityScore) { this.priorityScore = priorityScore; }
public Project getProject() { return project; }
public void setProject(Project project) { this.project = project; }
public TeamMember getAssignee() { return assignee; }
public void setAssignee(TeamMember assignee) {
this.assignee = assignee;
this.updatedAt = LocalDateTime.now();
}
public LocalDate getDueDate() { return dueDate; }
public void setDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
this.updatedAt = LocalDateTime.now();
}
public LocalDate getStartDate() { return startDate; }
public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
public Integer getEstimatedHours() { return estimatedHours; }
public void setEstimatedHours(Integer estimatedHours) { this.estimatedHours = estimatedHours; }
public Integer getActualHours() { return actualHours; }
public void setActualHours(Integer actualHours) { this.actualHours = actualHours; }
public Set<Task> getDependencies() { return dependencies; }
public void setDependencies(Set<Task> dependencies) { this.dependencies = dependencies; }
public Set<Task> getBlockingTasks() { return blockingTasks; }
public void setBlockingTasks(Set<Task> blockingTasks) { this.blockingTasks = blockingTasks; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getTags() { return tags; }
public void setTags(String tags) { this.tags = tags; }
public Integer getBusinessValue() { return businessValue; }
public void setBusinessValue(Integer businessValue) { this.businessValue = businessValue; }
public Integer getRiskLevel() { return riskLevel; }
public void setRiskLevel(Integer riskLevel) { this.riskLevel = riskLevel; }
public List<String> getRequirements() { return requirements; }
public void setRequirements(List<String> requirements) { this.requirements = requirements; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
// 业务方法
public boolean isOverdue() {
return dueDate != null && LocalDate.now().isAfter(dueDate) &&
status != TaskStatus.DONE && status != TaskStatus.CANCELLED;
}
public int getDaysUntilDue() {
if (dueDate == null) return Integer.MAX_VALUE;
return (int) java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), dueDate);
}
public void addDependency(Task task) {
this.dependencies.add(task);
this.updatedAt = LocalDateTime.now();
}
public void addBlocker(Task task) {
this.blockingTasks.add(task);
this.updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
2.2 项目实体 (Project.java)
java
package com.example.pmagent.model;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
@Entity
@Table(name = "projects")
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String description;
@Enumerated(EnumType.STRING)
private ProjectStatus status = ProjectStatus.PLANNING;
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Task> tasks = new ArrayList<>();
@ManyToMany
@JoinTable(
name = "project_team_members",
joinColumns = @JoinColumn(name = "project_id"),
inverseJoinColumns = @JoinColumn(name = "member_id")
)
private Set<TeamMember> teamMembers = new HashSet<>();
private LocalDate startDate;
private LocalDate endDate;
private LocalDate deadline;
private Double budget;
private Double spent;
private String client;
private String department;
@Enumerated(EnumType.STRING)
private ProjectType type;
private Integer strategicImportance; // 1-10分
@ElementCollection
@CollectionTable(name = "project_goals", joinColumns = @JoinColumn(name = "project_id"))
@Column(name = "goal")
private List<String> goals = new ArrayList<>();
@ElementCollection
@CollectionTable(name = "project_risks", joinColumns = @JoinColumn(name = "project_id"))
@MapKeyColumn(name = "risk_name")
@Column(name = "risk_level")
private Map<String, Integer> risks = new HashMap<>();
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 枚举定义
public enum ProjectStatus {
PLANNING, ACTIVE, ON_HOLD, COMPLETED, CANCELLED
}
public enum ProjectType {
PRODUCT_DEVELOPMENT, INFRASTRUCTURE, RESEARCH, MAINTENANCE, EMERGENCY, STRATEGIC
}
// 构造器
public Project() {
this.createdAt = LocalDateTime.now();
this.updatedAt = LocalDateTime.now();
}
public Project(String name, String description) {
this();
this.name = name;
this.description = description;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public ProjectStatus getStatus() { return status; }
public void setStatus(ProjectStatus status) {
this.status = status;
this.updatedAt = LocalDateTime.now();
}
public List<Task> getTasks() { return tasks; }
public void setTasks(List<Task> tasks) { this.tasks = tasks; }
public Set<TeamMember> getTeamMembers() { return teamMembers; }
public void setTeamMembers(Set<TeamMember> teamMembers) { this.teamMembers = teamMembers; }
public LocalDate getStartDate() { return startDate; }
public void setStartDate(LocalDate startDate) { this.startDate = startDate; }
public LocalDate getEndDate() { return endDate; }
public void setEndDate(LocalDate endDate) { this.endDate = endDate; }
public LocalDate getDeadline() { return deadline; }
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
this.updatedAt = LocalDateTime.now();
}
public Double getBudget() { return budget; }
public void setBudget(Double budget) { this.budget = budget; }
public Double getSpent() { return spent; }
public void setSpent(Double spent) { this.spent = spent; }
public String getClient() { return client; }
public void setClient(String client) { this.client = client; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public ProjectType getType() { return type; }
public void setType(ProjectType type) { this.type = type; }
public Integer getStrategicImportance() { return strategicImportance; }
public void setStrategicImportance(Integer strategicImportance) { this.strategicImportance = strategicImportance; }
public List<String> getGoals() { return goals; }
public void setGoals(List<String> goals) { this.goals = goals; }
public Map<String, Integer> getRisks() { return risks; }
public void setRisks(Map<String, Integer> risks) { this.risks = risks; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
// 业务方法
public void addTask(Task task) {
task.setProject(this);
this.tasks.add(task);
this.updatedAt = LocalDateTime.now();
}
public void addTeamMember(TeamMember member) {
this.teamMembers.add(member);
this.updatedAt = LocalDateTime.now();
}
public double getBudgetUtilization() {
if (budget == null || budget == 0) return 0;
return (spent != null ? spent : 0) / budget * 100;
}
public int getDaysUntilDeadline() {
if (deadline == null) return Integer.MAX_VALUE;
return (int) java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), deadline);
}
public int getCompletedTasksCount() {
return (int) tasks.stream()
.filter(task -> task.getStatus() == Task.TaskStatus.DONE)
.count();
}
public double getCompletionPercentage() {
if (tasks.isEmpty()) return 0;
return (double) getCompletedTasksCount() / tasks.size() * 100;
}
@PreUpdate
protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}
}
2.3 团队成员实体 (TeamMember.java)
java
package com.example.pmagent.model;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.*;
@Entity
@Table(name = "team_members")
public class TeamMember {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
private String email;
@Enumerated(EnumType.STRING)
private Role role;
@ElementCollection
@CollectionTable(name = "member_skills", joinColumns = @JoinColumn(name = "member_id"))
@MapKeyColumn(name = "skill_name")
@Column(name = "skill_level")
private Map<String, Integer> skills = new HashMap<>(); // 技能名称 -> 熟练度(1-5)
@ElementCollection
@CollectionTable(name = "member_availability", joinColumns = @JoinColumn(name = "member_id"))
@MapKeyColumn(name = "date")
@Column(name = "hours_available")
private Map<LocalDate, Integer> availability = new HashMap<>(); // 日期 -> 可用小时数
private Integer capacity; // 每周总工时容量
private Integer currentLoad; // 当前工作量(小时)
@ManyToMany(mappedBy = "teamMembers")
private Set<Project> projects = new HashSet<>();
@OneToMany(mappedBy = "assignee")
private Set<Task> assignedTasks = new HashSet<>();
private LocalDate hireDate;
private String department;
// 枚举定义
public enum Role {
PRODUCT_MANAGER, DEVELOPER, QA_ENGINEER, DEVOPS, DESIGNER,
BUSINESS_ANALYST, SCRUM_MASTER, ARCHITECT, TEAM_LEAD
}
// 构造器
public TeamMember() {}
public TeamMember(String name, String email, Role role) {
this.name = name;
this.email = email;
this.role = role;
}
// Getter和Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Role getRole() { return role; }
public void setRole(Role role) { this.role = role; }
public Map<String, Integer> getSkills() { return skills; }
public void setSkills(Map<String, Integer> skills) { this.skills = skills; }
public Map<LocalDate, Integer> getAvailability() { return availability; }
public void setAvailability(Map<LocalDate, Integer> availability) { this.availability = availability; }
public Integer getCapacity() { return capacity; }
public void setCapacity(Integer capacity) { this.capacity = capacity; }
public Integer getCurrentLoad() { return currentLoad; }
public void setCurrentLoad(Integer currentLoad) { this.currentLoad = currentLoad; }
public Set<Project> getProjects() { return projects; }
public void setProjects(Set<Project> projects) { this.projects = projects; }
public Set<Task> getAssignedTasks() { return assignedTasks; }
public void setAssignedTasks(Set<Task> assignedTasks) { this.assignedTasks = assignedTasks; }
public LocalDate getHireDate() { return hireDate; }
public void setHireDate(LocalDate hireDate) { this.hireDate = hireDate; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
// 业务方法
public void addSkill(String skill, int level) {
this.skills.put(skill, Math.min(Math.max(level, 1), 5));
}
public boolean hasSkill(String skill) {
return this.skills.containsKey(skill);
}
public int getSkillLevel(String skill) {
return this.skills.getOrDefault(skill, 0);
}
public void setAvailabilityForDate(LocalDate date, int hours) {
this.availability.put(date, Math.max(0, Math.min(hours, 24)));
}
public int getAvailableHoursForDate(LocalDate date) {
return this.availability.getOrDefault(date, 8); // 默认8小时
}
public double getUtilizationRate() {
if (capacity == null || capacity == 0) return 0;
return (double) (currentLoad != null ? currentLoad : 0) / capacity * 100;
}
public boolean isOverloaded() {
return getUtilizationRate() > 100;
}
public boolean canTakeMoreWork(int additionalHours) {
if (capacity == null) return true;
int current = currentLoad != null ? currentLoad : 0;
return current + additionalHours <= capacity;
}
}
2.4 优先级计算因素 (PriorityFactors.java)
java
package com.example.pmagent.model;
import java.time.LocalDate;
public class PriorityFactors {
// 截止日期紧迫性 (0-1)
private double deadlineUrgency;
// 战略重要性 (0-1)
private double strategicImportance;
// 业务价值 (0-1)
private double businessValue;
// 依赖关系影响 (0-1)
private double dependencyImpact;
// 风险评估 (0-1, 越高越危险)
private double riskLevel;
// 资源可用性 (0-1)
private double resourceAvailability;
// 客户影响 (0-1)
private double customerImpact;
// 技术复杂性 (0-1, 越高越复杂)
private double technicalComplexity;
// 团队技能匹配度 (0-1)
private double skillMatch;
// 权重配置
public static class Weights {
public double deadlineUrgency = 0.25;
public double strategicImportance = 0.20;
public double businessValue = 0.15;
public double dependencyImpact = 0.10;
public double riskLevel = 0.10;
public double resourceAvailability = 0.08;
public double customerImpact = 0.07;
public double technicalComplexity = 0.03;
public double skillMatch = 0.02;
}
// 构造器
public PriorityFactors() {}
// 从任务计算优先级因素
public static PriorityFactors fromTask(Task task, Project project) {
PriorityFactors factors = new PriorityFactors();
// 1. 截止日期紧迫性
if (task.getDueDate() != null) {
int daysUntilDue = task.getDaysUntilDue();
if (daysUntilDue <= 0) {
factors.deadlineUrgency = 1.0; // 已过期
} else if (daysUntilDue <= 3) {
factors.deadlineUrgency = 0.9;
} else if (daysUntilDue <= 7) {
factors.deadlineUrgency = 0.7;
} else if (daysUntilDue <= 14) {
factors.deadlineUrgency = 0.5;
} else if (daysUntilDue <= 30) {
factors.deadlineUrgency = 0.3;
} else {
factors.deadlineUrgency = 0.1;
}
}
// 2. 战略重要性
if (project != null && project.getStrategicImportance() != null) {
factors.strategicImportance = project.getStrategicImportance() / 10.0;
}
// 3. 业务价值
if (task.getBusinessValue() != null) {
factors.businessValue = task.getBusinessValue() / 10.0;
}
// 4. 依赖关系影响
int totalDependencies = task.getDependencies().size() + task.getBlockingTasks().size();
if (totalDependencies > 5) {
factors.dependencyImpact = 1.0;
} else {
factors.dependencyImpact = totalDependencies / 5.0;
}
// 5. 风险评估
if (task.getRiskLevel() != null) {
factors.riskLevel = task.getRiskLevel() / 5.0;
}
// 6. 客户影响 (简化处理)
if (project != null && "PRODUCT_DEVELOPMENT".equals(project.getType().name())) {
factors.customerImpact = 0.8;
} else if (project != null && project.getClient() != null &&
!project.getClient().toLowerCase().contains("internal")) {
factors.customerImpact = 0.6;
}
// 7. 技术复杂性 (简化处理)
if (task.getCategory() != null) {
switch (task.getCategory().toLowerCase()) {
case "architecture":
case "integration":
factors.technicalComplexity = 0.9;
break;
case "feature":
factors.technicalComplexity = 0.5;
break;
case "bugfix":
case "documentation":
factors.technicalComplexity = 0.2;
break;
}
}
return factors;
}
// 计算优先级分数
public double calculatePriorityScore(Weights weights) {
return
deadlineUrgency * weights.deadlineUrgency +
strategicImportance * weights.strategicImportance +
businessValue * weights.businessValue +
dependencyImpact * weights.dependencyImpact +
riskLevel * weights.riskLevel +
resourceAvailability * weights.resourceAvailability +
customerImpact * weights.customerImpact +
technicalComplexity * weights.technicalComplexity +
skillMatch * weights.skillMatch;
}
// 根据分数确定优先级等级
public static Task.PriorityLevel determinePriorityLevel(double score) {
if (score >= 0.8) return Task.PriorityLevel.CRITICAL;
if (score >= 0.6) return Task.PriorityLevel.HIGH;
if (score >= 0.4) return Task.PriorityLevel.MEDIUM;
if (score >= 0.2) return Task.PriorityLevel.LOW;
return Task.PriorityLevel.NONE;
}
// Getter和Setter
public double getDeadlineUrgency() { return deadlineUrgency; }
public void setDeadlineUrgency(double deadlineUrgency) { this.deadlineUrgency = deadlineUrgency; }
public double getStrategicImportance() { return strategicImportance; }
public void setStrategicImportance(double strategicImportance) { this.strategicImportance = strategicImportance; }
public double getBusinessValue() { return businessValue; }
public void setBusinessValue(double businessValue) { this.businessValue = businessValue; }
public double getDependencyImpact() { return dependencyImpact; }
public void setDependencyImpact(double dependencyImpact) { this.dependencyImpact = dependencyImpact; }
public double getRiskLevel() { return riskLevel; }
public void setRiskLevel(double riskLevel) { this.riskLevel = riskLevel; }
public double getResourceAvailability() { return resourceAvailability; }
public void setResourceAvailability(double resourceAvailability) { this.resourceAvailability = resourceAvailability; }
public double getCustomerImpact() { return customerImpact; }
public void setCustomerImpact(double customerImpact) { this.customerImpact = customerImpact; }
public double getTechnicalComplexity() { return technicalComplexity; }
public void setTechnicalComplexity(double technicalComplexity) { this.technicalComplexity = technicalComplexity; }
public double getSkillMatch() { return skillMatch; }
public void setSkillMatch(double skillMatch) { this.skillMatch = skillMatch; }
}
3. AI Agent 工具定义
3.1 任务管理工具 (TaskManagerTool.java)
java
package com.example.pmagent.agent.tools;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
import com.example.pmagent.model.*;
import com.example.pmagent.service.ProjectService;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class TaskManagerTool {
private final ProjectService projectService;
private final PriorityCalculatorTool priorityCalculator;
public TaskManagerTool(ProjectService projectService, PriorityCalculatorTool priorityCalculator) {
this.projectService = projectService;
this.priorityCalculator = priorityCalculator;
}
@Tool("创建新任务")
public String createTask(
@Tool.P("项目ID") Long projectId,
@Tool.P("任务标题") String title,
@Tool.P("任务描述") String description,
@Tool.P("截止日期,格式:yyyy-MM-dd") String dueDateStr,
@Tool.P("预估工时") Integer estimatedHours,
@Tool.P("业务价值评分(1-10)") Integer businessValue,
@Tool.P("风险等级(1-5)") Integer riskLevel,
@Tool.P("任务类别") String category
) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
Task task = new Task(title, description);
task.setProject(project);
if (dueDateStr != null && !dueDateStr.isEmpty()) {
LocalDate dueDate = LocalDate.parse(dueDateStr, DateTimeFormatter.ISO_DATE);
task.setDueDate(dueDate);
}
if (estimatedHours != null) {
task.setEstimatedHours(estimatedHours);
}
if (businessValue != null) {
task.setBusinessValue(Math.min(Math.max(businessValue, 1), 10));
}
if (riskLevel != null) {
task.setRiskLevel(Math.min(Math.max(riskLevel, 1), 5));
}
if (category != null) {
task.setCategory(category);
}
// 计算优先级
priorityCalculator.calculateAndSetTaskPriority(task);
projectService.saveTask(task);
return String.format(
"✅ 任务创建成功!\n" +
"ID: %d\n" +
"标题: %s\n" +
"优先级: %s (分数: %.2f)\n" +
"截止日期: %s\n" +
"状态: %s",
task.getId(),
task.getTitle(),
task.getPriority(),
task.getPriorityScore(),
task.getDueDate() != null ? task.getDueDate().format(DateTimeFormatter.ISO_DATE) : "未设置",
task.getStatus()
);
} catch (Exception e) {
return "创建任务时出错: " + e.getMessage();
}
}
@Tool("更新任务状态")
public String updateTaskStatus(
@Tool.P("任务ID") Long taskId,
@Tool.P("新状态") String status
) {
try {
Task task = projectService.getTaskById(taskId);
if (task == null) {
return "错误:找不到ID为" + taskId + "的任务";
}
Task.TaskStatus newStatus;
try {
newStatus = Task.TaskStatus.valueOf(status.toUpperCase());
} catch (IllegalArgumentException e) {
return "错误:无效的状态值。有效状态: BACKLOG, TODO, IN_PROGRESS, REVIEW, DONE, BLOCKED, CANCELLED";
}
task.setStatus(newStatus);
projectService.saveTask(task);
// 如果任务完成,重新计算依赖任务的优先级
if (newStatus == Task.TaskStatus.DONE) {
updateDependentTasks(task);
}
return String.format("✅ 任务状态已更新为: %s", newStatus);
} catch (Exception e) {
return "更新任务状态时出错: " + e.getMessage();
}
}
@Tool("为任务添加依赖关系")
public String addTaskDependency(
@Tool.P("任务ID") Long taskId,
@Tool.P("依赖的任务ID") Long dependencyId
) {
try {
Task task = projectService.getTaskById(taskId);
Task dependency = projectService.getTaskById(dependencyId);
if (task == null || dependency == null) {
return "错误:找不到指定的任务";
}
if (taskId.equals(dependencyId)) {
return "错误:任务不能依赖自身";
}
// 检查循环依赖
if (hasCircularDependency(task, dependency)) {
return "错误:检测到循环依赖,无法添加";
}
task.addDependency(dependency);
projectService.saveTask(task);
// 重新计算优先级
priorityCalculator.calculateAndSetTaskPriority(task);
return String.format(
"✅ 依赖关系添加成功\n" +
"任务 '%s' 现在依赖于 '%s'\n" +
"任务优先级已重新计算: %s",
task.getTitle(), dependency.getTitle(), task.getPriority()
);
} catch (Exception e) {
return "添加依赖关系时出错: " + e.getMessage();
}
}
@Tool("获取项目任务列表")
public String getProjectTasks(
@Tool.P("项目ID") Long projectId,
@Tool.P("按优先级排序") boolean sortByPriority,
@Tool.P("仅显示未完成的任务") boolean onlyIncomplete
) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
List<Task> tasks = project.getTasks();
if (onlyIncomplete) {
tasks = tasks.stream()
.filter(t -> t.getStatus() != Task.TaskStatus.DONE &&
t.getStatus() != Task.TaskStatus.CANCELLED)
.collect(Collectors.toList());
}
if (sortByPriority) {
tasks.sort((t1, t2) -> {
// 按优先级分数降序排序
Double score1 = t1.getPriorityScore() != null ? t1.getPriorityScore() : 0;
Double score2 = t2.getPriorityScore() != null ? t2.getPriorityScore() : 0;
return score2.compareTo(score1);
});
}
if (tasks.isEmpty()) {
return "该项目暂无任务";
}
StringBuilder result = new StringBuilder();
result.append(String.format("📋 项目 '%s' 的任务列表 (%d 个任务)\n\n",
project.getName(), tasks.size()));
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
result.append(String.format(
"%d. 【%s】%s\n" +
" 优先级: %s (%.2f) | 状态: %s | 截止: %s\n" +
" 负责人: %s | 预估: %d小时\n\n",
i + 1,
task.getPriority(),
task.getTitle(),
task.getPriority(),
task.getPriorityScore() != null ? task.getPriorityScore() : 0.0,
task.getStatus(),
task.getDueDate() != null ?
task.getDueDate().format(DateTimeFormatter.ISO_DATE) : "未设置",
task.getAssignee() != null ? task.getAssignee().getName() : "未分配",
task.getEstimatedHours() != null ? task.getEstimatedHours() : 0
));
}
// 添加统计信息
long overdue = tasks.stream().filter(Task::isOverdue).count();
long blocked = tasks.stream().filter(t -> t.getStatus() == Task.TaskStatus.BLOCKED).count();
result.append(String.format(
"\n📊 统计:\n" +
"• 逾期任务: %d\n" +
"• 被阻塞任务: %d\n" +
"• 项目完成度: %.1f%%",
overdue, blocked, project.getCompletionPercentage()
));
return result.toString();
} catch (Exception e) {
return "获取任务列表时出错: " + e.getMessage();
}
}
@Tool("搜索任务")
public String searchTasks(
@Tool.P("关键词") String keyword,
@Tool.P("状态筛选") String status,
@Tool.P("优先级筛选") String priority
) {
try {
List<Task> tasks = projectService.searchTasks(keyword, status, priority);
if (tasks.isEmpty()) {
return "未找到匹配的任务";
}
StringBuilder result = new StringBuilder();
result.append(String.format("🔍 找到 %d 个匹配的任务:\n\n", tasks.size()));
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
result.append(String.format(
"%d. 【%s】%s\n" +
" 项目: %s | 状态: %s | 截止: %s\n\n",
i + 1,
task.getPriority(),
task.getTitle(),
task.getProject().getName(),
task.getStatus(),
task.getDueDate() != null ?
task.getDueDate().format(DateTimeFormatter.ISO_DATE) : "未设置"
));
}
return result.toString();
} catch (Exception e) {
return "搜索任务时出错: " + e.getMessage();
}
}
// 私有方法
private boolean hasCircularDependency(Task task, Task potentialDependency) {
Set<Task> visited = new HashSet<>();
Queue<Task> queue = new LinkedList<>(potentialDependency.getDependencies());
while (!queue.isEmpty()) {
Task current = queue.poll();
if (current.equals(task)) {
return true;
}
if (!visited.contains(current)) {
visited.add(current);
queue.addAll(current.getDependencies());
}
}
return false;
}
private void updateDependentTasks(Task completedTask) {
// 查找所有依赖此任务的任务
List<Task> allTasks = projectService.getAllTasks();
List<Task> dependentTasks = allTasks.stream()
.filter(t -> t.getDependencies().contains(completedTask))
.collect(Collectors.toList());
// 重新计算这些任务的优先级
for (Task dependent : dependentTasks) {
priorityCalculator.calculateAndSetTaskPriority(dependent);
projectService.saveTask(dependent);
}
}
}
3.2 优先级计算工具 (PriorityCalculatorTool.java)
java
package com.example.pmagent.agent.tools;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
import com.example.pmagent.model.*;
import com.example.pmagent.service.ProjectService;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class PriorityCalculatorTool {
private final ProjectService projectService;
public PriorityCalculatorTool(ProjectService projectService) {
this.projectService = projectService;
}
@Tool("计算并设置任务优先级")
public String calculateAndSetTaskPriority(@Tool.P("任务ID") Long taskId) {
try {
Task task = projectService.getTaskById(taskId);
if (task == null) {
return "错误:找不到ID为" + taskId + "的任务";
}
calculateAndSetTaskPriority(task);
projectService.saveTask(task);
return String.format(
"✅ 任务优先级已重新计算\n" +
"任务: %s\n" +
"新优先级: %s\n" +
"优先级分数: %.2f",
task.getTitle(),
task.getPriority(),
task.getPriorityScore()
);
} catch (Exception e) {
return "计算优先级时出错: " + e.getMessage();
}
}
@Tool("批量重新计算项目任务优先级")
public String recalculateProjectPriorities(@Tool.P("项目ID") Long projectId) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
List<Task> tasks = project.getTasks();
int updatedCount = 0;
for (Task task : tasks) {
if (calculateAndSetTaskPriority(task)) {
projectService.saveTask(task);
updatedCount++;
}
}
return String.format(
"✅ 优先级重新计算完成\n" +
"项目: %s\n" +
"任务总数: %d\n" +
"已更新: %d\n" +
"关键任务数: %d",
project.getName(),
tasks.size(),
updatedCount,
tasks.stream().filter(t -> t.getPriority() == Task.PriorityLevel.CRITICAL).count()
);
} catch (Exception e) {
return "批量计算优先级时出错: " + e.getMessage();
}
}
@Tool("获取项目优先级报告")
public String getPriorityReport(@Tool.P("项目ID") Long projectId) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
List<Task> tasks = project.getTasks();
// 按优先级分组统计
Map<Task.PriorityLevel, Long> priorityCounts = tasks.stream()
.collect(Collectors.groupingBy(Task::getPriority, Collectors.counting()));
// 获取关键任务
List<Task> criticalTasks = tasks.stream()
.filter(t -> t.getPriority() == Task.PriorityLevel.CRITICAL)
.sorted((t1, t2) -> {
Double s1 = t1.getPriorityScore() != null ? t1.getPriorityScore() : 0;
Double s2 = t2.getPriorityScore() != null ? t2.getPriorityScore() : 0;
return s2.compareTo(s1);
})
.collect(Collectors.toList());
// 获取逾期任务
List<Task> overdueTasks = tasks.stream()
.filter(Task::isOverdue)
.filter(t -> t.getStatus() != Task.TaskStatus.DONE &&
t.getStatus() != Task.TaskStatus.CANCELLED)
.collect(Collectors.toList());
StringBuilder result = new StringBuilder();
result.append(String.format("📊 项目 '%s' 优先级报告\n\n", project.getName()));
// 优先级分布
result.append("📈 优先级分布:\n");
for (Task.PriorityLevel level : Task.PriorityLevel.values()) {
long count = priorityCounts.getOrDefault(level, 0L);
result.append(String.format(" %s: %d 个任务\n", level, count));
}
// 关键任务列表
if (!criticalTasks.isEmpty()) {
result.append("\n🔴 关键任务(需要立即处理):\n");
for (int i = 0; i < Math.min(criticalTasks.size(), 5); i++) {
Task task = criticalTasks.get(i);
result.append(String.format(
" %d. %s (分数: %.2f)\n" +
" 负责人: %s | 截止: %s\n",
i + 1,
task.getTitle(),
task.getPriorityScore() != null ? task.getPriorityScore() : 0.0,
task.getAssignee() != null ? task.getAssignee().getName() : "未分配",
task.getDueDate() != null ? task.getDueDate().toString() : "未设置"
));
}
}
// 逾期任务
if (!overdueTasks.isEmpty()) {
result.append("\n⏰ 逾期任务:\n");
for (Task task : overdueTasks) {
int daysOverdue = (int) java.time.temporal.ChronoUnit.DAYS.between(
task.getDueDate(), LocalDate.now());
result.append(String.format(
" • %s (逾期 %d 天)\n",
task.getTitle(),
daysOverdue
));
}
}
// 建议
result.append("\n💡 建议:\n");
if (!criticalTasks.isEmpty()) {
result.append(" 1. 优先处理关键任务,特别是逾期任务\n");
}
if (!overdueTasks.isEmpty()) {
result.append(" 2. 考虑重新评估逾期任务的截止日期\n");
}
if (tasks.stream().filter(t -> t.getAssignee() == null).count() > 0) {
result.append(" 3. 为未分配的任务分配负责人\n");
}
return result.toString();
} catch (Exception e) {
return "生成优先级报告时出错: " + e.getMessage();
}
}
@Tool("优化任务优先级分配")
public String optimizeTaskPriorities(@Tool.P("项目ID") Long projectId) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
List<Task> tasks = project.getTasks();
// 获取当前的关键任务
List<Task> currentCritical = tasks.stream()
.filter(t -> t.getPriority() == Task.PriorityLevel.CRITICAL)
.collect(Collectors.toList());
// 重新计算所有任务的优先级
tasks.forEach(this::calculateAndSetTaskPriority);
// 保存更新
tasks.forEach(projectService::saveTask);
// 获取新的关键任务
List<Task> newCritical = tasks.stream()
.filter(t -> t.getPriority() == Task.PriorityLevel.CRITICAL)
.collect(Collectors.toList());
// 找出变化
Set<Long> oldIds = currentCritical.stream().map(Task::getId).collect(Collectors.toSet());
Set<Long> newIds = newCritical.stream().map(Task::getId).collect(Collectors.toSet());
List<Task> addedCritical = newCritical.stream()
.filter(t -> !oldIds.contains(t.getId()))
.collect(Collectors.toList());
List<Task> removedCritical = currentCritical.stream()
.filter(t -> !newIds.contains(t.getId()))
.collect(Collectors.toList());
StringBuilder result = new StringBuilder();
result.append(String.format("🔄 任务优先级优化完成\n\n"));
result.append(String.format("关键任务变化:\n"));
result.append(String.format(" 之前: %d 个\n", currentCritical.size()));
result.append(String.format(" 现在: %d 个\n\n", newCritical.size()));
if (!addedCritical.isEmpty()) {
result.append("新标记为关键的任务:\n");
for (Task task : addedCritical) {
result.append(String.format(" • %s (分数: %.2f)\n",
task.getTitle(), task.getPriorityScore()));
}
result.append("\n");
}
if (!removedCritical.isEmpty()) {
result.append("不再标记为关键的任务:\n");
for (Task task : removedCritical) {
result.append(String.format(" • %s\n", task.getTitle()));
}
}
// 提供建议
result.append("\n💡 优化建议:\n");
if (!newCritical.isEmpty()) {
Task topTask = newCritical.get(0);
result.append(String.format(
" 1. 立即开始处理最高优先级任务: '%s' (分数: %.2f)\n",
topTask.getTitle(), topTask.getPriorityScore()
));
}
// 检查资源分配
boolean hasOverloaded = tasks.stream()
.filter(t -> t.getAssignee() != null)
.anyMatch(t -> {
TeamMember member = t.getAssignee();
return member.isOverloaded();
});
if (hasOverloaded) {
result.append(" 2. 检测到团队成员过载,考虑重新分配任务\n");
}
return result.toString();
} catch (Exception e) {
return "优化任务优先级时出错: " + e.getMessage();
}
}
// 内部方法 - 计算并设置任务优先级
public boolean calculateAndSetTaskPriority(Task task) {
try {
Project project = task.getProject();
PriorityFactors factors = PriorityFactors.fromTask(task, project);
// 根据项目类型调整权重
PriorityFactors.Weights weights = new PriorityFactors.Weights();
if (project != null) {
switch (project.getType()) {
case EMERGENCY:
weights.deadlineUrgency = 0.35;
weights.riskLevel = 0.25;
break;
case STRATEGIC:
weights.strategicImportance = 0.30;
weights.businessValue = 0.20;
break;
case PRODUCT_DEVELOPMENT:
weights.customerImpact = 0.15;
weights.businessValue = 0.20;
break;
}
}
// 计算分数
double score = factors.calculatePriorityScore(weights);
// 考虑依赖关系
if (!task.getDependencies().isEmpty()) {
// 如果依赖的任务未完成,增加紧迫性
long unfinishedDependencies = task.getDependencies().stream()
.filter(dep -> dep.getStatus() != Task.TaskStatus.DONE)
.count();
if (unfinishedDependencies > 0) {
score *= 0.7; // 降低优先级,因为依赖未完成
} else {
score *= 1.2; // 提高优先级,因为依赖已完成
}
}
// 考虑阻塞状态
if (task.getStatus() == Task.TaskStatus.BLOCKED) {
score *= 0.5; // 被阻塞的任务降低优先级
}
// 确保分数在0-1范围内
score = Math.max(0, Math.min(1, score));
// 设置优先级
task.setPriorityScore(score);
task.setPriority(PriorityFactors.determinePriorityLevel(score));
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
3.3 团队分配工具 (TeamAssignmentTool.java)
java
package com.example.pmagent.agent.tools;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
import com.example.pmagent.model.*;
import com.example.pmagent.service.ProjectService;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class TeamAssignmentTool {
private final ProjectService projectService;
public TeamAssignmentTool(ProjectService projectService) {
this.projectService = projectService;
}
@Tool("为任务分配团队成员")
public String assignTaskToMember(
@Tool.P("任务ID") Long taskId,
@Tool.P("成员ID") Long memberId
) {
try {
Task task = projectService.getTaskById(taskId);
TeamMember member = projectService.getTeamMemberById(memberId);
if (task == null || member == null) {
return "错误:找不到指定的任务或成员";
}
// 检查成员技能匹配度
double skillMatch = calculateSkillMatch(task, member);
// 检查成员工作负载
boolean canTakeWork = member.canTakeMoreWork(
task.getEstimatedHours() != null ? task.getEstimatedHours() : 8);
if (!canTakeMoreWork(task, member)) {
return String.format(
"⚠️ 警告:成员 '%s' 当前工作负载已满 (利用率: %.1f%%)\n" +
"仍然分配?请输入 'force' 参数强制分配",
member.getName(), member.getUtilizationRate()
);
}
// 分配任务
task.setAssignee(member);
// 更新成员工作负载
updateMemberWorkload(member, task.getEstimatedHours() != null ?
task.getEstimatedHours() : 8, true);
projectService.saveTask(task);
projectService.saveTeamMember(member);
return String.format(
"✅ 任务分配成功!\n" +
"任务: %s\n" +
"分配给: %s (%s)\n" +
"技能匹配度: %.1f%%\n" +
"成员当前利用率: %.1f%%",
task.getTitle(),
member.getName(),
member.getRole(),
skillMatch * 100,
member.getUtilizationRate()
);
} catch (Exception e) {
return "分配任务时出错: " + e.getMessage();
}
}
@Tool("智能推荐任务分配")
public String recommendTaskAssignment(
@Tool.P("任务ID") Long taskId,
@Tool.P("考虑技能匹配") boolean considerSkills,
@Tool.P("考虑工作负载") boolean considerWorkload
) {
try {
Task task = projectService.getTaskById(taskId);
if (task == null) {
return "错误:找不到ID为" + taskId + "的任务";
}
Project project = task.getProject();
if (project == null) {
return "错误:任务不属于任何项目";
}
List<TeamMember> candidates = new ArrayList<>(project.getTeamMembers());
if (candidates.isEmpty()) {
return "错误:项目中没有团队成员";
}
// 计算每个候选人的适合度分数
Map<TeamMember, Double> fitnessScores = new HashMap<>();
for (TeamMember member : candidates) {
double score = 0.0;
// 基础分数
score += 0.3;
// 技能匹配度
if (considerSkills) {
double skillMatch = calculateSkillMatch(task, member);
score += skillMatch * 0.4;
}
// 工作负载考虑
if (considerWorkload) {
double availabilityScore = 1.0 - (member.getUtilizationRate() / 100.0);
score += availabilityScore * 0.3;
}
fitnessScores.put(member, score);
}
// 排序候选人
List<Map.Entry<TeamMember, Double>> sortedCandidates = fitnessScores.entrySet()
.stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toList());
// 构建推荐结果
StringBuilder result = new StringBuilder();
result.append(String.format("🤖 任务 '%s' 分配推荐\n\n", task.getTitle()));
for (int i = 0; i < Math.min(sortedCandidates.size(), 3); i++) {
Map.Entry<TeamMember, Double> entry = sortedCandidates.get(i);
TeamMember member = entry.getKey();
double score = entry.getValue();
double skillMatch = calculateSkillMatch(task, member);
double availability = 100 - member.getUtilizationRate();
result.append(String.format(
"%d. %s (%s)\n" +
" 适合度分数: %.2f\n" +
" 技能匹配度: %.1f%%\n" +
" 可用工作负载: %.1f%%\n" +
" 当前任务数: %d\n\n",
i + 1,
member.getName(),
member.getRole(),
score,
skillMatch * 100,
availability,
member.getAssignedTasks().size()
));
}
// 添加分配建议
TeamMember bestCandidate = sortedCandidates.get(0).getKey();
result.append("💡 建议:\n");
result.append(String.format(
"推荐分配给: %s\n" +
"分配命令: assignTaskToMember(%d, %d)",
bestCandidate.getName(),
taskId,
bestCandidate.getId()
));
return result.toString();
} catch (Exception e) {
return "生成分配推荐时出错: " + e.getMessage();
}
}
@Tool("重新平衡团队工作负载")
public String rebalanceTeamWorkload(@Tool.P("项目ID") Long projectId) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
Set<TeamMember> teamMembers = project.getTeamMembers();
List<Task> unassignedTasks = project.getTasks().stream()
.filter(t -> t.getAssignee() == null)
.filter(t -> t.getStatus() != Task.TaskStatus.DONE &&
t.getStatus() != Task.TaskStatus.CANCELLED)
.collect(Collectors.toList());
// 计算当前工作负载
Map<TeamMember, Double> currentLoad = new HashMap<>();
for (TeamMember member : teamMembers) {
currentLoad.put(member, member.getUtilizationRate());
}
// 重新分配未分配的任务
List<String> assignments = new ArrayList<>();
for (Task task : unassignedTasks) {
// 找到最适合的成员
TeamMember bestMember = null;
double bestScore = -1;
for (TeamMember member : teamMembers) {
double score = calculateAssignmentScore(task, member, currentLoad);
if (score > bestScore) {
bestScore = score;
bestMember = member;
}
}
if (bestMember != null) {
// 分配任务
task.setAssignee(bestMember);
projectService.saveTask(task);
// 更新工作负载
double newLoad = currentLoad.get(bestMember) +
(task.getEstimatedHours() != null ? task.getEstimatedHours() : 8) /
(double) bestMember.getCapacity() * 100;
currentLoad.put(bestMember, newLoad);
assignments.add(String.format(
" • %s → %s (匹配度: %.1f%%)",
task.getTitle(),
bestMember.getName(),
calculateSkillMatch(task, bestMember) * 100
));
}
}
// 平衡已分配的任务
List<Task> assignedTasks = project.getTasks().stream()
.filter(t -> t.getAssignee() != null)
.filter(t -> t.getStatus() != Task.TaskStatus.DONE &&
t.getStatus() != Task.TaskStatus.CANCELLED)
.collect(Collectors.toList());
// 找出过载和轻载的成员
List<TeamMember> overloaded = new ArrayList<>();
List<TeamMember> underloaded = new ArrayList<>();
for (TeamMember member : teamMembers) {
double load = currentLoad.get(member);
if (load > 110) {
overloaded.add(member);
} else if (load < 70) {
underloaded.add(member);
}
}
// 重新分配任务以平衡负载
List<String> rebalances = new ArrayList<>();
for (TeamMember overloadedMember : overloaded) {
List<Task> memberTasks = assignedTasks.stream()
.filter(t -> t.getAssignee() != null &&
t.getAssignee().equals(overloadedMember))
.sorted((t1, t2) -> {
// 按优先级排序,先转移低优先级任务
return t1.getPriority().compareTo(t2.getPriority());
})
.collect(Collectors.toList());
for (Task task : memberTasks) {
if (currentLoad.get(overloadedMember) <= 100) break;
// 找到最适合接收此任务的轻载成员
TeamMember bestReceiver = null;
double bestReceiverScore = -1;
for (TeamMember underloadedMember : underloaded) {
double score = calculateAssignmentScore(task, underloadedMember, currentLoad);
if (score > bestReceiverScore) {
bestReceiverScore = score;
bestReceiver = underloadedMember;
}
}
if (bestReceiver != null) {
// 转移任务
task.setAssignee(bestReceiver);
projectService.saveTask(task);
// 更新工作负载
double taskLoad = (task.getEstimatedHours() != null ?
task.getEstimatedHours() : 8) /
(double) overloadedMember.getCapacity() * 100;
currentLoad.put(overloadedMember,
currentLoad.get(overloadedMember) - taskLoad);
currentLoad.put(bestReceiver,
currentLoad.get(bestReceiver) + taskLoad);
rebalances.add(String.format(
" • %s: %s → %s",
task.getTitle(),
overloadedMember.getName(),
bestReceiver.getName()
));
}
}
}
// 构建结果
StringBuilder result = new StringBuilder();
result.append(String.format("⚖️ 团队工作负载重新平衡完成\n\n"));
if (!assignments.isEmpty()) {
result.append("✅ 已分配未分配的任务:\n");
assignments.forEach(a -> result.append(a).append("\n"));
result.append("\n");
}
if (!rebalances.isEmpty()) {
result.append("🔄 已重新平衡过载任务:\n");
rebalances.forEach(r -> result.append(r).append("\n"));
result.append("\n");
}
// 显示最终工作负载
result.append("📊 最终工作负载分布:\n");
for (TeamMember member : teamMembers) {
result.append(String.format(
" • %s: %.1f%%\n",
member.getName(),
currentLoad.get(member)
));
}
return result.toString();
} catch (Exception e) {
return "重新平衡工作负载时出错: " + e.getMessage();
}
}
@Tool("获取团队能力报告")
public String getTeamCapabilityReport(@Tool.P("项目ID") Long projectId) {
try {
Project project = projectService.getProjectById(projectId);
if (project == null) {
return "错误:找不到ID为" + projectId + "的项目";
}
Set<TeamMember> teamMembers = project.getTeamMembers();
// 分析团队技能
Map<String, List<TeamMember>> skillMap = new HashMap<>();
for (TeamMember member : teamMembers) {
for (String skill : member.getSkills().keySet()) {
skillMap.computeIfAbsent(skill, k -> new ArrayList<>()).add(member);
}
}
// 分析项目所需技能
Set<String> requiredSkills = new HashSet<>();
for (Task task : project.getTasks()) {
if (task.getRequirements() != null) {
requiredSkills.addAll(task.getRequirements());
}
if (task.getCategory() != null) {
requiredSkills.add(task.getCategory());
}
}
StringBuilder result = new StringBuilder();
result.append(String.format("👥 团队 '%s' 能力报告\n\n", project.getName()));
// 团队成员概览
result.append("📋 团队成员概览:\n");
for (TeamMember member : teamMembers) {
result.append(String.format(
" • %s (%s)\n" +
" 技能: %s\n" +
" 工作负载: %.1f%% | 任务数: %d\n\n",
member.getName(),
member.getRole(),
String.join(", ",
member.getSkills().entrySet().stream()
.filter(e -> e.getValue() >= 3)
.map(e -> e.getKey() + "(" + e.getValue() + ")")
.collect(Collectors.toList())),
member.getUtilizationRate(),
member.getAssignedTasks().size()
));
}
// 技能覆盖分析
result.append("🎯 项目技能覆盖分析:\n");
for (String skill : requiredSkills) {
List<TeamMember> skilledMembers = skillMap.getOrDefault(skill, new ArrayList<>());
if (!skilledMembers.isEmpty()) {
result.append(String.format(
" ✅ %s: %d 名成员掌握\n",
skill,
skilledMembers.size()
));
} else {
result.append(String.format(
" ❌ %s: 无成员掌握\n",
skill
));
}
}
// 工作负载分析
long overloadedCount = teamMembers.stream()
.filter(m -> m.getUtilizationRate() > 100)
.count();
long underloadedCount = teamMembers.stream()
.filter(m -> m.getUtilizationRate() < 50)
.count();
result.append(String.format("\n⚖️ 工作负载分析:\n"));
result.append(String.format(" 过载成员: %d 人\n", overloadedCount));
result.append(String.format(" 轻载成员: %d 人\n", underloadedCount));
// 建议
result.append("\n💡 建议:\n");
if (overloadedCount > 0) {
result.append(" 1. 考虑为过载成员分配更多资源或重新分配任务\n");
}
if (!requiredSkills.isEmpty() && skillMap.keySet().containsAll(requiredSkills)) {
result.append(" 2. 团队技能覆盖良好\n");
} else {
result.append(" 2. 考虑培训或招聘以填补技能缺口\n");
}
return result.toString();
} catch (Exception e) {
return "生成团队能力报告时出错: " + e.getMessage();
}
}
// 私有辅助方法
private double calculateSkillMatch(Task task, TeamMember member) {
if (task.getRequirements() == null || task.getRequirements().isEmpty()) {
return 0.5; // 默认值
}
double totalScore = 0;
int count = 0;
for (String requirement : task.getRequirements()) {
if (member.hasSkill(requirement)) {
totalScore += member.getSkillLevel(requirement) / 5.0;
}
count++;
}
// 如果任务有类别,也考虑类别匹配
if (task.getCategory() != null && member.hasSkill(task.getCategory())) {
totalScore += member.getSkillLevel(task.getCategory()) / 5.0;
count++;
}
return count > 0 ? totalScore / count : 0;
}
private boolean canTakeMoreWork(Task task, TeamMember member) {
if (member.getCapacity() == null) return true;
int additionalHours = task.getEstimatedHours() != null ?
task.getEstimatedHours() : 8;
int currentLoad = member.getCurrentLoad() != null ?
member.getCurrentLoad() : 0;
return currentLoad + additionalHours <= member.getCapacity();
}
private void updateMemberWorkload(TeamMember member, Integer hours, boolean add) {
if (hours == null) return;
int currentLoad = member.getCurrentLoad() != null ?
member.getCurrentLoad() : 0;
if (add) {
member.setCurrentLoad(currentLoad + hours);
} else {
member.setCurrentLoad(Math.max(0, currentLoad - hours));
}
}
private double calculateAssignmentScore(Task task, TeamMember member,
Map<TeamMember, Double> currentLoad) {
double score = 0.0;
// 技能匹配度 (40%)
double skillMatch = calculateSkillMatch(task, member);
score += skillMatch * 0.4;
// 工作负载平衡 (30%)
double loadScore = 1.0 - (currentLoad.getOrDefault(member, 0.0) / 100.0);
score += Math.max(0, loadScore) * 0.3;
// 角色匹配度 (20%)
if (task.getCategory() != null) {
switch (task.getCategory().toLowerCase()) {
case "development":
case "backend":
case "frontend":
if (member.getRole() == TeamMember.Role.DEVELOPER) {
score += 0.2;
}
break;
case "testing":
case "qa":
if (member.getRole() == TeamMember.Role.QA_ENGINEER) {
score += 0.2;
}
break;
case "design":
if (member.getRole() == TeamMember.Role.DESIGNER) {
score += 0.2;
}
break;
}
}
// 经验因素 (10%)
if (member.getHireDate() != null) {
long monthsExperience = java.time.temporal.ChronoUnit.MONTHS.between(
member.getHireDate(), java.time.LocalDate.now());
double experienceScore = Math.min(monthsExperience / 24.0, 1.0);
score += experienceScore * 0.1;
}
return score;
}
}
4. AI Agent 核心
4.1 项目管理 AI Agent (ProjectManagementAgent.java)
java
package com.example.pmagent.agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import dev.langchain4j.service.spring.AiService;
import com.example.pmagent.agent.tools.*;
@AiService
public interface ProjectManagementAgent {
@SystemMessage("""
你是一个专业的项目管理AI助手,负责协助管理软件开发项目。
你的职责包括:
1. 创建和管理任务
2. 计算和优化任务优先级
3. 分配任务给团队成员
4. 监控项目进度
5. 提供项目洞察和建议
请保持专业、友好的语气,并提供清晰、有用的建议。
在调用工具时,请提供完整的参数信息。
当前日期: {{current_date}}
""")
String chat(@UserMessage String userMessage, @V("current_date") String currentDate);
@SystemMessage("""
你是一个任务管理专家,专门处理任务创建、更新和查询。
请根据用户请求选择适当的工具。
""")
String manageTasks(@UserMessage String userMessage);
@SystemMessage("""
你是一个优先级管理专家,擅长根据截止日期、依赖关系、业务价值等因素计算任务优先级。
请使用优先级计算工具来优化任务排序。
""")
String managePriorities(@UserMessage String userMessage);
@SystemMessage("""
你是一个团队管理专家,负责分配任务、平衡工作负载和优化团队资源。
请根据团队成员的技能和工作负载做出合理的分配决策。
""")
String manageTeam(@UserMessage String userMessage);
@SystemMessage("""
你是一个项目分析专家,能够提供项目洞察、进度报告和风险评估。
请综合分析项目数据,提供有价值的建议。
""")
String analyzeProject(@UserMessage String userMessage);
}
5. Spring Boot 服务层
5.1 项目服务 (ProjectService.java)
java
package com.example.pmagent.service;
import com.example.pmagent.model.*;
import com.example.pmagent.repository.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ProjectService {
private final ProjectRepository projectRepository;
private final TaskRepository taskRepository;
private final TeamMemberRepository teamMemberRepository;
public ProjectService(ProjectRepository projectRepository,
TaskRepository taskRepository,
TeamMemberRepository teamMemberRepository) {
this.projectRepository = projectRepository;
this.taskRepository = taskRepository;
this.teamMemberRepository = teamMemberRepository;
}
// 项目操作
@Transactional
public Project saveProject(Project project) {
return projectRepository.save(project);
}
public Project getProjectById(Long id) {
return projectRepository.findById(id).orElse(null);
}
public List<Project> getAllProjects() {
return projectRepository.findAll();
}
public List<Project> getActiveProjects() {
return projectRepository.findByStatus(Project.ProjectStatus.ACTIVE);
}
// 任务操作
@Transactional
public Task saveTask(Task task) {
return taskRepository.save(task);
}
public Task getTaskById(Long id) {
return taskRepository.findById(id).orElse(null);
}
public List<Task> getAllTasks() {
return taskRepository.findAll();
}
public List<Task> getTasksByProjectId(Long projectId) {
return taskRepository.findByProjectId(projectId);
}
public List<Task> getTasksByAssigneeId(Long assigneeId) {
return taskRepository.findByAssigneeId(assigneeId);
}
public List<Task> getOverdueTasks() {
return taskRepository.findOverdueTasks();
}
public List<Task> searchTasks(String keyword, String status, String priority) {
List<Task> tasks = taskRepository.searchByKeyword(keyword);
if (status != null && !status.isEmpty()) {
try {
Task.TaskStatus taskStatus = Task.TaskStatus.valueOf(status.toUpperCase());
tasks = tasks.stream()
.filter(t -> t.getStatus() == taskStatus)
.collect(Collectors.toList());
} catch (IllegalArgumentException e) {
// 忽略无效的状态值
}
}
if (priority != null && !priority.isEmpty()) {
try {
Task.PriorityLevel priorityLevel = Task.PriorityLevel.valueOf(priority.toUpperCase());
tasks = tasks.stream()
.filter(t -> t.getPriority() == priorityLevel)
.collect(Collectors.toList());
} catch (IllegalArgumentException e) {
// 忽略无效的优先级值
}
}
return tasks;
}
// 团队成员操作
@Transactional
public TeamMember saveTeamMember(TeamMember member) {
return teamMemberRepository.save(member);
}
public TeamMember getTeamMemberById(Long id) {
return teamMemberRepository.findById(id).orElse(null);
}
public List<TeamMember> getAllTeamMembers() {
return teamMemberRepository.findAll();
}
public List<TeamMember> getTeamMembersByProjectId(Long projectId) {
return teamMemberRepository.findByProjectId(projectId);
}
public List<TeamMember> getAvailableTeamMembers() {
return teamMemberRepository.findAvailableMembers();
}
// 项目分析
public Map<String, Object> getProjectAnalytics(Long projectId) {
Project project = getProjectById(projectId);
if (project == null) {
return null;
}
Map<String, Object> analytics = new HashMap<>();
// 基本统计
analytics.put("projectName", project.getName());
analytics.put("totalTasks", project.getTasks().size());
analytics.put("completedTasks", project.getCompletedTasksCount());
analytics.put("completionPercentage", project.getCompletionPercentage());
analytics.put("daysUntilDeadline", project.getDaysUntilDeadline());
analytics.put("budgetUtilization", project.getBudgetUtilization());
// 任务状态分布
Map<Task.TaskStatus, Long> statusDistribution = project.getTasks().stream()
.collect(Collectors.groupingBy(Task::getStatus, Collectors.counting()));
analytics.put("statusDistribution", statusDistribution);
// 优先级分布
Map<Task.PriorityLevel, Long> priorityDistribution = project.getTasks().stream()
.collect(Collectors.groupingBy(Task::getPriority, Collectors.counting()));
analytics.put("priorityDistribution", priorityDistribution);
// 团队成员负载
Map<String, Double> teamWorkload = new HashMap<>();
for (TeamMember member : project.getTeamMembers()) {
teamWorkload.put(member.getName(), member.getUtilizationRate());
}
analytics.put("teamWorkload", teamWorkload);
// 风险分析
List<Task> highRiskTasks = project.getTasks().stream()
.filter(t -> t.getRiskLevel() != null && t.getRiskLevel() >= 4)
.filter(t -> t.getStatus() != Task.TaskStatus.DONE)
.collect(Collectors.toList());
analytics.put("highRiskTasks", highRiskTasks.size());
// 依赖分析
long tasksWithDependencies = project.getTasks().stream()
.filter(t -> !t.getDependencies().isEmpty() || !t.getBlockingTasks().isEmpty())
.count();
analytics.put("tasksWithDependencies", tasksWithDependencies);
return analytics;
}
// 初始化示例数据
@Transactional
public void initSampleData() {
// 创建团队成员
TeamMember alice = new TeamMember("Alice Chen", "alice@example.com",
TeamMember.Role.DEVELOPER);
alice.setCapacity(40);
alice.addSkill("Java", 5);
alice.addSkill("Spring Boot", 4);
alice.addSkill("微服务", 4);
TeamMember bob = new TeamMember("Bob Wang", "bob@example.com",
TeamMember.Role.QA_ENGINEER);
bob.setCapacity(40);
bob.addSkill("测试自动化", 4);
bob.addSkill("性能测试", 3);
TeamMember carol = new TeamMember("Carol Liu", "carol@example.com",
TeamMember.Role.PRODUCT_MANAGER);
carol.setCapacity(40);
carol.addSkill("项目管理", 5);
carol.addSkill("敏捷开发", 4);
teamMemberRepository.saveAll(List.of(alice, bob, carol));
// 创建项目
Project project = new Project("电商平台升级", "升级现有电商平台,支持微服务架构");
project.setStatus(Project.ProjectStatus.ACTIVE);
project.setType(Project.ProjectType.PRODUCT_DEVELOPMENT);
project.setStrategicImportance(8);
project.setStartDate(java.time.LocalDate.now());
project.setDeadline(java.time.LocalDate.now().plusMonths(3));
project.setBudget(100000.0);
project.addTeamMember(alice);
project.addTeamMember(bob);
project.addTeamMember(carol);
projectRepository.save(project);
// 创建示例任务
Task task1 = new Task("设计微服务架构", "设计电商平台的微服务架构方案");
task1.setProject(project);
task1.setDueDate(java.time.LocalDate.now().plusDays(7));
task1.setEstimatedHours(40);
task1.setBusinessValue(9);
task1.setRiskLevel(3);
task1.setCategory("architecture");
task1.setRequirements(List.of("微服务", "架构设计"));
Task task2 = new Task("用户服务开发", "实现用户管理微服务");
task2.setProject(project);
task2.setDueDate(java.time.LocalDate.now().plusDays(14));
task2.setEstimatedHours(60);
task2.setBusinessValue(8);
task2.setRiskLevel(2);
task2.setCategory("backend");
task2.setRequirements(List.of("Java", "Spring Boot"));
Task task3 = new Task("商品服务开发", "实现商品管理微服务");
task3.setProject(project);
task3.setDueDate(java.time.LocalDate.now().plusDays(21));
task3.setEstimatedHours(50);
task3.setBusinessValue(8);
task3.setRiskLevel(2);
task3.setCategory("backend");
task3.setRequirements(List.of("Java", "Spring Boot"));
Task task4 = new Task("订单服务开发", "实现订单管理微服务");
task4.setProject(project);
task4.setDueDate(java.time.LocalDate.now().plusDays(28));
task4.setEstimatedHours(70);
task4.setBusinessValue(9);
task4.setRiskLevel(4);
task4.setCategory("backend");
task4.setRequirements(List.of("Java", "Spring Boot", "事务管理"));
taskRepository.saveAll(List.of(task1, task2, task3, task4));
}
}
6. REST API 控制器
6.1 项目控制器 (ProjectController.java)
java
package com.example.pmagent.web;
import com.example.pmagent.agent.ProjectManagementAgent;
import com.example.pmagent.service.ProjectService;
import com.example.pmagent.web.dto.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
@RestController
@RequestMapping("/api/project")
@CrossOrigin(origins = "*")
public class ProjectController {
private final ProjectManagementAgent projectAgent;
private final ProjectService projectService;
public ProjectController(ProjectManagementAgent projectAgent,
ProjectService projectService) {
this.projectAgent = projectAgent;
this.projectService = projectService;
}
@PostMapping("/chat")
public ResponseEntity<ApiResponse<String>> chatWithAgent(
@RequestBody AgentChatRequest request) {
try {
String currentDate = java.time.LocalDate.now()
.format(DateTimeFormatter.ISO_DATE);
String response = projectAgent.chat(request.getMessage(), currentDate);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("Agent处理失败: " + e.getMessage()));
}
}
@PostMapping("/tasks/create")
public ResponseEntity<ApiResponse<String>> createTask(
@RequestBody CreateTaskRequest request) {
try {
String response = projectAgent.manageTasks(
String.format("创建任务:项目ID=%d, 标题='%s', 描述='%s', 截止日期='%s', 预估工时=%d, 业务价值=%d, 风险等级=%d, 类别='%s'",
request.getProjectId(),
request.getTitle(),
request.getDescription(),
request.getDueDate(),
request.getEstimatedHours(),
request.getBusinessValue(),
request.getRiskLevel(),
request.getCategory()
));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("创建任务失败: " + e.getMessage()));
}
}
@PostMapping("/tasks/{taskId}/assign")
public ResponseEntity<ApiResponse<String>> assignTask(
@PathVariable Long taskId,
@RequestBody AssignTaskRequest request) {
try {
String response = projectAgent.manageTeam(
String.format("为任务ID=%d分配成员ID=%d", taskId, request.getMemberId()));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("分配任务失败: " + e.getMessage()));
}
}
@PostMapping("/tasks/{taskId}/priority/recalculate")
public ResponseEntity<ApiResponse<String>> recalculatePriority(
@PathVariable Long taskId) {
try {
String response = projectAgent.managePriorities(
String.format("重新计算任务ID=%d的优先级", taskId));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("重新计算优先级失败: " + e.getMessage()));
}
}
@PostMapping("/projects/{projectId}/priorities/optimize")
public ResponseEntity<ApiResponse<String>> optimizeProjectPriorities(
@PathVariable Long projectId) {
try {
String response = projectAgent.managePriorities(
String.format("优化项目ID=%d的任务优先级", projectId));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("优化优先级失败: " + e.getMessage()));
}
}
@GetMapping("/projects/{projectId}/analytics")
public ResponseEntity<ApiResponse<Map<String, Object>>> getProjectAnalytics(
@PathVariable Long projectId) {
try {
Map<String, Object> analytics = projectService.getProjectAnalytics(projectId);
if (analytics == null) {
return ResponseEntity.ok(ApiResponse.error("项目不存在"));
}
// 使用Agent进行分析
String analysis = projectAgent.analyzeProject(
String.format("分析项目ID=%d的数据", projectId));
analytics.put("aiAnalysis", analysis);
return ResponseEntity.ok(ApiResponse.success(analytics));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("获取分析数据失败: " + e.getMessage()));
}
}
@PostMapping("/team/{projectId}/rebalance")
public ResponseEntity<ApiResponse<String>> rebalanceTeamWorkload(
@PathVariable Long projectId) {
try {
String response = projectAgent.manageTeam(
String.format("重新平衡项目ID=%d的团队工作负载", projectId));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("重新平衡工作负载失败: " + e.getMessage()));
}
}
@GetMapping("/projects/{projectId}/report")
public ResponseEntity<ApiResponse<String>> getProjectReport(
@PathVariable Long projectId) {
try {
String response = projectAgent.analyzeProject(
String.format("生成项目ID=%d的详细报告", projectId));
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("生成报告失败: " + e.getMessage()));
}
}
@PostMapping("/init-sample")
public ResponseEntity<ApiResponse<String>> initSampleData() {
try {
projectService.initSampleData();
return ResponseEntity.ok(ApiResponse.success("示例数据初始化成功"));
} catch (Exception e) {
return ResponseEntity.ok(ApiResponse.error("初始化失败: " + e.getMessage()));
}
}
@GetMapping("/health")
public ResponseEntity<ApiResponse<String>> health() {
return ResponseEntity.ok(ApiResponse.success("Project Management AI Agent 运行正常"));
}
}
6.2 DTO 类
java
package com.example.pmagent.web.dto;
public class AgentChatRequest {
private String message;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
public class CreateTaskRequest {
private Long projectId;
private String title;
private String description;
private String dueDate;
private Integer estimatedHours;
private Integer businessValue;
private Integer riskLevel;
private String category;
// Getter和Setter
public Long getProjectId() { return projectId; }
public void setProjectId(Long projectId) { this.projectId = projectId; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getDueDate() { return dueDate; }
public void setDueDate(String dueDate) { this.dueDate = dueDate; }
public Integer getEstimatedHours() { return estimatedHours; }
public void setEstimatedHours(Integer estimatedHours) { this.estimatedHours = estimatedHours; }
public Integer getBusinessValue() { return businessValue; }
public void setBusinessValue(Integer businessValue) { this.businessValue = businessValue; }
public Integer getRiskLevel() { return riskLevel; }
public void setRiskLevel(Integer riskLevel) { this.riskLevel = riskLevel; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
}
public class AssignTaskRequest {
private Long memberId;
public Long getMemberId() { return memberId; }
public void setMemberId(Long memberId) { this.memberId = memberId; }
}
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
private ApiResponse(boolean success, String message, T data) {
this.success = success;
this.message = message;
this.data = data;
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, "操作成功", data);
}
public static <T> ApiResponse<T> success(String message, T data) {
return new ApiResponse<>(true, message, data);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, message, null);
}
// Getter和Setter
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}
7. 应用主类和配置
7.1 应用主类 (ProjectManagementAgentApplication.java)
java
package com.example.pmagent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProjectManagementAgentApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectManagementAgentApplication.class, args);
}
}
7.2 LangChain4j 配置
java
package com.example.pmagent.config;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.pmagent.agent.ProjectManagementAgent;
import com.example.pmagent.agent.tools.*;
import java.time.Duration;
@Configuration
public class LangChainConfig {
@Value("${ai.provider:ollama}")
private String aiProvider;
@Value("${ai.openai.api-key:}")
private String openAiApiKey;
@Value("${ai.openai.model:gpt-3.5-turbo}")
private String openAiModel;
@Value("${ai.openai.temperature:0.7}")
private double openAiTemperature;
@Value("${ai.ollama.base-url:http://localhost:11434}")
private String ollamaBaseUrl;
@Value("${ai.ollama.model:llama2}")
private String ollamaModel;
@Bean
public ChatLanguageModel chatLanguageModel() {
switch (aiProvider.toLowerCase()) {
case "openai":
return OpenAiChatModel.builder()
.apiKey(openAiApiKey)
.modelName(openAiModel)
.temperature(openAiTemperature)
.timeout(Duration.ofSeconds(60))
.build();
case "ollama":
return OllamaChatModel.builder()
.baseUrl(ollamaBaseUrl)
.modelName(ollamaModel)
.timeout(Duration.ofSeconds(120))
.build();
default:
throw new IllegalArgumentException("不支持的AI提供者: " + aiProvider);
}
}
@Bean
public ProjectManagementAgent projectManagementAgent(
ChatLanguageModel chatLanguageModel,
TaskManagerTool taskManagerTool,
PriorityCalculatorTool priorityCalculatorTool,
TeamAssignmentTool teamAssignmentTool) {
return AiServices.builder(ProjectManagementAgent.class)
.chatLanguageModel(chatLanguageModel)
.tools(taskManagerTool, priorityCalculatorTool, teamAssignmentTool)
.build();
}
}
7.3 应用配置文件 (application.yml)
XML
server:
port: 8081
servlet:
context-path: /pm-agent
spring:
application:
name: project-management-agent
datasource:
url: jdbc:h2:mem:pmdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
show-sql: false
properties:
hibernate:
format_sql: true
h2:
console:
enabled: true
path: /h2-console
# AI配置
ai:
# 选择AI服务提供者: openai 或 ollama
provider: ollama
# OpenAI配置(可选)
openai:
api-key: ${OPENAI_API_KEY:}
model: gpt-3.5-turbo
temperature: 0.7
# Ollama配置(推荐)
ollama:
base-url: http://localhost:11434
model: llama2
# 日志配置
logging:
level:
com.example.pmagent: DEBUG
dev.langchain4j: INFO
8. 数据仓库接口
java
package com.example.pmagent.repository;
import com.example.pmagent.model.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProjectRepository extends JpaRepository<Project, Long> {
List<Project> findByStatus(Project.ProjectStatus status);
List<Project> findByNameContainingIgnoreCase(String name);
}
@Repository
public interface TaskRepository extends JpaRepository<Task, Long> {
List<Task> findByProjectId(Long projectId);
List<Task> findByAssigneeId(Long assigneeId);
@Query("SELECT t FROM Task t WHERE t.dueDate < CURRENT_DATE " +
"AND t.status NOT IN ('DONE', 'CANCELLED')")
List<Task> findOverdueTasks();
@Query("SELECT t FROM Task t WHERE " +
"LOWER(t.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " +
"LOWER(t.description) LIKE LOWER(CONCAT('%', :keyword, '%'))")
List<Task> searchByKeyword(@Param("keyword") String keyword);
}
@Repository
public interface TeamMemberRepository extends JpaRepository<TeamMember, Long> {
@Query("SELECT DISTINCT tm FROM TeamMember tm JOIN tm.projects p WHERE p.id = :projectId")
List<TeamMember> findByProjectId(@Param("projectId") Long projectId);
@Query("SELECT tm FROM TeamMember tm WHERE " +
"tm.capacity IS NULL OR tm.currentLoad IS NULL OR " +
"tm.currentLoad < tm.capacity * 0.8")
List<TeamMember> findAvailableMembers();
}
9. 前端演示页面 (resources/static/index.html)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目管理 AI Agent 演示系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
header {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 40px;
margin-bottom: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
}
.logo {
display: flex;
align-items: center;
gap: 15px;
}
.logo-icon {
font-size: 40px;
}
h1 {
font-size: 2.8rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem;
color: #666;
max-width: 600px;
}
.ai-status {
background: linear-gradient(135deg, #4CAF50, #45a049);
color: white;
padding: 12px 24px;
border-radius: 50px;
font-weight: bold;
display: flex;
align-items: center;
gap: 10px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.main-content {
display: grid;
grid-template-columns: 300px 1fr;
gap: 30px;
margin-bottom: 30px;
}
.sidebar {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}
.chat-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
.nav-list {
list-style: none;
}
.nav-item {
padding: 15px 20px;
margin-bottom: 10px;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 15px;
font-weight: 500;
}
.nav-item:hover {
background: #f0f2ff;
transform: translateX(5px);
}
.nav-item.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.nav-icon {
font-size: 20px;
width: 30px;
text-align: center;
}
.chat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f2ff;
}
.chat-title {
font-size: 1.8rem;
color: #333;
}
.chat-messages {
flex: 1;
overflow-y: auto;
max-height: 500px;
margin-bottom: 20px;
padding-right: 10px;
}
.message {
margin-bottom: 20px;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.user-message {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 15px 20px;
border-radius: 18px 18px 4px 18px;
max-width: 80%;
margin-left: auto;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
}
.ai-message {
background: #f0f2ff;
color: #333;
padding: 20px;
border-radius: 18px 18px 18px 4px;
max-width: 90%;
margin-right: auto;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border-left: 4px solid #667eea;
}
.ai-message pre {
background: #fff;
padding: 15px;
border-radius: 10px;
overflow-x: auto;
margin: 10px 0;
border: 1px solid #e1e5e9;
}
.chat-input-container {
display: flex;
gap: 15px;
margin-top: 20px;
}
.chat-input {
flex: 1;
padding: 18px 24px;
border: 2px solid #e1e5e9;
border-radius: 15px;
font-size: 1rem;
transition: all 0.3s ease;
}
.chat-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.send-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 0 35px;
border-radius: 15px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 10px;
}
.send-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.send-button:active {
transform: translateY(0);
}
.quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 30px;
}
.quick-action {
background: white;
padding: 25px;
border-radius: 15px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid transparent;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.quick-action:hover {
transform: translateY(-5px);
border-color: #667eea;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.15);
}
.action-icon {
font-size: 30px;
margin-bottom: 15px;
color: #667eea;
}
.action-title {
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.action-desc {
font-size: 0.9rem;
color: #666;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 30px;
}
.dashboard-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
}
.card-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 20px;
}
.card-icon {
font-size: 24px;
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.card-title {
font-size: 1.3rem;
font-weight: 600;
color: #333;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.stat-item {
text-align: center;
padding: 20px;
background: #f8f9ff;
border-radius: 12px;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9rem;
color: #666;
}
.priority-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
margin-right: 8px;
margin-bottom: 8px;
}
.critical { background: #ff4757; color: white; }
.high { background: #ffa502; color: white; }
.medium { background: #2ed573; color: white; }
.low { background: #70a1ff; color: white; }
.none { background: #dfe4ea; color: #666; }
.task-list {
list-style: none;
}
.task-item {
padding: 15px;
margin-bottom: 10px;
background: #f8f9ff;
border-radius: 10px;
border-left: 4px solid #667eea;
cursor: pointer;
transition: all 0.3s ease;
}
.task-item:hover {
transform: translateX(5px);
background: #f0f2ff;
}
.task-title {
font-weight: 600;
margin-bottom: 5px;
}
.task-meta {
font-size: 0.9rem;
color: #666;
display: flex;
justify-content: space-between;
}
footer {
text-align: center;
padding: 30px;
color: rgba(255, 255, 255, 0.8);
font-size: 0.9rem;
margin-top: 50px;
}
.tool-response {
background: #e8f4ff;
border: 1px solid #b3d9ff;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
white-space: pre-wrap;
}
@media (max-width: 1024px) {
.main-content {
grid-template-columns: 1fr;
}
.sidebar {
order: 2;
}
.chat-container {
order: 1;
}
}
@media (max-width: 768px) {
.header-content {
flex-direction: column;
text-align: center;
}
.quick-actions {
grid-template-columns: 1fr;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="header-content">
<div class="logo">
<div class="logo-icon">🤖</div>
<div>
<h1>项目管理 AI Agent</h1>
<p class="subtitle">使用 LangChain4j 实现的智能项目管理助手,支持任务创建、优先级排序和团队分配</p>
</div>
</div>
<div class="ai-status">
<span>🟢</span>
<span>AI 助手在线</span>
</div>
</div>
</header>
<div class="main-content">
<div class="sidebar">
<h3 style="margin-bottom: 20px; color: #333;">导航菜单</h3>
<ul class="nav-list">
<li class="nav-item active" onclick="showSection('chat')">
<span class="nav-icon">💬</span>
<span>智能对话</span>
</li>
<li class="nav-item" onclick="showSection('tasks')">
<span class="nav-icon">📋</span>
<span>任务管理</span>
</li>
<li class="nav-item" onclick="showSection('priorities')">
<span class="nav-icon">🎯</span>
<span>优先级管理</span>
</li>
<li class="nav-item" onclick="showSection('team')">
<span class="nav-icon">👥</span>
<span>团队管理</span>
</li>
<li class="nav-item" onclick="showSection('analytics')">
<span class="nav-icon">📊</span>
<span>项目分析</span>
</li>
<li class="nav-item" onclick="showSection('dashboard')">
<span class="nav-icon">🏠</span>
<span>控制面板</span>
</li>
</ul>
<div style="margin-top: 40px;">
<h4 style="margin-bottom: 15px; color: #333;">快速操作</h4>
<button class="send-button" style="width: 100%; margin-bottom: 10px;" onclick="initializeSampleData()">
初始化示例数据
</button>
<button class="send-button" style="width: 100%; background: #4CAF50;" onclick="testConnection()">
测试连接
</button>
</div>
<div style="margin-top: 30px; padding: 20px; background: #f8f9ff; border-radius: 12px;">
<h4 style="margin-bottom: 10px; color: #333;">📈 系统状态</h4>
<div id="system-status">加载中...</div>
</div>
</div>
<div class="chat-container">
<!-- 聊天界面 -->
<div id="chat-section">
<div class="chat-header">
<h2 class="chat-title">💬 与 AI Agent 对话</h2>
<div style="color: #666;">尝试询问关于项目管理的问题</div>
</div>
<div class="chat-messages" id="chat-messages">
<div class="message ai-message">
👋 你好!我是项目管理 AI 助手。<br><br>
我可以帮助你:
• 创建和管理项目任务
• 智能计算任务优先级
• 优化团队任务分配
• 生成项目分析报告<br><br>
请告诉我你想做什么,或者使用右侧的快速操作按钮。
</div>
</div>
<div class="chat-input-container">
<input type="text" class="chat-input" id="chat-input"
placeholder="输入你的问题或指令..."
onkeypress="if(event.key === 'Enter') sendMessage()">
<button class="send-button" onclick="sendMessage()">
<span>发送</span>
<span>➤</span>
</button>
</div>
</div>
<!-- 任务管理界面 -->
<div id="tasks-section" style="display: none;">
<div class="chat-header">
<h2 class="chat-title">📋 任务管理</h2>
</div>
<div class="quick-actions">
<div class="quick-action" onclick="quickAction('createTask')">
<div class="action-icon">➕</div>
<div class="action-title">创建新任务</div>
<div class="action-desc">添加新任务到项目</div>
</div>
<div class="quick-action" onclick="quickAction('listTasks')">
<div class="action-icon">📝</div>
<div class="action-title">查看任务列表</div>
<div class="action-desc">浏览项目中的所有任务</div>
</div>
<div class="quick-action" onclick="quickAction('updateTaskStatus')">
<div class="action-icon">🔄</div>
<div class="action-title">更新任务状态</div>
<div class="action-desc">更改任务进度状态</div>
</div>
<div class="quick-action" onclick="quickAction('searchTasks')">
<div class="action-icon">🔍</div>
<div class="action-title">搜索任务</div>
<div class="action-desc">根据条件查找任务</div>
</div>
</div>
</div>
<!-- 优先级管理界面 -->
<div id="priorities-section" style="display: none;">
<div class="chat-header">
<h2 class="chat-title">🎯 优先级管理</h2>
</div>
<div class="quick-actions">
<div class="quick-action" onclick="quickAction('recalculatePriority')">
<div class="action-icon">🧮</div>
<div class="action-title">重新计算优先级</div>
<div class="action-desc">基于当前条件更新任务优先级</div>
</div>
<div class="quick-action" onclick="quickAction('optimizePriorities')">
<div class="action-icon">⚡</div>
<div class="action-title">优化优先级分配</div>
<div class="action-desc">智能优化项目任务优先级</div>
</div>
<div class="quick-action" onclick="quickAction('priorityReport')">
<div class="action-icon">📊</div>
<div class="action-title">优先级报告</div>
<div class="action-desc">生成优先级分析报告</div>
</div>
<div class="quick-action" onclick="quickAction('addDependency')">
<div class="action-icon">🔗</div>
<div class="action-title">添加依赖关系</div>
<div class="action-desc">设置任务之间的依赖</div>
</div>
</div>
</div>
<!-- 团队管理界面 -->
<div id="team-section" style="display: none;">
<div class="chat-header">
<h2 class="chat-title">👥 团队管理</h2>
</div>
<div class="quick-actions">
<div class="quick-action" onclick="quickAction('assignTask')">
<div class="action-icon">👤</div>
<div class="action-title">分配任务给成员</div>
<div class="action-desc">将任务分配给团队成员</div>
</div>
<div class="quick-action" onclick="quickAction('recommendAssignment')">
<div class="action-icon">🤖</div>
<div class="action-title">智能分配推荐</div>
<div class="action-desc">AI推荐最佳任务分配方案</div>
</div>
<div class="quick-action" onclick="quickAction('rebalanceWorkload')">
<div class="action-icon">⚖️</div>
<div class="action-title">重新平衡工作负载</div>
<div class="action-desc">优化团队工作量分配</div>
</div>
<div class="quick-action" onclick="quickAction('teamReport')">
<div class="action-icon">📈</div>
<div class="action-title">团队能力报告</div>
<div class="action-desc">分析团队技能和工作负载</div>
</div>
</div>
</div>
<!-- 项目分析界面 -->
<div id="analytics-section" style="display: none;">
<div class="chat-header">
<h2 class="chat-title">📊 项目分析</h2>
</div>
<div class="quick-actions">
<div class="quick-action" onclick="quickAction('projectAnalytics')">
<div class="action-icon">📈</div>
<div class="action-title">项目数据分析</div>
<div class="action-desc">查看项目关键指标</div>
</div>
<div class="quick-action" onclick="quickAction('projectReport')">
<div class="action-icon">📋</div>
<div class="action-title">生成项目报告</div>
<div class="action-desc">生成详细项目分析报告</div>
</div>
<div class="quick-action" onclick="quickAction('riskAnalysis')">
<div class="action-icon">⚠️</div>
<div class="action-title">风险分析</div>
<div class="action-desc">识别和评估项目风险</div>
</div>
<div class="quick-action" onclick="quickAction('dependencyAnalysis')">
<div class="action-icon">🔗</div>
<div class="action-title">依赖关系分析</div>
<div class="action-desc">分析任务依赖关系</div>
</div>
</div>
</div>
<!-- 控制面板 -->
<div id="dashboard-section" style="display: none;">
<div class="chat-header">
<h2 class="chat-title">🏠 项目控制面板</h2>
</div>
<div class="dashboard-grid">
<div class="dashboard-card">
<div class="card-header">
<div class="card-icon">📊</div>
<div>
<div class="card-title">项目概览</div>
<div style="color: #666; font-size: 0.9rem;">电商平台升级项目</div>
</div>
</div>
<div class="stats-grid">
<div class="stat-item">
<div class="stat-value" id="total-tasks">0</div>
<div class="stat-label">总任务数</div>
</div>
<div class="stat-item">
<div class="stat-value" id="completed-tasks">0</div>
<div class="stat-label">已完成</div>
</div>
<div class="stat-item">
<div class="stat-value" id="overdue-tasks">0</div>
<div class="stat-label">逾期任务</div>
</div>
<div class="stat-item">
<div class="stat-value" id="critical-tasks">0</div>
<div class="stat-label">关键任务</div>
</div>
</div>
</div>
<div class="dashboard-card">
<div class="card-header">
<div class="card-icon">🎯</div>
<div>
<div class="card-title">优先级分布</div>
<div style="color: #666; font-size: 0.9rem;">任务优先级统计</div>
</div>
</div>
<div id="priority-chart" style="height: 200px; display: flex; align-items: flex-end; gap: 10px; margin-top: 20px;">
<!-- 优先级图表将通过JS动态生成 -->
</div>
</div>
<div class="dashboard-card">
<div class="card-header">
<div class="card-icon">👥</div>
<div>
<div class="card-title">团队状态</div>
<div style="color: #666; font-size: 0.9rem;">团队成员工作负载</div>
</div>
</div>
<div id="team-workload">
<!-- 团队负载信息将通过JS动态生成 -->
</div>
</div>
<div class="dashboard-card">
<div class="card-header">
<div class="card-icon">🚨</div>
<div>
<div class="card-title">需要关注的
结论
总之,优先级排序模式是有效 Agentic AI 的基石,使系统能够有目的性和智能地应对动态环境的复杂性。它 允许 Agent 自主评估大量相互冲突的任务和目标,对在哪里集中其有限资源做出合理的决策。这种 Agentic 能力超越了简单的任务执行,使系统能够充当主动的战略决策者。通过权衡紧急性、重要性和依赖关系等标 准,Agent 展示了复杂的、类似人类的推理过程。
这种 Agentic 行为的关键特征是动态重新优先级排序,它赋予 Agent 在条件变化时实时调整其焦点的自主 权。如代码示例所示,Agent 解释模糊的请求,自主选择和使用适当的工具,并逻辑地排列其行动以实现其 目标。这种自我管理工作流程的能力是真正的 Agentic 系统与简单的自动化脚本的区别所在。最终,掌握优 先级排序对于创建能够在任何复杂的实际场景中有效可靠地运行的稳健和智能 Agent 至关重要。
参考文献
1、Examining the Security of Artificial Intelligence in Project Management: A Case Study of AI‐driven Project Scheduling and Resource Allocation in Information Systems Projects ; https://www.irejournals.com/paper‐details/1706160
2、AI‐Driven Decision Support Systems in Agile Software Project Management: Enhancing Risk Mitigation and Resource Allocation; https://www.mdpi.com/2079‐8954/13/3/208