【YiFeiWebApi】给鼎捷易飞 ERP 接一个大模型:我用 ASP.NET Core + DeepSeek 做了个“易飞小智“,自然语言直接查业务数据
给鼎捷易飞 ERP 接一个大模型:我用 ASP.NET Core + DeepSeek 做了个"易飞小智",自然语言直接查业务数据
零改造 ERP、纯只读、可审计。 业务人员问一句"A 客户今年有多少张订单",大模型自动调用易飞 WebApi 取数、汇总结论,全程不写一条数据。本文完整记录架构设计、Function Calling 落地、三条安全红线,以及一个非常反直觉的真实 Bug 排查过程。
一、为什么要做这件事
制造业上了易飞 ERP 之后,有一个很普遍的场景:
- 业务:“帮我看下哪些工单逾期未完工?”
- 物控:打开工单报表 → 设条件 → 导出 Excel → 筛选 → 回复,20 分钟;
- 老板:“C 客户今年接单情况怎么样?” → 又是一轮翻报表。
易飞本身没有内置大模型能力,报表体系强大但门槛高。而大模型最擅长的恰恰是:理解自然语言意图 → 拆解成结构化查询 → 读取数据 → 组织结论。
所以我做了一个独立的 AI 中间层服务 易飞小智(YiFeiErpAI),核心思路一句话:
ERP 只负责业务读写,AI 只负责查询和建议,两者通过标准 WebApi 解耦。
这也决定了它最重要的属性:不动易飞源码、不直连易飞数据库、不新增/修改任何单据。
二、整体架构:一个 ASP.NET Core 服务串起三方
技术栈:ASP.NET Core(.NET 10)+ DeepSeek(OpenAI 兼容协议)+ Function Calling,模型可配置切换 DeepSeek / 通义千问 / 本地 Ollama。
┌─────────────┐ 自然语言 ┌──────────────────────────────┐
│ 浏览器聊天页 │ ───────────▶ │ 易飞小智(ASP.NET Core) │
│ (纯静态HTML) │ ◀─────────── │ │
└─────────────┘ 结论+表格 │ ChatOrchestrator 工具调用循环 │
│ ToolRegistry 工具白名单 │
│ ErpClient 鉴权/解包/重试 │
│ Sanitizer 出网脱敏 │
│ AuditLogger 全程审计 │
└───────────┬──────────────────┘
│ HTTP(内网)
▼
┌──────────────────────────────┐
│ YiFeiWebApi(自研) │
│ /api/Copi06/Query 等标准接口 │
└───────────────┬──────────────┘
▼
易飞数据库(只读访问)
几个关键设计决策:
1)AI 不碰数据库,只调 WebApi。 直连库看似简单,但意味着 AI 服务要持有数据库账号、绕过所有业务校验。走 WebApi 则天然继承了易飞侧的授权码、账套隔离、IP 白名单和字段管控。
2)大模型通过 Function Calling 自己决定查什么。 我们把易飞的查询接口注册成 8 个"工具"(客户、供应商、品号信息、品号月档、采购单、工单、销售订单、销售发票/应收),模型理解问题后自己选择工具、生成过滤条件。AI 侧不需要为每种问题写硬编码规则。
3)模型供应商只改配置不改代码。 三家都走 OpenAI 兼容协议:
"Llm": {
"Provider": "DeepSeek", // 改成 Ollama / Qwen 即整体切换
"BaseUrl": "", // 留空自动用 DeepSeek 预设
"Model": "", // 留空自动用 deepseek-chat
"ApiKey": "" // 密钥走 user-secrets / 环境变量,绝不入库
}
数据敏感的客户切本地 Ollama 时,把脱敏开关关掉即可,数据不出内网。
三、Function Calling 是怎么落地的
DeepSeek 的工具调用是一个"思考 → 调工具 → 观察结果 → 再思考"的循环。以"哪些工单逾期未完工"为例,模型生成的工具参数是结构化 JSON:
{
"conditions": [
{ "field_name": "approve_status", "operator": "==", "value": "Y" },
{ "field_name": "status", "operator": "IN", "value": "1,2" },
{ "field_name": "plan_complete_date", "operator": "<", "value": "20260919" }
],
"result_mode": "rows",
"page_size": 50
}
这个 JSON 被翻译成易飞 WebApi 统一的 StdQuery 契约:
{
"std_data": {
"parameter": {
"page_no": 1,
"page_size": 50,
"conditions": {
"operator": "AND",
"fields": [
{ "field_name": "approve_status", "operator": "==", "value": "Y" }
]
}
}
}
}
这里有一个不能偷懒的关键点:工具的字段必须逐个核对 DTO 白名单。大模型会"自信地编造"字段名,如果不做白名单校验,轻则接口报错,重则 SELECT 出不该暴露的列。我把每个工具可过滤的字段、可用操作符(== / >= / <= / IN / BETWEEN / LIKE / STARTWITH)、可返回的列全部硬编码在工具目录里,模型只能在白名单内发挥。
调用易飞时统一注入三个鉴权头,账套(CompanyId)由服务端配置写死,模型无法指定账套:
httpRequest.Headers.TryAddWithoutValidation("X-API-License", _options.LicenseKey);
httpRequest.Headers.TryAddWithoutValidation("X-API-CompanyId", _options.CompanyId);
httpRequest.Headers.TryAddWithoutValidation("X-API-SecurityCode", _options.SecurityCode);
返回结果还做了三层 token 成本控制:
- 单次工具调用
page_size硬上限 50 行; - "有多少张/总金额"这类问题走 count 模式,只回总数不回明细;
- 返回列走白名单投影(
selectedColumns),银行账号等敏感列从源头不查。
四、三条安全红线,全部在架构上保证
ERP 数据是企业的命根子,做这类项目我的原则是:安全不能靠提示词约束,要靠物理隔离。
| 风险 | 应对 |
|---|---|
| AI 乱改单据 | 工具目录只注册了 Query,物理上不存在写通道。未来要放开写操作,也必须走"生成草稿 → 人工二次确认"两步式接口 |
| 核心数据泄露给公有云大模型 | 出网前脱敏层对手机/身份证/银行卡/邮箱自动掩码;可整组切换本地 Ollama;敏感列不查询 |
| 越权访问 / 无法追责 | 复用 WebApi 的授权码+账套+IP 白名单;建议给 AI 单独发只含 query 授权位的密钥;每次问答都记审计日志(谁、哪个账套、问了什么、调了哪些工具、命中多少行、耗时、报错) |
前端每条 AI 回复下方还可以展开"查询过程",让用户清楚看到这次结论背后调了哪几个接口、各返回多少条——AI 查了什么,必须可见。

系统提示词里也明确划定了边界:AI 是顾问不是决策者,数据查不到就如实说,禁止用推测数字替代真实数据。
五、实战踩坑:日期查询"全部失败",根因居然和日期无关
这是整个项目最值得分享的一段排查。
上线测试时,所有带日期条件的查询都报"查询失败":
- “客户 C-00992 今年的订单数” → 销售订单接口 code=-1;
- “哪些工单逾期” → 工单接口 400 Invalid operation;
- BETWEEN、>=、甚至 LIKE ‘2026’,全部失败。
而不带日期的条件(客户编号、审核状态)全部正常。
怀疑过的错误方向: 日期格式。易飞的日期到底是公历 20260919 还是民国年 1150919?是不是要传 2026-09-19?——最后确认易飞全系统日期统一是 8 位公历 yyyyMMdd,模型传的值完全正确。
真正的突破来自一组对照实验(直接用 Postman 风格脚本打接口):
| 查询条件 | 返回 |
|---|---|
order_date == '19990101'(库里肯定没有的日期) | code=-1 查询失败 |
order_date LIKE '2014'(确定有数据) | ✅ 成功,1443 条 |
order_date BETWEEN '20140101','20141231' | ✅ 成功,1443 条 |
order_date >= '20140101' | ✅ 成功,17523 条 |
order_date BETWEEN '2026全年' | ❌ code=-1 |
结论瞬间清晰了——日期操作符本身完全正常,问题是"查到 0 行"被当成了失败。 翻后端查询服务的代码,果然:
// 修复前:totalCount == 0 时 Success = false
if (totalCount > 0) {
// ...分页、填充、投影
result.Success = true;
} else {
result.Success = false; // ← 坑:空结果被标记成失败
}
控制器收到 Success=false 就统一回 code=-1「查询失败」。而这套演示库的销售订单数据恰好只到 2015 年,查"2026 年订单"命中 0 行,于是一个完全正常的业务问题被伪装成了接口故障,连带着让排查方向一路跑偏到日期格式上。
修复也很直接——空结果集是成功的查询,不是失败的查询:
// 修复后:0 行同样返回成功(total=0、rows=[])
result.Success = true;
if (totalCount > 0) {
// ...正常分页
} else {
result.Data = [];
result.Rows = [];
}
这个 Bug 的启示:
- "查询失败"这类笼统错误信息会严重误导排查,空结果和真异常必须在响应码语义上分开;
- 排查"某类条件必现失败"的问题,要设计有数据/无数据的对照实验,而不是盯着条件格式猜;
- 审计日志里记录模型实际发出的入参和原始报错,是这次能快速定位的关键——没有日志就只能靠猜。
顺带说一句:工单接口那个 400 Invalid operation 是另一个独立问题(多条件组合下动态 LINQ 翻译),排查方法相同:单条件逐个隔离。这也是把查询逻辑收敛到统一 QueryService 的好处——修一处,97 个单据控制器同时受益。
六、为什么聊天页直接内嵌在后端项目里
很多人第一反应是再建一个 Vue 工程。我的选择是 wwwroot 放一个零依赖的单文件 HTML 聊天页:
- 同源访问
/api/chat/ask,没有 CORS 问题; - 一个
dotnet run同时出页面和接口,内网演示/客户试用拷一个目录就行; - 自带轻量 Markdown 渲染(AI 回复的明细表直接渲染成表格)、会话 ID 多轮追问、工具轨迹折叠面板、打字动效;
- 以后要做登录权限、正式产品化,前端搬走即可,后端接口一个字不用改——架构本来就是前后端分离的,只是部署在一起。
七、后续规划(九大方向,按风险从低到高)
易飞 + AI 能做的远不止问答,我按"只读 → 分析 → 生成 → 写操作"排了落地顺序:
- ✅ Phase 1 自然语言业务问答(本文,纯只读,已落地);
- Phase 2 分析建议:应收账龄简报、工单逾期分析、呆滞料识别、供应商交期评分、安全库存建议——只输出建议,不替代 MRP/APS 运算;
- Phase 3 知识库 RAG + AIGC:易飞操作手册问答、月度经营简报/对账单初稿(人工审阅后才能对外);
- Phase 4 谨慎放开写:AI 只生成单据草稿,人工二次确认后才调 Create/Approve,独立开关 + 全程审计。
八、底座:YiFeiWebApi 是什么
"易飞小智"能在几周内跑起来,前提是底层有一套标准化的 YiFeiWebApi 中间层。它是面向鼎捷易飞 ERP 自研的 WebApi 服务,目前已覆盖约 100 个单据/档案控制器(采购 PURI、销售 COPI、库存 INVI、工单 MOC、质量 QMS、财务 ACP/ACR/ACT、BOM、CMS 等系列),统一提供:
- 标准 Query / Read / Create / Update / Delete / Approve 接口,入参出参全部走
StdQuery / StdRead / StdResult统一契约; - 数据库级分页与动态条件下推(
System.Linq.Dynamic.Core),大表查询只加载一页数据; - 授权码机制:按程序+操作粒度授权(如
copi06.query),支持账套隔离、安全码、IP 白名单、有效期校验; - AutoMapper 投影、关联数据自动填充、单号自动生成、审核/作废业务规则、Redis 缓存等基础设施。
正是因为有了这套"普通话",接 AI 时要做的只是把标准接口注册成工具,而不是为 AI 重写一遍数据访问层。同样的底座,接企业微信机器人、钉钉 H5、自研报表门户、RPA 平台都是同一套接口。
如果你们也在用鼎捷易飞,受困于"数据在 ERP 里、业务人员取数难",欢迎评论区或私信交流 YiFeiWebApi 的对接经验。
九、源码与交流
- 技术栈:ASP.NET Core 10 / DeepSeek(OpenAI 兼容)/ Function Calling / Serilog 审计 / 零依赖静态聊天页
- 完整项目结构与关键代码已在文中给出,建议动手时重点关注:工具字段白名单、page_size 硬上限、账套服务端固定、脱敏与审计、空结果语义这五个护栏。
如果觉得有帮助,点赞 👍 + 收藏 ⭐ + 关注,后续会更新 Phase 2 的分析模板实战(应收账龄、呆滞料、工单超耗根因分析),有易飞 AI 对接问题也欢迎在评论区提问,我会尽量回复。
声明:本文方案定位为业务查询与辅助建议,AI 不直接执行任何 ERP 增删改操作;涉及公有云大模型的环节应做好数据脱敏,核心经营数据建议私有化部署。
更多推荐




所有评论(0)