构建、采用、回馈:我们如何借助开源 CopilotKit 重建 fynk 的 AI 助手

HN Claude Code Adoption 2026-11-02T21:58:45.873777

Constantin Wintoniak 肖像

Constantin Wintoniak

发布于 2026 年 11 月 2 日 · 阅读时长 12 分钟

构建、采用、回馈:
我们如何借助开源 CopilotKit 重建 fynk 的 AI 助手

我们用开源方案替换了自研的 AI 聊天功能,还自己动手写出了这个框架的 Vue 版本。

我们应用内的助手,比它替换掉的那套聊天功能少了约 11,000 行自研代码。它所用的框架,直到 2026 年 5 月,在我们使用的技术栈里还根本不存在。我们的一位工程师 Abel Enekes 写出了这部分,并把它贡献给了项目。下面说说为什么这么做,以及我们会给任何想在自己产品里加入 AI 智能体(agent)的创始人或业务负责人什么建议。

CopilotKit Vue 版本带走了什么、又回馈了什么:8 个月、一位工程师、向上游贡献 62,517 行代码、让 fynk 减少 11,000 行代码

2026 年 8 月,我们上线了 fynk assistant:一个智能体,能回答合同相关问题、对比合同,还能替你创建文档、给文档打标签、移动文档。发布文章里展示了它的能力。这篇讲的是它如何构建,以及哪些部分我们决定不自己造。

我们最初的 AI 聊天功能有什么问题?

它一次只能围绕一份文档回答问题,而且每加一个新功能,都得先改应用、再改 AI 服务。2026 年初,fynk 有一套自研的文档对话功能:打开一份合同,提一个问题,得到答案,并附带指向相关条款的链接。它能用,客户也在用。

2026年1月与2026年8月各一轮助手对话对比。之前:每次对话只处理一个文档,纯文本回答,一个来源标签是唯一能跳回应用的入口,无法反问。之后:工具调用被渲染出来,可下载的表格,一张问题卡片,两个文档同时处于上下文中

回答里的每一段内容,都装在我们自己定义的消息格式里。下面是 2026 年 1 月服务器返回的「通知期为三个月」这句话的第一个词:

{
  "status": "ok",
  "message": {
    "type": "AIMessageChunk",
    "id": "…",
    "content": "The notice period",
    "additional_kwargs": {},
    "response_metadata": {}
  },
  "bounding_boxes": null
}

说白了就是:一段文本,套在我们自己编的字段里,还留了个槽位放 PDF 坐标。

这个格式只认得两样东西:一段文本,以及它在 PDF 中的位置。想加别的,就得新增字段,而且前后端和 AI 服务都得同步改完,功能才能上线。要对比两份合同,就得改格式;要展示表格,前端就得加一种新的消息类型;要让智能体问一句“需要我帮你打开吗?”,就得在手工维护的状态机里再加一个状态,旁边还挂着一条不太靠谱的 WebSocket 连接。

如果你不写代码,可以这样理解:用户看不到这些工作,但每个新功能都依赖它。我们反复在造的,是每家做 AI 智能体的公司都要造的那套基础设施。既然这样,就不该再自己造了。

为什么换用开放协议?

因为应用内智能体的难点,在每个产品里都差不多,而已经有人把它们标准化了。

2026 年初,我写信给 Abel,建议把“AI 交互结构的大部分”迁移到 AG-UI 这样的标准上。他从 2025 年秋天起就在看相关资料。今天助手里同一句话长这样:

{ "type": "RUN_STARTED",          "threadId": "…", "runId": "…" }
{ "type": "TEXT_MESSAGE_START",   "messageId": "m1", "role": "assistant" }
{ "type": "TEXT_MESSAGE_CONTENT", "messageId": "m1", "delta": "The notice period" }
{ "type": "TEXT_MESSAGE_END",     "messageId": "m1" }
{ "type": "RUN_FINISHED",         "threadId": "…", "runId": "…" }

用大白话说:只有五个标准事件,任何 AG-UI 界面都能读。

同一句话用了更多行,那为什么反而更好?因为下一次提问不需要再发明新格式。让助手去比较两份合同,同一条事件流里自然会多出几种事件(简化版):

RUN_STARTED
TOOL_CALL_START        Read Supplier agreement A
TOOL_CALL_END
TOOL_CALL_START        Read Supplier agreement B
TOOL_CALL_END
TEXT_MESSAGE_CONTENT   "I read both agreements. Three terms differ:"
STATE_SNAPSHOT         { documents in context: 2 }
RUN_FINISHED

用大白话说:助手把每一步都报出来,界面就能一步步展示给你看。

这套东西没有任何 fynk 特有的部分,而这正是关键所在。我们的 AI 服务负责运行 agent,它用开源框架 LangGraph 搭建,再由一个 AG-UI 适配器把输出转成 AG-UI 事件。后端保存会话线程和历史记录,前端用 CopilotKit 渲染这些事件。agent 与界面之间的事件走标准协议,而对话历史留在 fynk 自己的后端。

Architecture before and after. Before: the fynk frontend's hand-built chat talks to the AI service in custom JSON frames, and saves history in the fynk backend. After: the frontend runs CopilotKit and exchanges standard AG-UI events with the AI service, which saves and reads history in the fynk backend

框架不支持你的技术栈时,你该怎么办?

你可以 fork 它、等它支持,或者自己动手贡献代码。每条路都有代价,值得摊开来讲清楚。CopilotKit 原本只支持 React,而 fynk 用的是 Vue 应用。

Fork(分叉) 大概能管一个月。之后你就得一个人维护别人的代码库,上游每修一个地方,你都得手动合并。

等,是免费的——除非你要的那个功能,正好也是客户在等的。

共建,前期最贵。你得达到维护者的标准、贴合他们的测试、跟上他们的节奏。换来的回报是:你不再是唯一在乎这份代码的人。

我们选了共建。Abel 二月就开工,本以为用编程助手写「几行代码」就行。结果他的 PR 横跨 244 个文件、共 62,517 行,而且是照着 CopilotKit 自己的 React 测试套件先写测试、再写代码。维护者审了,该改架构的地方改了,2026 年 5 月 13 日合并。当天晚上,@copilotkit/vue 就挂上了 npm。

我本来只想省几行代码。真难的是从「能跑」变成「跑得和 CopilotKit 一样」。

—— Abel Enekes,fynk 软件工程师

大部分收益其实来自合并之后。五月至今,CopilotKit 团队和其他贡献者陆续发了二十多项 Vue 修复和功能,我们一行都没写:线程抽屉、文档、human-in-the-loop(人工介入流程)修复、无障碍支持。Abel 自己也继续贡献,到现在已经合并了五个 PR。当初要是 fork,这些我们一样都拿不到。

小团队加编程助手,现在能扛多大的活?

比一年前敢想的要多。搁以前,想把一个框架移植到维护者愿意接收的程度,得安排一整个开发团队。这次我们只有一个工程师,还是在他的本职工作之外完成的:二月首次提交,到五月合并大约三个月;到十月文档收尾,前后八个月。

这八个月让我们的代码库少了约 11,000 行,还白白拿到了二十多项 Vue 改动。我们是一家 25 到 30 人的公司,再选一次,还是这么选。

时间投入不是唯一的代价。参与开源意味着两条战线同时开工:一边是自己的产品,一边是项目的代码评审。对我们来说这还在可控范围内——Abel 提交 PR 的当天,维护者就完成了评审,架构上的问题由他们自己动手修,大约两周后合并。另外,客户已有的对话(包括历史记录)都要迁到新架构上,这本身就是一项实打实的工作。

作为团队,让我们意外的是精力到底花在了哪里。大部分精力都用在「告诉 agent 该做什么」和「检查结果对不对」上:先移植测试,再写代码;把决策写进 AGENTS.md 和一份新旧对齐文档(parity document),而不是每次都在提示词里重复一遍;还要逐个确认每个测试真的做到了它名字所说的那件事。

Abel Enekes,fynk 软件工程师

我要求的是一比一的测试,拿到的是一比一的测试名字。

应用内的 agent,凭什么让人放心把合同交给它?

主要就两点:你能看见它在做什么,并且能在它改动任何东西之前叫停。

fynk 助手的消息输入框,两份供应商协议以文档标签的形式附在消息上

AG-UI 让 agent 和界面之间保持一份共享状态的同步。在 fynk 里,这份状态存的是当前上下文中的文档。打开一份合同,助手就已经把它挂上了,所以一句「这里面的通知期是多久?」就能直接得到答案。在整页聊天界面里,输入 @ 就能添加文档,而且文档会一直挂着,方便你接着追问。切换到别的页面时,对话也不会中断,所以你可以先问一个宽泛的问题,再打开合同继续聊下去。对提问的人来说,这意味着不用复制粘贴,也不用解释自己指的是哪份合同。

2. 聊天里的工具调用:它的每一步你都看得见

fynk 助手的一条回答,展开了执行轨迹:已完成,共 2 步——读取供应商协议 A,读取供应商协议 B

Agent 调用的每个工具都会作为独立事件送达,CopilotKit 再把它们渲染到对话线程里。每条回答都带一个可折叠的“Finished in N steps”过程记录:读了哪些文档、搜了什么、改了什么,失败的步骤也会显示出来。合同场景下,用户需要核对 agent 到底做了什么,这份记录正好满足需求。

3. 人在回路:先询问,再执行

fynk 助手询问“要打开协议 B 供你查看吗?”,选项有“打开协议 B”“暂不”“其他”

Agent 可以暂停当前运行,等待用户确认。只要涉及数据变更,我们都用这个机制。打开文档前,它会先问一句;把合同从签署状态撤回前,它会弹出确认框说明后果(“这会取消当前签署流程,并使已有签名失效”),然后等待用户点击。这个决定在产品层面就定下了:助手每次都问,用户之后可以自行决定是否给它更大权限。

在旧版对话系统里,这种卡片要在五个地方改代码:

按以前的估算,这大约需要两周。现在只需改两个地方,大约两天:agent 暂停运行,一个 Vue 组件展示卡片并把答案送回。

4. 生成式 UI:用我们自己的组件渲染表格和列表

fynk 助手中的对比表格,展示两份协议的通知期、付款条款和自动续约情况,并带有“下载 CSV”和“下载 XLSX”按钮

纯文本并不适合用来“比较这两份供应商协议”。助手可以在聊天窗口里直接渲染我们自己的组件,所以对比结果会以真正的表格返回,每项条款并排展示,还能下载成 CSV 或 Excel。文档列表也一样:直接根据工具返回的结果渲染,而不是让模型重新敲一遍。这张表可以原样转发给 CFO。

5. 引用与权限:你能做什么,它才能做什么,仅此而已

A fynk assistant answer citing § 12, and the contract with § 12 highlighted as Chat reference 2

这部分是我们自己做的。点击带编号的引用,fynk 会把合同滚动到对应条款并高亮,核对答案只需一秒钟。助手拥有的权限和你完全一样:它只能看到你能看到的合同,也只能修改你有权修改的内容。如果某个模板需要审批,助手也会等待审批通过。如果你让它把这类合同发出去签署,它会告诉你还有哪个检查点没完成。所有 AI 处理都在欧盟境内完成,你的数据绝不会被用来训练 AI 模型。

Viola Dittmann,Advancis 法务团队负责人

最让我们印象深刻的是它用起来非常自然。像问同事一样提问,就能得到专业、可靠的回答。现在已经离不开它了。

用你自己的合同体验这五个构建模块。

哪些该自己造,哪些该直接采用?

只造能让产品与众不同的部分,其余每个智能体都需要的能力直接采用。如果你也要把智能体放进自己的产品,以下是我们当时的取舍思路:

What CopilotKit and AG-UI took over: chat UI, tool calls in the UI, human in the loop, shared agent-UI state, streaming state and the wire protocol, each compared between the self-built January 2026 version and August 2026

层级 自建还是采用 决策依据
聊天界面、流式输出、消息列表 采用(CopilotKit) 引入 CopilotKit 之前,我们大部分聊天代码都集中在这一层。替换之后,fynk 减少了约 11,000 行代码。
Agent 与 UI 之间的通信格式 采用(AG-UI) 在我们自己的消息格式里,每加一个新功能就得加一个新字段,前端和 AI 服务两端都要跟着改。AG-UI 已经有现成的事件类型,覆盖文本、工具调用、状态和暂停等待用户操作。
共享状态、工具渲染、human in the loop(人工介入) 采用(AG-UI、CopilotKit) 我们之前手写了一套状态机,一次只能处理一个文档。CopilotKit 已经能处理多个文档、工具调用以及暂停等待用户。
工具本身 自建 从模板创建、移动阶段、搭建仪表盘。助手用的工具和我们的 API、MCP 服务器是同一套,每个只需写一次。
权限与审批检查点 自建 这套逻辑本来就存在于 fynk 中。助手以用户身份调用它,所以检查点拦住助手的位置,和拦住人的位置完全一致。
引用跳转到原文 自建 要跳到 § 12 并高亮,得靠我们自己的文档查看器。这部分我们保留自研,基于第一版聊天已有的条款链接来实现。
数据和 AI 在哪里运行 尽早决定 客户在问助手能做什么之前,先问他们的合同在哪里处理。fynk 托管在欧盟境内、通过 ISO 27001 认证的数据中心。所有 AI 处理都留在欧盟,你的数据绝不会被用来训练 AI 模型。我们的安全页面列出了每一家服务商。

如果你是一位创始人,正在权衡要不要把更多技术栈自己造,我们写下了哪些合同工作流值得自建、以及在哪些环节会行不通。

常见问题

试用、读代码,或者两者都来

每个 fynk 账号都能用上助手:在侧边栏的“fynk AI”下,或通过每个页面上的助手徽标进入。如果你更想用自己的 AI 工具,fynk MCP 服务器能提供同样的工具。如果你用 Vue 开发,CopilotKit for Vue 已经为你的下一个智能体准备好了。其中有一部分是我们做的,我们很想看看你会用它做出什么。

CopilotKit 在他们的博客上宣布了对 Vue 的支持。

感谢 CopilotKit 和 AG-UI 团队的审阅、修复和耐心。也感谢 Abel——他本打算写几行代码,结果写出了一个框架移植版。

请注意,我们博客上的任何内容都不应被视为法律建议。我们理解法律事务的复杂性和细微之处,尽管我们努力确保信息准确有用,但它无法替代合格法律专业人士的个性化建议。

分享

gradient background

定期获取有见地的合同管理内容

我们尊重您的隐私。您可以随时取消订阅。

接下来

掌控你的协议。推动业务向前。

按应有的方式运行协议:快速、清晰,按你的条款来。

没有部署烦恼

几天就能上线,不用等几个季度

没有锁定

随时完整导出

没有隐藏费用

签名功能已包含,不按信封收费

查看原文