1. 项目概述:当MATLAB分析遇上Agentic AI

如果你和我一样,长期和MATLAB打交道,那你一定对下面这个场景深有体会:花了好几天时间,终于把一组复杂的数据分析跑通了,模型拟合得漂亮,图表也画得清晰。然后,老板或者合作者问你要一份分析报告。你看着满屏幕的.m脚本文件、Workspace里一堆临时变量、还有散落在各处的Figure窗口,瞬间头大——要把这些零散的代码、结果和思路,整理成一份逻辑清晰、可复现、甚至带点解释说明的文档,所花费的时间和精力,有时甚至不亚于重新做一次分析。

这就是“Automate the Documentation of Your MATLAB Analysis with Agentic AI”这个项目要解决的核心痛点。它不是一个简单的代码注释生成器,而是一个基于“智能体”(Agentic AI)理念构建的自动化工作流,旨在成为你的分析搭档。想象一下,有一个“数字助手”能理解你分析脚本的意图,跟踪你的数据处理流程,自动抓取关键结果,并以你预设的格式(比如Word、PDF、HTML,甚至是交互式仪表盘)生成结构化的分析文档。这不仅仅是节省时间,更是将分析过程的“暗知识”——那些你脑子里知道但没写出来的逻辑、参数选择的理由、异常值的处理依据——显性化、标准化。

对于数据分析师、算法工程师、科研人员来说,它的价值在于 提升分析工作的可追溯性、可复现性和协作效率 。你不再需要事后“补作业”,而是在分析进行的同时,文档就在同步生长。Agentic AI在这里扮演的角色,是理解上下文、执行特定任务(如提取代码块、解析变量、生成描述)并做出决策的智能体,它让文档生成从“基于模板的填充”升级为“基于理解的创作”。

2. 核心思路与架构设计

2.1 为什么是“Agentic AI”而不仅仅是“AI”?

在讨论具体实现前,有必要厘清“Agentic AI”在此场景下的独特含义。普通的自动化脚本或代码分析工具,遵循的是固定的“如果-那么”规则。例如,“如果检测到 plot 函数,就截图”。这种方式僵硬,无法处理复杂或非标准的代码结构。

Agentic AI,或者说智能体范式,引入了 目标导向、感知-决策-行动循环、以及工具使用 的能力。在这个项目中,智能体的“目标”是生成一份高质量的分析文档。为了实现这个目标,它需要:

  1. 感知(Perception) :读取并理解MATLAB脚本的内容、结构、执行环境(Workspace中的变量)和输出(图形、控制台信息)。
  2. 决策(Decision) :判断当前代码段属于数据分析的哪个环节(数据加载、预处理、建模、可视化、结果评估),并决定需要记录哪些关键信息。
  3. 行动(Action) :调用相应的工具来执行任务,例如:
    • 调用 publish 函数或自定义解析器来获取代码和输出。
    • 使用MATLAB的图形捕获功能保存图表。
    • 调用大型语言模型(LLM)API,为复杂的算法步骤生成自然语言描述。
    • 按照预定义的模板(如Jupyter Notebook、LaTeX、Microsoft Word),将内容组装成最终文档。

这个循环是动态的、可交互的。例如,智能体发现某个模型的拟合优度(R²)异常低,它可能会在文档中自动插入一个“注意”段落,提示用户检查数据或模型假设,而不仅仅是机械地报告这个数字。

2.2 系统架构拆解

一个可行的自动化文档生成系统,可以设计为以下几个核心模块:

1. 代码与执行上下文监听器(Listener) 这个模块是智能体的“眼睛和耳朵”。它需要深度集成到MATLAB环境或紧密监控分析过程。

  • 实现方式一(侵入式,高控制力) :创建一个自定义的MATLAB类或工具包。用户的分析脚本需要显式地调用这个工具包的函数,来标记分析阶段(如 docu.startSection('数据预处理') )和提交关键结果(如 docu.recordFigure(gcf, ‘相关性分析图’) )。这种方式给了智能体最明确的指令。
  • 实现方式二(非侵入式,高自动化) :开发一个独立的“监视器”应用。它通过MATLAB Engine API或文件系统监听,来跟踪指定脚本文件的执行。它可以捕获控制台输出、监测Workspace中特定变量的变化(如变量名包含 result , model , stats 等),并定期扫描打开的图形窗口。这种方式对用户原有代码改动最小,但实现复杂度更高,对上下文的解读更依赖推理。

2. 分析与规划引擎(Planner) 这是智能体的“大脑”。它接收监听器传来的原始信息(代码片段、变量、图形句柄),并对其进行分类和规划。

  • 分类 :基于启发式规则或轻量级机器学习模型(如文本分类),判断当前代码块的功能。例如,包含 readtable xlsread 的代码块被分类为“数据导入”;包含 fitlm fitrgp 的被分类为“模型训练”。
  • 规划 :根据分类结果,规划需要采集的信息单元。例如,对于“模型训练”,规划可能包括:记录使用的函数和参数、提取模型对象的关键属性(如系数、R²)、评估模型在测试集上的性能指标。

3. 内容生成与组装器(Assembler) 这是智能体的“手”。它负责执行规划好的任务,生成最终的文档内容。

  • 文本描述生成 :对于复杂的分析步骤,直接粘贴代码可能不够友好。这里可以集成LLM(如通过API调用GPT-4、Claude或本地部署的类似模型)。将代码片段、变量信息作为提示词(Prompt)输入,请求模型生成一段简洁、专业的描述。例如,提示词可以是:“请用一句话解释以下MATLAB代码在做什么,并说明参数‘RobustOpts’设置为‘on’的意义:[代码片段]”。
  • 多媒体内容处理 :自动将图形保存为高分辨率图片(如 .png .svg ),并生成带有编号和简要说明的图注。
  • 模板渲染 :使用像Jinja2(通过MATLAB的Python接口调用)或纯MATLAB的 fprintf /字符串操作,将生成的所有内容块(标题、描述、代码、结果表格、图片)填充到预定义的文档模板中,最终输出为 .html , .md , .docx .pdf

4. 用户配置与交互界面(Interface) 智能体不是完全自主的,它需要接受用户的指导和偏好设置。

  • 配置面板 :允许用户选择输出格式、文档模板、需要跳过的代码部分(如繁琐的路径设置)、以及LLM的调用参数(如API密钥、生成风格)。
  • 交互式修正 :生成初稿后,用户可能希望对某些描述进行微调。系统可以提供界面,让用户对智能体生成的文本进行快速编辑或给出反馈(如“这段描述太技术化了,请用更通俗的语言重写”),这些反馈可以被系统学习,用于优化后续的生成。

注意 :LLM的集成需要谨慎处理代码和数据的隐私问题。对于涉密或敏感项目,可以考虑使用本地化部署的开源模型,或者仅在不发送核心数据的前提下,使用LLM来润色已由本地规则生成的文本描述。

3. 关键技术点与实操实现

3.1 感知层实现:捕获MATLAB运行时信息

感知层的目标是可靠地获取分析过程的全貌。这里提供两种实操路径。

路径A:基于MATLAB publish 函数的增强改造 MATLAB自带的 publish 函数能将脚本输出为HTML、Word等格式,是一个很好的起点,但它生成的文档比较“呆板”,缺乏智能描述。

% 基础用法
publish(‘my_analysis.m’, ‘format’, ‘html’, ‘outputDir’, ‘./report’);

我们可以通过重写或扩展 publish 的中间生成物来实现增强。 publish 函数在内部会先将.m文件转换为一个中间XML文件。我们可以拦截这个过程:

  1. 使用 xmlread 解析生成的中间XML。
  2. 从中提取出独立的代码单元(code cell)及其对应的输出(文本、图形)。
  3. 对这些单元进行更精细的分析(即交给规划引擎),而不是原样输出。

路径B:构建自定义的日志装饰器 这种方法更灵活,对代码的侵入性是可管理的。我们创建一个 DocuAgent 类,它在用户代码中作为“装饰器”使用。

classdef DocuAgent < handle
    properties
        current_section = ‘Untitled‘;
        figures = {};
        logs = {};
    end
    
    methods
        function obj = startSection(obj, sectionName)
            obj.current_section = sectionName;
            obj.logs{end+1} = struct(‘type‘, ‘section‘, ‘title‘, sectionName, ‘timestamp‘, datetime);
            fprintf(‘[DocuAgent] 进入章节: %s\n‘, sectionName);
        end
        
        function recordFigure(obj, figHandle, caption)
            % 保存图形
            figFile = sprintf(‘figure_%s_%03d.png‘, obj.current_section, length(obj.figures)+1);
            saveas(figHandle, fullfile(‘./report/assets‘, figFile));
            % 记录信息
            figInfo = struct(‘file‘, figFile, ‘caption‘, caption, ‘section‘, obj.current_section);
            obj.figures{end+1} = figInfo;
            obj.logs{end+1} = struct(‘type‘, ‘figure‘, ‘info‘, figInfo);
        end
        
        function recordVariable(obj, varName, description)
            % 记录关键变量及其描述
            if evalin(‘base‘, sprintf(‘exist(”%s”, ”var”)‘, varName))
                varValue = evalin(‘base‘, varName);
                % 这里可以尝试获取变量的摘要信息,对于表格、结构体特别有用
                if istable(varValue)
                    summary = sprintf(‘Table: %d rows × %d columns‘, size(varValue,1), size(varValue,2));
                elseif isstruct(varValue)
                    summary = sprintf(‘Struct with fields: %s‘, strjoin(fieldnames(varValue), ‘, ‘));
                else
                    summary = sprintf(‘%s [%s]‘, class(varValue), mat2str(size(varValue)));
                end
                obj.logs{end+1} = struct(‘type‘, ‘variable‘, ‘name‘, varName, ‘summary‘, summary, ‘desc‘, description);
            end
        end
    end
end

在用户脚本中,可以这样使用:

% 用户的分析脚本
docu = DocuAgent();
docu.startSection(‘1. 数据加载与探索‘);

data = readtable(‘sales_data.csv‘);
docu.recordVariable(‘data‘, ‘原始销售数据表,包含日期、产品、销售额等字段‘);

% 绘制趋势图
figure;
plot(data.Date, data.Revenue);
title(‘月度销售额趋势‘);
docu.recordFigure(gcf, ‘月度销售额趋势图,可见明显的季节性波动‘);

docu.startSection(‘2. 线性回归模型‘);
mdl = fitlm(data, ‘Revenue ~ Month + Promotion‘);
docu.recordVariable(‘mdl‘, ‘多元线性回归模型对象‘);

这种方式将记录动作无缝嵌入分析流程,为智能体提供了最精准、结构化的上下文信息。

3.2 规划与决策层:上下文分析与任务分解

规划引擎需要处理 DocuAgent 收集的日志,或直接解析代码。一个简单的规则引擎可以这样构建:

  1. 阶段识别 :通过日志中的 section 标题或分析代码中的关键函数,确定分析阶段。我们可以维护一个“阶段-关键词”映射字典:
    stageKeywords = {
        ‘数据导入‘, {‘readtable‘, ‘xlsread‘, ‘load‘, ‘importdata‘};
        ‘数据清洗‘, {‘rmmissing‘, ‘fillmissing‘, ‘normalize‘, ‘smooth‘};
        ‘探索性分析‘, {‘histogram‘, ‘corrplot‘, ‘summary‘, ‘boxplot‘};
        ‘建模‘, {‘fitlm‘, ‘fitrgp‘, ‘trainNetwork‘, ‘kmeans‘};
        ‘评估‘, {‘predict‘, ‘residuals‘, ‘confusionmat‘, ‘rsquared‘};
    };
    
  2. 信息提取规则 :针对每个阶段,定义需要提取什么。
    • 建模阶段 :提取模型类型、公式、关键参数、拟合结果(如系数、R²)。
    • 可视化阶段 :确保每个图形都被捕获,并尝试从 title , xlabel , ylabel 或附近的注释中自动生成图注。
  3. LLM任务触发规则 :并非所有内容都需要LLM。规则可以设定为:当遇到一个新的分析阶段标题时,或当记录了一个复杂的模型变量时,触发LLM生成该阶段的概述或模型原理的简要说明。

3.3 内容生成层:集成LLM与模板渲染

这是让文档“有灵魂”的一步。我们需要安全、有效地调用LLM。

步骤1:构建提示词(Prompt)工程 提示词的质量直接决定输出文本的实用性。一个好的提示词应包含:

  • 角色 :你是一个资深数据分析师,正在为你的工作撰写文档。
  • 任务 :根据提供的MATLAB代码和上下文,生成一段简洁、专业、易于理解的描述。
  • 上下文 :提供当前章节标题、代码片段、关键变量的名称和类型。
  • 格式要求 :输出纯文本,不要包含Markdown格式,语言为中文。

示例提示词模板:

你是一位数据分析专家。请为以下MATLAB分析步骤撰写一段文档说明。
分析阶段:[此处插入阶段名,如“数据标准化处理”]
代码摘要:[此处插入关键的1-3行代码]
涉及的变量:[此处插入变量名和简要描述,如“`X_raw` (原始特征矩阵, 1000x5)”, “`X_scaled` (标准化后的矩阵)”]
请用一两句话解释这个步骤的目的和采用的方法。直接输出说明文字,不要加引号。

步骤2:在MATLAB中调用LLM API 我们可以使用MATLAB的 webwrite 函数来调用OpenAI或兼容OpenAI API的本地模型。

function response = callLLM(prompt, apiKey, model)
    url = ‘https://api.openai.com/v1/chat/completions‘;
    headers = [‘Authorization‘, sprintf(‘Bearer %s‘, apiKey); ‘Content-Type‘, ‘application/json‘];
    data = struct(…
        ‘model‘, model, … % 例如 ‘gpt-4-turbo-preview‘
        ‘messages‘, {{struct(‘role‘, ‘user‘, ‘content‘, prompt)}}, …
        ‘temperature‘, 0.7, …
        ‘max_tokens‘, 500);
    options = weboptions(‘HeaderFields‘, headers, ‘MediaType‘, ‘application/json‘, ‘Timeout‘, 30);
    
    try
        result = webwrite(url, data, options);
        response = result.choices(1).message.content;
    catch ME
        warning(‘LLM调用失败: %s‘, ME.message);
        response = ‘‘; % 失败时返回空,或一个默认描述
    end
end

步骤3:模板渲染与组装 最后,将所有生成的内容(章节标题、LLM描述、代码块、变量摘要表、图片引用)填充到模板中。以生成Markdown为例:

function generateMarkdown(docuAgent, outputPath)
    fid = fopen(outputPath, ‘w‘, ‘n‘, ‘UTF-8‘);
    fprintf(fid, ‘# 自动化分析报告\n\n‘);
    fprintf(fid, ‘> 本报告由MATLAB分析智能体于 %s 自动生成\n\n‘, datetime);
    
    currentSection = ‘‘;
    for i = 1:length(docuAgent.logs)
        log = docuAgent.logs{i};
        switch log.type
            case ‘section‘
                if ~strcmp(log.title, currentSection)
                    fprintf(fid, ‘\n## %s\n\n‘, log.title);
                    currentSection = log.title;
                    % 可以在这里插入调用LLM生成的章节概述
                    sectionPrompt = sprintf(‘请为“%s”这个数据分析章节写一段简要概述。‘, log.title);
                    overview = callLLM(sectionPrompt, yourApiKey, yourModel);
                    if ~isempty(overview)
                        fprintf(fid, ‘%s\n\n‘, overview);
                    end
                end
            case ‘figure‘
                fprintf(fid, ‘![%s](assets/%s)\n\n‘, log.info.caption, log.info.file);
                fprintf(fid, ‘*图 %d: %s*\n\n‘, i, log.info.caption);
            case ‘variable‘
                fprintf(fid, ‘**变量 `%s`**: %s。%s\n\n‘, log.name, log.summary, log.desc);
            % … 可以处理其他类型,如‘code‘
        end
    end
    fclose(fid);
end

4. 部署模式与集成方案

4.1 轻量级部署:作为MATLAB工具包

这是最直接的方式。将上述核心类( DocuAgent )和辅助函数打包成一个MATLAB工具箱( .mltbx 文件)。用户安装后,即可在自己的脚本中通过几行代码初始化智能体并开始记录。

优势

  • 部署简单,与MATLAB环境无缝集成。
  • 用户控制力强,可以精细地控制记录的内容和时机。
  • 适合对代码有修改权限的个人或小团队。

实操步骤

  1. 在MATLAB中,使用“打包工具”将你的 DocuAgent 类、 callLLM 函数、模板文件等打包。
  2. 编写详细的安装和使用说明,特别是如何配置LLM API密钥(建议通过环境变量或配置文件读取,避免硬编码在代码中)。
  3. 提供一个示例脚本 demo_usage.m ,展示从数据导入到生成报告的全流程。

4.2 后台服务模式:监听与自动触发

对于更自动化的场景,可以开发一个独立的桌面应用或系统服务。这个服务持续运行,监控指定文件夹下的MATLAB脚本文件(通过文件系统的最后修改时间戳)。一旦检测到脚本执行完毕(例如,关联的 .mat 结果文件被更新),便自动启动一个MATLAB引擎进程,在后台执行该脚本,同时注入文档生成代码,最终产出报告。

优势

  • 完全自动化,用户无需修改分析脚本。
  • 适合标准化分析流水线,如每日/每周定时运行的数据报告。
  • 可以将文档生成与版本控制系统(如Git)结合,每次提交代码后自动生成最新文档。

技术要点

  • 使用MATLAB Engine API for Python/Java/.NET来在外部程序中调用MATLAB。
  • 设计一个可靠的“脚本执行完毕”检测机制,避免重复触发。
  • 处理好多个分析任务并发的资源调度问题。

5. 避坑指南与实战心得

在实际构建和使用这样一个系统的过程中,我踩过不少坑,也积累了一些经验。

坑1:代码解析的复杂性 最初试图完全通过静态分析代码来理解意图,这几乎是不可能的任务。MATLAB代码风格多变,用户可能使用自定义函数、循环、条件语句,使得自动划分“分析步骤”极其困难。

  • 心得 :放弃完美的全自动解析。采用“ 用户标记为主,智能推断为辅 ”的策略。提供简单易用的API(如 startSection ),让用户在关键节点给出明确信号。智能体则专注于这些标记点之间的内容理解和美化。这大大降低了实现难度,提高了系统的鲁棒性和实用性。

坑2:LLM生成内容的不稳定性 直接让LLM描述大段代码,可能会产生“幻觉”,捏造不存在的细节,或者风格时好时坏。

  • 心得
    1. 分而治之 :不要一次性喂给LLM整个脚本。按章节或逻辑块拆分,每次只针对一个明确的小任务生成描述。
    2. 提供强上下文 :在提示词中,不仅提供代码,还要提供该代码块的 输入输出变量信息 。例如,“这段代码接收一个名为 rawData 的表格,经过处理,输出了一个名为 cleanData 的表格”。这能将LLM的注意力锚定在具体的数据流上。
    3. 设置约束 :在提示词中明确要求“只描述代码实际做的事情”、“不要添加假设性的内容”、“如果某项操作意图不明确,请直接说明‘此步骤进行数据转换’”。
    4. 提供备选方案 :LLM调用可能失败或超时。系统必须有一个降级方案,例如,回退到基于规则生成的简单描述(如“本节执行了线性回归建模”),或者使用预定义的描述模板。

坑3:文档模板的灵活性 一开始设计了一个非常精美的固定模板,但很快发现不同项目、不同团队对文档格式的要求天差地别。

  • 心得 :将 模板系统设计成可配置的、模块化的 。提供几种基础模板(如“技术报告”、“项目简报”、“实验记录”),并允许用户通过配置文件自定义:
    • 需要包含哪些章节?
    • 每个章节下,代码、描述、图表、变量摘要的排列顺序是怎样的?
    • 公司Logo、页眉页脚信息如何添加? 甚至可以支持用户完全自定义HTML/Jinja2模板,系统只负责提供结构化的数据(一个包含所有章节、内容块的MATLAB结构体或JSON对象),让模板去渲染。这样系统的适应性就强多了。

坑4:性能与用户体验 如果每次运行脚本都同步调用LLM生成描述,会严重拖慢分析速度,尤其是网络不佳时。

  • 心得 :采用 异步生成 缓存机制 。在脚本执行过程中,只做轻量级的记录和标记(记录日志、保存图片)。脚本执行完毕后,再启动一个后台任务,读取日志文件,调用LLM生成文本描述,最后组装文档。对于经常重复运行的脚本,可以缓存LLM对相同代码块生成的描述,除非代码本身发生了改变。

一个实用的进阶技巧:变量智能摘要 除了记录变量名,智能体可以尝试对复杂变量内容做智能摘要,这能让文档价值倍增。例如,对于一个训练好的分类模型,除了记录“ trainedModel 是一个 ClassificationTree 对象”,还可以自动提取并格式化关键信息:

% 在recordVariable方法中增强
if isa(varValue, ‘ClassificationTree‘)
    % 提取重要信息
    accuracy = 1 - resubLoss(varValue);
    numNodes = numel(varValue.CutPoint);
    summary = sprintf(‘决策树模型 | 重代入准确率: %.2f%% | 节点数: %d‘, accuracy*100, numNodes);
    % 甚至可以生成一个简单的文本描述
    descFromLLM = callLLM(sprintf(‘这是一个MATLAB的决策树分类模型,重代入准确率为%.1f%%,请用一句话描述这个模型的性能水平。‘, accuracy*100), apiKey, model);
end

这个小小的增强,能让生成的文档从“代码日志”升级为“分析简报”。

构建一个能自动化文档MATLAB分析的Agentic AI系统,是一个典型的“80/20”工程。用20%的精力实现基础框架(监听、记录、模板组装),就能解决80%的重复劳动问题。剩下的20%精力,则投入到让系统变得更智能(LLM集成)、更灵活(可配置模板)、更稳定(错误处理)上。它最终带来的不仅是时间的节约,更是分析工作流的规范化和知识资产的沉淀。当你每次按下运行键,都知道一份清晰的记录正在同步产生,这种体验会让你更专注于分析本身,而不是后续的“家务事”。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐