☰
Java开发者如何用LangChain4j快速集成大模型能力
2026/10/22 16:17:18 网站建设 项目流程

1. 为什么 Java 开发者值得花时间上手 LangChain4j

1.1 从一个真实痛点说起

我在几个不同的团队里都遇到过类似的场景:后端服务是清一色的 Java 技术栈,Spring Boot 跑得稳稳当当,结果产品那边突然提了个需求,要接入大模型做智能问答、文档摘要或者工单自动分类。团队里没人写过 Python,也不打算为了一个功能点专门维护一套 Python 服务,于是问题就变成了——Java 生态里到底有没有一套趁手的工具,能把大模型能力像调用普通 SDK 一样接进来?

LangChain4j 就是在这个背景下进入我视野的。它不是 Python 版 LangChain 的简单翻译,而是一套从零开始为 Java 设计的 LLM 应用开发框架。核心定位很清晰:把「和大模型打交道」这件事抽象成 Java 开发者熟悉的接口、注解和构建器模式,让你用写 Service 层代码的思路去写 AI 应用。它解决的核心问题是集成成本——不用手搓 HTTP 请求、不用自己管理对话历史、不用为每个模型厂商写一套适配代码。

这篇文章适合谁看?如果你是有 Spring Boot 基础、想快速把大模型能力落到业务里的 Java 后端,那这篇就是给你写的。如果你完全没接触过 LLM 应用开发也没关系,我会把每个概念用生活化的方式讲清楚。整篇内容基于我自己的实操经验,包含完整的依赖配置、代码示例、参数计算和踩坑记录,你可以直接抄作业。

1.2 LangChain4j 到底解决了哪些具体问题

在它出现之前,Java 接大模型通常有三种做法。第一种是直接用 HttpClient 调 REST API,灵活但重复劳动多,光是处理流式响应和重试就够写一堆工具类。第二种是用各家厂商自己的 Java SDK,问题是换一个模型供应商就得重写一遍调用逻辑。第三种是团队自己封装一层,但封装质量参差不齐,对话记忆、工具调用这些高级能力基本靠手写。

LangChain4j 的价值在于它把这几层都标准化了。它提供了统一的ChatLanguageModel接口,底层可以是不同厂商的模型,上层业务代码几乎不用改。它内置了ChatMemory做对话历史管理,内置了EmbeddingStore做向量存储抽象,还提供了AiServices这种声明式的方式,让你定义一个接口、加几个注解,框架自动帮你生成实现。这种设计思路和 Spring Data JPA 的 Repository 很像——你声明意图,框架负责实现。

从影响范围看,它覆盖的场景相当广:智能客服、RAG 知识库问答、文档处理、代码辅助、结构化信息抽取、多轮对话机器人,基本都能找到对应的模块。而且它对 Spring Boot 有一等公民级别的支持,有专门的 starter,配置写在 application.yml 里就行,这对 Java 团队来说几乎没有学习门槛。

2. 环境准备与依赖选型的关键决策

2.1 JDK 版本和构建工具怎么选

LangChain4j 对 JDK 的最低要求是 17,这一点必须先确认。我实测过在 JDK 11 上跑,编译阶段就会因为用到 record、sealed 等特性而报错。如果你所在的项目还停留在 JDK 8 或 11,要么升级,要么就别考虑这个框架了,硬凑只会浪费时间。JDK 17 是目前的稳妥选择,JDK 21 的虚拟线程在处理大量并发 LLM 调用时会有额外收益,但不是必须。

构建工具用 Maven 或 Gradle 都行,官方文档两边都有示例。我个人偏向 Maven,因为 LangChain4j 的模块划分很细,Maven 的依赖树看起来更直观,排查版本冲突时方便。下面是一个典型的 Maven 依赖配置,我把它拆成了几个部分说明。

<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <langchain4j.version>0.35.0</langchain4j.version> </properties> <dependencies> <!-- 核心模块,包含 ChatLanguageModel 等基础接口 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- 具体模型厂商的适配模块,按需引入 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>${langchain4j.version}</version> </dependency> <!-- Spring Boot 集成 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>${langchain4j.version}</version> </dependency> </dependencies>

这里有个关键点:核心模块和适配模块是分开的。langchain4j只包含接口和抽象,具体的模型实现要靠langchain4j-open-ai这类适配包。这种设计的好处是,你换模型供应商时只需要换适配包,业务代码不动。坏处是新手容易漏引依赖,然后对着ClassNotFoundException发懵。

2.2 模型接入方式的选择逻辑

接入模型有两条路:一是调用云端 API,二是本地部署模型。这两条路的取舍直接决定了你的架构。

云端 API 的优点是开箱即用,模型能力强,不用关心显卡和显存。缺点是数据要出你的服务器,有隐私合规要求的场景要慎重,另外按 token 计费,量大时成本要算清楚。本地部署的优点是数据不出内网,调用不计费,缺点是硬件门槛高,7B 参数的模型至少需要 8GB 显存才能跑得比较流畅,而且推理速度受硬件限制。

我的建议是分阶段来。开发调试阶段用云端 API,快速验证业务逻辑;上线前根据数据敏感度和调用量决定最终方案。LangChain4j 的好处是这两条路它都支持,本地部署可以通过兼容接口的方式接入,切换时上层代码基本不用改。

注意:无论选哪种方式,API Key 这类敏感信息绝对不要硬编码在代码里,用环境变量或配置中心管理。我见过有人把 Key 提交到代码仓库,结果被扫出来盗用,账单直接爆掉。

2.3 项目结构规划建议

在动手写代码之前,先把包结构规划好,后面扩展会省很多事。我习惯按职责分层:

  • config包放模型、记忆、向量库的配置类
  • service包放 AiServices 接口和业务服务
  • controller包放对外接口
  • model包放请求响应的 DTO
  • tools包放需要暴露给模型调用的工具类

这个结构不是强制的,但遵循它能让你的代码在功能变多之后依然清晰。特别是当你要接入 RAG、工具调用、多模型路由时,分层清晰的项目会好维护得多。

3. 核心概念拆解与第一个可运行示例

3.1 ChatLanguageModel:一切调用的起点

ChatLanguageModel是整个框架最基础的接口,你可以把它理解成一个「会聊天的对象」。它最常用的方法就两个:generate(String)接收一个字符串返回字符串,generate(List<ChatMessage>)接收消息列表返回Response<AiMessage>。前者适合简单场景,后者适合需要区分角色(系统、用户、AI)的复杂场景。

为什么要有消息列表这个概念?因为大模型的对话是有角色区分的。系统消息(SystemMessage)用来设定模型的角色和行为边界,用户消息(UserMessage)是用户的输入,AI 消息(AiMessage)是模型的回复。把这三者分开管理,模型才能理解上下文。这就像开会时你要先说明「今天讨论什么议题」,再让各方发言,而不是把所有话混在一起说。

下面是一个最小可运行的示例,我加了详细注释:

import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.chat.ChatLanguageModel; public class FirstDemo { public static void main(String[] args) { // 从环境变量读取 Key,不要硬编码 String apiKey = System.getenv("OPENAI_API_KEY"); ChatLanguageModel model = OpenAiChatModel.builder() .apiKey(apiKey) .modelName("gpt-4o-mini") // 模型名称按实际可用填写 .temperature(0.7) // 随机性,0 最确定,1 最发散 .timeout(Duration.ofSeconds(60)) .build(); String answer = model.generate("用一句话解释什么是向量数据库"); System.out.println(answer); } }

这段代码跑通,说明你的环境和依赖都没问题。如果报错,八成是 Key 没配好或者网络不通,先排查这两个。

3.2 参数背后的含义:temperature 和 maxTokens

很多人调模型时参数是抄来的,不知道为什么要这么设。我解释两个最关键的。

temperature控制输出的随机性。它的原理是在模型输出下一个词时,对概率分布做缩放。温度趋近 0,模型几乎总是选概率最高的词,输出稳定但可能死板;温度升高,低概率词也有机会被选中,输出多样但可能跑偏。做信息抽取、分类这种要求确定性的任务,设 0 到 0.3;做创意写作、头脑风暴,设 0.7 到 1.0。我一般默认 0.7,具体任务再调。

maxTokens限制单次回复的最大长度。这里有个容易踩的坑:它限制的是输出的 token 数,不是输入。而且不同模型的 token 和字符换算比例不一样,中文大致是 1 个汉字对应 1 到 2 个 token。如果你设了 100,模型回复到一半被截断,别以为是模型坏了,是参数设小了。反过来,设太大也没用,因为模型本身有上下文窗口上限,输入加输出不能超过这个上限。

3.3 用 AiServices 声明式地写业务逻辑

直接调ChatLanguageModel适合简单场景,但真实业务里你要处理对话历史、要注入系统提示词、要解析结构化输出,手写会很啰嗦。AiServices就是来解决这个问题的。

它的用法是定义一个 Java 接口,用注解描述行为,框架自动生成实现。这有点像 MyBatis 的 Mapper——你只写接口,SQL 映射交给框架。下面是一个例子:

import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.AiServices; interface Assistant { @SystemMessage("你是一个专业的 Java 技术顾问,回答要简洁准确") String chat(@UserMessage String userMessage); } // 构建 Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .build(); String reply = assistant.chat("解释一下什么是依赖注入");

@SystemMessage定义的角色设定会作为系统消息自动注入,@UserMessage标注的参数会作为用户消息。框架帮你把消息组装、调用、解析结果这一整套流程都封装了。你写的是接口,用的是对象,这就是声明式编程的威力。

4. 对话记忆与多轮交互的实现细节

4.1 为什么需要 ChatMemory

大模型本身是无状态的,它不记得你上一句说了什么。每次调用都是独立的,你发过去的消息列表就是它全部的「记忆」。所以多轮对话的本质是:每次调用时,把之前的对话历史一起发过去。

手动维护这个历史列表很麻烦,要控制长度、要处理截断、要区分角色。ChatMemory就是干这个的。它提供了add添加消息、messages获取历史、clear清空这几个操作。最常用的是MessageWindowChatMemory,它按消息条数保留最近的 N 条,超出就丢弃最旧的。

为什么按条数而不是按 token 数?因为按 token 数需要精确计算,实现复杂;按条数简单直接,对大多数场景够用。但要注意,如果单条消息特别长,按条数保留可能还是会超出模型上下文窗口,这时候就需要更精细的策略。

4.2 给 Assistant 加上记忆能力

把记忆接进 AiServices 很简单,构建时加一个chatMemory就行:

ChatMemory memory = MessageWindowChatMemory.withMaxMessages(20); Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemory(memory) .build(); assistant.chat("我叫小明"); assistant.chat("我叫什么名字?"); // 能正确回答"小明"

这里withMaxMessages(20)表示保留最近 20 条消息。为什么是 20?这是个经验值。太少,模型记不住上下文;太多,每次请求的 token 消耗大,成本高,而且可能超出上下文窗口。20 条大约对应 10 轮对话,对大多数客服、助手场景够用。如果你的场景需要更长的记忆,可以考虑用向量存储做长期记忆,把历史消息向量化后按相关性检索,而不是全量塞进上下文。

4.3 记忆隔离:多用户场景的必答题

单用户场景下,一个 Assistant 实例配一个 Memory 没问题。但如果是多用户,比如一个 Web 服务同时服务很多用户,就绝对不能共用一个 Memory,否则 A 用户的对话会被 B 用户看到,这是严重的数据泄露。

正确的做法是按用户 ID 隔离 Memory。常见方案是用ChatMemoryStore配合一个 Map 或者数据库,每个用户 ID 对应一份记忆。LangChain4j 提供了ChatMemoryProvider,可以根据内存 ID 动态提供记忆:

ChatMemoryProvider provider = memoryId -> MessageWindowChatMemory.builder() .id(memoryId) .maxMessages(20) .build(); Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .chatMemoryProvider(provider) .build(); // 调用时传入 memoryId 区分用户 assistant.chat("user-001", "你好"); assistant.chat("user-002", "你好");

memoryId就是用户标识,框架会根据它去找对应的记忆。生产环境里,ChatMemoryStore建议用 Redis 或数据库实现,这样服务重启后记忆不丢,多实例部署时也能共享。

提示:记忆持久化时要注意数据清理策略。用户注销、会话过期后,对应的记忆要及时删除,否则存储会无限膨胀,还可能违反数据留存的相关要求。

5. RAG 知识库问答的完整落地流程

5.1 RAG 要解决的核心问题

大模型有两个硬伤:一是知识有截止日期,训练之后的新信息它不知道;二是它不知道你公司内部的私有文档。RAG(检索增强生成)就是来解决这个的。思路很直白:把私有文档切块、向量化、存起来,用户提问时先检索出最相关的几块,连同问题一起发给模型,让模型基于这些材料回答。

这个流程听起来简单,但每一步都有讲究。切块大小、向量模型选择、检索条数、相似度阈值,任何一个参数没调好,效果都会大打折扣。我见过不少人搭完 RAG 发现答非所问,八成是检索环节出了问题,而不是模型不行。

5.2 文档加载与切分策略

LangChain4j 提供了DocumentLoader来加载各种格式的文档,PDF、Word、文本都支持。加载进来的是Document对象,包含文本内容和元数据。接下来是切分,这一步最关键。

为什么要切分?因为模型上下文窗口有限,不可能把整本书塞进去。而且检索时,粒度太粗会引入无关信息,粒度太细又会丢失上下文。常见的切分策略是按固定字符数切,配合重叠区。比如每块 500 字符,相邻块重叠 50 字符。重叠是为了避免一个完整的句子被从中间切断,导致语义不完整。

DocumentSplitter splitter = DocumentSplitters.recursive(500, 50); List<Document> documents = FileSystemDocumentLoader.loadDocuments("/path/to/docs"); List<TextSegment> segments = splitter.splitAll(documents);

recursive的意思是它会优先按段落切,段落太长再按句子切,句子还长才按字符切。这种递归策略比单纯按字符切效果好得多,因为它尽量保持了语义单元的完整。500 和 50 这两个数字不是金科玉律,中文文档我一般用 300 到 500 字符一块,英文可以大一些。具体要拿你的文档实测,看检索效果调整。

5.3 向量化与存储的选型

切分完的文本块要转成向量才能做相似度检索。向量化用的是 Embedding 模型,它把文本映射成一个高维浮点数数组,语义相近的文本在向量空间里距离也近。选 Embedding 模型主要看两点:一是支持的语言,中文场景要选中文效果好的;二是维度,维度越高表达能力越强,但存储和计算成本也越高。

存储向量的地方叫向量库。LangChain4j 支持很多种,从内存版到专业的向量数据库都有。开发阶段用InMemoryEmbeddingStore就够了,重启数据丢失但调试方便。生产环境建议用专业向量库,它们支持持久化、支持大规模检索、有索引优化。

EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder() .apiKey(apiKey) .modelName("text-embedding-3-small") .build(); EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(splitter) .embeddingModel(embeddingModel) .embeddingStore(store) .build(); ingestor.ingest(documents);

EmbeddingStoreIngestor把切分、向量化、存储三步串起来了,一行ingest搞定。这个设计很贴心,省得你手动循环处理每个文本块。

5.4 检索与生成的组装

检索环节的核心是EmbeddingStoreContentRetriever,它负责把用户问题向量化,然后在向量库里找最相似的几块。关键参数是maxResults(返回几块)和minScore(相似度阈值)。

maxResults设多少?太少可能漏掉关键信息,太多会引入噪声还增加 token 消耗。我一般从 3 到 5 开始试。minScore是过滤低质量结果的,设太高会检索不到,设太低会引入无关内容。这个值跟 Embedding 模型有关,需要实测确定,通常 0.6 到 0.8 之间。

ContentRetriever retriever = EmbeddingStoreContentRetriever.builder() .embeddingStore(store) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.7) .build(); Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .contentRetriever(retriever) .build();

把 retriever 接进 AiServices 后,每次提问框架会自动检索相关文档,拼进提示词,再发给模型。你调用assistant.chat("问题")时,背后已经完成了「检索 + 增强 + 生成」的全流程。

6. 工具调用与结构化输出的实战技巧

6.1 让模型调用你的 Java 方法

工具调用(Function Calling)是让模型从「只会说」变成「能做事」的关键。原理是:你把一些 Java 方法暴露给模型,模型在需要时返回一个「我要调用某个方法,参数是这些」的指令,框架执行方法后把结果回传给模型,模型再基于结果生成最终回复。

LangChain4j 里用@Tool注解标记方法,用AiServices的tools方法注册:

public class WeatherTool { @Tool("查询指定城市的天气") public String getWeather(@P("城市名称") String city) { // 实际调用天气 API return city + "今天晴,25度"; } } Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .tools(new WeatherTool()) .build(); assistant.chat("北京天气怎么样?");

@Tool的描述很重要,模型靠它判断什么时候该调用这个方法。描述要写清楚功能,参数用@P标注含义。描述写得含糊,模型可能该调的时候不调,或者不该调的时候乱调。

注意:工具方法的返回值会作为消息回传给模型,所以返回内容要简洁,别返回一大堆无关数据,否则会挤占上下文。另外工具方法里如果有副作用(比如写数据库),要做好幂等和权限校验,因为模型可能重复调用。

6.2 结构化输出的两种实现方式

很多时候你要的不是一段自然语言,而是结构化的数据,比如从简历里抽取姓名、电话、技能。LangChain4j 支持两种方式。

第一种是让模型返回 JSON,然后自己解析。这种方式灵活但容易出错,因为模型可能返回格式不对的 JSON。第二种是用AiServices的返回类型自动映射,你定义一个 POJO,框架负责让模型按这个结构输出并反序列化:

record PersonInfo(String name, String phone, List<String> skills) {} interface InfoExtractor { @UserMessage("从以下文本抽取信息:{{it}}") PersonInfo extract(String text); }

框架会自动在提示词里加入格式说明,并把模型返回的 JSON 映射成PersonInfo对象。这比手动解析省心得多,而且类型安全。实测下来,对于字段不多的结构,准确率相当高;字段特别多或者嵌套很深时,可能需要拆成多次调用。

6.3 提示词工程的几个实用原则

提示词写得好不好,直接决定输出质量。我总结了几个实操中验证有效的原则。

第一,角色设定要具体。「你是一个助手」不如「你是一个有十年经验的 Java 架构师,擅长性能优化」。角色越具体,模型的输出越贴合预期。

第二,给例子比讲道理管用。与其描述「输出要简洁」,不如直接给一个输入输出的示例。模型模仿示例的能力很强,这叫少样本提示。

第三,明确输出格式。要 JSON 就写清楚字段名和类型,要列表就说明用短横线还是数字。格式要求越明确,解析越省事。

第四,把约束条件写全。比如「如果信息不存在,返回 null,不要编造」,这种边界情况的说明能显著减少幻觉。

7. 常见问题排查与避坑经验实录

7.1 高频问题速查表

问题现象可能原因排查方向
启动报 ClassNotFoundException适配包没引检查是否引入了对应厂商的依赖
调用超时网络问题或 timeout 设太短加大 timeout,检查网络连通性
回复被截断maxTokens 设太小调大 maxTokens
多轮对话记不住没配 ChatMemory 或 memoryId 不对检查记忆配置和用户标识
RAG 答非所问检索质量差调整切分大小、maxResults、minScore
工具不被调用@Tool 描述不清优化描述,明确使用场景
结构化输出解析失败模型返回格式不符优化提示词,或改用自动映射

7.2 几个我踩过的坑

第一个坑是上下文窗口溢出。有次做长文档问答,把整篇文档塞进提示词,结果报错说超出 token 限制。后来改成 RAG 检索,只把相关片段塞进去,问题解决。教训是:永远不要假设模型能处理任意长度的输入,要主动控制输入规模。

第二个坑是并发下的记忆串号。早期没做记忆隔离,多个用户同时用,对话历史混在一起,闹了笑话。后来用ChatMemoryProvider按用户 ID 隔离,问题解决。这个坑很隐蔽,单用户测试时发现不了,上线后才暴露。

第三个坑是Embedding 模型和检索不匹配。文档用 A 模型向量化,检索时用 B 模型,结果相似度计算完全不对。记住:入库和检索必须用同一个 Embedding 模型,换了模型就要重新向量化全部文档。

第四个坑是成本失控。开发阶段没注意,每次请求都带一大堆历史消息和检索结果,token 消耗飞快。后来加了记忆窗口限制、检索条数限制,成本降了一大截。建议上线前算一笔账:单次请求平均多少 token,每天多少请求,乘以单价,心里有数。

7.3 性能优化的几个方向

如果发现响应慢,可以从几个方向优化。一是减少输入 token,精简提示词、控制记忆长度、减少检索条数。二是用流式响应,让用户先看到部分结果,感知上快很多。三是并发调用,如果一次请求要调多次模型,用异步并行。四是缓存,相同或相似的问题可以缓存结果,避免重复调用。

流式响应在 LangChain4j 里通过StreamingChatLanguageModel实现,配合StreamingResponseHandler处理分块返回。这个对用户体验提升很明显,尤其是生成长文本时,用户不用干等。

8. 从 Demo 到生产的工程化建议

8.1 配置外部化与多环境管理

Demo 里把 API Key、模型名写死在代码里没问题,生产环境绝对不行。正确做法是全部外部化到配置文件,用 Spring Boot 的@ConfigurationProperties绑定。不同环境(开发、测试、生产)用不同的配置文件,敏感信息用环境变量或配置中心注入。

langchain4j: open-ai: chat-model: api-key: ${OPENAI_API_KEY} model-name: gpt-4o-mini temperature: 0.7 timeout: 60s embedding-model: api-key: ${OPENAI_API_KEY} model-name: text-embedding-3-small

这样配置的好处是,换模型、调参数不用改代码,改配置重启即可。而且 Spring Boot 的 starter 会自动把这些配置装配成 Bean,你直接注入用就行。

8.2 可观测性:日志、指标与追踪

生产环境必须能观测。至少要记录每次调用的输入、输出、耗时、token 消耗。LangChain4j 提供了ChatModelListener接口,可以在调用前后插入逻辑,用来打日志、上报指标。

为什么要记录 token 消耗?因为这是成本的主要来源,也是发现异常调用的手段。如果某个接口突然 token 消耗暴涨,可能是提示词被注入了奇怪的内容,或者检索返回了超长文本。这些都要能及时发现。

8.3 降级与容错设计

大模型服务不是 100% 可用的,网络会抖、服务会限流、模型会超时。生产系统必须考虑这些。常见的做法是:设置合理的超时和重试,重试要带退避策略避免雪崩;准备降级方案,模型不可用时返回兜底话术或走规则引擎;对关键路径做熔断,避免一个慢调用拖垮整个服务。

我一般会给模型调用包一层,统一处理超时、重试、降级、日志。这层封装让业务代码保持干净,容错逻辑集中管理,改起来也方便。

8.4 安全与合规的底线

最后说几个安全要点。一是输入校验,用户输入可能包含提示词注入攻击,试图让模型忽略原有指令。对关键场景要做输入过滤和输出审核。二是权限控制,工具调用的方法要校验调用方权限,不能让模型随意触发敏感操作。三是数据脱敏,发给模型的数据要评估敏感度,该脱敏的脱敏。四是输出审核,模型生成的内容在展示给用户前,最好过一遍敏感词和合规检查。

这些不是可选项,是上线前的必答题。我见过因为没做输出审核,模型生成了不当内容导致事故的案例。技术能力越强,越要守住底线。

这套东西我从零搭到跑通,前后大概花了一周,其中大部分时间是在调 RAG 的检索效果和排查各种配置问题。LangChain4j 本身的 API 设计是清晰的,坑主要在于对 LLM 应用开发范式不熟悉。一旦理解了「消息列表」「记忆」「检索增强」这几个核心概念,剩下的就是熟练度问题。建议你从最小的 Demo 开始,跑通一个再加一个能力,别一上来就搭全套,那样出了问题很难定位。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询