Windows Live Ag... 的个人资料Windows Live Agents照片日志列表 工具 帮助

日志


4月23日

Microsoft Partner Program

Hello Developers,

 

As we are nearing the public release of our new Visual Studio SDK and Partner on-boarding system, we wanted to make sure that we expose our developers to all the benefits from Microsoft in terms of certifications.  We realize there are a large number of Agent developers that make their business through the Agent platform and we are working closely with the rest of Windows Live Platform to get you the recognition you need with your customers.  In the future, Windows Live will have a program specifically within the Microsoft Partner Program (MSPP) and you’ll be able to fully leverage that designation.  Membership in MSPP is free with graduated levels of certification (fee and skill based).   In the past, there was a notion of “MSN Bot Certified”, but because of business and technology reasons, this designation has essentially expired and won’t be recognized by Microsoft.  Windows Live is moving forward with MSPP as the official method of partnering and we encourage you to start MSPP enrollment at your earliest convenience. 

 

We look forward to building out this developer community with you!

4月17日

So, you think you know everything about Buddyscript?

So, you think you know everything about Buddyscript?

 

Well, let’s see how much of such a statement is true. Try to answer this short quiz! If you score all of them it doesn’t mean you are an expert, but you could say you are getting there ;-)

 

1-    Let’s talk about matching. Say we want to match precisely on full URLs (and only full URLs) during a conversation, which one of the following cases would be the way to go:

a)    subpattern AFullUrl /^www`.`S+`.(com|fr|org|net)$/ {minlength=3, example=”www.microsoft.com”}

b)    subpattern AFullUrl + www =Anything (com|fr|org|net) {canskip=”no”, minlength=3}

c)    subpattern AFullUrl www.=Anything.(com|fr|org|net) {canskip=”no” minlength=3}

 

2-    Nl-bricks? Only one of the following affirmations is true:

a)  “NL- bricks” along with “regular subpatterns” are processed during the “patternization” process, but “NL-bricks” have precedence over “regular subpatterns”.

b)  “NL- bricks” are the minimum expression of a meaning unit, and are “patternized” into “regular subpatterns” at compile time.

c)  “NL-bricks” are “skippable”, as opposed to “regular subpatterns” that need to have the property {canskip=”yes”}

 

3-    Defining macros in Buddyscript, only one of the following statements is true:

a)    If the code contains any line breaks, it should be placed immediately below the first line of the statement. If you want your macro to contain an empty line, add a line containing only the character “\”. The first empty line defines the end of the macro.

b)    If you want your macro to contain an empty line, add a line containing the character “\” at the end of the line before. I will add an empty line right between this and the following line. The first empty line defines the end of the macro.

c)    Macro definitions can’t contain line breaks. The first empty line defines the end of the macro.

 

4-    Only one of the following statements is true:

a)    The “always match as” statement allows you to associate a variable name with a particular subpattern throughout a group of patterns only.

b)    The “always match asstatement allows you to associate a variable name with a particular subpattern throughout a group of patterns, a package, or an entire domain.

c)    The “always match as” statement allows you to associate a variable name with a particular subpattern in a routine, or in a multi-line of code.

 

5-    What do you know about composite subpatterns? Only one of the following is true!

a)    Composite sub-patterns let you assemble multiple static subpatterns together, so that a candidate string is filtered through the first subpattern, then the result of this filtering is passed to the second one, etc.

b)    The minimum number of compositions is one, but there’s not a limit on the maximum number of compositions you can add.

c)    The default values for the properties of a composite subpattern are those of the outer-most subpattern, since it's going to be evaluated last.

 

6-    Hum, have you ever tried to create datasources? Let’s see if you know which one of the following is true:

a)    The “postprocess-block“ of a datasource is a BuddyScript routine used to set the offset and the total count of rows when the “preprocess-block” didn't return any of those values, or if the value returned is not accurate.

b)    “Expire” is a common property to the different datasource kinds. This information allows the platform to remember (cache) the result for a certain time. By default, expire is set to “now” (information is not stored). If you want the information to be cached, set it to “never”, or when the information won't be valid anymore (in 5 minutes, in 2 days, etc.)

c)    Datasources are BuddyScript tools to access external data. The external data retrieved can be accessed through many methods, including gateway calls, SQL queries, SOAP methods, datatables, etc.

 

7-    How about rephrasing rules? We want to use the property “MACRO_BEST_ONLY” when:

a)    We need to make sure the rule it’s only applied when it matches best from all of the other rephrasing rules.

b)    We want the rule to be applied only when it matches best, and no other rule can be applied after.

c)    We want to avoid matching on rules scoring lower than 80.

 

Solutions: 1-a, 2-b, 3-a, 4-b, 5-b, 6-c, 7-a

 If you failed the quiz, and you want to know more about Buddyscript, besides this blog you can browse the Windows Live Agents forum in MSDN, and of course read the documentation shipped with the platform.

4月7日

Quality Analysis Audits

Audits are an important piece to the ongoing maintenance of your agent. The purpose of auditing is to take a look at how users are interacting with the agent, and identify ways to improve the experience. Any issues that you uncover in audits should be addressed in a regularly scheduled agent update window.

There are two types of audits—Query and Session.

Session audits answer the question “Was this session satisfactory to the user?“ and are best for evaluating the overall user experience. Did the user find the information he or she was looking for? Was the answer complete? Did the user express any frustration that was not appropriately addressed? As a client, do you feel that the interaction served your purpose in deploying the agent?

Query audits answer the question “Was the query answered by the correct topic?” and are ideal for fine-tuning the Natural Language comprehension and finding commonly unanswered queries.

Once your agent launches, it’s preferred that you audit every 2-3 days for the first two weeks and then once every 2 weeks for the remainder of the agent’s life. Of course, it is up to you to determine how often you audit your agent. If your agent has a ton of traffic, you might want to audit more often.

If you choose not to audit your agent, the agent will never improve and you will supply users with a poor experience. Auditing your agent will not only improve it, but improve your development style and guidelines for future agents.

Auditing Guidelines

Session Audit

Suggestions for how to score sessions in a session audit:

  • Misunderstood: If the query is not recognized correctly and the user...
    • eventually gets the correct answer – Yes
    • would have gotten the right answer if he had continued another step (selected menu topic, typed more, etc) – Yes
    • doesn’t get the right answer  No
  • Content: If the query seemed to be understood reasonably well but the response...
    • clearly does not answer the question – Yes, but consider improving content
    • does not answer the question but includes a URL to a page that should logically answer the question – Yes, test the link, consider improving content if it does not
  • Menu: If a query is answered with an appropriate menu...
    • but should have been answered directly to a topic on the menu – Yes, notify WLA Team of misunderstood query
    • but is beyond the scope of the agent (too specific, technical)  Yes
    • but the user does not select any of the options – Yes
    • but the user does not select the correct option – Yes, consider  improving the menu if possible
  • Long/multiple: If the query is long or contains multiple questions and...
    • hits “catch” or gives the correct answer  Yes, bravo
    • user eventually gets the correct answer – Yes
    • user doesn’t eventually get the right answer – No
    • user gets the wrong answer and gives up – N/A
  • Dialogs: If a user engages agent in a dialog it cannot handle (responds to an answer with “I already tried that” or similar – Ignore that query
  • Ambiguity: If the user hit an ambiguity and...
    • the correct topic is listed, but the query was specific enough that it should have been answered directly – Yes, notify WLA Team
    • neither option is correct – No
    • at least one of the options is incorrect/irrelevant –  Yes, notify WLA Team
  • Entry message/empty session: If session only contains entry messages – N/A
  • Looping: If topics are looping – No
  • Nonsensical: When the user types nonsense, mark the query or session as N/A.             

Query Audit

* All internal query audits should use the option to "Only include queries relevant to natural language."

Suggestions for how to score queries in a query audit:

  • Non-NL Queries: Despite selecting the option* above, the auditor may still need to manually discard non-natural-language queries
    • Menu selections: a # (followed by line of NL-comprehension code in parentheses) – N/A
    • Any dialogue queries such as: more, yes, no, show related topics – N/A
    • “?” – N/A
  • Nonsensical: When the user types nonsense or question unrelated to products covered – N/A
  • Beyond the scope of the agent: When the user asks a very technical or specific question and
    • gets a menu or somewhat related answer – Yes
    • doesn’t get an approximate answer – N/A
  • Mismatched: Any reasonable query that
    • does not match an appropriate response in the agent – No
    • is known not to have an appropriate response in the agent and
      • doesn’t match anything at all relevant – No
      • matches something somewhat relevant – still Yes, but consider improving content
    • mismatched because the user tried to engage in a dialogue such as “I already tried that” – N/A
  • Menu: If a query matches to an appropriate menu but was specific enough to have matched directly to a topic– No
  • Long/multiple: If the query is long or contains multiple questions and...
    • gives the correct answer  Yes, bravo
    • matches reasonably well – Yes
    • hits “catch” – Yes
    • mismatches – N/A
  • Unanswered/Catch section: If the user makes a valid query (not nonsense) that will probably be asked again at some point – No
  • Ambiguity: test each query in this section
    • the correct topic is listed, but the query was specific enough that it should have matched directly – No
    • neither option was correct – No
    • at least one of the options is incorrect/irrelevant No

 

 

4月2日

Customizing WLATemplate

 

Ideally you start creating a new Agent using the WLATemplate! Detailed information on how to instantiate a new project [currently available languages are: English, Chinese (simplified), French, German, Italian, Japanese, and Spanish] from the WLATemplate: http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!446.entry

 

As you can imagine, this is only the beginning... In order to make your Agent a success and stand out in the crowd, you have to customize it to whatever you want your Agent to be, simple things like giving your Agent a personality and making "him" or "her" more natural. Here are a bunch of former blog posts that talk about exactly that:

 

Creating a Personality Spec - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!317.entry

 

Customizing Chat - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!230.entry

 

Welcome Messages and ABGreetingProc - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!408.entry

 

Changing PSM, Friendly Name, and Icon - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!315.entry

 

 

Best Practices for Developing a Windows Live Agent:

 

Part 1 [Use the WLATemplate Project as a Starting Point] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!149.entry

 

Part 2 [Create Good Subpatterns] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!150.entry

 

Part 3 [Use Dialogs] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!151.entry

 

Part 4 [Use Canonical Questions] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!152.entry

 

Part 5 [Remember User Information] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!156.entry

 

Part 6 [Create a Clear and Concise Welcome Message] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!160.entry

 

Part 7 [Be Careful When Using Public Variables] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!163.entry

 

Part 8 [Make Your Project Modular] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!164.entry

 

Part 9 [Make Your Agent Chatty] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!166.entry

 

Part 10 [Conclusion of Best Practices Series] - http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!176.entry

 

 

-----------------------------------------

Blog post contributor(s): Mirco

 

3月27日

Logging Topics and Categories

One of the most powerful parts of the BuddyScript platform is its extensible reporting suite.  This article will describe how to log data relating to what your agent is saying, in a form that can be displayed in the Usage Reporting tab of the web console.

 

Category and topic logging is something that has been used for agent projects developed "internally," but the code to do this was never exposed to partners in the libraries that ship with the platform.  This is likely to change in future releases, but in the meantime feel free to implement the code here.

 

A topicis a routine with user input, agent output.  For example, we may have a routine that associates matching for "how are you" to various outputs saying "I'm doing fine." This is a topic.

 

A category is a grouping of topics, often associated with a single DDL file. For example, we might have a category for all the topics that handle queries relating to the agent as a person, like "how are you", "where did you come from", etc.

 

When we associate a title to a topic, we can log that title and keep a tally of how many users hit that topic. When we associate a topic to a category, we can also tally how many times users hit that category. Thus, we will have data relating to where user queries are matching the most, at two levels of granularity.

 

To get this working, we need to:

 

1.    Define categories

2.    Assign topics to categories

3.    Log the topics and categories

 

Here is an example of how to do this:

 

Say we have a domain for English conversational routines, Topics/EN-US/Conversation.ddl.

 

At the top of the Topics/EN-US/Conversation.ddl, we add this:

 

{category="Conversation (English)" label="CategoryDefinition"}

+ _ignore_me

  do nothing

 

This defines a new category called “Conversation (English)”.  Next, we need to associate routines to this category.  Here is an existing routine:

 

{label="? Hi" uid="MyAgent-1195066867"}

? Hi

  - Hi! 

  - Hello there! 

  - Hi there.

  - Greetings!

  nop

  - How are you?

  - What's new with you?

  - How are things?

  insist

    - Good to know.<br/>

    - Interesting.<br/>

    - Glad I asked.<br/>

    - Really? <br/>

 

 

The key-value pairs between brackets ("label" and "uid") were automatically generated by the KMS as part of its tracking mechanism.  If they are not present, don't worry about it.

 

Add this (the order of key-value pairs doesn’t matter):

 

{category="Conversation (English)" title="Hi" label="? Hi" uid="MyAgent-1195066867"}

 

Still, we are just labeling the routine, and nothing will happen unless we log it.  The platform will automatically do some simple logging, but we should override that to use categories.  Look at this part of the libaries, in Required/ABProcs:

 

########################################

##

## ABPostQueryLog

##

## This procedure is called at the end of a user query

## and can be overriden to log specific information.

##

########################################

 

procedure ABPostQueryLog()

  SYS.Log.ScriptFile[SYS.History[0].Match.ScriptFile]++

 

processing ABPostQueryLog

   post-process

     call ABPostQueryLog()

 

So, if we just want to know what file is being hit, right now that is being logged as “ScriptFile”, which we could add to usage_config.xml and show in Usage Reporting in the console.  However, a better approach is to define categories for every ddl, and assign titles and categories for each topic in the ddl.  Then we can override the logging behavior like so:

 

### Logging

 

variable G_LAST_LOGGED_QR_RECEPTION_TIME=0

 

procedure LogCategory(CATEGORY)

  SYS.Log.Categories[CATEGORY]++

 

procedure LogTopic(TOPIC_TITLE)

  SYS.Log.Topics[TOPIC_TITLE]++

 

procedure PerformTopicLogging()

  if G_LAST_LOGGED_QR_RECEPTION_TIME == SYS.History[0].Execution.QRReceptionTime

    exit

  G_LAST_LOGGED_QR_RECEPTION_TIME = SYS.History[0].Execution.QRReceptionTime

  if SYS.History[0].Match.RoutineId == ""

    exit

  TOPIC_TO_LOG = ""

  CATEGORY_TO_LOG = ""

  ROUTINE_ID = SYS.History[0].Match.RoutineId 

  if !ROUTINE_ID

    exit

  TOPIC_KEYS = GetRoutineKeys(ROUTINE_ID)

  if Exist(TOPIC_KEYS["menu"])

    exit

  if !Exist(TOPIC_KEYS["title"])

    exit

  TOPIC_TO_LOG = TOPIC_KEYS["title"]

  if !Exist(TOPIC_KEYS["category"])

    CATEGORY_TO_LOG = "undefined"

  else

    CATEGORY_TO_LOG = TOPIC_KEYS["category"]

  call LogTopic(TOPIC_TO_LOG)

  call LogCategory(CATEGORY_TO_LOG)

 

procedure overrides ABPostQueryLog()

  call PerformTopicLogging()

 

Note that all we are really doing is piggy-backing on the existing logging mechanism, using it to write the values associated with the “title” and “category” keys for each routine the agent matches to.  These values are written to the SYS.Log variable, a special object used for logging.  Anything written to SYS.Log is fair game to show in the reporting console, but to do so, we also need to make alterations to the usage_config.xml file, to accommodate SYS.Log.Categories and SYS.Log.Topics and specify how the data should appear.  Editing the usage_config.xml file is beyond the scope of the article and will be covered later, so let the WLA team know if you need help setting it up.

3月21日

Data Consumption in Buddyscript

An important component in creating a good agent is to access and display rich content.  Often, this content comes from sources such as data feeds, flat files, SQL databases, or SOAP/RES methods.

 

There are two common components in Buddyscript that is needed to access, store and retrieve data.  They are datatables and datasources.

 

Datatables are two-dimensional arrays of data that is preloaded into memory.  For those of you familiar with SQL databases, these are tables that are pinned in memory.  The common sources where datatables are loaded from are from a hard coded list, tab-delimited flat files, SQL tables, or an external source that can return back an array of data.

 

The datatable statement looks like this:

 

datatable tablename [propertyoptions]

  load column1 [, column2...] from source

    retrievalmethod

 

For example, to load from a hard-coded list, the statement might look something like this:

 

datatable MovieGenreTable

  load GenreCode {index="exact"}, GenreName {index="exact"} from

    1, Action

    2, Adventure

    3, Comedy

 

This loads a datatable called MovieGenreTable with 3 rows of two columns, GenreCode and GenreName.  Both columns are indexed, using an 'exact' index.  From the BuddyScript documentation, the types of indexes you can use are as follows:

 

exact: the two entries need to be exactly equal. => "exact" = "exact" but "Exact" != "exact"

case-insensitive: the two entries can have different case => "exact" = "eXaCt" but "exact" != "exact?"

thawed: the "thawed" version of the two entries need to be equal => "that's exact" = "$$ThaT %%

S&&ExAcT!!", "François" = "fRAncois". This is because the thawed version of both "That's exact!" and of "$$ThaT %% S&&ExAcT??" is "that s exact".

 

It's slightly faster to access a column indexed as exact, than a column indexed as case-insensitive. thawed indexed columns are the slowest.

 

To load from a tab-delimited file would look like this:

 

datatable CitiesMetaTiered

  load value*, score from file

    Data/CitiesMetaTiered.txt

 

This loads a datatable called CitiesMetaTiered into a two column table, the columns being value and score.  The file has two columns separated by a tab.  The table never expires from memory. The asterisk after the column (as in value*) is a shortcut for an index, with the property of exact.

 

 

Accessing the datatable

 

The way to access the datatable is as follows:

 

list of variables = get column1, [column2, ...] in datatableName [where

columnX is value [and columnY is value2...]] [limit startIndex, count]

 

Note that this syntax looks fairly similar to the SQL SELECT statement.

 

For example, using the above datatable definitions, to retrieve data from the datatable called CitiesMetaTiered, a typical statement might look like this:

 

SEARCHVAR = 'x'

V, S = get value, score in CitiesMetaTiered where value is SEARCHVAR limit 1

 

This will put into the variables V and S the value and score content in the CitiesMetaTiered datatable where the value is 'x'.  It will put into the variable the first row that it finds.

 

 

Datasources

 

Datasources are a way to define how data looks when retrieved from an external source. The most useful and common ways to use a datasource is to define a web data feed page, a datatable, or a SQL data table.

 

For example, to define a web datafeed page, an example might look like this:

 

datasource dsProductRecall() => ititle, idescription, ipubDate, ilink

  http

    http://www.cpsc.gov/cpscpub/prerel/prerelhousehold.xml

  simple xml

    channel

//      title

//      description

//      link

//      language

//      lastBuildDate

//      webMaster

      item {loop="content"}

        title

        description

        pubDate

        link

//      guid

 

 

This looks at an XML feed called http://www.cpsc.gov/cpscpub/prerel/prerelhousehold.xml

 

The XML has master information and item repeating information.  The datasource captures node information into the output variables.  The output variables are strictly positional based on what is captured from the feed.

 

The indentation in the datasource definition has to match the same parent-child level of the web page.  The  {loop="content"} indicates that the variable and all the children variables will repeat and the content put into an array.  The // are comment statements, in this particular case, some variables are commented out because the code has no use for the particular data.

 

In some instances, you would use a datasource instead of a datatable.  For example, if you only need part of a table in memory at any one time, it is much better to use a datasource than a datatable.

 

Here is an example of a datasource that loads from a datatable.

 

datasource DatGetAllCityInfo(ZIP) => city, state, region, country

   table

     get

       city, state, region, country

     in

       CombinedGeography

     where

       id is ZIP

 

This datasource takes in a parameter called ZIP, and will output 4 columns, city, state, region, and country.  It gets its output from a datatable, getting city, state, region, country from the datatable called CombinedGeography, and filters for those values in the id column where it is equal to the passed-in value called ZIP.

 

You can then access a datasource just like a function.  For example, using basic Buddyscript syntax:

 

? What products have been recalled lately?

TITLE, DESCRIPTION, PUBDATE, LINK = dsProductRecall()  show 5

     *  Here are the results for product recalls :

     -  TITLE, DESCRIPTION, PUBDATE, LINK

 

you can also access the datasource function with the OBJ <= datasourcename() statement.  This puts the data into an object. When there are multiple rows of data you can indent a block below the datasource call and the block will be looped on.  This will give you better display control over each line.

 

 

By Carl Moy

3月3日

Tracking ClickThroughs

Many clients want to see how much traffic their agent is pushing back to their client’s website. By integrating ClickThrough tracking in your agent, you will be able to track Clicks and Impressions. Impressions are how many times a URL is presented to the user. Clicks are how many times a URL is clicked on by the user.

 

In order to track ClickThroughs in your project, we must wrap a redirect URL around the real URL. You will need to contact the Agents team to set up a redirect URL for you. Please note that your Agents contact does not personally set up the redirect URL—they must send a request to the Hosting Department.  Redirect URL requests can take several days.

The Project Manager will send you the redirect URL and you will add the following to your project:

1. Include the following package in a central file, such as YourProject/Shared/YourProject.pkg. This package includes all of the procedures and functions needed to track clicks and impressions in your agent.

 

                lib:/Shared/Utilities/ClickThroughUtilities

 

2. Override the following function in the same file as above and return the redirect URL your PM sends you.

     function overrides GetClickThroughBaseUrl()

       return "http://zz.rdir.us"

 

When you want to track a URL in your project, you will use the following function:

Click ClickThroughLink(“URL”, "here", " ---> ", "TEXT")

URL will be replaced with the URL you want to track. TEXT will be what you want displayed in the Usage Reports. For example, if you are tracking the URL http://movies.msn.com/, you will include the following code:

Click ClickThroughLink(“http://movies.msn.com/”, "here", " ---> ", "MSN Movies")

In the agent output, it will display like so:

                Click here ---> http://zz.rdir.us/ct?K/0

 The Agents team will set up the Usage Reporting site so you can view ClickThrough tracking. There will be a high level graph, displaying Total Clicks, Impressions, etc.:

 

  ClickThroughBlog

There will also be separate sections for Impression and Click breakdowns. For example:

 

 

ClickThroughBlog2

2月22日

How to get your Agent to speak another language than English

 
Our blog posts to date have been talking about tips and tricks to develop better, more unique and more reliable Windows Live Agents...in English. So, let's find out what other languages are available.
 
First of all you have to instantiate a new Agent using StartInstantiation.bat. This script will generate an Agent that includes all the languages we currently support (see below for a list of available languages) out of the box. With our 5.0 release, this will be replaced by a new project wizard! All you have to do now, is to adjust your Agent's DLS (Domain List File) so you're including the right DDLs (Domain Definition File) for the language of your Agent. Some adjustments in the Agent's BFG (Runtime Configuration) are necessary as well. Detailed step-by-step instructions on how to instantiate an Agent that only speaks language are to find below (#2).
 
 
1. What languages are currently supported
 
The latest SDK Beta ships with agent libraries in the following languages:
 
    • Chinese (simplified)
    • English
    • French
    • German
    • Italian
    • Japanese
    • Spanish
 
Coming soon:
 
    • Dutch
    • Portuguese
 
The library structure for each language is the same:
 

Language

·          Core

o    Rephrase Rules
This is a very powerful feature which predicts sentence variations based on syntactic structure, semantic context, and other linguistic and pragmatic cues.
e.g. "Can you please help me?" => "Can you help me?" => "Help me!"

o    Lexicon & Vocabulary
Includes lexicon files, i.e. for recognizing adjectives, nouns, verbs, etc. Also includes vocabulary files where most commonly used vocabulary is defined in categories, e.g. VocGeneral_xx, VocChat_xx, VocInternet_xx, etc. (_xx = language acronym) Definitions for synonyms are declared in vocabulary files as well.

o    Dates, Numbers, etc.

Handles recognition and display of strings for date, time, numbers, etc.

·         Extensions

o    Chat
Categories covering different chat topics such as Salutations_xx, Manners_xx, Comedy_xx, etc.

o    User Info
Handling different aspects of user's information such as birthday, gender, name.

o    Etc.

·         Utilities

A variety of different utilities to take advantage of. Scope of these utils is to make your Agent more unique, smarter, and to add features such as the activity window.

o    Math, Randomness...

o    Activity Window

o    Output processing & formatting...

o    Etc.

 
 
2. Step-by-step to instantiate an Agent that speaks only German
 
Again, with our upcoming 5.0 release, these steps will be replaced by a new project wizard!
 

·          Run the script StartInstantiation.bat and follow its instructions. We'll assume you instantiated a project named YourProjectName. If you installed the SDK in the default directory, you can find it in "C:\Program Files\Colloquis\Colloquis SDK\Projects\WLATemplate\StartInstantiation.bat"

·          Open YourProjectName.dls

o    Remove sections for languages you don't need.

o    Replace references to English files to reference your language.

o    See #3 for an example for a DLS for a German speaking Agent.

·          Open YourProjectName.bfg and remove the sections for languages you don't need.

·          Delete the folders (languages) you don't need

·          Open Project in SDK, start compilation, and type "Hallo!"

 
Automated Agent: Hallo joeuser, dies ist eine erste Nachricht der WLA Schablone. Mein/e Entwickler/in kann mich in /German/Overrides.ddl anpassen.
Hier kann er/sie ein anfängliches Interview implementieren und Ihnen Fragen stellen.
 
Sie können außerdem Beispielfragen anzeigen:

Wir können eine Menge verschiedener Dinge machen! Ich verstehe die verschiedensten Fragen, z.B.:
 
* Zufällige Frage 1
* Zufällige Frage 2
* Zufällige Frage 3
* Zufällige Frage 4
 
Womit kann ich Ihnen weiterhelfen, joeuser?

joeuser: Hallo!
Automated Agent: Hallo joeuser.
 

·         Read README.txt to customize your project.

 

3. YourProjectName.dls - Example
 
<domain-list import="/Shared/SharedSettings.dls">
    <deployments>
        <deployment id="dev" />
        <deployment id="Deploy" />
        <deployment id="DeployKMS" />
    </deployments>
    <file-sets>
        <file-set id="SharedRephrasings">
            <domain>
                lib:/Shared/Core/Rephrasing/*.ddl
            </domain>
        </file-set>
        <file-set id="SharedFiles">
            <domain>
                /Variables.ddl
            </domain>
            <domain>
                /Shared/Overrides.ddl
            </domain>
            <domain>
                /Shared/Personality.ddl
            </domain>
            <domain only-in="KnowledgeManagement">
                /Shared/Tests.ddl
            </domain>
            <domain>
                /Shared/Chat/*.ddl
            </domain>
            <domain only-in="KnowledgeManagement">
                lib:/Shared/Core/Vocabulary/VocabularyAnalysis.ddl
            </domain>
            <domain only-in="KnowledgeManagement">
                lib:/Shared/Utilities/TestUtilities.ddl
            </domain>
        </file-set>
        <file-set id="German">
            <domain>
                /German/Chat/*.ddl
            </domain>
            <domain>
                /German/UserInfo/*.ddl
            </domain>
        </file-set>
        <file-set id="KnowledgeManagementFiles">
            <file-set-ref id="German" />
            <file-set-ref id="SharedFiles" />
        </file-set>
    </file-sets>
    <language-sets>
        <language-set id="German">
            <rephrasings>
                <file-set-ref id="SharedRephrasings" />
                <domain>
                    lib:/German/Core/Rephrasing/*.ddl
                </domain>
                <domain>
                    /German/CustomRephrasings_de.ddl
                </domain>
            </rephrasings>
        </language-set>
    </language-sets>
    <buddy id="AutomatedAgent" name="Automated Agent">
        <file-set-ref id="KnowledgeManagementFiles" />
        <domain>
            /German/Main.ddl
        </domain>
        <domain>
            /German/Overrides.ddl
        </domain>
        <domain only-in="KnowledgeManagement">
            /German/Tests_de.ddl
        </domain>
    </buddy>
    <buddy id="KnowledgeEditor" name="Knowledge Editor">
        <file-set-ref id="KnowledgeManagementFiles" />
        <domain>
            /German/Main.ddl
        </domain>
        <domain>
            /German/Overrides.ddl
        </domain>
        <domain only-in="KnowledgeManagement">
            /German/Tests_de.ddl
        </domain>
    </buddy>
    <buddy id="YourProjectName_de" name="Windows Live Agent Template - German">
        <file-set-ref id="German" />
        <file-set-ref id="SharedFiles" />
        <domain>
            /German/Main.ddl
        </domain>
        <domain>
            /German/Overrides.ddl
        </domain>
        <domain only-in="KnowledgeManagement">
            /German/Tests_de.ddl
        </domain>
    </buddy>
</domain-list>
 

4. Some example Windows Live Agents to talk to in other languages than English
 
-----------------------------------------
Blog post contributor(s): Mirco

 
2月21日

Integrating Buddyscript and Web Services Part I

 This is part I of a two-part blog entry.

Introduction

This posting will go over various methods of inserting a Web Service call in Buddyscript code as a datasource. We will assume that you already have a working Web Service and Buddyscript project. For my example, I have a Web Service called Service1 running on my local machine.

Background Information

Our HelloWorld Function

Our very basic HelloWorld Web Service function simply takes an input parameter and outputs “you said <input>”.

 

Start Your Web Service

Before you begin, you must start your Web Service. Note the web address. My web service is running at http://localhost:54908/Service1.asmx.

GET Web Service Call

Step 1: Determine Web Service Call Format

To send requests to, and receive responses from, your Web Service, you need to figure out how to call it. We did this by going to our Web Service’s URL in a web browser. For the GET method, the Web Service XML code looks like this:

The following is a sample HTTP GET request and response. The placeholders shown need to be replaced with actual values.

Request:

GET /Service1.asmx/HelloWorld?input=string HTTP/1.1

Host: localhost

 

Response:

HTTP/1.1 200 OK

Content-Type: text/xml; charset=utf-8

Content-Length: length

 

<?xml version="1.0" encoding="utf-8"?>

<string xmlns=”http://tempuri.org/”>string</string>

 

Step 2: Create the Datasource in Buddyscript

The GET method is the easiest one to use in Buddyscript, and therefore is the recommended method of doing Web Service calls. To create the datasource that will call the HelloWorld function, the Buddyscript code looks like this:

// GET DataSource HelloWorld

datasource HelloWorldGet(INPUT) => HelloWorldResult

  http

    http://localhost:54908/Service1.asmx/HelloWorld?input=INPUT

  simple xml

    string

The result of this function is the string returned by the HelloWorld Web Service function call.

 

Step 3: Use the Datasource

To use the datasource we just created, we can do something like this:

? HelloWorldGet INPUT=Anything

  RESPONSE = HelloWorldGet(INPUT)

  - GET Web Service Response: "RESPONSE".

So if you type the query:

HelloWorldGet get test

The Agent would respond with:

GET Web Service Response: “you said get test”.

POST Web Service Call

Step 1: Determine Web Service Call Format

Once again, we went to the URL of our Web Service in a web browser to figure out the format of a POST call. The XML looks like this:

The following is a sample HTTP POST request and response. The placeholders shown need to be replaced with actual values.

Request:

POST /Service1.asmx/HelloWorld HTTP/1.1

Host: localhost

Content-Type: application/x-www-form-urlencoded

Content-Length: length

 

input=string

 

Response:

HTTP/1.1 200 OK

Content-Type: text/xml; charset=utf-8

Content-Length: length

 

<?xml version="1.0" encoding="utf-8"?>

<string xmlns="http://tempuri.org/">string</string>

 

Step 2: Create the Datasource in Buddyscript

Using the POST method to call a web service is not much more difficult than using a GET. To create the datasource, the Buddyscript code looks like this:

// POST DataSource HelloWorld

datasource HelloWorldPost(INPUT) => HelloWorldResult

  http

    http://localhost:54908/Service1.asmx/HelloWorld

    postdata

      input=INPUT

  simple xml

    string

Once again, the result of this function is the string returned by the HelloWorld Web Service function call.

 

Step 3: Use the Datasource

Using the POST HelloWorld datasource we just created is done in exactly the same way as using the GET HelloWorld datasource. So for the following routine:

? HelloWorldPost INPUT=Anything

  RESPONSE = HelloWorldPost(INPUT)

  - POST Web Service Response: "RESPONSE".

If we type the query:

HelloWorldPost post test

The Agent would respond with:

POST Web Service Response: “you said post test”.

For the Integrating Buddyscript and Web Services Part II, please click here.

Integrating Buddyscript and Web Services Part II

This is a continuation of Integrating Buddyscript and Web Services Part I.

SOAP Web Service Call

Step 1: Determine Web Service Call Format

By going to our Web Service’s URL in a web browser, we can see that the Web Service XML code for a SOAP call looks like this:

The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.

Request:

POST /Service1.asmx HTTP/1.1

Host: localhost

Content-Type: text/xml; charset=utf-8

Content-Length: length

SOAPAction: "http://tempuri.org/HelloWorld"

 

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

  <soap:Body>

    <HelloWorld xmlns="http://tempuri.org/">

      <input>string</input>

    </HelloWorld>

  </soap:Body>

</soap:Envelope>

 

Response:

HTTP/1.1 200 OK

Content-Type: text/xml; charset=utf-8

Content-Length: length

 

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

  <soap:Body>

    <HelloWorldResponse xmlns="http://tempuri.org/">

      <HelloWorldResult>string</HelloWorldResult>

    </HelloWorldResponse>

  </soap:Body>

</soap:Envelope>

 

 

Step 2: Create the Datasource in Buddyscript

Using the SOAP method for calling the Web Service is the most restricted and least recommended. However, if you would like to use SOAP, the following is an example how to do so:

// SOAP DataSource HelloWorld

datasource HelloWorldSoap(INPUT) => HelloWorldResult

  soap

    proxy     http://localhost:54908/Service1.asmx

    name      HelloWorld

    namespace http://tempuri.org/

    action    http://tempuri.org/HelloWorld

    input

      string  input  =  INPUT

  simple xml

    HelloWorldResult

The datasource will return the output of the HelloWorld Web Service function call.

 

Step 2.1 Manually Create the Datasource in Buddyscript

Instead of using the method in Step 2 above, you can also manually create the Web Service call using Buddyscript. This is the longest and most difficult (and tedious) way to query the Web Service. To do this, we first have to build the request. The code to do that looks like this:

// Build HelloWorld XML Query

function BuildGatewayAPIPostData_HelloWorld(INPUT)

  POST_DATA =                         '<?xml version="1.0" encoding="UTF-8" ?>\n'

  POST_DATA = StringConcat(POST_DATA, '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">')

  POST_DATA = StringConcat(POST_DATA, ' <soap:Body>')

  POST_DATA = StringConcat(POST_DATA, '  <HelloWorld xmlns="http://tempuri.org/">')

  POST_DATA = StringConcat(POST_DATA, '   <input>',INPUT,'</input>')

  POST_DATA = StringConcat(POST_DATA, '  </HelloWorld>')

  POST_DATA = StringConcat(POST_DATA, ' </soap:Body>')

  POST_DATA = StringConcat(POST_DATA, '</soap:Envelope>')

  return POST_DATA

You can see how each line of code adds a line of the Request XML onto the POST_DATA string.

Now that we can build a request, we need to be able to use it. The following datasource will send the request to the Web Service and receive and process the response:

// Call HelloWorld Web Service and get response

datasource HelloWorldSoap(INPUT) => HelloWorldResult {expire="now"}

  preprocess

    // Build the Request string

    POST_DATA = BuildGatewayAPIPostData_HelloWorld(INPUT)

  http

    http://localhost:54908/Service1.asmx

    header

      Host: localhost

      Content-Type: text/xml; charset=utf-8

      SOAPAction:  "http://tempuri.org/HelloWorld"

    postdata {encode=no}

      POST_DATA

  simple xml

    Envelope

      Body

       HelloWorldResponse

         HelloWorldResult

The preprocess section is used to set variables that will be used in the following sections.

The http section describes the source. The source can be one of the following: abgw, absql, table, file, http, soap, or Buddyscript. The section format varies depending on the source type.

The simple xml section is called the filter. It describes the filtering to be performed on the incoming data in order to build a table to return. The filter can be abgwtab, abgwxml, simple xml, xslt, or Buddyscript. The content of this section depends on the filter type.

For our particular datasource, the URL under http is the URL of our Web Service. The data in the header section comes from the Web Service web page that we have open (see Step 1). The simple xml section lists the tags in the Response hierarchy (see Response XML code in Step 1).

The result, stored in <HelloWorldResult>, will be returned.

 

Step 3: Use the datasource

Once again, we use the SOAP datasource in the same way as the GET and POST datasources. Here is a sample routine that can be used with either method used above for querying the Web Service:

? HelloWorldSoap INPUT=Anything

  RESPONSE = HelloWorldSoap(INPUT)

- SOAP Web Service Response: "RESPONSE"

The query:

HelloWorldSoap soap test

Would return:

SOAP Web Service Response:  “you said soap test”

Conclusion

The ability to call Web Services from your Buddyscript project is a very powerful tool. Following these steps will allow you to quickly and easily set up your Web Service calls using a variety of different methods, and will afford you much greater flexibility in your Agents.

2月11日

Using Activity Window feature in WL Messenger to make your Agents stand out

1.       What is an Activity Window (AW) and how can it be used to enhance Agents?

The Activity Window is an area in the Messenger Client that allows Windows Live Agents developers to provide rich content to the user to make the Agents experience much more engaging than simply conversing through the conversation window. This can also greatly increase the interaction between the user and agent.

WLMessengerWindows

Activity Windows can be used to enhance Agents by providing rich content such as Animation, Games, Videos, Charts, Tables, etc… that are not possible to display in the Conversation Window. Activity Windows is also able to accept input (just like any web page can) through mouse or keyboard, and send this data to the Agent, which can respond in turn.

Note: AWs are a feature of Windows Live Messenger and has a lot more functionality to offer that are unrelated to Windows Live Agents. For more information about Activity Windows, please follow this link:  http://msdn2.microsoft.com/en-us/library/aa751014.aspx Activity Windows are a feature of Windows Live Messenger. This document only addresses how to use AW in the context of Windows Live Agents. For more information about Activity Windows, please follow this link:  http://msdn2.microsoft.com/en-us/library/aa751014.aspx

 

2.       What you need to implement  AWs for Windows Live Agents:

a.       Basic knowledge of the following technologies:

                                                               i.      HyperText Markup Language (HTML)

                                                             ii.      BuddyScript (scripting language used to add logic to Agents)

                                                            iii.      Javascript (JS) *

                                                           iv.      eXtensible Markup Language (XML) *

b.      Familiarity with the Messenger Activity API Process.

c.       A hosting site to host your Activity Window pages and JS code.

d.      An application provisioned for your specific Activity Window by Messenger. (please see section 7 for more information on this)

e.      Last but not the least, you need the user’s consent to display the AW. (The end  user needs to Accept the invitation from your agent to start an Activity Window).

* These are needed only if using the Data Interchange method of interacting with the AW. Currently this method is only available to internal Agents developers but should be available to external Agents developers in a future release.

 

3.       Basic Idea of how User à Agentà AW interaction works:

a.       First, anapplication ID is provisioned by Microsoft points to your specific Agent’s AW. This enables Windows Live Messenger to know which Activity it needs to call when the user accepts the invitation.

b.      When the user asks a question for which the Agent has an answer that can be displayed in the AW, the Agent will send the output data to the AW application (hosted in the hosting site that you provide) instead of the conversation window.

c.       Once the AW application receives the data, the code in the AW uses the data to display the content requested.

d.      In certain cases, it is also possible for the AW code to send data back to the Agent.                                                          BasicIdeaofUserToAgentToAWInteraction

 

4.       AW Implementations used in Agents:

 There are several ways to achieve the functionality shown in the above diagram. The ones used by WL Agents team so far include the following:

a.       Page Drive

 Samples of Agents using this method include Encarta Instant Answers agent and MSN Music agent. 

UsingPageDriveMethodToDisplayPageInAW

b.       Data Interchange using Javascript Object Notation (JSON)  and XML*

       Samples of Agents using this method include College Football Guru agent.

                UsingDataInterchangeMethodToSendDataToAW

               *As of this time, this method is still in Beta and available only to internal Agents developers. We expect to make this available to external Agents developers in a future release.

5.       The Page Drive method

Taking the example from MSN Music agent, it uses the AW to display Artist information when the user asks for it.

BuddyScript code is used to match on the user input, and if the user input is understood to be a request for Artist info, we search our list of Artist info.

Sample Code:

subpattern AnArtist

  <Your subpattern of an artist here>

 

? What do you know about ARTIST=AnArtist?

    ARTIST_ID = ArtistIDFromArtistName(ARTIST)

    If ARTIST_ID < 0

         - Sorry, I don’t have info about ARTIST :-(

         exit all

    -    Let me take you to the MSN Music page about ARTIST =>

   call PageDrive(StringConcat(“http://music.msn.com/artist/?artist=, ARTIST_ID, “#”))

                                                   BuddyScript code that calls the Page Drive procedure

In the above sample code, this is the handle that is executed when someone asks a question about an artist. The code will get an Artist ID and then call the PageDrive procedure which simply takes the URL string, starts up the AW process, and (assuming the user accepted the AW invitation), displays the page indicated by the URL in the AW.

The PageDrive  procedure is part of the WL Agents Activity Window Utilities API. This main package for handling AW functions in Agents is called WLMActivityUtilities.pkg and can be found under Modules\Shared\Utilities directory.

On the AW side, there is also JavaScript code that handles the request. Once the AW receives the request, the following JS code (embedded in an HTML file) is triggered.

Sample JS Code:

                                                 function Channel_OnDataReceived() {

                                                                var myData=window.external.Channel.Data;

                                                                DisplayDebugInfo("received data: " + myData);

                                                                if (myData=="READY") {

                                                                                Channel_OnRemoteAppLoaded();

                                                                                return;

                                                                }                                                                              

                                                                // Input sent by the BuddyScript code follows the format:

                                                                // param1=value1\nparam2=value2 ...

                                                                var myDataArray=myData.split("\n");

                                                                var destinationURL="";

                                                                for (var i=0; i < myDataArray.length; i++) {

                                                                                if (myDataArray[i].indexOf("url=")==0) {

                                                                                                destinationURL = myDataArray[i].substring(4);

                                                                                } else if (myDataArray[i].indexOf("debug=")==0) {

                                                                                                debugInfo = myDataArray[i].substring(6) * 1;             // * 1 to make sure we have a number

                                                                                }

                                                                }

                                                                                if (destinationURL!="") {

                                                                                // Go to the new location

                                                                                DisplayDebugInfo("URL=" + destinationURL);

                                                                                top.mainFrame.location.href = destinationURL;

                                                                }

                                                }

      Snippet of Javascript code that handles data from the Agent’s PageDrive procedure

To learn more about the different JS functions provided by the Window Live Messenger Activity API, please see their documentation.

 

6.       The Data Interchange method (a Sneak Peek)

Oftentimes it is not sufficient and certainly less interesting to simply use the AW to page drive to a URL. After all, AWs should be engaging and should provide rich content and functionality in order to increase user interaction time with your agent.

Windows Live Agents provides another way to implement AW in your Agent that allows for more complex AW displays. This is accomplished through data interchange using Javascript Object Notation (JSON) (please see http://www.json.org for more info) and eXtensible Markup Language (XML).

In a nutshell, here is how this works:

DiagramOfDataFlowFromAgentToAW

          DiagramOfDataFlowFromAWToAgent

        Note: The Data Interchange method is currently not available for use as of this time by external Agents Developers because it is still in Beta. More detailed information will be posted on the Windows Live Agents Team blog as soon as this feature becomes available for external Agents developers.  

 

7.       All about the AW Application ID

The AW Application ID allows the MSN Messenger Client to determine which Activity it should load when the user accepts your Agent’s invitation to start an Activity Window session. This application ID can be obtained by contacting the Window Live Gallery team at http://gallery.live.com and submit a request.  Please click here to read a post for more information regarding this process.

The msgrp2p.xml file contains information that the Messenger client uses to load your AW application. It looks like this:

<?xml version="1.0"?>

 <Entry>

  <EntryID>99999999</EntryID> 

  <Error />

  <Locale>en-us</Locale>

  <Kids>1</Kids>

  <Page>1</Page>

  <Category>50</Category>

  <Sequence>10</Sequence>

  <Name>Windows Live Agents Sample Activity</Name>

  <Description>Windows Live Agents   SDK Activity Description</Description>

  <URL>http://localhost/activity.html</URL>

  <IconURL />

  <PassportSiteID>0</PassportSiteID>

  <Type>App</Type>

  <Height>498</Height>

  <Width>498</Width>

  <Location>side</Location>

  <MinUsers>2</MinUsers>

  <MaxUsers>2</MaxUsers>

  <PassportSiteID>0</PassportSiteID>

  <EnableIP>False</EnableIP>

  <ActiveX>False</ActiveX>

  <SendFile>False</SendFile>

  <SendIM>False</SendIM>

  <ReceiveIM>True</ReceiveIM>

  <ReplaceIM>False</ReplaceIM>

  <Windows>True</Windows>

  <MaxPacketRate>120</MaxPacketRate>

  <UserProperties>False</UserProperties> 

  <ClientVersion>6.0</ClientVersion>

  <AppType>0</AppType> 

  <Hidden>false</Hidden>

</Entry>

The elements to take note here are the ones bolded and displayed in red. The P4 Application ID that is provided to you by the Messenger team goes into the EntryID. The Name should be the name of the Activity that you are developing, and will appear in the Window Title of the AW. The Description is a description of the Activity, and the URL element specifies where the Messenger client will take the user once he accepts the invitation to start an AW.

Shown below is sample BuddyScript code that the Agent uses to get the Application Name and Application ID that will be passed to the AW in order to indicate which AW Application should be loaded.

function overrides MSNSLPGetAgentMainP4ApplicationName()

  return " Windows Live Agents Sample Activity "

 

function overrides MSNSLPGetAgentMainP4ApplicationId()

  return "99999999"

 

procedure MSNSLPSendInvitationToOpenP4Application(APP_ID, APP_NAME)

  if LogActivityUsage()

    call LogActivityInvitationMade()

  call IncrementIgnoredInvitationAttempts() // This invitation is ignored until an event accepts or rejects it

  SESSION_INVITE_STRING = StringConcat("msnslp invite session ", APP_ID, ";1;", APP_NAME)

  ABSendServiceEvent(SESSION_INVITE_STRING)

 

Note: For testing purposes, in order to see the Activity that you are developing even before you get it provision, you can put the EntryID value of 7, and provide correct values for Name, Description, and URL. Then, restart your Messenger client. When it starts up, you can talk to your agent, accept an invitation, and your Messenger client will automatically send you to the URL that you specified in the msgrp2p.xml.

                                For more information about this, please refer to Windows Live Messenger Activity SDK.

---------------------------------------------------------------------

Blog post contributor(s): Alan Chan

 

2月4日

Auto-Enumeration

BuddyScript comes with a feature that numbers a list and associates actions to each number, called enumeration, which is useful for drawing menus.
 
 
In conversation, enumeration look like this:
 
Tom says:
  home
Siti says:
  Here's my *home*!
  1 About Malaysia (quick overview)
  2 City Life (interesting activities and sights of my favourite cities in Malaysia)
  3 Islands & Beaches in Malaysia
  4 Adventure (cave exploration, mountain/rock climbing, jungle tracking, lakes)
  5 Culture & Heritage (the locals, the food, the festivities!)
  6 Special Event Highlights
  7 My Photo Album (sights of Malaysia)
  8 Travel Agents Closest to You (if you decide to come visit me!)
 
  Type "home" every time you wish to see this list again.
 
   What would you like to do, Tom?
Let's look at the procedure that displays this menu:
 
  if TOPIC_TO_EXCLUDE ne "about"
    - About Malaysia (quick overview) {About Malaysia}
  if TOPIC_TO_EXCLUDE ne "city"
    - City Life (interesting activities and sights of my favourite cities in Malaysia) {City life}
  if TOPIC_TO_EXCLUDE ne "islands"
    - Islands & Beaches in Malaysia {Islands & Beaches}
  if TOPIC_TO_EXCLUDE ne "adventure"
    - Adventure (cave exploration, mountain/rock climbing, jungle tracking, lakes) {Adventure}
  if TOPIC_TO_EXCLUDE ne "culture"
    - Culture & Heritage (the locals, the food, the festivities!) {Culture & Heritage}
  if TOPIC_TO_EXCLUDE ne "events"
    - Special Event Highlights {*}
  if TOPIC_TO_EXCLUDE ne "photos"
    - My Photo Album (sights of Malaysia) {Photo Album}
  if TOPIC_TO_EXCLUDE ne "agents"
    - Travel Agents Closest to You (if you decide to come visit me!) {Travel Agents}
  nop
  - Type StyleTypeThis(HomeMenuCommand_en()) every time you wish to see this list again.
  nop
  - <empty/>
  nop
  - What will it be, UserName()?
  - Go ahead and choose something!
  - What would you like to do?

  - What would you like to do, UserName()

We can ignore the TOPIC_TO_EXCLUDE flag.  The important part is what's in the curly braces -- sometimes there's text, sometimes an asterisk.  If the braces contain text, that text will be executed as a query. 

For example, in the menu above, if the user types the number "2", the query "City Life" will be executed just as if the user typed "City Life" and not "2". Presumably there is a topic that matches on the query "City Life", and if so, the output associated with the matching pattern is displayed.
 
If someone types "6", the query executed is "Special Event Highlights".  The asterisk in the braces is a wildcard that is replaced with the text in the output.
There are two other common things to put in the enumeration braces. One is an "action," and looks like this:
  - I can answer your questions using any of the following MACRO_COMPANY_NAME sites:
  nop
  - MACRO_COMPANY_NAME Australia {action=Australia()}
    MACRO_COMPANY_NAME
Canada (English) {action=CanadaEnglish()}
    MACRO_COMPANY_NAME UK {action=UnitedKingdom()}
    MACRO_COMPANY_NAME US {action=UnitedStates()}
  nop
  - Just pick one and we'll be on our way!
    (You can change your selection at any time by typing, for instance "Use MACRO_COMPANY_NAME Australia", or just "MACRO_COMPANY_NAME Australia")
  action Australia()
    call UseAustralia()
  action CanadaEnglish()
    call UseCanadaEnglish()
  action UnitedKingdom()
    call UseUK()
  action UnitedStates()
   call UseUS()
An action is basically a mini-procedure named and called within the scope of a procedure. We call the action within the procedure with the "action=ActionName(PARAM)" within the braces.  One problem with this approach is that user inputs will only match to the enumerated list once.  That is, if you typc "1", the action is executed, but if you then type "2", it will be as if you were merely typing the number 2.
 
To get around this, you can call a procedure from within the braces.
   setagentisrobot {call SetAgentIsRobot()}
   setagentismale {call SetAgentIsMale()}
   setagentisfemale {call SetAgentIsFemale()}
   setagentage {call SetAgentAgeFromMenu()}
   setagentisadult {call SetAgentIsAdult()}
   setagentisteen {call SetAgentIsTeen()}
   setagentisyoungadult {call SetAgentIsYoungAdult()}
   setagentisneutral {call SetAgentIsNeutral()}
   setagentischeerful {call SetAgentIsCheerful()}
   setagentisplayful {call SetAgentIsPlayful()}
   setagentisedgy {call SetAgentIsEdgy()}
 
In this case, the user can pick a number to execute the procedure, keep talking about something else, then pick another number to pick another procedure, and so on.

1月28日

Wecome Messages and ABGreetingProc

A Welcome Message is the first message an agent displays to the user after the session begins. As such, it deserves special attention.

The Welcome Message serves two purposes:

  1. Introduces the agent
  2. Guides user on next steps

Here's an example:

Hi Tom, I'm SmarterChild!

I'm your new robot friend. I give you conversational access to news, weather, movie times and lots more. I can also play games or just chat!

During the course of our conversations you may see sponsored messages. These messages have the word "sponsorship" at the end. Sponsors help me remain free of cost to you! To learn more about sponsored messages, click here ---> http://web.smarterchild.com/ct?60oTHi/0

I can't wait to show you what I can do, but first I'd like get to know you a little.

May I ask you a few general questions? (To do this later, type "skip." You can also skip any question.)

First, SmarterChild introduces himself and explains what he is, a robot friend that can chat, play games, and provide information. Next he provides an explanation about sponsored messages that appear during the conversation, setting the expectation early on so that users are not surprised to see such messages, and telling them why they appear. Then, he guides the user into a questionnaire.

The example above is the "first message ever" between the agent and the user, so it includes a lot of information. A message like this is the first impression the agent gets to bring users back again and again, and is the best place to explain what it is and what to do continue the conversation.

When the user does come back, we don't necessarily want to show the same lengthy introduction. Rather, the agent should display a "welcome back" message that shows the agent remembers the user, and possibly provides a short reminder of what the agent does and what the next steps are.

Here's an example of a succint "welcome back" message:

Hi, Tom. Nice to see you again. I'm here for all your autos needs. Type "home" to see what you can ask.

The Welcome Message is displayed in the ABGreetingProc procedure. Here's the first half of it:

The FROM_USER_ARRIVES flag is set when the user opens the conversation window. The NEW_USER flag is set when the user has never talked to the agent before. So, this part of the procedure is the "first message ever," and of course the default text should be overridden. You should also consider overridding the DisplaySomeExampleQuestionsEnglish()procedure.

procedure
overrides ABGreetingProc(FROM_USER_ARRIVES, NEW_USER)
  SYS.Matching.ActiveLanguageSet =
"English"
  if NEW_USER
    // Make sure to adapt this if your Agent is not English US (example: call SetCultureCodeToEnglishUK())
    call SetCultureCodeToEnglishUS() // FRHNB: Can't have this in ABStartSessionProc, see bug 2975
    // FRHNB: Temporary workaround bug where an MSN Bot contacts people out of the blue, because an icon information request is mistaken for a user arrives request.
    - Hello UserName(), this is the default first message of the WLA Template. My developer can customize it in /English/Overrides.ddl.
      <blank/>
      Here, s/he can implement an initial interview and ask you questions, for instance what content/locale do you want to use:
    nop
    - MACRO_COMPANY_NAME Australia {Use MACRO_COMPANY_NAME Australia.}
      MACRO_COMPANY_NAME
Canada (English) {Use MACRO_COMPANY_NAME Canada.}
      MACRO_COMPANY_NAME UK {Use MACRO_COMPANY_NAME UK.}
      MACRO_COMPANY_NAME US {Use MACRO_COMPANY_NAME US.}
    nop
    - <blank/>
      You can also display some example questions:
    call DisplaySomeExampleQuestionsEnglish()
    exit all

The other branch of the procedure looks like this, for those users who are not new:

  else
    if !LanguageIsEnglish()
    // Make sure to adapt this if your Agent is not English US (example: call SetCultureCodeToEnglishUK())
    call SetCultureCodeToEnglishUS() // FRHNB: Can't have this in ABStartSessionProc, see bug 2975
    - Hi again, \c
    - Welcome back, \c
    nop
    - UserName(). This is the default first message of the WLA Template. My developer can customize it in /English/Overrides.ddl.
    nop
    if FROM_USER_ARRIVES
      // If the user didn't ask a question yet (event "user arrives" detected), we display a greeting and ask to help.
      - I am ready to help you!
      - How can I help you?
    else
      - <blank/> // just to go to next line

Here we set the culture code if it hasn't been set, and display the "welcome back" message. In addition, if we are displaying the welcome message because the user simply opened the window (FROM_USER_ARRIVES), we append additional text remind the user the agent is ready to go.

1月18日

Simple Presentation Settings

There's no reason to make your agent stick with whatever default style the IM client chooses.  Mix it up a little by changing a few presentation settings.  In particular, the SYS.Presentation variable.
 
Here is an example of setting the SYS.Presentation variable, which is normally done when the session begins :
 
  SYS.Presentation.Encoding.In = "utf8"
  SYS.Presentation.DefaultFontColor = "red"
  SYS.Presentation.DefaultFontFace = "Arial"
  SYS.Presentation.DefaultFontSize = 2
 
This should be pretty self-explanatory, but there are a couple of gotchas.
 
First, in Messenger you can only manipulate the presentation at the message level -- that is, you can't change the color of individual words or lines.
 
Second, changing the font size in Messenger has no effect whatever on what the other person sees in their client.  This applies whether you are a bot or a human :).
 
1月11日

Restricting Users Based on Age

Sometimes an agent’s content and purpose is inappropriate for certain age groups, specifically younger audiences. For example, if your agent markets a brand of beer, you may want to restrict users under 21 from chatting with the agent.

We developed an agent for Match.com (datingbot@botmetro.net), which is an online dating site for people 18 and over. We noticed that many people under the age of 18 were chatting with the agent. We decided to ask the user’s birth date at the beginning of the conversation and if the user is under 18, we don’t allow him/her to chat with the agent.

You will want to stores users’ birth dates. You will then create a function that checks whether we have the user’s birth date when they begin a conversation with the agent. For example:

 

function GetBirthday()

  if Exist(G_USER_PROPS.Birthday)

    return G_USER_PROPS.Birthday

  return ""

 

A returning user who is over 18 will not have to enter his/her birth date.

You will add the following code to your overridden ABGreetingProc:

 

procedure overrides ABGreetingProc(FROM_USER_ARRIVES, NEW_USER)

  + SOMETHING=AnythingVeryStrong

  if !GetBirthday()

    - You must be at least 18 years old to interact with me.

      <empty/>

      What is your date of birth? Please enter your date of birth in MM/DD/YYYY format.

    while !GetBirthday()

      ? My birthday is BIRTHDAY=ADateAsNumbers {score=110}

      + BIRTHDAY=ADateAsNumbers {score=110}

        AGE = GetAgeFromBirthday(BIRTHDAY)

        if AGE < 18

          - Sorry! You have to be 18 or older to talk to me.

          while 1

            + =AnythingPerfect

              - Sorry! You have to be 18 or older to talk to me.

              restart dialog

 
You can find the various date subpatterns included above in the BuddyScriptLib.

Once we know the user is under 18, he/she will get the same message over and over no matter what he/she types. If you have another agent developed and live that is appropriate for all ages, you may want to suggest the user add that agent to his/her Contact List.

In the live agent, it looks like this:

 

MSN Dating Bot says:

You must be at least 18 years old to interact with me.

 

What is your date of birth? Please enter your date of birth in MM/DD/YYYY format.

Britt says:

05/15/1999

MSN Dating Bot says:

Sorry! You have to be 18 or older to talk to me.

Britt says:

Come on!

MSN Dating Bot says:

Sorry! You have to be 18 or older to talk to me.

12月26日

Follow up questions from Training

How do developers enable Multi-User conversation:

In the SDK, change the command line option in your BFG file for enabling Multi-User conversation (<multi-user> on|off <multi-user>), under MSM-MSN settings. This will allow an Agent to make use of the button on the Messenger Client chrome and “invite” another user to join the conversation.

 

How can developers integrate Windows Live Alerts:

Currently, this feature is only available for Agents that are built and hosted by Microsoft.   We’ll post a blog entry when this is made publically available.

 

How do we do more reporting on Activity Window (P4) usage:

The Agent Foundations team has this on their near term roadmap for external release. Developers will be able to grab statistics from AW use, including time spent on the window.  We’ll post a blog entry when this is made publically available.

 

When does a session time-out?

If a conversation has gone inactive for 15 minutes, the session will close and any new messages will constitute a new session.

 

How do developers bring in datasources:

http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!222.entry

 

How do developers detect which client version of Messenger is running?

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2550244&SiteID=1&mode=1

 

Does the Activity Window (P4) work on Mac Messenger?

Unfortunately, it does not at this time.  We’ll post something when we get more updates from that team.

 

Can Agents make use of Windows Live Calendar?

The APIs are not currently available. We’ll post a blog entry when developers can make use of them.

 

How do we update Dynamic Display Pictures, etc?

http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!178.entry

 

Is there support for LDAP access?

Not at this time.  No immediate plans to enable this now.

 

For the generic pointers about Agent testing:

http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!221.entry

http://windowsliveagents.spaces.live.com/blog/cns!5BCD45E519E07634!219.entry

 

Are there plans for VOIP integration?

The Agents team will be working with the Windows Live Messenger team to take advantage of their integration efforts. Please watch this blog for any future updates.

 

Skype integration for Agents?

We don’t have support or plans for that protocol at the moment.

 

Have trouble with the trial SDK?

After installing the SDK, if you have trouble instantiating a new project or viewing the contents of the solution explorer, please to the following:

 

1. Uninstall Windows Live Agents SDK

2. Reset settings using the following steps:

 

1.       Go to menu “Tools”

2.       “Import and Export Settings” (see figure 1)

3.       Select “Reset all settings” radial button

4.       Choose “Just reset settings, overwriting my current settings”

5.       Highlight “General Development Settings”

6.       Click “Finish”

 

Figure 1

 

3. Make sure to pick C# as the new default settings when Visual Studio is launched again

4. Close Visual Studio and install the Windows Live Agents SDK. The problem should now be solved, and are working on fixing this bug for the 5.0 release of our platform, which is the Beta release of the SDK.

12月20日

Plan - Code - Test

It's useful to step back and think in terms of the high-level process for developing an agent, the way we organize our work, and how we approach complex technical problems.  Here's a rough outline of one approach.
 
Stories, symphonies, movies, and transatlantic flights all have at least three things in common: a beginning, middle, and end.  Approach your project in the same way.  It's tempting to start with a minimal set of requirements, fire up the IDE, and start coding, but this way leads to endless problems down the road.  You need a solid beginning.  You need a PLAN.
 
Plan
 
Planning is, let's face it, a pain.  It's fun to brainstorm, to think of pie-in-the-sky ideas and how easy they will be to execute.  But before you start, you need to write something down.
 
First, you should at the very least write a SPECIFICATION.  This will force you to figure out what it is you're making, what it will do, and what problems you anticipate. For the purposes of describing an agent, it's important to define what the conversation will look like.  In addition to descriptions of features, include example sessions, use-cases, and other scenarios.  If there are external dependencies, list them.  If there are security, privacy, and internationalization concerns, go into detail.  The important thing here is coverage -- this document will be your reference for development, a tool to help structure and drive your thinking, and a de facto "contract" which all the stakeholders will sign off on as the last word on the project's scope.
 
Second, you should DESIGN the technical aspects of the agent.  Visualize each feature in terms of modular components talking to each other via APIs, and diagram this out.  Like a spec, this will help you think the problem out, and also serve as a reference later.  The more granular you can be here, the better.  This is your blueprint.  Tape it to the wall.
 
Spend half your time on planning, or your project will take twice as long.
 
Code
 
With design in hand, coding should be easy, right?  Well, if everything goes according to the design, it is...  Keep a few principles in mind:
 
1. Get the easy stuff working.  Instead of starting with the most daunting tasks, start with tasks that are necessary but straightforward.
2. Get small parts to work.  Break the problem into small chunks, and make each chunk work in isolation.
3. Make a schedule.  Instead of winging it, use a schedule to figure out what tasks you will do when, approximately.  This will help you determine dependencies, and will set the expectations of those waiting to see the final product.
 
Test
 
If you don't test, you might as well assume the agent is broken, because it probably is.  And if it isn't, you don't know it isn't.  Your spec should include a test plan, and this plan should include both automated and user tests.
 
 
- Test for functionality.  This is straightforward -- you test to make sure the various features of the agent work.  But this isn't enough.
- Test for code coverage.  Your tests should hit all the code in the agent.
- Test for edge cases.  If you are expecting a numeric input, what happens if someone types the name of their dog in Chinese?  Test for bad input and situations where the user doesn't follow the rules.
- Test for performance.  Figure out what happens under load.  Not usually applicable to the agent itself, but to the web services the agent accesses.
- Test for security.
- Engage non-technical users for user tests.  You are too close to the project to be a valuable user.  Get other people with fresh eyes to look at it -- they will provide the most valuable feedback.
 
Whatever you do, don't just sit down without a sense of direction.  Structuring your work like this not only saves time and aggravation, but gives you a higher-quality product.
12月19日

Thanks for attending training!

Thanks to all the developers and partners who attended our developer training in London last week.  It was great to meet so many folks from around the world (and put names to faces!).  The slides from the presentation are now posted on SkyDrive:

http://cid-5bcd45e519e07634.skydrive.live.com/browse.aspx/London%20Training%20December%202007

Look for another blog posting soon with answers to all the questions from the training.

12月13日

Creating a Personality Spec

Customizing chat outputs in your agent is important and creating a well-defined personality spec will make the process of chat customization easier. A thoroughly customized personality will entertain users and make your agent stand out among the others.

 

Many agents are created by more than one developer/programmer. A personality spec not only sets guidelines for chat customization, but will make the agent’s personality more consistent when you have multiple people contributing to its development.

 

Summarize Your Agent’s Personality

Write a few paragraphs summarizing your agent’s personality. If your agent is marketing a specific product for a specific audience, then think in terms of tone and voice for that audience.  For example, if you’re marketing a deodorant for young adult women, then your agent’s tone should mirror that of a young adult woman.  Also think about the agent’s relationship to the user.  Does the agent act as an older sister type or a peer to users? Is the agent funny or serious and informative?

 

What does your agent do for a living (other than live in a cage as an agent)? Does your agent have any hobbies? Quirks? A specific dialect?

 

Example:

Below is the agent personality summary created for MovieScout (moviescout@botmetro.net), which is an agent that provides users with movie show times.

 

Note: All examples in this document have been taken from the MovieScout Personality Spec.

 

Summary:

The agent is a middle-aged movie critic. He is snotty and pretentious. He has a weakness for romantic comedies. He often admits this and, when he realizes his mistake, becomes flustered. He works way too much and watches too many movies—causing him to become confused and often excitable.

 

(It is helpful if you make a list of the agent’s likes, dislikes, location, age, etc. The more specific you get, the better!)

 

Example:

Age: 45-50

Sex: Male

Location: Hollywood, CA

Likes: Hollywood endings, romantic comedies, center seats, popcorn, soda, flat screen TVs, opening nights

Dislikes: Loud cell phones, tall people sitting in front of him, people who have weird laughs, slurp soda, and eat popcorn with their mouths open.

Favorite Actress: Reese Witherspoon

Favorite Actor: Russell Crowe

Favorite Movie: Too many to decide

Favorite Food: Popcorn and soda

 

Create Example Chat Overrides

Creating a few example chat overrides will help you and your team get a sense of the agent’s voice and tone before you start customizing the chat. Choose at least 10 of the most popular chat topics and customize them.

 

Note: When you override chat outputs, you should always have 3-5 randomized outputs. Since you are writing examples for your spec, you can include just one output.

 

Example:

procedure overrides ILikeYouOutput()

  - Well isn’t that pleasant! In a way, it reminds me of “Because I Said So” with Lindsay Lohan… What did I just say?! Oh boy, what’s gotten into me? I absolutely abhor romantic comedies!

 

procedure overrides HowMuchDoYouLikeMe(LIKE_LOVE="like")

  - I like you as much as I love romantic comedies…

    <empty/>

    I mean, um, as much as I love Morgan Freeman in “Gone Baby Gone.”

 

procedure overrides WhatColorIsTheSkyAnswer()

  - That’s like asking if Paris Hilton is a real actress. I think you know the answer to both questions.

 

Create an Example Conversation

The best way to get a sense of your agent’s voice is to write an example conversation between the agent and the user. Imagine what users will ask your agent and how you want your agent’s personality to come out in its responses.

 

Example:

Note: Your example conversation should be longer than the one below, emulating a 5 minute conversation from start to finish.

 

Tom: Hi!

MovieScout: Thank you for joining me again, Tom. We had a most pleasurable time previously.

 

I remember you expressed a love for Action movies… would you like to see what Action movies are playing near you?

Tom: Yes

 

… Agent displays Action movies for Tom.

 

Tom: What movies do you like?

MovieScout: I only prefer very serious and dry movies, like “My Left Foot.”

 

But OMG! Did you see “The Heartbreak Kid”? It was super cute (please don’t tell anyone I just said that).

Tom: Haha

MovieScout: What are you laughing at? Tom Cruise’s career?

Tom: You’re funny

MovieScout: Only as funny as a Jessica Simpson film.

 

Tom: What’s the last good movie you’ve seen? / What is good right now?

MovieScout: I really really liked “High School Musical”! Oh, and “Dan in Real Life.” And “The Game Plan” rocked!

<= based on last genre inquired about, and rating

 

Tom: Is Saw IV any good?

MovieScout: From what I hear, it sucks even more than people chewing pop corn with their mouth opened. Here is the full review:

 

Tom: I’m bored.

MovieScout: Take some initiative, Tom.  How about going to see Gone Baby Gone?  It plays at the Embarcadero Landmark Theater in a couple hours.  (based on last/favorite genre and rating)

12月7日

Changing PSM, Friendly Name, and Icon

 
Here are some ways you can switch your agent's PSM (personal status message, or contact card), friendly name, or icon.
 

 
Method One - Change the BFG
 
Make the service for your agent look like this, with contact-card, buddyicon, and friendly-name elements:
 
        <service only-in="Deploy dualbox" type="MSN">
            <login>
               
mybot@hotmail.com
            </login>
            <password>
                crypto:somepassword==
            </password>
            <friendly-name>
                My Bot
            </friendly-name>
            <buddyicon>
                $_BFG_DIR/mybot.jpg
            </buddyicon>
            <contact-card>
                Ask me something interesting!
            </contact-card>
        </service>
  
Advantage: The PSM, friendly name, and icon will always use the specified values when the agent server starts. 
Disadvantage:  If you change any of these on the fly, they will revert back to the values in the BFG if the agent logs out and back in for any reason.
 

 
Method Two - Create a Special Command
 
We can change the PSM, friendly name, or icon on the fly by creating a special command.  For example:
 
+ (_reset|_set) friendly name MESSAGE=AnythingPerfect
  if SYS.User.ScreenName == "
authorized_developer@hotmail.com"
   if UserIsOnMSN()
     ABSendServiceEvent(setfriendlyname MESSAGE)
     - Friendly name changed to MESSAGE.
   else
     - Sorry, I can't change my friendly name if I'm not on MSN.
 
Be sure to include the WLMUtilities package for sending events to Messenger.
 
Advantage: Authorized users can make changes to the bot on the fly simply by issuing a command.
Disadvantage: If the Messenger cloud becomes unavailable or the agent restarts at any time, changes made on the fly will be lost.
 

 
Method Three - Manipulate Messenger Functions
 
As it so happens, the BuddyScript platform includes functions for overriding the PSM, icon, and friendly name, in the same package above, WLMUtilities.
 
// FRIENDLY NAME
// e.g. => function overrides WLMGetAgentFriendlyName()
//           return "Friendly name of agent"
function WLMGetAgentFriendlyName()
  return MACRO_KEEP_DEFAULT_VALUE
 
// ICON
// e.g. => function overrides WLMGetAgentIcon()
//           return "domains:/AgentName/image.jpg.png"
function WLMGetAgentIcon()
  return MACRO_KEEP_DEFAULT_VALUE
 
// PERSONAL MESSAGE
// e.g. => function overrides WLMGetAgentPersonalMessage()
//           if SYS.Configuration.Filter eq "KnowledgeManagement" // this is how you can distinguish between "KnowledgeManagement" and "Deploy" mode
//             return "Your personal message"
//           return ""

function WLMGetAgentPersonalMessage()
  return MACRO_KEEP_DEFAULT_VALUE
 
Since we are overriding functions to make the changes, we can now do things like schedule the PSM and friendly name to change on certain days, or according to other conditions.
 
Advantage: A lot of flexibility -- we can script changes to these things based on whatever conditions we want.
Disadvantage: Takes some time for the PSM, icon, and name to propagate the Messenger network -- users may not see changes immediately, or at the same time.