跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaAIjava算法

基于 Milvus 与混合检索的云厂商文档智能问答系统:Java SpringBoot 实现

介绍基于 Milvus 向量数据库和 Java SpringBoot 构建云厂商文档智能问答系统的方案。针对云文档结构化强、术语密集等特点,采用混合检索架构(稠密向量加稀疏向量),结合智能分块策略与动态权重调整算法。系统包含数据预处理、向量存储、异步处理管道及多层缓存优化,支持流式输出与自动化评估,旨在提升技术文档检索的精准度与效率。

RedisGeek发布于 2026/3/30更新于 2026/9/667 浏览
基于 Milvus 与混合检索的云厂商文档智能问答系统:Java SpringBoot 实现

基于 Milvus 与混合检索的云厂商文档智能问答系统:Java SpringBoot 实现

面对阿里云、腾讯云等厂商海量的产品文档、规格参数与价格清单,如何构建一个精准、高效的智能问答系统?本文介绍从技术选型到生产部署的完整方案。

云服务商的产品生态系统日益庞大,相关的技术文档、规格参数、定价清单等文档数量急剧增长。传统的文档查找方式已无法满足开发者和运维人员快速获取准确信息的需求。

基于检索增强生成(RAG)的智能问答系统成为解决这一难题的有效方案。本文将详细介绍如何使用 Java SpringBoot 和 Milvus 向量数据库,构建一个面向云厂商文档的高效混合检索问答系统。

一、核心挑战与架构选型

云厂商文档具有鲜明的技术特点,这些特点直接影响了我们的技术选择:

  1. 高度结构化:包含大量技术规格表、价格矩阵和配置参数
  2. 专业术语密集:如'ECS.g6.2xlarge'、'对象存储每秒请求数'等精确术语
  3. 多格式混合:Markdown、PDF、Word、TXT 等格式并存
  4. 版本频繁更新:产品迭代快,文档需要及时同步

针对这些挑战,我们选择了混合检索架构(Hybrid Search),结合稠密向量检索(语义理解)和稀疏向量检索(关键词匹配),以达到最佳效果。

系统整体架构分为三个核心层次:

  • 数据预处理层:负责多格式文档解析和智能分块
  • 向量存储与检索层:基于 Milvus 的混合检索实现
  • 应用服务层:SpringBoot 驱动的 REST API 和流式输出

二、数据预处理:多格式文档的智能分块策略

文档分块的质量直接决定了后续检索的精度,特别是在处理结构化云文档时,我们需要比普通文本更精细的策略。

2.1 统一文档解析接口

云厂商文档格式多样,我们使用 Apache Tika 作为统一解析入口,同时针对不同格式进行增强处理:

@Service
public class UnifiedDocumentParser {
    public ParsedDocument parseDocument(MultipartFile file) throws Exception {
        String contentType = file.getContentType();
        String filename = file.getOriginalFilename();
        if (filename.endsWith(".pdf")) {
            // 使用 PDFBox 增强 PDF 解析,保留书签和表格结构
            return parsePdfWithStructure(file);
        } else if (filename.endsWith(".md") || filename.endsWith(".markdown")) {
            // Markdown 按标题层级解析
            return parseMarkdownWithHeadings(file);
        } else if (filename.endsWith(".docx") || filename.endsWith(".doc")) {
            // 使用 POI 解析 Word,保留样式信息
            return parseWordDocument(file);
        } else {
            // TXT 和其他格式使用 Tika 标准解析
            return parseWithTika(file);
        }
    }

    private ParsedDocument parsePdfWithStructure(MultipartFile file) {
        // 提取 PDF 书签结构作为文档大纲
        // 识别表格区域并保持其完整性
        // 将视觉层次转换为逻辑层次
    }
}
2.2 文档类型识别与分块策略路由

不同类型的云文档需要不同的分块策略,我们设计了基于内容分析的智能路由:

文档类型识别特征分块策略块大小建议
规格参数文档包含参数表、技术指标表格保持完整,参数组为单位300-600 字符
价格文档价格表、计费规则按计费项分块,保持表格完整400-800 字符
产品使用文档操作步骤、示例代码按章节标题分块,代码块保持完整600-1200 字符
API 参考文档端点说明、请求响应示例按 API 端点分块500-1000 字符
@Component
public class SmartChunkingRouter {
    public List<DocumentChunk> chunkByContentAnalysis(ParsedDocument doc) {
        DocumentType docType = analyzeDocumentType(doc);
        switch (docType) {
            case SPECIFICATION:
                // 规格文档:检测参数表,保持表格完整性
                return chunkSpecificationDocument(doc);
            case PRICING:
                // 价格文档:检测价格矩阵,按服务项分块
                return chunkPricingDocument(doc);
            case TUTORIAL:
                // 教程文档:按操作步骤和示例分块
                return chunkTutorialDocument(doc);
            case API_REFERENCE:
                // API 文档:按端点和参数说明分块
                return chunkApiDocument(doc);
            default:
                // 默认策略:递归字符分块
                return recursiveTextSplit(doc, 800, 120);
        }
    }

    private DocumentType analyzeDocumentType(ParsedDocument doc) {
        // 基于关键词、结构特征和元数据识别文档类型
        String content = doc.getContent();
        if (containsPricingTable(content)) {
            return DocumentType.PRICING;
        } else if (containsApiEndpoints(content)) {
            return DocumentType.API_REFERENCE;
        } else if (containsSpecParameters(content)) {
            return DocumentType.SPECIFICATION;
        } else if (containsTutorialMarkers(content)) {
            return DocumentType.TUTORIAL;
        }
        return DocumentType.GENERAL;
    }
}
2.3 结构化元数据提取

为每个文档块提取丰富的元数据,为后续检索过滤奠定基础:

public class DocumentChunk {
    private String id;
    private String content;
    private Map<String, String> metadata;
    
    // 核心元数据字段
    private String documentSource;      // 文档来源:aliyun/tencent/huawei
    private String productCategory;     // 产品类别:compute/storage/network
    private String chunkType;           // 块类型:concept/parameter/price/example
    private String sectionTitle;        // 章节标题
    private String productName;         // 产品名称:ECS/RDS/VPC
    private String documentVersion;     // 文档版本
    private Date updateTime;            // 更新时间
}

三、Milvus 向量存储与混合检索实现

Milvus 2.3+ 版本原生支持混合检索(Hybrid Search),为我们提供了完美的技术基础。

3.1 集合 Schema 设计与优化

针对云文档的特点,我们设计了专门的集合结构:

@Data
@MilvusEntity(collectionName = "cloud_docs_chunks")
public class DocumentChunkEntity {
    // 主键字段
    @MilvusField(name = "chunk_id", isPrimaryKey = true)
    private String chunkId;
    
    // 内容字段(用于稀疏检索)
    @MilvusField(name = "content", dataType = DataType.VarChar, maxLength = 65535)
    private String content;
    
    // 稠密向量字段(768 维 BGE-M3 向量)
    @MilvusField(name = "dense_vector", dataType = DataType.FloatVector, dim = 768)
    private List<Float> denseVector;
    
    // 稀疏向量字段(BM25 权重表示)
    @MilvusField(name = "sparse_vector", dataType = DataType.SparseFloatVector)
    private Map<Long, Float> sparseVector;
    
    // 元数据字段(用于过滤和增强检索)
    @MilvusField(name = "doc_source", dataType = DataType.VarChar, maxLength = 50)
    private String docSource;
    
    @MilvusField(name = "product_name", dataType = DataType.VarChar, maxLength = 100)
    private String productName;
    
    @MilvusField(name = "chunk_type", dataType = DataType.VarChar, maxLength = 50)
    private String chunkType;
    
    @MilvusField(name = "tags", dataType = DataType.Array, elementType = DataType.VarChar)
    private List<String> tags;
}
3.2 混合检索的核心实现

混合检索的关键在于同时执行向量相似度搜索和关键词权重搜索,并将结果智能融合:

@Service
public class HybridSearchEngine {
    @Autowired
    private MilvusServiceClient milvusClient;

    public SearchResults hybridSearch(SearchRequest request) {
        // 1. 查询分析与路由
        QueryAnalysisResult analysis = analyzeQuery(request.getQuery());
        
        // 2. 并行执行两种检索
        CompletableFuture<List<SearchResult>> denseFuture = executeDenseVectorSearch(request, analysis);
        CompletableFuture<List<SearchResult>> sparseFuture = executeSparseVectorSearch(request, analysis);
        
        // 3. 结果融合与重排
        return CompletableFuture.allOf(denseFuture, sparseFuture).thenApply(v -> {
            List<SearchResult> denseResults = denseFuture.join();
            List<SearchResult> sparseResults = sparseFuture.join();
            
            // 基于查询类型动态调整权重
            float denseWeight = analysis.isSemanticQuery() ? 0.7f : 0.3f;
            float sparseWeight = 1.0f - denseWeight;
            
            // 加权分数融合
            List<SearchResult> fusedResults = fuseResults(denseResults, sparseResults, denseWeight, sparseWeight);
            
            // 重排提升精度
            return rerankResults(request.getQuery(), fusedResults);
        }).join();
    }

    private QueryAnalysisResult analyzeQuery(String query) {
        // 分析查询类型:概念性查询 vs 精确查询
        QueryAnalysisResult result = new QueryAnalysisResult();
        
        // 检测精确查询模式(产品型号、规格代码、价格查询)
        Pattern specPattern = Pattern.compile("[A-Z]{2,}\\.[a-z0-9]+\\.[a-z0-9]+");
        Pattern pricePattern = Pattern.compile("价格 | 费用 | 计费 | 成本");
        boolean isExactQuery = specPattern.matcher(query).find() || pricePattern.matcher(query).find() || containsExactProductCodes(query);
        
        result.setSemanticQuery(!isExactQuery);
        result.setExactQuery(isExactQuery);
        
        // 提取查询中的产品名称和关键词
        result.setProductNames(extractProductNames(query));
        result.setKeywords(extractKeywords(query));
        
        return result;
    }
}
3.3 查询权重动态调整算法

根据查询类型的分析结果,动态调整混合检索的权重分配:

public class WeightAdjustmentStrategy {
    public static SearchWeights calculateWeights(QueryAnalysisResult analysis) {
        SearchWeights weights = new SearchWeights();
        
        if (analysis.isExactQuery()) {
            // 精确查询:偏向关键词匹配
            weights.setDenseWeight(0.2f); // 语义权重 20%
            weights.setSparseWeight(0.8f); // 关键词权重 80%
            weights.setMetadataBoost(1.5f); // 元数据匹配增强
        } else if (analysis.isSemanticQuery()) {
            // 语义查询:偏向向量匹配
            weights.setDenseWeight(0.7f); // 语义权重 70%
            weights.setSparseWeight(0.3f); // 关键词权重 30%
            weights.setMetadataBoost(1.1f); // 元数据匹配轻微增强
        } else {
            // 混合查询:平衡权重
            weights.setDenseWeight(0.5f);
            weights.setSparseWeight(0.5f);
            weights.setMetadataBoost(1.3f);
        }
        
        // 根据查询长度微调
        int queryLength = analysis.getQueryLength();
        if (queryLength < 10) {
            // 短查询更依赖关键词
            weights.setSparseWeight(weights.getSparseWeight() + 0.1f);
            weights.setDenseWeight(weights.getDenseWeight() - 0.1f);
        }
        
        return weights;
    }
}

四、SpringBoot 微服务集成

4.1 异步文档处理管道

文档处理是计算密集型任务,我们采用全异步管道设计:

@Service
@Slf4j
public class AsyncDocumentPipeline {
    @Autowired
    private ThreadPoolTaskExecutor documentProcessor;

    @Async("documentProcessor")
    public CompletableFuture<ProcessResult> processDocumentAsync(MultipartFile file) {
        return CompletableFuture.supplyAsync(() -> parseDocument(file), documentProcessor)
            .thenApplyAsync(this::analyzeDocumentType, documentProcessor)
            .thenApplyAsync(this::chunkDocument, documentProcessor)
            .thenApplyAsync(chunks -> generateEmbeddings(chunks), documentProcessor)
            .thenApplyAsync(chunks -> generateSparseVectors(chunks), documentProcessor)
            .thenApplyAsync(chunks -> storeInMilvus(chunks), documentProcessor)
            .exceptionally(ex -> {
                log.error("文档处理失败:{}", ex.getMessage());
                return ProcessResult.failure(ex.getMessage());
            });
    }

    // 批量处理优化
    public CompletableFuture<List<ProcessResult>> batchProcess(List<MultipartFile> files) {
        List<CompletableFuture<ProcessResult>> futures = files.stream()
            .map(this::processDocumentAsync)
            .collect(Collectors.toList());
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
    }
}
4.2 REST API 设计

提供简洁清晰的 API 接口:

@RestController
@RequestMapping("/api/v1/rag")
@Tag(name = "智能文档问答", description = "基于 RAG 的云文档智能问答接口")
public class RagController {
    
    @PostMapping("/documents")
    @Operation(summary = "上传文档到知识库")
    public ResponseEntity<UploadResponse> uploadDocument(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "docSource", required = false) String docSource) {
        CompletableFuture<ProcessResult> future = documentPipeline.processDocumentAsync(file, docSource);
        return ResponseEntity.accepted().body(UploadResponse.accepted(future));
    }

    @PostMapping("/query")
    @Operation(summary = "查询知识库")
    public Flux<String> queryKnowledgeBase(@RequestBody QueryRequest request) {
        // 流式返回结果
        return Flux.create(sink -> {
            try {
                // 1. 检索相关文档块
                SearchResults results = searchEngine.hybridSearch(request);
                
                // 2. 构建 LLM 上下文
                String context = buildContext(results);
                
                // 3. 流式调用大模型
                streamLlmResponse(request.getQuestion(), context, sink);
            } catch (Exception e) {
                sink.error(e);
            }
        });
    }

    @GetMapping("/search/similar")
    @Operation(summary = "语义相似搜索")
    public ResponseEntity<List<SearchResult>> semanticSearch(
            @RequestParam String query,
            @RequestParam(defaultValue = "10") int topK) {
        List<SearchResult> results = searchEngine.semanticSearch(query, topK);
        return ResponseEntity.ok(results);
    }
}

五、性能优化与生产部署

5.1 向量检索性能调优
# application.yml Milvus 配置部分
milvus:
  host: ${MILVUS_HOST:localhost}
  port: 19530
  # 连接池配置
  connection-pool:
    max-size: 20
    min-size: 5
    connect-timeout-ms: 5000
    keep-alive-timeout-ms: 180000
  # 索引优化配置
  index:
    dense-vector:
      type: HNSW
      params:
        M: 16
        efConstruction: 200
    sparse-vector:
      type: SPARSE_INVERTED_INDEX
      params:
        drop_ratio_build: 0.2
  # 查询参数优化
  search:
    anns-field: dense_vector
    metric-type: IP
    params:
      nprobe: 16
    top-k: 50
    offset: 0
5.2 缓存策略设计

针对云文档查询特点,设计多层缓存策略:

@Component
@Slf4j
public class QueryCacheManager {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 本地缓存(Caffeine)用于热点查询
    private Cache<String, CacheEntry> localCache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();

    public SearchResults getCachedResults(String queryHash, String filtersHash) {
        String cacheKey = buildCacheKey(queryHash, filtersHash);
        
        // 1. 检查本地缓存
        CacheEntry entry = localCache.getIfPresent(cacheKey);
        if (entry != null && !entry.isExpired()) {
            log.debug("本地缓存命中:{}", cacheKey);
            return entry.getResults();
        }
        
        // 2. 检查 Redis 分布式缓存
        SearchResults redisResults = (SearchResults) redisTemplate.opsForValue().get(cacheKey);
        if (redisResults != null) {
            log.debug("Redis 缓存命中:{}", cacheKey);
            // 回填本地缓存
            localCache.put(cacheKey, new CacheEntry(redisResults));
            return redisResults;
        }
        return null;
    }

    public void cacheResults(String queryHash, String filtersHash, SearchResults results, Duration ttl) {
        String cacheKey = buildCacheKey(queryHash, filtersHash);
        
        // 1. 存入本地缓存
        localCache.put(cacheKey, new CacheEntry(results));
        
        // 2. 存入 Redis,设置 TTL
        redisTemplate.opsForValue().set(cacheKey, results, ttl);
        log.debug("缓存已更新:{}, TTL: {}秒", cacheKey, ttl.getSeconds());
    }

    // 缓存键生成策略:结合查询语义和过滤条件
    private String buildCacheKey(String queryHash, String filtersHash) {
        return String.format("rag:search:%s:%s", queryHash, filtersHash);
    }
}

六、系统监控与评估

6.1 关键监控指标

构建完整的监控体系,跟踪系统健康状态:

@Component
@Slf4j
public class SystemMetricsCollector {
    // 检索质量指标
    private AtomicLong totalQueries = new AtomicLong(0);
    private AtomicLong semanticQueries = new AtomicLong(0);
    private AtomicLong exactQueries = new AtomicLong(0);
    private AtomicLong hybridQueries = new AtomicLong(0);
    
    // 性能指标
    private AtomicLong averageRetrievalTime = new AtomicLong(0);
    private AtomicLong averageRerankTime = new AtomicLong(0);
    private AtomicLong averageLlMTime = new AtomicLong(0);
    
    // 准确率指标
    private Map<String, AtomicLong> chunkTypeHits = new ConcurrentHashMap<>();
    private Map<String, AtomicLong> productHits = new ConcurrentHashMap<>();

    public void recordQuery(QueryAnalysisResult analysis, long retrievalTime, List<SearchResult> results) {
        totalQueries.incrementAndGet();
        if (analysis.isSemanticQuery()) {
            semanticQueries.incrementAndGet();
        } else if (analysis.isExactQuery()) {
            exactQueries.incrementAndGet();
        } else {
            hybridQueries.incrementAndGet();
        }
        
        // 更新平均检索时间(滑动平均)
        long currentAvg = averageRetrievalTime.get();
        long newAvg = (currentAvg * 99 + retrievalTime) / 100;
        averageRetrievalTime.set(newAvg);
        
        // 记录命中类型分布
        if (!results.isEmpty()) {
            for (SearchResult result : results) {
                String chunkType = result.getChunkType();
                chunkTypeHits.computeIfAbsent(chunkType, k -> new AtomicLong(0)).incrementAndGet();
                
                String productName = result.getProductName();
                if (productName != null) {
                    productHits.computeIfAbsent(productName, k -> new AtomicLong(0)).incrementAndGet();
                }
            }
        }
    }

    public MetricsReport generateReport() {
        MetricsReport report = new MetricsReport();
        report.setTimestamp(Instant.now());
        report.setTotalQueries(totalQueries.get());
        report.setSemanticQueryRatio(totalQueries.get() > 0 ? (double) semanticQueries.get() / totalQueries.get() : 0);
        report.setAverageRetrievalTimeMs(averageRetrievalTime.get());
        report.setChunkTypeDistribution(new HashMap<>(chunkTypeHits));
        report.setProductDistribution(new HashMap<>(productHits));
        return report;
    }
}
6.2 检索质量评估

设计自动化评估流程,持续优化系统:

@Service
public class RetrievalEvaluator {
    // 评估检索系统在不同类型查询上的表现
    public EvaluationResult evaluateOnTestSet(TestDataset testSet) {
        EvaluationResult result = new EvaluationResult();
        for (TestCase testCase : testSet.getTestCases()) {
            // 执行检索
            SearchResults searchResults = searchEngine.hybridSearch(SearchRequest.fromTestCase(testCase));
            
            // 计算精度指标
            double precision = calculatePrecision(testCase.getRelevantIds(), searchResults.getResultIds());
            double recall = calculateRecall(testCase.getRelevantIds(), searchResults.getResultIds());
            double ndcg = calculateNDCG(testCase.getRelevantIds(), searchResults.getScoredResults());
            
            // 按查询类型聚合统计
            String queryType = classifyQueryType(testCase.getQuery());
            result.addMetric(queryType, "precision", precision);
            result.addMetric(queryType, "recall", recall);
            result.addMetric(queryType, "ndcg", ndcg);
            
            // 记录失败案例用于分析
            if (precision < 0.5) {
                result.addFailureCase(testCase, searchResults);
            }
        }
        return result;
    }

    // A/B 测试不同检索策略
    public ABTestResult compareStrategies(SearchStrategy strategyA, SearchStrategy strategyB, TestDataset testSet) {
        ABTestResult result = new ABTestResult();
        for (TestCase testCase : testSet.getTestCases()) {
            SearchResults resultsA = executeSearch(strategyA, testCase);
            SearchResults resultsB = executeSearch(strategyB, testCase);
            
            // 人工评估或自动评估
            double scoreA = evaluateResults(resultsA, testCase);
            double scoreB = evaluateResults(resultsB, testCase);
            result.recordComparison(testCase, scoreA, scoreB);
        }
        return result.calculateWinner();
    }
}

七、总结与展望

本文详细介绍了基于 Milvus 和 Java SpringBoot 构建云厂商文档智能问答系统的完整方案。通过混合检索架构,系统能够同时处理语义查询和精确查询;通过结构化分块策略,保持了云文档的技术细节完整性;通过动态权重调整,优化了不同类型查询的检索效果。

未来,我们计划在以下方向进一步优化:

  1. 多模态检索扩展:支持云架构图、流程图等图像内容的检索
  2. 个性化推荐:基于用户角色和历史查询,提供个性化文档推荐
  3. 实时知识更新:建立文档变更监控,自动同步最新内容到知识库
  4. 跨厂商统一检索:构建统一的查询接口,跨云厂商比较产品特性

云文档智能问答系统的建设是一个持续迭代的过程,随着大模型技术和向量数据库技术的快速发展,我们相信这类系统将变得更加智能、高效,成为云原生时代不可或缺的基础设施。


实现提示:在实际部署时,建议从少量核心文档开始,逐步扩展知识库范围。密切监控系统指标,根据实际查询模式调整分块策略和检索权重,确保系统在实际使用中达到最佳效果。

目录

  1. 基于 Milvus 与混合检索的云厂商文档智能问答系统:Java SpringBoot 实现
  2. 一、核心挑战与架构选型
  3. 二、数据预处理:多格式文档的智能分块策略
  4. 2.1 统一文档解析接口
  5. 2.2 文档类型识别与分块策略路由
  6. 2.3 结构化元数据提取
  7. 三、Milvus 向量存储与混合检索实现
  8. 3.1 集合 Schema 设计与优化
  9. 3.2 混合检索的核心实现
  10. 3.3 查询权重动态调整算法
  11. 四、SpringBoot 微服务集成
  12. 4.1 异步文档处理管道
  13. 4.2 REST API 设计
  14. 五、性能优化与生产部署
  15. 5.1 向量检索性能调优
  16. application.yml Milvus 配置部分
  17. 连接池配置
  18. 索引优化配置
  19. 查询参数优化
  20. 5.2 缓存策略设计
  21. 六、系统监控与评估
  22. 6.1 关键监控指标
  23. 6.2 检索质量评估
  24. 七、总结与展望

更多推荐文章

查看全部
  • JavaScript 不使用 flat 方法实现数组扁平化
  • 通义万相 2.1 文生视频模型部署与硬件性能实测
  • 深度学习模型优化策略与实战调参
  • 具身智能 AI 小车图形化编程 Mixly MQTT MCP 控制
  • Rust 从入门到实战:读取命令行参数
  • Project AirSim 无人机避障算法解析
  • 三步优化 AI 生成前端 UI:告别“土味”设计
  • 命令行大模型交互工具 MCPHost 实践
  • FPGA 实现 CAN 总线接口与数据帧解析
  • DeepSeek-R1-Distill-Llama-8B 驱动的 Python 爬虫实战与智能清洗
  • Spatial Joy 2025 全球 AR&AI 赛事参赛指南与赛道解析
  • FLUX.1-dev FP8 模型部署指南:6GB 显存运行 AI 绘画
  • Redis Sentinel 高可用集群的 Java 客户端实践
  • 宇树 G1 机器人开发:有线与无线连接配置指南
  • Python 魔术方法基础详解
  • NSP 新范式:AI 世界模型构建与物理规律建模指南
  • Linux 零基础入门:操作系统核心概念与实战指南
  • Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦
  • 远程调试 Python 服务时 screen 命令的应用实例
  • 浏览器快捷键绑定 KeyboardEvent 避坑指南及实战代码

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online