STAR形式分享AI编程-2

分享一个使用AI助手编程时AI表现不足的STAR:

开发环境

编码工具: Cursor
AI模型: claude-3.5-sonnet

实践描述

Situation & Task

最近接手做一个别的团队成员维护的AI项目,刚开始的时候我没有了解过这个项目之前的业务逻辑和数据处理逻辑,第一时间会让AI代替我理解和修改一些功能,比如打字机的处理、SSE的流式输出处理、对话流程处理,通常大家编码的时候一些函数里或多或少会有和业务相关的代码,这时候直接询问AI会将一些业务相关的东西帮你一起处理,处理的结果有几种可能的情况:

一是它不会去管那些和业务相关的代码,只帮你做数据处理

二是它会直接删除它觉得这里不应该有的代码,包括业务逻辑

三就像是前面两者的混合,有删有增,它看完上下文后觉得,前面一个地方改了某个逻辑,这里也要配合改才合适,然后主动帮你把它觉得这里不改就可能是个bug的地方给补上去

受各种业务逻辑的干扰,通常让AI改完再验证,再继续问答再验证,假如一个能2小时完成的事情,几个轮次下来可能时间已经过去一半仍然没有进展

Action

尝试细节拆分:

业务流程归业务流程,数据处理归数据处理,两者尽量改为独立的函数做处理,关键逻辑要像”把大象装冰箱分三步”那样写清楚

比如流式数据处理,里面包含think标签和content两部分,至于业务上是什么流程,不要在数据处理的同时让AI过多关注,只需要告诉它,在如下所示的格式里,前面一段think标签和里面的内容,需要单独放在一个字段,后面的单独放在一个字段,并确保在流式输出中不会因为不在同一帧里有截断漏掉的情况,并且有可能会多个开始和结尾标签都在同一帧的情况等等细节描述

<think>
这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容这里是思考内容。
</think>
这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文这里是正文。

首先对数据逻辑的可能性进行描述

处理thinkContent的方法逻辑
- 1. 当message包含<think>标签时,将message中的<think>标签和后面文案开头的br标签进行去除
- 2. 当message包含</think>标签时,将message中的</think>标签和后面文案开头的br标签进行去除,同时fullResponseContent需要拼接</think>后面的内容,否则正文会缺失一部分
- 3. 即没有<think>标签也没有</think>标签时,即代表是<think>标签和</think>标签之间的内容,正常拼接
- 4. 如果message既包含<think>标签,也包含</think>标签,那么需要把<think>标签和</think>标签之间的内容进行拼接,同时fullResponseContent需要拼接</think>后面的内容,否则正文会到think卡片里去

以下是AI给出的function的完整输出,基本是直接拿来用的:

/**
 * 处理thinkContent,这里需要处理<think>标签和</think>标签,和<br>标签
 * 1. 当message包含<think>标签时,将message中的<think>标签和后面文案开头的br标签进行去除
 * 2. 当message包含</think>标签时,将message中的</think>标签和后面文案开头的br标签进行去除,同时fullResponseContent需要拼接</think>后面的内容,否则正文会缺失一部分
 * 3. 即没有<think>标签也没有</think>标签时,即代表是<think>标签和</think>标签之间的内容,正常拼接
 * 4. 如果message既包含<think>标签,也包含</think>标签,那么需要把<think>标签和</think>标签之间的内容进行拼接,同时fullResponseContent需要拼接</think>后面的内容,否则正文会到think卡片里去
 * @param message event.data
 */
const handleThinkContent = (message: string) => {
  thinkBuffer.value += message;
  console.log("LOG: thinkBuffer:", thinkBuffer.value);
  // 检查是否一次性接收到完整的think内容或者一次性接收到所有思考+正文内容,这时候需要把think之后的内容全部放到fullResponseContent.value里
  if (message.includes("<think>") && message.includes("</think>")) {
    // 一次性接收到完整内容的处理
    const thinkStartIndex = message.indexOf("<think>");
    const thinkEndIndex = message.indexOf("</think>");
    
    // 提取think内容
    const thinkContentStr = message.substring(thinkStartIndex + 7, thinkEndIndex + 8)
      .replace(/^(?:<br>)+/, '')  // 去除开头的<br>
      .replace(/(?:<br>)+$/, ''); // 去除结尾的<br>
      // 更新think内容
    thinkContent.value = thinkContentStr;
    console.log("LOG: thinkContent:", thinkContent.value);
    
    // 提取think之前和之后的内容作为正文
    let afterThinkContent = (thinkBuffer.value.substring(thinkBuffer.value.indexOf("</think>") + 8)).replace(/^(?:<br>)+/, '');
    // 更新正文内容
    fullResponseContent.value += afterThinkContent;
    console.log("LOG: fullResponseContent:", fullResponseContent.value);
    handleFullResponseContent();
    
    // 重置状态
    isCollectingThink.value = false;
    thinkBuffer.value = "";
  } else {
    if (message.includes("<think>")) {
      isCollectingThink.value = true;
      thinkBuffer.value = (message.substring(message.indexOf("<think>") + 7)).replace(/^(?:<br>)+/, '');
      thinkContent.value = thinkBuffer.value;
      console.log("LOG: thinkContent:", thinkContent.value);
    } else if (message.includes("</think>")) {
      isCollectingThink.value = false;
      // 获取</think>标签后的内容并去除开头的所有<br>标签
      let afterThinkContent = (thinkBuffer.value.substring(thinkBuffer.value.indexOf("</think>") + 8)).replace(/^(?:<br>)+/, '');
      fullResponseContent.value += afterThinkContent;
      console.log("LOG: fullResponseContent:", fullResponseContent.value);
      handleFullResponseContent();
      // 这里需要把thinkBuffer.value进行截取,去除</think> 后面的内容
      let finalThinkPart = message.substring(0, message.indexOf("</think>"));
      console.log("LOG: content:", finalThinkPart);
      thinkContent.value += finalThinkPart;
      console.log("LOG: thinkContent:", thinkContent.value);
    } else if (isCollectingThink.value) {
      console.log("LOG: message:", message);
      thinkContent.value += message;
    }
  }
}

Result

经过这样的拆分和对功能点的细节描述给到AI,AI可以完美的处理并封装好方法,基本可以直接拿来用,且大大缩短了自己写代码来调试和AI干扰了业务代码后进行验证的时间,这一点经过多次尝试,非常有效~

🤔 举个栗子:
原来让AI改代码就像让一个新来的厨房学徒直接上手炒菜——ta分不清哪些是秘制调料(业务逻辑),哪些是普通配菜(数据处理),结果把你的招牌菜改成了”黑暗料理”。

现在只让他做食材预处理:
✅ 土豆切丝
✅ 青菜摘叶
✅ 肉片腌制

至于火候把控和调料搭配(业务逻辑),牢牢掌握在自己手里!他只需要保证切好的土豆丝粗细均匀,剩下的交给你这个大厨来掌勺 🧑‍🍳

总结来说,针对不熟悉的项目,且在保证原有功能的基础之上,这样做会很省心省力,且按时的完成了任务。