LangChain实战:构建RAG知识库问答系统全流程

让一个通用大模型老老实实只在指定资料里找答案,这件事我从一开始做就翻过车。当时还是直接把问题丢给LLM,让它基于一份内部产品手册回答客户问题,结果它发挥得很愉快,给出的答案里一半是手册里根本没有的内容,另一半年份都对不上。后来我才意识到,根本不是模型不够聪明,而是我压根没给它“只准看哪些材料”的边界。RAG这个思路就是来解决这个问题的——把资料先喂进一个可检索的索引,回答问题时先把相关资料捞出来,再让LLM只基于这些捞出来的片段作答。这篇文章是“RAG在LangChain中的实现”系列的第一篇,我会用LangChain把一个最基础的RAG问答Demo从头到尾搭起来,包含组件选型、核心代码、参数调优和踩坑记录,目标是让你能照着复现,并且知道每个环节为什么这么做。如果你正要处理内部知识库问答、产品文档答疑、私有数据问答这类“必须限定上下文”的场景,这篇内容可以直接当脚手架用。

1. 为什么需要RAG:LLM的“自由发挥”问题

1.1 直接问LLM的三个大坑

先看一个反例。我早期的做法很天真:把PDF手册喂给模型的上下文窗口,然后直接问“这款设备支持的最大电压是多少”。结果模型回答了一个看起来非常专业的数值,但这个数值来自它训练数据里的另一款产品,而且官方手册里明确写了不同的参数。这种问题根本不是个例,而是直接裸调LLM必然遇到的三个坑。

第一是幻觉。LLM的本质是预测下一个token,它没有“查证”能力,训练时见过的知识会被自信地拼接到答案里。第二是知识截止。模型训练完成那一刻之后的资料它完全不知道,你给它一份昨天刚更新的制度文件,它只会按老版本回答。第三是审计困难。就算模型答对了,你也无法追溯它依据的是哪一段原文,说不清楚答案来源,在企业场景里就难以验收。

RAG的核心思想其实不复杂:先建一个资料库,回答问题时把问题丢进去做检索,把最相关的那几段原文捞出来,再连问题一起塞给LLM,明确告诉它“只能根据这些原文回答”。这样模型的知识范围被硬生生限定住了,幻觉被大幅压缩,而且每句话都能对应到原文出处,可审计性也解决了。

1.2 RAG的核心流程:先检索,后生成

把RAG拆开看,就是一个清晰到可以背下来的链路:加载文档、切分文档、向量化、存向量库、检索、组装提示词、LLM生成。资料进来之后不是整个扔给模型,而是切成块,每块变成向量;用户提问时,把问题也变成向量,在向量库里做相似度搜索,取出最相近的几块;最后把这几块原文放进prompt,让模型在限定材料里作答。

打个比方,这就像考试时老师先发给你一张指定参考页的索引,你按图索骥把相关篇章翻出来放在桌上,再开始答卷。你不需要把所有参考书背下来,但你的答案必须引用桌上这几页的内容。RAG做的就是把“指定参考范围”这件事程序化。

1.3 什么场景适合RAG,什么场景不适合

不是所有问答都要套RAG。我做过的项目里,适合RAG的场景很典型:企业内部知识库问答、产品使用文档问答、政策法规查询、客服FAQ、私有的非公开资料问答。它们的共同点是资料是固定的、相对静态的、必须严格以原文为准,答案不能自由发挥。

不适合RAG的场景也有。比如开放式创作,你希望模型脑洞大开,那就不要用检索来限制它;再比如多轮闲聊,需要长期记忆上下文,这时该做的是维护对话记忆,而不是每次都重新检索一堆资料。搞清楚边界很重要,不然你会在错误的地方硬套RAG。

2. LangChain中RAG的整体设计与组件选型

2.1 RAG的五个核心组件

LangChain把RAG流程封装成了一组可替换的组件,每个环节都有对应的抽象接口。我最常用的是下面这几个:

组件角色常用选择说明
文档加载器读取原始文件PyPDFLoader、TextLoader、CSVLoader把PDF、txt、csv等不同格式统一转成文档对象
切分器把长文切成块RecursiveCharacterTextSplitter控制送入检索单元的粒度
向量化模型文本转向量OpenAIEmbeddings、HuggingFace BGE决定相似度计算的语义基础
向量库存储和检索向量FAISS、Chroma管理向量索引
链/检索器串联流程RetrievalQA、LCEL把检索和生成组合起来

组件化的好处是你可以分别换掉任意一个部件。比如今天觉得这个embedding模型效果不好,换一个表达式就行,不用改其他代码;明天想从FAISS换到Pinecone,也只替换向量库实例化的那一段。

2.2 为什么选LangChain而不是从零手写

其实RAG本身并不难,手写也能写:一个加载文档函数、一个切分函数、一个调embedding API的封装、一个向量库、再把检索结果拼进prompt。那为什么还要用LangChain?我的理由很实际:它把这些环节的边界统一了,让代码在不同组件之间切换的成本极低;而且它预置了prompt模板、文档格式处理、输出解析这些细节,能少写很多兼容代码。

但我也强烈建议不要只停留在“会用LangChain”层面。第一次搭的时候,最好手写一遍核心链路,搞清楚向量库返回的是什么、prompt里检索内容是怎么插入的。否则出了问题就跟玄学一样,只能瞎猜。

2.3 Embedding模型选型:同一个文本,不同向量差别巨大

刚开始做的时候我犯过一个错误:把英文专用的embedding模型拿来处理中文文档,结果检索质量惨不忍睹。后来才真正理解,embedding决定了“语义相似”这件事到底怎么衡量。中英文混排时需要选择对中文支持稳定的模型,目前实际项目中我遇到过两类方案:一类是收费但效果稳定、无需本地部署的API型Embedding模型;另一类是本地可部署的开源模型,比如针对中文优化的BGE系列。选择时主要看三点:模型词表是否覆盖目标语言、向量维度是否满足后续存储要求、以及是否有成本或部署条件的限制。

我现在的经验是:中文场景优先用中文语料训练的模型,或多语言模型;不要拿纯英文模型硬跑。这个选型不值得省。

2.4 向量库选型:FAISS、Chroma、Milvus

向量库这块我也横向比较过几个。FAISS适合本地验证和小规模应用,内存索引加载慢但查询快,简单直接。Chroma更偏轻量级应用,自带持久化。Milvus是服务化方案,功能全、支持分布式,但部署成本高。参考表格如下:

向量库部署方式适合场景缺点
FAISS本地文件Demo、小规模私密项目持久化管理弱
Chroma本地文件/服务中小应用、快速原型高并发能力一般
Milvus独立服务大规模生产环境运维成本高

本篇的Demo用FAISS,因为它的接口在LangChain里集成度高,代码量最少,能让你把注意力放在RAG流程本身。

3. 实操:从零搭建一个“受控问答”Demo

3.1 环境准备:安装依赖

先把依赖装上。下面的库分别对应加载、切分、向量化、向量库和语言模型调用。

pip install langchain langchain-community langchain-openai langchain-text-splitters faiss-cpu python-dotenv pypdf

需要注意的是,不同版本LangChain的包结构有调整,早期很多类都在langchain主包里,现在分类到了langchain-community、langchain-openai等子包。如果你安装的是最新版本,导入路径一定要按我下面代码里的来,否则会报模块不存在。

3.2 第一步:文档加载与切分

假设你手头有一份产品手册product_manual.md,先加载并切分。Loaders支持的格式很多,这里用TextLoader读纯文本,如果是PDF就用PyPDFLoader。

from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter loader = TextLoader("product_manual.md", encoding="utf-8") documents = loader.load() splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=80, ) chunks = splitter.split_documents(documents) print(len(chunks)) # 看切出了多少块

RecursiveCharacterTextSplitter是我默认首选的切分器,因为它会按优先级递归找分隔符,先尝试段落符、再找句号、逗号,这样切出来的块尽量保持语义完整。相比之下,按固定字符数硬切最容易把一句话从中间断开,造成检索倒没问题,但生成时上下文读着特别别扭。

3.3 第二步:向量化并存入向量库

将切好的块交给embedding模型转成向量,然后用FAISS建索引。

from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS # 如果你用本地模型,可以换成 HuggingFaceBgeEmbeddings,只是初始化参数不同 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = FAISS.from_documents( documents=chunks, embedding=embeddings, ) vectorstore.save_local("faiss_index")

这里的核心动作是from_documents:它遍历每一个chunk,调用embedding模型生成向量,再建索引。保存本地之后,下次启动不需要重新向量化整个文档库。

3.4 第三步:组装检索器与受限回答链

这是整篇文章的关键。先创建检索器,再写一个“只许用给定上下文作答”的prompt,最后把二者串成链。

from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) prompt = ChatPromptTemplate.from_template( """你是文档问答助手。请严格根据下面提供的资料片段回答问题。 如果资料片段中没有相关信息,请直接回答“资料中未找到相关内容”,不要自行推测。 资料片段: {context} 问题:{question} 回答:""" ) llm = ChatOpenAI(model="your-model-name", temperature=0) rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() )

这段代码里的链式写法是用LangChain的LCEL语法,retriever会自动接收输入问题,去向量库检索并把命中的chunk列表放进context字段。RunnablePassthrough原样把问题传给question字段。对初学者来说,也可以改用RetrievalQA封装类,但LCEL更透明,出了问题你能看清数据怎么流动。

3.5 第四步:完整代码串联

把上面几段拼成一个可运行的脚本:

from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough # 1. 加载并切分 loader = TextLoader("product_manual.md", encoding="utf-8") documents = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=80) chunks = splitter.split_documents(documents) # 2. 向量化并建库 embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = FAISS.from_documents(chunks, embeddings) # 3. 检索器与链 retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) prompt = ChatPromptTemplate.from_template( """你是文档问答助手。请严格根据下面提供的资料片段回答问题。 如果资料片段中没有相关信息,请直接回答“资料中未找到相关内容”,不要自行推测。 资料片段: {context} 问题:{question} 回答:""" ) llm = ChatOpenAI(model="your-model-name", temperature=0) rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) # 4. 测试 result = rag_chain.invoke("这款设备的最大输出电压是多少?") print(result)

运行之后,你会看到模型基于检索到的片段给出答案。如果文档里没有这个参数,模型会按prompt要求回答“资料中未找到相关内容”,而不是自己编一个值。

3.6 验证效果:对比有RAG和没RAG的差别

我做这个Demo时习惯同时跑两个版本:一个走上面的RAG链,另一个直接用裸LLM回答同一个问题。效果差异非常明显,同一个关于设备参数的问题,裸模型自信地给出一组数据,RAG模型则老老实实引用手册里那句原文。这个对比建议你也做一下,能直观感受到RAG“限定范围”到底限定了什么。

4. 参数调优:让回答既准确又不越界

4.1 chunk_size与chunk_overlap怎么定

这组参数直接决定召回颗粒度。切太小,语义可能不完整;切太大,经常把无关内容混进同一段,还浪费token。我的经验值参考如下:

chunk_size优点缺点适用场景
200以下定位精准上下文可能缺上下文词汇查找、关键词型问答
300-500兼顾语义完整性偶有信息割裂多数通用文档问答
800以上保留完整段落召回粗、容易混入噪音长段落法律条款、制度文件

chunk_overlap的作用是让相邻chunk之间保留交接处的信息,防止一个完整句子正好被切分线一分为二。我一般设为chunk_size的10%-20%,比如500字配80字。太小没效果,太大则会让相邻块几乎重复,同一内容被重复检索,白白占上下文窗口。

4.2 top_k怎么选:不是越大越好

top_k控制检索返回多少块。k太小,可能漏掉真正相关的内容;k太大,不相关的段落混入,模型反而被干扰。我在不同需求下的选择参考这几点:答案是名词型、出处明确,k选3足够;需要综合多段信息才能总结,k选4-6;文档质量参差、噪音较多时,反而要减小k,防止污染。

除了调k,还可以用search_type="similarity_score_threshold",给检索设一个相似度阈值,低于阈值的直接不上屏。这个机制在“宁可答不上来也不要答错”的场景里特别实用。

4.3 temperature与prompt约束:双重保险

LLM参数里,temperature直接决定生成随机性。RAG问答中我几乎一律设置为0,最多不超过0.2,否则同样的上下文会生成不同措辞,甚至偶尔发散。

温度是软约束,更硬的约束在prompt里。我在生产项目中使用的指令模板是三层结构:第一步声明角色,第二步定义回答边界“只能使用context中的内容”,第三步定义未知时的行为“资料中没有就明说没有”。这三层缺一不可,尤其是第三层,没有它模型大概率会强行编造一个“看起来合理”的答案。

4.4 评价检索质量的土办法

很多教程让你用精确率召回率评估,但个人项目里面并没有标注好的评估集。我提供一个土办法:人工准备10个问题,逐个打印retriever.invoke(question)的输出,看返回的chunk到底相关不相关。如果5个问题里超过2个检索结果明显不相关,那就不要急着调生成,先解决检索。

另外一个技巧是打印相似度分数。FAISS上你可以通过vectorstore.similarity_search_with_score(question, k=4)拿到分数,分数普遍很低时,说明embedding对当前语料不敏感,这时应该考虑换embedding模型或重写检索query,而不是继续加剧情。

5. 常见问题与排查技巧实录

5.1 模型明明有资料还是胡编

这个是最常见的翻车现场。排查顺序我建议这样:先把链里最终发给LLM的prompt完整打印出来,看看context字段里到底塞进去了什么。很多时候问题出在检索结果为空,或prompt模板的变量名写错了,context根本就是个空列表,模型没有资料可用,自然只好自由发挥。

如果prompt里有内容还是胡编,再看prompt里是否缺少“没有就承认没有”的约束。不少人把“严格根据资料”写在角色描述里,实际模板里却只有一个context变量,模型读不出强制边界。养成习惯:未知行为必须在prompt里显式声明。

5.2 检索出来的片段不相关

检索不相关的原因通常有四种:embedding模型语言不匹配;chunk块太大导致语义混杂;query太短或者包含太多口语化表达;文档里是扫描件或表格,加载器根本没读到正文。

我早期踩过最深的坑是PDF表格。PyPDFLoader读表格会把列和行都打碎成不连续字符串,检索时完全命中不了,生成自然就瞎编。这种情况要么先做OCR,要么把表格转成Markdown格式再喂给切分器。

5.3 上下文太长,token爆了

chunk_size=1000加top_k=6,再加上问题和历史对话,一个请求轻松超过一万token。爆token的典型现象是接口直接报错,或者是模型只回复前半段就截断。解决方式很直接:缩小chunk_size到500以内,k调到3-4,同时给context做截断或只保留得分最高的几段。

如果资料本身非常长,建议先做一层粗过滤再进RAG。比如先用关键词或元数据筛选掉明显无关的章节,再对剩余部分做向量检索。这比单纯扩大向量库管用得多。

5.4 回答每次都不一样

同一份资料同一个问题,跑两次结果不同。先查模型参数,temperature没设0导致生成随机。第二步查向量库是否有更新,如果保存的索引文件没有重建,新增文档不会自动进入检索结果。第三步看prompt里是否有排序相关表述,如果让prompt“按资料顺序回答”,而检索结果每次排序不同,输出也会不同。

实测下来,RAG链本身对检索结果顺序很敏感。如果你发现模型只在某些时候引用命中的第二段,另一些时候引用第三段,那多半不是模型问题,而是检索排名的微小变化。固定temperature、固定top_k、必要时把search_kwargs里的fetch_k调大再压缩,能减少很多不确定性。

5.5 文档更新后结果不更新

这是最容易忽视的问题。很多人改完源文档,重新跑了一次加载和切分,却没有重建向量库索引。结果FAISS里还是旧数据的向量,模型每次都在旧版本资料中找答案。我的习惯是每次文档更新后,把索引文件删掉重建,或者按文档ID做增量管理,绝不在旧索引上直接追加。

可以用一个简单自查:更新文档后问一个只有新版才有的问题,如果答不上来,先查索引更新时间,基本能定位。

5.6 常见问题速查表

现象最可能的原因第一排查动作
模型编造内容检索结果为空或prompt无边界声明打印最终prompt检查context
检索到无关内容Embedding语言不匹配、chunk过大换embedding模型、缩小chunk_size
上下文超长chunk_size/top_k设置过大降低两者并做得分截断
同问不同答temperature过高或索引未更新temperature设为0,重建索引
表格类文档检索差PDF表格被加载器打碎先转Markdown/OCR再入库
新文档答不上来旧索引未重建删除索引并重新向量化

说实话,上面这些坑我基本都踩过一遍,最痛苦的不是报错,而是“代码完全正常,但效果就是不对”。后来我才明白,RAG的效果大头不在LLM模型本身,而在检索质量。如果你遇到类似问题,先别急着换更贵的模型,老老实实检查检索、切分、embedding这三层,多半有收获。

这个系列后面会继续更新,比如查询改写、多路召回、重排序,以及如何用评估集量化RAG效果。第一篇先到这里,把地基打稳了,后面加什么花样都容易。

相关新闻

播客内容自动化矩阵:OpenClaw+Whisper+Claude实战指南

播客内容自动化矩阵:OpenClaw+Whisper+Claude实战指南

做播客的朋友应该都有这种体会:一期节目录完、剪完、发出去,好像就结束了。认真一点的人会自己写shownotes,剪两条短视频,再发一篇图文笔记,配一张封面图。听起来也不复杂,但真正做起来,每期至少…

2026/10/20 20:15:40 阅读更多 →
图像变形与实时渲染:哈哈镜从反向映射到GPU Shader实现

图像变形与实时渲染:哈哈镜从反向映射到GPU Shader实现

做了这么多年图像处理相关的开发,我始终觉得“哈哈镜”是个特别有意思的功能。它不像磨皮、美白那样追求“无痕”,反而要刻意把脸弄变形,逗人一笑。但在美颜SDK里,哈哈镜又是很考验基本功的功能,因为它要同时搞定图像几…

2026/10/20 20:15:40 阅读更多 →
OpenOCD ARMEL版解析:ESP32嵌入式调试环境的软浮点配置与实战指南

OpenOCD ARMEL版解析:ESP32嵌入式调试环境的软浮点配置与实战指南

简介:面向嵌入式物联网开发场景,这一基于OpenOCD的芯片级调试工具包专为Linux ARMEL架构编译,可对ESP32系列芯片执行JTAG/SWD在线调试、固件烧录与异常定位,适合需要打通ARM环境开发链路的软硬件工程师。压缩包中共有913个文件&am…

2026/10/20 20:14:39 阅读更多 →

最新新闻

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

前段时间重新整理之前的深度学习实验笔记,翻到鞋类识别(Fashion MNIST)这个项目时,觉得特别值得拿出来单独写一篇。原因很简单:很多人入门深度学习时,都会被建议先跑通手写数字识别(MNIST&#…

2026/10/20 20:57:37 阅读更多 →
用AI辅助Kamailio路由排错:一场人机讨论实录与故障切换实践

用AI辅助Kamailio路由排错:一场人机讨论实录与故障切换实践

最近接手一套老的SIP通信平台,核心信令是Kamailio撑着。业务要新增一个较复杂的路由策略:同一个主叫号码,在不同时间段、针对不同被叫前缀,要分别走不同的网关;而其中一路网关做故障切换时,还要保证已经建立…

2026/10/20 20:57:37 阅读更多 →
LibVLC轻量级视频播放模块:嵌入C/C++工程的硬件解码方案

LibVLC轻量级视频播放模块:嵌入C/C++工程的硬件解码方案

简介:本资源是一份基于C/C与VLC开源库开发Windows视频播放器的完整工程实践包,面向具备基础C编程能力、希望掌握多媒体开发技能的中级开发者及高校课程设计学习者。资源聚焦于在Visual Studio 2022环境下集成libvlc实现视频加载、播放控制、窗口渲染等核…

2026/10/20 20:57:37 阅读更多 →
AI Agent创业团队如何配置一支高效的Harness Engineering团队

AI Agent创业团队如何配置一支高效的Harness Engineering团队

1. 为什么需要一支专门的Harness Engineering团队先从一个很现实的场景说起:你所在的创业团队花三个月做出了一个基于大模型的Agent产品Demo,演示给投资人看时效果炸裂,但真正进入客户试用阶段,问题接踵而来——模型给出正确意图却…

2026/10/20 20:57:37 阅读更多 →
视觉语言模型与RGB-D感知融合:三条进化主线与工程实践

视觉语言模型与RGB-D感知融合:三条进化主线与工程实践

1. 从 Transformer 到多模态感知的演进逻辑视觉语言模型和 RGB-D 感知这两个方向,过去几年基本是各走各的路。做 VLM 的人盯着图文对齐、指令跟随、视觉问答这些任务,做 RGB-D 的人则埋头于点云配准、深度补全、SLAM 建图。但最近一年,这两条…

2026/10/20 20:57:37 阅读更多 →
VTI介质地震波数值模拟:MATLAB实现与各向异性正演避坑指南

VTI介质地震波数值模拟:MATLAB实现与各向异性正演避坑指南

简介:这份资源面向地震勘探与地球物理方向的学习者和研究者,提供一套基于Matlab的VTI介质地震波场数值模拟程序,用于分析各向异性介质中地震波的传播规律。VTI介质在垂直与平行层理方向上纵波速度存在差异,是实际地壳中常见的各向…

2026/10/20 20:56:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →