mirror of
https://github.com/snowy-yang/xiaonuan.git
synced 2026-10-20 23:54:48 +00:00
- FastAPI + WebSocket 服务,Textual TUI 终端客户端 - PostgreSQL 存储专用连接层(asyncpg 连接池,主程序零 SQL) - 无限且永久的记忆:语义检索注入 + 每夜巩固 + 身世档案(lore) - 作息推断(猜上下学时间)、定时事件(叫早/每日提醒) - 情绪回访 / 承诺追踪 / 慢信 / 回忆杀 / 她的生活 / 连续陪伴天数 - 原生 function calling 工具:天气 / 新闻 / 搜索(免 Key) - 极小模型快速路由(AI_SMALL_*)+ 夜间批量通道(AI_BATCH_*) - 小学徒小满:夜间琐事整理,小暖晨间检查转述 - 离线测试:路由器 / 工具循环 / 杂项 / 向量 / TUI 冒烟
50 lines
1.7 KiB
Python
50 lines
1.7 KiB
Python
"""原生工具调用循环的离线测试(mock 模型服务):uv run python tool_agent_test.py"""
|
|
import asyncio
|
|
import json
|
|
|
|
import httpx
|
|
|
|
from app import connector
|
|
|
|
calls = {"model": 0}
|
|
captured = {}
|
|
|
|
|
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
body = json.loads(request.content)
|
|
calls["model"] += 1
|
|
if calls["model"] == 1:
|
|
# 第一轮:模型决定调用天气工具
|
|
assert body.get("tools"), "第一轮应带上工具声明"
|
|
return httpx.Response(200, json={"choices": [{"message": {
|
|
"role": "assistant", "content": "",
|
|
"tool_calls": [{"id": "call-1", "type": "function",
|
|
"function": {"name": "get_weather",
|
|
"arguments": json.dumps({"city": "杭州"})}}]}}]})
|
|
# 第二轮:应带着工具结果回来
|
|
tool_msg = body["messages"][-1]
|
|
captured["tool_msg"] = tool_msg
|
|
assert tool_msg["role"] == "tool" and tool_msg["tool_call_id"] == "call-1"
|
|
return httpx.Response(200, json={"choices": [{"message": {
|
|
"role": "assistant",
|
|
"content": "杭州今天有小雨,出门记得带伞哦~"}}]})
|
|
|
|
|
|
async def main():
|
|
connector._TEST_TRANSPORT = httpx.MockTransport(handler)
|
|
|
|
async def exec_(name, args):
|
|
captured["exec"] = (name, args)
|
|
return "杭州 小雨 15~22°C 降水概率70%"
|
|
|
|
out = await connector.run_model_tools(
|
|
[{"role": "user", "content": "今天出门要带伞吗"}], exec_)
|
|
|
|
assert out == "杭州今天有小雨,出门记得带伞哦~", out
|
|
assert captured["exec"] == ("get_weather", {"city": "杭州"}), captured
|
|
assert calls["model"] == 2, calls
|
|
print("工具调用循环测试通过")
|
|
|
|
|
|
asyncio.run(main())
|