一、从一次线上事故说起去年冬天凌晨两点我被值班电话吵醒。客户那边一个自动化报表Agent跑崩了日志里只有一行MemoryError: cannot fit int into an index-sized integer。查了半天发现是Agent在处理一个300MB的Excel文件时用pandas.read_excel()直接全量加载把32GB的服务器内存吃光了。更坑的是这个Excel里还混着图片嵌入的批注——openpyxl解析时直接抛了InvalidFileException。从那以后我给自己定了个规矩Agent处理文件第一原则不是“能解析”而是“怎么优雅地崩”。今天这篇笔记就把我这两年踩过的坑、试过的方案从PDF到音频从解析到生成全盘托出。二、PDF别信“一行代码搞定”网上很多教程教你用PyPDF2或pdfplumber说“三行代码提取文本”。但真实场景里PDF是个大坑集合体。2.1 文本提取的“三座大山”第一座扫描件PDF你拿到的PDF可能根本不是文本而是一张张图片。PyPDF2提取出来全是空字符串。这时候得祭出OCRpytesseractpdf2image。但注意pdf2image依赖popplerWindows用户装起来能劝退一半人。我一般这样写frompdf2imageimportconvert_from_pathimportpytesseract# 这里踩过坑DPI设太低中文识别率暴跌设太高内存爆炸imagesconvert_from_path(report.pdf,dpi300,fmtjpeg)textforimginimages:# 别这样写直接传原图背景噪点会让OCR结果惨不忍睹# 先做预处理灰度化 二值化grayimg.convert(L)bwgray.point(lambdax:0ifx128else255)textpytesseract.image_to_string(bw,langchi_simeng)第二座表格PDFpdfplumber提取表格还行但遇到合并单元格、跨页表格结果就乱成一锅粥。我的经验是别指望一次提取完美。先提取原始表格数据再用规则修正importpdfplumberwithpdfplumber.open(invoice.pdf)aspdf:forpageinpdf.pages:tablespage.extract_tables()fortableintables:# 这里踩过坑空单元格返回None直接拼接会报错row_data[]forcellintable:# 别这样写cell.strip() 如果cell是None会AttributeErrorrow_data.append(cell.strip()ifcellelse)第三座加密PDF很多财务PDF有打开密码或权限密码。PyPDF2可以解密但只支持40位和128位RC4加密。遇到256位AES加密换pikepdfimportpikepdf# 别这样写硬编码密码在代码里被审计查到就凉了# 应该从环境变量或密钥管理服务读取passwordos.environ.get(PDF_PASSWORD)withpikepdf.open(encrypted.pdf,passwordpassword)aspdf:# 保存为无密码版本注意覆盖原文件有风险pdf.save(decrypted.pdf)2.2 PDF生成别用reportlab手写reportlab功能强大但API设计反人类。生成一个带中文的PDF字体配置能折腾半天。我现在只用fpdf2轻量且中文支持好fromfpdfimportFPDFclassPDF(FPDF):defheader(self):# 这里踩过坑set_font必须在add_page之后调用self.set_font(NotoSansSC,,12)self.cell(0,10,报告标题,alignC)pdfPDF()pdf.add_font(NotoSansSC,,NotoSansSC-Regular.ttf,uniTrue)pdf.add_page()pdf.set_font(NotoSansSC,,10)# 别这样写直接写长文本超出页面不会自动换行# 用multi_cell代替cellpdf.multi_cell(0,10,这是一段很长的中文文本会自动换行...)pdf.output(output.pdf)三、Excel从“读爆内存”到“流式处理”回到开头的故事。300MB的Excel用pandas.read_excel()直接读相当于把整个文件解压到内存。Excel本质是ZIP压缩包里面是XML文件。openpyxl的read_only模式可以流式读取fromopenpyxlimportload_workbook# 这里踩过坑不指定read_onlyTrue大文件直接OOMwbload_workbook(huge.xlsx,read_onlyTrue,data_onlyTrue)wswb.activeforrowinws.iter_rows(min_row1,max_row10000):# 别这样写row[0].value 如果单元格是公式且data_onlyTrue可能返回None# 应该先判断类型values[]forcellinrow:ifcell.valueisNone:values.append()elifisinstance(cell.value,datetime):values.append(cell.value.strftime(%Y-%m-%d))else:values.append(str(cell.value))# 处理这一行数据比如写入数据库process_row(values)wb.close()# 别忘了关闭否则文件句柄泄漏3.1 写入Excel别用pandas.to_excelpandas.to_excel方便但遇到大数据量写入速度慢得令人发指。而且它依赖openpyxl或xlsxwriter但默认配置下写入100万行数据内存占用会飙升。我改用xlsxwriter直接操作importxlsxwriter workbookxlsxwriter.Workbook(output.xlsx,{constant_memory:True})worksheetworkbook.add_worksheet()# 这里踩过坑不设置列宽中文可能显示不全worksheet.set_column(A:A,20)worksheet.set_column(B:B,30)# 写入表头header_formatworkbook.add_format({bold:True,bg_color:#D9E1F2})worksheet.write(A1,姓名,header_format)worksheet.write(B1,邮箱,header_format)# 流式写入内存恒定fori,rowinenumerate(data,start2):worksheet.write(i,0,row[name])worksheet.write(i,1,row[email])workbook.close()3.2 处理嵌入对象Excel里可能嵌着图片、PDF甚至视频。openpyxl可以读取图片但注意图片是作为PIL.Image对象存在的fromopenpyxlimportload_workbookfromopenpyxl.drawing.imageimportImageasXLImage wbload_workbook(with_images.xlsx)wswb.activeforimageinws._images:# 这里踩过坑image.ref 可能不存在需要先判断ifhasattr(image,ref):img_dataimage.ref# 这是BytesIO对象# 保存到文件withopen(extracted.png,wb)asf:f.write(img_data.getvalue())四、图片Agent的“眼睛”不能瞎Agent处理图片最常见的是OCR和分类。但图片格式、分辨率、色彩空间每个细节都能坑你。4.1 图片解析统一格式是第一步用户上传的图片可能是PNG、JPG、WebP甚至BMP。我习惯先统一转成RGB模式的PNGfromPILimportImageimportiodefnormalize_image(image_bytes):# 这里踩过坑直接Image.open(BytesIO)可能遇到截断的图片try:imgImage.open(io.BytesIO(image_bytes))exceptExceptionase:# 别这样写直接抛异常应该返回错误信息给AgentreturnNone,f图片损坏:{str(e)}# 转换色彩模式ifimg.mode!RGB:imgimg.convert(RGB)# 限制最大尺寸防止OCR时内存溢出max_size(2048,2048)img.thumbnail(max_size,Image.LANCZOS)# 保存为PNG字节流outputio.BytesIO()img.save(output,formatPNG)returnoutput.getvalue(),None4.2 OCR增强不是所有图片都适合直接识别对于扫描件、票据直接OCR效果很差。我总结了一套预处理流程importcv2importnumpyasnpdefpreprocess_for_ocr(image_path):imgcv2.imread(image_path)# 1. 灰度化graycv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 2. 去噪高斯模糊核大小根据图片分辨率调整blurredcv2.GaussianBlur(gray,(5,5),0)# 3. 二值化OTSU自动阈值_,binarycv2.threshold(blurred,0,255,cv2.THRESH_BINARYcv2.THRESH_OTSU)# 4. 去边框很多票据有表格线干扰OCR# 这里踩过坑直接腐蚀膨胀会丢失文字细节kernelnp.ones((2,2),np.uint8)cleanedcv2.morphologyEx(binary,cv2.MORPH_CLOSE,kernel)returncleaned4.3 图片生成别用PIL画复杂图表Agent需要生成图表时matplotlib是首选但注意中文字体问题importmatplotlib.pyplotaspltimportmatplotlibfrommatplotlib.font_managerimportFontProperties# 这里踩过坑不指定字体中文显示为方框fontFontProperties(fname/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc)matplotlib.rcParams[font.family]font.get_name()fig,axplt.subplots()ax.plot(x,y)ax.set_title(销售趋势图,fontpropertiesfont)# 别这样写直接plt.savefig背景默认白色但透明背景在某些场景更好plt.savefig(chart.png,dpi150,bbox_inchestight,transparentFalse)plt.close()五、音频从语音到文本的“最后一公里”音频处理在Agent场景里越来越常见比如语音助手、会议记录。但音频格式、采样率、噪声每个环节都可能翻车。5.1 音频解析格式转换是必修课用户上传的音频可能是MP3、WAV、FLAC甚至AMR。pydub可以处理大部分格式但依赖ffmpegfrompydubimportAudioSegment# 这里踩过坑不指定采样率语音识别模型可能报错audioAudioSegment.from_file(input.mp3)audioaudio.set_frame_rate(16000)# 大多数ASR模型要求16kHzaudioaudio.set_channels(1)# 单声道audioaudio.set_sample_width(2)# 16位PCM# 导出为WAV方便后续处理audio.export(output.wav,formatwav)5.2 语音识别别用免费API做生产环境很多教程推荐speech_recognition库但它底层调的是Google、IBM的免费API有调用次数限制而且延迟不稳定。生产环境我推荐两种方案方案一本地模型Vosk适合离线场景但模型文件较大~50MB中文识别率一般fromvoskimportModel,KaldiRecognizerimportwave modelModel(vosk-model-cn-0.22)wfwave.open(output.wav,rb)recKaldiRecognizer(model,wf.getframerate())whileTrue:datawf.readframes(4000)iflen(data)0:breakifrec.AcceptWaveform(data):resultrec.Result()# 解析JSON提取文本textjson.loads(result)[text]方案二云端API阿里云/腾讯云延迟低准确率高但注意并发限制和费用。我一般封装成异步调用importasyncioimportaiohttpasyncdefrecognize_audio(audio_bytes):# 这里踩过坑不设置超时网络波动时请求会挂起timeoutaiohttp.ClientTimeout(total30)asyncwithaiohttp.ClientSession(timeouttimeout)assession:# 假设是阿里云语音识别APIurlhttps://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asrheaders{Authorization:Bearer get_token()}asyncwithsession.post(url,dataaudio_bytes,headersheaders)asresp:ifresp.status200:resultawaitresp.json()returnresult[result]else:# 别这样写直接返回空字符串应该记录错误并重试returnNone5.3 音频生成TTS的坑Agent需要语音回复时pyttsx3是离线方案但声音机械感强。在线TTS如Azure、百度效果好但注意流式播放importazure.cognitiveservices.speechasspeechsdk speech_configspeechsdk.SpeechConfig(subscriptionos.environ[AZURE_KEY],regioneastasia)speech_config.speech_synthesis_voice_namezh-CN-XiaoxiaoNeuralsynthesizerspeechsdk.SpeechSynthesizer(speech_configspeech_config)# 这里踩过坑不设置SSML语速、音调无法控制ssmlf speak version1.0 xmlnshttp://www.w3.org/2001/10/synthesis xml:langzh-CN voice namezh-CN-XiaoxiaoNeural prosody rate10% pitch5%{text}/prosody /voice /speak resultsynthesizer.speak_ssml_async(ssml).get()ifresult.reasonspeechsdk.ResultReason.SynthesizingAudioCompleted:audio_dataresult.audio_data# 保存或播放六、个人经验性建议文件处理一定要有超时和大小限制。Agent面对的是不可控的用户输入一个100MB的PDF、10小时的音频如果不加限制能把整个系统拖垮。我一般在入口处就做校验文件大小超过50MB直接拒绝处理时间超过30秒就中断。临时文件管理是隐形炸弹。解析PDF、转换音频都会产生临时文件如果不清理磁盘很快会被占满。我习惯用tempfile.NamedTemporaryFile并设置deleteTrue或者用with语句确保释放。错误处理要分层。文件损坏、格式不支持、API超时每种错误都应该有对应的降级策略。比如PDF解析失败可以尝试用OCR兜底语音识别超时可以返回“暂时无法识别请稍后重试”。日志要记录文件指纹。出问题时光看“文件解析失败”根本没法排查。我每次处理文件都会记录MD5、文件大小、处理耗时方便事后回溯。别迷信“万能解析库”。没有哪个库能处理所有文件变体。PDF有扫描件、加密、表单、注释Excel有宏、嵌入对象、数据验证。我的做法是先尝试主流库失败后用备用方案再失败就返回明确的错误信息而不是让Agent卡死。文件处理是Agent连接物理世界的桥梁也是最容易出幺蛾子的环节。记住能优雅地失败比勉强地成功更重要。
# 026 Agent 的文件处理:PDF、Excel、图片、音频的解析与生成
发布时间:2026/7/18 0:56:48
一、从一次线上事故说起去年冬天凌晨两点我被值班电话吵醒。客户那边一个自动化报表Agent跑崩了日志里只有一行MemoryError: cannot fit int into an index-sized integer。查了半天发现是Agent在处理一个300MB的Excel文件时用pandas.read_excel()直接全量加载把32GB的服务器内存吃光了。更坑的是这个Excel里还混着图片嵌入的批注——openpyxl解析时直接抛了InvalidFileException。从那以后我给自己定了个规矩Agent处理文件第一原则不是“能解析”而是“怎么优雅地崩”。今天这篇笔记就把我这两年踩过的坑、试过的方案从PDF到音频从解析到生成全盘托出。二、PDF别信“一行代码搞定”网上很多教程教你用PyPDF2或pdfplumber说“三行代码提取文本”。但真实场景里PDF是个大坑集合体。2.1 文本提取的“三座大山”第一座扫描件PDF你拿到的PDF可能根本不是文本而是一张张图片。PyPDF2提取出来全是空字符串。这时候得祭出OCRpytesseractpdf2image。但注意pdf2image依赖popplerWindows用户装起来能劝退一半人。我一般这样写frompdf2imageimportconvert_from_pathimportpytesseract# 这里踩过坑DPI设太低中文识别率暴跌设太高内存爆炸imagesconvert_from_path(report.pdf,dpi300,fmtjpeg)textforimginimages:# 别这样写直接传原图背景噪点会让OCR结果惨不忍睹# 先做预处理灰度化 二值化grayimg.convert(L)bwgray.point(lambdax:0ifx128else255)textpytesseract.image_to_string(bw,langchi_simeng)第二座表格PDFpdfplumber提取表格还行但遇到合并单元格、跨页表格结果就乱成一锅粥。我的经验是别指望一次提取完美。先提取原始表格数据再用规则修正importpdfplumberwithpdfplumber.open(invoice.pdf)aspdf:forpageinpdf.pages:tablespage.extract_tables()fortableintables:# 这里踩过坑空单元格返回None直接拼接会报错row_data[]forcellintable:# 别这样写cell.strip() 如果cell是None会AttributeErrorrow_data.append(cell.strip()ifcellelse)第三座加密PDF很多财务PDF有打开密码或权限密码。PyPDF2可以解密但只支持40位和128位RC4加密。遇到256位AES加密换pikepdfimportpikepdf# 别这样写硬编码密码在代码里被审计查到就凉了# 应该从环境变量或密钥管理服务读取passwordos.environ.get(PDF_PASSWORD)withpikepdf.open(encrypted.pdf,passwordpassword)aspdf:# 保存为无密码版本注意覆盖原文件有风险pdf.save(decrypted.pdf)2.2 PDF生成别用reportlab手写reportlab功能强大但API设计反人类。生成一个带中文的PDF字体配置能折腾半天。我现在只用fpdf2轻量且中文支持好fromfpdfimportFPDFclassPDF(FPDF):defheader(self):# 这里踩过坑set_font必须在add_page之后调用self.set_font(NotoSansSC,,12)self.cell(0,10,报告标题,alignC)pdfPDF()pdf.add_font(NotoSansSC,,NotoSansSC-Regular.ttf,uniTrue)pdf.add_page()pdf.set_font(NotoSansSC,,10)# 别这样写直接写长文本超出页面不会自动换行# 用multi_cell代替cellpdf.multi_cell(0,10,这是一段很长的中文文本会自动换行...)pdf.output(output.pdf)三、Excel从“读爆内存”到“流式处理”回到开头的故事。300MB的Excel用pandas.read_excel()直接读相当于把整个文件解压到内存。Excel本质是ZIP压缩包里面是XML文件。openpyxl的read_only模式可以流式读取fromopenpyxlimportload_workbook# 这里踩过坑不指定read_onlyTrue大文件直接OOMwbload_workbook(huge.xlsx,read_onlyTrue,data_onlyTrue)wswb.activeforrowinws.iter_rows(min_row1,max_row10000):# 别这样写row[0].value 如果单元格是公式且data_onlyTrue可能返回None# 应该先判断类型values[]forcellinrow:ifcell.valueisNone:values.append()elifisinstance(cell.value,datetime):values.append(cell.value.strftime(%Y-%m-%d))else:values.append(str(cell.value))# 处理这一行数据比如写入数据库process_row(values)wb.close()# 别忘了关闭否则文件句柄泄漏3.1 写入Excel别用pandas.to_excelpandas.to_excel方便但遇到大数据量写入速度慢得令人发指。而且它依赖openpyxl或xlsxwriter但默认配置下写入100万行数据内存占用会飙升。我改用xlsxwriter直接操作importxlsxwriter workbookxlsxwriter.Workbook(output.xlsx,{constant_memory:True})worksheetworkbook.add_worksheet()# 这里踩过坑不设置列宽中文可能显示不全worksheet.set_column(A:A,20)worksheet.set_column(B:B,30)# 写入表头header_formatworkbook.add_format({bold:True,bg_color:#D9E1F2})worksheet.write(A1,姓名,header_format)worksheet.write(B1,邮箱,header_format)# 流式写入内存恒定fori,rowinenumerate(data,start2):worksheet.write(i,0,row[name])worksheet.write(i,1,row[email])workbook.close()3.2 处理嵌入对象Excel里可能嵌着图片、PDF甚至视频。openpyxl可以读取图片但注意图片是作为PIL.Image对象存在的fromopenpyxlimportload_workbookfromopenpyxl.drawing.imageimportImageasXLImage wbload_workbook(with_images.xlsx)wswb.activeforimageinws._images:# 这里踩过坑image.ref 可能不存在需要先判断ifhasattr(image,ref):img_dataimage.ref# 这是BytesIO对象# 保存到文件withopen(extracted.png,wb)asf:f.write(img_data.getvalue())四、图片Agent的“眼睛”不能瞎Agent处理图片最常见的是OCR和分类。但图片格式、分辨率、色彩空间每个细节都能坑你。4.1 图片解析统一格式是第一步用户上传的图片可能是PNG、JPG、WebP甚至BMP。我习惯先统一转成RGB模式的PNGfromPILimportImageimportiodefnormalize_image(image_bytes):# 这里踩过坑直接Image.open(BytesIO)可能遇到截断的图片try:imgImage.open(io.BytesIO(image_bytes))exceptExceptionase:# 别这样写直接抛异常应该返回错误信息给AgentreturnNone,f图片损坏:{str(e)}# 转换色彩模式ifimg.mode!RGB:imgimg.convert(RGB)# 限制最大尺寸防止OCR时内存溢出max_size(2048,2048)img.thumbnail(max_size,Image.LANCZOS)# 保存为PNG字节流outputio.BytesIO()img.save(output,formatPNG)returnoutput.getvalue(),None4.2 OCR增强不是所有图片都适合直接识别对于扫描件、票据直接OCR效果很差。我总结了一套预处理流程importcv2importnumpyasnpdefpreprocess_for_ocr(image_path):imgcv2.imread(image_path)# 1. 灰度化graycv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 2. 去噪高斯模糊核大小根据图片分辨率调整blurredcv2.GaussianBlur(gray,(5,5),0)# 3. 二值化OTSU自动阈值_,binarycv2.threshold(blurred,0,255,cv2.THRESH_BINARYcv2.THRESH_OTSU)# 4. 去边框很多票据有表格线干扰OCR# 这里踩过坑直接腐蚀膨胀会丢失文字细节kernelnp.ones((2,2),np.uint8)cleanedcv2.morphologyEx(binary,cv2.MORPH_CLOSE,kernel)returncleaned4.3 图片生成别用PIL画复杂图表Agent需要生成图表时matplotlib是首选但注意中文字体问题importmatplotlib.pyplotaspltimportmatplotlibfrommatplotlib.font_managerimportFontProperties# 这里踩过坑不指定字体中文显示为方框fontFontProperties(fname/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc)matplotlib.rcParams[font.family]font.get_name()fig,axplt.subplots()ax.plot(x,y)ax.set_title(销售趋势图,fontpropertiesfont)# 别这样写直接plt.savefig背景默认白色但透明背景在某些场景更好plt.savefig(chart.png,dpi150,bbox_inchestight,transparentFalse)plt.close()五、音频从语音到文本的“最后一公里”音频处理在Agent场景里越来越常见比如语音助手、会议记录。但音频格式、采样率、噪声每个环节都可能翻车。5.1 音频解析格式转换是必修课用户上传的音频可能是MP3、WAV、FLAC甚至AMR。pydub可以处理大部分格式但依赖ffmpegfrompydubimportAudioSegment# 这里踩过坑不指定采样率语音识别模型可能报错audioAudioSegment.from_file(input.mp3)audioaudio.set_frame_rate(16000)# 大多数ASR模型要求16kHzaudioaudio.set_channels(1)# 单声道audioaudio.set_sample_width(2)# 16位PCM# 导出为WAV方便后续处理audio.export(output.wav,formatwav)5.2 语音识别别用免费API做生产环境很多教程推荐speech_recognition库但它底层调的是Google、IBM的免费API有调用次数限制而且延迟不稳定。生产环境我推荐两种方案方案一本地模型Vosk适合离线场景但模型文件较大~50MB中文识别率一般fromvoskimportModel,KaldiRecognizerimportwave modelModel(vosk-model-cn-0.22)wfwave.open(output.wav,rb)recKaldiRecognizer(model,wf.getframerate())whileTrue:datawf.readframes(4000)iflen(data)0:breakifrec.AcceptWaveform(data):resultrec.Result()# 解析JSON提取文本textjson.loads(result)[text]方案二云端API阿里云/腾讯云延迟低准确率高但注意并发限制和费用。我一般封装成异步调用importasyncioimportaiohttpasyncdefrecognize_audio(audio_bytes):# 这里踩过坑不设置超时网络波动时请求会挂起timeoutaiohttp.ClientTimeout(total30)asyncwithaiohttp.ClientSession(timeouttimeout)assession:# 假设是阿里云语音识别APIurlhttps://nls-gateway.cn-shanghai.aliyuncs.com/stream/v1/asrheaders{Authorization:Bearer get_token()}asyncwithsession.post(url,dataaudio_bytes,headersheaders)asresp:ifresp.status200:resultawaitresp.json()returnresult[result]else:# 别这样写直接返回空字符串应该记录错误并重试returnNone5.3 音频生成TTS的坑Agent需要语音回复时pyttsx3是离线方案但声音机械感强。在线TTS如Azure、百度效果好但注意流式播放importazure.cognitiveservices.speechasspeechsdk speech_configspeechsdk.SpeechConfig(subscriptionos.environ[AZURE_KEY],regioneastasia)speech_config.speech_synthesis_voice_namezh-CN-XiaoxiaoNeuralsynthesizerspeechsdk.SpeechSynthesizer(speech_configspeech_config)# 这里踩过坑不设置SSML语速、音调无法控制ssmlf speak version1.0 xmlnshttp://www.w3.org/2001/10/synthesis xml:langzh-CN voice namezh-CN-XiaoxiaoNeural prosody rate10% pitch5%{text}/prosody /voice /speak resultsynthesizer.speak_ssml_async(ssml).get()ifresult.reasonspeechsdk.ResultReason.SynthesizingAudioCompleted:audio_dataresult.audio_data# 保存或播放六、个人经验性建议文件处理一定要有超时和大小限制。Agent面对的是不可控的用户输入一个100MB的PDF、10小时的音频如果不加限制能把整个系统拖垮。我一般在入口处就做校验文件大小超过50MB直接拒绝处理时间超过30秒就中断。临时文件管理是隐形炸弹。解析PDF、转换音频都会产生临时文件如果不清理磁盘很快会被占满。我习惯用tempfile.NamedTemporaryFile并设置deleteTrue或者用with语句确保释放。错误处理要分层。文件损坏、格式不支持、API超时每种错误都应该有对应的降级策略。比如PDF解析失败可以尝试用OCR兜底语音识别超时可以返回“暂时无法识别请稍后重试”。日志要记录文件指纹。出问题时光看“文件解析失败”根本没法排查。我每次处理文件都会记录MD5、文件大小、处理耗时方便事后回溯。别迷信“万能解析库”。没有哪个库能处理所有文件变体。PDF有扫描件、加密、表单、注释Excel有宏、嵌入对象、数据验证。我的做法是先尝试主流库失败后用备用方案再失败就返回明确的错误信息而不是让Agent卡死。文件处理是Agent连接物理世界的桥梁也是最容易出幺蛾子的环节。记住能优雅地失败比勉强地成功更重要。