继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

使用DSPy构建稳定可靠的AI代理和大型语言模型程序:实用技巧与代码示例

BIG阳
关注TA
已关注
手记 471
粉丝 72
获赞 458
如何在生产中使用DSPy来构建AI代理和大型语言模型的技术指南

图片由作者创作

DSPy 是我首选的框架,因为它简洁且设计贴心。我用它来构建最小可行产品,直到扩展系统以处理数百万请求。虽然 DSPy 和 AI 编程仍在不断发展,但在找到清晰且适合生产的指导方面却十分困难。

在这篇博客里,我分享了我的所学——从实际操作经验到文档和GitHub讨论的收获。这是一份实用指南,充满了实用DSPy工作的建议。

要是你觉得这有帮助,就关注我和 Firebird技术,了解更多这样的内容。

如何利用AI制作更可靠的报告 - 分享我与AI的协作经验
可靠的输出(也就是稳定的结果)

对于使用大语言模型(LLM)的开发人员来说,面临的最大挑战是实现结构化和一致的输出结果。让代理工作起来是一回事,但要确保超过95%的可靠性则是另一回事。用户每次都希望获得好结果——他们并不在意LLM是否容易出错、不一致或偶尔表现怪异。

虽然有些问题,比如“API服务中断”,是不在你控制范围内的,但你可以处理许多在你这边的问题。使用DSPy,你可以用断言和建议来引导你的程序达到更好的输出结果,并提升程序的可靠性。这些工具还可以捕获到不良结果,并自动重试以确保高质量的响应。

怎么用dspy.Assertdspy.Suggest

当你在 DSPy 中使用 dspy.Assertdspy.Suggest 时,你需要通过布尔验证检查来定义程序中的约束条件,以确保期望的结果。这些约束条件被实现为简单的 Python 函数,用于验证模型的输出。

主要的区别在于它们的严格性。

  • **Assert** 断言强制执行程序必须满足的严格条件;否则将会失败。
  • **Suggest** 提供非强制性的建议来提升性能或改善输出质量。

这里有一个简单的DSPy模块验证示例。

    从 dspy.primitives.assertions 导入 assert_transform_module, backtrack_handler  # 使用 assert_transform_module 和 backtrack_handler 模块
    # 定义一个验证函数  
    # 可以定义你自己的验证函数,用于特定的程序  

    # 一个简单的验证函数  
    def your_validation_fn(model_outputs):  
        return model_outputs.one_word_answer == "Islamabad"  

    # 一个简单的 DSPy 程序,用于回答问题的一词回答  
    your_module = dspy.ChainOfThought("question->one_word_answer")  

    model_outputs = your_module(question="巴基斯坦的首都是什么?")  

    # 在程序中添加断言  

    dspy.Assert(your_validation_fn(model_outputs), "验证失败", target_module=your_module)  # 如果验证失败,显示错误信息
    dspy.Suggest(your_validation_fn(model_outputs), "考虑修订输出", target_module=your_module)  # 如果需要,建议修改输出

你甚至可以将断言添加到自定义程序的前向函数中。定义完这些约束条件后,你可以通过使用 assert_transform_module 函数将断言包裹在你的 DSPy 模块周围,并使用一个 backtrack_handler。该函数将程序转换为包含内部断言和回溯重试逻辑的形式。

    class one_word_answer(dspy.Module):  
        def __init__(self):  
            self.one_word_program = dspy.ChainOfThought("question->one_word_answer")  

        def forward(self, question):  
            # 将问题发送给 one_word_program
            response = self.one_word_program(question=question)  
            # 断言检查是否为单个单词
            dspy.Assert(response.one_word_answer.count(" ") == 0, "验证失败,不是单一答案", target_module=self.one_word_program)  
            return response  

    my_module = one_word_answer()  

    # 一种激活模块中断言的方法,当断言失败时会抛出错误
    program_with_assertions = assert_transform_module(my_module(), backtrack_handler)  

    # 另一种方法激活断言
    program_with_assertions = my_module().activate_assertions()  

    # 如果输出不是单个单词或包含空格,程序将抛出错误。

作者提供的图片 — 如果输出验证失败,你将遇到的错误,你可以捕获该错误并实现你的解决方法

有大模型输出的难题或者需要开发AI解决方案的帮助?可以来这里联系我和我的团队:https://tally.so/r/3x9bgo

使用多个大语言模型

大型程序通常需要多个LLM。这里有三个原因:

  • 首先,不同的LLM擅长处理不同类型的数据。
  • 其次,多个LLM可以协同工作,提升程序的整体性能。
  • 最后,使用多个LLM能增强程序的稳定性和可靠性。
  1. 成本:当你用顶级模型来处理上千个请求时,成本会非常高,因为很多请求可以用较低端的模型来处理。
  2. 速度:所有的大型语言模型(LLM)API提供商都有基于不同层级的请求限制,但你的用户请求需要在规定时间内得到响应。你可以将请求发送到不同的API或模型中。
  3. 输出质量:你可以参考LLM基准测试,看看哪个模型在处理特定请求时表现更好。有些模型在代码生成方面表现更佳(在成本相同的情况下)。
  4. 系统过载:许多企业用例更偏好本地部署的LLM,将所有请求都指向一个LLM时,可能会导致系统过载。

在DSPy中,你定义一个全局LLM。这是系统在你请求时会默认使用的全局LLM。你可以这样定义全局LLM:

    lm = dspy.LM('openai/gpt-4o', model_type='chat', max_tokens=1000, api_key='<>')
    dspy.configure(lm=lm)

你可以像这样切换LM,但每次更改全局设置可能会遇到问题,特别是在你同时发出多个请求时(这在大型系统中很常见)。

幸运的是,你可以像这样用上下文管理器来切换大型语言模型。它还是线程安全的,并且支持异步操作。

    # 定义LLMs
    lm1 = dspy.LM()
    lm2 = dspy.LM()
    lm3 = dspy.LM()

    # 你可以在你的DSPy模块里设置这些条件
    # 系统根据这些条件来切换
    if condition1:
      with dspy.settings.context(lm=lm1):
        response = dspy.Predict()
    if condition2:
      with dspy.settings.context(lm=lm2):
        response = dspy.Predict()
    if condition3:
      with dspy.settings.context(lm=lm3):
        response = dspy.Predict()

多个大型语言模型确实不错,但如果需要处理多个请求来满足用户需求怎么办?下一章将教你如何使用DSPy进行异步或并行请求处理。

构建“自动分析师”——一个数据解析AI系统的技术指南

技术指南:构建AI“自动分析师”

并发请求或异步调用

构建最小可行原型很简单,因为它们通常一次只服务一个用户。但是当你需要扩展以处理成千上万甚至上百万的请求时会怎样呢?在这种情况下,你可以利用多线程或异步请求来高效地管理负载。以下是具体做法:

使用DSPy的Asyncify功能

这个小代码段可以帮助你在程序中进行异步请求,

    导入 asyncio  

    # 设置异步工人的最大数量  
    dspy.settings.configure(async_max_workers=4)  # 默认是8  

    # 定义你的模块的异步版本  
    # 系统之前已经定义过,但可以是任何dspy.Module  
    异步系统模块 = dspy.asyncify(one_word_answer())  # 将 one_word_answer() 转换为异步版本  

    # 一个简单的异步程序  
    async def 获取响应(问题):  
    # 创建一些协程用于稍后的等待  
      任务 = [异步系统模块(question=q) for q in 问题]  
    # 你也可以使用 asyncio.wait, as_completed, 等待等  
      响应 = await asyncio.gather(*任务)  
      return 响应  

    问题 = [  
        "你的名字是什么?",  
        "你多大了?",  
        "你来自哪里?",  
        "你最喜欢的爱好是什么?",  
        "你做什么工作?",  
        "你最喜欢的编程语言是什么?",  
        "你明年有什么计划或目标?",  
        "你最喜欢的书或电影是什么?",  
        "你有没有去过国外?",  
        "是什么激励你学习新技能?(例如,挑战、兴趣等)"  
    ]  

    # 运行异步程序  
    所有响应 = asyncio.run(获取响应(问题))  

上述代码片段,允许你异步等待响应消息。你可以调整 async_max_worker 参数以一次处理多达 1000 个工作者。

使用DSPy的线程功能

在 dspy 的最新版本中,每个模块都有一个内置的 batch 方法,让你能够并行发送请求。

这里列出的是该方法的输入。

图片由作者提供 — 展示的是批量处理方法的记录。

代码片段:

    导入 dspy

    # 创建用于分析的问题  
    问题 = [  
        "客户终身价值和交易频率之间有什么关系?",  
        "平均交易金额如何影响客户的留存率?",  
        "哪些客户细分显示出最高的预测CLV?为什么?",  
        "影响我们CLV预测准确性的关键因素是什么?",  
        "我们如何根据当前结果改进CLV预测模型?"  
    ]  

    # 创建一个小型DSPy模块,它提供一个单词的答案  
    系统 = dspy.ChainOfThought("问题->一个单词的答案")  

    # 创建示例,这些示例可以发送到模块的批处理  
    问题 = [dspy.Example(question=q).with_inputs('question') for q in 问题]  

    # 并行发送所有五个问题,带有某些设置  
    响应们 = 系统.batch(问题, num_threads = 3,return_failed_examples=True,max_errors=3)

图片由作者提供 — 批处理回复

通过使用线程或异步技术,我们可以同时为数百万用户提供服务。

感谢您的阅读,我会在学到更多东西时与大家分享。请关注我以及FireBird科技,并支持我们。

想利用我的经验吗?可以点击这个链接寻求帮助:https://tally.so/r/3x9bgo

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP