【技术专栏】 AI开发
【内容摘要】 综合运用提示词、结构化输出、校验、评估与日志,构建一个可运行的中文客服意图分类命令行工具。
前面的系列已经分别介绍了 API 调用、提示词、结构化输出、错误处理、日志和评估。本篇把这些能力组合成一个小项目:输入一条中文客服消息,模型将它归入固定意图,程序校验结果并记录耗时;随后用一组带标签的样例做简单回归测试。重点不是追求复杂架构,而是建立“模型给候选、程序做约束、测试发现退化”的基本工程闭环。
先定义任务和边界
本项目只识别四种意图:退款、物流、账户、其他。模型不能直接退款、查询订单或修改账户,它只返回分类结果和一句理由。真正的业务动作仍应由普通 Python 代码调用受控接口完成,这样即使模型判断错误,也不会直接造成外部副作用。
程序的数据流是:读取一条消息 → 组装提示词 → 调用模型 → 解析 JSON → 检查字段和枚举值 → 写入日志。测试时再重复这条流程,统计分类准确率。把校验放在模型调用之后,可以避免把任意字符串误当成业务指令。
准备环境和配置
在独立虚拟环境中安装官方 Python SDK:
1 2 3
|
python -m venv .venv source .venv/bin/activate python -m pip install openai python-dotenv
|
在项目目录创建 .env。下面的模型名和密钥只是占位符,真实密钥只能由环境变量提供:
1 2 3
|
OPENAI_API_KEY=替换为你的真实密钥 MODEL_NAME=替换为你可用的模型名称 OPENAI_BASE_URL=
|
不要提交 .env。如果使用兼容服务,只有在服务商文档明确支持相同接口时,才填写 OPENAI_BASE_URL 和对应模型名。
编写分类器
创建 intent_classifier.py。示例使用 OpenAI Python SDK 的 Responses API;response.output_text 是 SDK 提供的文本结果便捷属性。模型被要求只返回 JSON,但程序仍然必须自己解析和校验,不能只相信提示词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
import json import logging import os import sys import time
from dotenv import load_dotenv from openai import OpenAI
load_dotenv() MODEL = os.environ["MODEL_NAME"] client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"), base_url=os.environ.get("OPENAI_BASE_URL") or None)
LABELS = {"退款", "物流", "账户", "其他"} INSTRUCTIONS = """ 你是中文客服意图分类器。只能从退款、物流、账户、其他中选择一个 label。 只返回 JSON,不要 Markdown 代码围栏,格式必须是: {"label": "退款", "reason": "不超过20字的判断依据"} 无法确定时选择其他。不要执行任何操作。 """.strip()
logging.basicConfig(filename="classifier.log", level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def classify(text: str) -> dict: started = time.perf_counter() response = client.responses.create( model=MODEL, instructions=INSTRUCTIONS, input=text, ) data = json.loads(response.output_text) if set(data) != {"label", "reason"}: raise ValueError("返回字段不符合约定") if data["label"] not in LABELS or not isinstance(data["reason"], str): raise ValueError("label 或 reason 无效") logging.info("label=%s elapsed_ms=%.1f", data["label"], (time.perf_counter() - started) * 1000) return data
if __name__ == "__main__": message = " ".join(sys.argv[1:]).strip() if not message: raise SystemExit("用法:python intent_classifier.py '我的包裹到哪里了?'") print(json.dumps(classify(message), ensure_ascii=False))
|
运行前确认环境变量和模型可用,然后执行:
1
|
python intent_classifier.py "我想查一下快递到哪了"
|
这里没有写死某个返回内容,因为模型输出会随模型、提示词和服务状态变化。可验证的部分是:输出能否被 json.loads 解析,label 是否属于允许集合,以及日志是否记录了耗时。
加入最小回归测试
新建 cases.jsonl,每行是一条 JSON:
1 2 3 4
|
{"text": "这个月怎么扣了两次钱", "label": "退款"} {"text": "快递三天没有更新", "label": "物流"} {"text": "我忘记登录密码了", "label": "账户"} {"text": "你们周末营业吗", "label": "其他"}
|
可以在主程序中增加一个测试函数,复用同一个 classify:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
def evaluate(path: str) -> float: total = correct = 0 with open(path, encoding="utf-8") as file: for line in file: case = json.loads(line) try: result = classify(case["text"]) correct += result["label"] == case["label"] except (json.JSONDecodeError, ValueError) as exc: logging.warning("invalid result: %s", exc) total += 1 return correct / total if total else 0.0
|
这个指标只是小样本准确率,不代表真实线上效果。测试集应包含同一意图的不同说法、边界案例和容易混淆的句子;每次修改提示词、模型或参数,都重新运行并比较结果。若只看一次手工演示,很容易把偶然正确误认为稳定能力。
常见问题
为什么已经要求 JSON,还要校验? 提示词是软约束,模型可能输出解释文字、缺字段或不存在的标签。解析和枚举校验是程序边界;失败后可以记录原始响应,并按项目策略重试或转人工。
为什么不让模型直接调用退款接口? 分类任务和执行任务应分离。高风险动作需要权限检查、参数校验和人工确认,不能因为模型“理解了”就自动执行。
准确率下降怎么办? 先查看日志中的失败样例,而不是盲目提高提示词长度。补充有代表性的测试案例,明确标签边界,并固定模型版本和关键参数,才能定位变化来源。
小结
一个可用的 AI 功能不只是一次 API 调用。本文用一个小型分类器串起了输入、提示词、结构化解析、业务校验、日志和回归测试:模型负责提出候选判断,代码负责限制结果,测试负责持续发现退化。沿着这个闭环继续扩展时,可以加入重试、人工复核或指标存储,但每一步都应保持边界清晰、结果可验证。
技术分享自:时光笔记 (wxy.email) | 华为云开发者社区
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
评论(0)