Currently discounted. Expect bugs, incomplete features, and rough edges. Error handling and try/catch coverage will be improved over time.
A fully async, multi-agent CLI assistant with a custom adapter layer for LLM providers β swap between local Ollama models, OpenAI, DeepSeek, and more by changing one line in .env.
5.16.mp4
Agent (base.py)
β
ββββββββββββββββββΌβββββββββββββββββ
β β β
MainAgent PlannerAgent ExecutorAgent
(loop.py) (planner.py) (executor.py)
β
EvaluatorAgent
(evaluator.py)
All agents inherit from a shared Agent base class that handles the streaming LLM loop, tool execution, Rich.Live spinner/renderer, and depth limiting. Each subclass overrides only what it needs.
adapters/
βββ __init__.py # Adapter: unified entry point, routes by provider prefix
βββ schema.py # Response: canonical output format for all providers
βββ base_adapter.py # Abstract interface all adapters implement
βββ openai_compat_adapter.py # All OpenAI-compatible providers (DeepSeek, OpenAI, OpenRouter, etc.)
βββ ollama_adapter.py # Ollama (OpenAI-compatible client + embed support)
βββ anthropic_adapter.py # Anthropic
βββ gemini_adapter.py # Google Gemini
DeepSeek, OpenAI, and any external provider (OpenRouter, Together AI, Baidu AI, etc.) all share one adapter β OpenAICompatAdapter β since they all speak the OpenAI API. The router in __init__.py just passes the right api_key and base_url per provider. Only Ollama (embedding support), Anthropic, and Gemini need their own adapter.
Model strings use a provider/model format. The Adapter class reads the prefix, routes to the right provider adapter, and strips the prefix before the API call. All adapters return the same Response object:
class Response(BaseModel):
content: str
tool_calls: list[dict] = []
reasoning: str = ''
model: str = ''
finish_reason: str = ''User Input
β
βΌ
MainAgent
β ββββββββββββββββββββββββββββββββββββ
ββββΊ Tools: web_search, fetch_text, β
β β read_file, write_file, β
β β edit_file, run_bash, β
β β grep, glob, ask_user, β
β β to_do β
β ββββββββββββββββββββββββββββββββββββ
β
βΌ
Response (streamed, rendered as Markdown)
Single agent loop with full tool access and user profile context. Fast and suitable for most tasks.
User Input
β
βΌ
PlannerAgent βββΊ [ task1, task2, task3, ... ]
β
βΌ
ReasoningStep (main agent thinks through the plan)
- Are tasks in the right order?
- Dependencies between tasks?
- Risks or constraints executors should know?
β
βΌ (asyncio.gather β all run in parallel, with reasoning as context)
ExecutorAgent-1 ExecutorAgent-2 ExecutorAgent-N
β β β
ββββββββββββββββββββ΄βββββββββββββββββββ
β
βΌ
EvaluatorAgent
β
βββββββββββ΄βββββββββββ
PASS FAIL (up to 3 rounds)
β β
βΌ βΌ
Synthesize ExecutorAgent x M (fix issues, parallel)
β β
βΌ βΌ
Response EvaluatorAgent (re-evaluate)
Deep mode adds a reasoning step between planning and execution. After the planner produces tasks, the main agent thinks through the plan β checking task order, dependencies, and risks β before executors start. This reasoning is passed to every executor as additional context. The prompt for this step lives in prompts/reasoning.md and can be edited to tune the behaviour.
.
βββ main.py # Entry point (async REPL, banner, prompt_toolkit session)
βββ requirements.txt
βββ Docker-compose.yml # PostgreSQL + pgvector
βββ init.sql # DB schema (auto-run on first start)
βββ .env.example
β
βββ adapters/
β βββ __init__.py # Adapter: unified router by provider prefix
β βββ schema.py # Response dataclass (canonical output format)
β βββ base_adapter.py # Abstract base adapter interface
β βββ openai_compat_adapter.py # DeepSeek, OpenAI, OpenRouter, etc.
β βββ ollama_adapter.py # Ollama provider adapter (+ embeddings)
β βββ anthropic_adapter.py # Anthropic provider adapter
β βββ gemini_adapter.py # Gemini provider adapter
β
βββ agent/
β βββ base.py # Base Agent: Rich.Live streaming, tool dispatch, depth limit
β βββ loop.py # MainAgent: memory, profile, simple/deep routing, token summary
β βββ planner.py # PlannerAgent: structured task decomposition
β βββ executor.py # ExecutorAgent: executes one task with tools
β βββ evaluator.py # EvaluatorAgent: structured pass/fail evaluation
β βββ compact.py # Compactor: summarises conversation history
β βββ profile.py # ProfileManager: background user profiling
β
βββ cli/
β βββ commands.py # CLI command handler (/help, /model, /compact, etc.)
β βββ completions.py # prompt_toolkit SlashCompleter + FillBgProcessor
β βββ renderer.py # print_user_message (dark-bg submitted line)
β βββ theme.py # Shared Rich Console + named theme styles
β βββ diff.py # Claude Code-style red/green diff renderer
β
βββ common/
β βββ config.py # Loads .env and prompt files
β
βββ memory/
β βββ db.py # asyncpg pool, history, knowledge, profiles
β
βββ tools/
β βββ manager.py # Tool definitions, named tool sets, styled confirmations
β βββ crawl.py # web_search, fetch_text, fetch_html (PDF via pymupdf)
β βββ todo.py # ToDoManager + to_do tool
β βββ skill_manager.py # Skills loader
β
βββ prompts/
β βββ agent.md # Main agent system prompt
β βββ subagent.md # Subagent system prompt
β βββ planner.md # Planner instructions
β βββ executor.md # Executor instructions
β βββ evaluator.md # Evaluator instructions
β βββ compact.md # Compaction instructions
β βββ profile.md # Profile update instructions
β
βββ skills/ # Skill definitions (SKILL.md + examples + templates)
| Agent | Tools |
|---|---|
MainAgent (/simple) |
run_bash, read_file, write_file, edit_file, grep, glob, web_search, fetch_text, ask_user, to_do |
ExecutorAgent (/deep) |
run_bash, read_file, write_file, edit_file, grep, glob, web_search, fetch_text |
| PlannerAgent | none |
| EvaluatorAgent | none |
| Command | Description |
|---|---|
/help |
Show all available commands |
/exit |
Exit the agent |
/simple |
Switch to simple mode (single agent, fast) |
/deep |
Switch to deep mode (multi-agent, thorough) |
/profile |
View your current user profile |
/delete-profile |
Delete your saved user profile |
/clear-history |
Delete all conversation history |
/compact |
Summarise and compress conversation history |
/compact "focus on X" |
Compact with extra instructions |
/context-window <n|off> |
Limit history to last N messages; off removes the limit |
/model |
Show all current model settings |
/model <provider/model> |
Change the main model (e.g. /model deepseek/deepseek-chat) |
/model <role> <provider/model> |
Change a sub-model: compact, planner, evaluator, profile |
/model all <provider/model> |
Set all models (main + all sub-models) to the same value |
/apikey deepseek <key> |
Update DeepSeek API key β writes directly to .env |
/apikey ollama <key> |
Update Ollama API key β writes directly to .env |
/apikey external <key> |
Update external provider API key β writes directly to .env |
| Dependency | Why |
|---|---|
| Python 3.10+ | Runs the agent |
| Docker Desktop | Runs the PostgreSQL + pgvector database |
| Ollama | Only needed if using local models |
| Node.js 18+ | Only needed for WeChat ACP bridge |
git clone https://github.com/YSMsimon/this-is-my-agent.git
cd this-is-my-agent
pip install -r requirements.txtdocker compose up -dThis starts a PostgreSQL instance with the pgvector extension on port 5433. The init.sql file sets up the required tables automatically on first run.
cp .env.example .envEdit .env β model strings use provider/model format:
# ββ Models βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MODEL=deepseek/deepseek-chat
EMBEDDING_MODEL=ollama/nomic-embed-text
PROFILE_MODEL=deepseek/deepseek-chat
COMPACT_MODEL=deepseek/deepseek-chat
PLANNER_MODEL=deepseek/deepseek-chat
EVALUATOR_MODEL=deepseek/deepseek-chat
# ββ Database βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
DATABASE_URL=postgresql://myuser:mypassword@localhost:5433/agent_memory
# ββ API keys (fill in the provider(s) you use) βββββββββββββββββββββββββββββββ
DEEPSEEK_API_KEY=...
# ββ External provider (OpenRouter, Together AI, Baidu AI, etc.) βββββββββββββββ
# EXTERNAL_BASE_URL=https://openrouter.ai/api/v1
# EXTERNAL_API_KEY=sk-...
# ββ Ollama (only if not on default localhost:11434) βββββββββββββββββββββββββββ
# OLLAMA_BASE_URL=http://localhost:11434/v1python3 main.pyModel strings follow a provider/model format. The adapter layer reads the prefix, routes to the correct provider, and strips it before the API call.
| Provider | Format | Example |
|---|---|---|
| Ollama | ollama/model |
ollama/llama3.2 |
| DeepSeek | deepseek/model |
deepseek/deepseek-chat |
| OpenAI | openai/model |
openai/gpt-4o |
| External | external/model |
external/meta-llama/llama-3.1-70b-instruct |
| Variable | Description |
|---|---|
MODEL |
Main chat model |
EMBEDDING_MODEL |
Embedding model for RAG |
PROFILE_MODEL |
Model for background profile updates |
COMPACT_MODEL |
Model for history compaction |
PLANNER_MODEL |
Model for task planning (deep mode) |
EVALUATOR_MODEL |
Model for result evaluation (deep mode) |
DATABASE_URL |
PostgreSQL connection string |
DEEPSEEK_API_KEY |
DeepSeek API key |
OPENAI_API_KEY |
OpenAI API key |
OLLAMA_BASE_URL |
Custom Ollama URL (default: http://localhost:11434/v1) |
OLLAMA_API_KEY |
Ollama API key (default: dummy) |
EXTERNAL_BASE_URL |
Base URL for any OpenAI-compatible provider (e.g. OpenRouter, Together AI) |
EXTERNAL_API_KEY |
API key for the external provider |
Known issue: CLI commands (e.g.
/exit,/help) do not currently work when sent through the WeChat ACP interface. Use the terminal REPL for commands.
1. Edit wechat-acp.config.json and replace the placeholder paths with your own:
{
"agent": "python3 /YOUR/ABSOLUTE/PATH/this-is-my-agent/acp_agent.py",
"cwd": "/YOUR/ABSOLUTE/PATH/this-is-my-agent"
}2. Start the ACP bridge:
- Mac/Linux:
./start-wechat-acp.sh - Windows (PowerShell):
.\start-wechat-acp.ps1
Node.js must be installed for npx to work.