RAG 知识库工程化实践
RAG 知识库工程化实践 Retrieval-Augmented Generation(RAG)是当前最主流的大模型应用模式之一。本文从工程实践出发,梳理 RAG 系统的完整搭建流程。 为什么需要 RAG大模型虽然能力强大,但存在三个核心局限: 知识时效:训练数据有截止日期 幻觉问题:会”编造”看似合理但实际错误的内容 私域知识:无法回答企业内部文档相关的问题 RAG 通过在生成前先检索相关文档,把外部知识注入到大模型的上下文窗口,能有效缓解这些问题。 RAG 的核心流程12345678910111213用户提问 ↓Query 改写(可选) ↓向量检索 + 关键词检索(混合检索) ↓重排序(Rerank) ↓Prompt 构造(拼接检索结果) ↓LLM 生成答案 ↓引用溯源(可选) 文档处理流水线123456789原始文档(PDF / Word / Markdown / HTML) ↓格式解析(unstructured / docx / pypdf) ↓切片(chunking) ↓Embedding(向量化) ↓向量库索引(Milv...
从 .NET 单体到 Java 微服务的迁移实践
从 .NET 单体到 Java 微服务的迁移实践 技术栈迁移是架构师面对的最难课题之一:既要推倒旧的,又要保证业务不断。本文复盘我主导的一次公司级 .NET → Java 迁移。 为什么要迁做这个决策时,公司的核心系统是运行多年的 .NET 单体,问题很现实: 人才:Java 生态的招聘池远大于 .NET,团队扩编困难 生态:微服务、云原生、中间件,Java 侧的成熟方案更多 国产化:信创环境对 Java 的适配和支持更完善 迁移不是技术洁癖,而是工程现实倒逼的战略选择。 迁移策略:双轨并行 + 渐进替换最危险的做法是”停摆重写”。我们采用了双轨并行: 123老 .NET 系统(继续跑) ↓ 逐步替换新 Java 服务(增量上线) 具体节奏: 先建基础设施:注册中心、网关、数据库,先把”新世界的底座”搭好 从边缘模块开始:先迁相对独立、耦合度低的模块,积累经验 核心模块最后迁:等团队熟悉新栈、工具链稳定后再动核心 数据迁移的难点代码能重写,数据不能丢。数据迁移的几个关键点: 双写过渡期:新旧系统同时写库,用 MQ 同步,保证数据最终一致 数据校验:迁移...
大模型应用开发基础:API 调用与工程封装
大模型应用开发基础:API 调用与工程封装 这是我系统转向 AI 应用开发的第一篇笔记。一切从”调用一次大模型 API”开始,但真正要落地,靠的是工程封装。 一次最简调用调一个大模型 API 本质上就是发一个 HTTP 请求: 1234567891011import requestsresp = requests.post( "https://api.deepseek.com/v1/chat/completions", headers={"Authorization": "Bearer sk-xxx"}, json={ "model": "deepseek-chat", "messages": [{"role": "user", "content": "你好"}], },...
Go + KVM/libvirt 实现虚拟机管理
Go + KVM/libvirt 实现虚拟机管理 云平台最底层的核心,是让一台物理机”长出”多台虚拟机。这篇文章记录我用 Go 调用 libvirt 实现虚拟机生命周期管理的实践。 技术背景做超融合私有云时,我们面对的核心问题是:如何把 KVM 的虚拟化能力封装成可编程的接口,供上层平台调用。 技术栈选型: KVM:Linux 内核级虚拟化,负责真正的 CPU/内存虚拟化 QEMU:用户态设备模拟,配合 KVM 使用 libvirt:虚拟化管理抽象层,统一管理 KVM/QEMU/LXC 等 Go 侧通过 libvirt-go 绑定,直接调用 libvirt 的 C API。 连接与发现首先连接本机 libvirt 守护进程: 12345678conn, err := libvirt.NewConnect("qemu:///system")if err != nil { log.Fatal(err)}defer conn.Close()// 列出所有虚拟机doms, _ := conn.Li...
Go 并发编程:goroutine 与 channel
Go 并发编程:goroutine 与 channel Go 语言以简洁的并发模型著称。本文梳理 goroutine 与 channel 的核心用法。 goroutine启动一个 goroutine 非常简单: 123go func() { fmt.Println("hello from goroutine")}() 注意: main 结束时不会等待 goroutine 完成 goroutine 泄漏是常见 bug channel无缓冲 channel12345ch := make(chan int)go func() { ch <- 42 // 阻塞直到有接收者}()fmt.Println(<-ch) // 阻塞直到有数据 有缓冲 channel1234ch := make(chan int, 3)ch <- 1ch <- 2// 不会阻塞 常用并发模式1. Worker Pool123456789101112131415jobs := make(chan int, ...
Kubernetes 入门与生产实践
Kubernetes 入门与生产实践 Kubernetes 是云原生时代的事实标准。本文从核心概念到生产部署,系统梳理 K8s 学习路径。 核心概念1234567891011Pod(最小部署单元) ↓Deployment(无状态部署) ↓StatefulSet(有状态部署) ↓Service(服务发现) ↓Ingress(外部入口) ↓ConfigMap / Secret(配置管理) 为什么需要 K8s 自动伸缩:HPA 根据 CPU/内存/自定义指标扩缩容 自愈能力:Pod 异常自动重启 滚动更新:零停机发布 资源隔离:namespace + resource quota 生产部署要点1. 资源 requests / limits1234567resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi 2. 健康检查 livenessProbe:存活探针 readinessProbe:就绪探针 sta...
K3s NodePort 为什么只有一个节点能访问
K3s NodePort 为什么只有一个节点能访问 这类”诡异”的网络问题最能体现一个人的排障能力。本文完整记录一次 K3s NodePort 服务异常的真实排查过程。 问题现象集群有 3 个节点,部署了一个 NodePort 类型的服务。理论上,通过任意节点的 NodePort 端口都能访问到这个服务。但实际情况是: 节点 A 上访问:正常 ✅ 节点 B、C 上访问:超时 ❌ 排查思路遇到网络问题,先从数据包路径一层层排除。 第一层:服务本身是否正常在节点 A 上访问正常,说明服务本身没问题。问题出在”跨节点”的链路。 第二层:NodePort 规则是否生效1iptables -t nat -L -n | grep -A 5 NODEPORT 检查发现 B、C 节点上的 NodePort 转发规则是有的,问题不在 iptables。 第三层:跨节点 Pod 网络NodePort 流量进来后,最终要转发到 Pod。而 Pod 可能调度在别的节点上,流量要经过跨节点的容器网络(Flannel 隧道)。 在 B 节点上 ping Pod IP,发现不通。基本锁定是 Flann...
做完 RAG 技术培训之后的一些想法
最近花了一些时间整理 RAG 相关的内容,也算是把之前零零散散接触过的东西重新过了一遍。 刚开始接触 RAG 的时候,其实觉得这个东西挺简单。 把文档丢进去,切一下,做 Embedding,存到向量库里,用户提问的时候检索一下,再把检索结果交给大模型。 整个流程并不复杂。 真正自己去做、去讲的时候,才发现里面有不少细节。 最开始觉得问题都在模型以前遇到回答不准确,第一反应比较容易想到模型。 是不是模型能力不够? 是不是 Prompt 写得不好? 是不是换个模型就好了? 但把 RAG 的整个流程拆开之后,会发现很多问题其实和模型没什么关系。 比如一个很简单的问题: 知识库里明明有这条内容,为什么就是没搜出来? 这个问题继续往下查,可能发现是文档解析的问题,也可能是 Chunk 切得不合适,也可能是用户的问题和原文表达差异比较大,甚至可能是本来应该做关键词匹配,却只做了向量检索。 最后到了大模型这里,已经没有多少发挥空间了。 它拿到的内容都不对,你让它怎么回答? 所以现在再看到”RAG 幻觉”这个词,我不会第一时间把问题归到大模型身上。 很多时候,应该先看看: 到底给模型了什么。 文...
