最近,我跟团队落地了一个基于 Python 的 AI Web 应用项目,终于让我体会到,大模型与实际业务融合远比简单调用 API 更复杂,也更值得深究。
很多人觉得“大模型接入业务”不就是简单调用接口、传输数据、接收结果吗?
可当我们实际做了才发现,这种看似顺滑的流程,其实非常脆弱。一旦业务稍微复杂,光靠模型 API 堆功能,马上暴露各种问题:上下文丢失、逻辑割裂、输出格式随意。这就像买了一辆超级跑车,却只在狭窄的车库里来回倒腾,根本发挥不了它的真实价值。
我们要的是,让模型真正融入流程。
我们的 Python AI 应用主要实现了四个功能:文档校对、文档生成、智能邮件,以及未来的系统对接展望。
文档校对
第一大功能是文档校对。很多平台已经支持文件上传,但只是粗放地处理。我们设计了专用的 Python 解析脚本,先自动识别文档结构(比如代码的规范格式、财务报表特定指标等),然后结合明确的业务规则调用大模型。这种细致的前置处理,大大提高了模型的输出质量,降低了人工后续的干预。
功能展示:
代码示例:
// 开始核对按钮点击事件
document.getElementById('startCheck').addEventListener('click', async function() {
console.log('开始核对按钮被点击');
const preCheckFile = document.getElementById('preCheckFile').files[0];
const billingFiles = document.getElementById('billingReportFiles').files;
const steps = document.querySelectorAll('.step');
// 步骤1:解析预核对表
startStep(steps[0], '正在解析预核对表...');
updateProgressBar(1);
const preCheckFormData = new FormData();
preCheckFormData.append('file', preCheckFile);
try {
console.log('开始发送预核对表解析请求');
const preCheckResponse = await fetch('/parse_excel', {
method: 'POST',
body: preCheckFormData
});
console.log('预核对表解析请求响应状态:', preCheckResponse.status);
if (!preCheckResponse.ok) throw new Error('预核对表解析失败');
const preCheckResult = await preCheckResponse.json();
parseResults.preCheckData = preCheckResult.data;
completeStep(steps[0], `预核对表解析完成,共 ${preCheckResult.data.length} 条记录`);
// 修改调用方式,直接传入数据
showPartialData([preCheckResult.data]);
} catch (error) {
console.error('预核对表解析出错:', error);
failStep(steps[0], error.message);
return;
}
// 步骤2:解析计费报表
startStep(steps[1], '正在解析计费报表...');
updateProgressBar(2);
try {
console.log('开始发送计费报表解析请求');
for (let i = 0; i < billingFiles.length; i++) {
const billingFormData = new FormData();
billingFormData.append('file', billingFiles[i]);
const billingResponse = await fetch('/parse_excel', {
method: 'POST',
body: billingFormData
});
console.log(`第 ${i + 1} 个计费报表解析请求响应状态:`, billingResponse.status);
if (!billingResponse.ok) throw new Error('计费报表解析失败');
const billingResult = await billingResponse.json();
parseResults.billingData.push(billingResult.data);
}
completeStep(steps[1], `计费报表解析完成,共 ${billingFiles.length} 个文件`);
// 修改显示部分解析数据的调用,传入合并后的数据
showPartialData([parseResults.preCheckData, ...parseResults.billingData]);
} catch (error) {
console.error('计费报表解析出错:', error);
failStep(steps[1], error.message);
return;
}
// 步骤3:AI 分析差异
startStep(steps[2], 'AI正在分析差异...');
updateProgressBar(3);
// 先检查是否已经存在加载元素,避免重复创建
let loadingElement = document.getElementById('aiLoading');
if (!loadingElement) {
loadingElement = document.createElement('div');
loadingElement.id = 'aiLoading';
// 创建加载圈元素
const spinner = document.createElement('div');
spinner.className = 'loading-spinner';
// 创建提示文本元素
const text = document.createElement('span');
text.textContent = 'AI 分析中,请稍候...';
// 将加载圈和提示文本添加到加载提示元素中
loadingElement.appendChild(spinner);
loadingElement.appendChild(text);
document.body.appendChild(loadingElement);
}
try {
console.log('开始发送 AI 分析差异请求');
const compareResp = await fetch('/compare_and_ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pre_check: parseResults.preCheckData,
billing: parseResults.billingData
})
});
console.log('AI 分析差异请求响应状态:', compareResp.status);
if (!compareResp.ok) throw new Error('AI分析差异失败');
const compareResult = await compareResp.json();
parseResults.differences = compareResult.data;
// 显示 AI 分析结果
const parsedResultsElement = document.getElementById('parsedResults');
let resultContent = '';
if (compareResult.data) {
if (typeof compareResult.data === 'string') {
resultContent = compareResult.data;
console.log("resultContent", resultContent);
} else if (Array.isArray(compareResult.data) || typeof compareResult.data === 'object') {
resultContent = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${JSON.stringify(compareResult.data, null, 2)}</pre>`;
}
} else {
resultContent = '未获取到有效的 AI 分析结果';
}
if (parsedResultsElement) {
parsedResultsElement.innerHTML = resultContent;
} else {
console.error('未找到 parsedResults 元素');
}
completeStep(steps[2], 'AI分析差异完成');
// 移除加载提示
if (loadingElement) {
document.body.removeChild(loadingElement);
}
// 步骤4:生成核对报告
startStep(steps[3], '正在生成核对报告...');
updateProgressBar(4);
setTimeout(() => {
completeStep(steps[3], '核对报告生成完成');
// 移除切换到核对结果 tab 的逻辑
}, 1000);
} catch (error) {
console.error('AI 分析差异出错:', error);
failStep(steps[2], error.message);
// 移除加载提示
if (loadingElement) {
document.body.removeChild(loadingElement);
}
}
});
文档生成
第二大功能是文档生成。听起来简单,就是数据填报到报表中,但我们测试发现,GPT 之类的大模型并不擅长处理高度结构化和规则复杂的数据填充。这一步只能依靠 Python 原生脚本实现。我们使用 pandas 等工具,将处理好的结构化数据自动填入模板,实现批量文档输出。
可以实现:一键打印全部,包含8个合同、7个表单样式、97页文档,每月至少节省2人/天工作量。
自动邮件
第三大功能是智能邮件生成和发送。看似简单的邮件自动化,如果仅依靠模型生成内容,常常会缺乏具体业务语境。我们选择了一个巧妙的融合方式:通过前面生成的精准报表结果,让大模型编写出语义清晰且业务贴切的邮件内容,再通过 Python 自动发送邮件,实现无感式的业务流程闭环。
prompt 示例:
你是会议纪要生成专家,请根据如下规则,为每个月不同厂商生成标准格式的项目考核纪要。
【目录结构】
- 每个目录如 `json/3e9f7a`、`json/2a4b6c` 表示不同月份(如 3e9f7a 表示特定年月);
- 每个目录中包含:
- `a1b2c3.json`:包含厂商 A、厂商 B、厂商 C 各厂商的解析服务数据、评分、金额等结算信息;
- `d4e5f6.json`:包含当月各厂商的考核扣分项目和扣分事由;
- `7g8h9i/`:输出纪要存放路径,其中需生成:
- `x1.json` → 厂商A 纪要
- `y2.json` → 厂商B 纪要
- `z3.json` → 厂商C 纪要
【任务目标】
- 根据 `a1b2c3.json` 与 `d4e5f6.json` 两个文件,为每个厂商生成一个标准会议纪要(JSON格式);
- 每份纪要内容包括:会议信息、合同名称、考核评分、扣分详情、整改措施、结算信息;
- 请按以下映射关系提取数据生成:
- `"厂商A"` 对应 `x1.json`
- `"厂商B"` 对应 `y2.json`
- `"厂商C"` 对应 `z3.json`
【输出格式】
每个厂商输出一个 JSON 对象,结构统一如下:
{
"title": "...",
"meeting": {
"name": "特定年月考核评审会议纪要",
"date": "下一月份",
"participants": {
"公司X": ["人员A", "人员B", "人员C", "人员D", "人员E", "人员F", "人员G"],
"<厂商全称>": ["参会人列表"]
}
},
"contract": {
"name": "...",
"contract_number": "如有则填写,否则省略"
},
"assessment": {
"period": "特定年月",
"score_criteria": {
"指标1": 40,
"指标2": 20,
"指标3": 30,
"指标4": 10
},
"result": {
"评分": "...",
"扣分情况": "...",
"说明": "..."
},
"整改措施": { ...如有扣分则列出... }
},
"settlement": {
...每个模块的数据...
},
"note": "特此纪要"
}
【细节要求】
- 若某厂商无扣分,写明“扣分情况:无”,说明中突出“支撑稳定、无客户投诉”;
- 若有扣分,需写明模块、扣分原因、影响评分、整改方案;
- 所有金额含税,税率默认 6%,如有单价不同请按预处理文件给出;
- 会议名称与日期需自动根据目录名生成;
- 输出格式为 JSON,不能包含其他文字或注释;
请依次生成每个厂商的纪要,输出为 `7g8h9i/x1.json`、`y2.json`、`z3.json` 文件内容。
外部对接
第四个功能目前还在规划阶段:接入外部系统。我们希望未来实现系统与系统、模型与模型之间的直接交互。
小结
然而,实现前三个功能的过程中,我们也遇到了不少挑战。
1、首先是环境兼容性问题,比如内外网隔离导致的库依赖冲突。为了避免踩坑,我们严格控制依赖版本,争取做到一次构建,到处稳定运行。
2、其次是架构上的博弈。一开始我们希望打造一个通用的文档解析器,但实际运行后发现,业务场景差异巨大,通用型并不能真正适配所有情况。最终我们转向了“通用解析器 + 定制化脚本”的组合模式,既保持了通用性的框架,又能灵活响应不同业务的特殊需求。
3、最棘手的难点在于,大模型本质上并不会真正“思考”。它擅长的是即时响应,却无法贯穿历史上下文,缺乏整体一致性和逻辑重构的能力。为了解决这个问题,我们采用了代码规则前置、输出后验证、必要时人工闭环的三步组合策略,确保模型输出始终稳定可靠。
经过这一系列尝试,我想分享一个关键洞察:真正落地的大模型应用,不能只靠模型 API 堆叠功能,必须依托脚本能力、系统集成能力,以及业务场景的精准判断。