AI 只解决眼前那个坑【上篇】:来源、图片、鲁棒性,把能聊一处一处补齐

AI 只解决眼前那个坑【上篇】:来源、图片、鲁棒性,把能聊一处一处补齐

前一篇我把 16G 内存机器上的私文简搜重写成轻量版,后端能跑、前端能起,我以为快完事了。结果才发现「能跑」和「能聊」之间还隔着一片坑:答案不标来源、图片访问不了、deploy 目录不存在就报错、把检索到的全文整个塞给 Ollama、一次聊天要等半小时才出得来。这篇是补全实录------来源、图片、鲁棒性,一处一处填,又一次验证了那句话:AI 只解决眼前那个坑,你得把那些坑一个个夹逼回来。

前言

先说清楚写作背景,全文是我跟 opencode之间的拉扯的记录,满屏地情绪词,也真是难为我了,真的为真正项目使用AI的朋友感到默哀。

现在的AI啊,收费的也想尽办法将你的Token浪费掉,不收费的也总是想让你心情不好而弃用。我使用Claude整理一下文章,竟然将我的一周的量都用完了,觉得百炼有点水,这种包月的限量的,肯定是会水的,先让你用完,然后就可以休息五天了。这样想来OpenCode是真良心啊。

一、来源与图片:把「答得让人能信」立起来

开发时少了一项功能:检索结果要说明来源,聊天界面里注明「来自哪个 .md」,并且能打开查看;图片也必须是正确路径。于是第一件事,就是把需求发给它:

复制代码
前面开发还少了一个大功能,就是如果有文档要指明来源,在聊天下面注明,如来自XXX.md,并能单击显示出来。图片也要是正确的路径。

先拿日常问题试一试:怎样保持电脑干净?

复制代码
怎样保持电脑干净

结果被现实打脸:一句话没回,还在重复;就算显示点什么,也只是当前片段,图片没有替换。唉,真无语------docs 里明明有完整的 md 文件,为什么不用呢?

arduino 复制代码
好了,聊天一句没有,还重复,不用会set?

先看下能不能显示,只显示当前片段,且图片没有替换,是错误的。唉。真无语了。 docs里面不是有完整的md文件吗

凭良心讲:把搜索到的原文档放出来、改成聊天式展示、图片用正确路径,不就结了?哦对,图片这种,前端拿到正确地址就会自动渲染。于是又试一次这个测试:

复制代码
怎样保持电脑干净 

二、鲁棒性:目录不存在,别动不动报错

显示是能显示了,但一测试就报错。习惯性地将deploy等目录直接删除,再重新发布。结果坑来了报错:deploy 目录不存在,不会先创建目录,却抛一长串错误信息,还明确说"部署目录不存在"。 这种目录,不存在不会自动创建吗?又不是什么核心错误。

php 复制代码
部署目录不存在: E:\ivy\ai\ai-code-projects\opencode\ivy-chat-docsolo-wrapper\deploy
	at vip.wayhua.ivy.chat.service.impl.ImportServiceImpl.deploy(ImportServiceImpl.java:89)
	at vip.wayhua.ivy.chat.controller.ImportController.deploy(ImportController.java:34)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:991)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:896)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:88)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:1272)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1797)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:973)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:491)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
	at java.base/java.lang.Thread.run(Thread.java:833)  目录不存在,要先创建

改了deploy目录创建错误,又变成「清空失败」,原因还是同一个:目录不存在。

非常生气,这都不算 bug 了,必须改成能适配我的习惯。我测试时会先删库重导。要它做到:目录不存在自己建,别动不动报错;真正碰到原则性问题才停下来。

scss 复制代码
[14:52:10.749] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: Lucene 清空失败: E:\ivy\ai\ai-code-projects\opencode\ivy-chat-docsolo-wrapper\data\index] with root cause
java.nio.file.NoSuchFileException: E:\ivy\ai\ai-code-projects\opencode\ivy-chat-docsolo-wrapper\data\index
	at org.apache.lucene.util.IOUtils.fsync(IOUtils.java:460)
	at org.apache.lucene.store.FSDirectory.syncMetaData(FSDirectory.java:279)
	at org.apache.lucene.store.LockValidatingDirectoryWrapper.syncMetaData(LockValidatingDirectoryWrapper.java:62)
	at org.apache.lucene.index.SegmentInfos.prepareCommit(SegmentInfos.java:911)
	at org.apache.lucene.index.IndexWriter.startCommit(IndexWriter.java:5647)
	at org.apache.lucene.index.IndexWriter.prepareCommitInternal(IndexWriter.java:3790)
	at org.apache.lucene.index.IndexWriter.commitInternal(IndexWriter.java:4131)
	at org.apache.lucene.index.IndexWriter.commit(IndexWriter.java:4093)
	at vip.wayhua.ivy.chat.service.impl.LuceneFullTextIndexServiceImpl.clearAll(LuceneFullTextIndexServiceImpl.java:85)
	at vip.wayhua.ivy.chat.service.impl.ImportServiceImpl.deploy(ImportServiceImpl.java:119)
	at vip.wayhua.ivy.chat.controller.ImportController.deploy(ImportController.java:34)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:991)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:896)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:138)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:165)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:88)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:113)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:83)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:72)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:1272)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1797)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:973)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:491)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
	at java.base/java.lang.Thread.run(Thread.java:833)
怎么老报这种错误,
复制代码
我肯定会删除的,这种bug一定要修改
复制代码
不要动不动就报错,要鲁棒一点。只要不是原则性错误,报毛错啊,这种不存在,创建一个就好了。要给客户方便。我测试,肯定会直接删除里面内容的。
kotlin 复制代码
前面我就说了,怎么查看deploy后的数据,也就是data目录,将里面全部删除就好了。deploy里面也一样。

如果是以前milvus,会安装客户端attu来查看数据是否导入,现在改成文件版本,就没法使用工具了。我要怎么查看数据呢?结果是只能看data目录里面是否有数据,当然有对应格式,不过我也不想细了解,只要有数据就行吧。删除的话就直接删除。 它问能不能手动删库,我给出的答案是带前提的:手动删没问题,但程序必须保证不报错。

复制代码
不行,我还是要手动这样操作,你要保证这个不报错,我可以在先删除再运行

再来测试,终于不报错了。补一句真实体会:跟 AI 交流,命令尽量下成「命令式」,准确度立刻不同。

三、慢的元凶:把全文直接喂给模型

功能通畅了,进到对话测试。上来第一句:

复制代码
spring ai alibaba 使用

结果还是老问题:聊天没内容、图片地址打不开。其中图片这条最有意思------前端 5173 打不开,但直接访问后端 8200 却能通。应当是前端的代理没配好。

perl 复制代码
现在还是两个问题:
1、聊天没内容,
2、图片地址无法访问。

1.
spring ai alibaba 使用  我说的
ai回答
来源
6.AI BI Helper 开发实录 02.md
6.AI BI Helper 开发实录 02:Graph 工作流编排------SQL 生成、执行与邮件推送(Spring AI Alibaba 1.1.2.0 + Milvus 3.0).md
7.AI BI Helper 开发实录 03.md
7.AI BI Helper 开发实录 03:评估节点与重试循环------SQL 自校验让生成更稳定.md
double-ai-agent实战手记01:系列规划、架构设计与技术选型(Spring AI Alibaba 1.1.2.0+tk-mybatis).md

2.http://127.0.0.1:5173/api/data/docs/859c480110904b61a0a08a718533ce45/%E9%A6%96%E4%B8%AASpring%20AI%20Alibaba%20Graph/images/image-20260729223603038.png 无法访问,是不是vue没有配置好
perl 复制代码
http://127.0.0.1:8200/api/data/docs/859c480110904b61a0a08a718533ce45/%E9%A6%96%E4%B8%AASpring%20AI%20Alibaba%20Graph/images/image-20260729223603038.png 可以访问

在修改的同时,查看了一下代码,不看不知道,原来是这样让Ollama变慢的,它将搜索到的所有文章(md)全部直接传入Ollama,这样一来,我问的问题那几个字,在海量的文本里面几乎可以忽略不计了。如果搜索到五个文档,几万字一次进 context。本来使用的虚拟机就只有8g,安装的也是qwen3.5:4b都是从7b降级下来的。

我以往用 Milvus 时,走的是「文本向量」链路,先把相关片段取出来,再交给模型,完全不是这种方式:

java 复制代码
 String userInput = state.value(Constant.KeyName.USER_INPUT, "");
//        创建重写查询转换器,借助LLM帮我们进行用户查询的重写,变成可以让LLM更好理解的Query
        RewriteQueryTransformer queryTransformer = RewriteQueryTransformer.builder()
                .chatClientBuilder(builder)
                .build();


        RetrievalAugmentationAdvisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
                .queryTransformers(queryTransformer)
                //指定你使用哪一种文档检索器
                .documentRetriever(VectorStoreDocumentRetriever
                        .builder()
                        .vectorStore(vectorStore)
                        .build())
                .queryAugmenter(ContextualQueryAugmenter.builder()
                        .allowEmptyContext(true)
                        .build()
                )
                .build();

        Flux<String> content = chatClient.prompt()
                .advisors(retrievalAugmentationAdvisor)

Opencode还是我说的那个问题,我能帮你解决当前问题,至于后期多麻烦,你就说我有没有解决当前问题。

根本不管用户的需求,当然至少是解决了当前问题。至于是每次发送五篇大长文md文件,用户输入的内容几乎都忽略不计了。想想每次至少传入万字,qwen3.5 多少b都会死的,更何况只有8g内存。

你看他的解决方案

实测数据铁证:Ollama 纯 CPU 生成 100 token 花 200s(0.5 token/s)。你要的截断方向正确------prompt 越长,prompt-eval 阶段也越慢。但生成速度本身是硬约束,截断能把「首 token 时间」压下来,聊天的内容(最终答案)只要等,还是会慢慢生成完。

现在只改一处:buildContext 里每片截断到 300 字喂模型,来源弹窗全文不受。

是不是脑子有屎,这根本就不是解决问题的方法。

你确实可以说是用户提问不对,但我明确告诉了需求,还是不动脑的。

四、方向之争:AI 想「截断」,我要「向量」

我给出文本向量的代码,就想知道这样会不会快一些。 它给的方案却是「截断」------每段只留 300 字再喂。这分明是「把答案砍短来交差」。于是取消前面内容重新输入。

复制代码
我要的是走文本向量的方式 和Ollama沟通,本来就慢,难怪前面一个聊天要11分钟。我要的是简洁,你不用管是否是ollama的问题,我要的是传入文本向量这种方式。再说一篇我要的是传入文本向量这种方式。你确实可以说是用户提问不对,但我明确告诉了需求,还是不动脑的。不要再说ollama的问题了,我已降为qwen3.5:2b了。根本问题就是没使用文本向量。一定要使用文本向量,而不是传入全文。一定要使用文本向量,而不是传入全文。一定要使用文本向量,而不是传入全文。

我已经要崩溃了,还是那么偷懒,如果一个真实上线项目,万一有哪一步没有夹逼好,后果想想真可怕。

之所以会出现整套方案全换掉的原因是,AI只解决当时提问题的而不管全局。除非我刚才那样一定要强烈要求非要使用文本向量,才更改方案。按用户的要求,看前面上传zip,添加个deploy,和这次这个坑,充分说明了AI只解决当前问题,而不管全局以及下一步的。后面的问题可以推翻前面的问题,如果后面强调,而前面问题没结束,就忘记了。这就是AI,也是当前形式,及时行乐哪管明天是死是活。

我只好强烈拍板:我要的是文本向量。模型慢,不是字节多,而是你喂了全文。Ollama 本来就是本地 CPU,4b、2b 已降到底,一个回答 11 分钟,「截断」根本不治本。命令式强调三遍------一定用向量,不要传全文:

复制代码
我确定是使用文本向量传入ollama的?而不是其他取代方案。
不要搞折衷方案。确定吗? 不会传入全文到ollama吧
现在一点信任都没有了。

踩坑记录:AI 拿「每段 300 字截断」当方案,本质是在躲关键设计------它只想把这轮「跑完」,不管技术方案底子对不对。你不反复坚持,它就给你糊弄过去。

OpenCode不想认真解决问题,认真思索,而是一味的绕弯子,将就解决这一问题。哪管用户真实需求和想要的。

makefile 复制代码
那qwen3.5:4b行不行,我是一定要使用文本向量传入的
scss 复制代码
  //        1. 实现RAG的召回过程
        String userInput = state.value(Constant.KeyName.USER_INPUT, "");
//        创建重写查询转换器,借助LLM帮我们进行用户查询的重写,变成可以让LLM更好理解的Query
        RewriteQueryTransformer queryTransformer = RewriteQueryTransformer.builder()
                .chatClientBuilder(builder)
                .build();


        RetrievalAugmentationAdvisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
                .queryTransformers(queryTransformer)
                //指定你使用哪一种文档检索器
                .documentRetriever(VectorStoreDocumentRetriever
                        .builder()
                        .vectorStore(vectorStore)
                        .build())
                .queryAugmenter(ContextualQueryAugmenter.builder()
                        .allowEmptyContext(true)
                        .build()
                )
                .build();

        Flux<String> content = chatClient.prompt()
                .advisors(retrievalAugmentationAdvisor)
                
                这种代码实现做不到吗

OpenCode还和我说完全等价,既然是等价为什么不用我的方案。它又复述了一遍确认:现在用的是我的方案对吧?真无脑。

复制代码
操, 完全等价,那就用我的方案。

五、否定词它也分不清,连方案都要从头删

所有的AI基本上都有一个功能,有时会看不清否定词,所以尽量使用肯定语气,强列语气。 为了适配新需求,它差点把最早期定下的技术方案整个删掉------也包括 Spring AI Alibaba BOM 1.1.2.0(DashScope/Ollama 生态)。我立刻叫停:

复制代码
不会连最早定的技术方案
AI-Ali 扩展spring-ai-alibaba BOM1.1.2.0提供 DashScope 与 ollama 生态(可选)
连这个也去掉了吧

它不但看不懂,还连「肯定否定」都拎不清:把我说的「不要改掉 Spring AI Alibaba,只要用 Ollama」给理解成了「删掉 Spring AI Alibaba」。一个「不」字没了,整个方向反了半天。

复制代码
操 你看连肯定否定的话都看不懂,这可能是OpenCode免费的原因吧。

我要的是使用Spring AI Alibaba 1.1.2.0,只是使用Ollama。

不要改错了。

连肯定否定都看不懂了。真是唉。差点去掉了SpringAiAlibaba了,只能赶紧停止。

重新发。

复制代码
操, 完全等价,那就用我的方案。前面说的这个方案现在是使用的是我的方案吗

根本就不是理解反了的表面意思,而是只提取了几个关键字,比如不好,分解为不、好、不好,全文搜索时,可以不要"不",也可以不要"好",结果,不就丢了。

要再确定一下,前面功能会不会又丢了。

复制代码
以我的方案为准,要重新导入数据吗

六、又踩坑:批量与 append

重新部署总算能跑了,日志第一行就吓我一跳:vector 已清空全部数据。为什么重新部署,它反倒把库清得干干净净?deploy 词在语义上应该是 append(追加),不是 add(覆盖)。

ini 复制代码
[17:13:04.145] INFO  v.w.i.c.s.i.LuceneFullTextIndexServiceImpl - [lucene] 已清空全部索引
[17:13:04.145] INFO  v.w.i.c.s.i.ImportServiceImpl - [clear] data/docs 已清空: .\data\docs
[17:13:04.156] INFO  v.w.i.c.s.i.ImportServiceImpl - [clear] 全部数据已清空
[17:13:28.932] INFO  v.w.i.c.s.i.ImportServiceImpl - [import] 解压完成到部署目录, batchId=23dfb3a4bc954c70bb8af874400fbbb1, dir=E:\ivy\ai\ai-code-projects\opencode\ivy-chat-docsolo-wrapper\deploy
[17:13:32.731] INFO  v.w.i.c.s.i.ImportServiceImpl - [deploy] file=从Ghost到Vagrant,搭建干净、可重建的本地开发环境.md, chunks=62
[17:13:32.765] INFO  v.w.i.c.s.i.ImportServiceImpl - [deploy] file=代码心得.md, chunks=59
[17:13:32.766] INFO  v.w.i.c.c.LocalTfidfVectorStore - [tfidf] 已保存到 E:\ivy\ai\ai-code-projects\opencode\ivy-chat-docsolo-wrapper\.\data\vector\tfidf.json
[17:13:32.766] INFO  v.w.i.c.c.LocalTfidfVectorStore - [vector] 已清空全部数据
 是不是代码写错了,怎么已清空全部数据,我测试了两次。并且不走了。
arduino 复制代码
那你的日志要写清楚。那为什么不改成批量呢?以前都会设置一次多少条啊。是不是nomic-embed-text使用时,不用qwen3.5:4b
go 复制代码
为什么[vector] 已清空全部数据,deploy是append,不是add,不能删除以前的全部。所有deploy都是append
复制代码
为什么这个错误也会犯,脑子不好吧

不看代码就死了。 for 循环把所有文档的 chunk 全加进 all:

java 复制代码
   for (Path mdPath : mdFiles) {
            List<Document> chunks = parseMd(mdPath, batchId, deploy);
            all.addAll(chunks);
            log.info("[deploy] file={}, chunks={}", mdPath.getFileName(), chunks.size());
        }

这里肯定是不行的。要按maxChunk=5或10 每次maxCHunk来执行。一次全文,百炼都不行

七、收尾:让它直接写启动脚本

来点轻松的收尾------一个 Windows 下启动后台的脚本,把 jar 和 application.yml 一起拉起来:

复制代码
编写一个ivy-chat-docsolo.jar application.yml的运行cmd windows的,直接给出代码

为什么会这么笨呢?

八、小结

我发现使用Opencode改使用Opencode操作记录的文章,会偷偷地将对Opencode不利的内容删除掉,真人性化啊。或者是直接删除。厉害,没想到还有这个功能。

踩坑记录

坑 现象 解法
来源可点开 聊天界面没来源 输出带来源文件 + 链接
图片打不开 5173 打不开图 验证后端直连 → 代理问题
deploy 目录不存在 原生 Java 堆栈 先建目录再工作
索引目录不存在 Lucene 清空失败 先建目录
全文喂模型 一次回答 11 分钟+ 向量召回,只喂相关片段
「截断」糊弄 每段砍 300 字 明确要向量,命令式坚持
否定词反解 「不要删 A」成「删 A」 方案焊死「以我的方案为准」
deploy 清了全库 vector 已清空全部 改成 append
chunk 一次全推 推送极慢 按 maxChunk 分批

下期预告

上篇把「能聊」这一关勉强过了,而这一路踩的坑是真多。下一篇,让 opencode 把这些经验落在 skills 里、再造一个新环境试真章,看它是真的睁开眼认坑,还是又在同个位置摔下去。下篇见。


相关推荐
Navicat中国1 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI1 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区2 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris4 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy4 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V4 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI4 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技4 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
海宇数据4 小时前
零信任架构实战:基于海宇网上踪迹欺诈行为识别构建自动化信贷资信评估网关
运维·人工智能·架构·自动化