分诊 Agent 什么时候可以自己创建 Jira issue?
打标签不难,关键是把置信度阈值和风险边界放在模型之外,拿不准的工单交给人。
要点速览
- 工具描述比什么都重要,每个标签、每个优先级都要在描述里写清定义。
- 不要让模型决定何时升级:由模型之外的代码同时检查置信度阈值和风险边界。
- 阈值必须来自客户真实的工单分布,并根据审核人的决定持续重新校准。
设想一个 Agent 自行在客户的 Jira 里创建 issue,却把一次严重的登录故障归为低优先级的“功能需求”。客户的支持团队很可能从此再也不信任这个 Agent。
所以在工单分诊中,最难的问题不是“模型标签打得准不准”,而是“什么时候允许代码自己动手”。
本文一步步搭建一个小型 Agent,它只做三件事:通过 schema 严格的工具对工单分类;足够有把握时创建 issue;置信度低或操作越过风险边界时,把工单交给审核人。
客户想“把 AI 引入支持流程”时,这是一个规模小、可衡量的问题,很适合作为第一步尝试。
需要准备什么?
你需要 Claude API 的访问权限、一个至少有一个项目的 Jira Cloud 测试实例、Python,以及几十张已经正确打好标签的历史工单,用作对照。下面的代码做了简化:认证、HTTP 调用和日志存储都换成了占位函数,方便你接到自己常用的库上。
第 1 步:工具描述就是你的分诊规则手册
一个工具由 name、description、input_schema 和可选的 input_examples 字段组成。功夫要下在描述上:讲清楚工具做什么、什么时候用、应该怎样表现。Anthropic 认为,详尽的描述是决定工具质量的最重要因素。
对分诊 Agent 来说,要在描述里给出每个标签和每个优先级的定义,而不只是罗列标签名。给工具加上服务前缀,比如 jira_...,这样 Agent 日后接入其他系统时,模型不容易混淆。
{
"name": "jira_triage_ticket",
"description": "在创建 Jira issue 之前对支持工单分类。每个新工单都必须使用。category:'access' = 无法登录或缺少权限;'bug' = 功能表现与描述不符;'billing' = 发票、付款;'feature_request' = 新功能需求。priority:'critical' = 大量客户无法使用产品;'high' = 单个客户受阻且没有变通办法;'medium' = 有变通办法;'low' = 界面瑕疵或咨询问题。confidence:取值 0 到 1。evidence:工单中支持该判断的简短引文。",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"category": {"type": "string", "enum": ["access", "bug", "billing", "feature_request"]},
"priority": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"evidence": {"type": "string"}
},
"required": ["category", "priority", "confidence", "evidence"],
"additionalProperties": false
}
}
strict: true 会开启 schema 校验,返回的标签一定落在枚举范围内。evidence 字段只要求一句简短的引文作为依据,而不是逐步推理。审核人需要的是能快速核对的证据,而不是一段要读完的论证。
**检查:**跑五张样例工单。每个结果都必须包含全部四个字段,标签必须在枚举内,confidence 必须介于 0 和 1 之间。
第 2 步:强制调用工具,但要为做不到的情况留后路
tool_choice 参数设为 tool 类型时,模型必须且只能调用一个工具。但并非所有模型或配置都支持强制调用。Anthropic 文档明确写道,Sonnet 5.5 在使用 any 或 tool 时会返回 400 错误。
# 简化版:只保留与工具相关的配置
request = {
"tools": [TRIAGE_TOOL],
"tool_choice": {"type": "tool", "name": "jira_triage_ticket"}, # 如果所用模型返回 400,删除此行
}
因此,在依赖强制调用之前,先确认所用的模型是否支持。如果无法强制,代码就必须把“响应中没有工具调用”视为转人工的信号,而不是试图从自由文本里猜标签。
第 3 步:升级逻辑必须放在模型之外
这是最重要的一步。不要让模型决定何时升级,因为它做不到前后一致。触发升级的必须是你的代码。
也不要只看置信度分数:dev.to 上一篇分析 Agent 升级机制的文章把这称为常见错误。要再加一个条件:只要计划中的操作越过风险边界,就升级。在这里,风险边界指的是 critical 工单。
THRESHOLD = 0.85 # 起点,第 5 步再校准
RISKY_PRIORITIES = {"critical"}
def route(triage):
if triage is None:
return "human", "no_tool_call"
if triage["priority"] in RISKY_PRIORITIES:
return "human", "risk_boundary"
if triage["confidence"] < THRESHOLD:
return "human", "low_confidence"
return "auto", "passed_checks"
看两张工单。工单 A 写的是“修改密码后,我们三家子公司都登录不了”,模型返回 access、critical,置信度 0.93。工单 B 写的是“导出按钮有点没对齐”,模型返回 bug、low,置信度 0.62。
如果只看置信度,工单 A 会直接进入 Jira,而它恰恰是客户希望先由人来看的那类工单。上面的 route 函数把两张工单都拦了下来,但原因不同。这个原因必须记录下来,第 5 步要用到。
**检查:**在接入 Jira 之前,为三个升级分支都写好单元测试。
第 4 步:创建 issue,并记录每一个决定
在 Jira Cloud 中,向实例的 /rest/api/3/issue 端点发送 POST 请求即可创建 issue。响应会返回新 issue 的 id、key 和 REST 端点。把这三项都记下来,同时记录 route() 对每张工单给出的原因,没有生成 issue 的工单也要记。
# 简化版:jira_post()、send_to_reviewer()、audit_log()、audit_update() 均为占位函数。
# 请对照客户的项目配置核对 payload 字段名。
def handle(ticket, triage):
decision, reason = route(triage)
record = {"ticket_id": ticket["id"], "triage": triage,
"route": decision, "reason": reason,
"issue_id": None, "issue_key": None, "issue_url": None,
"reviewer_category": None, "reviewer_priority": None}
if decision == "auto":
payload = {"fields": {
"project": {"key": "SUP"},
"summary": ticket["title"],
"issuetype": {"name": "Task"},
"labels": [triage["category"], triage["priority"]],
}}
resp = jira_post("/rest/api/3/issue", payload)
record.update(issue_id=resp["id"], issue_key=resp["key"], issue_url=resp["self"])
else:
send_to_reviewer(ticket, triage, reason)
audit_log(record)
def on_review(ticket_id, category, priority):
# 审核人确认某张工单的标签时调用
audit_update(ticket_id, reviewer_category=category, reviewer_priority=priority)
客户问 issue SUP-… 为什么带某个标签时,打开记录即可:key、标签、置信度、引文证据和路由原因都在,几秒钟就能对清楚。两个 reviewer_* 字段则是下一步的原始数据。
**检查:**在测试项目里创建一个真实 issue,在 Jira 中打开返回的 key,把它的标签和日志对比。然后让一张工单走审核分支,调用 on_review,确认记录里已经存入审核人选择的标签。
第 5 步:阈值不是设一次就完事的数字
置信度阈值是一条分界线:高于它的决定自动处理,低于它的需要人来看。第 3 步里的 0.85 只是起点。
Galileo 的 Agent 监控指南强调,校准是持续性的工作,目标升级率必须来自你自己工单的真实分布,而不是通用的行业数字。
第 4 步的日志让这件事变得具体。一周后,把 triage 与 reviewer_category、reviewer_priority 做对比。你会看到哪些标签模型即使置信度很高也常出错,哪些标签即使置信度一般也几乎总是对的。这就是调高或调低 THRESHOLD、甚至为每个标签单独设阈值的依据。
把升级率作为系统的健康指标来跟踪,并按 reason 的每个取值拆开看。如果客户上线新产品后 low_confidence 的比例突然飙升,很可能是工具描述里缺了某个标签。
常见错误
第一个错误是把工作拆成太多小工具。当 Agent 还需要更新 issue 或添加评论时,应把相关操作合并成一个带 action 参数的工具,这样模型选错工具的情况会更少。
第二个错误是把模型自报的置信度当作经过校准的概率。它只是一个信号,需要用审核人的决定来检验。
第三个错误是隐藏升级原因。审核人需要知道工单被拦下是因为风险还是因为置信度低,两者需要投入的关注程度不同。
放到真实的客户项目里是什么样?
在客户现场,本文的代码大概不是最难的部分。更难的是和支持负责人坐下来,把他们自己对“critical”的定义写进描述,并商定由谁来当审核人。
团队还要明确什么算风险边界。第一件事是向客户要 50 张已经由人分诊过的历史工单,在碰真实 Jira 之前,先用它们评测 Agent。
如果你正在转向前线部署工程师(FDE)岗位,这是一个很有分量的作品集项目。简历上不要只写“搭建了工单分诊 Agent”,而要写明你把升级逻辑和模型分离开、阈值是怎么选的,以及校准后升级率发生了怎样的变化。
在职位描述里,“human-in-the-loop”“tool use”“与客户系统集成”这类说法,指的都是这项能力。
懂得何时停手的 Agent,往往比总是自作主张的 Agent 更快赢得客户信任。所以,花在 route() 函数上的时间,应该比花在提示词上的更多。
这篇文章对你有帮助吗?
感谢你的反馈!