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 里、再造一个新环境试真章,看它是真的睁开眼认坑,还是又在同个位置摔下去。下篇见。


相关推荐
晴天161 小时前
AgentLoop分享(上): 让 AI 真正“自主干活“-Day15
人工智能·python
phoenix@Capricornus1 小时前
从统计决策到贝叶斯估计
人工智能·算法·机器学习
NutShell Wang1 小时前
国产全模态开源潮:从语言模型到视频模型,中国开源生态再升级
人工智能·语言模型·开源·大模型·音视频·多模态·vibe coding
薛晓刚1 小时前
信息化、数字化:智能化的基础,还是历史包袱?
人工智能
梅头脑1 小时前
第一次用AI生图,出来一只六条腿的猫——直到我拆开了扩散模型的6步推理过程
ai编程
Black蜡笔小新1 小时前
从智慧社区到雪亮工程:国标GB28181公网平台EasyCVR如何构建视频“一张网”?
人工智能·自动化·easycvr
一次旅行1 小时前
Agent面试题答案整理(一)
人工智能·microsoft
Patrick在香港1 小时前
Python爬虫框架实战:优雅抓取香港政府公开API数据的通用方案
java·开发语言·爬虫·python·ai编程·数据可视化·可用性测试
AICDragon1 小时前
1.8%就够了:K3的896个MoE专家,为什么激活率这么低反而是好事?
人工智能·算法