一次 AI Agent项目(AI分析金融数据)实战体验:让我从过度自信到务实转型

我从过度自信到务实转型的经历,让我领悟到智能体AI(Agentic AI)的首要法则:为任务选择合适的工具。

我的想法简单却颇具野心:打造一个完全自动化的财务数据分析师。它能读取我的银行对账单、对支出进行分类,还能回答我提出的任何相关问题。我想象着用几个轻松的“氛围编码”周末,最终做出一个流畅的Streamlit应用程序来展示成果。

现在回想起来,当时的我过度自信到了危险的地步。接下来几周的历程,与其说是一场冲刺,不如说是在泥泞中蹒跚前行,这段经历也让我谦卑地认识到智能体AI在现实世界中的局限性,收获了宝贵的一课。

最初的梦想:打造一支自动化AI智能体团队

我的计划完全源自现代AI实践指南:一个由AutoGen驱动的多智能体系统。我设想了一支协作无间的AI专家团队:

  • 解析智能体团队(Parser Agent Team):由两个智能体组成,负责读取PDF文件并提取原始交易数据。其中包括一个编写Python解析脚本的数据分析师智能体(Data Analyzer Agent),以及一个运行脚本的代码执行智能体(Code Executor Agent)。
  • 分类智能体(Categorizer Agent):单个智能体,职责是接收原始交易数据,并为每一项支出分配对应的类别。
  • 报告智能体团队(Reporting Agent Team):结构与解析智能体团队一致,负责最终分析工作。包含一个解读用户需求、生成Python代码并返回结果的数据分析师智能体,以及一个运行代码的代码执行智能体。

现在回头看,当时的我陷入了严重的“隧道视野”。我对多智能体系统的概念太过痴迷,以至于没有认真考虑更稳健的传统架构。在我看来,专门的云服务有些大材小用,而基于模板的解析方式又显得过时。这是典型的“先有解决方案,再找问题”——我手握一把强大的新锤子,便执意要把所有问题都当成钉子来对待。

但现实是,我选择的道路彻底失败了。核心问题并非某个单一漏洞,而是普遍存在、令人抓狂的不稳定性:某个工作流程某次能奇迹般运行成功,可下一次用完全相同的输入执行时,却会突然崩溃。

我那些“氛围编码”周末,很快变成了无休止的调试和反复修改提示词的煎熬。我尝试了所有能想到的方法:为智能体提供更多上下文、撰写极其详尽的指令、重新调整团队结构,但这个系统从根本上就存在缺陷。如果有人想看“失败集锦”,可以在这个代码仓库里看到我那些混乱不堪的失败尝试。

以下是我遭遇的技术困境详情:

退一步思考:顿悟的时刻

经过数周的挣扎,我不得不面对现实:纯智能体方案已经走进了死胡同。我就像在用一把锤子——一把功能强大、具备智能的锤子——去做外科手术。尽管这些智能体在语言处理方面能力出众,却缺乏数据提取所需的结构化、确定性精度。它们无法“看见”文档,只能读取一连串的文本。

这成了项目的转折点。我意识到,我需要的不是一个“更聪明”的智能体,而是一个更适合这项任务的工具。

新方案:放下噱头,追求可靠

解决方案是转变思路:不再用智能体进行解析,而是改用专门工具——谷歌的Document AI。

Document AI专为一项任务设计:从复杂文档中提取结构化数据。它利用计算机视觉技术理解文档布局,识别标题、段落,最重要的是能识别表格。它不只是读取文本,还能理解文本的上下文。

两者的差距天差地别。我的智能体失败了上百次的任务,Document AI每次首次尝试就能输出近乎完美的结果。项目终于突破了瓶颈。

新的挑战与最后的优化

有了Document AI提供的结构化数据,其余的智能体系统突然变得可行起来。但“结构化”并不等同于“干净”:原始输出仍需经过大量数据清洗,处理不一致的日期格式、货币符号及其他变体,之后才能用于分析。

数据清洗完成后,智能体便有了可靠的基础,但不稳定性问题并未完全消失。报告智能体(Reporter Agent)有时能生成格式精美的Markdown文档,有时却只给出敷衍的一行摘要。为攻克这最后一道难关,我采取了多管齐下的策略:反复优化系统提示词、测试不同模型以找到更能可靠遵循复杂指令的方案、调整模型参数以降低随机性。

尽管这些措施大幅提升了稳定性,但并未实现完美解决。由于大语言模型(LLM)的智能体属性,仍存在一定程度的随机性,输出格式偶尔会出现偏差。我当时没时间实现,但读者可以尝试拓展的一个方案是“结构化输出(Structured Output)”:通过为报告定义严格的 schema(架构),强制LLM以可预测、机器可读的格式返回分析结果,这样应用程序就能每次都以完美一致的方式呈现内容。

幕后解析:最终的混合工作流程

最终成功的应用程序,与我最初的设想相去甚远,但它确实能正常运行。以下是驱动该应用的关键代码分步解析。

步骤1:借助谷歌Document AI实现精准解析

整个工作流程始于可靠的数据提取。我们没有使用智能体,而是采用了专门工具。services/parser.py模块负责将每个上传的PDF文件发送至谷歌云API。核心在于client.process_document()调用——它借助预训练模型理解对账单布局,并以极高的精度提取交易表格。

# In services/parser.py

def run_parsing():
    # ... 初始化谷歌云客户端 ...
    
    for file_name in file_names:
        # ... 读取PDF文件内容 ...
        
        raw_document = documentai.RawDocument(content=image_content, mime_type="application/pdf")
        request = documentai.ProcessRequest(name=name, raw_document=raw_document)
        
        # 调用专门的可靠工具
        result = client.process_document(request=request)
        document = result.document
        
        # 后处理逻辑将结果转换为干净的pandas DataFrame
        # 并保存为单个data.csv文件

步骤2:借助专注型智能体实现AI驱动的分类

获得干净的data.csv文件后,我们便可引入AI智能体。首先是来自agents/categorizer_agent.py的专注型分类智能体,它的唯一职责是读取数据并为每一行添加“类别”列。这是大语言模型的理想任务——它需要的是语义理解(比如判断“SQ *THE COPPER Q”是否属于餐厅消费),而非精准的结构化解析。

# In services/categorizer_task.py

async def run_categorization():
    # 将干净的CSV文件读取为字符串
    with open(constants.CSV_PATH, "r") as f:
        input_csv_text = f.read()
    
    # 获取带有特定系统提示词的预配置智能体
    agent = categorizer_agent.get_agent()
    
    # 为智能体创建简单任务
    task_message = TextMessage(
        content=(f"为以下CSV添加'Category'列...\\n\\n{input_csv_text}"),
        source="user",
    )

    # 运行单智能体团队
    team = RoundRobinGroupChat(participants=[agent], ...)
    chat_result = await team.run(task=task_message)
    
    # 提取智能体的响应并覆盖data.csv文件

步骤3:借助智能体团队实现交互式分析

数据经过充分预处理和分类后,应用程序便可以响应用户需求了。当用户提出问题时,我们会组建一个来自teams/finance_team.py的双智能体团队:

  • 数据分析师(Data_Analyzer):专业数据分析师智能体,接收用户问题并编写Python代码以解答问题;
  • Python代码执行器(Python_Code_Executor):非AI智能体,安全运行生成的代码并返回结果(文本、表格或图表文件路径)。

这个团队以循环方式工作:分析师制定计划、编写代码,然后等待;执行器运行代码并反馈结果或错误;分析师随后审查输出,要么给出最终答案,要么编写新代码优化分析。通过这一流程,系统能够生成复杂报告和精美图表。

# In streamlit_app.py(在“Analyze Data”按钮逻辑内)

# 1. 获取预配置智能体
analyzer = data_analyzer_agent.get_agent()
executor = code_executor_agent.get_agent(work_dir)

# 2. 创建智能体团队
team = finance_team.create_team(analyzer, executor)

# 3. 结合用户问题定义任务
task = TextMessage(
    content=(f"用户问题:{user_question}..."),
    source="user"
)

# 4. 运行团队并等待最终结果
chat_result = asyncio.run(team.run(task=task))

步骤4:借助Streamlit整合所有功能

streamlit_app.py脚本就像这场“交响乐”的指挥家:它提供用户界面、管理应用程序状态。其最重要的作用是通过st.session_state区分“一次性处理”和“交互式分析”,确保耗时的解析和分类步骤仅需运行一次。

# In streamlit_app.py

# 使用会话状态追踪一次性处理是否完成
if 'files_processed' not in st.session_state:
    st.session_state.files_processed = False

# “Process”按钮触发一次性设置
if st.button("Process Uploaded Statements ✨"):
    # 运行parser.run_parsing()和categorizer_task.run_categorization()
    st.session_state.files_processed = True

# 仅在处理完成后启用主要分析功能
if st.session_state.get("files_processed"):
    user_question = st.text_input("输入您的问题:")
    if st.button("Analyze Data 🧠"):
        # 调用Data_Analyzer和Code_Executor团队
        # 随后在界面上显示结果(文本、报告和图表)
        
        # 显示图表只需查找生成的.png文件
        chart_files = glob.glob(os.path.join(work_dir, "*.png"))
        if chart_files:
            st.subheader("📊 图表")
            for chart_file in sorted(chart_files):
                st.image(chart_file)

结语:为任务选择合适的工具

这个项目的经历,让我深刻体会到AI在实际应用中的关键教训:AutoGen等智能体AI框架固然功能强大,但并非解决所有问题的“万能药”。

最稳健、可靠的AI应用,往往源自“混合方案”:识别工作流程中需要高精度的环节,用专门工具处理这些任务,从而让AI智能体专注于它们最擅长的领域——推理、创造,以及应对模糊场景。曾经的过度自信让我走了不少弯路,但这场失败也教会我:要跳出“技术噱头”的误区,打造不仅智能、更能可靠运行的系统。

亲自尝试!

源码下载地址

参考文献

[1] Q. Wu, G. Bansal, J. Zhang, 等. AutoGen:通过多智能体对话实现下一代大语言模型应用(2023),arXiv。
[2] 微软. AutoGen文档(2024),谷歌云(Google Cloud)。
[3] 谷歌. Document AI文档(2025),谷歌(Google)。
[4] Streamlit团队. Streamlit文档(2025),Streamlit。
[5] L. Wang, W. Ma, X. Zhang, 等. 基于大语言模型的自主智能体研究综述(2025),arXiv。

更多推荐