<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      字節大模型應用開發框架 Eino 全解(一)|結合 RAG 知識庫案例分析框架生態

      前言

      大家好,這里是白澤,Eino 是字節開源的 Golang 大模型應用開發框架,諸如豆包、扣子等 Agent 應用或工作流都是借助這個框架進行開發。

      我將通過《字節大模型應用開發框架 Eino 全解》系列,從框架結構、組件生態、以及項目案例、mcp集成等維度,帶你全方面掌握 Golang 大模型應用開發。

      本章介紹

      • Eino 框架生態介紹,以及相關倉庫地址。
      • 借助白澤上一期開源的 Eino 編寫的 基于 Redis 文檔向量檢索系統,梳理 Eino 框架的各個組件模塊,以及交互、編排方式。

      Eino 框架生態

      image-20250511113615885

      • Eino(主代碼倉庫):包含類型定義、流處理機制、組件抽象、編排功能、切面機制等。
      • EinoExt:組件實現、回調處理程序實現、組件使用示例,以及各種工具,如評估器、提示優化器等。
      • Eino Devops:可視化開發、可視化調試等。
      • EinoExamples:是包含示例應用程序和最佳實踐的代碼倉庫。
      • Eino 用戶手冊:快速理解 Eino 中的概念,掌握基于 Eino 開發設計 AI 應用的技能。(Eino 開源不滿一年,文檔仍在完善)

      Redis 文檔向量檢索系統(RAG)

      接下來將通過這個案例,介紹一下 Eino 框架的各個組件,以及如何使用組件進行編排構建 Agent,同時帶你熟悉一下 Eino 本身的代碼結構。

      項目地址:https://github.com/BaiZe1998/go-learning/tree/main/eino_assistant

      項目架構圖:

      flowchart TD subgraph 索引構建階段 MD[Markdown文件] --> FL[文件加載器] FL --> SP[文檔分割器] SP --> EM1[嵌入模型] EM1 --> VEC1[文檔向量] VEC1 --> RDB[(Redis向量數據庫)] end subgraph 查詢檢索階段 Q[用戶問題] --> EM2[嵌入模型] EM2 --> VEC2[查詢向量] VEC2 --> KNN{KNN向量搜索} RDB --> KNN KNN --> TOP[TopK相關文檔] end subgraph 回答生成階段 TOP --> PC[提示構建] Q --> PC PC --> PROMPT[增強提示] PROMPT --> LLM[大語言模型] LLM --> ANS[生成回答] end subgraph 系統架構 direction LR RET[檢索器\nRetriever] --> RAG_SYS[RAG系統] GEN[生成器\nGenerator] --> RAG_SYS OPT[參數配置\ntopK等] --> RAG_SYS end Q --> RAG_SYS RAG_SYS --> ANS classDef phase fill:#f9f,stroke:#333,stroke-width:2px; class 索引構建階段,查詢檢索階段,回答生成階段 phase;

      整個項目包含三個階段,索引構建、檢查索引、回答生成、接下來以索引構建階段為例,介紹一下用上了 Eino 哪些組件,以及組件之間的關系,完整的項目講解可以看往期的文章。

      ?? 整個過程中我們的項目中會同時引入 Eino庫 和 Eino-Ext 庫的內容,希望你能體會 Eino 生態將穩定的類型定義、組件抽象、編排邏輯放置在 Eino 主庫中,而將可擴展的組件、工具實現拆分到 Eino-Ext 庫中的好處。

      一、組件初始化

      Eino 組件大全

      • tool: 對接外部工具,提供了常用工具集。
      • chatmodel:對接各家大模型的調用接口。
      • callbacks:一些工具的 hook 能力的實現。
      • chattemplate:提示詞工程相關,處理和格式化提示模板的組件。
      • indexer:Indexer 為把文本進行索引存儲,一般使用 Embedding 做語義化索引,也可做分詞索引等,以便于 Retriever 中召回使用。
      • retriver:Retriever 用于把 Indexer 構建索引之后的內容進行召回,在 AI 應用中,一般使用 Embedding 進行語義相似性召回。
      • document:對接各家的文檔切分和過濾。
      • embeding:對接各家文檔向量化模型。

      索引構建本質上也是一個局部完整的工作流,可以借助編輯器插件 Eino Dev 完成可視化的編輯工作流,在可視化的編輯窗口,編排工作流。

      image-20250505232622560

      點擊 generate 直接生成如下5個文件,然后手動替換內部的業務邏輯。

      Eino Dev 插件的使用將在組件講解篇完成后,單出一期講解。

      image-20250511140959179

      接下來我們看一下五個文件的內容,特別是關注 import 的庫的來源。

      • loader.go 創建文件加載組件
      package knowledgeindexing
      
      import (
      	"context"
      
      	"github.com/cloudwego/eino-ext/components/document/loader/file"
      	"github.com/cloudwego/eino/components/document"
      )
      
      // newLoader component initialization function of node 'FileLoader' in graph 'KnowledgeIndexing'
      func newLoader(ctx context.Context) (ldr document.Loader, err error) {
      	// TODO Modify component configuration here.
      	config := &file.FileLoaderConfig{}
      	ldr, err = file.NewFileLoader(ctx, config)
      	if err != nil {
      		return nil, err
      	}
      	return ldr, nil
      }
      

      document.Loader

      返回值類型是一個接口,定義在 Eino 主庫的 components/document 目錄下。

      type Loader interface {
      	Load(ctx context.Context, src Source, opts ...LoaderOption) ([]*schema.Document, error)
      }
      

      file.NewFileLoader

      返回一個具體的文件加載的實現,定義在 Eino-Ext 庫的 components/document 目錄下,是對應關系。

      unc NewFileLoader(ctx context.Context, config *FileLoaderConfig) (*FileLoader, error) {
      	if config == nil {
      		config = &FileLoaderConfig{}
      	}
      	if config.Parser == nil {
      		parser, err := parser.NewExtParser(ctx,
      			&parser.ExtParserConfig{
      				FallbackParser: parser.TextParser{},
      			},
      		)
      		if err != nil {
      			return nil, fmt.Errorf("new file parser fail: %w", err)
      		}
      
      		config.Parser = parser
      	}
      
      	return &FileLoader{FileLoaderConfig: *config}, nil
      }
      
      • transformer.go 創建 markdown 文件分割組件
      import (
      	"context"
      
      	"github.com/cloudwego/eino-ext/components/document/transformer/splitter/markdown"
      	"github.com/cloudwego/eino/components/document"
      )
      
      // newDocumentTransformer component initialization function of node 'MarkdownSplitter' in graph 'KnowledgeIndexing'
      func newDocumentTransformer(ctx context.Context) (tfr document.Transformer, err error) {
      	// TODO Modify component configuration here.
      	config := &markdown.HeaderConfig{
      		Headers: map[string]string{
      			"#": "title",
      		},
      		TrimHeaders: false}
      	tfr, err = markdown.NewHeaderSplitter(ctx, config)
      	if err != nil {
      		return nil, err
      	}
      	return tfr, nil
      }
      

      document.Transformer

      返回值類型是一個接口,定義在 Eino 主庫的 components/document 目錄下,定義文檔的過濾和分割。

      // Transformer is to convert documents, such as split or filter.
      type Transformer interface {
      	Transform(ctx context.Context, src []*schema.Document, opts ...TransformerOption) ([]*schema.Document, error)
      }
      

      markdown.NewHeaderSplitter

      創建一個基于 # 標簽進行分割的 markdown 組件,定義在 Eino-Ext 擴展庫的 components/document/transformer/splitter/markdown 目錄下。

      func NewHeaderSplitter(ctx context.Context, config *HeaderConfig) (document.Transformer, error) {
      	if len(config.Headers) == 0 {
      		return nil, fmt.Errorf("no headers specified")
      	}
      	for k := range config.Headers {
      		for _, c := range k {
      			if c != '#' {
      				return nil, fmt.Errorf("header can only consist of '#': %s", k)
      			}
      		}
      	}
      
      	return &headerSplitter{
      		headers:     config.Headers,
      		trimHeaders: config.TrimHeaders,
      	}, nil
      }
      

      到這一步你應該有了大致的感受,Eino 和 Eino-Ext 是相輔相成的。

      看一下 Eino 庫的組件目錄結構。

      image-20250511143643060

      看一下 Eino-Ext 的組件目錄結構。

      image-20250511143554102

      • embedding.go

      文檔向量化,需要在初始化的時候,指定一個向量化的模型,用于將文檔數據向量化之后,存入 Redis 向量索引中(也可以使用其他向量數據庫),這里使用了字節的 doubao-embedding-large-text-240915 模型。

      package knowledgeindexing
      
      import (
      	"context"
      	"os"
      
      	"github.com/cloudwego/eino-ext/components/embedding/ark"
      	"github.com/cloudwego/eino/components/embedding"
      )
      
      func newEmbedding(ctx context.Context) (eb embedding.Embedder, err error) {
      	// TODO Modify component configuration here.
      	config := &ark.EmbeddingConfig{
      		BaseURL: "https://ark.cn-beijing.volces.com/api/v3",
      		APIKey:  os.Getenv("ARK_API_KEY"),
      		Model:   os.Getenv("ARK_EMBEDDING_MODEL"),
      	}
      	eb, err = ark.NewEmbedder(ctx, config)
      	if err != nil {
      		return nil, err
      	}
      	return eb, nil
      }
      
      • indexer.go(這一步需要你本地通過啟動一個 redis)

      Redis向量索引(通過RediSearch模塊實現)是一種高性能的向量數據庫功能,它允許:

      1. 向量存儲: 在Redis中存儲高維向量數據

      2. 語義搜索: 基于向量相似度進行搜索(而非簡單的關鍵詞匹配)

      3. KNN查詢: 使用K-Nearest Neighbors算法找到最接近的向量

      Redis向量索引的核心概念:

      1. 哈希結構: 使用Redis Hash存儲文檔內容、元數據和向量

      2. 向量字段: 特殊字段類型,支持高效的向量操作

      3. 相似度計算: 支持多種距離度量方式(如余弦相似度、歐氏距離)

      import (
      	"context"
      	"encoding/json"
      	"fmt"
      	"log"
      	"os"
      
      	"github.com/cloudwego/eino-ext/components/indexer/redis"
      	"github.com/cloudwego/eino/components/indexer"
      	"github.com/cloudwego/eino/schema"
      	"github.com/google/uuid"
      	redisCli "github.com/redis/go-redis/v9"
      
      	redispkg "eino_assistant/pkg/redis"
      )
      
      func init() {
          // 初始化索引
      	err := redispkg.Init()
      	if err != nil {
      		log.Fatalf("failed to init redis index: %v", err)
      	}
      }
      
      // newIndexer component initialization function of node 'RedisIndexer' in graph 'KnowledgeIndexing'
      func newIndexer(ctx context.Context) (idr indexer.Indexer, err error) {
      	// TODO Modify component configuration here.
      	redisAddr := os.Getenv("REDIS_ADDR")
      	redisClient := redisCli.NewClient(&redisCli.Options{
      		Addr:     redisAddr,
      		Protocol: 2,
      	})
      	// 文檔向量轉換配置
      	config := &redis.IndexerConfig{
      		Client:    redisClient,
      		KeyPrefix: redispkg.RedisPrefix,
      		BatchSize: 1,
              // 文檔到 hash 的邏輯轉換
      		DocumentToHashes: func(ctx context.Context, doc *schema.Document) (*redis.Hashes, error) {
      			if doc.ID == "" {
      				doc.ID = uuid.New().String()
      			}
      			key := doc.ID
      
      			metadataBytes, err := json.Marshal(doc.MetaData)
      			if err != nil {
      				return nil, fmt.Errorf("failed to marshal metadata: %w", err)
      			}
      
      			return &redis.Hashes{
      				Key: key,
      				Field2Value: map[string]redis.FieldValue{
      					redispkg.ContentField:  {Value: doc.Content, EmbedKey: redispkg.VectorField},
      					redispkg.MetadataField: {Value: metadataBytes},
      				},
      			}, nil
      		},
      	}
          // 配置 doubao 嵌入模型(文檔向量化)
      	embeddingIns11, err := newEmbedding(ctx)
      	if err != nil {
      		return nil, err
      	}
      	config.Embedding = embeddingIns11
      	idr, err = redis.NewIndexer(ctx, config)
      	if err != nil {
      		return nil, err
      	}
      	return idr, nil
      }
      

      二、組件編排

      orchestration.go

      文檔索引構建階段,上文的代碼文件連同 orchestration.go 都是通過插件生成的,編排完 ui 工作流,就會為你生成組件之間的流式代碼。

      import (
      	"context"
      
      	"github.com/cloudwego/eino/components/document"
      	"github.com/cloudwego/eino/compose"
      )
      
      func BuildKnowledgeIndexing(ctx context.Context) (r compose.Runnable[document.Source, []string], err error) {
      	const (
      		FileLoader       = "FileLoader"
      		MarkdownSplitter = "MarkdownSplitter"
      		RedisIndexer     = "RedisIndexer"
      	)
      	g := compose.NewGraph[document.Source, []string]()
      	fileLoaderKeyOfLoader, err := newLoader(ctx)
      	if err != nil {
      		return nil, err
      	}
      	_ = g.AddLoaderNode(FileLoader, fileLoaderKeyOfLoader)
      	markdownSplitterKeyOfDocumentTransformer, err := newDocumentTransformer(ctx)
      	if err != nil {
      		return nil, err
      	}
      	_ = g.AddDocumentTransformerNode(MarkdownSplitter, markdownSplitterKeyOfDocumentTransformer)
      	redisIndexerKeyOfIndexer, err := newIndexer(ctx)
      	if err != nil {
      		return nil, err
      	}
          // 編排的核心:通過點和邊的概念,順序處理數據
      	_ = g.AddIndexerNode(RedisIndexer, redisIndexerKeyOfIndexer)
      	_ = g.AddEdge(compose.START, FileLoader)
      	_ = g.AddEdge(RedisIndexer, compose.END)
      	_ = g.AddEdge(FileLoader, MarkdownSplitter)
      	_ = g.AddEdge(MarkdownSplitter, RedisIndexer)
      	r, err = g.Compile(ctx, compose.WithGraphName("KnowledgeIndexing"), compose.WithNodeTriggerMode(compose.AllPredecessor))
      	if err != nil {
      		return nil, err
      	}
      	return r, err
      }
      

      ?? 通過 import 的庫可以看到,編排的流程抽象和數據傳輸類型,都是定義在 Eino 主庫當中的,這里使用了范型來動態定義輸入和輸出類型,此外 Eino 允許上下游之間通過流式或者非流失的形式交換數據,這都是框架的能力。

      // Runnable is the interface for an executable object. Graph, Chain can be compiled into Runnable.
      // runnable is the core conception of eino, we do downgrade compatibility for four data flow patterns,
      // and can automatically connect components that only implement one or more methods.
      // eg, if a component only implements Stream() method, you can still call Invoke() to convert stream output to invoke output.
      type Runnable[I, O any] interface {
      	Invoke(ctx context.Context, input I, opts ...Option) (output O, err error)
      	Stream(ctx context.Context, input I, opts ...Option) (output *schema.StreamReader[O], err error)
      	Collect(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output O, err error)
      	Transform(ctx context.Context, input *schema.StreamReader[I], opts ...Option) (output *schema.StreamReader[O], err error)
      }
      

      Eino 提供了兩組用于編排的 API:

      API 特性和使用場景
      Chain 簡單的鏈式有向圖,只能向前推進。
      Graph 循環或非循環有向圖。功能強大且靈活。

      我們來創建一個簡單的 chain: 一個模版(ChatTemplate)接一個大模型(ChatModel)。

      image-20250511151304392

      chain, _ := NewChain[map[string]any, *Message]().
                 AppendChatTemplate(prompt).
                 AppendChatModel(model).
                 Compile(ctx)
      chain.Invoke(ctx, map[string]any{"query": "what's your name?"})
      

      現在,我們來創建一個 Graph,先用一個 ChatModel 生成回復或者 Tool 調用指令,如生成了 Tool 調用指令,就用一個 ToolsNode 執行這些 Tool。

      image-20250511151333671

      graph := NewGraph[map[string]any, *schema.Message]()
      
      _ = graph.AddChatTemplateNode("node_template", chatTpl)
      _ = graph.AddChatModelNode("node_model", chatModel)
      _ = graph.AddToolsNode("node_tools", toolsNode)
      _ = graph.AddLambdaNode("node_converter", takeOne)
      
      _ = graph.AddEdge(START, "node_template")
      _ = graph.AddEdge("node_template", "node_model")
      _ = graph.AddBranch("node_model", branch)
      _ = graph.AddEdge("node_tools", "node_converter")
      _ = graph.AddEdge("node_converter", END)
      
      compiledGraph, err := graph.Compile(ctx)
      if err != nil {
      return err
      }
      out, err := r.Invoke(ctx, map[string]any{"query":"Beijing's weather this weekend"})
      

      小節

      下一章講解如何通過 Eino 集成 MCP,敬請期待。

      公眾號【白澤talk】,Golang|AI 大模型應用開發相關知識星球:白澤說 ,添加: baize_talk02 咨詢加入~

      posted on 2025-05-11 17:00  白澤talk  閱讀(2656)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产精品自拍自在线播放| 秋霞在线观看秋| 聂拉木县| 美日韩精品一区二区三区| 亚洲综合一区二区国产精品| 欧美大屁股喷潮水xxxx| 蜜臀久久精品亚洲一区| 激情偷乱人成视频在线观看| 国产精品亚洲中文字幕| 欧洲码亚洲码的区别入口| 国内精品大秀视频日韩精品| 在线精品国精品国产不卡| 99精品伊人久久久大香线蕉 | 国产成人午夜福利在线播放| 国产欧美亚洲精品第1页| 亚洲欧洲日产国无高清码图片| 亚洲综合无码一区二区三区不卡| 麻豆精品一区二区三区蜜臀 | 狠狠色丁香婷婷综合尤物| 国产精品国产三级国快看| 日日摸天天爽天天爽视频| 日韩乱码视频一区二区三区 | 乱60一70归性欧老妇| 精品粉嫩国产一区二区三区| 亚洲夂夂婷婷色拍ww47| 国产免费网站看v片元遮挡| 麻豆麻豆麻豆麻豆麻豆麻豆| 日本伊人色综合网| 韩国主播av福利一区二区| 久久精品国产亚洲av天海翼| 国产麻花豆剧传媒精品mv在线| 久久一日本道色综合久久| 日韩av中文字幕有码| 苍南县| 精品无码国产不卡在线观看| AV区无码字幕中文色| 亚洲欧美国产日韩天堂区| 日韩av裸体在线播放| 亚洲熟妇自偷自拍另类| 中文字幕有码日韩精品| 国产91麻豆视频免费看|