ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

10分钟从0到1:用CocoIndex把一堆Markdown变成可增量更新的向量索引

10分钟从0到1:用CocoIndex把一堆Markdown变成可增量更新的向量索引 10分钟从0到1用CocoIndex把一堆Markdown变成可增量更新的向量索引【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex手头有一堆 Markdown 文档想让 AI 按语义检索而不是只能做关键词匹配CocoIndex 是一个增量索引引擎你用原生 Python 声明目标状态 源状态的转换底下的 Rust 引擎负责跟踪变化、只对变化的部分重算。读完本文你将拥有一个可运行的向量索引 Postgres 里一张可以直接查询的向量表。先跑起来最小可运行环境 克隆仓库进入示例目录git clone https://gitcode.com/GitHub_Trending/co/cocoindex cd cocoindex/examples/text_embedding本文所有代码都对应这个示例扫目录里的 Markdown → 分块 → 嵌入 → 存入 Postgres是整条链路里最短的一条。安装依赖pip install -e .pyproject.toml 已声明cocoindex[postgres,sentence_transformers]、asyncpg、pgvector 等全部依赖一条命令装完。起一个带 pgvector 的 Postgresdocker compose -f ../../dev/postgres.yaml up -d仓库里现成的配置pgvector/pgvector:pg17镜像账号密码都是 cocoindex端口 5432。不展开 Docker 原理先跑起来再说。示例数据已就位markdown_files/目录下有三个 Markdown 文件两篇论文笔记 RFC 8259不需要自己准备。配置连接串export POSTGRES_URLpostgres://cocoindex:cocoindexlocalhost:5432/cocoindex代码里就是这个默认值本地 Docker 部署不加也能跑数据库在别的机器上时改这里。写一份索引定义核心代码按数据流向拆解 完整实现在 examples/text_embedding/main.py先看骨架import os from dataclasses import dataclass from typing import Annotated import asyncpg import cocoindex as coco from cocoindex.connectors import localfs, postgres from cocoindex.ops.text import RecursiveSplitter from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder from cocoindex.resources.file import FileLike, PatternFilePathMatcher from cocoindex.resources.id import IdGenerator from numpy.typing import NDArray DB_URL os.getenv(POSTGRES_URL, postgres://cocoindex:cocoindexlocalhost/cocoindex) PG_DB coco.ContextKeyasyncpg.Pool EMBEDDER coco.ContextKeySentenceTransformerEmbedder _splitter RecursiveSplitter() dataclass class DocEmbedding: # 表里一行 一个 chunk id: int filename: str text: str embedding: Annotated[NDArray, EMBEDDER] coco.fn(memoTrue) async def process_file(file: FileLike, table): text await file.read_text() chunks _splitter.split(text, chunk_size2000, chunk_overlap500, languagemarkdown) id_gen IdGenerator() await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)下面不按行号讲按数据流走一遍数据从哪来 → 怎么切 → 怎么变成向量 → 往哪存。数据从哪来walk_dirfiles localfs.walk_dir( sourcedir, recursiveTrue, path_matcherPatternFilePathMatcher(included_patterns[**/*.md]), liveTrue, )localfs.walk_dir递归扫描目录只挑**/*.md。liveTrue让它具备监听能力——配合cocoindex update -L main就能常驻盯着目录文件一变就重算而不是每次手动跑批。怎么切RecursiveSplitter分块参数就两个词chunk_size2000每块约 2000 字符、chunk_overlap500相邻块重叠 500 字符。重叠不是摆设如果一个概念正好横跨块边界重叠区能保证它至少在一个块里是完整的。languagemarkdown让分块器优先按段落、标题这类 Markdown 结构切而不是硬截断句子。怎么变成向量embed每个 chunk 的嵌入在一个coco.fn里完成完整版里是这样coco.fn async def process_chunk(chunk, filename, id_gen, table): table.declare_row(rowDocEmbedding( idawait id_gen.next_id(chunk.text), # id 由 chunk 文本推导 filenamestr(filename), textchunk.text, embeddingawait coco.use_context(EMBEDDER).embed(chunk.text), ))注意id_gen.next_id(chunk.text)id 从内容推导重跑时同一 chunk 落到同一行天然的 upsert 语义删除逻辑一行不用写。EMBEDDER是ContextKey上下文共享对象整个 pipeline 和查询端复用同一个嵌入器——索引用哪个模型查询就必须用哪个否则向量空间对不上。往哪存mount_table_targetcoco.fn async def app_main(sourcedir: pathlib.Path): table await postgres.mount_table_target( PG_DB, table_namedoc_embeddings, table_schemaawait postgres.TableSchema.from_class( DocEmbedding, primary_key[id]), ) table.declare_vector_index(columnembedding) files localfs.walk_dir( sourcedir, recursiveTrue, path_matcherPatternFilePathMatcher(included_patterns[**/*.md]), liveTrue, ) await coco.mount_each(process_file, files.items(), table) app coco.App( coco.AppConfig(nameTextEmbedding), app_main, sourcedirpathlib.Path(./markdown_files), )mount_table_target是托管目标managed target表结构从你的 dataclass 推导、pgvector 向量索引由declare_vector_index声明、行级 upsert 和孤儿行清理都由引擎代管。连接池和嵌入器通过coco.lifespan注入完整版里十几行思路是启动时建好随 pipeline 生命周期销毁。增量是这里最值钱的部分process_file标了memoTrue文件内容和处理代码都没变就整体跳过。改一个文件就只重嵌入一个文件。执行、验证与你会看到的输出 构建索引cocoindex update main跑完后你会看到同步统计输出各版本措辞略有差异格式以你本机为准documents: 3 added, 0 removed, 0 updated看到3 added就对了——三个 Markdown 文件全部入表。语义查询验证示例自带查询入口用同一个模型把你的问题嵌成向量再按余弦距离取 top5python main.py what is self-attention?输出形如分数因数据略有差异[0.631] 1706.03762v7.md …命中的 chunk 原文 ---第一行是注意力论文的 chunk哪怕你的提问和原文一个词都不重合——这就是向量索引存在的意义。SQL 快速验证不想走 Python直接看表也行docker compose -f ../../dev/postgres.yaml exec postgres psql -U cocoindex -d cocoindexSELECT filename, left(text, 40) FROM coco_examples.doc_embeddings LIMIT 5;有行返回说明向量已经落库。想验证增量往markdown_files/扔一个新的 .md 再跑一次cocoindex update main统计里只会出现新文件的那一行。首次执行报错速查避坑与调参 ⚠️现象update 报 connection refused→ 原因Postgres 容器没起或本机 5432 端口已被别的实例占用 → 修复先docker compose -f ../../dev/postgres.yaml up -d端口冲突就换一行起容器docker run -d -p 5433:5432 -e POSTGRES_PASSWORDcocoindex -e POSTGRES_USERcocoindex -e POSTGRES_DBcocoindex pgvector/pgvector:pg17然后把POSTGRES_URL指到 5433。现象首次运行卡住几分钟没输出→ 原因在从 Hugging Face 下载 all-MiniLM-L6-v2 模型不是挂了 → 修复耐心等网络不畅时先export HF_ENDPOINThttps://hf-mirror.com再跑镜像地址参考官方文档确认。现象换了嵌入模型检索结果却没变→ 原因memo 缓存按内容判重模型变化没被感知 → 修复完整版里EMBEDDER声明时带了detect_changeTrue换模型会自动触发全量重嵌入照抄即可不用手动清缓存。调参方面chunk_size和chunk_overlap是最常用的两个旋钮块太小行数和嵌入调用翻倍上下文还被切碎块太大检索命中范围变宽、定位变糊。默认 2000/500 对论文类长文偏稳。下一步往哪个方向延伸 常驻监听cocoindex update -L main让 pipeline 一直活着保存文件即重嵌入用法见 examples/text_embedding/README.md。换更宽的文档类型图片、PDF 混在一起也能索引这个示例的数据源里就有这种图实现参考 examples/multi_format_indexing/。换目标存储、看更多场景LanceDB、Qdrant、Kafka、知识图谱等 20 示例都在 examples/引擎原理从 docs/src/content/docs/getting_started/overview.mdx 读起。把 chunk_size 改小一点再跑一遍对比下检索效果。【免费下载链接】cocoindexIncremental engine for long horizon agents Star if you like it!项目地址: https://gitcode.com/GitHub_Trending/co/cocoindex创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表