- 装配只认 prompt_order.enabled;squash_system_messages 只合并相邻 system(新增 system 档,连接符按酒馆用单换行)
- 包装层:new_chat_prompt、示例对话(new_example_chat_prompt + <START> 切段 + name)、角色卡三块与 personality_format/scenario_format、{{user}}/{{char}} 取值、任务契约并进预设条目
- 工具协议:按 ToolBindings / 伪工具协议数据声明真 tools(presetToolPolicy=preset),工具描述用到的条目不再重复作为消息发出
- 正则:promptOnly 作用于对话历史(含 depth 过滤)、写入时脚本改写回复;{{//}} 注释按配平扫描
- tools/st-request-diff:真假上游抓包对比工具、三份预设的机制结论与实测报告
70 lines
2.2 KiB
TypeScript
70 lines
2.2 KiB
TypeScript
import type { ChatMessage } from "../llm/client.js";
|
||
|
||
/** 酒馆给示例对话消息打的 name(不是角色名)。 */
|
||
export const EXAMPLE_USER_NAME = "example_user";
|
||
export const EXAMPLE_ASSISTANT_NAME = "example_assistant";
|
||
|
||
/**
|
||
* 把酒馆 `mes_example` 切成示例消息(对应 `openai.js` 的 `setOpenAIMessageExamples` +
|
||
* `parseExampleIntoIndividual`):
|
||
* - 按 `<START>` 分块;
|
||
* - 块内以 `用户名:` / `角色名:` 开头的行切换说话人,其余行算续行;
|
||
* - 每条消息 role 都是 system,name 是 example_user / example_assistant,
|
||
* 正文去掉 `名字:` 前缀。
|
||
* 传进来的 text 应该已经展开过宏(`{{user}}` / `{{char}}`),否则名字对不上。
|
||
*/
|
||
export function parseExampleDialogue(params: {
|
||
text: string;
|
||
userName: string;
|
||
charName: string;
|
||
}): ChatMessage[][] {
|
||
const text = (params.text ?? "").replace(/\r/g, "");
|
||
if (!text.trim()) return [];
|
||
const userName = params.userName.trim();
|
||
const charName = params.charName.trim();
|
||
const blocks = text
|
||
.split(/<START>/i)
|
||
.map((block) => block.trim())
|
||
.filter(Boolean);
|
||
const source = blocks.length > 0 ? blocks : [text.trim()];
|
||
|
||
const out: ChatMessage[][] = [];
|
||
for (const block of source) {
|
||
const messages: ChatMessage[] = [];
|
||
let speaker: "user" | "assistant" | null = null;
|
||
let lines: string[] = [];
|
||
|
||
const flush = () => {
|
||
const content = lines.join("\n").trim();
|
||
if (speaker && content) {
|
||
messages.push({
|
||
role: "system",
|
||
content,
|
||
name: speaker === "user" ? EXAMPLE_USER_NAME : EXAMPLE_ASSISTANT_NAME,
|
||
});
|
||
}
|
||
lines = [];
|
||
};
|
||
|
||
for (const rawLine of block.split("\n")) {
|
||
const line = rawLine.replace(/\r$/, "");
|
||
if (userName && line.startsWith(`${userName}:`)) {
|
||
flush();
|
||
speaker = "user";
|
||
lines.push(line.slice(userName.length + 1));
|
||
continue;
|
||
}
|
||
if (charName && line.startsWith(`${charName}:`)) {
|
||
flush();
|
||
speaker = "assistant";
|
||
lines.push(line.slice(charName.length + 1));
|
||
continue;
|
||
}
|
||
lines.push(line);
|
||
}
|
||
flush();
|
||
if (messages.length > 0) out.push(messages);
|
||
}
|
||
return out;
|
||
}
|