Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

上次简单介绍了openmanus的使用,但是它究竟是怎么个原理还是一知半解的,如果想要能比较深入的理解,最直接粗暴的就是阅读源码了,然而对于很多人包括我来说阅读源码不是件简单的事情,有时候会陷入局部细节,不得要领
正好这次我发现了有个理解项目的神器,这次不加双引号是因为这个真的好
比如我们就拿openmanus举例,首先python的语法就没那么熟,以及对整体结构的理解
那么我们就可以打开 openmanus 的仓库地址
https://github.com/mannaandpoem/OpenManus
然后把 github 替换成 deepwiki,
页面变成了这样

左边是结构大纲,中间我们可以看到项目的主体介绍,包括有核心的架构图,agent的继承关系,tool的生态系统,LLM的集成

这里就展示了核心架构,通过这个方式我们如果想对一个项目有个初始的认识,就变得非常简单,因为很多项目的当前的代码都是非常复杂的,没有足够的时间精力是没办法一下子学习到项目的整体结构,因为除非我们是要真正投入到一个项目的开发贡献中,我们大概率都是从了解整体的概况,再分模块的去学习,而这个正是这个deepwiki做得非常牛的地方,相当于给每个项目都加了一个更详细具体的wiki,更牛的还有我们可以通过对话的形式进行提问题,比如我们想自己开发个工具,让openmanus集成进去进行调用

它就给出了一个非常详尽的回答,
首先

  1. 创建自定义工具类,创建一个基于 BaseTool 的新工具类
  2. 将工具添加到Manus代理,修改Manus类的available_tools定义,将您的工具添加到默认工具列表
  3. 使用MCP协议集成远程工具(可选)
  4. 工具执行流程
    一旦您的工具被集成,Manus代理会在执行过程中使用它:
  • 代理的think()方法会向LLM发送请求,包含所有可用工具的信息
  • LLM会决定使用哪个工具(包括您的Excel工具)
  • 代理的act()方法会执行工具调用
  • 您的工具的execute()方法会被调用,执行Excel函数并返回结果
  • 结果会被添加到代理的记忆中,用于后续决策
    这样子就让一个项目的理解跟上手变得非常简单,甚至比如我们想要参与这个项目的开源贡献,也能借助这个 deepwiki 来让我们能快速上手。
    如果对这个结果不满意还可以开启deep research,能让大模型通过深度思考来给出更加合理的答案,这个deepwiki是目前为止我觉得大模型对程序员最有效的一个工具了。

前阵子一个manus在目前的所谓人工智能圈子里甚至普通人视野里都很火了,宣称是什么中国的下一个deepseek时刻,首先deepseek是经过了v1,v2等一系列版本的迭代之后,并且一直是在技术上非常花功夫的,有种宝剑锋从磨砺出的感觉,而这个manus听着更像是个蹭热度的
这不没出多久有个openmanus宣称用了三小时做了个开源的复刻版,那么我们就来简单体验下,从概念上来说吧,有点类似于做了个mcp的规划和整合调用
我个人理解好像没有到改变世界的程度
首先呢我们先来安装下
可以使用conda,也可以使用uv,以conda举例,先建个环境

1
2
conda create -n open_manus python=3.12
conda activate open_manus

然后来clone下代码仓库

1
2
git clone https://github.com/mannaandpoem/OpenManus.git
cd OpenManus

接着再安装下依赖

1
pip install -r requirements.txt

这里可以借助下源替换加速,临时使用可以这样子 pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple some-package , 这里需要安装蛮久的,可能也说明了这是个依赖于很多现成库的工具

第二阶段配置

配置其实也是常规的,依赖于大模型,那么要不就是自己部署提供api,要不就是去火山引擎或者其他大模型api提供商搞个api(免费额度用完要自己付费的)

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

这一步其实跟之前使用chatbox连接火山的配置类似
因为像国外很多都还是openai的接口服务,这边就需要改用成国内可用的

1
2
3
4
5
6
[llm]
model = "deepseek-r1-250120" # The LLM model to use
base_url = "https://ark.cn-beijing.volces.com/api/v3" # API endpoint URL
api_key = "xxxxxxxxxxxxxxxxxx" # Your API key
max_tokens = 8192 # Maximum number of tokens in the response
temperature = 0.0

模型可以用deepseek-r1,不过要注意是什么时间版本的,否则也会访问不到
然后我们就可以运行

1
python main.py

来运行openmanus,我们简单问个问题

1
明天杭州的天气怎么样,给出个穿衣指南

看下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
python main.py
INFO [browser_use] BrowserUse logging setup complete with level info
INFO [root] Anonymized telemetry enabled. See https://docs.browser-use.com/development/telemetry for more information.
Enter your prompt: 帮我看下明天杭州的天气,并且生成穿衣指南
2025-04-20 20:40:14.721 | WARNING | __main__:main:16 - Processing your request...
2025-04-20 20:40:14.722 | INFO | app.agent.base:run:140 - Executing step 1/20
2025-04-20 20:40:33.488 | INFO | app.llm:update_token_count:250 - Token usage: Input=2165, Completion=658, Cumulative Input=2165, Cumulative Completion=658, Total=2823, Cumulative Total=2823
2025-04-20 20:40:33.489 | INFO | app.agent.toolcall:think:81 - ✨ Manus's thoughts:


2025-04-20 20:40:33.489 | INFO | app.agent.toolcall:think:82 - 🛠️ Manus selected 1 tools to use
2025-04-20 20:40:33.489 | INFO | app.agent.toolcall:think:86 - 🧰 Tools being prepared: ['browser_use']
2025-04-20 20:40:33.489 | INFO | app.agent.toolcall:think:89 - 🔧 Tool arguments: {
"action": "web_search",
"query": "杭州明天天气预报"
}
2025-04-20 20:40:33.490 | INFO | app.agent.toolcall:execute_tool:180 - 🔧 Activating tool: 'browser_use'...
ERROR [browser] Failed to initialize Playwright browser: BrowserType.launch: Executable doesn't exist at /Users/username/Library/Caches/ms-playwright/chromium-1161/chrome-mac/Chromium.app/Contents/MacOS/Chromium
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ playwright install ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════╝
2025-04-20 20:40:34.896 | INFO | app.agent.toolcall:act:150 - 🎯 Tool 'browser_use' completed its mission! Result: Observed output of cmd `browser_use` executed:
Error: Browser action 'web_search' failed: BrowserType.launch: Executable doesn't exist at /Users/username/Library/Caches/ms-playwright/chromium-1161/chrome-mac/Chromium.app/Contents/MacOS/Chromium
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ playwright install ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════╝
2025-04-20 20:40:34.896 | INFO | app.agent.base:run:140 - Executing step 2/20
ERROR [browser] Failed to initialize Playwright browser: BrowserType.launch: Executable doesn't exist at /Users/username/Library/Caches/ms-playwright/chromium-1161/chrome-mac/Chromium.app/Contents/MacOS/Chromium
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ playwright install ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════╝
WARNING [browser] Page load failed, continuing...
ERROR [browser] Failed to initialize Playwright browser: BrowserType.launch: Executable doesn't exist at /Users/username/Library/Caches/ms-playwright/chromium-1161/chrome-mac/Chromium.app/Contents/MacOS/Chromium
╔════════════════════════════════════════════════════════════╗
║ Looks like Playwright was just installed or updated. ║
║ Please run the following command to download new browsers: ║
║ ║
║ playwright install ║
║ ║
║ <3 Playwright Team ║
╚════════════════════════════════════════════════════════════╝

根据这个返回可以看到它做了些啥,主要是规划步骤和选择调用的工具,相对来说没有特别的,playwright我就不去搞了,浏览器那套

arthas是阿里开源的一个非常好用的java诊断工具,提供了很多很好用的命令,这里讲一个最近使用到的
就是将arthas挂载上我们的springboot应用,然后调用其中的方法,这样能够在如果没加日志已经看不到函数返回时更方便的排查问题
首先举个例子,我们有个Controller
它的一个query方法是这样的

1
2
3
4
5
@RequestMapping(value = "/query", method = RequestMethod.GET)
@ResponseBody
public String query() {
return demoService.queryName("1");
}

而在这个demoService中它的实现是这样

1
2
3
4
5
6
7
public String queryName(String no) {
if ("1".equals(no)) {
return "no1";
} else {
return "no2";
}
}

假如现在Controller这的这个方法有点问题,那么我想确认下是不是demoService这个方法的实现有问题,或者说确定下它的返回值是否符合预期
那么我们就可以在应用启动后,运行arthas,找到这个应用的进程,进行挂载
然后执行

1
vmtool --action getInstances --className com.nicksxs.spbdemo.service.DemoServiceImpl --express 'instances[0].queryName("1")'

先介绍下这个vmtool命令
主要来说 vmtool 可以利用JVMTI接口,实现查询内存对象,强制 GC 等功能。
例如官方示例里的,我想把内存里的string对象捞一些出来看看存的是啥

1
vmtool --action getInstances --className java.lang.String --limit 10

就可以这样,首先这个action就是指定要做的操作,支持的action 还包括

1
2
forceGc
interruptThread

等,那么对于 getInstances 就是从内存里捞出这个类的对象,然后是后面一部分
--express 就是执行表达式,这里的表达式,
instances[0].queryName("1") 其中 instances 就是前面从内存中获取的对象数组,因为这些是对象的非静态方法,那就需要从其中取一个来执行我们的方法
另外假如我们的场景里需要对比如返回结果做个json序列化
我们可以这样

1
vmtool --action getInstances --className com.nicksxs.spbdemo.service.DemoServiceImpl --express '@com.alibaba.fastjson.JSON@toJSONString(instances[0].queryName("1"))'

这里为什么类开头跟方法开头要用 @, 是因为对于类和静态方法的调用规则是这样,还有如果代码比较多,有可能默认的类加载器中没有加载这个JSON类,那么就需要在参数中加上指定的classloader,
可以用sc命令来查找我们的目标类的类加载器,一般来说如果目标类是我们核心业务的,大概率也会有JSON这个类

1
sc -d com.nicksxs.spbdemo.service.DemoServiceImpl

然后在上面命令中加上sc结果中的 classLoaderHash 的值,

1
vmtool --action getInstances -c 18b4aac2 --className com.nicksxs.spbdemo.service.DemoServiceImpl --express '@com.alibaba.fastjson.JSON@toJSONString(instances[0].queryName("1"))'

这样就能正常执行了

之前发现Termux在安卓手机中是个比较厉害的神器,就相当于一个随身携带的小型服务器,伴随着现在手机性能的逐渐强大,有些手机可能已经比很多个人用的云服务器还要强大很多,只是有着散热和电量的限制,但是充当一下临时的使用还是很不错的
记得之前提过,在用rustdesk的时候,如果被连端是Mac,并且被连端进入锁屏了,可能需要ssh连上去执行个唤醒命令,正好最近有需求要偶尔从手机连上家里的Mac,从而引发的一个问题是比如我经常用Termux来执行ssh命令,那么理论上我直接从shell的history就可以找到执行过的ssh命令,也就不用经常记对应的ip啥的,但是试用了下发现不行,一开始搜了下以为是没有设置历史存储的大小,可能默认是0,那么需要通过配置来设置

1
2
HISTSIZE=1000
HISTFILESIZE=2000

比如设置1000个命令大小,只是在Termux里不是这个原因,而是在于shell去保存历史的时机,安卓手机在退出应用或者切换应用程序都没法记录这个执行历史,而是需要在Termux中手动地用exit命令退出,才会记录下history历史,
当然也有人在Termux的issue上提到了,能不能捕捉安卓的事件来执行这个exit,否则这个exit有点难记得,相对来说也不方便了很多,容易遗忘,只是这个issue也被关掉了,看看后面会不会优化吧

因为上次那个问题,所以打算把图库迁移到靠谱一些的cloudflare上,这里用到了rclone这个很强大的工具

在开始迁移前,先做一下准备

  1. 分别在腾讯云和 Cloudflare 平台申请 Access Key 和 Secret Key
  2. 安装 rclone 工具(可从 rclone 官网 下载)

配置远程存储

配置步骤

  1. 打开终端,输入 rclone config 开始远程存储配置
  2. n 创建新的远程存储

配置腾讯云 COS

  1. 输入远程存储名称,例如 cos
  2. 选择存储类型,选择 Amazon S3 兼容模式(选项编号可能因 rclone 版本而异)
  3. 选择服务提供商为腾讯云 COS
  4. 输入您的 access_key_idsecret_access_key(从腾讯云控制台获取)
  5. 选择相应的端点 API(根据您的存储桶所在地区选择北京或上海等)
  6. 配置访问权限:可以选择仅资源所有者拥有完全访问权限,或根据您的安全需求选择其他选项
  7. 选择存储类型(通常选择标准存储)
  8. 确认配置

配置 Cloudflare R2

  1. 再次输入 n 创建新的远程存储
  2. 输入远程存储名称,例如 r2
  3. 同样选择 Amazon S3 兼容模式
  4. 选择服务提供商为 Cloudflare R2
  5. 输入您的 access_key_idsecret_access_key(从 Cloudflare 控制台获取)
  6. 输入 R2 的端点 URL(通常格式为 https://<account_id>.r2.cloudflarestorage.com
  7. 配置访问权限
  8. 选择存储类型
  9. 确认配置

执行迁移操作

完成配置后,使用以下命令执行迁移:

1
rclone copy cos:mystore-xxxxxxx/img/ r2:img/ --progress

上述命令将把腾讯云 COS 中 mystore-xxxxxxx 存储桶下的 img 目录中的所有文件复制到 Cloudflare R2 的 img 存储桶中。添加的 --progress 参数可以显示迁移进度。

高级选项

为了获得更好的迁移体验,您可以考虑以下高级选项:

  • 使用 --transfers=N 参数设置并行传输数量,加快迁移速度
  • 使用 --checkers=N 参数设置并行检查数量
  • 使用 --dry-run 参数测试迁移过程而不实际复制文件
  • 使用 --log-file=FILE 参数将迁移日志保存到文件中

例如:

1
rclone copy cos:mystore-xxxxxxx/img/ r2:img/ --progress --transfers=4 --checkers=8 --stats=10s

验证迁移结果

迁移完成后,可以使用以下命令验证两边的文件是否一致:

1
rclone check cos:mystore-xxxxxxx/img/ r2:img/ --one-way

结语

通过 rclone 这一强大工具,可以轻松实现云存储服务之间的数据迁移。Cloudflare R2 提供了稳定可靠的对象存储服务,同时其无出站流量费用的特性也使其成为图库存储的理想选择。

0%