9.5 KiB
Screen Agent
可视化大屏编辑器的后端服务,提供两部分能力:
- AI 编辑:把自然语言需求转换为经过校验的结构化编辑动作,由前端应用到画布;支持上传数据文件,并用查询结果直接制图。
- 大屏管理:大屏的保存、版本发布、回收站与线上访问。
功能
- 自然语言新建大屏,增删、更换图表,修改组件属性、位置、尺寸与画布,支持对齐和分布。
- 上传 CSV / TSV / Excel / JSON / JSONL / Parquet,对数据问答、汇总,并将查询结果填入图表。
- 动作按物料字段定义校验,并在画布副本上整批模拟执行;失败原因回传 Agent 修正,仅交付通过的动作。
- 多轮对话;信息不足时暂停任务向用户澄清,回答后继续,也可取消。
- 通过 SSE 推送处理进度、问答文本增量与最终结果。
- 大屏保存(乐观锁)、发布快照与历史版本、分享链接、回收站、封面图、数据源服务端代理。
架构
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、MongoDB、支持工具调用与结构化输出的 OpenAI 兼容聊天模型服务、TypeSafe API Key。
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 与 app/config.py。
物料初始化:物料定义位于 app/resources/materials,必须导入 MongoDB 后服务才能增改组件;导入会整体替换现有目录,之后需重启服务。导入脚本为 scripts/seed_materials.py,可附带自定义物料目录作为参数。
接口
所有接口以 /api 为前缀,JSON 响应统一为 {code, data, msg}。除标注外均需登录。
Agent
| 接口 | 说明 |
|---|---|
POST /agent/run |
提交需求与当前画布,返回 SSE |
POST /agent/resume |
回答澄清、选择选项或取消任务,返回 SSE |
请求示例:
{
"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 |
组件物料目录(无需登录) |
项目结构
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/services/agent.py → app/processor/graph.py → app/agent/screen_agent.py → app/actions/batch.py。动作协议定义在 app/schemas/actions。
部署注意
- 单实例:任务与会话的并发控制、上传进度保存在进程内,请以单 worker 运行;同一会话同时只能执行一个任务。
- 本地存储:数据文件与封面图写入磁盘(
DATA_FILE_DIR、THUMBNAIL_DIR,默认在data/下),容器部署需挂载持久卷。 - 反向代理:SSE 需关闭响应缓冲并放宽读取超时;上传大小限制需与
DATA_FILE_MAX_MB一致。应用未配置 CORS,请通过同源代理转发/api。 - 连接中断:客户端断开会取消当前任务,不支持心跳、事件重放或断线续传。
- 日志:写入
logs/app.log。
开发
扩展组件:修改物料定义,重新导入后重启。调整 Agent 行为:修改 skills 与 app/prompt。新增 MCP 服务:修改 mcp_servers.json,连接配置支持 ${环境变量} 展开。
测试使用标准库 unittest,目录 tests/ 不在版本控制内:
uv run python -m unittest discover -s tests