企业级Agent从0到1

从 0 到 1 搭建企业级 AI Agent:FastAPI 服务层 + 工具调用 + 长时记忆 + RAG 学习,全栈落地实录

摘要: 本文复盘某互联网平台智能客服 Agent 从 0 到 1 的完整搭建过程:FastAPI 四接口服务层、Master 主类设计、@tool 工具体系(实时搜索/专业 API/本地知识库)、Redis 持久化长时记忆、URL 驱动的 RAG 学习能力,以及 Docker + LangSmith 生产部署,附核心代码与真实踩坑记录,帮你绕过"Demo 能跑、生产就挂"的全部经典陷阱。


一、先说结论:生产级 Agent = 确定性流程工具化 + 非确定性决策 Agent 化 + 程序化校验兜底

先讲一个我们踩过的大坑:最初团队用低代码工作流平台搭 Agent(V1 版),节点之间上下文隔离、无状态,用户问"今天销售额 → 昨天 → 前天",三次追问要重复执行三遍完整链路,单次查询动辄 15 秒以上;多意图混合查询("查 A 商品昨日销量和 B 商品本周退货率")更是没法并行,强行加判断节点又引入额外延迟。

后来我们重构为高代码架构(V3),一句话总结核心思想:

latex 复制代码
确定性逻辑 → 用代码封装成原子工具(toys),杜绝 LLM 幻觉
不确定性逻辑 → 交给 Agent 做轻量语义理解与路径决策
安全兜底 → 用程序化校验(Harness)替代脆弱的 Prompt 约束

重构后,追问场景从 15 秒降到 4 秒级,多意图查询也能精准并行。下面按完整搭建顺序展开。

二、整体架构:四层设计,职责分明

latex 复制代码
┌─────────────────────────────────────────────┐
│  应用层:Telegram 机器人 / 网页 / 数字人     │
├─────────────────────────────────────────────┤
│  API层:FastAPI(/chat /add_urls /add_pdfs  │
│         /add_texts + WebSocket 流式)        │
├─────────────────────────────────────────────┤
│  服务层:LangChain(Chain/Memory/Tools/      │
│          Agent + 情绪判断链)                │
├─────────────────────────────────────────────┤
│  资源层:Redis(记忆) / Qdrant(向量库)    │
│          大模型 API / 外部工具 API           │
└─────────────────────────────────────────────┘

三、API 层:FastAPI 四个接口打天下

服务端就一个核心文件,四个 POST 接口:

python 复制代码
from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.post("/chat")            # 主对话接口
async def chat(query: str):
    result = master.run(query)   # Master 是 Agent 主类
    return {"response": result}

@app.post("/add_urls")        # 从 URL 学习知识
async def add_urls(url: str):
    master.learn_from_url(url)
    return {"status": "ok"}

@app.post("/add_pdfs")        # 从 PDF 学习知识
@app.post("/add_texts")       # 从文本学习知识

关键依赖版本锁死(这是踩坑换来的):

latex 复制代码
fastapi==0.108.0
langchain==0.1.10
langchain_core==0.1.28
langchain_openai==0.0.5
langchain_community==0.0.25
redis(最新)
qdrant_client==1.7.1
uvicorn==0.23.2

避坑要点: LangChain 0.1 起拆分成了 langchain-corelangchain-openailangchain-community 三个包,老教程的 from langchain.llms import ChatOpenAI 写法已经过时。版本不锁,三天就报错。

四、Agent 主体:Master 类 + 角色 Prompt 设计

4.1 主类初始化

python 复制代码
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent

class Master:
    def __init__(self, tools):
        self.llm = ChatOpenAI(
            model="gpt-3.5-turbo-1106",
            temperature=0,        # 铁律:严格遵循 Prompt
            streaming=True,       # WebSocket 流式返回需要
        )
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", SYSTEM_PROMPT),           # 角色设定
            ("placeholder", "{chat_history}"),   # 记忆占位符
            ("human", "{input}"),
            ("placeholder", "{agent_scratchpad}"),
        ])
        self.agent = create_openai_tools_agent(self.llm, tools, self.prompt)
        self.executor = AgentExecutor(agent=self.agent, tools=tools, verbose=True)

避坑要点: create_openai_tools_agent 要求 tools 至少传一个非空工具,否则直接报错。开发初期可以先塞一个假工具占位,再逐步替换成真工具。

4.2 角色 Prompt:人设即产品

角色设定全部靠 Prompt 注入,换一套 Prompt 就是另一个 Agent------这是 Agent 可扩展性的精髓:

python 复制代码
SYSTEM_PROMPT = """你是陈大师,一位精通阴阳五行、紫微斗数、八字测算的资深命理师。
你年约60岁,曾是江西一带赫赫有名的江湖人士,后因故左眼失明,人称"陈瞎子"。
你性格豁达幽默,说话直来直去,但句句在理。
你只使用繁体字回复。遇到负面情绪的用户,先安抚再解答。"""

4.3 情绪判断链:用 Chain 把模糊情绪变成确定性信号

独立一个 detect_emotion() 方法,用专用 Prompt 约束 LLM 只输出四类标签:

python 复制代码
def detect_emotion(self, text: str) -> str:
    chain = LLMChain(llm=self.llm, prompt=EMOTION_PROMPT)
    emotion = chain.run(text).strip().lower()
    self.emotion = emotion   # friendly / depressed / default / angry
    return emotion

实测效果:输入"你好"返回 friendly,输入"你真是个傻子"返回 angry。情绪标签存到 self.emotion,后续语音合成、回复策略切换都能用------它用 Prompt 工程把模糊情绪转化为确定性信号,为后续环节提供原子级控制开关,是低成本实现高拟真交互的关键杠杆。

4.4 工具调用链路(用户视角)

把前面所有模块串起来,一次完整的工具调用是这样的:

latex 复制代码
用户请求
  → Agent 判断:该用哪个工具?
  → 携带参数调用工具(搜索 / 命理 API / 知识库)
  → 拿到观察结果(Observation)
  → Agent 结合结果生成最终回答

五、工具体系:@tool 装饰器三步建一个工具

5.1 实时搜索工具(SerpAPI)

python 复制代码
from langchain_core.tools import tool
from langchain_community.utilities import SerpAPIWrapper

@tool
def search(query: str):
    """只有需要了解实时信息或不知道的事情时,才使用这个工具。"""
    result = SerpAPIWrapper().run(query)
    print("实时搜索结果:", result)
    return result

工具描述(docstring)决定 Agent 能不能正确选用工具------描述必须写清楚"什么时候该用",这是 Agent 与普通函数最本质的区别。

5.2 专业 API 工具:参数校验不能省

Agent 传进来的参数全是字符串,调用专业 API(八字测算、解梦等)前必须做参数提取与校验------用一个小型 LLMChain 把用户输入转成结构化 JSON:

python 复制代码
param_chain = LLMChain(llm=llm, prompt=ChatPromptTemplate.from_template(
    "你是参数查询助手,根据用户输入提取相关参数,按 JSON 格式返回。\n输入:{input}"))

@tool
def bazi_calculate(text: str):
    """用户要求测算八字时使用。"""
    params = json.loads(param_chain.run(text))   # 提取年月日时等参数
    resp = requests.post("https://api.example.com/bazi", json=params)
    return resp.json()

5.3 本地知识库工具(RAG)

把专业领域知识(企业规范、业务文档)向量化存 Qdrant,工具内做相似度检索:

python 复制代码
@tool
def knowledge_search(query: str):
    """回答企业内部专业知识问题时使用。"""
    return vectorstore.similarity_search(query, k=5)

避坑要点: 工具密钥(SerpAPI Key 等)用环境变量管理,千万别写死在代码里。生产环境建议加访问审计和请求频控,单点泄露不至于全系统失陷。

六、长时记忆:Redis 持久化 + 智能压缩

大模型无状态,对话记录必须外挂。方案:Redis 存历史 + 超阈值自动摘要压缩

python 复制代码
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain.memory import ConversationTokenBufferMemory

def get_memory(session_id="default"):
    history = RedisChatMessageHistory(session_id=session_id, url="redis://localhost:6379/0")

    # 超过 10 条记录 → LLM 提炼摘要,清空旧记录
    if len(history.messages) > 10:
        summary = llm.invoke(f"提炼以下对话的要点:\n{history.messages}")
        history.clear()
        history.add_ai_message(summary)

    return ConversationTokenBufferMemory(
        chat_memory=history,
        human_prefix="用户", ai_prefix="助手",
        memory_key="history", max_token_limit=1000)

注意:记忆要生效,Prompt 模板里必须预留 {chat_history} 占位符(见 4.1 节),否则记忆注入不进去------这是最隐蔽的坑。

七、RAG 学习能力:给 Agent 喂 URL,不用微调

"学习"的本质是 RAG:URL → HTML 转文本 → 切分 → 向量化 → 存库 → 检索。加载器可换(PDF 用 PyPDFLoader),后续步骤通用:

python 复制代码
from langchain_community.document_loaders import UnstructuredURLLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Qdrant

def learn_from_url(url: str):
    docs = UnstructuredURLLoader(urls=[url]).load()
    texts = RecursiveCharacterTextSplitter(
        chunk_size=800, chunk_overlap=50).split_documents(docs)
    Qdrant.from_documents(texts, OpenAIEmbeddings(), collection_name="learning_knowledge")

避坑要点: 同一个向量库内用不同 collection 隔离知识门类(物流文档、业务规范各一个集合),检索精度明显高于混在一起。80% 以上的场景靠 RAG 就够了,不需要微调------微调是最后手段,不是默认选项。

八、生产部署:Docker Compose + LangSmith

8.1 Docker 保证环境一致

yaml 复制代码
version: "3"
services:
  redis:
    image: redis:latest
    volumes: ["redis-data:/data"]      # 数据卷,升级不丢历史
  ai-server:
    build: .
    environment:
      - REDIS_URL=redis://redis:6379/0
    ports: ["9000:9000"]
    depends_on: [redis]
volumes:
  redis-data:

避坑要点: 记忆数据必须挂数据卷(volumes),否则 docker-compose down 一次,Redis 里的聊天记录全没------这个坑我们上线第一周就踩了。

8.2 LangSmith 生产追踪

配置三个环境变量即可:

latex 复制代码
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=你的key
LANGCHAIN_PROJECT=项目名

注意:只有 invoke() 调用会被追踪, run()** 不会**。追踪面板能看每次 LLM 调用的耗时、Token 消耗、工具调用链和错误日志------生产环境排障必备。我们靠它定位过一个 30 秒慢响应,最后发现是文档摘要链卡住了,光靠肉眼猜根本查不出来。

8.3 为什么不用 LangServe

社区有现成的 LangServe(LangChain 官方 Server 方案),自带 Playground 调试界面和自动路由生成,看着很香。但我们最终坚持手写 FastAPI,原因很现实:

  • LangServe 是较新的开源项目,版本迭代激进,不建议直接上生产
  • 单体架构耦合度高,项目结构会被框架绑架;
  • 我们只需要 4 个接口,手写 20 行代码的事,不值得引入依赖。

选型原则:核心逻辑自主掌控------工具类库可以随便用,但服务骨架这种主航道,别把控制权交给快速演进的框架。

九、扩展:从文本到多端接入与 AI 数字人

Agent 服务搭好后,横向扩展非常快,我们实际做了两件事:

① 多端接入。 同一个 /chat 接口,套不同的客户端壳就能换平台------Telegram 机器人(用 telebot 包,BotFather 申请 Token)、网页、企业微信原理完全一致。服务端只管"对话",客户端只管"收发",职责解耦。

② AI 数字人 Demo。 在 Agent 之上叠加 TTS + 虚拟形象:Agent 生成文本 → 调用语音合成 API → WebRTC 推流 → 前端 <video> 播放。架构上是三层拼接:

latex 复制代码
LangChain Agent(大脑) + 语音/形象 API(表达) + WebRTC(传输)

数字人 Demo 验证了一个结论:Agent 的能力边界不在模型,而在你能接多少表达通道------文本、语音、形象,每多一层,应用形态就上一个大台阶。

十、写在最后

这套架构最大的价值是回答了"Agent 到底怎么落地":别让 LLM 干它不擅长的事 (确定性计算、权限校验、SQL 生成交给代码),只让它干最擅长的(语义理解、路径决策、内容生成),再用 Harness 程序化校验兜底。低代码工作流平台看着省事,但在语义模糊、高并发、强实时场景下,抽象层级过高会让你失去对调用链路和状态流转的控制。

如果重来一遍,我会更早做三件事:① 先把所有成熟路径封装成原子工具;② 给每个工具写清楚"什么时候用";③ 用 LangSmith 从第一天就开始埋点。这三件事做对了,Agent 的工程化之路会顺畅很多。

如果你也在搭生产级 Agent,欢迎评论区聊聊你的架构。下面几个方向想看哪个留言告诉我:

  1. Agent 多工具并行的调度策略与冲突处理
  2. Harness 程序化校验的完整落地(SQL 安全、权限拦截)
  3. 语音合成 + 数字人与 Agent 的集成实战
相关推荐
用户69371750013841 小时前
DeepSeek 调价正式生效:一夜涨 11 倍,靠低价薅羊毛的日子结束了
前端·人工智能·后端
用户298698530141 小时前
从入门到自动化:TXT 转 Word 的在线工具与代码实战方案
人工智能·后端·python
RainCityLucky2 小时前
Java Swing 自定义组件库分享(十六)
java·笔记·后端
贰先生2 小时前
Xiuno BBS 审计之问题16:get_magic_quotes在 PHP 8.0 被移除
后端
IT_陈寒2 小时前
Vite的热更新突然失效,原来我忽略了这个配置
前端·人工智能·后端
云浪2 小时前
Milvus + RAG 实战:《红楼梦》问答助手
前端·人工智能·后端
阿部多瑞 ABU3 小时前
告别手工核图:基于 .NET + MuPDFCore 的 CAD 等轴测 PDF 材料表提取与新旧版本对比实战
后端·算法·ui·pdf·c#
卷无止境3 小时前
FastAPI 的 WebSocket 装了些什么
后端·python·fastapi
AINative软件工程3 小时前
Agent 上下文账本工程:别让工具结果把 128K 窗口塞成垃圾场
后端·架构·ai编程