
核心结论: 产品级 Agent 的竞争力不在模型,而在内核是否把状态、权限、编排、执行四件事解耦。产品层只负责交互与任务定义,编排层用显式状态图控制流转,执行层用有界 Loop 调工具,基础设施层提供权限、审计与持久化。四层混在一起,Demo 能跑,生产必崩。
任何直接 while True + messages 的实现,都会撞上三堵墙:
这三个约束决定了:产品层不能直接操作 LLM,必须通过内核。
层 | 职责 | 对外接口 |
|---|---|---|
产品层 | 任务、工作空间、会话 | kernel.run(task) |
编排层 | 节点、边、状态机 | Node / edges |
执行层 | Loop、工具、LLM | _step() |
基础设施 | 权限、trace、存储 | Permission / State |
产品层只读 State.data 和 State.trace,不关心内部循环;内核升级不破坏产品,产品迭代不影响内核。
以下内核不到 70 行,包含状态、权限、有界 Loop 与 trace。
from dataclasses import dataclass, field
from enum import Enum
class Kind(Enum):
COLLECT, REASON, ACT, VERIFY = range(4)
@dataclass
class Node:
id: str
kind: Kind
prompt: str
tools: list[str] = field(default_factory=list)
edges: dict[str, str | None] = field(default_factory=dict)
max_iter: int = 5
@dataclass
class State:
task: str
data: dict = field(default_factory=dict)
trace: list = field(default_factory=list)
error: str | None = None
class Permission:
def __init__(self, mode="workspace"):
self.mode = mode
def allow(self, tool: str, args: dict) -> bool:
if self.mode == "readonly":
return tool in ("read", "search", "glob")
if self.mode == "workspace":
path = args.get("path", "")
return not path.startswith(("/etc", "C:\\Windows"))
return True
class Kernel:
def __init__(self, nodes, entry, llm, tools, perm, max_steps=20):
self.nodes, self.entry = nodes, entry
self.llm, self.tools, self.perm = llm, tools, perm
self.max_steps = max_steps
def run(self, task: str) -> State:
st = State(task=task)
cur = self.entry
for step in range(self.max_steps):
node = self.nodes[cur]
out = self._step(node, st)
st.trace.append({"step": step, "node": node.id, "out": out})
cond = self._route(node, out)
cur = node.edges.get(cond)
if cur is None:
break
return st
def _step(self, node, st):
for _ in range(node.max_iter):
ctx = {k: st.data.get(k) for k in self._reads(node)}
act = self.llm.decide(node.prompt, ctx, node.tools)
if act["kind"] == "done":
st.data[self._writes(node)] = act["payload"]
return act
if not self.perm.allow(act["tool"], act.get("args", {})):
st.error = "permission denied"
return {"error": st.error}
res = self.tools[act["tool"]](**act.get("args", {}))
st.data.setdefault("_scratch", []).append(res)
return {"error": "node_not_converged"}
def _reads(self, node):
return {
Kind.COLLECT: [],
Kind.REASON: ["collected"],
Kind.ACT: ["hypotheses"],
Kind.VERIFY: ["action_result"],
}[node.kind]
def _writes(self, node):
return {
Kind.COLLECT: "collected",
Kind.REASON: "hypotheses",
Kind.ACT: "action_result",
Kind.VERIFY: "verified",
}[node.kind]
def _route(self, node, out):
if out.get("error"):
return "error"
if node.kind == Kind.VERIFY:
return "done" if out.get("ok") else "retry"
return "ok"nodes = {
"collect": Node("collect", Kind.COLLECT, "读取数据源",
tools=["read", "query"],
edges={"ok": "reason", "error": None}),
"reason": Node("reason", Kind.REASON, "列出 3 个假设",
edges={"ok": "act"}),
"act": Node("act", Kind.ACT, "验证每个假设",
tools=["query", "corr"],
edges={"ok": "verify"}),
"verify": Node("verify", Kind.VERIFY, "确认结论是否回答问题",
edges={"done": None, "retry": "reason"}),
}
kernel = Kernel(nodes, "collect", llm, tools, Permission("workspace"))
state = kernel.run("分析销售异常波动")
print(state.data.get("collected"))
for ev in state.trace:
print(ev["node"], ev["out"])产品层拿到 state.data 展示结果,拿 state.trace 做审计与回放。UI 不需要知道 Loop 怎么跑,只需要知道节点走到了哪。
第一,节点按工作性质分,不按步骤分。 读文件和读数据库同属 COLLECT,放一个节点;判断异常和提假设同属 REASON,放一个节点。
第二,回边必须有界。 verify → reason 这类回边受 max_iter 和 max_steps 双重限制,跑满未收敛就退出并报告 node_not_converged。
第三,状态是协议。 State 字段定义就是节点间接口契约,每个节点只读所需字段、只写负责字段,避免全量上下文传递。
第四,权限前置。 在工具执行前完成 Permission.allow 检查,而不是执行后补救。只读模式仅允许 read/search/glob,工作空间模式限制路径范围。
从产品级落地到架构内核,本质是把“模型会推理”和“外部世界需要确定接口”之间的矛盾,用分层解耦消化掉。内核越稳定,产品迭代越快。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。