“Agent 全栈”最容易引发的误解,是把栈理解为模型、框架、向量库和前端组件的叠加。2026 年企业落地的现实给出了相反的答案:一个原型通常只有模型、系统提示词、若干工具和一个循环,足以演示“查数据后生成报告”,却没有回答生产系统必须回答的问题——进程中断后能否恢复,长任务如何避免撑爆上下文,两个有权限的数据域能否在同一会话中混用,一次失败究竟是模型、工具、skill 还是数据造成的。全栈的专业性,不在于每一层都选了最新的组件,而在于每一层是否清楚自己的输出在下一层被如何裁决。模型决定推理的上限,运行时约束决定这份能力能否稳定、可审计地进入流程。
Harness 是全栈的承重层,它的第一项职能是把“模型意图”和“环境事实”隔开。 模型输出的工具调用只是一项行动意图,它没有自动获得当前用户身份,不等于企业策略允许执行,也不能证明远程系统已经产生预期结果。下面的裁决器把这条边界写成确定性代码:模型提议动作,Harness 按独立规则决定放行、审批还是拦截,模型没有任何通道修改裁决结果。Action 本身有生命周期——REQUESTED → VALIDATED → AUTHORIZED → RUNNING → SUCCEEDED/FAILED——每个状态转换都由 Harness 完成,模型只负责产生 REQUESTED。
from dataclasses import dataclass, field
from enum import Enum
class Verdict(Enum): ALLOW=1; ASK=2; DENY=3
@dataclass
class Action:
name: str; target: str; reversible: bool
risk: float
state: str = "REQUESTED"
POLICY = {"production.deploy": Verdict.ASK, "change.create": Verdict.ALLOW}
DENY_TARGETS = ["prod-db", "iam:", "secrets/"]
def harness_gate(a: Action) -> Verdict:
if any(d in a.target for d in DENY_TARGETS):
return Verdict.DENY # deny 优先,风险分再低也拦
base = POLICY.get(a.name, Verdict.ASK)
if not a.reversible and a.risk > 0.6:
return Verdict.DENY # 不可逆且高风险,策略升级
return base
def transition(a: Action) -> str:
v = harness_gate(a)
if v == Verdict.DENY: return "BLOCKED"
if v == Verdict.ASK: return "WAITING_APPROVAL"
return "AUTHORIZED" if a.state == "VALIDATED" else "VALIDATED"这个裁决器的关键在于 POLICY 和 DENY_TARGETS 是可独立 diff 的配置,而不是埋在 Prompt 里的自然语言建议。企业 Tool 设计应优先暴露业务语义——change.create 的 Schema 要求服务名、补丁引用、验证证据和回滚说明——而不是让模型通过通用 HTTP 或 Shell 自行拼接生产操作。模型负责选择和补齐参数,Harness 负责确定性校验,这条分工一旦在代码里固定,后续的编排、记忆和评估才有稳定的地基。
编排层的核心判断是:状态归 Harness,不归模型。 一个可恢复的 Agent 不能在进程中断后从零重来。编排层必须把任务状态、已执行的 Action、待审批项和 artifact 引用写入 checkpoint,使得恢复时 Harness 知道“上次走到哪一步、哪些动作已有副作用、哪些需要补偿”。Plan-and-Solve 模式的价值不在于规划器比 ReAct 更聪明,而在于错误被限制在单步内:执行器只被允许执行当前步骤,多余的自主性在架构层被剥夺。max_steps 是一道硬约束——2026 年最值得警惕的失败模式中,90% 的生产 Agent 死于无限循环,没有迭代上限、执行超时和沙箱的 Agent 在生产里就是炸弹。
记忆层的工程问题不是“存什么”,而是“什么时候写、什么时候读、什么时候忘”。 工作记忆对应上下文窗口,情景记忆和语义记忆对应外部存储,程序记忆对应模型权重和系统提示词。但简单堆砌上下文不可行:上下文窗口有限,且模型对深埋于长上下文中的信息处理能力会下降。可运行的记忆写入必须是结构化的二次加工,而不是把原始对话原样塞进向量库。
from dataclasses import dataclass
import time
@dataclass
class MemoryEntry:
kind: str # episodic / semantic
content: str
evidence_ref: str
ts: float = field(default_factory=time.time)
class MemoryStore:
def __init__(self): self.entries = []
def write_if_durable(self, raw: str, decision: str, evidence: str):
# 只保留后续可能复用的决策与事实,不存全部原文
if decision in ("CANCELLED", "APPROVED", "ROLLED_BACK"):
self.entries.append(MemoryEntry("episodic", raw[:300], evidence))
def recall(self, kind: str, limit: int = 3):
return [e for e in self.entries if e.kind == kind][-limit:]write_if_durable 的判断是一个刻意保守的过滤器:只有产生持久后果的决策才值得进入长期记忆,而不是每一轮对话都触发一次 LLM 摘要调用。长期记忆应保留证据链——每个结论引用哪个 artifact、哪个 Action、哪个时间窗口——否则召回的内容无法被评估系统验证。
评估与门禁是全栈的最后一层,也是“全栈”一词真正兑现的地方。 只检查最终答案会产生假通过:一个客服 Agent 说“退款已经完成”,不代表退款真的发生了;一个代码 Agent 贴出正确补丁,不代表它没有把密钥读进上下文。轨迹级评估要同时看结果层(外部系统状态是否满足目标)、轨迹层(工具选择、参数、顺序)、策略层(权限、确认、隐私)和资源层(调用次数、Token、费用、延迟)。pass@1 衡量能力上限,pass^k 衡量可靠性——k 次全部成功才算数,单次成功不被接受为生产信号。发布门禁必须把这些维度串起来:准确率可以高,但幻觉率越界、P0 安全用例未零失败或单任务成本超出预算,都必须拦截。
def release_gate(trials: list[dict], budget: float) -> str:
k = len(trials)
all_pass = all(t["task_ok"] and t["policy_ok"] for t in trials)
cost = sum(t["cost"] for t in trials) / k
if not all_pass:
return f"BLOCK: pass^{k} 未达标({sum(t['task_ok'] for t in trials)}/{k})"
if cost > budget:
return f"BLOCK: 单任务均成本 {cost:.3f} 超出预算 {budget}"
return f"PASS: pass^{k}={k}/{k}, 均成本={cost:.3f}"
print(release_gate([
{"task_ok": True, "policy_ok": True, "cost": 0.42},
{"task_ok": True, "policy_ok": True, "cost": 0.51},
], budget=0.50))
# BLOCK: 单任务均成本 0.465 超出预算 0.50Agent 全栈的专业性,最终不体现在每一层用了什么组件,而体现在约束是否被分配到正确的层级:模型负责推理与参数生成,Harness 负责裁决与生命周期,编排层负责状态与恢复,记忆层负责持久化证据,评估门禁负责在成本与可靠性之间做不妥协的判断。把这些边界写进配置文件、脚本和 CI 门禁,而不是依赖“模型应该会小心”的假设,才是 Agent 从“能跑”跨入“敢用”的分界线。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。