以下内容基本上是 AI 生成的,我还没校对,可能质量不高
promplate-recipes - 最佳实践合集¶
GitHub · PyPI · Core Framework
为 Promplate 框架量身打造的实用工具集,收录我在生产实践中总结的最佳实践。由于这些实现较为 opinionated,我选择独立维护而非并入核心包。
其实一开始我也没想过要单独发包的,但用着用着发现很多模式反复出现,就干脆整理出来了。
核心架构:分层上下文管理¶
项目最核心的创新是 SafeChainMapContext 的三层架构设计:
# BuiltinsLayer -> ComponentsLayer -> defaultdict(SilentBox)
context = SafeChainMapContext()
第一层:BuiltinsLayer¶
注入内置函数和常量,提供模板渲染的基础环境。
第二层:ComponentsLayer¶
动态组件加载系统,通过文件系统 glob 模式自动发现组件,实现组件的惰性加载。
这个设计灵感来自于我写 HMR 时对模块加载的思考:为什么不让组件也像模块一样自动发现呢?
第三层:SilentBox 智能缺省¶
自定义 Box 子类,空对象访问返回空字符串而非异常,调试模式下打印访问信息。
调试模板时最烦的就是各种 KeyError,这个小改动让开发体验好太多。
组件系统¶
Recipes 提供了两种组件实现模式:
SimpleComponent - 参数自适应
@component
def my_component():
# 无参调用:直接执行
return "result"
@component
def smart_component(context):
# 有参调用:传入上下文
return context.get("data", "default")
CallableWrapper - 元编程命名
class MyWrapper(CallableWrapper, AutoNaming):
# 自动将函数名作为模板名
pass
关键实现细节¶
惰性组件加载¶
ComponentsLayer 通过文件系统扫描实现组件的按需加载:
# 仅在首次访问时加载组件
components = ComponentsLayer("components/*.py")
result = components.my_component # 触发加载
流式节点处理¶
SimpleNode 支持生成器和异步生成器,实现中间结果的实时回调:
@node
def streaming_node():
for chunk in generate_chunks():
yield chunk # 实时输出
这个设计让我想起写 HMR 时处理异步更新的那些夜晚,同样的模式在不同场景下居然都适用。
DotTemplate 增强¶
继承自标准 Template,自动注入多层上下文:
template = DotTemplate("Hello {{name}} from {{component.helper}}")
result = template.render(context)
我干了些什么骚操作¶
- 分层隔离:三层上下文设计既保证了灵活性又维护了安全性
- 智能缺省:
SilentBox让模板编写更加宽容,减少调试时间 - 动态发现:组件通过文件系统 glob 自动注册,无需手动配置
- 参数自适应:组件根据调用方式自动调整行为
- 元编程命名:利用
AutoNaming自动生成有意义的标识符
其实很多想法都来自于我在实际项目中遇到的痛点,比如 Free Chat 的模板渲染和 Papers 的组件管理。
生产应用¶
Recipes 中的工具已经在多个项目中得到验证:
- Promplate 生态:作为核心框架的官方配套工具集
- Free Chat:LLM 聊天界面,使用 recipes 的上下文管理
- Papers:PDF 处理工作流,采用 recipes 的组件架构
相关项目¶
- promplate/core - 核心框架,支持 Jinja2 + 聊天标记 + FSM
- free-chat - LLM 聊天 UI,基于 SolidJS + Astro
- papers - PDF 嵌入工作流,RAG 实现
设计理念
Recipes 代表了我对框架设计的思考:实用主义优先。与其追求完美的抽象,不如从实际问题出发,提供可立即使用的解决方案。
说到底,编程就是解决问题嘛。