mirror of
https://github.com/hanzoai/bot.git
synced 2026-11-02 00:42:49 +00:00
Four new extensions implement the Hanzo Brain on top of @hanzo/bot
without renaming anything or carving a separate repo. Solo devs install
nothing extra — single binary, single config, SQLite by default.
brain/ meta-pack: wires the three below into one register()
memory-postgres/ dual-mode store: Postgres+pgvector OR SQLite+FTS5,
selected by HANZO_BRAIN_URL / DATABASE_URL. Schema
mirrors gbrain's: pages, edges, facts, FTS index.
graph-links/ zero-LLM typed-link extractor — runs on every page
write. Emits attended/works_at/invested_in/founded/
advises edges from regex + role inference. Strips
code fences. Self-wiring knowledge graph at
>10K pages/sec.
recipes-brain/ YAML recipe loader + scheduler. Flagship recipe:
recipes/email.yaml — daily Gmail pull → classify →
draft → enqueue for swipe-review in the Hanzo app
(bot/apps/{ios,android,macos}). Every swipe is a
training signal for tone.
Tests: 16/16 pass (12 graph-links unit + 4 memory-postgres SQLite).
End-to-end smoke (markdown ingest → edge extraction → fact upsert →
hybrid search → recipe load) runs clean against bun:sqlite.
Deploy path: bot CI builds @hanzo/bot image w/ new extensions →
workflow_dispatch on .github/workflows/k8s-deploy-bot.yml rolls it to
app.hanzo.bot.
93 lines
2.9 KiB
YAML
93 lines
2.9 KiB
YAML
# Hanzo Brain — Email Recipe
|
|
#
|
|
# Daily Gmail pull → classify → draft response → enqueue for swipe-review
|
|
# in the Hanzo app (~/work/hanzo/bot/apps/{ios,android,macos}).
|
|
#
|
|
# Run:
|
|
# hanzo-bot brain run-recipe email
|
|
# hanzo-bot brain drafts list
|
|
# hanzo-bot brain drafts approve <id> # sends via gmail
|
|
# hanzo-bot brain drafts reject <id> # logs as decline-fact for training
|
|
#
|
|
# Trains your reply tone over time. Every swipe is a signal.
|
|
|
|
recipe: email
|
|
version: 1
|
|
backend: gmail
|
|
|
|
auth:
|
|
provider: hanzo.id # OIDC via hanzo IAM — single sign-on
|
|
scopes:
|
|
- https://www.googleapis.com/auth/gmail.readonly
|
|
- https://www.googleapis.com/auth/gmail.compose
|
|
|
|
cron: "*/30 * * * *" # every 30 min — adjust per volume
|
|
|
|
ingest:
|
|
source: inbox
|
|
since: 24h
|
|
max: 100
|
|
filter:
|
|
has_thread: any
|
|
skip_labels: [SPAM, TRASH, PROMOTIONS]
|
|
|
|
classify:
|
|
model: zen-2-haiku # cheap classifier, fast
|
|
schema:
|
|
needs_reply: bool
|
|
priority: one_of [P0, P1, P2, P3]
|
|
intent: one_of [question, fyi, scheduling, ask, update, spam]
|
|
entities: [person, company, deal]
|
|
sentiment: one_of [positive, neutral, negative]
|
|
action_items: list
|
|
|
|
draft:
|
|
when: needs_reply == true
|
|
model: zen-2 # main model — bigger context, better tone
|
|
context:
|
|
- brain.recall(from_email) # who is this person (facts)
|
|
- brain.search(thread_id, limit=10) # past replies in thread (RAG)
|
|
- brain.facts({entity:from_email, since:30d, limit:30})
|
|
- brain.style(sender:me, recent:200) # 200-shot style examples from outbox
|
|
prompt: |
|
|
You are drafting a reply for me. Match my tone exactly — terse,
|
|
direct, no exclamation marks, no emoji. Answer the question in the
|
|
fewest words that are still useful. Sign off only if I usually do.
|
|
Never invent commitments. If you're unsure, suggest "let me check
|
|
and get back to you" and add an action_item.
|
|
output_schema:
|
|
subject: string
|
|
body: string
|
|
action_items: list
|
|
confidence: number # 0..1, used for triage ranking
|
|
|
|
enqueue:
|
|
queue: drafts.email
|
|
ttl: 7d
|
|
rank_by: [priority, confidence]
|
|
fact_on_enqueue:
|
|
subject: from_email
|
|
predicate: thread_active
|
|
object: true
|
|
source: thread_id
|
|
|
|
notify:
|
|
channel: hanzo-app
|
|
badge: count_unswiped
|
|
push: drafts.email.new
|
|
|
|
# After the user swipes in the app, the result feeds back to the brain:
|
|
on_swipe_send:
|
|
- send via gmail
|
|
- upsertFact: { subject: from_email, predicate: replied, object: thread_id, ts: now }
|
|
- learn: append (received_msg, sent_draft) to style examples
|
|
|
|
on_swipe_reject:
|
|
- delete draft
|
|
- upsertFact: { subject: from_email, predicate: declined_reply, object: thread_id, ts: now }
|
|
- learn: down-weight similar drafts in future ranking
|
|
|
|
on_swipe_edit:
|
|
- open editor with the draft pre-filled
|
|
- on save: send + learn (received_msg, edited_draft) as positive example
|