1772530674

This commit is contained in:
Docker7530
2026-03-03 17:37:57 +08:00
parent 546b32c344
commit a0d7c1ba97
18 changed files with 725 additions and 355 deletions
+53
View File
@@ -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(线程复用问题)
```
+73
View File
@@ -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(线程复用问题)
```
+60
View File
@@ -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
```
+44
View File
@@ -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
```
@@ -1,21 +1,9 @@
module.exports = async (params) => {
const { app } = params;
// 获取当前仓库路径
const vaultPath = app.vault.adapter.basePath;
// 构建 Windows Terminal 命令
const command = `wt -d "${vaultPath}"`;
// 使用 Node.js child_process 执行命令
const { exec } = require('child_process');
exec(command, (error, stdout, stderr) => {
if (error) {
console.error('执行失败:', error);
new Notice('无法打开终端');
return;
}
console.log('终端已打开');
});
module.exports = async function openTerminal(params) {
const { app } = params;
const vaultPath = app.vault.adapter.basePath;
const command = `wt -d "${vaultPath}"`;
const { exec } = require("node:child_process");
exec(command, (error) => {
if (error) console.error("执行失败:", error);
});
};
@@ -1,77 +0,0 @@
module.exports = async (params) => {
const { app, obsidian, quickAddApi } = params;
// 获取当前选中的文件或让用户选择图片文件
const activeFile = app.workspace.getActiveFile();
if (
!activeFile ||
!activeFile.extension.match(/(png|jpg|jpeg|gif|webp|svg)/i)
) {
new obsidian.Notice("请先选择一个图片文件");
return;
}
// 生成UUID
const uuid = crypto.randomUUID();
const newFileName = `${uuid}.${activeFile.extension}`;
const newPath = activeFile.path.replace(activeFile.name, newFileName);
// 获取所有引用该文件的笔记
const backlinks = app.metadataCache.getBacklinksForFile(activeFile);
console.log("引用文件的列表:");
for (const [file, links] of backlinks.data) {
console.log(`- ${file}: ${links.length} 个引用`);
for (const link of links) {
console.log(` ${link.link}`);
}
}
// 确认操作
const confirm = await quickAddApi.yesNoPrompt(
"确认重命名",
`${activeFile.name} 重命名为 ${newFileName}\n` +
`将更新 ${backlinks.data.size} 个文件中的引用`,
);
if (!confirm) return;
try {
// 重命名文件
await app.fileManager.renameFile(activeFile, newPath);
// 更新所有引用
let updatedCount = 0;
for (const [file, links] of backlinks.data) {
const fileObj = app.vault.getAbstractFileByPath(file);
if (fileObj instanceof obsidian.TFile) {
let content = await app.vault.read(fileObj);
// 更新所有链接引用
for (const link of links) {
const oldLink = `[[${activeFile.path}]]`;
const newLink = `[[${newPath}]]`;
const oldEmbed = `![[${activeFile.path}]]`;
const newEmbed = `![[${newPath}]]`;
content = content.replace(
new RegExp(oldLink.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
newLink,
);
content = content.replace(
new RegExp(oldEmbed.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
newEmbed,
);
}
await app.vault.modify(fileObj, content);
updatedCount++;
}
}
new obsidian.Notice(`成功重命名并更新了 ${updatedCount} 个文件`);
} catch (error) {
console.error("重命名失败:", error);
new obsidian.Notice("重命名失败: " + error.message);
}
};