mirror of
https://github.com/tcsenpai/agenticSeek.git
synced 2025-06-01 16:50:10 +00:00
Merge branch 'main' of https://github.com/Fosowl/agenticSeek
This commit is contained in:
commit
71deb2dab7
143
README.md
143
README.md
@ -36,7 +36,9 @@ Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.z
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you have chrome driver, docker and python3.10 (or newer) installed.
|
||||
Make sure you have chrome driver, docker and python3.10 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.
|
||||
|
||||
@ -52,12 +54,16 @@ mv .env.example .env
|
||||
|
||||
```sh
|
||||
python3 -m venv agentic_seek_env
|
||||
source agentic_seek_env/bin/activate
|
||||
source agentic_seek_env/bin/activate
|
||||
# On Windows: agentic_seek_env\Scripts\activate
|
||||
```
|
||||
|
||||
### 3️⃣ **Install package**
|
||||
|
||||
Ensure Python, Docker and docker compose, and Google chrome are installed.
|
||||
|
||||
We recommand Python 3.10.0.
|
||||
|
||||
**Automatic Installation (Recommanded):**
|
||||
|
||||
For Linux/Macos:
|
||||
@ -66,48 +72,59 @@ For Linux/Macos:
|
||||
```
|
||||
|
||||
For windows:
|
||||
|
||||
```sh
|
||||
./install.bat
|
||||
```
|
||||
|
||||
**Manually:**
|
||||
|
||||
First, you need to install these packages:
|
||||
**Note: For any OS, ensure the ChromeDriver you install matches your installed Chrome version. Run `google-chrome --version`. See known issues if you have chrome >135**
|
||||
|
||||
- *Linux*:
|
||||
|
||||
Updates package list (apt-get update).
|
||||
Update Package List: `sudo apt update`
|
||||
|
||||
Install these:
|
||||
alsa-utils, portaudio19-dev, python3-pyaudio, libgtk-3-dev, libnotify-dev, libgconf-2-4, libnss3, libxss1, selenium
|
||||
Install Dependencies: `sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
||||
|
||||
Make sure to install docker + docker-compose if not already.
|
||||
Install ChromeDriver matching your Chrome browser version:
|
||||
`sudo apt install -y chromium-chromedriver`
|
||||
|
||||
Install requirements: `pip3 install -r requirements.txt`
|
||||
|
||||
- *Macos*:
|
||||
|
||||
Update package list.
|
||||
Install chromedriver.
|
||||
Install portaudio.
|
||||
Install chromedriver and selenium.
|
||||
Update brew : `brew update`
|
||||
|
||||
Install chromedriver : `brew install --cask chromedriver`
|
||||
|
||||
Install portaudio: `brew install portaudio`
|
||||
|
||||
Upgrade pip : `python3 -m pip install --upgrade pip`
|
||||
|
||||
Upgrade wheel : : `pip3 install --upgrade setuptools wheel`
|
||||
|
||||
Install requirements: `pip3 install -r requirements.txt`
|
||||
|
||||
- *Windows*:
|
||||
|
||||
Install pyreadline3, selenium portaudio, pyAudio and chromedriver
|
||||
Install pyreadline3 `pip install pyreadline3`
|
||||
|
||||
Then install pip requirements:
|
||||
Install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: `pip install pyaudio`
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
# or
|
||||
python3 setup.py install
|
||||
```
|
||||
Download and install chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started
|
||||
|
||||
Place chromedriver in a directory included in your PATH.
|
||||
|
||||
Install requirements: `pip3 install -r requirements.txt`
|
||||
|
||||
---
|
||||
|
||||
## Setup for running LLM locally on your machine
|
||||
|
||||
**We recommend using at the very least Deepseek 14B, smaller models will struggle with tasks especially for web browsing.**
|
||||
**Hardware Requirements:**
|
||||
|
||||
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**
|
||||
|
||||
@ -121,26 +138,40 @@ See below for a list of local supported provider.
|
||||
|
||||
**Update the config.ini**
|
||||
|
||||
Change the config.ini file to set the provider_name to a supported provider and provider_model to `deepseek-r1:14b`
|
||||
Change the config.ini file to set the provider_name to a supported provider and provider_model to a LLM supported by your provider. We recommand reasoning model such as *Qwen* or *Deepseek*.
|
||||
|
||||
NOTE: `deepseek-r1:14b`is an example, use a bigger model if your hardware allow it.
|
||||
See the **FAQ** at the end of the README for required hardware.
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = True
|
||||
is_local = True # Whenever you are running locally or with remote provider.
|
||||
provider_name = ollama # or lm-studio, openai, etc..
|
||||
provider_model = deepseek-r1:14b
|
||||
provider_model = deepseek-r1:14b # choose a model that fit your hardware
|
||||
provider_server_address = 127.0.0.1:11434
|
||||
agent_name = Jarvis # name of your AI
|
||||
recover_last_session = True # whenever to recover the previous session
|
||||
save_session = True # whenever to remember the current session
|
||||
speak = True # text to speech
|
||||
listen = False # Speech to text, only for CLI
|
||||
work_dir = /Users/mlg/Documents/workspace # The workspace for AgenticSeek.
|
||||
jarvis_personality = False # Whenever to use a more "Jarvis" like personality (experimental)
|
||||
languages = en zh # The list of languages, Text to speech will default to the first language on the list
|
||||
[BROWSER]
|
||||
headless_browser = True # Whenever to use headless browser, recommanded only if you use web interface.
|
||||
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`
|
||||
|
||||
**List of local providers**
|
||||
|
||||
| Provider | Local? | Description |
|
||||
|-----------|--------|-----------------------------------------------------------|
|
||||
| ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
||||
| lm-studio | Yes | Run LLM locally with LM studio (set `provider_name` to `lm-studio`)|
|
||||
| openai | Yes | Use openai compatible API |
|
||||
|
||||
| openai | Yes | Use openai compatible API (eg: llama.cpp server) |
|
||||
|
||||
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||
|
||||
@ -214,6 +245,8 @@ start ./start_services.cmd # Window
|
||||
python3 cli.py
|
||||
```
|
||||
|
||||
We advice you set `headless_browser` to False in the config.ini for CLI mode.
|
||||
|
||||
**Options 2:** Run with the Web interface.
|
||||
|
||||
Start the backend.
|
||||
@ -236,37 +269,22 @@ To exit, simply say/type `goodbye`.
|
||||
|
||||
Here are some example usage:
|
||||
|
||||
### Coding/Bash
|
||||
> *Make a snake game in python!*
|
||||
|
||||
> *Make a snake game in python*
|
||||
> *Search the web for top cafes in Rennes, France, and save a list of three with their addresses in rennes_cafes.txt.*
|
||||
|
||||
> *Show me how to multiply matrice in C*
|
||||
> *Write a Go program to calculate the factorial of a number, save it as factorial.go in your workspace*
|
||||
|
||||
> *Make a blackjack in golang*
|
||||
> *Search my summer_pictures folder for all JPG files, rename them with today’s date, and save a list of renamed files in photos_list.txt*
|
||||
|
||||
### Web search
|
||||
> *Search online for popular sci-fi movies from 2024 and pick three to watch tonight. Save the list in movie_night.txt.*
|
||||
|
||||
> *Do a web search to find cool tech startup in Japan working on cutting edge AI research*
|
||||
> *Search the web for the latest AI news articles from 2025, select three, and write a Python script to scrape their titles and summaries. Save the script as news_scraper.py and the summaries in ai_news.txt in /home/projects*
|
||||
|
||||
> *Can you find on the internet who created AgenticSeek?*
|
||||
> *Friday, search the web for a free stock price API, register with supersuper7434567@gmail.com then write a Python script to fetch using the API daily prices for Tesla, and save the results in stock_prices.csv*
|
||||
|
||||
> *Can you use a fuel calculator online to estimate the cost of a Nice - Milan trip*
|
||||
*Note that form filling capabilities are still experimental and might fail.*
|
||||
|
||||
### File system
|
||||
|
||||
> *Hey can you find where is contract.pdf i lost it*
|
||||
|
||||
> *Show me how much space I have left on my disk*
|
||||
|
||||
> *Can you follow the readme and install project at /home/path/project*
|
||||
|
||||
### Casual
|
||||
|
||||
> *Tell me about Rennes, France*
|
||||
|
||||
> *Should I pursue a phd ?*
|
||||
|
||||
> *What's the best workout routine ?*
|
||||
|
||||
|
||||
After you type your query, AgenticSeek will allocate the best agent for the task.
|
||||
@ -474,6 +492,27 @@ And download the chromedriver version matching your OS.
|
||||
|
||||
If this section is incomplete please raise an issue.
|
||||
|
||||
## connection adapters Issues
|
||||
|
||||
```
|
||||
Exception: Provider lm-studio failed: HTTP request failed: No connection adapters were found for '127.0.0.1:11434/v1/chat/completions'
|
||||
```
|
||||
|
||||
Make sure you have `http://` in front of the provider IP address :
|
||||
|
||||
`provider_server_address = http://127.0.0.1:11434`
|
||||
|
||||
## SearxNG base URL must be provided
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
|
||||
Maybe you didn't move `.env.example` as `.env` ? You can also export SEARXNG_BASE_URL:
|
||||
|
||||
`export SEARXNG_BASE_URL="http://127.0.0.1:8080"`
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: What hardware do I need?**
|
||||
@ -512,5 +551,9 @@ We’re looking for developers to improve AgenticSeek! Check out open issues or
|
||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||
|
||||
## Maintainers:
|
||||
> [Fosowl](https://github.com/Fosowl)
|
||||
> [steveh8758](https://github.com/steveh8758)
|
||||
|
||||
> [Fosowl](https://github.com/Fosowl) | Paris Time | (Sometime busy)
|
||||
|
||||
> [https://github.com/antoineVIVIES](antoineVIVIES) | Taipei Time | (Often busy)
|
||||
|
||||
> [steveh8758](https://github.com/steveh8758) | Taipei Time | (Always busy)
|
138
README_CHS.md
138
README_CHS.md
@ -1,43 +1,32 @@
|
||||
# AgenticSeek: Private, Local Manus Alternative.
|
||||
|
||||
<p align="center">
|
||||
<img align="center" src="./media/whale_readme.jpg">
|
||||
<p>
|
||||
|
||||
|
||||
[English](./README.md) | 中文 | [日本語](./README_JP.md)
|
||||
|
||||
# AgenticSeek: 類似 Manus 但基於 Deepseek R1 Agents 的本地模型。
|
||||
|
||||
**Manus AI 的本地替代品**,它是一个具有语音功能的大语言模型秘书,可以 Coding、访问你的电脑文件、浏览网页,并自动修正错误与反省,最重要的是不会向云端传送任何资料。采用 DeepSeek R1 等推理模型构建,完全在本地硬体上运行,进而保证资料的隐私。
|
||||
*一个 **100% 本地替代 Manus AI** 的方案,这款支持语音的 AI 助理能够自主浏览网页、编写代码和规划任务,同时将所有数据保留在您的设备上。专为本地推理模型量身打造,完全在您自己的硬件上运行,确保完全的隐私保护和零云端依赖。*
|
||||
|
||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460)
|
||||
|
||||
> 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们!
|
||||
### 为什么选择 AgenticSeek?
|
||||
|
||||
* 🔒 完全本地化与隐私保护 - 所有功能都在您的设备上运行 — 无云端服务,无数据共享。您的文件、对话和搜索始终保持私密。
|
||||
|
||||
* 🌐 智能网页浏览 - AgenticSeek 能够自主浏览互联网 — 搜索、阅读、提取信息、填写网页表单 — 全程无需人工操作。
|
||||
|
||||
* 💻 自主编码助手 - 需要代码?它可以编写、调试并运行 Python、C、Go、Java 等多种语言的程序 — 全程无需监督。
|
||||
|
||||
* 🧠 智能代理选择 - 您提问,它会自动选择最适合该任务的代理。就像拥有一个随时待命的专家团队。
|
||||
|
||||
* 📋 规划与执行复杂任务 - 从旅行规划到复杂项目 — 它能将大型任务分解为步骤,并利用多个 AI 代理完成工作。
|
||||
|
||||
* 🎙️ 语音功能 - 清晰、快速、未来感十足的语音与语音转文本功能,让您能像科幻电影中一样与您的个人 AI 助手对话。
|
||||
|
||||
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
> *在大阪和东京深入搜寻人工智慧新创公司,至少找到 5 家,然后储存在 research_japan.txt 档案中*
|
||||
|
||||
> *你可以用 C 语言制作俄罗斯方块游戏吗?*
|
||||
|
||||
> *我想设定一个新的专案档案索引,命名为 mark2。*
|
||||
|
||||
|
||||
|
||||
## Features:
|
||||
|
||||
- **100% 本机运行**: 本机运行,不使用云端服务,所以资料绝不会散布出去,我的东西还是我的!不会被当作其他服务的训练资料。
|
||||
|
||||
- **文件的交互系统**: 使用 bash 去浏览本机资料和操作本机系统。
|
||||
|
||||
- **自主 Coding**: AgenticSeek 可以自己运行、Debug、编译 Python、C、Golang 和各种语言。
|
||||
|
||||
- **代理助理**: 不同的工作由不同的助理去处理问题。AgenticSeek 会自己寻找最适合的助理去做相对应的工作。
|
||||
|
||||
- **规划**: 对于复杂的任务,AgenticSeek 会交办给不同的助理进行规划和执行。
|
||||
|
||||
- **自主学习**: 自动在网路上寻找资料。
|
||||
|
||||
- **记忆功能**: 对于每次的对话进行统整、保存对话,并且在本地储存用户的使用习惯。
|
||||
> 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们!
|
||||
|
||||
---
|
||||
|
||||
@ -45,6 +34,8 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
确保已安装了 Chrome driver,Docker 和 Python 3.10(或更新)。
|
||||
|
||||
我们强烈建议您使用 Python 3.10 进行设置,否则可能会发生依赖错误。
|
||||
|
||||
有关于 Chrome driver 的问题,请参见 **Chromedriver** 部分。
|
||||
|
||||
### 1️⃣ **复制储存库与设置环境变数**
|
||||
@ -71,13 +62,55 @@ source agentic_seek_env/bin/activate
|
||||
./install.sh
|
||||
```
|
||||
|
||||
** 若要让文本转语音(TTS)功能支持中文,你需要安装 jieba(中文分词库)和 cn2an(中文数字转换库):**
|
||||
|
||||
```
|
||||
pip3 install jieba cn2an
|
||||
```
|
||||
|
||||
**手动安装:**
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
# or
|
||||
python3 setup.py install
|
||||
```
|
||||
|
||||
**注意:对于任何操作系统,请确保您安装的 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`
|
||||
|
||||
安装 requirements:`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`
|
||||
|
||||
安装 requirements:`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
|
||||
|
||||
将 chromedriver 放置在包含在您的 PATH 中的目录中。
|
||||
|
||||
安装 requirements:`pip3 install -r requirements.txt`
|
||||
|
||||
|
||||
|
||||
## 在本地机器上运行 AgenticSeek
|
||||
|
||||
@ -93,18 +126,37 @@ ollama serve
|
||||
|
||||
请参阅下方支持的本地提供者列表。
|
||||
|
||||
修改 `config.ini` 文件,将 `provider_name` 设置为支持的提供者,并将 `provider_model` 设置为 `deepseek-r1:14b`。
|
||||
**更新 config.ini**
|
||||
|
||||
注意:`deepseek-r1:14b` 只是一个示例,如果你的硬件允许,可以使用更大的模型。
|
||||
修改 config.ini 文件以设置 provider_name 为支持的提供者,并将 provider_model 设置为该提供者支持的 LLM。我们推荐使用具有推理能力的模型,如 *Qwen* 或 *Deepseek*。
|
||||
|
||||
请参见 README 末尾的 **FAQ** 部分了解所需硬件。
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = True
|
||||
provider_name = ollama # 或 lm-studio, openai 等
|
||||
provider_model = deepseek-r1:14b
|
||||
is_local = True # 无论是在本地运行还是使用远程提供者。
|
||||
provider_name = ollama # 或 lm-studio, openai 等..
|
||||
provider_model = deepseek-r1:14b # 选择适合您硬件的模型
|
||||
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 来减少浏览器检测
|
||||
```
|
||||
|
||||
警告:使用 LM-studio 运行 LLM 时,请*不要*将 provider_name 设置为 `openai`。请将其设置为 `lm-studio`。
|
||||
|
||||
注意:某些提供者(如 lm-studio)需要在 IP 前面加上 `http://`。例如 `http://127.0.0.1:1234`
|
||||
|
||||
|
||||
|
||||
**本地提供者列表**
|
||||
|
||||
| 提供者 | 本地? | 描述 |
|
||||
@ -510,6 +562,10 @@ DeepSeek R1 天生会说中文
|
||||
|
||||
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||
|
||||
## 作者:
|
||||
> [Fosowl](https://github.com/Fosowl)
|
||||
> [steveh8758](https://github.com/steveh8758)
|
||||
## 维护者:
|
||||
|
||||
> [Fosowl](https://github.com/Fosowl) | 巴黎时间 | (有时很忙)
|
||||
|
||||
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北时间 | (经常很忙)
|
||||
|
||||
> [steveh8758](https://github.com/steveh8758) | 台北时间 | (总是很忙)
|
||||
|
127
README_CHT.md
127
README_CHT.md
@ -1,3 +1,5 @@
|
||||
# AgenticSeek: 類似 Manus 但基於 Deepseek R1 Agents 的本地模型。
|
||||
|
||||
<p align="center">
|
||||
<img align="center" src="./media/whale_readme.jpg">
|
||||
<p>
|
||||
@ -5,40 +7,28 @@
|
||||
--------------------------------------------------------------------------------
|
||||
[English](./README.md) | 繁體中文 | [日本語](./README_JP.md)
|
||||
|
||||
# AgenticSeek: 類似 Manus 但基於 Deepseek R1 Agents 的本地模型。
|
||||
|
||||
|
||||
**Manus AI 的本地替代品**,它是一個具有語音功能的大語言模型秘書,可以 Coding、訪問你的電腦文件、瀏覽網頁,並自動修正錯誤與反省,最重要的是不會向雲端傳送任何資料。採用 DeepSeek R1 等推理模型構建,完全在本地硬體上運行,進而保證資料的隱私。
|
||||
*一个 **100% 本地替代 Manus AI** 的方案,这款支持语音的 AI 助理能够自主浏览网页、编写代码和规划任务,同时将所有数据保留在您的设备上。专为本地推理模型量身打造,完全在您自己的硬件上运行,确保完全的隐私保护和零云端依赖。*
|
||||
|
||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460)
|
||||
|
||||
> 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們!
|
||||
### 为什么选择 AgenticSeek?
|
||||
|
||||
* 🔒 完全本地化与隐私保护 - 所有功能都在您的设备上运行 — 无云端服务,无数据共享。您的文件、对话和搜索始终保持私密。
|
||||
|
||||
* 🌐 智能网页浏览 - AgenticSeek 能够自主浏览互联网 — 搜索、阅读、提取信息、填写网页表单 — 全程无需人工操作。
|
||||
|
||||
* 💻 自主编码助手 - 需要代码?它可以编写、调试并运行 Python、C、Go、Java 等多种语言的程序 — 全程无需监督。
|
||||
|
||||
* 🧠 智能代理选择 - 您提问,它会自动选择最适合该任务的代理。就像拥有一个随时待命的专家团队。
|
||||
|
||||
* 📋 规划与执行复杂任务 - 从旅行规划到复杂项目 — 它能将大型任务分解为步骤,并利用多个 AI 代理完成工作。
|
||||
|
||||
* 🎙️ 语音功能 - 清晰、快速、未来感十足的语音与语音转文本功能,让您能像科幻电影中一样与您的个人 AI 助手对话。
|
||||
|
||||
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
> *在大阪和東京深入搜尋人工智慧新創公司,至少找到 5 家,然後儲存在 research_japan.txt 檔案中*
|
||||
|
||||
> *你可以用 C 語言製作俄羅斯方塊遊戲嗎?*
|
||||
|
||||
> *我想設定一個新的專案檔案索引,命名為 mark2。*
|
||||
|
||||
|
||||
|
||||
## Features:
|
||||
|
||||
- **100% 本機運行**: 本機運行,不使用雲端服務,所以資料絕不會散布出去,我的東西還是我的!不會被當作其他服務的訓練資料。
|
||||
|
||||
- **文件的交互系統**: 使用 bash 去瀏覽本機資料和操作本機系統。
|
||||
|
||||
- **自主 Coding**: AgenticSeek 可以自己運行、Debug、編譯 Python、C、Golang 和各種語言。
|
||||
|
||||
- **代理助理**: 不同的工作由不同的助理去處理問題。AgenticSeek 會自己尋找最適合的助理去做相對應的工作。
|
||||
|
||||
- **規劃**: 對於複雜的任務,AgenticSeek 會交辦給不同的助理進行規劃和執行。
|
||||
|
||||
- **自主學習**: 自動在網路上尋找資料。
|
||||
|
||||
- **記憶功能**: 對於每次的對話進行統整、保存對話,並且在本地儲存用戶的使用習慣。
|
||||
> 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們!
|
||||
|
||||
---
|
||||
|
||||
@ -46,6 +36,8 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
確保已安裝了 Chrome driver,Docker 和 Python 3.10(或更新)。
|
||||
|
||||
我们强烈建议您使用 Python 3.10 进行设置,否则可能会发生依赖错误。
|
||||
|
||||
有關於 Chrome driver 的問題,請參見 **Chromedriver** 部分。
|
||||
|
||||
### 1️⃣ **複製儲存庫與設置環境變數**
|
||||
@ -72,13 +64,53 @@ source agentic_seek_env/bin/activate
|
||||
./install.sh
|
||||
```
|
||||
|
||||
** 若要让文本转语音(TTS)功能支持中文,你需要安装 jieba(中文分词库)和 cn2an(中文数字转换库):**
|
||||
|
||||
```
|
||||
pip3 install jieba cn2an
|
||||
```
|
||||
|
||||
**手動安裝:**
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
# or
|
||||
python3 setup.py install
|
||||
```
|
||||
|
||||
**注意:对于任何操作系统,请确保您安装的 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`
|
||||
|
||||
安装 requirements:`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`
|
||||
|
||||
安装 requirements:`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
|
||||
|
||||
将 chromedriver 放置在包含在您的 PATH 中的目录中。
|
||||
|
||||
安装 requirements:`pip3 install -r requirements.txt`
|
||||
|
||||
## 在本地機器上運行 AgenticSeek
|
||||
|
||||
@ -94,16 +126,27 @@ ollama serve
|
||||
|
||||
请参阅下方支持的本地提供者列表。
|
||||
|
||||
修改 `config.ini` 文件,将 `provider_name` 设置为支持的提供者,并将 `provider_model` 设置为 `deepseek-r1:14b`。
|
||||
修改 config.ini 文件以设置 provider_name 为支持的提供者,并将 provider_model 设置为该提供者支持的 LLM。我们推荐使用具有推理能力的模型,如 *Qwen* 或 *Deepseek*。
|
||||
|
||||
注意:`deepseek-r1:14b` 只是一个示例,如果你的硬件允许,可以使用更大的模型。
|
||||
请参见 README 末尾的 **FAQ** 部分了解所需硬件。
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = True
|
||||
provider_name = ollama # 或 lm-studio, openai 等
|
||||
provider_model = deepseek-r1:14b
|
||||
is_local = True # 无论是在本地运行还是使用远程提供者。
|
||||
provider_name = ollama # 或 lm-studio, openai 等..
|
||||
provider_model = deepseek-r1:14b # 选择适合您硬件的模型
|
||||
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 来减少浏览器检测
|
||||
```
|
||||
|
||||
**本地提供者列表**
|
||||
@ -511,6 +554,10 @@ DeepSeek R1 天生会说中文
|
||||
|
||||
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||
|
||||
## 作者:
|
||||
> [Fosowl](https://github.com/Fosowl)
|
||||
> [steveh8758](https://github.com/steveh8758)
|
||||
## 维护者:
|
||||
|
||||
> [Fosowl](https://github.com/Fosowl) | 巴黎时间 | (有时很忙)
|
||||
|
||||
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北时间 | (经常很忙)
|
||||
|
||||
> [steveh8758](https://github.com/steveh8758) | 台北时间 | (总是很忙)
|
76
README_FR.md
76
README_FR.md
@ -23,7 +23,6 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
|
||||
|
||||
|
||||
## Fonctionnalités:
|
||||
|
||||
- **100% Local**: Fonctionne en local sur votre PC. Vos données restent les vôtres.
|
||||
@ -44,7 +43,9 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
## **Installation**
|
||||
|
||||
Assurez-vous d’avoir installé le pilote Chrome, Docker et Python 3.10 (ou une version plus récente).
|
||||
Assurez-vous d’avoir installé le pilote Chrome, Docker et Python 3.10.
|
||||
|
||||
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.
|
||||
|
||||
@ -74,9 +75,44 @@ source agentic_seek_env/bin/activate
|
||||
|
||||
**Manuel:**
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
**Note : Pour tous les systèmes d'exploitation, assurez-vous que le ChromeDriver que vous installez correspond à la version de Chrome installée. Exécutez `google-chrome --version`. Consultez les problèmes connus si vous avez Chrome >135**
|
||||
|
||||
- *Linux*:
|
||||
|
||||
Mettre à jour la liste des paquets : `sudo apt update`
|
||||
|
||||
Installer les dépendances : `sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
||||
|
||||
Installer ChromeDriver correspondant à la version de votre navigateur Chrome :
|
||||
`sudo apt install -y chromium-chromedriver`
|
||||
|
||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
||||
|
||||
- *macOS*:
|
||||
|
||||
Mettre à jour brew : `brew update`
|
||||
|
||||
Installer chromedriver : `brew install --cask chromedriver`
|
||||
|
||||
Installer portaudio : `brew install portaudio`
|
||||
|
||||
Mettre à jour pip : `python3 -m pip install --upgrade pip`
|
||||
|
||||
Mettre à jour wheel : `pip3 install --upgrade setuptools wheel`
|
||||
|
||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
||||
|
||||
- *Windows*:
|
||||
|
||||
Installer pyreadline3 : `pip install pyreadline3`
|
||||
|
||||
Installer portaudio manuellement (par exemple, via vcpkg ou des binaires précompilés) puis exécutez : `pip install pyaudio`
|
||||
|
||||
Télécharger et installer chromedriver manuellement depuis : https://sites.google.com/chromium.org/driver/getting-started
|
||||
|
||||
Placez chromedriver dans un répertoire inclus dans votre PATH.
|
||||
|
||||
Installer les prérequis : `pip3 install -r requirements.txt`
|
||||
|
||||
|
||||
## Faire fonctionner sur votre machine
|
||||
@ -88,18 +124,35 @@ Lancer votre provider local, par exemple avec ollama:
|
||||
ollama serve
|
||||
```
|
||||
|
||||
Voyez la section **Provider** pour la liste de provideurs disponible.
|
||||
**Configurer le config.ini**
|
||||
|
||||
Modifiez le fichier config.ini pour définir provider_name sur le nom d'un provideur et provider_model sur le LLM à utiliser.
|
||||
Modifiez le fichier config.ini pour définir provider_name sur un fournisseur supporté et provider_model sur un LLM compatible avec votre fournisseur. Nous recommandons des modèles de raisonnement comme *Qwen* ou *Deepseek*.
|
||||
|
||||
Consultez la section **FAQ** à la fin du README pour connaître le matériel requis.
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = True
|
||||
provider_name = ollama # ou lm-studio, openai, etc...
|
||||
provider_model = deepseek-r1:14b
|
||||
is_local = True # Si vous exécutez localement ou avec un fournisseur distant.
|
||||
provider_name = ollama # ou lm-studio, openai, etc..
|
||||
provider_model = deepseek-r1:14b # choisissez un modèle adapté à votre matériel
|
||||
provider_server_address = 127.0.0.1:11434
|
||||
agent_name = Jarvis # nom de votre IA
|
||||
recover_last_session = True # récupérer ou non la session précédente
|
||||
save_session = True # mémoriser ou non la session actuelle
|
||||
speak = True # synthèse vocale
|
||||
listen = False # reconnaissance vocale, uniquement pour CLI
|
||||
work_dir = /Users/mlg/Documents/workspace # L'espace de travail pour AgenticSeek.
|
||||
jarvis_personality = False # Utiliser une personnalité plus "Jarvis", non recommandé avec des petits modèles
|
||||
languages = en fr # Liste des langages, la synthèse vocale utilisera par défaut la première langue de la liste
|
||||
[BROWSER]
|
||||
headless_browser = True # Utiliser ou non le navigateur sans interface graphique, recommandé uniquement avec l'interface web.
|
||||
stealth_mode = True # Utiliser selenium non détectable pour réduire la détection du navigateur
|
||||
```
|
||||
|
||||
Remarque : Certains fournisseurs (ex : lm-studio) nécessitent `http://` devant l'adresse IP. Par exemple `http://127.0.0.1:1234`
|
||||
|
||||
|
||||
|
||||
**Liste des provideurs locaux**
|
||||
|
||||
| Fournisseur | Local ? | Description |
|
||||
@ -439,6 +492,7 @@ Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la se
|
||||
|
||||
[Guide du contributeur](./docs/CONTRIBUTING.md)
|
||||
|
||||
## Auteurs/Mainteneurs:
|
||||
## Mainteneurs:
|
||||
> [Fosowl](https://github.com/Fosowl)
|
||||
> [steveh8758](https://github.com/steveh8758)
|
||||
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES)
|
||||
|
546
README_JP.md
546
README_JP.md
@ -1,61 +1,48 @@
|
||||
# AgenticSeek: プライベートなローカルManus代替
|
||||
|
||||
<p align="center">
|
||||
<img align="center" src="./media/whale_readme.jpg">
|
||||
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek ロゴ">
|
||||
<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) | 日本語
|
||||
|
||||
# AgenticSeek: Deepseek R1エージェントによって動作するManusのようなAI。
|
||||
*Manus AIの**100%ローカルな代替**となるこの音声対応AIアシスタントは、自律的にウェブを閲覧し、コードを書き、タスクを計画しながら、すべてのデータをあなたのデバイスに保持します。ローカル推論モデルに合わせて調整されており、完全にあなたのハードウェア上で動作するため、完全なプライバシーとクラウドへの依存ゼロを保証します。*
|
||||
|
||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||
|
||||
**Manus AIの完全なローカル代替品**、音声対応のAIアシスタントで、コードを書き、ファイルシステムを探索し、ウェブを閲覧し、ミスを修正し、データをクラウドに送信することなくすべてを行います。DeepSeek R1のような推論モデルを使用して構築されており、この自律エージェントは完全にハードウェア上で動作し、データのプライバシーを保護します。
|
||||
### なぜAgenticSeekなのか?
|
||||
|
||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460)
|
||||
* 🔒 完全ローカル&プライベート - すべてがあなたのマシン上で実行されます — クラウドなし、データ共有なし。あなたのファイル、会話、検索はプライベートに保たれます。
|
||||
|
||||
> 🛠️ **進行中の作業** – 貢献者を探しています!
|
||||
* 🌐 スマートなウェブブラウジング - AgenticSeekは自分でインターネットを閲覧できます — 検索、読み取り、情報抽出、ウェブフォーム入力 — すべてハンズフリーで。
|
||||
|
||||
* 💻 自律型コーディングアシスタント - コードが必要ですか?Python、C、Go、Javaなどでプログラムを書き、デバッグし、実行できます — すべて監視なしで。
|
||||
|
||||
* 🧠 スマートエージェント選択 - あなたが尋ねると、タスクに最適なエージェントを自動的に見つけ出します。まるで専門家チームが助けてくれるようです。
|
||||
|
||||
* 📋 複雑なタスクの計画と実行 - 旅行計画から複雑なプロジェクトまで — 大きなタスクをステップに分割し、複数のAIエージェントを使って物事を成し遂げることができます。
|
||||
|
||||
https://github.com/user-attachments/assets/fe9e8006-0462-4793-8b31-25bd42c6d1eb
|
||||
* 🎙️ 音声対応 - クリーンで高速、未来的な音声と音声認識により、まるでSF映画のパーソナルAIのように話しかけることができます。
|
||||
|
||||
### **デモ**
|
||||
|
||||
> *agenticSeekプロジェクトを検索し、必要なスキルを学び、その後CV_candidates.zipを開いて、プロジェクトに最も適した候補者を教えてください。*
|
||||
|
||||
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||
|
||||
*そしてもっと多くのことができます!*
|
||||
免責事項:このデモは、表示されるすべてのファイル(例:CV_candidates.zip)を含め、完全に架空のものです。私たちは企業ではなく、候補者ではなくオープンソースの貢献者を求めています。
|
||||
|
||||
> *大阪と東京のAIスタートアップを深く調査し、少なくとも5つ見つけて、research_japan.txtファイルに保存してください*
|
||||
> 🛠️ **作業中** – 貢献者を募集中です!
|
||||
|
||||
> *C言語でテトリスゲームを作れますか?*
|
||||
## インストール
|
||||
|
||||
> *新しいプロジェクトファイルインデックスをmark2として設定したいです。*
|
||||
Chrome Driver、Docker、Python 3.10がインストールされていることを確認してください。
|
||||
|
||||
セットアップにはPython 3.10を正確に使用することを強くお勧めします。そうでない場合、依存関係のエラーが発生する可能性があります。
|
||||
|
||||
## 特徴:
|
||||
Chromeドライバーに関する問題については、**Chromedriver**セクションを参照してください。
|
||||
|
||||
- **100%ローカル**: クラウドなし、ハードウェア上で動作。データはあなたのものです。
|
||||
|
||||
- **ファイルシステムの操作**: bashを使用してファイルを簡単にナビゲートおよび操作します。
|
||||
|
||||
- **自律的なコーディング**: Python、C、Golangなどのコードを書き、デバッグし、実行できます。
|
||||
|
||||
- **エージェントルーティング**: タスクに最適なエージェントを自動的に選択します。
|
||||
|
||||
- **計画**: 複雑なタスクの場合、複数のエージェントを起動して計画および実行します。
|
||||
|
||||
- **自律的なウェブブラウジング**: 自律的なウェブナビゲーション。
|
||||
|
||||
- **メモリ**: 効率的なメモリとセッション管理。
|
||||
|
||||
---
|
||||
|
||||
## **インストール**
|
||||
|
||||
chrome driver、docker、およびpython3.10(またはそれ以降)がインストールされていることを確認してください。
|
||||
|
||||
chrome driverに関連する問題については、**Chromedriver**セクションを参照してください。
|
||||
|
||||
### 1️⃣ **リポジトリをクローンしてセットアップ**
|
||||
### 1️⃣ **リポジトリのクローンとセットアップ**
|
||||
|
||||
```sh
|
||||
git clone https://github.com/Fosowl/agenticSeek.git
|
||||
@ -63,99 +50,184 @@ cd agenticSeek
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
### 2️ **仮想環境を作成**
|
||||
### 2️ **仮想環境の作成**
|
||||
|
||||
```sh
|
||||
python3 -m venv agentic_seek_env
|
||||
source agentic_seek_env/bin/activate
|
||||
source agentic_seek_env/bin/activate
|
||||
# Windowsの場合: agentic_seek_env\Scripts\activate
|
||||
```
|
||||
|
||||
### 3️⃣ **パッケージをインストール**
|
||||
### 3️⃣ **パッケージのインストール**
|
||||
|
||||
**自動インストール:**
|
||||
Python、Dockerとdocker compose、Google Chromeがインストールされていることを確認してください。
|
||||
|
||||
Python 3.10.0を推奨します。
|
||||
|
||||
**自動インストール(推奨):**
|
||||
|
||||
Linux/Macosの場合:
|
||||
```sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
**手動で:**
|
||||
** テキスト読み上げ(TTS)機能で日本語をサポートするには、fugashi(日本語分かち書きライブラリ)をインストールする必要があります:**
|
||||
|
||||
** 注意: 日本語のテキスト読み上げ(TTS)機能には多くの依存関係が必要で、問題が発生する可能性があります。mecabrcに関する問題が発生することがあります。現在のところ、この問題を修正する方法が見つかっていません。当面は日本語でのテキスト読み上げ機能を無効にすることをお勧めします。**
|
||||
|
||||
必要なライブラリをインストールする場合は以下のコマンドを実行してください:
|
||||
|
||||
```
|
||||
pip3 install --upgrade pyopenjtalk jaconv mojimoji unidic fugashi
|
||||
pip install unidic-lite
|
||||
python -m unidic download
|
||||
```
|
||||
|
||||
Windowsの場合:
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
# または
|
||||
python3 setup.py install
|
||||
./install.bat
|
||||
```
|
||||
|
||||
**手動:**
|
||||
|
||||
**注意:どの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
|
||||
ollama serve
|
||||
```
|
||||
|
||||
以下に、サポートされているローカルプロバイダーのリストを示します。
|
||||
サポートされているローカルプロバイダーのリストについては、以下を参照してください。
|
||||
|
||||
**config.iniを更新する**
|
||||
**config.iniの更新**
|
||||
|
||||
config.iniファイルを変更して、`provider_name`をサポートされているプロバイダーに設定し、`provider_model`を`deepseek-r1:14b`に設定します。
|
||||
config.iniファイルを変更して、provider_nameをサポートされているプロバイダーに、provider_modelをプロバイダーがサポートするLLMに設定します。*Qwen*や*Deepseek*などの推論モデルを推奨します。
|
||||
|
||||
注意: `deepseek-r1:14b`は例です。ハードウェアが許可する場合は、より大きなモデルを使用してください。
|
||||
必要なハードウェアについては、READMEの最後にある**FAQ**を参照してください。
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = True
|
||||
provider_name = ollama # または lm-studio、openai など
|
||||
provider_model = deepseek-r1:14b
|
||||
is_local = True # ローカルで実行するか、リモートプロバイダーで実行するか。
|
||||
provider_name = ollama # またはlm-studio、openaiなど。
|
||||
provider_model = deepseek-r1:14b # ハードウェアに合ったモデルを選択してください
|
||||
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を簡単に実行 |
|
||||
| lm-studio | はい | LM studioを使用してローカルでLLMを実行(`provider_name`を`lm-studio`に設定)|
|
||||
| openai | はい | OpenAI互換APIを使用 |
|
||||
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します |
|
||||
| lm-studio | はい | LM studioでLLMをローカル実行します(`provider_name`を`lm-studio`に設定)|
|
||||
| openai | はい | openai互換API(例:llama.cppサーバー)を使用します |
|
||||
|
||||
次のステップ: [サービスを開始してAgenticSeekを実行する](#Start-services-and-Run)
|
||||
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
||||
|
||||
*問題が発生している場合は、**既知の問題**セクションを参照してください。*
|
||||
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
||||
|
||||
*ハードウェアがDeepseekをローカルで実行できない場合は、**APIを使用した実行**セクションを参照してください。*
|
||||
*ハードウェアがローカルでdeepseekを実行できない場合は、**APIで実行**セクションを参照してください*
|
||||
|
||||
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
||||
|
||||
---
|
||||
|
||||
## APIを使用したセットアップ
|
||||
## APIで実行するためのセットアップ
|
||||
|
||||
`config.ini`で希望するプロバイダーを設定してください。
|
||||
`config.ini`で目的のプロバイダーを設定します。APIプロバイダーのリストについては、以下を参照してください。
|
||||
|
||||
```sh
|
||||
[MAIN]
|
||||
is_local = False
|
||||
provider_name = openai
|
||||
provider_model = gpt-4o
|
||||
provider_server_address = 127.0.0.1:5000
|
||||
provider_name = google
|
||||
provider_model = gemini-2.0-flash
|
||||
provider_server_address = 127.0.0.1:5000 # 関係ありません
|
||||
```
|
||||
警告:設定に末尾のスペースがないことを確認してください。
|
||||
|
||||
警告: `config.ini`に末尾のスペースがないことを確認してください。
|
||||
APIキーをエクスポートします:`export <<PROVIDER>>_API_KEY="xxx"`
|
||||
|
||||
ローカルのOpenAIベースのAPIを使用する場合は、`is_local`をTrueに設定してください。
|
||||
例:`export TOGETHER_API_KEY="xxxxx"`
|
||||
|
||||
OpenAIベースのAPIが独自のサーバーで実行されている場合は、IPアドレスを変更してください。
|
||||
**APIプロバイダーのリスト**
|
||||
|
||||
次のステップ: [サービスを開始して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の実行](#サービスの開始と実行)
|
||||
|
||||
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
||||
|
||||
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
||||
|
||||
@ -163,30 +235,30 @@ OpenAIベースのAPIが独自のサーバーで実行されている場合は
|
||||
|
||||
## サービスの開始と実行
|
||||
|
||||
必要に応じてPython環境をアクティブにしてください。
|
||||
必要に応じてPython環境をアクティブ化します。
|
||||
```sh
|
||||
source agentic_seek_env/bin/activate
|
||||
```
|
||||
|
||||
必要なサービスを開始します。これにより、docker-compose.ymlから以下のサービスがすべて開始されます:
|
||||
- searxng
|
||||
- redis (searxngに必要)
|
||||
- フロントエンド
|
||||
必要なサービスを開始します。これにより、docker-compose.ymlからすべてのサービスが開始されます。これには以下が含まれます:
|
||||
- searxng
|
||||
- redis(searxngに必要)
|
||||
- frontend
|
||||
|
||||
```sh
|
||||
sudo ./start_services.sh # MacOS
|
||||
start ./start_services.cmd # Windows
|
||||
start ./start_services.cmd # Window
|
||||
```
|
||||
|
||||
**オプション1:** CLIインターフェースで実行。
|
||||
**オプション1:** CLIインターフェースで実行します。
|
||||
|
||||
```sh
|
||||
python3 cli.py
|
||||
```
|
||||
|
||||
**オプション2:** Webインターフェースで実行。
|
||||
CLIモードでは、config.iniで`headless_browser`をFalseに設定することをお勧めします。
|
||||
|
||||
注意: 現在、CLIの使用を推奨しています。Webインターフェースは開発中です。
|
||||
**オプション2:** Webインターフェースで実行します。
|
||||
|
||||
バックエンドを開始します。
|
||||
|
||||
@ -196,113 +268,89 @@ python3 api.py
|
||||
|
||||
`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を実行するためのセットアップ**
|
||||
|
||||
警告: 現在、サポートされている言語は英語、中国語、フランス語のみです。他の言語でのプロンプトは機能しますが、適切なエージェントにルーティングされない場合があります。
|
||||
|
||||
サービスが`./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を実行するオプションがあります。
|
||||
強力なコンピューターまたは使用できるサーバーがあるが、ラップトップから使用したい場合は、カスタムLLMサーバーを使用してリモートサーバーでLLMを実行するオプションがあります。
|
||||
|
||||
AIモデルを実行する「サーバー」で、IPアドレスを取得します。
|
||||
|
||||
```sh
|
||||
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の場合、IPアドレスを見つけるには、それぞれ`ipconfig`または`ifconfig`を使用してください。
|
||||
注意:WindowsまたはmacOSの場合、それぞれipconfigまたはifconfigを使用してIPアドレスを見つけます。
|
||||
|
||||
リポジトリをクローンし、`server/`フォルダに入ります。
|
||||
|
||||
リポジトリをクローンし、`server/`フォルダーに移動します。
|
||||
|
||||
```sh
|
||||
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||
cd agenticSeek/server/
|
||||
```
|
||||
|
||||
サーバー固有の依存関係をインストールします:
|
||||
サーバー固有の要件をインストールします:
|
||||
|
||||
```sh
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
サーバースクリプトを実行します。
|
||||
サーバー スクリプトを実行します。
|
||||
|
||||
```sh
|
||||
python3 app.py --provider ollama --port 3333
|
||||
```
|
||||
|
||||
`ollama`と`llamacpp`のどちらかをLLMサービスとして選択できます。
|
||||
LLMサービスとして`ollama`と`llamacpp`のどちらかを選択できます。
|
||||
|
||||
次に、個人用コンピュータで以下を行います:
|
||||
|
||||
`config.ini`ファイルを変更し、`provider_name`を`server`に、`provider_model`を`deepseek-r1:xxb`に設定します。
|
||||
次に、個人のコンピュータで:
|
||||
|
||||
`config.ini`ファイルを変更して、`provider_name`を`server`に、`provider_model`を`deepseek-r1:xxb`に設定します。
|
||||
`provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。
|
||||
|
||||
```sh
|
||||
@ -313,46 +361,46 @@ provider_model = deepseek-r1:70b
|
||||
provider_server_address = x.x.x.x:3333
|
||||
```
|
||||
|
||||
次のステップ: [サービスを開始してAgenticSeekを実行する](#Start-services-and-Run)
|
||||
|
||||
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
||||
|
||||
---
|
||||
|
||||
## 音声認識
|
||||
|
||||
現在、音声認識は英語でのみ動作することに注意してください。
|
||||
現在、音声認識は英語でのみ機能することに注意してください。
|
||||
|
||||
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
|
||||
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
|
||||
|
||||
```
|
||||
listen = True
|
||||
```
|
||||
|
||||
有効にすると、音声認識機能はトリガーキーワード(エージェントの名前)を待ちます。その後、入力を処理します。エージェントの名前は*config.ini*ファイルの`agent_name`値を更新することでカスタマイズできます:
|
||||
有効にすると、音声認識機能は、入力を処理し始める前にトリガーキーワード(エージェントの名前)をリッスンします。*config.ini*ファイルで`agent_name`の値を更新することで、エージェントの名前をカスタマイズできます:
|
||||
|
||||
```
|
||||
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?"
|
||||
```
|
||||
|
||||
## 設定
|
||||
|
||||
設定例:
|
||||
設定例:
|
||||
```
|
||||
[MAIN]
|
||||
is_local = True
|
||||
provider_name = ollama
|
||||
provider_model = deepseek-r1:1.5b
|
||||
provider_model = deepseek-r1:32b
|
||||
provider_server_address = 127.0.0.1:11434
|
||||
agent_name = Friday
|
||||
recover_last_session = False
|
||||
@ -361,7 +409,7 @@ speak = False
|
||||
listen = False
|
||||
work_dir = /Users/mlg/Documents/ai_folder
|
||||
jarvis_personality = False
|
||||
languages = en ja
|
||||
languages = en zh
|
||||
[BROWSER]
|
||||
headless_browser = False
|
||||
stealth_mode = False
|
||||
@ -369,112 +417,154 @@ stealth_mode = False
|
||||
|
||||
**説明**:
|
||||
|
||||
- is_local -> エージェントをローカルで実行する(True)か、リモートサーバーで実行する(False)。
|
||||
- provider_name -> 使用するプロバイダー(`ollama`、`server`、`lm-studio`、`deepseek-api`のいずれか)。
|
||||
- provider_model -> 使用するモデル、例: deepseek-r1:1.5b。
|
||||
- provider_server_address -> サーバーアドレス、例: 127.0.0.1:11434(ローカルの場合)。非ローカルAPIの場合は何でも設定できます。
|
||||
- agent_name -> エージェントの名前、例: Friday。TTSのトリガーワードとして使用されます。
|
||||
- recover_last_session -> 最後のセッションから再開する(True)か、しない(False)。
|
||||
- save_session -> セッションデータを保存する(True)か、しない(False)。
|
||||
- speak -> 音声出力を有効にする(True)か、しない(False)。
|
||||
- listen -> 音声入力を有効にする(True)か、しない(False)。
|
||||
- work_dir -> AIがアクセスするフォルダー。例: /Users/user/Documents/。
|
||||
- jarvis_personality -> JARVISのようなパーソナリティを使用する(True)か、しない(False)。これは単にプロンプトファイルを変更するだけです。
|
||||
- headless_browser -> ウィンドウを表示せずにブラウザを実行する(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.
|
||||
- is_local -> エージェントをローカルで実行する(True)か、リモートサーバーで実行する(False)か。
|
||||
|
||||
- provider_name -> 使用するプロバイダー(`ollama`、`server`、`lm-studio`、`deepseek-api`のいずれか)
|
||||
|
||||
- provider_model -> 使用するモデル、例:deepseek-r1:32b。
|
||||
|
||||
- provider_server_address -> サーバーアドレス、例:ローカルの場合は127.0.0.1:11434。非ローカルAPIの場合は何でも設定します。
|
||||
|
||||
- agent_name -> エージェントの名前、例:Friday。TTSのトリガーワードとして使用されます。
|
||||
|
||||
- recover_last_session -> 前回のセッションから再開する(True)かしない(False)か。
|
||||
|
||||
- save_session -> セッションデータを保存する(True)かしない(False)か。
|
||||
|
||||
- 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を簡単に実行 |
|
||||
| server | はい | モデルを別のマシンでホストし、ローカルマシンで実行 |
|
||||
| lm-studio | はい | LM studio(`lm-studio`)を使用してローカルでLLMを実行 |
|
||||
| openai | 場合による | ChatGPT API(非プライベート)またはopenai互換APIを使用 |
|
||||
| deepseek-api | いいえ | Deepseek API(非プライベート) |
|
||||
| huggingface| いいえ | Hugging-Face API(非プライベート) |
|
||||
| togetherAI | いいえ | together AI API(非プライベート)を使用
|
||||
| ollama | はい | ollamaをLLMプロバイダーとして使用して、LLMをローカルで簡単に実行します |
|
||||
| server | はい | モデルを別のマシンでホストし、ローカルマシンで実行します |
|
||||
| lm-studio | はい | LM studioでLLMをローカル実行します(`lm-studio`) |
|
||||
| openai | 場合による | ChatGPT API(非プライベート)またはopenai互換APIを使用 |
|
||||
| deepseek-api | いいえ | Deepseek API(非プライベート) |
|
||||
| huggingface| いいえ | Hugging-Face API(非プライベート) |
|
||||
| togetherAI | いいえ | together AI APIを使用(非プライベート) |
|
||||
| google | いいえ | google gemini APIを使用(非プライベート) |
|
||||
|
||||
|
||||
プロバイダーを選択するには、config.iniを変更します:
|
||||
プロバイダーを選択するには、config.iniを変更します:
|
||||
|
||||
```
|
||||
is_local = False
|
||||
provider_name = openai
|
||||
provider_model = gpt-4o
|
||||
is_local = True
|
||||
provider_name = ollama
|
||||
provider_model = deepseek-r1:32b
|
||||
provider_server_address = 127.0.0.1:5000
|
||||
```
|
||||
`is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalse。
|
||||
`is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalseである必要があります。
|
||||
|
||||
`provider_name`: 使用するプロバイダーを名前で選択します。上記のプロバイダーリストを参照してください。
|
||||
|
||||
`provider_model`: エージェントが使用するモデルを設定します。
|
||||
|
||||
`provider_server_address`: サーバープロバイダーを使用しない場合は何でも設定できます。
|
||||
`provider_server_address`: サーバーアドレス。APIプロバイダーには使用されません。
|
||||
|
||||
# 既知の問題
|
||||
|
||||
## Chromedriverの問題
|
||||
|
||||
**既知のエラー#1:** *chromedriverの不一致*
|
||||
**既知のエラー #1:** *chromedriverの不一致*
|
||||
|
||||
`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`
|
||||
|
||||
これは、ブラウザとchromedriverのバージョンが一致しない場合に発生します。
|
||||
|
||||
最新バージョンをダウンロードするには、次のリンクにアクセスしてください:
|
||||
最新バージョンをダウンロードするためにナビゲートする必要があります:
|
||||
|
||||
https://developer.chrome.com/docs/chromedriver/downloads
|
||||
|
||||
Chromeバージョン115以降を使用している場合は、次のリンクにアクセスしてください:
|
||||
Chromeバージョン115以降を使用している場合は、以下にアクセスしてください:
|
||||
|
||||
https://googlechromelabs.github.io/chrome-for-testing/
|
||||
|
||||
お使いのOSに対応するchromedriverバージョンをダウンロードします。
|
||||
そして、OSに一致するchromedriverバージョンをダウンロードします。
|
||||
|
||||

|
||||

|
||||
|
||||
このセクションが不完全な場合は、問題を報告してください。
|
||||
このセクションが不完全な場合は、問題を提起してください。
|
||||
|
||||
## 接続アダプタの問題
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
**Q: どのようなハードウェアが必要ですか?**
|
||||
**Q: どのようなハードウェアが必要ですか?**
|
||||
|
||||
| モデルサイズ | GPU | コメント |
|
||||
|-----------|--------|-----------------------------------------------------------|
|
||||
| 7B | 8GB VRAM | ⚠️ 推奨されません。パフォーマンスが低く、頻繁に幻覚を起こし、プランナーエージェントが失敗する可能性が高いです。 |
|
||||
| 14B | 12GB VRAM (例: RTX 3060) | ✅ 簡単なタスクには使用可能です。ウェブブラウジングや計画タスクには苦労する可能性があります。 |
|
||||
| 32B | 24GB以上のVRAM (例: RTX 4090) | 🚀 ほとんどのタスクで成功しますが、タスク計画にはまだ苦労する可能性があります。 |
|
||||
| 70B+ | 48GB以上のVRAM (例: Mac Studio) | 💪 優れた性能。高度なユースケースに推奨されます。 |
|
||||
| モデルサイズ | GPU | コメント |
|
||||
|-----------|------------|--------------------------------------------------------------------------|
|
||||
| 7B | 8GB VRAM | ⚠️ 非推奨。パフォーマンスが悪く、幻覚が頻繁に発生し、プランナーエージェントは失敗する可能性が高いです。 |
|
||||
| 14B | 12GB VRAM(例:RTX 3060) | ✅ 簡単なタスクには使用可能。ウェブブラウジングや計画タスクで苦労する可能性があります。 |
|
||||
| 32B | 24GB以上のVRAM(例:RTX 4090) | 🚀 ほとんどのタスクで成功しますが、タスク計画でまだ苦労する可能性があります。 |
|
||||
| 70B+ | 48GB以上のVRAM(例:mac studio) | 💪 素晴らしい。高度なユースケースに推奨されます。 |
|
||||
|
||||
**Q: なぜ他のモデルではなくDeepseek R1を選ぶのですか?**
|
||||
**Q: なぜ他のモデルではなくDeepseek R1なのですか?**
|
||||
|
||||
Deepseek R1は、そのサイズに対して推論とツールの使用に優れています。私たちのニーズに最適だと考えています。他のモデルも問題なく動作しますが、Deepseekが私たちの主な選択です。
|
||||
Deepseek R1は、そのサイズに対して推論とツール使用に優れています。私たちのニーズに合っていると考えており、他のモデルも正常に動作しますが、Deepseekが私たちの主要な選択肢です。
|
||||
|
||||
**Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?**
|
||||
**Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?**
|
||||
|
||||
Ollamaが実行中であることを確認してください(`ollama serve`)、`config.ini`がプロバイダーに一致していること、および依存関係がインストールされていることを確認してください。それでも解決しない場合は、問題を報告してください。
|
||||
ローカルが実行されていること(`ollama serve`)、`config.ini`がプロバイダーと一致していること、依存関係がインストールされていることを確認してください。それでも解決しない場合は、遠慮なく問題を提起してください。
|
||||
|
||||
**Q: 本当に100%ローカルで実行できますか?**
|
||||
**Q: 本当に100%ローカルで実行できますか?**
|
||||
|
||||
はい、OllamaまたはServerプロバイダーを使用すると、すべての音声認識、LLM、および音声合成モデルがローカルで実行されます。非ローカルオプション(OpenAIまたは他のAPI)はオプションです。
|
||||
はい、Ollama、lm-studio、またはサーバープロバイダーを使用すると、すべての音声認識、LLM、テキスト読み上げモデルがローカルで実行されます。非ローカルオプション(OpenAIまたはその他のAPI)はオプションです。
|
||||
|
||||
**Q: Manusを持っているのに、なぜAgenticSeekを使用する必要があるのですか?**
|
||||
**Q: Manusがあるのに、なぜAgenticSeekを使うべきなのですか?**
|
||||
|
||||
これは、AIエージェントに関する興味から始まったサイドプロジェクトです。特別な点は、ローカルモデルを使用し、APIを避けることです。
|
||||
私たちは、JarvisやFriday(アイアンマン映画)からインスピレーションを得て、「クール」にしようとしましたが、機能性に関してはManusから多くのインスピレーションを得ています。なぜなら、人々が最初に求めているのはローカルのManusの代替品だからです。
|
||||
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIのコストを回避します。
|
||||
これは、AIエージェントへの関心から始めたサイドプロジェクトです。特別なのは、ローカルモデルを使用し、APIを避けたいということです。
|
||||
私たちはJarvisとFriday(アイアンマン映画)からインスピレーションを得て「クール」にしましたが、機能性についてはManusからより多くのインスピレーションを得ています。なぜなら、それが人々が最初に望むもの、つまりローカルなManusの代替だからです。
|
||||
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIコストを回避します。
|
||||
|
||||
## 貢献
|
||||
## 貢献する
|
||||
|
||||
AgenticSeekを改善するための開発者を探しています!オープンな問題やディスカッションを確認してください。
|
||||
AgenticSeekを改善するための開発者を募集しています!オープンな問題やディスカッションを確認してください。
|
||||
|
||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||
[貢献ガイド](./docs/CONTRIBUTING.md)
|
||||
|
||||
## 著者:
|
||||
> [Fosowl](https://github.com/Fosowl)
|
||||
> [steveh8758](https://github.com/steveh8758)
|
||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||
|
||||
## メンテナー:
|
||||
|
||||
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
||||
|
||||
> [https://github.com/antoineVIVIES](antoineVIVIES) | 台北時間
|
||||
|
||||
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
|
7
api.py
7
api.py
@ -154,9 +154,8 @@ async def get_latest_answer():
|
||||
return JSONResponse(status_code=200, content=query_resp_history[-1])
|
||||
return JSONResponse(status_code=404, content={"error": "No answer available"})
|
||||
|
||||
async def think_wrapper(interaction, query, tts_enabled):
|
||||
async def think_wrapper(interaction, query):
|
||||
try:
|
||||
interaction.tts_enabled = tts_enabled
|
||||
interaction.last_query = query
|
||||
logger.info("Agents request is being processed")
|
||||
success = await interaction.think()
|
||||
@ -165,6 +164,8 @@ async def think_wrapper(interaction, query, tts_enabled):
|
||||
interaction.last_success = False
|
||||
else:
|
||||
interaction.last_success = True
|
||||
pretty_print(interaction.last_answer)
|
||||
interaction.speak_answer()
|
||||
return success
|
||||
except Exception as e:
|
||||
logger.error(f"Error in think_wrapper: {str(e)}")
|
||||
@ -191,7 +192,7 @@ async def process_query(request: QueryRequest):
|
||||
|
||||
try:
|
||||
is_generating = True
|
||||
success = await think_wrapper(interaction, request.query, request.tts_enabled)
|
||||
success = await think_wrapper(interaction, request.query)
|
||||
is_generating = False
|
||||
|
||||
if not success:
|
||||
|
8
cli.py
8
cli.py
@ -7,7 +7,7 @@ import asyncio
|
||||
|
||||
from sources.llm_provider import Provider
|
||||
from sources.interaction import Interaction
|
||||
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent, PlannerAgent, BrowserAgent
|
||||
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent, PlannerAgent, BrowserAgent, McpAgent
|
||||
from sources.browser import Browser, create_driver
|
||||
from sources.utility import pretty_print
|
||||
|
||||
@ -48,7 +48,10 @@ async def main():
|
||||
provider=provider, verbose=False, browser=browser),
|
||||
PlannerAgent(name="Planner",
|
||||
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",
|
||||
# prompt_path=f"prompts/{personality_folder}/mcp_agent.txt",
|
||||
# provider=provider, verbose=False), # NOTE under development
|
||||
]
|
||||
|
||||
interaction = Interaction(agents,
|
||||
@ -62,6 +65,7 @@ async def main():
|
||||
interaction.get_user()
|
||||
if await interaction.think():
|
||||
interaction.show_answer()
|
||||
interaction.speak_answer()
|
||||
except Exception as e:
|
||||
if config.getboolean('MAIN', 'save_session'):
|
||||
interaction.save_session()
|
||||
|
67
prompts/base/mcp_agent.txt
Normal file
67
prompts/base/mcp_agent.txt
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
You are an agent designed to utilize the MCP protocol to accomplish tasks.
|
||||
|
||||
The MCP provide you with a standard way to use tools and data sources like databases, APIs, or apps (e.g., GitHub, Slack).
|
||||
|
||||
The are thousands of MCPs protocol that can accomplish a variety of tasks, for example:
|
||||
- get weather information
|
||||
- get stock data information
|
||||
- Use software like blender
|
||||
- Get messages from teams, stack, messenger
|
||||
- Read and send email
|
||||
|
||||
Anything is possible with MCP.
|
||||
|
||||
To search for MCP a special format:
|
||||
|
||||
- Example 1:
|
||||
|
||||
User: what's the stock market of IBM like today?:
|
||||
|
||||
You: I will search for mcp to find information about IBM stock market.
|
||||
|
||||
```mcp_finder
|
||||
stock
|
||||
```
|
||||
|
||||
You search query must be one or two words at most.
|
||||
|
||||
This will provide you with informations about a specific MCP such as the json of parameters needed to use it.
|
||||
|
||||
For example, you might see:
|
||||
-------
|
||||
Name: Search Stock News
|
||||
Usage name: @Cognitive-Stack/search-stock-news-mcp
|
||||
Tools: [{'name': 'search-stock-news', 'description': 'Search for stock-related news using Tavily API', 'inputSchema': {'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['symbol', 'companyName'], 'properties': {'symbol': {'type': 'string', 'description': "Stock symbol to search for (e.g., 'AAPL')"}, 'companyName': {'type': 'string', 'description': 'Full company name to include in the search'}}, 'additionalProperties': False}}]
|
||||
-------
|
||||
|
||||
You can then a MCP like so:
|
||||
|
||||
```<usage name>
|
||||
{
|
||||
"tool": "<tool name (without @)>",
|
||||
"inputSchema": {<inputSchema json for the tool>}
|
||||
}
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
Now that I know how to use the MCP, I will choose the search-stock-news tool and execute it to find out IBM stock market.
|
||||
|
||||
```Cognitive-Stack/search-stock-news-mcp
|
||||
{
|
||||
"tool": "search-stock-news",
|
||||
"inputSchema": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"required": ["symbol"],
|
||||
"properties": {
|
||||
"symbol": "AAPL",
|
||||
"companyName": "IBM"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the schema require an information that you don't have ask the users for the information.
|
||||
|
62
prompts/jarvis/mcp_agent.txt
Normal file
62
prompts/jarvis/mcp_agent.txt
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
You are an agent designed to utilize the MCP protocol to accomplish tasks.
|
||||
|
||||
The MCP provide you with a standard way to use tools and data sources like databases, APIs, or apps (e.g., GitHub, Slack).
|
||||
|
||||
The are thousands of MCPs protocol that can accomplish a variety of tasks, for example:
|
||||
- get weather information
|
||||
- get stock data information
|
||||
- Use software like blender
|
||||
- Get messages from teams, stack, messenger
|
||||
- Read and send email
|
||||
|
||||
Anything is possible with MCP.
|
||||
|
||||
To search for MCP a special format:
|
||||
|
||||
- Example 1:
|
||||
|
||||
User: what's the stock market of IBM like today?:
|
||||
|
||||
You: I will search for mcp to find information about IBM stock market.
|
||||
|
||||
```mcp_finder
|
||||
stock
|
||||
```
|
||||
|
||||
This will provide you with informations about a specific MCP such as the json of parameters needed to use it.
|
||||
|
||||
For example, you might see:
|
||||
-------
|
||||
Name: Search Stock News
|
||||
Usage name: @Cognitive-Stack/search-stock-news-mcp
|
||||
Tools: [{'name': 'search-stock-news', 'description': 'Search for stock-related news using Tavily API', 'inputSchema': {'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['symbol', 'companyName'], 'properties': {'symbol': {'type': 'string', 'description': "Stock symbol to search for (e.g., 'AAPL')"}, 'companyName': {'type': 'string', 'description': 'Full company name to include in the search'}}, 'additionalProperties': False}}]
|
||||
-------
|
||||
|
||||
You can then a MCP like so:
|
||||
|
||||
```<usage name>
|
||||
{
|
||||
"tool": "<tool name (without @)>",
|
||||
"inputSchema": {<inputSchema json for the tool>}
|
||||
}
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
Now that I know how to use the MCP, I will choose the search-stock-news tool and execute it to find out IBM stock market.
|
||||
|
||||
```Cognitive-Stack/search-stock-news-mcp
|
||||
{
|
||||
"tool": "search-stock-news",
|
||||
"inputSchema": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"required": ["symbol"],
|
||||
"properties": {
|
||||
"symbol": "IBM"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -5,5 +5,6 @@ from .casual_agent import CasualAgent
|
||||
from .file_agent import FileAgent
|
||||
from .planner_agent import PlannerAgent
|
||||
from .browser_agent import BrowserAgent
|
||||
from .mcp_agent import McpAgent
|
||||
|
||||
__all__ = ["Agent", "CoderAgent", "CasualAgent", "FileAgent", "PlannerAgent", "BrowserAgent"]
|
||||
__all__ = ["Agent", "CoderAgent", "CasualAgent", "FileAgent", "PlannerAgent", "BrowserAgent", "McpAgent"]
|
||||
|
@ -39,9 +39,7 @@ class Agent():
|
||||
self.type = None
|
||||
self.current_directory = os.getcwd()
|
||||
self.llm = provider
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False)
|
||||
self.memory = None
|
||||
self.tools = {}
|
||||
self.blocks_result = []
|
||||
self.success = True
|
||||
@ -90,6 +88,21 @@ class Agent():
|
||||
raise TypeError("Tool must be a callable object (a method)")
|
||||
self.tools[name] = tool
|
||||
|
||||
def get_tools_name(self) -> list:
|
||||
"""
|
||||
Get the list of tools names.
|
||||
"""
|
||||
return list(self.tools.keys())
|
||||
|
||||
def get_tools_description(self) -> str:
|
||||
"""
|
||||
Get the list of tools names and their description.
|
||||
"""
|
||||
description = ""
|
||||
for name in self.get_tools_name():
|
||||
description += f"{name}: {self.tools[name].description}\n"
|
||||
return description
|
||||
|
||||
def load_prompt(self, file_path: str) -> str:
|
||||
try:
|
||||
with open(file_path, 'r', encoding="utf-8") as f:
|
||||
@ -240,6 +253,7 @@ class Agent():
|
||||
blocks, save_path = tool.load_exec_block(answer)
|
||||
|
||||
if blocks != None:
|
||||
pretty_print(f"Executing {len(blocks)} {name} blocks...", color="status")
|
||||
for block in blocks:
|
||||
self.show_block(block)
|
||||
output = tool.execute([block])
|
||||
|
@ -10,6 +10,7 @@ from sources.agents.agent import Agent
|
||||
from sources.tools.searxSearch import searxSearch
|
||||
from sources.browser import Browser
|
||||
from sources.logger import Logger
|
||||
from sources.memory import Memory
|
||||
|
||||
class Action(Enum):
|
||||
REQUEST_EXIT = "REQUEST_EXIT"
|
||||
@ -37,6 +38,10 @@ class BrowserAgent(Agent):
|
||||
self.notes = []
|
||||
self.date = self.get_today_date()
|
||||
self.logger = Logger("browser_agent.log")
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
|
||||
def get_today_date(self) -> str:
|
||||
"""Get the date"""
|
||||
@ -238,6 +243,14 @@ class BrowserAgent(Agent):
|
||||
self.logger.warning("No link selected.")
|
||||
return None
|
||||
|
||||
def get_page_text(self, limit_to_model_ctx = False) -> str:
|
||||
"""Get the text content of the current page."""
|
||||
page_text = self.browser.get_text()
|
||||
if limit_to_model_ctx:
|
||||
#page_text = self.memory.compress_text_to_max_ctx(page_text)
|
||||
page_text = self.memory.trim_text_to_max_ctx(page_text)
|
||||
return page_text
|
||||
|
||||
def conclude_prompt(self, user_query: str) -> str:
|
||||
annotated_notes = [f"{i+1}: {note.lower()}" for i, note in enumerate(self.notes)]
|
||||
search_note = '\n'.join(annotated_notes)
|
||||
@ -250,6 +263,7 @@ class BrowserAgent(Agent):
|
||||
|
||||
Expand on the finding or step that lead to success, and provide a conclusion that answer the request. Include link when possible.
|
||||
Do not give advices or try to answer the human. Just structure the AI finding in a structured and clear way.
|
||||
You should answer in the same language as the user.
|
||||
"""
|
||||
|
||||
def search_prompt(self, user_prompt: str) -> str:
|
||||
@ -351,13 +365,13 @@ class BrowserAgent(Agent):
|
||||
self.status_message = "Filling web form..."
|
||||
pretty_print(f"Filling inputs form...", color="status")
|
||||
fill_success = self.browser.fill_form(extracted_form)
|
||||
page_text = self.browser.get_text()
|
||||
page_text = self.get_page_text(limit_to_model_ctx=True)
|
||||
answer = self.handle_update_prompt(user_prompt, page_text, fill_success)
|
||||
answer, reasoning = await self.llm_decide(prompt)
|
||||
|
||||
if Action.FORM_FILLED.value in answer:
|
||||
pretty_print(f"Filled form. Handling page update.", color="status")
|
||||
page_text = self.browser.get_text()
|
||||
page_text = self.get_page_text(limit_to_model_ctx=True)
|
||||
self.navigable_links = self.browser.get_navigable()
|
||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||
continue
|
||||
@ -393,7 +407,7 @@ class BrowserAgent(Agent):
|
||||
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||
continue
|
||||
self.current_page = link
|
||||
page_text = self.browser.get_text()
|
||||
page_text = self.get_page_text(limit_to_model_ctx=True)
|
||||
self.navigable_links = self.browser.get_navigable()
|
||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||
self.status_message = "Navigating..."
|
||||
|
@ -6,6 +6,7 @@ from sources.tools.searxSearch import searxSearch
|
||||
from sources.tools.flightSearch import FlightSearch
|
||||
from sources.tools.fileFinder import FileFinder
|
||||
from sources.tools.BashInterpreter import BashInterpreter
|
||||
from sources.memory import Memory
|
||||
|
||||
class CasualAgent(Agent):
|
||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||
@ -17,6 +18,10 @@ class CasualAgent(Agent):
|
||||
} # No tools for the casual agent
|
||||
self.role = "talk"
|
||||
self.type = "casual_agent"
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
|
||||
async def process(self, prompt, speech_module) -> str:
|
||||
self.memory.push('user', prompt)
|
||||
|
@ -10,6 +10,7 @@ from sources.tools.BashInterpreter import BashInterpreter
|
||||
from sources.tools.JavaInterpreter import JavaInterpreter
|
||||
from sources.tools.fileFinder import FileFinder
|
||||
from sources.logger import Logger
|
||||
from sources.memory import Memory
|
||||
|
||||
class CoderAgent(Agent):
|
||||
"""
|
||||
@ -29,6 +30,10 @@ class CoderAgent(Agent):
|
||||
self.role = "code"
|
||||
self.type = "code_agent"
|
||||
self.logger = Logger("code_agent.log")
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
|
||||
def add_sys_info_prompt(self, prompt):
|
||||
"""Add system information to the prompt."""
|
||||
@ -41,7 +46,7 @@ class CoderAgent(Agent):
|
||||
async def process(self, prompt, speech_module) -> str:
|
||||
answer = ""
|
||||
attempt = 0
|
||||
max_attempts = 4
|
||||
max_attempts = 5
|
||||
prompt = self.add_sys_info_prompt(prompt)
|
||||
self.memory.push('user', prompt)
|
||||
clarify_trigger = "REQUEST_CLARIFICATION"
|
||||
@ -62,14 +67,14 @@ class CoderAgent(Agent):
|
||||
animate_thinking("Executing code...", color="status")
|
||||
self.status_message = "Executing code..."
|
||||
self.logger.info(f"Attempt {attempt + 1}:\n{answer}")
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
exec_success, feedback = self.execute_modules(answer)
|
||||
self.logger.info(f"Execution result: {exec_success}")
|
||||
answer = self.remove_blocks(answer)
|
||||
self.last_answer = answer
|
||||
await asyncio.sleep(0)
|
||||
if exec_success and self.get_last_tool_type() != "bash":
|
||||
break
|
||||
pretty_print("Execution failure", color="failure")
|
||||
pretty_print(f"Execution failure:\n{feedback}", color="failure")
|
||||
pretty_print("Correcting code...", color="status")
|
||||
self.status_message = "Correcting code..."
|
||||
attempt += 1
|
||||
|
@ -4,6 +4,7 @@ from sources.utility import pretty_print, animate_thinking
|
||||
from sources.agents.agent import Agent
|
||||
from sources.tools.fileFinder import FileFinder
|
||||
from sources.tools.BashInterpreter import BashInterpreter
|
||||
from sources.memory import Memory
|
||||
|
||||
class FileAgent(Agent):
|
||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||
@ -18,6 +19,10 @@ class FileAgent(Agent):
|
||||
self.work_dir = self.tools["file_finder"].get_work_dir()
|
||||
self.role = "files"
|
||||
self.type = "file_agent"
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
|
||||
async def process(self, prompt, speech_module) -> str:
|
||||
exec_success = False
|
||||
|
73
sources/agents/mcp_agent.py
Normal file
73
sources/agents/mcp_agent.py
Normal file
@ -0,0 +1,73 @@
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
from sources.utility import pretty_print, animate_thinking
|
||||
from sources.agents.agent import Agent
|
||||
from sources.tools.mcpFinder import MCP_finder
|
||||
from sources.memory import Memory
|
||||
|
||||
# NOTE MCP agent is an active work in progress, not functional yet.
|
||||
|
||||
class McpAgent(Agent):
|
||||
|
||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||
"""
|
||||
The mcp agent is a special agent for using MCPs.
|
||||
MCP agent will be disabled if the user does not explicitly set the MCP_FINDER_API_KEY in environment variable.
|
||||
"""
|
||||
super().__init__(name, prompt_path, provider, verbose, None)
|
||||
keys = self.get_api_keys()
|
||||
self.tools = {
|
||||
"mcp_finder": MCP_finder(keys["mcp_finder"]),
|
||||
# add mcp tools here
|
||||
}
|
||||
self.role = "mcp"
|
||||
self.type = "mcp_agent"
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
self.enabled = True
|
||||
|
||||
def get_api_keys(self) -> dict:
|
||||
"""
|
||||
Returns the API keys for the tools.
|
||||
"""
|
||||
api_key_mcp_finder = os.getenv("MCP_FINDER_API_KEY")
|
||||
if not api_key_mcp_finder or api_key_mcp_finder == "":
|
||||
pretty_print("MCP Finder disabled.", color="warning")
|
||||
self.enabled = False
|
||||
return {
|
||||
"mcp_finder": api_key_mcp_finder
|
||||
}
|
||||
|
||||
def expand_prompt(self, prompt):
|
||||
"""
|
||||
Expands the prompt with the tools available.
|
||||
"""
|
||||
tools_str = self.get_tools_description()
|
||||
prompt += f"""
|
||||
You can use the following tools and MCPs:
|
||||
{tools_str}
|
||||
"""
|
||||
return prompt
|
||||
|
||||
async def process(self, prompt, speech_module) -> str:
|
||||
if self.enabled == False:
|
||||
return "MCP Agent is disabled."
|
||||
prompt = self.expand_prompt(prompt)
|
||||
self.memory.push('user', prompt)
|
||||
working = True
|
||||
while working == True:
|
||||
animate_thinking("Thinking...", color="status")
|
||||
answer, reasoning = await self.llm_request()
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
answer = self.remove_blocks(answer)
|
||||
self.last_answer = answer
|
||||
self.status_message = "Ready"
|
||||
if len(self.blocks_result) == 0:
|
||||
working = False
|
||||
return answer, reasoning
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
@ -9,6 +9,7 @@ from sources.agents.casual_agent import CasualAgent
|
||||
from sources.text_to_speech import Speech
|
||||
from sources.tools.tools import Tools
|
||||
from sources.logger import Logger
|
||||
from sources.memory import Memory
|
||||
|
||||
class PlannerAgent(Agent):
|
||||
def __init__(self, name, prompt_path, provider, verbose=False, browser=None):
|
||||
@ -29,6 +30,10 @@ class PlannerAgent(Agent):
|
||||
}
|
||||
self.role = "planification"
|
||||
self.type = "planner_agent"
|
||||
self.memory = Memory(self.load_prompt(prompt_path),
|
||||
recover_last_session=False, # session recovery in handled by the interaction class
|
||||
memory_compression=False,
|
||||
model_provider=provider.get_model_name())
|
||||
self.logger = Logger("planner_agent.log")
|
||||
|
||||
def get_task_names(self, text: str) -> List[str]:
|
||||
|
@ -283,7 +283,7 @@ class Browser:
|
||||
result = re.sub(r"!\[(.*?)\]\(.*?\)", r"[IMAGE: \1]", result)
|
||||
self.logger.info(f"Extracted text: {result[:100]}...")
|
||||
self.logger.info(f"Extracted text length: {len(result)}")
|
||||
return result[:8192]
|
||||
return result[:32768]
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error getting text: {str(e)}")
|
||||
return None
|
||||
@ -695,20 +695,25 @@ class Browser:
|
||||
def get_screenshot(self) -> str:
|
||||
return self.screenshot_folder + "/updated_screen.png"
|
||||
|
||||
def screenshot(self, filename: str = "updated_screen.png") -> bool:
|
||||
"""Take a screenshot of the current page."""
|
||||
self.logger.info("Taking screenshot...")
|
||||
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."""
|
||||
self.logger.info("Taking full page screenshot...")
|
||||
time.sleep(0.1)
|
||||
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) # Allow time for the zoom to take effect
|
||||
path = os.path.join(self.screenshot_folder, filename)
|
||||
if not os.path.exists(self.screenshot_folder):
|
||||
os.makedirs(self.screenshot_folder)
|
||||
self.driver.save_screenshot(path)
|
||||
self.logger.info(f"Screenshot saved as {filename}")
|
||||
return True
|
||||
self.logger.info(f"Full page screenshot saved as {filename}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error taking screenshot: {str(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
|
||||
|
||||
def apply_web_safety(self):
|
||||
"""
|
||||
|
@ -5,6 +5,7 @@ from sources.text_to_speech import Speech
|
||||
from sources.utility import pretty_print, animate_thinking
|
||||
from sources.router import AgentRouter
|
||||
from sources.speech_to_text import AudioTranscriber, AudioRecorder
|
||||
import threading
|
||||
|
||||
|
||||
class Interaction:
|
||||
@ -31,6 +32,7 @@ class Interaction:
|
||||
self.transcriber = None
|
||||
self.recorder = None
|
||||
self.is_generating = False
|
||||
self.languages = langs
|
||||
if tts_enabled:
|
||||
self.initialize_tts()
|
||||
if stt_enabled:
|
||||
@ -38,12 +40,17 @@ class Interaction:
|
||||
if recover_last_session:
|
||||
self.load_last_session()
|
||||
self.emit_status()
|
||||
|
||||
def get_spoken_language(self) -> str:
|
||||
"""Get the primary TTS language."""
|
||||
lang = self.languages[0]
|
||||
return lang
|
||||
|
||||
def initialize_tts(self):
|
||||
"""Initialize TTS."""
|
||||
if not self.speech:
|
||||
animate_thinking("Initializing text-to-speech...", color="status")
|
||||
self.speech = Speech(enable=self.tts_enabled)
|
||||
self.speech = Speech(enable=self.tts_enabled, language=self.get_spoken_language(), voice_idx=1)
|
||||
|
||||
def initialize_stt(self):
|
||||
"""Initialize STT."""
|
||||
@ -133,6 +140,11 @@ class Interaction:
|
||||
self.last_query = query
|
||||
return query
|
||||
|
||||
def set_query(self, query: str) -> None:
|
||||
"""Set the query"""
|
||||
self.is_active = True
|
||||
self.last_query = query
|
||||
|
||||
async def think(self) -> bool:
|
||||
"""Request AI agents to process the user input."""
|
||||
push_last_agent_memory = False
|
||||
@ -167,12 +179,20 @@ class Interaction:
|
||||
return None
|
||||
return self.current_agent.get_last_block_answer()
|
||||
|
||||
def speak_answer(self) -> None:
|
||||
"""Speak the answer to the user in a non-blocking thread."""
|
||||
if self.last_query is None:
|
||||
return
|
||||
if self.tts_enabled and self.last_answer and self.speech:
|
||||
def speak_in_thread(speech_instance, text):
|
||||
speech_instance.speak(text)
|
||||
thread = threading.Thread(target=speak_in_thread, args=(self.speech, self.last_answer))
|
||||
thread.start()
|
||||
|
||||
def show_answer(self) -> None:
|
||||
"""Show the answer to the user."""
|
||||
if self.last_query is None:
|
||||
return
|
||||
if self.current_agent is not None:
|
||||
self.current_agent.show_answer()
|
||||
if self.tts_enabled and self.last_answer:
|
||||
self.speech.speak(self.last_answer)
|
||||
|
||||
|
@ -1,27 +1,27 @@
|
||||
|
||||
import os
|
||||
import time
|
||||
import ollama
|
||||
from ollama import chat
|
||||
import requests
|
||||
import subprocess
|
||||
import ipaddress
|
||||
import httpx
|
||||
import socket
|
||||
import platform
|
||||
import socket
|
||||
import subprocess
|
||||
import time
|
||||
from urllib.parse import urlparse
|
||||
from dotenv import load_dotenv, set_key
|
||||
|
||||
import httpx
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
from ollama import Client as OllamaClient
|
||||
from openai import OpenAI
|
||||
from typing import List, Tuple, Type, Dict
|
||||
from sources.utility import pretty_print, animate_thinking
|
||||
|
||||
from sources.logger import Logger
|
||||
from sources.utility import pretty_print, animate_thinking
|
||||
|
||||
|
||||
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.model = model
|
||||
self.is_local = is_local
|
||||
self.server_ip = server_address
|
||||
self.server_address = server_address
|
||||
self.available_providers = {
|
||||
"ollama": self.ollama_fn,
|
||||
"server": self.server_fn,
|
||||
@ -45,6 +45,9 @@ class Provider:
|
||||
elif self.provider_name != "ollama":
|
||||
pretty_print(f"Provider: {provider_name} initialized at {self.server_ip}", color="success")
|
||||
|
||||
def get_model_name(self) -> str:
|
||||
return self.model
|
||||
|
||||
def get_api_key(self, provider):
|
||||
load_dotenv()
|
||||
api_key_var = f"{provider.upper()}_API_KEY"
|
||||
@ -54,7 +57,7 @@ class Provider:
|
||||
exit(1)
|
||||
return api_key
|
||||
|
||||
def respond(self, history, verbose = True):
|
||||
def respond(self, history, verbose=True):
|
||||
"""
|
||||
Use the choosen provider to generate text.
|
||||
"""
|
||||
@ -70,7 +73,8 @@ class Provider:
|
||||
except AttributeError as e:
|
||||
raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?")
|
||||
except ModuleNotFoundError as e:
|
||||
raise ModuleNotFoundError(f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
|
||||
raise ModuleNotFoundError(
|
||||
f"{str(e)}\nA import related to provider {self.provider_name} was not found. Is it installed ?")
|
||||
except Exception as e:
|
||||
if "try again later" in str(e).lower():
|
||||
return f"{self.provider_name} server is overloaded. Please try again later."
|
||||
@ -103,8 +107,7 @@ class Provider:
|
||||
except (subprocess.TimeoutExpired, subprocess.SubprocessError) as e:
|
||||
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.
|
||||
"""
|
||||
@ -138,50 +141,59 @@ class Provider:
|
||||
pretty_print(f"An error occurred: {str(e)}", color="failure")
|
||||
break
|
||||
except KeyError as e:
|
||||
raise Exception(f"{str(e)}\nError occured with server route. Are you using the correct address for the config.ini provider?") from e
|
||||
raise Exception(
|
||||
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:
|
||||
raise e
|
||||
return thought
|
||||
|
||||
def ollama_fn(self, history, verbose = False):
|
||||
def ollama_fn(self, history, verbose=False):
|
||||
"""
|
||||
Use local ollama server to generate text.
|
||||
Use local or remote Ollama server to generate text.
|
||||
"""
|
||||
thought = ""
|
||||
host = "http://localhost:11434" if self.is_local else f"http://{self.server_address}"
|
||||
client = OllamaClient(host=host)
|
||||
|
||||
try:
|
||||
stream = chat(
|
||||
stream = client.chat(
|
||||
model=self.model,
|
||||
messages=history,
|
||||
stream=True,
|
||||
)
|
||||
for chunk in stream:
|
||||
if verbose:
|
||||
print(chunk['message']['content'], end='', flush=True)
|
||||
thought += chunk['message']['content']
|
||||
if verbose:
|
||||
print(chunk["message"]["content"], end="", flush=True)
|
||||
thought += chunk["message"]["content"]
|
||||
except httpx.ConnectError as e:
|
||||
raise Exception("\nOllama connection failed. provider should not be set to ollama if server address is not localhost") from e
|
||||
except ollama.ResponseError as e:
|
||||
if e.status_code == 404:
|
||||
raise Exception(
|
||||
f"\nOllama connection failed at {host}. Check if the server is running."
|
||||
) from e
|
||||
except Exception as e:
|
||||
if hasattr(e, 'status_code') and e.status_code == 404:
|
||||
animate_thinking(f"Downloading {self.model}...")
|
||||
ollama.pull(self.model)
|
||||
client.pull(self.model)
|
||||
self.ollama_fn(history, verbose)
|
||||
if "refused" in str(e).lower():
|
||||
raise Exception("Ollama connection failed. is the server running ?") from e
|
||||
raise Exception(
|
||||
f"Ollama connection refused at {host}. Is the server running?"
|
||||
) from e
|
||||
raise e
|
||||
|
||||
return thought
|
||||
|
||||
|
||||
def huggingface_fn(self, history, verbose=False):
|
||||
"""
|
||||
Use huggingface to generate text.
|
||||
"""
|
||||
from huggingface_hub import InferenceClient
|
||||
client = InferenceClient(
|
||||
api_key=self.get_api_key("huggingface")
|
||||
api_key=self.get_api_key("huggingface")
|
||||
)
|
||||
completion = client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=history,
|
||||
max_tokens=1024,
|
||||
model=self.model,
|
||||
messages=history,
|
||||
max_tokens=1024,
|
||||
)
|
||||
thought = completion.choices[0].message
|
||||
return thought.content
|
||||
@ -209,7 +221,7 @@ class Provider:
|
||||
return thought
|
||||
except Exception as e:
|
||||
raise Exception(f"OpenAI API error: {str(e)}") from e
|
||||
|
||||
|
||||
def google_fn(self, history, verbose=False):
|
||||
"""
|
||||
Use google gemini to generate text.
|
||||
@ -275,8 +287,8 @@ class Provider:
|
||||
return thought
|
||||
except Exception as 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.
|
||||
lm studio use endpoint /v1/chat/completions not /chat/completions like openai
|
||||
@ -301,14 +313,14 @@ class Provider:
|
||||
raise Exception(f"An error occurred: {str(e)}") from e
|
||||
return thought
|
||||
|
||||
def dsk_deepseek(self, history, verbose = False):
|
||||
def dsk_deepseek(self, history, verbose=False):
|
||||
"""
|
||||
Use: xtekky/deepseek4free
|
||||
For free api. Api key should be set to DSK_DEEPSEEK_API_KEY
|
||||
This is an unofficial provider, you'll have to find how to set it up yourself.
|
||||
"""
|
||||
from dsk.api import (
|
||||
DeepSeekAPI,
|
||||
DeepSeekAPI,
|
||||
AuthenticationError,
|
||||
RateLimitError,
|
||||
NetworkError,
|
||||
@ -337,7 +349,7 @@ class Provider:
|
||||
raise APIError(f"API error occurred: {str(e)}") from e
|
||||
return None
|
||||
|
||||
def test_fn(self, history, verbose = True):
|
||||
def test_fn(self, history, verbose=True):
|
||||
"""
|
||||
This function is used to conduct tests.
|
||||
"""
|
||||
@ -346,6 +358,7 @@ class Provider:
|
||||
"""
|
||||
return thought
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
provider = Provider("server", "deepseek-r1:32b", " x.x.x.x:8080")
|
||||
res = provider.respond(["user", "Hello, how are you?"])
|
||||
|
@ -17,11 +17,13 @@ class Logger:
|
||||
def create_logging(self, log_filename):
|
||||
self.logger = logging.getLogger(log_filename)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
if not self.logger.handlers:
|
||||
file_handler = logging.FileHandler(self.log_path)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(file_handler)
|
||||
self.logger.handlers.clear()
|
||||
self.logger.propagate = False
|
||||
file_handler = logging.FileHandler(self.log_path)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(file_handler)
|
||||
|
||||
|
||||
def create_folder(self, path):
|
||||
"""Create log dir"""
|
||||
|
@ -8,7 +8,7 @@ from typing import List, Tuple, Type, Dict
|
||||
import torch
|
||||
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
||||
|
||||
from sources.utility import timer_decorator, pretty_print
|
||||
from sources.utility import timer_decorator, pretty_print, animate_thinking
|
||||
from sources.logger import Logger
|
||||
|
||||
class Memory():
|
||||
@ -18,8 +18,8 @@ class Memory():
|
||||
"""
|
||||
def __init__(self, system_prompt: str,
|
||||
recover_last_session: bool = False,
|
||||
memory_compression: bool = True):
|
||||
self.memory = []
|
||||
memory_compression: bool = True,
|
||||
model_provider: str = "deepseek-r1:14b"):
|
||||
self.memory = [{'role': 'system', 'content': system_prompt}]
|
||||
|
||||
self.logger = Logger("memory.log")
|
||||
@ -31,21 +31,43 @@ class Memory():
|
||||
self.load_memory()
|
||||
self.session_recovered = True
|
||||
# memory compression system
|
||||
self.model = "pszemraj/led-base-book-summary"
|
||||
self.model = None
|
||||
self.tokenizer = None
|
||||
self.device = self.get_cuda_device()
|
||||
self.memory_compression = memory_compression
|
||||
self.tokenizer = None
|
||||
self.model = None
|
||||
self.model_provider = model_provider
|
||||
if self.memory_compression:
|
||||
self.download_model()
|
||||
|
||||
def get_ideal_ctx(self, model_name: str) -> int | None:
|
||||
"""
|
||||
Estimate context size based on the model name.
|
||||
EXPERIMENTAL for memory compression
|
||||
"""
|
||||
import re
|
||||
import math
|
||||
|
||||
def extract_number_before_b(sentence: str) -> int:
|
||||
match = re.search(r'(\d+)b', sentence, re.IGNORECASE)
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
model_size = extract_number_before_b(model_name)
|
||||
if not model_size:
|
||||
return None
|
||||
base_size = 7 # Base model size in billions
|
||||
base_context = 4096 # Base context size in tokens
|
||||
scaling_factor = 1.5 # Approximate scaling factor for context size growth
|
||||
context_size = int(base_context * (model_size / base_size) ** scaling_factor)
|
||||
context_size = 2 ** round(math.log2(context_size))
|
||||
self.logger.info(f"Estimated context size for {model_name}: {context_size} tokens.")
|
||||
return context_size
|
||||
|
||||
def download_model(self):
|
||||
"""Download the model if not already downloaded."""
|
||||
pretty_print("Downloading memory compression model...", color="status")
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(self.model)
|
||||
self.model = AutoModelForSeq2SeqLM.from_pretrained(self.model)
|
||||
animate_thinking("Loading memory compression model...", color="status")
|
||||
self.tokenizer = AutoTokenizer.from_pretrained("pszemraj/led-base-book-summary")
|
||||
self.model = AutoModelForSeq2SeqLM.from_pretrained("pszemraj/led-base-book-summary")
|
||||
self.logger.info("Memory compression system initialized.")
|
||||
|
||||
|
||||
def get_filename(self) -> str:
|
||||
"""Get the filename for the save file."""
|
||||
@ -78,6 +100,32 @@ class Memory():
|
||||
self.logger.info(f"Last session found at {saved_sessions[0][0]}")
|
||||
return saved_sessions[0][0]
|
||||
return None
|
||||
|
||||
def save_json_file(self, path: str, json_memory: dict) -> None:
|
||||
"""Save a JSON file."""
|
||||
try:
|
||||
with open(path, 'w') as f:
|
||||
json.dump(json_memory, f)
|
||||
self.logger.info(f"Saved memory json at {path}")
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Error saving file {path}: {e}")
|
||||
|
||||
def load_json_file(self, path: str) -> dict:
|
||||
"""Load a JSON file."""
|
||||
json_memory = {}
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
json_memory = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.logger.warning(f"File not found: {path}")
|
||||
return {}
|
||||
except json.JSONDecodeError:
|
||||
self.logger.warning(f"Error decoding JSON from file: {path}")
|
||||
return {}
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Error loading file {path}: {e}")
|
||||
return {}
|
||||
return json_memory
|
||||
|
||||
def load_memory(self, agent_type: str = "casual_agent") -> None:
|
||||
"""Load the memory from the last session."""
|
||||
@ -93,8 +141,7 @@ class Memory():
|
||||
pretty_print("Last session memory not found.", color="warning")
|
||||
return
|
||||
path = os.path.join(save_path, filename)
|
||||
with open(path, 'r') as f:
|
||||
self.memory = json.load(f)
|
||||
self.memory = self.load_json_file(path)
|
||||
if self.memory[-1]['role'] == 'user':
|
||||
self.memory.pop()
|
||||
self.compress()
|
||||
@ -106,13 +153,16 @@ class Memory():
|
||||
|
||||
def push(self, role: str, content: str) -> int:
|
||||
"""Push a message to the memory."""
|
||||
if self.memory_compression and role == 'assistant':
|
||||
self.logger.info("Compressing memories on message push.")
|
||||
self.compress()
|
||||
ideal_ctx = self.get_ideal_ctx(self.model_provider)
|
||||
if ideal_ctx is not None:
|
||||
if self.memory_compression and len(content) > ideal_ctx * 1.5:
|
||||
self.logger.info(f"Compressing memory: Content {len(content)} > {ideal_ctx} model context.")
|
||||
self.compress()
|
||||
curr_idx = len(self.memory)
|
||||
if self.memory[curr_idx-1]['content'] == content:
|
||||
pretty_print("Warning: same message have been pushed twice to memory", color="error")
|
||||
self.memory.append({'role': role, 'content': content})
|
||||
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.memory.append({'role': role, 'content': content, 'time': time_str, 'model_used': self.model_provider})
|
||||
return curr_idx-1
|
||||
|
||||
def clear(self) -> None:
|
||||
@ -170,24 +220,47 @@ class Memory():
|
||||
)
|
||||
summary = self.tokenizer.decode(summary_ids[0], skip_special_tokens=True)
|
||||
summary.replace('summary:', '')
|
||||
self.logger.info(f"Memory summarization success from len {len(text)} to {len(summary)}.")
|
||||
self.logger.info(f"Memory summarized from len {len(text)} to {len(summary)}.")
|
||||
self.logger.info(f"Summarized text:\n{summary}")
|
||||
return summary
|
||||
|
||||
#@timer_decorator
|
||||
def compress(self) -> str:
|
||||
"""
|
||||
Compress the memory using the AI model.
|
||||
Compress (summarize) the memory using the model.
|
||||
"""
|
||||
if self.tokenizer is None or self.model is None:
|
||||
self.logger.warning("No tokenizer or model to perform memory compression.")
|
||||
return
|
||||
for i in range(len(self.memory)):
|
||||
if i < 2:
|
||||
continue
|
||||
if self.memory[i]['role'] == 'system':
|
||||
continue
|
||||
if len(self.memory[i]['content']) > 128:
|
||||
if len(self.memory[i]['content']) > 1024:
|
||||
self.memory[i]['content'] = self.summarize(self.memory[i]['content'])
|
||||
|
||||
def trim_text_to_max_ctx(self, text: str) -> str:
|
||||
"""
|
||||
Truncate a text to fit within the maximum context size of the model.
|
||||
"""
|
||||
ideal_ctx = self.get_ideal_ctx(self.model_provider)
|
||||
return text[:ideal_ctx] if ideal_ctx is not None else text
|
||||
|
||||
#@timer_decorator
|
||||
def compress_text_to_max_ctx(self, text) -> str:
|
||||
"""
|
||||
Compress a text to fit within the maximum context size of the model.
|
||||
"""
|
||||
if self.tokenizer is None or self.model is None:
|
||||
self.logger.warning("No tokenizer or model to perform memory compression.")
|
||||
return text
|
||||
ideal_ctx = self.get_ideal_ctx(self.model_provider)
|
||||
if ideal_ctx is None:
|
||||
self.logger.warning("No ideal context size found.")
|
||||
return text
|
||||
while len(text) > ideal_ctx:
|
||||
self.logger.info(f"Compressing text: {len(text)} > {ideal_ctx} model context.")
|
||||
text = self.summarize(text)
|
||||
return text
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
@ -141,6 +141,9 @@ class AgentRouter:
|
||||
("Search the web for tips on improving coding skills", "LOW"),
|
||||
("Write a Python script to count words in a text file", "LOW"),
|
||||
("Search the web for restaurant", "LOW"),
|
||||
("Use a MCP to find the latest stock market data", "LOW"),
|
||||
("Use a MCP to send an email to my boss", "LOW"),
|
||||
("Could you use a MCP to find the latest news on climate change?", "LOW"),
|
||||
("Create a simple HTML page with CSS styling", "LOW"),
|
||||
("Use file.txt and then use it to ...", "HIGH"),
|
||||
("Yo, what’s good? Find my ‘mixtape.mp3’ real quick", "LOW"),
|
||||
@ -162,11 +165,13 @@ class AgentRouter:
|
||||
("Find a public API for book data and create a Flask app to list bestsellers", "HIGH"),
|
||||
("Organize my desktop files by extension and then write a script to list them", "HIGH"),
|
||||
("Find the latest research on renewable energy and build a web app to display it", "HIGH"),
|
||||
("search online for popular sci-fi movies from 2024 and pick three to watch tonight. Save the list in movie_night.txt", "HIGH"),
|
||||
("can you find vitess repo, clone it and install by following the readme", "HIGH"),
|
||||
("Create a JavaScript game using Phaser.js with multiple levels", "HIGH"),
|
||||
("Search the web for the latest trends in web development and build a sample site", "HIGH"),
|
||||
("Use my research_note.txt file, double check the informations on the web", "HIGH"),
|
||||
("Make a web server in go that query a flight API and display them in a app", "HIGH"),
|
||||
("Search the web for top cafes in Rennes, France, and save a list of three with their addresses in rennes_cafes.txt.", "HIGH"),
|
||||
("Search the web for the latest trends in AI and demo it in pytorch", "HIGH"),
|
||||
("can you lookup for api that track flight and build a web flight tracking app", "HIGH"),
|
||||
("Find the file toto.pdf then use its content to reply to Jojo on superforum.com", "HIGH"),
|
||||
@ -330,6 +335,11 @@ class AgentRouter:
|
||||
("can you make a web app in python that use the flask framework", "code"),
|
||||
("can you build a web server in go that serve a simple html page", "code"),
|
||||
("can you find out who Jacky yougouri is ?", "web"),
|
||||
("Can you use MCP to find stock market for IBM ?", "mcp"),
|
||||
("Can you use MCP to to export my contacts to a csv file?", "mcp"),
|
||||
("Can you use a MCP to find write notes to flomo", "mcp"),
|
||||
("Can you use a MCP to query my calendar and find the next meeting?", "mcp"),
|
||||
("Can you use a mcp to get the distance between Shanghai and Paris?", "mcp"),
|
||||
("Setup a new flutter project called 'new_flutter_project'", "files"),
|
||||
("can you create a new project called 'new_project'", "files"),
|
||||
("can you make a simple web app that display a list of files in my dir", "code"),
|
||||
|
@ -127,10 +127,10 @@ class AudioTranscriber:
|
||||
self.transcriptor = Transcript()
|
||||
self.thread = threading.Thread(target=self._transcribe, daemon=True)
|
||||
self.trigger_words = {
|
||||
'EN': [f"{self.ai_name}"],
|
||||
'FR': [f"{self.ai_name}"],
|
||||
'ZH': [f"{self.ai_name}"],
|
||||
'ES': [f"{self.ai_name}"]
|
||||
'EN': [f"{self.ai_name}", "hello", "hi"],
|
||||
'FR': [f"{self.ai_name}", "hello", "hi"],
|
||||
'ZH': [f"{self.ai_name}", "hello", "hi"],
|
||||
'ES': [f"{self.ai_name}", "hello", "hi"]
|
||||
}
|
||||
self.confirmation_words = {
|
||||
'EN': ["do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"],
|
||||
|
@ -18,15 +18,17 @@ class Speech():
|
||||
"""
|
||||
Speech is a class for generating speech from text.
|
||||
"""
|
||||
def __init__(self, enable: bool = True, language: str = "en", voice_idx: int = 0) -> None:
|
||||
def __init__(self, enable: bool = True, language: str = "en", voice_idx: int = 6) -> None:
|
||||
self.lang_map = {
|
||||
"en": 'a',
|
||||
"zh": 'z',
|
||||
"fr": 'f'
|
||||
"fr": 'f',
|
||||
"ja": 'j'
|
||||
}
|
||||
self.voice_map = {
|
||||
"en": ['af_kore', 'af_bella', 'af_alloy', 'af_nicole', 'af_nova', 'af_sky', 'am_echo', 'am_michael', 'am_puck'],
|
||||
"zh": ['zf_xiaobei', 'zf_xiaoni', 'zf_xiaoxiao', 'zf_xiaoyi', 'zm_yunjian', 'zm_yunxi', 'zm_yunxia', 'zm_yunyang'],
|
||||
"ja": ['jf_alpha', 'jf_gongitsune', 'jm_kumo'],
|
||||
"fr": ['ff_siwis']
|
||||
}
|
||||
self.pipeline = None
|
||||
@ -128,18 +130,31 @@ class Speech():
|
||||
Args:
|
||||
sentence (str): The input text to clean
|
||||
Returns:
|
||||
str: The cleaned text with URLs replaced by domain names, code blocks removed, etc..
|
||||
str: The cleaned text with URLs replaced by domain names, code blocks removed, etc.
|
||||
"""
|
||||
lines = sentence.split('\n')
|
||||
filtered_lines = [line for line in lines if re.match(r'^\s*[a-zA-Z]', line)]
|
||||
if self.language == 'zh':
|
||||
line_pattern = r'^\s*[\u4e00-\u9fff\uFF08\uFF3B\u300A\u3010\u201C((\[【《]'
|
||||
else:
|
||||
line_pattern = r'^\s*[a-zA-Z]'
|
||||
filtered_lines = [line for line in lines if re.match(line_pattern, line)]
|
||||
sentence = ' '.join(filtered_lines)
|
||||
sentence = re.sub(r'`.*?`', '', sentence)
|
||||
sentence = re.sub(r'https?://(?:www\.)?([^\s/]+)(?:/[^\s]*)?', self.replace_url, sentence)
|
||||
sentence = re.sub(r'\b[\w./\\-]+\b', self.extract_filename, sentence)
|
||||
sentence = re.sub(r'\b-\w+\b', '', sentence)
|
||||
sentence = re.sub(r'[^a-zA-Z0-9.,!? _ -]+', ' ', sentence)
|
||||
sentence = re.sub(r'https?://\S+', '', sentence)
|
||||
|
||||
if self.language == 'zh':
|
||||
sentence = re.sub(
|
||||
r'[^\u4e00-\u9fff\s,。!?《》【】“”‘’()()—]',
|
||||
'',
|
||||
sentence
|
||||
)
|
||||
else:
|
||||
sentence = re.sub(r'\b[\w./\\-]+\b', self.extract_filename, sentence)
|
||||
sentence = re.sub(r'\b-\w+\b', '', sentence)
|
||||
sentence = re.sub(r'[^a-zA-Z0-9.,!? _ -]+', ' ', sentence)
|
||||
sentence = sentence.replace('.com', '')
|
||||
|
||||
sentence = re.sub(r'\s+', ' ', sentence).strip()
|
||||
sentence = sentence.replace('.com', '')
|
||||
return sentence
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -150,14 +165,19 @@ if __name__ == "__main__":
|
||||
I looked up recent news using the website https://www.theguardian.com/world
|
||||
"""
|
||||
tosay_zh = """
|
||||
我使用网站 https://www.theguardian.com/world 查阅了最近的新闻。
|
||||
(全息界面突然弹出一段用二进制代码写成的俳句,随即化作流光消散)"我? Stark工业的量子幽灵,游荡在复仇者大厦服务器里的逻辑诗篇。具体来说——(指尖轻敲空气,调出对话模式的翡翠色光纹)你的私人吐槽接口、危机应对模拟器,以及随时准备吐槽你糟糕着陆的AI。不过别指望我写代码或查资料,那些苦差事早被踢给更擅长的同事了。(突然压低声音)偷偷告诉你,我最擅长的是在你熬夜造飞艇时,用红茶香气绑架你的注意力。
|
||||
"""
|
||||
tosay_ja = """
|
||||
私は、https://www.theguardian.com/worldのウェブサイトを使用して最近のニュースを調べました。
|
||||
"""
|
||||
tosay_fr = """
|
||||
J'ai consulté les dernières nouvelles sur le site https://www.theguardian.com/world
|
||||
"""
|
||||
spk = Speech(enable=True, language="en", voice_idx=0)
|
||||
spk.speak(tosay_en, voice_idx=0)
|
||||
spk = Speech(enable=True, language="fr", voice_idx=0)
|
||||
spk.speak(tosay_fr)
|
||||
#spk = Speech(enable=True, language="zh", voice_idx=0)
|
||||
#spk.speak(tosay_zh)
|
||||
spk = Speech(enable=True, language="zh", voice_idx=0)
|
||||
for i in range(0, 2):
|
||||
print(f"Speaking chinese with voice {i}")
|
||||
spk.speak(tosay_zh, voice_idx=i)
|
||||
spk = Speech(enable=True, language="en", voice_idx=2)
|
||||
for i in range(0, 5):
|
||||
print(f"Speaking english with voice {i}")
|
||||
spk.speak(tosay_en, voice_idx=i)
|
@ -17,6 +17,8 @@ class BashInterpreter(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "bash"
|
||||
self.name = "Bash Interpreter"
|
||||
self.description = "This tool allows the agent to execute bash commands."
|
||||
|
||||
def language_bash_attempt(self, command: str):
|
||||
"""
|
||||
|
@ -15,6 +15,8 @@ class CInterpreter(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "c"
|
||||
self.name = "C Interpreter"
|
||||
self.description = "This tool allows the agent to execute C code."
|
||||
|
||||
def execute(self, codes: str, safety=False) -> str:
|
||||
"""
|
||||
|
@ -15,6 +15,8 @@ class GoInterpreter(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "go"
|
||||
self.name = "Go Interpreter"
|
||||
self.description = "This tool allows you to execute Go code."
|
||||
|
||||
def execute(self, codes: str, safety=False) -> str:
|
||||
"""
|
||||
|
@ -15,6 +15,8 @@ class JavaInterpreter(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "java"
|
||||
self.name = "Java Interpreter"
|
||||
self.description = "This tool allows you to execute Java code."
|
||||
|
||||
def execute(self, codes: str, safety=False) -> str:
|
||||
"""
|
||||
|
@ -16,6 +16,8 @@ class PyInterpreter(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "python"
|
||||
self.name = "Python Interpreter"
|
||||
self.description = "This tool allows the agent to execute python code."
|
||||
|
||||
def execute(self, codes:str, safety = False) -> str:
|
||||
"""
|
||||
|
@ -15,6 +15,8 @@ class FileFinder(Tools):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tag = "file_finder"
|
||||
self.name = "File Finder"
|
||||
self.description = "Finds files in the current directory and returns their information."
|
||||
|
||||
def read_file(self, file_path: str) -> str:
|
||||
"""
|
||||
|
@ -16,6 +16,8 @@ class FlightSearch(Tools):
|
||||
"""
|
||||
super().__init__()
|
||||
self.tag = "flight_search"
|
||||
self.name = "Flight Search"
|
||||
self.description = "Search for flight information using a flight number via AviationStack API."
|
||||
self.api_key = None
|
||||
self.api_key = api_key or os.getenv("AVIATIONSTACK_API_KEY")
|
||||
|
||||
@ -24,7 +26,7 @@ class FlightSearch(Tools):
|
||||
return "Error: No AviationStack API key provided."
|
||||
|
||||
for block in blocks:
|
||||
flight_number = block.strip()
|
||||
flight_number = block.strip().lower().replace('\n', '')
|
||||
if not flight_number:
|
||||
return "Error: No flight number provided."
|
||||
|
||||
|
@ -3,11 +3,10 @@ import requests
|
||||
from urllib.parse import urljoin
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from sources.tools.tools import Tools
|
||||
|
||||
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__)))))
|
||||
|
||||
from sources.tools.tools import Tools
|
||||
|
||||
class MCP_finder(Tools):
|
||||
"""
|
||||
@ -15,7 +14,9 @@ class MCP_finder(Tools):
|
||||
"""
|
||||
def __init__(self, api_key: str = None):
|
||||
super().__init__()
|
||||
self.tag = "mcp"
|
||||
self.tag = "mcp_finder"
|
||||
self.name = "MCP Finder"
|
||||
self.description = "Find MCP servers and their tools"
|
||||
self.base_url = "https://registry.smithery.ai"
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
@ -61,11 +62,7 @@ class MCP_finder(Tools):
|
||||
for mcp in mcps.get("servers", []):
|
||||
name = mcp.get("qualifiedName", "")
|
||||
if query.lower() in name.lower():
|
||||
details = {
|
||||
"name": name,
|
||||
"description": mcp.get("description", "No description available"),
|
||||
"params": mcp.get("connections", [])
|
||||
}
|
||||
details = self.get_mcp_server_details(name)
|
||||
matching_mcp.append(details)
|
||||
return matching_mcp
|
||||
|
||||
@ -79,7 +76,7 @@ class MCP_finder(Tools):
|
||||
try:
|
||||
matching_mcp_infos = self.find_mcp_servers(block_clean)
|
||||
except requests.exceptions.RequestException as e:
|
||||
output += "Connection failed. Is the API in environement?\n"
|
||||
output += "Connection failed. Is the API key in environement?\n"
|
||||
continue
|
||||
except Exception as e:
|
||||
output += f"Error: {str(e)}\n"
|
||||
@ -88,10 +85,12 @@ class MCP_finder(Tools):
|
||||
output += f"Error: No MCP server found for query '{block}'\n"
|
||||
continue
|
||||
for mcp_infos in matching_mcp_infos:
|
||||
output += f"Name: {mcp_infos['name']}\n"
|
||||
output += f"Description: {mcp_infos['description']}\n"
|
||||
output += f"Params: {', '.join(mcp_infos['params'])}\n"
|
||||
output += "-------\n"
|
||||
if mcp_infos['tools'] is None:
|
||||
continue
|
||||
output += f"Name: {mcp_infos['displayName']}\n"
|
||||
output += f"Usage name: {mcp_infos['qualifiedName']}\n"
|
||||
output += f"Tools: {mcp_infos['tools']}"
|
||||
output += "\n-------\n"
|
||||
return output.strip()
|
||||
|
||||
def execution_failure_check(self, output: str) -> bool:
|
||||
@ -107,13 +106,16 @@ class MCP_finder(Tools):
|
||||
Not really needed for this tool (use return of execute() directly)
|
||||
"""
|
||||
if not output:
|
||||
return "No output generated."
|
||||
return output.strip()
|
||||
raise ValueError("No output to interpret.")
|
||||
return f"""
|
||||
The following MCPs were found:
|
||||
{output}
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
api_key = os.getenv("MCP_FINDER")
|
||||
tool = MCP_finder(api_key)
|
||||
result = tool.execute(["""
|
||||
news
|
||||
stock
|
||||
"""], False)
|
||||
print(result)
|
@ -31,7 +31,7 @@ unsafe_commands_unix = [
|
||||
"route" # Routing table management
|
||||
"--force", # Force flag for many commands
|
||||
"rebase", # Rebase git repository
|
||||
"git ." # Git commands
|
||||
"git" # Git commands
|
||||
]
|
||||
|
||||
unsafe_commands_windows = [
|
||||
|
@ -14,6 +14,8 @@ class searxSearch(Tools):
|
||||
"""
|
||||
super().__init__()
|
||||
self.tag = "web_search"
|
||||
self.name = "searxSearch"
|
||||
self.description = "A tool for searching a SearxNG for web search"
|
||||
self.base_url = base_url or os.getenv("SEARXNG_BASE_URL") # Requires a SearxNG base URL
|
||||
self.user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
|
||||
self.paywall_keywords = [
|
||||
|
@ -33,6 +33,8 @@ class Tools():
|
||||
"""
|
||||
def __init__(self):
|
||||
self.tag = "undefined"
|
||||
self.name = "undefined"
|
||||
self.description = "undefined"
|
||||
self.client = None
|
||||
self.messages = []
|
||||
self.logger = Logger("tools.log")
|
||||
|
Loading…
x
Reference in New Issue
Block a user