分类目录归档:编程

用AI写小说的尝试

前情提要我的 AI Agent 实验项目 Sequoia

小说完成情况

  1. 已完成 10 万字左右,总共24章。
  2. 小说名字: 《他们都劝我冷静,然后我疯了》 发布在番茄小说。通过 签约认证 ,未通过 作品推荐 审核。
  3. 由于让 AI 写小说的主要目的是验证 AI 能力,近期应当不会继续更新。

小说创作系统搭建

  1. 使用 LangChain Deep Agents 作为 Agent 开发框架。
  2. 小说创作知识通过 SKILL 提供。
  3. 通过 tools 为平台提供图数据库读写等额外的能力(未实际使用)。

遇到的问题以及解决方案

根据我的最初预想,小说的摘要及人物小传等知识使用层次化的 Markdown 来存储,方便人工审阅。人物关系等信息通过图数据库存储,并通过向量数据库提供前期剧情的搜索。实际使用过程中发现,图数据库和向量数据库的使用必须给定详细的规范,如果只提供基础的读写接口,AI 无法很好的利用数据库能力来辅助写作,引入的问题远高于解决的问题。此外良好的 Markdown 结构设计已足以支持小说写作。

当使用一个工具时,该工具的所有知识需要加入到对话的上下文中。如果 Agent 用到的工具很多,且这些工具都有详细的使用知识,则会占用很多宝贵的上下窗口。为了解决该问题可以将这个工具作为独立的 SubAgent,将工具使用的详细说明放到 SubAgent 中。

Deep Agents 默认提供了文件读写工具,通常情况使用该工具实现文件读写即可。但在明确要求加载 outline 目录内设有知识的情况下依旧会根据自己的判断只加载部分文件。似乎在上下文过长时 AI 的遵从性会下降(注: AI 所使用的 Transformer 本就是 注意力机制 ,AI 和人一样,内容多了就容易丢失重点)。

为了保证摘要的完整加载,同时文件分别加载所带来的 AI 多次决策问题,需要提供一个一次性加载所有摘要知识的接口。尝试在 SKILL 里增加加载摘要的脚本。可能是出于安全等问题的考量, Deep Agents 在执行脚本前会对脚本做非常多的检查,即使在 SKILL 里明确要求直接执行也无法避免。最终将该脚本封装成 LangChain 的工具。

阿里百练平台为每个模型提供了 100 万免费 token。使用过程中发现只进行了几轮对话一百万 token 就会耗尽。后切换到 DeepSeek。DeepSeek 应当是主流 API 里最便宜的了,如果缓存命中,每百万的输入0.2元。对于小说写作任务,有着大量的缓存命中。随着章节的增加,写一章小说的 Token 消耗会持续增加。写到20章时,写一遍,再审阅一遍,一章花费1~2元。

我的 AI Agent 实验项目 Sequoia

今年 OpenClaw 的忽然爆火,让我有些难以理解。在我看来 OpenClaw 似乎没有太多实质性的创新。除了不支持 IM 控制外,给任何一个 AI Client 加上 SKILLS 和 MCP 似乎都可以做到类似的事情。在对 OpenClaw 有了些了解后,虽然依旧认为 OpenClaw 不存在太多颠覆性的东西,但要做好并不容易。

OpenClaw 本质是一个带记忆模块的Agent管理工具,并通过 IM 的集成极大增强了用户体验(虽然现阶段依旧是个玩具)。或许 OpenClaw 这样可以通过 “记忆” 持续学习并可以通过 SubAgent 分工合作完成任务的 AI 工具会成为今后一段时间 AI 的发展方向。

为了探索 AI 能力的边界,开了一个试验性项目 Sequoia 。Sequoia(红杉)世界上最大的树,寓意一颗小小的种子终有一天可以长成参天大树。

AI Agent 框架选型

为了方便验证想法,因此希望框架可以帮忙完成MCP/SKILL调用、交互界面、SubAgent创建等大部分常规工作。这样我可以聚焦记忆模块和关键工具模块的设计。同时尝试通过prompt让 AI 自己决定如何来使用这些工具。在研究过主流AI开发框架后最终选定了 LangChain Deep Agents。下面是考虑的一些框架。

Pydantic AI

可以说 Pydantic 是 Python 界数据校验框架的事实标准。在很早之前就简单了解过 Pydantic AI,Pydantic AI 的 API 也算是比较易用。但实际使用过程中发现 Pydantic AI 的 SubAgent 管理功能非常弱,涉及 SubAgent 的操作会很不方便。且 Pydantic AI 没有配套的 UI,想要一个易用的交互 UI 得花不少功夫。

CrewAI

CrewAI 是个多代理编排框架。强项是 SubAgent 的协同管理,可以轻松实现多 Agent 协同。CrewAI 的发展势头不错,且非常易用。不过 CrewAI 的记忆模块和框架深度绑定。由于我想探索如何精确的管理 AI 的记忆,因此放弃。

LangChain Deep Agents

LangChain 是使用最为广泛的 AI 框架,拥有完整的生态。Deep Agents 是 LangChain 团队推出的 Agent 框架,可以完美的融入 LangChain 生态。记忆模块作为独立组件,可以方面用户自行扩展。可以使用 Agent Chat 和自己的 Agent 交互,免去了 UI 的相关工作。

项目现状

目前搭建了基础的项目框架。利用 Deep Agents 框架本身能力提供了 SKILLS支持、SubAgent 管理、本地文件读写等功能。

按照我的预想,记忆应当通过“文本+图数据库+向量数据库”共通管理。为此我自己添加了向量数据库和图数据库的集成。

考虑如果一开始就将应用定位成一个带记忆,可以自主学习的完整“人”实现起来会非常困难。最初会尝试利用这个框架来写小说。

小说生功能设计及问题

小说的写作知识完全通过 SKILL 教给 AI。框架只提供文件读写、数据库读写工具,具体怎么用这些工具完全由 AI 自己决定。

AI 对于长文写作的一大难点是 LLM 的上下文长度限制。为了突破 LLM 的限制,必须将小说大纲、设定、章节摘要等信息分别保存,让 AI 在需要时再自行加载。

尝试用 AI 生成了一篇小说的前两章(使用 qwen3-plus)。似乎 AI 对指令的依从性不是很高。虽明确要求使用图数据库保存人物关系等信息,但在我主动要求前没被触发。明确强调了要读取 outline 目录下所有文件,AI依旧会根据自己的想法只读部分内容。

Token 的消耗速度非常惊人。没跑几次就把 qwen3-plus 赠送的 100 万 token 用完了。随着文章长度的增加,上下文长度会持续增长, token 的消耗量也会快速增加。根据最新情况,写完一章后再手动让 AI 审阅一遍 100万 token 就花光了。

后续应当会一边调整 SKILL 一边不定期更新。

小说链接:《他们都劝我冷静,然后我疯了》

py-cggtts 项目简介

项目地址:https://github.com/vicalloy/py-cggtts/

py-cggtts 是一个为高精度时间传递领域打造的 Python 工具包,它基于 Rust 编写的高性能 CGGTTS 库(GitHub 仓库),提供了对 BIPM CGGTTS 2E 格式文件 的完整解析能力。CGGTTS(Common-View GNSS Time Transfer Standard)是由国际计量局(BIPM)制定的标准格式,广泛应用于全球守时实验室之间通过 GNSS 卫星进行远程原子钟比对。

项目背景

CGGTTS 本身是纯文本结构,解析起来并不困难。不过 Python 性能一般,且已经有了 Rust 的完整实现,于是尝试将 Rust 的 CGGTTS 库导出 Python 接口。目前将 Rust 导入 Python 生态的技术已经成熟,不少 Python 库都通过 Rust 来提升性能。

项目的主要代码都由 AI 完成。对于接口封装这类本身不复杂但又繁琐的工作,在 AI 的加持下变的轻松加愉快了。

使用 Coder 搭建云开发环境相关问题记录

code-server 是开源的在浏览器中运行 VS Code。如果你想搭建一套自己的远程开发环境 code-server 是一个不错的选择。如果只是给自己用 code-server 没什么问题,如果想提供团队使用就得配合 Coder 来做用户和环境等管理。

下面是 Coder 使用过程中遇到问题的一些简单记录。

Docker 镜像拉取问题

在国内拉取 Docker 镜像几乎无法成功,如果使用 Docker 部署首先要修改 Docker 源。

Linux 下 Docker 的配置文件位于 /etc/docker/daemon.json 。修改 Docker 源的方法网上有很多资料,这里不再赘述。

为 Coder 设置代理

Coder 创建 Template 时需要从网络上下载资料,这一步很容易失败。通过环境变量的方式为 Coder 设置代理 HTTP_PROXY 和 HTTPS_PROXY

services:
  coder:
    # This MUST be stable for our documentation and
    # other automations.
    image: ghcr.io/coder/coder:${CODER_VERSION:-latest}
    ports:
      - "7080:7080"
    environment:
      CODER_PG_CONNECTION_URL: "postgresql://${POSTGRES_USER:-username}:${POSTGRES_PASSWORD:-password}@database/${POSTGRES_DB:-coder}?sslmode=disable"
      CODER_HTTP_ADDRESS: "0.0.0.0:7080"
      # You'll need to set CODER_ACCESS_URL to an IP or domain
      # that workspaces can reach. This cannot be localhost
      # or 127.0.0.1 for non-Docker templates!
      # CODER_ACCESS_URL: "${CODER_ACCESS_URL}"
      CODER_ACCESS_URL: "https://coder.your-domain.com"
      HTTP_PROXY: "http://your.proxy.com:1087"
      HTTPS_PROXY: "http://your.proxy.com:1087"
    group_add:
      - "124" # docker group on host `getent group docker | cut -d: -f3`
    user: "1000:1000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./coder_home:/home/coder
    depends_on:
      database:
        condition: service_healthy
  database:
    image: "docker.1ms.run/postgres:16"
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-username} # The PostgreSQL user (useful to connect to the database)
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} # The PostgreSQL password (useful to connect to the database)
      POSTGRES_DB: ${POSTGRES_DB:-coder} # The PostgreSQL default database (automatically created at first launch)
    volumes:
      - ./coder_data:/var/lib/postgresql/data # Use "docker volume rm coder_coder_data" to reset Coder
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "pg_isready -U ${POSTGRES_USER:-username} -d ${POSTGRES_DB:-coder}",
        ]
      interval: 5s
      timeout: 5s
      retries: 5

编辑 Template

在 Template 中设置代理

通过 Template 创建 Workspace 依旧会用到网络,这一步依旧很难成功。在 resource "coder_agent" "main" 中添加 HTTP_PROXYHTTPS_PROXY

Docker in Docker

如果要在 Workspace 中调用 Docker,需要将 docker.sock 映射给 workspace,且需要将传入的路径转换为 HOST 的文件路径。还需要将 HOST 的 docker 用户组添加给 coder 用户。

# ...
resource "coder_agent" "main" {
  # ...
  env = {
    # ...
    HTTP_PROXY          = "http://your-proxy.com:1087"
    HTTPS_PROXY         = "http://your-proxy.com:1087"
  }
  # ...
}

resource "docker_container" "workspace" {
  # ...
  group_add  = ["124"]  # host 的 docker 用户组id
  # unsafe, should https://coder.com/docs/v2/latest/templates/docker-in-workspaces#use-sysbox-in-docker-based-templates
  volumes {
    host_path = "/var/run/docker.sock"
    container_path = "/var/run/docker.sock"
  }
  # ...
}

禁用 autostop

为节约服务器资源,长期不用的 workspace 会被自动销毁,下次使用时再进行重建。如果服务用的人不多,资源足够,建议把 autostop 关闭掉。

选中 template 并进入 template 的 settings 并在 schedule 中将 default autostop 修改为 0。

Apple Guardian(打字练习游戏)加Cursor/Trae使用体验

项目地址:https://vicalloy.github.io/letter-fall/

游戏地址:https://github.com/vicalloy/apple-guardian

小时候练打字的时候玩的最多的是小霸王上的一个空中掉落字母掉打字游戏。
最近看到很多宣传 Cursor 的帖子,另外字节也出了个类似的编辑器 Trae ,于是试着用 AI 编辑器将这个游戏复刻。

项目的大部分代码使用 Cursor 和 Trae 的 Agent 功能自动完成。就初步的体验而言,用自己不熟悉的开发框架写一些小东西 Cursor 这类的 AI 工具确实很好用,少了很多查资料的工作。部分功能反复打回依旧无法正确修改,这时候是需要手动介入。另外两个编辑器的推理速度都不算太理想,特别是 Trae 经常要等挺久。

就个人而言,写玩具 Cursor 和 Trae 很棒。大些的项目还是用 GitHub Copilot 之类的插件更可控一些。

bye 2024

忙碌的 2024,其中几个月的连续加班弄的身心俱疲,好在最终结果还不错,希望在 2025 能有所收获。

今年全马第一次跑进四小时,不过身体上的体验不太好,太累了。以后再跑全马应当真就为了体验氛围了。

今年尝试用 Tauri 写了两个小东西。由于有前端经验,使用 Tauri 实现界面还是比较舒服。Tauri 后端用的 Rust,也算说有实际的写一点 Rust。作为一个现代语言 Rust 有着比较现代化的工具链,使用体验还不错。Tauri 整体开发体验还不错,不过前端作为一个套壳浏览器,一些限制还是挺让人难受。两个项目由于各种原因,到现在都还是半成品。

相比自己用 Tauri 整的两个玩具项目。年末的时候,由于工作中要用到一个开源的 Rust 项目,算是真正有正经的写点 Rust 代码了。由于只是在已有的项目上做一些修修补补,没有用到太多 Rust 的特性。还是希望以后有机会用 Rust 从头构建一个有趣的项目。

Vim下Python(Poetry)环境的设置

近期迁移到了 Neovim 。在 Pyright 的加持下,vim的代码补全和错误校验功能已经很接近 IDE 了。在未正确配置 Python 环境的情况下打开 Python 项目,项目里一堆的告警让人很不舒服。

在 vim 中编辑 Python,重要的是 PYTHONPATH 的设置。正确设置 PYTHONPATH 后,Pyright 可以正确的查找到相关的依赖库。我目前的解决方案是使用 direnv

编辑 direnv 通用配置,添加 poetry 初始化函数。

vim $HOME/.direnvrc
layout_poetry() {
  if [[ ! -f pyproject.toml ]]; then
    log_error 'No pyproject.toml found.  Use `poetry new` or `poetry init` to create one first.'
    exit 2
  fi

  local VENV=$(dirname $(poetry run which python))
  export VIRTUAL_ENV=$(echo "$VENV" | rev | cut -d'/' -f2- | rev)
  export POETRY_ACTIVE=1
  PATH_add "$VENV"
}

在 python 项目中添加 direnv 设置

vim .envrc
# 进入 Poetry 环境
layout_poetry
# 设置 PYTHONPATH
export PYTHONPATH="`pwd`/vendor/Model1:`pwd`/vendor/Model2"

在设置好 direnv 后,在 CLI 下进入目录时将设置好环境变量。CLI 下进入对应的项目目录,然后启动 vim/neovim ,Pyright即可正确的找到相关依赖。

参考:

从Vim切换到Neovim

vim作者的离世,让vim的后续发展存在很大的不确定性。加上现在用的vim配置一直有些小问题,其中一些插件的活跃度不够,bug一直无法修复。

近期花了些时间把neovim配置好,从vim切换到neovim。

配置文件: https://github.com/vicalloy/dotfiles/blob/master/.config/nvim/init.lua

  1. 没有使用别人配置好的环境,完全自行配置。
    • 之前也热衷于使用流行的集成配置。实际下来发现不管多受欢迎的配置都会有些不符合自己喜欢的地方。而且一旦遇到问题,很难定位问题。而且现在的插件管理机制已经很完善了,自行配置的门槛低了很多。
    • 不少优秀的vim插件都转由lua实现,只能在Neovim中使用。
  2. GUI界面用的 Neovide 。用 Rust 实现的 Neovim 前端。

使用Docker在Orange Pi上LLM(使用GPU加速)

最初买 Orange Pi 5 的目的之一就是想跑一些 AI 应用。Orange Pi 5 虽带了 NPU,但这颗 NPU 实在太小众,除了官方的 Demo 就没法轻松把 NPU 用起来。近期看到有人用 RK3588 跑LLM,于是把吃灰已久的 Orange Pi 5 拿出来折腾。

Orange Pi 5 使用的是 RK3588 芯片,该芯片配备的 GPU 是 Mali-G610。在 Orange Pi 5 上跑 LLM 用的就是这颗 GPU 。

基本用法

LLM 模型通过 MLC LLM 项目加载运行。在 Orange Pi 5 上通过 OpenCL 实现 GPU 加速,因此要求系统支持 OpenCL 。Orange Pi 5 的官方 Linux 镜像已添加了 OpenCL 支持,因此不用再额外安装驱动。

如果 Orange Pi 5 上已经安装了 Docker 可以使用下面的命令把服务跑起来。7b-f16 的模型会用到 6.xG 的内存,如果你的系统只有4G内存可以试试 3b-f16 的模型。

# 更多镜像见 https://hub.docker.com/r/vicalloy/mlc-llm-rk3588/tags
docker run --rm -it --privileged \
    vicalloy/mlc-llm-rk3588:FlagAlpha-Llama2-Chinese-7b-Chat-q4f16_1

编译自己的Docker镜像

rock5-toolchain 项目中提供了 MLC LLMDockerfile ,可以通过修改 Dockerfile 里的 ARG MODEL 来打包不同的模型。

为了更方便的打包不同的模型,更为了白嫖 Github Actions 服务器,我参考 rock5-toolchain 项目写了自己的 Dockerfile。相比原始的 Dockerfile,我把TVM编译/G610驱动安装等步骤打包在镜像 vicalloy/mlc-llm-rk3588:base 预置 model 的镜像从该镜像继承。要预置不同的模型,只要将对应的模型复制到镜像就好。

对应项目地址:https://github.com/vicalloy/docker-images/tree/main/mlc-llm-rk3588

参考链接:

Magpie 股价价格提醒工具

项目地址:https://github.com/vicalloy/magpie/

股票工具。设置股票的止损点和营收点,在到达止损点或营收点时发起消息推送。提供一个简易点web服务器用于查看相关股票的当前价格。

注:

用法

编辑规则文件

提醒规则使用 Json 格式进行描述。编辑规则并保存为文件 rule.json 。

[
  {
    "stock_code": "sh000001",
    "stock_name": "上证指数",
    "base_price": 3200,  # 基准价格,用于计算涨幅
    "alarm_price_min": 3100,  # 止损点
    "alarm_price_max": 3400  # 营收点
  },
  {
    "stock_code": "sz000333",
    "stock_name": "美的",
    "base_price": 54,
    "alarm_percentage_min": 0.15,  # 止损点 base_price * (1 - alarm_percentage_min)
    "alarm_percentage_max": 0.15  # 营收点 base_price * (1 + alarm_percentage_max)
  },
]

启动 Web 服务器

docker run --rm \
    -v `pwd`/rules.json:/app/rules.json \
    -p 8000:8000 vicalloy/magpie:latest \
    python -m magpie server -r ./rules.json

在浏览器中访问网址 http://localhost:8000/ 。

检查股价

docker run --rm \
    -v `pwd`/rules.json:/app/rules.json \
    magpie:latest \
    python -m magpie check -r ./rules.json \
    --datasource qq \
    --bark-token $(bark-token) \
    --tg-token $(tg-token) \
    --tg-chat-id $(tg-chat-id)

可以通过设置 crontab 的方式定时执行股价的检查。

备注

10 9,10,11,12,13,14 * * * sudo docker run ....