Reviewed-on: #14
Screen Agent
前端仓库:jiawei/my-v0
可视化大屏编辑器的后端服务,提供两部分能力:
- 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(可选) |
记忆与上下文管理
大屏是唯一的事实来源:每次请求由前端带来完整画布,记忆只保存画布上看不出来的东西,例如做过哪些修改、问答用过哪些查询。画布状态不进记忆,用户在前端手动修改或撤销后,不会出现记忆和画布不一致。
| 范围 | 做法 |
|---|---|
| 单次运行 | Agent 在工具循环里完成任务,模型调用次数有上限(MAX_AGENT_STEPS、MAX_QA_STEPS),上下文不压缩 |
| 同一会话 | 服务端按 conversation_id 读取历史,不采用前端传来的历史。最多带最近 CONVERSATION_HISTORY_TURNS(20)轮,再按 HISTORY_MAX_TOKENS(10000,o200k 计数,消息和记录合计)从最近一轮往前整轮保留;最近一轮总是保留,单独超出预算时截掉中间、保留开头和结尾 |
| 澄清恢复 | 任务暂停时状态存入检查点,恢复后同一任务覆盖保存为一轮,追问和回答记在这一轮里 |
| 跨会话 | 暂不做,新会话从当前画布开始 |
每轮除用户原话和回复外,服务端从运行结果中记下轮次记录,不经过模型:
changes:交付的修改,按原始画布与交付后画布逐组件比较,记为新增、修改或删除queries:问答中成功执行的 SQL,以及结果的列和行数,不保存结果本身clarifications:本轮的追问和用户的回答
下一轮请求时,原话和回复作为对话消息;记录作为 history_records 放进请求 JSON,不拼进消息。其中 on_screen 按当前画布计算,组件被撤销后不会被误认为还在。读取规则见 app/prompt/domain.py。意图判断只看原话、回复和澄清,不看 SQL 和修改记录。
暂不采用滚动摘要、向量检索和用户级偏好:各轮多是对可见画布的独立修改,摘要容易失真。跨会话的长期要求(如“金额统一用万元”)计划以大屏级备忘实现,待真实使用中出现需要时再做。
快速开始
依赖: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 TYPESAFE_DEFAULT_MODEL |
意图判断。模型固定为 jev-1.13.0,阈值按该版本校准,换版本需重新评测 |
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。 - 连接中断:客户端断开会取消当前任务,不支持心跳、事件重放或断线续传。
- 历史计数:会话历史用 tiktoken 的 o200k 编码表计数。Docker 镜像在构建时下载好;其他方式部署时首次请求会联网下载,离线环境需预先放入
TIKTOKEN_CACHE_DIR。下载失败时退回按字符估算,不影响请求。 - 日志:写入
logs/app.log。
开发
扩展组件:修改物料定义,重新导入后重启。调整 Agent 行为:修改 skills 与 app/prompt。新增 MCP 服务:修改 mcp_servers.json,连接配置支持 ${环境变量} 展开。