- services/api/README.md: new — contract, middleware stack, background tasks, config table (LOG_LEVEL, SENTRY_DSN), health story, extraction criteria - ml/serving/README.md: add Observability section (structlog JSON, traceparent → trace_id binding), add SENTRY_DSN + ENV to config table - services/recommender/README.md: fix policy table — egreedy-v2 is active (#99), egreedy-v1 is shadow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
43 lines
2.0 KiB
Markdown
43 lines
2.0 KiB
Markdown
# recommender
|
|
|
|
The core of oO. Takes a user + context, returns **one** tip.
|
|
|
|
## Contract
|
|
|
|
```
|
|
POST /api/recommend
|
|
{ } (user inferred from session)
|
|
→ { tip: { id, content, source, kind, sourceId?, rationale?, createdAt } }
|
|
|
|
POST /api/tip/:id/feedback
|
|
{ action: "done"|"dismiss"|"snooze"|"helpful"|"not_helpful", dwellMs? }
|
|
→ { ok: true }
|
|
```
|
|
|
|
## Pipeline
|
|
|
|
1. **Signals** — `SignalAggregator.fetchAll(userId)` fans out to all registered `SignalSource` implementations in parallel. Currently: `TodoistSignalSource`. Add a source via `aggregator.register(new MySource())`.
|
|
2. **LLM candidates** — `POST /generate` on `ml/serving` returns `TipCandidate[]` from the `tip-generator` LiteLLM alias.
|
|
3. **Scoring** — all candidates sent to `ml/serving` active policy (`POST /score/egreedy`). Falls back to random if `ml/serving` is unreachable.
|
|
4. **Shadow policies** — active policy runs shadow policies in the same request for offline comparison (ADR-0002). Currently: `egreedy-v2` shadows `egreedy-v1`.
|
|
5. **Persistence** — `tipViews` + `tipScores` rows written on every serve; `tipFeedback` row on reaction.
|
|
6. **Reward delivery** — reaction triggers `POST /reward/egreedy` on `ml/serving` with inferred reward value.
|
|
|
|
## Signal normalization
|
|
|
|
Signals carry `features: Record<string, number | boolean>` (bandit-ready) and `metadata: Record<string, unknown>` (source-specific raw fields). The bandit treats features as an opaque dict — sources own their feature names. See ADR-0009.
|
|
|
|
## Policy registry
|
|
|
|
| Policy | Status | Notes |
|
|
|--------|--------|-------|
|
|
| `random` | Fallback | Used when ml/serving is unreachable |
|
|
| `egreedy-v1` | Shadow | d=7, ADR-0007 |
|
|
| `egreedy-v2` | **Active** | d=12 + profile features, ADR-0012 |
|
|
|
|
Shadow → active promotion requires offline sim + online agreement (ADR-0002).
|
|
|
|
## Extraction criteria
|
|
|
|
Extract to its own process at scaling hotspot: when `POST /recommend` p99 latency exceeds SLA or when recommendation CPU displaces API serving on shared host.
|