AI相关组件整体架构
#
flowchart TB
U["前端 / 调用方"]
M["master<br/>统一入口 / 鉴权 / 反向代理"]
U --> M
subgraph PS["pyaiservice 进程(默认 / 桌面模式)"]
P["pyaiservice 主应用<br/>原生 AI API<br/>/ai/v1/*"]
C["chat-server<br/>会话编排层<br/>/chat/v1/*"]
X["mcp-server<br/>工具服务层<br/>/mcp/*"]
end
M -->|" /ai/v1/* "| P
M -->|" /chat/* "| C
M -->|" /mcp/* "| X
P --> L["LLM / OpenAI 兼容接口"]
P --> A["AIEngine"]
C -->|" GET /internal/v1/app/options "| M
C -->|" MCP transport: /mcp/ "| M
M --> X
X -->|" GET /internal/v1/app/options "| M
X -->|" /api/v1/data* /api/v1/datatype/config "| M
X -->|" /api/v1/file/content "| M
M --> S["storageservice<br/>结构化数据查询层"]
M --> G["manager<br/>文件访问层"]
一次聊天请求真实调用流程
#
sequenceDiagram
participant U as 前端/用户
participant M as master
participant C as chat-server
participant X as mcp-server
participant S as storageservice
participant G as manager
participant N as netfileserver
U->>M: POST /chat/v1/runs/{thread_id}/stream
M->>C: 转发原路径
C->>M: GET /internal/v1/app/options
M-->>C: 返回 LLM / MCP 配置
C->>M: MCP transport /mcp/
M->>X: 转发到 mcp-server
X->>M: GET /api/v1/data/node 或 /api/v1/data
M->>S: /internal/v1/nodes/param 或 /internal/v1/data
S-->>X: 返回结构化数据
X->>M: GET /api/v1/file/content
M->>G: /internal/v1/file/content
G->>N: 打开镜像内文件
N-->>G: 返回文件流
G-->>X: 返回文件内容
X-->>C: 返回工具执行结果
C-->>U: SSE 流式输出
mcp服务架构图
#
flowchart TB
A["mcp-server"]
A --> B["HTTP 路由层<br/>真正定义接口"]
A --> C["MCP 协议层<br/>把这些接口包装成 MCP tools"]
A --> D["VFS 子系统<br/>把 GoldenEyes 数据组织成“文件视图”"]
A --> E["向量检索子系统<br/>做语义搜索"]
D --> F["Tree<br/>管结构"]
D --> G["DataFetcher<br/>管数据"]
D --> H["CursorRegistry<br/>管分页"]
E --> I["Embedding"]
E --> J["Qdrant"]
E --> K["Rerank"]
启动
#
flowchart TB
A["create_app()<br/>创建 mcp-server 应用"]
A --> B["init_router(app)<br/>先把所有 HTTP 路由注册进 FastAPI"]
B --> C["init_mcp(app)<br/>再从这些已注册路由生成 MCP tools"]
C --> D["init_config()<br/>初始化 GES 配置、向量配置、缓存目录"]
D --> E["init_llm_cache()<br/>初始化 LLM 本地缓存"]
两种入口汇入
#
flowchart TB
A["入口一:普通 HTTP 请求<br/>/mcp/tools/v1/* 或 /mcp/tools/v1/vfs/*"]
A --> B["直接进入 FastAPI API 函数"]
B --> C["进入具体工具实现"]
D["入口二:MCP 请求<br/>/mcp/"]
D --> E["list_tools / call_tool"]
E --> F["根据 operation_id 找到对应 HTTP API"]
F --> B
GesClient路由规则图
#
flowchart TB
A["GESClient.get()/post()"]
A --> B{"请求路径长什么样?"}
B --> C["/internal/v1/app/options"]
C --> D["走 base_url<br/>通常是 GES/master 地址"]
B --> E["其他 /internal/v1/*"]
E --> F["走 storage_host<br/>通常是 storage 侧地址"]
B --> G["/api/v1/*"]
G --> H["走 base_url<br/>通常先到 master 再转发"]
VFS内部结构
#
flowchart TB
A["/mcp/tools/v1/vfs/*<br/>VFS 接口入口"]
A --> B["vfs_tools.py<br/>统一入口"]
B --> C["get_vfs(cid)<br/>拿案件对应的 GesFileSystem"]
B --> D["get_registry()<br/>拿全局 CursorRegistry"]
C --> E["GesFileSystem<br/>VFS 核心对象"]
D --> F["CursorRegistry<br/>分页句柄管理"]
E --> G["Tree<br/>负责路径、目录、节点结构"]
E --> H["DataFetcher<br/>负责读取叶子节点数据"]
E --> I["pickle 缓存<br/>减少重复建树"]
B --> J["LS / READ / GLOB / GREP / SEARCH / OUTLINE"]
J --> E
J --> F
Tree构建流程
#
flowchart TB
A["Tree.build()<br/>开始构建整棵案件树"]
A --> B["_initialize_tree()<br/>清空旧数据,创建根节点"]
B --> C["_fetch_evidences()<br/>读取案件下所有检材"]
C --> D["_build_tree_bfs()<br/>通过 /api/v1/data/node 做广度优先建树"]
D --> E["insert_evidence_layer()<br/>把首层节点重新挂到“检材层”下面"]
E --> F["insert_filesystem()<br/>补建“文件系统”子树"]
F --> G["insert_location()<br/>给每个检材插入位置虚拟节点"]
G --> H["remove_filesystem_redundant_layer()<br/>去掉冗余中间层"]
H --> I["_initialize_entity_mapper()<br/>初始化实体映射"]
- _initialize_tree()
清空旧树,重新创建根节点 /。
- _fetch_evidences()
先把案件下面有哪些检材拿回来。
接口是 /internal/v1/evidence/cid。
- _build_tree_bfs()
用 /api/v1/data/node 去做广度优先遍历,把应用节点、业务节点先搭起来。
- insert_evidence_layer()
这一步会把树重新整理成“按检材分组”的样子。
也就是让每个检材变成根下面的一层。
- insert_filesystem()
找到文件系统起始节点,再继续用 /api/v1/data?datatype=file 去补文件系统目录树。
- insert_location()
为每个检材插入一组位置虚拟节点,例如:
- 位置信息
- 位置点信息.csv
- 位置点聚类.csv
- 轨迹数据.csv
- remove_filesystem_redundant_layer()
去掉文件系统下多余的重复层级,让路径更干净。
- _initialize_entity_mapper()
给后续向量检索和实体处理准备映射器。
读取vfs文件流程
#
flowchart TB
A["POST /mcp/tools/v1/vfs/read"]
A --> B["vfs_read API"]
B --> C["get_vfs(cid)"]
C --> D["GesFileSystem"]
D --> E["_ensure_tree_loaded()<br/>先保证 Tree 已加载"]
E --> F["resolve_path(path)<br/>把路径解析成叶子节点"]
F --> G["GesFileSystem.read_chunk()"]
G --> H["_ensure_data_fetcher()<br/>拿到 DataFetcher"]
H --> I["fetch_leaf_data() / fetch_location_leaf_data()"]
I --> J["把结构化记录转成可见字段"]
J --> K["转成 CSV 文本"]
K --> L["READ._read_csv()<br/>按列、过滤、排序处理"]
L --> M["注册 StaticListCursor"]
M --> N["返回第一页 + handle_id"]
vector/search流程
#
flowchart TB
A["POST /mcp/tools/v1/vector/search"]
A --> B["vector_search API"]
B --> C["_parse_timerange()<br/>先解析时间条件"]
C --> D["search_documents()"]
D --> E["update_from_ges(cid)<br/>按案件更新向量搜索配置"]
E --> F["创建 SearchClient"]
F --> G["按 cid 重建 Qdrant collection 名"]
G --> H["client.search(request)"]
H --> I["加载 Tree pickle"]
I --> J["用 entity_mapper 提取实体条件"]
J --> K["EmbeddingClient<br/>把 query 变成向量"]
K --> L["QdrantStorage<br/>做向量召回"]
L --> M["RerankerClient<br/>做重排"]
M --> N["按阈值过滤结果"]
N --> O["返回最终搜索结果"]
主协作时序图
#
sequenceDiagram
participant U as 前端/用户
participant M as master
participant API as chat-server API
participant RM as RunManager
participant G as ReAct Graph
participant TM as ToolManager
participant MCPC as MultiServerMCPClient
participant MS as mcp-server
U->>M: POST /chat/v1/runs/{thread_id}/stream
M->>API: 转发到 chat-server
API->>RM: create_run() + start_run()
RM->>RM: 创建 RunInstance
RM->>G: graph.astream(...)
Note over G,TM: call_model 节点准备可用工具
G->>TM: get_all_tools(...)
TM->>TM: 组装本地工具 + MCP 工具
TM->>MCPC: get_tools()
MCPC->>M: MCP 请求 /mcp/ (list_tools)
M->>MS: 转发 /mcp/
MS-->>MCPC: 返回 MCP tools 列表
MCPC-->>TM: 返回 tools
TM-->>G: 返回 all_tools
G->>G: model.bind_tools(tools)
G->>G: LLM 决定是直接回答还是发起 tool_calls
alt 需要调用工具
G->>TM: 在 call_tools 节点执行 tool.ainvoke(args)
TM->>MCPC: call_tool(...)
MCPC->>M: MCP 请求 /mcp/ (call_tool)
M->>MS: 转发 /mcp/
MS-->>MCPC: 返回工具执行结果
MCPC-->>TM: 返回 tool result
TM-->>G: 生成 ToolMessage
G-->>RM: 继续下一轮 agent -> tools -> agent
else 不需要工具
G-->>RM: 直接得到 AIMessage
end
RM->>RM: SSEEventAdapter 处理 messages/custom 流
RM-->>API: 产出 SSE 事件
API-->>M: text/event-stream
M-->>U: 持续推送前端
flowchart TD
A[用户提问]
--> B[chat-server<br/>ReAct Agent]
B --> C{本轮是否启用向量检索<br/>use_vector}
C -- 否 --> D[VFS 工具]
D --> D1[ls / read / grep / glob / fetch_next]
D1 --> D2[mcp-server 虚拟文件系统]
D2 --> D3[加载或构建 tree.pkl]
D3 --> D4[按路径读取案件数据]
D4 --> Z[把内容交给大模型回答]
C -- 是 --> E[vector_search 工具]
E --> E1[mcp-server /mcp/tools/v1/vector/search]
E1 --> E2[加载 tree.pkl]
E2 --> E3[提取实体 / 校验 Embedding 模型]
E3 --> E4[问题转 Embedding]
E4 --> E5[查询 Qdrant]
E5 --> E6[reranker 重排序]
E6 --> Z
B --> F[ges_knowledge_qa 工具]
F --> F1[GraphRAG]
F1 --> F2[Qdrant + Neo4j]
F2 --> Z