时间:2026-03-18 19:47
人气:
作者:admin
在AI应用(如智能客服、代码助手、个性化推荐)中,上下文理解是决定用户体验的核心能力。然而,当前基于大语言模型(LLM)的应用普遍面临三大痛点:
本文提出一套基于向量数据库与动态会话管理的上下文理解增强方案,通过“会话存储-向量检索-动态修剪-上下文融合”的全流程架构,解决上述问题。读者将掌握:
本文适合有AI应用开发经验的架构师/资深工程师,需具备LLM基础(如GPT-4/ChatGLM)、RESTful API设计、Docker使用经验。
LLM 的“智能”依赖于对上下文的理解——用户的当前查询往往需要结合历史对话才能准确响应。例如:
本文方案的核心是**“会话管理+向量检索+动态修剪”**的三元架构,以下是关键概念解析:
{
"session_id": "user_123",
"history": [
{"role": "user", "content": "帮我订机票"},
{"role": "assistant", "content": "请问目的地是?"},
{"role": "user", "content": "北京"}
]
}
用户请求 → API网关 → 会话管理服务(获取历史)→ 向量数据库(检索相关上下文)→ 动态修剪(调整长度)→ 上下文融合(生成prompt)→ LLM调用(获取响应)→ 会话管理服务(更新历史)→ 向量数据库(更新Embedding)→ 返回响应
| 工具/库 | 版本 | 用途 |
|---|---|---|
| Python | 3.10+ | 后端服务开发 |
| FastAPI | 0.100+ | 构建RESTful API |
| Redis | 7.0+ | 会话管理 |
| Pinecone | 2.2.4+ | 向量数据库 |
| LangChain | 0.0.300+ | LLM框架(简化调用) |
| OpenAI | 0.27.0+ | LLM API(或用ChatGLM替代) |
| Docker | 24.0+ | 容器化部署 |
fastapi==0.100.0
uvicorn==0.23.0
redis==4.6.0
pinecone-client==2.2.4
langchain==0.0.300
openai==0.27.0
python-dotenv==1.0.0
tiktoken==0.4.0 # token计算库
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
OPENAI_API_KEY=your_openai_key
PINECONE_API_KEY=your_pinecone_key
PINECONE_INDEX_NAME=context-index
REDIS_URL=redis://redis:6379/0
# 启动Redis容器
docker run -d --name redis -p 6379:6379 redis:latest
# 启动FastAPI服务(需先构建镜像)
docker build -t context-service .
docker run -d --name context-service -p 8000:8000 --link redis:redis context-service
用FastAPI实现会话的创建、添加、获取接口,底层用Redis存储。
核心代码(main.py):
from fastapi import FastAPI, HTTPException
from redis import Redis
from pydantic import BaseModel
import json
app = FastAPI(title="上下文管理服务")
redis = Redis(host="redis", port=6379, db=0)
class Dialogue(BaseModel):
session_id: str
role: str # "user"或"assistant"
content: str
# 创建会话
@app.post("/sessions/{session_id}")
async def create_session(session_id: str):
if redis.exists(session_id):
raise HTTPException(status_code=400, detail="会话已存在")
redis.hset(session_id, "history", json.dumps([]))
return {"message": "会话创建成功"}
# 添加对话
@app.post("/dialogues")
async def add_dialogue(dialogue: Dialogue):
if not redis.exists(dialogue.session_id):
raise HTTPException(status_code=404, detail="会话不存在")
history = json.loads(redis.hget(dialogue.session_id, "history"))
history.append({"role": dialogue.role, "content": dialogue.content})
redis.hset(dialogue.session_id, "history", json.dumps(history))
return {"message": "对话添加成功"}
# 获取会话历史
@app.get("/sessions/{session_id}/history")
async def get_history(session_id: str):
if not redis.exists(session_id):
raise HTTPException(status_code=404, detail="会话不存在")
history = json.loads(redis.hget(session_id, "history"))
return {"history": history}
测试接口:
用Postman发送POST /sessions/user_123创建会话,再发送POST /dialogues添加对话:
{
"session_id": "user_123",
"role": "user",
"content": "帮我订一张明天去北京的机票"
}
调用GET /sessions/user_123/history可获取历史对话。
在Pinecone中创建索引,并实现对话Embedding的存储与检索。
核心代码(vector_db.py):
import pinecone
from langchain.embeddings import OpenAIEmbeddings
from dotenv import load_dotenv
load_dotenv()
embeddings = OpenAIEmbeddings()
pinecone.init(api_key=os.getenv("PINECONE_API_KEY"), environment="us-west1-gcp")
# 创建索引(仅需执行一次)
def create_pinecone_index(index_name: str):
if index_name not in pinecone.list_indexes():
pinecone.create_index(
name=index_name,
dimension=1536, # text-embedding-ada-002的维度
metric="cosine",
pods=1,
pod_type="p1.x1"
)
# 存储对话Embedding
def store_context(session_id: str, content: str):
index = pinecone.Index(os.getenv("PINECONE_INDEX_NAME"))
embedding = embeddings.embed_query(content)
index.upsert([(
f"{session_id}:{len(content)}", # 唯一ID(session_id+内容长度)
embedding,
{"session_id": session_id, "content": content} # 元数据
)])
# 检索相关上下文
def retrieve_context(session_id: str, query: str, k: int = 3):
index = pinecone.Index(os.getenv("PINECONE_INDEX_NAME"))
query_embedding = embeddings.embed_query(query)
results = index.query(
vector=query_embedding,
filter={"session_id": session_id}, # 按session_id过滤
top_k=k,
include_metadata=True
)
return [res["metadata"]["content"] for res in results["matches"]]
使用示例:
# 创建索引(首次运行)
create_pinecone_index("context-index")
# 存储对话
store_context("user_123", "帮我订一张明天去北京的机票")
# 检索相关上下文(当用户问“改成高铁”时)
related_context = retrieve_context("user_123", "改成高铁")
print(related_context) # 输出:["帮我订一张明天去北京的机票"]
用tiktoken计算token数量,根据LLM的上下文窗口限制修剪历史对话。
核心代码(context_trimmer.py):
import tiktoken
from typing import List
def trim_context(context: List[str], max_tokens: int = 6000, model: str = "gpt-4"):
"""
动态修剪上下文,确保总token数不超过max_tokens
:param context: 待修剪的上下文列表(每个元素是对话内容)
:param max_tokens: 最大token数(根据LLM的上下文窗口调整)
:param model: LLM模型(用于选择token编码器)
:return: 修剪后的上下文列表
"""
encoder = tiktoken.encoding_for_model(model)
total_tokens = 0
trimmed_context = []
# 倒序遍历,优先保留最新的对话
for item in reversed(context):
item_tokens = len(encoder.encode(item))
if total_tokens + item_tokens > max_tokens:
break
trimmed_context.append(item)
total_tokens += item_tokens
# 反转回来,保持对话顺序
return list(reversed(trimmed_context))
使用示例:
history = [
"帮我订一张明天去北京的机票",
"请问目的地是?",
"北京",
"请问你要订什么舱位?",
"经济舱"
]
trimmed = trim_context(history, max_tokens=200)
print(trimmed) # 输出:["帮我订一张明天去北京的机票", "请问目的地是?", "北京", "请问你要订什么舱位?", "经济舱"](假设总token≤200)
将会话管理、向量检索、动态修剪整合,生成最终的prompt并调用LLM。
核心代码(llm_integration.py):
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, AIMessage
from session_manager import get_history
from vector_db import retrieve_context
from context_trimmer import trim_context
from dotenv import load_dotenv
load_dotenv()
llm = ChatOpenAI(model_name="gpt-4", temperature=0)
def generate_response(session_id: str, query: str):
# 1. 获取会话历史
history = get_history(session_id)["history"]
# 2. 检索相关上下文(从向量数据库)
related_context = retrieve_context(session_id, query)
# 3. 融合历史对话与相关上下文
context = [msg["content"] for msg in history] + related_context
# 4. 动态修剪上下文
trimmed_context = trim_context(context)
# 5. 生成prompt(用LangChain的Message格式)
messages = [HumanMessage(content=ctx) for ctx in trimmed_context] + [HumanMessage(content=query)]
# 6. 调用LLM
response = llm(messages)
# 7. 更新会话历史与向量数据库
add_dialogue(session_id, "user", query)
add_dialogue(session_id, "assistant", response.content)
store_context(session_id, response.content)
return response.content
测试示例:
# 用户发送新查询:“改成高铁”
response = generate_response("user_123", "改成高铁")
print(response) # 输出:“好的,已将你的机票订单改为明天去北京的高铁票,经济舱。”
embeddings.embed_query("test")获取)。redis.expire(session_id, 86400),即24小时后过期)。POST /api/query(整合后的接口)
{
"session_id": "user_123",
"query": "改成高铁"
}
{
"response": "好的,已将你的机票订单改为明天去北京的高铁票,经济舱。"
}
namespace功能),提高检索速度;delete API删除session_id对应的向量);batch API),减少API请求次数;appendonly yes),并设置appendfsync everysec(每秒同步一次)。max_tokens参数(如从6000降到5000),或优化动态修剪策略(如提高相似度阈值)。当前方案仅支持文本上下文,未来可扩展到图片、语音等多模态:
当前的动态修剪策略是基于规则的(如相似度≥0.7),未来可用**强化学习(RL)**优化:
当前方案仅支持单会话的上下文,未来可扩展到跨会话:
本文提出的上下文理解增强方案,通过“会话管理+向量检索+动态修剪”的架构,解决了LLM应用中上下文丢失、token溢出、分布式一致性等问题。读者通过本文可以掌握:
随着AI应用的普及,上下文理解将成为差异化竞争的关键。希望本文能为架构师们提供实用的参考,帮助大家构建更智能、更贴合用户需求的AI应用。
(注:以上链接为示例,实际请替换为自己的仓库地址。)