2026-03-20 11:03:33
基于 Github Copilot SDK 的免费模型,做了一个中文翻译英文的本地服务(参考前文:博客新功能,中文版自动翻译为英文,基于 Github Copilot SDK 的免费 gpt 5 mini 模型)。但是在处理长文本时,总是不能返回完整的翻译结果,并且报错。
在处理长文本时,Github Copilot SDK 可能会报错:
waiting for session.idle: context deadline exceeded
并不是不处理,而是返回一定长度的内容后,就没有继续返回了。这种情况通常出现在需要返回较长文本的场景,例如翻译长文、生成长文等。
我不确定是返回的 token 长度有限制,还是请求的总时长有限制,或者是其他原因导致的。在官方的 github issue 里也没有找到相关的讨论。
为了绕过这个问题,我尝试将 Markdown 长文本分割成小段内容,逐个发送给 Github Copilot SDK 处理。这样每次处理的文本长度就不会太长,应该能够避免报错。
至少目前在翻译短标题的场景下,从来没有遇到过这个问题。
因为需要翻译的文本都是我自己写的博客,没有太复杂的结构,就是正常的 markdown 格式。所以,我按照 markdown 的二级标题来进行分割。每当遇到一个二级标题,就认为是一个新的段落,进行一次翻译请求。最后再把翻译结果拼接起来,形成完整的翻译文本。测试了一下,效果不错:
Connected to server. Waiting for tasks...
Received task for Article ID: 1661
Translating Title...
Translating Content in segments...
Translating Content Fragment 1/3...
Translating Content Fragment 2/3...
Translating Content Fragment 3/3...
Result sent successfully.
目前的基于二级标题的方式确实能解决我自己的问题,但是如果是一个结构更复杂,段落更长的 Markdown 文本,这种方式可能就不太适用了。比如说,如果一个段落下面还有三级标题,或者有很多的列表、代码块等,那么按照二级标题切割可能就不够细粒度了。
找到一个 golang 的 langchaingo 三方库,提供了文本切割的功能,可以按照 markdown 的结构进行切割
https://pkg.go.dev/github.com/tmc/langchaingo/textsplitter
langchaingo 是什么?
Building applications with LLMs through composability, with Go! This is the Go language implementation of LangChain.
为了我这么个简单的需求引入 langchaingo,感觉有点臃肿了,暂时先不考虑了。
2026-03-19 19:39:55
本来以为开发一套人事管理系统两个月就足够了。没想要真正用起来,天天一堆的问题需要优化。改造起来也异常的费劲,主要是每次修改都需要兼容历史数据,心惊胆战的。好在今天遇到的问题,不是大规模修改组织架构这样的变态需求,而是一个相对独立的功能需求,改起来比较简单。
目前的人事管理系统,包含一个入职员工自己填写个人基本资料的功能,以及 HR 编辑员工信息的功能。
出现了一个严重的 BUG,复现流程:
这时,HR 提交的内容会覆盖掉员工提交的内容,导致员工填写的个人基本资料丢失了 😓 估计入职的大兄弟已经泪流满面,还得重新填写一遍几十个字段的个人信息。。。
其实最好是可以同时编辑,这样比较节省时间,员工在填写个人资料的时候,HR 可以同步编辑岗位信息。
这个可以实现,但是得新增一个功能,就是增加一个独立的岗位信息编辑功能,或者叫非基本信息编辑功能(可能有更好的名字,再想想)。
点击这个按钮之后,可以编辑员工信息中除了基本信息的部分,即,跟员工端编辑的内容完全隔离开。这样就能同时操作了。
如果要保证用户体验,还是要加上一个状态提醒,即,员工在扫二维码编辑基本信息时,在 HR 端提示只能使用岗位信息编辑功能,不能使用原有的全量信息编辑功能。
我还写了一个演示 Demo 页面,来展示这个功能的实现细节,并且把 js 代码也放在了前端的页面里,方便前端同事了解如何实现。

在进入员工端编辑页面后,浏览器端的 js 每 30 秒钟向服务器发送一次 POST 请求,表示正在编辑基本信息。
后端会提供一个接口,接收员工的编辑状态,并把这个状态存储在内存中(或者 Redis 中)。这个状态可以设置一个过期时间,例如 60 秒,如果超过 60 秒没有收到更新,就认为员工已经不在编辑了。前端调用接口时,提供员工共享编辑接口使用的那个 token,后端会从 token 中解析出员工 ID,来标识哪个员工正在编辑。
通过 SSE(Server-Sent Events)接收服务器端推送来的员工编辑状态。
浏览器对同一个域名下的 SSE 连接数有限制(通常是 6 个),所以采用全局的方式来管理 SSE 连接,避免每个员工都建立一个 SSE 连接导致资源浪费。
服务器端会返回一个正在编辑个人资料的员工 ID 列表。
HR 端在收到这个列表后,更新界面上对应员工的编辑状态提示。包括:
SSE 的优点:
用 golang 来实现这个需求的服务端异常简单,既可以通过共享内存来存储员工的编辑状态,也可以方便的调用 Gin 框架的 SSE 功能来推送消息给前端。
如果用 Python 来搞,估计服务器部署就得折腾半天。
这里不使用 Redis,而直接使用 golang 的 go-cache 来存储员工的编辑状态。因为这个状态不需要持久化,也不需要跨服务器共享,所以 go-cache 就足够了。
import (
"time"
"github.com/patrickmn/go-cache"
)
// 创建一个缓存,默认 5 分钟过期,每 10 分钟清理一次过期项
var EditLock = cache.New(5*time.Minute, 10*time.Minute)
// 设置编辑锁
func LockUser(userID string) {
// 设置该用户正在编辑,有效期 60 秒
EditLock.Set(userID, true, 60*time.Second)
}
// 检查是否在编辑
func IsUserLocked(userID string) bool {
_, found := EditLock.Get(userID)
return found
}
// 返回正在编辑的用户列表
func GetLockedUsers() []string {
items := EditLock.Items()
lockedUsers := make([]string, 0, len(items))
for userID := range items {
lockedUsers = append(lockedUsers, userID)
}
return lockedUsers
}
golang Gin 端 SSE 的实现示例:
func StreamHandler(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "text/event-stream")
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
// 模拟持续推送,每 2 秒钟检查一次编辑状态并推送给前端
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// 检查内存/Redis 中的编辑状态
isEditing := CheckStatus(c.Query("id"))
c.SSEvent("message", map[string]interface{}{
"is_editing": isEditing,
})
c.Writer.Flush() // 必须手动 Flush 才能实时发送
case <-c.Request.Context().Done():
// HR 关闭了页面,后端停止推送
return
}
}
}
如果 Golang Gin 后端部署在 Nginx 反向代理后面,需要确保 Nginx 配置支持 SSE 的长连接。可以在 Nginx 配置中添加以下内容, 否则会出现线上接口收不到任何推送消息的情况:
# 专门为 SSE 接口做的特殊配置
location /api/v1/employee/status-stream {
proxy_pass http://golang_backend;
# 核心:必须关闭缓冲,否则消息会被攒在 Nginx 缓存里不发给前端
proxy_buffering off;
proxy_cache off;
# 核心:设置长连接超时(根据业务需求,比如 1 小时或 24 小时)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# 核心:处理 HTTP/1.1 协议及 Connection 头部
# Nginx 默认会把 Connection 改为 close,这会导致 SSE 无法持续
proxy_http_version 1.1;
proxy_set_header Connection "";
# 修正:告诉浏览器不要缓存这个流
proxy_set_header Cache-Control "no-cache";
# 实时转发,不缓冲
chunked_transfer_encoding off;
}
我这是第一次使用 SSE 来给前端推送消息,之前都是用 WebSocket 的。
我感觉 SSE 用起来非常方便,省去了很多 WebSocket 需要处理的细节,尤其是自动重连和协议升级方面。对于这种单向推送的场景,SSE 真的是非常合适的选择。
怪不得现在大模型的网页端,都是用 SSE 来实时推送 AI 生成的内容的,完美契合了单向通信的需求。
2026-03-18 22:09:46
我感觉中文博客已经没啥希望了,看的人寥寥无几,来自搜索引擎的流量也日渐衰落。而一些编程相关的内容,来自海外的反而多一些,即便是中文版。于是,我就想能否自动把中文的博客翻译为英文。这样也许受众能更大一些。
之前尝试过 免费 AI 大模型 API 接口,Gemini 3 Flash 预览版的 Golang 代理实现 。里面即便是小模型,效果也不错,但是国内服务器访问 Google 是个大麻烦,虽然我通过德国的代理是可以正常访问的,但是,犹豫再三我还是放弃了这个方案。一是因为,Gemini 相关的模型月配额太少,二是不想维护另一套代理服务。
参考前文:无限免费大模型 token, Github Copilot CLI SDK 安装及测试。既然 Github Copilot SDK 提供了免费的大模型 gpt 5 mini 和 gpt 4.1,这两个做英文翻译绰绰有余。
我不确定在服务器上部署一套 Copilot CLI 会不会导致封号,因为如果同时多台不同 IP 的机器上使用同一个 copilot pro 账号,总感觉是有风险的。于是,我采用了一个类似 OpenClaw 的方案:
效果很不错。效果参考这篇翻译:Setting up OpenClaw and Integrating GitHub Copilot for Unlimited Free Model Tokens
使用 gpt 5 mini 和 gpt 4.1 都可以。但是千万不要使用 gpt 4o,因为实际测试发现 gpt 4o 是占用 Premium requests 额度的。而且每次消耗 1x 。。。比 gemini 3 flash 都贵,服气了。但是,VSCode 里的 copilot 模型列表显示,这个 gpt 4o 是免费的。我说今天消耗了 N 多配额,吓哭。
我在 github issue 里找到一个类似的问题:
https://github.com/github/copilot-sdk/issues/334
Some models are missing from CopilotClient.list_models. The issue indicates that several models (Gemini 3 Flash, Groke Code Fast 1, GPT-4o, and Raptor mini) are not appearing in CopilotClient.list_models()
虽然没说占用 Premium requests 的问题,但是感觉 copilot SDK 有点混乱。
2026-03-18 19:03:23
我发现 Bing Webmaster Tool 中新增了一个 AI Performance 功能。可以显示你的网站内容被 AI 大模型推荐了多少次。但是这个页面没有中文翻译,我有点看不太懂。不了解指标 Total Citations 和 Avg. Cited Pages 分别是什么意思😅。。。

我对这两个名词感兴趣是因为,我发现 Clarity 最近也要出类似 AI 统计功能。现在 GEO (Generative Engine Optimization,生成式引擎优化)这么火,还是有必要了解一下。也方便做量化的统计,毕竟现在烟台本地也出现了专门做 GEO 的公司,我们公司就购买了这个服务,还挺贵,且没有量化指标。如果能记录 Bing Webmaster 这个数据监控来判断效果,就不至于被骗。
Microsoft Copilots 及其合作服务中的统计数据。虽然国内没法用 Windows 系统的 copilot,但是 bing 搜索很多时候在搜索结果的顶部会给出 AI 建议。估计也涵盖了这部分的展示数据。
此外,要了解这两个指标是什么,首先要知道 citation 这个单词的含义。citation 这个单词我还是第一次见,citation [saɪ’teɪʃnz] 的中文意思是“引用”。需要注意,引文(citation)和参考文献(Reference)的区别:引文(在正文中)对应参考文献(通常放在正文末尾,比如,在参考书目)。
即,总引用次数。指在选定的时间范围内,你的网站内容在 AI 生成的回答中被作为“来源(Source)”或“参考文献”显示的累计总次数。
即,平均被引用页面数。指在选定周期内,平均每天有多少个不重复的网页(Unique URLs)被 AI 引用。
这是一个覆盖面指标。它能帮你判断 AI 是只盯着你家的一两个“爆款”页面薅羊毛,还是觉得你整个网站的很多内容都很有价值。
总引用次数关注的是量级;平均被引用页面数关注的是多样性。
即,基准查询/支撑查询。在曲线图的底部,还会看到一个 Grounding Queries 的数据列表。里面是一堆关键词及对应的引用次数。
这些关键词并不是用户输入的原始问题,而是 AI 为了生成答案,去搜索引擎里进行“背景调查”时使用的关键词。
2026-03-18 18:34:15
总感觉我使用的 Github Copilot CLI 体验不太好,远不如 OpenCode 舒服。今天在测试使用 CLI 翻译一个文本文件的内容时,处理不但很慢,而且结果也非常不理想(翻译格式有问题,调用本地 PowerShell 命令也要想半天),甚至远远不如在线版本的 DeepSeek。我怀疑是我本地的 Copilot CLI 版本过低的原因。所以想升级一下到最新版本,看看有没有什么改善。
> copilot.exe version
GitHub Copilot CLI 0.0.418
Update available: 1.0.7
Run 'copilot update' to update, or download from: https://github.com/github/copilot-cli/releases/tag/v1.0.7
可以看到,我当前安装的版本是:0.0.418,而最新版本是: 1.0.7。感觉我的版本过旧了。
> copilot.exe update
Checking for updates...
这个提示有误导性,虽然显示是 “Checking for updates…”。但实际上已经开始在后台下载了,从 Windows 系统资源管理器就能看到 copilot 进程已经开始从 github cdn 服务器下载更新了。但是国内下载速度很慢,只有 10k 左右的下载速度。。。耐心等待吧。
升级完成会看到版本号就变成最新的了:
> copilot.exe update
Checking for updates...
Copilot CLI version 1.0.7 downloaded.
> copilot.exe version
GitHub Copilot CLI 1.0.7
You are running the latest version.
2026-03-15 11:14:52
手里一堆信用卡,招行的/中信的/农行的,现在除了农行有点活动,其他的啥优惠也没有。尤其是招行,要不是服务器续费和 github 续费需要用美金支付,招行我也想注销了。手机 APP 一个比一个臃肿,还占手机空间。
这些银行的 app 客户端越做越花哨,但是一点优惠也没,积分少,也没啥用,还不如直接用支付宝和微信。
注销的主要原因是,卡太多,每个月还款的时间也不一样,非常分散精力。虽然可以调整还款日期,我还是觉得麻烦。干脆不用了。
人工也各种套路,非得说 5 天后有人要联系我确认,我说不要确认了,立即注销。果然可以,真无语。都 2026 年了,注销还不能在手机上自助操作,甚至官方客户端里搜索“销卡”没有任何搜索结果,渣渣。
浪费 20 分钟。以后还是控制手里卡的数量。