Files
my-v0-server/README.md
T

19 KiB
Raw Blame History

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} 取消暂停中的任务。恢复接口沿用任务所属会话。

上传数据并生成图表

  1. 创建会话,将文件与 conversation_id 一起以 multipart/form-data 提交到 POST /api/files。
  2. 接口返回 HTTP 202 和 data.file_id,轮询 GET /api/files/{file_id}/progress。
  3. 等待 status=ready 后,在同一会话发起 Agent 请求,例如“按月份汇总销售额,生成柱状图”。解析失败时状态为 failed,原因在 error.msg 中。
  4. 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 或提前超时