返回博客

从零搭一个 AI 智能客服系统:用 Agent 接住用户问题,中途接入 4sapi 跑通对话

人工智能2908
从零搭一个 AI 智能客服系统:用 Agent 接住用户问题,中途接入 4sapi 跑通对话

用户在对话框里随便问,系统能听懂、查得到、答得准。这篇我把搭一套能落地的 AI 客服,从架构到代码、到中途怎么接中转 API,原原本本写给你。

写在前面

几乎每个做产品的人都动过"上个 AI 客服"的念头。道理很简单:人工客服贵、有班次、半夜没人,而用户的问题 80% 是重复的——退换货怎么弄、订单到哪了、这个功能怎么用。这些重复问题,正是 AI 最擅长接的活。

但真去搭,很多人第一步就卡住:直接拿大模型当客服,它会一本正经地胡编。用户问"我的订单 12345 到哪了",模型根本不知道你的订单系统,只会瞎猜。所以一套能用的 AI 客服,绝不是"接个大模型 API"那么简单,它至少得有三层能力:听懂问题、查到答案、稳定作答

这篇就带你把这套系统从零搭起来。我会把架构、知识库怎么接、多轮会话怎么管、关键代码、以及中途接入 4sapi.com 这个中转 API的具体步骤都讲清楚。


一、先想清楚:一个靠谱的 AI 客服要干哪几件事

动手之前先理清链路。用户发来一句话,系统要顺序做完这几件事:

用户提问

① 意图识别    这句话是问订单?问售后?还是闲聊?

② 知识检索    从知识库/业务系统里捞出相关资料(RAG)

③ 答案生成    把检索到的资料 + 问题,交给大模型组织成回答

④ 兜底与转人工  答不了的,老实承认并转人工,绝不硬编

这里头 ①③ 靠大语言模型,② 的语义检索靠向量嵌入(Embedding)模型。两类模型,又是分散在不同厂商——这正是我们中途要引入中转 API 的原因。


二、架构设计:四个 Agent + 一个中转网关

我把系统拆成"一个总调度 + 四个职责 Agent + 一个统一网关":

                   ┌──────────────────────────┐
   用户消息  ──→    │   Dispatcher 会话总调度    │
                   └─────────────┬────────────┘
                                 │ 按意图分流
      ┌──────────────┬──────────┼──────────┬──────────────┐
      ↓              ↓          ↓          ↓              ↓
 [意图Agent]   [检索Agent]  [回答Agent]  [兜底Agent]   会话记忆
      └──────────────┴──────────┼──────────┴──────────────┘

                   ┌──────────────────────────┐
                   │   统一 API 网关 (4sapi)    │  ← 文本/Embedding 都走这
                   └──────────────────────────┘

关键设计点还是最底层:所有 Agent 不直接调各家官方 API,统一走一个中转网关。 对客服这种场景,好处尤其明显:

我选的网关是 4sapi.com,兼容 OpenAI 格式,对话和 Embedding 模型都能通过它统一调用。


三、中途接入 4sapi:三步把网关接通

整套系统再漂亮,模型调不通就是空架子。接中转 API 其实就三步。

第一步:拿 Key、记住 BaseURL

注册 4sapi.com 后,在后台创建一个 API 令牌(形如 sk-xxxxxxxx),并记下接口地址:

https://4sapi.com/v1

放进环境变量,别硬编码进代码

bash
# .env
FOURSAPI_BASE_URL=https://4sapi.com/v1
FOURSAPI_KEY=sk-你的令牌

第二步:封装统一客户端

4sapi 兼容 OpenAI 格式,直接用 openai SDK,把 base_url 指过去。对话和 Embedding 都从这里复用:

python
# gateway.py —— 统一 API 网关客户端
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["FOURSAPI_KEY"],
    base_url=os.environ["FOURSAPI_BASE_URL"],   # 关键:指向中转网关
)

def chat(model, messages, **kwargs):
    resp = client.chat.completions.create(
        model=model, messages=messages, **kwargs
    )
    return resp.choices[0].message.content

def embed(text, model="text-embedding-3-small"):
    """语义检索要用的向量,也走同一个网关"""
    resp = client.embeddings.create(model=model, input=text)
    return resp.data[0].embedding

第三步:发请求验证连通

业务别急着写,先确认网关通了:

python
if __name__ == "__main__":
    print(chat(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "回复两个字:通了"}],
    ))   # 正常应打印「通了」

能打印结果,说明对话和 Embedding 两条路都打通了。本质就是「改一个 base_url + 配一个 Key」。


四、逐个环节实现:四个 Agent 怎么写

4.1 意图 Agent —— 先分清用户到底想干嘛

别上来就回答。先让模型给问题贴个标签,决定后面怎么走:

python
def intent_agent(question):
    prompt = f"""判断用户问题属于哪类,只返回一个词:
order(订单/物流) / aftersale(退换售后) / faq(功能咨询) / chat(闲聊) / other
用户问题:{question}"""
    return chat(
        model="gpt-4o-mini",          # 分类用小模型够了,省钱
        messages=[{"role": "user", "content": prompt}],
    ).strip()

4.2 检索 Agent —— 从知识库捞出真凭据(RAG 核心)

这是 AI 客服不胡编的关键。先把知识库文档切片、做好向量索引(离线一次性完成);用户提问时,把问题也转成向量,捞出最相关的几条:

python
import numpy as np

def retrieve_agent(question, kb_vectors, kb_texts, top_k=3):
    q_vec = embed(question)            # 问题向量,走网关
    # 余弦相似度排序,取最相关的 top_k 条
    sims = [np.dot(q_vec, v) for v in kb_vectors]
    idx = np.argsort(sims)[-top_k:][::-1]
    return [kb_texts[i] for i in idx]

实际生产里别自己手撸相似度,用 FAISS / Milvus 这类向量库扛检索。这里写成裸算法只是让你看清原理。

4.3 回答 Agent —— 拿着证据组织人话

把检索到的资料塞进 prompt,严令模型只能基于资料作答,这是压制幻觉的关键一招:

python
def answer_agent(question, contexts, history):
    context_text = "\n".join(contexts)
    system = f"""你是客服助手。只能依据【参考资料】回答,
资料里没有的,就说"这个我需要帮您转接人工",绝不要编。
【参考资料】
{context_text}"""
    messages = [{"role": "system", "content": system}]
    messages += history          # 带上多轮上下文
    messages.append({"role": "user", "content": question})
    return chat(model="gpt-4o", messages=messages, temperature=0.3)

注意 temperature=0.3——客服场景要的是稳定准确,不是创意发挥,温度调低能显著减少胡说。

4.4 兜底 Agent —— 答不了就老实转人工

python
def fallback_agent(question, ticket_system):
    # 触发条件:检索为空、用户连续追问、或回答里出现"转接人工"
    ticket_system.create(question)        # 落工单,转人工
    return "这个问题我帮您转接人工客服,请稍候~"

五、串起来:会话总调度

四个 Agent 用一个调度器按意图编排,并维护多轮会话记忆:

python
def handle_message(question, session):
    history = session.get_history()       # 取该用户的多轮上下文

    intent = intent_agent(question)
    if intent == "chat":
        reply = chat(model="gpt-4o-mini",
                     messages=history + [{"role": "user", "content": question}])
    else:
        contexts = retrieve_agent(question, KB_VEC, KB_TXT)
        if not contexts:                  # 检索不到 → 兜底
            reply = fallback_agent(question, TICKET_SYS)
        else:
            reply = answer_agent(question, contexts, history)

    session.append(question, reply)       # 写回会话记忆
    return reply

所有模型调用——分类、检索、回答——全走同一个 4sapi 网关,一个 Key 搞定


六、前端:一个能嵌进任何页面的对话窗

前端就是个常见的聊天气泡窗,核心是流式输出,让回答一个字一个字蹦出来,体验不卡:

jsx
function ChatWidget() {
  const [msgs, setMsgs] = useState([]);
  const [input, setInput] = useState("");

  async function send() {
    setMsgs(m => [...m, { role: "user", text: input }]);
    const res = await fetch("/api/chat", {
      method: "POST",
      body: JSON.stringify({ question: input }),
    });
    // 用 SSE 流式接收,逐字渲染助手回复
  }

  return (
    <div className="chat-widget">
      <MsgList items={msgs} />
      <input value={input} onChange={e => setInput(e.target.value)}
             placeholder="有什么可以帮您?" />
      <button onClick={send}>发送</button>
    </div>
  );
}

安全提醒:/api/chat 背后每次都在烧 token,上线前务必加用户鉴权 + 单用户频率限制,否则被人写脚本刷一晚上,账单很难看。另外,知识库里别塞敏感数据,模型有可能在回答里带出来。


七、几条实战经验

  1. 意图识别用小模型,回答才上大模型。客服 80% 是分类和检索,真正需要 gpt-4o 的只有最后组织语言那一步,账单立刻降一截。
  2. 回答 Agent 必须强约束"只依据资料"。这一句 system prompt,是 AI 客服从"能用"到"敢上线"的分水岭。
  3. 多轮会话记忆要做长度截断。别把几十轮全塞进去,token 爆炸还更容易跑偏,留最近 6~8 轮足够。
  4. 检索结果先过一遍相关度阈值。相似度太低的就当没检索到,直接走兜底,强行硬答比转人工更伤口碑。
  5. 所有网关调用包重试 + 超时。高频场景下偶发超时很正常,别让一次失败把整个会话搞挂。

写在最后

搭一套 AI 客服,难的从来不是"调用大模型",而是怎么让它在不知道的时候老实闭嘴、在知道的时候答得准

意图先分流、检索给证据、回答带约束、答不了转人工——这四件事想清楚,AI 客服就敢上线了。

中途接入 4sapi 这一步,把"对话模型 + Embedding 模型分头对接"的麻烦,收敛成了一个 base_url、一个 Key。复杂度挡在网关后面,业务代码只管编排逻辑——这套思路,在后面几篇里你还会反复见到。


本文代码为说明思路的简化示意,具体模型名称、接口字段请以 4sapi.com 官方文档为准。涉及付费接口,请做好用量控制与数据合规。

标签:人工智能客服4sapi代理人智能客服

推荐阅读

探索更多前沿洞察与行业干货。