不要依靠在单一聊天框中填写 Prompt 来解决复杂业务场景,AI 应用应该迈入严肃的软件工程化。
每次看到 AI 犯错、胡言乱语时,很多人的第一反应是大模型产生幻觉了,它还不成熟。
但实际上,现在头部大模型的逻辑推理能力已经很强了。AI 犯错,往往不是因为笨,而是因为你没有给它们加载正确的上下文,也没有给它们套上合适的缰绳。
模型只是引擎,Harness 才是那辆车
这些失败的 Agent 案例都有一个共同点:让 LLM 处于"裸奔"状态。
核心架构范式:Thin Harness, Fat Skills
为了不让 LLM 裸奔,这套现代 Agent 架构应运而生。"Thin Harness, Fat Skills"架构旨在将"概率性的意图推理"与"确定性的任务执行"进行物理隔离与完美解耦。该架构一般由四个核心组件构成:
1. Thin Harness(极简的调度器)
Harness(驾驭层)不应承载复杂的业务逻辑,必须保持极其轻量。它的核心职责包括:
- 状态机驱动:运行标准的智能体循环(比如如 ReAct 或 Plan-and-Execute 模式)。
- 上下文管理(Context Management):维护对话历史,控制 Token 窗口,适时进行上下文截断或总结。
- 边界与安全控制:拦截模型越界请求,管理异常与重试机制。
它的代码量可能撑死也就 200 行,是个无状态的轻量级调度器。它的职责很简单、很纯粹:跑一跑 ReAct 循环、管理一下上下文 Token、执行文件读写,以及守住安全边界(比如遇到错误时重试)。不要把业务逻辑塞给它。如果写的代码量过多反而会束缚大模型的灵活程度。
2. Fat Skills(沉淀业务逻辑的 Markdown)
不要用自然语言在单次会话中与模型"讲道理"或堆砌所有SOP。业务流程应当被提炼、固化为结构化的 Markdown 文件。
- 类代码结构 :这些文件就像传统编程中的类和方法,必须包含明确的:输入规范、前置条件、逐步执行逻辑、约束条件以及预期的输出格式。
- 高信噪比:高度结构化的 Markdown 能让 LLM 的注意力机制精准聚焦,显著降低执行漂移的概率。
你应该把复杂的业务流程提炼成结构化的 Markdown 文件。这些文件就像传统代码里的函数一样,里面写清楚:输入什么、前置条件是什么、第一步第二步做什么、输出格式是什么。把这些厚重的业务逻辑沉淀到文件里,大模型读起来才会极其专注,不容易跑偏。
3. Resolver(路由表)
简单说就是按需加载
如果将所有 Skill 文件全部塞入单一的 System Prompt 中,Agent 会因上下文过载而变得反应迟钝,还可能出现指令混淆。 Resolver 相当于充当了 Agent 的网关和决策中枢。面对用户请求,它通常使用极薄的提示词配合低延迟、低成本模型,也可以使用基于向量检索的语义路由,来决策当前任务应当动态挂载哪一个特定的 Skill 文件。
4. Deterministic Code(确定性执行工具层)
这是根除幻觉的杀手锏。架构必须恪守一条铁律:将判断力交由大模型,将执行力交由传统代码。 不要让大模型去直接执行数学计算、生成复杂的几何坐标或盲猜网页内容。大模型的职责仅限于生成符合 Schema 验证的参数,随后调用预先写好的确定性 Python/Go 等函数进行精确计算。
代码示例
python
import pydantic
from typing import Dict, Callable
# Deterministic Code (确定性代码工具)
# 绝对不让模型凭空捏造数据,必须调用确定性的 API
def fetch_stock_price(ticker: str) -> str:
"""获取真实股价的确定性工具"""
return db.execute("SELECT price FROM stocks WHERE ticker = ?", ticker)
def calculate_precise_layout(dimensions: dict) -> dict:
"""用传统的几何算法计算布局,而不是让模型瞎画"""
return layout_engine.compute(dimensions)
deterministic_tools = {
"fetch_stock_price": fetch_stock_price,
"calculate_precise_layout": calculate_precise_layout
}
# Thin Harness + Resolver
class AgentBrain:
def __init__(self, tools: Dict[str, Callable]):
self.tools = tools
self.context_window = []
def resolver(self, user_task: str) -> str:
"""
Resolver (路由表)
使用极薄的提示词或廉价极速模型(如 Haiku),将意图映射到具体的 Skill 文件。
避免把所有知识都塞进一次会话中。
"""
routing_rules = """
分析用户请求,返回对应的技能文件路径:
- 如果关于数据查询 -> 返回 'skills/data_analyst.md'
- 如果关于前端UI -> 返回 'skills/frontend_layout.md'
- 如果是代码审查 -> 返回 'skills/code_review.md'
"""
# 路由判定,输出对应的 markdown 文件路径
skill_file_path = fast_llm_route(routing_rules, user_task)
return skill_file_path
def run_harness(self, user_task: str):
"""
Thin Harness 调度循环 + 加载 Fat Skills
"""
# 动态加载厚技能 (Fat Skill)
target_skill_file = self.resolver(user_task)
skill_prompt = open(target_skill_file).read()
# 将高度结构化的 markdown SOP 放入系统提示词
self.context_window.append({"role": "system", "content": skill_prompt})
self.context_window.append({"role": "user", "content": user_task})
# Thin Harness:轻量级的 ReAct 循环
while True:
response = strong_llm_generate(self.context_window, available_tools=self.tools)
# 潜空间决策 (Latent) -> 确定性执行 (Deterministic)
if response.is_tool_call:
tool_name = response.tool.name
tool_args = response.tool.args
# 执行确定性代码,将幻觉转化为可捕捉的工程 Exception
try:
result = self.tools[tool_name](**tool_args)
self.context_window.append({"role": "tool_result", "content": str(result)})
except Exception as e:
# Debuggable Bug!
self.context_window.append({"role": "tool_error", "content": f"Bug: {e}"})
else:
return response.text # 任务完成
技能(Skill)即"永久升级"
"绝不允许做一次性的工作。"
如果在的开发过程中,有一件事情你需要手动做 3-10 次(比如分析某个特定的 Log、部署某个服务、清理某种数据),把这个流程固化成一个 Markdown 的 Skill 文件。
测试标准很简单:"如果同一件事情,我需要向你开口吩咐第二次,你就失败了。"
这样做的好处:
- 永不衰退: 写在 Markdown 里的业务流不会因为模型抽风而遗忘。
- 完美解耦: 的业务逻辑(Fat Skills)和 确定性执行(Tools)是稳定的。
- 免费升维: 当未来 GPT-6 或 Claude 5.0 发布时,只需要替换掉
strong_llm_generate里的 API Key,所有沉淀下来的 Skill 将在一夜之间自动变得更聪明。
总结
把大模型当做纯粹的推理引擎 ,用薄薄的 Harness 控制它,用 Resolver 精准路由上下文,将所有的计算丢给 确定性的代码 ,并将你的领域经验全部固化进 Markdown 技能文件 中。构建一次,它将永远为你运行。