使用Ollama本地离线体验SimpleRAG(手把手教程) - mingupupup - 博客园

mikel阅读(312)

来源: 使用Ollama本地离线体验SimpleRAG(手把手教程) – mingupupup – 博客园

Ollama介绍

Ollama是一个开源项目,专注于开发和部署大语言模型,特别是像LLaMA这样的模型,用于生成高质量的文本和进行复杂的自然语言处理任务。Ollama的目标是让大语言模型的运行和使用变得更加容易和普及,而无需复杂的基础设施或深度的机器学习知识。

image-20240822110024317

GitHub地址:https://github.com/ollama/ollama

RAG是什么?

检索生成增强(Retrieval-Augmented Generation,RAG)是一种结合了检索(Retrieval)和生成(Generation)两种技术的自然语言处理方法,主要用于改进文本生成任务的性能,如问答系统、对话系统、文本摘要和文档生成等。RAG模型通过在生成模型的基础上,引入一个检索模块,来增强生成模型的准确性和丰富性。

在传统的生成模型中,模型完全依赖于训练数据中学习到的模式和统计信息来生成文本,这可能导致生成的内容缺乏新颖性或准确性。而检索模块则可以从外部知识库或文档中检索相关的信息,将这些信息作为额外的输入,提供给生成模型,从而帮助生成更准确、更丰富和更具体的文本。

具体来说,RAG模型的工作流程如下:

  1. 检索阶段:模型首先根据输入的查询或上下文,从外部知识库中检索出与之最相关的文档或片段。
  2. 融合阶段:检索到的信息与输入的查询或上下文进行融合,形成增强的输入。
  3. 生成阶段:增强后的输入被送入生成模型,生成模型根据这些信息生成最终的文本输出。

通过这种方式,RAG模型能够在生成过程中利用到外部知识,提高了生成文本的准确性和丰富性,同时也增强了模型的可解释性,因为生成的文本可以追溯到具体的来源。RAG模型在处理需要大量领域知识或具体事实信息的任务时,表现出了显著的优势。

SimpleRAG介绍

A simple RAG demo based on WPF and Semantic Kernel.

SimpleRAG是基于WPF与Semantic Kernel实现的一个简单的RAG应用,可用于学习与理解如何使用Semantic Kernel构建一个简单的RAG应用。

image-20240822100239041

GitHub地址:https://github.com/Ming-jiayou/SimpleRAG

主要功能

AI聊天

支持所有兼容OpenAI格式的大语言模型:

image-20240819163701855

文本嵌入

支持所有兼容OpenAI格式的嵌入模型:

image-20240819163900106

简单的RAG回答

简单的RAG回答效果:

image-20240819164221306

对比不使用RAG的回答:

image-20240819164322893

使用Ollama本地离线体验SimpleRAG

来到SimpleRAG的GitHub参考,注意到这里有个Releases:

image-20240822100649148

点击SimpleRAG-v0.0.1,有两个压缩包,一个依赖net8.0-windows框架,一个独立:

image-20240822100817138

依赖框架的包会小一些,独立的包会大一些,如果你的电脑已经装了net8.0-windows框架可以选择依赖框架的包,考虑到可能大部分人不一定装了net8.0-windows框架,我以独立的包做演示,点击压缩包,就在下载了:

image-20240822101244281

解压该压缩包:

image-20240822101450182

打开appsettings.json文件:

image-20240822101600329

appsettings.json文件如下所示:

image-20240822101740892

在你的电脑上启动Ollama,在命令行中输入ollama list 查看已经下载的模型:

image-20240822113619155

由于我电脑的配置不是很好,对话模型以gemma2:2b为例,嵌入模型以bge-m3:latest为例,appsettings.json文件这样写:

image-20240822113903239

Endpoint输入Ollama的地址,默认是http://localhost:11434,Ollama不需要Api Key随便写。

现在点击SimpleRAG.exe即可运行程序:

image-20240822102117959

程序运行之后,如下所示:

image-20240822102215516

先通过AI聊天测试配置是否成功:

image-20240822114300380

配置已经成功。

现在来测试一下嵌入。

先拿一个简单的文本进行测试:

小k最喜欢的编程语言是C#。

image-20240822114549483

嵌入成功:

image-20240822114618014

这个Demo程序为了方便存储文本向量使用的是SQLite数据库,在这里可以看到:

image-20240822102554159

如果你有数据库管理软件的话,打开该数据库,会发现文本已经以向量的形式存入SQLite数据库中:

image-20240822114904572

现在开始测试RAG回答效果:

image-20240822115055457

对比不使用RAG的回答效果:

image-20240822115204218

可以发现大语言模型根本不知道我们想问的私有数据的事情。

现在我们可以来测试一下更复杂一点的文本了,一样的嵌入文本之后,测试RAG效果:

image-20240822115513523

RAG回答失败了,这是因为我使用的模型参数太少了,还不够强大。如果你的电脑配置好,可以改用更智能的模型,如果你的电脑配置不好,可以选择混合使用的方式,即使用在线的对话模型Api,使用本地Ollama中的嵌入模型。

使用在线对话Api+本地Ollama嵌入模型体验SimpleRAG

appsettings.json可以这样写:

image-20240822120347160

测试RAG效果:

image-20240822120526269

RAG还是失败了。

模型换成meta-llama/Meta-Llama-3.1-8B-Instruct:

image-20240822120706462

模型换成google/gemma-2-9b-it:

image-20240822121018509

模型换成Qwen/Qwen2-72B-Instruct:

image-20240822121616949

通过源码找原因:

image-20240822122700793

将相关度调成0.3就可以找到相关文本了,但是感觉这样也会出问题,文档一多很容易找到不相关的文档,后面appsettings.json中会增加相关度的配置:

image-20240822122749303

现在再测试一下Qwen/Qwen2-7B-Instruct:

image-20240822123253249

也可以了。

对比不使用RAG的回答效果:

image-20240822123617132

最后

如果对你有所帮助,点个Star✨,就是最大的支持😊。

如果您看了指南,还是遇到了问题,欢迎通过我的公众号联系我:

C# AIModelRouter:使用不同的AI模型完成不同的任务 - mingupupup - 博客园

mikel阅读(345)

来源: C# AIModelRouter:使用不同的AI模型完成不同的任务 – mingupupup – 博客园

AIModelRouter

AI模型路由,模型的能力有大小之分,有些简单任务,能力小一点的模型也能很好地完成,而有些比较难的或者希望模型做得更好的,则可以选择能力强的模型。为什么要这样做呢?可以降低AI模型的使用成本,毕竟能力强的模型会更贵一点,省着用挺好的。

Semantic Kernel中可以很简便地使用一个AIModelRouter。

实践

先来一个简单的例子

来自https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/Demos/AIModelRouter

新建一个CustomRouter类,如下所示:

internal sealed class CustomRouter()
{
    internal string GetService(string lookupPrompt, List<string> serviceIds)
    {
        // The order matters, if the keyword is not found, the first one is used.
        foreach (var serviceId in serviceIds)
        {
            if (Contains(lookupPrompt, serviceId))
            {
                return serviceId;
            }
        }

        return serviceIds[0];
    }

    // Ensure compatibility with both netstandard2.0 and net8.0 by using IndexOf instead of Contains
    private static bool Contains(string prompt, string pattern)
        => prompt.IndexOf(pattern, StringComparison.CurrentCultureIgnoreCase) >= 0;
}

新建一个SelectedServiceFilter类用于打印一些信息:

 internal sealed class SelectedServiceFilter : IPromptRenderFilter
 {
     /// <inheritdoc/>
     public Task OnPromptRenderAsync(PromptRenderContext context, Func<PromptRenderContext, Task> next)
     {
         Console.ForegroundColor = ConsoleColor.Yellow;
         Console.WriteLine($"Selected service id: '{context.Arguments.ExecutionSettings?.FirstOrDefault().Key}'");

         Console.ForegroundColor = ConsoleColor.White;
         Console.Write("Assistant > ");
         return next(context);
     }
 }

使用多个模型:

image-20250106101815911

为捕获路由器选择的服务 ID 添加自定义过滤器:

image-20250106101942229

开启一个聊天循环:

        Console.ForegroundColor = ConsoleColor.White;

        ChatHistory history = [];
        string history1 = string.Empty;
        bool isComplete = false;

        do
        {
            Console.WriteLine();
            Console.Write("> ");
            string? input = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(input))
            {
                continue;
            }
            if (input.Trim().Equals("EXIT", StringComparison.OrdinalIgnoreCase))
            {
                isComplete = true;
                break;
            }
            if (input.Trim().Equals("Clear", StringComparison.OrdinalIgnoreCase))
            {
                history.Clear();
                history1 = " ";
                Console.WriteLine("已清除聊天记录");
                continue;
            }

            history.Add(new ChatMessageContent(AuthorRole.User, input));
            history1 += $"User:{input}\n";

            Console.WriteLine();

            // Find the best service to use based on the user's input
            KernelArguments arguments = new(new PromptExecutionSettings()
            {
                ServiceId = router.GetService(input, serviceIds).Result,
                FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
            });

            // Invoke the prompt and print the response
            //await foreach (var chatChunk in kernel.InvokePromptStreamingAsync(userMessage, arguments).ConfigureAwait(false))
            //{
            //    Console.Write(chatChunk);
            //}
           
            var result = await kernel.InvokePromptAsync(history1, arguments).ConfigureAwait(false);
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(result);
            Console.WriteLine();

            // Add the message from the agent to the chat history
            history.AddMessage(AuthorRole.Assistant, result.ToString());
            history1 += $"Assistant:{result}\n";
        } while (!isComplete);
    }
}

来看看现在这个简单的路由规则:

image-20250106102824888

当你的提问中包含一个ServiceId的时候,就会选择那个服务ID对应的模型进行回复,如果不包含就选择第一个服务ID对应的模型进行回复。

实际上这样使用,很容易让AI迷惑,因为我们总是要带上一个ServiceId,如果让AI根据用户的提问,自己决定用哪个模型是更好的。

进阶使用,用AI自己来决定

image-20250106103343454

使用一个靠谱的AI模型来做这个事情比较好。

我们输入你好,那么Prompt就会变成这样:

image-20250106103624167

AI返回的结果如下:

image-20250106103713305

image-20250106103742224

再试试其他几个怎么触发:

image-20250106103848889

而工具调用与其他比较容易混淆,因为就算是我们自己,也很难分辨有什么区别:

image-20250106104310185

这时候或许修改Prompt可以奏效。

修改后的Prompt如下:

 string skPrompt = """
          根据用户的输入,返回最佳服务ID。
          如果用户需要获取当前时间与写邮件,则选择工具调用相关的服务ID。
          用户输入:
          {{$input}}
          服务ID列表:
          {{$serviceIds}}
          无需返回任何其他内容,只需返回服务ID。              
     """;

效果如下所示:

image-20250106113558077

以上就是本次分享的全部内容,希望对你有所帮助。

AutoGen入门-让两个AI自行聊天完成任务 - mingupupup - 博客园

mikel阅读(836)

来源: AutoGen入门-让两个AI自行聊天完成任务 – mingupupup – 博客园

AutoGen介绍

AutoGen 是一个开源编程框架,用于构建 AI 代理并促进多个代理之间的合作以解决问题。AutoGen 旨在提供一个易于使用和灵活的框架,以加速代理型 AI 的开发和研究,就像 PyTorch 之于深度学习。它提供了诸如代理之间可以对话、LLM 和工具使用支持、自主和人机协作工作流以及多代理对话模式等功能。

主要特点

AutoGen使得基于多智能体对话构建下一代LLM应用程序变得非常容易。它简化了复杂LLM工作流的编排、自动化和优化。它最大化了LLM模型的性能并克服了它们的弱点。

它支持复杂工作流的各种对话模式。通过使用可定制和可对话的代理,开发人员可以使用AutoGen构建各种涉及对话自主性、代理数量和代理对话拓扑的对话模式。

它提供了一系列不同复杂度的运行系统。这些系统涵盖了来自不同领域和复杂度的各种应用程序。这展示了AutoGen如何轻松支持各种对话模式。

入门

现在AutoGen有两个版本,一个是0.2,是稳定版本,一个是0.4,是正在开发的版本。

本次入门使用的是开发版本。

首先创建一个Python虚拟环境:

python -m venv myenv

激活虚拟环境:

myenv\Scripts\activate

安装相关包:

pip install "autogen-agentchat==0.4.0.dev13"
pip install "autogen-ext[openai]==0.4.0.dev13"

后面运行时,会显示还缺少几个库,按照报错的信息,继续安装所需的包即可。

入门的例子是创建一个AI团队,一个当任务执行者,一个当评判者,当评判者觉得可以了,就停止对话,完成任务。

Python代码如下:

import asyncio

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.base import TaskResult
from autogen_agentchat.conditions import ExternalTermination, TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_core import CancellationToken
from autogen_ext.models.openai import OpenAIChatCompletionClient

async def main():

    # Create an OpenAI model client.
    model_client = OpenAIChatCompletionClient(
        model="gpt-4o-2024-08-06",
        api_key="", # Optional if you have an OPENAI_API_KEY env variable set.
        )

    # Create the primary agent.
    primary_agent = AssistantAgent(
        "primary",
        model_client=model_client,
        system_message="You are a helpful AI assistant.",
    )

    # Create the critic agent.
    critic_agent = AssistantAgent(
        "critic",
        model_client=model_client,
        system_message="Provide constructive feedback. Respond with 'APPROVE' to when your feedbacks are addressed.",
    )

    # Define a termination condition that stops the task if the critic approves.
    text_termination = TextMentionTermination("APPROVE")


    team = RoundRobinGroupChat(
        [primary_agent, critic_agent],
        termination_condition=text_termination,  # Use the bitwise OR operator to combine conditions.
    )

    await Console(team.run_stream(task="写一首关于秋天的短诗"))

    
if __name__ == "__main__":
    asyncio.run(main())

运行效果:

---------- user ----------
写一首关于秋天的短诗
---------- primary ----------
秋风轻摇叶漫天,  
金黄稻穗笑丰年。  
枫红染尽山川路,  
硕果满枝惹人怜。  

晨霜点缀草间珠,  
夕阳映影湖中船。  
静谧凉月盈满空,  
秋夜温柔如梦甜。  
[Prompt tokens: 28, Completion tokens: 81]
---------- critic ----------
这首关于秋天的短诗运用了丰富的意象生动地描绘了秋天的美丽。以下是一些建议来进一步提升这首诗:

1. 开头的两行已经相当完整,但可以考虑将“轻摇”替换为更具体的动词,比如“飘落”,以增强画面感。
2. 第二节中的“晨霜点缀草间珠”可以更加具体化,比如但不局限于使用“白霜”,进一步富有诗意地描述秋季清晨的细节。
3. 尾句“秋夜温柔如梦甜”可以再结合一个感官描写,比如“如梦”可以结合沁人心脾的“桂香扑鼻”之类的细节来丰富整体体验。

这些建议旨在增加诗歌的生动性和情感深度,整体上已经非常优秀。请进行相应的改动后,我将很高兴地给予批准。
[Prompt tokens: 126, Completion tokens: 215]
---------- primary ----------
非常感谢您的建议!根据您的意见,我对诗歌做了一些修改:

秋风飘落叶漫天,  
金黄稻穗笑丰年。  
枫红染尽山川路,  
硕果满枝惹人怜。  

白霜点缀草间珠,  
夕阳映影湖中船。  
桂香扑鼻月满空,  
秋夜温柔如梦甜。  

希望这些改动能够增强诗歌的生动性,带来更加丰富的秋季体验。
[Prompt tokens: 334, Completion tokens: 120]
---------- critic ----------
改动后的诗歌确实更加生动且富有画面感。使用“飘落”更好地描绘了秋天的特征,而“白霜”和“桂香扑鼻”的加入使得诗歌在感官描述上更加具体和丰富。这 些改动有效地增强了诗歌的生动性和情感深度。因此,我准许该稿件。 

APPROVE
[Prompt tokens: 471, Completion tokens: 92]
---------- Summary ----------
Number of messages: 5
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 959
Total completion tokens: 508
Duration: 14.87 seconds

现在试试用一下国内的模型,毕竟使用OpenAI的模型不是很方便。

我以为改一下baseurl就行了,如下所示:

image-20250107163203243

运行时会报错,如下所示:

image-20250107163339701

现在需要解决这个问题。

改成这样:

image-20250107165213070

还是会出现问题:

image-20250107165043167

解决方案:

image-20250107170942735

改成这样就可以了,如下所示:

image-20250107171056791

让Qwen/Qwen2.5-72B-Instruct与deepseek-ai/DeepSeek-V2.5协作完成任务:

image-20250107171506469

image-20250107171534679

---------- user ----------
写一首关于秋天的短诗
---------- primary ----------
秋风轻抚过田野,
黄叶飘零诉离别。
果实累累挂枝头,
丰收的歌在空中回响。

白露凝霜晨光里,
红枫如火映晚晴。
秋水共长天一色,
宁静致远,思绪随风。
[Prompt tokens: 27, Completion tokens: 61]
---------- critic ----------
这首诗以自然意象为载体,描绘了秋天的丰富色彩与情感。秋风、黄叶、果实、白露、红枫等元素,形成了层次分明的秋日图景,表达了丰收与离别交替的复杂情感。诗中“宁静致远”一句,更是点明了秋天带给人们的沉思与感慨。整体语言流畅,意境深远,情感细腻,是一首不错的秋之赞歌。

以下是一些具体的反馈建议:

1. **“黄叶飘零诉离别”**:这一句的表达略显直白,可以考虑用更含蓄或独特的意象来表达离别之情,以增强诗歌的韵味。
   
2. **“秋水共长天一色”**:这一句虽然取自王勃的《滕王阁序》,但使用在此处稍显突兀,可能会让读者感受到某种经典符号的重复。可以考虑替换为更个性化的表达,与前文的自然意象形成更好 的呼应。

如果你能根据这些建议进行调整,这首诗将会更加出色。期待看到你的进一步修改!
[Prompt tokens: 103, Completion tokens: 239]
---------- primary ----------
非常感谢你的详细反馈和建议!根据你的意见,我将对这首诗进行一些调整,以增强其含蓄性和个性化表达。

---

秋风轻抚过田野,
黄叶轻舞似鸿羽。
果实累累挂枝头,
丰收的歌在空中回响。

白露凝霜晨光里,
红枫如火映晚晴。
秋水清澈连天际,
宁静致远,思绪随风。

---

1. **“黄叶轻舞似鸿羽”**:用“鸿羽”这一意象来表达黄叶的轻盈,增强视觉和情感的层次感。
2. **“秋水清澈连天际”**:替换“秋水共长天一色”为更个性化的表达,使意境更加独特和自然。

希望这些调整能更好地传达秋天的美好与复杂情感。感谢你的指导!
[Prompt tokens: 332, Completion tokens: 182]
---------- critic ----------
调整后的诗句更显细腻与含蓄,黄叶与秋水的意象都更加个性化,情感表达也更加丰富。以下是具体反馈:

1. **“黄叶轻舞似鸿羽”**:这一修改非常成功,“鸿羽”不仅增添了轻盈之感,还赋予了黄叶一种飘逸的动态美,增强了诗歌的视觉与情感张力。

2. **“秋水清澈连天际”**:这一改写避免了经典句式的重复,同时营造了秋水澄澈、连通天际的广阔意境,使整体氛围更加宁静致远。

3. **情感表达**:诗歌整体情感更加内敛,通过自然意象的细腻描绘,既表达了丰收的喜悦,又蕴含了秋天的宁静与思索,尤其最后一句“宁静致远,思绪随风”更是将情感推向了深远的境界。     

总体来说,这首诗的意境已经非常成熟,语言流畅,情感细腻,具有很高的艺术表现力。如果后续没有更多的修改需求,我认为可以定稿。

**APPROVE**
[Prompt tokens: 547, Completion tokens: 239]
---------- Summary ----------
Number of messages: 5
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 1009
Total completion tokens: 721
Duration: 75.84 seconds

DeepSeekV3+Roo Code,智能编码好助手 - mingupupup - 博客园

mikel阅读(784)

来源: DeepSeekV3+Roo Code,智能编码好助手 – mingupupup – 博客园

前言

硅基流动最近上线了deepseek-ai/DeepSeek-R1与deepseek-ai/DeepSeek-V3,感兴趣快来试试吧!

邀请注册得14元不过期额度:https://cloud.siliconflow.cn/i/Ia3zOSCU。

image-20250202084545172

实践

最近VS Code中的自动编程插件Cline很火爆,Roo Code也是Cline的一个fork版本。

Cline

自主编码代理直接集成在您的 IDE 中,能够创建/编辑文件,执行命令,使用浏览器等,每一步都需您的许可。

image-20250202081003394

首先在Cline中接入SiliconCloud可以使用OpenAI Compatible这种方式,配置如下所示:

image-20250202081241737

以创建一个使用ts的next.js项目todo-demo为例,查看效果。

之前使用Cline还好好的,但是最近遇到了这个问题:

image-20250202081507401

因此开始尝试使用Roo Code。

Roo Code

Roo Code(原名 Roo Cline)是 VS Code 插件,通过 AI 驱动的自动化、多模型支持和实验性功能增强编码。

image-20250202081802147

安装Roo Code:

image-20250202081858277

配置方式与Cline相同:

image-20250202081933858

开始测试使用DeepSeek-V3的效果:

image-20250202082142598

运行的每一个命令,都需要你同意。

安装next.js模板:

image-20250202082239724

安装antd:

image-20250202082357679

自动编写代码:

image-20250202082455957

写完的代码还有一个报错,但是显示任务已完成:

image-20250202082754213

如果自己解决不了,继续让Roo Code解决:

image-20250202083305520

查看效果:

image-20250202083551586

感觉还可以。

推荐给大家,可以提升自己的编码效率。

image-20250202084031409

token消耗量也还可以,只花了12万多,只用了0.13元。

AI工具推荐——open-interpreter - mingupupup - 博客园

mikel阅读(287)

来源: AI工具推荐——open-interpreter – mingupupup – 博客园

前言

Open Interpreter 是一个能让大型语言模型在你本地电脑上运行代码的工具。 简单来说:

它提供了一个类似于 ChatGPT 的自然语言界面,让你能通过代码与电脑互动。 你可以用它来:

  • 创建和编辑各种类型的文件(照片、视频、PDF 等)。
  • 控制 Chrome 浏览器进行研究。
  • 分析数据集。

工作原理: Open Interpreter 为语言模型配备了一个函数,该函数可以在 Python、JavaScript 和 Shell 等各种语言中执行代码。 然后,它会将模型的消息、正在运行的代码和系统的输出流式传输到你的终端。

主要特点:

  • 它在本地运行,无需担心网络限制或文件大小限制。
  • 它通过自然语言界面简化了复杂的任务。

该项目目前已经收获了58.2kstars,GitHub地址:https://github.com/OpenInterpreter/open-interpreter

image-20250208094027464

我在体验之后,觉得是一个很棒的项目,因此推荐给大家。它的思路很好,我之前想的是创建很多工具,然后根据用户的意图,选择工具调用,但是用户需求是多变的,创建的工具不可能全部涵盖到,但是通过open-interpreter这种方式,就可以应对用户多变的需求了。

实践

首先创建一个python虚拟环境,安装open-interpreter:

pip install open-interpreter

虽然官方推荐使用gpt-40,但是为了在国内更多感兴趣的朋友能够方便体验,这里我以硅基流动提供的模型为例。现在硅基流动也有DeepSeek-R1并且邀请注册可得14元不过期额度,邀请链接:https://cloud.siliconflow.cn/i/Ia3zOSCU。

由于我硅基流动还有很多额度,接下来演示如何接入硅基流动的模型。

from interpreter import interpreter

interpreter.llm.model = "openai/deepseek-ai/DeepSeek-V3"
interpreter.llm.api_key = "sk-xxx"
interpreter.llm.api_base = "https://api.siliconflow.cn/v1"
interpreter.llm.context_window = 64000

# Start an interactive chat session
interpreter.chat()

第一个踩坑点,注意要加上openai表示是openai兼容的,不然报错如下:

image-20250208095153912

现在我以一个简单的办公需求来演示open-interpreter的效果。

我在项目目录,放入了一个excel,该excel中的数据,如下所示:

image-20250208095524855

现在根据这个excel随机提需求。

问题1:读取test.xlsx内容,商品A的累计销量是多少?

image-20250208095743329

给出计划,命令行操作与运行代码需要你的同意。

image-20250208095955211

模型假设列名是Product A,失败之后,请求获取列名。

image-20250208100052033

image-20250208100144833

答案是82正确!!

问题2:读取test.xlsx内容,以月份为横坐标,商品B为纵坐标,画一个折线图,需要能显示中文。

image-20250208100313341

模型列出了计划。

image-20250208100450196

模型给出代码。

image-20250208100524122

绘图结果正确。

对比硅基流动平台不同模型的效果。

deepseek-ai/DeepSeek-V3:

meta-llama/Llama-3.3-70B-Instruct:

Qwen/Qwen2.5-72B-Instruct:

视频效果在AI工具推荐——open-interpreter

最后

open-interpreter这个项目非常有意思,可玩性非常高,感兴趣的朋友快去试试吧!!!

将它与自己的日常需求结合看看能不能提高自己的效率。

浏览器自动化与AI Agent结合项目browser-use初探 - mingupupup - 博客园

mikel阅读(757)

来源: 浏览器自动化与AI Agent结合项目browser-use初探 – mingupupup – 博客园

browser-use介绍

browser-use是将您的 AI 代理连接到浏览器的最简单方式。它通过提供一个强大且简单的接口来实现 AI 代理访问网站的自动化。

GitHub地址:https://github.com/browser-use/browser-use。目前已经获得了27.3k颗stars,2.7kforks,看得出来是一个比较热门的项目。我在上手体验了之后,发现确实是一个很有趣的项目,因此推荐给大家。

实践

上手也非常简单,创建一个python虚拟环境,pip install browser-use,再playwright install即可。

现在需要进行LLM的配置,官方推荐使用gpt-4o,但是为了降低成本,方便让看了教程感兴趣的人能够方便上手,这里我使用的是硅基流动提供的模型。目前硅基流动注册送14元不过期额度,够用一段时间的了,邀请链接:https://cloud.siliconflow.cn/i/Ia3zOSCU。如果你的额度不够了,但是也想体验一下,可以私聊我,我可以提供一个api key暂时供你快速上手体验,额度用差不多了,我就停止了。

创建一个.env文件,这样写:

Silicon_Cloud_API_KEY=xxx
Base_URL=https://api.siliconflow.cn
Model=Qwen/Qwen2.5-72B-Instruct

创建一个test脚本,这样写:

from langchain_openai import ChatOpenAI
from browser_use import Agent
from dotenv import load_dotenv
import os
load_dotenv()

import asyncio

api_key = os.getenv('Silicon_Cloud_API_KEY')
base_url = os.getenv('Base_URL')
model = os.getenv('Model')

llm = ChatOpenAI(model=model, api_key=api_key, base_url=base_url)

async def main():
    agent = Agent(
        task="获取https://github.com/OpenInterpreter/open-interpreter仓库的前五个问题",
        llm=llm,
        use_vision=False,
    )
    result = await agent.run()
    print(result)

asyncio.run(main())

查看效果:

image-20250212085716642

image-20250212085758634

image-20250212085834372

还生成了一个agent_history.gif可以查看流程:

将结果与实际对比:

image-20250212090403603

可以发现browser-use非常准确的获取了。

再使用一个更普遍的例子,就是获取当前微博前十的热搜。

from langchain_openai import ChatOpenAI
from browser_use import Agent
from dotenv import load_dotenv
import os
load_dotenv()

import asyncio

api_key = os.getenv('Silicon_Cloud_API_KEY')
base_url = os.getenv('Base_URL')
model = os.getenv('Model')

llm = ChatOpenAI(model=model, api_key=api_key, base_url=base_url)

async def main():
    agent = Agent(
        task="获取当前微博前十的热搜",
        llm=llm,
        use_vision=False,
    )
    result = await agent.run()
    print(result)

asyncio.run(main())

image-20250212090822384

image-20250212090907000

最后

以上就是使用硅基流动中的Qwen/Qwen2.5-72B-Instruct快速体验browser-use的效果。初步体验感觉是一个很有潜力的项目,将AI Agent与浏览器自动化结合确实可以做很多事情。

如何让大模型安全地自动生成代码并执行? - mingupupup - 博客园

mikel阅读(437)

来源: 如何让大模型安全地自动生成代码并执行? – mingupupup – 博客园

前言

本文带来的分享是在crewai中使用代码解释器,为了安全,代码在docker中运行。

为什么要使用代码解释器呢?

之前的文章中使用的是function call + 各种工具 来完成一个任务,比如文件读取工具、文件保存工具等。

但是用户的需求是多变的,你很难提前写好所有的工具。

读取文件内容,使用代码解释器的效果如下所示:

image-20250224100624862

实践

首先需要看下crewai中提供的代码解释器工具代码,学习一下。

代码在:https://github.com/crewAIInc/crewAI-tools/tree/main/crewai_tools/tools/code_interpreter_tool

文件如下:

image-20250224100921024

先来看一下Dockerfile:

FROM python:3.12-alpine

RUN pip install requests beautifulsoup4 

# Set the working directory
WORKDIR /workspace

这是一个Dockerfile的片段,用于构建一个Python环境的Docker镜像。解释如下:

FROM python:3.12-alpine:这行指定了基础镜像为Python 3.12版本的Alpine Linux镜像。Alpine Linux是一种轻量级的Linux发行版,非常适合作为Docker镜像的基础。
RUN pip install requests beautifulsoup4:这行使用pip安装两个Python包:requests和beautifulsoup4。requests是一个用于发送HTTP请求的库,而beautifulsoup4是一个用于解析HTML和XML文档的库。
WORKDIR /workspace:这行设置了容器中的工作目录为/workspace。当容器启动时,会自动进入这个目录。
总的来说,这个Dockerfile构建了一个Python环境,安装了两个常用的库,并将工作目录设置为/workspace。这个镜像可以用于运行Python应用程序,尤其是那些需要解析HTML和发送HTTP请求的应用程序。

再来看一下code_interpreter_tool.py的代码:

import importlib.util
import os
from typing import List, Optional, Type
from crewai.tools import BaseTool
from docker import from_env as docker_from_env
from docker import DockerClient
from docker.models.containers import Container
from docker.errors import ImageNotFound, NotFound
from docker.models.containers import Container
from pydantic import BaseModel, Field


class CodeInterpreterSchema(BaseModel):
    """
    Input for CodeInterpreterTool.
    供代码解释工具输入。
    """

    code: str = Field(
        ...,
        # Python3代码原来是在Docker容器中被解释的。始终打印最终结果和代码的输出。
        description="Python3 code used to be interpreted in the Docker container. ALWAYS PRINT the final result and the output of the code",
    )

    libraries_used: Optional[List[str]] = Field(
        None,
        # 代码中使用的库列表,带有适当的安装名称,以逗号分隔。例如: numpy,pandas,beautifulsoup4
        description="List of libraries used in the code with proper installing names separated by commas. Example: numpy,pandas,beautifulsoup4",
    )


class CodeInterpreterTool(BaseTool):
    name: str = "Code Interpreter"
    description: str = "Interprets Python3 code strings with a final print statement."
    args_schema: Type[BaseModel] = CodeInterpreterSchema
    default_image_tag: str = "code-interpreter:latest"
    code: Optional[str] = None
    user_dockerfile_path: Optional[str] = None
    user_docker_base_url: Optional[str] = None
    unsafe_mode: bool = False

    @staticmethod
    def _get_installed_package_path():
        spec = importlib.util.find_spec("crewai_tools")
        return os.path.dirname(spec.origin)

    def _verify_docker_image(self) -> None:
        """
        Verify if the Docker image is available. Optionally use a user-provided Dockerfile.
        验证Docker镜像是否可用。可选地使用用户提供的Dockerfile。
        """

        client = (
            docker_from_env()
            if self.user_docker_base_url == None
            else DockerClient(base_url=self.user_docker_base_url)
        )

        try:
            client.images.get(self.default_image_tag)

        except ImageNotFound:
            if self.user_dockerfile_path and os.path.exists(self.user_dockerfile_path):
                dockerfile_path = self.user_dockerfile_path
            else:
                package_path = self._get_installed_package_path()
                dockerfile_path = os.path.join(
                    package_path, "tools/code_interpreter_tool"
                )
                if not os.path.exists(dockerfile_path):
                    raise FileNotFoundError(
                        f"Dockerfile not found in {dockerfile_path}"
                    )

            client.images.build(
                path=dockerfile_path,
                tag=self.default_image_tag,
                rm=True,
            )

    def _run(self, **kwargs) -> str:
        code = kwargs.get("code", self.code)
        libraries_used = kwargs.get("libraries_used", [])

        if self.unsafe_mode:
            return self.run_code_unsafe(code, libraries_used)
        else:
            return self.run_code_in_docker(code, libraries_used)

    def _install_libraries(self, container: Container, libraries: List[str]) -> None:
        """
        Install missing libraries in the Docker container
        """
        for library in libraries:
            container.exec_run(["pip", "install", library])

    def _init_docker_container(self) -> Container:
        container_name = "code-interpreter"
        client = docker_from_env()
        current_path = os.getcwd()

        # Check if the container is already running
        try:
            existing_container = client.containers.get(container_name)
            existing_container.stop()
            existing_container.remove()
        except NotFound:
            pass  # Container does not exist, no need to remove

        return client.containers.run(
            self.default_image_tag,
            detach=True,
            tty=True,
            working_dir="/workspace",
            name=container_name,
            volumes={current_path: {"bind": "/workspace", "mode": "rw"}},  # type: ignore
        )

    def run_code_in_docker(self, code: str, libraries_used: List[str]) -> str:
        self._verify_docker_image()
        container = self._init_docker_container()
        self._install_libraries(container, libraries_used)

        exec_result = container.exec_run(["python3", "-c", code])

        container.stop()
        container.remove()

        if exec_result.exit_code != 0:
            return f"Something went wrong while running the code: \n{exec_result.output.decode('utf-8')}"
        return exec_result.output.decode("utf-8")

    def run_code_unsafe(self, code: str, libraries_used: List[str]) -> str:
        """
        Run the code directly on the host machine (unsafe mode).
        """
        # Install libraries on the host machine
        for library in libraries_used:
            os.system(f"pip install {library}")

        # Execute the code
        try:
            exec_locals = {}
            exec(code, {}, exec_locals)
            return exec_locals.get("result", "No result variable found.")
        except Exception as e:
            return f"An error occurred: {str(e)}"

主要的参数与描述如下:

code: str = Field(
        ...,
        # Python3代码原来是在Docker容器中被解释的。始终打印最终结果和代码的输出。
        description="Python3 code used to be interpreted in the Docker container. ALWAYS PRINT the final result and the output of the code",
    )

    libraries_used: Optional[List[str]] = Field(
        None,
        # 代码中使用的库列表,带有适当的安装名称,以逗号分隔。例如: numpy,pandas,beautifulsoup4
        description="List of libraries used in the code with proper installing names separated by commas. Example: numpy,pandas,beautifulsoup4",
    )

测试这个工具的代码:

from crewai import LLM, Agent, Crew, Process, Task
# from crewai_tools import CodeInterpreterTool
from code_interpreter_tool import CodeInterpreterTool
import os
from dotenv import load_dotenv
load_dotenv()

api_key = os.getenv('SiliconCloud_API_KEY')
base_url = os.getenv('SiliconCloud_API_BASE')
model = os.getenv('SiliconCloud_MODEL_NAME', 'openai/Qwen/Qwen2.5-72B-Instruct')  # Provide a default model if not set

agent_llm = LLM(
model=model,
base_url=base_url,
api_key=api_key
	)

# Create an LLM with a temperature of 0 to ensure deterministic outputs
# llm = LLM(model="gpt-4o-mini", temperature=0)

# Create an agent with the knowledge store
agent = Agent(
    role="Tool User",
    goal="You know how to use the tool.",
    backstory="""
    You are a master at using the tool.
    使用代码执行工具时,如果没有使用库,则libraries_used参数为空列表,需要写成libraries_used=[]。
    """,
    verbose=True,
    allow_delegation=False,
    llm=agent_llm,
    tools=[CodeInterpreterTool()],
)
task = Task(
    description="""
    根据用户需求:{question}
    使用相应的工具。
    """,
    expected_output="获取本次工具输出的结果,无需其它内容。",
    agent=agent,
)

crew = Crew(
    agents=[agent],
    tasks=[task],
    verbose=True,
    process=Process.sequential,
)

result = crew.kickoff(
    inputs={
        "question": "获取test2.txt的内容"
    }
)

在这个地方可以输入你的需求看看代码解释器能不能实现。

先来一个最简单的:

result = crew.kickoff(
    inputs={
        "question": "输出你好世界"
    }
)

image-20250224101613286

再来一个创建文件的:

result = crew.kickoff(
    inputs={
        "question": "创建test3.txt,里面写入我是一只鱼。"
    }
)

image-20250224101933531

image-20250224102041955

再试一下需要使用库的:

result = crew.kickoff(
    inputs={
        "question": "画一个折线图,横坐标为1月、2月、3月,纵坐标为12、15、17,将结果保存到test.png。"
    }
)

image-20250224102742800

image-20250224102759227

中文字体没有正常显示,修改需求:

画一个折线图,横坐标为1月、2月、3月,纵坐标为121517,需要显示中文字体,将结果保存到test.png。

还是没有成功。

在docker环境中没有安装中文字体。

image-20250224103628011

并且使用代码解释器的一个缺点就是你无法保证AI生成的代码就是能用的,稍微复杂一点的需求,就有可能生成无法运行的代码。

image-20250224103800196

这样来看代码解释器的可玩性还是有很大的限制。

但是确实可以替代一些简单的工具了。

使用crewai创建属于你自己的AI团队 - mingupupup - 博客园

mikel阅读(311)

来源: 使用crewai创建属于你自己的AI团队 – mingupupup – 博客园

crewai介绍

CrewAI 是一个用于协调自主 AI 代理的前沿框架。

CrewAI 允许你创建 AI 团队,其中每个代理都有特定的角色、工具和目标,协同工作以完成复杂任务。

把它想象成组建你的梦之队——每个成员(代理)都带来独特的技能和专业知识,无缝协作以实现你的目标。

最近使用了crewai这个框架,我觉得是一个比较好用的AI Agent框架,因此推荐给大家。

在crewai中涵盖了AgentsTasksCrewsFlowsKnowledgeLLMsTools等这些核心概念。

接下来我将以一个具体的例子,介绍一下crewai的使用。

crewai的GitHub地址为:https://github.com/crewAIInc/crewAI

image-20250219164001573

使用crewai构建一个翻译代理

创建一个python虚拟环境,安装crewai与crewai-tools。

运行命令:

crewai create crew translation_agent

会出现一个模板项目。

在config目录下,使用yaml配置agent与task:

image-20250219164418938

先来设置一下代理:

file_reader:
  role: >
    读取文件代理
  goal: >
    根据文件路径,读取文件内容
  backstory: >
    你是一个文件读取代理,你的任务是根据文件路径,读取文件内容
​
translation_agent:
  role: >
    翻译代理
  goal: >
    根据用户需求翻译文本
  backstory: >
    你是一个翻译代理,你的任务是根据用户需求翻译文本
​
file_saver:
  role: >
    文件保存代理
  goal: >
    根据用户需求保存文件
  backstory: >
    你是一个文件保存代理,你的任务是根据用户需求保存文件

在这里设置了三个代理,分别是读取文件代理、翻译代理与文件保存代理。

再来配置一下task:

file_read_task:
  description: >
    根据用户需求:{question}
    获取需要读取的文件路径
    使用工具读取文件内容
  expected_output: >
    返回文件内容
  agent: file_reader
​
translation_task:
  description: >
    根据file_reader获取的文件内容,将文本翻译成英文
  expected_output: >
    返回翻译后的文本内容
  agent: translation_agent
​
file_save_task:
  description: >
   根据用户需求:{question}提取出需要保存到的文件路径及相关信息
   将translation_agent的翻译内容,保存至指定文件
  expected_output: >
    返回保存结果
  agent: file_saver

设置了三个任务,分别是file_read_task、translation_task与file_save_task。

完成这些任务,需要代理能够使用读取文件与保存文件的工具。

在tools目录下可以写工具代码:

image-20250219165027457

file_read_tool工具代码:

from typing import Any, Optional, Type
​
from crewai.tools import BaseTool
from pydantic import BaseModel, Field
​
​
class FileReadToolSchema(BaseModel):
    """Input for FileReadTool."""
​
    # Mandatory file full path to read the file
    # 必须的文件全路径,以读取文件
    file_path: str = Field(..., description="Mandatory file full path to read the file")
​
​
class FileReadTool(BaseTool):
    """A tool for reading file contents.
​
    This tool inherits its schema handling from BaseTool to avoid recursive schema
    definition issues. The args_schema is set to FileReadToolSchema which defines
    the required file_path parameter. The schema should not be overridden in the
    constructor as it would break the inheritance chain and cause infinite loops.
​
    The tool supports two ways of specifying the file path:
    1. At construction time via the file_path parameter
    2. At runtime via the file_path parameter in the tool's input
​
    Args:
        file_path (Optional[str]): Path to the file to be read. If provided,
            this becomes the default file path for the tool.
        **kwargs: Additional keyword arguments passed to BaseTool.
​
    Example:
        >>> tool = FileReadTool(file_path="/path/to/file.txt")
        >>> content = tool.run()  # Reads /path/to/file.txt
        >>> content = tool.run(file_path="/path/to/other.txt")  # Reads other.txt
​
    用于读取文件内容的工具。
​
    该工具继承自 BaseTool 的 schema 处理,以避免递归 schema 定义问题。args_schema 设置为 FileReadToolSchema,定义了必需的 file_path 参数。构造函数中不应该覆盖 schema,否则会破坏继承链并导致无限循环。
​
    该工具支持两种指定文件路径的方法:
​
    在构造时通过 file_path 参数
    在运行时通过工具的输入参数 file_path
​
    参数:
    file_path (可选[str]): 要读取的文件路径。如果提供,则成为工具的默认文件路径。
    **kwargs: 传递给 BaseTool 的其他关键字参数。
​
    示例:
    >>> tool = FileReadTool(file_path="/path/to/file.txt")
    >>> content = tool.run()  # 读取 /path/to/file.txt
    >>> content = tool.run(file_path="/path/to/other.txt")  # 读取 other.txt
    """
​
    name: str = "Read a file's content"
    description: str = "A tool that reads the content of a file. To use this tool, provide a 'file_path' parameter with the path to the file you want to read."
    args_schema: Type[BaseModel] = FileReadToolSchema
    file_path: Optional[str] = None
​
    def __init__(self, file_path: Optional[str] = None, **kwargs: Any) -> None:
        """
        Initialize the FileReadTool.
​
        Args:
            file_path (Optional[str]): Path to the file to be read. If provided,
                this becomes the default file path for the tool.
            **kwargs: Additional keyword arguments passed to BaseTool.
​
        初始化 FileReadTool。
​
        参数:
        file_path(可选[str]):要读取的文件路径。如果提供,则此路径成为工具的默认文件路径。
        **kwargs:传递给 BaseTool 的其他关键字参数。
        """
​
        if file_path is not None:
            kwargs['description'] = f"A tool that reads file content. The default file is {file_path}, but you can provide a different 'file_path' parameter to read another file."
​
        super().__init__(**kwargs)
        self.file_path = file_path
​
    def _run(
        self,
        **kwargs: Any,
    ) -> str:
        file_path = kwargs.get("file_path", self.file_path)
        if file_path is None:
            return "Error: No file path provided. Please provide a file path either in the constructor or as an argument."
​
        try:
            with open(file_path, "r",encoding='utf-8') as file:
                return file.read()
        except FileNotFoundError:
            return f"Error: File not found at path: {file_path}"
        except PermissionError:
            return f"Error: Permission denied when trying to read file: {file_path}"
        except Exception as e:
            return f"Error: Failed to read file {file_path}. {str(e)}"

file_writer_tool工具代码:

import os
from ast import literal_eval
from typing import Any, Optional, Type
​
from crewai.tools import BaseTool
from pydantic import BaseModel
​
​
class FileWriterToolInput(BaseModel):
    filename: str
    directory: Optional[str] = "./"
    overwrite: str = "False"
    content: str
​
​
class FileWriterTool(BaseTool):
    name: str = "File Writer Tool"
    description: str = "A tool to write content to a specified file. Accepts filename, content, and optionally a directory path and overwrite flag as input,overwrite flag is True or False."
    args_schema: Type[BaseModel] = FileWriterToolInput
​
    def _run(self, **kwargs: Any) -> str:
        try:
            # Create the directory if it doesn't exist
            if kwargs.get("directory") and not os.path.exists(kwargs["directory"]):
                os.makedirs(kwargs["directory"])
​
            # Construct the full path
            filepath = os.path.join(kwargs.get("directory") or "", kwargs["filename"])
​
            # Convert overwrite to boolean
            kwargs["overwrite"] = bool(literal_eval(kwargs["overwrite"]))
​
            # Check if file exists and overwrite is not allowed
            if os.path.exists(filepath) and not kwargs["overwrite"]:
                return f"File {filepath} already exists and overwrite option was not passed."
​
            # Write content to the file
            mode = "w" if kwargs["overwrite"] else "x"
            with open(filepath, mode) as file:
                content = kwargs["content"]
                file.write(content)
            return f"Content successfully written to {filepath}"
        except FileExistsError:
            return (
                f"File {filepath} already exists and overwrite option was not passed."
            )
        except KeyError as e:
            return f"An error occurred while accessing key: {str(e)}"
        except Exception as e:
            return f"An error occurred while writing to the file: {str(e)}"

现在需要构建一个团队。

构建团队的代码:

from crewai import Agent, Crew, Process, Task,LLM
from crewai.project import CrewBase, agent, crew, task
from translation_agent.tools.file_read_tool import FileReadTool
from translation_agent.tools.file_writer_tool import FileWriterTool
import os
from dotenv import load_dotenv
load_dotenv()
​
file_read_tool = FileReadTool()
file_writer_tool = FileWriterTool()
​
api_key = os.getenv('OPENAI_API_KEY')
base_url = os.getenv('OPENAI_API_BASE')
model = os.getenv('OPENAI_MODEL_NAME', 'Qwen/Qwen2.5-72B-Instruct')  # Provide a default model if not set
agent_llm = LLM(
    model=model,
    base_url=base_url,
    api_key=api_key
    )
​
# If you want to run a snippet of code before or after the crew starts, 
# you can use the @before_kickoff and @after_kickoff decorators
# https://docs.crewai.com/concepts/crews#example-crew-class-with-decorators
​
​
@CrewBase
class TranslationAgent():
    """TranslationAgent crew"""
​
    # Learn more about YAML configuration files here:
    # Agents: https://docs.crewai.com/concepts/agents#yaml-configuration-recommended
    # Tasks: https://docs.crewai.com/concepts/tasks#yaml-configuration-recommended
    agents_config = 'config/agents.yaml'
    tasks_config = 'config/tasks.yaml'
    
    # If you would like to add tools to your agents, you can learn more about it here:
    # https://docs.crewai.com/concepts/agents#agent-tools
    # @agent
    # def researcher(self) -> Agent:
    #   return Agent(
    #       config=self.agents_config['researcher'],
    #       verbose=True
    #   )
​
    # @agent
    # def reporting_analyst(self) -> Agent:
    #   return Agent(
    #       config=self.agents_config['reporting_analyst'],
    #       verbose=True
    #   )
    
    @agent
    def file_reader(self) -> Agent:     
        return Agent(
            config=self.agents_config['file_reader'],
            verbose=True,
            llm=agent_llm,
            tools=[file_read_tool],
        )
    
    @agent
    def translation_agent(self) -> Agent:       
        return Agent(
            config=self.agents_config['translation_agent'],
            verbose=True,
            llm=agent_llm,      
        )
    
    @agent
    def file_saver(self) -> Agent:      
        return Agent(
            config=self.agents_config['file_saver'],
            verbose=True,
            llm=agent_llm,
            tools=[file_writer_tool],
        )
    
    # To learn more about structured task outputs, 
    # task dependencies, and task callbacks, check out the documentation:
    # https://docs.crewai.com/concepts/tasks#overview-of-a-task
    @task
    def file_read_task(self) -> Task:
        return Task(
            config=self.tasks_config['file_read_task'],
        )
​
    @task
    def translation_task(self) -> Task:
        return Task(
            config=self.tasks_config['translation_task'],
        )
    
    @task
    def file_save_task(self) -> Task:
        return Task(
            config=self.tasks_config['file_save_task'],
        )
​
    @crew
    def crew(self) -> Crew:
        """Creates the TranslationAgent crew"""
        # To learn how to add knowledge sources to your crew, check out the documentation:
        # https://docs.crewai.com/concepts/knowledge#what-is-knowledge
​
        return Crew(
            agents=self.agents, # Automatically created by the @agent decorator
            tasks=self.tasks, # Automatically created by the @task decorator
            process=Process.sequential,
            verbose=True,
            # process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/
        )

其中我想让代理使用硅基流动的模型可以这样写:

image-20250219165445665

需要在模型名称前加上openai才行,不如会报错。

如果你还没注册的话,可以点击邀请链接进行注册:https://cloud.siliconflow.cn/i/Ia3zOSCU

这里我以具有工具调用能力的meta-llama/Llama-3.3-70B-Instruct为例。

然后可以这样使用:

import os
from dotenv import load_dotenv
load_dotenv()
​
file_read_tool = FileReadTool()
file_writer_tool = FileWriterTool()
​
api_key = os.getenv('OPENAI_API_KEY')
base_url = os.getenv('OPENAI_API_BASE')
model = os.getenv('OPENAI_MODEL_NAME', 'Qwen/Qwen2.5-72B-Instruct')  # Provide a default model if not set
agent_llm = LLM(
    model=model,
    base_url=base_url,
    api_key=api_key
    )

在创建代理时,记得使用这个大模型,并且记得使用工具:

@agent
def file_reader(self) -> Agent:     
        return Agent(
            config=self.agents_config['file_reader'],
            verbose=True,
            llm=agent_llm,
            tools=[file_read_tool],
        )

这样这个团队就构建成功了。

在main.py中这样写:

#!/usr/bin/env python
import sys
import warnings
​
from datetime import datetime
​
from translation_agent.crew import TranslationAgent
​
warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")
​
# This main file is intended to be a way for you to run your
# crew locally, so refrain from adding unnecessary logic into this file.
# Replace with inputs you want to test with, it will automatically
# interpolate any tasks and agents information
​
def run():
    """
    Run the crew.
    """
    inputs = {
        'question': '读取test.txt文件内容,将其翻译为英文,然后写入test4.txt文件',
    }
    
    try:
        TranslationAgent().crew().kickoff(inputs=inputs)
    except Exception as e:
        raise Exception(f"An error occurred while running the crew: {e}")
​
​
def train():
    """
    Train the crew for a given number of iterations.
    """
    inputs = {
        "topic": "AI LLMs"
    }
    try:
        TranslationAgent().crew().train(n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs)
​
    except Exception as e:
        raise Exception(f"An error occurred while training the crew: {e}")
​
def replay():
    """
    Replay the crew execution from a specific task.
    """
    try:
        TranslationAgent().crew().replay(task_id=sys.argv[1])
​
    except Exception as e:
        raise Exception(f"An error occurred while replaying the crew: {e}")
​
def test():
    """
    Test the crew execution and returns the results.
    """
    inputs = {
        "topic": "AI LLMs"
    }
    try:
        TranslationAgent().crew().test(n_iterations=int(sys.argv[1]), openai_model_name=sys.argv[2], inputs=inputs)
​
    except Exception as e:
        raise Exception(f"An error occurred while testing the crew: {e}")

主要关注这里:

def run():
    """
    Run the crew.
    """
    inputs = {
        'question': '读取test.txt文件内容,将其翻译为英文,然后写入test4.txt文件',
    }
    
    try:
        TranslationAgent().crew().kickoff(inputs=inputs)
    except Exception as e:
        raise Exception(f"An error occurred while running the crew: {e}")

在inputs中输入task中的question占位符的内容。

现在创建一个test.txt,输入内容为:

CrewAI:用于编排复杂 AI 代理系统的生产级框架。从简单的自动化到复杂的现实世界应用,CrewAI 提供精确控制和深度定制。通过灵活的、可投入生产的架构促进协作智能,CrewAI 使代理能够无缝协作,以可预测和一致的结果解决复杂的商业挑战。

现在输入crewai run,看看这个翻译代理的效果。

image-20250219171523468

可以发现读取文件代理做的不够好的地方是多了一些内容。

需要进行改进。

改成这样再试试:

file_reader:
  role: >
    读取文件代理
  goal: >
    根据文件路径,读取文件内容
  backstory: >
    你是一个文件读取代理,你的任务是根据文件路径,读取文件内容,只需返回文件内容即可

现在效果就很好了,如下所示:

image-20250219172033798

翻译代理很好地进行翻译了,如下所示:

image-20250219172055009

文件保存代理将翻译结果进行保存,如下所示:

image-20250219172229853

image-20250219172317889

最后

这就是使用crewai构建一个翻译代理的步骤与效果。在crewai中还有很多很有趣的工具值得探索,下期介绍代码解释器工具的使用。

Manus的开源复刻OpenManus初探 - mingupupup - 博客园

mikel阅读(327)

来源: Manus的开源复刻OpenManus初探 – mingupupup – 博客园

OpenManus介绍

Manus需要邀请码才能体验,目前大部分人都体验不到。

有几个大佬花3个小时就复现了一个简单的原型OpenManus,让我们体验体验吧!!

截至目前,该项目已经获得了25.9k颗星标,是一个非常热门的项目。

GitHub地址:https://github.com/mannaandpoem/OpenManus

image-20250311100212446

实践

话不多说,直接上手实践。

git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus
uv venv
.venv\Scripts\activate
uv pip install -r requirements.txt

上手配置:

image-20250311100600763

将config.example.toml复制一份重命名为config.toml。

或者直接运行命令:

cp config/config.example.toml config/config.toml

我先使用硅基流动的Qwen/Qwen2.5-72B-Instruct模型试试水。

image-20250311100938940

直接运行main.py即可。

运行之后如下所示:

image-20250311101034092

需要你输入一个任务。

我就以“导航至https://github.com/mannaandpoem/OpenManus获取star数量”为例。

image-20250311101318649

image-20250311101355429

确实打开了浏览器,但是报错了没有能够完成这个任务。

可能跟模型的能力有关。

我这次使用gemini-2.0-flash再试试。

配置文件如下所示:

image-20250311101838068

再试试刚刚那个任务:

image-20250311102306312

再试试获取这个仓库的前五个问题:

image-20250311102448597

image-20250311102516892

接下来测试一下生成文件的功能,输入“生成一个test.txt文件,里面写入小铭同学AI工具学习记录”。

image-20250311102747068

image-20250311102806589

简单测试了一个OpenManus用Gemini可以跑通了。

还有一个类似的项目叫OWL,目前遇到的一个问题就是一直没有看到浏览器出现。

目前还没有解决,试了Gemini也还是不行。

等等看有没有什么解决方案吧!!

以上就是今天的分享,希望对你有所帮助。

前端订单编辑明细加载速度优化经验总结

mikel阅读(433)

现在有个订单表单布局如下

头部和明细的组合布局,现在出现的问题是明细加载很慢,前端用的是layui实现的,因为form初始化和table都是异步加载,通过谷歌的开发者工具查看页面加载的过程发现明细的异步加载需要10秒以上,尽管后端加了redis缓存,前端的效率还是上不去,开始以为是数据库查询太慢了,不过通过后台执行查询SQL语句,发现并不慢1秒就完成了,那是什么原因呢?!

首先,还是从前端加载开始分析,开始总是盯着明细的异步加载慢的优化,一直改善不了,包括IIS服务器的应用程序池的最大进程数量的调整,提高并发的响应速度,调整了,有效果但是不明显。

然后,开始查看页面的加载顺序,发现了一个比明细加载还慢的异步请求,如图

这个getEntity的异步请求7秒多,比明细加载还慢,于是追查下去发现这个请求了一个跨数据库的查询24万条记录的视图,查询很慢,于是初步分析判断是不是因为这个查询慢的异步请求在明细异步请求getList之前导致,明细的请求被后台数据库处理堵塞在了慢查询的后面等待队列中,导致整个明细查询结果返回慢,于是将慢查询的getEntity放在table明细的异步加载成功后再处理,是不是会提高加载速度?!于是调整了顺序,如上图所示,结果560ms就加载完明细了!尽管getEntiy还是7秒多,但是不影响整体页面呈现效果了!

下一步就是对getEntity整个慢查询的优化了。

总结,现在前端如果响应慢很多时候总是固化的思维模式“哪慢查哪的问题”这么来解决问题,对于常规的问题的确可以找到问题原因,但是特殊情况会如这个问题一样,很多因素导致加载慢的问题出现,这样往往忽略了问题的真正根源在哪?需要跳出固化思维从全局来看待,这才是更好的解决思路!