diff --git a/README.md b/README.md index fe3da3c..8864ca4 100644 --- a/README.md +++ b/README.md @@ -1,295 +1,198 @@ -# Screen Agent · my-v0-server +# Screen Agent -面向可视化大屏编辑器的 AI 后端。用户通过自然语言创建大屏、调整组件和分析上传的数据,服务将需求转换为经过校验的结构化编辑动作,交给前端执行。 +可视化大屏编辑器的后端服务,提供两部分能力: -本仓库提供 API、Agent 工作流、组件物料和数据处理能力。接入时,前端负责提交当前画布快照、展示流式反馈,并应用返回的动作。 +- **AI 编辑**:把自然语言需求转换为经过校验的结构化编辑动作,由前端应用到画布;支持上传数据文件,并用查询结果直接制图。 +- **大屏管理**:大屏的保存、版本发布、回收站与线上访问。 -[快速开始](#快速开始) · [使用指南](#使用指南) · [接口概览](#接口概览) · [架构设计](#架构设计) · [开发与测试](#开发与测试) +## 功能 -## 功能特性 +- 自然语言新建大屏,增删、更换图表,修改组件属性、位置、尺寸与画布,支持对齐和分布。 +- 上传 CSV / TSV / Excel / JSON / JSONL / Parquet,对数据问答、汇总,并将查询结果填入图表。 +- 动作按物料字段定义校验,并在画布副本上整批模拟执行;失败原因回传 Agent 修正,仅交付通过的动作。 +- 多轮对话;信息不足时暂停任务向用户澄清,回答后继续,也可取消。 +- 通过 SSE 推送处理进度、问答文本增量与最终结果。 +- 大屏保存(乐观锁)、发布快照与历史版本、分享链接、回收站、封面图、数据源服务端代理。 -- **自然语言编辑大屏**:新建大屏,新增、删除或更换图表,修改组件属性、位置、尺寸与画布配置,支持对齐和分布等布局操作。 -- **数据驱动的问答与制图**:上传 CSV、TSV、Excel(XLSX / XLS / XLSB)、JSON、JSONL 或 Parquet,查询和汇总数据,用查询结果填充图表。 -- **动作校验与模拟执行**:按物料字段定义校验修改,并在画布副本上模拟每批动作;成功的批次累加在工作画布上,失败原因返回 Agent - 修正,通过的动作按顺序交付。 -- **多轮对话与澄清**:保存会话历史;需求需要补充时暂停任务,用户回答后继续,也可以取消等待中的任务。 -- **实时反馈**:通过 SSE 返回处理进度、问答文本增量和最终结果,区分业务失败与执行异常。 -- **组件能力扩展**:以 JSON 定义物料、以 Skills 描述任务做法,通过 MCP 提供组件目录与字段查询。 +## 架构 + +```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 或以上,版本要求见 [pyproject.toml](pyproject.toml) | -| uv | 安装依赖并运行项目,仓库提供 `uv.lock` | -| MongoDB | 完整大屏编辑所需的物料目录,以及会话、检查点和文件元数据存储 | -| 聊天模型服务 | 通过 `ChatOpenAI` 接入;需要支持工具调用、结构化输出与流式响应 | -| TypeSafe / Jev | 判断请求是否进入问答流程,需要独立的 API Key | - -用户中心和 Langfuse 可在本地开发时关闭。默认物料 MCP 服务由应用自动启动,使用本机 `8201` 端口;API 默认使用 `8000` 端口。 - -> 不配置 MongoDB 时,会话和检查点可使用进程内存,但物料目录不会回退到本地 JSON,新增、更换组件等能力无法正常使用。首次运行请完成物料初始化。 - -### 1. 安装依赖 - -获取仓库后,在项目根目录运行: +**依赖**: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 ``` -### 2. 配置环境 +启动后访问 查看完整接口文档。 -将 [.env.example](.env.example) 复制为 `.env`,已有配置无需覆盖。填写以下配置: +| 配置 | 说明 | +|------|------| +| `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 `,服务调用用户中心 `GET /api/users/me` 校验 | +| `LANGFUSE_TRACING_ENABLED` | 未部署 Langfuse 时设为 `false` | -| 配置 | 说明 | -|--------------------------------------------|----------------------------------------------| -| `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` | +全部配置及默认值见 [.env.example](.env.example) 与 [app/config.py](app/config.py)。 -客户端默认发送 `reasoning_effort=low`;模型服务不支持该参数时,设置 `LLM_REASONING_EFFORT=`。 +**物料初始化**:物料定义位于 [app/resources/materials](app/resources/materials),必须导入 MongoDB 后服务才能增改组件;导入会整体替换现有目录,之后需重启服务。导入脚本为 [scripts/seed_materials.py](scripts/seed_materials.py),可附带自定义物料目录作为参数。 -正式接入用户中心时设置 `AUTH_ENABLED=true` 和 `USER_CENTER_URL`,请求携带 `Authorization: Bearer `。后端通过用户中心的 -`GET /api/users/me` 获取当前用户。 +## 接口 -### 3. 初始化物料目录 +所有接口以 `/api` 为前缀,JSON 响应统一为 `{code, data, msg}`。除标注外均需登录。 -首次使用需将 `app/resources/materials` 中的物料定义导入 MongoDB。工作区包含 `scripts/seed_materials.py` 时,可运行: +### Agent -```sh -uv run python -m scripts.seed_materials -``` +| 接口 | 说明 | +|------|------| +| `POST /agent/run` | 提交需求与当前画布,返回 SSE | +| `POST /agent/resume` | 回答澄清、选择选项或取消任务,返回 SSE | -该脚本会整体替换目标数据库中的物料目录和共享字段分组,更新后需重启服务。当前 `scripts/` 未纳入版本控制,新克隆的工作区需另行取得初始化脚本。 - -### 4. 启动与验证 - -```sh -uv run uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload -``` - -启动后访问: - -- [Swagger UI](http://127.0.0.1:8000/docs):查看请求模型和调试普通接口。 -- [OpenAPI](http://127.0.0.1:8000/openapi.json):获取接口定义。 -- [物料目录](http://127.0.0.1:8000/api/materials):确认物料已载入,正常返回 `code: 0`。 - -## 使用指南 - -### 创建会话并发起任务 - -调用 `POST /api/conversations` 创建会话,取得 `data.conversation_id`,再向 `POST /api/agent/run` 提交需求和当前画布: +请求示例: ```json { "prompt": "在画布顶部添加标题:销售数据概览", - "screen": { - "canvas": { - "width": 1920, - "height": 1080 - }, - "nodes": [] - }, - "context": { - "conversation_id": "创建会话接口返回的 ID" - } + "screen": { "canvas": { "width": 1920, "height": 1080 }, "nodes": [] }, + "context": { "conversation_id": "" } } ``` -开启鉴权后,创建会话和后续请求都需要携带 Bearer token。每次新任务都应提交 **当前画布的完整快照**;继续同一段对话时复用 -`conversation_id`,历史消息由服务端读取。 +每次 `run` 提交画布的完整快照;同一段对话复用 `conversation_id`,历史由服务端读取。 -### 处理流式结果 +SSE 事件: -`/api/agent/run` 和 `/api/agent/resume` 均返回 `text/event-stream`。前端使用支持 POST 和响应流读取的客户端,例如 `fetch`。 +| 事件 | 含义 | +|------|------| +| `progress` | 当前处理阶段 | +| `answer_delta` | 问答文本增量,最终以 `final.data.answer` 为准 | +| `final` | 本轮结果,`data.status` 见下 | +| `error` | 流开始后的执行异常 | -| 事件 | 客户端处理方式 | -|----------------|------------------------------------------------------------------------| -| `progress` | 展示当前处理阶段 | -| `answer_delta` | 按顺序追加 `data.delta`;用于问答场景,最终以 `final.data.answer` 为准 | -| `final` | 根据 `data.status` 展示结果、应用动作或发起澄清 | -| `error` | 展示 `msg` 中的执行错误,结束本轮处理 | +`final.data.status`:`completed`(有 `actions` 时由前端按序应用)、`waiting_for_input`(展示 `clarification`,用 `thread_id` 调用 `resume`)、`failed`(业务失败,不交付动作)、`cancelled`。流开始前的错误以 HTTP 状态码返回;未收到 `final` 或 `error` 即断开,应视为结果不完整。 -每个事件的 `data:` 行统一为 `{code, data, msg}` JSON,`final.data.status` 的含义如下: +### 会话 -- `completed`:本轮完成;若有 `actions`,由前端按顺序应用。此状态不表示前端已经完成修改。 -- `waiting_for_input`:展示 `clarification`,保留 `thread_id` 等待用户回答。 -- `failed`:业务流程无法完成,`answer` 说明原因,不交付动作草稿。 -- `cancelled`:用户取消了等待补充的任务。 +| 接口 | 说明 | +|------|------| +| `POST /conversations` | 创建会话 | +| `GET /conversations` | 分页列出当前用户的会话 | +| `GET /conversations/{id}` | 读取会话历史 | +| `PATCH /conversations/{id}` | 重命名 | +| `DELETE /conversations/{id}` | 删除 | -流开始前的错误通过 HTTP 状态码返回;流开始后的异常通过 `error` 事件返回。客户端不能仅凭 HTTP 200 判断成功,未收到 `final` 或 -`error` 就断开应视为结果不完整。 +### 数据文件 -### 回答澄清问题 +上传后后台解析(HTTP 202),轮询进度至 `ready` 后即可在同一会话中被 Agent 使用;已解析且未过期的文件自动带入后续对话。 -当结果为 `waiting_for_input` 时,向 `POST /api/agent/resume` 提交: +| 接口 | 说明 | +|------|------| +| `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}` | 删除 | -```json -{ - "thread_id": "替换为等待补充的任务 UUID", - "reply": { - "answer": "使用月度销售额,放在画布左侧" - } -} -``` +### 大屏管理 -也可使用 `reply.option_id` 提交返回的选项,或用 `reply: {"cancel": true}` 取消暂停中的任务。恢复接口沿用任务所属会话。 +大屏仅所有者可访问,他人的大屏与不存在同样返回 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) | -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](http://127.0.0.1:8000/docs) -为准;动作协议定义见 [app/schemas/actions](app/schemas/actions)。 - -## 架构设计 - -```mermaid -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 | 应用日志与可选的调用追踪 | +| 接口 | 说明 | +|------|------| +| `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/ -├── 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 # 依赖锁文件 +├── 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)。 -主要可调参数如下,完整配置见 [.env.example](.env.example) 和 [app/config.py](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 运行;同一会话同时只能执行一个任务。 +- **本地存储**:数据文件与封面图写入磁盘(`DATA_FILE_DIR`、`THUMBNAIL_DIR`,默认在 `data/` 下),容器部署需挂载持久卷。 +- **反向代理**:SSE 需关闭响应缓冲并放宽读取超时;上传大小限制需与 `DATA_FILE_MAX_MB` 一致。应用未配置 CORS,请通过同源代理转发 `/api`。 +- **连接中断**:客户端断开会取消当前任务,不支持心跳、事件重放或断线续传。 +- **日志**:写入 `logs/app.log`。 -- **单 worker 运行**:任务与会话的并发保护、上传进度保存在进程内;即使配置 MongoDB,也不能据此直接扩展为多 - worker。同一会话同时只能执行一个任务。 -- **文件存储**:MongoDB 保存元数据,原始文件和 Parquet 位于 `DATA_FILE_DIR`。容器部署需保留该目录,并为反向代理配置匹配的上传大小限制。 -- **流式连接**:断开连接会取消当前执行,不会转为后台任务。当前没有 SSE 心跳、事件重放或断线续传;代理应关闭响应缓冲,并给模型调用留出足够的等待时间。 -- **前端接入**:保持 `/api` 前缀;当前应用未配置 CORS,中间开发代理或同源反向代理可将 `/api` 转发到后端。 -- **日志与追踪**:日志写入 `logs/app.log`。启用 Langfuse 时填写真实的公钥、私钥和服务地址;追踪故障不阻断主业务。 +## 开发 -## 开发与测试 +扩展组件:修改物料定义,重新导入后重启。调整 Agent 行为:修改 [skills](skills) 与 [app/prompt](app/prompt)。新增 MCP 服务:修改 [mcp_servers.json](mcp_servers.json),连接配置支持 `${环境变量}` 展开。 -阅读主链路可从 [app/main.py](app/main.py) -开始,依次查看 [Agent 接口](app/api/agent.py)、[AgentService](app/services/agent.py)、[外层工作流](app/processor/graph.py)、[大屏 Agent](app/agent/screen_agent.py) -和 [动作模拟](app/actions/batch.py)。 - -扩展组件时修改 [物料定义](app/resources/materials),导入数据库后重启服务;修改任务处理规则时查看 [skills](skills);新增 MCP -服务时修改 [mcp_servers.json](mcp_servers.json),连接配置支持 `${环境变量名}` 展开。 - -当前 `.gitignore` 排除了 `tests/`、`docs/`、`scripts/` 等本地辅助目录,普通克隆不会包含这些内容。若工作区已有测试文件,在根目录使用标准库 -`unittest` 运行: +测试使用标准库 `unittest`,目录 `tests/` 不在版本控制内: ```sh -uv run python -m unittest discover -s tests -v +uv run python -m unittest discover -s tests ``` - -测试所需的 `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 或提前超时 |