在处理 AI 业务逻辑时,代码很容易沦为“if-else 地狱”。比如,你需要判断用户输入的安全性、识别意图、计算 Token、注入上下文,最后再格式化。
如果用传统的命令式写法,逻辑会像乱麻一样交织。而 函数组合(Function Composition / Compose) 则是将这些逻辑解耦成一颗颗“逻辑原子”,再通过流水线(Pipeline)串联起来。
以下是针对 前端 场景设计的 8 个 Compose 实战案例,让我们彻底告别面条代码。
核心工具:pipe 助手函数
为了符合人类从左到右的阅读习惯,我们通常使用 pipe(左向右组合)而非数学意义上的 compose(右向左)。
JavaScript
const pipe = (...fns) => (initialValue) =>
fns.reduce((v, f) => f(v), initialValue);
1. 自动化 Prompt 清理流水线
在将用户输入发送给 Gemini 之前,必须进行预处理。
JavaScript
const trim = (str) => str.trim();
const removeExtraSpaces = (str) => str.replace(/\s+/g, ' ');
const stripHtml = (str) => str.replace(/<[^>]*>?/gm, '');
const sanitizePrompt = pipe(
trim,
stripHtml,
removeExtraSpaces
);
const rawInput = " <div> Hello AI </div> ";
console.log(sanitizePrompt(rawInput)); // "Hello AI"
2. 敏感信息(PII)脱敏网关
在金融或 AI 场景下,保护隐私是红线。
JavaScript
const maskPhone = (s) => s.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
const maskEmail = (s) => s.replace(/(.{3}).*(@.*)/, '$1***$2');
const safetyGate = pipe(
maskPhone,
maskEmail
);
console.log(safetyGate("联系人:13812345678,邮箱:gemini@google.com"));
// "联系人:138****5678,邮箱:gem***@google.com"
3. 多级意图识别(Intent Routing)
替代臃肿的 switch-case,根据关键词动态打标签。
JavaScript
const checkFinance = (ctx) => ctx.text.includes('汇率') ? { ...ctx, intent: 'FINANCE' } : ctx;
const checkCoding = (ctx) => ctx.text.includes('代码') ? { ...ctx, intent: 'CODING' } : ctx;
const defaultIntent = (ctx) => ctx.intent ? ctx : { ...ctx, intent: 'GENERAL' };
const routeIntent = pipe(
checkFinance,
checkCoding,
defaultIntent
);
console.log(routeIntent({ text: '查询今日汇率' }).intent); // "FINANCE"
4. Token 消耗预估与截断
防止 Prompt 过长导致超支或报错。
JavaScript
const countTokens = (ctx) => ({ ...ctx, tokens: ctx.text.length / 4 }); // 简单估算
const checkLimit = (ctx) => ctx.tokens > 100 ? { ...ctx, isOver: true } : ctx;
const truncateIfNeed = (ctx) => ctx.isOver ? { ...ctx, text: ctx.text.slice(0, 400) } : ctx;
const tokenManager = pipe(
countTokens,
checkLimit,
truncateIfNeed
);
5. AI 响应内容的“去幻觉”清洗
AI 经常会说“作为一个 AI 模型...”,我们可以用 pipe 自动化剔除这些废话。
JavaScript
const removeApologies = (s) => s.replace(/抱歉,作为一个.*模型/g, '');
const removeAIPreach = (s) => s.replace(/我无法提供.*建议/g, '数据暂不可用');
const formatMarkdown = (s) => s.trim().startsWith('#') ? s : `# 回答\n${s}`;
const polishResponse = pipe(
removeApologies,
removeAIPreach,
formatMarkdown
);
6. 复杂的 Prompt 上下文注入
在你的 AI Prompt Manager 中,构建最终发送给模型的完整指令。
JavaScript
const injectRole = (role) => (input) => `你是${role}。${input}`;
const injectContext = (context) => (input) => `基于背景:${context},回答:${input}`;
const addOutputFormat = (input) => `${input} 请用 JSON 格式返回。`;
const buildPrompt = pipe(
injectRole('专业前端架构师'),
injectContext('当前项目使用 Vue3 + Vite'),
addOutputFormat
);
console.log(buildPrompt('如何优化打包体积?'));
7. 异常熔断与降级策略
当判别逻辑发现异常时,直接返回降级结果。
JavaScript
const validateSchema = (data) => data.id ? data : { error: 'Invalid Data' };
const handleErrors = (data) => data.error ? { ...data, fallback: true } : data;
const logTelemetry = (data) => { console.log('Tracking:', data); return data; };
const dataPipeline = pipe(
validateSchema,
handleErrors,
logTelemetry
);
8. 实时搜索列表的性能增强
结合我们之前聊过的 scheduler 概念,在 pipe 中嵌入性能控制。
JavaScript
const heavyFilter = (list) => list.filter(item => item.priority > 5);
const sortData = (list) => list.sort((a, b) => b.score - a.score);
const limitResults = (list) => list.slice(0, 10);
// 在 AI 建议搜索时调用
const processSearchList = pipe(
heavyFilter,
sortData,
limitResults
);
为什么资深开发必须用 Compose?
- 高度可测试性:每一个被组合的小函数(如
trim,maskPhone)都是纯函数,非常容易写单元测试。 - 声明式逻辑:看一眼
pipe(...)里的函数名,你就知道整个业务流程在干什么,而不是深陷在if的嵌套层级里。 - 对 AI 友好:如果你让 Trae 或 Cursor 重构一个 500 行的
if-else函数,它可能会出错;但如果你让它写一个“处理脱敏逻辑的原子函数”,它的准确率是 100% 。