别再写面条代码了!用LangGraph + Qwen大模型,5步搞定一个带记忆的智能客服原型
·
用LangGraph与Qwen大模型构建高可维护对话系统的工程实践
在开发智能对话系统时,许多开发者都经历过这样的困境:随着业务逻辑复杂度的增加,代码逐渐演变成难以维护的"面条式"结构。本文将介绍如何利用LangGraph的状态机思想和Qwen大模型的中文优势,构建一个既具备上下文记忆能力又易于维护的智能客服系统。
1. 为什么传统对话系统开发容易陷入混乱
典型的对话系统开发通常面临三大核心挑战:
- 状态管理复杂:多轮对话需要维护上下文状态,传统方式往往通过全局变量或数据库记录,导致代码分散
- 流程耦合严重:业务逻辑与对话流程深度绑定,任何需求变更都可能引发连锁修改
- 可观测性差:对话流转过程缺乏可视化手段,调试困难
对比常见解决方案:
| 方案 | 状态管理 | 流程可视化 | 学习曲线 | 中文支持 |
|---|---|---|---|---|
| 纯代码实现 | 差 | 无 | 低 | 依赖模型 |
| Rasa/Botpress | 中等 | 部分 | 高 | 需配置 |
| LangGraph | 优秀 | 优秀 | 中等 | 原生支持 |
提示:LangGraph将对话流程抽象为状态机,每个节点只关注特定业务逻辑,天然解耦复杂流程
2. 环境准备与基础配置
建议使用Python 3.10+环境,先安装核心依赖:
pip install langgraph==0.0.7 langchain-community==0.0.12
获取Qwen API密钥的步骤:
- 访问通义千问官方网站注册账号
- 在控制台创建API Key
- 在环境变量中配置:
import os
os.environ["DASHSCOPE_API_KEY"] = "your_api_key_here"
基础组件初始化:
from langgraph.graph import StateGraph, START, END
from langchain_community.llms import Tongyi
llm = Tongyi(
model="qwen-max",
streaming=True,
temperature=0.7
)
3. 构建对话状态机的核心架构
3.1 定义对话状态模型
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
class DialogState(TypedDict):
history: Annotated[list, add_messages]
user_profile: dict
conversation_phase: str
状态机各组件关系:
START → [节点1] → [节点2] → ... → [节点N] → END
3.2 实现典型对话节点
用户意图识别节点示例:
def intent_classifier(state: DialogState):
last_message = state["history"][-1]
# 此处可接入任何分类模型
if "售后" in last_message.content:
return {"conversation_phase": "after_sales"}
return {"conversation_phase": "normal"}
业务处理节点示例:
def handle_after_sales(state: DialogState):
from datetime import datetime
ticket_id = f"TS-{datetime.now().strftime('%Y%m%d%H%M%S')}"
response = llm.invoke({
"messages": [
*state["history"],
{"role": "system", "content": f"生成售后回复,工单号{ticket_id}"}
]
})
return {
"history": [*state["history"], response],
"conversation_phase": "waiting_feedback"
}
3.3 组装完整状态机
workflow = StateGraph(DialogState)
workflow.add_node("intent", intent_classifier)
workflow.add_node("normal_chat", handle_normal)
workflow.add_node("after_sales", handle_after_sales)
# 定义状态流转逻辑
workflow.add_edge("intent", "normal_chat")
workflow.add_conditional_edges(
"intent",
lambda x: "after_sales" if x["conversation_phase"] == "after_sales" else "normal_chat",
)
workflow.add_edge("normal_chat", END)
workflow.add_edge("after_sales", END)
workflow.set_entry_point("intent")
dialog_app = workflow.compile()
4. 高级功能实现技巧
4.1 长期记忆存储方案
import json
from pathlib import Path
def save_conversation(user_id: str, state: DialogState):
path = Path(f"conversations/{user_id}.json")
path.parent.mkdir(exist_ok=True)
with open(path, "w") as f:
json.dump({
"history": [msg.dict() for msg in state["history"]],
"profile": state["user_profile"]
}, f)
4.2 多工具集成示例
天气查询工具集成:
def weather_tool(state: DialogState):
if "天气" not in state["history"][-1].content:
return None
import requests
location = extract_location(state["history"][-1].content)
response = requests.get(f"https://weather.api?location={location}")
return {
"history": [
*state["history"],
{"role": "tool", "content": response.json()}
]
}
4.3 性能优化建议
- 缓存策略:对频繁查询的内容建立本地缓存
- 异步处理:非关键路径使用异步执行
- 批量处理:多个工具调用尽量合并请求
from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_llm_call(prompt: str):
return llm.invoke(prompt)
5. 生产环境部署方案
5.1 服务化封装
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserInput(BaseModel):
text: str
user_id: str
@app.post("/chat")
async def chat_endpoint(input: UserInput):
state = load_conversation(input.user_id)
updated_state = dialog_app.invoke({
**state,
"history": [*state["history"], {"role": "user", "content": input.text}]
})
save_conversation(input.user_id, updated_state)
return {"response": updated_state["history"][-1]}
5.2 监控与日志
推荐监控指标:
- 响应时间P99
- 对话轮次分布
- 意图识别准确率
- 异常请求比例
from prometheus_client import Counter
REQUEST_COUNTER = Counter('dialog_requests', 'Total chat requests')
@app.post("/chat")
async def chat_endpoint(input: UserInput):
REQUEST_COUNTER.inc()
# ...原有逻辑...
5.3 自动化测试方案
对话流程测试示例:
def test_after_sales_flow():
state = {
"history": [{"role": "user", "content": "我的订单有问题"}],
"user_profile": {},
"conversation_phase": ""
}
updated = dialog_app.invoke(state)
assert "工单号" in updated["history"][-1].content
assert updated["conversation_phase"] == "waiting_feedback"
在实际项目中,我们团队发现将温度参数(temperature)设置在0.6-0.8之间,能在创造性和稳定性之间取得较好平衡。对于客服场景,建议top_p值不要超过0.9,以避免回答过于发散。
更多推荐



所有评论(0)