mirror of
https://github.com/Zippland/Bubbles.git
synced 2026-01-19 09:41:17 +08:00
6.1 KiB
6.1 KiB
Function Call 架构改进指南
1. 架构一致性如何提升?
- 现状诊断
function_calls/handlers.py:147等 handler 仍依赖commands/handlers.py中的正则/自然语言逻辑,导致新旧两套体系并存,增大耦合与维护成本。function_calls/router.py:41的直接匹配逻辑与 LLM 选择逻辑混在一起,职责边界不清晰;而参数校验仅在 LLM 分支执行(function_calls/router.py:150)。- 业务逻辑散落:例如新闻查询同时存在
_get_news_info与命令 handler 版本,缺少统一 service 层。
- 建议的架构骨架
- 分层组织代码:
function_calls/spec.py:仅保留数据结构。function_calls/registry.py:集中注册所有函数。function_calls/router.py:只负责入口路由、模型互操作和参数校验。function_calls/services/(新增目录):存放与业务相关的纯函数(例如天气、提醒、骂人等),对外只接受结构化参数。function_calls/handlers/(可拆分模块):每个 handler 只做 (ctx, args) → 调 service → 组装 FunctionResult。
- 统一入口:
robot.py只初始化FunctionCallRouter,其余旧路由移除,避免双写状态。 - 约束约定:所有 handler 必须声明
args的 Pydantic 模型;禁止 handler 内再次解析自然语言或修改ctx.msg.content。 - 配置与日志:为
FunctionCallRouter添加统一的日志上下文(如 request_id),方便追踪函数调用链路。
- 分层组织代码:
2. 如何让 function 直接填参数、避免重复 LLM 解析?
- 目标:路由阶段完成所有参数解析与校验,handler 全部消费结构化
args,不再依赖二次自然语言处理。 - 改造步骤:
- 强制类型转换:在
_create_args_instance后无论 direct/LLM 均执行args_type.model_validate,并捕获校验错误,向用户返回提示。 - 拆分提醒业务逻辑:
- 编写
function_calls/services/reminder.py,内含create_reminder(ctx, ReminderArgs)等函数,直接调用ctx.robot.reminder_manager。 - 调整
ReminderArgs为真正的结构字段(例如schedule: ScheduleArgs),由 FunctionCallRouter/LLM 负责生成 JSON。 - 对 direct 命令的需求,若要保留,可做一个轻量的自然语言 →
ReminderArgs的解析器,独立成 util,避免回写ctx.msg.content。
- 编写
- 其他 handler 同理:
insult:直接调用封装好的generate_random_insult(target_user),不要构造 fake match(见function_calls/handlers.py:216)。perplexity_search:把query直接传给 service,service 负责与ctx.robot.perplexity交互。
- 落地校验:在
function_calls/llm.py:123的决策结果里,若 schema 校验不通过,构建友好错误提示并返回FunctionResult。 - 测试:为每个 handler 编写参数驱动的单测,例如
test_reminder_set_structured_args→ 传ReminderArgs(time_spec="2024-05-01 15:00", content="开会"),验证生成的提醒记录。
- 强制类型转换:在
3. 目前改造是否达标?还需修什么?
- 仍存在的问题:
- Handler 依赖旧命令逻辑(
function_calls/handlers.py:147,:189,:216),说明“function 直接消费参数”的目标尚未实现。 - 直接命令分支跳过
validate_arguments,导致ReminderArgs这类模型的必填字段不会被校验(function_calls/router.py:118)。 FunctionResult.at_list虽已在_execute_function中 join,但数据类仍声明为list[str]。如果后续有人直接调用ctx.send_text(message, result.at_list)将再次踩坑。建议统一改成str或封装发送逻辑。- 仍缺少针对新路由的自动化测试,仅有打印式脚本(
test_function_calls.py)。建议补充 pytest 单测或集成测试。
- Handler 依赖旧命令逻辑(
- 建议修复顺序:
- 清理 handler 对旧命令的依赖,迁移业务逻辑到 service 层。
- 在 router 中统一调用
validate_arguments→_create_args_instance→ handler。 - 更新
FunctionResult类型定义,提供 helper 方法如result.send_via(ctx)集中处理消息发送。 - 编写覆盖天气/新闻/提醒/骂人等核心流程的单测,确保 Function Call 路径稳定。
4. 只保留 Function Call,旧路由是否移除到位?
- 现状:
robot.py:172-272仍初始化并调用CommandRouter、ai_router,函数回退逻辑依旧存在。 - 移除建议:
- 删除
self.command_router = CommandRouter(...)及相关 import;同时移除CommandRouter.dispatch调用与辅助日志。 - 移除
ai_router回退逻辑和配置项FUNCTION_CALL_ROUTER.fallback_to_legacy。确保配置文件同步更新(config.yaml.template:151)。 - 将闲聊 fallback 改为:当
FunctionCallRouter返回False时直接走handle_chitchat,并记录原因日志。 - 清理不再使用的命令注册表与正则代码(
commands/registry.py、commands/router.py等),确认没有别的模块引用后可删。 - 回归测试:运行原有功能用例,确保删除旧路由不会影响提醒、天气等功能;同时观察日志,确认不再出现“命令路由器”相关输出。
- 删除
5. 推荐行动清单(按优先级)
- 剥离 handler 对旧命令体系的依赖:完成 service 层拆分,更新所有 handler 为结构化实现。
- 统一参数校验与错误返回:调整 router 逻辑,新增校验失败提示,并完善
FunctionResult类型。 - 移除旧路由与配置:清理
robot.py中的命令/AI 路由初始化与 fallback,更新配置模板。 - 补全测试:为 Function Call 核心流程编写 pytest 单元与集成测试,覆盖 direct/LLM 两条路径。
- 整理文档:更新开发文档,说明如何新增 function、如何编写参数模型与 service,确保团队成员按统一规范扩展功能。
执行完以上步骤后,你将拥有一套纯 Function Call、结构化且易维护的机器人指令体系,满足题述的四个目标。