news 2026/10/2 15:10:51

本地优先AI智能体实战:AnythingLLM私有知识库部署与检索调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
本地优先AI智能体实战:AnythingLLM私有知识库部署与检索调优

1. 为什么本地优先的 AI 智能体值得你花时间折腾

第一次接触 AnythingLLM 是在一个做企业内部知识库的项目里。当时客户的核心诉求很直接:文档不能出内网,但又要让大模型能基于这些文档回答问题。市面上大部分方案要么是纯云端 SaaS,要么是开源但部署链路长得让人头大。AnythingLLM 吸引我的点在于它把“本地优先”这四个字落到了实处——模型可以跑在本地,向量库可以跑在本地,连对话记录都存在本地 SQLite 里,整个数据闭环完全在你自己的机器上。

简单说,AnythingLLM 是一个开源的 AI 智能体与文档对话工具。它能把你手头的 PDF、Word、Markdown、网页链接等资料“喂”进去,然后基于这些资料进行问答、总结、推理。它支持接入多种大模型后端,包括本地运行的 Ollama、LM Studio,也支持 OpenAI、Anthropic 等云端 API。你可以把它理解成一个“自带知识库的 ChatGPT 客户端”,但这个客户端完全归你掌控。

这篇文章适合三类人看:一是想在自己电脑上跑一个私有知识库的开发者;二是需要给团队搭建内部文档问答系统的技术负责人;三是单纯对 AI 智能体感兴趣、想找一个能快速上手折腾的开源项目的爱好者。不管你之前有没有接触过 LLM 应用开发,只要你会用 Docker 或者愿意装一个桌面应用,就能跟着走下来。

我写这篇东西的出发点很简单:网上关于 AnythingLLM 的介绍大多停留在“它是什么”的层面,但真正落地时会遇到的一堆细节——比如向量库怎么选、嵌入模型怎么配、文档分块策略怎么调、本地模型和云端 API 怎么混用——很少有人系统讲清楚。我踩过的坑,尽量都写进来。

2. 核心架构拆解:它到底是怎么运转的

2.1 三层结构:前端、服务端、存储层

AnythingLLM 的架构可以用“三层两接口”来概括。前端是一个 React 应用,负责工作区管理、对话界面、文档上传这些交互。服务端是 Node.js 写的,承担了绝大部分逻辑:文档解析、文本分块、向量化、检索、提示词组装、模型调用。存储层则分三块——向量数据库存文档的语义向量,SQLite 存工作区配置和对话历史,文件系统存原始文档。

两接口指的是:一个是对外的 LLM 接口,可以指向 Ollama、OpenAI、LocalAI 等;另一个是对内的嵌入接口,负责把文本转成向量。这两个接口可以独立配置,也就是说你可以用 OpenAI 的嵌入模型配 Ollama 的对话模型,反过来也行。这种解耦设计在实际使用中非常关键,后面会详细讲。

为什么采用这种架构?核心考量是“可替换性”。LLM 这个领域变化太快,今天最好的模型下个月可能就被超越了。如果把模型调用写死在业务逻辑里,换一个后端就要改一堆代码。AnythingLLM 把模型调用抽象成 Provider 层,新增一个后端只需要实现对应的接口适配器。对用户来说,就是在设置页面里换个选项的事。

2.2 向量数据库的选择逻辑

AnythingLLM 内置了 LanceDB 作为默认向量库,同时支持 Chroma、Pinecone、Qdrant、Weaviate 等。这个选择不是随便定的。LanceDB 是一个嵌入式向量库,不需要单独起服务,数据直接存在本地文件里。对于个人用户和小团队来说,这意味着你不需要额外维护一个数据库服务,装完就能用。

但如果你要处理百万级以上的文档块,或者需要多节点共享向量数据,LanceDB 就不太够了。这时候可以切换到 Qdrant 或 Weaviate 这类支持独立部署的向量库。我在一个项目中用 Qdrant 替换了默认的 LanceDB,原因是那个项目需要多个 AnythingLLM 实例共享同一份向量数据,嵌入式方案做不到。

这里有个容易忽略的点:不同向量库的相似度计算方式可能不同。LanceDB 默认用余弦相似度,Chroma 也是,但有些库默认用欧氏距离。如果你在切换向量库后发现检索结果明显变差,先检查一下距离度量是否一致。这个坑我在第一次切换时踩过,排查了半天才发现是度量方式的问题。

2.3 文档处理流水线

文档从上传到能被检索,中间经历了一条完整的流水线:解析、清洗、分块、向量化、入库。每一步都有讲究。

解析阶段,AnythingLLM 支持 PDF、DOCX、TXT、Markdown、HTML、CSV 等格式。PDF 解析用的是 PDF.js,对纯文本 PDF 效果不错,但遇到扫描件就无能为力了——它不做 OCR。如果你有大量扫描版 PDF,需要先用 OCR 工具转成文本再上传。

清洗阶段主要是去掉多余的空白、页眉页脚、乱码字符。这一步看似简单,但对检索质量影响很大。我试过直接把一份带大量表格的 PDF 扔进去,结果分块后表格内容全乱了,检索出来的片段根本没法看。后来改成先把 PDF 转成 Markdown,手动整理表格结构,效果好了很多。

分块策略是整条流水线里最需要调优的环节。AnythingLLM 默认的块大小是 1000 个字符,重叠 200 个字符。这个默认值对一般文档够用,但对技术文档和法律合同就不太合适。技术文档里一个完整的函数说明可能超过 1000 字符,被截断后语义就不完整了。法律合同里一个条款往往就是一个完整的语义单元,按固定字符数切分容易把条款切断。

我的经验是:技术文档块大小调到 1500-2000,重叠 300;对话记录或短文本块大小 500-800,重叠 100;结构化程度高的文档(如 FAQ)可以按段落切分,块大小不固定。AnythingLLM 目前不支持按段落智能切分,但你可以通过预处理文档来实现——把每个段落用空行隔开,它就会倾向于在空行处切分。

3. 从零开始的完整部署实操

3.1 三种部署方式的选择

AnythingLLM 提供了三种部署方式:桌面应用、Docker 容器、源码运行。选哪种取决于你的使用场景。

桌面应用最简单,下载安装包双击就行,支持 Windows、macOS、Linux。它内置了一个精简版的运行环境,不需要你单独装 Node.js 或 Python。适合个人用户快速体验,但缺点是配置灵活性差一些,比如你想换向量库或者调服务端参数,桌面版给的空间有限。

Docker 部署是我最推荐的方式,兼顾了易用性和灵活性。官方提供了 Docker 镜像,一条命令就能跑起来。你可以通过环境变量控制几乎所有配置项,也方便做数据持久化和备份。

源码运行适合需要二次开发的场景。你可以改前端界面、加自定义的文档解析器、或者接入内部的身份认证系统。但需要自己管理 Node.js 依赖和构建流程,维护成本最高。

我个人的选择是:日常使用跑 Docker,需要改代码时切到源码模式。桌面版只在给别人演示时用一下,因为安装最快。

3.2 Docker 部署的完整步骤

先拉取镜像。官方镜像在 Docker Hub 上,直接docker pull mintplexlabs/anythingllm就行。但国内网络环境下可能会很慢,可以配置镜像加速器,或者从其他源拉取。

接下来准备数据目录。AnythingLLM 需要持久化的数据包括:SQLite 数据库、向量库文件、上传的文档、环境配置文件。我习惯在宿主机上建一个目录,比如/opt/anythingllm/data,然后挂载到容器里。

启动命令的关键参数有这么几个。端口映射默认是 3001,你可以改成其他端口。存储挂载要把宿主机的数据目录映射到容器的/app/server/storage。环境变量方面,STORAGE_DIR指定存储路径,LLM_PROVIDER指定默认的模型后端,EMBEDDING_ENGINE指定嵌入引擎。

docker run -d \ --name anythingllm \ -p 3001:3001 \ -v /opt/anythingllm/data:/app/server/storage \ -e STORAGE_DIR=/app/server/storage \ -e LLM_PROVIDER=ollama \ -e OLLAMA_BASE_PATH=http://host.docker.internal:11434 \ -e EMBEDDING_ENGINE=ollama \ -e VECTOR_DB=lancedb \ --add-host=host.docker.internal:host-gateway \ mintplexlabs/anythingllm

这里有个细节:如果你在 Linux 上跑 Docker,容器内访问宿主机的 Ollama 服务需要用host.docker.internal,并且要加--add-host参数。macOS 和 Windows 的 Docker Desktop 自带这个解析,不用额外加。这个坑我在 Linux 服务器上部署时踩过,容器里一直连不上宿主机的 Ollama,排查后发现是 DNS 解析的问题。

启动后访问http://你的IP:3001,第一次会引导你创建管理员账号。这个账号只存在本地,不走任何第三方认证。创建完成后进入设置页面,配置模型和嵌入引擎。

3.3 本地模型接入:Ollama 配置要点

Ollama 是目前最方便的本地模型运行工具,AnythingLLM 对它支持得很好。但有几个配置项容易出错。

首先是 Ollama 的监听地址。默认情况下 Ollama 只监听127.0.0.1:11434,这意味着只有本机能访问。如果你在 Docker 里跑 AnythingLLM,容器内的127.0.0.1指向的是容器本身,不是宿主机。所以需要把 Ollama 的监听地址改成0.0.0.0:11434。在 Linux 上可以通过 systemd 配置OLLAMA_HOST=0.0.0.0:11434,macOS 上通过launchctl setenv OLLAMA_HOST 0.0.0.0:11434。

其次是模型选择。AnythingLLM 需要两个模型:一个对话模型,一个嵌入模型。对话模型推荐用qwen2.5:7b或llama3.1:8b,这两个在中英文场景下表现都不错,7B 参数在 16GB 内存的机器上能跑。嵌入模型推荐nomic-embed-text,它专门为检索优化过,比用对话模型做嵌入效果好很多。

这里要强调一点:嵌入模型和对话模型是两回事。我见过有人为了省事,用同一个模型既做对话又做嵌入,结果检索质量惨不忍睹。嵌入模型需要把文本映射到一个高维向量空间,让语义相近的文本在空间中距离近。对话模型的目标是生成流畅的文本,两者的优化目标完全不同。用对话模型做嵌入,相当于让一个作家去当图书管理员,不是不能干,但干不好。

显存或内存不够怎么办?7B 模型用 4-bit 量化后大概占 4-5GB 内存,嵌入模型占 500MB 左右。如果机器只有 8GB 内存,可以选 3B 参数的模型,比如qwen2.5:3b,效果会打折扣但能用。再不行就用云端 API 做对话,本地只跑嵌入模型,这样内存压力小很多。

3.4 云端 API 接入的混合方案

本地模型的好处是数据不出门,坏处是效果受限于硬件。如果你有一台带独显的机器,跑 7B 或 14B 模型效果已经不错了。但如果只有核显或者内存有限,纯本地方案的效果可能达不到预期。

这时候可以考虑混合方案:嵌入模型跑本地,对话模型用云端 API。为什么这样分?因为嵌入过程涉及大量文档内容,如果走云端 API,等于把所有文档都传出去了,隐私优势就没了。而对话过程只涉及检索出来的片段和用户的问题,敏感度相对低一些。

AnythingLLM 支持这种混合配置。在设置页面里,LLM Provider 选 OpenAI 或 Anthropic,Embedding Engine 选 Ollama。这样文档向量化在本地完成,只有检索到的相关片段会发给云端模型。当然,如果你的文档涉密级别很高,连片段都不能外传,那就只能全本地。

还有一种折中方案:用本地小模型做对话,但配置一个云端模型作为“增强”。AnythingLLM 目前不支持自动切换,但你可以手动在设置里切换。比如日常问答用本地 7B 模型,遇到复杂推理问题时切到云端模型。

4. 工作区与智能体配置的实战细节

4.1 工作区的隔离逻辑

AnythingLLM 用“工作区”来隔离不同的知识库。每个工作区有独立的文档集合、向量数据、对话历史、系统提示词。这个设计很实用——你可以给市场部建一个工作区,给技术部建另一个,两边文档互不干扰。

但要注意,工作区之间的向量数据是存在同一个向量库里的,只是通过命名空间或元数据过滤来隔离。这意味着如果你用 LanceDB,所有工作区的向量都在同一个文件中。如果某个工作区的文档特别多,可能会影响其他工作区的检索速度。我实测下来,单个 LanceDB 文件超过 50 万个向量块后,检索延迟会明显上升。这时候要么拆分向量库,要么换 Qdrant 这类支持分片的方案。

创建工作区时有一个选项叫“Chat Mode”和“Query Mode”。Chat Mode 下,模型可以基于自己的知识回答,不一定要引用文档。Query Mode 下,模型被严格限制只能基于检索到的文档内容回答,如果文档里没有相关信息,它会说“我不知道”。做企业知识库时我强烈建议用 Query Mode,因为 Chat Mode 下模型可能会“编造”答案,这在内部问答场景里是致命的。

4.2 系统提示词的调优

系统提示词决定了智能体的“性格”和“行为边界”。AnythingLLM 给了一个默认提示词,大意是“你是一个有帮助的助手,基于提供的上下文回答问题”。这个默认值对通用场景够用,但对专业场景需要定制。

我调过的一个法律咨询场景,系统提示词改成了:“你是一个法律文档助手。只基于提供的法律条文和案例回答问题。如果上下文中没有明确依据,回答‘根据现有资料无法确定’。不要给出法律建议,只做信息检索和整理。”这样改完之后,模型胡编乱造的情况大幅减少。

另一个技巧是在提示词里加入“引用格式”要求。比如要求模型在回答时标注信息来源,格式为[文档名, 页码]。AnythingLLM 在检索时会返回文档的元数据,模型可以利用这些信息做引用。但默认提示词没有强调这一点,需要你手动加上。加上之后,回答的可信度会高很多,因为用户可以自己去核对原文。

4.3 智能体技能与工具调用

AnythingLLM 从某个版本开始引入了“Agent Skills”的概念,允许智能体调用外部工具。目前内置的技能包括网页浏览、文件读写、代码执行等。这个功能让 AnythingLLM 从一个“文档问答工具”升级成了“能动手的智能体”。

但工具调用对模型能力要求比较高。本地 7B 模型在工具调用的准确率上明显不如 GPT-4 或 Claude。我实测下来,qwen2.5:7b在简单工具调用场景(比如“搜索一下最新天气”)上成功率大概七成,复杂场景(多步工具调用)成功率不到一半。如果你要用工具调用功能,建议至少用 14B 以上的模型,或者直接用云端 API。

工具调用的配置在设置页面的“Agent Skills”里。每个技能可以单独启用或禁用。我建议按需启用,不要全开。因为启用的技能越多,系统提示词就越长,模型需要处理的上下文就越多,出错的概率也越大。而且有些技能(比如代码执行)有安全风险,在生产环境里要谨慎。

5. 检索质量调优:从“能用”到“好用”

5.1 分块策略的实战调整

前面提到了分块大小和重叠的调整,这里展开讲一下怎么判断当前分块策略是否合适。

一个简单的测试方法:上传文档后,用几个你知道答案的问题去问。如果模型回答得准确且完整,说明分块没问题。如果模型回答“根据现有资料无法确定”,但你知道文档里确实有答案,那很可能是分块把相关内容切散了。

我遇到过一个典型案例:一份产品需求文档,里面有一个功能点的描述跨了两页。默认分块把这两页切成了两个独立的块,检索时只召回了其中一块,模型只看到了半个功能描述,回答自然不完整。后来我把块大小从 1000 调到 2000,重叠从 200 调到 400,问题解决了。

但块大小不是越大越好。块太大,检索时召回的片段里包含大量无关信息,会稀释关键内容的权重。而且大块会占用更多上下文窗口,留给对话历史的空间就少了。我的经验值是:块大小控制在 1500-2500 字符,重叠 300-500 字符,对大多数文档类型都适用。

5.2 嵌入模型的选择与对比

嵌入模型的质量直接决定了检索的准确率。我对比过几个常用的嵌入模型,结果如下:

模型维度中文效果英文效果速度内存占用
nomic-embed-text768中等优秀快低
bge-m31024优秀优秀中等中等
text-embedding-3-small1536良好优秀快云端
mxbai-embed-large1024中等优秀中等中等

如果你的文档以中文为主,bge-m3是目前开源方案里综合表现最好的。它支持多语言,对中文语义的理解明显优于nomic-embed-text。但它的向量维度是 1024,比nomic-embed-text的 768 高,存储和计算开销会大一些。

换嵌入模型有一个大坑:换模型后,之前用旧模型生成的向量全部作废,必须重新向量化所有文档。因为不同模型的向量空间不兼容,用 A 模型生成的向量去和 B 模型生成的查询向量做相似度计算,结果完全是随机的。所以换嵌入模型前要有心理准备,留出重新处理文档的时间。

5.3 检索参数调优

AnythingLLM 的检索设置里有几个关键参数:Top N、相似度阈值、检索模式。

Top N 控制每次检索返回多少个文档块。默认是 4,意思是把最相关的 4 个块塞进上下文。调大这个值会让模型看到更多信息,但也可能引入噪声。我的建议是:文档质量高、主题集中时,Top N 设 3-4;文档质量参差不齐、主题分散时,设 5-6,让模型自己筛选。

相似度阈值控制“多相关才算相关”。默认是 0.25,低于这个分数的块会被过滤掉。这个值设得太低,会召回大量无关内容;设得太高,可能漏掉相关但表述不同的内容。我一般设在 0.3-0.4 之间,具体看文档的表述风格。如果文档用词比较规范统一,可以设高一点;如果文档口语化严重、表述多样,设低一点。

检索模式有“相似度”和“混合”两种。相似度模式就是纯向量检索,混合模式会结合关键词检索。对于包含大量专有名词、代码标识符、产品型号的文档,混合模式效果更好。因为纯向量检索对精确匹配不敏感,比如搜“ERR_4032”这个错误码,向量检索可能召回一堆语义相近但错误码不同的内容。混合模式会同时做关键词匹配,能准确命中。

6. 常见问题与排查实录

6.1 模型连接失败排查

这是最高频的问题。表现是设置页面里测试连接一直转圈或者报错。排查思路按以下顺序来:

先确认模型服务本身是否正常。如果是 Ollama,在宿主机上执行curl http://localhost:11434/api/tags,看能不能返回模型列表。如果返回不了,说明 Ollama 没跑起来或者端口不对。

再确认网络连通性。如果 AnythingLLM 跑在 Docker 里,Ollama 跑在宿主机上,容器内需要能访问到宿主机。在容器内执行curl http://host.docker.internal:11434/api/tags测试。如果失败,检查--add-host参数是否加了,或者试试用宿主机的实际 IP。

最后确认模型名称是否匹配。Ollama 的模型名称是大小写敏感的,qwen2.5:7b和Qwen2.5:7B是两个不同的名字。在 AnythingLLM 里填的模型名必须和ollama list输出的完全一致。

6.2 文档上传后检索不到内容

有时候文档上传成功了,但提问时模型说“没有找到相关内容”。可能的原因有几个:

文档解析失败。有些 PDF 是图片格式的,PDF.js 解析出来是空的。检查方法是看上传后的文档预览,如果预览里没有文字,说明解析失败。解决办法是先用 OCR 工具转成文本。

向量化失败。嵌入模型配置错误或者服务不可用,导致文档块没有被向量化。在 AnythingLLM 的日志里能看到相关错误。检查嵌入引擎的设置,确保测试连接通过。

工作区选错了。上传文档时需要选择目标工作区,如果选错了工作区,在当前工作区里自然搜不到。检查文档列表,确认文档在正确的工作区里。

相似度阈值设得太高。如果文档的表述方式和你的提问方式差异很大,相似度分数可能低于阈值被过滤掉。临时把阈值调到 0.1 试试,如果能搜到,说明是阈值问题。

6.3 回答质量差的优化方向

模型回答质量差通常表现为:答非所问、信息不完整、胡编乱造。针对不同表现,优化方向不同。

答非所问一般是检索环节的问题。检索出来的内容和问题不相关,模型自然答不对。优化方向是调整分块策略、换嵌入模型、开混合检索。

信息不完整通常是 Top N 太小或者分块太大。Top N 太小,相关信息没被召回;分块太大,关键信息被淹没在大量无关文本里。调整这两个参数试试。

胡编乱造在 Query Mode 下比较少见,但 Chat Mode 下很常见。如果必须用 Chat Mode,在系统提示词里加一句“如果上下文中没有相关信息,直接说不知道,不要编造”。另外,降低模型的 temperature 参数也能减少胡编乱造。AnythingLLM 默认 temperature 是 0.7,做知识问答时建议调到 0.2-0.3。

6.4 性能问题的排查

AnythingLLM 变慢通常有三个原因:向量库太大、模型推理慢、内存不足。

向量库太大表现为检索延迟高。前面说过,LanceDB 超过 50 万向量块后性能下降明显。解决办法是拆分工作区,或者换 Qdrant。

模型推理慢表现为回答生成时间长。本地 7B 模型在 CPU 上跑,生成速度可能只有每秒几个 token。如果有 GPU,确保 Ollama 正确调用了 GPU。在 Ollama 的日志里能看到是否使用了 GPU。

内存不足表现为服务频繁重启或者系统卡顿。本地模型加向量库加 Node.js 服务,内存占用可能超过 16GB。监控一下系统内存,如果持续在 90% 以上,考虑换小模型或者加内存。

7. 一些实战中攒下来的经验

AnythingLLM 的更新频率很高,几乎每个月都有新版本。升级前一定要备份数据目录,因为偶尔会有数据库 schema 变更导致旧数据不兼容。我吃过一次亏,升级后工作区配置全丢了,好在文档原始文件还在,重新向量化了一遍。

如果你要给团队用,建议在前面加一层反向代理做身份认证。AnythingLLM 自带的账号系统比较简单,没有细粒度的权限控制。用 Nginx 加 Basic Auth 或者接入公司的 SSO 都行。

文档预处理花的时间绝对值得。与其上传一堆格式混乱的文档然后抱怨检索效果差,不如花半小时把文档整理成干净的 Markdown。表格转成 Markdown 表格,标题层级用#标注,段落之间留空行。这样分块和检索的效果会有质的提升。

最后分享一个我常用的调试技巧:在 AnythingLLM 的对话界面里,每条回答下面有一个“显示引用”的按钮。点开能看到模型具体引用了哪些文档块。如果回答不对,先看引用的块对不对。如果引用的块就是错的,说明检索有问题;如果引用的块是对的但回答错了,说明模型能力不够或者提示词有问题。这个按钮是我排查问题时用得最多的功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 15:10:23

3D重建在工业预测性维护中的实战落地

1. 项目概述:3D重建不是炫技,是让机器真正“看见”空间的底层能力 “3D重建的相关应用(六)”这个标题看起来像系列文章的延续,但恰恰是这种看似平淡的编号,反而暴露了它最真实的价值——这不是一篇讲原理、秀模型的论文式科普&…

作者头像 李华
网站建设 2026/10/2 15:09:56

Claude Code九月更新:AGENTS.md转正、长任务恢复与插件系统实战

1. 九月这波更新,到底更新了什么 Claude Code 在九月的这波更新,说实话没有那种“憋个大招”的震撼感,但如果你一直在用,会发现每个改动都踩在痛点上。我把三个核心变化先摆出来:项目级指令文件 AGENTS.md 从社区提议转…

作者头像 李华
网站建设 2026/10/2 15:09:45

ComfyUI本地部署实战:从环境配置到文生图全流程拆解

玩AI绘画这两年,如果让我只推荐一个工具,我会毫不犹豫地选ComfyUI。这不是因为它界面好看,而是因为它把Stable Diffusion的生图流程彻底“可视化”了。网上关于ComfyUI本地部署的教程一搜一大把,但多数不是版本太老,就…

作者头像 李华
网站建设 2026/10/2 15:09:00

风光火储联合调频Simulink仿真建模与参数整定实战

刚开始接触风光火储联合调频的Simulink仿真时,大多数人会直接被一堆名词卡住:一次调频、二次调频、AGC、下垂控制、惯量响应、储能SOC……更别提还要把风机、光伏、火电、水电、储能甚至电动汽车塞进同一个模型里协调出力。这篇内容,我就按照…

作者头像 李华
网站建设 2026/10/2 15:07:34

微信小程序预约挂号系统开发:从数据库设计到上线避坑指南

2. 核心业务建模与数据库设计想清楚业务怎么流转,再动手写代码,能省掉后面一大半重构的功夫。预约挂号系统最核心的环节有几个:用户选科室、选医生、选时间、提交预约、医院确认、就诊签到。每一步的状态怎么流转,数据怎么存&…

作者头像 李华