我在做什么,以及我能提供什么
一句话我是凌贵黔(Ling Guiqian),后端 / 全栈工程师,目前在一家 AI 金融量化公司做 Go 方向的后端与量化系统。 这个博客前面两年基本只写了 Rust 学习笔记(2023~2024),中间停更了一段时间。现在重新开起来——原因有两个:做过的事值得写下来;以及需要一个地方,让想找人干活的人 30 秒看懂我能做什么。 我平时在做的三件事一、量化交易系统(Go)策略引擎与行情管线:K 线采集与数据校验、价格行为结构识别、回测、以及风控闸门(下单前的人工确认、额度限制、审计留痕)。 数据层最深的体会:**”看起来有数据”和”数据是对的”是两件事**。超短 K 线、丢根、时间边界这些问题,只看”有没有数据”永远发现不了,必须做成断言。 风控层的原则:只拦开仓,不拦平仓。平仓与保护单是逃生通道,任何时候都不能因为”记账失败””风控说不”而被卡住。 二、AI Agent 工程多智能体编排、工具调用与权限控制、本地大模型部署与推理服务。 我更在意的不是”让模型更聪明”,而是让它出错时不至于造成破坏:工具级权限、不可逆操作的二次确认、全量审计。 本地模型部署也踩过...
一个"静默丢弃"的编码坑:脚本明明有输出,系统却什么都没收到
现象一个定时脚本负责把内容推送到聊天工具。运行记录显示任务正常执行,但用户什么都没收到——没有报错,没有告警,日志里只有一行: 1empty stdout — silent run (静默运行:输出为空,按设计什么都不发。) 但脚本手动跑,输出明明白白有几百字。 这类 bug 为什么难查因为它什么都不说。脚本没崩(退出码 0)、没有异常、没有超时,调度器也认为一切正常。整个系统里没有一个环节认为出错——除了最终的用户。 静默失败是最贵的失败:它消耗的不是排查时间,而是信任(”我明明让系统每天发给我”)。 排查:九组对照,把变量一个一个钉死我一开始的思路是错的——去读调度器源码,想搞清它的输出捕获逻辑。读了半天,不如做一组对照实验快。 于是写 9 个最小脚本,唯一变量是”输出内容 + 编码方式”: 变体 脚本做什么 结果 A0 只输出纯 ASCII ✅ 收到(内容正确) C 中文 + reconfigure(utf-8) ⚠️ 收到,但乱码:变体C 变成 鍙樹綋C A1 中文 + reconfigure(utf-8, errors=replace) ❌ 静默 ...
RAG 不靠感觉:20 条评测集、命中率与拒答阈值
为什么要评测搭一个 RAG 检索问答不难,难的是回答这个问题:它到底行不行? 我见过太多”效果还行”的知识库——问几个问题答得不错,就上线了。但真正该问的是: 该答的,答对了吗? 不该答的(知识库里没有的),它有没有硬编? 我调了分块/模型/阈值之后,是变好了还是变坏了? 没有评测集,这三个问题一个都答不了,只能靠感觉。而”感觉变好了”是最贵的错觉。 一、评测集:20 条,其中 5 条必须”答不出来”我用的语料是一份自造的合成手册(6 篇文档),问题分两类: 类型 数量 期望行为 正例(语料里有答案) 15 检索到正确文档并引用 负例(语料里没有答案) 5 明确拒答,不能编 负例才是评测的灵魂。 只测正例的系统,会奖励”什么都敢答”的模型——分数很好看,上线全是幻觉。负例的作用就是把”敢编”的成本显式计进来。 设计负例有个小技巧:用同领域但语料里不存在的问题(比如手册里写了差旅报销,我就问”加班餐补每天多少钱”)。这样模型不能靠”话题不相关”蒙对,必须真的去检索。 二、四个指标1234命中@1 / 命中@3 / 命中@5 正确文档是...
桌面客户端交付实录:Tauri 2 的体积、冷启动与进程管理实测
为什么不用 Electron需求很简单:一个已经在网页上跑得好好的产品,想要一个桌面客户端——能双击打开、能常驻托盘、能自己更新。 第一反应是 Electron,我先把对照实测做了: 对照项 Electron Tauri 2(本项目) 运行时体积 368 MB(实测) 随包不携带浏览器(用系统 WebView2) 首屏时间 3017 ms(实测) 冷启动中位 608 ms 交付包 体积大 7.93 MB(绿色 zip) 差了将近 50 倍。所以我选了 Tauri 2,代价也说清楚:Windows 上依赖 WebView2、Rust 侧调试成本更高、生态比 Electron 小。**这些代价换来的是”用户下载 8 MB 而不是 300 MB”**,对我要交付的场景是划算的。 架构:壳只做两件事最重要的一条设计决定:这个外壳里没有一行业务逻辑。它只做两件事——进程管理和呈现。 1234567891011┌──────────────────────────────────────────┐│ Tauri 2 壳(Rust) ...
给 AI 工具装闸门:权限、人工确认与审计日志怎么落地
先说结论把大模型接到内部系统上,真正危险的不是它答错,而是它做错。问答错了顶多重来一次;工具调用错了——建了单、发了通知、改了数据——是要人去收拾的。 所以我做这类工具时,不把精力放在”让模型更聪明”,而是放在让它出错时不至于造成破坏。落到代码上就是三层闸门: 权限(scope):每个工具声明它需要什么权限,调用方(人/角色)持有什么权限,不匹配就拒绝; 人工确认(confirmation):不可逆操作必须由人点头,而且这个点头不能由模型自己给; 审计(audit):每一次调用都留下可追溯的记录——谁、在什么角色下、调了什么、结果如何。 下面用一个最小的 MCP 风格服务(stdio + JSON-RPC,零依赖)说明怎么落。 一、校验与执行必须分离工具调用只有两个结果:过闸门,或者被拦下。我的写法是先校验、后执行,校验层完全不碰业务: 123456789def call_tool(self, name, args, confirmed: bool): tool = TOOLS.get(name) if tool is None: ...
Rust学习第十六天
Rust学习第十六天Rust 异步编程 async/await在现代编程中,异步编程变得越来越重要,因为它允许程序在等待 I/O 操作(如文件读写、网络通信等)时不被阻塞,从而提高性能和响应性。 异步编程是一种在 Rust 中处理非阻塞操作的方式,允许程序在执行长时间的 I/O 操作时不被阻塞,而是在等待的同时可以执行其他任务。 Rust 提供了多种工具和库来实现异步编程,包括 async 和 await 关键字、futures 和异步运行时(如 tokio、async-std 等),以及其他辅助工具。 Future:Future 是 Rust 中表示异步操作的抽象。它是一个可能还没有完成的计算,将来某个时刻会返回一个值或一个错误。 async/await:async 关键字用于定义一个异步函数,它返回一个 Future。await 关键字用于暂停当前 Future 的执行,直到它完成。 实例 12345678910111213141516171819202122232425262728// 引入所需的依赖库use tokio;us...
Rust学习第十五天
Rust学习第十五天Rust智能指针智能指针(Smart pointers)是一种在 Rust 中常见的数据结构,它们提供了额外的功能和安全性保证,以帮助管理内存和数据。 在 Rust 中,智能指针是一种封装了对动态分配内存的所有权和生命周期管理的数据类型。 智能指针通常封装了一个原始指针,并提供了一些额外的功能,比如引用计数、所有权转移、生命周期管理等。 在 Rust 中,标准库提供了几种常见的智能指针类型,例如 Box、Rc、Arc 和 RefCell。 本文主要介绍声明式宏。 智能指针的使用场景: 当需要在堆上分配内存时,使用 Box<T>。 当需要多处共享所有权时,使用 Rc<T> 或 Arc<T>。 当需要内部可变性时,使用 RefCell<T>。 当需要线程安全的共享所有权时,使用 Arc<T>。 当需要互斥访问数据时,使用 Mutex<T>。 当需要读取-写入访问数据时,使用 RwLock<T>。 当需要解决循环引用问题时,使用 Weak<T>。 Box 智能指针Bo...
Rust学习第十四天
Rust学习第十四天Rust宏Rust 宏(Macros)是一种在编译时生成代码的强大工具,它允许你在编写代码时创建自定义语法扩展。 宏(Macro)是一种在代码中进行元编程(Metaprogramming)的技术,它允许在编译时生成代码,宏可以帮助简化代码,提高代码的可读性和可维护性,同时允许开发者在编译时执行一些代码生成的操作。 宏在 Rust 中有两种类型:声明式宏(Declarative Macros)和过程宏(Procedural Macros)。 本文主要介绍声明式宏。 宏的定义在 Rust 中,使用 macro_rules! 关键字来定义声明式宏。 1234567macro_rules! my_macro { // 模式匹配和展开 ($arg:expr) => { // 生成的代码 // 使用 $arg 来代替匹配到的表达式 };} 声明式宏使用 macro_rules! 关键字进行定义,它们被称为 “macro_rules” 宏。这种宏的定义是基于模式匹配的,可以匹配代码...
Rust学习第十三天
Rust学习第十三天Rust 并发编程安全高效的处理并发是 Rust 诞生的目的之一,主要解决的是服务器高负载承受能力。 并发(concurrent)的概念是指程序不同的部分独立执行,这与并行(parallel)的概念容易混淆,并行强调的是”同时执行”。 并发往往会造成并行。 本章讲述与并发相关的编程概念和细节。 线程线程(thread)是一个程序中独立运行的一个部分。 线程不同于进程(process)的地方是线程是程序以内的概念,程序往往是在一个进程中执行的。 在有操作系统的环境中进程往往被交替地调度得以执行,线程则在进程以内由程序进行调度。 由于线程并发很有可能出现并行的情况,所以在并行中可能遇到的死锁、延宕错误常出现于含有并发机制的程序。 为了解决这些问题,很多其它语言(如 Java、C#)采用特殊的运行时(runtime)软件来协调资源,但这样无疑极大地降低了程序的执行效率。 C/C++ 语言在操作系统的最底层也支持多线程,且语言本身以及其编译器不具备侦察和避免并行错误的能力,这对于开发者来说压力很大,开发者需要花费大量的精力避免发生错误。 Rust 不依靠运行...
Rust学习第十二天
Rust学习第十二天面向对象面向对象的编程语言通常实现了数据的封装与继承并能基于数据调用方法。 Rust 不是面向对象的编程语言,但这些功能都得以实现。 封装封装就是对外显示的策略,在 Rust 中可以通过模块的机制来实现最外层的封装,并且每一个 Rust 文件都可以看作一个模块,模块内的元素可以通过 pub 关键字对外明示。这一点在”组织管理”章节详细叙述过。 “类”往往是面向对象的编程语言中常用到的概念。”类”封装的是数据,是对同一类数据实体以及其处理方法的抽象。在 Rust 中,我们可以使用结构体或枚举类来实现类的功能: 1234567891011121314151617181920pub struct ClassName { pub field: Type,}pub impl ClassName { fn some_method(&self) { // 方法函数体 }}pub enum EnumName { A, B,}pub impl En...