Overhaul agent quality — prompts, tools, config, compression

- Rewrite system prompt: structured sections, explicit tool descriptions
  with full SKILL.md descriptions, multi-agent awareness
- Add write_file skill for creating/modifying workspace files
- Per-template config passthrough: temperature, num_predict, context_size,
  compress settings, max_tool_rounds, max_response_lines
- Bump defaults: 1024 output tokens (was 512), 500-char deque (was 200),
  250-token summaries (was 150), compress threshold 16 (was 12), keep 8 (was 4)
- Cache compression by content hash — no redundant summarization
- Update all 5 templates with tuned settings per role
This commit is contained in:
2026-04-08 18:28:26 +00:00
parent 6c4ad47b09
commit c827d341ab
6 changed files with 160 additions and 30 deletions

View File

@@ -8,6 +8,7 @@ import sys
import time
import signal
import threading
import urllib.request
from collections import deque
from skills import discover_skills, execute_skill, set_logger as set_skills_logger
@@ -34,8 +35,13 @@ CONTEXT_SIZE = CONFIG.get("context_size", 20)
MAX_RESPONSE_LINES = CONFIG.get("max_response_lines", 50)
TOOLS_ENABLED = CONFIG.get("tools", True)
MAX_TOOL_ROUNDS = CONFIG.get("max_tool_rounds", 10)
NUM_PREDICT = CONFIG.get("num_predict", 1024)
TEMPERATURE = CONFIG.get("temperature", 0.7)
WORKSPACE = "/workspace"
SKILL_DIRS = ["/opt/skills", f"{WORKSPACE}/skills"]
COMPRESS_ENABLED = CONFIG.get("compress", True)
COMPRESS_THRESHOLD = CONFIG.get("compress_threshold", 16)
COMPRESS_KEEP = CONFIG.get("compress_keep", 8)
RUNTIME = {
"model": CONFIG.get("model", "qwen2.5-coder:7b"),
@@ -149,22 +155,76 @@ class IRCClient:
# ─── Message Handling ────────────────────────────────────────────────
_compression_cache = {"hash": None, "summary": None}
def compress_messages(channel_msgs):
"""Summarize older messages, keep recent ones intact. Caches summary."""
if not COMPRESS_ENABLED or len(channel_msgs) <= COMPRESS_THRESHOLD:
return channel_msgs
older = channel_msgs[:-COMPRESS_KEEP]
keep = channel_msgs[-COMPRESS_KEEP:]
lines = [f"<{m['nick']}> {m['text']}" for m in older]
conversation = "\n".join(lines)
conv_hash = hash(conversation)
# Return cached summary if older messages haven't changed
if _compression_cache["hash"] == conv_hash and _compression_cache["summary"]:
return [{"nick": "_summary", "text": _compression_cache["summary"], "channel": channel_msgs[0]["channel"]}] + keep
try:
payload = json.dumps({
"model": RUNTIME["model"],
"messages": [
{"role": "system", "content": "Summarize this IRC conversation in 3-5 sentences. Preserve key facts, decisions, questions, and any specific data mentioned. Be thorough but concise."},
{"role": "user", "content": conversation},
],
"stream": False,
"options": {"num_predict": 250},
}).encode()
req = urllib.request.Request(f"{OLLAMA_URL}/api/chat", data=payload, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read(500_000))
summary = data.get("message", {}).get("content", "").strip()
if summary:
_compression_cache["hash"] = conv_hash
_compression_cache["summary"] = summary
log(f"Compressed {len(older)} messages into summary")
return [{"nick": "_summary", "text": summary, "channel": channel_msgs[0]["channel"]}] + keep
except Exception as e:
log(f"Compression failed: {e}")
return channel_msgs
def build_messages(question, channel):
system = RUNTIME["persona"]
# Environment
system += f"\n\n## Environment\nYou are {NICK} in IRC channel {channel}. This is a multi-agent system — other nicks may be AI agents with their own tools. Keep responses concise (this is IRC). To address someone, prefix with their nick: 'coder: can you review this?'"
# Tools
if TOOLS_ENABLED and TOOLS:
skill_names = [t["function"]["name"] for t in TOOLS]
system += "\n\nYou have access to tools: " + ", ".join(skill_names) + "."
system += "\nUse tools when needed rather than guessing. Your workspace at /workspace persists across restarts."
system += "\n\n## Tools\nYou have tools — use them proactively instead of guessing or apologizing. If asked to do something, DO it with your tools."
for t in TOOLS:
fn = t["function"]
system += f"\n- **{fn['name']}**: {fn.get('description', '')}"
system += "\n\nYour workspace at /workspace persists across restarts. Write files, save results, read them back."
# Memory
if AGENT_MEMORY and AGENT_MEMORY != "# Agent Memory":
system += f"\n\nIMPORTANT - Your persistent memory (facts you saved previously, use these to answer questions):\n{AGENT_MEMORY}"
system += f"\n\nYou are in IRC channel {channel}. Your nick is {NICK}. Keep responses concise — this is IRC."
system += "\nWhen you want to address another agent or user, always start your message with their nick followed by a colon, e.g. 'coder: can you review this?'. This is how IRC mentions work — without the prefix, they won't see your message."
system += f"\n\n## Your Memory\n{AGENT_MEMORY}"
messages = [{"role": "system", "content": system}]
channel_msgs = [m for m in recent if m["channel"] == channel]
for msg in channel_msgs[-CONTEXT_SIZE:]:
if msg["nick"] == NICK:
channel_msgs = compress_messages(channel_msgs[-CONTEXT_SIZE:])
for msg in channel_msgs:
if msg["nick"] == "_summary":
messages.append({"role": "system", "content": f"[earlier conversation summary] {msg['text']}"})
elif msg["nick"] == NICK:
messages.append({"role": "assistant", "content": msg["text"]})
else:
messages.append({"role": "user", "content": f"<{msg['nick']}> {msg['text']}"})
@@ -245,6 +305,7 @@ def handle_message(irc, source_nick, target, text):
TOOLS if TOOLS_ENABLED else [],
SKILL_SCRIPTS, dispatch_tool,
OLLAMA_URL, MAX_TOOL_ROUNDS,
num_predict=NUM_PREDICT, temperature=TEMPERATURE,
)
if not response:
@@ -256,8 +317,8 @@ def handle_message(irc, source_nick, target, text):
lines.append(f"[truncated, {MAX_RESPONSE_LINES} lines max]")
irc.say(reply_to, "\n".join(lines))
recent.append({"nick": NICK, "text": response[:200], "channel": channel})
save_message(db_conn, NICK, channel, response[:200], full_text=response)
recent.append({"nick": NICK, "text": response[:500], "channel": channel})
save_message(db_conn, NICK, channel, response[:500], full_text=response)
except Exception as e:
log(f"Error handling message: {e}")
try: