手里攒了大半年数据,散落在各个业务接口和遗留数据库里,想给团队搭一个能直接提问的内部知识库。刚开始我也想过最简单粗暴的方案:写脚本把数据导成 CSV,再手动塞进现成的 RAG 工具。结果数据一多、更新一频繁,这条路根本走不通。后来我把整条链路梳理成了Pandas+SQL+API→RAG 自动入库的流水线:从接口拉数据、用 Pandas 清洗、落到 SQL 存储,再用定时任务把增量数据切片、向量化、写入向量库,最后接上大模型做检索问答。
这篇文章把这套工程怎么落地、中间踩过的坑、以及每一步为什么这么设计,完整拆开讲。适合正在做知识库、RAG 应用或者数据中台的读者,也适合想把手动数据处理流程改造成自动化管道的人参考。
1. 管线全貌:为什么非要把 Pandas、SQL、API、RAG 串成一条链
1.1 四个环节的职责边界
你可以把这条流水线理解成一条工厂生产线,每台机器只干一件事:
| 环节 | 核心职责 | 产出物 | 可验证性 |
|---|---|---|---|
| API | 从业务系统、大模型接口、第三方平台拿数据 | 原始 JSON / CSV | 看字段是否完整、是否有分页遗漏 |
| Pandas | 清洗、转换、标准化,处理脏数据 | 规整的 DataFrame | 检查类型、空值、重复值 |
| SQL | 数据落库,形成可信的中间存储层 | 表记录 | 用 SQL 随时查询、审计、回溯 |
| RAG | 把文本切片、向量化,写入向量库供检索 | 向量索引 | 用测试问题检索,看召回质量 |
当初我踩过最大的坑,就是跨过 SQL 直接拿 Pandas 清洗后的数据去建 RAG 索引。看起来省了一步,其实失去了回滚能力和审计线索。一旦向量化策略调整,或者发现某批数据有错,你只能重跑全部流程,非常痛。
1.2 自动入库要解决的四个核心问题
第一是数据更新频繁。手工导出再导入,数据处理一次两次还能接受,但如果是每周、每天甚至每小时更新,就必须自动化。
第二是原始数据质量差。接口返回的 JSON 里经常混着空值、错别字、格式不统一的日期、被截断的长文本,直接拿去切片做 Embedding,检索质量会很差。
第三是增量处理。全量重建索引在数据量小的时候没问题,几万条文本也就几分钟。但数据量到了几十万、几百万级别,每次全量重建的成本就失控了,必须做增量。
第四是可追溯。领导突然问“这条回答引用的数据是哪来的、是哪天的”,如果你的数据管道没有中间存储,这个问题根本回答不上来。
系统化梳理之后,我的结论很明确:API 负责获取,Pandas 负责清洗,SQL 负责存储,RAG 负责知识化。每一层独立、可测试、可回滚,这才是工程化做法。
2. 第一站:API 数据获取,稳定拉取比炫技重要
2.1 用 requests 写一个能稳定跑的接口调用
很多新手写接口调用,就是requests.get(url),然后response.json()。在真实业务里,这么做分分钟出事:超时、限流、返回非 JSON、字段缺失。
我习惯用一个带 Session 的封装函数,统一处理超时和基本错误:
import requests import time from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session() retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], ) adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=10, pool_maxsize=20) session.mount("http://", adapter) session.mount("https://", adapter) def fetch_json(url, headers=None, params=None, timeout=10): resp = session.get(url, headers=headers, params=params, timeout=timeout) resp.raise_for_status() return resp.json()这里几个细节值得说明:
timeout必须设置,否则请求挂起会拖死整个脚本。Retry的status_forcelist处理限流和临时故障,但注意backoff_factor=1表示重试间隔按 1 秒、2 秒、4 秒递增,避免重试风暴。- 不要每次都
requests.get创建新连接,用 Session 复用连接池,性能差距在批量拉取时特别明显。
2.2 限流:不是你不行,是对方接口扛不住
有一次我写了一个拉取脚本,跑了几分钟后接口开始疯狂返回 429,后来对方的运维直接把我方出口 IP 临时封了。从那以后,只要不是官方承诺无限制的接口,我都会主动限速。
一般做法是拉取循环里加个time.sleep,但这比较粗暴。更好的做法是用令牌桶或简单的间隔控制:
import time def rate_limited_fetch(url_list, interval=0.5): results = [] for i, url in enumerate(url_list): results.append(fetch_json(url)) if i % 10 == 9: time.sleep(interval * 10) # 每 10 个请求暂停一下 return results真实场景里,接口往往是分批分页的,我会在分页循环里做控制,同时把当前页数、已拉取条数打到日志里。管道越是自动化,日志就越重要,不然半夜跑挂了都不知道卡在哪个接口上。
2.3 大模型 API 调用:错误码 400 的隐形陷阱
做 RAG 相关项目免不了要调用大模型接口,比如给切片生成摘要、做关键词抽取,或者直接用于问答。这里有一个非常常见、也非常隐蔽的问题——模型名参数传错。
很多大模型 API 的鉴权方式是Authorization请求头,这点大家基本不会错。容易翻车的是请求体里的model字段。接口文档里写的是deepseek-flash,实际开通的账号权限里模型名是deepseek-v4或是带-pro后缀的版本,你传错了系统直接返回类似下面这样的错误:
api error: 400 the supported api model names are deepseek-flash, deepseek-v4, ...这类 400 错误几乎都是模型名枚举不匹配导致的,而不是密钥问题。我的建议是:要么在代码里用一个常量统一维护可用模型名,要么启动时先调一次模型列表接口,动态拉取可用模型,不要硬编码。另外,密钥不要写死在脚本里,用环境变量或配置文件管理,否则代码一旦传到仓库里,泄漏就是时间问题。
2.4 增量拉取:时间窗口还是 ID 游标
自动入库必须解决的一个问题是:下一次运行怎么知道该拉哪些数据?
两种常用方案:
- 按时间窗口:传
start_time和end_time,每次拉取最近 N 分钟的增量数据。实现简单,但有两个隐患:一是上游服务崩溃导致某段时间数据缺失,你无法感知;二是时区处理不当会漏数据。 - 按 ID 游标:记录上一次拉到的最大 ID,下次从它后面继续拉。适合数据只追加、不修改的场景,比如消息日志、操作流水。比时间窗口更可靠,因为 ID 单调递增不容易受时区影响。
我现在的做法是双保险:主体按 ID 游标,同时用数据里的业务时间字段做交叉校验。每次跑完把游标和时间水位线写入一个pipeline_status表,下次直接从这张表读起点。这一步虽然简单,却是整个自动入库断点续跑的基础。
3. 第二站:Pandas 清洗,把原始数据变成“能入库的样子”
3.1 接口返回的 JSON 怎么变成规整的 DataFrame
接口返回的数据往往是嵌套结构,比如:
{ "code": 0, "data": { "list": [ {"id": 1, "title": "服务故障复盘", "content": "...", "author": {"name": "张三", "dept": "运维"}} ] } }直接pd.DataFrame(data["data"]["list"])得到的author列是字典,需要进一步展开。用pd.json_normalize可以一次展开多层:
import pandas as pd rows = data["data"]["list"] df = pd.json_normalize(rows, sep="_") # 得到 author_name、author_dept 这样的列然后我会立刻做三件小事:
- 把全角空格、零宽字符这些不可见字符替换掉。
- 把列名统一为小写加下划线,避免后续写 SQL 时被大小写折磨。
- 打印
df.shape和df.dtypes,确认数据规模和类型符合预期。
这三步看似琐碎,但能避免后面 80% 的麻烦。
3.2 科学计数法与长数字:Pandas 里最容易翻车的类型陷阱
网上经常有人问“从数据库导出的身份证信息变成科学计数法怎么处理”,这就是典型的数字精度问题。
Pandas 读入数据时,如果一列全是 18 位数字,会被推断成int64甚至float64,输出时自动变成科学计数法。更严重的是,数值类型会截断精度,超过 15 位有效数字就丢了末尾几位。
我处理这类长数字字段的固定姿势是:要么读入时就指定dtype=str,要么在清洗阶段用.astype(str)强制转回字符串。
# 读取时指定字段类型 df = pd.read_csv("input.csv", dtype={"id_card": str, "phone": str}) # 或者清洗阶段统一处理 for col in ["id_card", "phone"]: df[col] = df[col].astype(str).str.replace(r"\.0$", "", regex=True)有人会问:既然 Pandas 会丢精度,为什么不直接用数据库存?因为很多场景中,Pandas 是用来做跨系统数据融合的,Excel、CSV、接口返回、旧数据库导出,各种来源混在一起。只有在内存里统一处理完,才敢往正式表里写。所以这个类型陷阱只能靠 Pandas 阶段解决。
3.3 日期时间转换:to_datetime的 format 参数不是可选项
项目里收到的日期字段,可能是"2024-01-05 10:23:45",也可能是"2024/01/05",还有可能是"20240105"。如果不管格式直接pd.to_datetime(df["time"]),Pandas 大部分时候能猜对,但偶尔会猜错,尤其是遇到"01/02/2024"这种美式日期时。
我踩过一次坑:接口返回的"03/04/2024"被解析成了 3 月 4 日,但实际上是 4 月 3 日。从那时起,所有日期解析我都显式指定format:
df["created_at"] = pd.to_datetime(df["created_at"], format="%Y-%m-%d %H:%M:%S")如果多种格式混在一起,就用errors="coerce"把解析失败的置为NaT,然后单独排查:
df["created_at"] = pd.to_datetime(df["created_at"], format="mixed", errors="coerce")format="mixed"是新版 Pandas 提供的参数,它会尝试多种格式,但速度会慢一些。数据量大的时候,我宁愿先分桶再分别解析,也不全量用混合模式。
3.4 去重、空值与文本标准化
去重不是简单drop_duplicates(),关键在subset选哪几列。业务数据里,同一条记录可能id不同但title + content完全相同。我一般用业务主键去重:
df = df.drop_duplicates(subset=["title", "content"], keep="last")空值处理要分场景。如果是结构化字段,比如金额、日期,空值可以删除或填默认值。但如果是用于 RAG 的正文文本,我强烈建议不要因为某一列为空就整行删除——比如文章正文是空的,但标题和摘要还有价值,删掉就白白丢了一条知识。我会把空字段填成"未知"或"暂无",让切片阶段仍然能保留上下文。
文本标准化是最影响 RAG 效果的环节,包括去 HTML 标签、替换换行、统一标点:
import re def clean_text(text): if not isinstance(text, str): return "" text = re.sub(r"<[^>]+>", "", text) text = text.replace("\u3000", " ").replace("\r\n", "\n") text = re.sub(r"\n{3,}", "\n\n", text) return text.strip() df["content"] = df["content"].apply(clean_text)切分出来的文本质量高不高,这一步起了决定性作用。很多人做完 RAG 发现检索不准,追根溯源往往是原始文本里充满了 HTML 标签和异常换行,把向量语义都搅浑了。
4. 第三站:SQL 存储,给数据一个可信的中间态
4.1 为什么一定要落 SQL:审计、回滚、断点续跑
如果只是给自己写个演示 demo,不落 SQL 完全没问题。但真实的自动入库管道必须有一个中间存储层,理由有三个:
- 审计与回溯:数据出问题时,要能查到“某条文本是哪天进管道的、当时是什么状态”。
- 回滚能力:向量化策略升级后发现效果变差,需要重新生成索引,这时候从 SQL 重新读数据最方便,不用再调一次源接口。
- 断点续跑:如果管道在第 3 步失败,下次启动时能从失败点继续,而不是从头再来。
SQL 就是管道里的“检查点”。选型上,团队有现成的 SQL Server 或 PostgreSQL 就直接用,没有的话开源的 PostgreSQL 是首选,理由后面讲向量库时会提到。
4.2 表结构设计:文档主表 + 切片段
我给这套管线设计了两张表,核心思路是把“文档”和“切片”分开存,避免一张表字段过多导致读写性能差。
-- 文档主表 CREATE TABLE doc_metadata ( id BIGINT PRIMARY KEY, title NVARCHAR(500), author NVARCHAR(200), source_api NVARCHAR(100), raw_data NTEXT, -- 原始 JSON,保留审计 content NTEXT, -- 清洗后的正文 created_at DATETIME2, updated_at DATETIME2 ); -- 切片表 CREATE TABLE doc_chunks ( chunk_id BIGINT IDENTITY PRIMARY KEY, doc_id BIGINT NOT NULL, chunk_index INT NOT NULL, chunk_text NTEXT NOT NULL, token_count INT, created_at DATETIME2 DEFAULT GETDATE() );doc_metadata存文档元信息和清洗后的正文,doc_chunks存切片结果。每次跑完管道,可以通过doc_id关联看到这条知识是从哪来的。
这里有个细节容易被忽略:raw_data字段一定要留。数据管道跑得越长,你越会发现原始数据才是最可靠的参照物,清洗后的任何字段都可能被后续逻辑改了。
4.3 用 SQLAlchemy 与 to_sql 写入,dtype 映射别偷懒
Pandas 的to_sql很方便,但默认的 dtype 推断经常不如人意。object列会被映射成TEXT还行,但日期列、数值列、长文本列都需要手动确认。
我的习惯是先建好表,再通过if_exists="append"写入:
from sqlalchemy import create_engine engine = create_engine("mssql+pyodbc://user:pass@host:1433/dbname?driver=ODBC+Driver+17+for+SQL+Server") df.to_sql( "doc_metadata", engine, if_exists="append", index=False, dtype={ "title": sqlalchemy.NVARCHAR(500), "content": sqlalchemy.TEXT, "raw_data": sqlalchemy.TEXT, "created_at": sqlalchemy.DateTime, }, )注意:如果库里已经建好了表,to_sql的dtype参数只是辅助,数据库会按现有表结构接收。但如果让 Pandas 自动建表,dtype就非常关键了,不指定的话某些数据库会把中文字段列显示成TEXT或默认长度不够,报错之后再排查非常费时间。
4.4 增量更新与 Upsert:别用删了重插这种笨办法
增量更新最常见的需求:同一批数据再次跑管道时,已经存在的记录要更新,不存在的记录要插入。如果你的数据库是 PostgreSQL,直接用INSERT ... ON CONFLICT DO UPDATE(即 upsert):
INSERT INTO doc_metadata (id, title, content, updated_at) VALUES (%s, %s, %s, %s) ON CONFLICT (id) DO UPDATE SET title = EXCLUDED.title, content = EXCLUDED.content, updated_at = GETDATE();Pandas 侧可以先把增量数据追求出来,逐批执行 upsert:
from sqlalchemy.dialects.postgresql import insert def upsert_docs(df, engine): stmt = insert(doc_metadata_table).values(df.to_dict("records")) stmt = stmt.on_conflict_do_update( index_elements=["id"], set_={ "title": stmt.excluded.title, "content": stmt.excluded.content, "updated_at": stmt.excluded.updated_at, }, ) engine.execute(stmt)如果是 SQL Server,则用MERGE语句,语法稍微繁琐但逻辑一样。核心思路是保证管道重复执行不会产生重复数据,这也叫“幂等性”。自动任务的日志里如果出现大量重复主键,往往是这里没处理好。
4.5 SQL 窗口函数:给数据排序、打上批次标签
窗口函数是 SQL 里非常好用的工具。比如我想找出每个文档最新的一条历史记录,或者是给同一批导入的数据打上批次号:
SELECT *, ROW_NUMBER() OVER (PARTITION BY doc_id ORDER BY updated_at DESC) AS rn FROM doc_metadata;在管道场景里,我用它做过一次数据去重:因为上游接口偶尔会返回历史数据,导致同一doc_id对应多行记录。我先用窗口函数给每个doc_id按更新时间排序,只保留rn = 1的行作为有效记录,其余标记为待清理。
有些人觉得窗口函数难记,其实你只要记住一个场景:“分组内排序、取前 N”,只需要这一个场景就能应付数据清洗中 80% 的需求。
4.6 关于 SQL 注入和敏感字段的提醒
因为管道里有自动拼 SQL 的逻辑,我必须强调一点:任何来源的文本字段都不要直接拼进 SQL 语句,一律用参数化查询。Pandas 的to_sql内部是参数化执行的,相对安全;但手写 SQL 时容易图省事直接f"... WHERE title = '{title}'",这个习惯一定要戒掉。
另外,如果表里存了身份证号、手机号、地址这类个人信息,生产环境的库建议做脱敏或加密处理。管道日志里不要打印完整字段,只打印 ID 或摘要就够了。
5. 第四站:RAG 自动入库,把 SQL 数据变成可检索的知识
5.1 从 SQL 读取数据并做切片
RAG 管线的输入是 SQL 里的清洗后文本。我一般用分批查询的方式读取,避免一次性把几百万行加载到内存:
def fetch_docs_in_batches(engine, batch_size=1000): query = """ SELECT id, title, content, updated_at FROM doc_metadata WHERE updated_at > :last_run ORDER BY id """ # 用 server-side cursor 分批读取读取后,下一步就是切片。切片质量直接决定检索效果,这是整个 RAG 管道里最值得花时间调的部分。
我对比过几种切分策略:
| 切分策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 固定长度切分(如 500 字/段,重叠 50 字) | 实现简单,速度快 | 容易切断语义,上下文不完整 | 快速原型、非结构化短文本 |
| 按段落/标题切分 | 保留语义边界 | 段落过长或过短不稳定 | 文档结构清晰,如操作手册、规章制度 |
| 按语义切分(用 Embedding 相似度找边界) | 切片语义完整度高 | 计算量大,依赖向量接口 | 正式生产环境 |
我自己的默认方案是:先按段落边界切,超过上限的段落再用固定长度切一份。这个组合策略实现成本低,效果比纯固定长度好很多。
def split_text(text, max_chars=600, overlap=50): paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()] chunks = [] current = "" for para in paragraphs: if len(current) + len(para) <= max_chars: current += para + "\n\n" else: if current: chunks.append(current.strip()) current = para + "\n\n" if current: chunks.append(current.strip()) return chunks5.2 Embedding 批量向量化与成本控制
切片完成后,要调 Embedding 接口把每段文本转成向量。这里有两个非常实际的问题:
一是批量调用。Embedding 接口一般支持一次传入多条文本,尽量一次性多传几条,减少 HTTP 请求次数。但注意别超过接口上限,像有些接口单次最多上百条,我会把切片列表分批传。
二是成本与缓存。向量化是按 token 计费的,同一个切片如果因为管道重跑被反复向量化,很浪费钱。我在doc_chunks表里加了一个embedding_id字段,每次向量化前先查这个字段,已经向量化的直接跳过。这种“记录状态”的思路,跟前面 SQL 层做断点续跑是一致的。
def embed_chunks(chunks, batch_size=32): embeddings = [] for i in range(0, len(chunks), batch_size): batch = chunks[i:i + batch_size] resp = embedding_client.embeddings.create(model="embedding-model-name", input=batch) embeddings.extend([item.embedding for item in resp.data]) return embeddings5.3 向量库选型:pgvector 还是独立向量库
很多 RAG 教程会推荐像 Milvus、Weaviate、Chroma 这样的专用向量数据库。但如果你已经在 PostgreSQL 里存了源数据,我强烈建议先试pgvector扩展。
pgvector 的优势非常实际:它让文档数据和向量数据在同一个数据库里,不需要额外的数据同步组件,事务一致性天然满足,备份运维也少一套。
CREATE EXTENSION IF NOT EXISTS vector; ALTER TABLE doc_chunks ADD COLUMN embedding vector(1024); CREATE INDEX ON doc_chunks USING hnsw (embedding vector_cosine_ops);如果是几百万条量级以内的知识库,pgvector 完全能扛住。如果量级到了千万以上,或者检索延迟要求极高,再考虑迁移到独立的向量数据库。对大多数团队内部知识库场景,pgvector 是性价比最高的选择。
5.4 自动入库:定时任务与增量索引更新
有了前面的基础,自动入库就是一个调度问题了。我一般用 APScheduler 或系统 crontab 执行一个 Python 入口脚本,流程如下:
def run_pipeline(): last_run = get_last_run_time() df = fetch_from_api(last_run) df_clean = clean_with_pandas(df) upsert_to_sql(df_clean) new_chunks = fetch_new_chunks_from_sql(last_run) if new_chunks: embeddings = embed_chunks(new_chunks) write_embeddings_to_pgvector(new_chunks, embeddings) update_last_run_time(datetime.now())这个流程里有一个关键点:last_run的时间戳更新必须放在最后。如果中途挂了,下次启动还会从上次的时间点重新拉,最多产生重复处理,不会漏数据。
另一个建议是,把“入库 SQL”和“写入向量库”做成两个独立步骤,并且允许失败重试。我在生产环境就是分两步跑:数据入库是主任务,写向量库是异步任务。这样即使 Embedding 接口临时故障,数据也已经安全落在 SQL 里,不会丢。
5.5 Agentic RAG:自动入库只是第一步
现在 RAG 的进阶形态是 Agentic RAG。简单说,普通 RAG 是“用户提问 → 检索 → 拼 Prompt → 让大模型回答”,Agentic RAG 则是让大模型自己决定“要不要多检索几轮、要不要调用 SQL 查询、工具 API、要不要追问用户”。
自动入库管道已经把你需要检索的文档准备好了,后续不管是接 LangChain、LangGraph,还是自己写一个 Agent 编排层,都建立在“知识已经结构化、可检索”这个根基上。所以别急着先学 Agent 那一堆概念,先把数据管道跑通,你后面做 Agent 会顺手很多。
6. 端到端跑通后的实测效果与排错经验
6.1 实测效果参考
我用这套管道把团队的故障复盘文档、操作手册、接口变更记录全部入库,总量大概 12 万篇文档、30 万切片。测试几个典型问题:
- “支付接口最近一次变更是什么时候?”——能准确回答并给出文档原文链接。
- “服务启动失败一般怎么排查?”——能检索到 3 篇相关手册并综合出步骤。
- 跨文档问题“订单超时原因有哪些”——能拼接多个文档片段给出结构化回答。
整体效果比之前手动 CSV 导入的方案好很多,检索准确率从大概 60% 提升到了 85% 左右。提升最大的因素不是模型选得多好,而是数据清洗和切片策略做对了。
6.2 常见问题排查清单
我把跑管道过程中最常遇到的几类问题整理成一个表格,方便你对照排查:
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 接口返回 400 | 模型名、参数格式、鉴权头不对 | 先打印响应体,多半有 error message |
| 接口返回 429 | 请求太频繁,触发了限流 | 检查限速逻辑、重试策略是否生效 |
| 入库后中文乱码 | 数据库字符集不对 | 表和库都改成 UTF-8/UTF-8MB4 |
| Pandas 转 SQL 报长度超限 | dtype 映射没有指定 NVARCHAR/长文本 | 建表时把字符串列设为 NVARCHAR(MAX) 或 TEXT |
| 身份证变科学计数法 | 数值类型截断精度 | 读入或清洗阶段强制 astype(str) |
| RAG 检索答非所问 | 切片切断了语义、文本未清洗 | 检查切片前后文的连贯性、抽几条看 Embedding 向量 |
| 管道重跑有重复数据 | upsert 没写对或唯一键缺失 | 检查表里唯一索引,确认 ON CONFLICT 的条件 |
6.3 几个让我改代码的小细节
第一是编码问题。Windows 环境下 Python 脚本如果没写# -*- coding: utf-8 -*-,或者文件用 GBK 保存,写库时很容易中文乱码。我现在所有脚本统一 UTF-8,读取外部 CSV 文件时也显式指定encoding="utf-8-sig",可以去掉 BOM 头。
第二是超时与重载。向量化那一步如果 Embedding 接口超时,几十万切片可能全部白跑。我为这一层单独加了重试和断点续跑,已经成功写入向量库的切片 ID 记录在案,下次跳过。
第三是日志与告警。自动管道必须有日志,我每次运行都会输出:拉取多少条、清洗后剩多少条、写入 SQL 多少条、向量化多少条、失败多少条。一旦失败率超过阈值,就发告警通知。没有这套可观测性,自动入库就只是个“定时脚本”,离工程化还差很远。
6.4 关于整套方案的成本与收益
这套管道用到的技术栈全部开源:Pandas、SQLAlchemy、PostgreSQL、pgvector、APSchedule,唯一的硬成本是 Embedding 接口调用费和大模型 API 调用费,数据量不大的话每月几十到几百元足够了。相比每天人工导 CSV、手动整理、再灌入知识库的工时开销,自动化后的收益非常明显——数据更新的时效性从“周”提升到了“小时”。
如果你也想搭一套类似的自动入库管道,我的建议是:先不要追求一步到位把所有能力都加上。先用最简单的方式跑通一条链路:拿一个真实接口、每天定时跑一次、入库 1000 条文档,再慢慢加增量、加切片优化、加告警。管道是迭代出来的,不是一次性设计出来的。