接入项目:Python API 与 AI 编程助手
这一章把终端请求变成一段能读文件、检查返回值、选择本地队列的 Python 程序。你将看到完整的最小过程,但程序只打印建议,不执行退款、发消息或修改账户。
配套案例与本章主程序都使用标准库 HTTP。官方 SDK 是后文的可选路线;不安装它也能完成三个案例。这样你能直接看见发送了什么 JSON、收到什么字段,以及本地代码从哪里开始负责业务动作。
环境和文件
先按快速上手准备 Python 环境与 TYPESAFE_API_KEY。确认当前终端能使用 python;如果重开了终端,可能需要重新激活环境、重新设置变量。
在练习目录创建 UTF-8 文件 ticket.txt,放入一条合成消息:
My subscription was charged twice this month.
Could someone check the duplicate payment?
另建 hello_jev_once.py,粘贴下面程序。这是一个需要有效 Key 的单次真实调用练习。要在没有 Key 时跑完整流程,请使用配套命令的 --mode offline,不要把下面的 HTTP 返回偷偷替换成样例后仍称为实测。
完整最小程序
import json
import math
import os
import sys
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, Request, build_opener
class NoRedirects(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
raise HTTPError(req.full_url, code, "Redirect refused", headers, fp)
opener = build_opener(NoRedirects())
key = os.environ.get("TYPESAFE_API_KEY", "").strip()
if not key:
raise SystemExit("Set TYPESAFE_API_KEY before this live call.")
source = Path(sys.argv[1] if len(sys.argv) > 1 else "ticket.txt")
try:
message = source.read_text(encoding="utf-8").strip()
except (OSError, UnicodeError):
raise SystemExit("Cannot read the input as UTF-8 text.")
if not message:
raise SystemExit("The input is empty.")
criteria = {
"billing": "Only charges, invoices, or subscriptions.",
"technical": "Only login failures or broken product features.",
"mixed": "Both billing and technical concerns are present.",
"other": "Neither category fits, or the message is unclear.",
}
payload = {
"model": os.environ.get("TYPESAFE_MODEL", "jev-latest"),
"state": {"customer_message": message},
"questions": {
"department": {
"type": "choice",
"instructions": (
"Select the queue for `customer_message`. "
"Use only concerns stated in that message."
),
"criteria": criteria,
}
},
}
request = Request(
"https://api.typesafe.ai/v1/systemone",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": "Bearer " + key,
"Content-Type": "application/json",
},
method="POST",
)
try:
with opener.open(request, timeout=30) as http_response:
result = json.loads(http_response.read().decode("utf-8"))
except HTTPError as exc:
raise SystemExit(f"HTTP {exc.code}; check access and request shape.")
except (URLError, TimeoutError):
raise SystemExit("Network error or timeout; no queue was selected.")
except (json.JSONDecodeError, UnicodeError):
raise SystemExit("The service response was not valid UTF-8 JSON.")
try:
answer = result["answers"]["department"]
choice = answer["choice"]
confidence = answer["confidence"]
valid = (
answer["type"] == "choice"
and choice in criteria
and type(confidence) in (int, float)
and 0 <= confidence <= 1
and math.isfinite(confidence)
)
except (KeyError, TypeError):
valid = False
if not valid:
raise SystemExit("Invalid department answer; manual review needed.")
# Teaching policy only: validate this threshold on your own data.
queue = choice
if confidence < 0.75 or choice in {"mixed", "other"}:
queue = "review"
print(json.dumps({
"mode": "live",
"source_file": source.name,
"suggested_queue": queue,
"raw_response": result,
}, ensure_ascii=False, indent=2))
运行并保存结果:
python hello_jev_once.py ticket.txt > first-live-result.json
python -m json.tool first-live-result.json
Windows 使用虚拟环境解释器执行同一个 .py 文件即可。输出文件重定向后,命令失败时可能留下空文件,先看终端退出信息,再把文件当成有效结果。这个程序没有后台任务,也没有自动重试;每运行一次就尝试一次 HTTP 请求。
HTTP 请求只发往固定 API 地址,NoRedirects 拒绝服务器重定向,避免认证信息被转发到其他地址或明文 HTTP。收到 3xx 状态时程序停止;先核对官方端点和网络配置,再决定是否重试。
接口位置与字段根据 TypeSafe API 文档核对。主程序做了本次分支需要的检查,保留其他原始字段供你阅读;它不是涵盖全部 API 字段的通用验证器。
从哪一行开始是你的业务规则
程序把四个候选类别放在 criteria 中。返回的 choice 必须属于这个集合,confidence 必须是有限的 0–1 数值,类型也要匹配。缺字段或类型不对时停止,避免把“调用失败”当成默认部门。
confidence < 0.75 是教学策略,不是 TypeSafe 给所有业务规定的安全线。这里把 mixed 与 other 也放入 review,是因为这个小工具尚未定义跨部门协作方式。要改这条规则,先写出你希望处理的反例,再在保留样本上检查影响。
如果服务成功返回,但建议是 review,这不是程序错误。程序已经按定义完成工作。你需要追问的是:这条消息确实模糊吗?候选项是否重叠?当前规则是否故意要求人工处理?这个区分能避免把所有复核都当作“需要修好的失败”。
读取结果时,先看原文,再看 raw_response.answers.department,最后看 suggested_queue。模型答案与本地建议应同时保存:只留最终队列,会丢掉后来分析阈值和错误原因所需的信息。
改成你的项目时,保留这些边界
把文件读取替换成数据库查询,是一类改动;把问题标准替换成自己的业务定义,是另一类。每次先改一处并验证。已有可信的字段直接传入,不要让模型从无关长文中再次猜出这些字段。
一次请求共享同一份输入的多个问题可以放在 questions 中。若第二个问题必须看到第一个问题的返回值,则由代码组织后续请求;不能期待同一对象中的先后顺序实现串行推理。构建方式
接到生产任务前,还需要完善请求日志、受控重试、完整验证与结果存储。对超时不要记为某个默认类别;对认证失败不要无限重试。三个案例给出更完整的边界处理,故障排查提供逐层检查顺序。
做三次边界练习
第一次,把 ticket.txt 暂时改成空白文件,再运行。程序应在发送 HTTP 请求前停止。这个练习检查的是输入保护,不需要浪费一次模型调用;恢复原文后再继续。第二次,在一个没有设置 Key 的新终端运行,确认它明确提示配置问题。缺少凭据不应该被记成客户消息“不属于任何部门”。
第三次,保留“扣款两次”,再加一句 “I also cannot log in.”。先在纸上写出预期:类别定义应允许 mixed,本地策略应进入 review。有真实访问权限时再执行并比较。若结果没有符合预期,检查原始返回;不要只把最终队列改成你喜欢的值,掩盖模型与标准的分歧。
| 检查层 | 问题 | 可保存的证据 |
|---|---|---|
| 输入 | 是否读到预期文件,文本是否完整 | 输入文件副本、文件名 |
| 请求 | 是否发送了这版问题与模型名称 | 不含密钥的请求对象 |
| 返回 | 答案是否满足字段与类型约束 | 原始响应或脱敏错误 |
| 策略 | 为什么选这个本地队列 | 阈值版本与分支条件 |
如果要让编程助手测试错误响应,可以用测试替身返回缺少 answers 的字典,验证程序停止;也可以给出低置信度样本,验证进入复核。这些都是代码检查,测试替身的数字不能进入模型效果统计。配套案例的 fixture 为同类检查提供可重复材料,并通过指纹绑定防止误用旧返回。
保存测试记录时,写明本次有没有网络请求。一个从未访问服务的测试可以证明校验和分支可靠,却无法证明服务器今天可用。这个区分也能帮助你理解为什么离线测试全部通过之后,第一次真实调用仍可能遇到账号或网络问题。
可选路线:使用官方 Python SDK
SDK 的价值是提供类型化对象和客户端封装。下面是另一种接入方法,不是运行本书代码所必需的依赖。
python -m pip install typesafe-sdk
python -m pip show typesafe-sdk
from typesafe_sdk import Choice, TypeSafeClient
with TypeSafeClient() as client:
response = client.system_one(
model="jev-latest",
state="Please send a receipt for my subscription.",
questions={
"queue": Choice(
instructions="Choose a queue for the stated request.",
criteria={
"billing": "Receipts and payment questions.",
"other": "Every other request or unclear input.",
},
)
},
)
print(response.answers["queue"].choice)
安装包名是 typesafe-sdk,导入名是 typesafe_sdk,方法是 system_one。以上同步写法与官方 SDK 文档核对;本版没有用凭据执行此片段。SDK 的默认重试策略与最小 HTTP 练习不同,选择后应记录实际安装版本。Python SDK、客户端说明
给编程助手一个可以验收的任务
下面的任务可以直接交给你的编程助手。它规定了输入输出和失败方式,也避免助手凭熟悉的聊天接口猜测字段。
请基于当前项目实现一个本地工单队列建议工具。
先阅读 TypeSafe 当前 API 文档,使用 state/questions/answers。
端点是 /v1/systemone,默认模型是 jev-latest。
用 TYPESAFE_API_KEY 环境变量,不读取或打印密钥值。
使用 Python 标准库 HTTP,读取 UTF-8 工单并保存原始响应。
把问题、枚举值和阈值集中定义;错误输入与错误响应必须停止。
离线 fixture 和真实调用应有不同标记,禁止伪造用量或耗时。
只写本地建议,不发消息、不退款、不修改远程数据。
完成后演示正常、空输入、缺字段和需要复核的分支。
官方还提供 TypeSafe Skill。愿意在自己的编程环境安装时,可按官方 Skill 页面使用 npx skills add typesafe-ai/skills --skill typesafe-ai 并选择对应工具;这是可选的辅助资料安装,与 Python 包是两回事。仍应检查助手生成的接口字段和标准,不能把安装成功当成接入验证。
配套项目的稳定入口是 python -m examples.hello_jev,后接 tickets、content 或 route,并指定 --mode offline 或 --mode live。TYPESAFE_MODEL 可覆盖默认模型;记录请求模型与响应模型,才能知道一次结果来自哪个版本。