---
name: file-indexer
description: Index files into vector database for semantic search.
always: false
---

# File Indexer

将文件内容分词并存储到向量数据库，实现语义搜索。

## 使用场景

当用户说：
- "帮我把这个文件存入向量库"
- "索引这个文档"
- "让我可以搜索这个文件的内容"
- 上传了一个文件并要求建立索引

## 使用方法

### 步骤 1: 读取文件内容

使用 `read_file` 工具读取用户指定的文件：

```
read_file <filepath>
```

### 步骤 2: 语义分块（推荐）

使用“按句子切分 + token 预算 + 重叠句子”的方式分块，优于固定字符窗口。

```python
import re

def estimate_tokens(text: str) -> int:
    """粗略估算 token 数（中文按字，英文按词）。"""
    zh_chars = len(re.findall(r"[\u4e00-\u9fff]", text))
    en_words = len(re.findall(r"[A-Za-z0-9_]+", text))
    return zh_chars + en_words

def split_sentences(text: str) -> list[str]:
    """中英文句子切分，保留标点。"""
    parts = re.split(r"(?<=[。！？!?\.])\s+|(?<=[。！？!?\.])", text)
    return [p.strip() for p in parts if p and p.strip()]

def chunk_text_semantic(
    text: str,
    target_tokens: int = 380,
    max_tokens: int = 450,
    overlap_sentences: int = 1,
) -> list[str]:
    """按句子聚合为块，控制 token 预算，并保留少量上下文重叠。"""
    sents = split_sentences(text)
    chunks: list[str] = []
    cur: list[str] = []
    cur_tokens = 0

    for sent in sents:
        t = estimate_tokens(sent)
        # 超长句：单独成块，避免丢失
        if t > max_tokens:
            if cur:
                chunks.append("".join(cur))
                cur, cur_tokens = [], 0
            chunks.append(sent)
            continue

        if cur and (cur_tokens + t > target_tokens or cur_tokens > max_tokens):
            chunks.append("".join(cur))
            overlap = cur[-overlap_sentences:] if overlap_sentences > 0 else []
            cur = overlap + [sent]
            cur_tokens = estimate_tokens("".join(cur))
        else:
            cur.append(sent)
            cur_tokens += t

    if cur:
        chunks.append("".join(cur))
    return chunks
```

### 步骤 3: 存储到向量数据库

使用以下 Python 代码存储到向量库：

```python
from pathlib import Path
from chromadb import PersistentClient
import uuid
from datetime import datetime

# 获取向量库客户端
workspace = Path.home() / ".miobot" / "workspace"
client = PersistentClient(path=str(workspace / "vector_store"))
collection = client.get_or_create_collection("doc_knowledge", metadata={"hnsw:space": "cosine"})

# 添加分块结果（语义分块）
chunks = chunk_text_semantic(
    file_content,
    target_tokens=380,
    max_tokens=450,
    overlap_sentences=1,
)
for i, chunk in enumerate(chunks):
    collection.add(
        ids=[f"file_{Path(filename).stem}_{i}_{uuid.uuid4().hex[:6]}"],
        documents=[chunk],
        metadatas=[{
            "type": "indexed_file",
            "filename": filename,
            "chunk_index": i,
            "total_chunks": len(chunks),
            "timestamp": datetime.now().isoformat()
        }]
    )

print(f"已索引 {len(chunks)} 个片段到向量数据库")
```

硬规则：
- 文档/书籍索引必须写入 `doc_knowledge`
- 不要按文件名、书名动态创建 collection（例如 `book_xxx`）

### 步骤 4: 确认完成

告诉用户：
- 索引了多少个片段
- 文件名是什么
- 现在可以语义搜索文件内容了

## 搜索已索引的文件

用户可以这样搜索：

```
帮我搜索一下那个 Python 教程文件里关于函数的内容
```

Agent 会自动使用向量搜索来查找相关内容。

## 注意事项

- 支持 .txt, .md, .py, .json, .yaml 等文本文件
- 如果是 PDF 或 Word 文档，先尝试提取文本
- 大文件会自动分割成多个片段存储
- 推荐每块约 350-450 tokens，默认重叠 1 句以保持上下文连贯
