19 KiB
Screen Agent · my-v0-server
面向可视化大屏编辑器的 AI 后端。用户通过自然语言创建大屏、调整组件和分析上传的数据,服务将需求转换为经过校验的结构化编辑动作,交给前端执行。
本仓库提供 API、Agent 工作流、组件物料和数据处理能力。接入时,前端负责提交当前画布快照、展示流式反馈,并应用返回的动作。
快速开始 · 使用指南 · 接口概览 · 架构设计 · 开发与测试
功能特性
- 自然语言编辑大屏:新建大屏,新增、删除或更换图表,修改组件属性、位置、尺寸与画布配置,支持对齐和分布等布局操作。
- 数据驱动的问答与制图:上传 CSV、TSV、Excel(XLSX / XLS / XLSB)、JSON、JSONL 或 Parquet,查询和汇总数据,用查询结果填充图表。
- 动作校验与模拟执行:按物料字段定义校验修改,并在画布副本上模拟每批动作;成功的批次累加在工作画布上,失败原因返回 Agent 修正,通过的动作按顺序交付。
- 多轮对话与澄清:保存会话历史;需求需要补充时暂停任务,用户回答后继续,也可以取消等待中的任务。
- 实时反馈:通过 SSE 返回处理进度、问答文本增量和最终结果,区分业务失败与执行异常。
- 组件能力扩展:以 JSON 定义物料、以 Skills 描述任务做法,通过 MCP 提供组件目录与字段查询。
快速开始
环境要求
| 依赖 | 说明 |
|---|---|
| Python | 3.11 或以上,版本要求见 pyproject.toml |
| uv | 安装依赖并运行项目,仓库提供 uv.lock |
| MongoDB | 完整大屏编辑所需的物料目录,以及会话、检查点和文件元数据存储 |
| 聊天模型服务 | 通过 ChatOpenAI 接入;需要支持工具调用、结构化输出与流式响应 |
| TypeSafe / Jev | 判断请求是否进入问答流程,需要独立的 API Key |
用户中心和 Langfuse 可在本地开发时关闭。默认物料 MCP 服务由应用自动启动,使用本机 8201 端口;API 默认使用 8000 端口。
不配置 MongoDB 时,会话和检查点可使用进程内存,但物料目录不会回退到本地 JSON,新增、更换组件等能力无法正常使用。首次运行请完成物料初始化。
1. 安装依赖
获取仓库后,在项目根目录运行:
uv sync --locked
2. 配置环境
将 .env.example 复制为 .env,已有配置无需覆盖。填写以下配置:
| 配置 | 说明 |
|---|---|
LLM_API_KEY、LLM_MODEL、LLM_BASE_URL |
聊天模型密钥、模型名称和 API 地址 |
TYPESAFE_API_KEY |
Jev 意图判断服务密钥 |
MONGODB_URI、MONGODB_DB |
MongoDB 连接地址和数据库名 |
AUTH_ENABLED |
本地调试可设为 false,所有请求共用开发用户 |
LANGFUSE_TRACING_ENABLED |
未配置 Langfuse 时设为 false |
客户端默认发送 reasoning_effort=low;模型服务不支持该参数时,设置 LLM_REASONING_EFFORT=。
正式接入用户中心时设置 AUTH_ENABLED=true 和 USER_CENTER_URL,请求携带 Authorization: Bearer <token>。后端通过用户中心的
GET /api/users/me 获取当前用户。
3. 初始化物料目录
首次使用需将 app/resources/materials 中的物料定义导入 MongoDB。工作区包含 scripts/seed_materials.py 时,可运行:
uv run python -m scripts.seed_materials
该脚本会整体替换目标数据库中的物料目录和共享字段分组,更新后需重启服务。当前 scripts/ 未纳入版本控制,新克隆的工作区需另行取得初始化脚本。
4. 启动与验证
uv run uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload
启动后访问:
- Swagger UI:查看请求模型和调试普通接口。
- OpenAPI:获取接口定义。
- 物料目录:确认物料已载入,正常返回
code: 0。
使用指南
创建会话并发起任务
调用 POST /api/conversations 创建会话,取得 data.conversation_id,再向 POST /api/agent/run 提交需求和当前画布:
{
"prompt": "在画布顶部添加标题:销售数据概览",
"screen": {
"canvas": {
"width": 1920,
"height": 1080
},
"nodes": []
},
"context": {
"conversation_id": "创建会话接口返回的 ID"
}
}
开启鉴权后,创建会话和后续请求都需要携带 Bearer token。每次新任务都应提交 当前画布的完整快照;继续同一段对话时复用
conversation_id,历史消息由服务端读取。
处理流式结果
/api/agent/run 和 /api/agent/resume 均返回 text/event-stream。前端使用支持 POST 和响应流读取的客户端,例如 fetch。
| 事件 | 客户端处理方式 |
|---|---|
progress |
展示当前处理阶段 |
answer_delta |
按顺序追加 data.delta;用于问答场景,最终以 final.data.answer 为准 |
final |
根据 data.status 展示结果、应用动作或发起澄清 |
error |
展示 msg 中的执行错误,结束本轮处理 |
每个事件的 data: 行统一为 {code, data, msg} JSON,final.data.status 的含义如下:
completed:本轮完成;若有actions,由前端按顺序应用。此状态不表示前端已经完成修改。waiting_for_input:展示clarification,保留thread_id等待用户回答。failed:业务流程无法完成,answer说明原因,不交付动作草稿。cancelled:用户取消了等待补充的任务。
流开始前的错误通过 HTTP 状态码返回;流开始后的异常通过 error 事件返回。客户端不能仅凭 HTTP 200 判断成功,未收到 final 或
error 就断开应视为结果不完整。
回答澄清问题
当结果为 waiting_for_input 时,向 POST /api/agent/resume 提交:
{
"thread_id": "替换为等待补充的任务 UUID",
"reply": {
"answer": "使用月度销售额,放在画布左侧"
}
}
也可使用 reply.option_id 提交返回的选项,或用 reply: {"cancel": true} 取消暂停中的任务。恢复接口沿用任务所属会话。
上传数据并生成图表
- 创建会话,将文件与
conversation_id一起以multipart/form-data提交到POST /api/files。 - 接口返回 HTTP 202 和
data.file_id,轮询GET /api/files/{file_id}/progress。 - 等待
status=ready后,在同一会话发起 Agent 请求,例如“按月份汇总销售额,生成柱状图”。解析失败时状态为failed,原因在error.msg中。 - Agent 查询文件数据,将结果转换为图表配置,再返回经过校验的编辑动作。
会话中已解析且未过期的文件会自动提供给 Agent,无需每轮重新上传。
接口概览
所有业务接口统一使用 /api 前缀,普通 JSON 响应同样采用 {code, data, msg} 结构。除物料目录和上传限制外,下列接口默认需要登录。
| 方法与路径 | 用途 |
|---|---|
POST /api/conversations |
创建会话 |
GET /api/conversations |
分页查询当前用户的会话 |
GET /api/conversations/{conversation_id} |
读取会话历史 |
POST /api/agent/run |
发起任务,返回 SSE |
POST /api/agent/resume |
恢复或取消暂停中的任务,返回 SSE |
GET /api/materials |
获取公开的组件物料目录 |
GET /api/files/limits |
获取当前上传限制与支持格式,无需认证 |
POST /api/files |
上传文件并后台解析 |
GET /api/files?conversation_id=... |
获取会话文件和进行中的上传 |
GET /api/files/{file_id} |
获取文件详情与表信息 |
GET /api/files/{file_id}/progress |
查询解析进度 |
GET /api/files/{file_id}/tables/{table}/rows |
分页读取并排序表数据 |
POST /api/files/{file_id}/tables/{table}/aggregate |
分组汇总表数据 |
DELETE /api/files/{file_id} |
删除文件 |
完整字段、约束和响应模型以运行中的 Swagger UI 为准;动作协议定义见 app/schemas/actions。
架构设计
flowchart TD
Client[大屏编辑器] --> API[FastAPI / AgentService]
API --> Input[输入与会话上下文]
Input --> Intent[Jev 意图判断]
Intent -->|明确问答且达到阈值| QA[问答 Agent]
Intent -->|其他请求| Screen[大屏 Agent]
Screen --> Materials[MCP 物料与字段查询]
Screen --> Batch[动作校验与整批模拟执行]
Screen -->|需要补充| Wait[LangGraph 暂停]
Wait -->|用户回答并恢复| Input
QA --> Data[数据描述与 DuckDB 查询]
Screen --> Data
Data --> Files[会话文件 / Parquet]
QA --> SSE[SSE 进度与结果]
Screen --> SSE
SSE --> Client
外层 LangGraph 管理路由、任务状态与澄清恢复;内层 LangChain Agent 根据上下文调用工具。只有 Jev 选中问答且概率达到阈值时进入问答分支,其余请求交给大屏 Agent。
大屏 Agent 通过 get_nodes 读取组件完整配置,通过 MCP 查询物料,通过 apply_actions
提交修改。每批动作在工作画布(原始画布加上之前成功的修改)上模拟执行,读取和修改都基于它;字段和动作校验失败时这一批不生效,返回原因由
Agent 修正。上传数据解析为 Parquet,查询结果可直接填入图表配置,避免模型逐行复写数据。
| 技术 | 职责 |
|---|---|
| FastAPI / Pydantic | HTTP 接口、请求与响应模型、字段校验 |
| LangGraph | 外层工作流、检查点、暂停与恢复 |
| LangChain / langchain-openai | Agent 工具循环与聊天模型调用 |
| TypeSafe / Jev | 意图判断 |
| MCP | 组件物料和字段查询,接入额外工具服务 |
| MongoDB | 物料目录、会话历史、图检查点、文件元数据 |
| python-calamine / PyArrow / DuckDB | 表格读取、Parquet 存储、数据查询与汇总 |
| Loguru / Langfuse | 应用日志与可选的调用追踪 |
项目结构
app/
├── main.py # 应用入口与服务生命周期
├── config.py # 环境变量与配置校验
├── api/ # Agent、会话、物料与文件接口
├── services/ # 任务调度、会话存储与持久化装配
├── processor/ # LangGraph 工作流、节点、状态与历史裁剪
├── agent/ # 大屏和问答 Agent、本地工具、Skills 加载
├── actions/ # 动作执行、布局计算与整批模拟
├── datafiles/ # 文件解析、存储、查询、概况与后台任务
├── schemas/ # 请求、画布、动作、会话和数据文件模型
├── resources/ # 物料定义、目录缓存与字段规则
├── clients/ # LLM、Jev、MCP、用户中心客户端
├── prompt/ # 领域约束、意图、问答与数据分析提示词
├── core/ # 认证、异常处理和日志
├── common/ # 统一响应与 SSE 编码
└── observability/ # Langfuse 追踪
mcp_servers/ # 应用管理的本地 MCP 服务
skills/ # 大屏设计、编辑、图表替换与数据制图规则
mcp_servers.json # MCP 服务连接配置
.env.example # 环境变量示例
pyproject.toml # Python 依赖与项目元数据
uv.lock # 依赖锁文件
配置与运行边界
主要可调参数如下,完整配置见 .env.example 和 app/config.py。
| 配置 | 默认值 | 用途 |
|---|---|---|
MAX_AGENT_STEPS / MAX_QA_STEPS |
12 / 8 |
大屏和问答 Agent 的单次模型调用上限;大屏 Agent 没有给出结论时另加 1 次收尾 |
INTENT_QA_MIN_PROBABILITY |
0.5 |
问答分支的判定阈值 |
CONVERSATION_HISTORY_TURNS / HISTORY_MAX_TOKENS |
20 / 4096 |
历史读取轮数与带入模型的 token 预算 |
MONGODB_CHECKPOINT_TTL |
604800 |
检查点保留秒数,0 表示永久保留 |
DATA_FILE_DIR |
data/files |
原始文件和解析结果的磁盘目录 |
DATA_FILE_MAX_MB / DATA_FILE_TTL |
500 / 604800 |
单文件大小上限与保留秒数 |
DATA_FILE_INSIGHTS |
true |
上传后生成文件说明、列含义和图表建议,可关闭 |
DATA_QUERY_TIMEOUT / DATA_EMBED_MAX_ROWS |
10 / 1000 |
单次 SQL 查询超时秒数与每处查询写入组件的行数上限 |
AUTH_ENABLED / AUTH_CACHE_TTL_SECONDS |
true / 30 |
用户中心鉴权与 token 校验缓存秒数 |
MCP_TOOL_TIMEOUT |
5 |
单次 MCP 工具调用超时秒数 |
- 单 worker 运行:任务与会话的并发保护、上传进度保存在进程内;即使配置 MongoDB,也不能据此直接扩展为多 worker。同一会话同时只能执行一个任务。
- 文件存储:MongoDB 保存元数据,原始文件和 Parquet 位于
DATA_FILE_DIR。容器部署需保留该目录,并为反向代理配置匹配的上传大小限制。 - 流式连接:断开连接会取消当前执行,不会转为后台任务。当前没有 SSE 心跳、事件重放或断线续传;代理应关闭响应缓冲,并给模型调用留出足够的等待时间。
- 前端接入:保持
/api前缀;当前应用未配置 CORS,中间开发代理或同源反向代理可将/api转发到后端。 - 日志与追踪:日志写入
logs/app.log。启用 Langfuse 时填写真实的公钥、私钥和服务地址;追踪故障不阻断主业务。
开发与测试
阅读主链路可从 app/main.py 开始,依次查看 Agent 接口、AgentService、外层工作流、大屏 Agent 和 动作模拟。
扩展组件时修改 物料定义,导入数据库后重启服务;修改任务处理规则时查看 skills;新增 MCP
服务时修改 mcp_servers.json,连接配置支持 ${环境变量名} 展开。
当前 .gitignore 排除了 tests/、docs/、scripts/ 等本地辅助目录,普通克隆不会包含这些内容。若工作区已有测试文件,在根目录使用标准库
unittest 运行:
uv run python -m unittest discover -s tests -v
测试所需的 openpyxl 已列入开发依赖,uv sync --locked 默认安装。单元测试不能替代真实模型、Jev、MongoDB、用户中心和前端动作应用的联调验证。
常见问题
| 现象 | 检查项 |
|---|---|
| 会话或 Agent 接口返回 401 | 本地调试确认 AUTH_ENABLED=false;开启鉴权时携带有效 Bearer token |
| 提示物料目录未加载,或 MCP 物料服务无法启动 | 检查 MONGODB_URI、物料初始化结果与 8201 端口,导入后重启服务 |
| Agent 返回 422,提示缺少会话 | 先创建会话,并在 context.conversation_id 中传入返回的 ID |
| 请求返回 409 | 同一会话或任务仍在运行,或尝试恢复未处于等待补充状态的任务 |
| API 可访问,但 Agent 执行失败 | 查看流中的 error、final.data.answer 和 logs/app.log,核对模型、Jev 密钥与服务连通性 |
| 上传返回 202,但文件暂时不可用 | 202 仅表示上传已接收,继续轮询解析进度,等待 ready |
| 前端一直等不到流式反馈 | 检查是否持续读取响应流,以及代理是否缓冲 SSE 或提前超时 |