MoreRSS

site iconKai Xia | 夏恺修改

工程师在澳大利亚墨尔本。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Kai Xia | 夏恺的 RSS 预览

Pensieve: 2604

2026-04-26 21:35:00

所读所观所玩

上个月读书严重过量, 这个月基本没读书. 微信阅读才记录了一共两个半小时的阅读时间. 另外的, 剧也没怎么看, 所以这个部分真是乏善可陈. 倒是这个月买了Switch 2, 捡起暗黑三几天玩到了120级大秘境. 然后重开新档玩王国之泪, 现在解了一百多个神庙, 地下的地图全开, 希望这次可以通关.

另外, 近月底时买了一台佳能的相机, R8, 裸机1800澳币. 送的定焦50毫米的镜头还得等佳能发货, 于是在本地一个相机租赁的铺子租了一个24-105的变焦头拍了拍照过瘾. 我在这儿还闹了一个笑话, 我只是想着应该去租25-105的头, 结果租到的是一个24-105mm f/4L IS USM的定光圈镜头, 不是我希望的24-105mm f4.5-6.3 STM的镜头, 这个USM的镜头重得让我怀疑他们给错了镜头. 自己回头又搜索了下才发现是自己弄错了. 周五下午拿到, 周一归还, 三天一共六十澳币, 真不算太贵.

不要当这个日本人

我自己重新把有声书捡起来听. 这个月重听完了. 里面有个故事触动到了我. 费曼刚去巴西时, 在一个餐馆遇到一个卖算盘维生的日本人. 这个人为了推销算盘, 来跟他比算术. 费曼加减法不如他厉害, 乘除法就和他有来有往了. 这个时候这个日本人向他挑战开立方根. 选的数字是1729.03.别闹了费曼先生>

费曼这次狠狠地羞辱了他, 因为这个数字和12的立方(1728)非常接近, 于是费曼很容易就用近似计算的办法算出了很多位小数, 而这个日本人还在努力的计算12.0.

回头复盘的时候, 费曼发现这个人根本不懂数学, 甚至很难说他懂算术, 他只会使用算盘来做计算, 不提微积分, 我猜连指数对数都很少接触. 这样的人和费曼比赛, 一定是会被, 用一个很俗的词, 降维打击的. 因为在这个场景下, 两个人的思维模式根本就不在一个层面上, 一个懂微积分, 会背一些对数表, 数字们都是他的朋友; 而另一个真的只是算盘的操作员或者说奴隶而已.

这个故事在今天尤其有教育意义. 在AI大行其道的现在, 如果仍然抱着旧方式旧思路, 想要守成, 不去努力求新求变, 那么大概率守不了成.

小说修改

这个月还花了不少时间在修改忘川这部小说上. 小时候听说, 小说都是改出来的. 当时年少, 听听就算, 最多知道修改的重要性. 这次自己从头到尾自己完成, 然后数易其稿, 才知道这句话实在是真知灼见. 当时在AI的帮助下写小说很是春风得意马蹄疾, 等到了修改的时候才是愁苦到拈断数茎须. 好在Claude在能提供一些蹩脚的修改意见的同时还能提供大量的情绪价值, 让这个过程没有太过愁苦. 我随手改了几个助词都会被它称为是绝妙的修改. 要是自己没有自省之心, 我一准认为自己是啥啥啥在世了. 现在这个版本改过了, 尤其是第二章到第五章改得很细, 希望提供比较好的阅读体验.

除了在我和Claude讨论的时候经常觉得应该写一个界面来方便对文章的修改外, 我还有了下面这样一些收获:

  • 尽量不要串章节修改, 你必须假设AI知道故事梗概, 但是绝对不要期待它能够完全理解上一章和本章之间的联系.
  • 虽然小说修改时的上下文使用远比编程要少, 但是仍然要注意上下文的使用. 不是从计费角度考虑, 而是因为回复质量和上下文窗口内的文本息息相关. 相信我, 虽然这一行里面有不是… 而是… 它真的仍是我手打的产物.
  • 使用Claude Code来编辑小说也完全没问题. Claude Code不是代码编辑工具, 而是AI工具. 我甚至相信, 如果我们能把钢琴谱无损地转成纯文本, 那么Claude Code可以拿来作曲
  • 我自己是在和Claude不断地讨论小说, 但是所有的修改(除了部分比较枯燥的标点修改)仍然是我在另外一个nvim窗口中一点一点打出来的. 原因很简单, 这样才能产生灵感.

我另外已经完成了一个短一点的故事, 蔡州, 需要进一步修改. 下个月有空时再看吧.

跟着AI学编译原理

2026-04-04 04:07:00

有了AI之后, 学东西变得更容易了. 趁着复活节假期, 把之前接触过但是了解不深入的编译原理捡起来学习一下.

LLVM IR

首先我读了网上找的两篇关于编译原理入门的文章, 一篇是讲汇编的, 一篇是讲LLVM IR的. 我推荐读者自己去阅读一下这两篇, 内容都还挺不错(虽然对于这种内容, 怎么写也都算是re-hash而已). 如果不想读的话, LLVM IR是对于汇编的一层抽象, 是LLVM内部用来连接正常源代码和汇编之间的一个桥梁. 对于我这个toy project, 跟着LLVM IR就行了, 让实现一个编程语言变得特别简单(尤其是有AI帮忙).

Design

我无意从头创建一个可以日常用的编程语言, 只是想探索一下这个过程中的技术栈而已, 所以我的提示词类似:

如果从头来设计一个编程语言, 要求有rust/c/go那样的性能, 而且适宜让AI agent来编写, 这个编程语言应该如何设计? 更进一步的, 如果只是想实现一个PoC, 需要做哪些工作呢?

其后, Claude给我了一些选项, 我按自己的喜好/直觉做了一些选择(或者说是瞎选). 再往后Claude就开始干活了. 我还让Claude做了一下性能的比较和优化, 最后结果是这个PoC的语言性能和Golang/C相当, 如果C代码没有认真优化(比如使用arena allocator), 那么这个PoC的性能甚至更好一些. 整个代码实现是用golang写的, runtime使用的是C. 在这个时代, 这已经属于技术细节了.

{🏠? ~/.xia/sha/git/reg/benchmark}time ./alloc3_regi

real	0m0.345s
user	0m0.284s
sys	0m0.032s
{🏠? ~/.xia/sha/git/reg/benchmark}time ./alloc3_c

real	0m1.091s
user	0m1.037s
sys	0m0.031s
{🏠? ~/.xia/sha/git/reg/benchmark}time ./alloc3_go

real	0m1.142s
user	0m1.914s
sys	0m0.091s

成品语法是这样的:

type Point struct {
    x: f64
    y: f64
}

fn distance(a: &Point, b: &Point) -> f64 {
    let dx = a.x - b.x
    let dy = a.y - b.y
    return sqrt(dx * dx + dy * dy)
}

fn midpoint(a: &Point, b: &Point) -> &Point {
    return new Point {
        x: (a.x + b.x) / 2.0,
        y: (a.y + b.y) / 2.0
    }
}

fn scale(p: &mut Point, factor: f64) {
    p.x = p.x * factor
    p.y = p.y * factor
}

fn main() {
    let p1 = new Point { x: 0.0, y: 0.0 }
    let p2 = new Point { x: 3.0, y: 4.0 }

    print(distance(p1, p2))  // 5.0

    let mid = midpoint(p1, p2)
    print(mid.x)  // 1.5
    print(mid.y)  // 2.0
}

编译和使用类似:

{🏠? ~/.xia/sha/git/regi-lang}./regic examples/point.regi
{🏠? ~/.xia/sha/git/regi-lang}./point
5
1.5
2

需要注意的是, 这个编程语言和其他很多语言最大的区别在于内存的管理. 这个PoC编程语言是根据变量作用域来管理内存的. 也就是说, 每个变量都有一个明确的作用域(region), 当这个作用域结束的时候, 变量占用的内存就会被自动回收掉. 这就避免了GC的开销, 同时也避免了手动管理内存的麻烦. 但是这也带来了一些限制, 比如不能有全局变量, 不能有闭包等等. 不过对于一个PoC来说, 这些限制是可以接受的.

代码流程分析

regic的main.go里面, 核心逻辑是这样的:

p := parser.New(filename, string(src))   // 词法分析 + 语法分析 → AST
program := p.Parse()

typeChecker := checker.New()    //  类型推断和检查
typeChecker.Check(program)

regionChecker := checker.NewRegionChecker(typeChecker)
regionChecker.Check(program)    // 逃逸分析,确保引用不逃出 region

gen := codegen.New(typeChecker, targetTriple)
gen.Generate(program)
llvmIR := gen.Module().String() // AST → LLVM IR
cmd := exec.Command("clang", "-O2", "-Wno-override-module", "-o", *output, tmpFile.Name(), runtimePath, "-lm")  // 调用clang来编译.

下面分着来看看前三步的具体逻辑, 最后生成LLVM IR的步骤比较机械, 而调用clang来编译的这一步就更不用看了.

词法分析

词法分析的核心逻辑是这样的:

func (p *Parser) Parse() *ast.Program {
    program := &ast.Program{}

    for !p.curIs(token.EOF) {
        decl := p.parseDecl()
        if decl != nil {
            program.Decls = append(program.Decls, decl)
        }
        p.nextToken()
    }

    return program
}

这儿的parseDecl会看当前的token是fn(函数)还是struct. 否则就直接报错, 这是因为在语言设计上, 我为了简化内存模型, 要求所有的变量都有一个明确的作用域(region):

func (p *Parser) parseDecl() ast.Decl {
    switch p.cur.Type {
    case token.Fn:
        return p.parseFnDecl()
    case token.TypeKw:
        return p.parseTypeDecl()
    default:
        p.error(p.cur.Pos, fmt.Sprintf("expected declaration, got %s", p.cur.Type))
        return nil
    }
}

类型检查

类型检查的核心逻辑看了以后就觉得平平无奇了:

func (c *Checker) Check(program *ast.Program) {
    // First pass: collect all type and function declarations
    for _, decl := range program.Decls {
        switch d := decl.(type) {
        case *ast.TypeDecl:
            c.collectTypeDecl(d)
        case *ast.FnDecl:
            c.collectFnDecl(d)
        }
    }

    // Second pass: check function bodies
    for _, decl := range program.Decls {
        if fn, ok := decl.(*ast.FnDecl); ok {
            c.checkFnDecl(fn)
        }
    }
}

就是把所有的Declaration(函数和struct定义)读到一个结构里面去, 然后挨个去检查函数的参数/返回值的类型是否正确:

func (c *Checker) checkExpr(expr ast.Expr) types.Type {
    switch e := expr.(type) {
    case *ast.Ident:
        return c.checkIdent(e)
    case *ast.IntLit:
        return types.Typ_I64
    case *ast.BinaryExpr:
        return c.checkBinaryExpr(e)
    case *ast.CallExpr:
        return c.checkCallExpr(e)
    // ...
    default:
        return types.Typ_Invalid
    }
}

这儿, 有些能从ast拿到结果的就直接给了类型, 有些是到一个专门的函数里去做更细致的检查/推断, 比如, 我们在checkCallExpr里面, 先是对一些runtime层面实现的函数(print/sqrt)做类型检查, 然后再去检查用户提交的代码里定义的函数:

func (c *Checker) checkCallExpr(expr *ast.CallExpr) types.Type {
    name := expr.Func.Name

    // Built-in functions
    switch name {
    case "print":
        for _, arg := range expr.Args {
            c.checkExpr(arg)
        }
        return types.Typ_Void
    case "sqrt":
        // sqrt(f64) returns f64
        if len(expr.Args) != 1 {
            c.errorf(expr.Pos(), "E0003", "sqrt expects 1 argument, got %d", len(expr.Args))
        } else {
           argType := c.checkExpr(expr.Args[0])
           if argType != types.Typ_F64 {
               c.errorf(expr.Args[0].Pos(), "E0003", "sqrt expects f64, got %s", argType)
           }
        }
        return types.Typ_F64
    // ...
    }

    fn := c.funcs[name]
    if fn == nil {
        c.errorf(expr.Pos(), "E0004", "undefined function: %s", name)
        return types.Typ_Invalid
    }

    if len(expr.Args) != len(fn.Params) {
        c.errorf(expr.Pos(), "E0003", "%s expects %d arguments, got %d", name, len(fn.Params), len(expr.Args))
        return types.Typ_Invalid
    }

    for i, arg := range expr.Args {
        argType := c.checkExpr(arg)
        paramType := fn.Params[i].Type

        if !c.assignable(paramType, argType) {
            c.errorf(arg.Pos(), "E0003", "cannot pass %s as %s", argType, paramType)
        }
    }

    if fn.Result == nil {
        return types.Typ_Void
    }
    return fn.Result

逃逸分析和region

这部分就不结合实现的代码来解释了, 因为不太具有代表性. 毕竟, 这个PoC的内存管理模式比较特殊. 结合实例解释一下这个PoC语言的特性吧:

fn main() {
    region {                    // depth 1
        let a = new Data {}     // a 属于 depth 1
        region {                // depth 2
            let b = new Data {} // b 属于 depth 2
            a = b               // 错误!depth 2 → depth 1
        }
    }
}

上面这段代码中, a和b的类型相同, 但是在depth 2中, 我们没有办法把depth 2的数据赋值给depth 1的变量. 因为这儿的深度就是生命周期, 深度越高的生命周期越短. depth 2的东西在depth 2结束的时候就被回收掉了, 所以没办法返回给depth 1. 对于这种场景, 我们要么通过一个函数来把数据返回出去, 要么把a和b的定义放到同一层. 在更深的depth/region里面, 我们仅仅是使用而不用返回.

Learnings

在玩这个项目的过程中, 我也的确学到了编程语言里的各种特性都应该是在哪一层实现: 哪些东西是由runtime来提供的, 哪些东西是在CodeGen层面解决的, 而又有哪些是在库层面解决的. 比如, 在runtime层面, 我们会去:

  • 做系统API调用, 例如随机数, 系统时间, 文件io, 网络io/socket, 进程线程, 信号处理等等.
  • C-API调用比如类型转换等等.
  • 自己在runtime这一层管理的逻辑, 比如基于region的内存管理的实现等等.

在CodeGen层面, 一个编程语言要实现:

  • 控制流if/for等
  • 运算符加减乘除等
  • 结构体
  • 函数调用
  • 对于我们这个PoC的region, high level的region调度也是在语言/CodeGen层面实现的.

在库层面, 我们会需要提供一些函数供用户调用, 例如数学函数(sqrt/sin/cos等等), 字符串处理函数, 数据结构相关的函数(比如list/map/set等等), 以及一些系统调用的封装(比如文件io/网络io等等). 这些函数的实现可以是用C写的, 也可以是用我们这个编程语言写的. 往往, 我们需要将系统的API包装成更易于使用的函数. 不过对于我们这个PoC, 我们实际上没有实现这个特性.

Pensieve: 2603

2026-03-30 20:29:00

所读所观

这个月读完的书有四本了. 网络小说两本, 一本是学霸的星辰大海, 这本的内容比较一般, 仍只是有了系统就可以为所欲为的套路. 这类小说的卖点是多巴胺分泌很足, 最大问题是缺乏真正的戏剧冲突. 这本就是典型的例子, 到了后面就不知道怎么往下编了, 只好拿不可言说凑数. 另外一本亏成首富从游戏开始稍好些, 有一个必须亏钱的理由却不断赚钱, 顺便还嘲讽了一群不好好埋头做事却只想着挖空心思捞钱的行业. 但是这本实在是有点太长了, 我到了后面真实一目十行, 就这样也花了84个小时才读完. 这无疑是这过去几年里最花时间的一本了. 总体质量上来说, 这本硬伤也实在太多, 本来几十万字能写完的剧情撑到了500万字. 第三本是本月最佳, 台湾往事. 没想到这本书里有这么多重量级人物, 从侧面记录了很多台湾的史实, 读起来兴致盎然. 最后一本是What if的作者写的How to: 如何不切实际地解决实际问题, 这本我总觉得没之前的what if好, 因为每个不同的topic长短不一, 有些深入讨论, 有些相对比较浅, 或者画一张漫画就过了. 另一个可能我觉得一般的原因是这些讨论都有点太贴近实际, 印象最深的一个场景是一本正经地在房屋外面围一圈熔岩, 但是这远不如之前讨论里让人去摸中子星那么夸张.

这个月蒙同事推荐看完了Netflix上的Arcane, 讲LOL的背景故事. 对于一个从来没玩过这个游戏的人而言, 这个剧没有吸引到我. 最大的感触是, 怎么死一个人那么难? 到了后面很多双主角年幼时已经死透了的人争先恐后地复活, 让人目不暇接, 这个观感并不好. 看完这个, 捡起几年前半途而废的Dota: 龙之血, 这次算是看完了, 但是看完后感觉还是晕晕乎乎. 虽然里面的很多人物我是知道的, 但是他们之前的关系, 以及和这个游戏本身的关系, 我还是看不太明白. 当然, MOBA这种游戏也许不需要背景, 因为真的没人在乎.

Claude Code的网页版

这个月vibe coded了一个Claude Code的网页版. 一开始就是让Claude Code自己做一个PoC, 只是能够有一个页面, 里面没有任何前端框架, 纯js/css的页面, 被内嵌到一个go binary里面去, 页面通过websocket和后端的go服务器交互, 控制一个或者多个Claude Code进程. 控制Claude Code的方式是起进程的时候拿到前端状态里面的模型, repo和mode, 启动的时候使用--input-format stream-json来做所有的交互. 实际上, claude agent sdk里面也是这样和Claude Code交互的(ref). 后面, 页面上的内容越来越多, 于是只好转头转成了react, 当然, 还好不是我自己维护.

项目效果挺不错, 我肝了几天后觉得有点累, 特性也做得七七八八了, 就歇着了, 脑子里还没特别想好后面要如何继续演进. 除了可以控制多个tab, 在多个worktree里面打开各种不同的repository, 通过icon显示是否需要人工干预等基本特性外, 我还加上了和Github/Jira的连接, 这样我可以直接把分配给我自己的card里的context读到, 然后找到对应的repo, 让claude开始干活了. 另外, 我还加上了一个pipeline的特性, idea是我们可以将一些常见的操作封装到一个工作流程里面去(比如下面讲的PR审核).

AI时代该如何审核PR?

现在, 人们的创造力在AI的辅助下已经得到了极大的提升, 但是这也带来了一系列的问题, 比如机器生成的PR应该怎么样由人来review. 我们应该如何将碳基生命有限的注意力放在最需要关注的文件上?

我现在的想法是:

  1. 我们必须要依赖魔法才能打败魔法, 就好比只有一个作为机器的Christopher才能打败Engima一样. 妄图用人去实现这个目的是不现实的.
  2. 我们必须还是需要人在流程中起决策作用. 代码审核不是非黑即白, 往往我们需要有所取有所舍. 这种时候, 人的判断要比机器的判断更可靠.
  3. 单元测试必须要有, 这是我们能保证系统能正常工作的原因, 但是单元测试肯定不需要人来写. 机器在审核PR的时候, 必须要仔细推敲每个测试是否能覆盖核心逻辑, 并正反两个方面看看能正常工作.
  4. 我们需要机器预审核来判断每个PR的风险, 如果是比较小的PR, 实在没必要花人类宝贵的注意力来审核了. 而如果是修改十几个文件的PR, 我们需要在流程上由PR初审一遍, 找到这个PR最关键的几个文件, 然后交由人来审核和判断, 是否应该被合并.
  5. 说到底, 对于没什么风险的PR, 不需要人来审核, 而对于有风险的PR, 人类需要: a. 阅读AI初审得出的关键文件列表, 厘清核心逻辑. b. 人来判断这些核心逻辑对应的测试是否完备, 如果不完备, 就需要返工.

Context

2026-03-01 07:35:00

This is my very personal, very meta take on AI tooling — not a how-to, just a lens: treat an LLM like an improv comedian. The model is the performer; the context is whatever the performer has to work with — the audience suggestions, the rules of the game, and everything said so far. Change that input, and you change the performance. It’s a simple framing, but it matters, because most of the “new” tooling we talk about later is really just different ways of shaping context.

If we go back a bit — when ChatGPT first went mainstream — prompt engineering became a buzzword. The idea was that the right wording could coax noticeably better output. That hype has cooled, especially as a standalone job title, but not because the practice vanished. My take is that, back then, the comedian simply wasn’t that strong yet, so you had to feed it very specific suggestions to get a good bit. Today, the comedian is better, and the craft has shifted: less about finding magic phrases, more about shaping context and building workflows. In that sense, the kaleidoscopic tools we have today are still extending prompt engineering — they’re just turning it into scaffolding, templates, and context choreography.

Fast forward to today, we live in an age of abundance in AI models. There are a lot of capable performers, and within the top tier the difference is often subtle — and highly task-dependent. So the leverage moves to the room you put them in: the better you can shape the context, the better outcomes you tend to get. Give a decent comedian great suggestions and a clear setup, and they can outperform a better comedian stuck with a confused, noisy room. Context really matters.

How do we control context? Tools can help, but most of them are generic — they don’t really know what you consider signal versus noise, so they can’t safely curate the room for you. For example, Claude Code can compact the conversation to make more room for what comes next. But if you toss in an unrelated question halfway through, or dump a giant chunk of raw logs into the chat, the performer is now doing improv in a noisy room: it becomes harder to track what matters, what’s incidental, and what should be ignored.

At this point, you might think we’re powerless. We’re not. A whole set of inventions exists for one purpose: keep the context window aligned with the problem we’re actually trying to solve.

Let’s start with MCP. To me, it’s a great invention not because it’s yet another integration, but because it changes how much integration knowledge you have to carry in the context window. MCP gives the model a small, discoverable interface to external systems — tools, resources, and even reusable prompts — via clear schemas and structured inputs/outputs. In practice, an MCP server often fronts one or more APIs, but it doesn’t have to expose the entire surface area. It can present a curated menu of capabilities with sane defaults. The LLM sees a menu of dishes, not a book full of recipes: instead of pasting curl/auth/swagger docs into the chat, you give it a handful of typed tool calls. That keeps the context lean and the model focused on the task.

Next, I’d like to talk about subagents. In my opinion, this is another great invention: instead of stuffing everything into one ever-growing chat, you delegate a narrowly scoped task to a separate agent running in its own context window (often with its own prompt and tool permissions), then bring the result back to the main thread. The goal isn’t “zero context” so much as “clean context”: keep the orchestrator’s context focused on the plan and the decisions, not the churn. Debugging is a classic example. Logs are high-volume and usually low-signal, and they can quickly drown out what you actually care about. A subagent can wade through the noise in isolation and can be instructed/designed to return a short, decision-ready summary so the rest of the workflow doesn’t get contaminated.

I don’t see skills as a breakthrough. To me, it’s prompt engineering with better ergonomics: modular, shareable, and loaded on demand. That’s a real UX win. But context-wise, it’s an incremental improvement, not a new paradigm. You’re not eliminating instruction tokens — you’re moving them out of the chat transcript and pulling them in when relevant. Once loaded, those tokens still compete with conversation history and everything else. If anything, a large catalog of Skills can create a new kind of noise: routing and discoverability overhead.

While I know Geoffrey Huntley personally, Ralph Loop still doesn’t feel like a new kind of context management to me. It’s a solid reliability pattern: keep the long-term state outside the conversation, restart with a clean context each round, do one small unit of work, validate it, then loop. That can be very effective precisely because it avoids context rot. But it’s not the “art” I’m seeking. It’s less about shaping the room and more about clearing the room over and over — paying iteration cost for robustness.

Gas Town adds real value in coordination and persistence — it stores work state outside the chat in git-backed hooks/ledgers — but at the end of the day it still inherits the strengths and limits of the underlying agent runtime (e.g., Claude Code or Codex). So if you only care about token-level context shaping, it may feel like ‘nothing new’; if you care about multi-agent reliability, it’s a different story.

So if we zoom out, a lot of AI tooling innovation is really context management innovation — not making the comedian magically smarter, but managing what the comedian gets to hear, when, and at what bandwidth. Different tools optimize different bottlenecks — token budget, noise isolation, state persistence, and coordination. The “art”, for me, is knowing which bottleneck you’re hitting and picking the smallest intervention that keeps the room coherent.

Pensieve: 2602

2026-02-23 09:58:00

所读所观

这个月读完的书只有一本, 总共读书的时间也只有12个小时. 都算是很长时间以来的新低了. 读完的是一本无需动脑的网络小说成何体统, 主要也是看着新上的网剧/动漫在那儿挤牙膏, 于是直接找来书把小说读完了. 这本刚开始读的时候还好, 后面就开始拖沓和不堪了. 读到搞定太后之后都很难再读下去.

这个月看的剧会多一点, 首先是某天晚上无聊躺在沙发上看完了刚上的驯龙高手真人版, 不算什么上好的片, 不过诚意还算足, 合家欢可以一看. 另一个是爱情要怎么翻译, 这部肥皂剧很甜, 底子还是很正常的韩剧, 大概可以一看. 再后面是The Playbook: A coach’s rules for life, 这个是本月最佳, 让我对coach这个角色有了更多的理解. 能够从各种不同的方面让人信任, 给予反馈, 给予激励, 能够让人更好, 才是一个coach该做的事情. I’m not gonna coach you to who you are, I’m gonna coach you to who you should be someday. 另外还在Netflix上看了唐宫奇案, 这个片子的质量就太烂了, 各种硬伤层出不穷, 剧情和台词也幼稚得让人实在不好意思读下去, 所以后面就弃剧了.

这个月还看了一部非Netflix上的电影, The Life of Walter Mitty, 这个是真不错, 白日梦想虽然能够让人沉迷, 但是如果在真实世界不能像主角最后这样更主动地去拥抱这个世界, 你只会被这个世界遗忘. 另外, 如前所述, 也看了成何体统的剧集版和动画版的第二部, 仍然没看完, 一天一两集挤牙膏实在是让我不能接受, 而且老实说就这个剧本和演出, 不值得我那么长的注意力.

OpenClaw, PicoClaw和Oracle Cloud

OpenClaw大火我是实在没看太明白. 当然, 单纯从技术角度来说, 整个世界都会很无聊: 手机不过是一台微型电脑, iPad只不过是一台微型笔记本. 但是, 很多东西实际上是touch point的改变导致了质变. 所以, 要理解, 还得真正去touch一下, 然后发挥一下自己的想象力, 看看有什么是之前做不到而现在可以做到的了. 当然, 你让我心甘情愿的去安装一个TS写的放权给AI的软件, 我是一定不愿意的. 于是找来了PicoClaw. 在我一台美国的VPS上跑通了后, 又想着去拿一个Oracle Cloud的虚拟机来专门跑这个玩意.

然后就要开始连绵不绝地对Oracle吐槽了. 云服务我用过的也有不少了, 有像AWS这样挑不出大毛病的, 也有GCP这种用起来云里雾里的, 还有想IBM当年的Bluemix完全是半成品的. 但是像Oracle这样的厂商真是第一次见. 注册账号就花了两三天, 每次注册都报信用卡信息有问题, 但是我这个卡在全世界都买过服务了, 凭什么他们家有问题? 而且, 我每次重来换一个信用卡, 就开始报错说我尝试次数太多. 但是我一个人科人属的个体, 真的不是一个电子生命啊, 我就是之前试了一次第二次就不让了, 突然死亡是吧? 好容易回头记起来要做这个事情, 换了两次卡, 终于通过了注册这一关, 然后发现这个新账户创建后需要人工审核的, 于是我无语到家了. 接下来, 几天后, 这个账户终于姗姗来迟, 我开始创建机器. 然后这儿就开始好玩了. 我本来想着找个类似Cloudformation, Deployment manager这样的东西, 这样可以反复重试, 结果发现Oracle没有做自己的control plane, 而是让用户自己跑terraform, 挺古朴的. 接下来我就开始不断遇到Out of capacity的问题了, 其他资源都创建成功了, 关键要创建虚拟机的时候报这个错. 我甚至还写了一个脚本, 反复重试了一晚上, 但还是没能成功启动服务器. 我觉得像猴一样被耍了, 就去查了一下, 貌似必须要升级到Pay-As-You-Go账号才行. 好吧, 这算是游戏策划逼氪对吧? 升级就升级吧, 升级又需要人肉审核. 还好这次的审核没开账号那么久, 几个小时就搞定了, 回来直接跑terraform, 终于就可以连接到远程服服务器了.

PicoClaw开始跑了, 也连到了Telegram, 具体要怎么用, 还没想好, 找找有没有好玩的idea先.

用AI写小说

拖延了这么久, 终于写完了第一部中篇小说: 忘川, 欢迎捧场. 这本书是和Claude Opus 4.6一起完成的. 创作的出发点是某次我和deepseek聊天, 让它想想小小说的梗概. deepseek出了不少梗概, 其中我最喜欢一个保镖的故事, 不过它的背景是在现代, 我就想着放到古代去会怎么样. 以此, 我就有了一个这样的设定:

江湖人好排座次, 虽大多无缘得见真容, 但四绝之名无人不晓: 未尝一败的”凝岳”李慢, 身为第一美人, 腊月便将出阁的”无花”颜寂, 主动放弃高官厚禄加入忘川, 卦算无双的“司辰”陆承影, 一叹定生死的”金针”钟无鬼. 四位的传奇在坊间传了又传, 故事都已褪成了神话. 普天之下, 知晓内情者不过十指之数: 这四位绝顶人物, 皆听调于天下第一秘会, 忘川. 他们或曾并肩退敌, 或曾隔空交手. 偶遇江湖, 不过颔首互道”久仰”. 只是他们自己不知, 当这些偶遇与声名被细细织成江湖这张大网时, 布线之人, 正坐在忘川最深处的阴影里, 侧首垂目静听.

这个故事的背景是在宋朝, 我很为这个背景着迷, 还花了不少时间去研究北宋洛阳城里皇城有哪几个门. 但是, 显然, 如果简单就是这样一个组织, 就太假太烂了: 有这样一个五人组, 整个世界的平衡性就会被摧毁. 我写到这儿停了很久, 想着该怎么演进这个世界. 后来仍是和deepseek聊天, 我问它有没有什么其他的可能性, 讨论过程中, 我问他有没有什么宋朝背景的夺嫡的故事, 它告诉我的第一个故事就是赵竑的故事.

于是, 这几个人就从之前高高的架子上走下来了, 他们不再是那些神仙般的人物, 而变成了有血有肉有悲有喜的人. 李慢不再需要是天下第一, 颜寂不需要那么美, 陆承影不需要有那么深厚的背景, 钟医官也不需要那么神秘了(名字和性别也被改了, 我脑子里本来是类似平一指的角色). 而且, 我在读这段历史的时候, 也发现这个故事挺传奇的. 一场大雨, 真的就改变了一个王朝的命运. 另外, 我也不需要改太多史实就能让这个故事站住. 很幸运, 史弥远似乎当年也真的给赵竑身边安插了一个弹琴的女卧底. 我真正改过的有两处, 一处是让历史上寿终正寝的余天锡早死了几年, 因为当颜寂站在掌柜面前的时候, 我实在是没办法不让她开这个口. 另一处, 史书上说去赐死赵竑的门客叫秦天锡, 我囫囵地将这两个天锡合成一个了.

Pensieve: 2601

2026-01-26 12:06:00

所读所观所玩

这个月读的书稍多一点点, 首先是一人公司做私域, 这本名义上是书, 实际上是引流广告而已, 没什么读的必要. 然后是一个欧亨利的短篇小说集, 这本和我高中时读过的另外一本没什么大的区别, 印象深刻的是之前那个选本没收入的公主与美洲狮, 以及他未完成的手稿, 梦. 接下来是普通人如何做小红书, 这本比我想象中要实在, 介绍了很多心得体会, 是我的本月最佳. 最后是昨天花了三个小时一口气读完的太白金星有点烦, 这本数次逗得我大笑, 也数次鄙视微信读书里那些感觉没读过原著的人没理解亲王的梗. 但是这本最大的硬伤是国内的体制实在是一种很奇葩的存在, 因此这些政治笑话不能说有太多的普适性, 水平上比是大臣/是首相这种还是要稍差一点点.

游戏来说, 玩通了逃离鸭科夫的第一个结局(也是难度最低的一个结局), 农场镇的boss都打过至少一遍了, 实验室转了几圈后懒得往里面走了. 半路劝退我的大概是这样几件事情:

  • 疗养院门口的鬼魂一定要特定的武器才能打
  • 很多成就需要要特殊的武器击杀特定boss, 我没觉得有挑战只是觉得烦. 而且我用magic mouse能够玩到这个程度我很满意了.
  • 实验室的难度并没有太高, 让我没有特别的成就感.

Netflix里, 这个月看完的两个片子都告诉我不要害怕失败. 一个是100 meters, 里面的人物也会有挣扎, 也会有崩溃的场景, 但是无论如何, 这些人还是能够爬起来, 继续往前走. 这部的缺点是主线不够明确, 看得出来想要走多主角并行的故事, 但是在这个电影里非主角的背景交待得不够多, 所以有时候会有喧宾夺主的感觉. 第二个是昨天直播的Alex Honnold直播徒手爬台北101的Skyscraper Live, 真令人羡慕有这样的心理承受力. 另外, 无聊放松的时候随便从我的电影库里面找了两个老片看, 一个是加勒比海盗5, 视觉效果片. 另一个是更老的新龙门客栈, 这个电影豆瓣有8.7的高分, 其中9分都是张曼玉的, 其他人是在拖后腿.

另外, 这个月还看了Youtube上柴静对严歌苓的采访, 谈芳华和文革. 严的状态挺好, 敢说真话, 敢说直话, 不藏不掖. 另外, 里面有句话让我印象很深刻: “文学都是萌发于记忆的不可靠性”.

Working with AI

下面记录一些我最近是如何和AI协作的:

  • 首先是自己大概半天时间vibe coding出来的一个网站, WishMart, 使用场景是能够在团队建设中, 更了解每个人想要什么.
  • 另外, 自己在拿Deepseek写小说, 不在于AI能够很好地写这个小说, 因为我之前的经历告诉我, AI写出来的小说还是不太能看, 但是至少能够在遣词造句以及历史背景上给你一些帮助. 比如, 如果写一个小说, 那么AI可以告诉你, 也会颔首互道一声"久仰"静静听着都有些现代汉语的表达习惯, 在古代白话中是没有的. 这样, 至少可以避免你写中国古代背景的武侠小说里, 出现焦距这样让人出戏的词语.
  • 再次, 我在用chatGPT提示自己的摄影水平. 很多时候我知道自己没拍好照片, 但是为什么没拍好, 应该如何提高, 需要一个人来告诉我. 之前这种需要你报名摄影课程, 或者有身边玩摄影的人来告诉你. 有了ChatGPT后, 这个事情也就是一个上传照片的事儿了.
  • 最后, 我最近也玩了一下Claude Code里面的Skill, 感觉算是一个低成本共享prompt/tooling的工具. 好玩的approach, 而且成本比写MCP还要低.

在和AI合作了这么长时间后, 我也有了一些自己的心得: 在这个和AI共存的时代, 高等级的鉴赏力是最稀缺的资源. 比如AI写出来的小说梗概是不是合理, 是不是有一个好的内核, 是不是值得继续衍生发展, 都需要一定的文学鉴赏力. 而选定方向后, AI写出来的内容, 节奏感, 音韵感, 都大概率是不达标的, 都是需要人来读, 来朗读, 来指导AI提高的. LLM的机制决定了AI学习到的内容的水准不会高, 所以更需要人在其中来充当阀门, 掌控内容的输出. 又比如摄影, AI固然能够保证我的拍摄能够有一个底线, 不会差到哪儿去, 但是对于如何拍出好的照片, 仍然是需要大量的联系和大量的阅读, 真正提高自己的鉴赏水平. 换句话说, 对于掺杂了主管成分的艺术创作, 不管是写作绘画还是摄影, 甚至编程也可以算得上: AI能帮你入门, 但是至少目前没办法帮你精通.