存储系统
存储系统负责数据的持久化,包括配置、对话历史、记忆等。
存储层级
┌─────────────────────────────────────────┐
│ 应用层 │
│ ChatService / SkillsAgent / ... │
├─────────────────────────────────────────┤
│ 服务层 │
│ DatabaseService / MemoryService │
├─────────────────────────────────────────┤
│ 存储层 │
│ SQLite / Redis / 向量数据库 │
└─────────────────────────────────────────┘SQLite 数据库
主要用于结构化数据存储:
javascript
// services/storage/DatabaseService.js
import Database from 'better-sqlite3'
export class DatabaseService {
constructor(dbPath) {
this.db = new Database(dbPath)
this.init()
}
init() {
// 创建表
this.db.exec(`
CREATE TABLE IF NOT EXISTS conversations (
id TEXT PRIMARY KEY,
user_id TEXT,
group_id TEXT,
messages TEXT,
created_at INTEGER,
updated_at INTEGER
);
CREATE TABLE IF NOT EXISTS memories (
id TEXT PRIMARY KEY,
user_id TEXT,
content TEXT,
embedding BLOB,
created_at INTEGER
);
CREATE INDEX IF NOT EXISTS idx_conv_user
ON conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_conv_group
ON conversations(group_id);
`)
}
// 保存对话
saveConversation(id, userId, groupId, messages) {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO conversations
(id, user_id, group_id, messages, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`)
const now = Date.now()
stmt.run(id, userId, groupId, JSON.stringify(messages), now, now)
}
// 获取对话
getConversation(userId, groupId) {
const stmt = this.db.prepare(`
SELECT * FROM conversations
WHERE user_id = ? AND group_id = ?
ORDER BY updated_at DESC
LIMIT 1
`)
const row = stmt.get(userId, groupId)
if (row) {
row.messages = JSON.parse(row.messages)
}
return row
}
}Redis 缓存
用于高频访问数据的缓存:
javascript
// core/cache/RedisClient.js
import Redis from 'ioredis'
export class RedisClient {
constructor(options) {
this.client = new Redis(options)
}
async get(key) {
const value = await this.client.get(key)
return value ? JSON.parse(value) : null
}
async set(key, value, ttl = 3600) {
await this.client.setex(key, ttl, JSON.stringify(value))
}
async del(key) {
await this.client.del(key)
}
// 缓存装饰器
async cached(key, ttl, fn) {
const cached = await this.get(key)
if (cached) return cached
const result = await fn()
await this.set(key, result, ttl)
return result
}
}向量数据库
用于语义搜索和记忆检索:
javascript
// services/storage/VectorStore.js
import { LocalIndex } from 'vectra'
export class VectorStore {
constructor(options) {
this.index = new LocalIndex(options.path)
this.embedder = options.embedder
}
async init() {
if (!await this.index.isIndexCreated()) {
await this.index.createIndex()
}
}
// 添加文档
async add(id, text, metadata = {}) {
const vector = await this.embedder.embed(text)
await this.index.insertItem({
id,
vector,
metadata: { text, ...metadata }
})
}
// 语义搜索
async search(query, limit = 5) {
const vector = await this.embedder.embed(query)
const results = await this.index.queryItems(vector, limit)
return results
.filter(r => r.score > 0.7)
.map(r => ({
id: r.item.id,
text: r.item.metadata.text,
score: r.score
}))
}
// 删除
async delete(id) {
await this.index.deleteItem(id)
}
}记忆服务
javascript
// services/storage/MemoryService.js
export class MemoryService {
constructor(options) {
this.db = options.db
this.vectorStore = options.vectorStore
}
// 保存记忆
async save(userId, content) {
const id = generateId()
// 保存到 SQLite
this.db.saveMemory(id, userId, content)
// 保存到向量库
await this.vectorStore.add(id, content, { userId })
return id
}
// 检索相关记忆
async retrieve(userId, query, limit = 5) {
// 语义搜索
const results = await this.vectorStore.search(query, limit * 2)
// 过滤用户记忆
return results
.filter(r => r.metadata.userId === userId)
.slice(0, limit)
}
// 清除用户记忆
async clear(userId) {
const memories = this.db.getMemoriesByUser(userId)
for (const memory of memories) {
await this.vectorStore.delete(memory.id)
}
this.db.clearMemories(userId)
}
}数据模型
对话
typescript
interface Conversation {
id: string
userId: string
groupId?: string
messages: Message[]
createdAt: number
updatedAt: number
}
interface Message {
role: 'system' | 'user' | 'assistant' | 'tool'
content: string
toolCalls?: ToolCall[]
toolCallId?: string
}记忆
typescript
interface Memory {
id: string
userId: string
content: string
embedding: number[]
category?: string
createdAt: number
}数据迁移
javascript
// 版本迁移
const migrations = [
{
version: 1,
up: (db) => {
db.exec(`
ALTER TABLE conversations
ADD COLUMN preset_id TEXT
`)
}
},
{
version: 2,
up: (db) => {
db.exec(`
CREATE TABLE tool_logs (...)
`)
}
}
]
function migrate(db) {
const currentVersion = db.pragma('user_version', { simple: true })
for (const migration of migrations) {
if (migration.version > currentVersion) {
migration.up(db)
db.pragma(`user_version = ${migration.version}`)
}
}
}