Biasa bug yang sama berulang-ulang

This commit is contained in:
Dita Aji Pratama 2026-06-14 12:55:51 +07:00
parent fe065f74d6
commit 0fa6fc9db9
3 changed files with 46 additions and 18 deletions

View File

@ -44,7 +44,7 @@ AGENT_MAX_TOOL_OUTPUT = int(os.getenv("AGENT_MAX_TOOL_OUTPUT", default=_yaml_get
# ─── Persona / Mode (YAML, bisa di-override dari .env) ────────────────────────── # ─── Persona / Mode (YAML, bisa di-override dari .env) ──────────────────────────
AGENT_SKILL = os.getenv("AGENT_SKILL", default=_yaml_get("persona", "skill", default="programmer")).strip().lower() AGENT_SKILL = os.getenv("AGENT_SKILL", default="programmer").strip().lower()
PERSONA_NAME = os.getenv("PERSONA_NAME", default=_yaml_get("persona", "name", default="Hendrik")).strip() or "Hendrik" PERSONA_NAME = os.getenv("PERSONA_NAME", default=_yaml_get("persona", "name", default="Hendrik")).strip() or "Hendrik"
PERSONA_AGE = os.getenv("PERSONA_AGE", default=_yaml_get("persona", "age", default="")).strip() PERSONA_AGE = os.getenv("PERSONA_AGE", default=_yaml_get("persona", "age", default="")).strip()
PERSONA_GENDER = os.getenv("PERSONA_GENDER", default=_yaml_get("persona", "gender", default="")).strip() PERSONA_GENDER = os.getenv("PERSONA_GENDER", default=_yaml_get("persona", "gender", default="")).strip()
@ -58,8 +58,8 @@ PERSONA_CATCHPHRASES = os.getenv("PERSONA_CATCHPHRASES", default=_yaml_get("pers
# ─── Character & Skills (YAML, bisa di-override dari .env) ───────────────────── # ─── Character & Skills (YAML, bisa di-override dari .env) ─────────────────────
AGENT_CHARACTER = os.getenv("AGENT_CHARACTER", default=_yaml_get("character", "preset", default="")).strip().lower() AGENT_CHARACTER = os.getenv("AGENT_CHARACTER", default=_yaml_get("agent", "character", default="")).strip().lower()
AGENT_SKILLS = os.getenv("AGENT_SKILLS", default=_yaml_get("character", "skills", default="")).strip().lower() AGENT_SKILLS = os.getenv("AGENT_SKILLS", default="").strip().lower()
# ─── XMPP (non-credential dari YAML, credential dari .env) ───────────────────── # ─── XMPP (non-credential dari YAML, credential dari .env) ─────────────────────
@ -91,8 +91,21 @@ TYPING_MAX = float(os.getenv("TYPING_MAX", default=_yaml_get("delay", "t
ENV_CHARACTERS_DIR = Path(__file__).resolve().parent / "agent" / "characters" ENV_CHARACTERS_DIR = Path(__file__).resolve().parent / "agent" / "characters"
ENV_CHARACTER_CONFIG_PATH = ENV_CHARACTERS_DIR / AGENT_CHARACTER / "character.md" if AGENT_CHARACTER else None ENV_CHARACTER_CONFIG_PATH = ENV_CHARACTERS_DIR / AGENT_CHARACTER / "character.md" if AGENT_CHARACTER else None
if ENV_CHARACTER_CONFIG_PATH and ENV_CHARACTER_CONFIG_PATH.is_file(): # Coba persona.yaml dulu (prioritas utama), kalau tidak ada fallback ke character.md
_character_env: dict[str, str] = {} _persona_yaml_path = ENV_CHARACTERS_DIR / AGENT_CHARACTER / "persona.yaml" if AGENT_CHARACTER else None
if _persona_yaml_path and _persona_yaml_path.is_file():
# Prioritas utama: baca persona.yaml dari character directory
try:
_character_env = yaml.safe_load(_persona_yaml_path.read_text(encoding="utf-8")) or {}
if not isinstance(_character_env, dict):
_character_env = {}
except Exception as _e:
print(f"[config] Warning: gagal load persona.yaml untuk '{AGENT_CHARACTER}': {_e}")
_character_env = {}
elif ENV_CHARACTER_CONFIG_PATH and ENV_CHARACTER_CONFIG_PATH.is_file():
# Fallback: baca character.md (format lama)
_character_env = {}
for line in ENV_CHARACTER_CONFIG_PATH.read_text(encoding="utf-8").splitlines(): for line in ENV_CHARACTER_CONFIG_PATH.read_text(encoding="utf-8").splitlines():
line = line.strip() line = line.strip()
if not line or line.startswith("#"): if not line or line.startswith("#"):
@ -100,6 +113,10 @@ if ENV_CHARACTER_CONFIG_PATH and ENV_CHARACTER_CONFIG_PATH.is_file():
if "=" in line: if "=" in line:
key, value = line.split("=", 1) key, value = line.split("=", 1)
_character_env[key.strip()] = value.strip() _character_env[key.strip()] = value.strip()
else:
_character_env = None
if _character_env:
_character_overrides = { _character_overrides = {
"AGENT_SKILL": AGENT_SKILL, "AGENT_SKILL": AGENT_SKILL,
@ -113,8 +130,23 @@ if ENV_CHARACTER_CONFIG_PATH and ENV_CHARACTER_CONFIG_PATH.is_file():
"PERSONA_MOOD": PERSONA_MOOD, "PERSONA_MOOD": PERSONA_MOOD,
"PERSONA_CATCHPHRASES": PERSONA_CATCHPHRASES, "PERSONA_CATCHPHRASES": PERSONA_CATCHPHRASES,
} }
# Mapping dari key persona.yaml ke key config
_yaml_to_config_key = {
"AGENT_SKILL": "skill",
"PERSONA_NAME": "name",
"PERSONA_AGE": "age",
"PERSONA_GENDER": "gender",
"PERSONA_TONE": "tone",
"PERSONA_VERBOSITY": "verbosity",
"PERSONA_HUMOR": "humor",
"PERSONA_LANGUAGE": "language",
"PERSONA_MOOD": "mood",
"PERSONA_CATCHPHRASES": "catchphrases",
}
for key, fallback in _character_overrides.items(): for key, fallback in _character_overrides.items():
value = _character_env.get(key, fallback).strip() yaml_key = _yaml_to_config_key.get(key, key.lower())
raw = _character_env.get(yaml_key, _character_env.get(key, fallback))
value = str(raw).strip() if raw is not None else ""
if key in {"AGENT_SKILL", "PERSONA_TONE", "PERSONA_VERBOSITY", "PERSONA_HUMOR", "PERSONA_LANGUAGE", "PERSONA_MOOD"}: if key in {"AGENT_SKILL", "PERSONA_TONE", "PERSONA_VERBOSITY", "PERSONA_HUMOR", "PERSONA_LANGUAGE", "PERSONA_MOOD"}:
value = value.lower() or fallback value = value.lower() or fallback
if key == "PERSONA_NAME" and not value: if key == "PERSONA_NAME" and not value:

View File

@ -1,16 +1,10 @@
agent: agent:
max_iterations: 40 max_iterations: 40
max_tool_output: 40000 max_tool_output: 40000
character: lily # Directory name in agent/characters/<character>/
persona:
skill: programmer # Default skill: programmer, roleplayer, analyst
character:
preset: hendrik # Directory name in agent/characters/<preset>/
skills: programmer # comma-separated, e.g. "programmer,analyst"
xmpp: xmpp:
enabled: false enabled: true
muc_rooms: "" # comma-separated, e.g. "room1@conference.server,room2@conference.server" muc_rooms: "" # comma-separated, e.g. "room1@conference.server,room2@conference.server"
nickname: "" # custom MUC nickname (empty = use username) nickname: "" # custom MUC nickname (empty = use username)
selective_response: true # true = only response if mentioned/relevant selective_response: true # true = only response if mentioned/relevant

View File

@ -129,6 +129,7 @@ def _build_personality_block(cfg: PersonalityConfig) -> str:
"formal": "You speak formally and professionally, using polite language.", "formal": "You speak formally and professionally, using polite language.",
"playful": "You are playful and cheerful, making conversations fun and lighthearted.", "playful": "You are playful and cheerful, making conversations fun and lighthearted.",
"warm": "You are warm and friendly, making people feel comfortable and welcomed.", "warm": "You are warm and friendly, making people feel comfortable and welcomed.",
"sweet": "You speak in a sweet, gentle, and caring manner — soft and endearing.",
} }
parts.append(tone_map.get(cfg.tone, tone_map["casual"])) parts.append(tone_map.get(cfg.tone, tone_map["casual"]))
@ -254,13 +255,13 @@ def build_system_prompt(
Returns: Returns:
String system prompt lengkap. String system prompt lengkap.
""" """
selected_skill = (skill or SKILL).strip().lower() selected_skill = (skill or "").strip().lower()
cfg = personality or PERSONALITY cfg = personality or PERSONALITY
# Resolve character name # Resolve character name
character_name = (character or os.getenv("AGENT_CHARACTER", default="")).strip().lower() character_name = (character or os.getenv("AGENT_CHARACTER", default="")).strip().lower()
# ── Load persona.yaml dari character directory (override personality) ───────── # ── Load persona.yaml dari character directory ─────────────────────────────────
if character_name: if character_name:
char_dir = ENV_CHARACTERS_DIR / character_name char_dir = ENV_CHARACTERS_DIR / character_name
persona_yaml_path = char_dir / "persona.yaml" persona_yaml_path = char_dir / "persona.yaml"
@ -269,7 +270,6 @@ def build_system_prompt(
with open(persona_yaml_path, "r", encoding="utf-8") as f: with open(persona_yaml_path, "r", encoding="utf-8") as f:
_persona_data = yaml.safe_load(f) or {} _persona_data = yaml.safe_load(f) or {}
if isinstance(_persona_data, dict): if isinstance(_persona_data, dict):
# Override personality dari persona.yaml
if _persona_data.get("name"): if _persona_data.get("name"):
cfg.name = _persona_data["name"] cfg.name = _persona_data["name"]
if _persona_data.get("age"): if _persona_data.get("age"):
@ -286,10 +286,12 @@ def build_system_prompt(
cfg.language = _persona_data["language"] cfg.language = _persona_data["language"]
if _persona_data.get("mood"): if _persona_data.get("mood"):
cfg.mood = _persona_data["mood"] cfg.mood = _persona_data["mood"]
# key "skill" (baru) atau "mode" (legacy) menentukan active skill # Skill wajib dari persona.yaml
_skill_from_yaml = _persona_data.get("skill") or _persona_data.get("mode") _skill_from_yaml = _persona_data.get("skill") or _persona_data.get("mode")
if _skill_from_yaml: if _skill_from_yaml:
selected_skill = _skill_from_yaml.strip().lower() selected_skill = _skill_from_yaml.strip().lower()
else:
selected_skill = ""
except Exception as e: except Exception as e:
print(f"[persona] Warning: gagal load persona.yaml untuk '{character_name}': {e}") print(f"[persona] Warning: gagal load persona.yaml untuk '{character_name}': {e}")