Rollen-Ausbau: kritiker (GLM-4.7-Flash) + Gedaechtnis-Reranker (Qwen3-Reranker-0.6B)
Ergebnis des Rollen-Audits 07.07. (User: 'das beste, aber sinnvollste'): (1) KRITIKER: fremdblick-Prosa + Chef-Gutachter-Vertretung liefen auf der 9B-Vision-GLM, obwohl es reine Text-Jobs sind. Neu: GLM-4.7-Flash (30B-A3B, MIT, Unsloth UD-Q4_K_XL, 17,5 GB, on-demand ttl 600, Alias 'kritiker'). Gleicher Fremd-Vendor wie bisher -> die 'andere Brille' des Kritiker-Konzepts bleibt. 4.6V-Flash bleibt scout (Bild-Jobs). (2) RERANKER (die eine echte Rollen-Luecke): Mem0-Suche war reine Vektor-Aehnlichkeit. Neu: Qwen3-Reranker-0.6B (q8_0, Mungert-GGUF — Community-Konvertierungen liefern bekannt Nullscores) via llama-swap /v1/rerank ordnet die Top-20 Kandidaten nach echter Relevanz um. NUR die Reihenfolge aendert sich: score bleibt Vektor-Score (Hermes- Plugin-Filter RECALL_MIN_SCORE bleibt kalibriert), rerank_score kommt als neues Feld dazu; jeder Fehler -> lautlos Vektor-Reihenfolge. Env: MC_RERANK_ENABLED/_URL/_MODEL/_TIMEOUT/_CANDIDATES. In brains (verdraengungssicher, ~0,7 GB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ set -uo pipefail
|
|||||||
|
|
||||||
ENDPOINT="${CHEF_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}"
|
ENDPOINT="${CHEF_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}"
|
||||||
CHEF="${CHEF_MODEL:-gpt-oss-120b}"
|
CHEF="${CHEF_MODEL:-gpt-oss-120b}"
|
||||||
VERTRETUNG="${CHEF_FALLBACK:-GLM-4.6V-Flash}"
|
VERTRETUNG="${CHEF_FALLBACK:-GLM-4.7-Flash}"
|
||||||
MC="$HOME/mission-control-v2"
|
MC="$HOME/mission-control-v2"
|
||||||
VAULT="$HOME/wissens-vault"
|
VAULT="$HOME/wissens-vault"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Fremdblick — Ein-Schuss-Zweitmeinung von einem ANDEREN Modell (Default: GLM-4.6V-Flash, andere
|
# Fremdblick — Ein-Schuss-Zweitmeinung von einem ANDEREN Modell (Default seit 07.07.:
|
||||||
# Modell-FAMILIE/Vendor als das Qwen-Worker-Hirn → echte „andere Brille"). Eine EINZELNE
|
# GLM-4.7-Flash, 30B-A3B Text — klar stärkerer Kritiker als die 9B-Vision-Schwester 4.6V,
|
||||||
|
# gleicher Fremd-Vendor wie zuvor → echte „andere Brille" bleibt). Eine EINZELNE
|
||||||
# Completion — kein agentischer Subagent — umgeht Hermes' 64K-Delegations-Floor
|
# Completion — kein agentischer Subagent — umgeht Hermes' 64K-Delegations-Floor
|
||||||
# (MINIMUM_CONTEXT_LENGTH) UND das 60-GB-Ladeproblem von gpt-oss (das mit dem Warm-Set kollidiert
|
# (MINIMUM_CONTEXT_LENGTH) UND das 60-GB-Ladeproblem von gpt-oss (das mit dem Warm-Set kollidiert
|
||||||
# und „exited prematurely" wirft). GLM lädt klein neben dem Warm-Set und antwortet zuverlässig.
|
# und „exited prematurely" wirft). GLM lädt klein neben dem Warm-Set und antwortet zuverlässig.
|
||||||
@@ -37,7 +38,7 @@ if [ "$MODE" = "code" ]; then
|
|||||||
MAXTOK="${FREMDBLICK_MAXTOK:-1600}"
|
MAXTOK="${FREMDBLICK_MAXTOK:-1600}"
|
||||||
SYS="${FREMDBLICK_SYS:-$CODE_SYS}"
|
SYS="${FREMDBLICK_SYS:-$CODE_SYS}"
|
||||||
else
|
else
|
||||||
MODEL="${FREMDBLICK_MODEL:-GLM-4.6V-Flash}"
|
MODEL="${FREMDBLICK_MODEL:-GLM-4.7-Flash}"
|
||||||
MAXTOK="${FREMDBLICK_MAXTOK:-2200}"
|
MAXTOK="${FREMDBLICK_MAXTOK:-2200}"
|
||||||
SYS="${FREMDBLICK_SYS:-$PROSE_SYS}"
|
SYS="${FREMDBLICK_SYS:-$PROSE_SYS}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -77,6 +77,30 @@ models:
|
|||||||
out: [text]
|
out: [text]
|
||||||
tools: true
|
tools: true
|
||||||
context: 131072
|
context: 131072
|
||||||
|
GLM-4.7-Flash:
|
||||||
|
# KRITIKER (07.07., Rollen-Audit): Fremd-Vendor-Zweitmeinung für fremdblick-Prosa +
|
||||||
|
# Chef-Gutachter-Vertretung — reines Textmodell, 30B-A3B (MIT, Unsloth-Dynamic-Quant).
|
||||||
|
# Die Vision-Schwester GLM-4.6V-Flash bleibt als scout für Bild-Jobs. --jinja ist
|
||||||
|
# Pflicht (Chat-Template, sonst kaputte Tool-Calls).
|
||||||
|
cmd: |
|
||||||
|
llama-server -m /srv/models/GLM-4.7-Flash-GGUF/GLM-4.7-Flash-UD-Q4_K_XL.gguf --host 127.0.0.1 --port ${PORT} -c 65536 -ngl 999 -fa on --no-mmap --jinja
|
||||||
|
ttl: 600
|
||||||
|
aliases:
|
||||||
|
- kritiker
|
||||||
|
capabilities:
|
||||||
|
in: [text]
|
||||||
|
out: [text]
|
||||||
|
tools: true
|
||||||
|
context: 65536
|
||||||
|
Qwen3-Reranker-0.6B:
|
||||||
|
# Gedächtnis-Zweitstufe (07.07., Rollen-Audit): ordnet Mem0-Suchtreffer nach echter
|
||||||
|
# Relevanz (/v1/rerank, Mungert-GGUF — Community-Konvertierungen liefern oft Nullscores!).
|
||||||
|
# Winzig (~0,7 GB) → in brains (verdrängungssicher); lädt in ~1-2 s, erste Anfrage wärmt.
|
||||||
|
cmd: |
|
||||||
|
llama-server -m /srv/models/Qwen3-Reranker-0.6B-GGUF/Qwen3-Reranker-0.6B-q8_0.gguf --host 127.0.0.1 --port ${PORT} --reranking --pooling rank --embedding -ngl 999 -fa on --no-mmap -c 8192
|
||||||
|
ttl: 0
|
||||||
|
aliases:
|
||||||
|
- reranker
|
||||||
groups:
|
groups:
|
||||||
brains:
|
brains:
|
||||||
swap: false
|
swap: false
|
||||||
@@ -86,4 +110,5 @@ groups:
|
|||||||
persistent: true
|
persistent: true
|
||||||
members:
|
members:
|
||||||
- Qwen3-Embedding-0.6B
|
- Qwen3-Embedding-0.6B
|
||||||
|
- Qwen3-Reranker-0.6B
|
||||||
- Qwen3.6-35B-A3B
|
- Qwen3.6-35B-A3B
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ API_KEY = os.environ.get("MEM0_API_KEY", "sk-local") # llama.cpp ignoriert den
|
|||||||
LLM_MAX_TOKENS = int(os.environ.get("MEM0_LLM_MAX_TOKENS", "2048"))
|
LLM_MAX_TOKENS = int(os.environ.get("MEM0_LLM_MAX_TOKENS", "2048"))
|
||||||
SEARCH_TOP_K = int(os.environ.get("MEM0_SEARCH_TOP_K", "50"))
|
SEARCH_TOP_K = int(os.environ.get("MEM0_SEARCH_TOP_K", "50"))
|
||||||
|
|
||||||
|
# --- Rerank-Zweitstufe (07.07.2026): Die Vektor-Suche findet ÄHNLICHES, der Reranker ordnet
|
||||||
|
# nach ECHTER Relevanz (Qwen3-Reranker via llama-swap /v1/rerank). Es ändert sich NUR die
|
||||||
|
# Reihenfolge: `score` bleibt der Vektor-Score (RECALL_MIN_SCORE im Hermes-Plugin bleibt
|
||||||
|
# kalibriert), Konsumenten, die vorne abschneiden (RECALL_LIMIT), bekommen automatisch die
|
||||||
|
# relevantesten Fakten. Jeder Fehler → lautlos Vektor-Reihenfolge (Gedächtnis bricht NIE).
|
||||||
|
RERANK_ENABLED = os.environ.get("MC_RERANK_ENABLED", "1") != "0"
|
||||||
|
RERANK_URL = os.environ.get("MC_RERANK_URL", "http://127.0.0.1:8080/v1/rerank")
|
||||||
|
RERANK_MODEL = os.environ.get("MC_RERANK_MODEL", "Qwen3-Reranker-0.6B")
|
||||||
|
RERANK_TIMEOUT = float(os.environ.get("MC_RERANK_TIMEOUT", "8"))
|
||||||
|
RERANK_CANDIDATES = int(os.environ.get("MC_RERANK_CANDIDATES", "20")) # nur die Top-N neu ordnen (Latenz)
|
||||||
|
|
||||||
# Kategorien = fundierte Memory-Taxonomie (semantisch/prozedural/episodisch), bewusst knapp (4).
|
# Kategorien = fundierte Memory-Taxonomie (semantisch/prozedural/episodisch), bewusst knapp (4).
|
||||||
# Mem0-OSS kann nicht nativ klassifizieren (Cloud-Feature) → wir lassen das Hirn beim Lernen
|
# Mem0-OSS kann nicht nativ klassifizieren (Cloud-Feature) → wir lassen das Hirn beim Lernen
|
||||||
# einordnen (classify_facts). Die Beschreibungen steuern die Treffsicherheit der Auto-Zuordnung.
|
# einordnen (classify_facts). Die Beschreibungen steuern die Treffsicherheit der Auto-Zuordnung.
|
||||||
@@ -195,12 +206,42 @@ def health() -> dict:
|
|||||||
raise HTTPException(503, f"mem0 nicht bereit: {exc}")
|
raise HTTPException(503, f"mem0 nicht bereit: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
def _rerank(query: str, items: list[dict]) -> list[dict]:
|
||||||
|
"""Suchtreffer nach echter Relevanz umordnen; bei jedem Fehler unverändert zurück."""
|
||||||
|
if not (RERANK_ENABLED and len(items) > 1):
|
||||||
|
return items
|
||||||
|
try:
|
||||||
|
import httpx
|
||||||
|
r = httpx.post(RERANK_URL, json={
|
||||||
|
"model": RERANK_MODEL,
|
||||||
|
"query": query,
|
||||||
|
"documents": [i.get("content", "") for i in items],
|
||||||
|
"top_n": len(items),
|
||||||
|
}, timeout=RERANK_TIMEOUT)
|
||||||
|
r.raise_for_status()
|
||||||
|
results = r.json().get("results") or []
|
||||||
|
ranked = []
|
||||||
|
for s in sorted(results, key=lambda x: x.get("relevance_score", 0.0), reverse=True):
|
||||||
|
idx = s.get("index")
|
||||||
|
if isinstance(idx, int) and 0 <= idx < len(items):
|
||||||
|
item = dict(items[idx])
|
||||||
|
item["rerank_score"] = round(float(s.get("relevance_score", 0.0)), 4)
|
||||||
|
ranked.append(item)
|
||||||
|
# Nur übernehmen, wenn der Reranker ALLE Kandidaten bewertet hat (sonst Verlustgefahr).
|
||||||
|
return ranked if len(ranked) == len(items) else items
|
||||||
|
except Exception as exc: # noqa: BLE001 — Gedächtnis darf am Reranker nie scheitern
|
||||||
|
log.debug("rerank übersprungen: %s", exc)
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
@app.get("/memory")
|
@app.get("/memory")
|
||||||
def list_memory(q: str = "", category: str = "") -> list[dict]:
|
def list_memory(q: str = "", category: str = "") -> list[dict]:
|
||||||
"""q gesetzt → semantische Suche (mit Relevanz-Score). Sonst → alle Fakten."""
|
"""q gesetzt → semantische Suche (mit Relevanz-Score). Sonst → alle Fakten."""
|
||||||
if q:
|
if q:
|
||||||
res = mem().search(q, filters={"user_id": USER_ID}, top_k=SEARCH_TOP_K)
|
res = mem().search(q, filters={"user_id": USER_ID}, top_k=SEARCH_TOP_K)
|
||||||
items = [_to_item(r, r.get("score")) for r in res.get("results", [])]
|
items = [_to_item(r, r.get("score")) for r in res.get("results", [])]
|
||||||
|
# Zweitstufe: die vorderen Kandidaten nach echter Relevanz ordnen (Rest bleibt hinten dran).
|
||||||
|
items = _rerank(q, items[:RERANK_CANDIDATES]) + items[RERANK_CANDIDATES:]
|
||||||
else:
|
else:
|
||||||
res = mem().get_all(filters={"user_id": USER_ID}, top_k=1000)
|
res = mem().get_all(filters={"user_id": USER_ID}, top_k=1000)
|
||||||
items = [_to_item(r) for r in res.get("results", [])]
|
items = [_to_item(r) for r in res.get("results", [])]
|
||||||
|
|||||||
Reference in New Issue
Block a user