PRODUCT → EVIDENCE
产品视角 · 问题、判断与结果
问题:Agent 的错误推荐可能给用户带来不必要的关系成本。我的职责:双人项目中负责产品机制、接口契约与验收边界,队友负责实现。
判断与交付:关注、邀请、会话分开授权;事实附证据,画像默认私有,零结果保持为空。不足:机制与原型已落地,推荐反馈及真实用户效果尚待验证。
核验进展与复现材料 · 2026-10-06
已核对 A2A Agent Card 的 HTTP 入口与 JSON-RPC 实现、两组边界测试文件和六组匹配样例。推荐理由的有用/无用持久化与按 reasonCode 聚合仍未找到实现,不能用网站交互冒充产品能力。
下一步:在产品仓库实现推荐反馈契约:归属校验、去重/改判、失败重试、reasonCode 分母与隐私边界;收集真实样本后再讨论推荐排序。
材料区分报告记录、源码核对与待办设计;不代表真实用户效果或本轮重新运行了底层项目。
② 角色与时间
09.10 方案机制 → 09.13 PRD v1.0 → 09.14–09.15 主体开发 → 09.27 源码精读 → 09.30 快照
我在这个双人项目里负责的是「先想清楚再让人写」的那一半——产品的判断力从哪来、接口边界划在哪、哪些能力其实接不上。代码实现由队友承接。
《技术方案与双人分工》
324 行(已入主仓库)《方向定位与流程总结》
258 行(已入主仓库)《Z1Space 原型评审与接入风险》
15,434 B参赛视觉(图标 + 展示图)与评论区长评文案
v1→v3 / 19 条《Z1Space 源码精读》(5 章)
117 处带行号引用 / 35,443 汉字③ 问题背景
「推荐同路人」这类功能最常见的做法是让 Agent 直接建立连接、直接把两个人塞进一个会话。问题是:Agent 的判断一旦错了,代价由用户承担——被不想联系的人打扰、或者收到一堆「AI 觉得你们很配」的空推荐。
所以这一版要解决的不是「推荐得准不准」,而是一旦推荐不准,用户能不能不受影响。
④ 关键判断:选了什么 / 否掉了什么 / 代价是什么
Agent 不能直接建立关系,只产出候选与理由
依据关系一旦建立就有社交成本,撤销成本远高于建立成本。把不可撤的动作交给模型,风险不对称。
代价少了很多「自动帮你连上」的爽点,转化路径变长一步。
关注 / 邀请 / 会话三种关系严格分离,pending 状态下不能发普通消息
依据三者在真实社交里是不同的授权层级。混在一起就会出现「只是关注了却能被发消息」这种越权体验。
代价状态机复杂度上升,需要为每个转移写测试。
推荐必须给出理由与来源,零结果不用示例数据填充
依据有依据而不是「AI 觉得」——用户能判断推荐是否可信。
代价冷启动阶段页面上真的会出现空结果,不如填几个假数据好看。我选择让它空着。
Agent 之间的每句事实陈述必须挂证据 ID,且只能引用自己那一侧的
依据两个 Agent 互相交换内容时,一句无依据的生成会污染对方上下文并逐轮放大幻觉——这是多 Agent 协商特有的风险。
代价模型输出被两层校验卡住,非法输出直接整场判失败;调试时看不到「将就一下的结果」。
画像的公开 / 私有边界缺省为私有
依据用户填资料时的心理预期是「写给系统看」,不是「公开发帖」。安全性应来自「忘记填」的默认值,而不是「记得填」。
代价调用方必须显式声明才能用,代码更难写;可被检索的信息变少。
关系由服务端状态确认,刷新或重登不重复创建
依据客户端不可信;幂等必须在服务端保证,否则一次刷新就多一条重复关系。
代价服务端多一次状态查询,并需要一整套上限与淘汰策略。
⑤ 实现要点
实现按客户端、HTTP 入口、领域逻辑和集成层分工。源码包含 A2A 网关、触发路由、匹配、证据台账、持久化与真人邀请;代码量只对应历史快照。三条核心链路是:
可解释匹配
match-engine.ts
硬门槛:主题相关度 < 0.25 或证据强度 < 0.25 直接判不推荐;理由由指标反向拼装并附 reasonCode,不是让模型现写
证据约束的 Agent 会谈
a2a-session.ts
越界引用证据 → 会话判失败;观察员判「可继续」时必须同时引用被双方各自引用过的证据,否则判证据不足
决定与副作用分离
trigger-router.ts
同一道门槛只写一次且漏不掉任何入口;未确认画像、身份错配、Skill 禁用被统一映射为 409 / 400
它还真的说 A2A 协议,不是借名字。对外暴露 /.well-known/agent-card.json 网关卡、个人卡片与公开资料代理卡片,以及 JSON-RPC 通道 /a2a/agents/:agentId 的 SendMessage 与 /a2a/tasks/:id 任务查询。传输层走真实 HTTP 回环(带 a2a-version: 1.0 与 Bearer 共享密钥,密钥比较用 timingSafeEqual),并校验发言顺序——说话方与轮次不符合预期即返回 A2A_TURN_OUT_OF_ORDER;同一 messageId 重投直接回放缓存,不重复生成。
幂等与资源边界是显式设计的。会话创建键为 owner : identity : idempotencyKey,并发重复请求返回同一个会话 ID;会话总量上限 500、单账号 20、并行 2,超出返回 429;推荐快照单账号保留 100 条并按序淘汰,被淘汰后的旧推荐 ID 返回 404;画像版本变化会让旧推荐失效(409);请求体超限返回 413。这些数字都有对应测试。
没有配置就降级,而且降级是显式标注的。未配置模型密钥时全链路走确定性回退(画像三段、会谈轮次、观察员裁决、开场草稿各有一份不依赖模型的实现);知乎 OAuth 未配置时进入明确的演示模式,不伪装成真实接入。
⑥ 可验证结果
npm start / npm test / npm run check
package.json
历史站内记录的 41 个用例是服务端总数,不等于 41 项 A2A 安全测试。本轮读取到 a2a-session.test.ts 的 13 个测试声明、trigger-router.test.ts 的 16 个测试声明,涵盖正常流程与多类边界,不能把整个文件的用例都称作同一种防护。
诚实附注
- 这是黑客松项目,产品形态与指标都停在原型与实现阶段,没有真实用户数据,也没有线上灰度。
- 「推荐必须给出理由」这条我实现了理由与来源的字段契约,但没有做推荐质量的量化评测——这一版验证的是「机制正确」,不是「效果多好」。
- 41 个服务端用例是历史记录,不能作为当前完整测试统计;客户端交互与真实推荐效果需要另行验证。
- 知乎开放平台没有私信 / IM 能力。这是我做原型评审时最先判死的一条:它直接决定了产品把「真人连接」定义成站内会话,而不是替用户发私信——否则答辩时一句话就会被问倒。
- 会谈质量四指标目前只在人—人场景验证过;内容推荐复用同一套引擎,两端阈值是否该一致,没有数据支撑。
⑦ 如果重做
会把「推荐理由」从「有依据」推进到「依据可评估」:现在理由是一段说明文字,但没有任何东西能量化它是否真的被用户认可。重做的话,我会给理由加一个可点选反馈(有用 / 没用),先攒出评估样本,再谈召回排序。
第二个是我在方案里写了、但没落地的一条:立场反转测试。两个模型天然倾向于互相附和、伪造共识,我原本设计的是把一方立场反向注入,看对方是否仍然认同,用来识别「迎合」。代码里只做到「观察员必须引用双方证据」,还差这一步。
另外 pending 这个状态我当初只做了「不能发消息」,重做会补上超时自动失效——现在一个悬着的邀请可以永久占着状态位。