Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .tasks/2026-03-09_smart-customer-service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Background
File: 2026-03-09_smart-customer-service
Created: 2026-03-09
Task Branch: cursor/development-environment-setup-880d
Yolo Mode: On

# Task Description
Develop an intelligent customer service plugin (astrbot_plugin_smart_customer_service) for AstrBot, based on the Sales Skill system. Combines AI-powered auto-response with human agent escalation.

# Reference Plugins
- astrbot_plugin_astrbot_enhance_mode (by Axi404): LLM deep integration, Memory RAG, LLM Tools, output tag parsing
- astrbot_plugin_human_service (by Zhalslar): Human service queue, session routing, blacklist, timeout

# Architecture
- AI-first, human-fallback hybrid model
- 4-state session machine: idle → ai_serving → waiting_human → human_connected → closed
- Sales Skill (SKILL.md) for LLM behavior guidance
- LLM Tools for product query, order check, escalation, customer profiling
- Three-layer design: Entry → Managers → Helpers/Tools

# Implementation Checklist
1. [x] Save research report and development plan
2. [ ] Create plugin skeleton (directory, metadata.yaml, _conf_schema.json)
3. [ ] Implement SessionManager (4-state machine + SQLite)
4. [ ] Implement ConversationContextManager (history + LLM injection)
5. [ ] Implement CustomerProfileManager (CRUD + SQLite)
6. [ ] Implement ProductManager (product data + query)
7. [ ] Implement LLM Tools (query_product, check_order, escalate_to_human, update_customer_profile, create_lead)
8. [ ] Implement EscalationManager (queue + agent accept)
9. [ ] Implement MessageRouter + TimeoutManager
10. [ ] Implement main.py entry (event hooks + commands)
11. [ ] Write Sales Skill template (SKILL.md)
12. [ ] Write _conf_schema.json + HelpTextBuilder + README
13. [ ] Integration test
14. [ ] Lint, format, commit and push

# Task Progress
[2026-03-09 00:20:00]
- Created: Research report and development plan
- Status: Confirmed
24 changes: 24 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,27 @@ Runs on `http://localhost:3000` by default.

1. Title format: use conventional commit messages
2. Use English to write PR title and descriptions.

## Cursor Cloud specific instructions

### Services overview

| Service | Port | Start command |
|---|---|---|
| AstrBot Core (Python backend) | 6185 | `uv run main.py` |
| Dashboard (Vue dev server) | 3000 | `cd dashboard && pnpm dev` |

The backend uses embedded SQLite — no external database needed.

### Running the application

1. **Backend**: `uv run main.py` starts the API server on `http://localhost:6185`. Default credentials: username `astrbot`, password `astrbot`. On first login, a password change is mandatory.
2. **Dashboard dev server**: `cd dashboard && pnpm dev` proxies `/api` to `localhost:6185`. Only needed when developing the Vue frontend.

### Gotchas

- `uv` must be on `PATH`. It is installed to `~/.local/bin` which is added via `~/.local/bin/env`.
- After `pnpm install` in `dashboard/`, pnpm v10+ blocks build scripts for `esbuild` and `vue-demi`. Run `pnpm install` with the `--config.confirmModulesPurge=false` flag, or configure `pnpm.onlyBuiltDependencies` in `package.json` to allowlist `["esbuild","vue-demi"]` to unblock the Vite bundler.
- Lint: `uv run ruff format --check .` and `uv run ruff check .`.
- Tests: `TESTING=true uv run pytest -q tests/` (665 tests). Create `data/{plugins,config,temp,skills}` dirs before running tests.
- PR validation: `make pr-test-neo` (quick) or `make pr-test-full` (comprehensive). See `CONTRIBUTING.md`.
72 changes: 72 additions & 0 deletions astrbot_plugin_smart_customer_service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# AstrBot Smart Customer Service Plugin

AI-powered intelligent customer service plugin with sales skill integration, customer profiling, and human agent escalation.

## Features

- **AI-First**: Uses LLM with Sales Skill to handle customer inquiries automatically
- **Human Escalation**: Seamless transfer to human agents with queue management
- **Customer Profiling**: SQLite-backed customer profiles with tags and lead tracking
- **Product Knowledge**: JSON-based product catalog with search capabilities
- **LLM Tools**: AI can query products, check orders, create leads, and escalate
- **Session Management**: 4-state session machine (idle → ai_serving → waiting_human → human_connected)
- **Configurable**: Dashboard-friendly configuration schema

## Commands

### User Commands
| Command | Description |
|---------|-------------|
| `/ask <question>` | Ask the AI assistant |
| `/transfer_human` | Request a human agent |
| `/cancel_queue` | Cancel queue for human agent |
| `/queue_status` | Check queue position |
| `/end_service` | End current session |
| `/cs_help` | Show help |

### Agent Commands
| Command | Description |
|---------|-------------|
| `/accept_customer` | Accept next customer in queue |
| `/end_service` | End current customer session |
| `/queue_list` | View waiting queue |
| `/customer_info` | View current customer profile |
| `/cs_help` | Show help |

## Configuration

Configure via AstrBot Dashboard or `_conf_schema.json`:

- `agent_ids`: List of human agent user IDs
- `agent_names`: Display names for agents
- `max_sessions`: Max concurrent sessions (default: 200)
- `max_context_messages`: Max messages per session (default: 50)
- `conversation_timeout`: Auto-close timeout in seconds (0 = disabled)
- `queue_timeout`: Queue timeout in seconds (0 = disabled)

## Architecture

```
main.py (Entry + Event Hooks + LLM Tools)
├── managers/
│ ├── session_manager.py (4-state session machine)
│ ├── conversation_context_manager.py (LLM context injection)
│ ├── customer_profile_manager.py (SQLite profiles)
│ ├── product_manager.py (JSON product catalog)
│ ├── escalation_manager.py (Queue management)
│ └── timeout_manager.py (Timeout tracking)
├── helpers/
│ ├── message_router.py (Message routing)
│ └── help_text_builder.py (Help text)
└── skills/
└── sales_skill_template/SKILL.md (Sales behavior guide)
```

## Sales Skill

The plugin includes a Sales Skill template (`SKILL.md`) that guides the AI to:
1. Greet customers and discover needs
2. Recommend products using `query_product` tool
3. Handle objections with factual information
4. Capture leads with `create_lead` tool
5. Escalate to humans when appropriate
39 changes: 39 additions & 0 deletions astrbot_plugin_smart_customer_service/_conf_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"agent_ids": {
"description": "List of human agent user IDs who can accept customer sessions",
"type": "list",
"default": [],
"item_type": "string"
},
"agent_names": {
"description": "Display names for agents (same order as agent_ids)",
"type": "list",
"default": [],
"item_type": "string"
},
"max_sessions": {
"description": "Maximum number of concurrent sessions to track",
"type": "int",
"default": 200
},
"max_context_messages": {
"description": "Maximum conversation messages to keep per session",
"type": "int",
"default": 50
},
"conversation_timeout": {
"description": "Auto-close session after this many seconds (0 = disabled)",
"type": "int",
"default": 0
},
"timeout_warning_seconds": {
"description": "Send warning this many seconds before timeout",
"type": "int",
"default": 120
},
"queue_timeout": {
"description": "Remove from queue after this many seconds (0 = disabled)",
"type": "int",
"default": 0
}
}
7 changes: 7 additions & 0 deletions astrbot_plugin_smart_customer_service/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .help_text_builder import HelpTextBuilder
from .message_router import MessageRouter

__all__ = [
"HelpTextBuilder",
"MessageRouter",
]
30 changes: 30 additions & 0 deletions astrbot_plugin_smart_customer_service/helpers/help_text_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Builds help text for users and agents."""

from __future__ import annotations


class HelpTextBuilder:
@staticmethod
def build_user_help() -> str:
return (
"=== Smart Customer Service Help ===\n"
"Available commands:\n"
" /ask <question> - Ask AI assistant a question\n"
" /transfer_human - Request a human agent\n"
" /cancel_queue - Cancel queue for human agent\n"
" /queue_status - Check queue position\n"
" /end_service - End current service session\n"
" /cs_help - Show this help message\n"
)

@staticmethod
def build_agent_help() -> str:
return (
"=== Agent Help ===\n"
"Available commands:\n"
" /accept_customer - Accept next customer in queue\n"
" /end_service - End current customer session\n"
" /queue_list - View waiting queue\n"
" /customer_info - View current customer profile\n"
" /cs_help - Show this help message\n"
)
108 changes: 108 additions & 0 deletions astrbot_plugin_smart_customer_service/helpers/message_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Routes messages between customers and human agents."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from astrbot.api.event import AstrMessageEvent

from ..managers.session_manager import SessionState

if TYPE_CHECKING:
from ..main import SmartCustomerServicePlugin

logger = logging.getLogger("astrbot")


class MessageRouter:
"""Decides how to handle an incoming message based on session state."""

def __init__(self, plugin: SmartCustomerServicePlugin) -> None:
self.plugin = plugin

async def route(self, event: AstrMessageEvent) -> bool:
"""Route the message. Returns True if handled (event should stop)."""
sender_id = str(event.get_sender_id())
group_id = str(getattr(event, "get_group_id", lambda: "")() or "")
platform = event.session.platform_id

if self.plugin.escalation_mgr.is_agent(sender_id):
return await self._route_agent_message(event, sender_id)

session = self.plugin.session_mgr.get_session(platform, group_id, sender_id)
if session is None:
return False

if session.state == SessionState.AI_SERVING:
return False

if session.state == SessionState.HUMAN_CONNECTED:
return await self._forward_to_agent(event, session)

if session.state == SessionState.WAITING_HUMAN:
await event.send(
event.make_result().message(
"You are currently in queue for a human agent. "
"Please wait or type /cancel_queue to cancel."
)
)
event.stop_event()
return True

return False

async def _forward_to_agent(self, event: AstrMessageEvent, session) -> bool:
"""Forward customer message to the connected agent."""
if not session.agent_id:
return False
agent_name = self.plugin.escalation_mgr.get_agent_name(session.agent_id)
user_name = session.user_name or str(event.get_sender_id())
content = event.message_str
self.plugin.context_mgr.append(
session.session_key,
"user",
content,
sender_name=user_name,
)
try:
msg_chain = [
{"type": "text", "data": f"[Customer: {user_name}]\n{content}"}
]
await self.plugin.context.send_message(
event.session,
msg_chain,
)
logger.info(f"Forwarded message from {user_name} to agent {agent_name}")
except Exception as e:
logger.error(f"Failed to forward message to agent: {e}")
event.stop_event()
return True

async def _route_agent_message(
self, event: AstrMessageEvent, agent_id: str
) -> bool:
"""Handle message from a human agent, forward to connected customer."""
sessions = self.plugin.session_mgr.get_sessions_by_agent(agent_id)
if not sessions:
return False

session = sessions[0]
content = event.message_str
agent_name = self.plugin.escalation_mgr.get_agent_name(agent_id)
self.plugin.context_mgr.append(
session.session_key,
"agent",
content,
sender_name=agent_name,
)
try:
msg_chain = [{"type": "text", "data": f"[Agent: {agent_name}]\n{content}"}]
await self.plugin.context.send_message(
event.session,
msg_chain,
)
except Exception as e:
logger.error(f"Failed to forward agent message: {e}")
event.stop_event()
return True
Loading