在市场监管领域,政策法规更新频繁,企业和公众对政策咨询的需求日益增长。传统的人工咨询服务存在响应速度慢、信息更新不及时、服务时间受限等问题。我们基于Go语言技术栈和千问大模型,开发了一套智能问答系统,能够7×24小时提供准确、高效的政策咨询服务。
这套系统的核心价值在于:
提示:系统采用微服务架构设计,各组件可独立扩展,特别适合需要处理大量并发查询的政务场景。
系统采用典型的三层架构:
code复制前端展示层(Vue) → 业务逻辑层(Gin) → 数据服务层(PostgreSQL+pgvector)
↓
大模型服务层(千问API)
关键设计考量:
| 组件类型 | 候选方案 | 最终选择 | 选择理由 |
|---|---|---|---|
| Web框架 | Gin, Echo, Beego | Gin | 性能最优(每秒可处理3.4万请求) |
| ORM | GORM, XORM | GORM | 社区活跃,文档完善 |
| 向量数据库 | pgvector, Milvus | pgvector | 无需额外维护,与业务数据天然集成 |
| 大模型 | 千问, GPT, Claude | 千问 | 合规可控,中文理解能力强 |
完整的知识库构建包含以下步骤:
go复制func preprocessPDF(content []byte) (string, error) {
// 提取原始文本
rawText, err := pdf.ExtractText(content)
if err != nil {
return "", err
}
// 文本清洗
cleanText := strings.ReplaceAll(rawText, "\x00", "") // 去除空字符
cleanText = regexp.MustCompile(`\s+`).ReplaceAllString(cleanText, " ")
return cleanText, nil
}
go复制func generateEmbeddings(text string) ([]float64, error) {
// 调用千问嵌入API
resp, err := qianwenClient.GetEmbedding(text)
if err != nil {
return nil, err
}
// 校验维度
if len(resp.Embedding) != 1536 {
return nil, fmt.Errorf("unexpected embedding dimension")
}
return resp.Embedding, nil
}
检索流程优化点:
go复制func hybridRetrieve(query string) ([]Document, error) {
// 第一轮:关键词检索
keywordResults, _ := fullTextSearch(query)
// 第二轮:向量检索
vectorResults, _ := vectorSearch(query)
// 结果融合
return mergeResults(keywordResults, vectorResults), nil
}
SSE服务端关键实现:
go复制func streamHandler(c *gin.Context) {
// 设置SSE头
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
// 创建通道
messageChan := make(chan string)
go generateAnswers(c.Query("q"), messageChan)
// 实时推送
for msg := range messageChan {
c.SSEvent("message", msg)
c.Writer.Flush()
}
}
客户端处理示例:
javascript复制const eventSource = new EventSource('/api/stream?q=营业执照办理');
eventSource.onmessage = (e) => {
document.getElementById('answer').innerHTML += e.data;
};
sql复制CREATE INDEX idx_knowledge_chunks_embedding
ON knowledge_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
go复制// 使用预编译语句
stmt := db.Statement.Where(
"embedding <=> ? < ?",
pgvector.NewVector(embedding),
0.8,
).Order("embedding <=> ?", pgvector.NewVector(embedding))
text复制你是一位市场监管专家,请根据以下法规条款回答问题:
{{context}}
问题:{{question}}
要求:
1. 回答需引用具体条款
2. 语言简洁专业
3. 不确定时明确说明
go复制ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := qianwenClient.Chat(ctx, request)
优化后的docker-compose.yml:
yaml复制services:
postgres:
image: pgvector/pgvector:pg16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
app:
build: .
deploy:
resources:
limits:
cpus: '2'
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
关键监控项:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 向量检索慢 | HNSW参数不合理 | 调整m和ef参数 |
| 回答不准确 | 检索上下文不足 | 增加top_k值 |
| 流式中断 | 网络超时 | 调整keepalive时间 |
| PDF解析乱码 | 编码问题 | 增加UTF-8清洗步骤 |
实测优化效果:
在实际部署中,我们发现pgvector的HNSW索引需要根据数据规模动态调整参数,对于100万条以下的数据量,m=16和ef_construction=64是比较理想的配置。同时建议定期执行VACUUM ANALYZE维护索引性能。