Skip to content

Commit 5606401

Browse files
Александр СамофаловАлександр Самофалов
authored andcommitted
add README.md fast codex
1 parent 37d2e26 commit 5606401

4 files changed

Lines changed: 430 additions & 119 deletions

File tree

README.md

Lines changed: 262 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,276 @@
1-
# Union Agents
1+
# UnionChatBot
22

3-
This is a repo to develop Union Agents.
4-
5-
General idea is to create chatbot which can interact with AI models via YandexCloud API.
6-
Baseline idea is to help developers run agent. It is powered by ***YandexGPTAPI***, Redis, ChromaDB and Langfuse.
3+
`UnionChatBot` - REST-сервис с агентом на базе LangChain/LangGraph и YandexGPT API.
4+
Проект включает:
5+
- FastAPI API слой;
6+
- граф агента с RAG-обогащением;
7+
- Redis (semantic cache + rate limit);
8+
- ChromaDB (векторный поиск);
9+
- Postgres (чекпоинты состояния графа);
10+
- Langfuse (трейсинг и промпты).
711

8-
If you would like to check this bot go to tg link (sometimes i will run this for tests):
9-
> https://t.me/HelperUnionBot
10-
11-
- To start interact you need just to type /start or /начать.
12-
- If you want just to know something just start to type and you easily find out your answer.
12+
## Структура проекта
1313

14-
# UNDER CONSTRUCTION
14+
```text
15+
.
16+
├── src/
17+
│ ├── service/ # API, middleware, конфигурация, контекст приложения, логирование
18+
│ │ ├── api/
19+
│ │ │ ├── os_router.py # /health, /info
20+
│ │ │ ├── metric_router.py # /like, /dislike
21+
│ │ │ └── v1/
22+
│ │ │ ├── router.py # /api/v1/test_invoke, /api/v1/chat
23+
│ │ │ ├── schemas.py # Pydantic схемы запросов/ответов
24+
│ │ │ └── utils.py # обязательные service headers
25+
│ │ ├── context.py # инициализация клиентов и зависимостей
26+
│ │ └── config.py # настройки из env
27+
│ ├── agents/
28+
│ │ └── profkom_consultant/ # логика агентного графа
29+
│ │ ├── nodes/ # validate/decompose/answer/loop
30+
│ │ ├── workflow/base.py # сборка StateGraph
31+
│ │ └── states/ # состояние и статусы агента
32+
│ └── modules/
33+
│ ├── chroma_ext/ # адаптер Chroma + embeddings + rerank + sync-скрипты
34+
│ ├── redis_ext/ # semantic cache и rate limiter
35+
│ ├── postgres_ext/ # async pool и checkpointer для LangGraph
36+
│ └── langfuse_ext/ # клиент и callback Langfuse
37+
├── experiments/ # ноутбуки, nginx-конфиги, скрипты загрузки данных
38+
├── mlops/docker/ # Dockerfile и compose-файлы для VM/production/swarm
39+
├── .env.example # пример env для локальной разработки (с export)
40+
├── .env.prod.example # пример env для docker/prod (KEY=VALUE)
41+
└── pyproject.toml # зависимости и метаданные проекта
42+
```
43+
44+
## Модули и их назначение
45+
46+
### 1) `src/service`
47+
- Точка входа сервиса (`src/service/__main__.py`) и инициализация FastAPI.
48+
- Роутинг, middleware логирования и служебные ручки.
49+
- Централизованная конфигурация из переменных окружения (`APP_CONFIG`).
50+
- Контейнер зависимостей (`APP_CTX`) и lifecycle (`on_startup`/`on_shutdown`).
51+
- Логирование, метрики и аудит через `loguru`.
52+
53+
### 2) `src/agents/profkom_consultant`
54+
- Реализация агента `UnionAgent`.
55+
- Ноды графа:
56+
- валидация запроса/ответа;
57+
- декомпозиция вопроса;
58+
- генерация ответов по частям;
59+
- сборка финального ответа;
60+
- цикл "подумай еще раз", если ответ неполный.
61+
- Сборка `StateGraph` в `workflow/base.py`.
62+
63+
### 3) `src/modules/chroma_ext`
64+
- HTTP-адаптер к ChromaDB.
65+
- Поиск релевантных документов, фильтрация по distance, BM25 rerank.
66+
- Кастомный эмбеддер для Yandex embeddings API.
67+
- Скрипты для синхронизации `.docx` в коллекцию Chroma.
68+
69+
### 4) `src/modules/redis_ext`
70+
- Semantic cache для ответов/этапов агента.
71+
- Ограничение частоты запросов пользователя (`UserRateLimiter`).
72+
73+
### 5) `src/modules/postgres_ext`
74+
- Асинхронный connection pool.
75+
- Чекпоинтер `AsyncPostgresSaver` для хранения состояния графа по пользователю.
76+
77+
### 6) `src/modules/langfuse_ext`
78+
- Инициализация клиента Langfuse.
79+
- Callback handler для трейсинга вызовов LLM и работы графа.
80+
81+
### 7) `experiments`
82+
- Ноутбуки и прототипы по Yandex API, LangChain, Langfuse и логике агента.
83+
- Примеры локального nginx + self-signed сертификатов.
84+
- Утилиты подготовки и загрузки данных в Chroma.
85+
86+
### 8) `mlops/docker`
87+
- `Dockerfile` для сборки сервиса.
88+
- `docker-compose.yml` - базовый compose.
89+
- `docker-compose-prod.yml` - вариант для VM с nginx и внешними volume.
90+
- `docker-compose-swarm-prod.yml` - пример для Docker Swarm.
91+
92+
## Требования
93+
94+
- Python `3.12`
95+
- `uv` для управления окружением
96+
- Docker + Docker Buildx (для мультиплатформенной сборки)
97+
- Доступные внешние сервисы: Redis, ChromaDB, Postgres, Langfuse (или их контейнерные аналоги)
98+
99+
## Конфигурация окружения
100+
101+
1. Для локальной разработки используйте шаблон:
102+
```bash
103+
cp .env.example .env
104+
```
105+
106+
2. Для Docker/VM используйте шаблон:
107+
```bash
108+
cp .env.prod.example .env.prod
109+
```
110+
111+
3. Заполните секреты и адреса сервисов в скопированном файле.
112+
113+
## Локальный запуск (без Docker)
114+
115+
```bash
116+
uv sync --frozen --no-dev
117+
uv run python src/service/__main__.py
118+
```
119+
120+
После старта API доступно по адресу:
121+
- `http://localhost:8080/health`
122+
- `http://localhost:8080/docs`
123+
124+
## Типовой сценарий работы с `/chat` через `curl`
125+
126+
Фактический endpoint сервиса: `POST /api/v1/chat`.
127+
Если nginx проксирует маршрут `/chat` в приложение, используйте URL `https://localhost/chat`.
128+
Для nginx обязателен токен `X-API-TOKEN` (значение из `NGINX_TOKEN`), иначе nginx вернет `422`.
129+
130+
Обязательные заголовки:
131+
- `x-trace-id`
132+
- `x-request-time`
133+
- `x-source-name`
134+
- `x-user-id`
135+
- `X-API-TOKEN` (только для запроса через nginx `/chat`)
136+
137+
Тело запроса:
138+
```json
139+
{
140+
"text": "Как вступить в профсоюз?",
141+
"organisation": "ППО Невинномысский Азот"
142+
}
143+
```
144+
145+
### Вариант 1. HTTPS localhost без проверки сертификата (`-k`)
146+
147+
```bash
148+
curl -k --request POST "https://localhost/api/v1/chat" \
149+
--header "Content-Type: application/json" \
150+
--header "x-trace-id: $(uuidgen)" \
151+
--header "x-request-time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
152+
--header "x-source-name: local-dev" \
153+
--header "x-user-id: user-001" \
154+
--data '{
155+
"text": "Как вступить в профсоюз?",
156+
"organisation": "ППО Невинномысский Азот"
157+
}'
158+
```
159+
160+
Пример для проксированного endpoint `/chat`:
161+
162+
```bash
163+
curl -k --request POST "https://localhost/chat" \
164+
--header "Content-Type: application/json" \
165+
--header "X-API-TOKEN: ${NGINX_TOKEN}" \
166+
--header "x-trace-id: $(uuidgen)" \
167+
--header "x-request-time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
168+
--header "x-source-name: local-dev" \
169+
--header "x-user-id: user-001" \
170+
--data '{
171+
"text": "Как вступить в профсоюз?",
172+
"organisation": "ППО Невинномысский Азот"
173+
}'
174+
```
15175

16-
## How to start?
17-
0. Setup buildx for multiplatform building
176+
### Вариант 2. Прямой вызов FastAPI без TLS
177+
178+
```bash
179+
curl --request POST "http://localhost:8080/api/v1/chat" \
180+
--header "Content-Type: application/json" \
181+
--header "x-trace-id: $(uuidgen)" \
182+
--header "x-request-time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
183+
--header "x-source-name: local-dev" \
184+
--header "x-user-id: user-001" \
185+
--data '{
186+
"text": "Как вступить в профсоюз?",
187+
"organisation": "ППО Невинномысский Азот"
188+
}'
189+
```
190+
191+
## Типовой сценарий сборки Docker-образа (macOS / Ubuntu 22.04)
192+
193+
1. Один раз создайте buildx builder:
18194
```bash
19-
docker buildx create --use --name multiarch
195+
docker buildx create --name multiarch --use
196+
docker buildx inspect --bootstrap
20197
```
21-
1. Build Docker image (works for ubuntu and mac):
198+
199+
2. Сборка локального образа под текущую платформу:
22200
```bash
23201
docker buildx build \
24-
--platform linux/amd64,linux/arm64 \
25-
-t unionchatbot:latest \
202+
--platform linux/amd64 \
203+
-t union-chatbot:latest \
26204
-f mlops/docker/Dockerfile \
27205
--load .
28206
```
29-
2. Run the image:
207+
208+
3. Сборка мультиплатформенного образа (amd64 + arm64) с публикацией в registry:
209+
```bash
210+
docker buildx build \
211+
--platform linux/amd64,linux/arm64 \
212+
-t <registry>/union-chatbot:latest \
213+
-f mlops/docker/Dockerfile \
214+
--push .
215+
```
216+
217+
Примечание:
218+
- для Apple Silicon обычно целевая платформа `linux/arm64`;
219+
- для большинства VM/серверов - `linux/amd64`.
220+
221+
## Типовой деплой Docker-образа на ВМ
222+
223+
Вариант через compose-файл из проекта (`mlops/docker/docker-compose-prod.yml`):
224+
225+
```bash
226+
scp .env.prod user@vm:/opt/unionchatbot/.env.prod
227+
scp mlops/docker/docker-compose-prod.yml user@vm:/opt/unionchatbot/docker-compose-prod.yml
228+
ssh user@vm "cd /opt/unionchatbot && docker compose -f docker-compose-prod.yml pull"
229+
ssh user@vm "cd /opt/unionchatbot && docker compose -f docker-compose-prod.yml up -d"
230+
```
231+
232+
Для этого варианта на VM должны существовать nginx-конфиги, сертификаты и token-файлы в путях,
233+
которые смонтированы в `docker-compose-prod.yml`.
234+
235+
## Типовой деплой локально
236+
237+
Локальный сценарий через `docker-compose.yml` с чтением секретов из `.env.prod`:
238+
239+
1. Подготовьте `.env.prod` и проверьте значения:
240+
```bash
241+
cp .env.prod.example .env.prod
242+
```
243+
244+
Обязательно: `APP_HOST=0.0.0.0`, `APP_PORT=32000`, `PG_HOST=postgres_test`, `REDIS_HOST=redis`, `CHROMA_HOST=chromadb`.
245+
246+
2. Положите секреты в путь, который ожидает compose:
30247
```bash
31-
docker run -p 8080:8000 --env-file .env.prod --name unionchatbot unionchatbot:latest
248+
mkdir -p /opt/unionchatbot
249+
cp .env.prod /opt/unionchatbot/.env.prod
32250
```
33-
### Errors:
34-
In case of image error you can do the following:
251+
252+
3. Соберите образ и поднимите стек:
35253
```bash
36-
docker stop unionchatbot 2>/dev/null || true
37-
docker rm unionchatbot 2>/dev/null || true
38-
```
254+
docker build -t union-chatbot:latest -f mlops/docker/Dockerfile .
255+
docker compose -f mlops/docker/docker-compose.yml up -d
256+
```
257+
258+
4. Проверка:
259+
```bash
260+
docker compose -f mlops/docker/docker-compose.yml exec chatbot curl -s http://127.0.0.1:32000/health
261+
```
262+
263+
Остановка:
264+
```bash
265+
docker compose -f mlops/docker/docker-compose.yml down
266+
```
267+
268+
Если нужен запуск через HTTPS и `/chat`, добавьте nginx как reverse-proxy с конфигами из `experiments/nginx`.
269+
270+
## Полезные материалы в репозитории
271+
272+
- `experiments/yandex_api` - эксперименты с YandexGPT API.
273+
- `experiments/langchain_tests` - тестовые ноутбуки по LangChain.
274+
- `experiments/langfuse_experiments` - эксперименты по трейсингу и промптам.
275+
- `experiments/nginx` - примеры nginx-конфигурации и self-signed сертификатов.
276+
- `experiments/scripts` - утилиты подготовки/загрузки данных.

experiments/ChromaNew.ipynb

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
},
9494
{
9595
"cell_type": "code",
96-
"execution_count": 4,
96+
"execution_count": 1,
9797
"id": "8d8cb91e-166d-499a-9838-ebed25e6b3f7",
9898
"metadata": {},
9999
"outputs": [
@@ -114,6 +114,33 @@
114114
"print(len(res.get(\"ids\", [])))"
115115
]
116116
},
117+
{
118+
"cell_type": "code",
119+
"execution_count": 3,
120+
"id": "5c7397fa-4eec-4318-bf87-bf5183024641",
121+
"metadata": {},
122+
"outputs": [
123+
{
124+
"data": {
125+
"text/plain": [
126+
"{'ids': [],\n",
127+
" 'embeddings': None,\n",
128+
" 'metadatas': [],\n",
129+
" 'documents': [],\n",
130+
" 'data': None,\n",
131+
" 'uris': None,\n",
132+
" 'included': ['metadatas', 'documents']}"
133+
]
134+
},
135+
"execution_count": 3,
136+
"metadata": {},
137+
"output_type": "execute_result"
138+
}
139+
],
140+
"source": [
141+
"col.get(\"topic\")"
142+
]
143+
},
117144
{
118145
"cell_type": "code",
119146
"execution_count": 5,
@@ -271,9 +298,43 @@
271298
},
272299
{
273300
"cell_type": "code",
274-
"execution_count": null,
301+
"execution_count": 4,
275302
"id": "35b34c09-45bd-4467-b460-8323940010b9",
276303
"metadata": {},
304+
"outputs": [
305+
{
306+
"name": "stdout",
307+
"output_type": "stream",
308+
"text": [
309+
"Доступные топики: ['Коллективный договор', 'Локальные акты профсоюза', 'Общая информация об профсоюзе', 'Трудовой кодекс']\n",
310+
"Всего топиков: 4\n"
311+
]
312+
}
313+
],
314+
"source": [
315+
"import chromadb\n",
316+
"\n",
317+
"client = chromadb.HttpClient(host=\"127.0.0.1\", port=8000)\n",
318+
"col = client.get_collection(\"PROFKOM_NEW\")\n",
319+
"\n",
320+
"# Получить ВСЕ документы (без where)\n",
321+
"all_data = col.get(include=[\"metadatas\"])\n",
322+
"\n",
323+
"# Извлечь все topics и получить уникальные\n",
324+
"topics = set()\n",
325+
"for meta in all_data[\"metadatas\"]:\n",
326+
" if \"topic\" in meta:\n",
327+
" topics.add(meta[\"topic\"])\n",
328+
"\n",
329+
"print(\"Доступные топики:\", sorted(topics))\n",
330+
"print(f\"Всего топиков: {len(topics)}\")\n"
331+
]
332+
},
333+
{
334+
"cell_type": "code",
335+
"execution_count": null,
336+
"id": "a943b8f5-5c07-47ce-97af-37a76d329fdf",
337+
"metadata": {},
277338
"outputs": [],
278339
"source": []
279340
}

0 commit comments

Comments
 (0)