前言:

上一篇写了输入分类 × Tool 决策对照表,把哪些输入允许触发 Function、哪些必须兜底先定下来。

这一篇只写实现层:
怎么把那张表,翻译成可跑的代码结构。

一、拆成两个文件:main.py + tools.py

没有一上来就引入框架、消息队列、数据库之类的东西,原因很简单:

v1.0 的目标不是“做大”,而是“可控 + 可回放 + 可测试”。

所以只保留两个角色。

1)main.py:对话调度员(串流程,不做业务)

main.py 只负责三件事:

  1. 接收用户输入

  2. 调一次模型,让模型决定 要不要调用工具

  3. 如果模型调用了工具,就执行工具,再把结果回灌给模型,让它回一句“简短确认”

也就是说 main.py 的核心价值不是“聪明”,而是:

把流程写死,把行为收敛。

2)tools.py:工具箱(能做什么 + 怎么做)

tools.py 负责两块:

  • 工具定义(JSON Schema):告诉模型“你能调用哪些工具、参数是什么”

  • 工具实现(确定性动作):真正落到文件存储/状态更新

我把工具限定为 5 个(对齐第一篇的分类):

  • record_fragment:写入干净事实碎片

  • get_fragments_by_date:查询某天碎片

  • confirm_clock_event:记录一次打卡确认

  • mark_clock_timeout:记录一次打卡超时

  • get_clock_status:查询打卡状态

关键的一点是:

tools.py 里没有“写日报/写用例”这种生成类工具。
v1.0 宁可不做,也不让系统越权。

二、代码结构背后的一个核心取舍:AI 不做决定

很多人做 Agent 喜欢把 AI 放在最核心位置:
让模型自己决定“记什么、怎么记、写成什么样”。

但我在 v1.0 选择另外的方式:

AI 只负责“识别输入类型”和“触发哪个工具”,
真正写入/查询动作必须走工具。

原因也很现实:

  • 真实输入里有情绪、有吐槽、有半句话

  • AI 很容易把“像工作内容”的句子当事实写入

  • 一旦脏数据进了碎片层,后面总结全都变脏

所以我更看重“可控性”:

允许 AI 参与决策,但不允许 AI 直接动数据。

三、闭环怎么跑:一次输入的一整条链路

v1.0 的主链路非常固定:

  1. 用户说一句话

  2. 模型决定是否要调用工具(tool_choice=auto)

  3. 如果调用:执行工具

  4. 把工具结果回灌给模型

  5. 模型再回一句“确认/查询结果”,而不是长篇总结

这条链路的好处是:

  • 你能清楚地看到:模型“为什么会触发这个工具”

  • 工具执行是确定的:写入就是写入,查询就是查询

  • 系统出错也好定位:是模型误判?还是工具参数?还是兜底没挡住?

四、 reminders:做的三条“从严限制”

这三条限制其实比代码更重要,它们决定了 v1.0 的稳定性。

1)只收“干净事实”,情绪/吐槽一律不入库

像下面这种输入,在真实工作里非常常见:

  • 写用例了,烦死了

  • 今天好像没干啥

  • 开发接口不行

它们“真实”,但不“干净”。
v1.0 我选择 宁可不记,也不污染事实层

2)查询 ≠ 生成

“我今天都干了啥” → 只触发查询工具,把事实拿出来
“帮我写日报/写用例” → 直接兜底,不给生成能力

3)main.py 不做业务判断

不在 main.py 里堆“规则 if else”,保持它只管流程。
业务动作都收敛到工具上,这样后面测试也更清晰。

五、这一步的收获:代码很少,但行为边界很清楚

到这里,v1.0 其实已经能用:

  • 白天随口记一句“工作事实”

  • 下班前做“确认/查询”

  • 需要回顾时再查当天碎片

现在主要的不是“功能多”,而是:

系统不会因为真实输入的复杂性而乱触发工具。

下一篇我会写怎么做“冒烟测试”:
不是写复杂测试框架,而是用一组真实工作输入去撞系统,看看它会不会失控。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐