研究 Vue 3 源码的收获
掘金
最近阅读 Vue 3 源码时,我从一个看似简单的构造函数开始,重新理解了 TypeScript 的参数属性。例如 constructor(public fn: () => T) 中的 public f
Балл: 57.16Уверенность: 54%
ПодробнееЗагружаем каталог…
НАВИГАТОР ПО ВОЗМОЖНОСТЯМ ИИ
Найдите свой ИИ-инструмент. Бесплатный доступ, пробные периоды и кредиты — в одном месте.
掘金
最近阅读 Vue 3 源码时,我从一个看似简单的构造函数开始,重新理解了 TypeScript 的参数属性。例如 constructor(public fn: () => T) 中的 public f
Балл: 57.16Уверенность: 54%
Подробнее掘金
我跑 300 轮:真实 flaky 率 61.7%,套上「重试即通过」报 26.7%;真实 18.3% 报 0%,和真稳定的门禁一样。最狠的 Agent PR 是删掉了让测试不确定的 fixture。
Балл: 57.11Уверенность: 54%
ПодробнееiThome 新聞
Linux基金會(The Linux Foundation)託管的開源社群ELISA(Enabling Linux In Safety Applications)10月5日宣布,將Cregit與stress-ng兩項開源專案納入旗下,分別補強程式碼分析及壓力測試能力,進一步擴大ELISA支援安全關鍵軟體開發與測試的技術範圍。
Балл: 57.08Уверенность: 54%
Подробнее人人都是产品经理
过去十年快递有个铁律:件量涨 10%,收入涨 5%。国家邮政局 9 月 18 日的数据打破了它——收入增速 7.4% 跑赢了件量增速 4.6%。作者说这不是涨价,是结构变了:单票价格从 13 跌到 7 块,但冷链、大件、生鲜这些高价值件占比在提高。 过去十年,快递行业有个铁律:件量涨10%,收入涨5%。 今年,这个铁律破了。 9.18,国家邮政局数据:收入增速7.4% > 件量增速4.6% 01 不是涨价,是结构变了 收入增速跑赢量,不是涨价,是高价值件占比上来了。 什么是高价值件? 冷链、保价、大件、生鲜、医药、逆向退货。 单票收入数倍于普通电商件。 过去十年,快递靠电商件跑量。 电商件标准化程度高、价格敏感、利润薄。 单票价格从13块跌到7块, 跌了十年已见底,但冷链、大件、生鲜这些高价值件,占比在提高。 快递行业,从“靠量赚钱”,变成“靠结构赚钱”。 02 为什么现在才变?因为末端密度到头了 过去一直降价, 靠的是规模效应 :网点越多、单量越大,单票成本越低。 但 规模效应有个拐点: 末端网点密度到一定程度,新增一单的 边际成本不再下降 。 末端派送是刚性成本。一个快递员一天就送那么多件,你再增加单量,他要么加班、要么加人,成本是线性的,不是递减的。 据行业测算,末端成本占单票成本的四到五成——这部分降不下来,整个行业的单票价格就降不下来。 不是不想打价格战了,是打不动了。 单票价格降不动了,那收入怎么涨? 只能在同一套网络上,多卖一层服务。 03 从规模经济到范围经济 1. 规模经济: 同一套网络,送越多越便宜。 2. 范围经济: 同一套网络,多卖一层服务,边际成本接近零。 你去看行业报表就会发现一个分化: 1️⃣有的企业在同一套网络上叠了三四层服务——冷链、医药、商务件、大件,单票收入数倍于普通电商件。 2️⃣有的企业还在只卖电商件,同一套网络只赚一层钱。这个分化不是某一家公司的问题,是整个行业在重新洗牌。 网络是现成的。 你多卖一层服务,不需要新建一套网络。 这就是 范围经济 。 04 真正卡脖子的,不是技术,是组织能力 范围经济说起来容易。但你去一线看看,真正卡脖子的不是技术,是组织。 你让一个送了十年电商件的快递员,明天开始送冷链,他不敢。 据一线反馈,不少快递公司推冷链业务时,培训完的快递员还是不敢接。不是不会操作,是出了冷链事故谁担责——这个问题没人给答案。 你让他送电商件,闭着眼都能做。 但冷链是另一套流程,他怕出事。 这就是为什么很多公司嘴上说“要做范围经济”,实际跑不动。 不是系统不行,是人和组织的能力 ,还停在 规模经济 那套里。 最后 价格战打了十年,快递行业终于走到拐点。 末端成本到底了,规模经济到头了。 接下来拼的,不是谁更便宜,是谁能在同一套网络,上多卖一层服务。 从规模经济>范围经济不会自动演进,过程中需要组织,系统,更需要你和我。 附:近十年快递行业核心数据,送给大家 年份 业务量亿件 量增速 收入 亿元 收入 增速 单票 价格/元 2015 206.7 — 2769.6 — 13.40 2016 312.8 +51.4% 3974.4 +43.5% 12.71 2017 400.6 +28.0% 4957.1 +24.7% 12.37 2018 507.1 +26.6% 6038.4 +21.8% 11.91 2019 635.2 +25.3% 7497.8 +24.2% 11.80 2020 833.6 +31.2% 8795.4 +17.3% 10.55 2021 1083.0 +29.9% 10332.3 +17.5% 9.54 2022 1105.8 +2.1% 10566.7 +2.3% 9.56 2023 1320.7 +19.4% 12074.0 +14.3% 9.14 2024 1750.8 +21.5% 14034.0 +13.8% 8.02 2025 1989.5 +13.6% 15000.0 +6.5% 7.54 数据来源:国家邮政局历年统计公报。 2025年数据为初步统计数。 单票价格=快递业务收入÷快递业务量。 本文由人人都是产品经理作者【曼话产品】,微信公众号:【曼话产品】,原创/授权 发布于人人都是产品经理,未经许可,禁止转载 题图来自Unsplash,基于CC0协议
Балл: 57.02Уверенность: 54%
Подробнее人人都是产品经理
Tibo 宣布 28 天连续改进,首日只是把 GPT-6 Astra 与 6.1 Sol 提速 50%。SemiAnalysis 研报指出,Claude 订阅给开发者的等效 API 价值是 OpenAI 的五倍以上。作者从心流讲起,拆解额度腰斩与两家自救路线的差异。 最近 OpenAI 总想搞大新闻,掩盖模型不如 A 社,速度巨慢,消耗极快的事实 Tibo 发推要开启为期 28 天的连续改进挑战,无改进就重置,而在 Day 1 拿出的是:将 GPT-6 Astra 与 GPT-6.1 Sol 的默认推理速度全线拉升 50%,两小时内全部生效!包括通过 Sign in with ChatGPT 接入的所有生态伙伴,像 Devin、OpenCode、Amp、Pi 全都在提速名单里 就在 Tibo 发帖的前几个小时,华尔街与硅谷顶级半导体分析机构 SemiAnalysis 刚刚发布了一份深度研报《Anthropic Subscriptions Offer 5x+ More Value Than OpenAI》,直接把 OpenAI 底裤都给扒了个精光: 在实际工作负载下,Claude 订阅计划给开发者带来的等效 API 价值,足足是 OpenAI 的 5 倍以上 在 AI 编程的真实战场上,最值钱的永远不是某个模型的单次智商峰值,是开发者的 心流(Flow) 当你在 Cursor、OpenCode 或者终端里全神贯注地重构一个模块时,你脑子里的逻辑架构正在飞速运转,这时候如果模型卡壳,每隔几秒才挤出两个变量名,你的专注力就会被当场腰斩 有网友做过测试,对比 GPT 6.1 Sol 、 Gemini 3.8 Flash 、 Claude Opus 5.5 相同任务的吞吐速度 结果: 参赛选手 任务总耗时 实际吐字速度 🏎️ Claude Opus 5.5 88.12 秒 71.5 TPS 🏎️ Gemini 3.8 Flash 185.34 秒 239.1 TPS 🚜 GPT 6.1 Sol 317.95 秒+ 20.1 TPS 很多不经常写代码的同学看到这里可能会纳闷: Gemini 3.8 Flash 吐字速度高达 239.1 TPS,比 Opus 5.5 快了三倍多,怎么耗时反而多了近一倍? 这就是 Agent 任务规划与思维密度的本质差异!Gemini 3.8 Flash 爆发力极猛,像个机关枪一样疯狂输出长篇大论,把整个推演链路拉得极长,总 Token 堆得像山一样;而 Opus 5.5 凭借极其毒辣的架构判断,直切要害,只用最精炼的高信噪比代码完成闭环,因此即便速度是 71.5 TPS,也能在 88 秒内干脆利落地交卷 但最惨的当属 GPT-6.1 Sol 20.1 TPS 是什么概念?一个普通的 Python 脚本通常需要几百到上千 Token 的输出,这意味着每按一次确认,你就得坐在屏幕前盯着光标干等好几分钟,哪怕你起身去接杯水、去趟洗手间,回来它还在慢悠悠地吐着字! “Sol 的智商确实能够应付日常编码,Token 消耗也只要 Astra 的五分之一,但是慢到让人想摔键盘!我的思路高度连贯,结果等它吐完一段代码,我已经完全忘记上一层函数的传参规则了” 甚至为了对抗这种令人发指的龟速,被逼疯的工程师们硬生生自研出了一套“穷人版三层 Agent 架构”: 为什么大家不敢全量上 Astra? 因为 Astra 消耗配额的速度就像在烧干柴!OpenAI 的周配额只要一碰 Astra,两天内就会直接见底 在这种极度内耗的体验下,大量原本扎根在 Codex 的开发者,开始成群结队地迁移到 Claude Opus 5.5 的阵营 大家原本以为这只是模型速度的暂时波动,直到 SemiAnalysis 的研报出炉,所有人才恍然大悟: 背后其实是 OpenAI 算力告急后的算盘调整!这是一场把 AI 巨头逼到悬崖边缘的算力经济学生死战 1. 订阅套餐:占 10% 营收,却吃掉 40% 算力的吞金兽 过去大家总觉得包月 200 美元很贵,但对于重度开发者而言,这笔买卖简直是占尽了巨头的算力便宜! SemiAnalysis 的 Token 经济学财务模型测算显示: 订阅收入虽然只占 Anthropic 总营收的 10% 左右,但由于高频调用、长上下文缓存与无休止的 Agent 循环, 订阅用户吃掉了超过 40% 的全网推理算力! 这种极度不均衡的倒挂,直接把数据中心每兆瓦算力的综合收入拉低了整整 3,600 万美元 面对如此凶猛的算力失血,OpenAI 与 Anthropic 在应对策略上,走出了两条截然相反的分水岭道路 2. OpenAI 的“核打击式硬着陆”:额度腰斩与逼氪 500 刀 面对算力窟窿,OpenAI 的刀法极其决绝: 全新上线了每月 500 美元的至尊套餐,主打的就是 300 TPS 的 Ultrafast 超高速通道;紧接着,原本最受开发者青睐的 200 美元套餐,配额居然被全线腰斩了 50%! 阴谋论点,OpenAI 故意把默认的 Sol 压制在 20 TPS 龟速,再腰斩配额,把高速通道包装成每月 500 美元的奢侈品单独售卖! 3. 两种截然不同的自救路线与 5 倍价值碾压 对比两家的策略,差异更明显: OpenAI 选了一刀切的核选项 :直接把订阅额度砍到骨折,全面收缩亏损 Anthropic 选了精细化梯度补贴 :针对顶级模型 Fable 5.1 严防死守,但在最关键的编程生产力工具 Opus 5.5 上,依然给出了惊人的慷慨补贴! 数据不会说谎,看上面这张归一化对比图: 在每美元等效 API 价值的对决中, Claude Opus 5.5 的综合收益达到了 OpenAI 的整整 4 到 5 倍! 过去独立开发者们总夸赞 OpenAI 额度给得大方,甚至逼得 Anthropic 几次三番撤回缩减计划;但风水轮流转,到了今天,OpenAI 在开发者心中的性价比神话已经彻底瓦解 “从 20 TPS 提速到 30 TPS,就好比把自行车换成了电瓶车,可隔壁的 Claude Opus 5.5 天生就是开保时捷的;更何况人家一个 200 美元套餐顶你五个,你这边还把额度砍掉了一半,这道算术题小学生都会算” Codex 如果还靠重置这种小聪明维持口碑,模型能力依然被A家压着打,更别提还有谷歌的追赶,那真就会越来越拉了 就我目前使用体验,Gemini 3.8 Flash 又快又好,偶尔还能蹭蹭 Opus 5.5,已接是绝佳选择了 赚到了,Gemini 4 发布,又高又硬!碾压GPT-6 Astra、Claude Opus 5.5! 我的 Gemini 终于要回本了,开发了一个 macOS 原生客户端,已开源! 作者:Ai学习的老章 公众号:Ai学习的老章 本文由 @Ai学习的老章 原创发布于人人都是产品经理。未经作者许可,禁止转载 题图来自 Unsplash,基于CC0协议
人人都是产品经理
西贝深陷倒闭传闻,王慧文、林盛等大佬排队力挺,消费者却用脚投票。一边是资本看中的供应链与品牌资产,一边是信任崩塌的性价比争议。本文拆解这场舆论与市场的割裂,探讨西贝困局背后的深层逻辑。 想救西贝的人,一个接一个。 9月23日,美团联合创始人王慧文在社交平台“即刻”发了一条动态:“听说西贝要倒闭了,有没有人组局把西贝买下来?” 9月20日晚,钟薛高创始人林盛在直播间里替西贝撑腰:“西贝要是真的倒了,肯定是中国餐饮行业的倒退。” 去年9月,胖东来创始人于东来曾经在一周内三次公开力挺西贝。 三位企业家,不约而同地为同一家企业站台。 而在社交平台上,消费者的声音是另一个版本。“人均一百多,吃的是加热的菜”“味道配不上价格”,这些评价,从2025年9月罗永浩那条微博开始,就没有停过。 圈内人在排队站台,消费者的脚却还没跨回门槛。 一、一场“倒闭传闻”,炸出两种声音 事情从9月19日一条爆料开始。 微博大V“理记”发文称,连续多年位居中式正餐头部的西贝,将在两三个月内彻底倒闭;创始人贾国龙将个人承担绝大部分债务、放弃股份,仅保留约100家盈利门店交由员工自营。 西贝很快回应:全国门店均正常运营,公司一直是直营加内部合伙人模式,从未改变。 第二天,9月20日,贾国龙在一场专项领域协同发展大会上发声:结合西贝“惨痛的教训”,恳请监管部门进一步厘清高流量网络账号的言论边界,区分正常舆论监督、观点辩论,与恶意贬损、商业诋毁和网络软暴力。 老板在台上呼吁监管,市场和消费者给出的,是另一套反馈。 就在传闻发酵前后,凤凰网《风暴眼》实地探访了西贝门店:用餐高峰期,店里只有三四桌顾客。今年1月,贾国龙已宣布关掉全国102家门店,约占门店总数的三成,涉及约4000名员工。从去年9月到今年3月,西贝累计亏损预计超过6亿元。 门店数量,也从巅峰时期的近400家,缩减到今年3月的约250至268家。 一边是圈内人排着队要来“救”,一边是门店里空着三四张桌子。这条裂缝,其实一年前就裂开了。 回看一年前那场风波,埋下的东西很清楚。 慧科讯业的一份事件分析报告指出:当时餐饮从业者集体声援西贝,巴奴、吉祥馄饨、朱光玉等品牌创始人纷纷发声;而社媒评论区则聚集大量负面评价,针对的是性价比低、沟通态度不友好。 报告把分歧归结为两点:预制菜的行业规范没有被大众了解,以及西贝忽视了消费者对知情权的重视。 在同行眼里,这是一场关于“行业标准”的争论;在消费者眼里,这是“我花了现做的钱,吃到的却是复热菜”。从那天起,这两套语言,就没再对上过。 一年后,分歧不但没有弥合,反而从舆论场蔓延到了资本端。 二、投资人眼中的西贝:为什么大佬们想买 资本圈的态度,颇为直接。 先看王慧文。 这位美团联合创始人曾与王兴并称“二王”,退休后先是创办AI公司光年之外,又在2025年成立个人投资机构Lollapalooza Capital,目前已完成25笔投资,组合横跨理想汽车、猿辅导、水滴筹,以及月之暗面、Manus等AI明星项目。 他的判断,市场历来是当回事的。 再看真金白银。 2026年1月,西贝完成A轮融资。据工商信息,新增股东包括台州新荣泰投资,背后是新荣记创始人张勇;以及杭州舟轩股权投资,前蚂蚁集团CEO胡晓明持股97%并任执行事务合伙人。 2026年3月,据上海证券报报道,西贝再次引入新股东:上市公司大中矿业的实控人林来嵘,认缴出资约224.76万元,持股约2.16%。林来嵘与贾国龙同为内蒙古巴彦淖尔人,这轮融资被媒体称为“老乡驰援”。 一位餐饮大佬、一位互联网老兵、一位矿业资本,在2026年前三个月里,先后把筹码押在了西贝身上。 为什么?王慧文那句话,或许给出了部分答案:“我才不管是不是预制菜呢,反正卫生过硬,口味我也觉得不错。” 在投资人眼里,西贝的核心资产从来不是“舆论形象”,而是37年攒下来的东西:供应链、食安管理、门店卫生标准,以及那个仍然能唤起味觉记忆的品牌。这套资产,在行业内部有明确的估值逻辑。 林盛那句“倒了是行业倒退”的判断,代表的正是这套行业内部的评价体系。 三、消费者眼里的西贝:口碑是怎么一步步塌的 但消费者的评价体系,和行业完全不是一回事。 对普通食客来说,过去一年的西贝,是另一副面孔。 其一,“预制菜”的标签甩不掉。 罗永浩那场“炮轰”之后,“西贝=预制菜”的印象在社交网络上反复被强化,哪怕西贝开放后厨自证,也没能完全消解。消费者介意的,未必是“预制菜”本身,而是“花了现炒的钱,吃了预制的菜”。 价格与信任一旦错位,就很难修复。 其二,门店数据在说话。 据新京报贝壳财经援引窄门餐眼数据,西贝在营门店从2024年的约376家降至2026年9月的226家,一年减少约120家;2026年前九个月,仅新开1家门店。2025年9月至2026年3月,累计亏损超6亿元。 其三,成本结构高悬。 据公开报道,西贝人力成本占比持续超过35%,行业健康线约25%;单店净利润仅3%—5%,容错率极低。 这意味着,西贝的商业模式高度依赖客流与翻台,经不起风浪。 关于“西贝即将倒闭”的说法,目前并无官方证实,西贝也已公开辟谣。但门店腰斩、连续亏损、新品牌反复试错,这些已经确认的事实,足以让消费者用脚投票。 四、割裂从何而来 投资人眼中的西贝与消费者眼中的西贝,为什么像两个品牌? 拆开看,是三个层面的错位。 第一层,评价体系不同。 在餐饮行业内部,央厨与预制工艺是中餐标准化的通用做法,西贝的供应链与食安管理是被行业认可的;但在消费者端,“正餐价格”配上“预制菜”三个字,天然触发信任危机。 西贝未必输在“品质”上,它输的是“信任定价”,消费者不再相信,自己花的钱买到了对等的东西。 第二层,商业模式与消费趋势的碰撞。 人力成本占比超35%、客单价不低,意味着西贝的模型建立在“高客流+高毛利”上;当消费趋于理性、平价餐饮崛起,这个模型对外部环境变化的承受力,比想象中脆弱。 此外,新品牌试错也在消耗资源。2026年2月,西贝推出新品牌“天边砂锅焖面”,一度开出约30家门店。 但据新京报贝壳财经报道,开业不到半年,至少5家门店关闭或再次更换品牌;北京丰科万达店半年内换了三块招牌:从西贝换成天边砂锅焖面,2026年8月又换成主打鲜羊料理的“天边羊多”,目前仅此一家。 第三层, 创始人应对方式与公众期待的落差。 贾国龙是出了名的“不服输”。从起诉罗永浩,到恳请监管部门厘清言论边界,他的应对始终强势。 对照组是于东来:同样是“行业良心”的叙事,于东来在公众心中被信任,贾国龙却反复被质疑。 2025年,贾国龙还在媒体上说“不认识于东来”、把对方的力挺归为“真心照真心”;一年后,网友已经在讨论“请于东来救西贝”。 个人IP与品牌信任的强绑定,顺风时是放大器,逆风时,就成了靶子。 这场割裂的代价,正在被多方承担。 对西贝而言,是估值与融资节奏被重新定价。A轮融资的光环还在,但门店收缩与亏损数据,会让后续每一轮融资都更艰难。 对行业而言,预制菜标准的讨论被重新点燃。餐厅到底该不该标注预制菜?“现做”与“预制”的边界在哪里?这个问题,早晚需要一个明确的答案。 对消费者而言,是“下馆子”的信息不对称:我在餐厅花的钱,究竟买到了什么? 对企业品牌而言,这或许是一个更普遍的提醒:当一家企业同时面对资本与消费者时,两者给出的评价可能天差地别。资本看资产,消费者看信任。 结语 资本可以买下品牌,但买不回信任。 西贝的故事还没有结束。接下来真正值得关注的,是几件具体的事:西贝与罗永浩的诉讼进展;门店收缩能否止住、新品牌能否跑通;以及资本后续是否继续加注。 这些问题的答案,会比任何一句口号,都更能说明西贝的下一步。 至于“谁更懂西贝”?投资人和消费者,也许都没有错。他们只是拿着完全不同的尺子,在量同一个品牌。 免责声明: 请注意:本文分析源自公开披露资料(财报、公告等),作者不保证信息来源的充分性或最新状态。股市有风险,本金可能亏损,投资决策需极度谨慎! 本文所有观点仅为作者评论,绝非买卖建议。投资者必须依据自身情况独立研究、审慎判断,风险自担。 本文由 @颗粒度财经1 原创发布于人人都是产品经理。未经作者许可,禁止转载 题图来自作者提供 该文观点仅代表作者本人,人人都是产品经理平台仅提供信息存储空间服务
人人都是产品经理
同一道外观检测工序,一体机报价45万,平台方案却要80万,差价背后藏着两种截然不同的商业逻辑。本文从产品视角拆解两种形态的本质差异,用成本曲线和真实案例告诉你:选型的关键不在价格,而在产线的变更频率与团队承接能力。 上一篇结尾我留了个话头:算法、样本库、发布流程、指标对账,这套东西最后总得以某种形态交到客户手里——是封装死的一体机,插电就用;还是开放的二次开发平台,客户自己往里长本事。这篇就把这个取舍写透。 先摆两张报价单。这是我最常被叫去“救火”的一类会:同一家客户,同一道外观检测工序,前后脚收到两个方案。A 方案是智能检测一体机:软硬一体、出厂预调,现场装好开机就用,报价 45 万。B 方案是视觉软件平台加通用硬件:软件授权 30 万、工业相机镜头光源 35 万、首期开发实施 15 万,合计 80 万。 客户把两张单子拍在我面前:差 35 万,差在哪?A 的销售说 B 是“卖铲子的——铲子给你了,金子自己挖”;B 的实施商说 A 是“铁盒子——锁死了,明年你们产品一改型就等着返厂”。 两边都没说谎,但两边都只说了一半。这篇我把两种形态翻成产品语言,把两本账摊开算,最后给三个问题——答完,这个选择基本就定了。 一、先翻译:一个买结果,一个买能力 很多客户吵半天“买硬件还是买软件”,其实吵错了维度。一体机和平台的分水岭,从来不在软硬,而在三件事上。 第一件,你买到手的是什么。一体机买到的是一个“结果”:这台设备对这个工位、这类产品,检出率多少、节拍多少,出厂就定死了,你验收这个结果就行。平台买到的是一种“能力”:算子库、流程编辑器、SDK,配什么相机、搭什么流程、练什么模型,路给你,走不走、走多远看你自己。 第二件,也是本质:变更成本记在谁的账上。一体机是变更成本前置,一次性买断——供应商把未来可能的折腾折进售价里,所以前期贵。平台是变更成本后置,摊在日常——软件本身没那么贵,贵在你要有人接得住,每次变更的人力时间都是自己的账。 第三件,能力上限由谁定。一体机的上限是厂商的产品经理——他没做进去的功能,你等一年也等不来。平台的上限是你自己的团队——平台能力 100 分,你的工程师接得住 60 分,落地就是 60 分。 这三条想通了,后面两本账就好算了。图一是对照表,拿去就能用。 二、一体机的账:省心是真省,边界也是真硬 先说公道话,一体机这 45 万不是白花的。 交付快。标准机型下单到进厂,两到四周;现场安装调试两到三天,验收走完就投产。平台首期项目从需求调研到上线,两三个月算快的——这中间的产线等待、人员投入,都是隐性成本,采购部门往往不折算。 上手简单。一体机的界面是奔着操作工设计的:开机、选配方、按启动,三个动作。培训十分钟。平台的流程编辑器再“低代码”,也是给工程师用的工具,操作工看看界面就头大。 出了问题一个电话。软硬一家,没有“相机厂商说是软件问题、软件厂商说是镜头问题”的皮球。停线一小时损失几万块的产线,这条值钱。 但边界的硬,销售一般不讲透。 第一条硬边界:换产品等于重来。我见过一台外壳外观检测一体机,客户产品改了个丝印位置,原参数怎么调过杀都压不住——机型是按老产品调死的,最后返厂重调,来回三周。三周里那条线的检测走人工全检,品质部天天加班。 第二条:你能调的只有参数,动不了算法。界面上阈值、公差、速度都给你调,但遇到训练集里没有的新缺陷类型,你只能报给厂商排期。排期多久?看他手里有多少跟你一样的客户。改型的产线,这个等待就是停线。 第三条:绑定。第一年买设备你比了三家,第三年产线扩二十条工位,你只剩一家可谈——协议、配件、算法升级全是他的格式。议价权在谁手里,账就在谁手里。 还有一张销售不会主动翻开的页脚:服务费。多数一体机第一年免费保修,第二年起签年度服务合同,行业惯例是设备价的 8% 到 12% 一年,算法升级、新缺陷支持、远程诊断都打包在里面。45 万的设备,一年又是四万上下。这笔钱不是不该付——厂商持续服务理应收费——但它该出现在你最初比价的那张表上,而不是第二年续约时才第一次看见。 三、平台的账:便宜的是软件,贵的是人 平台的账,明面上那 80 万只是首付。 真正的成本项是养人。一个能独立扛起视觉检测项目的工程师,年薪 20 到 30 万——这还是能招到的情况下。平台买回来没人用,就是几十万买了个摆设。我算给客户听过:平台方案的真实首年成本 = 软件 30 + 硬件 35 + 实施 15 + 一个工程师的全年人力 25,约 105 万。比一体机贵出一倍还多。 那为什么还有人买?因为回报在后头,买的是三样东西。 自主迭代。新缺陷出现,现场标注、自己训练、按上一篇讲的流程影子跑加灰度上线,一周一个循环。不用看厂商排期,不用等返厂。 对接自由。检测数据是你自己的,直接落你的数据库、对接 MES、联动 PLC 剔除——第五篇写过的数据反哺,只有平台形态才做得起来。一体机的数据接口开不开、开多少,看厂商心情。 配方管理是基本功。几十种产品、上百个配方,切换、版本、权限都是平台原生能力。多品种小批量的产线,这是刚需。 一句话总结这本账:一体机把钱一次给厂商,换后面的省心;平台把钱一次给厂商、再持续给自己的团队,换可能性。前者买断,后者订阅——订阅的前提是,你得养得起这份订阅。 四、成本曲线会交叉,交叉点由变更频率决定 两本账摊开了,就可以画到一张图上。图二那条交叉曲线,我给至少五家客户画过。 横轴是时间,纵轴是三年累计总持有成本——人力、服务费、停线折算全算进去。一体机:首付 45 万,之后每年两笔支出——服务合同按设备价 8% 到 12% 签,一年 4 万上下;每次改型,显性的返厂上门费 3 到 8 万,加上停线等待折算,一次按 11 万记。平台:首付 80 万,就是第三节那三行报价,之后每年养人 25 万跑不掉,但每次变更只要 1 万多——自己标注、自己训练、自己上线,动的只是工程师的时间。 一年改型一两次的产线,两条线差距逐年拉大,永远交叉不了——一体机完胜。一年改型四次的产线,第二年底两条线打平,第三年平台开始反超,而且越往后甩得越开。 所以别问“哪种形态好”,要问“我的产线三年内会变更几次”。交叉点的位置,是变更频率画出来的,不是设备性能画出来的。 顺带提醒一句:很多采购比价,拿一体机的整机报价去砍平台的软件授权价,砍完觉得自己赚了——这两份报价根本不是同一个东西。一体机的 45 万里含着调试、质保和厂商对未来服务的预判;平台报价里软件是软件、硬件是硬件、开发是开发,三行分开。要比,就把双方三年的总持有成本拉通比,包含人力、包含停线、包含改型——比单日报价,比的只是谁的话术好。 五、两种翻车我都见过 理论讲完,讲两个真案子,都在我眼皮底下。 第一个,买一体机买错了产线。做消费电子结构件的一家厂,产品一年小改三次。买的时候销售拍胸脯“改型支持很快”,第一年返厂两次、工程师上门一次,运费、停线、复调合计 18 万——设备才 45 万。第三年他们咬牙把检测工序迁到平台上,当初那台一体机挪去做单一老产品的产线,算是物尽其用。教训一句话:把变更频繁的产线交给一体机,等于把滑动条焊死在刻度盘上。 第二个,买平台买错了自己。另一家买了平台,编制里那个视觉工程师第二年被挖走了,新人接不上手——流程是前任搭的,几十个配方的命名规则、复判逻辑的分支、跟 MES 的接口字段,全在前任脑子里,文档没人补。新人光是读懂旧流程就花了三个月,这期间平台变成摆设,紧急需求全走厂商外包,按次计费,一年下来比一体机还贵。后来我去复盘,问题不在平台,在采购那天没人问过一句:这套东西买回来,谁负责让它一直转?平台形态有个一体机没有的软肋:它的价值长在人身上,人走了,价值跟着走。买平台的同时要买三样配套——人、文档习惯、至少两个人的备份,缺一样,能力就悬在单点上。 所以这个选择的完整版不是二选一,是两问一起答:产线需要多少可能性?我们养不养得起把可能性变成本事的人?两头都答错了,选哪个都是翻车,只是翻的方向不同。 六、三问定形态,中间形态看穿一层 把前面的账收成三个问题,图三上半张就是这张决策卡。 一问换型频率:一年一到两次、产品型号多年稳定的,倾向一体机;一月一到两次、接单式生产的,倾向平台。这一问的分量最重,因为它直接决定上面那条成本曲线交不交叉。二问现场人力:有自动化团队、有视觉工程师编制、招得到也留得住人的,平台接得住;只有操作工加一个设备科、编制批不下来的,一体机省心。三问预算口径:只有一次性 CapEx、后续 OpEx 一概批不下来的,别碰平台——平台是吃运营预算的生意,养人养服务包,年年都要花钱,断一年,攒下的能力就开始漏气。 三问答案分裂怎么办?看中间形态。现在厂商都学精了:一体机宣称“支持二次开发”,平台宣称“行业包开箱即用”。这时候要问穿一件事——开放到哪一层。 参数级开放:能调阈值、公差、速度。这不算开放,一体机本来就该给。算子级开放:能自己组流程、加检测项、接自己的模型——这才算二次开发的门槛。架构级开放:能换相机、换执行机构、写插件甚至动源码——这是真平台。很多“支持二次开发”的宣传只到参数级,等于没开放;反过来“行业包”要看是真预调好的方案,还是只能演示的素材。层级行不行,别听销售说,要一条条写进合同验收项。 七、收个尾 写到这篇,这个系列从选型、算子、数据、算法、标注、发布、验收一路下来,最后一环——交付形态——也补上了。 形态本身没有优劣,错配才有。买一体机买的是省心,买平台买的是可能性,而可能性是要花钱养人的。选之前先想的不是“哪个好”,是“我的产线三年后会变成什么样、我身边会站着什么人”。 下一篇顺着这个话头往实处走:就算形态选对了,真到换型那天,到底要动哪些东西?配方、机构、光源、标定、模型,哪些能沿用、哪些必须重来?我把“换型”拆成一份清单,算一次换型的真实成本——那是很多采购在签合同前从没算过的账。 你如果踩过形态选错的坑——买一体机改型改到怀疑人生的,或者买平台养不起人的——评论区聊聊,你们的案子就是下一篇的素材。 (系列前篇:《我所了解的机器视觉检测产品应用(9):验收的 99%,量产怎么就成了 95%?》《(8):模型更新怎么上线?影子跑、灰度切,和那颗回滚按钮》) 专栏作家 互联网产品经理Mik,公众号:逆袭产品汪,人人都是产品经理专栏作家。专注于ToB领域产品,包括机器视觉、工业大数据、MES、ERP和物联网等方面。 本文原创发布于人人都是产品经理,未经许可,禁止转载 题图来自 Unsplash,基于 CC0 协议 该文观点仅代表作者本人,人人都是产品经理平台仅提供信息存储空间服务。
InfoQ - 促进软件开发领域知识与创新的传播
点击查看原文>
Балл: 54.57Уверенность: 54%
Подробнееvelog
The Bahrain vs Kuwait, 11th Match, ICC Men’s T20 World Cup Sub Regional Asia Qualifier A 2026 is set to bring together two competitive Asian sides in an important early-stage fixture. Both teams will look to make a strong start to their qualifying campaign, with disciplined batting, effective bowling, and sharp fielding likely to play key roles. Bahrain will aim to build momentum through a balanced team performance, while Kuwait will look to challenge their opponents with an energetic approach. The match should provide an interesting contest as both sides target a positive result in the race for progression. Bahrain vs Kuwait, Match Details: Location: Bangi, Selangor, Malaysia Venue: UKM-YSD Oval Date & Time: 9th Sep/06:15 AM PST LOCAL Time Streaming: JioHotstar Establish: 2010 Capacity: 5000 Owner: Universiti Kebangsaan Malaysia Home Team: – End Name: – Flood Light: Yes Bahrain vs Kuwait, ODI Head-to-Head Records: Total Match: 10 Bahrain: 3 Kuwait: 7 No Result: 0 Tie: 0 Team Form (Last Five Matches, Most Recent First) Bahrain: W W L L W Kuwait: W W L W L Bahrain vs Kuwait Last Five Head-to-Head Records: Teams: Bahrain vs Kuwait | Winner: Bahrain | Date: 14 Dec 2024 Teams: Kuwait vs Bahrain | Winner: Kuwait | Date: 15 Apr 2024 Teams: Bahrain vs Kuwait | Winner: Bahrain | Date: 5 Mar 2024 Teams: Kuwait vs Bahrain | Winner: Kuwait | Date: 2 Nov 2023 Teams: Kuwait vs Bahrain | Winner: Kuwait | Date: 16 Sep 2023 Bahrain vs Kuwait, Weather Report: Temperature: 33° Humidity: 70% Wind Speed: 10km/hr Cloud Cover: 45% Bahrain vs Kuwait, Pitch Report: Bahrain vs Kuwait UKM-YSD Cricket Oval in Bangi generally offers a balanced surface where both batters and bowlers can find opportunities. The pitch may provide some assistance to pace bowlers with the new ball, while spinners can become more effective as the game progresses and the surface slows down. Batters who settle early can score freely, but shot selection becomes increasingly important during the middle and later overs. Teams winning the toss may consider batting first to make the most of the fresher conditions before the pitch potentially becomes slower. Match Performance: Total Matches Played: 30 1st Batting Team Won: 17 2nd Batting Team Won: 13 No Result: 0 Average Batting Score: 113 Highest Score: 227/4 Lowest Score: 10/10 Pitch Report: Batting Pitch Bahrain vs Kuwait, Playing 11: Bahrain: Abdulla Pervaiz (WK), Ali Dawood, Abdul Majid, Ahmer Bin Nasir, Fiaz Ahmed, Imran Anwar, Junaid Aziz, Prashant Kurup, Rizwan Butt, Sohail Ahmed, Shujaat Rasool Kuwait: Mohammed Aslam (C), Meet Bhavsar (WK), Ravija Sandaruwan, Ali Zaheer, Muhammad Umar, Adnan Idrees, Bilal Tahir, Sandaruwan Chinthaka, Mohamed Shafeeq, Yasin Patel, Shahrukh Quddus Injury And Availability News: We will add any updates here. Bahrain vs Kuwait, Betting Tips: Who Will Win The Toss: Bahrain Match Winner: Kuwait Total Boundaries: 30+ Player Of The Match: Mohammed Aslam 1st Innings Total: 240+ Most Wicket Taker: Ali Zaheer My prediction Kuwait will win this match.
velog
들어가며 1편 에서 지식그래프와 온톨로지의 차이, 구축 절차를 정리했다. 이번 글은 그 절차대로 대전의 실제 하천·수위관측소 데이터로 지식그래프를 만들어 본 기록이다. 처음에는 가상의 도시를 하나 만들어서 가짜 하천·관측소 데이터로 연습했다. 그걸로는 잘 돌아갔는데, 실제 데이터로 바꾸자마자 문제가 줄줄이 나왔다. 원본에 값이 빠져 있기도 하고, 출처마다 값이 달랐고, 관측소마다 수위 기준이 다른 경우도 있었다. 구축 과정과 함께 이런 문제를 어떻게 처리했는지도 같이 적었다. 작업 환경은 Windows 11이고, Docker(Rancher Desktop)로 Neo4j 5.26 Community를 띄웠다. 데이터 수집은 Python 3.11 표준 라이브러리만으로 했다. 1) 이번 글에서 다룰 내용 답할 질문 정하기와 그래프 모델 설계 (노드 3종, 관계 4종) WAMIS API로 대전 관측소 19곳 수집 ( fetch_wamis.py ) Docker로 Neo4j 띄우고 LOAD CSV + MERGE 로 적재 가변 길이 경로 질의로 "금강까지 몇 단계?" 같은 질문에 답하기 트러블슈팅 — 경로 변환, BOM, 콘솔 인코딩, 원천 결측, 집계 중복, 수위 기준 2) 배경 / 설계 2-1. 답할 질문 1편에서 정리한 대로 질문부터 적었다. 하천이 어디로 흘러가는지, 넘치면 어디를 봐야 하는지처럼 연결을 따라가야 답이 나오는 질문 위주로 골랐다. # 질문 필요한 노드·관계 Q1 금강으로 흘러드는 대전 하천은 모두 무엇인가? (몇 단계든) 하천, 지류이다 Q2 유등천이 넘치면 어느 구, 어느 관측소를 봐야 하나? (상류 포함) 하천, 관측소, 행정구역 Q3 하천마다 합류 직전의 관측소는 어디인가? 관측소의 합류점거리 Q4 구별로 관측소가 몇 곳이고 어떤 하천을 보나? 관측소, 위치한다 Q5 어떤 하천과도 연결되지 않은 관측소가 있나? (품질 점검) 관측한다 2-2. 데이터 출처 관측소 정보는 처음에 웹 검색으로 모으려고 했다. 검색 결과에 관측소 이름이랑 주의보 수위 같은 값이 요약돼서 나오길래 금방 될 줄 알았는데, 근거 페이지를 열어 보니 404였다. 원문으로 확인이 안 되는 숫자를 실제 데이터라고 넣을 수는 없어서 API로 방향을 바꿨다. API도 처음엔 HRFCO 시리즈 때 쓰던 홍수통제소 API를 다시 쓰려고 했다. 그런데 인증키를 다시 꺼내서 설정하는 게 귀찮아서 키 없이 되는 게 있나 찾아봤고, WAMIS(국가수자원관리종합정보시스템)에서 관측소 제원이랑 시자료를 키 없이 준다는 걸 알게 됐다. 게다가 제원에 하천명까지 들어 있어서 오히려 이쪽이 더 편했다. 데이터 출처 비고 관측소 19곳 제원·최신 수위 WAMIS Open API ( wl_dubwlobs , wl_obsinfo , wl_hrdata ) 인증키 불필요, 스크립트로 수집 국가하천 4개 연장·합류점 대전시 하천관리사업소 하천현황 금강·갑천·유등천·대전천 갑천 지천 대전시 3대하천현황 진잠천·유성천·관평천·매노천 등 대동천→대전천, 반석천→유성천 위키백과 대전시 자료에 없는 2건 보완 출처를 확인 못 한 값은 넣지 않고 비워 두기로 했다. 이 원칙 때문에 뒤에서 몇 번 손해를 보긴 했는데, 그래도 틀린 값이 섞이는 것보다는 낫다고 봤다. 2-3. 그래프 모델 (관측소)-[:관측한다]->(하천) (관측소)-[:위치한다]->(행정구역) (하천)-[:흐른다]->(행정구역) ← 관측소 주소(양쪽 강변)에서 도출 (하천)-[:지류이다]->(하천) ← 몇 단계든 따라갈 수 있는 고리 라벨과 관계 타입은 한글로 지었다. Cypher가 유니코드 식별자를 허용해서 백틱 없이 (:하천) , [:지류이다] 로 쓸 수 있다. 업무 담당자랑 같이 질의를 볼 일이 있으면 한글 쪽이 훨씬 읽기 편하다. 2-4. 전체 흐름과 프로젝트 구조 knowledge-graph-lab/ ├── scripts/fetch_wamis.py # WAMIS → stations.csv, station_regions.csv ├── data/ │ ├── stations.csv # 관측소 19 (스크립트 생성) │ ├── station_regions.csv # 관측소-구 26 (스크립트 생성) │ ├── rivers.csv # 하천 10 (출처 컬럼 포함) │ ├── river_tributary.csv # 합류 9 (출처 컬럼 포함) │ └── regions.csv # 자치구 5 ├── neo4j/docker-compose.yml ├── cypher/ │ ├── 01_schema.cypher # 유일성 제약조건 │ ├── 02_load.cypher # CSV → 노드·관계 │ └── 03_queries.cypher # Q1~Q5 └── SOURCES.md # 항목별 출처와 알려진 한계 3) 목표 상태 항목 값 노드 34 (관측소 19 · 하천 10 · 행정구역 5) 관계 60 (관측한다 18 · 위치한다 19 · 지류이다 9 · 흐른다 14) 검증 원천 CSV 행 수와 노드·관계 수 일치 질의 Q1~Q5 결과가 원천 자료로 손 검산한 값과 일치 4) 데이터 분석 — WAMIS 응답 살펴보기 4-1. 관측소 목록 → 대전만 고르기 wl_dubwlobs 를 호출하면 전국 관측소 목록 1,363건이 온다. 관측소명이 대전시(원촌교) 형식이라 대전시( 로 시작하는 것만 골랐더니 19곳이었다. http://www.wamis.go.kr:8080/wamis/openapi/wkw/wl_dubwlobs?output=json 4-2. 관측소 제원 — 하천명이 들어 있다 wl_obsinfo 에 관측소 코드를 넣으면 제원이 나온다. 여기서 제일 반가웠던 게 rivnm (하천명) 필드다. 관측소 → 하천 관계를 다른 자료 없이 바로 만들 수 있었다. 그래프에 쓴 필드는 이 정도다. 필드 예 (원촌교) 처리 wlobscd 3009680 관측소 노드 식별자 rivnm 갑천 관측한다 관계 addr 대전광역시 대덕구 대화동 원촌교 (대전광역시시 유성구 원촌동) 괄호 밖 = 본주소 구, 괄호 안 = 맞은편 강변 구 lat / lon 36-22-42 / 127-24-36 도-분-초 문자열 → 십진수, point() 로 저장 bsnara 609.4 유역면적(km2) rvmjctdis 10.32 합류점까지 거리(km) — Q3에 사용 주소를 보다가 괄호 안에 다리 건너편 주소가 들어 있다는 걸 알았다. 하천은 구 경계를 따라 흐르는 경우가 많아서, 괄호 안 구까지 모으면 "이 하천이 지나는 구"를 어느 정도 만들 수 있다. 원본에는 대전광역시시 같은 오타나 lat 끝에 붙은 공백도 있어서, 공백 기준으로 잘라서 구 이름만 고르고 strip() 을 했다. 4-3. 시자료 수위 wl_hrdata 는 관측소별 시간 단위 수위를 준다. 최근 이틀 치를 받아서 값이 있는 마지막 행을 최신 수위로 썼다. .../wl_hrdata?obscd=3009680&startdt=20261006&enddt=20261007&output=json → [{"ymdh": "2026100601", "wl": ".45"}, ..., {"ymdh": "2026100709", "wl": ".40"}] 수위가 ".45" 처럼 앞의 0이 빠진 문자열로 와서 float() 로 바꿨다. 5) 해결 (구현) 5-1. 수집 스크립트 — fetch_wamis.py 표준 라이브러리만 썼다. 관측소 CSV와 관측소-구 CSV 두 개를 만든다. """WAMIS(국가수자원관리종합정보시스템) Open API 에서 대전 수위관측소 데이터를 받아 CSV 로 만든다. 인증키가 필요 없다. 실행: python scripts/fetch_wamis.py 결과: data/stations.csv, data/station_regions.csv """ import csv import json import re import urllib.request from datetime import datetime, timedelta from pathlib import Path BASE = "http://www.wamis.go.kr:8080/wamis/openapi/wkw" DATA = Path(__file__).resolve().parent.parent / "data" DISTRICTS = ["동구", "중구", "서구", "유성구", "대덕구"] def get(endpoint, **params): query = "&".join(f"{k}={v}" for k, v in {**params, "output": "json"}.items()) with urllib.request.urlopen(f"{BASE}/{endpoint}?{query}", timeout=30) as res: return json.load(res) def dms_to_decimal(dms): """'127-24-36' → 127.41""" d, m, s = (float(x) for x in dms.strip().split("-")) return round(d + m / 60 + s / 3600, 6) def districts_in(addr): """주소에서 대전 자치구를 순서대로 뽑는다. 괄호 안은 맞은편 강변 주소다.""" main, _, opposite = addr.partition("(") pick = lambda text: [t for t in re.split(r"\s+", text) if t in DISTRICTS] return pick(main), pick(opposite) def latest_level(obscd): """최근 이틀 시자료 중 마지막 값. 자료가 없으면 (None, None).""" today = datetime.now() rows = get("wl_hrdata", obscd=obscd, startdt=(today - timedelta(days=1)).strftime("%Y%m%d"), enddt=today.strftime("%Y%m%d")).get("list", []) rows = [r for r in rows if r.get("wl") not in (None, "", "-")] if not rows: return None, None last = rows[-1] return float(last["wl"]), last["ymdh"] def main(): stations = [s for s in get("wl_dubwlobs")["list"] if s["obsnm"].startswith("대전시(")] station_rows, region_rows = [], [] for s in stations: info = get("wl_obsinfo", obscd=s["obscd"])["list"][0] level, observed_at = latest_level(s["obscd"]) station_rows.append({ "id": info["wlobscd"], "name": info["obsnm"], "river": info["rivnm"] or "", # 원천 결측은 빈 값으로 둔다 — 추정하지 않는다 "agency": info["mggvcd"], "opened": info["obsopndt"], "basin_area_km2": info["bsnara"], "dist_to_confluence_km": info["rvmjctdis"], "lat": dms_to_decimal(info["lat"]), "lon": dms_to_decimal(info["lon"]), "address": info["addr"].strip(), "latest_level_m": "" if level is None else level, "observed_at": observed_at or "", }) main_side, opposite_side = districts_in(info["addr"]) for d in main_side: region_rows.append({"station_id": info["wlobscd"], "district": d, "side": "본주소"}) for d in opposite_side: region_rows.append({"station_id": info["wlobscd"], "district": d, "side": "맞은편"}) for name, rows in [("stations.csv", station_rows), ("station_regions.csv", region_rows)]: # utf-8-sig: BOM 을 붙여야 엑셀(한국어 Windows)이 한글을 깨뜨리지 않고 연다 with open(DATA / name, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=rows[0].keys()) writer.writeheader() writer.writerows(rows) print(f"{name}: {len(rows)}행") if __name__ == "__main__": main() > python scripts/fetch_wamis.py stations.csv: 19행 station_regions.csv: 26행 5-2. 정리 CSV — 출처 컬럼을 남긴다 하천과 합류 관계는 공식 자료를 보고 직접 옮겨 적었다. 손으로 만든 데이터라 행마다 source 컬럼을 두고, 그래프 관계 속성에도 그대로 넣었다. 나중에 "이 연결은 어디서 온 거지?" 싶을 때 질의로 바로 확인할 수 있다. child,parent,source 갑천,금강,대전시 하천관리사업소 하천현황 유등천,갑천,대전시 하천관리사업소 하천현황 대전천,유등천,대전시 하천관리사업소 하천현황 진잠천,갑천,대전시 3대하천현황 (갑천 지천) 유성천,갑천,대전시 3대하천현황 (갑천 지천) 관평천,갑천,대전시 3대하천현황 (갑천 지천) 매노천,갑천,대전시 3대하천현황 (갑천 지천) 대동천,대전천,위키백과 대동천(대전) 반석천,유성천,위키백과 반석천 rivers.csv 의 등급(국가하천/지방하천)도 출처가 있는 것만 채웠다. 대전시 자료에 국가하천으로 나온 4개와 위키백과에 지방하천으로 나온 2개만 값이 있고, 나머지 4개는 비어 있다. 5-3. Neo4j 실행 — docker-compose.yml # 지식그래프 실습용 Neo4j (로컬 데모 전용 — 비밀번호를 운영 환경에 그대로 쓰지 말 것) services: neo4j: image: neo4j:5-community container_name: kg-neo4j ports: - "7474:7474" # 브라우저 UI http://localhost:7474 - "7687:7687" # Bolt (드라이버·cypher-shell) environment: NEO4J_AUTH: neo4j/kgdemo1234 volumes: - ../data:/import:ro # LOAD CSV 가 읽는 위치 - ../cypher:/cypher:ro # 스크립트 실행용 /import 는 Neo4j가 LOAD CSV ... FROM 'file:///...' 로 읽는 기본 경로라서 CSV 폴더를 여기에 읽기 전용으로 붙였다. 5-4. 스키마 — 유일성 제약조건 // 같은 식별자를 가진 노드가 두 번 만들어지지 않도록 막는다 (ERD의 PK 역할). CREATE CONSTRAINT 행정구역_이름 IF NOT EXISTS FOR (n:행정구역) REQUIRE n.이름 IS UNIQUE; CREATE CONSTRAINT 하천_이름 IF NOT EXISTS FOR (n:하천) REQUIRE n.이름 IS UNIQUE; CREATE CONSTRAINT 관측소_코드 IF NOT EXISTS FOR (n:관측소) REQUIRE n.코드 IS UNIQUE; 제약조건을 걸면 인덱스도 같이 생겨서, 뒤에서 MERGE / MATCH 로 노드를 찾을 때도 빠르다. 5-5. 적재 — LOAD CSV + MERGE MERGE 는 없으면 만들고 있으면 그대로 둔다. 스크립트를 몇 번 다시 돌려도 결과가 같아서 테스트하면서 마음 편하게 재실행할 수 있었다. // --- 노드 --- LOAD CSV WITH HEADERS FROM 'file:///stations.csv' AS row MERGE (s:관측소 {코드: row.id}) SET s.이름 = row.name, s.운영기관 = row.agency, s.관측개시일 = date(row.opened), s.유역면적_km2 = toFloat(row.basin_area_km2), s.합류점거리_km = toFloat(row.dist_to_confluence_km), s.위치 = point({latitude: toFloat(row.lat), longitude: toFloat(row.lon)}), s.주소 = row.address, s.최신수위_m = toFloat(row.latest_level_m), s.관측시각 = row.observed_at; // (행정구역·하천 노드도 같은 방식 — 생략) // --- 관계 --- LOAD CSV WITH HEADERS FROM 'file:///river_tributary.csv' AS row MATCH (child:하천 {이름: row.child}), (parent:하천 {이름: row.parent}) MERGE (child)-[r:지류이다]->(parent) SET r.출처 = row.source; // 하천명이 비어 있는 관측소(원천 결측)는 관계를 만들지 않는다. LOAD CSV WITH HEADERS FROM 'file:///stations.csv' AS row WITH row WHERE row.river IS NOT NULL MATCH (s:관측소 {코드: row.id}), (rv:하천 {이름: row.river}) MERGE (s)-[:관측한다]->(rv); LOAD CSV WITH HEADERS FROM 'file:///station_regions.csv' AS row WITH row WHERE row.side = '본주소' MATCH (s:관측소 {코드: row.station_id}), (r:행정구역 {이름: row.district}) MERGE (s)-[:위치한다]->(r); // 하천이 지나는 구: 관측소 주소(양쪽 강변)에서 이끌어낸다. 관측소가 없는 구간은 반영되지 않는다. LOAD CSV WITH HEADERS FROM 'file:///station_regions.csv' AS row MATCH (:관측소 {코드: row.station_id})-[:관측한다]->(rv:하천), (r:행정구역 {이름: row.district}) MERGE (rv)-[:흐른다]->(r); LOAD CSV 는 빈 칸을 null 로 읽는다. 그래서 WHERE row.river IS NOT NULL 한 줄이면 하천명이 없는 행을 건너뛸 수 있다. 적재가 끝나면 건수부터 맞춰 봤다. MATCH (n) RETURN labels(n)[0] AS 라벨, count(*) AS 개수 ORDER BY 라벨; MATCH ()-[r]->() RETURN type(r) AS 관계, count(*) AS 개수 ORDER BY 관계; 라벨, 개수 관계, 개수 "관측소", 19 "관측한다", 18 ← 관측소 19 − 하천명 결측 1 "하천", 10 "위치한다", 19 "행정구역", 5 "지류이다", 9 "흐른다", 14 관측한다 가 19가 아니라 18인 게 처음엔 이상해 보였는데, 하천명이 비어 있는 관측소 1곳 때문이었다. 이 얘기는 트러블슈팅 D에 정리했다. 5-6. 질의와 결과 Q1. 금강으로 흘러드는 대전 하천 전부 가변 길이 경로 *1.. 을 쓴다. MATCH path = (t:하천)-[:지류이다*1..]->(:하천 {이름: '금강'}) RETURN t.이름 AS 하천, length(path) AS 단계, [n IN nodes(path) | n.이름] AS 경로 ORDER BY 단계, 하천; 하천, 단계, 경로 "갑천", 1, ["갑천", "금강"] "관평천", 2, ["관평천", "갑천", "금강"] "매노천", 2, ["매노천", "갑천", "금강"] "유등천", 2, ["유등천", "갑천", "금강"] "유성천", 2, ["유성천", "갑천", "금강"] "진잠천", 2, ["진잠천", "갑천", "금강"] "대전천", 3, ["대전천", "유등천", "갑천", "금강"] "반석천", 3, ["반석천", "유성천", "갑천", "금강"] "대동천", 4, ["대동천
velog
Ubuntu에서 Lotion 설치해보기 Week6. curl과 Git을 설치한 뒤에도 앱이 열리지 않았던 기록 Ubuntu로 한 학기 살아보기 프로젝트를 하면서, 평소에 쓰던 Notion도 계속 사용하고 싶어졌다. Linux에서 사용할 방법을 찾다가 Lotion 이라는 프로젝트를 알게 됐다. 설치 중에는 curl 과 git 이 없어 두 번 막혔다. 필요한 도구를 설치하고 다시 진행했지만, 마지막에는 아이콘을 눌러도 앱이 열리지 않았다. 이번 글은 그 과정과 끝내 확인하지 못한 부분을 함께 정리한 기록이다. 앞부분의 명령은 당시 실습 기록이다. 현재 설치를 권하는 절차로 읽지 않도록 주의하자. 명령의 역할과 점검 방법은 설명을 보탰고, 지금도 설치 파일이 배포되는지는 마지막 절에서 따로 확인했다. 1. Lotion은 어떤 프로그램이었나 Lotion은 Notion을 Linux 데스크톱에서 사용할 수 있게 만든 비공식 클라이언트 다. 현재 프로젝트도 Electron 기반이라고 소개하며, Notion이 제작하거나 지원하는 앱이 아니라고 명시한다. Lotion 프로젝트 당시 보고서의 Lotion 로고. 아래쪽에도 UNOFFICIAL이라고 적혀 있다. 원래 보고서에서는 이를 “Notion native”라고 적었다. 하지만 그 표현만으로 Notion의 공식 Linux 앱이 설치됐다고 볼 수는 없다. 아래 명령 끝의 native 역시 당시 설치 스크립트에 넘겼던 인자다. 이 글에서는 실제 사용한 프로젝트 이름인 Lotion으로 구분한다. 2. 먼저 압축 도구를 준비했다 당시 안내를 따라 7-Zip 관련 패키지부터 설치했다. 아래 세 줄은 보고서에 남아 있는 명령을 그대로 옮겼다. sudo add-apt-repository universe Ubuntu의 패키지 저장소 구성 요소인 universe 를 활성화한다. 프로그램을 받는 출처를 설정하는 단계다. sudo 는 이 명령을 관리자 권한으로 실행한다. 저장소 추가 명령 sudo apt update 등록된 저장소에서 설치 가능한 패키지 정보 를 새로 받는다. 이 단계에서 설치된 프로그램 전체가 업그레이드되는 것은 아니다. APT 설명 sudo apt install p7zip-full p7zip-rar 7-Zip 도구와 RAR 형식 지원을 위한 패키지를 설치한다. 여기까지는 Lotion 자체를 설치한 것이 아니라 당시 안내에서 요구한 압축 도구를 준비한 것이다. 지금도 같은 패키지 이름과 순서가 필요한지는 현재 배포 방식에 맞춰 확인해야 한다. 7-Zip 패키지 설명 3. 설치 파일을 받으려는데 curl이 없었다 다음으로 GitHub에 있던 설치 스크립트를 내려받으려고 했다. setup.sh 는 설치 명령들이 담긴 셸 스크립트 파일이다. curl https://raw.githubusercontent.com/puneetsl/lotion/master/setup.sh > setup.sh curl 은 URL을 통해 데이터를 주고받는 도구다. 여기서는 해당 주소의 내용을 받아 출력한다. 뒤의 > setup.sh 는 그 출력을 현재 폴더의 파일에 저장한다. 이 한 줄의 역할은 스크립트 다운로드까지다. 스크립트를 실행하는 명령은 뒤에 따로 나온다. curl 설명 · Bash 출력 저장 > 는 같은 이름의 파일이 있으면 일반적인 설정에서 기존 내용을 비우고 새로 쓴다. 파일이 생겼다는 사실만으로 다운로드가 정상적으로 끝났다고 판단해서는 안 된다. 오류 출력과 실제 파일 내용을 함께 봐야 한다. 첫 시도에서는 아래 메시지가 나왔다. 원본 화면의 메시지: Command 'curl' not found, but can be installed with: Lotion의 다운로드 주소에 접속하기 전에, 내 환경에서 curl 명령을 찾지 못한 것이다. 당시에는 다음 명령으로 curl을 설치했다. sudo apt install curl APT를 통해 curl 패키지를 설치한다. 설치한 뒤 같은 다운로드 명령을 다시 실행했다. 당시 보고서의 후속 화면에는 전송 진행률과 2,952바이트를 받은 기록이 표시됐다. 다운로드 명령은 실행됐지만, 받은 파일의 내용이나 안전성을 검증한 기록은 없다. 4. 이번에는 설치 스크립트가 Git을 요구했다 다운로드 이후에는 아래 두 명령을 실행했다. 외부에서 받은 스크립트를 관리자 권한으로 실행한 당시 기록 이므로 그대로 따라 하기 전에 출처와 파일 내용을 확인해야 한다. 스크립트가 실행되면 시스템 파일이나 설정을 바꿀 수 있다. chmod +x setup.sh chmod 는 파일 권한을 바꾸는 명령이고, +x 는 실행 권한을 더한다. 이 명령 자체로 스크립트가 실행되지는 않는다. 실행 권한 설명 sudo ./setup.sh native 현재 폴더를 뜻하는 ./ 를 붙여 setup.sh 를 실행하고, native 라는 인자를 전달한다. sudo 가 있으므로 관리자 권한으로 실행된다. 파일을 내려받는 단계와 그 안의 명령을 실제로 수행하는 단계는 이렇게 나뉜다. 여기서 다음 메시지가 나왔다. 아래 문장은 당시 터미널 화면에서 그대로 옮겼다. Command git is required to run this script 이번에는 설치 스크립트가 실행되다가 필요한 git 명령을 확인하는 단계에서 막혔다. 앞의 curl 오류와 달리, 내려받은 스크립트가 자체적으로 요구한 도구가 부족했던 것이다. sudo apt install git Git을 설치하는 명령이다. curl은 이 과정에서 스크립트 파일을 받아오는 데 쓰였고, Git은 프로젝트의 저장소와 변경 이력을 다루는 도구다. 예를 들어 git clone 은 저장소를 로컬로 복제한다. 원본 화면으로 확인되는 것은 이 설치 스크립트가 Git을 필수로 요구했다는 데까지다. 실제로 어떤 저장소를 복제했는지는 별도 로그가 남아 있지 않다. Git 저장소 복제 설명 당시에는 Git을 설치한 뒤 설치 명령을 다시 실행했다. 보고서에는 설치가 완료됐다고 적었지만, 설치 완료 로그나 설치된 버전을 확인한 화면은 남겨 두지 못했다. 5. 아이콘이 생겨도 실행 확인은 남아 있었다 설치 후 아이콘을 눌렀을 때 오류 메시지가 나타났고, 앱이 열리지 않았다. 몇 번을 다시 눌러보고 시간이 지난 뒤에도 시도했지만 결과는 같았다. 결국 앱 실행에는 실패했고, 원인은 확인하지 못했다. 마지막 오류의 정확한 문구나 화면도 남아 있지 않다. 앞에서 해결한 curl·Git 문제를 마지막 실행 실패의 원인으로 연결할 근거는 없다. 당시에는 GitHub 안내가 오래돼 그사이에 다른 오류가 생긴 것 아닐까 생각했다. 가능성을 떠올린 정도였고, 버전이나 실행 로그를 비교해 확인하지는 못했다. 지금 기록을 보완한다면 확인할 것 아래는 당시 실행한 해결 과정이 아니라, 원인을 좁히기 위해 덧붙인 점검 순서다. 실패한 단계와 오류 전문 : 다운로드, 설치 스크립트, 앱 실행 중 어느 단계인지 나누고 오류 문장을 그대로 남긴다. 환경과 버전 : Ubuntu 버전, CPU 아키텍처, 설치 파일 이름과 Lotion 버전을 기록한다. 같은 증상처럼 보여도 실행 환경이 다를 수 있다. 아이콘과 실행 파일 : 아이콘에 연결된 실행 경로와 파일이 실제로 있는지 확인한다. 앱 실행 로그를 확보한 뒤 해당 버전의 알려진 문제와 비교한다. 6. 지금도 설치할 수 있을까 2026년 10월 7일 확인 기준, 같은 Lotion 프로젝트에서 Linux용 설치 파일을 배포하고 있다. 릴리스 목록에는 2026년 5월 26일 공개된 v1.6.0이 표시된다. 배포 파일에는 Ubuntu·Debian용 amd64·arm64 .deb 패키지, Linux용 x64·arm64 압축 파일, Fedora 계열용 RPM이 있다. Lotion 릴리스 · 전체 배포 파일 현재 README의 Ubuntu 안내는 .deb 파일을 받은 뒤 APT로 설치하는 방식이다. 예시 명령은 다음과 같다. 이 명령은 현재 문서의 안내를 소개한 것이며, 이번 글을 정리하면서 직접 실행한 결과는 아니다. sudo apt install ./lotion_1.6.0_amd64.deb 현재 폴더에 내려받은 .deb 파일을 APT로 설치한다. 파일명의 amd64 는 x86-64 계열용이라는 뜻이다. ARM64용 패키지는 lotion_1.6.0_arm64.deb 로 별도 배포된다. 프로젝트는 APT가 필요한 의존성을 처리한다고 안내한다. 다만 Ubuntu 버전별 검증표는 확인하지 못했고, 내 당시 환경에서 이 버전이 정상 실행되는지도 시험하지 않았다. README 따라서 지금 확인할 수 있는 답은 “현재 설치 파일과 설치 안내가 제공된다” 까지다. 과거의 setup.sh 절차를 그대로 재사용하거나, 당시 실행 실패가 최신 버전에서 해결됐다고 단정할 수는 없다. 비공식 앱의 코드는 로그인 세션과 화면을 다루므로, 사용 전에는 배포 출처와 변경 내역, 필요한 권한도 살펴봐야 한다. Notion은 왜 Linux를 공식 지원하지 않을까 실습을 하면서 이 점도 궁금했다. 당시에는 기술적인 부담이나 수익성 때문일 수 있다고 추측했지만, 이를 뒷받침하는 공식 근거를 확인한 것은 아니었다. 현재 Notion 도움말에서 확인되는 내용은 Linux 앱을 제공하지 않으며 브라우저에서 사용할 수 있다 는 안내다. 구체적인 사업적 이유까지는 이 문서로 알 수 없다. Notion의 Linux 지원 안내 마치며 이번 실습은 앱 실행 문제를 해결하지 못한 채 끝났다. 그래도 기록을 다시 나눠 보니 curl 오류는 파일을 받기 전, Git 오류는 설치 스크립트 실행 중, 마지막 문제는 앱을 열 때 발생했다는 차이가 보였다. 설치 명령을 끝까지 입력한 것과 실제로 사용할 수 있는 상태가 된 것은 각각 확인이 필요했다. 실패한 단계와 정확한 메시지를 남기는 일도 설치 과정만큼 중요하다는 점이 이번 기록에 남았다. 이어서 읽기 이전 글: Install R-Ubuntu
velog
26.10.07 개인프로젝트 6일차 - 정답지 재검토 및 재채점 평가셋 정답지 재검토 및 재채점 앞선 실험에서는 RAG의 검색과 답변 생성을 개선하면서 여러 단계의 성능을 측정했다. 그런데 실험 결과를 다시 살펴보면서 한 가지 의문이 생겼다. 정말 모델이 틀린 것인지, 아니면 내가 만든 정답지와 채점 기준이 잘못된 것인지 구분할 필요가 있었다. 특히 일부 문항에서는 챗봇이 질문에 필요한 내용을 어느 정도 답했는데도, 정답지에서 질문에 직접 요구되지 않은 내용을 필수 조건으로 설정해 놓아 부분정답이나 오답으로 처리되는 경우가 있었다. 따라서 이번에는 모델을 추가로 개선하기 전에 평가셋 자체를 다시 검토 했다. 1. 정답지 검토 기준 문제가 있는 문항을 다음 세 가지로 나누어 검토했다. 1 정답지가 잘못된 경우 질문과 실제 공고 원문을 기준으로 봤을 때 기존 정답 기준이 질문보다 과하거나, 원문과 맞지 않는 경우이다. → 정답지를 수정한다. 2 정답지에는 문제가 없는 경우 질문이 요구하는 내용과 정답 기준이 적절한데, 챗봇이 필요한 내용을 제대로 답하지 못한 경우이다. → 모델의 검색 또는 답변 생성을 개선한다. 3 애매한 경우 질문의 표현이나 의도에 따라 답변 범위가 달라질 수 있는 경우이다. → 질문과 정답 기준을 다시 검토한다. 여기서 가장 중요한 원칙은 챗봇의 답변을 보고 정답지를 수정하지 않는 것 이다. 정답지는 질문과 실제 공고 원문을 기준으로 판단하고, 챗봇의 답변은 그 이후에 평가한다. 2. 문제가 발견된 문항 검토 과정에서 주요하게 확인한 문항은 A03, A04, A05, A09, A14, A16 이었다. A03. 쿤텍 신입은 무슨 언어 할 줄 알아? 기존 정답 기준에는 시스템 프로그래밍 이 포함되어 있었다. 하지만 질문은 "무슨 언어를 할 줄 알아야 하는가" 를 묻고 있었다. 따라서 질문에서 직접 요구하지 않은 시스템 프로그래밍 까지 반드시 언급해야 정답으로 인정하는 것은 질문의 범위를 넘어선다고 판단했다. 판단: 1 정답지 수정 수정 후에는 질문에서 요구하는 언어 관련 정보가 정확하게 답변되었는지 를 중심으로 평가한다. A04. 아인잡 프론트엔드 연봉 기존 평가에서는 연봉 질문에 스톡옵션 정보 를 필수 옵션으로 추가했었다. 하지만 연봉과 스톡옵션은 서로 다른 보상 항목이고, 질문에서 직접 요구한 것은 연봉이었다. 따라서 연봉 질문에 스톡옵션까지 필수적으로 요구하는 기존 기준은 적절하지 않다고 판단했다. 판단: 1 정답지 수정 기존: 연봉 + 스톡옵션을 함께 언급해야 함 수정: 연봉 정보를 정확하게 안내했는지를 평가 스톡옵션은 별도의 보상 정보로 분리한다. A05. Spring만 해 봤는데 지원해도 돼? 기존 기준에서는 Spring 경험을 바탕으로 Java/Kotlin까지 연결해서 판단하도록 요구하고 있었다. 하지만 질문 자체는 "Spring만 해 봤는데 지원해도 돼?" 라는 질문이다. 따라서 Java/Kotlin까지 반드시 언급해야 하는지는 질문의 해석에 따라 달라질 수 있었다. 판단: 3 애매한 경우 질문 자체가 어느 범위까지 답변을 요구하는지 먼저 검토할 필요가 있다고 판단했다. A09. 들어가면 무슨 일 해? 기존 정답 기준에는 EMR·CRM 백엔드 API 설계 및 비즈니스 로직 구현 이라는 표현이 사용되어 있었다. 이 때문에 답변에서 CRM이 따로 언급되지 않으면 일부 내용이 빠졌다고 판단했다. 하지만 챗봇의 답이 정답 내용이 맞는데 정답 기준에서 특정 표현을 묶어서 필수적으로 요구했기 때문에 틀리다고 나온 것을 확인할 수 있었다. 판단: 1 정답지 수정 수정 후에는 질문의 의도에 맞춰 공고에 실제로 명시된 주요 업무를 정확하게 설명했는지 를 중심으로 평가한다. A14. 재택·유연근무 되는 곳 있어? A14는 정답지보다는 채점 과정에서 문제가 발생한 경우 였다. 챗봇은 재택·유연근무가 가능한 6곳을 찾아냈지만, LLM 채점 과정에서 특정 공고를 경력 공고라고 잘못 판단하면서 오답으로 처리된 문제가 있었다. 즉, 답변이 틀린 것이 아니라 채점 과정에서 잘못 판단한 경우 였다. 판단: 1 정답지/채점 기준 수정 실제 공고 원문을 기준으로 판단하고, 채점자가 근거 없이 공고의 경력 여부를 추측하지 않도록 채점 기준을 수정했다. A16. 신입 백엔드 공고에서 공통으로 요구하는 게 뭐야? A16은 가장 복잡한 문항이었다. 모델은 여러 공고를 검색했지만 일부 공고만 중심적으로 비교하는 문제가 있었다. 반면 기존 정답지는 특정 공고들을 묶어 공통 요구사항을 분석하고, 다른 공고들은 성격이 다르기 때문에 제외하는 과정을 요구하고 있었다. 따라서 다음 두 가지를 나누어 볼 필요가 있었다. 정말 모델이 여러 공고를 제대로 종합하지 못한 것인가? 아니면 정답지가 질문보다 지나치게 복잡한 분석을 요구하고 있는가? 판단: 3 애매한 경우 질문과 원문을 기준으로 정답지가 요구하는 범위가 적절한지 먼저 검토하기로 했다. 3. 정답지 수정 후 기존 실험 전체 재채점 정답지가 수정되었다면 최근 실험만 다시 채점해서는 안 된다. 예를 들어 A04의 정답 기준이 변경되었다면, 기준선 ↓ 공고 전체 투입 ↓ 기술 동의어 ↓ 조건 코드 필터 ↓ 분류-본문 차이 ↓ 지시문 개선 ↓ 공고별 균등 검색 ↓ 최종 설정 각 실험 결과를 새로운 정답지 기준으로 다시 채점해야 한다. 그래야 정답지 변경으로 인한 점수 변화와 실제 모델 개선으로 인한 점수 변화를 구분할 수 있다. 4. 답변 정확도 v2 → v4 기존 채점 기준을 v2, 수정된 채점 기준을 v4로 두고 기존 실험 결과를 다시 채점했다. 실행 v2 v4 변화 0 기준선 (A) 64.0% 70.0% +6%p 0 기준선 (B) 66.0% 72.0% +6%p 1 공고 전체 투입 70.0% 78.0% +8%p 2 기술 동의어 76.0% 76.0% 0%p 3 조건 코드 필터 82.0% 86.0% +4%p 4-v1 분류-본문 82.0% 88.0% +6%p 4-v2 82.0% 88.0% +6%p 5-a 지시문 v2 88.0% 92.0% +4%p 5-b 80.0% 92.0% +12%p 6-a 공고별 균등 검색 84.0% 96.0% +12%p 6-b 86.0% 90.0% +4%p top_k=10 순수 검색 a 78.0% 82.0% +4%p top_k=10 순수 검색 b 68.0% 76.0% +8%p top_k=10 최종 a 82.0% 90.0% +8%p top_k=10 최종 b 86.0% 96.0% +10%p 이번 결과에서 가장 중요한 점은 위의 변화가 코드 수정으로 인한 성능 향상이 아니라는 것이다. 동일한 기존 실험 결과를 대상으로 수정된 정답지 기준으로 다시 채점했기 때문에 발생한 변화이다. 따라서 예를 들어 최종 설정이 86%에서 96%가 되었다고 해서 "모델 성능이 10%p 향상되었다." 라고 해석하면 안 된다. 이번 변화는 평가 기준을 바로잡은 결과이다. 5. 최종 설정의 재채점 결과 최종 설정의 경우 다음과 같이 변경되었다. 실행 기존 v2 수정 v4 6-a 공고별 균등 검색 84% 96% 6-b 86% 90% 동일 설정을 2회 실행한 결과의 평균을 비교하면, 기존 v2 평균: 85% 수정된 v4 평균: 93% 이다. 즉, 평가 기준을 수정한 뒤 동일한 결과를 다시 채점했을 때 평균 점수가 8%p 높아졌다. 하지만 이 8%p를 모델의 성능 향상으로 해석하지는 않았다. 이는 정답지 및 채점 기준의 수정으로 인한 평가 결과 변화이기 때문이다. 6. 문서에 없음 정확도 문서에 없음 유형에서도 일부 결과가 달라졌다. 실행 v2 v4 5-a 지시문 v2 80% 100% 6-a 공고별 균등 검색 80% 100% top_k=10 순수 검색 b 100% 80% 나머지 실행은 모두 100% → 100% 로 동일했다. 이번 변화 역시 모델의 성능 변화가 아니라 N01·N02 의 재채점 결과가 달라진 것으로 확인했다. 따라서 이 지표도 정답지 수정에 따른 변화와 실제 모델 개선을 구분해서 해석했다. 7. 정답지 수정과 관계없는 지표 이번 정답지 수정으로 다음 지표에는 변화가 없었다. 최신성 출처 정확도 근거 표시 정확도 검색 적중률 즉, 이번 작업은 검색 시스템 자체를 개선한 실험이 아니었다. 기존 실험 결과를 새로운 정답 기준으로 다시 평가하여 평가셋의 신뢰도를 높이는 과정이었다. 8. 이번 작업에서 알게 된 점 이번 검토를 통해 모든 오답을 단순히 "RAG가 검색을 못했다." 라고 판단하면 안 된다는 것을 확인했다. 평가 결과가 낮은 이유는 크게 다음과 같이 나눌 수 있었다. 평가 결과가 낮음 │ ├── 검색 문제 │ ├── 답변 생성 문제 │ ├── 정답지/채점 기준 문제 │ └── 질문 자체가 애매한 문제 특히 A03, A04, A09처럼 질문보다 정답 기준이 과했던 경우에는 모델을 개선하는 것만으로는 평가의 문제를 해결할 수 없다. 반대로 정답 기준이 적절한 문항에서 답변이 부족하다면 그때는 모델의 검색이나 답변 생성 방식을 개선해야 한다. 9. 이후 평가 기준은 고정 이번 검토를 통해 수정된 정답지를 확정하고, 이후 실험에서는 새로운 정답 기준을 고정해서 사용하기로 했다. 앞으로 성능을 높이기 위해 결과에 맞춰 정답지를 다시 변경하지 않고, 동일한 기준으로 평가한다. 최종 측정은 다음 순서로 진행한다. 평가셋 정답지 검토 ↓ 정답지 수정 ↓ 기존 실험 전체 재채점 ↓ 수정된 결과 확정 ↓ 평가 기준 고정 ↓ 코드 동결 ↓ 새로운 홀드아웃 문항 준비 ↓ 최종 측정 특히 최종 홀드아웃에서는 고치는 동안 한 번도 보지 않은 새로운 문항을 사용하고, 결과가 마음에 들지 않는다고 다시 고치거나 문항을 바꾸지 않고 그대로 실패 사례로 기록할 예정이다. 마무리 이번 작업의 목적은 단순히 정확도 숫자를 높이는 것이 아니었다. 오히려 기존 평가 결과를 다시 살펴보면서, 모델이 틀린 것인지, 정답지가 틀린 것인지, 아니면 질문 자체가 애매한 것인지를 구분하는 것이 목적이었다. 실제로 정답지를 다시 검토한 결과, 일부 문항에서는 질문에서 직접 요구하지 않은 내용을 정답 조건으로 포함하고 있거나 채점 과정에서 잘못 판단하는 문제가 있었다. 따라서 해당 문항을 수정하고 기존 실험 전체를 새로운 기준으로 다시 채점했다. 그 결과 최종 설정의 평균 점수도 기존 85%에서 93% 로 변경되었다. 하지만 이 변화는 모델을 개선해서 얻은 성능 향상이 아니라 평가 기준을 수정한 뒤 동일한 결과를 다시 채점하면서 발생한 변화이다. 이번 과정을 통해 RAG 프로젝트에서는 검색과 생성 모델을 개선하는 것뿐만 아니라, 무엇을 정답으로 볼 것인지에 대한 평가 기준을 제대로 설계하는 것도 중요하다는 것을 확인했다. 이제 수정된 정답지를 고정하고, 이후에는 평가 기준을 변경하지 않은 상태에서 코드 동결 → 홀드아웃 측정 → 최종 성능 평가를 진행할 예정이다.
Балл: 57.02Уверенность: 54%
Балл: 57.01Уверенность: 54%
Балл: 57Уверенность: 54%
Балл: 54.4Уверенность: 49%
Балл: 54.4Уверенность: 49%
Балл: 54.4Уверенность: 49%
Балл: 54.4Уверенность: 49%