buren-chatbot-mcp
by Nominjinhon
README.md
# Зээлийн чатбот (backend)
Банкны зээлийн мэдээлэл лавлах чатботын backend систем. Хэрэглэгч (харилцагч) зээлийн төлөв, хугацаа хэтэрсэн төлбөр, өр-орлогын харьцаа (DTI) зэрэг асуултыг байгалийн хэлээр асуухад, LangGraph агент нь MCP (Model Context Protocol) серверээр дамжуулан бодит (mock) өгөгдлөөс хариулт бүрдүүлдэг.
> **Анхаарах зүйл:** Энэ бол демо/тестийн систем. Жинхэнэ өгөгдлийн сан, гадаад банкны API, нэвтрэлт (authentication) байхгүй. `customer_id` нь хүсэлтийн биед шууд дамждаг (аль хэдийн нэвтэрсэн session-г төлөөлнө).
## Технологийн стек
- **Python 3.11+**, dependency management: `uv` (`pyproject.toml` + `uv.lock`)
- **FastAPI** - HTTP API
- **PostgreSQL** + **SQLAlchemy (async) + Alembic** - өгөгдлийн сан, migration
- **LangGraph** - агентын orchestration (state graph)
- **LangChain** + **langchain-google-genai** - prompt, LLM холболт (Gemini)
- **MCP Python SDK** - зээл/аналитик/харилцагчийн мэдээллийг ил гаргах read-only tool сервер (stdio transport)
- **Pydantic v2** - өгөгдлийн баталгаажуулалт
- **Gradio** - гар аргаар туршиж үзэх UI (`/ui`), үндсэн FastAPI дээр mount хийгдсэн
- **pytest + pytest-asyncio** - тест
- **Docker + docker-compose** - контейнержүүлэлт
## Төслийн бүтэц
```
app/
├── agent/ # LangGraph: state, prompts, nodes, graph
├── api/ # FastAPI route-ууд, dependency-ууд
├── services/ # loan_service, analytics_service (детерминист!), mcp_client
├── mcp_servers/ # 1 read-only MCP сервер (server.py)
├── config/ # Тохиргоо (Pydantic Settings)
├── database/ # SQLAlchemy models, async session, seed.py
├── schemas/ # Pydantic v2 schema-ууд
├── ui.py # Gradio UI, /ui дээр FastAPI-д mount хийгдсэн
└── main.py # FastAPI entrypoint
alembic/ # Migration-ууд
tests/ # pytest тестүүд (бүгд offline ажилладаг)
```
## Өгөгдлийн сан ба сервер архитектур
Энэ төсөл **хостын машин дээрх бодит PostgreSQL**-ийг ашигладаг (тусдаа docker-с удирдагдах `db` сервис биш). `docker-compose.yml` нь зөвхөн `app` (FastAPI) сервисийг агуулж, `host.docker.internal`-ээр дамжуулан хостын Postgres-т холбогддог (`extra_hosts: host.docker.internal:host-gateway`).
Migration нь **Alembic скрипт** хэлбэрээр хийгддэг (`alembic/versions/`), харин mock өгөгдлийг **`python -m app.database.seed` скрипт** үүсгэдэг (Alembic data migration биш) - учир нь энэ нь дахин ажиллуулахад аюулгүй (idempotent) бөгөөд `--reset` флагаар дахин үүсгэх боломжтой.
## Суулгах, тохируулах
### 1. Хостын Postgres дээр role/database үүсгэх (нэг удаа)
```bash
sudo -u postgres psql -c "CREATE ROLE buren LOGIN PASSWORD 'buren';"
-c "CREATE DATABASE buren_chatbot OWNER buren;"
```
### 2. `.env` файл үүсгэх
```bash
cp .env.example .env
```
Дараа нь `.env`-д өөрийн `MODEL_API_KEY`-г (Gemini API key, [Google AI Studio](https://aistudio.google.com/)-с авна) бичнэ үү. Жинхэнэ LLM дуудлага хийхэд шаардлагатай (үгүй бол `/chat` нь "API key required for Gemini Developer API" гэсэн 500 алдаа буцаана - энэ бол зөв ажиллаж байгааг илэрхийлнэ, гэхдээ LLM хариу өгөхгүй).
`.env.example`-ийн агуулга:
```env
DATABASE_URL=postgresql+psycopg://buren:buren@host.docker.internal:5432/buren_chatbot
MODEL=gemini-2.0-flash
MODEL_API_KEY=
MCP_SERVER_PATH=
# Заавал биш: LangSmith трэйсинг
LANGSMITH_TRACING=
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=
LANGSMITH_PROJECT=
```
Локал (Docker-гүй) хөгжүүлэлт хийх үед `DATABASE_URL`-д `host.docker.internal` оронд `localhost`-г ашиглана:
```bash
export DATABASE_URL="postgresql+psycopg://buren:buren@localhost:5432/buren_chatbot"
```
`MCP_SERVER_PATH`-г хоосон орхивол програм өөрөө `python -m app.mcp_servers.server`-ээр серверийг ажиллуулна (custom скрипт зам зааж өгвөл түүнийг ашиглана).
### 3. Хамааралтай сангуудыг суулгах
```bash
uv sync
```
## Migration ажиллуулах
```bash
uv run alembic upgrade head
```
## Mock өгөгдөл үүсгэх (seed)
```bash
uv run python -m app.database.seed # аль хэдийн байгаа бол алгасна
uv run python -m app.database.seed --reset # устгаад дахин үүсгэнэ
```
Энэ нь 5 харилцагч (сар бүрийн орлого 800,000-5,000,000₮), тус бүрд нь 5-10 зээл (идэвхтэй/хугацаа хэтэрсэн/хаагдсан холимог), сүүлийн 12 сарын төлбөрийн түүхийн хамт үүсгэдэг. Дор хаяж 2 харилцагчид хугацаа хэтэрсэн зээл байгаа. Харилцагчийн ID-ууд тогтмол (`uuid5` ашигласан тул дахин seed хийхэд ижил хэвээр байна) - гараар тестлэхэд хэрэг болно.
## Тест ажиллуулах
```bash
uv run pytest tests/ -v
```
Бүх тест **offline** ажилладаг - жинхэнэ Postgres, жинхэнэ Gemini key, жинхэнэ MCP дэд процесс хэрэггүй (in-memory SQLite болон fake LLM/mocked MCP client ашигладаг):
- `test_loan_service.py`, `test_analytics_service.py` - services давхарга (DTI тооцоолол нь LLM-гүйгээр, детерминист код гэдгийг батална)
- `test_agent_graph.py` - LangGraph граф, fake LLM-тэй тусгаарлагдсан
- `test_chat_api.py` - `/chat` endpoint, HTTP-ээр бодит хүсэлт илгээж, зөв intent → зөв MCP tool дуудагдсаныг шалгана
- `test_agent_nodes_helpers.py`, `test_mcp_client.py` - жижиг дотоод функцүүдийн regression тест (LLM-ийн хариу боловсруулалт, MCP дэд процессын орчны хувьсагч дамжуулалт)
## Локал сервер ажиллуулах (Docker-гүйгээр)
```bash
export DATABASE_URL="postgresql+psycopg://buren:buren@localhost:5432/buren_chatbot"
export MODEL_API_KEY="AIza..."
./run.sh # эсвэл: uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
```
`run.sh` нь FastAPI серверийг 8000 порт дээр ажиллуулна (migration/seed хийдэггүй - тэдгээрийг дээрх алхмуудад тусад нь хийсэн байх ёстой).
## Gradio UI
Сервер ажиллаж байхад `http://127.0.0.1:8000/ui` хаягаар нэвтэрч, curl бичихгүйгээр браузер дээрээс шууд туршиж болно:
- Харилцагч сонгох dropdown - сонгосон харилцагчийн бүх зээлийн мэдээлэл (төрөл, төлөв, үлдэгдэл, сар бүрийн төлбөр), DTI харьцаа хажуу талд шууд харагдана.
- Чат цонх - чөлөөт бичвэрээр асуулт бичих, эсвэл хажуугийн санал болгож буй асуултууд дээр дарж шууд илгээх боломжтой.
Энэ нь тусдаа процесс биш - `/chat` endpoint-той адил FastAPI апп дотор ажилладаг тул `run.sh`, `docker compose up` ямар ч тохиргоо нэмэлтгүйгээр `/ui`-г мөн ажиллуулна.
## LangSmith трэйсинг (заавал биш)
Агентын граф (intent ангилал, tool дуудлага, LLM хариу) бүрийг [LangSmith](https://smith.langchain.com/)-т трэйс хэлбэрээр илгээж болно - код өөрчлөх шаардлагагүй, зөвхөн `.env`-д доорх орчны хувьсагчдыг тохируулна:
```env
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=lsv2_...
LANGSMITH_PROJECT=<төслийн нэр>
```
`LANGSMITH_TRACING`-г хоосон орхивол (эсвэл огт бичихгүй бол) трэйсинг идэвхгүй байна - LangChain/LangGraph эдгээр хувьсагчийг байхгүй бол ямар ч нэмэлт зан үйлгүй хэвээрээ ажиллана.
**Анхаарах зүйл:** `Settings` (Pydantic Settings) нь `.env`-ээс зөвхөн өөрийн зарласан талбаруудыг (жишээ нь `database_url`, `model`) уншдаг бөгөөд бусад орчны хувьсагчийг жинхэнэ процессын environment рүү экспортлодоггүй. LangSmith SDK нь `os.environ`-оос шууд уншдаг тул `app/config/settings.py`-д `load_dotenv()`-г эхэнд нь дуудсан - ингэснээр `.env` доторх бүх түлхүүр (LangSmith-ийнх байх, эсвэл ирээдүйд нэмэгдэх бусад) жинхэнэ орчны хувьсагч болж, Docker дотор `env_file`-ээр аль хэдийн дамжуулагдсан утгыг дарж бичихгүй.
## Docker Compose-оор ажиллуулах
```bash
cp .env.example .env # .env дотор MODEL_API_KEY-г бөглөнө
docker compose up --build
```
Контейнер эхлэхэд автоматаар: хостын Postgres-г хүлээх → `alembic upgrade head` → seed скрипт (хоосон бол л ажиллана) → FastAPI сервер асна.
## API жишээ
### `GET /health`
```bash
curl http://127.0.0.1:8000/health
```
### `POST /chat`
```bash
curl -X POST http://127.0.0.1:8000/chat
-H "Content-Type: application/json"
-d '{
"customer_id": "f430bc26-9001-5579-bc8e-7051ff612b88",
"message": "Миний DTI хэд вэ?"
}'
```
Жишээ хариу:
```json
{
"response": "Таны өр-орлогын харьцаа (DTI) 31.72% байна. Энэ мэдээлэл нь зөвхөн лавлагааны зорилготой бөгөөд зээлийн шийдвэр гаргах үндэслэл болохгүй.",
"intent": "DTI_RATIO",
"data": {
"dti": {
"customer_id": "f430bc26-9001-5579-bc8e-7051ff612b88",
"monthly_income": 950000.0,
"total_monthly_debt_payments": 301339.04,
"dti_ratio": 31.72
}
}
}
```
Дэмжигдсэн асуултын төрлүүд (`intent`):
Intent
Жишээ асуулт
`ACTIVE_LOANS`
"Миний идэвхтэй зээлүүд юу вэ?"
`OVERDUE_STATUS`
"Надад хугацаа хэтэрсэн төлбөр байна уу?"
`DTI_RATIO`
"Миний өр-орлогын харьцаа хэд вэ?"
`LOAN_DETAIL`
"Миний машины зээлийн талаар хэлж өгөөч"
`GENERAL_SUMMARY`
"Миний зээлийн ерөнхий байдлыг хэлж өгөөч"
`OUT_OF_SCOPE`
Зээлтэй холбоогүй асуулт (шинэ зээл авах, цаг агаар г.м)
Хариу нь хэрэглэгчийн бичсэн хэлийг (монгол/англи) тольдоно.
## Чухал зарчмууд
- **DTI тооцоолол нь бодит код** (`services/analytics_service.py`) хийдэг - LLM тоо тооцдоггүй, зөвхөн бэлэн үр дүнг байгалийн хэлээр илэрхийлдэг.
- Санхүүгийн бүх хариу нь **сануулга өгүүлбэртэй** дуусна ("Энэ мэдээлэл нь зөвхөн лавлагааны зорилготой...") - энэ нь prompt-д бичигдсэн, кодоор залгаагүй тул хожим засварлахад хялбар.
- MCP сервер **зөвхөн унших** (read-only) - write/update/delete tool байхгүй.
- Бүх мөнгөн дүн **MNT (төгрөг)**-өөр илэрхийлэгдэнэ, валют хөрвүүлэлт хийгддэггүй.
## Хөгжүүлэлтийн талаар нэмэлт мэдээлэл
`AGENTS.md` файлд төслийн дизайны шийдвэрүүд, нарийн ширийн зүйлс (жишээ нь: "идэвхтэй зээл" гэдэг нь яг `status == 'active'` гэсэн үг, `overdue`-с ялгаатай; MCP хариуны JSON бүтцийн конвенц г.м) илүү дэлгэрэнгүй бичигдсэн.This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues