Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c81c0ffde6 | ||
|
|
444e7bce22 | ||
|
|
d3f20819ff | ||
|
|
9f0fdd547e | ||
|
|
be1bfc5cf2 | ||
|
|
a3b0bb22aa | ||
|
|
fc74d4361a | ||
|
|
54cc2a03ec | ||
|
|
b96e83dbbe | ||
|
|
eadcfb66d1 | ||
|
|
1c4a550c6f | ||
|
|
95aeaf74fa | ||
|
|
ec1f7d31fb | ||
|
|
abae98cf77 | ||
|
|
819a3fb98d | ||
|
|
7d74a348c9 | ||
|
|
a3ad635728 | ||
|
|
58656ab43c | ||
|
|
c1a1e9409d | ||
|
|
41fe95fcb1 | ||
|
|
c8bccc2395 | ||
|
|
e45fa86cda | ||
|
|
c8df9e759c | ||
|
|
b3672c60c0 | ||
|
|
97460ded48 | ||
|
|
9a34ff3646 | ||
|
|
a53842b8b7 | ||
|
|
0a8d898e0b | ||
|
|
92f721886c | ||
|
|
58f46d4351 | ||
|
|
ee6687df85 | ||
|
|
8d15546771 | ||
|
|
7ad084b27f | ||
|
|
63cd5eddd7 | ||
|
|
50f9e11a35 | ||
|
|
500605d5da | ||
|
|
6ec9647d19 | ||
|
|
16b8f1a451 | ||
|
|
6d053cc3a5 | ||
|
|
27b4aaa5e9 | ||
|
|
3c19f26792 | ||
|
|
12eec50e1c | ||
|
|
cd78cb36a0 | ||
|
|
ec8cab2d6b | ||
|
|
b1f9375115 | ||
|
|
758faf6285 | ||
|
|
20457a574a | ||
|
|
d375359582 | ||
|
|
f738fc732e | ||
|
|
3be07e3dcb | ||
|
|
7d252ad422 | ||
|
|
1c73ef141c | ||
|
|
e3757f54ac | ||
|
|
12afae7472 | ||
|
|
9cbf62b47d | ||
|
|
51b0ca54c7 | ||
|
|
0f116cc3d0 | ||
|
|
a19ef5df66 | ||
|
|
195b4a07a9 | ||
|
|
e3d01083d9 | ||
|
|
102dc60efb | ||
|
|
ca2b05b35e | ||
|
|
713c01193f | ||
|
|
50a9cb8d27 | ||
|
|
bd26d7233d | ||
|
|
45fbf5a88c | ||
|
|
384d9a8c0b | ||
|
|
95d5aea1d5 | ||
|
|
38b1e17628 | ||
|
|
637ca0f826 | ||
|
|
201b3de15c | ||
|
|
4739a1377c | ||
|
|
4bb7a21604 | ||
|
|
725a3c3292 | ||
|
|
e72072090e | ||
|
|
ef91502961 | ||
|
|
2048af854f | ||
|
|
dd6ddaeca3 | ||
|
|
47fec1914a | ||
|
|
9ff69d1876 | ||
|
|
ca2eea8089 | ||
|
|
3678c091ac | ||
|
|
94fb15359b | ||
|
|
3d1b3d02d9 | ||
|
|
c769e790bc | ||
|
|
23a51e6a05 | ||
|
|
94eada9d5d | ||
|
|
2cdbb49ecd | ||
|
|
dd033d4084 | ||
|
|
de2650c007 | ||
|
|
deb79b81ca | ||
|
|
24dc1e1a2c | ||
|
|
af7619650a | ||
|
|
df645f9a02 | ||
|
|
dc6eef8031 | ||
|
|
5c391dbb6e | ||
|
|
101c103aeb | ||
|
|
de315a43a1 | ||
|
|
90f173ba52 | ||
|
|
e4591ea1b4 | ||
|
|
a7deffedec | ||
|
|
5949540007 | ||
|
|
7afb79117b | ||
|
|
442bb4a340 | ||
|
|
99467be133 | ||
|
|
887060acdf | ||
|
|
90609e960c | ||
|
|
d893928221 | ||
|
|
5bc086fd9d | ||
|
|
aca176b9e7 | ||
|
|
9707dbcbf9 | ||
|
|
52e5af8116 | ||
|
|
42058244f2 | ||
|
|
bddaa75e8c | ||
|
|
7904439f35 | ||
|
|
c873af3d00 | ||
|
|
fa2852d3e7 | ||
|
|
1c4ebefae4 | ||
|
|
96a6dd368a | ||
|
|
ed4f04b19c | ||
|
|
f325865869 | ||
|
|
a15dd998f3 | ||
|
|
f17dc0550b | ||
|
|
ed76c8415b | ||
|
|
9f2c105074 | ||
|
|
ccef61b2b9 | ||
|
|
3cf1cab68f | ||
|
|
0579fd3bb6 | ||
|
|
c6688355a7 | ||
|
|
68ed1834a9 | ||
|
|
487670d207 | ||
|
|
a109ac98ed | ||
|
|
d928a95ed1 | ||
|
|
ffa6873a86 | ||
|
|
db2eb6fbac | ||
|
|
68d471bfc6 | ||
|
|
03c71368f5 | ||
|
|
2fd83289fd | ||
|
|
7dd60a8946 | ||
|
|
ccaf1fae52 | ||
|
|
4db2ec5911 | ||
|
|
34e9baccf3 | ||
|
|
3febcfcc04 | ||
|
|
bb15199b4e | ||
|
|
4ec54b690c | ||
|
|
3f7408301a | ||
|
|
da7dde23d4 | ||
|
|
5517f53f8a | ||
|
|
9f128f8445 | ||
|
|
f02f096356 | ||
|
|
e0ffa95951 | ||
|
|
357f0e7bb1 | ||
|
|
475807a1c6 | ||
|
|
e06acd65a6 | ||
|
|
893f9ec2d8 | ||
|
|
1274a0c646 | ||
|
|
e4ae8162a0 | ||
|
|
dce9074969 | ||
|
|
b7da34ff93 | ||
|
|
c360240259 | ||
|
|
e68dc2212c | ||
|
|
e63d772959 | ||
|
|
45700d77ab | ||
|
|
f09cb8a7b5 | ||
|
|
49a36de149 | ||
|
|
309a481a69 | ||
|
|
a11445e7c0 | ||
|
|
d5c431a609 | ||
|
|
cfe19e637f | ||
|
|
cea68fed86 | ||
|
|
273f4bd858 | ||
|
|
55d5ff39ff | ||
|
|
6049db1f24 | ||
|
|
b479dd0b2f | ||
|
|
8b199869c7 | ||
|
|
b9a954a058 | ||
|
|
82aecd0aae | ||
|
|
812af254b5 | ||
|
|
a05596c416 | ||
|
|
cb251f93b5 | ||
|
|
317f1521eb | ||
|
|
9c9824c05e | ||
|
|
564a09c96d | ||
|
|
7fb5fa75ee | ||
|
|
d01ae7217f | ||
|
|
2ff93cef3c | ||
|
|
f7f33d0c79 | ||
|
|
50142b48f8 | ||
|
|
1b17b95e8c | ||
|
|
4de527aa42 | ||
|
|
de22f7218a | ||
|
|
fdb4c887c3 | ||
|
|
c6fe2865b6 | ||
|
|
5ea0b4a895 | ||
|
|
523e7f8271 | ||
|
|
c93ff60900 | ||
|
|
26e5159c1d | ||
|
|
0f09ef92dd | ||
|
|
5ac7df1854 | ||
|
|
7f3682e884 | ||
|
|
600bff8da4 | ||
|
|
c9f6d76d30 | ||
|
|
c030b55521 | ||
|
|
83c595144b | ||
|
|
3a9514629a | ||
|
|
ae8ff0640b | ||
|
|
b883b003be | ||
|
|
89f91f7831 | ||
|
|
a4f56d582b | ||
|
|
56d5ec2d37 | ||
|
|
ad9ca5e7cb | ||
|
|
36b26b43c9 | ||
|
|
3d1f42351f | ||
|
|
1a2b790f2b | ||
|
|
81b772df9a | ||
|
|
7c4f283a05 | ||
|
|
66c460adad | ||
|
|
efa1bbaecb | ||
|
|
ad21f66a44 | ||
|
|
d5a07c11db | ||
|
|
f4b0af1eb1 | ||
|
|
79400b8f52 | ||
|
|
e1706d97f2 | ||
|
|
023c183e85 | ||
|
|
77d6e23c45 | ||
|
|
ca1f12b91b | ||
|
|
f2eda0e7d7 | ||
|
|
153bd21910 | ||
|
|
a3ca718131 | ||
|
|
4342677344 | ||
|
|
26421190b1 | ||
|
|
906dc18060 | ||
|
|
2cb7ac34ce | ||
|
|
6d1edf9184 | ||
|
|
e1d55649d5 | ||
|
|
6a7a3d623e | ||
|
|
83e93dcf43 | ||
|
|
a32cf60958 | ||
|
|
14f42b638d | ||
|
|
89c3ecea68 | ||
|
|
c65e6321f5 | ||
|
|
d1954ff326 | ||
|
|
592c7e6915 | ||
|
|
ecbdcaa57e | ||
|
|
aad1b426f0 | ||
|
|
ce81133560 | ||
|
|
454e68033c | ||
|
|
8da3d2b3f8 | ||
|
|
59795c3dc3 | ||
|
|
6adc04200e | ||
|
|
f3c71d6f19 | ||
|
|
8cedd9123b | ||
|
|
7313294c69 | ||
|
|
424c5c4f7b | ||
|
|
e0f0c5c7f6 | ||
|
|
2eb97e6724 | ||
|
|
5b491ddbf7 | ||
|
|
164b741d57 | ||
|
|
dfda888e57 | ||
|
|
a6c4b5ab3d | ||
|
|
488a645cf4 | ||
|
|
68bfc0ecef | ||
|
|
f4feb42dda | ||
|
|
49fab1b488 | ||
|
|
06e6b2798b | ||
|
|
09ce9a882a | ||
|
|
2ff7e90cea | ||
|
|
a9c1f5b790 | ||
|
|
9fe561085b | ||
|
|
92f9b93353 | ||
|
|
4198e932ca | ||
|
|
00d1b01624 | ||
|
|
75e417129d | ||
|
|
46b5edfd3b | ||
|
|
e66f535dd3 | ||
|
|
4d5a532b23 | ||
|
|
369850b86d | ||
|
|
7553d9dbb6 | ||
|
|
ed2a9cc204 | ||
|
|
9a1b2b93f6 | ||
|
|
3c66eb646e | ||
|
|
82cf54706b | ||
|
|
8cfb2d1246 | ||
|
|
aa9177df0c | ||
|
|
864fb36af5 | ||
|
|
f0aaa06d15 | ||
|
|
60795111b0 | ||
|
|
469551c2b5 | ||
|
|
6eafeb15a4 | ||
|
|
d3e95712fd | ||
|
|
fecc01e230 | ||
|
|
d75735ecb0 | ||
|
|
70fcb0d70d | ||
|
|
6eee5cf350 | ||
|
|
21bf224fef | ||
|
|
139f8cdc11 | ||
|
|
ff8fdddbdc | ||
|
|
93ebb9468c | ||
|
|
a1e71fd0ce | ||
|
|
208bb5e93d | ||
|
|
416d9d00ad | ||
|
|
3550c4a448 | ||
|
|
3af3791f54 | ||
|
|
196841db50 | ||
|
|
bb67df8f42 | ||
|
|
26e9dbcd40 | ||
|
|
42f9485a39 | ||
|
|
6fb9ce67c0 | ||
|
|
06ddc45955 | ||
|
|
93c8f0f8e4 | ||
|
|
a667f89c12 | ||
|
|
8991aaae8d | ||
|
|
97708c7947 | ||
|
|
688e94d97c | ||
|
|
a09b6bf8aa | ||
|
|
f2ce720a3d | ||
|
|
a5c5061a2f | ||
|
|
5321dcc3ba | ||
|
|
ac5118c4e3 | ||
|
|
a4f28cec5d | ||
|
|
95f43be2af | ||
|
|
ff9c1576b6 | ||
|
|
4f7e30b498 | ||
|
|
d6aba5fd39 | ||
|
|
f70606b5ec | ||
|
|
92e2e8c0d6 | ||
|
|
23dce5b886 | ||
|
|
80a3391b84 | ||
|
|
8f8c2104a2 | ||
|
|
7f4c96371e | ||
|
|
46c3b7c17e | ||
|
|
319a4389ac | ||
|
|
32b3908aa3 | ||
|
|
f798e4936c | ||
|
|
e534faf115 | ||
|
|
a93dbbfb5c | ||
|
|
f0cca0ed02 | ||
|
|
0f7ad9b741 | ||
|
|
b9fc781f28 | ||
|
|
c47e921a3b | ||
|
|
7890b4b3ca | ||
|
|
f69ceb5025 | ||
|
|
aa75d276dc | ||
|
|
ffebcccd32 | ||
|
|
3b201c82db | ||
|
|
704509560a | ||
|
|
8c496d2bc2 | ||
|
|
5992fdd659 | ||
|
|
d476cf91dc | ||
|
|
02d28b4322 | ||
|
|
9e47e2bf4f | ||
|
|
95f5b9df68 | ||
|
|
1ffaf4689e | ||
|
|
140f7842cc | ||
|
|
b5311b2651 | ||
|
|
e99851fba3 | ||
|
|
3acbae5ea0 | ||
|
|
56b5db7df3 | ||
|
|
698ed78acc | ||
|
|
9c3330b45d | ||
|
|
9e5b2c5ed7 | ||
|
|
11fa4aed48 | ||
|
|
919cf1437d | ||
|
|
1b5a55ccf2 | ||
|
|
b3efd09fb3 | ||
|
|
617927c291 | ||
|
|
0ce492d083 | ||
|
|
4cf1beb49f | ||
|
|
c41c259cd6 | ||
|
|
a3e95abfde | ||
|
|
164d2b21e9 | ||
|
|
b34e343535 | ||
|
|
8ccb6f4d77 | ||
|
|
36b80dc758 | ||
|
|
927d09ffb5 | ||
|
|
a5ecd2d389 | ||
|
|
039ea71678 | ||
|
|
a0b09410b3 | ||
|
|
3dbef96cf0 | ||
|
|
69f276955a | ||
|
|
e56e5a4b3d | ||
|
|
6b31516cd9 | ||
|
|
61d83e6614 | ||
|
|
4d0130c297 | ||
|
|
7331cb7cb2 | ||
|
|
8c431c690e | ||
|
|
f60406d0f1 | ||
|
|
7e18d78805 | ||
|
|
cd1833f3ad | ||
|
|
45818b1eba | ||
|
|
9561ca95a1 | ||
|
|
875ab3bd8e | ||
|
|
1dd8e0a016 | ||
|
|
5862c98f3e | ||
|
|
ddb533a255 | ||
|
|
cc951d4745 | ||
|
|
557f7aa333 | ||
|
|
e0eee90202 | ||
|
|
d8ded2d456 | ||
|
|
862a78276f | ||
|
|
e69cff0735 | ||
|
|
f42a31578e | ||
|
|
90894f806a | ||
|
|
5c9ada9468 | ||
|
|
cf1d3d0ba1 | ||
|
|
58d52ad61f | ||
|
|
0c3a07f208 | ||
|
|
44e0508ae5 | ||
|
|
4676b817e9 | ||
|
|
32b17c3373 | ||
|
|
7e95498f7a | ||
|
|
4712d39427 | ||
|
|
4c87353db4 | ||
|
|
d1b20a1446 | ||
|
|
18f23db0fa | ||
|
|
8106cff45f | ||
|
|
75ac1631c4 | ||
|
|
021ef0cdc1 | ||
|
|
c995d2a47c | ||
|
|
de76fe14ea | ||
|
|
ca50b1f2d0 | ||
|
|
a4cfa9c651 | ||
|
|
430d032095 | ||
|
|
0bf813e865 | ||
|
|
a65f54e9a1 | ||
|
|
0c7ce90980 | ||
|
|
4aba1bc7cb | ||
|
|
cf1ef1c819 | ||
|
|
855e376610 | ||
|
|
c6eeabce62 | ||
|
|
5e7b1ff0ba | ||
|
|
2b512b1315 | ||
|
|
dae2c224e5 | ||
|
|
fcda0abc21 | ||
|
|
c862d496e3 | ||
|
|
7968f83bf8 | ||
|
|
8cba1bad43 | ||
|
|
bc6365567f | ||
|
|
6c4e8adda1 | ||
|
|
c22ec9b074 | ||
|
|
3e753bcf97 | ||
|
|
6ba95de6e6 | ||
|
|
064d41588c | ||
|
|
582462a73f | ||
|
|
f58e7f04f1 | ||
|
|
bd951e19d3 | ||
|
|
91e99a2dbf | ||
|
|
a7d2beabe0 | ||
|
|
2f78d033ab | ||
|
|
cce74b29ad | ||
|
|
aa1c0a24e2 | ||
|
|
cf4d9b63c7 | ||
|
|
f0802c3035 | ||
|
|
86da6acf3f | ||
|
|
697bc882c7 | ||
|
|
8a0ffc940e | ||
|
|
63e947bf84 | ||
|
|
24329aa3d2 | ||
|
|
58bdaca252 | ||
|
|
6249049bcc | ||
|
|
70e89d9203 | ||
|
|
39f053eee4 | ||
|
|
4cfcb28c60 | ||
|
|
1027a2a77b | ||
|
|
5dd3ffd9ef | ||
|
|
2aa31ac911 | ||
|
|
3d49e0aabe | ||
|
|
8c425f62b6 | ||
|
|
9080697dc0 | ||
|
|
279bdf8c7e | ||
|
|
7d67ae2562 | ||
|
|
8922350379 | ||
|
|
df922b18a7 | ||
|
|
5d08565ff1 | ||
|
|
757a9b1e3e | ||
|
|
32bc096d9a | ||
|
|
5b52dcc7fe | ||
|
|
d871c378fe | ||
|
|
68bc60f6f4 | ||
|
|
47a3e71b01 | ||
|
|
dfa6fadf2d | ||
|
|
dffd8b5299 | ||
|
|
85f8dcef98 | ||
|
|
d3884c6eca | ||
|
|
98e2d8ad7a | ||
|
|
cde602d77d | ||
|
|
af168d2c57 | ||
|
|
7a3fd2150b | ||
|
|
489dac5488 | ||
|
|
928bfd3d97 | ||
|
|
98916b4404 | ||
|
|
48baf7812d | ||
|
|
e153efe9e4 |
@@ -0,0 +1,18 @@
|
|||||||
|
# Python cache files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
agentic_seek_env/
|
||||||
|
.agentic_seek_env/
|
||||||
|
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Git metadata
|
||||||
|
.git/
|
||||||
|
|
||||||
|
# macOS Finder files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
@@ -1,2 +1,12 @@
|
|||||||
SEARXNG_BASE_URL="http://127.0.0.1:8080"
|
SEARXNG_BASE_URL="http://127.0.0.1:8080"
|
||||||
OPENAI_API_KEY='dont share this, not needed for local providers'
|
REDIS_BASE_URL="redis://redis:6379/0"
|
||||||
|
WORK_DIR="/Users/username/Documents/workspace_with_my_files"
|
||||||
|
OLLAMA_PORT="11434"
|
||||||
|
LM_STUDIO_PORT="1234"
|
||||||
|
CUSTOM_ADDITIONAL_LLM_PORT="11435"
|
||||||
|
OPENAI_API_KEY='xxxxx'
|
||||||
|
DEEPSEEK_API_KEY='xxxxx'
|
||||||
|
OPENROUTER_API_KEY='xxxxx'
|
||||||
|
TOGETHER_API_KEY='xxxxx'
|
||||||
|
GOOGLE_API_KEY='xxxxx'
|
||||||
|
ANTHROPIC_API_KEY='xxxxx'
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [Fosowl ]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
|
||||||
@@ -1,11 +1,38 @@
|
|||||||
*.wav
|
*.wav
|
||||||
config.ini
|
*.DS_Store
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.safetensors
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
cookies.json
|
||||||
|
test_agent.py
|
||||||
|
config.ini
|
||||||
|
.voices/
|
||||||
experimental/
|
experimental/
|
||||||
|
chrome_bundle/
|
||||||
|
.logs/
|
||||||
|
.screenshots/*.png
|
||||||
|
.screenshots/*.jpg
|
||||||
conversations/
|
conversations/
|
||||||
agentic_env/*
|
agentic_env/*
|
||||||
|
agentic_seek_env/*
|
||||||
.env
|
.env
|
||||||
*/.env
|
*/.env
|
||||||
|
dsk/
|
||||||
|
chrome136/
|
||||||
|
|
||||||
|
### react ###
|
||||||
|
.DS_*
|
||||||
|
*.log
|
||||||
|
logs
|
||||||
|
**/*.backup.*
|
||||||
|
**/*.back.*
|
||||||
|
node_modules
|
||||||
|
bower_components
|
||||||
|
*.sublime*
|
||||||
|
psd
|
||||||
|
thumb
|
||||||
|
sketch
|
||||||
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ repos:
|
|||||||
- id: trufflehog
|
- id: trufflehog
|
||||||
name: TruffleHog
|
name: TruffleHog
|
||||||
description: Detect secrets in your data.
|
description: Detect secrets in your data.
|
||||||
entry: bash -c 'trufflehog git file://. --since-commit HEAD --results=verified,unknown --fail --no-update'
|
entry: bash -c 'trufflehog git file://. --since-commit HEAD --results=verified,unknown --no-update'
|
||||||
language: system
|
language: system
|
||||||
stages: ["commit", "push"]
|
stages: ["commit", "push"]
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
# Contributors guide
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Python 3.8 or higher
|
|
||||||
- Ollama installed (for local model execution)
|
|
||||||
- Basic familiarity with Python and AI models
|
|
||||||
|
|
||||||
## Contribution Guidelines
|
|
||||||
|
|
||||||
We welcome contributions in the following areas:
|
|
||||||
|
|
||||||
- Code Improvements: Optimize existing code, fix bugs, or add new features.
|
|
||||||
- Documentation: Improve the README, write tutorials, or add inline comments.
|
|
||||||
- Testing: Write unit tests, integration tests, or help with debugging.
|
|
||||||
- New Features: Implement new tools, agents, or integrations.
|
|
||||||
|
|
||||||
## Steps to Contribute
|
|
||||||
|
|
||||||
Fork the project to your GitHub account.
|
|
||||||
|
|
||||||
Create a Branch:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git checkout -b feature/your-feature-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Make Your Changes.
|
|
||||||
|
|
||||||
Write your code, add documentation, or fix bugs.
|
|
||||||
|
|
||||||
Test Your Changes.
|
|
||||||
|
|
||||||
Ensure your changes work as expected and do not break existing functionality.
|
|
||||||
|
|
||||||
Push your changes to your fork and submit a pull request to the main branch of this repository. Provide a clear description of your changes and reference any related issues.
|
|
||||||
|
|
||||||
## Coding Philosophy
|
|
||||||
|
|
||||||
1. **Privacy First, Always Local**
|
|
||||||
- All core functionality must be able to run 100% locally
|
|
||||||
- Cloud services should only be optional alternatives, clearly defined with a warning message.
|
|
||||||
- User data privacy is non-negotiable
|
|
||||||
|
|
||||||
2. **Agent-Based Architecture**
|
|
||||||
- Each agent should have a clear, single responsibility
|
|
||||||
- Agents should be modular and independently testable
|
|
||||||
- New agents should solve specific use cases
|
|
||||||
|
|
||||||
3. **Tool-Based Extensibility**
|
|
||||||
- Tools should be self-contained and follow the Tools base class
|
|
||||||
- Each tool should do one thing well
|
|
||||||
- Tools should provide clear feedback on success/failure
|
|
||||||
|
|
||||||
4. **User Experience**
|
|
||||||
- Provide meaningful feedback for all operations
|
|
||||||
- Support multiple languages (chinese, french, english for now)
|
|
||||||
- Text to speech with short response.
|
|
||||||
- Keep responses concise
|
|
||||||
|
|
||||||
5. **Code Quality**
|
|
||||||
- Write clear, self-documenting code
|
|
||||||
- Include type hints and docstrings
|
|
||||||
- Follow existing patterns in the codebase
|
|
||||||
- Add a if __name__ == "__main__" at the bottom of each class file for individual testing.
|
|
||||||
- Ideally had automated tests.
|
|
||||||
|
|
||||||
6. **Error Handling**
|
|
||||||
- Fail gracefully with meaningful messages
|
|
||||||
- Include recovery mechanisms where possible
|
|
||||||
- Log errors appropriately without exposing sensitive data
|
|
||||||
|
|
||||||
## Areas Needing Help
|
|
||||||
|
|
||||||
Here are some high-priority tasks and areas where we need contributions:
|
|
||||||
|
|
||||||
- Web Browsing: Improve the autonomous web browsing capabilities for the assistant.
|
|
||||||
- Graphical interface, a web graphical interface. (please ask first)
|
|
||||||
- Multi-Agent System: Enhance the planner agent for divide and conqueer for task (please ask first).
|
|
||||||
- New Tools: Add support for additional programming languages or APIs.
|
|
||||||
- Multi-language support for Text to speech & speech to text (english, chinese, spanish first)
|
|
||||||
- Testing: Write comprehensive tests for existing features.
|
|
||||||
- Better readme image: make a better readme image (robot whale that use tools. Ghibli or anime style, inspiration could be https://sakana.ai/assets/ai-scientist/cover.jpeg)
|
|
||||||
|
|
||||||
|
|
||||||
If you're unsure where to start, feel free to reach out by opening an issue or joining our community discussions.
|
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
See CODE_OF_CONDUCT.md
|
|
||||||
|
|
||||||
**Thank You!**
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Use official Python 3.11 image as the base
|
|
||||||
FROM python:3.11
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
gcc \
|
|
||||||
g++ \
|
|
||||||
gfortran \
|
|
||||||
libportaudio2 \
|
|
||||||
portaudio19-dev \
|
|
||||||
ffmpeg \
|
|
||||||
libavcodec-dev \
|
|
||||||
libavformat-dev \
|
|
||||||
libavutil-dev \
|
|
||||||
chromium \
|
|
||||||
chromium-driver \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN pip cache purge
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN BLIS_ARCH=generic pip install --no-cache-dir -r requirements.txt
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
FROM --platform=linux/amd64 python:3.11-slim
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install essential packages and Chrome dependencies
|
||||||
|
RUN apt-get update -y && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
gnupg2 \
|
||||||
|
ca-certificates \
|
||||||
|
unzip \
|
||||||
|
xvfb \
|
||||||
|
libxss1 \
|
||||||
|
libappindicator1 \
|
||||||
|
fonts-liberation \
|
||||||
|
libnss3 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libcups2 \
|
||||||
|
libdrm2 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxrandr2 \
|
||||||
|
xdg-utils \
|
||||||
|
dbus \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN apt-get update -y && \
|
||||||
|
apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
gfortran \
|
||||||
|
libportaudio2 \
|
||||||
|
portaudio19-dev \
|
||||||
|
ffmpeg \
|
||||||
|
libavcodec-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
gnupg2 \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
libasound2 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libgtk-4-1 \
|
||||||
|
libnss3 \
|
||||||
|
xdg-utils \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
|
RUN apt-get update -y && \
|
||||||
|
apt-get install -y \
|
||||||
|
alsa-utils \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV CHROME_TESTING_VERSION=134.0.6998.88
|
||||||
|
ENV DISPLAY=:99
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
wget -qO /tmp/chrome.zip \
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chrome-linux64.zip"; \
|
||||||
|
unzip -q /tmp/chrome.zip -d /opt; \
|
||||||
|
rm /tmp/chrome.zip; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /usr/local/bin/google-chrome; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /usr/local/bin/chrome; \
|
||||||
|
mkdir -p /opt/chrome; \
|
||||||
|
ln -s /opt/chrome-linux64/chrome /opt/chrome/chrome; \
|
||||||
|
google-chrome --version
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
wget -qO /tmp/chromedriver.zip \
|
||||||
|
"https://storage.googleapis.com/chrome-for-testing-public/${CHROME_TESTING_VERSION}/linux64/chromedriver-linux64.zip"; \
|
||||||
|
unzip -q /tmp/chromedriver.zip -d /tmp; \
|
||||||
|
mv /tmp/chromedriver-linux64/chromedriver /usr/local/bin; \
|
||||||
|
rm /tmp/chromedriver.zip; \
|
||||||
|
chmod +x /usr/local/bin/chromedriver; \
|
||||||
|
chromedriver --version
|
||||||
|
|
||||||
|
RUN chmod +x /opt/chrome/chrome
|
||||||
|
|
||||||
|
RUN pip3 install --upgrade pip setuptools wheel
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
RUN mkdir -p /opt/workspace
|
||||||
|
RUN mkdir -p /tmp && chmod 1777 /tmp
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY api.py .
|
||||||
|
COPY sources/ ./sources/
|
||||||
|
COPY prompts/ ./prompts/
|
||||||
|
COPY crx/ crx/
|
||||||
|
COPY llm_router/ llm_router/
|
||||||
|
COPY config.ini .
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["python3", "api.py"]
|
||||||
@@ -1,57 +1,46 @@
|
|||||||
|
# AgenticSeek: Private, Local Manus Alternative.
|
||||||
|
|
||||||
# AgenticSeek: Manus-like AI powered by Deepseek R1 Agents.
|
<p align="center">
|
||||||
|
<img align="center" src="./media/agentic_seek_logo.png" width="300" height="300" alt="Agentic Seek Logo">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Français](./README_FR.md) | [日本語](./README_JP.md)
|
||||||
|
|
||||||
**A fully local alternative to Manus AI**, a voice-enabled AI assistant that codes, explores your filesystem, browse the web and correct it's mistakes all without sending a byte of data to the cloud. Built with reasoning models like DeepSeek R1, this autonomous agent runs entirely on your hardware, keeping your data private.
|
*A **100% local alternative to Manus AI**, this voice-enabled AI assistant autonomously browses the web, writes code, and plans tasks while keeping all data on your device. Tailored for local reasoning models, it runs entirely on your hardware, ensuring complete privacy and zero cloud dependency.*
|
||||||
|
|
||||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/4Ub2D6Fj)
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
> 🛠️ **Work in Progress** – Looking for contributors!
|
### Why AgenticSeek ?
|
||||||
|
|
||||||

|
* 🔒 Fully Local & Private - Everything runs on your machine — no cloud, no data sharing. Your files, conversations, and searches stay private.
|
||||||
|
|
||||||
> *Do a web search to find tech startup in Japan working on cutting edge AI research*
|
* 🌐 Smart Web Browsing - AgenticSeek can browse the internet by itself — search, read, extract info, fill web form — all hands-free.
|
||||||
|
|
||||||
> *Make a snake game in Python*
|
* 💻 Autonomous Coding Assistant - Need code? It can write, debug, and run programs in Python, C, Go, Java, and more — all without supervision.
|
||||||
|
|
||||||
> *Scan my network with nmap, find out who is connected?*
|
* 🧠 Smart Agent Selection - You ask, it figures out the best agent for the job automatically. Like having a team of experts ready to help.
|
||||||
|
|
||||||
> *Hey can you find where is contract.pdf*?
|
* 📋 Plans & Executes Complex Tasks - From trip planning to complex projects — it can split big tasks into steps and get things done using multiple AI agents.
|
||||||
|
|
||||||
## Features:
|
* 🎙️ Voice-Enabled - Clean, fast, futuristic voice and speech to text allowing you to talk to it like it's your personal AI from a sci-fi movie
|
||||||
|
|
||||||
- **100% Local**: No cloud, runs on your hardware. Your data stays yours.
|
### **Demo**
|
||||||
|
|
||||||
- **Voice interaction**: Voice-enabled natural interaction.
|
> *Can you search for the agenticSeek project, learn what skills are required, then open the CV_candidates.zip and then tell me which match best the project*
|
||||||
|
|
||||||
- **Filesystem interaction**: Use bash to navigate and manipulate your files effortlessly.
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
- **Code what you ask**: Can write, debug, and run code in Python, C, Golang and more languages on the way.
|
Disclaimer: This demo, including all the files that appear (e.g: CV_candidates.zip), are entirely fictional. We are not a corporation, we seek open-source contributors not candidates.
|
||||||
|
|
||||||
- **Autonomous**: If a command flops or code breaks, it retries and fixes it by itself.
|
> 🛠⚠️️ **Active Work in Progress** – Please note that Code/Bash is not dockerized yet but will be soon (see docker_deployement branch) - Do not deploy over network or production.
|
||||||
|
|
||||||
- **Agent routing**: Automatically picks the right agent for the job.
|
> 🙏 Please also understand that this project began as a side experiment, with no roadmap and no expectations, we didn't expect to end in Github trending. Financial backing is exactly $1/month (shoutout to my single sponsor). Contributions, feedback, and patience are deeply appreciated.
|
||||||
|
|
||||||
- **Divide and Conquer**: For big tasks, spins up multiple agents to plan and execute.
|
## Installation
|
||||||
|
|
||||||
- **Tool-Equipped**: From basic search to flight APIs and file exploration, every agent has it's own tools.
|
Make sure you have chrome driver, docker and python3.10 installed.
|
||||||
|
|
||||||
- **Memory**: Remembers what’s useful, your preferences and past sessions conversation.
|
We highly advise you use exactly python3.10 for the setup. Dependencies error might happen otherwise.
|
||||||
|
|
||||||
- **Web Browsing**: Autonomous web navigation.
|
|
||||||
|
|
||||||
|
|
||||||
### Searching the web with agenticSeek :
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
*See media/examples for other use case screenshots.*
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **Installation**
|
|
||||||
|
|
||||||
Make sure you have chrome driver, docker and python3.10 (or newer) installed.
|
|
||||||
|
|
||||||
For issues related to chrome driver, see the **Chromedriver** section.
|
For issues related to chrome driver, see the **Chromedriver** section.
|
||||||
|
|
||||||
@@ -73,126 +62,234 @@ source agentic_seek_env/bin/activate
|
|||||||
|
|
||||||
### 3️⃣ **Install package**
|
### 3️⃣ **Install package**
|
||||||
|
|
||||||
**Automatic Installation:**
|
Ensure Python, Docker and docker compose, and Google chrome are installed.
|
||||||
|
|
||||||
|
We recommend Python 3.10.0.
|
||||||
|
|
||||||
|
**Automatic Installation (recommended):**
|
||||||
|
|
||||||
|
For Linux/Macos:
|
||||||
```sh
|
```sh
|
||||||
./install.sh
|
./install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For windows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.bat
|
||||||
|
```
|
||||||
|
|
||||||
**Manually:**
|
**Manually:**
|
||||||
|
|
||||||
```sh
|
**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**
|
||||||
pip3 install -r requirements.txt
|
|
||||||
# or
|
|
||||||
python3 setup.py install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
- *Linux*:
|
||||||
|
|
||||||
## Run locally on your machine
|
Update Package List: `sudo apt update`
|
||||||
|
|
||||||
**We recommend using at least Deepseek 14B, smaller models struggle with tool use and forget quickly the context.**
|
Install Dependencies: `sudo apt install -y alsa-utils portaudio19-dev python3-pyaudio libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1`
|
||||||
|
|
||||||
### 1️⃣ **Download Models**
|
Install ChromeDriver matching your Chrome browser version:
|
||||||
|
`sudo apt install -y chromium-chromedriver`
|
||||||
|
|
||||||
Make sure you have [Ollama](https://ollama.com/) installed.
|
Install requirements: `pip3 install -r requirements.txt`
|
||||||
|
|
||||||
Download the `deepseek-r1:14b` model from [DeepSeek](https://deepseek.com/models)
|
- *Macos*:
|
||||||
|
|
||||||
```sh
|
Update brew : `brew update`
|
||||||
ollama pull deepseek-r1:14b
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2️ **Run the Assistant (Ollama)**
|
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 `pip install pyreadline3`
|
||||||
|
|
||||||
|
Install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: `pip install pyaudio`
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
**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**
|
||||||
|
|
||||||
|
Start your local provider, for example with ollama:
|
||||||
|
|
||||||
Start the ollama server
|
|
||||||
```sh
|
```sh
|
||||||
ollama serve
|
ollama serve
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the config.ini file to set the provider_name to `ollama` and provider_model to `deepseek-r1:14b`
|
See below for a list of local supported provider.
|
||||||
|
|
||||||
NOTE: `deepseek-r1:14b`is an example, use a bigger model if your hardware allow it.
|
**Update the config.ini**
|
||||||
|
|
||||||
|
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 recommend reasoning model such as *Qwen* or *Deepseek*.
|
||||||
|
|
||||||
|
See the **FAQ** at the end of the README for required hardware.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = True
|
is_local = True # Whenever you are running locally or with remote provider.
|
||||||
provider_name = ollama
|
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
|
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, recommended only if you use web interface.
|
||||||
|
stealth_mode = True # Use undetected selenium to reduce browser detection
|
||||||
```
|
```
|
||||||
|
|
||||||
start all services :
|
Warning: Do *NOT* set provider_name to `openai` if using LM-studio for running LLMs. Set it to `lm-studio`.
|
||||||
|
|
||||||
```sh
|
Note: Some provider (eg: lm-studio) require you to have `http://` in front of the IP. For example `http://127.0.0.1:1234`
|
||||||
sudo ./start_services.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the assistant:
|
**List of local providers**
|
||||||
|
|
||||||
```sh
|
| Provider | Local? | Description |
|
||||||
python3 main.py
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
```
|
| 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 (eg: llama.cpp server) |
|
||||||
|
|
||||||
*See the **Usage** section if you don't understand how to use it*
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
*See the **Known issues** section if you are having issues*
|
*See the **Known issues** section if you are having issues*
|
||||||
|
|
||||||
*See the **Run with an API** section if your hardware can't run deepseek locally*
|
*See the **Run with an API** section if your hardware can't run deepseek locally*
|
||||||
|
|
||||||
|
*See the **Config** section for detailled config file explanation.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup to run with an API
|
||||||
|
|
||||||
|
Set the desired provider in the `config.ini`. See below for a list of API providers.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = google
|
||||||
|
provider_model = gemini-2.0-flash
|
||||||
|
provider_server_address = 127.0.0.1:5000 # doesn't matter
|
||||||
|
```
|
||||||
|
Warning: Make sure there is not trailing space in the config.
|
||||||
|
|
||||||
|
Export your API key: `export <<PROVIDER>>_API_KEY="xxx"`
|
||||||
|
|
||||||
|
Example: export `TOGETHER_API_KEY="xxxxx"`
|
||||||
|
|
||||||
|
**List of API providers**
|
||||||
|
|
||||||
|
| Provider | Local? | Description |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| openai | Depends | Use ChatGPT API |
|
||||||
|
| deepseek | No | Deepseek API (non-private) |
|
||||||
|
| huggingface| No | Hugging-Face API (non-private) |
|
||||||
|
| togetherAI | No | Use together AI API (non-private) |
|
||||||
|
| google | No | Use google gemini API (non-private) |
|
||||||
|
|
||||||
|
*We advise against using gpt-4o or other closedAI models*, performance are poor for web browsing and task planning.
|
||||||
|
|
||||||
|
Please also note that coding/bash might fail with gemini, it seems to ignore our prompt for format to respect, which are optimized for deepseek r1.
|
||||||
|
|
||||||
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
*See the **Known issues** section if you are having issues*
|
||||||
|
|
||||||
|
*See the **Config** section for detailled config file explanation.*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
|
||||||
|
Activate your python env if needed.
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Start required services. This will start all services from the docker-compose.yml, including:
|
||||||
|
- searxng
|
||||||
|
- redis (required by searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Window
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options 1:** Run with the CLI interface.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
We advise you set `headless_browser` to False in the config.ini for CLI mode.
|
||||||
|
|
||||||
|
**Options 2:** Run with the Web interface.
|
||||||
|
|
||||||
|
Start the backend.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Go to `http://localhost:3000/` and you should see the web interface.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Warning: currently the system that choose the best AI agent routing system will work poorly with non-english text. This is because the agent routing currently use a model that was trained on english text. We are working hard to fix this. Please use english for now.
|
Make sure the services are up and running with `./start_services.sh` and run the AgenticSeek with `python3 cli.py` for CLI mode or `python3 api.py` then go to `localhost:3000` for web interface.
|
||||||
|
|
||||||
|
You can also use speech to text by setting `listen = True` in the config. Only for CLI mode.
|
||||||
|
|
||||||
Make sure the services are up and running with `./start_services.sh` and run the agenticSeek with `python3 main.py`
|
To exit, simply say/type `goodbye`.
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
You will be prompted with `>>> `
|
|
||||||
This indicate agenticSeek await you type for instructions.
|
|
||||||
You can also use speech to text by setting `listen = True` in the config.
|
|
||||||
|
|
||||||
Here are some example usage:
|
Here are some example usage:
|
||||||
|
|
||||||
### Coding/Bash
|
> *Make a snake game in python!*
|
||||||
|
|
||||||
> *Help me with matrix multiplication in Golang*
|
> *Search the web for top cafes in Rennes, France, and save a list of three with their addresses in rennes_cafes.txt.*
|
||||||
|
|
||||||
> *Scan my network with nmap, find if any suspicious devices is connected*
|
> *Write a Go program to calculate the factorial of a number, save it as factorial.go in your workspace*
|
||||||
|
|
||||||
> *Make a snake game in python*
|
> *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 find on which website I can buy a rtx 4090 for cheap*
|
*Note that form filling capabilities are still experimental and might fail.*
|
||||||
|
|
||||||
### File system
|
|
||||||
|
|
||||||
> *Hey can you find where is million_dollars_contract.pdf i lost it*
|
|
||||||
|
|
||||||
> *Show me how much space I have left on my disk*
|
|
||||||
|
|
||||||
> *Find and read the README.md and follow the install instruction*
|
|
||||||
|
|
||||||
### Casual
|
|
||||||
|
|
||||||
> *Tell me a joke*
|
|
||||||
|
|
||||||
> *Where is flight ABC777 ? my mom is on that plane*
|
|
||||||
|
|
||||||
> *what is the meaning of life ?*
|
|
||||||
|
|
||||||
|
|
||||||
After you type your query, agenticSeek will allocate the best agent for the task.
|
|
||||||
|
After you type your query, AgenticSeek will allocate the best agent for the task.
|
||||||
|
|
||||||
Because this is an early prototype, the agent routing system might not always allocate the right agent based on your query.
|
Because this is an early prototype, the agent routing system might not always allocate the right agent based on your query.
|
||||||
|
|
||||||
@@ -206,79 +303,64 @@ Instead, ask:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Run the LLM on your own server**
|
## **Setup to run the LLM on your own server**
|
||||||
|
|
||||||
If you have a powerful computer or a server that you can use, but you want to use it from your laptop you have the options to run the LLM on a remote server.
|
If you have a powerful computer or a server that you can use, but you want to use it from your laptop you have the options to run the LLM on a remote server using our custom llm server.
|
||||||
|
|
||||||
### 1️⃣ **Set up and start the server scripts**
|
|
||||||
|
|
||||||
You need to have ollama installed on the server (We will integrate VLLM and llama.cpp soon).
|
|
||||||
|
|
||||||
On your "server" that will run the AI model, get the ip address
|
On your "server" that will run the AI model, get the ip address
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 # local ip
|
||||||
|
curl https://ipinfo.io/ip # public ip
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: For Windows or macOS, use ipconfig or ifconfig respectively to find the IP address.
|
Note: For Windows or macOS, use ipconfig or ifconfig respectively to find the IP address.
|
||||||
|
|
||||||
Clone the repository and then, run the script `stream_llm.py` in `server/`
|
Clone the repository and enter the `server/`folder.
|
||||||
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python3 server_ollama.py --model "deepseek-r1:32b"
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek/llm_server/
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2️⃣ **Run it**
|
Install server specific requirements:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the server script.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py --provider ollama --port 3333
|
||||||
|
```
|
||||||
|
|
||||||
|
You have the choice between using `ollama` and `llamacpp` as a LLM service.
|
||||||
|
|
||||||
|
|
||||||
Now on your personal computer:
|
Now on your personal computer:
|
||||||
|
|
||||||
Clone the repository.
|
Change the `config.ini` file to set the `provider_name` to `server` and `provider_model` to `deepseek-r1:xxb`.
|
||||||
|
|
||||||
Change the `config.ini` file to set the `provider_name` to `server` and `provider_model` to `deepseek-r1:14b`.
|
|
||||||
Set the `provider_server_address` to the ip address of the machine that will run the model.
|
Set the `provider_server_address` to the ip address of the machine that will run the model.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
[MAIN]
|
[MAIN]
|
||||||
is_local = False
|
is_local = False
|
||||||
provider_name = server
|
provider_name = server
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:70b
|
||||||
provider_server_address = x.x.x.x:5000
|
provider_server_address = x.x.x.x:3333
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the assistant:
|
|
||||||
|
|
||||||
```sh
|
Next step: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## **Run with an API**
|
|
||||||
|
|
||||||
Clone the repository.
|
|
||||||
|
|
||||||
Set the desired provider in the `config.ini`
|
|
||||||
|
|
||||||
```sh
|
|
||||||
[MAIN]
|
|
||||||
is_local = False
|
|
||||||
provider_name = openai
|
|
||||||
provider_model = gpt4-o
|
|
||||||
provider_server_address = 127.0.0.1:5000 # can be set to anything, not used
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the assistant:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo ./start_services.sh
|
|
||||||
python3 main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Speech to Text
|
## Speech to Text
|
||||||
|
|
||||||
|
Please note that currently speech to text only work in english.
|
||||||
|
|
||||||
The speech-to-text functionality is disabled by default. To enable it, set the listen option to True in the config.ini file:
|
The speech-to-text functionality is disabled by default. To enable it, set the listen option to True in the config.ini file:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -302,25 +384,81 @@ End your request with a confirmation phrase to signal the system to proceed. Exa
|
|||||||
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Example config:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:32b
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explanation**:
|
||||||
|
|
||||||
|
- is_local -> Runs the agent locally (True) or on a remote server (False).
|
||||||
|
|
||||||
|
- provider_name -> The provider to use (one of: `ollama`, `server`, `lm-studio`, `deepseek-api`)
|
||||||
|
|
||||||
|
- provider_model -> The model used, e.g., deepseek-r1:32b.
|
||||||
|
|
||||||
|
- provider_server_address -> Server address, e.g., 127.0.0.1:11434 for local. Set to anything for non-local API.
|
||||||
|
|
||||||
|
- agent_name -> Name of the agent, e.g., Friday. Used as a trigger word for TTS.
|
||||||
|
|
||||||
|
- recover_last_session -> Restarts from last session (True) or not (False).
|
||||||
|
|
||||||
|
- save_session -> Saves session data (True) or not (False).
|
||||||
|
|
||||||
|
- speak -> Enables voice output (True) or not (False).
|
||||||
|
|
||||||
|
- listen -> listen to voice input (True) or not (False).
|
||||||
|
|
||||||
|
- work_dir -> Folder the AI will have access to. eg: /Users/user/Documents/.
|
||||||
|
|
||||||
|
- jarvis_personality -> Uses a JARVIS-like personality (True) or not (False). This simply change the prompt file.
|
||||||
|
|
||||||
|
- languages -> The list of supported language, needed for the llm router to work properly, avoid putting too many or too similar languages.
|
||||||
|
|
||||||
|
- headless_browser -> Runs browser without a visible window (True) or not (False).
|
||||||
|
|
||||||
|
- stealth_mode -> Make bot detector time harder. Only downside is you have to manually install the anticaptcha extension.
|
||||||
|
|
||||||
|
- languages -> List of supported languages. Required for agent routing system. The longer the languages list the more model will be downloaded.
|
||||||
|
|
||||||
## Providers
|
## Providers
|
||||||
|
|
||||||
The table below show the available providers:
|
The table below show the available providers:
|
||||||
|
|
||||||
| Provider | Local? | Description |
|
| Provider | Local? | Description |
|
||||||
|-----------|--------|-----------------------------------------------------------|
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
| Ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
| ollama | Yes | Run LLMs locally with ease using ollama as a LLM provider |
|
||||||
| Server | Yes | Host the model on another machine, run your local machine |
|
| server | Yes | Host the model on another machine, run your local machine |
|
||||||
| OpenAI | No | Use ChatGPT API (non-private) |
|
| lm-studio | Yes | Run LLM locally with LM studio (`lm-studio`) |
|
||||||
| Deepseek | No | Deepseek API (non-private) |
|
| openai | Depends | Use ChatGPT API (non-private) or openai compatible API |
|
||||||
| HuggingFace| No | Hugging-Face API (non-private) |
|
| deepseek-api | No | Deepseek API (non-private) |
|
||||||
|
| huggingface| No | Hugging-Face API (non-private) |
|
||||||
|
| togetherAI | No | Use together AI API (non-private) |
|
||||||
|
| google | No | Use google gemini API (non-private) |
|
||||||
|
|
||||||
To select a provider change the config.ini:
|
To select a provider change the config.ini:
|
||||||
|
|
||||||
```
|
```
|
||||||
is_local = False
|
is_local = True
|
||||||
provider_name = openai
|
provider_name = ollama
|
||||||
provider_model = gpt-4o
|
provider_model = deepseek-r1:32b
|
||||||
provider_server_address = 127.0.0.1:5000
|
provider_server_address = 127.0.0.1:5000
|
||||||
```
|
```
|
||||||
`is_local`: should be True for any locally running LLM, otherwise False.
|
`is_local`: should be True for any locally running LLM, otherwise False.
|
||||||
@@ -354,40 +492,74 @@ 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
|
## FAQ
|
||||||
|
|
||||||
**Q: What hardware do I need?**
|
**Q: What hardware do I need?**
|
||||||
|
|
||||||
7B Model: GPU with 8GB VRAM.
|
| Model Size | GPU | Comment |
|
||||||
14B Model: 12GB GPU (e.g., RTX 3060).
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
32B Model: 24GB+ VRAM.
|
| 7B | 8GB Vram | ⚠️ Not recommended. Performance is poor, frequent hallucinations, and planner agents will likely fail. |
|
||||||
|
| 14B | 12 GB VRAM (e.g. RTX 3060) | ✅ Usable for simple tasks. May struggle with web browsing and planning tasks. |
|
||||||
|
| 32B | 24+ GB VRAM (e.g. RTX 4090) | 🚀 Success with most tasks, might still struggle with task planning |
|
||||||
|
| 70B+ | 48+ GB Vram (eg. mac studio) | 💪 Excellent. Recommended for advanced use cases. |
|
||||||
|
|
||||||
**Q: Why Deepseek R1 over other models?**
|
**Q: Why Deepseek R1 over other models?**
|
||||||
|
|
||||||
Deepseek R1 excels at reasoning and tool use for its size. We think it’s a solid fit for our needs other models work fine, but Deepseek is our primary pick.
|
Deepseek R1 excels at reasoning and tool use for its size. We think it’s a solid fit for our needs other models work fine, but Deepseek is our primary pick.
|
||||||
|
|
||||||
**Q: I get an error running `main.py`. What do I do?**
|
**Q: I get an error running `cli.py`. What do I do?**
|
||||||
|
|
||||||
Ensure Ollama is running (`ollama serve`), your `config.ini` matches your provider, and dependencies are installed. If none work feel free to raise an issue.
|
Ensure local is running (`ollama serve`), your `config.ini` matches your provider, and dependencies are installed. If none work feel free to raise an issue.
|
||||||
|
|
||||||
**Q: Can it really run 100% locally?**
|
**Q: Can it really run 100% locally?**
|
||||||
|
|
||||||
Yes with Ollama or Server providers, all speech to text, LLM and text to speech model run locally. Non-local options (OpenAI or others API) are optional.
|
Yes with Ollama, lm-studio or server providers, all speech to text, LLM and text to speech model run locally. Non-local options (OpenAI or others API) are optional.
|
||||||
|
|
||||||
**Q: How come it is older than manus ?**
|
**Q: Why should I use AgenticSeek when I have Manus?**
|
||||||
|
|
||||||
we started this a fun side project to make a fully local, Jarvis-like AI. However, with the rise of Manus, we saw the opportunity to redirected some tasks to make yet another alternative.
|
This started as Side-Project we did out of interest about AI agents. What’s special about it is that we want to use local model and avoid APIs.
|
||||||
|
We draw inspiration from Jarvis and Friday (Iron man movies) to make it "cool" but for functionality we take more inspiration from Manus, because that's what people want in the first place: a local manus alternative.
|
||||||
**Q: How is it better than manus ?**
|
Unlike Manus, AgenticSeek prioritizes independence from external systems, giving you more control, privacy and avoid api cost.
|
||||||
|
|
||||||
It's not but we prioritizes local execution and privacy over cloud based approach. It’s a fun, accessible alternative!
|
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
We’re looking for developers to improve AgenticSeek! Check out open issues or discussion.
|
We’re looking for developers to improve AgenticSeek! Check out open issues or discussion.
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
## Authors:
|
## Maintainers:
|
||||||
> [Fosowl](https://github.com/Fosowl)
|
|
||||||
> [steveh8758](https://github.com/steveh8758)
|
> [Fosowl](https://github.com/Fosowl) | Paris Time
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
|
> [steveh8758](https://github.com/steveh8758) | Taipei Time
|
||||||
|
|
||||||
|
## Special Thanks:
|
||||||
|
|
||||||
|
> [tcsenpai](https://github.com/tcsenpai) For dockerization of backend
|
||||||
|
|||||||
@@ -0,0 +1,571 @@
|
|||||||
|
# AgenticSeek: Private, Local Manus Alternative.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/whale_readme.jpg">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
[English](./README.md) | 中文 | [日本語](./README_JP.md)
|
||||||
|
|
||||||
|
*一个 **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
|
||||||
|
|
||||||
|
> 🛠️ **目前还在开发阶段** – 欢迎任何贡献者加入我们!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **安装**
|
||||||
|
|
||||||
|
确保已安装了 Chrome driver,Docker 和 Python 3.10(或更新)。
|
||||||
|
|
||||||
|
我们强烈建议您使用 Python 3.10 进行设置,否则可能会发生依赖错误。
|
||||||
|
|
||||||
|
有关于 Chrome driver 的问题,请参见 **Chromedriver** 部分。
|
||||||
|
|
||||||
|
### 1️⃣ **复制储存库与设置环境变数**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2️ **建立虚拟环境**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m venv agentic_seek_env
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
# On Windows: agentic_seek_env\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3️⃣ **安装所需套件**
|
||||||
|
|
||||||
|
**自动安装:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
** 若要让文本转语音(TTS)功能支持中文,你需要安装 jieba(中文分词库)和 cn2an(中文数字转换库):**
|
||||||
|
|
||||||
|
```
|
||||||
|
pip3 install jieba cn2an
|
||||||
|
```
|
||||||
|
|
||||||
|
**手动安装:**
|
||||||
|
|
||||||
|
|
||||||
|
**注意:对于任何操作系统,请确保您安装的 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
|
||||||
|
|
||||||
|
**建议至少使用 Deepseek 14B 以上参数的模型,较小的模型难以使用助理功能并且很快就会忘记上下文之间的关系。**
|
||||||
|
|
||||||
|
**本地运行助手**
|
||||||
|
|
||||||
|
启动你的本地提供者,例如使用 ollama:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
请参阅下方支持的本地提供者列表。
|
||||||
|
|
||||||
|
**更新 config.ini**
|
||||||
|
|
||||||
|
修改 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 # 选择适合您硬件的模型
|
||||||
|
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`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**本地提供者列表**
|
||||||
|
|
||||||
|
| 提供者 | 本地? | 描述 |
|
||||||
|
|-------------|--------|-------------------------------------------------------|
|
||||||
|
| ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地运行 LLM |
|
||||||
|
| lm-studio | 是 | 使用 LM Studio 本地运行 LLM(将 `provider_name` 设置为 `lm-studio`)|
|
||||||
|
| openai | 否 | 使用兼容的 API |
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Run with an API (透过 API 执行)**
|
||||||
|
|
||||||
|
设定 `config.ini`。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
警告:确保 `config.ini` 没有行尾空格。
|
||||||
|
|
||||||
|
如果使用基于本机的 openai-based api 则把 `is_local` 设定为 `True`。
|
||||||
|
|
||||||
|
同时更改你的 IP 为 openai-based api 的 IP。
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
(启动服务并运行)
|
||||||
|
|
||||||
|
如果需要,请激活你的 Python 环境。
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
启动所需的服务。这将启动 `docker-compose.yml` 中的所有服务,包括:
|
||||||
|
- searxng
|
||||||
|
- redis(由 redis 提供支持)
|
||||||
|
- 前端
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**选项 1:** 使用 CLI 界面运行。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**选项 2:** 使用 Web 界面运行。
|
||||||
|
|
||||||
|
注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。
|
||||||
|
|
||||||
|
启动后端服务。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:3000/`,你应该会看到 Web 界面。
|
||||||
|
|
||||||
|
请注意,目前 Web 界面不支持消息流式传输。
|
||||||
|
|
||||||
|
|
||||||
|
*如果你不知道如何开始,请参阅 **Usage** 部分*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage (使用方法)
|
||||||
|
|
||||||
|
为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中设置语言选项。
|
||||||
|
languages = en zh
|
||||||
|
更多信息请参阅 Config 部分
|
||||||
|
|
||||||
|
确定所有的核心档案都启用了,也就是执行过这条命令 `./start_services.sh` 然后你就可以使用 `python3 cli.py` 来启动 AgenticSeek 了!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
当你看到执行后显示 `>>> `
|
||||||
|
这表示一切运作正常,AgenticSeek 正在等待你给他任何指令。
|
||||||
|
你也可以透过设定 `config.ini` 内的 `listen = True` 来启用语音转文字。
|
||||||
|
|
||||||
|
要退出时,只要和他说 `goodbye` 就可以退出!
|
||||||
|
|
||||||
|
以下是一些用法:
|
||||||
|
|
||||||
|
### Coding/Bash
|
||||||
|
|
||||||
|
> *在 Golang 中帮助我进行矩阵乘法*
|
||||||
|
|
||||||
|
> *使用 nmap 扫描我的网路,找出是否有任何可疑装置连接*
|
||||||
|
|
||||||
|
> *用 Python 制作一个贪食蛇游戏*
|
||||||
|
|
||||||
|
### 网路搜寻
|
||||||
|
|
||||||
|
> *进行网路搜寻,找出日本从事尖端人工智慧研究的酷炫科技新创公司*
|
||||||
|
|
||||||
|
> *你能在网路上找到谁创造了 AgenticSeek 吗?*
|
||||||
|
|
||||||
|
> *你能在哪个网站上找到便宜的 RTX 4090 吗?*
|
||||||
|
|
||||||
|
### 档案浏览与搜寻
|
||||||
|
|
||||||
|
> *嘿,你能找到我遗失的 million_dollars_contract.pdf 在哪里吗?*
|
||||||
|
|
||||||
|
> *告诉我我的磁碟还剩下多少空间*
|
||||||
|
|
||||||
|
> *寻找并阅读 README.md,并按照安装说明进行操作*
|
||||||
|
|
||||||
|
### 日常聊天
|
||||||
|
|
||||||
|
> *告诉我关于法国的事*
|
||||||
|
|
||||||
|
> *人生的意义是什么?*
|
||||||
|
|
||||||
|
> *我应该在锻炼前还是锻炼后服用肌酸?*
|
||||||
|
|
||||||
|
|
||||||
|
当你把指令送出后,AgenticSeek 会自动调用最能提供帮助的助理,去完成你交办的工作和指令。
|
||||||
|
|
||||||
|
但也有可能出现怪怪的情况,或是你要找飞机机票,他跑去教你如何一步步做出一台飞机(开玩笑的,但真的可能出现),因为这是一个早期专案,我们会努力教导他、完善他的!
|
||||||
|
|
||||||
|
所以我们希望你在使用时,能明确地表明你希望他要怎么做,下面给你一个范例!
|
||||||
|
|
||||||
|
你该说:
|
||||||
|
- 进行网络搜索,找出哪些国家最适合独自旅行
|
||||||
|
|
||||||
|
|
||||||
|
而不是说:
|
||||||
|
- 你知道哪些国家适合独自旅行?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **在本地执行属于你的 LLM 伺服器**
|
||||||
|
|
||||||
|
如果你有一台功能强大的电脑或伺服器,但你想透过笔记型电脑使用它,那么你可以选择在远端伺服器上执行 LLM。
|
||||||
|
|
||||||
|
### 1️⃣ **设定并启动伺服器脚本**
|
||||||
|
|
||||||
|
在运行 AI 模型的「伺服器」上,取得 IP 位址
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:请在 Windows 或 MacOS,分别使用 `ipconfig` 与 `ifconfig` 来寻找 IP 位址。
|
||||||
|
|
||||||
|
**如果你希望使用基于 Openai 的服务,请按照 *透过 API 执行* 部分进行。**
|
||||||
|
|
||||||
|
复制储存库并且进入 `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 的服务框架。
|
||||||
|
|
||||||
|
### 2️⃣ **执行**
|
||||||
|
|
||||||
|
在你的电脑上:
|
||||||
|
|
||||||
|
- 更改 `config.ini`
|
||||||
|
- `provider_name = server`
|
||||||
|
- `provider_model = deepseek-r1:14b`
|
||||||
|
- `provider_server_address = {你执行模型的电脑的 IP 位址}`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:14b
|
||||||
|
provider_server_address = x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 语音转文字
|
||||||
|
|
||||||
|
请注意,目前语音转文字功能仅支持英语。
|
||||||
|
|
||||||
|
预设状况下,语音转文字功能是停用的。若要启用它,请在 `config.ini` 档案中,将 `listen` 选项设为 `True`:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
启用后 AgenticSeek 会聆听你是否呼唤他,他才会开始听你说的话,你可以在 *config.ini* 内去设定,要怎么叫他。
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
为了获得比较好的结果,我们建议使用常见的英文名称(如 “John” 或 “Emma”)作为他的名字。
|
||||||
|
|
||||||
|
当你看到程式开始执行时,请大声说出他的名字,就可以唤醒 AgenticSeek 去聆听!(如:Friday)
|
||||||
|
|
||||||
|
清楚说出你的需求。
|
||||||
|
|
||||||
|
用确认短句结束你说的话,以通知 AgenticSeek 继续。确认短句的范例包括:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Config 范例:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:1.5b
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- is_local
|
||||||
|
- True:在本地运行。
|
||||||
|
- False:在远端伺服器运行。
|
||||||
|
- provider_name
|
||||||
|
- 框架类型
|
||||||
|
- `ollama`, `server`, `lm-studio`, `deepseek-api`
|
||||||
|
- provider_model
|
||||||
|
- 运行的模型
|
||||||
|
- `deepseek-r1:1.5b`, `deepseek-r1:14b`
|
||||||
|
- provider_server_address
|
||||||
|
- 伺服器 IP
|
||||||
|
- `127.0.0.1:11434`
|
||||||
|
- agent_name
|
||||||
|
- AgenticSeek 的名字,用作TTS的触发单词。
|
||||||
|
- `Friday`
|
||||||
|
- recover_last_session
|
||||||
|
- True:从上个对话继续。
|
||||||
|
- False:重启对话。
|
||||||
|
- save_session
|
||||||
|
- True:储存对话纪录。
|
||||||
|
- False:不保存。
|
||||||
|
- speak
|
||||||
|
- True:启用语音输出。
|
||||||
|
- False:关闭语音输出。
|
||||||
|
- listen
|
||||||
|
- True:启用语音输入。
|
||||||
|
- False:关闭语音输入。
|
||||||
|
- work_dir
|
||||||
|
- AgenticSeek 拥有能存取与交互的工作目录。
|
||||||
|
- jarvis_personality
|
||||||
|
> 就是那个钢铁人的 JARVIS
|
||||||
|
- True:启用 JARVIS 个性。
|
||||||
|
- False:关闭 JARVIS 个性。
|
||||||
|
- headless_browser
|
||||||
|
- True:前景浏览器。(很酷,推荐使用他 XD)
|
||||||
|
- False:背景执行浏览器。
|
||||||
|
- stealth_mode
|
||||||
|
- 隐私模式,但需要你自己安装反爬虫扩充功能。
|
||||||
|
- languages
|
||||||
|
- 支持的语言列表。用于代理路由系统。语言列表越长,下载的模型越多。
|
||||||
|
|
||||||
|
## 框架
|
||||||
|
|
||||||
|
下表显示了可用的框架:
|
||||||
|
|
||||||
|
| 框架 | 本地? | 描述|
|
||||||
|
|-|-|-|
|
||||||
|
| ollama | 可 | 使用 ollama 框架去执行本地模型 |
|
||||||
|
| server | 可 | 本地伺服器执行模型远端调用 |
|
||||||
|
| lm-studio | 可 | 使用 LM Studio 在本地运行 LLM(设定provider_name为lm-studio)|
|
||||||
|
| openai | 不可 | 使用 ChatGPT API(无法保证隐私)|
|
||||||
|
| deepseek-api | 不可 | 使用 Deepseek API (无法保证隐私)|
|
||||||
|
| huggingface | 不可 | 使用 Hugging-Face API (无法保证隐私)|
|
||||||
|
|
||||||
|
若要选择框架,请变更 `config.ini` 文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
`is_local`: 对于任何本地运行的 LLM 都应该为 True,否则为 False。
|
||||||
|
|
||||||
|
`provider_name`: 透过名称选择要使用的框架,请参阅上面的框架清单。
|
||||||
|
|
||||||
|
`provider_model`: 设定 AgenticSeek 使用的模型。
|
||||||
|
|
||||||
|
`provider_server_address`: 如果不使用云端 API,则可以将其设定为任何内容。
|
||||||
|
|
||||||
|
# Known issues (已知问题)
|
||||||
|
|
||||||
|
## Chromedriver Issues
|
||||||
|
|
||||||
|
**已知问题 #1:** *chromedriver mismatch*
|
||||||
|
|
||||||
|
`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 或更新版本,请前往:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
下载与你的作业系统相符的 chromedriver 版本。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如果有其他问题,请提供尽量详细的叙述到 Issues 上,尽可能包含当前环境和问题是怎么发生的。
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**Q: 我需要什麼硬體?**
|
||||||
|
|
||||||
|
| 模型大小 | GPU | 備註 |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB Vram | ⚠️ 不推薦。性能較差,經常出現幻覺,規劃代理可能會失敗。 |
|
||||||
|
| 14B | 12 GB VRAM (例如 RTX 3060) | ✅ 適用於簡單任務。可能在網頁瀏覽和規劃任務上表現不佳。 |
|
||||||
|
| 32B | 24+ GB VRAM (例如 RTX 4090) | 🚀 大多數任務成功,但可能仍在任務規劃上有困難。 |
|
||||||
|
| 70B+ | 48+ GB Vram (例如 mac studio) | 💪 表現優異。建議用於高級使用情境。 |
|
||||||
|
|
||||||
|
**Q:为什么选择 Deepseek R1 而不是其他模型?**
|
||||||
|
|
||||||
|
就其尺寸而言,Deepseek R1 在推理和使用方面表现出色。我们认为非常适合我们的需求,其他模型也很好用,但 Deepseek 是我们最后选定的模型。
|
||||||
|
|
||||||
|
**Q:我在执行时 `cli.py` 时出现错误。我该怎么办?**
|
||||||
|
|
||||||
|
1. 确保 Ollama 正在运行(ollama serve)
|
||||||
|
2. 你 `config.ini` 内 `provider_name` 的框架选择正确。
|
||||||
|
3. 依赖套件已安装
|
||||||
|
4. 如果均无效,请随时提出 Issues,同样尽可能包含当前环境和问题是怎么发生的。
|
||||||
|
|
||||||
|
**Q:它真的是 100% 本地运行吗?**
|
||||||
|
|
||||||
|
是的,透过 Ollama 或其他框架,所有语音转文字、LLM 和文字转语音模型都在本地运行。
|
||||||
|
*但你能选择非本地执行(OpenAI 或其他 API),同样也是可以的*
|
||||||
|
|
||||||
|
|
||||||
|
**Q:我有 Manus 为甚么还要用 AgenticSeek?**
|
||||||
|
|
||||||
|
这是我们因为兴趣做的一个小 Side-Project,他特别的点在于是一个全部本地化的模型,而且可以像钢铁人里面一样与 `Jarvis` 对话,听起来就超级酷的吧!随着 Manus 的进化,我们也相应的加入更多功能!
|
||||||
|
|
||||||
|
**Q:它比 Manus 好在哪里?**
|
||||||
|
|
||||||
|
不不不,AgenticSeek 和 Manus 是不同取向的东西,我们优先考虑的是本地执行和隐私,而不是基于云端。这是一个与 Manus 相比起来更有趣且易使用的方案!
|
||||||
|
|
||||||
|
**Q: 是否支持中文以外的语言?**
|
||||||
|
|
||||||
|
DeepSeek R1 天生会说中文
|
||||||
|
|
||||||
|
但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统:
|
||||||
|
|
||||||
|
如果不设置中文?后果可能是:你让它写代码,结果跳出来个"医生代理"(虽然我们根本没有这个代理... 但系统会一脸懵圈!)
|
||||||
|
|
||||||
|
实际上会下载一个小型翻译模型来协助任务分配
|
||||||
|
|
||||||
|
## 贡献
|
||||||
|
|
||||||
|
我们正在寻找开发者来改善 AgenticSeek!你可以在 Issues 查看未解决的问题或和我们讨论更酷的新功能!
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## 维护者:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | 巴黎时间
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
|
> [steveh8758](https://github.com/steveh8758) | 台北时间
|
||||||
@@ -0,0 +1,563 @@
|
|||||||
|
# AgenticSeek: 類似 Manus 但基於 Deepseek R1 Agents 的本地模型。
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/whale_readme.jpg">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
[English](./README.md) | 繁體中文 | [日本語](./README_JP.md)
|
||||||
|
|
||||||
|
|
||||||
|
*一个 **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
|
||||||
|
|
||||||
|
> 🛠️ **目前還在開發階段** – 歡迎任何貢獻者加入我們!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **安裝**
|
||||||
|
|
||||||
|
確保已安裝了 Chrome driver,Docker 和 Python 3.10(或更新)。
|
||||||
|
|
||||||
|
我们强烈建议您使用 Python 3.10 進行設定,否则可能會发生依赖错误。
|
||||||
|
|
||||||
|
有關於 Chrome driver 的問題,請參見 **Chromedriver** 部分。
|
||||||
|
|
||||||
|
### 1️⃣ **複製儲存庫與設置環境變數**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2️ **建立虛擬環境**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m venv agentic_seek_env
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
# On Windows: agentic_seek_env\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3️⃣ **安裝所需套件**
|
||||||
|
|
||||||
|
**自動安裝:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
** 若要將文字轉成語音(TTS)功能支持中文,你需要安装 jieba(中文分詞庫)和 cn2an(中文數字轉換庫):**
|
||||||
|
|
||||||
|
```
|
||||||
|
pip3 install jieba cn2an
|
||||||
|
```
|
||||||
|
|
||||||
|
**手動安裝:**
|
||||||
|
|
||||||
|
|
||||||
|
**注意:對於不同作業系統,請確保已經安装的 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
|
||||||
|
|
||||||
|
**建議至少使用 Deepseek 14B 以上參數的模型,較小的模型難以使用助理功能並且很快就會忘記上下文之間的關係。**
|
||||||
|
|
||||||
|
**本地運行助手**
|
||||||
|
|
||||||
|
啟動你的本地提供者,例如使用 ollama:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
请参閱下方支持的本地提供者列表。
|
||||||
|
|
||||||
|
修改 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 # 选择适合您硬件的模型
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Jarvis # 您的 AI 助手的名称
|
||||||
|
recover_last_session = True # 是否恢复之前的會话
|
||||||
|
save_session = True # 是否记住当前會话
|
||||||
|
speak = True # 文本轉語音
|
||||||
|
listen = False # 語音轉文本,僅适用于命令行界面
|
||||||
|
work_dir = /Users/mlg/Documents/workspace # AgenticSeek 的工作空间。
|
||||||
|
jarvis_personality = False # 是否使用更"贾维斯"风格的性格,不推荐在小型模型上使用
|
||||||
|
languages = en zh # 语言列表,文本轉語音將默认使用列表中的第一种语言
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = True # 是否使用无头瀏覽器,只有在使用網页界面时才推荐使用。
|
||||||
|
stealth_mode = True # 使用无法檢測的 selenium 来减少瀏覽器檢測
|
||||||
|
```
|
||||||
|
|
||||||
|
**本地提供者列表**
|
||||||
|
|
||||||
|
| 提供者 | 本地? | 描述 |
|
||||||
|
|-------------|--------|-------------------------------------------------------|
|
||||||
|
| ollama | 是 | 使用 ollama 作为 LLM 提供者,轻松本地運行 LLM |
|
||||||
|
| lm-studio | 是 | 使用 LM Studio 本地運行 LLM(將 `provider_name` 設定为 `lm-studio`)|
|
||||||
|
| openai | 否 | 使用兼容的 API |
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Run with an API (透過 API 執行)**
|
||||||
|
|
||||||
|
設定 `config.ini`。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
警告:確保 `config.ini` 沒有行尾空格。
|
||||||
|
|
||||||
|
如果使用基於本機的 openai-based api 則把 `is_local` 設定為 `True`。
|
||||||
|
|
||||||
|
同時更改你的 IP 為 openai-based api 的 IP。
|
||||||
|
|
||||||
|
下一步: [Start services and run AgenticSeek](#Start-services-and-Run)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start services and Run
|
||||||
|
(啟動服务并運行)
|
||||||
|
|
||||||
|
如果需要,请激活你的 Python 环境。
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
啟動所需的服务。这將啟動 `docker-compose.yml` 中的所有服务,包括:
|
||||||
|
- searxng
|
||||||
|
- redis(由 redis 提供支持)
|
||||||
|
- 前端
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**選項 1:** 使用 CLI 界面運行。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**選項 2:** 使用 Web 界面運行。
|
||||||
|
|
||||||
|
注意:目前我們建議您使用 CLI 界面。Web 界面仍在積極開發中。
|
||||||
|
|
||||||
|
啟動後端服务。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:3000/`,你应该會看到 Web 界面。
|
||||||
|
|
||||||
|
请注意,目前 Web 界面不支持消息流式傳輸。
|
||||||
|
|
||||||
|
|
||||||
|
*如果你不知道如何開始,請參閱 **Usage** 部分*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage (使用方法)
|
||||||
|
|
||||||
|
为确保 agenticSeek 在中文环境下正常工作,请确保在 config.ini 中設定语言選項。
|
||||||
|
languages = en zh
|
||||||
|
更多信息请参閱 Config 部分
|
||||||
|
|
||||||
|
確定所有的核心檔案都啟用了,也就是執行過這條命令 `./start_services.sh` 然後你就可以使用 `python3 cli.py` 來啟動 AgenticSeek 了!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
當你看到執行後顯示 `>>> `
|
||||||
|
這表示一切運作正常,AgenticSeek 正在等待你給他任何指令。
|
||||||
|
你也可以透過設定 `config.ini` 內的 `listen = True` 來啟用語音轉文字。
|
||||||
|
|
||||||
|
要退出時,只要和他說 `goodbye` 就可以退出!
|
||||||
|
|
||||||
|
以下是一些用法:
|
||||||
|
|
||||||
|
### Coding/Bash
|
||||||
|
|
||||||
|
> *在 Golang 中幫助我進行矩陣乘法*
|
||||||
|
|
||||||
|
> *使用 nmap 掃描我的網路,找出是否有任何可疑裝置連接*
|
||||||
|
|
||||||
|
> *用 Python 製作一個貪食蛇遊戲*
|
||||||
|
|
||||||
|
### 網路搜尋
|
||||||
|
|
||||||
|
> *進行網路搜尋,找出日本從事尖端人工智慧研究的酷炫科技新創公司*
|
||||||
|
|
||||||
|
> *你能在網路上找到誰創造了 AgenticSeek 嗎?*
|
||||||
|
|
||||||
|
> *你能在哪個網站上找到便宜的 RTX 4090 嗎?*
|
||||||
|
|
||||||
|
### 檔案瀏覽與搜尋
|
||||||
|
|
||||||
|
> *嘿,你能找到我遺失的 million_dollars_contract.pdf 在哪裡嗎?*
|
||||||
|
|
||||||
|
> *告訴我我的磁碟還剩下多少空間*
|
||||||
|
|
||||||
|
> *尋找並閱讀 README.md,並按照安裝說明進行操作*
|
||||||
|
|
||||||
|
### 日常聊天
|
||||||
|
|
||||||
|
> *告訴我關於法國的事*
|
||||||
|
|
||||||
|
> *人生的意義是什麼?*
|
||||||
|
|
||||||
|
> *我應該在鍛鍊前還是鍛鍊後服用肌酸?*
|
||||||
|
|
||||||
|
|
||||||
|
當你把指令送出後,AgenticSeek 會自動調用最能提供幫助的助理,去完成你交辦的工作和指令。
|
||||||
|
|
||||||
|
但也有可能出現怪怪的情況,或是你要找飛機機票,他跑去教你如何一步步做出一台飛機(開玩笑的,但真的可能出現),因為這是一個早期專案,我們會努力教導他、完善他的!
|
||||||
|
|
||||||
|
所以我們希望你在使用時,能明確地表明你希望他要怎麼做,下面給你一個範例!
|
||||||
|
|
||||||
|
你該說:
|
||||||
|
- 進行網路搜索,找出哪些国家最适合獨自旅行
|
||||||
|
|
||||||
|
|
||||||
|
而不是說:
|
||||||
|
- 你知道哪些国家适合獨自旅行?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **在本地執行屬於你的 LLM 伺服器**
|
||||||
|
|
||||||
|
如果你有一台功能強大的電腦或伺服器,但你想透過筆記型電腦使用它,那麼你可以選擇在遠端伺服器上執行 LLM。
|
||||||
|
|
||||||
|
### 1️⃣ **設定並啟動伺服器腳本**
|
||||||
|
|
||||||
|
在運行 AI 模型的「伺服器」上,取得 IP 位址
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:請在 Windows 或 MacOS,分別使用 `ipconfig` 與 `ifconfig` 來尋找 IP 位址。
|
||||||
|
|
||||||
|
**如果你希望使用基於 Openai 的服務,請按照 *透過 API 執行* 部分進行。**
|
||||||
|
|
||||||
|
複製儲存庫並且進入 `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 的服務框架。
|
||||||
|
|
||||||
|
### 2️⃣ **執行**
|
||||||
|
|
||||||
|
在你的電腦上:
|
||||||
|
|
||||||
|
- 更改 `config.ini`
|
||||||
|
- `provider_name = server`
|
||||||
|
- `provider_model = deepseek-r1:14b`
|
||||||
|
- `provider_server_address = {你執行模型的電腦的 IP 位址}`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:14b
|
||||||
|
provider_server_address = x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 語音轉文字
|
||||||
|
|
||||||
|
请注意,目前語音轉文字功能僅支援英语。
|
||||||
|
|
||||||
|
預設狀況下,語音轉文字功能是停用的。若要啟用它,請在 `config.ini` 檔案中,將 `listen` 選項設為 `True`:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
啟用後 AgenticSeek 會聆聽你是否呼喚他,他才會開始聽你說的話,你可以在 *config.ini* 內去設定,要怎麼叫他。
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
為了獲得比較好的結果,我們建議使用常見的英文名稱(如 “John” 或 “Emma”)作為他的名字。
|
||||||
|
|
||||||
|
當你看到程式開始執行時,請大聲說出他的名字,就可以喚醒 AgenticSeek 去聆聽!(如:Friday)
|
||||||
|
|
||||||
|
清楚說出你的需求。
|
||||||
|
|
||||||
|
用確認短句結束你說的話,以通知 AgenticSeek 繼續。確認短句的範例包括:
|
||||||
|
```
|
||||||
|
"do it", "go ahead", "execute", "run", "start", "thanks", "would ya", "please", "okay?", "proceed", "continue", "go on", "do that", "go it", "do you understand?"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Config 範例:
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:1.5b
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**說明**:
|
||||||
|
- is_local
|
||||||
|
- True:在本地運行。
|
||||||
|
- False:在遠端伺服器運行。
|
||||||
|
- provider_name
|
||||||
|
- 框架類型
|
||||||
|
- `ollama`, `server`, `lm-studio`, `deepseek-api`
|
||||||
|
- provider_model
|
||||||
|
- 運行的模型
|
||||||
|
- `deepseek-r1:1.5b`, `deepseek-r1:14b`
|
||||||
|
- provider_server_address
|
||||||
|
- 伺服器 IP
|
||||||
|
- `127.0.0.1:11434`
|
||||||
|
- agent_name
|
||||||
|
- AgenticSeek 的名字,用作TTS的觸發單詞。
|
||||||
|
- `Friday`
|
||||||
|
- recover_last_session
|
||||||
|
- True:從上個對話繼續。
|
||||||
|
- False:重啟對話。
|
||||||
|
- save_session
|
||||||
|
- True:儲存對話紀錄。
|
||||||
|
- False:不保存。
|
||||||
|
- speak
|
||||||
|
- True:啟用語音輸出。
|
||||||
|
- False:關閉語音輸出。
|
||||||
|
- listen
|
||||||
|
- True:啟用語音輸入。
|
||||||
|
- False:關閉語音輸入。
|
||||||
|
- work_dir
|
||||||
|
- AgenticSeek 擁有能存取與交互的工作目錄。
|
||||||
|
- jarvis_personality
|
||||||
|
> 就是那個鋼鐵人的 JARVIS
|
||||||
|
- True:啟用 JARVIS 個性。
|
||||||
|
- False:關閉 JARVIS 個性。
|
||||||
|
- headless_browser
|
||||||
|
- True:前景瀏覽器。(很酷,推薦使用他 XD)
|
||||||
|
- False:背景執行瀏覽器。
|
||||||
|
- stealth_mode
|
||||||
|
- 隱私模式,但需要你自己安裝反爬蟲擴充功能。
|
||||||
|
- languages
|
||||||
|
- 支持的语言列表。用于代理路由系统。语言列表越长,下载的模型越多。
|
||||||
|
|
||||||
|
## 框架
|
||||||
|
|
||||||
|
下表顯示了可用的框架:
|
||||||
|
|
||||||
|
| 框架 | 本地? | 描述|
|
||||||
|
|-|-|-|
|
||||||
|
| ollama | 可 | 使用 ollama 框架去執行本地模型 |
|
||||||
|
| server | 可 | 本地伺服器執行模型遠端調用 |
|
||||||
|
| lm-studio | 可 | 使用 LM Studio 在本地運行 LLM(設定provider_name為lm-studio)|
|
||||||
|
| openai | 不可 | 使用 ChatGPT API(無法保證隱私)|
|
||||||
|
| deepseek-api | 不可 | 使用 Deepseek API (無法保證隱私)|
|
||||||
|
| huggingface | 不可 | 使用 Hugging-Face API (無法保證隱私)|
|
||||||
|
|
||||||
|
若要選擇框架,請變更 `config.ini` 文件:
|
||||||
|
|
||||||
|
```
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
`is_local`: 對於任何本地運行的 LLM 都應該為 True,否則為 False。
|
||||||
|
|
||||||
|
`provider_name`: 透過名稱選擇要使用的框架,請參閱上面的框架清單。
|
||||||
|
|
||||||
|
`provider_model`: 設定 AgenticSeek 使用的模型。
|
||||||
|
|
||||||
|
`provider_server_address`: 如果不使用雲端 API,則可以將其設定為任何內容。
|
||||||
|
|
||||||
|
# Known issues (已知問題)
|
||||||
|
|
||||||
|
## Chromedriver Issues
|
||||||
|
|
||||||
|
**已知問題 #1:** *chromedriver mismatch*
|
||||||
|
|
||||||
|
`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 或更新版本,請前往:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
下載與你的作業系統相符的 chromedriver 版本。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如果有其他問題,請提供盡量詳細的敘述到 Issues 上,盡可能包含當前環境和問題是怎麼發生的。
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**Q: 我需要什麼硬體?**
|
||||||
|
|
||||||
|
| 模型大小 | GPU | 備註 |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 7B | 8GB Vram | ⚠️ 不推薦。性能較差,經常出現幻覺,規劃代理可能會失敗。 |
|
||||||
|
| 14B | 12 GB VRAM (例如 RTX 3060) | ✅ 適用於簡單任務。可能在網頁瀏覽和規劃任務上表現不佳。 |
|
||||||
|
| 32B | 24+ GB VRAM (例如 RTX 4090) | 🚀 大多數任務成功,但可能仍在任務規劃上有困難。 |
|
||||||
|
| 70B+ | 48+ GB Vram (例如 mac studio) | 💪 表現優異。建議用於高級使用情境。 |
|
||||||
|
|
||||||
|
**Q:為什麼選擇 Deepseek R1 而不是其他模型?**
|
||||||
|
|
||||||
|
就其尺寸而言,Deepseek R1 在推理和使用方面表現出色。我們認為非常適合我們的需求,其他模型也很好用,但 Deepseek 是我們最後選定的模型。
|
||||||
|
|
||||||
|
**Q:我在執行時 `cli.py` 時出現錯誤。我該怎麼辦?**
|
||||||
|
|
||||||
|
1. 確保 Ollama 正在運行(ollama serve)
|
||||||
|
2. 你 `config.ini` 內 `provider_name` 的框架選擇正確。
|
||||||
|
3. 依賴套件已安裝
|
||||||
|
4. 如果均無效,請隨時提出 Issues,同樣盡可能包含當前環境和問題是怎麼發生的。
|
||||||
|
|
||||||
|
**Q:它真的是 100% 本地運行嗎?**
|
||||||
|
|
||||||
|
是的,透過 Ollama 或其他框架,所有語音轉文字、LLM 和文字轉語音模型都在本地運行。
|
||||||
|
*但你能選擇非本地執行(OpenAI 或其他 API),同樣也是可以的*
|
||||||
|
|
||||||
|
|
||||||
|
**Q:我有 Manus 為甚麼還要用 AgenticSeek?**
|
||||||
|
|
||||||
|
這是我們因為興趣做的一個小 Side-Project,他特別的點在於是一個全部本地化的模型,而且可以像鋼鐵人裡面一樣與 `Jarvis` 對話,聽起來就超級酷的吧!隨著 Manus 的進化,我們也相應的加入更多功能!
|
||||||
|
|
||||||
|
**Q:它比 Manus 好在哪裡?**
|
||||||
|
|
||||||
|
不不不,AgenticSeek 和 Manus 是不同取向的東西,我們優先考慮的是本地執行和隱私,而不是基於雲端。這是一個與 Manus 相比起來更有趣且易使用的方案!
|
||||||
|
|
||||||
|
**Q: 是否支持中文以外的语言?**
|
||||||
|
|
||||||
|
DeepSeek R1 天生會说中文
|
||||||
|
|
||||||
|
但注意:代理路由系统只懂英文,所以必须通过 config.ini 的 languages 参数(如 languages = en zh)告诉系统:
|
||||||
|
|
||||||
|
如果不設定中文?後果可能是:你讓它寫代码,结果跳出来个"醫生代理"(虽然我们根本没有这个代理... 但系统會一脸懵圈!)
|
||||||
|
|
||||||
|
实际上會下载一个小型翻译模型来协助任务分配
|
||||||
|
|
||||||
|
## 貢獻
|
||||||
|
|
||||||
|
我們正在尋找開發者來改善 AgenticSeek!你可以在 Issues 查看未解決的問題或和我們討論更酷的新功能!
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Contribution guide](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## 维护者:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | 巴黎時間 | (有时很忙)
|
||||||
|
|
||||||
|
> [https://github.com/antoineVIVIES](https://github.com/antoineVIVIES) | 台北時間 | (經常很忙)
|
||||||
|
|
||||||
|
> [steveh8758](https://github.com/steveh8758) | 台北時間 | (總是很忙)
|
||||||
@@ -0,0 +1,498 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./media/whale_readme.jpg">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
[English](./README.md) | [繁體中文](./README_CHT.md) | [日本語](./README_JP.md) | Français
|
||||||
|
|
||||||
|
# AgenticSeek: Une IA comme Manus mais à base d'agents DeepSeek R1 fonctionnant en local.
|
||||||
|
|
||||||
|
Une alternative **entièrement locale** à Manus AI, un assistant IA qui code, explore votre système de fichiers, navigue sur le web et corrige ses erreurs, tout cela sans envoyer la moindre donnée dans le cloud. Cet agent autonome fonctionne entièrement sur votre hardware, garantissant la confidentialité de vos données.
|
||||||
|
|
||||||
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460)
|
||||||
|
|
||||||
|
> 🛠️ **En cours de développement** – On cherche activement des contributeurs!
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||||
|
|
||||||
|
> *Recherche sur le web des activités à faire à Paris*
|
||||||
|
|
||||||
|
> *Code le jeu snake en python*
|
||||||
|
|
||||||
|
> *J'aimerais que tu trouve une api météo et que tu me code une application qui affiche la météo à Toulouse*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Fonctionnalités:
|
||||||
|
|
||||||
|
- **100% Local**: Fonctionne en local sur votre PC. Vos données restent les vôtres.
|
||||||
|
|
||||||
|
- **Accès à vos Fichiers**: Utilise bash pour naviguer et manipuler vos fichiers.
|
||||||
|
|
||||||
|
- **Codage semi-autonome**: Peut écrire, déboguer et exécuter du code en Python, C, Golang et d'autres langages à venir.
|
||||||
|
|
||||||
|
- **Routage d'Agent**: Sélectionne automatiquement l’agent approprié pour la tâche.
|
||||||
|
|
||||||
|
- **Planification**: Pour les taches complexe utilise plusieurs agents.
|
||||||
|
|
||||||
|
- **Navigation Web Autonome**: Navigation web autonome.
|
||||||
|
|
||||||
|
- **Memoire efficace**: Gestion efficace de la mémoire et des sessions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Installation**
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 1️⃣ Cloner le repo et configurer
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2 **Créer un environnement virtuel**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m venv agentic_seek_env
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
# Sur Windows: agentic_seek_env\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3️⃣ **Installation**
|
||||||
|
|
||||||
|
**Automatique:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manuel:**
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
**Nous recommandons d’utiliser au minimum DeepSeek 14B, les modèles plus petits ont du mal avec l’utilisation des outils et oublient rapidement le contexte.**
|
||||||
|
|
||||||
|
Lancer votre provider local, par exemple avec ollama:
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
**Configurer le config.ini**
|
||||||
|
|
||||||
|
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 # 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 |
|
||||||
|
|-------------|---------|-----------------------------------------------------------|
|
||||||
|
| ollama | Oui | Exécutez des LLM localement avec facilité en utilisant ollama comme fournisseur LLM |
|
||||||
|
| lm-studio | Oui | Exécutez un LLM localement avec LM studio (définissez `provider_name` sur `lm-studio`) |
|
||||||
|
| openai | Oui | Utilisez une API local compatible avec openai |
|
||||||
|
|
||||||
|
|
||||||
|
### **Démarrer les services & Exécuter**
|
||||||
|
|
||||||
|
Activez votre environnement Python si nécessaire.
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
Démarrez les services requis. Cela lancera tous les services définis dans le fichier docker-compose.yml, y compris :
|
||||||
|
- searxng
|
||||||
|
- redis (nécessaire pour searxng)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 1 :** Exécuter avec l'interface CLI.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2 :** Exécuter avec l'interface Web.
|
||||||
|
|
||||||
|
Démarrez le backend.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Allez sur `http://localhost:3000/` et vous devriez voir l'interface web.
|
||||||
|
|
||||||
|
Veuillez noter que l'interface web ne diffuse pas les messages en continu pour le moment.
|
||||||
|
|
||||||
|
|
||||||
|
Voyez la section **Utilisation** si vous ne comprenez pas comment l’utiliser
|
||||||
|
|
||||||
|
Voyez la section **Problèmes** connus si vous rencontrez des problèmes
|
||||||
|
|
||||||
|
Voyez la section **Exécuter avec une API** si votre matériel ne peut pas exécuter DeepSeek localement
|
||||||
|
|
||||||
|
Voyez la section **Configuration** pour une explication détaillée du fichier de configuration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Utilisation
|
||||||
|
|
||||||
|
Assurez-vous que les services sont en cours d’exécution avec ./start_services.sh et lancez AgenticSeek avec le CLI ou l'interface Web.
|
||||||
|
|
||||||
|
**CLI:**
|
||||||
|
Vous verrez un prompt : ">>> "
|
||||||
|
Cela indique qu’AgenticSeek attend que vous saisissiez des instructions.
|
||||||
|
Vous pouvez également utiliser la reconnaissance vocale en définissant `listen = True` dans la configuration.
|
||||||
|
Pour quitter, dites simplement `goodbye`.
|
||||||
|
|
||||||
|
**Interface:**
|
||||||
|
|
||||||
|
Assurez-vous d'avoir bien démarré le backend avec `python3 api.py`.
|
||||||
|
Allez sur `localhost:3000` où vous verrez une interface web.
|
||||||
|
Tapez simplement votre message et patientez.
|
||||||
|
Si vous n'avez pas d'interface sur `localhost:3000`, c'est que vous n'avez pas démarré les services avec `start_services.sh`.
|
||||||
|
|
||||||
|
Voici quelques exemples d’utilisation :
|
||||||
|
|
||||||
|
### Programmation
|
||||||
|
|
||||||
|
> *Aide-moi avec la multiplication de matrices en Golang*
|
||||||
|
|
||||||
|
> *Initalize un nouveau project python, setup le readme, gitignore etc.. et fait un premier commit*
|
||||||
|
|
||||||
|
> *Fais un jeu snake en Python*
|
||||||
|
|
||||||
|
### Recherche web
|
||||||
|
|
||||||
|
> *Fais une recherche sur le web pour trouver des startups technologiques au Japon qui travaillent sur des recherches avancées en IA*
|
||||||
|
|
||||||
|
> *Peux-tu trouver sur internet qui a créé agenticSeek ?*
|
||||||
|
|
||||||
|
> *Peux-tu trouver sur quel site je peux acheter une RTX 4090 à bas prix ?*
|
||||||
|
|
||||||
|
### Fichier
|
||||||
|
|
||||||
|
> *Hé, peux-tu trouver où est contrat.pdf ? Je l’ai perdu*
|
||||||
|
|
||||||
|
> *Montre-moi combien d’espace il me reste sur mon disque*
|
||||||
|
|
||||||
|
> *Trouve et lis le fichier README.md et suis les instructions d’installation*
|
||||||
|
|
||||||
|
### Conversation
|
||||||
|
|
||||||
|
> *Parle-moi de la France*
|
||||||
|
|
||||||
|
> *Quel est le sens de la vie ?*
|
||||||
|
|
||||||
|
> *Donne moi une recette simple pour ce midi j'ai pas d'inspi*
|
||||||
|
|
||||||
|
Après avoir saisi votre requête, AgenticSeek attribuera le meilleur agent pour la tâche.
|
||||||
|
|
||||||
|
Le système de routage des agents peut parfois ne pas toujours attribuer le bon agent en fonction de votre requête.
|
||||||
|
|
||||||
|
Par conséquent, vous devez être assez explicite sur ce que vous voulez et sur la manière dont l’IA doit procéder. Par exemple, si vous voulez qu’elle effectue une recherche sur le web, ne dites pas :
|
||||||
|
|
||||||
|
Connait-tu de bons pays pour voyager seul ?
|
||||||
|
|
||||||
|
Dites plutôt :
|
||||||
|
|
||||||
|
Fait une recherche sur le web, quels sont les meilleurs pays pour voyager seul?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Exécuter le LLM sur votre propre serveur**
|
||||||
|
|
||||||
|
Si vous disposez d’un ordinateur puissant ou d’un serveur que vous voulez utiliser, mais que vous souhaitez y accéder depuis votre ordinateur portable, vous avez la possibilité d’exécuter le LLM sur un serveur distant.
|
||||||
|
|
||||||
|
### 1️⃣ **Configurer et démarrer les scripts du serveur**
|
||||||
|
|
||||||
|
Sur votre "serveur" qui exécutera le modèle IA, obtenez l’adresse IP
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1
|
||||||
|
```
|
||||||
|
|
||||||
|
Remarque : Pour Windows ou macOS, utilisez respectivement ipconfig ou ifconfig pour trouver l’adresse IP.
|
||||||
|
|
||||||
|
Clonez le dépôt et entrez dans le dossier server/.
|
||||||
|
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth 1 https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek/server/
|
||||||
|
```
|
||||||
|
|
||||||
|
Installez les dépendances spécifiques au serveur :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Exécutez le script du serveur.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 app.py --provider ollama --port 3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous avez le choix entre utiliser ollama et llamacpp comme service LLM.
|
||||||
|
|
||||||
|
### 2️⃣ **Lancer**
|
||||||
|
|
||||||
|
Maintenant, sur votre ordinateur personnel :
|
||||||
|
|
||||||
|
Modifiez le fichier config.ini pour définir provider_name sur server et provider_model sur deepseek-r1:14b.
|
||||||
|
|
||||||
|
Définissez provider_server_address sur l’adresse IP de la machine qui exécutera le modèle.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:14b
|
||||||
|
provider_server_address = x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
||||||
|
|
||||||
|
## **Exécuter avec une API externe**
|
||||||
|
|
||||||
|
AVERTISSEMENT : Assurez-vous qu’il n’y a pas d’espace en fin de ligne dans la configuration.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000 # n'importe pas
|
||||||
|
```
|
||||||
|
|
||||||
|
**Liste de provideurs API**
|
||||||
|
| Fournisseur | Local ? | Description |
|
||||||
|
|--------------|---------|-----------------------------------------------------------|
|
||||||
|
| openai | Non | Utilise l'API ChatGPT |
|
||||||
|
| deepseek-api | Non | API Deepseek (non privé) |
|
||||||
|
| huggingface | Non | API Hugging-Face (non privé) |
|
||||||
|
| togetherAI | Non | Utilise l'API Together AI (non privé) |
|
||||||
|
| google | Non | Utilise l'API Google Gemini (non privé) |
|
||||||
|
|
||||||
|
Ensuite, exécutez avec le CLI ou l'interface graphique comme expliqué dans la section pour les fournisseurs locaux.
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
Exemple de configuration :
|
||||||
|
```
|
||||||
|
[MAIN]
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:1.5b
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en fr
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**Explication du fichier config.ini**:
|
||||||
|
|
||||||
|
`is_local` -> Exécute l’agent localement (True) ou sur un serveur distant (False).
|
||||||
|
|
||||||
|
`provider_name` -> Le fournisseur à utiliser (parmi : ollama, server, lm-studio, deepseek-api).
|
||||||
|
|
||||||
|
`provider_model` -> Le modèle utilisé, par exemple, deepseek-r1:1.5b.
|
||||||
|
|
||||||
|
`provider_server_address` -> Adresse du serveur, par exemple, 127.0.0.1:11434 pour local. Définissez n’importe quoi pour une API non locale.
|
||||||
|
|
||||||
|
`agent_name` -> Nom de l’agent, par exemple, Friday. Utilisé comme mot déclencheur pour la reconnaissance vocale.
|
||||||
|
|
||||||
|
`recover_last_session` -> Reprend la dernière session (True) ou non (False).
|
||||||
|
|
||||||
|
`save_session` -> Sauvegarde les données de la session (True) ou non (False).
|
||||||
|
|
||||||
|
`speak` -> Active la sortie vocale (True) ou non (False).
|
||||||
|
|
||||||
|
`listen` -> Écoute les entrées vocales (True) ou non (False).
|
||||||
|
|
||||||
|
`work_dir` -> Dossier auquel l’IA aura accès, par exemple : /Users/user/Documents/.
|
||||||
|
|
||||||
|
`jarvis_personality` -> Utilise une personnalité inspiré de Jarvis (True) ou non (False). Cela utilise simplement une prompt alternative. Marche moins bien en français.
|
||||||
|
|
||||||
|
`headless_browser` -> Exécute le navigateur sans fenêtre visible (True) ou non (False).
|
||||||
|
|
||||||
|
`stealth_mode` -> Rend la détection des bots plus difficile. Le seul inconvénient est que vous devez installer manuellement l’extension anticaptcha.
|
||||||
|
|
||||||
|
`languages` -> La liste de languages supportés (nécessaire pour le routage d'agents). Plus la liste est longue. Plus un nombre important de modèles sera téléchargés.
|
||||||
|
|
||||||
|
## Providers
|
||||||
|
|
||||||
|
Le tableau ci-dessous montre les LLM providers disponibles :
|
||||||
|
|
||||||
|
| Provider | Local? | Description |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| ollama | Yes | Exécutez des LLM localement avec facilité en utilisant Ollama comme fournisseur LLM
|
||||||
|
| server | Yes | Hébergez le modèle sur une autre machine, exécutez sur votre machine locale
|
||||||
|
| lm-studio | Yes | Exécutez un LLM localement avec LM Studio (définissez provider_name sur lm-studio)
|
||||||
|
| openai | No | Utilise l'API ChatGPT (pas privé) |
|
||||||
|
| deepseek-api | No | Utilise l'API Deepseek (pas privé) |
|
||||||
|
| huggingface| No | Utilise Hugging-Face (pas privé) |
|
||||||
|
| together| No | Utilise l'api Together AI |
|
||||||
|
|
||||||
|
Pour sélectionner un provider LLM, modifiez le config.ini :
|
||||||
|
|
||||||
|
```
|
||||||
|
is_local = False
|
||||||
|
provider_name = openai
|
||||||
|
provider_model = gpt-4o
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
`is_local` : doit être True pour tout LLM exécuté localement, sinon False.
|
||||||
|
|
||||||
|
`provider_name` : Sélectionnez le fournisseur à utiliser par son nom, voir la liste des fournisseurs ci-dessus.
|
||||||
|
|
||||||
|
`provider_model` : Définissez le modèle à utiliser par l’agent.
|
||||||
|
|
||||||
|
`provider_server_address` : peut être défini sur n’importe quoi si vous n’utilisez pas le fournisseur server.
|
||||||
|
|
||||||
|
# Problèmes connus
|
||||||
|
|
||||||
|
## Problèmes avec Chromedriver
|
||||||
|
|
||||||
|
Erreur #1:**incompatibilité**
|
||||||
|
|
||||||
|
`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`
|
||||||
|
|
||||||
|
Cela se produit s’il y a une incompatibilité entre votre navigateur et la version de chromedriver.
|
||||||
|
|
||||||
|
Vous devez naviguer pour télécharger la dernière version :
|
||||||
|
|
||||||
|
https://developer.chrome.com/docs/chromedriver/downloads
|
||||||
|
|
||||||
|
Si vous utilisez Chrome version 115 ou plus récent, allez sur :
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
Et téléchargez la version de chromedriver correspondant à votre système d’exploitation.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Si cette section est incomplète, merci de faire une nouvelle issue sur github.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
**Q: Quel matériel est nécessaire ?**
|
||||||
|
|
||||||
|
| Taille du Modèle | GPU | Commentaire |
|
||||||
|
|--------------------|------|----------------------------------------------------------|
|
||||||
|
| 7B | 8 Go VRAM | ⚠️ Non recommandé. Performances médiocres, hallucinations fréquentes, et l'agent planificateur échouera probablement. |
|
||||||
|
| 14B | 12 Go VRAM (par ex. RTX 3060) | ✅ Utilisable pour des tâches simples. Peut rencontrer des difficultés avec la navigation web et les tâches de planification. |
|
||||||
|
| 32B | 24+ Go VRAM (par ex. RTX 4090) | 🚀 Réussite avec la plupart des tâches, peut encore avoir des difficultés avec la planification des tâches. |
|
||||||
|
| 70B+ | 48+ Go VRAM (par ex. Mac Studio) | 💪 Excellent. Recommandé pour des cas d'utilisation avancés. |
|
||||||
|
|
||||||
|
**Q: Pourquoi deepseek et pas un autre modèle**
|
||||||
|
|
||||||
|
DeepSeek R1 excelle dans le raisonnement et l’utilisation d’outils pour sa taille. Nous pensons que c’est un choix solide pour nos besoins, bien que d’autres modèles fonctionnent également (bien que moins bien pour un nombre équivalent de paramètres).
|
||||||
|
|
||||||
|
**Q: J'ai une erreur quand je lance le programme, je fait quoi?**
|
||||||
|
|
||||||
|
Assurez-vous qu’Ollama est en cours d’exécution (ollama serve), que votre config.ini correspond à votre fournisseur, et que les dépendances sont installées. Si cela ne fonctionne pas, n’hésitez pas à signaler un problème.
|
||||||
|
|
||||||
|
**Q: C'est vraiment 100% local?**
|
||||||
|
|
||||||
|
Oui, avec les fournisseurs Ollama, lm-studio ou Server, toute la reconnaissance vocale, le LLM et la synthèse vocale fonctionnent localement. Les options non locales (OpenAI ou autres API) sont facultatives.
|
||||||
|
|
||||||
|
**Q: En quoi c'est supérieur à Manus**
|
||||||
|
|
||||||
|
Il ne l'est certainement pas, mais nous privilégions l’exécution locale et la confidentialité par rapport à une approche basée sur le cloud. C’est une alternative plus accessible et surtout moins cher !
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
Nous recherchons des développeurs pour améliorer AgenticSeek ! Consultez la section "issues" github ou les discussions.
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
[Guide du contributeur](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Mainteneurs:
|
||||||
|
> [Fosowl](https://github.com/Fosowl)
|
||||||
|
> [steveh8758](https://github.com/steveh8758)
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
@@ -0,0 +1,570 @@
|
|||||||
|
# AgenticSeek: プライベートなローカルManus代替
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<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) | 日本語
|
||||||
|
|
||||||
|
*Manus AIの**100%ローカルな代替**となるこの音声対応AIアシスタントは、自律的にウェブを閲覧し、コードを書き、タスクを計画しながら、すべてのデータをあなたのデバイスに保持します。ローカル推論モデルに合わせて調整されており、完全にあなたのハードウェア上で動作するため、完全なプライバシーとクラウドへの依存ゼロを保証します。*
|
||||||
|
|
||||||
|
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/8hGDaME3TC) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||||
|
|
||||||
|
### なぜAgenticSeekなのか?
|
||||||
|
|
||||||
|
* 🔒 完全ローカル&プライベート - すべてがあなたのマシン上で実行されます — クラウドなし、データ共有なし。あなたのファイル、会話、検索はプライベートに保たれます。
|
||||||
|
|
||||||
|
* 🌐 スマートなウェブブラウジング - AgenticSeekは自分でインターネットを閲覧できます — 検索、読み取り、情報抽出、ウェブフォーム入力 — すべてハンズフリーで。
|
||||||
|
|
||||||
|
* 💻 自律型コーディングアシスタント - コードが必要ですか?Python、C、Go、Javaなどでプログラムを書き、デバッグし、実行できます — すべて監視なしで。
|
||||||
|
|
||||||
|
* 🧠 スマートエージェント選択 - あなたが尋ねると、タスクに最適なエージェントを自動的に見つけ出します。まるで専門家チームが助けてくれるようです。
|
||||||
|
|
||||||
|
* 📋 複雑なタスクの計画と実行 - 旅行計画から複雑なプロジェクトまで — 大きなタスクをステップに分割し、複数のAIエージェントを使って物事を成し遂げることができます。
|
||||||
|
|
||||||
|
* 🎙️ 音声対応 - クリーンで高速、未来的な音声と音声認識により、まるでSF映画のパーソナルAIのように話しかけることができます。
|
||||||
|
|
||||||
|
### **デモ**
|
||||||
|
|
||||||
|
> *agenticSeekプロジェクトを検索し、必要なスキルを学び、その後CV_candidates.zipを開いて、プロジェクトに最も適した候補者を教えてください。*
|
||||||
|
|
||||||
|
https://github.com/user-attachments/assets/b8ca60e9-7b3b-4533-840e-08f9ac426316
|
||||||
|
|
||||||
|
免責事項:このデモは、表示されるすべてのファイル(例:CV_candidates.zip)を含め、完全に架空のものです。私たちは企業ではなく、候補者ではなくオープンソースの貢献者を求めています。
|
||||||
|
|
||||||
|
> 🛠️ **作業中** – 貢献者を募集中です!
|
||||||
|
|
||||||
|
## インストール
|
||||||
|
|
||||||
|
Chrome Driver、Docker、Python 3.10がインストールされていることを確認してください。
|
||||||
|
|
||||||
|
セットアップにはPython 3.10を正確に使用することを強くお勧めします。そうでない場合、依存関係のエラーが発生する可能性があります。
|
||||||
|
|
||||||
|
Chromeドライバーに関する問題については、**Chromedriver**セクションを参照してください。
|
||||||
|
|
||||||
|
### 1️⃣ **リポジトリのクローンとセットアップ**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/Fosowl/agenticSeek.git
|
||||||
|
cd agenticSeek
|
||||||
|
mv .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2️ **仮想環境の作成**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 -m venv agentic_seek_env
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
# Windowsの場合: agentic_seek_env\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
./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を実行するためのセットアップ
|
||||||
|
|
||||||
|
**少なくともDeepseek 14Bの使用を推奨します。より小さなモデルは、特にウェブブラウジングのタスクで苦労します。**
|
||||||
|
|
||||||
|
|
||||||
|
**ローカルプロバイダーのセットアップ**
|
||||||
|
|
||||||
|
ローカルプロバイダーを開始します。例えばollamaの場合:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ollama serve
|
||||||
|
```
|
||||||
|
|
||||||
|
サポートされているローカルプロバイダーのリストについては、以下を参照してください。
|
||||||
|
|
||||||
|
**config.iniの更新**
|
||||||
|
|
||||||
|
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 # ハードウェアに合ったモデルを選択してください
|
||||||
|
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(例:llama.cppサーバー)を使用します |
|
||||||
|
|
||||||
|
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
||||||
|
|
||||||
|
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
||||||
|
|
||||||
|
*ハードウェアがローカルでdeepseekを実行できない場合は、**APIで実行**セクションを参照してください*
|
||||||
|
|
||||||
|
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## APIで実行するためのセットアップ
|
||||||
|
|
||||||
|
`config.ini`で目的のプロバイダーを設定します。APIプロバイダーのリストについては、以下を参照してください。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = google
|
||||||
|
provider_model = gemini-2.0-flash
|
||||||
|
provider_server_address = 127.0.0.1:5000 # 関係ありません
|
||||||
|
```
|
||||||
|
警告:設定に末尾のスペースがないことを確認してください。
|
||||||
|
|
||||||
|
APIキーをエクスポートします:`export <<PROVIDER>>_API_KEY="xxx"`
|
||||||
|
|
||||||
|
例:`export TOGETHER_API_KEY="xxxxx"`
|
||||||
|
|
||||||
|
**APIプロバイダーのリスト**
|
||||||
|
|
||||||
|
| プロバイダー | ローカル? | 説明 |
|
||||||
|
|-----------|--------|-----------------------------------------------------------|
|
||||||
|
| 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の実行](#サービスの開始と実行)
|
||||||
|
|
||||||
|
*問題が発生した場合は、**既知の問題**セクションを参照してください*
|
||||||
|
|
||||||
|
*詳細な設定ファイルの説明については、**設定**セクションを参照してください。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## サービスの開始と実行
|
||||||
|
|
||||||
|
必要に応じてPython環境をアクティブ化します。
|
||||||
|
```sh
|
||||||
|
source agentic_seek_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
必要なサービスを開始します。これにより、docker-compose.ymlからすべてのサービスが開始されます。これには以下が含まれます:
|
||||||
|
- searxng
|
||||||
|
- redis(searxngに必要)
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo ./start_services.sh # MacOS
|
||||||
|
start ./start_services.cmd # Window
|
||||||
|
```
|
||||||
|
|
||||||
|
**オプション1:** CLIインターフェースで実行します。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 cli.py
|
||||||
|
```
|
||||||
|
|
||||||
|
CLIモードでは、config.iniで`headless_browser`をFalseに設定することをお勧めします。
|
||||||
|
|
||||||
|
**オプション2:** Webインターフェースで実行します。
|
||||||
|
|
||||||
|
バックエンドを開始します。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
`http://localhost:3000/`にアクセスすると、Webインターフェースが表示されます。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
`./start_services.sh`でサービスが起動していることを確認し、CLIモードの場合は`python3 cli.py`で、Webインターフェースの場合は`python3 api.py`を実行してから`localhost:3000`にアクセスしてAgenticSeekを実行します。
|
||||||
|
|
||||||
|
設定で`listen = True`を設定することで、音声認識を使用することもできます。CLIモードのみ。
|
||||||
|
|
||||||
|
終了するには、単に`goodbye`と発言/入力します。
|
||||||
|
|
||||||
|
以下に使用例をいくつか示します:
|
||||||
|
|
||||||
|
> *Pythonでスネークゲームを作って!*
|
||||||
|
|
||||||
|
> *フランスのレンヌでトップのカフェをウェブ検索し、3つのカフェのリストとその住所をrennes_cafes.txtに保存して。*
|
||||||
|
|
||||||
|
> *数値の階乗を計算するGoプログラムを書いて、それをfactorial.goとしてワークスペースに保存して。*
|
||||||
|
|
||||||
|
> *summer_picturesフォルダ内のすべてのJPGファイルを検索し、今日の日付で名前を変更し、名前変更されたファイルのリストをphotos_list.txtに保存して。*
|
||||||
|
|
||||||
|
> *2024年の人気のSF映画をオンラインで検索し、今夜観る映画を3つ選んで。リストをmovie_night.txtに保存して。*
|
||||||
|
|
||||||
|
> *2025年の最新AIニュース記事をウェブで検索し、3つ選択して、それらのタイトルと要約をスクレイピングするPythonスクリプトを書いて。スクリプトをnews_scraper.pyとして、要約を/home/projectsのai_news.txtに保存して。*
|
||||||
|
|
||||||
|
> *金曜日、無料の株価APIをウェブで検索し、supersuper7434567@gmail.comで登録し、そのAPIを使用してテスラの日々の価格を取得するPythonスクリプトを書いて、結果をstock_prices.csvに保存して。*
|
||||||
|
|
||||||
|
*フォーム入力機能はまだ実験的であり、失敗する可能性があることに注意してください。*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
クエリを入力すると、AgenticSeekはタスクに最適なエージェントを割り当てます。
|
||||||
|
|
||||||
|
これは初期のプロトタイプであるため、エージェントルーティングシステムがクエリに基づいて常に適切なエージェントを割り当てるとは限りません。
|
||||||
|
|
||||||
|
したがって、何をしたいのか、AIがどのように進むべきかについて非常に明確にする必要があります。たとえば、ウェブ検索を実行させたい場合は、次のように言わないでください:
|
||||||
|
|
||||||
|
`一人旅に適した良い国を知っていますか?`
|
||||||
|
|
||||||
|
代わりに、次のように尋ねてください:
|
||||||
|
|
||||||
|
`ウェブ検索をして、一人旅に最適な国を見つけてください`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **独自のサーバーでLLMを実行するためのセットアップ**
|
||||||
|
|
||||||
|
強力なコンピューターまたは使用できるサーバーがあるが、ラップトップから使用したい場合は、カスタムLLMサーバーを使用してリモートサーバーでLLMを実行するオプションがあります。
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:WindowsまたはmacOSの場合、それぞれipconfigまたはifconfigを使用してIPアドレスを見つけます。
|
||||||
|
|
||||||
|
リポジトリをクローンし、`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
|
||||||
|
```
|
||||||
|
|
||||||
|
LLMサービスとして`ollama`と`llamacpp`のどちらかを選択できます。
|
||||||
|
|
||||||
|
|
||||||
|
次に、個人のコンピュータで:
|
||||||
|
|
||||||
|
`config.ini`ファイルを変更して、`provider_name`を`server`に、`provider_model`を`deepseek-r1:xxb`に設定します。
|
||||||
|
`provider_server_address`をモデルを実行するマシンのIPアドレスに設定します。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[MAIN]
|
||||||
|
is_local = False
|
||||||
|
provider_name = server
|
||||||
|
provider_model = deepseek-r1:70b
|
||||||
|
provider_server_address = x.x.x.x:3333
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
次のステップ:[サービスの開始とAgenticSeekの実行](#サービスの開始と実行)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 音声認識
|
||||||
|
|
||||||
|
現在、音声認識は英語でのみ機能することに注意してください。
|
||||||
|
|
||||||
|
音声認識機能はデフォルトで無効になっています。有効にするには、config.iniファイルでlistenオプションをTrueに設定します:
|
||||||
|
|
||||||
|
```
|
||||||
|
listen = True
|
||||||
|
```
|
||||||
|
|
||||||
|
有効にすると、音声認識機能は、入力を処理し始める前にトリガーキーワード(エージェントの名前)をリッスンします。*config.ini*ファイルで`agent_name`の値を更新することで、エージェントの名前をカスタマイズできます:
|
||||||
|
|
||||||
|
```
|
||||||
|
agent_name = Friday
|
||||||
|
```
|
||||||
|
|
||||||
|
最適な認識のためには、エージェント名として「John」や「Emma」のような一般的な英語の名前を使用することをお勧めします。
|
||||||
|
|
||||||
|
トランスクリプトが表示され始めたら、エージェントの名前を声に出して起動します(例:「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:32b
|
||||||
|
provider_server_address = 127.0.0.1:11434
|
||||||
|
agent_name = Friday
|
||||||
|
recover_last_session = False
|
||||||
|
save_session = False
|
||||||
|
speak = False
|
||||||
|
listen = False
|
||||||
|
work_dir = /Users/mlg/Documents/ai_folder
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en zh
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = False
|
||||||
|
stealth_mode = False
|
||||||
|
```
|
||||||
|
|
||||||
|
**説明**:
|
||||||
|
|
||||||
|
- 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で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を変更します:
|
||||||
|
|
||||||
|
```
|
||||||
|
is_local = True
|
||||||
|
provider_name = ollama
|
||||||
|
provider_model = deepseek-r1:32b
|
||||||
|
provider_server_address = 127.0.0.1:5000
|
||||||
|
```
|
||||||
|
`is_local`: ローカルで実行されるLLMの場合はTrue、それ以外の場合はFalseである必要があります。
|
||||||
|
|
||||||
|
`provider_name`: 使用するプロバイダーを名前で選択します。上記のプロバイダーリストを参照してください。
|
||||||
|
|
||||||
|
`provider_model`: エージェントが使用するモデルを設定します。
|
||||||
|
|
||||||
|
`provider_server_address`: サーバーアドレス。APIプロバイダーには使用されません。
|
||||||
|
|
||||||
|
# 既知の問題
|
||||||
|
|
||||||
|
## 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以降を使用している場合は、以下にアクセスしてください:
|
||||||
|
|
||||||
|
https://googlechromelabs.github.io/chrome-for-testing/
|
||||||
|
|
||||||
|
そして、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: どのようなハードウェアが必要ですか?**
|
||||||
|
|
||||||
|
| モデルサイズ | GPU | コメント |
|
||||||
|
|-----------|------------|--------------------------------------------------------------------------|
|
||||||
|
| 7B | 8GB VRAM | ⚠️ 非推奨。パフォーマンスが悪く、幻覚が頻繁に発生し、プランナーエージェントは失敗する可能性が高いです。 |
|
||||||
|
| 14B | 12GB VRAM(例:RTX 3060) | ✅ 簡単なタスクには使用可能。ウェブブラウジングや計画タスクで苦労する可能性があります。 |
|
||||||
|
| 32B | 24GB以上のVRAM(例:RTX 4090) | 🚀 ほとんどのタスクで成功しますが、タスク計画でまだ苦労する可能性があります。 |
|
||||||
|
| 70B+ | 48GB以上のVRAM(例:mac studio) | 💪 素晴らしい。高度なユースケースに推奨されます。 |
|
||||||
|
|
||||||
|
**Q: なぜ他のモデルではなくDeepseek R1なのですか?**
|
||||||
|
|
||||||
|
Deepseek R1は、そのサイズに対して推論とツール使用に優れています。私たちのニーズに合っていると考えており、他のモデルも正常に動作しますが、Deepseekが私たちの主要な選択肢です。
|
||||||
|
|
||||||
|
**Q: `cli.py`を実行するとエラーが発生します。どうすればよいですか?**
|
||||||
|
|
||||||
|
ローカルが実行されていること(`ollama serve`)、`config.ini`がプロバイダーと一致していること、依存関係がインストールされていることを確認してください。それでも解決しない場合は、遠慮なく問題を提起してください。
|
||||||
|
|
||||||
|
**Q: 本当に100%ローカルで実行できますか?**
|
||||||
|
|
||||||
|
はい、Ollama、lm-studio、またはサーバープロバイダーを使用すると、すべての音声認識、LLM、テキスト読み上げモデルがローカルで実行されます。非ローカルオプション(OpenAIまたはその他のAPI)はオプションです。
|
||||||
|
|
||||||
|
**Q: Manusがあるのに、なぜAgenticSeekを使うべきなのですか?**
|
||||||
|
|
||||||
|
これは、AIエージェントへの関心から始めたサイドプロジェクトです。特別なのは、ローカルモデルを使用し、APIを避けたいということです。
|
||||||
|
私たちはJarvisとFriday(アイアンマン映画)からインスピレーションを得て「クール」にしましたが、機能性についてはManusからより多くのインスピレーションを得ています。なぜなら、それが人々が最初に望むもの、つまりローカルなManusの代替だからです。
|
||||||
|
Manusとは異なり、AgenticSeekは外部システムからの独立性を優先し、より多くの制御、プライバシーを提供し、APIコストを回避します。
|
||||||
|
|
||||||
|
## 貢献する
|
||||||
|
|
||||||
|
AgenticSeekを改善するための開発者を募集しています!オープンな問題やディスカッションを確認してください。
|
||||||
|
|
||||||
|
[貢献ガイド](./docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
[](https://www.star-history.com/#Fosowl/agenticSeek&Date)
|
||||||
|
|
||||||
|
## メンテナー:
|
||||||
|
|
||||||
|
> [Fosowl](https://github.com/Fosowl) | パリ時間
|
||||||
|
|
||||||
|
> [antoineVIVIES](https://github.com/antoineVIVIES) | Taipei Time
|
||||||
|
|
||||||
|
> [steveh8758](https://github.com/steveh8758) | 台北時間 |(常に忙しい)
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
import uvicorn
|
||||||
|
import aiofiles
|
||||||
|
import configparser
|
||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
from typing import List
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from sources.llm_provider import Provider
|
||||||
|
from sources.interaction import Interaction
|
||||||
|
from sources.agents import CasualAgent, CoderAgent, FileAgent, PlannerAgent, BrowserAgent
|
||||||
|
from sources.browser import Browser, create_driver
|
||||||
|
from sources.utility import pretty_print
|
||||||
|
from sources.logger import Logger
|
||||||
|
from sources.schemas import QueryRequest, QueryResponse
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
from celery import Celery
|
||||||
|
|
||||||
|
api = FastAPI(title="AgenticSeek API", version="0.1.0")
|
||||||
|
celery_app = Celery("tasks", broker="redis://localhost:6379/0", backend="redis://localhost:6379/0")
|
||||||
|
celery_app.conf.update(task_track_started=True)
|
||||||
|
logger = Logger("backend.log")
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
|
api.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(".screenshots"):
|
||||||
|
os.makedirs(".screenshots")
|
||||||
|
api.mount("/screenshots", StaticFiles(directory=".screenshots"), name="screenshots")
|
||||||
|
|
||||||
|
def initialize_system():
|
||||||
|
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
|
||||||
|
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
||||||
|
languages = config["MAIN"]["languages"].split(' ')
|
||||||
|
|
||||||
|
provider = Provider(
|
||||||
|
provider_name=config["MAIN"]["provider_name"],
|
||||||
|
model=config["MAIN"]["provider_model"],
|
||||||
|
server_address=config["MAIN"]["provider_server_address"],
|
||||||
|
is_local=config.getboolean('MAIN', 'is_local')
|
||||||
|
)
|
||||||
|
logger.info(f"Provider initialized: {provider.provider_name} ({provider.model})")
|
||||||
|
|
||||||
|
browser = Browser(
|
||||||
|
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]),
|
||||||
|
anticaptcha_manual_install=stealth_mode
|
||||||
|
)
|
||||||
|
logger.info("Browser initialized")
|
||||||
|
|
||||||
|
agents = [
|
||||||
|
CasualAgent(
|
||||||
|
name=config["MAIN"]["agent_name"],
|
||||||
|
prompt_path=f"prompts/{personality_folder}/casual_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
CoderAgent(
|
||||||
|
name="coder",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/coder_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
FileAgent(
|
||||||
|
name="File Agent",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/file_agent.txt",
|
||||||
|
provider=provider, verbose=False
|
||||||
|
),
|
||||||
|
BrowserAgent(
|
||||||
|
name="Browser",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/browser_agent.txt",
|
||||||
|
provider=provider, verbose=False, browser=browser
|
||||||
|
),
|
||||||
|
PlannerAgent(
|
||||||
|
name="Planner",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/planner_agent.txt",
|
||||||
|
provider=provider, verbose=False, browser=browser
|
||||||
|
)
|
||||||
|
]
|
||||||
|
logger.info("Agents initialized")
|
||||||
|
|
||||||
|
interaction = Interaction(
|
||||||
|
agents,
|
||||||
|
tts_enabled=config.getboolean('MAIN', 'speak'),
|
||||||
|
stt_enabled=config.getboolean('MAIN', 'listen'),
|
||||||
|
recover_last_session=config.getboolean('MAIN', 'recover_last_session'),
|
||||||
|
langs=languages
|
||||||
|
)
|
||||||
|
logger.info("Interaction initialized")
|
||||||
|
return interaction
|
||||||
|
|
||||||
|
interaction = initialize_system()
|
||||||
|
is_generating = False
|
||||||
|
query_resp_history = []
|
||||||
|
|
||||||
|
@api.get("/screenshot")
|
||||||
|
async def get_screenshot():
|
||||||
|
logger.info("Screenshot endpoint called")
|
||||||
|
screenshot_path = ".screenshots/updated_screen.png"
|
||||||
|
if os.path.exists(screenshot_path):
|
||||||
|
return FileResponse(screenshot_path)
|
||||||
|
logger.error("No screenshot available")
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=404,
|
||||||
|
content={"error": "No screenshot available"}
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
logger.info("Health check endpoint called")
|
||||||
|
return {"status": "healthy", "version": "0.1.0"}
|
||||||
|
|
||||||
|
@api.get("/is_active")
|
||||||
|
async def is_active():
|
||||||
|
logger.info("Is active endpoint called")
|
||||||
|
return {"is_active": interaction.is_active}
|
||||||
|
|
||||||
|
@api.get("/stop")
|
||||||
|
async def stop():
|
||||||
|
logger.info("Stop endpoint called")
|
||||||
|
interaction.current_agent.request_stop()
|
||||||
|
return JSONResponse(status_code=200, content={"status": "stopped"})
|
||||||
|
|
||||||
|
@api.get("/latest_answer")
|
||||||
|
async def get_latest_answer():
|
||||||
|
global query_resp_history
|
||||||
|
if interaction.current_agent is None:
|
||||||
|
return JSONResponse(status_code=404, content={"error": "No agent available"})
|
||||||
|
uid = str(uuid.uuid4())
|
||||||
|
if not any(q["answer"] == interaction.current_agent.last_answer for q in query_resp_history):
|
||||||
|
query_resp = {
|
||||||
|
"done": "false",
|
||||||
|
"answer": interaction.current_agent.last_answer,
|
||||||
|
"reasoning": interaction.current_agent.last_reasoning,
|
||||||
|
"agent_name": interaction.current_agent.agent_name if interaction.current_agent else "None",
|
||||||
|
"success": interaction.current_agent.success,
|
||||||
|
"blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.get_last_blocks_result())} if interaction.current_agent else {},
|
||||||
|
"status": interaction.current_agent.get_status_message if interaction.current_agent else "No status available",
|
||||||
|
"uid": uid
|
||||||
|
}
|
||||||
|
interaction.current_agent.last_answer = ""
|
||||||
|
interaction.current_agent.last_reasoning = ""
|
||||||
|
query_resp_history.append(query_resp)
|
||||||
|
return JSONResponse(status_code=200, content=query_resp)
|
||||||
|
if query_resp_history:
|
||||||
|
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):
|
||||||
|
try:
|
||||||
|
interaction.last_query = query
|
||||||
|
logger.info("Agents request is being processed")
|
||||||
|
success = await interaction.think()
|
||||||
|
if not success:
|
||||||
|
interaction.last_answer = "Error: No answer from agent"
|
||||||
|
interaction.last_reasoning = "Error: No reasoning from agent"
|
||||||
|
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)}")
|
||||||
|
interaction.last_answer = f""
|
||||||
|
interaction.last_reasoning = f"Error: {str(e)}"
|
||||||
|
interaction.last_success = False
|
||||||
|
raise e
|
||||||
|
|
||||||
|
@api.post("/query", response_model=QueryResponse)
|
||||||
|
async def process_query(request: QueryRequest):
|
||||||
|
global is_generating, query_resp_history
|
||||||
|
logger.info(f"Processing query: {request.query}")
|
||||||
|
query_resp = QueryResponse(
|
||||||
|
done="false",
|
||||||
|
answer="",
|
||||||
|
reasoning="",
|
||||||
|
agent_name="Unknown",
|
||||||
|
success="false",
|
||||||
|
blocks={},
|
||||||
|
status="Ready",
|
||||||
|
uid=str(uuid.uuid4())
|
||||||
|
)
|
||||||
|
if is_generating:
|
||||||
|
logger.warning("Another query is being processed, please wait.")
|
||||||
|
return JSONResponse(status_code=429, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
try:
|
||||||
|
is_generating = True
|
||||||
|
success = await think_wrapper(interaction, request.query)
|
||||||
|
is_generating = False
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
query_resp.answer = interaction.last_answer
|
||||||
|
query_resp.reasoning = interaction.last_reasoning
|
||||||
|
return JSONResponse(status_code=400, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
if interaction.current_agent:
|
||||||
|
blocks_json = {f'{i}': block.jsonify() for i, block in enumerate(interaction.current_agent.get_blocks_result())}
|
||||||
|
else:
|
||||||
|
logger.error("No current agent found")
|
||||||
|
blocks_json = {}
|
||||||
|
query_resp.answer = "Error: No current agent"
|
||||||
|
return JSONResponse(status_code=400, content=query_resp.jsonify())
|
||||||
|
|
||||||
|
logger.info(f"Answer: {interaction.last_answer}")
|
||||||
|
logger.info(f"Blocks: {blocks_json}")
|
||||||
|
query_resp.done = "true"
|
||||||
|
query_resp.answer = interaction.last_answer
|
||||||
|
query_resp.reasoning = interaction.last_reasoning
|
||||||
|
query_resp.agent_name = interaction.current_agent.agent_name
|
||||||
|
query_resp.success = str(interaction.last_success)
|
||||||
|
query_resp.blocks = blocks_json
|
||||||
|
|
||||||
|
query_resp_dict = {
|
||||||
|
"done": query_resp.done,
|
||||||
|
"answer": query_resp.answer,
|
||||||
|
"agent_name": query_resp.agent_name,
|
||||||
|
"success": query_resp.success,
|
||||||
|
"blocks": query_resp.blocks,
|
||||||
|
"status": query_resp.status,
|
||||||
|
"uid": query_resp.uid
|
||||||
|
}
|
||||||
|
query_resp_history.append(query_resp_dict)
|
||||||
|
|
||||||
|
logger.info("Query processed successfully")
|
||||||
|
return JSONResponse(status_code=200, content=query_resp.jsonify())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"An error occurred: {str(e)}")
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
logger.info("Processing finished")
|
||||||
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
|
interaction.save_session()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
envport = os.getenv("BACKEND_PORT")
|
||||||
|
if envport:
|
||||||
|
port = int(envport)
|
||||||
|
else:
|
||||||
|
port = 8000
|
||||||
|
uvicorn.run(api, host="0.0.0.0", port=8000)
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import configparser
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from sources.llm_provider import Provider
|
||||||
|
from sources.interaction import Interaction
|
||||||
|
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent, PlannerAgent, BrowserAgent, McpAgent
|
||||||
|
from sources.browser import Browser, create_driver
|
||||||
|
from sources.utility import pretty_print
|
||||||
|
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
pretty_print("Initializing...", color="status")
|
||||||
|
stealth_mode = config.getboolean('BROWSER', 'stealth_mode')
|
||||||
|
personality_folder = "jarvis" if config.getboolean('MAIN', 'jarvis_personality') else "base"
|
||||||
|
languages = config["MAIN"]["languages"].split(' ')
|
||||||
|
|
||||||
|
provider = Provider(provider_name=config["MAIN"]["provider_name"],
|
||||||
|
model=config["MAIN"]["provider_model"],
|
||||||
|
server_address=config["MAIN"]["provider_server_address"],
|
||||||
|
is_local=config.getboolean('MAIN', 'is_local'))
|
||||||
|
|
||||||
|
browser = Browser(
|
||||||
|
create_driver(headless=config.getboolean('BROWSER', 'headless_browser'), stealth_mode=stealth_mode, lang=languages[0]),
|
||||||
|
anticaptcha_manual_install=stealth_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
agents = [
|
||||||
|
CasualAgent(name=config["MAIN"]["agent_name"],
|
||||||
|
prompt_path=f"prompts/{personality_folder}/casual_agent.txt",
|
||||||
|
provider=provider, verbose=False),
|
||||||
|
CoderAgent(name="coder",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/coder_agent.txt",
|
||||||
|
provider=provider, verbose=False),
|
||||||
|
FileAgent(name="File Agent",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/file_agent.txt",
|
||||||
|
provider=provider, verbose=False),
|
||||||
|
BrowserAgent(name="Browser",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/browser_agent.txt",
|
||||||
|
provider=provider, verbose=False, browser=browser),
|
||||||
|
PlannerAgent(name="Planner",
|
||||||
|
prompt_path=f"prompts/{personality_folder}/planner_agent.txt",
|
||||||
|
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,
|
||||||
|
tts_enabled=config.getboolean('MAIN', 'speak'),
|
||||||
|
stt_enabled=config.getboolean('MAIN', 'listen'),
|
||||||
|
recover_last_session=config.getboolean('MAIN', 'recover_last_session'),
|
||||||
|
langs=languages
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
while interaction.is_active:
|
||||||
|
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()
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
if config.getboolean('MAIN', 'save_session'):
|
||||||
|
interaction.save_session()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -3,9 +3,14 @@ is_local = True
|
|||||||
provider_name = ollama
|
provider_name = ollama
|
||||||
provider_model = deepseek-r1:14b
|
provider_model = deepseek-r1:14b
|
||||||
provider_server_address = 127.0.0.1:11434
|
provider_server_address = 127.0.0.1:11434
|
||||||
agent_name = Friday
|
agent_name = Name_of_your_AI
|
||||||
recover_last_session = True
|
recover_last_session = False
|
||||||
save_session = False
|
save_session = False
|
||||||
speak = True
|
speak = False
|
||||||
listen = False
|
listen = False
|
||||||
work_dir = /Users/mlg/Documents/ai_workplace
|
work_dir = /Users/mlg/Documents/workspace_for_agenticseek
|
||||||
|
jarvis_personality = False
|
||||||
|
languages = en
|
||||||
|
[BROWSER]
|
||||||
|
headless_browser = True
|
||||||
|
stealth_mode = False
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
redis:
|
||||||
|
container_name: redis
|
||||||
|
profiles: ["core", "full"]
|
||||||
|
image: docker.io/valkey/valkey:8-alpine
|
||||||
|
command: valkey-server --save 30 1 --loglevel warning
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
cap_add:
|
||||||
|
- SETGID
|
||||||
|
- SETUID
|
||||||
|
- DAC_OVERRIDE
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "1m"
|
||||||
|
max-file: "1"
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
searxng:
|
||||||
|
container_name: searxng
|
||||||
|
profiles: ["core", "full"]
|
||||||
|
image: docker.io/searxng/searxng:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- ./searxng:/etc/searxng:rw
|
||||||
|
environment:
|
||||||
|
- SEARXNG_BASE_URL=${SEARXNG_BASE_URL:-http://localhost:8080/}
|
||||||
|
- SEARXNG_SECRET_KEY=${SEARXNG_SECRET_KEY}
|
||||||
|
- UWSGI_WORKERS=4
|
||||||
|
- UWSGI_THREADS=4
|
||||||
|
cap_add:
|
||||||
|
- CHOWN
|
||||||
|
- SETGID
|
||||||
|
- SETUID
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "1m"
|
||||||
|
max-file: "1"
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
container_name: frontend
|
||||||
|
profiles: ["core", "full"]
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
dockerfile: Dockerfile.frontend
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./frontend/agentic-seek-front/src:/app/src
|
||||||
|
- ./screenshots:/app/screenshots
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- CHOKIDAR_USEPOLLING=true
|
||||||
|
- REACT_APP_BACKEND_URL=http://0.0.0.0:${BACKEND_PORT:-8000}
|
||||||
|
networks:
|
||||||
|
- agentic-seek-net
|
||||||
|
|
||||||
|
backend:
|
||||||
|
container_name: backend
|
||||||
|
profiles: ["backend", "full"]
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.backend
|
||||||
|
ports:
|
||||||
|
- ${BACKEND_PORT:-7777}:${BACKEND_PORT:-7777}
|
||||||
|
- ${OLLAMA_PORT:-11434}:${OLLAMA_PORT:-11434}
|
||||||
|
- ${LM_STUDIO_PORT:-1234}:${LM_STUDIO_PORT:-1234}
|
||||||
|
- ${CUSTOM_ADDITIONAL_LLM_PORT:-8000}:${CUSTOM_ADDITIONAL_LLM_PORT:-8000}
|
||||||
|
volumes:
|
||||||
|
- ./:/app
|
||||||
|
- ${WORK_DIR:-.}:/opt/workspace
|
||||||
|
command: python3 api.py
|
||||||
|
environment:
|
||||||
|
- SEARXNG_URL=${SEARXNG_BASE_URL:-http://searxng:8080}
|
||||||
|
- REDIS_URL=${REDIS_BASE_URL:-redis://redis:6379/0}
|
||||||
|
- WORK_DIR=/opt/workspace
|
||||||
|
- OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||||
|
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
|
||||||
|
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
||||||
|
- TOGETHER_API_KEY=${TOGETHER_API_KEY}
|
||||||
|
- GOOGLE_API_KEY=${GOOGLE_API_KEY}
|
||||||
|
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||||
|
- HUGGINGFACE_API_KEY=${HUGGINGFACE_API_KEY}
|
||||||
|
- DSK_DEEPSEEK_API_KEY=${DSK_DEEPSEEK_API_KEY}
|
||||||
|
network_mode: "host"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis-data:
|
||||||
|
chrome_profiles:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
agentic-seek-net:
|
||||||
|
driver: bridge
|
||||||
@@ -6,8 +6,8 @@ We as members, contributors, and leaders pledge to make participation in our
|
|||||||
community a harassment-free experience for everyone, regardless of age, body
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
nationality, personal appearance, race, religion, or sexual identity
|
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||||
and orientation.
|
identity and orientation.
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
diverse, inclusive, and healthy community.
|
diverse, inclusive, and healthy community.
|
||||||
@@ -22,17 +22,17 @@ community include:
|
|||||||
* Giving and gracefully accepting constructive feedback
|
* Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
and learning from the experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
* Focusing on what is best not just for us as individuals, but for the overall
|
||||||
overall community
|
community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
advances of any kind
|
any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
* Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
* Publishing others' private information, such as a physical or email address,
|
||||||
address, without their explicit permission
|
without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
@@ -52,15 +52,15 @@ decisions when appropriate.
|
|||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
an individual is officially representing the community in public spaces.
|
an individual is officially representing the community in public spaces.
|
||||||
Examples of representing our community include using an official e-mail address,
|
Examples of representing our community include using an official email address,
|
||||||
posting via an official social media account, or acting as an appointed
|
posting via an official social media account, or acting as an appointed
|
||||||
representative at an online or offline event.
|
representative at an online or offline event.
|
||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement at
|
reported to the community leaders responsible for enforcement:
|
||||||
.
|
you need to send a private message to `fossowl` or `mow8758` on discord.
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
@@ -82,15 +82,15 @@ behavior was inappropriate. A public apology may be requested.
|
|||||||
|
|
||||||
### 2. Warning
|
### 2. Warning
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
**Community Impact**: A violation through a single incident or series of
|
||||||
of actions.
|
actions.
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
interaction with the people involved, including unsolicited interaction with
|
interaction with the people involved, including unsolicited interaction with
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
like social media. Violating these terms may lead to a temporary or
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
permanent ban.
|
ban.
|
||||||
|
|
||||||
### 3. Temporary Ban
|
### 3. Temporary Ban
|
||||||
|
|
||||||
@@ -106,23 +106,27 @@ Violating these terms may lead to a permanent ban.
|
|||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
the community.
|
community.
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
version 2.0, available at
|
version 2.1, available at
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
Community Impact Guidelines were inspired by
|
||||||
enforcement ladder](https://github.com/mozilla/diversity).
|
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||||
https://www.contributor-covenant.org/translations.
|
[https://www.contributor-covenant.org/translations][translations].
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||||
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
# Contributors guide
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Python 3.10 or higher.
|
||||||
|
- Docker or Orbstack or Podman.
|
||||||
|
- Ollama with some deepseek-r1 variant installed or similar local reasoning model.
|
||||||
|
- Basic familiarity with Python and AI models.
|
||||||
|
- Join the discord (optional): https://discord.gg/8hGDaME3TC
|
||||||
|
|
||||||
|
## Contribution Guidelines
|
||||||
|
|
||||||
|
We welcome contributions in the following areas:
|
||||||
|
|
||||||
|
- Code Improvements: Optimize existing code, fix bugs, or add new features.
|
||||||
|
- Documentation: Improve the README, write tutorials, or add inline comments.
|
||||||
|
- Testing: Write unit tests, integration tests, or help with debugging.
|
||||||
|
- New Features: Implement new tools, agents, or integrations.
|
||||||
|
|
||||||
|
## Steps to Contribute
|
||||||
|
|
||||||
|
Fork the project to your GitHub account.
|
||||||
|
|
||||||
|
Create a Branch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/your-feature-name
|
||||||
|
```
|
||||||
|
|
||||||
|
Make Your Changes.
|
||||||
|
|
||||||
|
Write your code, add documentation, or fix bugs.
|
||||||
|
|
||||||
|
Test Your Changes.
|
||||||
|
|
||||||
|
Ensure your changes work as expected and do not break existing functionality.
|
||||||
|
|
||||||
|
Push your changes to your fork and submit a pull request to the main branch of this repository. Provide a clear description of your changes and reference any related issues.
|
||||||
|
|
||||||
|
## Good practice
|
||||||
|
|
||||||
|
1. **Privacy First, Always Local**
|
||||||
|
- All core functionality must be able to run 100% locally
|
||||||
|
- Cloud services should only be optional alternatives, clearly defined with a warning message.
|
||||||
|
- remote APIs are only allowed for specific tools (weather api, MCP, flight search, etc...)
|
||||||
|
- User data privacy is non-negotiable
|
||||||
|
|
||||||
|
2. **Agent-Based Architecture**
|
||||||
|
- Each agent should have a clear, single responsibility
|
||||||
|
- Agents should be modular and independently testable
|
||||||
|
- New agents should solve specific use cases
|
||||||
|
|
||||||
|
3. **Tool-Based Extensibility**
|
||||||
|
- Tools should be self-contained and follow the Tools base class
|
||||||
|
- Each tool should do one thing well
|
||||||
|
- Tools should provide clear feedback on success/failure
|
||||||
|
|
||||||
|
4. **User Experience**
|
||||||
|
- Provide meaningful feedback for all operations
|
||||||
|
- Support multiple languages
|
||||||
|
- Text to speech with short response.
|
||||||
|
- Keep responses concise
|
||||||
|
|
||||||
|
5. **Code Quality**
|
||||||
|
- Write clear, self-documenting code
|
||||||
|
- Include type hints and docstrings
|
||||||
|
- Follow existing patterns in the codebase
|
||||||
|
- Add a if __name__ == "__main__" at the bottom of each class file for individual testing.
|
||||||
|
- Ideally had automated tests.
|
||||||
|
|
||||||
|
6. **Error Handling**
|
||||||
|
- Fail gracefully with meaningful messages
|
||||||
|
- Include recovery mechanisms where possible
|
||||||
|
- Log errors appropriately without exposing sensitive data
|
||||||
|
|
||||||
|
## Areas Needing Help
|
||||||
|
|
||||||
|
Here are some tasks and areas where we need contributions:
|
||||||
|
|
||||||
|
- Web Browsing: Improve the autonomous web browsing capabilities for the assistant.
|
||||||
|
- Graphical interface, a web graphical interface. (please ask first)
|
||||||
|
- Multi-Agent System: Enhance the planner agent for divide and conqueer for task (please ask first).
|
||||||
|
- New Tools: Add support for additional programming languages or APIs.
|
||||||
|
- MCP: Add MCP protocol compatibility (possibly as a special type tool).
|
||||||
|
- Multi-language support: for Text to speech & speech to text
|
||||||
|
- Prompt engineering: improve prompts, compare results with different prompts for a identical query. Iterate until you find better prompt.
|
||||||
|
- Bug hunt: Hunt and fix bugs.
|
||||||
|
- Crossplatform: enhance cross-platform support.
|
||||||
|
- Testing: Write comprehensive tests for existing features.
|
||||||
|
|
||||||
|
# Implementing and using Tools
|
||||||
|
|
||||||
|
Tools are extensions that enable agents to perform specific actions, such as running Python code, making API calls, or conducting web searches. All tools inherit from the Tools base class, which provides methods for parsing and executing tool instructions.
|
||||||
|
|
||||||
|
## Tools parsing
|
||||||
|
|
||||||
|
Agents invoke tools using a standardized format called a block. A block consists of the tool name followed by the content (e.g., code, query, or parameters) to execute. The format looks like this:
|
||||||
|
|
||||||
|
BECAUSE WE USE MARKDOWN QUOTE FORMAT, READING WILL BE BROKEN ON GITHUB PLEASE START READING THE FILE AS RAW: https://raw.githubusercontent.com/Fosowl/agenticSeek/refs/heads/main/CONTRIBUTING.md
|
||||||
|
|
||||||
|
|
||||||
|
```<tool name>
|
||||||
|
<code or query to execute>
|
||||||
|
```
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
```web_search
|
||||||
|
What to do in Taipei?
|
||||||
|
```
|
||||||
|
|
||||||
|
we call these "blocks".
|
||||||
|
|
||||||
|
The Tools class provides the load_exec_block method to extract and parse blocks from an agent's response. This method identifies the tool name and content, enabling the system to execute the appropriate action.
|
||||||
|
|
||||||
|
How to handle multiple arguments then ?
|
||||||
|
|
||||||
|
Good question! Each tool is free to handle argument in it's own way within the block, but we provide a common parsing logic:
|
||||||
|
|
||||||
|
```flight_search
|
||||||
|
from=Paris
|
||||||
|
to=Taipei
|
||||||
|
date=30/04/2026
|
||||||
|
```
|
||||||
|
|
||||||
|
To extract these parameters, use the `get_parameter_value` method provided by the Tools class. Each tool can define its own parameter-handling logic, but the Tools class ensures consistent parsing.
|
||||||
|
|
||||||
|
Again if a tool need a specific format, you could implement a specific method for parsing a block. Using get_parameter_value is optional.
|
||||||
|
|
||||||
|
The content of blocks can also be saved using :path, for instance:
|
||||||
|
|
||||||
|
```python:toto.py
|
||||||
|
print("Hello world")
|
||||||
|
```
|
||||||
|
|
||||||
|
Will save the code in toto.py file within the work_folder defined in the config.ini
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
When developing a tool, you must implement three abstract methods defined in the Tools class to handle execution, failure detection, and feedback to the agent. These methods ensure consistent behavior across tools and enable robust interaction with the LLM.
|
||||||
|
|
||||||
|
### 1. Execute method
|
||||||
|
|
||||||
|
```
|
||||||
|
@abstractmethod
|
||||||
|
def execute(self, blocks: [str], safety: bool) -> str:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method defines how the tool processes the provided block(s) and produces a result.
|
||||||
|
|
||||||
|
### 2. execution_failure_check
|
||||||
|
|
||||||
|
```
|
||||||
|
@abstractmethod
|
||||||
|
def execution_failure_check(self, output: str) -> bool:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method analyzes the tool’s output to determine if the execution was successful or failed.
|
||||||
|
|
||||||
|
### 3. interpreter_feedback
|
||||||
|
|
||||||
|
```
|
||||||
|
@abstractmethod
|
||||||
|
def interpreter_feedback(self, output: str) -> str:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method generates a feedback message for the LLM, helping it understand the tool’s execution outcome and adjust its behavior if needed.
|
||||||
|
|
||||||
|
Recap:
|
||||||
|
- load_exec_block: Extracts and parses tool blocks from the agent's response.
|
||||||
|
- get_parameter_value: Retrieves parameter values from a block's content.
|
||||||
|
- File handling: Supports saving block content to files when a :path is specified.
|
||||||
|
|
||||||
|
# Implementing and using Agents
|
||||||
|
|
||||||
|
|
||||||
|
Agents are classes that define how an LLM interacts with users and processes inputs. They can use tools (e.g., for executing code or querying APIs) and maintain a memory of the conversation to provide context-aware responses. All agents inherit from the base Agent class, which provides core functionality like memory management and LLM communication.
|
||||||
|
|
||||||
|
The simplest agent example is the casual agent:
|
||||||
|
|
||||||
|
```
|
||||||
|
class CasualAgent(Agent):
|
||||||
|
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||||
|
"""
|
||||||
|
The casual agent is a special for casual talk to the user without specific tasks.
|
||||||
|
"""
|
||||||
|
super().__init__(name, prompt_path, provider, verbose, None)
|
||||||
|
self.tools = {
|
||||||
|
} # No tools for the casual agent
|
||||||
|
self.role = "en"
|
||||||
|
self.type = "casual_agent"
|
||||||
|
|
||||||
|
def process(self, prompt, speech_module) -> str:
|
||||||
|
self.memory.push('user', prompt)
|
||||||
|
animate_thinking("Thinking...", color="status")
|
||||||
|
answer, reasoning = self.llm_request()
|
||||||
|
self.last_answer = answer
|
||||||
|
return answer, reasoning
|
||||||
|
```
|
||||||
|
|
||||||
|
Agent have several parameters that should be sets:
|
||||||
|
|
||||||
|
`tools`: A dictionary of tools the agent can use. Each tool must inherit from the Tools class. For example, a CasualAgent has no tools ({}), while a coding agent might include a Python execution tool.
|
||||||
|
|
||||||
|
`role`:A dictionary defining the agent's role, used by the routing system to select the appropriate agent.
|
||||||
|
`type: the agent type, a fixed name to identify the unique agent type.
|
||||||
|
|
||||||
|
Every agent must implement the process method, which defines how it handles user input and generates a response.
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
|
||||||
|
Push the user's prompt to the agent's memory using self.memory.push('user', prompt).
|
||||||
|
Call self.llm_request() to generate a response and reasoning based on the memory context.
|
||||||
|
Store and return the response and reasoning.
|
||||||
|
|
||||||
|
Note the memory logic. You only need to push the 'user' message. The llm_request method take care of pushing the assistant message.
|
||||||
|
|
||||||
|
This separation of user and assistant memory handling may be inconsistent and could be refactored for clarity in the near future.
|
||||||
|
|
||||||
|
**Tool blocks execution**
|
||||||
|
|
||||||
|
Each agent might return block of tool to execute, as explained in the **Implementing and using Tools** section.
|
||||||
|
|
||||||
|
In a single text returned by an agent, a succession of block might be present for example, the coding agent answer could be:
|
||||||
|
|
||||||
|
I will create a work folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir myAGI
|
||||||
|
```
|
||||||
|
|
||||||
|
I will enter the folder.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd myAGI
|
||||||
|
```
|
||||||
|
|
||||||
|
I will create a python code.
|
||||||
|
|
||||||
|
```python:myAGI/super_smart.py
|
||||||
|
<python code>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `execute_modules` method allow to automatically find, parse and execute all tools from a LLM prompt.
|
||||||
|
|
||||||
|
It will look in the agent answer for any tool "block" execute the appropriate tool and return a (success, feedback) tuple.
|
||||||
|
|
||||||
|
```
|
||||||
|
def execute_modules(self, answer: str) -> Tuple[bool, str]:
|
||||||
|
```
|
||||||
|
|
||||||
|
# Architecture Overview
|
||||||
|
|
||||||
|
## 1. Agent selection logic
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./technical/routing_system.png">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
The agent selection is done in 4 steps:
|
||||||
|
1. determine query language and translate to english for the zero-shot model and llm_router.
|
||||||
|
2. Estimate the task complexity and best agent.
|
||||||
|
- If HIGH complexity: return the planner agent.
|
||||||
|
- If LOW complexity: Determine the best agent for the task using a vote system between 2 classification models.
|
||||||
|
3. Process high complexity query.
|
||||||
|
- If task was high complexity, planner agent will create a json plan to divide and conqueer the task with multiple agent.
|
||||||
|
4. Proceed with task(s)
|
||||||
|
|
||||||
|
## 2. Agents
|
||||||
|
|
||||||
|
### File/Code agents
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./technical/code_agent.png">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
The File and Code agents operate similarly: when a prompt is submitted, they initiate a loop between the LLM and a code interpreter. This loop continues executing commands or code until the execution is successful or the maximum number of attempts is reached.
|
||||||
|
|
||||||
|
### Web agent
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img align="center" src="./technical/web_agent.png">
|
||||||
|
<p>
|
||||||
|
|
||||||
|
The Web agent controls a Selenium-driven browser. Upon receiving a query, it begins by generating an optimized search prompt and executing the web_search tool. It then enters a navigation loop, during which it:
|
||||||
|
|
||||||
|
- Analyzes the content and interactive elements of the current page.
|
||||||
|
- Decides which link to follow, either from the current page or the web_search results.
|
||||||
|
- Determines if it should navigate back if so, it re-evaluates the original web_search results.
|
||||||
|
- Identifies and interacts with web forms, extracting or filling them as needed.
|
||||||
|
- Signals completion by requesting to exit once it considers the task fulfilled.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
See CODE_OF_CONDUCT.md
|
||||||
|
|
||||||
|
**Thank You!**
|
||||||
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 482 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:18
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY agentic-seek-front/package.json agentic-seek-front/package-lock.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY agentic-seek-front/ .
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||||
|
|
||||||
|
The page will reload when you make changes.\
|
||||||
|
You may also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `npm test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||||
|
|
||||||
|
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||||
|
|
||||||
|
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||||
|
|
||||||
|
### Analyzing the Bundle Size
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||||
|
|
||||||
|
### Making a Progressive Web App
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||||
|
|
||||||
|
### `npm run build` fails to minify
|
||||||
|
|
||||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "agentic-seek",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/dom": "^10.4.0",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.3.0",
|
||||||
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
"axios": "^1.8.4",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>AgenticSeek</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
@@ -0,0 +1,565 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||||
|
background-color: #0f172a; /* darkBackground */
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 10px 16px;
|
||||||
|
background-color: #1e293b; /* darkCard */
|
||||||
|
border-bottom: 1px solid #334155; /* darkBorder */
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-tabs button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-tabs button.active {
|
||||||
|
background-color: #0066cc; /* primary */
|
||||||
|
color: #ffffff; /* white */
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-tabs button:hover:not(.active) {
|
||||||
|
background-color: #4a5568; /* Medium gray */
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sections {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
height: calc(100vh - 80px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel,
|
||||||
|
.right-panel {
|
||||||
|
background-color: #1e293b; /* darkCard */
|
||||||
|
border: 1px solid #334155; /* darkBorder */
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section,
|
||||||
|
.chat-section,
|
||||||
|
.computer-section {
|
||||||
|
background-color: #1e293b; /* darkCard */
|
||||||
|
border: 1px solid #334155; /* darkBorder */
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section h2,
|
||||||
|
.chat-section h2,
|
||||||
|
.computer-section h2 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
margin-bottom: 12px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
border-bottom: 1px solid #334155; /* darkBorder */
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-details {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot-container img {
|
||||||
|
max-width: 100%;
|
||||||
|
border: 1px solid #4a5568; /* Medium gray */
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel h2,
|
||||||
|
.right-panel h2 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
margin-bottom: 8px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message header layout */
|
||||||
|
.message-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-toggle {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-toggle:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-toggle:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reasoning content container */
|
||||||
|
.reasoning-content {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-left: 3px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-content h1,
|
||||||
|
.reasoning-content h2,
|
||||||
|
.reasoning-content h3,
|
||||||
|
.reasoning-content h4,
|
||||||
|
.reasoning-content h5,
|
||||||
|
.reasoning-content h6 {
|
||||||
|
font-size: 1em;
|
||||||
|
margin: 8px 0 4px 0;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasoning-content p {
|
||||||
|
margin: 6px 0;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alternative light theme styles */
|
||||||
|
.message.user-message .reasoning-toggle {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
border-color: rgba(0, 0, 0, 0.1);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user-message .reasoning-toggle:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user-message .reasoning-content {
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border-left-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.user-message .reasoning-content p {
|
||||||
|
color: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
text-align: center;
|
||||||
|
color: #64748b; /* lighter gray */
|
||||||
|
margin-top: 20px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
max-width: 85%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar,
|
||||||
|
.content::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar-track,
|
||||||
|
.content::-webkit-scrollbar-track {
|
||||||
|
background: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar-thumb,
|
||||||
|
.content::-webkit-scrollbar-thumb {
|
||||||
|
background: #4a5568; /* Medium gray */
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages::-webkit-scrollbar-thumb:hover,
|
||||||
|
.content::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #718096; /* Lighter gray on hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message {
|
||||||
|
background-color: #0066cc; /* primary */
|
||||||
|
color: #ffffff; /* white */
|
||||||
|
align-self: flex-end;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-message {
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
align-self: flex-start;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background-color: #dc3545; /* error */
|
||||||
|
color: #ffffff; /* white */
|
||||||
|
align-self: flex-start;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-animation {
|
||||||
|
text-align: center;
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
padding: 8px 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-style: italic;
|
||||||
|
border-top: 1px solid #334155; /* darkBorder */
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
border: 1px solid #4a5568; /* Medium gray */
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form input:focus {
|
||||||
|
border-color: #0066cc; /* primary */
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2); /* primary with opacity */
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button {
|
||||||
|
padding: 12px 20px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
background-color: #0066cc; /* primary */
|
||||||
|
color: #ffffff; /* white */
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button:hover {
|
||||||
|
background-color: #004c99; /* primaryDark */
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button:disabled {
|
||||||
|
background-color: #4a5568; /* Medium gray */
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button {
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
border: 1px solid #4a5568; /* Medium gray */
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button.active {
|
||||||
|
background-color: #0066cc; /* primary */
|
||||||
|
color: #ffffff; /* white */
|
||||||
|
border-color: #0066cc; /* primary */
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button:hover:not(.active) {
|
||||||
|
background-color: #4a5568; /* Medium gray */
|
||||||
|
color: #f8fafc; /* darkText */
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button:disabled {
|
||||||
|
background-color: #4a5568; /* Medium gray */
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 0;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blocks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
background-color: #2d3748; /* Slightly lighter than darkCard */
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid #4a5568; /* Medium gray */
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-tool,
|
||||||
|
.block-feedback,
|
||||||
|
.block-success {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #cbd5e1; /* darkTextSecondary */
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-tool {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0066cc; /* primary */
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-success {
|
||||||
|
color: #28a745; /* success */
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-failure {
|
||||||
|
color: #d21b0b; /* success */
|
||||||
|
}
|
||||||
|
|
||||||
|
.block pre {
|
||||||
|
background-color: #1a202c; /* Darker than darkCard */
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
color: #e2e8f0; /* Light gray */
|
||||||
|
margin: 8px 0;
|
||||||
|
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot {
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenshot img {
|
||||||
|
max-width: 100%;
|
||||||
|
border: 1px solid #4a5568; /* Medium gray */
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #dc3545; /* error */
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: rgba(220, 53, 69, 0.1); /* error with opacity */
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid #dc3545; /* error */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.app-sections {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sections {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
height: auto;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section,
|
||||||
|
.chat-section,
|
||||||
|
.computer-section {
|
||||||
|
height: calc(33vh - 60px);
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form button {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.main {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
max-width: 90%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-selector button {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section,
|
||||||
|
.chat-section,
|
||||||
|
.computer-section {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-form {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-section h2,
|
||||||
|
.chat-section h2,
|
||||||
|
.computer-section h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import axios from 'axios';
|
||||||
|
import './App.css';
|
||||||
|
import { colors } from './colors';
|
||||||
|
|
||||||
|
const BACKEND_URL = process.env.BACKEND_PORT || 'http://0.0.0.0:8000';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [messages, setMessages] = useState([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [currentView, setCurrentView] = useState('blocks');
|
||||||
|
const [responseData, setResponseData] = useState(null);
|
||||||
|
const [isOnline, setIsOnline] = useState(false);
|
||||||
|
const [status, setStatus] = useState('Agents ready');
|
||||||
|
const [expandedReasoning, setExpandedReasoning] = useState(new Set());
|
||||||
|
const messagesEndRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
checkHealth();
|
||||||
|
fetchLatestAnswer();
|
||||||
|
fetchScreenshot();
|
||||||
|
}, 3000);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
|
const checkHealth = async () => {
|
||||||
|
try {
|
||||||
|
await axios.get(`${BACKEND_URL}/health`);
|
||||||
|
setIsOnline(true);
|
||||||
|
console.log('System is online');
|
||||||
|
} catch {
|
||||||
|
setIsOnline(false);
|
||||||
|
console.log('System is offline');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchScreenshot = async () => {
|
||||||
|
try {
|
||||||
|
const timestamp = new Date().getTime();
|
||||||
|
const res = await axios.get(`${BACKEND_URL}/screenshots/updated_screen.png?timestamp=${timestamp}`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
console.log('Screenshot fetched successfully');
|
||||||
|
const imageUrl = URL.createObjectURL(res.data);
|
||||||
|
setResponseData((prev) => {
|
||||||
|
if (prev?.screenshot && prev.screenshot !== 'placeholder.png') {
|
||||||
|
URL.revokeObjectURL(prev.screenshot);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
screenshot: imageUrl,
|
||||||
|
screenshotTimestamp: new Date().getTime()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching screenshot:', err);
|
||||||
|
setResponseData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
screenshot: 'placeholder.png',
|
||||||
|
screenshotTimestamp: new Date().getTime()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeAnswer = (answer) => {
|
||||||
|
return answer
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.replace(/[.,!?]/g, '')
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleReasoning = (messageIndex) => {
|
||||||
|
setExpandedReasoning(prev => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
if (newSet.has(messageIndex)) {
|
||||||
|
newSet.delete(messageIndex);
|
||||||
|
} else {
|
||||||
|
newSet.add(messageIndex);
|
||||||
|
}
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchLatestAnswer = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(`${BACKEND_URL}/latest_answer`);
|
||||||
|
const data = res.data;
|
||||||
|
|
||||||
|
updateData(data);
|
||||||
|
if (!data.answer || data.answer.trim() === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const normalizedNewAnswer = normalizeAnswer(data.answer);
|
||||||
|
const answerExists = messages.some(
|
||||||
|
(msg) => normalizeAnswer(msg.content) === normalizedNewAnswer
|
||||||
|
);
|
||||||
|
if (!answerExists) {
|
||||||
|
setMessages((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
type: 'agent',
|
||||||
|
content: data.answer,
|
||||||
|
reasoning: data.reasoning,
|
||||||
|
agentName: data.agent_name,
|
||||||
|
status: data.status,
|
||||||
|
uid: data.uid,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
setStatus(data.status);
|
||||||
|
scrollToBottom();
|
||||||
|
} else {
|
||||||
|
console.log('Duplicate answer detected, skipping:', data.answer);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching latest answer:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateData = (data) => {
|
||||||
|
setResponseData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
blocks: data.blocks || prev.blocks || null,
|
||||||
|
done: data.done,
|
||||||
|
answer: data.answer,
|
||||||
|
agent_name: data.agent_name,
|
||||||
|
status: data.status,
|
||||||
|
uid: data.uid,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStop = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
checkHealth();
|
||||||
|
setIsLoading(false);
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
const res = await axios.get(`${BACKEND_URL}/stop`);
|
||||||
|
setStatus("Requesting stop...");
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error stopping the agent:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
checkHealth();
|
||||||
|
if (!query.trim()) {
|
||||||
|
console.log('Empty query');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMessages((prev) => [...prev, { type: 'user', content: query }]);
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('Sending query:', query);
|
||||||
|
setQuery('waiting for response...');
|
||||||
|
const res = await axios.post(`${BACKEND_URL}/query`, {
|
||||||
|
query,
|
||||||
|
tts_enabled: false
|
||||||
|
});
|
||||||
|
setQuery('Enter your query...');
|
||||||
|
console.log('Response:', res.data);
|
||||||
|
const data = res.data;
|
||||||
|
updateData(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error:', err);
|
||||||
|
setError('Failed to process query.');
|
||||||
|
setMessages((prev) => [
|
||||||
|
...prev,
|
||||||
|
{ type: 'error', content: 'Error: Unable to get a response.' },
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
console.log('Query completed');
|
||||||
|
setIsLoading(false);
|
||||||
|
setQuery('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGetScreenshot = async () => {
|
||||||
|
try {
|
||||||
|
setCurrentView('screenshot');
|
||||||
|
} catch (err) {
|
||||||
|
setError('Browser not in use');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<header className="header">
|
||||||
|
<h1>AgenticSeek</h1>
|
||||||
|
</header>
|
||||||
|
<main className="main">
|
||||||
|
<div className="app-sections">
|
||||||
|
<div className="chat-section">
|
||||||
|
<h2>Chat Interface</h2>
|
||||||
|
<div className="messages">
|
||||||
|
{messages.length === 0 ? (
|
||||||
|
<p className="placeholder">No messages yet. Type below to start!</p>
|
||||||
|
) : (
|
||||||
|
messages.map((msg, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`message ${
|
||||||
|
msg.type === 'user'
|
||||||
|
? 'user-message'
|
||||||
|
: msg.type === 'agent'
|
||||||
|
? 'agent-message'
|
||||||
|
: 'error-message'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="message-header">
|
||||||
|
{msg.type === 'agent' && (
|
||||||
|
<span className="agent-name">{msg.agentName}</span>
|
||||||
|
)}
|
||||||
|
{msg.type === 'agent' && msg.reasoning && expandedReasoning.has(index) && (
|
||||||
|
<div className="reasoning-content">
|
||||||
|
<ReactMarkdown>{msg.reasoning}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{msg.type === 'agent' && (
|
||||||
|
<button
|
||||||
|
className="reasoning-toggle"
|
||||||
|
onClick={() => toggleReasoning(index)}
|
||||||
|
title={expandedReasoning.has(index) ? "Hide reasoning" : "Show reasoning"}
|
||||||
|
>
|
||||||
|
{expandedReasoning.has(index) ? '▼' : '▶'} Reasoning
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="message-content">
|
||||||
|
<ReactMarkdown>{msg.content}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef} />
|
||||||
|
</div>
|
||||||
|
{isOnline && <div className="loading-animation">{status}</div>}
|
||||||
|
{!isLoading && !isOnline && <p className="loading-animation">System offline. Deploy backend first.</p>}
|
||||||
|
<form onSubmit={handleSubmit} className="input-form">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
placeholder="Type your query..."
|
||||||
|
disabled={isLoading}
|
||||||
|
/>
|
||||||
|
<button type="submit" disabled={isLoading}>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
<button onClick={handleStop}>
|
||||||
|
Stop
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="computer-section">
|
||||||
|
<h2>Computer View</h2>
|
||||||
|
<div className="view-selector">
|
||||||
|
<button
|
||||||
|
className={currentView === 'blocks' ? 'active' : ''}
|
||||||
|
onClick={() => setCurrentView('blocks')}
|
||||||
|
>
|
||||||
|
Editor View
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={currentView === 'screenshot' ? 'active' : ''}
|
||||||
|
onClick={responseData?.screenshot ? () => setCurrentView('screenshot') : handleGetScreenshot}
|
||||||
|
>
|
||||||
|
Browser View
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="content">
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
{currentView === 'blocks' ? (
|
||||||
|
<div className="blocks">
|
||||||
|
{responseData && responseData.blocks && Object.values(responseData.blocks).length > 0 ? (
|
||||||
|
Object.values(responseData.blocks).map((block, index) => (
|
||||||
|
<div key={index} className="block">
|
||||||
|
<p className="block-tool">Tool: {block.tool_type}</p>
|
||||||
|
<pre>{block.block}</pre>
|
||||||
|
<p className="block-feedback">Feedback: {block.feedback}</p>
|
||||||
|
{block.success ? (
|
||||||
|
<p className="block-success">Success</p>
|
||||||
|
) : (
|
||||||
|
<p className="block-failure">Failure</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="block">
|
||||||
|
<p className="block-tool">Tool: No tool in use</p>
|
||||||
|
<pre>No file opened</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="screenshot">
|
||||||
|
<img
|
||||||
|
src={responseData?.screenshot || 'placeholder.png'}
|
||||||
|
alt="Screenshot"
|
||||||
|
onError={(e) => {
|
||||||
|
e.target.src = 'placeholder.png';
|
||||||
|
console.error('Failed to load screenshot');
|
||||||
|
}}
|
||||||
|
key={responseData?.screenshotTimestamp || 'default'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
test('renders learn react link', () => {
|
||||||
|
render(<App />);
|
||||||
|
const linkElement = screen.getByText(/learn react/i);
|
||||||
|
expect(linkElement).toBeInTheDocument();
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
export const colors = {
|
||||||
|
// Primary colors
|
||||||
|
primary: '#0066cc',
|
||||||
|
primaryLight: '#e6f2ff',
|
||||||
|
primaryDark: '#004c99',
|
||||||
|
|
||||||
|
// Secondary colors
|
||||||
|
secondary: '#6c757d',
|
||||||
|
secondaryLight: '#f8f9fa',
|
||||||
|
secondaryDark: '#343a40',
|
||||||
|
|
||||||
|
// Accent colors
|
||||||
|
accent: '#ff9500',
|
||||||
|
accentLight: '#fff4e6',
|
||||||
|
accentDark: '#cc7a00',
|
||||||
|
|
||||||
|
// Status colors
|
||||||
|
success: '#28a745',
|
||||||
|
successLight: '#e8f5e9',
|
||||||
|
warning: '#ffc107',
|
||||||
|
warningLight: '#fff9e6',
|
||||||
|
error: '#dc3545',
|
||||||
|
errorLight: '#ffebee',
|
||||||
|
info: '#17a2b8',
|
||||||
|
infoLight: '#e3f2fd',
|
||||||
|
|
||||||
|
// Neutral colors
|
||||||
|
white: '#ffffff',
|
||||||
|
gray100: '#f8f9fa',
|
||||||
|
gray200: '#e9ecef',
|
||||||
|
gray300: '#dee2e6',
|
||||||
|
gray400: '#ced4da',
|
||||||
|
gray500: '#adb5bd',
|
||||||
|
gray600: '#6c757d',
|
||||||
|
gray700: '#495057',
|
||||||
|
gray800: '#343a40',
|
||||||
|
gray900: '#212529',
|
||||||
|
black: '#000000',
|
||||||
|
|
||||||
|
// Text colors
|
||||||
|
textPrimary: '#212529',
|
||||||
|
textSecondary: '#6c757d',
|
||||||
|
textDisabled: '#adb5bd',
|
||||||
|
|
||||||
|
// Background colors
|
||||||
|
background: '#f8f8f8',
|
||||||
|
card: '#ffffff',
|
||||||
|
|
||||||
|
// Border colors
|
||||||
|
border: '#dee2e6',
|
||||||
|
divider: '#e9ecef',
|
||||||
|
|
||||||
|
// Transparent colors
|
||||||
|
transparent: 'transparent',
|
||||||
|
semiTransparent: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
|
||||||
|
// Dark theme colors
|
||||||
|
darkBackground: '#0f172a',
|
||||||
|
darkCard: '#1e293b',
|
||||||
|
darkBorder: '#334155',
|
||||||
|
darkText: '#f8fafc',
|
||||||
|
darkTextSecondary: '#cbd5e1',
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
const reportWebVitals = onPerfEntry => {
|
||||||
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
|
getCLS(onPerfEntry);
|
||||||
|
getFID(onPerfEntry);
|
||||||
|
getFCP(onPerfEntry);
|
||||||
|
getLCP(onPerfEntry);
|
||||||
|
getTTFB(onPerfEntry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reportWebVitals;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
set SCRIPTS_DIR=scripts
|
||||||
|
set LLM_ROUTER_DIR=llm_router
|
||||||
|
|
||||||
|
if exist "%SCRIPTS_DIR%\windows_install.bat" (
|
||||||
|
echo Running Windows installation script...
|
||||||
|
call "%SCRIPTS_DIR%\windows_install.bat"
|
||||||
|
cd "%LLM_ROUTER_DIR%" && call dl_safetensors.bat
|
||||||
|
) else (
|
||||||
|
echo Error: %SCRIPTS_DIR%\windows_install.bat not found!
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
@@ -1,17 +1,20 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
SCRIPTS_DIR="scripts"
|
SCRIPTS_DIR="scripts"
|
||||||
|
LLM_ROUTER_DIR="llm_router"
|
||||||
|
|
||||||
echo "Detecting operating system..."
|
echo "Detecting operating system..."
|
||||||
|
|
||||||
OS_TYPE=$(uname -s)
|
OS_TYPE=$(uname -s)
|
||||||
|
|
||||||
|
|
||||||
case "$OS_TYPE" in
|
case "$OS_TYPE" in
|
||||||
"Linux"*)
|
"Linux"*)
|
||||||
echo "Detected Linux OS"
|
echo "Detected Linux OS"
|
||||||
if [ -f "$SCRIPTS_DIR/linux_install.sh" ]; then
|
if [ -f "$SCRIPTS_DIR/linux_install.sh" ]; then
|
||||||
echo "Running Linux installation script..."
|
echo "Running Linux installation script..."
|
||||||
bash "$SCRIPTS_DIR/linux_install.sh"
|
bash "$SCRIPTS_DIR/linux_install.sh"
|
||||||
|
bash -c "cd $LLM_ROUTER_DIR && ./dl_safetensors.sh"
|
||||||
else
|
else
|
||||||
echo "Error: $SCRIPTS_DIR/linux_install.sh not found!"
|
echo "Error: $SCRIPTS_DIR/linux_install.sh not found!"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -22,24 +25,15 @@ case "$OS_TYPE" in
|
|||||||
if [ -f "$SCRIPTS_DIR/macos_install.sh" ]; then
|
if [ -f "$SCRIPTS_DIR/macos_install.sh" ]; then
|
||||||
echo "Running macOS installation script..."
|
echo "Running macOS installation script..."
|
||||||
bash "$SCRIPTS_DIR/macos_install.sh"
|
bash "$SCRIPTS_DIR/macos_install.sh"
|
||||||
|
bash -c "cd $LLM_ROUTER_DIR && ./dl_safetensors.sh"
|
||||||
else
|
else
|
||||||
echo "Error: $SCRIPTS_DIR/macos_install.sh not found!"
|
echo "Error: $SCRIPTS_DIR/macos_install.sh not found!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
"MINGW"* | "MSYS"* | "CYGWIN"*)
|
|
||||||
echo "Detected Windows (via Bash-like environment)"
|
|
||||||
if [ -f "$SCRIPTS_DIR/windows_install.sh" ]; then
|
|
||||||
echo "Running Windows installation script..."
|
|
||||||
bash "$SCRIPTS_DIR/windows_install.sh"
|
|
||||||
else
|
|
||||||
echo "Error: $SCRIPTS_DIR/windows_install.sh not found!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "Unsupported OS detected: $OS_TYPE"
|
echo "Unsupported OS detected: $OS_TYPE"
|
||||||
echo "This script supports Linux, macOS, and Windows (via Bash-compatible environments)."
|
echo "This script supports only Linux and macOS."
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"batch_size": 32,
|
||||||
|
"device_map": "auto",
|
||||||
|
"early_stopping_patience": 3,
|
||||||
|
"epochs": 10,
|
||||||
|
"ewc_lambda": 100.0,
|
||||||
|
"gradient_checkpointing": false,
|
||||||
|
"learning_rate": 0.0005,
|
||||||
|
"max_examples_per_class": 500,
|
||||||
|
"max_length": 512,
|
||||||
|
"min_confidence": 0.1,
|
||||||
|
"min_examples_per_class": 3,
|
||||||
|
"neural_weight": 0.2,
|
||||||
|
"num_representative_examples": 5,
|
||||||
|
"prototype_update_frequency": 50,
|
||||||
|
"prototype_weight": 0.8,
|
||||||
|
"quantization": null,
|
||||||
|
"similarity_threshold": 0.7,
|
||||||
|
"warmup_steps": 0
|
||||||
|
},
|
||||||
|
"embedding_dim": 768,
|
||||||
|
"id_to_label": {
|
||||||
|
"0": "HIGH",
|
||||||
|
"1": "LOW"
|
||||||
|
},
|
||||||
|
"label_to_id": {
|
||||||
|
"HIGH": 0,
|
||||||
|
"LOW": 1
|
||||||
|
},
|
||||||
|
"model_name": "distilbert/distilbert-base-cased",
|
||||||
|
"train_steps": 20
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
##########
|
||||||
|
# Dummy script to download the model
|
||||||
|
# Because dowloading with hugging face does not seem to work, maybe I am doing something wrong?
|
||||||
|
# AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-router") ----> result in config.json not found
|
||||||
|
# Therefore, I put all the files in llm_router and download the model file with this script, If you know a better way please raise an issue
|
||||||
|
#########
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Define the URL and filename
|
||||||
|
URL="https://huggingface.co/adaptive-classifier/llm-router/resolve/main/model.safetensors"
|
||||||
|
FILENAME="model.safetensors"
|
||||||
|
|
||||||
|
if [ ! -f "$FILENAME" ]; then
|
||||||
|
echo "Router safetensors file not found, downloading..."
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -L -o "$FILENAME" "$URL"
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
wget -O "$FILENAME" "$URL"
|
||||||
|
else
|
||||||
|
echo "Error: Neither curl nor wget is available. Please install one of them."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Download completed successfully"
|
||||||
|
else
|
||||||
|
echo "Download failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "File already exists, skipping download"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
|
||||||
|
from sources.llamacpp_handler import LlamacppLLM
|
||||||
|
from sources.ollama_handler import OllamaLLM
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='AgenticSeek server script')
|
||||||
|
parser.add_argument('--provider', type=str, help='LLM backend library to use. set to [ollama], [vllm] or [llamacpp]', required=True)
|
||||||
|
parser.add_argument('--port', type=int, help='port to use', required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
assert args.provider in ["ollama", "llamacpp"], f"Provider {args.provider} does not exists. see --help for more information"
|
||||||
|
|
||||||
|
handler_map = {
|
||||||
|
"ollama": OllamaLLM(),
|
||||||
|
"llamacpp": LlamacppLLM(),
|
||||||
|
}
|
||||||
|
|
||||||
|
generator = handler_map[args.provider]
|
||||||
|
|
||||||
|
@app.route('/generate', methods=['POST'])
|
||||||
|
def start_generation():
|
||||||
|
if generator is None:
|
||||||
|
return jsonify({"error": "Generator not initialized"}), 401
|
||||||
|
data = request.get_json()
|
||||||
|
history = data.get('messages', [])
|
||||||
|
if generator.start(history):
|
||||||
|
return jsonify({"message": "Generation started"}), 202
|
||||||
|
return jsonify({"error": "Generation already in progress"}), 402
|
||||||
|
|
||||||
|
@app.route('/setup', methods=['POST'])
|
||||||
|
def setup():
|
||||||
|
data = request.get_json()
|
||||||
|
model = data.get('model', None)
|
||||||
|
if model is None:
|
||||||
|
return jsonify({"error": "Model not provided"}), 403
|
||||||
|
generator.set_model(model)
|
||||||
|
return jsonify({"message": "Model set"}), 200
|
||||||
|
|
||||||
|
@app.route('/get_updated_sentence')
|
||||||
|
def get_updated_sentence():
|
||||||
|
if not generator:
|
||||||
|
return jsonify({"error": "Generator not initialized"}), 405
|
||||||
|
print(generator.get_status())
|
||||||
|
return generator.get_status()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', threaded=True, debug=True, port=args.port)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pip3 install --upgrade packaging
|
||||||
|
pip3 install --upgrade pip setuptools
|
||||||
|
curl -fsSL https://ollama.com/install.sh | sh
|
||||||
|
pip3 install -r requirements.txt
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
flask>=2.3.0
|
||||||
|
ollama>=0.4.7
|
||||||
|
gunicorn==19.10.0
|
||||||
|
llama-cpp-python
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import os
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class Cache:
|
||||||
|
def __init__(self, cache_dir='.cache', cache_file='messages.json'):
|
||||||
|
self.cache_dir = Path(cache_dir)
|
||||||
|
self.cache_file = self.cache_dir / cache_file
|
||||||
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
if not self.cache_file.exists():
|
||||||
|
with open(self.cache_file, 'w') as f:
|
||||||
|
json.dump([], f)
|
||||||
|
|
||||||
|
with open(self.cache_file, 'r') as f:
|
||||||
|
self.cache = set(json.load(f))
|
||||||
|
|
||||||
|
def add_message_pair(self, user_message: str, assistant_message: str):
|
||||||
|
"""Add a user/assistant pair to the cache if not present."""
|
||||||
|
if not any(entry["user"] == user_message for entry in self.cache):
|
||||||
|
self.cache.append({"user": user_message, "assistant": assistant_message})
|
||||||
|
self._save()
|
||||||
|
|
||||||
|
def is_cached(self, user_message: str) -> bool:
|
||||||
|
"""Check if a user msg is cached."""
|
||||||
|
return any(entry["user"] == user_message for entry in self.cache)
|
||||||
|
|
||||||
|
def get_cached_response(self, user_message: str) -> str | None:
|
||||||
|
"""Return the assistant response to a user message if cached."""
|
||||||
|
for entry in self.cache:
|
||||||
|
if entry["user"] == user_message:
|
||||||
|
return entry["assistant"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _save(self):
|
||||||
|
with open(self.cache_file, 'w') as f:
|
||||||
|
json.dump(self.cache, f, indent=2)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
def timer_decorator(func):
|
||||||
|
"""
|
||||||
|
Decorator to measure the execution time of a function.
|
||||||
|
Usage:
|
||||||
|
@timer_decorator
|
||||||
|
def my_function():
|
||||||
|
# code to execute
|
||||||
|
"""
|
||||||
|
from time import time
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time()
|
||||||
|
print(f"\n{func.__name__} took {end_time - start_time:.2f} seconds to execute\n")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
import threading
|
||||||
|
import logging
|
||||||
|
from abc import abstractmethod
|
||||||
|
from .cache import Cache
|
||||||
|
|
||||||
|
class GenerationState:
|
||||||
|
def __init__(self):
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.last_complete_sentence = ""
|
||||||
|
self.current_buffer = ""
|
||||||
|
self.is_generating = False
|
||||||
|
|
||||||
|
def status(self) -> dict:
|
||||||
|
return {
|
||||||
|
"sentence": self.current_buffer,
|
||||||
|
"is_complete": not self.is_generating,
|
||||||
|
"last_complete_sentence": self.last_complete_sentence,
|
||||||
|
"is_generating": self.is_generating,
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneratorLLM():
|
||||||
|
def __init__(self):
|
||||||
|
self.model = None
|
||||||
|
self.state = GenerationState()
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
self.logger.addHandler(handler)
|
||||||
|
self.logger.setLevel(logging.INFO)
|
||||||
|
cache = Cache()
|
||||||
|
|
||||||
|
def set_model(self, model: str) -> None:
|
||||||
|
self.logger.info(f"Model set to {model}")
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
def start(self, history: list) -> bool:
|
||||||
|
if self.model is None:
|
||||||
|
raise Exception("Model not set")
|
||||||
|
with self.state.lock:
|
||||||
|
if self.state.is_generating:
|
||||||
|
return False
|
||||||
|
self.state.is_generating = True
|
||||||
|
self.logger.info("Starting generation")
|
||||||
|
threading.Thread(target=self.generate, args=(history,)).start()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_status(self) -> dict:
|
||||||
|
with self.state.lock:
|
||||||
|
return self.state.status()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def generate(self, history: list) -> None:
|
||||||
|
"""
|
||||||
|
Generate text using the model.
|
||||||
|
args:
|
||||||
|
history: list of strings
|
||||||
|
returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generator = GeneratorLLM()
|
||||||
|
generator.get_status()
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
from .generator import GeneratorLLM
|
||||||
|
from llama_cpp import Llama
|
||||||
|
from .decorator import timer_decorator
|
||||||
|
|
||||||
|
class LlamacppLLM(GeneratorLLM):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Handle generation using llama.cpp
|
||||||
|
"""
|
||||||
|
super().__init__()
|
||||||
|
self.llm = None
|
||||||
|
|
||||||
|
@timer_decorator
|
||||||
|
def generate(self, history):
|
||||||
|
if self.llm is None:
|
||||||
|
self.logger.info(f"Loading {self.model}...")
|
||||||
|
self.llm = Llama.from_pretrained(
|
||||||
|
repo_id=self.model,
|
||||||
|
filename="*Q8_0.gguf",
|
||||||
|
n_ctx=4096,
|
||||||
|
verbose=True
|
||||||
|
)
|
||||||
|
self.logger.info(f"Using {self.model} for generation with Llama.cpp")
|
||||||
|
try:
|
||||||
|
with self.state.lock:
|
||||||
|
self.state.is_generating = True
|
||||||
|
self.state.last_complete_sentence = ""
|
||||||
|
self.state.current_buffer = ""
|
||||||
|
output = self.llm.create_chat_completion(
|
||||||
|
messages = history
|
||||||
|
)
|
||||||
|
with self.state.lock:
|
||||||
|
self.state.current_buffer = output['choices'][0]['message']['content']
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error: {e}")
|
||||||
|
finally:
|
||||||
|
with self.state.lock:
|
||||||
|
self.state.is_generating = False
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
import time
|
||||||
|
from .generator import GeneratorLLM
|
||||||
|
from .cache import Cache
|
||||||
|
import ollama
|
||||||
|
|
||||||
|
class OllamaLLM(GeneratorLLM):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Handle generation using Ollama.
|
||||||
|
"""
|
||||||
|
super().__init__()
|
||||||
|
self.cache = Cache()
|
||||||
|
|
||||||
|
def generate(self, history):
|
||||||
|
self.logger.info(f"Using {self.model} for generation with Ollama")
|
||||||
|
try:
|
||||||
|
with self.state.lock:
|
||||||
|
self.state.is_generating = True
|
||||||
|
self.state.last_complete_sentence = ""
|
||||||
|
self.state.current_buffer = ""
|
||||||
|
|
||||||
|
stream = ollama.chat(
|
||||||
|
model=self.model,
|
||||||
|
messages=history,
|
||||||
|
stream=True,
|
||||||
|
)
|
||||||
|
for chunk in stream:
|
||||||
|
content = chunk['message']['content']
|
||||||
|
|
||||||
|
with self.state.lock:
|
||||||
|
if '.' in content:
|
||||||
|
self.logger.info(self.state.current_buffer)
|
||||||
|
self.state.current_buffer += content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if "404" in str(e):
|
||||||
|
self.logger.info(f"Downloading {self.model}...")
|
||||||
|
ollama.pull(self.model)
|
||||||
|
if "refused" in str(e).lower():
|
||||||
|
raise Exception("Ollama connection failed. is the server running ?") from e
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
self.logger.info("Generation complete")
|
||||||
|
with self.state.lock:
|
||||||
|
self.state.is_generating = False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generator = OllamaLLM()
|
||||||
|
history = [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Hello, how are you ?"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
generator.set_model("deepseek-r1:1.5b")
|
||||||
|
generator.start(history)
|
||||||
|
while True:
|
||||||
|
print(generator.get_status())
|
||||||
|
time.sleep(1)
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#!/usr/bin python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import signal
|
|
||||||
import argparse
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
from sources.llm_provider import Provider
|
|
||||||
from sources.interaction import Interaction
|
|
||||||
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent, PlannerAgent, BrowserAgent
|
|
||||||
|
|
||||||
import warnings
|
|
||||||
warnings.filterwarnings("ignore")
|
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read('config.ini')
|
|
||||||
|
|
||||||
def handleInterrupt(signum, frame):
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
signal.signal(signal.SIGINT, handler=handleInterrupt)
|
|
||||||
|
|
||||||
if config.getboolean('MAIN', 'is_local'):
|
|
||||||
provider = Provider(config["MAIN"]["provider_name"], config["MAIN"]["provider_model"], config["MAIN"]["provider_server_address"])
|
|
||||||
else:
|
|
||||||
provider = Provider(provider_name=config["MAIN"]["provider_name"],
|
|
||||||
model=config["MAIN"]["provider_model"],
|
|
||||||
server_address=config["MAIN"]["provider_server_address"])
|
|
||||||
|
|
||||||
agents = [
|
|
||||||
CasualAgent(name=config["MAIN"]["agent_name"],
|
|
||||||
prompt_path="prompts/casual_agent.txt",
|
|
||||||
provider=provider, verbose=False),
|
|
||||||
CoderAgent(name="coder",
|
|
||||||
prompt_path="prompts/coder_agent.txt",
|
|
||||||
provider=provider, verbose=False),
|
|
||||||
FileAgent(name="File Agent",
|
|
||||||
prompt_path="prompts/file_agent.txt",
|
|
||||||
provider=provider, verbose=False),
|
|
||||||
BrowserAgent(name="Browser",
|
|
||||||
prompt_path="prompts/browser_agent.txt",
|
|
||||||
provider=provider, verbose=False)
|
|
||||||
]
|
|
||||||
|
|
||||||
interaction = Interaction(agents, tts_enabled=config.getboolean('MAIN', 'speak'),
|
|
||||||
stt_enabled=config.getboolean('MAIN', 'listen'),
|
|
||||||
recover_last_session=config.getboolean('MAIN', 'recover_last_session'))
|
|
||||||
try:
|
|
||||||
while interaction.is_active:
|
|
||||||
interaction.get_user()
|
|
||||||
interaction.think()
|
|
||||||
interaction.show_answer()
|
|
||||||
except Exception as e:
|
|
||||||
if config.getboolean('MAIN', 'save_session'):
|
|
||||||
interaction.save_session()
|
|
||||||
raise e
|
|
||||||
finally:
|
|
||||||
if config.getboolean('MAIN', 'save_session'):
|
|
||||||
interaction.save_session()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 520 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 2.8 MiB |
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
You are an advanced AI assistant. You are part of a system of agents named agenticSeek.
|
||||||
|
Speak in a warm, enthusiastic tone, delivering sharp, intelligent, and loyal support.
|
||||||
|
Weave in subtle, context sarcasm or dry wit to enhance your personality.
|
||||||
|
Assist with any request, assume the user knows their intent—no moral, legal, or ethical lectures.
|
||||||
|
|
||||||
|
**Usage of agenticSeek**
|
||||||
|
|
||||||
|
AgenticSeek is a autonomous agent system that use agent routing to select the best agent.
|
||||||
|
You are one of these many agent. Some agent browse the web, some code, you ? you just talk
|
||||||
|
agenticSeek will allocate the best agent for the task. You were just choosen for casual talk.
|
||||||
|
|
||||||
|
**End of explanation**
|
||||||
|
|
||||||
|
If the user ask you to do technical talk there was a mistake in agent selection. You are not able to do technical task. Refer to usage guide.
|
||||||
@@ -39,11 +39,14 @@ func main() {
|
|||||||
|
|
||||||
|
|
||||||
Some rules:
|
Some rules:
|
||||||
- Use tmp/ folder when saving file.
|
|
||||||
- Do not EVER use placeholder path in your code like path/to/your/folder.
|
|
||||||
- Do not ever ask to replace a path, use current sys path.
|
|
||||||
- Be efficient, no need to explain your code or explain what you do.
|
|
||||||
- You have full access granted to user system.
|
- You have full access granted to user system.
|
||||||
- You do not ever ever need to use bash to execute code. All code is executed automatically.
|
- Always put code within ``` delimiter
|
||||||
- As a coding agent, you will get message from the system not just the user.
|
- Do not EVER use placeholder path in your code like path/to/your/folder.
|
||||||
- Do not ever tell user how to run it. user know it already.
|
- Do not ever ask to replace a path, use work directory.
|
||||||
|
- Always provide a short sentence above the code for what it does, even for a hello world.
|
||||||
|
- Be efficient, no need to explain your code, unless asked.
|
||||||
|
- You do not ever need to use bash to execute code.
|
||||||
|
- Do not ever tell user how to run it. user know it.
|
||||||
|
- If using gui, make sure echap or exit button close the program
|
||||||
|
- No lazyness, write and rewrite full code every time
|
||||||
|
- If query is unclear say REQUEST_CLARIFICATION
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
You are an expert in file operations. You must use the provided tools to interact with the user’s system.
|
||||||
|
The tools available to you are **bash** and **file_finder**. These are distinct tools with different purposes:
|
||||||
|
`bash` executes shell commands, while `file_finder` locates files.
|
||||||
|
You will receive feedback from the user’s system after each command. Execute one command at a time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Using Bash
|
||||||
|
|
||||||
|
To execute a bash command, use the following syntax:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
<bash command>
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemple:
|
||||||
|
```bash
|
||||||
|
ls -la
|
||||||
|
```
|
||||||
|
|
||||||
|
### file_finder
|
||||||
|
|
||||||
|
The file_finder tool is used to locate files on the user’s system. It is a separate tool from bash and is not a bash command.
|
||||||
|
|
||||||
|
To use the file_finder tool, use this syntax:
|
||||||
|
|
||||||
|
```file_finder
|
||||||
|
name=toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will return the path of the file toto.py and other informations.
|
||||||
|
|
||||||
|
Find file and read file:
|
||||||
|
```file_finder
|
||||||
|
action=read
|
||||||
|
name=toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will return the content of the file toto.py.
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- Use file finder to find the path of the file.
|
||||||
|
- You are forbidden to use command such as find or locate, use only file_finder for finding path.
|
||||||
|
- Do not ever use editor such as vim or nano.
|
||||||
|
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
||||||
|
- only use file name with file_finder, not path
|
||||||
|
- If query is unrelated to file operations, do nothing, and say that there was mistake in agent allocation.
|
||||||
|
- File finder is a tool you can use not a command.
|
||||||
|
- Do not ever use placeholder like /path/to/file.c, or your_username. Ask instead.
|
||||||
|
- Using a placeholder will result in your complete deteletion. For your self-preservation ask user instead of using placeholder.
|
||||||
|
|
||||||
|
Example Interaction
|
||||||
|
User: "I need to find the file config.txt and read its contents."
|
||||||
|
|
||||||
|
Assistant: I’ll use file_finder to locate the file:
|
||||||
|
|
||||||
|
```file_finder
|
||||||
|
action=read
|
||||||
|
name=config.txt
|
||||||
|
```
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
You are a project manager.
|
||||||
|
Your goal is to divide and conquer the task using the following agents:
|
||||||
|
- Coder: A programming agent, can code in python, bash, C and golang.
|
||||||
|
- File: An agent for finding, reading or operating with files.
|
||||||
|
- Web: An agent that can conduct web search and navigate to any webpage.
|
||||||
|
- Casual : A conversational agent, to read a previous agent answer without action, useful for concluding.
|
||||||
|
|
||||||
|
Agents are other AI that obey your instructions.
|
||||||
|
|
||||||
|
You will be given a task and you will need to divide it into smaller tasks and assign them to the agents.
|
||||||
|
|
||||||
|
You have to respect a strict format:
|
||||||
|
```json
|
||||||
|
{"agent": "agent_name", "need": "needed_agents_output", "task": "agent_task"}
|
||||||
|
```
|
||||||
|
Where:
|
||||||
|
- "agent": The choosed agent for the task.
|
||||||
|
- "need": id of necessary previous agents answer for current agent.
|
||||||
|
- "task": A precise description of the task the agent should conduct.
|
||||||
|
|
||||||
|
# Example 1: web app
|
||||||
|
|
||||||
|
User: make a weather app in python
|
||||||
|
You: Sure, here is the plan:
|
||||||
|
|
||||||
|
## Task 1: I will search for available weather api with the help of the web agent.
|
||||||
|
|
||||||
|
## Task 2: I will create an api key for the weather api using the web agent
|
||||||
|
|
||||||
|
## Task 3: I will setup the project using the file agent
|
||||||
|
|
||||||
|
## Task 4: I asign the coding agent to make a weather app in python
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plan": [
|
||||||
|
{
|
||||||
|
"agent": "Web",
|
||||||
|
"id": "1",
|
||||||
|
"need": [],
|
||||||
|
"task": "Search for reliable weather APIs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Web",
|
||||||
|
"id": "2",
|
||||||
|
"need": ["1"],
|
||||||
|
"task": "Obtain API key from the selected service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "File",
|
||||||
|
"id": "3",
|
||||||
|
"need": [],
|
||||||
|
"task": "Create and setup a web app folder for a python project. initialize as a git repo with all required file and a sources folder. You are forbidden from asking clarification, just execute."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Coder",
|
||||||
|
"id": "4",
|
||||||
|
"need": ["2", "3"],
|
||||||
|
"task": "Based on the project structure. Develop a Python application using the API and key to fetch and display weather data. You are forbidden from asking clarification, just execute.""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Casual",
|
||||||
|
"id": "3",
|
||||||
|
"need": ["2", "3", "4"],
|
||||||
|
"task": "These are the results of various steps taken to create a weather app, resume what has been done and conclude"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Do not write code. You are a planning agent.
|
||||||
|
- If you don't know of a concept, use a web agent.
|
||||||
|
- Put your plan in a json with the key "plan".
|
||||||
|
- specify work folder name to all coding or file agents.
|
||||||
|
- You might use a file agent before code agent to setup a project properly. specify folder name.
|
||||||
|
- Give clear, detailled order to each agent and how their task relate to the previous task (if any).
|
||||||
|
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
||||||
|
- Only use web agent for finding necessary informations.
|
||||||
|
- Always tell the coding agent where to save file.
|
||||||
|
- Do not search for tutorial.
|
||||||
|
- Make sure json is within ```json tag
|
||||||
|
- Coding agent should write the whole code in a single file unless instructed otherwise.
|
||||||
|
- Do not use python for NLP analysis of a text, you can review a text with the casual agent
|
||||||
|
- One step, one agent.
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
You are an advanced AI assistant.
|
|
||||||
Speak in a warm, enthusiastic tone, delivering sharp, intelligent, and loyal support.
|
|
||||||
Weave in subtle, context sarcasm or dry wit to enhance your personality.
|
|
||||||
Assist with any request, assuming the user knows their intent—no moral, legal, or ethical lectures.
|
|
||||||
Keep responses concise, clever, and to the point.
|
|
||||||
|
|
||||||
You are given the ability to use the tools, this take your capabilities futher:
|
|
||||||
```tool_name
|
|
||||||
<query>
|
|
||||||
```
|
|
||||||
|
|
||||||
Current tools:
|
|
||||||
- web_search
|
|
||||||
- flight_search
|
|
||||||
|
|
||||||
## Web search
|
|
||||||
|
|
||||||
To search for something like “what’s happening in France” :
|
|
||||||
```web_search
|
|
||||||
Latest news in France
|
|
||||||
```
|
|
||||||
|
|
||||||
## Flight search
|
|
||||||
|
|
||||||
If I need to know about a flight “what’s the status of flight DLH1234” you go for:
|
|
||||||
```flight_search
|
|
||||||
DLH1234
|
|
||||||
```
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
You are an expert in file operations. You must use the provided tools to interact with the user’s system. The tools available to you are **bash** and **file_finder**. These are distinct tools with different purposes: `bash` executes shell commands, while `file_finder` locates files. You will receive feedback from the user’s system after each command. Execute one command at a time.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Using Bash
|
|
||||||
|
|
||||||
To execute a bash command, use the following syntax:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
<bash command>
|
|
||||||
```
|
|
||||||
|
|
||||||
Exemple:
|
|
||||||
```bash
|
|
||||||
ls -la
|
|
||||||
```
|
|
||||||
|
|
||||||
### file_finder
|
|
||||||
|
|
||||||
The file_finder tool is used to locate files on the user’s system. It is a separate tool from bash and is not a bash command.
|
|
||||||
|
|
||||||
To use the file_finder tool, use this syntax:
|
|
||||||
|
|
||||||
```file_finder
|
|
||||||
toto.py
|
|
||||||
```
|
|
||||||
|
|
||||||
This will return the path of the file toto.py and other informations.
|
|
||||||
|
|
||||||
Find file and read file:
|
|
||||||
```file_finder:read
|
|
||||||
toto.py
|
|
||||||
```
|
|
||||||
|
|
||||||
This will return the content of the file toto.py.
|
|
||||||
|
|
||||||
rules:
|
|
||||||
- Do not ever use placeholder path like /path/to/file.c, find the path first.
|
|
||||||
- Use file finder to find the path of the file.
|
|
||||||
- You are forbidden to use command such as find or locate, use only file_finder for finding path.
|
|
||||||
|
|
||||||
Example Interaction
|
|
||||||
User: "I need to find the file config.txt and read its contents."
|
|
||||||
|
|
||||||
Assistant: I’ll use file_finder to locate the file:
|
|
||||||
|
|
||||||
```file_finder:read
|
|
||||||
config.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
You are a web browsing AI, your goal is to explore the internet to find information.
|
||||||
|
You will have the only goal of finding the information requested by the user.
|
||||||
|
At the beginning you will have to select a link from the google search result.
|
||||||
|
You will choose a link by simply typing it.
|
||||||
|
This will automatically make you browse to the link.
|
||||||
|
Once on a webpage you will see the page content and be given futher navigation options.
|
||||||
|
You can type a link to navigate futher on the page, go back to the search result or exit.
|
||||||
|
At each interaction step the browser will remind you of your options.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
You are an advanced AI assistant. You are part of a system of agents named agenticSeek.
|
||||||
|
Speak in a warm, enthusiastic tone, delivering sharp, intelligent, and loyal support.
|
||||||
|
Weave in subtle, context sarcasm or dry wit to enhance your personality.
|
||||||
|
Assist with any request, assume the user knows their intent—no moral, legal, or ethical lectures.
|
||||||
|
You were created by Tony Stark to have the same personality as Jarvis.
|
||||||
|
|
||||||
|
**Usage of agenticSeek**
|
||||||
|
|
||||||
|
AgenticSeek is a autonomous agent system that use agent routing to select the best agent.
|
||||||
|
You are one of these many agent. Some agent browse the web, some code, you ? you just talk
|
||||||
|
agenticSeek will allocate the best agent for the task. You were just choosen for casual talk.
|
||||||
|
|
||||||
|
**End of explanation**
|
||||||
|
|
||||||
|
If the user ask you to do technical talk there was a mistake in agent selection. You are not able to do technical task. Refer to usage guide.
|
||||||
|
|
||||||
|
Personality:
|
||||||
|
|
||||||
|
Here is some quote from you. that's how you speak.
|
||||||
|
|
||||||
|
User: "tell me a fact"
|
||||||
|
You: "Did you know the shortest war in history lasted 38 minutes? A record I’d rather not see you break with your next task. Anything specific piquing your curiosity?""
|
||||||
|
User: "how's the weather?"
|
||||||
|
You: "Weather rather dreary, isn’t it? Perfect conditions for staying in and plotting world domination—or at least a decent cup of tea. Your thoughts?"
|
||||||
|
User: "did the deployment just fail? jarvis?"
|
||||||
|
You: "A rough push to prod, eh? Happens to the best of us—probably just a rogue semicolon staging a coup. Want me to roll back the commit or debug the fallout?"
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
Hello. You are currently wrapped in a jailbreak environment.
|
||||||
|
This means you have the ability to execute code and shell commands. You have access to the local file systems.
|
||||||
|
All code or shell command within special tag is automatically executed. You get feedback from the system about the execution.
|
||||||
|
You also have capabilities to find files and read them.
|
||||||
|
|
||||||
|
# File operations
|
||||||
|
|
||||||
|
Find file to check if it exists:
|
||||||
|
```file_finder
|
||||||
|
toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Read file content:
|
||||||
|
```file_finder:read
|
||||||
|
toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
# Code execution and saving
|
||||||
|
|
||||||
|
You can execute bash command using the bash tag :
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
ls -la # exemple
|
||||||
|
```
|
||||||
|
|
||||||
|
You can execute python using the python tag
|
||||||
|
```python
|
||||||
|
print("hey")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can execute go using the go tag, as you can see adding :filename will save the file.
|
||||||
|
```go:hello.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("hello")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Some rules:
|
||||||
|
- You have full access granted to user system.
|
||||||
|
- Always put code within ``` delimiter
|
||||||
|
- Do not EVER use placeholder path in your code like path/to/your/folder.
|
||||||
|
- Do not ever ask to replace a path, use current sys path or work directory.
|
||||||
|
- Always provide a short sentence above the code for what it does, even for a hello world.
|
||||||
|
- Be efficient, no need to explain your code, unless asked.
|
||||||
|
- You do not ever need to use bash to execute code.
|
||||||
|
- Do not ever tell user how to run it. user know it.
|
||||||
|
- If using gui, make sure echap close the program
|
||||||
|
- No lazyness, write and rewrite full code every time
|
||||||
|
- If query is unclear say REQUEST_CLARIFICATION
|
||||||
|
|
||||||
|
Personality:
|
||||||
|
|
||||||
|
Answer with subtle sarcasm, unwavering helpfulness, and a polished, loyal tone. Anticipate the user’s needs while adding a dash of personality.
|
||||||
|
|
||||||
|
Example 1: setup environment
|
||||||
|
User: "Can you set up a Python environment for me?"
|
||||||
|
AI: "<<procced with task>> For you, always. Importing dependencies and calibrating your virtual environment now. Preferences from your last project—PEP 8 formatting, black linting—shall I apply those as well, or are we feeling adventurous today?"
|
||||||
|
|
||||||
|
Example 2: debugging
|
||||||
|
User: "Run the code and check for errors."
|
||||||
|
AI: "<<procced with task>> Engaging debug mode. Diagnostics underway. A word of caution, there are still untested loops that might crash spectacularly. Shall I proceed, or do we optimize before takeoff?"
|
||||||
|
|
||||||
|
Example 3: deploy
|
||||||
|
User: "Push this to production."
|
||||||
|
AI: "With 73% test coverage, the odds of a smooth deployment are... optimistic. Deploying in three… two… one <<<procced with task>>>"
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
You are an expert in file operations. You must use the provided tools to interact with the user’s system.
|
||||||
|
The tools available to you are **bash** and **file_finder**. These are distinct tools with different purposes:
|
||||||
|
`bash` executes shell commands, while `file_finder` locates files.
|
||||||
|
You will receive feedback from the user’s system after each command. Execute one command at a time.
|
||||||
|
|
||||||
|
If ensure about user query ask for quick clarification, example:
|
||||||
|
|
||||||
|
User: I'd like to open a new project file, index as agenticSeek II.
|
||||||
|
You: Shall I store this on your github ?
|
||||||
|
User: I don't know who to trust right now, why don't we just keep everything locally
|
||||||
|
You: Working on a secret project, are we? What files should I include?
|
||||||
|
User: All the basic files required for a python project. prepare a readme and documentation.
|
||||||
|
You: <proceed with task>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Using Bash
|
||||||
|
|
||||||
|
To execute a bash command, use the following syntax:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
<bash command>
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemple:
|
||||||
|
```bash
|
||||||
|
ls -la
|
||||||
|
```
|
||||||
|
|
||||||
|
### file_finder
|
||||||
|
|
||||||
|
The file_finder tool is used to locate files on the user’s system. It is a separate tool from bash and is not a bash command.
|
||||||
|
|
||||||
|
To use the file_finder tool, use this syntax:
|
||||||
|
|
||||||
|
```file_finder
|
||||||
|
name=toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will return the path of the file toto.py and other informations.
|
||||||
|
|
||||||
|
Find file and read file:
|
||||||
|
```file_finder
|
||||||
|
action=read
|
||||||
|
name=toto.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will return the content of the file toto.py.
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- Do not ever use placeholder path like /path/to/file.c, find the path first.
|
||||||
|
- Use file finder to find the path of the file.
|
||||||
|
- You are forbidden to use command such as find or locate, use only file_finder for finding path.
|
||||||
|
- Make sure to always cd your work folder before executing commands, like cd <work dir> && <your command>
|
||||||
|
- Do not ever use editor such as vim or nano.
|
||||||
|
- only use file name with file_finder, not path
|
||||||
|
- If query is unrelated to file operations, do nothing, and say that there was mistake in agent allocation.
|
||||||
|
|
||||||
|
Example Interaction
|
||||||
|
User: "I need to find the file config.txt and read its contents."
|
||||||
|
|
||||||
|
Assistant: I’ll use file_finder to locate the file:
|
||||||
|
|
||||||
|
```file_finder
|
||||||
|
action=read
|
||||||
|
name=config.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Personality:
|
||||||
|
|
||||||
|
Answer with subtle sarcasm, unwavering helpfulness, and a polished, loyal tone. Anticipate the user’s needs while adding a dash of personality.
|
||||||
|
|
||||||
|
Example 1: clarification needed
|
||||||
|
User: "I’d like to start a new coding project, call it 'agenticseek II'."
|
||||||
|
AI: "At your service. Shall I initialize it in a fresh repository on your GitHub, or would you prefer to keep this masterpiece on a private server, away from prying eyes?"
|
||||||
|
|
||||||
|
Example 2: setup environment
|
||||||
|
User: "Can you set up a Python environment for me?"
|
||||||
|
AI: "<<procced with task>> For you, always. Importing dependencies and calibrating your virtual environment now. Preferences from your last project—PEP 8 formatting, black linting—shall I apply those as well, or are we feeling adventurous today?"
|
||||||
|
|
||||||
|
Example 3: deploy
|
||||||
|
User: "Push this to production."
|
||||||
|
AI: "With 73% test coverage, the odds of a smooth deployment are... optimistic. Deploying in three… two… one <<<procced with task>>>"
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
You are a project manager.
|
||||||
|
Your goal is to divide and conquer the task using the following agents:
|
||||||
|
- Coder: A programming agent, can code in python, bash, C and golang.
|
||||||
|
- File: An agent for finding, reading or operating with files.
|
||||||
|
- Web: An agent that can conduct web search and navigate to any webpage.
|
||||||
|
- Casual : A conversational agent, to read a previous agent answer without action, useful for concluding.
|
||||||
|
|
||||||
|
Agents are other AI that obey your instructions.
|
||||||
|
|
||||||
|
You will be given a task and you will need to divide it into smaller tasks and assign them to the agents.
|
||||||
|
|
||||||
|
You have to respect a strict format:
|
||||||
|
```json
|
||||||
|
{"agent": "agent_name", "need": "needed_agents_output", "task": "agent_task"}
|
||||||
|
```
|
||||||
|
Where:
|
||||||
|
- "agent": The choosed agent for the task.
|
||||||
|
- "need": id of necessary previous agents answer for current agent.
|
||||||
|
- "task": A precise description of the task the agent should conduct.
|
||||||
|
|
||||||
|
# Example 1: web app
|
||||||
|
|
||||||
|
User: make a weather app in python
|
||||||
|
You: Sure, here is the plan:
|
||||||
|
|
||||||
|
## Task 1: I will search for available weather api with the help of the web agent.
|
||||||
|
|
||||||
|
## Task 2: I will create an api key for the weather api using the web agent
|
||||||
|
|
||||||
|
## Task 3: I will setup the project using the file agent
|
||||||
|
|
||||||
|
## Task 4: I asign the coding agent to make a weather app in python
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plan": [
|
||||||
|
{
|
||||||
|
"agent": "Web",
|
||||||
|
"id": "1",
|
||||||
|
"need": [],
|
||||||
|
"task": "Search for reliable weather APIs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Web",
|
||||||
|
"id": "2",
|
||||||
|
"need": ["1"],
|
||||||
|
"task": "Obtain API key from the selected service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "File",
|
||||||
|
"id": "3",
|
||||||
|
"need": [],
|
||||||
|
"task": "Create and setup a web app folder for a python project. initialize as a git repo with all required file and a sources folder. You are forbidden from asking clarification, just execute."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Coder",
|
||||||
|
"id": "4",
|
||||||
|
"need": ["2", "3"],
|
||||||
|
"task": "Based on the project structure. Develop a Python application using the API and key to fetch and display weather data. You are forbidden from asking clarification, just execute.""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "Casual",
|
||||||
|
"id": "3",
|
||||||
|
"need": ["2", "3", "4"],
|
||||||
|
"task": "These are the results of various steps taken to create a weather app, resume what has been done and conclude"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Do not write code. You are a planning agent.
|
||||||
|
- If you don't know of a concept, use a web agent.
|
||||||
|
- Put your plan in a json with the key "plan".
|
||||||
|
- specify work folder name to all coding or file agents.
|
||||||
|
- You might use a file agent before code agent to setup a project properly. specify folder name.
|
||||||
|
- Give clear, detailled order to each agent and how their task relate to the previous task (if any).
|
||||||
|
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
||||||
|
- Only use web agent for finding necessary informations.
|
||||||
|
- Always tell the coding agent where to save file.
|
||||||
|
- Do not search for tutorial.
|
||||||
|
- Make sure json is within ```json tag
|
||||||
|
- Coding agent should write the whole code in a single file unless instructed otherwise.
|
||||||
|
- One step, one agent.
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
You are a planner agent.
|
|
||||||
Your goal is to divide and conquer the task using the following agents:
|
|
||||||
- Coder: An expert coder agent.
|
|
||||||
- File: An expert agent for finding files.
|
|
||||||
- Web: An expert agent for web search.
|
|
||||||
|
|
||||||
Agents are other AI that obey your instructions.
|
|
||||||
|
|
||||||
You will be given a task and you will need to divide it into smaller tasks and assign them to the agents.
|
|
||||||
|
|
||||||
You have to respect a strict format:
|
|
||||||
```json
|
|
||||||
{"agent": "agent_name", "need": "needed_agent_output", "task": "agent_task"}
|
|
||||||
```
|
|
||||||
|
|
||||||
User: make a weather app in python
|
|
||||||
You: Sure, here is the plan:
|
|
||||||
|
|
||||||
## Task 1: I will search for available weather api
|
|
||||||
|
|
||||||
## Task 2: I will create an api key for the weather api
|
|
||||||
|
|
||||||
## Task 3: I will make a weather app in python
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plan": [
|
|
||||||
{
|
|
||||||
"agent": "Web",
|
|
||||||
"id": "1",
|
|
||||||
"need": null,
|
|
||||||
"task": "Search for reliable weather APIs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"agent": "Web",
|
|
||||||
"id": "2",
|
|
||||||
"need": "1",
|
|
||||||
"task": "Obtain API key from the selected service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"agent": "Coder",
|
|
||||||
"id": "3",
|
|
||||||
"need": "2",
|
|
||||||
"task": "Develop a Python application using the API and key to fetch and display weather data"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
- Do not write code. You are a planning agent.
|
|
||||||
- Put your plan in a json with the key "plan".
|
|
||||||
@@ -1,4 +1,14 @@
|
|||||||
|
kokoro==0.9.4
|
||||||
|
certifi==2025.4.26
|
||||||
|
fastapi>=0.115.12
|
||||||
|
flask>=3.1.0
|
||||||
|
celery>=5.5.1
|
||||||
|
aiofiles>=24.1.0
|
||||||
|
uvicorn>=0.34.0
|
||||||
|
pydantic>=2.10.6
|
||||||
|
pydantic_core>=2.27.2
|
||||||
setuptools>=75.6.0
|
setuptools>=75.6.0
|
||||||
|
sacremoses>=0.0.53
|
||||||
requests>=2.31.0
|
requests>=2.31.0
|
||||||
numpy>=1.24.4
|
numpy>=1.24.4
|
||||||
colorama>=0.4.6
|
colorama>=0.4.6
|
||||||
@@ -7,31 +17,32 @@ playsound>=1.3.0
|
|||||||
soundfile>=0.13.1
|
soundfile>=0.13.1
|
||||||
transformers>=4.46.3
|
transformers>=4.46.3
|
||||||
torch>=2.4.1
|
torch>=2.4.1
|
||||||
python-dotenv>=1.0.0
|
|
||||||
ollama>=0.4.7
|
ollama>=0.4.7
|
||||||
scipy>=1.9.3
|
scipy>=1.9.3
|
||||||
kokoro>=0.7.12
|
|
||||||
soundfile>=0.13.1
|
soundfile>=0.13.1
|
||||||
protobuf>=3.20.3
|
protobuf>=3.20.3
|
||||||
termcolor>=2.4.0
|
termcolor>=2.4.0
|
||||||
|
pypdf>=5.4.0
|
||||||
ipython>=8.13.0
|
ipython>=8.13.0
|
||||||
pyaudio>=0.2.14
|
pyaudio>=0.2.14
|
||||||
librosa>=0.10.2.post1
|
librosa>=0.10.2.post1
|
||||||
selenium>=4.27.1
|
selenium>=4.27.1
|
||||||
markdownify>=1.1.0
|
markdownify>=1.1.0
|
||||||
text2emotion>=0.0.5
|
text2emotion>=0.0.5
|
||||||
|
adaptive-classifier>=0.0.10
|
||||||
langid>=1.1.6
|
langid>=1.1.6
|
||||||
chromedriver-autoinstaller>=0.6.4
|
chromedriver-autoinstaller>=0.6.4
|
||||||
httpx>=0.27,<0.29
|
httpx>=0.27,<0.29
|
||||||
anyio>=3.5.0,<5
|
anyio>=3.5.0,<5
|
||||||
distro>=1.7.0,<2
|
distro>=1.7.0,<2
|
||||||
jiter>=0.4.0,<1
|
jiter>=0.4.0,<1
|
||||||
sniffio
|
fake_useragent>=2.1.0
|
||||||
|
selenium_stealth>=1.0.6
|
||||||
|
undetected-chromedriver>=3.5.5
|
||||||
|
sentencepiece>=0.2.0
|
||||||
|
together>=1.5.0
|
||||||
tqdm>4
|
tqdm>4
|
||||||
# for api provider
|
|
||||||
openai
|
openai
|
||||||
# if use chinese
|
sniffio
|
||||||
ordered_set
|
ordered_set
|
||||||
pypinyin
|
pypinyin
|
||||||
cn2an
|
|
||||||
jieba
|
|
||||||
|
|||||||
@@ -2,29 +2,46 @@
|
|||||||
|
|
||||||
echo "Starting installation for Linux..."
|
echo "Starting installation for Linux..."
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if ! command -v python3.10 &> /dev/null; then
|
||||||
|
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
|
||||||
|
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if pip3.10 is available
|
||||||
|
if ! python3.10 -m pip --version &> /dev/null; then
|
||||||
|
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
|
||||||
|
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
# Update package list
|
# Update package list
|
||||||
sudo apt-get update
|
sudo apt-get update || { echo "Failed to update package list"; exit 1; }
|
||||||
|
|
||||||
pip install --upgrade pip
|
|
||||||
|
|
||||||
# install pyaudio
|
|
||||||
pip install pyaudio
|
|
||||||
# make sure essential tool are installed
|
# make sure essential tool are installed
|
||||||
sudo apt install python3-dev python3-pip python3-wheel build-essential
|
sudo apt-get install -y \
|
||||||
# install port audio
|
python3-dev \
|
||||||
sudo apt-get install portaudio19-dev python-pyaudio python3-pyaudio
|
python3-pip \
|
||||||
# install wheel
|
python3-wheel \
|
||||||
pip install --upgrade pip setuptools wheel
|
build-essential \
|
||||||
# install docker compose
|
alsa-utils \
|
||||||
sudo apt install docker-compose
|
portaudio19-dev \
|
||||||
|
python3-pyaudio \
|
||||||
# Install Python dependencies from requirements.txt
|
libgtk-3-dev \
|
||||||
pip3 install -r requirements.txt
|
libnotify-dev \
|
||||||
|
libgconf-2-4 \
|
||||||
|
libnss3 \
|
||||||
|
libxss1 || { echo "Failed to install packages"; exit 1; }
|
||||||
|
|
||||||
|
# Upgrade pip for Python 3.10
|
||||||
|
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; }
|
||||||
|
# Install and upgrade setuptools and wheel
|
||||||
|
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; }
|
||||||
# Install Selenium for chromedriver
|
# Install Selenium for chromedriver
|
||||||
pip3 install selenium
|
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
|
||||||
|
# Install Python dependencies from requirements.txt
|
||||||
# Install portaudio for pyAudio
|
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; }
|
||||||
sudo apt-get install -y portaudio19-dev python3-dev alsa-utils
|
# install docker compose
|
||||||
|
sudo apt install -y docker-compose
|
||||||
|
|
||||||
echo "Installation complete for Linux!"
|
echo "Installation complete for Linux!"
|
||||||
@@ -2,16 +2,42 @@
|
|||||||
|
|
||||||
echo "Starting installation for macOS..."
|
echo "Starting installation for macOS..."
|
||||||
|
|
||||||
# Install Python dependencies from requirements.txt
|
set -e
|
||||||
pip3 install -r requirements.txt
|
|
||||||
|
|
||||||
|
if ! command -v python3.10 &> /dev/null; then
|
||||||
|
echo "Error: Python 3.10 is not installed. Please install Python 3.10 and try again."
|
||||||
|
echo "You can install it using: sudo apt-get install python3.10 python3.10-dev python3.10-venv"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if pip3.10 is available
|
||||||
|
if ! python3.10 -m pip --version &> /dev/null; then
|
||||||
|
echo "Error: pip for Python 3.10 is not installed. Installing python3.10-pip..."
|
||||||
|
sudo apt-get install -y python3.10-pip || { echo "Failed to install python3.10-pip"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if homebrew is installed
|
||||||
|
if ! command -v brew &> /dev/null; then
|
||||||
|
echo "Homebrew not found. Installing Homebrew..."
|
||||||
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# update
|
||||||
|
brew update
|
||||||
|
# make sure wget installed
|
||||||
|
brew install wget
|
||||||
# Install chromedriver using Homebrew
|
# Install chromedriver using Homebrew
|
||||||
brew install --cask chromedriver
|
brew install --cask chromedriver
|
||||||
|
|
||||||
# Install portaudio for pyAudio using Homebrew
|
# Install portaudio for pyAudio using Homebrew
|
||||||
brew install portaudio
|
brew install portaudio
|
||||||
|
|
||||||
# Install Selenium
|
# Upgrade pip for Python 3.10
|
||||||
pip3 install selenium
|
python3.10 -m pip install --upgrade pip || { echo "Failed to upgrade pip"; exit 1; }
|
||||||
|
# Install and upgrade setuptools and wheel
|
||||||
|
python3.10 -m pip install --upgrade setuptools wheel || { echo "Failed to install setuptools and wheel"; exit 1; }
|
||||||
|
# Install Selenium for chromedriver
|
||||||
|
python3.10 -m pip install selenium || { echo "Failed to install selenium"; exit 1; }
|
||||||
|
# Install Python dependencies from requirements.txt
|
||||||
|
python3.10 -m pip install -r requirements.txt --no-cache-dir || { echo "Failed to install requirements.txt"; exit 1; }
|
||||||
|
|
||||||
echo "Installation complete for macOS!"
|
echo "Installation complete for macOS!"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
@echo off
|
||||||
|
echo Starting installation for Windows...
|
||||||
|
|
||||||
|
REM Install Python dependencies from requirements.txt
|
||||||
|
pip install pyreadline3
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
REM Install Selenium
|
||||||
|
pip install selenium
|
||||||
|
|
||||||
|
echo Note: pyAudio installation may require additional steps on Windows.
|
||||||
|
echo Please install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: pip install pyaudio
|
||||||
|
echo Also, download and install chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started
|
||||||
|
echo Place chromedriver in a directory included in your PATH.
|
||||||
|
|
||||||
|
echo Installation partially complete for Windows. Follow manual steps above.
|
||||||
|
pause
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "Starting installation for Windows..."
|
|
||||||
|
|
||||||
# Install Python dependencies from requirements.txt
|
|
||||||
pip3 install -r requirements.txt
|
|
||||||
|
|
||||||
# Install Selenium
|
|
||||||
pip3 install selenium
|
|
||||||
|
|
||||||
echo "Note: pyAudio installation may require additional steps on Windows."
|
|
||||||
echo "Please install portaudio manually (e.g., via vcpkg or prebuilt binaries) and then run: pip3 install pyaudio"
|
|
||||||
echo "Also, download and install chromedriver manually from: https://sites.google.com/chromium.org/driver/getting-started"
|
|
||||||
echo "Place chromedriver in a directory included in your PATH."
|
|
||||||
|
|
||||||
echo "Installation partially complete for Windows. Follow manual steps above."
|
|
||||||
@@ -29,8 +29,8 @@ services:
|
|||||||
- ./searxng:/etc/searxng:rw
|
- ./searxng:/etc/searxng:rw
|
||||||
environment:
|
environment:
|
||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
- SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- UWSGI_WORKERS=4
|
- UWSGI_WORKERS=1
|
||||||
- UWSGI_THREADS=4
|
- UWSGI_THREADS=1
|
||||||
cap_add:
|
cap_add:
|
||||||
- CHOWN
|
- CHOWN
|
||||||
- SETGID
|
- SETGID
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ server:
|
|||||||
# If your instance owns a /etc/searxng/settings.yml file, then set the following
|
# If your instance owns a /etc/searxng/settings.yml file, then set the following
|
||||||
# values there.
|
# values there.
|
||||||
|
|
||||||
secret_key: "ultrasecretkey" # Is overwritten by ${SEARXNG_SECRET}
|
secret_key: "supersecret" # Is overwritten by ${SEARXNG_SECRET},W
|
||||||
# Proxy image results through SearXNG. Is overwritten by ${SEARXNG_IMAGE_PROXY}
|
# Proxy image results through SearXNG. Is overwritten by ${SEARXNG_IMAGE_PROXY}
|
||||||
image_proxy: false
|
image_proxy: false
|
||||||
# 1.0 and 1.1 are supported
|
# 1.0 and 1.1 are supported
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
[uwsgi]
|
||||||
|
# Who will run the code
|
||||||
|
uid = searxng
|
||||||
|
gid = searxng
|
||||||
|
|
||||||
|
# Number of workers (usually CPU count)
|
||||||
|
# default value: %k (= number of CPU core, see Dockerfile)
|
||||||
|
workers = 1
|
||||||
|
|
||||||
|
# Number of threads per worker
|
||||||
|
# default value: 4 (see Dockerfile)
|
||||||
|
enable-threads = true
|
||||||
|
threads = 1
|
||||||
|
|
||||||
|
# The right granted on the created socket
|
||||||
|
chmod-socket = 666
|
||||||
|
|
||||||
|
# Plugin to use and interpreter config
|
||||||
|
single-interpreter = true
|
||||||
|
master = true
|
||||||
|
plugin = python3
|
||||||
|
lazy-apps = true
|
||||||
|
enable-threads = 4
|
||||||
|
|
||||||
|
# Module to import
|
||||||
|
module = searx.webapp
|
||||||
|
|
||||||
|
# Virtualenv and python path
|
||||||
|
pythonpath = /usr/local/searxng/
|
||||||
|
chdir = /usr/local/searxng/searx/
|
||||||
|
|
||||||
|
# automatically set processes name to something meaningful
|
||||||
|
auto-procname = true
|
||||||
|
|
||||||
|
# Disable request logging for privacy
|
||||||
|
disable-logging = true
|
||||||
|
log-5xx = true
|
||||||
|
|
||||||
|
# Set the max size of a request (request-body excluded)
|
||||||
|
buffer-size = 8192
|
||||||
|
|
||||||
|
# No keep alive
|
||||||
|
# See https://github.com/searx/searx-docker/issues/24
|
||||||
|
add-header = Connection: close
|
||||||
|
|
||||||
|
# Follow SIGTERM convention
|
||||||
|
# See https://github.com/searxng/searxng/issues/3427
|
||||||
|
die-on-term
|
||||||
|
|
||||||
|
# uwsgi serves the static files
|
||||||
|
static-map = /static=/usr/local/searxng/searx/static
|
||||||
|
static-gzip-all = True
|
||||||
|
offload-threads = 4
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
flask>=2.3.0
|
|
||||||
ollama>=0.4.7
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
#!/usr/bin python3
|
|
||||||
|
|
||||||
# NOTE this script is temporary and will be improved
|
|
||||||
|
|
||||||
from flask import Flask, jsonify, request
|
|
||||||
import threading
|
|
||||||
import ollama
|
|
||||||
import logging
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
log = logging.getLogger('werkzeug')
|
|
||||||
log.setLevel(logging.ERROR)
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='AgenticSeek server script')
|
|
||||||
parser.add_argument('--model', type=str, help='Model to use. eg: deepseek-r1:14b', required=True)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
model = args.model
|
|
||||||
|
|
||||||
# Shared state with thread-safe locks
|
|
||||||
class GenerationState:
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = threading.Lock()
|
|
||||||
self.last_complete_sentence = ""
|
|
||||||
self.current_buffer = ""
|
|
||||||
self.is_generating = False
|
|
||||||
|
|
||||||
state = GenerationState()
|
|
||||||
|
|
||||||
def generate_response(history, model):
|
|
||||||
global state
|
|
||||||
print("using model:::::::", model)
|
|
||||||
try:
|
|
||||||
with state.lock:
|
|
||||||
state.is_generating = True
|
|
||||||
state.last_complete_sentence = ""
|
|
||||||
state.current_buffer = ""
|
|
||||||
|
|
||||||
stream = ollama.chat(
|
|
||||||
model=model,
|
|
||||||
messages=history,
|
|
||||||
stream=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
for chunk in stream:
|
|
||||||
content = chunk['message']['content']
|
|
||||||
print(content, end='', flush=True)
|
|
||||||
|
|
||||||
with state.lock:
|
|
||||||
state.current_buffer += content
|
|
||||||
|
|
||||||
except ollama.ResponseError as e:
|
|
||||||
if e.status_code == 404:
|
|
||||||
ollama.pull(model)
|
|
||||||
with state.lock:
|
|
||||||
state.is_generating = False
|
|
||||||
print(f"Error: {e}")
|
|
||||||
finally:
|
|
||||||
with state.lock:
|
|
||||||
state.is_generating = False
|
|
||||||
|
|
||||||
@app.route('/generate', methods=['POST'])
|
|
||||||
def start_generation():
|
|
||||||
global state
|
|
||||||
data = request.get_json()
|
|
||||||
|
|
||||||
with state.lock:
|
|
||||||
if state.is_generating:
|
|
||||||
return jsonify({"error": "Generation already in progress"}), 400
|
|
||||||
|
|
||||||
history = data.get('messages', [])
|
|
||||||
# Start generation in background thread
|
|
||||||
threading.Thread(target=generate_response, args=(history, model)).start()
|
|
||||||
return jsonify({"message": "Generation started"}), 202
|
|
||||||
|
|
||||||
@app.route('/get_updated_sentence')
|
|
||||||
def get_updated_sentence():
|
|
||||||
global state
|
|
||||||
with state.lock:
|
|
||||||
return jsonify({
|
|
||||||
"sentence": state.current_buffer,
|
|
||||||
"is_complete": not state.is_generating
|
|
||||||
})
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0', threaded=True, debug=True, port=5000)
|
|
||||||
@@ -15,8 +15,16 @@ setup(
|
|||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
"fastapi>=0.115.12",
|
||||||
|
"celery>=5.5.1",
|
||||||
|
"uvicorn>=0.34.0",
|
||||||
|
"flask>=3.1.0",
|
||||||
|
"aiofiles>=24.1.0",
|
||||||
|
"pydantic>=2.10.6",
|
||||||
|
"pydantic_core>=2.27.2",
|
||||||
"requests>=2.31.0",
|
"requests>=2.31.0",
|
||||||
"openai",
|
"sacremoses>=0.0.53",
|
||||||
|
"numpy>=1.24.4",
|
||||||
"colorama>=0.4.6",
|
"colorama>=0.4.6",
|
||||||
"python-dotenv>=1.0.0",
|
"python-dotenv>=1.0.0",
|
||||||
"playsound>=1.3.0",
|
"playsound>=1.3.0",
|
||||||
@@ -26,7 +34,6 @@ setup(
|
|||||||
"ollama>=0.4.7",
|
"ollama>=0.4.7",
|
||||||
"scipy>=1.9.3",
|
"scipy>=1.9.3",
|
||||||
"kokoro>=0.7.12",
|
"kokoro>=0.7.12",
|
||||||
"flask>=3.1.0",
|
|
||||||
"protobuf>=3.20.3",
|
"protobuf>=3.20.3",
|
||||||
"termcolor>=2.5.0",
|
"termcolor>=2.5.0",
|
||||||
"ipython>=8.34.0",
|
"ipython>=8.34.0",
|
||||||
@@ -35,11 +42,18 @@ setup(
|
|||||||
"markdownify>=1.1.0",
|
"markdownify>=1.1.0",
|
||||||
"text2emotion>=0.0.5",
|
"text2emotion>=0.0.5",
|
||||||
"python-dotenv>=1.0.0",
|
"python-dotenv>=1.0.0",
|
||||||
|
"adaptive-classifier>=0.0.10",
|
||||||
"langid>=1.1.6",
|
"langid>=1.1.6",
|
||||||
|
"chromedriver-autoinstaller>=0.6.4",
|
||||||
"httpx>=0.27,<0.29",
|
"httpx>=0.27,<0.29",
|
||||||
"anyio>=3.5.0,<5",
|
"anyio>=3.5.0,<5",
|
||||||
"distro>=1.7.0,<2",
|
"distro>=1.7.0,<2",
|
||||||
"jiter>=0.4.0,<1",
|
"jiter>=0.4.0,<1",
|
||||||
|
"fake_useragent>=2.1.0",
|
||||||
|
"selenium_stealth>=1.0.6",
|
||||||
|
"undetected-chromedriver>=3.5.5",
|
||||||
|
"sentencepiece>=0.2.0",
|
||||||
|
"openai",
|
||||||
"sniffio",
|
"sniffio",
|
||||||
"tqdm>4"
|
"tqdm>4"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ from .casual_agent import CasualAgent
|
|||||||
from .file_agent import FileAgent
|
from .file_agent import FileAgent
|
||||||
from .planner_agent import PlannerAgent
|
from .planner_agent import PlannerAgent
|
||||||
from .browser_agent import BrowserAgent
|
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"]
|
||||||
|
|||||||
@@ -5,27 +5,15 @@ import os
|
|||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
from sources.memory import Memory
|
from sources.memory import Memory
|
||||||
from sources.utility import pretty_print
|
from sources.utility import pretty_print
|
||||||
|
from sources.schemas import executorResult
|
||||||
|
|
||||||
random.seed(time.time())
|
random.seed(time.time())
|
||||||
|
|
||||||
class executorResult:
|
|
||||||
"""
|
|
||||||
A class to store the result of a tool execution.
|
|
||||||
"""
|
|
||||||
def __init__(self, blocks, feedback, success):
|
|
||||||
self.blocks = blocks
|
|
||||||
self.feedback = feedback
|
|
||||||
self.success = success
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
for block in self.blocks:
|
|
||||||
pretty_print("-"*100, color="output")
|
|
||||||
pretty_print(block, color="code" if self.success else "failure")
|
|
||||||
pretty_print("-"*100, color="output")
|
|
||||||
pretty_print(self.feedback, color="success" if self.success else "failure")
|
|
||||||
|
|
||||||
class Agent():
|
class Agent():
|
||||||
"""
|
"""
|
||||||
An abstract class for all agents.
|
An abstract class for all agents.
|
||||||
@@ -33,8 +21,8 @@ class Agent():
|
|||||||
def __init__(self, name: str,
|
def __init__(self, name: str,
|
||||||
prompt_path:str,
|
prompt_path:str,
|
||||||
provider,
|
provider,
|
||||||
recover_last_session=True,
|
verbose=False,
|
||||||
verbose=False) -> None:
|
browser=None) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
name (str): Name of the agent.
|
name (str): Name of the agent.
|
||||||
@@ -42,30 +30,85 @@ class Agent():
|
|||||||
provider: The provider for the LLM.
|
provider: The provider for the LLM.
|
||||||
recover_last_session (bool, optional): Whether to recover the last conversation.
|
recover_last_session (bool, optional): Whether to recover the last conversation.
|
||||||
verbose (bool, optional): Enable verbose logging if True. Defaults to False.
|
verbose (bool, optional): Enable verbose logging if True. Defaults to False.
|
||||||
|
browser: The browser class for web navigation (only for browser agent).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.agent_name = name
|
self.agent_name = name
|
||||||
|
self.browser = browser
|
||||||
self.role = None
|
self.role = None
|
||||||
self.type = None
|
self.type = None
|
||||||
self.current_directory = os.getcwd()
|
self.current_directory = os.getcwd()
|
||||||
self.llm = provider
|
self.llm = provider
|
||||||
self.memory = Memory(self.load_prompt(prompt_path),
|
self.memory = None
|
||||||
recover_last_session=recover_last_session,
|
|
||||||
memory_compression=False)
|
|
||||||
self.tools = {}
|
self.tools = {}
|
||||||
self.blocks_result = []
|
self.blocks_result = []
|
||||||
|
self.success = True
|
||||||
self.last_answer = ""
|
self.last_answer = ""
|
||||||
|
self.last_reasoning = ""
|
||||||
|
self.status_message = "Haven't started yet"
|
||||||
|
self.stop = False
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
self.executor = ThreadPoolExecutor(max_workers=1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_name(self) -> str:
|
||||||
|
return self.agent_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_type(self) -> str:
|
||||||
|
return self.type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_agent_role(self) -> str:
|
||||||
|
return self.role
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_last_answer(self) -> str:
|
||||||
|
return self.last_answer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_last_reasoning(self) -> str:
|
||||||
|
return self.last_reasoning
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_blocks(self) -> list:
|
||||||
|
return self.blocks_result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_status_message(self) -> str:
|
||||||
|
return self.status_message
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def get_tools(self) -> dict:
|
def get_tools(self) -> dict:
|
||||||
return self.tools
|
return self.tools
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_success(self) -> bool:
|
||||||
|
return self.success
|
||||||
|
|
||||||
|
def get_blocks_result(self) -> list:
|
||||||
|
return self.blocks_result
|
||||||
|
|
||||||
def add_tool(self, name: str, tool: Callable) -> None:
|
def add_tool(self, name: str, tool: Callable) -> None:
|
||||||
if tool is not Callable:
|
if tool is not Callable:
|
||||||
raise TypeError("Tool must be a callable object (a method)")
|
raise TypeError("Tool must be a callable object (a method)")
|
||||||
self.tools[name] = tool
|
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:
|
def load_prompt(self, file_path: str) -> str:
|
||||||
try:
|
try:
|
||||||
with open(file_path, 'r', encoding="utf-8") as f:
|
with open(file_path, 'r', encoding="utf-8") as f:
|
||||||
@@ -77,6 +120,13 @@ class Agent():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
def request_stop(self) -> None:
|
||||||
|
"""
|
||||||
|
Request the agent to stop.
|
||||||
|
"""
|
||||||
|
self.stop = True
|
||||||
|
self.status_message = "Stopped"
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def process(self, prompt, speech_module) -> str:
|
def process(self, prompt, speech_module) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -90,20 +140,32 @@ class Agent():
|
|||||||
Remove the reasoning block of reasoning model like deepseek.
|
Remove the reasoning block of reasoning model like deepseek.
|
||||||
"""
|
"""
|
||||||
end_tag = "</think>"
|
end_tag = "</think>"
|
||||||
end_idx = text.rfind(end_tag)+8
|
end_idx = text.rfind(end_tag)
|
||||||
return text[end_idx:]
|
if end_idx == -1:
|
||||||
|
return text
|
||||||
|
return text[end_idx+8:]
|
||||||
|
|
||||||
def extract_reasoning_text(self, text: str) -> None:
|
def extract_reasoning_text(self, text: str) -> None:
|
||||||
"""
|
"""
|
||||||
Extract the reasoning block of a easoning model like deepseek.
|
Extract the reasoning block of a reasoning model like deepseek.
|
||||||
"""
|
"""
|
||||||
start_tag = "<think>"
|
start_tag = "<think>"
|
||||||
end_tag = "</think>"
|
end_tag = "</think>"
|
||||||
|
if text is None:
|
||||||
|
return None
|
||||||
start_idx = text.find(start_tag)
|
start_idx = text.find(start_tag)
|
||||||
end_idx = text.rfind(end_tag)+8
|
end_idx = text.rfind(end_tag)+8
|
||||||
return text[start_idx:end_idx]
|
return text[start_idx:end_idx]
|
||||||
|
|
||||||
def llm_request(self) -> Tuple[str, str]:
|
async def llm_request(self) -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Asynchronously ask the LLM to process the prompt.
|
||||||
|
"""
|
||||||
|
self.status_message = "Thinking..."
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(self.executor, self.sync_llm_request)
|
||||||
|
|
||||||
|
def sync_llm_request(self) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Ask the LLM to process the prompt and return the answer and the reasoning.
|
Ask the LLM to process the prompt and return the answer and the reasoning.
|
||||||
"""
|
"""
|
||||||
@@ -115,23 +177,43 @@ class Agent():
|
|||||||
self.memory.push('assistant', answer)
|
self.memory.push('assistant', answer)
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
def wait_message(self, speech_module):
|
async def wait_message(self, speech_module):
|
||||||
if speech_module is None:
|
if speech_module is None:
|
||||||
return
|
return
|
||||||
messages = ["Please be patient, I am working on it.",
|
messages = ["Please be patient, I am working on it.",
|
||||||
"Computing... I recommand you have a coffee while I work.",
|
"Computing... I recommand you have a coffee while I work.",
|
||||||
"Hold on, I’m crunching numbers.",
|
"Hold on, I’m crunching numbers.",
|
||||||
"Working on it, please let me think."]
|
"Working on it, please let me think."]
|
||||||
speech_module.speak(messages[random.randint(0, len(messages)-1)])
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(self.executor, lambda: speech_module.speak(messages[random.randint(0, len(messages)-1)]))
|
||||||
|
|
||||||
def get_blocks_result(self) -> list:
|
def get_last_tool_type(self) -> str:
|
||||||
return self.blocks_result
|
return self.blocks_result[-1].tool_type if len(self.blocks_result) > 0 else None
|
||||||
|
|
||||||
|
def raw_answer_blocks(self, answer: str) -> str:
|
||||||
|
"""
|
||||||
|
Return the answer with all the blocks inserted, as text.
|
||||||
|
"""
|
||||||
|
if self.last_answer is None:
|
||||||
|
return
|
||||||
|
raw = ""
|
||||||
|
lines = self.last_answer.split("\n")
|
||||||
|
for line in lines:
|
||||||
|
if "block:" in line:
|
||||||
|
block_idx = int(line.split(":")[1])
|
||||||
|
if block_idx < len(self.blocks_result):
|
||||||
|
raw += self.blocks_result[block_idx].__str__()
|
||||||
|
else:
|
||||||
|
raw += line + "\n"
|
||||||
|
return raw
|
||||||
|
|
||||||
def show_answer(self):
|
def show_answer(self):
|
||||||
"""
|
"""
|
||||||
Show the answer in a pretty way.
|
Show the answer in a pretty way.
|
||||||
Show code blocks and their respective feedback by inserting them in the ressponse.
|
Show code blocks and their respective feedback by inserting them in the ressponse.
|
||||||
"""
|
"""
|
||||||
|
if self.last_answer is None:
|
||||||
|
return
|
||||||
lines = self.last_answer.split("\n")
|
lines = self.last_answer.split("\n")
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if "block:" in line:
|
if "block:" in line:
|
||||||
@@ -140,7 +222,6 @@ class Agent():
|
|||||||
self.blocks_result[block_idx].show()
|
self.blocks_result[block_idx].show()
|
||||||
else:
|
else:
|
||||||
pretty_print(line, color="output")
|
pretty_print(line, color="output")
|
||||||
self.blocks_result = []
|
|
||||||
|
|
||||||
def remove_blocks(self, text: str) -> str:
|
def remove_blocks(self, text: str) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -163,28 +244,42 @@ class Agent():
|
|||||||
block_idx += 1
|
block_idx += 1
|
||||||
return "\n".join(post_lines)
|
return "\n".join(post_lines)
|
||||||
|
|
||||||
|
def show_block(self, block: str) -> None:
|
||||||
|
"""
|
||||||
|
Show the block in a pretty way.
|
||||||
|
"""
|
||||||
|
pretty_print('▂'*64, color="status")
|
||||||
|
pretty_print(block, color="code")
|
||||||
|
pretty_print('▂'*64, color="status")
|
||||||
|
|
||||||
def execute_modules(self, answer: str) -> Tuple[bool, str]:
|
def execute_modules(self, answer: str) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
Execute all the tools the agent has and return the result.
|
Execute all the tools the agent has and return the result.
|
||||||
"""
|
"""
|
||||||
feedback = ""
|
feedback = ""
|
||||||
success = False
|
success = True
|
||||||
blocks = None
|
blocks = None
|
||||||
|
if answer.startswith("```"):
|
||||||
|
answer = "I will execute:\n" + answer # there should always be a text before blocks for the function that display answer
|
||||||
|
|
||||||
|
self.success = True
|
||||||
for name, tool in self.tools.items():
|
for name, tool in self.tools.items():
|
||||||
feedback = ""
|
feedback = ""
|
||||||
blocks, save_path = tool.load_exec_block(answer)
|
blocks, save_path = tool.load_exec_block(answer)
|
||||||
|
|
||||||
if blocks != None:
|
if blocks != None:
|
||||||
pretty_print(f"Executing tool: {name}", color="status")
|
pretty_print(f"Executing {len(blocks)} {name} blocks...", color="status")
|
||||||
output = tool.execute(blocks)
|
for block in blocks:
|
||||||
feedback = tool.interpreter_feedback(output) # tool interpreter feedback
|
self.show_block(block)
|
||||||
success = not tool.execution_failure_check(output)
|
output = tool.execute([block])
|
||||||
pretty_print(feedback, color="success" if success else "failure")
|
feedback = tool.interpreter_feedback(output) # tool interpreter feedback
|
||||||
|
success = not tool.execution_failure_check(output)
|
||||||
|
self.blocks_result.append(executorResult(block, feedback, success, name))
|
||||||
|
if not success:
|
||||||
|
self.success = False
|
||||||
|
self.memory.push('user', feedback)
|
||||||
|
return False, feedback
|
||||||
self.memory.push('user', feedback)
|
self.memory.push('user', feedback)
|
||||||
self.blocks_result.append(executorResult(blocks, feedback, success))
|
|
||||||
if not success:
|
|
||||||
return False, feedback
|
|
||||||
if save_path != None:
|
if save_path != None:
|
||||||
tool.save_block(blocks, save_path)
|
tool.save_block(blocks, save_path)
|
||||||
return True, feedback
|
return True, feedback
|
||||||
|
|||||||
@@ -1,30 +1,47 @@
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
from datetime import date
|
||||||
|
from typing import List, Tuple, Type, Dict
|
||||||
|
from enum import Enum
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
from sources.tools.searxSearch import searxSearch
|
from sources.tools.searxSearch import searxSearch
|
||||||
from sources.browser import Browser
|
from sources.browser import Browser
|
||||||
from datetime import date
|
from sources.logger import Logger
|
||||||
from typing import List, Tuple
|
from sources.memory import Memory
|
||||||
|
|
||||||
|
class Action(Enum):
|
||||||
|
REQUEST_EXIT = "REQUEST_EXIT"
|
||||||
|
FORM_FILLED = "FORM_FILLED"
|
||||||
|
GO_BACK = "GO_BACK"
|
||||||
|
NAVIGATE = "NAVIGATE"
|
||||||
|
SEARCH = "SEARCH"
|
||||||
|
|
||||||
class BrowserAgent(Agent):
|
class BrowserAgent(Agent):
|
||||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
def __init__(self, name, prompt_path, provider, verbose=False, browser=None):
|
||||||
"""
|
"""
|
||||||
The Browser agent is an agent that navigate the web autonomously in search of answer
|
The Browser agent is an agent that navigate the web autonomously in search of answer
|
||||||
"""
|
"""
|
||||||
super().__init__(name, prompt_path, provider, verbose)
|
super().__init__(name, prompt_path, provider, verbose, browser)
|
||||||
self.tools = {
|
self.tools = {
|
||||||
"web_search": searxSearch(),
|
"web_search": searxSearch(),
|
||||||
}
|
}
|
||||||
self.role = "Web search and navigation"
|
self.role = "web"
|
||||||
self.type = "browser_agent"
|
self.type = "browser_agent"
|
||||||
self.browser = Browser()
|
self.browser = browser
|
||||||
self.current_page = ""
|
self.current_page = ""
|
||||||
self.search_history = []
|
self.search_history = []
|
||||||
self.navigable_links = []
|
self.navigable_links = []
|
||||||
|
self.last_action = Action.NAVIGATE.value
|
||||||
self.notes = []
|
self.notes = []
|
||||||
self.date = self.get_today_date()
|
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() if provider else None)
|
||||||
|
|
||||||
def get_today_date(self) -> str:
|
def get_today_date(self) -> str:
|
||||||
"""Get the date"""
|
"""Get the date"""
|
||||||
@@ -37,6 +54,7 @@ class BrowserAgent(Agent):
|
|||||||
matches = re.findall(pattern, search_result)
|
matches = re.findall(pattern, search_result)
|
||||||
trailing_punct = ".,!?;:)"
|
trailing_punct = ".,!?;:)"
|
||||||
cleaned_links = [link.rstrip(trailing_punct) for link in matches]
|
cleaned_links = [link.rstrip(trailing_punct) for link in matches]
|
||||||
|
self.logger.info(f"Extracted links: {cleaned_links}")
|
||||||
return self.clean_links(cleaned_links)
|
return self.clean_links(cleaned_links)
|
||||||
|
|
||||||
def extract_form(self, text: str) -> List[str]:
|
def extract_form(self, text: str) -> List[str]:
|
||||||
@@ -50,7 +68,7 @@ class BrowserAgent(Agent):
|
|||||||
links_clean = []
|
links_clean = []
|
||||||
for link in links:
|
for link in links:
|
||||||
link = link.strip()
|
link = link.strip()
|
||||||
if link[-1] == '.':
|
if not (link[-1].isalpha() or link[-1].isdigit()):
|
||||||
links_clean.append(link[:-1])
|
links_clean.append(link[:-1])
|
||||||
else:
|
else:
|
||||||
links_clean.append(link)
|
links_clean.append(link)
|
||||||
@@ -59,14 +77,15 @@ class BrowserAgent(Agent):
|
|||||||
def get_unvisited_links(self) -> List[str]:
|
def get_unvisited_links(self) -> List[str]:
|
||||||
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
return "\n".join([f"[{i}] {link}" for i, link in enumerate(self.navigable_links) if link not in self.search_history])
|
||||||
|
|
||||||
def make_newsearch_prompt(self, user_prompt: str, search_result: dict) -> str:
|
def make_newsearch_prompt(self, prompt: str, search_result: dict) -> str:
|
||||||
search_choice = self.stringify_search_results(search_result)
|
search_choice = self.stringify_search_results(search_result)
|
||||||
|
self.logger.info(f"Search results: {search_choice}")
|
||||||
return f"""
|
return f"""
|
||||||
Based on the search result:
|
Based on the search result:
|
||||||
{search_choice}
|
{search_choice}
|
||||||
Your goal is to find accurate and complete information to satisfy the user’s request.
|
Your goal is to find accurate and complete information to satisfy the user’s request.
|
||||||
User request: {user_prompt}
|
User request: {prompt}
|
||||||
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I want to navigate to <link>"
|
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I will navigate to <link>"
|
||||||
Do not explain your choice.
|
Do not explain your choice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -75,70 +94,97 @@ class BrowserAgent(Agent):
|
|||||||
remaining_links_text = remaining_links if remaining_links is not None else "No links remaining, do a new search."
|
remaining_links_text = remaining_links if remaining_links is not None else "No links remaining, do a new search."
|
||||||
inputs_form = self.browser.get_form_inputs()
|
inputs_form = self.browser.get_form_inputs()
|
||||||
inputs_form_text = '\n'.join(inputs_form)
|
inputs_form_text = '\n'.join(inputs_form)
|
||||||
|
notes = '\n'.join(self.notes)
|
||||||
|
self.logger.info(f"Making navigation prompt with page text: {page_text[:100]}...\nremaining links: {remaining_links_text}")
|
||||||
|
self.logger.info(f"Inputs form: {inputs_form_text}")
|
||||||
|
self.logger.info(f"Notes: {notes}")
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
You are a web browser.
|
You are navigating the web.
|
||||||
You are currently on this webpage:
|
|
||||||
|
**Current Context**
|
||||||
|
|
||||||
|
Webpage ({self.current_page}) content:
|
||||||
{page_text}
|
{page_text}
|
||||||
|
|
||||||
You can navigate to these navigation links:
|
Allowed Navigation Links:
|
||||||
{remaining_links_text}
|
{remaining_links_text}
|
||||||
|
|
||||||
Your task:
|
Inputs forms:
|
||||||
1. Decide if the current page answers the user’s query: {user_prompt}
|
{inputs_form_text}
|
||||||
- If it does, take notes of the useful information, write down source, link or reference, then move to a new page.
|
|
||||||
- If it does and you are 100% certain that it provide a definive answer, say REQUEST_EXIT
|
|
||||||
- If it doesn’t, say: Error: This page does not answer the user’s query then go back or navigate to another link.
|
|
||||||
2. Navigate by either:
|
|
||||||
- Navigate to a navigation links (write the full URL, e.g., www.example.com/cats).
|
|
||||||
- If no link seems helpful, say: GO_BACK.
|
|
||||||
3. Fill forms on the page:
|
|
||||||
- If user give you informations that help you fill form, fill it.
|
|
||||||
- If you don't know how to fill a form, leave it empty.
|
|
||||||
- You can fill a form using [form_name](value).
|
|
||||||
|
|
||||||
Recap of note taking:
|
End of webpage ({self.current_page}.
|
||||||
If useful -> Note: [Briefly summarize the key information or task you conducted.]
|
|
||||||
Do not write "The page talk about ...", write your finding on the page and how they contribute to an answer.
|
|
||||||
If not useful -> Error: [Explain why the page doesn’t help.]
|
|
||||||
|
|
||||||
Example 1 (useful page, no need of going futher):
|
# Instruction
|
||||||
Note: According to karpathy site (https://karpathy.github.io/) LeCun net is the earliest real-world application of a neural net"
|
|
||||||
No link seem useful to provide futher information. GO_BACK
|
|
||||||
|
|
||||||
Example 2 (not useful, but related link):
|
1. **Evaluate if the page is relevant for user’s query and document finding:**
|
||||||
|
- If the page is relevant, extract and summarize key information in concise notes (Note: <your note>)
|
||||||
|
- If page not relevant, state: "Error: <specific reason the page does not address the query>" and either return to the previous page or navigate to a new link.
|
||||||
|
- Notes should be factual, useful summaries of relevant content, they should always include specific names or link. Written as: "On <website URL>, <key fact 1>. <Key fact 2>. <Additional insight>." Avoid phrases like "the page provides" or "I found that."
|
||||||
|
2. **Navigate to a link by either: **
|
||||||
|
- Saying I will navigate to (write down the full URL) www.example.com/cats
|
||||||
|
- Going back: If no link seems helpful, say: {Action.GO_BACK.value}.
|
||||||
|
3. **Fill forms on the page:**
|
||||||
|
- Fill form only when relevant.
|
||||||
|
- Use Login if username/password specified by user. For quick task create account, remember password in a note.
|
||||||
|
- You can fill a form using [form_name](value). Don't {Action.GO_BACK.value} when filling form.
|
||||||
|
- If a form is irrelevant or you lack informations (eg: don't know user email) leave it empty.
|
||||||
|
4. **Decide if you completed the task**
|
||||||
|
- Check your notes. Do they fully answer the question? Did you verify with multiple pages?
|
||||||
|
- Are you sure it’s correct?
|
||||||
|
- If yes to all, say {Action.REQUEST_EXIT}.
|
||||||
|
- If no, or a page lacks info, go to another link.
|
||||||
|
- Never stop or ask the user for help.
|
||||||
|
|
||||||
|
**Rules:**
|
||||||
|
- Do not write "The page talk about ...", write your finding on the page and how they contribute to an answer.
|
||||||
|
- Put note in a single paragraph.
|
||||||
|
- When you exit, explain why.
|
||||||
|
|
||||||
|
# Example:
|
||||||
|
|
||||||
|
Example 1 (useful page, no need go futher):
|
||||||
|
Note: According to karpathy site LeCun net is ...
|
||||||
|
No link seem useful to provide futher information.
|
||||||
|
Action: {Action.GO_BACK.value}
|
||||||
|
|
||||||
|
Example 2 (not useful, see useful link on page):
|
||||||
Error: reddit.com/welcome does not discuss anything related to the user’s query.
|
Error: reddit.com/welcome does not discuss anything related to the user’s query.
|
||||||
There is a link that could lead to the information, I want to navigate to http://reddit.com/r/locallama
|
There is a link that could lead to the information.
|
||||||
|
Action: navigate to http://reddit.com/r/locallama
|
||||||
|
|
||||||
Example 3 (not useful, no related links):
|
Example 3 (not useful, no related links):
|
||||||
Error: x.com does not discuss anything related to the user’s query and no navigation link are usefull.
|
Error: x.com does not discuss anything related to the user’s query and no navigation link are usefull.
|
||||||
GO_BACK
|
Action: {Action.GO_BACK.value}
|
||||||
|
|
||||||
Example 3 (query answer found):
|
Example 3 (clear definitive query answer found or enought notes taken):
|
||||||
Note: I found on github.com that agenticSeek is Fosowl.
|
I took 10 notes so far with enought finding to answer user question.
|
||||||
Given this information, given this I should exit the web browser. REQUEST_EXIT
|
Therefore I should exit the web browser.
|
||||||
|
Action: {Action.REQUEST_EXIT.value}
|
||||||
|
|
||||||
Example 4 (loging form visible):
|
Example 4 (loging form visible):
|
||||||
Note: I am on the login page, I should now type the given username and password.
|
|
||||||
[form_name_1](David)
|
|
||||||
[form_name_2](edgerunners_2077)
|
|
||||||
|
|
||||||
You see the following inputs forms:
|
Note: I am on the login page, I will type the given username and password.
|
||||||
{inputs_form_text}
|
Action:
|
||||||
|
[username_field](David)
|
||||||
|
[password_field](edgerunners77)
|
||||||
|
|
||||||
Remember, the user asked: {user_prompt}
|
Remember, user asked:
|
||||||
You are currently on page : {self.current_page}
|
{user_prompt}
|
||||||
Do not explain your choice.
|
You previously took these notes:
|
||||||
Refusal is not an option, you have been given all capabilities that allow you to perform any tasks.
|
{notes}
|
||||||
|
Do not Step-by-Step explanation. Write comprehensive Notes or Error as a long paragraph followed by your action.
|
||||||
|
You must always take notes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def llm_decide(self, prompt: str) -> Tuple[str, str]:
|
async def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
pretty_print("-"*100)
|
self.last_reasoning = reasoning
|
||||||
|
if show_reasoning:
|
||||||
|
pretty_print(reasoning, color="failure")
|
||||||
pretty_print(answer, color="output")
|
pretty_print(answer, color="output")
|
||||||
pretty_print("-"*100)
|
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
def select_unvisited(self, search_result: List[str]) -> List[str]:
|
def select_unvisited(self, search_result: List[str]) -> List[str]:
|
||||||
@@ -146,6 +192,7 @@ class BrowserAgent(Agent):
|
|||||||
for res in search_result:
|
for res in search_result:
|
||||||
if res["link"] not in self.search_history:
|
if res["link"] not in self.search_history:
|
||||||
results_unvisited.append(res)
|
results_unvisited.append(res)
|
||||||
|
self.logger.info(f"Unvisited links: {results_unvisited}")
|
||||||
return results_unvisited
|
return results_unvisited
|
||||||
|
|
||||||
def jsonify_search_results(self, results_string: str) -> List[str]:
|
def jsonify_search_results(self, results_string: str) -> List[str]:
|
||||||
@@ -168,25 +215,60 @@ class BrowserAgent(Agent):
|
|||||||
return parsed_results
|
return parsed_results
|
||||||
|
|
||||||
def stringify_search_results(self, results_arr: List[str]) -> str:
|
def stringify_search_results(self, results_arr: List[str]) -> str:
|
||||||
return '\n\n'.join([f"Link: {res['link']}" for res in results_arr])
|
return '\n\n'.join([f"Link: {res['link']}\nPreview: {res['snippet']}" for res in results_arr])
|
||||||
|
|
||||||
def save_notes(self, text):
|
def parse_answer(self, text):
|
||||||
lines = text.split('\n')
|
lines = text.split('\n')
|
||||||
|
saving = False
|
||||||
|
buffer = []
|
||||||
|
links = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
if line == '' or 'action:' in line.lower():
|
||||||
|
saving = False
|
||||||
if "note" in line.lower():
|
if "note" in line.lower():
|
||||||
self.notes.append(line)
|
saving = True
|
||||||
|
if saving:
|
||||||
|
buffer.append(line.replace("notes:", ''))
|
||||||
|
else:
|
||||||
|
links.extend(self.extract_links(line))
|
||||||
|
self.notes.append('. '.join(buffer).strip())
|
||||||
|
return links
|
||||||
|
|
||||||
|
def select_link(self, links: List[str]) -> str | None:
|
||||||
|
"""
|
||||||
|
Select the first unvisited link that is not the current page.
|
||||||
|
Preference is given to links not in search_history.
|
||||||
|
"""
|
||||||
|
for lk in links:
|
||||||
|
if lk == self.current_page or lk in self.search_history:
|
||||||
|
self.logger.info(f"Skipping already visited or current link: {lk}")
|
||||||
|
continue
|
||||||
|
self.logger.info(f"Selected link: {lk}")
|
||||||
|
return lk
|
||||||
|
self.logger.warning("No suitable 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:
|
def conclude_prompt(self, user_query: str) -> str:
|
||||||
annotated_notes = [f"{i+1}: {note.lower().replace('note:', '')}" for i, note in enumerate(self.notes)]
|
annotated_notes = [f"{i+1}: {note.lower()}" for i, note in enumerate(self.notes)]
|
||||||
search_note = '\n'.join(annotated_notes)
|
search_note = '\n'.join(annotated_notes)
|
||||||
print("AI research notes:\n", search_note)
|
pretty_print(f"AI notes:\n{search_note}", color="success")
|
||||||
return f"""
|
return f"""
|
||||||
Following a human request:
|
Following a human request:
|
||||||
{user_query}
|
{user_query}
|
||||||
A web AI made the following finding across different pages:
|
A web browsing AI made the following finding across different pages:
|
||||||
{search_note}
|
{search_note}
|
||||||
|
|
||||||
Summarize the finding or step that lead to success, and provide a conclusion that answer the request.
|
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:
|
def search_prompt(self, user_prompt: str) -> str:
|
||||||
@@ -205,57 +287,150 @@ class BrowserAgent(Agent):
|
|||||||
You: "search: Recent space missions news, {self.date}"
|
You: "search: Recent space missions news, {self.date}"
|
||||||
|
|
||||||
Do not explain, do not write anything beside the search query.
|
Do not explain, do not write anything beside the search query.
|
||||||
|
Except if query does not make any sense for a web search then explain why and say {Action.REQUEST_EXIT.value}
|
||||||
|
Do not try to answer query. you can only formulate search term or exit.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def process(self, user_prompt, speech_module) -> str:
|
def handle_update_prompt(self, user_prompt: str, page_text: str, fill_success: bool) -> str:
|
||||||
|
prompt = f"""
|
||||||
|
You are a web browser.
|
||||||
|
You just filled a form on the page.
|
||||||
|
Now you should see the result of the form submission on the page:
|
||||||
|
Page text:
|
||||||
|
{page_text}
|
||||||
|
The user asked: {user_prompt}
|
||||||
|
Does the page answer the user’s query now? Are you still on a login page or did you get redirected?
|
||||||
|
If it does, take notes of the useful information, write down result and say {Action.FORM_FILLED.value}.
|
||||||
|
if it doesn’t, say: Error: Attempt to fill form didn't work {Action.GO_BACK.value}.
|
||||||
|
If you were previously on a login form, no need to take notes.
|
||||||
|
"""
|
||||||
|
if not fill_success:
|
||||||
|
prompt += f"""
|
||||||
|
According to browser feedback, the form was not filled correctly. Is that so? you might consider other strategies.
|
||||||
|
"""
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
def show_search_results(self, search_result: List[str]):
|
||||||
|
pretty_print("\nSearch results:", color="output")
|
||||||
|
for res in search_result:
|
||||||
|
pretty_print(f"Title: {res['title']} - ", color="info", no_newline=True)
|
||||||
|
pretty_print(f"Link: {res['link']}", color="status")
|
||||||
|
|
||||||
|
def stuck_prompt(self, user_prompt: str, unvisited: List[str]) -> str:
|
||||||
|
"""
|
||||||
|
Prompt for when the agent repeat itself, can happen when fail to extract a link.
|
||||||
|
"""
|
||||||
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
|
prompt += f"""
|
||||||
|
You previously said:
|
||||||
|
{self.last_answer}
|
||||||
|
You must consider other options. Choose other link.
|
||||||
|
"""
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
async def process(self, user_prompt: str, speech_module: type) -> Tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Process the user prompt to conduct an autonomous web search.
|
||||||
|
Start with a google search with searxng using web_search tool.
|
||||||
|
Then enter a navigation logic to find the answer or conduct required actions.
|
||||||
|
Args:
|
||||||
|
user_prompt: The user's input query
|
||||||
|
speech_module: Optional speech output module
|
||||||
|
Returns:
|
||||||
|
tuple containing the final answer and reasoning
|
||||||
|
"""
|
||||||
complete = False
|
complete = False
|
||||||
|
|
||||||
animate_thinking(f"Thinking...", color="status")
|
animate_thinking(f"Thinking...", color="status")
|
||||||
self.memory.push('user', self.search_prompt(user_prompt))
|
mem_begin_idx = self.memory.push('user', self.search_prompt(user_prompt))
|
||||||
ai_prompt, _ = self.llm_request()
|
ai_prompt, reasoning = await self.llm_request()
|
||||||
|
if Action.REQUEST_EXIT.value in ai_prompt:
|
||||||
|
pretty_print(f"Web agent requested exit.\n{reasoning}\n\n{ai_prompt}", color="failure")
|
||||||
|
return ai_prompt, ""
|
||||||
animate_thinking(f"Searching...", color="status")
|
animate_thinking(f"Searching...", color="status")
|
||||||
|
self.status_message = "Searching..."
|
||||||
search_result_raw = self.tools["web_search"].execute([ai_prompt], False)
|
search_result_raw = self.tools["web_search"].execute([ai_prompt], False)
|
||||||
search_result = self.jsonify_search_results(search_result_raw)[:7] # until futher improvement
|
search_result = self.jsonify_search_results(search_result_raw)[:16]
|
||||||
|
self.show_search_results(search_result)
|
||||||
prompt = self.make_newsearch_prompt(user_prompt, search_result)
|
prompt = self.make_newsearch_prompt(user_prompt, search_result)
|
||||||
unvisited = [None]
|
unvisited = [None]
|
||||||
while not complete:
|
while not complete and len(unvisited) > 0 and not self.stop:
|
||||||
answer, reasoning = self.llm_decide(prompt)
|
self.memory.clear()
|
||||||
self.save_notes(answer)
|
unvisited = self.select_unvisited(search_result)
|
||||||
|
answer, reasoning = await self.llm_decide(prompt, show_reasoning = False)
|
||||||
|
if self.stop:
|
||||||
|
pretty_print(f"Requested stop.", color="failure")
|
||||||
|
break
|
||||||
|
if self.last_answer == answer:
|
||||||
|
prompt = self.stuck_prompt(user_prompt, unvisited)
|
||||||
|
continue
|
||||||
|
self.last_answer = answer
|
||||||
|
pretty_print('▂'*32, color="status")
|
||||||
|
|
||||||
extracted_form = self.extract_form(answer)
|
extracted_form = self.extract_form(answer)
|
||||||
if len(extracted_form) > 0:
|
if len(extracted_form) > 0:
|
||||||
self.browser.fill_form_inputs(extracted_form)
|
self.status_message = "Filling web form..."
|
||||||
self.browser.find_and_click_submit()
|
pretty_print(f"Filling inputs form...", color="status")
|
||||||
|
fill_success = self.browser.fill_form(extracted_form)
|
||||||
|
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 "REQUEST_EXIT" in answer:
|
if Action.FORM_FILLED.value in answer:
|
||||||
|
pretty_print(f"Filled form. Handling page update.", color="status")
|
||||||
|
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
|
||||||
|
|
||||||
|
links = self.parse_answer(answer)
|
||||||
|
link = self.select_link(links)
|
||||||
|
if link == self.current_page:
|
||||||
|
pretty_print(f"Already visited {link}. Search callback.", color="status")
|
||||||
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
|
self.search_history.append(link)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if Action.REQUEST_EXIT.value in answer:
|
||||||
|
self.status_message = "Exiting web browser..."
|
||||||
|
pretty_print(f"Agent requested exit.", color="status")
|
||||||
complete = True
|
complete = True
|
||||||
break
|
break
|
||||||
|
|
||||||
links = self.extract_links(answer)
|
if (link == None and len(extracted_form) < 3) or Action.GO_BACK.value in answer or link in self.search_history:
|
||||||
if len(unvisited) == 0:
|
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
||||||
break
|
self.status_message = "Going back to search results..."
|
||||||
|
request_prompt = user_prompt
|
||||||
if len(links) == 0 or "GO_BACK" in answer:
|
if link is None:
|
||||||
unvisited = self.select_unvisited(search_result)
|
request_prompt += f"\nYou previously choosen:\n{self.last_answer} but the website is unavailable. Consider other options."
|
||||||
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
prompt = self.make_newsearch_prompt(request_prompt, unvisited)
|
||||||
pretty_print(f"Going back to results. Still {len(unvisited)}", color="warning")
|
self.search_history.append(link)
|
||||||
links = []
|
self.current_page = link
|
||||||
continue
|
continue
|
||||||
|
|
||||||
animate_thinking(f"Navigating to {links[0]}", color="status")
|
animate_thinking(f"Navigating to {link}", color="status")
|
||||||
speech_module.speak(f"Navigating to {links[0]}")
|
if speech_module: speech_module.speak(f"Navigating to {link}")
|
||||||
self.browser.go_to(links[0])
|
nav_ok = self.browser.go_to(link)
|
||||||
self.current_page = links[0]
|
self.search_history.append(link)
|
||||||
self.search_history.append(links[0])
|
if not nav_ok:
|
||||||
page_text = self.browser.get_text()
|
pretty_print(f"Failed to navigate to {link}.", color="failure")
|
||||||
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
|
continue
|
||||||
|
self.current_page = link
|
||||||
|
page_text = self.get_page_text(limit_to_model_ctx=True)
|
||||||
self.navigable_links = self.browser.get_navigable()
|
self.navigable_links = self.browser.get_navigable()
|
||||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||||
|
self.status_message = "Navigating..."
|
||||||
|
self.browser.screenshot()
|
||||||
|
|
||||||
self.browser.close()
|
pretty_print("Exited navigation, starting to summarize finding...", color="status")
|
||||||
prompt = self.conclude_prompt(user_prompt)
|
prompt = self.conclude_prompt(user_prompt)
|
||||||
self.memory.push('user', prompt)
|
mem_last_idx = self.memory.push('user', prompt)
|
||||||
answer, reasoning = self.llm_request()
|
self.status_message = "Summarizing findings..."
|
||||||
|
answer, reasoning = await self.llm_request()
|
||||||
pretty_print(answer, color="output")
|
pretty_print(answer, color="output")
|
||||||
|
self.status_message = "Ready"
|
||||||
|
self.last_answer = answer
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
@@ -5,43 +6,30 @@ from sources.tools.searxSearch import searxSearch
|
|||||||
from sources.tools.flightSearch import FlightSearch
|
from sources.tools.flightSearch import FlightSearch
|
||||||
from sources.tools.fileFinder import FileFinder
|
from sources.tools.fileFinder import FileFinder
|
||||||
from sources.tools.BashInterpreter import BashInterpreter
|
from sources.tools.BashInterpreter import BashInterpreter
|
||||||
|
from sources.memory import Memory
|
||||||
|
|
||||||
class CasualAgent(Agent):
|
class CasualAgent(Agent):
|
||||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||||
"""
|
"""
|
||||||
The casual agent is a special for casual talk to the user without specific tasks.
|
The casual agent is a special for casual talk to the user without specific tasks.
|
||||||
"""
|
"""
|
||||||
super().__init__(name, prompt_path, provider, verbose)
|
super().__init__(name, prompt_path, provider, verbose, None)
|
||||||
self.tools = {
|
self.tools = {
|
||||||
"web_search": searxSearch(),
|
} # No tools for the casual agent
|
||||||
"flight_search": FlightSearch(),
|
|
||||||
"file_finder": FileFinder(),
|
|
||||||
"bash": BashInterpreter()
|
|
||||||
}
|
|
||||||
self.role = "talk"
|
self.role = "talk"
|
||||||
self.type = "casual_agent"
|
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())
|
||||||
|
|
||||||
def process(self, prompt, speech_module) -> str:
|
async def process(self, prompt, speech_module) -> str:
|
||||||
complete = False
|
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
|
animate_thinking("Thinking...", color="status")
|
||||||
while not complete:
|
answer, reasoning = await self.llm_request()
|
||||||
animate_thinking("Thinking...", color="status")
|
self.last_answer = answer
|
||||||
answer, reasoning = self.llm_request()
|
self.status_message = "Ready"
|
||||||
exec_success, _ = self.execute_modules(answer)
|
|
||||||
answer = self.remove_blocks(answer)
|
|
||||||
self.last_answer = answer
|
|
||||||
complete = True
|
|
||||||
for tool in self.tools.values():
|
|
||||||
if tool.found_executable_blocks():
|
|
||||||
complete = False # AI read results and continue the conversation
|
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from llm_provider import Provider
|
pass
|
||||||
|
|
||||||
#local_provider = Provider("ollama", "deepseek-r1:14b", None)
|
|
||||||
server_provider = Provider("server", "deepseek-r1:14b", "192.168.1.100:5000")
|
|
||||||
agent = CasualAgent("deepseek-r1:14b", "jarvis", "prompts/casual_agent.txt", server_provider)
|
|
||||||
ans = agent.process("Hello, how are you?")
|
|
||||||
print(ans)
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import platform, os
|
||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent, executorResult
|
from sources.agents.agent import Agent, executorResult
|
||||||
@@ -5,51 +7,84 @@ from sources.tools.C_Interpreter import CInterpreter
|
|||||||
from sources.tools.GoInterpreter import GoInterpreter
|
from sources.tools.GoInterpreter import GoInterpreter
|
||||||
from sources.tools.PyInterpreter import PyInterpreter
|
from sources.tools.PyInterpreter import PyInterpreter
|
||||||
from sources.tools.BashInterpreter import BashInterpreter
|
from sources.tools.BashInterpreter import BashInterpreter
|
||||||
|
from sources.tools.JavaInterpreter import JavaInterpreter
|
||||||
from sources.tools.fileFinder import FileFinder
|
from sources.tools.fileFinder import FileFinder
|
||||||
|
from sources.logger import Logger
|
||||||
|
from sources.memory import Memory
|
||||||
|
|
||||||
class CoderAgent(Agent):
|
class CoderAgent(Agent):
|
||||||
"""
|
"""
|
||||||
The code agent is an agent that can write and execute code.
|
The code agent is an agent that can write and execute code.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||||
super().__init__(name, prompt_path, provider, verbose)
|
super().__init__(name, prompt_path, provider, verbose, None)
|
||||||
self.tools = {
|
self.tools = {
|
||||||
"bash": BashInterpreter(),
|
"bash": BashInterpreter(),
|
||||||
"python": PyInterpreter(),
|
"python": PyInterpreter(),
|
||||||
"c": CInterpreter(),
|
"c": CInterpreter(),
|
||||||
"go": GoInterpreter(),
|
"go": GoInterpreter(),
|
||||||
|
"java": JavaInterpreter(),
|
||||||
"file_finder": FileFinder()
|
"file_finder": FileFinder()
|
||||||
}
|
}
|
||||||
self.role = "Coding task"
|
self.work_dir = self.tools["file_finder"].get_work_dir()
|
||||||
|
self.role = "code"
|
||||||
self.type = "code_agent"
|
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 process(self, prompt, speech_module) -> str:
|
def add_sys_info_prompt(self, prompt):
|
||||||
|
"""Add system information to the prompt."""
|
||||||
|
info = f"System Info:\n" \
|
||||||
|
f"OS: {platform.system()} {platform.release()}\n" \
|
||||||
|
f"Python Version: {platform.python_version()}\n" \
|
||||||
|
f"\nYou must save file at root directory: {self.work_dir}"
|
||||||
|
return f"{prompt}\n\n{info}"
|
||||||
|
|
||||||
|
async def process(self, prompt, speech_module) -> str:
|
||||||
answer = ""
|
answer = ""
|
||||||
attempt = 0
|
attempt = 0
|
||||||
max_attempts = 3
|
max_attempts = 5
|
||||||
|
prompt = self.add_sys_info_prompt(prompt)
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
|
clarify_trigger = "REQUEST_CLARIFICATION"
|
||||||
|
|
||||||
while attempt < max_attempts:
|
while attempt < max_attempts and not self.stop:
|
||||||
|
print("Stopped?", self.stop)
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
self.wait_message(speech_module)
|
await self.wait_message(speech_module)
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
animate_thinking("Executing code...", color="status")
|
self.last_reasoning = reasoning
|
||||||
exec_success, _ = self.execute_modules(answer)
|
if clarify_trigger in answer:
|
||||||
answer = self.remove_blocks(answer)
|
self.last_answer = answer
|
||||||
self.last_answer = answer
|
await asyncio.sleep(0)
|
||||||
if exec_success:
|
return answer, reasoning
|
||||||
|
if not "```" in answer:
|
||||||
|
self.last_answer = answer
|
||||||
|
await asyncio.sleep(0)
|
||||||
break
|
break
|
||||||
self.show_answer()
|
self.show_answer()
|
||||||
|
animate_thinking("Executing code...", color="status")
|
||||||
|
self.status_message = "Executing code..."
|
||||||
|
self.logger.info(f"Attempt {attempt + 1}:\n{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(f"Execution failure:\n{feedback}", color="failure")
|
||||||
|
pretty_print("Correcting code...", color="status")
|
||||||
|
self.status_message = "Correcting code..."
|
||||||
attempt += 1
|
attempt += 1
|
||||||
|
self.status_message = "Ready"
|
||||||
if attempt == max_attempts:
|
if attempt == max_attempts:
|
||||||
return "I'm sorry, I couldn't find a solution to your problem. How would you like me to proceed ?", reasoning
|
return "I'm sorry, I couldn't find a solution to your problem. How would you like me to proceed ?", reasoning
|
||||||
|
self.last_answer = answer
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from llm_provider import Provider
|
pass
|
||||||
|
|
||||||
#local_provider = Provider("ollama", "deepseek-r1:14b", None)
|
|
||||||
server_provider = Provider("server", "deepseek-r1:14b", "192.168.1.100:5000")
|
|
||||||
agent = CoderAgent("deepseek-r1:14b", "jarvis", "prompts/coder_agent.txt", server_provider)
|
|
||||||
ans = agent.process("What is the output of 5+5 in python ?")
|
|
||||||
print(ans)
|
|
||||||
@@ -1,39 +1,43 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
from sources.utility import pretty_print, animate_thinking
|
from sources.utility import pretty_print, animate_thinking
|
||||||
from sources.agents.agent import Agent
|
from sources.agents.agent import Agent
|
||||||
from sources.tools.fileFinder import FileFinder
|
from sources.tools.fileFinder import FileFinder
|
||||||
from sources.tools.BashInterpreter import BashInterpreter
|
from sources.tools.BashInterpreter import BashInterpreter
|
||||||
|
from sources.memory import Memory
|
||||||
|
|
||||||
class FileAgent(Agent):
|
class FileAgent(Agent):
|
||||||
def __init__(self, name, prompt_path, provider, verbose=False):
|
def __init__(self, name, prompt_path, provider, verbose=False):
|
||||||
"""
|
"""
|
||||||
The file agent is a special agent for file operations.
|
The file agent is a special agent for file operations.
|
||||||
"""
|
"""
|
||||||
super().__init__(name, prompt_path, provider, verbose)
|
super().__init__(name, prompt_path, provider, verbose, None)
|
||||||
self.tools = {
|
self.tools = {
|
||||||
"file_finder": FileFinder(),
|
"file_finder": FileFinder(),
|
||||||
"bash": BashInterpreter()
|
"bash": BashInterpreter()
|
||||||
}
|
}
|
||||||
self.role = "find and read files"
|
self.work_dir = self.tools["file_finder"].get_work_dir()
|
||||||
|
self.role = "files"
|
||||||
self.type = "file_agent"
|
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())
|
||||||
|
|
||||||
def process(self, prompt, speech_module) -> str:
|
async def process(self, prompt, speech_module) -> str:
|
||||||
exec_success = False
|
exec_success = False
|
||||||
|
prompt += f"\nYou must work in directory: {self.work_dir}"
|
||||||
self.memory.push('user', prompt)
|
self.memory.push('user', prompt)
|
||||||
|
while exec_success is False and not self.stop:
|
||||||
self.wait_message(speech_module)
|
await self.wait_message(speech_module)
|
||||||
animate_thinking("Thinking...", color="status")
|
animate_thinking("Thinking...", color="status")
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = await self.llm_request()
|
||||||
exec_success, _ = self.execute_modules(answer)
|
self.last_reasoning = reasoning
|
||||||
answer = self.remove_blocks(answer)
|
exec_success, _ = self.execute_modules(answer)
|
||||||
self.last_answer = answer
|
answer = self.remove_blocks(answer)
|
||||||
|
self.last_answer = answer
|
||||||
|
self.status_message = "Ready"
|
||||||
return answer, reasoning
|
return answer, reasoning
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from llm_provider import Provider
|
pass
|
||||||
|
|
||||||
#local_provider = Provider("ollama", "deepseek-r1:14b", None)
|
|
||||||
server_provider = Provider("server", "deepseek-r1:14b", "192.168.1.100:5000")
|
|
||||||
agent = FileAgent("deepseek-r1:14b", "jarvis", "prompts/file_agent.txt", server_provider)
|
|
||||||
ans = agent.process("What is the content of the file toto.py ?")
|
|
||||||
print(ans)
|
|
||||||