编程 Koog 深度解析:JetBrains 如何用工程化思维重新定义 JVM AI Agent 框架

2026-04-13 04:23:36 +0800 CST views 9

Koog 深度解析:JetBrains 如何用工程化思维重新定义 JVM AI Agent 框架

一、引言:当 AI Agent 来到 JVM 生态

2026 年 3 月,JetBrains 正式发布了 Koog——一个面向 JVM (Java 和 Kotlin) 的 AI Agent 框架。这个消息在技术社区引发了广泛讨论,原因很简单:这是主流 IDE 厂商首次以官方身份进军 AI Agent 框架领域。

在 Koog 发布之前,JVM 生态已经有了不少 AI 框架的选择:Spring AI、LangChain4j、Helidon AI 等等。但这些框架大多停留在"将 LLM 包装成 API 调用"的层面,对于真正构建生产级 AI Agent 应用所需的可预测性、容错性和企业级特性支持并不完善。

Koog 的出现,正是为了解决这些问题。作为一个由 IDE 厂商打造的框架,Koog 天然继承了 JetBrains 对开发者体验的深刻理解,同时也融入了其在工具链构建方面的工程化思维。

二、Koog 是什么:重新定义企业级 AI Agent

2.1 框架定位

Koog 的官方定义是:"A JVM (Java and Kotlin) framework for building predictable, fault-tolerant and enterprise-ready AI agents"

这个定义中有三个关键词值得我们深入理解:

  1. Predictable(可预测性) - 这可能是 Koog 与其他框架最核心的差异点。在 AI Agent 领域,由于大模型的不确定性,很多框架的输出难以预测和控制。Koog 通过一系列设计决策,试图在保持 Agent 灵活性的同时,提供更强的行为确定性保证。

  2. Fault-tolerant(容错性) - 生产环境中的 AI Agent 需要能够优雅地处理各种异常情况:模型响应延迟、网络中断、工具执行失败等。Koog 内置了一套完整的容错机制,包括重试策略、降级处理、错误恢复等。

  3. Enterprise-ready(企业级就绪) - 这意味着 Koog 从设计之初就考虑到了企业应用的需求:安全性、可观测性、可扩展性、审计能力等。

2.2 技术架构概览

Koog 的架构设计遵循了分层解耦的原则,主要包含以下几个核心组件:

┌─────────────────────────────────────────────────────────────┐
│                      Application Layer                        │
│                   (User-defined Agents)                      │
├─────────────────────────────────────────────────────────────┤
│                      Agent Runtime                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │   Planner   │  │   Executor  │  │   Memory        │    │
│  │   (规划器)  │  │   (执行器)   │  │   (记忆系统)     │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                    Tool System                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │  Tool       │  │  Tool       │  │   Tool          │    │
│  │  Registry   │  │  Invoker    │  │   Chain         │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                    LLM Adapter Layer                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │  OpenAI     │  │  Anthropic  │  │   Custom        │    │
│  │  Adapter    │  │  Adapter     │  │   Adapters      │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                 Infrastructure Layer                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │   Logging   │  │  Metrics    │  │   Tracing       │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
└─────────────────────────────────────────────────────────────┘

三、核心设计理念:可预测性优先

3.1 为什么可预测性如此重要

在传统的 LLM 应用中,模型输出的不确定性是一个被广泛讨论的话题。对于简单的问答场景,这种不确定性可能无伤大雅;但对于需要精确控制的 AI Agent 应用,不确定性会成为最大的障碍。

举一个具体的例子:假设你构建了一个自动化代码审查 Agent,要求它只对特定的代码问题进行标记。但如果模型在某次运行中"创意爆发",开始对代码风格提出各种主观意见,这种行为就偏离了预期。

Koog 通过以下机制来解决这个问题:

3.2 结构化输出强制

Koog 强制要求 Agent 的输出遵循预定义的结构。这不是简单的 JSON Schema 校验,而是从根本上改变了 Agent 与模型交互的方式。

// 定义输出结构
@KoogOutput
data class CodeReviewResult(
    val issues: List<CodeIssue>,
    val summary: String,
    val confidence: Float
)

@KoogOutput
data class CodeIssue(
    val severity: IssueSeverity,
    val lineNumber: Int,
    val description: String,
    val suggestion: String?
)

// 构建 Agent
val reviewer = KoogAgentBuilder()
    .withModel(OpenAI.model("gpt-4"))
    .withOutputSchema(CodeReviewResult::class)
    .withSystemPrompt("""
        You are a code reviewer. Analyze the provided code and output results
        in the specified format. Only report objective issues that can be
        verified through static analysis.
    """.trimIndent())
    .build()

// 执行审查
val result = reviewer.execute(codeInput)

在这个例子中,@KoogOutput 注解告诉 Koog 这个数据类定义了 Agent 的输出结构。Koog 会自动:

  1. 将结构定义转换为详细的 prompt,引导模型按格式输出
  2. 解析模型输出为结构化数据
  3. 如果解析失败,触发预定义的降级处理

3.3 确定性执行模式

除了输出结构化,Koog 还提供了"确定性执行模式"。在这种模式下,Agent 的行为被限制在预定义的路径上,只有在特定条件下才会调用 LLM 进行决策。

// 确定性工作流
val workflow = KoogWorkflow<ReviewRequest, ReviewReport>()
    .step(Step.ParseCode)          // 静态分析,无需 LLM
        .execute { request -> 
            parseSourceCode(request.code) 
        }
    .step(Step.DetectIssues)       // 规则匹配,无需 LLM
        .execute { parsed -> 
            ruleEngine.detect(parsed) 
        }
    .step(Step.AssessSeverity)     // 需要理解上下文,调用 LLM
        .condition { issues -> issues.isNotEmpty() }
        .execute { issues -> 
            llm.assessSeverity(issues) 
        }
    .step(Step.GenerateReport)     // 模板渲染,无需 LLM
        .execute { assessment -> 
            template.render(assessment) 
        }
    .build()

// 执行工作流
val report = workflow.execute(ReviewRequest(code = sourceCode))

这种设计带来的好处是:大部分执行路径是可预测的、可测试的、可调试的。只有在真正需要 LLM 智慧的地方,才会让模型介入。

四、容错机制:企业级的韧性设计

4.1 多层重试策略

Koog 的容错机制建立在对 AI 应用异常的深刻理解之上。不同于传统应用的简单重试,Koog 针对不同类型的异常提供了差异化的处理策略。

val agent = KoogAgentBuilder()
    .withModel(OpenAI.model("gpt-4"))
    .withRetryPolicy(
        RetryPolicy<KoogExecutionContext>()
            .onException<RateLimitException>()  // 速率限制
                .wait(Duration.ofSeconds(30))
                .maxAttempts(3)
                .exponentialBackoff()
            .onException<ModelTimeoutException>() // 模型超时
                .wait(Duration.ofSeconds(10))
                .maxAttempts(2)
                .linearBackoff()
            .onException<InvalidOutputException>() // 输出解析失败
                .retryOnce()  // 只重试一次,因为问题可能在 prompt
                .withFallback { ctx ->
                    // 降级处理:返回结构化的错误结果
                    FallbackResult(
                        status = Status.PARTIAL,
                        message = "Failed to parse model output",
                        data = null
                    )
                }
            .onException<AllRetriesExhaustedException>() // 所有重试耗尽
                .execute { ctx ->
                    // 最后的兜底:记录详细日志,触发告警
                    alertService.send(ctx.summary())
                    throw ctx.lastException()
                }
    )
    .build()

4.2 熔断器模式

在大规模 AI 应用中,如果某个模型或服务持续失败,继续尝试不仅浪费资源,还可能导致级联故障。Koog 集成了熔断器模式来应对这种情况。

val modelAdapter = OpenAI.adapter()
    .withCircuitBreaker(
        CircuitBreakerConfig()
            .failureThreshold(5)           // 连续 5 次失败后打开
            .successThreshold(2)          // 连续 2 次成功后关闭
            .timeout(Duration.ofMinutes(1)) // 1 分钟后尝试半开
            .halfOpenRequests(3)          // 半开状态下最多 3 个请求
    )
    .withFallback { error ->
        // 熔断打开时的降级逻辑
        when (error) {
            is RateLimitException -> cachedResponse.orElse(defaultResponse)
            else -> defaultResponse
        }
    }

4.3 资源隔离与配额管理

在企业环境中,多个 Agent 往往需要共享有限的 LLM API 配额。Koog 提供了资源隔离和配额管理机制,确保关键业务的 Agent 不会因为其他 Agent 的过度使用而受到影响。

// 资源组配置
val resourceManager = ResourceManagerBuilder()
    .group("critical")          // 关键业务组
        .quota(Quota.of(1000, Duration.ofMinutes(1))) // 每分钟 1000 请求
        .priority(10)
    .group("standard")         // 标准业务组
        .quota(Quota.of(100, Duration.ofMinutes(1)))  // 每分钟 100 请求
        .priority(5)
    .group("background")       // 后台任务组
        .quota(Quota.of(10, Duration.ofMinutes(1)))  // 每分钟 10 请求
        .priority(1)
    .build()

// Agent 使用资源组
val agent = KoogAgentBuilder()
    .withModel(OpenAI.model("gpt-4"))
    .withResourceGroup("critical")
    .build()

五、工具系统:扩展 Agent 能力边界

5.1 统一工具接口

Koog 定义了一套统一的工具接口,使得开发者可以方便地将各种能力封装为 Agent 可用的工具。

// 定义一个搜索工具
@KoogTool(name = "search_code", description = "Search for code patterns in the repository")
class SearchCodeTool(
    private val searchService: CodeSearchService
) : KoogToolBase<SearchRequest, SearchResult>() {
    
    override fun execute(input: SearchRequest): SearchResult {
        return searchService.search(
            query = input.query,
            language = input.language,
            maxResults = input.maxResults
        )
    }
    
    override fun validateInput(input: SearchRequest): ValidationResult {
        return if (input.query.isBlank()) {
            ValidationResult.invalid("Query cannot be empty")
        } else {
            ValidationResult.valid()
        }
    }
}

// 定义文件读取工具
@KoogTool(name = "read_file", description = "Read file contents from the project")
class ReadFileTool(
    private val fileSystem: VirtualFileSystem
) : KoogToolBase<ReadFileRequest, FileContent>() {
    
    override fun execute(input: ReadFileRequest): FileContent {
        val path = Path.of(input.projectRoot, input.relativePath)
        return FileContent(
            path = input.relativePath,
            content = fileSystem.readContent(path),
            lineCount = fileSystem.lineCount(path)
        )
    }
    
    override fun getSchema(): ToolSchema {
        return ToolSchema(
            name = "read_file",
            description = description,
            parameters = mapOf(
                "projectRoot" to ParameterSchema("string", required = true),
                "relativePath" to ParameterSchema("string", required = true),
                "startLine" to ParameterSchema("integer", required = false),
                "endLine" to ParameterSchema("integer", required = false)
            )
        )
    }
}

5.2 工具链编排

在复杂任务中,Agent 通常需要按顺序使用多个工具。Koog 提供了工具链编排能力,使得这种编排变得声明式和可测试。

// 声明工具链
val toolChain = ToolChainBuilder<AnalyzeRequest, AnalysisReport>()
    .tool(ReadFileTool(fileSystem))
    .tool(SearchCodeTool(searchService))
    .tool(CodeFormatterTool(formatter))
    .tool(GitBlameTool(gitService))
    .withFlow { ctx ->
        // 读取源文件
        val fileContent = ctx.call<ReadFileTool, _>(
            ReadFileRequest(projectRoot, sourceFile)
        )
        
        // 搜索相关代码
        val relatedCode = ctx.call<SearchCodeTool, _>(
            SearchRequest(
                query = extractSearchTerms(fileContent),
                language = fileContent.language
            )
        )
        
        // 获取代码历史
        val blame = ctx.call<GitBlameTool, _>(
            GitBlameRequest(projectRoot, sourceFile)
        )
        
        // 格式化结果
        val formatted = ctx.call<CodeFormatterTool, _>(
            CodeFormatterRequest(format = "json", data = mapOf(
                "content" to fileContent,
                "related" to relatedCode,
                "blame" to blame
            ))
        )
        
        AnalysisReport(
            sourceFile = sourceFile,
            content = fileContent,
            relatedCode = relatedCode,
            history = blame,
            formatted = formatted
        )
    }
    .build()

六、记忆系统:赋予 Agent 长期记忆能力

6.1 分层记忆架构

Koog 实现了分层记忆架构,模拟人类记忆的不同层次:

val agent = KoogAgentBuilder()
    .withMemory(
        MemoryConfig()
            // 工作记忆:当前会话的短期上下文
            .workingMemory(WorkingMemoryConfig()
                .maxTokens(8000)
                .summaryStrategy(RecursiveSummaryStrategy())
            )
            // 情景记忆:跨会话的重要事件
            .episodicMemory(EpisodicMemoryConfig()
                .storage(RedisStorage("episodic"))
                .retrieval(SimilarityRetrieval(topK = 5))
                .importanceThreshold(0.7f)
            )
            // 语义记忆:结构化知识和事实
            .semanticMemory(SemanticMemoryConfig()
                .storage(VectorDBStorage(chroma, collection = "knowledge"))
                .embeddingModel(OpenAI.embedding("text-embedding-3-small"))
            )
            // 程序记忆:学习到的流程和技能
            .proceduralMemory(ProceduralMemoryConfig()
                .storage(DocumentStorage("procedures"))
                .learningFromFeedback(true)
            )
    )
    .build()

6.2 记忆检索与回溯

当 Agent 需要回忆相关信息时,Koog 会自动从多个记忆层中检索:

class ContextManager(
    private val memory: KoogMemory
) {
    suspend fun retrieveContext(query: String, sessionId: String): ContextBundle {
        // 并行检索各层记忆
        val working = memory.workingMemory.retrieve(sessionId)
        val episodic = memory.episodicMemory.search(query, topK = 3)
        val semantic = memory.semanticMemory.search(query, topK = 5)
        
        // 智能排序和过滤
        val context = ContextBundle(
            working = working,
            episodes = episodic.filter { it.relevance > 0.6f },
            knowledge = semantic.filter { it.confidence > 0.7f }
        )
        
        // 裁剪到模型上下文限制
        return context.truncate(maxTokens = 6000)
    }
}

七、代码实战:构建一个代码审查 Agent

7.1 项目设置

首先,添加 Koog 依赖:

// build.gradle.kts
dependencies {
    implementation("io.koog:agent-core:0.1.0")
    implementation("io.koog:tool-filesystem:0.1.0")
    implementation("io.koog:tool-git:0.1.0")
    implementation("io.koog:adapter-openai:0.1.0")
    
    // 可选:其他适配器
    implementation("io.koog:adapter-anthropic:0.1.0")
    implementation("io.koog:adapter-local:0.1.0")
}

7.2 定义 Agent 配置

@Configuration
class KoogAgentConfig {
    
    @Bean
    fun codeReviewAgent(
        openAIAdapter: OpenAIAdapter,
        toolRegistry: ToolRegistry
    ): KoogAgent {
        return KoogAgentBuilder<CodeReviewInput, CodeReviewReport>()
            .name("CodeReviewAgent")
            .model(openAIAdapter.model("gpt-4"))
            .tools(toolRegistry.getTools(
                "read_file",
                "search_code",
                "git_blame",
                "static_analysis"
            ))
            .systemPrompt("""
                You are an expert code reviewer with deep knowledge of:
                - Java, Kotlin, and Scala best practices
                - Design patterns and clean code principles
                - Security vulnerabilities and common attack vectors
                - Performance anti-patterns
                
                Your review must be:
                1. Objective and evidence-based
                2. Prioritized by severity (Critical, High, Medium, Low, Info)
                3. Actionable with specific suggestions
                
                Always check for:
                - Null pointer risks
                - Resource leaks (connections, streams)
                - SQL injection vulnerabilities
                - Race conditions
                - Unnecessary complexity
            """.trimIndent())
            .outputSchema(CodeReviewReport::class)
            .withRetryPolicy(defaultRetryPolicy())
            .withCircuitBreaker(CircuitBreakerConfig.default())
            .withMemory(MemoryConfig.default())
            .build()
    }
}

7.3 定义输入输出结构

@KoogOutput
data class CodeReviewReport(
    val summary: ReviewSummary,
    val issues: List<CodeIssue>,
    val positiveFindings: List<String>,
    val metrics: CodeMetrics
)

@KoogOutput
data class ReviewSummary(
    val totalLines: Int,
    val filesReviewed: Int,
    val criticalCount: Int,
    val highCount: Int,
    val mediumCount: Int,
    val lowCount: Int,
    val overallQuality: String  // "Excellent", "Good", "Needs Work", "Poor"
)

@KoogOutput
enum class IssueSeverity {
    CRITICAL, HIGH, MEDIUM, LOW, INFO
}

@KoogOutput
data class CodeIssue(
    val id: String,
    val severity: IssueSeverity,
    val category: String,
    val title: String,
    val description: String,
    val filePath: String,
    val lineNumber: Int,
    val codeSnippet: String,
    val suggestion: String,
    val references: List<String> = emptyList()
)

@KoogOutput
data class CodeMetrics(
    val cyclomaticComplexity: Double,
    val cognitiveComplexity: Double,
    val linesOfCode: Int,
    val commentRatio: Double,
    val duplicationPercentage: Double
)

7.4 使用 Agent

@Service
class CodeReviewService(
    private val codeReviewAgent: KoogAgent<CodeReviewInput, CodeReviewReport>
) {
    
    suspend fun reviewPullRequest(prId: String): CodeReviewReport {
        // 1. 获取 PR 变更内容
        val changes = gitService.getPullRequestChanges(prId)
        
        // 2. 构建审查输入
        val input = CodeReviewInput(
            repository = changes.repository,
            files = changes.modifiedFiles.map { file ->
                FileToReview(
                    path = file.path,
                    content = file.content,
                    diff = file.diff,
                    language = detectLanguage(file.path)
                )
            },
            context = ReviewContext(
                author = changes.author,
                branch = changes.sourceBranch,
                targetBranch = changes.targetBranch
            )
        )
        
        // 3. 执行审查
        val report = codeReviewAgent.execute(input)
        
        // 4. 存储审查结果到记忆系统
        memoryService.storeReview(prId, report)
        
        // 5. 根据严重程度决定是否阻塞合并
        if (report.summary.criticalCount > 0) {
            blockingMerge(prId, report.summary)
        }
        
        return report
    }
}

7.5 集成到 CI/CD

# .github/workflows/code-review.yml
name: AI Code Review

on:
  pull_request:
    branches: [main, develop]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Run AI Code Review
        run: |
          java -jar review-bot.jar \
            --pr=${{ github.event.pull_request.number }} \
            --token=${{ secrets.GITHUB_TOKEN }} \
            --openai-key=${{ secrets.OPENAI_API_KEY }}
        
      - name: Post Review Comments
        if: always()
        run: |
          # 将审查结果发布为 PR 评论
          gh pr comment ${{ github.event.pull_request.number }} \
            --body-file=review-report.md

八、性能优化与最佳实践

8.1 提示词优化

在 Koog 中,提示词的质量直接影响 Agent 的行为可预测性:

// ❌ 模糊的提示词导致不可预测的行为
val badPrompt = """
    Review this code and find issues.
"""

// ✅ 精确的提示词带来可预测的结果
val goodPrompt = """
    You are reviewing Java code for security vulnerabilities.
    
    OUTPUT FORMAT:
    Return a JSON array of issues, each with:
    - severity: one of [CRITICAL, HIGH, MEDIUM, LOW]
    - type: one of [SQL_INJECTION, XSS, NPE, RACE_CONDITION, RESOURCE_LEAK, OTHER]
    - line: exact line number
    - description: max 100 characters
    - fix: specific code change required
    
    RULES:
    1. Only report issues you can verify with >80% confidence
    2. Do NOT suggest style changes (formatting, naming)
    3. Do NOT report issues in test files
    4. Focus on security and correctness, not best practices
    
    If no issues found, return: []
""".trimIndent()

8.2 缓存策略

对于不频繁变化的上下文,可以利用缓存减少 LLM 调用:

val agent = KoogAgentBuilder()
    .withCaching(
        CacheConfig()
            // 缓存语义记忆的检索结果
            .semanticCache(
                ttl = Duration.ofHours(24),
                keyStrategy = HashKeyStrategy()
            )
            // 缓存代码分析结果
            .toolResultCache(
                ttl = Duration.ofMinutes(30),
                cacheToolResults = true
            )
            // 不缓存需要实时性的结果
            .excludeFromCache("search_code", "git_blame")
    )
    .build()

8.3 批处理优化

当需要处理大量相似任务时,批处理可以显著降低成本:

class BatchCodeReviewService(
    private val agent: KoogAgent<CodeReviewInput, CodeReviewReport>
) {
    // 串行处理
    suspend fun reviewSerially(requests: List<CodeReviewInput>): List<CodeReviewReport> {
        return requests.map { agent.execute(it) }
    }
    
    // 批量处理 - 更高效
    suspend fun reviewInBatch(requests: List<CodeReviewInput>): List<CodeReviewReport> {
        return agent.executeBatch(
            inputs = requests,
            batchSize = 10,           // 每批 10 个
            maxConcurrent = 3,        // 最多 3 个并发
            onPartialFailure = { batch, errors ->
                // 批量中的部分失败处理
                logger.warn("Batch had ${errors.size} failures")
            }
        )
    }
}

九、对比分析:Koog 与其他 JVM AI 框架

特性KoogLangChain4jSpring AIHelidon AI
专注领域Enterprise AgentLLM AppLLM AppMicroservices + AI
输出确定性强 (结构化输出)
容错机制内置完整需自行实现需自行实现部分支持
工具系统声明式、类型安全注解驱动Spring 集成声明式
记忆系统多层记忆架构基础基础
工作流编排确定性工作流DAG
资源管理配额、隔离
学习曲线中高

何时选择 Koog:

  1. 构建生产级 AI Agent - 需要高可靠性、可观测性
  2. 需要确定性行为 - 输出格式必须严格可控
  3. 企业级应用 - 需要资源隔离、配额管理、审计能力
  4. 复杂多步骤任务 - 需要工作流编排和工具链

何时考虑其他框架:

  1. 快速原型 - LangChain4j 上手更快
  2. Spring 生态 - 已有 Spring Boot 项目,用 Spring AI 更自然
  3. 轻量级 LLM 调用 - 不需要 Agent 能力

十、总结与展望

Koog 的出现,标志着 JVM 生态在 AI Agent 领域进入了一个新阶段。它不再满足于"把 LLM 包装成 API",而是真正从企业级应用的角度出发,解决生产环境中的核心挑战:可预测性、容错性、可观测性。

作为 JetBrains 的产品,Koog 继承了其一贯对开发者体验的重视。类型安全的工具定义、声明式的工作流配置、完善的 IDE 支持(这是其他框架难以匹敌的优势),都体现了 JetBrains 对开发者生产力的深刻理解。

展望未来,我们有理由期待:

  1. IDE 深度集成 - Koog Agent 可能会与 IntelliJ IDEA 深度集成,实现实时代码分析和辅助
  2. 多模型编排 - 支持在同一 Agent 中灵活切换不同模型
  3. 更丰富的工具生态 - 官方和社区提供的工具库
  4. 性能优化 - 更高效的推理和批处理能力

对于 JVM 开发者而言,现在是进入 AI Agent 领域的最好时机。Koog 提供了一套工程化的方法论,让构建可靠的 AI Agent 不再是少数人的专利。


参考资源


Tags: AI, Agent, JVM, Java, Kotlin, Koog, JetBrains, 企业级, 框架

Keywords: Koog, JetBrains, AI Agent, JVM, Java, Kotlin, LangChain4j, Spring AI, 人工智能, 智能体

复制全文 生成海报 AI Agent JVM Java Kotlin Koog JetBrains

推荐文章

CSS 特效与资源推荐
2024-11-19 00:43:31 +0800 CST
黑客帝国代码雨效果
2024-11-19 01:49:31 +0800 CST
js常用通用函数
2024-11-17 05:57:52 +0800 CST
Vue3中如何处理SEO优化?
2024-11-17 08:01:47 +0800 CST
一些好玩且实用的开源AI工具
2024-11-19 09:31:57 +0800 CST
Vue3 结合 Driver.js 实现新手指引
2024-11-18 19:30:14 +0800 CST
File 和 Blob 的区别
2024-11-18 23:11:46 +0800 CST
Nginx 防止IP伪造,绕过IP限制
2025-01-15 09:44:42 +0800 CST
JavaScript中的常用浏览器API
2024-11-18 23:23:16 +0800 CST
Redis和Memcached有什么区别?
2024-11-18 17:57:13 +0800 CST
如何配置获取微信支付参数
2024-11-19 08:10:41 +0800 CST
支付页面html收银台
2025-03-06 14:59:20 +0800 CST
Redis函数在PHP中的使用方法
2024-11-19 04:42:21 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
程序员茄子在线接单