Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/面试/代码/正则表达式

Agent中的正则

3 分钟阅读 · Note

目录树 578 篇

            • 解析模板字符串
            • Agent中的正则
          • 面试-常考经典1
          • 面试-常考经典2
          • 面试-数组专项
        • 可投递企业
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗解析模板字符串同一路径↗5 最长回文子串 ⌚️共同主题↗面试-常考经典1共同主题↗面试-常考经典2共同主题↗面试-数组专项共同主题↗牛客 101共同主题
  • Agent中的正则

Agent中的正则

我们需要实现一个 parseOutput 函数,用来提取大模型返回的结构化字符串

字符串的match()

match() 是 JavaScript 字符串(String)原型上的一个非常强大的内置方法。它的核心作用就是让一个字符串去匹配正则表达式,并把匹配到的结果提取出来。

基础语法与返回值

它的基本调用格式是:字符串.match(正则表达式)。 它的返回值只有两种情况:

  • 匹配成功:返回一个数组,里面装着匹配到的各种信息。
  • 匹配失败:返回 null。(这也是为什么我们在写代码时,一定要先判断 if (matchResult) 防止报错的原因)。

决定返回结果的“开关”:全局标志 g

match() 方法的行为,完全取决于你传入的正则表达式有没有带 g(全局匹配)标志。

  • 情况一:不带 g 标志(你刚才代码中的用法) 它只会找第一个符合条件的匹配项就停下来。返回的数组非常详细,不仅包含匹配到的文本,还带有额外的属性:

    • array[0]:完整匹配到的文本。
    • array[1], array[2]...:对应正则里第1个、第2个**小括号 ()(捕获组)**里匹配到的内容。
    • index:匹配到的文本在原字符串中的起始位置。
    • input:原始字符串本身。
  • 情况二:带了 g 标志(例如 /abc/g) 它会化身“吸尘器”,把字符串里所有符合条件的内容全部找出来。

    • 返回的数组只包含所有匹配到的完整文本,不会包含小括号里的捕获组信息,也没有 index 和 input 属性。
const str = "Chapter 1.1 and Chapter 2.3";

// 1. 不带 g 标志:只找第一个,但提供详细信息(包括捕获组)
const regex1 = /Chapter (\d+\.\d+)/; 
const result1 = str.match(regex1);
console.log(result1);
// 输出结果:
// [
//   'Chapter 1.1',   // [0] 完整匹配
//   '1.1',           // [1] 第一个小括号 (\d+\.\d+) 捕获的内容
//   index: 0,        // 起始位置
//   input: 'Chapter 1.1 and Chapter 2.3', // 原字符串
//   groups: undefined
// ]

// 2. 带了 g 标志:找出所有,但只给纯文本,没有捕获组和位置信息
const regex2 = /Chapter (\d+\.\d+)/g;
const result2 = str.match(regex2);
console.log(result2);
// 输出结果:
// ['Chapter 1.1', 'Chapter 2.3'] 

函数实现

要实现这个 parseOutput 函数,核心就是通过字符串匹配把 Thought: 和 Action: 后面的内容分别提取出来:

function parseOutput(responseWithThoughtAndAction) {
    let thought = ''
    let action = ''

    // 1. 提取 Thought 部分
    // 匹配 "Thought:" 开头,直到遇到 "Action:" 之前的所有内容
    const thoughtMatch = responseWithThoughtAndAction.match(/Thought:\s*([\s\S]*?)(?=\s*Action:)/)
    if (thoughtMatch && thoughtMatch[1]) {
        thought = thoughtMatch[1].trim()
    }

    // 2. 提取 Action 部分
    // 匹配 "Action:" 开头,直到字符串结束的所有内容
    const actionMatch = responseWithThoughtAndAction.match(/Action:\s*([\s\S]*)/)
    if (actionMatch && actionMatch[1]) {
        action = actionMatch[1].trim()
    }

    return { thought, action }
}

// --- 测试一下 ---
const testInput = `Thought: 用户想要去北京旅行,需要一个旅行计划。我可以 根据自己对北京的了解来生成一个详细的计划,包括景点、行程安排、美食建议等。不需要调用任何工具,直接给出答案即可。
Action: Finish[北京旅行计划(建议3-5天)]
## 第一天:历史经典游
- **上午**:天安门广场(免费,需预约)→ 故宫博物院(门票60元,建议提前购票,游览3-4小时)
- **下午**:景山公园(2元,俯瞰故宫全景)→ 北海公园(10元,可 划船)
- **晚上**:王府井步行街(小吃街、购物)`

const result = parseOutput(testInput)

console.log('--- 提取出的 Thought ---')
console.log(result.thought)
console.log('\n--- 提取出的 Action ---')
console.log(result.action)
  1. /Thought:\s*([\s\S]*?)(?=\s*Action:)/:

    • Thought:\s*:匹配 "Thought:" 及其后面的空白字符(包括换行),\s*用来尽可能多地去匹配空白字符,直到遇到第一个非空白字符(比如你文本里的“用”字)时,\s* 就会停下来,把控制权交给后面。
    • ([\s\S]*?):核心捕获组。[\s\S] 表示匹配任意字符(包括换行符),*? 表示非贪婪匹配,即尽可能少地匹配,直到遇到后面的条件为止。
    • (?=\s*Action:):这是一个正向先行断言,意思是“匹配到这里就停,因为后面紧跟着的是 Action:”,但它本身不会被包含在提取结果里。
  2. /Action:\s*([\s\S]*)/:

    • 匹配 "Action:" 之后的所有剩余内容(包括多行的旅行计划详情),直到字符串结束。
  3. .trim():

    • 最后对提取出的字符串调用 trim(),是为了去掉首尾可能多余的空格或换行符,保证数据干净。