本文档已过时——下列内容仅为开发过程中的初步设想。
与需要被严格被遵守的“项目开发规范”相比,本文档是相对松散的规范。
- 这些松散规范的价值类似系统各部分之间的 API。遵守这些规范能让我们绕开任务依赖关系的限制并行工作并避免最终合并各人的并行工作成果时对接失败。
- “松散”指的是这些规范相对模糊并且可能被推翻。我们尚未深入开发,所以我们的讨论只能浅尝辄止、挂一漏万,我们讨论出的规范只能松散,从而为不确定性留出空间。
Pinia store
你应该设置专门的 store 来集中管理你的组件/页面渲染所用的数据。比如,渲染计划列表、计划详情、计划编辑面板、计划流程图等功能涉及的组件/页面都需要使用同一组关于计划的数据,那么你该在 plan
store 里集中维护这些数据,让这些组件/页面都从 store 里访问这份共享的数据。这么做的好处有:
- 集中管理数据,代码更清晰。
- 各个组件可以很轻松地访问 store 里用于自身渲染的数据。如果不使用 store,你可能需要在每个组件的
<script>
块里都向后端发一份请求以获取自身所需的数据,或者是利用相对复杂的props
传递数据。 - store 中数据的生命周期比
<script>
中数据的生命周期长。如果你只用<script>
的话,一路由其中的数据就没了,再想用这些数据只能重新从后端请求,性能差。 - 可以轻松实现局部刷新。假如有多个组件共用 store 里的同一个状态,其中组件甲利用
action
更新了状态的值,这些组件都会自动更新渲染。
关于项目和计划系统,我建议用 plan
store 维护所需状态。用一个 store 而不是用 project
和 plan
两个 store 是因为计划从属于项目,二者的数据联系很密切。
- 我初步设想在
plan
store 中维护projects
、plans
、currentProject
和currentPlan
四个状态。 - 其中前两个是数组、
Map
一类的对象,列表等组件利用这两个状态渲染概况信息(后端不会直接把所有项目/计划的一切信息都传过来,否则数据量太大,性能差)。 - 后两个用于“详情”和“编辑”等场景,它们会记录用户正在浏览的一个特定项目/计划的所有信息。路由到详情页时需要及时向后端发送请求以更新
current××
的值。 - 进行增删改查等操作时除了向后端发送请求外也要及时更新 store 中对应的状态,以实现各组件的局部刷新。
对于人格、记忆等功能,也请认真考虑是否利用 store。
LLM
LLM 输出任务的处理流程
- 未来可能会插入新环节。比如,如果我们不想仅仅用提示词控制输出的创造力等级的话,我们可以在请求 DeepSeek-R1 前多请求一次轻量级模型/DeepSeek-R1,这次请求专门要求模型提出有创意的点子,我们把这一步得到的点子作为对 DeepSeek-R1 的正式请求的上下文的一部分,要求 DeepSeek-R1 在写作时尝试融入这些点子。
结构化上下文
我之前一直说要用“结构化提示词”,是受到了 Chatbot 用户视角的思维惯性干扰。
“结构化提示词”是一个字符串,是只能通过 OpenAI 等厂商提供的 Chatbot 界面与 LLM 对话的用户能间接控制的 messages
数组内的最后一个 role: "user"
对象的 content
值。“结构化提示词”通常用 markdown 各级标题把提示词分为 ## goal
、## background
、## tone
和 ## attention
等部分,在不同场景下改变框架填充物而不改变这些框架(标题)。
回顾诤略参谋的需求,我们自然会想到给提示词划定 ## background
(项目上下文)、## goal
(核心目标)、## 严格等级
、## 创造力等级
、## REMEMBER
(记忆,不要用 ## memory
)和 ## personality
(人格)等部分,根据用户的设置决定应该在这些标题后拼接的字符串的内容。如下图所示,结构化提示词只是在控制这个小小的红框。
但我想以后我们应该说“结构化上下文”,而不是“结构化提示词”。因为我们是调用 API 的开发者,我们可以控制 messages
中的所有对象。我们可以把项目上下文放到第一个对象的 content
里,把核心目标放到第二个对象的 content
里,把计划正文放到第三个对象的 content
里……把工作命令放到最后一个对象的 content
里。我们也应该这样做。“结构化上下文”如下图所示。
不同的任务的结构化上下文组成不同。比如,“生成流程图”的结构化上下文不需要 ## background
、## goal
、## REMEMBER
和 ## 人格
等部分,只需要 ## 计划正文
和最后的工作命令;“生成计划”的结构化上下文肯定不需要 ## 计划正文
部分。
其他
- 我还是建议直接命令要求 DeepSeek-R1 输出纯文本,不要带伪代码、表格、超链接、markdown list 这些东西,禁止一切 markdown 语法符号。
- 我们可以争取在一部分场景下保留 markdown 输出和渲染,但是涉及到结构化输出的场景(比如贝格拉夫的“生成改进方案”功能,那个页面会像 Office 批注一样展示结果)肯定不能用 markdown。
- 需要注意 LLM 输出长度超过限制的情况。比如,计划正文除了 LLM 生成外还允许用户手动填写,手动填写使用的
Field
/Txt
要求输入长度不超过X
字,那你也得考虑是否该限制 LLM 输出的计划正文的长度也不超过X
字。 - 有时间可以尝试为模型加入联网搜索能力,这会显著扩展模型的能力边界,突破 knowledge cutoff 的限制。
- 对于贝格拉夫的“生成改进方案”功能,我建议并行地请求“全局评价”和“针对性改进建议”。“针对性改进建议”可以分为多类,比如“句有歧义”类、“事实错误”类、“遗漏”类和“可取之处(鼓励/继续保持)”类。在前端用
Switch type="checkbox"
控制批注栏同时显示哪几类建议。
统计
假设数据库里存储了一堆原始信息,你有两种利用这些信息算出统计量的值的方法:
- 使用数据库的聚合函数。还可以额外设置索引以提高效率。
- 利用 Spring Data JPA 的能力获得一堆实体类对象,然后用 Java 循环等代码计算统计量。
第一种方法的性能开销远远小于第二种方法。
现在,假设用户路由到了“统计信息”页面,页面上的可视化图表组件需要从后端获取统计数据用以渲染。我们粗略分析一下可能的方案:
- 后端收到统计信息
GET
请求时,现场利用原始数据从零算出各统计量的值。- 优点:与方案二相比不需要额外存储空间,而且算出的数值较精确。代码集中,也基本不需要在其他人写的函数中追加语句。
- 缺点:这个
GET
请求的处理耗时可能偏长。
- 开一张表维护统计信息(每一种统计信息对应表的一列,和用户是一对一关系),把统计信息的计算开销分摊到平时的请求里。比如当用户添加了一份新计划时,顺带更新对应的平均值(新
avg
= ( = ( =( 旧avg
× \times × 旧数目 + + + 新值 ) / )/ )/ ( ( (旧数目 + 1 ) +1) +1))。- 优点:对统计信息的
GET
请求响应快。 - 缺点:需要专开一张表维护统计信息,算出的数值的精确度不如方案一(比如浮点数每次计算都有误差,你在新请求里基于已经包含误差的旧浮点数迭代出新浮点数,误差可能变大)。
- 优点:对统计信息的
- 开一张表维护统计信息,把统计信息的计算开销分摊到平时的请求里。比如当用户添加了一份新计划时,顺带用原始数据重算对应的平均值(新
avg
= Σ = \Sigma =Σ 所有数据 / / /总数目)。- 优点:对统计信息的
GET
请求响应快,而且算出的数值较精确。 - 缺点:平时请求的处理耗时可能会显著增长(不过你可以把计算任务放到另外的线程里,那就没什么影响了——但是你要考虑“计算任务尚未完成就收到了
GET
统计数据的请求”时该怎么办),因为每次都要从头算(注意与方案一区分,方案一是从头算所有统计量,方案三是从头算这个请求会影响的几个统计量)。需要专开一张表维护统计信息。
- 优点:对统计信息的
核心差异在于方案一是 LAZY 的而另外两个方案不是。但是说实话,我们的玩具级项目不会感受到方案一里 GET
统计数据的请求处理耗时偏长,也很难感受到方案二里的误差(我们也不关心小数点三四五六位的值)。所以请自行选择方案。你也可以采用混合方案、物化视图、定时任务……可以多和 Gemini 2.5 Pro/Deep Research 聊聊。
提醒:
- 注意限制小数点位数,避免过长的小数破坏网页布局。
- 你可能需要在已有的类中追加标记用字段以帮助统计系统工作。