我们在列计划时经常犯错——无心的和有意的错。
无心之失通常源自认知局限和思维惯性。因为认知局限,我们误把管中窥豹当总览全局,把舍近求远当最佳路径,把天马行空当脚踏实地。我们在打地基时掺入误解,在立框架时挂一漏万;因为思维惯性,我们沿着相同的路径一错再错却浑然不觉,上穷碧落下黄泉也找不出错因。
有意之错通常源自怠惰。我们路径依赖,抱残守缺,明知故犯地重走那条舍近求远的路;我们收集了大量行之有效的方法论却从不落实它们,因为那太费心费力;我们一边清楚记着“细致”“居安思危”这些道理,一边忍不住说“就这样吧”“大概如此”“马马虎虎”“应付一下”。我们知道这样的计划会埋下隐患,可我们很难做到永远严格要求自己。
那就请 LLM 帮我们制定和审查计划吧。它是虎视眈眈的对手,是吹毛求疵的参谋。有无心之失时它提醒我们补充,犯有意之错时它迫使我们消除一切马虎。它是对世界知识的有损压缩,因此与它合作将扩展我们的认知边界,补充我们缺失的视角和遗漏的因素,尽可能地识别和纠正我们的误解;它是第三方,因此它没有当局者迷的思维惯性;它忠诚地按提示词行事,于是它殚精竭虑,永不懈怠。我们懈怠时,它为我们兜底。
于是我们打算利用 DeepSeek-R1 创造一位“诤略参谋”。“诤”表直言劝诫,“略”表计划。“参谋”则是说,它是盟友,不是敌人;是辅助,不是主导。
- 作者:魂兮归乡(优快云:@theColumnSpace)
- 为获取最好的阅读体验,建议在飞书平台阅读此文:一、项目伊始
目录
需求分析:用户视角下的走一步画一步
我认为需求有两大类,一类是对用户提供的服务,另一类是高质量服务引出的在性能和交互等细节上的要求。前者对着屏幕瞪眼就能基本想清楚,而后者只能在实践中显露。我们正处在列计划画饼的阶段,我们离实践还差得远。可我们真的不能到那时才发现要考虑一些贯穿全局的细节,因为那时我们已积重难返。
我想立刻揪出这些需求,所以我的方法是“模拟实践”。
- 以某个页面 P 0 P_0 P0 为“出发页面”,绘制这个页面的 UI 草稿 S 0 S_0 S0。之后代入正在 S 0 S_0 S0 上的用户视角,想象用户期望在 S 0 S_0 S0 中怎样交互、在各种交互后期望获得什么回应、什么时候因为内容量、语义或逻辑要求需要离开这个页面路由到其他页面 P x P_x Px、怎样路由到 P x P_x Px。
- 补充绘制这些“期望”以及页面 P x P_x Px 的 UI 草稿 S k S_k Sk。之后同理,代入用户视角,走一步,画一页,再在新画的页面上走一步,再画一页,直到收敛。
我们会立刻发现这种方法虽是为了需求分析,却捎带产生了 UI 布局、交互和路由等方面的设计草案。而且这完全可以称作“交互驱动的页面设计”,已经抢了博客“四、UX 设计”的很多工作。
第一版(重点记录过程)
注意:这些 UI 草稿在我完成博客“三、数据通讯(下)”后已经过时,所以对于第一版,我只重点记录“走一步画一步”这个方法和我的分析过程而非分析出的需求。对于第二版,因为第一版一节中已经讲明了这种需求分析方法,我只重点记录分析出的需求。
首页不是我的 P 0 P_0 P0,这个才是。因为在本节前我讨论的是借助 LLM 制订计划,那么我脑中自然对这个场景最清晰。在画图过程中,我确定了有侧边栏的核心工作区布局和页面 01(计划详情页)提供的功能(展示计划信息、修改计划名、修改计划正文、配置计划偏好、配置计划上下文、根据计划正文生成 mermaid 流程图和“检查”计划正文),也如愿发现了很多细节要求:
- 有多级设置,它们的作用域不同,存在覆盖关系,越有针对性的设置的优先级越高。
- 提高效率,计划正文出现差异时才让 LLM 生成新的 mermaid 代码,而不是每次点击按钮都重新生成 mermaid 代码。
- 需要检查用户输入的“计划正文”的语义。博客“三、数据通讯(下)”讨论了这一点。
- 需要弹窗提醒。博客“三、数据通讯(下)”处理了这个需求。
- 如果
textarea
的内容被更新却尚未保存,在路由时用路由守卫提醒用户。博客“四、UX 设计”处理了这个需求。 - 限制计划名的长度以免用户起长名破坏页面布局。
P 0 = P_0 = P0= 页面 01。“检查”指的是让 LLM 基于原有的计划正文给出多条修改建议。这些建议以批注形式显示,内容量和空间占用较大,不宜挤在 P 0 P_0 P0,所以单开一页 P 1 P_1 P1。接下来我们点击“检查”按钮,进入 P 1 = P_1 = P1= 页面 03。
这一步我们确定了“检查”的工作形式——LLM 给出若干条引用原文的修改建议,用户按需采纳其中的建议,之后点击“按采纳项更改”,LLM 会结合原计划正文和被采纳的修改建议生成新版计划正文。浮现的细节要求有:
- 精确、正确地高亮原文。开发者要思考如何让 LLM 准确指出对原文的引用、程序该怎样得知 LLM 做了引用。我们应该要要求 LLM 格式化输出。确定高亮块的起止点大概需要字符索引,最简单的思路是对每条引用遍历匹配,可这听起来性能不好,未来需要思考……
- 绘制侧边批注和高亮块之间的连线。如果
overflow-y: scroll
导致高亮块和批注中只有一个在 viewport 中,我们该怎么办? - 没有建议时直接庆祝,不要强行凑建议。
- 不同类型的修改建议,有局部修改建议,如有必要还有全局建议(LLM 觉得你的计划有根本问题,比如 X-Y Problem)。
- LLM 只提醒用户存在歧义和模糊处,而不去亲自猜测要从可能项中选哪个。
- 用户在 LOADING 时该做什么。博客“三、数据通讯(下)”给出的答案是任务系统和弹窗系统。
- 允许用户拿着完全相同的计划正文和采纳建议“一键重抽”。
我们可以在页面 01 编辑计划名或删除计划。
“确认删除”和编辑面板该设计成弹窗还是专页?这是个问题。我倾向于把“确认删除”设为 Modal,把轻量级的编辑面板(比如“编辑计划名”)也设为 Modal。博客“三、数据通讯(下)”里实现了 GlobalModalWrapper
和 Deletor
。
接下来我们思考页面 01 里一条计划都没有的场景。这会是初来乍到的用户第一眼看到的东西,也会是把计划删光了的用户看到的东西。
- 需要“空空如也”提示。
- “添加新计划”需要专门的页面,涉及 OCR 技术。
最后,侧边栏底部的全局设置该包含哪些选项,计划内设置又该包含哪些选项?
- 主题(需要配色方案系统,详见博客“四、UX 设计”)、LLM 记忆、LLM 创造力等级、LLM 严格程度、LLM 人格……
- 需要统计一些信息,用 ECharts 可视化。需要讨论如何处理统计数据——收到请求后 LAZY 地现场计算还是划出空间预存统计数据,把统计数据的计算开销分摊到平时的每次更新里?
- 需要
Switch
、带档位的滑动条等组件。
总结上文,这就是我们的第一版需求分析成果了:
第二版(重点记录需求)
本节的内容是我在完成了博客“三、数据通讯(下)”后的工作交接阶段写的,所以第二版比第一版会具体很多,也会推翻一些第一版的设计。这次的“需求”与其说是“用户对开发者的需求”,不如说是“我对队员的需求”。我会用下面这些示意图准确传达我的想法,尽力避免模糊和误解。
严格等级
控制参谋对计划提建议时的严苛程度。一共有六档,档位越高越“无情”。下面的表格仅供参考,真正实现时可以减少档位、增大不同档的差异……
创造力等级
记忆
- 抄袭了 OpenAI 的记忆功能。当然我们的这个就是一玩具,效果远远比不上人家。
- 一些高频复用的背景信息,比如“我是一名数学老师”暗示 LLM 多从数学和教育角度出发思考问题,比如情感提示词“如果你做得好,我会给你十美元小费;如果你做得不好,我会失业”。
- 其实就是数据库开了一张
Memory
表(一用户对多记忆),每次调用 LLM API 时都把这些记忆自动追加到提示词里。再通俗点儿说就是程序在与 LLM 的每次对话里都自动帮你说了一段背景信息,这段信息你不需要自己每次都手打了。
多个计划共用一个项目的上下文和项目配置。有点儿类似多个线程共用一组数据。
技术选型
分工
项目任务书上的分工很仓促,有些零碎和碎片化,会带来本不必要的沟通和依赖约束,我对此很不满意。所以接下来我会用由粗到细逐步迭代的方式重新分工。
1. 需求分析 - 面向非开发者
只保留目标服务,把异常处理等写给程序员看的名词全扔掉。不要被细节淹没。
1.1 极简版本
- 在一个项目内手动增、删、改、查多个计划 → 引出项目和计划管理
- 让 LLM 根据你填的背景帮你写计划 → 引出项目上下文管理
- 让 LLM 对你的计划提建议、帮你改计划 → 诤略参谋核心
- 让 LLM 帮你画计划的流程图
- 让 LLM 帮你分析计划的预算和风险
- 让 LLM 按你的偏好行事 → 引出三级设置和拼接提示词
1.2 展开版本
2. 任务分析与划分 - 面向开发者
2.1 初步分析但不划分
2.2 纯靠功能划分(不考虑人数和工作量均衡等课程考核指标)
2.3 评估工作量、合并部分项目
2.4 依赖关系图
每次课程设计分工都让我很头疼……
- 我们在刚开始做项目时不清楚采用的技术,难以评估技术难度,也理不清依赖关系。
- 同时考虑公平、依赖关系、并行效率很难。截止目前我依旧不擅长分工。
我想看看能不能有多个连通分量——大概没有——那就找割边。要是割边也没有,就只好大出度结点了。
所以,重要结点有:
- 地基部分(E5 可以通过先确定 API 绕过)。
- C2 - 上下文管理。
- B5 - 各级设置覆盖。
- D1 - 计划增删改查。
2.5 依赖关系图(任务书版本)
2.6 依赖关系图(重分工版本)
工作类似拓扑排序的方式推进。你可以根据依赖图判断你需要与谁沟通合作。
原则:
- 人人都有可以展示的有关 LLM 的部分。
- A:快思考、防越狱、语义审查、严格程度、人格提示词 × 1。
- B:写计划、结构化提示词和结构化输出研究、吹毛求疵、重写计划、人格提示词 × 1。
- C:内容概括、上下文窗口研究、人格提示词 × 1。
- D:记忆、结构化提示词、mermaid 提示词、人格提示词 × 1。
- E:“说人话”、人格、预算分析、风险分析、人格提示词 × 1。
- 每个人分得的任务在逻辑上联系密切。
- 计划管理是项目管理的一部分,二者联系密切,不考虑工作量的话其实最好交给一个人。可是我们得考虑工作量,所以特意交给联系密切的 dogdogw 和 epiphany 狂人。
下表是简化的表述。
项目文件结构
项目文件结构主要受三个因素影响:
- 还没有写代码就有的显而易见的想法。比如
front-end
、back-end
、database
同级,比如把 Vue 组件分为component
、view
和layout
三级。三级划分也与路由设计高度相关,详见博客“四、UX 设计”。 - LLM。我并不熟悉 Git 以及项目里自带的大部分配置文件,所以 .gitignore 和 pom.xml 基本上是 LLM 写的,为了确保万无一失我反复地向 LLM 确认有无疏漏。
- 写博客“二、数据通讯(上)”、“三、数据通讯(下)”和“四、UX 设计”涉及的代码时被迫设立的层次结构。比如 CSS 和 Pinia 相关的结构。
critistrat/ # 诤略参谋 CRITISTRAT 项目 Git 仓库
├── .gitignore
│
├── front-end/ # 前端 Vue3 项目
│ ├── public/
│ │ ├── imgs/ # LOGO 等图像
│ │ └── sounds/ # 音效
│ │ ├── ambience/ # 氛围音效
│ │ └── ... # 一个文件夹对应一个组件的音效
│ ├── src/ # 源码
│ │ ├── components/ # 可复用组件
│ │ │ ├── FormWrapper.vue # 博客二中的三件套
│ │ │ ├── Btn.vue # 博客二中的三件套
│ │ │ ├── Field.vue # 博客二中的三件套
│ │ │ ├── Txt.vue # Field 变种
│ │ │ ├── TaskBtn.vue # 博客三中的 Btn 变种,进行 LLM 相关任务用
│ │ │ └── ...
│ │ ├── views/ # 渲染在非顶层 RouterView 中的面板
│ │ ├── layouts/ # 渲染在顶层 RouterView 中的面板
│ │ │ ├── Welcome.vue # “报上名来”页
│ │ │ ├── WorkSpace.vue # 核心工作区
│ │ │ └── NotFound.vue # 404 页
│ │ ├── config/ # 全局设置(其实就是把常量汇集起来)
│ │ ├── utilities/ # 工具函数或对象
│ │ │ ├── http.js # axios 请求/响应配置
│ │ │ ├── validators.js # Field 组件使用的 validator(前端校验函数)
│ │ │ ├── ocrHelper.js # 利用 OCR 向 Txt 追加文本相关函数
│ │ │ └── tools.js # 常用工具函数
│ │ ├── router/ # 路由层级配置
│ │ ├── stores/ # 响应式状态集中管理
│ │ │ ├── plan.js # 项目、计划与计划下参谋生成项相关状态
│ │ │ ├── user.js # 自动登录和主题等全局配置相关状态
│ │ │ ├── memory.js # 记忆相关状态
│ │ │ ├── personality.js # 人格相关状态
│ │ │ ├── txt.js # Txt 的“注册表”,路由守卫据此给出未保存时路由 Modal 提醒
│ │ │ ├── task.js # 任务系统核心逻辑,包含轮询
│ │ │ ├── toast.js # 博客三中弹窗系统的 Toast 部分,与 axios 响应拦截器协作
│ │ │ ├── notification.js # 博客三中弹窗系统的 Notification 部分,与任务系统协作
│ │ │ └── modal.js # 博客三中弹窗系统的 Modal 部分
│ │ ├── styles/ # 全局样式和变量、函数、动画、响应式定义
│ │ │ ├── main.scss # 联系各 partial、全局重置、定义 CSS 颜色变量、可复用样式
│ │ │ ├── _variables.scss # 颜色、字号、圆角等变量
│ │ │ ├── _mixins.scss # 共用小代码段
│ │ │ ├── _keyframes.scss # 共用关键帧动画
│ │ │ └── _breakpoints.scss # 未来实现响应式网站用
│ │ ├── App.vue
│ │ └── main.js
│ ├── index.html # 在这里设置资源引用、favicon 等
│ ├── persona-viz.html # 官方人格深度可视化介绍页,详见项目文档九
│ ├── package.json
│ └── vite.config.js # 插件、服务器代理等配置
│
├── back-end/ # 后端
│ ├── src/main/java/com.critistrat.backend/
│ │ ├── service/ # 业务层:写逻辑,供 controller 调用
│ │ │ ├── EmailService # 发送邮箱验证码
│ │ │ ├── LLMService # One API 异步请求和响应处理,详见博客三
│ │ │ ├── ContextFileService # 文件上传、删除、阈值判断等
│ │ │ ├── AliDocumentParsingService # 利用阿里云文档大模型解析用户上传的文件
│ │ │ ├── PlanTransactionalUpdater # 应对计划生成异步任务出现竞态条件的情况
│ │ │ ├── SuggestionPositionCorrector # 为参谋提出的针对性建议确定原文起止索引
│ │ │ └── ...
│ │ ├── controller/ # 接口层:RESTful API,接收和响应 HTTP 请求
│ │ │ ├── AuthController # 一些 Jwt 不检查的登录前操作
│ │ │ ├── LLMController # POST /api/llm/xx 提交任务,详见博客三
│ │ │ ├── TaskController # 任务系统初始化和轮询自用,详见博客三
│ │ │ └── ...
│ │ ├── entity/ # 实体类
│ │ │ ├── User
│ │ │ ├── Project
│ │ │ ├── Plan
│ │ │ ├── EmailCode # 邮箱验证码
│ │ │ ├── EmailUsage # 枚举类,反映邮箱验证码的用途
│ │ │ ├── Task # 任务系统自用
│ │ │ ├── TaskStatus # 枚举类,反映任务状态
│ │ │ └── ...
│ │ ├── repository/ # JPA 接口
│ │ ├── format/ # 数据传输模型
│ │ │ ├── BizCode # 业务码
│ │ │ ├── StdResponse # 统一响应格式
│ │ │ ├── Conversation # 结构化上下文
│ │ │ ├── CCO # Chat Completions API 返回数据的格式
│ │ │ ├── CCR # 调用 Chat Completions API 时需先把 Conversation 对象转为 CCR 对象,再进行序列化
│ │ │ └── ...
│ │ ├── exception/
│ │ │ ├── GlobalExceptionHandler # MVC 层全局异常处理器
│ │ │ ├── ErrorAggregator # /error API,其实就一 RESTful Controller
│ │ │ ├── BizException # 建议在异常情况下用 BizCode 实例化并抛出之
│ │ │ ├── AutoLoginFailedException
│ │ │ ├── OutputFormatException # LLM 输出不符合格式异常
│ │ │ └── PromptInvalidException # 语义审查不通过异常
│ │ ├── config/
│ │ │ ├── SecurityConfig # Security 过滤器链配置
│ │ │ ├── WebClientConfig # 配置调用 LLM API 的对象,类似 axios 的配置
│ │ │ └── WebConfig # 配置 /avatars/** 静态资源映射
│ │ ├── security/ # Security 过滤器链相关,详见博客二
│ │ │ ├── CustomAuthenticationEntryPoint
│ │ │ │ # 自定义入口点,会把异常请求转发到 /error
│ │ │ └── JwtAuthFilter # JwtAuthFilter 相关配置
│ │ ├── utility/ # 工具
│ │ │ ├── JwtUtil # 签发、校验 Jwt 等函数
│ │ │ └── Utility # 其他工具函数。大部分 LLM 提示词集中于此
│ │ └── BackEndApplication.java # 启动类
│ ├── pom.xml # Maven 配置(第三方库)
│ ├── resources/
│ │ ├── application.yml # 后端配置(数据库、邮箱、端口、API、文件、阈值等)
│ │ └── ...
│ ├── uploads/ # 用户上传的文件
│ │ ├── avatars/ # 人格使用的头像
│ │ └── k/ # 用户以文件形式上传的、项目 k 的上下文
│ └── ...
├── database/
│ └── data.db # SQLite 数据库文件
└── README.md # 显示在 Gitee 仓库首页的介绍
- 执行一切 Git 指令时务必保证工作目录是
/critistrat
。 application.yml
文件被我故意写到了.gitignore
里,因为内含 API Key。请自行从群文件中下载并把它放到对应位置。- 你可以把一切都写到
controller
里而完全忽略service
,但我建议service
负责具体逻辑而controller
主要负责调度。如果你发现两个 API 的函数体重叠度高,你应该把公共部分写到service
的一个函数里,并在两个 API 中分别调用那个函数。 format
除了一些特殊格式外,主要分 VO 和 DTO 两类,DTO 是你要根据前端请求的请求体定义的数据结构(多用于 API 的@RequestBody
形参),VO 是你根据向前端传回数据的需要定义的数据结构。BizCode
很重要,它能声明式地控制前端 axios 响应拦截器的行为,包括是否弹窗、给出什么信息等。- 对 CSS 代码的解释:
特别感谢 LLM,否则我会在恐惧、怀疑、随机碰壁和破罐破摔里搭起一个漏洞百出的项目起点。LLM 帮我梳理文件结构和依赖项的例子:
问题
- 有一种厂商在开发布会,背对着“精美”的 PPT 用一堆语不惊人死不休的名词包装了一个平平无奇的东西的感觉。
- 诚然,这只是一个学校课程设计,不能要求它有什么作用,但是我还是觉得“我为什么不直接去找个 Agent 直接帮我从零跳到做完呢?我为什么要关注中间计划呢?我为什么要先找你给出一个计划,之后我再去亲自落地呢?”
- 我知道我这种感觉不对——相当多的项目涉及对复杂物理世界的精确实时感知与交互、长时间的海量的上下文信息、不容任何偏差的安全要求,而当今的 Agent(比如非常惊艳的 Deep Research)只能在数据域里活动,即使是当今具身智能也离这个要求差得远。所以多得是只能由我们亲自根据计划去落地的项目,而不是向 AI 许个愿就完成了的项目。诤略参谋是有用的,但我无法打消这种感觉。
- 真实原因是——我认为“你干嘛不直接把你的计划复制到任何一个现成的 Chatbot 聊天框里去问模型建议呢?这不就是诤略参谋这个壳的本质吗?为这么简单的东西费力套个壳好像有些幽默”。不过这么说有些绝对,一方面这终究是简化了计划管理相关的操作,另一方面我看到过其他“Agent(类似 GPTs 这种只是把提示词捆进去的家伙)”,那个 Agent 与不用它时相比只是简化了一次复制粘贴操作。可是它的火热程度说明我们不能把这种需求不当回事。
- 我还是觉得分工很差劲。我对分工一窍不通。这次的进步是我想到了利用图的结构和拓扑排序分析,可是我感觉帮助不大。我需要在未来进行专门的学习和实践……感觉我对“分工参谋”的需求比对“诤略参谋”的需求更强。