Skip to content

Repository files navigation

procurepilot

面向采购业务的多用户、多 Agent 智能协作系统。项目将 Vue 3 聊天前端、FastAPI 聚合 API、DeepAgents/LangGraph 多 Agent 编排、FastMCP 业务工具、OpenSandbox 用户隔离沙箱、MongoDB 持久化和外部 Java ERP 数据源组合成一套完整的采购智能助手。

它不只是“给大模型接几个工具”:系统将简单问答、同步订单操作和耗时采购分析分成不同执行路径,对写操作加入两阶段 Human-in-the-Loop(HITL)保护,对长任务提供后台异步执行与前端状态追踪,并用“工具分组 + 26 合 1 + Skills 渐进加载 + 大结果文件卸载 + 对话摘要”共同控制上下文规模。

仓库边界:当前仓库包含 Agent、Web API、前端、MCP Server、OpenSandbox/MongoDB 编排和 MySQL 初始化数据,但不包含 Java ERP 源码。运行完整系统前,需要另行启动兼容本文接口的 Java ERP 服务(默认 http://127.0.0.1:8080/api)。

推荐Java ERP:https://github.com/KagaribiDev/java-erp

目录

开发者如需按目录逐文件复习实现,请阅读 代码库结构与复习指南

核心能力

  • 多 Agent 分工:主 Agent 只负责理解、路由和汇总;采购订单与采购分析由专业子 Agent 执行。
  • 同步与异步并存:订单操作走同步子 Agent;耗时分析通过 Agent Protocol 后台运行,立即返回 task_id
  • 真实 ERP 数据:本地 FastMCP 将 Java ERP REST API 转换为 8 个业务工具。
  • 采购分析:支持供应商、零部件、历史订单、库存预警、外部价格和市场信息的联合分析。
  • 可视化:对接第三方 MCP 的 26 类图表/地图/关系图,并统一成一个可路由工具。
  • HITL 安全控制:缺少订单字段时暂停补充;真正创建或修改订单前再次请求批准/拒绝。
  • 用户级沙箱:每个登录用户拥有独立 OpenSandbox,同一用户的多条会话共享自己的沙箱。
  • 异步任务隔离:异步任务通过短时签名绑定 user_id + sandbox_id + task_id,报告写入任务专属目录。
  • 自动恢复:沙箱失效后自动重建,并通过稳定代理热替换底层实例;连续错误触发熔断。
  • 长上下文治理:MCP 分组、图表工具合并、Skills 渐进加载、工具大结果 offload、自动/主动摘要共同降低上下文成本。
  • 多层持久化:MongoDB 保存账户、会话归属、LangGraph checkpoint、展示消息、长期记忆、技能、沙箱绑定和异步任务元数据。
  • 实时前端:Vue 3 消费 SSE,实时渲染 token、工具调用、子 Agent 来源、HITL 中断和后台任务状态。

总体架构

flowchart LR
    U[浏览器用户] -->|Vue 3 / Fetch| FE[前端 :3000]
    FE -->|JWT + REST/SSE| API[FastAPI Web API :8090]

    API --> AUTH[认证与会话归属]
    API --> LOADER[AgentLoader]
    LOADER --> MAIN[主 Agent Graph]

    MAIN --> ORDER[同步 procurement-order]
    MAIN -->|start_async_task| PROTO[Agent Protocol :2024]
    PROTO --> ANALYST[异步 procurement-analyst]

    ORDER --> MCP[FastMCP ERP Server :8000]
    ANALYST --> MCP
    MCP -->|HTTP| JAVA[外部 Java ERP :8080]
    JAVA --> MYSQL[(MySQL motorparts_db)]

    MAIN --> CHART[第三方可视化 MCP]
    ANALYST --> CHART
    MAIN --> SEARCH[智谱 Web Search]
    ANALYST --> SEARCH

    LOADER --> SANDBOX[OpenSandbox Server :8100]
    SANDBOX --> S1[用户 A 沙箱]
    SANDBOX --> S2[用户 B 沙箱]

    API --> MONGO[(MongoDB :27017)]
    MAIN --> MONGO
    PROTO --> MONGO
Loading

一次普通请求的核心路径如下:

sequenceDiagram
    participant B as Browser
    participant F as FastAPI
    participant L as AgentLoader
    participant S as User Sandbox
    participant A as Main Agent
    participant M as MCP/ERP

    B->>F: POST /api/chat/stream + JWT
    F->>F: 校验 JWT 与 thread 所有权
    F->>L: get_agent_for_user(user_id)
    L->>S: 获取/重连/创建用户沙箱
    L->>A: 创建绑定该沙箱的请求级 Graph
    A-->>B: SSE token/tool_start/tool_args
    A->>M: 委派子 Agent 并调用业务工具
    M-->>A: 真实 ERP 结果
    A-->>B: SSE tool_result/token/done
    F->>F: 保存 checkpoint 与展示消息
Loading

服务与端口

服务 默认地址 作用 启动方式
Vue 前端 http://127.0.0.1:3000 登录、聊天、历史、工具面板、HITL、异步任务抽屉 npm run devstart_web.py
FastAPI Web API http://127.0.0.1:8090 认证、会话、SSE、任务查询、Agent 生命周期 uvicornstart_web.py
LangGraph Agent Protocol http://127.0.0.1:2024 异步采购分析 thread/run 执行 langgraph devstart_web.py
FastMCP ERP Server http://127.0.0.1:8000/mcp 将 Java ERP REST API 暴露为 MCP 工具 独立启动
Java ERP(外部) http://127.0.0.1:8080/api 供应商、零件、库存、订单业务数据源 由外部 Java 项目启动
OpenSandbox http://127.0.0.1:8100 创建、连接、销毁隔离执行容器 Docker Compose
MongoDB mongodb://127.0.0.1:27017 Agent、会话、用户、技能、任务持久化 Docker Compose
MySQL(外部) 通常 127.0.0.1:3306 Java ERP 业务数据库 使用 motorparts_db.sql 初始化

注意:start_web.py 会启动 Agent Protocol、FastAPI 和前端,但不会启动 Docker、Java ERP 或 FastMCP。FastMCP 必须在 FastAPI 初始化 Agent 前可用,否则核心 ERP 工具加载会失败。

快速启动

1. 环境要求

  • Python 3.11+pyproject.toml 要求 <4.0
  • Node.js 18+ 与 npm
  • Docker Desktop / Docker Engine,支持 Docker Compose v2
  • MySQL 8(SQL 文件来源版本为 8.0.21)
  • 一个兼容的 Java ERP 服务,监听 127.0.0.1:8080
  • DeepSeek/OpenAI 兼容模型服务密钥
  • 智谱 API Key(用于 Web Search)

Windows 是当前主要开发环境;Linux/macOS 也可运行,但需将虚拟环境激活命令替换为对应平台写法。

2. 安装 Python 依赖

推荐使用 uv

uv sync

或使用标准虚拟环境:

python -m venv .venv

# Windows PowerShell
.\.venv\Scripts\Activate.ps1

# Linux/macOS
source .venv/bin/activate

pip install -r requirements.txt

3. 配置环境变量

在项目根目录创建 .env。不要把真实密钥提交到 GitHub。

# 主模型与摘要模型使用 OpenAI-compatible 接口
DEEPSEEK_API_KEY=your_deepseek_compatible_api_key
DEEPSEEK_BASE_URL=https://your-provider.example.com/v1

# web_search 工具
ZHIPU_API_KEY=your_zhipu_api_key

# 生产环境必须更换,并保证 Web API 与 Agent Protocol 进程一致
JWT_SECRET_KEY=replace-with-a-long-random-secret

# 可选覆盖
MONGODB_URI=mongodb://root:123456@127.0.0.1:27017/?authSource=admin
ASYNC_AGENT_PROTOCOL_URL=http://127.0.0.1:2024
JWT_EXPIRE_HOURS=12
SANDBOX_IMAGE=erp-openclaw-sandbox:v1
SANDBOX_RUNTIME_INSTALL_FALLBACK=false
APP_ENV=development

当前模型名称在 src/agent/core/config.py 中配置:

  • 主模型:deepseek-v4-pro
  • 摘要模型:deepseek-v4-flash
  • 备用模型:glm-5.1(当前主流程未自动切换到备用模型)

如果你的服务商没有这些模型名,请在 src/agent/core/config.py 中替换为其 OpenAI-compatible 模型名称。

配置项与代码常量

当前项目同时存在环境变量配置和代码常量配置。使用默认本地端口时无需调整;部署到远程主机或修改账号时,应同时检查下表,避免只改 .env 但某个子系统仍连接旧地址。

配置 默认值/是否必需 读取位置 说明
DEEPSEEK_API_KEY 必需 agent/core/env_utils.py 主模型和摘要模型密钥
DEEPSEEK_BASE_URL 必需 agent/core/env_utils.py OpenAI-compatible base URL
ZHIPU_API_KEY Web Search 必需 agent/core/env_utils.py 智谱搜狗搜索工具
ZHIPU_BASE_URL 仅备用模型需要 agent/core/env_utils.py GLM OpenAI-compatible URL
JWT_SECRET_KEY 有开发默认值 web_config.pycore/async_sandbox_claims.py 登录 JWT 与异步沙箱 claim 共用;生产必须覆盖
JWT_EXPIRE_HOURS 12 web_config.py 登录 token 有效期
ASYNC_AGENT_PROTOCOL_URL http://127.0.0.1:2024 graphs/main_agent.pyapi/tasks.py 启动和查询异步任务
MONGODB_URI 本地 root URI web_config.py 认证、展示消息、沙箱管理和任务 API
SANDBOX_IMAGE erp-openclaw-sandbox:v1 sandbox_setup.py 新沙箱镜像
SANDBOX_RUNTIME_INSTALL_FALLBACK false sandbox_setup.py 是否允许损坏环境现场装包
APP_ENV development graphs/main_agent.py production 时日志写入 erp_agent.log

目前以下连接信息仍是代码常量:

常量 默认值 文件
Java ERP base URL http://127.0.0.1:8080/api src/mcp_server/server_config.py
本地 ERP MCP URL http://127.0.0.1:8000/mcp src/agent/tools/mcp_client.py
第三方分析 MCP URL ModelScope MCP URL src/agent/tools/mcp_client.py
OpenSandbox URL http://127.0.0.1:8100 src/agent/core/config.py
Agent Store/checkpoint Mongo URI 本地 root URI src/agent/core/config.py
模型名称与参数 deepseek-v4-pro/flash src/agent/core/config.py

特别注意:MONGODB_URI 环境变量由 Web API 配置读取,但 Agent 的 MongoDBStoreMongoDBSaver 当前仍使用 src/agent/core/config.py 中的 URI 常量。若不使用默认 MongoDB 地址,必须同步修改该常量,或在二次开发时将它统一改为读取同一个环境变量。

4. 启动 MongoDB、OpenSandbox 和项目沙箱镜像

docker compose -f docker/docker-compose.yml up -d --build
docker compose -f docker/docker-compose.yml ps

首次执行会构建 erp-openclaw-sandbox:v1。该镜像预装 pandas、numpy、matplotlib、requests、BeautifulSoup、lxml 和 markdownify,避免每个新用户首次使用时在线安装依赖。

健康检查:

curl http://127.0.0.1:8100/health
docker compose -f docker/docker-compose.yml exec mongodb mongosh --quiet \
  -u root -p 123456 --authenticationDatabase admin \
  --eval "db.adminCommand('ping')"

5. 准备 Java ERP 与 MySQL

  1. 创建 MySQL 数据库并导入 motorparts_db.sql
mysql -u root -p < motorparts_db.sql
  1. 启动兼容的 Java ERP,确保基础地址为:
http://127.0.0.1:8080/api
  1. 至少需要实现 MCP 所依赖的这些 REST API:
方法 路径 用途
GET /suppliers/search?name= 模糊查询供应商
GET /parts/page 分页/条件查询零部件
GET /parts/search?name= 按名称查询零部件
GET /parts/supplier/{id} 查询供应商对应零部件
GET /inventory/warning 查询库存预警
GET /orders/search-details 查询订单明细
POST /orders/create 创建采购订单
PUT /orders/update/{id} 更新采购订单

验证示例:

curl "http://127.0.0.1:8080/api/parts/search?name=火花塞"

6. 启动 FastMCP ERP Server

python src/mcp_server/server_main.py

MCP Server 使用一个生命周期级 httpx.AsyncClient 连接池访问 Java ERP,避免每次工具调用重新建立连接。

7. 一键启动 Agent Protocol、Web API 和前端

另开终端,在项目根目录执行:

uv run python start_web.py

启动器会依次:

  1. 启动 langgraph dev,等待 http://127.0.0.1:2024/ok
  2. 启动 FastAPI,等待 http://127.0.0.1:8090/health
  3. 检查并安装前端依赖,然后启动 Vite;
  4. Ctrl+C 时停止这三个子进程。

浏览器打开:

http://127.0.0.1:3000

API 文档:

  • FastAPI:http://127.0.0.1:8090/docs
  • Agent Protocol:http://127.0.0.1:2024/docs

完整启动方式

需要分别观察日志或部署到不同主机时,可以手工启动各服务。

Agent Protocol

langgraph dev --host 127.0.0.1 --port 2024 \
  --no-browser --no-reload --allow-blocking

langgraph.json 将图 procurement_analyst_async 映射到:

./src/agent/graphs/async_analyst.py:create_procurement_analyst

FastAPI

Windows PowerShell:

$env:PYTHONPATH = "$PWD\src"
python -m uvicorn api_view.web_main:app --host 127.0.0.1 --port 8090

Linux/macOS:

PYTHONPATH=src python -m uvicorn api_view.web_main:app \
  --host 127.0.0.1 --port 8090

前端

cd frontend
npm install
npm run dev

Vite 将 /api 代理到 http://localhost:8090

停止 Docker 服务

# 保留 MongoDB/OpenSandbox 命名卷
docker compose -f docker/docker-compose.yml down

# 危险:同时删除 MongoDB 和 OpenSandbox 持久化卷
docker compose -f docker/docker-compose.yml down -v

多 Agent 架构

角色划分

Agent 执行方式 责任 主要工具
主 Agent 请求内同步 理解意图、读取偏好、任务路由、结果汇总、技能管理、普通问答 task、异步任务工具、web_searchassign_skill、文件工具
procurement-order 同步子 Agent 订单查询、字段校验、信息补充、创建/修改和审批 订单 MCP、request_order_infoweb_search
procurement-analyst Agent Protocol 后台异步 库存、供应商、行情、比价、成本评估、图表和报告 分析 MCP、可视化、Web Search、采购 Skills、沙箱文件系统

主 Agent 的关键原则是“协调者,不是业务执行者”。涉及 ERP 数据的分析任务必须交给分析 Agent;订单意图必须交给订单 Agent。这样可以限制每个 Agent 所见工具,降低误调用概率,并让提示词、Skills 和调用上限按业务独立配置。

Graph Factory 与启动预计算

AgentLoader.initialize() 在 FastAPI 启动时只做一次重操作:

  1. 初始化 MongoDB 与沙箱管理器;
  2. 连接 MCP Server 并加载工具;
  3. 将 26 个图表工具合并成统一入口;
  4. 读取子 Agent YAML;
  5. 启动一个后台预热沙箱。

这些结果存入 PrecomputedContext。每次用户请求只执行轻量的请求级 Graph 创建:获取该用户稳定沙箱代理、构建 CompositeBackend、创建沙箱相关工具、组装 Middleware,并调用 create_deep_agent()

这一设计同时满足:

  • MCP 工具不在每个请求中重复发现;
  • 每个请求仍能绑定正确用户的沙箱和上下文;
  • 沙箱重建后 Graph 与工具引用不失效;
  • 不同用户不会共用同一个执行容器。

YAML 驱动的子 Agent

子 Agent 位于 src/agent/subagents/configs/

  • procurement_order.yaml
  • procurement_analyst.yaml

每个配置包含 namedescriptionsystem_prompttools、可选 skillsmodelinterrupt_onloader.py 根据工具名称子串解析实际工具对象并去重。

新增子 Agent 时,不需要把长系统提示词塞进 graphs/main_agent.py;新增 YAML 后,再决定它是同步加入主 Graph,还是像分析 Agent 一样发布成独立 Agent Protocol graph。

同步订单 Agent

订单流程:

flowchart TD
    A[用户提出订单需求] --> B[主 Agent 委派 procurement-order]
    B --> C{必填字段齐全?}
    C -- 否 --> D[request_order_info 中断]
    D --> E[用户补充自由文本]
    E --> C
    C -- 是 --> F[准备 order_create/order_update]
    F --> G{HITL 最终审批}
    G -- reject --> H[终止,不写 ERP]
    G -- approve --> I[调用 MCP 写入 Java ERP]
    I --> J[校验并返回订单结果]
Loading

第一层中断用于补齐 partIdquantityunitPrice 等必要信息;第二层 interrupt_on 在执行 order_createorder_update 前要求用户批准。中断状态由 MongoDB checkpoint 保存,因此一次 HTTP 流结束后可以通过 /resume 接着执行。

异步 Agent 完整链路

耗时采购分析不能阻塞主对话。项目使用 DeepAgents AsyncSubAgentMiddleware 与 LangGraph Agent Protocol 实现后台任务。

sequenceDiagram
    participant U as 用户
    participant M as 主 Agent
    participant W as Web API/Middleware
    participant P as Agent Protocol
    participant A as Async Analyst
    participant S as 用户私有沙箱
    participant DB as MongoDB
    participant F as 前端

    U->>M: 请求库存分析/比价/报告
    M->>W: start_async_task(description)
    W->>P: 创建 thread + run
    W->>W: 签名 user_id+sandbox_id+task_id
    W->>DB: 保存 async_tasks 元数据
    W-->>M: task_id
    M-->>U: 立即返回任务 ID
    P->>A: 创建 procurement_analyst graph
    A->>A: 验证 sandbox_claim
    A->>S: 连接原用户沙箱并续期 2 小时
    A->>S: mkdir /analysis/tasks/{task_id}
    A->>S: 查询、分析、生成报告
    loop 每 5 秒
        F->>W: GET /api/tasks
        W->>P: 查询 run 状态/最终 thread state
        W-->>F: running/success/error + result
    end
Loading

为什么返回 task_id

task_id 同时是 Agent Protocol 的 thread ID,也是任务工作目录标识。主对话只负责启动任务并立即返回,不在同一轮里等待或反复查询。用户可以继续聊天;前端右侧抽屉每 5 秒刷新任务状态,完成后自动显示结果。

可用的异步操作

中间件保留标准异步工具集:

  • start_async_task:启动任务;
  • list_async_tasks:列出当前 Graph 跟踪的任务;
  • check_async_task:检查状态与结果;
  • update_async_task:追加要求,并用 multitask_strategy="interrupt" 更新运行;
  • cancel_async_task:取消任务。

项目替换了标准 start_async_taskupdate_async_task 的实现,用于注入可信用户/沙箱绑定;其余任务工具沿用框架实现。

异步任务安全绑定

浏览器和模型都不能任意指定 sandbox_id。Web 后端使用 JWT_SECRET_KEY 生成 6 小时有效的 HS256 claim,包含:

sub/user_id + sandbox_id + task_id + purpose + iat + exp

Agent Protocol 必须验证:

  • 签名正确;
  • purpose == async-user-sandbox
  • claim 中的 task_id 与当前任务一致;
  • 用户和沙箱标识非空。

验签后才连接沙箱。每个任务只能写入 /analysis/tasks/{task_id}/,从而形成“用户级容器隔离 + 任务级目录隔离”两层边界。

异步任务状态持久化

async_tasks 集合记录 task_iduser_idsandbox_idrun_id、状态和时间。GET /api/tasks 只按 JWT 用户查询,并向 Agent Protocol 刷新状态。对于功能上线前创建的旧任务,系统还会从该用户的展示消息中提取历史 UUID 并迁移元数据。

MCP 架构与上下文优化

本地 ERP MCP

src/mcp_server/server_main.py 使用 FastMCP 的 Streamable HTTP 传输发布 8 个工具:

分组前缀 工具 Java ERP 映射 写操作
supplier_ supplier_query GET /suppliers/search
part_ part_query GET /parts/page
part_ part_search GET /parts/search
part_ part_by_supplier GET /parts/supplier/{id}
inventory_ inventory_warning GET /inventory/warning
order_ order_search_details GET /orders/search-details
order_ order_create POST /orders/create 是,受 HITL 保护
order_ order_update PUT /orders/update/{id} 是,受 HITL 保护

MCP lifespan 只创建一个 httpx.AsyncClient,配置最大 100 个连接和 20 个 keep-alive 连接;每个工具通过 Context 获取这个客户端。

MCP 工具分组

Agent 客户端按名称前缀分组:

分析类:supplier_* / part_* / inventory_*
订单类:order_*
图表类:generate_*

然后只把匹配的工具交给对应子 Agent。分组的价值不仅是代码整洁,还包括:

  • 减少模型每次决策时看到的工具数量;
  • 缩短工具 Schema 占用的上下文;
  • 避免分析 Agent 获得订单写权限;
  • 降低名称相似工具之间的误选择概率。

26 个图表工具合并为 1 个

第三方分析 MCP 通常返回 27 个 generate_* 工具,其中 26 个是图表、地图和关系图,另一个是 spreadsheet。直接把 26 套完整 JSON Schema 全部放进模型上下文会带来明显的 token 浪费。

chart_generator.py 的处理方式:

  1. 启动时读取第三方工具;
  2. generate_bar_chartgenerate_line_chart 等映射到 chart_type
  3. 对外只暴露:
generate_visualization(chart_type, chart_config)
  1. 工具描述中只保留约一页的紧凑速查;
  2. 完整参数说明存放在 /skills/procurement/chart_params.md
  3. Agent 只有在不确定参数时才读取该文件;读取一次后可连续生成多张图。

支持的可视化包括柱状图、折线图、饼图、面积图、雷达图、箱线图、直方图、漏斗图、桑基图、瀑布图、词云、地图、流程图、网络图、鱼骨图和思维导图等。

防止上下文爆炸的完整策略

项目不是依赖单一摘要机制,而是分层治理:

层级 机制 作用
工具发现 MCP 仅在启动时预计算一次 避免每次请求重复拉取和解析 Schema
工具权限 按业务前缀分组 每个 Agent 只看到必要工具
工具数量 26 个图表工具合并成 1 个路由工具 大幅减少工具描述长度
参数文档 完整 Schema 写入 Skill 文件 用到时再加载,渐进式披露
工具结果 DeepAgents 自动 offload 大于约 20k token 的结果 上下文仅保留文件路径和预览
对话历史 上下文接近约 85% 时自动摘要 防止长会话超过模型窗口
主动压缩 compact_conversation 子 Agent 返回长报告后主动压缩
最终交付 报告/图表写入沙箱文件 回复只返回摘要、结论和路径
调用上限 Model/Tool Call Limit 防止循环调用导致成本和上下文失控

第三方可视化 MCP 不可用时,启动流程会降级图表能力并保留 ERP 核心工具;本地 ERP MCP 加载失败则视为核心依赖失败。

Middleware 中间件栈

主 Agent 按以下顺序组装 Middleware:

顺序 Middleware 作用
1 SandboxHealthMiddleware 请求前执行 echo ok,失败时自动重建用户沙箱并重新上传 AGENTS.md
2 ContextInjectionMiddleware 把可信 user_id、用户名和偏好路径注入系统消息
3 SkillsSyncMiddleware 计算本地技能文件 MD5,只上传新增/变化内容
4 UserSkillsRestoreMiddleware 从用户私有 Store namespace 恢复动态技能到沙箱
5 SummarizationToolMiddleware 自动摘要,并提供 compact_conversation 主动摘要工具
6 MemoryUpdateMiddleware ERP 对话后用摘要模型提取供应商与查询主题,更新长期记忆
7 SandboxCircuitBreakerMiddleware 连续沙箱工具错误达到阈值后跳转结束,阻止无限恢复循环
8 AsyncSubAgentMiddleware 提供后台任务工具并注入签名用户/沙箱绑定
9 ModelCallLimitMiddleware 主运行最多 50 次模型调用
10 ToolCallLimitMiddleware 主运行最多 200 次工具调用

订单子 Agent 另有更紧的上限:20 次模型调用、50 次工具调用。分析 Agent 的提示词、Skills 和异步进程承担长任务,不占用主聊天请求。

自动记忆更新

MemoryUpdateMiddleware 只处理有采购含义的对话:

  1. 找最后一条用户消息;
  2. 跳过问候和无意义模式;
  3. 检查采购关键词或子 Agent 调用;
  4. 使用摘要模型提取供应商和查询摘要;
  5. 合并最近 10 个供应商、最近 5 条查询;
  6. 写回用户偏好文件。

内部摘要调用带 internal-memory-update tag,SSE 桥会过滤这些 token,避免把维护用 JSON 泄漏给用户。

Skills 技能系统

Skills 不是一次性塞进 system prompt 的长文本,而是沙箱中的文件化操作手册。Agent 先发现技能目录,再按任务读取具体 SKILL.md 和脚本。

Scope 与目录

Agent Scope 沙箱路径
主 Agent main /skills/main/
procurement-analyst procurement /skills/procurement/
procurement-order order /skills/order/

仓库预置技能:

技能 用途
skill-management 下载/创建、验证、分配和持久化用户技能
procurement-analysis 采购分析五步流程、图表选择和报告模板
supplier-price-urls 供应商/零件到报价页面的 URL 映射
web-scraper 在沙箱内直接抓取页面并转换为 Markdown
web-content-fetcher 常规抓取失败时使用第三方 Markdown 转换服务

两类技能来源

flowchart LR
    LOCAL["src/skills 预置技能"] -->|SkillsSyncMiddleware| SB["/skills/... 沙箱"]
    USER["用户下载/创建技能"] --> TEST["沙箱内测试"]
    TEST --> ASSIGN["assign_skill"]
    ASSIGN --> STORE[(MongoDB Store)]
    STORE -->|UserSkillsRestoreMiddleware| SB
Loading
  • 预置技能:来自仓库 src/skills/,新沙箱初始化时播种,运行前通过 MD5 增量同步。
  • 用户技能:先进入 /skills/main/{name},在沙箱内测试,通过后复制到目标 scope,并持久化到 ("users", user_id, "skills") namespace。

用户技能生命周期

  1. 下载 ZIP 或由 Agent 创建 SKILL.md
  2. 在沙箱内检查 frontmatter、语法和脚本入口;
  3. 通过 assign_skill(skill_name, agent_name) 分配;
  4. 持久化每个技能文件;
  5. 清理压缩包;
  6. 新会话开始时自动恢复到同一用户沙箱。

未验证的技能代码不在宿主机执行。需要额外依赖时,安装发生在用户沙箱的 /opt/skills-venv 中。

OpenSandbox 核心机制

沙箱机制是本项目最重要的安全和可靠性基础。

为什么需要沙箱

DeepAgents 可以执行 shell、Python、文件读写和技能脚本。如果直接在 Web API 宿主机运行,模型生成的命令可能接触应用源码、密钥、其他用户文件和系统进程。OpenSandbox 把这些能力放进独立 Docker 容器中,并通过受控 SDK 暴露 execute/read/write/edit/upload/download

OpenSandbox Server

Docker Compose 中的 opensandbox-server

  • 宿主机 8100 映射容器内 8090
  • 挂载 /var/run/docker.sock 创建沙箱容器;
  • 使用 opensandbox.toml 配置 Docker runtime;
  • 状态保存到 opensandbox-data 命名卷中的 SQLite;
  • 默认 bridge 网络;
  • 端口范围 40000-60000
  • 启用 no_new_privileges
  • 丢弃 NET_ADMINNET_RAWSYS_ADMINSYS_PTRACE 等高风险 capabilities;
  • 每个容器 pids_limit=4096

本地开发通过 OPENSANDBOX_INSECURE_SERVER=YES 允许无 API Key 启动。该模式不能直接暴露到公网。

用户级沙箱生命周期

每个用户一个沙箱,同一用户的多条聊天 thread 共享它。生命周期状态:

stateDiagram-v2
    [*] --> MemoryCache: 已在进程缓存
    MemoryCache --> Ready: ping 成功
    MemoryCache --> Recreate: ping 失败
    [*] --> MongoReconnect: MongoDB 有 sandbox_id
    MongoReconnect --> Ready: connect + ping 成功
    MongoReconnect --> Recreate: 过期/不可达
    [*] --> WarmReserve: 新用户且预热池可用
    WarmReserve --> Ready: 认领并持久化
    [*] --> Create: 无记录且无预热沙箱
    Create --> Ready
    Recreate --> Ready: 创建新实例并更新 MongoDB
Loading

关键实现:

  • sandbox_registry 保存 user_id -> sandbox_id
  • 每个用户有独立异步锁,避免并发请求重复创建;
  • 服务启动时预热一个沙箱;新用户认领后后台补充新的预热实例;
  • 老用户优先重连自己的持久化沙箱,绝不会错误认领预热实例覆盖绑定;
  • Web API 关闭时只清理本地连接和预热实例,已分配用户沙箱保留以供重启重连。

稳定代理与热替换

Graph、工具和 CompositeBackend 都持有沙箱对象引用。如果底层容器过期后简单创建新对象,旧引用仍会指向失效容器。

SandboxBackendProxy 显式代理 SandboxBackendProtocol 的同步/异步方法,并提供:

replace_backend(new_backend)

恢复时只替换代理内部 backend,Graph、工具和 Middleware 继续使用同一个稳定代理对象。这让沙箱健康恢复对上层透明。

项目专用预构建镜像

docker/sandbox-image/Dockerfile 基于 OpenSandbox Code Interpreter 镜像构建:

  • Python 3.11 venv:/opt/skills-venv
  • 预装分析/抓取依赖;
  • 预创建 /analysis/temp/analysis/tasks/data
  • 写入镜像 marker;
  • 非交互 shell 执行时把 Python、Node、Go、Java 路径注入 PATH

每个新沙箱默认资源:2 CPU、4 GiB 内存、2 小时 TTL。创建后只做 marker/import 快速检查。只有显式设置 SANDBOX_RUNTIME_INSTALL_FALLBACK=true 才允许依赖损坏时现场安装;生产环境建议保持 false,让错误快速暴露。

CompositeBackend:一个虚拟文件系统,两种存储介质

主 Agent 使用 CompositeBackend

虚拟路径 实际后端 隔离方式 用途
默认路径,如 /analysis/data/skills 用户 OpenSandbox 每用户独立容器 脚本、报告、图表、技能运行
/memories/ MongoDB StoreBackend namespace 为用户 ID 长期偏好和近期业务记忆
/persisted-skills/ MongoDB StoreBackend users/{user_id}/skills 用户动态技能跨会话恢复

对 Agent 来说它们都是文件路径;对系统来说,执行文件在沙箱,长期数据在 MongoDB。这避免沙箱 TTL 到期后丢失记忆和已安装技能。

沙箱保护链

  1. 每轮前 echo ok 健康检查;
  2. 失败则创建新沙箱;
  3. 通过稳定代理热替换;
  4. 重新播种目录、Skills、Python 环境和 AGENTS.md
  5. 更新 MongoDB 中的实际 sandbox ID;
  6. 连续沙箱错误达到阈值时熔断并结束本轮,避免无限循环。

SSE 事件流与 HITL

为什么使用 SSE

聊天请求需要 POST body、JWT、实时 token、工具进度和中断事件。前端使用 fetch + ReadableStream 消费 text/event-stream,而不是浏览器 EventSource,因为 EventSource 不方便发送 POST JSON 和 Authorization header。

后端调用:

agent.astream(
  stream_mode=["messages", "values"],
  subgraphs=True,
  version="v2"
)

messages 流用于 token/工具渲染,values 流用于检测 LangGraph interrupt。subgraphs=True 允许同步子 Agent 的输出进入同一条 SSE;namespace 中的 tools:{subagent} 用于标记消息来源。

SSE 事件类型

每个事件格式为:

data: {JSON}\n\n
type 关键字段 前端行为
token content, source 追加主 Agent/子 Agent 文本
tool_start tool_call_id, tool_name, source 创建工具调用卡片
tool_args args, source 流式追加工具参数
tool_result text, images, source 填充工具结果与图片
tool_end tool_call_id, tool_name 将工具状态标记为完成
interrupt interrupt_type, 业务字段 显示补充信息或审批 UI
done thread_id, content, interrupted 结束加载状态并保存会话
error message 显示错误

后端维护工具调用栈,支持“主 Agent 调子 Agent、子 Agent 再调 MCP”的嵌套结构。工具结果不会重复作为 AI token 输出。

中断与恢复

两种中断:

  • order_info_supplement:展示缺失字段和已收集数据,用户输入自由文本;
  • hitl_approval:展示待执行的订单动作,用户选择 approve 或 reject。

恢复请求:

{"resume": {"supplement": "物料 ID=1,数量=10,单价=12.5"}}

或:

{"resume": {"decisions": [{"type": "approve"}]}}

后端使用 Command(resume=...) 从 MongoDB checkpoint 恢复原 Graph,而不是启动一个新对话。

展示消息与 checkpoint 为什么分开

LangGraph checkpoint 保存可恢复的执行状态,但前端还需要主/子 Agent 来源、工具参数、图片和调用状态。项目将完整展示消息逐条写入 session_display_messages

  • 每条消息一个 MongoDB 文档,避免整段会话逼近 16 MB;
  • 单字段超过 500,000 字符时截断;
  • 中断时也保存当前 UI 状态;
  • 历史页优先读取展示消息,旧会话可回退 checkpoint 序列化逻辑。

认证与多用户隔离

认证

  • 用户保存在 MongoDB users
  • 用户名允许 3-32 位中文、字母、数字、下划线或连字符;
  • 密码要求 8-128 位;
  • 使用 hashlib.scrypt,参数 N=16384, r=8, p=1,每个用户随机 16 字节 salt;
  • 登录成功签发 HS256 JWT,默认 12 小时;
  • 前端把 token 保存到 sessionStorage,浏览器标签页会话结束后失效;
  • 收到 401 时前端清除 token 并回到登录页。

隔离边界

数据/能力 隔离键
用户账户 MongoDB _id
会话访问 session_owners.thread_id + user_id
Checkpoint thread_id,访问前额外校验 owner
展示消息 thread_id + user_id
长期记忆 Store namespace (user_id,)
动态 Skills ("users", user_id, "skills")
OpenSandbox sandbox_registry.user_id
异步任务 async_tasks.user_id + 签名 claim
异步文件 用户沙箱 + /analysis/tasks/{task_id}

客户端不能通过请求体覆盖 user_id。所有身份都来自已验证 JWT;复用其他用户的 thread_id 会被拒绝。

存储架构

MongoDB

默认数据库:langchain_db

集合 内容 关键索引/特点
users 用户名、显示名、scrypt 密码哈希、状态 username_normalized 唯一
session_owners thread 与用户归属 thread_id 唯一;用户+更新时间索引
checkpoints LangGraph 对话状态、HITL 中断状态 MongoDBSaver 管理
checkpoint_writes checkpoint 增量写入 由 LangGraph MongoDB saver 管理
session_display_messages 前端需要的完整消息和工具状态 每条消息独立文档,按 thread/index 排序
store_items 用户长期记忆与动态技能 namespace_path + key 唯一
sandbox_registry 用户与 OpenSandbox ID 的稳定绑定 user_id 唯一
async_tasks 异步任务元数据与最终状态 task_id 唯一;用户+创建时间索引

自定义 MongoDBStore 使用 JSON 标量 namespace_path,而不是直接对 namespace 数组建立复合唯一索引。这样 ("users", "A", "skills")("users", "B", "skills") 不会因共享数组元素而发生 MongoDB multikey 唯一索引冲突。

当前 Store 实现支持精确 namespace/filter 查询,不提供向量语义搜索。

OpenSandbox 状态

  • OpenSandbox 服务自身状态:Docker 命名卷 opensandbox-data
  • 每用户执行文件:对应沙箱容器生命周期内;
  • 需要长期保存的偏好和 Skills:转存 MongoDB;
  • 后端下载的报告:宿主机 src/download/

Java ERP / MySQL

motorparts_db.sql 包含:

customer, inventory, logistics, order_detail,
part, purchase_order, supplier, user

该数据库属于外部 Java ERP,不与 Agent 的 MongoDB 混用。MCP Server 是两套系统之间的边界适配层。

浏览器存储

Key 存储 作用
erp_agent_access_token sessionStorage 当前登录 JWT
erp_async_tasks_collapsed localStorage 记住后台任务抽屉展开/收起状态

前端架构

前端采用 Vue 3 Composition API,无额外状态管理库。

主要组件:

组件 作用
App.vue 认证状态、会话、SSE、任务轮询、中断恢复的总协调
AuthScreen.vue 注册/登录
Sidebar.vue 用户信息、会话搜索、切换、新建和删除
ChatArea.vue 混合渲染用户、Agent 和工具消息
MessageItem.vue 单条消息展示
ToolCallPanel.vue 工具名称、参数、结果和图片
InterruptBanner.vue 数据补充与订单审批
AsyncTaskPanel.vue 右侧可收缩后台任务抽屉
InputArea.vue 发送、停止、显示/隐藏工具调用
MarkdownRenderer.vue Markdown、代码高亮和结果内容

异步任务面板每 5 秒请求 /api/tasks。展开时固定在画面右侧并独立滚动;收起后只保留任务数量入口,不参与聊天区域布局。

API 说明与示例

路由总览

方法 路径 鉴权 说明
POST /api/auth/register 注册并返回 JWT
POST /api/auth/login 登录并返回 JWT
GET /api/auth/me 获取当前用户
POST /api/chat/stream 新消息 SSE 流
POST /api/chat/{thread_id}/resume 恢复 HITL 中断
GET /api/chat/{thread_id} 当前 checkpoint 消息
GET /api/chat/{thread_id}/history Graph 状态历史
GET /api/history 当前用户会话列表
GET /api/history/{thread_id}/messages 前端展示消息
DELETE /api/history/{thread_id} 删除会话及消息
PATCH /api/history/{thread_id} 标题更新预留接口
GET /api/tasks 当前用户异步任务状态与结果
GET /health Web API 健康检查

注册

curl -X POST http://127.0.0.1:8090/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username":"demo_user","password":"DemoPass2026","display_name":"演示用户"}'

响应:

{
  "access_token": "<JWT>",
  "token_type": "bearer",
  "expires_in": 43200,
  "user": {
    "user_id": "<MongoDB ObjectId>",
    "username": "demo_user",
    "display_name": "演示用户"
  }
}

SSE 对话

curl -N -X POST http://127.0.0.1:8090/api/chat/stream \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"message":"查询当前库存预警并给出采购建议","thread_id":null}'

典型事件:

data: {"type":"token","content":"我会启动后台分析任务。","source":"main"}

data: {"type":"tool_start","tool_call_id":"...","tool_name":"start_async_task","source":"main"}

data: {"type":"done","thread_id":"...","content":"..."}

查询后台任务

curl http://127.0.0.1:8090/api/tasks?limit=50 \
  -H "Authorization: Bearer <JWT>"

恢复订单补充中断

curl -N -X POST http://127.0.0.1:8090/api/chat/<THREAD_ID>/resume \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"resume":{"supplement":"物料 ID=1,数量=10,单价=12.5"}}'

批准订单操作

curl -N -X POST http://127.0.0.1:8090/api/chat/<THREAD_ID>/resume \
  -H "Authorization: Bearer <JWT>" \
  -H "Content-Type: application/json" \
  -d '{"resume":{"decisions":[{"type":"approve"}]}}'

项目目录

procurepilot/
├── docker/
│   ├── docker-compose.yml          # MongoDB、OpenSandbox、沙箱镜像编排
│   ├── opensandbox.toml            # OpenSandbox Docker runtime/security 配置
│   └── sandbox-image/              # 项目专用预构建沙箱镜像
├── docs/                           # 联调、压力与多用户 API 文档
├── frontend/
│   ├── src/api/                    # auth/chat/history/tasks API 客户端
│   ├── src/components/             # Vue UI 组件
│   └── src/App.vue                 # 前端总状态协调
├── reports/                        # 自动化基准 JSON/Markdown 报告
├── scripts/
│   ├── run_benchmark.py            # 健康、并发、可选 SSE 基准
│   ├── generate_report.py          # JSON 转 Markdown 报告
│   ├── run_agent_smoke.py          # Agent 交互式冒烟诊断
│   └── run_mcp_smoke.py            # MCP/Agent 客户端连接诊断
├── src/
│   ├── agent/
│   │   ├── backends/               # OpenSandbox adapter、proxy、生命周期
│   │   ├── core/                   # 配置、环境变量、Schema 与异步签名
│   │   ├── graphs/                 # 主 Graph 与异步分析 Graph Factory
│   │   ├── memory/                 # 主提示词与 AGENTS.md
│   │   ├── middlewares/            # 上下文、记忆、Skills、沙箱、异步等
│   │   ├── stores/                 # MongoDB Store 与异步任务元数据
│   │   ├── subagents/configs/      # YAML 子 Agent
│   │   └── tools/                  # MCP 客户端、图表路由、HITL、下载等
│   ├── api_view/
│   │   ├── api/                    # FastAPI 路由
│   │   ├── agent_loader.py         # 请求级 Graph 与会话管理
│   │   └── auth_service.py         # scrypt + JWT
│   ├── mcp_server/                 # Java ERP -> FastMCP 适配层
│   └── skills/                     # 预置 Skills 和脚本
├── tests/                           # 单元、隔离、API、SSE、MCP 与 Agent 集成测试
├── langgraph.json                  # Agent Protocol graph 配置
├── motorparts_db.sql               # 外部 Java ERP 的 MySQL 业务数据
├── pyproject.toml                  # Python 包与依赖声明
├── requirements.txt                # uv 导出的固定依赖
├── start_web.py                    # 三服务一键启动器
└── README.md

测试与性能报告

默认离线单元测试

不启动外部服务也可以执行:

pytest -q

默认配置会排除 integrationlive 测试,只运行 sandbox claim、Skills 隔离、MongoDB namespace 和沙箱环境检查等离线回归测试。

只读集成测试

先确保 MongoDB、OpenSandbox、Java ERP、MCP 和 Web API 已启动。

pytest tests -m integration

该命令覆盖认证、未授权拒绝和 MCP 只读查询。认证测试会创建随机 qa_auto_* 用户;在共享环境运行后应按测试账号前缀清理。

Agent/沙箱测试

pytest tests -m "not integration and not live"

其中包含:

  • 异步 sandbox claim 防重放;
  • 用户 Skills namespace 隔离;
  • MongoDB Store namespace 索引;
  • 沙箱预构建环境;
  • MCP 工具;
  • 用户/任务沙箱绑定。

真实模型 SSE 测试

默认跳过,避免无意消耗模型额度:

Windows PowerShell:

$env:RUN_LIVE_LLM_TESTS = "1"
pytest tests/test_sse_stream.py -m live -s

Linux/macOS:

RUN_LIVE_LLM_TESTS=1 pytest tests/test_sse_stream.py -m live -s

ERP 写操作测试

写测试会真实创建/修改 Java ERP 数据,必须显式开启:

$env:RUN_ERP_WRITE_TESTS = "1"
pytest tests/test_all_tools.py -m integration -s

建议只在可回滚测试库运行。

前端构建

cd frontend
npm run build

并发基准

只读 HTTP 基准:

python scripts/run_benchmark.py --concurrency 5 --samples 10

包含真实模型/SSE:

python scripts/run_benchmark.py --concurrency 2 --samples 5 --include-llm

生成 Markdown:

python scripts/generate_report.py reports/benchmark-YYYYMMDD-HHMMSS.json

仓库现有验收报告见:

截至 2026-08-21 的一次真实只读基准:10/10 成功,平均约 0.107 秒,P95 约 0.155 秒。该数字只代表当次本地环境,不应作为生产 SLA。

如何扩展项目

新增一个 Java ERP MCP 工具

  1. src/mcp_server/tools/ 选择或新增业务分组文件;
  2. @mcp.tool(name="group_action") 定义工具;
  3. ctx.request_context.lifespan_context 获取共享 http_client
  4. server_main.py 注册分组;
  5. 如果属于现有前缀,mcp_client.py 会自动分组;否则增加新的前缀规则;
  6. 在对应子 Agent YAML 的 tools 中添加名称模式;
  7. 增加只读测试,写工具同时接入 HITL。

推荐命名规则:

supplier_*  供应商
part_*      零部件
inventory_* 库存
order_*     订单

新增同步子 Agent

  1. src/agent/subagents/configs/ 新建 YAML;
  2. 填写职责、工具模式、系统提示词和 Skills scope;
  3. 如需专属 Middleware,在 middlewares/factories.py 创建工厂;
  4. graphs/main_agent.pyextra_middleware 中绑定;
  5. 确保主 Agent 提示词中有明确路由规则;
  6. 运行工具解析和真实对话测试。

新增长任务异步 Agent

  1. 在 YAML 中定义该 Agent 的工具与提示词;
  2. 新建 Agent Protocol graph factory;
  3. langgraph.json 注册 graph ID;
  4. 为任务建立可信用户资源 claim;
  5. 创建 task 专属工作目录;
  6. 在主 Agent 中配置 AsyncSubAgent spec 和启动规则;
  7. 把任务元数据写入用户级存储并提供前端查询 API。

不能只把同步 Agent 放到后台线程:可靠的异步任务还需要独立 thread/run、状态持久化、取消/更新、资源绑定和结果恢复。

新增预置 Skill

src/skills/{scope}/{skill-name}/SKILL.md

SKILL.md 至少包含:

---
name: your-skill
description: 何时使用、解决什么问题
---

可以附带 Python/Shell 脚本和 data/ 文件。新沙箱会自动播种;已有沙箱在下一轮由 SkillsSyncMiddleware 增量同步。

修改模型

src/agent/core/config.py 调整 MAIN_MODELSUMMARY_MODEL。主模型需要可靠的 tool calling;摘要模型应使用低温度、低成本配置。修改上下文长度时,同时重新评估自动摘要阈值、工具调用上限和供应商接口的 max_tokens

替换存储

  • Checkpoint:替换 MongoDBSaver
  • 长期 KV:实现 LangGraph BaseStore
  • 语义记忆:为 search(query=...) 接入向量检索;
  • 异步任务:保持 task_id + user_id 访问边界,不要只依赖前端过滤。

生产部署建议

当前默认配置面向本地开发。上线前至少完成以下事项:

安全

  • 更换 MongoDB 默认账号密码和 JWT_SECRET_KEY
  • .env 加入 .gitignore,确认 Git 历史中没有密钥;
  • OpenSandbox 配置 api_key,移除 OPENSANDBOX_INSECURE_SERVER=YES
  • 不把 Docker socket 暴露给非受信任服务;
  • 启用并收紧 OpenSandbox network_policy,默认拒绝不必要的外联;
  • 将 FastAPI CORS 从 * 改为真实前端域名;
  • 所有服务置于 TLS 反向代理之后;
  • 审计 Skills 来源,未验证技能不得分配给生产用户;
  • 对 MCP 写工具继续保留 HITL,不允许主 Agent 直接获得写工具。

Agent Protocol

当前 start_web.py 使用 langgraph dev,适合本地开发,不是生产任务平台。生产环境应:

  • 使用带持久化队列的 Agent Protocol 部署;
  • 配置多个 worker;
  • 为任务服务增加认证;
  • 定义重试、超时、死信和取消策略;
  • 持久化 run/thread 状态;
  • 监控任务成功率、排队时长和执行时长。

OpenSandbox

  • 固定 opensandbox/server、execd 和 egress 镜像版本,避免 latest 漂移;
  • erp-openclaw-sandbox:v1 推送到私有镜像仓库;
  • 为 CPU、内存、磁盘、进程数和网络设置租户配额;
  • 对过期容器和孤儿容器建立定时清理;
  • sandbox_registry 与真实容器状态建立巡检。

Web 与 SSE

  • 反向代理关闭 SSE buffering;Nginx 需保持 proxy_buffering off
  • 增大长任务相关的读取超时,但不要无限超时;
  • Web API 多实例部署时,进程内 Agent/沙箱缓存不能作为唯一状态源;
  • 会话归属、checkpoint、任务和沙箱映射必须继续落到共享 MongoDB;
  • /api/tasks 轮询增加退避或改为服务端推送,以降低大规模用户下的查询压力。

可观测性

建议至少采集:

  • HTTP/SSE 请求量、首 token 延迟、完整响应耗时;
  • 模型调用次数、token、失败率和费用;
  • MCP 各工具调用次数、P95、错误率;
  • 异步任务排队、运行、成功、取消、超时;
  • 沙箱创建、预热命中、自动重建、熔断;
  • MongoDB checkpoint/store 延迟与容量;
  • Java ERP 写操作审批通过率和失败原因。

故障排查

FastAPI 启动时提示 MCP 工具加载失败

检查:

curl http://127.0.0.1:8080/api/parts/search?name=火花塞
python src/mcp_server/server_main.py

确认 MCP 地址仍是 http://127.0.0.1:8000/mcp。本地 ERP MCP 是核心依赖,必须先于 FastAPI 启动。

第三方图表 MCP 不可用

系统会降级到无图表模式,ERP 查询仍应正常。检查网络和 src/agent/tools/mcp_client.py 中的第三方 URL。不要把第三方临时故障误判为 Java ERP 故障。

OpenSandbox 健康检查失败

docker compose -f docker/docker-compose.yml ps
docker compose -f docker/docker-compose.yml logs -f opensandbox-server
curl http://127.0.0.1:8100/health

还应确认 Docker socket 挂载、项目镜像 erp-openclaw-sandbox:v1 存在,以及 Docker Desktop 可以创建容器。

新沙箱提示 Python 环境不完整

重新构建镜像:

docker compose -f docker/docker-compose.yml build sandbox-image
docker compose -f docker/docker-compose.yml up -d

开发排障可临时设置:

SANDBOX_RUNTIME_INSTALL_FALLBACK=true

生产环境不建议依赖现场安装。

MongoDB 认证失败

默认连接:

mongodb://root:123456@127.0.0.1:27017/?authSource=admin

如果 MongoDB 命名卷已经存在,修改 Compose 中的初始化账号不会重置旧账号。请使用原凭据,或在确认不需要数据后重建卷。

前端能打开但 API 401

  • 检查 sessionStorage 中的 token 是否过期;
  • 重新登录;
  • 确认前端 /api 代理指向 8090;
  • 确认所有 Web API 实例使用同一 JWT_SECRET_KEY

SSE 一直加载或被一次性返回

  • 检查代理是否缓冲 text/event-stream
  • 保留 X-Accel-Buffering: no
  • 使用 curl -N 验证;
  • 检查模型服务和 MCP 是否仍有未完成调用;
  • 查看系统临时目录下的 deepagent_debug/stream_*.log

异步任务一直 running

依次检查:

  1. http://127.0.0.1:2024/ok
  2. Agent Protocol 日志;
  3. /api/tasks 返回的 status_error
  4. 用户 sandbox ID 是否仍有效;
  5. 分析报告是否写入 /analysis/tasks/{task_id}/
  6. 开发版 Agent Protocol 是否因单 worker 排队。

订单没有真正写入

创建/修改订单需要两步:字段齐全,然后 approve。选择 reject 或尚未恢复 HITL 时,系统按设计不会写入。检查 SSE 中是否收到 hitl_approval,以及恢复请求是否使用正确 thread ID。

前端构建出现 chunk 超过 500 kB

这是 Vite 的体积警告,不影响功能。生产优化可对 Markdown/highlight 依赖和大型组件使用动态 import,并配置 Rollup manualChunks

已知边界

  • 当前仓库没有 Java ERP 源码;完整复现需要外部服务实现约定 REST API。
  • langgraph dev 是开发运行时,异步任务的生产持久性和水平扩展需要独立部署方案。
  • MongoDBStore 没有语义向量搜索,只适合当前确定路径/namespace 的记忆和技能访问。
  • OpenSandbox 本地配置未开启 API Key,且网络策略示例仍处于注释状态。
  • 异步任务前端目前使用 5 秒轮询,不是 WebSocket/SSE 主动推送。
  • FastAPI CORS 当前允许所有来源,只适合本地开发。
  • 后端文件下载工具把文件保存到 src/download/,当前没有独立的鉴权文件下载 HTTP 路由;对公网提供下载前应增加用户/任务归属校验。
  • 会话标题 PATCH 是预留接口,当前不会真正持久化自定义标题。
  • 前端主 bundle 仍有进一步拆包空间。

开源许可与贡献

procurepilot 的自有代码采用 MIT License。Java ERP、第三方 MCP、模型服务、外部 API 和素材仍分别受其自身许可与服务条款约束,不因本仓库使用 MIT License 而改变。

  • 参与开发前请阅读 CONTRIBUTING.md
  • 安全问题请按 SECURITY.md 私密报告,不要在公开 Issue 中披露利用细节或真实凭据。
  • 本地配置请从 .env.example 复制;.env 与任何真实密钥都不应提交到 Git。

About

以Openclaw架构思想构建的基于Deep Agents、MCP、OpenSandbox、FastAPI 与 Vue 3 的多用户智能采购协作平台,支持多 Agent 编排、异步任务、SSE 流式交互、HITL 审批、Opensandbox沙箱隔离与 ERP 数据分析。

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages