Parlant 项目拆解:如何让Agent在多轮对话场景更听话

0 阅读35分钟

项目地址: github.com/emcie-co/pa…

项目定位与核心价值

项目定位

Parlant 是一个开源的 Agentic Behavior Modeling (ABM) 引擎,专门用于构建面向客户的对话式 AI 代理。与传统的基于提示工程的框架不同,Parlant 通过结构化的行为建模方法,确保 AI 代理能够可靠地遵循业务规则和预期行为。

核心价值主张

解决的核心问题
  • 问题 1:LLM 不遵循指令 - 传统方法依赖系统提示词,但 LLM 经常忽略或误解这些指令
  • 问题 2:行为不一致 - 相同输入可能产生不同输出,难以保证生产环境下的可靠性
  • 问题 3:难以控制 - 缺乏有效机制确保代理在关键时刻遵循业务规则
  • 问题 4:调试困难 - 无法理解代理为何做出特定决策
Parlant 的解决方案
  • 确保规则遵循 - 通过动态指南匹配和上下文应用,确保代理遵循业务规则
  • 行为可预测 - 结构化的行为建模方法提供一致、可预测的行为
  • 生产就绪 - 从第一天起就具备企业级功能,支持大规模部署
  • 完全可解释 - 提供详细的决策过程追踪和可解释性

与传统方法的对比

传统 AI 框架Parlant
编写复杂的系统提示词用自然语言定义规则
希望 LLM 遵循提示确保规则遵循
调试不可预测的行为可预测、一致的行为
通过提示工程扩展通过添加指南扩展
可靠性靠运气从第一天起就生产就绪

第一章:Guidelines(行为准则)

1.1 什么是 Guidelines?

Guidelines 是 Parlant 中用于以上下文和目标方式调整代理行为的主要方法。它们允许我们指示代理在特定情况下如何响应,覆盖其默认行为,从而确保行为符合我们的期望和业务需求。

核心定义

Guideline:用自然语言定义的上下文规则,指导代理在特定情况下的行为。

每个 Guideline 由两部分组成:

  • Condition(条件):指定何时应该应用该指南
  • Action(动作):描述应该做什么

基本结构

await agent.create_guideline(
    condition="客户询问产品库存",
    action="先检查库存状态,然后提供准确信息"
)

当条件满足时,代理会执行动作。在非正式讨论中,我们通常用"当...时,则..."的形式描述:当客户询问产品库存时,则先检查库存状态,然后提供准确信息

1.2 Guidelines 的核心特性

1.2.1 条件-动作结构

每个 Guideline 都遵循清晰的条件-动作模式:

# 基本指南
await agent.create_guideline(
    condition="客户要求退款",
    action="验证订单状态,然后处理退款请求"
)

# 观察性指南(只有条件,没有动作)
await agent.create_guideline(
    condition="客户是 VIP 会员",
    action=None  # 仅用于上下文理解,不执行动作
)

1.2.2 动态匹配

系统自动评估对话上下文,只加载相关的 Guidelines。这确保了:

  • 最大化 LLM 注意力:只关注相关的指南
  • 降低认知负担:避免信息过载
  • 提高准确性:专注于最相关的规则

1.2.3 工具关联

Guidelines 可以与工具(函数)关联,确保工具在正确时机被调用:

@p.tool
async def check_inventory(
    context: p.ToolContext,
    product_name: str
) -> p.ToolResult:
    stock = await db.get_stock(product_name)
    return p.ToolResult(data=stock)

await agent.create_guideline(
    condition="客户询问特定产品的库存",
    action="查询该产品的实时库存信息",
    tools=[check_inventory]  # 关联工具
)

1.2.4 应用追踪

系统跟踪 Guideline 是否已被应用,避免重复:

  • 默认行为:一旦应用就不再重复应用
  • 持续型指南:可以重复应用
  • 上下文变化:如果上下文发生变化,可能重新应用

1.3 Guidelines 的类型

1.3.1 类型确定方式

重要说明:Guidelines 的类型可以通过两种方式确定:

  1. 手动指定(推荐):创建者在创建 Guideline 时明确指定类型
  2. 自动检测(可选):系统可以使用 LLM 自动检测某些类型(主要用于评估和索引阶段)

运行时行为

  • 在运行时匹配阶段,系统使用 metadata 中的显式标记来确定类型
  • 如果创建者没有指定,系统会使用默认行为

1.3.2 Observational Guidelines(观察性指南)

特点:只有条件,没有动作

确定方式

  • 手动指定:创建 Guideline 时设置 action=None
  • 自动判断:系统通过检查 g.content.action 是否为 None 来判断

用途

  • 理解对话上下文
  • 影响其他指南的匹配
  • 不触发任何行为

示例

await agent.create_guideline(
    condition="客户是 VIP 会员",
    action=None  # 系统会自动识别为观察性指南
)

1.3.3 Actionable Guidelines(可操作指南)

特点:有条件和动作

确定方式

  • 手动指定:创建 Guideline 时提供 action 参数
  • 自动判断:系统通过检查 g.content.action 是否存在来判断

用途

  • 当条件满足时,代理执行动作
  • 默认情况下,一旦应用就不再重复应用

示例

await agent.create_guideline(
    condition="客户要求退款",
    action="验证订单状态,然后处理退款请求"
)

1.3.4 Continuous Guidelines(持续型指南)

特点:可以重复应用

确定方式

  • 手动指定(推荐):在 metadata 中明确设置 {"continuous": True}
  • 自动检测(可选):系统可以使用 LLM 自动检测(主要用于评估和索引阶段)

用途

  • 适用于需要持续关注的情况
  • 每次条件满足时都会应用

示例

await agent.create_guideline(
    condition="对话中出现技术问题",
    action="提供技术支持",
    metadata={"continuous": True}  # 明确标记为持续型
)

1.3.5 Customer-Dependent Guidelines(客户依赖型指南)

特点:动作需要客户提供信息才能完成

确定方式

  • 手动指定(推荐):在 metadata 中明确设置 customer_dependent_action_data
  • 自动检测(可选):系统可以使用 LLM 自动检测(主要用于评估和索引阶段)

用途

  • 系统会追踪客户是否已提供所需信息
  • 只有在客户提供信息后才认为动作完成

示例

await agent.create_guideline(
    condition="需要验证客户身份",
    action="获取客户的账户号码",
    metadata={"customer_dependent_action_data": {
        "is_customer_dependent": True,
        "customer_action": "客户提供了账户号码",
        "agent_action": "代理询问了客户的账户号码"
    }}
)

1.3.6 类型确定总结

类型手动指定方式自动检测运行时判断依据
Observationalaction=None不需要(直接判断)g.content.action 是否为 None
Actionable提供 action不需要(直接判断)g.content.action 是否存在
Continuousmetadata={"continuous": True}✅ LLM 自动检测g.metadata.get("continuous", False)
Customer-Dependentmetadata={"customer_dependent_action_data": {...}}✅ LLM 自动检测g.metadata.get("customer_dependent_action_data", {}).get("is_customer_dependent", False)

最佳实践

  • 推荐手动指定:明确指定类型可以确保行为符合预期
  • 使用自动检测作为辅助:在评估阶段使用自动检测来验证和补充
  • ⚠️ 运行时优先使用显式标记:运行时匹配阶段优先使用 metadata 中的显式标记

1.4 Guidelines 的优先级和关系

1.4.1 优先级关系

Guidelines 可以建立优先级关系,确保重要规则优先执行:

guideline1 = await agent.create_guideline(
    condition="紧急情况",
    action="立即处理"
)

guideline2 = await agent.create_guideline(
    condition="一般请求",
    action="按正常流程处理"
)

# guideline1 优先于 guideline2
await guideline1.prioritize_over(guideline2)

1.4.2 依赖关系

Guidelines 可以建立依赖关系:

# guideline2 依赖 guideline1
await guideline2.depend_on(guideline1)

注意:Guidelines 还可以与 Journeys 建立关系(优先级和依赖关系),这部分内容将在第二章"Journeys"中详细介绍。

1.5 Guidelines 的作用域

1.5.1 Agent-Scoped Guidelines(代理作用域)

代理级别的指南,适用于该代理的所有对话:

await agent.create_guideline(
    condition="客户询问产品信息",
    action="提供准确的产品信息"
)

1.5.2 Journey-Scoped Guidelines(Journey 作用域)

只在特定 Journey 激活时才生效的指南:

journey = await agent.create_journey(
    title="订单处理",
    conditions=["客户有订单相关问题"]
)

# 这个指南只在"订单处理"Journey 激活时才会被评估
await journey.create_guideline(
    condition="客户想要取消订单",
    action="确认订单状态,然后处理取消请求",
    tools=[cancel_order]
)

1.5.3 Global Guidelines(全局指南)

没有特定作用域的指南,适用于所有代理:

await agent.create_guideline(
    condition="客户表达了不满",
    action="表示歉意并提供帮助"
)

1.6 Guidelines 的实现架构

1.6.1 数据模型

Guideline(指南)

  • id:唯一标识符
  • content:包含条件和动作
  • enabled:是否启用
  • tags:标签列表(用于作用域控制)
  • metadata:元数据(存储指南类型、依赖关系等)
  • criticality:重要性级别
  • composition_mode:组合模式(可选)

GuidelineContent(指南内容)

  • condition:条件字符串
  • action:动作字符串(可选)
  • description:描述(可选)

GuidelineMatch(指南匹配结果)

  • guideline:匹配的指南
  • score:匹配分数(0-10)
  • rationale:匹配理由
  • metadata:匹配元数据

1.6.2 存储架构

文档数据库存储

  • guidelines 集合:存储指南基本信息
  • guideline_tags 集合:存储标签关联
  • guideline_tool_associations 集合:存储工具关联

向量数据库存储

  • 用于语义搜索相关指南
  • 支持快速查找匹配的指南

第二章:Journeys(对话流程)

2.1 什么是 Journeys?

Journeys 是 Parlant 中用于定义结构化、多步骤对话流程的功能。它们允许代理引导客户完成特定目标,如预订行程、故障排除或其他需要多轮交互的过程。 Journeys 是一组 Guidelines 的逻辑组合,通过一个状态图维护进度。

核心定义

Journey:结构化的多步骤对话流程,引导客户完成特定目标。

Journey 有 4 个重要组成部分:

  1. Title(标题):简短、描述性的名称,用于区分不同的 Journey
  2. Conditions(条件):上下文查询,确定何时应该激活 Journey
  3. Description(描述):描述 Journey 的性质,包括动机或指导说明
  4. States & Transitions(状态和转换):状态图,传达理想的流程

核心特性

  • 状态图模型:基于状态图定义对话流程
  • 灵活导航:代理可以跳过状态、回溯或提前跳转,保持自然对话
  • 条件激活:通过上下文查询确定何时激活 Journey
  • 平衡灵活性与控制:在灵活性和协议遵循之间取得平衡

2.2 Journey 的状态类型

2.2.1 Chat States(对话状态)

代理与客户交流的状态:

journey = await agent.create_journey(
    title="预订航班",
    conditions=["客户想要预订航班"]
)

# 创建对话状态
initial_state = journey.initial_state
state1 = await initial_state.transition_to(
    chat_state="询问目的地和出发日期"
)

2.2.2 Tool States(工具状态)

调用外部工具执行操作的状态:

@p.tool
async def search_flights(
    context: p.ToolContext,
    origin: str,
    destination: str,
    date: str
) -> p.ToolResult:
    flights = await flight_api.search(origin, destination, date)
    return p.ToolResult(data=flights)

# 创建工具状态
state2 = await state1.transition_to(
    tool_state=search_flights
)

2.2.3 Fork States(分支状态)

根据条件分支对话流程的状态:

# 创建分支状态
fork_state = await state2.transition_to(
    fork_state="根据搜索结果决定下一步"
)

# 分支条件
await fork_state.fork(
    condition="找到可用航班",
    target=state3  # 继续预订流程
)

await fork_state.fork(
    condition="未找到可用航班",
    target=state4  # 提供替代方案
)

2.3 Journey 的灵活性与控制

2.3.1 灵活性特性

Parlant 的 Journey 设计在灵活性和控制之间取得平衡:

灵活性

  • 代理可以跳过已完成的状态
  • 支持回溯到之前的状态
  • 允许客户提供信息顺序与流程不同
  • 支持多轮对话停留在同一状态

2.3.2 控制性特性

控制性

  • 确保关键步骤不被跳过
  • 验证必要信息已收集
  • 强制执行工具调用顺序
  • 确保协议遵循

2.4 Journey 的激活条件

2.4.1 激活条件定义

Journey 的激活条件实际上是观察性 Guidelines:

journey = await agent.create_journey(
    title="预订航班",
    conditions=["客户想要预订航班"]  # 转换为观察性指南
)

系统内部会创建:

Guideline(condition="客户想要预订航班", action=None)

2.4.2 激活机制

当 Journey 的激活条件 Guideline 匹配成功时,Journey 被激活:

1. 用户消息:"我想预订航班"2. 系统评估 Guidelines(包括 Journey 的激活条件)
   → 匹配到"客户想要预订航班"Guideline
   ↓
3. 激活"预订航班"Journey

2.4.3 Journey 激活的详细机制

激活过程

  1. 检查匹配的 Guidelines

    # 获取所有匹配成功的 Guideline ID
    matched_guideline_ids = {g.id for g in matched_guidelines}
    
    # 检查哪些 Journey 的激活条件在匹配的 Guidelines 中
    activated_journeys = [
        journey for journey in all_journeys
        if set(journey.conditions).intersection(matched_guideline_ids)
    ]
    
  2. 激活 Journey

    • 如果 Journey 的激活条件 Guideline 匹配成功,Journey 被激活
    • 重要:所有匹配条件的 Journey 都会被激活,不限制数量
    • 激活后,Journey 的状态会被投影为 Guidelines 供后续使用
  3. 保持已激活的 Journey

    • 如果 Journey 在之前的交互中已经激活,且仍在路径中,保持激活状态
    • 这确保了 Journey 的连续性
  4. 多个 Journey 同时激活

    • 多个 Journey 可以同时激活(如果它们的激活条件都匹配)
    • 每个激活的 Journey 都会独立处理其状态选择
    • 所有激活的 Journey 的状态都会被投影为 Guidelines
    • 这些 Guidelines 会一起参与响应生成
    • LLM 会根据所有激活的 Journey 状态和匹配的 Guidelines 生成响应

示例

场景:用户说"我想预订航班和酒店"

匹配结果:
→ "客户想要预订航班"Guideline 匹配 ✓
→ "客户想要预订酒店"Guideline 匹配 ✓

激活结果:
→ JourneyA(预订航班)被激活 ✓
→ JourneyB(预订酒店)被激活 ✓
→ 两个 Journey 同时激活

状态选择:
→ JourneyA 选择状态:"询问目的地"
→ JourneyB 选择状态:"询问入住日期"

响应生成:
→ 使用两个 Journey 的状态 Guidelines + 其他匹配的 Guidelines
→ 生成:"好的,我来帮您预订航班和酒店。请问您想去哪里?入住日期是什么时候?"

2.5 Journey 状态投影为 Guidelines

2.5.1 投影机制

Journey 的每个节点(Node)和转换(Edge)会被自动投影成 Guidelines:

  • 节点动作 → Guideline 的动作
  • 边条件 → Guideline 的条件

投影过程JourneyGuidelineProjection.project_journey_to_guidelines()

2.5.2 投影示例

# Journey 定义
journey = await agent.create_journey(...)
t1 = await journey.initial_state.transition_to(
    chat_state="询问目的地"
)

# 系统内部会自动创建 Guideline:
# Guideline(
#     condition="",  # 从边条件
#     action="询问目的地",  # 从节点动作
#     metadata={"journey_node": {...}}  # 包含 Journey 信息
# )

2.5.3 状态图 Guidelines 的逻辑关系

重要:Journey 状态图投影生成的 Guidelines 是有逻辑关系的,这些关系反映了状态图的结构:

  1. 状态图的连接关系

    • 每个节点对应一个 Guideline
    • 节点之间的转换(Edge)关系通过 follow_ups 字段保存在 Guideline 的 metadata 中
    • follow_ups 记录了从当前节点可以到达的下一个节点的 Guideline ID
  2. 逻辑关系示例

    # Journey 状态图:
    # 节点1 → 节点2 → 节点3
    
    # 投影后的 Guidelines:
    # Guideline1 (节点1):
    #   - action: "询问目的地"
    #   - metadata.journey_node.follow_ups: [Guideline2的ID]
    #
    # Guideline2 (节点2):
    #   - condition: "客户提供了目的地"
    #   - action: "询问出发日期"
    #   - metadata.journey_node.follow_ups: [Guideline3的ID]
    #
    # Guideline3 (节点3):
    #   - condition: "客户提供了出发日期"
    #   - action: "搜索航班"
    
  3. 关系的作用

    • 系统使用 follow_ups 来确定 Journey 的下一步状态
    • 这些关系确保了 Journey 的状态转换逻辑被正确执行
    • 代理可以根据这些关系选择下一个要执行的状态
  4. 与 Journey-Scoped Guidelines 的区别

    • 状态图投影的 Guidelines:有逻辑关系(通过 follow_ups 和状态图结构)
    • Journey-Scoped Guidelines(通过 journey.create_guideline() 创建):
      • 不一定有逻辑关系
      • 只是作用域绑定到 Journey
      • 用于处理 Journey 中的特殊情况,不一定与状态图有直接关系

2.6 Journey 状态选择机制

重要:每个 Journey 在每次交互中只会选择一个状态转化为 Guidelines 给 agent。

执行时机:Journey 状态选择是在 Guideline 匹配过程中进行的,作为 Guideline 匹配批次的一部分。

完整流程

1. 匹配 Guidelines(包括 Journey 的激活条件)
   ↓
2. 激活 Journey(基于匹配的激活条件)
   ↓
3. 对于每个激活的 Journey,在 Guideline 匹配批次中:
   a. 调用 LLM 评估当前状态完成度
   b. 调用 LLM 选择下一个状态(如果需要)
   c. 更新 Journey 路径状态
   d. 将选中的状态投影为 Guideline
   ↓
4. 所有匹配的 Guidelines(包括 Journey 状态 Guidelines)一起给 agent 参考

状态选择过程

  1. 评估当前状态完成度

    • 对于每个激活的 Journey,调用 LLM 评估当前状态是否完成
    • 对于客户依赖型状态,检查客户是否已提供所需信息
    • 对于工具状态,检查工具是否已执行
  2. 评估转换条件

    • 如果当前状态已完成,评估所有可能的转换条件
    • 使用 LLM 判断哪个转换条件最匹配当前上下文
    • 重要:LLM 只会选择一个最匹配的转换条件(applied_condition_id
  3. 选择下一个状态

    • 情况 A:当前状态未完成 → 保持当前状态(applied_condition_id = "0"
    • 情况 B:Journey 应该结束 → 选择根节点(applied_condition_id = "None"
    • 情况 C:当前状态已完成 → 选择匹配度最高的转换条件对应的目标状态
    • 结果:每个 Journey 只会选择一个状态(当前状态或下一个状态)
  4. 更新 Journey 路径状态

    • 将选中的状态添加到 journey_paths[JourneyId]
    • 记录 Journey 的状态转换历史
  5. 生成状态 Guidelines

    • 将选中的状态投影为 Guidelines
    • 每个 Journey 只会生成一个状态对应的 Guideline
    • 这些 Guidelines 会和其他匹配的 Guidelines 一起参与响应生成

示例

场景:JourneyA(预订航班)已激活,当前在"询问目的地"状态

情况 1:客户还未提供目的地
→ 当前状态未完成
→ 保持当前状态:"询问目的地"
→ 生成 1 个 Guideline(询问目的地)

情况 2:客户提供了目的地
→ 当前状态已完成
→ 评估转换条件:
   - 条件1"客户提供了目的地" → 状态:"询问出发日期"
   - 条件2"客户提供了目的地和出发日期" → 状态:"搜索航班"
→ LLM 选择最匹配的条件(假设是条件1)
→ 选择下一个状态:"询问出发日期"
→ 生成 1 个 Guideline(询问出发日期)

情况 3:客户说"我不想预订了"
→ Journey 应该结束
→ 选择根节点(退出 Journey)
→ 生成 1 个 Guideline(根节点,表示退出)

关键理解

  • 每个 Journey 每次交互只选择一个状态:要么保持当前状态,要么选择下一个状态,要么退出 Journey
  • 状态选择是互斥的:不会同时选择多个状态
  • Journey 路径是线性的journey_paths[JourneyId] 是一个列表,记录 Journey 的状态路径
  • 多个 Journey 可以并行:不同 Journey 可以同时激活,每个 Journey 独立选择自己的状态

2.7 Journey-Scoped Guidelines

2.7.1 定义

可以创建只在特定 Journey 激活时才生效的 Guidelines:

journey = await agent.create_journey(
    title="预订航班",
    conditions=["客户想要预订航班"]
)

# 这个 Guideline 只在"预订航班"Journey 激活时才会被评估
await journey.create_guideline(
    condition="客户想要取消预订",
    action="提供取消选项并确认"
)

2.7.2 使用场景

  • 处理 Journey 中的特殊情况
  • 处理偏离或异常情况
  • 在 Journey 中提供额外的行为指导

2.8 Guidelines 与 Journey 的关系

2.8.1 核心关系

Guidelines 和 Journey 在 Parlant 中紧密相关,但服务于不同的目的:

关系类型说明示例
激活条件Journey 的条件是观察性 Guidelineconditions=["客户想要预订"] → 观察性 Guideline
状态投影Journey 状态自动转换为 Guidelines节点动作 → Guideline 动作
作用域绑定Guideline 绑定到特定 Journeyjourney.create_guideline()
优先级Guideline 可以优先于 Journey 状态guideline.prioritize_over(journey)
依赖关系Guideline 可以依赖 Journeyguideline.depend_on(journey)

2.8.2 Guidelines 与 Journey 的优先级和依赖关系

Guidelines 可以优先于 Journey 状态:

journey = await agent.create_journey(...)

# Guideline 优先于 Journey 状态
await guideline.prioritize_over(journey)

# Guideline 依赖 Journey(只有在 Journey 激活时才生效)
await guideline.depend_on(journey)

使用场景

  • 优先级关系:当 Guideline 和 Journey 状态冲突时,Guideline 优先执行
  • 依赖关系:Guideline 只在特定 Journey 激活时才生效,用于 Journey 中的特殊情况处理

示例

# 即使 Journey 状态说"询问目的地"
# 但如果这个 Guideline 匹配,会优先执行 Guideline 的动作
await journey.create_guideline(
    condition="客户表达了紧急需求",
    action="先处理紧急情况,然后再继续预订流程"
)

2.8.3 使用场景对比

场景使用 Guideline使用 Journey
简单的条件-动作规则
多步骤流程
处理特殊情况
结构化对话流程
工具调用触发
需要状态管理

2.9 Journey 的实现架构

2.9.1 数据模型

Journey(旅程)

  • id:唯一标识符
  • title:标题
  • description:描述
  • conditions:激活条件(Guideline ID 列表)
  • root_id:根节点 ID
  • tags:标签列表
  • composition_mode:组合模式(可选)

JourneyNode(节点)

  • id:节点 ID
  • action:节点动作(Chat State 的指令或 Tool State 的工具)
  • tools:关联的工具列表
  • description:节点描述
  • metadata:元数据
  • composition_mode:节点级别的组合模式(覆盖 Journey 级别)

JourneyEdge(边/转换)

  • id:边 ID
  • source:源节点 ID
  • target:目标节点 ID
  • condition:转换条件(可选)
  • metadata:元数据

2.9.2 存储架构

文档数据库存储

  • journeys 集合:存储 Journey 基本信息
  • journey_nodes 集合:存储节点信息
  • journey_edges 集合:存储边信息
  • journey_conditions 集合:存储条件关联
  • journey_tags 集合:存储标签关联

向量数据库存储

  • 用于语义搜索相关 Journey
  • 内容包含标题、描述、节点动作和边条件
  • 支持快速查找匹配的 Journey

2.10 Journey 的最佳实践

2.10.1 设计原则

  1. 从简单开始

    • ✅ 从简单流程开始,逐步增加复杂性
    • ✅ 每个状态应该有明确的完成标准
  2. 状态设计

    • ✅ Chat States 应该清晰、具体
    • ✅ Tool States 应该专注于单一操作
    • ✅ 使用 Fork States 处理明确的分支场景
  3. 转换设计

    • ✅ 条件应该明确、可评估
    • ✅ 避免过于复杂的嵌套结构

2.10.2 常见陷阱

  1. 过度复杂化

    • ❌ 创建过于复杂的嵌套结构
    • ✅ 保持流程简单、清晰
  2. 忽略灵活性

    • ❌ 假设客户会严格按照流程
    • ✅ 允许客户以不同顺序提供信息

第三章:Recall and Matching Logic(召回与匹配逻辑)

3.1 概述

当用户发送消息时,Parlant 需要确定使用哪些 Guidelines 和 Journeys。这个过程涉及多个阶段的过滤和匹配,以确保只评估相关的规则,同时优化性能和成本。

3.1.1 核心原则

重要澄清:不是"先匹配 Journey 再匹配 Guideline",而是:

  1. 预测高概率 Journey(用于过滤 Guidelines,不是匹配)
  2. 匹配 Guidelines(LLM 评估)
  3. 激活 Journey(基于匹配结果)

关键区别

  • 预测 Journey:使用向量搜索或已有路径,快速找到可能相关的 Journey(用于优化)
  • 匹配 Guidelines:使用 LLM 评估 Guideline 条件是否匹配(核心步骤)
  • 激活 Journey:检查匹配的 Guideline ID 中是否有 Journey 的激活条件 ID

重要澄清:向量搜索不会导致不准确

向量搜索只是用于性能优化,不会影响最终结果的准确性,原因如下:

  1. 向量搜索只是过滤,不是最终判断

    • 向量搜索只用于预测"可能相关"的 Journey,用于减少需要评估的 Guidelines 数量
    • 真正的匹配判断还是通过 LLM 评估 Guidelines 来完成的
    • Journey 的激活由其激活条件 Guideline 的匹配结果决定,而不是向量搜索的结果
  2. 有补充匹配机制

    • 如果向量搜索预测错误(低概率 Journey 实际上被激活),系统会在阶段 8:补充匹配中自动处理
    • 当低概率 Journey 的激活条件 Guideline 匹配成功时,系统会:
      • 检测到该 Journey 被激活
      • 加载该 Journey 相关的 Guidelines
      • 进行额外的 LLM 匹配
      • 确保不遗漏相关行为
  3. 准确性保障

    • 向量搜索的准确性不影响最终结果,因为:
      • 如果预测正确:正常流程,性能优化
      • 如果预测错误:补充匹配机制会自动纠正,确保准确性
    • 最终激活的 Journey 都是基于 LLM 精确评估的结果,而不是向量搜索的相似度

示例

场景:向量搜索预测错误的情况

阶段 4:概率过滤
→ 向量搜索找到 JourneyA(预订航班)作为高概率 Journey
→ JourneyB(预订酒店)被标记为低概率

阶段 5:过滤 Guidelines
→ 保留 JourneyA 的 Guidelines
→ 过滤掉 JourneyB 的 Guidelines

阶段 6:LLM 语义匹配
→ 评估所有保留的 Guidelines(包括 JourneyA 的激活条件)
→ 同时评估全局 Guidelines(包括 JourneyB 的激活条件)

阶段 7:激活 Journey
→ JourneyA 的激活条件匹配 ✓ → 激活 JourneyA
→ JourneyB 的激活条件也匹配 ✓ → 激活 JourneyB(虽然被标记为低概率)

阶段 8:补充匹配(关键步骤)
→ 检测到 JourneyB 被激活(虽然之前被过滤)
→ 加载 JourneyB 相关的 Guidelines
→ 进行额外的 LLM 匹配
→ 确保 JourneyB 的行为被正确执行

结果:即使向量搜索预测错误,系统仍然能够正确激活所有相关的 Journey

3.2 完整处理流程

当用户发送消息时,系统按照以下步骤处理:

用户消息
  ↓
[阶段 1] 加载上下文
  - 加载代理、会话、客户信息
  - 检查是否有激活的 Journey 路径
  ↓
[阶段 2] 获取可用 Journey
  - 查询该代理的所有可用 Journey
  ↓
[阶段 3] 标签过滤 - 加载相关 Guidelines
  - 代理特定 Guidelines
  - 全局 Guidelines
  - Journey 关联的 Guidelines
  - Journey 投影生成的 Guidelines
  ↓
[阶段 4] 概率过滤 - 确定高概率 Journey
  ├─ 情况A:有激活的 Journey 路径 → 使用这些 Journey
  └─ 情况B:无激活路径 → 向量搜索找到最相关的 Journey(top_k=1)
  ↓
[阶段 5] 过滤 Guidelines
  - 保留:高概率 Journey 相关的 Guidelines + 全局 Guidelines
  - 过滤掉:低概率 Journey 相关的 Guidelines
  ↓
[阶段 6] LLM 语义匹配 - 评估 Guidelines
  - 对过滤后的 Guidelines 进行分类和批次处理
  - 使用 LLM 评估每个 Guideline 的条件是否匹配
  - 生成匹配结果(分数、理由)
  ↓
[阶段 7] 激活 Journey
  - 检查匹配的 Guidelines 中是否有 Journey 的激活条件
  - 如果有,激活对应 Journey
  ↓
[阶段 8] 补充匹配(如果需要)
  - 如果低概率 Journey 被激活,加载其 Guidelines 并重新匹配
  ↓
[阶段 9] 关系解析
  - 加载依赖和优先级相关的 Guidelines
  ↓
[阶段 10] Journey 状态选择(在 Guideline 匹配过程中进行)
  - 对于每个激活的 Journey,调用 LLM 评估当前状态完成度并选择下一个状态
  - 更新 Journey 路径状态
  - 将选中的状态投影为 Guidelines
  - 这些 Guidelines 会和其他匹配的 Guidelines 一起参与响应生成
  ↓
[阶段 11] 工具调用(如果有工具关联的 Guidelines)
  - 提取参数 → 调用工具 → 获取结果
  - 如果需要,返回阶段 6 重新评估
  ↓
[阶段 12] 生成响应
  - 使用所有匹配的 Guidelines 和 Journey 状态
  - 生成最终响应

3.3 多阶段过滤机制详解

3.3.1 阶段 1:标签过滤(Tag-based Filtering)

目的:只加载与当前上下文相关的 Guidelines

加载的 Guidelines

  • 代理特定的 Guidelines(通过 Tag.for_agent_id()
  • 全局 Guidelines(无标签)
  • 代理标签关联的 Guidelines
  • 这些 Journey 关联的 Guidelines
  • Journey 投影生成的 Guidelines(从 Journey 状态转换而来)

代码实现

# find_guidelines_for_context
agent_guidelines = await guideline_store.list_guidelines(
    tags=[Tag.for_agent_id(agent_id)]
)
global_guidelines = await guideline_store.list_guidelines(tags=[])
guidelines_for_journeys = await guideline_store.list_guidelines(
    tags=[Tag.for_journey_id(journey.id) for journey in journeys]
)

效果:从所有 Guidelines 减少到代理相关的 Guidelines(例如:从 1000 个减少到 200 个)

3.3.2 阶段 2:概率过滤(Probability-based Pruning)

目的:基于 Journey 激活概率进一步过滤 Guidelines

3.3.2.1 判断高概率 Journey

情况 A:已有激活的 Journey 路径

  • 如果对话中已经有激活的 Journey(journey_paths 不为空)
  • 这些 Journey 被认为是"高概率"的
  • 不需要使用向量搜索,直接使用这些 Journey
  • 重要:如果激活的 Journey 数量超过 top_k,会保留所有激活的 Journey(不限制数量)

情况 B:没有激活的 Journey

  • 如果对话中没有任何激活的 Journey
  • 使用向量数据库语义搜索找到与当前对话上下文最相关的 Journey
  • 向量搜索的对象是Journey,不是 Guidelines
  • 默认只选择最相关的 top_k=1 个 Journey 作为"高概率"候选
  • 如果激活的 Journey 数量少于 top_k,会补充最相关的其他 Journey 直到达到 top_k

向量搜索查询构建

# 构建查询文本
query = ""
query += 对话历史
query += 上下文变量
query += 已匹配的 Guidelines
query += 术语表

# 向量搜索
高概率 Journey = 向量搜索(query, top_k=1)

多个 Journey 的处理逻辑

当有多个 Journey 被召回或激活时,系统采用以下策略:

  1. 概率过滤阶段(用于过滤 Guidelines)

    • 默认 top_k=1,只选择最相关的 1 个 Journey 用于过滤 Guidelines
    • 但如果已经有激活的 Journey 路径,会保留所有激活的 Journey(不限制数量)
    • 如果激活的 Journey 数量少于 top_k,会补充最相关的其他 Journey
  2. 激活阶段(实际激活 Journey)

    • 所有匹配条件的 Journey 都会被激活,不限制数量
    • 激活条件:
      • Journey 的激活条件 Guideline 匹配成功
      • 或者 Journey 在之前的交互中已经激活(保持连续性)
    • 多个 Journey 可以同时激活
  3. 并行处理多个激活的 Journey

    • 每个激活的 Journey 都会独立处理其状态选择
    • 所有激活的 Journey 的状态都会被投影为 Guidelines
    • 这些 Guidelines 会一起参与响应生成
    • LLM 会根据所有激活的 Journey 状态和匹配的 Guidelines 生成响应

示例

场景:用户说"我想预订航班和酒店"

概率过滤阶段:
→ 向量搜索找到最相关的 Journey(top_k=1)
→ 假设找到 JourneyA(预订航班)

激活阶段:
→ 评估所有 Journey 的激活条件
→ JourneyA(预订航班)的激活条件匹配 ✓
→ JourneyB(预订酒店)的激活条件也匹配 ✓
→ 两个 Journey 都被激活

并行处理:
→ JourneyA 选择状态:"询问目的地"
→ JourneyB 选择状态:"询问入住日期"
→ 两个状态都被投影为 Guidelines
→ LLM 根据所有 Guidelines 生成响应:"好的,我来帮您预订航班和酒店。请问您想去哪里?入住日期是什么时候?"

关键理解

  • 概率过滤:用于优化性能,减少需要评估的 Guidelines 数量(默认 top_k=1)
  • 激活阶段:基于匹配结果,所有匹配条件的 Journey 都会被激活(不限制数量)
  • 并行处理:多个 Journey 可以同时激活,它们的状态会一起影响响应生成
3.3.2.2 过滤 Guidelines

过滤逻辑

# 伪代码
if 有激活的 Journey 路径:
    高概率 Journey = 所有激活的 Journey
else:
    高概率 Journey = 向量搜索找到的最相关的 top_k 个 Journey

# 只保留高概率 Journey 相关的 Guidelines
过滤后的 Guidelines = [
    Guideline for Guideline in 所有 Guidelines
    if (Guideline 属于高概率 Journey) or (Guideline 不依赖任何 Journey)
]

关键理解

  1. 通过向量选择 Journey,就默认选择了挂载在 Journey 上的 Guidelines

    • 当向量搜索找到高概率 Journey 后,系统会自动包含这些 Journey 相关的所有 Guidelines
    • 包括:
      • Journey 的激活条件 Guidelines
      • Journey-Scoped Guidelines(通过 journey.create_guideline() 创建的)
      • Journey 状态投影生成的 Guidelines(从 Journey 节点和转换自动生成)
  2. 不挂在 Journey 上的 Guidelines 会全量通过 LLM 匹配

    • 全局 Guidelines(不依赖任何 Journey)会全部保留
    • 这些 Guidelines 会全部通过 LLM 进行匹配评估
    • 因为它们是全局的,不依赖 Journey,所以不能通过 Journey 过滤
  3. 挂载在低概率 Journey 上的 Guidelines 会被过滤掉

    • 如果某个 Journey 不是高概率的,它相关的 Guidelines 会被过滤掉
    • 这样可以大幅减少需要评估的 Guidelines 数量
    • 如果低概率 Journey 后来被激活,会在补充匹配阶段重新加载其 Guidelines

实际例子

假设有:
- JourneyA(预订航班):高概率,有 50 个相关 Guidelines
- JourneyB(预订酒店):低概率,有 40 个相关 Guidelines
- JourneyC(查询天气):低概率,有 30 个相关 Guidelines
- 全局 Guidelines:30 个

过滤后:
- ✅ JourneyA 的 50 个 Guidelines(保留)
- ❌ JourneyB 的 40 个 Guidelines(过滤掉)
- ❌ JourneyC 的 30 个 Guidelines(过滤掉)
- ✅ 全局 Guidelines 30 个(保留)

结果:只对 80 个 Guidelines 进行 LLM 匹配,而不是 150 个
3.3.2.3 为什么 Journey 使用向量搜索,而 Guidelines 不使用?

核心原因

  1. 数量差异

    • Journey 数量少:通常只有几十个到几百个
    • Guidelines 数量多:可能有成千上万个
    • 对少量 Journey 进行向量搜索成本低,对大量 Guidelines 进行向量搜索成本高
  2. 用途不同

    • Journey 向量搜索 = 粗筛选(性能优化)
      • 目的是快速找到"可能相关"的 Journey
      • 用于过滤 Guidelines,减少需要评估的 Guidelines 数量
      • 不需要精确匹配,只需要找到最相关的几个
      • 重要:向量搜索的准确性不影响最终结果,因为有补充匹配机制保障
    • Guidelines LLM 匹配 = 精筛选(准确性保障)
      • 目的是精确判断"是否真的匹配"当前上下文
      • 需要理解复杂的语义和上下文关系
      • 必须给出准确的匹配结果(匹配/不匹配、分数、理由)
  3. 成本优化

    • 向量搜索成本低:只是计算向量相似度,不需要调用 LLM
    • LLM 评估成本高:需要调用 LLM 进行语义理解
    • 策略:先用便宜的向量搜索过滤 Journey,再用 LLM 评估少量过滤后的 Guidelines
    • 效果:从评估 1000 个 Guidelines 减少到评估 80 个 Guidelines,大幅降低成本
  4. 设计逻辑

    • Journey 的激活条件本身就是 Guidelines
    • 最终还是要通过 LLM 评估 Guidelines 来判断 Journey 是否激活
    • 向量搜索只是用来预测哪些 Journey 可能相关,用于过滤 Guidelines
    • 真正的匹配判断还是通过 LLM 评估 Guidelines 来完成
    • 即使向量搜索预测错误,补充匹配机制也会自动纠正
  5. 为什么 Guidelines 不使用向量搜索

    • Guidelines 数量太多,向量搜索成本仍然较高
    • Guidelines 的匹配需要精确的语义理解,向量搜索的相似度匹配不够精确
    • 标签过滤已经能够有效减少需要评估的 Guidelines 数量
    • 通过 Journey 过滤进一步减少后,剩余的 Guidelines 数量已经足够少,可以直接用 LLM 评估
  6. 准确性保障机制

    • 补充匹配:如果低概率 Journey 被激活,系统会自动加载其 Guidelines 并重新匹配
    • 全局 Guidelines 不受影响:全局 Guidelines 不会被 Journey 过滤,始终参与匹配
    • 激活条件匹配:所有 Journey 的激活条件都会参与 LLM 匹配,不受向量搜索影响
    • 结果:即使向量搜索预测错误,系统仍然能够正确激活所有相关的 Journey

总结

  • Journey 使用向量搜索:数量少 + 粗筛选 + 成本低 + 有补充匹配机制保障准确性
  • Guidelines 使用 LLM 匹配:数量多 + 精筛选 + 已经通过标签和 Journey 过滤
  • 组合策略:向量搜索(Journey)→ 过滤 Guidelines → LLM 匹配(Guidelines)→ 激活 Journey → 补充匹配(如果需要)
  • 准确性保障:即使向量搜索预测错误,补充匹配机制也会自动纠正,确保最终结果的准确性

3.3.3 阶段 3:初始匹配(Initial Matching)

目的:对过滤后的 Guidelines 进行匹配评估

过程

  1. Guidelines 分类

    • 观察性 Guidelines 批次
    • 可操作 Guidelines 批次
    • 已应用 Guidelines 批次
    • 客户依赖型 Guidelines 批次
    • 消歧批次(处理冲突的 Guidelines)
  2. 批次处理

    • 使用批次处理优化 LLM 调用
    • 每个批次包含多个 Guidelines,一起评估
  3. LLM 评估

    • 对每个 Guideline 的条件进行评估
    • 判断是否匹配当前上下文
    • 生成匹配结果(分数 0-10、理由)
  4. 生成匹配结果

    • 返回匹配成功的 Guidelines
    • 包括匹配分数和理由

3.3.4 阶段 4:补充匹配(Supplemental Matching)

目的:如果低概率 Journey 被激活,进行额外的匹配

触发条件

  • 在阶段 7 中,如果低概率 Journey 被激活(其激活条件 Guideline 匹配成功)
  • 需要加载该 Journey 相关的 Guidelines 并重新匹配

过程

  • 加载新激活 Journey 相关的 Guidelines
  • 进行额外的 LLM 匹配
  • 确保不遗漏相关行为
  • 合并到主匹配结果中

为什么需要

  • 初始过滤时,低概率 Journey 的 Guidelines 被过滤掉了
  • 但如果这些 Journey 的激活条件匹配成功,说明它们实际上是相关的
  • 需要补充匹配这些 Journey 的 Guidelines,确保完整的行为覆盖

3.4 匹配与激活机制

3.4 匹配与激活机制

3.4.1 Guidelines 匹配机制

匹配过程

  1. 批次分类

    # 系统自动将 Guidelines 分类为不同批次
    observational_guidelines = [g for g in guidelines if not g.content.action]
    actionable_guidelines = [g for g in guidelines if g.content.action]
    previously_applied_guidelines = [g for g in guidelines if g.id in applied_ids]
    customer_dependent_guidelines = [g for g in guidelines if g.metadata.get("customer_dependent")]
    
  2. LLM 评估

    # 对每个批次进行评估
    for batch in batches:
        匹配结果 = LLM评估(batch, 当前对话上下文)
        # 返回每个 Guideline 的匹配结果(匹配/不匹配、分数、理由)
    
  3. 匹配结果

    • 每个 Guideline 的匹配状态(匹配/不匹配)
    • 匹配分数(0-10)
    • 匹配理由(为什么匹配或不匹配)

3.5 优化策略与效果

3.5.1 优化策略

核心策略:先过滤,后匹配

  1. 标签过滤

    • 只加载代理相关的 Guidelines
    • 减少需要处理的 Guidelines 数量
  2. 概率过滤

    • 使用向量搜索预测高概率 Journey
    • 过滤掉低概率 Journey 相关的 Guidelines
    • 大幅减少需要评估的 Guidelines 数量
  3. 批次处理

    • 将 Guidelines 分类为不同批次
    • 每个批次一起评估,减少 LLM 调用次数
  4. 智能回退

    • 如果低概率 Journey 被激活,补充匹配其 Guidelines
    • 确保不遗漏相关行为

3.5.2 优化效果

性能优化示例

假设有 1000 个 Guidelines:

阶段 1:标签过滤
→ 只加载当前代理相关的 Guidelines(例如:200 个)

阶段 2:概率过滤
→ 基于 Journey 概率进一步过滤(例如:50 个高概率 Guidelines)

阶段 3:初始匹配
→ 只对这 50 个 Guidelines 进行 LLM 评估

阶段 4:补充匹配(如果需要)
→ 如果低概率 Journey 被激活,再评估相关 Guidelines(例如:额外 20 个)

总计:只评估了 70/1000 = 7% 的 Guidelines

优化效果总结

  • 减少 LLM 调用:只评估相关 Guidelines,大幅减少成本(从 1000 个减少到 70 个)
  • 降低延迟:减少需要处理的 Guidelines 数量,提高响应速度
  • 提高准确性:专注于最相关的 Guidelines,减少噪音
  • 智能回退:如果低概率 Journey 被激活,自动补充匹配,确保完整性

3.6 迭代处理机制

3.6.1 多轮迭代

如果工具调用后需要重新评估,系统会进行多轮迭代:

第一次迭代:
→ 匹配 Guidelines → 调用工具 → 获取结果

第二次迭代(如果需要):
→ 基于工具结果重新评估 Guidelines
→ 可能激活新的 Journey
→ 可能匹配新的 Guidelines
→ 继续处理直到准备好响应

3.6.2 迭代条件

系统会在以下情况进行迭代:

  • 工具调用返回了新信息
  • 需要重新评估客户依赖型 Guidelines
  • Journey 状态发生变化
  • 需要获取更多信息才能生成响应

3.7 完整示例

3.7.1 场景:用户说"我想预订航班"

完整流程追踪

[阶段 1] 加载上下文
→ 会话历史:[](新会话)
→ 激活 Journey:无

[阶段 2] 获取可用 Journey
→ ["预订航班", "预订酒店", "查询天气"]

[阶段 3] 标签过滤
→ 加载 200 个 Guidelines(包括所有 Journey 的 Guidelines)

[阶段 4] 概率过滤
→ 无激活 Journey → 向量搜索
→ 查询:"我想预订航班"
→ 结果:JourneyA(预订航班)最相关
→ 高概率 Journey = [JourneyA]

[阶段 5] 过滤 Guidelines
→ 保留:JourneyA 的 Guidelines(50个)+ 全局 Guidelines(30个)
→ 过滤:JourneyB 和 JourneyC 的 Guidelines(120个)
→ 结果:80 个 Guidelines

[阶段 6] LLM 语义匹配
→ 评估 80 个 Guidelines
→ 匹配成功:
   - "客户想要预订航班"(JourneyA 的激活条件)✓
   - "询问客户偏好"(全局 Guideline)✓
   - ...(其他匹配的 Guidelines)
→ 结果:10 个 Guidelines 匹配

[阶段 7] 激活 Journey
→ "客户想要预订航班"Guideline 匹配 → 激活 JourneyA

[阶段 8] 补充匹配
→ JourneyA 被激活,加载 JourneyA 作用域的 Guidelines
→ 评估这些 Guidelines

[阶段 9] 关系解析
→ 加载依赖和优先级相关的 Guidelines

[阶段 10] Journey 状态选择
→ JourneyA 激活 → 选择初始状态
→ 状态:"询问目的地"
→ 生成状态 Guidelines

[阶段 11] 工具调用
→ 检查是否有工具关联的 Guidelines 需要调用
→ 无工具调用

[阶段 12] 生成响应
→ 使用所有匹配的 Guidelines 和 Journey 状态
→ 生成:"好的,我来帮您预订航班。请问您想去哪里?"

3.8 核心原则总结

3.8.1 原则 1:先过滤,后匹配

  • 不是评估所有 Guidelines,而是先过滤出相关的 Guidelines
  • 通过标签系统和概率过滤大幅减少需要评估的 Guidelines 数量

3.8.2 原则 2:先预测 Journey,再匹配 Guidelines,最后激活 Journey

不是先匹配 Journey 再匹配 Guidelines,实际流程是:

  1. 预测高概率 Journey(用于过滤 Guidelines,不是匹配 Journey)
  2. 过滤 Guidelines(基于高概率 Journey)
  3. 匹配 Guidelines(LLM 评估)
  4. 激活 Journey(基于匹配结果)

3.8.3 原则 3:匹配决定激活

  • Journey 的激活由其条件 Guidelines 的匹配结果决定
  • 如果 Journey 的激活条件 Guideline 匹配成功,Journey 被激活
  • 激活后,Journey 的状态会被投影为 Guidelines 供后续使用

3.8.4 原则 4:Guidelines 优先于 Journey 状态

  • Guidelines 被视为更具体的行为覆盖
  • 如果 Guideline 匹配,通常会优先于 Journey 状态
  • 允许在 Journey 中处理特殊情况

3.8.5 原则 5:迭代优化

  • 工具调用后可能触发重新评估
  • 确保基于最新信息做出决策

总结

本文档详细介绍了 Parlant 的三个核心概念:

  1. Guidelines(行为准则):用自然语言定义的上下文规则,指导代理在特定情况下的行为
  2. Journeys(对话流程):结构化的多步骤对话流程,引导客户完成特定目标
  3. Recall and Matching Logic(召回与匹配逻辑):确定使用哪些 Guidelines 和 Journeys 的完整流程

这三个概念共同构成了 Parlant 的行为建模框架,确保了 AI 代理能够可靠地遵循业务规则和预期行为。