Day07-电商小二-26-09-29

一、回忆

TaskHandler之步骤1:CommandProcessor:根据不同的Command设置state。
Flow数据模型task/flow/model。
输入之加载器loader:读取yaml文件,转为Flow对象
漏掉了的代码在加载器中把系统和用户合并
![[images/Pasted image 20260929083542.png]]
CommandProcessor组件设计图设计。
CommandProcessor之task/commands/processor处理命令。

二、内容

1.获取Command对象

2.使用CommandProcessor组件设置状态

3.使用FlowExecutor(双层循环设计)进行命令执行,并改state。

知识点1:策略模式(Strategy Pattern)

说明:

定义一系列算法(策略),把它们一个个封装起来,并且使它们可以互相替换。
Action 是策略接口,ActionResponse、ActionListen、LookupOrderStatusAction 是具体策略。ActionRunner 不需要知道具体执行的是哪个策略,统一调 run() 方法就行。

使用场景:

- 同一个动作有多种实现方式(比如回复用户有 static/rephrase/llm 三种模式)
- 需要在运行时动态切换算法
- 避免写一大堆 if-else 判断类型

代码复现:

# 策略接口
class Action(ABC):
    @abstractmethod
    async def run(self, state, args) -> ActionResult:
        pass

# 具体策略 1
class ActionResponse(Action):
    async def run(self, state, args): ...

# 具体策略 2
class LookupOrderStatusAction(Action):
    async def run(self, state, args): ...

# 上下文(使用策略的地方)
class ActionRunner:
    async def execute(self, action_call, state):
        action = self.registry.get(action_call.action_name)
        return await action.run(state, action_call.action_args)  # 统一调用

知识点2:注册表模式(Registry Pattern)

说明:

用一个字典(注册表)把 "名字" 和 "实现类" 映射起来,运行时根据名字动态查找实现。

使用场景:

  • 配置文件里只写字符串(比如 YAML 里写 action: action_lookup_order),运行时需要找到对应的类
  • 需要动态扩展,新增实现不用改核心代码
  • 插件化架构

代码复现:

class ActionRegistry:
    def __init__(self):
        self._actions = {}  # 注册表:name → 实例

    def register(self, action):
        self._actions[action.name] = action

    def get(self, action_name):
        return self._actions[action_name]  # 根据名字找实现

知识点3:(Dependency Injection)

说明:

把对象依赖的其他对象从外面传进来,而不是在内部自己创建。

使用场景:

  • 方便单元测试(可以传 mock 对象进去)
  • 解耦:使用方不关心依赖对象怎么创建的
  • 同一个依赖可以被多个对象共享

代码复现:

class ActionRunner:
    def __init__(self, action_registry: ActionRegistry):
        self.action_registry = action_registry  # 从外面传进来,不是自己 new

# 使用的时候
registry = ActionRegistry()
runner = ActionRunner(registry)  # 注入依赖

知识 4:动态导入 + 反射(Dynamic Import + Reflection)

说明:

在运行时动态导入模块、获取模块里的类、实例化对象,而不是在代码顶部写死 import。

使用场景:

  • 自动扫描某个目录下的所有插件并加载
  • 插件化架构,新增插件不用改主代码
  • 框架开发(比如 Django 的 app 自动发现)

代码复现:

import importlib
import inspect
import pkgutil

def registry_custom_actions(registry):
    # 1. 动态导入包
    package = importlib.import_module("task.action.custom.sub_action")

    # 2. 遍历包下所有模块
    for __, name, is_pkg in pkgutil.iter_modules(package.__path__):
        module = importlib.import_module(name)  # 动态导入

        # 3. 反射:获取模块里所有类
        for __, obj in inspect.getmembers(module, inspect.isclass):
            if issubclass(obj, Action) and obj != Action:
                registry.register(obj())  # 动态实例化并注册

知识 5:Jinja2 模板渲染

说明:

用模板引擎把字符串里的 {{变量}} 替换成实际值。

使用场景:

  • 动态生成文本(邮件、消息、配置文件)
  • 模板和数据分离,方便修改文案
  • 支持条件、循环等复杂逻辑

代码复现:

from jinja2 import Template

text = "您的订单号是 {{ slots.order_number }},状态是 {{ slots.order_status }}"
data = {
    "slots": {"order_number": "A123", "order_status": "已发货"}
}
rendered = Template(text).render(data)
# 结果:"您的订单号是 A123,状态是 已发货"

知识 6:LangChain LCEL 链式调用

说明:

LangChain Expression Language,用 | 符号把多个组件串成一条链,数据从左到右流动。

使用场景:

  • LLM 应用开发:prompt → LLM → 输出解析
  • 快速搭建处理管道
  • 每个组件可以单独替换

代码复现:

from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = PromptTemplate.from_template(prompt_text, template_format="jinja2")
chain = prompt | llm_client | StrOutputParser()  # 链式组合
result = chain.invoke({"history": "...", "user_message": "..."})
# 数据流向:字典 → prompt 渲染 → LLM 生成 → 解析成字符串

知识 7:异步编程(async/await)

说明:

用 async def 定义异步函数,用 await 等待异步操作(比如 HTTP 请求、LLM 调用),等待期间可以去做别的事,不阻塞线程。

使用场景:

  • I/O 密集型操作(HTTP 请求、数据库查询、文件读写)
  • 同时处理多个请求
  • Web 框架(FastAPI 原生支持 async)

代码复现:

class LookupOrderStatusAction(Action):
    async def run(self, state, args) -> ActionResult:  # async 定义
        response = await http_client.get(url)  # await 等待,不阻塞
        dict_data = response.json()
        return ActionResult(...)

# 调用的时候也要 await
result = await action.run(state, args)

知识 8:抽象基类(ABC + abstractmethod)

说明:用 ABC 做父类,用 @abstractmethod 标记抽象方法,子类必须实现这些方法,否则不能实例化。
使用场景:

  • 定义接口规范,强制子类实现某些方法
  • 防止直接实例化基类
  • 多态:统一调用接口,具体实现由子类决定
    代码复习:
from abc import ABC, abstractmethod

class Action(ABC):
    @abstractmethod
    async def run(self, state, args) -> ActionResult:
        pass  # 子类必须实现这个方法,否则实例化报错

class MyAction(Action):
    async def run(self, state, args):  # 必须实现
        return ActionResult()

知识 9:dataclass (slots=True)

说明:@dataclass 自动生成 __init__、__repr__ 等方法;slots=True 限制实例只能有定义过的属性,更省内存、访问更快。
使用场景:

  • 纯数据容器类(只有属性,没有复杂方法)
  • 需要创建大量实例(比如几百个 FlowStep)
  • 想要类型安全,防止乱加属性
    代码复现:
@dataclass(slots=True)
class ActionResult:
    message: List[BotMessage] = field(default_factory=list)
    slot_updates: Dict[str, Any] = field(default_factory=dict)

# 不用写 __init__,直接 ActionResult(message=[], slot_updates={})

知识 10:假数据过渡(Mock Data)

说明:在外部接口还没准备好的时候,先用写死的假数据让流程跑通,后面再换成真实接口调用。
使用场景:

  • 前后端并行开发,后端接口还没好
  • 测试的时候不想真的调外部接口
  • 演示的时候用固定数据,结果可控
    代码复现:
# 现在:假数据
class LookupOrderLogisticsAction(Action):
    async def run(self, state, args):
        return ActionResult(
            slot_updates={
                "logistics_company": "顺丰快递",      # 写死的
                "tracking_number": "LH123546789",     # 写死的
                "logistics_status": "已签收",          # 写死的
            }
        )

# 以后:换成真实接口
# response = await http_client.get(f".../logistics/{order_number}")
# return ActionResult(slot_updates=response.json())

三、总结

数据流转图

流程配置(YAML)里某一步配置了 action: action_lookup_order_status
    ↓
引擎拿到这一步的 action_name 和 args
    ↓
构造 ActionCall(action_name="action_lookup_order_status", args={...})
    ↓
ActionRunner.execute(action_call, state)
    ↓
    ├─ registry.get("action_lookup_order_status")
    │   ↓ 找到 LookupOrderStatusAction 实例
    │
    └─ action.run(state, args)
        ↓
        ├─ 从 state.active_task.slots 拿参数(订单号)
        ├─ 调外部接口查数据
        └─ 返回 ActionResult(message=[], slot_updates={"order_status": "已发货"})
    ↓
引擎拿到 ActionResult
    ↓
    ├─ 把 message 里的 BotMessage 发给用户
    └─ 把 slot_updates 合并到 state.active_task.slots

ProcessOn绘图

暂时显示不了

四、安排

posted @ 2026-09-29 21:19  琳浪  阅读(6)  评论(0)    收藏  举报