LlamaIndex工作流生成幻灯片

LlamaIndex 最近推出了一项新功能:工作流。它对那些想要创建既可靠又灵活的 AI 解决方案的人来说非常有用。为什么?因为它允许你使用控制流定义自定义步骤。它支持循环、反馈和错误处理。它就像一个支持 AI 的管道。但与通常以有向无环图 (DAG) 形式实现的典型管道不同,工作流还支持循环执行,使其成为实现代理和其他更复杂流程的良好候选者。

在本文中,我将展示如何使用 LlamaIndex 工作流简化我研究某个主题的最新进展的过程,然后将该研究转化为 PowerPoint 演示文稿。

1、背景简介

当谈到寻找新的研究出版物或论文时,ArXiv.org 是我的主要来源。但是,这个网站上有很多论文。截至 2024 年 9 月,ArXiv 上大约有 250 万篇论文,其中包括 8 月份提交的 17,000 篇(统计数据在这里)。即使仅限于一个主题,也要阅读大量内容。但这不是一个新问题。长期以来,学术研究人员必须查阅大量作品才能进行自己的研究。过去两年,大型语言模型 (LLM) 的兴起为我们带来了 ResearchGPTpapersGPT 等工具,以及 OpenAI 平台上为特定研究目的构建的许多自定义 GPT,它们有助于文档搜索、摘要和展示。

虽然这些工具很有用,但我选择使用 LlamaIndex Workflows 构建自己的工作流程,主要有几个原因:

  • 我已经有一个特定的研究流程,我想保留它但希望能有更高的效率。
  • 我想利用 LLM 和代理行为并控制大部分步骤。
  • 我的目标不仅仅是获得最终的 PowerPoint 演示文稿;我还希望能够访问中间结果,以便在整个过程中进行观察、微调和故障排除。
  • 我需要一个可以端到端处理所有事情的一体化解决方案,而无需在不同的工具之间切换以完成总结和幻灯片制作等任务。
  • 如果我的需求发生变化,我可以在未来轻松扩展或修改工作流程。

我将设置一个工作流程,用户在其中给出一个研究主题(例如“使用 GenAI 制作 PowerPoint 幻灯片”),并从 arxiv.org 网站提取几篇论文,然后使用 LLM 总结每一篇论文。更具体地说,我想要总结的一些关键见解包括:方法类型、模型的组成部分、预训练或微调方法、数据集、评估方法指标和结论。所有这些的输出将是一个 PowerPoint 演示文稿,每篇论文一张幻灯片,其中包含摘要中的关键见解。

在我解释如何实现此工作流之前,了解 LlamaIndex 工作流中的两个关键概念很重要:事件和步骤。

  • 步骤:步骤是工作流的构建块。这些是代表工作流各个组件的 Python 函数。每个步骤执行特定任务,例如发送 Web 查询、获取 LLM 响应或处理数据。步骤可以通过接收和发出事件与其他步骤交互。步骤还可以访问共享上下文,从而实现跨不同步骤的状态管理。
  • 事件:事件充当工作流的数据载体和流控制器,它们以 Pydantic 对象的形式实现。它们控制工作流的执行路径,使其动态且灵活。用户可以自定义事件的属性。两种特殊类型的预定义事件 StartEvent 和 StopEvent 控制工作流的入口和出口点。

LlamaIndex 提供了几个笔记本示例视频系列,更详细地介绍了这些概念。

除了基本组件之外,在我的工作流中,我还使用了:

  • 异步和并行执行:在同时完成多个项目的同时提高效率。
  • 嵌套工作流:工作流中更复杂的层次结构。
  • LLM 的结构化输出:确保数据在步骤之间流动时具有结构。
  • 不同的 LLM 模型:允许在步骤之间使用具有不同功能和推理速度的模型(gpt-4o 和 gpt-4o-mini)。
  • 用于代码执行的动态会话:允许在隔离环境中执行代码。
  • 不同步骤中的单个代理:在流程中使用特定代理执行特定任务。

你可以在 Github 上找到此工作流的完整代码。要运行它,你将需要 Tavily 搜索、Semantic Sc​​holar 和 Azure OpenAI 的 API 密钥(因为这是使用 Azure 资源实现的,但你可以使用 LlamaIndex 轻松将其切换到 OpenAI 或其他模型)。在以下部分中,我将介绍构建此工作流程的一些关键细节和步骤。

2、主要工作流程

主要工作流程由两个嵌套的子工作流程组成:

  • summary_gen:此子工作流程查找给定主题的研究论文并生成摘要。它通过网络查询搜索论文,并按照指示使用 LLM 获取见解和摘要。
  • slide_gen:此子工作流程负责使用上一步中的摘要生成 PowerPoint 幻灯片。它使用提供的 PowerPoint 模板格式化幻灯片,并通过使用 python-pptx 库创建和执行 Python 代码来生成它们。
主流程概览

让我们仔细看看这些子工作流程。首先, summary_gen 工作流程非常简单。它遵循一个简单的线性过程。它基本上充当“数据处理”工作流程,其中一些步骤会向 LLM 发送请求。

摘要生成工作流程

工作流程首先获取用户输入(研究主题),然后执行以下步骤:

  • tavily_query:使用 Tavily API 进行查询,以获取与主题相关的学术论文作为结构化响应。
  • get_paper_with_citations:对于从 Tavily 查询返回的每篇论文,此步骤使用 SemanticScholar API 检索论文元数据以及引用论文的元数据。
  • filter_papers:由于并非所有检索到的引用都与原始主题直接相关,因此此步骤会优化结果。每篇论文的标题和摘要都会发送到 LLM 以评估其相关性。此步骤定义为:
@step(num_workers=4)
async def filter_papers(self, ev: PaperEvent) -> FilteredPaperEvent:
    llm = new_gpt4o_mini(temperature=0.0)
    response = await process_citation(ev.paper, llm)
    return FilteredPaperEvent(paper=ev.paper, is_relevant=response)

process_citation() 函数中,我们使用 LlamaIndex 中的 FunctionCallingProgram 来获取结构化响应:

IS_CITATION_RELEVANT_PMT = """
You help a researcher decide whether a paper is relevant to their current research topic: {topic}
You are given the title and abstract of a paper.
title: {title}
abstract: {abstract}

Give a score indicating the relevancy to the research topic, where:
Score 0: Not relevant
Score 1: Somewhat relevant
Score 2: Very relevant

Answer with integer score 0, 1 or 2 and your reason.
"""

class IsCitationRelevant(BaseModel):
    score: int
    reason: str

async def process_citation(citation, llm):
    program = FunctionCallingProgram.from_defaults(
        llm=llm,
        output_cls=IsCitationRelevant,
        prompt_template_str=IS_CITATION_RELEVANT_PMT,
        verbose=True,
    )
    response = await program.acall(
        title=citation.title,
        abstract=citation.summary,
        topic=citation.topic,
        description="Data model for whether the paper is relevant to the research topic.",
    )
    return response
  • download_papers:此步骤收集所有经过筛选的论文,根据相关性分数和 ArXiv 上的可用性对它们进行优先排序,并下载最相关的论文。
  • paper2summary_dispatcher:通过设置用于存储图像和摘要的路径,为每篇下载的论文生成摘要做好准备。此步骤使用 self.send_event() 来启用对每篇论文的 paper2summary 步骤的并行执行。它还使用变量 ctx.data["n_pdfs"] 设置工作流上下文中的论文数量,以便后续步骤知道它们预计总共要处理多少篇论文。
@step(pass_context=True)
async def paper2summary_dispatcher(
    self, ctx: Context, ev: Paper2SummaryDispatcherEvent
) -> Paper2SummaryEvent:
    ctx.data["n_pdfs"] = 0
    for pdf_name in Path(ev.papers_path).glob("*.pdf"):
        img_output_dir = self.papers_images_path / pdf_name.stem
        img_output_dir.mkdir(exist_ok=True, parents=True)
        summary_fpath = self.paper_summary_path / f"{pdf_name.stem}.md"
        ctx.data["n_pdfs"] += 1
        self.send_event(
            Paper2SummaryEvent(
                pdf_path=pdf_name,
                image_output_dir=img_output_dir,
                summary_path=summary_fpath,
            )
        )
  • paper2summary:对于每篇论文,它将 PDF 转换为图像,然后将其发送给 LLM 进行摘要。摘要生成后,将保存在 markdown 文件中以供将来参考。特别是,这里生成的摘要非常详细,就像一篇小文章,因此还不太适合直接放在演示文稿中。但它被保留下来,以便用户可以查看这些中间结果。在后面的步骤之一中,我们将使这些信息更易于呈现。提供给 LLM 的提示包括确保准确和简洁摘要的关键说明:
SUMMARIZE_PAPER_PMT = """
You are an AI specialized in summarizing scientific papers.
 Your goal is to create concise and informative summaries, with each section preferably around 100 words and 
 limited to a maximum of 200 words, focusing on the core approach, methodology, datasets,
 evaluation details, and conclusions presented in the paper. After you summarize the paper,
 save the summary as a markdown file.
 
Instructions:
- Key Approach: Summarize the main approach or model proposed by the authors.
 Focus on the core idea behind their method, including any novel techniques, algorithms, or frameworks introduced.
- Key Components/Steps: Identify and describe the key components or steps in the model or approach.
 Break down the architecture, modules, or stages involved, and explain how each contributes to the overall method.
- Model Training/Finetuning: Explain how the authors trained or finetuned their model.
 Include details on the training process, loss functions, optimization techniques, 
 and any specific strategies used to improve the model’s performance.
- Dataset Details: Provide an overview of the datasets used in the study.
 Include information on the size, type and source. Mention whether the dataset is publicly available
 and if there are any benchmarks associated with it.
- Evaluation Methods and Metrics: Detail the evaluation process used to assess the model's performance.
 Include the methods, benchmarks, and metrics employed.
- Conclusion: Summarize the conclusions drawn by the authors. Include the significance of the findings, 
any potential applications, limitations acknowledged by the authors, and suggested future work.

Ensure that the summary is clear and concise, avoiding unnecessary jargon or overly technical language.
 Aim to be understandable to someone with a general background in the field.
 Ensure that all details are accurate and faithfully represent the content of the original paper. 
 Avoid introducing any bias or interpretation beyond what is presented by the authors. Do not add any
 information that is not explicitly stated in the paper. Stick to the content presented by the authors.

"""
  • finish:工作流收集所有生成的摘要,验证它们是否正确存储,并记录过程的完成情况,并返回 StopEvent 作为最终结果。

如果此工作流独立运行,执行将在此处结束。但是,由于这只是主流程的子工作流,因此完成后,将触发下一个子工作流 -  slide_gen

3、幻灯片生成子工作流

此工作流根据上一步中创建的摘要生成幻灯片。以下是 slide_gen 工作流的概述:

幻灯片生成工作流

当上一个子工作流完成并且摘要 markdown 文件准备就绪时,此工作流开始:

  • get_summaries:此步骤读取摘要文件的内容,为每个文件触发 SummaryEvent,再次利用 self.send_event() 启用并发执行以加快处理速度。
  • summary2outline:此步骤使用 LLM 将摘要转换为幻灯片大纲文本。它将摘要缩短为句子或演示文稿中的要点。
  • gather_feedback_outline:在此步骤中,它向用户展示建议的幻灯片大纲以及论文摘要,供他们审阅。用户提供反馈,如果需要修订,可能会触发 OutlineFeedbackEvent。此反馈循环继续执行 summary2outline 步骤,直到用户批准最终大纲,此时会触发 OutlineOkEvent
@step(pass_context=True)
async def gather_feedback_outline(
    self, ctx: Context, ev: OutlineEvent
) -> OutlineFeedbackEvent | OutlineOkEvent:
    """Present user the original paper summary and the outlines generated, gather feedback from user"""
    print(f"the original summary is: {ev.summary}")
    print(f"the outline is: {ev.outline}")
    print("Do you want to proceed with this outline? (yes/no):")
    feedback = input()
    if feedback.lower().strip() in ["yes", "y"]:
        return OutlineOkEvent(summary=ev.summary, outline=ev.outline)
    else:
        print("Please provide feedback on the outline:")
        feedback = input()
        return OutlineFeedbackEvent(
            summary=ev.summary, outline=ev.outline, feedback=feedback
        )
  • outlines_with_layout:它使用 LLM 包含给定 PowerPoint 模板中的页面布局详细信息,从而增强每个幻灯片大纲。此阶段将所有幻灯片页面的内容和设计保存在 JSON 文件中。
  • slide_gen:它使用 ReAct 代理根据给定的大纲和布局细节制作幻灯片。此代理有一个代码解释器工具,用于在隔离环境中运行和更正代码,还有一个布局检查工具,用于查看给定的 PowerPoint 模板信息。系统提示代理使用 python-pptx 创建幻灯片,并可以观察和修复错误。
@step(pass_context=True)
async def slide_gen(
    self, ctx: Context, ev: OutlinesWithLayoutEvent
) -> SlideGeneratedEvent:
    agent = ReActAgent.from_tools(
        tools=self.azure_code_interpreter.to_tool_list() + [self.all_layout_tool],
        llm=new_gpt4o(0.1),
        verbose=True,
        max_iterations=50,
    )

    prompt = (
        SLIDE_GEN_PMT.format(
            json_file_path=ev.outlines_fpath.as_posix(),
            template_fpath=self.slide_template_path,
            final_slide_fname=self.final_slide_fname,
        )
        + REACT_PROMPT_SUFFIX
    )
    agent.update_prompts({"agent_worker:system_prompt": PromptTemplate(prompt)})

    res = self.azure_code_interpreter.upload_file(
        local_file_path=self.slide_template_path
    )
    logging.info(f"Uploaded file to Azure: {res}")

    response = agent.chat(
        f"An example of outline item in json is {ev.outline_example.json()},"
        f" generate a slide deck"
    )
    local_files = self.download_all_files_from_session()
    return SlideGeneratedEvent(
        pptx_fpath=f"{self.workflow_artifacts_path}/{self.final_slide_fname}"
    )
  • validate_slides:检查幻灯片以确保其符合给定的标准。此步骤涉及将幻灯片转换为图像,并让 LLM 根据指南目视检查其内容是否正确且风格一致。根据 LLM 发现的内容,如果存在问题,它将发送 SlideValidationEvent,如果一切正常,它将发送 StopEvent
@step(pass_context=True)
async def validate_slides(
    self, ctx: Context, ev: SlideGeneratedEvent
) -> StopEvent | SlideValidationEvent:
    """Validate the generated slide deck"""
    ctx.data["n_retry"] += 1
    ctx.data["latest_pptx_file"] = Path(ev.pptx_fpath).name
    img_dir = pptx2images(Path(ev.pptx_fpath))
    image_documents = SimpleDirectoryReader(img_dir).load_data()
    llm = mm_gpt4o
    program = MultiModalLLMCompletionProgram.from_defaults(
        output_parser=PydanticOutputParser(SlideValidationResult),
        image_documents=image_documents,
        prompt_template_str=SLIDE_VALIDATION_PMT,
        multi_modal_llm=llm,
        verbose=True,
    )
    response = program()
    if response.is_valid:
        return StopEvent(
            self.workflow_artifacts_path.joinpath(self.final_slide_fname)
        )
    else:
        if ctx.data["n_retry"] < self.max_validation_retries:
            return SlideValidationEvent(result=response)
        else:
            return StopEvent(
                f"The slides are not fixed after {self.max_validation_retries} retries!"
            )

用于验证的标准是:

SLIDE_VALIDATION_PMT = """
You are an AI that validates the slide deck generated according to following rules:
- The slide need to have a front page 
- The slide need to have a final page (e.g. a 'thank you' or 'questions' page)
- The slide texts are clearly readable, not cut off, not overflowing the textbox
 and not overlapping with other elements

If any of the above rules are violated, you need to provide the index of the slide that violates the rule,
 as well as suggestion on how to fix it. 

"""
  • modify_slides: 如果幻灯片未通过验证检查,上一步将发送 SlideValidationEvent 事件。此时,另一个 ReAct 代理将根据验证器反馈更新幻灯片,更新后的幻灯片将被保存并返回以再次进行验证。根据 SlideGenWorkflow 类的 max_validation_retries 变量属性,此验证循环可能会发生多次。

要端到端运行完整的工作流,我们通过以下方式启动该过程:

class SummaryAndSlideGenerationWorkflow(Workflow):
    @step
    async def summary_gen(
        self, ctx: Context, ev: StartEvent, summary_gen_wf: SummaryGenerationWorkflow
    ) -> SummaryWfReadyEvent:
        print("Need to run reflection")
        res = await summary_gen_wf.run(user_query=ev.user_query)
        return SummaryWfReadyEvent(summary_dir=res)

    @step
    async def slide_gen(
        self, ctx: Context, ev: SummaryWfReadyEvent, slide_gen_wf: SlideGenerationWorkflow
    ) -> StopEvent:
        res = await slide_gen_wf.run(file_dir=ev.summary_dir)
        return StopEvent()


async def run_workflow(user_query: str):
    wf = SummaryAndSlideGenerationWorkflow(timeout=2000, verbose=True)
    wf.add_workflows(
        summary_gen_wf=SummaryGenerationWorkflow(timeout=800, verbose=True)
    )
    wf.add_workflows(slide_gen_wf=SlideGenerationWorkflow(timeout=1200, verbose=True))
    result = await wf.run(
        user_query=user_query,
    )
    print(result)


@click.command()
@click.option(
    "--user-query",
    "-q",
    required=False,
    help="The user query",
    default="powerpoint slides automation",
)
def main(user_query: str):
    asyncio.run(run_workflow(user_query))


if __name__ == "__main__":
    draw_all_possible_flows(
        SummaryAndSlideGenerationWorkflow, filename="summary_slide_gen_flows.html"
    )
    main()

4、结果

现在让我们看一个为论文 LayoutGPT: Compositional Visual Planning and Generation with Large Language Models 生成的中间摘要的示例:

# Summary of "LayoutGPT: Compositional Visual Planning and Generation with Large Language Models"

## Key Approach
The paper introduces LayoutGPT, a framework leveraging large language models (LLMs) for compositional visual planning and generation. The core idea is to utilize LLMs to generate 2D and 3D scene layouts from textual descriptions, integrating numerical and spatial reasoning. LayoutGPT employs a novel prompt construction method and in-context learning to enhance the model's ability to understand and generate complex visual scenes.

## Key Components/Steps
1. **Prompt Construction**: LayoutGPT uses detailed task instructions and CSS-like structures to guide the LLMs in generating layouts.
2. **In-Context Learning**: Demonstrative exemplars are provided to the LLMs to improve their understanding and generation capabilities.
3. **Numerical and Spatial Reasoning**: The model incorporates reasoning capabilities to handle numerical and spatial relationships in scene generation.
4. **Scene Synthesis**: LayoutGPT generates 2D keypoint layouts and 3D scene layouts, ensuring spatial coherence and object placement accuracy.

## Model Training/Finetuning
LayoutGPT is built on GPT-3.5 and GPT-4 models, utilizing in-context learning rather than traditional finetuning. The training process involves providing the model with structured prompts and examples to guide its generation process. Loss functions and optimization techniques are not explicitly detailed, as the focus is on leveraging pre-trained LLMs with minimal additional training.

## Dataset Details
The study uses several datasets:
- **NSR-1K**: A new benchmark for numerical and spatial reasoning, created from MSCOCO annotations.
- **3D-FRONT**: Used for 3D scene synthesis, containing diverse indoor scenes.
- **HRS-Bench**: For evaluating color binding accuracy in generated scenes.
These datasets are publicly available and serve as benchmarks for evaluating the model's performance.

## Evaluation Methods and Metrics
The evaluation involves:
- **Quantitative Metrics**: Precision, recall, and F1 scores for layout accuracy, numerical reasoning, and spatial reasoning.
- **Qualitative Analysis**: Visual inspection of generated scenes to assess spatial coherence and object placement.
- **Comparative Analysis**: Benchmarking against existing methods like GLIGEN and ATISS to demonstrate improvements in layout generation.

## Conclusion
The authors conclude that LayoutGPT effectively integrates LLMs for visual planning and scene generation, achieving state-of-the-art performance in 2D and 3D layout tasks. The framework's ability to handle numerical and spatial reasoning is highlighted as a significant advancement. Limitations include the focus on specific scene types and the need for further exploration of additional visual reasoning tasks. Future work suggests expanding the model's capabilities to more diverse and complex visual scenarios.

毫不奇怪,总结对于 LLM 来说并不是一项特别具有挑战性的任务。通过仅以图像形式提供论文,LLM 有效地捕捉了提示中概述的所有关键方面,并很好地遵守了样式说明。

至于最终结果,以下是生成的演示幻灯片的几个示例:

生成的幻灯片

当按照模板中的布局填写摘要内容、保持文本的模板样式、将总结的要点放在项目符号格式中以及在幻灯片中包含所需的所有相关论文时,工作流程表现良好。这是一个问题,有时主内容占位符中的文本未调整大小以适合文本框。文本溢出幻灯片边界。这种类型的错误可能可以通过使用更有针对性的幻灯片验证提示来修复。

5、最后的想法

在本文中,我展示了如何使用 LlamaIndex 工作流来简化我的研究和演示过程,从查询学术论文到生成最终的 PowerPoint 幻灯片。以下是我在实施此工作流程时的一些想法和观察,以及我认为可以改进的一些方面。

  • gpt-4o 模型与 gpt-4o-mini 模型:虽然据称 gpt-4o-mini 的性能与 gpt-4o 相当,但我发现 gpt-4o-mini 在用作工作流程中的 ReAct 代理时,显然难以完成复杂的任务,例如规划和修复错误。但是,它在总结内容等简单任务中表现得还不错。
  • 创建中间文件:生成中间文件(摘要 markdown 文件和摘要布局 JSON 文件)是一种有用的方法,可以消除代理在编写生成幻灯片的代码时跟踪幻灯片内容和样式的负担。
  • 处理边缘情况:从端到端运行工作流程发现了许多边缘情况,尤其是在验证幻灯片样式方面。目前,这是通过迭代修改相关提示来处理的。但我认为促进这一点这种类型的协作和人机交互机制将对此大有帮助,同时还能提供更高的准确性。
  • python-pptx 的局限性。工作流程受限于 python-pptx 在 PowerPoint 幻灯片中实际呈现和操作的内容。因此,值得进一步考虑其他潜在的高效幻灯片生成方式,例如使用 VBA。
  • 摘要生成的代理和工具:使用一个或多个可以访问工具(当前为步骤函数)的代理,而不是严格的分步摘要生成过程,可以使工作流程更加灵活,并能适应未来的变化。
  • 增强人机交互。当前的实现不允许许多用户交互。让最终用户更多地参与工作流程,尤其是对于涉及用户判断的任务(如内容验证和细化),可能会非常有益。一种方法是添加更多步骤,让工作流程可以要求用户进行验证并考虑用户的反馈。人工参与对于纠正错误和实时做出更改非常有价值。
  • 论文查询引擎。还可以构建每篇论文的查询引擎,以便用户可以根据需要提出问题并修改摘要。这有助于工作流程产生更加个性化的结果。

综上所述,LlamaIndex 工作流程是一种非常灵活且可定制的工具,可用于制作复杂且量身定制的 AI 解决方案。它让我可以自由地定义我的流程,既具有可控性又具有灵活性,同时能够利用库中的许多内置工具。

6、下一步是什么?

如上所述,主要的改进将是实现更多人机交互类型的功能。

例如,通过将交互式步骤合并到工作流程中并为用户提供机会检查工作流程是否在任何阶段产生令人满意的输出,允许更多交互式检查点,用户可以在需要时覆盖步骤执行。与能够提供更好的用户体验的目标一致,构建 Streamlit 前端也是一个很好的补充,可以对工作流程的执行提供更多见解。拥有前端还可以让用户实时监控工作流程并相应地更快地调整轨迹,从而改善用户体验。此外,获得用户反馈和验证,可视化中间和最终输出将增加工作流程的透明度。

本教程的代码可以从 GitHub 下载。


原文链接:How I Streamline My Research and Presentation with LlamaIndex Workflows

汇智网翻译整理,转载请标明出处