【用DeepWiki学源码】Serena模板模块实现机制

底层:基础模板类
1. JinjaTemplate
目的: 封装 Jinja2 模板引擎,提供模板渲染和参数提取功能。
关键方法:
-
render(**params): 使用提供的参数渲染模板,返回字符串 -
get_parameters(): 提取模板中的变量参数列表(从{{ }}中推断)
2. PromptTemplate
目的: 表示单个提示模板,为 JinjaTemplate 添加名称标识。
功能:
-
实现
ParameterizedTemplateInterface接口 -
存储模板名称和 Jinja 模板字符串
-
在初始化时自动去除模板字符串的首尾空白
关键方法:
-
__init__(name, jinja_template_string): 创建模板实例,初始化_jinja_template -
render(**params): 委托给内部_jinja_template.render() -
get_parameters(): 返回模板参数列表
3. PromptList
目的: 表示提示列表,将字符串列表格式化为可读的项目符号格式。
功能:
-
存储字符串列表,自动去除每项的首尾空白
-
提供格式化输出功能
关键方法:
-
__init__(items): 初始化列表,对每项执行strip() -
to_string(): 格式化为带缩进的项目符号列表,处理多行项目的缩进对齐
中层:多语言容器
4. LanguageFallbackMode (枚举)
目的: 定义当请求的语言不存在时的回退策略。
三种模式:
-
ANY: 返回任意语言(第一个找到的) -
EXCEPTION: 抛出 KeyError 异常 -
USE_DEFAULT_LANG: 回退到默认语言
5. _MultiLangContainer[T]
目的: 泛型容器基类,管理同一语义实体的多语言版本。
数据结构:
-
_lang2item: dict[str, T]: 语言代码到项目的映射
关键方法:
-
add_item(item, lang_code, allow_overwrite): 添加特定语言的项目,默认不允许覆盖 -
has_item(lang_code): 检查是否存在指定语言的项目 -
get_item(lang, fallback_mode): 获取项目,支持回退策略。实现逻辑:先尝试直接获取,失败后根据fallback_mode决定是抛异常、返回任意语言或使用默认语言
6. MultiLangPromptTemplate
目的: 管理单个提示模板的多语言版本,确保所有语言版本参数一致。
功能:
-
实现
ParameterizedTemplateInterface接口 -
内部使用
_MultiLangContainer[PromptTemplate]存储不同语言版本 -
参数一致性验证
关键方法:
-
add_prompt_template(prompt_template, lang_code, allow_overwrite): 添加新语言版本。如果已有其他语言版本,会验证参数是否一致,不一致则抛出 ValueError -
get_prompt_template(lang_code, fallback_mode): 获取指定语言的PromptTemplate -
get_parameters(): 返回模板参数列表(从第一个语言版本获取,因为所有版本参数相同) -
render(params, lang_code, fallback_mode): 渲染指定语言的模板
7. MultiLangPromptList
目的: 管理提示列表的多语言版本。
实现: 直接继承 _MultiLangContainer[PromptList],无额外逻辑。
上层:集合管理
8. MultiLangPromptCollection
目的: 主要管理类,从 YAML 文件加载并管理所有提示模板和列表。
数据结构:
-
_multi_lang_prompt_templates: dict[str, MultiLangPromptTemplate]: 模板名称到多语言模板的映射 -
_multi_lang_prompt_lists: dict[str, MultiLangPromptList]: 列表名称到多语言列表的映射
关键方法:
初始化和加载:
-
__init__(prompts_dir, fallback_mode): 支持单个或多个目录,实现优先级加载机制。第一个目录优先级最高,后续目录的同名提示被忽略 -
_load_from_disc(prompts_dir, on_name_collision): 从目录加载所有.yml/.yaml文件,解析 YAML 并根据值类型(字符串或列表)调用_add_prompt_template或_add_prompt_list
添加方法:
-
_add_prompt_template(name, template_str, lang_code, on_name_collision): 创建PromptTemplate,添加到对应的MultiLangPromptTemplate。如果模板名称不存在,先创建MultiLangPromptTemplate -
_add_prompt_list(name, prompt_list, lang_code, on_name_collision): 类似逻辑,处理提示列表
查询方法:
-
get_prompt_template_names(): 返回所有模板名称列表 -
get_prompt_list_names(): 返回所有列表名称 -
get_prompt_template(prompt_name, lang_code): 获取指定模板的PromptTemplate对象 -
get_prompt_template_parameters(prompt_name): 获取模板参数列表 -
get_prompt_list(prompt_name, lang_code): 获取指定列表的PromptList对象
渲染方法:
-
render_prompt_template(prompt_name, params, lang_code): 直接渲染模板并返回字符串,是最常用的方法
工厂层
9. PromptFactoryBase
目的: 提示工厂基类,为子类提供模板渲染和列表获取的统一接口。
数据结构:
-
lang_code: 存储的语言代码 -
_prompt_collection:MultiLangPromptCollection实例
关键方法:
-
__init__(prompts_dir, lang_code, fallback_mode): 创建MultiLangPromptCollection实例 -
_render_prompt(prompt_name, params): 受保护方法,删除params中的self键(避免将实例传递给模板),然后委托给_prompt_collection.render_prompt_template() -
_get_prompt_list(prompt_name): 受保护方法,委托给_prompt_collection.get_prompt_list()
10. autogenerate_prompt_factory_module (函数)
目的: 代码生成函数,扫描提示目录并生成 PromptFactory 类。
实现流程:
-
创建临时
MultiLangPromptCollection扫描提示 -
为每个模板生成
create_<name>方法,包含参数签名 -
为每个列表生成
get_list_<name>方法 -
写入生成的 Python 代码到目标文件
11. PromptFactory (自动生成)
目的: 自动生成的类,为每个提示模板和列表提供类型安全的方法。
示例方法:
-
create_onboarding_prompt(*, system): 调用_render_prompt('onboarding_prompt', locals()) -
create_system_prompt(*, available_markers, available_tools, ...): 带多个参数的模板渲染<cite repo=”oraios/serena
夜雨聆风
