Graph 要素解析 Llamaindex推出workflow应答复杂LLM运行构建 转向事情驱动 以及技术成功从图 EDA

同一天,LLM运行开发另一个代表产品Llamaindex也颁布了其在此畛域的新配置——workflow,进一步优化运行编排的才干。早在去年,Llamaindex在这方面曾经有了举措,推出了Query Pipeline(详见: ​ ​ ​运行编排的未来是Pipeline,LlamaIndex开发预览版推出Query Pipeline,优化运行开发的灵敏性​ ​ ​ ),它是一个申明式设计,可以自定义整个查问流程为一个DAG(有向无环图)流程,允许从繁难到复杂的不同服务流程。

关于普通的RAG类的流程来讲,DAG是可以接待的,并且繁难直观,这也是少量LLM workflow设计的分歧选用,但是关于Agent流程来讲,规范的DAG的结构有必定的毛病,比如不可处置循环逻辑(有环),但是经典的ReAct的流程就是一个循环迭代的环节。 除此之外,Llamaindex官网还提出了这一结构的一些其余疑问:

2)含糊了组件和模块口头逻辑

3)Pipeline口头器成功越来越复杂,必定处置少量不同的边(edge)状况

一旦咱们在查问Pipeline中减少了环,这些围绕图的开发运行的用户体验疑问就会被加大。以下是一些经常出现费事:

1)很多外围编排逻辑(如 if-else 语句和 while 循环)都被定义到图的边(edge)上。定义这些边(edge)会变得繁琐简短。

2)处置可选值和自动值的边的状况变得很艰巨。作为一个框架,很难确定参数能否会从抢先节点传递。

3)关于构建Agent的开发人员来说,用有环的图来定义并不总那么人造。Agent封装了一个由 LLM 驱动的通用实体,它可以接纳观察结果并生成照应。在这里,图的方式强迫要求 "Agent"节点明白定义传入边和传出边,迫经常使用户定义与其余节点的简短通讯形式。

这一些疑问,迫使Llamaindex官网团队从新扫视这种设计的正当性。实践上,笔者在设计Flowengine时也遇到这样的疑问,顺着dag图来设计编排口头器只管很直觉,但是并不是最佳做法,理由两点:

一,它迫使开发者须要从微观解析图中边(edge)和节点(node)的相关,整个逻辑十分复杂,特意是关于复杂的流程节点的处置以及失败状况复原来讲,都触及到少量的形态治理,这都使得图很复杂,特意是对边的处置,进而造成编排器成功复杂。

二,违犯依赖倒置准则,选用运行编排的方式,很大水平上是宿愿图上的组件是可以复用,可插拔的,不应该思考它究竟处于一个什么样的图中,毕竟先有组件,再有详细的业务流程Pipeline。而前面的做法,就使得组件节点须要适配图的结构,这显然不利于组件积淀复用,也造成了组件开发的复杂性。

关于此,Llamaindex推出了新的成功方法——事情驱动的形式来协调组件流程口头。显然事情驱动的形式,将图流程的调度变成了组件如何订阅和处置事情过去。这样很多原来边上的处置逻辑就变成了组件自己的行为,极大的降落了复杂度和依赖,并且这样的设计可以很容易的成功重试,失败,超时,循环,甚至是human-in-loop等原本间接解析图而发生的复杂逻辑。关于pipeline口头器来讲,提供信息散发和Context维持,以及依据订阅状况唤醒口头相关组件即可,也简化了整个成功的复杂度。

咱们来看看Llamaindex的workflow是如何编写的:

from llama_index.core.workflow import (StartEvent,StopEvent,Workflow,step,)from llama_index.llms.openai import OpenAIclass OpenAIGenerator(Workflow):@step()async def generate(self, ev: StartEvent) -> StopEvent:query = ev.get("query")llm = OpenAI()response = await llm.acomplete(query)return StopEvent(result=str(response))w = OpenAIGenerator(timeout=10, verbose=False)result = await w.run(query="What's LlamaIndex?")print(result)

下面例子定义了一个workflow类OpenAIGenerator,其中generate函数经常使用@step装璜器标志为这是一个workflow步骤,方法签名定义了其接纳什么样的事情信息以及前往值定义该步骤口头后颁布什么样的信息。

Llamaindex同时给出了这种方式下循环的成功方法:

class ExtractionDone(Event):output: strpassage: strclass ValidationErrorEvent(Event):error: strwrong_output: strpassage: strclass ReflectionWorkflow(Workflow):@step()async def extract(self, ev: StartEvent | ValidationErrorEvent) -> StopEvent | ExtractionDone:if isinstance(ev, StartEvent):passage = ev.get("passage")if not passage:return StopEvent(result="Please provide some text in input")reflection_prompt = ""elif isinstance(ev, ValidationErrorEvent):passage = ev.passagereflection_prompt = REFLECTION_PROMPT.format(wrong_answer=ev.wrong_output, error=ev.error)llm = Ollama(model="llama3", request_timeout=30)prompt = EXTRACTION_PROMPT.format(passage=passage, schema=CarCollection.schema_json())if reflection_prompt:prompt += reflection_promptoutput = await llm.acomplete(prompt)return ExtractionDone(output=str(output), passage=passage)@step()async def validate(self, ev: ExtractionDone) -> StopEvent | ValidationErrorEvent:try:json.loads(ev.output)except Exception as e:print("Validation failed, retrying...")return ValidationErrorEvent(error=str(e), wrong_output=ev.output, passage=ev.passage)return StopEvent(result=ev.output)w = ReflectionWorkflow(timeout=60, verbose=True)result = await w.run(passage="There are two cars available: a Fiat Panda with 45Hp and a Honda Civic with 330Hp.")print(result)

在这个例子中,validate步骤接纳实验性形式提取的结果作为事情,并且它可以经过前往ValidationErrorEvent来选择再次尝试,该ValidationErrorEvent最终将被传递到extract步骤,该extract步骤将口头下一次性尝试。这样就成功了循环迭代的逻辑。

因为编程自身的疑问,复杂的业务流程读代码是件痛苦的事情,Llamaindex提供了相似LangGraph Studio的才干,对口头流程可视化,繁难开发者启动调试。

可以看出,Llamaindex在应答复杂的LLM运行时,驳回了与Langchain相似的战略,即高代码+可视化辅佐调试的思绪。这其中,事情驱动的流程编排是一个共同的设计。但笔者以为,事情驱动自身是可以和申明式、低代码Pipeline开发相融合的,用户可以驳回直观的拖延拽编排整个流程,而编排器成功可以驳回事情驱动的方式而非解析图的方式,这样岂不是更好?甚至可以提供两种形式编程和低代码可视化,两者还可以成功互操作,更大层面笼罩了不同背景的开发者。理想上,FlowEngine便是驳回了这样的设计,更多细节可以参与群了解。

更多llamaxindex的workflow细节可以参看官网博文 :​ ​​ ​

本文转载自​​,作者:​​

您可能还会对下面的文章感兴趣: