上篇回顾:上一篇我们聊了 DIFY 的边界,结论是它适合做"接客"的轻量级编排,但核心逻辑还得靠代码。这一篇,我想把"代码"这件事聊透。
面试中最虚的就是被问到"你具体怎么做的?"。如果我说"我用 DIFY 做了个流程",那太浅了。但如果我说"我手写了一套 Harness 机制来管控 AI 的行为",这就有了技术深度。
今天,我就以一个虚构但极其常见的 "订单核查机器人" 为例,复盘一下我是如何用 Python 实现 Harness Engineering 的。
1. 场景设定:AI 的"幻觉"是致命的
假设业务场景是这样的:用户问客服机器人------"订单号 ORD12345 的物流卡住了,发票金额是不是多算了?"
如果直接用 DIFY 或者简单的 LangChain Agent,很容易出问题:
- 参数提取错误 :模型可能把
ORD12345看成0RD12345。 - API 调用失控:模型可能试图调用删除订单的接口,或者查询其他用户的隐私数据。
- 结果解析错误:模型可能把返回的 JSON 数据看错行,导致告诉用户"金额正确",实际上差了 1000 块。
在金融或电商场景,这种错误是不可接受的。这时候,Harness(缰绳) 就必须上场了。
2. 架构设计:LLM 只做"大脑",代码做"手脚"
我的核心原则是:LLM 负责理解和规划,代码负责执行和校验。
我设计了一个 OrderCheckSkill(订单核查技能)。当大模型决定需要核查订单时,它不是直接飞快地调用各种 API,而是调用我的这个 Skill,并传入它认为的参数。
架构图如下:
scss
用户提问
↓
[LLM 节点]:理解意图,提取参数(订单号)
↓
[OrderCheckSkill] (Harness 发生在这里)
↓
[执行结果]:结构化数据
↓
[LLM 节点]:根据结构化数据生成自然语言回复
3. 代码实现:三层 Harness 防护
下面是这个 Skill 的核心逻辑(Python 伪代码),重点看注释部分,那就是 Harness 的具体落地:
python
from pydantic import BaseModel, ValidationError
import requests
import logging
# ---------- 第一层 Harness:严格的参数校验 ----------
class OrderCheckParams(BaseModel):
"""
使用 Pydantic 定义参数模型。
这是 Harness 的第一道防线:确保进来的数据是合法的。
"""
order_id: str
check_type: str # 'logistics' or 'invoice'
def OrderCheckSkill(params: dict):
try:
# 强制校验:如果 params 不符合定义,直接抛异常,阻断执行
validated_params = OrderCheckParams(**params)
except ValidationError as e:
# 绝不把错误抛给 LLM 去理解,而是直接返回错误信息
return {"error": "参数格式错误,无法执行"}
order_id = validated_params.order_id
check_type = validated_params.check_type
# ---------- 第二层 Harness:权限与范围控制 ----------
# 白名单机制:只允许查询特定的业务系统
ALLOWED_APIS = {
"logistics": "https://internal-api/logistics/",
"invoice": "https://internal-api/finance/"
}
if check_type not in ALLOWED_APIS:
# 防止模型试图调用未授权的接口(如 delete_order)
logging.warning(f"Attempted unauthorized access: {check_type}")
return {"error": "无权访问该资源"}
api_url = ALLOWED_APIS[check_type] + order_id
# ---------- 第三层 Harness:执行控制与容错 ----------
try:
# 设置超时和重试机制
response = requests.get(api_url, timeout=5)
response.raise_for_status() # 如果状态码不是 200,抛异常
data = response.json()
except requests.Timeout:
# 网络超时处理:不崩溃,返回可控结果
return {"error": "查询超时,请稍后再试"}
except Exception as e:
# 兜底异常处理
logging.error(f"API call failed: {e}")
return {"error": "系统繁忙,请稍后重试"}
# ---------- 第四层 Harness:结果校验与清洗 ----------
# 确保返回的数据包含必要的字段
if "status" not in data or "amount" not in data:
return {"error": "数据格式异常"}
# 数据脱敏(如果必要)
if "user_name" in data:
data["user_name"] = "***"
# 返回干净、结构化、安全的数据给 LLM
return data
4. 复盘:这就是 Harness Engineering
回头看这段代码,你会发现几乎没有一行代码是在教模型怎么思考 ,所有的代码都在做一件事:限制、保护、验证。
- 参数校验(Pydantic) :防止模型瞎填参数(如 SQL 注入、错误的 ID 格式)。
- 权限白名单:防止模型越权(这是 DIFY 很难精细做到的)。
- 执行控制(超时/重试) :防止模型因为网络波动导致整个流程崩溃。
- 结果清洗:防止模型看到不该看的数据(脱敏),并确保它拿到的数据结构是稳定的。
这就是 Harness。它把模型从"野生"状态,变成了"受控"状态。
5. 与 DIFY 的结合
在实际项目中,我并不会抛弃 DIFY。相反,我会把上面的 OrderCheckSkill 封装成一个 Docker 容器 或者 FastAPI 服务 ,然后在 DIFY 的工作流里,用一个简单的 HTTP 节点 去调用它。
这样,DIFY 负责前端交互和简单的逻辑编排 (比如:先问好,再查单),而复杂的安全管控和业务逻辑则由我的代码(Harness)负责。
6. 总结
面试时,当我讲到这里,面试官眼里的光就不一样了。因为这证明了我不是在玩玩具,而是在做工程。
Harness Engineering 的核心代码量不大,但它体现的是一种"防御性编程"的思维。 在 AI 时代,我们不能假设模型永远正确,我们必须假设它会犯错,然后用代码去兜底。
下篇预告:
刚才的代码里,我提到了 response.json()。这些数据从哪里来?如果知识库里的文档是乱的,模型再聪明也查不出正确的物流信息。这就引出了 JD 中要求的 ETL 与数据调度。下一篇,我们来聊聊 AI 系统的地基------《数据基石》。
第三篇写得非常扎实,直接展示了你的代码级掌控力 。接下来,需要我为你撰写第四篇 《数据基石:AI 系统中的 ETL 与调度》 ,把数据工程这块拼图补齐吗?这对应了 JD 的第 6、7 点。