feat : 调整skillhub-scanner 服务启动 root_path路径问题

This commit is contained in:
翟二远 2026-04-07 14:07:33 +08:00
parent 048b137ee8
commit 29093a452d
18 changed files with 6124 additions and 5 deletions

View file

@ -0,0 +1,674 @@
# Agent+Skill 检索系统架构设计
## 1. 系统概述
### 1.1 设计目标
构建一个基于 PostgreSQL + pgvector 的智能技能检索系统,通过意图识别、多轮对话上下文、场景描述、用户角色、标签和元数据等多维度信息,为 Agent 精准匹配适合的 Skills。
### 1.2 核心能力
- **语义理解**:基于向量嵌入的语义相似度检索
- **意图识别**:识别用户查询的真实意图类别
- **上下文感知**:结合多轮对话历史,理解用户当前需求
- **场景适配**:根据使用场景和用户角色推荐技能
- **多维度融合**:综合向量、意图、标签、流行度、评分等多维度评分
## 2. 系统分层架构
### 2.1 整体分层
```mermaid
graph TB
subgraph "表现层 (Presentation Layer)"
A1[REST API Gateway]
A2[WebSocket Gateway<br/>实时对话]
end
subgraph "应用层 (Application Layer)"
B1[Agent Skill Search Controller]
B2[Agent Session Controller]
B3[Skill Embedding Sync Controller]
end
subgraph "服务层 (Service Layer)"
C1[Context Builder<br/>上下文构建器]
C2[Intent Classifier<br/>意图分类器]
C3[Query Embedder<br/>查询向量化]
C4[Hybrid Search Service<br/>混合检索服务]
C5[Skill Reranker<br/>技能重排序器]
C6[Agent Session Service<br/>会话管理服务]
C7[Embedding Sync Service<br/>嵌入同步服务]
end
subgraph "领域层 (Domain Layer)"
D1[Agent Session]
D2[Agent Message]
D3[Skill Embedding]
D4[Intent Mapping]
D5[Search Context]
D6[Match Result]
end
subgraph "基础设施层 (Infrastructure Layer)"
E1[(PostgreSQL<br/>+ pgvector)]
E2[(Redis Cache)]
E3[OpenAI Embedding API]
E4[Message Queue<br/>异步处理]
end
A1 --> B1
A1 --> B2
A1 --> B3
A2 --> B2
B1 --> C1
B1 --> C4
B2 --> C6
B3 --> C7
C1 --> C6
C1 --> D5
C2 --> D4
C3 --> E3
C4 --> C3
C4 --> C2
C4 --> C5
C6 --> D1
C6 --> D2
C7 --> D3
C1 --> E2
C2 --> E2
C4 --> E1
C5 --> E1
C6 --> E1
C6 --> E2
C7 --> E4
style A1 fill:#e1f5ff
style A2 fill:#e1f5ff
style E3 fill:#ffebee
style E1 fill:#c8e6c9
style E2 fill:#fff9c4
```
### 2.2 分层职责
| 层级 | 职责 | 核心组件 |
|------|------|----------|
| 表现层 | 暴露 API,处理请求响应 | REST API、WebSocket |
| 应用层 | 编排业务流程,处理应用逻辑 | Controller、DTO |
| 服务层 | 核心业务逻辑实现 | 各种 Service |
| 领域层 | 领域模型和业务规则 | Entity、Domain Object |
| 基础设施层 | 数据持久化、外部服务调用 | 数据库、缓存、API |
## 3. 核心模块设计
### 3.1 模块关系图
```mermaid
graph TD
subgraph "输入模块"
IN1[Query Input]
IN2[Session Context]
IN3[User Context]
end
subgraph "处理模块"
P1[Context Builder]
P2[Intent Classifier]
P3[Query Embedder]
P4[Hybrid Search]
P5[Reranker]
end
subgraph "存储模块"
S1[Skill Embeddings]
S2[Intent Mapping]
S3[Session Store]
end
subgraph "输出模块"
OUT1[Ranked Skills]
OUT2[Match Reasons]
OUT3[Confidence Scores]
end
IN1 --> P1
IN2 --> P1
IN3 --> P1
P1 --> P2
P1 --> P3
P2 --> P4
P3 --> P4
P4 --> S1
P4 --> S2
P5 --> P4
P4 --> P5
P5 --> OUT1
P5 --> OUT2
P5 --> OUT3
P1 --> S3
style IN1 fill:#e1f5ff
style S1 fill:#c8e6c9
style OUT1 fill:#c8e6c9
```
### 3.2 模块详细设计
#### 3.2.1 Context Builder(上下文构建器)
**职责**:收集并整合所有上下文信息
**输入**:
- 查询文本
- Session ID
- 用户 ID
- 场景描述(可选)
- 用户角色(可选)
- 元数据(可选)
**输出**:`AgentSearchContext`
**核心逻辑**:
1. 从缓存/数据库加载会话历史
2. 提取最近 N 轮对话作为上下文
3. 整合用户角色和场景信息
4. 构建统一的上下文对象
#### 3.2.2 Intent Classifier(意图分类器)
**职责**:识别用户查询的意图类型
**实现方式**:基于向量相似度的分类
**流程**:
1. 将查询文本转换为向量
2. 与预定义的意图嵌入计算余弦相似度
3. 返回 Top-N 意图及置信度
**输出**:`IntentClassificationResult`
#### 3.2.3 Query Embedder(查询向量化)
**职责**:将综合查询上下文转换为向量
**向量融合公式**:
```
query_vector = normalize(
w1 * embed(query_text) +
w2 * embed(conversation_context) +
w3 * embed(scenario_description) +
w4 * embed(user_role) +
w5 * intent_embedding
)
```
**权重配置**:
- w1 (查询文本): 0.4
- w2 (对话上下文): 0.2
- w3 (场景描述): 0.15
- w4 (用户角色): 0.1
- w5 (意图): 0.15
#### 3.2.4 Hybrid Search Service(混合检索服务)
**职责**:执行多维度检索,综合多种检索策略
**检索维度**:
1. 向量检索:pgvector 语义相似度
2. 意图过滤:基于意图类别过滤
3. 标签匹配:精确匹配用户指定标签
4. 权限过滤:根据用户角色过滤可见技能
**检索策略**:先向量检索获得候选集,再逐步过滤
#### 3.2.5 Skill Reranker(技能重排序器)
**职责**:对检索结果进行多维度重排序
**评分公式**:
```
final_score = α * vector_similarity
+ β * intent_match_score
+ γ * tag_match_score
+ δ * popularity_score
+ ε * rating_score
```
**权重配置**:
- α (向量相似度): 0.4
- β (意图匹配): 0.25
- γ (标签匹配): 0.2
- δ (流行度): 0.1
- ε (评分): 0.05
#### 3.2.6 Agent Session Service(会话管理服务)
**职责**:管理 Agent 对话会话的生命周期
**核心功能**:
- 创建新会话
- 追加对话消息
- 获取会话历史
- 会话过期管理
- 异步持久化
## 4. 数据流设计
### 4.1 主检索流程
```mermaid
sequenceDiagram
participant Client
participant API
participant ContextBuilder
participant IntentClassifier
participant QueryEmbedder
participant HybridSearch
participant Reranker
participant Redis
participant PostgreSQL
Client->>API: POST /api/agent/skills/search
API->>ContextBuilder: buildContext(request)
ContextBuilder->>Redis: getSession(sessionId)
Redis-->>ContextBuilder: session data
ContextBuilder-->>API: AgentSearchContext
API->>IntentClassifier: classify(query, context)
IntentClassifier->>PostgreSQL: query intents by vector
PostgreSQL-->>IntentClassifier: top intents
IntentClassifier-->>API: IntentClassificationResult
API->>QueryEmbedder: embed(query, context, intent)
QueryEmbedder->>Redis: get cached embedding
alt cache miss
QueryEmbedder->>QueryEmbedder: call OpenAI API
QueryEmbedder->>Redis: cache embedding
end
QueryEmbedder-->>API: query_vector
API->>HybridSearch: search(vector, context, intent)
HybridSearch->>PostgreSQL: vector search with filters
PostgreSQL-->>HybridSearch: candidate skills
HybridSearch-->>API: candidate results
API->>Reranker: rerank(candidates, context)
Reranker-->>API: ranked results
API-->>Client: SearchResponse
Note over API,Redis: async save message
API->>Redis: saveMessage(sessionId, message)
```
### 4.2 技能向量同步流程
```mermaid
sequenceDiagram
participant SkillService
participant MessageQueue
participant EmbeddingSync
participant OpenAI
participant PostgreSQL
SkillService->>MessageQueue: publish skill update event
MessageQueue->>EmbeddingSync: consume event
EmbeddingSync->>PostgreSQL: get skill metadata
PostgreSQL-->>EmbeddingSync: skill data
EmbeddingSync->>OpenAI: generate embeddings
OpenAI-->>EmbeddingSync: embedding vectors
EmbeddingSync->>PostgreSQL: upsert embeddings
EmbeddingSync->>PostgreSQL: rebuild vector index
PostgreSQL-->>EmbeddingSync: success
```
## 5. 架构图
### 5.1 系统架构图
```mermaid
C4Context
title Agent+Skill 检索系统架构
Person(agent_user, "Agent 用户", "使用 Agent 进行技能搜索")
System(skillhub_system, "SkillHub 系统", "技能注册与发现平台")
System(external_ai, "OpenAI API", "嵌入向量生成服务")
SystemDb(database, "PostgreSQL", "数据持久化与向量检索")
Rel(agent_user, skillhub_system, "使用", "HTTPS/JSON")
Rel(skillhub_system, external_ai, "调用", "HTTPS")
Rel(skillhub_system, database, "读写", "JDBC")
System_Boundary(skillhub_boundary, "SkillHub 系统") {
System_Ext(api_gateway, "API Gateway", "REST API 接入层")
System(application, "Application Layer", "业务应用层")
System(service_layer, "Service Layer", "核心服务层")
System(cache, "Redis", "缓存层")
}
Rel(agent_user, api_gateway, "调用")
Rel(api_gateway, application, "路由")
Rel(application, service_layer, "调用")
Rel(service_layer, cache, "缓存读写")
```
### 5.2 容器架构图
```mermaid
graph TB
subgraph "用户接入层"
LB[Load Balancer]
end
subgraph "应用服务层"
GW[API Gateway]
APP1[SkillHub App 1]
APP2[SkillHub App 2]
APP_N[SkillHub App N]
end
subgraph "数据处理层"
WORKER[Embedding Worker]
end
subgraph "存储层"
PG[(PostgreSQL Primary)]
PG_REPLICA[(PostgreSQL Replica)]
REDIS[(Redis Cluster)]
MQ[Message Queue]
end
subgraph "外部服务"
OPENAI[OpenAI API]
end
LB --> GW
GW --> APP1
GW --> APP2
GW --> APP_N
APP1 --> PG
APP1 --> PG_REPLICA
APP1 --> REDIS
APP2 --> PG
APP2 --> PG_REPLICA
APP2 --> REDIS
APP_N --> PG
APP_N --> PG_REPLICA
APP_N --> REDIS
APP1 --> MQ
APP2 --> MQ
APP_N --> MQ
MQ --> WORKER
WORKER --> OPENAI
WORKER --> PG
PG -.->|Replication| PG_REPLICA
APP1 --> OPENAI
APP2 --> OPENAI
APP_N --> OPENAI
style LB fill:#e1f5ff
style PG fill:#c8e6c9
style REDIS fill:#fff9c4
style OPENAI fill:#ffebee
```
## 6. 可扩展性设计
### 6.1 水平扩展
| 组件 | 扩展方式 | 说明 |
|------|----------|------|
| 应用服务 | 多实例部署 | 通过负载均衡分发请求 |
| 数据库 | 读写分离 | 主库写入,从库读取 |
| 缓存 | 集群模式 | Redis Cluster 分片存储 |
| 向量索引 | 分区表 | 按技能类别分区 |
### 6.2 垂直扩展
| 资源 | 优化方向 | 说明 |
|------|----------|------|
| CPU | 异步处理 | 嵌入生成异步化 |
| 内存 | 缓存优化 | 热数据缓存 |
| 网络 | 批量请求 | 减少 API 调用次数 |
| 存储 | 索引优化 | ivfflat 参数调优 |
### 6.3 扩展点
1. **嵌入提供者接口**:支持切换不同的嵌入模型
2. **意图分类器接口**:支持自定义意图分类逻辑
3. **重排序策略接口**:支持业务特定的排序规则
4. **过滤器插件接口**:支持添加自定义过滤逻辑
## 7. 可靠性设计
### 7.1 故障处理策略
| 故障场景 | 处理策略 | 降级方案 |
|----------|----------|----------|
| OpenAI API 调用失败 | 重试 3 次,指数退避 | 使用本地哈希向量 |
| 向量索引不可用 | 记录告警,使用全文搜索 | 基于关键词检索 |
| 意图识别失败 | 使用默认意图 "general" | 无降级 |
| 数据库连接失败 | 切换到只读副本 | 返回缓存结果 |
| Redis 不可用 | 直接查询数据库 | 稍微增加响应时间 |
| 消息队列不可用 | 同步处理嵌入 | 稍微增加发布时间 |
### 7.2 数据一致性
| 场景 | 策略 |
|------|------|
| 会话消息保存 | 写后读一致性 |
| 嵌入向量更新 | 最终一致性 |
| 缓存更新 | Write-Through |
| 搜索索引更新 | 异步重建 |
### 7.3 容灾设计
- 数据库定期备份(每日全量 + 每小时增量)
- Redis AOF 持久化 + RDB 快照
- 应用服务多可用区部署
- 关键接口重试机制
## 8. 性能设计
### 8.1 性能目标
| 指标 | 目标值 |
|------|--------|
| P95 查询响应时间 | < 500ms |
| P99 查询响应时间 | < 1000ms |
| 并发 QPS | > 100 |
| 支持 Agent 数量 | > 1000 |
| 单会话消息数 | > 1000 |
| 技能库规模 | > 10000 |
### 8.2 性能优化策略
1. **缓存策略**
- 活跃会话:Redis 缓存,TTL 1 小时
- 意图嵌入:Redis 缓存,永久
- 技能嵌入:PostgreSQL 存储,版本更新时失效
- 查询结果:Redis 缓存,TTL 5 分钟
2. **数据库优化**
- 向量索引:ivfflat,lists = sqrt(行数)
- 查询优化:限制候选集大小
- 连接池:合理配置连接池大小
3. **异步处理**
- 嵌入生成:异步处理
- 会话消息保存:异步持久化
- 指标收集:异步上报
4. **批量操作**
- 批量向量生成
- 批量数据库写入
- 批量缓存更新
## 9. 安全设计
### 9.1 安全层次
```mermaid
graph TB
subgraph "网络安全"
N1[TLS/HTTPS 加密传输]
N2[防火墙规则]
end
subgraph "应用安全"
A1[身份认证<br/>JWT/OAuth2]
A2[权限控制<br/>RBAC]
A3[输入验证]
A4[输出编码]
end
subgraph "数据安全"
D1[SQL 注入防护]
D2[敏感数据加密]
D3[访问审计]
end
subgraph "API 安全"
API1[速率限制]
API2[API Key 管理]
API3[请求签名]
end
N1 --> A1
A1 --> A2
A2 --> D1
D1 --> API1
style A1 fill:#fff9c4
style D1 fill:#ffebee
```
### 9.2 安全措施
| 安全领域 | 措施 |
|----------|------|
| 认证 | JWT Token,有效期 1 小时 |
| 授权 | 基于角色的访问控制 (RBAC) |
| 加密 | HTTPS 传输,数据库字段加密 |
| 审计 | 记录关键操作日志 |
| 限流 | 用户级别 + IP 级别限流 |
| 输入验证 | 所有输入参数校验 |
## 10. 监控与可观测性
### 10.1 监控指标
| 类别 | 指标 |
|------|------|
| 业务 | 搜索请求量、Top-10 准确率、意图识别准确率 |
| 性能 | 响应时间 P50/P95/P99、QPS、错误率 |
| 系统 | CPU 使用率、内存使用率、磁盘 I/O |
| 数据库 | 连接数、查询耗时、慢查询数 |
| 缓存 | 命中率、内存使用、键数量 |
### 10.2 日志策略
| 日志类型 | 级别 | 内容 |
|----------|------|------|
| 访问日志 | INFO | 请求/响应摘要 |
| 业务日志 | INFO | 关键业务操作 |
| 错误日志 | ERROR | 异常堆栈 |
| 审计日志 | WARN | 敏感操作 |
### 10.3 告警规则
| 场景 | 阈值 | 级别 |
|------|------|------|
| P95 响应时间 | > 1000ms | WARNING |
| 错误率 | > 1% | WARNING |
| 错误率 | > 5% | CRITICAL |
| 数据库连接数 | > 80% | WARNING |
| OpenAI API 失败率 | > 10% | CRITICAL |
## 11. 部署架构
### 11.1 开发环境
```mermaid
graph TB
DEV[开发环境] --> DEV_APP[单实例应用]
DEV_APP --> DEV_PG[(PostgreSQL)]
DEV_APP --> DEV_REDIS[(Redis)]
style DEV_APP fill:#e1f5ff
style DEV_PG fill:#c8e6c9
```
### 11.2 生产环境
```mermaid
graph TB
subgraph "生产环境"
LB[Load Balancer]
APP1[App Instance 1]
APP2[App Instance 2]
APP3[App Instance N]
PG_MASTER[(PostgreSQL Master)]
PG_SLAVE1[(PostgreSQL Slave 1)]
PG_SLAVE2[(PostgreSQL Slave 2)]
REDIS_CLUSTER[Redis Cluster]
MQ_CLUSTER[Message Queue Cluster]
end
LB --> APP1
LB --> APP2
LB --> APP3
APP1 --> PG_MASTER
APP1 --> PG_SLAVE1
APP1 --> REDIS_CLUSTER
APP1 --> MQ_CLUSTER
APP2 --> PG_MASTER
APP2 --> PG_SLAVE2
APP2 --> REDIS_CLUSTER
APP2 --> MQ_CLUSTER
APP3 --> PG_MASTER
APP3 --> PG_SLAVE1
APP3 --> REDIS_CLUSTER
APP3 --> MQ_CLUSTER
PG_MASTER -.->|Replication| PG_SLAVE1
PG_MASTER -.->|Replication| PG_SLAVE2
style LB fill:#e1f5ff
style PG_MASTER fill:#c8e6c9
style REDIS_CLUSTER fill:#fff9c4
```
### 11.3 部署策略
| 环境 | 实例数 | 数据库 | 缓存 |
|------|--------|--------|------|
| 开发 | 1 | 单实例 | 单实例 |
| 测试 | 2 | 1主1从 | 单实例 |
| 预发 | 2 | 1主2从 | 哨兵模式 |
| 生产 | 3+ | 1主N从 | 集群模式 |
## 12. 总结
本架构设计遵循以下原则:
1. **分层清晰**:表现层、应用层、服务层、领域层、基础设施层职责分明
2. **高内聚低耦合**:各模块职责单一,依赖关系清晰
3. **可扩展**:通过接口抽象支持多种扩展方式
4. **高可用**:多实例部署、读写分离、故障降级
5. **高性能**:多层缓存、异步处理、批量操作
6. **安全可靠**:多层次安全防护、完善的监控告警
该架构能够满足 Agent+Skill 检索系统的核心需求,为后续实现提供清晰的指导。

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,321 @@
# Skills 模块设计方案
## 1. Skill 数据模型设计
### 1.1 核心数据表结构
#### skills 表
| 字段名 | 类型 | 说明 | 索引 |
|--------|------|------|------|
| id | uuid | 主键 | PK |
| name | varchar(255) | Skill 名称 | |
| description | text | Skill 描述 | |
| embedding | vector(1536) | 语义向量(基于 description 生成) | pgvector |
| category_id | uuid | 所属分类 | FK |
| is_active | boolean | 是否启用 | |
| created_at | timestamp | 创建时间 | |
| updated_at | timestamp | 更新时间 | |
#### skill_tags 表
| 字段名 | 类型 | 说明 | 索引 |
|--------|------|------|------|
| id | uuid | 主键 | PK |
| skill_id | uuid | 关联的 skill | FK |
| tag | varchar(100) | 标签名称 | |
| tag_type | varchar(50) | 标签类型(domain/role/action) | |
#### skill_metadata 表
| 字段名 | 类型 | 说明 | 索引 |
|--------|------|------|------|
| id | uuid | 主键 | PK |
| skill_id | uuid | 关联的 skill | FK |
| key | varchar(100) | 元数据键 | |
| value | text | 元数据值(JSON 格式) | |
#### skill_categories 表
| 字段名 | 类型 | 说明 | 索引 |
|--------|------|------|------|
| id | uuid | 主键 | PK |
| name | varchar(100) | 分类名称 | |
| parent_id | uuid | 父分类 | FK |
| level | int | 分类层级 | |
### 1.2 向量化方案
#### 向量化内容来源
1. **主要向量**:基于 Skill 的 `description` 字段
2. **增强向量**:合并 `name` + `description` + `tags` 生成更丰富的语义表示
3. **多维度向量**:针对不同场景生成独立向量(如:技术栈向量、业务场景向量)
#### 向量维度选择
- 推荐维度:1536(OpenAI text-embedding-3-small/large)
- 备选维度:768(轻量级场景)
- 存储类型:`vector(1536)` 或 `vector(768)`
#### 向量更新策略
- 创建 Skill 时自动生成向量
- 当 `description`、`name`、`tags` 变更时,异步重新计算向量
- 支持手动触发向量重新生成
## 2. Skill 标签和元数据结构
### 2.1 标签分类体系
#### Domain 标签(领域标签)
- 描述 Skill 所属业务领域
- 示例:`database`、`api`、`auth`、`ui`、`testing`、`deployment`
#### Role 标签(角色标签)
- 描述适合使用该 Skill 的角色
- 示例:`developer`、`devops`、`analyst`、`designer`、`manager`
#### Action 标签(动作标签)
- 描述 Skill 的主要动作类型
- 示例:`create`、`read`、`update`、`delete`、`analyze`、`deploy`
#### Tech Stack 标签(技术栈标签)
- 描述 Skill 涉及的技术栈
- 示例:`python`、`java`、`react`、`kubernetes`、`postgresql`
### 2.2 元数据结构设计
#### 标准元数据字段
```json
{
"complexity": "low|medium|high",
"estimated_time": "5m",
"dependencies": ["skill_id_1", "skill_id_2"],
"required_permissions": ["read_code", "write_files"],
"compatibility": {
"min_agent_version": "1.0.0",
"supported_models": ["claude-sonnet-4.6", "claude-opus-4.6"]
},
"usage_stats": {
"total_calls": 1000,
"success_rate": 0.95
}
}
```
#### 扩展元数据
- 自定义字段支持 JSON 格式存储
- 支持元数据查询和过滤
## 3. Skill 匹配算法设计
### 3.1 匹配流程架构
```
输入:查询上下文(意图、历史、场景、用户角色)
↓
阶段 1:规则过滤
├── 活跃状态过滤(is_active = true)
├── 角色权限过滤
├── 技术栈兼容性过滤
└── 标签前置过滤
↓
阶段 2:语义相似度计算
├── 查询向量生成(意图 + 场景 + 上下文)
├── 向量相似度检索(ANN 搜索)
└── 候选集排序
↓
阶段 3:多维度评分
├── 语义相似度分(40%)
├── 标签匹配分(30%)
├── 历史使用频率分(20%)
└── 上下文相关性分(10%)
↓
阶段 4:结果重排
├── 多样性保证(避免重复类型)
├── 相关性阈值过滤
└── Top-K 返回
↓
输出:匹配的 Skills 列表(带评分)
```
### 3.2 查询向量生成策略
#### 组合查询向量
将以下内容合并生成查询向量:
1. **当前意图文本**:用户当前的请求意图
2. **场景描述**:当前工作场景的上下文描述
3. **对话历史摘要**:最近 N 轮对话的语义摘要
4. **用户角色信息**:用户角色相关的描述文本
#### 向量生成方式
- 方式 A:直接拼接文本,生成单一向量
- 方式 B:分别生成向量后加权平均
- 方式 C:生成多个独立向量,分别检索后合并结果
### 3.3 相似度计算方法
#### 向量相似度
- 使用余弦相似度(Cosine Similarity)
- 公式:`similarity = (A · B) / (||A|| × ||B||)`
- pgvector 操作符:`<=>`(余弦距离,越小越相似)
#### 标签匹配度
- 精确匹配:标签完全一致
- 层次匹配:父子标签层级匹配
- 权重计算:不同标签类型设置不同权重
#### 上下文相关性
- 基于对话历史中的 Skill 使用模式
- 计算 Skill 与历史上下文的关联度
### 3.4 多维度评分公式
```
Total Score = w1 × SemanticScore + w2 × TagScore + w3 × HistoryScore + w4 × ContextScore
其中:
- w1 = 0.4(语义相似度权重)
- w2 = 0.3(标签匹配权重)
- w3 = 0.2(历史使用权重)
- w4 = 0.1(上下文相关性权重)
```
## 4. 场景描述处理策略
### 4.1 场景描述来源
1. **显式场景描述**:用户提供的场景文本
2. **隐式场景推断**:从对话历史和上下文自动提取
3. **环境上下文**:当前工作目录、文件类型、Git 状态等
### 4.2 场景描述处理流程
```
原始场景输入
↓
文本预处理
├── 去除噪声和冗余
├── 提取关键实体
└── 识别技术关键词
↓
场景分类
├── 开发场景(coding/debugging/refactoring)
├── 部署场景(deploy/monitor/maintenance)
└── 分析场景(review/analysis/report)
↓
场景增强
├── 关联历史相似场景
├── 添加相关技术标签
└── 生成场景向量
↓
输出:结构化场景描述
```
### 4.3 场景描述缓存策略
- 热门场景描述缓存(LRU 策略)
- 场景向量预计算
- 定期清理过期缓存
## 5. pgvector 索引策略和检索优化
### 5.1 索引类型选择
#### HNSW 索引(推荐)
- 优点:查询速度快,适合大规模数据
- 适用场景:实时检索,查询频繁
- 参数配置:
- `m = 16`(每个节点的连接数)
- `ef_construction = 64`(构建时的搜索宽度)
```sql
CREATE INDEX idx_skills_embedding_hnsw
ON skills USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
```
#### IVFFlat 索引
- 优点:构建速度快,内存占用低
- 适用场景:数据更新频繁
- 参数配置:
- `lists = 100`(聚类中心数量)
```sql
CREATE INDEX idx_skills_embedding_ivfflat
ON skills USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
```
### 5.2 检索优化策略
#### 批量检索优化
- 使用 `array_agg` 批量获取结果
- 减少 SQL 查询次数
#### 预过滤优化
- 先用规则过滤缩小候选集
- 再在候选集上进行向量检索
#### 查询参数调优
- `ef_search`:控制搜索精度与速度的权衡
- 推荐值:`ef_search = 40`(平衡性能)
```sql
SET hnsw.ef_search = 40;
```
#### 分页优化
- 使用游标分页代替 OFFSET
- 避免深度分页性能问题
### 5.3 索引维护策略
#### 定期重建索引
- 当数据变更超过 20% 时重建 HNSW 索引
- 使用 `REINDEX INDEX` 命令
#### 向量更新优化
- 批量更新向量,减少索引重建频率
- 使用异步任务处理向量计算
#### 索引监控
- 监控索引大小和查询性能
- 定期收集统计信息
```sql
ANALYZE skills;
```
### 5.4 查询性能优化
#### 查询计划分析
- 使用 `EXPLAIN ANALYZE` 分析查询性能
- 识别性能瓶颈并优化
#### 结果缓存
- 缓存热门查询结果
- 设置合理的缓存过期时间
#### 连接池配置
- 合理配置数据库连接池大小
- 避免连接频繁创建和销毁
## 6. 扩展性设计
### 6.1 插件化标签系统
- 支持自定义标签类型
- 支持标签权重动态配置
### 6.2 多语言向量支持
- 支持不同语言的向量嵌入
- 支持多语言混合查询
### 6.3 A/B 测试框架
- 支持不同匹配算法的对比测试
- 支持实时算法切换
### 6.4 可观测性
- 记录匹配过程的详细日志
- 统计各项指标(召回率、准确率、响应时间)
- 支持算法效果分析
---
**设计版本**:v1.0
**设计日期**:2026-04-05
**设计人**:Skills 开发工程师

View file

@ -0,0 +1,96 @@
# Agent Skills 智能检索系统 - 系统概述
## 1. 项目背景
SkillHub 是一个 Agent 技能注册中心,用于管理 Agent 技能包的发布、版本控制和发现。为了提升 Agent 选择技能的智能化水平,需要构建一个基于向量检索的智能匹配系统。
## 2. 核心目标
构建一个稳定可靠、精准匹配的技能检索系统,通过以下能力实现:
- **意图识别**:理解用户查询的真实意图
- **上下文感知**:结合多轮对话历史,理解用户当前需求
- **场景适配**:根据使用场景和用户角色,推荐最合适的技能
- **精准匹配**:结合语义向量、标签、类别等多维度信息进行匹配
## 3. 核心输入
| 输入项 | 说明 | 示例 |
|--------|------|------|
| 意图识别 | 用户查询的意图类型 | "代码生成"、"数据分析"、"文本处理" |
| 多轮对话上下文 | 用户与 Agent 的历史对话 | 过去 N 轮的问答记录 |
| 场景描述 | 当前使用场景 | "Web 开发"、"数据可视化" |
| 用户角色信息 | 用户身份和权限 | "开发者"、"数据分析师" |
| 标签信息 | 技能的标签分类 | ["python", "react", "api"] |
| 元数据 | 技能的详细元信息 | 版本、依赖、示例代码等 |
## 4. 技术选型
| 组件 | 选型 | 说明 |
|------|------|------|
| 向量数据库 | PostgreSQL + pgvector | 利用现有数据库,减少架构复杂度 |
| 嵌入模型 | OpenAI text-embedding-3-small | 1536 维向量,高质量语义表示 |
| 意图识别 | 基于向量相似度 | 预定义意图类别,通过向量匹配 |
| 向量索引 | ivfflat | 平衡精度和性能 |
| 对话管理 | Redis + PostgreSQL | 热数据缓存,冷数据持久化 |
| 后端框架 | Spring Boot 3.2.3 | 现有技术栈,无缝集成 |
## 5. 系统特性
### 5.1 稳定性
- 基于成熟的 PostgreSQL 数据库
- pgvector 扩展经过生产验证
- 完善的错误处理和降级机制
### 5.2 可靠性
- 多维度评分,避免单点失败
- 权限控制确保数据安全
- 异步处理保障响应性能
### 5.3 精准匹配
- 语义向量相似度检索
- 意图类别过滤
- 标签精确匹配
- 多维度权重融合
## 6. 性能指标
| 指标 | 目标值 | 说明 |
|------|--------|------|
| 查询响应时间 | < 500ms | 95 分位 |
| 并发 QPS | > 100 | 稳定支持 |
| Top-10 准确率 | > 80% | 相关技能在前 10 |
| 意图匹配准确率 | > 85% | 意图分类正确率 |
## 7. 与现有系统集成
```
┌─────────────────────────────────────────────────────────────┐
│ SkillHub 现有系统 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Skill 管理 │ │ 搜索服务 │ │ 权限系统 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ 复用现有能力
▼
┌─────────────────────────────────────────────────────────────┐
│ Agent Skills 智能检索系统 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 对话管理 │ │ 意图识别 │ │ 智能匹配 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## 8. 文档结构
| 文档 | 内容 |
|------|------|
| 01-overview.md | 系统概述(本文档) |
| 02-architecture.md | 架构设计 |
| 03-data-model.md | 数据模型 |
| 04-algorithms.md | 算法设计 |
| 05-flow-diagrams.md | 流程图 |
| 06-architecture-diagrams.md | 架构图 |
| 07-api-design.md | API 设计 |
| 08-implementation-guide.md | 实施指南 |

View file

@ -0,0 +1,266 @@
# Agent Skills 智能检索系统 - 架构设计
## 1. 系统架构分层
```
┌─────────────────────────────────────────────────────────────────┐
│ API Gateway Layer │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Agent Skill Search Controller │ │
│ │ - POST /api/agent/skills/search │ │
│ │ - POST /api/agent/sessions │ │
│ │ - GET /api/agent/sessions/{id} │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ Service Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Context │ │ Intent │ │ Query │ │
│ │ Builder │ │ Classifier │ │ Embedder │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Hybrid │ │ Skill │ │ Agent │ │
│ │ Search │ │ Reranker │ │ Session │ │
│ │ Service │ │ │ │ Service │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ Domain Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Agent │ │ Skill │ │ Intent │ │
│ │ Session │ │ Embedding │ │ Mapping │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────▼───────────────────────────────────┐
│ Infrastructure Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ Redis │ │ OpenAI │ │
│ │ + pgvector │ │ Cache │ │ Embedding │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## 2. 核心组件详解
### 2.1 Context Builder(上下文构建器)
**职责**:收集并构建完整的查询上下文
**输入**:
- 用户查询文本
- Session ID
- 用户 ID
- 场景描述
- 用户角色
**输出**:`AgentSearchContext`
```java
public record AgentSearchContext(
String query, // 查询文本
List<AgentMessage> conversationHistory, // 对话历史
String scenarioDescription, // 场景描述
String userRole, // 用户角色
Map<String, Object> metadata // 元数据
) {}
```
### 2.2 Intent Classifier(意图分类器)
**职责**:识别用户查询的意图类型
**实现方式**:基于向量相似度的分类
**流程**:
1. 将查询文本转换为向量
2. 与预定义的意图嵌入计算余弦相似度
3. 返回 Top-N 意图及置信度
```java
public interface IntentClassifier {
IntentClassificationResult classify(
String query,
AgentSearchContext context
);
}
public record IntentClassificationResult(
String intentLabel, // 主要意图
double confidence, // 置信度
List<String> alternativeIntents // 备选意图
) {}
```
### 2.3 Query Embedder(查询向量化)
**职责**:将综合查询上下文转换为向量
**向量构建公式**:
```
query_vector = normalize(
w1 * embed(query_text) +
w2 * embed(conversation_context) +
w3 * embed(scenario_description) +
w4 * embed(user_role) +
w5 * intent_embedding
)
```
**权重配置**:
- w1 (查询文本): 0.4
- w2 (对话上下文): 0.2
- w3 (场景描述): 0.15
- w4 (用户角色): 0.1
- w5 (意图): 0.15
### 2.4 Hybrid Search Service(混合检索服务)
**职责**:执行多维度检索
**检索流程**:
1. **向量检索**:使用 pgvector 进行语义相似度检索
2. **意图过滤**:根据识别的意图过滤技能类别
3. **标签匹配**:匹配用户指定或相关的标签
4. **权限过滤**:根据用户角色过滤可见技能
### 2.5 Skill Reranker(技能重排序器)
**职责**:对检索结果进行多维度重排序
**评分公式**:
```
final_score = α * vector_similarity
+ β * intent_match_score
+ γ * tag_match_score
+ δ * popularity_score
+ ε * rating_score
```
**权重配置**:
- α (向量相似度): 0.4
- β (意图匹配): 0.25
- γ (标签匹配): 0.2
- δ (流行度): 0.1
- ε (评分): 0.05
### 2.6 Agent Session Service(会话管理服务)
**职责**:管理 Agent 对话会话
**功能**:
- 创建新会话
- 保存对话消息
- 获取会话历史
- 会话过期管理
## 3. 数据流
```
┌──────────────┐
│ Agent Request│
└──────┬───────┘
│
▼
┌─────────────────────────────────┐
│ 1. Context Builder │
│ - 收集查询、历史、角色 │
│ - 构建 SearchContext │
└──────┬──────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 2. Intent Classifier │
│ - 向量化查询 │
│ - 匹配意图 │
└──────┬──────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 3. Query Embedder │
│ - 融合多维度信息 │
│ - 生成查询向量 │
└──────┬──────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 4. Hybrid Search │
│ - 向量检索 (pgvector) │
│ - 意图/标签/权限过滤 │
└──────┬──────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 5. Skill Reranker │
│ - 多维度评分 │
│ - 重排序结果 │
└──────┬──────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 6. Return Results │
│ - 返回匹配技能列表 │
│ - 包含匹配原因 │
└─────────────────────────────────┘
```
## 4. 模块依赖关系
```
Agent Skill Search Controller
│
├─► AgentContextBuilder
│ └─► AgentSessionService
│ └─► Redis / PostgreSQL
│
├─► IntentClassifier
│ ├─► QueryEmbedder
│ └─► IntentMappingRepository
│ └─► PostgreSQL
│
├─► HybridSkillSearchService
│ ├─► QueryEmbedder
│ ├─► SkillEmbeddingRepository
│ ├─► IntentClassifier
│ └─► SkillReranker
│
└─► SkillReranker
├─► SkillRepository
└─► LabelRepository
```
## 5. 外部依赖
| 依赖 | 用途 | 调用方式 |
|------|------|----------|
| PostgreSQL | 数据持久化、向量检索 | JPA / Native SQL |
| Redis | 会话缓存、热数据存储 | Spring Data Redis |
| OpenAI API | 嵌入向量生成 | HTTP REST API |
## 6. 缓存策略
| 数据类型 | 缓存位置 | TTL | 更新策略 |
|----------|----------|-----|----------|
| 活跃会话 | Redis | 1小时 | 每次访问更新 |
| 意图嵌入 | Redis | 永久 | 管理接口更新 |
| 技能嵌入 | PostgreSQL | - | 版本更新时 |
| 查询结果 | Redis | 5分钟 | 主动失效 |
## 7. 错误处理
| 场景 | 处理策略 |
|------|----------|
| OpenAI API 调用失败 | 降级到基于关键词的检索 |
| 向量索引未就绪 | 使用全文搜索 |
| 会话不存在 | 创建新会话 |
| 意图识别失败 | 使用通用意图 "general" |
## 8. 扩展点
1. **嵌入模型切换**:支持配置不同的嵌入模型
2. **意图库扩展**:通过数据库动态添加新意图
3. **评分策略**:可配置的权重参数
4. **检索后处理**:支持自定义业务规则过滤

View file

@ -0,0 +1,573 @@
# Agent Skills 智能检索系统 - 数据模型
## 1. 数据库扩展
### 1.1 pgvector 扩展安装
```sql
-- 在 PostgreSQL 中安装 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 验证安装
SELECT * FROM pg_extension WHERE extname = 'vector';
```
## 2. 核心数据表
### 2.1 agent_session(Agent 会话表)
存储 Agent 与用户的对话会话信息。
```sql
CREATE TABLE agent_session (
-- 主键
id BIGSERIAL PRIMARY KEY,
-- 标识信息
agent_id VARCHAR(128) NOT NULL,
user_id VARCHAR(128) NOT NULL,
session_id VARCHAR(128) UNIQUE NOT NULL,
-- 上下文信息
scenario_description TEXT,
user_role VARCHAR(128),
-- 元数据(JSONB 格式,灵活扩展)
metadata JSONB DEFAULT '{}',
-- 时间戳
started_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
ended_at TIMESTAMP WITH TIME ZONE,
-- 索引
CONSTRAINT fk_agent_session_user FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE
);
-- 创建索引
CREATE INDEX idx_agent_session_agent_id ON agent_session(agent_id);
CREATE INDEX idx_agent_session_user_id ON agent_session(user_id);
CREATE INDEX idx_agent_session_session_id ON agent_session(session_id);
CREATE INDEX idx_agent_session_started_at ON agent_session(started_at DESC);
-- GIN 索引用于 JSONB 查询
CREATE INDEX idx_agent_session_metadata ON agent_session USING GIN(metadata);
```
**字段说明**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGSERIAL | 主键 |
| agent_id | VARCHAR(128) | Agent 标识符 |
| user_id | VARCHAR(128) | 用户 ID |
| session_id | VARCHAR(128) | 会话唯一标识 |
| scenario_description | TEXT | 场景描述 |
| user_role | VARCHAR(128) | 用户角色 |
| metadata | JSONB | 扩展元数据 |
| started_at | TIMESTAMPTZ | 会话开始时间 |
| updated_at | TIMESTAMPTZ | 会话更新时间 |
| ended_at | TIMESTAMPTZ | 会话结束时间 |
### 2.2 agent_message(对话消息表)
存储会话中的每条消息。
```sql
CREATE TABLE agent_message (
-- 主键
id BIGSERIAL PRIMARY KEY,
-- 关联会话
session_id BIGINT NOT NULL,
-- 消息内容
role VARCHAR(32) NOT NULL, -- 'user' | 'assistant' | 'system'
content TEXT NOT NULL,
-- 意图识别结果
intent_label VARCHAR(128),
intent_confidence DECIMAL(5,4),
-- 向量嵌入
embedding vector(1536),
-- 元数据
metadata JSONB DEFAULT '{}',
-- 时间戳
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
-- 外键约束
CONSTRAINT fk_agent_message_session
FOREIGN KEY (session_id)
REFERENCES agent_session(id)
ON DELETE CASCADE,
-- 检查约束
CONSTRAINT chk_agent_message_role
CHECK (role IN ('user', 'assistant', 'system')),
CONSTRAINT chk_agent_message_confidence
CHECK (intent_confidence IS NULL OR
(intent_confidence >= 0 AND intent_confidence <= 1))
);
-- 创建索引
CREATE INDEX idx_agent_message_session_id ON agent_message(session_id);
CREATE INDEX idx_agent_message_created_at ON agent_message(created_at DESC);
CREATE INDEX idx_agent_message_intent ON agent_message(intent_label);
-- 向量索引(需要先有足够数据)
-- CREATE INDEX idx_agent_message_embedding ON agent_message
-- USING ivfflat(embedding vector_cosine_ops) WITH (lists = 100);
-- GIN 索引用于 JSONB 查询
CREATE INDEX idx_agent_message_metadata ON agent_message USING GIN(metadata);
```
**字段说明**:
| 字段 | 类型 | 说明 |
|------|------|------|
| id | BIGSERIAL | 主键 |
| session_id | BIGINT | 关联的会话 ID |
| role | VARCHAR(32) | 消息角色 |
| content | TEXT | 消息内容 |
| intent_label | VARCHAR(128) | 识别的意图标签 |
| intent_confidence | DECIMAL(5,4) | 意图置信度 |
| embedding | vector(1536) | 消息向量 |
| metadata | JSONB | 扩展元数据 |
| created_at | TIMESTAMPTZ | 创建时间 |
### 2.3 skill_embedding(技能向量表)
存储技能的向量嵌入,用于语义检索。
```sql
CREATE TABLE skill_embedding (
-- 主键
id BIGSERIAL PRIMARY KEY,
-- 关联技能
skill_id BIGINT NOT NULL,
skill_version_id BIGINT,
-- 嵌入类型和向量
embedding_type VARCHAR(32) NOT NULL,
embedding vector(1536) NOT NULL,
-- 元数据
metadata JSONB DEFAULT '{}',
-- 时间戳
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
-- 外键约束
CONSTRAINT fk_skill_embedding_skill
FOREIGN KEY (skill_id)
REFERENCES skill(id)
ON DELETE CASCADE,
CONSTRAINT fk_skill_embedding_version
FOREIGN KEY (skill_version_id)
REFERENCES skill_version(id)
ON DELETE SET NULL,
-- 唯一约束
CONSTRAINT uk_skill_embedding UNIQUE(skill_id, embedding_type),
-- 检查约束
CONSTRAINT chk_skill_embedding_type
CHECK (embedding_type IN ('description', 'example', 'usage', 'combined'))
);
-- 创建索引
CREATE INDEX idx_skill_embedding_skill_id ON skill_embedding(skill_id);
CREATE INDEX idx_skill_embedding_version_id ON skill_embedding(skill_version_id);
-- 向量索引(ivfflat 类型,平衡精度和性能)
CREATE INDEX idx_skill_embedding_embedding ON skill_embedding
USING ivfflat(embedding vector_cosine_ops)
WITH (lists = 100);
-- GIN 索引用于 JSONB 查询
CREATE INDEX idx_skill_embedding_metadata ON skill_embedding USING GIN(metadata);
```
**embedding_type 枚举值**:
| 类型 | 说明 |
|------|------|
| description | 技能描述向量化 |
| example | 示例代码向量化 |
| usage | 使用说明向量化 |
| combined | 综合向量 |
### 2.4 intent_mapping(意图映射表)
存储意图类别及其相关配置。
```sql
CREATE TABLE intent_mapping (
-- 主键
id BIGSERIAL PRIMARY KEY,
-- 意图信息
intent_label VARCHAR(128) UNIQUE NOT NULL,
description TEXT,
-- 关联的技能类别
skill_categories TEXT[],
-- 示例查询(用于意图识别)
example_queries TEXT[],
-- 意图向量
embedding vector(1536) NOT NULL,
-- 优先级和状态
priority INTEGER DEFAULT 0,
active BOOLEAN DEFAULT true,
-- 时间戳
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
-- 检查约束
CONSTRAINT chk_intent_mapping_priority
CHECK (priority >= 0)
);
-- 创建索引
CREATE INDEX idx_intent_mapping_label ON intent_mapping(intent_label);
CREATE INDEX idx_intent_mapping_active ON intent_mapping(active);
-- 向量索引
CREATE INDEX idx_intent_mapping_embedding ON intent_mapping
USING ivfflat(embedding vector_cosine_ops)
WITH (lists = 50);
-- GIN 索引用于数组查询
CREATE INDEX idx_intent_mapping_categories ON intent_mapping
USING GIN(skill_categories);
```
## 3. Java 领域模型
### 3.1 AgentSession
```java
package com.iflytek.skillhub.domain.agent;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.Map;
@Entity
@Table(name = "agent_session")
public class AgentSession {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "agent_id", nullable = false, length = 128)
private String agentId;
@Column(name = "user_id", nullable = false, length = 128)
private String userId;
@Column(name = "session_id", nullable = false, unique = true, length = 128)
private String sessionId;
@Column(name = "scenario_description", columnDefinition = "TEXT")
private String scenarioDescription;
@Column(name = "user_role", length = 128)
private String userRole;
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "metadata", columnDefinition = "jsonb")
private Map<String, Object> metadata = Map.of();
@Column(name = "started_at", nullable = false)
private Instant startedAt = Instant.now();
@Column(name = "updated_at", nullable = false)
private Instant updatedAt = Instant.now();
@Column(name = "ended_at")
private Instant endedAt;
@PreUpdate
protected void onUpdate() {
updatedAt = Instant.now();
}
// Getters and Setters...
}
```
### 3.2 AgentMessage
```java
package com.iflytek.skillhub.domain.agent;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.Map;
@Entity
@Table(name = "agent_message")
public class AgentMessage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "session_id", nullable = false)
private Long sessionId;
@Enumerated(EnumType.STRING)
@Column(name = "role", nullable = false, length = 32)
private MessageRole role;
@Column(name = "content", nullable = false, columnDefinition = "TEXT")
private String content;
@Column(name = "intent_label", length = 128)
private String intentLabel;
@Column(name = "intent_confidence", precision = 5, scale = 4)
private BigDecimal intentConfidence;
// 注意:pgvector 的 Java 类型需要自定义类型处理器
// 这里使用 String 存储序列化后的向量
@Column(name = "embedding", columnDefinition = "vector(1536)")
private String embedding;
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "metadata", columnDefinition = "jsonb")
private Map<String, Object> metadata = Map.of();
@Column(name = "created_at", nullable = false)
private Instant createdAt = Instant.now();
public enum MessageRole {
USER, ASSISTANT, SYSTEM
}
// Getters and Setters...
}
```
### 3.3 SkillEmbedding
```java
package com.iflytek.skillhub.domain.agent;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.Map;
@Entity
@Table(name = "skill_embedding")
public class SkillEmbedding {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "skill_id", nullable = false)
private Long skillId;
@Column(name = "skill_version_id")
private Long skillVersionId;
@Enumerated(EnumType.STRING)
@Column(name = "embedding_type", nullable = false, length = 32)
private EmbeddingType embeddingType;
@Column(name = "embedding", nullable = false, columnDefinition = "vector(1536)")
private String embedding;
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "metadata", columnDefinition = "jsonb")
private Map<String, Object> metadata = Map.of();
@Column(name = "updated_at", nullable = false)
private Instant updatedAt = Instant.now();
@PreUpdate
protected void onUpdate() {
updatedAt = Instant.now();
}
public enum EmbeddingType {
DESCRIPTION, EXAMPLE, USAGE, COMBINED
}
// Getters and Setters...
}
```
### 3.4 IntentMapping
```java
package com.iflytek.skillhub.domain.agent;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@Entity
@Table(name = "intent_mapping")
public class IntentMapping {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "intent_label", nullable = false, unique = true, length = 128)
private String intentLabel;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@ElementCollection
@CollectionTable(name = "intent_skill_categories", joinColumns = @JoinColumn(name = "intent_id"))
@Column(name = "category")
private List<String> skillCategories;
@ElementCollection
@CollectionTable(name = "intent_example_queries", joinColumns = @JoinColumn(name = "intent_id"))
@Column(name = "query")
private List<String> exampleQueries;
@Column(name = "embedding", nullable = false, columnDefinition = "vector(1536)")
private String embedding;
@Column(name = "priority", nullable = false)
private Integer priority = 0;
@Column(name = "active", nullable = false)
private Boolean active = true;
@Column(name = "created_at", nullable = false)
private Instant createdAt = Instant.now();
@Column(name = "updated_at", nullable = false)
private Instant updatedAt = Instant.now();
@PreUpdate
protected void onUpdate() {
updatedAt = Instant.now();
}
// Getters and Setters...
}
```
## 4. 初始数据
### 4.1 预定义意图类别
```sql
-- 插入预定义意图
INSERT INTO intent_mapping (intent_label, description, skill_categories, example_queries, priority, active) VALUES
('code_generation', '代码生成相关技能', ARRAY['coding', 'development'], ARRAY['写一个函数', '生成代码', '创建类'], 10, true),
('data_analysis', '数据分析相关技能', ARRAY['data', 'analysis', 'visualization'], ARRAY['分析数据', '绘制图表', '数据统计'], 10, true),
('text_processing', '文本处理相关技能', ARRAY['text', 'nlp', 'language'], ARRAY['处理文本', '提取关键词', '文本分类'], 10, true),
('api_integration', 'API 集成相关技能', ARRAY['api', 'integration', 'web'], ARRAY['调用 API', 'HTTP 请求', 'REST API'], 10, true),
('file_processing', '文件处理相关技能', ARRAY['file', 'io', 'storage'], ARRAY['读取文件', '写入文件', '文件转换'], 10, true),
('database', '数据库相关技能', ARRAY['database', 'sql', 'storage'], ARRAY['查询数据库', 'SQL 语句', '数据库操作'], 10, true),
('general', '通用查询', ARRAY['general'], ARRAY['帮助', '能做什么', '功能列表'], 0, true);
```
## 5. 数据迁移
### 5.1 Flyway 迁移脚本
```sql
-- V__create_agent_tables.sql
-- 1. 安装 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 2. 创建 agent_session 表
CREATE TABLE agent_session (
id BIGSERIAL PRIMARY KEY,
agent_id VARCHAR(128) NOT NULL,
user_id VARCHAR(128) NOT NULL,
session_id VARCHAR(128) UNIQUE NOT NULL,
scenario_description TEXT,
user_role VARCHAR(128),
metadata JSONB DEFAULT '{}',
started_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
ended_at TIMESTAMP WITH TIME ZONE
);
-- 3. 创建 agent_message 表
CREATE TABLE agent_message (
id BIGSERIAL PRIMARY KEY,
session_id BIGINT NOT NULL,
role VARCHAR(32) NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
content TEXT NOT NULL,
intent_label VARCHAR(128),
intent_confidence DECIMAL(5,4) CHECK (intent_confidence IS NULL OR (intent_confidence >= 0 AND intent_confidence <= 1)),
embedding vector(1536),
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CONSTRAINT fk_agent_message_session FOREIGN KEY (session_id) REFERENCES agent_session(id) ON DELETE CASCADE
);
-- 4. 创建 skill_embedding 表
CREATE TABLE skill_embedding (
id BIGSERIAL PRIMARY KEY,
skill_id BIGINT NOT NULL,
skill_version_id BIGINT,
embedding_type VARCHAR(32) NOT NULL CHECK (embedding_type IN ('description', 'example', 'usage', 'combined')),
embedding vector(1536) NOT NULL,
metadata JSONB DEFAULT '{}',
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CONSTRAINT uk_skill_embedding UNIQUE(skill_id, embedding_type),
CONSTRAINT fk_skill_embedding_skill FOREIGN KEY (skill_id) REFERENCES skill(id) ON DELETE CASCADE
);
-- 5. 创建 intent_mapping 表
CREATE TABLE intent_mapping (
id BIGSERIAL PRIMARY KEY,
intent_label VARCHAR(128) UNIQUE NOT NULL,
description TEXT,
skill_categories TEXT[],
example_queries TEXT[],
embedding vector(1536) NOT NULL,
priority INTEGER DEFAULT 0 CHECK (priority >= 0),
active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- 6. 创建索引
CREATE INDEX idx_agent_session_agent_id ON agent_session(agent_id);
CREATE INDEX idx_agent_session_user_id ON agent_session(user_id);
CREATE INDEX idx_agent_session_session_id ON agent_session(session_id);
CREATE INDEX idx_agent_session_metadata ON agent_session USING GIN(metadata);
CREATE INDEX idx_agent_message_session_id ON agent_message(session_id);
CREATE INDEX idx_agent_message_intent ON agent_message(intent_label);
CREATE INDEX idx_agent_message_metadata ON agent_message USING GIN(metadata);
CREATE INDEX idx_skill_embedding_skill_id ON skill_embedding(skill_id);
CREATE INDEX idx_skill_embedding_embedding ON skill_embedding USING ivfflat(embedding vector_cosine_ops) WITH (lists = 100);
CREATE INDEX idx_skill_embedding_metadata ON skill_embedding USING GIN(metadata);
CREATE INDEX idx_intent_mapping_label ON intent_mapping(intent_label);
CREATE INDEX idx_intent_mapping_active ON intent_mapping(active);
CREATE INDEX idx_intent_mapping_embedding ON intent_mapping USING ivfflat(embedding vector_cosine_ops) WITH (lists = 50);
CREATE INDEX idx_intent_mapping_categories ON intent_mapping USING GIN(skill_categories);
```

View file

@ -0,0 +1,445 @@
# Agent Skills 智能检索系统 - 算法设计
## 1. 向量嵌入生成
### 1.1 嵌入模型配置
使用 OpenAI `text-embedding-3-small` 模型:
- 维度:1536
- 输入长度:最多 8191 tokens
- 输出格式:浮点数数组
### 1.2 技能向量生成
**输入文本构建**:
```
skill_text = skill_name + "\n" +
skill_description + "\n" +
skill_keywords + "\n" +
usage_examples
```
**生成流程**:
```python
def generate_skill_embedding(skill):
# 构建输入文本
text = build_skill_text(skill)
# 调用 OpenAI API
response = openai.Embedding.create(
model="text-embedding-3-small",
input=text
)
# 返回向量
return response['data'][0]['embedding']
```
**存储策略**:
- 为每个技能生成 4 种类型的嵌入
- `description`: 仅技能描述
- `example`: 仅示例代码
- `usage`: 仅使用说明
- `combined`: 综合所有内容
## 2. 意图识别算法
### 2.1 基于向量相似度的意图分类
**算法流程**:
```
1. 将用户查询转换为向量
query_vector = embed(query_text)
2. 与所有活跃意图的嵌入计算余弦相似度
for each intent in active_intents:
similarity = cosine_similarity(query_vector, intent.embedding)
3. 按相似度排序,取 Top-N
top_intents = sort_by_similarity(all_similarities)[:N]
4. 返回结果
return top_intents[0] as primary_intent
```
**相似度计算**:
```sql
-- 使用 pgvector 的 <=> 操作符
SELECT
intent_label,
1 - (embedding <=> :query_vector) as similarity
FROM intent_mapping
WHERE active = true
ORDER BY embedding <=> :query_vector
LIMIT 5;
```
**置信度处理**:
- 最高相似度 < 0.3:返回默认意图 "general"
- 最高相似度 >= 0.3:返回识别的意图
- 返回 Top-3 备选意图
### 2.2 意图库更新
当添加新意图时:
1. 收集该意图的示例查询
2. 生成意图嵌入向量(使用示例查询的平均向量)
3. 插入 `intent_mapping` 表
## 3. 查询向量构建
### 3.1 多维度向量融合
**公式**:
```
query_vector = normalize(
w1 * embed(query_text) +
w2 * embed(conversation_context) +
w3 * embed(scenario_description) +
w4 * embed(user_role) +
w5 * intent_embedding
)
```
**权重配置**:
| 权重 | 值 | 说明 |
|------|-----|------|
| w1 | 0.4 | 查询文本(最重要) |
| w2 | 0.2 | 对话上下文 |
| w3 | 0.15 | 场景描述 |
| w4 | 0.1 | 用户角色 |
| w5 | 0.15 | 意图向量 |
**归一化**:
```python
def normalize(vector):
norm = np.linalg.norm(vector)
if norm == 0:
return vector
return vector / norm
```
### 3.2 对话上下文编码
**策略**:使用最近 N 轮对话的摘要
```python
def encode_conversation(messages, max_turns=5):
# 取最近 N 轮对话
recent_messages = messages[-2*max_turns:]
# 构建上下文文本
context_text = "\n".join([
f"{msg.role}: {msg.content}"
for msg in recent_messages
])
# 生成嵌入
return embed(context_text)
```
## 4. 混合检索算法
### 4.1 向量检索(Vector Search)
**SQL 查询**:
```sql
SELECT
se.skill_id,
s.name,
s.summary,
1 - (se.embedding <=> :query_vector) as similarity
FROM skill_embedding se
JOIN skill s ON s.id = se.skill_id
WHERE se.embedding_type = 'description'
AND s.status = 'ACTIVE'
ORDER BY se.embedding <=> :query_vector
LIMIT 200;
```
**向量索引**:
```sql
CREATE INDEX idx_skill_embedding_embedding ON skill_embedding
USING ivfflat(embedding vector_cosine_ops)
WITH (lists = 100);
```
### 4.2 意图过滤(Intent Filter)
**过滤逻辑**:
```sql
SELECT vc.skill_id, vc.similarity
FROM vector_candidates vc
JOIN skill s ON s.id = vc.skill_id
WHERE s.category = ANY(:intent_categories)
OR vc.similarity > 0.7; -- 高相似度可跨意图
```
### 4.3 标签匹配(Tag Matching)
**精确匹配**:
```sql
SELECT s.id, COUNT(*) as match_count
FROM skill s
JOIN skill_label sl ON sl.skill_id = s.id
JOIN label_definition ld ON ld.id = sl.label_id
WHERE s.id IN (:candidate_ids)
AND ld.slug = ANY(:user_tags)
GROUP BY s.id;
```
**模糊匹配**:
- 计算标签向量与查询向量的相似度
- 相似度 > 0.6 的标签视为匹配
### 4.4 权限过滤(Permission Filter)
```sql
-- 公开技能
visibility = 'PUBLIC'
-- 用户私有技能
visibility = 'PRIVATE' AND (owner_id = :user_id OR namespace_id IN (:admin_namespaces))
-- 命名空间内可见
visibility = 'NAMESPACE_ONLY' AND namespace_id IN (:member_namespaces)
```
### 4.5 综合检索 SQL
```sql
WITH vector_candidates AS (
SELECT
se.skill_id,
1 - (se.embedding <=> :query_vector) as vector_score
FROM skill_embedding se
JOIN skill s ON s.id = se.skill_id
WHERE se.embedding_type = 'description'
AND s.status = 'ACTIVE'
ORDER BY se.embedding <=> :query_vector
LIMIT 200
),
intent_filtered AS (
SELECT
vc.skill_id,
vc.vector_score
FROM vector_candidates vc
JOIN skill s ON s.id = vc.skill_id
WHERE s.category = ANY(:intent_categories)
OR vc.vector_score > 0.7
),
permission_filtered AS (
SELECT
if.skill_id,
if.vector_score
FROM intent_filtered if
JOIN skill s ON s.id = if.skill_id
WHERE (s.visibility = 'PUBLIC')
OR (s.visibility = 'NAMESPACE_ONLY' AND s.namespace_id = ANY(:member_namespace_ids))
OR (s.visibility = 'PRIVATE' AND (s.owner_id = :user_id OR s.namespace_id = ANY(:admin_namespace_ids)))
),
tag_matched AS (
SELECT
pf.skill_id,
pf.vector_score,
COUNT(*) FILTER (WHERE ld.slug = ANY(:user_tags)) as tag_count
FROM permission_filtered pf
LEFT JOIN skill_label sl ON sl.skill_id = pf.skill_id
LEFT JOIN label_definition ld ON ld.id = sl.label_id
GROUP BY pf.skill_id, pf.vector_score
)
SELECT
tm.skill_id,
s.name,
s.summary,
s.description,
tm.vector_score,
tm.tag_count,
s.download_count,
s.rating_avg
FROM tag_matched tm
JOIN skill s ON s.id = tm.skill_id
ORDER BY tm.vector_score DESC
LIMIT :limit;
```
## 5. 重排序算法
### 5.1 多维度评分
**评分公式**:
```
final_score = α * normalize(vector_score)
+ β * normalize(intent_match_score)
+ γ * normalize(tag_match_score)
+ δ * normalize(popularity_score)
+ ε * normalize(rating_score)
```
**权重配置**:
| 权重 | 值 | 说明 |
|------|-----|------|
| α | 0.4 | 向量相似度 |
| β | 0.25 | 意图匹配度 |
| γ | 0.2 | 标签匹配度 |
| δ | 0.1 | 流行度(下载次数) |
| ε | 0.05 | 用户评分 |
### 5.2 各维度计算
**1. 向量相似度**:
```
vector_score = 1 - (skill_embedding <=> query_embedding)
范围:[0, 1]
```
**2. 意图匹配度**:
```
intent_match_score = 1.0 # 如果技能类别匹配意图
0.7 # 如果技能类别在意图的相关类别中
0.3 # 其他
```
**3. 标签匹配度**:
```
tag_match_score = min(tag_count / max_user_tags, 1.0)
范围:[0, 1]
```
**4. 流行度**:
```
popularity_score = download_count / max_download_count
使用对数缩放:log(1 + download_count) / log(1 + max_download_count)
```
**5. 用户评分**:
```
rating_score = rating_avg / 5.0
范围:[0, 1]
```
### 5.3 重排序实现
```java
public class SkillReranker {
private final double wVector = 0.4;
private final double wIntent = 0.25;
private final double wTag = 0.2;
private final double wPopularity = 0.1;
private final double wRating = 0.05;
public List<SkillMatchResult> rerank(
List<SkillCandidate> candidates,
IntentClassificationResult intent,
Set<String> userTags,
double maxDownloadCount
) {
// 计算每个候选技能的综合分数
return candidates.stream()
.map(candidate -> calculateScore(candidate, intent, userTags, maxDownloadCount))
.sorted(Comparator.comparingDouble(SkillMatchResult::finalScore).reversed())
.collect(Collectors.toList());
}
private SkillMatchResult calculateScore(
SkillCandidate candidate,
IntentClassificationResult intent,
Set<String> userTags,
double maxDownloadCount
) {
double vectorScore = candidate.vectorSimilarity();
double intentScore = calculateIntentScore(candidate, intent);
double tagScore = calculateTagScore(candidate, userTags);
double popularityScore = Math.log(1 + candidate.downloadCount())
/ Math.log(1 + maxDownloadCount);
double ratingScore = candidate.ratingAvg() / 5.0;
double finalScore = wVector * vectorScore
+ wIntent * intentScore
+ wTag * tagScore
+ wPopularity * popularityScore
+ wRating * ratingScore;
return new SkillMatchResult(
candidate.skillId(),
candidate.name(),
candidate.summary(),
vectorScore,
intentScore,
tagScore,
finalScore,
buildMatchReason(vectorScore, intentScore, tagScore)
);
}
}
```
## 6. 缓存算法
### 6.1 查询结果缓存
**缓存键**:
```
cache_key = "agent_search:" +
hash(query_text + ":" +
intent_label + ":" +
sorted(user_tags) + ":" +
user_role + ":" +
scenario_description)
```
**TTL**:5 分钟
### 6.2 嵌入向量缓存
**缓存策略**:
- 技能嵌入:永久缓存(版本更新时失效)
- 意图嵌入:永久缓存(意图更新时失效)
- 查询嵌入:TTL 1 小时
## 7. 降级策略
| 场景 | 降级方案 |
|------|----------|
| OpenAI API 调用失败 | 使用本地哈希向量 |
| 向量索引未就绪 | 使用全文搜索 |
| 意图识别失败 | 使用默认意图 |
| 响应超时 | 返回部分结果 |
## 8. 性能优化
### 8.1 向量索引调优
```sql
-- ivfflat 索引参数
-- lists = sqrt(行数)
-- 1000 行:lists = 32
-- 10000 行:lists = 100
-- 100000 行:lists = 316
```
### 8.2 查询优化
1. **限制候选集**:向量检索先返回 Top-200,再过滤
2. **异步处理**:非阻塞式嵌入生成
3. **批量查询**:减少数据库往返
### 8.3 批量向量化
```python
def batch_embed(texts, batch_size=100):
"""批量生成嵌入向量"""
all_embeddings = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i + batch_size]
response = openai.Embedding.create(
model="text-embedding-3-small",
input=batch
)
all_embeddings.extend([item['embedding'] for item in response['data']])
return all_embeddings
```

View file

@ -0,0 +1,255 @@
# Agent Skills 智能检索系统 - 流程图
## 1. 主检索流程
```mermaid
flowchart TD
A[Agent 发起技能搜索请求] --> B[Context Builder<br/>收集上下文]
B --> C{检查 Session}
C -->|存在| D[从 Redis/PG 加载历史对话]
C -->|不存在| E[创建新 Session]
D --> F[Intent Classifier<br/>意图识别]
E --> F
F --> G[Query Embedder<br/>构建查询向量]
G --> H[Hybrid Search Service<br/>混合检索]
H --> I[向量检索<br/>pgvector 相似度计算]
I --> J[意图过滤]
J --> K[标签匹配]
K --> L[权限过滤]
L --> M[Skill Reranker<br/>多维度重排序]
M --> N[Result Enricher<br/>结果丰富化]
N --> O[异步保存对话记录]
O --> P[返回匹配技能列表]
style A fill:#e1f5ff
style P fill:#c8e6c9
style F fill:#fff9c4
style H fill:#fff9c4
style M fill:#fff9c4
```
## 2. 意图识别流程
```mermaid
flowchart TD
A[接收用户查询] --> B[文本预处理<br/>清理、分词]
B --> C[调用 OpenAI Embedding API<br/>生成查询向量]
C --> D[查询活跃意图列表]
D --> E[遍历意图嵌入]
E --> F[计算余弦相似度]
F --> G{是否还有意图?}
G -->|是| E
G -->|否| H[按相似度降序排序]
H --> I{最高相似度 >= 0.3?}
I -->|是| J[返回 Top-N 意图<br/>包含置信度]
I -->|否| K[返回默认意图 'general']
J --> L[输出意图分类结果]
K --> L
style C fill:#ffebee
style F fill:#e3f2fd
style J fill:#c8e6c9
```
## 3. 向量检索流程
```mermaid
flowchart TD
A[接收查询向量] --> B[构建 pgvector 查询]
B --> C[执行 SQL 查询<br/>使用 <=> 操作符]
C --> D{向量索引可用?}
D -->|是| E[使用 ivfflat 索引<br/>快速检索]
D -->|否| F[暴力扫描计算]
E --> G[返回 Top-K 候选]
F --> G
G --> H[过滤非活跃技能]
H --> I[应用可见性过滤]
I --> J[返回候选技能列表]
style C fill:#e3f2fd
style E fill:#c8e6c9
style F fill:#fff3e0
```
## 4. 混合检索详细流程
```mermaid
flowchart TD
A[开始混合检索] --> B[阶段 1: 向量检索]
B --> B1[查询 pgvector]
B1 --> B2[获取 Top-200 候选]
B2 --> C[阶段 2: 意图过滤]
C --> C1{技能类别匹配意图?}
C1 -->|是| C2[保留候选]
C1 -->|否| C3{向量相似度 > 0.7?}
C3 -->|是| C2
C3 -->|否| C4[丢弃候选]
C2 --> D[阶段 3: 标签匹配]
D --> D1[统计匹配标签数]
D1 --> E[阶段 4: 权限过滤]
E --> E1{用户有权限访问?}
E1 -->|是| E2[保留候选]
E1 -->|否| C4
E2 --> F[返回过滤后的候选]
style B fill:#e3f2fd
style C fill:#fff9c4
style D fill:#fff9c4
style E fill:#fff9c4
style F fill:#c8e6c9
style C4 fill:#ffcdd2
```
## 5. 重排序流程
```mermaid
flowchart TD
A[接收候选技能列表] --> B[计算各维度分数]
B --> B1[向量相似度: 40%]
B --> B2[意图匹配度: 25%]
B --> B3[标签匹配度: 20%]
B --> B4[流行度: 10%]
B --> B5[用户评分: 5%]
B1 --> C[计算综合分数]
B2 --> C
B3 --> C
B4 --> C
B5 --> C
C --> D[按综合分数降序排序]
D --> E[取 Top-N 结果]
E --> F[生成分数明细]
F --> G[生成匹配原因]
G --> H[返回重排序结果]
style B fill:#e1f5ff
style C fill:#fff9c4
style H fill:#c8e6c9
```
## 6. 会话管理流程
```mermaid
flowchart TD
A[接收消息] --> B{Session ID 存在?}
B -->|是| C[从 Redis 获取 Session]
B -->|否| D[创建新 Session]
C --> E{Session 有效?}
D --> F[保存到 Redis]
E -->|是| G[追加消息到历史]
E -->|否| F
F --> G
G --> H[更新 updated_at]
H --> I[异步保存到 PostgreSQL]
I --> J[处理业务逻辑]
J --> K[返回响应]
style D fill:#fff9c4
style F fill:#e3f2fd
style I fill:#e1f5ff
style K fill:#c8e6c9
```
## 7. 技能向量生成流程
```mermaid
flowchart TD
A[技能版本发布] --> B[提取技能元数据]
B --> C[构建嵌入文本]
C --> C1[技能名称]
C --> C2[技能描述]
C --> C3[关键词]
C --> C4[使用示例]
C1 --> D[调用 OpenAI Embedding API]
C2 --> D
C3 --> D
C4 --> D
D --> E[接收向量响应]
E --> F[存储到 skill_embedding 表]
F --> G[创建 4 种类型嵌入]
G --> G1[description]
G --> G2[example]
G --> G3[usage]
G --> G4[combined]
G1 --> H[更新向量索引]
G2 --> H
G3 --> H
G4 --> H
H --> I[完成]
style D fill:#ffebee
style F fill:#e3f2fd
style H fill:#c8e6c9
```
## 8. 错误处理流程
```mermaid
flowchart TD
A[执行操作] --> B{成功?}
B -->|是| C[返回结果]
B -->|否| D{错误类型}
D -->|OpenAI API 失败| E[降级到哈希向量]
D -->|向量索引不可用| F[使用全文搜索]
D -->|意图识别失败| G[使用默认意图]
D -->|数据库连接失败| H[返回缓存结果或错误]
D -->|超时| I[返回部分结果]
E --> J[记录降级日志]
F --> J
G --> J
H --> J
I --> J
J --> C
style E fill:#fff3e0
style F fill:#fff3e0
style G fill:#fff3e0
style H fill:#ffcdd2
style I fill:#fff3e0
```
## 9. 缓存查询流程
```mermaid
flowchart TD
A[接收查询请求] --> B[生成缓存键]
B --> C{缓存命中?}
C -->|是| D[返回缓存结果]
C -->|否| E[执行完整检索]
E --> F[保存到缓存]
F --> G[返回结果]
D --> H{缓存 TTL 过期?}
H -->|是| I[异步刷新缓存]
H -->|否| J[完成]
I --> J
G --> J
style C fill:#e1f5ff
style D fill:#c8e6c9
style I fill:#fff9c4
```
## 10. 批量向量更新流程
```mermaid
flowchart TD
A[定时任务触发] --> B[查询待更新技能]
B --> C{有待更新技能?}
C -->|否| D[结束]
C -->|是| E[批量获取技能元数据]
E --> F[批量调用 Embedding API<br/>batch_size=100]
F --> G{API 调用成功?}
G -->|是| H[批量更新数据库]
G -->|否| I[记录失败日志<br/>重试队列]
H --> J[更新向量索引]
I --> K{重试次数 < 3?}
K -->|是| F
K -->|否| L[标记为失败]
J --> B
L --> B
D --> M[完成]
style F fill:#e3f2fd
style H fill:#e3f2fd
style I fill:#fff3e0
style L fill:#ffcdd2
```

View file

@ -0,0 +1,526 @@
# Agent Skills 智能检索系统 - 架构图
## 1. 系统整体架构
```mermaid
graph TB
subgraph "Client Layer"
A[Agent Client]
end
subgraph "API Gateway"
B[REST API]
C[Authentication]
end
subgraph "Application Layer"
D[Agent Skill Search Controller]
E[Agent Session Controller]
end
subgraph "Service Layer"
F[AgentContextBuilder]
G[IntentClassifier]
H[QueryEmbedder]
I[HybridSkillSearchService]
J[SkillReranker]
K[AgentSessionService]
end
subgraph "Domain Layer"
L[AgentSession]
M[AgentMessage]
N[SkillEmbedding]
O[IntentMapping]
end
subgraph "Infrastructure Layer"
P[(PostgreSQL<br/>+ pgvector)]
Q[(Redis Cache)]
R[OpenAI Embedding API]
end
A -->|HTTP/JSON| B
B --> C
C --> D
C --> E
D --> F
D --> I
E --> K
F --> K
F --> G
G --> H
H --> R
G --> O
I --> H
I --> N
I --> J
J --> L
K --> L
K --> M
L --> P
M --> P
N --> P
O --> P
K --> Q
style A fill:#e1f5ff
style R fill:#ffebee
style P fill:#c8e6c9
style Q fill:#fff9c4
```
## 2. 数据流架构
```mermaid
graph LR
subgraph "Input"
A1[Query Text]
A2[Conversation History]
A3[Scenario Description]
A4[User Role]
end
subgraph "Processing"
B1[Context Builder]
B2[Intent Classifier]
B3[Query Embedder]
B4[Hybrid Search]
B5[Reranker]
end
subgraph "Storage"
C1[(PostgreSQL)]
C2[(Redis)]
C3[OpenAI API]
end
subgraph "Output"
D1[Matched Skills]
D2[Match Reasons]
D3[Confidence Scores]
end
A1 --> B1
A2 --> B1
A3 --> B1
A4 --> B1
B1 --> B2
B1 --> B3
B2 --> C3
B3 --> C3
B2 --> B4
B3 --> B4
B4 --> C1
B4 --> B5
B5 --> D1
B5 --> D2
B5 --> D3
B1 --> C2
B2 --> C2
style A1 fill:#e1f5ff
style C3 fill:#ffebee
style D1 fill:#c8e6c9
```
## 3. 模块依赖关系
```mermaid
graph TD
A[AgentSkillSearchController] --> B[AgentContextBuilder]
A --> C[HybridSkillSearchService]
B --> D[AgentSessionService]
D --> E[AgentSessionRepository]
E --> F[(PostgreSQL)]
C --> G[QueryEmbedder]
C --> H[IntentClassifier]
C --> I[SkillReranker]
C --> J[SkillEmbeddingRepository]
G --> K[OpenAIEmbeddingClient]
K --> L[OpenAI API]
H --> G
H --> M[IntentMappingRepository]
M --> F
I --> N[SkillRepository]
I --> O[LabelRepository]
N --> F
O --> F
J --> F
D --> P[RedisTemplate]
P --> Q[(Redis)]
style A fill:#e1f5ff
style L fill:#ffebee
style F fill:#c8e6c9
style Q fill:#fff9c4
```
## 4. 数据库架构
```mermaid
erDiagram
agent_session ||--o{ agent_message : contains
skill ||--o{ skill_embedding : has
skill ||--o{ skill_label : tagged_with
label_definition ||--o{ skill_label : used_in
agent_session {
bigint id PK
varchar agent_id
varchar user_id FK
varchar session_id UK
text scenario_description
varchar user_role
jsonb metadata
timestamptz started_at
timestamptz updated_at
timestamptz ended_at
}
agent_message {
bigint id PK
bigint session_id FK
varchar role
text content
varchar intent_label
decimal intent_confidence
vector embedding
jsonb metadata
timestamptz created_at
}
skill {
bigint id PK
bigint namespace_id FK
varchar slug
varchar display_name
text summary
varchar owner_id
varchar visibility
varchar status
bigint latest_version_id
bigint download_count
int star_count
decimal rating_avg
int rating_count
}
skill_embedding {
bigint id PK
bigint skill_id FK
bigint skill_version_id FK
varchar embedding_type
vector embedding
jsonb metadata
timestamptz updated_at
}
skill_label {
bigint id PK
bigint skill_id FK
bigint label_id FK
}
label_definition {
bigint id PK
varchar name
varchar slug UK
text description
varchar color
int priority
}
intent_mapping {
bigint id PK
varchar intent_label UK
text description
text[] skill_categories
text[] example_queries
vector embedding
int priority
boolean active
timestamptz created_at
timestamptz updated_at
}
```
## 5. 部署架构
```mermaid
graph TB
subgraph "Load Balancer"
LB[Nginx / ALB]
end
subgraph "Application Servers"
APP1[SkillHub App 1]
APP2[SkillHub App 2]
APP3[SkillHub App N]
end
subgraph "Data Layer"
PG[PostgreSQL<br/>Primary]
PG_REPLICA[PostgreSQL<br/>Read Replica]
REDIS[Redis Cluster]
end
subgraph "External Services"
OPENAI[OpenAI API]
end
LB --> APP1
LB --> APP2
LB --> APP3
APP1 --> PG
APP1 --> PG_REPLICA
APP1 --> REDIS
APP1 --> OPENAI
APP2 --> PG
APP2 --> PG_REPLICA
APP2 --> REDIS
APP2 --> OPENAI
APP3 --> PG
APP3 --> PG_REPLICA
APP3 --> REDIS
APP3 --> OPENAI
PG -.->|Replication| PG_REPLICA
style LB fill:#e1f5ff
style PG fill:#c8e6c9
style REDIS fill:#fff9c4
style OPENAI fill:#ffebee
```
## 6. 缓存架构
```mermaid
graph LR
subgraph "Application"
A[Service Layer]
end
subgraph "Cache Layer - Redis"
B1[Session Cache<br/>TTL: 1h]
B2[Query Result Cache<br/>TTL: 5min]
B3[Embedding Cache<br/>TTL: 1h/Permanent]
end
subgraph "Database"
C[(PostgreSQL)]
end
A -->|Get| B1
A -->|Get| B2
A -->|Get| B3
B1 -.->|Miss| C
B2 -.->|Miss| C
B3 -.->|Miss| C
C -->|Load| B1
C -->|Load| B2
C -->|Load| B3
A -->|Update| B1
A -->|Update| B2
A -->|Update| B3
B1 -.->|Write Through| C
B2 -.->|Write Back| C
B3 -.->|Write Through| C
style A fill:#e1f5ff
style B1 fill:#fff9c4
style B2 fill:#fff9c4
style B3 fill:#fff9c4
style C fill:#c8e6c9
```
## 7. 搜索流程架构
```mermaid
graph TB
subgraph "Query Processing"
A[User Query] --> B[Context Builder]
B --> C[Intent Classifier]
C --> D[Query Embedder]
end
subgraph "Search Pipeline"
D --> E[Vector Search]
E --> F[Intent Filter]
F --> G[Tag Matcher]
G --> H[Permission Filter]
H --> I[Reranker]
end
subgraph "Data Sources"
J[(Skill Embeddings)]
K[(Intent Mapping)]
L[(Skill Metadata)]
M[(User Permissions)]
end
subgraph "Result"
I --> N[Ranked Skills]
N --> O[Response Builder]
end
E --> J
F --> K
G --> L
H --> M
I --> L
style A fill:#e1f5ff
style J fill:#c8e6c9
style K fill:#c8e6c9
style L fill:#c8e6c9
style M fill:#c8e6c9
style N fill:#c8e6c9
```
## 8. 安全架构
```mermaid
graph TB
subgraph "Client"
A[Agent Client]
end
subgraph "Security Layer"
B[Authentication<br/>JWT/OAuth2]
C[Authorization<br/>RBAC]
D[Rate Limiting]
end
subgraph "Application"
E[API Controller]
end
subgraph "Data Protection"
F[Input Validation]
G[SQL Injection Prevention]
H[Data Encryption]
end
subgraph "Infrastructure"
I[PostgreSQL]
J[Redis]
end
A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
H --> J
style B fill:#fff9c4
style C fill:#fff9c4
style D fill:#fff9c4
style G fill:#ffebee
style H fill:#ffebee
```
## 9. 监控与日志架构
```mermaid
graph TB
subgraph "Application"
A[Services]
end
subgraph "Logging"
B[Structured Logs]
C[Log Aggregation]
end
subgraph "Metrics"
D[Prometheus]
E[Custom Metrics]
end
subgraph "Tracing"
F[OpenTelemetry]
G[Jaeger/Tempo]
end
subgraph "Alerting"
H[Alert Manager]
I[Notifications]
end
A --> B
A --> E
A --> F
B --> C
E --> D
F --> G
D --> H
C --> H
H --> I
style A fill:#e1f5ff
style D fill:#c8e6c9
style G fill:#fff9c4
style H fill:#ffebee
```
## 10. 扩展性架构
```mermaid
graph TB
subgraph "Current System"
A[Core Services]
end
subgraph "Extension Points"
B1[Embedding Provider<br/>Interface]
B2[Intent Classifier<br/>Interface]
B3[Reranking Strategy<br/>Interface]
B4[Filter Plugin<br/>Interface]
end
subgraph "Possible Extensions"
C1[Local Embedding<br/>Models]
C2[Custom Intent<br/>Classifiers]
C3[Business-specific<br/>Reranking]
C4[Domain Filters]
end
A --> B1
A --> B2
A --> B3
A --> B4
B1 -.->|Implement| C1
B2 -.->|Implement| C2
B3 -.->|Implement| C3
B4 -.->|Implement| C4
style A fill:#e1f5ff
style B1 fill:#fff9c4
style B2 fill:#fff9c4
style B3 fill:#fff9c4
style B4 fill:#fff9c4
style C1 fill:#e8f5e9
style C2 fill:#e8f5e9
style C3 fill:#e8f5e9
style C4 fill:#e8f5e9
```

View file

@ -0,0 +1,606 @@
# Agent Skills 智能检索系统 - API 设计
## 1. 概述
Agent Skills 智能检索系统提供 RESTful API,支持 Agent 进行智能技能检索和会话管理。
## 2. API 基础信息
### 2.1 基础 URL
```
生产环境: https://api.skillhub.example.com/api/agent
开发环境: http://localhost:8080/api/agent
```
### 2.2 认证方式
使用 Bearer Token 认证:
```
Authorization: Bearer {access_token}
```
### 2.3 通用响应格式
**成功响应**:
```json
{
"code": 0,
"message": "success",
"data": { ... },
"timestamp": "2024-04-05T10:30:00Z"
}
```
**错误响应**:
```json
{
"code": 40001,
"message": "Invalid request parameter",
"details": {
"field": "query",
"error": "Query text cannot be empty"
},
"timestamp": "2024-04-05T10:30:00Z"
}
```
## 3. 会话管理 API
### 3.1 创建会话
**请求**:
```
POST /api/agent/sessions
```
**请求体**:
```json
{
"agentId": "agent-001",
"userId": "user-123",
"scenarioDescription": "Web 前端开发",
"userRole": "frontend-developer",
"metadata": {
"projectId": "project-456",
"environment": "development"
}
}
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"sessionId": "sess-789-xyz",
"agentId": "agent-001",
"userId": "user-123",
"scenarioDescription": "Web 前端开发",
"userRole": "frontend-developer",
"metadata": {
"projectId": "project-456",
"environment": "development"
},
"startedAt": "2024-04-05T10:30:00Z",
"updatedAt": "2024-04-05T10:30:00Z"
}
}
```
### 3.2 获取会话
**请求**:
```
GET /api/agent/sessions/{sessionId}
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"sessionId": "sess-789-xyz",
"agentId": "agent-001",
"userId": "user-123",
"scenarioDescription": "Web 前端开发",
"userRole": "frontend-developer",
"messageCount": 5,
"startedAt": "2024-04-05T10:30:00Z",
"updatedAt": "2024-04-05T10:35:00Z",
"endedAt": null
}
}
```
### 3.3 获取会话历史
**请求**:
```
GET /api/agent/sessions/{sessionId}/messages?limit=10&offset=0
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"messages": [
{
"id": 1,
"role": "user",
"content": "我需要一个处理 JSON 数据的技能",
"intentLabel": "data_processing",
"intentConfidence": 0.92,
"createdAt": "2024-04-05T10:30:00Z"
},
{
"id": 2,
"role": "assistant",
"content": "我为您找到了几个相关技能...",
"createdAt": "2024-04-05T10:30:05Z"
}
],
"total": 5,
"limit": 10,
"offset": 0
}
}
```
### 3.4 结束会话
**请求**:
```
POST /api/agent/sessions/{sessionId}/end
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"sessionId": "sess-789-xyz",
"endedAt": "2024-04-05T11:00:00Z"
}
}
```
## 4. 技能搜索 API
### 4.1 智能技能搜索
**请求**:
```
POST /api/agent/skills/search
```
**请求体**:
```json
{
"query": "我需要一个处理 JSON 数据的技能",
"sessionId": "sess-789-xyz",
"limit": 10,
"options": {
"enableIntentRecognition": true,
"enableContextAware": true,
"tags": ["json", "data-processing"],
"intent": "data_processing",
"minConfidence": 0.3
}
}
```
**参数说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| query | string | 是 | 搜索查询文本 |
| sessionId | string | 否 | 会话 ID,用于上下文感知 |
| limit | integer | 否 | 返回结果数量,默认 10 |
| options.enableIntentRecognition | boolean | 否 | 是否启用意图识别,默认 true |
| options.enableContextAware | boolean | 否 | 是否启用上下文感知,默认 true |
| options.tags | string[] | 否 | 指定标签过滤 |
| options.intent | string | 否 | 指定意图(跳过自动识别) |
| options.minConfidence | number | 否 | 最小置信度,默认 0.3 |
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"query": "我需要一个处理 JSON 数据的技能",
"intent": {
"label": "data_processing",
"confidence": 0.92,
"alternatives": [
{
"label": "file_processing",
"confidence": 0.75
}
]
},
"results": [
{
"skillId": 123,
"name": "json-parser",
"displayName": "JSON Parser",
"namespace": "default",
"summary": "强大的 JSON 解析和处理库",
"version": "1.2.3",
"scores": {
"vectorSimilarity": 0.89,
"intentMatchScore": 1.0,
"tagMatchScore": 0.8,
"popularityScore": 0.65,
"ratingScore": 0.88,
"finalScore": 0.87
},
"matchReason": "高度匹配:查询向量相似度 0.89,意图完全匹配,标签匹配度 80%",
"tags": ["json", "parser", "data"],
"downloadCount": 1520,
"ratingAvg": 4.4,
"ratingCount": 45
},
{
"skillId": 456,
"name": "data-transformer",
"displayName": "Data Transformer",
"namespace": "utils",
"summary": "通用数据转换工具",
"version": "2.1.0",
"scores": {
"vectorSimilarity": 0.76,
"intentMatchScore": 1.0,
"tagMatchScore": 0.6,
"popularityScore": 0.45,
"ratingScore": 0.8,
"finalScore": 0.72
},
"matchReason": "良好匹配:意图完全匹配,部分标签匹配",
"tags": ["data", "transform"],
"downloadCount": 890,
"ratingAvg": 4.0,
"ratingCount": 28
}
],
"total": 45,
"limit": 10,
"processingTimeMs": 245
}
}
```
### 4.2 快速搜索(简化版)
**请求**:
```
GET /api/agent/skills/search?q={query}&limit={limit}
```
**示例**:
```
GET /api/agent/skills/search?q=json%20parser&limit=5
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"query": "json parser",
"results": [
{
"skillId": 123,
"name": "json-parser",
"displayName": "JSON Parser",
"namespace": "default",
"summary": "强大的 JSON 解析和处理库",
"version": "1.2.3",
"score": 0.87
}
]
}
}
```
## 5. 意图管理 API
### 5.1 获取所有意图
**请求**:
```
GET /api/agent/intents?activeOnly=true
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"intents": [
{
"id": 1,
"label": "code_generation",
"description": "代码生成相关技能",
"skillCategories": ["coding", "development"],
"exampleQueries": [
"写一个函数",
"生成代码",
"创建类"
],
"priority": 10,
"active": true
},
{
"id": 2,
"label": "data_analysis",
"description": "数据分析相关技能",
"skillCategories": ["data", "analysis"],
"exampleQueries": [
"分析数据",
"绘制图表",
"数据统计"
],
"priority": 10,
"active": true
}
]
}
}
```
### 5.2 识别意图
**请求**:
```
POST /api/agent/intents/recognize
```
**请求体**:
```json
{
"query": "我需要生成一个处理用户认证的函数"
}
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"primaryIntent": {
"label": "code_generation",
"confidence": 0.88
},
"alternativeIntents": [
{
"label": "authentication",
"confidence": 0.72
},
{
"label": "general",
"confidence": 0.15
}
],
"processingTimeMs": 85
}
}
```
## 6. 批量操作 API
### 6.1 批量生成技能嵌入
**请求**:
```
POST /api/agent/skills/embeddings/batch
```
**请求体**:
```json
{
"skillIds": [123, 456, 789],
"embeddingTypes": ["description", "combined"]
}
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"taskId": "task-001-xyz",
"status": "processing",
"totalCount": 3,
"successCount": 0,
"failureCount": 0,
"estimatedCompletion": "2024-04-05T10:35:00Z"
}
}
```
### 6.2 查询批量任务状态
**请求**:
```
GET /api/agent/tasks/{taskId}
```
**响应**:
```json
{
"code": 0,
"message": "success",
"data": {
"taskId": "task-001-xyz",
"status": "completed",
"totalCount": 3,
"successCount": 3,
"failureCount": 0,
"results": [
{
"skillId": 123,
"status": "success",
"embeddingTypes": ["description", "combined"]
}
],
"startedAt": "2024-04-05T10:30:00Z",
"completedAt": "2024-04-05T10:31:15Z"
}
}
```
## 7. 错误码
| 错误码 | 说明 | HTTP 状态码 |
|--------|------|-------------|
| 0 | 成功 | 200 |
| 40001 | 请求参数错误 | 400 |
| 40002 | 缺少必填参数 | 400 |
| 40101 | 未认证 | 401 |
| 40301 | 无权限访问 | 403 |
| 40401 | 资源不存在 | 404 |
| 40901 | 资源冲突 | 409 |
| 42901 | 请求频率超限 | 429 |
| 50001 | 服务器内部错误 | 500 |
| 50301 | 服务不可用 | 503 |
| 50002 | OpenAI API 调用失败 | 500 |
| 50003 | 向量索引不可用 | 500 |
## 8. 速率限制
| 端点 | 限制 | 时间窗口 |
|------|------|----------|
| POST /api/agent/skills/search | 100 | 1分钟 |
| GET /api/agent/sessions/{id} | 200 | 1分钟 |
| POST /api/agent/sessions | 50 | 1分钟 |
| POST /api/agent/intents/recognize | 60 | 1分钟 |
**响应头**:
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1712289000
```
## 9. Webhook 通知
### 9.1 向量生成完成通知
**请求体**:
```json
{
"eventType": "embedding.completed",
"timestamp": "2024-04-05T10:30:00Z",
"data": {
"skillId": 123,
"skillVersionId": 456,
"embeddingTypes": ["description", "combined"],
"status": "success"
}
}
```
## 10. SDK 示例
### 10.1 JavaScript/TypeScript
```typescript
import { SkillHubAgentClient } from '@skillhub/agent-sdk';
const client = new SkillHubAgentClient({
baseURL: 'https://api.skillhub.example.com/api/agent',
apiKey: 'your-api-key'
});
// 创建会话
const session = await client.createSession({
agentId: 'agent-001',
userId: 'user-123',
scenarioDescription: 'Web 前端开发',
userRole: 'frontend-developer'
});
// 搜索技能
const results = await client.searchSkills({
query: '我需要一个处理 JSON 数据的技能',
sessionId: session.sessionId,
limit: 10
});
console.log(results.results);
```
### 10.2 Python
```python
from skillhub_agent import SkillHubAgentClient
client = SkillHubAgentClient(
base_url='https://api.skillhub.example.com/api/agent',
api_key='your-api-key'
)
# 创建会话
session = client.create_session(
agent_id='agent-001',
user_id='user-123',
scenario_description='Web 前端开发',
user_role='frontend-developer'
)
# 搜索技能
results = client.search_skills(
query='我需要一个处理 JSON 数据的技能',
session_id=session['sessionId'],
limit=10
)
print(results['results'])
```
### 10.3 Java
```java
import com.iflytek.skillhub.agent.SkillHubAgentClient;
import com.iflytek.skillhub.agent.dto.*;
SkillHubAgentClient client = new SkillHubAgentClient(
"https://api.skillhub.example.com/api/agent",
"your-api-key"
);
// 创建会话
CreateSessionRequest sessionRequest = CreateSessionRequest.builder()
.agentId("agent-001")
.userId("user-123")
.scenarioDescription("Web 前端开发")
.userRole("frontend-developer")
.build();
AgentSession session = client.createSession(sessionRequest);
// 搜索技能
SearchSkillsRequest searchRequest = SearchSkillsRequest.builder()
.query("我需要一个处理 JSON 数据的技能")
.sessionId(session.getSessionId())
.limit(10)
.build();
SearchSkillsResponse results = client.searchSkills(searchRequest);
results.getResults().forEach(skill -> {
System.out.println(skill.getName() + ": " + skill.getFinalScore());
});
```

View file

@ -0,0 +1,648 @@
# Agent Skills 智能检索系统 - 实施指南
## 1. 实施阶段
### 阶段 1:基础设施准备(1-2 周)
#### 1.1 数据库配置
**安装 pgvector 扩展**:
```bash
# 连接到 PostgreSQL
psql -U postgres -d skillhub
# 安装 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
# 验证安装
SELECT * FROM pg_extension WHERE extname = 'vector';
# 查看可用版本
SELECT * FROM pg_available_extensions WHERE name = 'vector';
```
**配置连接池**(application.yml):
```yaml
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
```
#### 1.2 Redis 配置
```yaml
spring:
data:
redis:
host: localhost
port: 6379
password: ${REDIS_PASSWORD}
database: 0
timeout: 5000ms
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
```
#### 1.3 OpenAI API 配置
```yaml
openai:
api-key: ${OPENAI_API_KEY}
embedding:
model: text-embedding-3-small
dimension: 1536
timeout: 30000
max-retries: 3
```
### 阶段 2:数据库迁移(1 周)
#### 2.1 创建迁移脚本
创建 `V__create_agent_tables.sql`:
```sql
-- 参见 03-data-model.md 中的完整 SQL
-- 1. 安装 pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- 2. 创建 agent_session 表
...
-- 3. 创建 agent_message 表
...
-- 4. 创建 skill_embedding 表
...
-- 5. 创建 intent_mapping 表
...
-- 6. 创建索引
...
-- 7. 插入初始数据
INSERT INTO intent_mapping (intent_label, description, skill_categories, example_queries, priority, active)
VALUES
('code_generation', '代码生成相关技能', ARRAY['coding', 'development'], ARRAY['写一个函数', '生成代码', '创建类'], 10, true),
('data_analysis', '数据分析相关技能', ARRAY['data', 'analysis'], ARRAY['分析数据', '绘制图表', '数据统计'], 10, true),
...
;
```
#### 2.2 执行迁移
```bash
# 使用 Flyway
mvn flyway:migrate
# 或使用 Spring Boot 自动迁移
mvn spring-boot:run -Dspring.flyway.enabled=true
```
### 阶段 3:核心服务开发(2-3 周)
#### 3.1 领域模型
创建 `server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/agent/` 目录:
```java
// AgentSession.java
@Entity
@Table(name = "agent_session")
public class AgentSession {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "agent_id", nullable = false)
private String agentId;
@Column(name = "user_id", nullable = false)
private String userId;
@Column(name = "session_id", nullable = false, unique = true)
private String sessionId;
// ... 其他字段
@PrePersist
protected void onCreate() {
startedAt = Instant.now();
updatedAt = Instant.now();
}
}
```
#### 3.2 Repository 接口
```java
// AgentSessionRepository.java
@Repository
public interface AgentSessionRepository extends JpaRepository<AgentSession, Long> {
Optional<AgentSession> findBySessionId(String sessionId);
List<AgentSession> findByUserIdAndEndedAtIsNull(String userId);
@Query("SELECT a FROM AgentSession a WHERE a.sessionId = :sessionId AND a.endedAt IS NULL")
Optional<AgentSession> findActiveBySessionId(@Param("sessionId") String sessionId);
}
```
#### 3.3 服务实现
**AgentContextBuilder.java**:
```java
@Service
public class AgentContextBuilderImpl implements AgentContextBuilder {
private final AgentSessionService sessionService;
private final int maxHistorySize;
@Override
public AgentSearchContext build(AgentSearchRequest request) {
// 获取会话历史
List<AgentMessage> history = request.sessionId() != null
? sessionService.getRecentMessages(request.sessionId(), maxHistorySize)
: List.of();
return new AgentSearchContext(
request.query(),
history,
request.scenarioDescription(),
request.userRole(),
request.metadata()
);
}
}
```
**IntentClassifier.java**:
```java
@Service
public class VectorIntentClassifier implements IntentClassifier {
private final IntentMappingRepository intentRepository;
private final OpenAiEmbeddingClient embeddingClient;
private final double minConfidence = 0.3;
@Override
public IntentClassificationResult classify(String query, AgentSearchContext context) {
// 生成查询向量
float[] queryVector = embeddingClient.embed(query);
// 获取所有活跃意图
List<IntentMapping> intents = intentRepository.findByActiveTrue();
// 计算相似度
List<IntentScore> scores = intents.stream()
.map(intent -> new IntentScore(
intent.getIntentLabel(),
cosineSimilarity(queryVector, parseVector(intent.getEmbedding()))
))
.sorted(Comparator.comparingDouble(IntentScore::score).reversed())
.toList();
if (scores.isEmpty() || scores.get(0).score() < minConfidence) {
return new IntentClassificationResult("general", 0.0, List.of());
}
return new IntentClassificationResult(
scores.get(0).label(),
scores.get(0).score(),
scores.subList(1, Math.min(3, scores.size()))
.stream()
.map(IntentScore::label)
.toList()
);
}
private double cosineSimilarity(float[] a, float[] b) {
double dot = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
```
#### 3.4 OpenAI 集成
**OpenAiEmbeddingClient.java**:
```java
@Service
public class OpenAiEmbeddingClient {
private final String apiKey;
private final String model;
private final RestTemplate restTemplate;
public float[] embed(String text) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(apiKey);
Map<String, Object> body = Map.of(
"model", model,
"input", text
);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
OpenAiResponse response = restTemplate.postForObject(
"https://api.openai.com/v1/embeddings",
request,
OpenAiResponse.class
);
return response.data().get(0).embedding();
}
}
```
### 阶段 4:向量生成(1 周)
#### 4.1 批量向量生成任务
```java
@Service
public class SkillEmbeddingGenerator {
@Scheduled(fixedDelay = 3600000) // 每小时检查一次
public void generatePendingEmbeddings() {
List<Skill> skills = skillRepository.findSkillsWithoutEmbeddings();
for (Skill skill : skills) {
try {
generateEmbeddings(skill);
} catch (Exception e) {
log.error("Failed to generate embeddings for skill {}", skill.getId(), e);
}
}
}
private void generateEmbeddings(Skill skill) {
String description = skill.getSummary() != null ? skill.getSummary() : "";
float[] descVector = embeddingClient.embed(description);
SkillEmbedding embedding = new SkillEmbedding();
embedding.setSkillId(skill.getId());
embedding.setEmbeddingType(EmbeddingType.DESCRIPTION);
embedding.setEmbedding(serializeVector(descVector));
embedding.setUpdatedAt(Instant.now());
embeddingRepository.save(embedding);
}
}
```
#### 4.2 手动触发向量生成
```java
@RestController
@RequestMapping("/api/admin/skills")
public class AdminSkillController {
@PostMapping("/{skillId}/embeddings")
public ResponseEntity<?> generateEmbeddings(@PathVariable Long skillId) {
Skill skill = skillRepository.findById(skillId)
.orElseThrow(() -> new NotFoundException("Skill not found"));
embeddingGenerator.generateEmbeddings(skill);
return ResponseEntity.ok(Map.of("status", "processing"));
}
}
```
### 阶段 5:API 开发(1 周)
#### 5.1 Controller 实现
**AgentSkillSearchController.java**:
```java
@RestController
@RequestMapping("/api/agent")
@RequiredArgsConstructor
public class AgentSkillSearchController {
private final HybridSkillSearchService searchService;
@PostMapping("/skills/search")
public ResponseEntity<ApiResponse<SearchResult>> searchSkills(
@Valid @RequestBody AgentSearchRequest request
) {
SearchResult result = searchService.search(request);
return ResponseEntity.ok(ApiResponse.success(result));
}
@PostMapping("/sessions")
public ResponseEntity<ApiResponse<AgentSession>> createSession(
@Valid @RequestBody CreateSessionRequest request
) {
AgentSession session = sessionService.createSession(request);
return ResponseEntity.ok(ApiResponse.success(session));
}
}
```
#### 5.2 DTO 定义
```java
public record AgentSearchRequest(
@NotBlank String query,
String sessionId,
@Min(1) @Max(100) Integer limit,
SearchOptions options
) {
public AgentSearchRequest {
if (limit == null) limit = 10;
if (options == null) options = new SearchOptions();
}
}
public record SearchOptions(
Boolean enableIntentRecognition,
Boolean enableContextAware,
List<String> tags,
String intent,
Double minConfidence
) {
public SearchOptions {
if (enableIntentRecognition == null) enableIntentRecognition = true;
if (enableContextAware == null) enableContextAware = true;
if (minConfidence == null) minConfidence = 0.3;
}
}
```
### 阶段 6:测试(1-2 周)
#### 6.1 单元测试
```java
@SpringBootTest
class IntentClassifierTest {
@Autowired
private IntentClassifier intentClassifier;
@Test
void testClassifyCodeGenerationIntent() {
String query = "写一个快速排序算法";
IntentClassificationResult result = intentClassifier.classify(query, null);
assertEquals("code_generation", result.intentLabel());
assertTrue(result.confidence() > 0.5);
}
}
```
#### 6.2 集成测试
```java
@SpringBootTest
@AutoConfigureMockMvc
class AgentSkillSearchIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void testSearchSkillsEndpoint() throws Exception {
String request = """
{
"query": "处理 JSON 数据",
"sessionId": "test-session-123",
"limit": 5
}
""";
mockMvc.perform(post("/api/agent/skills/search")
.contentType(MediaType.APPLICATION_JSON)
.content(request))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0))
.andExpect(jsonPath("$.data.results").isArray());
}
}
```
### 阶段 7:部署(1 周)
#### 7.1 Docker 配置
**Dockerfile**:
```dockerfile
FROM openjdk:21-slim
WORKDIR /app
COPY target/skillhub-app.jar /app/
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "skillhub-app.jar"]
```
#### 7.2 Kubernetes 配置
**deployment.yaml**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: skillhub-agent
spec:
replicas: 3
selector:
matchLabels:
app: skillhub-agent
template:
metadata:
labels:
app: skillhub-agent
spec:
containers:
- name: skillhub
image: skillhub/agent:latest
ports:
- containerPort: 8080
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: skillhub-secrets
key: database-url
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: skillhub-secrets
key: openai-api-key
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
```
### 阶段 8:监控与调优(持续)
#### 8.1 监控指标
```java
@Component
public class SearchMetrics {
private final MeterRegistry meterRegistry;
public void recordSearch(SearchResult result, long duration) {
meterRegistry.counter("skill.search.total",
"intent", result.intent().label()
).increment();
meterRegistry.timer("skill.search.duration").record(duration, TimeUnit.MILLISECONDS);
result.results().forEach(r ->
meterRegistry.histogram("skill.search.score").record(r.finalScore())
);
}
}
```
#### 8.2 性能调优
**向量索引调优**:
```sql
-- 根据数据量调整 lists 参数
-- 建议值:sqrt(行数)
CREATE INDEX idx_skill_embedding_embedding ON skill_embedding
USING ivfflat(embedding vector_cosine_ops)
WITH (lists = 100);
```
**缓存配置优化**:
```yaml
skillhub:
agent:
search:
cache:
enabled: true
ttl: 300 # 5分钟
max-size: 10000
```
## 9. 检查清单
### 准备阶段
- [ ] PostgreSQL 17 安装完成
- [ ] pgvector 扩展安装完成
- [ ] Redis 7.0 配置完成
- [ ] OpenAI API Key 获取
- [ ] 数据库迁移脚本准备
### 开发阶段
- [ ] 领域模型定义完成
- [ ] Repository 接口定义完成
- [ ] 核心服务实现完成
- [ ] OpenAI 集成完成
- [ ] 混合检索实现完成
- [ ] API 端点实现完成
### 测试阶段
- [ ] 单元测试覆盖率 > 80%
- [ ] 集成测试通过
- [ ] 性能测试达标(< 500ms)
- [ ] 负载测试通过(100+ QPS)
### 部署阶段
- [ ] Docker 镜像构建完成
- [ ] Kubernetes 配置完成
- [ ] 环境变量配置完成
- [ ] 健康检查配置完成
- [ ] 日志收集配置完成
### 上线后
- [ ] 监控指标配置完成
- [ ] 告警规则配置完成
- [ ] 文档更新完成
- [ ] 团队培训完成
## 10. 常见问题
### Q1: pgvector 索引创建失败
**原因**:数据量不足或参数不当
**解决**:
```sql
-- 确保有足够的数据(至少 100 行)
-- 调整 lists 参数
CREATE INDEX idx_skill_embedding_embedding ON skill_embedding
USING ivfflat(embedding vector_cosine_ops)
WITH (lists = 32);
```
### Q2: OpenAI API 调用超时
**原因**:网络延迟或 API 限制
**解决**:
```yaml
openai:
embedding:
timeout: 60000 # 增加超时时间
max-retries: 5 # 增加重试次数
```
### Q3: 搜索结果不准确
**原因**:向量未生成或权重配置不当
**解决**:
1. 检查技能向量是否生成
2. 调整重排序权重
3. 增加意图类别和示例
### Q4: 内存占用过高
**原因**:向量数据量大,缓存过多
**解决**:
```yaml
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
skillhub:
agent:
search:
cache:
max-size: 5000 # 减少缓存大小
```
## 11. 参考资源
- [pgvector 官方文档](https://github.com/pgvector/pgvector)
- [OpenAI Embeddings API](https://platform.openai.com/docs/guides/embeddings)
- [Spring Boot 文档](https://spring.io/projects/spring-boot)
- [PostgreSQL 性能调优](https://wiki.postgresql.org/wiki/Performance_Optimization)

View file

@ -0,0 +1,231 @@
# MinIO 迁移到 Ceph 方案
## 背景
当前项目使用 MinIO 作为 S3 兼容对象存储,现需迁移到已部署的 Ceph RGW。由于测试环境暂无生产数据,无需数据迁移,直接配置切换即可。
## 关键发现
**代码层面无需修改**:
- 项目已有 `ObjectStorageService` 抽象接口
- `S3StorageService` 使用 AWS S3 SDK v2,完全兼容 Ceph RGW
- 通过 `@ConditionalOnProperty(name = "skillhub.storage.provider")` 条件装配
## 改动点
### 1. K8s ConfigMap 配置
**文件**: `deploy/k8s/base/configmap.yaml`
```yaml
# 修改存储提供商
skillhub-storage-provider: s3 # 原值: local
# 添加 Ceph S3 配置
skillhub-storage-s3-endpoint: http://ceph-rgw:7480
skillhub-storage-s3-public-endpoint: https://ceph.example.com:7480
skillhub-storage-s3-bucket: skillhub
skillhub-storage-s3-region: default
skillhub-storage-s3-force-path-style: "true"
skillhub-storage-s3-auto-create-bucket: "true"
skillhub-storage-s3-presign-expiry: PT10M
```
**移除本地存储配置**(可选):
```yaml
# 注释或删除
# storage-base-path: /var/lib/skillhub/storage
```
### 2. K8s Secret 配置
**文件**: `deploy/k8s/base/secret.yaml.example`
```yaml
# 添加 Ceph 凭证
skillhub-storage-s3-access-key: your-ceph-access-key
skillhub-storage-s3-secret-key: your-ceph-secret-key
```
创建实际 Secret:
```bash
kubectl create secret generic skillhub-secret \
--from-literal=skillhub-storage-s3-access-key=your-access-key \
--from-literal=skillhub-storage-s3-secret-key=your-secret-key \
--dry-run=client -o yaml | kubectl apply -f -
```
### 3. Backend Deployment 配置
**文件**: `deploy/k8s/base/backend-deployment.yaml`
**添加 S3 环境变量**(在 env 部分添加):
```yaml
# Storage - S3
- name: SKILLHUB_STORAGE_S3_ENDPOINT
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-endpoint
- name: SKILLHUB_STORAGE_S3_PUBLIC_ENDPOINT
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-public-endpoint
- name: SKILLHUB_STORAGE_S3_BUCKET
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-bucket
- name: SKILLHUB_STORAGE_S3_ACCESS_KEY
valueFrom:
secretKeyRef:
name: skillhub-secret
key: skillhub-storage-s3-access-key
- name: SKILLHUB_STORAGE_S3_SECRET_KEY
valueFrom:
secretKeyRef:
name: skillhub-secret
key: skillhub-storage-s3-secret-key
- name: SKILLHUB_STORAGE_S3_REGION
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-region
- name: SKILLHUB_STORAGE_S3_FORCE_PATH_STYLE
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-force-path-style
- name: SKILLHUB_STORAGE_S3_AUTO_CREATE_BUCKET
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-auto-create-bucket
- name: SKILLHUB_STORAGE_S3_PRESIGN_EXPIRY
valueFrom:
configMapKeyRef:
name: skillhub-config
key: skillhub-storage-s3-presign-expiry
```
**移除本地存储挂载**(可选):
```yaml
# 注释或删除 volumeMounts 中的 skillhub-storage
# 注释或删除 volumes 中的 skillhub-storage-pvc
```
### 4. Docker Compose 配置
**文件**: `docker-compose.yml`
**移除 MinIO 服务**:
```yaml
# minio:
# image: minio/minio:RELEASE.2025-09-07T16-13-09Z
# ports:
# - "9000:9000"
# - "9001:9001"
# ...
```
### 5. ConfigMap 中移除 PVC 定义
**文件**: `deploy/k8s/base/configmap.yaml`
```yaml
# 注释或删除 PersistentVolumeClaim 部分
# ---
# apiVersion: v1
# kind: PersistentVolumeClaim
# metadata:
# name: skillhub-storage-pvc
# ...
```
## 执行步骤
1. **更新 ConfigMap**
```bash
kubectl apply -f deploy/k8s/base/configmap.yaml
```
2. **创建/更新 Secret**
```bash
kubectl create secret generic skillhub-secret \
--from-literal=skillhub-storage-s3-access-key=<your-key> \
--from-literal=skillhub-storage-s3-secret-key=<your-secret> \
--dry-run=client -o yaml | kubectl apply -f -
```
3. **更新 Deployment**
```bash
kubectl apply -f deploy/k8s/base/backend-deployment.yaml
```
4. **重启 Pod**
```bash
kubectl rollout restart deployment/skillhub-server
```
5. **验证配置**
```bash
kubectl logs -f deployment/skillhub-server
```
## 验证测试
### 功能验证清单
- [ ] Pod 正常启动,无错误日志
- [ ] 上传技能包
- [ ] 下载技能包
- [ ] 生成预签名 URL
- [ ] 删除技能包
### 验证命令
```bash
# 检查 Pod 日志
kubectl logs deployment/skillhub-server
# 检查环境变量
kubectl exec deployment/skillhub-server -- env | grep STORAGE
# 测试上传(通过 API)
curl -X POST http://<service>/api/skills/{namespace}/publish \
-F "file=@test.zip" \
-F "visibility=public"
```
## 关键文件清单
| 文件路径 | 操作 |
|---------|------|
| `deploy/k8s/base/configmap.yaml` | 修改:添加 S3 配置 |
| `deploy/k8s/base/secret.yaml.example` | 修改:添加 S3 凭证模板 |
| `deploy/k8s/base/backend-deployment.yaml` | 修改:添加 S3 环境变量 |
| `docker-compose.yml` | 修改:移除 MinIO 服务 |
## 注意事项
1. **Ceph 网络连通性**:确保集群能访问 Ceph RGW 的 endpoint
2. **Bucket 创建**:建议提前创建 bucket,或启用 `auto-create-bucket`
3. **Region 配置**:Ceph 通常使用 `default` region
4. **Path Style**:Ceph RGW 需要启用 `force-path-style`
5. **SSL/TLS**:生产环境建议使用 HTTPS
## 回滚方案
如果出现问题,快速回滚:
```bash
# 1. 恢复 ConfigMap
git checkout deploy/k8s/base/configmap.yaml
kubectl apply -f deploy/k8s/base/configmap.yaml
# 2. 恢复 Deployment
git checkout deploy/k8s/base/backend-deployment.yaml
kubectl apply -f deploy/k8s/base/backend-deployment.yaml
# 3. 重启服务
kubectl rollout restart deployment/skillhub-server
```

View file

@ -65,4 +65,4 @@ EXPOSE 8000
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8000/health || exit 1
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000"]
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000", "--root-path", "/skillhub"]

View file

@ -39,4 +39,4 @@ RUN mkdir -p /tmp/skillhub-scans
EXPOSE 8000
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000"]
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000", "--root-path", "/skillhub"]

View file

@ -63,4 +63,4 @@ EXPOSE 8000
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8000/health || exit 1
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000"]
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000", "--root-path", "/skillhub"]

View file

@ -45,4 +45,4 @@ EXPOSE 8000
HEALTHCHECK --interval=10s --timeout=3s \
CMD wget -qO- http://127.0.0.1:8000/health || exit 1
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000"]
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000", "--root-path", "/skillhub"]

View file

@ -46,4 +46,4 @@ EXPOSE 8000
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8000/health || exit 1
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000"]
CMD ["skill-scanner-api", "--host", "0.0.0.0", "--port", "8000", "--root-path", "/skillhub"]

View file

@ -3,6 +3,10 @@ spring:
hibernate:
ddl-auto: validate
show-sql: true
properties:
hibernate:
format_sql: true
use_sql_comments: true
flyway:
enabled: true
datasource:
@ -51,3 +55,5 @@ logging:
level:
com.iflytek.skillhub: INFO
org.springframework.security: WARN
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE