当前位置: 首页 > news >正文

企业级AI Agent平台架构设计与Spring Boot实现

在实际企业级应用开发中,AI Agent 已不再是简单的聊天机器人,而是能够感知环境、规划决策、执行复杂任务并持续学习的智能体。一个健壮的 AI Agent 平台,其核心挑战在于如何将大语言模型(LLM)的认知能力与确定性的业务流程、异构的工具调用以及持久化的状态管理无缝集成。这涉及到清晰的分层架构设计、灵活的任务编排引擎以及对异常与并发场景的稳健处理。

本文将以一个可落地的技术视角,深入剖析一个面向生产环境的 AI Agent 平台架构。我们将从顶层设计思路出发,逐步拆解核心模块,并聚焦于任务编排这一中枢系统的实现细节。通过结合 Spring Boot 的工程化实践,展示如何构建一个支持多技能(Skill)调度、具备上下文管理能力、且易于监控扩展的 Agent 系统。无论你是正在设计此类系统,还是准备应对相关的技术面试,理解这套从设计到实现的完整链路都至关重要。

1. 理解 AI Agent 平台的核心架构与设计目标

在动手写代码之前,必须明确我们要构建的系统是什么,以及它需要解决哪些关键问题。一个 AI Agent 平台不同于单次问答的 ChatGPT 应用,它是一个支持多个智能体(Agent)运行、管理其生命周期、并协调其完成复杂、多步骤任务的系统。

1.1 什么是 AI Agent 平台?

你可以将其类比为一个“智能体工厂”或“任务调度中心”。它的核心职责包括:

  • Agent 管理:创建、配置、销毁不同的 Agent 实例。每个 Agent 可以拥有不同的角色设定、系统提示词(System Prompt)和可用技能集。
  • 任务编排:接收一个高层级的目标(如“分析本季度销售数据并生成报告”),将其分解为一系列可执行的原子步骤(调用数据查询技能、调用图表生成技能、调用报告格式化技能),并控制这些步骤的执行顺序和条件分支。
  • 上下文管理:在 Agent 执行多轮对话或多步骤任务时,持久化和管理对话历史、中间结果、工具调用记录等,确保 Agent 拥有连续的“记忆”。
  • 工具集成:将外部能力(如数据库查询、API 调用、代码执行、文件操作)封装成统一的“工具”或“技能”,供 Agent 在规划时调用。
  • 状态监控与可观测性:跟踪每个 Agent 和任务的生命周期状态、耗时、Token 消耗、工具调用成功率等,便于问题排查和性能优化。

1.2 平台的设计目标与技术指标

在设计之初,就需要确立非功能性需求(技术指标),它们将直接影响技术选型和架构决策。

设计目标具体技术指标与考量
高可用与弹性核心编排引擎无单点故障,支持水平扩展。Agent 任务执行不应因单个节点宕机而丢失。
可扩展性技能(Tool/Skill)应能像插件一样方便地接入和卸载,不影响核心流程。支持新的 LLM 供应商接入。
可维护性与可观测性系统需提供清晰的日志、指标(Metrics)和链路追踪(Tracing)。任务流应可视化,便于调试。
性能与成本合理设计上下文窗口,避免不必要的 Token 消耗。支持异步执行长任务,避免阻塞请求。实现 LLM 调用缓存、限流与降级策略。
安全性对工具调用进行权限校验和输入过滤。防止提示词注入(Prompt Injection)。管理好包含敏感信息的上下文。

基于以上目标,一个典型的分层架构便浮现出来。

2. 平台分层架构设计与核心组件

我们采用分层架构来分离关注点,确保系统清晰、可测试、易扩展。一个常见的 AI Agent 平台可以分为以下四层:

表现层 (Presentation Layer) ├── Web API (RESTful / WebSocket) ├── 管理控制台 (前端,如 Vue.js) └── 客户端 SDK 应用服务层 (Application Service Layer) ├── Agent 管理服务 ├── 任务编排引擎 (核心) ├── 会话/上下文管理服务 └── 技能路由服务 领域层 (Domain Layer) ├── Agent (聚合根,包含状态、记忆、技能集) ├── Task / Workflow (任务定义与执行实例) ├── Skill/Tool (领域服务,封装具体能力) ├── Conversation (对话上下文) └── LLM Adapter (抽象LLM调用) 基础设施层 (Infrastructure Layer) ├── LLM 供应商客户端 (OpenAI, Anthropic, 本地模型等) ├── 向量数据库 (用于长期记忆检索) ├── 关系型数据库 (存储元数据、状态) ├── 消息队列 (用于异步任务) └── 缓存、对象存储等

2.1 各层职责详解

应用服务层是业务逻辑的协调者。其中的任务编排引擎是整个平台的大脑。它不关心具体技能如何实现,只负责解析用户目标,根据预定义的流程或动态规划(利用LLM)生成执行计划(Plan),然后驱动 Agent 按计划一步步执行。

领域层是业务核心的体现。Agent是一个富领域模型,它持有当前会话的Conversation上下文,并拥有一个SkillRegistry来查询可用的技能。Skill是一个抽象,定义了统一的调用接口(execute方法),其具体实现则依赖基础设施层的各种客户端。

基础设施层提供技术能力。LLM Adapter是一个关键设计,它抽象了不同供应商(OpenAI GPT, Claude, 本地 Llama 等)的 API 差异,向上层提供统一的文本补全、聊天、函数调用等接口。这符合依赖倒置原则,使得更换模型供应商变得容易。

2.2 关键技术选型建议

对于基于 Spring Boot 的 Java 技术栈实现,可以参考以下选型:

  • Web 框架: Spring Boot 3.x + Spring MVC / WebFlux (如需响应式)。
  • 任务编排: 可选用轻量级工作流引擎如FlowableCamunda,或自研基于状态机的编排器。对于复杂度不高的场景,自定义一个Pipeline处理器也足够。
  • 状态持久化: 使用Spring Data JPA+Hibernate存储 Agent、Task、Conversation 等实体。对于高频读写的上下文片段,可结合 Redis 缓存。
  • 异步处理: 使用Spring @Async或集成RabbitMQ/Kafka处理耗时任务。
  • 可观测性: 集成Micrometer+Prometheus收集指标,使用Spring Cloud SleuthOpenTelemetry实现链路追踪。
  • LLM 集成: 可使用Spring AI项目,它提供了对多种 LLM 的统一抽象和便捷的 Starter,能极大简化配置和调用代码。

3. 任务编排引擎:从设计思路到 Spring Boot 实现

任务编排是平台最复杂的部分。其本质是将一个抽象目标转化为一系列有序的、可执行的动作。有两种主要模式:静态编排(预定义工作流)和动态编排(LLM实时规划)。

3.1 编排引擎的设计思路

  1. 接收目标:引擎接收一个用户请求,其中包含目标描述(如“订一张明天北京飞上海的最便宜机票”)和初始会话ID。
  2. 规划生成
    • 静态模式:根据目标类型匹配预定义的流程图(BPMN)或 YAML/JSON 描述的工作流模板。
    • 动态模式:将目标、可用技能列表、历史上下文发送给 LLM,要求其生成一个 JSON 格式的执行计划。例如,LLM 可能输出:[{"skill": “search_flights”, “input”: {“from”: “北京”, “to”: “上海”, “date”: “明天”}}, {"skill”: “compare_prices”, “input”: {“results”: “$step1.output”}}, ...]
  3. 计划执行:引擎按顺序(或并行)执行计划中的每个步骤。对于每个步骤:
    • 根据skill名称从技能注册中心查找对应的Skill实现。
    • input参数(可能包含上一步的输出$step1.output)进行解析和替换。
    • 调用skill.execute(input)方法。
    • 捕获执行结果或异常,更新步骤状态。
  4. 上下文管理:将每一步的输入、输出、元数据追加到当前会话的上下文中,为后续步骤或LLM的下一轮思考提供信息。
  5. 状态推进与异常处理:监控每个步骤的执行状态(PENDING, RUNNING, SUCCESS, FAILED)。某个步骤失败时,可根据预定义策略(重试、跳过、终止整个流程)进行处理。

3.2 基于 Spring Boot 的简化实现

下面我们实现一个高度简化的动态编排引擎核心,展示其关键代码结构。

首先,定义领域模型和关键接口:

// 领域模型:任务执行步骤 @Data public class WorkflowStep { private String stepId; private String skillName; // 要调用的技能名称 private Map<String, Object> input; // 输入参数,支持模板表达式如 ${previousStep.output} private Map<String, Object> output; // 执行输出 private StepStatus status; private String errorMessage; } public enum StepStatus { PENDING, RUNNING, SUCCESS, FAILED } // 技能抽象接口 public interface Skill { String getName(); // 技能唯一标识 String getDescription(); // 技能描述,用于生成LLM提示词 SkillResult execute(Map<String, Object> input) throws SkillExecutionException; } @Data public class SkillResult { private boolean success; private Map<String, Object> data; // 技能执行返回的数据 private String message; }

其次,实现一个核心的编排服务OrchestrationService

@Service @Slf4j public class OrchestrationService { @Autowired private SkillRegistry skillRegistry; // 技能注册中心 @Autowired private LLMService llmService; // 统一的LLM服务 @Autowired private ConversationService conversationService; // 上下文服务 /** * 执行动态编排任务 * @param agentId 智能体ID * @param userGoal 用户目标 * @return 最终执行结果 */ @Async // 异步执行长任务 public CompletableFuture<Map<String, Object>> executeDynamicWorkflow(String agentId, String userGoal) { // 1. 获取或创建会话上下文 ConversationContext context = conversationService.getOrCreateContext(agentId, userGoal); // 2. 获取可用技能列表描述,用于LLM规划 List<Skill> availableSkills = skillRegistry.getAllSkills(); String skillsDescription = buildSkillsDescription(availableSkills); // 3. 调用LLM进行规划,生成步骤列表 String planningPrompt = String.format(""" 你是一个任务规划AI。用户目标是:%s 你可以使用的技能有: %s 请生成一个JSON数组,每个元素是一个步骤,包含skillName和input字段。 例如:[{"skillName": "search_web", "input": {"query": "xxx"}}] 只返回JSON,不要有其他解释。 """, userGoal, skillsDescription); String llmResponse = llmService.chatCompletion(planningPrompt, context.getMemory()); List<WorkflowStep> steps = parseStepsFromLLMResponse(llmResponse); // 4. 顺序执行步骤 Map<String, Object> finalOutput = new HashMap<>(); for (int i = 0; i < steps.size(); i++) { WorkflowStep step = steps.get(i); step.setStatus(StepStatus.RUNNING); try { // 解析输入参数中的模板(如 ${step1.output.price}) Map<String, Object> resolvedInput = resolveInputTemplates(step.getInput(), finalOutput); // 查找并执行技能 Skill skill = skillRegistry.getSkill(step.getSkillName()); SkillResult result = skill.execute(resolvedInput); if (result.isSuccess()) { step.setStatus(StepStatus.SUCCESS); step.setOutput(result.getData()); // 将本步骤结果存入finalOutput,供后续步骤引用 finalOutput.put("step" + i, result.getData()); // 更新上下文记忆 context.appendMemory(String.format("步骤[%s]执行成功,输入:%s,输出:%s", step.getSkillName(), resolvedInput, result.getData())); } else { step.setStatus(StepStatus.FAILED); step.setErrorMessage(result.getMessage()); // 处理失败逻辑:重试、终止或转入人工处理 handleStepFailure(step, context); break; } } catch (SkillExecutionException e) { log.error("技能执行异常: {}", step.getSkillName(), e); step.setStatus(StepStatus.FAILED); step.setErrorMessage(e.getMessage()); handleStepFailure(step, context); break; } } // 5. 持久化最终结果和上下文 conversationService.saveContext(context); return CompletableFuture.completedFuture(finalOutput); } // ... 其他辅助方法:buildSkillsDescription, parseStepsFromLLMResponse, resolveInputTemplates, handleStepFailure }

最后,实现一个具体的技能示例,比如“获取天气”:

@Component public class WeatherSkill implements Skill { @Override public String getName() { return "get_weather"; } @Override public String getDescription() { return "获取指定城市的当前天气情况。输入参数:city(城市名)。输出:temperature(温度),condition(天气状况)。"; } @Override public SkillResult execute(Map<String, Object> input) throws SkillExecutionException { String city = (String) input.get("city"); if (StringUtils.isBlank(city)) { throw new SkillExecutionException("城市参数不能为空"); } // 这里模拟调用外部天气API // 实际项目中,会使用RestTemplate或WebClient调用第三方服务 log.info("调用天气API查询城市: {}", city); // 模拟API返回 Map<String, Object> weatherData = new HashMap<>(); weatherData.put("temperature", "22℃"); weatherData.put("condition", "晴"); weatherData.put("city", city); SkillResult result = new SkillResult(); result.setSuccess(true); result.setData(weatherData); result.setMessage("天气查询成功"); return result; } }

3.3 关键配置与依赖

pom.xml中,你需要引入 Spring Boot Web、Spring AI(如果使用)以及数据库等依赖。

<dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI (以OpenAI为例) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>0.8.1</version> <!-- 请使用最新版本 --> </dependency> <!-- 数据持久化 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- 缓存 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>

application.yml中配置 Spring AI 和数据库:

spring: ai: openai: api-key: ${OPENAI_API_KEY:your-key-here} base-url: https://api.openai.com/v1 chat: options: model: gpt-4-turbo-preview # 或 gpt-3.5-turbo temperature: 0.2 # 降低随机性,使规划更稳定 datasource: url: jdbc:mysql://localhost:3306/agent_platform username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true

4. 系统实现中的关键问题与排查路径

即使架构清晰,在实现和运行过程中也会遇到诸多挑战。以下是几个典型问题及其排查思路。

4.1 LLM 规划结果不稳定或格式错误

现象:编排引擎调用 LLM 生成的计划步骤列表(JSON)经常解析失败,或者步骤逻辑不合理。

可能原因与排查

  1. 提示词(Prompt)设计不佳:LLM 没有清晰理解任务和技能描述。检查buildSkillsDescription方法生成的技能描述是否准确、无歧义。提示词中必须严格要求输出格式,并给出明确示例。
  2. 模型温度(Temperature)过高:在规划任务时,应使用较低的temperature(如 0.1-0.3)以减少随机性,使输出更稳定。
  3. 上下文窗口混乱:发送给 LLM 的对话历史(context.getMemory())可能包含过多无关信息或格式混乱,干扰了规划。需要设计有效的上下文窗口滑动策略,只保留相关记忆。
  4. JSON 解析容错性差:LLM 可能在 JSON 外添加了额外标记或解释文本。应在解析前使用正则表达式或尝试使用 LLM 的“函数调用”(Function Calling)或“结构化输出”(Structured Outputs)特性来直接获取结构化数据。

解决建议

  • 优化提示词工程,采用更鲁棒的模板。
  • 在调用 LLM 规划后,加入一个“计划验证”步骤,可以用一条简单的规则或另一个 LLM 调用来检查生成计划的合理性。
  • 使用 Spring AI 的StructuredOutputConverter等功能来获取结构化响应。

4.2 技能执行超时或失败影响整体流程

现象:某个外部 API 技能调用超时,导致整个任务链卡住,或者失败后不知道如何继续。

排查

  1. 检查网络与依赖服务:确认技能调用的外部 API 是否可达、认证是否有效。
  2. 检查技能实现:在Skill.execute方法内部是否有充分的超时设置和异常捕获。推荐使用 Spring 的@Retryable注解为可重试的异常添加重试机制。
  3. 检查编排引擎的异常处理策略handleStepFailure方法的逻辑是否完备?是重试、跳过、还是转到备用技能?

解决建议

  • 为所有外部调用设置合理的超时时间(如使用RestTemplateWebClient配置connectTimeoutreadTimeout)。
  • 在编排引擎中实现断路器模式(Circuit Breaker),例如使用 Resilience4j,当某个技能连续失败时,暂时熔断,避免雪崩。
  • 设计任务流的补偿机制。对于关键步骤,考虑实现其逆向操作(补偿技能),在整体失败时进行回滚。

4.3 上下文管理导致 Token 超限或性能下降

现象:随着对话轮次或任务步骤增加,发送给 LLM 的上下文越来越长,导致 API 调用成本剧增、速度变慢,甚至超出模型上下文窗口限制。

排查

  1. 记录 Token 消耗:在每次调用 LLM 前后,计算提示词的 Token 数。许多客户端库支持此功能。
  2. 分析上下文内容:检查持久化的Conversation中是否存储了过多原始数据(如大段文本、完整 JSON)。这些应被摘要或索引替代。
  3. 检查检索策略:如果是基于向量数据库的长期记忆检索,检查检索到的片段是否精准,是否引入了大量无关信息。

解决建议

  • 摘要压缩:在对话轮次或任务阶段完成后,调用 LLM 对之前的上下文进行摘要,用摘要替换原始长文本。
  • 滑动窗口:只保留最近 N 轮对话或最相关的 K 条记忆。
  • 分层存储:将详细数据存储在数据库,只将关键元数据或索引放入 LLM 上下文。当 LLM 需要细节时,再通过技能去查询。
  • 使用更大上下文窗口的模型:根据成本权衡,选择如 GPT-4 Turbo(128K上下文)等模型。

4.4 常见问题速查表

问题现象可能原因检查点处理建议
Agent 对目标无响应或响应无关1. 系统提示词(System Prompt)未生效或冲突。
2. 技能描述不清晰,LLM无法理解。
3. 上下文被污染。
1. 检查创建Agent时注入的初始提示词。
2. 检查getDescription()返回的技能描述是否清晰。
3. 检查对话历史记录。
1. 优化Agent角色设定和系统指令。
2. 为技能描述提供更具体的示例。
3. 重置或清理当前会话上下文。
任务状态卡在RUNNING1. 技能执行线程阻塞或死锁。
2. 异步任务管理器(如线程池)耗尽。
3. 消息队列消费者宕机。
1. 检查应用日志,寻找技能执行线程的堆栈信息。
2. 监控线程池状态。
3. 检查消息队列健康状态。
1. 为技能执行增加超时和中断机制。
2. 合理配置线程池参数。
3. 实现任务状态心跳和超时自动置为失败。
技能注册中心找不到技能1. Skill 实现类未被 Spring 容器扫描到。
2.getName()返回的值与规划中的skillName不匹配(大小写、空格)。
3. 技能依赖的服务未启动。
1. 检查@Component@Service注解。
2. 对比规划 JSON 中的skillName和注册中心里的 key。
3. 检查技能类依赖注入是否成功。
1. 确保技能包在@SpringBootApplication扫描路径下。
2. 使用常量定义技能名,避免拼写错误。
3. 在技能执行前增加健康检查。

5. 生产环境最佳实践与扩展方向

将 AI Agent 平台投入生产,需要超越“跑通”的层面,关注稳定性、安全性和可维护性。

5.1 安全加固

  • 输入验证与净化:对所有传入技能的参数进行严格的类型检查和内容过滤,防止 SQL 注入、命令注入等攻击。特别是当技能涉及系统调用或数据库操作时。
  • 权限控制:实现基于角色的技能访问控制(RBAC)。不是所有 Agent 都能调用所有技能。在SkillRegistry查找技能时,应校验当前 Agent 或用户的权限。
  • 提示词安全:避免将用户输入直接拼接到系统提示词中,防止提示词注入攻击。对用户输入进行转义或使用独立的“用户消息”字段。
  • 敏感信息处理:在日志和上下文中,对 API Keys、个人信息等敏感数据进行脱敏。考虑使用安全的配置管理服务(如 Vault)存储密钥。

5.2 可观测性与监控

  • 结构化日志:使用 JSON 或结构化格式记录日志,包含agentId,sessionId,workflowId,stepId等关键字段,便于聚合查询。记录每一次 LLM 调用的请求和响应摘要(注意脱敏)。
  • 关键指标监控
    • agent.task.received.count:接收任务数。
    • agent.task.success.rate:任务成功率。
    • llm.api.call.duration:LLM API 调用耗时。
    • skill.execution.duration/skill.execution.error.count:各技能执行耗时和错误数。
    • token.usage.prompt/token.usage.completion:Token 消耗量。
  • 分布式追踪:为每个用户请求或任务生成唯一的traceId,并贯穿所有服务、LLM 调用和技能执行,以便在出现问题时快速定位全链路瓶颈。

5.3 性能与成本优化

  • LLM 调用缓存:对于内容生成类且对实时性要求不高的技能,可以将(prompt, parameters)作为 key,将 LLM 响应缓存一段时间(如 Redis),避免重复计算。
  • 异步与流式响应:对于长耗时任务,务必采用异步接口(如@Async,CompletableFuture)或 WebSocket 推送进度和结果,避免 HTTP 请求超时。对于文本生成,可以考虑使用流式响应(Streaming)提升用户体验。
  • 模型选型与降级:非核心场景可使用更便宜、更快的模型(如 GPT-3.5-Turbo)。当主要模型服务不可用时,应有自动降级到备用模型的策略。
  • 上下文优化:如前所述,积极采用摘要、滑动窗口等技术,严格控制送入模型的 Token 数量,这是控制成本最有效的手段之一。

5.4 扩展方向

  • 支持静态工作流定义:除了动态规划,可以集成工作流引擎(如 Flowable),让业务人员通过可视化界面拖拽定义复杂的、确定的业务流程,与 LLM 动态规划相辅相成。
  • 实现技能市场:设计一套技能描述、发布、发现和安装的机制,让第三方开发者可以贡献技能,丰富平台生态。
  • 强化长期记忆:集成向量数据库(如 Pinecone, Weaviate, Milvus),将对话和任务结果向量化存储。当 Agent 需要历史信息时,通过语义检索召回相关片段,而非简单的时间滑动窗口。
  • 多模态能力:扩展技能框架,支持处理图像、音频等多模态输入,并调用相应的多模态模型(如 GPT-4V)进行分析。
  • Agent 间协作:设计多个专长不同的 Agent 之间通信与协作的协议,让它们能共同解决更宏大的问题。

构建一个成熟的 AI Agent 平台是一个持续迭代的过程。从最小可行产品(MVP)开始,聚焦于核心编排引擎和几个关键技能的打通,然后逐步完善监控、安全、性能优化和生态扩展。理解本文剖析的架构层次和设计思路,能帮助你在技术选型和代码实现上做出更明智的决策,避免在后期陷入重构的泥潭。

http://www.jsqmd.com/news/1099931/

相关文章:

  • MCP Python SDK:给大模型接入工具和数据的标准协议
  • 130多个 Home Assistant 插件,一个人维护的仓库
  • 盐城装修付款避坑的四个核心注意要点
  • 1500公里跑掉的21斤,我用15个月又长了回来——36岁,我决定重返跑步的江湖
  • 鸿蒙原生 ArkTS 布局深度解析:width / height 固定尺寸与百分比尺寸完全指南
  • 基于单片机人脸识别电子密码锁智能门禁指纹识别语音提醒防盗成品11(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 离石 KTV 全套设备
  • 2026年7月国内充值 GPT:为什么我不再建议只找低价渠道?
  • PHP+VUE医疗预约系统毕业设计:全栈开发实战与二次开发指南
  • 3步解锁加密音频:让您的QQ音乐文件在任何设备自由播放
  • 深度测评2026年AI论文工具:这几款让论文写作不再是难题
  • Python 入门:常用数据类型与程序结构详解(二)
  • 从Coze到Dify:手把手构建电商AI智能体工作流实战
  • 算法之旅-Hot100—字母异位词分组
  • DiffusionGemma 是什么:Google 为什么用扩散模型做文本生成
  • AI时代下的前端求生之路
  • 第一章Netty,如何处理客户端断开连接的事件
  • 最新量化验证,回测模拟实盘不是一件事
  • PHP+VUE医疗预约系统毕业设计:从环境搭建到核心业务实现全流程详解
  • 从Prompt到RAG:AI大模型应用开发全链路实战指南
  • 全星 APQP——QMS 一体化平台:打通 QMS,AI 赋能研发数智化建设——上海全星数智平台
  • Mac 党转 Linux 必看:用 keyd 复刻你最熟悉的快捷键习惯
  • Sa-Token:48,800+ Star 的背后让鉴权变得简单优雅
  • open harmony 项目实战:给语文学习 App 做一个高端精致的沉浸式界面
  • OpenCV VideoCapture 类
  • 无人机合速度和航捷转速度分量
  • 大数据志愿填报冲稳保如何搭配院校梯度
  • 龙芯3B6000服务器手动安装Docker 29.5.1实战指南
  • PHP+VUE医疗预约系统毕业设计:全栈开发实战与部署指南
  • MultiFunPlayer完整指南:设备同步与媒体播放的终极解决方案