第二部 原理篇:规则驱动的智能体设计

第一部回答的是“如何用通用工具把 AI 跑起来”(实操),第二部回答的是“如何让 AI 在企业里不出错、不乱来、可审计”(原理)。

第一部提到的随机性问题——同样的问题两次回答不一样、该拒绝的不拒绝、该留痕的不留痕——正是通用工具在企业场景下“不敢用”的根源。第二部用规则的确定性约束模型生成的随机性:意图识别、规则引擎、审批链、权限审计、可训练配置,构成“生成 × 裁决”的工程闭环。

第二部面向需要为业务系统构建内嵌智能体能力的架构师与开发者;普通用户可先读第 1~5 章上手,再回到本部分理解“为什么这样做才可靠”。

第 6 章:绪论:规则驱动的智能体

6.1 从大语言模型到业务智能体

大语言模型(LLM)的本质能力是语言建模:给定前文,预测续文。当这一能力被嵌入业务系统时,一个朴素的做法是直接向模型提问并展示回答。然而业务系统对输出的要求远不止"通顺":

「智能体」这一概念正是在此背景下被引入:将 LLM 封装为一个具备感知、决策、行动能力的组件,使其能在约束条件下完成业务任务[42][35]。而约束从何而来,正是 Agent 框架设计的核心分水岭——也是本书全部章节的总纲。

6.2 工程问题的本质:业务正确性的来源

当一个 LLM 应用从对话原型演进为生产系统,工程问题会集中爆发。但这些问题并非彼此独立,它们有一个共同的根源:系统的可信输出依赖一条不稳定的链路——模型输出 → 直接呈现 → 直接落库。链路中的每一环都可能被模型幻觉、越权操作、注入攻击或过期数据破坏。

表 6-1 业务正确性面临的四类挑战

挑战 表现 后果
业务正确性 模型忽略指令、幻觉业务数据 成本线、信用额度等防线失效
合规审计 操作无痕迹、无法追溯 无法通过内审与质量体系审核
权限失控 越权操作、职责冲突 数据泄露、舞弊风险
安全攻击 提示注入、敏感信息泄漏 系统被操控
配置僵化 业务规则硬编码在代码中 每次调整都要发版
可观测性 跨层日志无法关联 故障无法定位

应对这些挑战的常见路径有三条:其一,依赖通用编排框架,以图结构组织智能体,规则的约束力取决于提示词措辞;其二,引入独立的安全与合规组件栈,自行拼装;其三,构建内嵌的、以规则为第一约束的运行时——本书所研究的路线。

核心原理: 业务正确性不应是"模型碰巧答对",而应是"系统结构上不可能答错"。后者的工程含义是:将关键裁决从概率性的模型推理中剥离出来,交给确定性的规则与审核机制。模型负责"听懂与表达",规则负责"裁决与约束"。

6.3 设计定位与四条约束

绳墨 Runtime 的核心设计定位可以概括为一句话:业务正确性靠规则引擎 + 审核链,而非 LLM 编排。这一原则展开为四条设计约束:

  1. 硬规则不可绕过:规则引擎中 bypass=false 的规则,返回 block 时不可被任何角色覆盖,包括管理员;LLM 输出同样不可覆盖。
  2. 审核链不可跳过:每笔关键操作须经过七层审核方可落库,链式哈希保证记录不可篡改。
  3. 配置可训练但须审批:规则参数、槽位定义、子意图关键词、审批链均可通过训练修改,但修改须经审批链;审批链本身亦可训练(治理之治理)。
  4. 单智能体为主:每轮路由仅一个智能体处理,降低编排复杂度与调试成本。

这四条约束回答了四个本质问题:谁能覆盖裁决(没人)、操作能否无痕(不能)、配置能否失控改动(不能)、编排能否无限复杂(不必)。

6.4 框架全景与模块地图

图 6-1 绳墨 Runtime 框架全景与四层模块地图

flowchart TD
    BIZ["业务层(框架边界之外)<br/>领域 Agent · 业务规则 · HTTP API · MCP · 数据库迁移 · 部署配置"]
    subgraph RT["runtime/ 框架运行时(本书叙述范围)"]
        direction TB
        subgraph SEM["语义层"]
            direction LR
            S1["intent_recognition 意图识别"]
            S2["sub_intent_engine 子意图"]
            S3["slot_engine 槽位提取"]
            S4["query_param_parser 附加词"]
        end
        subgraph ORC["编排层"]
            direction LR
            O1["coordinator 路由调度"]
            O2["base_agent 生命周期"]
            O3["multi_hop 多跳拆解"]
            O4["workflow_enforcer 流程约束"]
            O5["chain_gate 链式 Gate"]
            O6["approval_chain 审批链"]
        end
        subgraph GOV["治理层"]
            direction LR
            G1["rule_registry / rule_engine 规则引擎"]
            G2["audit_engine 审核链"]
            G3["permission RBAC+ABAC"]
            G4["sod 职责分离"]
            G5["agent_security 安全防护"]
            G6["module_toggles 模块开关"]
        end
        subgraph INF["基础设施层"]
            direction LR
            I1["trace 链路追踪"]
            I2["session_manager 会话"]
            I3["logger 日志"]
            I4["cache 缓存"]
            I5["event_bus 事件总线"]
            I6["file_storage 存储"]
            I7["streaming SSE"]
            I8["scheduler 调度"]
            I9["auth 认证"]
        end
    end
    BIZ --> RT

框架在结构上划分为四个层次:语义层负责"听懂",编排层负责"怎么走",治理层负责"能不能做",基础设施层提供"运行的底座"。分层本身就是原理——每一层只承诺一类职责,任何一条执行路径都必须穿过治理层,这正是"业务正确性在结构上不可能被绕过"的实现方式。

6.5 技术选型:零第三方依赖的工程意义

框架核心仅依赖 Python 标准库。这一约束是设计决策而非偶然:

  1. 无框架升级风险:不依赖外部编排生态,版本演进完全自主可控;
  2. 内网可部署:企业内网常禁止外网拉取依赖包,标准库零安装即可运行;
  3. 降级语义清晰:redis、boto3 等可选依赖未安装时自动降级为内存或本地实现,框架仍可独立运行。

原理: 依赖即风险。每引入一个第三方能力,就引入一个不可控的失效点、升级点与供应链审计面。零依赖不是炫技,而是把"运行时可失效的东西"压到最少。

6.5.1 与通用编排框架的定位差异

技术选型需要明确边界。通用编排框架(如以图结构组织智能体的 LangGraph 系、以多智能体对话为范式的 AutoGen 系)与绳墨 Runtime 的差异是定位性的,而非能力高下,见表 6-2:

表 6-2 与通用编排框架的对比

维度 绳墨 Runtime 通用编排框架
依赖 零第三方依赖,仅 Python 标准库 依赖生态包,版本耦合较重
定位 业务规则驱动的企业内嵌运行时 通用 LLM 应用编排框架
规则约束 硬规则(bypass=false)优先于 LLM 输出 规则以工具 / 提示词方式软约束
审计 内置七层审核链 + 链式哈希防篡改 无内建审计链,需自行实现
权限 内置 RBAC + ABAC + SOD 矩阵 无内建权限模型
安全 内置注入检测 / 输出脱敏 / 频率限制 依赖第三方安全组件
可观测 审核链结果时间线 + 哈希完整性验证 依赖外部 tracing

两类框架各有所适:通用编排框架适合快速搭建原型与探索性应用;以规则为第一约束的运行时适合企业内部、合规要求高、业务规则明确的场景。

6.5.2 规则优先的论证框架与适用边界

"业务正确性靠规则"需要一个可检验的论证框架,而非信念。论证基于两类错误的成本不对称,见表 6-3:

表 6-3 两类错误的成本不对称

错误类型 后果 成本特征
假放行(规则该挡而没挡) 违规操作落库:超成本线售单、越权审批 高、可放大、难撤销
假阻断(规则误挡) 合法请求被拒,用户重试或走人工 低、可重试、可解释

当"假放行的成本"显著高于"假阻断的成本"时,规则优先(且失败默认阻断)是理性选择——这正是制造业订单、质检、库存流转场景的特征。反之,在创意生成、探索性任务中,假阻断的代价(扼杀创新表达)可能高于假放行,此时规则优先不适用。

适用边界(反向界定): 本框架的设计目标场景满足三个条件——① 业务事实可结构化(订单、库存、状态有确定 schema);② 决策可被规则描述(存在明确的成本线、审批链、状态机);③ 错误不可逆性高(落库即事实)。不满足条件的场景(开放创意、语义无界的自由问答、低风险高探索性任务)应选用模型主导的编排,而非本框架。

6.5.3 质量度量框架(非功能性视图)

专家级工程评审以可度量的视图收尾。本书各章的设计决策可统一映射到四组度量,见表 6-4:

表 6-4 框架的度量域与指标

度量域 示例指标 对应章节
正确性 规则命中率、误路由率、审批守卫拦截数 4 / 6 / 8
治理 审批通过率、训练生效时长、热更新次数 8 / 9
安全 注入拦截数、审计完整性验证通过率 7
性能 端到端 P95 延迟、LLM 调用次数/请求、快路径命中率 12

6.6 实现剖析:模块地图与依赖边界的代码落点

6.4 节给出四层架构与模块地图,6.5 节论证"零第三方依赖"的工程意义。本节从 runtime/__init__.py(74 行)与 runtime/database.py(30 行)摘取三段(节选),看这两条设计如何被代码"钉死"。

6.6.1 模块地图:__init__.py 顶部的模块一览

"""
绳墨 Runtime(Shéng Mò)· 规则驱动的业务场景 Agent 运行时
自研轻量级 Agent 运行时框架,无第三方 Agent 框架依赖(LangChain/LangGraph/AutoGen 等),
面向制造业业务规则驱动场景。

模块一览:
    - base_agent         : Agent 基类 + AgentResponse 统一响应契约
    - auth               : 认证能力(TokenSigner/Authenticator/MockUserSource)
    - coordinator        : 协调器(意图识别 -> 路由 -> 上下文隔离 -> 分发 -> 聚合)
    - rule_registry      : 规则引擎(RuleResult/BaseRule/RuleRegistry,bypass=false 硬规则)
    - rule_engine        : 数据驱动规则引擎(RuleEngine + 安全沙箱 DSL)
    - audit_engine       : 七层审核链
    - permission         : 权限系统(RBAC + ABAC)
    - sod                : SOD 职责分离矩阵
    - agent_security     : Agent 安全防护(注入检测/输出过滤/脱敏/频率限制)
    - module_toggles     : 模块开关 + @require_module 装饰器
    - chain_gate         : 链式 Gate 校验器
    - workflow_enforcer  : 流程约束引擎(三道校验 + 实例化)
    - intent_recognition : 意图识别
    - trace              : 统一追踪上下文(contextvars)
    - session_manager    : 会话管理 + token 窗口裁剪
    - logger             : 结构化日志(JSON Lines + 脱敏)
    - cache              : 缓存管理器(Redis/内存降级)
    - event_bus          : 事件总线(Redis Streams/内存降级)
    - file_storage       : 文件存储(S3/本地降级)
    - streaming          : SSE 流式输出
"""

这段代码印证 6.4 节:模块地图不是插图,而是模块文件顶部逐条维护的清单,其顺序即四层架构的顺序——契约层(base_agent)、编排层(coordinator)、裁决层(rule_registry / rule_engine)、安全层(audit_engine / permission / sod / agent_security / module_toggles)、流程层(chain_gate / workflow_enforcer)、基础设施层(trace / session_manager / logger / cache / event_bus / file_storage / streaming)。

最后一组模块的注释统一带"降级"字样,正是 6.5 节"零依赖"在模块粒度上的体现:每个横切能力都自带降级实现,缺外部依赖不阻断框架启动。

6.6.2 统一导入面:__all__ 划出对外承诺

# prog/runtime/__init__.py —— 框架唯一公开导入面(节选,按层分组)
from prog.runtime.base_agent import BaseAgent, AgentResponse           # 契约层
from prog.runtime.coordinator import CoordinatorAgent                  # 编排层
from prog.runtime.rule_registry import BaseRule, RuleRegistry, RuleResult
from prog.runtime.rule_engine import RuleEngine                        # 裁决层
from prog.runtime.audit_engine import AuditEngine
from prog.runtime.permission import PermissionSystem                   # 安全层
from prog.runtime.sod import SODChecker
from prog.runtime.agent_security import AgentSecurity, SecurityResult
from prog.runtime.module_toggles import ModuleToggleManager, require_module
from prog.runtime.chain_gate import ChainGateChecker, GateResult        # 流程层
from prog.runtime.workflow_enforcer import WorkflowEnforcer
from prog.runtime.intent_recognition import IntentRecognizer
from prog.runtime.logger import Logger                                 # 基础设施层
from prog.runtime.cache import CacheManager, get_cache
from prog.runtime.event_bus import (EventBus, RedisStreamBus,
                                    create_event_bus, get_event_bus)
from prog.runtime.file_storage import (FileStorageBase, S3Storage,
                                       get_file_storage)
from prog.runtime.streaming import (StreamingResponse, SSEHelper,
                                    create_streaming_response)
# ...

这段代码印证 6.4 节:"层"的边界落在导入关系上。业务方只能经 prog.runtime 这一个入口取用能力,层内模块之间不越过公开面互相引用;__all__ 显式列出全部导出符号,把"对外承诺的稳定面"与"内部实现"分开——附录 A 的模块 API 速查表正是对这份清单的展开。

这种"单一导入面"是接口兼容性回归(18.5 节)能够制度化的前提:要判定"版本演进是否破坏下游",先要有一份可枚举的对外承诺清单。

6.6.3 依赖边界:注入点 + 鸭子类型 + 未注入即降级

# prog/runtime/database.py —— 数据库注入点(全文仅 14 行代码)
_database: Any = None

def set_database(db: Any) -> None:
    """注册数据库对象(鸭子类型:提供 query_one/query_many/insert/update)"""
    global _database
    _database = db

def get_database() -> Any:
    """获取注册的数据库对象,未注册时返回 None(框架组件自动降级)"""
    return _database

这段代码印证 6.5 节:框架与外部系统的边界被压缩成一个全局注入点。框架不 import 任何具体数据库驱动,只约定四个方法名(鸭子类型);未注册时 get_database() 返回 None,各组件据此走降级路径(审核链用内存链存储、规则引擎返回默认配置)。

这带来一个关键的工程性质——降级不是异常分支,而是默认路径:CI 中不接数据库即可全量测试,正确性验证与外部能力彻底解耦。

把"层"的边界摊开成依赖方向,可看清公开面在结构上的确切位置,如图 6-2 所示:

图 6-2 依赖方向:业务侧只经公开面,层内模块直接互引

flowchart TD
    BIZ["业务侧<br/>领域 Agent / 业务规则 / HTTP API"]
    PUB["prog.runtime 公开导入面<br/>(__all__ 显式导出)"]
    subgraph RT["runtime/ 内部:层内模块直接互相引用"]
        direction TB
        SEM["语义层"]
        ORC["编排层"]
        GOV["治理层"]
        INF["基础设施层"]
        ORC --> SEM
        ORC --> GOV
        ORC --> INF
        GOV --> INF
        SEM --> INF
    end
    BIZ -->|唯一合法入口| PUB
    PUB --> SEM
    PUB --> ORC
    PUB --> GOV
    PUB --> INF

原理: 依赖方向"内外有别"——对外,业务侧只经 prog.runtime 公开面取用能力,绝不 import 层内模块;对内,层内模块之间直接互相引用(如 coordinator 直接 import intent_recognition / permission / event_bus),不再绕回公开面。

这一"外严内松"的边界使公开面成为唯一需要维护的兼容契约:层内重构不会波及业务侧,与 6.4 节"层边界落在导入关系上"互为表里。

6.7 本章小结