feat(skills): integrate web-search, db-yaml, agent-cost, diataxis-docs, kb-search; retire searxng
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/mnt/8TB/projects/ai/agent-skills/bin/db/psql-yq
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/mnt/8TB/projects/ai/agent-skills/bin/web/search
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: agent-cost
|
||||||
|
description: >-
|
||||||
|
Measure what an agent session actually costs in tokens using bin/agents/cost.
|
||||||
|
Use before and after changing documentation, skills or context layout, and when
|
||||||
|
a session feels unexpectedly expensive.
|
||||||
|
---
|
||||||
|
|
||||||
|
# agent-cost
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/agents/cost # every project, YAML
|
||||||
|
bin/agents/cost --repo brain-ui # only sessions whose cwd matches
|
||||||
|
bin/agents/cost --json | jq .cursor.by_tool
|
||||||
|
bin/agents/cost --snapshot after-x --repo brain-ui # append a row to docs/CONTEXT-BUDGET.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Reads local session storage from OpenCode and Cursor transcripts. Reports the
|
||||||
|
always-loaded baseline, cache hit/miss/thrash, and which tools moved the most
|
||||||
|
bytes.
|
||||||
|
|
||||||
|
## How to read it
|
||||||
|
|
||||||
|
- **Baseline** is what every single message pays for: `AGENTS.md` plus anything
|
||||||
|
eagerly linked from it. Keep it small; it multiplies by message count.
|
||||||
|
- **Cache thrash** matters more than raw size. Editing a file that sits early in
|
||||||
|
the context invalidates the prompt cache for the whole session.
|
||||||
|
- **by_tool bytes** shows where the real spend is. Usually it is unfiltered
|
||||||
|
command output, not documentation.
|
||||||
|
|
||||||
|
## Rule
|
||||||
|
|
||||||
|
Measure before and after. A claim that something "reduces tokens" without a
|
||||||
|
before and an after number is an opinion, not a result.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: db-yaml
|
||||||
|
description: >-
|
||||||
|
Read any Postgres as compact YAML through db/psql-yq, with a read-only guard and
|
||||||
|
named profiles. Use when a task needs table contents, column types or a SELECT
|
||||||
|
against cs_brain or another project database.
|
||||||
|
---
|
||||||
|
|
||||||
|
# db-yaml
|
||||||
|
|
||||||
|
`agent-skills/bin/db/psql-yq` talks to Postgres and returns YAML, which is far
|
||||||
|
cheaper than a psql ASCII table and easy to slice with `yq`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/db/offline -s kunde # column list (brain-ui wrapper)
|
||||||
|
bin/db/offline -t tour -l 20 # 20 sample rows as YAML
|
||||||
|
bin/db/offline -c 'SELECT ...' # query -> YAML
|
||||||
|
bin/db/live -c 'SELECT ...' # live database instead of the snapshot
|
||||||
|
```
|
||||||
|
|
||||||
|
Ad-hoc targets without a profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-skills/bin/db/psql-yq --container my-pg --db app -c 'SELECT 1'
|
||||||
|
agent-skills/bin/db/psql-yq --dsn 'postgres://user@host:5432/db' -c 'SELECT 1'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Profiles
|
||||||
|
|
||||||
|
Connection details live in `~/.config/brain/db-profiles.yml` (mode 600), never in a
|
||||||
|
project repo. A profile names either a `container` or a `host`; passwords are read
|
||||||
|
from a separate `password_env_file` and never appear in argv.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- **Read-only.** Any `insert|update|delete|drop|truncate|alter|create|grant|
|
||||||
|
revoke|vacuum|copy` is rejected with exit 3. Do not work around it.
|
||||||
|
- **PII.** `cs_brain` holds client data. Aggregate and count freely; never copy
|
||||||
|
names or addresses into chat, issues or docs.
|
||||||
|
- Use `-l` to keep samples small. Twenty rows answer most questions.
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
name: diataxis-docs
|
||||||
|
description: >-
|
||||||
|
Classify and structure documentation by Diataxis (tutorial, howto, reference,
|
||||||
|
explanation) with PARA status, using frontmatter that the knowledge-base index
|
||||||
|
reads. Use when creating a document, when one file tries to do two jobs, or when
|
||||||
|
auditing a docs folder.
|
||||||
|
---
|
||||||
|
|
||||||
|
# diataxis-docs
|
||||||
|
|
||||||
|
Every markdown file answers exactly one of four questions. A file that answers
|
||||||
|
two should be split; that is the whole method.
|
||||||
|
|
||||||
|
| Type | Answers | Reader is |
|
||||||
|
|------|---------|-----------|
|
||||||
|
| `tutorial` | "teach me by doing" | learning |
|
||||||
|
| `howto` | "help me do this task" | working |
|
||||||
|
| `reference` | "tell me the facts" | looking up |
|
||||||
|
| `explanation` | "help me understand why" | studying |
|
||||||
|
|
||||||
|
## Frontmatter
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
type: reference # one of the four above
|
||||||
|
status: current # current | archive (PARA)
|
||||||
|
related:
|
||||||
|
- docs/OPS.md
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
`bin/kb/index` reads this. `type` becomes a searchable column and `related`
|
||||||
|
becomes a graph edge:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/kb/search search "deploy" --type howto
|
||||||
|
bin/kb/search search "Stecktafel" --hop 1 # follow links and related
|
||||||
|
```
|
||||||
|
|
||||||
|
## Audit checklist
|
||||||
|
|
||||||
|
1. Does the title match the type? A reference that starts "first, open..." is a
|
||||||
|
howto wearing a disguise.
|
||||||
|
2. More than one H2 topic that a reader would search separately? Split the file.
|
||||||
|
The indexer chunks on H2, so split files also search better.
|
||||||
|
3. Is `status: archive` set on anything superseded? Archived files stay indexed
|
||||||
|
but stop competing with current ones for a reader's attention.
|
||||||
|
4. Does every explanation link the reference it explains, and vice versa? That
|
||||||
|
link is what `--hop 1` walks.
|
||||||
|
|
||||||
|
## Rule
|
||||||
|
|
||||||
|
Do not invent a fifth type. If a document does not fit, it is usually two
|
||||||
|
documents.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
name: kb-search
|
||||||
|
description: >-
|
||||||
|
Deduction search over the 2dph brain (Ladybug graph: ops corpus, portfolio,
|
||||||
|
ssh hosts) with bin/kb/search instead of reading files or grepping repos.
|
||||||
|
Use whenever a question starts with "where is", "what runs on", "which file
|
||||||
|
describes", "who is", "how is X done", before opening any documentation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# kb-search — deduction over facts and info
|
||||||
|
|
||||||
|
One embedded Ladybug graph (`var/kb.lbug`, read-only when queried) holding two
|
||||||
|
roots:
|
||||||
|
|
||||||
|
- **facts** — assertions backed by ≥2 independent sources (docker ps × compose
|
||||||
|
× ssh-config × docs), `confidence: confirmed`.
|
||||||
|
- **info** — descriptive/narrative leafs, searchable, never asserted.
|
||||||
|
|
||||||
|
Search = deduction: facts root first, info root second, `web-search` as the
|
||||||
|
second independent source when local roots cannot confirm. An answer is
|
||||||
|
`confirmed` only if it comes off the facts root; anything else is
|
||||||
|
`(not confirmed)`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/kb/search "Matrix federation" # pointers + snippets, YAML
|
||||||
|
bin/kb/search "what runs on arc-2" --hop 1 # follow graph edges
|
||||||
|
bin/kb/search "onlyoffice postgres" --root facts # restrict to confirmed
|
||||||
|
bin/kb/search "where is cs-lexicon" --json | yq '.[].ref'
|
||||||
|
bin/kb/get <id> --body # full chunk only when needed
|
||||||
|
bin/kb/stats # index health
|
||||||
|
bin/kb/eval # recall@5 >= 0.95 gate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Search before you read. Never grep a repo for a concept the graph covers.
|
||||||
|
- `--root facts` returns only confirmed evidence-linked answers. Default shows
|
||||||
|
facts first, then info leafs clearly marked `(not confirmed)`.
|
||||||
|
- If recall looks wrong, run `bin/kb/eval`; it gates control questions and
|
||||||
|
should stay at or above 95% recall@5.
|
||||||
|
- `--hop N` follows sibling leaves, owning files, `related:` links and
|
||||||
|
vector-neighbours — that is the deduction walk, not random expansion.
|
||||||
|
- Escalate to `web-search` (the `web-search` skill) as the independent second
|
||||||
|
source when both local roots cannot confirm; never report an unconfirmed
|
||||||
|
single-source local answer as fact.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
name: web-search
|
||||||
|
description: >-
|
||||||
|
Search the public web through the self-hosted SearXNG at search.ops.io
|
||||||
|
using bin/web/search. Use for German care law, SGB paragraphs, vendor
|
||||||
|
documentation and any fact that is not in our own repos - and as the second
|
||||||
|
independent source the detective method requires.
|
||||||
|
---
|
||||||
|
|
||||||
|
# web-search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bin/web/search "Pflegegrad SGB XI Einstufung"
|
||||||
|
bin/web/search "Toureffizienz" --site ticket.detective.de
|
||||||
|
bin/web/search "sqlite-vec" --category it -n 3 --json | jq -r '.results[].url'
|
||||||
|
bin/web/search "Pflegereform" --lang de --fresh year
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web or knowledge base
|
||||||
|
|
||||||
|
`bin/kb/search` holds our own facts: product docs, detective business logic, the
|
||||||
|
lexicon. Go there first. Reach for `bin/web/search` when the answer is outside
|
||||||
|
our repos: legislation, vendor documentation, upstream library behaviour.
|
||||||
|
|
||||||
|
Keep the two apart. A finding is stronger when the reader can see that one
|
||||||
|
source was ours and one was not.
|
||||||
|
|
||||||
|
## PII: this query leaves the host
|
||||||
|
|
||||||
|
The search goes to external engines. Never put a client or staff identifier in
|
||||||
|
it. The tool refuses long digit runs, `Personalnummer`, `KV-Nr`, dates of birth
|
||||||
|
and street-with-number, and exits 2. Rephrase rather than reaching for `--force`.
|
||||||
|
|
||||||
|
## Read the status, not just the results
|
||||||
|
|
||||||
|
The instance answers HTTP 200 with an empty list when it throttles, so an empty
|
||||||
|
answer is ambiguous by construction. The tool resolves that for you:
|
||||||
|
|
||||||
|
| status | exit | meaning |
|
||||||
|
|--------|------|---------|
|
||||||
|
| `ok` | 0 | engines answered |
|
||||||
|
| `throttled` | 3 | nobody answered; say nothing about what exists |
|
||||||
|
|
||||||
|
Never turn a `throttled` result into "there is no information about X".
|
||||||
|
|
||||||
|
## Etiquette
|
||||||
|
|
||||||
|
Calls are serialised host-wide and kept ten seconds apart, and answers are
|
||||||
|
cached for seven days. Do not loop over queries, and do not use `--refresh`
|
||||||
|
unless the cached answer is genuinely stale: a burst suspends the engines for
|
||||||
|
several minutes for everyone.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
type: howto
|
||||||
|
status: current
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tuning the SearXNG instance
|
||||||
|
|
||||||
|
The client works around a fragile instance. These changes fix the cause, and
|
||||||
|
they need shell access to the host behind `search.ops.io`
|
||||||
|
(`90.169.228.16` / `ops.mywire.org`), which is a different machine from
|
||||||
|
the one the agents run on.
|
||||||
|
|
||||||
|
## Why it is needed
|
||||||
|
|
||||||
|
Measured on 2026-08-10 from this host:
|
||||||
|
|
||||||
|
- The default engine set for the `general` category is only `duckduckgo`,
|
||||||
|
`brave` and `startpage`. `brave` and `startpage` sit in
|
||||||
|
`Suspended: too many requests` or `Suspended: CAPTCHA` almost permanently, so
|
||||||
|
in practice a single engine carries every query.
|
||||||
|
- About 25 probe requests over a few minutes pushed `duckduckgo` into `CAPTCHA`
|
||||||
|
as well. The instance then answered HTTP 200 with `results: []` and an empty
|
||||||
|
`unresponsive_engines` - indistinguishable from "nothing found" without the
|
||||||
|
client-side handling we added.
|
||||||
|
- Recovery took roughly six minutes.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
1. **Allow our egress IP through the limiter.** In `limiter.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[botdetection.ip_lists]
|
||||||
|
pass_ip = ["77.7.46.234"]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Shorten the suspensions.** In `settings.yml` the defaults are 24 hours for
|
||||||
|
a CAPTCHA and one hour for too-many-requests, which is far longer than the
|
||||||
|
condition lasts:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
search:
|
||||||
|
suspended_times:
|
||||||
|
SearxEngineCaptcha: 300
|
||||||
|
SearxEngineTooManyRequests: 120
|
||||||
|
SearxEngineAccessDenied: 300
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Give `general` more than one working engine.** `google` and `wikipedia`
|
||||||
|
report `enabled: true` in `/config` yet never appear in a `general` response,
|
||||||
|
so they are not in the default set. Put them in it; one live engine per
|
||||||
|
category is a single point of failure.
|
||||||
|
|
||||||
|
4. **Keep the JSON API on.** `formats: [html, json]` must stay, otherwise every
|
||||||
|
client here breaks.
|
||||||
|
|
||||||
|
## Verifying
|
||||||
|
|
||||||
|
Ten requests in a row used to suspend the instance for minutes. After the
|
||||||
|
change they should all answer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for i in $(seq 10); do
|
||||||
|
bin/web/search "test $i" -n 1 --refresh --json | jq -r .status
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Ten lines of `ok` means it is fixed.
|
||||||
Reference in New Issue
Block a user