1. 从一次线上事故说起:为什么纯 RAG 的 Agent 会翻车
去年我帮一个做工业设备运维的团队调过一个 Agent,场景很典型:设备传感器每秒上报温度、振动、电流,Agent 要根据这些实时数据判断"要不要停机检修"。他们最初的方案是纯 RAG——把设备手册、历史工单、专家经验全塞进向量库,每次决策前检索 top-k 片段喂给模型。
上线第一周就出事了。某台压缩机振动值连续 3 分钟超过阈值,但 Agent 给出的结论是"继续观察",理由是检索到的历史工单里有一条"振动 4.2mm/s 属正常波动"。问题是那条工单对应的是另一型号设备,而且当时的环境温度比现在低 15 度。模型把"领域知识"当成了放之四海皆准的真理,完全忽略了实时数据里的上下文。
这就是 Harness Engineering 要解决的核心问题:Agent 的决策不能只靠静态知识检索,也不能只靠实时数据流,而要让两者在推理链路里真正"对话"。所谓 Harness Engineering,我理解就是给 Agent 套上一副"驾驭装置"——用结构化的领域知识约束推理方向,用实时数据动态修正置信度,最后输出一个可解释、可追溯的决策。
这篇文章我会交付一套可复制的 Agent 配置骨架,包含工具调用定义、推理参数、知识注入方式,以及一个能跑通的验证脚本。你可以在自己的环境里复现,然后拿真实数据评估推理效果。适合已经写过基础 Agent、但发现"检索增强"在动态场景下不够用的人。
2. 前置准备:用 TaoToken 统一模型接入层
在动手写决策逻辑之前,得先把模型调用这层理顺。我试过在项目里同时接三四家模型 API,光是 key 管理和计费对账就够烦的。TaoToken 的价值在于它把主流模型的调用协议统一了,你换模型只需要改一个 model 字段,不用重写 SDK 初始化代码。
具体来说,TaoToken 提供 OpenAI 兼容的接口,base_url 指向https://taotoken.net/api,鉴权用 Bearer Token。这意味着你现有的 openai Python SDK 几乎不用改,只换 base_url 和 api_key 就行。对于 Agent 场景特别有用的一点是:你可以在同一个决策链路里,让"知识检索后的推理"用强模型,"实时数据格式化"用便宜的快模型,成本可控。
你需要准备的东西:
- 一个 TaoToken 账号,在控制台创建一个 API Key
- Python 3.9+ 环境,安装
openai、numpy、pydantic - 一份你自己的领域知识(哪怕先用 JSON 手写几条规则也行)
- 一个实时数据源(没有的话用模拟数据生成器代替)
API Key 的创建入口在控制台的 API Keys 页面,建议单独建一个 key 给这个项目用,方便后面排查调用量。如果你还没决定用哪个模型,可以先去模型对话页面测一下不同模型在你领域问题上的表现,再决定推理链路里各环节的模型分配。
3. 可复制的 Agent 配置骨架
下面这套骨架我拆成了三块:知识层、实时数据层、推理编排层。核心思路是不让模型直接看原始数据,而是先经过一层"特征提取 + 知识匹配",把结构化的上下文喂给模型做最终决策。
3.1 知识层:把领域知识写成可匹配的结构
别一上来就搞向量库。对于决策类 Agent,很多领域知识其实是"条件-结论"型的,用结构化规则表达比 embedding 更可靠。我一般用 Pydantic 定义知识条目:
from pydantic import BaseModel from typing import List, Optional class DomainRule(BaseModel): rule_id: str condition: str # 自然语言描述的条件 conclusion: str # 结论或建议动作 applicable_models: List[str] # 适用的设备型号 confidence: float # 专家给的初始置信度 0-1 source: str # 知识来源,便于追溯 # 示例知识库 knowledge_base = [ DomainRule( rule_id="VIB-001", condition="振动值持续超过 4.5mm/s 且环境温度高于 30 度", conclusion="建议降载运行并安排 24 小时内检修", applicable_models=["C-200", "C-250"], confidence=0.85, source="2023年华东区运维手册第4章" ), DomainRule( rule_id="VIB-002", condition="振动值在 3.0-4.5mm/s 之间且持续小于 5 分钟", conclusion="继续观察,记录趋势", applicable_models=["C-200", "C-250", "C-300"], confidence=0.7, source="设备厂商技术通报" ), ]这样做的好处是:每条知识都带applicable_models和confidence,推理时可以先按设备型号过滤,再用实时数据修正置信度。比直接检索文本片段可控得多。
3.2 实时数据层:滑动窗口 + 趋势特征
实时数据不能只看瞬时值,要看趋势。我封装了一个滑动窗口特征提取器:
import numpy as np from collections import deque class RealtimeFeatureExtractor: def __init__(self, window_size=60): self.window_size = window_size self.buffers = {} # {metric_name: deque} def update(self, metric: str, value: float): if metric not in self.buffers: self.buffers[metric] = deque(maxlen=self.window_size) self.buffers[metric].append(value) def get_features(self, metric: str) -> dict: if metric not in self.buffers or len(self.buffers[metric]) < 5: return {"status": "insufficient_data"} arr = np.array(self.buffers[metric]) return { "current": float(arr[-1]), "mean": float(arr.mean()), "std": float(arr.std()), "trend": float(np.polyfit(range(len(arr)), arr, 1)[0]), # 斜率 "max": float(arr.max()), "duration_above_threshold": int((arr > arr.mean() + 2 * arr.std()).sum()) }trend这个斜率特征特别关键。很多事故不是瞬时超标,而是缓慢爬升。纯看当前值的 Agent 会漏掉这种渐进式异常。
3.3 推理编排层:知识匹配 → 数据修正 → 模型决策
这是整个骨架的核心。流程分三步:
第一步,根据设备型号和实时特征,从知识库里筛出候选规则。第二步,用实时数据计算每条规则的"动态置信度"——如果实时特征和规则条件高度吻合,置信度上调;如果条件里的关键指标不满足,置信度下调。第三步,把候选规则、动态置信度、实时特征一起喂给模型,让它输出最终决策和理由。
import json from openai import OpenAI client = OpenAI( base_url="https://taotoken.net/api", api_key="你的_TAOTOKEN_API_KEY" ) def build_reasoning_prompt(device_model, features, candidate_rules): rules_text = "\n".join([ f"- [{r.rule_id}] 条件:{r.condition} | 结论:{r.conclusion} | 初始置信度:{r.confidence}" for r in candidate_rules ]) return f"""你是一个工业设备决策 Agent。当前设备型号:{device_model} 实时特征数据: {json.dumps(features, ensure_ascii=False, indent=2)} 候选领域规则: {rules_text} 请完成以下推理: 1. 逐条评估每条规则的条件是否被实时数据支持,给出动态置信度(0-1) 2. 如果多条规则冲突,说明你的取舍理由 3. 输出最终决策:动作(停机/降载/继续观察)+ 置信度 + 一句话理由 以 JSON 格式输出,字段:decision, confidence, reasoning, matched_rules """ def decide(device_model, features, candidate_rules): prompt = build_reasoning_prompt(device_model, features, candidate_rules) resp = client.chat.completions.create( model="gpt-4o", # 可在 TaoToken 控制台换成其他模型 messages=[{"role": "user", "content": prompt}], temperature=0.2, # 决策场景要低温度,减少随机性 response_format={"type": "json_object"} ) return json.loads(resp.choices[0].message.content)注意temperature=0.2这个参数。决策类 Agent 不需要创造性,需要的是稳定复现。我见过有人用默认温度 1.0 跑决策,同样的输入两次结果不一样,根本没法做回归测试。
4. 验证请求:跑通一次完整决策
把上面三块拼起来,写一个验证脚本。我用模拟数据构造一个"振动缓慢爬升 + 温度偏高"的场景:
import time # 初始化 extractor = RealtimeFeatureExtractor(window_size=60) device_model = "C-200" # 模拟 60 秒的实时数据:振动从 3.0 缓慢爬到 4.8,温度稳定在 32 for i in range(60): vibration = 3.0 + (1.8 * i / 59) + np.random.normal(0, 0.05) temperature = 32.0 + np.random.normal(0, 0.3) extractor.update("vibration", vibration) extractor.update("temperature", temperature) # 提取特征 vib_features = extractor.get_features("vibration") temp_features = extractor.get_features("temperature") features = {"vibration": vib_features, "temperature": temp_features} # 按型号过滤候选规则 candidates = [r for r in knowledge_base if device_model in r.applicable_models] # 执行决策 result = decide(device_model, features, candidates) print(json.dumps(result, ensure_ascii=False, indent=2))跑通后你会看到类似这样的输出:
{ "decision": "降载运行并安排检修", "confidence": 0.82, "reasoning": "振动值当前 4.78mm/s 且趋势斜率为正,环境温度 32 度超过 30 度阈值,VIB-001 规则条件被实时数据支持,动态置信度上调至 0.82。VIB-002 因持续时间已超过 5 分钟不适用。", "matched_rules": ["VIB-001"] }关键验证点有三个:一是matched_rules是否正确命中了 VIB-001 而不是 VIB-002;二是confidence是否因为实时数据吻合而上调;三是reasoning里是否引用了具体的实时特征值。如果 reasoning 里只有规则原文没有数据引用,说明模型没真正做融合推理,你得检查 prompt 里的数据格式是不是太啰嗦了。
5. 本篇常见错排查
报错一:openai.AuthenticationError: Incorrect API key provided
先确认 base_url 是不是写成了https://taotoken.net/api,末尾不要加/v1。TaoToken 的兼容层已经处理了路径。然后检查 key 有没有多余空格,从控制台复制时容易带上换行符。
报错二:模型返回的 JSON 解析失败
response_format={"type": "json_object"}不是所有模型都支持。如果你在 TaoToken 里换了一个不支持 JSON mode 的模型,就得在 prompt 里加"只输出 JSON,不要 markdown 代码块",然后用正则提取。我一般会写一个safe_parse_json兜底函数,先尝试直接解析,失败就用re.search(r'\{.*\}', text, re.DOTALL)提取。
报错三:决策结果不稳定,同样输入两次不一样
检查 temperature 是不是设高了。另外确认你的特征提取器有没有在两次调用之间被意外重置。滑动窗口的 deque 是可变对象,如果你在多线程环境里共享一个 extractor 实例,会出现数据竞争。每个设备实例应该持有自己的 extractor。
报错四:知识规则匹配不到
大概率是applicable_models过滤太严。实际设备型号可能有后缀,比如 "C-200A" 和 "C-200" 被当成两个型号。建议在过滤前做一次型号归一化,或者用前缀匹配。
报错五:实时数据特征全是insufficient_data
窗口大小设太大了。如果你 60 秒才采一次数据,window_size=60 意味着要等一小时才能出特征。根据你的采样频率调整,一般保证窗口内至少有 10 个点。
6. 下一步:把决策链路接进你的生产环境
这套骨架跑通之后,你可以做几件事让它更贴近生产。一是把知识库从硬编码 JSON 换成数据库或配置文件,支持热更新;二是给决策结果加一个反馈回路,人工确认或修正后的结果写回知识库,调整对应规则的 confidence;三是把推理链路里的模型调用换成 TaoToken 的 Coding Plan,如果你要长期跑 Agent 做批量决策,按量计费比按 token 计费更可控。
接入文档在 TaoToken 的文档页面有完整的参数说明,包括流式输出、函数调用、多模型路由的配置方式。如果你想把决策 Agent 做成一个常驻服务,建议先用模型对话页面把 prompt 调稳,再固化到代码里。