AI写作智能体 自主规划任务,支持联网查询和网页读取,多模态高效创作各类分析报告、商业计划、营销方案、教学内容等。 广告
[TOC] > [home](https://tmc.github.io/langchaingo/docs/) ## 概述 LangChainGo 的设计围绕以下几个关键组件,帮助开发者更轻松地与语言模型交互: * **模型(LLMs)**:支持调用大语言模型(如 Ollama 本地模型、OpenAI 等)。 * **嵌入(Embeddings)**:将文本转化为向量表示,用于语义搜索或相似性匹配。 * **向量存储(Vector Stores)**:存储和管理文本嵌入,支持快速检索。 * **提示模板(Prompt Templates)**:动态生成模型输入,提升生成质量。 * **记忆(Memory)**:保存对话上下文,实现多轮交互。 * **文档加载与分割(Document Loaders & Splitters)**:处理外部文档,构建知识库。 * **链(Chains)**:组合上述组件,创建复杂的工作流。 ## 实例 <details> <summary>main.go</summary> ``` package main import ( "bufio" "context" "fmt" "log" "os" "path/filepath" "strings" "database/sql" _ "github.com/lib/pq" // 导入 postgres 驱动 "github.com/go-ego/gse" // 导入 gse 分词库 "github.com/tmc/langchaingo/embeddings" "github.com/tmc/langchaingo/llms" "github.com/tmc/langchaingo/llms/ollama" "github.com/tmc/langchaingo/schema" "github.com/tmc/langchaingo/vectorstores" "github.com/tmc/langchaingo/vectorstores/pgvector" ) // KnowledgeBase 定义知识库结构 type KnowledgeBase struct { llm *ollama.LLM vectorStore vectorstores.VectorStore embedder embeddings.Embedder } // NewKnowledgeBase 初始化知识库 func NewKnowledgeBase(modelName string) (*KnowledgeBase, error) { // 初始化 Ollama LLM llm, err := ollama.New(ollama.WithModel(modelName)) if err != nil { return nil, fmt.Errorf("初始化 LLM 失败: %v", err) } // 初始化嵌入器 embedder, err := embeddings.NewEmbedder(llm) if err != nil { return nil, fmt.Errorf("初始化嵌入器失败: %v", err) } return &KnowledgeBase{ llm: llm, embedder: embedder, }, nil } // ChineseTextSplitter 定义中文分词器结构 type ChineseTextSplitter struct { seg *gse.Segmenter } // NewChineseTextSplitter 初始化中文分词器 func NewChineseTextSplitter() *ChineseTextSplitter { seg, err := gse.New() if err != nil { log.Fatalf("初始化中文分词器失败: %v", err) } seg.LoadDict() return &ChineseTextSplitter{ seg: &seg, } } // SplitText 分割中文文本 func (s *ChineseTextSplitter) SplitText(text string) []string { return s.seg.Cut(text, true) } // LoadDirectory 加载目录数据到内存向量存储 func (kb *KnowledgeBase) LoadDirectory(dirPath string) error { // 创建中文分词器 splitter := NewChineseTextSplitter() // 使用新的中文分词器 var documents []schema.Document // 递归遍历目录 err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() || !strings.HasSuffix(info.Name(), ".txt") { return nil } // 读取文件 content, err := os.ReadFile(path) if err != nil { log.Printf("读取文件 %s 失败: %v", path, err) return nil } // 分割文本 chunks := splitter.SplitText(string(content)) // 使用新的分词方法 // 添加到文档列表 for _, chunk := range chunks { documents = append(documents, schema.Document{ PageContent: chunk, Metadata: map[string]interface{}{ "source": info.Name(), "path": path, }, }) } return nil }) if err != nil { return fmt.Errorf("遍历目录失败: %v", err) } connStr := "postgres://postgres:123456@localhost/ai?sslmode=disable" client, err := sql.Open("postgres", connStr) if err != nil { return fmt.Errorf("连接到 PostgreSQL 失败: %v", err) } defer client.Close() // 创建内存向量存储 kb.vectorStore, err = pgvector.New( context.Background(), pgvector.WithEmbedder(kb.embedder), // 直接传递嵌入器 pgvector.WithConnectionURL(connStr), // 添加连接参数 ) if err != nil { return fmt.Errorf("初始化向量存储失败: %v", err) } if _, err := kb.vectorStore.AddDocuments(context.Background(), documents); err != nil { log.Printf("将文档 %s 添加到向量存储失败: %v", documents[0].Metadata["source"], err) } log.Printf("成功加载 %d 个文档到内存向量存储", len(documents)) return nil } // Ask 根据问题查询内存向量存储并生成回答 func (kb *KnowledgeBase) Ask(ctx context.Context, question string) (string, error) { if kb.vectorStore == nil { return "", fmt.Errorf("向量存储未初始化,请先加载数据") } // 检索相关文档 docs, err := kb.vectorStore.SimilaritySearch(ctx, question, 3) // 返回前 3 个相关文档 if err != nil { return "", fmt.Errorf("相似性搜索失败: %v", err) } // 构建上下文 var context strings.Builder for _, doc := range docs { context.WriteString(fmt.Sprintf("来源: %s\n内容: %s\n\n", doc.Metadata["source"], doc.PageContent)) } // 未找到相关文档 if len(docs) == 0 { return "无法根据现有知识库回答", nil } // 构造提示 prompt := fmt.Sprintf(` 基于以下知识库内容回答问题。"。 知识库内容: %s 问题:%s 回答:`, context.String(), question) // 生成回答 response, err := llms.GenerateFromSinglePrompt(ctx, kb.llm, prompt) if err != nil { return "", fmt.Errorf("生成回答失败: %v", err) } return response, nil } func (kb *KnowledgeBase) StreamAsk(ctx context.Context, question string, streamFunc func(ctx context.Context, chunk []byte) error) error { if kb.vectorStore == nil { return fmt.Errorf("向量存储未初始化,请先加载数据") } // 检索相关文档 docs, err := kb.vectorStore.SimilaritySearch(ctx, question, 3) // 返回前 3 个相关文档 if err != nil { return fmt.Errorf("相似性搜索失败: %v", err) } // 构建上下文 var context strings.Builder for _, doc := range docs { context.WriteString(fmt.Sprintf("来源: %s\n内容: %s\n\n", doc.Metadata["source"], doc.PageContent)) } // 未找到相关文档 if len(docs) == 0 { fmt.Printf("无法根据现有知识库回答") // return fmt.Errorf("无法根据现有知识库回答") } fmt.Printf("===== 找到知识库 %d 个 =====\n", len(docs)) // 构造提示 prompt := fmt.Sprintf(` 如果存在知识库,则根据知识库进行解答,如果不存在,则根据问题进行解答。 知识库内容: %s 问题:%s 回答:`, context.String(), question) // 流式生成回答 completion, err := kb.llm.Call(ctx, prompt, llms.WithStreamingFunc(streamFunc)) if err != nil { return fmt.Errorf("流式生成回答失败: %v", err) } fmt.Printf("completion=%v", completion) return nil } func main() { // 初始化知识库 kb, err := NewKnowledgeBase("deepseek-r1:1.5b") if err != nil { log.Fatal(err) } // 加载目录数据到内存向量存储 dirPath := "/idcpj/workspace/antbiz/data/knowledge/" if err := kb.LoadDirectory(dirPath); err != nil { log.Fatal(err) } // 测试提问 ctx := context.Background() // questions := []string{ // // "公司今年的销售目标是多少?", // "我想继承大蚂蚁即时通讯的接口,想知道添加人员的API,直接告诉我地址", // } // 批量回答 // for _, q := range questions { // answer, err := kb.Ask(ctx, q) // if err != nil { // log.Printf("提问失败: %v", err) // continue // } // fmt.Printf("问题: %s\n回答: %s\n\n", q, answer) // } // 流式回答 // kb.StreamAsk(ctx, questions[0], func(ctx context.Context, chunk []byte) error { // fmt.Printf("%v", string(chunk)) // return nil // }) // 创建一个扫描器来监听命令行输入 scanner := bufio.NewScanner(os.Stdin) fmt.Println("请输入您的问题:") for scanner.Scan() { question := scanner.Text() if question == "exit" { fmt.Println("退出程序") break } // 流式生成回答 err := kb.StreamAsk(ctx, question, func(ctx context.Context, chunk []byte) error { fmt.Printf("%v", string(chunk)) return nil }) if err != nil { log.Printf("提问失败: %v\n请输入您的问题\n", err) } } } ``` </details>