复杂任务拆解:让AI像项目经理一样思考

为什么 AI 需要学会拆解任务?

一个让AI 崩溃的真实场景

用户说了一句看似简单的话:

text 复制代码
"找出本月修改次数最多的文件,备份到桌面"

人类的直觉处理

  1. 我知道"本月"是什么意思(时间范围)。
  2. 我知道"修改次数最多"意味着要统计。
  3. 我知道"备份"是复制操作。
  4. 我凭经验知道执行顺序。

AI 面临的挑战

  • 没有"文件系统"的直觉。
  • 不知道"本月"在代码中怎么表示。
  • 不知道"修改次数"从哪获取。
  • 不知道"最多"需要比较。

核心矛盾

自然语言 vs 计算机指令

维度 自然语言(用户) 计算机指令(AI需要产出)
表达方式 声明式(说什么) 命令式(怎么做)
结构 模糊、隐含 精确、显式
依赖 隐含依赖 明确依赖
粒度 粗粒度 原子操作

解决方案:任务拆解

让 AI 像项目经理一样,把一句话的需求分解成多个可执行的任务列表。例如:用户的原始需求是:"找出本月修改次数最多的文件,备份到桌面":

  1. 获取当前目录所有文件列表。
  2. 获取每个文件的修改历史。
  3. 筛选本月内的修改记录。
  4. 统计每个文件的修改次数。
  5. 比较找出修改次数最多的文件。
  6. 复制该文件到桌面。
  7. 返回操作结果。

什么是复杂任务拆解?

拆解的关键特征

  • 原子性:每个步骤不可再分,可直接执行。
  • 有序性:步骤之间有明确的先后顺序。
  • 可验证性:每个步骤的输出可被验证。
  • 可组合性:子任务可以复用。

拆解的三个层次

层次1: 任务理解

  • 意图识别:用户想做什么?
  • 约束提取:有时间限制吗?范围是什么?
  • 隐含需求:用户没说但需要的(如错误处理、反馈)

层次2: 任务分解

  • 将目标拆分为 3-7 个逻辑子任务。
  • 标注子任务间的依赖关系。
  • 识别可并行执行的任务。

层次3: 原子操作

  • 将每个子任务映射到具体工具。
  • 确定工具参数和调用顺序。
  • 设计输入输出格式。

为什么拆解如此重要?

维度 无拆解 有拆解
成功率 低(一步到位容易出错) 高(每步可验证)
可调试性 无法定位问题 可定位到具体步骤
可复用性 每个任务单独处理 子任务可复用
可解释性 黑盒 每步可见
错误恢复 全部重来 从失败步骤恢复

任务拆解的5种策略模式

策略一:顺序拆解

适用于步骤有明确的前后依赖关系的场景。

顺序拆解示例

text 复制代码
查询 -─▶ 统计 ──▶ 比较 ──▶ 操作

每个步骤的输出是下一步的输入!

代码示例

javascript 复制代码
async function sequentialPlan() {
  const files = await listFiles()           // Step 1
  const stats = await countModifications(files) // Step 2
  const max = await findMax(stats)          // Step 3
  const result = await backup(max)          // Step 4
  return result
}

策略二:并行拆解

适用于存在独立、无依赖关系的子任务的场景。

并行拆解示例

graph TD Start([任务拆解]) --> Query1[查北京天气] Start --> Query2[查上海天气] Start --> Query3[查武汉天气] Query1 --> Merge[合并结果] Query2 --> Merge Query3 --> Merge Merge --> End([结束])

代码示例

javascript 复制代码
async function parallelPlan() {
  const [beijing, shanghai, shenzhen] = await Promise.all([
    getWeather("北京"),
    getWeather("上海"),
    getWeather("武汉")
  ])
  return { beijing, shanghai, shenzhen }
}

策略三:条件拆解

适用于有决策点的任务的场景。

条件拆解示例

graph TD Start([获取天气]) --> Decision{判断天气} Decision -->|下雨| Rain[提醒带伞
建议穿雨鞋] Decision -->|晴天| Sunny[推荐公园
注意防晒] Decision -->|下雪| Snow[提醒保暖
注意路滑] Rain --> End([结束]) Sunny --> End Snow --> End

代码示例

javascript 复制代码
async function conditionalPlan() {
  const weather = await getWeather("北京")
  
  if (weather.condition === "rain") {
    return "下雨了,记得带伞,建议穿雨鞋"
  } else if (weather.condition === "sunny") {
    const park = await recommendPark("北京")
    return `天气好,推荐去${park},注意防晒`
  } else if (weather.condition === "snowy") {
    return `下雪了,记得多穿点衣服,注意保暖,防止路滑`
  }
}

策略四:循环拆解

适用于需要批量处理相同操作的场景。

循环拆解示例

graph TD Start([获取文件列表]) --> Decision{还有文件
未处理?} Decision -->|是| Process[处理文件] Decision -->|否| End([结束]) Process --> Decision

代码示例

javascript 复制代码
async function loopPlan(files) {
  const results = []
  for (const file of files) {
    const result = await processFile(file)
    results.push(result)
  }
  return results
}

策略五:聚合拆解

适用于需要汇总多个结果的场景。

聚合拆解示例

graph TD File1[文件1统计] --> Merge[比较聚合] File2[文件2统计] --> Merge File3[文件3统计] --> Merge Merge --> Output[输出结果]

代码示例

javascript 复制代码
async function aggregatePlan() {
  // 并行执行多个统计
  const stats = await Promise.all(
    files.map(file => countModifications(file))
  )
  
  // 聚合比较
  const max = stats.reduce((max, current) => 
    current.count > max.count ? current : max
  )
  
  return max
}

让 AI 学会拆解的 Prompt 技术

技术一:显式要求拆解

text 复制代码
在回答问题前,请先将任务拆解为具体步骤:

1. 列出所有需要的子任务
2. 说明每个子任务的作用
3. 标注子任务间的依赖关系
4. 确认所有子任务都能执行

任务: {task}

技术二:提供拆解模板

text 复制代码
请使用以下模板拆解任务:

### 任务分析
- 目标: [一句话总结]
- 约束: [时间、范围、条件]
- 隐含需求: [用户没明说但需要的]

### 子任务列表
| ID | 子任务 | 依赖 | 工具 | 输出 |
|----|--------|------|------|------|
| 1 | ... | 无 | ... | ... |
| 2 | ... | 1 | ... | ... |
| 3 | ... | 1,2 | ... | ... |

### 异常处理
- 如果步骤1失败,则...
- 如果步骤2无结果,则...

技术三:Few-shot 示例

text 复制代码
## 示例1:文件备份任务

用户输入:找出本月修改次数最多的文件,备份到桌面

拆解结果:
1. [查询] 获取当前目录所有文件列表
2. [筛选] 获取每个文件的修改历史,筛选本月记录
3. [统计] 统计每个文件的修改次数
4. [比较] 找出修改次数最多的文件
5. [处理并列] 如果有多个并列,询问用户选择
6. [执行] 复制文件到桌面
7. [反馈] 返回备份结果

## 示例2:代码质量分析任务

用户输入:分析项目代码,找出圈复杂度超过10的函数

拆解结果:
1. [扫描] 扫描项目所有代码文件
2. [解析] 解析每个文件的AST
3. [提取] 提取所有函数定义
4. [计算] 计算每个函数的圈复杂度
5. [筛选] 筛选复杂度>10的函数
6. [生成] 生成报告(文件、函数名、复杂度值)

## 当前任务
{task}

技术四:思维链引导

text 复制代码
请按以下思路思考任务拆解:

1. **目标分析**:用户想要达到什么目的?
2. **信息需求**:完成这个目的需要哪些信息?
3. **信息获取**:如何获取这些信息?(需要哪些工具)
4. **信息处理**:获取信息后如何处理?(计算、比较、筛选)
5. **结果呈现**:最后如何呈现结果?
6. **异常处理**:什么情况会出错?如何处理?

任务: {task}

实战:任务拆解 Agent 实现

完整的拆解 Agent 代码

typescript 复制代码
import axios from 'axios'
import dotenv from 'dotenv'

dotenv.config()

// ==================== 类型定义 ====================

interface SubTask {
  id: string
  description: string
  tool?: string
  dependsOn: string[]
  fallback?: string
  output?: string
}

interface TaskPlan {
  originalTask: string
  goal: string
  constraints: string[]
  implicitNeeds: string[]
  steps: SubTask[]
  exceptions: Array<{
    condition: string
    action: string
  }>
}

interface TaskUnderstanding {
  goal: string
  constraints: string[]
  implicitNeeds: string[]
  complexity: 'simple' | 'moderate' | 'complex'
  estimatedSteps: number
}

interface LLMResponse {
  choices: Array<{
    message: {
      content: string
    }
  }>
}

// ==================== 任务拆解Agent ====================

class TaskDecompositionAgent {
  private llm: any
  private apiKey: string
  private apiUrl: string
  
  constructor() {
    this.apiKey = process.env.DEEPSEEK_API_KEY || ''
    this.apiUrl = process.env.DEEPSEEK_API_URL || 'https://api.deepseek.com/v1/chat/completions'
    
    if (!this.apiKey) {
      throw new Error('请设置 DEEPSEEK_API_KEY 环境变量')
    }
  }
  
  /**
   * 核心方法:将用户输入拆解为可执行计划
   */
  async decompose(userInput: string): Promise<TaskPlan> {
    try {
      // 步骤1:任务理解
      const understanding = await this.understandTask(userInput)
      
      // 步骤2:任务分解
      const plan = await this.decomposeIntoSteps(understanding, userInput)
      
      // 步骤3:验证计划
      const validatedPlan = await this.validatePlan(plan)
      
      // 步骤4:优化计划(并行化、缓存等)
      const optimizedPlan = await this.optimizePlan(validatedPlan)
      
      return optimizedPlan
    } catch (error) {
      console.error('任务拆解失败:', error)
      // 返回降级计划
      return this.createFallbackPlan(userInput)
    }
  }
  
  private async understandTask(userInput: string): Promise<TaskUnderstanding> {
    const prompt = `
你是一个任务分析专家。请分析用户的任务,提取关键信息。

用户输入: ${userInput}

请输出JSON格式,不要包含其他文字:
{
  "goal": "一句话总结任务目标",
  "constraints": ["时间约束", "范围约束", "条件约束"],
  "implicitNeeds": ["用户没说但需要的", "如错误处理、反馈等"],
  "complexity": "simple | moderate | complex",
  "estimatedSteps": 预计需要多少步骤
}
`
    const response = await this.callLLM(prompt)
    
    try {
      // 清理响应,提取JSON部分
      const jsonMatch = response.match(/\{[\s\S]*\}/)
      if (!jsonMatch) {
        throw new Error('无法从LLM响应中提取JSON')
      }
      return JSON.parse(jsonMatch[0]) as TaskUnderstanding
    } catch (error) {
      console.error('解析任务理解失败:', error)
      // 返回默认理解
      return {
        goal: userInput,
        constraints: [],
        implicitNeeds: ['错误处理', '结果反馈'],
        complexity: 'moderate',
        estimatedSteps: 3
      }
    }
  }
  
  private async decomposeIntoSteps(
    understanding: TaskUnderstanding, 
    originalTask: string
  ): Promise<TaskPlan> {
    const prompt = `
基于以下任务分析,将其拆解为可执行的子任务列表。

任务分析:
- 目标: ${understanding.goal}
- 约束: ${understanding.constraints.join(', ')}
- 隐含需求: ${understanding.implicitNeeds.join(', ')}

## 拆解要求
1. 每个子任务应该是原子操作(不可再分)
2. 使用数字ID(1, 2, 3...)标识子任务
3. 明确标注子任务间的依赖关系
4. 为关键子任务设计异常处理
5. 考虑可并行执行的子任务

## 输出格式(JSON)
{
  "steps": [
    {
      "id": "1",
      "description": "子任务描述",
      "tool": "推荐使用的工具名",
      "dependsOn": [],
      "fallback": "失败时的备选方案",
      "output": "预期输出"
    }
  ],
  "exceptions": [
    {
      "condition": "异常条件",
      "action": "处理动作"
    }
  ]
}

请直接输出JSON,不要添加其他文字:
`
    const response = await this.callLLM(prompt)
    
    try {
      const jsonMatch = response.match(/\{[\s\S]*\}/)
      if (!jsonMatch) {
        throw new Error('无法从LLM响应中提取JSON')
      }
      
      const parsed = JSON.parse(jsonMatch[0])
      
      return {
        originalTask: originalTask,
        goal: understanding.goal,
        constraints: understanding.constraints,
        implicitNeeds: understanding.implicitNeeds,
        steps: parsed.steps || [],
        exceptions: parsed.exceptions || []
      }
    } catch (error) {
      console.error('解析任务分解失败:', error)
      // 返回默认计划
      return this.createFallbackPlan(originalTask)
    }
  }
  
  private async validatePlan(plan: TaskPlan): Promise<TaskPlan> {
    // 检查完整性
    if (!plan.steps || plan.steps.length === 0) {
      throw new Error('计划为空')
    }
    
    // 检查步骤ID唯一性
    const ids = plan.steps.map(s => s.id)
    if (new Set(ids).size !== ids.length) {
      throw new Error('步骤ID不唯一')
    }
    
    // 检查依赖关系
    const allIds = new Set(plan.steps.map(s => s.id))
    for (const step of plan.steps) {
      if (step.dependsOn) {
        for (const dep of step.dependsOn) {
          if (!allIds.has(dep)) {
            throw new Error(`步骤 ${step.id} 依赖不存在的步骤 ${dep}`)
          }
        }
      }
    }
    
    // 检查循环依赖
    this.checkCyclicDependency(plan.steps)
    
    // 检查原子性(只警告,不抛出错误)
    for (const step of plan.steps) {
      if (this.isTooCoarse(step.description)) {
        console.warn(`⚠️ 步骤 ${step.id} 可能过粗: ${step.description}`)
      }
    }
    
    // 为没有fallback的步骤添加默认fallback
    for (const step of plan.steps) {
      if (!step.fallback) {
        step.fallback = `重试步骤 ${step.id} 或跳过并记录错误`
      }
    }
    
    return plan
  }
  
  private checkCyclicDependency(steps: SubTask[]): void {
    const graph = new Map<string, string[]>()
    for (const step of steps) {
      graph.set(step.id, step.dependsOn || [])
    }
    
    const visited = new Set<string>()
    const recursionStack = new Set<string>()
    
    const hasCycle = (node: string): boolean => {
      visited.add(node)
      recursionStack.add(node)
      
      const deps = graph.get(node) || []
      for (const dep of deps) {
        if (!visited.has(dep)) {
          if (hasCycle(dep)) return true
        } else if (recursionStack.has(dep)) {
          return true
        }
      }
      
      recursionStack.delete(node)
      return false
    }
    
    for (const step of steps) {
      if (!visited.has(step.id)) {
        if (hasCycle(step.id)) {
          throw new Error(`检测到循环依赖,涉及步骤: ${step.id}`)
        }
      }
    }
  }
  
  private isTooCoarse(description: string): boolean {
    // 启发式判断:如果描述中包含"处理"、"分析"等模糊词汇,可能过粗
    const coarseWords = ['处理', '分析', '操作', '管理', '进行', '执行']
    return coarseWords.some(word => description.includes(word))
  }
  
  private async optimizePlan(plan: TaskPlan): Promise<TaskPlan> {
    // 识别可并行的步骤
    const parallelGroups = this.identifyParallelGroups(plan.steps)
    
    // 为并行组添加元数据(不修改tool字段,避免破坏原有逻辑)
    const optimizedSteps = plan.steps.map(step => ({
      ...step,
      parallelWith: parallelGroups.find(group => group.includes(step.id))?.filter(id => id !== step.id) || []
    }))
    
    // 重新排序步骤:将可并行的步骤放在相邻位置
    const reorderedSteps = this.reorderStepsForParallel(optimizedSteps, parallelGroups)
    
    return {
      ...plan,
      steps: reorderedSteps
    }
  }
  
  private identifyParallelGroups(steps: SubTask[]): string[][] {
    const groups: string[][] = []
    const processed = new Set<string>()
    let remainingSteps = [...steps]
    
    while (remainingSteps.length > 0) {
      // 找出所有依赖都已满足的步骤
      const parallelGroup: string[] = []
      
      for (const step of remainingSteps) {
        const hasUnmetDeps = step.dependsOn.some(dep => !processed.has(dep))
        if (!hasUnmetDeps) {
          parallelGroup.push(step.id)
        }
      }
      
      if (parallelGroup.length === 0) {
        // 如果没有可执行的步骤,说明有循环依赖或依赖错误
        console.warn('无法识别并行组,可能存在依赖问题')
        break
      }
      
      groups.push(parallelGroup)
      for (const id of parallelGroup) {
        processed.add(id)
      }
      
      // 移除已处理的步骤
      remainingSteps = remainingSteps.filter(step => !processed.has(step.id))
    }
    
    return groups
  }
  
  private reorderStepsForParallel(steps: SubTask[], parallelGroups: string[][]): SubTask[] {
    const reordered: SubTask[] = []
    const stepMap = new Map(steps.map(s => [s.id, s]))
    
    for (const group of parallelGroups) {
      // 按原顺序添加同组的步骤
      for (const id of group) {
        const step = stepMap.get(id)
        if (step) {
          reordered.push(step)
        }
      }
    }
    
    return reordered
  }
  
  private createFallbackPlan(userInput: string): TaskPlan {
    return {
      originalTask: userInput,
      goal: userInput,
      constraints: [],
      implicitNeeds: ['错误处理'],
      steps: [
        {
          id: '1',
          description: `执行任务: ${userInput}`,
          dependsOn: [],
          fallback: '任务失败,请手动处理',
          output: '执行结果'
        }
      ],
      exceptions: [
        {
          condition: '任何步骤失败',
          action: '记录错误并终止执行'
        }
      ]
    }
  }
  
  private async callLLM(prompt: string): Promise<string> {
    if (!this.apiKey) {
      throw new Error('请设置 DEEPSEEK_API_KEY 环境变量')
    }
    
    try {
      // 修复:添加必要的headers
      const response = await axios.post<LLMResponse>(
        this.apiUrl,
        {
          model: 'deepseek-chat',
          messages: [{ role: 'user', content: prompt }],
          temperature: 0.3, // 低温度,保证拆解稳定
          max_tokens: 2000
        },
        {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${this.apiKey}`
          },
          timeout: 30000 // 30秒超时
        }
      )
      
      const content = response.data.choices[0]?.message?.content
      if (!content) {
        throw new Error('LLM返回空响应')
      }
      
      return content
    } catch (error) {
      if (axios.isAxiosError(error)) {
        if (error.response) {
          // 服务器返回了错误状态码
          throw new Error(`API调用失败 [${error.response.status}]: ${error.response.data?.error?.message || error.message}`)
        } else if (error.request) {
          // 请求已发送但没有收到响应
          throw new Error(`API调用失败: 无响应 (${error.message})`)
        } else {
          // 请求配置出错
          throw new Error(`API调用失败: ${error.message}`)
        }
      }
      throw error
    }
  }
  
  /**
   * 获取任务的执行顺序(拓扑排序)
   */
  getExecutionOrder(plan: TaskPlan): string[][] {
    return this.identifyParallelGroups(plan.steps)
  }
  
  /**
   * 获取任务的统计信息
   */
  getStats(plan: TaskPlan): {
    totalSteps: number
    parallelGroups: number
    maxParallelism: number
    hasFallbacks: number
  } {
    const parallelGroups = this.identifyParallelGroups(plan.steps)
    const maxParallelism = Math.max(...parallelGroups.map(g => g.length), 0)
    const hasFallbacks = plan.steps.filter(s => s.fallback).length
    
    return {
      totalSteps: plan.steps.length,
      parallelGroups: parallelGroups.length,
      maxParallelism,
      hasFallbacks
    }
  }
}

// ==================== 使用示例 ====================

async function main() {
  // 检查环境变量
  if (!process.env.DEEPSEEK_API_KEY) {
    console.error('❌ 错误: 请设置 DEEPSEEK_API_KEY 环境变量')
    console.error('示例: export DEEPSEEK_API_KEY="your-api-key"')
    process.exit(1)
  }
  
  console.log('🚀 启动任务拆解Agent...\n')
  
  const agent = new TaskDecompositionAgent()
  
  const tasks = [
    "找出本月修改次数最多的文件,备份到桌面",
    "分析项目代码,找出圈复杂度超过10的函数",
    "将下载文件夹中超过30天未使用的文件移动到归档文件夹"
  ]
  
  for (const task of tasks) {
    console.log('\n' + '='.repeat(60))
    console.log(`📝 原始任务: ${task}`)
    console.log('='.repeat(60))
    
    try {
      const plan = await agent.decompose(task)
      
      console.log('\n🎯 目标:', plan.goal)
      console.log('\n📋 执行计划:')
      plan.steps.forEach(step => {
        const deps = step.dependsOn.length > 0 
          ? ` (依赖: ${step.dependsOn.join(', ')})` 
          : ''
        console.log(`   ${step.id}. ${step.description}${deps}`)
        if (step.tool) {
          console.log(`      🔧 工具: ${step.tool}`)
        }
        if (step.fallback) {
          console.log(`      🔄 备选: ${step.fallback}`)
        }
      })
      
      if (plan.exceptions.length > 0) {
        console.log('\n⚠️ 异常处理:')
        plan.exceptions.forEach(ex => {
          console.log(`   如果 ${ex.condition},则 ${ex.action}`)
        })
      }
      
      // 显示执行顺序
      const executionOrder = agent.getExecutionOrder(plan)
      console.log('\n⚡ 执行顺序(可并行组):')
      executionOrder.forEach((group, idx) => {
        console.log(`   组${idx + 1}: [${group.join(', ')}] ${group.length > 1 ? '(可并行)' : '(串行)'}`)
      })
      
      // 显示统计信息
      const stats = agent.getStats(plan)
      console.log('\n📊 统计信息:')
      console.log(`   总步骤数: ${stats.totalSteps}`)
      console.log(`   并行组数: ${stats.parallelGroups}`)
      console.log(`   最大并行度: ${stats.maxParallelism}`)
      console.log(`   有备选方案: ${stats.hasFallbacks}`)
      
    } catch (error) {
      console.error(`❌ 处理任务失败:`, error)
    }
  }
}

main().catch(console.error)

运行输出示例

text 复制代码
============================================================
📝 原始任务: 找出本月修改次数最多的文件,备份到桌面
============================================================

🎯 目标: 找出本月修改最多的文件并备份

📋 执行计划:
   1. 获取当前目录所有文件列表
   2. 获取每个文件的修改历史记录
   3. 筛选本月内的修改记录
   4. 统计每个文件的本月修改次数
   5. 比较找出修改次数最多的文件 (依赖: 4)
   6. 处理并列情况,询问用户选择 (依赖: 5)
   7. 复制文件到桌面 (依赖: 6)
   8. 返回备份结果 (依赖: 7)
      备选: 如果备份失败,提示用户手动检查

⚠️ 异常处理:
   如果 步骤3 无结果,则 告知用户本月无修改记录
   如果 步骤6 用户取消,则 终止操作并告知

拆解质量评估

质量评估清单

维度 检查项 通过标准
完整性 是否覆盖所有用户需求? 无遗漏
粒度 每个步骤是否原子操作? 不可再分
可执行性 每个步骤是否有对应工具? 工具存在
依赖正确 步骤顺序是否合理? 数据流正确
异常处理 是否考虑失败场景? 有备选方案
可验证性 是否有中间结果检查点? 关键步骤可验证

常见问题与优化

问题 表现 优化方法
拆解过粗 步骤如"处理文件" 细化:打开→读取→修改→保存
遗漏步骤 忘记备份后的验证 增加验证步骤
依赖错误 步骤顺序不对 检查数据流向,绘制依赖图
无异常处理 文件不存在直接崩溃 增加条件判断和fallback
粒度不一致 有的步骤过粗,有的过细 统一粒度标准

最佳实践清单

  • 先理解后拆解:明确目标、约束、隐含需求。
  • 原子化:每个步骤应该是可执行的最小单元。
  • 标注依赖:明确步骤间的数据依赖。
  • 设计异常:为关键步骤准备fallback。
  • 保留中间结果:便于调试和恢复。
  • 可验证:关键步骤后设置检查点。
  • 迭代优化:根据执行结果持续优化拆解。

结语

你在使用 AI 处理复杂任务时,遇到过哪些"拆解失败"的案例?欢迎在评论区分享,一起分析优化!

对于文章中错误的地方或有任何疑问,欢迎在评论区留言讨论!

相关推荐
Arya_aa2 小时前
拿取gitee中现成的vue-manage-system模板
前端·javascript·vue.js
白嫖叫上我2 小时前
弹窗之封装拖拽与拖动拉伸
前端
清汤饺子2 小时前
Spec Kit:让 AI 编程从 Vibe Coding 到 Spec First
前端·javascript·后端
wuhen_n2 小时前
LangChain.js 初探:从手写代码到框架思维
langchain·ai编程·编程语言
爱学习的小仙女!2 小时前
面试题 前端(二)元素显示模式 块元素行内元素区别
前端·前端面试题
酉鬼女又兒2 小时前
零基础快速入门前端蓝桥杯 Web 备考:AJAX 与 XMLHttpRequest 核心知识点及实战(可用于备赛蓝桥杯Web应用开发)
前端·ajax·职场和发展·蓝桥杯·css3·js
祈澈菇凉2 小时前
Next.js + OpenAI API 跑通一个带流式输出的聊天机器人
开发语言·javascript·机器人
Sammyyyyy2 小时前
Node.js、Bun 与 Deno,2026 年后端运行时选择指南
前端·后端·node.js·servbay
前端程序猿i2 小时前
纯JS 导出 Excel 工具
开发语言·javascript·excel