本页只译了一部分
本页 33 段里还有 5 段没译成简体中文,没译的段落显示英文原文。函数调用
机器翻译译自英文(docs.typesafe.ai)本页 33 段里 5 段还没译
文档索引
在以下地址获取完整的文档索引:https://docs.typesafe.ai/llms.txt 使用此文件可以在进一步探索之前发现所有可用页面。
函数调用
通过将函数名称和封闭集合参数映射到具有置信度感知的 TypeSafe 问题,将自然语言交易请求转换为对普通类型函数的调用。
当你点一杯“大杯冰燕麦拿铁,不加甜味剂”时,咖啡师不会记下你的句子。他们会在杯子上标记四个选项。本 cookbook 对交易 API 执行相同的操作:输入一个句子,输出一个函数名称及其作为已评估枚举的参数,每个参数都带有置信度。
"plot rolling correlation between nvda and spy for the past month"
rolling_correlation(symbol='NVDA', benchmark='SPY', window='1mo') confidence 0.91
"compare nvda amd and msft over the past three months"
compare_returns(symbols=['NVDA', 'AMD', 'MSFT'], window='3mo') confidence 0.94
"show me apple daily with volume"
plot_price(symbol='AAPL', resolution='1d', include_volume=True) confidence 0.75
"what tickers do you have"
list_symbols() confidence 1.00这些调用被发送到交易助手中的十个普通函数。它们的参数取值来自固定列表,因此它们已经是 Literal 类型:
def plot_price(
symbol: Literal["SPY", "NVDA", "AMD", "AAPL", "MSFT", "TSLA"],
style: Literal["line", "candles"] = "line",
resolution: Literal["1m", "5m", "15m", "1h", "1d"] = "15m",
window: Literal["1d", "1w", "1mo", "3mo"] = "1w",
include_volume: bool = False,
moving_average: Literal["9", "20", "50"] | None = None,
log_scale: bool = False,
): ...值来自固定列表的参数属于闭合集合。当它从该列表中采用一个值时,会生成一个针对这些特定值的 Choice 问题,因此传入函数的任何内容都是函数可接受的值。你无需改动函数本身。你所添加的是用通俗语言说明每个参数含义的规范。最终你将获得一个 Dispatcher,可将其指向你自己的函数。
Setup
pip install ipython polars matplotlib numpy "typesafe-sdk>=0.5.7" cooksafe --extra-index-url https://pypi.typesafe.ai/设置 TYPESAFE_API_KEY。此文件旁边有两个模块。trader.py 包含十个函数,以及一个 TypeSafe 客户端,该客户端从缓存中读取答案,因此重新渲染时可回放下方的数值而无需调用 API。dispatch.py 包含读取签名和规范并发起调用的代码。
import json
from pathlib import Path
from cooksafe import make_playground_link
from dispatch import ROUTE, Dispatcher, closed_sets
from IPython.display import Markdown, display
from trader import TOOLS, client, load
TYPESAFE_MODEL = "jev-1.12"
print(f"{len(TOOLS)} functions over {load().height:,} one-minute bars")10 functions over 156,780 one-minute bars
在签名中查找封闭集合
类型提示已经说明了哪些参数来自固定列表,以及每个列表中有什么内容。closed_sets 读取签名并将这些参数分为三种形状:选择(一个 Literal,即列表中的一个值)、集合(一个 list[Literal[...]],即任意数量的值)或标志(一个 bool,即开或关)。所有十个函数都在 trader.py 中定义。
for name, fn in TOOLS.items():
shapes = closed_sets(fn)
print(
f" {name:<20}{len(shapes)} "
+ ", ".join(f"{a}:{s}" for a, (s, _) in shapes.items())
)
print(
f"\n{sum(len(closed_sets(fn)) for fn in TOOLS.values())} fillable arguments in total"
) list_symbols 0
market_summary 1 window:choice
plot_price 7 symbol:choice, style:choice, resolution:choice, window:choice, include_volume:flag, moving_average:choice, log_scale:flag
intraday_pattern 3 symbol:choice, window:choice, metric:choice
compare_returns 3 symbols:set, window:choice, normalize:flag
rolling_correlation 4 symbol:choice, benchmark:choice, window:choice, resolution:choice
summary_stats 2 symbol:choice, window:choice
volatility 3 symbol:choice, window:choice, annualized:flag
top_movers 2 window:choice, direction:choice
drawdown 3 symbol:choice, window:choice, plot:flag
28 fillable arguments in total
top_movers 展示了哪些内容被省略。在其三个参数中,两个是封闭集合。第三个参数 limit 是 int,因此它不会生成问题,并保持默认值 3。自由文本、数字和日期以相同方式工作:不生成问题,并使用函数的默认值。
编写规范
Literal 提供字符串 "1mo" 和 "3mo"。它并未说明用户输入“本季度”意味着后者。规范说明了这一点。它持有每个参数一个问题、每个选项一行、每个函数一个描述,以及另一个在函数之间进行选择的问题。它保存在 spec.json 中,LLM 可以根据签名为你编写它。
SPEC = json.loads(Path("spec.json").read_text())
for argument in ("style", "moving_average"):
print(
json.dumps(
{argument: SPEC["functions"]["plot_price"]["arguments"][argument]}, indent=2
)
){
"style": {
"question": "Does the user want a plain line or candles?",
"stated": "Does the user say how the chart should be drawn, such as a line, candles, or OHLC bars?",
"options": {
"line": "a simple line through the closing prices",
"candles": "a candlestick or OHLC chart, showing each bar's open, high, low and close"
}
}
}
{
"moving_average": {
"question": "How many bars should the moving average cover - nine, twenty, or fifty?",
"stated": "Does the user ask for a moving average or a smoothed line over the candles?",
"options": {
"9": "a nine-bar moving average, a fast one",
"20": "a twenty-bar moving average",
"50": "a fifty-bar moving average, a slow one"
}
}
}
选项键是函数接受的字符串,因此之后无需将标签映射回参数。stated 使参数可选。它是第二个是/否问题,询问命令是否完全提到了与该参数相关的内容。如果答案为否,则调用会忽略该参数,并应用函数自身的默认值。
集合参数针对其每个成员各自生成一个问题,其中 {} 代表成员名称。"Does the user want {} in the comparison?" 将为每个代码生成一个问题。
编写关于概念本身而非用户可能选择的词语的问题,因为匹配基于语义:即使 spec.json 中没有任何地方出现 tracking 或 lately,"is amd tracking nvidia lately" 也能匹配到 rolling_correlation。避免使用参数名称来命名问题——"Which resolution?" 无法为命令提供匹配依据。
将规范转换为问题
Dispatcher 仅根据规范构建一次问题。每个命令随后成为一次请求,携带函数选择以及每个函数的全部参数,而调度器仅读取所选函数的答案。
assistant = Dispatcher(SPEC, TOOLS, client)
print(f"{len(assistant.questions)} questions per command, among them:")
for qid in (
"__tool__",
"plot_price.style",
"plot_price.style?",
"compare_returns.symbols.NVDA",
):
question = assistant.questions[qid]
print(f" {qid:<30}{question['type']:<8}{str(question['instructions'])[:64]}")54 questions per command, among them:
__tool__ choice What is the user asking the trading assistant to do?
plot_price.style choice Does the user want a plain line or candles?
plot_price.style? noul Does the user say how the chart should be drawn, such as a line,
compare_returns.symbols.NVDA noul Does the user want NVDA in the comparison?
运行十四条命令
一个请求占用一行,其 confidence 是该调用背后最不确定的判断。
COMMANDS = [
"show nvda 1h",
"plot rolling correlation between nvda and spy for the past month",
"when during the day does nvda trade the most",
"what moved today",
"what tickers do you have",
"how did the market do this week",
"candles for tesla with a 20 period moving average",
"compare nvda amd and msft over the past three months",
"how volatile is tsla",
"biggest losers today",
"worst drawdown for nvda this quarter, and chart it please",
"spy stats for the last month",
"show me apple daily with volume",
"is amd tracking nvidia lately",
]
CALLS = {command: assistant(command) for command in COMMANDS}
for command, call in CALLS.items():
print(f' "{command}"')
print(
f" {str(call):<66}confidence {call.confidence:.2f}"
f" tool {call.tool.probability:.2f}"
) "show nvda 1h"
plot_price(symbol='NVDA', resolution='1h') confidence 0.78 tool 1.00
"plot rolling correlation between nvda and spy for the past month"
rolling_correlation(symbol='NVDA', benchmark='SPY', window='1mo') confidence 0.91 tool 1.00
"when during the day does nvda trade the most"
intraday_pattern(symbol='NVDA') confidence 0.53 tool 1.00
"what moved today"
top_movers(window='1d', direction='gainers') confidence 0.90 tool 0.90
"what tickers do you have"
list_symbols() confidence 1.00 tool 1.00
"how did the market do this week"
market_summary(window='1w') confidence 0.96 tool 0.99
"candles for tesla with a 20 period moving average"
plot_price(symbol='TSLA', style='candles', moving_average='20') confidence 0.69 tool 0.97
"compare nvda amd and msft over the past three months"
compare_returns(symbols=['NVDA', 'AMD', 'MSFT'], window='3mo') confidence 0.94 tool 1.00
"how volatile is tsla"
volatility(symbol='TSLA') confidence 0.96 tool 1.00
"biggest losers today"
top_movers(window='1d', direction='losers') confidence 0.98 tool 0.98
"worst drawdown for nvda this quarter, and chart it please"
drawdown(symbol='NVDA', window='3mo', plot=True) confidence 0.84 tool 0.84
"spy stats for the last month"
summary_stats(symbol='SPY', window='1mo') confidence 0.88 tool 0.88
"show me apple daily with volume"
plot_price(symbol='AAPL', resolution='1d', include_volume=True) confidence 0.75 tool 0.85
"is amd tracking nvidia lately"
rolling_correlation(symbol='AMD', benchmark='NVDA') confidence 0.82 tool 0.82
两个长命令都按要求输出。“绘制过去一个月 nvda 和 spy 的滚动相关性”从一句话中填充了四个参数。其中两个参数,symbol 和 benchmark,来自相同的六个股票代码,并且每个股票代码都落在了正确的参数中,因为问题明确了角色:作为度量对象的第一个名称 对比 第二个名称,即基准。“比较过去三个月的 nvda amd 和 msft”将三个股票代码放入集合中,并将其他三个排除在外。
运行其中三个:
for command in (
"plot rolling correlation between nvda and spy for the past month",
"compare nvda amd and msft over the past three months",
"when during the day does nvda trade the most",
):
print(f'"{command}" -> {CALLS[command]}')
display(CALLS[command].run())"plot rolling correlation between nvda and spy for the past month" -> rolling_correlation(symbol='NVDA', benchmark='SPY', window='1mo')
"compare nvda amd and msft over the past three months" -> compare_returns(symbols=['NVDA', 'AMD', 'MSFT'], window='3mo')
"when during the day does nvda trade the most" -> intraday_pattern(symbol='NVDA')
以及那些以文本作答的命令:
for command in ("how did the market do this week", "biggest losers today"):
print(f'"{command}" -> {CALLS[command]}')
print(CALLS[command].run(), "\n")"how did the market do this week" -> market_summary(window='1w')
the board over 1w
NVDA 254.12 9.62% 389,465,563
AMD 184.20 1.51% 182,740,497
AAPL 258.71 0.97% 223,818,998
SPY 664.86 0.40% 138,617,365
MSFT 451.35 0.26% 113,427,173
TSLA 320.22 -0.97% 266,317,023
"biggest losers today" -> top_movers(window='1d', direction='losers')
top 3 losers over 1d
AMD -0.57% -> 184.20
MSFT 0.67% -> 451.35
AAPL 1.40% -> 258.71
读取置信度
confidence 报告调用中最不确定的一次判断,而不是所有判断的乘积,因为只要有一个参数错误就足以破坏结果。乘积回答的是一个不同的问题("每个部分都正确吗"),并且随着函数参数增多,乘积值会下降,无论其中任何单个判断是否存疑。
该数字的来源,逐项说明:
call = CALLS["is amd tracking nvidia lately"]
print(f'"is amd tracking nvidia lately" -> {call} confidence {call.confidence:.2f}')
for name, argument in call.arguments.items():
top = sorted(argument.distribution.items(), key=lambda kv: -kv[1])[:3]
shown = "omitted, default stands" if argument.omitted else repr(argument.value)
print(
f" {name:<12}{shown:<26}p {argument.probability:.2f} "
+ " ".join(f"{k} {v:.2f}" for k, v in top)
)
print(f" weakest argument: {call.weakest().name}")"is amd tracking nvidia lately" -> rolling_correlation(symbol='AMD', benchmark='NVDA') confidence 0.82
symbol 'AMD' p 0.87 AMD 0.87 NVDA 0.13 AAPL 0.00
benchmark 'NVDA' p 0.78 NVDA 0.92 AMD 0.08 AAPL 0.00
window omitted, default stands p 0.96
resolution omitted, default stands p 0.99
weakest argument: benchmark
此处省略了 window 和 resolution,因为 "lately" 并未指明回溯多久或使用什么周期,因此 rolling_correlation 将使用其默认的值为一个月和小时级周期。这正是 stated 问题的用途。如果没有它,选择就必须明确指定某个窗口,并且会以确定的语气命名该窗口。
Open it in the playground
下方的链接包含一个命令及其所选函数的问题:十个函数描述之间的选择,以及 rolling_correlation 的四个参数。在那里编辑命令,参数也会随之更改。
COMMAND = "plot rolling correlation between nvda and spy for the past month"
picked = CALLS[COMMAND]
playground_link = make_playground_link(
COMMAND,
{ROUTE: assistant.questions[ROUTE]}
| {q: v for q, v in assistant.questions.items() if q.startswith(f"{picked.name}.")},
models=[TYPESAFE_MODEL],
)
display(
Markdown(
f"🔗 [Open the command and its questions in the TypeSafe playground]({playground_link})"
)
)