2026/10/17 15:07:15

用Agent自动生成报告:从任务规划到文档交付的完整实践

用Agent自动生成报告:从任务规划到文档交付的完整实践 最近在搭建一个专门的Agent Demo——生成报告。这个demo要解决的问题很直接用一句自然语言描述你想要的报告Agent自动拆解任务、去数据源取数、计算指标、组织内容最后在本地生成一份完整可用的文档。日常工作中大批量、重复性的分析报告如果都要靠人手动拉数据、算指标、排版面几个小时就耗进去。把这个流程交给Agent跑第一版人只做最后审核和修订才是这套东西真正的使用方式。这个内容适合两类读者一类是想快速体验Agent开发流程、但不想一上来就啃复杂框架的开发者另一类是经常处理数据报告、希望用自动化手段替代重复劳动的产品、运营或分析人员。即便你之前没有深入的AI编程经验只要熟悉基础Python和SQL也能跟着这篇文章把核心链路复现出来。1. Agent生成报告的整体设计思路1.1 先想清楚为什么不直接写脚本而是用Agent早期我也做过“自动报表”类的功能本质上是一个很长的过程式脚本连数据库跑SQL把结果套进模板生成文档。用起来总有几个痛点——换了指标口径要改代码换了报告场景要重写模板数据异常时程序直接卡死根本不会变通。Agent的思路和脚本完全不同。它不是把每一步都写死而是给你三步核心能力理解目标、拆解步骤、调用工具。Agent拿到“帮我做一个上月销售分析报告”这种需求时会自己判断需要哪些数据、该算什么指标、内容怎么组织然后调用我预先准备的工具去执行再把执行结果汇总成最终报告。用Agent而不是脚本核心理由是报告场景变化多端。今天可能是销售周报明天可能是用户留存分析后天可能是库存盘点。如果每一类都手写脚本维护成本太高Agent的做法是数据工具和文档工具作为基础能力沉淀任务逻辑由模型动态编排。代价是每次执行有模型调用成本而且偶尔需要兜底逻辑但换来的灵活性和覆盖面完全值得。1.2 系统架构四个模块的职责划分整个demo的架构我非常简化为四个模块每个模块只干一件事避免纠缠不清。第一个是规划模块负责把用户的自然语言任务翻译成一组有序的执行步骤。比如用户说“分析最近30天各区域订单量变化”规划模块会输出一个步骤列表拉取订单数据、按区域聚合、计算环比、生成趋势小结。这个模块是整个Agent能“随机应变”的关键简单说就是给大模型一个清晰的指令边界让它输出结构化的任务清单。第二个是工具模块封装数据访问和文档生成能力。数据访问包括执行SQL、读取本地CSV或者调用内部API文档生成包括写Markdown段落、生成表格、插入统计图。工具的设计原则是单一职责每个工具只做一件确定的事情这样模型调用起来才不会混乱。比如“查询订单数据”是一个工具“计算环比增长率”又是另一个工具两者不混在一起。大模型调用工具时传入的参数就是SQL语句或者数据集标识。第三个是执行模块也就是Agent运行的主循环。它负责把规划模块产出的步骤逐个执行执行过程中如果某个工具调用失败会做有限次重试。执行模块还要维护一个临时存储区把工具返回的中间结果暂存起来供后续步骤使用。这一步非常像人在手工做报告时的过程先把数据查出来放在一边备用再慢慢分析和排版。第四个是内容组装模块在所有工具结果都齐全之后把中间结果、分析结论、图表文件统一塞进报告模板生成最终文档。我这里会区分“正文内容”和“附属材料”正文内容由模型基于数据生成附属材料则是图表图片、数据表文件等两者最后拼装成一个完整的文件夹或者压缩包。这四个模块的配合关系可以用一句大白话概括规划模块说“做什么“工具模块说”能做啥“执行模块负责”按计划做“内容组装模块负责”整出一份能交付的报告“。1.3 技术选型语言模型之外的三个基础组件模型本身我直接用通用的对话式大模型接口没有做任何微调。因为报告生成的关键不在模型肚子里有多少知识而在于你怎么把数据和分析链路接到模型的外围。工具调用能力比模型聪明程度更值得关注我用的是支持函数调用Function Calling接口的方案模型可以按约定输出“调用哪个工具、传什么参数”而不是只能返回一段文字。除了模型接口三个基础组件是必须的一个内存态的流程编排器负责跑Agent主循环我用Python简单实现事件循环加状态字典不到一百行一套数据连接层封装了一次数据库查询、CSV文件读取和内部API轮询统一返回DataFrame格式的数据表这里你可以换成任何你熟悉的数据方案不只是固定数据库。一个报告生成器内置三套常见模板数据分析型、工作汇报型、调研纪要型先用Markdown生成内容再按需转成Word或PDF。这些组件不用都从零造Python生态里有大量现成库可用但我的建议是Agent的逻辑主循环尽量自己控制不要过度依赖别人的框架。原因后面在踩坑部分会详细说核心是Agent的主循环涉及大量自定义策略框架封装的越深排查问题就越难。2. 核心细节解析提示词、工具与模板设计2.1 提示词设计的两个要点给示例给边界Agent的规划模块靠提示词驱动。写提示词时我最深的体会是能力不是靠模型硬悟出来的而是靠示例和边界划出来的。这里的示例是指“输入任务→输出步骤”的几组固定样例要让模型知道你应该输出什么粒度的步骤列表。我给规划模块的提示词里包含一个任务格式描述输出必须是一份JSON数组每个元素包含步骤编号、动作名称、目标说明三部分动作名称只能从工具清单中选不允许凭空杜撰如果一个动作和已有工具不匹配必须标记为“待确认”不能跳过。边界条件我写得很严格告诉模型“你只负责做任务拆解不负责实际计算所有计算类动作必须落到工具层执行”。很多人写Agent提示词时喜欢让模型“自由发挥”我试下来的结果是自由发挥过头了模型会试图在文本里直接完成计算然后给出一个编造的结论这是报告生成类Agent最应该避免的情况。所有数值都必须有据可查对话上下文里出现计算的中间步骤不是好事必须靠工具流。2.2 工具链设计数据查询、统计计算、文本生成分离工具模块是我花心思最多的地方。这里我强调“分离”两个字是因为模型调工具时参数匹配非常容易出错。如果某个工具同时要拉数据、算指标、写分析句子一次调用里塞了太多逻辑模型很难说清楚参数到底该怎么传出错概率直线上升。实际拆下来我设计了四个工具查询原始数据输入是SQL语句或数据表名称输出是DataFrame。计算指标输入是指标名称和过滤条件输出是指标数值。比如“计算最近30天环比增长率”。数据汇总分析输入是一个数据集ID和想要的分析维度输出是一段文字版的分析结论这段结论会作为报告正文的素材。写入报告章节输入是章节标题和正文内容输出没什么就是把它塞进报告缓冲区。这种设计的优点是模型每一步只需要做一个小决策参数范围非常可控。缺点是工具调用次数变多了报告里如果有五个指标、三个结论可能就要调用十几次工具。实际跑下来成本和时间都在可接受范围一个完整报告大约2到3分钟模型API消耗也不算夸张。2.3 报告模板先骨架后填肉再润色报告模板我用的是三阶段方案先定骨架再填内容最后统一润色。骨架就是模板固定的章节结构比如数据分析型报告一般是“概述→数据总览→分项分析→结论与建议”。这个骨架不是模型临时生成的而是我预置的好处是报告结构稳定不会出现这次有“总结”下次没有的尴尬。填肉阶段比较特殊。我让它分成两次大模型调用第一次根据数据生成分析观点要点第二次把要点扩展成完整段落。两次拆分的原因很实际如果让模型一次性输出一个完整章节内容经常出现过长的开头铺垫而真正有信息量的分析反而很少。先零散写出要点模型在扩展时会更克制废话也少。这算是我在多次实验里总结出的小技巧。润色阶段会统一检查单位、日期、数据一致性。比如模型如果在前一章提到“环比下降8%”后面又写“环比下降10%”润色阶段会标记不一致要求重新核对。这个过程也是用一次模型调用实现的给它整个报告的草稿让它输出发现的问题列表而不是直接改稿。2.4 上下文管理别把所有历史消息都丢给模型Agent跑报告时工具调用的中间结果、模型之前的输出这些信息如果全塞进上下文几轮之后窗口就要爆了而且越靠后的消息对模型注意力的干扰越严重。我的处理方式分两级短期上下文和长期记忆。短期上下文只保存最近五轮的关键信息包括工具名称、工具返回的数据摘要比如“查询订单数据返回1000行记录四个字段”不存全量数据。长期记忆保存报告骨架、已经写好的章节、待处理指标清单。每次规划模块调用模型时输入实际上是“短期上下文摘要长期记忆中的关键状态”而不是原始对话记录的总和。这套机制做下来一个中等复杂度的报告单次模型输入最多控制在四千个Token以内既不会爆窗口响应速度也快很多。另一个好处是即使某些中间工具调用出现失败重试之前的报告草稿不会因为上下文被挤掉而丢失。3. 实操过程一步步跑通一个报告生成Demo3.1 演示场景与数据准备我这次演示用的是模拟项目X一个虚拟的销售运营分析报告。数据源我自己造了一份CSV共2000多行每行是一条订单记录字段包括日期、区域、产品线、销售额、订单量。为了演示效果数据里我特意埋了三个特征一个区域连续两周销售额下滑一个产品线增速明显还有一个区域的数据存在异常波动。这样Agent生成报告时能有一定的“发现空间”。在真正跑Agent之前我先用脚本做了一次数据质量检查发现日期字段有两个格式不统一缺失了约2%的订单量数据。这种情况在真实业务数据里几乎一定会出现我建议你第一步就先做清洗不然Agent在后续计算指标时会被脏数据搞得很狼狈。3.2 Agent主循环的骨架代码Agent主循环我写得很精简核心逻辑就是“取任务、选工具、执行、汇总结果、判断是否完成”五个环节。这里给一个简化版伪代码我用Python描述def run_agent(task, planner, tools, reporter): # 第一步规划步骤 steps planner.plan(task) workspace {} for step in steps: action step[action] tool tools.get(action) if tool is None: # 工具不存在记录为失败步骤等待兜底 workspace[step[id]] {status: failed, reason: 工具缺失} continue # 第二步执行单步动作携带必要的上下文信息 params step[params] result tool.execute(params, workspace) # 第三步判断结果是否正常 if result.ok: workspace[step[id]] { status: ok, data: result.data, # 可能是一个 DataFrame summary: result.summary # 也可能是文字摘要 } else: # 第四步失败后做一次智能降级 fallback tool.fallback(params) workspace[step[id]] { status: fallback, data: fallback.data, summary: fallback.summary } # 第五步所有步骤完成进入内容组装 report reporter.assemble(workspace) return report这段代码的主意在于用workspace字典把所有中间结果集中保存起来每个步骤执行时都能读取之前步骤的输出。比如数据汇总分析工具要用查询结果它直接从工作区读那一步的数据集ID即可不必重新查一遍数据库。3.3 规划模块的提示词示例规划模块我是按角色任务约束输出格式三部分组织的。这里贴一个精简版提示词框架你可以直接改着用你是一个报告任务规划器。你的任务是把用户描述的报告需求拆解为一系列可执行步骤。 约束条件 1. 只能使用下列工具query_data, compute_metric, summarize_analysis, write_section。 2. 所有数值计算必须通过工具执行不要直接在文字中推导数据。 3. 步骤之间不能有隐含依赖如果后一步依赖前一步的输出必须显式说明依赖哪个步骤ID。 4. 输出必须是JSON列表格式如下 [ {step_id: 1, action: query_data, params: {...}, depends_on: [0]}, ... ] 用户报告需求 {user_input}第一次跑这个提示词时我发现一个问题模型有时会在params里塞一个很大的SQL语句字段名和我们的实际表结构根本对不上。因为模型没见过真实表结构。解决方案也很简单提示词里增加一份数据字典说明表中的字段名、类型、例子模型生成SQL的准确率会显著提高。这块经验特别值得记录数据字典就是Agent工具调用质量的隐形保障。3.4 工具执行和内容组装的实际细节查询工具执行时我对SQL做了一层白名单保护只允许SELECT开头不允许更新或删除操作防止模型乱写。另外查询超时设为30秒避免一条慢SQL把整个Agent卡住。指标计算工具里有一个容易被忽略的点计算口径的定义。比如“销售额”是按订单金额算还是按成交金额算要不要含税。我在工具的参数说明里用了两个典型口径名称模型调用时会明确说用哪个口径。如果没有这一步最终报告里可能出现同一指标前后两种算法看起来数值矛盾。内容组装阶段我额外设置了查重机制。模型写出来的段落会和同章节其他段落的嵌入向量算一遍相似度相似度高于一定阈值的段落会被标红等人工确认再合并。报告生成最怕的就是车轱辘话来回说这个检查能过滤掉大量重复内容。组装完成后报告以一个文件夹形式输出里面有一个主文档和多个附属表格、图片。主文档里每一章节开头标注了它对应的数据来源和工具调用记录这个“可追溯”的设计在后续审核时非常有用。你一眼就能看出哪一段结论是哪个SQL查出来的出问题方便回溯。3.5 演示输出效果概览最终生成的模拟项目X销售分析报告包含五个章节大约四千字正文三张统计表格一张按区域聚合的柱状图。Agent发现了我预埋的三个特征中的两个一是西部区域连续下滑二是某个产品线增速明显。异常的波动数值虽然被识别了但Agent没有主动深挖原因只写了一句“该区域波动幅度较大建议进一步排查”。这个结果符合预期因为Agent本身不知道业务背景只能从数据变化上去描述深层次归因还是要靠人或者接入更多背景知识。第一次跑完整流程耗时约2分40秒模型API调用次数是23次其中15次是工具调用8次是规划与润色。成本折算下来非常低完全可以作为自动化定期任务批量运行。4. 常见问题与排查技巧实录4.1 工具参数幻觉模型编造不存在的字段这是第一个让我头疼的问题。模型在生成SQL时偶尔会凭空编出几个字段名比如表里根本没有“提成比例”它照样写进查询里结果工具报错然后它重新生成的SQL又会编造其他字段。根源是数据字典没塞进规划模块的上下文。解决办法是两件事一是在提示词中注入表结构和字段说明二是在工具层增加字段名校验发现SQL里的字段不在白名单时自动将错误信息返回给模型让模型用正确字段重写。第二次错误率会明显降低。4.2 模型把分析结论写得像正确但空泛的套话“整体业绩表现稳定部分区域有所波动建议持续跟进。”这种句子听起来没错但没有信息量。最开始我以为是模型能力问题调了好几个模型都一样。后面意识到不是模型的问题而是我给它的数据摘要太粗了。调整办法数据汇总分析工具的输入不仅仅是数据集还要指定按哪个维度分组。比如“按区域分组输出最近四周销售额和环比变化”模型拿到带分组趋势的数据才能生成有具体指向的说法。现在它写出来的结论会是“A区域连续两周环比下降5%以上主要受B产品线影响”这种就有用多了。4.3 Agent中断后没有中间产物等于白跑有一次跑了五分钟最后输出阶段因为网络问题直接报错。重启之后发现中间结果全丢了所有步骤要重新执行一遍。这非常浪费时间。我很早就给工作区加了本地落盘能力每个工具执行完就把结果以JSON格式存到一个临时目录。后来中断恢复时Agent能自动读取之前已完成的步骤从断点继续跑。等于让这个Agent具备了很初级的“记忆”能力。现在这是固定设计不只是保险也是效率优化。4.4 上下文越长输出格式越不稳定报告较长时较后写的章节的格式经常和前面不一致可能前几章用了四级标题后面就变成三级列表样式也会漂移。原因是上下文叠加后模型对“严格按模板格式来”这个指令的遵循度会下降。我采用的方法是把报告的固定排版规则从“对话提示词”移到“内容组装模块”。模型只输出内容块每个块有明确的类型标记比如段落、表格、列表排版规则统一在组装模块执行。这样模型的任务变简单了格式漂移也基本解决了。4.5 工具返回的数据集太大直接塞进模型上下文会爆有一次查询数据返回一万多行直接当作上下文传给模型瞬间超长API报错。我后续对所有工具返回结果都加了概括层每次查询都会生成一个“数据画像”——行数、列数、最大值、最小值、均值、分布情况模型接触的是画像而非原始数据。只有那些真正需要模型计算深度的场景才把部分抽样数据传入上下文。数据画像方式对报告类需求已经足够因为Agent写报告看重的是统计趋势和异常值不需要逐行读。5. 更深入的优化把Agent报告质量往前再推一步5.1 引入校验Agent生成完报告后先内部评审单独一个Agent生成报告再交给用户有一个天然缺陷它对自己的产出缺乏外部视角很容易整体上发现不了问题。我试过加一个“校验Agent”只读报告内容和数据摘要不修改任何东西它的工作就是列出逻辑矛盾、数据出入、结构缺失和表述模糊的地方。校验Agent和生成Agent面对的任务不同提示词也应该完全不一样。生成Agent要策略性地组织语言校验Agent则需要怀着挑毛病的心态去审稿。两个Agent的分离让我最后拿到的报告质量明显提升尤其是数据矛盾这类硬伤基本能过滤掉。5.2 报告反馈闭环一次生成不满意别急着修改最初用户对报告不满意时我直接把意见拼进上下文再问一遍模型重写。效果很一般模型容易矫枉过正为迎合意见把之前正确的也都改了。现在改成结构化反馈闭环先让用户对报告各章节打分并写原因然后规划模块把反馈拆成两类——结构问题还是内容问题。结构问题修改模板框架内容问题则定位到具体章节和具体工具结果只重跑那一部分。这个方案让迭代效率高多了也避免引入新的错误。5.3 模板库维护的心得报告模板不要放在代码里而是抽成JSON配置。每个模板包含固定章节、章节顺序、需要哪些工具结果、内容渲染建议。这样换一个报告类型时不需要改代码只需要新增一个JSON文件。我目前维护了接近二十个模板覆盖了比较常见的数据分析、周报月报、复盘总结等场景代码主体变化很小。对Agent类项目来说多样性靠数据配置而不是代码逻辑越早这么规划越省事。5.4 批量调度与成本控制的平衡跑不定期自动报告时最担心的就是模型API成本。我先后试过多次让Agent发起规划调用发现平均每个报告二十几次模型调用里至少有三到四次是单纯用来纠正自己错误的。优化工具层参数校验之后这类重复调用降到了一次左右。所以与其花精力调低模型价格档位不如先把工具参数约束和上下文数据说清楚错误少了成本自然降下来。还有一个小经验大批量跑报告优先选在工作负载低的时段无论是我这边还是模型服务那边都会启动超时重试机制保证任务不会因为偶发的网络问题整体失败。调度系统本身不用太复杂一个简单的任务队列加几个worker进程就够。5.5 后续扩展从单Agent到多Agent协作现在的系统相当于一个主Agent加一个校验Agent分工已经比单Agent清晰。我接下来的方向是继续拆出更细的特化角色比如一个专门做数据探索的Agent、一个专门负责图表生成的Agent它们之间通过中间文件交换结果。主Agent作为调度者不参与具体计算。分割到最后单个Agent提示词更短更聚焦任务边界更明确模型表现也会更稳定。当然代价是模块间的通信和状态管理变得更复杂。如果报告场景进一步扩大比如要做几十种类型的报告这种多Agent结构会更值得投入。如果是固定几种报告类型维持现在的单Agent加工具流反而是最优解。我在实际使用中最大的体会是Agent生成报告这个方向真正的门槛不在模型而在于工程化的外围工具设计、提示词约束、状态管理、质量校验和失败兜底每一样都比模型本身更影响最终效果。不要一上来就追求复杂的框架把一个最小链路跑通再一步步加校验和反馈机制你会看到它从“能用”到“好用”的完整过程。