
去年帮一家做进出口贸易的公司搭了一套轻量级AI中台从需求梳理到跑通第一个业务流程前后差不多三周。项目目标非常明确消除重复录入、消减对账困难。业务部门一开始听到“中台”两个字就紧张以为要动他们用了十年的ERP和财务系统。我反复解释这个中台不动存量业务系统只做“翻译官”和“快递员”——把纸质单据、PDF、Excel里的信息识别出来转成各系统认得的标准化数据再按规矩送回各个系统同时把月底那场“对账大战”里最耗人力的机械核对工作接过来。这个项目适合谁参考一类是被多系统数据重复录入折磨的信息化管理员另一类是财务团队想减少手工对账的公司还有就是想用AI但又不愿意上重型中台架构的中小企业IT负责人。全文不涉及大型平台那套复杂的湖仓一体和数据治理体系所有组件都是开源或免费的一台普通服务器就能承载。我会把架构选型、部署过程、核心业务逻辑和踩过的坑都展开讲内容比较实操可以按图索骥直接复现。1. 为什么是“轻型”AI中台而不是重型架构1.1 业务痛点重复录入到底从哪来大多数中型企业的信息化建设都是“业务倒逼”的销售先上了CRM财务早就用着ERP仓库后来补了WMS行政那边还有一套OA。系统之间没有集成数据就靠人工搬运。讲一个这家公司的实际场景采购部门收到供应商的送货单要先在ERP里做采购入库单然后把送货单拍照发给财务财务在财务系统里手工录入发票信息月底还要把ERP的应付账款、仓库的入库记录、财务的发票数据三边对在一起。同一批货在ERP录一次、在WMS登记一次、在财务系统再录一次中间任何一次录错月底对账就会出现差异。这个案例里最痛苦的不是录入本身而是口径不一致。同一个供应商销售系统里叫“上海华茂贸易有限公司”ERP里叫“华茂贸易”财务系统里叫“上海华茂”对账的时候系统按名称精确匹配直接就判为两笔不同数据。这种差异靠人工核对一晚上能核几百条已经是极限。1.2 “轻型”的定义和选型思路市面上说的“中台”往往让人联想到巨大的数据中台项目——建数仓、搭ESB总线、上微服务治理周期以季度为单位预算动不动几百万。这套东西对业务跑得动、但信息化底子薄的公司来说有点杀鸡用牛刀。这个项目的定位是“轻型AI中台”核心逻辑就四点只解决两件事单据数据的自动识别与分发消除重复录入、账务数据的自动匹配与差异解释消减对账困难。技术栈用开源组件单机Docker Compose即可部署不引入K8s和分布式事务。AI能力通过本地化部署的大模型和OCR引擎提供数据不出内网。与业务系统之间通过标准化API对接不改造存量业务系统内部结构。选这个思路的原因是见效快、可控性高。重型中台要解决的是全公司的数据资产化问题而这个项目只要解决“录一遍”和“对得上”这两个具体痛点没必要一上来就搭湖仓。轻型的代价是扩展性有限但作为一家几百人规模的公司反而很合适。1.3 本地化部署的价值在哪里很多公司一听到“AI中台”就想用云端API但财务数据和供应商信息是敏感数据走公网接口首先过不了合规这一关。这家公司的要求很明确模型必须跑在内网服务器上。本地部署大语言模型在这两年已经非常成熟Ollama、vLLM这些工具把部署门槛降得很低一台带GPU的服务器就能跑量化版模型。OCR引擎用PaddleOCR同样本地部署。整个推理链路都发生在内网审计、合规、数据安全这些问题一并解决了。这样一个“企业大模型私有化部署”的形态在中小企业里其实越来越主流。2. 整体架构与核心技术选型解析2.1 架构总览能力管线与数据总线整套中台跑下来核心就五个组件API网关FastAPI、OCR识别服务、大模型推理服务、规则引擎/定时调度、数据库PostgreSQL Redis。数据流向是这样的业务人员把单据照片或PDF传到中台统一入口OCR服务先做文字识别和版面分析把整页内容切成“表头字段表格行”的结构化数据。接着结构化数据进入大模型服务做纠错和标准化比如把“上海华茂贸易有限公司”和“华茂贸易”统一映射成标准供应商名称。标准化后的数据落到中台的暂存区规则引擎根据配置好的分发规则调用各业务系统的API把数据回填。对账任务则是定时从各系统拉取账务数据在中台完成自动匹配。这套架构里没有消息队列用的是Redis Streams做轻量级任务队列。因为项目初期并发量并不高一天处理几百张单据完全够用没必要上Kafka这种重量级组件。2.2 OCR引擎选型PaddleOCR为什么够用选OCR引擎时对比过Tesseract和PaddleOCR。Tesseract是老牌开源项目但对中文和表格的支持一般拍歪的发票识别率下降明显。PaddleOCR在中文场景下的识别效果好尤其是PP-OCRv4模型对模糊、倾斜、光照不均的图片鲁棒性很强还自带表格结构识别能力能把“品名、数量、金额、税率”这些列从图片里分割出来。在部署上PaddleOCR可以跑在CPU上但速度偏慢一张A4单据在CPU上大概要3-5秒在GPU上不到1秒。这个项目用的是GTX 3060 12G显卡OCR服务的推理延迟基本在0.5秒以内完全满足使用。PaddleOCR的模型可以通过paddleocr命令行直接调用也可以封装成Python服务和FastAPI集成很方便。补充一点OCR识别不可能做到100%准确所以架构里必须有一个“大模型纠错”环节。比如“¥1,200.00”被OCR识别成“¥1200.00”或者“税率13%”识别成“税率13%。”这些看似小问题落到财务系统里就是天大的差异。纠错这件事交给大模型做比写正则靠谱得多。2.3 大模型推理服务本地部署怎么选型这家公司本来想采购商业大模型API后来还是决定本地部署。用Ollama部署了Qwen2.5系列的量化版模型显存占用约8G在12G显卡上跑得动。如果算力更紧张还可以考虑更小的量化等级比如Q4_K_M牺牲一点精度换速度。大模型在中台里只干三件事不做复杂推理字段纠错与标准化把OCR出来的非结构化文本转成标准JSON字段。对账差异的归因分析给出差异原因的初步判断。备注信息的摘要与规范化。选型时没选更大的模型是因为部署成本和推理延迟都要控制。本地部署大模型的另一个好处是Prompt可以反复试试错成本很低不像云端API每调一次都花钱。这一点在项目调试阶段帮了大忙。2.4 统一单据接口和数据规范重复录入的根源是各系统“语言不通”。AI中台要做的不是教会各系统互相说话而是定义一套“普通话”——统一单据接口。所有系统跟中台之间的数据交互都走这一套规范。字段规范上我设计了一套基础单据模型单据编号、单据类型、往来单位、金额、税额、日期、备注、状态。业务系统接入时需要提供字段映射表告诉中台“我们的客户名称字段对应你们的往来单位字段”。这套映射关系维护在中台的配置表里新系统接入的时候不需要改代码改配置就行。这一点在项目实施中非常值得注意不要低估字段映射的工作量。ERP里的“供应商编号”可能有好几套编码规则财务系统里则完全不存编号只存名称。这些历史包袱只能通过映射规则和清洗策略去处理没有捷径。3. 实操部署从零到一搭起整个中台3.1 环境准备一台服务器够不够整个中台不需要集群一台16核32G内存的服务器就可以。GPU是加分项建议至少12G显存主要用于跑大模型推理。操作系统我用的是Ubuntu 22.04 LTSDocker版本24Docker Compose V2。在这台机器上同时跑PostgreSQL、Redis、OCR服务、大模型服务、API服务、调度服务资源完全够用。部署大模型之前先要确认显存。以Qwen2.5 7B的Q4_K_M量化版为例模型文件大概5G左右运行显存占用在8G上下。如果用更大的模型比如14B量化版显存需求会到14G以上12G显卡就跑不动了。这个项目测试下来7B量化版做字段纠错和对账归因精度完全够用。3.2 本地部署大模型Ollama的命令与参数Ollama的部署非常简单安装完直接拉模型就行# 拉取模型这里用qwen2.5 7b的q4量化版本 ollama pull qwen2.5:7b-instruct-q4_K_M # 启动服务默认监听在11434端口 ollama serve如果需要自定义模型行为写一个Modelfile把系统提示词内置进去FROM qwen2.5:7b-instruct-q4_K_M SYSTEM 你是一个企业数据清洗助手。你的任务是从OCR识别结果中提取结构化字段。 要求 1. 只输出JSON不要多余解释。 2. 金额字段统一转为数字格式去掉货币符号和千分位分隔符。 3. 供应商名称尽量标准化去掉公司后缀以外的冗余信息。 这个Modelfile在实战中很关键。把系统提示词烧进模型调用的时候就不用每次重复传一大堆Prompt响应速度和稳定性都更好。对账归因也可以用同样的方式做一个专门的归因模型实例。3.3 Docker Compose编排一个文件拉起全套服务这是整个部署最核心的部分。我给出一份精简但完整的Compose编排实际项目在此基础上加了健康检查和日志轮转version: 3.8 services: postgres: image: postgres:15-alpine environment: POSTGRES_DB: aiplatform POSTGRES_USER: aiplatform POSTGRES_PASSWORD: change_me volumes: - pgdata:/var/lib/postgresql/data ports: - 5432:5432 redis: image: redis:7-alpine command: redis-server --appendonly yes volumes: - redisdata:/data ports: - 6379:6379 ollama: image: ollama/ollama:latest volumes: - ollama_models:/root/.ollama environment: - OLLAMA_KEEP_ALIVE24h deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] ports: - 11434:11434 ocr-api: build: ./services/ocr environment: - OCR_DEVICEgpu - PADDLEOCR_MODEL_DIR/models volumes: - ./models:/models depends_on: - ollama ports: - 8081:8080 platform-api: build: ./services/api environment: - DATABASE_URLpostgresql://aiplatform:change_mepostgres:5432/aiplatform - REDIS_URLredis://redis:6379/0 - OLLAMA_BASE_URLhttp://ollama:11434 - OCR_API_URLhttp://ocr-api:8080 depends_on: - postgres - redis - ollama ports: - 8000:8000 scheduler: build: ./services/scheduler environment: - DATABASE_URLpostgresql://aiplatform:change_mepostgres:5432/aiplatform - REDIS_URLredis://redis:6379/0 depends_on: - postgres - redis volumes: pgdata: redisdata: ollama_models:几个设计细节说明一下。OLLAMA_KEEP_ALIVE24h是为了让模型常驻显存否则每次请求都要重新加载模型响应时间会从几百毫秒变成几十秒。OCR服务和平台API分开部署是考虑到OCR服务的计算密集特性如果后续需要升级硬件单独扩这一层就行。调度服务用APScheduler实现负责定时触发的对账任务。对账任务是每天凌晨2点跑那时候业务系统负载低拉取数据不会给生产系统造成压力。如果这个时间点有一些特殊的财务结算流程可以在配置里调整。3.4 API服务框架FastAPI还是Flask热搜里有人问Flask部署但其实这个项目我用的是FastAPI。理由很简单FastAPI自带OpenAPI文档业务系统接入时开发人员打开/docs就能看到所有接口的定义和参数说明不需要我额外写文档。这对企业内部的对接效率提升非常明显。核心接口主要有三个POST /api/v1/parse_document上传单据图片OCR大模型识别返回结构化JSON。POST /api/v1/sync_record接收业务系统的数据同步请求写入中台暂存区。POST /api/v1/reconcile/run触发对账任务返回差异结果。接口代码很短核心就是调用OCR和Ollamafrom fastapi import FastAPI, UploadFile import httpx app FastAPI() app.post(/api/v1/parse_document) async def parse_document(file: UploadFile): # 1. 存储图片 img_bytes await file.read() # 2. 调用OCR服务获取结构化文本片段 async with httpx.AsyncClient() as client: ocr_resp await client.post( http://ocr-api:8080/ocr, files{file: (file.filename, img_bytes, file.content_type)} ) ocr_text ocr_resp.json()[text] # 3. 调用大模型做字段抽取和标准化 async with httpx.AsyncClient() as client: llm_resp await client.post( http://ollama:11434/api/generate, json{ model: qwen2.5:7b-instruct-q4_K_M, prompt: f提取单据字段并输出JSON\n{ocr_text}, stream: False } ) structured_data llm_resp.json()[response] # 4. 返回结果 return {data: structured_data}实际项目里这段代码会更厚一些比如要处理OCR返回空文本的情况、大模型返回非法JSON的情况、请求超时重试等。但基础骨架就是上面这样。部署到服务器上直接用uvicorn启动就行也可以用gunicornuvicorn worker做多进程。4. 核心业务逻辑实现消除重复录入与消减对账困难4.1 重复录入是怎么被消除的统一入口与自动回填这家公司的“重复录入”痛点集中在三个场景采购收货、销售开票、费用报销。拿采购收货说以前的流程是供应商送货 → 仓库在WMS录入收货单 → 采购在ERP录入采购入库单 → 财务在财务系统录入发票现在改成仓库拍照上传送货单 → AI中台OCR大模型识别 → 自动生成WMS收货单 → 自动生成ERP采购入库单 → 财务发票要素同步到财务系统员工只需要在中台做一次确认确认识别结果无误后系统自动把数据分发到三个业务系统。这个“确认”环节非常重要不能完全无人化因为OCR的准确率再高遇到特别脏的单据还是可能出错。但是把“录入所有字段”变成“确认几个关键字段”效率提升已经是数量级的了。具体操作上中台会根据单据类型走不同的分发流程。配置表里定义了每个单据类型要调用哪些系统接口、每个字段映射到目标系统的哪个字段。分发结果会记录同步日志哪些系统同步成功、哪些失败、失败原因是什么全都可以查。需要特别提醒的是回填操作必须做幂等设计。如果ERP接口返回超时但实际已经写入数据重试就会造成重复入库。解决方法是每个单据在中台侧生成一个全局唯一的request_id业务系统接口接收这个ID如果发现该ID已处理过就直接返回成功不再重复写入。这个设计在项目调试阶段帮了大忙否则一旦发生重复数据对账差异反而会变大。4.2 数据标准化字段映射是大模型的用武之地数据标准化是消除对账差异的根基也是让大模型发挥价值的地方。我建立了一套标准主体库每条标准主体记录包括标准名称、统一社会信用代码、简称、别名列表。OCR或业务系统传来的原始数据先经过一次匹配如果能匹配到标准主体的信用代码直接关联到该主体。如果没有信用代码就用大模型做名称归一化。比如“上海华茂贸易有限公司”和“华茂贸易”这两个名称大模型会基于语义判断为同一主体并把别名加入主体库。字段映射这块我列个表展示一下这家公司最常用的几个映射原始系统字段中台标准字段处理规则客户名称CRM往来单位名称名称标准化映射到主体库供应商编号ERP往来单位编码关联后写入不直接使用原编号入库数量WMS单据数量数量为0或负数时报错税额财务系统税额转换为两位小数发票号纸质发票凭证号OCR识别正则校验格式这里有一个实战经验不要试图在第一次上线时就把所有系统所有字段都映射干净。先只映射最核心的“往来单位、金额、日期、单号”这四个维度跑通之后再逐步扩展。项目启动前我把这家公司近一年的对账差异数据拉出来分析了一下发现85%的差异来自名称口径不一致和金额精度问题把这两个解决了对账工作量已经降了一大半。4.3 自动对账引擎从“人海战术”到“机器匹配人工确认”对账这件事财务人员的痛苦在于“大海捞针”。几千条单据去对大部分是能对上的真正的差异可能只有几十条但为了找到这几十条不得不把所有数据都过一遍。自动对账引擎的思路是分三步走第一步粗匹配。按“往来单位单据编号”建立索引两边数据先碰撞一遍。这一步能对上大部分。第二步精确匹配。在粗匹配的基础上比对金额、税额、日期。金额相差超过设定阈值比如0.01元就标记为待复核。这里要注意浮点数比较不要直接用后端代码里统一用“金额以分为单位存整数”的方式避免精度问题。第三步差异归因。对没匹配上的数据先分类是单边数据还是双边数据是金额不一致还是缺少单据大模型根据上下文给出差异解释建议。比如“ERP采购订单PO-2024-0112金额为¥12,000.00WMS入库单IN-2024-0098金额为¥12,000.01差异0.01元可能由汇率四舍五入导致建议人工确认。”这类解释虽然不是最终结论但能极大缩小财务人员的排查范围。以前财务要自己去三个系统翻数据才能找到可能原因现在AI中台直接把相关单据和可能原因都列出来了。对账结果以“对账任务”为单位存库每一轮对账都会生成一份差异清单标记哪些已处理、哪些待处理。整个对账过程有完整审计记录财务ERP审计时能说清楚数据差异的发现和处理过程。4.4 大模型在对账动作中的角色边界把这个项目经验放大一点说大模型在“对账”这个动作里只是辅助不是决策者。系统里我限制了它的权限只能对差异做解释和建议不能直接修改账务数据。任何修改动作都必须由财务人员确认后走业务系统接口完成。这样做的原因一方面是对财务合规的敬畏账务数据不能由AI直接写另一方面也是技术稳定性考虑大模型输出可能存在幻觉万一给出错误的归因解读至少人还在中间环节把关风险可控。实际操作中大模型对差异的解释准确率大概在85%左右剩下的15%多为比较复杂的例外情况比如红字冲销、部分退货款等这时候人工介入是合理且必要的。5. 常见问题与排查技巧实录5.1 模型服务部署踩过的坑这个项目的模型部署踩了不少坑代表性的几个列出来。坑一Ollama拉取模型太慢。内网服务器下载模型时会遇到网络问题官方源在访问量大的时段特别慢。解决办法是换国内镜像源或者找一台能正常下载的机器先把模型文件拷贝过来通过ollama import导入。这个操作很简单但一开始确实没想到白白等了几个小时。坑二显存不足导致OOM。12G显存跑7B量化版模型如果同时有OCR服务的GPU占用偶尔会出现显存不足。排查后发现是Ollama默认会把模型完全加载进显存我通过设置OLLAMA_MAX_LOADED_MODELS1和减少num_ctx参数把上下文窗口从4096调到2048问题就解决了。对账和字段抽取的任务上下文窗口2048完全够用没必要占用那么多显存。5.2 OCR识别率不高的处理办法OCR识别最大的问题是“脏单据”——模糊、歪斜、有印章覆盖、打印字迹颜色深浅不一。PaddleOCR对这些问题有一定容忍度但遇到特别脏的单据还是会出问题。我加了两层保险。第一层是图像预处理在进入OCR之前先把图片转成灰度图做一次对比度增强和透视矫正。透视矫正用OpenCV的findHomography能解决拍照倾斜的问题。第二层是结果纠错OCR返回的全文进入大模型后大模型会结合上下文把明显识别错误纠正过来。比如“上海市”被识别成“海市上”大模型根据上下文能自动修正成“上海市”。实测下来这两层叠加之后关键字段的识别准确率从初期的88%提升到了97%左右。剩下的3%主要集中在金额数字上这也是为什么流程里保留了人工确认环节的原因。5.3 数据同步的幂等与重试机制数据同步最容易出的问题就是“重复”。接口超时重试、消息队列重复消费、人工误操作重新提交任何一个环节出问题都可能导致同一条数据被写入两次。我给每一笔同步记录都分配了唯一单据流水号用数据库唯一索引兜底。同步状态机设计为“待同步→同步中→成功/失败”如果状态已经是“成功”重复请求会直接返回幂等成功。重试机制上有指数退避第一次失败等5秒重试第二次等25秒第三次等125秒超过3次就转人工处理并推送告警。这样既不会把业务系统接口打爆也能保证数据最终一致。5.4 对账结果不准确的排查思路如果跑完对账任务发现差异特别多先不要怀疑算法有问题按下面顺序排查现象优先排查方向所有单据都匹配不上检查两边数据的日期口径是否一致比如一个按业务日期、一个按过账日期金额差0.01元检查是否存在税额四舍五入差异这是最高频原因特定供应商的单据匹配不上检查名称标准化是否正确主体库是否需要补充别名单据编号匹配不上检查是否存在相同单据编号但不同系统含义不同的情况比如ERP的单号是“采购订单号”财务系统存的是“发票号”这个排查顺序看起来很简单但在实际项目里帮我们省了大量时间。很多对账异常不是AI能力不够而是数据口径本身就不一致规则引擎的前提条件没满足。5.5 服务器资源占用与优化整套中台部署在一台16核32G内存的服务器上GPU是12G的GTX 3060。资源占用方面大模型常驻显存约8GOCR在GPU上推理时偶尔会额外占用1G左右剩余显存空间足够应对。CPU方面PaddleOCR的预处理和后处理是CPU密集的但毕竟是低频调用一天几百张单据的量完全扛得住。如果后续单量增长到每天几千张优先考虑把OCR服务和API服务多开几个实例用负载均衡分发请求。再往上走才需要考虑GPU服务器扩容或者模型换成更大的。但就这个项目目前的规模单机方案运行了半年多稳定性很高没有因为资源不足导致过服务中断。优化方面还有一个细节大模型的响应时间受Prompt长度影响很大。我预先写好了一套精简Prompt模板把任务描述控制在200-300字以内不做“少样本”示例响应时间稳定在1-3秒。如果传入的原文特别长就先截断关键段落再送模型避免上下文过长导致推理变慢。6. 落地效果与一些实在的体会这个项目上线半年多最直观的效果是月底对账时间从“三个人各花两天”压缩到“一个人半天”。重复录入方面采购收货和销售开票两个场景基本实现了单据一次上传、多系统自动同步。仓库人员不再需要同时打开三个系统录入三遍数据财务也不用再一封一封邮件去催业务部门补资料。我个人在这个项目里体会最深的一件事是AI中台能不能落地取决于“标准”做得好不好而不取决于模型有多聪明。大模型只是让“读懂脏数据”这件事变得容易了但真正决定效果的是字段映射、命名规范、幂等机制这些看起来很不起眼的基础工作。把这些基础打好AI能力才能被真正用起来。最后再分享一个小技巧中台刚上线时不要急着把所有业务场景都接进来先挑一个“录入最痛、对账最烦”的场景跑通让业务人员感受到省力后续推广阻力会小非常多。这比一开始就画一个宏大蓝图、结果上线三个月都没落地要靠谱得多。项目做下来我对“轻型”这两个字的理解也更深了——不是功能少而是每一块都在刀刃上都解决真实问题。