6.3 KiB
6.3 KiB
Phase-6 Anti-Silent Lessons Tuning (Closed-loop)
Goal
Tune anti-silent ACK template selection using evidence from gateway events (reason + chat_type + template_id + user_signal) without unsafe auto-mutation.
Safety Invariants
- No automatic global policy rewrite.
- Gateway applies tuning only when
ANTI_SILENT_TUNING_ENABLED=true. - Learner emits tuning lessons only with thresholds (
MIN_EVIDENCE,MIN_SCORE). - Lessons have TTL (
expires_at) for rollback-by-expiry.
Components
services/experience-learner/main.py- emits
lesson_type=anti_silent_tuning - computes score:
1 - (w_retry*retry_rate + w_negative*negative_rate + w_suppressed*suppressed_rate)
- emits
gateway-bot/http_api.py- applies tuning in anti-silent template resolver under feature flag
- fail-open on DB/lookup errors
gateway-bot/gateway_experience_bus.py- DB lookup of active tuning lesson by trigger (
reason=<...>;chat_type=<...>)
- DB lookup of active tuning lesson by trigger (
Environment
Learner
ANTI_SILENT_TUNING_ENABLED=trueANTI_SILENT_TUNING_WINDOW_DAYS=7ANTI_SILENT_TUNING_MIN_EVIDENCE=20ANTI_SILENT_TUNING_MIN_SCORE=0.75ANTI_SILENT_TUNING_WEIGHT_RETRY=0.6ANTI_SILENT_TUNING_WEIGHT_NEGATIVE=0.3ANTI_SILENT_TUNING_WEIGHT_SUPPRESSED=0.1ANTI_SILENT_TUNING_TTL_DAYS=7
Gateway
ANTI_SILENT_TUNING_ENABLED=false(default; turn on only after smoke)ANTI_SILENT_TUNING_DB_TIMEOUT_MS=40ANTI_SILENT_TUNING_CACHE_TTL_SECONDS=60
Deploy
cd /opt/microdao-daarion
docker compose -f docker-compose.node1.yml up -d --no-deps --build --force-recreate experience-learner gateway
Single-Command Smoke (Phase-6.1)
make phase6-smoke
The command runs:
- deterministic seed events
- learner lesson generation assertion
- gateway apply assertion (flag ON)
- gateway fallback assertion (flag OFF)
- seed cleanup (events + lessons)
Use PHASE6_CLEANUP=0 make phase6-smoke to keep artifacts for debugging.
CI Integration
- Workflow:
.github/workflows/phase6-smoke.yml - Modes:
workflow_dispatch(manual)workflow_runafter successful deploy workflow
- Operations guide:
docs/ops/ci_smoke.md
Fixed Smoke (Deterministic)
1) Temporary smoke thresholds
Use low thresholds only for smoke:
ANTI_SILENT_TUNING_MIN_EVIDENCE=3ANTI_SILENT_TUNING_MIN_SCORE=0.5ANTI_SILENT_TUNING_WINDOW_DAYS=1
2) Seed synthetic gateway events
export PG_CONTAINER='dagi-postgres'
docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -c "
WITH seed AS (
SELECT
(
substr(md5(random()::text || clock_timestamp()::text), 1, 8) || '-' ||
substr(md5(random()::text || clock_timestamp()::text), 9, 4) || '-' ||
substr(md5(random()::text || clock_timestamp()::text), 13, 4) || '-' ||
substr(md5(random()::text || clock_timestamp()::text), 17, 4) || '-' ||
substr(md5(random()::text || clock_timestamp()::text), 21, 12)
)::uuid AS event_id,
now() - (g * interval '1 minute') AS ts,
CASE WHEN g <= 3 THEN 'UNSUPPORTED_INPUT' ELSE 'SILENT_POLICY' END AS template_id,
CASE WHEN g <= 3 THEN 'retry' ELSE 'none' END AS user_signal
FROM generate_series(1,6) g
)
INSERT INTO agent_experience_events (
event_id, ts, node_id, source, agent_id, task_type, request_id,
channel, inputs_hash, provider, model, profile, latency_ms,
tokens_in, tokens_out, ok, error_class, error_msg_redacted,
http_status, raw
)
SELECT
event_id,
ts,
'NODA1',
'gateway',
'agromatrix',
'webhook',
'phase6-seed-' || event_id::text,
'telegram',
md5(event_id::text),
'gateway',
'gateway',
NULL,
25,
NULL,
NULL,
true,
NULL,
NULL,
200,
jsonb_build_object(
'event_id', event_id::text,
'ts', to_char(ts, 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'source', 'gateway',
'agent_id', 'agromatrix',
'chat_type', 'group',
'anti_silent_action', 'ACK_EMITTED',
'anti_silent_template', template_id,
'policy', jsonb_build_object('sowa_decision', 'SILENT', 'reason', 'unsupported_no_message'),
'feedback', jsonb_build_object('user_signal', user_signal),
'result', jsonb_build_object('ok', true, 'http_status', 200)
)
FROM seed;
"
3) Trigger learner evaluation with one real event
export GATEWAY_WEBHOOK_URL='http://127.0.0.1:9300/agromatrix/telegram/webhook'
curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \
-H 'content-type: application/json' \
-d @docs/ops/payloads/phase5_payload_group_unsupported_no_message.json
4) Verify tuning lesson exists
docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -P pager=off -c "
SELECT ts,
trigger,
action,
raw->>'lesson_type' AS lesson_type,
raw->>'expires_at' AS expires_at,
evidence
FROM agent_lessons
WHERE raw->>'lesson_type'='anti_silent_tuning'
ORDER BY ts DESC
LIMIT 5;
"
Expected: lesson with
trigger=reason=unsupported_no_message;chat_type=groupaction=prefer_template=SILENT_POLICY
5) Enable gateway tuning and verify apply
# set ANTI_SILENT_TUNING_ENABLED=true for gateway and restart container
# then replay unsupported payload:
curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \
-H 'content-type: application/json' \
-d @docs/ops/payloads/phase5_payload_group_unsupported_no_message.json
# verify latest gateway events
docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -P pager=off -c "
SELECT ts,
raw->'policy'->>'reason' AS reason,
raw->>'chat_type' AS chat_type,
raw->>'anti_silent_template' AS template_id,
raw->>'anti_silent_tuning_applied' AS tuning_applied,
raw->>'anti_silent_action' AS anti_action
FROM agent_experience_events
WHERE source='gateway'
AND raw->'policy'->>'reason'='unsupported_no_message'
ORDER BY ts DESC
LIMIT 10;
"
Expected: newest rows have
template_id=SILENT_POLICYtuning_applied=true
PASS
- Tuning lesson created only when evidence/score thresholds pass.
- Gateway does not change template when feature flag is off.
- With feature flag on, gateway applies active non-expired tuning lesson.
- Expired lessons are ignored.
FAIL
- Tuning lesson appears below evidence/score threshold.
- Gateway changes template while feature flag is off.
- Gateway applies expired lesson.
- Webhook path fails when tuning lookup fails (must stay fail-open).
Manual Cleanup Query
DELETE FROM agent_lessons
WHERE raw->>'lesson_type'='anti_silent_tuning'
AND raw->>'seed_test'='true';