500 家中国大陆金融机构 · 公开信息检索问答系统。小数据量、单机、可落地的轻量方案。
// 选型先做减法
500 家机构、即使每家几页资料,全部文本也就 几 MB 到几十 MB。这个量级用分布式数据库属于杀鸡用牛刀——部署、运维、资源占用都不划算。单台 VPS 配一个本地向量库就完全够用,且零运维成本。
| 组件 | 选择 | 理由 |
|---|---|---|
| 向量库 | ChromaDB | 纯本地、零运维,小规模绰绰有余;若已有 Postgres 可换 pgvector |
| Embedding | bge-large-zh | 中文金融文本效果好;嫌本地慢可改用通义 / OpenAI 嵌入 API |
| LLM | API 调用 | DeepSeek / 通义 / OpenAI;VPS 不本地跑大模型 |
| 框架 | LlamaIndex | 把爬取 → 切分 → 嵌入 → 检索 → 生成串成一条流水线 |
| 接口 | FastAPI | 对外暴露一个查询端点 |
// pipeline overview
// 先读这一段,再写爬虫
只爬取官网与公开监管披露(金融监管总局、证监会、央行等公示信息);遵守目标站点的 robots.txt;控制请求频率;不爬登录后 / 付费 / 明确禁止抓取的内容;不采集个人隐私数据。优先使用官方公开数据源或开放接口,比公开爬取更稳妥。
// dependencies
pip install chromadb sentence-transformers llama-index \
llama-index-vector-stores-chroma \
llama-index-embeddings-huggingface \
llama-index-llms-openai-like \
fastapi uvicorn requests beautifulsoup4
// crawler.py — 示意,按实际数据源调整
import requests, time, json
from bs4 import BeautifulSoup
def fetch_institution(name, url):
# 务必先看 robots.txt,加 headers、限速
headers = {"User-Agent": "Mozilla/5.0 (research; contact: you@example.com)"}
r = requests.get(url, headers=headers, timeout=15)
r.encoding = r.apparent_encoding
soup = BeautifulSoup(r.text, "html.parser")
text = soup.get_text(separator="\n", strip=True)
time.sleep(2) # 限速,做个有礼貌的爬虫
return {"name": name, "url": url, "content": text}
# institutions 来自你的清单 [{name, url}, ...]
docs = [fetch_institution(i["name"], i["url"]) for i in institutions]
json.dump(docs, open("data.json", "w"), ensure_ascii=False, indent=2)
// build_index.py — 一次性建库
import chromadb
from llama_index.core import Document, VectorStoreIndex, StorageContext
from llama_index.core.node_parser import SentenceSplitter
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
import json
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-large-zh-v1.5")
docs = json.load(open("data.json"))
documents = [
Document(text=d["content"], metadata={"name": d["name"], "url": d["url"]})
for d in docs
]
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("fin_institutions")
vector_store = ChromaVectorStore(chroma_collection=collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context,
embed_model=embed_model,
transformations=[SentenceSplitter(chunk_size=512, chunk_overlap=64)],
)
// main.py — FastAPI + LLM
from fastapi import FastAPI
from llama_index.core import Settings
from llama_index.llms.openai_like import OpenAILike
# 接 DeepSeek / 通义 等兼容 OpenAI 协议的 API
Settings.llm = OpenAILike(
model="deepseek-chat",
api_base="https://api.deepseek.com/v1",
api_key="YOUR_KEY",
is_chat_model=True,
)
Settings.embed_model = embed_model # 与建库时同一个模型
app = FastAPI()
query_engine = index.as_query_engine(similarity_top_k=5)
@app.get("/ask")
def ask(q: str):
resp = query_engine.query(q)
return {
"answer": str(resp),
"sources": [n.metadata for n in resp.source_nodes],
}
uvicorn main:app --host 0.0.0.0 --port 8000
# 访问
http://你的VPS:8000/ask?q=某某银行的注册资本是多少
// sizing & hardening
数据量大了再考虑:重排序(bge-reranker)、混合检索(向量 + BM25 关键词)、给 chunk 加结构化 metadata 以便按机构类型 / 地区过滤。现在这个量级不用管。