KinglittleQ/catbot

A minimal Python agent framework with Feishu support

β˜… 3Forks 0PythonGitHub β†—Compare

README

catbot 🐱

A minimal Python agent framework with Feishu support β€” inspired by openclaw.

Python License

Overview

catbot is a clean Python implementation of the openclaw agent architecture:

  • Agent loop β€” think β†’ tool call β†’ observe β†’ repeat (like openclaw's pi-agent-core)
  • openclaw-style session keys β€” agent:<id>:<channel>:<type>:<chat_id>
  • Compaction β€” summarize old messages when context window fills up (mirrors openclaw's compaction.ts)
  • Memory β€” MEMORY.md (long-term facts) + HISTORY.md (event log)
  • Feishu WebSocket β€” native lark-oapi integration, no public server needed
  • Middleware chain β€” rate limiting, allowlists, logging (openclaw's send-policy)
  • Multi-provider β€” OpenAI-compatible + Anthropic (with prompt caching)

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    CHANNELS                          β”‚
β”‚         Feishu (WebSocket)    CLI    (custom)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚ IncomingMessage
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    GATEWAY                            β”‚
β”‚  Session keys β€’ Middleware chain β€’ Send policy        β”‚
β”‚  Concurrency control β€’ Channel routing               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό                             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  SESSION STORE    β”‚     β”‚        MEMORY             β”‚
β”‚                   β”‚     β”‚                           β”‚
β”‚ β€’ JSONL append    β”‚     β”‚ β€’ SOUL.md (personality)   β”‚
β”‚ β€’ openclaw keys   β”‚     β”‚ β€’ AGENTS.md (instructions)β”‚
β”‚ β€’ Compaction      β”‚     β”‚ β€’ MEMORY.md (long-term)   β”‚
β”‚ β€’ Daily reset     β”‚     β”‚ β€’ HISTORY.md (log)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     AGENT                             β”‚
β”‚   Build system prompt β†’ LLM β†’ Tool calls β†’ Loop      β”‚
β”‚   Compaction trigger β€’ on_tool_call callbacks        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β–Ό                             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   PROVIDERS   β”‚           β”‚      TOOLS          β”‚
β”‚               β”‚           β”‚                     β”‚
β”‚ β€’ OpenAI      β”‚           β”‚ β€’ @tool decorator   β”‚
β”‚ β€’ Anthropic   β”‚           β”‚ β€’ Auto JSON schema  β”‚
β”‚   (+ caching) β”‚           β”‚ β€’ read/write/exec   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start

pip install catbot
export OPENAI_API_KEY=sk-...
python examples/hello_world.py

Or in 5 lines:

import asyncio, os
from catbot import Agent, Gateway, GatewayConfig
from catbot.channels.cli import CLIChannel
from catbot.providers.openai import OpenAIProvider

agent = Agent(provider=OpenAIProvider(api_key=os.environ["OPENAI_API_KEY"]))
gw = Gateway(agent=agent)
gw.add_channel(CLIChannel())
asyncio.run(gw.run())

Feishu Setup

  1. Create an app at open.feishu.cn
  2. Enable Bot capability
  3. Subscribe to im.message.receive_v1 event
  4. Set connection mode to WebSocket (ι•ΏθΏžζŽ₯樑式)
  5. Grant permissions: im:message, im:message:send_as_bot
pip install "catbot[feishu]"
export FEISHU_APP_ID=cli_xxx
export FEISHU_APP_SECRET=xxx
export ANTHROPIC_API_KEY=sk-ant-xxx
python examples/feishu_bot.py

Features:

  • πŸ‘€ reaction when processing, βœ… when done
  • Group @ detection (only_at_in_group=True)
  • Supports text, image, file messages
  • No public server needed (WebSocket long connection)

Custom Tools

from catbot import tool, ToolRegistry

@tool()
async def search_web(query: str, max_results: int = 5) -> str:
    """Search the web for information.
    
    query: The search query.
    max_results: Maximum number of results to return.
    """
    # your implementation
    return results

tools = ToolRegistry()
tools.register(search_web)
agent = Agent(provider=..., tools=tools)

Middleware

from catbot import Gateway, rate_limit, allow_senders, log_messages

gw = Gateway(agent=agent)
gw.use(log_messages())                          # Log all messages
gw.use(rate_limit(max_per_minute=10))           # Rate limit
gw.use(allow_senders(["ou_abc123", "ou_xyz"]))  # Allowlist

Session Keys (openclaw-compatible)

Session keys follow openclaw's format:

Format Example Use case
agent:main:feishu:direct:<openId> DM with user 1:1 chat
agent:main:feishu:group:<chatId> Group chat Multi-user
agent:main:cli:direct:local CLI session Testing
agent:main:cron:cron:<jobId> Cron job Scheduled tasks

Memory / Workspace

catbot uses the same workspace file convention as openclaw:

~/.catbot/workspace/
β”œβ”€β”€ SOUL.md      # Agent personality (loaded into system prompt)
β”œβ”€β”€ AGENTS.md    # Agent instructions
β”œβ”€β”€ USER.md      # User context
└── memory/
    β”œβ”€β”€ MEMORY.md    # Long-term facts (loaded every turn)
    └── HISTORY.md   # Append-only event log (grep-searchable)

Compaction

When the session token estimate exceeds 70% of the context window, catbot automatically compacts old messages:

  1. Summarize messages [0 .. -keep_last] via LLM
  2. Replace with a summary system message
  3. Keep the last keep_last messages verbatim

This mirrors openclaw's compaction.ts behavior.

Providers

OpenAI-compatible

from catbot.providers.openai import OpenAIProvider

# OpenAI
p = OpenAIProvider(api_key="sk-...", model="gpt-4o")

# DeepSeek
p = OpenAIProvider(
    api_key="sk-...",
    api_base="https://api.deepseek.com/v1",
    model="deepseek-chat",
)

Anthropic (with prompt caching)

from catbot.providers.anthropic import AnthropicProvider

p = AnthropicProvider(
    api_key="sk-ant-...",
    model="claude-opus-4-5",
    enable_cache=True,   # Adds cache_control to system + last N user messages
)

Installation

# Core (OpenAI)
pip install catbot

# With Anthropic
pip install "catbot[anthropic]"

# With Feishu
pip install "catbot[feishu]"

# Everything
pip install "catbot[all]"

License

MIT

Issues