docs(prompt): 添加提示词工程方案设计文档

This commit is contained in:
徐涛
2026-06-02 22:19:15 +08:00
parent ea1e5c7f7e
commit 7f5513adf3
+611
View File
@@ -0,0 +1,611 @@
# Phase 1: Prompt Engineering — 方案设计
> 定稿日期:2026-06-02
## 背景与目标
AG Core Phase 0Foundation)已完成 LLM 调用周期的全部基础设施。Phase 1 的目标是补齐**提示词工程**能力,提供提示词的组合、模板化与优化能力,使其能直接服务于 Phase 2(工具系统)和 Phase 4Agent 运行时)。
**目标**:实现 `PromptTemplate`(模板引擎)和 `PromptComposer`(提示词组合器)两个核心组件,覆盖变量插值、条件渲染、多消息序列拼接等场景。
---
## 需求分析
### 功能需求
| 模块 | 需求 | 验收条件 |
|------|------|---------|
| `PromptTemplate` | 支持变量插值 `{{ var }}` | 渲染后正确替换所有变量 |
| `PromptTemplate` | 支持条件渲染 `{{#if var}}...{{/if}}` | 变量非空/非 false 时渲染块 |
| `PromptTemplate` | 支持列表循环 `{{#each list}}...{{/each}}` | 遍历渲染集合元素 |
| `PromptTemplate` | 支持嵌套模板引用 `{{> template_name }}` | 引用已注册的模板片段 |
| `PromptTemplate` | 支持部分转义(原样输出 `{{ literal }}` | 提供 raw block 语法 |
| `PromptComposer` | 按角色拼接 system/user/assistant 消息序列 | 输出 `Vec<OpenaiChatMessage>` |
| `PromptComposer` | 支持插入预编译的 PromptTemplate | 组合器中混合使用静态文本和模板 |
| `PromptComposer` | 支持从已有消息列表扩展 | 接收 `Vec<OpenaiChatMessage>` 作为初始状态 |
| `PromptComposer` | 支持多模态 ContentPart 构建(图片/音频/文件) | `user_content()`/`system_content()` 等接受 `OpenaiContentPart` |
| `PromptComposer` | 支持 Developer 角色(o1 系列模型) | `developer()` / `developer_template()` / `developer_content()` |
| `PromptComposer` | 支持 Tool 角色(工具执行结果回传) | `tool()` / `tool_content()` 接受 `tool_call_id` |
| `PromptComposer` | 支持 `name` 字段设置(多角色区分) | `with_name()` 链式方法为上一消息设置名称 |
| `PromptComposer` | 支持消息序列合法性验证 | `validate_messages()` 检查顺序约束与角色交替 |
| `PromptTemplateRegistry` | 模板注册表:按名称注册、查找、文件加载 | 从字符串/文件注册模板,按名称渲染 |
| `PromptTemplateRegistry` | 支持延迟编译模式 | `register_lazy()` 存储原始字符串,首次渲染时编译 |
| `PromptTemplate` | 实现 `Display` trait | 输出原始模板字符串(用于日志和调试) |
| `TemplateContext` | 支持从 JSON 构造 | `from_json()` 递归转换 `serde_json::Value``TemplateValue` |
### 非功能需求
- 所有公开 API 必须带 `///` 文档注释
- 无新增 `unwrap()` 调用
- **零运行时依赖**(不使用 tera、askama 等模板引擎 crate
- 模板引擎失败时返回结构化错误(`PromptError`
- 与现有 `OpenaiChatMessage` / `ChatRequest` 类型自然集成
---
## 方案设计
### 模块结构
```
src/
prompt.rs # prompt 模块根:声明子模块 + 重导出公共 API
prompt/
template.rs # PromptTemplate — 模板引擎
template/
registry.rs # PromptTemplateRegistry — 模板注册表
composer.rs # PromptComposer — 提示词组合器
error.rs # PromptError — 错误类型
```
`prompt.rs` 根模块声明:
```rust
// prompt.rs
pub mod composer;
pub mod error;
pub mod template;
pub use composer::PromptComposer;
pub use error::PromptError;
pub use template::{PromptTemplate, PromptTemplateRegistry};
```
`lib.rs` 添加:
```diff
pub mod llm;
+pub mod prompt;
```
### 1. 模板引擎选择:自建轻量
**决策**:不使用 `tera` / `askama` / `maud` / `minijinja` 等第三方模板 crate。
**理由**
- Phase 1 模板需求极其简单(变量插值 + 条件 + 列表循环),不需要 Jinja2/Handlebars 全能力
- 无依赖 = 编译更快、无安全面、版本冲突为 0
- 自建 50-80 行核心逻辑即可覆盖所有需求
- Roadmap 估算 400 行总代码,60 行模板引擎足够
**语法设计**(参考 Handlebars 子集):
```
{{ variable_name }} → 变量插值
{{#if var}}...{{/if}} → 条件渲染(var 存在且非空)
{{#if var}}...{{else}}...{{/if}} → 条件 + 否则
{{#each list}} {{item}} {{/each}} → 列表循环
{{#raw}} {{literal}} {{/raw}} → 原始块(不解析内部模板语法)
{{> template_name}} → 引用已注册的嵌套模板
```
### 2. PromptTemplate — 模板引擎
```rust
// prompt/template.rs
use std::collections::HashMap;
use crate::prompt::error::PromptError;
/// 渲染上下文中使用的值类型。
pub enum TemplateValue {
String(String),
Bool(bool),
Array(Vec<TemplateValue>),
Object(HashMap<String, TemplateValue>),
}
/// `TemplateValue` 自动转换(提升 `ctx.insert("name", "Alice")` 的易用性)。
impl From<String> for TemplateValue { ... }
impl From<&str> for TemplateValue { ... }
impl From<bool> for TemplateValue { ... }
/// 模板变量上下文。
pub struct TemplateContext {
vars: HashMap<String, TemplateValue>,
}
impl TemplateContext {
pub fn new() -> Self;
/// 插入变量(支持 `&str` / `String` / `bool` 自动转换)。
pub fn insert(&mut self, key: impl Into<String>, value: impl Into<TemplateValue>);
pub fn get(&self, key: &str) -> Option<&TemplateValue>;
/// 从 `serde_json::Value` 递归构造(支持嵌套 Object/Array)。
pub fn from_json(value: &serde_json::Value) -> Result<Self, PromptError>;
/// 从 `HashMap` 构造(适用于配置加载场景)。
pub fn from_map(map: HashMap<String, TemplateValue>) -> Self;
}
/// 预编译的模板。
pub struct PromptTemplate {
/// 原始模板字符串(用于 debug)。
raw: String,
/// 编译后的 AST 片段。
fragments: Vec<Fragment>,
}
/// 编译后的 AST 节点(内部枚举)。
enum Fragment {
Literal(String),
Variable { name: String },
If { condition: String, body: Vec<Fragment>, else_body: Vec<Fragment> },
Each { variable: String, body: Vec<Fragment> },
Raw(String),
Include(String),
}
impl PromptTemplate {
/// 从模板字符串编译。
pub fn compile(template: &str) -> Result<Self, PromptError>;
/// 使用上下文渲染。
pub fn render(&self, ctx: &TemplateContext) -> Result<String, PromptError>;
/// 注册可引用的子模板。
pub fn register_partial(&mut self, name: &str, template: PromptTemplate);
}
/// `Display` 输出原始模板字符串,便于日志和调试。
impl fmt::Display for PromptTemplate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.raw)
}
}
```
**编译流程**
1. 逐字符扫描模板字符串
2. 遇到 `{{` 时解析指令类型(variable/if/each/raw/include
3. 生成 `Fragment` AST 列表
4. 返回 `PromptTemplate { raw, fragments }`
**渲染流程**
1. 遍历 `fragments`
2. `Literal` → 直接追加
3. `Variable { name }``ctx.get(name)` → 追加字符串值
4. `If { condition, body, else_body }` → 判断 `ctx.get(condition)` 是否存在且真 → 递归渲染 body/else_body
5. `Each { variable, body }``ctx.get(variable)` 转为数组 → 为每个元素设置 `item` 变量 → 递归渲染 body
6. `Raw(text)` → 原样追加(不解析 `{{ }}`
7. `Include(name)` → 查找已注册的 partials → 递归渲染
### 3. PromptTemplateRegistry — 模板注册表
提供轻量的模板管理能力,按名称注册、查找、从文件加载,适用于管理多个 Agent 的提示词模板。
```rust
// prompt/template/registry.rs
use std::collections::HashMap;
use std::path::Path;
use crate::prompt::error::PromptError;
use crate::prompt::template::{PromptTemplate, TemplateContext};
/// 内部存储的模板(支持延迟编译)。
enum StoredTemplate {
Compiled(PromptTemplate),
Raw(String),
}
/// 模板注册表——管理多模板实例。
pub struct PromptTemplateRegistry {
templates: HashMap<String, StoredTemplate>,
}
impl PromptTemplateRegistry {
pub fn new() -> Self;
/// 从模板字符串编译并注册(立即编译)。
pub fn register(&mut self, name: &str, template: &str) -> Result<(), PromptError>;
/// 延迟编译注册:只存储原始字符串,首次渲染时编译。
/// 适合模板数量多但并非全部立即使用的场景。
pub fn register_lazy(&mut self, name: &str, template: &str);
/// 从文件读取并编译注册。
pub fn register_file(&mut self, name: &str, path: &Path) -> Result<(), PromptError>;
/// 获取已注册的模板(延迟编译的模板在此首次编译)。
pub fn get(&mut self, name: &str) -> Result<&PromptTemplate, PromptError>;
/// 按名称渲染(`{{> name }}` 引用时自动查找)。
pub fn render(&mut self, name: &str, ctx: &TemplateContext) -> Result<String, PromptError>;
}
```
**设计约束**
- 不设全局单例,用户自行创建和持有
- 不引入文件系统监听、热加载、序列化等复杂能力
- 注册表内部模板可用于解析 `{{> partial_name }}` 子模板引用
- 用户仍可单独持有 `PromptTemplate` 实例,不强制使用注册表
### 4. PromptComposer — 提示词组合器
```rust
// prompt/composer.rs
use crate::llm::types::message::{OpenaiChatMessage, OpenaiContentPart, ContentField};
use crate::prompt::error::PromptError;
use crate::prompt::template::{PromptTemplate, TemplateContext};
/// 提示词组合器——构建多角色消息序列。
pub struct PromptComposer {
messages: Vec<OpenaiChatMessage>,
pending_name: Option<String>,
}
impl PromptComposer {
/// 创建一个空的组合器。
pub fn new() -> Self;
/// 从已有的消息列表初始化。
pub fn from_messages(messages: Vec<OpenaiChatMessage>) -> Self;
// ===== 纯文本消息 =====
/// 添加一条纯文本 system 消息。
pub fn system(mut self, text: impl Into<String>) -> Self;
/// 添加一条纯文本 user 消息。
pub fn user(mut self, text: impl Into<String>) -> Self;
/// 添加一条纯文本 assistant 消息。
pub fn assistant(mut self, text: impl Into<String>) -> Self;
/// 添加一条纯文本 developer 消息(o1 系列模型使用)。
pub fn developer(mut self, text: impl Into<String>) -> Self;
/// 添加一条 Tool 消息(工具执行结果回传)。
pub fn tool(mut self, tool_call_id: impl Into<String>, content: impl Into<String>) -> Self;
// ===== 模板消息 =====
/// 使用模板和上下文渲染后添加为 user 消息。
pub fn user_template(
mut self,
template: &PromptTemplate,
ctx: &TemplateContext,
) -> Result<Self, PromptError>;
/// 使用模板和上下文渲染后添加为 system 消息。
pub fn system_template(
mut self,
template: &PromptTemplate,
ctx: &TemplateContext,
) -> Result<Self, PromptError>;
/// 使用模板和上下文渲染后添加为 assistant 消息。
pub fn assistant_template(
mut self,
template: &PromptTemplate,
ctx: &TemplateContext,
) -> Result<Self, PromptError>;
/// 使用模板和上下文渲染后添加为 developer 消息。
pub fn developer_template(
mut self,
template: &PromptTemplate,
ctx: &TemplateContext,
) -> Result<Self, PromptError>;
// ===== 多模态 ContentPart =====
/// 添加一条含指定 ContentPart 的 system 消息。
pub fn system_content(mut self, part: OpenaiContentPart) -> Self;
/// 添加一条含指定 ContentPart 的 user 消息。
pub fn user_content(mut self, part: OpenaiContentPart) -> Self;
/// 添加一条含指定 ContentPart 的 assistant 消息。
pub fn assistant_content(mut self, part: OpenaiContentPart) -> Self;
/// 添加一条含指定 ContentPart 的 developer 消息。
pub fn developer_content(mut self, part: OpenaiContentPart) -> Self;
/// 添加一条含指定 ContentPart 的 Tool 消息。
pub fn tool_content(mut self, tool_call_id: impl Into<String>, part: OpenaiContentPart) -> Self;
/// 批量添加 ContentPart 作为 user 消息。
pub fn user_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self;
/// 批量添加 ContentPart 作为 system 消息。
pub fn system_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self;
/// 批量添加 ContentPart 作为 assistant 消息。
pub fn assistant_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self;
/// 批量添加 ContentPart 作为 developer 消息。
pub fn developer_contents(mut self, parts: Vec<OpenaiContentPart>) -> Self;
// ===== 角色标识 =====
/// 为上一条添加的消息设置 `name` 字段(多 agent 系统中区分同角色实体)。
pub fn with_name(mut self, name: impl Into<String>) -> Self;
// ===== 构建 =====
/// 构建最终的消息列表。
pub fn build(self) -> Vec<OpenaiChatMessage>;
/// 构建并直接创建 ChatRequest(需搭配 model 参数)。
/// 返回的 `OpenaiChatRequest` 中 `tools`、`temperature`、`max_tokens` 等字段均为 `None`
/// 可通过结构体更新语法补全:`OpenaiChatRequest { tools: Some(...), ..req }`。
pub fn build_request(
self,
model: impl Into<String>,
) -> crate::llm::types::request::OpenaiChatRequest;
}
/// 验证消息序列是否符合 OpenAI API 要求。
/// 检查项:Tool 消息必须紧跟在匹配的 Assistant 消息后、角色交替规则等。
pub fn validate_messages(messages: &[OpenaiChatMessage]) -> Result<(), PromptError>;
```
**Builder 模式设计**
- `PromptComposer` 采用链式调用(builder pattern),与 Rust 生态的主流风格一致
- 每个 `system()` / `user()` / `assistant()` / `developer()` / `tool()` 方法返回 `Self`,支持连续调用
- `with_name()` 作用于上一条消息,内部通过 `pending_name: Option<String>` 暂存,push 消息时消费
- `build()` 返回 `Vec<OpenaiChatMessage>``build_request()` 创建完整的 `OpenaiChatRequest`
- **`ContentField` 类型约定**:所有纯文本消息(`system()` / `user()` / `assistant()` / `developer()`)统一使用 `ContentField::Array(vec![OpenaiContentPart::Text{...}])`,与 OpenAI API 非流式响应的标准格式一致
**`validate_messages()` 校验规则**
1. `Tool` 角色的消息必须跟在 `Assistant` 角色且含 `tool_calls` 的消息之后
2. 禁止连续出现两条同角色的非 Tool 消息(system 除外)
3. 消息列表不能为空
### 5. PromptError — 错误类型
```rust
// prompt/error.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum PromptError {
#[error("模板解析错误: {0}")]
Parse(String),
#[error("渲染错误: 变量 '{0}' 未找到")]
VariableNotFound(String),
#[error("渲染错误: 引用的子模板 '{0}' 未注册")]
PartialNotFound(String),
#[error("渲染错误: '{0}' 不是数组,无法遍历")]
NotAnArray(String),
#[error("渲染递归超过最大深度限制 ({0})")]
MaxDepthReached(u8),
#[error("渲染错误: {0}")]
Render(String),
#[error("消息序列校验失败: {0}")]
InvalidSequence(String),
#[error("文件读取错误: {0}")]
Io(#[from] std::io::Error),
}
```
### 6. 使用示例
```rust
use agcore::prompt::{PromptComposer, PromptTemplate, TemplateContext};
// 编译模板
let tpl = PromptTemplate::compile(
"你是{{role}}。请回答以下问题:\n{{#if context}}参考背景:{{context}}\n{{/if}}提问:{{question}}"
)?;
// 构建上下文
let mut ctx = TemplateContext::new();
ctx.insert("role", "资深工程师");
ctx.insert("question", "Rust 的所有权规则是什么?");
ctx.insert("context", "用户有 Java 背景");
// 使用组合器构建消息序列
let messages = PromptComposer::new()
.system("你是一个专业的 Rust 助手")
.user_template(&tpl, &ctx)?
.build();
// 可选:直接构建 ChatRequest
let request = PromptComposer::new()
.system("你是一个翻译助手")
.user("Hello, world!")
.build_request("gpt-4o");
```
### 7. 与 LlmCycle 的集成
`PromptComposer::build()` 输出 `Vec<OpenaiChatMessage>`,但 **`LlmCycle.messages` 是私有字段**,无法直接赋值。因此**需要对 `LlmCycle` 进行扩展**,提供消息注入入口,使 Composer 能和 `LlmCycle` 的多轮对话循环协同工作。
**方案**:在 `LlmCycle` 上新增 3 个方法:
```rust
// llm/cycle.rs
impl LlmCycle {
/// 直接设置消息历史(覆盖已有消息),支持 Builder 链式调用。
pub fn with_messages(mut self, messages: Vec<Message>) -> Self;
/// 追加消息到历史尾部。
pub fn extend_messages(&mut self, messages: Vec<Message>);
/// 使用预构建消息提交(跳过自动 push user prompt)。
/// 与 submit() 不同,不自动添加 user_text(prompt)。
pub async fn submit_messages(
&mut self,
messages: Vec<Message>,
tools: Vec<ToolDefinition>,
) -> Result<ChatResponse, LlmError>;
}
```
**`submit_messages()``submit()` 的区别**
| 维度 | `submit()` | `submit_messages()` |
|------|-----------|-------------------|
| 输入 | `prompt: String` + `tools` | `messages: Vec<Message>` + `tools` |
| 内部操作 | 自动 push `user_text(prompt)` | 不自动添加任何消息 |
| 适用场景 | 简单单轮对话 | 多轮/预构建消息序列 |
| system_prompt 处理 | 自动插入(如果无 System 消息) | 完全由调用方控制 |
**`system_prompt` 冲突处理**`submit_messages()` 关闭 `LlmCycle` 的自动 system prompt 插入逻辑,避免与 `PromptComposer` 已构建的 System/Developer 消息重复。由调用方全权控制消息序列内容。
**使用示例**
```rust
let messages = PromptComposer::new()
.system("你是一个专业的 Rust 助手")
.user_template(&query_tpl, &ctx)?
.build();
let mut cycle = LlmCycle::new(provider, config)
.with_messages(messages);
let resp = cycle.submit_messages(vec![], tools).await?;
```
`PromptComposer::build_request()` 直接创建 `OpenaiChatRequest`,可用于绕过 `LlmCycle` 直接调用 `LlmProvider` 的场景。
**注意**`PromptComposer` 模块 **不** 直接依赖 `LlmCycle`(避免 `prompt → cycle` 的强耦合)。集成方法全部在 `LlmCycle` 侧实现,保持单一职责。
---
## 实现计划
### Step 1: 创建方案文档
创建 `docs/4-prompt-engineering.md`(即本文档)。
### Step 2: PromptError
- 创建 `src/prompt/error.rs`
- 定义 `PromptError` 枚举(Parse / Render / VariableNotFound / PartialNotFound
### Step 3: PromptTemplate
- 创建 `src/prompt.rs`(模块根)
- 创建 `src/prompt/template.rs`
- 实现编译(`compile()`):逐字符扫描 → 生成 `Vec<Fragment>`
- 注意处理:嵌套 `#if` 栈匹配、`{{` 字面量转义、未闭合标签检测
- 实现渲染(`render()`):递归遍历 Fragment → 拼接字符串
- 定义非字符串值渲染格式:`Bool``"true"`/`"false"``Array`→JSON、`Object`→JSON
- 递归加深度限制(16 层)防止循环引用
- 支持功能:变量插值、条件渲染、列表循环、原始块、子模板引用
- 实现 `Display for PromptTemplate`(输出原始模板字符串)
- 编写 20+ 边界测试覆盖:嵌套 if/each、未闭合标签、空变量、空数组 each、递归深度超限
- 运行 `cargo test + cargo check` 验证
### Step 4: PromptTemplateRegistry
- 推荐选项:`template` 保持单文件,`PromptTemplateRegistry` 合并同文件(~40 行不值得单独目录)
- 内部存储使用 `StoredTemplate` 枚举(支持 `Compiled``Raw` 两种状态)
- 实现:`register()` 立即编译、`register_lazy()` 延迟编译、`register_file()` 文件加载
- 实现 `get()` / `render()`(延迟编译的模板首次渲染时编译)
- 运行 `cargo check` 验证
### Step 5: PromptComposer
- 创建 `src/prompt/composer.rs`
- 实现 Builder 链式 API,内部维护 `messages: Vec<OpenaiChatMessage>` + `pending_name: Option<String>`
- 角色方法:`system()` / `user()` / `assistant()` / `developer()` / `tool()`
- 纯文本消息统一使用 `ContentField::Array([Text])`
- `tool()` 需传入 `tool_call_id``content`
- 模板方法:`system_template()` / `user_template()` / `assistant_template()` / `developer_template()`
- 多模态方法:`*_content()`(单个 ContentPart)和 `*_contents()`(批量)
- `with_name()`:作用于上一条消息的 `name` 字段
- `build()` / `build_request()`
- `validate_messages()`:独立的纯函数,校验 Tool→Assistant 顺序、角色交替、非空
- 运行 `cargo check` 验证
### Step 6: LlmCycle 扩展
- `cycle.rs` 新增 3 个方法:
- `with_messages(self, messages: Vec<Message>) -> Self` — 链式设置消息历史
- `extend_messages(&mut self, messages: Vec<Message>)` — 追加消息
- `submit_messages(&mut self, messages: Vec<Message>, tools: Vec<ToolDefinition>) -> Result<...>` — 预构建消息提交
- `submit_messages()` 关闭自动 system_prompt 插入(避免与 Composer 的 System/Developer 消息重复)
- 运行 `cargo check` 验证
### Step 7: lib.rs 注册
- `lib.rs` 添加 `pub mod prompt;`
- 运行 `cargo check` 验证
### Step 8: 收尾
- `cargo clippy` — 无警告
- `cargo build` — 完整构建
- 检查所有新公开 API 有 `///` 文档注释
---
## 术语表
| 术语 | 说明 |
|------|------|
| `TemplateValue` | 模板渲染上下文中使用的值类型枚举 |
| `TemplateContext` | 模板变量上下文,持有所有变量 |
| `PromptTemplate` | 预编译的模板,持有 AST 片段列表 |
| `Fragment` | 编译后的 AST 节点(内部枚举) |
| `PromptComposer` | 提示词组合器,构建多角色消息序列 |
| `PromptError` | 提示词工程专属错误类型 |
---
## 风险评估
| 风险 | 概率 | 缓解措施 |
|------|------|---------|
| 模板语法不支持复杂场景(如嵌套 each) | 低 | 当前需求不涉及;后续可引入 tera 替换 |
| 自定义模板引擎解析有 bug | 中 | 编写单元测试覆盖所有语法分支 |
| 与未来 Prompt Optimizer 冲突 | 低 | PromptOptimizer 只修改模板/上下文,不改模板引擎接口 |
| 条件语义不明确(什么是"假"值) | 低 | 明确定义:None / false / 空字符串 / 空数组 均为假 |
| 子模板循环引用导致栈溢出 | 低 | 渲染时加递归深度限制(16 层)或已注册集合去重检测 |
---
## 验收标准
1. `cargo check` 编译通过
2. `cargo clippy` 无警告
3. 模块文件路径正确:`src/prompt.rs` + `src/prompt/{template,composer,error}.rs`
4. `PromptTemplate::compile()` 能解析含变量/条件/循环的模板
5. `PromptTemplate::render()` 正确渲染所有语法
6. `PromptTemplate` 实现 `Display` trait,输出原始模板字符串
7. `TemplateContext` 提供 `from_json()` / `from_map()` 构造方式,支持 `From<&str>` 自动转换
8. `PromptTemplateRegistry` 支持立即编译(`register()`)、延迟编译(`register_lazy()`)、文件加载(`register_file()`
9. `PromptComposer` 支持链式调用,覆盖 System / User / Assistant / Developer / Tool 五种角色
10. `PromptComposer` 支持 `user_content()` / `system_content()` / `assistant_content()` / `developer_content()` / `tool_content()` 多模态方法
11. `PromptComposer` 支持 `with_name()` 设置消息角色标识
12. `PromptComposer::build_request()` 能创建 `OpenaiChatRequest`
13. `validate_messages()` 能校验消息序列合法性
14. `LlmCycle` 新增 `with_messages()` / `extend_messages()` / `submit_messages()` 支持 Composer 集成
15. `lib.rs` 包含 `pub mod prompt;`
16. 所有新公开 API 有文档注释