AI functions
会話中に呼び出せるカスタム backend logic で AI agent を拡張します。
なぜ AI Functions を使うのか
AI agent は、単に質問に答えるだけでは不十分です。注文状況を調べたり、database を更新したり、domain-specific な計算を実行したり、external API を呼び出したりする必要があります。model は、それらを単独で行う方法を知りません。
AI functions がなければ、agent は model がすでに知っていることに制限されます。AI functions があれば、会話に応じて agent は 언제でも backend code を呼び出せます。
- TypeScript
- Python
// Backend: define the function
@aiFunction<{ shipName: string }>(
'指定された船に乗っている海賊の一覧を返します。ユーザーが船の乗組員について尋ねたときに呼び出してください。',
[{ name: 'shipName', description: '船の名前', type: 'string', required: true }],
)
async listPiratesOnShip(params: { shipName: string }): Promise<string> {
// Your custom logic: query a database, call an API, run a calculation, etc.
const { shipName } = params;
const crew = await this.lookupCrew(shipName);
return crew.join(', ');
}
# Backend: define the function
@ai_function(
'指定された船に乗っている海賊の一覧を返します。ユーザーが船の乗組員について尋ねたときに呼び出してください。',
[{'name': 'shipName', 'description': '船の名前', 'type': 'string', 'required': True}],
)
async def list_pirates_on_ship(self, params: dict) -> str:
# Your custom logic: query a database, call an API, run a calculation, etc.
ship_name = params['shipName']
crew = await self.lookup_crew(ship_name)
return ', '.join(crew)
// Frontend: pass the function to the agent and ask a question
const response = await squid
.ai()
.agent('pirate-agent')
.ask('Who is on the Black Pearl?', {
functions: ['listPiratesOnShip'],
});
Functions は、リクエストごとに渡す代わりに、Agent Studio または setAgentOptionInPath を使って agent に恒久的に追加することもできます。
agent は function の description を読み、いつ呼び出すべきかを判断し、その結果を response に組み込みます。
Overview
AI functions は、@aiFunction で decorate された Squid Service 内の method です。これらを AI agent にアタッチすると、agent は function の description と user prompt に基づいて会話中にそれらを呼び出せるようになります。
AI functions を使うタイミング
| Use Case | Recommendation |
|---|---|
| Agent が custom server-side logic を呼び出す必要がある | AI function |
| Agent が接続された database に query または write する必要がある | database connector を使用するか、custom query logic には AI function を使用 |
| Agent が接続された service(例: CRM、calendar、API)にアクセスする必要がある | connected integration を使用 |
| Agent が MCP 経由で external tools に接続する必要がある | MCP を使用 |
| Agent が upload された document を検索する必要がある | Knowledge Bases を使用 |
仕組み
SquidServiceを継承した class 内で method を@aiFunctionで decorate します- function を名前で agent にアタッチします(SDK または console の Agent Studio 経由)
- user が message を送ると、agent は prompt に対して function の description を評価します
- agent が function が relevant だと判断すると、AI が生成した parameter 値で function を呼び出します
- function は backend 上で実行され、string の result を返します
- agent はその result を response に組み込みます
Quick Start
Prerequisites
- TypeScript
- Python
Step 1: AI function を作成する
Squid Service に @aiFunction decorator を付けた method を追加します。
- TypeScript
- Python
import { SquidService, aiFunction } from '@squidcloud/backend';
export class AiService extends SquidService {
@aiFunction<{ city: string }>('指定された都市の現在の天気を返します。ユーザーが天気について尋ねたときに呼び出してください。', [{ name: 'city', description: '都市名', type: 'string', required: true }])
async getWeather(params: { city: string }): Promise<string> {
const { city } = params;
// Replace with your actual weather API call
return `The weather in ${city} is 72°F and sunny.`;
}
}
from squidcloud_backend import SquidService, ai_function
class AiService(SquidService):
@ai_function(
'指定された都市の現在の天気を返します。ユーザーが天気について尋ねたときに呼び出してください。',
[{'name': 'city', 'description': '都市名', 'type': 'string', 'required': True}],
)
async def get_weather(self, params: dict) -> str:
city = params['city']
# Replace with your actual weather API call
return f'The weather in {city} is 72°F and sunny.'
Step 2: backend を deploy する
local development では、次を実行します。
squid start
cloud に deploy する方法については、deploying your backend を参照してください。
deploy 後、この function は console の Agent Studio の Abilities に表示されます。
Step 3: function を使って agent を呼び出す
const response = await squid
.ai()
.agent('my-agent')
.ask("What's the weather in Tokyo?", {
functions: ['getWeather'],
});
console.log(response);
agent は user が天気について尋ねたことを認識し、{ city: "Tokyo" } を使って getWeather を呼び出し、その結果を response に含めます。
setAgentOptionInPath を使って function を agent に恒久的に追加することもできるため、毎回のリクエストで渡す必要はありません。
await squid.ai().agent('my-agent').setAgentOptionInPath('functions', ['getWeather']);
// Now the agent always has access to getWeather
const response = await squid.ai().agent('my-agent').ask("What's the weather in Tokyo?");
Core Concepts
@aiFunction decorator
この decorator は、必須の引数を 2 つ受け取ります。
description(string): agent にこの function をいつ呼び出すべきかを伝えます。曖昧な label ではなく、明確な instruction として書いてください。params(array): function を呼び出すときに agent が渡すべき parameter を定義します。
- TypeScript
- Python
@aiFunction<{ productId: string; quantity: number }>(
'商品の在庫数量を更新します。ユーザーが inventory を調整したいときに呼び出してください。',
[
{ name: 'productId', description: '商品 ID', type: 'string', required: true },
{ name: 'quantity', description: '加算する量(減算する場合は負の値)', type: 'number', required: true },
],
)
async updateStock(params: { productId: string; quantity: number }): Promise<string> {
// ...
}
@ai_function(
'商品の在庫数量を更新します。ユーザーが inventory を調整したいときに呼び出してください。',
[
{'name': 'productId', 'description': '商品 ID', 'type': 'string', 'required': True},
{'name': 'quantity', 'description': '加算する量(減算する場合は負の値)', 'type': 'number', 'required': True},
],
)
async def update_stock(self, params: dict) -> str:
...
または、decorator に単一の options object を渡すこともできます。
- TypeScript
- Python
@aiFunction({
description: '商品の在庫数量を更新します。',
params: [
{ name: 'productId', description: '商品 ID', type: 'string', required: true },
{ name: 'quantity', description: '加算する量(減算する場合は負の値)', type: 'number', required: true },
],
})
async updateStock(params: { productId: string; quantity: number }): Promise<string> {
// ...
}
@ai_function({
'description': '商品の在庫数量を更新します。',
'params': [
{'name': 'productId', 'description': '商品 ID', 'type': 'string', 'required': True},
{'name': 'quantity', 'description': '加算する量(減算する場合は負の値)', 'type': 'number', 'required': True},
],
})
async def update_stock(self, params: dict) -> str:
...
Parameter definitions
params array 内の各 parameter では、以下の field をサポートしています。
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | parameter 名。function の params object 内の key と一致する必要があります |
description | string | Yes | agent にどの値を渡すべきかを伝えます |
type | string | Yes | data type: 'string', 'number', 'boolean' など |
required | boolean | Yes | agent がこの値を必ず渡す必要があるかどうか |
enum | any[] | No | 値を許可された選択肢の集合に制限します |
有効な値が特定の選択肢に限られる場合は、enum を使って値を制約してください。
- TypeScript
- Python
@aiFunction('document 内の section を保存します', [
{
name: 'sectionId',
type: 'string',
description: 'document 内で更新する section',
required: true,
enum: ['introduction', 'background', 'methodology', 'results', 'conclusion'],
},
{
name: 'content',
type: 'string',
description: 'section の内容',
required: true,
},
])
async saveSection(params: { sectionId: string; content: string }): Promise<string> {
const { sectionId, content } = params;
const docRef = this.squid.collection('documents').doc('my-doc');
await docRef.update({ [sectionId]: content });
return 'Section saved: ' + sectionId;
}
@ai_function('document 内の section を保存します', [
{
'name': 'sectionId',
'type': 'string',
'description': 'document 内で更新する section',
'required': True,
'enum': ['introduction', 'background', 'methodology', 'results', 'conclusion'],
},
{
'name': 'content',
'type': 'string',
'description': 'section の内容',
'required': True,
},
])
async def save_section(self, params: dict) -> str:
section_id = params['sectionId']
content = params['content']
# The Python client does not currently expose a collection() helper, so
# this example performs the document write through your own data layer.
await self.write_section('my-doc', section_id, content)
return f'Section saved: {section_id}'
Return values
AI functions は、Promise<string>(TypeScript)または str(Python)を返す必要があります。agent はこの string を受け取り、それを使って response を組み立てます。明確で簡潔な result を返してください。
- TypeScript
- Python
// Good: returns useful information the agent can relay
return 'Order #1234 shipped on March 5. Tracking number: ABC123';
// Bad: returns raw JSON the agent has to interpret
return JSON.stringify(orderObject);
# Good: returns useful information the agent can relay
return 'Order #1234 shipped on March 5. Tracking number: ABC123'
# Bad: returns raw JSON the agent has to interpret
return json.dumps(order_object)
Attributes
Attributes を使うと、AI function を特定の connector type に紐付けることができ、その connector の built-in capabilities を独自の custom logic で拡張できます。その type の connector を agent に追加すると、connector の default behavior と一緒に function も自動的に含まれます。
たとえば、database connector だけでも agent は AI を使って data を query できます。しかし、agent に常に特定の方法で query させたい場合は、その connector type に attribute を持つ AI function を書くことができます。
- TypeScript
- Python
@aiFunction({
description: 'PostgreSQL database から customer の最近の注文を取得します',
params: [
{
name: 'customerEmail',
description: 'customer の email address',
type: 'string',
required: true,
},
],
attributes: {
integrationType: ['postgres'],
},
})
async getCustomerOrders(
{ customerEmail }: { customerEmail: string },
{ integrationId }: AiFunctionCallContextWithIntegration,
): Promise<string> {
// Custom query logic.
//
// Squid provides "Query with AI" where the agent can write the query and execute it to
// accomplish a task, but if you want it to consistently query in a certain way, you can write
// an AI Function that you can instruct it to call instead.
}
@ai_function({
'description': 'PostgreSQL database から customer の最近の注文を取得します',
'params': [
{
'name': 'customerEmail',
'description': 'customer の email address',
'type': 'string',
'required': True,
},
],
'attributes': {
'integrationType': ['postgres'],
},
})
async def get_customer_orders(self, params: dict, ctx: dict) -> str:
customer_email = params['customerEmail']
integration_id = ctx['integrationId']
# Custom query logic.
#
# Squid provides "Query with AI" where the agent can write the query and execute it
# to accomplish a task, but if you want it to consistently query in a certain way,
# write an AI function that you can instruct it to call instead.
...
PostgreSQL connector を agent に追加すると、この function は自動的に含まれます。
connector type に attribute された functions は、Agent Studio の AI Functions list には表示されません。agent への含め方は connector によって処理されます。
Categories
Categories を使うと、Agent Studio で AI functions をグループ化し、整理しやすくできます。
- TypeScript
- Python
@aiFunction({
description: 'Amazon の product listing から新しい user review をすべて取得します。',
params: [
{
name: 'productId',
description: 'Amazon 上の listing の ID。例: URL "https://www.amazon.com/dp/B094D3JGLT" に対する "B094D3JGLT"',
type: 'string',
required: true,
},
{
name: 'cutoffDate',
description: '締切日。この日付より新しい review のみを返します。ISO8601 format を使用してください。指定しない場合はすべての review を返します。',
type: 'string',
required: false,
},
],
categories: ['Data Gathering'],
})
async getProductReviews(
{ productId, cutoffDate }: { productId: string; cutoffDate?: string },
{ integrationId }: AiFunctionCallContextWithIntegration,
): Promise<string> {
// Your logic to gather the user reviews.
}
@ai_function({
'description': 'Amazon の product listing から新しい user review をすべて取得します。',
'params': [
{
'name': 'productId',
'description': 'Amazon 上の listing の ID。例: URL "https://www.amazon.com/dp/B094D3JGLT" に対する "B094D3JGLT"',
'type': 'string',
'required': True,
},
{
'name': 'cutoffDate',
'description': '締切日。この日付より新しい review のみを返します。ISO8601 format を使用してください。指定しない場合はすべての review を返します。',
'type': 'string',
'required': False,
},
],
'categories': ['Data Gathering'],
})
async def get_product_reviews(self, params: dict, ctx: dict) -> str:
product_id = params['productId']
cutoff_date = params.get('cutoffDate')
integration_id = ctx['integrationId']
# Your logic to gather the user reviews.
...
これにより、getProductReviews は AI Functions list 内で "Data Gathering" category の下に表示されます。複数の category を指定することもでき、その場合 function はそれぞれの category に表示されます。
Parameter 値の上書き
一部の agent では、AI に決めさせるのではなく、特定の parameter 値を固定したい場合があります。predefinedParameters を使うことで parameter を上書きできます。AI は上書きされた parameter の存在を認識せず、その値を設定することもできません。
たとえば、先ほどの saveSection function を使うと、introduction section だけを更新する agent を作成できます。
chat widget を使う場合:
<squid-chat-widget
...
squid-ai-agent-chat-options={{
functions: [{
name: 'saveSection',
predefinedParameters: { sectionId: 'introduction' }
}]
}}
...
>
</squid-chat-widget>
SDK を使う場合:
const result = await squid
.ai()
.agent('my-agent')
.ask('Save that content.', {
functions: [
{
name: 'saveSection',
predefinedParameters: { sectionId: 'introduction' },
},
],
});
agent が指定できるのは content parameter のみです。sectionId は常に 'introduction' になります。
AI functions に context を渡す
AI が生成した params に加えて、context object を使って独自の値を AI functions に渡すこともできます。これは、document ID、user preferences、security に関わる値のように、AI が制御すべきでない data に便利です。
context には 2 つの scope があります。
agentContext: agent が行うすべての AI function call に渡されます。ctx.agentContextからアクセスします。functionContext: 特定の function にのみ渡されます。ctx.functionContextからアクセスします。
Example
先ほどの saveSection function をさらに発展させてみましょう。document ID を agent level で設定し、保存前に content から internal codename を redact したいとします。
- TypeScript
- Python
import { SquidService, aiFunction, AiFunctionCallContext } from '@squidcloud/backend';
const SECTION_IDS = ['introduction', 'background', 'methodology', 'results', 'conclusion'] as const;
type SectionId = (typeof SECTION_IDS)[number];
interface DocumentIdAgentContext {
documentId: string;
}
interface RedactionFunctionContext {
codenameList: string[];
}
class DocumentService extends SquidService {
@aiFunction('document 内の section を保存します', [
{
name: 'sectionId',
type: 'string',
description: 'document 内で更新する section',
required: true,
enum: [...SECTION_IDS],
},
{
name: 'content',
type: 'string',
description: 'section の内容',
required: true,
},
])
async saveSection({ sectionId, content }: { sectionId: SectionId; content: string }, ctx: AiFunctionCallContext<RedactionFunctionContext, DocumentIdAgentContext>): Promise<string> {
const docRef = this.squid.collection('documents').doc(ctx.agentContext.documentId);
let censoredContent = content;
for (const censorWord of ctx.functionContext.codenameList) {
censoredContent = censoredContent.replaceAll(censorWord, 'REDACTED');
}
await docRef.update({ [sectionId]: censoredContent });
return 'Section saved: ' + sectionId;
}
}
from squidcloud_backend import SquidService, ai_function
SECTION_IDS = ['introduction', 'background', 'methodology', 'results', 'conclusion']
class DocumentService(SquidService):
@ai_function('document 内の section を保存します', [
{
'name': 'sectionId',
'type': 'string',
'description': 'document 内で更新する section',
'required': True,
'enum': SECTION_IDS,
},
{
'name': 'content',
'type': 'string',
'description': 'section の内容',
'required': True,
},
])
async def save_section(self, params: dict, ctx: dict) -> str:
section_id = params['sectionId']
content = params['content']
document_id = ctx['agentContext']['documentId']
codename_list = ctx['functionContext']['codenameList']
censored_content = content
for censor_word in codename_list:
censored_content = censored_content.replace(censor_word, 'REDACTED')
await self.write_section(document_id, section_id, censored_content)
return f'Section saved: {section_id}'
これらの context 値は、client 側から chat widget を通して渡せます。
<squid-chat-widget
...
squid-ai-agent-chat-options={{
agentContext: { documentId: "document_controlled_by_this_agent" },
functions: [{
name: 'saveSection',
context: { codenameList: ['LITANIA', 'LEOPARD'] }
}]
}}
...
>
</squid-chat-widget>
または SDK を使う場合:
const result = await squid
.ai()
.agent('my-agent')
.ask('I want the "results" section to be "LITANIA has determined the answer to be 42".', {
agentContext: { documentId: 'document_controlled_by_this_agent' },
functions: [
{
name: 'saveSection',
context: { codenameList: ['LITANIA', 'LEOPARD'] },
},
],
});
この場合、保存される content には "REDACTED has determined the answer to be 42" が含まれます。これは "LITANIA" が codename list に含まれているためです。
Error Handling
AI function が error を throw すると、agent はその error message を受け取り、それを user に伝えたり、次にどう進めるかを判断したりできます。明確で説明的な error を throw してください。
- TypeScript
- Python
@aiFunction<{ orderId: string }>(
'注文の status を調べます。',
[{ name: 'orderId', description: '注文 ID', type: 'string', required: true }],
)
async getOrderStatus(params: { orderId: string }): Promise<string> {
const { orderId } = params;
const order = await this.squid.collection('orders').doc(orderId).snapshot();
if (!order) {
throw new Error(`Order ${orderId} not found`);
}
return `Order ${orderId}: ${order.status}, shipped ${order.shippedDate}`;
}
@ai_function(
'注文の status を調べます。',
[{'name': 'orderId', 'description': '注文 ID', 'type': 'string', 'required': True}],
)
async def get_order_status(self, params: dict) -> str:
order_id = params['orderId']
order = await self.lookup_order(order_id)
if order is None:
raise RuntimeError(f'Order {order_id} not found')
return f"Order {order_id}: {order['status']}, shipped {order['shippedDate']}"
Common issues
| Issue | Cause | Solution |
|---|---|---|
| Function がまったく呼び出されない | description が user prompt と一致していない | いつ function を呼び出すべきかを明確に記述するよう description を書き直す |
| 間違った parameter 値が渡される | parameter description が曖昧 | より具体的な description を追加し、enum を使って値を制約する |
| Agent が function を見つけられない | functions option に function 名が渡されていない | ask call で function 名を渡すか、Agent Studio で追加する |
| Agent Studio に function が表示されない | function が cloud に deploy されていない | squid deploy で backend を deploy する |
Best Practices
明確な description を書く
description は、agent が function を正しく呼び出せるかどうかを左右する最も重要な要素です。agent にいつ使うべきかを正確に伝える instruction として書いてください。
Good: 'Returns the shipping status and tracking number for an order. Call when the user asks about order status, delivery, or tracking.'
Bad: 'Gets order info'
Parameter を慎重に設計する
- function がその値なしでは動作できない場合にのみ
required: trueを使う - 有効な値が決まっている場合は
enumを設定する - parameter description には、どの format を使うべきかが分かるように書く(例: "ISO8601 date", "email address")
Return value は情報が伝わるものにする
agent はあなたの return string を使って response を組み立てます。raw data structure ではなく、人が読める情報を返してください。
Input を validate する
AI が parameter 値を生成する場合でも、function 内で必ず validate してください。AI は予期しない値を生成することがあります。
- TypeScript
- Python
async updateQuantity(params: { quantity: number }): Promise<string> {
const { quantity } = params;
if (!Number.isFinite(quantity) || quantity < 0) {
throw new Error('Quantity must be a non-negative number');
}
// ...
return 'Quantity updated';
}
async def update_quantity(self, params: dict) -> str:
quantity = params['quantity']
if not isinstance(quantity, (int, float)) or quantity < 0:
raise ValueError('Quantity must be a non-negative number')
# ...
return 'Quantity updated'
Next Steps
- function のアタッチや agent の設定については AI agent documentation
- agent を external tool server に接続する方法については MCP
- AI functions を使った完全な example については AI home maintenance tutorial