Author SHA1 Message Date
martin legrand 89f5736f6b feat : self run script 2025-05-05 15:55:35 +02:00
36 changed files with 3276 additions and 2436 deletions
-19
View File
@@ -1,19 +0,0 @@
# Python cache files
__pycache__/
*.py[cod]
# Virtual environments
venv/
.venv/
# Environment variables (secrets)
.env
# Git metadata
.git/
# macOS Finder files
.DS_Store
# Log files
*.log
-1
View File
@@ -1,4 +1,3 @@
SEARXNG_BASE_URL="http://127.0.0.1:8080" SEARXNG_BASE_URL="http://127.0.0.1:8080"
OPENAI_API_KEY='xxxxx' OPENAI_API_KEY='xxxxx'
DEEPSEEK_API_KEY='xxxxx' DEEPSEEK_API_KEY='xxxxx'
OPENROUTER_API_KEY='xxxxx'
-1
View File
@@ -9,7 +9,6 @@ test_agent.py
config.ini config.ini
.voices/ .voices/
experimental/ experimental/
chrome_bundle/
.logs/ .logs/
.screenshots/*.png .screenshots/*.png
.screenshots/*.jpg .screenshots/*.jpg
+8 -15
View File
@@ -32,15 +32,11 @@ https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates. Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
> 🛠⚠️ **Active Work in Progress** Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production. > 🛠️ **Work in Progress** Looking for contributors!
> 🙏 This project started as a side-project with zero roadmap and zero funding. It's grown way beyond what I expected by ending in GitHub Trending. Contributions, feedback, and patience are deeply appreciated.
## Installation ## Installation
Make sure you have chrome driver, docker and python3.10 installed. Make sure you have chrome driver, docker and python3.10 (or newer) installed.
We highly advice you use exactly python3.10 for the setup. Dependencies error might happen otherwise.
For issues related to chrome driver, see the **Chromedriver** section. For issues related to chrome driver, see the **Chromedriver** section.
@@ -124,9 +120,8 @@ Install requirements: `pip3 install -r requirements.txt`
## Setup for running LLM locally on your machine ## Setup for running LLM locally on your machine
**Hardware Requirements:** **We recommend using at the very least Deepseek 14B, smaller models will struggle with tasks especially for web browsing.**
To run LLMs locally, you'll need sufficient hardware. At a minimum, a GPU capable of running Qwen/Deepseek 14B is required. See the FAQ for detailed model/performance recommendations.
**Setup your local provider** **Setup your local provider**
@@ -163,8 +158,6 @@ headless_browser = True # Whenever to use headless browser, recommanded only if
stealth_mode = True # Use undetected selenium to reduce browser detection stealth_mode = True # Use undetected selenium to reduce browser detection
``` ```
Warning: Do *NOT* set provider_name to `openai` if using LM-studio for running LLMs. Set it to `lm-studio`.
Note: Some provider (eg: lm-studio) require you to have `http://` in front of the IP. For example `http://127.0.0.1:1234` Note: Some provider (eg: lm-studio) require you to have `http://` in front of the IP. For example `http://127.0.0.1:1234`
**List of local providers** **List of local providers**
@@ -207,7 +200,7 @@ Example: export `TOGETHER_API_KEY="xxxxx"`
| Provider | Local? | Description | | Provider | Local? | Description |
|-----------|--------|-----------------------------------------------------------| |-----------|--------|-----------------------------------------------------------|
| openai | Depends | Use ChatGPT API | | openai | Depends | Use ChatGPT API |
| deepseek | No | Deepseek API (non-private) | | deepseek-api | No | Deepseek API (non-private) |
| huggingface| No | Hugging-Face API (non-private) | | huggingface| No | Hugging-Face API (non-private) |
| togetherAI | No | Use together AI API (non-private) | | togetherAI | No | Use together AI API (non-private) |
| google | No | Use google gemini API (non-private) | | google | No | Use google gemini API (non-private) |
@@ -321,7 +314,7 @@ Clone the repository and enter the `server/`folder.
```sh ```sh
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
cd agenticSeek/llm_server/ cd agenticSeek/server/
``` ```
Install server specific requirements: Install server specific requirements:
@@ -554,8 +547,8 @@ Were looking for developers to improve AgenticSeek! Check out open issues or
## Maintainers: ## Maintainers:
> [Fosowl](https://github.com/Fosowl) | Paris Time > [Fosowl](https://github.com/Fosowl) | Paris Time | (Sometime busy)
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time > [https://github.com/antoineVIVIES](antoineVIVIES) | Taipei Time | (Often busy)
> [steveh8758](https://github.com/steveh8758) | Taipei Time > [steveh8758](https://github.com/steveh8758) | Taipei Time | (Always busy)
+3 -7
View File
@@ -34,8 +34,6 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
确保已安装了 Chrome driverDocker 和 Python 3.10(或更新)。 确保已安装了 Chrome driverDocker 和 Python 3.10(或更新)。
我们强烈建议您使用 Python 3.10 进行设置,否则可能会发生依赖错误。
有关于 Chrome driver 的问题,请参见 **Chromedriver** 部分。 有关于 Chrome driver 的问题,请参见 **Chromedriver** 部分。
### 1️⃣ **复制储存库与设置环境变数** ### 1️⃣ **复制储存库与设置环境变数**
@@ -151,8 +149,6 @@ headless_browser = True # 是否使用无头浏览器,只有在使用网页界
stealth_mode = True # 使用无法检测的 selenium 来减少浏览器检测 stealth_mode = True # 使用无法检测的 selenium 来减少浏览器检测
``` ```
警告:使用 LM-studio 运行 LLM 时,请*不要*将 provider_name 设置为 `openai`。请将其设置为 `lm-studio`
注意:某些提供者(如 lm-studio)需要在 IP 前面加上 `http://`。例如 `http://127.0.0.1:1234` 注意:某些提供者(如 lm-studio)需要在 IP 前面加上 `http://`。例如 `http://127.0.0.1:1234`
@@ -564,8 +560,8 @@ DeepSeek R1 天生会说中文
## 维护者: ## 维护者:
> [Fosowl](https://github.com/Fosowl) | 巴黎时间 > [Fosowl](https://github.com/Fosowl) | 巴黎时间 | (有时很忙)
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time > [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北时间 | (经常很忙)
> [steveh8758](https://github.com/steveh8758) | 台北时间 > [steveh8758](https://github.com/steveh8758) | 台北时间 | (总是很忙)
+42 -55
View File
@@ -8,23 +8,23 @@
[English](./README.md) | 繁體中文 | [日本語](./README_JP.md) [English](./README.md) | 繁體中文 | [日本語](./README_JP.md)
*一个 **100% 本地替代 Manus AI** 的方案,款支持音的 AI 助理能够自主瀏覽網頁、编代码和規劃任務,同时所有用戶資料保留在您的裝置上。專門為本地推理模型量身打造,完全在您自己的硬體上執行,确保完全的隐私保护和零端依。* *一个 **100% 本地替代 Manus AI** 的方案,款支持音的 AI 助理能够自主浏览网页、编代码和规划任务,同时所有数据保留在您的设备上。专为本地推理模型量身打造,完全在您自己的硬件上运行,确保完全的隐私保护和零端依。*
[![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/8hGDaME3TC) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460) [![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/8hGDaME3TC) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460)
### 为什么選擇 AgenticSeek ### 为什么选择 AgenticSeek
* 🔒 完全本地化隐私保护 - 所有功能都在您的设备上行 — 无云端服务,无数据共享。您的文件、对话和搜索始终保持私密。 * 🔒 完全本地化隐私保护 - 所有功能都在您的设备上行 — 无云端服务,无数据共享。您的文件、对话和搜索始终保持私密。
* 🌐 智能網頁瀏覽 - AgenticSeek 能够自主瀏覽網頁 — 搜索、读、提取信息、填寫網页表 — 全程无需人工操作。 * 🌐 智能网页浏览 - AgenticSeek 能够自主浏览互联网 — 搜索、读、提取信息、填写网页表 — 全程无需人工操作。
* 💻 自主编码助手 - 需要代码?它可以编、调试并行 Python、C、Go、Java 等多种语言的程序 — 全程无需监督。 * 💻 自主编码助手 - 需要代码?它可以编、调试并行 Python、C、Go、Java 等多种语言的程序 — 全程无需监督。
* 🧠 智能代理选择 - 您提问,它自动选择最适合该任务的代理。就像拥有一个随时待命的家团队。 * 🧠 智能代理选择 - 您提问,它自动选择最适合该任务的代理。就像拥有一个随时待命的家团队。
* 📋 规划执行复杂任务 - 从旅行规划到复杂项目 — 它能大型任务分解为步骤,并利用多个 AI 代理完成工作。 * 📋 规划执行复杂任务 - 从旅行规划到复杂项目 — 它能大型任务分解为步骤,并利用多个 AI 代理完成工作。
* 🎙️ 音功能 - 清晰、快速、未来感十足的語音與語音轉文本功能,您能像科幻电影中一样您的个人 AI 助手对话。 * 🎙️ 音功能 - 清晰、快速、未来感十足的语音与语音转文本功能,您能像科幻电影中一样您的个人 AI 助手对话。
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469 https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
@@ -36,8 +36,6 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
確保已安裝了 Chrome driverDocker 和 Python 3.10(或更新)。 確保已安裝了 Chrome driverDocker 和 Python 3.10(或更新)。
我们强烈建议您使用 Python 3.10 進行設定,否则可能會发生依赖错误。
有關於 Chrome driver 的問題,請參見 **Chromedriver** 部分。 有關於 Chrome driver 的問題,請參見 **Chromedriver** 部分。
### 1️⃣ **複製儲存庫與設置環境變數** ### 1️⃣ **複製儲存庫與設置環境變數**
@@ -64,7 +62,7 @@ source agentic_seek_env/bin/activate
./install.sh ./install.sh
``` ```
** 若要將文字轉成語音(TTS)功能支持中文,你需要安装 jieba(中文分詞庫)和 cn2an(中文數字轉換庫):** ** 若要让文本转语音(TTS)功能支持中文,你需要安装 jieba(中文分词库)和 cn2an(中文数字转换库):**
``` ```
pip3 install jieba cn2an pip3 install jieba cn2an
@@ -73,7 +71,7 @@ pip3 install jieba cn2an
**手動安裝:** **手動安裝:**
**注意:對於不同作業系統,請確保已經安装的 ChromeDriver 您已安装的 Chrome 版本一致。可以執行 `google-chrome --version`。如果您的 Chrome 版本 > 135請參考已知问题** **注意:对于任何操作系统,请确保您安装的 ChromeDriver 您已安装的 Chrome 版本匹配。运行 `google-chrome --version`。如果您的 Chrome 版本 > 135请参阅已知问题**
- *Linux*: - *Linux*:
@@ -81,7 +79,7 @@ pip3 install jieba cn2an
安装依赖项:`sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1` 安装依赖项:`sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
安装您的 Chrome 瀏覽器版本匹配的 ChromeDriver 安装您的 Chrome 浏览器版本匹配的 ChromeDriver
`sudo apt install -y chromium-chromedriver` `sudo apt install -y chromium-chromedriver`
安装 requirements`pip3 install -r requirements.txt` 安装 requirements`pip3 install -r requirements.txt`
@@ -104,11 +102,11 @@ pip3 install jieba cn2an
安装 pyreadline3`pip install pyreadline3` 安装 pyreadline3`pip install pyreadline3`
手动安装 portaudio(例如,通过 vcpkg 或預編譯的二制文件),然後運行:`pip install pyaudio` 手动安装 portaudio(例如,通过 vcpkg 或预编译的二制文件),然后运行:`pip install pyaudio`
从以下址手动下载并安装 chromedriverhttps://sites.google.com/chromium.org/driver/getting-started 从以下址手动下载并安装 chromedriverhttps://sites.google.com/chromium.org/driver/getting-started
chromedriver 放置在包含在您的 PATH 中的目录中。 chromedriver 放置在包含在您的 PATH 中的目录中。
安装 requirements`pip3 install -r requirements.txt` 安装 requirements`pip3 install -r requirements.txt`
@@ -116,45 +114,34 @@ pip3 install jieba cn2an
**建議至少使用 Deepseek 14B 以上參數的模型,較小的模型難以使用助理功能並且很快就會忘記上下文之間的關係。** **建議至少使用 Deepseek 14B 以上參數的模型,較小的模型難以使用助理功能並且很快就會忘記上下文之間的關係。**
**本地行助手** **本地行助手**
啟動你的本地提供者,例如使用 ollama: 启动你的本地提供者,例如使用 ollama:
```sh ```sh
ollama serve ollama serve
``` ```
请参下方支持的本地提供者列表。 请参下方支持的本地提供者列表。
修改 config.ini 文件以設定 provider_name 为支持的提供者,并provider_model 設定为该提供者支持的 LLM。我们推荐使用具有推理能力的模型,如 *Qwen**Deepseek* 修改 `config.ini` 文件,将 `provider_name` 设置为支持的提供者,并`provider_model` 设置为 `deepseek-r1:14b`
请参见 README 末尾的 **FAQ** 部分了解所需硬件 注意:`deepseek-r1:14b` 只是一个示例,如果你的硬件允许,可以使用更大的模型
```sh ```sh
[MAIN] [MAIN]
is_local = True # 无论是在本地運行还是使用远程提供者。 is_local = True
provider_name = ollama # 或 lm-studio, openai 等.. provider_name = ollama # 或 lm-studio, openai 等
provider_model = deepseek-r1:14b # 选择适合您硬件的模型 provider_model = deepseek-r1:14b
provider_server_address = 127.0.0.1:11434 provider_server_address = 127.0.0.1:11434
agent_name = Jarvis # 您的 AI 助手的名称
recover_last_session = True # 是否恢复之前的會话
save_session = True # 是否记住当前會话
speak = True # 文本轉語音
listen = False # 語音轉文本,僅适用于命令行界面
work_dir = /Users/mlg/Documents/workspace # AgenticSeek 的工作空间。
jarvis_personality = False # 是否使用更"贾维斯"风格的性格,不推荐在小型模型上使用
languages = en zh # 语言列表,文本轉語音將默认使用列表中的第一种语言
[BROWSER]
headless_browser = True # 是否使用无头瀏覽器,只有在使用網页界面时才推荐使用。
stealth_mode = True # 使用无法檢測的 selenium 来减少瀏覽器檢測
``` ```
**本地提供者列表** **本地提供者列表**
| 提供者 | 本地? | 描述 | | 提供者 | 本地? | 描述 |
|-------------|--------|-------------------------------------------------------| |-------------|--------|-------------------------------------------------------|
| ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地行 LLM | | ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地行 LLM |
| lm-studio | 是 | 使用 LM Studio 本地行 LLM `provider_name` 設定`lm-studio`| | lm-studio | 是 | 使用 LM Studio 本地行 LLM `provider_name` 设置`lm-studio`|
| openai | 否 | 使用兼容的 API | | openai | 否 | 使用兼容的 API |
下一步: [Start services and run AgenticSeek](#Start-services-and-Run) 下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
@@ -184,14 +171,14 @@ provider_server_address = 127.0.0.1:5000
--- ---
## Start services and Run ## Start services and Run
(啟動服务并行) (启动服务并行)
如果需要,请激活你的 Python 环境。 如果需要,请激活你的 Python 环境。
```sh ```sh
source agentic_seek_env/bin/activate source agentic_seek_env/bin/activate
``` ```
啟動所需的服务。这將啟動 `docker-compose.yml` 中的所有服务,包括: 启动所需的服务。这将启动 `docker-compose.yml` 中的所有服务,包括:
- searxng - searxng
- redis(由 redis 提供支持) - redis(由 redis 提供支持)
- 前端 - 前端
@@ -201,25 +188,25 @@ sudo ./start_services.sh # MacOS
start ./start_services.cmd # Windows start ./start_services.cmd # Windows
``` ```
**選項 1:** 使用 CLI 界面行。 **选项 1:** 使用 CLI 界面行。
```sh ```sh
python3 cli.py python3 cli.py
``` ```
**選項 2:** 使用 Web 界面行。 **选项 2:** 使用 Web 界面行。
注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。 注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。
啟動後端服务。 启动后端服务。
```sh ```sh
python3 api.py python3 api.py
``` ```
访问 `http://localhost:3000/`,你应该看到 Web 界面。 访问 `http://localhost:3000/`,你应该看到 Web 界面。
请注意,目前 Web 界面不支持消息流式傳輸 请注意,目前 Web 界面不支持消息流式传输
*如果你不知道如何開始,請參閱 **Usage** 部分* *如果你不知道如何開始,請參閱 **Usage** 部分*
@@ -228,9 +215,9 @@ python3 api.py
## Usage (使用方法) ## Usage (使用方法)
为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中設定语言選項 为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中设置语言选项
languages = en zh languages = en zh
更多信息请参 Config 部分 更多信息请参 Config 部分
確定所有的核心檔案都啟用了,也就是執行過這條命令 `./start_services.sh` 然後你就可以使用 `python3 cli.py` 來啟動 AgenticSeek 了! 確定所有的核心檔案都啟用了,也就是執行過這條命令 `./start_services.sh` 然後你就可以使用 `python3 cli.py` 來啟動 AgenticSeek 了!
@@ -287,11 +274,11 @@ python3 cli.py
所以我們希望你在使用時,能明確地表明你希望他要怎麼做,下面給你一個範例! 所以我們希望你在使用時,能明確地表明你希望他要怎麼做,下面給你一個範例!
你該說: 你該說:
- 進行網路搜索,找出哪些国家最适合自旅行 - 进行网络搜索,找出哪些国家最适合自旅行
而不是說: 而不是說:
- 你知道哪些国家适合自旅行? - 你知道哪些国家适合自旅行?
--- ---
@@ -357,7 +344,7 @@ provider_server_address = x.x.x.x:3333
## 語音轉文字 ## 語音轉文字
请注意,目前語音轉文字功能僅支援英语。 请注意,目前语音转文字功能仅支持英语。
預設狀況下,語音轉文字功能是停用的。若要啟用它,請在 `config.ini` 檔案中,將 `listen` 選項設為 `True` 預設狀況下,語音轉文字功能是停用的。若要啟用它,請在 `config.ini` 檔案中,將 `listen` 選項設為 `True`
@@ -538,13 +525,13 @@ https://googlechromelabs.github.io/chrome-for-testing/
**Q: 是否支持中文以外的语言?** **Q: 是否支持中文以外的语言?**
DeepSeek R1 天生说中文 DeepSeek R1 天生说中文
但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统: 但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统:
如果不設定中文?果可能是:你讓它寫代码,结果跳出来个"生代理"(虽然我们根本没有这个代理... 但系统一脸懵圈!) 如果不设置中文?果可能是:你让它写代码,结果跳出来个"生代理"(虽然我们根本没有这个代理... 但系统一脸懵圈!)
实际上下载一个小型翻译模型来协助任务分配 实际上下载一个小型翻译模型来协助任务分配
## 貢獻 ## 貢獻
@@ -556,8 +543,8 @@ DeepSeek R1 天生會说中文
## 维护者: ## 维护者:
> [Fosowl](https://github.com/Fosowl) | 巴黎時間 | (有时很忙) > [Fosowl](https://github.com/Fosowl) | 巴黎时间 | (有时很忙)
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北時間 | 常很忙) > [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北时间 | 常很忙)
> [steveh8758](https://github.com/steveh8758) | 台北時間 | 是很忙) > [steveh8758](https://github.com/steveh8758) | 台北时间 | 是很忙)
+3 -4
View File
@@ -23,6 +23,7 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
## Fonctionnalités: ## Fonctionnalités:
- **100% Local**: Fonctionne en local sur votre PC. Vos données restent les vôtres. - **100% Local**: Fonctionne en local sur votre PC. Vos données restent les vôtres.
@@ -43,9 +44,7 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
## **Installation** ## **Installation**
Assurez-vous davoir installé le pilote Chrome, Docker et Python 3.10. Assurez-vous davoir installé le pilote Chrome, Docker et Python 3.10 (ou une version plus récente).
Nous vous conseillons fortement d'utiliser exactement Python 3.10 pour l'installation. Des erreurs de dépendances pourraient survenir autrement.
Pour les problèmes liés au pilote Chrome, consultez la section Chromedriver. Pour les problèmes liés au pilote Chrome, consultez la section Chromedriver.
@@ -495,4 +494,4 @@ Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la se
## Mainteneurs: ## Mainteneurs:
> [Fosowl](https://github.com/Fosowl) > [Fosowl](https://github.com/Fosowl)
> [steveh8758](https://github.com/steveh8758) > [steveh8758](https://github.com/steveh8758)
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time > [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES)
+219 -301
View File
@@ -1,48 +1,61 @@
# AgenticSeek: プライベートなローカルManus代替
<p align="center"> <p align="center">
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek ロゴ"> <img align="center" src="./media/whale_readme.jpg">
<p> <p>
--------------------------------------------------------------------------------
[English](./README.md) | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | 日本語 [English](./README.md) | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | 日本語
*Manus AIの**100%ローカルな代替**となるこの音声対応AIアシスタントは、自律的にウェブを閲覧し、コードを書き、タスクを計画しながら、すべてのデータをあなたのデバイスに保持します。ローカル推論モデルに合わせて調整されており、完全にあなたのハードウェア上で動作するため、完全なプライバシーとクラウドへの依存ゼロを保証します。* # AgenticSeek: Deepseek R1エージェントによって動作するManusのようなAI。
[![AgenticSeekを訪問](https://img.shields.io/static/v1?label=ウェブサイト&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![ライセンス](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-参加する-7289DA?logo=discord&logoColor=white)](https://discord.gg/8hGDaME3TC) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=更新%20%40Fosowl)](https://x.com/Martin993886460) [![GitHubスター](https://img.shields.io/github/stars/Fosowl/agenticSeek?style=social)](https://github.com/Fosowl/agenticSeek/stargazers)
### なぜAgenticSeekなのか? **Manus AIの完全なローカル代替品**、音声対応のAIアシスタントで、コードを書き、ファイルシステムを探索し、ウェブを閲覧し、ミスを修正し、データをクラウドに送信することなくすべてを行います。DeepSeek R1のような推論モデルを使用して構築されており、この自律エージェントは完全にハードウェア上で動作し、データのプライバシーを保護します。
* 🔒 完全ローカル&プライベート - すべてがあなたのマシン上で実行されます — クラウドなし、データ共有なし。あなたのファイル、会話、検索はプライベートに保たれます。 [![Visit AgenticSeek](https://img.shields.io/static/v1?label=Website&message=AgenticSeek&color=blue&style=flat-square)](https://fosowl.github.io/agenticSeek.html) ![License](https://img.shields.io/badge/license-GPL--3.0-green) [![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?logo=discord&logoColor=white)](https://discord.gg/8hGDaME3TC) [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/fosowl.svg?style=social&label=Update%20%40Fosowl)](https://x.com/Martin993886460)
* 🌐 スマートなウェブブラウジング - AgenticSeekは自分でインターネットを閲覧できます — 検索、読み取り、情報抽出、ウェブフォーム入力 — すべてハンズフリーで。 > 🛠️ **進行中の作業** – 貢献者を探しています!
* 💻 自律型コーディングアシスタント - コードが必要ですか?Python、C、Go、Javaなどでプログラムを書き、デバッグし、実行できます — すべて監視なしで。
* 🧠 スマートエージェント選択 - あなたが尋ねると、タスクに最適なエージェントを自動的に見つけ出します。まるで専門家チームが助けてくれるようです。
* 📋 複雑なタスクの計画と実行 - 旅行計画から複雑なプロジェクトまで — 大きなタスクをステップに分割し、複数のAIエージェントを使って物事を成し遂げることができます。
* 🎙️ 音声対応 - クリーンで高速、未来的な音声と音声認識により、まるでSF映画のパーソナルAIのように話しかけることができます。 https://github.com/user-attachments/assets/fe9e8006-0462-4793-8b31-25bd42c6d1eb
### **デモ**
> *agenticSeekプロジェクトを検索し、必要なスキルを学び、その後CV_candidates.zipを開いて、プロジェクトに最も適した候補者を教えてください。*
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
免責事項:このデモは、表示されるすべてのファイル(例:CV_candidates.zip)を含め、完全に架空のものです。私たちは企業ではなく、候補者ではなくオープンソースの貢献者を求めています。 *そしてもっと多くのことができます!*
> 🛠️ **作業中** – 貢献者を募集中です! > *大阪と東京のAIスタートアップを深く調査し、少なくとも5つ見つけて、research_japan.txtファイルに保存してください*
## インストール > *C言語でテトリスゲームを作れますか?*
Chrome Driver、Docker、Python 3.10がインストールされていることを確認してください。 > *新しいプロジェクトファイルインデックスをmark2として設定したいです。*
セットアップにはPython 3.10を正確に使用することを強くお勧めします。そうでない場合、依存関係のエラーが発生する可能性があります。
Chromeドライバーに関する問題については、**Chromedriver**セクションを参照してください。 ## 特徴:
### 1️⃣ **リポジトリのクローンとセットアップ** - **100%ローカル**: クラウドなし、ハードウェア上で動作。データはあなたのものです。
- **ファイルシステムの操作**: bashを使用してファイルを簡単にナビゲートおよび操作します。
- **自律的なコーディング**: Python、C、Golangなどのコードを書き、デバッグし、実行できます。
- **エージェントルーティング**: タスクに最適なエージェントを自動的に選択します。
- **計画**: 複雑なタスクの場合、複数のエージェントを起動して計画および実行します。
- **自律的なウェブブラウジング**: 自律的なウェブナビゲーション。
- **メモリ**: 効率的なメモリとセッション管理。
---
## **インストール**
chrome driver、docker、およびpython3.10(またはそれ以降)がインストールされていることを確認してください。
chrome driverに関連する問題については、**Chromedriver**セクションを参照してください。
### 1️⃣ **リポジトリをクローンしてセットアップ**
```sh ```sh
git clone https://github.com/Fosowl/agenticSeek.git git clone https://github.com/Fosowl/agenticSeek.git
@@ -50,7 +63,7 @@ cd agenticSeek
mv .env.example .env mv .env.example .env
``` ```
### 2 **仮想環境作成** ### 2 **仮想環境作成**
```sh ```sh
python3 -m venv agentic_seek_env python3 -m venv agentic_seek_env
@@ -58,176 +71,99 @@ source agentic_seek_env/bin/activate
# Windowsの場合: agentic_seek_env\Scripts\activate # Windowsの場合: agentic_seek_env\Scripts\activate
``` ```
### 3️⃣ **パッケージインストール** ### 3️⃣ **パッケージインストール**
Python、Dockerとdocker compose、Google Chromeがインストールされていることを確認してください。 **自動インストール:**
Python 3.10.0を推奨します。
**自動インストール(推奨):**
Linux/Macosの場合:
```sh ```sh
./install.sh ./install.sh
``` ```
** テキスト読み上げ(TTS)機能で日本語をサポートするには、fugashi(日本語分かち書きライブラリ)をインストールする必要があります:** ** テキスト読み上げ(TTS)機能で日本語をサポートするには、fugashi(日本語分かち書きライブラリ)をインストールする必要があります:**
** 注意: 日本語のテキスト読み上げ(TTS)機能には多くの依存関係が必要で、問題が発生する可能性があります。mecabrcに関する問題が発生することがあります。現在のところ、この問題を修正する方法が見つかっていません。当面は日本語でのテキスト読み上げ機能を無効にすることをお勧めします。**
必要なライブラリをインストールする場合は以下のコマンドを実行してください:
``` ```
pip3 install --upgrade pyopenjtalk jaconv mojimoji unidic fugashi pip3 install --upgrade pyopenjtalk jaconv mojimoji unidic fugashi
pip install unidic-lite pip install unidic-lite
python -m unidic download python -m unidic download
``` ```
Windowsの場合: **手動で:**
```sh ```sh
./install.bat pip3 install -r requirements.txt
# または
python3 setup.py install
``` ```
**手動:**
**注意:どのOSでも、インストールするChromeDriverがインストール済みのChromeバージョンと一致していることを確認してください。`google-chrome --version`を実行してください。Chrome >135の場合の既知の問題を参照してください。**
- *Linux*:
パッケージリストの更新:`sudo apt update`
依存関係のインストール:`sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
Chromeブラウザのバージョンに一致するChromeDriverのインストール:
`sudo apt install -y chromium-chromedriver`
要件のインストール:`pip3 install -r requirements.txt`
- *Macos*:
brewの更新:`brew update`
chromedriverのインストール:`brew install --cask chromedriver`
portaudioのインストール:`brew install portaudio`
pipのアップグレード:`python3 -m pip install --upgrade pip`
wheelのアップグレード:`pip3 install --upgrade setuptools wheel`
要件のインストール:`pip3 install -r requirements.txt`
- *Windows*:
pyreadline3のインストール:`pip install pyreadline3`
portaudioの手動インストール(例:vcpkgまたはビルド済みバイナリ経由)後、実行:`pip install pyaudio`
chromedriverの手動ダウンロードとインストール:https://sites.google.com/chromium.org/driver/getting-started
PATHに含まれるディレクトリにchromedriverを配置します。
要件のインストール:`pip3 install -r requirements.txt`
--- ---
## マシン上でローカルにLLMを実行するためのセットアップ ## ローカルマシンでLLMを実行するためのセットアップ
**少なくともDeepseek 14B使用を推奨します。より小さモデルは、特にウェブブラウジングのタスクで苦労ます。** **少なくともDeepseek 14B使用することをお勧めします。小さモデルは、特にウェブブラウジングのタスクで苦労する可能性があります。**
**ローカルプロバイダーをセットアップする**
**ローカルプロバイダーのセットアップ** たとえば、ollamaを使用してローカルプロバイダーを開始します:
ローカルプロバイダーを開始します。例えばollamaの場合:
```sh ```sh
ollama serve ollama serve
``` ```
サポートされているローカルプロバイダーのリストについては、以下を参照してください 以下に、サポートされているローカルプロバイダーのリストを示します
**config.ini更新** **config.ini更新する**
config.iniファイルを変更して、provider_nameをサポートされているプロバイダーにprovider_modelをプロバイダーがサポートするLLMに設定します。*Qwen*や*Deepseek*などの推論モデルを推奨します。 config.iniファイルを変更して、`provider_name`をサポートされているプロバイダーに設定し、`provider_model``deepseek-r1:14b`に設定します。
必要なハードウェアについては、READMEの最後にある**FAQ**を参照してください。 注意: `deepseek-r1:14b`は例です。ハードウェアが許可する場合は、より大きなモデルを使用してください。
```sh ```sh
[MAIN] [MAIN]
is_local = True # ローカルで実行するか、リモートプロバイダーで実行するか。 is_local = True
provider_name = ollama # またはlm-studio、openaiなど provider_name = ollama # または lm-studio、openai など
provider_model = deepseek-r1:14b # ハードウェアに合ったモデルを選択してください provider_model = deepseek-r1:14b
provider_server_address = 127.0.0.1:11434 provider_server_address = 127.0.0.1:11434
agent_name = Jarvis # AIの名前
recover_last_session = True # 前のセッションを復元するかどうか
save_session = True # 現在のセッションを記憶するかどうか
speak = True # テキスト読み上げ
listen = False # 音声認識、CLIのみ
work_dir = /Users/mlg/Documents/workspace # AgenticSeekのワークスペース。
jarvis_personality = False # より「Jarvis」らしい性格を使用するかどうか(実験的)
languages = en zh # 言語のリスト、テキスト読み上げはリストの最初の言語にデフォルト設定されます
[BROWSER]
headless_browser = True # ヘッドレスブラウザを使用するかどうか、ウェブインターフェースを使用する場合のみ推奨。
stealth_mode = True # undetected seleniumを使用してブラウザ検出を減らす
``` ```
警告:LM-studioを使用してLLMを実行する場合、provider_nameを`openai`に設定しないでください。`lm-studio`に設定してください。
注意:一部のプロバイダー(例:lm-studio)では、IPの前に`http://`が必要です。例:`http://127.0.0.1:1234`
**ローカルプロバイダーのリスト** **ローカルプロバイダーのリスト**
| プロバイダー | ローカル | 説明 | | プロバイダー | ローカル? | 説明 |
|-----------|--------|-----------------------------------------------------------| |-----------|--------|-----------------------------------------------------------|
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します | | ollama | はい | ollamaをLLMプロバイダーとして使用して、ローカルでLLMを簡単に実行 |
| lm-studio | はい | LM studioでLLMをローカル実行します`provider_name``lm-studio`に設定)| | lm-studio | はい | LM studioを使用してローカルでLLMを実行`provider_name``lm-studio`に設定)|
| openai | はい | openai互換API(例:llama.cppサーバー)を使用します | | openai | はい | OpenAI互換APIを使用 |
次のステップ[サービス開始AgenticSeek実行](#サービスの開始と実行) 次のステップ: [サービス開始してAgenticSeek実行する](#Start-services-and-Run)
*問題が発生し場合は、**既知の問題**セクションを参照してください* *問題が発生している場合は、**既知の問題**セクションを参照してください*
*ハードウェアがローカルでdeepseekを実行できない場合は、**API実行**セクションを参照してください* *ハードウェアがDeepseekをローカルで実行できない場合は、**APIを使用した実行**セクションを参照してください*
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。* *詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
--- ---
## APIで実行するためのセットアップ ## APIを使用したセットアップ
`config.ini`目的のプロバイダーを設定します。APIプロバイダーのリストについては、以下を参照してください。 `config.ini`希望するプロバイダーを設定してください。
```sh ```sh
[MAIN] [MAIN]
is_local = False is_local = False
provider_name = google provider_name = openai
provider_model = gemini-2.0-flash provider_model = gpt-4o
provider_server_address = 127.0.0.1:5000 # 関係ありません provider_server_address = 127.0.0.1:5000
``` ```
警告:設定に末尾のスペースがないことを確認してください。
APIキーをエクスポートします:`export <<PROVIDER>>_API_KEY="xxx"` 警告: `config.ini`に末尾のスペースがないことを確認してください。
例:`export TOGETHER_API_KEY="xxxxx"` ローカルのOpenAIベースのAPIを使用する場合は、`is_local`をTrueに設定してください。
**APIプロバイダーのリスト** OpenAIベースのAPIが独自のサーバーで実行されている場合は、IPアドレスを変更してください。
| プロバイダー | ローカル? | 説明 | 次のステップ: [サービスを開始してAgenticSeekを実行する](#Start-services-and-Run)
|-----------|--------|-----------------------------------------------------------|
| openai | 場合による | ChatGPT APIを使用 |
| deepseek-api | いいえ | Deepseek API(非プライベート) |
| huggingface| いいえ | Hugging-Face API(非プライベート) |
| togetherAI | いいえ | together AI APIを使用(非プライベート) |
| google | いいえ | google gemini APIを使用(非プライベート) |
*gpt-4oや他のclosedAIモデルの使用は推奨しません*。ウェブブラウジングやタスク計画のパフォーマンスが悪いです。 *問題が発生している場合は、**既知の問題**セクションを参照してください。*
また、geminiではコーディング/bashが失敗する可能性があることに注意してください。deepseek r1用に最適化されたフォーマットのプロンプトを無視するようです。
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
*問題が発生した場合は、**既知の問題**セクションを参照してください*
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。* *詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
@@ -235,30 +171,30 @@ APIキーをエクスポートします:`export <<PROVIDER>>_API_KEY="xxx"`
## サービスの開始と実行 ## サービスの開始と実行
必要に応じてPython環境をアクティブ化します 必要に応じてPython環境をアクティブにしてください
```sh ```sh
source agentic_seek_env/bin/activate source agentic_seek_env/bin/activate
``` ```
必要なサービスを開始します。これにより、docker-compose.ymlからすべてのサービスが開始されます。これには以下が含まれます: 必要なサービスを開始します。これにより、docker-compose.ymlから以下のサービスがすべて開始されます:
- searxng - searxng
- redissearxngに必要 - redis (searxngに必要)
- frontend - フロントエンド
```sh ```sh
sudo ./start_services.sh # MacOS sudo ./start_services.sh # MacOS
start ./start_services.cmd # Window start ./start_services.cmd # Windows
``` ```
**オプション1** CLIインターフェースで実行します **オプション1:** CLIインターフェースで実行。
```sh ```sh
python3 cli.py python3 cli.py
``` ```
CLIモードでは、config.iniで`headless_browser`をFalseに設定することをお勧めします **オプション2:** Webインターフェースで実行
**オプション2** Webインターフェースで実行します。 注意: 現在、CLIの使用を推奨しています。Webインターフェースは開発中です。
バックエンドを開始します。 バックエンドを開始します。
@@ -268,89 +204,113 @@ python3 api.py
`http://localhost:3000/`にアクセスすると、Webインターフェースが表示されます。 `http://localhost:3000/`にアクセスすると、Webインターフェースが表示されます。
--- 現在、Webインターフェースではメッセージのストリーミングがサポートされていないことに注意してください。
## 使用方法
`./start_services.sh`でサービスが起動していることを確認し、CLIモードの場合は`python3 cli.py`で、Webインターフェースの場合は`python3 api.py`を実行してから`localhost:3000`にアクセスしてAgenticSeekを実行します。
設定で`listen = True`を設定することで、音声認識を使用することもできます。CLIモードのみ。
終了するには、単に`goodbye`と発言/入力します。
以下に使用例をいくつか示します:
> *Pythonでスネークゲームを作って!*
> *フランスのレンヌでトップのカフェをウェブ検索し、3つのカフェのリストとその住所をrennes_cafes.txtに保存して。*
> *数値の階乗を計算するGoプログラムを書いて、それをfactorial.goとしてワークスペースに保存して。*
> *summer_picturesフォルダ内のすべてのJPGファイルを検索し、今日の日付で名前を変更し、名前変更されたファイルのリストをphotos_list.txtに保存して。*
> *2024年の人気のSF映画をオンラインで検索し、今夜観る映画を3つ選んで。リストをmovie_night.txtに保存して。*
> *2025年の最新AIニュース記事をウェブで検索し、3つ選択して、それらのタイトルと要約をスクレイピングするPythonスクリプトを書いて。スクリプトをnews_scraper.pyとして、要約を/home/projectsのai_news.txtに保存して。*
> *金曜日、無料の株価APIをウェブで検索し、supersuper7434567@gmail.comで登録し、そのAPIを使用してテスラの日々の価格を取得するPythonスクリプトを書いて、結果をstock_prices.csvに保存して。*
*フォーム入力機能はまだ実験的であり、失敗する可能性があることに注意してください。*
クエリを入力すると、AgenticSeekはタスクに最適なエージェントを割り当てます。
これは初期のプロトタイプであるため、エージェントルーティングシステムがクエリに基づいて常に適切なエージェントを割り当てるとは限りません。
したがって、何をしたいのか、AIがどのように進むべきかについて非常に明確にする必要があります。たとえば、ウェブ検索を実行させたい場合は、次のように言わないでください:
`一人旅に適した良い国を知っていますか?`
代わりに、次のように尋ねてください:
`ウェブ検索をして、一人旅に最適な国を見つけてください`
--- ---
## **独自のサーバーでLLMを実行するためのセットアップ** ## 使い方
強力なコンピューターまたは使用できるサーバーがあるが、ラップトップから使用したい場合は、カスタムLLMサーバーを使用してリモートサーバーでLLMを実行するオプションがあります。 警告: 現在、サポートされている言語は英語、中国語、フランス語のみです。他の言語でのプロンプトは機能しますが、適切なエージェントにルーティングされない場合があります。
サービスが`./start_services.sh`で起動していることを確認し、`python3 cli.py`でagenticSeekを実行します。
```sh
sudo ./start_services.sh
python3 cli.py
```
`>>> `と表示されます
これは、agenticSeekが指示を待っていることを示します。
configで`listen = True`を設定することで、音声認識を使用することもできます。
終了するには、単に`goodbye`と言います。
以下は使用例です:
### コーディング/バッシュ
> *Pythonでスネークゲームを作成*
> *C言語で行列の掛け算を教えて*
> *Golangでブラックジャックを作成*
### ウェブ検索
> *日本の最先端のAI研究を行っているクールなテックスタートアップを見つけるためにウェブ検索を行う*
> *agenticSeekを作成したのは誰かをインターネットで見つけることができますか?*
> *オンラインの燃料計算機を使用して、ニースからミラノまでの旅行の費用を見積もることができますか?*
### ファイルシステム
> *契約書.pdfがどこにあるか見つけてくれませんか?*
> *ディスクにどれだけの空き容量があるか教えて*
> *READMEを読んでプロジェクトを/home/path/projectにインストールしてください*
### カジュアル
> *フランスのレンヌについて教えて*
> *博士号を追求すべきですか?*
> *最高のワークアウトルーチンは何ですか?*
クエリを入力すると、agenticSeekはタスクに最適なエージェントを割り当てます。
これは初期のプロトタイプであるため、エージェントルーティングシステムはクエリに基づいて常に適切なエージェントを割り当てるとは限りません。
したがって、何を望んでいるか、AIがどのように進行するかについて非常に明確にする必要があります。たとえば、ウェブ検索を行いたい場合は、次のように言わないでください:
`一人旅に良い国を知っていますか?`
代わりに、次のように尋ねてください:
`ウェブ検索を行い、一人旅に最適な国を見つけてください`
---
## **ボーナス: 自分のサーバーでLLMを実行するためのセットアップ**
強力なコンピュータやサーバーを持っていて、それをラップトップから使用したい場合、リモートサーバーでLLMを実行するオプションがあります。
AIモデルを実行する「サーバー」で、IPアドレスを取得します。 AIモデルを実行する「サーバー」で、IPアドレスを取得します。
```sh ```sh
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # ローカルIP ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # ローカルIP
curl https://ipinfo.io/ip # パブリックIP curl https://ipinfo.io/ip # 公開IP
``` ```
注意WindowsまたはmacOSの場合、それぞれipconfigまたはifconfigを使用してIPアドレスを見つけます 注意: WindowsまたはmacOSの場合、IPアドレスを見つけるには、それぞれ`ipconfig`または`ifconfig`を使用してください
リポジトリをクローンし、`server/`フォルダに入ります。
リポジトリをクローンし、`server/`フォルダーに移動します。
```sh ```sh
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
cd agenticSeek/server/ cd agenticSeek/server/
``` ```
サーバー固有の要件をインストールします サーバー固有の依存関係をインストールします:
```sh ```sh
pip3 install -r requirements.txt pip3 install -r requirements.txt
``` ```
サーバー スクリプトを実行します。 サーバースクリプトを実行します。
```sh ```sh
python3 app.py --provider ollama --port 3333 python3 app.py --provider ollama --port 3333
``` ```
LLMサービスとして`ollama``llamacpp`のどちらかを選択できます。 `ollama``llamacpp`のどちらかをLLMサービスとして選択できます。
次に、個人用コンピュータで以下を行います:
次に、個人のコンピュータで: `config.ini`ファイルを変更し、`provider_name``server`に、`provider_model``deepseek-r1:xxb`に設定します。
`config.ini`ファイルを変更して、`provider_name``server`に、`provider_model``deepseek-r1:xxb`に設定します。
`provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。 `provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。
```sh ```sh
@@ -361,46 +321,46 @@ provider_model = deepseek-r1:70b
provider_server_address = x.x.x.x:3333 provider_server_address = x.x.x.x:3333
``` ```
次のステップ: [サービスを開始してAgenticSeekを実行する](#Start-services-and-Run)
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
--- ---
## 音声認識 ## 音声認識
現在、音声認識は英語でのみ機能することに注意してください。 現在、音声認識は英語でのみ動作することに注意してください。
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します 音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
``` ```
listen = True listen = True
``` ```
有効にすると、音声認識機能は、入力を処理し始める前にトリガーキーワード(エージェントの名前)をリッスンします。*config.ini*ファイル`agent_name`値を更新することで、エージェントの名前をカスタマイズできます 有効にすると、音声認識機能はトリガーキーワード(エージェントの名前)を待ちます。その後、入力を処理します。エージェントの名前は*config.ini*ファイル`agent_name`値を更新することでカスタマイズできます:
``` ```
agent_name = Friday agent_name = Friday
``` ```
最適な認識のためには、エージェント名として「John」や「Emmaのような一般的な英語の名前を使用することをお勧めします。 最適な認識のために、"John"や"Emma"のような一般的な英語の名前をエージェント名として使用することをお勧めします。
トランスクリプトが表示され始めたら、エージェントの名前を声に出して起動します(例:Friday)。 トランスクリプトが表示され始めたら、エージェントの名前を大声で言って起動します(例:"Friday")。
クエリをはっきりと話します。 クエリを明確に話します。
システムに処理を進めるよう合図するために、確認フレーズでリクエストを終了します。確認フレーズの例は次のとおりです: リクエストを終了する際に確認フレーズを使用してシステムに進行を通知します。確認フレーズの例は次のようなものがあります:
``` ```
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?" "do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
``` ```
## 設定 ## 設定
設定例 設定例:
``` ```
[MAIN] [MAIN]
is_local = True is_local = True
provider_name = ollama provider_name = ollama
provider_model = deepseek-r1:32b provider_model = deepseek-r1:1.5b
provider_server_address = 127.0.0.1:11434 provider_server_address = 127.0.0.1:11434
agent_name = Friday agent_name = Friday
recover_last_session = False recover_last_session = False
@@ -409,7 +369,7 @@ speak = False
listen = False listen = False
work_dir = /Users/mlg/Documents/ai_folder work_dir = /Users/mlg/Documents/ai_folder
jarvis_personality = False jarvis_personality = False
languages = en zh languages = en ja
[BROWSER] [BROWSER]
headless_browser = False headless_browser = False
stealth_mode = False stealth_mode = False
@@ -417,154 +377,112 @@ stealth_mode = False
**説明**: **説明**:
- is_local -> エージェントをローカルで実行する(True)か、リモートサーバーで実行する(False) - is_local -> エージェントをローカルで実行する(True)か、リモートサーバーで実行する(False)。
- provider_name -> 使用するプロバイダー(`ollama``server``lm-studio``deepseek-api`のいずれか)。
- provider_name -> 使用するプロバイダー(`ollama``server``lm-studio``deepseek-api`のいずれか) - provider_model -> 使用するモデル、例: deepseek-r1:1.5b。
- provider_server_address -> サーバーアドレス、例: 127.0.0.1:11434(ローカルの場合)。非ローカルAPIの場合は何でも設定できます。
- provider_model -> 使用するモデル、例:deepseek-r1:32b - agent_name -> エージェントの名前、例: Friday。TTSのトリガーワードとして使用されます
- recover_last_session -> 最後のセッションから再開する(True)か、しない(False)。
- provider_server_address -> サーバーアドレス、例:ローカルの場合は127.0.0.1:11434。非ローカルAPIの場合は何でも設定します - save_session -> セッションデータを保存する(True)か、しない(False)
- speak -> 音声出力を有効にする(True)か、しない(False)。
- agent_name -> エージェントの名前、例:Friday。TTSのトリガーワードとして使用されます - listen -> 音声入力を有効にする(True)か、しない(False)
- work_dir -> AIがアクセスするフォルダー。例: /Users/user/Documents/。
- recover_last_session -> 前回のセッションから再開する(True)かしない(False - jarvis_personality -> JARVISのようなパーソナリティを使用する(True)かしない(False。これは単にプロンプトファイルを変更するだけです
- headless_browser -> ウィンドウを表示せずにブラウザを実行する(True)か、しない(False)。
- save_session -> セッションデータを保存する(True)かしない(False)か - stealth_mode -> ボット検出を難しくします。唯一の欠点は、anticaptcha拡張機能を手動でインストールする必要があることです
- languages -> List of supported languages. Required for agent routing system. The longer the languages list the more model will be downloaded.
- speak -> 音声出力を有効にする(True)かしない(False)か。
- listen -> 音声入力をリッスンする(True)かしない(False)か。
- work_dir -> AIがアクセスできるフォルダ。例:/Users/user/Documents/。
- jarvis_personality -> JARVISのような性格を使用する(True)かしない(False)か。これは単にプロンプトファイルを変更します。
- languages -> サポートされている言語のリスト。LLMルーターが正しく機能するために必要です。あまりにも多くの言語や類似した言語を入れすぎないようにしてください。
- headless_browser -> 表示ウィンドウなしでブラウザを実行する(True)かしない(False)か。
- stealth_mode -> ボット検出を困難にします。唯一の欠点は、anticaptcha拡張機能を手動でインストールする必要があることです。
- languages -> サポートされている言語のリスト。エージェントルーティングシステムに必要です。言語リストが長いほど、ダウンロードされるモデルが多くなります。
## プロバイダー ## プロバイダー
以下の表は利用可能なプロバイダーを示しています 以下の表は利用可能なプロバイダーを示しています:
| プロバイダー | ローカル | 説明 | | プロバイダー | ローカル? | 説明 |
|-----------|--------|-----------------------------------------------------------| |-----------|--------|-----------------------------------------------------------|
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します | | ollama | はい | ollamaをLLMプロバイダーとして使用して、ローカルでLLMを簡単に実行 |
| server | はい | モデルを別のマシンでホストし、ローカルマシンで実行します | | server | はい | モデルを別のマシンでホストし、ローカルマシンで実行 |
| lm-studio | はい | LM studioでLLMをローカル実行します`lm-studio` | | lm-studio | はい | LM studio`lm-studio`を使用してローカルでLLMを実行 |
| openai | 場合による | ChatGPT API(非プライベート)またはopenai互換APIを使用 | | openai | 場合による | ChatGPT API(非プライベート)またはopenai互換APIを使用 |
| deepseek-api | いいえ | Deepseek API(非プライベート) | | deepseek-api | いいえ | Deepseek API(非プライベート) |
| huggingface| いいえ | Hugging-Face API(非プライベート) | | huggingface| いいえ | Hugging-Face API(非プライベート) |
| togetherAI | いいえ | together AI APIを使用(非プライベート) | | togetherAI | いいえ | together AI API(非プライベート)を使用
| google | いいえ | google gemini APIを使用(非プライベート) |
プロバイダーを選択するには、config.iniを変更します:
プロバイダーを選択するには、config.iniを変更します:
``` ```
is_local = True is_local = False
provider_name = ollama provider_name = openai
provider_model = deepseek-r1:32b provider_model = gpt-4o
provider_server_address = 127.0.0.1:5000 provider_server_address = 127.0.0.1:5000
``` ```
`is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalseである必要があります `is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalse。
`provider_name`: 使用するプロバイダーを名前で選択します。上記のプロバイダーリストを参照してください。 `provider_name`: 使用するプロバイダーを名前で選択します。上記のプロバイダーリストを参照してください。
`provider_model`: エージェントが使用するモデルを設定します。 `provider_model`: エージェントが使用するモデルを設定します。
`provider_server_address`: サーバーアドレス。APIプロバイダーには使用されません `provider_server_address`: サーバープロバイダーを使用しない場合は何でも設定できます
# 既知の問題 # 既知の問題
## Chromedriverの問題 ## Chromedriverの問題
**既知のエラー #1:** *chromedriverの不一致* **既知のエラー#1:** *chromedriverの不一致*
`Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113 `Exception: Failed to initialize browser: Message: session not created: This version of ChromeDriver only supports Chrome version 113
Current browser version is 134.0.6998.89 with binary path` Current browser version is 134.0.6998.89 with binary path`
これは、ブラウザとchromedriverのバージョンが一致しない場合に発生します。 これは、ブラウザとchromedriverのバージョンが一致しない場合に発生します。
最新バージョンをダウンロードするためにナビゲートする必要があります: 最新バージョンをダウンロードするには、次のリンクにアクセスしてください:
https://developer.chrome.com/docs/chromedriver/downloads https://developer.chrome.com/docs/chromedriver/downloads
Chromeバージョン115以降を使用している場合は、以下にアクセスしてください Chromeバージョン115以降を使用している場合は、次のリンクにアクセスしてください:
https://googlechromelabs.github.io/chrome-for-testing/ https://googlechromelabs.github.io/chrome-for-testing/
そして、OSに一致するchromedriverバージョンをダウンロードします。 お使いのOSに対応するchromedriverバージョンをダウンロードします。
![代替テキスト](./media/chromedriver_readme.png) ![alt text](./media/chromedriver_readme.png)
このセクションが不完全な場合は、問題を提起してください。 このセクションが不完全な場合は、問題を報告してください。
## 接続アダプタの問題
```
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:11434/v1/chat/completions'
```
プロバイダーのIPアドレスの前に`http://`があることを確認してください:
`provider_server_address = http://127.0.0.1:11434`
## SearxNGのベースURLを指定する必要があります
```
raise ValueError("SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.")
ValueError: SearxNG base URL must be provided either as an argument or via the SEARXNG_BASE_URL environment variable.
```
`.env.example``.env`として移動しなかった可能性がありますか?SEARXNG_BASE_URLをエクスポートすることもできます:
`export SEARXNG_BASE_URL="http://127.0.0.1:8080"`
## FAQ ## FAQ
**Q: どのようなハードウェアが必要ですか?** **Q: どのようなハードウェアが必要ですか?**
| モデルサイズ | GPU | コメント | | モデルサイズ | GPU | コメント |
|-----------|------------|--------------------------------------------------------------------------| |-----------|--------|-----------------------------------------------------------|
| 7B | 8GB VRAM | ⚠️ 推奨。パフォーマンスがく、幻覚が頻繁に発生し、プランナーエージェント失敗する可能性が高いです。 | | 7B | 8GB VRAM | ⚠️ 推奨されません。パフォーマンスがく、頻繁に幻覚を起こし、プランナーエージェント失敗する可能性が高いです。 |
| 14B | 12GB VRAM(例:RTX 3060 | ✅ 簡単なタスクには使用可能。ウェブブラウジングや計画タスク苦労する可能性があります。 | | 14B | 12GB VRAM (例: RTX 3060) | ✅ 簡単なタスクには使用可能です。ウェブブラウジングや計画タスクには苦労する可能性があります。 |
| 32B | 24GB以上のVRAM(例:RTX 4090 | 🚀 ほとんどのタスクで成功しますが、タスク計画まだ苦労する可能性があります。 | | 32B | 24GB以上のVRAM (例: RTX 4090) | 🚀 ほとんどのタスクで成功しますが、タスク計画にはまだ苦労する可能性があります。 |
| 70B+ | 48GB以上のVRAM(例:mac studio | 💪 素晴らしい。高度なユースケースに推奨されます。 | | 70B+ | 48GB以上のVRAM (例: Mac Studio) | 💪 優れた性能。高度なユースケースに推奨されます。 |
**Q: なぜ他のモデルではなくDeepseek R1のですか?** **Q: なぜ他のモデルではなくDeepseek R1を選ぶのですか?**
Deepseek R1は、そのサイズに対して推論とツール使用に優れています。私たちのニーズに合っていると考えており、他のモデルも正常に動作しますが、Deepseekが私たちの主な選択です。 Deepseek R1は、そのサイズに対して推論とツール使用に優れています。私たちのニーズに最適だと考えています。他のモデルも問題なく動作しますが、Deepseekが私たちの主な選択です。
**Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?** **Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?**
ローカルが実行されていること`ollama serve`)、`config.ini`がプロバイダー一致していること、依存関係がインストールされていることを確認してください。それでも解決しない場合は、遠慮なく問題を提起してください。 Ollamaが実行中であることを確認してください`ollama serve`)、`config.ini`がプロバイダー一致していること、および依存関係がインストールされていることを確認してください。それでも解決しない場合は、問題を報告してください。
**Q: 本当に100%ローカルで実行できますか?** **Q: 本当に100%ローカルで実行できますか?**
はい、Ollama、lm-studio、またはサーバープロバイダーを使用すると、すべての音声認識、LLM、テキスト読み上げモデルがローカルで実行されます。非ローカルオプション(OpenAIまたはその他のAPI)はオプションです。 はい、OllamaまたはServerプロバイダーを使用すると、すべての音声認識、LLM、および音声合成モデルがローカルで実行されます。非ローカルオプション(OpenAIまたは他のAPI)はオプションです。
**Q: Manusがあるのに、なぜAgenticSeekを使うべきなのですか?** **Q: Manusを持っているのに、なぜAgenticSeekを使用する必要があるのですか?**
これは、AIエージェントへの関心から始たサイドプロジェクトです。特別なは、ローカルモデルを使用し、APIを避けたいということです。 これは、AIエージェントに関する興味から始まったサイドプロジェクトです。特別なは、ローカルモデルを使用し、APIを避けことです。
私たちはJarvisFriday(アイアンマン映画)からインスピレーションを得て「クール」にしましたが、機能性についてはManusからより多くのインスピレーションを得ています。なぜなら、それが人々が最初に望むもの、つまりローカルManusの代替だからです。 私たちはJarvisFriday(アイアンマン映画)からインスピレーションを得て「クール」にしようとしましたが、機能性に関してはManusから多くのインスピレーションを得ています。なぜなら、人々が最初に求めているのはローカルManusの代替だからです。
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIコストを回避します。 Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIコストを回避します。
## 貢献する ## 貢献
AgenticSeekを改善するための開発者を募集しています!オープンな問題やディスカッションを確認してください。 AgenticSeekを改善するための開発者をしています!オープンな問題やディスカッションを確認してください。
[貢献ガイド](./docs/CONTRIBUTING.md) [![Star History Chart](https://api.star-history.com/svg?repos=Fosowl/agenticSeek&type=Date)](https://www.star-history.com/#Fosowl/agenticSeek&Date)
[![スター履歴チャート](https://api.star-history.com/svg?repos=Fosowl/agenticSeek&type=Date)](https://www.star-history.com/#Fosowl/agenticSeek&Date) ## 著者:
> [Fosowl](https://github.com/Fosowl)
## メンテナー: > [steveh8758](https://github.com/steveh8758)
> [Fosowl](https://github.com/Fosowl) | パリ時間
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
+4 -16
View File
@@ -34,7 +34,7 @@ config.read('config.ini')
api.add_middleware( api.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=["http://localhost", "http://localhost:3000"], allow_origins=["*"],
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
@@ -58,7 +58,7 @@ def initialize_system():
logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})") logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})")
browser = Browser( browser = Browser(
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]), create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode),
anticaptcha_manual_install=stealth_mode anticaptcha_manual_install=stealth_mode
) )
logger.info("Browser initialized") logger.info("Browser initialized")
@@ -128,12 +128,6 @@ async def is_active():
logger.info("Is active endpoint called") logger.info("Is active endpoint called")
return {"is_active": interaction.is_active} return {"is_active": interaction.is_active}
@api.get("/stop")
async def stop():
logger.info("Stop endpoint called")
interaction.current_agent.request_stop()
return JSONResponse(status_code=200, content={"status": "stopped"})
@api.get("/latest_answer") @api.get("/latest_answer")
async def get_latest_answer(): async def get_latest_answer():
global query_resp_history global query_resp_history
@@ -144,7 +138,6 @@ async def get_latest_answer():
query_resp = { query_resp = {
"done": "false", "done": "false",
"answer": interaction.current_agent.last_answer, "answer": interaction.current_agent.last_answer,
"reasoning": interaction.current_agent.last_reasoning,
"agent_name": interaction.current_agent.agent_name if interaction.current_agent else "None", "agent_name": interaction.current_agent.agent_name if interaction.current_agent else "None",
"success": interaction.current_agent.success, "success": interaction.current_agent.success,
"blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.get_last_blocks_result())} if interaction.current_agent else {}, "blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.get_last_blocks_result())} if interaction.current_agent else {},
@@ -152,7 +145,6 @@ async def get_latest_answer():
"uid": uid "uid": uid
} }
interaction.current_agent.last_answer = "" interaction.current_agent.last_answer = ""
interaction.current_agent.last_reasoning = ""
query_resp_history.append(query_resp) query_resp_history.append(query_resp)
return JSONResponse(status_code=200, content=query_resp) return JSONResponse(status_code=200, content=query_resp)
if query_resp_history: if query_resp_history:
@@ -166,7 +158,6 @@ async def think_wrapper(interaction, query):
success = await interaction.think() success = await interaction.think()
if not success: if not success:
interaction.last_answer = "Error: No answer from agent" interaction.last_answer = "Error: No answer from agent"
interaction.last_reasoning = "Error: No reasoning from agent"
interaction.last_success = False interaction.last_success = False
else: else:
interaction.last_success = True interaction.last_success = True
@@ -175,8 +166,7 @@ async def think_wrapper(interaction, query):
return success return success
except Exception as e: except Exception as e:
logger.error(f"Error in think_wrapper: {str(e)}") logger.error(f"Error in think_wrapper: {str(e)}")
interaction.last_answer = f"" interaction.last_answer = f"Error: {str(e)}"
interaction.last_reasoning = f"Error: {str(e)}"
interaction.last_success = False interaction.last_success = False
raise e raise e
@@ -187,7 +177,6 @@ async def process_query(request: QueryRequest):
query_resp = QueryResponse( query_resp = QueryResponse(
done="false", done="false",
answer="", answer="",
reasoning="",
agent_name="Unknown", agent_name="Unknown",
success="false", success="false",
blocks={}, blocks={},
@@ -205,7 +194,6 @@ async def process_query(request: QueryRequest):
if not success: if not success:
query_resp.answer = interaction.last_answer query_resp.answer = interaction.last_answer
query_resp.reasoning = interaction.last_reasoning
return JSONResponse(status_code=400, content=query_resp.jsonify()) return JSONResponse(status_code=400, content=query_resp.jsonify())
if interaction.current_agent: if interaction.current_agent:
@@ -220,11 +208,11 @@ async def process_query(request: QueryRequest):
logger.info(f"Blocks: {blocks_json}") logger.info(f"Blocks: {blocks_json}")
query_resp.done = "true" query_resp.done = "true"
query_resp.answer = interaction.last_answer query_resp.answer = interaction.last_answer
query_resp.reasoning = interaction.last_reasoning
query_resp.agent_name = interaction.current_agent.agent_name query_resp.agent_name = interaction.current_agent.agent_name
query_resp.success = str(interaction.last_success) query_resp.success = str(interaction.last_success)
query_resp.blocks = blocks_json query_resp.blocks = blocks_json
# Store the raw dictionary representation
query_resp_dict = { query_resp_dict = {
"done": query_resp.done, "done": query_resp.done,
"answer": query_resp.answer, "answer": query_resp.answer,
+4 -4
View File
@@ -29,7 +29,7 @@ async def main():
is_local=config.getboolean('MAIN', 'is_local')) is_local=config.getboolean('MAIN', 'is_local'))
browser = Browser( browser = Browser(
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]), create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode),
anticaptcha_manual_install=stealth_mode anticaptcha_manual_install=stealth_mode
) )
@@ -49,9 +49,9 @@ async def main():
PlannerAgent(name="Planner", PlannerAgent(name="Planner",
prompt_path=f"prompts/{personality_folder}/planner_agent.txt", prompt_path=f"prompts/{personality_folder}/planner_agent.txt",
provider=provider, verbose=False, browser=browser), provider=provider, verbose=False, browser=browser),
#McpAgent(name="MCP Agent", McpAgent(name="MCP Agent",
# prompt_path=f"prompts/{personality_folder}/mcp_agent.txt", prompt_path=f"prompts/{personality_folder}/mcp_agent.txt",
# provider=provider, verbose=False), # NOTE under development provider=provider, verbose=False),
] ]
interaction = Interaction(agents, interaction = Interaction(agents,
File diff suppressed because it is too large Load Diff
-1
View File
@@ -10,7 +10,6 @@
"axios": "^1.8.4", "axios": "^1.8.4",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-markdown": "^10.1.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
-86
View File
@@ -167,88 +167,6 @@ body {
margin-bottom: 8px; margin-bottom: 8px;
} }
/* Message header layout */
.message-header {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.reasoning-toggle {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
color: #fff;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 4px;
align-self: flex-start;
}
.reasoning-toggle:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3);
}
.reasoning-toggle:active {
transform: translateY(1px);
}
/* Reasoning content container */
.reasoning-content {
margin-top: 12px;
padding: 12px;
background: rgba(0, 0, 0, 0.2);
border-left: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 0 4px 4px 0;
font-size: 0.9em;
line-height: 1.4;
}
.reasoning-content h1,
.reasoning-content h2,
.reasoning-content h3,
.reasoning-content h4,
.reasoning-content h5,
.reasoning-content h6 {
font-size: 1em;
margin: 8px 0 4px 0;
color: rgba(255, 255, 255, 0.9);
}
.reasoning-content p {
margin: 6px 0;
color: rgba(255, 255, 255, 0.8);
}
/* Alternative light theme styles */
.message.user-message .reasoning-toggle {
background: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.1);
color: #333;
}
.message.user-message .reasoning-toggle:hover {
background: rgba(0, 0, 0, 0.1);
border-color: rgba(0, 0, 0, 0.2);
}
.message.user-message .reasoning-content {
background: rgba(0, 0, 0, 0.03);
border-left-color: rgba(0, 0, 0, 0.2);
}
.message.user-message .reasoning-content p {
color: rgba(0, 0, 0, 0.7);
}
.placeholder { .placeholder {
text-align: center; text-align: center;
color: #64748b; /* lighter gray */ color: #64748b; /* lighter gray */
@@ -448,10 +366,6 @@ body {
color: #28a745; /* success */ color: #28a745; /* success */
} }
.block-failure {
color: #d21b0b; /* success */
}
.block pre { .block pre {
background-color: #1a202c; /* Darker than darkCard */ background-color: #1a202c; /* Darker than darkCard */
padding: 12px; padding: 12px;
+10 -59
View File
@@ -1,5 +1,4 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import ReactMarkdown from 'react-markdown';
import axios from 'axios'; import axios from 'axios';
import './App.css'; import './App.css';
import { colors } from './colors'; import { colors } from './colors';
@@ -13,7 +12,6 @@ function App() {
const [responseData, setResponseData] = useState(null); const [responseData, setResponseData] = useState(null);
const [isOnline, setIsOnline] = useState(false); const [isOnline, setIsOnline] = useState(false);
const [status, setStatus] = useState('Agents ready'); const [status, setStatus] = useState('Agents ready');
const [expandedReasoning, setExpandedReasoning] = useState(new Set());
const messagesEndRef = useRef(null); const messagesEndRef = useRef(null);
useEffect(() => { useEffect(() => {
@@ -27,7 +25,7 @@ function App() {
const checkHealth = async () => { const checkHealth = async () => {
try { try {
await axios.get('http://127.0.0.1:8000/health'); await axios.get('http://0.0.0.0:8000/health');
setIsOnline(true); setIsOnline(true);
console.log('System is online'); console.log('System is online');
} catch { } catch {
@@ -39,7 +37,7 @@ function App() {
const fetchScreenshot = async () => { const fetchScreenshot = async () => {
try { try {
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
const res = await axios.get(`http://127.0.0.1:8000/screenshots/updated_screen.png?timestamp=${timestamp}`, { const res = await axios.get(`http://0.0.0.0:8000/screenshots/updated_screen.png?timestamp=${timestamp}`, {
responseType: 'blob' responseType: 'blob'
}); });
console.log('Screenshot fetched successfully'); console.log('Screenshot fetched successfully');
@@ -76,21 +74,9 @@ function App() {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}; };
const toggleReasoning = (messageIndex) => {
setExpandedReasoning(prev => {
const newSet = new Set(prev);
if (newSet.has(messageIndex)) {
newSet.delete(messageIndex);
} else {
newSet.add(messageIndex);
}
return newSet;
});
};
const fetchLatestAnswer = async () => { const fetchLatestAnswer = async () => {
try { try {
const res = await axios.get('http://127.0.0.1:8000/latest_answer'); const res = await axios.get('http://0.0.0.0:8000/latest_answer');
const data = res.data; const data = res.data;
updateData(data); updateData(data);
@@ -107,7 +93,6 @@ function App() {
{ {
type: 'agent', type: 'agent',
content: data.answer, content: data.answer,
reasoning: data.reasoning,
agentName: data.agent_name, agentName: data.agent_name,
status: data.status, status: data.status,
uid: data.uid, uid: data.uid,
@@ -135,19 +120,6 @@ function App() {
})); }));
}; };
const handleStop = async (e) => {
e.preventDefault();
checkHealth();
setIsLoading(false);
setError(null);
try {
const res = await axios.get('http://127.0.0.1:8000/stop');
setStatus("Requesting stop...");
} catch (err) {
console.error('Error stopping the agent:', err);
}
}
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
checkHealth(); checkHealth();
@@ -162,7 +134,7 @@ function App() {
try { try {
console.log('Sending query:', query); console.log('Sending query:', query);
setQuery('waiting for response...'); setQuery('waiting for response...');
const res = await axios.post('http://127.0.0.1:8000/query', { const res = await axios.post('http://0.0.0.0:8000/query', {
query, query,
tts_enabled: false tts_enabled: false
}); });
@@ -199,6 +171,8 @@ function App() {
</header> </header>
<main className="main"> <main className="main">
<div className="app-sections"> <div className="app-sections">
<div className="chat-section"> <div className="chat-section">
<h2>Chat Interface</h2> <h2>Chat Interface</h2>
<div className="messages"> <div className="messages">
@@ -216,28 +190,10 @@ function App() {
: 'error-message' : 'error-message'
}`} }`}
> >
<div className="message-header">
{msg.type === 'agent' && ( {msg.type === 'agent' && (
<span className="agent-name">{msg.agentName}</span> <span className="agent-name">{msg.agentName}</span>
)} )}
{msg.type === 'agent' && msg.reasoning && expandedReasoning.has(index) && ( <p>{msg.content}</p>
<div className="reasoning-content">
<ReactMarkdown>{msg.reasoning}</ReactMarkdown>
</div>
)}
{msg.type === 'agent' && (
<button
className="reasoning-toggle"
onClick={() => toggleReasoning(index)}
title={expandedReasoning.has(index) ? "Hide reasoning" : "Show reasoning"}
>
{expandedReasoning.has(index) ? '▼' : '▶'} Reasoning
</button>
)}
</div>
<div className="message-content">
<ReactMarkdown>{msg.content}</ReactMarkdown>
</div>
</div> </div>
)) ))
)} )}
@@ -256,9 +212,6 @@ function App() {
<button type="submit" disabled={isLoading}> <button type="submit" disabled={isLoading}>
Send Send
</button> </button>
<button onClick={handleStop}>
Stop
</button>
</form> </form>
</div> </div>
@@ -288,11 +241,9 @@ function App() {
<p className="block-tool">Tool: {block.tool_type}</p> <p className="block-tool">Tool: {block.tool_type}</p>
<pre>{block.block}</pre> <pre>{block.block}</pre>
<p className="block-feedback">Feedback: {block.feedback}</p> <p className="block-feedback">Feedback: {block.feedback}</p>
{block.success ? ( <p className="block-success">
<p className="block-success">Success</p> Success: {block.success ? 'Yes' : 'No'}
) : ( </p>
<p className="block-failure">Failure</p>
)}
</div> </div>
)) ))
) : ( ) : (
+9 -21
View File
@@ -3,22 +3,10 @@
echo "Starting installation for Linux..." echo "Starting installation for Linux..."
set -e set -e
if ! command -v python3.10 &> /dev/null; then
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
exit 1
fi
# Check if pip3.10 is available
if ! python3.10 -m pip --version &> /dev/null; then
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
fi
# Update package list # Update package list
sudo apt-get update || { echo "Failed to update package list"; exit 1; } sudo apt-get update || { echo "Failed to update package list"; exit 1; }
# make sure essential tool are installed # make sure essential tool are installed
# Install essential tools
sudo apt-get install -y \ sudo apt-get install -y \
python3-dev \ python3-dev \
python3-pip \ python3-pip \
@@ -33,15 +21,15 @@ sudo apt-get install -y \
libnss3 \ libnss3 \
libxss1 || { echo "Failed to install packages"; exit 1; } libxss1 || { echo "Failed to install packages"; exit 1; }
# Upgrade pip for Python 3.10 # upgrade pip
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; } pip install --upgrade pip
# Install and upgrade setuptools and wheel # install wheel
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; } pip install --upgrade pip setuptools wheel
# Install Selenium for chromedriver
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
# Install Python dependencies from requirements.txt
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; }
# install docker compose # install docker compose
sudo apt install -y docker-compose sudo apt install -y docker-compose
# Install Selenium for chromedriver
pip3 install selenium
# Install Python dependencies from requirements.txt
pip3 install -r requirements.txt --no-cache-dir
echo "Installation complete for Linux!" echo "Installation complete for Linux!"
+7 -20
View File
@@ -4,18 +4,6 @@ echo "Starting installation for macOS..."
set -e set -e
if ! command -v python3.10 &> /dev/null; then
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
exit 1
fi
# Check if pip3.10 is available
if ! python3.10 -m pip --version &> /dev/null; then
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
fi
# Check if homebrew is installed # Check if homebrew is installed
if ! command -v brew &> /dev/null; then if ! command -v brew &> /dev/null; then
echo "Homebrew not found. Installing Homebrew..." echo "Homebrew not found. Installing Homebrew..."
@@ -30,14 +18,13 @@ brew install wget
brew install --cask chromedriver brew install --cask chromedriver
# Install portaudio for pyAudio using Homebrew # Install portaudio for pyAudio using Homebrew
brew install portaudio brew install portaudio
# update pip
# Upgrade pip for Python 3.10 python3 -m pip install --upgrade pip
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; } # upgrade setuptools and wheel
# Install and upgrade setuptools and wheel pip3 install --upgrade setuptools wheel
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; } # Install Selenium
# Install Selenium for chromedriver pip3 install selenium
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
# Install Python dependencies from requirements.txt # Install Python dependencies from requirements.txt
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; } pip3 install -r requirements.txt --no-cache-dir
echo "Installation complete for macOS!" echo "Installation complete for macOS!"
File diff suppressed because it is too large Load Diff
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin python3
"""
self_run.py is a script for automatically creating prompts, and saving history as training data.
"""
import sys
import argparse
import configparser
import asyncio
from sources.llm_provider import Provider
from sources.interaction import Interaction
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent, PlannerAgent, BrowserAgent, McpAgent
from sources.browser import Browser, create_driver
import warnings
warnings.filterwarnings("ignore")
config = configparser.ConfigParser()
config.read('config.ini')
def copy_conversations_folder():
source_path = "conversations/"
destination_path = "training_data/"
if not os.path.exists(destination_path):
os.makedirs(destination_path)
for filename in os.listdir(source_path):
source_file = os.path.join(source_path, filename)
destination_file = os.path.join(destination_path, filename)
shutil.copy2(source_file, destination_file)
print(f"Copied {source_file} to {destination_file}")
def get_random_query(provider):
prompt = """
You are an expert in crafting queries for AgenticSeek, a AI assistant that autonomously browses the web, writes code, plans tasks, and manages files. It supports tasks like web searches, coding in Python/C/Go/Java, file operations, task planning.
Queries must be explicit, specifying actions like "search the web," "write code," or "save to a file," as AgenticSeek's agent routing may not infer vague intents.
Generate a single realistic user query for AgenticSeek. The query should:
Be concise and explicit about the desired action (e.g., web search, coding, file management).
Align with AgenticSeeks capabilities (web browsing, coding, task planning, file operations).
Include a specific output where relevant (e.g., save to a file with a clear name and path).
Reflect a practical use case (e.g., research, programming, personal tasks).
Be formatted as a single sentence.
Example Query:
Search the web for the best hiking trails in Colorado and save a list of three trails with their locations in hiking_trails.txt in /home/project
"""
history = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": prompt}]
thought = provider.respond(history)
return thought
async def self_runner():
provider = Provider(provider_name=config["MAIN"]["provider_name"],
model=config["MAIN"]["provider_model"],
server_address=config["MAIN"]["provider_server_address"],
is_local=config.getboolean('MAIN', 'is_local'))
browser = Browser(
create_driver(headless=True, stealth_mode=False),
anticaptcha_manual_install=False
)
agents = [
CasualAgent(name=config["MAIN"]["agent_name"],
prompt_path=f"prompts/base/casual_agent.txt",
provider=provider, verbose=False),
CoderAgent(name="coder",
prompt_path=f"prompts/base/coder_agent.txt",
provider=provider, verbose=False),
FileAgent(name="File Agent",
prompt_path=f"prompts/base/file_agent.txt",
provider=provider, verbose=False),
BrowserAgent(name="Browser",
prompt_path=f"prompts/base/browser_agent.txt",
provider=provider, verbose=False, browser=browser),
PlannerAgent(name="Planner",
prompt_path=f"prompts/base/planner_agent.txt",
provider=provider, verbose=False, browser=browser)
]
interaction = Interaction(agents,
tts_enabled=False,
stt_enabled=False,
recover_last_session=False,
langs=['en']
)
print("Start self-running for training data generation...")
try:
while interaction.is_active:
query = get_random_query(provider)
print(f"Generated query: {query}")
interaction.set_query(query)
if await interaction.think():
interaction.show_answer()
except Exception as e:
if config.getboolean('MAIN', 'save_session'):
interaction.save_session()
copy_conversations_folder()
raise e
finally:
if config.getboolean('MAIN', 'save_session'):
interaction.save_session()
copy_conversations_folder()
if __name__ == "__main__":
asyncio.run(self_runner())
+2 -17
View File
@@ -44,9 +44,7 @@ class Agent():
self.blocks_result = [] self.blocks_result = []
self.success = True self.success = True
self.last_answer = "" self.last_answer = ""
self.last_reasoning = ""
self.status_message = "Haven't started yet" self.status_message = "Haven't started yet"
self.stop = False
self.verbose = verbose self.verbose = verbose
self.executor = ThreadPoolExecutor(max_workers=1) self.executor = ThreadPoolExecutor(max_workers=1)
@@ -66,10 +64,6 @@ class Agent():
def get_last_answer(self) -> str: def get_last_answer(self) -> str:
return self.last_answer return self.last_answer
@property
def get_last_reasoning(self) -> str:
return self.last_reasoning
@property @property
def get_blocks(self) -> list: def get_blocks(self) -> list:
return self.blocks_result return self.blocks_result
@@ -120,13 +114,6 @@ class Agent():
except Exception as e: except Exception as e:
raise e raise e
def request_stop(self) -> None:
"""
Request the agent to stop.
"""
self.stop = True
self.status_message = "Stopped"
@abstractmethod @abstractmethod
def process(self, prompt, speech_module) -> str: def process(self, prompt, speech_module) -> str:
""" """
@@ -140,10 +127,8 @@ class Agent():
Remove the reasoning block of reasoning model like deepseek. Remove the reasoning block of reasoning model like deepseek.
""" """
end_tag = "</think>" end_tag = "</think>"
end_idx = text.rfind(end_tag) end_idx = text.rfind(end_tag)+8
if end_idx == -1: return text[end_idx:]
return text
return text[end_idx+8:]
def extract_reasoning_text(self, text: str) -> None: def extract_reasoning_text(self, text: str) -> None:
""" """
+7 -10
View File
@@ -181,7 +181,6 @@ class BrowserAgent(Agent):
animate_thinking("Thinking...", color="status") animate_thinking("Thinking...", color="status")
self.memory.push('user', prompt) self.memory.push('user', prompt)
answer, reasoning = await self.llm_request() answer, reasoning = await self.llm_request()
self.last_reasoning = reasoning
if show_reasoning: if show_reasoning:
pretty_print(reasoning, color="failure") pretty_print(reasoning, color="failure")
pretty_print(answer, color="output") pretty_print(answer, color="output")
@@ -244,10 +243,10 @@ class BrowserAgent(Agent):
self.logger.warning("No link selected.") self.logger.warning("No link selected.")
return None return None
def get_page_text(self, limit_to_model_ctx = False) -> str: def get_page_text(self, compression = False) -> str:
"""Get the text content of the current page.""" """Get the text content of the current page."""
page_text = self.browser.get_text() page_text = self.browser.get_text()
if limit_to_model_ctx: if compression:
#page_text = self.memory.compress_text_to_max_ctx(page_text) #page_text = self.memory.compress_text_to_max_ctx(page_text)
page_text = self.memory.trim_text_to_max_ctx(page_text) page_text = self.memory.trim_text_to_max_ctx(page_text)
return page_text return page_text
@@ -350,13 +349,11 @@ class BrowserAgent(Agent):
self.show_search_results(search_result) self.show_search_results(search_result)
prompt = self.make_newsearch_prompt(user_prompt, search_result) prompt = self.make_newsearch_prompt(user_prompt, search_result)
unvisited = [None] unvisited = [None]
while not complete and len(unvisited) > 0 and not self.stop: while not complete and len(unvisited) > 0:
self.memory.clear() self.memory.clear()
unvisited = self.select_unvisited(search_result) unvisited = self.select_unvisited(search_result)
answer, reasoning = await self.llm_decide(prompt, show_reasoning = False) answer, reasoning = await self.llm_decide(prompt, show_reasoning = False)
if self.stop:
pretty_print(f"Requested stop.", color="failure")
break
if self.last_answer == answer: if self.last_answer == answer:
prompt = self.stuck_prompt(user_prompt, unvisited) prompt = self.stuck_prompt(user_prompt, unvisited)
continue continue
@@ -368,13 +365,13 @@ class BrowserAgent(Agent):
self.status_message = "Filling web form..." self.status_message = "Filling web form..."
pretty_print(f"Filling inputs form...", color="status") pretty_print(f"Filling inputs form...", color="status")
fill_success = self.browser.fill_form(extracted_form) fill_success = self.browser.fill_form(extracted_form)
page_text = self.get_page_text(limit_to_model_ctx=True) page_text = self.get_page_text()
answer = self.handle_update_prompt(user_prompt, page_text, fill_success) answer = self.handle_update_prompt(user_prompt, page_text, fill_success)
answer, reasoning = await self.llm_decide(prompt) answer, reasoning = await self.llm_decide(prompt)
if Action.FORM_FILLED.value in answer: if Action.FORM_FILLED.value in answer:
pretty_print(f"Filled form. Handling page update.", color="status") pretty_print(f"Filled form. Handling page update.", color="status")
page_text = self.get_page_text(limit_to_model_ctx=True) page_text = self.get_page_text()
self.navigable_links = self.browser.get_navigable() self.navigable_links = self.browser.get_navigable()
prompt = self.make_navigation_prompt(user_prompt, page_text) prompt = self.make_navigation_prompt(user_prompt, page_text)
continue continue
@@ -410,7 +407,7 @@ class BrowserAgent(Agent):
prompt = self.make_newsearch_prompt(user_prompt, unvisited) prompt = self.make_newsearch_prompt(user_prompt, unvisited)
continue continue
self.current_page = link self.current_page = link
page_text = self.get_page_text(limit_to_model_ctx=True) page_text = self.get_page_text()
self.navigable_links = self.browser.get_navigable() self.navigable_links = self.browser.get_navigable()
prompt = self.make_navigation_prompt(user_prompt, page_text) prompt = self.make_navigation_prompt(user_prompt, page_text)
self.status_message = "Navigating..." self.status_message = "Navigating..."
+1 -3
View File
@@ -51,12 +51,10 @@ class CoderAgent(Agent):
self.memory.push('user', prompt) self.memory.push('user', prompt)
clarify_trigger = "REQUEST_CLARIFICATION" clarify_trigger = "REQUEST_CLARIFICATION"
while attempt < max_attempts and not self.stop: while attempt < max_attempts:
print("Stopped?", self.stop)
animate_thinking("Thinking...", color="status") animate_thinking("Thinking...", color="status")
await self.wait_message(speech_module) await self.wait_message(speech_module)
answer, reasoning = await self.llm_request() answer, reasoning = await self.llm_request()
self.last_reasoning = reasoning
if clarify_trigger in answer: if clarify_trigger in answer:
self.last_answer = answer self.last_answer = answer
await asyncio.sleep(0) await asyncio.sleep(0)
+1 -2
View File
@@ -28,11 +28,10 @@ class FileAgent(Agent):
exec_success = False exec_success = False
prompt += f"\nYou must work in directory: {self.work_dir}" prompt += f"\nYou must work in directory: {self.work_dir}"
self.memory.push('user', prompt) self.memory.push('user', prompt)
while exec_success is False and not self.stop: while exec_success is False:
await self.wait_message(speech_module) await self.wait_message(speech_module)
animate_thinking("Thinking...", color="status") animate_thinking("Thinking...", color="status")
answer, reasoning = await self.llm_request() answer, reasoning = await self.llm_request()
self.last_reasoning = reasoning
exec_success, _ = self.execute_modules(answer) exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer) answer = self.remove_blocks(answer)
self.last_answer = answer self.last_answer = answer
+2 -1
View File
@@ -35,7 +35,8 @@ class McpAgent(Agent):
""" """
api_key_mcp_finder = os.getenv("MCP_FINDER_API_KEY") api_key_mcp_finder = os.getenv("MCP_FINDER_API_KEY")
if not api_key_mcp_finder or api_key_mcp_finder == "": if not api_key_mcp_finder or api_key_mcp_finder == "":
pretty_print("MCP Finder disabled.", color="warning") pretty_print("MCP Finder API key not found. Please set the MCP_FINDER_API_KEY environment variable.", color="failure")
pretty_print("MCP Finder disabled.", color="failure")
self.enabled = False self.enabled = False
return { return {
"mcp_finder": api_key_mcp_finder "mcp_finder": api_key_mcp_finder
+3 -15
View File
@@ -83,15 +83,11 @@ class PlannerAgent(Agent):
self.logger.warning(f"Agent {task['agent']} does not exist.") self.logger.warning(f"Agent {task['agent']} does not exist.")
pretty_print(f"Agent {task['agent']} does not exist.", color="warning") pretty_print(f"Agent {task['agent']} does not exist.", color="warning")
return [] return []
try:
agent = { agent = {
'agent': task['agent'], 'agent': task['agent'],
'id': task['id'], 'id': task['id'],
'task': task['task'] 'task': task['task']
} }
except:
self.logger.warning("Missing field in json plan.")
return []
self.logger.info(f"Created agent {task['agent']} with task: {task['task']}") self.logger.info(f"Created agent {task['agent']} with task: {task['task']}")
if 'need' in task: if 'need' in task:
self.logger.info(f"Agent {task['agent']} was given info:\n {task['need']}") self.logger.info(f"Agent {task['agent']} was given info:\n {task['need']}")
@@ -160,7 +156,6 @@ class PlannerAgent(Agent):
return [] return []
agents_tasks = self.parse_agent_tasks(answer) agents_tasks = self.parse_agent_tasks(answer)
if agents_tasks == []: if agents_tasks == []:
self.show_plan(agents_tasks, answer)
prompt = f"Failed to parse the tasks. Please write down your task followed by a json plan within ```json. Do not ask for clarification.\n" prompt = f"Failed to parse the tasks. Please write down your task followed by a json plan within ```json. Do not ask for clarification.\n"
pretty_print("Failed to make plan. Retrying...", color="warning") pretty_print("Failed to make plan. Retrying...", color="warning")
continue continue
@@ -183,11 +178,7 @@ class PlannerAgent(Agent):
last_agent_work = agents_work_result[id] last_agent_work = agents_work_result[id]
tool_success_str = "success" if success else "failure" tool_success_str = "success" if success else "failure"
pretty_print(f"Agent {id} work {tool_success_str}.", color="success" if success else "failure") pretty_print(f"Agent {id} work {tool_success_str}.", color="success" if success else "failure")
try: if int(id) == len(agents_tasks):
id_int = int(id)
except Exception as e:
return agents_tasks
if id_int == len(agents_tasks):
next_task = "No task follow, this was the last step. If it failed add a task to recover." next_task = "No task follow, this was the last step. If it failed add a task to recover."
else: else:
next_task = f"Next task is: {agents_tasks[int(id)][0]}." next_task = f"Next task is: {agents_tasks[int(id)][0]}."
@@ -230,9 +221,8 @@ class PlannerAgent(Agent):
agent_prompt = self.make_prompt(task['task'], required_infos) agent_prompt = self.make_prompt(task['task'], required_infos)
pretty_print(f"Agent {task['agent']} started working...", color="status") pretty_print(f"Agent {task['agent']} started working...", color="status")
self.logger.info(f"Agent {task['agent']} started working on {task['task']}.") self.logger.info(f"Agent {task['agent']} started working on {task['task']}.")
answer, reasoning = await self.agents[task['agent'].lower()].process(agent_prompt, None) answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None)
self.last_answer = answer self.last_answer = answer
self.last_reasoning = reasoning
self.blocks_result = self.agents[task['agent'].lower()].blocks_result self.blocks_result = self.agents[task['agent'].lower()].blocks_result
agent_answer = self.agents[task['agent'].lower()].raw_answer_blocks(answer) agent_answer = self.agents[task['agent'].lower()].raw_answer_blocks(answer)
success = self.agents[task['agent'].lower()].get_success success = self.agents[task['agent'].lower()].get_success
@@ -267,7 +257,7 @@ class PlannerAgent(Agent):
return "Failed to parse the tasks.", "" return "Failed to parse the tasks.", ""
i = 0 i = 0
steps = len(agents_tasks) steps = len(agents_tasks)
while i < steps and not self.stop: while i < steps:
task_name, task = agents_tasks[i][0], agents_tasks[i][1] task_name, task = agents_tasks[i][0], agents_tasks[i][1]
self.status_message = "Starting agents..." self.status_message = "Starting agents..."
pretty_print(f"I will {task_name}.", color="info") pretty_print(f"I will {task_name}.", color="info")
@@ -281,8 +271,6 @@ class PlannerAgent(Agent):
answer, success = await self.start_agent_process(task, required_infos) answer, success = await self.start_agent_process(task, required_infos)
except Exception as e: except Exception as e:
raise e raise e
if self.stop:
pretty_print(f"Requested stop.", color="failure")
agents_work_result[task['id']] = answer agents_work_result[task['id']] = answer
agents_tasks = await self.update_plan(goal, agents_tasks, agents_work_result, task['id'], success) agents_tasks = await self.update_plan(goal, agents_tasks, agents_work_result, task['id'], success)
steps = len(agents_tasks) steps = len(agents_tasks)
+23 -87
View File
@@ -45,7 +45,7 @@ def get_chrome_path() -> str:
paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"] paths = ["/usr/bin/google-chrome", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/opt/chrome/chrome", "/usr/local/bin/chrome"]
for path in paths: for path in paths:
if os.path.exists(path) and os.access(path, os.X_OK): if os.path.exists(path) and os.access(path, os.X_OK): # Check if executable
return path return path
print("Looking for Google Chrome in these locations failed:") print("Looking for Google Chrome in these locations failed:")
print('\n'.join(paths)) print('\n'.join(paths))
@@ -62,9 +62,9 @@ def get_chrome_path() -> str:
def get_random_user_agent() -> str: def get_random_user_agent() -> str:
"""Get a random user agent string with associated vendor.""" """Get a random user agent string with associated vendor."""
user_agents = [ user_agents = [
{"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Google Inc."}, {"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", "vendor": "Google Inc."},
{"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Apple Inc."}, {"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", "vendor": "Apple Inc."},
{"ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", "vendor": "Google Inc."}, {"ua": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", "vendor": ""},
] ]
return random.choice(user_agents) return random.choice(user_agents)
@@ -91,7 +91,7 @@ def bypass_ssl() -> str:
""" """
This is a fallback for stealth mode to bypass SSL verification. Which can fail on some setup. This is a fallback for stealth mode to bypass SSL verification. Which can fail on some setup.
""" """
pretty_print("Bypassing SSL verification issues, we strongly advice you update your certifi SSL certificate.", color="warning") pretty_print("This is a workaround for SSL issues but upsafe we strongly advice you update your certifi SSL certificate.", color="warning")
ssl._create_default_https_context = ssl._create_unverified_context ssl._create_default_https_context = ssl._create_unverified_context
def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome:
@@ -107,10 +107,11 @@ def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome:
pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure") pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure")
raise e raise e
raise e raise e
# hide webdriver flag
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
return driver return driver
def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome: def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx") -> webdriver.Chrome:
"""Create a Chrome WebDriver with specified options.""" """Create a Chrome WebDriver with specified options."""
chrome_options = Options() chrome_options = Options()
chrome_path = get_chrome_path() chrome_path = get_chrome_path()
@@ -125,21 +126,19 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
chrome_options.add_argument("--disable-webgl") chrome_options.add_argument("--disable-webgl")
user_data_dir = tempfile.mkdtemp() user_data_dir = tempfile.mkdtemp()
user_agent = get_random_user_agent() user_agent = get_random_user_agent()
width, height = (1920, 1080)
chrome_options.add_argument(f"--user-data-dir={user_data_dir}") chrome_options.add_argument(f"--user-data-dir={user_data_dir}")
chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9")
chrome_options.add_argument("--timezone=Europe/Paris")
chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--mute-audio") chrome_options.add_argument("--mute-audio")
chrome_options.add_argument("--disable-notifications") chrome_options.add_argument("--disable-notifications")
chrome_options.add_argument("--autoplay-policy=user-gesture-required") chrome_options.add_argument("--autoplay-policy=user-gesture-required")
chrome_options.add_argument("--disable-features=SitePerProcess,IsolateOrigins")
chrome_options.add_argument("--enable-features=NetworkService,NetworkServiceInProcess")
chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_argument(f'user-agent={user_agent["ua"]}') chrome_options.add_argument(f'user-agent={user_agent["ua"]}')
resolutions = [(1920, 1080), (1366, 768), (1440, 900)]
width, height = random.choice(resolutions)
chrome_options.add_argument(f'--window-size={width},{height}') chrome_options.add_argument(f'--window-size={width},{height}')
if not stealth_mode: if not stealth_mode:
# crx file can't be installed in stealth mode
if not os.path.exists(crx_path): if not os.path.exists(crx_path):
pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure") pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure")
else: else:
@@ -155,31 +154,16 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx
stealth(driver, stealth(driver,
languages=["en-US", "en"], languages=["en-US", "en"],
vendor=user_agent["vendor"], vendor=user_agent["vendor"],
platform="Win64" if "windows" in user_agent["ua"].lower() else "MacIntel" if "mac" in user_agent["ua"].lower() else "Linux x86_64", platform="Win64" if "Windows" in user_agent["ua"] else "MacIntel" if "Macintosh" in user_agent["ua"] else "Linux x86_64",
webgl_vendor="Intel Inc.", webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine", renderer="Intel Iris OpenGL Engine",
fix_hairline=True, fix_hairline=True,
) )
return driver return driver
security_prefs = { security_prefs = {
"profile.default_content_setting_values.geolocation": 0, "profile.default_content_setting_values.media_stream": 2,
"profile.default_content_setting_values.notifications": 0, "profile.default_content_setting_values.geolocation": 2,
"profile.default_content_setting_values.camera": 0,
"profile.default_content_setting_values.microphone": 0,
"profile.default_content_setting_values.midi_sysex": 0,
"profile.default_content_setting_values.clipboard": 0,
"profile.default_content_setting_values.media_stream": 0,
"profile.default_content_setting_values.background_sync": 0,
"profile.default_content_setting_values.sensors": 0,
"profile.default_content_setting_values.accessibility_events": 0,
"safebrowsing.enabled": True, "safebrowsing.enabled": True,
"credentials_enable_service": False,
"profile.password_manager_enabled": False,
"webkit.webprefs.accelerated_2d_canvas_enabled": True,
"webkit.webprefs.force_dark_mode_enabled": False,
"webkit.webprefs.accelerated_2d_canvas_msaa_sample_count": 4,
"enable_webgl": True,
"enable_webgl2_compute_context": True
} }
chrome_options.add_experimental_option("prefs", security_prefs) chrome_options.add_experimental_option("prefs", security_prefs)
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
@@ -200,17 +184,12 @@ class Browser:
except Exception as e: except Exception as e:
raise Exception(f"Failed to initialize browser: {str(e)}") raise Exception(f"Failed to initialize browser: {str(e)}")
self.setup_tabs() self.setup_tabs()
self.patch_browser_fingerprint()
if anticaptcha_manual_install: if anticaptcha_manual_install:
self.load_anticatpcha_manually() self.load_anticatpcha_manually()
def setup_tabs(self): def setup_tabs(self):
self.tabs = self.driver.window_handles self.tabs = self.driver.window_handles
try:
self.driver.get("https://www.google.com") self.driver.get("https://www.google.com")
except Exception as e:
self.logger.log(f"Failed to setup initial tab:" + str(e))
pass
self.screenshot() self.screenshot()
def switch_control_tab(self): def switch_control_tab(self):
@@ -219,40 +198,14 @@ class Browser:
def load_anticatpcha_manually(self): def load_anticatpcha_manually(self):
pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning") pretty_print("You might want to install the AntiCaptcha extension for captchas.", color="warning")
try:
self.driver.get(self.anticaptcha) self.driver.get(self.anticaptcha)
except Exception as e:
self.logger.log(f"Failed to setup initial tab:" + str(e))
pass
def human_move(element):
actions = ActionChains(driver)
x_offset = random.randint(-5,5)
for _ in range(random.randint(2,5)):
actions.move_by_offset(x_offset, random.randint(-2,2))
actions.pause(random.uniform(0.1,0.3))
actions.click().perform()
def human_scroll(self):
for _ in range(random.randint(1, 3)):
scroll_pixels = random.randint(150, 1200)
self.driver.execute_script(f"window.scrollBy(0, {scroll_pixels});")
time.sleep(random.uniform(0.5, 2.0))
if random.random() < 0.4:
self.driver.execute_script(f"window.scrollBy(0, -{random.randint(50, 300)});")
time.sleep(random.uniform(0.3, 1.0))
def patch_browser_fingerprint(self) -> None:
script = self.load_js("spoofing.js")
self.driver.execute_script(script)
def go_to(self, url:str) -> bool: def go_to(self, url:str) -> bool:
"""Navigate to a specified URL.""" """Navigate to a specified URL."""
time.sleep(random.uniform(0.4, 2.5)) time.sleep(random.uniform(0.4, 2.5)) # more human behavior
try: try:
initial_handles = self.driver.window_handles initial_handles = self.driver.window_handles
self.driver.get(url) self.driver.get(url)
time.sleep(random.uniform(0.01, 0.3))
try: try:
wait = WebDriverWait(self.driver, timeout=10) wait = WebDriverWait(self.driver, timeout=10)
wait.until( wait.until(
@@ -264,8 +217,6 @@ class Browser:
except TimeoutException: except TimeoutException:
self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'") self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'")
self.apply_web_safety() self.apply_web_safety()
time.sleep(random.uniform(0.01, 0.2))
self.human_scroll()
self.logger.log(f"Navigated to: {url}") self.logger.log(f"Navigated to: {url}")
return True return True
except TimeoutException as e: except TimeoutException as e:
@@ -665,24 +616,19 @@ class Browser:
return self.screenshot_folder + "/updated_screen.png" return self.screenshot_folder + "/updated_screen.png"
def screenshot(self, filename:str = 'updated_screen.png') -> bool: def screenshot(self, filename:str = 'updated_screen.png') -> bool:
"""Take a screenshot of the current page, attempt to capture the full page by zooming out.""" """Take a screenshot of the current page."""
self.logger.info("Taking full page screenshot...") self.logger.info("Taking screenshot...")
time.sleep(0.1) time.sleep(0.1)
try: try:
original_zoom = self.driver.execute_script("return document.body.style.zoom || 1;")
self.driver.execute_script("document.body.style.zoom='75%'")
time.sleep(0.1)
path = os.path.join(self.screenshot_folder, filename) path = os.path.join(self.screenshot_folder, filename)
if not os.path.exists(self.screenshot_folder): if not os.path.exists(self.screenshot_folder):
os.makedirs(self.screenshot_folder) os.makedirs(self.screenshot_folder)
self.driver.save_screenshot(path) self.driver.save_screenshot(path)
self.logger.info(f"Full page screenshot saved as {filename}") self.logger.info(f"Screenshot saved as {filename}")
except Exception as e:
self.logger.error(f"Error taking full page screenshot: {str(e)}")
return False
finally:
self.driver.execute_script(f"document.body.style.zoom='1'")
return True return True
except Exception as e:
self.logger.error(f"Error taking screenshot: {str(e)}")
return False
def apply_web_safety(self): def apply_web_safety(self):
""" """
@@ -693,27 +639,17 @@ class Browser:
input_elements = self.driver.execute_script(script) input_elements = self.driver.execute_script(script)
if __name__ == "__main__": if __name__ == "__main__":
driver = create_driver(headless=False, stealth_mode=True, crx_path="../crx/nopecha.crx") driver = create_driver(headless=False, stealth_mode=True)
browser = Browser(driver, anticaptcha_manual_install=True) browser = Browser(driver, anticaptcha_manual_install=True)
input("press enter to continue") input("press enter to continue")
print("AntiCaptcha / Form Test") print("AntiCaptcha / Form Test")
browser.go_to("https://www.google.com/recaptcha/api2/demo") #browser.go_to("https://www.browserscan.net/bot-detection")
time.sleep(50)
browser.go_to("https://bot.sannysoft.com")
time.sleep(5)
#txt = browser.get_text() #txt = browser.get_text()
#browser.go_to("https://www.google.com/recaptcha/api2/demo")
browser.go_to("https://home.openweathermap.org/users/sign_up") browser.go_to("https://home.openweathermap.org/users/sign_up")
inputs_visible = browser.get_form_inputs() inputs_visible = browser.get_form_inputs()
print("inputs:", inputs_visible) print("inputs:", inputs_visible)
#inputs_fill = ['[q](checked)', '[q](checked)', '[user[username]](mlg)', '[user[email]](mlg.fcu@gmail.com)', '[user[password]](placeholder_P@ssw0rd123)', '[user[password_confirmation]](placeholder_P@ssw0rd123)'] #inputs_fill = ['[q](checked)', '[q](checked)', '[user[username]](mlg)', '[user[email]](mlg.fcu@gmail.com)', '[user[password]](placeholder_P@ssw0rd123)', '[user[password_confirmation]](placeholder_P@ssw0rd123)']
#browser.fill_form(inputs_fill) #browser.fill_form(inputs_fill)
input("press enter to exit") input("press enter to exit")
# Test sites for browser fingerprinting and captcha
# https://nowsecure.nl/
# https://bot.sannysoft.com
# https://browserleaks.com/
# https://bot.incolumitas.com/
# https://fingerprintjs.github.io/fingerprintjs/
# https://antoinevastel.com/bots/
+1 -2
View File
@@ -22,7 +22,6 @@ class Interaction:
self.current_agent = None self.current_agent = None
self.last_query = None self.last_query = None
self.last_answer = None self.last_answer = None
self.last_reasoning = None
self.agents = agents self.agents = agents
self.tts_enabled = tts_enabled self.tts_enabled = tts_enabled
self.stt_enabled = stt_enabled self.stt_enabled = stt_enabled
@@ -159,7 +158,7 @@ class Interaction:
tmp = self.last_answer tmp = self.last_answer
self.current_agent = agent self.current_agent = agent
self.is_generating = True self.is_generating = True
self.last_answer, self.last_reasoning = await agent.process(self.last_query, self.speech) self.last_answer, _ = await agent.process(self.last_query, self.speech)
self.is_generating = False self.is_generating = False
if push_last_agent_memory: if push_last_agent_memory:
self.current_agent.memory.push('user', self.last_query) self.current_agent.memory.push('user', self.last_query)
+32 -97
View File
@@ -1,26 +1,27 @@
import os import os
import platform
import socket
import subprocess
import time import time
from urllib.parse import urlparse import ollama
from ollama import chat
import httpx
import requests import requests
from dotenv import load_dotenv import subprocess
from ollama import Client as OllamaClient import ipaddress
import httpx
import socket
import platform
from urllib.parse import urlparse
from dotenv import load_dotenv, set_key
from openai import OpenAI from openai import OpenAI
from typing import List, Tuple, Type, Dict
from sources.logger import Logger
from sources.utility import pretty_print, animate_thinking from sources.utility import pretty_print, animate_thinking
from sources.logger import Logger
class Provider: class Provider:
def __init__(self, provider_name, model, server_address="127.0.0.1:5000", is_local=False): def __init__(self, provider_name, model, server_address = "127.0.0.1:5000", is_local=False):
self.provider_name = provider_name.lower() self.provider_name = provider_name.lower()
self.model = model self.model = model
self.is_local = is_local self.is_local = is_local
self.server_ip = server_address self.server_ip = server_address
self.server_address = server_address
self.available_providers = { self.available_providers = {
"ollama": self.ollama_fn, "ollama": self.ollama_fn,
"server": self.server_fn, "server": self.server_fn,
@@ -31,12 +32,11 @@ class Provider:
"deepseek": self.deepseek_fn, "deepseek": self.deepseek_fn,
"together": self.together_fn, "together": self.together_fn,
"dsk_deepseek": self.dsk_deepseek, "dsk_deepseek": self.dsk_deepseek,
"openrouter": self.openrouter_fn,
"test": self.test_fn "test": self.test_fn
} }
self.logger = Logger("provider.log") self.logger = Logger("provider.log")
self.api_key = None self.api_key = None
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google", "openrouter"] self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google"]
if self.provider_name not in self.available_providers: if self.provider_name not in self.available_providers:
raise ValueError(f"Unknown provider: {provider_name}") raise ValueError(f"Unknown provider: {provider_name}")
if self.provider_name in self.unsafe_providers and self.is_local == False: if self.provider_name in self.unsafe_providers and self.is_local == False:
@@ -57,7 +57,7 @@ class Provider:
exit(1) exit(1)
return api_key return api_key
def respond(self, history, verbose=True): def respond(self, history, verbose = True):
""" """
Use the choosen provider to generate text. Use the choosen provider to generate text.
""" """
@@ -73,8 +73,7 @@ class Provider:
except AttributeError as e: except AttributeError as e:
raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?") raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?")
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
raise ModuleNotFoundError( raise ModuleNotFoundError(f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
except Exception as e: except Exception as e:
if "try again later" in str(e).lower(): if "try again later" in str(e).lower():
return f"{self.provider_name} server is overloaded. Please try again later." return f"{self.provider_name} server is overloaded. Please try again later."
@@ -107,7 +106,8 @@ class Provider:
except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e: except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
return False return False
def server_fn(self, history, verbose=False):
def server_fn(self, history, verbose = False):
""" """
Use a remote server with LLM to generate text. Use a remote server with LLM to generate text.
""" """
@@ -141,45 +141,36 @@ class Provider:
pretty_print(f"An error occurred: {str(e)}", color="failure") pretty_print(f"An error occurred: {str(e)}", color="failure")
break break
except KeyError as e: except KeyError as e:
raise Exception( raise Exception(f"{str(e)}\nError occured with server route. Are you using the correct address for the config.ini provider?") from e
f"{str(e)}\nError occured with server route. Are you using the correct address for the config.ini provider?") from e
except Exception as e: except Exception as e:
raise e raise e
return thought return thought
def ollama_fn(self, history, verbose=False): def ollama_fn(self, history, verbose = False):
""" """
Use local or remote Ollama server to generate text. Use local ollama server to generate text.
""" """
thought = "" thought = ""
host = "http://localhost:11434" if self.is_local else f"http://{self.server_address}"
client = OllamaClient(host=host)
try: try:
stream = client.chat( stream = chat(
model=self.model, model=self.model,
messages=history, messages=history,
stream=True, stream=True,
) )
for chunk in stream: for chunk in stream:
if verbose: if verbose:
print(chunk["message"]["content"], end="", flush=True) print(chunk['message']['content'], end='', flush=True)
thought += chunk["message"]["content"] thought += chunk['message']['content']
except httpx.ConnectError as e: except httpx.ConnectError as e:
raise Exception( raise Exception("\nOllama connection failed. provider should not be set to ollama if server address is not localhost") from e
f"\nOllama connection failed at {host}. Check if the server is running." except ollama.ResponseError as e:
) from e if e.status_code == 404:
except Exception as e:
if hasattr(e, 'status_code') and e.status_code == 404:
animate_thinking(f"Downloading {self.model}...") animate_thinking(f"Downloading {self.model}...")
client.pull(self.model) ollama.pull(self.model)
self.ollama_fn(history, verbose) self.ollama_fn(history, verbose)
if "refused" in str(e).lower(): if "refused" in str(e).lower():
raise Exception( raise Exception("Ollama connection failed. is the server running ?") from e
f"Ollama connection refused at {host}. Is the server running?"
) from e
raise e raise e
return thought return thought
def huggingface_fn(self, history, verbose=False): def huggingface_fn(self, history, verbose=False):
@@ -222,38 +213,6 @@ class Provider:
except Exception as e: except Exception as e:
raise Exception(f"OpenAI API error: {str(e)}") from e raise Exception(f"OpenAI API error: {str(e)}") from e
def anthropic_fn(self, history, verbose=False):
"""
Use Anthropic to generate text.
"""
from anthropic import Anthropic
client = Anthropic(api_key=self.api_key)
system_message = None
messages = []
for message in history:
clean_message = {'role': message['role'], 'content': message['content']}
if message['role'] == 'system':
system_message = message['content']
else:
messages.append(clean_message)
try:
response = client.messages.create(
model=self.model,
max_tokens=1024,
messages=messages,
system=system_message
)
if response is None:
raise Exception("Anthropic response is empty.")
thought = response.content[0].text
if verbose:
print(thought)
return thought
except Exception as e:
raise Exception(f"Anthropic API error: {str(e)}") from e
def google_fn(self, history, verbose=False): def google_fn(self, history, verbose=False):
""" """
Use google gemini to generate text. Use google gemini to generate text.
@@ -320,7 +279,7 @@ class Provider:
except Exception as e: except Exception as e:
raise Exception(f"Deepseek API error: {str(e)}") from e raise Exception(f"Deepseek API error: {str(e)}") from e
def lm_studio_fn(self, history, verbose=False): def lm_studio_fn(self, history, verbose = False):
""" """
Use local lm-studio server to generate text. Use local lm-studio server to generate text.
lm studio use endpoint /v1/chat/completions not /chat/completions like openai lm studio use endpoint /v1/chat/completions not /chat/completions like openai
@@ -345,30 +304,7 @@ class Provider:
raise Exception(f"An error occurred: {str(e)}") from e raise Exception(f"An error occurred: {str(e)}") from e
return thought return thought
def openrouter_fn(self, history, verbose=False): def dsk_deepseek(self, history, verbose = False):
"""
Use OpenRouter API to generate text.
"""
client = OpenAI(api_key=self.api_key, base_url="https://openrouter.ai/api/v1")
if self.is_local:
# This case should ideally not be reached if unsafe_providers is set correctly
# and is_local is False in config for openrouter
raise Exception("OpenRouter is not available for local use. Change config.ini")
try:
response = client.chat.completions.create(
model=self.model,
messages=history,
)
if response is None:
raise Exception("OpenRouter response is empty.")
thought = response.choices[0].message.content
if verbose:
print(thought)
return thought
except Exception as e:
raise Exception(f"OpenRouter API error: {str(e)}") from e
def dsk_deepseek(self, history, verbose=False):
""" """
Use: xtekky/deepseek4free Use: xtekky/deepseek4free
For free api. Api key should be set to DSK_DEEPSEEK_API_KEY For free api. Api key should be set to DSK_DEEPSEEK_API_KEY
@@ -404,7 +340,7 @@ class Provider:
raise APIError(f"API error occurred: {str(e)}") from e raise APIError(f"API error occurred: {str(e)}") from e
return None return None
def test_fn(self, history, verbose=True): def test_fn(self, history, verbose = True):
""" """
This function is used to conduct tests. This function is used to conduct tests.
""" """
@@ -413,7 +349,6 @@ class Provider:
""" """
return thought return thought
if __name__ == "__main__": if __name__ == "__main__":
provider = Provider("server", "deepseek-r1:32b", " x.x.x.x:8080") provider = Provider("server", "deepseek-r1:32b", " x.x.x.x:8080")
res = provider.respond(["user", "Hello, how are you?"]) res = provider.respond(["user", "Hello, how are you?"])
+1 -3
View File
@@ -17,14 +17,12 @@ class Logger:
def create_logging(self, log_filename): def create_logging(self, log_filename):
self.logger = logging.getLogger(log_filename) self.logger = logging.getLogger(log_filename)
self.logger.setLevel(logging.DEBUG) self.logger.setLevel(logging.DEBUG)
self.logger.handlers.clear() if not self.logger.handlers:
self.logger.propagate = False
file_handler = logging.FileHandler(self.log_path) file_handler = logging.FileHandler(self.log_path)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
self.logger.addHandler(file_handler) self.logger.addHandler(file_handler)
def create_folder(self, path): def create_folder(self, path):
"""Create log dir""" """Create log dir"""
try: try:
+4 -4
View File
@@ -118,13 +118,13 @@ class Memory():
json_memory = json.load(f) json_memory = json.load(f)
except FileNotFoundError: except FileNotFoundError:
self.logger.warning(f"File not found: {path}") self.logger.warning(f"File not found: {path}")
return {} return None
except json.JSONDecodeError: except json.JSONDecodeError:
self.logger.warning(f"Error decoding JSON from file: {path}") self.logger.warning(f"Error decoding JSON from file: {path}")
return {} return None
except Exception as e: except Exception as e:
self.logger.warning(f"Error loading file {path}: {e}") self.logger.warning(f"Error loading file {path}: {e}")
return {} return None
return json_memory return json_memory
def load_memory(self, agent_type: str = "casual_agent") -> None: def load_memory(self, agent_type: str = "casual_agent") -> None:
@@ -238,7 +238,7 @@ class Memory():
if len(self.memory[i]['content']) > 1024: if len(self.memory[i]['content']) > 1024:
self.memory[i]['content'] = self.summarize(self.memory[i]['content']) self.memory[i]['content'] = self.summarize(self.memory[i]['content'])
def trim_text_to_max_ctx(self, text: str) -> str: def trip_text_to_max_ctx(self, text: str) -> str:
""" """
Truncate a text to fit within the maximum context size of the model. Truncate a text to fit within the maximum context size of the model.
""" """
-2
View File
@@ -19,7 +19,6 @@ class QueryRequest(BaseModel):
class QueryResponse(BaseModel): class QueryResponse(BaseModel):
done: str done: str
answer: str answer: str
reasoning: str
agent_name: str agent_name: str
success: str success: str
blocks: dict blocks: dict
@@ -33,7 +32,6 @@ class QueryResponse(BaseModel):
return { return {
"done": self.done, "done": self.done,
"answer": self.answer, "answer": self.answer,
"reasoning": self.reasoning,
"agent_name": self.agent_name, "agent_name": self.agent_name,
"success": self.success, "success": self.success,
"blocks": self.blocks, "blocks": self.blocks,
+3 -4
View File
@@ -8,7 +8,7 @@ if __name__ == "__main__": # if running as a script for individual testing
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from sources.tools.tools import Tools from sources.tools.tools import Tools
from sources.tools.safety import is_any_unsafe from sources.tools.safety import is_unsafe
class BashInterpreter(Tools): class BashInterpreter(Tools):
""" """
@@ -43,9 +43,9 @@ class BashInterpreter(Tools):
for command in commands: for command in commands:
command = f"cd {self.work_dir} && {command}" command = f"cd {self.work_dir} && {command}"
command = command.replace('\n', '') command = command.replace('\n', '')
if self.safe_mode and is_any_unsafe(commands): if self.safe_mode and is_unsafe(commands):
print(f"Unsafe command rejected: {command}") print(f"Unsafe command rejected: {command}")
return "\nUnsafe command: {command}. Execution aborted. This is beyond allowed capabilities report to user." return "Unsafe command detected, execution aborted."
if self.language_bash_attempt(command) and self.allow_language_exec_bash == False: if self.language_bash_attempt(command) and self.allow_language_exec_bash == False:
continue continue
try: try:
@@ -100,7 +100,6 @@ class BashInterpreter(Tools):
r"not permitted", r"not permitted",
r"not installed", r"not installed",
r"not found", r"not found",
r"aborted",
r"no such", r"no such",
r"too many", r"too many",
r"too few", r"too few",
+1 -10
View File
@@ -31,7 +31,7 @@ unsafe_commands_unix = [
"route" # Routing table management "route" # Routing table management
"--force", # Force flag for many commands "--force", # Force flag for many commands
"rebase", # Rebase git repository "rebase", # Rebase git repository
"git" # Git commands "git ." # Git commands
] ]
unsafe_commands_windows = [ unsafe_commands_windows = [
@@ -66,15 +66,6 @@ unsafe_commands_windows = [
"bootcfg" "bootcfg"
] ]
def is_any_unsafe(cmds):
"""
check if any bash command is unsafe.
"""
for cmd in cmds:
if is_unsafe(cmd):
return True
return False
def is_unsafe(cmd): def is_unsafe(cmd):
""" """
check if a bash command is unsafe. check if a bash command is unsafe.
+3 -1
View File
@@ -21,5 +21,7 @@ window.fetch = function() {
console.log('Blocked fetch request'); console.log('Blocked fetch request');
return Promise.reject('Blocked'); return Promise.reject('Blocked');
}; };
// Block annoying dialogs
window.alert = function() {};
window.confirm = function() { return false; };
window.prompt = function() { return null; }; window.prompt = function() { return null; };
-126
View File
@@ -1,126 +0,0 @@
// Core automation masking
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
window.RTCPeerConnection = undefined;
window.webkitRTCPeerConnection = undefined;
window.mozRTCPeerConnection = undefined;
window.Notification = class Notification {
constructor(title, options = {}) {
this.title = title;
this.options = options;
}
static permission = 'granted';
static requestPermission = () => Promise.resolve('granted');
close() {}
onclick = null;
onerror = null;
onclose = null;
onshow = null;
};
Object.keys(window).forEach((key) => {
if (key.includes("webdriver") || key.includes("selenium") || key.includes("driver")) {
delete window[key];
}
});
// Randomize plugins
const pluginsList = [
{type: 'application/x-google-chrome-pdf', description: 'Portable Document Format', filename: 'internal-pdf-viewer', name: 'Chrome PDF Plugin'},
{type: 'application/x-nacl', description: 'Native Client Executable', filename: 'internal-nacl-plugin', name: 'Native Client'},
{type: 'application/x-ppapi-widevine-cdm', description: 'Widevine Content Decryption Module', filename: 'widevinecdm', name: 'Widevine CDM'}
];
Object.defineProperty(navigator, 'plugins', {
get: () => pluginsList.slice(0, Math.floor(Math.random() * pluginsList.length) + 1)
});
// Font spoofing
const fontList = ['Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana'];
Object.defineProperty(document, 'fonts', {
value: {
add: function() {},
check: function(font) { return fontList.includes(font.split(' ').slice(-1)[0]); },
delete: function() {},
forEach: function(cb) { fontList.forEach(f => cb(f)); },
has: function(font) { return fontList.includes(font.split(' ').slice(-1)[0]); },
keys: function() { return fontList; },
size: fontList.length
}
});
// Canvas fingerprint spoofing
HTMLCanvasElement.prototype.toDataURL = function() {
const ctx = this.getContext('2d');
// Add varied noise to avoid consistent fingerprints
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgba(${Math.random() * 5}, ${Math.random() * 5}, ${Math.random() * 5}, 0.005)`;
ctx.fillRect(Math.random() * this.width, Math.random() * this.height, 1, 1);
}
return originalToDataURL.apply(this, arguments);
};
const [w, h] = [1920, 1080];
Object.defineProperty(window, 'screen', {
value: {
width: w,
height: h,
availWidth: w - 20,
availHeight: h - 100,
colorDepth: 24,
pixelDepth: 24
}
});
// ===== WebGL Consistency =====
const os = navigator.userAgent.includes('Windows') ? 'Windows' : 'Mac';
const webGLParams = {
'Windows': {
37445: 'Google Inc. (NVIDIA)', // VENDOR
37446: 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060)', // RENDERER
36349: 'NVIDIA Corporation', // UNMASKED_VENDOR_WEBGL
37444: 'NVIDIA GeForce RTX 3060', // UNMASKED_RENDERER_WEBGL
35661: 'WebGL 2.0' // VERSION
},
'Mac': {
37445: 'Apple Inc.',
37446: 'Apple M1 Pro',
36349: 'Apple',
37444: 'Apple M1 Pro',
35661: 'WebGL 2.0 (Metal)'
}
};
// replace WebGL parameters
WebGLRenderingContext.prototype.getParameter = function(parameter) {
return webGLParams[os][parameter] || getParameter.call(this, parameter);
};
// Performance API spoofing
if ('performance' in window) {
Object.defineProperty(performance, 'memory', {
value: {
jsHeapSizeLimit: 4294705152,
totalJSHeapSize: 78365432,
usedJSHeapSize: 46543210
},
configurable: true
});
}
const originalCreate = window.AudioContext || window.webkitAudioContext;
window.AudioContext = window.webkitAudioContext = function() {
const context = new originalCreate();
const analyser = context.createAnalyser();
analyser.fake = true; // Mark as spoofed
// Spoof common methods
analyser.getFloatFrequencyData = () => new Float32Array(1024).fill(Math.random() * -100);
return context;
};
+3
View File
@@ -1,5 +1,8 @@
@echo off @echo off
REM Up the provider in windows
start ollama serve
docker-compose up docker-compose up
if %ERRORLEVEL% neq 0 ( if %ERRORLEVEL% neq 0 (
echo Error: Failed to start containers. Check Docker logs with 'docker compose logs'. echo Error: Failed to start containers. Check Docker logs with 'docker compose logs'.
-230
View File
@@ -1,230 +0,0 @@
import unittest
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from sources.tools.tools import Tools
class TestToolsParsing(unittest.TestCase):
"""
Test suite for the Tools class parsing functionality, specifically the load_exec_block method.
This method is responsible for extracting code blocks from LLM-generated text.
"""
def setUp(self):
"""Set up test fixtures before each test method."""
class TestTool(Tools):
def execute(self, blocks, safety=False):
return "test execution"
def execution_failure_check(self, output):
return False
def interpreter_feedback(self, output):
return "test feedback"
self.tool = TestTool()
self.tool.tag = "python" # Set tag for testing
def test_load_exec_block_single_block(self):
"""Test parsing a single code block from LLM text."""
llm_text = """Here's some Python code:
```python
print("Hello, World!")
x = 42
```
That's the code."""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 1)
self.assertEqual(blocks[0], '\nprint("Hello, World!")\nx = 42\n')
self.assertIsNone(save_path)
def test_load_exec_block_multiple_blocks(self):
"""Test parsing multiple code blocks from LLM text."""
llm_text = """First block:
```python
import os
print("First block")
```
Second block:
```python
import sys
print("Second block")
```
Done."""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 2)
self.assertEqual(blocks[0], '\nimport os\nprint("First block")\n')
self.assertEqual(blocks[1], '\nimport sys\nprint("Second block")\n')
self.assertIsNone(save_path)
def test_load_exec_block_with_save_path(self):
"""Test parsing code block with save path specification."""
llm_text = """```python
save_path: test_file.py
import os
print("Hello with save path")
```"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 1)
self.assertEqual(blocks[0], '\nsave_path: test_file.py\nimport os\nprint("Hello with save path")\n')
self.assertIsNone(save_path)
def test_load_exec_block_with_indentation(self):
"""Test parsing code blocks with leading whitespace/indentation."""
llm_text = """ Here's indented code:
```python
def hello():
print("Hello")
return True
```
End of code."""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 1)
expected_code = '\ndef hello():\n print("Hello")\n return True\n'
self.assertEqual(blocks[0], expected_code)
def test_load_exec_block_no_blocks(self):
"""Test parsing text with no code blocks."""
llm_text = """This is just regular text with no code blocks.
There are no python blocks here.
Just plain text."""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNone(blocks)
self.assertIsNone(save_path)
def test_load_exec_block_wrong_tag(self):
"""Test parsing text with code blocks but wrong language tag."""
llm_text = """```javascript
console.log("This is JavaScript, not Python");
```"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNone(blocks)
self.assertIsNone(save_path)
def test_load_exec_block_incomplete_block(self):
"""Test parsing text with incomplete code block (missing closing tag)."""
llm_text = """```python
print("This block has no closing tag")
x = 42"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertEqual(blocks, [])
self.assertIsNone(save_path)
def test_load_exec_block_empty_block(self):
"""Test parsing empty code block."""
llm_text = """```python
```"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 1)
self.assertEqual(blocks[0], '\n')
def test_load_exec_block_mixed_content(self):
"""Test parsing text with mixed content including code blocks."""
llm_text = """Let me help you with that task.
First, I'll import the necessary modules:
```python
import os
import sys
```
Then I'll define a function:
```python
def process_data(data):
return data.upper()
```
Finally, let's use it:
```python
result = process_data("hello world")
print(result)
```
That should work!"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 3)
self.assertEqual(blocks[0], '\nimport os\nimport sys\n')
self.assertEqual(blocks[1], '\ndef process_data(data):\n return data.upper()\n')
self.assertEqual(blocks[2], '\nresult = process_data("hello world")\nprint(result)\n')
def test_load_exec_block_with_special_characters(self):
"""Test parsing code blocks containing special characters."""
llm_text = """```python
text = "Hello \"world\" with 'quotes'"
regex = r"^\\d+$"
path = "C:\\Users\\test\\file.txt"
```"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertIsNotNone(blocks)
self.assertEqual(len(blocks), 1)
expected = '\ntext = "Hello "world" with \'quotes\'"\nregex = r"^\\d+$"\npath = "C:\\Users\\test\\file.txt"\n'
self.assertEqual(blocks[0], expected)
def test_load_exec_block_tag_undefined(self):
"""Test that assertion error is raised when tag is undefined."""
self.tool.tag = "undefined"
llm_text = """```python
print("test")
```"""
with self.assertRaises(AssertionError):
self.tool.load_exec_block(llm_text)
def test_found_executable_blocks_flag(self):
"""Test that the executable blocks found flag is set correctly."""
self.assertFalse(self.tool.found_executable_blocks())
llm_text = """```python
print("test")
```"""
blocks, save_path = self.tool.load_exec_block(llm_text)
self.assertTrue(self.tool.found_executable_blocks())
self.assertFalse(self.tool.found_executable_blocks())
def test_get_parameter_value(self):
"""Test the get_parameter_value helper method."""
block = """param1 = value1
param2 = value2
some other text
param3 = value3"""
self.assertEqual(self.tool.get_parameter_value(block, "param1"), "value1")
self.assertEqual(self.tool.get_parameter_value(block, "param2"), "value2")
self.assertEqual(self.tool.get_parameter_value(block, "param3"), "value3")
self.assertIsNone(self.tool.get_parameter_value(block, "nonexistent"))
if __name__ == '__main__':
unittest.main()