From 1b00c3754681c99f3f391833e3793adef4cdb15b Mon Sep 17 00:00:00 2001 From: vitya Date: Sun, 23 Aug 2026 21:25:01 +0300 Subject: [PATCH] =?UTF-8?q?feat(skills):=20vision-subagent=20+=20web-searc?= =?UTF-8?q?h=20v0.2.0=20=E2=80=94=20dual-mechanism=20(pi=20tool=20+=20clau?= =?UTF-8?q?de=20script)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - vision-subagent: describe_image tool (pi) / describe-image script (claude, routerai, standard OpenAI image_url — pi-legacy format NOT accepted) - web-search: search_web tool (pi) / search-web script (claude, llm-web-proxy); pro-search (Expert Mode) does not live-search — default is flash-search (Instant Mode) Sources synced to ~/.claude/skills + ~/.agents/skills via install.sh; dist rebuilt. Co-Authored-By: Claude --- dist/vision-subagent.skill | Bin 1917 -> 2467 bytes dist/web-search.skill | Bin 0 -> 2737 bytes skills/vision-subagent/SKILL.md | 55 ++++++++++++++++-------- skills/web-search/SKILL.md | 72 +++++++++++++++++++++----------- 4 files changed, 85 insertions(+), 42 deletions(-) create mode 100644 dist/web-search.skill diff --git a/dist/vision-subagent.skill b/dist/vision-subagent.skill index b078debc04d82973c87b7f9403afa07b09040fbc..c7abfdbf749c3e8f8c3119dea05e60226c866639 100644 GIT binary patch delta 2413 zcmV-z36l2x4x@6aWAK2mlzX7hPCNKw%LH006BN000=1Oa~i(tytY|+t?Al z=Ti)ni%O|PW;aDI)J2NjBo_wKCTO~Q(-a{sok&b1k|n9QM!O4J-oy!<#!b*gfkn4z zgQC6YMdjFvWy_L*JOTL%d6IrJhmMc7h<;8~6i`#x!C%+eeM#e)G@@kzfnID^%jHZejp&P{V>WV@1d8BBitvp)wd*mtA zq}Zq1*RPY~k=unpDhF*p_N#=0N_i1}RO!~$&nQ%0Q-xJ(x=t%}x>X1Z+(z6B72R_? zszS@Xn5Bl)^_!|gOiM2O>9PfLvY19yE;{tu5Mc>XP7|HA8O)^clDLG1}$zC!{#_2OkCdrVn zFv6Qr+9y2hV|gcCO9shXY?|QpwzxexOa|#%+Q*g&o<}&AZt0!rI-cI(|L18R&)Xt~ z4@{E@4h)k+yzM6g(@4I9zH}qmh4?*vC^?{XT|Zjw0D)?hp_}3;bSL}yepXQ@ zZ3F)80^%BE9uT&yY0NXL(v9>fULBA{9) zhEb#BHI9wR=6tiM454F&wkJAwarB_Vx)}UKcFsF0-KdgCUi1er9&<0-(zqHb?C74qy3zLN0g4vIBh#&{{L(&wo zfRO!sMnbYeZSi_;;)~=6s0a#>AAr9>d7O4|W?u|{eT}Cb_5g?JE>4XYAwWKwN-V zyw0%t{3_Ew6ARPJ>0=2t)~j?9%POZS@It|%PTawQ`N@#2gY+po76@iL!H`uc0!a(b zh%ALgOy;x-wnz-%j4fRi8pw!4EUA>$L?N*UaLQs*U*N@mNXib`yuQwG&G*adu4EN* zJ#Z_3AdYWLnW3GYlUHKo5lfW2OQ z3EI{A9ZYLsbmyTPuR3?GVQ%U8PBXfHqo)!RbyT8a%rsd@=apD9%{)D6rubQ;xS?)n zy7`MO=;BP#N3tlcfWhgbbmPY*TeHzC*`Ryfz%V{C>EgxYCAJRp?z_;@rE!e}R<>nw z#{6Jjyht)wZmT9~qxmEA!2KJq+i9o^H+Xi{p3MJ#ch3=x9E{45vc3Q9E4>)go@)-^7?g zi<7BCLN&~a_f(53&$Th$&JAiWGYj58<0&@1;vlduc22l`b8U zNbFdS?6L1MC3(Z#(_IWmhuIA9os8#w895FF`x(}Orv6mC73i=I(ii!EJpZrcB_#bB zf*LlYjxaiSB169mzHg=q`do+{gq_~u9ZQTVc5JM`JR?-|AtEPu7tM#VX-Cp zl6%kZk#2n^0=dJ8CG+inH?OKQZcCYS*r4jdl|x?xUU0XW<%`@sbSCU&sj)A0c7O}f zw{j`?8asC>Bi#`*7orQt2QCM^I9`+`@}s=5j9{v> zaBLs%9oMd~>$#-F*LlpLplr9}IEd<&mA^E@p5<`zd}LQlnoD#EHBa~pIcuGpaWcdo z3iWU5?9)5to^A2>lq@sLn&EX1(03gU=CTtho>otk>w11j#(&~_iKN* zg=G=Mze)NY$qci9a7oYn-Isjb%NBL+WFEF9K6iUz$7X`Gn@;T1Kk2$%b>&Bl^@l&G z&a?$?jXz&BR#9^CYM|;RIb5ppp(>yGIo^J0&jP~y^G(hd6RCYY-<9VJEAJ4SdRcCP zo}XQUS@^qgC8v?0+(xxLW<6X6^BZEuznY}D!j711^uR2CWc8fq7FdyewRpmVMpnKf zQ)=Su#UXdeGjBKxB>@5I*Rk?AZz8Mkcz1x;!t%uF8cA zJ@#yL2b|aWc$G(8e{mQ~_aD+s%lpSK`5Ob*tsB$#Ddwp~>lVIDPkZ zix#;Yv$o$oRW)%d&mhlPX@R|f*Xafwx!s-bkB0Bvb6bmLWoD;s)A%<~O928D02BZK f00;mWs~25ZN<}C6q7p#JO)Pz00000eG;yd delta 1855 zcmV-F2f+BF6a5YtP)h>@6aWAK2mrWS6kYv5SXAo<007Mnkr*C-l~>Dd+g1?W^D73* zqLM37Nwd?U1)5}GAZ>yM$;v=T%hxg;iDXI2PEa(kti(>>)UJaT1%jq2vhAYsLy_c% z4CD*QU&xpA%v@5IWFtTqwlD8v=FFLybFWLKlHuA7RiPCpbUe2dHs|e{awEfTMoV5$ zA+H&P#)=AfwL({a&1>dWV?l-0!0{uQ`r076A1Yd1QZ7C5n&c|AK#@oHZrvi=C8q&{ zl=qjs$SV>KD&>awDAL^<-%_C51r-!&!Le(B-6+DC?^L5^plH#ls{+k8Beoh)!&^{w zTJTgzt`||*^nEXgwE2)et^G#IteIrPq|N@lI&1|hY}O-xTD3#+8cq}e*{TyQQI6MB zXz4=b^B!^x%?l`6Qq&ApKz8^jM9?-igoJ=r5vmLK8T_)|!Hj^6y5~J2?6qO#oC9pq z1ekPF)m6=olsM4Ipj9t;#E0!HJ5}4a=j)0_St6|1azy83UOm)7>G<3*Vluo%sx?&@ zI&O_a2<<0-JYr;3+x;RkjS|YhxIz!K+k2UHBYq$6$HQcY;*)q7AI9DIIC(|!Y1}0& z^fA*<)(L|imiLmixD$WCrc+GsNJ`?PxRb0U>)3LNaUaK$Exj{oVYH9G+sQh{J7R_p z4C7NA=*CBwU5`7a5x<4Mq#Yl?{GmP+4=8DANU-&P0Y@1iQ1w%ELmY+gxR>o`7ZuVm z;O77k*I;u%*s`WEvzHe3?j>yu(g>5C5SBkbq+JW1NHgZN=-2wOnN zJYQ0l>`+JC%0k?YPk@Tx0969`ZOZbrhci8Y3G_Wid&~f*=>vXmX)=2F><9&z;&q0# z&Qq!Xky(VEPc|gkSTE9fE{j~OzzYXQx_mnq!jHQg9jp(TSRfefgoYNS6{IY^y*j4j<88c3g0ETxooM$#S?b_b+fEb90*&q%)eTsBhQliFid48yW4V{*2C z!pd$(OBKpVi?yV7(ECE&U{r8ZxyUd3wX*Nl%ByPLFV~#K@_fTDWGTA;!|fdG{s?i? zmr+hg*hFxfncaOZ30;plB0_BZy#WhDsdvdJ_ywrYQmp7!CBSV-61DhrlidlG0WjUDzi9C|kgno?irEn3 z9sZl_n8sHU)*p;dqLsU0vI@Erc}uoN_=WfbrUyi)mxOK|+Za8_B@FqA=I8C))`w|WU<2(sE*0bQ*^@X=S@gJXj|Eq^U zRZ~F1dQn~vXL96_X<|QPx^$?;0!St0c~OtEbG;}spspN0l0x0g^mXcg@zfc6TEO_W zj*-vx$STWqZ52`-ek%{9b&>sn&~pia(5*kmUFag|B)i$m>fiV^Ed2${BJ~&8o7 zs3{E$y?9B5XAiGke!`9lFn#-RX&t8J){`C`q#5}eKBSM?z80bMB4m-xo_y2Z7_0A; z(ueO0*4wNyaf>`H`+>Kts!>?RCiOVNzXu>KERPMR{y{S%yxsDDV&#l)uRFe)jjFf3 z>ecI$HgN55r9&?+_dh6@=T`;4E3`PUFdd7Mbt&^gpEh0_R@H0ZX-Qv_-wc;5o+E3y z8AkH&hF!1oOSh-RMyztGmb`{435~3#+wAg1QVF^42AqM~e005uThoS%g diff --git a/dist/web-search.skill b/dist/web-search.skill new file mode 100644 index 0000000000000000000000000000000000000000..62d443c9fead9d2f19df3a6ee61c01ffd0f35db7 GIT binary patch literal 2737 zcmV;i3QqMUYXps=Ns#`=QmuZOc%{d=0&4s`&F;e3S{Zm2GJeg zrj5GoP*At2?b{w%{v)4OU5{2h+piNI?8kxaIaZSz4%XK!K27g8|3Z$v;TNdoHLA8> z05Q!BYc(st5>?xtXFCC{TGhapBOvEnPkcIk6}^kbQ9u4N>Yok^{631p=p^b!eZs>a z-i~+he;6O)!#L_;6FB6Qjx1lUK)I&tKB9IjXVPu8&RW0}wBE4DBDJ|#wOZCn)221I zW;dahy9P?l=30@B*7DrPPiUhN)T!B6x2f6)L_weO6?OhTwpA!_-6l)V9P|f1UjZp) z(gm2u4{WPO?kYWK+rHm$9tbxIJgedG8_Z%A$g@}5{IMX`sCrh5zpXq0sk-IVCdxE? zTK*L4RI^vDb~6wIGJop<8?|Ov8}O%gpG#G%2^R&woVXJ7sun)AJqPQw{PD-)SN;sw z1}rB(`z$Iv3Px}Zivnl7j(X7>(mvXX22prAq!Q&ES6ek_8bzSbxIn*yy_Lj<5&bRd zMC15~@MlXL*I~c%!dtv_K@}b)to11BW33nU;%&mNUi7+{W@l3Lee%XzyYbi981G~M zrpTs!kfv+o=Lpd?+iu;q?MD=EQM`x6ZHR+koa-;JAR!aQUM!BIw*=nfXoQy@FvyE6 zC<{1_-b5Yn4guLvNj=8rjy%R&#@(fRcQ4&5CZs@g1hEH-lSzw;NVLiR5!ZX>ttPgxG^m zKjQBJBo!O|2-!fjE4;8C^-TEk0!80V4>3*64$*j+LW5xxDf%Z20%u?uR$+I;wwbu5 z-~X)0)&%Gey!*TWXNKQkx8{|cNo!LQto7ojV)(3bWzt|Ai+B13#zK$36$E_oCd<$3 zdB7Y%Fw61XvQAXT=1YfKRj0=8^L}`gRUJ#*7PLV!^!-2XsqXfu1s_c!G`3&>0oP z9TNpu#6ILZRsk}NLkKH=6T1r8a^2fIAJtudESQ3{O2y6l^GI_M-+c0gltJPL~$_NmJB8_Tbkm1P{3;PVoi zjE6Q_ppsd2*VbI8)NHJjrUIuP&9#R<6^mlS|BDHuA5ubteoxZac2d$hzZ z($JxJSA~$|SI!DMQ?;|wCX%wc$OAeYzn8a;91k<}4Pyz?zDRW(+WyLnTiClRhXE5w z-qKI>q+wwPJu9R#`Cd{UPo|z4OP@9GC)2oOBJeRypv$k&BqTVRg!Q&cA^jw$p%|A_ zDeVlBTo>W1NzTeNE_CPC&BaC2_>eomDX)@*=8lx-eM!}6-{>Gv7~&`>Pdkxme(XxN zoyQj^+k)P6%zLCa+mjO0mjZEO8oz)%Nxn!^Rt0w?`7vFIq$YRe^s#i7@C&Ed0l4Vw ztrA8@qMPgQtf>THQuAS^Lk*F?;@GZg5do;T=M@h_>diGZIV>)gXJk&dpBg|R%eW~W z11E#0@!|i_Lr(j`^3NM1=>6U|3&lUR-gJ6 zV!AGu1rb?xPk_d$=;>qzd!CZxgq1Xm+w;>d<&%2>=2og<(DgCs7FV0-KZ>aE++AwN=? zJ=4c!8s5@5{cageQRTnwMfrY9rHpL*tLxi9L{klMkCt1ubL9qI`Rs<|v5;#WwpLuM zTMdZ>X~^U%Ma~=5OnEX8N9HH9aRsir5maxw)r%iy35X?=HgurJT}BF0@(0vv`*o%5LmoZ&#?xvx zwE;?=-E^&5ML~DDEjNsQ72cQGUoHhgRojqgq)S!_o%zfJv$U$AT$f1i#Qmu Use when you need to SEE an image (photo, screenshot, PCB render, diagram, picture file) but your model does not support images — e.g. a read tool result was omitted with "tool image omitted: model does not support images", or the user asks what is shown in a picture / asks you to look at a screenshot / render. Delegate the vision work to a vision-capable subagent - via the describe_image tool instead of guessing or saying you can't. + instead of guessing or saying you can't. --- # Vision Subagent Твоя модель не видит изображения — картинки выбрасываются из контекста. -Не угадывай содержимое и не отказывайся: делегируй зрение сабу с видящей моделью. +Не угадывай содержимое и не отказывайся: делегируй зрение видущему субагенту. ## Когда вызывать @@ -27,7 +27,9 @@ description: > ## Как вызывать -Вызови тул `describe_image`: +### Через тул `describe_image` (pi) + +Если у тебя доступен тул `describe_image`: ``` describe_image: @@ -35,8 +37,19 @@ describe_image: question: "точечный вопрос" # опционально ``` +### Через скрипт describe-image (Claude Code на deepseek и любой агент) + +Если тула нет — запусти субагента-скрипт через Bash: + +``` +node ~/projects/.common/lib/describe-image/describe-image.mjs --path "<абсолютный путь|URL>" [--question "точечный вопрос"] +``` + +Скрипт печатает в stdout описание от видящей модели — этот текст и есть +результат «зрения», верни его пользователю/в свой ответ. + Пример точечного вопроса: -`path: "C:\Users\vitya\Downloads\screen.png"`, `question: "Какое сообщение об ошибке в красной рамке сверху?"` +`node ~/projects/.common/lib/describe-image/describe-image.mjs --path "C:\Users\vitya\Downloads\screen.png" --question "Какое сообщение об ошибке в красной рамке сверху?"` ## Правила @@ -44,23 +57,29 @@ describe_image: если ты её не видел — это галлюцинация. 2. **Задавай точечные вопросы.** Не соглашайся на generic-описание, если тебе нужны конкретные детали (текст, координаты, цвета, что на заднем плане). - Если ответа не хватило — вызови `describe_image` ещё раз с уточнением. -3. **Если `describe_image` упал** (нет vision-модели, ошибка сети) — честно скажи - пользователю, что зрение сейчас недоступно, и предложи путь дальше - (например, переключить модель на видящую через `/model`). + Если ответа не хватило — вызови ещё раз с уточнением. +3. **Если зрение недоступно** (упал тул/скрипт: нет vision-модели, ошибка сети) — + честно скажи пользователю, что зрение сейчас недоступно, и предложи путь дальше. ## Настройка vision-модели Модель задаётся ЯВНО (по приоритету): -1. env `PI_VISION_MODEL` — например `routerai/minimax-m3` -2. файл `~/.pi/vision-model.json` — `{ "model": "routerai/minimax-m3" }` +1. env `PI_VISION_MODEL` — например `routerai/meta/muse-glimmer-30b` +2. файл `~/.pi/vision-model.json` — `{ "model": "routerai/meta/muse-glimmer-30b" }` -Авто-выбора нет — если модель не задана, `describe_image` вернёт ошибку с подсказкой. +Авто-выбора нет — если модель не задана, зрение вернёт ошибку с подсказкой. +Конфиг общий для pi и Claude Code (единый источник правды). -## Реализация тула +## Реализация -Тул `describe_image` — pi-расширение: -`~/projects/pi-extensions/extensions/vision-subagent.ts` (репо -`OpeItcLoc03/pi-extensions`, дом pi-расширений). Правки — в клоне репо → -`commit + push` → `just install` (затирает home-копию). Конфиг-example: -`~/projects/pi-extensions/config/vision-model.json.example`. +- **Скрипт (Claude Code / любой агент):** `~/projects/.common/lib/describe-image/describe-image.mjs` + — субагент на клиенте OpenAI API (пакет `openai`), зовёт routerai + (`https://routerai.ru/api/v1`). Модель — из `PI_VISION_MODEL`/`~/.pi/vision-model.json`, + ключ — `~/.pi/agent/auth.json` (провайдер и base URL — из префикса модели, напр. `routerai/…`). + **Формат картинки — стандартный OpenAI `image_url`** (`data:;base64,…`). + pi-legacy `{type:"image",data,mimeType}` routerai НЕ принимает (модель отвечает + «no image data») — не «чинить» обратно по комментарию в pi-расширении. +- **pi-расширение (тул `describe_image`):** `~/projects/pi-extensions/extensions/vision-subagent.ts` + (репо `OpeItcLoc03/pi-extensions`, дом pi-расширений). Правки — в клоне репо → + `commit + push` → `just install` (затирает home-копию). Конфиг-example: + `~/projects/pi-extensions/config/vision-model.json.example`. diff --git a/skills/web-search/SKILL.md b/skills/web-search/SKILL.md index 6d118ee..94034b3 100644 --- a/skills/web-search/SKILL.md +++ b/skills/web-search/SKILL.md @@ -1,25 +1,25 @@ --- name: web-search author: ours -version: 0.1.0 +version: 0.2.0 description: > Use when the user asks for fresh or external information — news, prices, versions, dates, current facts — or says «поищи», «найди актуальное», - "search for", "look up". Search the web via the `search_web` tool (a - search-capable model from llm-web-proxy with live citations) instead of - guessing from training data, refusing, or scraping by hand. Search is ON by - default — the agent decides when to call it; the user can turn it OFF for - the rest of the session by saying «без поиска» / "no search". + "search for", "look up". Search the web via a search-capable model from + llm-web-proxy with live citations (`search_web` tool or the `search-web` + script) instead of guessing from training data, refusing, or scraping by + hand. Search is ON by default — the agent decides when to call it; the user + can turn it OFF for the rest of the session by saying «без поиска» / "no search". --- # Web Search -Твоя основная модель не имеет веб-поиска. `search_web` — тул, который -отправляет запрос search-модели (веб-поиск DeepSeek с цитатами) на чистом -контексте и возвращает ответ с [1][2]-источниками. **Поиск включён по -умолчанию** — не отказывайся и не выдумывай, когда нужны свежие данные. +Твоя основная модель не имеет веб-поиска. Поиск идёт через search-модель +(веб-поиск DeepSeek с цитатами) на чистом контексте и возвращает ответ с +[1][2]-источниками. **Поиск включён по умолчанию** — не отказывайся и не +выдумывай, когда нужны свежие данные. -## Когда вызывать search_web +## Когда вызывать поиск - Юзер просит «поищи X», «найди актуальное про Y», "search for", "look up". - Вопрос про свежие/внешние данные: новости, цены, версии, даты релизов, @@ -30,17 +30,30 @@ description: > - Вопросы по коду/проекту, дизайн, рефакторинг — ответ в репо и контексте. - Уже загруженные доки/файлы сессии — не ищи то, что уже прочитано. -- **Юзер сказал «без поиска» в этой сессии** — тул не вызывать до конца сессии. +- **Юзер сказал «без поиска» в этой сессии** — поиск не вызывать до конца сессии. ## Как вызывать +### Через тул `search_web` (pi) + ``` search_web: query: "последняя версия Rust — что нового в релизе" ``` -Один запрос = **один вызов** тула. Не спамить серией поисков — если ответа -не хватило, уточни запрос один раз. +### Через скрипт search-web (Claude Code и любой агент) + +Если тула нет — запусти субагента-скрипт через Bash: + +``` +node ~/projects/.common/lib/search-web/search-web.mjs --query "последняя версия Rust — что нового в релизе" +``` + +Скрипт печатает в stdout ответ search-модели (с [1][2]-цитатами и списком +источников) — это и есть результат поиска. + +Один запрос = **один** вызов (тула/скрипта). Не спамить серией поисков — +если ответа не хватило, уточни запрос один раз. ## Правила ответа @@ -52,29 +65,40 @@ search_web: ## Механизм «без поиска» - **Default**: поиск включён — сам решаешь, когда нужны свежие данные. -- Юзер: «без поиска» / "no search" → **перестань вызывать search_web до конца +- Юзер: «без поиска» / "no search" → **перестань вызывать поиск до конца сессии**. Подтверди одной строкой. Не искать даже если запрос «поисковый». - Юзер снова: «поищи» → верни поиск (повторный грант, до конца сессии). - Следующая сессия — снова поиск включён (грант не переживает сессии). -## Ошибки тула +## Ошибки -`search_web failed: ...` — честно скажи юзеру, что поиск недоступен -(модель не настроена: `/search-status`, или сервер llm-web-proxy не запущен: -`lwp serve`), и предложи путь дальше. +`search_web failed: ...` (или ненулевой exit скрипта) — честно скажи юзеру, +что поиск недоступен (модель не настроена, или сервер llm-web-proxy не +запущен: `lwp serve`), и предложи путь дальше. ## Настройка -Search-модель (default `lwp/deepseek-pro-search-web`, pro-search): +Search-модель (default `lwp/deepseek-flash-search-web`): 1. env `PI_SEARCH_MODEL` — например `lwp/deepseek-flash-search-web` 2. файл `~/.pi/search-model.json` — `{ "model": "..." }` -Тул `search_web` — pi-расширение `~/projects/pi-extensions/extensions/search-web.ts` -(репо `OpeItcLoc03/pi-extensions`). Правки — commit + push → `just install` → `/reload`. +⚠️ `lwp/deepseek-pro-search-web` (pro-search, Expert Mode) живой поиск НЕ +выполняет — отвечает из обучения («unavailable in Expert Mode»). Рабочая — +flash-search (Instant Mode). Не «чинить» дефолт обратно на pro. + +## Реализация + +- **Скрипт (Claude Code / любой агент):** `~/projects/.common/lib/search-web/search-web.mjs` + — субагент на клиенте OpenAI API (пакет `openai`), зовёт локальный + llm-web-proxy (`http://127.0.0.1:8788/v1`, `stream:false`). Модель — из + `PI_SEARCH_MODEL`/`~/.pi/search-model.json`, ключ+baseUrl — из + `~/.pi/agent/models.json` → `providers["llm-web"]` (общий конфиг с pi). +- **pi-расширение (тул `search_web`):** `~/projects/pi-extensions/extensions/search-web.ts` + (репо `OpeItcLoc03/pi-extensions`). Правки — commit + push → `just install` → `/reload`. ## Out of scope -- НЕ автоматический поиск в каждом сообщении — тул зовётся, когда запрос +- НЕ автоматический поиск в каждом сообщении — поиск зовётся, когда запрос реально требует внешних данных. -- НЕ скрейпинг страниц (это browser-cdp): search_web ищет и отвечает с цитатами, +- НЕ скрейпинг страниц (это browser-cdp): поиск ищет и отвечает с цитатами, а не открывает конкретные URL.