1772530674
This commit is contained in:
@@ -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_traffic(Tool)
|
||||
autonumber
|
||||
|
||||
User->>Gateway: 提问:“帮我查一下 a.com 昨天的流量”
|
||||
Gateway->>Gateway: 校验 Token,解析 TenantID
|
||||
Gateway->>Agent: 转发请求(带 TenantID 到 header 或 context)
|
||||
Agent->>Agent: 拦截器读取 header -> 写入 ThreadLocal(TenantID)
|
||||
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_result(JSON 数据)
|
||||
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_result(JSON 域名列表)
|
||||
|
||||
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 流量数据接口(Tool:query/export)
|
||||
participant OSS as 对象存储
|
||||
autonumber
|
||||
|
||||
User->>Gateway: 提问:“帮我把 a.com 1月到3月的流量数据导出来”
|
||||
Gateway->>Gateway: 校验 Token,解析 TenantID
|
||||
Gateway->>Agent: 转发请求(带 TenantID 到 header 或 context)
|
||||
Agent->>Agent: 拦截器读取 header -> 写入 ThreadLocal(TenantID)
|
||||
Agent->>LLM: system prompt + 对话历史 + tools schema(query_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_result(JSON:url/expireAt...)
|
||||
LLM-->>Agent: 生成答复(Markdown 链接 + 过期提示)
|
||||
Agent-->>User: 返回最终结果
|
||||
|
||||
DataAPI-->>Agent: 202 JSON{jobId,status=PROCESSING}
|
||||
Agent->>LLM: tool_result(jobId + 引导用户等待/可查询进度)
|
||||
LLM-->>Agent: 生成答复:“已开始导出,任务号xxx…”
|
||||
Agent-->>User: 返回任务已创建
|
||||
|
||||
Note over User, Agent: 用户稍后追问“导出好了没?”或前端轮询(模型能力弱我感觉最好别轮训,可以在对话框提示询问)
|
||||
User->>Gateway: “查询导出进度 jobId=xxx”
|
||||
Gateway->>Agent: 转发(TenantID)
|
||||
Agent->>Agent: 写 ThreadLocal(TenantID)
|
||||
Agent->>LLM: tools schema(get_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_result(JSON)
|
||||
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
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user