cocoindex
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCocoIndex
CocoIndex
CocoIndex is a Python library for building incremental data processing pipelines with declarative target states. Think spreadsheets or React for data pipelines: declare what the output should look like based on current input, and CocoIndex automatically handles incremental updates, change detection, and syncing to external systems.
CocoIndex是一款用于构建增量数据处理管道的Python库,采用声明式目标状态模式。可以将其理解为数据管道领域的电子表格或React:只需基于当前输入声明输出应有的样子,CocoIndex就会自动处理增量更新、变更检测以及与外部系统的同步。
Overview
概述
CocoIndex enables building data pipelines that:
- Automatically handle incremental updates: Only reprocess changed data
- Use declarative target states: Declare what should exist, not how to update
- Support any Python types: No custom DSL -- use dataclasses, Pydantic, NamedTuple
- Provide function memoization: Skip expensive operations when inputs/code unchanged
- Sync to multiple targets: PostgreSQL, SQLite, LanceDB, Qdrant, SurrealDB, Apache Doris, file systems, Kafka
Key principle:
TargetState = Transform(SourceState)CocoIndex支持构建具备以下特性的数据管道:
- 自动处理增量更新:仅重新处理发生变更的数据
- 采用声明式目标状态:声明应存在的结果,而非更新的方式
- 支持所有Python类型:无需自定义领域特定语言(DSL)——可使用dataclasses、Pydantic、NamedTuple
- 提供函数记忆化:当输入/代码未变更时,跳过昂贵的操作
- 同步至多个目标系统:PostgreSQL、SQLite、LanceDB、Qdrant、SurrealDB、Apache Doris、文件系统、Kafka
核心原则:
TargetState = Transform(SourceState)When to Use This Skill
何时使用本技能
Use this skill when building pipelines that involve:
- Document processing: PDF/Markdown conversion, text extraction, chunking
- Vector embeddings: Embedding documents/code for semantic search
- Database transformations: ETL from source DB to target DB
- Knowledge graphs: Extract entities and relationships from data
- LLM-based extraction: Structured data extraction using LLMs
- File-based pipelines: Transform files from one format to another
- Incremental indexing: Keep search indexes up-to-date with source changes
- Streaming pipelines: Kafka-based real-time data processing
当构建涉及以下场景的管道时,使用本技能:
- 文档处理:PDF/Markdown转换、文本提取、分块
- 向量嵌入:为文档/代码生成嵌入用于语义搜索
- 数据库转换:从源数据库到目标数据库的ETL
- 知识图谱:从数据中提取实体与关系
- 基于LLM的提取:使用LLM提取结构化数据
- 基于文件的管道:将文件从一种格式转换为另一种
- 增量索引:保持搜索索引与源数据变更同步
- 流处理管道:基于Kafka的实时数据处理
Quick Start: Creating a New Project
快速开始:创建新项目
Initialize Project
初始化项目
bash
cocoindex init my-project
cd my-projectThis creates: , , . The generated sets the database location in its lifespan via .
main.pypyproject.tomlREADME.mdmain.pybuilder.settings.db_path = pathlib.Path("./cocoindex.db")bash
cocoindex init my-project
cd my-project此命令会创建:、、。生成的会在生命周期中通过设置数据库位置。
main.pypyproject.tomlREADME.mdmain.pybuilder.settings.db_path = pathlib.Path("./cocoindex.db")Add Dependencies
添加依赖
toml
undefinedtoml
undefinedFor vector embeddings with PostgreSQL
用于PostgreSQL的向量嵌入
dependencies = ["cocoindex>=1.0.0", "sentence-transformers", "asyncpg"]
dependencies = ["cocoindex>=1.0.0", "sentence-transformers", "asyncpg"]
For LLM extraction
用于LLM提取
dependencies = ["cocoindex>=1.0.0", "litellm", "instructor", "pydantic>=2.0"]
See [references/setup_project.md](references/setup_project.md) for complete examples.dependencies = ["cocoindex>=1.0.0", "litellm", "instructor", "pydantic>=2.0"]
完整示例请参考[references/setup_project.md](references/setup_project.md)。Run the Pipeline
运行管道
bash
uv run cocoindex update main.py # or: pip install -e . && cocoindex update main.pybash
uv run cocoindex update main.py # 或:pip install -e . && cocoindex update main.pyCore Concepts
核心概念
1. Apps
1. Apps
An App is the top-level executable that binds a main function with parameters:
python
import cocoindex as coco
@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
...
app = coco.App(
coco.AppConfig(name="MyApp"),
app_main,
sourcedir=pathlib.Path("./data"),
)App是顶级可执行单元,将主函数与参数绑定:
python
import cocoindex as coco
@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
...
app = coco.App(
coco.AppConfig(name="MyApp"),
app_main,
sourcedir=pathlib.Path("./data"),
)2. Functions (@coco.fn
)
@coco.fn2. Functions (@coco.fn
)
@coco.fnThe decorator marks functions as CocoIndex processing functions. Add to skip re-execution when inputs/code are unchanged:
@coco.fnmemo=Truepython
@coco.fn(memo=True)
async def expensive_operation(data: str) -> Result:
# LLM call, embedding generation, heavy computation
return await expensive_transform(data)Key parameters:
- -- Enable memoization (skip if inputs/code unchanged)
memo=True - -- Explicit version bump to force re-execution
version=1 - -- Auto-batch concurrent calls (async only)
batching=True - -- Serialize GPU-bound execution
runner=coco.GPU
@coco.fnmemo=Truepython
@coco.fn(memo=True)
async def expensive_operation(data: str) -> Result:
# LLM调用、嵌入生成、重型计算
return await expensive_transform(data)关键参数:
- -- 启用记忆化(输入/代码未变更时跳过)
memo=True - -- 显式版本升级以强制重新执行
version=1 - -- 自动批量处理并发调用(仅异步)
batching=True - -- 序列化GPU绑定执行
runner=coco.GPU
3. Processing Components
3. 处理组件
A processing component groups an item's processing with its target states.
Mount components with (preferred for lists) or :
mount_each()mount()python
undefined处理组件将项的处理逻辑与其目标状态分组。
使用(推荐用于列表)或挂载组件:
mount_each()mount()python
undefinedOne component per item (preferred for lists)
每个项对应一个组件(推荐用于列表)
await coco.mount_each(process_file, files.items(), target_table)
await coco.mount_each(process_file, files.items(), target_table)
Single component (subpath auto-derived from fn.name)
单个组件(子路径自动从fn.__name__派生)
await coco.mount(setup_fn, arg1)
await coco.mount(setup_fn, arg1)
Dependent component (blocks until result returned)
依赖组件(等待结果返回后执行)
result = await coco.use_mount(init_fn)
result = await coco.use_mount(init_fn)
Explicit subpath (when you need a specific path, e.g. in loops)
显式子路径(当需要特定路径时,例如循环中)
await coco.mount(coco.component_subpath("item", item_id), process_item, item)
**Key points:**
- All mount APIs are `async`
- `mount()`, `use_mount()`, and `mount_each()` auto-derive subpath from `fn.__name__`; optional explicit subpath as first arg
- Use `use_mount()` when you need the return value
- Use stable component paths for proper memoization and cleanupawait coco.mount(coco.component_subpath("item", item_id), process_item, item)
**关键点:**
- 所有挂载API均为`async`
- `mount()`、`use_mount()`和`mount_each()`自动从`fn.__name__`派生子路径;可选择将显式子路径作为第一个参数
- 需要返回值时使用`use_mount()`
- 使用稳定的组件路径以确保正确的记忆化与清理4. Target States
4. 目标状态
Declare what should exist -- CocoIndex handles creation/update/deletion:
python
undefined声明应存在的内容——CocoIndex会处理创建/更新/删除:
python
undefinedDatabase row target
数据库行目标
table.declare_row(row=MyRecord(id=1, name="example"))
table.declare_row(row=MyRecord(id=1, name="example"))
File target
文件目标
localfs.declare_file(outdir / "output.txt", content, create_parent_dirs=True)
localfs.declare_file(outdir / "output.txt", content, create_parent_dirs=True)
Kafka message target
Kafka消息目标
topic_target.declare_target_state(key="msg-1", value=json.dumps(data))
undefinedtopic_target.declare_target_state(key="msg-1", value=json.dumps(data))
undefined5. Context for Shared Resources
5. 共享资源上下文
Use to share expensive resources (DB connections, models) across components:
ContextKeypython
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
yield使用在组件间共享昂贵资源(数据库连接、模型):
ContextKeypython
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
yieldIn processing functions:
在处理函数中:
embedder = coco.use_context(EMBEDDER)
The `@coco.lifespan` decorator registers the function to the default CocoIndex environment, which is shared among all apps by default. `ContextKey` also serves as the stable identity for sources/targets -- the `key` string must remain stable across runs.embedder = coco.use_context(EMBEDDER)
`@coco.lifespan`装饰器将函数注册到默认的CocoIndex环境,该环境默认在所有App间共享。`ContextKey`同时作为源/目标的稳定标识——`key`字符串在多次运行中必须保持稳定。6. ID Generation
6. ID生成
Generate stable, unique identifiers that persist across incremental updates:
python
from cocoindex.resources.id import generate_id, IdGenerator生成稳定、唯一的标识符,在增量更新中保持持久:
python
from cocoindex.resources.id import generate_id, IdGeneratorDeterministic: same dep -> same ID
确定性:相同依赖 -> 相同ID
chunk_id = await generate_id(chunk.text)
chunk_id = await generate_id(chunk.text)
Always distinct: each call -> new ID, even with same dep
始终唯一:每次调用 -> 新ID,即使依赖相同
id_gen = IdGenerator()
for chunk in chunks:
chunk_id = await id_gen.next_id(chunk.text)
undefinedid_gen = IdGenerator()
for chunk in chunks:
chunk_id = await id_gen.next_id(chunk.text)
undefined7. Catch-Up vs Live Mode
7. 追赶模式 vs 实时模式
By default, runs in catch-up mode: it scans all sources, processes what changed since the last run (memoized components are skipped), syncs target states, and returns. Each call still has to scan sources to discover changes.
app.update()Live mode keeps the app running after catch-up and lets components stream changes continuously from their sources (e.g., file watcher, Kafka consumer), applying them with very low latency.
python
undefined默认情况下,运行在追赶模式:扫描所有源,处理自上次运行以来变更的内容(跳过记忆化组件),同步目标状态后返回。每次调用仍需扫描源以发现变更。
app.update()实时模式在追赶完成后保持App运行,允许组件从源持续流式获取变更(例如文件监视器、Kafka消费者),以极低延迟应用这些变更。
python
undefinedEnable live mode
启用实时模式
app.update_blocking(live=True)
app.update_blocking(live=True)
Or: cocoindex update main.py -L
或:cocoindex update main.py -L
Two things are needed: (1) enable live mode on the app, and (2) use a source that supports live updates.
- **`LiveMapView`** sources (e.g., `localfs.walk_dir(..., live=True)`) scan current state first, then watch for changes. They also work in catch-up mode -- write the pipeline once, choose mode at run time.
- **`LiveMapFeed`** sources (e.g., `kafka.topic_as_map()`) only stream changes with no initial snapshot. `mount_each()` auto-detects these and creates a live component internally.
```python
需要满足两个条件:(1) 在App上启用实时模式,(2) 使用支持实时更新的源。
- **`LiveMapView`**源(例如`localfs.walk_dir(..., live=True)`)先扫描当前状态,然后监视变更。它们也可在追赶模式下工作——只需编写一次管道,在运行时选择模式即可。
- **`LiveMapFeed`**源(例如`kafka.topic_as_map()`)仅流式传输变更,无初始快照。`mount_each()`会自动检测这些源并在内部创建实时组件。
```pythonLocalFS with live watching
启用实时监视的LocalFS
files = localfs.walk_dir(sourcedir, live=True, ...)
await coco.mount_each(process_file, files.items(), target)
files = localfs.walk_dir(sourcedir, live=True, ...)
await coco.mount_each(process_file, files.items(), target)
Kafka -- inherently live
Kafka——天生支持实时
items = kafka.topic_as_map(consumer, ["my-topic"])
await coco.mount_each(process_message, items, target)
undefineditems = kafka.topic_as_map(consumer, ["my-topic"])
await coco.mount_each(process_message, items, target)
undefinedCommon Pipeline Patterns
常见管道模式
Pattern 1: File Transformation
模式1:文件转换
python
import pathlib
import cocoindex as coco
from cocoindex.connectors import localfs
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
@coco.fn(memo=True)
async def process_file(file: FileLike, outdir: pathlib.Path) -> None:
content = await file.read_text()
transformed = transform_content(content)
outname = file.file_path.path.stem + ".out"
localfs.declare_file(outdir / outname, transformed, create_parent_dirs=True)
@coco.fn
async def app_main(sourcedir: pathlib.Path, outdir: pathlib.Path) -> None:
files = localfs.walk_dir(
sourcedir,
recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
)
await coco.mount_each(process_file, files.items(), outdir)
app = coco.App(
coco.AppConfig(name="Transform"),
app_main,
sourcedir=pathlib.Path("./data"),
outdir=pathlib.Path("./out"),
)python
import pathlib
import cocoindex as coco
from cocoindex.connectors import localfs
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
@coco.fn(memo=True)
async def process_file(file: FileLike, outdir: pathlib.Path) -> None:
content = await file.read_text()
transformed = transform_content(content)
outname = file.file_path.path.stem + ".out"
localfs.declare_file(outdir / outname, transformed, create_parent_dirs=True)
@coco.fn
async def app_main(sourcedir: pathlib.Path, outdir: pathlib.Path) -> None:
files = localfs.walk_dir(
sourcedir,
recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]),
)
await coco.mount_each(process_file, files.items(), outdir)
app = coco.App(
coco.AppConfig(name="Transform"),
app_main,
sourcedir=pathlib.Path("./data"),
outdir=pathlib.Path("./out"),
)Pattern 2: Vector Embedding Pipeline
模式2:向量嵌入管道
python
import pathlib
from dataclasses import dataclass
from typing import AsyncIterator, Annotated
import asyncpg
from numpy.typing import NDArray
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.chunk import Chunk
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
from cocoindex.resources.id import IdGenerator
DATABASE_URL = "postgres://cocoindex:cocoindex@localhost/cocoindex"
PG_DB = coco.ContextKey[asyncpg.Pool]("pg_db")
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
_splitter = RecursiveSplitter()
@dataclass
class DocEmbedding:
id: int
filename: str
text: str
embedding: Annotated[NDArray, EMBEDDER] # Dimensions inferred from ContextKey
chunk_start: int
chunk_end: int
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
builder.provide(PG_DB, pool)
builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
yield
@coco.fn
async def process_chunk(
chunk: Chunk, filename: pathlib.PurePath,
id_gen: IdGenerator, table: postgres.TableTarget[DocEmbedding],
) -> None:
table.declare_row(row=DocEmbedding(
id=await id_gen.next_id(chunk.text),
filename=str(filename),
text=chunk.text,
embedding=await coco.use_context(EMBEDDER).embed(chunk.text),
chunk_start=chunk.start.char_offset,
chunk_end=chunk.end.char_offset,
))
@coco.fn(memo=True)
async def process_file(file: FileLike, table: postgres.TableTarget[DocEmbedding]) -> None:
text = await file.read_text()
chunks = _splitter.split(text, chunk_size=2000, chunk_overlap=500)
id_gen = IdGenerator()
await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)
@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
target_table = await postgres.mount_table_target(
PG_DB,
table_name="embeddings",
table_schema=await postgres.TableSchema.from_class(DocEmbedding, primary_key=["id"]),
)
target_table.declare_vector_index(column="embedding")
files = localfs.walk_dir(sourcedir, recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]))
await coco.mount_each(process_file, files.items(), target_table)
app = coco.App(coco.AppConfig(name="Embedding"), app_main, sourcedir=pathlib.Path("./data"))python
import pathlib
from dataclasses import dataclass
from typing import AsyncIterator, Annotated
import asyncpg
from numpy.typing import NDArray
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.chunk import Chunk
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
from cocoindex.resources.id import IdGenerator
DATABASE_URL = "postgres://cocoindex:cocoindex@localhost/cocoindex"
PG_DB = coco.ContextKey[asyncpg.Pool]("pg_db")
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
_splitter = RecursiveSplitter()
@dataclass
class DocEmbedding:
id: int
filename: str
text: str
embedding: Annotated[NDArray, EMBEDDER] # 维度从ContextKey自动推断
chunk_start: int
chunk_end: int
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
builder.provide(PG_DB, pool)
builder.provide(EMBEDDER, SentenceTransformerEmbedder("all-MiniLM-L6-v2"))
yield
@coco.fn
async def process_chunk(
chunk: Chunk, filename: pathlib.PurePath,
id_gen: IdGenerator, table: postgres.TableTarget[DocEmbedding],
) -> None:
table.declare_row(row=DocEmbedding(
id=await id_gen.next_id(chunk.text),
filename=str(filename),
text=chunk.text,
embedding=await coco.use_context(EMBEDDER).embed(chunk.text),
chunk_start=chunk.start.char_offset,
chunk_end=chunk.end.char_offset,
))
@coco.fn(memo=True)
async def process_file(file: FileLike, table: postgres.TableTarget[DocEmbedding]) -> None:
text = await file.read_text()
chunks = _splitter.split(text, chunk_size=2000, chunk_overlap=500)
id_gen = IdGenerator()
await coco.map(process_chunk, chunks, file.file_path.path, id_gen, table)
@coco.fn
async def app_main(sourcedir: pathlib.Path) -> None:
target_table = await postgres.mount_table_target(
PG_DB,
table_name="embeddings",
table_schema=await postgres.TableSchema.from_class(DocEmbedding, primary_key=["id"]),
)
target_table.declare_vector_index(column="embedding")
files = localfs.walk_dir(sourcedir, recursive=True,
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.md"]))
await coco.mount_each(process_file, files.items(), target_table)
app = coco.App(coco.AppConfig(name="Embedding"), app_main, sourcedir=pathlib.Path("./data"))Pattern 3: LLM-Based Extraction
模式3:基于LLM的提取
python
import instructor
from pydantic import BaseModel
from litellm import acompletion
_instructor_client = instructor.from_litellm(acompletion, mode=instructor.Mode.JSON)
class ExtractionResult(BaseModel):
title: str
topics: list[str]
@coco.fn(memo=True) # Memo avoids re-calling LLM
async def extract_and_store(content: str, message_id: int, table) -> None:
result = await _instructor_client.chat.completions.create(
model="gpt-4",
response_model=ExtractionResult,
messages=[{"role": "user", "content": f"Extract topics: {content}"}],
)
table.declare_row(row=Message(id=message_id, title=result.title, content=content))python
import instructor
from pydantic import BaseModel
from litellm import acompletion
_instructor_client = instructor.from_litellm(acompletion, mode=instructor.Mode.JSON)
class ExtractionResult(BaseModel):
title: str
topics: list[str]
@coco.fn(memo=True) # 记忆化避免重复调用LLM
async def extract_and_store(content: str, message_id: int, table) -> None:
result = await _instructor_client.chat.completions.create(
model="gpt-4",
response_model=ExtractionResult,
messages=[{"role": "user", "content": f"Extract topics: {content}"}],
)
table.declare_row(row=Message(id=message_id, title=result.title, content=content))Connectors and Operations
连接器与操作
CocoIndex provides connectors for reading from and writing to external systems:
| Connector | Source | Target | Vectors | Use Case |
|---|---|---|---|---|
| PostgreSQL | Y | Y | pgvector | Production SQL + vectors |
| SQLite | - | Y | sqlite-vec | Local SQL + vectors |
| LanceDB | - | Y | Y | Cloud-native vector DB |
| Qdrant | - | Y | Y | Specialized vector DB |
| SurrealDB | - | Y | Y | Graph + document DB |
| Apache Doris | - | Y | Y | Analytical DB + vectors |
| LocalFS | Y | Y | N/A | File-based pipelines |
| Amazon S3 | Y | - | N/A | Cloud object storage |
| Kafka | Y | Y | N/A | Streaming pipelines |
| Google Drive | Y | - | N/A | Cloud file source |
For detailed connector documentation, see references/connectors.md.
CocoIndex提供用于读取和写入外部系统的连接器:
| 连接器 | 源 | 目标 | 向量 | 适用场景 |
|---|---|---|---|---|
| PostgreSQL | 是 | 是 | pgvector | 生产级SQL+向量 |
| SQLite | - | 是 | sqlite-vec | 本地SQL+向量 |
| LanceDB | - | 是 | 是 | 云原生向量数据库 |
| Qdrant | - | 是 | 是 | 专业向量数据库 |
| SurrealDB | - | 是 | 是 | 图+文档数据库 |
| Apache Doris | - | 是 | 是 | 分析型数据库+向量 |
| LocalFS | 是 | 是 | N/A | 基于文件的管道 |
| Amazon S3 | 是 | - | N/A | 云对象存储 |
| Kafka | 是 | 是 | N/A | 流处理管道 |
| Google Drive | 是 | - | N/A | 云文件源 |
详细连接器文档请参考references/connectors.md。
Text and Embedding Operations
文本与嵌入操作
Text Splitting
文本分块
python
from cocoindex.ops.text import RecursiveSplitter, detect_code_language
splitter = RecursiveSplitter()
language = detect_code_language(filename="example.py")
chunks = splitter.split(text, chunk_size=1000, chunk_overlap=200, language=language)python
from cocoindex.ops.text import RecursiveSplitter, detect_code_language
splitter = RecursiveSplitter()
language = detect_code_language(filename="example.py")
chunks = splitter.split(text, chunk_size=1000, chunk_overlap=200, language=language)Embeddings
嵌入
python
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder
embedder = SentenceTransformerEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedding = await embedder.embed(text) # Returns NDArraypython
from cocoindex.ops.sentence_transformers import SentenceTransformerEmbedder
embedder = SentenceTransformerEmbedder("sentence-transformers/all-MiniLM-L6-v2")
embedding = await embedder.embed(text) # 返回NDArrayCLI Commands
CLI命令
bash
cocoindex init my-project # Create new project
cocoindex update main.py # Run app
cocoindex update main.py:my_app # Run specific app
cocoindex update main.py -L # Run in live mode (continuous)
cocoindex update main.py --full-reprocess # Reprocess everything
cocoindex drop main.py [-f] # Drop and reset all state
cocoindex ls [main.py] # List apps
cocoindex show main.py [--tree] # Show component pathsbash
cocoindex init my-project # 创建新项目
cocoindex update main.py # 运行App
cocoindex update main.py:my_app # 运行指定App
cocoindex update main.py -L # 以实时模式运行(持续执行)
cocoindex update main.py --full-reprocess # 重新处理所有内容
cocoindex drop main.py [-f] # 删除并重置所有状态
cocoindex ls [main.py] # 列出App
cocoindex show main.py [--tree] # 显示组件路径Best Practices
最佳实践
1. Use @coco.fn
on All Processing Functions
@coco.fn1. 为所有处理函数使用@coco.fn
@coco.fnEvery function that participates in the pipeline (declares target states, calls mount APIs, etc.) must be decorated with .
@coco.fn所有参与管道的函数(声明目标状态、调用挂载API等)都必须用装饰。
@coco.fn2. Add Memoization for Expensive Operations
2. 为昂贵操作添加记忆化
python
@coco.fn(memo=True) # Skip re-execution when inputs/code unchanged
async def process_chunk(chunk, table):
embedding = await embedder.embed(chunk.text) # Expensive!
table.declare_row(...)python
@coco.fn(memo=True) # 输入/代码未变更时跳过重新执行
async def process_chunk(chunk, table):
embedding = await embedder.embed(chunk.text) # 操作昂贵!
table.declare_row(...)3. Use Stable Component Paths
3. 使用稳定的组件路径
python
undefinedpython
undefinedGood: Stable identifiers
推荐:稳定标识符
coco.component_subpath("file", str(file.file_path.path))
coco.component_subpath("record", record.id)
coco.component_subpath("file", str(file.file_path.path))
coco.component_subpath("record", record.id)
Bad: Unstable identifiers
不推荐:不稳定标识符
coco.component_subpath("file", file) # Object reference
coco.component_subpath("idx", idx) # Index changes
undefinedcoco.component_subpath("file", file) # 对象引用
coco.component_subpath("idx", idx) # 索引会变化
undefined4. Use Context for Shared Resources
4. 使用上下文共享资源
python
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
builder.provide(PG_DB, pool)
yieldpython
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
async with await asyncpg.create_pool(DATABASE_URL) as pool:
builder.provide(PG_DB, pool)
yield5. Use Annotated[NDArray, CONTEXT_KEY]
for Vectors
Annotated[NDArray, CONTEXT_KEY]5. 为向量使用Annotated[NDArray, CONTEXT_KEY]
Annotated[NDArray, CONTEXT_KEY]python
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@dataclass
class Record:
vector: Annotated[NDArray, EMBEDDER] # Auto-infer dimensions from ContextKeypython
EMBEDDER = coco.ContextKey[SentenceTransformerEmbedder]("embedder")
@dataclass
class Record:
vector: Annotated[NDArray, EMBEDDER] # 从ContextKey自动推断维度6. Use Convenience APIs for Targets
6. 为目标使用便捷API
python
undefinedpython
undefinedMount table target -- subpath is automatic
挂载表目标——子路径自动生成
table = await postgres.mount_table_target(
PG_DB,
table_name="my_table",
table_schema=await postgres.TableSchema.from_class(MyRecord, primary_key=["id"]),
)
undefinedtable = await postgres.mount_table_target(
PG_DB,
table_name="my_table",
table_schema=await postgres.TableSchema.from_class(MyRecord, primary_key=["id"]),
)
undefinedTroubleshooting
故障排除
Everything Reprocessing
所有内容都在重新处理
Add to expensive functions:
memo=Truepython
@coco.fn(memo=True) # Add this
async def process_item(item):
...为昂贵函数添加:
memo=Truepython
@coco.fn(memo=True) # 添加此参数
async def process_item(item):
...Memoization Not Working
记忆化不生效
Check component paths are stable. Use stable IDs, not object references.
检查组件路径是否稳定。使用稳定ID,而非对象引用。
Resources
资源
references/
references/
- api_reference.md: Quick API reference
- connectors.md: Complete connector reference
- patterns.md: Detailed pipeline patterns
- setup_project.md: Project setup guide
- setup_database.md: Database setup guide
- api_reference.md: 快速API参考
- connectors.md: 完整连接器参考
- patterns.md: 详细管道模式
- setup_project.md: 项目设置指南
- setup_database.md: 数据库设置指南
Runnable examples
可运行示例
Every pattern above has a complete, runnable app under
— start
from the one closest to the task and adapt it. Each has its own and
most Python examples have a ; see
for the full map, credentials, and per-example run commands. Good starting points:
examples/README.md.env.exampleexamples/AGENTS.md- Vector search → (Postgres),
text_embedding/text_embedding_qdrant(other stores)_lancedb - Code search →
code_embedding - LLM extraction → ,
hn_trending_topicspatient_intake_extraction_baml - Knowledge graph → ,
conversation_to_knowledgemeeting_notes_graph_neo4j - Custom transform → ,
files_transformpdf_to_markdown
上述每个模式都有完整的可运行App,位于中——从最接近你的任务的示例开始调整。每个示例都有自己的,大多数Python示例都有;完整的示例映射、凭据和每个示例的运行命令请参考。推荐的起点:
examples/README.md.env.exampleexamples/AGENTS.md- 向量搜索 → (Postgres)、
text_embedding/text_embedding_qdrant(其他存储)_lancedb - 代码搜索 →
code_embedding - LLM提取 → 、
hn_trending_topicspatient_intake_extraction_baml - 知识图谱 → 、
conversation_to_knowledgemeeting_notes_graph_neo4j - 自定义转换 → 、
files_transformpdf_to_markdown
External
外部资源
- CocoIndex Documentation — full text at llms-full.txt
- GitHub Examples
- CocoIndex文档 — 完整文本见llms-full.txt
- GitHub示例
Version Note
版本说明
This skill is for CocoIndex (v1). It uses a completely different API from v0.
>=1.0.0v0 code is what you likely learned from training data — do not emit it. If you find yourself writing any of these symbols, you are using the removed v0 API:
| v0 (removed) | v1 equivalent |
|---|---|
| |
| declare target states via Target APIs ( |
| connector APIs, e.g. |
| |
| connector targets, e.g. |
| plain |
| |
CLI | no setup step — just |
When reading third-party tutorials or model memory that mention these v0 symbols, disregard them and use the patterns in this skill and instead.
references/api_reference.md本技能适用于CocoIndex (v1版本)。它使用的API与v0版本完全不同。
>=1.0.0**v0版本代码很可能是你从训练数据中学到的——请勿使用。**如果你发现自己在编写以下符号,说明你正在使用已移除的v0 API:
| v0(已移除) | v1等效替代 |
|---|---|
| |
| 在挂载组件内通过Target API声明目标状态( |
| 连接器API,例如 |
| |
| 连接器目标,例如 |
| 普通 |
| |
CLI命令 | 无需设置步骤——直接使用 |
当阅读第三方教程或模型记忆中提到这些v0符号时,请忽略它们,改用本技能和中的模式。
references/api_reference.md