面向复杂场景的大模型应用落地与能力优化实践
大模型在真实业务中的落地,难点往往不在模型本身,而在工程化:如何管理多轮上下文、控制推理成本、稳定输出结构化结果。本文以一个"智能客服知识问答"为例,演示从 Prompt 设计到检索增强、再到结果校验的完整链路。
一、场景与问题定义
业务方要求:根据企业内部知识库回答用户问题,支持多轮追问,且回答必须引用知识原文,禁止编造。
三个核心痛点:
- 幻觉:模型自由发挥,答案脱离知识库;
- 上下文失控:多轮对话中历史消息无限累积,Token 成本高、噪声大;
- 输出不可解析:下游系统需要结构化字段,直接输出文本难以对接。
二、解决方案架构
采用 RAG(检索增强生成)+ 滑动窗口记忆 + JSON 结构化输出 三段式设计。
用户提问
│
▼
① 记忆裁剪(滑动窗口,保留最近 N 轮)
│
▼
② 向量检索(知识库 Top-K 召回)
│
▼
③ 结构化 Prompt(限定引用原文,强制 JSON)
│
▼
④ 结果校验(字段完整性 + 引用存在性)
三、核心代码演示
1. 上下文裁剪
def trim_history(history: list, max_rounds: int = 5):
"""仅保留最近 max_rounds 轮对话,防止 Token 膨胀。"""
return history[-max_rounds * 2:] # 每轮含 user + assistant 两条
2. 向量检索召回
def retrieve(query: str, index, top_k: int = 3):
"""对 query 编码后从向量库召回最相关的知识片段。"""
vec = index.encode([query])
scores, ids = index.search(vec, top_k)
return [knowledge[doc_id] for doc_id in ids[0]]
3. 结构化生成与校验
import json, re
SYSTEM_PROMPT = """你是客服助手。请严格依据【参考资料】回答,
不得编造。输出格式必须为 JSON:
{"answer": "回答正文", "citations": ["引用片段"]}"""
def generate_answer(query, context, history, llm):
messages = trim_history(history) + [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user",
"content": f"参考资料:{context}\n问题:{query}"},
]
raw = llm.chat(messages)
return validate_response(raw, context)
def validate_response(raw, context):
try:
data = json.loads(re.search(r"\{.*\}", raw, re.S).group())
except Exception:
raise ValueError("输出非法 JSON,需重试")
# 校验引用确实来自知识库,遏制幻觉
assert all(c in context for c in data["citations"]), "引用不在知识库中"
return data
4. 统一入口
def ask(query, history, index, llm):
context = retrieve(query, index)
result = generate_answer(query, context, history, llm)
history.append({"user": query, "assistant": result["answer"]})
return result
四、效果与收益
- 幻觉率:引入"引用必须命中知识库"的硬校验后,明显下降;
- 成本:滑动窗口使单次请求 Token 量保持稳定,长对话不再线性膨胀;
- 可对接:JSON 输出可直接落入客服工单、质检系统,无需二次解析。
五、优化要点小结
- 始终给模型"可验证的上下文",而非只靠指令约束;
- 用结构化输出 + 后置校验兜底,而非完全信任模型;
- 对长对话做显式裁剪,平衡信息量与成本。
以上方案稍作改造,即可迁移到工单分类、财报问答、文档摘要等各类复杂场景。
海量精选技术文档和实战案例持续更新,敬请关注【风骏时光少年】