Compare commits

..

15 Commits

118 changed files with 6919 additions and 271 deletions

View File

@@ -11,8 +11,8 @@
- 文件上传与附件管理
- 前后端统一的管理控制台
当前阶段已经完成平台骨架、公共接口规范、知识库/知识文档管理、文档上传、文档解析、解析快照手动切片入口。
后续重点从"文档可切片"推进到"向量可检索"、"模型可路由"和"Agent 运行"。
当前阶段已经完成平台骨架、公共接口规范、知识库/知识文档管理、文档上传、文档解析、解析快照手动切片入口、模型平台基础配置与 Agent 定义管理/调试入口
后续重点从"文档可切片"推进到"向量可检索"、"模型可路由"和"Agent 运行时可编排"。
## 2. 总体设计思路
@@ -74,18 +74,24 @@
### 3.3 Agent 运行模块
后续平台重点能力,建议逐步补齐
当前已落地最小可用能力
- Agent 定义
- Prompt 模板
- 工具注册与调用
- 会话上下文与记忆
- 执行日志与任务状态
- `agent_definition`Agent 定义管理CRUD、编码唯一校验、知识库绑定校验
- Agent 管理接口:`/api/agents/list``/api/agents/query``/api/agents/detail``/api/agents/save``/api/agents/delete`
- Agent 调试接口:`POST /api/agents/{agentId}/chat`,支持普通对话与 RAG 对话两种模式
- Agent 调试链路:用户问题向量化 -> `rag_chunk_embedding` 相似度召回 -> 组装上下文 -> Chat 模型回答 -> 返回引用切片
- 统一模型调用日志:通过 `ChatModelGateway``model_call_log` 记录请求 ID、模型、耗时与 token 信息
后续平台重点能力:
- Prompt 模板管理
- 会话上下文持久化与记忆
- 工具注册与调用协议
- 执行任务状态与日志
- 多步骤编排
建议未来增加的核心对象:
建议后续补齐的核心对象:
- `agent_definition`
- `agent_session`
- `agent_message`
- `agent_task`
@@ -104,6 +110,8 @@
- 知识库管理页(完整 CRUD + 概览卡片 + 双栏详情 + 批量上传入口)
- 知识文档页(条件查询 + 批量上传 + 解析重试 + 批量切片 + 编辑/启停用/删除)
- 切片任务页(解析成功/失败文档概览与切片入口)
- Agent 管理页Agent 定义管理与知识库绑定)
- Agent 调试页(普通对话 / RAG 对话切换、引用切片回显)
前端技术要点:
@@ -125,7 +133,7 @@
- 附件管理页面前端联调
- RAG 检索配置、向量索引任务和最近任务页面联调
- 模型服务商、模型配置、路由规则和调用日志管理
- Agent 调试
- Agent 会话历史与运行日志
- 执行日志查看
## 4. 当前接口设计原则
@@ -219,7 +227,7 @@
5. ~~接入切片生成与切片持久化~~(已完成定长/分隔符切片与手动切片入口)
6. 建设模型服务商配置与模型路由层
7. 接入 Embedding / Chat 模型并完成向量写入
8. 建立 Agent 运行时骨架
8. 完善 Agent 运行时骨架(会话、工具、任务)
9. ~~补前端控制台基础骨架~~(已完成,部分高级页面待联调)
剩余重点:
@@ -227,17 +235,17 @@
- 完成模型服务商配置、模型配置、路由规则和调用日志基础能力
- 接入 Embedding生成并保存 `rag_chunk_embedding`
- 补齐索引任务、重试、重建索引和最近任务接口
- 接入 OpenAI-compatible / Spring AI 适配层并实现最小模型调用链路
- 扩展 Agent 会话、工具调用与任务编排能力
## 7. 下一步建议
结合当前代码状态,接下来建议重点做:
- 实现模型服务商和模型配置表:支持 Ollama、硅基流动、百炼等 OpenAI-compatible 来源
- 实现 Embedding 网关:对已落库切片调用 Embedding 模型并写入 `rag_chunk_embedding`
- 完成 RAG 全量向量化链路,确保知识库可稳定召回
- 为 Agent 调试链路补齐会话持久化与多轮上下文管理
- 建立 Agent 工具注册与调用协议,沉淀最小工具集
-`indexStatus` 从手工字段推进为真实状态流转
- 补齐重建索引、失败重试、最近任务接口和前端展示
- 接入模型路由,实现本地小模型与云端大模型的成本优先调用链路
## 8. 文档用途说明

View File

@@ -3,8 +3,8 @@
Common Agent 是一个规划中的通用 Agent 平台,技术路线基于 Java、Spring Boot 和 Spring AI。
项目目标是建设一套完整的前后端系统,支持 Agent 编排、工具调用、会话管理、RAG 知识库和平台管理能力。
当前项目已经完成基础工程、公共模块、RAG 元数据管理、文档上传、文档解析入口、解析快照、手动切片入口、前端知识库与知识文档管理页面。
Agent 运行时、RAG 向量化检索问答、模型服务商配置与更多平台管理能力会在后续阶段逐步实现
当前项目已经完成基础工程、公共模块、RAG 元数据管理、文档上传、文档解析入口、解析快照、手动切片入口、模型服务商配置基础能力、Agent 定义管理与调试页面。
会话持久化、工具调用编排、RAG 全量向量化检索问答能力会在后续阶段逐步完善
## 项目愿景
@@ -13,7 +13,7 @@ Common Agent 希望成为一个可复用的企业级 AI 应用基础平台:
- Agent 运行时:支持对话、工具调用、记忆、任务执行和流程编排。
- RAG 知识库:支持文档导入、解析、切片、向量化、检索和基于上下文的回答生成。
- 模型抽象:通过 Spring AI 统一接入聊天模型、Embedding 模型和重排序模型。
- 管理控制台:提供会话、Agent、知识库、文档、提示词和系统配置的 Web 管理界面。
- 管理控制台:提供 Agent、知识库、文档、模型配置和系统配置的 Web 管理界面。
- 多环境部署:支持本地开发、测试环境和生产环境的配置隔离。
## 当前技术栈
@@ -36,7 +36,7 @@ common_agent
│ ├── src/
│ │ ├── api/ # Axios 封装与各模块 API
│ │ ├── layouts/ # AdminLayout 管理后台布局
│ │ ├── pages/ # 业务页面(工作台、枚举、附件、知识库、文档
│ │ ├── pages/ # 业务页面(系统、RAG、Agent
│ │ ├── router/ # Vue Router 配置
│ │ ├── stores/ # Pinia 状态管理
│ │ ├── styles/ # 全局样式
@@ -59,14 +59,16 @@ common_agent
│ │ │ ├── handler/ # GlobalExceptionHandler
│ │ │ ├── mapper/ # SysAttachmentMapper, SysEnumMapper
│ │ │ └── service/ # 接口与实现
│ │ ── rag/ # RAG 知识库模块
│ │ ├── constant/ # RagSystemConstants
│ │ ├── controller/ # RagStoreController, RagDocumentController
│ │ ├── dto/ # 请求/响应 DTO
│ │ ├── entity/ # RagStore, RagDocument, RagChunk, RagChunkEmbedding
│ │ ├── enums/ # RagParseStatusEnum, RagIndexStatusEnum, RagChunkStrategyEnum
│ │ ├── mapper/ # RagDocumentMapper, RagStoreMapper
│ │ └── service/ # 接口与实现
│ │ ── rag/ # RAG 知识库模块
│ │ ├── constant/ # RagSystemConstants
│ │ ├── controller/ # RagStoreController, RagDocumentController
│ │ ├── dto/ # 请求/响应 DTO
│ │ ├── entity/ # RagStore, RagDocument, RagChunk, RagChunkEmbedding
│ │ ├── enums/ # RagParseStatusEnum, RagIndexStatusEnum, RagChunkStrategyEnum
│ │ ├── mapper/ # RagDocumentMapper, RagStoreMapper
│ │ └── service/ # 接口与实现
│ │ ├── modelprovider/ # 模型服务商、模型配置、路由、网关与调用日志
│ │ └── agent/ # Agent 定义管理与调试链路
│ ├── main/resources/
│ │ ├── application.yaml # 环境选择
│ │ ├── application-dev.yaml # 开发环境配置
@@ -74,8 +76,12 @@ common_agent
│ └── test/java/ # 单元测试(结构稳定性测试 + 前端 API 测试)
├── docs/
│ ├── ARCHITECTURE.md # 架构说明
── ROADMAP.md # 开发路线图
── ROADMAP.md # 开发路线图
│ ├── MODEL_PROVIDER_REQUIREMENTS.md # 模型服务商配置与路由需求
│ ├── MODEL_PROVIDER_DESIGN.md # 模型服务商配置与路由设计
│ └── MODEL_PROVIDER_SCHEMA.sql # 模型平台与Agent核心表结构
├── AGENT.md # 平台设计草案
├── agent-page-apis.md # Agent页面后端接口清单
├── pom.xml
└── README.md
```
@@ -152,6 +158,8 @@ npm run build
| 知识库 | 完整 CRUD + 双栏详情 |
| 知识文档 | 条件查询 + 批量上传 + 解析重试 + 批量切片 + 编辑/启停用/删除 |
| 切片任务 | 解析成功/失败文档概览 + 切片入口 |
| Agent管理 | Agent 定义 CRUD + 知识库绑定 |
| Agent调试 | 普通对话 / RAG 对话切换 + 引用切片回显 |
当前 UI 规范:
@@ -171,16 +179,24 @@ npm run build
## RAG 当前能力边界
当前 RAG 已经从元数据管理推进到"上传 + 解析 + 手动切片"阶段:
当前 RAG 已经从元数据管理推进到"上传 + 解析 + 手动切片 + Agent 调试召回"阶段:
- 知识库:支持列表、条件查询、详情、总览、单库文档概览、新增、编辑、删除。
- 知识文档:支持列表、条件查询、详情、新增/编辑、删除、批量上传。
- 文档解析:基于 Apache Tika 支持 TXT/Markdown/LOG、PDF、Word、Excel 文本抽取,解析时更新 `parseStatus` 并保存解析快照。
- 文档切片:支持按解析快照进行手动异步切片,已落地定长切片和分隔符切片,写入 `rag_chunk`
- 向量表:`rag_chunk_embedding` 实体、Mapper、Service 已有结构,向量写入、检索召回和重排序仍待接入
- 模型配置:已补充模型服务商配置路由需求/设计文档,后续用于统一接入 Ollama、硅基流动、百炼等来源
- 向量表:`rag_chunk_embedding` 实体、Mapper、Service 已有结构,向量写入与召回 SQL 已用于 Agent 调试链路RAG 检索问答接口仍待补齐
- 模型配置:模型服务商、模型配置路由规则、调用日志基础能力已落地Embedding/Chat 网关可用于 RAG 与 Agent 调试调用
- 前端知识库页、知识文档页、RAG 工作台和切片任务页已经接入当前接口,检索配置、最近任务、重建索引仍是后续能力。
## Agent 当前能力边界
- Agent 定义:支持 `agent_definition` 的列表、查询、详情、新增/更新、删除。
- Agent 对话:支持 `POST /api/agents/{agentId}/chat``ragEnabled=true` 时走 RAG 召回,`false` 时走普通对话。
- RAG 对话流程:用户问题向量化 -> 按知识库召回 TopK 切片 -> 组装系统提示词与上下文 -> Chat 模型回答。
- 调试回显:返回答案、请求 ID 和引用切片,便于前端页面展示与排障。
- 当前限制:尚未持久化 `agent_session/agent_message`,工具调用和任务编排仍在规划中。
## 规划模块
- `agent-core`Agent 执行模型、工具注册、记忆和编排能力。
@@ -197,6 +213,7 @@ npm run build
- [模型服务商配置与路由需求](docs/MODEL_PROVIDER_REQUIREMENTS.md)
- [模型服务商配置与路由设计](docs/MODEL_PROVIDER_DESIGN.md)
- [平台设计草案](AGENT.md)
- [Agent 页面接口清单](agent-page-apis.md)
## 参考资料

159
agent-page-apis.md Normal file
View File

@@ -0,0 +1,159 @@
# Agent 页面后端接口清单
本文对应前端页面:[AgentManagePage.vue](frontend/src/pages/agent/AgentManagePage.vue) 和 [AgentDebugPage.vue](frontend/src/pages/agent/AgentDebugPage.vue)。
## 1. 页面目标
Agent 页面分为两块:
- Agent 管理:维护 `agent_definition` 基础配置(编码、名称、知识库绑定、状态、系统提示词)。
- Agent 调试:选择 Agent 发起对话,支持普通对话与 RAG 对话切换,并回显引用切片。
## 2. Agent 管理接口
### 2.1 查询全部 Agent
- `POST /api/agents/list`
返回类型:
- `RequestResult<List<AgentDefinitionResponse>>`
### 2.2 条件查询 Agent
- `POST /api/agents/query`
请求体示例:
```json
{
"agentCode": "AGENT_RAG_HELPER",
"agentName": "知识助手",
"status": "ENABLED",
"storeId": 1001
}
```
### 2.3 查询 Agent 详情
- `GET /api/agents/detail?id={id}`
### 2.4 新增或更新 Agent
- `POST /api/agents/save`
请求体示例:
```json
{
"id": 1,
"agentCode": "AGENT_RAG_HELPER",
"agentName": "知识问答助手",
"systemPrompt": "你是企业知识助手,请优先基于知识库回答。",
"storeId": 1001,
"status": "ENABLED",
"remark": "客服场景"
}
```
说明:
- `id` 为空时新增,非空时更新。
- `agentCode` 全局唯一。
- `storeId` 必须指向已存在的 `rag_store`
- `status` 默认 `ENABLED`,可选 `ENABLED` / `DISABLED`
### 2.5 删除 Agent
- `POST /api/agents/delete?id={id}`
## 3. Agent 调试接口
### 3.1 发起对话
- `POST /api/agents/{agentId}/chat`
请求体示例:
```json
{
"messages": [
{ "role": "user", "content": "请说明请假流程" }
],
"ragEnabled": true
}
```
返回示例:
```json
{
"resultcode": "0",
"message": null,
"data": {
"agentId": 1,
"agentCode": "AGENT_RAG_HELPER",
"agentName": "知识问答助手",
"storeId": 1001,
"storeName": "企业知识库",
"answer": "根据知识库,先提交 OA 审批单。",
"modelRequestId": "f4215d13d0b3493e963297f15428e2f2",
"references": [
{
"chunkId": 9001,
"documentId": 8001,
"chunkContent": "请假流程员工先在OA提交审批单...",
"score": 0.9123
}
]
}
}
```
## 4. 对话模式说明
### 4.1 `ragEnabled=true`(默认)
执行路径:
1. 从消息列表中提取最后一条 `role=user` 的问题。
2. 读取该 Agent 绑定知识库的生效 Embedding 配置。
3. 生成查询向量并在 `rag_chunk_embedding` 按知识库 TopK 召回切片。
4. 将系统提示词、召回片段和会话消息组装后调用 Chat 模型。
5. 返回回答 + 引用切片 + `modelRequestId`
### 4.2 `ragEnabled=false`
执行路径:
- 跳过向量化与召回,直接使用会话消息调用 Chat 模型,返回普通对话结果。
## 5. 调试联调前置条件
### 5.1 普通对话前置条件
- Agent 状态为 `ENABLED`
- Agent 已绑定存在的知识库。
- 已配置可用的 Chat 路由(`taskType=CHAT_SIMPLE``RAG_ANSWER`)。
### 5.2 RAG 对话前置条件
- 满足普通对话前置条件。
- 知识库存在生效 `rag_store_model_config` 且已绑定 Embedding 模型。
- 目标知识库至少有可用向量数据(`rag_chunk_embedding`)。
## 6. 常见失败提示
- `Agent已停用暂不支持对话`:需启用 Agent。
- `当前知识库未配置Embedding模型无法执行检索对话`:需先配置知识库 Embedding 模型。
- `未召回到可用知识切片,请先完成知识库切片与向量化`:需补齐切片向量化流程。
## 7. 相关代码入口
- `src/main/java/com/bruce/agent/controller/AgentDefinitionController.java`
- `src/main/java/com/bruce/agent/service/impl/AgentDefinitionServiceImpl.java`
- `src/main/java/com/bruce/agent/entity/AgentDefinition.java`
- `src/main/java/com/bruce/modelprovider/gateway/ChatModelGatewayImpl.java`
- `frontend/src/api/agent.ts`
- `frontend/src/pages/agent/AgentManagePage.vue`
- `frontend/src/pages/agent/AgentDebugPage.vue`

View File

@@ -135,8 +135,50 @@
- 知识库 CRUD、文档 CRUD、批量上传、Tika 文本解析、解析快照和状态流转已完成。
- `rag_chunk` 已支持基于解析快照的手动异步切片,当前已落地定长切片和分隔符切片。
- `rag_chunk_embedding` 的结构层已就绪,尚未实现模型调用、向量化、索引任务和检索问答
- 模型服务商配置与路由已有需求/设计文档,后续会作为 Embedding、Chat 和 Rerank 的统一接入层
- `rag_chunk_embedding` 已支持按知识库向量相似度召回 TopK用于 Agent 调试链路引用回显
- RAG 对外检索问答接口、索引任务化和重排序能力仍在后续建设中
### 3.3 Agent 模块
包路径:`com.bruce.agent`
职责:
- 维护 Agent 定义主数据CRUD + 编码唯一性 + 绑定知识库校验)。
- 提供 Agent 调试对话接口,支持普通对话与 RAG 对话模式切换。
- 在 RAG 对话模式下,完成“问题向量化 -> 切片召回 -> 上下文组装 -> Chat 模型回答”的最小链路。
- 返回引用切片和请求 ID便于前端调试与调用追踪。
关键类:
| 类 | 路径 |
|----|------|
| AgentDefinition | `agent/entity/AgentDefinition.java` |
| AgentDefinitionController | `agent/controller/AgentDefinitionController.java` |
| AgentDefinitionServiceImpl | `agent/service/impl/AgentDefinitionServiceImpl.java` |
| AgentDefinitionResponse | `agent/dto/response/AgentDefinitionResponse.java` |
| AgentChatResponse | `agent/dto/response/AgentChatResponse.java` |
| ChatModelGateway | `modelprovider/gateway/ChatModelGateway.java` |
| ChatModelGatewayImpl | `modelprovider/gateway/ChatModelGatewayImpl.java` |
| ChatRequest | `modelprovider/gateway/ChatRequest.java` |
| ChatResult | `modelprovider/gateway/ChatResult.java` |
接口列表:
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/agents/list` | 查询全部 Agent |
| POST | `/api/agents/query` | Agent 条件查询 |
| GET | `/api/agents/detail` | 获取 Agent 详情 |
| POST | `/api/agents/save` | 新增/更新 Agent |
| POST | `/api/agents/delete` | 删除 Agent |
| POST | `/api/agents/{agentId}/chat` | Agent 调试对话 |
当前边界:
- `agent_definition` 与前端 Agent 管理页已完成联调。
- 对话入口已支持 `ragEnabled` 开关,`true` 走 RAG 召回,`false` 走普通对话。
- 尚未落地会话持久化(`agent_session` / `agent_message`)和工具调用编排。
## 4. 数据模型关系
@@ -150,8 +192,9 @@
| `rag_document` | 知识库文档表 | 关联 `rag_store.id``sys_attachment.id` |
| `rag_chunk` | 知识切片表 | 关联 `rag_store.id``rag_document.id` |
| `rag_chunk_embedding` | 切片向量表 | 关联 `rag_store.id``rag_document.id``rag_chunk.id` |
| `agent_definition` | Agent 定义表 | 关联 `rag_store.id` |
`rag_document` 是 RAG 模块与附件模块的连接点,`rag_chunk``rag_chunk_embedding`下一步检索链路核心落点。
`rag_document` 是 RAG 模块与附件模块的连接点,`rag_chunk``rag_chunk_embedding` 是检索链路核心落点`agent_definition` 负责把 Agent 与知识库绑定到同一调用链路
## 5. 配置与运行
@@ -183,16 +226,16 @@
## 7. 当前不足
- RAG 尚未进入"可检索链路",当前完成上传、解析和手动切片,但未完成向量化和召回
- 模型服务商配置、模型路由和调用日志尚未落地代码
- Agent 运行时相关模型与服务尚未开始建设。
- RAG 尚未形成独立检索问答接口,当前召回能力主要用于 Agent 调试链路
- Agent 运行时尚未持久化会话,工具调用与任务编排仍未落地。
- 前端部分页面(附件管理、检索配置、最近任务)为占位或后续能力提示。
- 缺少鉴权、租户、操作日志。
## 8. 建议演进方向
1. 补 RAG 最小检索闭环:解析文本 → 生成切片 → 生成向量 → 检索召回。
2. 建设模型服务商配置与路由层,统一接入 Ollama、硅基流动、百炼等 OpenAI-compatible 来源
3. 建设 Agent 域模型Agent、Session、Message、Tool、Task
2. 把当前 Agent 调试链路升级为会话化运行:沉淀 Session、Message 和上下文裁剪策略
3. 建设 Agent 工具注册与调用协议,补齐任务状态与执行日志
4. 补齐索引任务、重试、重建索引和前端任务视图。
5. 衔接模型供应商、Spring AI 适配层、工作流编排和前端管理台。

View File

@@ -896,3 +896,4 @@ OpenAI-compatible 客户端使用 Mock Web Server 或类似方式测试:
- Ollama Embeddings: https://docs.ollama.com/capabilities/embeddings
- Spring AI OpenAI Embeddings: https://docs.spring.io/spring-ai/reference/api/embeddings/openai-embeddings.html
- SiliconFlow Embedding 模型列表: https://www.siliconflow.com/models/embedding

View File

@@ -446,3 +446,4 @@ Embedding 批量生成应支持批处理,减少网络请求次数。批量大
- Ollama Embeddings: https://docs.ollama.com/capabilities/embeddings
- Spring AI OpenAI Embeddings: https://docs.spring.io/spring-ai/reference/api/embeddings/openai-embeddings.html
- SiliconFlow Embedding 模型列表: https://www.siliconflow.com/models/embedding

View File

@@ -91,9 +91,27 @@ CREATE TABLE IF NOT EXISTS rag_store_model_config (
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_rag_store_model_config_store_active UNIQUE (store_id, active),
CONSTRAINT fk_rag_store_model_config_store_id FOREIGN KEY (store_id) REFERENCES rag_store (id),
CONSTRAINT fk_rag_store_model_config_embedding_model_id FOREIGN KEY (embedding_model_id) REFERENCES model_config (id)
);
CREATE TABLE IF NOT EXISTS agent_definition (
id BIGSERIAL PRIMARY KEY,
agent_code VARCHAR(100) NOT NULL,
agent_name VARCHAR(200) NOT NULL,
system_prompt TEXT,
store_id BIGINT NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'ENABLED',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_definition_code UNIQUE (agent_code),
CONSTRAINT fk_agent_definition_store_id FOREIGN KEY (store_id) REFERENCES rag_store (id)
);
CREATE TABLE IF NOT EXISTS model_call_log (
id BIGSERIAL PRIMARY KEY,
request_id VARCHAR(64) NOT NULL,
@@ -112,8 +130,12 @@ CREATE TABLE IF NOT EXISTS model_call_log (
request_hash VARCHAR(64),
error_code VARCHAR(100),
error_message VARCHAR(1000),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_model_call_log_request_id UNIQUE (request_id)
);
@@ -200,6 +222,20 @@ COMMENT ON COLUMN rag_store_model_config.remark IS '备注';
COMMENT ON COLUMN rag_store_model_config.create_by IS '创建者';
COMMENT ON COLUMN rag_store_model_config.update_by IS '更新者';
COMMENT ON TABLE agent_definition IS 'Agent定义表';
COMMENT ON COLUMN agent_definition.id IS 'ID';
COMMENT ON COLUMN agent_definition.agent_code IS 'Agent编码';
COMMENT ON COLUMN agent_definition.agent_name IS 'Agent名称';
COMMENT ON COLUMN agent_definition.system_prompt IS '系统提示词';
COMMENT ON COLUMN agent_definition.store_id IS '绑定知识库ID';
COMMENT ON COLUMN agent_definition.status IS '状态';
COMMENT ON COLUMN agent_definition.version IS '版本';
COMMENT ON COLUMN agent_definition.create_time IS '创建时间';
COMMENT ON COLUMN agent_definition.update_time IS '更新时间';
COMMENT ON COLUMN agent_definition.remark IS '备注';
COMMENT ON COLUMN agent_definition.create_by IS '创建者';
COMMENT ON COLUMN agent_definition.update_by IS '更新者';
COMMENT ON TABLE model_call_log IS '模型调用日志表';
COMMENT ON COLUMN model_call_log.id IS 'ID';
COMMENT ON COLUMN model_call_log.request_id IS '请求唯一ID';
@@ -218,5 +254,10 @@ COMMENT ON COLUMN model_call_log.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN model_call_log.request_hash IS '请求哈希';
COMMENT ON COLUMN model_call_log.error_code IS '错误码';
COMMENT ON COLUMN model_call_log.error_message IS '错误信息摘要';
COMMENT ON COLUMN model_call_log.version IS '版本';
COMMENT ON COLUMN model_call_log.create_time IS '创建时间';
COMMENT ON COLUMN model_call_log.update_time IS '更新时间';
COMMENT ON COLUMN model_call_log.remark IS '备注';
COMMENT ON COLUMN model_call_log.create_by IS '创建者';
COMMENT ON COLUMN model_call_log.update_by IS '更新者';

View File

@@ -1,6 +1,6 @@
# Common Agent 开发路线图
本文档基于 2026-05-25 当前分支代码整理,用来区分"已经完成""建议优先做""中期建设项"。
本文档基于 2026-05-27 当前分支代码整理,用来区分"已经完成""建议优先做""中期建设项"。
## 已完成
@@ -52,30 +52,34 @@
- 知识文档管理页:条件查询 + 批量上传 + 解析重试 + 批量切片入口 + 编辑/启停用/删除。
- RAG 工作台与切片任务页:展示文档解析/切片概览并提供切片入口。
- RAG 文档批量上传组件:支持锁定知识库或选择知识库上传。
- Agent 管理页:支持 Agent 定义新增、编辑、删除、状态管理和知识库绑定。
- Agent 调试页:支持普通对话 / RAG 对话切换、请求 ID 与引用切片回显。
- API 层Axios 封装 + Long 类型安全解析 + 统一错误拦截。
- 单元测试Vitest + @vue/test-utils覆盖路由、布局、页面和 API。
### 模型平台设计
### 模型平台与 Agent 最小链路
- 已新增模型服务商配置路由需求文档:`docs/MODEL_PROVIDER_REQUIREMENTS.md`
- 已新增模型服务商配置与路由设计文档:`docs/MODEL_PROVIDER_DESIGN.md`
- 已明确后续通过模型网关统一接入 Ollama、硅基流动、百炼等 OpenAI-compatible 来源
- 模型服务商、模型配置路由规则、知识库模型绑定、调用日志核心表结构已落地(`docs/MODEL_PROVIDER_SCHEMA.sql`
- `EmbeddingModelGateway``ChatModelGateway` 已落地,统一走 OpenAI-compatible 协议调用上游模型
- `AgentDefinitionController``AgentDefinitionServiceImpl` 已提供 Agent CRUD 与调试对话接口
- Agent 调试链路已接入:问题向量化 -> `rag_chunk_embedding` 召回 -> 组装上下文 -> Chat 回答 -> 返回引用切片。
### 质量保障
- 后端结构稳定性单元测试。
- 前端组件与 API 单元测试。
- Agent 结构与服务单元测试(`AgentComponentStructureTests``AgentDefinitionServiceImplTests`)。
## 短期优先级
建议优先完成下面几项,把 RAG 上传解析切片链路升级为可检索链路:
1. 模型服务商配置:新增服务商、模型、路由规则和调用日志基础表
2. Embedding 网关:优先支持 OpenAI-compatible 接口,接入硅基流动或 Ollama Embedding
3. 向量写入:对 `rag_chunk` 调用 Embedding 模型并保存 `rag_chunk_embedding`
4. 索引任务入口:把文档或知识库的 `indexStatus` 推进为真实状态流转
5. 补齐重建索引、失败重试、最近任务接口
6. 前端接入模型配置、检索配置、最近任务和重建索引动作
1. 打通文档切片后的全量向量写入,确保 `rag_chunk_embedding` 可持续更新
2. 新增独立 RAG 检索问答接口,避免仅依赖 Agent 调试入口消费召回能力
3. 索引任务入口:把文档或知识库的 `indexStatus` 推进为真实状态流转
4. 补齐重建索引、失败重试、最近任务接口
5. 落地 Agent 会话持久化(`agent_session``agent_message`)与多轮上下文管理
6. 补齐 Agent 工具注册、工具调用协议和任务执行日志
## RAG 最小闭环
@@ -84,14 +88,14 @@
1. ~~批量上传文件,自动创建 `sys_attachment` 与 `rag_document`。~~
2. ~~调用解析入口,使用 Tika 抽取文本并更新 `parseStatus`。~~
3. ~~根据切片策略生成 `rag_chunk`。~~
4. 调用 Embedding 模型生成向量并写入 `rag_chunk_embedding`
5. 提供检索接口,按 query 向量召回切片并返回引用元数据。
4. 调用 Embedding 模型生成向量并写入 `rag_chunk_embedding`(已被 Agent 调试链路消费)
5. 提供独立检索接口,按 query 向量召回切片并返回引用元数据。
## Agent 核心能力
RAG 数据链路稳定后,再进入 Agent 主线:
Agent 主线能力按以下顺序继续推进
1. Agent 定义管理。
1. ~~Agent 定义管理。~~
2. 会话与消息模型。
3. 工具注册与工具调用协议。
4. Prompt 模板管理。
@@ -120,6 +124,8 @@ RAG 数据链路稳定后,再进入 Agent 主线:
- RAG 文档批量上传接口POST `/api/rag/documents/batchUpload`
- RAG 文档解析接口POST `/api/rag/documents/parse`,当前同步解析、保存解析快照并返回解析元数据。
- RAG 文档切片接口POST `/api/rag/documents/chunk`,当前异步生成并替换 `rag_chunk`
- Agent 管理接口:`/api/agents/list``/api/agents/query``/api/agents/detail``/api/agents/save``/api/agents/delete`
- Agent 调试接口POST `/api/agents/{agentId}/chat`,支持 `ragEnabled` 开关。
- 大整数 ID 通过 `@JsonSerialize(ToStringSerializer.class)` 输出为字符串。
## 里程碑
@@ -137,8 +143,10 @@ RAG 数据链路稳定后,再进入 Agent 主线:
### 里程碑 3Agent 最小运行时
- 支持一个可配置 Agent、一个会话、一次模型调用、一次工具调用
- 已完成:支持一个可配置 Agent、一次模型调用与 RAG 召回调试链路
- 待完成:会话持久化、工具调用与任务编排。
### 里程碑 4平台管理化
- 补齐前端占位页面联调与后台配置能力,形成完整平台雏形。

View File

@@ -0,0 +1,432 @@
-- Common Agent Studio prototype schema draft.
-- These tables extend the current RAG, model-provider and agent_definition data model
-- without replacing existing core entities.
CREATE TABLE IF NOT EXISTS studio_project (
id BIGSERIAL PRIMARY KEY,
project_code VARCHAR(100) NOT NULL,
project_name VARCHAR(200) NOT NULL,
environment VARCHAR(50) NOT NULL DEFAULT 'DEV',
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
current_version VARCHAR(50),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_studio_project_code UNIQUE (project_code)
);
CREATE TABLE IF NOT EXISTS workflow_definition (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT,
workflow_code VARCHAR(100) NOT NULL,
workflow_name VARCHAR(200) NOT NULL,
description VARCHAR(1000),
bound_agent_id BIGINT,
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_definition_code UNIQUE (workflow_code),
CONSTRAINT fk_workflow_definition_project_id FOREIGN KEY (project_id) REFERENCES studio_project (id),
CONSTRAINT fk_workflow_definition_agent_id FOREIGN KEY (bound_agent_id) REFERENCES agent_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_version (
id BIGSERIAL PRIMARY KEY,
workflow_id BIGINT NOT NULL,
version_no INTEGER NOT NULL,
snapshot_name VARCHAR(100) NOT NULL,
graph_json JSONB NOT NULL DEFAULT '{}'::jsonb,
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
published_time TIMESTAMP,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_version_no UNIQUE (workflow_id, version_no),
CONSTRAINT fk_workflow_version_workflow_id FOREIGN KEY (workflow_id) REFERENCES workflow_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_run (
id BIGSERIAL PRIMARY KEY,
request_id VARCHAR(64) NOT NULL,
workflow_id BIGINT,
workflow_version_id BIGINT,
agent_id BIGINT,
run_source VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL,
input_json JSONB NOT NULL DEFAULT '{}'::jsonb,
output_json JSONB NOT NULL DEFAULT '{}'::jsonb,
duration_ms INTEGER,
estimated_cost NUMERIC(14, 8),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_run_request_id UNIQUE (request_id),
CONSTRAINT fk_workflow_run_workflow_id FOREIGN KEY (workflow_id) REFERENCES workflow_definition (id),
CONSTRAINT fk_workflow_run_version_id FOREIGN KEY (workflow_version_id) REFERENCES workflow_version (id),
CONSTRAINT fk_workflow_run_agent_id FOREIGN KEY (agent_id) REFERENCES agent_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_run_step (
id BIGSERIAL PRIMARY KEY,
run_id BIGINT NOT NULL,
node_id VARCHAR(100) NOT NULL,
node_type VARCHAR(50) NOT NULL,
node_name VARCHAR(200),
status VARCHAR(50) NOT NULL,
input_json JSONB NOT NULL DEFAULT '{}'::jsonb,
output_json JSONB NOT NULL DEFAULT '{}'::jsonb,
duration_ms INTEGER,
error_message VARCHAR(1000),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT fk_workflow_run_step_run_id FOREIGN KEY (run_id) REFERENCES workflow_run (id)
);
CREATE TABLE IF NOT EXISTS mcp_server (
id BIGSERIAL PRIMARY KEY,
server_code VARCHAR(100) NOT NULL,
server_name VARCHAR(200) NOT NULL,
import_type VARCHAR(50) NOT NULL,
endpoint_url VARCHAR(500),
package_name VARCHAR(200),
manifest_json JSONB NOT NULL DEFAULT '{}'::jsonb,
auth_type VARCHAR(50),
secret_ref VARCHAR(200),
health_status VARCHAR(50) NOT NULL DEFAULT 'UNKNOWN',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_mcp_server_code UNIQUE (server_code)
);
CREATE TABLE IF NOT EXISTS mcp_capability (
id BIGSERIAL PRIMARY KEY,
server_id BIGINT NOT NULL,
capability_code VARCHAR(150) NOT NULL,
capability_name VARCHAR(200) NOT NULL,
capability_type VARCHAR(50) NOT NULL,
schema_json JSONB NOT NULL DEFAULT '{}'::jsonb,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_mcp_capability_code UNIQUE (server_id, capability_code),
CONSTRAINT fk_mcp_capability_server_id FOREIGN KEY (server_id) REFERENCES mcp_server (id)
);
CREATE TABLE IF NOT EXISTS skill_definition (
id BIGSERIAL PRIMARY KEY,
skill_code VARCHAR(100) NOT NULL,
skill_name VARCHAR(200) NOT NULL,
skill_type VARCHAR(50) NOT NULL,
description VARCHAR(1000),
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_skill_definition_code UNIQUE (skill_code)
);
CREATE TABLE IF NOT EXISTS skill_version (
id BIGSERIAL PRIMARY KEY,
skill_id BIGINT NOT NULL,
version_no INTEGER NOT NULL,
prompt_text TEXT,
code_text TEXT,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
variable_schema_json JSONB NOT NULL DEFAULT '{}'::jsonb,
test_result_json JSONB NOT NULL DEFAULT '{}'::jsonb,
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
published_time TIMESTAMP,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_skill_version_no UNIQUE (skill_id, version_no),
CONSTRAINT fk_skill_version_skill_id FOREIGN KEY (skill_id) REFERENCES skill_definition (id)
);
CREATE TABLE IF NOT EXISTS agent_session (
id BIGSERIAL PRIMARY KEY,
session_code VARCHAR(100) NOT NULL,
agent_id BIGINT NOT NULL,
workflow_run_id BIGINT,
title VARCHAR(200),
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_session_code UNIQUE (session_code),
CONSTRAINT fk_agent_session_agent_id FOREIGN KEY (agent_id) REFERENCES agent_definition (id),
CONSTRAINT fk_agent_session_run_id FOREIGN KEY (workflow_run_id) REFERENCES workflow_run (id)
);
CREATE TABLE IF NOT EXISTS agent_message (
id BIGSERIAL PRIMARY KEY,
session_id BIGINT NOT NULL,
role VARCHAR(50) NOT NULL,
content TEXT NOT NULL,
citation_json JSONB NOT NULL DEFAULT '[]'::jsonb,
token_count INTEGER,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT fk_agent_message_session_id FOREIGN KEY (session_id) REFERENCES agent_session (id)
);
CREATE TABLE IF NOT EXISTS agent_capability_binding (
id BIGSERIAL PRIMARY KEY,
owner_type VARCHAR(50) NOT NULL,
owner_id BIGINT NOT NULL,
capability_type VARCHAR(50) NOT NULL,
capability_id BIGINT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_capability_binding UNIQUE (owner_type, owner_id, capability_type, capability_id)
);
CREATE INDEX IF NOT EXISTS idx_studio_project_environment ON studio_project (environment);
CREATE INDEX IF NOT EXISTS idx_studio_project_publish_status ON studio_project (publish_status);
CREATE INDEX IF NOT EXISTS idx_workflow_definition_project_id ON workflow_definition (project_id);
CREATE INDEX IF NOT EXISTS idx_workflow_definition_status ON workflow_definition (status);
CREATE INDEX IF NOT EXISTS idx_workflow_version_workflow_id ON workflow_version (workflow_id);
CREATE INDEX IF NOT EXISTS idx_workflow_run_workflow_id ON workflow_run (workflow_id);
CREATE INDEX IF NOT EXISTS idx_workflow_run_status ON workflow_run (status);
CREATE INDEX IF NOT EXISTS idx_workflow_run_step_run_id ON workflow_run_step (run_id);
CREATE INDEX IF NOT EXISTS idx_agent_session_agent_id ON agent_session (agent_id);
CREATE INDEX IF NOT EXISTS idx_agent_session_status ON agent_session (status);
CREATE INDEX IF NOT EXISTS idx_agent_message_session_id ON agent_message (session_id);
CREATE INDEX IF NOT EXISTS idx_agent_message_role ON agent_message (role);
CREATE INDEX IF NOT EXISTS idx_agent_capability_owner ON agent_capability_binding (owner_type, owner_id);
CREATE INDEX IF NOT EXISTS idx_agent_capability_type ON agent_capability_binding (capability_type, capability_id);
CREATE INDEX IF NOT EXISTS idx_mcp_server_import_type ON mcp_server (import_type);
CREATE INDEX IF NOT EXISTS idx_mcp_server_health_status ON mcp_server (health_status);
CREATE INDEX IF NOT EXISTS idx_mcp_capability_server_id ON mcp_capability (server_id);
CREATE INDEX IF NOT EXISTS idx_mcp_capability_type ON mcp_capability (capability_type);
CREATE INDEX IF NOT EXISTS idx_skill_definition_status ON skill_definition (status);
CREATE INDEX IF NOT EXISTS idx_skill_definition_type ON skill_definition (skill_type);
CREATE INDEX IF NOT EXISTS idx_skill_version_skill_id ON skill_version (skill_id);
CREATE INDEX IF NOT EXISTS idx_skill_version_publish_status ON skill_version (publish_status);
COMMENT ON TABLE studio_project IS 'Studio项目空间表';
COMMENT ON COLUMN studio_project.id IS 'ID';
COMMENT ON COLUMN studio_project.project_code IS '项目编码';
COMMENT ON COLUMN studio_project.project_name IS '项目名称';
COMMENT ON COLUMN studio_project.environment IS '环境';
COMMENT ON COLUMN studio_project.publish_status IS '发布状态';
COMMENT ON COLUMN studio_project.current_version IS '当前发布版本';
COMMENT ON COLUMN studio_project.version IS '版本';
COMMENT ON COLUMN studio_project.create_time IS '创建时间';
COMMENT ON COLUMN studio_project.update_time IS '更新时间';
COMMENT ON COLUMN studio_project.remark IS '备注';
COMMENT ON COLUMN studio_project.create_by IS '创建者';
COMMENT ON COLUMN studio_project.update_by IS '更新者';
COMMENT ON TABLE workflow_definition IS 'Workflow定义表';
COMMENT ON COLUMN workflow_definition.id IS 'ID';
COMMENT ON COLUMN workflow_definition.project_id IS '所属项目ID';
COMMENT ON COLUMN workflow_definition.workflow_code IS 'Workflow编码';
COMMENT ON COLUMN workflow_definition.workflow_name IS 'Workflow名称';
COMMENT ON COLUMN workflow_definition.description IS 'Workflow描述';
COMMENT ON COLUMN workflow_definition.bound_agent_id IS '绑定Agent ID';
COMMENT ON COLUMN workflow_definition.status IS '状态';
COMMENT ON COLUMN workflow_definition.version IS '版本';
COMMENT ON COLUMN workflow_definition.create_time IS '创建时间';
COMMENT ON COLUMN workflow_definition.update_time IS '更新时间';
COMMENT ON COLUMN workflow_definition.remark IS '备注';
COMMENT ON COLUMN workflow_definition.create_by IS '创建者';
COMMENT ON COLUMN workflow_definition.update_by IS '更新者';
COMMENT ON TABLE workflow_version IS 'Workflow版本快照表';
COMMENT ON COLUMN workflow_version.id IS 'ID';
COMMENT ON COLUMN workflow_version.workflow_id IS 'Workflow定义ID';
COMMENT ON COLUMN workflow_version.version_no IS '版本号';
COMMENT ON COLUMN workflow_version.snapshot_name IS '快照名称';
COMMENT ON COLUMN workflow_version.graph_json IS '编排图JSON';
COMMENT ON COLUMN workflow_version.publish_status IS '发布状态';
COMMENT ON COLUMN workflow_version.published_time IS '发布时间';
COMMENT ON COLUMN workflow_version.version IS '版本';
COMMENT ON COLUMN workflow_version.create_time IS '创建时间';
COMMENT ON COLUMN workflow_version.update_time IS '更新时间';
COMMENT ON COLUMN workflow_version.remark IS '备注';
COMMENT ON COLUMN workflow_version.create_by IS '创建者';
COMMENT ON COLUMN workflow_version.update_by IS '更新者';
COMMENT ON TABLE workflow_run IS 'Workflow运行记录表';
COMMENT ON COLUMN workflow_run.id IS 'ID';
COMMENT ON COLUMN workflow_run.request_id IS '请求唯一ID';
COMMENT ON COLUMN workflow_run.workflow_id IS 'Workflow定义ID';
COMMENT ON COLUMN workflow_run.workflow_version_id IS 'Workflow版本ID';
COMMENT ON COLUMN workflow_run.agent_id IS '执行Agent ID';
COMMENT ON COLUMN workflow_run.run_source IS '运行来源';
COMMENT ON COLUMN workflow_run.status IS '运行状态';
COMMENT ON COLUMN workflow_run.input_json IS '输入参数JSON';
COMMENT ON COLUMN workflow_run.output_json IS '输出结果JSON';
COMMENT ON COLUMN workflow_run.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN workflow_run.estimated_cost IS '预估成本';
COMMENT ON COLUMN workflow_run.version IS '版本';
COMMENT ON COLUMN workflow_run.create_time IS '创建时间';
COMMENT ON COLUMN workflow_run.update_time IS '更新时间';
COMMENT ON COLUMN workflow_run.remark IS '备注';
COMMENT ON COLUMN workflow_run.create_by IS '创建者';
COMMENT ON COLUMN workflow_run.update_by IS '更新者';
COMMENT ON TABLE workflow_run_step IS 'Workflow运行步骤表';
COMMENT ON COLUMN workflow_run_step.id IS 'ID';
COMMENT ON COLUMN workflow_run_step.run_id IS '运行记录ID';
COMMENT ON COLUMN workflow_run_step.node_id IS '节点ID';
COMMENT ON COLUMN workflow_run_step.node_type IS '节点类型';
COMMENT ON COLUMN workflow_run_step.node_name IS '节点名称';
COMMENT ON COLUMN workflow_run_step.status IS '步骤状态';
COMMENT ON COLUMN workflow_run_step.input_json IS '步骤输入JSON';
COMMENT ON COLUMN workflow_run_step.output_json IS '步骤输出JSON';
COMMENT ON COLUMN workflow_run_step.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN workflow_run_step.error_message IS '错误信息';
COMMENT ON COLUMN workflow_run_step.version IS '版本';
COMMENT ON COLUMN workflow_run_step.create_time IS '创建时间';
COMMENT ON COLUMN workflow_run_step.update_time IS '更新时间';
COMMENT ON COLUMN workflow_run_step.remark IS '备注';
COMMENT ON COLUMN workflow_run_step.create_by IS '创建者';
COMMENT ON COLUMN workflow_run_step.update_by IS '更新者';
COMMENT ON TABLE mcp_server IS 'MCP服务表';
COMMENT ON COLUMN mcp_server.id IS 'ID';
COMMENT ON COLUMN mcp_server.server_code IS '服务编码';
COMMENT ON COLUMN mcp_server.server_name IS '服务名称';
COMMENT ON COLUMN mcp_server.import_type IS '导入方式';
COMMENT ON COLUMN mcp_server.endpoint_url IS '服务端点地址';
COMMENT ON COLUMN mcp_server.package_name IS '安装包名称';
COMMENT ON COLUMN mcp_server.manifest_json IS 'Manifest声明JSON';
COMMENT ON COLUMN mcp_server.auth_type IS '鉴权类型';
COMMENT ON COLUMN mcp_server.secret_ref IS '密钥引用';
COMMENT ON COLUMN mcp_server.health_status IS '健康状态';
COMMENT ON COLUMN mcp_server.enabled IS '是否启用';
COMMENT ON COLUMN mcp_server.version IS '版本';
COMMENT ON COLUMN mcp_server.create_time IS '创建时间';
COMMENT ON COLUMN mcp_server.update_time IS '更新时间';
COMMENT ON COLUMN mcp_server.remark IS '备注';
COMMENT ON COLUMN mcp_server.create_by IS '创建者';
COMMENT ON COLUMN mcp_server.update_by IS '更新者';
COMMENT ON TABLE mcp_capability IS 'MCP能力表';
COMMENT ON COLUMN mcp_capability.id IS 'ID';
COMMENT ON COLUMN mcp_capability.server_id IS '所属服务ID';
COMMENT ON COLUMN mcp_capability.capability_code IS '能力编码';
COMMENT ON COLUMN mcp_capability.capability_name IS '能力名称';
COMMENT ON COLUMN mcp_capability.capability_type IS '能力类型';
COMMENT ON COLUMN mcp_capability.schema_json IS '能力Schema JSON';
COMMENT ON COLUMN mcp_capability.enabled IS '是否启用';
COMMENT ON COLUMN mcp_capability.version IS '版本';
COMMENT ON COLUMN mcp_capability.create_time IS '创建时间';
COMMENT ON COLUMN mcp_capability.update_time IS '更新时间';
COMMENT ON COLUMN mcp_capability.remark IS '备注';
COMMENT ON COLUMN mcp_capability.create_by IS '创建者';
COMMENT ON COLUMN mcp_capability.update_by IS '更新者';
COMMENT ON TABLE skill_definition IS 'Skill定义表';
COMMENT ON COLUMN skill_definition.id IS 'ID';
COMMENT ON COLUMN skill_definition.skill_code IS 'Skill编码';
COMMENT ON COLUMN skill_definition.skill_name IS 'Skill名称';
COMMENT ON COLUMN skill_definition.skill_type IS 'Skill类型';
COMMENT ON COLUMN skill_definition.description IS 'Skill描述';
COMMENT ON COLUMN skill_definition.status IS '状态';
COMMENT ON COLUMN skill_definition.version IS '版本';
COMMENT ON COLUMN skill_definition.create_time IS '创建时间';
COMMENT ON COLUMN skill_definition.update_time IS '更新时间';
COMMENT ON COLUMN skill_definition.remark IS '备注';
COMMENT ON COLUMN skill_definition.create_by IS '创建者';
COMMENT ON COLUMN skill_definition.update_by IS '更新者';
COMMENT ON TABLE skill_version IS 'Skill版本表';
COMMENT ON COLUMN skill_version.id IS 'ID';
COMMENT ON COLUMN skill_version.skill_id IS 'Skill定义ID';
COMMENT ON COLUMN skill_version.version_no IS '版本号';
COMMENT ON COLUMN skill_version.prompt_text IS '提示词内容';
COMMENT ON COLUMN skill_version.code_text IS '代码内容';
COMMENT ON COLUMN skill_version.config_json IS '运行配置JSON';
COMMENT ON COLUMN skill_version.variable_schema_json IS '变量Schema JSON';
COMMENT ON COLUMN skill_version.test_result_json IS '测试结果JSON';
COMMENT ON COLUMN skill_version.publish_status IS '发布状态';
COMMENT ON COLUMN skill_version.published_time IS '发布时间';
COMMENT ON COLUMN skill_version.version IS '版本';
COMMENT ON COLUMN skill_version.create_time IS '创建时间';
COMMENT ON COLUMN skill_version.update_time IS '更新时间';
COMMENT ON COLUMN skill_version.remark IS '备注';
COMMENT ON COLUMN skill_version.create_by IS '创建者';
COMMENT ON COLUMN skill_version.update_by IS '更新者';
COMMENT ON TABLE agent_session IS 'Agent会话表';
COMMENT ON COLUMN agent_session.id IS 'ID';
COMMENT ON COLUMN agent_session.session_code IS '会话编码';
COMMENT ON COLUMN agent_session.agent_id IS 'Agent ID';
COMMENT ON COLUMN agent_session.workflow_run_id IS '来源Workflow运行ID';
COMMENT ON COLUMN agent_session.title IS '会话标题';
COMMENT ON COLUMN agent_session.status IS '会话状态';
COMMENT ON COLUMN agent_session.metadata_json IS '会话元数据JSON';
COMMENT ON COLUMN agent_session.version IS '版本';
COMMENT ON COLUMN agent_session.create_time IS '创建时间';
COMMENT ON COLUMN agent_session.update_time IS '更新时间';
COMMENT ON COLUMN agent_session.remark IS '备注';
COMMENT ON COLUMN agent_session.create_by IS '创建者';
COMMENT ON COLUMN agent_session.update_by IS '更新者';
COMMENT ON TABLE agent_message IS 'Agent消息表';
COMMENT ON COLUMN agent_message.id IS 'ID';
COMMENT ON COLUMN agent_message.session_id IS '会话ID';
COMMENT ON COLUMN agent_message.role IS '消息角色';
COMMENT ON COLUMN agent_message.content IS '消息内容';
COMMENT ON COLUMN agent_message.citation_json IS '引用信息JSON';
COMMENT ON COLUMN agent_message.token_count IS 'Token数量';
COMMENT ON COLUMN agent_message.version IS '版本';
COMMENT ON COLUMN agent_message.create_time IS '创建时间';
COMMENT ON COLUMN agent_message.update_time IS '更新时间';
COMMENT ON COLUMN agent_message.remark IS '备注';
COMMENT ON COLUMN agent_message.create_by IS '创建者';
COMMENT ON COLUMN agent_message.update_by IS '更新者';
COMMENT ON TABLE agent_capability_binding IS 'Agent能力绑定表';
COMMENT ON COLUMN agent_capability_binding.id IS 'ID';
COMMENT ON COLUMN agent_capability_binding.owner_type IS '绑定主体类型';
COMMENT ON COLUMN agent_capability_binding.owner_id IS '绑定主体ID';
COMMENT ON COLUMN agent_capability_binding.capability_type IS '能力类型';
COMMENT ON COLUMN agent_capability_binding.capability_id IS '能力ID';
COMMENT ON COLUMN agent_capability_binding.enabled IS '是否启用';
COMMENT ON COLUMN agent_capability_binding.config_json IS '绑定配置JSON';
COMMENT ON COLUMN agent_capability_binding.version IS '版本';
COMMENT ON COLUMN agent_capability_binding.create_time IS '创建时间';
COMMENT ON COLUMN agent_capability_binding.update_time IS '更新时间';
COMMENT ON COLUMN agent_capability_binding.remark IS '备注';
COMMENT ON COLUMN agent_capability_binding.create_by IS '创建者';
COMMENT ON COLUMN agent_capability_binding.update_by IS '更新者';

View File

@@ -0,0 +1,28 @@
# 前端模块总览
## 1. 当前原型入口
Studio 原型页面位于 `frontend/src/pages/studio/`,路由集中在 `frontend/src/router/index.ts`,模拟数据位于 `frontend/src/data/studioMock.ts`
## 2. 页面与模块映射
| 页面 | 模块 |
|------|------|
| `StudioDashboardPage.vue` | 工作台与发布就绪 |
| `KnowledgeWorkspacePage.vue` | 知识资产 |
| `IngestionPipelinePage.vue` | 文件解析管道 |
| `ModelWorkspacePage.vue` | 模型与路由 |
| `WorkflowBuilderPage.vue` | Workflow 编排 |
| `AgentWorkspacePage.vue` | Agent 对话调试 |
| `McpImportPage.vue` | MCP 能力接入 |
| `SkillWorkspacePage.vue` | Skill 编辑 |
| `ObservabilityPage.vue` | 运行观测 |
## 3. 前端实现原则
- 页面使用聚合 ViewModel避免页面直接拼多个低层接口。
- 已落地旧接口保持兼容,新 Studio 接口以聚合资源为主。
- Long ID 继续按字符串处理,避免 JS 精度问题。
- 枚举值按整型协议处理,页面展示通过枚举字典或常量映射。
- 管理后台保持信息密度、稳定布局和清晰状态提示。

View File

@@ -0,0 +1,29 @@
# 系统基础模块前端实现
## 1. 页面范围
系统基础前端能力主要体现为枚举字典、上传控件和统一请求处理。当前已有系统枚举管理页,后续 Studio 页面继续复用这些能力。
## 2. API 使用
| 能力 | 接口 |
|------|------|
| 枚举管理查询 | `POST /api/sys-enum/queryForManagement` |
| 枚举详情 | `GET /api/sys-enum/detail` |
| 枚举保存 | `POST /api/sys-enum/save` |
| 枚举删除 | `POST /api/sys-enum/delete` |
| 附件上传 | `POST /api/attachments/upload` |
## 3. 实现约定
- API 层继续使用 `frontend/src/api/request.ts` 解包 `RequestResult<T>`
- Long ID 继续通过 `json-bigint` 安全解析。
- 枚举展示通过字典或本地常量映射,不直接依赖数据库展示顺序。
- 上传失败时展示后端错误摘要,不吞掉异常。
## 4. Studio 复用点
- 文件解析管道复用附件上传能力。
- 知识资产、模型路由、Workflow、Agent 等页面复用枚举字典。
- 运行观测页面复用统一状态颜色和错误提示格式。

View File

@@ -0,0 +1,46 @@
# 知识资产与文件解析模块前端实现
## 1. 页面
| 页面 | 职责 |
|------|------|
| `KnowledgeWorkspacePage.vue` | 展示知识库列表、配置、文档状态和发布影响 |
| `IngestionPipelinePage.vue` | 展示上传、解析、切片、向量化和任务日志 |
| 旧 `RagStoresPage.vue` | 保留知识库 CRUD 联调参考 |
| 旧 `RagDocumentsPage.vue` | 保留知识文档 CRUD 联调参考 |
## 2. 聚合 ViewModel
知识工作台建议使用 `KnowledgeWorkspaceView`
- 当前知识库基础信息。
- 文档健康度。
- Embedding 模型和维度。
- 检索配置。
- 文档列表摘要。
- 待处理任务。
- 发布影响说明。
文件解析管道建议使用 `IngestionRunView`
- 上传文件列表。
- 管道阶段状态。
- 解析文本预览。
- 切片预览。
- 任务日志。
## 3. 接口草案
- `GET /api/knowledge/workspaces/{storeId}`
- `POST /api/knowledge/ingestion-runs`
- `GET /api/knowledge/ingestion-runs/{runId}`
- `POST /api/rag/documents/parse`
- `POST /api/rag/documents/chunk`
## 4. 交互状态
- 解析失败显示可重试状态。
- 索引中显示进行中状态,不允许误标为可检索。
- 切片参数变更后提示需要重建索引。
- Embedding 模型变更后提示会影响当前知识库向量空间。

View File

@@ -0,0 +1,31 @@
# 模型与路由模块前端实现
## 1. 页面
`ModelWorkspacePage.vue` 当前展示任务路由规则,保留 `model_provider``model_config``model_route_rule` 语义。
## 2. ViewModel
`ModelRoutingView` 建议包含:
- 服务商健康概览。
- 模型配置列表。
- 任务路由规则。
- Fallback 关系。
- 最近模型调用失败摘要。
## 3. 接口草案
- `POST /api/model-providers/query`
- `POST /api/model-configs/query`
- `POST /api/model-route-rules/query`
- `POST /api/model-route-rules/save`
- `GET /api/model-call-logs/query`
## 4. 交互规则
- 禁用服务商时提示关联模型和路由受影响。
- Embedding 模型维度变更时提示知识库需要重建索引。
- 草稿路由不得用于生产发布就绪校验。
- Fallback 模型为空时在发布检查中提示风险。

View File

@@ -0,0 +1,32 @@
# Workflow 编排模块前端实现
## 1. 页面
`WorkflowBuilderPage.vue` 提供节点库、画布、运行 Trace 抽屉和节点 Inspector。
## 2. ViewModel
`WorkflowBuilderView` 建议包含:
- Workflow 基本信息。
- 当前草稿版本。
- 节点列表。
- 边列表。
- 当前选中节点配置。
- 最近测试运行 Trace。
## 3. 接口草案
- `GET /api/workflows/{workflowId}`
- `POST /api/workflows/save-draft`
- `POST /api/workflows/{workflowId}/publish`
- `POST /api/workflows/{workflowId}/runs`
- `GET /api/workflows/runs/{runId}`
## 4. 交互规则
- 保存草稿只更新草稿版本。
- 运行测试生成 `workflow_run``workflow_run_step`
- 发布前调用发布就绪检查。
- 节点 Inspector 只编辑当前节点配置,不直接改其他节点。

View File

@@ -0,0 +1,34 @@
# Agent 会话模块前端实现
## 1. 页面
`AgentWorkspacePage.vue` 当前展示 Agent 对话调试、引用切片、运行追踪和成本延迟指标。
## 2. ViewModel
`AgentWorkspaceView` 建议包含:
- 当前 Agent 基本信息。
- 当前会话消息列表。
- 输入框状态。
- 引用切片列表。
- 模型请求 ID。
- 运行追踪步骤。
- 成本、延迟和 Token 统计。
## 3. 接口草案
- `POST /api/agents/{agentId}/runs`
- `GET /api/agents/{agentId}/sessions`
- `GET /api/agent-sessions/{sessionId}`
- `POST /api/agent-sessions/{sessionId}/messages`
现有 `POST /api/agents/{agentId}/chat` 保留为兼容调试入口。
## 4. 交互规则
- RAG 开关关闭时不展示引用切片。
- 未召回知识切片时给出明确提示。
- 模型调用失败时展示 `request_id` 和错误摘要。
- 会话保存失败不得清空用户输入。

View File

@@ -0,0 +1,20 @@
# MCP 能力接入模块前端实现
## 1. 页面
`McpImportPage.vue` 展示导入方式、Manifest 预览和能力预览。
## 2. 接口草案
- `POST /api/mcp/import`
- `GET /api/mcp/servers/{serverCode}/capabilities`
- `POST /api/mcp/servers/query`
- `POST /api/mcp/capabilities/save`
## 3. 交互规则
- 导入前校验地址或 Manifest 格式。
- 能力预览成功后才允许保存。
- 未授权能力显示待授权状态。
- 已停用 Server 的能力不可被 Workflow 新增选择。

View File

@@ -0,0 +1,20 @@
# Skill 编辑模块前端实现
## 1. 页面
`SkillWorkspacePage.vue` 提供 Prompt、Code、Config 编辑区,测试面板和版本列表。
## 2. 接口草案
- `GET /api/skills/{skillCode}`
- `PUT /api/skills/{skillCode}/draft`
- `POST /api/skills/{skillCode}/test`
- `POST /api/skills/{skillCode}/publish`
## 3. 交互规则
- 发布版本只读。
- 草稿变更后提示未发布。
- 测试结果保存到版本草稿。
- Workflow 只能选择发布版本或明确选择草稿测试。

View File

@@ -0,0 +1,29 @@
# 运行观测模块前端实现
## 1. 页面
`ObservabilityPage.vue` 展示运行记录和步骤日志。
## 2. ViewModel
`ObservabilityView` 建议包含:
- 运行列表。
- 当前运行详情。
- 步骤日志。
- 模型调用摘要。
- 成本和延迟统计。
## 3. 接口草案
- `GET /api/observability/runs`
- `GET /api/observability/runs/{requestId}`
- `GET /api/observability/model-calls`
- `GET /api/observability/runs/{requestId}/export`
## 4. 交互规则
- 异常运行高亮展示。
- 步骤日志按执行时间排序。
- 导出日志不包含密钥或完整敏感请求体。

View File

@@ -0,0 +1,38 @@
# 后端模块总览
## 1. 当前代码边界
当前后端已有 `common``rag``modelprovider``agent` 包。后续重新实现时,可以保留这些包作为技术分层落点,但业务文档按产品域拆分。
## 2. 后端实现原则
- Controller 不直接暴露实体。
- 请求使用 `XxxRequest`,响应使用 `XxxResponse`
- 统一返回 `RequestResult<T>`
- 实体继承 `BaseEntity`,保持审计字段和乐观锁字段。
- Mapper 继续使用 MyBatis-Plus `BaseMapper<T>`
- Service 负责业务校验、状态流转和跨表协调。
## 3. 模块落点建议
| 产品域 | 后端包建议 |
|--------|------------|
| 系统基础 | `com.bruce.common` |
| 知识资产与文件解析 | `com.bruce.rag` |
| 模型与路由 | `com.bruce.modelprovider` |
| Workflow 编排 | `com.bruce.workflow` |
| Agent 会话 | `com.bruce.agent` |
| MCP 能力接入 | `com.bruce.mcp` |
| Skill 编辑 | `com.bruce.skill` |
| 运行观测 | `com.bruce.observability` 或复用运行来源模块 |
## 4. 枚举实现约束
新增结构化枚举时,继续实现 `PersistableSysEnumDefinition`,并同步:
- Java 枚举定义。
- `sys_enum` 初始化测试。
- `script/sql/18.studio_enum.sql` 或对应模块枚举脚本。
- 前端枚举常量或字典接口。

View File

@@ -0,0 +1,40 @@
# 系统基础模块后端实现
## 1. 包结构
当前实现位于 `com.bruce.common`
- `controller`:枚举和附件 API。
- `domain/entity``SysEnum``SysAttachment`
- `domain/model``BaseEntity``RequestResult`
- `document/parse`:文档解析抽象和 Tika 实现。
- `enums`:通用枚举与可持久化枚举契约。
- `service`:枚举与附件服务。
## 2. Controller 约定
Controller 只接收请求 DTO 或基础参数,不直接暴露实体。响应统一使用 `RequestResult<T>`
## 3. Service 约定
`SysEnumService` 负责:
- 管理端查询。
- 单条保存和删除。
- 批量保存。
-`catalog + type` 全量替换初始化。
`SysAttachmentService` 负责:
- 校验上传文件。
- 生成存储路径。
- 保存本地文件。
- 写入附件元数据。
## 4. 后续实现注意
- 不调整 `PersistableSysEnumDefinition` 现有方法。
- 不调整 `sys_enum` 表字段。
- 新增枚举时同步初始化测试和 SQL 初始化脚本。
- 文档解析器新增类型时,只扩展解析器实现和工厂注册,不影响 RAG 业务服务。

View File

@@ -0,0 +1,40 @@
# 知识资产与文件解析模块后端实现
## 1. 当前实现落点
当前实现位于 `com.bruce.rag`,包含知识库、文档、解析、切片和向量实体。
## 2. Controller
已落地接口继续保留:
- `/api/rag/store/list`
- `/api/rag/store/query`
- `/api/rag/store/detail`
- `/api/rag/store/overview`
- `/api/rag/store/documentOverview`
- `/api/rag/documents/query`
- `/api/rag/documents/batchUpload`
- `/api/rag/documents/parse`
- `/api/rag/documents/chunk`
Studio 聚合接口后续新增在知识资产模块,不替代旧接口。
## 3. Service
| Service | 职责 |
|---------|------|
| `IRagStoreService` | 知识库 CRUD、概览统计 |
| `IRagDocumentService` | 文档 CRUD、批量上传 |
| `IRagDocumentParseService` | 解析状态流转和解析快照 |
| `IRagDocumentChunkService` | 根据解析快照生成切片 |
| `IRagChunkEmbeddingService` | 向量写入和召回 |
## 4. 校验规则
- 知识库编码唯一。
- 文档必须绑定存在的知识库和附件。
- 只有解析成功的文档允许切片。
- 向量化必须读取知识库生效模型配置。
- 重新切片或模型变更后,索引状态必须回到待索引或索引中。

View File

@@ -0,0 +1,42 @@
# 模型与路由模块后端实现
## 1. 当前实现落点
当前实现位于 `com.bruce.modelprovider`,包含服务商、模型、路由、网关、客户端和调用日志。
## 2. Controller
- `ModelProviderController`
- `ModelConfigController`
- `ModelRouteRuleController`
- `RagStoreModelConfigController`
- `ModelCallLogController`
## 3. Service
| Service | 职责 |
|---------|------|
| `IModelProviderService` | 服务商 CRUD 和健康检查 |
| `IModelConfigService` | 模型配置 CRUD |
| `IModelRouteRuleService` | 路由规则 CRUD |
| `IModelRouteService` | 根据任务上下文选择模型 |
| `IRagStoreModelConfigService` | 知识库模型绑定 |
| `IModelCallLogService` | 调用日志写入与查询 |
## 4. 网关
业务模块只能依赖:
- `ChatModelGateway`
- `EmbeddingModelGateway`
不得直接依赖具体上游客户端。
## 5. 校验规则
- 服务商编码唯一。
- 同一服务商下模型编码唯一。
- 路由主模型必须启用。
- 路由任务类型必须来自枚举。
- 知识库 Embedding 维度必须与模型配置一致。

View File

@@ -0,0 +1,39 @@
# Workflow 编排模块后端实现
## 1. 包建议
后续实现建议新增 `com.bruce.workflow`
- `controller`
- `dto/request`
- `dto/response`
- `entity`
- `enums`
- `mapper`
- `service`
- `runner`
## 2. Controller 草案
- `WorkflowDefinitionController`
- `WorkflowVersionController`
- `WorkflowRunController`
## 3. Service 草案
| Service | 职责 |
|---------|------|
| `IWorkflowDefinitionService` | 定义 CRUD、绑定 Agent |
| `IWorkflowVersionService` | 草稿保存、发布快照 |
| `IWorkflowRunService` | 运行测试、运行记录查询 |
| `IWorkflowRunner` | 按图执行节点并写入步骤日志 |
## 4. 校验规则
- Workflow 编码唯一。
- 发布版本号不可重复。
- 运行时必须使用确定的版本快照。
- 节点类型必须来自枚举。
- LLM 节点必须配置任务类型。
- Knowledge Retrieval 节点必须配置知识库或继承项目默认知识库。

View File

@@ -0,0 +1,30 @@
# Agent 会话模块后端实现
## 1. 当前实现落点
当前 Agent 定义和调试入口位于 `com.bruce.agent`。后续会话能力建议继续放在该包下。
## 2. Controller 草案
- `AgentDefinitionController`:保留定义管理和兼容调试入口。
- `AgentSessionController`:新增会话查询、详情和消息发送。
- `AgentRunController`:新增运行入口,关联 Workflow 或模型调用。
## 3. Service 草案
| Service | 职责 |
|---------|------|
| `IAgentDefinitionService` | Agent 定义管理 |
| `IAgentSessionService` | 会话创建、关闭、查询 |
| `IAgentMessageService` | 消息写入和引用保存 |
| `IAgentRunService` | 对话编排、RAG 召回、模型调用 |
| `IAgentCapabilityBindingService` | 能力绑定维护 |
## 4. 校验规则
- Agent 必须启用才允许发起对话。
- RAG 对话必须绑定可用知识库和 Embedding 配置。
- 消息内容不能为空。
- `citation_json` 必须是数组结构。
- 会话关闭后不允许继续写入消息。

View File

@@ -0,0 +1,21 @@
# MCP 能力接入模块后端实现
## 1. 包建议
后续实现建议新增 `com.bruce.mcp`,包含 Controller、DTO、Entity、Mapper、Service 和 MCP Client 适配。
## 2. Service 草案
| Service | 职责 |
|---------|------|
| `IMcpServerService` | Server 导入、启停用、健康检查 |
| `IMcpCapabilityService` | 能力发现、保存、启停用 |
| `IMcpImportService` | 处理 URL、package、Manifest 导入 |
## 3. 校验规则
- Server 编码唯一。
- 能力编码在 Server 下唯一。
- 导入方式必须来自枚举。
- 能力 schema 必须为合法 JSON。

View File

@@ -0,0 +1,21 @@
# Skill 编辑模块后端实现
## 1. 包建议
后续实现建议新增 `com.bruce.skill`
## 2. Service 草案
| Service | 职责 |
|---------|------|
| `ISkillDefinitionService` | Skill 定义管理 |
| `ISkillVersionService` | 草稿保存、测试、发布、归档 |
| `ISkillRunner` | 运行 Skill 并生成测试结果 |
## 3. 校验规则
- Skill 编码唯一。
- 版本号在 Skill 内唯一。
- Prompt、Code、Config 至少一种非空。
- 变量 schema 必须为合法 JSON。

View File

@@ -0,0 +1,25 @@
# 运行观测模块后端实现
## 1. 包建议
后续实现可新增 `com.bruce.observability`,也可以先由 Workflow、Agent 和模型模块分别提供查询接口。
## 2. Controller 草案
- `ObservabilityRunController`
- `ObservabilityModelCallController`
## 3. Service 草案
| Service | 职责 |
|---------|------|
| `IObservabilityRunService` | 聚合运行记录 |
| `IObservabilityTraceService` | 聚合步骤、会话和模型调用 |
| `IObservabilityExportService` | 导出脱敏日志 |
## 4. 校验规则
- 只允许查询脱敏后的运行摘要。
- 导出接口必须过滤密钥和完整请求内容。
- requestId 不存在时返回空结果而不是内部异常。

View File

@@ -0,0 +1,57 @@
# 数据库与枚举总览
## 1. 文档目的
本文集中说明本轮数据库和枚举脚本的更新范围,避免需求文档、设计文档、前端文档、后端文档与 SQL 脚本出现冲突。
## 2. SQL 脚本分层
| 脚本位置 | 作用 |
|----------|------|
| `script/` | 可执行的模块化建表或初始化脚本 |
| `docs/MODEL_PROVIDER_SCHEMA.sql` | 模型平台 schema 汇总 |
| `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | Studio 原型 schema 汇总 |
模块化脚本是后续落库执行的优先参考,`docs/*SCHEMA.sql` 是完整设计快照。
## 3. 必须保持不变的内容
`script/sql/1.enum.sql``sys_enum` 表结构不变:
- `catalog`
- `type`
- `name`
- `value`
- `strvalue`
- `sort`
- `version`
- `create_time`
- `update_time`
- `remark`
- `create_by`
- `update_by`
Java 枚举契约 `PersistableSysEnumDefinition` 的格式不变。后续如果实现新枚举类,仍按现有 `getCatalog()``getType()``getValue()``getLabel()``getRemark()` 风格实现。
## 4. 本轮新增脚本
| 脚本 | 内容 |
|------|------|
| `script/sql/11.studio_project.sql` | Studio 项目空间表 |
| `script/sql/12.workflow.sql` | Workflow 定义、版本、运行、步骤日志 |
| `script/sql/14.mcp.sql` | MCP Server 与能力表 |
| `script/sql/15.skill.sql` | Skill 定义与版本表 |
| `script/sql/13.agent_session.sql` | Agent 会话与消息表 |
| `script/sql/16.agent_capability_binding.sql` | Agent/Workflow 与 MCP/Skill/知识能力绑定表 |
| `script/sql/18.studio_enum.sql` | Studio 相关枚举初始化 |
## 5. 校验规则
- 所有新增表必须包含 `id``version``create_time``update_time``remark``create_by``update_by`
- 需要唯一业务编码的表必须增加唯一约束。
- 需要跨模块引用的字段必须在文档中说明外键关系。
- JSON 扩展字段统一使用 `JSONB`
- 枚举初始化脚本必须使用 `ON CONFLICT (catalog, type, name) DO UPDATE`,与现有脚本风格一致。

View File

@@ -0,0 +1,40 @@
# 知识资产数据库设计
## 1. 表关系
| 表 | 说明 | 关键关系 |
|----|------|----------|
| `rag_store` | 知识库主表 | 独立业务编码 |
| `rag_document` | 知识文档 | 引用 `rag_store``sys_attachment` |
| `rag_document_parse_result` | 解析快照 | 引用 `rag_store``rag_document` |
| `rag_chunk` | 知识切片 | 引用 `rag_store``rag_document` |
| `rag_chunk_embedding` | 切片向量 | 引用 `rag_store``rag_document``rag_chunk` |
| `rag_store_model_config` | 知识库模型配置 | 引用 `rag_store``model_config` |
## 2. 脚本清单
- `script/sql/3.rag_store.sql`
- `script/sql/4.rag_document.sql`
- `script/sql/5.rag_document_parse_result.sql`
- `script/sql/6.rag_chunk.sql`
- `script/sql/7.rag_chunk_embedding.sql`
- `docs/MODEL_PROVIDER_SCHEMA.sql` 中的 `rag_store_model_config`
## 3. 枚举组
| catalog | type | Java 枚举 |
|---------|------|-----------|
| `rag` | `parse_status` | `RagParseStatusEnum` |
| `rag` | `index_status` | `RagIndexStatusEnum` |
| `rag` | `chunk_strategy` | `RagChunkStrategyEnum` |
## 4. 一致性要求
- `rag_document.attachment_id` 保持唯一,避免一个附件重复建档。
- `rag_document_parse_result.document_id` 保持唯一,表示一个文档一个当前解析快照。
- `rag_chunk` 使用 `(document_id, chunk_index)` 保证同文档切片序号唯一。
- `rag_chunk_embedding` 使用 `(chunk_id, embedding_model)` 防止同模型重复写入。
- RAG 现有脚本与本文档一致,本轮不调整 `sys_enum` 格式。

View File

@@ -0,0 +1,40 @@
# 模型与路由数据库设计
## 1. 表关系
| 表 | 说明 |
|----|------|
| `model_provider` | 模型服务商配置 |
| `model_config` | 服务商下具体模型 |
| `model_route_rule` | 任务路由规则 |
| `rag_store_model_config` | 知识库 Embedding 与切片配置 |
| `model_call_log` | 模型调用日志 |
## 2. 关键约束
- `model_provider.provider_code` 全局唯一。
- `model_config` 使用 `(provider_id, model_code)` 唯一。
- `model_route_rule.route_code` 全局唯一。
- `rag_store_model_config` 使用 `(store_id, active)` 约束一个知识库只有一个生效配置。
- `model_call_log.request_id` 全局唯一。
## 3. 枚举组
| catalog | type |
|---------|------|
| `model_provider` | `provider_type` |
| `model_provider` | `protocol_type` |
| `model_provider` | `model_type` |
| `model_provider` | `task_type` |
| `model_provider` | `route_strategy` |
| `model_provider` | `call_status` |
| `model_provider` | `health_status` |
## 4. 脚本
- `docs/MODEL_PROVIDER_SCHEMA.sql` 是完整 schema 快照。
- `script/sql/9.model_provider.sql` 是后续执行入口。
- 若模型枚举变更,需要同步 Java 枚举、初始化测试和 SQL 枚举脚本。

View File

@@ -0,0 +1,35 @@
# Workflow 数据库设计
## 1. 表结构
| 表 | 说明 |
|----|------|
| `studio_project` | Studio 项目空间 |
| `workflow_definition` | Workflow 主定义 |
| `workflow_version` | Workflow 版本快照 |
| `workflow_run` | Workflow 运行记录 |
| `workflow_run_step` | Workflow 节点步骤日志 |
## 2. 关键约束
- `studio_project.project_code` 唯一。
- `workflow_definition.workflow_code` 唯一。
- `workflow_version` 使用 `(workflow_id, version_no)` 唯一。
- `workflow_run.request_id` 唯一。
- `workflow_definition.bound_agent_id` 可引用 `agent_definition`
## 3. 脚本
- `script/sql/11.studio_project.sql`
- `script/sql/12.workflow.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`
## 4. JSON 字段
- `workflow_version.graph_json` 保存流程图快照。
- `workflow_run.input_json``output_json` 保存运行输入输出摘要。
- `workflow_run_step.input_json``output_json` 保存节点级输入输出摘要。
JSON 字段只保存运行必要摘要,不保存敏感密钥。

View File

@@ -0,0 +1,35 @@
# Agent 会话数据库设计
## 1. 表结构
| 表 | 说明 |
|----|------|
| `agent_definition` | Agent 定义 |
| `agent_session` | Agent 会话 |
| `agent_message` | Agent 消息 |
| `agent_capability_binding` | 能力绑定 |
## 2. 关键约束
- `agent_definition.agent_code` 唯一。
- `agent_session.session_code` 唯一。
- `agent_session.agent_id` 引用 `agent_definition`
- `agent_session.workflow_run_id` 可引用 `workflow_run`
- `agent_message.session_id` 引用 `agent_session`
- `agent_capability_binding` 使用 `(owner_type, owner_id, capability_type, capability_id)` 唯一。
## 3. JSON 字段
- `agent_session.metadata_json` 保存会话扩展信息。
- `agent_message.citation_json` 保存引用切片摘要。
- `agent_capability_binding.config_json` 保存能力绑定配置。
## 4. 脚本
- `script/sql/8.agent_definition.sql`
- `script/sql/13.agent_session.sql`
- `script/sql/16.agent_capability_binding.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`

View File

@@ -0,0 +1,22 @@
# MCP 数据库设计
## 1. 表结构
| 表 | 说明 |
|----|------|
| `mcp_server` | MCP 服务配置 |
| `mcp_capability` | MCP 能力清单 |
## 2. 约束
- `mcp_server.server_code` 唯一。
- `mcp_capability` 使用 `(server_id, capability_code)` 唯一。
- `mcp_capability.server_id` 引用 `mcp_server`
## 3. 脚本
- `script/sql/14.mcp.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`
- `script/sql/18.studio_enum.sql`

View File

@@ -0,0 +1,22 @@
# Skill 数据库设计
## 1. 表结构
| 表 | 说明 |
|----|------|
| `skill_definition` | Skill 定义 |
| `skill_version` | Skill 版本 |
## 2. 约束
- `skill_definition.skill_code` 唯一。
- `skill_version` 使用 `(skill_id, version_no)` 唯一。
- `skill_version.skill_id` 引用 `skill_definition`
## 3. 脚本
- `script/sql/15.skill.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`
- `script/sql/18.studio_enum.sql`

View File

@@ -0,0 +1,32 @@
# 运行观测数据库设计
## 1. 表范围
运行观测复用已有运行表,不单独新增观测主表:
- `workflow_run`
- `workflow_run_step`
- `model_call_log`
- `agent_session`
- `agent_message`
## 2. 索引建议
- `workflow_run.request_id`
- `workflow_run.status`
- `workflow_run_step.run_id`
- `model_call_log.request_id`
- `model_call_log.task_type`
- `agent_session.agent_id`
## 3. 脚本
索引分散在:
- `script/sql/12.workflow.sql`
- `script/sql/9.model_provider.sql`
- `script/sql/13.agent_session.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`

View File

@@ -0,0 +1,82 @@
# 模块一致性校验
## 1. 校验范围
本次校验覆盖:
- `需求分析/`
- `设计文档/`
- `数据库设计/`
- `前端实现文档/`
- `后端实现文档/`
- `script/*.sql`
- `docs/MODEL_PROVIDER_SCHEMA.sql`
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`
## 2. 文档占位检查
已检查常见未决占位关键词,当前新增文档与 SQL 脚本中未保留未决占位。
## 3. 数据库脚本一致性
| 模块 | 汇总 schema | 模块化脚本 | 结论 |
|------|-------------|------------|------|
| 模型与路由 | `docs/MODEL_PROVIDER_SCHEMA.sql` | `script/sql/9.model_provider.sql` | 已补齐 `rag_store_model_config.store_id` 外键 |
| Studio 项目 | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/11.studio_project.sql` | 表、唯一约束、索引一致 |
| Workflow | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/12.workflow.sql` | 定义、版本、运行、步骤表一致 |
| Agent 会话 | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/13.agent_session.sql` | 会话和消息表一致 |
| 能力绑定 | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/16.agent_capability_binding.sql` | 唯一约束和索引一致 |
| MCP | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/14.mcp.sql` | Server 与 Capability 表一致 |
| Skill | `docs/STUDIO_PROTOTYPE_SCHEMA.sql` | `script/sql/15.skill.sql` | 定义与版本表一致 |
## 4. 枚举一致性
`script/sql/18.studio_enum.sql` 保持 `sys_enum` 既有格式:
- `catalog`
- `type`
- `name`
- `value`
- `strvalue`
- `sort`
- `version`
- `remark`
校验结果:
- 枚举行数48。
- 枚举组数14。
- 同一 `catalog + type``value` 无重复。
- 同一 `catalog + type``sort` 无重复。
## 5. 跨模块依赖检查
| 依赖 | 结论 |
|------|------|
| 知识资产依赖系统附件 | `rag_document.attachment_id` 引用 `sys_attachment` 的设计保持不变 |
| 知识资产依赖模型配置 | `rag_store_model_config` 连接 `rag_store``model_config` |
| Workflow 依赖 Agent | `workflow_definition.bound_agent_id` 引用 `agent_definition` |
| Workflow 运行依赖 Agent | `workflow_run.agent_id` 引用 `agent_definition` |
| Agent 会话依赖 Workflow 运行 | `agent_session.workflow_run_id` 引用 `workflow_run` |
| MCP/Skill 依赖能力绑定 | `agent_capability_binding` 通过类型和 ID 绑定能力 |
| 运行观测依赖运行日志 | 复用 `workflow_run``workflow_run_step``model_call_log``agent_session` |
## 6. 保留约束
- 不修改 `sys_enum` 表结构。
- 不修改 `PersistableSysEnumDefinition` 枚举契约。
- 不把结构化枚举协议从整型值改为字符串值。
- 本轮不修改 Java、Vue 或测试代码。
## 7. 后续实现提示
后续按文档重写代码时,新增 Java 枚举必须补齐:
- Java 枚举类。
- `SysEnumDataInitTests` 初始化入口。
- `EnumDefinitionTests` 稳定性断言。
- `script/sql/18.studio_enum.sql` 初始化数据。
- 前端字典或常量映射。

View File

@@ -0,0 +1,51 @@
# Common Agent Studio 模块设计总览
## 1. 总体设计
Common Agent Studio 以一次 AI 应用发布旅程为主线知识接入、模型路由、Workflow 编排、Agent 调试、MCP/Skill 扩展和运行观测围绕同一个项目空间协作。
旧代码中的 `common``rag``modelprovider``agent` 仍作为实现包存在,但新文档按产品域组织,便于后续重新实现时形成更清晰的模块边界。
## 2. 产品域关系
```mermaid
flowchart LR
Common["系统基础"] --> Knowledge["知识资产与文件解析"]
Common --> Model["模型与路由"]
Knowledge --> Workflow["Workflow 编排"]
Model --> Workflow
Workflow --> Agent["Agent 会话"]
MCP["MCP 能力接入"] --> Workflow
Skill["Skill 编辑"] --> Workflow
Agent --> Observability["运行观测"]
Workflow --> Observability
Model --> Observability
```
## 3. 数据主线
| 主线 | 表 |
|------|------|
| 系统基础 | `sys_enum``sys_attachment` |
| 知识资产 | `rag_store``rag_document``rag_document_parse_result``rag_chunk``rag_chunk_embedding` |
| 模型路由 | `model_provider``model_config``model_route_rule``rag_store_model_config``model_call_log` |
| Studio 项目 | `studio_project` |
| Workflow | `workflow_definition``workflow_version``workflow_run``workflow_run_step` |
| Agent | `agent_definition``agent_session``agent_message``agent_capability_binding` |
| MCP | `mcp_server``mcp_capability` |
| Skill | `skill_definition``skill_version` |
## 4. 接口命名原则
- 已落地接口保持兼容,例如 `/api/rag/documents/query``/api/agents/{agentId}/chat`
- Studio 新接口使用聚合资源命名,例如 `/api/knowledge/workspaces/{storeId}``/api/workflows/{workflowId}/runs`
- 请求和响应继续使用 DTO不直接暴露实体。
- 统一返回体保持 `RequestResult<T>`
## 5. 状态设计原则
- 长期稳定状态使用结构化枚举。
- 枚举设计必须保留 `sys_enum` 当前格式。
- 数据库中状态字段允许继续使用 `VARCHAR` 保存枚举代码,前后端协议层对结构化枚举继续传整型值。
- 页面展示文案来自枚举定义或前端常量映射,不在业务逻辑中散落硬编码。

View File

@@ -0,0 +1,33 @@
# 系统基础模块设计
## 1. 模块边界
系统基础模块不承载具体 AI 业务逻辑只提供跨模块复用能力。知识资产、模型路由、Workflow、Agent、MCP、Skill 和观测模块都可以依赖它。
## 2. 核心对象
| 对象 | 说明 |
|------|------|
| `SysEnum` | 系统枚举配置,面向前端字典和初始化脚本 |
| `SysAttachment` | 附件元数据,保存文件名称、路径、来源和大小 |
| `BaseEntity` | 主键、审计字段、乐观锁字段 |
| `RequestResult<T>` | 统一 API 响应信封 |
| `DocumentParser` | 文档文本抽取接口 |
| `DocumentParserFactory` | 根据文件类型选择解析器 |
## 3. 枚举设计原则
结构化枚举继续以 Java 枚举为单一事实来源。Java 枚举实现 `PersistableSysEnumDefinition`,暴露 `catalog``type``name``value``strvalue``sort``remark`
`sys_enum` 表结构不变,新增 Studio 枚举只能新增枚举组或枚举行,不能调整原字段含义。
## 4. 状态与错误
- 业务启停用统一使用 `EnableStatusEnum` 或模块自有状态。
- 长流程处理状态使用模块自有枚举,但必须同步到 `sys_enum`
- 全局异常处理将校验错误转换为统一响应。
## 5. 依赖关系
系统基础模块不能依赖其他业务模块。业务模块可依赖系统基础模块的枚举、附件、解析、返回体和异常处理。

View File

@@ -0,0 +1,45 @@
# 知识资产与文件解析模块设计
## 1. 领域模型
| 对象 | 职责 |
|------|------|
| 知识库 | 聚合文档、模型配置、检索配置和索引版本 |
| 知识文档 | 关联附件,维护解析和索引状态 |
| 解析快照 | 保存文本抽取结果,作为切片输入 |
| 知识切片 | 保存切片内容、序号、元数据和启用状态 |
| 切片向量 | 保存 Embedding 向量、模型名和维度 |
| 知识库模型配置 | 固定知识库 Embedding 模型和切片配置 |
## 2. 状态流转
文档解析状态:
`UPLOADED -> PARSING -> PARSED`,失败时进入 `FAILED`
文档索引状态:
`PENDING -> INDEXING -> INDEXED`,失败时进入 `FAILED`
切片策略继续使用整型枚举值,例如 `1` 表示固定长度,`5` 表示按分隔符。
## 3. 数据流
```mermaid
flowchart LR
Upload["上传文件"] --> Attachment["sys_attachment"]
Attachment --> Document["rag_document"]
Document --> Parse["rag_document_parse_result"]
Parse --> Chunk["rag_chunk"]
Chunk --> Embedding["rag_chunk_embedding"]
Embedding --> Retrieval["检索召回"]
```
## 4. 设计约束
- `rag_document` 只引用附件和知识库,不存储大段解析文本。
- 解析快照按文档唯一,重新解析时更新快照。
- `rag_chunk_embedding` 必须记录模型和维度,防止向量空间混用。
- 知识库模型配置由模型与路由模块维护,但知识资产模块负责消费。
- 检索配置要面向 Workflow 和 Agent 复用,不绑定某一个页面。

View File

@@ -0,0 +1,48 @@
# 模型与路由模块设计
## 1. 分层设计
| 层 | 职责 |
|----|------|
| 配置层 | 管理服务商、模型、路由规则和知识库模型绑定 |
| 路由层 | 根据任务类型、范围和策略选择模型 |
| 调用层 | 通过 OpenAI-compatible 客户端调用 Chat/Embedding |
| 观测层 | 写入模型调用日志,供运行观测查询 |
## 2. 路由输入
模型路由至少需要:
- `taskType`
- `matchScope`
- `scopeId`
- `bizType`
- `bizId`
- 调用参数。
`matchScope` 初始支持 `GLOBAL``RAG_STORE``AGENT`
## 3. 路由输出
路由结果包含:
- 服务商。
- 模型配置。
- 上游模型名。
- 超时时间。
- Fallback 列表。
- 调用选项。
## 4. 失败策略
- 主模型失败时按 Fallback 顺序重试。
- 全部失败时返回统一错误。
- 每次调用均写入 `model_call_log`
- Fallback 成功时状态记为 `FALLBACK_SUCCESS`
## 5. 与其他模块关系
- 知识资产模块使用 Embedding 路由和知识库模型绑定。
- Workflow 和 Agent 使用 Chat 路由。
- 运行观测读取 `model_call_log`

View File

@@ -0,0 +1,47 @@
# Workflow 编排模块设计
## 1. 核心模型
| 对象 | 说明 |
|------|------|
| Studio 项目 | 聚合多个 Workflow、Agent 和发布配置 |
| Workflow 定义 | 可编辑流程主数据 |
| Workflow 版本 | 发布或草稿快照,保存 `graph_json` |
| Workflow 运行 | 一次测试或正式运行 |
| Workflow 步骤 | 单个节点的输入、输出、耗时和错误 |
## 2. 图模型
`workflow_version.graph_json` 保存节点与边:
- 节点:`id``type``label``config``position`
- 边:`from``to``condition`
图模型由后端做最小结构校验,复杂交互由前端画布负责。
## 3. 运行链路
```mermaid
flowchart LR
Start["Start"] --> Retrieve["Knowledge Retrieval"]
Retrieve --> LLM["LLM"]
LLM --> Tool["MCP Tool"]
Tool --> Skill["Skill"]
Skill --> Answer["Answer"]
```
## 4. 状态设计
- Workflow 定义状态:草稿、启用、停用、归档。
- 发布状态:草稿、已发布、已归档。
- 运行状态:排队中、运行中、成功、失败、取消。
- 节点状态:等待、运行中、成功、失败、跳过。
## 5. 依赖关系
- Knowledge Retrieval 节点依赖知识资产模块。
- LLM 节点依赖模型与路由模块。
- MCP Tool 节点依赖 MCP 模块。
- Skill 节点依赖 Skill 模块。
- 运行记录被运行观测模块消费。

View File

@@ -0,0 +1,51 @@
# Agent 会话模块设计
## 1. 核心模型
| 对象 | 说明 |
|------|------|
| Agent 定义 | Agent 配置主数据 |
| Agent 会话 | 一段连续对话上下文 |
| Agent 消息 | 会话中的单条消息 |
| 能力绑定 | Agent 或 Workflow 绑定知识库、MCP、Skill 等能力 |
## 2. 对话模式
- 普通对话:直接根据会话消息调用 Chat 模型。
- RAG 对话:先向量化问题,再召回知识切片,再组装上下文调用 Chat 模型。
- Workflow 对话:通过绑定的 Workflow 运行,消息与运行记录互相关联。
## 3. 引用设计
`agent_message.citation_json` 保存回答引用:
- 文档 ID。
- 切片 ID。
- 分数。
- 引用文本摘要。
- 知识库 ID。
引用只保存必要摘要,完整切片仍以 `rag_chunk` 为准。
## 4. 状态设计
会话状态建议包括:
- ACTIVE活跃。
- CLOSED已关闭。
- FAILED异常终止。
消息角色建议包括:
- USER。
- ASSISTANT。
- SYSTEM。
- TOOL。
## 5. 模块依赖
- 依赖知识资产模块完成 RAG 召回。
- 依赖模型与路由模块完成 Chat 调用。
- 可依赖 Workflow 模块执行复杂流程。
- 运行观测模块读取会话关联的运行记录。

View File

@@ -0,0 +1,27 @@
# MCP 能力接入模块设计
## 1. 核心模型
| 对象 | 说明 |
|------|------|
| MCP Server | 外部能力服务 |
| MCP Capability | Server 暴露的工具或资源 |
| Manifest | Server 能力声明 |
## 2. 导入流程
```mermaid
flowchart LR
Input["输入 URL/package/Manifest"] --> Validate["校验连接与声明"]
Validate --> Server["写入 mcp_server"]
Server --> Capability["写入 mcp_capability"]
Capability --> Binding["绑定给 Workflow/Agent"]
```
## 3. 设计约束
- `secret_ref` 只保存密钥引用,不保存明文密钥。
- `manifest_json` 保存原始能力声明摘要。
- `schema_json` 保存单个能力输入输出 schema。
- Server 停用时,其能力不应被新运行选择。

View File

@@ -0,0 +1,24 @@
# Skill 编辑模块设计
## 1. 核心模型
| 对象 | 说明 |
|------|------|
| Skill 定义 | Skill 主数据 |
| Skill 版本 | Prompt、Code、Config 与测试结果快照 |
## 2. 版本规则
- 草稿版本可编辑。
- 发布版本不可直接修改。
- 归档版本仅可查看。
- Workflow 运行必须引用确定版本。
## 3. 数据设计
- `prompt_text` 保存提示词。
- `code_text` 保存脚本或函数片段。
- `config_json` 保存运行配置。
- `variable_schema_json` 保存输入输出变量定义。
- `test_result_json` 保存最近测试结果摘要。

View File

@@ -0,0 +1,36 @@
# 运行观测模块设计
## 1. 观测主键
运行观测以 `request_id` 为主线串联:
- Workflow 运行。
- Workflow 步骤。
- Agent 会话。
- 模型调用日志。
## 2. 展示维度
- 名称。
- 类型。
- 状态。
- 延迟。
- 成本。
- 步骤输出。
- 错误摘要。
## 3. 数据来源
| 来源 | 表 |
|------|----|
| 流程运行 | `workflow_run` |
| 步骤日志 | `workflow_run_step` |
| 模型调用 | `model_call_log` |
| Agent 会话 | `agent_session``agent_message` |
## 4. 设计约束
- 观测模块只读业务运行数据。
- 不保存完整 Prompt 或敏感密钥。
- 错误信息只保存摘要,详细日志由运行环境负责。

View File

@@ -0,0 +1,67 @@
# Common Agent Studio 模块总览
## 1. 文档目的
本文用于统一本轮文档重构的模块口径。后续需求分析、设计文档、前端实现文档、后端实现文档和数据库设计均以本文的产品域模块为准,不再直接按旧后端包名划分。
本轮只处理文档和 SQL 脚本,不修改 Java、Vue、测试代码。
## 2. 模块划分
| 序号 | 模块 | 范围 |
|------|------|------|
| 1 | 系统基础 | 系统枚举、附件、审计字段、统一返回体、文档解析抽象 |
| 2 | 知识资产与文件解析 | 知识库、知识文档、解析快照、切片、向量、索引任务 |
| 3 | 模型与路由 | 模型服务商、模型配置、任务路由、知识库模型绑定、调用日志 |
| 4 | Workflow 编排 | 项目空间、Workflow 定义、版本快照、运行记录、步骤日志 |
| 5 | Agent 会话 | Agent 定义、调试运行、会话、消息、引用切片 |
| 6 | MCP 能力接入 | MCP Server 导入、能力发现、能力启停用 |
| 7 | Skill 编辑 | Skill 定义、版本、Prompt/Code/Config、测试结果 |
| 8 | 运行观测 | Workflow Trace、模型调用日志、成本、延迟、异常排查 |
## 3. 目录规范
| 目录 | 用途 |
|------|------|
| `需求分析/` | 描述业务目标、角色、场景、范围和验收标准 |
| `设计文档/` | 描述领域模型、状态流转、模块依赖和接口形态 |
| `数据库设计/` | 描述表结构、枚举、脚本同步规则和跨模块关系 |
| `前端实现文档/` | 描述页面、ViewModel、API 调用和交互状态 |
| `后端实现文档/` | 描述 Controller、DTO、Service、Entity、Mapper 和校验规则 |
## 4. 脚本范围
本轮 SQL 脚本属于正式交付范围:
- `script/*.sql`:面向落库执行的模块化脚本。
- `docs/MODEL_PROVIDER_SCHEMA.sql`:模型平台 schema 汇总。
- `docs/STUDIO_PROTOTYPE_SCHEMA.sql`Studio 原型 schema 汇总。
新增或调整数据库设计时,必须同步更新对应脚本。新增或调整枚举设计时,必须同步更新 `sys_enum` 初始化脚本。
## 5. 枚举约束
`sys_enum` 表结构保持不变Java 枚举契约保持现有格式不变:
- `catalog`
- `type`
- `name`
- `value`
- `strvalue`
- `sort`
- `remark`
前后端结构化枚举继续使用整型 `value` 作为协议值,不改成字符串协议。
## 6. 交叉引用规则
每个模块文档必须说明:
- 关联数据库表。
- 关联枚举组。
- 关联 SQL 脚本。
- 关联前端页面或原型 View。
- 关联后端接口草案。
最终以 `数据库设计/9.模块一致性校验.md` 统一检查跨模块冲突。

View File

@@ -0,0 +1,41 @@
# 系统基础模块需求
## 1. 模块目标
系统基础模块为 Common Agent Studio 提供所有产品域共用的底座能力,包括系统枚举、附件上传、审计字段、统一响应、文档解析抽象和全局异常处理。
## 2. 用户角色
| 角色 | 诉求 |
|------|------|
| 平台管理员 | 维护系统枚举、检查附件上传和基础配置 |
| 开发者 | 复用统一 DTO、返回体、审计字段和解析能力 |
| 前端开发者 | 使用一致的枚举字典和错误响应 |
| 运维人员 | 通过统一字段排查创建人、更新时间和异常信息 |
## 3. 功能需求
1. 系统枚举必须支持按 `catalog + type` 查询,用于前端字典、后台管理和初始化校验。
2. `sys_enum` 结构必须保持现状,不因 Studio 新增模块调整字段格式。
3. 附件模块必须支持本地上传、元数据入库和业务来源关联。
4. 文档解析抽象必须支持 TXT/Markdown/LOG、PDF、Word、Excel 的文本抽取。
5. 所有业务接口继续返回 `RequestResult<T>`
6. 所有业务实体继续继承公共审计字段和乐观锁字段。
## 4. 非功能需求
- 枚举值稳定,不能随展示文案调整而改变。
- 附件路径不直接暴露为外部可访问地址。
- 异常响应保持统一结构,便于前端统一提示。
- 文档解析失败必须返回可定位的错误摘要。
## 5. 关联资料
- 表:`sys_enum``sys_attachment`
- 枚举:`common/enable_status``common/common_status`
- 脚本:`script/sql/1.enum.sql``script/sql/2.attachment.sql`
- 后端入口:`SysEnumController``SysAttachmentController``DocumentParserFactory`
- 前端入口:系统枚举 API、文件上传组件、枚举字典调用

View File

@@ -0,0 +1,39 @@
# 知识资产与文件解析模块需求
## 1. 模块目标
知识资产与文件解析模块负责把外部文件变成可检索知识资产,覆盖知识库维护、文件上传、文本解析、切片、向量化和索引状态管理。
## 2. 核心场景
1. 知识维护者创建知识库并批量上传文档。
2. 系统保存附件元数据并创建 `rag_document`
3. 文档解析管道抽取文本并保存解析快照。
4. 用户选择切片策略生成 `rag_chunk`
5. 系统调用 Embedding 模型写入 `rag_chunk_embedding`
6. 知识库达到可检索状态后供 Workflow 和 Agent 调用。
## 3. 功能需求
- 知识库支持新增、编辑、删除、查询和概览统计。
- 文档支持上传、解析、解析失败重试、切片和索引状态查看。
- 解析结果必须落到 `rag_document_parse_result`,切片不能直接依赖原始附件。
- 同一文档重新切片时,必须替换旧切片并推动索引重建。
- 知识库必须绑定稳定的 Embedding 模型和向量维度。
- 前端需要展示文档健康度、解析失败数、待向量化任务数和发布影响。
## 4. 验收标准
- 能从知识库视角看到文档数量、解析状态、索引状态和切片数量。
- 能从文件解析管道看到上传、解析、切片、向量化、可检索的阶段。
- 枚举值与现有 `RagParseStatusEnum``RagIndexStatusEnum``RagChunkStrategyEnum` 一致。
## 5. 关联资料
- 表:`rag_store``rag_document``rag_document_parse_result``rag_chunk``rag_chunk_embedding``rag_store_model_config`
- 枚举:`rag/parse_status``rag/index_status``rag/chunk_strategy`
- 脚本:`script/sql/3.rag_store.sql``script/sql/4.rag_document.sql``script/sql/5.rag_document_parse_result.sql``script/sql/6.rag_chunk.sql``script/sql/7.rag_chunk_embedding.sql`
- 前端原型:`KnowledgeWorkspacePage.vue``IngestionPipelinePage.vue`

View File

@@ -0,0 +1,42 @@
# 模型与路由模块需求
## 1. 模块目标
模型与路由模块负责统一管理模型服务商、模型配置、任务路由、知识库模型绑定和模型调用日志,为 RAG、Workflow、Agent 和 Skill 提供统一模型入口。
## 2. 功能需求
- 支持配置 Ollama、硅基流动、百炼、OpenAI 和自定义 OpenAI-compatible 服务。
- 支持维护 Chat、Embedding、Rerank、多模态模型。
- 支持按任务类型配置主模型、Fallback 模型和路由策略。
- 支持知识库固定 Embedding 模型、向量维度、切片策略和索引版本。
- 支持记录模型调用状态、耗时、Token、成本和错误摘要。
## 3. 任务类型
初始任务类型包括:
- RAG 文档向量化。
- RAG 查询向量化。
- RAG 问答生成。
- 简单文本处理。
- 复杂文本处理。
- Agent 规划。
- 重排序。
## 4. 验收标准
- 业务模块不直接调用上游模型服务。
- 同一知识库不能混用不同 Embedding 向量空间。
- 所有调用必须有 `request_id`,方便观测模块追踪。
- 路由规则禁用时不能被选中。
## 5. 关联资料
- 表:`model_provider``model_config``model_route_rule``rag_store_model_config``model_call_log`
- 枚举:`model_provider/provider_type``protocol_type``model_type``task_type``route_strategy``call_status``health_status`
- 脚本:`docs/MODEL_PROVIDER_SCHEMA.sql``script/sql/9.model_provider.sql`
- 前端原型:`ModelWorkspacePage.vue`

View File

@@ -0,0 +1,29 @@
# Workflow 编排模块需求
## 1. 模块目标
Workflow 编排模块负责把知识检索、模型调用、MCP 工具、Skill 和回答输出组织成可保存、可测试、可发布、可追踪的图形化流程。
## 2. 功能需求
- 支持项目空间管理,区分环境和发布状态。
- 支持 Workflow 草稿编辑、保存和发布版本。
- 支持节点库Start、LLM、Knowledge Retrieval、MCP Tool、Skill、Condition、Answer。
- 支持 JSON Graph 保存画布结构。
- 支持运行测试并生成运行记录和步骤日志。
- 支持绑定默认 Agent供对话调试和发布使用。
## 3. 发布要求
- 草稿可以保存但不能直接作为生产版本。
- 发布时必须生成不可变版本快照。
- 发布前必须检查模型路由、知识库索引、MCP 授权和 Skill 发布状态。
## 4. 关联资料
- 表:`studio_project``workflow_definition``workflow_version``workflow_run``workflow_run_step`
- 枚举:`studio/environment``studio/publish_status``workflow/status``workflow/run_status``workflow/node_type`
- 脚本:`script/sql/11.studio_project.sql``script/sql/12.workflow.sql`
- 前端原型:`StudioDashboardPage.vue``WorkflowBuilderPage.vue`

View File

@@ -0,0 +1,32 @@
# Agent 会话模块需求
## 1. 模块目标
Agent 会话模块负责 Agent 定义、对话调试、会话持久化、消息记录、引用切片和运行追踪,使一次调试或发布后的对话可以被复盘。
## 2. 功能需求
- 支持 Agent 定义管理,包含编码、名称、系统提示词、默认知识库和状态。
- 支持普通对话和 RAG 对话。
- 支持会话持久化,记录 `agent_session`
- 支持消息持久化记录用户、Agent、系统等角色消息。
- 支持保存引用切片 JSON便于回答溯源。
- 支持关联 Workflow 运行记录,形成端到端 Trace。
## 3. 会话场景
1. 用户选择 Agent 输入调试问题。
2. 系统创建或复用会话。
3. RAG 模式下执行检索召回。
4. 调用 Chat 模型生成回答。
5. 写入消息、引用、模型请求 ID 和运行追踪。
## 4. 关联资料
- 表:`agent_definition``agent_session``agent_message``agent_capability_binding`
- 脚本:`script/sql/8.agent_definition.sql``script/sql/13.agent_session.sql``script/sql/16.agent_capability_binding.sql`
- 前端原型:`AgentWorkspacePage.vue`
- 后端入口:`AgentDefinitionController``AgentDefinitionServiceImpl`

View File

@@ -0,0 +1,22 @@
# MCP 能力接入模块需求
## 1. 模块目标
MCP 能力接入模块负责导入外部 MCP Server读取能力清单并把工具或资源能力绑定给 Workflow 和 Agent 使用。
## 2. 功能需求
- 支持 URL、npm package、JSON Manifest 三种导入方式。
- 支持保存 Server 连接信息、鉴权方式、密钥引用和 Manifest。
- 支持发现工具、资源等能力。
- 支持能力启停用。
- 支持健康状态展示。
## 3. 关联资料
- 表:`mcp_server``mcp_capability`
- 枚举:`mcp/import_type``mcp/capability_type``mcp/health_status`
- 脚本:`script/sql/14.mcp.sql``script/sql/18.studio_enum.sql`
- 前端原型:`McpImportPage.vue`

View File

@@ -0,0 +1,22 @@
# Skill 编辑模块需求
## 1. 模块目标
Skill 编辑模块负责维护可复用的提示词、代码片段、配置和测试结果,使 Workflow 或 Agent 可以复用稳定的能力单元。
## 2. 功能需求
- 支持 Skill 定义管理。
- 支持版本草稿、发布和归档。
- 支持 Prompt、Code、Config 三类编辑区域。
- 支持变量 schema 和测试结果保存。
- 支持发布版本供 Workflow 或 Agent 绑定。
## 3. 关联资料
- 表:`skill_definition``skill_version`
- 枚举:`skill/skill_type``skill/status``studio/publish_status`
- 脚本:`script/sql/15.skill.sql``script/sql/18.studio_enum.sql`
- 前端原型:`SkillWorkspacePage.vue`

View File

@@ -0,0 +1,22 @@
# 运行观测模块需求
## 1. 模块目标
运行观测模块负责展示 Workflow、Agent、MCP、Skill 和模型调用的运行记录,支撑排障、成本分析和发布质量评估。
## 2. 功能需求
- 展示最近运行记录。
- 展示步骤日志和节点输出摘要。
- 展示模型调用耗时、Token 和成本。
- 支持按 requestId 追踪一次完整运行。
- 支持导出日志。
## 3. 关联资料
- 表:`workflow_run``workflow_run_step``model_call_log``agent_session``agent_message`
- 脚本:`script/sql/12.workflow.sql``script/sql/9.model_provider.sql``script/sql/13.agent_session.sql`
- 前端原型:`ObservabilityPage.vue`

View File

@@ -0,0 +1,43 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
chatWithAgent,
deleteAgent,
getAgentById,
listAgents,
queryAgents,
saveAgent,
} from '../agent';
import { get, post } from '../request';
vi.mock('../request', () => ({
get: vi.fn(),
post: vi.fn(),
}));
describe('agent api', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('maps agent endpoints correctly', () => {
listAgents();
queryAgents({ agentCode: 'demo' });
getAgentById('1001');
saveAgent({ agentCode: 'agent_1', agentName: 'Agent 1', storeId: '2001', status: 'ENABLED' });
deleteAgent('1001');
chatWithAgent('1001', { messages: [{ role: 'user', content: '你好' }] });
expect(post).toHaveBeenCalledWith('/agents/list');
expect(post).toHaveBeenCalledWith('/agents/query', { agentCode: 'demo' });
expect(get).toHaveBeenCalledWith('/agents/detail', { params: { id: '1001' } });
expect(post).toHaveBeenCalledWith('/agents/save', {
agentCode: 'agent_1',
agentName: 'Agent 1',
storeId: '2001',
status: 'ENABLED',
});
expect(post).toHaveBeenCalledWith('/agents/delete', undefined, { params: { id: '1001' } });
expect(post).toHaveBeenCalledWith('/agents/1001/chat', { messages: [{ role: 'user', content: '你好' }] });
});
});

70
frontend/src/api/agent.ts Normal file
View File

@@ -0,0 +1,70 @@
import { get, post } from './request';
export interface AgentDefinition {
id?: string;
agentCode: string;
agentName: string;
systemPrompt?: string;
storeId: string;
status: string;
remark?: string;
}
export interface AgentDefinitionQueryRequest {
agentCode?: string;
agentName?: string;
status?: string;
storeId?: string;
}
export interface AgentMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface AgentChatRequest {
messages: AgentMessage[];
ragEnabled?: boolean;
}
export interface AgentReferenceChunk {
chunkId: string;
documentId: string;
chunkContent: string;
score?: number;
}
export interface AgentChatResponse {
agentId: string;
agentCode: string;
agentName: string;
storeId: string;
storeName?: string;
answer: string;
modelRequestId: string;
references: AgentReferenceChunk[];
}
export function listAgents() {
return post<AgentDefinition[]>('/agents/list');
}
export function queryAgents(query?: AgentDefinitionQueryRequest) {
return post<AgentDefinition[], AgentDefinitionQueryRequest | undefined>('/agents/query', query);
}
export function getAgentById(id: string) {
return get<AgentDefinition>('/agents/detail', { params: { id } });
}
export function saveAgent(data: Partial<AgentDefinition> & { id?: string }) {
return post<boolean>('/agents/save', data);
}
export function deleteAgent(id: string) {
return post<boolean>('/agents/delete', undefined, { params: { id } });
}
export function chatWithAgent(agentId: string, data: AgentChatRequest) {
return post<AgentChatResponse, AgentChatRequest>(`/agents/${agentId}/chat`, data);
}

View File

@@ -0,0 +1,143 @@
export type PipelineStatus = 'done' | 'running' | 'blocked' | 'idle';
export interface LifecycleStep {
name: string;
description: string;
status: PipelineStatus;
}
export interface RecentRun {
id: string;
name: string;
type: string;
status: string;
latency: string;
cost: string;
}
export interface KnowledgeDocument {
id: string;
name: string;
parseStatus: string;
indexStatus: string;
chunks: number;
updatedAt: string;
}
export interface WorkflowNode {
id: string;
type: string;
label: string;
description: string;
x: number;
y: number;
}
export interface WorkflowEdge {
from: string;
to: string;
}
export interface TraceStep {
node: string;
status: string;
duration: string;
output: string;
}
export const lifecycleSteps: LifecycleStep[] = [
{ name: '知识接入', description: '上传、解析、切片、向量化', status: 'done' },
{ name: '能力编排', description: 'Workflow 连接模型、工具与 Skill', status: 'running' },
{ name: '对话调试', description: '验证引用、成本、延迟与回答质量', status: 'running' },
{ name: '发布观测', description: '版本快照、运行追踪、异常排查', status: 'idle' },
];
export const readinessChecklist = [
{ label: '知识库已绑定 Embedding 模型', done: true },
{ label: 'Workflow 草稿存在未发布节点变更', done: false },
{ label: 'Agent 已绑定默认知识库与 Skill', done: true },
{ label: '生产环境路由规则仍需压测', done: false },
];
export const recentRuns: RecentRun[] = [
{ id: 'run-1842', name: '售前问答 Agent', type: 'Agent', status: '成功', latency: '1.42s', cost: '¥0.018' },
{ id: 'run-1841', name: '合同知识召回', type: 'Workflow', status: '成功', latency: '860ms', cost: '¥0.006' },
{ id: 'run-1840', name: 'MCP: jira.search', type: 'MCP', status: '重试', latency: '2.8s', cost: '¥0.000' },
];
export const knowledgeStores = [
{ id: '1001', name: '产品制度库', docs: 128, health: 96, status: '可检索' },
{ id: '1002', name: '交付项目资料', docs: 64, health: 82, status: '索引中' },
{ id: '1003', name: '客服 FAQ', docs: 214, health: 91, status: '可检索' },
];
export const knowledgeDocuments: KnowledgeDocument[] = [
{ id: 'doc-01', name: '售前方案模板.pdf', parseStatus: 'PARSED', indexStatus: 'INDEXED', chunks: 42, updatedAt: '10分钟前' },
{ id: 'doc-02', name: '项目实施手册.docx', parseStatus: 'PARSED', indexStatus: 'INDEXING', chunks: 88, updatedAt: '23分钟前' },
{ id: 'doc-03', name: '服务条款更新.md', parseStatus: 'FAILED', indexStatus: 'PENDING', chunks: 0, updatedAt: '今天 09:12' },
{ id: 'doc-04', name: '客服高频问题.xlsx', parseStatus: 'PARSED', indexStatus: 'INDEXED', chunks: 119, updatedAt: '昨天' },
];
export const ingestionSteps: LifecycleStep[] = [
{ name: '上传', description: '4 个文件已入库 sys_attachment', status: 'done' },
{ name: '解析', description: 'Tika 抽取文本并保存快照', status: 'done' },
{ name: '切片', description: '固定长度 800 / overlap 120', status: 'running' },
{ name: '向量化', description: 'Qwen3 Embedding 1024 维', status: 'idle' },
{ name: '可检索', description: '等待索引任务完成', status: 'idle' },
];
export const workflowNodes: WorkflowNode[] = [
{ id: 'start', type: 'START', label: 'Start', description: '接收用户问题', x: 4, y: 42 },
{ id: 'retrieve', type: 'KNOWLEDGE_RETRIEVAL', label: 'Knowledge Retrieval', description: 'TopK=6 / score>0.72', x: 25, y: 18 },
{ id: 'llm', type: 'LLM', label: 'LLM', description: 'RAG_ANSWER 路由', x: 47, y: 42 },
{ id: 'mcp', type: 'MCP_TOOL', label: 'MCP Tool', description: 'jira.search / docs.lookup', x: 47, y: 70 },
{ id: 'skill', type: 'SKILL', label: 'Skill', description: '答案审校与引用整理', x: 69, y: 42 },
{ id: 'answer', type: 'ANSWER', label: 'Answer', description: '返回回答与引用', x: 88, y: 42 },
];
export const workflowEdges: WorkflowEdge[] = [
{ from: 'start', to: 'retrieve' },
{ from: 'retrieve', to: 'llm' },
{ from: 'llm', to: 'skill' },
{ from: 'mcp', to: 'skill' },
{ from: 'skill', to: 'answer' },
];
export const traceSteps: TraceStep[] = [
{ node: 'Start', status: '完成', duration: '4ms', output: '用户问题已标准化' },
{ node: 'Knowledge Retrieval', status: '完成', duration: '218ms', output: '召回 6 个切片' },
{ node: 'LLM', status: '完成', duration: '1.12s', output: '生成 612 tokens' },
{ node: 'Skill', status: '完成', duration: '88ms', output: '引用格式已校验' },
];
export const chatMessages = [
{ role: 'user', content: '如果客户要求私有化部署,售前方案里必须说明哪些内容?' },
{
role: 'assistant',
content: '建议说明部署拓扑、模型服务商、知识库索引策略、权限边界、日志留存周期和故障恢复方式。当前回答引用了 3 个知识切片。',
},
];
export const citations = [
{ title: '售前方案模板.pdf', score: '0.91', text: '私有化部署章节应覆盖基础设施、网络、安全与运维边界。' },
{ title: '项目实施手册.docx', score: '0.87', text: '交付计划需包含数据导入、索引重建与验收标准。' },
{ title: '服务条款更新.md', score: '0.82', text: '客户数据默认不出域,模型调用日志需保留审计字段。' },
];
export const mcpCapabilities = [
{ name: 'jira.search', type: 'tool', status: '已启用', description: '按项目、状态、负责人检索任务' },
{ name: 'docs.lookup', type: 'resource', status: '已启用', description: '读取外部文档中心条目' },
{ name: 'deploy.trigger', type: 'tool', status: '待授权', description: '触发测试环境部署流水线' },
];
export const skillVersions = [
{ version: 'v4', status: 'Draft', updatedAt: '刚刚', note: '增加引用一致性检查' },
{ version: 'v3', status: 'Published', updatedAt: '昨天', note: '生产环境当前版本' },
{ version: 'v2', status: 'Archived', updatedAt: '5天前', note: '旧版回答润色策略' },
];
export const modelRoutes = [
{ task: 'RAG_ANSWER', primary: 'qwen-plus', fallback: 'deepseek-v3', latency: '1800ms', status: '启用' },
{ task: 'RAG_EMBEDDING', primary: 'Qwen3-Embedding', fallback: '无', latency: '900ms', status: '启用' },
{ task: 'AGENT_PLAN', primary: 'gpt-4.1', fallback: 'qwen-max', latency: '3200ms', status: '草稿' },
];

View File

@@ -1,64 +1,71 @@
<script setup lang="ts">
import {
Box,
ChatDotRound,
Collection,
Connection,
Cpu,
DataBoard,
Document,
Grid,
Histogram,
List,
Setting,
MagicStick,
Monitor,
Operation,
UploadFilled,
} from '@element-plus/icons-vue';
const systemMenuItems = [
{ path: '/system/enums', label: '系统枚举', icon: Grid },
{ path: '/system/model/providers', label: '模型服务商', icon: Setting },
{ path: '/system/model/configs', label: '模型配置', icon: Setting },
{ path: '/system/model/routes', label: '路由规则', icon: Setting },
{ path: '/system/model/call-logs', label: '调用日志', icon: Setting },
];
const ragMenuItems = [
{ path: '/rag/stores', label: '知识库', icon: Collection },
{ path: '/rag/workbench', label: 'RAG工作台', icon: Histogram },
{ path: '/rag/documents', label: '知识文档', icon: Document },
{ path: '/rag/tasks/chunk', label: '切片任务', icon: List },
const studioMenuItems = [
{ path: '/studio', label: '工作台', icon: DataBoard },
{ path: '/knowledge', label: '知识资产', icon: Collection },
{ path: '/knowledge/ingestion', label: '文件解析', icon: UploadFilled },
{ path: '/workflows', label: 'Workflow', icon: Connection },
{ path: '/agents', label: 'Agent 对话', icon: ChatDotRound },
{ path: '/mcp', label: 'MCP', icon: Operation },
{ path: '/skills', label: 'Skills', icon: MagicStick },
{ path: '/models', label: '模型', icon: Cpu },
{ path: '/observability', label: '观测', icon: Monitor },
];
</script>
<template>
<el-container class="admin-layout">
<el-aside class="admin-sidebar" width="232px">
<el-container class="admin-layout studio-shell">
<el-aside class="admin-sidebar studio-sidebar" width="248px">
<div class="brand">
<el-icon :size="24">
<Box />
<Document />
</el-icon>
<span>Common Agent</span>
<div>
<span>Common Agent Studio</span>
<small>AI Agent Development Platform</small>
</div>
</div>
<el-menu class="side-menu" :default-active="$route.path" router>
<el-sub-menu index="system">
<template #title>系统管理</template>
<el-menu-item v-for="item in systemMenuItems" :key="item.path" :index="item.path">
<el-icon>
<component :is="item.icon" />
</el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="rag">
<template #title>RAG</template>
<el-menu-item v-for="item in ragMenuItems" :key="item.path" :index="item.path">
<el-icon>
<component :is="item.icon" />
</el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
</el-sub-menu>
<el-menu class="side-menu studio-menu" :default-active="$route.path" router>
<el-menu-item v-for="item in studioMenuItems" :key="item.path" :index="item.path">
<el-icon>
<component :is="item.icon" />
</el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
</el-menu>
<div class="sidebar-status">
<span>Dev 环境</span>
<strong>Draft / Published</strong>
</div>
</el-aside>
<el-container>
<el-main class="admin-main">
<el-container class="studio-content-shell">
<header class="studio-topbar">
<div class="project-switcher">
<span>项目</span>
<strong>通用 AI Agent 开发平台</strong>
</div>
<div class="topbar-actions">
<span class="run-status">运行状态正常</span>
<el-button>版本快照</el-button>
<el-button type="primary">发布</el-button>
</div>
</header>
<el-main class="admin-main studio-main">
<RouterView />
</el-main>
</el-container>

View File

@@ -6,16 +6,16 @@ import { describe, expect, it, vi } from 'vitest';
import AdminLayout from '../AdminLayout.vue';
vi.mock('vue-router', () => ({
useRoute: () => ({ meta: { title: '系统枚举' } }),
useRoute: () => ({ meta: { title: '工作台' } }),
}));
describe('AdminLayout', () => {
it('does not render a duplicate page header above the main page content', () => {
it('renders the Studio shell navigation without legacy admin groups', () => {
const wrapper = mount(AdminLayout, {
global: {
plugins: [createPinia(), ElementPlus],
mocks: {
$route: { path: '/system/enums' },
$route: { path: '/studio' },
},
stubs: {
RouterView: { template: '<main data-test="router-view" />' },
@@ -25,5 +25,12 @@ describe('AdminLayout', () => {
expect(wrapper.find('.admin-header').exists()).toBe(false);
expect(wrapper.find('[data-test="router-view"]').exists()).toBe(true);
expect(wrapper.text()).toContain('Common Agent Studio');
expect(wrapper.text()).toContain('知识资产');
expect(wrapper.text()).toContain('Workflow');
expect(wrapper.text()).toContain('观测');
expect(wrapper.text()).not.toContain('系统管理');
expect(wrapper.text()).not.toContain('RAG');
expect(wrapper.text()).not.toContain('Agent管理');
});
});

View File

@@ -0,0 +1,270 @@
<script setup lang="ts">
import { ElMessage } from 'element-plus';
import { computed, onMounted, ref } from 'vue';
import { chatWithAgent, listAgents, type AgentDefinition, type AgentMessage, type AgentReferenceChunk } from '@/api/agent';
interface ChatBubble {
id: string;
role: 'user' | 'assistant';
content: string;
references?: AgentReferenceChunk[];
requestId?: string;
}
const loading = ref(false);
const sending = ref(false);
const agents = ref<AgentDefinition[]>([]);
const selectedAgentId = ref('');
const inputText = ref('');
const messages = ref<ChatBubble[]>([]);
const ragEnabled = ref(true);
const selectedAgent = computed(() => agents.value.find((agent) => agent.id === selectedAgentId.value));
async function loadAgents() {
loading.value = true;
try {
const response = await listAgents();
agents.value = (response.data ?? []).filter((item) => item.status === 'ENABLED');
if (!selectedAgentId.value && agents.value.length > 0) {
const firstAgent = agents.value[0];
selectedAgentId.value = firstAgent && firstAgent.id ? firstAgent.id : '';
}
} finally {
loading.value = false;
}
}
function buildRequestMessages(nextUserText: string): AgentMessage[] {
const historyMessages: AgentMessage[] = messages.value.map((message) => ({
role: message.role,
content: message.content,
}));
historyMessages.push({ role: 'user', content: nextUserText });
return historyMessages;
}
async function sendMessage() {
const trimmed = inputText.value.trim();
if (!selectedAgentId.value) {
ElMessage.warning('请先选择Agent');
return;
}
if (!trimmed) {
return;
}
const requestMessages = buildRequestMessages(trimmed);
const userBubble: ChatBubble = {
id: `${Date.now()}_u`,
role: 'user',
content: trimmed,
};
messages.value.push(userBubble);
inputText.value = '';
sending.value = true;
try {
const response = await chatWithAgent(selectedAgentId.value, {
messages: requestMessages,
ragEnabled: ragEnabled.value,
});
const result = response.data;
messages.value.push({
id: `${Date.now()}_a`,
role: 'assistant',
content: result?.answer ?? '',
references: result?.references ?? [],
requestId: result?.modelRequestId,
});
} finally {
sending.value = false;
}
}
function clearChat() {
messages.value = [];
}
onMounted(loadAgents);
</script>
<template>
<section class="page-panel agent-debug">
<div class="page-panel__header">
<h2>Agent 调试</h2>
<span>Chat Debugger</span>
</div>
<div class="debug-toolbar">
<el-select v-model="selectedAgentId" class="debug-toolbar__agent" :loading="loading" placeholder="请选择Agent">
<el-option
v-for="item in agents"
:key="item.id"
:label="`${item.agentName}(${item.agentCode})`"
:value="item.id"
/>
</el-select>
<el-switch v-model="ragEnabled" active-text="RAG对话" inactive-text="普通对话" />
<el-button @click="loadAgents">刷新Agent</el-button>
<el-button @click="clearChat">清空会话</el-button>
</div>
<div class="debug-chat">
<div v-for="bubble in messages" :key="bubble.id" class="chat-row" :class="`chat-row--${bubble.role}`">
<div class="chat-bubble">
<div class="chat-bubble__role">{{ bubble.role === 'user' ? '用户' : '助手' }}</div>
<div class="chat-bubble__content">{{ bubble.content }}</div>
<template v-if="bubble.role === 'assistant'">
<div v-if="bubble.references?.length" class="chat-bubble__refs">
<div class="chat-bubble__refs-title">引用切片</div>
<ul>
<li v-for="reference in bubble.references" :key="reference.chunkId">
<span class="ref-meta">#{{ reference.chunkId }} · 相似度 {{ (reference.score ?? 0).toFixed(4) }}</span>
<span>{{ reference.chunkContent }}</span>
</li>
</ul>
</div>
<div v-if="bubble.requestId" class="chat-bubble__request-id">requestId: {{ bubble.requestId }}</div>
</template>
</div>
</div>
<div v-if="messages.length === 0" class="chat-empty">
选择Agent后输入问题发起对话调试
</div>
</div>
<div class="debug-input">
<el-input
v-model="inputText"
type="textarea"
:rows="3"
resize="none"
:disabled="sending || !selectedAgent"
placeholder="输入问题回车发送Shift+Enter换行"
@keydown.enter.exact.prevent="sendMessage"
/>
<el-button type="primary" :loading="sending" :disabled="!selectedAgent" @click="sendMessage">发送</el-button>
</div>
</section>
</template>
<style scoped>
.agent-debug {
display: flex;
flex-direction: column;
}
.debug-toolbar {
display: flex;
gap: 10px;
padding: 16px 22px 12px;
}
.debug-toolbar__agent {
width: 320px;
}
.debug-chat {
flex: 1;
min-height: 420px;
max-height: 58vh;
padding: 12px 22px;
overflow-y: auto;
border-top: 1px solid var(--app-border-soft);
border-bottom: 1px solid var(--app-border-soft);
background: #fafcff;
}
.chat-row {
display: flex;
margin-bottom: 14px;
}
.chat-row--user {
justify-content: flex-end;
}
.chat-row--assistant {
justify-content: flex-start;
}
.chat-bubble {
width: min(80%, 860px);
padding: 12px;
border-radius: 8px;
border: 1px solid var(--app-border);
background: #ffffff;
}
.chat-row--user .chat-bubble {
background: #eef5ff;
border-color: #d3e5ff;
}
.chat-bubble__role {
margin-bottom: 6px;
color: var(--app-text-muted);
font-size: 12px;
font-weight: 600;
}
.chat-bubble__content {
white-space: pre-wrap;
line-height: 1.6;
}
.chat-bubble__refs {
margin-top: 12px;
padding: 10px;
border-radius: 8px;
background: #f8fafc;
}
.chat-bubble__refs-title {
margin-bottom: 8px;
color: #344054;
font-size: 12px;
font-weight: 600;
}
.chat-bubble__refs ul {
margin: 0;
padding-left: 16px;
}
.chat-bubble__refs li {
margin-bottom: 8px;
display: flex;
flex-direction: column;
gap: 4px;
}
.ref-meta {
color: var(--app-text-muted);
font-size: 12px;
}
.chat-bubble__request-id {
margin-top: 8px;
color: var(--app-text-muted);
font-size: 12px;
}
.chat-empty {
color: var(--app-text-muted);
text-align: center;
padding: 36px 0;
}
.debug-input {
display: flex;
flex-direction: column;
gap: 10px;
padding: 12px 22px 18px;
}
.debug-input .el-button {
align-self: flex-end;
}
</style>

View File

@@ -0,0 +1,195 @@
<script setup lang="ts">
import { Delete, Edit, Plus, RefreshRight } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed, onMounted, reactive, ref } from 'vue';
import { deleteAgent, queryAgents, saveAgent, type AgentDefinition } from '@/api/agent';
import { listRagStores, type RagStore } from '@/api/ragStores';
const loading = ref(false);
const saving = ref(false);
const dialogVisible = ref(false);
const agents = ref<AgentDefinition[]>([]);
const stores = ref<RagStore[]>([]);
const statusOptions = [
{ label: '启用', value: 'ENABLED' },
{ label: '禁用', value: 'DISABLED' },
];
const editForm = reactive<AgentDefinition>({
agentCode: '',
agentName: '',
systemPrompt: '',
storeId: '',
status: 'ENABLED',
remark: '',
});
const dialogTitle = computed(() => (editForm.id ? '编辑Agent' : '新增Agent'));
function resetForm(row?: AgentDefinition) {
editForm.id = row?.id;
editForm.agentCode = row?.agentCode ?? '';
editForm.agentName = row?.agentName ?? '';
editForm.systemPrompt = row?.systemPrompt ?? '';
editForm.storeId = row?.storeId ?? stores.value[0]?.id ?? '';
editForm.status = row?.status ?? 'ENABLED';
editForm.remark = row?.remark ?? '';
}
async function loadStores() {
const response = await listRagStores();
stores.value = response.data ?? [];
}
async function loadAgents() {
loading.value = true;
try {
const response = await queryAgents();
agents.value = response.data ?? [];
} finally {
loading.value = false;
}
}
function openCreateDialog() {
resetForm();
dialogVisible.value = true;
}
function openEditDialog(row: AgentDefinition) {
resetForm(row);
dialogVisible.value = true;
}
async function submitAgent() {
if (!editForm.agentCode || !editForm.agentName || !editForm.storeId) {
ElMessage.warning('请填写Agent编码、名称和绑定知识库');
return;
}
saving.value = true;
try {
await saveAgent({ ...editForm });
ElMessage.success('保存成功');
dialogVisible.value = false;
await loadAgents();
} finally {
saving.value = false;
}
}
async function removeAgent(row: AgentDefinition) {
if (!row.id) {
return;
}
await ElMessageBox.confirm(`确认删除Agent「${row.agentName || row.agentCode}」?`, '删除确认', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消',
});
await deleteAgent(row.id);
ElMessage.success('已删除');
await loadAgents();
}
function storeLabel(storeId?: string) {
const store = stores.value.find((item) => item.id === storeId);
return store?.storeName ?? store?.storeCode ?? storeId ?? '-';
}
onMounted(async () => {
await loadStores();
resetForm();
await loadAgents();
});
</script>
<template>
<section class="page-panel">
<div class="page-panel__header">
<h2>Agent 管理</h2>
<span>Agent Config</span>
</div>
<div class="toolbar">
<div class="toolbar__actions">
<el-button :icon="RefreshRight" @click="loadAgents">刷新</el-button>
<el-button type="primary" :icon="Plus" @click="openCreateDialog">新增Agent</el-button>
</div>
</div>
<el-table v-loading="loading" :data="agents" row-key="id">
<el-table-column prop="agentCode" label="Agent编码" min-width="140" />
<el-table-column prop="agentName" label="Agent名称" min-width="140" />
<el-table-column label="知识库" min-width="140">
<template #default="{ row }">{{ storeLabel(row.storeId) }}</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 'ENABLED' ? 'success' : 'info'">
{{ row.status === 'ENABLED' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="systemPrompt" label="系统提示词" min-width="220" show-overflow-tooltip />
<el-table-column label="操作" width="160" fixed="right">
<template #default="{ row }">
<el-button link type="primary" :icon="Edit" @click="openEditDialog(row)">编辑</el-button>
<el-button link type="danger" :icon="Delete" @click="removeAgent(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="720px">
<el-form :model="editForm" label-width="120px">
<el-form-item label="Agent编码" required>
<el-input v-model="editForm.agentCode" placeholder="如 AGENT_RAG_HELPER" />
</el-form-item>
<el-form-item label="Agent名称" required>
<el-input v-model="editForm.agentName" placeholder="如 知识问答助手" />
</el-form-item>
<el-form-item label="绑定知识库" required>
<el-select v-model="editForm.storeId">
<el-option
v-for="store in stores"
:key="store.id"
:label="`${store.storeName}(${store.storeCode})`"
:value="store.id"
/>
</el-select>
</el-form-item>
<el-form-item label="系统提示词">
<el-input v-model="editForm.systemPrompt" type="textarea" :rows="4" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="editForm.status">
<el-radio-button v-for="item in statusOptions" :key="item.value" :value="item.value">
{{ item.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="editForm.remark" type="textarea" :rows="2" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitAgent">保存</el-button>
</template>
</el-dialog>
</section>
</template>
<style scoped>
.toolbar {
display: flex;
justify-content: flex-end;
padding: 16px 22px;
}
.toolbar__actions {
display: flex;
gap: 8px;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<main class="not-found">
<h1>404</h1>
<RouterLink to="/rag/workbench">返回RAG工作台</RouterLink>
<RouterLink to="/studio">返回工作台</RouterLink>
</main>
</template>

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import { ChatDotRound, Coin, Timer } from '@element-plus/icons-vue';
import { chatMessages, citations, traceSteps } from '@/data/studioMock';
</script>
<template>
<section class="studio-page agent-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">AgentWorkspaceView</p>
<h1>Agent 对话调试</h1>
</div>
<el-button type="primary">发布 Agent</el-button>
</header>
<div class="agent-layout">
<section class="studio-panel chat-panel">
<div class="panel-heading">
<div>
<h2>售前问答 Agent</h2>
<span>POST /api/agents/1001/runs</span>
</div>
<el-tag>Draft</el-tag>
</div>
<div class="message-list">
<article v-for="message in chatMessages" :key="message.content" :class="message.role">
<strong>{{ message.role === 'user' ? '用户' : 'Agent' }}</strong>
<p>{{ message.content }}</p>
</article>
</div>
<div class="chat-composer">
<span>输入调试问题运行会写入 agent_session / agent_message 草案</span>
<el-button type="primary"><el-icon><ChatDotRound /></el-icon> 发送</el-button>
</div>
</section>
<aside class="studio-panel citation-panel">
<div class="panel-heading compact">
<h2>引用切片</h2>
<span>3 个来源</span>
</div>
<article v-for="citation in citations" :key="citation.title" class="citation-card">
<strong>{{ citation.title }}</strong>
<el-tag type="success">score {{ citation.score }}</el-tag>
<p>{{ citation.text }}</p>
</article>
</aside>
<aside class="studio-panel run-inspector">
<div class="panel-heading compact">
<h2>运行追踪</h2>
<span>modelRequestId: f4215d</span>
</div>
<div class="metric-mini">
<span><el-icon><Timer /></el-icon> 1.42s</span>
<span><el-icon><Coin /></el-icon> ¥0.018</span>
<span>1,248 tokens</span>
</div>
<ol class="log-list">
<li v-for="step in traceSteps" :key="step.node">
<time>{{ step.duration }}</time>
<span>{{ step.node }} · {{ step.output }}</span>
</li>
</ol>
</aside>
</div>
</section>
</template>

View File

@@ -0,0 +1,73 @@
<script setup lang="ts">
import { UploadFilled } from '@element-plus/icons-vue';
import { ingestionSteps } from '@/data/studioMock';
</script>
<template>
<section class="studio-page ingestion-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">IngestionPipelineView</p>
<h1>文件解析管道</h1>
</div>
<el-button type="primary">启动索引任务</el-button>
</header>
<div class="ingestion-layout">
<section class="studio-panel upload-panel">
<div class="upload-dropzone">
<el-icon><UploadFilled /></el-icon>
<strong>拖拽文件到这里</strong>
<span>支持 PDF / Word / Excel / Markdown / TXT上传后自动创建 ingestion run</span>
<el-button type="primary">选择文件</el-button>
</div>
<div class="pipeline-timeline">
<article v-for="step in ingestionSteps" :key="step.name" :class="`is-${step.status}`">
<div class="timeline-dot" />
<div class="timeline-content">
<strong>{{ step.name }}</strong>
<span>{{ step.description }}</span>
</div>
</article>
</div>
</section>
<section class="studio-panel preview-panel">
<div class="panel-heading">
<h2>解析与切片预览</h2>
<span>GET /api/knowledge/ingestion-runs/run-20260531</span>
</div>
<div class="preview-split">
<article>
<h3>解析文本</h3>
<p>私有化部署章节应覆盖基础设施网络安全与运维边界平台需说明模型服务商知识库索引策略与日志留存周期...</p>
</article>
<article>
<h3>切片 #24</h3>
<p>chunk_size=800, overlap=120, strategy=FIXED_LENGTH该切片将进入 rag_chunk 并在向量化后写入 rag_chunk_embedding</p>
</article>
</div>
<div class="pipeline-controls">
<label>切片策略 <strong>固定长度</strong></label>
<label>Chunk Size <strong>800</strong></label>
<label>Overlap <strong>120</strong></label>
<label>Embedding <strong>Qwen3 1024d</strong></label>
</div>
</section>
<aside class="studio-panel task-log-panel">
<div class="panel-heading compact">
<h2>任务日志</h2>
<span>run-20260531</span>
</div>
<ol class="log-list">
<li><time>23:08:12</time><span>上传 4 个文件并创建 rag_document</span></li>
<li><time>23:08:24</time><span>Tika 解析完成 3 个文件</span></li>
<li class="warn"><time>23:08:31</time><span>服务条款更新.md 编码检测失败等待重试</span></li>
<li><time>23:08:40</time><span>切片任务进行中 68 / 119</span></li>
</ol>
</aside>
</div>
</section>
</template>

View File

@@ -0,0 +1,94 @@
<script setup lang="ts">
import { DataAnalysis, Document, Setting } from '@element-plus/icons-vue';
import { knowledgeDocuments, knowledgeStores } from '@/data/studioMock';
</script>
<template>
<section class="studio-page workspace-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">KnowledgeWorkspaceView</p>
<h1>知识资产</h1>
</div>
<el-button type="primary">新建知识库</el-button>
</header>
<div class="three-column-layout">
<aside class="studio-panel collection-rail">
<div class="panel-heading compact">
<h2>知识集合</h2>
<span>{{ knowledgeStores.length }} 个库</span>
</div>
<button
v-for="store in knowledgeStores"
:key="store.id"
class="collection-item"
:class="{ active: store.id === '1001' }"
>
<strong>{{ store.name }}</strong>
<span>{{ store.docs }} 文档 · 健康度 {{ store.health }}%</span>
<em>{{ store.status }}</em>
</button>
</aside>
<main class="studio-panel knowledge-main">
<div class="panel-heading">
<div>
<h2>产品制度库</h2>
<span>绑定旧数据语义rag_store / rag_document / rag_chunk_embedding</span>
</div>
<el-tag type="success">可检索</el-tag>
</div>
<div class="config-grid">
<article>
<el-icon><Setting /></el-icon>
<strong>Embedding 模型</strong>
<span>Qwen3-Embedding · 1024 </span>
</article>
<article>
<el-icon><DataAnalysis /></el-icon>
<strong>检索配置</strong>
<span>TopK 6 · Score 0.72 · Rerank 关闭</span>
</article>
<article>
<el-icon><Document /></el-icon>
<strong>索引版本</strong>
<span>index_version 14 · Draft 快照</span>
</article>
</div>
<div class="document-table">
<div class="table-row table-head">
<span>文档</span><span>解析</span><span>索引</span><span>切片</span><span>更新</span>
</div>
<div v-for="doc in knowledgeDocuments" :key="doc.id" class="table-row">
<strong>{{ doc.name }}</strong>
<span>{{ doc.parseStatus }}</span>
<span>{{ doc.indexStatus }}</span>
<span>{{ doc.chunks }}</span>
<span>{{ doc.updatedAt }}</span>
</div>
</div>
</main>
<aside class="studio-panel inspector-panel">
<div class="panel-heading compact">
<h2>状态 Inspector</h2>
<span>聚合接口</span>
</div>
<dl class="inspector-list">
<dt>Workspace API</dt>
<dd>GET /api/knowledge/workspaces/1001</dd>
<dt>文档健康度</dt>
<dd>96% · 1 个解析失败</dd>
<dt>待处理任务</dt>
<dd>2 个文档等待向量化</dd>
<dt>发布影响</dt>
<dd>更新后需要 Workflow 重新验证引用质量</dd>
</dl>
</aside>
</div>
</section>
</template>

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import { Link, Upload } from '@element-plus/icons-vue';
import { mcpCapabilities } from '@/data/studioMock';
</script>
<template>
<section class="studio-page mcp-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">McpImportView</p>
<h1>MCP 导入</h1>
</div>
<el-button type="primary"><el-icon><Upload /></el-icon> 导入 Server</el-button>
</header>
<div class="mcp-layout">
<section class="studio-panel import-panel">
<div class="panel-heading">
<h2>外部能力接入</h2>
<span>POST /api/mcp/import</span>
</div>
<div class="import-options">
<button class="active"><el-icon><Link /></el-icon><strong>URL</strong><span>https://mcp.example.com/sse</span></button>
<button><strong>npm package</strong><span>@acme/mcp-jira</span></button>
<button><strong>JSON Manifest</strong><span>粘贴 server 能力声明</span></button>
</div>
<div class="manifest-box">
<span>{ "server": "jira", "transport": "sse", "auth": "oauth2" }</span>
</div>
</section>
<section class="studio-panel capability-panel">
<div class="panel-heading">
<h2>能力预览</h2>
<span>GET /api/mcp/servers/jira/capabilities</span>
</div>
<div class="capability-grid">
<article v-for="item in mcpCapabilities" :key="item.name">
<el-tag>{{ item.type }}</el-tag>
<strong>{{ item.name }}</strong>
<p>{{ item.description }}</p>
<span>{{ item.status }}</span>
</article>
</div>
</section>
</div>
</section>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import { modelRoutes } from '@/data/studioMock';
</script>
<template>
<section class="studio-page model-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">ModelRoutingView</p>
<h1>模型与路由</h1>
</div>
<el-button type="primary">新增路由</el-button>
</header>
<div class="studio-panel model-panel">
<div class="panel-heading">
<h2>任务路由规则</h2>
<span>保留 model_provider / model_config / model_route_rule 语义</span>
</div>
<div class="document-table">
<div class="table-row table-head">
<span>任务</span><span>主模型</span><span>Fallback</span><span>最大延迟</span><span>状态</span>
</div>
<div v-for="route in modelRoutes" :key="route.task" class="table-row">
<strong>{{ route.task }}</strong>
<span>{{ route.primary }}</span>
<span>{{ route.fallback }}</span>
<span>{{ route.latency }}</span>
<span class="status-cell">
<span class="status-pill" :class="route.status === '启用' ? 'is-success' : 'is-warning'">
{{ route.status }}
</span>
</span>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,53 @@
<script setup lang="ts">
import { recentRuns, traceSteps } from '@/data/studioMock';
</script>
<template>
<section class="studio-page observability-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">ObservabilityView</p>
<h1>运行观测</h1>
</div>
<el-button>导出日志</el-button>
</header>
<div class="observability-layout">
<section class="studio-panel">
<div class="panel-heading">
<h2>运行记录</h2>
<span>workflow_run / workflow_run_step / model_call_log</span>
</div>
<div class="run-table">
<div class="run-row run-head">
<span>名称</span><span>类型</span><span>状态</span><span>延迟</span><span>成本</span>
</div>
<div v-for="run in recentRuns" :key="run.id" class="run-row">
<strong>{{ run.name }}</strong>
<span>{{ run.type }}</span>
<span class="status-cell">
<span class="status-pill" :class="run.status === '成功' ? 'is-success' : 'is-warning'">
{{ run.status }}
</span>
</span>
<span>{{ run.latency }}</span>
<span>{{ run.cost }}</span>
</div>
</div>
</section>
<aside class="studio-panel">
<div class="panel-heading compact">
<h2>步骤日志</h2>
<span>run-1842</span>
</div>
<ol class="log-list">
<li v-for="step in traceSteps" :key="step.node">
<time>{{ step.duration }}</time>
<span>{{ step.node }} · {{ step.status }} · {{ step.output }}</span>
</li>
</ol>
</aside>
</div>
</section>
</template>

View File

@@ -0,0 +1,56 @@
<script setup lang="ts">
import { skillVersions } from '@/data/studioMock';
</script>
<template>
<section class="studio-page skill-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">SkillWorkspaceView</p>
<h1>Skill 编辑与使用</h1>
</div>
<el-button type="primary">测试 Skill</el-button>
</header>
<div class="skill-layout">
<section class="studio-panel skill-editor">
<div class="panel-heading">
<h2>引用审校 Skill</h2>
<span>PUT /api/skills/skill-citation/draft</span>
</div>
<div class="editor-tabs">
<button class="active">Prompt</button>
<button>Code</button>
<button>Config</button>
</div>
<pre class="prompt-editor">你是回答审校器请检查答案是否完整引用知识库切片并输出
1. answer_quality
2. missing_citations
3. rewrite_suggestion</pre>
<div class="variable-grid">
<label>变量 <strong>answer</strong></label>
<label>变量 <strong>citations[]</strong></label>
<label>输出 <strong>quality_score</strong></label>
</div>
</section>
<aside class="studio-panel test-panel">
<div class="panel-heading compact">
<h2>测试面板</h2>
<span>POST /api/skills/skill-citation/test</span>
</div>
<div class="test-result">
<strong>quality_score: 0.86</strong>
<p>建议补充日志留存周期的引用来源并将私有化部署边界写得更明确</p>
</div>
<div class="version-list">
<article v-for="version in skillVersions" :key="version.version">
<strong>{{ version.version }}</strong>
<span>{{ version.status }}</span>
<em>{{ version.note }} · {{ version.updatedAt }}</em>
</article>
</div>
</aside>
</div>
</section>
</template>

View File

@@ -0,0 +1,99 @@
<script setup lang="ts">
import { ArrowRight, Check, Warning } from '@element-plus/icons-vue';
import { lifecycleSteps, readinessChecklist, recentRuns } from '@/data/studioMock';
</script>
<template>
<section class="studio-page dashboard-page">
<header class="studio-hero">
<div>
<p class="studio-kicker">项目 / Common Agent Studio</p>
<h1>从知识接入到 Agent 发布的一体化工作台</h1>
<p>
使用新的聚合 ViewModel 驱动原型知识资产WorkflowMCPSkillAgent 调试与观测都围绕一次发布旅程组织
</p>
</div>
<div class="hero-actions">
<el-button type="primary">新建 Workflow</el-button>
<el-button>导入 MCP</el-button>
</div>
</header>
<div class="lifecycle-strip">
<article v-for="(step, index) in lifecycleSteps" :key="step.name" class="lifecycle-step" :class="`is-${step.status}`">
<div class="step-index">{{ index + 1 }}</div>
<div>
<strong>{{ step.name }}</strong>
<span>{{ step.description }}</span>
</div>
<el-icon v-if="index < lifecycleSteps.length - 1"><ArrowRight /></el-icon>
</article>
</div>
<div class="dashboard-grid">
<section class="studio-panel readiness-panel">
<div class="panel-heading">
<div>
<h2>发布就绪检查</h2>
<span>ViewModel: StudioDashboardView</span>
</div>
<el-tag type="warning">Draft</el-tag>
</div>
<ul class="check-list">
<li v-for="item in readinessChecklist" :key="item.label" :class="{ done: item.done }">
<el-icon>
<Check v-if="item.done" />
<span v-else class="pending-dot" />
</el-icon>
<span>{{ item.label }}</span>
</li>
</ul>
</section>
<section class="studio-panel metrics-panel">
<div class="panel-heading">
<h2>运行概览</h2>
<span>环境: Dev</span>
</div>
<div class="metric-row">
<div><strong>27</strong><span>今日运行</span></div>
<div><strong>96.4%</strong><span>成功率</span></div>
<div><strong>1.28s</strong><span>P50 延迟</span></div>
<div><strong>¥4.82</strong><span>预估成本</span></div>
</div>
</section>
<section class="studio-panel recent-panel">
<div class="panel-heading">
<h2>最近运行</h2>
<span>Run Trace</span>
</div>
<div class="run-table">
<div class="run-row run-head">
<span>名称</span><span>类型</span><span>状态</span><span>延迟</span><span>成本</span>
</div>
<div v-for="run in recentRuns" :key="run.id" class="run-row">
<strong>{{ run.name }}</strong>
<span>{{ run.type }}</span>
<span class="status-cell">
<span class="status-pill" :class="run.status === '成功' ? 'is-success' : 'is-warning'">
{{ run.status }}
</span>
</span>
<span>{{ run.latency }}</span>
<span>{{ run.cost }}</span>
</div>
</div>
</section>
<section class="studio-panel warning-panel">
<el-icon><Warning /></el-icon>
<div>
<h2>生产发布前仍需确认路由兜底</h2>
<p>AGENT_PLAN 任务当前只有草稿路由建议补齐 fallback 模型和最大延迟阈值</p>
</div>
</section>
</div>
</section>
</template>

View File

@@ -0,0 +1,112 @@
<script setup lang="ts">
import { Connection, Cpu, VideoPlay } from '@element-plus/icons-vue';
import { traceSteps, workflowEdges, workflowNodes } from '@/data/studioMock';
const nodeById = Object.fromEntries(workflowNodes.map((node) => [node.id, node]));
const canvasEdges = workflowEdges.flatMap((edge) => {
const from = nodeById[edge.from];
const to = nodeById[edge.to];
if (!from || !to) {
return [];
}
return [
{
id: `${edge.from}-${edge.to}`,
x1: from.x + 5,
y1: from.y + 4,
x2: to.x,
y2: to.y + 4,
},
];
});
</script>
<template>
<section class="studio-page workflow-page">
<header class="page-title-row">
<div>
<p class="studio-kicker">WorkflowBuilderView · Draft / Published</p>
<h1>Workflow 图形化编排</h1>
</div>
<div class="toolbar-actions">
<el-button>保存草稿</el-button>
<el-button type="primary"><el-icon><VideoPlay /></el-icon> 运行测试</el-button>
</div>
</header>
<div class="workflow-layout">
<aside class="studio-panel node-library">
<div class="panel-heading compact">
<h2>节点库</h2>
<span>JSON Graph</span>
</div>
<button>Start</button>
<button>LLM</button>
<button>Knowledge Retrieval</button>
<button>MCP Tool</button>
<button>Skill</button>
<button>Condition</button>
<button>Answer</button>
</aside>
<main class="studio-panel workflow-canvas">
<div class="canvas-toolbar">
<span><el-icon><Connection /></el-icon> workflow-support-rag</span>
<span>版本快照 v7</span>
<span>环境: Dev</span>
</div>
<div class="canvas-surface">
<svg class="edge-layer" viewBox="0 0 100 100" preserveAspectRatio="none">
<line
v-for="edge in canvasEdges"
:key="edge.id"
:x1="edge.x1"
:y1="edge.y1"
:x2="edge.x2"
:y2="edge.y2"
/>
</svg>
<article
v-for="node in workflowNodes"
:key="node.id"
class="workflow-node"
:class="{ selected: node.id === 'llm' }"
:style="{ left: `${node.x}%`, top: `${node.y}%` }"
>
<span>{{ node.type }}</span>
<strong>{{ node.label }}</strong>
<em>{{ node.description }}</em>
</article>
</div>
<div class="run-trace-drawer">
<strong>Run Trace</strong>
<div v-for="step in traceSteps" :key="step.node">
<span>{{ step.node }}</span>
<em>{{ step.status }} · {{ step.duration }} · {{ step.output }}</em>
</div>
</div>
</main>
<aside class="studio-panel inspector-panel">
<div class="panel-heading compact">
<h2>节点 Inspector</h2>
<span>LLM</span>
</div>
<dl class="inspector-list">
<dt>任务类型</dt>
<dd>RAG_ANSWER</dd>
<dt>输入 Schema</dt>
<dd>question, retrieved_chunks, conversation</dd>
<dt>输出 Schema</dt>
<dd>answer, citations, safety_flags</dd>
<dt>路由策略</dt>
<dd>primary qwen-plus / fallback deepseek-v3</dd>
</dl>
<button class="blue-command"><el-icon><Cpu /></el-icon> 打开模型路由</button>
</aside>
</div>
</section>
</template>

View File

@@ -3,19 +3,19 @@ import { describe, expect, it } from 'vitest';
import { routes } from '../index';
describe('router', () => {
it('defines the admin shell routes', () => {
it('defines the Studio product routes', () => {
const paths = routes.map((route) => route.path);
expect(paths).toContain('/');
expect(paths).toContain('/rag/stores');
expect(paths).toContain('/rag/workbench');
expect(paths).toContain('/rag/documents');
expect(paths).toContain('/rag/tasks/chunk');
expect(paths).toContain('/system/enums');
expect(paths).toContain('/system/model/providers');
expect(paths).toContain('/system/model/configs');
expect(paths).toContain('/system/model/routes');
expect(paths).toContain('/system/model/call-logs');
expect(paths).toContain('/studio');
expect(paths).toContain('/knowledge');
expect(paths).toContain('/knowledge/ingestion');
expect(paths).toContain('/workflows');
expect(paths).toContain('/agents');
expect(paths).toContain('/mcp');
expect(paths).toContain('/skills');
expect(paths).toContain('/models');
expect(paths).toContain('/observability');
expect(paths).toContain('/:pathMatch(.*)*');
});
});

View File

@@ -1,76 +1,76 @@
import type { RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHistory } from 'vue-router';
import NotFoundPage from '@/pages/common/NotFoundPage.vue';
import RagDocumentsPage from '@/pages/rag/RagDocumentsPage.vue';
import RagStoresPage from '@/pages/rag/RagStoresPage.vue';
import RagChunkTasksPage from '@/pages/rag/tasks/RagChunkTasksPage.vue';
import RagWorkbenchPage from '@/pages/rag/workbench/RagWorkbenchPage.vue';
import SystemEnumsPage from '@/pages/system/SystemEnumsPage.vue';
import ModelProvidersPage from '@/pages/system/ModelProvidersPage.vue';
import ModelConfigsPage from '@/pages/system/ModelConfigsPage.vue';
import ModelRouteRulesPage from '@/pages/system/ModelRouteRulesPage.vue';
import ModelCallLogsPage from '@/pages/system/ModelCallLogsPage.vue';
import AdminLayout from '@/layouts/AdminLayout.vue';
import NotFoundPage from '@/pages/common/NotFoundPage.vue';
import AgentWorkspacePage from '@/pages/studio/AgentWorkspacePage.vue';
import IngestionPipelinePage from '@/pages/studio/IngestionPipelinePage.vue';
import KnowledgeWorkspacePage from '@/pages/studio/KnowledgeWorkspacePage.vue';
import McpImportPage from '@/pages/studio/McpImportPage.vue';
import ModelWorkspacePage from '@/pages/studio/ModelWorkspacePage.vue';
import ObservabilityPage from '@/pages/studio/ObservabilityPage.vue';
import SkillWorkspacePage from '@/pages/studio/SkillWorkspacePage.vue';
import StudioDashboardPage from '@/pages/studio/StudioDashboardPage.vue';
import WorkflowBuilderPage from '@/pages/studio/WorkflowBuilderPage.vue';
export const routes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/rag/workbench',
redirect: '/studio',
},
{
path: '/system/enums',
name: 'system-enums',
component: SystemEnumsPage,
meta: { title: '系统枚举' },
path: '/studio',
name: 'studio-dashboard',
component: StudioDashboardPage,
meta: { title: '工作台' },
},
{
path: '/system/model/providers',
name: 'system-model-providers',
component: ModelProvidersPage,
meta: { title: '模型服务商' },
path: '/knowledge',
name: 'knowledge-workspace',
component: KnowledgeWorkspacePage,
meta: { title: '知识资产' },
},
{
path: '/system/model/configs',
name: 'system-model-configs',
component: ModelConfigsPage,
meta: { title: '模型配置' },
path: '/knowledge/ingestion',
name: 'knowledge-ingestion',
component: IngestionPipelinePage,
meta: { title: '文件解析' },
},
{
path: '/system/model/routes',
name: 'system-model-routes',
component: ModelRouteRulesPage,
meta: { title: '路由规则' },
path: '/workflows',
name: 'workflow-builder',
component: WorkflowBuilderPage,
meta: { title: 'Workflow' },
},
{
path: '/system/model/call-logs',
name: 'system-model-call-logs',
component: ModelCallLogsPage,
meta: { title: '调用日志' },
path: '/agents',
name: 'agent-workspace',
component: AgentWorkspacePage,
meta: { title: 'Agent 对话' },
},
{
path: '/rag/stores',
name: 'rag-stores',
component: RagStoresPage,
meta: { title: '知识库' },
path: '/mcp',
name: 'mcp-import',
component: McpImportPage,
meta: { title: 'MCP 导入' },
},
{
path: '/rag/workbench',
name: 'rag-workbench',
component: RagWorkbenchPage,
meta: { title: 'RAG工作台' },
path: '/skills',
name: 'skill-workspace',
component: SkillWorkspacePage,
meta: { title: 'Skill 编辑' },
},
{
path: '/rag/documents',
name: 'rag-documents',
component: RagDocumentsPage,
meta: { title: '知识文档' },
path: '/models',
name: 'model-workspace',
component: ModelWorkspacePage,
meta: { title: '模型路由' },
},
{
path: '/rag/tasks/chunk',
name: 'rag-chunk-tasks',
component: RagChunkTasksPage,
meta: { title: '切片任务' },
path: '/observability',
name: 'observability',
component: ObservabilityPage,
meta: { title: '观测' },
},
{
path: '/:pathMatch(.*)*',
@@ -81,73 +81,14 @@ export const routes: RouteRecordRaw[] = [
];
const routerRoutes: RouteRecordRaw[] = [
{
path: '/',
redirect: '/rag/workbench',
},
{
path: '/',
component: AdminLayout,
children: [
{
path: 'system/enums',
name: 'system-enums',
component: SystemEnumsPage,
meta: { title: '系统枚举' },
},
{
path: 'system/model/providers',
name: 'system-model-providers',
component: ModelProvidersPage,
meta: { title: '模型服务商' },
},
{
path: 'system/model/configs',
name: 'system-model-configs',
component: ModelConfigsPage,
meta: { title: '模型配置' },
},
{
path: 'system/model/routes',
name: 'system-model-routes',
component: ModelRouteRulesPage,
meta: { title: '路由规则' },
},
{
path: 'system/model/call-logs',
name: 'system-model-call-logs',
component: ModelCallLogsPage,
meta: { title: '调用日志' },
},
{
path: 'rag/stores',
name: 'rag-stores',
component: RagStoresPage,
meta: { title: '知识库' },
},
{
path: 'rag/workbench',
name: 'rag-workbench',
component: RagWorkbenchPage,
meta: { title: 'RAG工作台' },
},
{
path: 'rag/documents',
name: 'rag-documents',
component: RagDocumentsPage,
meta: { title: '知识文档' },
},
{
path: 'rag/tasks/chunk',
name: 'rag-chunk-tasks',
component: RagChunkTasksPage,
meta: { title: '切片任务' },
},
],
children: routes.filter((route) => route.path !== '/:pathMatch(.*)*'),
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
name: 'not-found-shell',
component: NotFoundPage,
meta: { title: '页面不存在' },
},

File diff suppressed because it is too large Load Diff

View File

@@ -436,3 +436,14 @@
- 模型服务商与 Embedding 模型配置
- 检索配置
- 检索测试/召回预览
## 7. 与 Agent 调试链路的关联
当前 RAG 切片与向量数据已经被 Agent 调试页直接消费:
- Agent 调试接口 `POST /api/agents/{agentId}/chat``ragEnabled=true` 时会读取 `rag_chunk_embedding` 进行 TopK 召回。
- 若未完成切片向量化Agent 调试会返回“未召回到可用知识切片”。
关联文档:
- [Agent 页面后端接口清单](agent-page-apis.md)

View File

@@ -0,0 +1,20 @@
-- model_call_log 补丁脚本
-- 目的:对齐 BaseEntity 字段,避免 MyBatis 查询 create_by / update_by / update_time / version 报错
ALTER TABLE model_call_log
ADD COLUMN IF NOT EXISTS create_by VARCHAR(64);
ALTER TABLE model_call_log
ADD COLUMN IF NOT EXISTS update_by VARCHAR(64);
ALTER TABLE model_call_log
ADD COLUMN IF NOT EXISTS update_time TIMESTAMP;
ALTER TABLE model_call_log
ADD COLUMN IF NOT EXISTS version INTEGER NOT NULL DEFAULT 1;
COMMENT ON COLUMN model_call_log.create_by IS '创建者';
COMMENT ON COLUMN model_call_log.update_by IS '更新者';
COMMENT ON COLUMN model_call_log.update_time IS '更新时间';
COMMENT ON COLUMN model_call_log.version IS '版本';

View File

@@ -0,0 +1,34 @@
-- Studio 项目空间表。
CREATE TABLE IF NOT EXISTS studio_project (
id BIGSERIAL PRIMARY KEY,
project_code VARCHAR(100) NOT NULL,
project_name VARCHAR(200) NOT NULL,
environment VARCHAR(50) NOT NULL DEFAULT 'DEV',
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
current_version VARCHAR(50),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_studio_project_code UNIQUE (project_code)
);
CREATE INDEX IF NOT EXISTS idx_studio_project_environment ON studio_project (environment);
CREATE INDEX IF NOT EXISTS idx_studio_project_publish_status ON studio_project (publish_status);
COMMENT ON TABLE studio_project IS 'Studio项目空间表';
COMMENT ON COLUMN studio_project.id IS 'ID';
COMMENT ON COLUMN studio_project.project_code IS '项目编码';
COMMENT ON COLUMN studio_project.project_name IS '项目名称';
COMMENT ON COLUMN studio_project.environment IS '环境';
COMMENT ON COLUMN studio_project.publish_status IS '发布状态';
COMMENT ON COLUMN studio_project.current_version IS '当前发布版本';
COMMENT ON COLUMN studio_project.version IS '版本';
COMMENT ON COLUMN studio_project.create_time IS '创建时间';
COMMENT ON COLUMN studio_project.update_time IS '更新时间';
COMMENT ON COLUMN studio_project.remark IS '备注';
COMMENT ON COLUMN studio_project.create_by IS '创建者';
COMMENT ON COLUMN studio_project.update_by IS '更新者';

153
script/sql/12.workflow.sql Normal file
View File

@@ -0,0 +1,153 @@
-- Workflow 编排核心表。
CREATE TABLE IF NOT EXISTS workflow_definition (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT,
workflow_code VARCHAR(100) NOT NULL,
workflow_name VARCHAR(200) NOT NULL,
description VARCHAR(1000),
bound_agent_id BIGINT,
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_definition_code UNIQUE (workflow_code),
CONSTRAINT fk_workflow_definition_project_id FOREIGN KEY (project_id) REFERENCES studio_project (id),
CONSTRAINT fk_workflow_definition_agent_id FOREIGN KEY (bound_agent_id) REFERENCES agent_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_version (
id BIGSERIAL PRIMARY KEY,
workflow_id BIGINT NOT NULL,
version_no INTEGER NOT NULL,
snapshot_name VARCHAR(100) NOT NULL,
graph_json JSONB NOT NULL DEFAULT '{}'::jsonb,
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
published_time TIMESTAMP,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_version_no UNIQUE (workflow_id, version_no),
CONSTRAINT fk_workflow_version_workflow_id FOREIGN KEY (workflow_id) REFERENCES workflow_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_run (
id BIGSERIAL PRIMARY KEY,
request_id VARCHAR(64) NOT NULL,
workflow_id BIGINT,
workflow_version_id BIGINT,
agent_id BIGINT,
run_source VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL,
input_json JSONB NOT NULL DEFAULT '{}'::jsonb,
output_json JSONB NOT NULL DEFAULT '{}'::jsonb,
duration_ms INTEGER,
estimated_cost NUMERIC(14, 8),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_workflow_run_request_id UNIQUE (request_id),
CONSTRAINT fk_workflow_run_workflow_id FOREIGN KEY (workflow_id) REFERENCES workflow_definition (id),
CONSTRAINT fk_workflow_run_version_id FOREIGN KEY (workflow_version_id) REFERENCES workflow_version (id),
CONSTRAINT fk_workflow_run_agent_id FOREIGN KEY (agent_id) REFERENCES agent_definition (id)
);
CREATE TABLE IF NOT EXISTS workflow_run_step (
id BIGSERIAL PRIMARY KEY,
run_id BIGINT NOT NULL,
node_id VARCHAR(100) NOT NULL,
node_type VARCHAR(50) NOT NULL,
node_name VARCHAR(200),
status VARCHAR(50) NOT NULL,
input_json JSONB NOT NULL DEFAULT '{}'::jsonb,
output_json JSONB NOT NULL DEFAULT '{}'::jsonb,
duration_ms INTEGER,
error_message VARCHAR(1000),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT fk_workflow_run_step_run_id FOREIGN KEY (run_id) REFERENCES workflow_run (id)
);
CREATE INDEX IF NOT EXISTS idx_workflow_definition_project_id ON workflow_definition (project_id);
CREATE INDEX IF NOT EXISTS idx_workflow_definition_status ON workflow_definition (status);
CREATE INDEX IF NOT EXISTS idx_workflow_version_workflow_id ON workflow_version (workflow_id);
CREATE INDEX IF NOT EXISTS idx_workflow_run_workflow_id ON workflow_run (workflow_id);
CREATE INDEX IF NOT EXISTS idx_workflow_run_status ON workflow_run (status);
CREATE INDEX IF NOT EXISTS idx_workflow_run_step_run_id ON workflow_run_step (run_id);
COMMENT ON TABLE workflow_definition IS 'Workflow定义表';
COMMENT ON COLUMN workflow_definition.id IS 'ID';
COMMENT ON COLUMN workflow_definition.project_id IS '所属项目ID';
COMMENT ON COLUMN workflow_definition.workflow_code IS 'Workflow编码';
COMMENT ON COLUMN workflow_definition.workflow_name IS 'Workflow名称';
COMMENT ON COLUMN workflow_definition.description IS 'Workflow描述';
COMMENT ON COLUMN workflow_definition.bound_agent_id IS '绑定Agent ID';
COMMENT ON COLUMN workflow_definition.status IS '状态';
COMMENT ON COLUMN workflow_definition.version IS '版本';
COMMENT ON COLUMN workflow_definition.create_time IS '创建时间';
COMMENT ON COLUMN workflow_definition.update_time IS '更新时间';
COMMENT ON COLUMN workflow_definition.remark IS '备注';
COMMENT ON COLUMN workflow_definition.create_by IS '创建者';
COMMENT ON COLUMN workflow_definition.update_by IS '更新者';
COMMENT ON TABLE workflow_version IS 'Workflow版本快照表';
COMMENT ON COLUMN workflow_version.id IS 'ID';
COMMENT ON COLUMN workflow_version.workflow_id IS 'Workflow定义ID';
COMMENT ON COLUMN workflow_version.version_no IS '版本号';
COMMENT ON COLUMN workflow_version.snapshot_name IS '快照名称';
COMMENT ON COLUMN workflow_version.graph_json IS '编排图JSON';
COMMENT ON COLUMN workflow_version.publish_status IS '发布状态';
COMMENT ON COLUMN workflow_version.published_time IS '发布时间';
COMMENT ON COLUMN workflow_version.version IS '版本';
COMMENT ON COLUMN workflow_version.create_time IS '创建时间';
COMMENT ON COLUMN workflow_version.update_time IS '更新时间';
COMMENT ON COLUMN workflow_version.remark IS '备注';
COMMENT ON COLUMN workflow_version.create_by IS '创建者';
COMMENT ON COLUMN workflow_version.update_by IS '更新者';
COMMENT ON TABLE workflow_run IS 'Workflow运行记录表';
COMMENT ON COLUMN workflow_run.id IS 'ID';
COMMENT ON COLUMN workflow_run.request_id IS '请求唯一ID';
COMMENT ON COLUMN workflow_run.workflow_id IS 'Workflow定义ID';
COMMENT ON COLUMN workflow_run.workflow_version_id IS 'Workflow版本ID';
COMMENT ON COLUMN workflow_run.agent_id IS '执行Agent ID';
COMMENT ON COLUMN workflow_run.run_source IS '运行来源';
COMMENT ON COLUMN workflow_run.status IS '运行状态';
COMMENT ON COLUMN workflow_run.input_json IS '输入参数JSON';
COMMENT ON COLUMN workflow_run.output_json IS '输出结果JSON';
COMMENT ON COLUMN workflow_run.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN workflow_run.estimated_cost IS '预估成本';
COMMENT ON COLUMN workflow_run.version IS '版本';
COMMENT ON COLUMN workflow_run.create_time IS '创建时间';
COMMENT ON COLUMN workflow_run.update_time IS '更新时间';
COMMENT ON COLUMN workflow_run.remark IS '备注';
COMMENT ON COLUMN workflow_run.create_by IS '创建者';
COMMENT ON COLUMN workflow_run.update_by IS '更新者';
COMMENT ON TABLE workflow_run_step IS 'Workflow运行步骤表';
COMMENT ON COLUMN workflow_run_step.id IS 'ID';
COMMENT ON COLUMN workflow_run_step.run_id IS '运行记录ID';
COMMENT ON COLUMN workflow_run_step.node_id IS '节点ID';
COMMENT ON COLUMN workflow_run_step.node_type IS '节点类型';
COMMENT ON COLUMN workflow_run_step.node_name IS '节点名称';
COMMENT ON COLUMN workflow_run_step.status IS '步骤状态';
COMMENT ON COLUMN workflow_run_step.input_json IS '步骤输入JSON';
COMMENT ON COLUMN workflow_run_step.output_json IS '步骤输出JSON';
COMMENT ON COLUMN workflow_run_step.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN workflow_run_step.error_message IS '错误信息';
COMMENT ON COLUMN workflow_run_step.version IS '版本';
COMMENT ON COLUMN workflow_run_step.create_time IS '创建时间';
COMMENT ON COLUMN workflow_run_step.update_time IS '更新时间';
COMMENT ON COLUMN workflow_run_step.remark IS '备注';
COMMENT ON COLUMN workflow_run_step.create_by IS '创建者';
COMMENT ON COLUMN workflow_run_step.update_by IS '更新者';

View File

@@ -0,0 +1,69 @@
-- Agent 会话与消息表。
CREATE TABLE IF NOT EXISTS agent_session (
id BIGSERIAL PRIMARY KEY,
session_code VARCHAR(100) NOT NULL,
agent_id BIGINT NOT NULL,
workflow_run_id BIGINT,
title VARCHAR(200),
status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
metadata_json JSONB NOT NULL DEFAULT '{}'::jsonb,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_session_code UNIQUE (session_code),
CONSTRAINT fk_agent_session_agent_id FOREIGN KEY (agent_id) REFERENCES agent_definition (id),
CONSTRAINT fk_agent_session_run_id FOREIGN KEY (workflow_run_id) REFERENCES workflow_run (id)
);
CREATE TABLE IF NOT EXISTS agent_message (
id BIGSERIAL PRIMARY KEY,
session_id BIGINT NOT NULL,
role VARCHAR(50) NOT NULL,
content TEXT NOT NULL,
citation_json JSONB NOT NULL DEFAULT '[]'::jsonb,
token_count INTEGER,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT fk_agent_message_session_id FOREIGN KEY (session_id) REFERENCES agent_session (id)
);
CREATE INDEX IF NOT EXISTS idx_agent_session_agent_id ON agent_session (agent_id);
CREATE INDEX IF NOT EXISTS idx_agent_session_status ON agent_session (status);
CREATE INDEX IF NOT EXISTS idx_agent_message_session_id ON agent_message (session_id);
CREATE INDEX IF NOT EXISTS idx_agent_message_role ON agent_message (role);
COMMENT ON TABLE agent_session IS 'Agent会话表';
COMMENT ON COLUMN agent_session.id IS 'ID';
COMMENT ON COLUMN agent_session.session_code IS '会话编码';
COMMENT ON COLUMN agent_session.agent_id IS 'Agent ID';
COMMENT ON COLUMN agent_session.workflow_run_id IS '来源Workflow运行ID';
COMMENT ON COLUMN agent_session.title IS '会话标题';
COMMENT ON COLUMN agent_session.status IS '会话状态';
COMMENT ON COLUMN agent_session.metadata_json IS '会话元数据JSON';
COMMENT ON COLUMN agent_session.version IS '版本';
COMMENT ON COLUMN agent_session.create_time IS '创建时间';
COMMENT ON COLUMN agent_session.update_time IS '更新时间';
COMMENT ON COLUMN agent_session.remark IS '备注';
COMMENT ON COLUMN agent_session.create_by IS '创建者';
COMMENT ON COLUMN agent_session.update_by IS '更新者';
COMMENT ON TABLE agent_message IS 'Agent消息表';
COMMENT ON COLUMN agent_message.id IS 'ID';
COMMENT ON COLUMN agent_message.session_id IS '会话ID';
COMMENT ON COLUMN agent_message.role IS '消息角色';
COMMENT ON COLUMN agent_message.content IS '消息内容';
COMMENT ON COLUMN agent_message.citation_json IS '引用信息JSON';
COMMENT ON COLUMN agent_message.token_count IS 'Token数量';
COMMENT ON COLUMN agent_message.version IS '版本';
COMMENT ON COLUMN agent_message.create_time IS '创建时间';
COMMENT ON COLUMN agent_message.update_time IS '更新时间';
COMMENT ON COLUMN agent_message.remark IS '备注';
COMMENT ON COLUMN agent_message.create_by IS '创建者';
COMMENT ON COLUMN agent_message.update_by IS '更新者';

78
script/sql/14.mcp.sql Normal file
View File

@@ -0,0 +1,78 @@
-- MCP 服务与能力表。
CREATE TABLE IF NOT EXISTS mcp_server (
id BIGSERIAL PRIMARY KEY,
server_code VARCHAR(100) NOT NULL,
server_name VARCHAR(200) NOT NULL,
import_type VARCHAR(50) NOT NULL,
endpoint_url VARCHAR(500),
package_name VARCHAR(200),
manifest_json JSONB NOT NULL DEFAULT '{}'::jsonb,
auth_type VARCHAR(50),
secret_ref VARCHAR(200),
health_status VARCHAR(50) NOT NULL DEFAULT 'UNKNOWN',
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_mcp_server_code UNIQUE (server_code)
);
CREATE TABLE IF NOT EXISTS mcp_capability (
id BIGSERIAL PRIMARY KEY,
server_id BIGINT NOT NULL,
capability_code VARCHAR(150) NOT NULL,
capability_name VARCHAR(200) NOT NULL,
capability_type VARCHAR(50) NOT NULL,
schema_json JSONB NOT NULL DEFAULT '{}'::jsonb,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_mcp_capability_code UNIQUE (server_id, capability_code),
CONSTRAINT fk_mcp_capability_server_id FOREIGN KEY (server_id) REFERENCES mcp_server (id)
);
CREATE INDEX IF NOT EXISTS idx_mcp_server_import_type ON mcp_server (import_type);
CREATE INDEX IF NOT EXISTS idx_mcp_server_health_status ON mcp_server (health_status);
CREATE INDEX IF NOT EXISTS idx_mcp_capability_server_id ON mcp_capability (server_id);
CREATE INDEX IF NOT EXISTS idx_mcp_capability_type ON mcp_capability (capability_type);
COMMENT ON TABLE mcp_server IS 'MCP服务表';
COMMENT ON COLUMN mcp_server.id IS 'ID';
COMMENT ON COLUMN mcp_server.server_code IS '服务编码';
COMMENT ON COLUMN mcp_server.server_name IS '服务名称';
COMMENT ON COLUMN mcp_server.import_type IS '导入方式';
COMMENT ON COLUMN mcp_server.endpoint_url IS '服务端点地址';
COMMENT ON COLUMN mcp_server.package_name IS '安装包名称';
COMMENT ON COLUMN mcp_server.manifest_json IS 'Manifest声明JSON';
COMMENT ON COLUMN mcp_server.auth_type IS '鉴权类型';
COMMENT ON COLUMN mcp_server.secret_ref IS '密钥引用';
COMMENT ON COLUMN mcp_server.health_status IS '健康状态';
COMMENT ON COLUMN mcp_server.enabled IS '是否启用';
COMMENT ON COLUMN mcp_server.version IS '版本';
COMMENT ON COLUMN mcp_server.create_time IS '创建时间';
COMMENT ON COLUMN mcp_server.update_time IS '更新时间';
COMMENT ON COLUMN mcp_server.remark IS '备注';
COMMENT ON COLUMN mcp_server.create_by IS '创建者';
COMMENT ON COLUMN mcp_server.update_by IS '更新者';
COMMENT ON TABLE mcp_capability IS 'MCP能力表';
COMMENT ON COLUMN mcp_capability.id IS 'ID';
COMMENT ON COLUMN mcp_capability.server_id IS '所属服务ID';
COMMENT ON COLUMN mcp_capability.capability_code IS '能力编码';
COMMENT ON COLUMN mcp_capability.capability_name IS '能力名称';
COMMENT ON COLUMN mcp_capability.capability_type IS '能力类型';
COMMENT ON COLUMN mcp_capability.schema_json IS '能力Schema JSON';
COMMENT ON COLUMN mcp_capability.enabled IS '是否启用';
COMMENT ON COLUMN mcp_capability.version IS '版本';
COMMENT ON COLUMN mcp_capability.create_time IS '创建时间';
COMMENT ON COLUMN mcp_capability.update_time IS '更新时间';
COMMENT ON COLUMN mcp_capability.remark IS '备注';
COMMENT ON COLUMN mcp_capability.create_by IS '创建者';
COMMENT ON COLUMN mcp_capability.update_by IS '更新者';

74
script/sql/15.skill.sql Normal file
View File

@@ -0,0 +1,74 @@
-- Skill 定义与版本表。
CREATE TABLE IF NOT EXISTS skill_definition (
id BIGSERIAL PRIMARY KEY,
skill_code VARCHAR(100) NOT NULL,
skill_name VARCHAR(200) NOT NULL,
skill_type VARCHAR(50) NOT NULL,
description VARCHAR(1000),
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_skill_definition_code UNIQUE (skill_code)
);
CREATE TABLE IF NOT EXISTS skill_version (
id BIGSERIAL PRIMARY KEY,
skill_id BIGINT NOT NULL,
version_no INTEGER NOT NULL,
prompt_text TEXT,
code_text TEXT,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
variable_schema_json JSONB NOT NULL DEFAULT '{}'::jsonb,
test_result_json JSONB NOT NULL DEFAULT '{}'::jsonb,
publish_status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
published_time TIMESTAMP,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_skill_version_no UNIQUE (skill_id, version_no),
CONSTRAINT fk_skill_version_skill_id FOREIGN KEY (skill_id) REFERENCES skill_definition (id)
);
CREATE INDEX IF NOT EXISTS idx_skill_definition_status ON skill_definition (status);
CREATE INDEX IF NOT EXISTS idx_skill_definition_type ON skill_definition (skill_type);
CREATE INDEX IF NOT EXISTS idx_skill_version_skill_id ON skill_version (skill_id);
CREATE INDEX IF NOT EXISTS idx_skill_version_publish_status ON skill_version (publish_status);
COMMENT ON TABLE skill_definition IS 'Skill定义表';
COMMENT ON COLUMN skill_definition.id IS 'ID';
COMMENT ON COLUMN skill_definition.skill_code IS 'Skill编码';
COMMENT ON COLUMN skill_definition.skill_name IS 'Skill名称';
COMMENT ON COLUMN skill_definition.skill_type IS 'Skill类型';
COMMENT ON COLUMN skill_definition.description IS 'Skill描述';
COMMENT ON COLUMN skill_definition.status IS '状态';
COMMENT ON COLUMN skill_definition.version IS '版本';
COMMENT ON COLUMN skill_definition.create_time IS '创建时间';
COMMENT ON COLUMN skill_definition.update_time IS '更新时间';
COMMENT ON COLUMN skill_definition.remark IS '备注';
COMMENT ON COLUMN skill_definition.create_by IS '创建者';
COMMENT ON COLUMN skill_definition.update_by IS '更新者';
COMMENT ON TABLE skill_version IS 'Skill版本表';
COMMENT ON COLUMN skill_version.id IS 'ID';
COMMENT ON COLUMN skill_version.skill_id IS 'Skill定义ID';
COMMENT ON COLUMN skill_version.version_no IS '版本号';
COMMENT ON COLUMN skill_version.prompt_text IS '提示词内容';
COMMENT ON COLUMN skill_version.code_text IS '代码内容';
COMMENT ON COLUMN skill_version.config_json IS '运行配置JSON';
COMMENT ON COLUMN skill_version.variable_schema_json IS '变量Schema JSON';
COMMENT ON COLUMN skill_version.test_result_json IS '测试结果JSON';
COMMENT ON COLUMN skill_version.publish_status IS '发布状态';
COMMENT ON COLUMN skill_version.published_time IS '发布时间';
COMMENT ON COLUMN skill_version.version IS '版本';
COMMENT ON COLUMN skill_version.create_time IS '创建时间';
COMMENT ON COLUMN skill_version.update_time IS '更新时间';
COMMENT ON COLUMN skill_version.remark IS '备注';
COMMENT ON COLUMN skill_version.create_by IS '创建者';
COMMENT ON COLUMN skill_version.update_by IS '更新者';

View File

@@ -0,0 +1,36 @@
-- Agent 与 Workflow 能力绑定表。
CREATE TABLE IF NOT EXISTS agent_capability_binding (
id BIGSERIAL PRIMARY KEY,
owner_type VARCHAR(50) NOT NULL,
owner_id BIGINT NOT NULL,
capability_type VARCHAR(50) NOT NULL,
capability_id BIGINT NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_capability_binding UNIQUE (owner_type, owner_id, capability_type, capability_id)
);
CREATE INDEX IF NOT EXISTS idx_agent_capability_owner ON agent_capability_binding (owner_type, owner_id);
CREATE INDEX IF NOT EXISTS idx_agent_capability_type ON agent_capability_binding (capability_type, capability_id);
COMMENT ON TABLE agent_capability_binding IS 'Agent能力绑定表';
COMMENT ON COLUMN agent_capability_binding.id IS 'ID';
COMMENT ON COLUMN agent_capability_binding.owner_type IS '绑定主体类型';
COMMENT ON COLUMN agent_capability_binding.owner_id IS '绑定主体ID';
COMMENT ON COLUMN agent_capability_binding.capability_type IS '能力类型';
COMMENT ON COLUMN agent_capability_binding.capability_id IS '能力ID';
COMMENT ON COLUMN agent_capability_binding.enabled IS '是否启用';
COMMENT ON COLUMN agent_capability_binding.config_json IS '绑定配置JSON';
COMMENT ON COLUMN agent_capability_binding.version IS '版本';
COMMENT ON COLUMN agent_capability_binding.create_time IS '创建时间';
COMMENT ON COLUMN agent_capability_binding.update_time IS '更新时间';
COMMENT ON COLUMN agent_capability_binding.remark IS '备注';
COMMENT ON COLUMN agent_capability_binding.create_by IS '创建者';
COMMENT ON COLUMN agent_capability_binding.update_by IS '更新者';

View File

@@ -0,0 +1,60 @@
-- Studio 相关枚举初始化。
-- 保持 sys_enum 现有 catalog/type/name/value/strvalue/sort/remark 格式不变。
INSERT INTO sys_enum (catalog, type, name, value, strvalue, sort, version, remark)
VALUES
('studio', 'environment', '开发环境', 1, 'DEV', 1, 1, 'Studio环境'),
('studio', 'environment', '测试环境', 2, 'TEST', 2, 1, 'Studio环境'),
('studio', 'environment', '生产环境', 3, 'PROD', 3, 1, 'Studio环境'),
('studio', 'publish_status', '草稿', 1, 'DRAFT', 1, 1, '发布状态'),
('studio', 'publish_status', '已发布', 2, 'PUBLISHED', 2, 1, '发布状态'),
('studio', 'publish_status', '已归档', 3, 'ARCHIVED', 3, 1, '发布状态'),
('workflow', 'status', '草稿', 1, 'DRAFT', 1, 1, 'Workflow状态'),
('workflow', 'status', '启用', 2, 'ENABLED', 2, 1, 'Workflow状态'),
('workflow', 'status', '停用', 3, 'DISABLED', 3, 1, 'Workflow状态'),
('workflow', 'run_status', '排队中', 1, 'QUEUED', 1, 1, 'Workflow运行状态'),
('workflow', 'run_status', '运行中', 2, 'RUNNING', 2, 1, 'Workflow运行状态'),
('workflow', 'run_status', '成功', 3, 'SUCCESS', 3, 1, 'Workflow运行状态'),
('workflow', 'run_status', '失败', 4, 'FAILED', 4, 1, 'Workflow运行状态'),
('workflow', 'run_status', '取消', 5, 'CANCELED', 5, 1, 'Workflow运行状态'),
('workflow', 'node_type', '开始', 1, 'START', 1, 1, 'Workflow节点类型'),
('workflow', 'node_type', '大模型', 2, 'LLM', 2, 1, 'Workflow节点类型'),
('workflow', 'node_type', '知识检索', 3, 'KNOWLEDGE_RETRIEVAL', 3, 1, 'Workflow节点类型'),
('workflow', 'node_type', 'MCP工具', 4, 'MCP_TOOL', 4, 1, 'Workflow节点类型'),
('workflow', 'node_type', 'Skill', 5, 'SKILL', 5, 1, 'Workflow节点类型'),
('workflow', 'node_type', '条件', 6, 'CONDITION', 6, 1, 'Workflow节点类型'),
('workflow', 'node_type', '回答', 7, 'ANSWER', 7, 1, 'Workflow节点类型'),
('agent', 'session_status', '活跃', 1, 'ACTIVE', 1, 1, 'Agent会话状态'),
('agent', 'session_status', '已关闭', 2, 'CLOSED', 2, 1, 'Agent会话状态'),
('agent', 'session_status', '异常终止', 3, 'FAILED', 3, 1, 'Agent会话状态'),
('agent', 'message_role', '用户', 1, 'USER', 1, 1, 'Agent消息角色'),
('agent', 'message_role', '助手', 2, 'ASSISTANT', 2, 1, 'Agent消息角色'),
('agent', 'message_role', '系统', 3, 'SYSTEM', 3, 1, 'Agent消息角色'),
('agent', 'message_role', '工具', 4, 'TOOL', 4, 1, 'Agent消息角色'),
('mcp', 'import_type', 'URL导入', 1, 'URL', 1, 1, 'MCP导入方式'),
('mcp', 'import_type', 'npm包导入', 2, 'NPM_PACKAGE', 2, 1, 'MCP导入方式'),
('mcp', 'import_type', 'JSON声明导入', 3, 'JSON_MANIFEST', 3, 1, 'MCP导入方式'),
('mcp', 'capability_type', '工具', 1, 'TOOL', 1, 1, 'MCP能力类型'),
('mcp', 'capability_type', '资源', 2, 'RESOURCE', 2, 1, 'MCP能力类型'),
('mcp', 'capability_type', '提示词', 3, 'PROMPT', 3, 1, 'MCP能力类型'),
('mcp', 'health_status', '未知', 1, 'UNKNOWN', 1, 1, 'MCP健康状态'),
('mcp', 'health_status', '健康', 2, 'HEALTHY', 2, 1, 'MCP健康状态'),
('mcp', 'health_status', '异常', 3, 'UNHEALTHY', 3, 1, 'MCP健康状态'),
('skill', 'skill_type', '提示词', 1, 'PROMPT', 1, 1, 'Skill类型'),
('skill', 'skill_type', '代码', 2, 'CODE', 2, 1, 'Skill类型'),
('skill', 'skill_type', '混合', 3, 'MIXED', 3, 1, 'Skill类型'),
('skill', 'status', '草稿', 1, 'DRAFT', 1, 1, 'Skill状态'),
('skill', 'status', '已发布', 2, 'PUBLISHED', 2, 1, 'Skill状态'),
('skill', 'status', '已归档', 3, 'ARCHIVED', 3, 1, 'Skill状态'),
('agent', 'capability_owner_type', 'Agent', 1, 'AGENT', 1, 1, '能力绑定主体类型'),
('agent', 'capability_owner_type', 'Workflow', 2, 'WORKFLOW', 2, 1, '能力绑定主体类型'),
('agent', 'capability_type', '知识库', 1, 'RAG_STORE', 1, 1, '能力绑定能力类型'),
('agent', 'capability_type', 'MCP能力', 2, 'MCP_CAPABILITY', 2, 1, '能力绑定能力类型'),
('agent', 'capability_type', 'Skill', 3, 'SKILL', 3, 1, '能力绑定能力类型')
ON CONFLICT (catalog, type, name)
DO UPDATE SET
value = EXCLUDED.value,
strvalue = EXCLUDED.strvalue,
sort = EXCLUDED.sort,
remark = EXCLUDED.remark,
update_time = CURRENT_TIMESTAMP;

View File

@@ -0,0 +1,35 @@
DROP TABLE IF EXISTS agent_definition;
CREATE TABLE agent_definition (
id BIGSERIAL PRIMARY KEY,
agent_code VARCHAR(100) NOT NULL,
agent_name VARCHAR(200) NOT NULL,
system_prompt TEXT,
store_id BIGINT NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'ENABLED',
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_agent_definition_code UNIQUE (agent_code),
CONSTRAINT fk_agent_definition_store_id FOREIGN KEY (store_id) REFERENCES rag_store (id)
);
CREATE INDEX idx_agent_definition_store_id ON agent_definition (store_id);
CREATE INDEX idx_agent_definition_status ON agent_definition (status);
COMMENT ON TABLE agent_definition IS 'Agent定义表';
COMMENT ON COLUMN agent_definition.id IS 'ID';
COMMENT ON COLUMN agent_definition.agent_code IS 'Agent编码';
COMMENT ON COLUMN agent_definition.agent_name IS 'Agent名称';
COMMENT ON COLUMN agent_definition.system_prompt IS '系统提示词';
COMMENT ON COLUMN agent_definition.store_id IS '绑定知识库ID';
COMMENT ON COLUMN agent_definition.status IS '状态';
COMMENT ON COLUMN agent_definition.version IS '版本';
COMMENT ON COLUMN agent_definition.create_time IS '创建时间';
COMMENT ON COLUMN agent_definition.update_time IS '更新时间';
COMMENT ON COLUMN agent_definition.remark IS '备注';
COMMENT ON COLUMN agent_definition.create_by IS '创建者';
COMMENT ON COLUMN agent_definition.update_by IS '更新者';

View File

@@ -0,0 +1,239 @@
-- 模型平台核心表。
-- 与 docs/MODEL_PROVIDER_SCHEMA.sql 保持一致,作为后续落库执行入口。
CREATE TABLE IF NOT EXISTS model_provider (
id BIGSERIAL PRIMARY KEY,
provider_code VARCHAR(64) NOT NULL,
provider_name VARCHAR(100) NOT NULL,
provider_type VARCHAR(50) NOT NULL,
protocol_type VARCHAR(50) NOT NULL DEFAULT 'OPENAI_COMPATIBLE',
base_url VARCHAR(500) NOT NULL,
auth_type VARCHAR(50) NOT NULL DEFAULT 'BEARER_TOKEN',
secret_ref VARCHAR(200),
api_key_cipher TEXT,
timeout_ms INTEGER NOT NULL DEFAULT 60000,
priority INTEGER NOT NULL DEFAULT 100,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
health_status VARCHAR(50) NOT NULL DEFAULT 'UNKNOWN',
last_health_check_time TIMESTAMP,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_model_provider_code UNIQUE (provider_code)
);
CREATE TABLE IF NOT EXISTS model_config (
id BIGSERIAL PRIMARY KEY,
provider_id BIGINT NOT NULL,
model_code VARCHAR(100) NOT NULL,
model_name VARCHAR(200) NOT NULL,
upstream_model VARCHAR(200) NOT NULL,
model_type VARCHAR(50) NOT NULL,
context_window INTEGER,
max_output_tokens INTEGER,
embedding_dimension INTEGER,
input_price_per_1k NUMERIC(12, 8),
output_price_per_1k NUMERIC(12, 8),
local_model BOOLEAN NOT NULL DEFAULT FALSE,
default_model BOOLEAN NOT NULL DEFAULT FALSE,
capabilities_json JSONB NOT NULL DEFAULT '{}'::jsonb,
options_json JSONB NOT NULL DEFAULT '{}'::jsonb,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_model_config_provider_code UNIQUE (provider_id, model_code),
CONSTRAINT fk_model_config_provider_id FOREIGN KEY (provider_id) REFERENCES model_provider (id)
);
CREATE TABLE IF NOT EXISTS model_route_rule (
id BIGSERIAL PRIMARY KEY,
route_code VARCHAR(100) NOT NULL,
route_name VARCHAR(100) NOT NULL,
task_type VARCHAR(50) NOT NULL,
match_scope VARCHAR(50) NOT NULL DEFAULT 'GLOBAL',
scope_id BIGINT,
primary_model_id BIGINT NOT NULL,
fallback_model_ids_json JSONB NOT NULL DEFAULT '[]'::jsonb,
route_strategy VARCHAR(50) NOT NULL DEFAULT 'MANUAL',
max_latency_ms INTEGER,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_model_route_rule_code UNIQUE (route_code),
CONSTRAINT fk_model_route_primary_model_id FOREIGN KEY (primary_model_id) REFERENCES model_config (id)
);
CREATE TABLE IF NOT EXISTS rag_store_model_config (
id BIGSERIAL PRIMARY KEY,
store_id BIGINT NOT NULL,
embedding_model_id BIGINT NOT NULL,
embedding_dimension INTEGER NOT NULL DEFAULT 1024,
chunk_strategy INTEGER,
chunk_size INTEGER,
chunk_overlap INTEGER,
delimiter VARCHAR(50),
active BOOLEAN NOT NULL DEFAULT TRUE,
index_version INTEGER NOT NULL DEFAULT 1,
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_rag_store_model_config_store_active UNIQUE (store_id, active),
CONSTRAINT fk_rag_store_model_config_store_id FOREIGN KEY (store_id) REFERENCES rag_store (id),
CONSTRAINT fk_rag_store_model_config_embedding_model_id FOREIGN KEY (embedding_model_id) REFERENCES model_config (id)
);
CREATE TABLE IF NOT EXISTS model_call_log (
id BIGSERIAL PRIMARY KEY,
request_id VARCHAR(64) NOT NULL,
provider_id BIGINT,
model_id BIGINT,
task_type VARCHAR(50) NOT NULL,
biz_type VARCHAR(50),
biz_id VARCHAR(100),
call_type VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL,
prompt_tokens INTEGER,
completion_tokens INTEGER,
total_tokens INTEGER,
estimated_cost NUMERIC(14, 8),
duration_ms INTEGER,
request_hash VARCHAR(64),
error_code VARCHAR(100),
error_message VARCHAR(1000),
version INTEGER NOT NULL DEFAULT 1,
create_time TIMESTAMP,
update_time TIMESTAMP,
remark VARCHAR(500) DEFAULT '',
create_by VARCHAR(64),
update_by VARCHAR(64),
CONSTRAINT uk_model_call_log_request_id UNIQUE (request_id)
);
CREATE INDEX IF NOT EXISTS idx_model_config_provider_id ON model_config (provider_id);
CREATE INDEX IF NOT EXISTS idx_model_config_model_type ON model_config (model_type);
CREATE INDEX IF NOT EXISTS idx_model_route_task_scope ON model_route_rule (task_type, match_scope, scope_id);
CREATE INDEX IF NOT EXISTS idx_rag_store_model_config_store_id ON rag_store_model_config (store_id);
CREATE INDEX IF NOT EXISTS idx_model_call_log_request_id ON model_call_log (request_id);
CREATE INDEX IF NOT EXISTS idx_model_call_log_task_type ON model_call_log (task_type);
COMMENT ON TABLE model_provider IS '模型服务商配置表';
COMMENT ON COLUMN model_provider.id IS 'ID';
COMMENT ON COLUMN model_provider.provider_code IS '服务商编码';
COMMENT ON COLUMN model_provider.provider_name IS '服务商名称';
COMMENT ON COLUMN model_provider.provider_type IS '服务商类型';
COMMENT ON COLUMN model_provider.protocol_type IS '协议类型';
COMMENT ON COLUMN model_provider.base_url IS '服务基础地址';
COMMENT ON COLUMN model_provider.auth_type IS '鉴权类型';
COMMENT ON COLUMN model_provider.secret_ref IS '密钥环境变量引用';
COMMENT ON COLUMN model_provider.api_key_cipher IS '密钥密文';
COMMENT ON COLUMN model_provider.timeout_ms IS '超时时间(毫秒)';
COMMENT ON COLUMN model_provider.priority IS '优先级';
COMMENT ON COLUMN model_provider.enabled IS '是否启用';
COMMENT ON COLUMN model_provider.health_status IS '健康状态';
COMMENT ON COLUMN model_provider.last_health_check_time IS '最近健康检查时间';
COMMENT ON COLUMN model_provider.version IS '版本';
COMMENT ON COLUMN model_provider.create_time IS '创建时间';
COMMENT ON COLUMN model_provider.update_time IS '更新时间';
COMMENT ON COLUMN model_provider.remark IS '备注';
COMMENT ON COLUMN model_provider.create_by IS '创建者';
COMMENT ON COLUMN model_provider.update_by IS '更新者';
COMMENT ON TABLE model_config IS '模型配置表';
COMMENT ON COLUMN model_config.id IS 'ID';
COMMENT ON COLUMN model_config.provider_id IS '服务商ID';
COMMENT ON COLUMN model_config.model_code IS '模型编码';
COMMENT ON COLUMN model_config.model_name IS '模型名称';
COMMENT ON COLUMN model_config.upstream_model IS '上游模型名称';
COMMENT ON COLUMN model_config.model_type IS '模型类型';
COMMENT ON COLUMN model_config.context_window IS '上下文窗口大小';
COMMENT ON COLUMN model_config.max_output_tokens IS '最大输出Token数';
COMMENT ON COLUMN model_config.embedding_dimension IS '向量维度';
COMMENT ON COLUMN model_config.input_price_per_1k IS '输入千Token单价';
COMMENT ON COLUMN model_config.output_price_per_1k IS '输出千Token单价';
COMMENT ON COLUMN model_config.local_model IS '是否本地模型';
COMMENT ON COLUMN model_config.default_model IS '是否默认模型';
COMMENT ON COLUMN model_config.capabilities_json IS '能力配置JSON';
COMMENT ON COLUMN model_config.options_json IS '扩展选项JSON';
COMMENT ON COLUMN model_config.enabled IS '是否启用';
COMMENT ON COLUMN model_config.version IS '版本';
COMMENT ON COLUMN model_config.create_time IS '创建时间';
COMMENT ON COLUMN model_config.update_time IS '更新时间';
COMMENT ON COLUMN model_config.remark IS '备注';
COMMENT ON COLUMN model_config.create_by IS '创建者';
COMMENT ON COLUMN model_config.update_by IS '更新者';
COMMENT ON TABLE model_route_rule IS '模型路由规则表';
COMMENT ON COLUMN model_route_rule.id IS 'ID';
COMMENT ON COLUMN model_route_rule.route_code IS '路由规则编码';
COMMENT ON COLUMN model_route_rule.route_name IS '路由规则名称';
COMMENT ON COLUMN model_route_rule.task_type IS '任务类型';
COMMENT ON COLUMN model_route_rule.match_scope IS '匹配范围';
COMMENT ON COLUMN model_route_rule.scope_id IS '匹配范围业务ID';
COMMENT ON COLUMN model_route_rule.primary_model_id IS '主模型ID';
COMMENT ON COLUMN model_route_rule.fallback_model_ids_json IS '降级模型ID列表JSON';
COMMENT ON COLUMN model_route_rule.route_strategy IS '路由策略';
COMMENT ON COLUMN model_route_rule.max_latency_ms IS '最大延迟限制(毫秒)';
COMMENT ON COLUMN model_route_rule.enabled IS '是否启用';
COMMENT ON COLUMN model_route_rule.version IS '版本';
COMMENT ON COLUMN model_route_rule.create_time IS '创建时间';
COMMENT ON COLUMN model_route_rule.update_time IS '更新时间';
COMMENT ON COLUMN model_route_rule.remark IS '备注';
COMMENT ON COLUMN model_route_rule.create_by IS '创建者';
COMMENT ON COLUMN model_route_rule.update_by IS '更新者';
COMMENT ON TABLE rag_store_model_config IS '知识库模型配置表';
COMMENT ON COLUMN rag_store_model_config.id IS 'ID';
COMMENT ON COLUMN rag_store_model_config.store_id IS '知识库ID';
COMMENT ON COLUMN rag_store_model_config.embedding_model_id IS 'Embedding模型ID';
COMMENT ON COLUMN rag_store_model_config.embedding_dimension IS '向量维度';
COMMENT ON COLUMN rag_store_model_config.chunk_strategy IS '切片策略';
COMMENT ON COLUMN rag_store_model_config.chunk_size IS '切片大小';
COMMENT ON COLUMN rag_store_model_config.chunk_overlap IS '切片重叠大小';
COMMENT ON COLUMN rag_store_model_config.delimiter IS '切片分隔符';
COMMENT ON COLUMN rag_store_model_config.active IS '是否生效';
COMMENT ON COLUMN rag_store_model_config.index_version IS '索引版本号';
COMMENT ON COLUMN rag_store_model_config.version IS '版本';
COMMENT ON COLUMN rag_store_model_config.create_time IS '创建时间';
COMMENT ON COLUMN rag_store_model_config.update_time IS '更新时间';
COMMENT ON COLUMN rag_store_model_config.remark IS '备注';
COMMENT ON COLUMN rag_store_model_config.create_by IS '创建者';
COMMENT ON COLUMN rag_store_model_config.update_by IS '更新者';
COMMENT ON TABLE model_call_log IS '模型调用日志表';
COMMENT ON COLUMN model_call_log.id IS 'ID';
COMMENT ON COLUMN model_call_log.request_id IS '请求唯一ID';
COMMENT ON COLUMN model_call_log.provider_id IS '服务商ID';
COMMENT ON COLUMN model_call_log.model_id IS '模型ID';
COMMENT ON COLUMN model_call_log.task_type IS '任务类型';
COMMENT ON COLUMN model_call_log.biz_type IS '业务类型';
COMMENT ON COLUMN model_call_log.biz_id IS '业务ID';
COMMENT ON COLUMN model_call_log.call_type IS '调用类型';
COMMENT ON COLUMN model_call_log.status IS '调用状态';
COMMENT ON COLUMN model_call_log.prompt_tokens IS '输入Token数';
COMMENT ON COLUMN model_call_log.completion_tokens IS '输出Token数';
COMMENT ON COLUMN model_call_log.total_tokens IS '总Token数';
COMMENT ON COLUMN model_call_log.estimated_cost IS '预估成本';
COMMENT ON COLUMN model_call_log.duration_ms IS '耗时(毫秒)';
COMMENT ON COLUMN model_call_log.request_hash IS '请求哈希';
COMMENT ON COLUMN model_call_log.error_code IS '错误码';
COMMENT ON COLUMN model_call_log.error_message IS '错误信息摘要';
COMMENT ON COLUMN model_call_log.version IS '版本';
COMMENT ON COLUMN model_call_log.create_time IS '创建时间';
COMMENT ON COLUMN model_call_log.update_time IS '更新时间';
COMMENT ON COLUMN model_call_log.remark IS '备注';
COMMENT ON COLUMN model_call_log.create_by IS '创建者';
COMMENT ON COLUMN model_call_log.update_by IS '更新者';

View File

@@ -0,0 +1,58 @@
package com.bruce.agent.controller;
import com.bruce.agent.dto.request.AgentChatRequest;
import com.bruce.agent.dto.request.AgentDefinitionQueryRequest;
import com.bruce.agent.dto.request.AgentDefinitionSaveRequest;
import com.bruce.agent.dto.response.AgentChatResponse;
import com.bruce.agent.dto.response.AgentDefinitionResponse;
import com.bruce.agent.service.IAgentDefinitionService;
import com.bruce.common.domain.model.RequestResult;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/agents")
@RequiredArgsConstructor
public class AgentDefinitionController {
private final IAgentDefinitionService agentDefinitionService;
@PostMapping("/list")
public RequestResult<List<AgentDefinitionResponse>> list() {
return RequestResult.success(agentDefinitionService.listResponses());
}
@PostMapping("/query")
public RequestResult<List<AgentDefinitionResponse>> query(@RequestBody(required = false) AgentDefinitionQueryRequest request) {
return RequestResult.success(agentDefinitionService.query(request));
}
@GetMapping("/detail")
public RequestResult<AgentDefinitionResponse> detail(@RequestParam("id") Long id) {
return RequestResult.success(agentDefinitionService.getResponseById(id));
}
@PostMapping("/save")
public RequestResult<Boolean> save(@RequestBody AgentDefinitionSaveRequest request) {
return RequestResult.success(agentDefinitionService.saveOrUpdate(request));
}
@PostMapping("/delete")
public RequestResult<Boolean> delete(@RequestParam("id") Long id) {
return RequestResult.success(agentDefinitionService.removeById(id));
}
@PostMapping("/{agentId}/chat")
public RequestResult<AgentChatResponse> chat(@PathVariable("agentId") Long agentId,
@RequestBody AgentChatRequest request) {
return RequestResult.success(agentDefinitionService.chat(agentId, request));
}
}

View File

@@ -0,0 +1,17 @@
package com.bruce.agent.dto.request;
import lombok.Data;
import java.util.List;
@Data
public class AgentChatRequest {
private List<AgentMessage> messages;
private Boolean ragEnabled;
@Data
public static class AgentMessage {
private String role;
private String content;
}
}

View File

@@ -0,0 +1,11 @@
package com.bruce.agent.dto.request;
import lombok.Data;
@Data
public class AgentDefinitionQueryRequest {
private String agentCode;
private String agentName;
private String status;
private Long storeId;
}

View File

@@ -0,0 +1,14 @@
package com.bruce.agent.dto.request;
import lombok.Data;
@Data
public class AgentDefinitionSaveRequest {
private Long id;
private String agentCode;
private String agentName;
private String systemPrompt;
private Long storeId;
private String status;
private String remark;
}

View File

@@ -0,0 +1,25 @@
package com.bruce.agent.dto.response;
import lombok.Data;
import java.util.List;
@Data
public class AgentChatResponse {
private Long agentId;
private String agentCode;
private String agentName;
private Long storeId;
private String storeName;
private String answer;
private String modelRequestId;
private List<ReferenceChunk> references;
@Data
public static class ReferenceChunk {
private Long chunkId;
private Long documentId;
private String chunkContent;
private Double score;
}
}

View File

@@ -0,0 +1,25 @@
package com.bruce.agent.dto.response;
import com.bruce.agent.entity.AgentDefinition;
import lombok.Data;
import org.springframework.beans.BeanUtils;
@Data
public class AgentDefinitionResponse {
private Long id;
private String agentCode;
private String agentName;
private String systemPrompt;
private Long storeId;
private String status;
private String remark;
public static AgentDefinitionResponse fromEntity(AgentDefinition entity) {
if (entity == null) {
return null;
}
AgentDefinitionResponse response = new AgentDefinitionResponse();
BeanUtils.copyProperties(entity, response);
return response;
}
}

View File

@@ -0,0 +1,29 @@
package com.bruce.agent.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.bruce.common.domain.model.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("agent_definition")
public class AgentDefinition extends BaseEntity {
@TableField("agent_code")
private String agentCode;
@TableField("agent_name")
private String agentName;
@TableField("system_prompt")
private String systemPrompt;
@TableField("store_id")
private Long storeId;
private String status;
private String remark;
}

Some files were not shown because too many files have changed in this diff Show More