JManus 分析
项目概览
- 项目定位:JManus 是 Spring Boot 驱动的多智能体计划执行平台,目标是在企业环境中提供强确定性、可审计的 AI 工作流编排能力。前端采用 Vue3 + Ant Design Vue,提供图形化的计划编排、运行与监控界面。
- 总体形态:单体式 Spring Boot 应用承载全部业务 API、计划执行内核、工具体系与持久化层,并通过静态资源方式托管 Vue 前端;同时兼容 Docker 与 Native Image(GraalVM)场景。
- 核心能力:Plan-Act 模式的计划生成与调度、工具调用(数据库、浏览器、Bash、OCR 等)、执行轨迹记录、对话上下文记忆、MCP(Model Context Protocol)扩展、开放的 HTTP API 以及 Cron 定时。
- 运行依赖:Java 17+、默认 H2 内嵌数据库(可切换 MySQL/PostgreSQL)、外部 LLM 服务(DashScope 等)、可选 Playwright/ChromeDriver。
总体架构蓝图
- 界面层:
ui-vue3子仓负责所有交互式 UI,打包后进入src/main/resources/static并由 Spring Boot 静态资源端点提供。 - 服务层:Spring Boot 暴露
/api/*REST API;高层 API 聚焦计划执行(ManusController)、计划模板、配置与工具编排。 - 执行内核:
planning、runtime、agent、tool、recorder等包构成执行链路,从计划解析、Agent 分发、工具调用到执行记录。 - 数据与配置层:基于 Spring Data JPA 与
Config/Recorder等实体包实现持久化,config包提供动态配置、启动加载与属性映射。 - 外部集成层:
llm负责与 LLM 服务交互,mcp引入 MCP 服务与工具,adapter提供 OpenAI 协议兼容层,deploy目录封装容器化部署脚本。 - 事件与任务调度:
event包提供领域事件总线,cron包装 Spring Scheduling,实现计划的定时执行与监控。
后端架构
核心分层与入口
- 启动入口:
OpenManusSpringBootApplication统一启用 Scheduling、JPA、Component 扫描,并提供 Playwright 初始化脚本开关。
36:44:src/main/java/com/alibaba/cloud/ai/manus/OpenManusSpringBootApplication.java
else {
SpringApplication.run(OpenManusSpringBootApplication.class, args);
}
- **分层结构**:
- `controller`:暴露 REST API,处理任务提交、计划模板、配置与工具操作。
- `service`:封装业务流程(计划生成、工具注册、执行协调、配置同步等)。
- `entity`/`vo`:对应数据库表与前端展示模型,支持计划树、执行记录、配置项等。
- `repository`:Spring Data JPA 仓储接口。
- `runtime` 与 `planning`:执行内核,负责解析计划、分配 Agent、调度工具与记录执行。
- `tool`:工具插件集合,采用 `ToolCallBiFunctionDef` 标准封装工具元数据、输入参数与执行逻辑。
### 计划执行链路
- **计划装配与工具注册**:`PlanningFactory` 聚合浏览器、数据库、Cron 等工具的回调注册,并按 MCP 服务动态扩展工具列表。
207:306:src/main/java/com/alibaba/cloud/ai/manus/planning/PlanningFactory.java
List functionCallbacks = mcpService.getFunctionCallbacks(planId);
for (McpServiceEntity toolCallback : functionCallbacks) {
String serviceGroup = toolCallback.getServiceGroup();
ToolCallback[] tCallbacks = toolCallback.getAsyncMcpToolCallbackProvider().getToolCallbacks();
for (ToolCallback tCallback : tCallbacks) {
toolDefinitions.add(new McpTool(tCallback, serviceGroup, planId, innerStorageService, objectMapper));
}
}
// Create FunctionToolCallback for each tool
for (ToolCallBiFunctionDef> toolDefinition : toolDefinitions) {
- **执行协调**:`PlanningCoordinator` 将计划转换为 `ExecutionContext`,交由 `PlanExecutorFactory` 选择具体执行器,并在执行完成后进入 `PlanFinalizer`。
69:131:src/main/java/com/alibaba/cloud/ai/manus/runtime/service/PlanningCoordinator.java
public CompletableFuture executeByPlan(PlanInterface plan, String rootPlanId,
String parentPlanId, String currentPlanId, String toolcallId, boolean isVueRequest, String uploadKey,
int planDepth) {
try {
// ...
PlanExecutorInterface executor = planExecutorFactory.createExecutor(plan);
CompletableFuture executionFuture = executor.executeAllStepsAsync(context);
return executionFuture.thenCompose(result -> {
try {
PlanExecutionResult processedResult = planFinalizer.handlePostExecution(context, result);
return CompletableFuture.completedFuture(processedResult);
- **执行器设计**:`AbstractPlanExecutor` 控制步骤循环、Agent 执行、上传文件同步、步骤结果记录与中断处理,通过 `LevelBasedExecutorPool` 实现分层线程池调度。
215:299:src/main/java/com/alibaba/cloud/ai/manus/runtime/executor/AbstractPlanExecutor.java
public CompletableFuture executeAllStepsAsync(ExecutionContext context) {
int planDepth = context.getPlanDepth();
ExecutorService executor = levelBasedExecutorPool.getExecutorForLevel(planDepth);
return CompletableFuture.supplyAsync(() -> {
PlanExecutionResult result = new PlanExecutionResult();
PlanInterface plan = context.getPlan();
plan.setCurrentPlanId(context.getCurrentPlanId());
plan.updateStepIndices();
try {
syncUploadedFilesToPlan(context);
List steps = plan.getAllSteps();
recorder.recordPlanExecutionStart(context.getCurrentPlanId(), context.getPlan().getTitle(),
context.getUserRequest(), steps, context.getParentPlanId(), context.getRootPlanId(),
- **后置处理**:`PlanFinalizer` 根据上下文决定生成执行摘要、直接响应或中断提示,同时写入执行记录并交互 LLM 对话记忆。
180:221:src/main/java/com/alibaba/cloud/ai/manus/planning/service/PlanFinalizer.java
public PlanExecutionResult handlePostExecution(ExecutionContext context, PlanExecutionResult result) {
if (context == null || result == null) {
return result;
}
try {
if (isTaskInterrupted(context, result)) {
handleInterruptedTask(context, result);
return result;
}
if (context.isNeedSummary()) {
generateSummary(context, result);
return result;
}
else if (context.getPlan() != null && context.getPlan().isDirectResponse()) {
generateDirectResponse(context, result);
### 工具体系
- **工具抽象**:所有工具实现 `ToolCallBiFunctionDef` 接口,统一提供名称、描述、输入 Schema 与执行回调,可贴合 Model Function Calling。
- **内置工具族群**:
- **浏览器自动化**:`BrowserUseTool` 借助 `ChromeDriverService` 与 Playwright,实现页面抓取与交互。
- **数据库工具**:`DatabaseReadTool`/`DatabaseWriteTool`/`DatabaseMetadataTool` 封装查询与元数据访问,通过 `DataSourceService` 管理连接。
- **文件与存储**:`TextFileService`、`UnifiedDirectoryManager`、OCR 转 Markdown(`MarkdownConverterTool`)等处理文件。
- **Cron 工具**:`CronTool` 将计划注册到 `CronService`,形成计划级定时任务。
- **并行执行**:`ParallelExecutionTool` 触发子计划并发。
- **扩展机制**:
- `tool/howToCreateNewTool.md` 指导自定义工具实现。
- MCP 工具通过 `McpService` 动态加载远端能力。
### 配置与启动
- `config` 包含:
- **属性映射**:`ManusProperties`、`CoordinatorProperties`、`MemoryConfig` 等映射 `application.yml`。
- **动态配置管理**:`ConfigController` + `ConfigService` + `ConfigEntity` 支持 REST 修改配置。
- **启动监听**:`AppStartupListener`、`ConfigAppStartupListener` 导入默认配置、迁移数据库。
- **CORS/JSON/Playwright**:`CorsConfig`、`JacksonConfig`、`PlaywrightConfig`。
- 多环境配置:`src/main/resources` 下提供 `application-{profile}.yml`,`Makefile` 与 `deploy/Dockerfile` 支持本地与容器化构建。
### 事件与调度
- `event` 包定义 `JmanusEvent`、`PlanExceptionEvent`、`JmanusListenerRegister`,在计划异常时被 `ManusController` 缓存并反馈给 API 调用方。
- `cron` 包含计划实体、枚举、调度器与 REST API,实现周期性执行与 Cron 工具的持久化。
- `runtime/service/TaskInterruptionManager` 与数据库表 `RootTaskManagerEntity` 协同,实现执行中断与状态查询。
### 数据持久化与审计
- 计划执行轨迹存储在 `recorder` 包,`NewRepoPlanExecutionRecorder` 写入执行记录(Plan、Agent、Think-Act 层级)。
- `runtime/entity`/`recorder/entity` 结构化定义执行日志、工具调用、内嵌上下文,支持 UI 上的执行树展示。
- 配置信息由 `ConfigRepository`、工具目录由 `CoordinatorToolRepository` 管理;命名空间、用户等业务实体独立在 `namespace`、`user` 包内。
- 日志体系基于 `logback-spring.xml`,同时支持 `ManusProperties` 中的 debug 细节开关。
## 前端架构
### 技术栈与工程化
- **框架组合**:Vue 3(Composition API)+ TypeScript + Vite + Pinia + Vue Router + Ant Design Vue,辅以 `vue3-colorpicker`、`nprogress` 等增强组件。
- **构建与部署**:开发态 `pnpm run dev`,生产态 `pnpm run build` 并将 `ui-vue3/dist` 复制到后端 `static/ui`。`vite.config.ts` 配置静态资源路径与代理(可与后端同域)。
- **国际化与主题**:`base/i18n` 目录整合多语言文案,支持按路由/组件切换;Ant Design 的 `reset.css` 引导统一 UI 基线。
### 状态管理与交互模式
- `stores` 下多个 Pinia Store 管理全局状态:
- `task`:负责任务提交流程、运行状态、停止操作、表单预填。
- `sidebar`、`memory`、`namespace`:分别维护侧边栏折叠、对话记忆、命名空间上下文。
- Store 通过浏览器事件(`window.dispatchEvent`)、`localStorage`(记录引导完成)与 API 调用交织,保证任务跨界面同步。
28:192:ui-vue3/src/stores/task.ts
export const useTaskStore = defineStore('task', () => {
const currentTask = ref(null)
const taskToInput = ref('')
const hasVisitedHome = ref(false)
const setTask = (prompt: string) => {
if (!prompt.trim()) {
return
}
const newTask = {
prompt,
timestamp: Date.now(),
processed: false,
}
currentTask.value = newTask
}
// ...
const stopCurrentTask = async () => {
if (currentTask.value && currentTask.value.isRunning && currentTask.value.planId) {
const { DirectApiService } = await import('@/api/direct-api-service')
await DirectApiService.stopTask(currentTask.value.planId)
currentTask.value.isRunning = false
return true
### API 协议与数据流
- API 层集中在 `src/api`,以 `fetch` 封装 REST 调用,约定 `/api/executor` 等后端路径,统一处理错误与 JSON 解析。
- `CommonApiService` 负责计划详情轮询、Form 表单提交、Prompt 列表获取,避免异常情况下抛出错误导致轮询中断。
22:74:ui-vue3/src/api/common-api-service.ts
public static async getDetails(planId: string): Promise {
try {
const response = await fetch( ${this.BASE_URL}/details/${planId})
if (response.status === 404) {
return null
}
if (!response.ok) {
const errorText = await response.text()
throw new Error(Failed to get detailed information: ${response.status} - ${errorText})
}
const rawText = await response.text()
const data = JSON.parse(rawText)
if (data && typeof data === 'object' && !data.currentPlanId) {
data.currentPlanId = planId
}
return data
} catch (error: unknown) {
console.error('[CommonApiService] Failed to get plan details:', error)
return null
}
`
- 计划执行流程:前端提交任务 → 获取
planId → 使用 Pinia Store 记录状态 → 轮询 /api/executor/details/{planId} 获取执行树 → 若待用户输入,通过 /submit-input/{planId} 填写表单。
任务中断: stopCurrentTask 调用 /api/executor/stopTask/{planId},并更新 Store 状态。
服务交互与关键流程
计划创建到执行
- 入口:
ManusController.executeByToolNameAsync 支持工具名称或模板 ID 调用,自动写入对话记忆、同步上传文件,并触发 PlanningCoordinator。
计划模板: PlanTemplateService 读取最新版本 JSON,通过 IPlanParameterMappingService 进行参数替换(支持 <> 占位符)。
执行记录: PlanExecutionRecorder 全程记录计划 → 步骤 → Think-Act → 工具调用,并在 API 层对接详情查询。
结果生成: PlanFinalizer 决定是否基于 LLM 生成摘要或直接回应;对 Vue 请求默认生成摘要文本,满足 UI 展示。
用户输入与中断
- 表单等待:当工具请求用户输入时,
UserInputService 在 RootTaskManagerEntity 中标记等待状态,ManusController 在详情 API 中合并 UserInputWaitState 返回 UI。
用户提交: /api/executor/submit-input/{planId} 接收 JSON 表单,写回等待队列并唤醒执行器。
中断确认: TaskInterruptionManager 通过数据库状态控制执行线程,在 AbstractPlanExecutor 的循环与 PlanFinalizer 后置处理中判定并返回中断消息。
扩展性与定制化
工具扩展
- 开发者可参考
tool/howToCreateNewTool.md,继承 ToolCallBiFunctionDef 并注册到 Spring 容器或 MCP 服务。
PlanningFactory 在启动时根据 agent.init 配置决定是否注册全量工具,支持按环境裁剪。
MCP 与外部服务
mcp 包对接外部 MCP 服务目录与工具注册,McpService 负责缓存、鉴权与动态生成 McpTool,让计划执行时透明调用远程能力。
LLM 集成通过 LlmService 适配 Spring AI,支持多厂商模型、流式响应与对话记忆策略。
动态 Agent 与子计划
agent 包含 DynamicAgent、ConfigurableDynaAgent 与注解 DynamicAgentDefinition,可在运行时装配不同 Agent 流程。
subplan 模块允许计划执行过程中动态生成子计划,通过 SubplanToolService 注册子计划工具,形成树状执行。
PlanExecutorFactory 可按计划类型(Function 模式、动态工具模式等)选择不同执行器,实现差异化控制策略。
部署与运维
- 构建链路:
Makefile 与 tools/make 模块封装 Maven、前端构建、Docker 镜像制作、CI 检查(codespell、markdownlint、yamllint)。
容器化: deploy/Dockerfile 基于 JDK17,复制后端 Jar 与构建后的前端静态文件,deploy/start.sh 提供启动脚本。
配置管理:通过 /api/config 对外提供动态配置接口;ConfigAppStartupListener 支持启动时加载默认配置模板。
日志与调试: logback-spring.xml 控制日志级别;ManusProperties.debugDetail 可打开 LLM 调试信息;PlanExecutionRecorder 的持久化记录方便线下排错。
Playwright/Chrome 驱动: OpenManusSpringBootApplication 启动参数 playwright-init 可预下载浏览器内核,避免首次运行延迟。
附录:关键类索引
- HTTP API:
runtime/controller/ManusController、planning/controller、config/ConfigController、coordinator/controller/CoordinatorToolController。
执行核心: planning/PlanningFactory、runtime/service/PlanningCoordinator、runtime/executor/*、agent/BaseAgent。
工具体系: tool/*、tool/browse、tool/database、tool/convertToMarkdown、tool/mapreduce。
持久化: recorder/service/PlanExecutionRecorder、runtime/entity、config/entity/ConfigEntity、coordinator/entity/po/CoordinatorToolEntity。
前端入口: ui-vue3/src/main.ts、ui-vue3/src/router、ui-vue3/src/stores、ui-vue3/src/views。
部署脚本: Makefile、deploy/Dockerfile、tools/make/*.mk、tools/scripts/*`。
通过上述架构设计,JManus 形成了“前端编排 → 后端计划执行内核 → 工具与外部服务 → 执行记录与可视化反馈”的闭环。开发者可在保持核心执行链路稳定的前提下,按需扩展工具能力、定制计划模板或替换 LLM 服务,以适配不同业务场景。JManus 架构深度分析:企业级多智能体协作系统的设计思想与实践
引言
JManus 是阿里巴巴基于 Spring AI Alibaba 构建的企业级多智能体协作系统,代表了当前 AI 工程化领域的先进实践。作为 Manus 的 Java 实现版本,JManus 不仅继承了多智能体协作的核心思想,更在架构设计上体现了企业级应用所需的稳定性、可扩展性和可维护性。本文将从架构和设计思想的角度,对 JManus 进行系统性的深度分析。
一、总体架构概览
1.1 架构设计理念
JManus 的架构设计遵循以下核心原则:
- 模块化设计:通过清晰的分层和模块化,确保系统的可维护性和可扩展性
- 插件化扩展:支持动态工具注册和 MCP 协议,实现功能的灵活扩展
- 企业级可靠:提供完整的监控、记录、错误处理和容错机制
- 云原生架构:支持容器化部署和弹性伸缩
- 多智能体协作:实现复杂的智能体间协作和任务编排
1.2 技术栈架构
graph TB
subgraph "前端层"
UI[Vue.js 前端界面]
API[RESTful API]
end
subgraph "应用服务层"
Controller[控制器层]
Service[业务服务层]
Agent[智能体层]
Tool[工具层]
end
subgraph "数据持久层"
JPA[JPA/Hibernate]
DB[(H2/MySQL/PostgreSQL)]
Cache[内存缓存]
end
subgraph "AI 能力层"
LLM[大语言模型服务]
MCP[MCP 协议集成]
Memory[对话记忆管理]
end
subgraph "基础设施层"
SpringBoot[Spring Boot 3.x]
Reactor[WebFlux 响应式]
Docker[容器化]
end
UI --> API
API --> Controller
Controller --> Service
Service --> Agent
Agent --> Tool
Service --> JPA
JPA --> DB
Service --> Cache
Agent --> LLM
Tool --> MCP
LLM --> Memory
Controller --> SpringBoot
SpringBoot --> Reactor
SpringBoot --> Docker
二、多智能体系统架构
2.1 智能体层次结构
JManus 采用分层智能体架构,核心抽象为 BaseAgent:
classDiagram
class BaseAgent {
-currentPlanId: String
-rootPlanId: String
-planDepth: int
-maxSteps: int
-currentStep: int
-llmService: LlmService
-planExecutionRecorder: PlanExecutionRecorder
+run(): AgentExecResult
+step(): AgentExecResult
+getThinkMessage(): Message
+getNextStepWithEnvMessage(): Message
}
class ReActAgent {
+think(): boolean
+act(): AgentExecResult
}
class ConfigurableDynaAgent {
-configuration: Map
+execute(): void
}
class ToolCallAgent {
-toolCallbacks: List~ToolCallback~
+selectTools(): List~ToolCallback~
+executeTools(): ToolExecuteResult
}
BaseAgent <|-- ReActAgent
ReActAgent <|-- ConfigurableDynaAgent
ConfigurableDynaAgent <|-- ToolCallAgent
2.2 ReAct 模式实现
JManus 实现了经典的 ReAct(Reasoning + Acting)模式,通过 ReActAgent 提供思考-行动交替执行的框架:
@Override
public AgentExecResult step() {
try {
boolean shouldAct = think(); // 思考阶段
if (!shouldAct) {
return new AgentExecResult("Thinking complete - no action needed", AgentState.IN_PROGRESS);
}
return act(); // 行动阶段
} catch (TaskInterruptedException e) {
return new AgentExecResult("Agent execution interrupted: " + e.getMessage(), AgentState.INTERRUPTED);
}
}
2.3 智能体状态管理
智能体状态通过 AgentState 枚举管理,支持以下状态:
- IDLE:初始状态
- RUNNING:执行中
- COMPLETED:成功完成
- INTERRUPTED:被中断
- ERROR:执行错误
三、工具系统架构
3.1 工具抽象设计
工具系统基于 AbstractBaseTool 抽象类,提供统一的工具接口:
classDiagram
class AbstractBaseTool {
-currentPlanId: String
-rootPlanId: String
+run(I input): ToolExecuteResult
+apply(I input, ToolContext): ToolExecuteResult
+isSelectable(): boolean
+isReturnDirect(): boolean
}
class BrowserUseTool {
-chromeDriverService: ChromeDriverService
-innerStorageService: SmartContentSavingService
+navigate(): void
+click(): void
+inputText(): void
}
class DatabaseReadTool {
-dataSourceService: DataSourceService
+executeQuery(): List~Map~
+getTableMetadata(): TableMeta
}
class Bash {
-unifiedDirectoryManager: UnifiedDirectoryManager
+executeCommand(): CommandResult
}
class TerminateTool {
+terminate(): void
}
AbstractBaseTool <|-- BrowserUseTool
AbstractBaseTool <|-- DatabaseReadTool
AbstractBaseTool <|-- Bash
AbstractBaseTool <|-- TerminateTool
3.2 工具生命周期管理
工具系统实现了完整的生命周期管理,确保资源的正确分配和释放:
1. 初始化阶段:工具在 PlanningFactory 中注册
2. 执行阶段:工具在智能体执行过程中被调用
3. 清理阶段:通过 clearUp() 方法释放资源
3.3 工具扩展机制
JManus 提供了多种工具扩展方式:
- 内置工具:浏览器操作、数据库访问、文件系统操作等
- MCP 工具:通过 Model Context Protocol 集成外部工具
- 动态工具:支持运行时动态注册和卸载工具
四、规划与执行框架
4.1 计划执行模型
计划执行通过 AbstractPlanExecutor 实现,采用异步执行模式:
sequenceDiagram
participant Client
participant Controller
participant Executor
participant Agent
participant Tool
Client->>Controller: 提交执行请求
Controller->>Executor: 创建执行上下文
Executor->>Executor: 异步执行计划
loop 每个步骤
Executor->>Agent: 创建智能体
Agent->>Agent: 执行思考
Agent->>Tool: 调用工具
Tool-->>Agent: 返回结果
Agent-->>Executor: 返回执行结果
end
Executor-->>Client: 返回最终结果
4.2 层级执行池
为了支持复杂的嵌套计划执行,JManus 实现了 LevelBasedExecutorPool:
- 深度分级:根据计划的嵌套深度分配不同的执行线程池
- 资源隔离:避免深层计划阻塞浅层计划的执行
- 性能优化:提高整体执行效率和响应性
4.3 执行状态管理
执行状态通过 ExecutionContext 管理,包含:
- 计划信息(PlanInterface)
- 执行参数和环境数据
- 文件上传信息
- 父子计划关系
五、MCP(Model Context Protocol)集成
5.1 MCP 架构设计
JManus 原生支持 MCP 协议,通过 McpService 提供服务:
graph LR
subgraph "JManus Core"
MS[McpService]
CM[CacheManager]
CV[ConfigValidator]
end
subgraph "MCP Clients"
MC1[MCP Client 1]
MC2[MCP Client 2]
MC3[MCP Client 3]
end
subgraph "External Services"
ES1[外部服务A]
ES2[外部服务B]
ES3[外部服务C]
end
MS --> CM
MS --> CV
MS --> MC1
MS --> MC2
MS --> MC3
MC1 --> ES1
MC2 --> ES2
MC3 --> ES3
5.2 MCP 配置管理
MCP 配置通过 McpConfigEntity 持久化,支持:
- 多种连接类型:stdio、http、websocket
- 动态配置:运行时修改配置并热加载
- 状态管理:启用/禁用服务状态控制
- 缓存机制:提高服务访问性能
5.3 MCP 工具集成
MCP 工具通过 McpTool 包装器集成到 JManus 工具系统中,实现与内置工具的无缝协作。
六、运行时与执行编排
6.1 运行时架构
运行时系统通过 ManusController 提供统一的执行入口:
graph TB
subgraph "API 层"
GET[/executeByToolNameSync/GET]
POST[/executeByToolNameSync/POST]
POST[/executeByToolNameAsync/POST]
end
subgraph "业务逻辑层"
PC[计划模板服务]
PMS[参数映射服务]
FUS[文件上传服务]
TIS[任务中断服务]
end
subgraph "执行层"
PCO[PlanningCoordinator]
APE[AbstractPlanExecutor]
BA[BaseAgent]
end
GET --> PC
POST --> PC
PC --> PMS
PC --> FUS
PC --> TIS
PC --> PCO
PCO --> APE
APE --> BA
6.2 异步执行模型
JManus 采用 CompletableFuture 实现异步执行,提供:
- 非阻塞执行:客户端提交后立即返回任务ID
- 状态查询:通过任务ID查询执行状态
- 结果获取:支持轮询和回调两种方式获取结果
- 错误处理:完整的异常捕获和错误报告机制
6.3 任务中断机制
通过 TaskInterruptionManager 实现任务中断:
- 数据库驱动:使用数据库状态实现可靠的中断机制
- 多级检查:在智能体、步骤、计划多个层级检查中断状态
- 优雅终止:确保资源正确清理和状态一致性
七、配置与属性管理
7.1 动态配置系统
配置系统基于 ManusProperties 实现,支持运行时动态修改:
@ConfigProperty(
group = "manus",
subGroup = "agent",
key = "maxSteps",
path = "manus.maxSteps",
description = "manus.agent.maxSteps.description",
defaultValue = "200",
inputType = ConfigInputType.NUMBER
)
private volatile Integer maxSteps;
7.2 配置分类管理
配置按功能模块分类管理:
- 智能体配置:最大步数、内存限制、并行工具调用等
- 浏览器配置:无头模式、请求超时等
- MCP 配置:连接超时、重试次数、并发连接数等
- 文件系统配置:外部访问权限、上传限制等
- 图像识别配置:模型名称、DPI、重试次数等
7.3 配置持久化
配置通过 ConfigService 持久化到数据库,支持:
- 多环境支持:开发、测试、生产环境配置隔离
- 版本管理:配置变更历史记录
- 热加载:运行时配置修改立即生效
八、记录与监控能力
8.1 执行记录系统
执行记录通过 PlanExecutionRecorder 接口实现,提供完整的执行轨迹:
graph TD
subgraph "记录层级"
PE[计划执行记录<br/>PlanExecutionRecord]
AE[智能体执行记录<br/>AgentExecutionRecord]
TA[思考行动记录<br/>ThinkActRecord]
AT[工具调用记录<br/>ActToolInfo]
end
PE -->|包含多个| AE
AE -->|包含多个| TA
TA -->|包含多个| AT
8.2 多级记录机制
记录系统实现多级详细记录:
1. 计划级别:记录计划的整体执行状态、开始结束时间、最终结果 2. 智能体级别:记录每个智能体的执行过程、步数、状态变化 3. 思考-行动级别:记录智能体的每次思考和行动过程 4. 工具调用级别:记录每个工具调用的参数、结果、错误信息
8.3 性能监控
集成 Micrometer 观测框架,提供:
- 执行时间统计:各层级执行时间监控
- 资源使用监控:内存、CPU、数据库连接等资源使用
- 错误率统计:各类型错误发生频率
- 吞吐量监控:单位时间处理能力
九、前端与 UI 架构
9.1 前后端分离架构
前端采用 Vue.js 3 构建,通过 RESTful API 与后端交互:
- 现代化 UI:基于 Vue 3 的响应式界面
- 实时通信:WebSocket 支持实时状态更新
- 文件上传:支持大文件分片上传和进度显示
- 国际化支持:中英文双语界面
9.2 用户体验设计
前端设计注重用户体验:
- 引导式配置:新用户配置向导
- 实时反馈:执行状态实时更新
- 错误友好:清晰的错误提示和解决方案
- 响应式布局:适配不同屏幕尺寸
十、数据库与持久化层
10.1 多数据库支持
JManus 支持多种数据库:
- H2:默认嵌入式数据库,适合开发和测试
- MySQL:生产环境推荐,支持高并发
- PostgreSQL:企业级特性支持
10.2 数据模型设计
核心实体包括:
- 计划执行记录:存储计划执行的完整轨迹
- 智能体配置:动态智能体的配置信息
- MCP 配置:外部工具服务配置
- 对话记忆:用户对话历史记录
- 系统配置:运行时配置参数
10.3 数据访问层
使用 Spring Data JPA 实现数据访问:
- Repository 模式:统一的数据访问接口
- 查询优化:关键查询添加索引优化
- 事务管理:确保数据一致性
- 连接池管理:HikariCP 提供高性能连接池
十一、安全与访问控制
11.1 文件系统安全
通过 ManusProperties.getAllowExternalAccess() 控制文件访问:
- 沙箱机制:默认限制在工作目录内操作
- 权限控制:可配置是否允许外部文件访问
- 路径验证:防止路径穿越攻击
11.2 工具执行安全
工具执行采用多层安全机制:
- 参数验证:所有工具参数经过严格验证
- 超时控制:防止工具执行无限等待
- 资源限制:限制内存、CPU 使用
- 错误隔离:工具错误不会影响系统稳定性
11.3 网络安全
- API 安全:RESTful API 支持认证和授权
- 数据加密:敏感数据加密存储
- 传输安全:支持 HTTPS 加密传输
十二、部署与容器化
12.1 Docker 容器化
JManus 提供完整的 Docker 支持,通过 Dockerfile 实现:
# 多阶段构建优化
FROM eclipse-temurin:17-jdk-noble
# 系统依赖安装
RUN apt-get update && apt-get install -y \
ca-certificates curl gnupg \
xvfb x11vnc fluxbox \
fonts-liberation fonts-dejavu-core fonts-noto-cjk \
nodejs npm playwright
# 应用部署
COPY target/jmanus.jar app.jar
COPY deploy/start.sh /app/start.sh
# 环境配置
ENV DISPLAY=:99 \
PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright \
JAVA_OPTS="-Xmx2g -Xms1g -XX:+UseG1GC -XX:+UseContainerSupport"
EXPOSE 18080
ENTRYPOINT ["/app/start.sh"]
12.2 云原生特性
- 健康检查:提供
/actuator/health健康检查端点 - 指标暴露:通过
/actuator/metrics暴露运行指标 - 配置外部化:支持环境变量和配置中心
- 日志聚合:结构化日志输出,便于日志收集
12.3 弹性伸缩
- 水平扩展:无状态设计支持多实例部署
- 负载均衡:支持通过负载均衡器分发请求
- 资源监控:集成 Prometheus 指标暴露
十三、架构优势与创新点
13.1 架构优势
1. 企业级可靠性:
- 完整的错误处理和容错机制
- 多级记录和监控系统
- 数据库驱动的任务状态管理
- 插件化工具系统
- MCP 协议支持
- 动态配置管理
- 异步执行模型
- 层级执行池设计
- 智能缓存机制
- 清晰的架构分层
- 完善的文档和示例
- 便捷的工具开发框架
13.2 技术创新点
1. Func-Agent 模式:提供极高执行确定性的功能智能体模式 2. 层级执行池:根据计划深度分配不同执行资源 3. 数据库驱动中断:可靠的任务中断机制 4. 多智能体协作:复杂的智能体间协作和状态共享 5. MCP 原生集成:无缝集成外部工具和服务
十四、应用场景与最佳实践
14.1 典型应用场景
1. 数据处理自动化:
- 大规模数据清洗和转换
- 复杂的数据分析流程
- 报告生成和分发
- 端到端测试流程
- 多步骤业务场景验证
- 可视化测试报告
- 复杂的业务流程编排
- 多系统数据同步
- 定时任务执行
- 多轮对话管理
- 工具调用和查询
- 个性化响应生成
14.2 最佳实践建议
1. 智能体设计:
- 保持智能体职责单一
- 合理设置最大执行步数
- 实现适当的错误处理
- 遵循工具生命周期管理
- 提供清晰的工具描述
- 实现参数验证和错误处理
- 合理使用异步执行
- 配置适当的执行池大小
- 启用必要的缓存机制
- 配置完整的记录级别
- 设置合理的日志策略
- 建立性能监控告警
十五、未来发展方向
15.1 技术演进方向
1. AI 能力增强:
- 支持更多大语言模型
- 集成多模态 AI 能力
- 实现更智能的任务规划
- 支持分布式智能体部署
- 实现跨节点的任务协调
- 提供高可用和容灾能力
- 支持多人协作编辑
- 实现实时状态同步
- 提供协作权限管理
15.2 生态建设
1. 工具生态:
- 建设工具市场
- 提供工具开发 SDK
- 建立工具认证机制
- 开源社区运营
- 开发者培训认证
- 最佳实践分享
- 参与行业标准制定
- 推动 MCP 协议发展
- 建立互操作性标准
结语
JManus 作为企业级多智能体协作系统的优秀实践,其架构设计体现了现代 AI 工程化的先进理念。通过模块化的架构设计、插件化的扩展机制、企业级的可靠性保障,JManus 为复杂 AI 应用的开发和部署提供了完整的解决方案。
其创新的 Func-Agent 模式、层级执行池设计、数据库驱动中断机制等技术创新,不仅解决了多智能体协作中的关键技术挑战,更为整个行业的发展提供了宝贵的经验和参考。随着 AI 技术的不断发展和应用场景的不断扩展,JManus 必将在企业数字化转型的进程中发挥更加重要的作用。
通过深入分析 JManus 的架构设计,我们可以看到未来 AI 系统的发展方向:更加智能化、更加可靠、更加易用、更加开放。JManus 不仅是一个技术产品,更是 AI 工程化方法论的具体实践,为推动 AI 技术在企业级应用中的落地提供了重要的技术支撑和最佳实践指导。
JManus UI-Vue3 前端架构深度分析:现代化企业级 Vue3 应用的设计思想与实践
引言
JManus UI-Vue3 是 JManus 多智能体协作系统的前端实现,采用 Vue 3 + TypeScript 技术栈构建。作为一个企业级 AI 应用的前端,它不仅需要处理复杂的实时数据流和多步骤执行流程,还要提供优秀的用户体验和可维护的代码架构。本文将从架构和设计思想的角度,对 UI-Vue3 进行系统性的深度分析。
一、总体架构概览
1.1 技术栈架构
graph TB
subgraph "核心框架"
VUE[Vue 3.3+]
TS[TypeScript]
VITE[Vite 5+]
end
subgraph "状态管理"
PINIA[Pinia]
REACTIVE[Vue Reactive]
end
subgraph "UI 组件库"
ANTD[Ant Design Vue]
ICONIFY[Iconify]
MONACO[Monaco Editor]
end
subgraph "工具库"
AXIOS[Axios]
DAYJS[Dayjs]
LODASH[Lodash]
MARKED[Marked]
end
subgraph "构建工具"
ESLINT[ESLint 9+]
PRETTIER[Prettier]
VITEST[Vitest]
CYPRESS[Cypress]
end
VUE --> PINIA
VUE --> ANTD
TS --> VITE
PINIA --> REACTIVE
ANTD --> ICONIFY
AXIOS --> DAYJS
VITE --> ESLINT
ESLINT --> PRETTIER
VITEST --> CYPRESS
1.2 架构设计理念
UI-Vue3 的架构设计遵循以下核心原则:
- 组件化架构:高度模块化的组件设计,支持复用和独立测试
- 响应式状态管理:基于 Pinia 和 Vue 3 Composition API 的状态管理
- 类型安全:全面的 TypeScript 类型定义和接口设计
- 实时交互:WebSocket 和长轮询结合的实时数据更新
- 国际化支持:完整的 i18n 国际化架构
- 可访问性:良好的用户体验和无障碍设计
二、项目结构与模块划分
2.1 目录结构分析
ui-vue3/
├── src/
│ ├── api/ # API 服务层
│ ├── base/ # 基础配置(i18n等)
│ ├── components/ # 通用组件
│ │ ├── chat/ # 聊天组件
│ │ ├── editor/ # 编辑器组件
│ │ ├── file-browser/ # 文件浏览器
│ │ └── ...
│ ├── composables/ # 组合式函数
│ ├── router/ # 路由配置
│ ├── stores/ # 状态管理
│ ├── types/ # TypeScript 类型定义
│ ├── utils/ # 工具函数
│ └── views/ # 页面视图
├── public/ # 静态资源
├── cypress/ # E2E 测试
└── ...
2.2 模块化架构设计
graph LR
subgraph "视图层 Views"
HOME[HomeView]
DIRECT[DirectView]
CONFIG[ConfigView]
end
subgraph "组件层 Components"
CHAT[ChatComponents]
EDITOR[EditorComponents]
FILE[FileComponents]
end
subgraph "状态层 Stores"
TASK[TaskStore]
MEMORY[MemoryStore]
SIDEBAR[SidebarStore]
end
subgraph "API层"
COMMON[CommonApiService]
DIRECT_API[DirectApiService]
PLAN_API[PlanActApiService]
end
subgraph "工具层 Utils"
PLAN_MGR[PlanExecutionManager]
REQUEST[RequestUtils]
I18N[I18nUtils]
end
HOME --> CHAT
DIRECT --> CHAT
CONFIG --> EDITOR
CHAT --> TASK
CHAT --> MEMORY
EDITOR --> SIDEBAR
TASK --> DIRECT_API
MEMORY --> COMMON
SIDEBAR --> PLAN_API
DIRECT_API --> PLAN_MGR
PLAN_MGR --> REQUEST
三、核心架构组件分析
3.1 路由架构设计
路由系统采用 Vue Router 4 的 Hash 模式,通过 createWebHashHistory('/ui') 实现:
const router = createRouter({
history: createWebHashHistory('/ui'),
routes,
})
路由守卫实现了系统初始化检查:
router.beforeEach(async (to, _from, next) => {
// 跳过初始化检查对于初始化页面本身
if (to.path === '/init') {
next()
return
}
try {
// 从服务器检查初始化状态
const response = await fetch('/api/init/status')
const result = await response.json()
if (result.success && !result.initialized) {
// 系统未初始化,重定向到初始化页面
localStorage.removeItem('hasInitialized')
next('/init')
return
}
} catch (error) {
console.warn('Failed to check initialization status:', error)
}
next()
})
3.2 状态管理架构
采用 Pinia 作为状态管理库,设计了多个专门的 Store:
#### 3.2.1 任务状态管理 (TaskStore)
export const useTaskStore = defineStore('task', () => {
const currentTask = ref<TaskPayload | null>(null)
const taskToInput = ref<string>('')
const hasVisitedHome = ref(false)
// 任务状态管理方法
const setTask = (prompt: string) => { /* ... */ }
const markTaskAsProcessed = () => { /* ... */ }
const setTaskRunning = (planId: string) => { /* ... */ }
const stopCurrentTask = async () => { /* ... */ }
return {
currentTask,
taskToInput,
hasVisitedHome,
setTask,
setTaskRunning,
stopCurrentTask,
// ...
}
})
#### 3.2.2 侧边栏状态管理 (SidebarStore)
实现了复杂的模板管理功能:
export class SidebarStore {
// 基础状态
isCollapsed = false
currentTab: TabType = 'list'
// 模板列表相关状态
currentPlanTemplateId: string | null = null
planTemplateList: PlanTemplate[] = []
selectedTemplate: PlanTemplate | null = null
// 配置相关状态
jsonContent = ''
planType = 'dynamic_agent'
generatorPrompt = ''
executionParams = ''
// 计算属性
get sortedTemplates(): PlanTemplate[] { /* ... */ }
get groupedTemplates(): Map<string | null, PlanTemplate[]> { /* ... */ }
get canRollback(): boolean { /* ... */ }
// 操作方法
async loadPlanTemplateList() { /* ... */ }
async selectTemplate(template: PlanTemplate) { /* ... */ }
async saveTemplate() { /* ... */ }
}
#### 3.2.3 内存状态管理 (MemoryStore)
export class MemoryStore {
isCollapsed = false
selectMemoryId = ''
loadMessages = () => {}
intervalId: number | undefined = undefined
toggleSidebar() {
this.isCollapsed = !this.isCollapsed
if (this.isCollapsed) {
this.loadMessages()
this.intervalId = window.setInterval(() => {
this.loadMessages()
}, 3000)
} else {
clearInterval(this.intervalId)
}
}
}
3.3 组合式函数架构 (Composables)
Vue 3 Composition API 的最佳实践,实现了多个可复用的组合式函数:
#### 3.3.1 请求处理组合式 (useRequest)
export function useRequest() {
const loading = ref(false)
const executeRequest = async <T>(
requestFn: () => Promise<ApiResponse<T>>,
successMessage?: string,
errorMessage?: string
): Promise<ApiResponse<T> | null> => {
try {
loading.value = true
const result = await requestFn()
if (result.success && successMessage) {
console.log(successMessage)
} else if (!result.success && errorMessage) {
console.error(errorMessage)
}
return result
} catch (error) {
console.error('Request execution failed:', error)
return null
} finally {
loading.value = false
}
}
return {
loading,
executeRequest,
}
}
#### 3.3.2 聊天消息组合式 (useChatMessages)
export function useChatMessages() {
// 状态
const messages = ref<ChatMessage[]>([])
const isLoading = ref(false)
const streamingMessageId = ref<string | null>(null)
const activeMessageId = ref<string | null>(null)
// 计算属性
const lastMessage = computed(() => {
return messages.value.length > 0 ? messages.value[messages.value.length - 1] : null
})
const isStreaming = computed(() => {
return streamingMessageId.value !== null
})
// 方法
const addMessage = (type: 'user' | 'assistant', content: string, options?: Partial<ChatMessage>) => {
const message: ChatMessage = {
id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
type,
content,
timestamp: new Date(),
isStreaming: false,
...options,
}
messages.value.push(message)
return message
}
const updateMessage = (id: string, updates: Partial<ChatMessage>) => {
const index = messages.value.findIndex(m => m.id === id)
if (index !== -1) {
messages.value[index] = { ...messages.value[index], ...updates }
}
}
return {
messages: readonly(messages),
isLoading,
streamingMessageId: readonly(streamingMessageId),
lastMessage,
isStreaming,
addMessage,
updateMessage,
// ...
}
}
四、核心功能模块分析
4.1 计划执行管理器 (PlanExecutionManager)
这是整个前端应用的核心组件,负责管理复杂的计划执行流程:
export class PlanExecutionManager {
private static instance: PlanExecutionManager | null = null
private readonly POLL_INTERVAL = 5000
// 响应式状态
private state = reactive<ExecutionState>({
activePlanId: null,
lastSequenceSize: 0,
isPolling: false,
pollTimer: null,
})
// 事件回调
private callbacks: EventCallbacks = {}
// 缓存系统
private planExecutionCache = new Map<string, PlanExecutionRecord>()
private uiStateCache = new Map<string, UIStateData>()
// 核心方法
public async handleUserMessageSendRequested(query: string): Promise<void>
public handlePlanExecutionRequested(planId: string, query?: string): void
public initiatePlanExecutionSequence(query: string, planId: string): void
private async pollPlanStatus(): Promise<void>
}
#### 4.1.1 执行流程管理
执行管理器实现了完整的执行生命周期管理:
1. 请求验证:检查输入有效性和系统状态 2. 消息发送:通过 API 发送用户消息到后端 3. 计划初始化:获取计划ID并启动执行序列 4. 轮询监控:定期轮询计划执行状态 5. 结果处理:处理执行结果和错误状态 6. 资源清理:执行完成后的资源清理
#### 4.1.2 缓存系统设计
实现了双层缓存机制:
- 计划执行缓存:存储计划执行记录,支持快速查询
- UI状态缓存:存储用户界面状态,支持状态恢复
/**
* 获取缓存的计划执行记录
*/
getCachedPlanRecord(rootPlanId: string): PlanExecutionRecord | undefined {
return this.planExecutionCache.get(rootPlanId)
}
/**
* 设置缓存的计划执行记录
*/
setCachedPlanRecord(rootPlanId: string, record: PlanExecutionRecord): void {
this.planExecutionCache.set(rootPlanId, record)
console.log(`[PlanExecutionManager] Cached plan execution record for rootPlanId: ${rootPlanId}`)
}
4.2 聊天界面架构
聊天界面采用组件化设计,核心组件包括:
#### 4.2.1 聊天容器 (ChatContainer)
<template>
<div class="chat-container">
<!-- 消息容器 -->
<div class="messages" ref="messagesRef" @scroll="handleScroll" @click="handleMessageContainerClick">
<!-- 消息列表 -->
<ChatMessage
v-for="message in compatibleMessages"
:key="message.id"
:message="message"
:is-streaming="isMessageStreaming(message.id)"
@copy="handleCopyMessage"
@regenerate="handleRegenerateMessage"
@retry="handleRetryMessage"
@step-selected="handleStepSelected"
/>
<!-- 加载指示器 -->
<div v-if="isLoading" class="loading-message">
<div class="loading-content">
<Icon icon="carbon:circle-dash" class="loading-icon" />
<span>{{ $t('chat.processing') }}</span>
</div>
</div>
</div>
<!-- 滚动到底部按钮 -->
<Transition name="scroll-button">
<button
v-if="showScrollToBottom"
class="scroll-to-bottom"
@click="() => scrollToBottom()"
:title="$t('chat.scrollToBottom')"
>
<Icon icon="carbon:chevron-down" />
</button>
</Transition>
</div>
</template>
#### 4.2.2 消息组件架构
消息系统支持多种消息类型:
- 用户消息:用户输入的文本消息
- 助手消息:AI 助手的响应消息
- 执行消息:计划执行的状态消息
- 错误消息:执行过程中的错误信息
4.3 侧边栏架构设计
侧边栏实现了复杂的模板管理功能:
#### 4.3.1 模板组织系统
支持多种组织方式:
// 组织方式:'by_time' | 'by_abc' | 'by_group_time' | 'by_group_abc'
organizationMethod: 'by_time' | 'by_abc' | 'by_group_time' | 'by_group_abc' = 'by_time'
get sortedTemplates(): PlanTemplate[] {
const templates = [...this.planTemplateList]
switch (this.organizationMethod) {
case 'by_time':
return templates.sort((a, b) => {
const timeA = this.parseDateTime(a.updateTime ?? a.createTime)
const timeB = this.parseDateTime(b.updateTime ?? b.createTime)
return timeB.getTime() - timeA.getTime()
})
case 'by_abc':
return templates.sort((a, b) => {
const titleA = (a.title ?? '').toLowerCase()
const titleB = (b.title ?? '').toLowerCase()
return titleA.localeCompare(titleB)
})
case 'by_group_time':
case 'by_group_abc': {
// 分组逻辑处理
const groups = new Map<string, PlanTemplate[]>()
const ungrouped: PlanTemplate[] = []
templates.forEach(template => {
const serviceGroup = this.templateServiceGroups.get(template.id) ?? ''
if (!serviceGroup || serviceGroup === 'default' || serviceGroup === '') {
ungrouped.push(template)
} else {
if (!groups.has(serviceGroup)) {
groups.set(serviceGroup, [])
}
groups.get(serviceGroup)!.push(template)
}
})
// 返回排序后的结果
// ...
}
}
}
#### 4.3.2 版本控制系统
实现了完整的版本管理功能:
- 版本历史:保存模板的多个版本
- 版本回滚:支持回退到之前的版本
- 版本比较:比较不同版本的差异
五、国际化架构设计
5.1 国际化系统架构
采用 Vue I18n 9 实现完整的国际化支持:
export const i18n = createI18n({
legacy: false,
locale: localeConfig.locale,
fallbackLocale: 'en',
messages: {
en: en,
zh: zh,
},
})
5.2 动态语言切换
实现了运行时语言切换功能:
export const changeLanguage = async (locale: string) => {
localStorage.setItem(LOCAL_STORAGE_LOCALE, locale)
i18n.global.locale.value = locale as 'zh' | 'en'
localeConfig.locale = locale
console.log(`Successfully switched frontend language to: ${locale}`)
}
/**
* 初始化期间更改语言并重置所有智能体和提示
*/
export const changeLanguageWithAgentReset = async (locale: string) => {
// 首先更改前端语言
await changeLanguage(locale)
try {
// 重置提示为新语言
const promptResponse = await fetch(`/admin/prompts/switch-language?language=${locale}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
if (promptResponse.ok) {
console.log(`Successfully reset prompts to language: ${locale}`)
}
// 用新语言初始化智能体
const agentResponse = await fetch('/api/agent-management/initialize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ language: locale }),
})
if (agentResponse.ok) {
const result = await agentResponse.json()
console.log(`Successfully initialized agents with language: ${locale}`, result)
}
} catch (error) {
console.error('Error initializing agents and prompts during language change:', error)
throw error
}
}
六、API 架构设计
6.1 API 服务层架构
采用面向对象的 API 服务设计,每个功能模块都有对应的 API 服务类:
// 通用 API 服务
export class CommonApiService {
private static readonly BASE_URL = '/api/executor'
// 获取详细执行记录
public static async getDetails(planId: string): Promise<PlanExecutionRecordResponse>
// 提交用户表单输入
public static async submitFormInput(planId: string, formData: Record<string, unknown>): Promise<Record<string, unknown>>
// 获取所有提示列表
static async getAllPrompts(): Promise<unknown[]>
}
6.2 专门的 API 服务
为不同功能模块提供专门的 API 服务:
- DirectApiService:直接执行模式的 API 服务
- PlanActApiService:计划模板相关的 API 服务
- ToolApiService:工具管理相关的 API 服务
- McpApiService:MCP 配置相关的 API 服务
- ConfigApiService:系统配置相关的 API 服务
6.3 请求处理机制
实现了统一的请求处理机制:
export function useRequest() {
const loading = ref(false)
const executeRequest = async <T>(
requestFn: () => Promise<ApiResponse<T>>,
successMessage?: string,
errorMessage?: string
): Promise<ApiResponse<T> | null> => {
try {
loading.value = true
const result = await requestFn()
// 统一的成功/错误处理
if (result.success && successMessage) {
console.log(successMessage)
} else if (!result.success && errorMessage) {
console.error(errorMessage)
}
return result
} catch (error) {
console.error('Request execution failed:', error)
return null
} finally {
loading.value = false
}
}
return { loading, executeRequest }
}
七、UI/UX 设计架构
7.1 设计语言系统
采用现代化的设计语言:
- 深色主题:主色调为深色系 (#0a0a0a)
- 渐变效果:使用蓝紫色渐变作为主色调
- 毛玻璃效果:backdrop-filter 实现现代化视觉效果
- 动画过渡:平滑的过渡动画提升用户体验
7.2 响应式设计
实现了完整的响应式设计:
@media (max-width: 768px) {
.chat-container {
.messages {
padding: 16px;
}
.scroll-to-bottom {
bottom: 20px;
right: 20px;
width: 36px;
height: 36px;
svg {
font-size: 18px;
}
}
}
}
7.3 可访问性设计
- 键盘导航:完整的键盘操作支持
- 屏幕阅读器:语义化的 HTML 结构
- 高对比度:确保文本可读性
- 焦点管理:清晰的焦点指示器
八、性能优化架构
8.1 构建优化
Vite 配置优化:
export default defineConfig({
base: '/ui',
build: {
outDir: './ui',
sourcemap: true, // 启用 source maps
},
css: {
devSourcemap: true, // 启用 CSS source maps
},
server: {
open: true,
host: true,
proxy: {
'/api': {
target: 'http://localhost:18080',
changeOrigin: true,
},
},
},
})
8.2 运行时优化
- 组件懒加载:路由组件按需加载
- 状态缓存:智能的状态缓存机制
- 虚拟滚动:大量数据时的虚拟滚动
- 防抖节流:用户输入的防抖处理
8.3 内存管理
- 组件卸载清理:及时清理定时器和事件监听
- 缓存大小控制:限制缓存数据的大小
- 垃圾回收优化:避免内存泄漏
九、错误处理与监控架构
9.1 错误处理机制
实现了多层次的错误处理:
try {
const response = await fetch(`${this.BASE_URL}/details/${planId}`)
if (response.status === 404) {
return null
}
if (!response.ok) {
const errorText = await response.text()
throw new Error(`Failed to get detailed information: ${response.status} - ${errorText}`)
}
const rawText = await response.text()
const data = JSON.parse(rawText)
return data
} catch (error: unknown) {
console.error('[CommonApiService] Failed to get plan details:', error)
return null
}
9.2 日志系统
实现了结构化的日志系统:
- 错误日志:记录所有错误信息
- 调试日志:开发阶段的调试信息
- 性能日志:关键操作的性能数据
- 用户行为日志:用户操作轨迹记录
9.3 监控指标
- 性能监控:页面加载时间、API 响应时间
- 错误监控:JavaScript 错误、API 错误
- 用户行为监控:页面访问、功能使用频率
十、测试架构设计
10.1 单元测试
使用 Vitest 进行单元测试:
{
"scripts": {
"test:unit": "vitest",
"test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'"
}
}
10.2 E2E 测试
使用 Cypress 进行端到端测试:
// cypress.config.ts
export default defineConfig({
e2e: {
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}',
baseUrl: 'http://localhost:4173'
}
})
10.3 代码质量工具
- ESLint:代码规范检查
- Prettier:代码格式化
- TypeScript:类型检查
- Vue TSC:Vue 模板类型检查
十一、部署与构建架构
11.1 构建配置
多环境构建支持:
// 支持多种构建模式
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"serve": "vite preview"
}
11.2 部署架构
- 静态资源部署:构建后的静态文件
- CDN 支持:支持 CDN 加速
- 环境变量:多环境配置支持
- 健康检查:部署后的健康状态检查
11.3 容器化支持
Docker 容器化部署:
# 多阶段构建优化
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
十二、架构优势与创新点
12.1 架构优势
1. 现代化技术栈:
- Vue 3 Composition API 的最佳实践
- TypeScript 的完整类型支持
- Vite 的高性能构建
- Pinia 的响应式状态管理
- 模块化的 Store 设计
- 智能的缓存机制
- 高度可复用的组件
- 清晰的组件职责划分
- 组合式函数的最佳实践
- WebSocket 和长轮询结合
- 流畅的实时数据更新
- 优雅的错误处理
- 完整的 i18n 架构
- 运行时语言切换
- 后端语言同步
12.2 技术创新点
1. 执行管理器模式:
- 单例模式的管理器设计
- 事件驱动的架构
- 智能的轮询机制
- 响应式的消息状态管理
- 流式消息处理
- 消息类型扩展性
- 多种组织方式支持
- 分组和排序功能
- 版本控制机制
- 双层缓存设计
- 智能缓存清理
- 内存优化管理
- 多层次的错误捕获
- 用户友好的错误提示
- 自动恢复机制
十三、性能与用户体验优化
13.1 性能优化策略
1. 组件渲染优化:
- 使用
v-show替代v-if对于频繁切换的元素 - 合理使用
computed和watch - 避免不必要的组件重新渲染
- API 请求的防抖处理
- 数据的本地缓存
- 分页和懒加载机制
- 组件的异步加载
- 图片的懒加载
- 字体和图标的优化
13.2 用户体验优化
1. 交互反馈:
- 加载状态的清晰指示
- 操作成功的即时反馈
- 错误信息的友好展示
- 现代化的 UI 设计
- 流畅的动画过渡
- 响应式布局适配
- 键盘导航支持
- 屏幕阅读器兼容
- 高对比度模式
十四、未来发展方向
14.1 技术演进方向
1. 性能提升:
- 更智能的缓存策略
- 更高效的渲染机制
- 更好的内存管理
- 更多的交互模式
- 更丰富的可视化组件
- 更强大的编辑功能
- 微前端架构支持
- 服务端渲染(SSR)
- 渐进式Web应用(PWA)
14.2 生态建设
1. 组件库建设:
- 通用的组件库
- 主题定制系统
- 插件扩展机制
- 可视化开发工具
- 自动化测试工具
- 性能分析工具
- 开源社区运营
- 最佳实践分享
- 开发者培训
结语
JManus UI-Vue3 作为现代化企业级 Vue3 应用的优秀实践,其架构设计体现了当前前端开发的最佳实践。通过组件化架构、响应式状态管理、实时交互能力、国际化支持等核心特性,为用户提供了优秀的使用体验。
其创新的执行管理器模式、智能的缓存策略、完善的错误处理机制等技术亮点,不仅解决了复杂 AI 应用的前端挑战,更为整个前端开发领域提供了宝贵的经验和参考。随着技术的不断发展和用户需求的不断变化,UI-Vue3 必将在企业级前端应用开发中发挥更加重要的作用。
通过深入分析 UI-Vue3 的架构设计,我们可以看到现代前端应用的发展方向:更加组件化、更加响应式、更加用户友好、更加可维护。UI-Vue3 不仅是一个技术产品,更是前端工程化方法论的具体实践,为推动前端技术在企业级应用中的落地提供了重要的技术支撑和最佳实践指导。
JManus 项目编译与启动完整指南
📋 前置要求
在开始之前,请确保您的系统满足以下要求:
1. 系统环境要求
- 操作系统:Windows 10/11、macOS 10.15+ 或 Linux (Ubuntu 18.04+)
- 内存:至少 8GB RAM(推荐 16GB)
- 磁盘空间:至少 5GB 可用空间
2. 软件依赖
#### Java 环境
- Java JDK:17 或更高版本
- Maven:3.8+(项目已包含 Maven Wrapper)
- Node.js:18.x 或更高版本
- pnpm:包管理器(推荐)
- Git:用于克隆代码仓库
- Docker:用于容器化部署(可选)
🚀 快速启动方法(推荐)
方法 1:使用预编译 JAR 文件(最简单)
# 1. 下载最新版本的 JAR 文件
wget https://github.com/spring-ai-alibaba/JManus/releases/latest/download/jmanus.jar
# 或者使用 curl
curl -L -o jmanus.jar https://github.com/spring-ai-alibaba/JManus/releases/latest/download/jmanus.jar
# 2. 直接运行
java -jar jmanus.jar
# 3. 访问应用
# 打开浏览器访问 http://localhost:18080
方法 2:使用 Docker(推荐)
# 1. 确保已安装 Docker
docker --version
# 2. 运行容器(自动下载镜像)
docker run -d -p 18080:18080 --name jmanus springai/jmanus:latest
# 3. 访问应用
# 打开浏览器访问 http://localhost:18080
🔧 从源码编译启动
步骤 1:环境准备
#### 安装 Java 17+
# 检查 Java 版本
java -version
# 如果未安装,请下载安装:
# Windows: https://adoptium.net/
# macOS: brew install openjdk@17
# Linux: sudo apt install openjdk-17-jdk
#### 安装 Node.js 18+
# 检查 Node.js 版本
node --version
# 如果未安装,请下载安装:
# https://nodejs.org/ 下载 18.x 版本
# 安装 pnpm(推荐)
npm install -g pnpm
# 或者使用 npm
npm install -g npm@latest
步骤 2:获取源码
# 克隆代码仓库
git clone https://github.com/spring-ai-alibaba/JManus.git
cd JManus
# 或者下载 ZIP 包并解压
# wget https://github.com/spring-ai-alibaba/JManus/archive/refs/heads/main.zip
# unzip main.zip
# cd JManus-main
步骤 3:编译后端(Java)
#### 选项 A:使用 Maven Wrapper(推荐)
# 进入项目目录
cd JManus
# 编译项目(跳过测试以加快速度)
./mvnw clean package -DskipTests
# 或者使用 Maven(如果已安装)
mvn clean package -DskipTests
#### 选项 B:使用 Makefile
# 编译 Java 后端
make build
# 或者分别执行
make java-build
步骤 4:编译前端(Vue3)
#### 进入前端目录
cd ui-vue3
#### 安装依赖
# 使用 pnpm(推荐)
pnpm install
# 或者使用 npm
npm install
# 或者使用 yarn
yarn install
#### 编译前端
# 构建生产版本
pnpm run build
# 或者使用 Makefile
cd .. && make ui-build
步骤 5:启动应用
#### 选项 A:直接启动 JAR 文件
# 进入项目根目录
cd ..
# 运行编译好的 JAR 文件
java -jar target/jmanus.jar
# 或者使用 Maven 运行
./mvnw spring-boot:run
#### 选项 B:使用 Makefile 启动
# 启动后端服务
make run
# 或者分别启动
make java-run
#### 选项 C:开发模式(前后端分离)
后端开发模式:
# 在项目根目录启动后端
./mvnw spring-boot:run
# 后端将在 http://localhost:18080 运行
前端开发模式:
# 在 ui-vue3 目录启动前端开发服务器
cd ui-vue3
pnpm run dev
# 前端将在 http://localhost:5173 运行
# 会自动代理 API 请求到 http://localhost:18080
🛠️ 高级配置
数据库配置(可选)
默认使用 H2 内存数据库,如需使用 MySQL/PostgreSQL:
1. 修改配置文件 src/main/resources/application.yml:
spring:
profiles:
active: mysql # 或 postgres
2. 配置数据库连接 src/main/resources/application-mysql.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/jmanus
username: your_username
password: your_password
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
API 密钥配置
1. 启动后配置:访问 http://localhost:18080 按向导配置 2. 手动配置:修改配置文件或设置环境变量
📊 验证启动
1. 检查服务状态
# 检查端口是否监听
netstat -an | grep 18080
# 或者使用 curl
curl http://localhost:18080/api/init/status
2. 访问应用
- Web 界面:http://localhost:18080
- API 文档:http://localhost:18080/swagger-ui.html
- 健康检查:http://localhost:18080/actuator/health
3. 测试 API
# 测试初始化状态
curl http://localhost:18080/api/init/status
# 测试基本功能
curl -X POST http://localhost:18080/api/executor/executeByToolNameSync/test \
-H "Content-Type: application/json" \
-d '{"toolName":"test"}'
🐛 常见问题解决
问题 1:端口被占用
# 查找占用 18080 端口的进程
lsof -i :18080
# 或
netstat -ano | findstr 18080
# 终止进程(Linux/macOS)
kill -9 <PID>
# 或修改端口
java -jar jmanus.jar --server.port=8080
问题 2:内存不足
# 增加 JVM 内存
java -Xmx4g -Xms2g -jar jmanus.jar
# 或者设置环境变量
export JAVA_OPTS="-Xmx4g -Xms2g"
java -jar jmanus.jar
问题 3:前端构建失败
# 清除缓存重新安装
cd ui-vue3
rm -rf node_modules pnpm-lock.yaml
pnpm install
# 检查 Node.js 版本
node --version # 需要 18+
问题 4:Maven 构建失败
# 清除 Maven 缓存
./mvnw clean
# 强制更新依赖
./mvnw dependency:purge-local-repository
# 跳过测试构建
./mvnw package -DskipTests -Dmaven.test.skip=true
🚀 生产环境部署
Docker 部署
# 构建 Docker 镜像
docker build -t jmanus:latest .
# 运行容器
docker run -d \
--name jmanus \
-p 18080:18080 \
-e JAVA_OPTS="-Xmx2g -Xms1g" \
jmanus:latest
系统服务(Linux)
# 创建 systemd 服务文件
sudo nano /etc/systemd/system/jmanus.service
# 添加以下内容
[Unit]
Description=JManus AI System
After=network.target
[Service]
Type=simple
User=jmanus
WorkingDirectory=/opt/jmanus
ExecStart=/usr/bin/java -jar jmanus.jar
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
# 启动服务
sudo systemctl enable jmanus
sudo systemctl start jmanus
sudo systemctl status jmanus
📋 开发环境设置
IDE 配置
1. IntelliJ IDEA(推荐)- 导入为 Maven 项目
- 配置 Java 17 SDK
- 启用 Spring Boot 支持
- 安装 Java Extension Pack
- 安装 Spring Boot Extension Pack
- 配置调试环境
调试模式
# 启用调试模式
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar jmanus.jar
# 前端开发模式(热重载)
cd ui-vue3
pnpm run dev
🔧 常用命令速查
后端命令
# 编译
./mvnw clean package
# 运行测试
./mvnw test
# 代码格式化
./mvnw spotless:apply
# 启动应用
./mvnw spring-boot:run
前端命令
# 安装依赖
pnpm install
# 开发模式
pnpm run dev
# 构建生产版本
pnpm run build
# 运行测试
pnpm run test:unit
# 代码检查
pnpm run lint
Makefile 命令
# 查看所有可用命令
make help
# 编译项目
make build
# 运行测试
make test
# 构建 UI
make ui-build
# 运行 UI 开发服务器
make ui-run
# 完整构建(后端+前端)
make build && make ui-build
📚 下一步
启动成功后,您可以:
1. 访问 Web 界面:http://localhost:18080 2. 配置 API 密钥:按照向导配置您的 AI 模型 API 密钥 3. 查看文档:访问 API 文档了解可用接口 4. 运行示例:尝试内置的示例任务 5. 自定义配置:根据需求调整系统配置
JManus 存储层架构与设计思想深度解析
引言
JManus 作为基于 Spring AI Alibaba 构建的 AI Agent 管理系统,其存储层设计体现了现代分布式系统中数据持久化的最佳实践。本文将从架构和设计思想的角度,深入剖析 JManus 存储层的设计理念、技术选型、核心组件以及实现细节,为读者呈现一个完整的企业级存储解决方案。
一、整体架构概览
1.1 架构设计理念
JManus 存储层采用分层解耦、多数据库支持、领域驱动设计三大核心设计理念:
- 分层解耦:通过 Repository 模式将数据访问逻辑与业务逻辑分离,实现关注点分离
- 多数据库支持:支持 H2、MySQL、PostgreSQL 三种数据库,满足不同场景需求
- 领域驱动设计:按照业务领域划分存储模块,每个领域拥有独立的实体和存储逻辑
1.2 技术栈选型
graph TD
A[Spring Boot 3.5.6] --> B[Spring Data JPA]
B --> C[Hibernate]
C --> D[H2/MySQL/PostgreSQL]
E[Spring AI] --> F[Chat Memory]
F --> G[Multi-database Support]
H[Jackson] --> I[JSON Serialization]
I --> J[Entity Conversion]
K[HikariCP] --> L[Connection Pool]
L --> M[Database Performance]
核心依赖:
- Spring Data JPA 3.5.6:提供声明式数据访问
- Hibernate:ORM 框架,支持自动 DDL 生成
- HikariCP:高性能数据库连接池
- Jackson:JSON 序列化与反序列化
- Spring AI:AI 对话内存管理
二、多数据库支持架构
2.1 数据库配置策略
JManus 采用配置文件分离的策略,为每种数据库提供独立的配置:
#### H2 数据库配置(开发环境)
spring:
datasource:
url: jdbc:h2:file:./h2-data/openmanus_db;MODE=MYSQL;DATABASE_TO_LOWER=TRUE
driver-class-name: org.h2.Driver
username: sa
password: $FSD#@!@#!#$!12341234
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: update
#### MySQL 配置(生产环境)
spring:
datasource:
url: jdbc:mysql://your-mysql-host:3306/openmanus_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
username: your_mysql_username
password: your_mysql_password
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
hibernate:
ddl-auto: update
#### PostgreSQL 配置(企业环境)
spring:
datasource:
url: jdbc:postgresql://localhost:5432/openmanus_db
driver-class-name: org.postgresql.Driver
username: postgres
password: 123456
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
2.2 连接池优化配置
JManus 使用 HikariCP 作为连接池,提供了精细化的配置:
spring:
datasource:
hikari:
maximum-pool-size: 20 # 最大连接数
minimum-idle: 5 # 最小空闲连接
connection-timeout: 30000 # 连接超时时间
idle-timeout: 600000 # 空闲连接超时
max-lifetime: 1800000 # 连接最大生命周期
pool-name: Spring-AI-Alibaba-JManus-${spring.profiles.active}-Pool
connection-test-query: SELECT 1
validation-timeout: 5000
leak-detection-threshold: 60000
2.3 数据库特定内存支持
JManus 为每种数据库实现了特定的聊天内存存储:
#### 抽象基类设计
public abstract class JdbcChatMemoryRepository implements ChatMemoryRepository {
protected abstract String hasTableSql(String tableName);
protected abstract String createTableSql(String tableName);
protected abstract String getAddSql();
protected abstract String getGetSql();
}
#### H2 实现
public class H2ChatMemoryRepository extends JdbcChatMemoryRepository {
@Override
protected String createTableSql(String tableName) {
return String.format(
"CREATE TABLE %s (id BIGINT AUTO_INCREMENT PRIMARY KEY, "
+ "conversation_id VARCHAR(256) NOT NULL, content LONGTEXT NOT NULL, "
+ "type VARCHAR(100) NOT NULL, timestamp TIMESTAMP NOT NULL, "
+ "CONSTRAINT chk_message_type CHECK (type IN ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL')))",
tableName);
}
}
#### MySQL 实现
public class MysqlChatMemoryRepository extends JdbcChatMemoryRepository {
@Override
protected String hasTableSql(String tableName) {
return String.format(
"SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '%s'",
tableName);
}
}
#### PostgreSQL 实现
public class PostgresChatMemoryRepository extends JdbcChatMemoryRepository {
@Override
protected String createTableSql(String tableName) {
return String.format(
"CREATE TABLE %s (id BIGSERIAL PRIMARY KEY, "
+ "conversation_id VARCHAR(256) NOT NULL, content TEXT NOT NULL, "
+ "type VARCHAR(100) NOT NULL, timestamp TIMESTAMP NOT NULL, "
+ "CONSTRAINT chk_message_type CHECK (type IN ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL')))",
tableName);
}
}
三、实体设计与 JPA 实现
3.1 实体设计原则
JManus 的实体设计遵循以下原则:
1. 领域边界清晰:每个业务领域拥有独立的实体 2. 关系映射合理:使用适当的 JPA 关系注解 3. 索引优化:为频繁查询的字段添加索引 4. 数据完整性:使用约束保证数据质量
3.2 核心实体架构
#### 计划执行记录实体
@Entity
@Table(name = "plan_execution_record")
public class PlanExecutionRecordEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "current_plan_id", nullable = false, unique = true)
private String currentPlanId;
@Column(name = "root_plan_id")
private String rootPlanId;
@Column(name = "parent_plan_id")
private String parentPlanId;
@Column(name = "user_request", columnDefinition = "LONGTEXT")
private String userRequest;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "plan_execution_id")
private List<AgentExecutionRecordEntity> agentExecutionSequence;
}
#### Agent 执行记录实体
@Entity
@Table(name = "agent_execution_record", indexes = { @Index(columnList = "step_id") })
public class AgentExecutionRecordEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "step_id", unique = true)
private String stepId;
@Column(name = "agent_request", columnDefinition = "LONGTEXT")
private String agentRequest;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "agent_execution_record_id")
private List<ThinkActRecordEntity> thinkActSteps;
}
3.3 复杂类型转换
JManus 使用 JPA 属性转换器处理复杂数据类型:
@Converter
public class MapToStringConverter implements AttributeConverter<Map<String, String>, String> {
private final ObjectMapper objectMapper;
@Override
public String convertToDatabaseColumn(Map<String, String> attribute) {
try {
return objectMapper.writeValueAsString(attribute);
} catch (Exception e) {
throw new IllegalArgumentException("Error converting map to string", e);
}
}
@Override
public Map<String, String> convertToEntityAttribute(String dbData) {
if (dbData == null || dbData.isEmpty()) {
return new HashMap<>();
}
try {
return objectMapper.readValue(dbData, new TypeReference<>() {});
} catch (Exception e) {
throw new IllegalArgumentException("Error converting string to map", e);
}
}
}
四、执行记录与审计追踪
4.1 分层执行记录架构
JManus 采用三层执行记录架构:
graph TD
A[PlanExecutionRecordEntity] --> B[AgentExecutionRecordEntity]
B --> C[ThinkActRecordEntity]
C --> D[ActToolInfoEntity]
#### 计划执行记录层
- 记录整个计划的执行生命周期
- 维护计划层次结构(根计划、父计划、子计划)
- 存储用户请求和执行结果摘要
- 记录每个 Agent 的执行过程
- 维护执行状态和时间戳
- 关联具体的模型信息
- 记录 Agent 的思考和行动过程
- 存储工具调用信息和结果
- 支持并行工具执行记录
4.2 执行记录服务实现
@Service
public class NewRepoPlanExecutionRecorder implements PlanExecutionRecorder {
@Transactional
public Long recordPlanExecutionStart(String currentPlanId, String title, String userRequest,
List<ExecutionStep> executionSteps, String parentPlanId, String rootPlanId, String toolcallId) {
// 检查计划是否已存在
Optional<PlanExecutionRecordEntity> existingPlanOpt =
planExecutionRecordRepository.findByCurrentPlanId(currentPlanId);
PlanExecutionRecordEntity planExecutionRecordEntity;
if (existingPlanOpt.isPresent()) {
// 更新现有计划
planExecutionRecordEntity = existingPlanOpt.get();
} else {
// 创建新计划
planExecutionRecordEntity = new PlanExecutionRecordEntity(currentPlanId);
}
// 设置层次关系
if (rootPlanId != null && !rootPlanId.trim().isEmpty()) {
createPlanRelationship(currentPlanId, parentPlanId, rootPlanId, toolcallId);
}
return planExecutionRecordRepository.save(planExecutionRecordEntity).getId();
}
}
4.3 事务管理策略
JManus 在关键业务操作中使用声明式事务管理:
@Transactional
public void recordCompleteAgentExecution(ExecutionStep step) {
// 查询 Agent 执行记录
Optional<AgentExecutionRecordEntity> agentRecordOpt =
agentExecutionRecordRepository.findByStepId(step.getStepId());
if (!agentRecordOpt.isPresent()) {
throw new IllegalArgumentException("agent record is null");
}
AgentExecutionRecordEntity agentRecord = agentRecordOpt.get();
// 更新执行状态
agentRecord.setStatus(ExecutionStatusEntity.FINISHED);
agentRecord.setEndTime(LocalDateTime.now());
// 保存更新
agentExecutionRecordRepository.save(agentRecord);
}
五、MCP 配置存储架构
5.1 MCP 配置模型设计
Model Context Protocol (MCP) 配置存储体现了 JManus 对 AI 工具生态的深度支持:
@Entity
@Table(name = "mcp_config")
public class McpConfigEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String mcpServerName;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private McpConfigType connectionType;
@Column(nullable = false, length = 4000)
private String connectionConfig;
@Column(nullable = false, columnDefinition = "VARCHAR(10) DEFAULT 'ENABLE'")
@Enumerated(EnumType.STRING)
private McpConfigStatus status = McpConfigStatus.ENABLE;
}
5.2 MCP 状态管理
JManus 实现了完整的 MCP 服务状态管理:
public enum McpConfigStatus {
ENABLE, // 启用状态
DISABLE, // 禁用状态
ERROR, // 错误状态
CONNECTING // 连接中状态
}
public enum McpConfigType {
HTTP, // HTTP 连接
WEBSOCKET, // WebSocket 连接
STDIO // 标准输入输出
}
5.3 MCP 配置验证与连接管理
@Service
public class McpService {
public McpConfigVO validateAndConnect(McpServerRequestVO request) {
// 配置验证
McpConfigEntity configEntity = mcpConfigRepository
.findByMcpServerName(request.getServerName());
if (configEntity == null) {
throw new McpConfigNotFoundException(
"MCP config not found: " + request.getServerName());
}
// 连接测试
McpConnection connection = mcpConnectionFactory
.createConnection(configEntity);
// 状态更新
configEntity.setStatus(McpConfigStatus.ENABLE);
mcpConfigRepository.save(configEntity);
return convertToVO(configEntity);
}
}
六、计划模板与执行计划存储
6.1 计划模板存储架构
计划模板存储支持版本管理和参数化配置:
@Entity
@Table(name = "plan_template")
public class PlanTemplate {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "plan_template_id", length = 50, unique = true, nullable = false)
private String planTemplateId;
@Column(name = "title", length = 255)
private String title;
@Column(name = "user_request", length = 4000)
private String userRequest;
@Column(name = "is_internal_toolcall", nullable = false)
private boolean isInternalToolcall = false;
}
6.2 计划版本管理
@Entity
@Table(name = "plan_template_version")
public class PlanTemplateVersion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "plan_template_id", nullable = false)
private PlanTemplate planTemplate;
@Column(name = "version", nullable = false)
private Integer version;
@Column(name = "content", columnDefinition = "LONGTEXT")
private String content;
@Column(name = "is_current", nullable = false)
private boolean isCurrent = false;
}
6.3 动态计划创建与存储
@Service
public class DynamicAgentPlanCreator {
public PlanExecutionRecordEntity createDynamicPlan(
String planTemplateId,
Map<String, Object> parameters,
String parentPlanId,
String rootPlanId) {
// 获取计划模板
PlanTemplate template = planTemplateRepository
.findByPlanTemplateId(planTemplateId)
.orElseThrow(() -> new PlanTemplateNotFoundException(planTemplateId));
// 参数映射
Map<String, Object> mappedParameters = planParameterMappingService
.mapParameters(template, parameters);
// 创建执行计划
PlanExecutionRecordEntity executionPlan = new PlanExecutionRecordEntity();
executionPlan.setCurrentPlanId(generatePlanId());
executionPlan.setTitle(template.getTitle());
executionPlan.setUserRequest(processTemplate(template.getUserRequest(), mappedParameters));
executionPlan.setParentPlanId(parentPlanId);
executionPlan.setRootPlanId(rootPlanId);
return planExecutionRecordRepository.save(executionPlan);
}
}
七、用户管理与命名空间隔离
7.1 用户实体设计
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
@Column(name = "email", unique = true, nullable = false, length = 100)
private String email;
@Column(name = "display_name", length = 100)
private String displayName;
@Column(name = "status", length = 20)
private String status;
@ElementCollection
@CollectionTable(name = "user_preferences", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "preference")
private List<String> preferences;
}
7.2 命名空间隔离机制
@Entity
@Table(name = "namespace")
public class NamespaceEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name", nullable = false, unique = true, length = 100)
private String name;
@Column(name = "code", nullable = false, unique = true, length = 50)
private String code;
@Column(name = "description", length = 500)
private String description;
}
7.3 数据初始化策略
JManus 使用 Spring Boot 的 CommandLineRunner 实现数据初始化:
@Component
public class UserDataInitializer implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
initializeDefaultUsers();
}
private void initializeDefaultUsers() {
if (!userRepository.existsByUsername("jmanus_user")) {
UserEntity defaultUser = new UserEntity(
"jmanus_user",
"user@jmanus.ai",
"JManus User");
defaultUser.setStatus("active");
defaultUser.setCreatedAt(LocalDateTime.now().minusDays(30));
defaultUser.setPreferences(Arrays.asList("dark_mode", "notifications_enabled"));
userRepository.save(defaultUser);
}
}
}
八、内存管理与聊天历史存储
8.1 聊天内存架构设计
JManus 实现了多数据库支持的聊天内存存储:
graph TD
A[ChatMemory] --> B[ChatMemoryRepository]
B --> C[JdbcChatMemoryRepository]
C --> D[H2ChatMemoryRepository]
C --> E[MysqlChatMemoryRepository]
C --> F[PostgresChatMemoryRepository]
8.2 内存配置管理
@Configuration
public class MemoryConfig {
@Bean
public ChatMemoryRepository chatMemoryRepository(JdbcTemplate jdbcTemplate) {
ChatMemoryRepository chatMemoryRepository = null;
if (mysqlEnabled) {
chatMemoryRepository = MysqlChatMemoryRepository
.mysqlBuilder()
.jdbcTemplate(jdbcTemplate)
.build();
} else if (postgresEnabled) {
chatMemoryRepository = PostgresChatMemoryRepository
.postgresBuilder()
.jdbcTemplate(jdbcTemplate)
.build();
} else if (h2Enabled) {
chatMemoryRepository = H2ChatMemoryRepository
.h2Builder()
.jdbcTemplate(jdbcTemplate)
.build();
}
return chatMemoryRepository;
}
}
8.3 消息窗口管理
@Bean
public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.build();
}
九、文件上传与静态资源管理
9.1 文件上传配置
manus:
file-upload:
max-file-size: 1073741824 # 1GB
max-files-per-upload: 10
upload-directory: uploaded_files
validation-strategy: code # code 或 config
9.2 文件验证策略
JManus 提供两种文件验证策略:
1. 代码验证策略:通过代码逻辑验证文件类型 2. 配置验证策略:通过配置文件定义允许的文件类型
9.3 文件存储服务
@Service
public class FileUploadService {
public FileUploadResult uploadFiles(MultipartFile[] files, String uploadId) {
List<FileInfo> uploadedFiles = new ArrayList<>();
for (MultipartFile file : files) {
// 文件验证
if (!fileValidationService.validateFile(file)) {
throw new FileValidationException("Invalid file type: " + file.getOriginalFilename());
}
// 文件存储
String filePath = storeFile(file, uploadId);
FileInfo fileInfo = createFileInfo(file, filePath);
uploadedFiles.add(fileInfo);
}
return new FileUploadResult(uploadId, uploadedFiles);
}
}
十、定时任务与调度存储
10.1 Cron 任务实体设计
@Entity
@Table(name = "cron_tasks")
public class CronEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "cron_name", nullable = false, length = 100)
private String cronName;
@Column(name = "cron_expression", nullable = false, length = 100)
private String cronExpression;
@Column(name = "plan_desc", columnDefinition = "TEXT")
private String planDescription;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private TaskStatus status;
}
10.2 动态任务调度
@Service
public class DynamicCronTaskScheduler {
public void scheduleCronTask(CronEntity cronTask) {
// 解析 Cron 表达式
CronExpression cronExpression = CronExpression.parse(cronTask.getCronExpression());
// 创建调度任务
ScheduledFuture<?> scheduledTask = taskScheduler.schedule(
() -> executeCronTask(cronTask),
cronExpression
);
// 存储任务引用
scheduledTasks.put(cronTask.getId(), scheduledTask);
}
}
十一、模型配置与动态模型管理
11.1 动态模型实体设计
@Entity
@Table(name = "dynamic_models")
public class DynamicModelEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "model_name", nullable = false, unique = true, length = 100)
private String modelName;
@Column(name = "base_url", nullable = false, length = 500)
private String baseUrl;
@Column(name = "api_key", length = 500)
private String apiKey;
@Convert(converter = MapToStringConverter.class)
@Column(name = "headers", columnDefinition = "TEXT")
private Map<String, String> headers;
@Column(name = "is_default")
private Boolean isDefault = false;
@Column(name = "temperature")
private Double temperature = 0.7;
@Column(name = "top_p")
private Double topP = 1.0;
}
11.2 模型缓存策略
@Service
public class ModelServiceImpl implements ModelService {
// 第三方 API 调用缓存,2秒过期
private final Map<String, CacheEntry<List<AvailableModel>>> apiCache =
new ConcurrentHashMap<>();
private static final long CACHE_EXPIRY_MS = 2000; // 2秒
public List<AvailableModel> getAvailableModels(String baseUrl, String apiKey) {
String cacheKey = baseUrl + ":" + apiKey;
// 检查缓存
CacheEntry<List<AvailableModel>> cachedEntry = apiCache.get(cacheKey);
if (cachedEntry != null && !cachedEntry.isExpired()) {
return cachedEntry.getData();
}
// 调用 API
List<AvailableModel> models = callThirdPartyApiInternal(baseUrl, apiKey);
// 缓存结果
apiCache.put(cacheKey, new CacheEntry<>(models));
return models;
}
}
11.3 模型验证与连接测试
public ValidationResult validateConfig(String baseUrl, String apiKey) {
try {
// 1. 验证 Base URL 格式
if (!isValidBaseUrl(baseUrl)) {
return ValidationResult.invalid("Base URL format is incorrect");
}
// 2. 验证 API Key 格式
if (!isValidApiKey(apiKey)) {
return ValidationResult.invalid("API Key format is incorrect");
}
// 3. 调用第三方 API 验证
List<AvailableModel> models = callThirdPartyApiInternal(baseUrl, apiKey);
return ValidationResult.valid(models);
} catch (AuthenticationException e) {
return ValidationResult.invalid("API Key is invalid or expired");
} catch (NetworkException e) {
return ValidationResult.invalid("Network connection failed");
}
}
十二、数据初始化与迁移策略
12.1 数据初始化架构
JManus 采用模块化初始化策略,每个业务领域拥有独立的数据初始化器:
graph TD
A[Application Startup] --> B[CommandLineRunner]
B --> C[UserDataInitializer]
B --> D[NamespaceDataInitialization]
B --> E[ModelDataInitialization]
B --> F[PlanTemplateInitializationService]
C --> G[Default User Creation]
D --> H[Default Namespace Creation]
E --> I[Default Models Setup]
F --> J[Plan Templates Setup]
12.2 初始化实现模式
所有初始化器遵循统一的模式:
@Component
public class DataInitializer implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(DataInitializer.class);
@Override
public void run(String... args) throws Exception {
logger.info("Starting {} data initialization", getInitializerName());
try {
if (shouldInitialize()) {
initializeData();
logger.info("{} data initialization completed successfully", getInitializerName());
} else {
logger.info("{} data already exists, skipping initialization", getInitializerName());
}
} catch (Exception e) {
logger.error("Error during {} data initialization: {}", getInitializerName(), e.getMessage(), e);
// 初始化失败不应阻止应用启动
}
}
protected abstract String getInitializerName();
protected abstract boolean shouldInitialize();
protected abstract void initializeData();
}
12.3 幂等性保证
所有初始化操作都具有幂等性:
private void initializeDefaultUsers() {
// 检查是否已存在,避免重复创建
if (!userRepository.existsByUsername("jmanus_user")) {
UserEntity defaultUser = new UserEntity(
"jmanus_user",
"user@jmanus.ai",
"JManus User");
defaultUser.setStatus("active");
defaultUser.setCreatedAt(LocalDateTime.now());
userRepository.save(defaultUser);
logger.info("Created default user: {}", defaultUser.getUsername());
}
}
十三、事务管理与数据一致性
13.1 事务管理策略
JManus 采用声明式事务管理,在关键业务操作中使用 @Transactional 注解:
#### 计划执行记录事务
@Transactional
public Long recordPlanExecutionStart(String currentPlanId, String title, String userRequest,
List<ExecutionStep> executionSteps, String parentPlanId, String rootPlanId, String toolcallId) {
// 创建或更新计划执行记录
PlanExecutionRecordEntity planRecord = createOrUpdatePlan(currentPlanId, title, userRequest);
// 创建 Agent 执行记录
List<AgentExecutionRecordEntity> agentRecords = createAgentExecutionRecords(executionSteps);
// 建立关联关系
planRecord.setAgentExecutionSequence(agentRecords);
// 创建层次关系
if (rootPlanId != null) {
createPlanRelationship(currentPlanId, parentPlanId, rootPlanId, toolcallId);
}
// 保存所有实体
return planExecutionRecordRepository.save(planRecord).getId();
}
#### Agent 执行完成事务
@Transactional
public void recordCompleteAgentExecution(ExecutionStep step) {
// 查询 Agent 执行记录
AgentExecutionRecordEntity agentRecord = agentExecutionRecordRepository
.findByStepId(step.getStepId())
.orElseThrow(() -> new IllegalArgumentException("Agent record not found"));
// 更新执行状态
agentRecord.setStatus(convertAgentStateToExecutionStatus(step.getStatus()));
agentRecord.setEndTime(LocalDateTime.now());
agentRecord.setResult(step.getResult());
// 保存更新
agentExecutionRecordRepository.save(agentRecord);
}
13.2 数据一致性保证
#### 乐观锁机制 对于可能并发更新的实体,使用版本字段实现乐观锁:
@Entity
public class PlanExecutionRecordEntity {
@Version
private Long version;
// 其他字段...
}
#### 唯一约束保证 通过数据库唯一约束防止重复数据:
@Entity
@Table(name = "agent_execution_record",
uniqueConstraints = @UniqueConstraint(columnNames = "step_id"))
public class AgentExecutionRecordEntity {
@Column(name = "step_id", unique = true)
private String stepId;
}
#### 业务层一致性检查 在业务逻辑层进行一致性验证:
private void createPlanRelationship(String currentPlanId, String parentPlanId,
String rootPlanId, String toolcallId) {
// 验证必需参数
if (currentPlanId == null || currentPlanId.trim().isEmpty()) {
throw new IllegalArgumentException("currentPlanId is required");
}
if (rootPlanId == null || rootPlanId.trim().isEmpty()) {
throw new IllegalArgumentException("rootPlanId is required");
}
// 验证业务规则
if (parentPlanId != null && !parentPlanId.trim().isEmpty()) {
if (toolcallId == null || toolcallId.trim().isEmpty()) {
throw new IllegalArgumentException(
"toolcallId is required when parentPlanId is provided");
}
if (rootPlanId.equals(currentPlanId)) {
throw new IllegalArgumentException(
"rootPlanId cannot equal currentPlanId when parentPlanId is provided");
}
}
}
十四、性能优化与索引策略
14.1 数据库索引设计
JManus 针对高频查询场景设计了优化的索引策略:
#### 执行记录索引
@Entity
@Table(name = "agent_execution_record",
indexes = { @Index(columnList = "step_id") })
public class AgentExecutionRecordEntity {
@Column(name = "step_id", unique = true)
private String stepId;
}
#### 计划执行记录复合索引
@Entity
@Table(name = "plan_execution_record",
indexes = {
@Index(columnList = "current_plan_id"),
@Index(columnList = "root_plan_id"),
@Index(columnList = "parent_plan_id")
})
public class PlanExecutionRecordEntity {
// 实体字段...
}
#### MCP 配置索引
@Entity
@Table(name = "mcp_config",
indexes = { @Index(columnList = "mcp_server_name") })
public class McpConfigEntity {
@Column(nullable = false, unique = true)
private String mcpServerName;
}
14.2 查询优化策略
#### 延迟加载策略
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "plan_execution_id")
private List<AgentExecutionRecordEntity> agentExecutionSequence;
#### 批量操作优化
@Override
public void saveAll(String conversationId, List<Message> messages) {
// 先删除旧数据
this.deleteByConversationId(conversationId);
// 批量插入新数据
this.jdbcTemplate.batchUpdate(getAddSql(),
new AddBatchPreparedStatement(conversationId, messages));
}
14.3 缓存策略
#### API 调用缓存
// 第三方 API 调用缓存,2秒过期
private final Map<String, CacheEntry<List<AvailableModel>>> apiCache =
new ConcurrentHashMap<>();
private static final long CACHE_EXPIRY_MS = 2000; // 2秒
public List<AvailableModel> getAvailableModels(String baseUrl, String apiKey) {
String cacheKey = baseUrl + ":" + apiKey;
// 检查缓存
CacheEntry<List<AvailableModel>> cachedEntry = apiCache.get(cacheKey);
if (cachedEntry != null && !cachedEntry.isExpired()) {
return cachedEntry.getData();
}
// 调用 API 并缓存结果
List<AvailableModel> models = callThirdPartyApiInternal(baseUrl, apiKey);
apiCache.put(cacheKey, new CacheEntry<>(models));
return models;
}
#### 默认模型缓存
@Service
public class LlmService {
private volatile ChatModel defaultChatModel;
public void refreshDefaultModelCache() {
// 刷新默认模型缓存
DynamicModelEntity defaultModel = dynamicModelRepository
.findByIsDefaultTrue();
if (defaultModel != null) {
this.defaultChatModel = createChatModel(defaultModel);
}
}
}
14.4 数据库连接池优化
#### HikariCP 配置优化
spring:
datasource:
hikari:
maximum-pool-size: 20 # 根据服务器配置调整
minimum-idle: 5 # 保持最小连接数
connection-timeout: 30000 # 连接超时时间
idle-timeout: 600000 # 空闲连接超时
max-lifetime: 1800000 # 连接最大生命周期
leak-detection-threshold: 60000 # 连接泄露检测
#### 连接池监控
@Component
public class DatabaseConnectionPoolMonitor {
@Autowired
private DataSource dataSource;
@Scheduled(fixedDelay = 60000) // 每分钟监控一次
public void monitorConnectionPool() {
if (dataSource instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
logger.info("Connection Pool Stats - Active: {}, Idle: {}, Total: {}, Waiting: {}",
hikariDataSource.getHikariPoolMXBean().getActiveConnections(),
hikariDataSource.getHikariPoolMXBean().getIdleConnections(),
hikariDataSource.getHikariPoolMXBean().getTotalConnections(),
hikariDataSource.getHikariPoolMXBean().getThreadsAwaitingConnection());
}
}
}
十五、设计思想总结
15.1 架构设计原则
JManus 存储层的设计体现了以下核心原则:
#### 1. 领域驱动设计 (DDD)
- 按照业务领域划分存储模块
- 每个领域拥有独立的实体、仓库和服务
- 清晰的领域边界和职责分离
- 表现层 → 业务层 → 数据访问层 → 数据存储层
- 每层职责单一,依赖关系清晰
- 通过接口实现层间解耦
- 通过配置控制不同数据库的支持
- 灵活的配置文件分离策略
- 环境特定的配置优化
- 完整的事务管理机制
- 数据一致性保证
- 性能优化和监控
- 安全性和权限控制
15.2 技术创新点
#### 1. 多数据库统一抽象 通过抽象基类和模板方法模式,实现了对 H2、MySQL、PostgreSQL 的统一支持,同时保留了各数据库的特性优化。
#### 2. 分层执行记录架构 创新的三层执行记录架构(计划→Agent→思考行动),为 AI 执行过程提供了完整的审计追踪能力。
#### 3. 动态模型管理 支持运行时动态配置和切换 AI 模型,通过缓存策略优化性能,为 AI 应用提供了灵活的模型管理能力。
#### 4. MCP 生态集成 深度集成 Model Context Protocol,为 AI 工具生态提供了标准化的配置和管理机制。
15.3 最佳实践总结
#### 1. 数据访问最佳实践
- 使用 Spring Data JPA 简化数据访问
- 通过 Repository 模式实现数据访问抽象
- 合理使用索引优化查询性能
- 采用延迟加载避免 N+1 查询问题
- 在业务服务层使用声明式事务
- 合理控制事务边界和粒度
- 使用只读事务优化查询性能
- 实现幂等性保证重复操作安全
- 数据库连接池的合理配置
- 缓存策略的灵活运用
- 批量操作减少数据库交互
- 索引设计的针对性优化
- 统一的编码规范和命名约定
- 完善的日志记录和监控
- 模块化的代码组织结构
- 详尽的文档和注释
结语
JManus 的存储层架构设计充分体现了现代分布式系统中数据持久化的先进理念和最佳实践。通过多数据库支持、领域驱动设计、分层架构、事务管理、性能优化等多个维度的深入设计,构建了一个高可用、高性能、可扩展、易维护的企业级存储解决方案。
该架构不仅满足了当前 AI Agent 管理系统的需求,也为未来的功能扩展和性能优化奠定了坚实的基础。其设计思想和实现模式对于构建类似的企业级应用具有重要的参考价值。
随着 AI 技术的不断发展,JManus 的存储架构也将持续演进,为 AI 应用提供更加强大和灵活的数据支撑能力。
TypeScript 接口的两种用法
1. 定义数据结构(数据模型/DTO)
在 agent.ts 中,接口用于定义数据结构,描述从 API 返回的数据形状:
``1:9:ui-vue3/src/api/agent.ts
export interface AgentEntity {
id: number
agentName: string
agentDescription: string
nextStepPrompt: string
availableToolKeys: string[]
namespace: string
className: string
}
typescript
// 接口定义方法签名的例子
export interface Calculator {
add(a: number, b: number): number
subtract(a: number, b: number): number
multiply(a: number, b: number): number
}这表示:一个 `AgentEntity` 对象必须包含这些属性,每个属性有特定类型。用于类型检查和自动补全。
### 2. 定义方法签名(契约)
接口也可以定义方法,例如:
// 实现这个接口的类 export class BasicCalculator implements Calculator { add(a: number, b: number): number { return a + b } subtract(a: number, b: number): number { return a - b } multiply(a: number, b: number): number { return a * b } }
## 为什么 `agent.ts` 中只有属性?
这个文件中的接口是数据模型(DTO),用于描述 API 请求/响应的数据结构,而不是定义行为(方法)。
- 属性接口:描述数据形状
- 方法接口:描述行为契约
## 实际使用示例
当调用 API 时,TypeScript 会检查返回的数据是否符合接口定义:
typescript
// 调用 API
const agents = await getAllAgents() // 返回 Promise// TypeScript 知道 agents 是 AgentEntity[] 类型
// 可以安全地访问属性
agents.forEach(agent => {
console.log(agent.agentName) // ✅ TypeScript 知道这个属性存在
console.log(agent.id) // ✅ TypeScript 知道这个属性存在
console.log(agent.nonExistent) // ❌ TypeScript 会报错
})
`
总结
- 接口可以定义属性(数据)和方法(函数)
- 在 agent.ts` 中,接口只定义属性,因为它们是数据模型
- 如果需要定义行为,可以在接口中添加方法签名
- 这种用法在 TypeScript 中很常见,用于类型安全和代码提示
解读 common-api-service.ts 文件:
文件概述
这是一个使用类(Class)和静态方法(Static Methods)的 API 服务文件,与 agent.ts 的函数式风格不同。它封装了通用的执行器相关 API 调用。
设计模式对比
agent.ts vs common-api-service.ts
| 特性 | agent.ts | common-api-service.ts |
|---|---|---|
| 组织方式 | 导出独立函数 | 类的静态方法 |
| 使用方式 | getAllAgents() | CommonApiService.getDetails() |
| 共享状态 | 无 | 有(BASE_URL 常量) |
类结构分析
1. 基础配置
``21:22:ui-vue3/src/api/common-api-service.ts
export class CommonApiService {
private static readonly BASE_URL = '/api/executor'
${this.BASE_URL}/details/${planId}
25:53:ui-vue3/src/api/common-api-service.ts
public static async getDetails(planId: string): Promise- `private static readonly`:私有静态只读常量
- `private`:仅在类内部使用
- `static`:属于类,不依赖实例
- `readonly`:不可修改
- `BASE_URL`:所有 API 调用的基础路径
### 2. `getDetails` - 获取执行记录详情
)
if (response.status === 404) {
// 404 returns null
return null
}
if (!response.ok) {
const errorText = await response.text()
throw new Error(Failed to get detailed information: ${response.status} - ${errorText})
}
const rawText = await response.text()
const data = JSON.parse(rawText)
// Type validation - ensure the response contains required currentPlanId if (data && typeof data === 'object' && !data.currentPlanId) { // If currentPlanId is missing from response, add it from the parameter data.currentPlanId = planId }
return data } catch (error: unknown) { // Log error but don't throw exception console.error('[CommonApiService] Failed to get plan details:', error) // Don't return failed status for network errors - let polling continue // Only return null to indicate no data available return null } }
特点:
- 404 返回 `null`(不抛错)
- 数据校验:缺失 `currentPlanId` 时自动补充
- 错误处理:捕获异常并返回 `null`,便于轮询继续
- 使用场景:适合轮询场景,避免因临时错误中断
### 3. `submitFormInput` - 提交表单输入
56:79:ui-vue3/src/api/common-api-service.ts
public static async submitFormInput(
planId: string,
formData: Record, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
})
if (!response.ok) {
let errorData
try {
errorData = await response.json()
} catch {
errorData = { message: Failed to submit form input: ${response.status} }
}
throw new Error(errorData.message || Failed to submit form input: ${response.status})
}
const contentType = response.headers.get('content-type')
if (contentType && contentType.indexOf('application/json') !== -1) {
return await response.json()
}
return { success: true }
}
特点:
- POST 请求,发送 JSON 数据
- 错误处理:解析错误信息并抛出异常
- 响应处理:检查 Content-Type,非 JSON 时返回 `{ success: true }`
- 使用场景:用户交互提交,需要明确的成功/失败反馈
### 4. `getAllPrompts` - 获取所有提示列表
84:93:ui-vue3/src/api/common-api-service.ts
static async getAllPrompts(): Promise {
try {
const response = await fetch(this.BASE_URL)
const result = await this.handleResponse(response)
return await result.json()
} catch (error) {
console.error('Failed to get Prompt list:', error)
throw error
}
}
特点:
- 使用私有方法 `handleResponse` 统一处理响应
- 错误会向上抛出,调用方需要处理
- 返回类型为 `unknown[]`(较宽松)
### 5. `handleResponse` - 私有响应处理
95:105:ui-vue3/src/api/common-api-service.ts
private static async handleResponse(response: Response) {
if (!response.ok) {
try {
const errorData = await response.json()
throw new Error(errorData.message || API request failed: ${response.status})
} catch {
throw new Error(API request failed: ${response.status} ${response.statusText})
}
}
return response
}
特点:
- `private static`:仅内部使用
- 统一错误处理逻辑
- 尝试解析 JSON 错误,失败则使用状态文本
## 设计模式总结
### 1. 静态方法模式
typescript
// 使用方式:不需要实例化
CommonApiService.getDetails('plan-123')
// 而不是:
const service = new CommonApiService()
service.getDetails('plan-123')
### 2. 不同的错误处理策略
| 方法 | 错误处理策略 | 原因 |
|------|------------|------|
| `getDetails` | 捕获异常,返回 `null` | 轮询场景,不应中断 |
| `submitFormInput` | 抛出异常 | 用户操作,需要明确反馈 |
| `getAllPrompts` | 抛出异常 | 调用方处理错误 |
### 3. 数据校验和修复
`getDetails` 中自动补充缺失的 `currentPlanId`,增强健壮性。
## 使用示例
typescript
// 获取详情(轮询场景)
const details = await CommonApiService.getDetails('plan-123')
if (details) {
console.log('获取成功:', details)
} else {
console.log('数据不存在或网络错误')
}// 提交表单(需要错误处理)
try {
const result = await CommonApiService.submitFormInput('plan-123', {
name: 'John',
age: 30
})
console.log('提交成功:', result)
} catch (error) {
console.error('提交失败:', error)
}
`与
agent.ts 的对比
agent.ts:函数式,简单直接,适合独立 API
common-api-service.ts`:类式,可共享配置和私有方法,适合相关 API 的集中管理
两种方式都有效,选择取决于项目风格和需求。TypeScript 的 async 函数不需要显式配置调度器。执行机制由 JavaScript 运行时(浏览器或 Node.js)内置提供。
内置机制
1. 编译后的代码
TypeScript 的 async/await 会被编译成标准的 JavaScript Promise:
// TypeScript 代码
async function getDetails(planId: string) {
const response = await fetch('/api/details')
return response.json()
}
编译后大致等价于:
function getDetails(planId) {
return fetch('/api/details')
.then(response => response.json())
}
2. 执行机制
async 函数的执行依赖:
- Promise:异步操作的基础
- 事件循环(Event Loop):由运行时提供,负责调度异步任务
- 微任务队列(Microtask Queue):Promise 回调在这里执行
实际执行流程
以 common-api-service.ts 中的代码为例:
``25:27:ui-vue3/src/api/common-api-service.ts
public static async getDetails(planId: string): Promise${this.BASE_URL}/details/${planId})
`
typescript
async function delayedFetch() {
await new Promise(resolve => setTimeout(resolve, 1000)) // 延迟 1 秒
return fetch('/api/data')
}
执行流程:
1. 调用 `getDetails()` 返回一个 Promise
2. `fetch()` 发起网络请求(浏览器/Node.js 处理)
3. `await` 暂停函数执行,将后续代码放入微任务队列
4. 网络请求完成后,Promise resolve,微任务被调度执行
5. 继续执行后续代码
## 无需配置的原因
### 浏览器环境
- 事件循环由浏览器引擎(V8、SpiderMonkey 等)提供
- `fetch` API 由浏览器实现
- Promise 和微任务队列已内置
### Node.js 环境
- 事件循环由 libuv 提供
- `fetch`(Node 18+)或 `http` 模块处理网络请求
- Promise 和微任务队列已内置
## 如果需要自定义调度
虽然不需要配置,但在某些场景下可以控制执行时机:
### 1. 使用 `setTimeout` 延迟执行
typescript
async function customSchedule() {
await new Promise(resolve => queueMicrotask(resolve))
// 确保在下一个微任务队列中执行
}
### 2. 使用 `queueMicrotask` 控制微任务
typescript
// 在单独的线程中执行
const worker = new Worker('worker.js')
worker.postMessage({ type: 'fetch', url: '/api/data' })
### 3. 使用 Web Workers(浏览器)
总结
- 不需要显式配置:调度器由运行时内置提供
- 自动调度:事件循环自动处理异步任务
- 无需额外设置:直接使用 async/await
即可
中,所有 async` 函数都会自动使用这些内置机制,无需额外配置。在 Java 中,与 JavaScript 的 Promise 最接近的是 CompletableFuture(Java 8+)。下面是对比和示例:
主要等价物
1. CompletableFuture(最接近 Promise)
CompletableFuture 提供了与 Promise 类似的链式调用和组合能力:
#### JavaScript Promise 示例
// TypeScript/JavaScript
async function getDetails(planId: string) {
const response = await fetch(`/api/details/${planId}`)
return response.json()
}
#### Java CompletableFuture 等价写法
import java.util.concurrent.CompletableFuture;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public CompletableFuture<String> getDetails(String planId) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("/api/details/" + planId))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> {
// 处理响应
return response.body();
});
}
对比表
| JavaScript Promise | Java 等价物 | 说明 |
|---|---|---|
Promise | CompletableFuture | 最接近,支持链式调用 |
async/await | CompletableFuture + thenApply/thenCompose | 需要手动链式调用 |
Promise.all() | CompletableFuture.allOf() | 等待所有任务完成 |
Promise.race() | CompletableFuture.anyOf() | 等待任意一个完成 |
.then() | .thenApply() / .thenCompose() | 链式处理 |
.catch() | .exceptionally() / .handle() | 错误处理 |
实际代码对比
JavaScript(来自你的代码)
// agent.ts 中的代码风格
export const getAllAgents = async (): Promise<AgentEntity[]> => {
const response = await fetch('/api/agent-management')
return handleResponse(response)
}
Java 等价写法
import java.util.concurrent.CompletableFuture;
import java.util.List;
public class AgentService {
private static final String BASE_URL = "/api/agent-management";
public CompletableFuture<List<AgentEntity>> getAllAgents() {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> {
if (response.statusCode() != 200) {
throw new RuntimeException("HTTP error: " + response.statusCode());
}
return parseJson(response.body()); // 解析 JSON
})
.exceptionally(error -> {
System.err.println("Failed to get agents: " + error);
throw new RuntimeException(error);
});
}
}
其他 Java 异步选项
2. Future(基础版本)
import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> {
// 异步任务
return "result";
});
String result = future.get(); // 阻塞等待
- 功能较少,不支持链式调用
- 需要手动管理线程池
3. ListenableFuture(Guava)
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Futures;
ListenableFuture<String> future = service.submit(() -> "result");
Futures.addCallback(future, new FutureCallback<String>() {
public void onSuccess(String result) { }
public void onFailure(Throwable t) { }
}, executor);
- 需要 Guava 依赖
- 功能不如
CompletableFuture丰富
4. Reactive Streams(RxJava / Project Reactor)
// RxJava
Observable<String> observable = Observable.fromCallable(() -> "result");
observable.subscribe(
result -> System.out.println(result),
error -> System.err.println(error)
);
// Project Reactor (Spring WebFlux)
Mono<String> mono = Mono.fromCallable(() -> "result");
mono.subscribe(
result -> System.out.println(result),
error -> System.err.println(error)
);
- 更强大的响应式编程模型
- 适合复杂的数据流处理
推荐使用
对于大多数场景,推荐使用 CompletableFuture:
优点
- Java 8+ 内置,无需额外依赖
- 支持链式调用,代码清晰
- 支持组合多个异步操作
- 错误处理机制完善
示例:类似你的 handleResponse
private CompletableFuture<Response> handleResponse(HttpResponse<String> response) {
if (response.statusCode() != 200) {
return CompletableFuture.failedFuture(
new RuntimeException("HTTP error: " + response.statusCode())
);
}
return CompletableFuture.completedFuture(response);
}
// 使用
public CompletableFuture<AgentEntity[]> getAllAgents() {
return sendRequest("/api/agent-management")
.thenCompose(this::handleResponse)
.thenApply(response -> parseJson(response.body()));
}
总结
- 最接近 Promise:
CompletableFuture - 使用场景:异步网络请求、并发任务处理
- 优势:内置、链式调用、组合能力强
- 适用版本:Java 8+
@Async 注解配合 CompletableFuture,使用更简洁。JManus是一个前后端分离的项目。说明如下:
项目架构
1. 后端:Java Spring Boot
- 位置:项目根目录(
pom.xml) - 技术栈:Spring Boot 3.x + Java 17
- 运行端口:
18080 - 作用:提供 RESTful API 服务
2. 前端:Vue3 + Vite
- 位置:
ui-vue3/目录 - 技术栈:Vue 3 + TypeScript + Vite
- 作用:纯前端应用,运行在浏览器中
API 目录的作用
src/api/ 目录中的代码是前端调用后端 API 的客户端封装,运行在浏览器中,不是服务器端代码。
工作原理
// cron-api-service.ts 中的代码
static async getAllCronTasks(): Promise<CronConfig[]> {
const response = await fetch(this.BASE_URL) // 浏览器发起 HTTP 请求
// ...
}
这段代码:
- 运行在浏览器中(不是 Node.js 服务器)
- 使用浏览器的
fetchAPI 发起 HTTP 请求 - 请求发送到后端 Spring Boot 服务器(
http://localhost:18080)
开发时的代理配置
查看 vite.config.ts:
``31:43:ui-vue3/vite.config.ts
server: {
open: true, // Automatically open browser on startup
host: true, // Allow external access
proxy: {
'/api': {
target: 'http://localhost:18080',
changeOrigin: true,
},
'/admin': {
target: 'http://localhost:18080',
changeOrigin: true,
},
},
},
`
22:27:ui-vue3/vite.config.ts
export default defineConfig({
base: '/ui',
build: {
outDir: './ui',
sourcemap: true, // Enable source maps for production builds
},
开发时:
- 前端运行在 Vite 开发服务器(通常是 `http://localhost:5173`)
- 后端运行在 Spring Boot(`http://localhost:18080`)
- Vite 代理:前端请求 `/api/*` 会被代理到后端服务器
### 生产环境的部署
查看构建配置:
┌─────────────────────────────────────────┐
│ 浏览器(用户访问) │
│ http://localhost:18080/ui │
└──────────────┬──────────────────────────┘
│
│ HTTP 请求
▼
┌─────────────────────────────────────────┐
│ Spring Boot 服务器 (端口 18080) │
│ ┌──────────────────────────────────┐ │
│ │ 静态资源服务 │ │
│ │ /ui/* → src/main/resources/ │ │
│ │ static/ui/ │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ RESTful API │ │
│ │ /api/* → Java Controller │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
▲
│
│ fetch('/api/...')
│
┌──────────────┴──────────────────────────┐
│ 前端代码(运行在浏览器中) │
│ ui-vue3/src/api/*.ts │
│ 这些文件被编译打包后运行在浏览器 │
└─────────────────────────────────────────┘
生产环境:
1. 前端构建:`pnpm build` 生成静态文件到 `ui-vue3/ui/` 目录
2. 部署到后端:构建产物会被复制到 `src/main/resources/static/ui/`
3. 统一服务:Spring Boot 同时提供:
- API 服务:`http://localhost:18080/api/*`
- 前端页面:`http://localhost:18080/ui/*`
## 架构图
总结
- ui-vue3
是纯前端项目,不是 Node.js 服务器项目 - src/api/
目录是前端 API 客户端封装,运行在浏览器中 - 开发时:前端(Vite)和后端(Spring Boot)分离运行,通过代理通信
- 生产时:前端构建后由 Spring Boot 统一提供服务
- 前后端分离,可独立开发和部署
- 类型安全:TypeScript 提供类型检查
- 代码复用:API 调用逻辑集中管理
- 统一部署:生产环境由 Spring Boot 统一服务
ui-vue3 项目架构分析
一、技术栈
#### 核心框架
- Vue 3.3.8(Composition API)
- TypeScript 5.3.3
- Vite 5.4.21
- Ant Design Vue 4.0.8
- Vue Router 4.2.5
- Pinia 2.3.1(状态管理)
- vue-i18n 9.14.5(国际化)
- Monaco Editor(代码编辑器)
- Axios 1.12.0(HTTP 客户端)
二、项目目录结构
ui-vue3/src/
├── api/ # API 服务层(17个服务文件)
│ ├── agent.ts
│ ├── common-api-service.ts
│ ├── cron-api-service.ts
│ └── ...
├── base/ # 基础配置
│ └── i18n/ # 国际化配置
├── components/ # 可复用组件(43个组件)
│ ├── chat/ # 聊天相关组件
│ ├── sidebar/ # 侧边栏组件
│ ├── editor/ # 编辑器组件
│ └── ...
├── composables/ # 组合式函数(3个)
│ ├── useRequest.ts
│ ├── useMessage.ts
│ └── useMcpConfigForm.ts
├── plugins/ # 插件
│ └── useToast.ts
├── router/ # 路由配置
│ ├── index.ts
│ └── defaultRoutes.ts
├── stores/ # Pinia 状态管理(6个 store)
│ ├── task.ts
│ ├── memory.ts
│ ├── namespace.ts
│ └── ...
├── types/ # TypeScript 类型定义(8个)
│ ├── agent-execution-detail.ts
│ ├── cron-task.ts
│ └── ...
├── utils/ # 工具函数(5个)
│ ├── request.ts
│ ├── plan-execution-manager.ts
│ └── ...
└── views/ # 页面视图(4个主要页面)
├── home/
├── direct/
├── configs/
└── init/
三、核心架构层次
#### 1. 入口层(Entry Layer)
``17:33:ui-vue3/src/main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import Antd from 'ant-design-vue'
import router from './router' import App from './App.vue' import 'ant-design-vue/dist/reset.css' import { i18n } from '@/base/i18n'
import Vue3ColorPicker from 'vue3-colorpicker' import 'vue3-colorpicker/style.css' import 'nprogress/nprogress.css'
const app = createApp(App) const pinia = createPinia()
app.use(pinia).use(Antd).use(Vue3ColorPicker).use(i18n).use(router).mount('#app')
职责:
- 初始化 Vue 应用
- 注册全局插件(Pinia、Ant Design、i18n、Router)
- 挂载根组件
#### 2. 路由层(Router Layer)
17:59:ui-vue3/src/router/index.ts
import { createRouter, createWebHashHistory } from 'vue-router'
import { routes } from '@/router/defaultRoutes'const options = { history: createWebHashHistory('/ui'), routes, } const router = createRouter(options)
// Global route guard for initialization check router.beforeEach(async (to, _from, next) => { // Skip initialization check for the init page itself if (to.path === '/init') { next() return }
try { // Check initialization status from server const response = await fetch('/api/init/status') const result = await response.json()
if (result.success && !result.initialized) { // System not initialized, redirect to init page localStorage.removeItem('hasInitialized') next('/init') return } else if (result.success && result.initialized) { // System is initialized, save to localStorage localStorage.setItem('hasInitialized', 'true') } } catch (error) { console.warn('Failed to check initialization status:', error) // If check fails, rely on localStorage const hasInitialized = localStorage.getItem('hasInitialized') === 'true' if (!hasInitialized) { next('/init') return } }
next() })
特点:
- 使用 Hash 路由(`createWebHashHistory`)
- 全局前置守卫:检查系统初始化状态
- 路由懒加载:使用动态 `import()`
路由结构:
- `/init` - 初始化页面
- `/home` - 首页(对话)
- `/direct/:id?` - 直接执行页面
- `/configs/:category?` - 配置页面
#### 3. API 服务层(API Service Layer)
组织方式:按功能模块划分,每个模块一个服务文件
19:34:ui-vue3/src/api/cron-api-service.ts
export class CronApiService {
private static readonly BASE_URL = '/api/cron-tasks'/**
- Get all cron tasks
设计模式:
- 类静态方法模式(如 `CronApiService`)
- 函数导出模式(如 `agent.ts`)
- 统一错误处理:私有 `handleResponse` 方法
API 服务列表(17个):
- `agent.ts` - 代理管理
- `common-api-service.ts` - 通用执行器
- `cron-api-service.ts` - 定时任务
- `config-api-service.ts` - 配置管理
- `memory-api-service.ts` - 记忆管理
- `model-api-service.ts` - 模型配置
- 等等...
#### 4. 状态管理层(State Management Layer)
使用 Pinia,采用 Composition API 风格:
28:193:ui-vue3/src/stores/task.ts
export const useTaskStore = defineStore('task', () => {
const currentTask = ref// Set new task const setTask = (prompt: string) => { console.log('[TaskStore] setTask called with prompt:', prompt)
// Don't create tasks with empty prompts if (!prompt.trim()) { console.warn('[TaskStore] Empty prompt provided, not creating task') return }
const newTask = { prompt, timestamp: Date.now(), processed: false, } currentTask.value = newTask console.log('[TaskStore] Task set, currentTask.value:', currentTask.value) } // ... 更多方法
Store 列表(6个):
- `task.ts` - 任务管理
- `memory.ts` - 记忆存储
- `namespace.ts` - 命名空间
- `sidebar.ts` - 侧边栏状态
- `uploadedFiles.ts` - 上传文件
- `counter.ts` - 计数器(示例)
#### 5. 组件层(Component Layer)
组件组织:按功能模块划分
主要组件模块:
- `chat/` - 聊天相关(9个组件)
- `ChatContainer.vue` - 聊天容器
- `ChatMessage.vue` - 消息组件
- `AssistantMessage.vue` - AI 助手消息
- `UserMessage.vue` - 用户消息
- `composables/` - 聊天相关组合式函数
- `sidebar/` - 侧边栏(6个组件)
- `Sidebar.vue` - 主侧边栏
- `PlanGenerator.vue` - 计划生成器
- `JsonEditor.vue` - JSON 编辑器
- `editor/` - 代码编辑器
- `MonacoEditor.ts` - Monaco 编辑器封装
- `configs/` - 配置相关组件
#### 6. 视图层(View Layer)
页面结构:
- `home/index.vue` - 首页(对话界面)
- `direct/index.vue` - 直接执行页面
- `configs/index.vue` - 配置页面(包含多个子配置)
- `init/index.vue` - 初始化页面
#### 7. 工具层(Utility Layer)
1:39:ui-vue3/src/composables/useRequest.ts
import { ref } from 'vue'
import type { ApiResponse } from '@/types/mcp'export function useRequest() { const loading = ref(false)
const executeRequest = async
if (result.success && successMessage) { // Need to pass showMessage function from outside to avoid circular dependencies console.log(successMessage) } else if (!result.success && errorMessage) { console.error(errorMessage) }
return result } catch (error) { console.error('Request execution failed:', error) if (errorMessage) { console.error(errorMessage) } return null } finally { loading.value = false } }
return { loading, executeRequest, } }
工具函数:
- `useRequest.ts` - 请求处理组合式函数
- `request.ts` - HTTP 请求封装
- `plan-execution-manager.ts` - 计划执行管理
- `cron-task-utils.ts` - 定时任务工具
### 四、数据流架构
┌─────────────────────────────────────────────────┐
│ 用户交互(Views/Components) │
└──────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 状态管理(Pinia Stores) │
│ - task.ts │
│ - memory.ts │
│ - namespace.ts │
└──────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ API 服务层(API Services) │
│ - agent.ts │
│ - cron-api-service.ts │
│ - common-api-service.ts │
└──────────────┬──────────────────────────────────┘
│
│ fetch('/api/...')
▼
┌─────────────────────────────────────────────────┐
│ 后端 Spring Boot(端口 18080) │
│ RESTful API │
└─────────────────────────────────────────────────┘
### 五、设计模式与最佳实践
#### 1. 组合式 API(Composition API)
- 使用 `<script setup>` 语法
- 组合式函数(Composables)复用逻辑
#### 2. 关注点分离
- API 层:独立服务文件
- 状态管理:Pinia stores
- UI 组件:按功能模块组织
- 工具函数:独立 utils
#### 3. 类型安全
- TypeScript 类型定义(`types/` 目录)
- 接口定义用于 API 请求/响应
#### 4. 国际化支持
38:46:ui-vue3/src/base/i18n/index.ts
export const i18n = createI18n({
legacy: false,
locale: localeConfig.locale,
fallbackLocale: 'en',
messages: {
en: en,
zh: zh,
},
})
`#### 5. 路由守卫
- 全局前置守卫检查初始化状态
- 自动重定向到初始化页面
六、构建与部署
#### 开发环境
- Vite 开发服务器
- 代理配置:
/api → http://localhost:18080
热模块替换(HMR)
#### 生产环境
- 构建输出:
ui-vue3/ui/
部署到: src/main/resources/static/ui/`
七、架构特点总结
优点: 1. 模块化:按功能清晰划分 2. 类型安全:TypeScript 覆盖 3. 可维护性:代码组织清晰 4. 可扩展性:易于添加新功能 5. 前后端分离:独立开发和部署
技术亮点:
- Vue 3 Composition API
- Pinia 状态管理
- TypeScript 类型系统
- 组件化设计
- 国际化支持
状态管理层(State Management Layer)的作用
状态管理层用于集中管理应用的状态,让多个组件共享和同步数据。
一、核心问题:为什么需要状态管理?
#### 问题场景:没有状态管理时
假设没有状态管理,组件之间需要这样传递数据:
组件A (首页)
↓ props 传递
组件B (侧边栏)
↓ emit 事件
组件C (聊天容器)
↓ props 传递
组件D (输入框)
问题: 1. 数据传递链路过长(Props Drilling) 2. 兄弟组件难以通信 3. 状态分散,难以追踪 4. 代码重复,难以维护
#### 解决方案:使用状态管理
┌─────────────────────────────────┐
│ 状态管理层 (Pinia Store) │
│ ┌───────────────────────────┐ │
│ │ taskStore │ │
│ │ - currentTask │ │
│ │ - setTask() │ │
│ └───────────────────────────┘ │
│ ┌───────────────────────────┐ │
│ │ memoryStore │ │
│ │ - selectMemoryId │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
↑ ↑ ↑
│ │ │
组件A 组件B 组件C
所有组件直接访问同一个状态源,无需层层传递。
二、项目中的实际应用
#### 1. 任务状态管理(Task Store)
用途:管理当前执行的任务状态
``28:50:ui-vue3/src/stores/task.ts
export const useTaskStore = defineStore('task', () => {
const currentTask = ref
// Set new task const setTask = (prompt: string) => { console.log('[TaskStore] setTask called with prompt:', prompt)
// Don't create tasks with empty prompts if (!prompt.trim()) { console.warn('[TaskStore] Empty prompt provided, not creating task') return }
const newTask = { prompt, timestamp: Date.now(), processed: false, } currentTask.value = newTask console.log('[TaskStore] Task set, currentTask.value:', currentTask.value) }
使用场景:
- 首页设置任务 → 直接执行页面接收并执行
- 侧边栏生成计划 → 任务状态更新
- 多个组件需要知道当前是否有运行中的任务
#### 2. 命名空间管理(Namespace Store)
用途:管理当前选中的命名空间
20:32:ui-vue3/src/stores/namespace.ts
export const usenameSpaceStore = defineStore('namespace', () => {
const namespace = ref const namespaces = ref
return { namespace, namespaces, setCurrentNs, setNamespaces } })
使用场景:
- 配置页面切换命名空间 → 所有页面自动更新
- API 调用需要知道当前命名空间
- 多个组件需要显示当前命名空间
#### 3. 记忆管理(Memory Store)
用途:管理对话记忆状态
29:52:ui-vue3/src/stores/memory.ts
export class MemoryStore {
// Basic state
isCollapsed = false
selectMemoryId = ''
loadMessages = () => {}
intervalId: number | undefined = undefinedtoggleSidebar() { this.isCollapsed = !this.isCollapsed if (this.isCollapsed) { this.loadMessages() this.intervalId = window.setInterval(() => { this.loadMessages() }, 3000) } else { clearInterval(this.intervalId) } }
selectMemory(memoryId: string) { this.toggleSidebar() this.selectMemoryId = memoryId }
使用场景:
- 记忆侧边栏的展开/折叠状态
- 当前选中的记忆 ID
- 自动刷新消息的定时器
### 三、状态管理的核心功能
#### 1. 集中存储(Centralized Storage)
所有状态集中在一个地方,便于管理:
typescript
// 所有任务相关的状态都在这里
const taskStore = useTaskStore()// 任何组件都可以访问 taskStore.currentTask // 当前任务 taskStore.setTask() // 设置任务 taskStore.hasRunningTask() // 检查是否有运行中的任务
#### 2. 响应式更新(Reactive Updates)
状态改变时,所有使用该状态的组件自动更新:
typescript
// 组件A:设置任务
taskStore.setTask('分析日志')// 组件B:自动接收到更新(无需手动刷新) watch(() => taskStore.currentTask, (newTask) => { console.log('任务已更新:', newTask) })
#### 3. 跨组件通信(Cross-Component Communication)
不同组件之间可以轻松共享状态:
typescript
// 首页组件
const taskStore = useTaskStore()
taskStore.setTask('新任务')// 直接执行页面组件(自动接收) watch(() => taskStore.currentTask, (task) => { if (task && !task.processed) { // 自动执行任务 executeTask(task.prompt) } })
#### 4. 状态持久化(State Persistence)
可以结合 localStorage 实现状态持久化:
95:100:ui-vue3/src/stores/task.ts
// Set that home page has been visited
const markHomeVisited = () => {
hasVisitedHome.value = true
// Save to localStorage
localStorage.setItem('hasVisitedHome', 'true')
}
### 四、实际使用示例
#### 场景:从首页跳转到直接执行页面并执行任务
typescript
// 1. 首页组件 (home/index.vue)
const taskStore = useTaskStore()function handleSend() { // 设置任务到 store taskStore.setTask('分析系统日志') // 跳转到直接执行页面 router.push('/direct') }
// 2. 直接执行页面 (direct/index.vue) const taskStore = useTaskStore()
// 监听任务变化 watch( () => taskStore.currentTask, (newTask) => { if (newTask && !newTask.processed && newTask.prompt.trim()) { // 自动执行任务 executeTask(newTask.prompt) // 标记为已处理 taskStore.markTaskAsProcessed() } } ) ``
优势:
- 无需通过 URL 参数传递
- 无需通过事件总线
- 代码清晰,易于维护
五、状态管理 vs 其他方案
| 方案 | 适用场景 | 缺点 |
|---|---|---|
| Props/Events | 父子组件通信 | 深层传递困难 |
| Event Bus | 简单事件通信 | 难以追踪,容易混乱 |
| 状态管理 (Pinia) | 复杂应用,多组件共享状态 | 需要学习成本 |
| localStorage | 持久化数据 | 不是响应式的 |
六、总结
状态管理层的作用:
1. 集中管理:所有状态集中在一个地方 2. 响应式更新:状态改变自动更新所有组件 3. 跨组件通信:不同组件轻松共享状态 4. 代码复用:状态逻辑可以复用 5. 易于调试:状态变化可追踪 6. 类型安全:TypeScript 提供类型检查
在这个项目中,状态管理层是连接各个组件的桥梁,让整个应用的状态管理更加清晰和高效。
🌟 智谱 GLM-5 已上线
我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。
🎁 领取 2000万 Tokens