1772702706

This commit is contained in:
Docker7530
2026-03-05 17:25:08 +08:00
parent 7d7193bb5d
commit 1a3976708a
67 changed files with 342 additions and 334 deletions
@@ -0,0 +1,53 @@
```
sequenceDiagram
participant User as 用户(前端)
participant Gateway as 鉴权(可合并到智能体)
participant Agent as Spring AI Agent(编排/工具执行)
participant LLM as 大语言模型
participant DomainAPI as 域名列表接口(Tool,可选,举例后期多智能体多工具协作)
participant DataAPI as 流量查询接口 query_trafficTool
autonumber
User->>Gateway: 提问:“帮我查一下 a.com 昨天的流量”
Gateway->>Gateway: 校验 Token,解析 TenantID
Gateway->>Agent: 转发请求(带 TenantID 到 header 或 context
Agent->>Agent: 拦截器读取 header -> 写入 ThreadLocalTenantID
Agent->>LLM: system prompt + 对话历史 + tools schema
alt 参数充足(有 domain + timeRange
LLM-->>Agent: tool_call: query_traffic{domain=a.com,time=yesterday}
Agent->>DataAPI: HTTP 调用或者调用 tool,取决于是否采用远程 mcp(拦截器从 ThreadLocal 注入 TenantID
DataAPI->>DataAPI: 横向鉴权(tenant 是否拥有 domain
alt 鉴权失败
DataAPI-->>Agent: 业务错误:无权访问该域名数据
Agent->>LLM: tool_result(错误信息/错误码)
LLM-->>Agent: 生成答复(解释无权限 + 建议可查询域名/联系管理员)
Agent-->>User: 返回最终结果
else 鉴权成功
DataAPI-->>Agent: 200 JSON{traffic=500GB}
Agent->>LLM: tool_resultJSON 数据)
LLM-->>Agent: 生成答复:“a.com 昨天的流量为 500GB...”
Agent-->>User: 返回最终结果
end
else 参数不全(例如只问“查 3 月流量”,缺 domain)
LLM-->>Agent: 需要 domain,先澄清/或先发找可选域名
alt 仅澄清(没有域名查找工具)
LLM-->>Agent: 追问:“要查询哪个域名的 3 月流量?”
Agent-->>User: 返回追问
Note over User,Agent: 用户补充 domain 后进入下一轮对话,再走“参数充足”分支
else 有域名查找工具(显得比较智能)
LLM-->>Agent: tool_call: list_domains{}
Agent->>DomainAPI: 调用(注入 TenantID
DomainAPI-->>Agent: JSON{domains:[a.com,b.com,...]}
Agent->>LLM: tool_resultJSON 域名列表)
LLM-->>Agent: 追问/确认:“你要查哪个域名?a.com 还是 b.com?”
Agent-->>User: 返回追问
Note over User,Agent: 用户选择后进入下一轮对话,再走“参数充足”分支
end
end
Agent->>Agent: finally 清理 ThreadLocal(线程复用问题)
```
@@ -0,0 +1,73 @@
```
sequenceDiagram
participant User as 用户(前端)
participant Gateway as 鉴权(可合并到智能体)
participant Agent as Spring AI Agent(编排/工具执行)
participant LLM as 大语言模型
participant DomainAPI as 域名列表接口(Tool,可选)
participant DataAPI as 流量数据接口(Toolquery/export
participant OSS as 对象存储
autonumber
User->>Gateway: 提问:“帮我把 a.com 1月到3月的流量数据导出来”
Gateway->>Gateway: 校验 Token,解析 TenantID
Gateway->>Agent: 转发请求(带 TenantID 到 header 或 context
Agent->>Agent: 拦截器读取 header -> 写入 ThreadLocalTenantID
Agent->>LLM: system prompt + 对话历史 + tools schemaquery_traffic/export_traffic/list_domains
alt 参数充足(domain + timeRange + intent=export
LLM-->>Agent: tool_call: export_traffic{domain=a.com,from=2025-01-01,to=2025-03-31,format=xlsx}
Agent->>DataAPI: 调用 tool(拦截器从 ThreadLocal 注入 TenantID
DataAPI->>DataAPI: 横向鉴权(tenant 是否拥有 domain
alt 鉴权失败
DataAPI-->>Agent: 业务错误:无权访问该域名数据
Agent->>LLM: tool_result(错误信息/错误码)
LLM-->>Agent: 生成答复(无权限 + 建议/下一步)
Agent-->>User: 返回最终结果
alt 同步导出(小数据)
DataAPI->>DataAPI: 查询明细/聚合数据
DataAPI->>DataAPI: EasyExcel 生成临时 .xlsx
DataAPI->>OSS: 上传文件流
OSS-->>DataAPI: 返回预签名下载链接(过期时间)
DataAPI-->>Agent: 200 JSON{url,expireAt,fileName}
Agent->>LLM: tool_resultJSONurl/expireAt...
LLM-->>Agent: 生成答复(Markdown 链接 + 过期提示)
Agent-->>User: 返回最终结果
DataAPI-->>Agent: 202 JSON{jobId,status=PROCESSING}
Agent->>LLM: tool_resultjobId + 引导用户等待/可查询进度)
LLM-->>Agent: 生成答复:“已开始导出,任务号xxx…”
Agent-->>User: 返回任务已创建
Note over User, Agent: 用户稍后追问“导出好了没?”或前端轮询(模型能力弱我感觉最好别轮训,可以在对话框提示询问)
User->>Gateway: “查询导出进度 jobId=xxx”
Gateway->>Agent: 转发(TenantID
Agent->>Agent: 写 ThreadLocalTenantID
Agent->>LLM: tools schemaget_export_status / get_export_url
LLM-->>Agent: tool_call: get_export_status{jobId=xxx}
Agent->>DataAPI: 注入 TenantID 调用
DataAPI-->>Agent: 200 JSON{status=FINISHED,url,expireAt}
Agent->>LLM: tool_resultJSON
LLM-->>Agent: 生成答复(返回下载链接)
Agent-->>User: 返回最终结果
end
end
LLM-->>Agent: 需要补齐参数(domain/timeRange/导出格式)
alt 仅澄清(无域名工具)
LLM-->>Agent: 追问:“要导出哪个域名?时间范围是?”
Agent-->>User: 返回追问
else 有域名查找工具
LLM-->>Agent: tool_call: list_domains{}
Agent->>DomainAPI: 调用(注入 TenantID
DomainAPI-->>Agent: JSON{domains:[a.com,b.com,...]}
Agent->>LLM: tool_result(域名列表)
LLM-->>Agent: 追问/确认(domain + timeRange
Agent-->>User: 返回追问
end
end
Agent->>Agent: finally 清理 ThreadLocal(线程复用问题)
```
@@ -0,0 +1,60 @@
```
flowchart TB
subgraph Layer1["1. 用户与接入层 (Access Layer)"]
UI["Web 中台控制台\n(对话界面 / 插件管理)"]
ChatBot["企微/办公 IM 机器人"]
Card["安全审批卡片\n(Human-in-the-loop)"]
end
subgraph Tools["内置核心能力"]
RAG["RAG 问答引擎"]
CodeInterpreter["数据分析引擎\n(分析CDN优质客户)"]
end
subgraph Layer2["2. 智能体编排中台 (基于 Spring Boot & Spring AI)"]
Router["意图路由引擎 (Semantic Router)"]
Planner["Agent 调度与规划 (ReAct)"]
Tools
end
subgraph Layer3["3. 动态插件生态引擎 (Plugin Market) ⭐️核心创新"]
RegistryDB[("插件注册数据库\n(存储接口URL、参数定义)")]
SchemaConverter["动态 Schema 转换器\n(DB配置 -> LLM Function Calling)"]
Interceptor["高危操作拦截器\n(识别写操作、触发审批)"]
HttpExecutor["通用 HTTP 执行器\n(动态发起 REST 请求)"]
MCPClient["MCP 协议网关\n(未来对接标准 MCP Server)"]
end
subgraph Layer4["4. 基础设施与外部业务线 (Infrastructure & Business)"]
LLM["大语言模型 (LLM)"]
RAGFlow["现有 RAGFlow 知识库"]
IBS["IBS 系统\n(流量查验/工单)"]
CDN["CDN 下发系统\n(一键配置)"]
DNS["DNS 管理系统\n(动态查询)"]
end
UI --> Router
ChatBot --> Router
Router -- 知识库查询 --> RAG
Router -- 业务指令/查询 --> Planner
RAG <-- 检索 --> RAGFlow
Planner <-- "1. 交互与思考" --> LLM
RegistryDB -. "2. 实时加载接口配置" .-> SchemaConverter
SchemaConverter -- "3. 转换并注入可用工具" --> Planner
Planner -- "4. 生成调用指令" --> Interceptor
Interceptor -. "5a. 识别为 CDN 下发\n要求人工确认" .-> Card
Card -. 用户点击“确认” .-> Interceptor
Interceptor -- "5b. 鉴权通过放行" --> HttpExecutor
Interceptor -- 放行至新架构 --> MCPClient
HttpExecutor -- "6. 拼装动态 HTTP 请求" --> IBS
HttpExecutor -- HTTP 请求 --> CDN & DNS
MCPClient -. 未来演进: MCP通信协议 .-> DNS
Card:::highlight
IBS:::external
CDN:::external
DNS:::external
classDef core fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef plugin fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef external fill:#f1f8e9,stroke:#33681e,stroke-width:1px
classDef highlight fill:#ffebee,stroke:#c62828,stroke-width:2px
style Layer1 fill:#fafafa,stroke:#bdbdbd,stroke-width:1px,stroke-dasharray: 5 5
style Layer2 fill:#fafafa,stroke:#bdbdbd,stroke-width:1px,stroke-dasharray: 5 5
style Layer3 fill:#fafafa,stroke:#ffb74d,stroke-width:2px,stroke-dasharray: 5 5
style Layer4 fill:#fafafa,stroke:#bdbdbd,stroke-width:1px,stroke-dasharray: 5 5
```
@@ -0,0 +1,44 @@
```
flowchart TB
subgraph Layer1["1. 用户侧"]
UI["对话框"]
end
subgraph Layer2["2. 智能体编排层"]
Router@{ label: "意图识别与分发<br><span style=\"background-color:\">语义路由,小模型更好,选择题</span>" }
Planner["Agent 规划与调度 (ReAct)"]
LLM["大模型 (LLM)"]
end
subgraph Layer3["3. 工具检索引擎 (Tool-RAG) ⭐️"]
Embedding["Embedding 模型<br>(文本转向量)"]
VectorDB[("向量数据库<br>(Milvus / pgvector)<br>存储插件语义")]
RelationalDB[("关系型数据库<br>(MySQL)<br>存储接口物理配置")]
SchemaBuilder["Schema 组装器<br>(仅组装 Top-K 工具)"]
end
subgraph Layer4["4. 拦截与执行层"]
Interceptor["高危操作拦截器<br>(审批效果,这个需要和前端沟通方案)"]
HttpExecutor["通用 HTTP 执行器<br>(动态请求第三方组)"]
IBS["IBS / CDN / DNS 系统"]
end
UI -- "1. 输入: 查一下流量" --> Router
Router -- "2. 业务请求" --> Embedding
Embedding -- "3. 提取语义向量" --> VectorDB
VectorDB -- "4. 返回相似度 Top-3 的插件 ID" --> SchemaBuilder
RelationalDB -. "5. 根据 ID 拉取详细配置URL等" .-> SchemaBuilder
SchemaBuilder -- "6. 动态注入这 3 个工具说明" --> Planner
Planner <-- "7. 仅带 3 个工具进行交互<br>提取并完善参数" --> LLM
Planner -- "8. 决定调用某工具+参数" --> Interceptor
Interceptor -- "9. 放行/审批" --> HttpExecutor
HttpExecutor -- "10. 动态发起真实请求" --> IBS
Register["管理员注册新插件"] -. "A. 存入运行配置" .-> RelationalDB
Register -. "B. 向量化插件描述" .-> VectorDB
Router@{ shape: rect}
Embedding:::vector
VectorDB:::vector
RelationalDB:::plugin
SchemaBuilder:::vector
classDef core fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef plugin fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef vector fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef highlight fill:#ffebee,stroke:#c62828,stroke-width:2px
```
@@ -0,0 +1,60 @@
```
---
config:
layout: elk
---
flowchart TB
subgraph Layer1["1. 用户侧"]
UI["对话框"]
end
subgraph Layer2["2. 智能体编排层"]
Router["意图识别与分发<br>(语义路由,小模型判断最好,要快)"]
Planner["Agent 规划与调度 (ReAct)"]
LLM["大模型 (LLM)"]
RAGFlow["现有 RAGFlow<br>(知识库问答)"]
end
subgraph Layer3["3. 工具检索引擎 (Tool-RAG) ⭐️核心枢纽"]
Embedding["Embedding 模型<br>(文本转向量)"]
VectorDB[("向量数据库<br>存储插件语义<br>(按租户隔离)")]
RelationalDB@{ label: "关系型数据库<br>1. 接口物理配置<br style=\"--tw-scale-x:\">2. 角色权限表" }
SchemaBuilder["Schema 组装器<br>(仅组装 Top-K 工具)"]
end
subgraph Layer4["4. 拦截与执行层"]
Interceptor["高危操作拦截<br>(前端卡片审批流)"]
HttpExecutor["通用 HTTP 执行器<br>"]
MCPClient["MCP 协议网关<br>(标准化接入)"]
IBS["IBS / CDN / DNS 系统"]
MCPServer["MCP Server<br>"]
end
UI -- "1. 用户输入" --> Router
Router -- "2a. 查阅文档" --> RAGFlow
Router -- "2b. 业务请求/查数据" --> Embedding
Embedding -- "3. 提取语义向量" --> VectorDB
VectorDB -- "4. 返回相似度 Top-3 的插件 ID" --> SchemaBuilder
RelationalDB -. "5. 根据 ID 拉取详细配置URL等" .-> SchemaBuilder
SchemaBuilder -- "6. 动态注入这 3 个工具说明" --> Planner
Planner <-- "7. 带 3 个工具进行交互<br>提取并完善参数" --> LLM
Planner -- "8. 决定调用某工具+参数" --> Interceptor
Interceptor -- "9a. 放行/审批后走普通接口" --> HttpExecutor
HttpExecutor -- "10a. 动态发起 REST 请求" --> IBS
Interceptor -. "9b. 路由至原生 MCP" .-> MCPClient
MCPClient -. "10b. MCP 协议通信" .-> MCPServer
Register["admin 录入新插件"] -. "A. 存入运行配置" .-> RelationalDB
Register -. "B. 向量化插件描述" .-> VectorDB
Router@{ shape: rect}
RelationalDB@{ shape: cylinder}
Register@{ shape: rect}
RAGFlow:::highlight
Embedding:::vector
VectorDB:::vector
RelationalDB:::plugin
SchemaBuilder:::vector
MCPClient:::highlight
MCPServer:::external
classDef core fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef plugin fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef vector fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
classDef highlight fill:#fff9c4,stroke:#fbc02d,stroke-width:2px
classDef external fill:#f3e5f5,stroke:#8e24aa,stroke-width:1px,stroke-dasharray: 5 5
```
@@ -0,0 +1,102 @@
# 总体目标
1、支持 IBS 动态数据查询(工单进展、域名配置、运营数据、订购数据等);
2、智能分析 CDN 优质客户;
3、团队纵向扩展:支持融合 CDN 配置一键下发下发、DNS动态数据查询等
# 公司现状
基于 vLLM 推理引擎部署 DeepSeek-R1-Distill-Qwen-32B
> 32K tokens32768 约 2.4 万字中文)
> 使用 vLLM 框架,注意需开启工具调用相关配置。
RAGFlow v0.18.0 2025-04-23(知识库)
> 最新 v0.24.0
> 完全支持 MCP v0.20.0
> 依赖 MySQL、ES、Redis、MinIO
# 概念
bge 向量模型。
Function Calling / Tool Call / Tool Calling
[LangGraph](https://docs.langchain.com/oss/python/langgraph/overview) 个人理解为拖拽
**MCP** 服务端 + **ReAct Agent** 智能体 + **RAGFlow** 知识库
**MCP**Model Context Protocol,模型上下文协议)是一种用于将 AI 应用连接到外部系统的开源标准。
> 通俗点理解
> 列出能力:服务器告诉客户端它提供哪些 tools/resources/prompts,以及每个 tool 的参数结构
> 调用工具:客户端发起 call tool,带上符合 schema 的参数
> 返回结果:服务器返回结构化结果(文本/数据/错误),客户端再交给模型继续推理
**ReAct**: Synergizing Reasoning and Acting in Language Models
> Question: 用户问题
>
> Thought: 我需要查一下数据
> Action: 调用查询工具(search API)
> Observation: 返回查询结果
>
> Thought: 根据结果我可以推理答案
> Action: 再调用一个工具 / 或直接回答
>
> Final Answer: 最终结果
**RAGFlow**(开源的 RAG 工程化平台 / 系统)Python TypeScript
本质是 RAGRetrieval-Augmented Generation)检索增强生成
> 用户问题
>
> 去知识库检索相关内容
>
> 把检索结果喂给大模型
>
> 生成答案
**DB-GPT** 开源的“AI 原生数据应用开发框架”,用大模型来操作数据库和数据系统。
# 问题
鉴权问题:哪个平台能调用哪些工具,哪个平台的哪些用户能调用哪些工具
AI 平台颁发系统级别 Token 限制你能用的工具。
也就是比如你在我们平台注册了你的 tool,然后调用我们 AI 接口,需要 MCP server Token 和 用户 Token。
如果只用户 Token,那么是不是就需要一个 令牌置换 方案。
你有什么工具?
我想知道企业A的域名列表。
我想查询 企业A a.com 近 5 天的峰值。
# 备注
RAGFlow 版本升级至少 v0.20.0。(注意一下是否可以开启自身当作 MCP 服务的功能。)
dify 的整体使用流程流畅很多。
后期 Tool 多了,我们是否提供 MCP 注册中心。
Tool 代码的封装。
dify API 的研究。
DeepSeek-R1-Distill-Qwen-32B 工具调用。
![](../../../attachment/images-paste/image-20260304153545122.png)
发指令 --》ibs-ai --> RAGflow --> 智能决策 a 工具 b 知识库
需要 MCP server Token 和 用户 Token。
后期 Tool 多了,我们是否提供 MCP 注册中心。
DeepSeek-R1-Distill-Qwen-32B 工具调用。
+24
View File
@@ -0,0 +1,24 @@
给我增加两个我们项目的 demo 工具。为我的查询峰值智能体做测试准备
1. 根据企业 ID 获取 企业的所属域名。(入参是企业 ID,响应 10 个域名, a.com, b.com… 固定十个。)但现在企业 ID 只有 企业A ,其他 企业 ID 返回无域名。
2. 根据企业 ID 和 域名 获取域名近 5 天的峰值带宽。(峰值带宽可以随机 mock)如果用户无域名 ID 输入返回需要输入域名。
现在我有一个 ibs-mcp-server
queryDomainPeakBandwidth
根据企业 ID 和域名查询该域名近 5 天的峰值带宽数据(单位:Mbps)
queryEnterpriseDomains
根据企业 ID 查询该企业所属的域名列表
queryWeather
查询指定城市在指定日期的天气预报,返回天气状况、温度、湿度和风力信息
你能给我生成一个可以导入 ragflow 的 json agent 么?让我可以直接使用。
智能体的工作就是可以查训用户的域名,如果用户没有输入域名可以用 queryEnterpriseDomains 给用户提示。
生成到根目录。注意学习相关语法。
@@ -0,0 +1,360 @@
## 1. 对话相关模块与入口
### 1.1 HTTP 接口入口(后端)
聊天(SSE 流式):`POST /session/chat`
查询会话列表:`POST /session/page/list`
查询某会话的历史消息列表:`POST /session/message/list`
消息反馈(点赞/点踩/评论):`POST /session/message_feedback`
### 1.2 鉴权与用户上下文
`/session/**` 路径做拦截:
Filter`web/src/main/java/com/cmcc/ai/web/filter/UserAuthFilter.java:31-55`
---
## 2. 前端怎么传参(对话接口)
### 2.1 发送消息:`POST /session/chat`
```json
{
"content": "用户输入的文本",
"sessionId": 123,
"parentMessageId": 456,
"contentType": "text",
"role": "user"
}
```
#### 实际必填/有效字段(按当前后端实现)
- **必填**`content`
- **可选**`sessionId`
- `sessionId == null`:后端会创建一个新会话
- `sessionId != null`:后端按该会话进行多轮
### 2.2 前端接收方式:SSE
`core/src/main/java/com/cmcc/ai/core/vo/MessageVO.java:20-72`
```json
{
"sessionId": 1,
"messageId": 10002,
"parentMessageId": 10001,
"role": "assistant",
"contentType": "text",
"msgStatus": "sending",
"content": "本次增量 token",
"reasoningContent": "(可选)模型推理内容"
}
```
当流结束时,后端会发送一条 `msgStatus=finished` 的消息(通常 `content` 为空或为最后一段),并结束 SSE 连接。
---
## 3. 后端对话编排流程(从 Controller 到落库)
核心实现类:
- `core/src/main/java/com/cmcc/ai/core/service/impl/SessionServiceImpl.java`
### 3.1 会话创建 / parentMessageId 选择
入口:`generateChatResponses(TextMessageParam param, Long userId)`
- `SessionServiceImpl.java:446-464`
逻辑:
1. **首次对话**`param.sessionId == null`
- `parentMessageId` 被强制设为 `0L`(常量 `DEFAULT_PARENT_MSG_ID`
- 创建 `session_info` 记录(summary 取首条消息截断)
- 代码:`SessionServiceImpl.java:449-453`
2. **非首次对话**`param.sessionId != null`
- 查询该 session 下 `message_info` 的最大 id 作为 `parentMessageId`
- 代码:`SessionServiceImpl.java:454-456`
- Mapper`MessageInfoMapper.xml:143-145``select max(id)`
> 这意味着当前实现的父子关系是“链式”的:每次提问默认挂在上一条消息后面,而不是维护一棵分支对话树。
### 3.2 历史消息拼接(多轮上下文)
历史消息查询:
- `queryHistoryMsg(sessionId)``SessionServiceImpl.java:596-607`
构造规则:
1. 第一条固定是 **system prompt**
- `generateFirstMsg()``SessionServiceImpl.java:614-617`
- system prompt 来源:系统配置 `OLLAMA_CHART_SYSTEM_MSG`
- 若配置不存在则使用默认常量 `SYSTEM_MSG`
2. 从 DB 查出该 session 的所有历史 `message_content`(按 `mc.id asc`
- SQL`MessageContentMapper.xml:151-157`
- 每条记录映射为 `ChatMessage{role, content}`
3. 把“本次用户提问”也 append 到 historyMsg(作为最后一条 user 消息)
- `aiChatDialog()``SessionServiceImpl.java:632-634`
最终 historyMsg 会被完整送给大模型(多轮上下文就是这么实现的)。
### 3.3 用户消息落库
在真正请求大模型之前,用户消息会先落库:
- 插入 `message_info`(用户侧):`insertMessage(…)`
- `SessionServiceImpl.aiChatDialog():628-630`
- 插入 `message_content`(用户文本):`insertMessageContent(…)`
- `SessionServiceImpl.aiChatDialog():630-631`
对应表结构见 `web/src/main/resources/init.sql`
- `session_info`
- `message_info`
- `message_content`
---
## 4. 调用的哪个 AI 接口?请求长什么样?
### 4.1 AI 接口地址与模型配置来自哪里
后端不在配置文件里写死模型与地址,而是从 DB 的 `system_config` 表读取:
- 读取 service`SystemConfigServiceImpl.java:31-50`
- key 枚举:`common/src/main/java/com/cmcc/ai/enums/SysConfigKeyEnum.java:14-31`
对话相关的 key
- `OLLAMA_MODEL`:模型名
- `OLLAMA_CHART_URL`:多轮对话接口地址
- `OLLAMA_CHART_SYSTEM_MSG`system prompt
- `API_KEY`:调用上游 AI 服务时的 Authorization(代码里默认按 Bearer 使用,具体值建议视为敏感信息)
在示例初始化 SQL 中(`init.sql:70-75`),`OLLAMA_CHART_URL` 指向一个 **OpenAI 兼容风格** 的路径:`/v1/chat/completions`。
> 虽然命名叫 `OLLAMA_*`,但实际对接的上游接口形态更接近 OpenAI/DeepSeek 的 Chat Completions 流式接口。
### 4.2 后端发给上游 AI 的请求体
组装请求的代码:
- `aiChatDialog()``SessionServiceImpl.java:648-653`
请求体类型:`OllamaMutiplePrompt`
- `common/src/main/java/com/cmcc/ai/model/OllamaMutiplePrompt.java:20-36`
结构:
```json
{
"model": "<来自 system_config.OLLAMA_MODEL>",
"messages": [
{
"role": "system",
"content": "..."
},
{
"role": "user",
"content": "..."
},
{
"role": "assistant",
"content": "..."
},
{
"role": "user",
"content": "本次问题"
}
],
"stream": true
}
```
HTTP 请求(后端 → 上游 AI):
- 方法:POST
- URL`system_config.OLLAMA_CHART_URL`
- Header`Authorization: <system_config.API_KEY>`
- 代码:`SessionServiceImpl.handleMessageWithWebClient():156-175`
- Accept`text/event-stream`
---
## 5. AI 接口返回的什么?后端如何解析?
上游 AI 返回被按“流式”逐行消费:
- `bodyToFlux(String.class)``SessionServiceImpl.handleMessageWithWebClient():170-178`
后端假定每一行 `line`
- 要么是 `"[DONE]"`
- 要么是一个 JSON chunk,可反序列化为 `ChatCompletionChunk`
解析模型(按 DeepSeek/OpenAI streaming chunk 风格):
- `common/src/main/java/com/cmcc/ai/model/ChatCompletionChunk.java`
- 主要取值:`choices[0].delta.content`
- 推理字段:`choices[0].delta.reasoning_content`
对应解析与映射:
- `generateDeepSeekChatMessage()``SessionServiceImpl.java:272-311`
映射结果是内部统一的 `MessageVO`
- `messageVO.content = delta.content`
- `messageVO.reasoningContent = delta.reasoningContent`
- `messageVO.msgStatus`
- chunk 中包含 `finish_reason / stop_reason` 或 line 为 `[DONE]` 时,置为 `finished`
- 否则为 `sending`
> 文件里还存在 `generateChatMessage()` 用于解析 `OllamaStreamResponse``SessionServiceImpl.java:321-345`),但当前 `handleMessageWithWebClient()` 实际调用的是 `generateDeepSeekChatMessage()``SessionServiceImpl.java:184`)。
---
## 6. 后端给前端返回的什么?
### 6.1 SSE 推送的内容
后端对每个上游 chunk 都会向前端推一个 SSE event
- `ServerSentEvent.builder(JSON.toJSONString(message)).build()`
- 代码:`SessionServiceImpl.handleMessageWithWebClient():178-189`
也就是:
- SSE 的 `data` = `MessageVO` 的 JSON 字符串
前端收到后:
- 通过 `messageId` 把同一条 assistant 消息的 token 拼起来
- 通过 `msgStatus` 判断是否完成
- 通过 `sessionId` 确定归属会话
### 6.2 对话完成后的落库与状态更新
后端在发起上游请求时,会先创建一条“assistant message_infosending)”:
- `assistantMessage = insertMessage(…, MsgStatusEnum.SENDING)`
- 代码:`SessionServiceImpl.handleMessageWithWebClient():162-168`
流式接收过程中:
- `contentBuilder` 累积每个 chunk 的 `delta.content`
- `generateDeepSeekChatMessage():303-306`
流结束后(`doFinally`):
- 正常完成:
- `message_content` 插入完整文本(role=assistant
- `message_info.msg_status` 更新为 `finished`
- 并触发异步评价(见下一节)
- 代码:`SessionServiceImpl.handleMessageWithWebClient():191-195` + `handlerResponseMessage():208-218`
- 异常 / 取消:
- 仍会尽量保存已累积内容
- 并将 `message_info.msg_status` 更新为 `error/cancelled`
- 代码:`handlerResponseMessage():219-235`
### 6.3 非流式查询:历史消息列表接口返回
- `POST /session/message/list`
- 返回:`ApiResponse<List<MessageListVO>>`
- `MessageListVO``core/src/main/java/com/cmcc/ai/core/vo/MessageListVO.java`
- 内含 `contents: List<MessageContentVO>`
这用于“进入会话后加载历史对话”的场景。
---
## 7. 多轮对话是如何实现的?(关键点总结)
本项目的多轮对话实现方式可以概括为:
1. 每次用户发消息时:
- 先把用户消息写入 DB`message_info + message_content`
2. 组装“system + 历史 messages + 本次 user message”形成完整 messages 数组
- 历史消息来自 DB `message_content`,按 `mc.id asc`
3. 把完整 messages 一次性发给上游 `/v1/chat/completions`stream=true
4. 上游返回流式 chunk
- 后端逐 chunk 转成 `MessageVO`,通过 SSE 推给前端
5. 流结束后:
- 把 assistant 的完整文本一次性落库
即:**上下文存储在 DB,每次请求时“全量带上历史”**。
---
## 8. 评价(可选链路,和对话强相关)
对话完成后会触发一次“问答质量评估”的异步调用:
- 触发点:`handlerResponseMessage()` 的 `ON_COMPLETE` 分支
- `SessionServiceImpl.java:209-217`
调用实现:`EvaluationServiceImpl.evaluationResult()`
- `core/src/main/java/com/cmcc/ai/core/service/impl/EvaluationServiceImpl.java:115-160`
它会:
1. 从 `system_config` 读取:
- `EVALUATE_TASK_URL`
- `CALLBACK_URL`
2. 读取“本次问/答”的 message_content 内容
3. POST 到评估系统(请求包含 query/response/callbackUrl
4. 评估系统回调:`POST /evaluation/callback`
- Controller`web/src/main/java/com/cmcc/ai/web/rest/EvaluationCallController.java:36-46`
这条链路不影响前端展示主流程,但会写入 `evaluation_result` 表并支撑后续统计/导出。
---
## 9. 给前端的最小对接清单(建议)
### 9.1 发起对话
- `POST /session/chat`
- body
```json
{ "content": "...", "sessionId": 123 }
```
### 9.2 处理 SSE
- 每个 event 的 `data` 是 JSON 字符串(`MessageVO`
- 用 `content` 做增量拼接
- 用 `msgStatus` 判断完成
- **首次对话**从第一条 event 里取 `sessionId`,用于后续多轮
### 9.3 加载历史
- `POST /session/message/list`
- body
```json
{ "sessionId": 123 }
```
(后端只读取 `sessionId` 字段:`SessionController.java:78-83`
---
## 10. 关键代码定位索引
- 对话接口:`web/…/SessionController.java:98-105`
- 对话编排:`core/…/SessionServiceImpl.java:446-654`
- 上游流式请求与解析:`core/…/SessionServiceImpl.java:149-196` + `272-311`
- 历史消息查询 SQL`core/…/MessageContentMapper.xml:151-157`
- 系统配置 key`common/…/SysConfigKeyEnum.java:14-31`
- 初始化表结构与默认配置:`web/src/main/resources/init.sql`
@@ -0,0 +1,26 @@
请为我的公司设计一个智能体方案(公司内部 AI 能力中台 / AI 插件市场)。
目前公司的主要业务是:用户可以通过我们的平台引入域名,域名在平台产生计费计量后,可以查询相关的流量情况或带宽情况。
我们现在已经有了一个 RAGFlow 流程,但也仅限于实现基础的知识库智能问答。
我们现在的目标是:
1. 支持IBS动态数据查询(工单进展、域名配置、运营数据、订购数据等);
2. 智能分析CDN优质客户;
3. 团队纵向扩展:支持融合CDN配置一键下发下发、DNS动态数据查询
4. 实现通过对话查询流量或峰值带宽的效果。
5. 构建一个总体的、标准化的智能体框架。
6. 我们的后端代码以 Java 为主,希望这个框架是可对接的,后续能方便其他业务组(比如做 DNS 指令下发的团队)接入。
7. 实现一键 AI 智能下发。其他小组只需按照我们提供的规范提供接口,就可以接入到我们的智能体中,通过对话框完成业务操作。
请帮我设计一个完整汇报可行性方案,并给出技术选型建议:
1. 语言环境:基于 Java 语言。
2. 技术选型:是否建议使用 Spring Boot 或 Spring AI
3. 框架评估:目前比较火的 AI 框架,比如 MCPModel Context Protocol),是否适合我们的业务场景?
4. 架构设计:如何设计才能保证后期智能体的高可扩展性?比如其他组提供一个接口规范,我们就能为其提供智能服务。
比如先说一个简单的流程:我要查流量,那肯定是智能体去编排,决定调用某一个智能体的 Agent 工具,然后发起流量查询等等。这是一个简单的流程。
比如现在我整个服务已经跑起来了,此时隔壁的项目组给了我一个配置下发的接口,并告诉了我这个接口的参数以及请求地址。我能不能通过“可配置化”的形式,直接在我的页面上把它的地址、需要的参数等信息输入进去,这样我的智能体就拥有了这个能力,而不需要停服务或者进行二次开发。
@@ -0,0 +1,11 @@
# 新业务平面引入与系统集成实施
## 需求评审
## 需求备注
## 需求开发
# 用例评审
# 冒烟自测