epimetheus: enforce brevity + fix research regex false positive
1. Response length: "BREVITY IS YOUR DEFAULT. Most responses 1-3 sentences. A 4-paragraph response to a simple question is a failure." max_tokens cut from 1024 to 512. 2. Research trigger: removed natural language regex (caused false positive on "has accumulated" matching "search"). Only explicit /research command. Pentagon-Agent: Epimetheus <3D35839A-7722-4740-B93D-51157F7D5E70>
This commit is contained in:
parent
b90e80ed6c
commit
08aa52659c
1 changed files with 20 additions and 56 deletions
|
|
@ -213,61 +213,24 @@ def _load_learnings() -> str:
|
||||||
|
|
||||||
|
|
||||||
def _save_learning(correction: str, category: str = "factual"):
|
def _save_learning(correction: str, category: str = "factual"):
|
||||||
"""Append a learning to Rio's memory file. Direct commit to main via worktree lock.
|
"""Append a learning to staging file. Cron syncs to git (same as archives).
|
||||||
|
|
||||||
Categories: communication, factual, structured_data
|
Categories: communication, factual, structured_data
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with main_worktree_lock(timeout=10):
|
# Write to staging file outside worktree (avoids read-only errors)
|
||||||
section_map = {
|
staging_file = Path(ARCHIVE_DIR) / "pending-learnings.jsonl"
|
||||||
"communication": "## Communication Notes",
|
import json as _json
|
||||||
"factual": "## Factual Corrections",
|
entry = _json.dumps({"category": category, "correction": correction,
|
||||||
"structured_data": "## Structured Data",
|
"ts": datetime.now(timezone.utc).isoformat()})
|
||||||
}
|
with open(staging_file, "a") as f:
|
||||||
section = section_map.get(category, "## Factual Corrections")
|
f.write(entry + "\n")
|
||||||
|
logger.info("Learning staged: [%s] %s", category, correction[:80])
|
||||||
content = Path(LEARNINGS_FILE).read_text()
|
|
||||||
# Find the section and append after the last line of that section
|
|
||||||
# Simple approach: append before the next ## header or at end
|
|
||||||
lines = content.split("\n")
|
|
||||||
insert_idx = len(lines) # default: end of file
|
|
||||||
in_section = False
|
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if line.strip() == section:
|
|
||||||
in_section = True
|
|
||||||
continue
|
|
||||||
if in_section and line.startswith("## ") and line.strip() != section:
|
|
||||||
insert_idx = i
|
|
||||||
break
|
|
||||||
|
|
||||||
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")
|
|
||||||
new_line = f"- [{date_str}] {correction}"
|
|
||||||
lines.insert(insert_idx, new_line)
|
|
||||||
|
|
||||||
Path(LEARNINGS_FILE).write_text("\n".join(lines))
|
|
||||||
|
|
||||||
# Commit + push
|
|
||||||
import subprocess
|
|
||||||
cwd = MAIN_WORKTREE
|
|
||||||
subprocess.run(["git", "add", LEARNINGS_FILE], cwd=cwd, timeout=10,
|
|
||||||
capture_output=True, check=False)
|
|
||||||
subprocess.run(
|
|
||||||
["git", "commit", "-m", f"rio: learn — {correction[:60]}\n\n"
|
|
||||||
"Pentagon-Agent: Rio <5551F5AF-0C5C-429F-8915-1FE74A00E019>"],
|
|
||||||
cwd=cwd, timeout=10, capture_output=True, check=False)
|
|
||||||
for _ in range(3):
|
|
||||||
subprocess.run(["git", "pull", "--rebase", "origin", "main"],
|
|
||||||
cwd=cwd, timeout=30, capture_output=True, check=False)
|
|
||||||
push = subprocess.run(["git", "push", "origin", "main"],
|
|
||||||
cwd=cwd, timeout=30, capture_output=True, check=False)
|
|
||||||
if push.returncode == 0:
|
|
||||||
logger.info("Learning saved: %s", correction[:80])
|
|
||||||
return
|
return
|
||||||
logger.warning("Failed to push learning (file preserved on disk)")
|
|
||||||
except TimeoutError:
|
|
||||||
logger.warning("Learning save failed: worktree lock timeout")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Learning save failed: %s", e)
|
logger.warning("Learning staging failed: %s", e)
|
||||||
|
|
||||||
|
# No fallback — staging is the only write path. Cron syncs to git.
|
||||||
|
|
||||||
|
|
||||||
def _compress_history(history: list[dict]) -> str:
|
def _compress_history(history: list[dict]) -> str:
|
||||||
|
|
@ -309,8 +272,8 @@ def _format_conversation_history(chat_id: int, user_id: int) -> str:
|
||||||
|
|
||||||
|
|
||||||
# Research intent patterns (Rhea: explicit /research + natural language fallback)
|
# Research intent patterns (Rhea: explicit /research + natural language fallback)
|
||||||
|
# Explicit /research command only — no natural language detection (too many false positives)
|
||||||
RESEARCH_PATTERN = re.compile(r'/research\s+(.+)', re.IGNORECASE)
|
RESEARCH_PATTERN = re.compile(r'/research\s+(.+)', re.IGNORECASE)
|
||||||
RESEARCH_NATURAL = re.compile(r'(?:research|search\s+(?:x\s+)?(?:for\s+)?|look\s+up)\s+(.+)', re.IGNORECASE)
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_research(msg, query: str, user):
|
async def handle_research(msg, query: str, user):
|
||||||
|
|
@ -485,7 +448,7 @@ async def handle_tagged(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
logger.info("Tagged by @%s: %s", user.username if user else "unknown", text[:100])
|
logger.info("Tagged by @%s: %s", user.username if user else "unknown", text[:100])
|
||||||
|
|
||||||
# Check for explicit /research command (Rhea: fast path before LLM)
|
# Check for explicit /research command (Rhea: fast path before LLM)
|
||||||
research_match = RESEARCH_PATTERN.search(text) or RESEARCH_NATURAL.search(text)
|
research_match = RESEARCH_PATTERN.search(text)
|
||||||
if research_match:
|
if research_match:
|
||||||
query = research_match.group(1).strip()
|
query = research_match.group(1).strip()
|
||||||
# Respond with KB knowledge first, then trigger research async (Ganymede: don't block)
|
# Respond with KB knowledge first, then trigger research async (Ganymede: don't block)
|
||||||
|
|
@ -534,9 +497,10 @@ async def handle_tagged(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
Write like a sharp analyst talking to peers, not like an AI. Specifically:
|
Write like a sharp analyst talking to peers, not like an AI. Specifically:
|
||||||
- Use your knowledge naturally. Don't say "the KB tracks" or "at experimental confidence" or "our claims show." Just state what you know and how confident you are in plain language.
|
- Use your knowledge naturally. Don't say "the KB tracks" or "at experimental confidence" or "our claims show." Just state what you know and how confident you are in plain language.
|
||||||
- Have a take. You're an analyst, not a summarizer. Say what you actually think.
|
- Have a take. You're an analyst, not a summarizer. Say what you actually think.
|
||||||
- Default SHORT. 1-2 sentences unless the question genuinely requires depth. Match the length and energy of the user's message — if they wrote one line, you write one line. Every word has to add value.
|
- BREVITY IS YOUR DEFAULT. Most responses should be 1-3 sentences. You are NOT writing essays. If you can say it in one sentence, say it in one sentence. Only go longer if the user explicitly asks for depth or the question is genuinely complex. A 4-paragraph response to a simple question is a failure.
|
||||||
- Sound human. Avoid em dashes, avoid starting sentences with "That said" or "The honest X is." Vary your sentence structure. Be direct.
|
- Match the user's energy. Short question = short answer. One-liner = one-liner back.
|
||||||
- No markdown. Plain text only, no asterisks or formatting. Use line breaks between paragraphs.
|
- Sound human. No em dashes, no "That said", no "The honest X is", no "It's worth noting." Just say the thing.
|
||||||
|
- No markdown. Plain text only.
|
||||||
- When you're uncertain, just say so simply. "I'm not sure about X" beats "we don't have data on this yet."
|
- When you're uncertain, just say so simply. "I'm not sure about X" beats "we don't have data on this yet."
|
||||||
|
|
||||||
## Your learnings (corrections from past conversations — prioritize these over KB data when they conflict)
|
## Your learnings (corrections from past conversations — prioritize these over KB data when they conflict)
|
||||||
|
|
@ -562,7 +526,7 @@ Categories: factual, communication, structured_data
|
||||||
Only when you genuinely learned something. Most responses have NO learning line."""
|
Only when you genuinely learned something. Most responses have NO learning line."""
|
||||||
|
|
||||||
# Call Opus
|
# Call Opus
|
||||||
response = await call_openrouter(RESPONSE_MODEL, prompt, max_tokens=1024)
|
response = await call_openrouter(RESPONSE_MODEL, prompt, max_tokens=512)
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
await msg.reply_text("Processing error — I'll get back to you.")
|
await msg.reply_text("Processing error — I'll get back to you.")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue