Phase 3:Flows 接管网页文字路径(默认仍 legacy)
按 P1-Pipecat Flows Agent化与记忆改造交接.md 的 Phase 3 完成。 **语音路径保持 legacy**,文字路径可由 AI_AGENT_RUNTIME=flows 切换。 服务器实测通过工具、姓名、记忆三项验收,随后按文档要求改回 legacy。 新增 app/agent/flows_runtime.py - FlowsBridge:LLM 文本帧 → RTVI 消息 + 分句 TTS,产出与 legacy 的 _run_turn 一致(bot-llm-started → 逐 token bot-llm-text → stopped → bot-transcription / bot-output) - UserTextRecorder:截下用户原话供策略层判定语义等级 - build_flows_parts / make_flow_manager:拆成两段,解决 "FlowManager 要 worker、worker 要等管线" 的先后依赖 - 节点只用 task_messages,**不用 role_message**(避免两条 system 消息) - respond_immediately=False(不自动开口) - handler 用闭包捕获 ctx_factory,不用模块级全局(并发会话会串身份) 新增 app/agent/llm_service.py - 固化 Phase 1 spike 的选型:复用 DeepSeekLLMService,显式覆盖模型名 (Pipecat 默认的 deepseek-v4-flash 不存在) 配置 app/config.py - 新增 AgentConfig + AI_AGENT_RUNTIME,**默认 legacy** 改造 services/bot_server.py - EngineProcessor 加 owns_text:flows 模式下语音仍归它,文字帧往下传 - runtime_deps() 暴露 memory/identity/session/tts,两条路径共用同一份资源 (避免各建一份 SQLite 连接或各加载一次 TTS) - **_run_task 改用 PipelineRunner**:直接 task.run() 时 runner 没挂上, Flows 的工具执行会失败("runner is not set; call attach() first"), 异常被转成给 LLM 的通用错误串,默认日志看不到。这正是 Phase 1 spike 记录的坑 2,集成时又踩了一次。 新增 app/tests/test_flows_runtime.py(22 项,不依赖网络) 新增 deploy.sh - **不用 --delete**、一次只同步一个目录、显式排除清单。 起因见下。 客户端 client/test_client.py 加 --text 模式(RTVI send-text) - 信封字段实测校正两次:**是 id 不是 msg_id**,**data.content 不是 text**; 写错会被服务端以 "Invalid RTVI transport message" 拒掉且客户端无感知 - 等待条件改为"等到非静音音频",不能数音频块——WebRTC 空闲时也在推静音 服务器实测(2026-09-16) - legacy 文字:11.2s 音频 / 25 个有声段 - flows 文字:9.3s 音频 / 18 个有声段,首音约 2.8s - flows 记忆闭环:只说"记住我不吃香菜" → 工具写入 confirmed 记忆 → 数据库确认 #4 [confirmed] preference 饮食偏好=不吃香菜 - 称呼"林"从档案恢复 已知问题(留给 Phase 4/7) - Pipecat 报 "Both system_instruction and an initial system message in context are set" 警告。来源未查明(我只用了 task_messages)。 切回本地 vLLM 时可能 400,因为该后端拒绝多条 system 消息。 - Flows 路径暂不暴露 web_search(交接文档要求分阶段,搜索仍走 legacy)
This commit is contained in:
@@ -212,6 +212,23 @@ class MemoryConfig:
|
||||
|
||||
# ---------------------------------------------------------------- 汇总
|
||||
|
||||
@dataclass
|
||||
class AgentConfig:
|
||||
"""Agent 运行时的选择。
|
||||
|
||||
Phase 3 起有两条路径,**默认必须是 legacy**——交接文档要求
|
||||
"flows 默认值只有在线上验收后才能从 legacy 改为 flows"。
|
||||
"""
|
||||
|
||||
#: "legacy" = ConversationEngine 手写工具循环(当前线上行为)
|
||||
#: "flows" = Pipecat Flows 驱动文字路径(Phase 3 新路径)
|
||||
runtime: str = field(default_factory=lambda: _env("AI_AGENT_RUNTIME", "legacy"))
|
||||
|
||||
@property
|
||||
def use_flows(self) -> bool:
|
||||
return self.runtime.strip().lower() == "flows"
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppConfig:
|
||||
# 单用户原型的稳定内部档案 ID。它只用于数据隔离,绝不是用户可见姓名。
|
||||
@@ -228,6 +245,7 @@ class AppConfig:
|
||||
search: SearchConfig = field(default_factory=SearchConfig)
|
||||
voice: VoiceConfig = field(default_factory=VoiceConfig)
|
||||
memory: MemoryConfig = field(default_factory=MemoryConfig)
|
||||
agent: AgentConfig = field(default_factory=AgentConfig)
|
||||
|
||||
@classmethod
|
||||
def load(cls) -> "AppConfig":
|
||||
|
||||
Reference in New Issue
Block a user