SuperIndex
Vectorless, page-cited question answering over long financial reports: Azure Document Intelligence Markdown → tree index + BM25 → LLM agent.
superindex 面向上市公司年报、中期报告这类长篇、表格密集的财报问答:
- 建库:把 Azure Document Intelligence(DI)产出的 Markdown(或任意带
#标题的 Markdown)按标题建成层级目录树,可选用 LLM 为节点写摘要;同时建 BM25 关键词索引。 - 问答:LLM agent 先拿到 BM25 预取的候选页,再沿目录树决定打开哪些节点、读哪几页,最后作答。
- 无向量库:不做 embedding,不需要向量数据库;每个答案都能追溯到页码(DI 注入的
<!-- page: N -->页标记)。 - 模型通过 LiteLLM 接入:OpenAI 兼容网关、Azure OpenAI、Ollama、OpenAI / Anthropic / DeepSeek 等均可,模型需支持 tool calling。
安装
需要 Python 3.11–3.13。
pip install superindex
# 或者作为独立命令行工具安装(推荐,自动隔离环境)
uv tool install superindex
装好后直接使用 superindex 命令:
superindex --help
superindex index|search|ask|serve|batch --help
最小配置(.env)
在当前工作目录放一个 .env(已存在的环境变量优先)。完整模板见
.env.example。
公司内网 OpenAI 兼容网关(vLLM、各类代理等,注意 /v1 后缀):
SUPERINDEX_BASE_URL=https://your-gateway.example.com/v1
SUPERINDEX_API_KEY_OVERRIDE=your-gateway-key
SUPERINDEX_INDEX_MODEL=openai/your-model-name
SUPERINDEX_CHAT_MODEL=openai/your-model-name
SUPERINDEX_REASONING_EFFORT=
本机 Ollama(先 ollama pull qwen2.5:7b,并调大上下文 OLLAMA_CONTEXT_LENGTH=32768):
SUPERINDEX_INDEX_MODEL=ollama_chat/qwen2.5:7b
SUPERINDEX_CHAT_MODEL=ollama_chat/qwen2.5:7b
SUPERINDEX_BASE_URL=http://localhost:11434
SUPERINDEX_API_KEY_OVERRIDE=ollama
# 非推理模型必须留空,否则报 "does not support thinking"
SUPERINDEX_REASONING_EFFORT=
没有配置模型时不会回落到任何云端模型,命令会直接报错并提示该设置哪个变量。
用法
建库:index
superindex index report.md --no-summary # 单个文件,不调 LLM,几秒建完
superindex index ./corpus_md --no-summary # 整个目录(递归查找 .md)
superindex index ./corpus_md # 带 LLM 节点摘要(--concurrency 默认 8)
superindex index ./corpus_md --force # 内容未变也强制重建
superindex index ./corpus_md --store ./my_store
- 文档库默认在当前工作目录下的
superindex_store/;用--store或SUPERINDEX_STORE覆盖。之后的search/ask/serve/batch要用同一个 store。 - 建议先
--no-summary+search自检(不花钱),跑通问答后再带摘要重建。 - Windows 路径同理,如
superindex index D:\corpus_md --store D:\si_store。
检索自检:search(BM25,不调 LLM)
superindex search "final dividend" --top-k 3
superindex search "末期股息 2022" --doc HarbourLife --match passage --json
--match page(默认)按整页打分;passage 按页内小段(约 300–800 字符,表格按行切分并重复表头)打分,仍返回整页,长页多主题时可能更好。
问答:ask
superindex ask "港湾人寿 2022 年新加坡的新业务价值是多少?" -v
superindex ask "..." --doc HarbourLife # 限定文档(名称、id 或名称片段,可重复)
superindex ask "..." --instructions "只用中文回答,数字保留原单位。"
superindex ask "..." --instructions-file ./instructions.txt
-v把预取的候选页和每次工具调用打印到 stderr,便于排查"答非所问"。- 检索预取(默认开):问题进入 agent 前先跑 BM25,把 top-k 候选页(文档、页码、章节、片段)作为线索附在问题前,模型即使不主动调用
search_pages也能从可能的页开始。--no-prefetch关闭,--prefetch-k N调整条数(默认 5)。
网页:serve
superindex serve --port 8787 # 浏览器打开 http://127.0.0.1:8787
superindex serve --host 0.0.0.0 --port 8787 # 局域网访问(注意防火墙)
「SuperIndex 财报问答」网页:勾选可用文档范围、流式输出答案,每条答案可展开查看模型的思考过程与全部工具调用(读了哪棵树、哪几页)。serve 与 ask 接受相同的 --instructions / --match / --prefetch / --page-image / 模型参数。
批量问答:batch
superindex batch questions.jsonl
superindex batch questions.csv --concurrency 2 --timeout 300
superindex batch questions.jsonl --doc HarbourLife --limit 5
superindex batch questions.csv --resume # 续跑最近一次,跳过已完成的题
superindex batch questions.jsonl --retrieval-only --match page # 纯检索评测,不调 LLM
superindex batch questions.jsonl --retrieval-only --match passage
题集格式:
| 扩展名 | 格式 |
|---|---|
.txt |
每行一题,# 开头为注释 |
.jsonl |
每行 {"id": "Q1", "question": "...", "expected": "...", "doc": "文档名片段"},只有 question 必填 |
.csv |
UTF-8,表头至少有 question,可选 expected、doc、id |
.json |
题目列表(仓库 scripts/questions.json 的格式) |
结果默认写到 <当前目录>/results/batch/<时间戳>/(--out 可改):summary.md(总览表 + 逐题问答与工具调用)和 results.jsonl(逐题完整记录)。"命中"是粗评分(期望答案里的数字全部出现在回答中),需要人工复核。
--retrieval-only 只跑每题的 BM25 检索,按 top-k(默认 5)页是否含期望答案计算 recall@k、MRR,秒级完成,适合比较 --match page|passage。
回答指令(ask / serve / batch 通用)
三个命令使用同一套"常驻指令",替换内置默认。优先级从高到低:
--instructions "文本"--instructions-file 路径(UTF-8 文本文件)- 环境变量
SUPERINDEX_INSTRUCTIONS(直接文本) - 环境变量
SUPERINDEX_INSTRUCTIONS_FILE(文件路径) - 内置中性默认:财务分析助手,按文档给出准确的数字、单位与报告期,文档没有答案时明确说明而不是猜测。
页码与附图
- 页码引用:DI Markdown 中的
<!-- page: N -->页标记在建库时保留,答案引用具体页码;没有页标记的 Markdown 按--page-chars(默认 4000 字符)切伪页。 - 关联源 PDF:
superindex index ./corpus_md --pdf-dir ./corpus_pdf(或SUPERINDEX_PDF_DIR)按同名文件(年报.md↔年报.pdf,子目录递归)关联 PDF;已有的库再跑一次只补关联,不重建文本。 - 多模态附图(默认关,仅适用于能看图的模型):
--page-image off|auto|always(SUPERINDEX_PAGE_IMAGE)。auto:预取候选页中含表格、图或文字很少(扫描页、图表页)的页附截图;always:候选页都附。- 两种模式下 agent 还能调用
get_page_image(doc_name, page)按需取图。 - 每题最多
SUPERINDEX_PAGE_IMAGE_MAX张(默认 3),长边SUPERINDEX_PAGE_IMAGE_MAX_SIDE像素(默认 1600)。每张约 1–2.5K 输入 token,建议先在题集上对比off的命中率和成本。
数值计算:calculate
agent 带一个 calculate 工具,基于 avada-eval 0.1.1+(安全的算式求值,不执行任意代码;Decimal 精确计算,含 pct_change / cagr / ratio 等财务函数与具名变量)。系统提示要求增长率、占比、差额、单位换算等一律经由它计算。千分位写作 1,234(逗号两侧无空格),函数参数的逗号后加空格。
配置变量
| 变量 | 说明 |
|---|---|
SUPERINDEX_CHAT_MODEL |
问答模型(LiteLLM 模型名),ask / serve / batch 必填 |
SUPERINDEX_INDEX_MODEL |
节点摘要模型;index --no-summary 时不用 |
SUPERINDEX_BASE_URL |
OpenAI 兼容网关或 Ollama 地址 |
SUPERINDEX_API_KEY_OVERRIDE |
上面地址对应的 key |
SUPERINDEX_REASONING_EFFORT |
推理强度(如 low);不设或留空则不发送,非推理模型必须留空 |
SUPERINDEX_STORE |
文档库目录,默认 ./superindex_store |
SUPERINDEX_INSTRUCTIONS / SUPERINDEX_INSTRUCTIONS_FILE |
回答指令(见上文) |
SUPERINDEX_BM25_MATCH |
page(默认)/ passage |
SUPERINDEX_PREFETCH / SUPERINDEX_PREFETCH_K |
检索预取开关(默认 1)/ 条数(默认 5) |
SUPERINDEX_PDF_DIR |
源 PDF 目录 |
SUPERINDEX_PAGE_IMAGE / _MAX / _MAX_SIDE |
附图模式 / 每题张数 / 长边像素 |
命令行参数(--chat-model、--index-model、--base-url、--api-key、--reasoning-effort 等)优先于环境变量。Azure OpenAI、代理与自签证书等写法见 .env.example。
旧名兼容:早期版本的 PAGEINDEX_INDEX_MODEL / PAGEINDEX_CHAT_MODEL / PAGEINDEX_BASE_URL / PAGEINDEX_API_KEY_OVERRIDE / PAGEINDEX_REASONING_EFFORT 仍然有效(新名优先),使用旧名时会在 stderr 提示一次已更名,请改为对应的 SUPERINDEX_*。
许可与来源
MIT 许可。检索引擎(superindex.engine)衍生自 VectifyAI/PageIndex(MIT,v0.2.10,commit 71714e8),并在此基础上改名、裁剪与扩展。版权与来源说明见
LICENSE、
NOTICE 与
docs/engine/UPSTREAM.md。
开发者 / 仓库用法
源码:https://github.com/VoldemortGin/SuperIndex
环境
用 uv 管理 Python 与依赖:
git clone https://github.com/VoldemortGin/SuperIndex.git
cd SuperIndex
uv sync # 建 .venv,superindex 以 editable 方式安装,并装 dev、build 依赖组
uv run superindex --help # 或:uv run scripts/superindex.py --help
uv run pytest tests -q
- 依赖组只有
dev(pytest、ruff)和build(PyInstaller);只要运行时依赖:uv sync --no-default-groups。 - Windows 上的完整上手步骤(含公司 LLM 网关、代理/证书、Ollama):docs/windows-quickstart.md。
- 仓库自带样例:
uv run superindex index samples/test_corpus_long/HarbourLife_AR2022.md --no-summary,然后uv run superindex ask "港湾人寿董事会建议的末期股息是每股多少?" -v(港湾人寿 Harbour Life 为虚构公司)。
目录结构
.
├── superindex/
│ ├── cli.py, __main__.py # superindex 命令入口
│ ├── engine/ # 树索引 + agent 检索引擎(衍生自 PageIndex)
│ ├── extractors/ # PDF 抽取后端:Azure DI(REST)/ 文本层
│ ├── nav/ # 两级导航:语料目录 → 文档 → 章节
│ ├── webapp/ # serve 的网页服务与 static/
│ └── bm25.py, prefetch.py, calc.py, page_images.py, batch.py, ...
├── scripts/ # 实验与辅助脚本(superindex.py、06_azure_extract.py 等)
├── samples/ # 样例 Markdown 与题集
├── tests/ # 离线测试
├── packaging/ # PyInstaller 打包脚本
└── docs/ # quickstart、交接文档、engine 来源说明
打包为免 Python 可执行程序(PyInstaller)
bash packaging/build_macos.sh # macOS
powershell -ExecutionPolicy Bypass -File packaging\build_windows.ps1 # Windows
冻结版的文档库默认在 exe 同目录的 superindex_store/,.env 先找当前工作目录再找 exe 目录。离线部署、onedir/onefile 取舍等见 packaging/README.md。
PDF → Markdown:Azure Document Intelligence
superindex index 只读 Markdown。PDF 先用 Azure DI(prebuilt-layout)转成 Markdown:表格保留为真正的 Markdown 表格、扫描页做 OCR,并在页边界注入 <!-- page: N --> 页标记。在 .env 设置 AZURE_DI_ENDPOINT 与 AZURE_DI_KEY 后:
# 检查配置(并只分析一份 PDF 的第 1 页做冒烟测试;--only 按文件名片段筛选)
uv run scripts/06_azure_extract.py ./pdfs --check --only AR2022
# 把 PDF 抽成 Markdown 落盘,再建库
uv run scripts/06_azure_extract.py ./pdfs --out ./corpus_md
uv run superindex index ./corpus_md --pdf-dir ./pdfs
与 PDF 自带文本层(PyPDF2)相比:
| 文本层 | Azure Document Intelligence | |
|---|---|---|
| 成本 | 免费 | 按页计费 |
| 表格 | 糊掉——图表页抽成 175230,两个数粘在一起,标签与数值的对应丢失 |
真正的 Markdown 表格 |
| 扫描件 / 纯图片 PDF | 直接拒绝(无文本层、无 OCR) | OCR |
| 正文段落 | 好 | 好 |
| 页码锚点 | 原生(页范围) | 注入 <!-- page: N --> |
- 配置了 Azure 但调用失败时直接报错停下,不会静默退回文本层(否则写错 key 会悄悄改变索引质量);设
AZURE_DI_FALLBACK=1才退回。 AZURE_DI_STRING_INDEX_TYPE必须保持unicodeCodePoint,页标记注入依赖字符偏移与 Python 字符串下标对齐。superindex/extractors/azure_di.py是基于 httpx 的纯 REST 调用,不依赖 Azure SDK;superindex/extractors/backend.py决定当前使用哪个抽取器。其余AZURE_DI_*选项见.env.example。
两级导航(nav)
面向"多级目录 + 上千文件"语料的检索层,把树的粒度扩展为「语料目录 → 文档 → 章节」:先在目录树里定位文件,再在文件的章节树里定位章节。
uv run python -m superindex.nav.build ./corpus_md --out ./corpus_index --summarize-files
uv run python -m superindex.nav.route ./corpus_index "港湾人寿 2022 年的每股股息是多少?" --show-content
配置 Azure DI 后 superindex.nav.build 也可直接吃 PDF(启动时打印所用抽取器,--extractor {auto,azure-di,text-layer} 可强制指定)。详见 superindex/nav/README.md。
Metadata
Release files for superindex 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| superindex-0.1.1.tar.gz | 573.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| superindex-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.2 MB
Release files / superindex-0.1.1.tar.gz
| Download URL | superindex-0.1.1.tar.gz |
|---|---|
| Size | 573.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cbd98f8a62e9d17df76d60dec68dbef342034a21b56e38a0d0c9c4d3586a515e
|
|
BLAKE2b-256 checksum How to use checksums |
308d55931a89216903943acd99207f41152cd313eedf7613e4b2e94edf955ebd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|
Release files / superindex-0.1.1-py3-none-any.whl
| Download URL | superindex-0.1.1-py3-none-any.whl |
|---|---|
| Size | 649.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
884c3df702f9e8f21d5cf1dd0034daeca32650889e094cc70235c3ea8a208583
|
|
BLAKE2b-256 checksum How to use checksums |
36628a9415f00dab1606fab648131258e8ed992b8c41e8ac8b2fc008404f5a67
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|