前言
本文基于之前的本地大模型对话Demo进行优化,重点介绍如何将内存存储升级为PostgreSQL持久化存储,并增强对话安全性与检索准确性。仍需在Node.js 18+环境下运行,核心依赖工具(Ollama、PostgreSQL)的安装请参考官方文档。
为何升级为PostgreSQL存储?
内存存储虽适合快速原型验证,但存在重启后数据丢失的问题,无法满足实际应用场景。PostgreSQL作为成熟的关系型数据库,不仅能持久化存储对话记录,配合pgvector插件还能高效管理向量数据,为生产环境提供可靠支撑。
代码实现:从内存存储到PostgreSQL持久化
1. 数据库配置与实例初始化
修改内容:新增PostgreSQL配置,替代原内存存储的sessionStores设计
arduino
// 替代原内存存储的全局配置
const pgConfig = {
host: "127.0.0.1",
port: 5432,
user: "fanxiaosi",
password: "f15130026310.",
database: "One-AI-DB",
};
修改原因:内存存储无法持久化数据,服务重启后对话历史会丢失。
改进好处:使用PostgreSQL实现对话记录的永久存储,支持多实例共享数据,为分布式部署奠定基础。
2. 语言模型配置优化
修改内容:在原有基础上增加防复读配置
arduino
const llm = new ChatOllama({
model: 'deepseek-r1:1.5b',
baseUrl: "http://127.0.0.1:11434",
topP: 0.9, // 从1.0调整为0.9
topK: 40, // 从20调整为40
streaming: true,
temperature: 0, // 新增:固定输出温度,保证结果稳定性
repeatPenalty: 1.2, // 新增:防止多轮对话后复读系统指令
})
修改原因:原配置可能导致输出多样性不足或出现重复内容。
改进好处:通过repeatPenalty减少复读现象,调整topP/topK平衡输出多样性与相关性,固定temperature确保回复稳定性。
3. 提示词模板增强
修改内容:优化提示词模板结构,增加背景信息注入点
css
const textPrompt = ChatPromptTemplate.fromMessages([ ["system", "你是一个专业的知识助手。只需回答用户问题,不要重复系统指令。如果无法从背景信息中找到答案,请基于已知对话回复。"],
["placeholder", "{chat_history}"],
["human", "背景信息:{relevant_history}\n\n当前问题:{user_input}"] // 新增背景信息字段
]);
修改原因:原模板仅依赖完整历史对话,长对话场景下可能超出模型上下文窗口。
改进好处:通过{relevant_history}注入检索到的相关片段,既保证上下文相关性,又避免全量历史导致的窗口溢出问题。
4. 敏感词检查机制强化
修改内容:完善敏感词检查的错误处理流程
ini
const profanityChecker = (input: { user_input: string }) => {
const sensitiveWords = ["傻瓜", "白痴", "不良内容"];
const userInput = input.user_input;
if (sensitiveWords.some(word => userInput.includes(word))) {
throw new Error("检测到敏感内容。请使用文明用语。"); // 明确的错误提示
}
return input; // 检查通过则透传输入
};
const customCheckStep = RunnableLambda.from(profanityChecker);
修改原因:原实现未明确错误处理机制,可能导致异常传播不规范。
改进好处:通过抛出结构化错误,使前端能清晰捕获敏感词提示,同时中断后续处理流程,提升系统安全性。
5. 向量存储重构:单例模式+PGVector
修改内容:用单例模式管理PGVectorStore,替代原内存向量库
php
// 替代原MemoryVectorStore的实现
let globalVectorStore: PGVectorStore | null = null;
export const getVectorStore = async (): Promise<PGVectorStore> => {
if (!globalVectorStore) {
globalVectorStore = await PGVectorStore.initialize(embeddingModel, {
postgresConnectionOptions: pgConfig,
tableName: "test_langchain_embeddings",
columns: {
idColumnName: "id",
vectorColumnName: "embedding",
contentColumnName: "text",
metadataColumnName: "metadata",
},
});
}
return globalVectorStore;
};
修改原因:原内存向量库存在数据易失性和多实例同步问题。
改进好处:
- 单例模式避免重复创建数据库连接池,减少资源消耗
- PGVectorStore支持向量数据的持久化存储和高效检索
- 结构化的表设计便于后期数据维护和分析
6. 对话历史存储优化
修改内容:新增对话内容清洗逻辑,增强检索准确性
typescript
export const addMessageToVectorStore = async (
sessionId: string,
humanInput: string,
aiResponse?: string
) => {
// 新增:清洗AI回复中的特殊标签
const cleanAIResponse = aiResponse?.replace(/[\s\S]*?</think>/g, "").trim();
const vectorStore = await getVectorStore();
const messageText = cleanAIResponse
? `用户:${humanInput}\n助手:${cleanAIResponse}`
: `用户:${humanInput}`;
await vectorStore.addDocuments([{
pageContent: messageText,
metadata: {
sessionId,
timestamp: Date.now(),
type: aiResponse ? "qa_pair" : "user_query" // 新增类型标识
}
}]);
};
修改原因:原实现可能存储未经处理的模型输出(含思考过程标签),影响检索精度。
改进好处:
- 移除特殊标签确保存储内容纯净度
- 增加type字段区分消息类型,便于后续检索过滤
- 标准化存储格式提升语义匹配准确性
7. 检索逻辑优化
修改内容:增强检索器的会话隔离和结果去重能力
typescript
export const retrieveRelevantHistory = async (
userInput: string,
sessionId: string
) => {
const vectorStore = await getVectorStore();
const retriever = vectorStore.asRetriever({
searchType: "mmr",
searchKwargs: {
fetchK: 20, // 扩大候选集
lambda: 0.7, // 平衡相关性和多样性
},
k: 3, // 小模型适配的最优返回数量
filter: { sessionId }, // 严格的会话隔离
});
const relevantDocs = await retriever.invoke(userInput);
return relevantDocs.length > 0
? relevantDocs.map(doc => doc.pageContent).join("\n\n")
: ""; // 空字符串替代原"无相关背景知识"
};
修改原因:原检索逻辑未严格隔离会话,且返回无关提示可能干扰模型。
改进好处:
- 通过filter确保只检索当前会话历史,避免跨会话信息泄露
- 调整k值适配1.5B小模型的上下文处理能力
- 返回空字符串替代提示文本,防止干扰模型判断
8. 链式调用流程重构
修改内容:优化调用链的参数传递逻辑
javascript
const ragChain = RunnableSequence.from([
customCheckStep, // 优先执行敏感词检查
{
user_input: (input) => input.user_input,
sessionId: (_input, config) => config.configurable?.sessionId,
chat_history: (input) => input.chat_history,
},
RunnablePassthrough.assign({
relevant_history: retrieveStep, // 注入检索结果
}),
textPrompt,
llm,
]);
修改原因:原链式调用参数传递不够清晰,可能导致上下文丢失。
改进好处:
- 明确参数映射关系,提升代码可读性
- 将敏感词检查置于流程最前端,提前拦截不良内容
- 分离参数提取与检索逻辑,便于单独调试
9. 会话历史管理升级
修改内容:使用PostgresChatMessageHistory替代内存存储
typescript
const textChainWithHistory = new RunnableWithMessageHistory<any, any>({
runnable: ragChain,
getMessageHistory: (sessionId: string) => {
return new PostgresChatMessageHistory({
tableName: "chat_messages",
sessionId,
poolConfig: pgConfig,
});
},
inputMessagesKey: "user_input",
historyMessagesKey: "chat_history",
} as any);
修改原因:原InMemoryChatMessageHistory无法持久化存储对话记录。
改进好处:
- 实现对话历史的持久化存储,服务重启后不丢失
- 与PostgreSQL向量库形成数据联动,便于数据备份与迁移
- 支持跨进程共享会话历史,为集群部署提供可能
10. 服务器与请求处理增强
修改内容:完善HTTP服务器的错误处理和跨域支持
typescript
const app = http.createServer(async (req, res) => {
// 新增:过滤浏览器图标请求
if (req.url === "/favicon.ico") {
res.writeHead(404);
res.end();
return;
}
// 增强跨域处理
if (req.method === "OPTIONS") {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
res.writeHead(204);
res.end();
return;
}
try {
// 统一参数解析逻辑
const params = await parseRequestParams(req);
const sessionId = params.sessionId || "default-session-id";
const user_input = params.user_input;
// 输入验证
if (!user_input) {
res.writeHead(400).end("Missing user_input");
return;
}
// 流式响应处理
res.writeHead(200, {
"Content-Type": "text/event-stream; charset=utf-8",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
});
const stream = await textChainWithHistory.stream(
{ user_input },
{ configurable: { sessionId } }
);
let fullSummary = "";
for await (const chunk of stream) {
const content = chunk.content || "";
fullSummary += content;
res.write(`data: ${JSON.stringify({ content })}\n\n`);
}
// 新增:回复完成后存入向量库
if (fullSummary) {
await addMessageToVectorStore(sessionId, user_input, fullSummary);
}
res.write(`data: ${JSON.stringify({ done: true })}\n\n`);
res.end();
} catch (error: any) {
// 错误信息通过SSE传递
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
}
})
修改原因:原服务器实现缺乏完整的错误处理和请求过滤机制。
改进好处:
- 增加图标请求过滤,减少无效处理
- 完善的错误捕获与前端通知机制
- 标准化SSE响应格式,便于前端处理
- 确保AI回复在生成完成后才存入向量库,避免部分内容存储
结语
本次优化通过PostgreSQL实现了对话数据的持久化存储,增强了系统的稳定性和安全性,同时优化了检索精度和模型输出质量。相比内存存储方案,新版Demo更接近生产环境需求,可作为企业级本地大模型应用的基础框架。建议结合实际业务场景进一步扩展用户认证、权限控制等功能。
总结
- 核心升级方向:内存存储→PostgreSQL持久化,解决数据易失问题,适配生产场景;
- 关键优化点:敏感词前置检查、检索会话隔离、模型防复读配置,提升安全性与回复质量;
- 体验增强:标准化SSE流式响应、完善的错误处理,降低前端对接成本。