返回笔记首页

完整项目总结与上线指南

主题配置

完整项目总结与上线指南

访问地址:https://workmind-ai.pages.dev/agent


整个课程做了什么

七章内容,从零搭了一个能实际使用的智能办公平台。回顾一下每章解决了什么问题:

章节 模块 核心技术 解决的问题
第一章 项目脚手架 Vue3 + Express + SSE 怎么搭出能跑通的骨架,流式输出怎么实现
第二章 RAG 知识库 Chroma + Embedding 如何让 AI 回答公司内部文档里的问题
第三章 任务 Agent LangGraph + Function Call 如何让 AI 自主规划步骤、调工具完成复杂任务
第四章 内容工作流 LangGraph 工作流 + HIL 固定步骤的内容生成,中途暂停等人工确认
第五章 ERP 报销请假 Structured Output + Multi-Agent 自然语言填单,多角色模拟审批
第六章 Prompt 调试 A/B 测试 + 版本管理 不改代码就能调试 Prompt,自动对比效果
第七章 用量看板 统计聚合 + P99 延迟 实时看 API 费用、缓存命中率、慢请求

项目完整目录结构

markdown
workmind/
│
├── frontend/                          Vue3 前端
│   ├── index.html
│   ├── vite.config.js                 配置了 /api 代理到后端
│   ├── package.json
│   └── src/
│       ├── main.js                    入口,注册 Pinia + Router
│       ├── App.vue                    根布局:侧边栏 + 内容区 + Toast
│       ├── router/
│       │   └── index.js              7个模块的路由(懒加载)
│       ├── styles/
│       │   └── global.css            CSS 变量系统(颜色/间距/圆角)
│       ├── utils/
│       │   └── http.js               axios 封装 + fetchStream SSE 工具
│       ├── stores/                    Pinia 状态管理
│       │   ├── app.js                全局(主题切换、Toast)
│       │   ├── chat.js               对话模块
│       │   ├── knowledge.js          RAG 知识库
│       │   ├── agent.js              任务 Agent
│       │   ├── workflow.js           内容工作流
│       │   ├── erp.js                ERP 报销请假
│       │   ├── prompt.js             Prompt 调试
│       │   └── monitor.js            用量看板
│       ├── views/                     7个页面组件
│       │   ├── ChatView.vue
│       │   ├── KnowledgeView.vue
│       │   ├── AgentView.vue
│       │   ├── WorkflowView.vue
│       │   ├── ErpView.vue
│       │   ├── PromptView.vue
│       │   └── MonitorView.vue
│       └── components/                可复用组件
│           ├── layout/
│           │   ├── AppSidebar.vue    左侧导航栏
│           │   └── AppHeader.vue    顶部栏
│           ├── common/
│           │   └── ToastList.vue    全局提示
│           ├── chat/                 对话模块组件
│           │   ├── SessionSidebar.vue
│           │   ├── RoleSelector.vue
│           │   ├── MessageBubble.vue
│           │   ├── ChatInput.vue
│           │   └── ProfilePanel.vue
│           ├── rag/                  知识库模块组件
│           │   ├── DocumentUploader.vue
│           │   ├── DocumentList.vue
│           │   └── RagChat.vue
│           ├── agent/
│           │   └── ToolCallCard.vue
│           ├── workflow/
│           │   ├── WorkflowGraph.vue
│           │   └── HumanReviewPanel.vue
│           └── erp/
│               ├── SmartFormParser.vue
│               └── ApprovalTimeline.vue
│
├── server/                            Node.js 后端
│   ├── Dockerfile
│   ├── package.json
│   ├── .env.example
│   └── src/
│       ├── index.js                  服务入口(注册路由、优雅退出)
│       ├── config/
│       │   └── index.js             统一配置管理
│       ├── middleware/
│       │   └── index.js             限流 + 输入校验 + 安全检查
│       ├── utils/
│       │   ├── logger.js            结构化日志
│       │   └── errors.js            错误分类 + Express 错误中间件
│       ├── routes/                   8个路由文件
│       │   ├── health.js            健康检查(liveness/readiness)
│       │   ├── chat.js              对话(SSE 流式 + 缓存 + 用户画像)
│       │   ├── knowledge.js         RAG(上传/列表/删除/问答)
│       │   ├── agent.js             Agent(流式执行 + 工具列表)
│       │   ├── workflow.js          工作流(启动/恢复)
│       │   ├── erp.js               ERP(解析 + 审批流)
│       │   ├── prompt.js            Prompt 调试(测试/A/B/模板)
│       │   └── monitor.js           用量统计
│       └── services/                 业务逻辑层
│           ├── model.js             模型工厂(DeepSeek 配置)
│           ├── cache.js             精确缓存(MD5 + TTL)
│           ├── chat/
│           │   └── memory.js        会话记忆 + 用户画像提取
│           ├── rag/
│           │   ├── ingest.js        文档入库(分片 + 向量化)
│           │   └── query.js         RAG 查询(检索 + 生成)
│           ├── agent/
│           │   ├── tools.js         6个工具定义
│           │   └── agent.js         LangGraph ReAct 循环
│           ├── workflow/
│           │   └── workflows.js     4个工作流模板
│           ├── erp/
│           │   ├── parser.js        自然语言 → 结构化表单
│           │   └── approval.js      Multi-Agent 审批流
│           └── prompt/
│               └── promptService.js 模板管理 + A/B 评分
│
└── docker-compose.yml                 一键启动所有服务

从零到运行的完整步骤

第一步:准备环境

确认本机有以下工具:

bash
node --version    # 需要 v20 以上
npm --version     # v9 以上
docker --version  # 用于启动 Chroma 向量数据库

第二步:克隆项目

bash
git clone <你的仓库地址>
cd workmind

第三步:配置环境变量

bash
cd server
cp .env.example .env

用文本编辑器打开 .env,至少要填这两个:

bash
# 必填:DeepSeek 对话模型
# 申请地址:https://platform.deepseek.com
DEEPSEEK_API_KEY=sk-你的key

# 如果要用 RAG 知识库,还需要 OpenAI Embedding
# 申请地址:https://platform.openai.com
OPENAI_API_KEY=sk-你的key

其他配置有默认值,不用改也能跑。

第四步:安装依赖

bash
# 后端
cd server
npm install

# 前端(新开一个终端)
cd ../frontend
npm install

第五步:启动向量数据库(RAG 功能需要)

bash
docker run -d -p 8000:8000 chromadb/chroma

# 验证是否启动成功
curl http://localhost:8000/api/v1/heartbeat
# 返回 {"nanosecond heartbeat": ...} 说明成功

如果不用 RAG 知识库功能,这步可以跳过。

第六步:启动服务

bash
# 终端1:启动后端
cd server
npm run dev

# 看到这个就成功了:
# 🚀 WorkMind Server 已启动
#    地址: http://localhost:3000

# 终端2:启动前端
cd frontend
npm run dev

# 看到这个就成功了:
# ➜  Local:   http://localhost:5173/

打开浏览器访问 http://localhost:5173,就能看到界面了。


每个模块的验证清单

✅ 模块一:智能对话

javascript
1. 侧边栏点「智能对话」
2. 顶部选择「技术顾问」角色
3. 发送消息:「Vue3 的 ref 和 reactive 有什么区别?」
4. 应该看到:流式输出(字一个个出现),右侧画像面板出现信息
5. 发同一个问题再次:第二次出现「⚡ 缓存」标签,秒回

✅ 模块二:RAG 知识库

javascript
前提:已配置 OPENAI_API_KEY + 已启动 Chroma

1. 准备一个 .txt 文件(写几段任意内容)
2. 左侧「上传入库」,拖拽上传文件
3. 看到「共 N 个片段」说明入库成功
4. 右侧问答框提问文档里的内容
5. 应该看到:回答带「📎 参考文档」来源标注

✅ 模块三:任务 Agent

javascript
1. 点击左侧示例任务「技术调研」
2. 任务自动填入,点击「▶ 执行任务」
3. 应该看到:步骤卡片依次出现,蓝色边框表示执行中
4. 展开步骤卡片,能看到工具的入参和出参
5. 最终回答在所有步骤完成后出现

✅ 模块四:内容工作流

javascript
1. 左侧选「周报生成」模板
2. 填入几条本周工作内容
3. 点「▶ 开始执行」
4. 应该看到:左侧节点图依次变绿,执行到「人工审核」时暂停
5. 右侧出现黄色审核面板,点「✅ 确认并继续」
6. 继续执行生成最终周报

✅ 模块五:ERP 报销请假

javascript
报销测试:
1. 点「💰 报销申请」
2. 输入:「上周去北京出差,高铁来回1200,住宿两晚1600,餐费三天共480」
3. 点「🔍 AI 解析」,看到自动填写的表单
4. 如果住宿超标,warnings 里应该有提示
5. 点「🚀 提交审批」
6. 右侧出现审批对话:主管问 → 申请人答 → 财务审核

请假测试:
1. 切换到「📅 请假申请」
2. 输入:「下周一到周三请年假,出去旅游」
3. AI 解析出日期、天数
4. 提交审批后出现:主管 + HR 两个角色

✅ 模块六:Prompt 调试

javascript
单次测试:
1. System Prompt 填:「用最简洁的语言回答,不超过两句话」
2. User Message 填:「什么是 JavaScript 的闭包?」
3. 点运行,看输出
4. 把 Temperature 调到 1.5,再运行,输出是否有变化?

A/B 测试:
1. 切到「A/B 对比」
2. A 填简洁版 Prompt,B 填详细版 Prompt
3. 同一个问题:「解释一下 async/await」
4. 点「▶ 开始对比」
5. 等待两边都有输出,看哪边赢了

模板:
1. 在单次测试里调好一个 Prompt
2. 点「💾 另存为模板」
3. 切到「模板库」,能看到刚保存的模板

✅ 模块七:用量看板

plain
1. 做几次对话/RAG/Agent 操作
2. 点「 用量看板」
3. 看指标卡:API 调用次数是否增加
4. 看明细表格:刚才的操作应该在里面
5. 把日预算改成 ¥0.01,进行几次对话
6. 预算进度条应该变黄或红

常见问题汇总

启动相关

Q:后端启动报错**Cannot find package '@langchain/langgraph'**

bash
cd server && npm install

如果已经安装了还报错,删掉 node_modules 重装:

bash
rm -rf node_modules && npm install

Q:前端启动正常,但发请求报 404

检查后端是否启动。Vite 的 proxy 只是把 /api 请求转发到 localhost:3000,如果后端没跑,就 404。

Q:后端启动报**DEEPSEEK_API_KEY 未配置**

.env 文件不存在,或者 Key 没填对。确认 server/.env 存在且内容正确:

bash
cat server/.env | grep DEEPSEEK
# 应该输出:DEEPSEEK_API_KEY=sk-xxxx

功能相关

Q:RAG 提问没有来源引用,或者说"未找到相关内容"

两个可能:

  1. Chroma 没有启动:curl http://localhost:8000/api/v1/heartbeat 检查
  2. 没有配置 OPENAI_API_KEY,入库失败了但没报错
Q:Agent 执行卡住不动

DeepSeek 偶尔会慢。等 30 秒,如果还没反应,刷新页面重试。

Agent 是调用真实的 DeepSeek API,网络延迟是正常的。如果一直卡,可以检查 .env 里的 Key 是否有效:

bash
curl https://api.deepseek.com/v1/models \
  -H "Authorization: Bearer 你的key"
# 返回模型列表说明 Key 有效
Q:工作流暂停后,刷新页面找不到了

当前用 MemorySaver,状态存在内存里,刷新服务端就丢失了。这是 demo 版本的局限。生产上要换 PostgresSaver,但需要配数据库。

Q:ERP 审批永远都是通过,想让它有时候驳回

server/src/services/erp/approval.js 里,finance 角色的 system prompt 加上明确的驳回条件:

javascript
finance: `...
必须驳回的情形(遇到立即驳回,不要通过):
- 酒店单晚超过 800 元且无额外说明
- 机票选了商务舱或头等舱
- 餐费单次超过 500 元
满足以上任一,必须输出"驳回"并说明原因。`

二次开发指南

学完整个课程,想在项目基础上做扩展,几个方向:

方向一:接入真实数据

替换模拟数据

Agent 的 searchTool 现在返回模拟数据,可以接真实的搜索 API:

bash
npm install @langchain/community
javascript
import { TavilySearchResults } from '@langchain/community/tools/tavily_search'

export const searchTool = new TavilySearchResults({
  apiKey: process.env.TAVILY_API_KEY,
  maxResults: 3,
})
替换内存存储为数据库

用户画像、会话历史、申请记录现在都在内存里,重启就没了。

接 PostgreSQL:

bash
npm install pg @langchain/langgraph-checkpoint-postgres
javascript
// 工作流的 MemorySaver 换成 PostgresSaver
import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres'
const checkpointer = PostgresSaver.fromConnString(process.env.DATABASE_URL)
await checkpointer.setup()

方向二:加新工具

server/src/services/agent/tools.js 里新增工具:

javascript
// 例:查询天气
export const weatherTool = tool(
  async ({ city }) => {
    const res = await fetch(`https://api.weather.com/...?city=${city}`)
    const data = await res.json()
    return `${city}今天:${data.weather},${data.temp}℃`
  },
  {
    name: 'get_weather',
    description: '查询某个城市的天气。当用户问天气相关问题时使用。',
    schema: z.object({
      city: z.string().describe('城市名,如"北京"'),
    }),
  }
)

// 加入工具列表
export const allTools = [
  searchTool,
  readDocTool,
  weatherTool,  // 新增
  // ...
]

方向三:新增工作流模板

server/src/services/workflow/workflows.js 里复制一个现有工作流,改改节点逻辑:

javascript
// 例:竞品分析工作流
export function buildCompetitorAnalysis() {
  const State = Annotation.Root({
    target:     Annotation({ reducer: (_, n) => n, default: () => '' }),
    competitors: Annotation({ reducer: (_, n) => n, default: () => '' }),
    analysis:   Annotation({ reducer: (_, n) => n, default: () => '' }),
    report:     Annotation({ reducer: (_, n) => n, default: () => '' }),
    humanFeedback: Annotation({ reducer: (_, n) => n, default: () => '' }),
  })

  // 各节点...
}

别忘了在 WORKFLOW_META 里加元数据(前端展示用)。

方向四:增强 ERP 审批

现在审批角色是固定的几个。可以让用户自定义审批流:

javascript
// 用户在前端配置审批链
const customFlow = ['manager', 'finance', 'director']

// 传给 runApprovalFlow
await runApprovalFlow(formData, formType, customFlow, onEvent)

课程技术栈对照表

学完这个项目,你掌握了哪些技术:

LangChain.js 核心用法

API 在哪里用的
ChatOpenAI 对话、RAG、Agent、工作流、ERP
withStructuredOutput(schema) 用户画像提取、ERP 解析、A/B 评分
model.stream() 所有流式输出场景
model.bindTools(tools) Agent 的工具调用
RecursiveCharacterTextSplitter RAG 文档分片
OpenAIEmbeddings RAG 向量化
Chroma RAG 向量库

LangGraph 核心用法

API 在哪里用的
StateGraph + Annotation Agent + 工作流
ToolNode Agent 自动执行工具
addConditionalEdges Agent 路由(有工具调用→继续,无→结束)
streamEvents Agent 步骤可视化
MemorySaver + interruptBefore 工作流 Human-in-Loop
graph.getState + updateState 工作流暂停状态检测和注入
Vue3 核心用法
API 在哪里用的
ref / reactive 所有响应式状态
computed 缓存命中率、过滤列表、派生状态
watch 消息变化时滚底
onMounted / onUnmounted 初始化数据、清理定时器
defineEmits 子组件向父组件传事件
<Teleport to="body"> Toast 渲染到 body
<TransitionGroup> Toast 进入退出动画
<Transition name="slide"> 工具卡片展开/折叠动画
:class
动态绑定
节点状态颜色、激活态样式
v-model.number range/number 输入框类型转换
Express + Node.js
技术 在哪里用的
SSE(text/event-stream) 所有流式接口
multer 文件上传
令牌桶限流 middleware/index.js
Zod 输入校验 所有接口的参数校验
错误分类中间件 utils/errors.js
结构化日志 utils/logger.js
优雅退出 SIGTERM/SIGINT 处理

项目数据流完整图

以「对话」为例,一条消息从前端到后端再回到前端的完整路径:

javascript
用户在 ChatInput 按 Enter
    ↓
chat store.sendMessage(text)
    ↓
messages 数组推入 userMsg(立即显示)
messages 数组推入 aiMsg(占位,content 为空)
    ↓
fetchStream('/api/chat/stream', { message, sessionId, role, userId })
    ↓ HTTP POST(前端)

    ─────── 网络 ───────

后端 chat.js 路由接收
    ↓
rateLimiter 检查令牌桶
    ↓
validateChat 校验 Zod Schema
    ↓
securityCheck 检查 Prompt 注入
    ↓
config.js 读取角色 system prompt
memory.js 读取用户画像,转为 profileCtx
拼接 systemPrompt = 角色 + 画像
    ↓
cache.get(systemPrompt, message) 查精确缓存
    ├── 命中 → SSE 推 cache_hit + 模拟流式输出
    └── 未命中 ↓
    ↓
memory.getHistory(sessionId) 获取会话历史
trimHistory(history, 2000) Token 感知截取
    ↓
chatModel.stream([system, ...history, humanMsg])
    ↓
    for await chunk:
        SSE 推 event: token, data: { token: chunk }
    ↓
history.push(humanMsg, aiMsg)
cache.set(systemPrompt, message, { content: fullReply })
extractAndUpdateProfile(userId, ...) ← 异步,不等
SSE 推 event: done, data: { inputTokens, outputTokens }

    ─────── 网络 ───────

fetchStream 的 onToken 回调
    ↓
aiMsg.content += token(响应式,Vue 自动重渲染)
    ↓
watch 检测到消息长度变化
    ↓
bottomEl.scrollIntoView() 自动滚底
    ↓
onDone 回调:aiMsg.streaming = false
              monitor.recordCall(...)
              chat.loadProfile()(刷新画像面板)

最后几点实用建议

关于 DeepSeek 的 API 配额

DeepSeek 新账号有免费额度,做实验足够了。项目上线前,建议去后台看一下当前账户余额,设好预算预警(模块七就是做这个的)。

关于 temperature 的调法

做对话类功能,默认 0.7 就好。做数据提取、结构化输出,用 0。做内容生成,可以试试 0.8 到 1.0。不要急着调,先看默认值的效果。

关于 RAG 的效果调优

如果回答质量不稳定:

  1. 先检查分片大小(chunkSize=500 是起点,可以试试 300 或 800)
  2. 看相似度分数,如果总是 0.3 左右说明文档和问题匹配度低,考虑换更精确的问法
  3. System Prompt 一定要写"只根据参考文档回答,文档之外的不回答"
关于生产部署

这个项目的数据都在内存里(Map、MemorySaver),重启就没了。真正上线前,这几个地方要换:

  • 会话历史:换 Redis
  • 用户画像:换 PostgreSQL
  • 工作流状态:换 PostgresSaver
  • 向量数据库:Chroma 可以持久化,配 volumes 就行

Docker Compose 文件已经写好了,加上这些服务就可以。


感谢

这个项目把课程里学的东西都用上了:SSE 流式输出、RAG 检索增强、Agent 工具调用、LangGraph 工作流、Multi-Agent 协作、结构化输出、缓存策略、成本监控……

每个功能都有对应的教学文档,把"为什么这样写"说清楚,不只是"代码是什么"。

有问题欢迎提 Issue 或者在课程社群里讨论。