Imported from fluviusmagnus/Seelenmaschine (
AGENTS.md). Install upstream withnpx skills add fluviusmagnus/Seelenmaschine. Copyright stays with the author.
AGENTS.md - Seelenmaschine Development Guide
This file provides guidelines for AI agents working on the Seelenmaschine codebase.
Build, Lint, and Test Commands
Virtualenv: Platform-Specific Rules
This project has two separate virtualenvs:
.venv/— Windows (native).venv-linux/— Linux / WSL
On Linux or WSL, ALWAYS use .venv-linux. The Windows .venv will not
work on WSL (wrong Python interpreter, wrong platform binaries). Never run
.venv/bin/python or .venv/bin/pytest on WSL — those paths don't even exist.
| Platform | Virtualenv | Interpreter |
|---|---|---|
| Windows | .venv |
.venv\Scripts\python.exe |
| Linux / WSL | .venv-linux |
.venv-linux/bin/python |
Codex Python Environment
On Windows, Codex should prefer the project virtualenv interpreter when running Python modules:
# Windows
.venv\Scripts\python.exe -m pytest tests
.venv\Scripts\python.exe -m ruff check src
On Linux / WSL, use .venv-linux:
# Linux / WSL
.venv-linux/bin/python -m pytest tests
.venv-linux/bin/python -m ruff check src
The Windows virtualenv may depend on a base interpreter installed in a
user-local directory such as %LOCALAPPDATA%\Programs\Python\.... Codex sandbox
commands may run under a restricted service account that cannot access that
user-local interpreter. If .venv\Scripts\python.exe reports No Python at ...
or Access to the path ... is denied, rerun the same command with escalated
permissions instead of assuming the interpreter is missing.
Running Application
Use commands that work on both Windows and Unix/Linux when possible.
# Telegram Bot mode
python src/main_telegram.py <profile>
# Convenience scripts
./start-telegram.sh <profile> # Unix/Linux/macOS
start-telegram.bat <profile> # Windows
Linting and Formatting
Prefer python -m form for cross-platform compatibility.
# Run ruff for linting
python -m ruff check src
# Auto-fix issues
python -m ruff check --fix src
# Check a specific file
python -m ruff check src/adapter/telegram/handlers.py
If you explicitly need the virtualenv executable path:
# Linux / WSL — use .venv-linux (NOT .venv)
.venv-linux/bin/ruff check src
# Windows
.venv\Scripts\ruff.exe check src
Testing
Prefer python -m pytest for cross-platform compatibility.
# Run all tests
python -m pytest tests
# Run a specific test file
python -m pytest tests/test_config.py
# Run with verbose output
python -m pytest -v tests
If you explicitly need the virtualenv executable path:
# Linux / WSL — use .venv-linux (NOT .venv)
.venv-linux/bin/pytest tests
# Windows
.venv\Scripts\pytest.exe tests
Code Style Guidelines
Imports
- Order: standard library -> third-party -> local modules
- Use absolute imports for local modules (for example,
from core.config import Config) - Group imports with blank lines between categories
import os
from datetime import datetime
from openai import AsyncOpenAI
from core.config import Config
from memory.manager import MemoryManager
Type Hints
- Use type hints for all function parameters and return values
- Import from
typingfor complex types when needed
from typing import Dict, List, Optional, Tuple
def process_input(text: str, limit: int = 10) -> List[Dict[str, str]]:
pass
Naming Conventions
- Classes: PascalCase (for example,
TelegramBot,MemoryManager) - Functions/variables: snake_case (for example,
process_user_input,session_id) - Constants: UPPER_SNAKE_CASE (for example,
MAX_CONV_NUM,AI_NAME) - Private methods: underscore prefix (for example,
_get_db,_ensure_mcp_connected) - Type variables in generic types:
T
Error Handling
- Use
try/exceptwith specific exception types when possible - Log errors with loguru
- Provide meaningful error messages
try:
response = client.call()
except ValueError as e:
logger.error(f"Invalid value: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
Logging
- Use loguru instead of the standard logging module
- Import
get_loggerfromutils.logger
from utils.logger import get_logger
logger = get_logger()
logger.debug(f"Processing session {session_id}")
logger.info("MCP client connected")
logger.error(f"Failed to retrieve data: {error}")
Configuration
- All runtime configuration goes through the
Configclass insrc/core/config.py - Use
Config.*for accessing settings - Initialize config with
init_config(profile)before constructing objects that depend on profile-specific paths or settings - Be careful with module-level imports that read
Configvalues too early
from core.config import Config, init_config
init_config(profile)
if Config.DEBUG_MODE:
logger.debug("Debug mode enabled")
Database Operations
- Use
DatabaseManagerfor SQLite operations - The project uses SQLite with
sqlite-vec - Always close connections after use or rely on the existing manager abstractions
- Parameterize all queries to prevent SQL injection
Async/Sync
- The project is now async-first in
core / memory / llm / tools - Core business logic should keep only one main implementation, and that implementation should be async
- Sync APIs should exist only as thin compatibility wrappers around async implementations
- Do not maintain long-term parallel sync/async business logic bodies for the same workflow
- Use the shared async wrapper helpers in
src/utils/async_utils.py - Standard wrapper expectations:
- use
ensure_not_in_async_context(...)before entering sync wrappers - use
run_sync(...)instead of handwrittenrun_until_completelogic
- use
- Sync wrappers should have consistent event-loop and error behavior across modules
- Telegram bot handlers, scheduler callbacks, MCP access, shell/file tools, and several LLM paths are async
- Use async entry points inside async contexts; sync wrappers must clearly reject being called from an async context
- Do not introduce new module-local event-loop helper implementations when the shared helper can be used
- This async-first refactor does not imply converting the SQLite layer itself to async
Docstrings
- Use concise docstrings for public classes and methods
def search_related_memories(
self, query: str, current_session_id: str
) -> Tuple[List[str], List[str]]:
"""Search related summaries and conversations for a query."""
String Formatting
- Use f-strings for string interpolation
- Use
str.format()or%only when necessary
message = f"Processing {item_type} with ID {item_id}"
Class Design
- Prefer composition over inheritance where possible
- Initialize dependencies in
__init__ - Use
@staticmethodonly for methods that do not need instance state - Keep methods focused and single-purpose
Architecture Ownership and Refactor Style
Ownership Boundary
adapteris the transport / I/O boundary and should only own platform-specific ingress, egress, formatting, and delivery behaviorcoreowns system behavior, runtime wiring, approval flow, session flow, tool execution, and file-delivery policy- Practical rule: if code would still matter after removing Telegram, it belongs in
core - If code depends on Telegram update/message/output semantics, it belongs in
adapter
Where New Code Should Go
- Move ownership to
corebefore adding new adapter helpers - Do not let adapter-side code accumulate stateful workflow logic that is not inherently Telegram-specific
- Keep Telegram controllers and adapter services thin: they should assemble boundary services and delegate to
core - Prefer adding new behavior to existing core owners before introducing a new layer
Refactor Direction
- Prefer deleting thin pass-through wrappers when they do not protect a real boundary
- Prefer collapsing short-lived transitional seams after ownership has stabilized
- Reduce duplicated
create_*,get_*, andattach_*scaffolding when it adds ceremony more than clarity - Do not reintroduce adapter-side runtime/manager/host/bridge layers for core behavior
- Do not introduce new façade/host/helper layers unless they clearly own distinct state, lifecycle, or policy
Runtime Shape
- Keep
CoreBotas the direct core runtime entry surface - Keep adapter controllers focused on boundary orchestration, not business ownership
- Keep tool/runtime ownership inside
core, especially incore.toolsandcore.bot
Refactor Style
- Prefer small, low-risk simplification steps over speculative rewrites
- Optimize for reducing redundant abstraction layers
- Keep registrations and state access direct when extra indirection adds no ownership clarity
- Avoid splitting modules only for cosmetic reasons; split only when ownership becomes clearer
- Prefer deleting obsolete compatibility layers instead of preserving them indefinitely
- For sync/async cleanup, first collapse duplicate logic into the async path, then leave sync as a thin wrapper before deciding whether removal is safe
- When compatibility is still needed, prefer deprecated shims over keeping two full implementations alive
Tests During Refactors
- Update tests to target the real owner of behavior, not historical shells
- For architecture cleanup, prefer focused regression coverage before broad rewrites
- Keep the existing Telegram/Core regression set healthy when changing ownership boundaries
- Add regression coverage when collapsing duplicated sync/async flows so behavior does not diverge again
- For wrapper APIs, test both:
- sync calls from normal synchronous contexts succeed
- sync calls from async contexts fail with clear errors
- For fallback-heavy areas such as
src/memory/seele.py, prefer adding focused regression tests before further compression
File Structure
src/- All Python source codesrc/main_telegram.py- Telegram bot entry pointsrc/core/- Application coordination (bot.py,adapter_contracts.py,config.py,conversation.py,database.py,file_service.py,hitl.py,scheduler.py,tools.py)src/memory/- Memory subsystem (manager.py,context.py,vector_retriever.py,sessions.py,seele.py)src/llm/- LLM clients and orchestration helpers (chat_client.py,memory_client.py,message_builder.py,request_executor.py,tool_loop.py,embedding.py,reranker.py)src/prompts/- Prompt builders and prompt-related helpers (system_prompt.py,chat_prompt.py,memory_prompts.py,runtime.py)src/texts/- Text catalog helpers (catalog.py)src/adapter/telegram/- Telegram adapter implementation (adapter.py,commands.py,controller.py,delivery.py,files.py,formatter.py)src/tools/- Tool implementations (memory_search.py,mcp_client.py,scheduled_tasks.py,file_io.py,file_search.py,shell.py,send_file.py,tool_trace.py)src/utils/- Utilities (async_utils.py,logger.py,time.py,text.py,tool_safety.py)data/<profile>/- Profile-specific data directorydocs/- Project documentation and refactor planstests/- Test filesmigration/- Database migration scriptsstatic/- Static assetstemplate/- Template files<profile>.env- Profile config file in the repository root
Async-First Refactor Notes
- The current architectural direction is: async-first + thin sync wrapper
- New code should follow that direction by default
- Avoid adding new dual-track sync/async implementations in
memory,llm, andtools - Prefer shared helpers and common internal async flows for:
- session handling
- retrieval/rerank pipelines
- long-term memory patch/fallback/retry flows
- client close / tool-call wrapper behavior
- Audit sync APIs before expanding them; if a sync API has no meaningful external need, prefer reducing it to an internal compatibility interface or removing it when safe
Memory System Patterns
- Use
MemoryManagerfor memory operations - Conversations are stored with
text_idfor vector mapping - Sessions can be active or archived
- Use blockquote tags
<blockquote>...</blockquote>for memory citations
MCP Integration
- MCP tools are loaded dynamically via
MCPClient - MCP is optional and controlled by
ENABLE_MCP - Web search is an optional fallback tool controlled by
ENABLE_WEB_SEARCH - Tools are cached in
LLMClient._tools_cache
Telegram Bot
- Uses
python-telegram-bot - Single-user mode is enforced with
TELEGRAM_USER_ID - Outbound messages are currently formatted primarily as HTML in Telegram handlers
- Typing indicators should stay active through response delivery and post-response summary / seele memory updates; do not stop them immediately after the visible reply if background memory work is still running
- Telegram has no Bot API cancel action for typing status; avoid fake cleanup messages because they may briefly disturb the chat
- Core commands include
/new,/reset,/help,/start - Dangerous tool actions may require explicit approval through
/approve
Task Scheduler
TaskSchedulermanages one-time and interval tasksScheduledTaskToolexposes scheduler operations to the LLM- Tasks are persisted in the database
- Supported actions:
add,list,get,cancel,pause,resume
Environment Files
- Never commit
.envfiles with real API keys - Use
.env.exampleas the template - Each profile uses its own
<profile>.envfile - Keep examples cross-platform and avoid hard-coding OS-specific paths unless both variants are documented
Working Language for Communication
Use the language which the developer speaks in communication. If the developer asks in Chinese, respond in Chinese.
