乐于分享
好东西不私藏

企业级AI Agent开发踩坑实录:从"导出本周考勤"到崩溃的24小时

企业级AI Agent开发踩坑实录:从"导出本周考勤"到崩溃的24小时

如果你的AI Agent能正确回答"本周是几号到几号",恭喜你,你已经跨过了企业级应用的第一道门槛。

这是一个真实的技术复盘。
在开发HR智能助手的过程中,一个看似简单的"导出本周考勤明细"功能,让我们的技术团队经历了从信心满满到怀疑人生,再到最终解决的完整历程。

01 案发现场:一个简单需求的复杂化

需求描述:用户在AI Chat中说"导出本周考勤明细",系统应该:

  1. 正确理解"本周"的时间范围
  2. 查询数据库获取考勤数据
  3. 生成Excel文件并下载

听起来很简单?实际上,我们团队花了整整一天时间才让它正常工作。

问题现象

  • LLM返回的日期范围错误:说"本周"却返回4月7日-12日(实际应该是4月6日-12日)
  • 导出的Excel只有部分数据:数据库有41条,Excel只有26条
  • 导出的文件格式错误:生成的是Word而不是Excel
  • 数据全是编造的假数据,而不是数据库真实数据

02 踩坑一:基础设施与业务代码的边界

开发过程中,我们的开发人员不断在Harness底座中添加业务逻辑:

  • 在orchestrator.ts中硬编码考勤查询逻辑
  • 在system-prompt.ts中写HR相关的日期示例
  • 修改hr-queries.ts中的SQL查询

反复强调:"不要在Harness底座写任何业务代码",但开发人员为了业务实现还是一次次违反这个原则。

反思

企业级AI Agent架构应该是:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
Harness底座(纯基础设施)├── 核心引擎├── LLM接口├── Skill加载器├── 意图路由
业务层(独立目录)├── HR查询处理器├── HR API路由├── 数据库Schema
Skills定义├── hr/attendance-stats├── hr/payroll-generator

核心原则

  • Harness只负责调度,不负责业务
  • 业务逻辑通过接口调用,不直接写SQL
  • Skill定义在skills目录,通过metadata配置data_queries

03 踩坑二:日期计算的手写陷阱

问题

最初我们的技术人员用JS手写日期计算:

  • 1
  • 2
  • 3
  • 4
  • 5
// 计算本周一const dayOfWeek = now.getDay();const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;const thisMonday = newDate(now);thisMonday.setDate(now.getDate() + mondayOffset);

这在周日时计算结果是错误的。
其实老大一直在反复强调说:"为什么不用现成的库?date-fns、moment.js都是专门解决这个问题的"

解决

安装date-fns,一行代码解决:

  • 1
  • 2
const { startOfWeek, format } = require('date-fns');const thisMonday = startOfWeek(now, { weekStartsOn1 });

教训:任何功能开发前,先问三个问题:

  1. npm上有没有现成的包?
  2. GitHub上有没有开源方案?
  3. Stack Overflow上有没有最佳实践?

04 踩坑三:pageSize限制导致数据截断

问题

Agent查询考勤数据时,默认pageSize=20,但数据库有41条记录。LLM只拿到前20条,生成的Excel也只有20条。

解决

AI查询场景不需要分页,传递pageSize=10000或者pageSize=0忽略这个参数都可以直接获取全部数据:

  • 1
  • 2
// orchestrator.tsconst fullPayload = { ...payload, pageSize0 };

教训:AI Agent的数据查询场景与前端列表分页场景不同,需要根据上下文动态调整。

05 踩坑四:LLM不按指令生成invoke

问题

system prompt写了导出规则,但LLM:

  • 直接返回JSON数据,不生成invoke标签
  • 生成invoke但格式错误:sheets不是数组
  • 只取部分数据放入Excel

解决

在system prompt中加强指令:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
## 导出规则(必须遵守)
用户说"导出"时,你必须输出以下格式:
<invokename="excel-xlsx">{"sheets":[{"name":"本周考勤明细","columns":["日期","姓名","状态"],"data":[["2026-04-06","张三","normal"],...]}]}</invoke>
关键点:1. sheets必须是数组2. data必须是二维数组(数组套数组)3. 数据必须来自"查询数据",不能编造

教训:给LLM的指令要具体到代码级别,不能只说"导出Excel"。

06 踩坑五:预查询与动态需求的矛盾

问题

Skill配置了data_queries预查询:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
data_queries:-name:recent_attendancehandler:query_attendancedefault_params:start_date:"${current_week_start}"end_date:"${current_week_end}"

但用户说"上周"时,预查询还是本周数据,导致LLM用错误的数据生成Excel。

解决

将预查询范围改为两周(上周一到今天),覆盖本周和上周:

  • 1
  • 2
  • 3
default_params:start_date:"${previous_week_start}"end_date:"${current_week_end}"

并在system prompt中明确告诉LLM上周的时间范围,让它自己筛选。

07 踩坑六:UTC时间转换问题

问题

数据库存储的日期是UTC时间,如2026-04-09T16:00:00.000Z,转换为北京时间后实际是4月10日。导出的Excel日期显示错误。

解决

在formatQueryResult中转换UTC时间:

  • 1
  • 2
  • 3
const utcDate = newDate(formatted.date);utcDate.setHours(utcDate.getHours() + 8);formatted.date = utcDate.toISOString().slice(010);

08 踩坑七:文件图标显示

问题

用户要求文件图标显示扩展名和颜色:

  • DOC:蓝色
  • XLS:绿色
  • PDF:红色

解决

使用开源库react-file-icon,而不是自己手写SVG:

  • 1
npm install react-file-icon

09 最终架构: Harness + Skills + 业务API

经过这次踩坑,我们再次修正了架构:

Harness职责

  • 意图识别(关键词匹配 + LLM理解)
  • Skill调度(加载SKILL.md,执行data_queries)
  • System Prompt构建(动态注入时间、数据)
  • Invoke解析和执行

Skill职责

  • 定义触发关键词
  • 配置data_queries(预查询数据)
  • 定义输出格式(Markdown表格、Excel等)

业务API职责

  • 数据查询接口(不在Harness里面写SQL)
  • 数据格式转换
  • 分页处理

10 开发铁律

总结这次踩坑,我给自己写下三条铁律:

铁律一:先搜索,再编码

编写任何代码前,必须先查有没有现有的成熟包、GitHub开源方案。默认假设已有现成解决方案。

铁律二:基础设施不写业务

Harness底座只负责调度,业务逻辑必须在独立目录,通过接口调用。

铁律三:给LLM的指令要代码级别

不能只说"导出Excel",要给出完整的JSON格式示例。

结语

企业级AI Agent开发,看似是技术问题,实际是架构问题。

日期计算数据截断invoke格式——这些看起来是代码bug,根源都在架构边界不清。

当你为一个小功能反复debug时,停下来问自己:是不是架构设计有问题?

好的架构让简单的问题保持简单,相反会让简单的问题变得复杂。