- 人工智能
- 大模型
- AI 安全治理
- 模型安全
- 内容安全
- 提示词注入防护
- RAG
【免费下载链接】Guardrails
NeMo Guardrails is an open-source toolkit for easily adding programmable guardrails to LLM-based conversational systems.
本指南聚焦 NeMo Guardrails 中 Colang 2.x 运行时(runtime/README.md)的核心设计文档,系统拆解Flow(流程)这一核心抽象、标准事件体系、上下文变量,以及驱动整个对话系统的推理循环(Reasoning Loop)工作机制。你将掌握 flow 如何被启动、匹配、暂停、中止与完成,理解阻塞/非阻塞语句、内部事件队列、匹配评分与动作冲突消解等底层原理,并借助源码与测试实例获得可直接定位、可继续深入研读的工程路线图。
一、从 Flow 出发:Colang 2.x 的核心抽象
NeMo Guardrails 工具包的核心抽象是一个flow。在 Colang 2.x 中,flow 是用 Colang 语言书写的、描述一次交互中「事件如何被匹配、动作如何被触发」的可执行流程单元——它既是对话逻辑的载体,也是运行时状态机处理的基本单位。
从源码结构看,Colang 2.x 运行时由 flows.py(数据模型)、statemachine.py(状态机执行)、runtime.py(外部 API 与动作调度)、serialization.py(状态序列化)与 errors.py(异常体系)组成。其中与「flow」直接对应的三个核心类分别是:
- FlowConfig:flow 的静态配置,记录其
id、元素序列elements、参数parameters、装饰器decorators(如@loop、@meta、@override)、返回成员与标签位置element_labels。每个 flow 都会有一个main主流程作为故事起点(initialize_state中强制断言"main" in state.flow_configs)。 - FlowState:flow 的实例状态,包含唯一实例 id、所属交互循环
loop_id、层级位置hierarchy_position(如"0.1.0.4"中的每个数字代表父流程中相关启动元素的位置)、局部上下文context、参数arguments、优先级priority(默认 1.0)、子流程与动作列表,以及activated(激活引用计数)与生命周期状态status(WAITING → STARTING → STARTED → STOPPING → STOPPED/FINISHED)。 - FlowHead(流程头):指向 flow 元素序列中某位置的「游标」,是状态机推进的最小单元。一个 flow 可以有多个 head(支持 fork/merge),每个 head 记录自己的匹配评分历史
matching_scores、作用域scope_uids、子 head 列表以及可选的catch_pattern_failure_label(匹配失败时跳转到指定标签而非中止流程)。
# nemoguardrails/colang/v2_x/runtime/statemachine.py 中 create_flow_instance 的关键片段 flow_state = FlowState( uid=flow_instance_uid, flow_id=flow_config.id, loop_id=loop_uid, hierarchy_position=flow_hierarchy_position, heads={head_uid: FlowHead(uid=head_uid, flow_state_uid=flow_instance_uid, matching_scores=[])}, )所有 flow 实例与配置都挂在State(flows.py)上:flow_states、flow_configs、actions、内部事件队列internal_events(deque)、全局上下文context、出站事件outgoing_events,以及两个加速查找结构event_matching_heads与event_matching_heads_reverse_map(按事件名索引到等待匹配的 head,供事件匹配阶段快速定位候选 head)。
二、标准事件:连接用户、意图与动作的桥梁
运行时文档给出了标准事件清单,它们是外部世界(用户、意图分类器、动作执行器)与 flow 引擎交互的协议:
| 事件 | 语义 |
|---|---|
UtteranceUserActionFinished(final_transcript) | 收到了用户的新话语(utterance) |
UserIntent(intent) | 已识别出用户话语的规范化形式(意图) |
BotIntent(intent) | 已决定机器人应该表达什么(新 bot 意图) |
StartUtteranceBotAction(content) | 已确定一条 bot 消息的文本内容 |
StartInternalSystemAction(action_name, is_system_action, action_parameters) | 决定启动某个动作 |
InternalSystemActionFinished(action_name, action_parameters, action_result) | 某个动作已完成 |
Listen | 没有可做之事,bot 应监听新的用户输入/事件 |
ContextUpdate | 上下文数据已更新 |
在源码层面,事件被细分为三类对象(flows.py):
- Event:基础事件类,含
name与arguments,并提供from_umim_event将扁平字典转换为事件对象;所有事件参数同时暴露为事件属性(__getattr__)。 - InternalEvent:内部事件,附属于某个 flow(
flow字段),不会出现在对外事件流中且优先级高于外部事件。 - ActionEvent:动作事件,带
action_uid与action引用,使表达式可以直接访问动作对象。
Action类统一了动作生命周期的事件命名约定(flows.py):Started / Updated / Finished / Start / Change / Stop分别映射到started_event / updated_event / finished_event / start_event / change_event / stop_event,因此任意动作X对应StartX、XStarted、XUpdated、XFinished、StopX等事件;ActionStatus枚举跟踪INITIALIZED → STARTING → STARTED → STOPPING → FINISHED的状态迁移。这也解释了文档中StartUtteranceBotAction、InternalSystemActionFinished等命名的由来——它们都是 UMIM 事件约定的实例。
三、标准上下文变量
运行时文档定义了三个标准上下文变量,供所有 flow 读取:
last_user_message:用户的最后一条话语。last_bot_message:bot 的最后一条话语。relevant_chunks:与用户所说内容相关的文本块(RAG 检索结果)。
在 library/core.co 中可以找到它们的实际写入位置:_bot_say内部 flow 会执行global $bot_message、global $last_bot_message并将$last_bot_message = $text;_user_said_something_unexpected则写入global $last_user_message。这些变量在 flow 内通过global关键字声明后,实际存储在State.context(全局上下文)中——slide函数处理Global元素时,会在 flow 局部上下文写入_global_<var>标记,并把变量本体放入state.context(statemachine.py)。
四、推理循环(Reasoning Loop)设计
推理循环是运行时文档的核心章节,描述引擎如何反复消费事件、推进 flow head、解决动作冲突,直到系统达到稳定(等待监听)状态。
4.1 语句类型:阻塞、非阻塞与模式相关性
运行时把 flow 中的语句按两类维度分类:
按阻塞性分类:
- 非阻塞语句(sliding elements):纯流程逻辑,如 if/else、while、变量赋值、return、abort 等,执行后 head 立即前移。
- 阻塞语句(matching elements):流程控制(start、activate、stop、deactivate——底层等价于
send StartFlow+match FlowStarted)、动作发送(send 事件)、决策(match 事件)。
按模式相关性分类:
- 模式无关语句(无冲突):流程逻辑、发送内部事件(start flow、stop flow、flow finished 等)、match 语句(决策)。
- 模式相关语句(潜在冲突):发送 UMIM 事件(即触发动作)。
源码中对应的判定函数是is_action_op_element(send 且事件名不在InternalEvents.ALL中即视为可执行动作)与is_match_op_element(op == "match")(statemachine.py)。slide()函数(statemachine.py)是「滑动」的实际实现:遇到send且目标为内部事件时推入内部事件队列并继续前移;遇到动作事件(非内部事件)则停止;遇到Label/Goto/ForkHead/MergeHeads/WaitForHeads/Assignment/Return/Abort/Priority/Global/CatchPatternFailure/BeginScope/EndScope等元素分别执行对应逻辑。
4.2 内部事件
内部事件不进入对外事件流,仅用于引擎内部的流程编排:
StartFlowFlowStartedFlowFinishedFlowFailedFlowPausedFlowResumed
源码中InternalEvents枚举(flows.py)实际定义了StartFlow / FinishFlow / StopFlow / FlowStarted / FlowFinished / FlowFailed / UnhandledEvent,以及BotIntentLog / UserIntentLog / BotActionLog / UserActionLog四个用于意图与动作日志记录的事件。其中UnhandledEvent是关键设计:对于每个独立交互循环中未被任何 head 处理的事件,引擎会生成UnhandledEvent(event=..., loop_ids=...),从而让warning of unexpected user utterance、notification of undefined flow start等调试/兜底 flow 能够匹配它(见 core.co)。
4.3 处理概述
文档给出的顶层处理流程如下:
- 所有 head 都停留在匹配语句(matching statements)上。
- 弹出下一个外部事件。
- 处理事件:
- 检查 head 的相关性(relevance);
- 对所有相关 head 判定匹配/不匹配:不匹配的 head 标记为
paused,匹配的 head 标记为matched,并分配匹配评分与处理组 id; - 迭代推进所有匹配 head,直到它们停在一个阻塞语句上:head 可以为流程逻辑元素前进、分支或合并(并跟踪 head 历史),可以为流程控制元素生成新的内部事件(如 StartFlow、FlowAdvanced、FlowFinished);
- 弹出下一个内部事件并重复处理,直到内部事件队列为空——同一事件若有多个匹配语句将形成 head 树,决策语句可在此建立分支点;
- 所有 head 现在都停在决策语句上;
- 利用 head 图(相同处理组 id 且位于同一交互循环内)解析所有冲突的动作语句:失败的 head 标记为
aborted并生成FlowAborted内部事件; - 执行所有未被中止的动作语句;
- 继续推进 head 并处理内部事件,直到所有 head 停在决策语句上。
这段描述在源码中的直接对应物是run_to_completion()(statemachine.py)的三层 while 循环:最内层消费state.internal_events并完成事件匹配、失败处理与 head 推进;中层处理MERGING状态的 head;外层通过_resolve_action_conflicts消解动作冲突后再次推进,直到没有 head 可推进为止。
五、处理细节:从故事启动到事件处理
5.1 启动故事(Start story)
- 所有 flow head 都位于第一条语句(可以是任何元素,因为无名 flow 不必以 match 语句开头)。
- 为每个 head 分配独立的处理组 id。
- 对每个处理组:
- 重复执行「处理 head 语句(滑动)」,将组内所有非阻塞 head 推进到阻塞语句;
- 若
action_statement列表非空,则执行「冲突消解」,然后重复上一步。
- 所有 head 都停在匹配语句上。
- 若内部事件队列非空,弹出下一事件并分配处理组 id,然后「处理事件」。
对应到运行时代码:process_events()检测到state.main_flow_state.status == FlowStatus.WAITING时(runtime.py),会在输入事件队首插入InternalEvent(name="StartFlow", arguments={"flow_id": "main"}),并在其之前为所有带active装饰器的模块级 flow 插入启动事件,随后进入主循环while input_events or local_running_actions:逐条处理。
5.2 处理事件(Process event)
- 对所有 flow head 检查事件是否相关,再判定匹配/不匹配:
- 相关但不匹配的 head 追加到
paused_flow列表(并推送FlowsPaused事件); - 相关且匹配的 head 追加到
matched_flows列表(并推送FlowsMatched事件)。
- 相关但不匹配的 head 追加到
- 处理
FlowsMatched事件。 - 对所有相关 head 再次检查事件是否匹配:对于已激活 flow(head > 0),不匹配则标记为
Paused,匹配则标记为Matched。
源码中,候选 head 通过_get_all_head_candidates(statemachine.py)按事件名从event_matching_heads索引取出,并按交互循环优先级与流程层级位置排序;随后_compute_event_matching_score计算匹配评分:1.0 表示精确匹配,小于 1.0 表示模糊匹配(部分参数缺失但其余都匹配),0.0 表示不匹配,-1.0 表示失配(mismatch)——失配的 head 若配置了catch_pattern_failure_label则跳到标签继续,否则整个 flow 被中止(_abort_flow)。
评分细则(_compute_arguments_dict_matching_score,statemachine.py)值得注意:参数支持正则(re.Pattern)、比较表达式(ComparisonExpression)、字典、列表、集合等多种匹配形态;参考参数多于实际参数时直接返回 0;参数全部匹配但数量不一致时按0.9 ** (len(args) - len(ref_args))打折,实现模糊匹配;最终评分还会乘以 flow 的priority([0.0-1.0])。另外,文档中提到的「匹配失败(not关键字)」在源码中仍以硬编码逻辑形式存在:例如FlowFinished与FlowFailed、FlowStarted与FlowFinished/Failed之间的互斥失配关系(返回 -1.0),以及argument_filter = ["return_value", "activated", "source_flow_instance_uid"]对这些参数的特殊处理。
5.3 处理 head 语句(滑动)
按元素类型分派:
- 流程逻辑:执行语句并推进/分支/合并 head;
- 发送事件:阻塞,并追加到
action_statement列表; - 匹配事件:阻塞,并追加到
matching_statement列表。
5.4 冲突消解(Resolve conflicts)
- 仅比较同一交互循环内、语句不同的 head;
- 检查 head 历史,取最早匹配评分最高者。
源码_resolve_action_conflicts(statemachine.py)的实现细节:只有一个可执行 head 时直接生成动作事件;多个 head 时先按loop_id分组,组内按匹配评分(不足部分以 1.0 补齐)降序排序,评分完全相同则随机选取一个胜者(对应文档中的start a, start bvsstart a and b的待讨论问题);与胜者事件完全相同的 head 视为共同胜者(co-winning),会把动作引用替换为胜者动作的引用并共享同一action_uid;配置了失败捕获标签的 head 跳转到标签;其余失败 head 中止对应 flow 并生成FlowFailed事件。所有被选中启动的动作通过_generate_umim_event写入state.outgoing_events,成为Start<ActionName>形式的外部事件。
5.5 动作调度与对外输出
对外事件产出后,runtime.py 的process_events会逐一识别Start(.*)Action类型事件并分流:注册在本地action_dispatcher的动作被创建为 asyncio 任务执行(支持execute_async异步执行与instant_actions即时完成模式),未注册的动作则原样作为输出事件交给上游(如 actions server,经/v1/actions/run转发,见_get_action_resp)。动作完成后的...Finished事件会被重新注入input_events继续驱动状态机,直到内部事件队列耗尽。state.last_events保留最近 500 条事件历史,供 LLM 提示等场景使用。
六、从标准库到测试:引擎行为的实证
6.1 标准语义 flow 的定义方式
core.co 展示了 flow 语义层与事件层之间的映射,例如user said系列 flow 通过await _user_said(内部匹配UtteranceUserAction.Finished(final_transcript=$text))实现「等待用户说出某话」;bot say $text通过await UtteranceBotAction(script=$text) as $action触发 bot 消息;状态跟踪 flow 使用@loop("state_tracking", 10)装饰器声明自己的交互循环与优先级,通过await bot started saying something/await bot said something维护$bot_talking_state、$last_bot_message等全局变量。这些正是文档中「标准事件」「标准上下文变量」与「交互循环」概念的活体示例。
6.2 交互循环(Interaction Loop)
flows.py 定义了InteractionLoopType枚举:NEW(每个新实例独立循环)、PARENT(与父流程同循环,默认)、NAMED(按@loop(id)指定的命名循环)。FlowConfig.loop_id / loop_priority / loop_type属性解析@loop装饰器参数。交互循环的意义在于:事件匹配与动作冲突消解都以 loop 为边界进行——不同循环中的 flow 互不竞争,这使多个独立子对话(例如并行工具调用跟踪)可以在同一状态机内共存。此外,create_flow_instance会依据 loop 类型在实例化时预分配loop_uid(NEW用随机 uuid,NAMED用命名 id,PARENT留空在_start_flow时继承父 flow 的 loop_id)。
6.3 测试覆盖印证
仓库测试目录 tests/v2_x/ 包含大量针对本运行时文档所述机制的测试:
- test_event_mechanics.py:验证
send StartUtteranceBotAction(script="Hello world")等语句产生对应StartUtteranceBotAction输出事件,以及UtteranceUserActionFinished输入如何驱动后续 bot 动作——直接对应文档的事件清单。 - test_flow_mechanics.py、test_story_mechanics.py、test_slide_mechanics.py:覆盖 flow 启动、head 滑动、故事级多事件驱动的行为。
- test_group_mechanics.py、test_state_serialization.py、test_python_api.py:覆盖交互循环分组、状态序列化与外部 Python API 集成。
这些测试与RuntimeV2_x.process_events的while input_events or local_running_actions:主循环、max_events防失控保护(默认上限,超出即返回)共同构成了推理循环的工程化保障。
七、已知限制与演进路线
运行时文档同时以 Questions / Todos / To Discuss / Changes 的形式记录了引擎的设计边界与未来方向,阅读时请勿将其视为已实现能力:
- 未完成项(Todos):
match语句与not关键字结合、通过stop $flow_ref/send $flow_ref.Stop()停止 flow 与 action、已激活 flow 的 pause/resume 机制、Colang 三引号多行字符串、start $flow_x变量启动、flow()系统函数、send user say "how are you".Start() as $id的解析修复、内部事件参数(如flow_id、activated)与 flow 上下文变量的隔离(计划封装进internal字典)、expression_functions作为 IF 条件、match (bot ask ...).Finished("confirmed")的解析支持等。 - 待讨论(To Discuss):动作参数是否应放入事件的独立键(目前用黑名单从
Start...Action事件中提取实际参数);是否需要将动作分类为「冲突/不冲突」类型以区分start a, start b与start a and b的语义。 - 待确认(Questions):
process_events既修改又返回传入的 state,需要清理该 API。 - 变更方向(Changes):为 flow 配置与状态加入交互循环 id;将 flow heads 扩展为列表以支持单 flow 多 head;每个命名 flow 的首个元素为 StartFlow 事件匹配器(flow 状态需在事件处理前从 flow 配置初始化),无名 flow 则立即启动(大概率处于激活模式)。
从源码可以进一步推断,部分 Todo 已有对应雏形:PauseFlow / ResumeFlow事件在FlowState._event_name_map中已定义(pause_event、resume_event),但在_process_internal_events_without_default_matchers中仍被注释禁用;catch_pattern_failure_label与_resolve_action_conflicts中的标签跳转已实现「匹配失败非中止」的替代路径,是not语义的部分前身。
结语
Colang 2.x 运行时文档与 statemachine.py、flows.py、runtime.py 共同呈现了一套事件驱动、head 游标推进、内部事件队列 + 匹配评分 + 冲突消解的对话状态机方案。理解这套推理循环,是深入 NeMo Guardrails 二次开发、调试复杂多轮对话、设计自定义 guardrail flow 的基础;建议结合 core.co 的标准语义 flow 与 tests/v2_x/ 的机制测试逐段对照阅读,以获得「设计文档 → 源码实现 → 测试验证」的完整闭环认知。
- 人工智能
- 大模型
- AI 安全治理
- 模型安全
- 内容安全
- 提示词注入防护
- RAG
【免费下载链接】Guardrails
NeMo Guardrails is an open-source toolkit for easily adding programmable guardrails to LLM-based conversational systems.
相关推荐
NeMo Guardrails事件驱动API深度解析
NeMo Guardrails事件驱动API深度解析 引言:为什么需要事件驱动架构? 在构建基于大语言模型(LLM)的对话系统时,传统的请求 响应模式往往难以满
人工智能大模型AI 安全治理模型安全内容安全提示词注入防护RAG如何利用NeMo Guardrails事件驱动API实时监控AI对话状态:完整操作指南
如何利用NeMo Guardrails事件驱动API实时监控AI对话状态:完整操作指南 NeMo Guardrails是一款开源工具包,专为LLM对话系统添加可
人工智能大模型AI 安全治理模型安全内容安全提示词注入防护RAGFoundationDB Flow 运行时深度解析:Actor 协程、Future/Promise 异步原语与 Net2 事件循环
FoundationDB Flow 运行时深度解析:Actor 协程、Future/Promise 异步原语与 Net2 事件循环 导读 Flow 是 Foun
分布式数据库KV存储数据库后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考