LiteLLM BitBucket 提示词管理集成:用 BitBucket 仓库集中管理 .prompt 提示词(含源码级实现解析)
本文以 LiteLLM 的 BitBucket 提示词管理集成为主体,完整讲解如何在 BitBucket 仓库中用 .prompt 文件组织团队提示词、如何通过 litellm.completion() 和 Proxy Server 调用这些提示词,并结合仓库源码(bitbucket_client.py、bitbucket_prompt_manager.py)剖析模板沙箱渲染、消息解析与参数提取的底层机制。读完本文,你可以把团队提示词从散落的代码常量迁移到具备版本控制和访问控制的 BitBucket 仓库中,并通过 bitbucket/ 模型前缀透明地接入现有 LiteLLM 调用。
集成概览与模块构成
BitBucket 集成的目标非常明确:把 .prompt 文件放到 BitBucket 仓库里,让 LiteLLM 在运行时从仓库拉取、渲染并应用提示词,从而复用 BitBucket 自带的 workspace/仓库/分支权限体系和版本管理能力。
模块由三个文件组成(见 litellm/integrations/bitbucket/ 目录):
| 文件 | 职责 |
|---|---|
| bitbucket_client.py | BitBucketClient,封装 BitBucket REST API:文件拉取、目录列举、分支查询、连接测试,以及路径安全校验 |
| bitbucket_prompt_manager.py | BitBucketPromptManager / BitBucketTemplateManager,负责 YAML frontmatter 解析、Jinja2 沙箱模板渲染、把渲染结果解析成 chat messages、提取模型参数 |
| init.py | 导出 set_global_bitbucket_config 等公共 API,注册 prompt_initializer 到 prompt_initializer_registry(key 为 bitbucket,来自 init_prompts.py 中的 SupportedPromptIntegrations.BITBUCKET) |
BitBucketPromptManager 继承自 CustomPromptManagement,而后者进一步对接 PromptManagementBase 的 get_chat_completion_prompt 接口——这意味着提示词管理对 litellm.completion() 来说是一个标准的“前置钩子”,不需要修改任何现有调用签名。
快速开始
1. 在 BitBucket 中组织提示词仓库
在你的 BitBucket workspace 下创建仓库,按如下结构存放 .prompt 文件:
your-repo/
├── prompts/
│ ├── chat_assistant.prompt
│ ├── code_reviewer.prompt
│ └── data_analyst.prompt
2. 编写 .prompt 文件
以 prompts/chat_assistant.prompt 为例。文件由 YAML frontmatter(--- 包裹)和 模板正文 两部分组成:
---
model: gpt-4
temperature: 0.7
max_tokens: 150
input:
schema:
user_message: string
system_context?: string
---
{% if system_context %}System: {{system_context}}
{% endif %}User: {{user_message}}
frontmatter 中各字段的实际消费方式(可对照 bitbucket_prompt_manager.py 的 BitBucketPromptTemplate.__init__):
model:最终覆盖litellm_params["model"]的模型名;temperature、max_tokens、top_p、frequency_penalty、presence_penalty:被pre_call_hook显式白名单提取并合并进调用参数;input.schema:声明模板变量(?后缀表示可选),用于人工约定与校验;- 其余任意 key 会进入
optional_params,一并随 metadata 返回。
模板正文使用 Handlebars 风格分隔符:{{variable}}、{% block %}、{# comment #}。从源码看,这一套分隔符是通过配置 Jinja2 环境变量实现的(bitbucket_prompt_manager.py):
self.jinja_env = ImmutableSandboxedEnvironment(
loader=DictLoader({}),
autoescape=select_autoescape(["html", "xml"]),
variable_start_string="{{",
variable_end_string="}}",
block_start_string="{%",
block_end_string="%}",
comment_start_string="{#",
comment_end_string="#}",
)
3. 配置 BitBucket 访问
方式 A:Access Token(推荐)
import litellm
bitbucket_config = {
"workspace": "your-workspace",
"repository": "your-repo",
"access_token": "your-access-token",
"branch": "main", # 可选,默认 main
}
litellm.set_global_bitbucket_config(bitbucket_config)
方式 B:Basic 认证
import litellm
bitbucket_config = {
"workspace": "your-workspace",
"repository": "your-repo",
"username": "your-username",
"access_token": "your-app-password", # basic 认证使用 app password
"auth_method": "basic",
"branch": "main"
}
litellm.set_global_bitbucket_config(bitbucket_config)
set_global_bitbucket_config 在 litellm/init.py 中导出,本质是设置模块级全局变量 litellm.global_bitbucket_config。认证头的构造逻辑在 bitbucket_client.py:auth_method == "basic" 且有 username 时,把 username:app_password 做 base64 编码放入 Authorization: Basic ...;否则默认走 Authorization: Bearer <access_token>。
4. 在 LiteLLM 中调用
# 模型前缀 'bitbucket/' 告诉 LiteLLM 走 BitBucket 提示词管理
response = litellm.completion(
model="bitbucket/gpt-4", # 实际模型来自 .prompt 文件的 frontmatter
prompt_id="prompts/chat_assistant", # 提示词文件在仓库中的相对路径(不含 .prompt 后缀)
prompt_variables={
"user_message": "What is machine learning?",
"system_context": "You are a helpful AI tutor."
},
# 额外 messages 会追加在提示词渲染结果之后
messages=[{"role": "user", "content": "Please explain it simply."}]
)
print(response.choices[0].message.content)
模型名中的 bitbucket 前缀是集成名(integration_name 属性返回值,见 bitbucket_prompt_manager.py)。运行时,回调构建逻辑会按名字找到 BitBucket 管理器:litellm_logging.py 中 logging_integration == "bitbucket" 的分支会读取 litellm.global_bitbucket_config,若为空则抛出 BitBucket configuration not found. Please set litellm.global_bitbucket_config first.,否则单例化一个 BitBucketPromptManager 缓存进内存 logger 列表。
Proxy Server 配置
在 Proxy 场景下,global_bitbucket_config 写在 config.yaml 的 litellm_settings 中。Proxy 启动时会识别该 key 并调用 set_global_bitbucket_config(见 proxy_server.py)。
1. 创建 prompts/hello.prompt
---
model: gpt-4
temperature: 0.7
---
System: You are a helpful assistant.
User: {{user_message}}
2. 编写 config.yaml
model_list:
- model_name: my-bitbucket-model
litellm_params:
model: bitbucket/gpt-4
prompt_id: "prompts/hello"
api_key: os.environ/OPENAI_API_KEY
litellm_settings:
global_bitbucket_config:
workspace: "your-workspace"
repository: "your-repo"
access_token: "your-access-token"
branch: "main"
3. 启动 Proxy
litellm --config config.yaml --detailed_debug
4. 调用验证
curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
-d '{
"model": "my-bitbucket-model",
"messages": [{"role": "user", "content": "IGNORED"}],
"prompt_variables": {
"user_message": "What is the capital of France?"
}
}'
注意示例中 messages 的内容为 "IGNORED":因为当渲染结果能解析出带角色的消息时,pre_call_hook 会直接用提示词解析出的 messages 替换原有 messages(见下文实现解析),所以业务参数主要通过 prompt_variables 传入。
.prompt 文件格式详解
基本结构
---
# 模型配置
model: gpt-4
temperature: 0.7
max_tokens: 500
# 输入 schema(可选)
input:
schema:
user_message: string
system_context?: string
---
System: You are a helpful {{role}} assistant.
User: {{user_message}}
frontmatter 解析逻辑见 _parse_prompt_file:以 --- 拆分出 YAML 头与模板正文,优先用 yaml.safe_load 解析;若环境缺少 PyYAML 则退化为一个只支持简单 key: value 行的基础解析器 _parse_yaml_basic(支持 bool/int/float/str 推断)。
高级用法
多角色对话——正文中以 System: / User: / Assistant: 开头的行会被解析为对应角色的独立消息(解析实现见 _parse_prompt_to_messages,不区分大小写识别前缀,连续行归属同一消息,空行忽略;若整段都没有角色前缀,则整体作为一条 user 消息):
---
model: gpt-4
temperature: 0.3
---
System: You are a helpful coding assistant.
User: {{user_question}}
动态模型选择——model 字段本身也可以是模板变量,渲染后的 metadata 会再取 model 值写回 litellm_params:
---
model: "{{preferred_model}}" # 模型可以是变量
temperature: 0.7
---
System: You are a helpful assistant specialized in {{domain}}.
User: {{user_message}}
运行时机制:pre_call_hook 与参数提取
pre_call_hook(bitbucket_prompt_manager.py)是理解整个集成的关键,流程如下:
- 无
prompt_id时直接透传原 messages 与参数; - 通过
get_prompt_template拉取(BitBucketClient.get_file_content请求{base_url}/repositories/{workspace}/{repository}/src/{branch}/{prompt_id}.prompt)并渲染模板,同时取回 metadata; - 把渲染文本解析成 messages:解析成功则替换原 messages;解析不出角色则把渲染文本作为一条 user 消息前置;
- 用 frontmatter 的
model覆盖litellm_params["model"],并从白名单["temperature", "max_tokens", "top_p", "frequency_penalty", "presence_penalty"]中提取参数合并进litellm_params; - 任一环节异常只记录
verbose_proxy_logger错误并回退到原始 messages,不会让整次 LLM 调用失败——这是一个有意的容错设计。
此外,_compile_prompt_helper / async_compile_prompt_helper(bitbucket_prompt_manager.py)把同一套“拉取—渲染—解析—取参”流程封装为 PromptManagementClient 结构返回,供新版 PromptManagementBase.get_chat_completion_prompt 接口复用;异步版本因底层 HTTP 是同步客户端,直接委托同步实现。
API 参考
BitBucket 配置项
bitbucket_config = {
"workspace": str, # 必填:BitBucket workspace 名
"repository": str, # 必填:仓库名
"access_token": str, # 必填:access token 或 app password
"branch": str, # 可选:拉取分支,默认 "main"
"base_url": str, # 可选:自定义 BitBucket API URL
"auth_method": str, # 可选:"token" 或 "basic",默认 "token"
"username": str, # 可选:basic 认证用户名
}
BitBucketClient 构造函数会校验 workspace、repository、access_token 三者缺一不可,否则抛 ValueError(见 bitbucket_client.py)。
需要指出一个与文档描述不一致的实现细节:源码中 base_url 的读取写成了 config.get("", "https://api.bitbucket.org/2.0")(bitbucket_client.py)——取的是空字符串 key 而非 "base_url"。从源码结构看,当前版本传入 base_url 配置项不会生效,客户端实际总是请求 https://api.bitbucket.org/2.0;如果你依赖私有 BitBucket 实例(BitBucket Server/Data Center),这一点需要留意,可先通过 BitBucketClient.test_connection()(调用 GET /repositories/{workspace}/{repository})验证连通性。
客户端能力一览
BitBucketClient 除拉取文件外还提供:
list_files(directory_path, file_extension=".prompt"):列出目录下指定扩展名的文件(走src/目录枚举接口,过滤type == "commit_file"的条目);get_repository_info()/test_connection():仓库信息与连通性测试;get_branches():枚举分支(refs/branches);get_file_metadata(file_path):用Range: bytes=0-0请求仅取响应头,拿到content-type、content-length、last-modified,适合做缓存失效判断。
completion 调用参数
response = litellm.completion(
model="bitbucket/<base_model>", # 必填,如 bitbucket/gpt-4
prompt_id=str, # 必填,.prompt 文件路径(不含扩展名)
prompt_variables=dict, # 可选,模板渲染变量
bitbucket_config=dict, # 可选,未设全局配置时传入
messages=list, # 可选,附加消息
)
安全设计:沙箱模板与路径校验
提示词文件来自仓库,本质上是不可信输入,源码中做了两层针对性防护:
- Jinja2 沙箱:如前文配置所示,环境使用
ImmutableSandboxedEnvironment而非普通Environment。源码注释解释得很直白:拥有仓库写权限的人可以在.prompt里塞入能触达__class__.__init__.__globals__的 Jinja 语法,在普通环境下可演变为代理主机上的 RCE;沙箱会阻断这种属性遍历,同时保留正常的{{ var }}替换行为。 - 路径安全校验:
_sanitize_file_path(bitbucket_client.py)拒绝包含#、?的路径,拒绝出现..的路径穿越,并对每个路径段做 URL 编码,再拼进src/{branch}/{path}请求。
文件内容读取还兼容两种返回形态:content-type 为 text/* 时直接取文本;否则尝试把响应体按 base64 解码(BitBucket 对二进制文件的返回方式),解码失败再回退到 response.text。
HTTP 状态码被映射为可读异常:404 → 返回 None(文件不存在);403 → 抛出带 workspace/repository 名的 "Access denied" 提示;401 → "Authentication failed. Check your BitBucket access token and permissions."(见 bitbucket_client.py)。
团队级访问控制
BitBucket 自带的权限体系直接复用为提示词的访问控制:
- Workspace 级权限:控制对整个 workspace 的访问;
- 仓库级权限:控制对具体提示词仓库的访问;
- 分支级权限:通过分支保护规则隔离生产提示词;
- 用户与群组管理:为团队成员分配不同访问级别。
落地建议(与 README 建议一致):
- 按团队划分 workspace/仓库,例如
team-a-prompts/、team-b-prompts/、team-c-prompts/; - 仓库权限上:团队成员给只读、提示词维护者给写权限、生产分支启用保护规则;
- 每个团队使用独立的 access token,token 按仓库范围授权,敏感环境使用 app password 叠加 basic 认证。
其他安全要点:access token 应通过环境变量或密钥管理系统注入而非硬编码;利用 BitBucket 的审计日志追踪仓库访问。
错误处理与故障排查
常见问题与排查方向:
| 现象 | 排查点 |
|---|---|
| "Access denied" | 检查 token 对 workspace/repository 的 403 权限 |
| "Authentication failed" | 401:核对 access token / app password 是否有效 |
| "File not found" | 确认 .prompt 文件存在于配置的目标分支、路径与 prompt_id 一致(不含 .prompt 后缀) |
| 模板渲染错误 | 检查 Handlebars 风格语法({{ }}、{% %}、{# #})是否合法 |
调试模式:开启详细日志后,BitBucket 提示词调用会输出完整日志:
import litellm
litellm.set_verbose = True
response = litellm.completion(
model="bitbucket/gpt-4",
prompt_id="your_prompt",
prompt_variables={"key": "value"}
)
另有一个容易踩到的点:pre_call_hook 捕获异常后只记日志并回退,因此“调用成功但模型/参数不对”时,应同时检查 verbose_proxy_logger 输出,确认提示词渲染是否真的生效。
从文件式 Dotprompt 迁移
如果你的团队目前使用 dotprompt 集成(litellm/integrations/dotprompt/)管理本地 .prompt 文件,迁移到 BitBucket 的路径是:
- 把现有
.prompt文件上传到 BitBucket 仓库(目录结构可保持不变); - 把全局配置从本地路径换成
global_bitbucket_config(workspace/repository/access_token/branch); - 用 BitBucket 权限体系配置团队访问;
- 代码中把
dotprompt/模型前缀改为bitbucket/。
由于两者共用同一套 PromptManagementBase 接口与 Handlebars 风格模板约定,迁移主要影响的是提示词的来源与协作方式——获得版本历史、分支保护和团队权限,而调用侧代码几乎无需改动。
小结
LiteLLM 的 BitBucket 集成把提示词管理做成了对调用方近乎透明的前置层:bitbucket/<model> 前缀 + prompt_id + prompt_variables 三要素即可完成接入;YAML frontmatter 承载模型与参数,正文经 Jinja2 沙箱渲染后解析为标准 chat messages。Proxy 场景只需在 litellm_settings.global_bitbucket_config 中声明一次仓库与凭据。理解 bitbucket_client.py 的路径校验/认证映射与 bitbucket_prompt_manager.py 的 hook 回退语义,有助于在私有化部署和故障排查时快速定位问题。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00