feat(advisor): financial advisor skill (LLM-reasoning) - #50
Merged
Conversation
Реализует epic simpleAI-ws8 — auto-routed skill для покупочных решений. Phase 1 (snapshot, simpleAI-blg): - AdvisorSnapshot + MinTxForConfidence в budget.model - Store.GetAdvisorSnapshot — один SQL CTE: budget_transaction (global, MTD), budget_debt (global, status='active'+direction='owe', due_date<=eom), budget_recurring (chat-scoped, enabled, next_date<=eom). Конверсия через rates[currency]/rates['THB']; THB rate отсутствует → error, прочие отсутствующие → slog.Warn + skip. Phase 2 (scaffold, simpleAI-cyb): - AdvisorSkill + Manifest (positive triggers + negative phrase про budget) - Регистрация в plugin.Registry рядом с BudgetSkill - advisorStore interface — узкий контракт для тестируемости Phase 3 (run+llm, simpleAI-yvq): - Run: parse → rates → conversion → snapshot → forecast → prompt → LLM → JSON parse → Markdown - Friendly fallback на каждой стадии (rates / snapshot / llm / parse) - slog: skill='advisor', chat_id, question (truncated 200 рун), verdict, free_cash_thb, duration_ms Phase 4 (tests+lint, simpleAI-rvs): - Property-based тесты на pgregory.net/rapid (FreeCash/LowData invariants, currency-conversion consistency, parse roundtrip+rejection, format reply contract, ForecastRemaining bound) - golangci-lint = 0 issues Code review WARNINGS закрыты: - UTF-8 safe truncation (truncateRunes) - Telegram Markdown escape для LLM полей (verdict/explanation/recommendation) - chatID отсутствует в ctx → slog.Warn - testdata/rapid/ в .gitignore Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…budget Smoke на dev: «Планирую купить байк» роутился в budget.add_expense (amount=0 → ошибка). Manifest descriptions недостаточно различали PLANNING покупку (advisor) и RECORDING покупки (budget.add_expense). - advisor: русские триггеры (планирую/хочу/думаю/стоит ли/хватит ли/ потянем/что приоритетнее), явное past-tense разделение - budget: past-tense маркеры (купил/потратил/заплатил), negative phrase «Do NOT use for purchase advice — use advisor skill» Closes simpleAI-9wq. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug simpleAI-q49: «хочу купить блендер» уходило в budget.add_expense вместо advisor → "Ошибка: amount must be positive". Причина: LLM-роутер игнорировал negative phrase в budget manifest и positive trigger в advisor manifest — позиция в prompt слабее чем явное правило. Фикс: блок ROUTING RULES выводится ДО списка инструментов: 1. future-purchase intent («хочу/планирую/думаю купить», «стоит ли», «можем ли позволить», «хватит ли») → advisor. 2. past-tense («купил», «потратил», «заплатил») → budget.add_expense. 3. нет суммы + нет past-tense глагола → НЕ add_expense. Тест TestBuildToolsSystemPrompt_ContainsRoutingRules проверяет маркеры и позицию rules перед списком tools. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
simpleAI-yza (P2): Observability UI — Langfuse self-hosted
блокируется существующими 0ya (compose) и 8h8 (инструментация)
референс: /home/malinovskiy.t/GolandProjects/assister/langfuse/
simpleAI-wr7 (P1): Eval Suite — golden-set routing + harness + baseline
подзадачи: cts (структура), 85c (harness), 578 (golden_set), lpy (baseline)
триггер: q49 baselines нет — не знаем сколько таких кейсов
референс: /home/malinovskiy.t/GolandProjects/assister/evals/
simpleAI-4fc (P2): Refactor по образцу assister/rag_mm
подзадачи: 5mi (prompts→files), ckb (split budget_skill), 2vw (split agent/service)
блокируется wr7 — рефакторим только когда есть guard от регрессий
референс: assister/rag_mm/{configs/agents,dr_tools,pipelines}/
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Изолированный compose deploy/langfuse/ (web+worker+postgres+clickhouse+redis+minio). UI на :3001, postgres на :5433, остальные порты на 127.0.0.1. Init seed создаёт org simpleAI / project rag-mm / admin user из env. Корневой .env.example дополнен LANGFUSE_HOST/PUBLIC_KEY/SECRET_KEY для будущей инструментации (simpleAI-8h8). Закрывает simpleAI-0ya.
Минимальный async HTTP-клиент Langfuse ingestion API (internal/observability) + интеграция в agent.Service: trace на запрос, generation на каждый LLM-вызов, span на каждый tool call. nil-Tracer безопасен — без env LANGFUSE_* агент работает как раньше. Init из env LANGFUSE_HOST/PUBLIC_KEY/SECRET_KEY в main.go, события буферизуются в канал и шлются батчами раз в 2 сек (или 100 событий). Smoke verified — trace со spans/generations виден в http://localhost:3001/project/rag-mm/traces. Закрывает simpleAI-8h8.
Расширил deploy/langfuse/README.md (как смотреть трейсы, что под капотом, как отключить, ограничения) + раздел Observability в корневом README. Закрывает simpleAI-11s.
evals/ на верхнем уровне репо: README, пустой golden_set.jsonl, runs/.gitkeep. ADR-003 (local) фиксирует jsonl-схему кейса, append-only процесс, baseline tracking. .gitignore: evals/runs/* кроме .gitkeep и baseline-*.json. Подготовка к Eval Suite epic (simpleAI-wr7) — разблокирует harness (85c) и golden_set (578).
internal/agent/parse.go — публичный API (ParseCalls, BuildToolsSystemPrompt, ParsedCall) для approach B (parse-only): один LLM-вызов на кейс, парсим первый tool call, skills не выполняем. evals/eval.go — типы Case/Result/Run/Summary/Diff + Summarize (accuracy, per-skill, per-tag, failed_ids) + DiffRuns (regressed/improved) + WriteRun/ReadRun (jsonl с meta+summary в последней строке). evals/cmd/routing/main.go — CLI с -input/-out/-tag/-limit/-prev/-dry-run, per-case timeout 60s, имя run-файла <timestamp>_<promptHash[:12]>.jsonl. evals/eval_test.go — table-tests на все метрики, edge cases (empty/invalid/deprecated/round-trip). Разблокирует simpleAI-578 (golden_set ≥20) и simpleAI-lpy (baseline run).
Покрыты 6 классов из DoD: future_purchase/advisor (7), past_tense/add_expense (4), summary (3), forecast (2), list_transactions (3), edge cases (5). Bug q49 покрыт r001. Парсится harness'ом, schema ADR-003.
Accuracy 0.83 (20/24) на golden_set из 24 кейсов. Прогон выполнен на Ollama-fallback (qwen) — DEEPSEEK_API_KEY не сконфигурирован локально. 3 fail из-за low-quality LLM, 1 routing-неточность r017 → simpleAI-399. r001 PASS — регрессия simpleAI-q49 не воспроизводится. .gitignore: разрешить baseline-*.jsonl и BASELINE.md (выровнено с harness).
…leAI-7o2) - deploy.yml: пробрасывает LANGFUSE_HOST/PUBLIC_KEY/SECRET_KEY из GH Secrets в .env на сервере - langfuse.go: Warn вместо Info при missing keys + поля host_set/public_key_set/secret_key_set для диагностики - bd: closed simpleAI-7o2 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pgregory.net/rapid(test-only dep) + golangci-lint = 0 issuesАрхитектура
internal/budget/model.go—AdvisorSnapshot+MinTxForConfidenceinternal/budget/store.go—GetAdvisorSnapshot(один SQL CTE: tx global, debt global, recurring chat-scoped — асимметрия задокументирована в SQL)internal/skills/advisor_skill.go— skill + manifest + Run + helpers (computeForecastRemaining,buildAdvisorPrompt,parseAdvisorLLMResponse,formatAdvisorReply)slog.Warn+ skipskill=advisor, chat_id, question (200 рун), verdict, free_cash_thb, duration_msCode review WARNINGS закрыты
truncateRunes)slog.Warntestdata/rapid/в.gitignoreTest plan
go test ./internal/budget/ ./internal/skills/— green (rapid property tests)golangci-lint run ./...— 0 issuesgo build ./...+go vet ./...— greenskill=advisor🤖 Generated with Claude Code