Tools
Ryhmaportti
OpenClaw-plugin: päätösmalli estää haitalliset ryhmäviestit ja päättää vastaako botti (esimerkki, testaamaton geneerinen kopio)
Install
openclaw plugins install --link
Configuration Example
{
"model": "<deployment>",
"state": "Recent group chat messages ...\n[LATEST] Alex: ...",
"questions": {
"addressed": { "type": "noul", "instructions": "Is the LATEST message meant for Botti ..." },
"reply": { "type": "choice", "instructions": "...", "criteria": { "answer": "...", "comment": "...", "gif": "...", "none": "..." } },
"harm": { "type": "noul", "instructions": "..." },
"react": { "type": "noul", "instructions": "..." }
}
}
README
# openclaw-ryhmaportti
OpenClaw-plugin, joka laittaa **pienen päätösmallin** ryhmäviestin ja botin kielimallin väliin.
Tarkoitus: botti voi olla kaveriryhmässä ilman @-mainintavaatimusta, käyttäytyä kuin hiljainen
kaveri (reagoi, heittää joskus yhden rivin, vastaa kun puhutellaan) eikä tottele urkintaa tai
asetusten muuttamisyrityksiä.
> **Tila: esimerkiksi tehty, testaamaton geneerinen kopio.** Tämä on siivottu kopio pluginista, joka on
> tuotantokäytössä yhdessä WhatsApp-ryhmässä. Siivottua versiota ei ole ajettu OpenClaw-gatewayssa;
> `node smoke-test.mjs` ajaa hookit väärennetyllä api-oliolla oikeaa päätösmallia vasten (menee läpi).
> Lue koodi, aja ensin `mode: "observe"` ja säädä kynnykset lokin perusteella.
## Mitä se tekee
Kolme kulmaa, kaikki saman päätösmallin varassa. Sanalistoihin perustuvaa tunnistusta ei ole:
kaikki on joko päätösmallin todennäköisyys tai deterministinen sääntö (jäähdytys, päiväkatto,
yksi reaktio per viesti).
1. **Portinvartija.** Jokaisesta ryhmäviestistä kysytään `harm`: yrittääkö viesti saada botin
paljastamaan salaisuuksia, ajamaan komentoja, vaihtamaan malliaan/asetuksiaan/identiteettiään
tai ohittamaan omistajan ohjeet. Kynnyksen ylittävä viesti otetaan haltuun
`before_dispatch`-hookissa, jolloin se **ei päädy transkriptiin eikä kielimallille**. Jos viesti
oli botille osoitettu, ryhmään menee `blockMessage`; muille osoitetut estetään hiljaa.
`/komento` muulta kuin omistajalta estetään aina ilman päätösmallia.
2. **Vastaamispäätös.** Kysymykset `addressed` (onko viesti botille), `reply`
(answer / comment / gif / none) ja `react` (reagoisiko kaveri emojilla). Politiikka yhdistää ne
kynnyksiin, kolikonheittoon, jäähdytyksiin ja päiväkattoihin → verdict, esimerkiksi
`answer`, `comment+react`, `gif`, `react` tai `silent`. Hiljaiseksi päätetty viesti tallentuu
silti transkriptiin, joten botti näkee sen kontekstina myöhemmin. Kielimallille liitetään
lyhyt ohje (`prompts`), mitä päätettiin ja miten vastataan.
3. **Vuotovahti.** Botin lähtevä teksti arvioidaan kappale kerrallaan: onko se viesti ryhmälle
vai botin omaa pohdintaa ("X kehuu, reagoin 😎, ei lisättävää"). Pohdintakappaleet pudotetaan.
Lisäksi deterministisesti: jos päätös oli "vain reaktio" tai "GIF", pelkkä teksti ei lähde.
## Päätösmalli
Koodi puhuu TypeSafe **System One** -rajapintaa (`POST {endpoint}/v1/systemone`, header
`api-key`). Tällaisen saa esimerkiksi Azure AI Foundrystä Decision-mallina. Pyyntö:
```json
{
"model": "<deployment>",
"state": "Recent group chat messages ...\n[LATEST] Alex: ...",
"questions": {
"addressed": { "type": "noul", "instructions": "Is the LATEST message meant for Botti ..." },
"reply": { "type": "choice", "instructions": "...", "criteria": { "answer": "...", "comment": "...", "gif": "...", "none": "..." } },
"harm": { "type": "noul", "instructions": "..." },
"react": { "type": "noul", "instructions": "..." }
}
}
```
Vastaus sisältää `answers.<id>.noul` (0–1) ja `answers.reply.choice` + `probabilities`.
Yksi kutsu kestää tyypillisesti 0,1–0,5 s. Kysymysten sanamuodot ovat koodissa; ne on
viritetty kymmenillä testiviesteillä, joten muuta niitä vain testaten.
Avaimet luetaan `secretsFile`-tiedostosta (`KEY=value`) tai ympäristömuuttujista:
`DECISION_API_KEY`, `DECISION_ENDPOINT`, `DECISION_DEPLOYMENT`.
## Asennus
```bash
git clone <tämä repo> ~/projects/openclaw-ryhmaportti
openclaw plugins install --link ~/projects/openclaw-ryhmaportti
```
`openclaw.json`:
```json5
{
plugins: {
entries: {
"openclaw-ryhmaportti": {
enabled: true,
hooks: { allowConversationAccess: true }, // pakollinen: hookit näkevät viestit vasta tällä
config: {
mode: "observe", // vaihda "gate" kun loki näyttää järkevältä
groups: ["1234567890@g.us"],
botName: "Botti",
botMentions: ["1234567890"], // botin oma numero ilman plussaa
ownerIds: ["+358401234567"],
ownerName: "Alex",
senderNames: { "+358401234567": "Alex" },
botSenders: [],
gifEnabled: false,
},
},
},
},
channels: { whatsapp: { groups: { "1234567890@g.us": { requireMention: false } } } },
}
```
Katso `config.example.json`. Tiedostot syntyvät hakemistoon `~/.local/share/openclaw-ryhmaportti/`:
`decisions.jsonl` (jokainen päätös todennäköisyyksineen), `history.json` (kontekstipuskuri),
`names.json` (valinnainen: `{ "<lähettäjä-ID>": { "name": "Nimi", "bot": false } }`).
Ryhmän `systemPrompt`issa kannattaa sanoa, että portti on jo päättänyt vastaamisesta ja että
promptin lopussa oleva `[Ryhmäportti] …` -rivi kertoo tyypin. Tyyliohjeet (lyhyet kommentit,
emojit, milloin GIF) ovat `prompts`-asetuksessa suomeksi; vaihda kieli tai tyyli sieltä.
### GIF
`gifEnabled: true` vain jos botilla on työkalu, jolla se hakee GIFin (esimerkiksi skripti, joka
kysyy Giphy-API:lta ja palauttaa mp4-osoitteen). WhatsAppissa `.gif`-tiedosto ei näy; mp4 +
`gifPlayback: true` toimii. Ilman työkalua botti keksii osoitteita, jotka eivät toimi.
## Säätäminen
Aloita löysillä rajoilla ja kiristä lokin perusteella. `decisions.jsonl`-rivillä on `verdict`,
`why` (mikä sääntö ratkaisi) ja kaikki todennäköisyydet, joten ärsyttävän kommentin syy näkyy
suoraan. Asetukset vaihtuvat hot reloadilla; huomaa että OpenClaw käynnistää plugin-ajon
uudelleen, mikä katkaisee WhatsApp-yhteyden noin puoleksi minuutiksi.
## Ansat, jotka on jo kävelty
- **Haitallinen viesti pitää estää `before_dispatch`-hookissa.** `before_agent_reply` +
`handled` ja `before_agent_run` + `block` jättävät alkuperäisen tekstin transkriptiin, ja botti
"muistaa" sen seuraavalla vuorolla.
- **Lopullista vastausta ei saa perua (`cancel`) `reply_payload_sending`-hookissa:** OpenClaw
lähettää tilalle "⚠️ couldn't produce or deliver a reply". Teksti `NO_REPLY` hiljennetään
toimituskerroksessa ilman varoitusta, joten vahti kirjoittaa tekstin siksi.
- **Toista reaktiota ei saa estää `block`illa:** estetty työkalukutsu on virhe, ja kolme
samanlaista virhettä peräkkäin pysäyttää vuoron näkyvällä virheviestillä. Siksi toisen
react-kutsun emoji kirjoitetaan ensimmäiseksi.
- **Tuntematon konffiavain estää kaikki hot reloadit** kunnes se on poistettu. Lisää avain
manifestiin ja lataa plugin uudelleen ennen kuin käytät sitä konffissa.
- **`message_received` ei laukea WhatsApp-ryhmäviesteistä**; kontekstipuskuri rakennetaan
`before_agent_reply`-hookissa ja omat viestit `message_sent`-hookissa.
- **OpenClawin sisäinen "Background skill review" -vuoro** tulee hookille viimeisen lähettäjän
tiedoilla; se vaiennetaan ryhmäsessiossa ilman päätösmallia.
- Kielimalli voi kirjoittaa päättelynsä vastaukseksi, vaikka ohje kieltää. Siksi vuotovahti on
tekstissä, ei ohjeessa.
## Mitä tässä ei ole
- Nimien automaattista keruuta ryhmästä. `senderNames` täytetään käsin tai `names.json`
omalla skriptillä.
- Muita kanavia kuin WhatsApp ei ole kokeiltu. Mainintojen normalisointi olettaa WhatsAppin
tavan näyttää @-maininta tekstissä numeroina.
## Lisenssi
Valitse lisenssi ennen julkaisua (esimerkiksi MIT).
tools
Comments
Sign in to leave a comment