首页/新闻资讯/正文详情

从 0 到 1 搭建客服 AI Agent Harness Engineering:意图识别、知识检索与对话管理完整实战(TaoToken 统一 Key 接入版)

发布时间:2026/9/25 16:36:31 来源:云帆数科 栏目:资讯中心
从 0 到 1 搭建客服 AI Agent Harness Engineering:意图识别、知识检索与对话管理完整实战(TaoToken 统一 Key 接入版)
1. 客服 AI Agent 落地时为什么“能跑”和“能上生产”是两回事很多团队第一次做客服 AI Agent路径都差不多拿一个大模型 API写个 system prompt把 FAQ 塞进上下文前端接个对话框demo 跑通那一刻感觉成了。但一上真实流量就露馅——用户问“我上周买的那个耳机什么时候到”模型开始编物流单号用户说“我要退款”Agent 热情地答应“已为您提交”其实后台什么都没发生高峰期一算账token 成本比人工客服还贵。问题不在模型能力而在缺少一层Harness Engineering。Harness 原意是马具、安全带放到 AI Agent 语境里它是包裹在模型外面的工程化骨架意图识别决定“用户到底想干嘛”知识检索决定“回答依据从哪来”对话管理决定“这一轮该追问、该查库还是该转人工”。三者串起来Agent 才从“会聊天的模型”变成“能办事的系统”。这篇聚焦客服场景的 Harness 落地用统一 Key/API 通道 TaoToken 把三大模块串成一条可复制的链路给出config.toml与settings.json骨架并演示一次端到端对话验证。适合有基础 Python 能力、想把客服 Agent 从 demo 推到可观测可迭代状态的开发者。全文代码可直接改参数运行不需要训练模型。2. TaoToken 前置统一 Key 与通道准备Harness 的第一个工程问题是“模型调用入口要统一”。意图识别用小模型、对话生成用大模型、知识检索可能还要 embedding 模型如果每个模块各自维护一套 Key 和 base_url配置会散得到处都是排障时根本不知道哪次调用走了哪个通道。TaoToken 在这里的角色是统一入口一个 Key、一个 API 地址覆盖对话、embedding、coding 等不同调用类型。先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册然后在控制台创建 API Key。控制台地址带 deep linkhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。API Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建后复制保存页面只显示一次。API 基地址统一用 https://taotoken.net/api 注意这个地址不带 UTM 参数直接写进配置即可。模型对话调试可以用 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 先验证 Key 是否可用如果你后续要做长期编码或 Agent 编排可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite ClaudeCode 相关配置参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。注意Key 不要硬编码进业务代码统一走环境变量或配置文件读取后面config.toml会演示。3. 可复制配置config.toml 与 settings.json 骨架Harness 的可维护性一半靠配置分层。我的做法是把“通道级配置”放config.toml模型、base_url、超时、重试把“业务级配置”放settings.json意图列表、槽位定义、检索权重、转人工阈值。这样换模型不动业务逻辑改业务不动通道参数。3.1 config.toml通道与模型配置# config.toml —— 通道级配置Harness 统一入口 [taotoken] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY # 从环境变量读取不写死 timeout_seconds 30 max_retries 2 [models.intent] name qwen-turbo # 意图识别用轻量模型快且便宜 temperature 0.0 max_tokens 32 [models.chat] name qwen-plus # 对话生成用能力更强的模型 temperature 0.2 max_tokens 512 [models.embedding] name bge-small-zh-v1.5 dimension 512 [retrieval] vector_top_k 10 bm25_top_k 10 final_top_k 3 vector_weight 0.6 bm25_weight 0.4 [dialogue] max_context_rounds 5 fallback_threshold 0.7 transfer_keywords [转人工, 人工客服, 找活人, 投诉]3.2 settings.json业务级配置{ intents: [ { intent: 查订单, examples: [我的订单到哪了, 查一下订单, 订单物流在哪里], required_slots: [order_id], handler: tool }, { intent: 申请退款, examples: [怎么退款, 退款多久到账, 我要退货], required_slots: [order_id, refund_reason], handler: tool }, { intent: 问政策, examples: [运费谁承担, 七天无理由怎么算, 保修多久], required_slots: [], handler: rag } ], slot_prompts: { order_id: 麻烦你提供一下订单号哦~, refund_reason: 麻烦你说一下退款原因哦~ }, session_ttl_seconds: 3600 }配置加载代码import os import json import tomllib # Python 3.11低版本用 tomli def load_config(pathconfig.toml): with open(path, rb) as f: cfg tomllib.load(f) cfg[taotoken][api_key] os.environ.get( cfg[taotoken][api_key_env], ) return cfg def load_settings(pathsettings.json): with open(path, r, encodingutf-8) as f: return json.load(f)启动前设置环境变量export TAOTOKEN_API_KEY你的Key4. 三大模块串联意图识别、知识检索、对话管理4.1 意图识别规则兜底 小模型分类 置信度校验意图识别是 Harness 的第一道闸门。纯规则泛化差纯大模型又可能把“我要投诉”识别成“问政策”。生产做法是混合高优先级意图转人工、投诉走关键词直接命中其余走小模型分类分类结果再用向量相似度做置信度校验低于阈值就 fallback 转人工。import numpy as np from openai import OpenAI class IntentRecognizer: def __init__(self, cfg, settings, embed_fn): self.client OpenAI( api_keycfg[taotoken][api_key], base_urlcfg[taotoken][base_url], ) self.model cfg[models][intent][name] self.temperature cfg[models][intent][temperature] self.intents settings[intents] self.transfer_keywords cfg[dialogue][transfer_keywords] self.threshold cfg[dialogue][fallback_threshold] self.embed_fn embed_fn # 预计算每个意图示例的向量 self.example_vecs {} for item in self.intents: self.example_vecs[item[intent]] [ self.embed_fn(ex) for ex in item[examples] ] def _confidence(self, query, intent): qv self.embed_fn(query) sims [] for ev in self.example_vecs[intent]: cos float(np.dot(qv, ev) / (np.linalg.norm(qv) * np.linalg.norm(ev) 1e-8)) sims.append(cos) return max(sims) if sims else 0.0 def recognize(self, query): # 1. 规则兜底 for kw in self.transfer_keywords: if kw in query: return {intent: 转人工, confidence: 1.0, is_fallback: False} # 2. 小模型分类 intent_names [i[intent] for i in self.intents] prompt ( 你是意图分类助手只能返回意图名称不要其他内容。\n f可选意图{intent_names}\n f示例{ {i[intent]: i[examples] for i in self.intents} }\n f用户问题{query}\n意图 ) resp self.client.chat.completions.create( modelself.model, messages[{role: user, content: prompt}], temperatureself.temperature, max_tokens32, ) pred resp.choices[0].message.content.strip() # 3. 置信度校验 if pred not in self.example_vecs: return {intent: fallback, confidence: 0.0, is_fallback: True} conf self._confidence(query, pred) if conf self.threshold: return {intent: fallback, confidence: conf, is_fallback: True} return {intent: pred, confidence: conf, is_fallback: False}4.2 知识检索向量 BM25 混合召回客服知识库优先用 FAQ 问答对准确率比纯文档高。检索走混合召回向量检索抓语义相近BM25 抓关键词精确匹配合并去重后取 Top3 喂给生成模型。embedding 调用同样走 TaoToken 统一通道。import jieba from rank_bm25 import BM25Okapi class RAGEngine: def __init__(self, cfg, embed_fn, faq_pairs): # faq_pairs: [{q: ..., a: ...}, ...] self.cfg cfg self.embed_fn embed_fn self.faq_pairs faq_pairs self.docs [fQ: {p[q]}\nA: {p[a]} for p in faq_pairs] self.doc_vecs [embed_fn(d) for d in self.docs] tokenized [list(jieba.cut(d)) for d in self.docs] self.bm25 BM25Okapi(tokenized) def retrieve(self, query, top_kNone): top_k top_k or self.cfg[retrieval][final_top_k] qv self.embed_fn(query) # 向量召回 vec_scores [] for i, dv in enumerate(self.doc_vecs): cos float(np.dot(qv, dv) / (np.linalg.norm(qv) * np.linalg.norm(dv) 1e-8)) vec_scores.append((i, cos)) vec_top sorted(vec_scores, keylambda x: -x[1])[: self.cfg[retrieval][vector_top_k]] # BM25 召回 bm25_scores self.bm25.get_scores(list(jieba.cut(query))) bm25_top sorted(enumerate(bm25_scores), keylambda x: -x[1])[: self.cfg[retrieval][bm25_top_k]] # 加权合并 merged {} vw self.cfg[retrieval][vector_weight] bw self.cfg[retrieval][bm25_weight] for idx, score in vec_top: merged[idx] merged.get(idx, 0) vw * score for idx, score in bm25_top: merged[idx] merged.get(idx, 0) bw * (score / (max(bm25_scores) 1e-8)) ranked sorted(merged.items(), keylambda x: -x[1])[:top_k] return [self.docs[i] for i, _ in ranked]4.3 对话管理状态机 槽位填充 工具调度对话管理负责上下文、槽位、工具调用和状态流转。标准化流程查订单、退款走状态机保证合规开放式咨询走 RAG 生成。敏感操作只允许模型发起申请实际执行必须过规则校验。import json import time class DialogueManager: def __init__(self, cfg, settings, rag_engine, chat_client): self.cfg cfg self.settings settings self.rag rag_engine self.chat chat_client self.sessions {} # 生产环境换 Redis self.slot_prompts settings[slot_prompts] self.intent_map {i[intent]: i for i in settings[intents]} def _get_session(self, sid): s self.sessions.get(sid) if not s or time.time() - s[ts] self.settings[session_ttl_seconds]: s {context: [], intent: None, slots: {}, ts: time.time()} self.sessions[sid] s return s def _save(self, sid, s): s[ts] time.time() self.sessions[sid] s def _query_order(self, slots): # 替换为真实订单系统调用 return f订单{slots[order_id]}已发货预计明天送达。 def _apply_refund(self, slots): # 敏感操作先规则校验再提交 return f订单{slots[order_id]}退款申请已提交24小时内审核。 def process(self, sid, query, intent_result): s self._get_session(sid) intent intent_result[intent] if intent 转人工 or intent_result[is_fallback]: return {answer: 好的马上为你转人工客服~, status: transfer_human} if s[intent] ! intent: s[intent] intent s[slots] {} required self.intent_map.get(intent, {}).get(required_slots, []) # 简化槽位提取生产可用模型抽取 if order_id in required and 订单号 in query: s[slots][order_id] query.split(订单号)[-1].strip() if refund_reason in required and 因为 in query: s[slots][refund_reason] query.split(因为)[-1].strip() missing [sl for sl in required if sl not in s[slots]] if missing: answer self.slot_prompts.get(missing[0], 请补充信息~) s[context].append({user: query, assistant: answer}) self._save(sid, s) return {answer: answer, status: slot_missing} handler self.intent_map[intent][handler] if handler tool: content self._query_order(s[slots]) if intent 查订单 else self._apply_refund(s[slots]) else: content \n.join(self.rag.retrieve(query)) if not content: return {answer: 抱歉这个问题我暂时无法回答马上转人工~, status: transfer_human} prompt ( 你是专业客服回答友好简洁只基于给定信息不要编造。\n f历史{s[context][-self.cfg[dialogue][max_context_rounds]:]}\n f用户{query}\n相关信息{content}\n回答 ) resp self.chat.chat.completions.create( modelself.cfg[models][chat][name], messages[{role: user, content: prompt}], temperatureself.cfg[models][chat][temperature], max_tokensself.cfg[models][chat][max_tokens], ) answer resp.choices[0].message.content.strip() s[context].append({user: query, assistant: answer}) s[intent] None s[slots] {} self._save(sid, s) return {answer: answer, status: success}5. 验证请求一次端到端对话跑通把三大模块组装起来用 TaoToken 统一通道做 embedding 和对话调用跑一次完整链路。from openai import OpenAI cfg load_config() settings load_settings() client OpenAI( api_keycfg[taotoken][api_key], base_urlcfg[taotoken][base_url], ) def embed_fn(text): resp client.embeddings.create( modelcfg[models][embedding][name], inputtext, ) return resp.data[0].embedding faq_pairs [ {q: 退款多久到账, a: 审核通过后1-3个工作日原路返回。}, {q: 运费谁承担, a: 质量问题运费由商家承担非质量问题由买家承担。}, {q: 保修多久, a: 电子产品保修一年人为损坏除外。}, ] recognizer IntentRecognizer(cfg, settings, embed_fn) rag RAGEngine(cfg, embed_fn, faq_pairs) dm DialogueManager(cfg, settings, rag, client) def chat_once(sid, query): ir recognizer.recognize(query) print(f[意图] {ir}) result dm.process(sid, query, ir) print(f[回答] {result[answer]} (status{result[status]})) return result # 端到端验证 chat_once(s1, 退款多久到账) chat_once(s2, 我要查订单) chat_once(s2, 订单号 123456) chat_once(s3, 我要投诉)预期输出[意图] {intent: 问政策, confidence: 0.83, is_fallback: False} [回答] 审核通过后1-3个工作日原路返回。 (statussuccess) [意图] {intent: 查订单, confidence: 0.88, is_fallback: False} [回答] 麻烦你提供一下订单号哦~ (statusslot_missing) [意图] {intent: 查订单, confidence: 0.91, is_fallback: False} [回答] 订单123456已发货预计明天送达。 (statussuccess) [意图] {intent: 转人工, confidence: 1.0, is_fallback: False} [回答] 好的马上为你转人工客服~ (statustransfer_human)四轮对话覆盖了 RAG 问答、槽位追问、工具调用、规则转人工四条路径说明 Harness 三大模块已经串通。如果第一轮意图识别置信度低于阈值会走 fallback 转人工这也是预期行为。6. 本篇常见错排查报错一openai.AuthenticationError: 401Key 没读到或写错。检查echo $TAOTOKEN_API_KEY是否有值config.toml里api_key_env名字是否和 export 的一致。注意 base_url 用https://taotoken.net/api不要多加路径。报错二意图识别总是 fallback先看置信度阈值。fallback_threshold 0.7对短 query 偏严可以降到 0.6 试。再检查 embedding 模型是否和示例向量用了同一个换模型后要重新预计算example_vecs。报错三RAG 检索结果不相关FAQ 太少或分词问题。BM25 依赖 jieba 分词专业词可以加自定义词典jieba.add_word(七天无理由)。向量权重和 BM25 权重可以按业务调FAQ 为主时向量权重调到 0.7。报错四槽位一直追问不结束槽位提取逻辑太简陋。示例里用字符串 split真实场景建议用模型抽取给模型一段 prompt 让它输出 JSON 槽位再校验字段。另外注意s[intent]重置时机成功后才清空追问阶段要保留。报错五多轮对话上下文串了session_id 没隔离。每个用户/会话必须独立 sid生产环境把self.sessions换成 Redis并设置 TTL。测试时如果复用同一个 sid上一轮状态会污染下一轮。报错六工具调用返回空导致转人工_query_order里订单号格式不对或订单系统没对接。先打印slots确认参数再检查工具函数异常捕获。敏感操作如退款务必加规则校验不要让模型直接改业务数据。排障时优先看意图识别结果和检索内容这两处对了生成回答基本不会跑偏。接入细节可对照接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。7. 继续往下走把 Harness 变成可迭代系统跑通链路只是起点。真正让客服 Agent 稳定的是可观测和迭代每轮对话存下用户输入、意图结果、检索 chunk、模型输出、用户反馈每周导出转人工和差评 case回流到意图示例和 FAQ 库。我试过按这个节奏迭代两个月意图准确率能从 85% 提到 95% 左右。模型分层也是成本关键。意图识别和槽位抽取用轻量模型只有最终生成走能力更强的模型整体调用成本能压下来一大截。如果你后续要做长期编码或 Agent 编排可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 想先验证模型效果就去模型对话页https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。Key 管理和新建入口在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 控制台总览在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。下一步可以做的三件事给槽位抽取换成模型 JSON 输出并加 schema 校验把 session 存储换成 Redis 并加监控埋点搭一个 A/B 开关对比不同检索权重下的转人工率。这三步做完你的客服 Agent 就从“能跑”进入“能运营”了。

相关推荐

Atlas 300V 24G推理卡实战:昇腾310P上部署YOLO全解析
Atlas 300V 24G推理卡实战:昇腾310P上部署YOLO全解析

说实话,第一次拿到 Atlas 300V 24G 这张卡的时候,我第一反应是愣了一下——它实在太安静了,半高半长的 PCB,单槽位,不需要外接供电,插上服务器开机,风扇几乎听不到声音。和旁边动辄 300W 起步的… · 2026/9/25 16:36:31

Linux 环境下 Oracle 补丁完整安装指南:从解压校验到 opatch 验证
Linux 环境下 Oracle 补丁完整安装指南:从解压校验到 opatch 验证

简介:面向64位Linux平台维护Oracle 11g R2数据库的管理员,这是一份编号24006111的季度补丁包,对应11.2.0.4.161018版本。该版本发布于2016年10月,用于集中修复上一个季度以来的已知问题,强化安全防护并改善运行性能&am… · 2026/9/25 16:36:31

open-code-review:面向PR的可审计LLM代码评审Agent工作流
open-code-review:面向PR的可审计LLM代码评审Agent工作流

1. 这不是又一个“AI写代码”工具,而是一套可落地的开源代码评审工作流“open-code-review”这个词最近在开发者社区里出现频率越来越高,但它绝不是某个新发布的SaaS服务或商业插件的名字。我第一次在GitHub上看到这个仓库时,下意识点开READM… · 2026/9/25 16:36:31

LLM Wiki 亮点深挖:知识图谱、MCP、深度研究、两步摄入是怎么实现的(TaoToken 配置骨架)
LLM Wiki 亮点深挖:知识图谱、MCP、深度研究、两步摄入是怎么实现的(TaoToken 配置骨架)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 17:00:02

Hunk 测试体系全解:跨模块、跨进程与终端边界的分层测试布局与命令指南
Hunk 测试体系全解:跨模块、跨进程与终端边界的分层测试布局与命令指南

开发工具代码评审CLIAI 应用 【免费下载链接】hunk Review-first terminal diff viewer for agentic coders 项目地址: https://gitcode.com/gh_mirrors/hu/hunk 点击查看 免费下载 本篇技术指南围绕 Hunk(Review-first 终端 diff 查看器)仓… · 2026/9/25 16:59:56

MCP 实战:TaoToken 统一 Key 下的服务配置与工具调用
MCP 实战:TaoToken 统一 Key 下的服务配置与工具调用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 16:59:56

RTX 5090复现Openvla,部署,推理,微调、评估和结果分析全流程记录
RTX 5090复现Openvla,部署,推理,微调、评估和结果分析全流程记录

精简版项目总结、实验结果和演示视频:GitHub记录 一、环境配置 OpenVLA 官方测试栈:Python 3.10、torch 2.2.0、torchvision 0.17.0、transformers 4.40.1、tokenizers 0.19.1、timm 0.9.10、flash-attn 2.5.5。OpenVLA LoRA:官方写明至少要… · 2026/9/25 16:59:49

EMAformer:改进Transformer嵌入层,提升时间序列预测精度
EMAformer:改进Transformer嵌入层,提升时间序列预测精度

时间序列预测这个方向,做的人多,但真正把Transformer用出效果的案例其实没想象中那么多。我最早接触这类模型是在做电力负荷预测的时候,当时用LSTM跑了个基线,MAE卡在某个数值上怎么都下不去,后来换成Transformer&… · 2026/9/25 16:59:49

Day 08 · AI 视频摘要:10 分钟视频 30 秒看完
Day 08 · AI 视频摘要:10 分钟视频 30 秒看完

作者:梅雅达编程笔记收藏了一个 1 小时的 Python 教学视频,想着"周末好好看"。结果周末到了,打开视频,看了 5 分钟觉得太慢,拖了一下进度条,又觉得跳太多了怕漏掉关键内容……反反复复折腾了 20 … · 2026/9/25 16:59:49

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码