ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用Trae自动编程从零实现小型中文搜索引擎全记录

用Trae自动编程从零实现小型中文搜索引擎全记录 最近我把盘了大半年的baigle搜索引擎项目用Trae自动编程终于落了地。第一阶段基本实现已经跑通从指定种子页面开始抓取经过中文分词、倒排索引、TF-IDF打分最后在命令行里输入关键词就能返回相关结果。这个项目规模不大但爬虫、索引、检索、排序一个不少而且整个开发过程大量依赖Trae的AI生成与对话调试期间踩了不少坑也攒了一些心得。这篇就记录一下从0到1的完整过程适合想动手写一个小型搜索引擎、或者想尝试AI辅助编程的朋友参考也顺便聊聊我对Trae自动编程的实际体感。1. 项目概述与核心思路1.1 为什么做一个小型搜索引擎做搜索引擎这事儿听起来像是大厂才碰的领域但实际上搜索引擎最核心的流程并不复杂爬取网页、解析内容、建立索引、检索排序。你不需要几亿个页面也不需要分布式集群只要能把几百个页面做成一个能用的检索系统整个原理就通了。我最初的想法很简单平时用惯了搜索引擎但很少真正思考它背后怎么工作于是想自己动手写一个迷你版看看从URL到搜索结果中间到底发生了什么。baigle这个名字是从“beagle”小猎犬变形来的寓意是一个小而敏捷的搜索引擎能快速跑起来而不是臃肿的巨兽。第一阶段我给自己定的验收标准很实在给一批种子页面能爬下来并清洗成干净文本对中文内容能做分词能建立倒排索引在命令行输入查询词能返回排序后的TopK结果。不做前端界面不做分布式不追求搜索质量多高但每一步都要能跑通每一步都要能看到中间结果。这样后面加功能时也有一个稳的基础。1.2 第一阶段的目标与技术选型目标范围明确之后技术选型就很顺手了。语言我用Python原因很简单爬虫、HTML解析、中文分词的生态最全写起来也快。具体库选了requests加BeautifulSoup做抓取和解析jieba做中文分词numpy做向量运算索引和检索逻辑用Python原生数据结构加标准库就够了。排序方面没有上太复杂的模型第一阶段用TF-IDF加分数累加后面想换BM25也容易。真正让我纠结的是要不要用Trae。之前我试过不少AI编程工具Trae的特点是它不仅是编辑器插件更是一个完整的IDE而且内置了多个模型可以在对话里直接生成和修改代码。我当时的判断是这个项目模块边界清晰非常适合用自然语言描述需求、让AI生成骨架我再做审查和联调。事实证明这个选择没问题但也没有想象中那么“无脑”。AI能帮你写出大量代码但前提是你得把需求描述清楚并且知道怎么把它生成的代码组织进一个可维护的项目里。这部分后面具体展开。2. 用Trae自动编程的实战方法论2.1 Trae的基本使用与积分说明先说Trae怎么上手。它本身是一个AI驱动的IDE下载安装后可以用内置的AI助手聊天直接在对话里让它“写一个爬虫”“修复这个报错”也可以选中代码片段让AI解释或者重构。Trae支持多个模型不同模型消耗的积分不一样。免费额度用完之后可以通过签到或者兑换码等方式获得积分。网上经常有人问Trae积分兑换码哪里获得我的经验是官方活动、社区福利以及新用户邀请都比较常见但别盲信第三方卖码容易踩坑。日常写这种小项目其实免费额度是够用的因为每次生成代码可以指定比较小的范围不需要一次性生成整个项目。我第一次打开Trae时也一脸懵因为它的聊天面板和普通IDE习惯不太一样。后来我的工作流固定成左侧写需求文档和提示词右侧是项目文件聊天面板用来生成和修改代码。比较顺手的是Trae可以直接引用项目里的文件比如选中一个文件后跟AI说“给这个模块补上异常处理”它给出的diff比重新生成整个文件要可控得多。这里提醒一下AI生成的代码一定要自己读一遍再合入尤其是涉及网络请求和文件写入的部分容易出现资源泄漏或者编码问题。2.2 如何把需求拆成AI能理解的提示词用Trae自动编程最大的经验就是提示词的质量决定代码的质量。你不能只说“给我写个搜索引擎”而要说清楚输入输出、边界条件、依赖库、文件组织方式。比如我写爬虫模块时的提示词是“用requests和BeautifulSoup写一个爬虫函数输入是URL列表输出是每个页面的标题、正文文本和页面中的链接要求设置请求超时、伪装User-Agent、返回字典列表”。这样AI生成的代码直接就能用而不是一个空泛的demo。另外一个大任务要拆成多个小任务。搜索引擎涉及模块多如果一次性让AI生成整个项目它大概率会生成一个结构混乱的脚本。我的做法是先拆成爬虫、分词、索引、检索四个独立模块每个模块单独让AI生成然后我再手动写主流程把模块串起来。每完成一个小模块就运行一下确认没有基础报错再继续。这样定位问题也容易因为出错的范围很小基本不会出现“整个项目跑不起来但不知道错在哪”的情况。我还会在提示词里加上“给出关键注释”和“不要使用第三方库除...之外”这类约束。尤其是搜索引擎里链接去重、编码处理这些细节AI容易忽略需要你在提示词里主动点出来。实测下来带上具体边界条件的提示词生成代码的可用率能从50%提到80%以上。2.3 让AI生成的代码符合项目结构的技巧Trae自动编程有个常见的坑AI默认生成的是单个脚本所有函数堆在一起。如果页面多、模块多后期维护会很难受。我的解决办法是在项目根目录放一个README和requirements.txt然后告诉AI“项目结构采用src/目录每个模块一个文件不要写在单个脚本里”。这样它生成的代码会自然地分散到多个文件而不是一个长文件。还有一个技巧是让AI先设计接口再写实现。在真正写代码前我先让Trae帮我列出每个模块应该暴露哪些函数、函数签名和返回值确认没有歧义后再让它填充实现。这个步骤看起来很笨但能避免后面改接口导致的连锁返工。比如最开始我让爬虫模块直接返回字符串后来发现检索需要doc_id于是让爬虫返回包含doc_id和text的字典如果一开始就定好接口这步就不用折腾。合入AI生成代码时我还会自己跑一遍静态检查。Trae内置了基本的语法检查但逻辑漏洞它不一定看得出来。比如它生成的倒排索引代码用的是列表来存储文档ID当文档增多时查询会越来越慢这种性能问题AI不会主动优化。所以我通常会在关键数据结构上自己加一层设计让AI在给定设计下实现而不是完全放权。3. 核心模块设计与实现拆解3.1 爬虫模块从种子URL到页面清洗爬虫是搜索引擎的数据入口。第一阶段我不做全网爬取只从一个手动维护的种子URL列表开始。每个页面抓下来后先用BeautifulSoup解析提取标签作为标题去掉script、style标签后取正文文本再把页面里出现的a链接抽取出来存进待爬队列。为了避免爬到重复页面我维护了一个seen集合URL放进队列前先规范化去掉锚点、统一协议名。/p p这里有个容易被忽略的点编码。requests默认会用响应头里的编码去解码但很多中文站点响应头不标准导致页面乱码。我的做法是先用resp.encoding resp.apparent_encoding或者统一用utf-8去尝试解码如果还是乱码就在清洗阶段丢弃这个页面。AI生成的代码基本不会考虑这种细节需要你在提示词里明确说明或者自己在review时加上去。/p p实际爬虫代码我让AI生成的核心逻辑大致是这样的/p precode classlanguage-pythonimport requests from bs4 import BeautifulSoup def fetch_page(url, timeout10): headers {User-Agent: Mozilla/5.0 (baigle-crawler/0.1)} try: resp requests.get(url, headersheaders, timeouttimeout) resp.encoding resp.apparent_encoding if resp.status_code ! 200: return None soup BeautifulSoup(resp.text, html.parser) title soup.title.get_text(stripTrue) if soup.title else for tag in soup([script, style]): tag.decompose() text soup.get_text(separator\n, stripTrue) links [a.get(href) for a in soup.find_all(a) if a.get(href)] return {title: title, text: text, links: links} except Exception as exc: print(ffetch failed: {url}, {exc}) return None /code/pre p这段代码看起来简单但关键点不少。超时时间设置成10秒太长会拖慢整体速度User-Agent伪装成浏览器能减少被拒的概率异常捕获要打在函数内部避免一个坏链接导致整个爬虫退出。至于robots.txt第一阶段我没有严格实现只在提示词里让AI加了一个简单的判断如果页面返回403或明显不允许抓取直接跳过。/p p正文提取这块用soup.get_text抽取会有个问题会把导航栏、页脚、广告文本全混进来。第一阶段我选择了容忍这个噪声因为索引和排序用的是TF-IDF正文里混点噪声对TopK结果影响不大。如果你对内容质量要求高可以后面再加正文提取算法比如统计文本密度来抽取主体内容这部分可以在第二阶段实现。/p h33.2 中文分词与倒排索引/h3 p搜索引擎的核心是倒排索引。简单说就是建立一个映射从词到包含它的文档ID列表。这样查询时只需要查这个词对应的列表而不用遍历所有文档。中文和英文不一样英文按空格切词就行中文必须分词。我用的是jieba它支持精确模式、全模式和搜索引擎模式通常我们用jieba.lcut(text)拿到词语列表即可。注意要把标点符号和停用词过滤掉否则索引里全是“的”“了”“是”这种无效词。/p p倒排索引我用defaultdict(list)来实现代码结构如下/p precode classlanguage-pythonfrom collections import defaultdict import jieba STOP_WORDS {的, 了, 是, 我, 你, 他, 在, 有, 和, 就, 不, 人, 都} def build_index(docs): index defaultdict(list) term_freq defaultdict(dict) for doc_id, text in docs: words [w for w in jieba.lcut(text) if w.strip() and w not in STOP_WORDS] tf defaultdict(int) for w in words: tf[w] 1 total len(words) for w, cnt in tf.items(): index[w].append(doc_id) term_freq[w][doc_id] cnt / total return index, term_freq /code/pre p这里有几个细节首先每个词即使在同一文档中出现多次index[w]里的doc_id会重复后面检索时需要去重或者干脆不存储重复值。其次我同时计算了词频term_freq因为后面算TF-IDF需要它。最后分词结果里非中文词也要过滤可以用正则把纯数字和英文保留但第一阶段为了简单全部交给jieba和停用词表处理。/p p为什么要用倒排索引而不是直接遍历所有文档匹配关键词因为时间复杂度差太多了。假设有1000个文档每个文档1000个词如果查询时遍历全部文档最坏情况要扫描一百万个词而倒排索引只需要查索引里的一个词表再取交并集即可。搜索引擎为什么快就是靠这个数据结构。你可以让AI帮你写但自己一定要理解这层原理不然后面加多词查询时根本不知道该怎么组合。/p h33.3 检索排序TF-IDF与TopK/h3 p检索模块的输入是查询词输出是排序后的文档ID列表。第一阶段我选用TF-IDF加权打分一个词在某个文档里出现得越多越重要但如果在所有文档里都出现区分度就低权重需要调低。排序时直接累加每个查询词在文档上的权重得到最终分数然后按分数取TopK。严格来说标准的做法还会对文档向量做余弦归一化避免长文档天然分数偏高第一阶段为了先把链路跑通我暂时省略了这一步后面优化时再补上。/p p打分逻辑不复杂/p ul liTF(t, d) 词t在文档d中出现的次数 / 文档d的总词数/li liIDF(t) log(N / (1 df(t)))N是文档总数df(t)是包含词t的文档数/li li文档分数 查询词t的TF值 * IDF(t) * IDF(t) 累加/li /ul p我用numpy配合纯Python来实现代码大致如下/p precode classlanguage-pythonimport math import jieba def search(query, index, term_freq, doc_count, top_k10): query_words [w for w in jieba.lcut(query) if w.strip() and w not in STOP_WORDS] scores {} for w in set(query_words): df len(index.get(w, [])) idf math.log(doc_count / (1 df)) for doc_id in set(index.get(w, [])): tf term_freq[w].get(doc_id, 0) scores[doc_id] scores.get(doc_id, 0) tf * idf * idf return sorted(scores.items(), keylambda x: x[1], reverseTrue)[:top_k] /code/pre p这段代码是简化版实际项目里我会把文档模长预先计算好缓存起来再进行真正的余弦归一化。另一个可以优化的点是查询词有多词时文档ID集合要做交集还是并集。第一阶段我用的是并集因为TF-IDF排序自然会压低不相关文档的分数而且交集容易让结果太少影响体验。如果你希望结果更精准可以在并集基础上先做初步筛选再进入打分。/p p排序结果出来后我会把doc_id映射回标题和URL打印成类似“1. 标题 (URL)”的格式。这个映射表在索引构建时顺势保存一份就行非常简单。/p h24. 实操过程记录与关键步骤/h2 h34.1 环境准备与项目初始化/h3 p动手之前先把环境弄干净。我本地的Python版本是3.10Trae安装的是最新版。项目初始化时我在Trae里新建了一个src目录然后手动创建了requirements.txt里面列了requests、beautifulsoup4、jieba、numpy。接着在Trae的终端里执行pip install -r requirements.txt很快装完。/p pTrae的联网能力需要注意它默认可以访问网络但我不确定它会不会自动帮我装包所以环境这一步我坚持手动做。这也是我用AI编程的一个原则AI可以生成代码但环境、依赖、部署这些环节还是要自己能掌控不然出问题都不知道是代码问题还是环境问题。/p p项目结构我让AI按我的约定生成最终长这样/p precode classlanguage-textbaigle/ ├── src/ │ ├── crawler.py │ ├── indexer.py │ ├── search.py │ └── main.py ├── data/ │ ├── seed_urls.txt │ └── docs.json ├── requirements.txt └── README.md /code/pre p这个结构很精简但每个模块职责清楚。种子URL列表放在data/seed_urls.txt里爬下来的页面统一存成docs.json方便后面索引模块读取。main.py负责串起整个流程爬取 - 建索引 - 进入交互式检索。/p h34.2 从零到跑通的完整流程/h3 p实操时可以按下面顺序来/p ol li在data/seed_urls.txt里写入种子URL每行一个。/li li运行python src/crawler.py观察抓取日志检查data/docs.json是否生成。/li li运行python src/indexer.py确认index.pkl和term_freq.pkl生成。/li li运行python src/main.py输入测试查询词。/li /ol p全程在Trae终端里执行如果某个模块报错直接把报错信息粘贴给AI让它修。这里有个细节种子URL我选的是自己经常逛的文档站和博客站页面结构差异大能更好地暴露清洗逻辑的问题。爬完检查docs.json发现有些页面text字段是空的多半是页面内容由JavaScript动态渲染导致requests拿到的HTML里没有正文。第一阶段我没有上无头浏览器直接把这些空文档过滤掉了反正不影响主流程跑通。/p p第二步是建索引。先写一个简单的脚本读取docs.json调用jieba分词构建倒排索引和term_freq字典然后把结果序列化成pkl文件存储。这一步跑得很快几百个文档分词加建索引用了不到两秒。我特意打印了几个词的倒排列表检查比如“搜索”这个词能看到它出现在哪些文档里确认索引结构是对的。/p p第三步是检索。写search.py的时候我先让Trae实现一个最简单的“单关键词精确匹配”版本验证能返回结果后再让它升级成TF-IDF排序。为什么分两步因为直接生成复杂版本容易错出了问题不好定位。单关键词版本跑通后我又加了一个不带numpy的朴素打分函数作为基准然后对比numpy版本的输出确保两边算出的分数一致。这种对比测试在AI生成代码的场景下特别好用能快速暴露AI在数学实现上的小错误。/p p最后是把三个模块串进main.py。main.py的逻辑很直白读种子URL - 爬取 - 建索引 - 进入while循环等待用户输入查询词输入exit退出否则显示TopK结果。跑通后我试了几个查询比如“搜索引擎”“分词”“爬虫”结果排序还算合理包含这些词的文章基本都排在了前面。到这里第一阶段验收标准全部达成。/p h34.3 验证效果与性能观察/h3 p功能跑通之后我还做了个简单验证拿100个页面做索引然后连续查询20次看响应耗时。结果每次查询基本都在10毫秒以内主要时间花在分词和向量计算上。这个速度对一个小型搜索引擎来说已经非常舒服了。当然如果把文档数量放大到一万甚至十万numpy版本的余弦计算可能就会成为瓶颈届时要考虑用稀疏矩阵或者改成BM25加倒排列表剪枝。/p p我还观察到一个有意思的点文档数量增加后倒排索引的构建时间主要是分词而分词的速度取决于文档总词数。jieba的精确模式在几百个文档的体量下很快但如果以后爬取规模上来可能需要换成jieba.lcut_for_search或者引入缓存。这个属于第二阶段再说但我在README里留了优化笔记防止自己忘记当时的思路。/p h25. 常见问题与排查技巧实录/h2 h35.1 高频问题速查表/h3 p整个开发过程中我最常遇到的问题整理成了一张表方便以后自查也给看到这篇的朋友一个参考/p table thead tr th问题现象/th th可能原因/th th解决方案/th /tr /thead tbody tr td爬取页面中文乱码/td td响应头编码不规范/td td设置resp.encoding resp.apparent_encoding或强制utf-8/td /tr tr td查询结果为空/td td停用词过滤太狠或分词后词为空/td td检查查询词是否在倒排索引中用print调试索引/td /tr tr td同一文档在结果中重复/td td倒排索引中doc_id未去重/td td查询时使用set(index.get(w, []))/td /tr tr td排序结果不相关/td tdTF-IDF打分bug或正文噪声太大/td td先跑基准测试对比朴素打分/td /tr tr tdTrae生成的代码缩进错误/td tdAI偶尔生成混用Tab和空格/td td让Trae重新格式化或用IDE自动PEP8/td /tr tr td爬虫请求被拒绝/td tdUser-Agent或请求频次太高/td td设置合理UA增加请求间隔/td /tr /tbody /table p这张表基本覆盖了第一阶段90%的报错场景。看到问题不要慌先在索引和中间结果上多打印几行多数问题马上就能定位。/p h35.2 三个让我印象深刻的Bug/h3 p第一个Bug是编码问题。我一开始没设置apparent_encoding爬下来的页面全是乱码分词结果都是“锟斤拷”这种词索引建出来完全不能用。后来我打印了resp.encoding才发现requests默认用了ISO-8859-1改成从内容推断编码后立刻正常。这个坑几乎必踩建议大家爬虫第一步就把编码处理好。/p p第二个Bug是倒排索引的doc_id重复。我直接用index[w].append(doc_id)没有去重结果查询时一个文档被多次计算排序分数被放大导致TopK结果里同一个链接出现好几次。排查方式很简单在查询词对应的列表里打印出来看看发现有大量重复项。修复方式是在构建索引时或者查询时加set去重我选择了查询时set因为索引更小空间上更划算。/p p第三个Bug是Trae生成的代码里混用了Tab和空格导致运行时缩进错误。这个特别隐蔽因为看着代码没问题一运行就报IndentationError。后来我在Trae的终端里执行python -m py_compile src/*.py一下就找到了问题文件。让AI重新格式化之后解决。经验就是AI生成的代码也需要静态检查别因为代码是AI写的就跳过语法验证。/p h26. 经验心得与下一阶段规划/h2 h36.1 对Trae自动编程的整体评价/h3 p用Trae完成baigle第一阶段之后我对AI辅助编程的看法变得更务实了。Trae确实能大幅提升写原型代码的速度尤其是爬虫、索引、检索这种有固定套路的模块说清楚需求后AI生成的核心逻辑基本是能用的。它最大的价值不是替你思考而是帮你把已经想清楚的方案快速落地省去大量敲代码的时间。/p p但我也有几个明显的体感AI对边界条件、性能细节和项目整体结构的把握还比较弱。你如果不明确告诉它“链接要去重”“编码要设置”“doc_id集合要去重”它就容易漏。所以用Trae的姿势应该是“人指挥AI执行”而不是“AI指挥人验收”。另外Trae的免费模型在生成代码质量上比付费模型略低但对这种小项目完全够用。积分兑换码没必要花冤枉钱日常折腾免费额度加活动兑换基本够。/p p我现在的工作流已经固定下来先在README里写清模块接口和验收标准再把任务拆成小片段逐项发给Trae每生成一块就立刻运行测试最后人肉review一遍关键逻辑。这套流程下来项目的代码质量和可控性都还不错也减少了AI生成的“惊喜Bug”。/p h36.2 第二阶段可以怎么扩展/h3 p第一阶段只是地基我已经在计划第二阶段的内容了。首先爬虫要支持更灵活的策略比如深度优先遍历和基于robots.txt的合规检查正文提取也得升级用文本密度算法把导航和页脚噪声去掉这会对排序质量有明显的提升。其次索引要引入BM25排序它比TF-IDF更贴近实际搜索场景而且计算开销也不大。第三可以给项目加一个简单的Web界面用Flask提供搜索API前端就做一个极简搜索框这样baigle就从命令行玩具变成一个真正可用的工具。/p p还有一个我想做但优先级靠后的方向是拼写纠错和搜索建议。比如用户输错一个词系统能提示“你是不是想搜XX”。这个功能在小型数据集上可以用编辑距离实现难度不大但很能提升体验。整体来看第二阶段的核心不是堆功能而是把第一阶段的每个模块做得更稳、更符合真实搜索引擎的工程习惯。等第二阶段跑通我大概率还会用Trae来辅助实现但会在提示词里把第一阶段踩过的坑全部列进去让AI别再犯同样的错。/p p最后再分享一个小技巧如果你也打算用AI辅助写这种多模块项目建议不管AI给出的代码多完美都要自己动手跑一遍核心流程亲手打印几个中间结果看看。搜索引擎项目尤其如此文档ID、词频、倒排列表这些中间状态只有亲眼确认过后面调起来才有底气。/p
返回列表