v3: Self-learning loop — nightly /api/learn, keyword-based scoring boost, performance feedback into topic selection

This commit is contained in:
drjones
2026-08-03 22:15:34 -07:00
parent 33f76cdc3b
commit 066cf076aa
2 changed files with 154 additions and 4 deletions

View File

@@ -508,6 +508,62 @@ def api_stats():
})
@app.route("/api/learn", methods=["POST"])
def api_learn():
"""Nightly learning: analyze performance and update internal models."""
db = get_db()
# Aggregate keyword performance
rows = db.execute("""
SELECT a.keywords, COUNT(p.id) as views
FROM articles a
LEFT JOIN pageviews p ON a.id = p.article_id
WHERE a.status = 'published'
GROUP BY a.id
ORDER BY views DESC
LIMIT 20
""").fetchall()
keyword_views = {}
for row in rows:
try:
kws = json.loads(row["keywords"]) if isinstance(row["keywords"], str) else (row["keywords"] or [])
except (json.JSONDecodeError, TypeError):
kws = []
for kw in kws:
keyword_views[kw] = keyword_views.get(kw, 0) + (row["views"] or 0)
top_keywords = sorted(keyword_views.items(), key=lambda x: x[1], reverse=True)[:15]
# Best performing word count range
wc_row = db.execute("""
SELECT AVG(a.word_count) as avg_wc, AVG(a.reading_time) as avg_rt
FROM articles a
LEFT JOIN pageviews p ON a.id = p.article_id
WHERE a.status = 'published'
GROUP BY a.id
HAVING COUNT(p.id) > 0
ORDER BY COUNT(p.id) DESC
LIMIT 10
""").fetchone()
# Store learning
db.execute("""
INSERT OR REPLACE INTO learning (metric, value, recorded_at)
VALUES ('top_keywords', ?, datetime('now'))
""", (json.dumps(top_keywords),))
db.commit()
return jsonify({
"status": "learned",
"top_keywords": [{"keyword": kw, "views": v} for kw, v in top_keywords[:10]],
"optimal_word_count": round(wc_row["avg_wc"]) if wc_row and wc_row["avg_wc"] else None,
"optimal_reading_time": round(wc_row["avg_rt"]) if wc_row and wc_row["avg_rt"] else None,
"total_articles_analyzed": db.execute("SELECT COUNT(*) FROM articles WHERE status='published'").fetchone()[0],
})
@app.route("/health")
def health():
db = get_db()