尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java后端用LangChain4j与LangGraph4j搭建RAG知识库实战
先说结论Java后端团队想把大模型能力接进自己的业务系统想做企业知识库问答没必要全部押注在Python生态上。LangChain4j到目前为止已经能覆盖文档解析、切块、向量化存储、检索增强生成这条完整链路再配合LangGraph4j做流程编排基本上能在纯Java技术栈内把RAG知识库系统从0到1跑通。这篇文章是我把完整的实战过程梳理了一遍从技术选型、工程目录搭建、核心代码实现到多轮对话、混合检索、Agentic RAG的编排最后是生产环境落地时那堆只有踩过坑才知道的细节。无论你是刚接触RAG的Java开发还是已经在做AI应用但想换个技术栈这篇都有可以直接抄作业的部分。1. 项目定位Java生态里的RAG知识库该怎么做1.1 为什么Java开发者要自己搭RAG很多团队聊到RAG知识库第一反应是“用Python写”因为大模型相关的开源组件几乎都是Python优先。但真实业务场景里绝大多数企业的核心系统是Java写的用户体系、权限模块、数据权限、审批流、文档管理这些东西全部沉淀在Java服务里。如果为了做一个知识库问答功能就引入一套Python微服务那就意味着要同时维护两套技术栈、两套部署链路、两套监控体系对一个小团队来说是实实在在的负担。用Java直接构建RAG核心价值在于技术栈收敛。可以复用现有的Spring Boot工程、已有的用户权限上下文甚至直接读取业务数据库里的数据通过Embedding模型变成向量再走RAG链路做问答。知识库不再是孤立的AI Demo而是能跟业务系统嵌套在一起的能力。还有一个现实问题很多公司的算法团队和工程团队是分开的。算法同学可以产出模型但落地上生产、接业务系统最后还是Java后端来干。这时候如果手头有一份Java版的RAG实现对接成本会低非常多。1.2 技术选型LangChain4j和LangGraph4j到底是什么LangChain4j是LangChain的Java移植版但更准确地说它是专为Java生态重新设计的LLM应用框架。它把大模型接入、消息历史管理、文档解析、文本切块、向量化、向量存储、检索增强、AI服务接口定义这些东西全部抽象成了易用的API。你不用自己手写拼接Prompt的代码也不用自己维护对话历史框架层面已经做了统一处理。LangGraph4j则是对标Python LangGraph的Java实现用来编排复杂的Agent工作流。它的核心思想是把AI应用拆成“节点”和“边”每个节点做一件具体的事比如改写问题、检索文档、生成回答节点之间根据条件跳转。相比写一堆if-else去控制流程LangGraph4j能把流程可视化、可维护对复杂RAG场景特别有用。有人会问既然LangChain4j已经能做RAG为什么还要上LangGraph4j答案是LangChain4j解决的是“一条直线链路”的问题LangGraph4j解决的是“多分支、可循环、可条件跳转”的问题。比如一个问答系统先判断用户问题是否需要检索知识库不需要就直接回复需要才走RAG链路再比如检索结果不理想时自动改写问题再检索一次。这种流程用线性RAG做起来很别扭用图编排就顺理成章。1.3 系统整体架构与核心组件整个RAG知识库系统从架构上可以分成两大阶段。索引阶段做的事情是把PDF、Word、Markdown、数据库记录等原始文档读取进来做切块然后把每个块通过Embedding模型转成向量最后写入向量数据库。这块在LangChain4j中对应的是Document、DocumentSplitter、EmbeddingModel、EmbeddingStore这些组件。推理阶段做的事情是把用户的问题经过“改写/扩展”和已有的对话历史合并去向量数据库做相似度检索拿到TopK相关的文本块把文本块作为上下文和用户问题一起交给LLM生成最终答案。这块涉及ChatMemory、ContentRetriever、RetrievalAugmentor、ChatLanguageModel这些组件。当我们引入LangGraph4j后推理阶段就从“单行道”变成了“流程图”可以有一个节点负责判断“是否需要检索”一个节点负责“改写问题”一个节点负责“检索”一个节点负责“生成”。节点之间通过状态对象传递数据整体控制力更强。2. 从零搭建第一版文档解析、切块与向量化存储2.1 工程初始化与Maven依赖建议直接在一个Spring Boot 3.x工程上做增量开发这样后续接Web接口、接权限、接配置中心都方便。核心依赖只有两个一个是LangChain4j主包另一个是按需引入的模型厂商包。我用的是OpenAI的Embedding和Chat模型所以额外引入了open-ai的桥接包。如果你要接国内大模型厂商也可以找到对应的包或者用OpenAI兼容协议走自定义配置这块LangChain4j做得比较灵活。dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version0.36.2/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version0.36.2/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-easy-rag/artifactId version0.36.2/version /dependency先解释一下为什么需要langchain4j-easy-rag。这个模块里提前封装了一套“最小可用的RAG链路”包括默认的文本切分器、默认的向量存储实现、默认的检索增强器。第一版跑通的时候先用它把整条链路打通后面再逐步替换成自己的组件是很省事的路径。LangGraph4j的依赖单独引入dependency groupIdorg.bsc.langgraph4j/groupId artifactIdlanggraph4j-core/artifactId version1.0.0/version /dependency dependency groupIdorg.bsc.langgraph4j/groupId artifactIdlanggraph4j-langchain4j/artifactId version1.0.0/version /dependency目前的Maven中央仓库版本号变化比较快建议实际引入时以你看到的latest为为准。注意别混用大版本LangGraph4j对LangChain4j的版本有兼容要求如果引了最新的langgraph4j-core但langchain4j还是老版本运行期大概率会碰到方法找不到这类问题。2.2 文档加载从PDF、Word到纯文本LangChain4j对文档加载的处理思路很统一不管原始文件是什么格式最终都通过一个DocumentParser转成统一的Document对象Document内部就是文本内容和元数据比如文件名、页码、来源链接。PDF解析使用的是Apache PDFBoxWord解析用的是Apache POI纯文本和Markdown则直接按字符流读取。使用方式很简单DocumentParser pdfParser new PdfDocumentParser(); DocumentParser wordParser new ApacheTikaDocumentParser(); DocumentParser txtParser new TextDocumentParser(); Document pdfDoc pdfParser.parse(new FileInputStream(/path/file.pdf)); Document txtDoc txtParser.parse(new FileInputStream(/path/file.txt));我实际项目里遇到过一个问题很多PDF是扫描件本质是图片再好的解析器也抽不出文字。这种情况要么先接OCR服务把图片转成文字要么提前约定好知识库只接受电子版PDF。还有一个更隐蔽的问题PDF表格解析出来之后行列关系往往会丢失变成一段混乱的文本。这块没有特别好的一劳永逸的方案业务上需要技术侧做评估哪些文档适合进知识库哪些文档需要先做预处理。2.3 切块策略影响检索质量的第一道关口切块是整个RAG链路里最影响效果、也最容易被忽略的环节。切太小每个块包含的语义信息不完整检索到的片段可能缺上下文切太大向量表示的语义太泛而且超出模型上下文窗口时还要二次截断。实际工程里没有标准答案只能根据文档类型、检索效果反复调。LangChain4j提供了多种内置切分器最常用的是递归字符文本切分器DocumentSplitter splitter DocumentSplitters.recursive(500, 100);第一个参数是每个块的目标字符数第二个参数是相邻块之间的重叠字符数。重叠的目的是为了保证两个块中间的语义断层不要太大。比如一个500字的块最后50字正在描述一个关键概念下一个块没有承接检索的时候就会漏掉上下文。加了重叠之后同一个概念大概率会同时出现在两个块里检索命中率会高不少。从我自己的调参经验来看面向内部规章制度、操作手册这类文档块大小500到800字、重叠80到150字是一个比较稳的起步区间。技术文档代码片段多的话块可以再小一点政策法规这类长段落多的文档块可以适当放大到1000字左右。但最终效果一定要靠验证集来评估拿几十个真实问题去跑检索看TopK返回的片段是不是“正好能回答这个问题”。还有一点要特别注意切块最好在Document级别做并且在切分后的TextSegment里保留原始文档ID和自然段序号。这样后期做去重、做引用溯源、做权限过滤都方便。LangChain4j的TextSegment自带metadata提前把source、docId、page等字段塞进去后面能省很多事。2.4 嵌入模型与向量存储选型嵌入模型的作用是把文本变成一个固定维度的向量让语义相近的文本在向量空间里距离更近。选型上有两条路线一是调API比如OpenAI的text-embedding-3-small、text-embedding-3-large二是在内网部署开源模型比如Ollama跑bge-m3、Qwen3-Embedding这类。没有安全要求、数据可以出域的话直接调API最省事。LangChain4j的接入方式非常直接EmbeddingModel embeddingModel OpenAiEmbeddingModel.builder() .apiKey(System.getenv(OPENAI_API_KEY)) .modelName(text-embedding-3-small) .build();但是企业知识库场景文档大概率涉及内部经营数据数据出境本身就是风险。更稳妥的方式是内网部署Ollama或同类框架然后用LangChain4j的OllamaEmbeddingModel接入。性能稍弱一点但数据和合规压力小很多。很多团队问“RAG必须用API吗”——真不一定核心是Embedding模型和Chat模型都选本地可部署的整套系统就能离网运行。向量存储方面LangChain4j提供了内存版、Chroma、Qdrant、Milvus、Pgvector等一堆实现。原型阶段用InMemoryEmbeddingStore就够了但生产环境至少要上Pgvector这类能持久化、能和业务数据库放一起的存储。引入方式EmbeddingStoreTextSegment embeddingStore new InMemoryEmbeddingStore(new GsonJsonCodec());如果后续要替换成Pgvector只需要换一个创建EmbeddingStore的方式上层代码几乎不用动。这一点也是LangChain4j做得比较舒服的地方存储实现被抽象得很干净。索引流程组合起来就是EmbeddingStoreIngestor ingestor EmbeddingStoreIngestor.builder() .documentSplitter(splitter) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); ingestor.ingest(pdfDoc);ingest方法内部会自动完成“解析结果切块 - 逐块向量化 - 写入向量库”三步非常省心。第一次跑通时把几个测试文档ingest进去然后直接调用检索接口验证效果整个链路就算立住了。3. 检索增强生成把知识库接进LLM对话3.1 最小可用的RAG链路实现如果只是要一个最简单的问答接口不引入LangGraph4j用LangChain4j的AiServices就够了。AiServices的核心思想是先定义一个Java接口把“用户问一句话、系统返回一个答案”声明成抽象方法然后框架自动生成实现类检索增强、上下文拼接、模型调用这些逻辑全部封装在内部。interface Assistant { String answer(String query); } Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(chatModel) .contentRetriever(retriever) .build(); String answer assistant.answer(公司的年假政策是什么);这里的retriever是一个ContentRetriever可以从向量库里查询相关文本块也可以把外部API查询结果作为内容源。最常见的实现是EmbeddingStoreContentRetrieverContentRetriever retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(5) .minScore(0.6) .build();maxResults控制返回几个相关文本块minScore是相似度阈值。这两个参数直接影响生成质量返回太多会把不相关内容塞进上下文浪费token还容易干扰模型返回太少可能漏掉关键内容。我建议起步给5个块、相似度阈值0.5到0.7再根据实际问答效果调。生成阶段框架会自动把检索到的TextSegment列表拼接到Prompt中默认的Prompt结构大致是“已知信息节 用户问题 指令”。如果对默认Prompt不满意可以自定义PromptTemplate把“仅依据给定内容回答不要编造”这类约束加进去。3.2 多轮对话场景下的上下文设计真正接业务系统后“单轮问答”远远不够用户会连续追问比如先问“员工的年假政策是什么”再追问“那产假呢”。这时候如果只把当前问题拿去检索就会丢失“员工假期”这个主题。LangChain4j的多轮方案是通过ChatMemory实现的。最常用的是MessageWindowChatMemory它维护一个滑动窗口只保留最近N条消息避免上下文无限膨胀。接入方式也非常简单ChatMemory chatMemory MessageWindowChatMemory.withMaxMessages(20); Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(chatModel) .contentRetriever(retriever) .chatMemory(chatMemory) .build();但这里有一个关键性能点框架默认会直接把历史对话和检索到的知识块全部拼在一起如果历史消息很长再加上5个文本块Prompt很容易超长。而且还有一个更隐蔽的问题用户在第二句话里并没有提到“年假”这个词直接拿“那产假呢”去向量库检索效果肯定不好。更合理的做法是引入“查询改写”环节先用LLM把“历史对话 当前问题”合并成一个完整的、可独立检索的查询语句再用改写后的语句去检索。LangChain4j的QueryTransformer就是干这个的QueryTransformer queryTransformer CompressingQueryTransformer.builder() .chatLanguageModel(chatModel) .build();这样“那产假呢”经过改写后会变成“公司关于产假的政策是什么”检索效果会好很多。多轮对话并不是“把历史全塞进去就行”核心是要让检索语句和上下文解耦。3.3 混合检索与重排序什么时候需要RRF纯向量检索适合语义匹配但对精确关键词不敏感。比如用户搜“BG-CS-2024-001号文件”向量检索可能会返回一大堆“文件管理制度”精确匹配反而排不到前面。这种场景就需要混合检索同时跑向量检索和BM25关键词检索再把两路结果合并排序。LangChain4j里的做法是同时配置多个ContentRetriever再用一个聚合器合并结果。RRFReciprocal Rank Fusion是合并多路检索结果的经典算法核心思想是对每个结果在多路排序中的名次取倒数然后加总得分最终按总得分排序。它不需要归一化分数实现简单效果却不错。但这里我要专门提醒一个坑LangChain4j默认的RRF实现在去重逻辑上是存在缺陷的。具体表现是同一个TextSegment被多个Retriever返回时默认逻辑并没有做严格的内容去重导致最终Prompt里出现好几段一模一样的文本。不仅浪费token还会让模型过度关注重复内容回答质量明显下降。我的处理方式是在ContentAggregator层自定义一个按TextSegment的文本内容做去重的聚合器。核心逻辑是维护一个Mapkey取segment.text()的哈希值value存合并后的条目只有新的评分高于已有评分才替换。这样既保留了RRF的融合效果又消除了重复内容。同时为了不丢失原始文档维度我会在TextSegment的metadata里记录docId聚合完成后如果同一docId的分片超过两个也只保留排名最高的两个避免一个文档把上下文窗口占满。4. 用LangGraph4j编排智能检索流程4.1 从线性RAG到Agentic RAG前面讲的链路本质上是“问题进来固定走一遍检索然后生成”。这在很多场景下够用但一旦碰上复杂问题线性流程的短板就很明显。比如有用户问“今天天气怎么样”系统也会去知识库检索搜出来一堆无关文档然后LLM基于无关内容生成一个莫名其妙的答案。再比如用户问“请对比一下我们公司和竞品在数据安全方面的规定”一次检索拿到的内容可能不够需要拆成两次查询。Agentic RAG的思路就是让“是否检索、检索什么、要不要再检一次”这些决策由流程来定而不是写死在链路里。LangGraph4j就是用来实现这种流程编排的。它不是死板的流水线而是一张有向图节点是具体的处理动作边是节点之间的流转关系节点可以选择只走一条边或者走多条边。4.2 LangGraph4j核心概念与最小示例LangGraph4j的核心概念有三个State、Node、Edge。State是贯穿整个图的数据对象可以在节点之间传递。通常需要保存用户输入、改写后的查询、检索到的文档列表、最终答案。Node是处理单元写法就是一个接收State、返回部分State更新的函数。Edge定义节点之间的连接关系还支持条件边即根据State里的某个字段决定走哪条分支。下面是一个最小的状态定义和节点示例Data Builder class RagState { private String query; private String rewrittenQuery; private ListTextSegment segments; private String answer; } StateGraphRagState graph new StateGraph(RagState.class); graph.addNode(rewrite, state - { String rewritten rewriteQuery(state.query); return Map.of(rewrittenQuery, rewritten); }); graph.addNode(retrieve, state - { ListTextSegment segments retrieve(state.rewrittenQuery); return Map.of(segments, segments); }); graph.addNode(generate, state - { String answer generate(state.query, state.segments); return Map.of(answer, answer); }); graph.addEdge(rewrite, retrieve); graph.addEdge(retrieve, generate); graph.setEntryPoint(rewrite); graph.setFinishPoint(generate); CompiledGraphRagState compiled graph.compile(); RagState result compiled.invoke(RagState.builder().query(年假政策).build());这里每个节点都返回一个Map其中key是状态字段名value是要更新的值。LangGraph4j会把返回的Map合并到当前State里传给下一个节点。你可以把State理解成一个可变的上下文袋子节点从袋子里取数据再往袋子里放数据。4.3 实战带检索开关和问题改写的工作流有了基础概念就可以做一个稍微复杂一些的Agentic RAG。我的设计里加了三个关键逻辑先判断用户问题是否需要检索如果需要先改写问题再做检索如果检索结果太少或分数太低自动换一种写法再试一次。第一步是“是否检索”的判断。这个节点可以做成两种实现一种是用LLM判断给模型一个带指令的Prompt让它输出“需要/不需要”另一种是规则判断比如用户问“你好”“谢谢”这类寒暄语以及“1加1等于几”这类百科全书问题直接在代码里拦截。规则判断省一次LLM调用响应更快实际项目里我建议两者结合先规则拦截再让LLM兜底判断。第二步是“问题改写”。注意这个改写跟3.2节多轮场景下的改写不完全一样。这里的改写更多是面向检索质量的扩展把“年假政策”改写成“年假天数、年假申请条件、年假逾期处理”用多个维度的表述去提升检索召回率。改写后的文本不一定可读但检索效果会更好。第三步是“检索并评估”。检索完成后对结果做质量判断。比如取最高相似度分数如果低于阈值0.4就认为本次检索失败触发重写再试一次如果重写后仍然很低就让生成节点直接告诉用户“知识库里没有找到相关内容建议补充资料”而不是硬编一个答案。LangGraph4j的条件边在这里发挥了很大作用graph.addConditionalEdge(evaluate, state - { if (state.getSegments() null || state.getSegments().get(0).score() 0.4) { return rewrite; // 回到改写节点再试一次 } return generate; });这套流程跑下来最直观的感受是“系统会判断该不该查库、查得不好会重查”比原来死板的固定链路聪明很多而且每个判断逻辑都能单独测试和调整。4.4 RAG和MCP的区别与配合很多刚接触AI应用的Java工程师会把RAG和MCP混淆。简单说RAG是一种“把外部知识在生成时注入上下文”的技术方案适合处理静态或半静态的知识文档MCP是模型与外部工具、数据源之间的标准通信协议解决的是“LLM如何主动调用API、如何读取数据库、如何操作业务系统”的问题。两者的应用场景有清晰边界知识库问答优先用RAG查询实时订单状态、写工单、调用内部系统接口更适合用MCP。但它们不是二选一的关系在一个完整系统里完全可以配合使用。可以设计这样的流程用户问题进来后先由一个路由器判断是知识检索型问题还是工具调用型问题知识型走RAG链路工具型走MCP协议调内部API复杂的任务甚至可以在一个图里串联两条链路。LangGraph4j这样的编排框架正好承载这种混合流程。后面如果你们团队要上Agent项目这个架构会很有参考价值。5. 生产环境落地性能优化与常见问题排查5.1 性能瓶颈分析与优化方向RAG系统上线后最先暴露的问题往往不是AI效果而是响应速度和成本。响应速度上最耗时的三个环节依次是LLM生成、向量检索、Embedding计算。LLM生成耗时受模型大小和输出长度限制优化空间有限但可以靠流式输出提升用户体感向量检索在数据量达到百万量级时需要考虑索引类型和分片LangChain4j底层对接的Qdrant、Milvus都支持HNSW索引通过调整M和efConstruction参数可以平衡检索速度和精度Embedding计算在索引大量文档时非常吃资源建议用批处理异步任务不要在Web请求线程里同步执行。成本上最容易失控的是token消耗。我见过一个团队上线第一周token账单翻了好几倍查了半天发现是历史对话没有限制窗口再加上每次检索的5个文本块全部塞进Prompt一个问题就要消耗几千token。解决思路是限制ChatMemory窗口大小对检索回来的文本块做裁剪只保留与问题最相关的内容对用户问题和文本块做个粗略相关度过滤太低的直接丢弃。工程落地还有一点不能忽略就是缓存。高频问同一个问题的场景非常多完全可以在服务端做一层语义缓存把问题和答案的哈希、或者问题和答案的Embedding近似匹配存起来命中缓存直接返回。这个优化能把整个RAG链路的P99响应时间从几秒降到几十毫秒。5.2 常见问题排查实录我在实际项目中整理了一份排错清单按出现频率排序基本能覆盖大多数问题。现象一检索结果全是无关内容。先不要怀疑模型90%的情况是切块策略有问题。检查一下是不是切出来的块里掺杂了大量页眉页脚、目录、表格噪声再看Embedding模型的输入长度是不是被截断了。我遇到过一种情况技术手册里大量代码片段切块后代码和文字混在一起向量表示被代码特征主导导致语义检索跑偏。解决办法是切块前先对文本做一次分类识别出纯代码块用单独的切块参数处理。现象二相似度阈值设了没用。LangChain4j的EmbeddingStoreContentRetriever里minScore的取值和Embedding模型有关不同模型产出的向量余弦相似度分布差异很大。有些模型在正常语义相关的情况下相似度只有0.5有些模型能达到0.8。不要迷信某个固定阈值建议先导出一批真实问答对的相似度分布再看阈值设在哪里合适。现象三同一个问题多轮追问后答案前后矛盾。这个问题通常出在ChatMemory和历史消息处理上。比如用户第一轮问“公司年假几天”第二轮问“那是自然日还是工作日”系统在生成第二轮答案时历史消息已经被第一轮的检索结果覆盖模型没看到第一轮的准确答案很容易自己编。解决思路是把第一轮的关键事实摘要单独维护一份作为生成阶段的稳定上下文。现象四LangGraph4j节点之间数据丢失。这个最坑往往表现为节点A给State放了一个字段节点B读出来是null。原因基本都是状态字段没做初始化或者类型不匹配。LangGraph4j对State的更新是合并式的如果新返回的Map里某个字段是null它可能会覆盖掉已有值。我踩过这个坑之后每个节点返回前都会做一次null值防护。5.3 进一步优化方向生产系统跑稳定之后可以再往两个方向升级一是精细化权限控制企业知识库不同于公开语料不同角色能看到的内容范围是不同的。一种做法是给每个TextSegment打上权限标签检索阶段根据当前用户角色过滤另一种做法是检索结果出来后再做权限过滤但这样会浪费检索性能建议两种结合。二是引入反馈闭环用户对每个回答做“有帮助/无帮助”评价把负反馈样本收集起来定期分析是检索问题还是生成问题形成知识库质量优化的数据基础。如果想再进阶还可以往Ontology RAG方向探索用领域本体来组织实体关系让检索结果更具逻辑性。不过这是后话先把LangChain4j和LangGraph4j这条路走通已经能解决大多数业务问题了。从我个人的实操体会来说用Java技术栈做RAG知识库最舒服的一点是从文档解析到向量检索再到LLM调用整条链路都在一个熟悉的工程体系里不需要跨语言维护两套服务。LangChain4j把门槛降到了“定义一个接口就能用”的程度LangGraph4j则给复杂流程留足了扩展空间。如果你正打算在团队里落地知识库问答建议先按文章里的路径把最小闭环跑起来再根据业务反馈逐步调整。
RELATED

相关推荐

反馈周期:AI智能进化的底层加速器

反馈周期:AI智能进化的底层加速器

1. 项目概述:反馈周期不是“快慢”问题,而是智能演化的底层开关你有没有注意过,一个刚学会走路的孩子,摔一跤后下一次迈步会明显调整重心;而一台工业机械臂,哪怕重复执行同一套动作上万次,只要没…

📅 2026/9/24 23:40:59
多路用电采集设备的SPI与UART组网设计实战指南

多路用电采集设备的SPI与UART组网设计实战指南

1. 项目概述:为什么多路用电采集设备的组网方案不能“拍脑袋”决定?做电表、智能插座、能源监控终端这类产品,我干了十二年,从第一代用单片机分立元件搭采样电路,到今天带边缘计算能力的模块化终端,踩过的坑…

📅 2026/9/24 23:40:59
Qt C++数独游戏全解析:从源码编译到部署发布

Qt C++数独游戏全解析:从源码编译到部署发布

简介:这是一款基于Qt框架实现的C数独游戏完整工程,代码已经过测试并成功运行,适合计算机相关专业学生、初学Qt的开发者及需要课程设计或毕业设计参考的读者,同时也便于在现有代码上做二次功能扩展。压缩包内共包含五十九个文件&am…

📅 2026/9/24 23:35:59
MORE NEWS

更多资讯

📰

深度学习新闻分类推荐系统:从TextCNN到个性化推荐

简介:这份基于深度学习的新闻分类推荐系统Python实现源码,是专为课程设计与期末大作业准备的高分项目,下载后无需修改即可运行,适用于需要快速交付完整课题的高校学生。系统涵盖新闻数据预处理、文本分类模型训练、推荐逻辑展示等…

📰

汽车电子底层软件开发:AUTOSAR与CAN总线实战解析

1. 这门“汽车电子底层软件开发就业课”到底在教什么?——不是写个LED闪烁就能上岗的很多人看到“汽车电子底层软件开发就业课”这个标题,第一反应是:不就是嵌入式C语言单片机CAN通信?刷几道LeetCode、调通一个STM32 CAN收发例程&…

📰

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

📰

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

📰

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

📰

从harness工程到认知工程:Agent系统升级的完整指南

1. 先搞清楚一件事:什么是harness工程1.1 从“给马套缰绳”说起harness这个词,英语本意是“马具、缰绳”,引申到软件工程里就是“给系统套上约束和工具的一整套装置”。在Agent开发圈子里,harness工程指的是围绕大模型Agent构建的…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬