内容
# agent-tutor
完整的智能体,搭配MCP服务器,用于大学助教,基于LLM。 通过一组工具使语言模型能够访问教学过程的数据,包括学生、课程表、学科、教学材料。 同时,附加RAG用于根据内容搜索教学材料。
| 部分 | 描述 |
|---|---|
| MCP服务器 | 与数据库和RAG交互的服务器 |
| RAG | 根据内容搜索教学材料(独立的HTTP服务) |
| 代理API | 与代理交互以及自定义代理客户端:[代理核心](demo/api/agent/) |
| Web UI | 带有聊天功能的展示网站,用于与代理交互和演示 |
## 功能
代理可以使用的工具:
| 工具 | 功能 |
|---|---|
| `get_student(student_id)` | 学生信息卡 |
| `find_student_by_name(name)` | 按姓名查找学生 |
| `get_schedule(group_id, day?)` | 课程表,可选按日期 |
| `get_disciplines(student_id)` | 通过学生所在班级获取学科 |
| `get_materials(discipline_id, type?)` | 获取学科下的文件列表 |
| `search_materials(query, discipline_id?)` | 根据内容搜索教学材料 |
| `get_student_grades(student_id, discipline_id?)` | 学生成绩,可选按学科 |
| `get_teacher_by_name(name)` | 按姓名查找教师 |
| `get_teacher_schedule(teacher_name, day?)` | 教师课程表,可选按日期 |
| `list_documents(discipline_id?)` | RAG索引中的文档列表 |
| `search_documents(query, discipline_id?, limit?)` | 搜索相关文档片段 |
| `get_rag_context(query, discipline_id?, limit?)` | 根据文档生成回答上下文 |
> **注意:** `import_document` 仅可通过CLI `agent-ingest` 访问,不通过MCP服务器。
## 技术栈
- Python 3.12 和 3.13
- [FastMCP](https://github.com/modelcontextprotocol/python-sdk) — MCP传输层
- [LiteLLM](https://github.com/BerriAI/litellm) — 统一的LLM提供商接口
- SQLite — 本地存储
- Pydantic — 回答模式
- Faker — 测试数据生成
- python-docx — 生成DOCX材料用于测试
- 内置PDF生成器 — 无需外部工具生成PDF讲义
- Sentence Transformers — 本地embedding模型
- ChromaDB — 本地向量数据库用于RAG搜索
- Ollama — 通过`agent-ingest` 进行本地文本生成
## 结构
```
agent-tutor/
├── mcp_server/ # MCP服务器(原server.py)
│ ├── __init__.py
│ ├── server.py # FastMCP服务器,入口点,HTTP传输
│ └── tools/
│ ├── student.py # StudentTools
│ ├── disciplines.py # DisciplineTools(通过doc_repo获取materials/search_materials)
│ ├── grades.py # GradeTools
│ ├── teacher.py # TeacherTools
│ └── rag.py # 与RAG兼容的接口(使用RagClient)
├── db/
│ ├── database.py # SQLite,创建表,加载测试数据
│ └── models.py # Pydantic模型(从rag.models重新导出RAG模型)
├── rag/ # RAG层(不依赖于db包)
│ ├── __init__.py # create_rag_pipeline(connection,config)
│ ├── config.py # RagConfig来自环境变量
│ ├── interfaces.py # EmbeddingProtocol,VectorStoreProtocol
│ ├── embeddings.py # SentenceTransformerEmbedding
│ ├── vector_store.py # ChromaDBVectorStore
│ ├── parser.py # DocumentParser(PDF,DOCX,TXT,MD,HTML)
│ ├── chunker.py # TextChunker(语义,递归,句子)
│ ├── repository.py # DocumentRepository — SQLite中CRUD文档/块
│ ├── pipeline.py # RAGPipeline — 解析 → 分块 → 保存的编排
│ ├── models.py # Pydantic模型(Document,Material,RagSearchResult,...)
│ ├── http_models.py # RAG服务的HTTP DTO模型
│ ├── service.py # RAG的HTTP服务(Starlette)— 端点/health,/search,/context,/documents/*
│ └── client.py # 从MCP调用RAG服务的HTTP客户端
├── fixtures/
│ ├── generate.py # 测试数据生成器
│ ├── ingest.py # CLI agent-ingest 用于RAG文档和材料生成
│ └── document_generator.py # 为测试生成PDF/DOCX材料
├── demo/ # 演示部分:API和网页界面
│ ├── settings.py # 演示配置(端口,Ollama URL,超时)
│ ├── api/
│ │ ├── __init__.py
│ │ ├── agent/ # 代理逻辑:编排器,LLM,MCP,工具,上下文
│ │ ├── backlog.py # 与模型的交互日志(JSONL)
│ │ ├── data.py # 数据库管理
│ │ └── server.py # REST/SSE API服务器
│ └── web/
│ ├── __init__.py
│ ├── server.py # 静态网站服务器
│ └── static/ # 静态文件(HTML,CSS,JS)
└── fixtures.json # 测试数据
```
## RAG包架构
`rag/`包不依赖于`db/`。
- `rag/interfaces.py` — 协议(`EmbeddingProtocol`,`VectorStoreProtocol`)用于替换实现
- `rag/embeddings.py` → `SentenceTransformerEmbedding`,`rag/vector_store.py` → `ChromaDBVectorStore`
- Pydantic模型(`Document`,`Material`,`RagSearchResult`,...)移到`rag/models.py`;`db/models.py`重新导出
## 新架构(第0阶段完成)
RAG被提取到**独立的HTTP服务**中:
- `rag/service.py` — Starlette应用,端点:
- `GET /health` — 服务状态检查
- `POST /search` — 文档语义搜索
- `POST /context` — 获取RAG上下文供LLM使用
- `POST /documents/list` — 文档列表
- `POST /documents/import` — 文档导入
- `POST /documents/delete` — 文档删除
- `rag/client.py` — 从MCP和其他组件调用RAG服务的HTTP客户端
- `rag/http_models.py` — RAG服务的HTTP契约的Pydantic模型
- MCP服务器(`server.py`)使用`RagClient`通过HTTP调用RAG
- `tools/rag.py`作为**最小化存根**与`fixtures/document_generator.py`保持兼容
## 文档RAG
RAG层在本地工作,通过SQLite + ChromaDB:
1. `import_document` 读取文件。
2. 文本分成块。
3. 每个块的嵌入由`sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2`模型计算。
4. 块的向量和文本存储在ChromaDB中。
5. 文档元数据存储在SQLite中。
6. `search_documents` 在ChromaDB中查找相似片段。
7. `get_rag_context` 返回片段和模型指令:仅根据找到的来源回答。
默认为`chroma_db/`目录存储ChromaDB索引。
首次使用RAG时,如果通过MCP服务器启动且未启用`RAG_LOCAL_FILES_ONLY`,则embedding模型可以从Hugging Face下载。 CLI `agent-ingest`强制设置`RAG_LOCAL_FILES_ONLY=1`,因此对于它,模型必须已经在本地缓存中或通过`RAG_EMBEDDING_MODEL`指定本地路径。
```bash
RAG_LOCAL_FILES_ONLY=1
RAG_DEVICE=cuda
```
示例:
```bash
uv run agent-ingest import ~/Documents/test.pdf
```
```text
查找第11个任务在“数据库方法”指南中
```
## 快速开始
```bash
git clone https://github.com/trash2bin/agent-tutor
cd agent-tutor
```
项目通过`uv`管理,并在`pyproject.toml`中描述。主要入口点命令:
- `agent-tutor` — MCP服务器,入口点`mcp_server.server:main`。
- `agent-ingest` — 文档和生成的CLI,入口点`fixtures.ingest:main`。
- `rag-service` — HTTP RAG服务,入口点`rag.service:main`。
基本命令:
```bash
uv sync
uv run agent-ingest --help
uv run mcp dev server.py
uv run python -m rag.service # 在端口8082启动RAG HTTP服务
uv tool install . --reinstall
```
使用`uv run ...`进行开发。 `uv tool install . --reinstall`在更改代码后需要更新全局安装的CLI命令。
## 演示网站和聊天
演示有两个组件,分为API和网页部分:
- **API服务器**(`demo/api/server.py`)— 模型的REST/SSE API,负责通过LiteLLM调用模型,工具管理和上下文管理
- **网页服务器**(`demo/web/server.py`)— 静态网站,带有浮动聊天窗口
架构:
- `demo/api/agent/` — 代理逻辑:通过LiteLLM调用模型,连接到MCP服务器,工具调用,上下文验证,递归模型调用,上下文内存,答案流式传输,交互处理;主要编排器位于`demo/api/agent/orchestrator.py`
- `demo/api/backlog.py` — 完整的交互日志:每个会话的JSONL文件,包含请求,回答,工具调用,令牌和计时信息(存储在`backlog/`中)
- `demo/api/server.py` — HTTP API,具有端点:`GET /health`,`GET /api/data`,`POST /api/chat`(Server-Sent Events),`GET /api/backlog`,`GET /api/backlog/{session_id}`
### 关键功能
- **LiteLLM集成**:代理使用LiteLLM与各种LLM提供商统一交互
- **思维模式**:模型可以分阶段推理,得出特定答案
- **答案流式传输**:通过Server-Sent Events(SSE)传输令牌和事件
- **会话内存**:自动为每个会话存储对话历史
- **完整日志**:所有与模型交互的日志记录在`backlog/`目录的JSONL文件中
启动:
```bash
# API服务器(默认端口8081)
uv run python -m demo.api.server
# 使用Mostral启动
DEMO_DEBUG=1 MISTRAL_API_KEY={TOKEN} MISTRAL_MODEL=mistral-medium uv run python -m demo.api.server
# Web服务器(默认端口8080)
uv run python -m demo.web.server
```
默认情况下,网站位于`http://127.0.0.1:8080`,API位于`http://127.0.0.1:8081`。
环境变量:
- `DEMO_API_HOST`/`DEMO_API_PORT` — API服务器主机/端口(默认为`127.0.0.1:8081`)
- `DEMO_WEB_HOST`/`DEMO_WEB_PORT` — Web服务器主机/端口(默认为`127.0.0.1:8080`)
- `OLLAMA_URL` — Ollama地址(默认为`http://127.0.0.1:11434`)
- `OLLAMA_MODEL` — Ollama模型(默认为`qwen2.5:0.5b`)
- `DEMO_REQUEST_TIMEOUT` — 请求超时(默认为`600`秒 = 10分钟)
- `ENABLE_THINK` — 启用思维/推理模式(`true`/`false`,默认为`true`)
- `DEMO_HISTORY_TURNS` — 会话历史中存储的回合数(默认为`8`)
- `DEMO_HISTORY_CONTENT_CHARS` — 历史内容的最大长度(默认为`6000`)
- `BACKLOG_DIR` — 日志文件目录(默认为`./backlog/`)
- `BACKLOG_RETENTION_DAYS` — 日志文件保留天数(默认为`30`)
- `LITELLM_DEBUG` — 启用LiteLLM调试模式(`true`/`false`)
- `DEMO_DEBUG` — 启用演示调试模式
示例启动自定义模型:```bash
OLLAMA_MODEL=carstenuhlig/omnicoder-9b:latest uv run python -m demo.api.server
```
> **注意:** 演示需要运行本地Ollama(或其他LiteLLM支持的提供商)。
同步本地环境中的依赖`.venv`:
```bash
uv sync
```
在不激活环境的情况下运行命令:
```bash
uv run agent-ingest --help
uv run agent-tutor
```
## 虚拟环境
通常,`uv sync`就足够了:它创建`.venv`(如果不存在),并从`pyproject.toml`/`uv.lock`安装依赖项。
有用的虚拟环境命令:
| 命令 | 功能 |
|---|---|
| `uv sync` | 根据`uv.lock`创建/更新`.venv` |
| `uv run <command>` | 在项目环境中运行命令 |
| `uv venv --python 3.12` | 显式创建Python 3.12的`.venv` |
| `source .venv/bin/activate` | 在当前shell中激活环境 |
| `deactivate` | 退出已激活的环境 |
| `uv pip list` | 显示`.venv`中的软件包 |
| `uv add <package>` | 将运行时依赖项添加到`pyproject.toml` |
| `uv remove <package>` | 从项目中删除依赖项 |
| `rm -rf .venv && uv sync` | 从头重新创建环境 |
开发通常更适合使用`uv run ...`,因为命令始终使用项目中的代码和依赖项。安装`uv tool install . --reinstall`需要在更改代码后更新全局安装的CLI命令。
如果需要测试数据库(`fixtures.json`):
```bash
uv run python fixtures/generate.py
```
将CLI命令安装为`uv tool`:
```bash
uv tool install .
```
重新编译包更改代码后:
```bash
uv tool install . --reinstall
```
`uv tool install`将命令安装到单独的工具环境中。对于开发,通常更适合使用`uv run ...`,因为命令始终使用项目中的代码和依赖项。
## 环境变量
`agent-ingest` 启动时会自动设置:
| 变量名 | 值 | 作用 |
|---|---|---|
| `RAG_LOCAL_FILES_ONLY` | `1` | CLI导入时不从网络下载embedding模型 |
| `HF_HUB_DISABLE_TELEMETRY` | `1` | 禁用Hugging Face的遥测功能 |
| `TOKENIZERS_PARALLELISM` | `false` | 移除tokenizer库的多线程/警告 |
数据库和RAG设置:
| 变量名 | 默认值 | 作用 |
|---|---|---|
| `DB_PATH` | `./university.db` | SQLite数据库路径 |
| `RAG_SERVICE_URL` | `http://127.0.0.1:8082` | MCP服务器的RAG HTTP服务URL |
| `RAG_HOST` | `127.0.0.1` | RAG服务主机 |
| `RAG_PORT` | `8082` | RAG服务端口 |
| `RAG_HTTP_TIMEOUT` | `60.0` | RAG服务HTTP请求超时时间(秒) |
| `RAG_EMBEDDING_MODEL` | `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` | HF ID或embedding模型本地路径 |
| `RAG_EMBEDDING_BATCH_SIZE` | `64` | embedding批次大小 |
| `RAG_DEVICE` | `cpu` | embedding设备:`cpu`、`cuda`、`mps` |
| `RAG_CHUNKER_TYPE` | `semantic` | 分块类型:`semantic`、`recursive`、`sentence` |
| `RAG_CHUNK_SIZE` | `512` | 目标分块大小 |
| `RAG_CHUNK_OVERLAP` | `80` | 分块重叠大小 |
| `RAG_PAGE_OVERLAP_TOKENS` | `50` | 页面间文本重叠token数 |
| `CHROMA_PATH` | `./chroma_db` | ChromaDB索引文件夹 |
| `CHROMA_COLLECTION` | `university_documents` | ChromaDB集合名称 |
| `RAG_CONTEXT_MAX_TOKENS` | `8000` | `get_rag_context`的最大上下文大小 |
材料生成设置:
材料生成需要本地Ollama。检查可用性:`curl ${OLLAMA_HOST:-http://127.0.0.1:11434}/api/tags`。
| 变量名 | 默认值 | 作用 |
|---|---|---|
| `DOCGEN_MODEL` | `qwen2.5:0.5b` | Ollama文本生成模型 |
| `OLLAMA_HOST` | `http://127.0.0.1:11434` | Ollama基准地址(若未设置`DOCGEN_OLLAMA_URL`) |
| `DOCGEN_OLLAMA_URL` | `$OLLAMA_HOST/api/generate` | Ollama生成API完整端点 |
| `DOCGEN_NUM_PREDICT` | `4500` | 生成token限制 |
| `DOCGEN_MAX_ATTEMPTS` | `2` | 获取不过于简短的回答尝试次数 |
| `DOCGEN_MIN_RESPONSE_CHARS` | `120` | 模型回答最小期望长度 |
| `DOCGEN_FAKE_SEED` | 未设置 | 可复现Faker框架的种子 |
| `DOCGEN_OUTPUT_DIR` | `./generated_materials` | PDF/DOCX文件文件夹 |
独立运行示例:
```bash
DB_PATH=/tmp/agent-tutor.db \
CHROMA_PATH=/tmp/agent-tutor-chroma \
uv run agent-ingest list
```
通过MCP Inspector运行检查工具:
```bash
# 需要单独启动HTTP RAG服务
uv run python -m rag.service
# 在另一个终端中,使用RAG服务URL启动MCP服务器
RAG_SERVICE_URL=http://127.0.0.1:8082 uv run mcp_serve/server.py
```
在浏览器中选择**HTTP**(或**STDIO**用于开发)。
对于STDIO:输入命令`uv`,参数`mcp_serve/server.py`,然后点击Connect。
对于HTTP:输入URL `http://127.0.0.1:8083/mcp`(如果MCP服务器使用HTTP传输启动)。
### 查询模型示例:
```
学生ID为"456c4e68-290a-4f8b-b0d0-545534adaf3e"的学生在其学科中有哪些可用材料?
```
## 当前状态
项目处于工作原型阶段。**已完成ROADMAP的阶段0.0、0.1、0.2、0.3**。
功能:
- MCP服务器启动并发布工具(HTTP传输端口8083,mount_path `/mcp`)
- SQLite数据库初始化并在启动时加载fixtures
- 工具返回类型化的Pydantic响应
- RAG元数据存储在SQLite中,文档向量索引存储在ChromaDB中
- **RAG被分离为独立的HTTP服务** (`rag/service.py`) - 服务在端口8082上启动
- **MCP服务器使用HTTP客户端** (`rag/client.py`) 通过`RAG_SERVICE_URL`调用RAG
- 删除MCP和RAG管道之间的直接连接
- `mcp_server/tools/rag.py`作为**最小化存根**以与`fixtures/document_generator.py`保持向后兼容
- **API服务迁移到FastAPI**(之前为Starlette),保留SSE流式传输
- **API使用MCP的HTTP客户端** (`mcp.client.streamable_http.streamablehttp_client`)
- 在MCP Inspector、Goose、Claude Code、Pi中测试
- **演示部分**:LiteLLM集成、回答流式传输、会话内存、思维模式、与模型交互的完整日志
- 网站可用于演示
- 网站上的自定义客户端已经可以进行逻辑叙述并调用工具来解决用户任务
### 修改后如何运行
```bash
# 终端1:RAG HTTP服务(端口8082)
uv run python -m rag.service
# 终端2:MCP服务器(端口8083,HTTP传输,使用RAG的HTTP服务)
RAG_SERVICE_URL=http://127.0.0.1:8082 uv run python -m mcp_server.server
# 终端3:API服务器(端口8081,FastAPI,使用MCP的HTTP服务)
MCP_SERVICE_URL=http://127.0.0.1:8083/mcp uv run python -m demo.api.server
# 终端4:Web服务器(端口8080)
uv run python -m demo.web.server
```
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器