企业把大语言模型接入审批、风控、客服升级和运营流程后,真正棘手的问题通常不是“模型能不能给出答案”,而是“这次答案为什么是这样、谁能负责、下次能否稳定复现”。在高风险场景中,非确定性输出和缺少责任边界会直接削弱系统的可信度。
一种更稳妥的架构是:让 LLM 或智能体负责理解非结构化输入,让 DMN(Decision Model and Notation)等决策模型负责执行明确规则,再用 Agent Skills 组织能力边界,并通过 NeMo Guardrails 一类的护栏控制输入、输出和工具调用。这样,业务负责人可以维护决策逻辑,工程团队则继续负责运行时治理、权限、观测和故障处理。
不要让 LLM 直接拥有最终裁决权
LLM 擅长从邮件、对话、工单和文档中提取事实,也擅长把复杂结果解释成人类容易理解的语言;但它并不适合直接承担所有业务决策。原因很具体:
- 同一个输入可能得到不同表述甚至不同结论;
- 规则变化无法只靠模型版本或提示词清晰追踪;
- 发生争议时,很难回答“采用了哪一条规则”;
- 模型可能把缺失信息、猜测和事实混在一起。
更好的分工是把流程拆成三段:
- 感知与提取:模型从自然语言中提取结构化事实,并标记不确定字段。
- 决策与计算:DMN 决策表、规则引擎或受控代码根据事实给出确定结果。
- 解释与执行:智能体把决策结果解释给用户,或调用经过授权的工具执行后续动作。
这里的关键不是“完全不用 LLM”,而是让模型处于可约束的位置。模型可以建议、归纳和请求补充信息,但不能绕过决策模型自行修改高风险结果。
DMN 把业务规则变成可审计资产
DMN 的价值不只是把 if/else 换成表格,而是把“决定什么”和“依据什么”显式化。以退款审批为例,业务人员可以维护类似这样的决策表:
| 订单金额 | 是否在退款期限内 | 是否存在欺诈信号 | 决策 |
|---|---|---|---|
| 不超过 500 | 是 | 否 | 自动批准 |
| 不超过 500 | 否 | 任意 | 转人工 |
| 超过 500 | 任意 | 任意 | 转人工 |
| 任意 | 任意 | 是 | 拒绝并触发复核 |
实际项目中,这些规则可以落在 DMN 文件和决策引擎中。重要的是为每次决策保存输入事实、规则版本、输出结果和时间戳。这样,审计人员不需要阅读一段不可预测的提示词,便能重建一次决策过程。
业务和工程的职责也会更清晰:业务团队负责规则含义和例外策略,工程团队负责模型调用、版本发布、权限隔离、超时重试和日志留存。规则可变,不代表整个智能体系统都要随之重写。
Agent Skills 负责“能做什么”,Guardrails 负责“不能做什么”
可以把 Agent Skill 理解为一个经过定义的能力单元:它有明确输入、输出、前置条件、工具权限和失败处理。例如,refund_review Skill 可以读取订单信息、调用退款决策服务并生成解释,但不能直接写入支付系统,除非决策结果为“自动批准”且满足幂等条件。
一个实用的 Skill 契约至少应包含:
- 输入字段及类型;
- 缺失字段的处理方式;
- 可调用的工具和权限范围;
- 必须经过确定性决策的步骤;
- 需要人工确认的边界;
- 审计事件格式。
Guardrails 则位于运行时边界上。例如,它可以拒绝包含敏感数据的外发请求,限制模型只能调用白名单工具,强制输出符合 JSON Schema,或者在模型试图直接批准高金额退款时转人工。护栏不是业务规则的替代品,而是防止模型越权、格式失控和流程绕行的安全层。
一个可运行的最小实现
下面的示例不依赖第三方库,模拟“模型提取事实、确定性策略做决定”的最小流程。真实系统中,可以把 extract_facts 替换成 LLM 的结构化输出调用,把 decide_refund 替换成 DMN 引擎或决策服务。
from dataclasses import dataclass, asdict
import json
import re
from typing import Optional
@dataclass
class RefundFacts:
amount: float
days_since_purchase: int
fraud_signal: bool
def extract_facts(text: str) -> RefundFacts:
"""示例解析器;生产环境可替换为带 schema 校验的 LLM 调用。"""
amount_match = re.search(r"金额\s*([0-9]+(?:\.[0-9]+)?)", text)
days_match = re.search(r"购买后\s*([0-9]+)\s*天", text)
if not amount_match or not days_match:
raise ValueError("缺少金额或购买天数,不能进入自动决策")
fraud_signal = any(word in text for word in ("盗刷", "欺诈", "异常登录"))
return RefundFacts(
amount=float(amount_match.group(1)),
days_since_purchase=int(days_match.group(1)),
fraud_signal=fraud_signal,
)
def decide_refund(facts: RefundFacts) -> dict:
"""确定性决策层:规则版本应与结果一起记录。"""
if facts.fraud_signal:
decision = "拒绝并触发复核"
elif facts.amount <= 500 and facts.days_since_purchase <= 30:
decision = "自动批准"
else:
decision = "转人工"
return {
"decision": decision,
"rule_version": "refund-policy-2025-01",
"facts": asdict(facts),
}
def run_agent(user_text: str) -> str:
facts = extract_facts(user_text)
result = decide_refund(facts)
# 模型可以负责把结果改写为自然语言,但不能改写 decision 字段。
return json.dumps(result, ensure_ascii=False, indent=2)
if __name__ == "__main__":
request = "客户申请退款,金额 299,购买后 7 天,没有异常登录"
print(run_agent(request))
运行方式:
python refund_agent.py
这个例子刻意把“提取事实”和“做决定”分开。模型提取错误时,流程应该进入补充信息或人工队列,而不是让模型自行猜测;规则命中后,后续自然语言解释可以由模型生成,但最终的 decision、rule_version 和原始事实必须来自受控组件。
如果要接入 NeMo Guardrails 或其他护栏框架,可以把以下配置思路作为起点。它是示意配置,具体字段需要根据所用版本和部署方式调整:
rails:
input:
- reject_sensitive_data
- require_refund_fields
dialog:
- call_skill: refund_review
output:
- validate_decision_schema
- block_unapproved_payment_action
skills:
refund_review:
allowed_tools:
- order_lookup
- refund_decision_service
requires_human_approval_when:
- decision == "转人工"
- amount > 500
- fraud_signal == true
落地时要守住四条边界
一是区分建议和裁决。 模型输出可以是候选分类、缺失字段提示或解释文本;高风险裁决应来自版本化规则或受控服务。
二是让每次决策可重放。 至少记录输入摘要、结构化事实、规则版本、模型版本、工具调用、最终结果和人工修改。敏感数据需要按合规要求脱敏或分级存储。
三是把异常当作正常路径。 字段缺失、规则冲突、工具超时和护栏拦截都应有明确状态,例如 NEEDS_HUMAN_REVIEW,而不是让智能体继续自由发挥。
四是先从边界清楚的流程开始。 退款、授信、理赔或合规审批都可能很复杂。可以先挑一个规则稳定、输入可结构化、失败代价可控的子流程,验证决策表、审计日志和人工接管机制,再扩展到更多 Skill。
结语:智能体的可信度来自责任分层
Agentic Architecture 的成熟,不是让一个更大的模型包办所有事情,而是把非确定性的理解能力放在合适的位置,把确定性的业务规则交给可审计组件,把越权风险交给运行时护栏。DMN 让业务规则可见、可改、可追溯;Agent Skills 让能力和权限有边界;Guardrails 则把这些边界落实到每一次调用。
采用前可以检查三个问题:
- 最终决定是否能由规则版本和输入事实重建?
- 模型是否拥有超出业务需要的工具权限?
- 缺失信息、规则冲突和高风险结果是否会明确转人工?
如果答案都是否定的,继续堆叠提示词通常不是解决方案。先拆分责任,再设计决策模型和治理边界,往往更接近一套真正能进入生产的智能体架构。