不是泛泛的“提示词工程”,而是从底层架构到生产级代码的全链路实战。
2026年的AI工具早已不是“调个API、写个prompt”那么简单。企业级场景下,我们需要的是可编排、可溯源、可观测的智能系统。RAG(检索增强生成)作为当前最务实的落地范式,其技术栈已经演变为:
本文将基于腾讯云混元大模型 API 和 LangChain,从零搭建一个具备“文档解析→切片→向量化→混合检索→生成回答”完整链路的RAG系统,并部署到腾讯云服务器上。
组件 | 选型 | 理由 |
|---|---|---|
大模型 | 腾讯云混元 hunyuan-pro | 中文理解强,支持32K上下文,企业级SLA |
向量模型 | 腾讯云 hunyuan-embedding | 与混元同源,768维,延迟<50ms |
向量数据库 | PGVector(云上PostgreSQL插件) | 降低额外运维成本,支持ACID |
文档解析 | Unstructured + Tesseract OCR | 支持PDF/Word/图片中的表格和公式 |
检索策略 | 混合检索(稠密+稀疏)+ RRF重排 | 兼顾语义匹配和关键词命中 |
编排框架 | LangChain v0.3 + LCEL | 声明式链式调用,易于缓存和流式输出 |
部署环境 | 腾讯云CVM(8C16G)+ Ubuntu 22.04 | 生产级稳定性 |
hunyuan-pro的API Key。pgvector扩展(CREATE EXTENSION vector;)。python -m venv rag_env
source rag_env/bin/activate
pip install --upgrade piprequirements.txt:
langchain==0.3.7
langchain-core==0.3.0
langchain-community==0.3.0
langchain-openai # 实际用混元,但兼容openai接口
pypdf==4.3.0
unstructured[pdf]==0.15.0
pytesseract==0.3.13
psycopg2-binary==2.9.10
pgvector==0.2.5
tiktoken==0.7.0
tencentcloud-sdk-python==3.0.1200 # 混元官方SDK
fastapi==0.115.5
uvicorn==0.32.0由于LangChain原生未内置混元,我们通过继承BaseChatModel实现自定义,或使用ChatOpenAI兼容模式(混元支持OpenAI协议)。这里采用官方SDK封装,保证最佳性能。
# hunyuan_llm.py
import json
from typing import Any, List, Mapping, Optional, Iterator
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from langchain_core.language_models.chat_models import BaseChatModel
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage
from langchain_core.outputs import ChatGeneration, ChatResult
from tencentcloud.common import credential
from tencentcloud.hunyuan.v20230901 import hunyuan_client, models
class HunyuanChat(BaseChatModel):
secret_id: str
secret_key: str
region: str = "ap-guangzhou"
model: str = "hunyuan-pro"
temperature: float = 0.7
top_p: float = 0.95
stream: bool = False
def _generate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs,
) -> ChatResult:
# 将LangChain消息转为混元格式
contents = []
for msg in messages:
if isinstance(msg, SystemMessage):
role = "system"
elif isinstance(msg, HumanMessage):
role = "user"
elif isinstance(msg, AIMessage):
role = "assistant"
else:
continue
contents.append({"Role": role, "Content": msg.content})
cred = credential.Credential(self.secret_id, self.secret_key)
client = hunyuan_client.HunyuanClient(cred, self.region)
req = models.ChatCompletionsRequest()
req.Model = self.model
req.Messages = contents
req.Temperature = self.temperature
req.TopP = self.top_p
req.Stream = False # 流式在另一个方法实现
resp = client.ChatCompletions(req)
content = resp.Choices[0].Message.Content
message = AIMessage(content=content)
generation = ChatGeneration(message=message)
return ChatResult(generations=[generation])
def _stream(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs,
) -> Iterator[ChatGeneration]:
# 流式实现略(可参考腾讯云SDK流式接口)
pass
@property
def _llm_type(self) -> str:
return "hunyuan"采用RecursiveCharacterTextSplitter,并针对表格和代码块进行保护,避免切碎关键结构。
# chunker.py
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
def smart_chunk(documents: List[Document], chunk_size=512, overlap=50):
splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""],
chunk_size=chunk_size,
chunk_overlap=overlap,
length_function=len,
keep_separator=True,
)
chunks = splitter.split_documents(documents)
# 添加元数据:来源文件名、页码(若有)
for i, chunk in enumerate(chunks):
chunk.metadata["chunk_id"] = i
return chunks使用PGVector同时存储稠密向量(embedding)和全文检索字段(tsvector)。
-- 建表语句(通过SQLAlchemy执行)
CREATE TABLE IF NOT EXISTS doc_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content TEXT NOT NULL,
embedding vector(768),
metadata JSONB,
ts_content tsvector GENERATED ALWAYS AS (to_tsvector('chinese', content)) STORED
);
CREATE INDEX idx_embedding ON doc_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
CREATE INDEX idx_tsv ON doc_chunks USING GIN (ts_content);LangChain的PGVector集成(需自定义,因为原版不支持混合检索)。我们直接使用psycopg2执行原生SQL实现混合检索。
# hybrid_retriever.py
import psycopg2
from pgvector.psycopg2 import register_vector
import numpy as np
from typing import List, Tuple
class HybridRetriever:
def __init__(self, conn_string: str, embedding_model):
self.conn = psycopg2.connect(conn_string)
register_vector(self.conn)
self.embed_model = embedding_model # 使用混元embedding
def _get_embedding(self, text: str) -> List[float]:
# 调用混元embedding API
from tencentcloud.hunyuan.v20230901 import models
# ... 实际调用省略,返回768维向量
pass
def hybrid_search(self, query: str, top_k: int = 5, alpha: float = 0.5):
query_vec = self._get_embedding(query)
# 稠密检索(余弦相似度)
dense_sql = """
SELECT id, content, metadata, 1 - (embedding <=> %s) AS score
FROM doc_chunks
ORDER BY embedding <=> %s
LIMIT %s * 2
"""
# 稀疏检索(全文匹配)
sparse_sql = """
SELECT id, content, metadata, ts_rank(ts_content, plainto_tsquery('chinese', %s)) AS score
FROM doc_chunks
WHERE ts_content @@ plainto_tsquery('chinese', %s)
ORDER BY score DESC
LIMIT %s * 2
"""
# 分别执行,然后RRF重排
with self.conn.cursor() as cur:
cur.execute(dense_sql, (query_vec, query_vec, top_k))
dense_results = cur.fetchall()
cur.execute(sparse_sql, (query, query, top_k))
sparse_results = cur.fetchall()
# RRF (Reciprocal Rank Fusion)
scores = {}
for rank, (doc_id, content, meta, score) in enumerate(dense_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (rank + 60)
for rank, (doc_id, content, meta, score) in enumerate(sparse_results):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (rank + 60)
sorted_ids = sorted(scores.keys(), key=lambda x: scores[x], reverse=True)[:top_k]
# 重新获取完整内容
final = []
for doc_id in sorted_ids:
cur.execute("SELECT content, metadata FROM doc_chunks WHERE id = %s", (doc_id,))
final.append(cur.fetchone())
return final# rag_chain.py
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from hunyuan_llm import HunyuanChat
from hybrid_retriever import HybridRetriever
# 初始化
llm = HunyuanChat(secret_id="***", secret_key="***")
retriever = HybridRetriever(conn_string="postgresql://...", embedding_model=...)
# 提示模板(包含上下文和问题)
template = """
你是一个专业的企业知识助手。根据以下参考资料回答用户的问题。
如果参考资料不足以回答,请明确说明“根据现有资料无法回答”,不要编造。
参考资料:
{context}
用户问题:{question}
请给出准确、简洁的回答,并在回答末尾用 [citation:序号] 引用对应的参考资料编号。
"""
prompt = ChatPromptTemplate.from_template(template)
def format_docs(docs):
return "\n\n".join([f"[{i+1}] {doc[0]}" for i, doc in enumerate(docs)])
# LCEL链
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 调用
answer = rag_chain.invoke("2025年Q3的销售目标是多少?")
print(answer)# server.py
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.responses import StreamingResponse
import json
import tempfile
from chunker import smart_chunk
from rag_chain import rag_chain
app = FastAPI()
@app.post("/upload")
async def upload_document(file: UploadFile = File(...)):
# 保存临时文件,调用unstructured解析
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
content = await file.read()
tmp.write(content)
tmp.flush()
# 解析为LangChain Document列表
from langchain_community.document_loaders import UnstructuredPDFLoader
loader = UnstructuredPDFLoader(tmp.name)
docs = loader.load()
chunks = smart_chunk(docs)
# 存入PGVector(调用存储逻辑)
# ...
return {"status": "ok", "chunks": len(chunks)}
@app.post("/chat")
async def chat_stream(question: str = Form(...)):
def generate():
for chunk in rag_chain.stream(question):
yield f"data: {json.dumps({'token': chunk})}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")对高频相似问题,缓存生成结果,TTL=1小时。使用langchain.caches.RedisCache。
from langchain.globals import set_llm_cache
from langchain.cache import RedisCache
import redis
redis_client = redis.Redis(host='localhost', port=6379)
set_llm_cache(RedisCache(redis_client))ivfflat 的 lists 参数设为 sqrt(行数),约100~200。pg_prewarm预热索引,减少冷启动延迟。将HybridRetriever的连接池改为asyncpg,配合FastAPI的异步特性,提升吞吐量。
import asyncpg
from pgvector.asyncpg import register_vector
class AsyncHybridRetriever:
async def __init__(self, dsn):
self.pool = await asyncpg.create_pool(dsn)
await self.pool.fetch("CREATE EXTENSION IF NOT EXISTS vector")
await register_vector(self.pool)import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your_key"
os.environ["LANGCHAIN_PROJECT"] = "hunyuan-rag-prod"FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
apt-get update && apt-get install -y tesseract-ocr tesseract-ocr-chi-sim poppler-utils
COPY . .
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]docker build -t ccr.ccs.tencentyun.com/your-namespace/rag:v1 .
docker push ...
ssh cvm "docker run -d -p 8000:8000 --env-file .env ccr.ccs.tencentyun.com/your-namespace/rag:v1"location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off; # 流式必需
}使用locust模拟100并发用户,每个用户提3个问题,观察:
关键调优点:
本文从接口封装、混合检索、流式输出到云上部署,完整呈现了基于混元大模型的RAG系统落地路径。新一代AI工具的核心能力不在于“调用模型”,而在于如何将业务数据与模型能力融合成可靠的自动化流水线。后续可以扩展:
Rerank模型(如Cohere)进一步提升排序精度代码已全部托管至腾讯云开源社区(附仓库链接,此处略)。希望这篇实战能帮助你在企业场景中真正掌握新一代AI工具,而不是停留在“调参侠”阶段。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。