从原理到落地:RAG 技术深度拆解与 Java 实战全攻略

一、RAG:破解大模型「知识困境」的核心方案

大语言模型(LLM)凭借强大的语义理解和生成能力,成为当下技术领域的核心抓手,但实际落地中两个痛点始终无法回避:一是「知识时效性」,模型训练数据存在截止时间,无法获取最新信息;二是「幻觉问题」,模型可能生成不符合事实的内容,尤其在企业私有数据场景下,这两个问题直接制约了LLM的生产应用。

检索增强生成(Retrieval-Augmented Generation,RAG)正是为解决这些问题而生的技术架构------它将「检索」与「生成」结合,先从私有/外部数据源中检索与用户问题相关的信息,再将这些信息作为上下文传入大模型,让模型基于真实、最新的信息生成回答。相比模型微调,RAG无需重新训练模型,成本更低、更新更快,是企业落地LLM的首选方案。

二、RAG核心原理与流程拆解

2.1 核心流程

RAG的完整执行链路可拆解为6个核心步骤,各环节环环相扣,决定了最终回答的准确性和效率:

(1)文档加载

核心目标是将不同格式(PDF、TXT、Word、数据库等)的数据源,转化为程序可处理的文本格式。这一步的关键是兼容多源数据,同时保证文本提取的完整性(比如PDF的分页、格式还原)。

(2)文档分割

大模型的上下文窗口存在长度限制,且长文本直接传入会导致关键信息被稀释。文档分割需按「语义粒度」拆分(而非简单按字符数),比如按段落、章节拆分,既保证单段文本的语义完整性,又适配模型的上下文长度。

(3)文本向量化

将自然语言文本转化为机器可计算的向量(Embedding),向量的维度和表征能力直接影响检索精度。向量化的核心是通过预训练的嵌入模型,将文本的语义信息映射到高维向量空间,语义相似的文本,向量距离也更近。

(4)向量存储

将向量化后的文本片段存储到向量数据库(或简易的内存结构),方便后续快速检索。向量数据库需支持「相似性检索」(如余弦相似度、欧氏距离计算),常见的有Milvus、Pinecone、FAISS等,轻量场景也可使用Redis的向量扩展。

(5)检索相关文本

接收用户问题后,先将问题向量化,再从向量存储中检索出与问题语义最相似的文本片段(Top-N)。检索策略可优化,比如结合关键词检索+向量检索,提升召回率。

(6)构造提示词&生成回答

将检索到的文本片段作为「上下文」,与用户问题拼接成提示词,传入大模型。模型基于上下文生成回答,从根源上减少幻觉,保证回答的准确性。

2.2 RAG与微调的核心差异

维度 RAG 微调
知识更新 实时更新(修改数据源即可) 需重新训练/增量微调,周期长
成本 低(无需算力训练) 高(需GPU算力、数据标注)
适用场景 私有数据频繁更新、中小规模数据 通用知识固化、大规模专有数据
技术门槛 低(侧重工程实现) 高(需算法、训练经验)

三、实战准备:技术栈与环境搭建

3.1 技术栈选型

本次实战基于企业级Java技术栈,所有组件均选用稳定且主流的版本,兼顾兼容性和实用性:

  • 基础框架:Spring Boot 3.2.5(JDK 17)
  • 文档处理:Spring AI 1.0.0-M1(PDF读取)、Apache PDFBox 3.0.1
  • 大模型调用:RestTemplate(HTTP请求)、火山引擎LLM API
  • 工具类:Lombok 1.18.30、Spring Utils、FastJSON2 2.0.48、Guava 32.1.3-jre
  • 接口文档:SpringDoc OpenAPI 2.2.0(Swagger3)
  • 构建工具:Maven 3.9.6

3.2 Maven依赖配置

创建Maven项目,在pom.xml中引入以下依赖,确保版本兼容且无冲突:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>
    <groupId>com.jam.demo</groupId>
    <artifactId>rag-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rag-demo</name>
    <description>RAG实战演示项目</description>
    
    <properties>
        <java.version>17</java.version>
        <fastjson2.version>2.0.48</fastjson2.version>
        <guava.version>32.1.3-jre</guava.version>
        <lombok.version>1.18.30</lombok.version>
        <springdoc.version>2.2.0</springdoc.version>
        <spring-ai.version>1.0.0-M1</spring-ai.version>
        <pdfbox.version>3.0.1</pdfbox.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot核心依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Swagger3(SpringDoc) -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>
        
        <!-- Spring AI(文档处理) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-core</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pdf-document-reader</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        
        <!-- PDF处理增强 -->
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>${pdfbox.version}</version>
        </dependency>
        
        <!-- FastJSON2 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>${fastjson2.version}</version>
        </dependency>
        
        <!-- Guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>
        
        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
    <!-- Spring AI仓库(M1版本需手动引入) -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

3.3 配置文件(application.yml)

src/main/resources下创建application.yml,配置火山引擎LLM API信息、文件路径等核心参数:

yaml 复制代码
spring:
  # 火山引擎LLM配置
  volc:
    api-key: your-volc-api-key  # 替换为实际API Key
    base-url: https://ark.cn-beijing.volces.com/api/v3/chat/completions  # 火山引擎API地址
    endpoint-id: your-endpoint-id  # 替换为实际模型端点ID
  # 文档路径配置
  doc:
    pdf-path: C:\Users\jamhi\Downloads\低代码开发师【初级】实战教程.pdf  # 替换为实际PDF路径

# 日志配置
logging:
  level:
    com.jam.demo: INFO
    org.springframework.web: WARN
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 服务器配置
server:
  port: 8080
  servlet:
    context-path: /rag-demo

四、核心模块实现:从文档加载到回答生成

4.1 文档加载工具类(DocLoadUtil.java)

封装PDF、TXT等文档的加载逻辑,保证文本提取的完整性,同时处理文件不存在、读取失败等异常:

ini 复制代码
package com.jam.demo.util;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

/**
 * 文档加载工具类
 * 作者:ken
 * 功能:支持PDF、TXT格式的文档加载,提取文本内容
 */
@Slf4j
public class DocLoadUtil {

    /**
     * 加载PDF文档并提取文本
     * @param pdfPath PDF文件绝对路径
     * @return 文档文本内容
     * @throws IOException 文件读取异常
     */
    public static String loadPdf(String pdfPath) throws IOException {
        // 参数校验
        StringUtils.hasText(pdfPath, "PDF文件路径不能为空");
        File pdfFile = new File(pdfPath);
        if (!pdfFile.exists()) {
            throw new IOException("PDF文件不存在:" + pdfPath);
        }
        
        // 方式1:基于Spring AI的PDF读取(简化版)
        PagePdfDocumentReader reader = new PagePdfDocumentReader(new FileSystemResource(pdfFile));
        List<Document> documents = reader.read();
        if (ObjectUtils.isEmpty(documents)) {
            log.warn("PDF文件内容为空:{}", pdfPath);
            return "";
        }
        
        // 拼接所有页面文本
        StringBuilder pdfContent = new StringBuilder();
        for (Document doc : documents) {
            String text = doc.getText();
            if (StringUtils.hasText(text)) {
                pdfContent.append(text);
            }
        }
        return pdfContent.toString();
    }

    /**
     * 加载TXT文档并提取文本
     * @param txtPath TXT文件绝对路径
     * @return 文档文本内容
     * @throws IOException 文件读取异常
     */
    public static String loadTxt(String txtPath) throws IOException {
        // 参数校验
        StringUtils.hasText(txtPath, "TXT文件路径不能为空");
        File txtFile = new File(txtPath);
        if (!txtFile.exists()) {
            throw new IOException("TXT文件不存在:" + txtPath);
        }
        
        // 按字符读取TXT内容
        char[] buffer = new char[1024];
        StringBuilder txtContent = new StringBuilder();
        try (FileReader reader = new FileReader(txtFile)) {
            int len;
            while ((len = reader.read(buffer)) != -1) {
                txtContent.append(buffer, 0, len);
            }
        }
        return txtContent.toString();
    }

    /**
     * 按语义分割文本(简易版)
     * @param content 原始文本
     * @param chunkSize 每个片段的最大字符数
     * @return 分割后的文本片段列表
     */
    public static List<String> splitText(String content, int chunkSize) {
        if (!StringUtils.hasText(content)) {
            return Lists.newArrayList();
        }
        if (chunkSize <= 0) {
            chunkSize = 1000; // 默认每段1000字符
        }
        
        List<String> chunks = Lists.newArrayList();
        int start = 0;
        int end = chunkSize;
        int contentLength = content.length();
        
        while (start < contentLength) {
            // 避免最后一段超出文本长度
            if (end > contentLength) {
                end = contentLength;
            }
            // 按段落分割(优先按换行符截断,保证语义完整)
            int splitIndex = content.lastIndexOf("\n", end);
            if (splitIndex == -1 || splitIndex < start) {
                splitIndex = end;
            }
            chunks.add(content.substring(start, splitIndex));
            start = splitIndex + 1;
            end = start + chunkSize;
        }
        return chunks;
    }
}

4.2 大模型调用工具类(LlmClientUtil.java)

封装火山引擎LLM API的调用逻辑,统一处理请求头、请求体构造,以及响应的安全解析:

typescript 复制代码
package com.jam.demo.util;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;

/**
 * 大模型调用工具类
 * 作者:ken
 * 功能:封装火山引擎LLM API的调用逻辑,处理请求与响应
 */
@Slf4j
public class LlmClientUtil {

    private static final RestTemplate REST_TEMPLATE = new RestTemplate();

    /**
     * 调用火山引擎LLM API生成回答
     * @param apiKey 火山引擎API Key
     * @param baseUrl API请求地址
     * @param endpointId 模型端点ID
     * @param prompt 拼接后的提示词(上下文+用户问题)
     * @return 大模型生成的回答
     * @throws Exception 调用异常
     */
    public static String callVolcLlm(String apiKey, String baseUrl, String endpointId, String prompt) throws Exception {
        // 参数校验
        StringUtils.hasText(apiKey, "API Key不能为空");
        StringUtils.hasText(baseUrl, "API请求地址不能为空");
        StringUtils.hasText(endpointId, "模型端点ID不能为空");
        StringUtils.hasText(prompt, "提示词不能为空");
        
        // 1. 构造请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + apiKey);
        
        // 2. 构造请求体
        Map<String, Object> params = Maps.newHashMap();
        params.put("model", endpointId);
        params.put("messages", List.of(Maps.newHashMap(
                "role", "user",
                "content", prompt
        )));
        
        // 3. 发送POST请求
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(params, headers);
        ResponseEntity<Map> response = REST_TEMPLATE.postForEntity(baseUrl, request, Map.class);
        
        // 4. 安全解析响应(逐层判空,避免空指针)
        Map<String, Object> responseBody = response.getBody();
        if (ObjectUtils.isEmpty(responseBody)) {
            log.warn("LLM API返回空响应");
            return "未获取到有效回答";
        }
        
        List<Map<String, Object>> choices = (List<Map<String, Object>>) responseBody.get("choices");
        if (CollectionUtils.isEmpty(choices)) {
            log.warn("LLM API响应中无choices字段");
            return "未获取到有效回答";
        }
        
        Map<String, Object> firstChoice = choices.get(0);
        if (ObjectUtils.isEmpty(firstChoice)) {
            log.warn("LLM API响应中第一个choice为空");
            return "未获取到有效回答";
        }
        
        Map<String, Object> message = (Map<String, Object>) firstChoice.get("message");
        if (ObjectUtils.isEmpty(message)) {
            log.warn("LLM API响应中无message字段");
            return "未获取到有效回答";
        }
        
        String content = (String) message.get("content");
        if (!StringUtils.hasText(content)) {
            log.warn("LLM API响应中content字段为空");
            return "未获取到有效回答";
        }
        
        log.info("LLM API调用成功,生成回答长度:{}", content.length());
        return content;
    }
}

4.3 RAG核心控制器(RagChatController.java)

整合文档加载、提示词构造、大模型调用逻辑,提供REST接口,添加Swagger3注解,支持参数校验和异常处理:

kotlin 复制代码
package com.jam.demo.controller;

import com.jam.demo.util.DocLoadUtil;
import com.jam.demo.util.LlmClientUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * RAG聊天控制器
 * 作者:ken
 * 功能:提供RAG增强的聊天接口,基于PDF文档回答用户问题
 */
@Slf4j
@RestController
@RequestMapping("/ragChat")
@Tag(name = "RAG聊天接口", description = "基于PDF文档的检索增强生成接口")
public class RagChatController {

    @Value("${spring.volc.api-key}")
    private String volcApiKey;

    @Value("${spring.volc.base-url}")
    private String volcBaseUrl;

    @Value("${spring.volc.endpoint-id}")
    private String volcEndpointId;

    @Value("${spring.doc.pdf-path}")
    private String pdfPath;

    /**
     * 基于PDF文档的RAG聊天接口
     * @param question 用户问题
     * @return 基于文档内容的回答
     */
    @GetMapping("/v2")
    @Operation(summary = "PDF文档RAG聊天", description = "传入用户问题,基于指定PDF文档内容生成回答")
    public String ragChat(
            @Parameter(description = "用户问题", required = true)
            @RequestParam String question) {
        // 参数校验
        if (!StringUtils.hasText(question)) {
            log.error("用户问题为空");
            return "用户问题不能为空";
        }
        
        try {
            // 1. 加载PDF文档内容
            String pdfContent = DocLoadUtil.loadPdf(pdfPath);
            if (!StringUtils.hasText(pdfContent)) {
                log.warn("PDF文档内容为空,路径:{}", pdfPath);
                return "文档内容为空,无法回答问题";
            }
            
            // 2. 分割文档(避免提示词过长)
            // 实际场景可根据模型上下文长度调整,这里暂不分割,直接拼接
            String prompt = "请基于以下文档内容回答用户问题,仅使用文档中的信息,不要编造内容:\n"
                    + "文档内容:\n" + pdfContent + "\n"
                    + "用户问题:" + question;
            
            // 3. 调用大模型生成回答
            return LlmClientUtil.callVolcLlm(volcApiKey, volcBaseUrl, volcEndpointId, prompt);
            
        } catch (Exception e) {
            log.error("RAG聊天接口调用失败", e);
            return "回答生成失败:" + e.getMessage();
        }
    }
}

4.4 启动类(RagDemoApplication.java)

配置Spring Boot启动类,排除不必要的自动配置(如数据库相关),减少资源占用:

arduino 复制代码
package com.jam.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;

/**
 * RAG实战项目启动类
 * 作者:ken
 */
@SpringBootApplication(exclude = {
        // 排除数据库自动配置(无需数据库)
        DataSourceAutoConfiguration.class,
        JdbcTemplateAutoConfiguration.class
})
public class RagDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RagDemoApplication.class, args);
        log.info("RAG实战项目启动成功,访问地址:http://localhost:8080/rag-demo/swagger-ui/index.html");
    }
}

五、接口测试与问题排查

5.1 接口访问与测试

项目启动后,可通过以下方式测试接口:

  1. Swagger3文档:访问http://localhost:8080/rag-demo/swagger-ui/index.html,找到/ragChat/v2接口,传入用户问题(如"低代码开发师初级的核心知识点有哪些?"),点击执行即可获取回答。
  2. Postman/浏览器:访问http://localhost:8080/rag-demo/ragChat/v2?question=低代码开发师初级的核心知识点有哪些?,直接查看返回结果。 以下是返回截图:

5.2 常见问题与解决方案

(1)PDF读取乱码

  • 原因:PDF文件编码不兼容,或包含图片型文本(无法提取)。

  • 解决方案:

    • 对于编码问题:使用PDFBox的PDFTextStripper指定编码(如UTF-8);
    • 对于图片型PDF:集成OCR工具(如Tesseract),先识别图片文本再提取。

(2)大模型调用返回空内容

  • 原因:API Key/endpoint-id错误、请求体格式不合法、模型上下文长度超限。

  • 解决方案:

    • 校验API参数是否正确,可通过火山引擎控制台验证;
    • 拆分长文本,减少单次提示词长度;
    • 检查LlmClientUtil中的响应解析逻辑,确保字段名与API文档一致。

(3)接口调用超时

  • 原因:网络延迟、火山引擎API响应慢、文档加载耗时过长。

  • 解决方案:

    • RestTemplate设置超时时间:

      scss 复制代码
      RestTemplate restTemplate = new RestTemplate();
      restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom()
              .setConnectTimeout(5000)
              .setConnectionRequestTimeout(5000)
              .setSocketTimeout(10000)
              .build()));
    • 异步加载文档,缓存已加载的文档内容(如Redis),避免重复读取。

六、RAG底层逻辑深挖与优化

6.1 向量检索的核心:余弦相似度

文本向量化后,检索的核心是计算「问题向量」与「文档片段向量」的余弦相似度,公式如下:

  • 相似度取值范围为[-1,1],越接近1,语义越相似;
  • 实战中无需手动计算,向量数据库(如Milvus)已内置该算法,只需调用检索接口即可。

6.2 提示词工程优化

RAG的效果与提示词直接相关,以下是实战验证的优化技巧:

  1. 明确指令:在提示词中加入"仅使用文档中的信息回答,不要编造内容",减少幻觉;
  2. 上下文排序:将与问题最相似的文档片段放在提示词前部,提升模型关注度;
  3. 精简上下文:只保留与问题相关的片段,避免无关信息稀释核心内容。

6.3 检索策略优化

单一的向量检索可能存在召回率不足的问题,可采用「混合检索」:

  1. 关键词检索:先通过ES/MySQL全文索引筛选出包含问题关键词的文档片段;
  2. 向量检索:对筛选后的片段做向量相似度排序,取Top-N;
  3. 结果融合:将两种检索结果融合,提升召回率和精度。

七、RAG扩展与进阶方向

7.1 多数据源接入

除了本地PDF,可扩展支持以下数据源:

  • 数据库:通过MyBatis Plus读取MySQL/PostgreSQL中的结构化数据;
  • 云存储:接入阿里云OSS、腾讯云COS,加载云端文档;
  • 网页:通过Jsoup爬取指定网页内容,补充数据源。

7.2 缓存层设计

文档加载和向量检索是耗时操作,可引入Redis缓存:

  1. 缓存已加载的文档文本(按文件路径为Key);
  2. 缓存已向量化的文档片段(按片段ID为Key);
  3. 缓存高频问题的检索结果(按问题哈希为Key)。

7.3 分布式部署

当文档量和请求量增大时,可采用分布式架构:

  • 文档加载服务:独立部署,负责多源文档的加载和分割;
  • 向量检索服务:独立部署,对接向量数据库,提供检索接口;
  • 大模型调用服务:独立部署,封装不同厂商的LLM API,实现多模型兼容。

八、总结

RAG并非复杂的算法创新,而是工程化的架构思路------它将「检索」的精准性与「生成」的灵活性结合,解决了大模型落地的核心痛点。从实战角度看,RAG的效果取决于三个核心:文档处理的完整性、检索的精准性、提示词的合理性。在企业落地中,无需追求"大而全"的架构,可从简单场景(如单PDF文档问答)切入,逐步扩展多数据源、缓存、分布式等能力。同时,需结合业务场景持续调优:比如ToB场景需注重回答的准确性和合规性,ToC场景需兼顾回答的流畅性和时效性。 RAG的价值不仅在于解决当下的问题,更在于为LLM落地提供了"低成本、快速迭代"的路径------企业无需投入巨额算力做微调,只需维护好数据源,就能让大模型持续输出有价值的内容。这也是RAG成为当前LLM落地主流方案的核心原因。

📦 本文配套源码已备好,评论区留个言,我发你邮箱!

相关推荐
suke2 小时前
马斯克 600 亿美元收购 Cursor!史上最贵「分手费」100 亿,xAI 代码能力要起飞了
人工智能·ai编程
Mn0102032 小时前
关于使用Gemini来辅助编写CI/CD脚本的技术文章大纲
ai编程
踩着两条虫2 小时前
VTJ:架构设计模式
前端·架构·ai编程
码路飞2 小时前
这周 AI 圈连炸两次:我把 Kimi K2.6 和 Claude Opus 4.7 都接进 OpenClaw 试了一遍
ai编程·claude
nebula-AI2 小时前
llm wiki的固定提示词
人工智能·ai·个人开发·ai编程
好多渔鱼好多2 小时前
【AI编程工具】华为CodeArts Snap 极速上手指南:从安装到高效编码
ai编程·华为snap
AI技术社区3 小时前
Claude Code源码分析之提示词工程
java·开发语言·ai·ai编程
好多渔鱼好多3 小时前
【AI编程工具】华为CodeArts Snap 实战进阶:从后端接口生成到鸿蒙 ArkTS 深度适配
ai编程·华为snap
六月的可乐3 小时前
快速搭建 AI 客服系统:用 AI-Agent-Node + AISuspendedBallChat 打造可落地的智能客服方案
人工智能·gpt·ai·ai编程