引言:从“对话机器人”到“AI 业务员”的演进

随着大语言模型(LLM)能力的爆发,单纯依赖模型内在知识已经无法满足企业级业务的需求。我们正面临着三大核心挑战:

  1. 幻觉问题:LLM 容易捏造事实,无法确保回答严格基于企业内部数据。
  2. 时效性问题:LLM 的训练数据存在截止日期,无法回答最新的业务动态。
  3. 能力边界问题: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 这种简单的命令运行,必须使用类似 GunicornUvicorn 的多进程/异步Worker来并发处理SSE长连接,否则一旦并发数上升,服务器会瞬间过载。

4.2 CI/CD 与 版本管理策略

在 GitLab / GitHub Actions 中,我们可以配置如下流水线:

  1. 代码提交(触发 Webhook)。
  2. 单元测试:对 RAG 向量检索准确率、Agent工具调用逻辑做单测(Mock 外部 API)。
  3. 镜像构建:根据提交的 Tag(如 v1.2.0)构建 Docker 镜像并推送到仓库。
  4. 安全扫描:对镜像进行漏洞扫描(使用 Trivy 等工具)。
  5. 蓝绿部署 / 金丝雀发布:在 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 智能体真正从实验室走入生产,释放降本增效的业务价值。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐