Skip to content

数据加载 (Data Loading)

在 RAG 系统中,数据加载是第一步。我们需要将各种格式的非结构化数据(PDF, Word, Markdown, HTML)加载到内存中,并统一转换为标准格式。

1. 核心数据结构

在 Golang 中,我们通常定义一个通用的 Document 结构体来承载数据:

go
type Document struct {
    PageContent string                 // 文档正文
    Metadata    map[string]interface{} // 元数据(如文件名、页码、来源URL)
}

这与 langchaingo 中的 schema.Document 是一致的。

2. 常见文件格式加载

2.1 加载 TXT / Markdown 文件

对于纯文本文件,使用 Golang 标准库 osio 即可轻松搞定。

go
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/tmc/langchaingo/documentloaders"
)

func LoadText() {
	f, _ := os.Open("example.md")
	defer f.Close()

	// 使用 langchaingo 的 TextLoader
	loader := documentloaders.NewText(f)
	docs, err := loader.Load(context.Background())
	if err != nil {
		panic(err)
	}

	fmt.Println(docs[0].PageContent)
}

2.2 加载 PDF 文件

PDF 解析稍微复杂一些,推荐使用 github.com/ledongthuc/pdfrsc.io/pdflangchaingo 暂未内置 PDF Loader,我们可以自己封装一个。

封装 PDFLoader 示例:

go
package main

import (
	"bytes"
	"context"
	"fmt"
	
	"github.com/ledongthuc/pdf"
	"github.com/tmc/langchaingo/schema"
)

// PDFLoader 实现 documentloaders.Loader 接口
type PDFLoader struct {
	path string
}

func NewPDFLoader(path string) *PDFLoader {
	return &PDFLoader{path: path}
}

func (l *PDFLoader) Load(ctx context.Context) ([]schema.Document, error) {
	f, r, err := pdf.Open(l.path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var buf bytes.Buffer
	b, err := r.GetPlainText() // 获取纯文本
	if err != nil {
		return nil, err
	}
	buf.ReadFrom(b)

	return []schema.Document{
		{
			PageContent: buf.String(),
			Metadata: map[string]any{
				"source": l.path,
			},
		},
	}, nil
}

func main() {
	loader := NewPDFLoader("manual.pdf")
	docs, _ := loader.Load(context.Background())
	fmt.Printf("PDF 内容长度: %d\n", len(docs[0].PageContent))
}

2.3 加载网页 (HTML)

可以使用 goquery 库来提取网页正文,去除 HTML 标签。

go
import (
	"net/http"
	"strings"
	"github.com/PuerkitoBio/goquery"
)

func FetchURL(url string) string {
	res, _ := http.Get(url)
	defer res.Body.Close()
	
	doc, _ := goquery.NewDocumentFromReader(res.Body)
	
	// 移除 script 和 style 标签
	doc.Find("script,style").Each(func(i int, s *goquery.Selection) {
		s.Remove()
	})
	
	return strings.TrimSpace(doc.Text())
}

3. 并发加载优化

Golang 的最大优势在于并发。当需要加载数千个文档时,一定要利用 Goroutine。

go
func LoadDir(dir string) []schema.Document {
	files, _ := os.ReadDir(dir)
	ch := make(chan schema.Document, len(files))
	
	// 启动并发加载
	for _, file := range files {
		go func(f os.DirEntry) {
			// ... 加载逻辑 ...
			ch <- doc
		}(file)
	}
	
	// 收集结果
	var allDocs []schema.Document
	for range files {
		allDocs = append(allDocs, <-ch)
	}
	return allDocs
}

总结

数据加载的核心是将异构数据转化为同构的 Document 对象。在 Golang 中,利用接口(Interface)和并发(Goroutine)可以构建出非常高效的数据处理管道(ETL Pipeline)。

🚀 学习遇到瓶颈?想进大厂?

看完这篇技术文章,如果还是觉得不够系统,或者想在实战中快速提升?
王中阳的就业陪跑训练营,提供定制化学习路线 + 企业级实战项目 + 简历优化 + 模拟面试。

了解训练营详情