199 lines
9.5 KiB
Markdown
199 lines
9.5 KiB
Markdown
# Screen Agent
|
||
|
||
可视化大屏编辑器的后端服务,提供两部分能力:
|
||
|
||
- **AI 编辑**:把自然语言需求转换为经过校验的结构化编辑动作,由前端应用到画布;支持上传数据文件,并用查询结果直接制图。
|
||
- **大屏管理**:大屏的保存、版本发布、回收站与线上访问。
|
||
|
||
## 功能
|
||
|
||
- 自然语言新建大屏,增删、更换图表,修改组件属性、位置、尺寸与画布,支持对齐和分布。
|
||
- 上传 CSV / TSV / Excel / JSON / JSONL / Parquet,对数据问答、汇总,并将查询结果填入图表。
|
||
- 动作按物料字段定义校验,并在画布副本上整批模拟执行;失败原因回传 Agent 修正,仅交付通过的动作。
|
||
- 多轮对话;信息不足时暂停任务向用户澄清,回答后继续,也可取消。
|
||
- 通过 SSE 推送处理进度、问答文本增量与最终结果。
|
||
- 大屏保存(乐观锁)、发布快照与历史版本、分享链接、回收站、封面图、数据源服务端代理。
|
||
|
||
## 架构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Client[大屏编辑器] --> API[FastAPI]
|
||
API --> Graph[LangGraph 工作流]
|
||
Graph --> Intent[意图判断]
|
||
Intent -->|问答| QA[问答 Agent]
|
||
Intent -->|其他| Screen[大屏 Agent]
|
||
Screen --> MCP[MCP 物料查询]
|
||
Screen --> Batch[动作校验与模拟]
|
||
QA --> Data[DuckDB / Parquet]
|
||
Screen --> Data
|
||
Graph -->|SSE| Client
|
||
API --> Mongo[(MongoDB)]
|
||
```
|
||
|
||
外层 LangGraph 负责路由、状态检查点与澄清恢复,内层 LangChain Agent 通过工具循环完成任务。意图由 TypeSafe Jev 判断,仅在明确为问答且概率达到阈值时进入问答分支。
|
||
|
||
| 组件 | 技术 |
|
||
|------|------|
|
||
| Web / 模型 | FastAPI、Pydantic |
|
||
| Agent 工作流 | LangGraph、LangChain(`langchain-openai`) |
|
||
| 意图判断 | TypeSafe Jev |
|
||
| 物料与字段查询 | MCP(`mcp_servers/`,由应用自动拉起,端口 8201) |
|
||
| 存储 | MongoDB(物料、会话、检查点、文件元数据、大屏) |
|
||
| 数据文件 | python-calamine、PyArrow(Parquet)、DuckDB |
|
||
| 观测 | Loguru、Langfuse(可选) |
|
||
|
||
## 快速开始
|
||
|
||
**依赖**:Python ≥ 3.11、[uv](https://docs.astral.sh/uv/)、MongoDB、支持工具调用与结构化输出的 OpenAI 兼容聊天模型服务、TypeSafe API Key。
|
||
|
||
```sh
|
||
uv sync --locked
|
||
cp .env.example .env # 填写下表中的配置
|
||
uv run python -m scripts.seed_materials # 导入物料目录,见下方说明
|
||
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000
|
||
```
|
||
|
||
启动后访问 <http://127.0.0.1:8000/docs> 查看完整接口文档。
|
||
|
||
| 配置 | 说明 |
|
||
|------|------|
|
||
| `LLM_API_KEY` `LLM_MODEL` `LLM_BASE_URL` | 聊天模型。默认发送 `reasoning_effort=low`,服务端不支持时设 `LLM_REASONING_EFFORT=` |
|
||
| `TYPESAFE_API_KEY` | 意图判断 |
|
||
| `MONGODB_URI` `MONGODB_DB` | 物料目录与大屏存取必需;未配置时会话与检查点退回进程内存,大屏接口返回 503 |
|
||
| `AUTH_ENABLED` `USER_CENTER_URL` | 鉴权。本地开发设 `AUTH_ENABLED=false`;开启后请求携带 `Authorization: Bearer <token>`,服务调用用户中心 `GET /api/users/me` 校验 |
|
||
| `LANGFUSE_TRACING_ENABLED` | 未部署 Langfuse 时设为 `false` |
|
||
|
||
全部配置及默认值见 [.env.example](.env.example) 与 [app/config.py](app/config.py)。
|
||
|
||
**物料初始化**:物料定义位于 [app/resources/materials](app/resources/materials),必须导入 MongoDB 后服务才能增改组件;导入会整体替换现有目录,之后需重启服务。导入脚本为 [scripts/seed_materials.py](scripts/seed_materials.py),可附带自定义物料目录作为参数。
|
||
|
||
## 接口
|
||
|
||
所有接口以 `/api` 为前缀,JSON 响应统一为 `{code, data, msg}`。除标注外均需登录。
|
||
|
||
### Agent
|
||
|
||
| 接口 | 说明 |
|
||
|------|------|
|
||
| `POST /agent/run` | 提交需求与当前画布,返回 SSE |
|
||
| `POST /agent/resume` | 回答澄清、选择选项或取消任务,返回 SSE |
|
||
|
||
请求示例:
|
||
|
||
```json
|
||
{
|
||
"prompt": "在画布顶部添加标题:销售数据概览",
|
||
"screen": { "canvas": { "width": 1920, "height": 1080 }, "nodes": [] },
|
||
"context": { "conversation_id": "<POST /conversations 返回的 ID>" }
|
||
}
|
||
```
|
||
|
||
每次 `run` 提交画布的完整快照;同一段对话复用 `conversation_id`,历史由服务端读取。
|
||
|
||
SSE 事件:
|
||
|
||
| 事件 | 含义 |
|
||
|------|------|
|
||
| `progress` | 当前处理阶段 |
|
||
| `answer_delta` | 问答文本增量,最终以 `final.data.answer` 为准 |
|
||
| `final` | 本轮结果,`data.status` 见下 |
|
||
| `error` | 流开始后的执行异常 |
|
||
|
||
`final.data.status`:`completed`(有 `actions` 时由前端按序应用)、`waiting_for_input`(展示 `clarification`,用 `thread_id` 调用 `resume`)、`failed`(业务失败,不交付动作)、`cancelled`。流开始前的错误以 HTTP 状态码返回;未收到 `final` 或 `error` 即断开,应视为结果不完整。
|
||
|
||
### 会话
|
||
|
||
| 接口 | 说明 |
|
||
|------|------|
|
||
| `POST /conversations` | 创建会话 |
|
||
| `GET /conversations` | 分页列出当前用户的会话 |
|
||
| `GET /conversations/{id}` | 读取会话历史 |
|
||
| `PATCH /conversations/{id}` | 重命名 |
|
||
| `DELETE /conversations/{id}` | 删除 |
|
||
|
||
### 数据文件
|
||
|
||
上传后后台解析(HTTP 202),轮询进度至 `ready` 后即可在同一会话中被 Agent 使用;已解析且未过期的文件自动带入后续对话。
|
||
|
||
| 接口 | 说明 |
|
||
|------|------|
|
||
| `GET /files/limits` | 上传限制与支持格式(无需登录) |
|
||
| `POST /files` | `multipart/form-data` 上传,需带 `conversation_id` |
|
||
| `GET /files?conversation_id=` | 会话下的文件 |
|
||
| `GET /files/{id}` | 文件详情与表信息 |
|
||
| `GET /files/{id}/progress` | 解析进度 |
|
||
| `GET /files/{id}/tables/{table}/rows` | 分页、排序读取 |
|
||
| `POST /files/{id}/tables/{table}/aggregate` | 分组汇总 |
|
||
| `DELETE /files/{id}` | 删除 |
|
||
|
||
### 大屏管理
|
||
|
||
大屏仅所有者可访问,他人的大屏与不存在同样返回 404。保存与发布需携带 `revision`,与服务端不一致时返回 409。
|
||
|
||
| 接口 | 说明 |
|
||
|------|------|
|
||
| `POST /screens` | 创建 |
|
||
| `GET /screens` | 列表:关键字、状态、排序、分页,`deleted=true` 查看回收站 |
|
||
| `GET /screens/{id}` | 详情 |
|
||
| `PUT /screens/{id}` | 保存(乐观锁) |
|
||
| `PATCH /screens/{id}` | 重命名 |
|
||
| `DELETE /screens/{id}` | 移入回收站,默认保留 30 天后自动清除 |
|
||
| `POST /screens/{id}/restore` | 从回收站恢复 |
|
||
| `DELETE /screens/trash` | 清空回收站 |
|
||
| `POST /screens/{id}/publish` | 发布当前版本,首次发布生成 `share_id` |
|
||
| `POST /screens/{id}/unpublish` | 下线 |
|
||
| `GET /screens/{id}/publishes` | 发布历史 |
|
||
| `POST /screens/{id}/publishes/{version}/restore` | 将历史版本恢复为草稿 |
|
||
| `POST /screens/{id}/copy` | 复制 |
|
||
| `PUT /screens/{id}/thumbnail` | 上传封面图(≤ 1 MB) |
|
||
|
||
### 公开与其他
|
||
|
||
| 接口 | 说明 |
|
||
|------|------|
|
||
| `GET /public/screens/{share_id}` | 线上版本(发布快照),匿名;数据源 `headers` 不下发 |
|
||
| `GET /public/screens/{share_id}/datasources/{id}` | 线上取数,请求配置取自发布快照,匿名 |
|
||
| `GET /public/thumbnails/{id}` | 封面图,匿名 |
|
||
| `POST /datasource/fetch` | 编辑态数据源代理;仅允许公网 http(s) 地址,限 5 秒、2 MB |
|
||
| `GET /materials` | 组件物料目录(无需登录) |
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
app/
|
||
├── api/ HTTP 路由
|
||
├── services/ 会话、大屏、物料、数据源代理与持久化装配
|
||
├── processor/ LangGraph 工作流与节点
|
||
├── agent/ 大屏 / 问答 Agent、工具、Skills 加载
|
||
├── actions/ 动作执行、布局计算、整批模拟
|
||
├── datafiles/ 数据文件解析、存储、查询与后台任务
|
||
├── schemas/ 请求、画布、动作、会话、大屏模型
|
||
├── resources/ 物料定义与字段规则
|
||
├── clients/ LLM、Jev、MCP、用户中心客户端
|
||
├── prompt/ 提示词
|
||
└── core/ common/ observability/ 认证、异常、日志、响应与 SSE、追踪
|
||
mcp_servers/ 应用管理的本地 MCP 服务(配置见 mcp_servers.json)
|
||
skills/ 大屏设计、编辑、图表替换与数据制图规则
|
||
```
|
||
|
||
阅读主链路:[app/api/agent.py](app/api/agent.py) → [app/services/agent.py](app/services/agent.py) → [app/processor/graph.py](app/processor/graph.py) → [app/agent/screen_agent.py](app/agent/screen_agent.py) → [app/actions/batch.py](app/actions/batch.py)。动作协议定义在 [app/schemas/actions](app/schemas/actions)。
|
||
|
||
## 部署注意
|
||
|
||
- **单实例**:任务与会话的并发控制、上传进度保存在进程内,请以单 worker 运行;同一会话同时只能执行一个任务。
|
||
- **本地存储**:数据文件与封面图写入磁盘(`DATA_FILE_DIR`、`THUMBNAIL_DIR`,默认在 `data/` 下),容器部署需挂载持久卷。
|
||
- **反向代理**:SSE 需关闭响应缓冲并放宽读取超时;上传大小限制需与 `DATA_FILE_MAX_MB` 一致。应用未配置 CORS,请通过同源代理转发 `/api`。
|
||
- **连接中断**:客户端断开会取消当前任务,不支持心跳、事件重放或断线续传。
|
||
- **日志**:写入 `logs/app.log`。
|
||
|
||
## 开发
|
||
|
||
扩展组件:修改物料定义,重新导入后重启。调整 Agent 行为:修改 [skills](skills) 与 [app/prompt](app/prompt)。新增 MCP 服务:修改 [mcp_servers.json](mcp_servers.json),连接配置支持 `${环境变量}` 展开。
|
||
|
||
测试使用标准库 `unittest`,目录 `tests/` 不在版本控制内:
|
||
|
||
```sh
|
||
uv run python -m unittest discover -s tests
|
||
```
|