前言
> 下周 0.1.39 版新功能预告
之前我写过一篇文章,叫 Funciton Impl, 具体链接在这里:函数实现越通用越好?来看看 Byzer-LLM 的 Function Implementation 带来的编程思想大变化
通过 Funcition Impl 功能,我们以后写函数,只需要提供函数签名,无需做具体的实现,然后正常使用。
这个好处是,程序员可以正儿八经做架构,做设计,具体一些细节留给大模型来完成。
实际上,这个功能对大模型要求还是比较高的,因为除了要生成代码以外,还需要依赖 Function Calling 和 Respond with class等能力,所以 Function Impl 算的上是大语言模型和Python 融合的集大成者。
不过之前的实现,终究还是有那么丢丢不够自然,用户依然需要显式调用大模型来完成函数的实现和调用,这是个典型例子:
def calculate_time_range()->TimeRange:
'''
计算时间区间,时间格式为 yyyy-MM-dd.
'''
pass
t = llm.chat_oai([{
"content":"去年三月到七月",
"role":"user"
}],impl_func=calculate_time_range,response_class=TimeRange,
execute_impl_func=True,enable_default_sys_message=True)
从上面的代码可以看到,为了调用这个函数,我们需要显示的调用 llm.chat_oai 聊天接口,还要配置一些参数,能不能像以前那样,写好了 calculate_time_range 后,直接就 calculate_time_range() ?
这个就是我们今天的主题啦。
llm.impl
现在我们提供了一个新的方式,对普通 Python 程序员而言会更加友好,看下面例子:
from byzerllm.utils.client import code_utils,message_utils
from typing import List,Union,Optional
import pydantic
class Time(pydantic.BaseModel):
time: str = pydantic.Field(...,description="时间,时间格式为 yyyy-MM-dd")
@llm.impl()
def calculate_current_time()->Time:
'''
计算当前时间
'''
pass
calculate_current_time()
# output: Time(time='2024-01-28')
我们定义了一个函数叫 calculate_current_time , 这个函数是个空实现,反之值是 Time, 和普通函数的唯一区别是加了一个 @llm.impl() 注解。
接着就可以直接调用这个函数了,输出结果 Time(time='2024-01-28')
那这个 llm 是哪里来的呢?你只要在用之前这么初始化就可以:
import ray
from byzerllm.utils.client import ByzerLLM,Templates
ray.init(address="auto",namespace="default",ignore_reinit_error=True)
chat_model_name = "wenxin_chat"
llm = ByzerLLM()
llm.setup_default_model_name(chat_model_name)
llm.setup_template(chat_model_name,"auto")
llm.setup_extra_generation_params(chat_model_name,extra_generation_params={
"temperature":0.01,"top_p":0.99
})
这里我们对接了文心一言的SaaS版本,那怎么部署这些模型呢?别忘了 Byzer-LLM可以很方便的部署私有模型或者SaaS模型,参看这里:Byzer-LLM 支持同时开源和SaaS版通义千问
好,现在回过头来,我们看看实现有参数的函数调用:
from byzerllm.utils.client import code_utils,message_utils
from typing import List,Union,Optional,Annotated
import pydantic
from datetime import datetime
class Time(pydantic.BaseModel):
time: str = pydantic.Field(...,description="时间,时间格式为 yyyy-MM-dd")
@llm.impl()
def add_one_day(current_day:Annotated[datetime,"当前日期,类型是datatime.datetime"])->Time:
'''
给传入的日期加一天,得到明天的时间
'''
pass
add_one_day(datetime.now())
# output: Time(time='2024-01-29')
完全和以前没有任何区别。不过为了代码自动生成能效果更好,我们对此参数使用 annotated ,方便加入一些prompt,这种就叫 LLM-Friendly 函数,可以看这里:
https://github.com/allwefantasy/byzer-llm?tab=readme-ov-file#llm-friendly-functiondataclass
根据问题动态变化实现函数
前面我们都是给一个空函数,函数的是根据入参,返回值以及doc进行固定的。如果现在还有一类新的问题,就是我们需要动态调整函数的实现。比如我现在有个计算时间区间的函数,但是呢,我希望每次这个函数要根据一段话动态变化。
下面有一段计算时间区间的函数:
class TimeRange(pydantic.BaseModel):
'''
时间区间
格式需要如下:yyyy-MM-dd
'''
start: str = pydantic.Field(...,description="开始时间.时间格式为 yyyy-MM-dd")
end: str = pydantic.Field(...,description="截止时间.时间格式为 yyyy-MM-dd")
def calculate_time_range()->TimeRange:
'''
计算时间区间,时间格式为 yyyy-MM-dd.
'''
pass
这个calculate_time_range函数,其实并不明确,我们希望他计算一个时间区间,但是怎么计算,这个是不确定的。比如我可以让这个函数计算这个问题:去年三月到七月
这个时候你可以这样:
@llm.impl(instruction="去年三月到七月")
def calculate_time_range()->TimeRange:
'''
计算时间区间,时间格式为 yyyy-MM-dd.
'''
pass
calculate_time_range()
# output: TimeRange(start='2023-03-01', end='2023-07-31')
如果我希望每次instruction 都变化,怎么办?
难不倒我们,可以这样:
from byzerllm.utils.client import code_utils,message_utils
from typing import List,Union,Optional
import pydantic
class TimeRange(pydantic.BaseModel):
'''
时间区间
格式需要如下:yyyy-MM-dd
'''
start: str = pydantic.Field(...,description="开始时间.时间格式为 yyyy-MM-dd")
end: str = pydantic.Field(...,description="截止时间.时间格式为 yyyy-MM-dd")
def calculate_time_range()->TimeRange:
'''
计算时间区间,时间格式为 yyyy-MM-dd.
'''
pass
llm.impl(instruction="去年三月到七月")(calculate_time_range)()
虽然是 Function Impl 的语法糖,但是用起来确实方便对不对?
总结
对于前面不需要动态变换实现的函数,我们实际上可以进行缓存,也就是生成只会执行一次,后续调用速度就和正常的函数调用一样了。
而对于最后那个需要根据问题动态生成函数实现的部分,做缓存可能需要更谨慎一些,但通过一些手段的优化,也是可以获得更好的性能的。
在真正的 AGI 到来之前,这才是大模型构建一个高效复杂项目的正确姿势。