小叶想做一个阅读卡片工具:从课程资料中选一段文字,请 AI 整理成简洁的摘要,自己核对以后把它作为正式卡片保存。卡片既能在页面里阅读,也能导出,遇到不确定的说法还可以回到原文。

起初,页面能显示卡片,导出按钮也能生成文件。可是小叶很快遇到两件奇怪的事:有的卡片文字还在,原文却找不到了;为了让页面看起来清爽而缩短预览后,导出的完整摘要居然也跟着变短。他检查每个功能,都能看到输入和输出,却不清楚问题在哪一次交接中发生。

上一章讨论怎样划分软件责任。这一章继续往里看:信息怎样经过这些部分,一处工作依靠另一处提供的什么条件,又为什么会发生看似无关的连带变化。你将学会沿着数据走一遍系统,并据此判断哪些联系应该保留,哪些依赖需要调整。

本章另设阅读卡片工具作为虚构教学案例,资料文字、编号和程序行为都是为讲解而构造的,不代表前一章的软件已经完成这些功能。理解下面的关系不要求先搭建整个应用,也不需要记住一套复杂画图规范。

一、数据流让我们看见,信息怎样成为最后的结果

数据流关注数据从哪里取得,经过哪些处理,在哪里保存,又被送到哪里使用。在小叶的工具里,资料文字是输入,摘要整理是一种处理,保存后的卡片供页面和导出使用。这些内容之间的联系,构成了理解系统的一条路线。

这里的“流”不表示信息只能像水一样向前移动。程序可以读取同一份资料多次,把一个结果交给两个使用者,也可以保留旧结果稍后再用。原文被读取后通常仍留在原处,不是到了摘要步骤就必须从来源中消失。

从这个角度看,“资料读取完成”和“卡片能够使用”之间还有很多工作。读取可能只取得了文字,却没有保留它属于哪个版本;摘要可能已经生成,却把原文的限制条件省掉;导出可能取得了一个文本字段,但那个字段装的是短预览。每一步都有输出,并不说明输出足以支持下一步的用途。

小叶需要问的便不只是“这个功能是否成功”,而是“成功取得了什么”。同样一段文字,如果表示完整原文、AI 的摘要候选或页面上的简短预览,后续应该怎样使用就不同。只看它们都是字符串,会错过真正影响判断的差别。

很多数据处理工具也把工作组织为来源、转换和去向。Microsoft 对数据流组件的说明提供了这种具体视角。我们借用它来观察小叶的工作,不需要为了本章安装某个数据平台。

沿数据流阅读程序,还有一个实际好处:它使遗漏容易定位。假如最后导出的卡片没有出处,可以从导出拿到的内容往前查,看看整理后的卡片有没有出处,再看读取时有没有取得。问题可能发生在读取、转换或导出中的某一处,而不是因为最后页面报了问题,就一定由页面造成。

数据流图也不能替代所有设计说明。它能帮助我们看清信息去向,但谁有权更改它、哪个条件满足后才允许导出,还需要继续说明。先跟住信息,再补出这些条件,才能从“有几个步骤”走到“步骤为什么能组成可用结果”。

二、先跟住一段原文,再看它怎样变成卡片

我们给小叶选中的这段资料一个固定身份:资料12、版本2、段落4。这里的12、2、4只是标识,用来认出资料、版本和片段,不代表页数、字数或某项计算。

这段虚构原文是:

课程报告应说明研究问题、资料来源与主要结论。引用外部材料时应标注出处。只有已经获准延期的同学可以补交。

资料读取部分取得的不应只有这几句话。既然工具要支持回查,就还要保留这段文字来自哪里。可以把文字和来源身份放在一条来源记录中,由它保存“哪段内容对应哪个出处”的关系。

接下来,整理部分把原文交给 AI,取得这样一份摘要候选:

报告要交代问题、来源和结论,引用需注明出处;补交须先获准延期。

这份摘要和原文表达方式不同,来源关系却应该保持。整理部分可以建立一条编号为 K08 的卡片记录,保存候选摘要,并关联到资料12、版本2、段落4。K08用于识别这张卡片,不是原文的位置;同一段原文以后也可能产生不同用途的卡片。

本例约定:AI 刚生成的内容标为“待核对”,小叶对照原文检查后,才能把这份卡片内容标为“已确认”。页面可以显示待核对的草稿,但导出为正式卡片时,只使用符合本轮确认要求的内容。这个约定先写清,后面才能判断哪些交接算正确。

三种信息便有了不同的位置。

信息 本例中保存的内容 后续用来做什么
来源记录 完整原文,以及资料12、版本2、段落4 回查依据,核对摘要是否忠实
卡片记录K08 摘要、来源关联、当前核对状态 保存和使用这张卡片的内容
页面短预览 从卡片摘要临时取得的开头部分,带省略提示 让页面列表便于浏览,点击后查看完整卡片

这张表并不是要求一定建立三张数据库表。它先区分三种用途,具体可以怎样存放,属于实现选择。小叶至少需要保证:把摘要改得更顺,不会把来源记录里的原文也改掉;把短预览做得更短,不会让完整卡片跟着丢字。

来源、版本、片段位置等描述内容来历的信息,常被称为元数据。这个词不神秘。它的作用是让使用者知道一段内容应该怎样理解和查找。在当前任务里,出处信息虽然不是摘要正文的一部分,却是摘要能够被核查的条件。

如果只留下摘要,等到导出时再让 AI 猜出处,问题并没有得到补救。它可以给出一个看起来合理的标题,却不能凭摘要唯一确定当初使用的资料版本和段落。应在有可靠来源对应的阶段保留这项联系,而不是丢掉以后再要求下游重建。

于是,这条数据流的基本关系就清楚了:原文支持生成候选,候选需要回到同一原文核对;页面可以展示候选草稿,正式导出则使用本轮已确认的卡片。不同阶段产生不同内容,但完成任务所必需的来源对应继续保留。

三、转换可以改变表达,不能随意改变信息的含义

数据处理经常需要转换。读取文件时把页面内容变成可处理的文字,整理时把长段变成摘要,展示时把完整摘要缩成预览,都属于转换。转换的价值正在于让同一份材料适合不同用途,并不是所有内容都应原封不动地传到底。

问题在于,哪些改变是当前用途允许的,哪些改变会破坏后续判断。

比如,K08的摘要把“研究问题、资料来源与主要结论”压缩为“问题、来源和结论”,在当前上下文中仍能对应原意。它也保留了“补交须先获准延期”的条件。小叶可以据此回查,判断简写有没有遗漏要求。

现在另设一份改坏的摘要:

报告说明问题、来源、结论,支持补交。

这份文字短了,却把补交的适用条件删掉了。即使来源身份仍然完整,读者也可能把它理解成任何人都能补交。问题发生在内容转换,而不是出处字段丢失。因此,能够回查来源和摘要符合来源含义要分别检查。

再看短预览。页面列表里显示“报告要交代问题、来源和结论……”可以是合理设计,因为省略号告诉使用者还有内容,并且可以打开完整卡片。可是如果导出也只拿到这一小段,再把它当作完整摘要保存,原本只为浏览而作的省略,就被错误地带进了正式成果。

这两种情况的差别来自用途。短预览没有承诺覆盖完整要求,完整卡片则应保留本次整理和确认后的内容。小叶应让后续部分知道自己拿到的是哪一种,而不是都叫“文本”,再由各处根据长度自行猜测。

转换时,信息还可能发生合并。假设两张卡片都带有“报告要求”的标题,标题相同不等于它们来自同一段原文。如果导出为了去重只比较标题,就可能把来源不同的卡片合成一张,或者把其中一张的出处贴到另一张的摘要上。

要决定能否合并,先要明确这次想消除什么重复:是同一张卡片被重复加入,还是同一资料段落产生了多个表达,或者只是标题碰巧一样。这些情况需要保留的内容不同。卡片编号和来源身份能够帮助区分它们,单靠显示名称通常不够。

这种检查方式也适用于 AI 自动整理表格、归纳访谈和制作报告。简化表达本来是有用工作,条件、来源和归属却可能正是结论成立所需的信息。合理的转换应该说明保留什么、改变什么,以及接收结果的一方据此能作出哪些判断。

四、画出数据去向,再补上决定动作的条件

下面把小叶的卡片工具画成一张简化的数据流示意图。白色直角框表示处理活动,蓝色带顶线框表示保存的记录,箭头标出传递的内容。它说明本章的信息关系,并不规定全部动作的先后顺序。

阅读卡片工具的来源记录、整理核对与两种输出
图 S04-01|阅读卡片工具的来源记录、整理核对与两种输出

沿图看,资料读取把原文与来源身份写入来源记录;卡片整理读取这些信息,形成待核对候选;核对过程中,候选与相应原文都需要能够取得。卡片记录保留摘要、来源关联和核对状态,页面展示与导出再按各自用途读取。

分支说明同一份卡片可以供两个去向使用,不表示页面展示完成后才能导出。反过来,也不能仅凭两条分支,就断定它们在实现中一定同时执行。图中的线表达信息供给,完整动作顺序要根据使用场景说明。

控制流关注什么动作在什么条件下发生。对当前工具来说,取得待核对卡片后可以展示草稿;只有本次内容已经确认,才允许导出为正式卡片。核对状态本身是一项数据,导出过程读取并判断它,才形成相应的控制作用。

这一区别能够解释一种常见遗漏。AI 生成的图把“核对状态”画进卡片记录,小叶便以为草稿不会被导出。实际上,如果导出程序拿到摘要就直接写文件,从来不查看这个状态,数据虽然保存了,限制却没有执行。

相反,若导出知道“要确认后才能继续”,但收到的数据里没有足以判断当前内容是否确认的信息,它也不能靠页面颜色或按钮文字来猜。完成控制条件需要相应数据,而保存了数据还需要明确谁使用它。

小叶可以用一次操作把两者接起来:打开K08时看见待核对摘要,对照原文完成检查,确认后保存相应状态,再发起导出。若中途修改了摘要,就要判断先前确认是否仍适用于现在这份内容。不能把“这张卡片曾经确认过”自然等同于“当前文字已确认”。

这也说明数据流图为什么常常需要配一小段文字。图可以告诉我们状态从哪里来、传到哪里;使用条件则说明某个值会让流程继续、暂停还是给出说明。两者共同支持行为,单独一张看起来完整的图还不足以保证程序按要求运行。

五、依赖要说清,究竟依靠了对方什么

依赖表示某部分要完成自己的工作,需要另一部分提供某种能力、数据或条件。卡片导出依赖卡片记录中有它需要的完整摘要和来源信息;回查原文依赖资料身份能够找到正确版本;页面展示依赖拿到的状态和内容能够对应。

依赖的方向要按正在表达的关系判断。导出使用卡片读取能力,可以说“导出依赖卡片读取”;与此同时,数据是从读取部分返回给导出的。若一张图的箭头表示数据去向,另一张表示谁使用谁,同一合作中的箭头方向就可能不同。

上一章已经区分过使用关系与返回消息。本章更进一步:即使知道谁使用谁,还应说明使用了什么条件。只画“导出依赖卡片”,对分析一次变化仍然太宽。

假如导出只需要卡片的完整摘要、出处和核对状态,它通常不需要知道页面宽度、卡片背景色或手机列表显示几行。如果这些显示字段改变,是否影响导出,取决于导出是否真的使用了它们,不能因为都装在一个记录里,就一概认定任何改动都必然传播。

反过来,导出若偷偷把“预览文本”当成完整摘要,它便依赖了一项不合用途的条件:页面提供的短文本恰好足够完整。起初卡片很短时,这个问题可能没有显现;后来页面缩短显示,它才暴露。所谓“突然坏了”,常常是原先未写清的依赖条件不再碰巧满足。

Daniel Jackson 对模块依赖的讨论提出,应关注依赖所依靠的假设和约定,而不仅记录两个模块之间有没有连线。论文原作者版本中的这一视角,有助于把“可能影响很多地方”变成可以逐项核对的问题。

依赖链也不意味着每次变化都要求所有下游改代码。资料读取内部换了存放办法,如果向外提供的原文、身份与查找行为仍然符合约定,卡片整理可能不必修改。但若资料内容更新,即使读取方式没变,原有摘要是否仍适用也需要检查。沿链追踪时,应分清需要配合修改的实现与需要重新判断的已有结果。

因此,画出关系以后,小叶可以在重要连线旁写一句具体话:“导出需要已确认的完整卡片内容”“整理需要与文字对应的来源身份”“回查需要定位到当时版本”。这些话不必成为冗长文档,却能让 AI 的改动说明与实际任务联系起来。

同样,也不要只看代码中有没有直接调用。两个部分可能都读写同一个文件,即使彼此从不调用,仍然共同依靠文件中字段的含义。写入部分把一个字段换成别的用途,读取部分就可能出错。依赖有时藏在共享内容和约定里,需要沿数据的读写关系查找。

六、耦合的关键,是这份依赖让双方绑得多紧

耦合描述软件部分之间相互依赖的程度和性质。它让我们进一步判断:为了正确使用对方,一个部分必须知道多少事情;对方发生哪些变化时,自己就需要配合修改。SEI 对设计问题的说明也将耦合与不同软件部分的相互依赖联系起来。

在小叶的工具中,可以比较两种导出办法。

第一种办法直接读取页面正在使用的一整份内部记录,知道哪个字段存预览、哪个标记代表展开、页面怎样拼来源文字。导出要取得完整摘要,还得沿页面的内部结构找到相应位置。页面一改这些做法,导出就可能跟着改。

第二种办法通过明确的卡片读取入口取得完整摘要、来源关联和核对状态。页面怎样折叠长文字、怎样排列按钮,由展示部分自己处理。此时导出仍然依赖卡片内容,但可以少依赖页面的内部决定。

这项改进减少的是不必要的共同知识,并不是把两个部分变得完全没有联系。如果导出不再取得来源,以此换来“少一个字段”,回查目标就被破坏了。低耦合的意义在于保留完成任务所需的依赖,并让它们更明确、更不容易被无关变化打断。

有些耦合来自格式。比如原来把来源写成“资料12|版本2|段落4”这一条显示字符串,导出靠分隔符拆开它。后来界面为了好读,改成“资料12,第2版,第4段”,内容对人仍然清楚,导出却解析失败。导出依赖了面向阅读的排版格式,而不仅依赖三项来源信息。

如果共同使用的是分开的资料、版本、片段字段,界面再把它们排成句子,导出就可以直接按字段处理。这样的安排让两个输出共享来源含义,同时各自决定表现形式。真正要共同维护的是来源信息的解释,而不是恰好用了哪一个分隔符。

双方还需要共同理解数据的含义。假如整理部分只要读到资料文件,就把候选卡片标为“已确认”;导出部分却认为这个标记表示小叶已经核对过当前摘要,于是把卡片正式导出。两边读写的是同一个状态名称,指的却不是同一件事。这样,未经人工核对的摘要就可能越过本例的正式导出门槛。

这时只换一种数据格式没有用。小叶需要把状态代表什么、由谁在什么条件下设置讲清楚,并让两处使用同一个含义。减少不必要格式依赖和明确必要业务含义,是两项相连但不同的工作。

所以,判断耦合要看双方需要共同了解什么。只传入一个参数,却把整份全局记录交给接收者,仍可能让它依赖许多内部状态;几个含义明确的输入,反而可能让合作关系更清楚。连线数、参数数或文件大小可以作为观察线索,不能单独决定这份联系是否合适。

七、共享可变数据,会让一次修改从另一个入口显现

前面主要讨论取到了什么。现在再看一项容易被数据流箭头掩盖的区别:把信息交给另一个部分,究竟是给了一份独立内容,还是让它也能修改同一份记录?

我们先明确本例的实现假设:资料读取和卡片整理在同一个程序里工作,两个部分拿到的是同一份可修改记录的访问入口。记录中有一个“正文”字段,最初存着资料12、版本2、段落4的完整原文。回查功能也通过这份记录显示原文。

为了省事,整理代码没有另外建立卡片内容,而是把 AI 的摘要直接写回这个“正文”字段。整理结果看起来出现了,但回查随后从同一份记录读取,自然也只能读到摘要。没有人专门调用“修改原文页面”,那个页面看到的内容却已经变了。

这就是共享可变数据造成的影响:多个入口指向同一份能够被修改的内容,一处原地修改,另一处再读时会看到修改后的结果。在这个小例中,程序内部用于回查的记录被覆盖,磁盘上的原资料文件并没有被这项操作改写;不能把两者混为一谈。

程序设计课程讨论过两个名字指向同一个列表或字典。放到系统设计里,需要继续问:哪项责任可以修改这份记录,其他使用者期待它维持什么含义?MIT 关于可变数据和别名的课程说明也指出,共享的可变对象会使不同部分的修改相互影响。

共享记录被覆盖与按用途分开的来源、卡片和短预览
图 S04-02|共享记录被覆盖与按用途分开的来源、卡片和短预览

图左的问题不能靠换一个变量名解决。新名字如果仍指向同一份记录,写回摘要时,原来的访问入口仍会看到变化。图右则先区分用途:来源记录保留原文,整理结果写进卡片记录,卡片通过来源身份关联原文。内容彼此有关联,但摘要的修改不需要覆盖原文。

小叶随后还要检查页面预览。即使已经把来源和卡片分开,如果页面为了缩短显示,仍把短预览写回卡片的完整摘要字段,导出就会读取被缩短的内容。这是同一种问题发生在另一个边界:展示为了自己的临时需要,修改了其他使用者依靠的正式内容。

更合适的安排是,页面从完整摘要生成自己的预览结果,保留完整卡片作为导出的来源。这里不必承诺所有内容都彻底复制;需要分开的,是会因临时显示用途而发生变化的那一部分。若内部还有可变的嵌套记录,就要继续看实际修改会到达哪一层,不能只凭“已经复制”便认定全都独立。

共享本身也有合理用途。小叶在编辑页正式修改了卡片,并按约定保存,阅读页随后应能取得更新后的内容。若为了避免所有联系,让两个页面永远各用一份独立旧副本,反而无法满足这一需要。

因此,关键不是禁止共享,而是把共同维护的内容、临时派生的内容和允许修改的入口分清。正式编辑通过约定更新卡片;短预览只改变自己的展示结果;原文的来源记录按其版本要求维护。不同性质的修改各有归属,其他部分才能判断自己会受到什么影响。

八、一项内容更新以后,哪些已有结果仍然有效

来源和卡片分开保存后,另一个问题就出现了:原文更新时,已经保存的卡片应该怎样处理?分开能够阻止摘要覆盖原文,却不会自动替小叶管理两者的有效关系。

仍以K08为例。它来自资料12、版本2、段落4,小叶确认的是依据这个版本形成的摘要。后来资料发布了版本3,其中补交规则发生变化。此时版本2的卡片作为旧版阅读记录仍有自己的意义,但不能在不说明的情况下被展示成依据版本3整理的当前要求。

这里涉及衍生结果:它依据其他内容产生,因此它的适用范围与所依据的条件有关。摘要依据某个资料版本,确认状态又针对某一份摘要内容。这些关系不同于“文件还在不在”,而是回答“现在使用这份结果,仍符合原来给它的含义吗”。

一种含混做法是只保存资料名称。文件更新后,回查入口默认打开最新文件,卡片上却保留旧摘要和旧确认标记。三个界面元素看起来都正常,组合起来却把不同时间的内容接在一起。小叶甚至可能以为自己已经核对过最新版,因为页面没有告诉他版本已改变。

要改善这个问题,先保留生成卡片时的来源身份。想保留旧版笔记,就让它明确指向旧版;想把卡片更新成最新版,就重新取得新内容,判断摘要需要怎样调整,并核对新摘要。当前支持哪一种,应由这个工具的使用要求决定,不能让文件名相同替我们作决定。

确认状态也应与被确认的内容相对应。小叶核对完K08后,又把摘要里的补交条件删掉了,先前的确认不能自动证明这份新文字仍然正确。可以把修改后的卡片重新标为待核对,或者用别的明确办法记录哪一份内容经过确认;无论采用哪种做法,都要让使用者能够识别当前状态。

也不是每一个界面变化都要让内容确认失效。仅把卡片背景改成浅色,原摘要、来源和含义没有改变,就没有理由说原来的内容核对全部白做。变更影响什么,仍然要回到实际依赖:确认针对的是内容及其依据,还是页面的显示样式。

有了这层认识,小叶就能区分两件常被混在一起的事:防止无关修改影响其他部分,以及在必要条件变了时让相关结果得到更新。前者要求隔开无用依赖,后者要求保留必要关联。把所有联系切断,不能同时解决这两项任务。

九、循环依赖为什么容易让局部工作难以独立进行

有时,小叶沿依赖关系往前查,会绕回起点。例如卡片整理为了生成标题,直接调用页面展示里的一段排版函数;页面展示又为了取得可显示的卡片,调用卡片整理。两边各有自己的工作,却都需要使用对方的一部分实现。

如果这里说的确实是模块对代码或能力的使用,就形成了循环依赖:沿着“谁使用谁”的关系走,可以回到出发的部分。它不是因为卡片数据传给页面以后,页面向用户返回结果,就自然形成了代码上的环。

循环可能带来几种具体困难。只想试验卡片整理时,却必须连页面一起准备;改页面标题规则时,整理输出也可能受到影响;两边初始化或调用时需要满足什么先后条件,变得更难单独说明。这些影响需要根据实现核查,但已经足以促使小叶重新检查职责和使用关系。

在本例中,先要问整理为什么需要页面排版。如果所谓标题只是从已整理内容取得一段描述,整理可以交出内容,页面再决定怎样排版。原先不必要的“整理使用页面”这条关系便有机会去掉。

如果两处确实需要同一项稳定的标题生成规则,也可以把这项共同能力放到双方都能使用的位置。这样做的依据是它有清楚的共同用途,而不是看见一个环就机械新建一个“公共中心”,把很多无关工作继续塞进去。

还有一种看似断开循环的办法:双方不再直接调用,而是都读写一个全局文件。代码图上的直接连线少了,但它们如果仍依赖彼此写入的字段和先后顺序,合作困难并没有消失,只是换了藏身处。需要把共享数据关系也画出来,才能判断改动是否真的减少了牵连。

循环关系并非仅凭一个符号就足以判定整个程序不可用。某些交互需要双向通知,可能通过明确约定合理组织。当前学习重点是识别实际使用关系,说明它造成了什么理解、测试或修改困难,再判断能否移除其中不必要的一段;不必先背下一整套设计模式名称。

十、把一个改动沿着实际依赖传一遍

现在,小叶可以回到开头的问题,判断一次修改会影响哪里。有效的办法不是要求 AI 把所有模块都改一遍,而是把具体变化沿实际使用的条件逐处检查。

先看缩短页面预览。当前约定是页面从完整卡片生成临时短预览,导出直接读取完整卡片。那么修改预览长度应主要影响展示,导出的摘要内容不应因此改变。若实测导出仍变短,就需要检查页面是否写回了完整摘要,或者导出是否仍从预览字段取内容。

这两种原因不能只用“数据有问题”概括。前者是展示修改了共享正式内容,后者是导出依赖了不合用途的数据。一个需要调整修改范围,另一个需要调整读取来源;定位到具体关系,修复才会针对问题发生的地方。

再看来源格式变化。若只是页面把“资料12|版本2|段落4”显示得更自然,内部三个来源字段的意义没有变,整理和导出便没有必要依赖这个排版改变。若实际来源身份的含义发生变化,就要检查使用这些身份的查找、整理、核对和导出,而不能把它当成纯显示修改。

第三项是摘要内容变化。更换一句话、删除一项条件,可能影响既有核对是否适用。它需要重新判断内容,而非只看卡片编号K08没有变,就保留原来的正式状态。相反,改一个与内容无关的边框颜色,不必顺着所有关系把整张卡片当成重新生成。

可以把当前检查整理为一张小表。

具体变化 首先核对的关系 本例中应当保持或重新判断什么
短预览更短 页面是否只改变临时展示;导出是否读取完整卡片 完整摘要及来源不因预览而丢失
来源文字换排版 输出是否使用独立来源字段,还是拆显示字符串 来源身份含义不变时,各输出仍能使用
修改卡片摘要 旧确认针对哪一份内容 当前文字是否还在已确认范围内
来源发布新版本 卡片关联哪个版本,页面怎样说明 保留旧版记录或更新核对,选择要明确
内部存储办法改变 外部依靠的内容、标识和行为是否保持 没有破坏约定时,不扩大为所有下游必改

这张表说明的并不是未来永不出错的保证,而是一种有依据的检查方向。依赖图帮助小叶找可能受影响的部分,具体输入输出和使用约定帮助他判断影响是否真的发生。两者都要联系到当前实现,不能只凭一张旧图就断言修改范围已经查全。

若最终结果缺少某项信息,可以沿数据流找它最后一次明确存在的位置。K08的来源在读取记录里存在,整理输出中却没有了,就先检查整理这次转换;如果卡片记录完整,导出文件里才消失,就看导出怎样选择字段。这种追踪能把“最后看见问题的地方”和“第一次丢失信息的地方”分开。

还需要验证修改后的完整结果。修正导出读取来源以后,应检查短预览仍正常、导出仍保留完整摘要和出处、回查仍打开相应版本。局部修正是否生效与原任务是否重新接通相互联系,但不能因为一个字段终于出现了,就省掉它所支持的使用行为。

十一、把依赖说清以后,再让 AI 分工完成

这些知识会改变小叶交给 AI 的任务方式。与其只说“帮我解耦一下”,他可以给出一条具体记录和用途:原文用于回查,摘要用于卡片,短预览只用于浏览;导出要保留完整摘要和来源,并依据当前核对状态决定是否作为正式卡片输出。

接着让 AI 说明实际路径:资料读取交出了哪些信息,整理保留了什么,页面会修改哪里,导出从哪里取值。若多个部分共同使用一份可变记录,还要指出谁能写、写入后哪些使用者会看到变化。这个说明应能落到实际代码或数据安排,不能只是一张理想流程图。

安排分别修改时,可以把任务边界写成可观察结果。页面任务是改变预览表现,完整卡片内容应按原用途保存;导出任务是使用完整卡片的约定字段,不能靠页面当前显示文字拼结果;整理任务是形成新的摘要内容,并保留其来源对应。每项任务都有清楚对象,组合以后也有共同核查的依据。

这不是要求小叶先把全部设计写完才能使用 AI。他可以先选一段资料、一张卡片和一个导出结果,把整条关系走通。发现来源丢失或修改越界时,再针对这一处调整;比一开始增设大量抽象层更容易知道改进有没有解决实际问题。

在本章的小项目里,最后可以作出这样的安排:来源记录保存原文及其版本身份,整理另建带来源关联的卡片;小叶核对当前摘要以后再确认,修改摘要时重新判断确认状态;页面只派生短预览,导出读取完整卡片;来源更新时明确选择保留旧版笔记还是更新内容,不混接版本。

随后用开头那两件事来验收这个安排:一张卡片从原文走到导出,来源是否始终可回查;页面缩短预览以后,原文与完整卡片是否仍各自保持应有内容。通过这些实际结果,才能判断这次调整是否把必要联系接好,并把无关修改隔开。

学会数据流、依赖与耦合,最终要获得的就是这种辨认能力:同样一段数据在不同阶段表示什么,一个部分实际依靠另一部分的什么条件,哪些变化应当被隔开,哪些变化又必须让相关结果知道。这样面对 AI 生成的一组功能时,就能继续追问它们怎样共同形成可靠的成果。

练习与参考分析

练习一:有摘要,为什么仍不能补齐出处

小叶导出K08时,发现只保存了摘要正文和“课程报告要求”这个标题,没有资料身份、版本和片段位置。AI 建议搜索标题最相近的文件,再把找到的段落填成出处。这个办法是否已经恢复了原来的来源关系?应当怎样继续处理?

**参考分析:**标题相近或内容相似只能提供候选线索,不能证明那就是生成摘要时使用的资料版本和段落。若把猜到的位置直接写成确定出处,就把新的推测冒充原来的数据关系。当前有摘要,并不足以唯一反推出其来历。

小叶应查找生成时是否还有来源记录、交接记录或其他能够可靠确认对应的信息。找不到时,就需要明确这张卡片的来源尚未确认;也可以选择一份已经确定的资料重新整理和核对,而不能说原对应已经自动恢复。修复程序时,应在读取与整理交接阶段保留必要身份,让导出使用已有的可靠关联。

练习二:新建了预览变量,为什么导出仍变短

页面和导出原先共同访问同一份可修改的卡片记录。页面新建一个名叫“预览”的变量,但它仍指向这份记录,然后把记录中的完整摘要字段替换为开头的一小段。导出随后读取这个字段,也得到了短文字。问题仅仅是变量名不够清楚吗?

**参考分析:**名字虽然不同,两处仍使用同一份可修改记录。页面写的是共享的完整摘要字段,不是独立的展示结果;导出读取到短文字符合题面给出的共享关系。改名不能改变实际修改范围。

应让页面从完整摘要生成独立的短预览,保留正式卡片内容,再确认导出实际读取完整字段。如果预览内部还有共享的可变内容,需要进一步检查它是否会写回正式记录。这里不要求一律深复制所有数据,而是区分临时展示与正式编辑各自应改变什么。

练习三:两段程序没有互相调用,就没有耦合吗

一个程序把卡片来源写入文件,另一个程序读取该文件导出。写入方原来分别保存资料、版本、段落,后来为减少字段,把三项合成一条供人阅读的句子。读取方仍按原字段查找,因而无法导出出处。两段程序彼此没有直接调用,是否可以说它们不存在依赖问题?

**参考分析:**不能。它们通过文件共享信息,读取方依赖写入方提供特定含义和结构的内容。直接调用只是依赖的一种表现,文件格式与字段约定也会让两者联系起来。

应先明确哪种对外交接继续有效,再协调写入和读取。若显示句子只为了方便人阅读,可以保留结构化来源信息,再另外生成句子;若确实要改变共同格式,就需要检查所有使用者如何迁移,而不能因为代码图上没有相互调用箭头便只改一处。具体采取哪种方案,要由用途决定。

练习四:原文升级以后,已确认卡片还能怎样使用

K08根据资料12的版本2整理并核对。资料后来发布版本3,补交要求改变。工具没有改摘要,却让K08的原文按钮默认打开版本3,并继续显示“已确认”。怎样解释这里的问题?是不是只能删除旧卡片?

**参考分析:**问题是把旧摘要、旧确认和新来源混接在一起。K08曾被确认,并不能证明它反映了版本3。打开的文件存在、编号仍叫K08,都不能代替版本与内容之间的对应。

旧卡片并非一定要删除。如果用途是保留阅读记录,可以明确它依据版本2,并支持相应回查;如果用途是表达当前要求,就要根据版本3更新摘要并重新核对。题面没有指定必须采用哪一种,但无论怎样选择,都不能继续把不同版本的内容悄悄组合成一项当前已确认结果。

练习五:去掉一条线,是否就改善了结构

整理部分原来调用页面的一段标题生成代码,页面又调用整理取得卡片。AI 为了让图中不再出现双向使用,改成双方各自在本地维护同一套标题生成规则。以后标题含义一变,两处都得同步修改。怎样评价这次调整?

**参考分析:**直接调用环被去掉,但共同规则变成两份实现,仍有必须同步理解和修改的关系。图上少了一条线只说明某一种使用关系改变了,不能独自证明整体维护更容易。

应先确认两处是否确实需要同一规则。如果整理只需交出内容,标题排版可以归页面,就能移除不必要的责任;如果双方确实都要使用同一种稳定标题含义,可以给共同能力明确归属。选择应减少实际重复和无关依赖,而不是只满足图形不成环的外观。

练习六:为什么不能看到一条依赖链就全部重写

资料读取向卡片整理提供原文及来源身份,导出又使用整理后的卡片。现在只改变资料读取内部的存放办法,向外得到的内容、身份含义和操作行为均保持原约定,调用者也没有直接读取内部存放结构。有人认为既然导出间接依赖资料读取,便必须连整理和导出全部重写。这个结论有依据吗?

**参考分析:**题面已经给出外部依赖的条件得到保留,因此不能仅凭图上存在路径就认定全部下游代码都必须改变。依赖链指出可能需要关注的关系,具体影响要看哪个使用条件被破坏。这里没有给出破坏条件的证据。

可以集中修改资料读取,再验证它仍满足约定,并检查从原文到卡片、导出的完整使用是否保持。验证不等于预先重写所有下游;若检查发现实际还有未记录的内部依赖,再依据新证据调整范围。这样既不因图有连线而扩大改动,也不把“实现内部改了”当成免检理由。