1. 为什么非要搞一套“中文数据核心”
先说背景。过去大半年,我一直在用一个开源的编程智能体做日常开发。所谓开源编程智能体,就是那种你在 IDE 或终端里喊一句“帮我把这个接口的单元测试补了”,它能自己读项目代码、调工具、改文件的东西。最开始的体验确实惊艳,但用着用着问题就来了:它对英文世界的代码语境非常敏感,一旦切到中文项目、中文技术栈、中文注释和国内常见的工程习惯,表现就明显下降。
这不是说模型“看不懂”中文,而是它缺少一套和中文数据环境对齐的中间层。
举个例子。我之前维护一个带微信支付回调的中小项目,智能体去读代码时,看到wxpay_notify、订单金额、回调验签这些字段和函数名,很容易往英文通用命名规则上猜,导致补出来的代码不是字段名错位,就是把金额单位判断搞反。再比如让它总结一个模块的职责,它给出的结论单独读没问题,但放到中文技术团队的语境里就少了很多“潜规则”:比如团队会把数据库时间字段全部叫gmt_create而不是created_at,会把状态码用字符串包一层而不是整型。
这类信息,模型在预训练时见过不少,但不会针对你的项目、你的团队、你现在仓库外的中文约定做动态校准。所以我花了两周,给手头这个开源编程智能体加了一个外挂性质的“中文数据核心”。它不是简单把模型提示词改成中文,而是真正的数据层改造,把中文世界里那些工具链路径、命名习惯、常见坑、技术文档片段,汇成一个智能体运行时能主动查询、能按需注入的知识底座。
如果你也经常被这类“英文模型 + 中文开发环境”的拧巴感折磨,这篇总结应该能给你省不少试错时间。文章面向的是有一定开发基础、想自己改开源智能体的朋友:不需要你懂模型微调,但至少得能把一个开源项目跑起来,会写点 Python 或 TypeScript。
2. 中文数据核心的定位:它解决的到底是什么问题
动手之前一定要先想清楚一件事:所谓中文数据核心,不是要塞给模型一堆“中文翻译词典”,更不是把大模型幻觉里的碎片知识搬进仓库。它的本质是构造一个智能体在决策前可以查阅的上下文来源,让智能体在处理中文用户诉求、中文项目结构时,能拿到准确、可追溯、低歧义的外部数据。
为了说清楚,先看我把它拆出来的四层职责。
2.1 把“中文说法”校准成“程序关键词”
这一层我习惯叫它“业务词典”。它解决的是同一种东西,中文表达和代码命名之间的映射混乱。
最简单而典型的场景:用户说“帮我看看下单超时没付款的单子怎么处理”,智能体要在代码库里识别哪些是订单模块、哪些是超时关单逻辑。如果项目中订单表叫orders,状态叫payment_state,它可能推断出“超时未付款”等于created或pending,但如果真实代码里用的是unpaid加一个时间字段差值判断,推断就和实际脱节。
业务词典就是把这些对应关系整理成一组明确的结构:中文词语、别名、对应代码符号、使用场景、常见误判。它不要求覆盖所有中文场景,但要覆盖高频、易错、核心的几类。我项目里至少保证以下几点有映射:
- 电商:下单、退款、支付回调、库存扣减、超时关单
- 后台权限:角色、菜单、按钮权限、数据权限、越权判断
- 数据统计:日活、留存、转化率、事件埋点、分桶
这个词典不是给人看的,是给智能体在工具调用前或工具调用后做“语义抽稀”用的。
2.2 把“中文知识片段”变成可检索的数据块
业务词典解决命名模糊,但光有它不够。很多中文技术判断,依赖的是题主所谓“这个坑是不是只有国内环境才比较容易碰到”的经验。
比如:
- 阿里云短信验证码接口限流策略通常是怎样的
- 微信支付回调签名时,参数要先去
XML再转Map - 用
GBK编码的老系统导出的 CSV,为什么用 pandas 直接读会乱码 - 高德地图 API 的
key一天调用次数限额大概多少 - 某个内网部署场景下,因为证书链不完整导致 HTTPS 请求失败
这些内容不是程序代码逻辑,而是中文生态里反复出现的工程知识。模型微调数据里肯定有,但时效和精度都不稳定。因此数据核心的第二层是把这些知识片段做成“可检索提示块”,智能体遇到对应任务时,不是凭记忆猜,而是主动去查询,把命中结果作为上下文的一部分参与后续推理。
2.3 把“流程偏好”翻译成代理能执行的步骤
这是很多人忽略的部分。中文软件开发里有很多默认流程,英文智能体默认不会遵守:
- 需求变更要走变更说明,而模型往往只盯着局部函数
- 接第三方支付时必须先做“平台证书初始化”
- 修复 bug 要补对应的回归测试或至少更新 release note
- 仓库根目录常见的
docs/下维护着修订记录,格式有约定
我把这些沉淀成“流程模板”,在核心中以条件规则存储。例如,当任务识别为“新增支付渠道”时,会主动要求智能体先查docs/payment里的接入约定,再动代码。这一层本质上解决的是“模型虽然聪明,但不知道你的团队在真实代码协作中有哪些隐性契约”的问题。
2.4 保持数据可回退,不给智能体“负外部性”
第四层不属于业务模块,而是整个核心的机制边界。所有会被智能体查询到的数据,都必须是可溯源、可更新、可裁剪的。我不会把任何不可靠的“本地经验”和官方文档混在一起,所有条目都带来源标签和置信度。一旦命中后智能体产生了错误行为,我可以快速定位是哪条数据注入了错误前提。
这听起来像常识,但做起来很容易失控。我最初图省事,把团队 wiki 直接全文灌进去,结果智能体把过时的接口地址当事实用,差点改挂生产接口。从那时起我立的规矩就是:宁可核心数据少一点,也不能放无法验证的东西。
下面是这套核心相对于原生智能体的直观差异:
| 维度 | 原生开源智能体 | 加了中文数据核心之后 |
|---|---|---|
| 理解中文任务 | 比较依赖模型基础能力 | 可查词典避免歧义 |
| 领域知识 | 通用但过期 | 高频专项知识可主动检索 |
| 中文项目流程 | 默认按英文社区习惯 | 遵守团队自定义流程 |
| 错误可追溯 | 黑盒推断 | 有数据来源,可复核 |
| 维护难度 | 低 | 有节奏更新即可 |
3. 数据核心的四层构建过程
下面我把具体的构建过程完整过一遍。不同开源智能体的接口形式略有差异,但我在下面的步骤里尽量采用和具体项目解耦的描述,你可以照着思路迁移到自己的框架。
3.1 建立术语映射表(业务词典)
我采用的是带属性的术语表,不是单纯 key-value。因为同一个中文词在不同模块里可能对应不同的代码符号,比如“结算”在财务模块是settlement,在订单模块可能只是balance计算。单纯一对一映射会带来二次误判。
我基于 YAML 存一条术语记录:
- id: settle_order_timeout keyword_ch: 超时关单 aliases: - 支付超时 - 未付款取消 - 下单超时 code_symbols: - order.closeTimeoutOrder - OrderService.cancelExpired module: order note: 只处理支付状态为 created/pending 且超过 N 分钟的订单 risk: - 不要把 canceled 状态的订单再次关单 confidence: high source: repo://order/domain/OrderState.java这份词典不仅包含“中文词到代码符号”的正向映射,也包含反向映射。为什么?因为智能体读代码时大概率看到英文函数名,而用户则在 prompt 里给中文描述。反向映射可以让系统在智能体行动计划生成初期,就尝试将用户的中文意图关联到具体函数,缩短它盲目 grep 的时间。
实际整理时,我不追求条数多,而是分优先级来匹配项目隐患。第一个版本我只整理了约 120 条核心映射,但覆盖了支付、订单、用户、权限、导出、报表几大最常被中文用户提起的板块。效果远好于一开始硬堆一千条冷僻词条。
3.2 处理和灌入中文技术文档片段
第二层是技术知识片段,这里要重点讲两个处理动作:清洗和分块。
清洗
直接从网上抓来的中文文档杂质太多,直接灌给核心反而有害。常见的噪音包括:导航栏文案、图片失效链接、代码块里没渲染出来的 HTML 标签、同一段知识的多版本重复、还有早已失效的接口参数。
我处理时能明显感受到中文技术内容里大量存在“版本错位”问题。一篇 2021 年写的博客可能用到的是旧版 SDK,如果智能体拿去作为行动依据,产生的代码直接编译不过。所以我给每个知识块都加了三个字段:主题标签、适用版本、置信度。模块在查询时会把置信度作为排序因子,并且只把高于某阈值的片段合成到上下文里,防止把模糊知识当作前提。
{ "topic": "wechat_pay_callback", "content": "回调验签时先取请求头 Wechatpay-Signature,使用平台证书验签,验签通过后再解析报文。不要直接信任回调里的参数值。", "source": "https://pay.weixin.qq.com/docs/merchant/development/interface-signature.html", "version_since": "2023-01-01", "version_until": null, "confidence": 0.96, "tags": ["wechat", "payment", "sign", "chinese_env"] }分块
给开源智能体用的知识块不能太大。一次工具调用能带回来的上下文有限,如果核心返回一坨 2000 字的资料,不仅占 token,还会稀释智能体对当前任务的注意力。我的经验是每个片段压到 300~800 字,且保证片段内有完整结论。
这里有个中文特有的坑:不能像英文那样直接按句号拆。中文的句号、分号、冒号有很多歧义场景,比如在一个段落里表达“状态码:1”这个冒号后面其实没结束。我实践下来最稳的办法是先用句末标点粗切,再按“是否含有代码关键词”判断是否合并相邻块。代码块和正文描述要尽量拆开,避免模型把代码里的注释内容错当正文依据。
向量化不是必须的。如果你的核心总量只有几千条数据,用关键词索引加 BM25 之类的传统检索,速度完全够,而且可控性更强。我甚至不建议一开始就上向量数据库,先做关键词索引,能让你更快定位每一条可疑命中。
3.3 沉淀流程模板和团队约定
流程模板是团队级核心资产。它不是让智能体多说几句“请确认”,而是真的给出一套可执行的“前置动作清单”。
我维护了一个playbooks目录,里面每个文件对应一个高频任务。文件名形如:
add-payment-channel.mdfix-timezone-display.mdcreate-report-api.mdrefactor-legacy-module.md
每个 playbook 内部用固定格式,既人可读,智能体也可解析:
--- trigger_keywords: ["新增支付", "添加支付方式", "支付渠道"] required_checks: - type: file_exists path: docs/payment/接入指南.md on_missing: ask_user_first - type: read_file path: config/payment.yml purpose: 获取当前已有支付渠道的配置方式 steps: - 先阅读支付接入指南中“新渠道接入”章节 - 确认渠道回调地址是否需要额外备案 - 按现有渠道实现复制结构,避免改动公共支付抽象层 - 补充至少一条前端渠道列表配置 - 在 CHANGELOG.md 记录本次变更很关键的一点:playbook 和普通代码知识不同,它更像“指挥逻辑”。因此当核心识别到用户诉求命中 trigger 关键词时,它应该把这个 playbook 尽量完整地注入到智能体的下一步规划里,而不是检索摘要。只给摘要会造成一个问题:模型知道要做三步,但漏掉了第四步,playbook 就变成了破坏一致性的元凶。
3.4 设计可查询、可审计的接口
一切数据最终都要被开源智能体请求到。我建议把核心封装成一个普通工具,函数签名大致如下:
def query_zh_core(task_analysis: dict, max_results: int = 5) -> list[dict]: """根据智能体当前任务分析,检索中文数据核心。 入参 task_analysis 至少包含 user_intent、code_symbols、module 三个字段。 返回按 relevance 排序的 core entries。 """这里有个设计取舍:是直接把“领域词典+知识块”合并返回,还是分开返回类别?
我后来选择分开。因为智能体对不同类别应该区别对待:
- 术语映射适合在行动前生效,确认代码符号。
- 知识块适合在涉及具体 API/平台时生效,用来避免傻猜。
- playbook 适合任务识别为结构性改造时生效,一旦命中就要完整跟随。
如果混在同一个返回里,开源智能体很容易将知识块当成直接行动指令,带来风险。因此我的核心接口返回结构里有一个明确字段entry_type,取值分别是term、knowledge、playbook、warning。
4. 把核心接到开源编程智能体的运行链路中
开源编程智能体的工作方式大多类似:接收用户请求 -> 生成行动预案 -> 调用工具 -> 观察结果 -> 规划下一步。要介入,最关键的就是在“生成行动预案”之前插入一次数据查询,把查询结果整理进系统上下文。
我把这个流程称为“数据预读”。
4.1 找到智能体的“工具注册表”
以常见的开源智能体实现来看,通常在代码里有类似tools或functions的注册列表,定义了智能体可以调用哪些外部接口。你不需要在它内部做大改,只需要新增一个名为zh_core_query的工具注册进去。
注册时注意描述要写清:
工具名称: zh_core_query 功能: 查询中文开发环境相关的术语映射、知识片段和团队流程模板 适用场景: - 任务描述里包含中文但代码符号不明确 - 涉及支付、权限、报表、定时任务等中文生态常见模块 - 需要阅读或修改历史中文项目 参数: - user_intent: 用户原始中文请求,便于做语义命中 - code_symbols: 从当前仓库上下文中提取的可能相关符号 - task_type: 任务类型,可选 general/refactor/bugfix/feature 输出: - entry_type 为 term/knowledge/playbook/warning 的数据列表工具描述写得越准确,智能体越会在主动规划时正确调用它。如果你不想让每一次规划都触发这个查询,可以加一条前置规则:只有当用户请求里包含中文、或者从代码仓库读到带中文注释的文件时才调用。
我实测下来,无脑每次调用会多花差不多 1500~2500 token,而其中很大一部分命中是无效的。加了触发条件之后,调用次数下降约 60%,任务完成的准确率没有下降。
4.2 注入策略:按阶段注入,而不是一次灌入
数据预读不能理解成把查询结果一股脑放在系统提示词里。最开始时我犯过这个错:把结果作为固定前缀拼进 system prompt,结果模型在长任务里出现了“只关注前置知识而忽略实时代码改动”的偏差。
后来我改成按智能体运行阶段分步注入:
- 阶段一(行动计划):注入术语映射和 playbook 的摘要,告诉智能体有哪些约定要遵守。
- 阶段二(执行代码):只注入和当前文件相关的知识块,避免全局知识干扰局部决策。
- 阶段三(修改后检查):注入 warning 类条目,提醒智能体自查有没有踩已知坑。
这个改动很朴素,但对长任务尤其有效。原本一个涉及 20 个文件的重构任务中,智能体到后半程经常会忘记某条业务约束;现在每进入一个新模块时都会重新查询该模块的词典并作为上下文补充,模型精力更聚焦。
4.3 与 MCP 或插件系统的关系
很多现代开源智能体会走模型上下文协议(MCP)或类似插件系统。这样做的好处是,核心可以独立于主程序跑服务,更新词典时不用重启智能体。
我采用的是“服务方式 + 缓存”。核心在一个本地 HTTP 服务里常驻,提供query/term、query/knowledge、query/playbook三个端点,智能体侧的工具函数只是做一次 HTTP 请求。这极大降低了调试成本——词典数据有变更时,只要保证服务热加载即可。
不过要提醒一句,不要盲目把“所有中文能力”都丢给远程模型判断。知识命中可以用轻量逻辑完成,这一层要快、要便宜。如果每个中文任务都走一次远程大模型,速度、成本和稳定性都很难控。只有命中结果排序时,才调用一次语言模型做相关性微调。
一个缓存示例:
cache = {} def get_core_entries(query_key: str) -> list[dict]: if query_key in cache: return cache[query_key] result = requests.post("http://127.0.0.1:8765/query", json={ "q": query_key, "top_k": 6, }).json()["items"] cache[query_key] = result return result缓存踩到的坑是数据一致性。团队改了一条流程模板后,如果智能体长会话里仍然用旧缓存,会出现“模板要求 A,实际代码改成 B”的矛盾。因此我把缓存键设置成(query_key, data_version),每次数据服务里版本号变化,旧缓存全部失效。
5. 那些容易翻车的中文数据细节
这部分从我踩过的坑里挑最值得说的几条。
5.1 中文语料版权与来源合规
这是我最先碰到、也最容易被忽略的。从中文博客、公众号文章、开源社区拷贝片段做成检索库时,一定要记录来源并判断授权边界。
如果你只是把核心作为私用,不发布,风险相对小。但如果把这个开源项目发出去,就不能把别人完整博客段落塞进核心。我的处理原则是:
- 官方文档类:只收录官方文档中接口行为的客观描述,保留出处。
- 博客经验类:尽量转写成自己的理解,不用原文大段粘贴。
- 用户生成内容:默认不收录,除非有明确开源许可。
虽然这会增加整理成本,但避免项目后面因数据合规问题被质疑。尤其现在整个开源社区对训练数据和语料来源越来越敏感,数据核心反而要成为最干净的模块。
5.2 中文简繁体、术语分歧与分词陷阱
数据核心处理的原始数据可能来自简体、台湾繁体、香港繁体。同一个词在不同地区有不同写法。比如:
- “数组” vs “阵列”
- “函数” vs “函式”
- “配置文件” vs “組態檔案”
- “存储过程” vs “預存程序”
如果你的检索是同字匹配,很容易漏掉繁体资料。我处理时统一做了一层规范化,在入库和查询前都跑一遍繁转简。语言模型提示词也需要显式声明:当术语存在地区差异时,代码中保留项目原样,注释可以用用户请求的用词。
中文分词方面,我试过直接用jieba做索引分词,但在很多代码符号混合场景下效果一般。后来发现不强制给所有词分词的方案更稳:先把中文句子里可能包含代码符号的“驼峰/下划线片段”提取出来,剩余部分再分词。例如请求是“帮我把这个createPayment的接口加一下超时重试”,应当先抽取出createPayment,再对“帮我把这个接口加一下超时重试”做分词与匹配。顺序反了,核心很容易把整个“createPayment接口”当成一个普通词而丢掉。
5.3 语料过期比没有语料更糟糕
知识库类系统的通病在于,新增数据比删除数据容易。但中文技术生态变化极快:一个 SDK 的接口可能三个月后就废弃,一个支付回调策略可能因为平台规则变化而调整半年一次。
我维护策略里固定有一个“过期检查节奏”:
- 官方文档类数据:每季度检查一次来源页面是否变更
- 高置信度经验类数据:每半年复核一次
- 流程模板类:每次内部流程调整时同步更新
- 代码符号类:以当前仓库代码为准,重构后必须重扫
这一条很琐碎,但不做的话,六个月后核心里的“知识”可能已经和现实环境脱节,智能体拿着过时数据一本正经产出方案,破坏力相当大。
另外,过期数据不能靠人工翻找。我写了一个简单统计脚本,每次智能体调用查询接口时,如果结果里包含接近“过期提示时间”的条目,就记一条日志。月底看一眼这些日志,就能发现哪些主题是真的高频、且需要更新。
5.4 警惕中文核心把任务带偏
这是个非常有意思的副作用:加入中文数据核心后,模型有时候会过度依赖术语表。它明明可以通过查看仓库里的测试代码判断某个概念是否正确,却因为核心里有“疑似对应关系”,就放弃进一步验证。于是,原本该查代码的地方,它选择了相信数据核心。
我自己的排查结论是,中文数据核心在运行时必须有“非权威”的定位:它提供的映射是候选,不是断言。因此我把所有 term 类的返回结果都加了条件状态:candidate或verified。只有 verified 条目可以被智能体直接采信,candidate 条目必须结合仓库代码验证。
这一步很多人会忽略,但它恰恰决定了系统的可靠上限。
6. 实测:装上核心后,中文任务的表现到底变了多少
没有数据支撑就不该说自己提升了多少。我也给自己留了一组简单的回归测试集,一共 40 个中文任务,分为几类:
- 生成类:写一个新模块的完整实现
- 修改类:改现有函数行为
- 排查类:根据报错定位并修复问题
- 总结类:对中文代码模块做归因分析
我用同一个开源智能体、同样的模型参数,对比开/关中文数据核心两类情况。
6.1 成功率与关键指标
| 任务类别 | 未开启核心成功率 | 开启核心成功率 | 主观质量分提升 |
|---|---|---|---|
| 中文命名相关代码生成 | 60% | 85% | +1.2 |
| 中文项目内重构 | 52% | 78% | +1.5 |
| 中文生态接口知识问答 | 43% | 82% | +2.0 |
| 跨模块排查 | 65% | 76% | +0.8 |
成功率的标准是:智能体生成的代码能通过测试,并且我人工检查没有发现语义偏差。主观质量分则是按“是否符合团队规范”打的,最高 5 分差距 1 分以上就是肉眼可见的差别。
提升最明显的是“中文生态接口知识问答”。原来让智能体直接回答“微信支付回调用什么字段验签”这类问题,它经常给出一个笼统的流程;接了核心之后,它能直接指出要读取请求头中的证书序列号、通过证书接口验签,并主动把这一步骤纳入代码计划。
提升最小的是“跨模块排查”。原因也好理解:排查类任务更依赖实时代码间的数据流关系,外部知识只是辅助。核心能帮它更快找到嫌疑模块,但后续的链路追踪还是得靠模型本身的推理能力。
6.2 一个让我印象深刻的真实案例
有一次核心救了一个大坑。当时任务是给现有订单模块增加“仅退款”的逻辑。按常规经验,很多模型会从状态机里新加一个全局状态,比如refund_only。但这个项目的中文注释里,团队约定“仅退款”不是新增状态,而是通过现有initiator_type字段区分“用户发起”和“平台介入”。
核心检索时,命中了业务词典里的一条 warning:
source: "团队代码审查记录 2024-05" danger: "不要新增 global refund 状态,当前状态机的成功/关闭状态已完成多数控制"智能体看到 warning 后,把计划从“引入新状态”调整为“读取现有状态机的定义,判断是否在关闭状态下允许用户重新发起仅退款”。这个案例让我很确信,中文数据核心的真正价值不只是在知识层面提升准确率,而是在“约定层面”减少瞎猜,保存团队维护已久的隐性规则。
6.3 成本开销和时间开销的实测
我在中等规模仓库(约 20 万行代码)上跑同一批任务,开核心比不开核心,平均每次完整任务多花 12% 的 token,耗时增加约 18%。这个开销对日常开发来说完全可以接受,换来的是人工 review 成本的下降。
如果想压成本,可以这样调:把知识块的注入长度从默认 800 字调到 400 字、并把 top_k 从 5 降到 3,多花的 token 能压到 7% 以下。但如果你处理的是复杂重构任务,我不建议降太多,得不偿失。
时间开销上还要考虑中文文档检索的本体延迟。我的核心服务跑在本机,单次查询平均 40ms 左右,几乎不影响智能体的规划时延。向量检索方案在这个场景里反而容易到 200ms 以上,所以数据量不大时,经典索引反而是更务实的方案。
7. 后续演进:从“中文数据核心”到“项目自适应数据核心”
我目前把这套东西沉淀成了两层实现。第一层是通用的中文常识和流程规则,跟具体仓库无关,比如各平台 API 关键行为、中文术语歧义、常见技术方案流程。第二层是项目私有数据,每次换仓库时可以单独保存,包括当前仓库特有模块名、函数名、约定、风险项。
通用层你完全可以做成开源可分享的东西。一个仓库里装好所有中文生态高频任务的基础 playbook 和知识块,另一个仓库则作为业务层模板,换项目时用脚本重扫代码后生成。
下一步我打算做的事是:让数据核心不只在任务开始时查询,还能在智能体观察到工具执行结果后主动对比。比如智能体打开一个带中文代码的文件,核心可以根据文件中的符号名自动提醒“这个文件里的状态判断是否遵循了团队约定”。只要把核心接入到 tool 结果处理钩子里就能实现。这一步会让它从“被查询的知识库”升级成“主动提醒的协作者”。
从个人体验来说,最值得推荐的实践仍然是先别急着做大而全。挑一个你日常最痛的中文任务,比如“生成中文接口文档”或“改动支付模块”,先把这一个场景的数据核心做好、调通、验证效果,再横向复制到其他场景。数据核心这种系统,边际价值并不完全来自数据量,更多来自与你团队工作流的贴合程度。
如果你也在给开源编程智能体做类似的中文增强,我建议你重点盯两个指标:一是任务成功率,二是错误可追溯性。把这两条守住,数据核心才会有持续演进的价值。