zhkj_llm_function 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zhkj_llm_function-0.2.0/PKG-INFO +383 -0
- zhkj_llm_function-0.2.0/README.md +365 -0
- zhkj_llm_function-0.2.0/pyproject.toml +32 -0
- zhkj_llm_function-0.2.0/setup.cfg +4 -0
- zhkj_llm_function-0.2.0/tests/test_framework.py +411 -0
- zhkj_llm_function-0.2.0/tests/test_tool_session_compact.py +229 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/__init__.py +78 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/agent.py +285 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/compat.py +63 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/errors.py +29 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/protocol.py +109 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/result.py +82 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/runtime.py +412 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/schema.py +139 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/session.py +95 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function/tools.py +130 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function.egg-info/PKG-INFO +383 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function.egg-info/SOURCES.txt +19 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function.egg-info/dependency_links.txt +1 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function.egg-info/requires.txt +5 -0
- zhkj_llm_function-0.2.0/zhkj_llm_function.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zhkj_llm_function
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 基于 tinyagent-py 的 DeclareAgent 插件框架:@agent_def 声明 + Pydantic 类型反射 + 统一返回结构
|
|
5
|
+
Keywords: agent,llm,tinyagent,pydantic,framework,plugin
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: tinyagent-py>=0.1.20
|
|
15
|
+
Requires-Dist: pydantic>=2.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# zhkj_llm_function(DeclareAgent 框架 / tinyagent-py 引擎)
|
|
20
|
+
|
|
21
|
+
基于 `@agent_def` 装饰器 + Pydantic 类型注解反射的 Agent 插件框架。
|
|
22
|
+
执行引擎使用 [tinyagent-py](https://pypi.org/project/tinyagent-py/):ReAct 循环、工具调用、上下文压缩、token 统计。
|
|
23
|
+
|
|
24
|
+
包名与导入名一致:`pip install zhkj_llm_function` → `from zhkj_llm_function import ...`。
|
|
25
|
+
|
|
26
|
+
📄 对外介绍版(痛点、方案、架构、对比、路线图)见 [INTRO.md](INTRO.md)。
|
|
27
|
+
|
|
28
|
+
**特点**
|
|
29
|
+
|
|
30
|
+
- 声明式:插件里只写 `@agent_def` 方法 + Pydantic 入参/出参 + docstring 业务规则,方法体只写 `...`
|
|
31
|
+
- 返回值干净:直接调用拿到的就是**出参模型实例**,`model_dump()` 与出参签名完全一致,扩展信息走代理属性
|
|
32
|
+
- 工具外置:工具由调用方在运行时传入,参数 JSON Schema 由函数签名自动反射,插件与调用方都不用手写 schema
|
|
33
|
+
- 统一错误:校验失败、工具失败、agent 未注册都抛明确异常,不返回"半成品"对象
|
|
34
|
+
- 会话内自动带历史,超阈值自动压缩上下文
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 使用方:直接安装已发布的包
|
|
40
|
+
pip install zhkj_llm_function
|
|
41
|
+
|
|
42
|
+
# 从源码安装(开发模式)
|
|
43
|
+
pip install -e .
|
|
44
|
+
|
|
45
|
+
# 安装开发依赖(pytest)
|
|
46
|
+
pip install -e ".[dev]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
环境要求:Python ≥ 3.10(`tinyagent-py` 依赖 `mcp`)。
|
|
50
|
+
依赖:`tinyagent-py>=0.1.20`、`pydantic>=2.0`。
|
|
51
|
+
|
|
52
|
+
发布到内部源时:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m build # 产出 dist/*.whl 与 *.tar.gz
|
|
56
|
+
pip install dist/zhkj_llm_function-0.2.0-py3-none-any.whl
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 快速开始
|
|
60
|
+
|
|
61
|
+
### 1. 写插件
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pydantic import BaseModel, Field
|
|
65
|
+
from zhkj_llm_function import BaseAgentGroup, ToolDef, agent_def
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class CourierTraceInput(BaseModel):
|
|
69
|
+
"""快递轨迹查询入参"""
|
|
70
|
+
|
|
71
|
+
waybill_no: str = Field(..., description="快递运单号")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CourierTraceOutput(BaseModel):
|
|
75
|
+
"""快递轨迹查询出参"""
|
|
76
|
+
|
|
77
|
+
current_status: str = Field(..., description="当前状态,如 已签收/运输中")
|
|
78
|
+
is_signed: bool = Field(..., description="是否已签收")
|
|
79
|
+
summary: str = Field(..., description="一句话中文结论")
|
|
80
|
+
error_message: str = Field(..., description="失败原因,成功时为空字符串")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CourierAgentPlugin(BaseAgentGroup):
|
|
84
|
+
"""快递物流轨迹查询插件。"""
|
|
85
|
+
|
|
86
|
+
@agent_def(ttl=86400, compact_config=None)
|
|
87
|
+
async def courier_trace_agent(
|
|
88
|
+
self, payload: CourierTraceInput, tools: list[ToolDef] | None = None
|
|
89
|
+
) -> CourierTraceOutput:
|
|
90
|
+
"""
|
|
91
|
+
角色:快递物流轨迹查询助手,负责查询运单最新状态与历史轨迹。
|
|
92
|
+
|
|
93
|
+
业务规则:
|
|
94
|
+
1. 必须调用工具查询,禁止凭记忆或推测输出物流状态。
|
|
95
|
+
2. 工具调用失败时保留运单号回显,其余字段置为未知/空值,失败原因写入 error_message。
|
|
96
|
+
3. 最终输出必须严格符合 CourierTraceOutput 结构,禁止附加自然语言解释。
|
|
97
|
+
"""
|
|
98
|
+
...
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. 定义工具(放在调用方,插件内不写工具)
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
tools = [
|
|
105
|
+
{
|
|
106
|
+
"name": "query_courier_waybill",
|
|
107
|
+
"description": "查询运单当前状态与轨迹列表",
|
|
108
|
+
"func": query_courier_waybill, # 支持同步/异步函数
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`func` 的**函数签名就是工具参数 schema 的唯一来源**,框架自动反射生成 JSON Schema:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
async def query_courier_waybill(waybill_no: str, include_history: bool = True) -> dict:
|
|
117
|
+
"""查询运单当前状态与轨迹列表"""
|
|
118
|
+
...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### 3. 调用
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
plugin = CourierAgentPlugin(model="gpt-4o-mini", api_key=os.getenv("LLM_API_KEY"))
|
|
125
|
+
|
|
126
|
+
result = await plugin.courier_trace_agent(
|
|
127
|
+
{"waybill_no": "SF123456"}, # 也可以传 CourierTraceInput 实例
|
|
128
|
+
tools,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
result.current_status # "运输中" ← 直接按出参签名取用
|
|
132
|
+
result.is_signed # False
|
|
133
|
+
result.model_dump() # 只有出参声明的字段
|
|
134
|
+
result.ok # True ← 代理属性
|
|
135
|
+
result.finish_reason # "stop" ← 代理属性
|
|
136
|
+
result.meta # token 统计等扩展信息 ← 代理属性
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 核心概念
|
|
140
|
+
|
|
141
|
+
### agent 声明
|
|
142
|
+
|
|
143
|
+
| 项 | 规则 |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| 方法签名 | `async def xxx_agent(self, payload: XXXInput, tools: list[ToolDef] \| None = None) -> XXXOutput` |
|
|
146
|
+
| 参数名 | 必须是 `self` / `payload` / `tools`(框架按位置校验,写错会直接报错) |
|
|
147
|
+
| 入参/出参 | 必须是 Pydantic `BaseModel` 子类 |
|
|
148
|
+
| 方法体 | 只写 `...`;业务规则写 docstring,框架自动转 system prompt |
|
|
149
|
+
| `ttl` | 会话有效期(秒),常用 `86400`(一天),必须是正整数 |
|
|
150
|
+
| `compact_config` | `None` 用默认压缩策略,也可传 `{"enabled": True, "max_prompt_tokens": 8000}` |
|
|
151
|
+
|
|
152
|
+
插件可以声明多个 agent:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
class MyPlugin(BaseAgentGroup):
|
|
156
|
+
@agent_def(ttl=86400, compact_config=None)
|
|
157
|
+
async def a_agent(self, payload: AIn, tools=None) -> AOut:
|
|
158
|
+
"""..."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
@agent_def(ttl=600, compact_config=None)
|
|
162
|
+
async def b_agent(self, payload: BIn, tools=None) -> BOut:
|
|
163
|
+
"""..."""
|
|
164
|
+
...
|
|
165
|
+
|
|
166
|
+
MyPlugin.agent_names() # ['a_agent', 'b_agent']
|
|
167
|
+
MyPlugin.get_agent_spec("a_agent") # AgentSpec:schema / ttl / 业务指令
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 工具
|
|
171
|
+
|
|
172
|
+
- 工具**一律由外部在调用时传入**,插件内不允许定义工具函数(保证插件可复用、可测试)。
|
|
173
|
+
- 工具定义形如 `{"name": str, "description": str, "func": callable}`;`description` 缺省时取函数 docstring。
|
|
174
|
+
- 参数 schema 由 `func` 签名自动反射(`city: str` → `{"type": "string"}`),**不需要手写**。
|
|
175
|
+
- 一次只允许一个工具;工具异常包装成 `ToolExecutionError`。
|
|
176
|
+
- 调用协议:优先 tinyagent 原生 function calling;模型不支持时兼容框架标记协议兜底:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
<|tool_call|>
|
|
180
|
+
{"name": "query_courier_waybill", "arguments": {"waybill_no": "SF123"}}
|
|
181
|
+
<|end_tool_call|>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 调用入口(三种等价写法)
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# 1) 普通方法方式(和普通 async 方法一样)
|
|
188
|
+
result = await plugin.xxx_agent(payload, tools)
|
|
189
|
+
result = await plugin.xxx_agent(payload, tools, session_id="s1")
|
|
190
|
+
|
|
191
|
+
# 2) 统一入口(按名字调用,动态分发)
|
|
192
|
+
result = await plugin.run("xxx_agent", payload, tools=tools, session_id="s1")
|
|
193
|
+
|
|
194
|
+
# 3) 需要框架原始封装(监控/排障用)
|
|
195
|
+
envelope = await plugin.run_envelope("xxx_agent", payload, tools=tools)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 返回值语义
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
result # XXXOutput 实例(动态子类),isinstance(result, XXXOutput) == True
|
|
202
|
+
result.model_dump() # 只含声明字段,结构与出参签名完全一致
|
|
203
|
+
result.ok # 代理属性:True
|
|
204
|
+
result.finish_reason # 代理属性:stop / reset / compact ...
|
|
205
|
+
result.meta # 代理属性:token 统计、session 信息等扩展信息
|
|
206
|
+
result.error # 代理属性:失败原因(成功时为空)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
扩展信息保存在 Pydantic 私有属性里,**不会污染出参 schema**,也不会出现在 `model_dump()` 中。
|
|
210
|
+
|
|
211
|
+
框架原始封装结构(`run_envelope`):
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"ok": true,
|
|
216
|
+
"data": "<XxxOutput 实例>",
|
|
217
|
+
"finish_reason": "stop",
|
|
218
|
+
"meta": {
|
|
219
|
+
"agent_name": "xxx_agent",
|
|
220
|
+
"token_original": 1465,
|
|
221
|
+
"token_compacted": 1465,
|
|
222
|
+
"session_id": "...",
|
|
223
|
+
"session_reset": false,
|
|
224
|
+
"iterations": 1,
|
|
225
|
+
"tool_calls": 1,
|
|
226
|
+
"compactions": 0
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`finish_reason`:`stop` / `reset`(会话 ttl 过期重置)/ `compact`(工具轮次超限)/ `not_registered` /
|
|
232
|
+
`input_schema_invalid` / `output_schema_invalid` / `tool_error` / `error`。
|
|
233
|
+
|
|
234
|
+
### 会话与压缩
|
|
235
|
+
|
|
236
|
+
会话只存在于**进程内存**中(框架不落盘、不做持久化):
|
|
237
|
+
|
|
238
|
+
- 不传 `session_id`:每次调用都是全新会话,不带任何历史。
|
|
239
|
+
- 传同一个 `session_id`:在 ttl 内复用同一个引擎实例,历史自动带入上下文。
|
|
240
|
+
- ttl 到期后再次使用同一 `session_id`:会话重置(`finish_reason="reset"`),历史清空。
|
|
241
|
+
- 进程重启后历史即失效;如需跨重启保留,请由业务侧自行把 `session_id` 映射到外部存储。
|
|
242
|
+
|
|
243
|
+
上下文压缩:
|
|
244
|
+
|
|
245
|
+
- 上下文 token 超过 `max_prompt_tokens`(默认 8000)时自动调用 `agent.compact()` 压缩。
|
|
246
|
+
- 压缩会先做一次摘要(额外一次模型调用),压缩后上下文只剩 `system + 摘要`。
|
|
247
|
+
- 压缩失败不阻塞主流程,原因写入 `meta["compact_error"]`。
|
|
248
|
+
- 发生过工具调用的会话也能正常压缩:框架在压缩前把 `tool_calls` 转成可读形式(引擎按 dict 读取,运行期却是对象)。
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
plugin = XxxPlugin(
|
|
252
|
+
model="gpt-4o-mini",
|
|
253
|
+
api_key=api_key,
|
|
254
|
+
compact_config={"enabled": True, "max_prompt_tokens": 8000},
|
|
255
|
+
max_tool_iterations=8, # 工具调用轮次上限
|
|
256
|
+
max_turns=10, # 单轮 ReAct 轮数上限
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 错误处理
|
|
261
|
+
|
|
262
|
+
直接调用与 `run(...)` 在失败时**抛异常**,不返回空对象:
|
|
263
|
+
|
|
264
|
+
| 异常 | 场景 |
|
|
265
|
+
| --- | --- |
|
|
266
|
+
| `AgentNotRegisteredError` | agent 未注册 |
|
|
267
|
+
| `InputSchemaError` | 入参不符合声明模型 |
|
|
268
|
+
| `OutputSchemaError` | 模型输出不符合声明模型 |
|
|
269
|
+
| `ToolExecutionError` | 工具缺失或执行失败 |
|
|
270
|
+
| `FrameworkError` | 其他运行时错误(基类) |
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from zhkj_llm_function import OutputSchemaError
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
result = await plugin.xxx_agent(payload, tools)
|
|
277
|
+
except OutputSchemaError as exc:
|
|
278
|
+
...
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## 构造参数
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
plugin = XxxPlugin(
|
|
285
|
+
model="gpt-4o-mini",
|
|
286
|
+
api_key=os.getenv("LLM_API_KEY"),
|
|
287
|
+
model_kwargs={"base_url": os.getenv("LLM_BASE_URL")}, # 自定义网关/代理
|
|
288
|
+
temperature=1,
|
|
289
|
+
session_store=None, # 自定义会话存储(默认内存)
|
|
290
|
+
compact_config=None, # 压缩策略
|
|
291
|
+
max_tool_iterations=8,
|
|
292
|
+
max_turns=10,
|
|
293
|
+
enable_todo_write=False,
|
|
294
|
+
storage=None, # 透传给 tinyagent 的 storage
|
|
295
|
+
logger=None,
|
|
296
|
+
)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`set_llm()` 可在运行期切换模型/密钥:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
plugin.set_llm({"model": "gpt-4o", "api_key": "sk-xxx"})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## 示例
|
|
306
|
+
|
|
307
|
+
仓库内含两个可直接运行的示例插件:
|
|
308
|
+
|
|
309
|
+
### 快递轨迹查询
|
|
310
|
+
|
|
311
|
+
- 插件:`plugins/courier_plugin.py`(`courier_trace_agent`)
|
|
312
|
+
- 运行:`.venv\Scripts\python.exe examples\run_demo.py`
|
|
313
|
+
(未设置 `LLM_API_KEY` 时走内置假模型,可离线跑通)
|
|
314
|
+
|
|
315
|
+
### 结合「明天」天气规划明天行程
|
|
316
|
+
|
|
317
|
+
- 插件:`plugins/weather_plan_plugin.py`(`weather_plan_agent`)
|
|
318
|
+
- 外部工具:`examples/weather_tools.py`(`query_weather_forecast`,数据源 Open-Meteo,免费无需 key)
|
|
319
|
+
- 运行:`.venv\Scripts\python.exe examples\run_weather_plan.py`
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
result = await plugin.weather_plan_agent({"city": "杭州"}, tools)
|
|
323
|
+
# result.plan_date / result.weather_summary / result.temperature_range
|
|
324
|
+
# result.precipitation_probability_max / result.has_heavy_rain / result.plan[] / result.suggestions[]
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
业务规则示例(写在 docstring,框架自动转 system prompt):
|
|
328
|
+
必须调用天气工具、禁止编造;`plan_date` 取 `payload.date` 或当前日期次日(框架会把当前日期注入用户输入);
|
|
329
|
+
降水概率 ≥ 0.5 或雷暴/雨雪时该时段改为室内;≥ 33℃ 或 ≤ 5℃ 减少户外;按上午/下午/晚上给出 ≥ 3 条行程,
|
|
330
|
+
每条写明天气依据与理由。
|
|
331
|
+
|
|
332
|
+
## 目录结构
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
zhkj_llm_function/ # 框架层(可安装的包)
|
|
336
|
+
__init__.py # 对外导出
|
|
337
|
+
agent.py # agent_def 装饰器 / AgentSpec / BaseAgentGroup
|
|
338
|
+
runtime.py # 运行时:驱动 tinyagent,产出统一返回结构
|
|
339
|
+
tools.py # 工具归一化 + 自动反射参数 schema + 执行
|
|
340
|
+
session.py # 会话存储(内存 + ttl 管控)
|
|
341
|
+
schema.py # Pydantic 模型 schema / docstring 解析
|
|
342
|
+
protocol.py # <|tool_call|> 标记协议与 JSON 提取
|
|
343
|
+
compat.py # tinyagent / litellm 消息对象兼容转换
|
|
344
|
+
result.py # 出参代理结果对象
|
|
345
|
+
errors.py # 异常定义
|
|
346
|
+
plugins/ # 示例插件(业务侧参考)
|
|
347
|
+
examples/ # 可运行示例
|
|
348
|
+
tests/ # 端到端测试
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## 开发与测试
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
python -m pip install -r requirements.txt
|
|
355
|
+
python -m pip install -e ".[dev]"
|
|
356
|
+
|
|
357
|
+
python -m pytest tests -q # 全部用例
|
|
358
|
+
python -m pytest tests/test_tool_session_compact.py -v # 工具调用 + 会话 + 压缩
|
|
359
|
+
python examples/run_demo.py # 离线端到端示例
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
测试不依赖真实模型:伪造 `litellm.acompletion` 模拟 OpenAI 风格响应,因此可在 CI 稳定运行。
|
|
363
|
+
|
|
364
|
+
## 常见问题
|
|
365
|
+
|
|
366
|
+
**Q:为什么工具要外置?**
|
|
367
|
+
插件里写死工具会让插件与具体数据源耦合、无法在测试中替换。工具外置后,插件只声明"需要什么能力",
|
|
368
|
+
由调用方决定接哪个数据源,插件可以被多个业务复用。
|
|
369
|
+
|
|
370
|
+
**Q:为什么返回值不是 `{ok, data, ...}`?**
|
|
371
|
+
业务方通常只关心业务字段。直接返回出参实例可以让调用方按出参签名写代码;
|
|
372
|
+
`ok / finish_reason / meta` 这些框架信息通过代理属性提供,既不丢信息,也不污染出参结构。
|
|
373
|
+
需要原始封装时用 `run_envelope(...)`。
|
|
374
|
+
|
|
375
|
+
**Q:模型输出不是合法 JSON 怎么办?**
|
|
376
|
+
会抛 `OutputSchemaError`(`run_envelope` 里则是 `finish_reason="output_schema_invalid"`)。
|
|
377
|
+
建议在业务规则里明确要求"仅输出结构化 JSON,禁止附加解释"。
|
|
378
|
+
|
|
379
|
+
**Q:如何接自建网关 / 代理?**
|
|
380
|
+
通过 `model_kwargs={"base_url": ...}` 透传给 litellm;示例里用的是 `LLM_BASE_URL` 环境变量。
|
|
381
|
+
|
|
382
|
+
**Q:会话会一直增长吗?**
|
|
383
|
+
会话在内存中且受 ttl 约束,过期即重置;单轮内上下文超过 `max_prompt_tokens` 会自动压缩。
|