RAG与智能问答系统实战:从需求分析到Agent流程自动化完整落地
引言:从“对话机器人”到“AI 业务员”的演进
随着大语言模型(LLM)能力的爆发,单纯依赖模型内在知识已经无法满足企业级业务的需求。我们正面临着三大核心挑战:
- 幻觉问题:LLM 容易捏造事实,无法确保回答严格基于企业内部数据。
- 时效性问题:LLM 的训练数据存在截止日期,无法回答最新的业务动态。
- 能力边界问题:LLM 只能“说”,无法“做”。例如,它无法查询库存、无法下单、无法调用外部业务系统。
检索增强生成(RAG) 解决了前两个问题,而 AI Agent(智能体) 正是解决第三个问题的核心利器。本文将带领读者一步步从需求分析入手,完成一套包含 RAG 核心链路、Agent 动作编排、异步实时交互以及生产级部署运维的完整实战。
一、 需求分析、技术选型与系统架构设计
1.1 业务场景与痛点挖掘
场景设定:某大型制造企业需要构建一个“内部知识专家 AI 助手”。这个助手需要解决三个层次的业务需求:
- 基础层:回答员工关于公司制度、技术文档、产品手册的问题(基础 RAG)。
- 动作层:当员工询问“我的项目审批进度如何了”时,AI 需要调用内部 OA 接口查询,并返回实时数据(Agent 工具调用)。
- 分析层:当员工问“今年 Q1 的客户投诉数据有什么趋势”时,AI 需要跨多个知识库检索,进行逻辑推理并生成分析结论(Agent 任务拆解与上下文管理)。
1.2 总体系统架构设计
基于上述需求,我们采用前后端分离 + 编排层中间件的架构:
- 前端层:基于 React / Vue 构建。
- 网关层(BFF):负责身份认证、限流与 WebSocket/SSE 协议转换。
- 编排层(Agent Orchestration):核心大脑,使用 LangChain / LangGraph 实现。
- 向量检索与数据层:Pinecone / Milvus 向量数据库 + 关系型数据库(存储工单状态、用户权限等)。
- 大模型层:通过 Azure OpenAI / DeepSeek / 自研大模型提供的 API 接口。
- 工具与插件层:内部 OA 系统 API、汇率查询、天气预报、搜索引擎等。
二、 核心 RAG 与 AI Agent 工作流编排实现
2.1 基础 RAG 链路代码实战(Python + LangChain)
我们首先实现标准的 RAG 链路:文档加载 -> 文本切分 -> 向量化存储 -> 检索 -> 上下文合成 -> 生成。
# rag_core.py
import os
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
class RAGService:
def __init__(self, persist_directory="./chroma_db"):
self.embeddings = OpenAIEmbeddings()
self.vectorstore = Chroma(persist_directory=persist_directory, embedding_function=self.embeddings)
self.llm = ChatOpenAI(model="gpt-4-turbo", temperature=0.2)
def load_and_index(self, file_paths):
"""在线构建知识库"""
docs = []
for path in file_paths:
if path.endswith(".pdf"):
loader = PyPDFLoader(path)
else:
loader = TextLoader(path, encoding='utf-8')
docs.extend(loader.load())
# 文本切分(Chunking),设定重叠区域保持上下文
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
# 存入向量数据库
self.vectorstore.add_documents(splits)
self.vectorstore.persist()
return f"成功存入 {len(splits)} 个片段。"
def simple_rag_query(self, query):
"""基础检索增强生成"""
# 1. 检索 Top-K 相关知识片段
retriever = self.vectorstore.as_retriever(search_kwargs={"k": 4})
context_docs = retriever.get_relevant_documents(query)
context_text = "\n\n".join([doc.page_content for doc in context_docs])
# 2. 构造 Prompt
prompt_template = ChatPromptTemplate.from_template(
"你是一个企业内部专家,请基于以下提供的资料,严谨地回答用户的问题。如果资料中没有相关内容,请回答“资料库中未找到相关信息”,不要捏造。\n\n"
"【企业资料】:{context}\n\n"
"【用户问题】:{question}\n"
)
chain = prompt_template | self.llm
# 3. 调用 LLM 生成回复
return chain.invoke({"context": context_text, "question": query}).content
2.2 升级为具有“行动力”的 AI Agent
仅仅检索文档是远远不够的。接下来我们将 RAG 封装成一个工具(Tool),并利用 LangGraph 构建具备状态管理和工具调用能力的 Agent 工作流(对标 JD 第 3 条中的“Agent工作流编排”)。
# agent_workflow.py
import json
import requests
from typing import TypedDict, List
from langgraph.graph import StateGraph, END
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage, AIMessage
from langgraph.prebuilt import ToolExecutor
from langgraph.graph import StateGraph, END
# 1. 定义 Agent 的状态(State)
class AgentState(TypedDict):
messages: List[BaseMessage]
next_step: str
# 2. 定义工具(Tools),将 RAG 包装为工具
def rag_search_tool(query: str) -> str:
"""查询企业内部资料库,获取知识信息"""
# 这里复用前面的 RAGService
rag = RAGService()
return rag.simple_rag_query(query)
def query_internal_api_tool(parameter: str) -> str:
"""查询企业内部 OA 系统 API(例如审批状态)"""
# 模拟调用内部 API
print(f"正在调用内部 API,参数:{parameter}")
# response = requests.get("https://internal.company.com/api/approve?flow_id=...")
return json.dumps({"status": "已完成", "detail": "审批流已流转至最后节点"})
# 3. 构建 Agent 节点(LLM 决策核心)
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolExecutor, ToolInvocation
from langchain_core.tools import StructuredTool
# 注册工具
tools = [
StructuredTool.from_function(rag_search_tool),
StructuredTool.from_function(query_internal_api_tool)
]
# 模型绑定工具
model = ChatOpenAI(model="gpt-4-turbo", temperature=0).bind_tools(tools)
def agent_node(state: AgentState):
"""Agent 节点:决定下一步是思考、调用工具、还是结束"""
messages = state["messages"]
response = model.invoke(messages)
return {"messages": [response]}
# 4. 执行工具节点
def tool_node(state: AgentState):
"""执行工具调用的节点"""
last_message = state["messages"][-1]
tool_calls = last_message.tool_calls
if not tool_calls:
return {"messages": []}
tool_invocation = ToolInvocation(
tool=tool_calls[0]["name"],
tool_input=tool_calls[0]["args"],
)
tool_result = tools[0].invoke(tool_invocation) # 简化逻辑,实际需循环调用
response = AIMessage(
content=str(tool_result),
name=tool_invocation.tool,
additional_kwargs={"tool_call_id": tool_calls[0]["id"]}
)
return {"messages": [response]}
# 5. 构建流程图与路由(Workflow)
def should_continue(state: AgentState) -> str:
last_message = state["messages"][-1]
if last_message.tool_calls:
return "tools"
return END
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
workflow.add_edge("tools", "agent")
# 编译为可运行的应用
app = workflow.compile()
# 测试调用
inputs = {"messages": [HumanMessage(content="请帮我查询昨天发布的《Q2采购指南》文档内容,并查一下我的工单编号 'T-20260716' 的审批进度。")]}
for event in app.stream(inputs):
for key, value in event.items():
print(f"节点 [{key}]: {value['messages'][-1].content if value.get('messages') else ''}")
架构洞察:通过 LangGraph 构建的循环逻辑,Agent 可以在一轮对话中主动分步执行(先搜资料,再调 API),并将两次返回结果汇总,结合上下文管理,最终给出高确定性的结论,这彻底打破了传统规则引擎的死板限制。
三、 面向生产的前后端工程实践
在实际生产环境中,AI 回复往往需要几秒甚至几十秒。如果直接使用 HTTP 阻塞式接口,前端会因为请求超时而报错。因此,我们需要引入 SSE(Server-Sent Events) 或 WebSocket 实现流式传输(Streaming)。
3.1 后端异步流式接口设计(FastAPI + SSE)
# api.py
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from sse_starlette.sse import EventSourceResponse
import asyncio
from agent_workflow import app as agent_app # 导入上文中构建的 Agent
from langchain_core.messages import HumanMessage, AIMessageChunk
app = FastAPI()
async def stream_agent_response(query: str):
"""
在 Agent 执行过程中,使用流式方式逐步返回 LLM 的 Token 和 工具执行状态
"""
inputs = {"messages": [HumanMessage(content=query)]}
try:
# 使用异步流式执行 LangGraph Agent
async for event in agent_app.astream(inputs):
for key, value in event.items():
msg = value.get('messages', [])
if not msg:
continue
msg = msg[-1]
# 如果 Agent 正在执行工具
if key == "tools":
yield {
"event": "tool_call",
"data": f"【Action】正在调用工具: {msg.content}"
}
# 如果 Agent 正在进行LLM生成(核心需要流式输出 Token)
elif key == "agent":
if hasattr(msg, 'content') and msg.content:
# 确保是逐步吐出的 Token
yield {
"event": "message",
"data": msg.content # 在实际生产中,这里应处理 AIMessageChunk 的增量
}
# 流结束
yield {"event": "end", "data": "[DONE]"}
except Exception as e:
yield {"event": "error", "data": f"系统异常: {str(e)}"}
@app.get("/chat/stream")
async def chat_stream(query: str):
"""SSE 流式接口"""
return EventSourceResponse(stream_agent_response(query))
3.2 前端工程化:基于 React 的流式交互体验
前端需使用 EventSource 来解析 text/event-stream 响应,实现打字机效果的交互体验。
// ChatBox.jsx (React伪代码)
import React, { useState } from 'react';
const ChatBox = () => {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isLoading, setIsLoading] = useState(false);
const sendMessage = () => {
if (!input.trim()) return;
setMessages(prev => [...prev, { role: 'user', content: input }]);
setIsLoading(true);
const currentInput = input;
setInput('');
// 构造 SSE 连接
const eventSource = new EventSource(`/api/chat/stream?query=${encodeURIComponent(currentInput)}`);
let aiMessageContent = "";
// 添加一条空消息等待流式填充
setMessages(prev => [...prev, { role: 'assistant', content: '' }]);
eventSource.onmessage = (event) => {
const data = event.data;
if (data === '[DONE]') {
eventSource.close();
setIsLoading(false);
return;
}
// 流式拼接 Token
aiMessageContent += data;
setMessages(prev => {
const newMsgs = [...prev];
newMsgs[newMsgs.length - 1].content = aiMessageContent;
return newMsgs;
});
};
eventSource.onerror = (err) => {
console.error("SSE Error:", err);
eventSource.close();
setIsLoading(false);
};
};
return (
<div>
<div className="chat-history">
{messages.map((msg, idx) => <div key={idx}>{msg.content}</div>)}
</div>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button onClick={sendMessage} disabled={isLoading}>发送</button>
</div>
);
};
关键设计点:这种 SSE 异步非阻塞 的交互方式,配合 React 的 useState 增量更新,完美解决了 LLM 响应慢导致的用户体验降级问题,同时缓解了后端的长连接资源占用。
四、 工程化落地与部署运维
企业级 AI 应用不能停留在本地代码,必须走完 CI/CD、容器化、监控告警 的全链路。
4.1 容器化与 Docker 镜像构建
我们需要将 Python 后端和 React 前端分别打包成镜像。
# Dockerfile (Backend)
FROM python:3.10-slim
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
# 暴露端口
EXPOSE 8000
# 启动命令,使用 Gunicorn + Uvicorn 保证生产环境的高并发稳定性
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "api:app", "--bind", "0.0.0.0:8000"]
- 运维关键点:生产环境绝对不建议使用
python api.py这种简单的命令运行,必须使用类似Gunicorn或Uvicorn的多进程/异步Worker来并发处理SSE长连接,否则一旦并发数上升,服务器会瞬间过载。
4.2 CI/CD 与 版本管理策略
在 GitLab / GitHub Actions 中,我们可以配置如下流水线:
- 代码提交(触发 Webhook)。
- 单元测试:对 RAG 向量检索准确率、Agent工具调用逻辑做单测(Mock 外部 API)。
- 镜像构建:根据提交的 Tag(如
v1.2.0)构建 Docker 镜像并推送到仓库。 - 安全扫描:对镜像进行漏洞扫描(使用 Trivy 等工具)。
- 蓝绿部署 / 金丝雀发布:在 Kubernetes 中更新 Deployment,确保 Zero Downtime(零停机)。
4.3 监控告警与日志分析
在生产环境,需要监控以下黄金指标:
- LLM API 请求延迟与 Token 消耗(用于成本控制)。
- 向量数据库 QPS 与 内存占用量。
- Agent 各节点执行耗时(如果某一步骤超时,可及时告警)。
- Linux 机器层面的监控:CPU、内存、磁盘 I/O、网络带宽(尤其是 SSE 长连接产生的流量)。
使用 Prometheus + Grafana 搭建可视化仪表盘,结合 Elasticsearch + Filebeat + Kibana (EFK) 实现日志的集中管理和错误追踪。
五、 生产级进阶优化策略
从 Demo 走向 100万级 QPS 的高并发生产环境,还需要解决 3 大隐性技术问题:
5.1 检索增强优化(HyDE 与 多查询机制)
直接使用用户自然语言检索,有时无法命中精确关键词。
- 多查询(Multi-Query):让 LLM 针对同一个问题,生成 3 个不同角度的问题描述,并分别进行检索,最后去重。
- HyDE(假设性文档嵌入):让 LLM 先基于当前问题“假设”出一个完美答案,然后将这个“假设答案”作为检索的 Query,能大幅提高召回率。可以在代码中封装一个
rewrite_query(query)预处理器。
5.2 上下文管理与缓存机制
- 长上下文管理:对历史对话记录进行 Summary 摘要,只将摘要传给 LLM,以节省 Token 消耗,同时保留记忆。
- 多级缓存:对于高频重复的咨询问题(如“公司休假政策”),在前置缓存(Redis)中直接缓存完整响应,不再走完整的 RAG 链路,响应时间可从 5s 降低至 50ms。
5.3 系统稳定性的兜底策略(容错与 Fallback)
- 当外部大模型 API 服务降级或中断时,系统必须提供 Fallback 机制(例如降级到仅使用向量检索结果,不加生成,只展示原文)。
- 优雅停机:在 Kubernetes Pod 停止时,确保当前的 SSE 连接已经处理完毕并关闭,避免客户端连接中断。
结语:AI 应用落地的关键不是模型,而是工程
通过本文从需求分析、RAG 链路代码编写、Agent 工作流编排、前后端流式交互,到最终的容器化部署与链路优化的完整实战,可以清晰地看到:在一线企业级 AIGC 开发中,AI 算法能力只占 20%,剩余 80% 都是后端架构的稳定度、前端交互的流畅度以及 DevSecOps 的工程化实力。
作为全栈 AI 工程师(如招聘 JD 所要求),我们需要深度拥抱云原生技术(Docker/K8s),熟练通过优雅的代码模块(如 LangGraph)来封装业务逻辑,并时刻关注系统底层性能与稳定性。唯有“工程+算法”双向驱动,才能让 AI 智能体真正从实验室走入生产,释放降本增效的业务价值。
更多推荐


所有评论(0)