放弃 if-else:学会用 Compose(组合) 将复杂 AI 判别逻辑串成流水线

1 阅读3分钟

在处理 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

  1. 高度可测试性:每一个被组合的小函数(如 trim, maskPhone)都是纯函数,非常容易写单元测试。
  2. 声明式逻辑:看一眼 pipe(...) 里的函数名,你就知道整个业务流程在干什么,而不是深陷在 if 的嵌套层级里。
  3. 对 AI 友好:如果你让 Trae 或 Cursor 重构一个 500 行的 if-else 函数,它可能会出错;但如果你让它写一个“处理脱敏逻辑的原子函数”,它的准确率是 100%