Today I Learned

A collection of things I learn every day - Page 2

nixos 更新 flake

flakes 的设计初衷是“重现性”(Reproducibility),而不是“实时性”。

获取最新的分支构建信息

# 只更新 nixpkgs 这个 input
nix flake update nixpkgs

# 或者更新所有 inputs
nix flake update

注意,建议更新所有 inputs,不然可能会出现 nixpkgs 和 home-manager 版本不一致的问题

nixos

nixos 清理旧版本

# 删除所有 7 天前的旧世代(Generations)
sudo nix-collect-garbage --delete-older-than 7d
nixos

nixos install clash

在 nixos 配置中添加:

  # Setup Clash
  programs.clash-verge = {
    enable = true;
    serviceMode = true;
    tunMode = true;
  };

开启了 tunMode 和 serviceMode

nixosclashgfw

cronjob 中的环境变量问题

当我在 cronjob 中设置:LOGSEQ_FOLDER=$HOME/logseq 时 (这个语法会被 cronjob 设置为环境变量),在后续的命令调用 LOGSEQ_FOLDER 这个变量的时候,并不能正确的获取到 $HOME 变量。但是我们可以尝试在 SHELL 中运行命令export LOGSEQ_FOLDER=$HOME/logseq,然后从环境变量中查找 env|grep LOGSEQ_FOLDER 得到结果是:/home/alex/logseq, 为什么?

因为 cronjob 中不会对环境变量的值 $HOME 二次展开,但是 shell 中,是直接展开了的!

  • cronjob 中,LOGSEQ_FOLDER=$HOME/logseq 表示的是一个键值对,原样放入子进程的模块的环境变量中。
  • shell 中,LOGSEQ_FOLDER=$HOME/logseq 表示的是一个表达式,会先计算,再展开,所以你 export 后的环境变量就已经是展开过后的了。
cronjobenvshell

windows 手机连接区域限制

在 windows 下的手机连接有时候会报错:"Microsoft 手机连接 - 无法连接到你的 Android 设备,因为你正在尝试访问中国以外的应用程序,目前我们不支持漫游区域。"

只需要添加对应的 "dcg.microsoft.com" 为直连即可。

windowsvpnsurge

判断 macos 安装的软件是否是 mac app store 下载的

Bitwarden 为例:

确定软件已安装

ls -la /Applications/ | grep -i bitwarden

使用 SPCTL 查看该应用的信息

spctl -a -t exec -vvv /Applications/Bitwarden.app

输出如下:

/Applications/Bitwarden.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: 8bit Solutions LLC (LTZ2PFU5D6)

如果 source 是 Mac App Store, 那就是通过 app store 下载的,反之如果 source 是 Notarized Developer ID, 那就是通过 DMG 下载的。

这个命令的详细解析

这个命令是 macOS 系统中用于 Gatekeeper(门禁)安全机制的 spctl 工具的一个用法。具体解释如下:

spctl -a -t exec -vvv /Applications/Bitwarden.app

各部分含义:

  • spctl
    是 macOS 自带的命令行工具,全称是 System Policy Control,用于管理系统策略,尤其是 Gatekeeper 对应用程序的验证和授权。

  • -a(或 --assess
    表示“评估”(assess)指定的项目,即检查该应用是否被系统策略允许运行。

  • -t exec(或 --type execute
    指定评估类型为“可执行”(executable),即检查该应用是否可以被当作可执行程序运行。这是针对应用程序(.app)的标准类型。

  • -vvv
    表示输出详细(verbose)信息,v 越多,输出越详细。-vvv 是非常详细的输出,会显示签名信息、证书链、权限来源等。

  • /Applications/Bitwarden.app
    要评估的目标应用程序路径,这里是安装在应用程序文件夹中的 Bitwarden 密码管理器。


这个命令的作用:

检查 Bitwarden.app 是否通过了 macOS 的 Gatekeeper 验证,是否被允许运行。

运行后,你会看到类似这样的输出(取决于实际情况):

  • 如果应用已正确签名且被信任:
    /Applications/Bitwarden.app: accepted
    source=Apple Notarized Developer ID
    origin=Developer ID Application: Bitwarden Inc. (XXXXX)
    
macos

nixos 修改 capslock 为长按 ctrl 短按 esc

/etc/nixos/configuration 中这样配置即可:

services.interception-tools =
  let
    itools = pkgs.interception-tools;
    itools-caps = pkgs.interception-tools-plugins.caps2esc;
  in
  {
    enable = true;
    plugins = [ itools-caps ];
    # requires explicit paths: https://github.com/NixOS/nixpkgs/issues/126681
    udevmonConfig = pkgs.lib.mkDefault ''
      - JOB: "${itools}/bin/intercept -g $DEVNODE | ${itools-caps}/bin/caps2esc -m 1 | ${itools}/bin/uinput -d $DEVNODE"
        DEVICE:
          EVENTS:
            EV_KEY: [KEY_CAPSLOCK, KEY_ESC]
    '';
  };

参考连接:https://discourse.nixos.org/t/best-way-to-remap-caps-lock-to-esc-with-wayland/39707/6

nixoscapslock

logseq block 确保有 uuid

logseq 中 api 返回的 uuid 在真实的 block 中未必会有,需要检查是否有 id 这个属性才能够正确获取到:

    def ensure_uuid_property(self, blocks: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        updated_blocks = []
        for block in blocks:
            if 'properties' not in block or 'id' not in block['properties']:
                # Logseq block UUID is typically in block['uuid']
                block_uuid = block.get('uuid')
                if block_uuid:
                    print(f"Ensuring uuid property for block: {block_uuid}")
                    self.upsert_block_property(block_uuid, "id", block_uuid)
                    # Update the block in memory for consistency
                    if 'properties' not in block:
                        block['properties'] = {}
                    block['properties']['id'] = block_uuid
            updated_blocks.append(block)
        return updated_blocks

参考:https://github.com/vipzhicheng/logseq-plugin-comment-block/blob/8da0b723c2ec4660d2136a6aed213aa022d03113/src/main.ts#L69-L71

logsequuid

LangChain 中 qwen embeddings 使用 openai embeddings 兼容性问题

使用本地 embddings 遇到如下问题:

qwen3_embdings = OpenAIEmbeddings(
    model="text-embedding-qwen3-embedding-0.6b",
    base_url="http://127.0.0.1:1234/v1",
)

运行时报错:

Traceback (most recent call last):
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/main.py", line 20, in <module>
    _ = vector_store.add_documents(documents=all_splits)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/langchain_core/vectorstores/in_memory.py", line 195, in add_documents
    vectors = self.embedding.embed_documents(texts)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/langchain_openai/embeddings/base.py", line 590, in embed_documents
    return self._get_len_safe_embeddings(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/langchain_openai/embeddings/base.py", line 480, in _get_len_safe_embeddings
    response = self.client.create(
               ^^^^^^^^^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/openai/resources/embeddings.py", line 132, in create
    return self._post(
           ^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1259, in post
    return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/zhaochunqi/ghq/github.com/zhaochunqi/ai-agents-learning/.venv/lib/python3.12/site-packages/openai/_base_client.py", line 1047, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': "'input' field must be a string or an array of strings"}

添加 check_embedding_ctx_length 参数即可

qwen3_embdings = OpenAIEmbeddings(
    model="text-embedding-qwen3-embedding-0.6b",
    base_url="http://127.0.0.1:1234/v1",
    check_embedding_ctx_length=False,  # 关键:禁用长度检查以兼容 LM Studio
)

原因是这个参数默认 True, LangChain 会检查输入文本是否超过模型的最大上下文长度,如果文本太长,会被分割成多个 chunk,这样向 api 发送的是 token 数组,不是原始的字符串。

具体的执行流程差异:

默认情况 (True) - 会出错的流程:

# 输入:"Hello world"
# 1. tiktoken 处理:[15496, 1917] (token IDs)
# 2. API 请求:{"input": [15496, 1917], "model": "..."}
# 3. LM Studio 收到整数数组,报错:"'input' field must be a string"

设为 False 后 - 正常的流程:

# 输入:"Hello world" 
# 1. 直接发送:{"input": "Hello world", "model": "..."}
# 2. LM Studio 收到字符串,正常处理
langchainembeddings